i2o: move to staging
The I2O layer deals with a technology that to say the least didn't catch on
in the market.
The only relevant products are some of the AMI MegaRAID - which supported I2O
and its native mode (The native mode is faster and runs on Linux), an
obscure crypto ethernet card that's now so many years out of date nobody
would use it, the old DPT controllers, which speak their own dialect and
have their own driver - and ermm.. thats about it.
We also know the code isn't in good shape as recently a patch was proposed
and queried as buggy, which in turn showed the existing code was broken
already by prior "clean up" and nobody had noticed that either.
It's coding style robot code nothing more. Like some forgotten corridor
cleaned relentlessly by a lost Roomba but where no user has trodden in years.
Move it to staging and then to /dev/null.
The headers remain as they are shared with dpt_i2o.
Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 071ac11..9e52bcd 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -110,4 +110,6 @@
source "drivers/staging/fbtft/Kconfig"
+source "drivers/staging/i2o/Kconfig"
+
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index b65ca37..6e0ac52 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -47,3 +47,4 @@
obj-$(CONFIG_UNISYSSPAR) += unisys/
obj-$(CONFIG_COMMON_CLK_XLNX_CLKWZRD) += clocking-wizard/
obj-$(CONFIG_FB_TFT) += fbtft/
+obj-$(CONFIG_I2O) += i2o/
diff --git a/drivers/staging/i2o/Kconfig b/drivers/staging/i2o/Kconfig
new file mode 100644
index 0000000..286c53f
--- /dev/null
+++ b/drivers/staging/i2o/Kconfig
@@ -0,0 +1,120 @@
+menuconfig I2O
+ tristate "I2O device support"
+ depends on PCI
+ ---help---
+ The Intelligent Input/Output (I2O) architecture allows hardware
+ drivers to be split into two parts: an operating system specific
+ module called the OSM and an hardware specific module called the
+ HDM. The OSM can talk to a whole range of HDM's, and ideally the
+ HDM's are not OS dependent. This allows for the same HDM driver to
+ be used under different operating systems if the relevant OSM is in
+ place. In order for this to work, you need to have an I2O interface
+ adapter card in your computer. This card contains a special I/O
+ processor (IOP), thus allowing high speeds since the CPU does not
+ have to deal with I/O.
+
+ If you say Y here, you will get a choice of interface adapter
+ drivers and OSM's with the following questions.
+
+ To compile this support as a module, choose M here: the
+ modules will be called i2o_core.
+
+ If unsure, say N.
+
+if I2O
+
+config I2O_LCT_NOTIFY_ON_CHANGES
+ bool "Enable LCT notification"
+ default y
+ ---help---
+ Only say N here if you have a I2O controller from SUN. The SUN
+ firmware doesn't support LCT notification on changes. If this option
+ is enabled on such a controller the driver will hang up in a endless
+ loop. On all other controllers say Y.
+
+ If unsure, say Y.
+
+config I2O_EXT_ADAPTEC
+ bool "Enable Adaptec extensions"
+ default y
+ ---help---
+ Say Y for support of raidutils for Adaptec I2O controllers. You also
+ have to say Y to "I2O Configuration support", "I2O SCSI OSM" below
+ and to "SCSI generic support" under "SCSI device configuration".
+
+config I2O_EXT_ADAPTEC_DMA64
+ bool "Enable 64-bit DMA"
+ depends on I2O_EXT_ADAPTEC && ( 64BIT || HIGHMEM64G )
+ default y
+ ---help---
+ Say Y for support of 64-bit DMA transfer mode on Adaptec I2O
+ controllers.
+ Note: You need at least firmware version 3709.
+
+config I2O_CONFIG
+ tristate "I2O Configuration support"
+ depends on VIRT_TO_BUS
+ ---help---
+ Say Y for support of the configuration interface for the I2O adapters.
+ If you have a RAID controller from Adaptec and you want to use the
+ raidutils to manage your RAID array, you have to say Y here.
+
+ To compile this support as a module, choose M here: the
+ module will be called i2o_config.
+
+ Note: If you want to use the new API you have to download the
+ i2o_config patch from http://i2o.shadowconnect.com/
+
+config I2O_CONFIG_OLD_IOCTL
+ bool "Enable ioctls (OBSOLETE)"
+ depends on I2O_CONFIG
+ default y
+ ---help---
+ Enables old ioctls.
+
+config I2O_BUS
+ tristate "I2O Bus Adapter OSM"
+ ---help---
+ Include support for the I2O Bus Adapter OSM. The Bus Adapter OSM
+ provides access to the busses on the I2O controller. The main purpose
+ is to rescan the bus to find new devices.
+
+ To compile this support as a module, choose M here: the
+ module will be called i2o_bus.
+
+config I2O_BLOCK
+ tristate "I2O Block OSM"
+ depends on BLOCK
+ ---help---
+ Include support for the I2O Block OSM. The Block OSM presents disk
+ and other structured block devices to the operating system. If you
+ are using an RAID controller, you could access the array only by
+ the Block OSM driver. But it is possible to access the single disks
+ by the SCSI OSM driver, for example to monitor the disks.
+
+ To compile this support as a module, choose M here: the
+ module will be called i2o_block.
+
+config I2O_SCSI
+ tristate "I2O SCSI OSM"
+ depends on SCSI
+ ---help---
+ Allows direct SCSI access to SCSI devices on a SCSI or FibreChannel
+ I2O controller. You can use both the SCSI and Block OSM together if
+ you wish. To access a RAID array, you must use the Block OSM driver.
+ But you could use the SCSI OSM driver to monitor the single disks.
+
+ To compile this support as a module, choose M here: the
+ module will be called i2o_scsi.
+
+config I2O_PROC
+ tristate "I2O /proc support"
+ ---help---
+ If you say Y here and to "/proc file system support", you will be
+ able to read I2O related information from the virtual directory
+ /proc/i2o.
+
+ To compile this support as a module, choose M here: the
+ module will be called i2o_proc.
+
+endif # I2O
diff --git a/drivers/staging/i2o/Makefile b/drivers/staging/i2o/Makefile
new file mode 100644
index 0000000..b0982da
--- /dev/null
+++ b/drivers/staging/i2o/Makefile
@@ -0,0 +1,16 @@
+#
+# Makefile for the kernel I2O OSM.
+#
+# Note : at this point, these files are compiled on all systems.
+# In the future, some of these should be built conditionally.
+#
+
+i2o_core-y += iop.o driver.o device.o debug.o pci.o exec-osm.o memory.o
+i2o_bus-y += bus-osm.o
+i2o_config-y += config-osm.o
+obj-$(CONFIG_I2O) += i2o_core.o
+obj-$(CONFIG_I2O_CONFIG)+= i2o_config.o
+obj-$(CONFIG_I2O_BUS) += i2o_bus.o
+obj-$(CONFIG_I2O_BLOCK) += i2o_block.o
+obj-$(CONFIG_I2O_SCSI) += i2o_scsi.o
+obj-$(CONFIG_I2O_PROC) += i2o_proc.o
diff --git a/drivers/staging/i2o/README b/drivers/staging/i2o/README
new file mode 100644
index 0000000..f072a8e
--- /dev/null
+++ b/drivers/staging/i2o/README
@@ -0,0 +1,98 @@
+
+ Linux I2O Support (c) Copyright 1999 Red Hat Software
+ and others.
+
+ 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.
+
+AUTHORS (so far)
+
+Alan Cox, Building Number Three Ltd.
+ Core code, SCSI and Block OSMs
+
+Steve Ralston, LSI Logic Corp.
+ Debugging SCSI and Block OSM
+
+Deepak Saxena, Intel Corp.
+ Various core/block extensions
+ /proc interface, bug fixes
+ Ioctl interfaces for control
+ Debugging LAN OSM
+
+Philip Rumpf
+ Fixed assorted dumb SMP locking bugs
+
+Juha Sievanen, University of Helsinki Finland
+ LAN OSM code
+ /proc interface to LAN class
+ Bug fixes
+ Core code extensions
+
+Auvo Häkkinen, University of Helsinki Finland
+ LAN OSM code
+ /Proc interface to LAN class
+ Bug fixes
+ Core code extensions
+
+Taneli Vähäkangas, University of Helsinki Finland
+ Fixes to i2o_config
+
+CREDITS
+
+ This work was made possible by
+
+Red Hat Software
+ Funding for the Building #3 part of the project
+
+Symbios Logic (Now LSI)
+ Host adapters, hints, known to work platforms when I hit
+ compatibility problems
+
+BoxHill Corporation
+ Loan of initial FibreChannel disk array used for development work.
+
+European Commission
+ Funding the work done by the University of Helsinki
+
+SysKonnect
+ Loan of FDDI and Gigabit Ethernet cards
+
+ASUSTeK
+ Loan of I2O motherboard
+
+STATUS:
+
+o The core setup works within limits.
+o The scsi layer seems to almost work.
+ I'm still chasing down the hang bug.
+o The block OSM is mostly functional
+o LAN OSM works with FDDI and Ethernet cards.
+
+TO DO:
+
+General:
+o Provide hidden address space if asked
+o Long term message flow control
+o PCI IOP's without interrupts are not supported yet
+o Push FAIL handling into the core
+o DDM control interfaces for module load etc
+o Add I2O 2.0 support (Deffered to 2.5 kernel)
+
+Block:
+o Multiple major numbers
+o Read ahead and cache handling stuff. Talk to Ingo and people
+o Power management
+o Finish Media changers
+
+SCSI:
+o Find the right way to associate drives/luns/busses
+
+Lan:
+o Performance tuning
+o Test Fibre Channel code
+
+Tape:
+o Anyone seen anything implementing this ?
+ (D.S: Will attempt to do so if spare cycles permit)
diff --git a/drivers/staging/i2o/README.ioctl b/drivers/staging/i2o/README.ioctl
new file mode 100644
index 0000000..4a7d2eb
--- /dev/null
+++ b/drivers/staging/i2o/README.ioctl
@@ -0,0 +1,394 @@
+
+Linux I2O User Space Interface
+rev 0.3 - 04/20/99
+
+=============================================================================
+Originally written by Deepak Saxena(deepak@plexity.net)
+Currently maintained by Deepak Saxena(deepak@plexity.net)
+=============================================================================
+
+I. Introduction
+
+The Linux I2O subsystem provides a set of ioctl() commands that can be
+utilized by user space applications to communicate with IOPs and devices
+on individual IOPs. This document defines the specific ioctl() commands
+that are available to the user and provides examples of their uses.
+
+This document assumes the reader is familiar with or has access to the
+I2O specification as no I2O message parameters are outlined. For information
+on the specification, see http://www.i2osig.org
+
+This document and the I2O user space interface are currently maintained
+by Deepak Saxena. Please send all comments, errata, and bug fixes to
+deepak@csociety.purdue.edu
+
+II. IOP Access
+
+Access to the I2O subsystem is provided through the device file named
+/dev/i2o/ctl. This file is a character file with major number 10 and minor
+number 166. It can be created through the following command:
+
+ mknod /dev/i2o/ctl c 10 166
+
+III. Determining the IOP Count
+
+ SYNOPSIS
+
+ ioctl(fd, I2OGETIOPS, int *count);
+
+ u8 count[MAX_I2O_CONTROLLERS];
+
+ DESCRIPTION
+
+ This function returns the system's active IOP table. count should
+ point to a buffer containing MAX_I2O_CONTROLLERS entries. Upon
+ returning, each entry will contain a non-zero value if the given
+ IOP unit is active, and NULL if it is inactive or non-existent.
+
+ RETURN VALUE.
+
+ Returns 0 if no errors occur, and -1 otherwise. If an error occurs,
+ errno is set appropriately:
+
+ EFAULT Invalid user space pointer was passed
+
+IV. Getting Hardware Resource Table
+
+ SYNOPSIS
+
+ ioctl(fd, I2OHRTGET, struct i2o_cmd_hrt *hrt);
+
+ struct i2o_cmd_hrtlct
+ {
+ u32 iop; /* IOP unit number */
+ void *resbuf; /* Buffer for result */
+ u32 *reslen; /* Buffer length in bytes */
+ };
+
+ DESCRIPTION
+
+ This function returns the Hardware Resource Table of the IOP specified
+ by hrt->iop in the buffer pointed to by hrt->resbuf. The actual size of
+ the data is written into *(hrt->reslen).
+
+ RETURNS
+
+ This function returns 0 if no errors occur. If an error occurs, -1
+ is returned and errno is set appropriately:
+
+ EFAULT Invalid user space pointer was passed
+ ENXIO Invalid IOP number
+ ENOBUFS Buffer not large enough. If this occurs, the required
+ buffer length is written into *(hrt->reslen)
+
+V. Getting Logical Configuration Table
+
+ SYNOPSIS
+
+ ioctl(fd, I2OLCTGET, struct i2o_cmd_lct *lct);
+
+ struct i2o_cmd_hrtlct
+ {
+ u32 iop; /* IOP unit number */
+ void *resbuf; /* Buffer for result */
+ u32 *reslen; /* Buffer length in bytes */
+ };
+
+ DESCRIPTION
+
+ This function returns the Logical Configuration Table of the IOP specified
+ by lct->iop in the buffer pointed to by lct->resbuf. The actual size of
+ the data is written into *(lct->reslen).
+
+ RETURNS
+
+ This function returns 0 if no errors occur. If an error occurs, -1
+ is returned and errno is set appropriately:
+
+ EFAULT Invalid user space pointer was passed
+ ENXIO Invalid IOP number
+ ENOBUFS Buffer not large enough. If this occurs, the required
+ buffer length is written into *(lct->reslen)
+
+VI. Setting Parameters
+
+ SYNOPSIS
+
+ ioctl(fd, I2OPARMSET, struct i2o_parm_setget *ops);
+
+ struct i2o_cmd_psetget
+ {
+ u32 iop; /* IOP unit number */
+ u32 tid; /* Target device TID */
+ void *opbuf; /* Operation List buffer */
+ u32 oplen; /* Operation List buffer length in bytes */
+ void *resbuf; /* Result List buffer */
+ u32 *reslen; /* Result List buffer length in bytes */
+ };
+
+ DESCRIPTION
+
+ This function posts a UtilParamsSet message to the device identified
+ by ops->iop and ops->tid. The operation list for the message is
+ sent through the ops->opbuf buffer, and the result list is written
+ into the buffer pointed to by ops->resbuf. The number of bytes
+ written is placed into *(ops->reslen).
+
+ RETURNS
+
+ The return value is the size in bytes of the data written into
+ ops->resbuf if no errors occur. If an error occurs, -1 is returned
+ and errno is set appropriately:
+
+ EFAULT Invalid user space pointer was passed
+ ENXIO Invalid IOP number
+ ENOBUFS Buffer not large enough. If this occurs, the required
+ buffer length is written into *(ops->reslen)
+ ETIMEDOUT Timeout waiting for reply message
+ ENOMEM Kernel memory allocation error
+
+ A return value of 0 does not mean that the value was actually
+ changed properly on the IOP. The user should check the result
+ list to determine the specific status of the transaction.
+
+VII. Getting Parameters
+
+ SYNOPSIS
+
+ ioctl(fd, I2OPARMGET, struct i2o_parm_setget *ops);
+
+ struct i2o_parm_setget
+ {
+ u32 iop; /* IOP unit number */
+ u32 tid; /* Target device TID */
+ void *opbuf; /* Operation List buffer */
+ u32 oplen; /* Operation List buffer length in bytes */
+ void *resbuf; /* Result List buffer */
+ u32 *reslen; /* Result List buffer length in bytes */
+ };
+
+ DESCRIPTION
+
+ This function posts a UtilParamsGet message to the device identified
+ by ops->iop and ops->tid. The operation list for the message is
+ sent through the ops->opbuf buffer, and the result list is written
+ into the buffer pointed to by ops->resbuf. The actual size of data
+ written is placed into *(ops->reslen).
+
+ RETURNS
+
+ EFAULT Invalid user space pointer was passed
+ ENXIO Invalid IOP number
+ ENOBUFS Buffer not large enough. If this occurs, the required
+ buffer length is written into *(ops->reslen)
+ ETIMEDOUT Timeout waiting for reply message
+ ENOMEM Kernel memory allocation error
+
+ A return value of 0 does not mean that the value was actually
+ properly retrieved. The user should check the result list
+ to determine the specific status of the transaction.
+
+VIII. Downloading Software
+
+ SYNOPSIS
+
+ ioctl(fd, I2OSWDL, struct i2o_sw_xfer *sw);
+
+ struct i2o_sw_xfer
+ {
+ u32 iop; /* IOP unit number */
+ u8 flags; /* DownloadFlags field */
+ u8 sw_type; /* Software type */
+ u32 sw_id; /* Software ID */
+ void *buf; /* Pointer to software buffer */
+ u32 *swlen; /* Length of software buffer */
+ u32 *maxfrag; /* Number of fragments */
+ u32 *curfrag; /* Current fragment number */
+ };
+
+ DESCRIPTION
+
+ This function downloads a software fragment pointed by sw->buf
+ to the iop identified by sw->iop. The DownloadFlags, SwID, SwType
+ and SwSize fields of the ExecSwDownload message are filled in with
+ the values of sw->flags, sw->sw_id, sw->sw_type and *(sw->swlen).
+
+ The fragments _must_ be sent in order and be 8K in size. The last
+ fragment _may_ be shorter, however. The kernel will compute its
+ size based on information in the sw->swlen field.
+
+ Please note that SW transfers can take a long time.
+
+ RETURNS
+
+ This function returns 0 no errors occur. If an error occurs, -1
+ is returned and errno is set appropriately:
+
+ EFAULT Invalid user space pointer was passed
+ ENXIO Invalid IOP number
+ ETIMEDOUT Timeout waiting for reply message
+ ENOMEM Kernel memory allocation error
+
+IX. Uploading Software
+
+ SYNOPSIS
+
+ ioctl(fd, I2OSWUL, struct i2o_sw_xfer *sw);
+
+ struct i2o_sw_xfer
+ {
+ u32 iop; /* IOP unit number */
+ u8 flags; /* UploadFlags */
+ u8 sw_type; /* Software type */
+ u32 sw_id; /* Software ID */
+ void *buf; /* Pointer to software buffer */
+ u32 *swlen; /* Length of software buffer */
+ u32 *maxfrag; /* Number of fragments */
+ u32 *curfrag; /* Current fragment number */
+ };
+
+ DESCRIPTION
+
+ This function uploads a software fragment from the IOP identified
+ by sw->iop, sw->sw_type, sw->sw_id and optionally sw->swlen fields.
+ The UploadFlags, SwID, SwType and SwSize fields of the ExecSwUpload
+ message are filled in with the values of sw->flags, sw->sw_id,
+ sw->sw_type and *(sw->swlen).
+
+ The fragments _must_ be requested in order and be 8K in size. The
+ user is responsible for allocating memory pointed by sw->buf. The
+ last fragment _may_ be shorter.
+
+ Please note that SW transfers can take a long time.
+
+ RETURNS
+
+ This function returns 0 if no errors occur. If an error occurs, -1
+ is returned and errno is set appropriately:
+
+ EFAULT Invalid user space pointer was passed
+ ENXIO Invalid IOP number
+ ETIMEDOUT Timeout waiting for reply message
+ ENOMEM Kernel memory allocation error
+
+X. Removing Software
+
+ SYNOPSIS
+
+ ioctl(fd, I2OSWDEL, struct i2o_sw_xfer *sw);
+
+ struct i2o_sw_xfer
+ {
+ u32 iop; /* IOP unit number */
+ u8 flags; /* RemoveFlags */
+ u8 sw_type; /* Software type */
+ u32 sw_id; /* Software ID */
+ void *buf; /* Unused */
+ u32 *swlen; /* Length of the software data */
+ u32 *maxfrag; /* Unused */
+ u32 *curfrag; /* Unused */
+ };
+
+ DESCRIPTION
+
+ This function removes software from the IOP identified by sw->iop.
+ The RemoveFlags, SwID, SwType and SwSize fields of the ExecSwRemove message
+ are filled in with the values of sw->flags, sw->sw_id, sw->sw_type and
+ *(sw->swlen). Give zero in *(sw->len) if the value is unknown. IOP uses
+ *(sw->swlen) value to verify correct identication of the module to remove.
+ The actual size of the module is written into *(sw->swlen).
+
+ RETURNS
+
+ This function returns 0 if no errors occur. If an error occurs, -1
+ is returned and errno is set appropriately:
+
+ EFAULT Invalid user space pointer was passed
+ ENXIO Invalid IOP number
+ ETIMEDOUT Timeout waiting for reply message
+ ENOMEM Kernel memory allocation error
+
+X. Validating Configuration
+
+ SYNOPSIS
+
+ ioctl(fd, I2OVALIDATE, int *iop);
+ u32 iop;
+
+ DESCRIPTION
+
+ This function posts an ExecConfigValidate message to the controller
+ identified by iop. This message indicates that the current
+ configuration is accepted. The iop changes the status of suspect drivers
+ to valid and may delete old drivers from its store.
+
+ RETURNS
+
+ This function returns 0 if no erro occur. If an error occurs, -1 is
+ returned and errno is set appropriately:
+
+ ETIMEDOUT Timeout waiting for reply message
+ ENXIO Invalid IOP number
+
+XI. Configuration Dialog
+
+ SYNOPSIS
+
+ ioctl(fd, I2OHTML, struct i2o_html *htquery);
+ struct i2o_html
+ {
+ u32 iop; /* IOP unit number */
+ u32 tid; /* Target device ID */
+ u32 page; /* HTML page */
+ void *resbuf; /* Buffer for reply HTML page */
+ u32 *reslen; /* Length in bytes of reply buffer */
+ void *qbuf; /* Pointer to HTTP query string */
+ u32 qlen; /* Length in bytes of query string buffer */
+ };
+
+ DESCRIPTION
+
+ This function posts an UtilConfigDialog message to the device identified
+ by htquery->iop and htquery->tid. The requested HTML page number is
+ provided by the htquery->page field, and the resultant data is stored
+ in the buffer pointed to by htquery->resbuf. If there is an HTTP query
+ string that is to be sent to the device, it should be sent in the buffer
+ pointed to by htquery->qbuf. If there is no query string, this field
+ should be set to NULL. The actual size of the reply received is written
+ into *(htquery->reslen).
+
+ RETURNS
+
+ This function returns 0 if no error occur. If an error occurs, -1
+ is returned and errno is set appropriately:
+
+ EFAULT Invalid user space pointer was passed
+ ENXIO Invalid IOP number
+ ENOBUFS Buffer not large enough. If this occurs, the required
+ buffer length is written into *(ops->reslen)
+ ETIMEDOUT Timeout waiting for reply message
+ ENOMEM Kernel memory allocation error
+
+XII. Events
+
+ In the process of determining this. Current idea is to have use
+ the select() interface to allow user apps to periodically poll
+ the /dev/i2o/ctl device for events. When select() notifies the user
+ that an event is available, the user would call read() to retrieve
+ a list of all the events that are pending for the specific device.
+
+=============================================================================
+Revision History
+=============================================================================
+
+Rev 0.1 - 04/01/99
+- Initial revision
+
+Rev 0.2 - 04/06/99
+- Changed return values to match UNIX ioctl() standard. Only return values
+ are 0 and -1. All errors are reported through errno.
+- Added summary of proposed possible event interfaces
+
+Rev 0.3 - 04/20/99
+- Changed all ioctls() to use pointers to user data instead of actual data
+- Updated error values to match the code
diff --git a/drivers/staging/i2o/bus-osm.c b/drivers/staging/i2o/bus-osm.c
new file mode 100644
index 0000000..7aa0339
--- /dev/null
+++ b/drivers/staging/i2o/bus-osm.c
@@ -0,0 +1,176 @@
+/*
+ * Bus Adapter OSM
+ *
+ * Copyright (C) 2005 Markus Lidel <Markus.Lidel@shadowconnect.com>
+ *
+ * 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.
+ *
+ * Fixes/additions:
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>
+ * initial version.
+ */
+
+#include <linux/module.h>
+#include "i2o.h"
+
+#define OSM_NAME "bus-osm"
+#define OSM_VERSION "1.317"
+#define OSM_DESCRIPTION "I2O Bus Adapter OSM"
+
+static struct i2o_driver i2o_bus_driver;
+
+/* Bus OSM class handling definition */
+static struct i2o_class_id i2o_bus_class_id[] = {
+ {I2O_CLASS_BUS_ADAPTER},
+ {I2O_CLASS_END}
+};
+
+/**
+ * i2o_bus_scan - Scan the bus for new devices
+ * @dev: I2O device of the bus, which should be scanned
+ *
+ * Scans the bus dev for new / removed devices. After the scan a new LCT
+ * will be fetched automatically.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_bus_scan(struct i2o_device *dev)
+{
+ struct i2o_message *msg;
+
+ msg = i2o_msg_get_wait(dev->iop, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return -ETIMEDOUT;
+
+ msg->u.head[0] = cpu_to_le32(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_BUS_SCAN << 24 | HOST_TID << 12 | dev->lct_data.
+ tid);
+
+ return i2o_msg_post_wait(dev->iop, msg, 60);
+};
+
+/**
+ * i2o_bus_store_scan - Scan the I2O Bus Adapter
+ * @d: device which should be scanned
+ * @attr: device_attribute
+ * @buf: output buffer
+ * @count: buffer size
+ *
+ * Returns count.
+ */
+static ssize_t i2o_bus_store_scan(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2o_device *i2o_dev = to_i2o_device(d);
+ int rc;
+
+ if ((rc = i2o_bus_scan(i2o_dev)))
+ osm_warn("bus scan failed %d\n", rc);
+
+ return count;
+}
+
+/* Bus Adapter OSM device attributes */
+static DEVICE_ATTR(scan, S_IWUSR, NULL, i2o_bus_store_scan);
+
+/**
+ * i2o_bus_probe - verify if dev is a I2O Bus Adapter device and install it
+ * @dev: device to verify if it is a I2O Bus Adapter device
+ *
+ * Because we want all Bus Adapters always return 0.
+ * Except when we fail. Then we are sad.
+ *
+ * Returns 0, except when we fail to excel.
+ */
+static int i2o_bus_probe(struct device *dev)
+{
+ struct i2o_device *i2o_dev = to_i2o_device(get_device(dev));
+ int rc;
+
+ rc = device_create_file(dev, &dev_attr_scan);
+ if (rc)
+ goto err_out;
+
+ osm_info("device added (TID: %03x)\n", i2o_dev->lct_data.tid);
+
+ return 0;
+
+err_out:
+ put_device(dev);
+ return rc;
+};
+
+/**
+ * i2o_bus_remove - remove the I2O Bus Adapter device from the system again
+ * @dev: I2O Bus Adapter device which should be removed
+ *
+ * Always returns 0.
+ */
+static int i2o_bus_remove(struct device *dev)
+{
+ struct i2o_device *i2o_dev = to_i2o_device(dev);
+
+ device_remove_file(dev, &dev_attr_scan);
+
+ put_device(dev);
+
+ osm_info("device removed (TID: %03x)\n", i2o_dev->lct_data.tid);
+
+ return 0;
+};
+
+/* Bus Adapter OSM driver struct */
+static struct i2o_driver i2o_bus_driver = {
+ .name = OSM_NAME,
+ .classes = i2o_bus_class_id,
+ .driver = {
+ .probe = i2o_bus_probe,
+ .remove = i2o_bus_remove,
+ },
+};
+
+/**
+ * i2o_bus_init - Bus Adapter OSM initialization function
+ *
+ * Only register the Bus Adapter OSM in the I2O core.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int __init i2o_bus_init(void)
+{
+ int rc;
+
+ printk(KERN_INFO OSM_DESCRIPTION " v" OSM_VERSION "\n");
+
+ /* Register Bus Adapter OSM into I2O core */
+ rc = i2o_driver_register(&i2o_bus_driver);
+ if (rc) {
+ osm_err("Could not register Bus Adapter OSM\n");
+ return rc;
+ }
+
+ return 0;
+};
+
+/**
+ * i2o_bus_exit - Bus Adapter OSM exit function
+ *
+ * Unregisters Bus Adapter OSM from I2O core.
+ */
+static void __exit i2o_bus_exit(void)
+{
+ i2o_driver_unregister(&i2o_bus_driver);
+};
+
+MODULE_AUTHOR("Markus Lidel <Markus.Lidel@shadowconnect.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(OSM_DESCRIPTION);
+MODULE_VERSION(OSM_VERSION);
+
+module_init(i2o_bus_init);
+module_exit(i2o_bus_exit);
diff --git a/drivers/staging/i2o/config-osm.c b/drivers/staging/i2o/config-osm.c
new file mode 100644
index 0000000..519f52f
--- /dev/null
+++ b/drivers/staging/i2o/config-osm.c
@@ -0,0 +1,90 @@
+/*
+ * Configuration OSM
+ *
+ * Copyright (C) 2005 Markus Lidel <Markus.Lidel@shadowconnect.com>
+ *
+ * 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.
+ *
+ * Fixes/additions:
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>
+ * initial version.
+ */
+
+#include <linux/module.h>
+#include "i2o.h"
+#include <linux/dcache.h>
+#include <linux/namei.h>
+#include <linux/fs.h>
+
+#include <asm/uaccess.h>
+
+#define OSM_NAME "config-osm"
+#define OSM_VERSION "1.323"
+#define OSM_DESCRIPTION "I2O Configuration OSM"
+
+/* access mode user rw */
+#define S_IWRSR (S_IRUSR | S_IWUSR)
+
+static struct i2o_driver i2o_config_driver;
+
+/* Config OSM driver struct */
+static struct i2o_driver i2o_config_driver = {
+ .name = OSM_NAME,
+};
+
+#ifdef CONFIG_I2O_CONFIG_OLD_IOCTL
+#include "i2o_config.c"
+#endif
+
+/**
+ * i2o_config_init - Configuration OSM initialization function
+ *
+ * Registers Configuration OSM in the I2O core and if old ioctl's are
+ * compiled in initialize them.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int __init i2o_config_init(void)
+{
+ printk(KERN_INFO OSM_DESCRIPTION " v" OSM_VERSION "\n");
+
+ if (i2o_driver_register(&i2o_config_driver)) {
+ osm_err("handler register failed.\n");
+ return -EBUSY;
+ }
+#ifdef CONFIG_I2O_CONFIG_OLD_IOCTL
+ if (i2o_config_old_init()) {
+ osm_err("old config handler initialization failed\n");
+ i2o_driver_unregister(&i2o_config_driver);
+ return -EBUSY;
+ }
+#endif
+
+ return 0;
+}
+
+/**
+ * i2o_config_exit - Configuration OSM exit function
+ *
+ * If old ioctl's are compiled in exit remove them and unregisters
+ * Configuration OSM from I2O core.
+ */
+static void i2o_config_exit(void)
+{
+#ifdef CONFIG_I2O_CONFIG_OLD_IOCTL
+ i2o_config_old_exit();
+#endif
+
+ i2o_driver_unregister(&i2o_config_driver);
+}
+
+MODULE_AUTHOR("Markus Lidel <Markus.Lidel@shadowconnect.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(OSM_DESCRIPTION);
+MODULE_VERSION(OSM_VERSION);
+
+module_init(i2o_config_init);
+module_exit(i2o_config_exit);
diff --git a/drivers/staging/i2o/core.h b/drivers/staging/i2o/core.h
new file mode 100644
index 0000000..91614f1
--- /dev/null
+++ b/drivers/staging/i2o/core.h
@@ -0,0 +1,69 @@
+/*
+ * I2O core internal declarations
+ *
+ * Copyright (C) 2005 Markus Lidel <Markus.Lidel@shadowconnect.com>
+ *
+ * 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.
+ *
+ * Fixes/additions:
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>
+ * initial version.
+ */
+
+/* Exec-OSM */
+extern struct i2o_driver i2o_exec_driver;
+extern int i2o_exec_lct_get(struct i2o_controller *);
+
+extern int __init i2o_exec_init(void);
+extern void i2o_exec_exit(void);
+
+/* driver */
+extern struct bus_type i2o_bus_type;
+
+extern int i2o_driver_dispatch(struct i2o_controller *, u32);
+
+extern int __init i2o_driver_init(void);
+extern void i2o_driver_exit(void);
+
+/* PCI */
+extern int __init i2o_pci_init(void);
+extern void __exit i2o_pci_exit(void);
+
+/* device */
+extern const struct attribute_group *i2o_device_groups[];
+
+extern void i2o_device_remove(struct i2o_device *);
+extern int i2o_device_parse_lct(struct i2o_controller *);
+
+int i2o_parm_issue(struct i2o_device *i2o_dev, int cmd, void *oplist,
+ int oplen, void *reslist, int reslen);
+
+/* IOP */
+extern struct i2o_controller *i2o_iop_alloc(void);
+
+/**
+ * i2o_iop_free - Free the i2o_controller struct
+ * @c: I2O controller to free
+ */
+static inline void i2o_iop_free(struct i2o_controller *c)
+{
+ i2o_pool_free(&c->in_msg);
+ kfree(c);
+}
+
+extern int i2o_iop_add(struct i2o_controller *);
+extern void i2o_iop_remove(struct i2o_controller *);
+
+/* control registers relative to c->base */
+#define I2O_IRQ_STATUS 0x30
+#define I2O_IRQ_MASK 0x34
+#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
diff --git a/drivers/staging/i2o/debug.c b/drivers/staging/i2o/debug.c
new file mode 100644
index 0000000..7a16114
--- /dev/null
+++ b/drivers/staging/i2o/debug.c
@@ -0,0 +1,472 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include "i2o.h"
+
+static void i2o_report_util_cmd(u8 cmd);
+static void i2o_report_exec_cmd(u8 cmd);
+static void i2o_report_fail_status(u8 req_status, u32 * msg);
+static void i2o_report_common_status(u8 req_status);
+static void i2o_report_common_dsc(u16 detailed_status);
+
+/*
+ * Used for error reporting/debugging purposes.
+ * Report Cmd name, Request status, Detailed Status.
+ */
+void i2o_report_status(const char *severity, const char *str,
+ struct i2o_message *m)
+{
+ u32 *msg = (u32 *) m;
+ u8 cmd = (msg[1] >> 24) & 0xFF;
+ u8 req_status = (msg[4] >> 24) & 0xFF;
+ u16 detailed_status = msg[4] & 0xFFFF;
+
+ if (cmd == I2O_CMD_UTIL_EVT_REGISTER)
+ return; // No status in this reply
+
+ printk("%s%s: ", severity, str);
+
+ if (cmd < 0x1F) // Utility cmd
+ i2o_report_util_cmd(cmd);
+
+ else if (cmd >= 0xA0 && cmd <= 0xEF) // Executive cmd
+ i2o_report_exec_cmd(cmd);
+ else
+ printk("Cmd = %0#2x, ", cmd); // Other cmds
+
+ if (msg[0] & MSG_FAIL) {
+ i2o_report_fail_status(req_status, msg);
+ return;
+ }
+
+ i2o_report_common_status(req_status);
+
+ if (cmd < 0x1F || (cmd >= 0xA0 && cmd <= 0xEF))
+ i2o_report_common_dsc(detailed_status);
+ else
+ printk(" / DetailedStatus = %0#4x.\n",
+ detailed_status);
+}
+
+/* Used to dump a message to syslog during debugging */
+void i2o_dump_message(struct i2o_message *m)
+{
+#ifdef DEBUG
+ u32 *msg = (u32 *) m;
+ int i;
+ printk(KERN_INFO "Dumping I2O message size %d @ %p\n",
+ msg[0] >> 16 & 0xffff, msg);
+ for (i = 0; i < ((msg[0] >> 16) & 0xffff); i++)
+ printk(KERN_INFO " msg[%d] = %0#10x\n", i, msg[i]);
+#endif
+}
+
+/*
+ * Used for error reporting/debugging purposes.
+ * Following fail status are common to all classes.
+ * The preserved message must be handled in the reply handler.
+ */
+static void i2o_report_fail_status(u8 req_status, u32 * msg)
+{
+ static char *FAIL_STATUS[] = {
+ "0x80", /* not used */
+ "SERVICE_SUSPENDED", /* 0x81 */
+ "SERVICE_TERMINATED", /* 0x82 */
+ "CONGESTION",
+ "FAILURE",
+ "STATE_ERROR",
+ "TIME_OUT",
+ "ROUTING_FAILURE",
+ "INVALID_VERSION",
+ "INVALID_OFFSET",
+ "INVALID_MSG_FLAGS",
+ "FRAME_TOO_SMALL",
+ "FRAME_TOO_LARGE",
+ "INVALID_TARGET_ID",
+ "INVALID_INITIATOR_ID",
+ "INVALID_INITIATOR_CONTEX", /* 0x8F */
+ "UNKNOWN_FAILURE" /* 0xFF */
+ };
+
+ if (req_status == I2O_FSC_TRANSPORT_UNKNOWN_FAILURE)
+ printk("TRANSPORT_UNKNOWN_FAILURE (%0#2x).\n",
+ req_status);
+ else
+ printk("TRANSPORT_%s.\n",
+ FAIL_STATUS[req_status & 0x0F]);
+
+ /* Dump some details */
+
+ printk(KERN_ERR " InitiatorId = %d, TargetId = %d\n",
+ (msg[1] >> 12) & 0xFFF, msg[1] & 0xFFF);
+ printk(KERN_ERR " LowestVersion = 0x%02X, HighestVersion = 0x%02X\n",
+ (msg[4] >> 8) & 0xFF, msg[4] & 0xFF);
+ printk(KERN_ERR " FailingHostUnit = 0x%04X, FailingIOP = 0x%03X\n",
+ msg[5] >> 16, msg[5] & 0xFFF);
+
+ printk(KERN_ERR " Severity: 0x%02X\n", (msg[4] >> 16) & 0xFF);
+ if (msg[4] & (1 << 16))
+ printk(KERN_DEBUG "(FormatError), "
+ "this msg can never be delivered/processed.\n");
+ if (msg[4] & (1 << 17))
+ printk(KERN_DEBUG "(PathError), "
+ "this msg can no longer be delivered/processed.\n");
+ if (msg[4] & (1 << 18))
+ printk(KERN_DEBUG "(PathState), "
+ "the system state does not allow delivery.\n");
+ if (msg[4] & (1 << 19))
+ printk(KERN_DEBUG
+ "(Congestion), resources temporarily not available;"
+ "do not retry immediately.\n");
+}
+
+/*
+ * Used for error reporting/debugging purposes.
+ * Following reply status are common to all classes.
+ */
+static void i2o_report_common_status(u8 req_status)
+{
+ static char *REPLY_STATUS[] = {
+ "SUCCESS",
+ "ABORT_DIRTY",
+ "ABORT_NO_DATA_TRANSFER",
+ "ABORT_PARTIAL_TRANSFER",
+ "ERROR_DIRTY",
+ "ERROR_NO_DATA_TRANSFER",
+ "ERROR_PARTIAL_TRANSFER",
+ "PROCESS_ABORT_DIRTY",
+ "PROCESS_ABORT_NO_DATA_TRANSFER",
+ "PROCESS_ABORT_PARTIAL_TRANSFER",
+ "TRANSACTION_ERROR",
+ "PROGRESS_REPORT"
+ };
+
+ if (req_status >= ARRAY_SIZE(REPLY_STATUS))
+ printk("RequestStatus = %0#2x", req_status);
+ else
+ printk("%s", REPLY_STATUS[req_status]);
+}
+
+/*
+ * Used for error reporting/debugging purposes.
+ * Following detailed status are valid for executive class,
+ * utility class, DDM class and for transaction error replies.
+ */
+static void i2o_report_common_dsc(u16 detailed_status)
+{
+ static char *COMMON_DSC[] = {
+ "SUCCESS",
+ "0x01", // not used
+ "BAD_KEY",
+ "TCL_ERROR",
+ "REPLY_BUFFER_FULL",
+ "NO_SUCH_PAGE",
+ "INSUFFICIENT_RESOURCE_SOFT",
+ "INSUFFICIENT_RESOURCE_HARD",
+ "0x08", // not used
+ "CHAIN_BUFFER_TOO_LARGE",
+ "UNSUPPORTED_FUNCTION",
+ "DEVICE_LOCKED",
+ "DEVICE_RESET",
+ "INAPPROPRIATE_FUNCTION",
+ "INVALID_INITIATOR_ADDRESS",
+ "INVALID_MESSAGE_FLAGS",
+ "INVALID_OFFSET",
+ "INVALID_PARAMETER",
+ "INVALID_REQUEST",
+ "INVALID_TARGET_ADDRESS",
+ "MESSAGE_TOO_LARGE",
+ "MESSAGE_TOO_SMALL",
+ "MISSING_PARAMETER",
+ "TIMEOUT",
+ "UNKNOWN_ERROR",
+ "UNKNOWN_FUNCTION",
+ "UNSUPPORTED_VERSION",
+ "DEVICE_BUSY",
+ "DEVICE_NOT_AVAILABLE"
+ };
+
+ if (detailed_status > I2O_DSC_DEVICE_NOT_AVAILABLE)
+ printk(" / DetailedStatus = %0#4x.\n",
+ detailed_status);
+ else
+ printk(" / %s.\n", COMMON_DSC[detailed_status]);
+}
+
+/*
+ * Used for error reporting/debugging purposes
+ */
+static void i2o_report_util_cmd(u8 cmd)
+{
+ switch (cmd) {
+ case I2O_CMD_UTIL_NOP:
+ printk("UTIL_NOP, ");
+ break;
+ case I2O_CMD_UTIL_ABORT:
+ printk("UTIL_ABORT, ");
+ break;
+ case I2O_CMD_UTIL_CLAIM:
+ printk("UTIL_CLAIM, ");
+ break;
+ case I2O_CMD_UTIL_RELEASE:
+ printk("UTIL_CLAIM_RELEASE, ");
+ break;
+ case I2O_CMD_UTIL_CONFIG_DIALOG:
+ printk("UTIL_CONFIG_DIALOG, ");
+ break;
+ case I2O_CMD_UTIL_DEVICE_RESERVE:
+ printk("UTIL_DEVICE_RESERVE, ");
+ break;
+ case I2O_CMD_UTIL_DEVICE_RELEASE:
+ printk("UTIL_DEVICE_RELEASE, ");
+ break;
+ case I2O_CMD_UTIL_EVT_ACK:
+ printk("UTIL_EVENT_ACKNOWLEDGE, ");
+ break;
+ case I2O_CMD_UTIL_EVT_REGISTER:
+ printk("UTIL_EVENT_REGISTER, ");
+ break;
+ case I2O_CMD_UTIL_LOCK:
+ printk("UTIL_LOCK, ");
+ break;
+ case I2O_CMD_UTIL_LOCK_RELEASE:
+ printk("UTIL_LOCK_RELEASE, ");
+ break;
+ case I2O_CMD_UTIL_PARAMS_GET:
+ printk("UTIL_PARAMS_GET, ");
+ break;
+ case I2O_CMD_UTIL_PARAMS_SET:
+ printk("UTIL_PARAMS_SET, ");
+ break;
+ case I2O_CMD_UTIL_REPLY_FAULT_NOTIFY:
+ printk("UTIL_REPLY_FAULT_NOTIFY, ");
+ break;
+ default:
+ printk("Cmd = %0#2x, ", cmd);
+ }
+}
+
+/*
+ * Used for error reporting/debugging purposes
+ */
+static void i2o_report_exec_cmd(u8 cmd)
+{
+ switch (cmd) {
+ case I2O_CMD_ADAPTER_ASSIGN:
+ printk("EXEC_ADAPTER_ASSIGN, ");
+ break;
+ case I2O_CMD_ADAPTER_READ:
+ printk("EXEC_ADAPTER_READ, ");
+ break;
+ case I2O_CMD_ADAPTER_RELEASE:
+ printk("EXEC_ADAPTER_RELEASE, ");
+ break;
+ case I2O_CMD_BIOS_INFO_SET:
+ printk("EXEC_BIOS_INFO_SET, ");
+ break;
+ case I2O_CMD_BOOT_DEVICE_SET:
+ printk("EXEC_BOOT_DEVICE_SET, ");
+ break;
+ case I2O_CMD_CONFIG_VALIDATE:
+ printk("EXEC_CONFIG_VALIDATE, ");
+ break;
+ case I2O_CMD_CONN_SETUP:
+ printk("EXEC_CONN_SETUP, ");
+ break;
+ case I2O_CMD_DDM_DESTROY:
+ printk("EXEC_DDM_DESTROY, ");
+ break;
+ case I2O_CMD_DDM_ENABLE:
+ printk("EXEC_DDM_ENABLE, ");
+ break;
+ case I2O_CMD_DDM_QUIESCE:
+ printk("EXEC_DDM_QUIESCE, ");
+ break;
+ case I2O_CMD_DDM_RESET:
+ printk("EXEC_DDM_RESET, ");
+ break;
+ case I2O_CMD_DDM_SUSPEND:
+ printk("EXEC_DDM_SUSPEND, ");
+ break;
+ case I2O_CMD_DEVICE_ASSIGN:
+ printk("EXEC_DEVICE_ASSIGN, ");
+ break;
+ case I2O_CMD_DEVICE_RELEASE:
+ printk("EXEC_DEVICE_RELEASE, ");
+ break;
+ case I2O_CMD_HRT_GET:
+ printk("EXEC_HRT_GET, ");
+ break;
+ case I2O_CMD_ADAPTER_CLEAR:
+ printk("EXEC_IOP_CLEAR, ");
+ break;
+ case I2O_CMD_ADAPTER_CONNECT:
+ printk("EXEC_IOP_CONNECT, ");
+ break;
+ case I2O_CMD_ADAPTER_RESET:
+ printk("EXEC_IOP_RESET, ");
+ break;
+ case I2O_CMD_LCT_NOTIFY:
+ printk("EXEC_LCT_NOTIFY, ");
+ break;
+ case I2O_CMD_OUTBOUND_INIT:
+ printk("EXEC_OUTBOUND_INIT, ");
+ break;
+ case I2O_CMD_PATH_ENABLE:
+ printk("EXEC_PATH_ENABLE, ");
+ break;
+ case I2O_CMD_PATH_QUIESCE:
+ printk("EXEC_PATH_QUIESCE, ");
+ break;
+ case I2O_CMD_PATH_RESET:
+ printk("EXEC_PATH_RESET, ");
+ break;
+ case I2O_CMD_STATIC_MF_CREATE:
+ printk("EXEC_STATIC_MF_CREATE, ");
+ break;
+ case I2O_CMD_STATIC_MF_RELEASE:
+ printk("EXEC_STATIC_MF_RELEASE, ");
+ break;
+ case I2O_CMD_STATUS_GET:
+ printk("EXEC_STATUS_GET, ");
+ break;
+ case I2O_CMD_SW_DOWNLOAD:
+ printk("EXEC_SW_DOWNLOAD, ");
+ break;
+ case I2O_CMD_SW_UPLOAD:
+ printk("EXEC_SW_UPLOAD, ");
+ break;
+ case I2O_CMD_SW_REMOVE:
+ printk("EXEC_SW_REMOVE, ");
+ break;
+ case I2O_CMD_SYS_ENABLE:
+ printk("EXEC_SYS_ENABLE, ");
+ break;
+ case I2O_CMD_SYS_MODIFY:
+ printk("EXEC_SYS_MODIFY, ");
+ break;
+ case I2O_CMD_SYS_QUIESCE:
+ printk("EXEC_SYS_QUIESCE, ");
+ break;
+ case I2O_CMD_SYS_TAB_SET:
+ printk("EXEC_SYS_TAB_SET, ");
+ break;
+ default:
+ printk("Cmd = %#02x, ", cmd);
+ }
+}
+
+void i2o_debug_state(struct i2o_controller *c)
+{
+ printk(KERN_INFO "%s: State = ", c->name);
+ switch (((i2o_status_block *) c->status_block.virt)->iop_state) {
+ case 0x01:
+ printk("INIT\n");
+ break;
+ case 0x02:
+ printk("RESET\n");
+ break;
+ case 0x04:
+ printk("HOLD\n");
+ break;
+ case 0x05:
+ printk("READY\n");
+ break;
+ case 0x08:
+ printk("OPERATIONAL\n");
+ break;
+ case 0x10:
+ printk("FAILED\n");
+ break;
+ case 0x11:
+ printk("FAULTED\n");
+ break;
+ default:
+ printk("%x (unknown !!)\n",
+ ((i2o_status_block *) c->status_block.virt)->iop_state);
+ }
+};
+
+void i2o_dump_hrt(struct i2o_controller *c)
+{
+ u32 *rows = (u32 *) c->hrt.virt;
+ u8 *p = (u8 *) c->hrt.virt;
+ u8 *d;
+ int count;
+ int length;
+ int i;
+ int state;
+
+ if (p[3] != 0) {
+ printk(KERN_ERR
+ "%s: HRT table for controller is too new a version.\n",
+ c->name);
+ return;
+ }
+
+ count = p[0] | (p[1] << 8);
+ length = p[2];
+
+ printk(KERN_INFO "%s: HRT has %d entries of %d bytes each.\n",
+ c->name, count, length << 2);
+
+ rows += 2;
+
+ for (i = 0; i < count; i++) {
+ printk(KERN_INFO "Adapter %08X: ", rows[0]);
+ p = (u8 *) (rows + 1);
+ d = (u8 *) (rows + 2);
+ state = p[1] << 8 | p[0];
+
+ printk("TID %04X:[", state & 0xFFF);
+ state >>= 12;
+ if (state & (1 << 0))
+ printk("H"); /* Hidden */
+ if (state & (1 << 2)) {
+ printk("P"); /* Present */
+ if (state & (1 << 1))
+ printk("C"); /* Controlled */
+ }
+ if (state > 9)
+ printk("*"); /* Hard */
+
+ printk("]:");
+
+ switch (p[3] & 0xFFFF) {
+ case 0:
+ /* Adapter private bus - easy */
+ printk("Local bus %d: I/O at 0x%04X Mem 0x%08X", p[2],
+ d[1] << 8 | d[0], *(u32 *) (d + 4));
+ break;
+ case 1:
+ /* ISA bus */
+ printk("ISA %d: CSN %d I/O at 0x%04X Mem 0x%08X", p[2],
+ d[2], d[1] << 8 | d[0], *(u32 *) (d + 4));
+ break;
+
+ case 2: /* EISA bus */
+ printk("EISA %d: Slot %d I/O at 0x%04X Mem 0x%08X",
+ p[2], d[3], d[1] << 8 | d[0], *(u32 *) (d + 4));
+ break;
+
+ case 3: /* MCA bus */
+ printk("MCA %d: Slot %d I/O at 0x%04X Mem 0x%08X", p[2],
+ d[3], d[1] << 8 | d[0], *(u32 *) (d + 4));
+ break;
+
+ case 4: /* PCI bus */
+ printk("PCI %d: Bus %d Device %d Function %d", p[2],
+ d[2], d[1], d[0]);
+ break;
+
+ case 0x80: /* Other */
+ default:
+ printk("Unsupported bus type.");
+ break;
+ }
+ printk("\n");
+ rows += length;
+ }
+}
+
+EXPORT_SYMBOL(i2o_dump_message);
diff --git a/drivers/staging/i2o/device.c b/drivers/staging/i2o/device.c
new file mode 100644
index 0000000..2af2255
--- /dev/null
+++ b/drivers/staging/i2o/device.c
@@ -0,0 +1,594 @@
+/*
+ * Functions to handle I2O devices
+ *
+ * Copyright (C) 2004 Markus Lidel <Markus.Lidel@shadowconnect.com>
+ *
+ * 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.
+ *
+ * Fixes/additions:
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>
+ * initial version.
+ */
+
+#include <linux/module.h>
+#include "i2o.h"
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "core.h"
+
+/**
+ * i2o_device_issue_claim - claim or release a device
+ * @dev: I2O device to claim or release
+ * @cmd: claim or release command
+ * @type: type of claim
+ *
+ * Issue I2O UTIL_CLAIM or UTIL_RELEASE messages. The message to be sent
+ * is set by cmd. dev is the I2O device which should be claim or
+ * released and the type is the claim type (see the I2O spec).
+ *
+ * Returs 0 on success or negative error code on failure.
+ */
+static inline int i2o_device_issue_claim(struct i2o_device *dev, u32 cmd,
+ u32 type)
+{
+ struct i2o_message *msg;
+
+ msg = i2o_msg_get_wait(dev->iop, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(cmd << 24 | HOST_TID << 12 | dev->lct_data.tid);
+ msg->body[0] = cpu_to_le32(type);
+
+ return i2o_msg_post_wait(dev->iop, msg, 60);
+}
+
+/**
+ * i2o_device_claim - claim a device for use by an OSM
+ * @dev: I2O device to claim
+ *
+ * Do the leg work to assign a device to a given OSM. If the claim succeeds,
+ * the owner is the primary. If the attempt fails a negative errno code
+ * is returned. On success zero is returned.
+ */
+int i2o_device_claim(struct i2o_device *dev)
+{
+ int rc = 0;
+
+ mutex_lock(&dev->lock);
+
+ rc = i2o_device_issue_claim(dev, I2O_CMD_UTIL_CLAIM, I2O_CLAIM_PRIMARY);
+ if (!rc)
+ pr_debug("i2o: claim of device %d succeeded\n",
+ dev->lct_data.tid);
+ else
+ pr_debug("i2o: claim of device %d failed %d\n",
+ dev->lct_data.tid, rc);
+
+ mutex_unlock(&dev->lock);
+
+ return rc;
+}
+
+/**
+ * i2o_device_claim_release - release a device that the OSM is using
+ * @dev: device to release
+ *
+ * Drop a claim by an OSM on a given I2O device.
+ *
+ * AC - some devices seem to want to refuse an unclaim until they have
+ * finished internal processing. It makes sense since you don't want a
+ * new device to go reconfiguring the entire system until you are done.
+ * Thus we are prepared to wait briefly.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+int i2o_device_claim_release(struct i2o_device *dev)
+{
+ int tries;
+ int rc = 0;
+
+ mutex_lock(&dev->lock);
+
+ /*
+ * If the controller takes a nonblocking approach to
+ * releases we have to sleep/poll for a few times.
+ */
+ for (tries = 0; tries < 10; tries++) {
+ rc = i2o_device_issue_claim(dev, I2O_CMD_UTIL_RELEASE,
+ I2O_CLAIM_PRIMARY);
+ if (!rc)
+ break;
+
+ ssleep(1);
+ }
+
+ if (!rc)
+ pr_debug("i2o: claim release of device %d succeeded\n",
+ dev->lct_data.tid);
+ else
+ pr_debug("i2o: claim release of device %d failed %d\n",
+ dev->lct_data.tid, rc);
+
+ mutex_unlock(&dev->lock);
+
+ return rc;
+}
+
+/**
+ * i2o_device_release - release the memory for a I2O device
+ * @dev: I2O device which should be released
+ *
+ * Release the allocated memory. This function is called if refcount of
+ * device reaches 0 automatically.
+ */
+static void i2o_device_release(struct device *dev)
+{
+ struct i2o_device *i2o_dev = to_i2o_device(dev);
+
+ pr_debug("i2o: device %s released\n", dev_name(dev));
+
+ kfree(i2o_dev);
+}
+
+/**
+ * class_id_show - Displays class id of I2O device
+ * @dev: device of which the class id should be displayed
+ * @attr: pointer to device attribute
+ * @buf: buffer into which the class id should be printed
+ *
+ * Returns the number of bytes which are printed into the buffer.
+ */
+static ssize_t class_id_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct i2o_device *i2o_dev = to_i2o_device(dev);
+
+ sprintf(buf, "0x%03x\n", i2o_dev->lct_data.class_id);
+ return strlen(buf) + 1;
+}
+static DEVICE_ATTR_RO(class_id);
+
+/**
+ * tid_show - Displays TID of I2O device
+ * @dev: device of which the TID should be displayed
+ * @attr: pointer to device attribute
+ * @buf: buffer into which the TID should be printed
+ *
+ * Returns the number of bytes which are printed into the buffer.
+ */
+static ssize_t tid_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct i2o_device *i2o_dev = to_i2o_device(dev);
+
+ sprintf(buf, "0x%03x\n", i2o_dev->lct_data.tid);
+ return strlen(buf) + 1;
+}
+static DEVICE_ATTR_RO(tid);
+
+/* I2O device attributes */
+static struct attribute *i2o_device_attrs[] = {
+ &dev_attr_class_id.attr,
+ &dev_attr_tid.attr,
+ NULL,
+};
+
+static const struct attribute_group i2o_device_group = {
+ .attrs = i2o_device_attrs,
+};
+
+const struct attribute_group *i2o_device_groups[] = {
+ &i2o_device_group,
+ NULL,
+};
+
+/**
+ * i2o_device_alloc - Allocate a I2O device and initialize it
+ *
+ * Allocate the memory for a I2O device and initialize locks and lists
+ *
+ * Returns the allocated I2O device or a negative error code if the device
+ * could not be allocated.
+ */
+static struct i2o_device *i2o_device_alloc(void)
+{
+ struct i2o_device *dev;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&dev->list);
+ mutex_init(&dev->lock);
+
+ dev->device.bus = &i2o_bus_type;
+ dev->device.release = &i2o_device_release;
+
+ return dev;
+}
+
+/**
+ * i2o_device_add - allocate a new I2O device and add it to the IOP
+ * @c: I2O controller that the device is on
+ * @entry: LCT entry of the I2O device
+ *
+ * Allocate a new I2O device and initialize it with the LCT entry. The
+ * device is appended to the device list of the controller.
+ *
+ * Returns zero on success, or a -ve errno.
+ */
+static int i2o_device_add(struct i2o_controller *c, i2o_lct_entry *entry)
+{
+ struct i2o_device *i2o_dev, *tmp;
+ int rc;
+
+ i2o_dev = i2o_device_alloc();
+ if (IS_ERR(i2o_dev)) {
+ printk(KERN_ERR "i2o: unable to allocate i2o device\n");
+ return PTR_ERR(i2o_dev);
+ }
+
+ i2o_dev->lct_data = *entry;
+
+ dev_set_name(&i2o_dev->device, "%d:%03x", c->unit,
+ i2o_dev->lct_data.tid);
+
+ i2o_dev->iop = c;
+ i2o_dev->device.parent = &c->device;
+
+ rc = device_register(&i2o_dev->device);
+ if (rc)
+ goto err;
+
+ list_add_tail(&i2o_dev->list, &c->devices);
+
+ /* create user entries for this device */
+ tmp = i2o_iop_find_device(i2o_dev->iop, i2o_dev->lct_data.user_tid);
+ if (tmp && (tmp != i2o_dev)) {
+ rc = sysfs_create_link(&i2o_dev->device.kobj,
+ &tmp->device.kobj, "user");
+ if (rc)
+ goto unreg_dev;
+ }
+
+ /* create user entries referring to this device */
+ list_for_each_entry(tmp, &c->devices, list)
+ if ((tmp->lct_data.user_tid == i2o_dev->lct_data.tid)
+ && (tmp != i2o_dev)) {
+ rc = sysfs_create_link(&tmp->device.kobj,
+ &i2o_dev->device.kobj, "user");
+ if (rc)
+ goto rmlink1;
+ }
+
+ /* create parent entries for this device */
+ tmp = i2o_iop_find_device(i2o_dev->iop, i2o_dev->lct_data.parent_tid);
+ if (tmp && (tmp != i2o_dev)) {
+ rc = sysfs_create_link(&i2o_dev->device.kobj,
+ &tmp->device.kobj, "parent");
+ if (rc)
+ goto rmlink1;
+ }
+
+ /* create parent entries referring to this device */
+ list_for_each_entry(tmp, &c->devices, list)
+ if ((tmp->lct_data.parent_tid == i2o_dev->lct_data.tid)
+ && (tmp != i2o_dev)) {
+ rc = sysfs_create_link(&tmp->device.kobj,
+ &i2o_dev->device.kobj, "parent");
+ if (rc)
+ goto rmlink2;
+ }
+
+ i2o_driver_notify_device_add_all(i2o_dev);
+
+ pr_debug("i2o: device %s added\n", dev_name(&i2o_dev->device));
+
+ return 0;
+
+rmlink2:
+ /* If link creating failed halfway, we loop whole list to cleanup.
+ * And we don't care wrong removing of link, because sysfs_remove_link
+ * will take care of it.
+ */
+ list_for_each_entry(tmp, &c->devices, list) {
+ if (tmp->lct_data.parent_tid == i2o_dev->lct_data.tid)
+ sysfs_remove_link(&tmp->device.kobj, "parent");
+ }
+ sysfs_remove_link(&i2o_dev->device.kobj, "parent");
+rmlink1:
+ list_for_each_entry(tmp, &c->devices, list)
+ if (tmp->lct_data.user_tid == i2o_dev->lct_data.tid)
+ sysfs_remove_link(&tmp->device.kobj, "user");
+ sysfs_remove_link(&i2o_dev->device.kobj, "user");
+unreg_dev:
+ list_del(&i2o_dev->list);
+ device_unregister(&i2o_dev->device);
+err:
+ kfree(i2o_dev);
+ return rc;
+}
+
+/**
+ * i2o_device_remove - remove an I2O device from the I2O core
+ * @i2o_dev: I2O device which should be released
+ *
+ * Is used on I2O controller removal or LCT modification, when the device
+ * is removed from the system. Note that the device could still hang
+ * around until the refcount reaches 0.
+ */
+void i2o_device_remove(struct i2o_device *i2o_dev)
+{
+ struct i2o_device *tmp;
+ struct i2o_controller *c = i2o_dev->iop;
+
+ i2o_driver_notify_device_remove_all(i2o_dev);
+
+ sysfs_remove_link(&i2o_dev->device.kobj, "parent");
+ sysfs_remove_link(&i2o_dev->device.kobj, "user");
+
+ list_for_each_entry(tmp, &c->devices, list) {
+ if (tmp->lct_data.parent_tid == i2o_dev->lct_data.tid)
+ sysfs_remove_link(&tmp->device.kobj, "parent");
+ if (tmp->lct_data.user_tid == i2o_dev->lct_data.tid)
+ sysfs_remove_link(&tmp->device.kobj, "user");
+ }
+ list_del(&i2o_dev->list);
+
+ device_unregister(&i2o_dev->device);
+}
+
+/**
+ * i2o_device_parse_lct - Parse a previously fetched LCT and create devices
+ * @c: I2O controller from which the LCT should be parsed.
+ *
+ * The Logical Configuration Table tells us what we can talk to on the
+ * board. For every entry we create an I2O device, which is registered in
+ * the I2O core.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+int i2o_device_parse_lct(struct i2o_controller *c)
+{
+ struct i2o_device *dev, *tmp;
+ i2o_lct *lct;
+ u32 *dlct = c->dlct.virt;
+ int max = 0, i = 0;
+ u16 table_size;
+ u32 buf;
+
+ mutex_lock(&c->lct_lock);
+
+ kfree(c->lct);
+
+ buf = le32_to_cpu(*dlct++);
+ table_size = buf & 0xffff;
+
+ lct = c->lct = kmalloc(table_size * 4, GFP_KERNEL);
+ if (!lct) {
+ mutex_unlock(&c->lct_lock);
+ return -ENOMEM;
+ }
+
+ lct->lct_ver = buf >> 28;
+ lct->boot_tid = buf >> 16 & 0xfff;
+ lct->table_size = table_size;
+ lct->change_ind = le32_to_cpu(*dlct++);
+ lct->iop_flags = le32_to_cpu(*dlct++);
+
+ table_size -= 3;
+
+ pr_debug("%s: LCT has %d entries (LCT size: %d)\n", c->name, max,
+ lct->table_size);
+
+ while (table_size > 0) {
+ i2o_lct_entry *entry = &lct->lct_entry[max];
+ int found = 0;
+
+ buf = le32_to_cpu(*dlct++);
+ entry->entry_size = buf & 0xffff;
+ entry->tid = buf >> 16 & 0xfff;
+
+ entry->change_ind = le32_to_cpu(*dlct++);
+ entry->device_flags = le32_to_cpu(*dlct++);
+
+ buf = le32_to_cpu(*dlct++);
+ entry->class_id = buf & 0xfff;
+ entry->version = buf >> 12 & 0xf;
+ entry->vendor_id = buf >> 16;
+
+ entry->sub_class = le32_to_cpu(*dlct++);
+
+ buf = le32_to_cpu(*dlct++);
+ entry->user_tid = buf & 0xfff;
+ entry->parent_tid = buf >> 12 & 0xfff;
+ entry->bios_info = buf >> 24;
+
+ memcpy(&entry->identity_tag, dlct, 8);
+ dlct += 2;
+
+ entry->event_capabilities = le32_to_cpu(*dlct++);
+
+ /* add new devices, which are new in the LCT */
+ list_for_each_entry_safe(dev, tmp, &c->devices, list) {
+ if (entry->tid == dev->lct_data.tid) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ i2o_device_add(c, entry);
+
+ table_size -= 9;
+ max++;
+ }
+
+ /* remove devices, which are not in the LCT anymore */
+ list_for_each_entry_safe(dev, tmp, &c->devices, list) {
+ int found = 0;
+
+ for (i = 0; i < max; i++) {
+ if (lct->lct_entry[i].tid == dev->lct_data.tid) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ i2o_device_remove(dev);
+ }
+
+ mutex_unlock(&c->lct_lock);
+
+ return 0;
+}
+
+/*
+ * Run time support routines
+ */
+
+/* Issue UTIL_PARAMS_GET or UTIL_PARAMS_SET
+ *
+ * This function can be used for all UtilParamsGet/Set operations.
+ * The OperationList is given in oplist-buffer,
+ * and results are returned in reslist-buffer.
+ * Note that the minimum sized reslist is 8 bytes and contains
+ * ResultCount, ErrorInfoSize, BlockStatus and BlockSize.
+ */
+int i2o_parm_issue(struct i2o_device *i2o_dev, int cmd, void *oplist,
+ int oplen, void *reslist, int reslen)
+{
+ struct i2o_message *msg;
+ int i = 0;
+ int rc;
+ struct i2o_dma res;
+ struct i2o_controller *c = i2o_dev->iop;
+ struct device *dev = &c->pdev->dev;
+
+ res.virt = NULL;
+
+ if (i2o_dma_alloc(dev, &res, reslen))
+ return -ENOMEM;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg)) {
+ i2o_dma_free(dev, &res);
+ return PTR_ERR(msg);
+ }
+
+ i = 0;
+ msg->u.head[1] =
+ cpu_to_le32(cmd << 24 | HOST_TID << 12 | i2o_dev->lct_data.tid);
+ msg->body[i++] = cpu_to_le32(0x00000000);
+ msg->body[i++] = cpu_to_le32(0x4C000000 | oplen); /* OperationList */
+ memcpy(&msg->body[i], oplist, oplen);
+ i += (oplen / 4 + (oplen % 4 ? 1 : 0));
+ msg->body[i++] = cpu_to_le32(0xD0000000 | res.len); /* ResultList */
+ msg->body[i++] = cpu_to_le32(res.phys);
+
+ msg->u.head[0] =
+ cpu_to_le32(I2O_MESSAGE_SIZE(i + sizeof(struct i2o_message) / 4) |
+ SGL_OFFSET_5);
+
+ rc = i2o_msg_post_wait_mem(c, msg, 10, &res);
+
+ /* This only looks like a memory leak - don't "fix" it. */
+ if (rc == -ETIMEDOUT)
+ return rc;
+
+ memcpy(reslist, res.virt, res.len);
+ i2o_dma_free(dev, &res);
+
+ return rc;
+}
+
+/*
+ * Query one field group value or a whole scalar group.
+ */
+int i2o_parm_field_get(struct i2o_device *i2o_dev, int group, int field,
+ void *buf, int buflen)
+{
+ u32 opblk[] = { cpu_to_le32(0x00000001),
+ cpu_to_le32((u16) group << 16 | I2O_PARAMS_FIELD_GET),
+ cpu_to_le32((s16) field << 16 | 0x00000001)
+ };
+ u8 *resblk; /* 8 bytes for header */
+ int rc;
+
+ resblk = kmalloc(buflen + 8, GFP_KERNEL);
+ if (!resblk)
+ return -ENOMEM;
+
+ rc = i2o_parm_issue(i2o_dev, I2O_CMD_UTIL_PARAMS_GET, opblk,
+ sizeof(opblk), resblk, buflen + 8);
+
+ memcpy(buf, resblk + 8, buflen); /* cut off header */
+
+ kfree(resblk);
+
+ return rc;
+}
+
+/*
+ * if oper == I2O_PARAMS_TABLE_GET, get from all rows
+ * if fieldcount == -1 return all fields
+ * ibuf and ibuflen are unused (use NULL, 0)
+ * else return specific fields
+ * ibuf contains fieldindexes
+ *
+ * if oper == I2O_PARAMS_LIST_GET, get from specific rows
+ * if fieldcount == -1 return all fields
+ * ibuf contains rowcount, keyvalues
+ * else return specific fields
+ * fieldcount is # of fieldindexes
+ * ibuf contains fieldindexes, rowcount, keyvalues
+ *
+ * You could also use directly function i2o_issue_params().
+ */
+int i2o_parm_table_get(struct i2o_device *dev, int oper, int group,
+ int fieldcount, void *ibuf, int ibuflen, void *resblk,
+ int reslen)
+{
+ u16 *opblk;
+ int size;
+
+ size = 10 + ibuflen;
+ if (size % 4)
+ size += 4 - size % 4;
+
+ opblk = kmalloc(size, GFP_KERNEL);
+ if (opblk == NULL) {
+ printk(KERN_ERR "i2o: no memory for query buffer.\n");
+ return -ENOMEM;
+ }
+
+ opblk[0] = 1; /* operation count */
+ opblk[1] = 0; /* pad */
+ opblk[2] = oper;
+ opblk[3] = group;
+ opblk[4] = fieldcount;
+ memcpy(opblk + 5, ibuf, ibuflen); /* other params */
+
+ size = i2o_parm_issue(dev, I2O_CMD_UTIL_PARAMS_GET, opblk,
+ size, resblk, reslen);
+
+ kfree(opblk);
+ if (size > reslen)
+ return reslen;
+
+ return size;
+}
+
+EXPORT_SYMBOL(i2o_device_claim);
+EXPORT_SYMBOL(i2o_device_claim_release);
+EXPORT_SYMBOL(i2o_parm_field_get);
+EXPORT_SYMBOL(i2o_parm_table_get);
+EXPORT_SYMBOL(i2o_parm_issue);
diff --git a/drivers/staging/i2o/driver.c b/drivers/staging/i2o/driver.c
new file mode 100644
index 0000000..111c3ed
--- /dev/null
+++ b/drivers/staging/i2o/driver.c
@@ -0,0 +1,382 @@
+/*
+ * Functions to handle I2O drivers (OSMs) and I2O bus type for sysfs
+ *
+ * Copyright (C) 2004 Markus Lidel <Markus.Lidel@shadowconnect.com>
+ *
+ * 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.
+ *
+ * Fixes/additions:
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>
+ * initial version.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/rwsem.h>
+#include "i2o.h"
+#include <linux/workqueue.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "core.h"
+
+#define OSM_NAME "i2o"
+
+/* max_drivers - Maximum I2O drivers (OSMs) which could be registered */
+static unsigned int i2o_max_drivers = I2O_MAX_DRIVERS;
+module_param_named(max_drivers, i2o_max_drivers, uint, 0);
+MODULE_PARM_DESC(max_drivers, "maximum number of OSM's to support");
+
+/* I2O drivers lock and array */
+static spinlock_t i2o_drivers_lock;
+static struct i2o_driver **i2o_drivers;
+
+/**
+ * i2o_bus_match - Tell if I2O device class id matches the class ids of the I2O driver (OSM)
+ * @dev: device which should be verified
+ * @drv: the driver to match against
+ *
+ * Used by the bus to check if the driver wants to handle the device.
+ *
+ * Returns 1 if the class ids of the driver match the class id of the
+ * device, otherwise 0.
+ */
+static int i2o_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct i2o_device *i2o_dev = to_i2o_device(dev);
+ struct i2o_driver *i2o_drv = to_i2o_driver(drv);
+ struct i2o_class_id *ids = i2o_drv->classes;
+
+ if (ids)
+ while (ids->class_id != I2O_CLASS_END) {
+ if (ids->class_id == i2o_dev->lct_data.class_id)
+ return 1;
+ ids++;
+ }
+ return 0;
+};
+
+/* I2O bus type */
+struct bus_type i2o_bus_type = {
+ .name = "i2o",
+ .match = i2o_bus_match,
+ .dev_groups = i2o_device_groups,
+};
+
+/**
+ * i2o_driver_register - Register a I2O driver (OSM) in the I2O core
+ * @drv: I2O driver which should be registered
+ *
+ * Registers the OSM drv in the I2O core and creates an event queues if
+ * necessary.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+int i2o_driver_register(struct i2o_driver *drv)
+{
+ struct i2o_controller *c;
+ int i;
+ int rc = 0;
+ unsigned long flags;
+
+ osm_debug("Register driver %s\n", drv->name);
+
+ if (drv->event) {
+ drv->event_queue = alloc_workqueue("%s", WQ_MEM_RECLAIM, 1,
+ drv->name);
+ if (!drv->event_queue) {
+ osm_err("Could not initialize event queue for driver "
+ "%s\n", drv->name);
+ return -EFAULT;
+ }
+ osm_debug("Event queue initialized for driver %s\n", drv->name);
+ } else
+ drv->event_queue = NULL;
+
+ drv->driver.name = drv->name;
+ drv->driver.bus = &i2o_bus_type;
+
+ spin_lock_irqsave(&i2o_drivers_lock, flags);
+
+ for (i = 0; i2o_drivers[i]; i++)
+ if (i >= i2o_max_drivers) {
+ osm_err("too many drivers registered, increase "
+ "max_drivers\n");
+ spin_unlock_irqrestore(&i2o_drivers_lock, flags);
+ rc = -EFAULT;
+ goto out;
+ }
+
+ drv->context = i;
+ i2o_drivers[i] = drv;
+
+ spin_unlock_irqrestore(&i2o_drivers_lock, flags);
+
+ osm_debug("driver %s gets context id %d\n", drv->name, drv->context);
+
+ list_for_each_entry(c, &i2o_controllers, list) {
+ struct i2o_device *i2o_dev;
+
+ i2o_driver_notify_controller_add(drv, c);
+ list_for_each_entry(i2o_dev, &c->devices, list)
+ i2o_driver_notify_device_add(drv, i2o_dev);
+ }
+
+ rc = driver_register(&drv->driver);
+ if (rc)
+ goto out;
+
+ return 0;
+out:
+ if (drv->event_queue) {
+ destroy_workqueue(drv->event_queue);
+ drv->event_queue = NULL;
+ }
+
+ return rc;
+};
+
+/**
+ * i2o_driver_unregister - Unregister a I2O driver (OSM) from the I2O core
+ * @drv: I2O driver which should be unregistered
+ *
+ * Unregisters the OSM drv from the I2O core and cleanup event queues if
+ * necessary.
+ */
+void i2o_driver_unregister(struct i2o_driver *drv)
+{
+ struct i2o_controller *c;
+ unsigned long flags;
+
+ osm_debug("unregister driver %s\n", drv->name);
+
+ driver_unregister(&drv->driver);
+
+ list_for_each_entry(c, &i2o_controllers, list) {
+ struct i2o_device *i2o_dev;
+
+ list_for_each_entry(i2o_dev, &c->devices, list)
+ i2o_driver_notify_device_remove(drv, i2o_dev);
+
+ i2o_driver_notify_controller_remove(drv, c);
+ }
+
+ spin_lock_irqsave(&i2o_drivers_lock, flags);
+ i2o_drivers[drv->context] = NULL;
+ spin_unlock_irqrestore(&i2o_drivers_lock, flags);
+
+ if (drv->event_queue) {
+ destroy_workqueue(drv->event_queue);
+ drv->event_queue = NULL;
+ osm_debug("event queue removed for %s\n", drv->name);
+ }
+};
+
+/**
+ * i2o_driver_dispatch - dispatch an I2O reply message
+ * @c: I2O controller of the message
+ * @m: I2O message number
+ *
+ * The reply is delivered to the driver from which the original message
+ * was. This function is only called from interrupt context.
+ *
+ * Returns 0 on success and the message should not be flushed. Returns > 0
+ * on success and if the message should be flushed afterwords. Returns
+ * negative error code on failure (the message will be flushed too).
+ */
+int i2o_driver_dispatch(struct i2o_controller *c, u32 m)
+{
+ struct i2o_driver *drv;
+ struct i2o_message *msg = i2o_msg_out_to_virt(c, m);
+ u32 context = le32_to_cpu(msg->u.s.icntxt);
+ unsigned long flags;
+
+ if (unlikely(context >= i2o_max_drivers)) {
+ osm_warn("%s: Spurious reply to unknown driver %d\n", c->name,
+ context);
+ return -EIO;
+ }
+
+ spin_lock_irqsave(&i2o_drivers_lock, flags);
+ drv = i2o_drivers[context];
+ spin_unlock_irqrestore(&i2o_drivers_lock, flags);
+
+ if (unlikely(!drv)) {
+ osm_warn("%s: Spurious reply to unknown driver %d\n", c->name,
+ context);
+ return -EIO;
+ }
+
+ if ((le32_to_cpu(msg->u.head[1]) >> 24) == I2O_CMD_UTIL_EVT_REGISTER) {
+ struct i2o_device *dev, *tmp;
+ struct i2o_event *evt;
+ u16 size;
+ u16 tid = le32_to_cpu(msg->u.head[1]) & 0xfff;
+
+ osm_debug("event received from device %d\n", tid);
+
+ if (!drv->event)
+ return -EIO;
+
+ /* cut of header from message size (in 32-bit words) */
+ size = (le32_to_cpu(msg->u.head[0]) >> 16) - 5;
+
+ evt = kzalloc(size * 4 + sizeof(*evt), GFP_ATOMIC);
+ if (!evt)
+ return -ENOMEM;
+
+ evt->size = size;
+ evt->tcntxt = le32_to_cpu(msg->u.s.tcntxt);
+ evt->event_indicator = le32_to_cpu(msg->body[0]);
+ memcpy(&evt->data, &msg->body[1], size * 4);
+
+ list_for_each_entry_safe(dev, tmp, &c->devices, list)
+ if (dev->lct_data.tid == tid) {
+ evt->i2o_dev = dev;
+ break;
+ }
+
+ INIT_WORK(&evt->work, drv->event);
+ queue_work(drv->event_queue, &evt->work);
+ return 1;
+ }
+
+ if (unlikely(!drv->reply)) {
+ osm_debug("%s: Reply to driver %s, but no reply function"
+ " defined!\n", c->name, drv->name);
+ return -EIO;
+ }
+
+ return drv->reply(c, m, msg);
+}
+
+/**
+ * i2o_driver_notify_controller_add_all - Send notify of added controller
+ * @c: newly added controller
+ *
+ * Send notifications to all registered drivers that a new controller was
+ * added.
+ */
+void i2o_driver_notify_controller_add_all(struct i2o_controller *c)
+{
+ int i;
+ struct i2o_driver *drv;
+
+ for (i = 0; i < i2o_max_drivers; i++) {
+ drv = i2o_drivers[i];
+
+ if (drv)
+ i2o_driver_notify_controller_add(drv, c);
+ }
+}
+
+/**
+ * i2o_driver_notify_controller_remove_all - Send notify of removed controller
+ * @c: controller that is being removed
+ *
+ * Send notifications to all registered drivers that a controller was
+ * removed.
+ */
+void i2o_driver_notify_controller_remove_all(struct i2o_controller *c)
+{
+ int i;
+ struct i2o_driver *drv;
+
+ for (i = 0; i < i2o_max_drivers; i++) {
+ drv = i2o_drivers[i];
+
+ if (drv)
+ i2o_driver_notify_controller_remove(drv, c);
+ }
+}
+
+/**
+ * i2o_driver_notify_device_add_all - Send notify of added device
+ * @i2o_dev: newly added I2O device
+ *
+ * Send notifications to all registered drivers that a device was added.
+ */
+void i2o_driver_notify_device_add_all(struct i2o_device *i2o_dev)
+{
+ int i;
+ struct i2o_driver *drv;
+
+ for (i = 0; i < i2o_max_drivers; i++) {
+ drv = i2o_drivers[i];
+
+ if (drv)
+ i2o_driver_notify_device_add(drv, i2o_dev);
+ }
+}
+
+/**
+ * i2o_driver_notify_device_remove_all - Send notify of removed device
+ * @i2o_dev: device that is being removed
+ *
+ * Send notifications to all registered drivers that a device was removed.
+ */
+void i2o_driver_notify_device_remove_all(struct i2o_device *i2o_dev)
+{
+ int i;
+ struct i2o_driver *drv;
+
+ for (i = 0; i < i2o_max_drivers; i++) {
+ drv = i2o_drivers[i];
+
+ if (drv)
+ i2o_driver_notify_device_remove(drv, i2o_dev);
+ }
+}
+
+/**
+ * i2o_driver_init - initialize I2O drivers (OSMs)
+ *
+ * Registers the I2O bus and allocate memory for the array of OSMs.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+int __init i2o_driver_init(void)
+{
+ int rc = 0;
+
+ spin_lock_init(&i2o_drivers_lock);
+
+ if ((i2o_max_drivers < 2) || (i2o_max_drivers > 64)) {
+ osm_warn("max_drivers set to %d, but must be >=2 and <= 64\n",
+ i2o_max_drivers);
+ i2o_max_drivers = I2O_MAX_DRIVERS;
+ }
+ osm_info("max drivers = %d\n", i2o_max_drivers);
+
+ i2o_drivers =
+ kcalloc(i2o_max_drivers, sizeof(*i2o_drivers), GFP_KERNEL);
+ if (!i2o_drivers)
+ return -ENOMEM;
+
+ rc = bus_register(&i2o_bus_type);
+
+ if (rc < 0)
+ kfree(i2o_drivers);
+
+ return rc;
+};
+
+/**
+ * i2o_driver_exit - clean up I2O drivers (OSMs)
+ *
+ * Unregisters the I2O bus and frees driver array.
+ */
+void i2o_driver_exit(void)
+{
+ bus_unregister(&i2o_bus_type);
+ kfree(i2o_drivers);
+};
+
+EXPORT_SYMBOL(i2o_driver_register);
+EXPORT_SYMBOL(i2o_driver_unregister);
+EXPORT_SYMBOL(i2o_driver_notify_controller_add_all);
+EXPORT_SYMBOL(i2o_driver_notify_controller_remove_all);
+EXPORT_SYMBOL(i2o_driver_notify_device_add_all);
+EXPORT_SYMBOL(i2o_driver_notify_device_remove_all);
diff --git a/drivers/staging/i2o/exec-osm.c b/drivers/staging/i2o/exec-osm.c
new file mode 100644
index 0000000..16d857d
--- /dev/null
+++ b/drivers/staging/i2o/exec-osm.c
@@ -0,0 +1,612 @@
+/*
+ * Executive OSM
+ *
+ * Copyright (C) 1999-2002 Red Hat Software
+ *
+ * Written by Alan Cox, Building Number Three Ltd
+ *
+ * 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.
+ *
+ * A lot of the I2O message side code from this is taken from the Red
+ * Creek RCPCI45 adapter driver by Red Creek Communications
+ *
+ * Fixes/additions:
+ * Philipp Rumpf
+ * Juha Sievänen <Juha.Sievanen@cs.Helsinki.FI>
+ * Auvo Häkkinen <Auvo.Hakkinen@cs.Helsinki.FI>
+ * Deepak Saxena <deepak@plexity.net>
+ * Boji T Kannanthanam <boji.t.kannanthanam@intel.com>
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>:
+ * Ported to Linux 2.5.
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>:
+ * Minor fixes for 2.6.
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>:
+ * Support for sysfs included.
+ */
+
+#include <linux/module.h>
+#include "i2o.h"
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/sched.h> /* wait_event_interruptible_timeout() needs this */
+#include <asm/param.h> /* HZ */
+#include "core.h"
+
+#define OSM_NAME "exec-osm"
+
+struct i2o_driver i2o_exec_driver;
+
+/* global wait list for POST WAIT */
+static LIST_HEAD(i2o_exec_wait_list);
+
+/* Wait struct needed for POST WAIT */
+struct i2o_exec_wait {
+ wait_queue_head_t *wq; /* Pointer to Wait queue */
+ struct i2o_dma dma; /* DMA buffers to free on failure */
+ u32 tcntxt; /* transaction context from reply */
+ int complete; /* 1 if reply received otherwise 0 */
+ u32 m; /* message id */
+ struct i2o_message *msg; /* pointer to the reply message */
+ struct list_head list; /* node in global wait list */
+ spinlock_t lock; /* lock before modifying */
+};
+
+/* Work struct needed to handle LCT NOTIFY replies */
+struct i2o_exec_lct_notify_work {
+ struct work_struct work; /* work struct */
+ struct i2o_controller *c; /* controller on which the LCT NOTIFY
+ was received */
+};
+
+/* Exec OSM class handling definition */
+static struct i2o_class_id i2o_exec_class_id[] = {
+ {I2O_CLASS_EXECUTIVE},
+ {I2O_CLASS_END}
+};
+
+/**
+ * i2o_exec_wait_alloc - Allocate a i2o_exec_wait struct an initialize it
+ *
+ * Allocate the i2o_exec_wait struct and initialize the wait.
+ *
+ * Returns i2o_exec_wait pointer on success or negative error code on
+ * failure.
+ */
+static struct i2o_exec_wait *i2o_exec_wait_alloc(void)
+{
+ struct i2o_exec_wait *wait;
+
+ wait = kzalloc(sizeof(*wait), GFP_KERNEL);
+ if (!wait)
+ return NULL;
+
+ INIT_LIST_HEAD(&wait->list);
+ spin_lock_init(&wait->lock);
+
+ return wait;
+};
+
+/**
+ * i2o_exec_wait_free - Free an i2o_exec_wait struct
+ * @wait: I2O wait data which should be cleaned up
+ */
+static void i2o_exec_wait_free(struct i2o_exec_wait *wait)
+{
+ kfree(wait);
+};
+
+/**
+ * i2o_msg_post_wait_mem - Post and wait a message with DMA buffers
+ * @c: controller
+ * @msg: message to post
+ * @timeout: time in seconds to wait
+ * @dma: i2o_dma struct of the DMA buffer to free on failure
+ *
+ * This API allows an OSM to post a message and then be told whether or
+ * not the system received a successful reply. If the message times out
+ * then the value '-ETIMEDOUT' is returned. This is a special case. In
+ * this situation the message may (should) complete at an indefinite time
+ * in the future. When it completes it will use the memory buffer
+ * attached to the request. If -ETIMEDOUT is returned then the memory
+ * buffer must not be freed. Instead the event completion will free them
+ * for you. In all other cases the buffer are your problem.
+ *
+ * Returns 0 on success, negative error code on timeout or positive error
+ * code from reply.
+ */
+int i2o_msg_post_wait_mem(struct i2o_controller *c, struct i2o_message *msg,
+ unsigned long timeout, struct i2o_dma *dma)
+{
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
+ struct i2o_exec_wait *wait;
+ static u32 tcntxt = 0x80000000;
+ unsigned long flags;
+ int rc = 0;
+
+ wait = i2o_exec_wait_alloc();
+ if (!wait) {
+ i2o_msg_nop(c, msg);
+ return -ENOMEM;
+ }
+
+ if (tcntxt == 0xffffffff)
+ tcntxt = 0x80000000;
+
+ if (dma)
+ wait->dma = *dma;
+
+ /*
+ * Fill in the message initiator context and transaction context.
+ * We will only use transaction contexts >= 0x80000000 for POST WAIT,
+ * so we could find a POST WAIT reply easier in the reply handler.
+ */
+ msg->u.s.icntxt = cpu_to_le32(i2o_exec_driver.context);
+ wait->tcntxt = tcntxt++;
+ msg->u.s.tcntxt = cpu_to_le32(wait->tcntxt);
+
+ wait->wq = &wq;
+ /*
+ * we add elements to the head, because if a entry in the list will
+ * never be removed, we have to iterate over it every time
+ */
+ list_add(&wait->list, &i2o_exec_wait_list);
+
+ /*
+ * Post the message to the controller. At some point later it will
+ * return. If we time out before it returns then complete will be zero.
+ */
+ i2o_msg_post(c, msg);
+
+ wait_event_interruptible_timeout(wq, wait->complete, timeout * HZ);
+
+ spin_lock_irqsave(&wait->lock, flags);
+
+ wait->wq = NULL;
+
+ if (wait->complete)
+ rc = le32_to_cpu(wait->msg->body[0]) >> 24;
+ else {
+ /*
+ * We cannot remove it now. This is important. When it does
+ * terminate (which it must do if the controller has not
+ * died...) then it will otherwise scribble on stuff.
+ *
+ * FIXME: try abort message
+ */
+ if (dma)
+ dma->virt = NULL;
+
+ rc = -ETIMEDOUT;
+ }
+
+ spin_unlock_irqrestore(&wait->lock, flags);
+
+ if (rc != -ETIMEDOUT) {
+ i2o_flush_reply(c, wait->m);
+ i2o_exec_wait_free(wait);
+ }
+
+ return rc;
+};
+
+/**
+ * i2o_msg_post_wait_complete - Reply to a i2o_msg_post request from IOP
+ * @c: I2O controller which answers
+ * @m: message id
+ * @msg: pointer to the I2O reply message
+ * @context: transaction context of request
+ *
+ * This function is called in interrupt context only. If the reply reached
+ * before the timeout, the i2o_exec_wait struct is filled with the message
+ * and the task will be waked up. The task is now responsible for returning
+ * the message m back to the controller! If the message reaches us after
+ * the timeout clean up the i2o_exec_wait struct (including allocated
+ * DMA buffer).
+ *
+ * Return 0 on success and if the message m should not be given back to the
+ * I2O controller, or >0 on success and if the message should be given back
+ * afterwords. Returns negative error code on failure. In this case the
+ * message must also be given back to the controller.
+ */
+static int i2o_msg_post_wait_complete(struct i2o_controller *c, u32 m,
+ struct i2o_message *msg, u32 context)
+{
+ struct i2o_exec_wait *wait, *tmp;
+ unsigned long flags;
+ int rc = 1;
+
+ /*
+ * We need to search through the i2o_exec_wait_list to see if the given
+ * message is still outstanding. If not, it means that the IOP took
+ * longer to respond to the message than we had allowed and timer has
+ * already expired. Not much we can do about that except log it for
+ * debug purposes, increase timeout, and recompile.
+ */
+ list_for_each_entry_safe(wait, tmp, &i2o_exec_wait_list, list) {
+ if (wait->tcntxt == context) {
+ spin_lock_irqsave(&wait->lock, flags);
+
+ list_del(&wait->list);
+
+ wait->m = m;
+ wait->msg = msg;
+ wait->complete = 1;
+
+ if (wait->wq)
+ rc = 0;
+ else
+ rc = -1;
+
+ spin_unlock_irqrestore(&wait->lock, flags);
+
+ if (rc) {
+ struct device *dev;
+
+ dev = &c->pdev->dev;
+
+ pr_debug("%s: timedout reply received!\n",
+ c->name);
+ i2o_dma_free(dev, &wait->dma);
+ i2o_exec_wait_free(wait);
+ } else
+ wake_up_interruptible(wait->wq);
+
+ return rc;
+ }
+ }
+
+ osm_warn("%s: Bogus reply in POST WAIT (tr-context: %08x)!\n", c->name,
+ context);
+
+ return -1;
+};
+
+/**
+ * i2o_exec_show_vendor_id - Displays Vendor ID of controller
+ * @d: device of which the Vendor ID should be displayed
+ * @attr: device_attribute to display
+ * @buf: buffer into which the Vendor ID should be printed
+ *
+ * Returns number of bytes printed into buffer.
+ */
+static ssize_t i2o_exec_show_vendor_id(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2o_device *dev = to_i2o_device(d);
+ u16 id;
+
+ if (!i2o_parm_field_get(dev, 0x0000, 0, &id, 2)) {
+ sprintf(buf, "0x%04x", le16_to_cpu(id));
+ return strlen(buf) + 1;
+ }
+
+ return 0;
+};
+
+/**
+ * i2o_exec_show_product_id - Displays Product ID of controller
+ * @d: device of which the Product ID should be displayed
+ * @attr: device_attribute to display
+ * @buf: buffer into which the Product ID should be printed
+ *
+ * Returns number of bytes printed into buffer.
+ */
+static ssize_t i2o_exec_show_product_id(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct i2o_device *dev = to_i2o_device(d);
+ u16 id;
+
+ if (!i2o_parm_field_get(dev, 0x0000, 1, &id, 2)) {
+ sprintf(buf, "0x%04x", le16_to_cpu(id));
+ return strlen(buf) + 1;
+ }
+
+ return 0;
+};
+
+/* Exec-OSM device attributes */
+static DEVICE_ATTR(vendor_id, S_IRUGO, i2o_exec_show_vendor_id, NULL);
+static DEVICE_ATTR(product_id, S_IRUGO, i2o_exec_show_product_id, NULL);
+
+/**
+ * i2o_exec_probe - Called if a new I2O device (executive class) appears
+ * @dev: I2O device which should be probed
+ *
+ * Registers event notification for every event from Executive device. The
+ * return is always 0, because we want all devices of class Executive.
+ *
+ * Returns 0 on success.
+ */
+static int i2o_exec_probe(struct device *dev)
+{
+ struct i2o_device *i2o_dev = to_i2o_device(dev);
+ int rc;
+
+ rc = i2o_event_register(i2o_dev, &i2o_exec_driver, 0, 0xffffffff);
+ if (rc) goto err_out;
+
+ rc = device_create_file(dev, &dev_attr_vendor_id);
+ if (rc) goto err_evtreg;
+ rc = device_create_file(dev, &dev_attr_product_id);
+ if (rc) goto err_vid;
+
+ i2o_dev->iop->exec = i2o_dev;
+
+ return 0;
+
+err_vid:
+ device_remove_file(dev, &dev_attr_vendor_id);
+err_evtreg:
+ i2o_event_register(to_i2o_device(dev), &i2o_exec_driver, 0, 0);
+err_out:
+ return rc;
+};
+
+/**
+ * i2o_exec_remove - Called on I2O device removal
+ * @dev: I2O device which was removed
+ *
+ * Unregisters event notification from Executive I2O device.
+ *
+ * Returns 0 on success.
+ */
+static int i2o_exec_remove(struct device *dev)
+{
+ device_remove_file(dev, &dev_attr_product_id);
+ device_remove_file(dev, &dev_attr_vendor_id);
+
+ i2o_event_register(to_i2o_device(dev), &i2o_exec_driver, 0, 0);
+
+ return 0;
+};
+
+#ifdef CONFIG_I2O_LCT_NOTIFY_ON_CHANGES
+/**
+ * i2o_exec_lct_notify - Send a asynchronus LCT NOTIFY request
+ * @c: I2O controller to which the request should be send
+ * @change_ind: change indicator
+ *
+ * This function sends a LCT NOTIFY request to the I2O controller with
+ * the change indicator change_ind. If the change_ind == 0 the controller
+ * replies immediately after the request. If change_ind > 0 the reply is
+ * send after change indicator of the LCT is > change_ind.
+ */
+static int i2o_exec_lct_notify(struct i2o_controller *c, u32 change_ind)
+{
+ i2o_status_block *sb = c->status_block.virt;
+ struct device *dev;
+ struct i2o_message *msg;
+
+ mutex_lock(&c->lct_lock);
+
+ dev = &c->pdev->dev;
+
+ if (i2o_dma_realloc(dev, &c->dlct,
+ le32_to_cpu(sb->expected_lct_size))) {
+ mutex_unlock(&c->lct_lock);
+ return -ENOMEM;
+ }
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg)) {
+ mutex_unlock(&c->lct_lock);
+ return PTR_ERR(msg);
+ }
+
+ msg->u.head[0] = cpu_to_le32(EIGHT_WORD_MSG_SIZE | SGL_OFFSET_6);
+ msg->u.head[1] = cpu_to_le32(I2O_CMD_LCT_NOTIFY << 24 | HOST_TID << 12 |
+ ADAPTER_TID);
+ msg->u.s.icntxt = cpu_to_le32(i2o_exec_driver.context);
+ msg->u.s.tcntxt = cpu_to_le32(0x00000000);
+ msg->body[0] = cpu_to_le32(0xffffffff);
+ msg->body[1] = cpu_to_le32(change_ind);
+ msg->body[2] = cpu_to_le32(0xd0000000 | c->dlct.len);
+ msg->body[3] = cpu_to_le32(c->dlct.phys);
+
+ i2o_msg_post(c, msg);
+
+ mutex_unlock(&c->lct_lock);
+
+ return 0;
+}
+#endif
+
+/**
+ * i2o_exec_lct_modified - Called on LCT NOTIFY reply
+ * @_work: work struct for a specific controller
+ *
+ * This function handles asynchronus LCT NOTIFY replies. It parses the
+ * new LCT and if the buffer for the LCT was to small sends a LCT NOTIFY
+ * again, otherwise send LCT NOTIFY to get informed on next LCT change.
+ */
+static void i2o_exec_lct_modified(struct work_struct *_work)
+{
+ struct i2o_exec_lct_notify_work *work =
+ container_of(_work, struct i2o_exec_lct_notify_work, work);
+ u32 change_ind = 0;
+ struct i2o_controller *c = work->c;
+
+ kfree(work);
+
+ if (i2o_device_parse_lct(c) != -EAGAIN)
+ change_ind = c->lct->change_ind + 1;
+
+#ifdef CONFIG_I2O_LCT_NOTIFY_ON_CHANGES
+ i2o_exec_lct_notify(c, change_ind);
+#endif
+};
+
+/**
+ * i2o_exec_reply - I2O Executive reply handler
+ * @c: I2O controller from which the reply comes
+ * @m: message id
+ * @msg: pointer to the I2O reply message
+ *
+ * This function is always called from interrupt context. If a POST WAIT
+ * reply was received, pass it to the complete function. If a LCT NOTIFY
+ * reply was received, a new event is created to handle the update.
+ *
+ * Returns 0 on success and if the reply should not be flushed or > 0
+ * on success and if the reply should be flushed. Returns negative error
+ * code on failure and if the reply should be flushed.
+ */
+static int i2o_exec_reply(struct i2o_controller *c, u32 m,
+ struct i2o_message *msg)
+{
+ u32 context;
+
+ if (le32_to_cpu(msg->u.head[0]) & MSG_FAIL) {
+ struct i2o_message __iomem *pmsg;
+ u32 pm;
+
+ /*
+ * If Fail bit is set we must take the transaction context of
+ * the preserved message to find the right request again.
+ */
+
+ pm = le32_to_cpu(msg->body[3]);
+ pmsg = i2o_msg_in_to_virt(c, pm);
+ context = readl(&pmsg->u.s.tcntxt);
+
+ i2o_report_status(KERN_INFO, "i2o_core", msg);
+
+ /* Release the preserved msg */
+ i2o_msg_nop_mfa(c, pm);
+ } else
+ context = le32_to_cpu(msg->u.s.tcntxt);
+
+ if (context & 0x80000000)
+ return i2o_msg_post_wait_complete(c, m, msg, context);
+
+ if ((le32_to_cpu(msg->u.head[1]) >> 24) == I2O_CMD_LCT_NOTIFY) {
+ struct i2o_exec_lct_notify_work *work;
+
+ pr_debug("%s: LCT notify received\n", c->name);
+
+ work = kmalloc(sizeof(*work), GFP_ATOMIC);
+ if (!work)
+ return -ENOMEM;
+
+ work->c = c;
+
+ INIT_WORK(&work->work, i2o_exec_lct_modified);
+ queue_work(i2o_exec_driver.event_queue, &work->work);
+ return 1;
+ }
+
+ /*
+ * If this happens, we want to dump the message to the syslog so
+ * it can be sent back to the card manufacturer by the end user
+ * to aid in debugging.
+ *
+ */
+ printk(KERN_WARNING "%s: Unsolicited message reply sent to core!"
+ "Message dumped to syslog\n", c->name);
+ i2o_dump_message(msg);
+
+ return -EFAULT;
+}
+
+/**
+ * i2o_exec_event - Event handling function
+ * @work: Work item in occurring event
+ *
+ * Handles events send by the Executive device. At the moment does not do
+ * anything useful.
+ */
+static void i2o_exec_event(struct work_struct *work)
+{
+ struct i2o_event *evt = container_of(work, struct i2o_event, work);
+
+ if (likely(evt->i2o_dev))
+ osm_debug("Event received from device: %d\n",
+ evt->i2o_dev->lct_data.tid);
+ kfree(evt);
+};
+
+/**
+ * i2o_exec_lct_get - Get the IOP's Logical Configuration Table
+ * @c: I2O controller from which the LCT should be fetched
+ *
+ * Send a LCT NOTIFY request to the controller, and wait
+ * I2O_TIMEOUT_LCT_GET seconds until arrival of response. If the LCT is
+ * to large, retry it.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+int i2o_exec_lct_get(struct i2o_controller *c)
+{
+ struct i2o_message *msg;
+ int i = 0;
+ int rc = -EAGAIN;
+
+ for (i = 1; i <= I2O_LCT_GET_TRIES; i++) {
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] =
+ cpu_to_le32(EIGHT_WORD_MSG_SIZE | SGL_OFFSET_6);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_LCT_NOTIFY << 24 | HOST_TID << 12 |
+ ADAPTER_TID);
+ msg->body[0] = cpu_to_le32(0xffffffff);
+ msg->body[1] = cpu_to_le32(0x00000000);
+ msg->body[2] = cpu_to_le32(0xd0000000 | c->dlct.len);
+ msg->body[3] = cpu_to_le32(c->dlct.phys);
+
+ rc = i2o_msg_post_wait(c, msg, I2O_TIMEOUT_LCT_GET);
+ if (rc < 0)
+ break;
+
+ rc = i2o_device_parse_lct(c);
+ if (rc != -EAGAIN)
+ break;
+ }
+
+ return rc;
+}
+
+/* Exec OSM driver struct */
+struct i2o_driver i2o_exec_driver = {
+ .name = OSM_NAME,
+ .reply = i2o_exec_reply,
+ .event = i2o_exec_event,
+ .classes = i2o_exec_class_id,
+ .driver = {
+ .probe = i2o_exec_probe,
+ .remove = i2o_exec_remove,
+ },
+};
+
+/**
+ * i2o_exec_init - Registers the Exec OSM
+ *
+ * Registers the Exec OSM in the I2O core.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+int __init i2o_exec_init(void)
+{
+ return i2o_driver_register(&i2o_exec_driver);
+};
+
+/**
+ * i2o_exec_exit - Removes the Exec OSM
+ *
+ * Unregisters the Exec OSM from the I2O core.
+ */
+void i2o_exec_exit(void)
+{
+ i2o_driver_unregister(&i2o_exec_driver);
+};
+
+EXPORT_SYMBOL(i2o_msg_post_wait_mem);
+EXPORT_SYMBOL(i2o_exec_lct_get);
diff --git a/drivers/staging/i2o/i2o.h b/drivers/staging/i2o/i2o.h
new file mode 100644
index 0000000..d23c3c2
--- /dev/null
+++ b/drivers/staging/i2o/i2o.h
@@ -0,0 +1,988 @@
+/*
+ * I2O kernel space accessible structures/APIs
+ *
+ * (c) Copyright 1999, 2000 Red Hat Software
+ *
+ * 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 header file defined the I2O APIs/structures for use by
+ * the I2O kernel modules.
+ *
+ */
+
+#ifndef _I2O_H
+#define _I2O_H
+
+#include <linux/i2o-dev.h>
+
+/* How many different OSM's are we allowing */
+#define I2O_MAX_DRIVERS 8
+
+#include <linux/pci.h>
+#include <linux/bug.h>
+#include <linux/dma-mapping.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h> /* work_struct */
+#include <linux/mempool.h>
+#include <linux/mutex.h>
+#include <linux/scatterlist.h>
+#include <linux/semaphore.h> /* Needed for MUTEX init macros */
+
+#include <asm/io.h>
+
+/* message queue empty */
+#define I2O_QUEUE_EMPTY 0xffffffff
+
+/*
+ * Cache strategies
+ */
+
+/* The NULL strategy leaves everything up to the controller. This tends to be a
+ * pessimal but functional choice.
+ */
+#define CACHE_NULL 0
+/* Prefetch data when reading. We continually attempt to load the next 32 sectors
+ * into the controller cache.
+ */
+#define CACHE_PREFETCH 1
+/* Prefetch data when reading. We sometimes attempt to load the next 32 sectors
+ * into the controller cache. When an I/O is less <= 8K we assume its probably
+ * not sequential and don't prefetch (default)
+ */
+#define CACHE_SMARTFETCH 2
+/* Data is written to the cache and then out on to the disk. The I/O must be
+ * physically on the medium before the write is acknowledged (default without
+ * NVRAM)
+ */
+#define CACHE_WRITETHROUGH 17
+/* Data is written to the cache and then out on to the disk. The controller
+ * is permitted to write back the cache any way it wants. (default if battery
+ * backed NVRAM is present). It can be useful to set this for swap regardless of
+ * battery state.
+ */
+#define CACHE_WRITEBACK 18
+/* Optimise for under powered controllers, especially on RAID1 and RAID0. We
+ * write large I/O's directly to disk bypassing the cache to avoid the extra
+ * memory copy hits. Small writes are writeback cached
+ */
+#define CACHE_SMARTBACK 19
+/* Optimise for under powered controllers, especially on RAID1 and RAID0. We
+ * write large I/O's directly to disk bypassing the cache to avoid the extra
+ * memory copy hits. Small writes are writethrough cached. Suitable for devices
+ * lacking battery backup
+ */
+#define CACHE_SMARTTHROUGH 20
+
+/*
+ * Ioctl structures
+ */
+
+#define BLKI2OGRSTRAT _IOR('2', 1, int)
+#define BLKI2OGWSTRAT _IOR('2', 2, int)
+#define BLKI2OSRSTRAT _IOW('2', 3, int)
+#define BLKI2OSWSTRAT _IOW('2', 4, int)
+
+/*
+ * I2O Function codes
+ */
+
+/*
+ * Executive Class
+ */
+#define I2O_CMD_ADAPTER_ASSIGN 0xB3
+#define I2O_CMD_ADAPTER_READ 0xB2
+#define I2O_CMD_ADAPTER_RELEASE 0xB5
+#define I2O_CMD_BIOS_INFO_SET 0xA5
+#define I2O_CMD_BOOT_DEVICE_SET 0xA7
+#define I2O_CMD_CONFIG_VALIDATE 0xBB
+#define I2O_CMD_CONN_SETUP 0xCA
+#define I2O_CMD_DDM_DESTROY 0xB1
+#define I2O_CMD_DDM_ENABLE 0xD5
+#define I2O_CMD_DDM_QUIESCE 0xC7
+#define I2O_CMD_DDM_RESET 0xD9
+#define I2O_CMD_DDM_SUSPEND 0xAF
+#define I2O_CMD_DEVICE_ASSIGN 0xB7
+#define I2O_CMD_DEVICE_RELEASE 0xB9
+#define I2O_CMD_HRT_GET 0xA8
+#define I2O_CMD_ADAPTER_CLEAR 0xBE
+#define I2O_CMD_ADAPTER_CONNECT 0xC9
+#define I2O_CMD_ADAPTER_RESET 0xBD
+#define I2O_CMD_LCT_NOTIFY 0xA2
+#define I2O_CMD_OUTBOUND_INIT 0xA1
+#define I2O_CMD_PATH_ENABLE 0xD3
+#define I2O_CMD_PATH_QUIESCE 0xC5
+#define I2O_CMD_PATH_RESET 0xD7
+#define I2O_CMD_STATIC_MF_CREATE 0xDD
+#define I2O_CMD_STATIC_MF_RELEASE 0xDF
+#define I2O_CMD_STATUS_GET 0xA0
+#define I2O_CMD_SW_DOWNLOAD 0xA9
+#define I2O_CMD_SW_UPLOAD 0xAB
+#define I2O_CMD_SW_REMOVE 0xAD
+#define I2O_CMD_SYS_ENABLE 0xD1
+#define I2O_CMD_SYS_MODIFY 0xC1
+#define I2O_CMD_SYS_QUIESCE 0xC3
+#define I2O_CMD_SYS_TAB_SET 0xA3
+
+/*
+ * Utility Class
+ */
+#define I2O_CMD_UTIL_NOP 0x00
+#define I2O_CMD_UTIL_ABORT 0x01
+#define I2O_CMD_UTIL_CLAIM 0x09
+#define I2O_CMD_UTIL_RELEASE 0x0B
+#define I2O_CMD_UTIL_PARAMS_GET 0x06
+#define I2O_CMD_UTIL_PARAMS_SET 0x05
+#define I2O_CMD_UTIL_EVT_REGISTER 0x13
+#define I2O_CMD_UTIL_EVT_ACK 0x14
+#define I2O_CMD_UTIL_CONFIG_DIALOG 0x10
+#define I2O_CMD_UTIL_DEVICE_RESERVE 0x0D
+#define I2O_CMD_UTIL_DEVICE_RELEASE 0x0F
+#define I2O_CMD_UTIL_LOCK 0x17
+#define I2O_CMD_UTIL_LOCK_RELEASE 0x19
+#define I2O_CMD_UTIL_REPLY_FAULT_NOTIFY 0x15
+
+/*
+ * SCSI Host Bus Adapter Class
+ */
+#define I2O_CMD_SCSI_EXEC 0x81
+#define I2O_CMD_SCSI_ABORT 0x83
+#define I2O_CMD_SCSI_BUSRESET 0x27
+
+/*
+ * Bus Adapter Class
+ */
+#define I2O_CMD_BUS_ADAPTER_RESET 0x85
+#define I2O_CMD_BUS_RESET 0x87
+#define I2O_CMD_BUS_SCAN 0x89
+#define I2O_CMD_BUS_QUIESCE 0x8b
+
+/*
+ * Random Block Storage Class
+ */
+#define I2O_CMD_BLOCK_READ 0x30
+#define I2O_CMD_BLOCK_WRITE 0x31
+#define I2O_CMD_BLOCK_CFLUSH 0x37
+#define I2O_CMD_BLOCK_MLOCK 0x49
+#define I2O_CMD_BLOCK_MUNLOCK 0x4B
+#define I2O_CMD_BLOCK_MMOUNT 0x41
+#define I2O_CMD_BLOCK_MEJECT 0x43
+#define I2O_CMD_BLOCK_POWER 0x70
+
+#define I2O_CMD_PRIVATE 0xFF
+
+/* Command status values */
+
+#define I2O_CMD_IN_PROGRESS 0x01
+#define I2O_CMD_REJECTED 0x02
+#define I2O_CMD_FAILED 0x03
+#define I2O_CMD_COMPLETED 0x04
+
+/* I2O API function return values */
+
+#define I2O_RTN_NO_ERROR 0
+#define I2O_RTN_NOT_INIT 1
+#define I2O_RTN_FREE_Q_EMPTY 2
+#define I2O_RTN_TCB_ERROR 3
+#define I2O_RTN_TRANSACTION_ERROR 4
+#define I2O_RTN_ADAPTER_ALREADY_INIT 5
+#define I2O_RTN_MALLOC_ERROR 6
+#define I2O_RTN_ADPTR_NOT_REGISTERED 7
+#define I2O_RTN_MSG_REPLY_TIMEOUT 8
+#define I2O_RTN_NO_STATUS 9
+#define I2O_RTN_NO_FIRM_VER 10
+#define I2O_RTN_NO_LINK_SPEED 11
+
+/* Reply message status defines for all messages */
+
+#define I2O_REPLY_STATUS_SUCCESS 0x00
+#define I2O_REPLY_STATUS_ABORT_DIRTY 0x01
+#define I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER 0x02
+#define I2O_REPLY_STATUS_ABORT_PARTIAL_TRANSFER 0x03
+#define I2O_REPLY_STATUS_ERROR_DIRTY 0x04
+#define I2O_REPLY_STATUS_ERROR_NO_DATA_TRANSFER 0x05
+#define I2O_REPLY_STATUS_ERROR_PARTIAL_TRANSFER 0x06
+#define I2O_REPLY_STATUS_PROCESS_ABORT_DIRTY 0x08
+#define I2O_REPLY_STATUS_PROCESS_ABORT_NO_DATA_TRANSFER 0x09
+#define I2O_REPLY_STATUS_PROCESS_ABORT_PARTIAL_TRANSFER 0x0A
+#define I2O_REPLY_STATUS_TRANSACTION_ERROR 0x0B
+#define I2O_REPLY_STATUS_PROGRESS_REPORT 0x80
+
+/* Status codes and Error Information for Parameter functions */
+
+#define I2O_PARAMS_STATUS_SUCCESS 0x00
+#define I2O_PARAMS_STATUS_BAD_KEY_ABORT 0x01
+#define I2O_PARAMS_STATUS_BAD_KEY_CONTINUE 0x02
+#define I2O_PARAMS_STATUS_BUFFER_FULL 0x03
+#define I2O_PARAMS_STATUS_BUFFER_TOO_SMALL 0x04
+#define I2O_PARAMS_STATUS_FIELD_UNREADABLE 0x05
+#define I2O_PARAMS_STATUS_FIELD_UNWRITEABLE 0x06
+#define I2O_PARAMS_STATUS_INSUFFICIENT_FIELDS 0x07
+#define I2O_PARAMS_STATUS_INVALID_GROUP_ID 0x08
+#define I2O_PARAMS_STATUS_INVALID_OPERATION 0x09
+#define I2O_PARAMS_STATUS_NO_KEY_FIELD 0x0A
+#define I2O_PARAMS_STATUS_NO_SUCH_FIELD 0x0B
+#define I2O_PARAMS_STATUS_NON_DYNAMIC_GROUP 0x0C
+#define I2O_PARAMS_STATUS_OPERATION_ERROR 0x0D
+#define I2O_PARAMS_STATUS_SCALAR_ERROR 0x0E
+#define I2O_PARAMS_STATUS_TABLE_ERROR 0x0F
+#define I2O_PARAMS_STATUS_WRONG_GROUP_TYPE 0x10
+
+/* DetailedStatusCode defines for Executive, DDM, Util and Transaction error
+ * messages: Table 3-2 Detailed Status Codes.*/
+
+#define I2O_DSC_SUCCESS 0x0000
+#define I2O_DSC_BAD_KEY 0x0002
+#define I2O_DSC_TCL_ERROR 0x0003
+#define I2O_DSC_REPLY_BUFFER_FULL 0x0004
+#define I2O_DSC_NO_SUCH_PAGE 0x0005
+#define I2O_DSC_INSUFFICIENT_RESOURCE_SOFT 0x0006
+#define I2O_DSC_INSUFFICIENT_RESOURCE_HARD 0x0007
+#define I2O_DSC_CHAIN_BUFFER_TOO_LARGE 0x0009
+#define I2O_DSC_UNSUPPORTED_FUNCTION 0x000A
+#define I2O_DSC_DEVICE_LOCKED 0x000B
+#define I2O_DSC_DEVICE_RESET 0x000C
+#define I2O_DSC_INAPPROPRIATE_FUNCTION 0x000D
+#define I2O_DSC_INVALID_INITIATOR_ADDRESS 0x000E
+#define I2O_DSC_INVALID_MESSAGE_FLAGS 0x000F
+#define I2O_DSC_INVALID_OFFSET 0x0010
+#define I2O_DSC_INVALID_PARAMETER 0x0011
+#define I2O_DSC_INVALID_REQUEST 0x0012
+#define I2O_DSC_INVALID_TARGET_ADDRESS 0x0013
+#define I2O_DSC_MESSAGE_TOO_LARGE 0x0014
+#define I2O_DSC_MESSAGE_TOO_SMALL 0x0015
+#define I2O_DSC_MISSING_PARAMETER 0x0016
+#define I2O_DSC_TIMEOUT 0x0017
+#define I2O_DSC_UNKNOWN_ERROR 0x0018
+#define I2O_DSC_UNKNOWN_FUNCTION 0x0019
+#define I2O_DSC_UNSUPPORTED_VERSION 0x001A
+#define I2O_DSC_DEVICE_BUSY 0x001B
+#define I2O_DSC_DEVICE_NOT_AVAILABLE 0x001C
+
+/* DetailedStatusCode defines for Block Storage Operation: Table 6-7 Detailed
+ Status Codes.*/
+
+#define I2O_BSA_DSC_SUCCESS 0x0000
+#define I2O_BSA_DSC_MEDIA_ERROR 0x0001
+#define I2O_BSA_DSC_ACCESS_ERROR 0x0002
+#define I2O_BSA_DSC_DEVICE_FAILURE 0x0003
+#define I2O_BSA_DSC_DEVICE_NOT_READY 0x0004
+#define I2O_BSA_DSC_MEDIA_NOT_PRESENT 0x0005
+#define I2O_BSA_DSC_MEDIA_LOCKED 0x0006
+#define I2O_BSA_DSC_MEDIA_FAILURE 0x0007
+#define I2O_BSA_DSC_PROTOCOL_FAILURE 0x0008
+#define I2O_BSA_DSC_BUS_FAILURE 0x0009
+#define I2O_BSA_DSC_ACCESS_VIOLATION 0x000A
+#define I2O_BSA_DSC_WRITE_PROTECTED 0x000B
+#define I2O_BSA_DSC_DEVICE_RESET 0x000C
+#define I2O_BSA_DSC_VOLUME_CHANGED 0x000D
+#define I2O_BSA_DSC_TIMEOUT 0x000E
+
+/* FailureStatusCodes, Table 3-3 Message Failure Codes */
+
+#define I2O_FSC_TRANSPORT_SERVICE_SUSPENDED 0x81
+#define I2O_FSC_TRANSPORT_SERVICE_TERMINATED 0x82
+#define I2O_FSC_TRANSPORT_CONGESTION 0x83
+#define I2O_FSC_TRANSPORT_FAILURE 0x84
+#define I2O_FSC_TRANSPORT_STATE_ERROR 0x85
+#define I2O_FSC_TRANSPORT_TIME_OUT 0x86
+#define I2O_FSC_TRANSPORT_ROUTING_FAILURE 0x87
+#define I2O_FSC_TRANSPORT_INVALID_VERSION 0x88
+#define I2O_FSC_TRANSPORT_INVALID_OFFSET 0x89
+#define I2O_FSC_TRANSPORT_INVALID_MSG_FLAGS 0x8A
+#define I2O_FSC_TRANSPORT_FRAME_TOO_SMALL 0x8B
+#define I2O_FSC_TRANSPORT_FRAME_TOO_LARGE 0x8C
+#define I2O_FSC_TRANSPORT_INVALID_TARGET_ID 0x8D
+#define I2O_FSC_TRANSPORT_INVALID_INITIATOR_ID 0x8E
+#define I2O_FSC_TRANSPORT_INVALID_INITIATOR_CONTEXT 0x8F
+#define I2O_FSC_TRANSPORT_UNKNOWN_FAILURE 0xFF
+
+/* Device Claim Types */
+#define I2O_CLAIM_PRIMARY 0x01000000
+#define I2O_CLAIM_MANAGEMENT 0x02000000
+#define I2O_CLAIM_AUTHORIZED 0x03000000
+#define I2O_CLAIM_SECONDARY 0x04000000
+
+/* Message header defines for VersionOffset */
+#define I2OVER15 0x0001
+#define I2OVER20 0x0002
+
+/* Default is 1.5 */
+#define I2OVERSION I2OVER15
+
+#define SGL_OFFSET_0 I2OVERSION
+#define SGL_OFFSET_4 (0x0040 | I2OVERSION)
+#define SGL_OFFSET_5 (0x0050 | I2OVERSION)
+#define SGL_OFFSET_6 (0x0060 | I2OVERSION)
+#define SGL_OFFSET_7 (0x0070 | I2OVERSION)
+#define SGL_OFFSET_8 (0x0080 | I2OVERSION)
+#define SGL_OFFSET_9 (0x0090 | I2OVERSION)
+#define SGL_OFFSET_10 (0x00A0 | I2OVERSION)
+#define SGL_OFFSET_11 (0x00B0 | I2OVERSION)
+#define SGL_OFFSET_12 (0x00C0 | I2OVERSION)
+#define SGL_OFFSET(x) (((x)<<4) | I2OVERSION)
+
+/* Transaction Reply Lists (TRL) Control Word structure */
+#define TRL_SINGLE_FIXED_LENGTH 0x00
+#define TRL_SINGLE_VARIABLE_LENGTH 0x40
+#define TRL_MULTIPLE_FIXED_LENGTH 0x80
+
+ /* msg header defines for MsgFlags */
+#define MSG_STATIC 0x0100
+#define MSG_64BIT_CNTXT 0x0200
+#define MSG_MULTI_TRANS 0x1000
+#define MSG_FAIL 0x2000
+#define MSG_FINAL 0x4000
+#define MSG_REPLY 0x8000
+
+ /* minimum size msg */
+#define THREE_WORD_MSG_SIZE 0x00030000
+#define FOUR_WORD_MSG_SIZE 0x00040000
+#define FIVE_WORD_MSG_SIZE 0x00050000
+#define SIX_WORD_MSG_SIZE 0x00060000
+#define SEVEN_WORD_MSG_SIZE 0x00070000
+#define EIGHT_WORD_MSG_SIZE 0x00080000
+#define NINE_WORD_MSG_SIZE 0x00090000
+#define TEN_WORD_MSG_SIZE 0x000A0000
+#define ELEVEN_WORD_MSG_SIZE 0x000B0000
+#define I2O_MESSAGE_SIZE(x) ((x)<<16)
+
+/* special TID assignments */
+#define ADAPTER_TID 0
+#define HOST_TID 1
+
+/* outbound queue defines */
+#define I2O_MAX_OUTBOUND_MSG_FRAMES 128
+#define I2O_OUTBOUND_MSG_FRAME_SIZE 128 /* in 32-bit words */
+
+/* inbound queue definitions */
+#define I2O_MSG_INPOOL_MIN 32
+#define I2O_INBOUND_MSG_FRAME_SIZE 128 /* in 32-bit words */
+
+#define I2O_POST_WAIT_OK 0
+#define I2O_POST_WAIT_TIMEOUT -ETIMEDOUT
+
+#define I2O_CONTEXT_LIST_MIN_LENGTH 15
+#define I2O_CONTEXT_LIST_USED 0x01
+#define I2O_CONTEXT_LIST_DELETED 0x02
+
+/* timeouts */
+#define I2O_TIMEOUT_INIT_OUTBOUND_QUEUE 15
+#define I2O_TIMEOUT_MESSAGE_GET 5
+#define I2O_TIMEOUT_RESET 30
+#define I2O_TIMEOUT_STATUS_GET 5
+#define I2O_TIMEOUT_LCT_GET 360
+#define I2O_TIMEOUT_SCSI_SCB_ABORT 240
+
+/* retries */
+#define I2O_HRT_GET_TRIES 3
+#define I2O_LCT_GET_TRIES 3
+
+/* defines for max_sectors and max_phys_segments */
+#define I2O_MAX_SECTORS 1024
+#define I2O_MAX_SECTORS_LIMITED 128
+#define I2O_MAX_PHYS_SEGMENTS BLK_MAX_SEGMENTS
+
+/*
+ * Message structures
+ */
+struct i2o_message {
+ union {
+ struct {
+ u8 version_offset;
+ u8 flags;
+ u16 size;
+ u32 target_tid:12;
+ u32 init_tid:12;
+ u32 function:8;
+ u32 icntxt; /* initiator context */
+ u32 tcntxt; /* transaction context */
+ } s;
+ u32 head[4];
+ } u;
+ /* List follows */
+ u32 body[0];
+};
+
+/* MFA and I2O message used by mempool */
+struct i2o_msg_mfa {
+ u32 mfa; /* MFA returned by the controller */
+ struct i2o_message msg; /* I2O message */
+};
+
+/*
+ * Each I2O device entity has one of these. There is one per device.
+ */
+struct i2o_device {
+ i2o_lct_entry lct_data; /* Device LCT information */
+
+ struct i2o_controller *iop; /* Controlling IOP */
+ struct list_head list; /* node in IOP devices list */
+
+ struct device device;
+
+ struct mutex lock; /* device lock */
+};
+
+/*
+ * Event structure provided to the event handling function
+ */
+struct i2o_event {
+ struct work_struct work;
+ struct i2o_device *i2o_dev; /* I2O device pointer from which the
+ event reply was initiated */
+ u16 size; /* Size of data in 32-bit words */
+ u32 tcntxt; /* Transaction context used at
+ registration */
+ u32 event_indicator; /* Event indicator from reply */
+ u32 data[0]; /* Event data from reply */
+};
+
+/*
+ * I2O classes which could be handled by the OSM
+ */
+struct i2o_class_id {
+ u16 class_id:12;
+};
+
+/*
+ * I2O driver structure for OSMs
+ */
+struct i2o_driver {
+ char *name; /* OSM name */
+ int context; /* Low 8 bits of the transaction info */
+ struct i2o_class_id *classes; /* I2O classes that this OSM handles */
+
+ /* Message reply handler */
+ int (*reply) (struct i2o_controller *, u32, struct i2o_message *);
+
+ /* Event handler */
+ work_func_t event;
+
+ struct workqueue_struct *event_queue; /* Event queue */
+
+ struct device_driver driver;
+
+ /* notification of changes */
+ void (*notify_controller_add) (struct i2o_controller *);
+ void (*notify_controller_remove) (struct i2o_controller *);
+ void (*notify_device_add) (struct i2o_device *);
+ void (*notify_device_remove) (struct i2o_device *);
+
+ struct semaphore lock;
+};
+
+/*
+ * Contains DMA mapped address information
+ */
+struct i2o_dma {
+ void *virt;
+ dma_addr_t phys;
+ size_t len;
+};
+
+/*
+ * Contains slab cache and mempool information
+ */
+struct i2o_pool {
+ char *name;
+ struct kmem_cache *slab;
+ mempool_t *mempool;
+};
+
+/*
+ * Contains IO mapped address information
+ */
+struct i2o_io {
+ void __iomem *virt;
+ unsigned long phys;
+ unsigned long len;
+};
+
+/*
+ * Context queue entry, used for 32-bit context on 64-bit systems
+ */
+struct i2o_context_list_element {
+ struct list_head list;
+ u32 context;
+ void *ptr;
+ unsigned long timestamp;
+};
+
+/*
+ * Each I2O controller has one of these objects
+ */
+struct i2o_controller {
+ char name[16];
+ int unit;
+ int type;
+
+ struct pci_dev *pdev; /* PCI device */
+
+ unsigned int promise:1; /* Promise controller */
+ unsigned int adaptec:1; /* DPT / Adaptec controller */
+ unsigned int raptor:1; /* split bar */
+ unsigned int no_quiesce:1; /* dont quiesce before reset */
+ unsigned int short_req:1; /* use small block sizes */
+ unsigned int limit_sectors:1; /* limit number of sectors / request */
+ unsigned int pae_support:1; /* controller has 64-bit SGL support */
+
+ struct list_head devices; /* list of I2O devices */
+ struct list_head list; /* Controller list */
+
+ void __iomem *in_port; /* Inbout port address */
+ void __iomem *out_port; /* Outbound port address */
+ void __iomem *irq_status; /* Interrupt status register address */
+ void __iomem *irq_mask; /* Interrupt mask register address */
+
+ struct i2o_dma status; /* IOP status block */
+
+ struct i2o_dma hrt; /* HW Resource Table */
+ i2o_lct *lct; /* Logical Config Table */
+ struct i2o_dma dlct; /* Temp LCT */
+ struct mutex lct_lock; /* Lock for LCT updates */
+ struct i2o_dma status_block; /* IOP status block */
+
+ struct i2o_io base; /* controller messaging unit */
+ struct i2o_io in_queue; /* inbound message queue Host->IOP */
+ struct i2o_dma out_queue; /* outbound message queue IOP->Host */
+
+ struct i2o_pool in_msg; /* mempool for inbound messages */
+
+ unsigned int battery:1; /* Has a battery backup */
+ unsigned int io_alloc:1; /* An I/O resource was allocated */
+ unsigned int mem_alloc:1; /* A memory resource was allocated */
+
+ struct resource io_resource; /* I/O resource allocated to the IOP */
+ struct resource mem_resource; /* Mem resource allocated to the IOP */
+
+ struct device device;
+ struct i2o_device *exec; /* Executive */
+#if BITS_PER_LONG == 64
+ spinlock_t context_list_lock; /* lock for context_list */
+ atomic_t context_list_counter; /* needed for unique contexts */
+ struct list_head context_list; /* list of context id's
+ and pointers */
+#endif
+ spinlock_t lock; /* lock for controller
+ configuration */
+ void *driver_data[I2O_MAX_DRIVERS]; /* storage for drivers */
+};
+
+/*
+ * I2O System table entry
+ *
+ * The system table contains information about all the IOPs in the
+ * system. It is sent to all IOPs so that they can create peer2peer
+ * connections between them.
+ */
+struct i2o_sys_tbl_entry {
+ u16 org_id;
+ u16 reserved1;
+ u32 iop_id:12;
+ u32 reserved2:20;
+ u16 seg_num:12;
+ u16 i2o_version:4;
+ u8 iop_state;
+ u8 msg_type;
+ u16 frame_size;
+ u16 reserved3;
+ u32 last_changed;
+ u32 iop_capabilities;
+ u32 inbound_low;
+ u32 inbound_high;
+};
+
+struct i2o_sys_tbl {
+ u8 num_entries;
+ u8 version;
+ u16 reserved1;
+ u32 change_ind;
+ u32 reserved2;
+ u32 reserved3;
+ struct i2o_sys_tbl_entry iops[0];
+};
+
+extern struct list_head i2o_controllers;
+
+/* Message functions */
+extern struct i2o_message *i2o_msg_get_wait(struct i2o_controller *, int);
+extern int i2o_msg_post_wait_mem(struct i2o_controller *, struct i2o_message *,
+ unsigned long, struct i2o_dma *);
+
+/* IOP functions */
+extern int i2o_status_get(struct i2o_controller *);
+
+extern int i2o_event_register(struct i2o_device *, struct i2o_driver *, int,
+ u32);
+extern struct i2o_device *i2o_iop_find_device(struct i2o_controller *, u16);
+extern struct i2o_controller *i2o_find_iop(int);
+
+/* Functions needed for handling 64-bit pointers in 32-bit context */
+#if BITS_PER_LONG == 64
+extern u32 i2o_cntxt_list_add(struct i2o_controller *, void *);
+extern void *i2o_cntxt_list_get(struct i2o_controller *, u32);
+extern u32 i2o_cntxt_list_remove(struct i2o_controller *, void *);
+extern u32 i2o_cntxt_list_get_ptr(struct i2o_controller *, void *);
+
+static inline u32 i2o_ptr_low(void *ptr)
+{
+ return (u32) (u64) ptr;
+};
+
+static inline u32 i2o_ptr_high(void *ptr)
+{
+ return (u32) ((u64) ptr >> 32);
+};
+
+static inline u32 i2o_dma_low(dma_addr_t dma_addr)
+{
+ return (u32) (u64) dma_addr;
+};
+
+static inline u32 i2o_dma_high(dma_addr_t dma_addr)
+{
+ return (u32) ((u64) dma_addr >> 32);
+};
+#else
+static inline u32 i2o_cntxt_list_add(struct i2o_controller *c, void *ptr)
+{
+ return (u32) ptr;
+};
+
+static inline void *i2o_cntxt_list_get(struct i2o_controller *c, u32 context)
+{
+ return (void *)context;
+};
+
+static inline u32 i2o_cntxt_list_remove(struct i2o_controller *c, void *ptr)
+{
+ return (u32) ptr;
+};
+
+static inline u32 i2o_cntxt_list_get_ptr(struct i2o_controller *c, void *ptr)
+{
+ return (u32) ptr;
+};
+
+static inline u32 i2o_ptr_low(void *ptr)
+{
+ return (u32) ptr;
+};
+
+static inline u32 i2o_ptr_high(void *ptr)
+{
+ return 0;
+};
+
+static inline u32 i2o_dma_low(dma_addr_t dma_addr)
+{
+ return (u32) dma_addr;
+};
+
+static inline u32 i2o_dma_high(dma_addr_t dma_addr)
+{
+ return 0;
+};
+#endif
+
+extern u16 i2o_sg_tablesize(struct i2o_controller *c, u16 body_size);
+extern dma_addr_t i2o_dma_map_single(struct i2o_controller *c, void *ptr,
+ size_t size,
+ enum dma_data_direction direction,
+ u32 ** sg_ptr);
+extern int i2o_dma_map_sg(struct i2o_controller *c,
+ struct scatterlist *sg, int sg_count,
+ enum dma_data_direction direction,
+ u32 ** sg_ptr);
+extern int i2o_dma_alloc(struct device *dev, struct i2o_dma *addr, size_t len);
+extern void i2o_dma_free(struct device *dev, struct i2o_dma *addr);
+extern int i2o_dma_realloc(struct device *dev, struct i2o_dma *addr,
+ size_t len);
+extern int i2o_pool_alloc(struct i2o_pool *pool, const char *name,
+ size_t size, int min_nr);
+extern void i2o_pool_free(struct i2o_pool *pool);
+/* I2O driver (OSM) functions */
+extern int i2o_driver_register(struct i2o_driver *);
+extern void i2o_driver_unregister(struct i2o_driver *);
+
+/**
+ * i2o_driver_notify_controller_add - Send notification of added controller
+ * @drv: I2O driver
+ * @c: I2O controller
+ *
+ * Send notification of added controller to a single registered driver.
+ */
+static inline void i2o_driver_notify_controller_add(struct i2o_driver *drv,
+ struct i2o_controller *c)
+{
+ if (drv->notify_controller_add)
+ drv->notify_controller_add(c);
+};
+
+/**
+ * i2o_driver_notify_controller_remove - Send notification of removed controller
+ * @drv: I2O driver
+ * @c: I2O controller
+ *
+ * Send notification of removed controller to a single registered driver.
+ */
+static inline void i2o_driver_notify_controller_remove(struct i2o_driver *drv,
+ struct i2o_controller *c)
+{
+ if (drv->notify_controller_remove)
+ drv->notify_controller_remove(c);
+};
+
+/**
+ * i2o_driver_notify_device_add - Send notification of added device
+ * @drv: I2O driver
+ * @i2o_dev: the added i2o_device
+ *
+ * Send notification of added device to a single registered driver.
+ */
+static inline void i2o_driver_notify_device_add(struct i2o_driver *drv,
+ struct i2o_device *i2o_dev)
+{
+ if (drv->notify_device_add)
+ drv->notify_device_add(i2o_dev);
+};
+
+/**
+ * i2o_driver_notify_device_remove - Send notification of removed device
+ * @drv: I2O driver
+ * @i2o_dev: the added i2o_device
+ *
+ * Send notification of removed device to a single registered driver.
+ */
+static inline void i2o_driver_notify_device_remove(struct i2o_driver *drv,
+ struct i2o_device *i2o_dev)
+{
+ if (drv->notify_device_remove)
+ drv->notify_device_remove(i2o_dev);
+};
+
+extern void i2o_driver_notify_controller_add_all(struct i2o_controller *);
+extern void i2o_driver_notify_controller_remove_all(struct i2o_controller *);
+extern void i2o_driver_notify_device_add_all(struct i2o_device *);
+extern void i2o_driver_notify_device_remove_all(struct i2o_device *);
+
+/* I2O device functions */
+extern int i2o_device_claim(struct i2o_device *);
+extern int i2o_device_claim_release(struct i2o_device *);
+
+/* Exec OSM functions */
+extern int i2o_exec_lct_get(struct i2o_controller *);
+
+/* device / driver / kobject conversion functions */
+#define to_i2o_driver(drv) container_of(drv,struct i2o_driver, driver)
+#define to_i2o_device(dev) container_of(dev, struct i2o_device, device)
+#define to_i2o_controller(dev) container_of(dev, struct i2o_controller, device)
+
+/**
+ * i2o_out_to_virt - Turn an I2O message to a virtual address
+ * @c: controller
+ * @m: message engine value
+ *
+ * Turn a receive message from an I2O controller bus address into
+ * a Linux virtual address. The shared page frame is a linear block
+ * so we simply have to shift the offset. This function does not
+ * work for sender side messages as they are ioremap objects
+ * provided by the I2O controller.
+ */
+static inline struct i2o_message *i2o_msg_out_to_virt(struct i2o_controller *c,
+ u32 m)
+{
+ BUG_ON(m < c->out_queue.phys
+ || m >= c->out_queue.phys + c->out_queue.len);
+
+ return c->out_queue.virt + (m - c->out_queue.phys);
+};
+
+/**
+ * i2o_msg_in_to_virt - Turn an I2O message to a virtual address
+ * @c: controller
+ * @m: message engine value
+ *
+ * Turn a send message from an I2O controller bus address into
+ * a Linux virtual address. The shared page frame is a linear block
+ * so we simply have to shift the offset. This function does not
+ * work for receive side messages as they are kmalloc objects
+ * in a different pool.
+ */
+static inline struct i2o_message __iomem *i2o_msg_in_to_virt(struct
+ i2o_controller *c,
+ u32 m)
+{
+ return c->in_queue.virt + m;
+};
+
+/**
+ * i2o_msg_get - obtain an I2O message from the IOP
+ * @c: I2O controller
+ *
+ * This function tries to get a message frame. If no message frame is
+ * available do not wait until one is available (see also i2o_msg_get_wait).
+ * The returned pointer to the message frame is not in I/O memory, it is
+ * allocated from a mempool. But because a MFA is allocated from the
+ * controller too it is guaranteed that i2o_msg_post() will never fail.
+ *
+ * On a success a pointer to the message frame is returned. If the message
+ * queue is empty -EBUSY is returned and if no memory is available -ENOMEM
+ * is returned.
+ */
+static inline struct i2o_message *i2o_msg_get(struct i2o_controller *c)
+{
+ struct i2o_msg_mfa *mmsg = mempool_alloc(c->in_msg.mempool, GFP_ATOMIC);
+ if (!mmsg)
+ return ERR_PTR(-ENOMEM);
+
+ mmsg->mfa = readl(c->in_port);
+ if (unlikely(mmsg->mfa >= c->in_queue.len)) {
+ u32 mfa = mmsg->mfa;
+
+ mempool_free(mmsg, c->in_msg.mempool);
+
+ if (mfa == I2O_QUEUE_EMPTY)
+ return ERR_PTR(-EBUSY);
+ return ERR_PTR(-EFAULT);
+ }
+
+ return &mmsg->msg;
+};
+
+/**
+ * i2o_msg_post - Post I2O message to I2O controller
+ * @c: I2O controller to which the message should be send
+ * @msg: message returned by i2o_msg_get()
+ *
+ * Post the message to the I2O controller and return immediately.
+ */
+static inline void i2o_msg_post(struct i2o_controller *c,
+ struct i2o_message *msg)
+{
+ struct i2o_msg_mfa *mmsg;
+
+ mmsg = container_of(msg, struct i2o_msg_mfa, msg);
+ memcpy_toio(i2o_msg_in_to_virt(c, mmsg->mfa), msg,
+ (le32_to_cpu(msg->u.head[0]) >> 16) << 2);
+ writel(mmsg->mfa, c->in_port);
+ mempool_free(mmsg, c->in_msg.mempool);
+};
+
+/**
+ * i2o_msg_post_wait - Post and wait a message and wait until return
+ * @c: controller
+ * @msg: message to post
+ * @timeout: time in seconds to wait
+ *
+ * This API allows an OSM to post a message and then be told whether or
+ * not the system received a successful reply. If the message times out
+ * then the value '-ETIMEDOUT' is returned.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static inline int i2o_msg_post_wait(struct i2o_controller *c,
+ struct i2o_message *msg,
+ unsigned long timeout)
+{
+ return i2o_msg_post_wait_mem(c, msg, timeout, NULL);
+};
+
+/**
+ * i2o_msg_nop_mfa - Returns a fetched MFA back to the controller
+ * @c: I2O controller from which the MFA was fetched
+ * @mfa: MFA which should be returned
+ *
+ * This function must be used for preserved messages, because i2o_msg_nop()
+ * also returns the allocated memory back to the msg_pool mempool.
+ */
+static inline void i2o_msg_nop_mfa(struct i2o_controller *c, u32 mfa)
+{
+ struct i2o_message __iomem *msg;
+ u32 nop[3] = {
+ THREE_WORD_MSG_SIZE | SGL_OFFSET_0,
+ I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | ADAPTER_TID,
+ 0x00000000
+ };
+
+ msg = i2o_msg_in_to_virt(c, mfa);
+ memcpy_toio(msg, nop, sizeof(nop));
+ writel(mfa, c->in_port);
+};
+
+/**
+ * i2o_msg_nop - Returns a message which is not used
+ * @c: I2O controller from which the message was created
+ * @msg: message which should be returned
+ *
+ * If you fetch a message via i2o_msg_get, and can't use it, you must
+ * return the message with this function. Otherwise the MFA is lost as well
+ * as the allocated memory from the mempool.
+ */
+static inline void i2o_msg_nop(struct i2o_controller *c,
+ struct i2o_message *msg)
+{
+ struct i2o_msg_mfa *mmsg;
+ mmsg = container_of(msg, struct i2o_msg_mfa, msg);
+
+ i2o_msg_nop_mfa(c, mmsg->mfa);
+ mempool_free(mmsg, c->in_msg.mempool);
+};
+
+/**
+ * i2o_flush_reply - Flush reply from I2O controller
+ * @c: I2O controller
+ * @m: the message identifier
+ *
+ * The I2O controller must be informed that the reply message is not needed
+ * anymore. If you forget to flush the reply, the message frame can't be
+ * used by the controller anymore and is therefore lost.
+ */
+static inline void i2o_flush_reply(struct i2o_controller *c, u32 m)
+{
+ writel(m, c->out_port);
+};
+
+/*
+ * Endian handling wrapped into the macro - keeps the core code
+ * cleaner.
+ */
+
+#define i2o_raw_writel(val, mem) __raw_writel(cpu_to_le32(val), mem)
+
+extern int i2o_parm_field_get(struct i2o_device *, int, int, void *, int);
+extern int i2o_parm_table_get(struct i2o_device *, int, int, int, void *, int,
+ void *, int);
+
+/* debugging and troubleshooting/diagnostic helpers. */
+#define osm_printk(level, format, arg...) \
+ printk(level "%s: " format, OSM_NAME , ## arg)
+
+#ifdef DEBUG
+#define osm_debug(format, arg...) \
+ osm_printk(KERN_DEBUG, format , ## arg)
+#else
+#define osm_debug(format, arg...) \
+ do { } while (0)
+#endif
+
+#define osm_err(format, arg...) \
+ osm_printk(KERN_ERR, format , ## arg)
+#define osm_info(format, arg...) \
+ osm_printk(KERN_INFO, format , ## arg)
+#define osm_warn(format, arg...) \
+ osm_printk(KERN_WARNING, format , ## arg)
+
+/* debugging functions */
+extern void i2o_report_status(const char *, const char *, struct i2o_message *);
+extern void i2o_dump_message(struct i2o_message *);
+extern void i2o_dump_hrt(struct i2o_controller *c);
+extern void i2o_debug_state(struct i2o_controller *c);
+
+#endif /* _I2O_H */
diff --git a/drivers/staging/i2o/i2o_block.c b/drivers/staging/i2o/i2o_block.c
new file mode 100644
index 0000000..0a13c64
--- /dev/null
+++ b/drivers/staging/i2o/i2o_block.c
@@ -0,0 +1,1228 @@
+/*
+ * Block OSM
+ *
+ * Copyright (C) 1999-2002 Red Hat Software
+ *
+ * Written by Alan Cox, Building Number Three Ltd
+ *
+ * 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.
+ *
+ * For the purpose of avoiding doubt the preferred form of the work
+ * for making modifications shall be a standards compliant form such
+ * gzipped tar and not one requiring a proprietary or patent encumbered
+ * tool to unpack.
+ *
+ * Fixes/additions:
+ * Steve Ralston:
+ * Multiple device handling error fixes,
+ * Added a queue depth.
+ * Alan Cox:
+ * FC920 has an rmw bug. Dont or in the end marker.
+ * Removed queue walk, fixed for 64bitness.
+ * Rewrote much of the code over time
+ * Added indirect block lists
+ * Handle 64K limits on many controllers
+ * Don't use indirects on the Promise (breaks)
+ * Heavily chop down the queue depths
+ * Deepak Saxena:
+ * Independent queues per IOP
+ * Support for dynamic device creation/deletion
+ * Code cleanup
+ * Support for larger I/Os through merge* functions
+ * (taken from DAC960 driver)
+ * Boji T Kannanthanam:
+ * Set the I2O Block devices to be detected in increasing
+ * order of TIDs during boot.
+ * Search and set the I2O block device that we boot off
+ * from as the first device to be claimed (as /dev/i2o/hda)
+ * Properly attach/detach I2O gendisk structure from the
+ * system gendisk list. The I2O block devices now appear in
+ * /proc/partitions.
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>:
+ * Minor bugfixes for 2.6.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include "i2o.h"
+#include <linux/mutex.h>
+
+#include <linux/mempool.h>
+
+#include <linux/genhd.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+
+#include <scsi/scsi.h>
+
+#include "i2o_block.h"
+
+#define OSM_NAME "block-osm"
+#define OSM_VERSION "1.325"
+#define OSM_DESCRIPTION "I2O Block Device OSM"
+
+static DEFINE_MUTEX(i2o_block_mutex);
+static struct i2o_driver i2o_block_driver;
+
+/* global Block OSM request mempool */
+static struct i2o_block_mempool i2o_blk_req_pool;
+
+/* Block OSM class handling definition */
+static struct i2o_class_id i2o_block_class_id[] = {
+ {I2O_CLASS_RANDOM_BLOCK_STORAGE},
+ {I2O_CLASS_END}
+};
+
+/**
+ * i2o_block_device_free - free the memory of the I2O Block device
+ * @dev: I2O Block device, which should be cleaned up
+ *
+ * Frees the request queue, gendisk and the i2o_block_device structure.
+ */
+static void i2o_block_device_free(struct i2o_block_device *dev)
+{
+ blk_cleanup_queue(dev->gd->queue);
+
+ put_disk(dev->gd);
+
+ kfree(dev);
+};
+
+/**
+ * i2o_block_remove - remove the I2O Block device from the system again
+ * @dev: I2O Block device which should be removed
+ *
+ * Remove gendisk from system and free all allocated memory.
+ *
+ * Always returns 0.
+ */
+static int i2o_block_remove(struct device *dev)
+{
+ struct i2o_device *i2o_dev = to_i2o_device(dev);
+ struct i2o_block_device *i2o_blk_dev = dev_get_drvdata(dev);
+
+ osm_info("device removed (TID: %03x): %s\n", i2o_dev->lct_data.tid,
+ i2o_blk_dev->gd->disk_name);
+
+ i2o_event_register(i2o_dev, &i2o_block_driver, 0, 0);
+
+ del_gendisk(i2o_blk_dev->gd);
+
+ dev_set_drvdata(dev, NULL);
+
+ i2o_device_claim_release(i2o_dev);
+
+ i2o_block_device_free(i2o_blk_dev);
+
+ return 0;
+};
+
+/**
+ * i2o_block_device flush - Flush all dirty data of I2O device dev
+ * @dev: I2O device which should be flushed
+ *
+ * Flushes all dirty data on device dev.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_block_device_flush(struct i2o_device *dev)
+{
+ struct i2o_message *msg;
+
+ msg = i2o_msg_get_wait(dev->iop, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_BLOCK_CFLUSH << 24 | HOST_TID << 12 | dev->
+ lct_data.tid);
+ msg->body[0] = cpu_to_le32(60 << 16);
+ osm_debug("Flushing...\n");
+
+ return i2o_msg_post_wait(dev->iop, msg, 60);
+};
+
+/**
+ * i2o_block_device_mount - Mount (load) the media of device dev
+ * @dev: I2O device which should receive the mount request
+ * @media_id: Media Identifier
+ *
+ * Load a media into drive. Identifier should be set to -1, because the
+ * spec does not support any other value.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_block_device_mount(struct i2o_device *dev, u32 media_id)
+{
+ struct i2o_message *msg;
+
+ msg = i2o_msg_get_wait(dev->iop, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_BLOCK_MMOUNT << 24 | HOST_TID << 12 | dev->
+ lct_data.tid);
+ msg->body[0] = cpu_to_le32(-1);
+ msg->body[1] = cpu_to_le32(0x00000000);
+ osm_debug("Mounting...\n");
+
+ return i2o_msg_post_wait(dev->iop, msg, 2);
+};
+
+/**
+ * i2o_block_device_lock - Locks the media of device dev
+ * @dev: I2O device which should receive the lock request
+ * @media_id: Media Identifier
+ *
+ * Lock media of device dev to prevent removal. The media identifier
+ * should be set to -1, because the spec does not support any other value.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_block_device_lock(struct i2o_device *dev, u32 media_id)
+{
+ struct i2o_message *msg;
+
+ msg = i2o_msg_get_wait(dev->iop, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_BLOCK_MLOCK << 24 | HOST_TID << 12 | dev->
+ lct_data.tid);
+ msg->body[0] = cpu_to_le32(-1);
+ osm_debug("Locking...\n");
+
+ return i2o_msg_post_wait(dev->iop, msg, 2);
+};
+
+/**
+ * i2o_block_device_unlock - Unlocks the media of device dev
+ * @dev: I2O device which should receive the unlocked request
+ * @media_id: Media Identifier
+ *
+ * Unlocks the media in device dev. The media identifier should be set to
+ * -1, because the spec does not support any other value.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_block_device_unlock(struct i2o_device *dev, u32 media_id)
+{
+ struct i2o_message *msg;
+
+ msg = i2o_msg_get_wait(dev->iop, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_BLOCK_MUNLOCK << 24 | HOST_TID << 12 | dev->
+ lct_data.tid);
+ msg->body[0] = cpu_to_le32(media_id);
+ osm_debug("Unlocking...\n");
+
+ return i2o_msg_post_wait(dev->iop, msg, 2);
+};
+
+/**
+ * i2o_block_device_power - Power management for device dev
+ * @dev: I2O device which should receive the power management request
+ * @op: Operation to send
+ *
+ * Send a power management request to the device dev.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_block_device_power(struct i2o_block_device *dev, u8 op)
+{
+ struct i2o_device *i2o_dev = dev->i2o_dev;
+ struct i2o_controller *c = i2o_dev->iop;
+ struct i2o_message *msg;
+ int rc;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_BLOCK_POWER << 24 | HOST_TID << 12 | i2o_dev->
+ lct_data.tid);
+ msg->body[0] = cpu_to_le32(op << 24);
+ osm_debug("Power...\n");
+
+ rc = i2o_msg_post_wait(c, msg, 60);
+ if (!rc)
+ dev->power = op;
+
+ return rc;
+};
+
+/**
+ * i2o_block_request_alloc - Allocate an I2O block request struct
+ *
+ * Allocates an I2O block request struct and initialize the list.
+ *
+ * Returns a i2o_block_request pointer on success or negative error code
+ * on failure.
+ */
+static inline struct i2o_block_request *i2o_block_request_alloc(void)
+{
+ struct i2o_block_request *ireq;
+
+ ireq = mempool_alloc(i2o_blk_req_pool.pool, GFP_ATOMIC);
+ if (!ireq)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&ireq->queue);
+ sg_init_table(ireq->sg_table, I2O_MAX_PHYS_SEGMENTS);
+
+ return ireq;
+};
+
+/**
+ * i2o_block_request_free - Frees a I2O block request
+ * @ireq: I2O block request which should be freed
+ *
+ * Frees the allocated memory (give it back to the request mempool).
+ */
+static inline void i2o_block_request_free(struct i2o_block_request *ireq)
+{
+ mempool_free(ireq, i2o_blk_req_pool.pool);
+};
+
+/**
+ * i2o_block_sglist_alloc - Allocate the SG list and map it
+ * @c: I2O controller to which the request belongs
+ * @ireq: I2O block request
+ * @mptr: message body pointer
+ *
+ * Builds the SG list and map it to be accessible by the controller.
+ *
+ * Returns 0 on failure or 1 on success.
+ */
+static inline int i2o_block_sglist_alloc(struct i2o_controller *c,
+ struct i2o_block_request *ireq,
+ u32 ** mptr)
+{
+ int nents;
+ enum dma_data_direction direction;
+
+ ireq->dev = &c->pdev->dev;
+ nents = blk_rq_map_sg(ireq->req->q, ireq->req, ireq->sg_table);
+
+ if (rq_data_dir(ireq->req) == READ)
+ direction = PCI_DMA_FROMDEVICE;
+ else
+ direction = PCI_DMA_TODEVICE;
+
+ ireq->sg_nents = nents;
+
+ return i2o_dma_map_sg(c, ireq->sg_table, nents, direction, mptr);
+};
+
+/**
+ * i2o_block_sglist_free - Frees the SG list
+ * @ireq: I2O block request from which the SG should be freed
+ *
+ * Frees the SG list from the I2O block request.
+ */
+static inline void i2o_block_sglist_free(struct i2o_block_request *ireq)
+{
+ enum dma_data_direction direction;
+
+ if (rq_data_dir(ireq->req) == READ)
+ direction = PCI_DMA_FROMDEVICE;
+ else
+ direction = PCI_DMA_TODEVICE;
+
+ dma_unmap_sg(ireq->dev, ireq->sg_table, ireq->sg_nents, direction);
+};
+
+/**
+ * i2o_block_prep_req_fn - Allocates I2O block device specific struct
+ * @q: request queue for the request
+ * @req: the request to prepare
+ *
+ * Allocate the necessary i2o_block_request struct and connect it to
+ * the request. This is needed that we not lose the SG list later on.
+ *
+ * Returns BLKPREP_OK on success or BLKPREP_DEFER on failure.
+ */
+static int i2o_block_prep_req_fn(struct request_queue *q, struct request *req)
+{
+ struct i2o_block_device *i2o_blk_dev = q->queuedata;
+ struct i2o_block_request *ireq;
+
+ if (unlikely(!i2o_blk_dev)) {
+ osm_err("block device already removed\n");
+ return BLKPREP_KILL;
+ }
+
+ /* connect the i2o_block_request to the request */
+ if (!req->special) {
+ ireq = i2o_block_request_alloc();
+ if (IS_ERR(ireq)) {
+ osm_debug("unable to allocate i2o_block_request!\n");
+ return BLKPREP_DEFER;
+ }
+
+ ireq->i2o_blk_dev = i2o_blk_dev;
+ req->special = ireq;
+ ireq->req = req;
+ }
+ /* do not come back here */
+ req->cmd_flags |= REQ_DONTPREP;
+
+ return BLKPREP_OK;
+};
+
+/**
+ * i2o_block_delayed_request_fn - delayed request queue function
+ * @work: the delayed request with the queue to start
+ *
+ * If the request queue is stopped for a disk, and there is no open
+ * request, a new event is created, which calls this function to start
+ * the queue after I2O_BLOCK_REQUEST_TIME. Otherwise the queue will never
+ * be started again.
+ */
+static void i2o_block_delayed_request_fn(struct work_struct *work)
+{
+ struct i2o_block_delayed_request *dreq =
+ container_of(work, struct i2o_block_delayed_request,
+ work.work);
+ struct request_queue *q = dreq->queue;
+ unsigned long flags;
+
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_start_queue(q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+ kfree(dreq);
+};
+
+/**
+ * i2o_block_end_request - Post-processing of completed commands
+ * @req: request which should be completed
+ * @error: 0 for success, < 0 for error
+ * @nr_bytes: number of bytes to complete
+ *
+ * Mark the request as complete. The lock must not be held when entering.
+ *
+ */
+static void i2o_block_end_request(struct request *req, int error,
+ int nr_bytes)
+{
+ struct i2o_block_request *ireq = req->special;
+ struct i2o_block_device *dev = ireq->i2o_blk_dev;
+ struct request_queue *q = req->q;
+ unsigned long flags;
+
+ if (blk_end_request(req, error, nr_bytes))
+ if (error)
+ blk_end_request_all(req, -EIO);
+
+ spin_lock_irqsave(q->queue_lock, flags);
+
+ if (likely(dev)) {
+ dev->open_queue_depth--;
+ list_del(&ireq->queue);
+ }
+
+ blk_start_queue(q);
+
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ i2o_block_sglist_free(ireq);
+ i2o_block_request_free(ireq);
+};
+
+/**
+ * i2o_block_reply - Block OSM reply handler.
+ * @c: I2O controller from which the message arrives
+ * @m: message id of reply
+ * @msg: the actual I2O message reply
+ *
+ * This function gets all the message replies.
+ *
+ */
+static int i2o_block_reply(struct i2o_controller *c, u32 m,
+ struct i2o_message *msg)
+{
+ struct request *req;
+ int error = 0;
+
+ req = i2o_cntxt_list_get(c, le32_to_cpu(msg->u.s.tcntxt));
+ if (unlikely(!req)) {
+ osm_err("NULL reply received!\n");
+ return -1;
+ }
+
+ /*
+ * Lets see what is cooking. We stuffed the
+ * request in the context.
+ */
+
+ if ((le32_to_cpu(msg->body[0]) >> 24) != 0) {
+ u32 status = le32_to_cpu(msg->body[0]);
+ /*
+ * Device not ready means two things. One is that the
+ * the thing went offline (but not a removal media)
+ *
+ * The second is that you have a SuperTrak 100 and the
+ * firmware got constipated. Unlike standard i2o card
+ * setups the supertrak returns an error rather than
+ * blocking for the timeout in these cases.
+ *
+ * Don't stick a supertrak100 into cache aggressive modes
+ */
+
+ osm_err("TID %03x error status: 0x%02x, detailed status: "
+ "0x%04x\n", (le32_to_cpu(msg->u.head[1]) >> 12 & 0xfff),
+ status >> 24, status & 0xffff);
+
+ req->errors++;
+
+ error = -EIO;
+ }
+
+ i2o_block_end_request(req, error, le32_to_cpu(msg->body[1]));
+
+ return 1;
+};
+
+static void i2o_block_event(struct work_struct *work)
+{
+ struct i2o_event *evt = container_of(work, struct i2o_event, work);
+ osm_debug("event received\n");
+ kfree(evt);
+};
+
+/*
+ * SCSI-CAM for ioctl geometry mapping
+ * Duplicated with SCSI - this should be moved into somewhere common
+ * perhaps genhd ?
+ *
+ * LBA -> CHS mapping table taken from:
+ *
+ * "Incorporating the I2O Architecture into BIOS for Intel Architecture
+ * Platforms"
+ *
+ * This is an I2O document that is only available to I2O members,
+ * not developers.
+ *
+ * From my understanding, this is how all the I2O cards do this
+ *
+ * Disk Size | Sectors | Heads | Cylinders
+ * ---------------+---------+-------+-------------------
+ * 1 < X <= 528M | 63 | 16 | X/(63 * 16 * 512)
+ * 528M < X <= 1G | 63 | 32 | X/(63 * 32 * 512)
+ * 1 < X <528M | 63 | 16 | X/(63 * 16 * 512)
+ * 1 < X <528M | 63 | 16 | X/(63 * 16 * 512)
+ *
+ */
+#define BLOCK_SIZE_528M 1081344
+#define BLOCK_SIZE_1G 2097152
+#define BLOCK_SIZE_21G 4403200
+#define BLOCK_SIZE_42G 8806400
+#define BLOCK_SIZE_84G 17612800
+
+static void i2o_block_biosparam(unsigned long capacity, unsigned short *cyls,
+ unsigned char *hds, unsigned char *secs)
+{
+ unsigned long heads, sectors, cylinders;
+
+ sectors = 63L; /* Maximize sectors per track */
+ if (capacity <= BLOCK_SIZE_528M)
+ heads = 16;
+ else if (capacity <= BLOCK_SIZE_1G)
+ heads = 32;
+ else if (capacity <= BLOCK_SIZE_21G)
+ heads = 64;
+ else if (capacity <= BLOCK_SIZE_42G)
+ heads = 128;
+ else
+ heads = 255;
+
+ cylinders = (unsigned long)capacity / (heads * sectors);
+
+ *cyls = (unsigned short)cylinders; /* Stuff return values */
+ *secs = (unsigned char)sectors;
+ *hds = (unsigned char)heads;
+}
+
+/**
+ * i2o_block_open - Open the block device
+ * @bdev: block device being opened
+ * @mode: file open mode
+ *
+ * Power up the device, mount and lock the media. This function is called,
+ * if the block device is opened for access.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_block_open(struct block_device *bdev, fmode_t mode)
+{
+ struct i2o_block_device *dev = bdev->bd_disk->private_data;
+
+ if (!dev->i2o_dev)
+ return -ENODEV;
+
+ mutex_lock(&i2o_block_mutex);
+ if (dev->power > 0x1f)
+ i2o_block_device_power(dev, 0x02);
+
+ i2o_block_device_mount(dev->i2o_dev, -1);
+
+ i2o_block_device_lock(dev->i2o_dev, -1);
+
+ osm_debug("Ready.\n");
+ mutex_unlock(&i2o_block_mutex);
+
+ return 0;
+};
+
+/**
+ * i2o_block_release - Release the I2O block device
+ * @disk: gendisk device being released
+ * @mode: file open mode
+ *
+ * Unlock and unmount the media, and power down the device. Gets called if
+ * the block device is closed.
+ */
+static void i2o_block_release(struct gendisk *disk, fmode_t mode)
+{
+ struct i2o_block_device *dev = disk->private_data;
+ u8 operation;
+
+ /*
+ * This is to deal with the case of an application
+ * opening a device and then the device disappears while
+ * it's in use, and then the application tries to release
+ * it. ex: Unmounting a deleted RAID volume at reboot.
+ * If we send messages, it will just cause FAILs since
+ * the TID no longer exists.
+ */
+ if (!dev->i2o_dev)
+ return;
+
+ mutex_lock(&i2o_block_mutex);
+ i2o_block_device_flush(dev->i2o_dev);
+
+ i2o_block_device_unlock(dev->i2o_dev, -1);
+
+ if (dev->flags & (1 << 3 | 1 << 4)) /* Removable */
+ operation = 0x21;
+ else
+ operation = 0x24;
+
+ i2o_block_device_power(dev, operation);
+ mutex_unlock(&i2o_block_mutex);
+}
+
+static int i2o_block_getgeo(struct block_device *bdev, struct hd_geometry *geo)
+{
+ i2o_block_biosparam(get_capacity(bdev->bd_disk),
+ &geo->cylinders, &geo->heads, &geo->sectors);
+ return 0;
+}
+
+/**
+ * i2o_block_ioctl - Issue device specific ioctl calls.
+ * @bdev: block device being opened
+ * @mode: file open mode
+ * @cmd: ioctl command
+ * @arg: arg
+ *
+ * Handles ioctl request for the block device.
+ *
+ * Return 0 on success or negative error on failure.
+ */
+static int i2o_block_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned int cmd, unsigned long arg)
+{
+ struct gendisk *disk = bdev->bd_disk;
+ struct i2o_block_device *dev = disk->private_data;
+ int ret = -ENOTTY;
+
+ /* Anyone capable of this syscall can do *real bad* things */
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ mutex_lock(&i2o_block_mutex);
+ switch (cmd) {
+ case BLKI2OGRSTRAT:
+ ret = put_user(dev->rcache, (int __user *)arg);
+ break;
+ case BLKI2OGWSTRAT:
+ ret = put_user(dev->wcache, (int __user *)arg);
+ break;
+ case BLKI2OSRSTRAT:
+ ret = -EINVAL;
+ if (arg < 0 || arg > CACHE_SMARTFETCH)
+ break;
+ dev->rcache = arg;
+ ret = 0;
+ break;
+ case BLKI2OSWSTRAT:
+ ret = -EINVAL;
+ if (arg != 0
+ && (arg < CACHE_WRITETHROUGH || arg > CACHE_SMARTBACK))
+ break;
+ dev->wcache = arg;
+ ret = 0;
+ break;
+ }
+ mutex_unlock(&i2o_block_mutex);
+
+ return ret;
+};
+
+/**
+ * i2o_block_check_events - Have we seen a media change?
+ * @disk: gendisk which should be verified
+ * @clearing: events being cleared
+ *
+ * Verifies if the media has changed.
+ *
+ * Returns 1 if the media was changed or 0 otherwise.
+ */
+static unsigned int i2o_block_check_events(struct gendisk *disk,
+ unsigned int clearing)
+{
+ struct i2o_block_device *p = disk->private_data;
+
+ if (p->media_change_flag) {
+ p->media_change_flag = 0;
+ return DISK_EVENT_MEDIA_CHANGE;
+ }
+ return 0;
+}
+
+/**
+ * i2o_block_transfer - Transfer a request to/from the I2O controller
+ * @req: the request which should be transferred
+ *
+ * This function converts the request into a I2O message. The necessary
+ * DMA buffers are allocated and after everything is setup post the message
+ * to the I2O controller. No cleanup is done by this function. It is done
+ * on the interrupt side when the reply arrives.
+ *
+ * Return 0 on success or negative error code on failure.
+ */
+static int i2o_block_transfer(struct request *req)
+{
+ struct i2o_block_device *dev = req->rq_disk->private_data;
+ struct i2o_controller *c;
+ u32 tid;
+ struct i2o_message *msg;
+ u32 *mptr;
+ struct i2o_block_request *ireq = req->special;
+ u32 tcntxt;
+ u32 sgl_offset = SGL_OFFSET_8;
+ u32 ctl_flags = 0x00000000;
+ int rc;
+ u32 cmd;
+
+ if (unlikely(!dev->i2o_dev)) {
+ osm_err("transfer to removed drive\n");
+ rc = -ENODEV;
+ goto exit;
+ }
+
+ tid = dev->i2o_dev->lct_data.tid;
+ c = dev->i2o_dev->iop;
+
+ msg = i2o_msg_get(c);
+ if (IS_ERR(msg)) {
+ rc = PTR_ERR(msg);
+ goto exit;
+ }
+
+ tcntxt = i2o_cntxt_list_add(c, req);
+ if (!tcntxt) {
+ rc = -ENOMEM;
+ goto nop_msg;
+ }
+
+ msg->u.s.icntxt = cpu_to_le32(i2o_block_driver.context);
+ msg->u.s.tcntxt = cpu_to_le32(tcntxt);
+
+ mptr = &msg->body[0];
+
+ if (rq_data_dir(req) == READ) {
+ cmd = I2O_CMD_BLOCK_READ << 24;
+
+ switch (dev->rcache) {
+ case CACHE_PREFETCH:
+ ctl_flags = 0x201F0008;
+ break;
+
+ case CACHE_SMARTFETCH:
+ if (blk_rq_sectors(req) > 16)
+ ctl_flags = 0x201F0008;
+ else
+ ctl_flags = 0x001F0000;
+ break;
+
+ default:
+ break;
+ }
+ } else {
+ cmd = I2O_CMD_BLOCK_WRITE << 24;
+
+ switch (dev->wcache) {
+ case CACHE_WRITETHROUGH:
+ ctl_flags = 0x001F0008;
+ break;
+ case CACHE_WRITEBACK:
+ ctl_flags = 0x001F0010;
+ break;
+ case CACHE_SMARTBACK:
+ if (blk_rq_sectors(req) > 16)
+ ctl_flags = 0x001F0004;
+ else
+ ctl_flags = 0x001F0010;
+ break;
+ case CACHE_SMARTTHROUGH:
+ if (blk_rq_sectors(req) > 16)
+ ctl_flags = 0x001F0004;
+ else
+ ctl_flags = 0x001F0010;
+ default:
+ break;
+ }
+ }
+
+#ifdef CONFIG_I2O_EXT_ADAPTEC
+ if (c->adaptec) {
+ u8 cmd[10];
+ u32 scsi_flags;
+ u16 hwsec;
+
+ hwsec = queue_logical_block_size(req->q) >> KERNEL_SECTOR_SHIFT;
+ memset(cmd, 0, 10);
+
+ sgl_offset = SGL_OFFSET_12;
+
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_PRIVATE << 24 | HOST_TID << 12 | tid);
+
+ *mptr++ = cpu_to_le32(I2O_VENDOR_DPT << 16 | I2O_CMD_SCSI_EXEC);
+ *mptr++ = cpu_to_le32(tid);
+
+ /*
+ * ENABLE_DISCONNECT
+ * SIMPLE_TAG
+ * RETURN_SENSE_DATA_IN_REPLY_MESSAGE_FRAME
+ */
+ if (rq_data_dir(req) == READ) {
+ cmd[0] = READ_10;
+ scsi_flags = 0x60a0000a;
+ } else {
+ cmd[0] = WRITE_10;
+ scsi_flags = 0xa0a0000a;
+ }
+
+ *mptr++ = cpu_to_le32(scsi_flags);
+
+ *((u32 *) & cmd[2]) = cpu_to_be32(blk_rq_pos(req) * hwsec);
+ *((u16 *) & cmd[7]) = cpu_to_be16(blk_rq_sectors(req) * hwsec);
+
+ memcpy(mptr, cmd, 10);
+ mptr += 4;
+ *mptr++ = cpu_to_le32(blk_rq_bytes(req));
+ } else
+#endif
+ {
+ msg->u.head[1] = cpu_to_le32(cmd | HOST_TID << 12 | tid);
+ *mptr++ = cpu_to_le32(ctl_flags);
+ *mptr++ = cpu_to_le32(blk_rq_bytes(req));
+ *mptr++ =
+ cpu_to_le32((u32) (blk_rq_pos(req) << KERNEL_SECTOR_SHIFT));
+ *mptr++ =
+ cpu_to_le32(blk_rq_pos(req) >> (32 - KERNEL_SECTOR_SHIFT));
+ }
+
+ if (!i2o_block_sglist_alloc(c, ireq, &mptr)) {
+ rc = -ENOMEM;
+ goto context_remove;
+ }
+
+ msg->u.head[0] =
+ cpu_to_le32(I2O_MESSAGE_SIZE(mptr - &msg->u.head[0]) | sgl_offset);
+
+ list_add_tail(&ireq->queue, &dev->open_queue);
+ dev->open_queue_depth++;
+
+ i2o_msg_post(c, msg);
+
+ return 0;
+
+ context_remove:
+ i2o_cntxt_list_remove(c, req);
+
+ nop_msg:
+ i2o_msg_nop(c, msg);
+
+ exit:
+ return rc;
+};
+
+/**
+ * i2o_block_request_fn - request queue handling function
+ * @q: request queue from which the request could be fetched
+ *
+ * Takes the next request from the queue, transfers it and if no error
+ * occurs dequeue it from the queue. On arrival of the reply the message
+ * will be processed further. If an error occurs requeue the request.
+ */
+static void i2o_block_request_fn(struct request_queue *q)
+{
+ struct request *req;
+
+ while ((req = blk_peek_request(q)) != NULL) {
+ if (req->cmd_type == REQ_TYPE_FS) {
+ struct i2o_block_delayed_request *dreq;
+ struct i2o_block_request *ireq = req->special;
+ unsigned int queue_depth;
+
+ queue_depth = ireq->i2o_blk_dev->open_queue_depth;
+
+ if (queue_depth < I2O_BLOCK_MAX_OPEN_REQUESTS) {
+ if (!i2o_block_transfer(req)) {
+ blk_start_request(req);
+ continue;
+ } else
+ osm_info("transfer error\n");
+ }
+
+ if (queue_depth)
+ break;
+
+ /* stop the queue and retry later */
+ dreq = kmalloc(sizeof(*dreq), GFP_ATOMIC);
+ if (!dreq)
+ continue;
+
+ dreq->queue = q;
+ INIT_DELAYED_WORK(&dreq->work,
+ i2o_block_delayed_request_fn);
+
+ if (!queue_delayed_work(i2o_block_driver.event_queue,
+ &dreq->work,
+ I2O_BLOCK_RETRY_TIME))
+ kfree(dreq);
+ else {
+ blk_stop_queue(q);
+ break;
+ }
+ } else {
+ blk_start_request(req);
+ __blk_end_request_all(req, -EIO);
+ }
+ }
+};
+
+/* I2O Block device operations definition */
+static const struct block_device_operations i2o_block_fops = {
+ .owner = THIS_MODULE,
+ .open = i2o_block_open,
+ .release = i2o_block_release,
+ .ioctl = i2o_block_ioctl,
+ .compat_ioctl = i2o_block_ioctl,
+ .getgeo = i2o_block_getgeo,
+ .check_events = i2o_block_check_events,
+};
+
+/**
+ * i2o_block_device_alloc - Allocate memory for a I2O Block device
+ *
+ * Allocate memory for the i2o_block_device struct, gendisk and request
+ * queue and initialize them as far as no additional information is needed.
+ *
+ * Returns a pointer to the allocated I2O Block device on success or a
+ * negative error code on failure.
+ */
+static struct i2o_block_device *i2o_block_device_alloc(void)
+{
+ struct i2o_block_device *dev;
+ struct gendisk *gd;
+ struct request_queue *queue;
+ int rc;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ osm_err("Insufficient memory to allocate I2O Block disk.\n");
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+ INIT_LIST_HEAD(&dev->open_queue);
+ spin_lock_init(&dev->lock);
+ dev->rcache = CACHE_PREFETCH;
+ dev->wcache = CACHE_WRITEBACK;
+
+ /* allocate a gendisk with 16 partitions */
+ gd = alloc_disk(16);
+ if (!gd) {
+ osm_err("Insufficient memory to allocate gendisk.\n");
+ rc = -ENOMEM;
+ goto cleanup_dev;
+ }
+
+ /* initialize the request queue */
+ queue = blk_init_queue(i2o_block_request_fn, &dev->lock);
+ if (!queue) {
+ osm_err("Insufficient memory to allocate request queue.\n");
+ rc = -ENOMEM;
+ goto cleanup_queue;
+ }
+
+ blk_queue_prep_rq(queue, i2o_block_prep_req_fn);
+
+ gd->major = I2O_MAJOR;
+ gd->queue = queue;
+ gd->fops = &i2o_block_fops;
+ gd->private_data = dev;
+
+ dev->gd = gd;
+
+ return dev;
+
+ cleanup_queue:
+ put_disk(gd);
+
+ cleanup_dev:
+ kfree(dev);
+
+ exit:
+ return ERR_PTR(rc);
+};
+
+/**
+ * i2o_block_probe - verify if dev is a I2O Block device and install it
+ * @dev: device to verify if it is a I2O Block device
+ *
+ * We only verify if the user_tid of the device is 0xfff and then install
+ * the device. Otherwise it is used by some other device (e. g. RAID).
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_block_probe(struct device *dev)
+{
+ struct i2o_device *i2o_dev = to_i2o_device(dev);
+ struct i2o_controller *c = i2o_dev->iop;
+ struct i2o_block_device *i2o_blk_dev;
+ struct gendisk *gd;
+ struct request_queue *queue;
+ static int unit = 0;
+ int rc;
+ u64 size;
+ u32 blocksize;
+ u16 body_size = 4;
+ u16 power;
+ unsigned short max_sectors;
+
+#ifdef CONFIG_I2O_EXT_ADAPTEC
+ if (c->adaptec)
+ body_size = 8;
+#endif
+
+ if (c->limit_sectors)
+ max_sectors = I2O_MAX_SECTORS_LIMITED;
+ else
+ max_sectors = I2O_MAX_SECTORS;
+
+ /* skip devices which are used by IOP */
+ if (i2o_dev->lct_data.user_tid != 0xfff) {
+ osm_debug("skipping used device %03x\n", i2o_dev->lct_data.tid);
+ return -ENODEV;
+ }
+
+ if (i2o_device_claim(i2o_dev)) {
+ osm_warn("Unable to claim device. Installation aborted\n");
+ rc = -EFAULT;
+ goto exit;
+ }
+
+ i2o_blk_dev = i2o_block_device_alloc();
+ if (IS_ERR(i2o_blk_dev)) {
+ osm_err("could not alloc a new I2O block device");
+ rc = PTR_ERR(i2o_blk_dev);
+ goto claim_release;
+ }
+
+ i2o_blk_dev->i2o_dev = i2o_dev;
+ dev_set_drvdata(dev, i2o_blk_dev);
+
+ /* setup gendisk */
+ gd = i2o_blk_dev->gd;
+ gd->first_minor = unit << 4;
+ sprintf(gd->disk_name, "i2o/hd%c", 'a' + unit);
+ gd->driverfs_dev = &i2o_dev->device;
+
+ /* setup request queue */
+ queue = gd->queue;
+ queue->queuedata = i2o_blk_dev;
+
+ blk_queue_max_hw_sectors(queue, max_sectors);
+ blk_queue_max_segments(queue, i2o_sg_tablesize(c, body_size));
+
+ osm_debug("max sectors = %d\n", queue->max_sectors);
+ osm_debug("phys segments = %d\n", queue->max_phys_segments);
+ osm_debug("max hw segments = %d\n", queue->max_hw_segments);
+
+ /*
+ * Ask for the current media data. If that isn't supported
+ * then we ask for the device capacity data
+ */
+ if (!i2o_parm_field_get(i2o_dev, 0x0004, 1, &blocksize, 4) ||
+ !i2o_parm_field_get(i2o_dev, 0x0000, 3, &blocksize, 4)) {
+ blk_queue_logical_block_size(queue, le32_to_cpu(blocksize));
+ } else
+ osm_warn("unable to get blocksize of %s\n", gd->disk_name);
+
+ if (!i2o_parm_field_get(i2o_dev, 0x0004, 0, &size, 8) ||
+ !i2o_parm_field_get(i2o_dev, 0x0000, 4, &size, 8)) {
+ set_capacity(gd, le64_to_cpu(size) >> KERNEL_SECTOR_SHIFT);
+ } else
+ osm_warn("could not get size of %s\n", gd->disk_name);
+
+ if (!i2o_parm_field_get(i2o_dev, 0x0000, 2, &power, 2))
+ i2o_blk_dev->power = power;
+
+ i2o_event_register(i2o_dev, &i2o_block_driver, 0, 0xffffffff);
+
+ add_disk(gd);
+
+ unit++;
+
+ osm_info("device added (TID: %03x): %s\n", i2o_dev->lct_data.tid,
+ i2o_blk_dev->gd->disk_name);
+
+ return 0;
+
+ claim_release:
+ i2o_device_claim_release(i2o_dev);
+
+ exit:
+ return rc;
+};
+
+/* Block OSM driver struct */
+static struct i2o_driver i2o_block_driver = {
+ .name = OSM_NAME,
+ .event = i2o_block_event,
+ .reply = i2o_block_reply,
+ .classes = i2o_block_class_id,
+ .driver = {
+ .probe = i2o_block_probe,
+ .remove = i2o_block_remove,
+ },
+};
+
+/**
+ * i2o_block_init - Block OSM initialization function
+ *
+ * Allocate the slab and mempool for request structs, registers i2o_block
+ * block device and finally register the Block OSM in the I2O core.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int __init i2o_block_init(void)
+{
+ int rc;
+ int size;
+
+ printk(KERN_INFO OSM_DESCRIPTION " v" OSM_VERSION "\n");
+
+ /* Allocate request mempool and slab */
+ size = sizeof(struct i2o_block_request);
+ i2o_blk_req_pool.slab = kmem_cache_create("i2o_block_req", size, 0,
+ SLAB_HWCACHE_ALIGN, NULL);
+ if (!i2o_blk_req_pool.slab) {
+ osm_err("can't init request slab\n");
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+ i2o_blk_req_pool.pool =
+ mempool_create_slab_pool(I2O_BLOCK_REQ_MEMPOOL_SIZE,
+ i2o_blk_req_pool.slab);
+ if (!i2o_blk_req_pool.pool) {
+ osm_err("can't init request mempool\n");
+ rc = -ENOMEM;
+ goto free_slab;
+ }
+
+ /* Register the block device interfaces */
+ rc = register_blkdev(I2O_MAJOR, "i2o_block");
+ if (rc) {
+ osm_err("unable to register block device\n");
+ goto free_mempool;
+ }
+#ifdef MODULE
+ osm_info("registered device at major %d\n", I2O_MAJOR);
+#endif
+
+ /* Register Block OSM into I2O core */
+ rc = i2o_driver_register(&i2o_block_driver);
+ if (rc) {
+ osm_err("Could not register Block driver\n");
+ goto unregister_blkdev;
+ }
+
+ return 0;
+
+ unregister_blkdev:
+ unregister_blkdev(I2O_MAJOR, "i2o_block");
+
+ free_mempool:
+ mempool_destroy(i2o_blk_req_pool.pool);
+
+ free_slab:
+ kmem_cache_destroy(i2o_blk_req_pool.slab);
+
+ exit:
+ return rc;
+};
+
+/**
+ * i2o_block_exit - Block OSM exit function
+ *
+ * Unregisters Block OSM from I2O core, unregisters i2o_block block device
+ * and frees the mempool and slab.
+ */
+static void __exit i2o_block_exit(void)
+{
+ /* Unregister I2O Block OSM from I2O core */
+ i2o_driver_unregister(&i2o_block_driver);
+
+ /* Unregister block device */
+ unregister_blkdev(I2O_MAJOR, "i2o_block");
+
+ /* Free request mempool and slab */
+ mempool_destroy(i2o_blk_req_pool.pool);
+ kmem_cache_destroy(i2o_blk_req_pool.slab);
+};
+
+MODULE_AUTHOR("Red Hat");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(OSM_DESCRIPTION);
+MODULE_VERSION(OSM_VERSION);
+
+module_init(i2o_block_init);
+module_exit(i2o_block_exit);
diff --git a/drivers/staging/i2o/i2o_block.h b/drivers/staging/i2o/i2o_block.h
new file mode 100644
index 0000000..cf8873c
--- /dev/null
+++ b/drivers/staging/i2o/i2o_block.h
@@ -0,0 +1,103 @@
+/*
+ * Block OSM structures/API
+ *
+ * Copyright (C) 1999-2002 Red Hat Software
+ *
+ * Written by Alan Cox, Building Number Three Ltd
+ *
+ * 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.
+ *
+ * For the purpose of avoiding doubt the preferred form of the work
+ * for making modifications shall be a standards compliant form such
+ * gzipped tar and not one requiring a proprietary or patent encumbered
+ * tool to unpack.
+ *
+ * Fixes/additions:
+ * Steve Ralston:
+ * Multiple device handling error fixes,
+ * Added a queue depth.
+ * Alan Cox:
+ * FC920 has an rmw bug. Dont or in the end marker.
+ * Removed queue walk, fixed for 64bitness.
+ * Rewrote much of the code over time
+ * Added indirect block lists
+ * Handle 64K limits on many controllers
+ * Don't use indirects on the Promise (breaks)
+ * Heavily chop down the queue depths
+ * Deepak Saxena:
+ * Independent queues per IOP
+ * Support for dynamic device creation/deletion
+ * Code cleanup
+ * Support for larger I/Os through merge* functions
+ * (taken from DAC960 driver)
+ * Boji T Kannanthanam:
+ * Set the I2O Block devices to be detected in increasing
+ * order of TIDs during boot.
+ * Search and set the I2O block device that we boot off
+ * from as the first device to be claimed (as /dev/i2o/hda)
+ * Properly attach/detach I2O gendisk structure from the
+ * system gendisk list. The I2O block devices now appear in
+ * /proc/partitions.
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>:
+ * Minor bugfixes for 2.6.
+ */
+
+#ifndef I2O_BLOCK_OSM_H
+#define I2O_BLOCK_OSM_H
+
+#define I2O_BLOCK_RETRY_TIME HZ/4
+#define I2O_BLOCK_MAX_OPEN_REQUESTS 50
+
+/* request queue sizes */
+#define I2O_BLOCK_REQ_MEMPOOL_SIZE 32
+
+#define KERNEL_SECTOR_SHIFT 9
+#define KERNEL_SECTOR_SIZE (1 << KERNEL_SECTOR_SHIFT)
+
+/* I2O Block OSM mempool struct */
+struct i2o_block_mempool {
+ struct kmem_cache *slab;
+ mempool_t *pool;
+};
+
+/* I2O Block device descriptor */
+struct i2o_block_device {
+ struct i2o_device *i2o_dev; /* pointer to I2O device */
+ struct gendisk *gd;
+ spinlock_t lock; /* queue lock */
+ struct list_head open_queue; /* list of transferred, but unfinished
+ requests */
+ unsigned int open_queue_depth; /* number of requests in the queue */
+
+ int rcache; /* read cache flags */
+ int wcache; /* write cache flags */
+ int flags;
+ u16 power; /* power state */
+ int media_change_flag; /* media changed flag */
+};
+
+/* I2O Block device request */
+struct i2o_block_request {
+ struct list_head queue;
+ struct request *req; /* corresponding request */
+ struct i2o_block_device *i2o_blk_dev; /* I2O block device */
+ struct device *dev; /* device used for DMA */
+ int sg_nents; /* number of SG elements */
+ struct scatterlist sg_table[I2O_MAX_PHYS_SEGMENTS]; /* SG table */
+};
+
+/* I2O Block device delayed request */
+struct i2o_block_delayed_request {
+ struct delayed_work work;
+ struct request_queue *queue;
+};
+
+#endif
diff --git a/drivers/staging/i2o/i2o_config.c b/drivers/staging/i2o/i2o_config.c
new file mode 100644
index 0000000..04bd3b6
--- /dev/null
+++ b/drivers/staging/i2o/i2o_config.c
@@ -0,0 +1,1163 @@
+/*
+ * I2O Configuration Interface Driver
+ *
+ * (C) Copyright 1999-2002 Red Hat
+ *
+ * Written by Alan Cox, Building Number Three Ltd
+ *
+ * Fixes/additions:
+ * Deepak Saxena (04/20/1999):
+ * Added basic ioctl() support
+ * Deepak Saxena (06/07/1999):
+ * Added software download ioctl (still testing)
+ * Auvo Häkkinen (09/10/1999):
+ * Changes to i2o_cfg_reply(), ioctl_parms()
+ * Added ioct_validate()
+ * Taneli Vähäkangas (09/30/1999):
+ * Fixed ioctl_swdl()
+ * Taneli Vähäkangas (10/04/1999):
+ * Changed ioctl_swdl(), implemented ioctl_swul() and ioctl_swdel()
+ * Deepak Saxena (11/18/1999):
+ * Added event managmenet support
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>:
+ * 2.4 rewrite ported to 2.5
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>:
+ * Added pass-thru support for Adaptec's raidutils
+ *
+ * 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.
+ */
+
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/compat.h>
+#include <linux/slab.h>
+
+#include <asm/uaccess.h>
+
+#include "core.h"
+
+#define SG_TABLESIZE 30
+
+static DEFINE_MUTEX(i2o_cfg_mutex);
+static long i2o_cfg_ioctl(struct file *, unsigned int, unsigned long);
+
+static spinlock_t i2o_config_lock;
+
+#define MODINC(x,y) ((x) = ((x) + 1) % (y))
+
+struct sg_simple_element {
+ u32 flag_count;
+ u32 addr_bus;
+};
+
+struct i2o_cfg_info {
+ struct file *fp;
+ struct fasync_struct *fasync;
+ struct i2o_evt_info event_q[I2O_EVT_Q_LEN];
+ u16 q_in; // Queue head index
+ u16 q_out; // Queue tail index
+ u16 q_len; // Queue length
+ u16 q_lost; // Number of lost events
+ ulong q_id; // Event queue ID...used as tx_context
+ struct i2o_cfg_info *next;
+};
+static struct i2o_cfg_info *open_files = NULL;
+static ulong i2o_cfg_info_id = 0;
+
+static int i2o_cfg_getiops(unsigned long arg)
+{
+ struct i2o_controller *c;
+ u8 __user *user_iop_table = (void __user *)arg;
+ u8 tmp[MAX_I2O_CONTROLLERS];
+ int ret = 0;
+
+ memset(tmp, 0, MAX_I2O_CONTROLLERS);
+
+ list_for_each_entry(c, &i2o_controllers, list)
+ tmp[c->unit] = 1;
+
+ if (copy_to_user(user_iop_table, tmp, MAX_I2O_CONTROLLERS))
+ ret = -EFAULT;
+
+ return ret;
+};
+
+static int i2o_cfg_gethrt(unsigned long arg)
+{
+ struct i2o_controller *c;
+ struct i2o_cmd_hrtlct __user *cmd = (struct i2o_cmd_hrtlct __user *)arg;
+ struct i2o_cmd_hrtlct kcmd;
+ i2o_hrt *hrt;
+ int len;
+ u32 reslen;
+ int ret = 0;
+
+ if (copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_hrtlct)))
+ return -EFAULT;
+
+ if (get_user(reslen, kcmd.reslen) < 0)
+ return -EFAULT;
+
+ if (kcmd.resbuf == NULL)
+ return -EFAULT;
+
+ c = i2o_find_iop(kcmd.iop);
+ if (!c)
+ return -ENXIO;
+
+ hrt = (i2o_hrt *) c->hrt.virt;
+
+ len = 8 + ((hrt->entry_len * hrt->num_entries) << 2);
+
+ if (put_user(len, kcmd.reslen))
+ ret = -EFAULT;
+ else if (len > reslen)
+ ret = -ENOBUFS;
+ else if (copy_to_user(kcmd.resbuf, (void *)hrt, len))
+ ret = -EFAULT;
+
+ return ret;
+};
+
+static int i2o_cfg_getlct(unsigned long arg)
+{
+ struct i2o_controller *c;
+ struct i2o_cmd_hrtlct __user *cmd = (struct i2o_cmd_hrtlct __user *)arg;
+ struct i2o_cmd_hrtlct kcmd;
+ i2o_lct *lct;
+ int len;
+ int ret = 0;
+ u32 reslen;
+
+ if (copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_hrtlct)))
+ return -EFAULT;
+
+ if (get_user(reslen, kcmd.reslen) < 0)
+ return -EFAULT;
+
+ if (kcmd.resbuf == NULL)
+ return -EFAULT;
+
+ c = i2o_find_iop(kcmd.iop);
+ if (!c)
+ return -ENXIO;
+
+ lct = (i2o_lct *) c->lct;
+
+ len = (unsigned int)lct->table_size << 2;
+ if (put_user(len, kcmd.reslen))
+ ret = -EFAULT;
+ else if (len > reslen)
+ ret = -ENOBUFS;
+ else if (copy_to_user(kcmd.resbuf, lct, len))
+ ret = -EFAULT;
+
+ return ret;
+};
+
+static int i2o_cfg_parms(unsigned long arg, unsigned int type)
+{
+ int ret = 0;
+ struct i2o_controller *c;
+ struct i2o_device *dev;
+ struct i2o_cmd_psetget __user *cmd =
+ (struct i2o_cmd_psetget __user *)arg;
+ struct i2o_cmd_psetget kcmd;
+ u32 reslen;
+ u8 *ops;
+ u8 *res;
+ int len = 0;
+
+ u32 i2o_cmd = (type == I2OPARMGET ?
+ I2O_CMD_UTIL_PARAMS_GET : I2O_CMD_UTIL_PARAMS_SET);
+
+ if (copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_psetget)))
+ return -EFAULT;
+
+ if (get_user(reslen, kcmd.reslen))
+ return -EFAULT;
+
+ c = i2o_find_iop(kcmd.iop);
+ if (!c)
+ return -ENXIO;
+
+ dev = i2o_iop_find_device(c, kcmd.tid);
+ if (!dev)
+ return -ENXIO;
+
+ /*
+ * Stop users being able to try and allocate arbitrary amounts
+ * of DMA space. 64K is way more than sufficient for this.
+ */
+ if (kcmd.oplen > 65536)
+ return -EMSGSIZE;
+
+ ops = memdup_user(kcmd.opbuf, kcmd.oplen);
+ if (IS_ERR(ops))
+ return PTR_ERR(ops);
+
+ /*
+ * It's possible to have a _very_ large table
+ * and that the user asks for all of it at once...
+ */
+ res = kmalloc(65536, GFP_KERNEL);
+ if (!res) {
+ kfree(ops);
+ return -ENOMEM;
+ }
+
+ len = i2o_parm_issue(dev, i2o_cmd, ops, kcmd.oplen, res, 65536);
+ kfree(ops);
+
+ if (len < 0) {
+ kfree(res);
+ return -EAGAIN;
+ }
+
+ if (put_user(len, kcmd.reslen))
+ ret = -EFAULT;
+ else if (len > reslen)
+ ret = -ENOBUFS;
+ else if (copy_to_user(kcmd.resbuf, res, len))
+ ret = -EFAULT;
+
+ kfree(res);
+
+ return ret;
+};
+
+static int i2o_cfg_swdl(unsigned long arg)
+{
+ struct i2o_sw_xfer kxfer;
+ struct i2o_sw_xfer __user *pxfer = (struct i2o_sw_xfer __user *)arg;
+ unsigned char maxfrag = 0, curfrag = 1;
+ struct i2o_dma buffer;
+ struct i2o_message *msg;
+ unsigned int status = 0, swlen = 0, fragsize = 8192;
+ struct i2o_controller *c;
+
+ if (copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer)))
+ return -EFAULT;
+
+ if (get_user(swlen, kxfer.swlen) < 0)
+ return -EFAULT;
+
+ if (get_user(maxfrag, kxfer.maxfrag) < 0)
+ return -EFAULT;
+
+ if (get_user(curfrag, kxfer.curfrag) < 0)
+ return -EFAULT;
+
+ if (curfrag == maxfrag)
+ fragsize = swlen - (maxfrag - 1) * 8192;
+
+ if (!kxfer.buf || !access_ok(VERIFY_READ, kxfer.buf, fragsize))
+ return -EFAULT;
+
+ c = i2o_find_iop(kxfer.iop);
+ if (!c)
+ return -ENXIO;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ if (i2o_dma_alloc(&c->pdev->dev, &buffer, fragsize)) {
+ i2o_msg_nop(c, msg);
+ return -ENOMEM;
+ }
+
+ if (__copy_from_user(buffer.virt, kxfer.buf, fragsize)) {
+ i2o_msg_nop(c, msg);
+ i2o_dma_free(&c->pdev->dev, &buffer);
+ return -EFAULT;
+ }
+
+ msg->u.head[0] = cpu_to_le32(NINE_WORD_MSG_SIZE | SGL_OFFSET_7);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_SW_DOWNLOAD << 24 | HOST_TID << 12 |
+ ADAPTER_TID);
+ msg->u.head[2] = cpu_to_le32(i2o_config_driver.context);
+ msg->u.head[3] = cpu_to_le32(0);
+ msg->body[0] =
+ cpu_to_le32((((u32) kxfer.flags) << 24) | (((u32) kxfer.
+ sw_type) << 16) |
+ (((u32) maxfrag) << 8) | (((u32) curfrag)));
+ msg->body[1] = cpu_to_le32(swlen);
+ msg->body[2] = cpu_to_le32(kxfer.sw_id);
+ msg->body[3] = cpu_to_le32(0xD0000000 | fragsize);
+ msg->body[4] = cpu_to_le32(buffer.phys);
+
+ osm_debug("swdl frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize);
+ status = i2o_msg_post_wait_mem(c, msg, 60, &buffer);
+
+ if (status != -ETIMEDOUT)
+ i2o_dma_free(&c->pdev->dev, &buffer);
+
+ if (status != I2O_POST_WAIT_OK) {
+ // it fails if you try and send frags out of order
+ // and for some yet unknown reasons too
+ osm_info("swdl failed, DetailedStatus = %d\n", status);
+ return status;
+ }
+
+ return 0;
+};
+
+static int i2o_cfg_swul(unsigned long arg)
+{
+ struct i2o_sw_xfer kxfer;
+ struct i2o_sw_xfer __user *pxfer = (struct i2o_sw_xfer __user *)arg;
+ unsigned char maxfrag = 0, curfrag = 1;
+ struct i2o_dma buffer;
+ struct i2o_message *msg;
+ unsigned int status = 0, swlen = 0, fragsize = 8192;
+ struct i2o_controller *c;
+ int ret = 0;
+
+ if (copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer)))
+ return -EFAULT;
+
+ if (get_user(swlen, kxfer.swlen) < 0)
+ return -EFAULT;
+
+ if (get_user(maxfrag, kxfer.maxfrag) < 0)
+ return -EFAULT;
+
+ if (get_user(curfrag, kxfer.curfrag) < 0)
+ return -EFAULT;
+
+ if (curfrag == maxfrag)
+ fragsize = swlen - (maxfrag - 1) * 8192;
+
+ if (!kxfer.buf)
+ return -EFAULT;
+
+ c = i2o_find_iop(kxfer.iop);
+ if (!c)
+ return -ENXIO;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ if (i2o_dma_alloc(&c->pdev->dev, &buffer, fragsize)) {
+ i2o_msg_nop(c, msg);
+ return -ENOMEM;
+ }
+
+ msg->u.head[0] = cpu_to_le32(NINE_WORD_MSG_SIZE | SGL_OFFSET_7);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_SW_UPLOAD << 24 | HOST_TID << 12 | ADAPTER_TID);
+ msg->u.head[2] = cpu_to_le32(i2o_config_driver.context);
+ msg->u.head[3] = cpu_to_le32(0);
+ msg->body[0] =
+ cpu_to_le32((u32) kxfer.flags << 24 | (u32) kxfer.
+ sw_type << 16 | (u32) maxfrag << 8 | (u32) curfrag);
+ msg->body[1] = cpu_to_le32(swlen);
+ msg->body[2] = cpu_to_le32(kxfer.sw_id);
+ msg->body[3] = cpu_to_le32(0xD0000000 | fragsize);
+ msg->body[4] = cpu_to_le32(buffer.phys);
+
+ osm_debug("swul frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize);
+ status = i2o_msg_post_wait_mem(c, msg, 60, &buffer);
+
+ if (status != I2O_POST_WAIT_OK) {
+ if (status != -ETIMEDOUT)
+ i2o_dma_free(&c->pdev->dev, &buffer);
+
+ osm_info("swul failed, DetailedStatus = %d\n", status);
+ return status;
+ }
+
+ if (copy_to_user(kxfer.buf, buffer.virt, fragsize))
+ ret = -EFAULT;
+
+ i2o_dma_free(&c->pdev->dev, &buffer);
+
+ return ret;
+}
+
+static int i2o_cfg_swdel(unsigned long arg)
+{
+ struct i2o_controller *c;
+ struct i2o_sw_xfer kxfer;
+ struct i2o_sw_xfer __user *pxfer = (struct i2o_sw_xfer __user *)arg;
+ struct i2o_message *msg;
+ unsigned int swlen;
+ int token;
+
+ if (copy_from_user(&kxfer, pxfer, sizeof(struct i2o_sw_xfer)))
+ return -EFAULT;
+
+ if (get_user(swlen, kxfer.swlen) < 0)
+ return -EFAULT;
+
+ c = i2o_find_iop(kxfer.iop);
+ if (!c)
+ return -ENXIO;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_SW_REMOVE << 24 | HOST_TID << 12 | ADAPTER_TID);
+ msg->u.head[2] = cpu_to_le32(i2o_config_driver.context);
+ msg->u.head[3] = cpu_to_le32(0);
+ msg->body[0] =
+ cpu_to_le32((u32) kxfer.flags << 24 | (u32) kxfer.sw_type << 16);
+ msg->body[1] = cpu_to_le32(swlen);
+ msg->body[2] = cpu_to_le32(kxfer.sw_id);
+
+ token = i2o_msg_post_wait(c, msg, 10);
+
+ if (token != I2O_POST_WAIT_OK) {
+ osm_info("swdel failed, DetailedStatus = %d\n", token);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+};
+
+static int i2o_cfg_validate(unsigned long arg)
+{
+ int token;
+ int iop = (int)arg;
+ struct i2o_message *msg;
+ struct i2o_controller *c;
+
+ c = i2o_find_iop(iop);
+ if (!c)
+ return -ENXIO;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_CONFIG_VALIDATE << 24 | HOST_TID << 12 | iop);
+ msg->u.head[2] = cpu_to_le32(i2o_config_driver.context);
+ msg->u.head[3] = cpu_to_le32(0);
+
+ token = i2o_msg_post_wait(c, msg, 10);
+
+ if (token != I2O_POST_WAIT_OK) {
+ osm_info("Can't validate configuration, ErrorStatus = %d\n",
+ token);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+};
+
+static int i2o_cfg_evt_reg(unsigned long arg, struct file *fp)
+{
+ struct i2o_message *msg;
+ struct i2o_evt_id __user *pdesc = (struct i2o_evt_id __user *)arg;
+ struct i2o_evt_id kdesc;
+ struct i2o_controller *c;
+ struct i2o_device *d;
+
+ if (copy_from_user(&kdesc, pdesc, sizeof(struct i2o_evt_id)))
+ return -EFAULT;
+
+ /* IOP exists? */
+ c = i2o_find_iop(kdesc.iop);
+ if (!c)
+ return -ENXIO;
+
+ /* Device exists? */
+ d = i2o_iop_find_device(c, kdesc.tid);
+ if (!d)
+ return -ENODEV;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_UTIL_EVT_REGISTER << 24 | HOST_TID << 12 |
+ kdesc.tid);
+ msg->u.head[2] = cpu_to_le32(i2o_config_driver.context);
+ msg->u.head[3] = cpu_to_le32(i2o_cntxt_list_add(c, fp->private_data));
+ msg->body[0] = cpu_to_le32(kdesc.evt_mask);
+
+ i2o_msg_post(c, msg);
+
+ return 0;
+}
+
+static int i2o_cfg_evt_get(unsigned long arg, struct file *fp)
+{
+ struct i2o_cfg_info *p = NULL;
+ struct i2o_evt_get __user *uget = (struct i2o_evt_get __user *)arg;
+ struct i2o_evt_get kget;
+ unsigned long flags;
+
+ for (p = open_files; p; p = p->next)
+ if (p->q_id == (ulong) fp->private_data)
+ break;
+
+ if (!p->q_len)
+ return -ENOENT;
+
+ memcpy(&kget.info, &p->event_q[p->q_out], sizeof(struct i2o_evt_info));
+ MODINC(p->q_out, I2O_EVT_Q_LEN);
+ spin_lock_irqsave(&i2o_config_lock, flags);
+ p->q_len--;
+ kget.pending = p->q_len;
+ kget.lost = p->q_lost;
+ spin_unlock_irqrestore(&i2o_config_lock, flags);
+
+ if (copy_to_user(uget, &kget, sizeof(struct i2o_evt_get)))
+ return -EFAULT;
+ return 0;
+}
+
+#ifdef CONFIG_COMPAT
+static int i2o_cfg_passthru32(struct file *file, unsigned cmnd,
+ unsigned long arg)
+{
+ struct i2o_cmd_passthru32 __user *cmd;
+ struct i2o_controller *c;
+ u32 __user *user_msg;
+ u32 *reply = NULL;
+ u32 __user *user_reply = NULL;
+ u32 size = 0;
+ u32 reply_size = 0;
+ u32 rcode = 0;
+ struct i2o_dma sg_list[SG_TABLESIZE];
+ u32 sg_offset = 0;
+ u32 sg_count = 0;
+ u32 i = 0;
+ u32 sg_index = 0;
+ i2o_status_block *sb;
+ struct i2o_message *msg;
+ unsigned int iop;
+
+ cmd = (struct i2o_cmd_passthru32 __user *)arg;
+
+ if (get_user(iop, &cmd->iop) || get_user(i, &cmd->msg))
+ return -EFAULT;
+
+ user_msg = compat_ptr(i);
+
+ c = i2o_find_iop(iop);
+ if (!c) {
+ osm_debug("controller %d not found\n", iop);
+ return -ENXIO;
+ }
+
+ sb = c->status_block.virt;
+
+ if (get_user(size, &user_msg[0])) {
+ osm_warn("unable to get size!\n");
+ return -EFAULT;
+ }
+ size = size >> 16;
+
+ if (size > sb->inbound_frame_size) {
+ osm_warn("size of message > inbound_frame_size");
+ return -EFAULT;
+ }
+
+ user_reply = &user_msg[size];
+
+ size <<= 2; // Convert to bytes
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ rcode = -EFAULT;
+ /* Copy in the user's I2O command */
+ if (copy_from_user(msg, user_msg, size)) {
+ osm_warn("unable to copy user message\n");
+ goto out;
+ }
+ i2o_dump_message(msg);
+
+ if (get_user(reply_size, &user_reply[0]) < 0)
+ goto out;
+
+ reply_size >>= 16;
+ reply_size <<= 2;
+
+ rcode = -ENOMEM;
+ reply = kzalloc(reply_size, GFP_KERNEL);
+ if (!reply) {
+ printk(KERN_WARNING "%s: Could not allocate reply buffer\n",
+ c->name);
+ goto out;
+ }
+
+ sg_offset = (msg->u.head[0] >> 4) & 0x0f;
+
+ memset(sg_list, 0, sizeof(sg_list[0]) * SG_TABLESIZE);
+ if (sg_offset) {
+ struct sg_simple_element *sg;
+
+ if (sg_offset * 4 >= size) {
+ rcode = -EFAULT;
+ goto cleanup;
+ }
+ // TODO 64bit fix
+ sg = (struct sg_simple_element *)((&msg->u.head[0]) +
+ sg_offset);
+ sg_count =
+ (size - sg_offset * 4) / sizeof(struct sg_simple_element);
+ if (sg_count > SG_TABLESIZE) {
+ printk(KERN_DEBUG "%s:IOCTL SG List too large (%u)\n",
+ c->name, sg_count);
+ rcode = -EINVAL;
+ goto cleanup;
+ }
+
+ for (i = 0; i < sg_count; i++) {
+ int sg_size;
+ struct i2o_dma *p;
+
+ if (!(sg[i].flag_count & 0x10000000
+ /*I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT */ )) {
+ printk(KERN_DEBUG
+ "%s:Bad SG element %d - not simple (%x)\n",
+ c->name, i, sg[i].flag_count);
+ rcode = -EINVAL;
+ goto cleanup;
+ }
+ sg_size = sg[i].flag_count & 0xffffff;
+ p = &(sg_list[sg_index]);
+ /* Allocate memory for the transfer */
+ if (i2o_dma_alloc(&c->pdev->dev, p, sg_size)) {
+ printk(KERN_DEBUG
+ "%s: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
+ c->name, sg_size, i, sg_count);
+ rcode = -ENOMEM;
+ goto sg_list_cleanup;
+ }
+ sg_index++;
+ /* Copy in the user's SG buffer if necessary */
+ if (sg[i].
+ flag_count & 0x04000000 /*I2O_SGL_FLAGS_DIR */ ) {
+ // TODO 64bit fix
+ if (copy_from_user
+ (p->virt,
+ (void __user *)(unsigned long)sg[i].
+ addr_bus, sg_size)) {
+ printk(KERN_DEBUG
+ "%s: Could not copy SG buf %d FROM user\n",
+ c->name, i);
+ rcode = -EFAULT;
+ goto sg_list_cleanup;
+ }
+ }
+ //TODO 64bit fix
+ sg[i].addr_bus = (u32) p->phys;
+ }
+ }
+
+ rcode = i2o_msg_post_wait(c, msg, 60);
+ msg = NULL;
+ if (rcode) {
+ reply[4] = ((u32) rcode) << 24;
+ goto sg_list_cleanup;
+ }
+
+ if (sg_offset) {
+ u32 rmsg[I2O_OUTBOUND_MSG_FRAME_SIZE];
+ /* Copy back the Scatter Gather buffers back to user space */
+ u32 j;
+ // TODO 64bit fix
+ struct sg_simple_element *sg;
+ int sg_size;
+
+ // re-acquire the original message to handle correctly the sg copy operation
+ memset(&rmsg, 0, I2O_OUTBOUND_MSG_FRAME_SIZE * 4);
+ // get user msg size in u32s
+ if (get_user(size, &user_msg[0])) {
+ rcode = -EFAULT;
+ goto sg_list_cleanup;
+ }
+ size = size >> 16;
+ size *= 4;
+ if (size > sizeof(rmsg)) {
+ rcode = -EINVAL;
+ goto sg_list_cleanup;
+ }
+
+ /* Copy in the user's I2O command */
+ if (copy_from_user(rmsg, user_msg, size)) {
+ rcode = -EFAULT;
+ goto sg_list_cleanup;
+ }
+ sg_count =
+ (size - sg_offset * 4) / sizeof(struct sg_simple_element);
+
+ // TODO 64bit fix
+ sg = (struct sg_simple_element *)(rmsg + sg_offset);
+ for (j = 0; j < sg_count; j++) {
+ /* Copy out the SG list to user's buffer if necessary */
+ if (!
+ (sg[j].
+ flag_count & 0x4000000 /*I2O_SGL_FLAGS_DIR */ )) {
+ sg_size = sg[j].flag_count & 0xffffff;
+ // TODO 64bit fix
+ if (copy_to_user
+ ((void __user *)(u64) sg[j].addr_bus,
+ sg_list[j].virt, sg_size)) {
+ printk(KERN_WARNING
+ "%s: Could not copy %p TO user %x\n",
+ c->name, sg_list[j].virt,
+ sg[j].addr_bus);
+ rcode = -EFAULT;
+ goto sg_list_cleanup;
+ }
+ }
+ }
+ }
+
+sg_list_cleanup:
+ /* Copy back the reply to user space */
+ if (reply_size) {
+ // we wrote our own values for context - now restore the user supplied ones
+ if (copy_from_user(reply + 2, user_msg + 2, sizeof(u32) * 2)) {
+ printk(KERN_WARNING
+ "%s: Could not copy message context FROM user\n",
+ c->name);
+ rcode = -EFAULT;
+ }
+ if (copy_to_user(user_reply, reply, reply_size)) {
+ printk(KERN_WARNING
+ "%s: Could not copy reply TO user\n", c->name);
+ rcode = -EFAULT;
+ }
+ }
+ for (i = 0; i < sg_index; i++)
+ i2o_dma_free(&c->pdev->dev, &sg_list[i]);
+
+cleanup:
+ kfree(reply);
+out:
+ if (msg)
+ i2o_msg_nop(c, msg);
+ return rcode;
+}
+
+static long i2o_cfg_compat_ioctl(struct file *file, unsigned cmd,
+ unsigned long arg)
+{
+ int ret;
+ switch (cmd) {
+ case I2OGETIOPS:
+ ret = i2o_cfg_ioctl(file, cmd, arg);
+ break;
+ case I2OPASSTHRU32:
+ mutex_lock(&i2o_cfg_mutex);
+ ret = i2o_cfg_passthru32(file, cmd, arg);
+ mutex_unlock(&i2o_cfg_mutex);
+ break;
+ default:
+ ret = -ENOIOCTLCMD;
+ break;
+ }
+ return ret;
+}
+
+#endif
+
+#ifdef CONFIG_I2O_EXT_ADAPTEC
+static int i2o_cfg_passthru(unsigned long arg)
+{
+ struct i2o_cmd_passthru __user *cmd =
+ (struct i2o_cmd_passthru __user *)arg;
+ struct i2o_controller *c;
+ u32 __user *user_msg;
+ u32 *reply = NULL;
+ u32 __user *user_reply = NULL;
+ u32 size = 0;
+ u32 reply_size = 0;
+ u32 rcode = 0;
+ struct i2o_dma sg_list[SG_TABLESIZE];
+ u32 sg_offset = 0;
+ u32 sg_count = 0;
+ int sg_index = 0;
+ u32 i = 0;
+ i2o_status_block *sb;
+ struct i2o_message *msg;
+ unsigned int iop;
+
+ if (get_user(iop, &cmd->iop) || get_user(user_msg, &cmd->msg))
+ return -EFAULT;
+
+ c = i2o_find_iop(iop);
+ if (!c) {
+ osm_warn("controller %d not found\n", iop);
+ return -ENXIO;
+ }
+
+ sb = c->status_block.virt;
+
+ if (get_user(size, &user_msg[0]))
+ return -EFAULT;
+ size = size >> 16;
+
+ if (size > sb->inbound_frame_size) {
+ osm_warn("size of message > inbound_frame_size");
+ return -EFAULT;
+ }
+
+ user_reply = &user_msg[size];
+
+ size <<= 2; // Convert to bytes
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ rcode = -EFAULT;
+ /* Copy in the user's I2O command */
+ if (copy_from_user(msg, user_msg, size))
+ goto out;
+
+ if (get_user(reply_size, &user_reply[0]) < 0)
+ goto out;
+
+ reply_size >>= 16;
+ reply_size <<= 2;
+
+ reply = kzalloc(reply_size, GFP_KERNEL);
+ if (!reply) {
+ printk(KERN_WARNING "%s: Could not allocate reply buffer\n",
+ c->name);
+ rcode = -ENOMEM;
+ goto out;
+ }
+
+ sg_offset = (msg->u.head[0] >> 4) & 0x0f;
+
+ memset(sg_list, 0, sizeof(sg_list[0]) * SG_TABLESIZE);
+ if (sg_offset) {
+ struct sg_simple_element *sg;
+ struct i2o_dma *p;
+
+ if (sg_offset * 4 >= size) {
+ rcode = -EFAULT;
+ goto cleanup;
+ }
+ // TODO 64bit fix
+ sg = (struct sg_simple_element *)((&msg->u.head[0]) +
+ sg_offset);
+ sg_count =
+ (size - sg_offset * 4) / sizeof(struct sg_simple_element);
+ if (sg_count > SG_TABLESIZE) {
+ printk(KERN_DEBUG "%s:IOCTL SG List too large (%u)\n",
+ c->name, sg_count);
+ rcode = -EINVAL;
+ goto cleanup;
+ }
+
+ for (i = 0; i < sg_count; i++) {
+ int sg_size;
+
+ if (!(sg[i].flag_count & 0x10000000
+ /*I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT */ )) {
+ printk(KERN_DEBUG
+ "%s:Bad SG element %d - not simple (%x)\n",
+ c->name, i, sg[i].flag_count);
+ rcode = -EINVAL;
+ goto sg_list_cleanup;
+ }
+ sg_size = sg[i].flag_count & 0xffffff;
+ p = &(sg_list[sg_index]);
+ if (i2o_dma_alloc(&c->pdev->dev, p, sg_size)) {
+ /* Allocate memory for the transfer */
+ printk(KERN_DEBUG
+ "%s: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
+ c->name, sg_size, i, sg_count);
+ rcode = -ENOMEM;
+ goto sg_list_cleanup;
+ }
+ sg_index++;
+ /* Copy in the user's SG buffer if necessary */
+ if (sg[i].
+ flag_count & 0x04000000 /*I2O_SGL_FLAGS_DIR */ ) {
+ // TODO 64bit fix
+ if (copy_from_user
+ (p->virt, (void __user *)sg[i].addr_bus,
+ sg_size)) {
+ printk(KERN_DEBUG
+ "%s: Could not copy SG buf %d FROM user\n",
+ c->name, i);
+ rcode = -EFAULT;
+ goto sg_list_cleanup;
+ }
+ }
+ sg[i].addr_bus = p->phys;
+ }
+ }
+
+ rcode = i2o_msg_post_wait(c, msg, 60);
+ msg = NULL;
+ if (rcode) {
+ reply[4] = ((u32) rcode) << 24;
+ goto sg_list_cleanup;
+ }
+
+ if (sg_offset) {
+ u32 rmsg[I2O_OUTBOUND_MSG_FRAME_SIZE];
+ /* Copy back the Scatter Gather buffers back to user space */
+ u32 j;
+ // TODO 64bit fix
+ struct sg_simple_element *sg;
+ int sg_size;
+
+ // re-acquire the original message to handle correctly the sg copy operation
+ memset(&rmsg, 0, I2O_OUTBOUND_MSG_FRAME_SIZE * 4);
+ // get user msg size in u32s
+ if (get_user(size, &user_msg[0])) {
+ rcode = -EFAULT;
+ goto sg_list_cleanup;
+ }
+ size = size >> 16;
+ size *= 4;
+ if (size > sizeof(rmsg)) {
+ rcode = -EFAULT;
+ goto sg_list_cleanup;
+ }
+
+ /* Copy in the user's I2O command */
+ if (copy_from_user(rmsg, user_msg, size)) {
+ rcode = -EFAULT;
+ goto sg_list_cleanup;
+ }
+ sg_count =
+ (size - sg_offset * 4) / sizeof(struct sg_simple_element);
+
+ // TODO 64bit fix
+ sg = (struct sg_simple_element *)(rmsg + sg_offset);
+ for (j = 0; j < sg_count; j++) {
+ /* Copy out the SG list to user's buffer if necessary */
+ if (!
+ (sg[j].
+ flag_count & 0x4000000 /*I2O_SGL_FLAGS_DIR */ )) {
+ sg_size = sg[j].flag_count & 0xffffff;
+ // TODO 64bit fix
+ if (copy_to_user
+ ((void __user *)sg[j].addr_bus, sg_list[j].virt,
+ sg_size)) {
+ printk(KERN_WARNING
+ "%s: Could not copy %p TO user %x\n",
+ c->name, sg_list[j].virt,
+ sg[j].addr_bus);
+ rcode = -EFAULT;
+ goto sg_list_cleanup;
+ }
+ }
+ }
+ }
+
+sg_list_cleanup:
+ /* Copy back the reply to user space */
+ if (reply_size) {
+ // we wrote our own values for context - now restore the user supplied ones
+ if (copy_from_user(reply + 2, user_msg + 2, sizeof(u32) * 2)) {
+ printk(KERN_WARNING
+ "%s: Could not copy message context FROM user\n",
+ c->name);
+ rcode = -EFAULT;
+ }
+ if (copy_to_user(user_reply, reply, reply_size)) {
+ printk(KERN_WARNING
+ "%s: Could not copy reply TO user\n", c->name);
+ rcode = -EFAULT;
+ }
+ }
+
+ for (i = 0; i < sg_index; i++)
+ i2o_dma_free(&c->pdev->dev, &sg_list[i]);
+
+cleanup:
+ kfree(reply);
+out:
+ if (msg)
+ i2o_msg_nop(c, msg);
+ return rcode;
+}
+#endif
+
+/*
+ * IOCTL Handler
+ */
+static long i2o_cfg_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+{
+ int ret;
+
+ mutex_lock(&i2o_cfg_mutex);
+ switch (cmd) {
+ case I2OGETIOPS:
+ ret = i2o_cfg_getiops(arg);
+ break;
+
+ case I2OHRTGET:
+ ret = i2o_cfg_gethrt(arg);
+ break;
+
+ case I2OLCTGET:
+ ret = i2o_cfg_getlct(arg);
+ break;
+
+ case I2OPARMSET:
+ ret = i2o_cfg_parms(arg, I2OPARMSET);
+ break;
+
+ case I2OPARMGET:
+ ret = i2o_cfg_parms(arg, I2OPARMGET);
+ break;
+
+ case I2OSWDL:
+ ret = i2o_cfg_swdl(arg);
+ break;
+
+ case I2OSWUL:
+ ret = i2o_cfg_swul(arg);
+ break;
+
+ case I2OSWDEL:
+ ret = i2o_cfg_swdel(arg);
+ break;
+
+ case I2OVALIDATE:
+ ret = i2o_cfg_validate(arg);
+ break;
+
+ case I2OEVTREG:
+ ret = i2o_cfg_evt_reg(arg, fp);
+ break;
+
+ case I2OEVTGET:
+ ret = i2o_cfg_evt_get(arg, fp);
+ break;
+
+#ifdef CONFIG_I2O_EXT_ADAPTEC
+ case I2OPASSTHRU:
+ ret = i2o_cfg_passthru(arg);
+ break;
+#endif
+
+ default:
+ osm_debug("unknown ioctl called!\n");
+ ret = -EINVAL;
+ }
+ mutex_unlock(&i2o_cfg_mutex);
+ return ret;
+}
+
+static int cfg_open(struct inode *inode, struct file *file)
+{
+ struct i2o_cfg_info *tmp = kmalloc(sizeof(struct i2o_cfg_info),
+ GFP_KERNEL);
+ unsigned long flags;
+
+ if (!tmp)
+ return -ENOMEM;
+
+ mutex_lock(&i2o_cfg_mutex);
+ file->private_data = (void *)(i2o_cfg_info_id++);
+ tmp->fp = file;
+ tmp->fasync = NULL;
+ tmp->q_id = (ulong) file->private_data;
+ tmp->q_len = 0;
+ tmp->q_in = 0;
+ tmp->q_out = 0;
+ tmp->q_lost = 0;
+ tmp->next = open_files;
+
+ spin_lock_irqsave(&i2o_config_lock, flags);
+ open_files = tmp;
+ spin_unlock_irqrestore(&i2o_config_lock, flags);
+ mutex_unlock(&i2o_cfg_mutex);
+
+ return 0;
+}
+
+static int cfg_fasync(int fd, struct file *fp, int on)
+{
+ ulong id = (ulong) fp->private_data;
+ struct i2o_cfg_info *p;
+ int ret = -EBADF;
+
+ mutex_lock(&i2o_cfg_mutex);
+ for (p = open_files; p; p = p->next)
+ if (p->q_id == id)
+ break;
+
+ if (p)
+ ret = fasync_helper(fd, fp, on, &p->fasync);
+ mutex_unlock(&i2o_cfg_mutex);
+ return ret;
+}
+
+static int cfg_release(struct inode *inode, struct file *file)
+{
+ ulong id = (ulong) file->private_data;
+ struct i2o_cfg_info *p, **q;
+ unsigned long flags;
+
+ mutex_lock(&i2o_cfg_mutex);
+ spin_lock_irqsave(&i2o_config_lock, flags);
+ for (q = &open_files; (p = *q) != NULL; q = &p->next) {
+ if (p->q_id == id) {
+ *q = p->next;
+ kfree(p);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&i2o_config_lock, flags);
+ mutex_unlock(&i2o_cfg_mutex);
+
+ return 0;
+}
+
+static const struct file_operations config_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .unlocked_ioctl = i2o_cfg_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = i2o_cfg_compat_ioctl,
+#endif
+ .open = cfg_open,
+ .release = cfg_release,
+ .fasync = cfg_fasync,
+};
+
+static struct miscdevice i2o_miscdev = {
+ I2O_MINOR,
+ "i2octl",
+ &config_fops
+};
+
+static int __init i2o_config_old_init(void)
+{
+ spin_lock_init(&i2o_config_lock);
+
+ if (misc_register(&i2o_miscdev) < 0) {
+ osm_err("can't register device.\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static void i2o_config_old_exit(void)
+{
+ misc_deregister(&i2o_miscdev);
+}
+
+MODULE_AUTHOR("Red Hat Software");
diff --git a/drivers/staging/i2o/i2o_proc.c b/drivers/staging/i2o/i2o_proc.c
new file mode 100644
index 0000000..ad84f33
--- /dev/null
+++ b/drivers/staging/i2o/i2o_proc.c
@@ -0,0 +1,2045 @@
+/*
+ * procfs handler for Linux I2O subsystem
+ *
+ * (c) Copyright 1999 Deepak Saxena
+ *
+ * Originally written by Deepak Saxena(deepak@plexity.net)
+ *
+ * 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 is an initial test release. The code is based on the design of the
+ * ide procfs system (drivers/block/ide-proc.c). Some code taken from
+ * i2o-core module by Alan Cox.
+ *
+ * DISCLAIMER: This code is still under development/test and may cause
+ * your system to behave unpredictably. Use at your own discretion.
+ *
+ *
+ * Fixes/additions:
+ * Juha Sievänen (Juha.Sievanen@cs.Helsinki.FI),
+ * Auvo Häkkinen (Auvo.Hakkinen@cs.Helsinki.FI)
+ * University of Helsinki, Department of Computer Science
+ * LAN entries
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>
+ * Changes for new I2O API
+ */
+
+#define OSM_NAME "proc-osm"
+#define OSM_VERSION "1.316"
+#define OSM_DESCRIPTION "I2O ProcFS OSM"
+
+#define I2O_MAX_MODULES 4
+// FIXME!
+#define FMT_U64_HEX "0x%08x%08x"
+#define U64_VAL(pu64) *((u32*)(pu64)+1), *((u32*)(pu64))
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include "i2o.h"
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+
+/* Structure used to define /proc entries */
+typedef struct _i2o_proc_entry_t {
+ char *name; /* entry name */
+ umode_t mode; /* mode */
+ const struct file_operations *fops; /* open function */
+} i2o_proc_entry;
+
+/* global I2O /proc/i2o entry */
+static struct proc_dir_entry *i2o_proc_dir_root;
+
+/* proc OSM driver struct */
+static struct i2o_driver i2o_proc_driver = {
+ .name = OSM_NAME,
+};
+
+static int print_serial_number(struct seq_file *seq, u8 * serialno, int max_len)
+{
+ int i;
+
+ /* 19990419 -sralston
+ * The I2O v1.5 (and v2.0 so far) "official specification"
+ * got serial numbers WRONG!
+ * Apparently, and despite what Section 3.4.4 says and
+ * Figure 3-35 shows (pg 3-39 in the pdf doc),
+ * the convention / consensus seems to be:
+ * + First byte is SNFormat
+ * + Second byte is SNLen (but only if SNFormat==7 (?))
+ * + (v2.0) SCSI+BS may use IEEE Registered (64 or 128 bit) format
+ */
+ switch (serialno[0]) {
+ case I2O_SNFORMAT_BINARY: /* Binary */
+ seq_printf(seq, "0x");
+ for (i = 0; i < serialno[1]; i++) {
+ seq_printf(seq, "%02X", serialno[2 + i]);
+ }
+ break;
+
+ case I2O_SNFORMAT_ASCII: /* ASCII */
+ if (serialno[1] < ' ') { /* printable or SNLen? */
+ /* sanity */
+ max_len =
+ (max_len < serialno[1]) ? max_len : serialno[1];
+ serialno[1 + max_len] = '\0';
+
+ /* just print it */
+ seq_printf(seq, "%s", &serialno[2]);
+ } else {
+ /* print chars for specified length */
+ for (i = 0; i < serialno[1]; i++) {
+ seq_printf(seq, "%c", serialno[2 + i]);
+ }
+ }
+ break;
+
+ case I2O_SNFORMAT_UNICODE: /* UNICODE */
+ seq_printf(seq, "UNICODE Format. Can't Display\n");
+ break;
+
+ case I2O_SNFORMAT_LAN48_MAC: /* LAN-48 MAC Address */
+ seq_printf(seq, "LAN-48 MAC address @ %pM", &serialno[2]);
+ break;
+
+ case I2O_SNFORMAT_WAN: /* WAN MAC Address */
+ /* FIXME: Figure out what a WAN access address looks like?? */
+ seq_printf(seq, "WAN Access Address");
+ break;
+
+/* plus new in v2.0 */
+ case I2O_SNFORMAT_LAN64_MAC: /* LAN-64 MAC Address */
+ /* FIXME: Figure out what a LAN-64 address really looks like?? */
+ seq_printf(seq,
+ "LAN-64 MAC address @ [?:%02X:%02X:?] %pM",
+ serialno[8], serialno[9], &serialno[2]);
+ break;
+
+ case I2O_SNFORMAT_DDM: /* I2O DDM */
+ seq_printf(seq,
+ "DDM: Tid=%03Xh, Rsvd=%04Xh, OrgId=%04Xh",
+ *(u16 *) & serialno[2],
+ *(u16 *) & serialno[4], *(u16 *) & serialno[6]);
+ break;
+
+ case I2O_SNFORMAT_IEEE_REG64: /* IEEE Registered (64-bit) */
+ case I2O_SNFORMAT_IEEE_REG128: /* IEEE Registered (128-bit) */
+ /* FIXME: Figure if this is even close?? */
+ seq_printf(seq,
+ "IEEE NodeName(hi,lo)=(%08Xh:%08Xh), PortName(hi,lo)=(%08Xh:%08Xh)\n",
+ *(u32 *) & serialno[2],
+ *(u32 *) & serialno[6],
+ *(u32 *) & serialno[10], *(u32 *) & serialno[14]);
+ break;
+
+ case I2O_SNFORMAT_UNKNOWN: /* Unknown 0 */
+ case I2O_SNFORMAT_UNKNOWN2: /* Unknown 0xff */
+ default:
+ seq_printf(seq, "Unknown data format (0x%02x)", serialno[0]);
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * i2o_get_class_name - do i2o class name lookup
+ * @class: class number
+ *
+ * Return a descriptive string for an i2o class.
+ */
+static const char *i2o_get_class_name(int class)
+{
+ int idx = 16;
+ static char *i2o_class_name[] = {
+ "Executive",
+ "Device Driver Module",
+ "Block Device",
+ "Tape Device",
+ "LAN Interface",
+ "WAN Interface",
+ "Fibre Channel Port",
+ "Fibre Channel Device",
+ "SCSI Device",
+ "ATE Port",
+ "ATE Device",
+ "Floppy Controller",
+ "Floppy Device",
+ "Secondary Bus Port",
+ "Peer Transport Agent",
+ "Peer Transport",
+ "Unknown"
+ };
+
+ switch (class & 0xfff) {
+ case I2O_CLASS_EXECUTIVE:
+ idx = 0;
+ break;
+ case I2O_CLASS_DDM:
+ idx = 1;
+ break;
+ case I2O_CLASS_RANDOM_BLOCK_STORAGE:
+ idx = 2;
+ break;
+ case I2O_CLASS_SEQUENTIAL_STORAGE:
+ idx = 3;
+ break;
+ case I2O_CLASS_LAN:
+ idx = 4;
+ break;
+ case I2O_CLASS_WAN:
+ idx = 5;
+ break;
+ case I2O_CLASS_FIBRE_CHANNEL_PORT:
+ idx = 6;
+ break;
+ case I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL:
+ idx = 7;
+ break;
+ case I2O_CLASS_SCSI_PERIPHERAL:
+ idx = 8;
+ break;
+ case I2O_CLASS_ATE_PORT:
+ idx = 9;
+ break;
+ case I2O_CLASS_ATE_PERIPHERAL:
+ idx = 10;
+ break;
+ case I2O_CLASS_FLOPPY_CONTROLLER:
+ idx = 11;
+ break;
+ case I2O_CLASS_FLOPPY_DEVICE:
+ idx = 12;
+ break;
+ case I2O_CLASS_BUS_ADAPTER:
+ idx = 13;
+ break;
+ case I2O_CLASS_PEER_TRANSPORT_AGENT:
+ idx = 14;
+ break;
+ case I2O_CLASS_PEER_TRANSPORT:
+ idx = 15;
+ break;
+ }
+
+ return i2o_class_name[idx];
+}
+
+#define SCSI_TABLE_SIZE 13
+static char *scsi_devices[] = {
+ "Direct-Access Read/Write",
+ "Sequential-Access Storage",
+ "Printer",
+ "Processor",
+ "WORM Device",
+ "CD-ROM Device",
+ "Scanner Device",
+ "Optical Memory Device",
+ "Medium Changer Device",
+ "Communications Device",
+ "Graphics Art Pre-Press Device",
+ "Graphics Art Pre-Press Device",
+ "Array Controller Device"
+};
+
+static char *chtostr(char *tmp, u8 *chars, int n)
+{
+ tmp[0] = 0;
+ return strncat(tmp, (char *)chars, n);
+}
+
+static int i2o_report_query_status(struct seq_file *seq, int block_status,
+ char *group)
+{
+ switch (block_status) {
+ case -ETIMEDOUT:
+ return seq_printf(seq, "Timeout reading group %s.\n", group);
+ case -ENOMEM:
+ return seq_printf(seq, "No free memory to read the table.\n");
+ case -I2O_PARAMS_STATUS_INVALID_GROUP_ID:
+ return seq_printf(seq, "Group %s not supported.\n", group);
+ default:
+ return seq_printf(seq,
+ "Error reading group %s. BlockStatus 0x%02X\n",
+ group, -block_status);
+ }
+}
+
+static char *bus_strings[] = {
+ "Local Bus",
+ "ISA",
+ "EISA",
+ "PCI",
+ "PCMCIA",
+ "NUBUS",
+ "CARDBUS"
+};
+
+static int i2o_seq_show_hrt(struct seq_file *seq, void *v)
+{
+ struct i2o_controller *c = (struct i2o_controller *)seq->private;
+ i2o_hrt *hrt = (i2o_hrt *) c->hrt.virt;
+ u32 bus;
+ int i;
+
+ if (hrt->hrt_version) {
+ seq_printf(seq,
+ "HRT table for controller is too new a version.\n");
+ return 0;
+ }
+
+ seq_printf(seq, "HRT has %d entries of %d bytes each.\n",
+ hrt->num_entries, hrt->entry_len << 2);
+
+ for (i = 0; i < hrt->num_entries; i++) {
+ seq_printf(seq, "Entry %d:\n", i);
+ seq_printf(seq, " Adapter ID: %0#10x\n",
+ hrt->hrt_entry[i].adapter_id);
+ seq_printf(seq, " Controlling tid: %0#6x\n",
+ hrt->hrt_entry[i].parent_tid);
+
+ if (hrt->hrt_entry[i].bus_type != 0x80) {
+ bus = hrt->hrt_entry[i].bus_type;
+ seq_printf(seq, " %s Information\n",
+ bus_strings[bus]);
+
+ switch (bus) {
+ case I2O_BUS_LOCAL:
+ seq_printf(seq, " IOBase: %0#6x,",
+ hrt->hrt_entry[i].bus.local_bus.
+ LbBaseIOPort);
+ seq_printf(seq, " MemoryBase: %0#10x\n",
+ hrt->hrt_entry[i].bus.local_bus.
+ LbBaseMemoryAddress);
+ break;
+
+ case I2O_BUS_ISA:
+ seq_printf(seq, " IOBase: %0#6x,",
+ hrt->hrt_entry[i].bus.isa_bus.
+ IsaBaseIOPort);
+ seq_printf(seq, " MemoryBase: %0#10x,",
+ hrt->hrt_entry[i].bus.isa_bus.
+ IsaBaseMemoryAddress);
+ seq_printf(seq, " CSN: %0#4x,",
+ hrt->hrt_entry[i].bus.isa_bus.CSN);
+ break;
+
+ case I2O_BUS_EISA:
+ seq_printf(seq, " IOBase: %0#6x,",
+ hrt->hrt_entry[i].bus.eisa_bus.
+ EisaBaseIOPort);
+ seq_printf(seq, " MemoryBase: %0#10x,",
+ hrt->hrt_entry[i].bus.eisa_bus.
+ EisaBaseMemoryAddress);
+ seq_printf(seq, " Slot: %0#4x,",
+ hrt->hrt_entry[i].bus.eisa_bus.
+ EisaSlotNumber);
+ break;
+
+ case I2O_BUS_PCI:
+ seq_printf(seq, " Bus: %0#4x",
+ hrt->hrt_entry[i].bus.pci_bus.
+ PciBusNumber);
+ seq_printf(seq, " Dev: %0#4x",
+ hrt->hrt_entry[i].bus.pci_bus.
+ PciDeviceNumber);
+ seq_printf(seq, " Func: %0#4x",
+ hrt->hrt_entry[i].bus.pci_bus.
+ PciFunctionNumber);
+ seq_printf(seq, " Vendor: %0#6x",
+ hrt->hrt_entry[i].bus.pci_bus.
+ PciVendorID);
+ seq_printf(seq, " Device: %0#6x\n",
+ hrt->hrt_entry[i].bus.pci_bus.
+ PciDeviceID);
+ break;
+
+ default:
+ seq_printf(seq, " Unsupported Bus Type\n");
+ }
+ } else
+ seq_printf(seq, " Unknown Bus Type\n");
+ }
+
+ return 0;
+}
+
+static int i2o_seq_show_lct(struct seq_file *seq, void *v)
+{
+ struct i2o_controller *c = (struct i2o_controller *)seq->private;
+ i2o_lct *lct = (i2o_lct *) c->lct;
+ int entries;
+ int i;
+
+#define BUS_TABLE_SIZE 3
+ static char *bus_ports[] = {
+ "Generic Bus",
+ "SCSI Bus",
+ "Fibre Channel Bus"
+ };
+
+ entries = (lct->table_size - 3) / 9;
+
+ seq_printf(seq, "LCT contains %d %s\n", entries,
+ entries == 1 ? "entry" : "entries");
+ if (lct->boot_tid)
+ seq_printf(seq, "Boot Device @ ID %d\n", lct->boot_tid);
+
+ seq_printf(seq, "Current Change Indicator: %#10x\n", lct->change_ind);
+
+ for (i = 0; i < entries; i++) {
+ seq_printf(seq, "Entry %d\n", i);
+ seq_printf(seq, " Class, SubClass : %s",
+ i2o_get_class_name(lct->lct_entry[i].class_id));
+
+ /*
+ * Classes which we'll print subclass info for
+ */
+ switch (lct->lct_entry[i].class_id & 0xFFF) {
+ case I2O_CLASS_RANDOM_BLOCK_STORAGE:
+ switch (lct->lct_entry[i].sub_class) {
+ case 0x00:
+ seq_printf(seq, ", Direct-Access Read/Write");
+ break;
+
+ case 0x04:
+ seq_printf(seq, ", WORM Drive");
+ break;
+
+ case 0x05:
+ seq_printf(seq, ", CD-ROM Drive");
+ break;
+
+ case 0x07:
+ seq_printf(seq, ", Optical Memory Device");
+ break;
+
+ default:
+ seq_printf(seq, ", Unknown (0x%02x)",
+ lct->lct_entry[i].sub_class);
+ break;
+ }
+ break;
+
+ case I2O_CLASS_LAN:
+ switch (lct->lct_entry[i].sub_class & 0xFF) {
+ case 0x30:
+ seq_printf(seq, ", Ethernet");
+ break;
+
+ case 0x40:
+ seq_printf(seq, ", 100base VG");
+ break;
+
+ case 0x50:
+ seq_printf(seq, ", IEEE 802.5/Token-Ring");
+ break;
+
+ case 0x60:
+ seq_printf(seq, ", ANSI X3T9.5 FDDI");
+ break;
+
+ case 0x70:
+ seq_printf(seq, ", Fibre Channel");
+ break;
+
+ default:
+ seq_printf(seq, ", Unknown Sub-Class (0x%02x)",
+ lct->lct_entry[i].sub_class & 0xFF);
+ break;
+ }
+ break;
+
+ case I2O_CLASS_SCSI_PERIPHERAL:
+ if (lct->lct_entry[i].sub_class < SCSI_TABLE_SIZE)
+ seq_printf(seq, ", %s",
+ scsi_devices[lct->lct_entry[i].
+ sub_class]);
+ else
+ seq_printf(seq, ", Unknown Device Type");
+ break;
+
+ case I2O_CLASS_BUS_ADAPTER:
+ if (lct->lct_entry[i].sub_class < BUS_TABLE_SIZE)
+ seq_printf(seq, ", %s",
+ bus_ports[lct->lct_entry[i].
+ sub_class]);
+ else
+ seq_printf(seq, ", Unknown Bus Type");
+ break;
+ }
+ seq_printf(seq, "\n");
+
+ seq_printf(seq, " Local TID : 0x%03x\n",
+ lct->lct_entry[i].tid);
+ seq_printf(seq, " User TID : 0x%03x\n",
+ lct->lct_entry[i].user_tid);
+ seq_printf(seq, " Parent TID : 0x%03x\n",
+ lct->lct_entry[i].parent_tid);
+ seq_printf(seq, " Identity Tag : 0x%x%x%x%x%x%x%x%x\n",
+ lct->lct_entry[i].identity_tag[0],
+ lct->lct_entry[i].identity_tag[1],
+ lct->lct_entry[i].identity_tag[2],
+ lct->lct_entry[i].identity_tag[3],
+ lct->lct_entry[i].identity_tag[4],
+ lct->lct_entry[i].identity_tag[5],
+ lct->lct_entry[i].identity_tag[6],
+ lct->lct_entry[i].identity_tag[7]);
+ seq_printf(seq, " Change Indicator : %0#10x\n",
+ lct->lct_entry[i].change_ind);
+ seq_printf(seq, " Event Capab Mask : %0#10x\n",
+ lct->lct_entry[i].device_flags);
+ }
+
+ return 0;
+}
+
+static int i2o_seq_show_status(struct seq_file *seq, void *v)
+{
+ struct i2o_controller *c = (struct i2o_controller *)seq->private;
+ char prodstr[25];
+ int version;
+ i2o_status_block *sb = c->status_block.virt;
+
+ i2o_status_get(c); // reread the status block
+
+ seq_printf(seq, "Organization ID : %0#6x\n", sb->org_id);
+
+ version = sb->i2o_version;
+
+/* FIXME for Spec 2.0
+ if (version == 0x02) {
+ seq_printf(seq, "Lowest I2O version supported: ");
+ switch(workspace[2]) {
+ case 0x00:
+ seq_printf(seq, "1.0\n");
+ break;
+ case 0x01:
+ seq_printf(seq, "1.5\n");
+ break;
+ case 0x02:
+ seq_printf(seq, "2.0\n");
+ break;
+ }
+
+ seq_printf(seq, "Highest I2O version supported: ");
+ switch(workspace[3]) {
+ case 0x00:
+ seq_printf(seq, "1.0\n");
+ break;
+ case 0x01:
+ seq_printf(seq, "1.5\n");
+ break;
+ case 0x02:
+ seq_printf(seq, "2.0\n");
+ break;
+ }
+ }
+*/
+ seq_printf(seq, "IOP ID : %0#5x\n", sb->iop_id);
+ seq_printf(seq, "Host Unit ID : %0#6x\n", sb->host_unit_id);
+ seq_printf(seq, "Segment Number : %0#5x\n", sb->segment_number);
+
+ seq_printf(seq, "I2O version : ");
+ switch (version) {
+ case 0x00:
+ seq_printf(seq, "1.0\n");
+ break;
+ case 0x01:
+ seq_printf(seq, "1.5\n");
+ break;
+ case 0x02:
+ seq_printf(seq, "2.0\n");
+ break;
+ default:
+ seq_printf(seq, "Unknown version\n");
+ }
+
+ seq_printf(seq, "IOP State : ");
+ switch (sb->iop_state) {
+ case 0x01:
+ seq_printf(seq, "INIT\n");
+ break;
+
+ case 0x02:
+ seq_printf(seq, "RESET\n");
+ break;
+
+ case 0x04:
+ seq_printf(seq, "HOLD\n");
+ break;
+
+ case 0x05:
+ seq_printf(seq, "READY\n");
+ break;
+
+ case 0x08:
+ seq_printf(seq, "OPERATIONAL\n");
+ break;
+
+ case 0x10:
+ seq_printf(seq, "FAILED\n");
+ break;
+
+ case 0x11:
+ seq_printf(seq, "FAULTED\n");
+ break;
+
+ default:
+ seq_printf(seq, "Unknown\n");
+ break;
+ }
+
+ seq_printf(seq, "Messenger Type : ");
+ switch (sb->msg_type) {
+ case 0x00:
+ seq_printf(seq, "Memory mapped\n");
+ break;
+ case 0x01:
+ seq_printf(seq, "Memory mapped only\n");
+ break;
+ case 0x02:
+ seq_printf(seq, "Remote only\n");
+ break;
+ case 0x03:
+ seq_printf(seq, "Memory mapped and remote\n");
+ break;
+ default:
+ seq_printf(seq, "Unknown\n");
+ }
+
+ seq_printf(seq, "Inbound Frame Size : %d bytes\n",
+ sb->inbound_frame_size << 2);
+ seq_printf(seq, "Max Inbound Frames : %d\n",
+ sb->max_inbound_frames);
+ seq_printf(seq, "Current Inbound Frames : %d\n",
+ sb->cur_inbound_frames);
+ seq_printf(seq, "Max Outbound Frames : %d\n",
+ sb->max_outbound_frames);
+
+ /* Spec doesn't say if NULL terminated or not... */
+ memcpy(prodstr, sb->product_id, 24);
+ prodstr[24] = '\0';
+ seq_printf(seq, "Product ID : %s\n", prodstr);
+ seq_printf(seq, "Expected LCT Size : %d bytes\n",
+ sb->expected_lct_size);
+
+ seq_printf(seq, "IOP Capabilities\n");
+ seq_printf(seq, " Context Field Size Support : ");
+ switch (sb->iop_capabilities & 0x0000003) {
+ case 0:
+ seq_printf(seq, "Supports only 32-bit context fields\n");
+ break;
+ case 1:
+ seq_printf(seq, "Supports only 64-bit context fields\n");
+ break;
+ case 2:
+ seq_printf(seq, "Supports 32-bit and 64-bit context fields, "
+ "but not concurrently\n");
+ break;
+ case 3:
+ seq_printf(seq, "Supports 32-bit and 64-bit context fields "
+ "concurrently\n");
+ break;
+ default:
+ seq_printf(seq, "0x%08x\n", sb->iop_capabilities);
+ }
+ seq_printf(seq, " Current Context Field Size : ");
+ switch (sb->iop_capabilities & 0x0000000C) {
+ case 0:
+ seq_printf(seq, "not configured\n");
+ break;
+ case 4:
+ seq_printf(seq, "Supports only 32-bit context fields\n");
+ break;
+ case 8:
+ seq_printf(seq, "Supports only 64-bit context fields\n");
+ break;
+ case 12:
+ seq_printf(seq, "Supports both 32-bit or 64-bit context fields "
+ "concurrently\n");
+ break;
+ default:
+ seq_printf(seq, "\n");
+ }
+ seq_printf(seq, " Inbound Peer Support : %s\n",
+ (sb->
+ iop_capabilities & 0x00000010) ? "Supported" :
+ "Not supported");
+ seq_printf(seq, " Outbound Peer Support : %s\n",
+ (sb->
+ iop_capabilities & 0x00000020) ? "Supported" :
+ "Not supported");
+ seq_printf(seq, " Peer to Peer Support : %s\n",
+ (sb->
+ iop_capabilities & 0x00000040) ? "Supported" :
+ "Not supported");
+
+ seq_printf(seq, "Desired private memory size : %d kB\n",
+ sb->desired_mem_size >> 10);
+ seq_printf(seq, "Allocated private memory size : %d kB\n",
+ sb->current_mem_size >> 10);
+ seq_printf(seq, "Private memory base address : %0#10x\n",
+ sb->current_mem_base);
+ seq_printf(seq, "Desired private I/O size : %d kB\n",
+ sb->desired_io_size >> 10);
+ seq_printf(seq, "Allocated private I/O size : %d kB\n",
+ sb->current_io_size >> 10);
+ seq_printf(seq, "Private I/O base address : %0#10x\n",
+ sb->current_io_base);
+
+ return 0;
+}
+
+static int i2o_seq_show_hw(struct seq_file *seq, void *v)
+{
+ struct i2o_controller *c = (struct i2o_controller *)seq->private;
+ static u32 work32[5];
+ static u8 *work8 = (u8 *) work32;
+ static u16 *work16 = (u16 *) work32;
+ int token;
+ u32 hwcap;
+
+ static char *cpu_table[] = {
+ "Intel 80960 series",
+ "AMD2900 series",
+ "Motorola 68000 series",
+ "ARM series",
+ "MIPS series",
+ "Sparc series",
+ "PowerPC series",
+ "Intel x86 series"
+ };
+
+ token =
+ i2o_parm_field_get(c->exec, 0x0000, -1, &work32, sizeof(work32));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token, "0x0000 IOP Hardware");
+ return 0;
+ }
+
+ seq_printf(seq, "I2O Vendor ID : %0#6x\n", work16[0]);
+ seq_printf(seq, "Product ID : %0#6x\n", work16[1]);
+ seq_printf(seq, "CPU : ");
+ if (work8[16] > 8)
+ seq_printf(seq, "Unknown\n");
+ else
+ seq_printf(seq, "%s\n", cpu_table[work8[16]]);
+ /* Anyone using ProcessorVersion? */
+
+ seq_printf(seq, "RAM : %dkB\n", work32[1] >> 10);
+ seq_printf(seq, "Non-Volatile Mem : %dkB\n", work32[2] >> 10);
+
+ hwcap = work32[3];
+ seq_printf(seq, "Capabilities : 0x%08x\n", hwcap);
+ seq_printf(seq, " [%s] Self booting\n",
+ (hwcap & 0x00000001) ? "+" : "-");
+ seq_printf(seq, " [%s] Upgradable IRTOS\n",
+ (hwcap & 0x00000002) ? "+" : "-");
+ seq_printf(seq, " [%s] Supports downloading DDMs\n",
+ (hwcap & 0x00000004) ? "+" : "-");
+ seq_printf(seq, " [%s] Supports installing DDMs\n",
+ (hwcap & 0x00000008) ? "+" : "-");
+ seq_printf(seq, " [%s] Battery-backed RAM\n",
+ (hwcap & 0x00000010) ? "+" : "-");
+
+ return 0;
+}
+
+/* Executive group 0003h - Executing DDM List (table) */
+static int i2o_seq_show_ddm_table(struct seq_file *seq, void *v)
+{
+ struct i2o_controller *c = (struct i2o_controller *)seq->private;
+ int token;
+ int i;
+
+ typedef struct _i2o_exec_execute_ddm_table {
+ u16 ddm_tid;
+ u8 module_type;
+ u8 reserved;
+ u16 i2o_vendor_id;
+ u16 module_id;
+ u8 module_name_version[28];
+ u32 data_size;
+ u32 code_size;
+ } i2o_exec_execute_ddm_table;
+
+ struct {
+ u16 result_count;
+ u16 pad;
+ u16 block_size;
+ u8 block_status;
+ u8 error_info_size;
+ u16 row_count;
+ u16 more_flag;
+ i2o_exec_execute_ddm_table ddm_table[I2O_MAX_MODULES];
+ } *result;
+
+ i2o_exec_execute_ddm_table ddm_table;
+ char tmp[28 + 1];
+
+ result = kmalloc(sizeof(*result), GFP_KERNEL);
+ if (!result)
+ return -ENOMEM;
+
+ token = i2o_parm_table_get(c->exec, I2O_PARAMS_TABLE_GET, 0x0003, -1,
+ NULL, 0, result, sizeof(*result));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token,
+ "0x0003 Executing DDM List");
+ goto out;
+ }
+
+ seq_printf(seq,
+ "Tid Module_type Vendor Mod_id Module_name Vrs Data_size Code_size\n");
+ ddm_table = result->ddm_table[0];
+
+ for (i = 0; i < result->row_count; ddm_table = result->ddm_table[++i]) {
+ seq_printf(seq, "0x%03x ", ddm_table.ddm_tid & 0xFFF);
+
+ switch (ddm_table.module_type) {
+ case 0x01:
+ seq_printf(seq, "Downloaded DDM ");
+ break;
+ case 0x22:
+ seq_printf(seq, "Embedded DDM ");
+ break;
+ default:
+ seq_printf(seq, " ");
+ }
+
+ seq_printf(seq, "%-#7x", ddm_table.i2o_vendor_id);
+ seq_printf(seq, "%-#8x", ddm_table.module_id);
+ seq_printf(seq, "%-29s",
+ chtostr(tmp, ddm_table.module_name_version, 28));
+ seq_printf(seq, "%9d ", ddm_table.data_size);
+ seq_printf(seq, "%8d", ddm_table.code_size);
+
+ seq_printf(seq, "\n");
+ }
+ out:
+ kfree(result);
+ return 0;
+}
+
+/* Executive group 0004h - Driver Store (scalar) */
+static int i2o_seq_show_driver_store(struct seq_file *seq, void *v)
+{
+ struct i2o_controller *c = (struct i2o_controller *)seq->private;
+ u32 work32[8];
+ int token;
+
+ token =
+ i2o_parm_field_get(c->exec, 0x0004, -1, &work32, sizeof(work32));
+ if (token < 0) {
+ i2o_report_query_status(seq, token, "0x0004 Driver Store");
+ return 0;
+ }
+
+ seq_printf(seq, "Module limit : %d\n"
+ "Module count : %d\n"
+ "Current space : %d kB\n"
+ "Free space : %d kB\n",
+ work32[0], work32[1], work32[2] >> 10, work32[3] >> 10);
+
+ return 0;
+}
+
+/* Executive group 0005h - Driver Store Table (table) */
+static int i2o_seq_show_drivers_stored(struct seq_file *seq, void *v)
+{
+ typedef struct _i2o_driver_store {
+ u16 stored_ddm_index;
+ u8 module_type;
+ u8 reserved;
+ u16 i2o_vendor_id;
+ u16 module_id;
+ u8 module_name_version[28];
+ u8 date[8];
+ u32 module_size;
+ u32 mpb_size;
+ u32 module_flags;
+ } i2o_driver_store_table;
+
+ struct i2o_controller *c = (struct i2o_controller *)seq->private;
+ int token;
+ int i;
+
+ typedef struct {
+ u16 result_count;
+ u16 pad;
+ u16 block_size;
+ u8 block_status;
+ u8 error_info_size;
+ u16 row_count;
+ u16 more_flag;
+ i2o_driver_store_table dst[I2O_MAX_MODULES];
+ } i2o_driver_result_table;
+
+ i2o_driver_result_table *result;
+ i2o_driver_store_table *dst;
+ char tmp[28 + 1];
+
+ result = kmalloc(sizeof(i2o_driver_result_table), GFP_KERNEL);
+ if (result == NULL)
+ return -ENOMEM;
+
+ token = i2o_parm_table_get(c->exec, I2O_PARAMS_TABLE_GET, 0x0005, -1,
+ NULL, 0, result, sizeof(*result));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token,
+ "0x0005 DRIVER STORE TABLE");
+ kfree(result);
+ return 0;
+ }
+
+ seq_printf(seq,
+ "# Module_type Vendor Mod_id Module_name Vrs"
+ "Date Mod_size Par_size Flags\n");
+ for (i = 0, dst = &result->dst[0]; i < result->row_count;
+ dst = &result->dst[++i]) {
+ seq_printf(seq, "%-3d", dst->stored_ddm_index);
+ switch (dst->module_type) {
+ case 0x01:
+ seq_printf(seq, "Downloaded DDM ");
+ break;
+ case 0x22:
+ seq_printf(seq, "Embedded DDM ");
+ break;
+ default:
+ seq_printf(seq, " ");
+ }
+
+ seq_printf(seq, "%-#7x", dst->i2o_vendor_id);
+ seq_printf(seq, "%-#8x", dst->module_id);
+ seq_printf(seq, "%-29s",
+ chtostr(tmp, dst->module_name_version, 28));
+ seq_printf(seq, "%-9s", chtostr(tmp, dst->date, 8));
+ seq_printf(seq, "%8d ", dst->module_size);
+ seq_printf(seq, "%8d ", dst->mpb_size);
+ seq_printf(seq, "0x%04x", dst->module_flags);
+ seq_printf(seq, "\n");
+ }
+
+ kfree(result);
+ return 0;
+}
+
+/* Generic group F000h - Params Descriptor (table) */
+static int i2o_seq_show_groups(struct seq_file *seq, void *v)
+{
+ struct i2o_device *d = (struct i2o_device *)seq->private;
+ int token;
+ int i;
+ u8 properties;
+
+ typedef struct _i2o_group_info {
+ u16 group_number;
+ u16 field_count;
+ u16 row_count;
+ u8 properties;
+ u8 reserved;
+ } i2o_group_info;
+
+ struct {
+ u16 result_count;
+ u16 pad;
+ u16 block_size;
+ u8 block_status;
+ u8 error_info_size;
+ u16 row_count;
+ u16 more_flag;
+ i2o_group_info group[256];
+ } *result;
+
+ result = kmalloc(sizeof(*result), GFP_KERNEL);
+ if (!result)
+ return -ENOMEM;
+
+ token = i2o_parm_table_get(d, I2O_PARAMS_TABLE_GET, 0xF000, -1, NULL, 0,
+ result, sizeof(*result));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token, "0xF000 Params Descriptor");
+ goto out;
+ }
+
+ seq_printf(seq,
+ "# Group FieldCount RowCount Type Add Del Clear\n");
+
+ for (i = 0; i < result->row_count; i++) {
+ seq_printf(seq, "%-3d", i);
+ seq_printf(seq, "0x%04X ", result->group[i].group_number);
+ seq_printf(seq, "%10d ", result->group[i].field_count);
+ seq_printf(seq, "%8d ", result->group[i].row_count);
+
+ properties = result->group[i].properties;
+ if (properties & 0x1)
+ seq_printf(seq, "Table ");
+ else
+ seq_printf(seq, "Scalar ");
+ if (properties & 0x2)
+ seq_printf(seq, " + ");
+ else
+ seq_printf(seq, " - ");
+ if (properties & 0x4)
+ seq_printf(seq, " + ");
+ else
+ seq_printf(seq, " - ");
+ if (properties & 0x8)
+ seq_printf(seq, " + ");
+ else
+ seq_printf(seq, " - ");
+
+ seq_printf(seq, "\n");
+ }
+
+ if (result->more_flag)
+ seq_printf(seq, "There is more...\n");
+ out:
+ kfree(result);
+ return 0;
+}
+
+/* Generic group F001h - Physical Device Table (table) */
+static int i2o_seq_show_phys_device(struct seq_file *seq, void *v)
+{
+ struct i2o_device *d = (struct i2o_device *)seq->private;
+ int token;
+ int i;
+
+ struct {
+ u16 result_count;
+ u16 pad;
+ u16 block_size;
+ u8 block_status;
+ u8 error_info_size;
+ u16 row_count;
+ u16 more_flag;
+ u32 adapter_id[64];
+ } result;
+
+ token = i2o_parm_table_get(d, I2O_PARAMS_TABLE_GET, 0xF001, -1, NULL, 0,
+ &result, sizeof(result));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token,
+ "0xF001 Physical Device Table");
+ return 0;
+ }
+
+ if (result.row_count)
+ seq_printf(seq, "# AdapterId\n");
+
+ for (i = 0; i < result.row_count; i++) {
+ seq_printf(seq, "%-2d", i);
+ seq_printf(seq, "%#7x\n", result.adapter_id[i]);
+ }
+
+ if (result.more_flag)
+ seq_printf(seq, "There is more...\n");
+
+ return 0;
+}
+
+/* Generic group F002h - Claimed Table (table) */
+static int i2o_seq_show_claimed(struct seq_file *seq, void *v)
+{
+ struct i2o_device *d = (struct i2o_device *)seq->private;
+ int token;
+ int i;
+
+ struct {
+ u16 result_count;
+ u16 pad;
+ u16 block_size;
+ u8 block_status;
+ u8 error_info_size;
+ u16 row_count;
+ u16 more_flag;
+ u16 claimed_tid[64];
+ } result;
+
+ token = i2o_parm_table_get(d, I2O_PARAMS_TABLE_GET, 0xF002, -1, NULL, 0,
+ &result, sizeof(result));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token, "0xF002 Claimed Table");
+ return 0;
+ }
+
+ if (result.row_count)
+ seq_printf(seq, "# ClaimedTid\n");
+
+ for (i = 0; i < result.row_count; i++) {
+ seq_printf(seq, "%-2d", i);
+ seq_printf(seq, "%#7x\n", result.claimed_tid[i]);
+ }
+
+ if (result.more_flag)
+ seq_printf(seq, "There is more...\n");
+
+ return 0;
+}
+
+/* Generic group F003h - User Table (table) */
+static int i2o_seq_show_users(struct seq_file *seq, void *v)
+{
+ struct i2o_device *d = (struct i2o_device *)seq->private;
+ int token;
+ int i;
+
+ typedef struct _i2o_user_table {
+ u16 instance;
+ u16 user_tid;
+ u8 claim_type;
+ u8 reserved1;
+ u16 reserved2;
+ } i2o_user_table;
+
+ struct {
+ u16 result_count;
+ u16 pad;
+ u16 block_size;
+ u8 block_status;
+ u8 error_info_size;
+ u16 row_count;
+ u16 more_flag;
+ i2o_user_table user[64];
+ } *result;
+
+ result = kmalloc(sizeof(*result), GFP_KERNEL);
+ if (!result)
+ return -ENOMEM;
+
+ token = i2o_parm_table_get(d, I2O_PARAMS_TABLE_GET, 0xF003, -1, NULL, 0,
+ result, sizeof(*result));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token, "0xF003 User Table");
+ goto out;
+ }
+
+ seq_printf(seq, "# Instance UserTid ClaimType\n");
+
+ for (i = 0; i < result->row_count; i++) {
+ seq_printf(seq, "%-3d", i);
+ seq_printf(seq, "%#8x ", result->user[i].instance);
+ seq_printf(seq, "%#7x ", result->user[i].user_tid);
+ seq_printf(seq, "%#9x\n", result->user[i].claim_type);
+ }
+
+ if (result->more_flag)
+ seq_printf(seq, "There is more...\n");
+ out:
+ kfree(result);
+ return 0;
+}
+
+/* Generic group F005h - Private message extensions (table) (optional) */
+static int i2o_seq_show_priv_msgs(struct seq_file *seq, void *v)
+{
+ struct i2o_device *d = (struct i2o_device *)seq->private;
+ int token;
+ int i;
+
+ typedef struct _i2o_private {
+ u16 ext_instance;
+ u16 organization_id;
+ u16 x_function_code;
+ } i2o_private;
+
+ struct {
+ u16 result_count;
+ u16 pad;
+ u16 block_size;
+ u8 block_status;
+ u8 error_info_size;
+ u16 row_count;
+ u16 more_flag;
+ i2o_private extension[64];
+ } result;
+
+ token = i2o_parm_table_get(d, I2O_PARAMS_TABLE_GET, 0xF000, -1, NULL, 0,
+ &result, sizeof(result));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token,
+ "0xF005 Private Message Extensions (optional)");
+ return 0;
+ }
+
+ seq_printf(seq, "Instance# OrgId FunctionCode\n");
+
+ for (i = 0; i < result.row_count; i++) {
+ seq_printf(seq, "%0#9x ", result.extension[i].ext_instance);
+ seq_printf(seq, "%0#6x ", result.extension[i].organization_id);
+ seq_printf(seq, "%0#6x", result.extension[i].x_function_code);
+
+ seq_printf(seq, "\n");
+ }
+
+ if (result.more_flag)
+ seq_printf(seq, "There is more...\n");
+
+ return 0;
+}
+
+/* Generic group F006h - Authorized User Table (table) */
+static int i2o_seq_show_authorized_users(struct seq_file *seq, void *v)
+{
+ struct i2o_device *d = (struct i2o_device *)seq->private;
+ int token;
+ int i;
+
+ struct {
+ u16 result_count;
+ u16 pad;
+ u16 block_size;
+ u8 block_status;
+ u8 error_info_size;
+ u16 row_count;
+ u16 more_flag;
+ u32 alternate_tid[64];
+ } result;
+
+ token = i2o_parm_table_get(d, I2O_PARAMS_TABLE_GET, 0xF006, -1, NULL, 0,
+ &result, sizeof(result));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token,
+ "0xF006 Autohorized User Table");
+ return 0;
+ }
+
+ if (result.row_count)
+ seq_printf(seq, "# AlternateTid\n");
+
+ for (i = 0; i < result.row_count; i++) {
+ seq_printf(seq, "%-2d", i);
+ seq_printf(seq, "%#7x ", result.alternate_tid[i]);
+ }
+
+ if (result.more_flag)
+ seq_printf(seq, "There is more...\n");
+
+ return 0;
+}
+
+/* Generic group F100h - Device Identity (scalar) */
+static int i2o_seq_show_dev_identity(struct seq_file *seq, void *v)
+{
+ struct i2o_device *d = (struct i2o_device *)seq->private;
+ static u32 work32[128]; // allow for "stuff" + up to 256 byte (max) serial number
+ // == (allow) 512d bytes (max)
+ static u16 *work16 = (u16 *) work32;
+ int token;
+ char tmp[16 + 1];
+
+ token = i2o_parm_field_get(d, 0xF100, -1, &work32, sizeof(work32));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token, "0xF100 Device Identity");
+ return 0;
+ }
+
+ seq_printf(seq, "Device Class : %s\n", i2o_get_class_name(work16[0]));
+ seq_printf(seq, "Owner TID : %0#5x\n", work16[2]);
+ seq_printf(seq, "Parent TID : %0#5x\n", work16[3]);
+ seq_printf(seq, "Vendor info : %s\n",
+ chtostr(tmp, (u8 *) (work32 + 2), 16));
+ seq_printf(seq, "Product info : %s\n",
+ chtostr(tmp, (u8 *) (work32 + 6), 16));
+ seq_printf(seq, "Description : %s\n",
+ chtostr(tmp, (u8 *) (work32 + 10), 16));
+ seq_printf(seq, "Product rev. : %s\n",
+ chtostr(tmp, (u8 *) (work32 + 14), 8));
+
+ seq_printf(seq, "Serial number : ");
+ print_serial_number(seq, (u8 *) (work32 + 16),
+ /* allow for SNLen plus
+ * possible trailing '\0'
+ */
+ sizeof(work32) - (16 * sizeof(u32)) - 2);
+ seq_printf(seq, "\n");
+
+ return 0;
+}
+
+static int i2o_seq_show_dev_name(struct seq_file *seq, void *v)
+{
+ struct i2o_device *d = (struct i2o_device *)seq->private;
+
+ seq_printf(seq, "%s\n", dev_name(&d->device));
+
+ return 0;
+}
+
+/* Generic group F101h - DDM Identity (scalar) */
+static int i2o_seq_show_ddm_identity(struct seq_file *seq, void *v)
+{
+ struct i2o_device *d = (struct i2o_device *)seq->private;
+ int token;
+
+ struct {
+ u16 ddm_tid;
+ u8 module_name[24];
+ u8 module_rev[8];
+ u8 sn_format;
+ u8 serial_number[12];
+ u8 pad[256]; // allow up to 256 byte (max) serial number
+ } result;
+
+ char tmp[24 + 1];
+
+ token = i2o_parm_field_get(d, 0xF101, -1, &result, sizeof(result));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token, "0xF101 DDM Identity");
+ return 0;
+ }
+
+ seq_printf(seq, "Registering DDM TID : 0x%03x\n", result.ddm_tid);
+ seq_printf(seq, "Module name : %s\n",
+ chtostr(tmp, result.module_name, 24));
+ seq_printf(seq, "Module revision : %s\n",
+ chtostr(tmp, result.module_rev, 8));
+
+ seq_printf(seq, "Serial number : ");
+ print_serial_number(seq, result.serial_number, sizeof(result) - 36);
+ /* allow for SNLen plus possible trailing '\0' */
+
+ seq_printf(seq, "\n");
+
+ return 0;
+}
+
+/* Generic group F102h - User Information (scalar) */
+static int i2o_seq_show_uinfo(struct seq_file *seq, void *v)
+{
+ struct i2o_device *d = (struct i2o_device *)seq->private;
+ int token;
+
+ struct {
+ u8 device_name[64];
+ u8 service_name[64];
+ u8 physical_location[64];
+ u8 instance_number[4];
+ } result;
+
+ char tmp[64 + 1];
+
+ token = i2o_parm_field_get(d, 0xF102, -1, &result, sizeof(result));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token, "0xF102 User Information");
+ return 0;
+ }
+
+ seq_printf(seq, "Device name : %s\n",
+ chtostr(tmp, result.device_name, 64));
+ seq_printf(seq, "Service name : %s\n",
+ chtostr(tmp, result.service_name, 64));
+ seq_printf(seq, "Physical name : %s\n",
+ chtostr(tmp, result.physical_location, 64));
+ seq_printf(seq, "Instance number : %s\n",
+ chtostr(tmp, result.instance_number, 4));
+
+ return 0;
+}
+
+/* Generic group F103h - SGL Operating Limits (scalar) */
+static int i2o_seq_show_sgl_limits(struct seq_file *seq, void *v)
+{
+ struct i2o_device *d = (struct i2o_device *)seq->private;
+ static u32 work32[12];
+ static u16 *work16 = (u16 *) work32;
+ static u8 *work8 = (u8 *) work32;
+ int token;
+
+ token = i2o_parm_field_get(d, 0xF103, -1, &work32, sizeof(work32));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token,
+ "0xF103 SGL Operating Limits");
+ return 0;
+ }
+
+ seq_printf(seq, "SGL chain size : %d\n", work32[0]);
+ seq_printf(seq, "Max SGL chain size : %d\n", work32[1]);
+ seq_printf(seq, "SGL chain size target : %d\n", work32[2]);
+ seq_printf(seq, "SGL frag count : %d\n", work16[6]);
+ seq_printf(seq, "Max SGL frag count : %d\n", work16[7]);
+ seq_printf(seq, "SGL frag count target : %d\n", work16[8]);
+
+/* FIXME
+ if (d->i2oversion == 0x02)
+ {
+*/
+ seq_printf(seq, "SGL data alignment : %d\n", work16[8]);
+ seq_printf(seq, "SGL addr limit : %d\n", work8[20]);
+ seq_printf(seq, "SGL addr sizes supported : ");
+ if (work8[21] & 0x01)
+ seq_printf(seq, "32 bit ");
+ if (work8[21] & 0x02)
+ seq_printf(seq, "64 bit ");
+ if (work8[21] & 0x04)
+ seq_printf(seq, "96 bit ");
+ if (work8[21] & 0x08)
+ seq_printf(seq, "128 bit ");
+ seq_printf(seq, "\n");
+/*
+ }
+*/
+
+ return 0;
+}
+
+/* Generic group F200h - Sensors (scalar) */
+static int i2o_seq_show_sensors(struct seq_file *seq, void *v)
+{
+ struct i2o_device *d = (struct i2o_device *)seq->private;
+ int token;
+
+ struct {
+ u16 sensor_instance;
+ u8 component;
+ u16 component_instance;
+ u8 sensor_class;
+ u8 sensor_type;
+ u8 scaling_exponent;
+ u32 actual_reading;
+ u32 minimum_reading;
+ u32 low2lowcat_treshold;
+ u32 lowcat2low_treshold;
+ u32 lowwarn2low_treshold;
+ u32 low2lowwarn_treshold;
+ u32 norm2lowwarn_treshold;
+ u32 lowwarn2norm_treshold;
+ u32 nominal_reading;
+ u32 hiwarn2norm_treshold;
+ u32 norm2hiwarn_treshold;
+ u32 high2hiwarn_treshold;
+ u32 hiwarn2high_treshold;
+ u32 hicat2high_treshold;
+ u32 hi2hicat_treshold;
+ u32 maximum_reading;
+ u8 sensor_state;
+ u16 event_enable;
+ } result;
+
+ token = i2o_parm_field_get(d, 0xF200, -1, &result, sizeof(result));
+
+ if (token < 0) {
+ i2o_report_query_status(seq, token,
+ "0xF200 Sensors (optional)");
+ return 0;
+ }
+
+ seq_printf(seq, "Sensor instance : %d\n", result.sensor_instance);
+
+ seq_printf(seq, "Component : %d = ", result.component);
+ switch (result.component) {
+ case 0:
+ seq_printf(seq, "Other");
+ break;
+ case 1:
+ seq_printf(seq, "Planar logic Board");
+ break;
+ case 2:
+ seq_printf(seq, "CPU");
+ break;
+ case 3:
+ seq_printf(seq, "Chassis");
+ break;
+ case 4:
+ seq_printf(seq, "Power Supply");
+ break;
+ case 5:
+ seq_printf(seq, "Storage");
+ break;
+ case 6:
+ seq_printf(seq, "External");
+ break;
+ }
+ seq_printf(seq, "\n");
+
+ seq_printf(seq, "Component instance : %d\n",
+ result.component_instance);
+ seq_printf(seq, "Sensor class : %s\n",
+ result.sensor_class ? "Analog" : "Digital");
+
+ seq_printf(seq, "Sensor type : %d = ", result.sensor_type);
+ switch (result.sensor_type) {
+ case 0:
+ seq_printf(seq, "Other\n");
+ break;
+ case 1:
+ seq_printf(seq, "Thermal\n");
+ break;
+ case 2:
+ seq_printf(seq, "DC voltage (DC volts)\n");
+ break;
+ case 3:
+ seq_printf(seq, "AC voltage (AC volts)\n");
+ break;
+ case 4:
+ seq_printf(seq, "DC current (DC amps)\n");
+ break;
+ case 5:
+ seq_printf(seq, "AC current (AC volts)\n");
+ break;
+ case 6:
+ seq_printf(seq, "Door open\n");
+ break;
+ case 7:
+ seq_printf(seq, "Fan operational\n");
+ break;
+ }
+
+ seq_printf(seq, "Scaling exponent : %d\n",
+ result.scaling_exponent);
+ seq_printf(seq, "Actual reading : %d\n", result.actual_reading);
+ seq_printf(seq, "Minimum reading : %d\n", result.minimum_reading);
+ seq_printf(seq, "Low2LowCat treshold : %d\n",
+ result.low2lowcat_treshold);
+ seq_printf(seq, "LowCat2Low treshold : %d\n",
+ result.lowcat2low_treshold);
+ seq_printf(seq, "LowWarn2Low treshold : %d\n",
+ result.lowwarn2low_treshold);
+ seq_printf(seq, "Low2LowWarn treshold : %d\n",
+ result.low2lowwarn_treshold);
+ seq_printf(seq, "Norm2LowWarn treshold : %d\n",
+ result.norm2lowwarn_treshold);
+ seq_printf(seq, "LowWarn2Norm treshold : %d\n",
+ result.lowwarn2norm_treshold);
+ seq_printf(seq, "Nominal reading : %d\n", result.nominal_reading);
+ seq_printf(seq, "HiWarn2Norm treshold : %d\n",
+ result.hiwarn2norm_treshold);
+ seq_printf(seq, "Norm2HiWarn treshold : %d\n",
+ result.norm2hiwarn_treshold);
+ seq_printf(seq, "High2HiWarn treshold : %d\n",
+ result.high2hiwarn_treshold);
+ seq_printf(seq, "HiWarn2High treshold : %d\n",
+ result.hiwarn2high_treshold);
+ seq_printf(seq, "HiCat2High treshold : %d\n",
+ result.hicat2high_treshold);
+ seq_printf(seq, "High2HiCat treshold : %d\n",
+ result.hi2hicat_treshold);
+ seq_printf(seq, "Maximum reading : %d\n", result.maximum_reading);
+
+ seq_printf(seq, "Sensor state : %d = ", result.sensor_state);
+ switch (result.sensor_state) {
+ case 0:
+ seq_printf(seq, "Normal\n");
+ break;
+ case 1:
+ seq_printf(seq, "Abnormal\n");
+ break;
+ case 2:
+ seq_printf(seq, "Unknown\n");
+ break;
+ case 3:
+ seq_printf(seq, "Low Catastrophic (LoCat)\n");
+ break;
+ case 4:
+ seq_printf(seq, "Low (Low)\n");
+ break;
+ case 5:
+ seq_printf(seq, "Low Warning (LoWarn)\n");
+ break;
+ case 6:
+ seq_printf(seq, "High Warning (HiWarn)\n");
+ break;
+ case 7:
+ seq_printf(seq, "High (High)\n");
+ break;
+ case 8:
+ seq_printf(seq, "High Catastrophic (HiCat)\n");
+ break;
+ }
+
+ seq_printf(seq, "Event_enable : 0x%02X\n", result.event_enable);
+ seq_printf(seq, " [%s] Operational state change. \n",
+ (result.event_enable & 0x01) ? "+" : "-");
+ seq_printf(seq, " [%s] Low catastrophic. \n",
+ (result.event_enable & 0x02) ? "+" : "-");
+ seq_printf(seq, " [%s] Low reading. \n",
+ (result.event_enable & 0x04) ? "+" : "-");
+ seq_printf(seq, " [%s] Low warning. \n",
+ (result.event_enable & 0x08) ? "+" : "-");
+ seq_printf(seq,
+ " [%s] Change back to normal from out of range state. \n",
+ (result.event_enable & 0x10) ? "+" : "-");
+ seq_printf(seq, " [%s] High warning. \n",
+ (result.event_enable & 0x20) ? "+" : "-");
+ seq_printf(seq, " [%s] High reading. \n",
+ (result.event_enable & 0x40) ? "+" : "-");
+ seq_printf(seq, " [%s] High catastrophic. \n",
+ (result.event_enable & 0x80) ? "+" : "-");
+
+ return 0;
+}
+
+static int i2o_seq_open_hrt(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_hrt, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_lct(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_lct, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_status(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_status, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_hw(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_hw, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_ddm_table(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_ddm_table, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_driver_store(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_driver_store, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_drivers_stored(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_drivers_stored, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_groups(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_groups, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_phys_device(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_phys_device, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_claimed(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_claimed, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_users(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_users, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_priv_msgs(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_priv_msgs, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_authorized_users(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_authorized_users,
+ PDE_DATA(inode));
+};
+
+static int i2o_seq_open_dev_identity(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_dev_identity, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_ddm_identity(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_ddm_identity, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_uinfo(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_uinfo, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_sgl_limits(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_sgl_limits, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_sensors(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_sensors, PDE_DATA(inode));
+};
+
+static int i2o_seq_open_dev_name(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_dev_name, PDE_DATA(inode));
+};
+
+static const struct file_operations i2o_seq_fops_lct = {
+ .open = i2o_seq_open_lct,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_hrt = {
+ .open = i2o_seq_open_hrt,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_status = {
+ .open = i2o_seq_open_status,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_hw = {
+ .open = i2o_seq_open_hw,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_ddm_table = {
+ .open = i2o_seq_open_ddm_table,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_driver_store = {
+ .open = i2o_seq_open_driver_store,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_drivers_stored = {
+ .open = i2o_seq_open_drivers_stored,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_groups = {
+ .open = i2o_seq_open_groups,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_phys_device = {
+ .open = i2o_seq_open_phys_device,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_claimed = {
+ .open = i2o_seq_open_claimed,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_users = {
+ .open = i2o_seq_open_users,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_priv_msgs = {
+ .open = i2o_seq_open_priv_msgs,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_authorized_users = {
+ .open = i2o_seq_open_authorized_users,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_dev_name = {
+ .open = i2o_seq_open_dev_name,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_dev_identity = {
+ .open = i2o_seq_open_dev_identity,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_ddm_identity = {
+ .open = i2o_seq_open_ddm_identity,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_uinfo = {
+ .open = i2o_seq_open_uinfo,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_sgl_limits = {
+ .open = i2o_seq_open_sgl_limits,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations i2o_seq_fops_sensors = {
+ .open = i2o_seq_open_sensors,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/*
+ * IOP specific entries...write field just in case someone
+ * ever wants one.
+ */
+static i2o_proc_entry i2o_proc_generic_iop_entries[] = {
+ {"hrt", S_IFREG | S_IRUGO, &i2o_seq_fops_hrt},
+ {"lct", S_IFREG | S_IRUGO, &i2o_seq_fops_lct},
+ {"status", S_IFREG | S_IRUGO, &i2o_seq_fops_status},
+ {"hw", S_IFREG | S_IRUGO, &i2o_seq_fops_hw},
+ {"ddm_table", S_IFREG | S_IRUGO, &i2o_seq_fops_ddm_table},
+ {"driver_store", S_IFREG | S_IRUGO, &i2o_seq_fops_driver_store},
+ {"drivers_stored", S_IFREG | S_IRUGO, &i2o_seq_fops_drivers_stored},
+ {NULL, 0, NULL}
+};
+
+/*
+ * Device specific entries
+ */
+static i2o_proc_entry generic_dev_entries[] = {
+ {"groups", S_IFREG | S_IRUGO, &i2o_seq_fops_groups},
+ {"phys_dev", S_IFREG | S_IRUGO, &i2o_seq_fops_phys_device},
+ {"claimed", S_IFREG | S_IRUGO, &i2o_seq_fops_claimed},
+ {"users", S_IFREG | S_IRUGO, &i2o_seq_fops_users},
+ {"priv_msgs", S_IFREG | S_IRUGO, &i2o_seq_fops_priv_msgs},
+ {"authorized_users", S_IFREG | S_IRUGO, &i2o_seq_fops_authorized_users},
+ {"dev_identity", S_IFREG | S_IRUGO, &i2o_seq_fops_dev_identity},
+ {"ddm_identity", S_IFREG | S_IRUGO, &i2o_seq_fops_ddm_identity},
+ {"user_info", S_IFREG | S_IRUGO, &i2o_seq_fops_uinfo},
+ {"sgl_limits", S_IFREG | S_IRUGO, &i2o_seq_fops_sgl_limits},
+ {"sensors", S_IFREG | S_IRUGO, &i2o_seq_fops_sensors},
+ {NULL, 0, NULL}
+};
+
+/*
+ * Storage unit specific entries (SCSI Periph, BS) with device names
+ */
+static i2o_proc_entry rbs_dev_entries[] = {
+ {"dev_name", S_IFREG | S_IRUGO, &i2o_seq_fops_dev_name},
+ {NULL, 0, NULL}
+};
+
+/**
+ * i2o_proc_create_entries - Creates proc dir entries
+ * @dir: proc dir entry under which the entries should be placed
+ * @i2o_pe: pointer to the entries which should be added
+ * @data: pointer to I2O controller or device
+ *
+ * Create proc dir entries for a I2O controller or I2O device.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_proc_create_entries(struct proc_dir_entry *dir,
+ i2o_proc_entry * i2o_pe, void *data)
+{
+ struct proc_dir_entry *tmp;
+
+ while (i2o_pe->name) {
+ tmp = proc_create_data(i2o_pe->name, i2o_pe->mode, dir,
+ i2o_pe->fops, data);
+ if (!tmp)
+ return -1;
+
+ i2o_pe++;
+ }
+
+ return 0;
+}
+
+/**
+ * i2o_proc_device_add - Add an I2O device to the proc dir
+ * @dir: proc dir entry to which the device should be added
+ * @dev: I2O device which should be added
+ *
+ * Add an I2O device to the proc dir entry dir and create the entries for
+ * the device depending on the class of the I2O device.
+ */
+static void i2o_proc_device_add(struct proc_dir_entry *dir,
+ struct i2o_device *dev)
+{
+ char buff[10];
+ struct proc_dir_entry *devdir;
+ i2o_proc_entry *i2o_pe = NULL;
+
+ sprintf(buff, "%03x", dev->lct_data.tid);
+
+ osm_debug("adding device /proc/i2o/%s/%s\n", dev->iop->name, buff);
+
+ devdir = proc_mkdir_data(buff, 0, dir, dev);
+ if (!devdir) {
+ osm_warn("Could not allocate procdir!\n");
+ return;
+ }
+
+ i2o_proc_create_entries(devdir, generic_dev_entries, dev);
+
+ /* Inform core that we want updates about this device's status */
+ switch (dev->lct_data.class_id) {
+ case I2O_CLASS_SCSI_PERIPHERAL:
+ case I2O_CLASS_RANDOM_BLOCK_STORAGE:
+ i2o_pe = rbs_dev_entries;
+ break;
+ default:
+ break;
+ }
+ if (i2o_pe)
+ i2o_proc_create_entries(devdir, i2o_pe, dev);
+}
+
+/**
+ * i2o_proc_iop_add - Add an I2O controller to the i2o proc tree
+ * @dir: parent proc dir entry
+ * @c: I2O controller which should be added
+ *
+ * Add the entries to the parent proc dir entry. Also each device is added
+ * to the controllers proc dir entry.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_proc_iop_add(struct proc_dir_entry *dir,
+ struct i2o_controller *c)
+{
+ struct proc_dir_entry *iopdir;
+ struct i2o_device *dev;
+
+ osm_debug("adding IOP /proc/i2o/%s\n", c->name);
+
+ iopdir = proc_mkdir_data(c->name, 0, dir, c);
+ if (!iopdir)
+ return -1;
+
+ i2o_proc_create_entries(iopdir, i2o_proc_generic_iop_entries, c);
+
+ list_for_each_entry(dev, &c->devices, list)
+ i2o_proc_device_add(iopdir, dev);
+
+ return 0;
+}
+
+/**
+ * i2o_proc_fs_create - Create the i2o proc fs.
+ *
+ * Iterate over each I2O controller and create the entries for it.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int __init i2o_proc_fs_create(void)
+{
+ struct i2o_controller *c;
+
+ i2o_proc_dir_root = proc_mkdir("i2o", NULL);
+ if (!i2o_proc_dir_root)
+ return -1;
+
+ list_for_each_entry(c, &i2o_controllers, list)
+ i2o_proc_iop_add(i2o_proc_dir_root, c);
+
+ return 0;
+};
+
+/**
+ * i2o_proc_fs_destroy - Cleanup the all i2o proc entries
+ *
+ * Iterate over each I2O controller and remove the entries for it.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int __exit i2o_proc_fs_destroy(void)
+{
+ remove_proc_subtree("i2o", NULL);
+
+ return 0;
+};
+
+/**
+ * i2o_proc_init - Init function for procfs
+ *
+ * Registers Proc OSM and creates procfs entries.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int __init i2o_proc_init(void)
+{
+ int rc;
+
+ printk(KERN_INFO OSM_DESCRIPTION " v" OSM_VERSION "\n");
+
+ rc = i2o_driver_register(&i2o_proc_driver);
+ if (rc)
+ return rc;
+
+ rc = i2o_proc_fs_create();
+ if (rc) {
+ i2o_driver_unregister(&i2o_proc_driver);
+ return rc;
+ }
+
+ return 0;
+};
+
+/**
+ * i2o_proc_exit - Exit function for procfs
+ *
+ * Unregisters Proc OSM and removes procfs entries.
+ */
+static void __exit i2o_proc_exit(void)
+{
+ i2o_driver_unregister(&i2o_proc_driver);
+ i2o_proc_fs_destroy();
+};
+
+MODULE_AUTHOR("Deepak Saxena");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(OSM_DESCRIPTION);
+MODULE_VERSION(OSM_VERSION);
+
+module_init(i2o_proc_init);
+module_exit(i2o_proc_exit);
diff --git a/drivers/staging/i2o/i2o_scsi.c b/drivers/staging/i2o/i2o_scsi.c
new file mode 100644
index 0000000..1b11dcb
--- /dev/null
+++ b/drivers/staging/i2o/i2o_scsi.c
@@ -0,0 +1,814 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * For the avoidance of doubt the "preferred form" of this code is one which
+ * is in an open non patent encumbered format. Where cryptographic key signing
+ * forms part of the process of creating an executable the information
+ * including keys needed to generate an equivalently functional executable
+ * are deemed to be part of the source code.
+ *
+ * Complications for I2O scsi
+ *
+ * o Each (bus,lun) is a logical device in I2O. We keep a map
+ * table. We spoof failed selection for unmapped units
+ * o Request sense buffers can come back for free.
+ * o Scatter gather is a bit dynamic. We have to investigate at
+ * setup time.
+ * o Some of our resources are dynamically shared. The i2o core
+ * needs a message reservation protocol to avoid swap v net
+ * deadlocking. We need to back off queue requests.
+ *
+ * In general the firmware wants to help. Where its help isn't performance
+ * useful we just ignore the aid. Its not worth the code in truth.
+ *
+ * Fixes/additions:
+ * Steve Ralston:
+ * Scatter gather now works
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>:
+ * Minor fixes for 2.6.
+ *
+ * To Do:
+ * 64bit cleanups
+ * Fix the resource management problems.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/jiffies.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/prefetch.h>
+#include <linux/pci.h>
+#include <linux/blkdev.h>
+#include "i2o.h"
+#include <linux/scatterlist.h>
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <linux/atomic.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/sg.h>
+
+#define OSM_NAME "scsi-osm"
+#define OSM_VERSION "1.316"
+#define OSM_DESCRIPTION "I2O SCSI Peripheral OSM"
+
+static struct i2o_driver i2o_scsi_driver;
+
+static unsigned int i2o_scsi_max_id = 16;
+static unsigned int i2o_scsi_max_lun = 255;
+
+struct i2o_scsi_host {
+ struct Scsi_Host *scsi_host; /* pointer to the SCSI host */
+ struct i2o_controller *iop; /* pointer to the I2O controller */
+ u64 lun; /* lun's used for block devices */
+ struct i2o_device *channel[0]; /* channel->i2o_dev mapping table */
+};
+
+static struct scsi_host_template i2o_scsi_host_template;
+
+#define I2O_SCSI_CAN_QUEUE 4
+
+/* SCSI OSM class handling definition */
+static struct i2o_class_id i2o_scsi_class_id[] = {
+ {I2O_CLASS_SCSI_PERIPHERAL},
+ {I2O_CLASS_END}
+};
+
+static struct i2o_scsi_host *i2o_scsi_host_alloc(struct i2o_controller *c)
+{
+ struct i2o_scsi_host *i2o_shost;
+ struct i2o_device *i2o_dev;
+ struct Scsi_Host *scsi_host;
+ int max_channel = 0;
+ u8 type;
+ int i;
+ size_t size;
+ u16 body_size = 6;
+
+#ifdef CONFIG_I2O_EXT_ADAPTEC
+ if (c->adaptec)
+ body_size = 8;
+#endif
+
+ list_for_each_entry(i2o_dev, &c->devices, list)
+ if (i2o_dev->lct_data.class_id == I2O_CLASS_BUS_ADAPTER) {
+ if (!i2o_parm_field_get(i2o_dev, 0x0000, 0, &type, 1)
+ && (type == 0x01)) /* SCSI bus */
+ max_channel++;
+ }
+
+ if (!max_channel) {
+ osm_warn("no channels found on %s\n", c->name);
+ return ERR_PTR(-EFAULT);
+ }
+
+ size = max_channel * sizeof(struct i2o_device *)
+ + sizeof(struct i2o_scsi_host);
+
+ scsi_host = scsi_host_alloc(&i2o_scsi_host_template, size);
+ if (!scsi_host) {
+ osm_warn("Could not allocate SCSI host\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ scsi_host->max_channel = max_channel - 1;
+ scsi_host->max_id = i2o_scsi_max_id;
+ scsi_host->max_lun = i2o_scsi_max_lun;
+ scsi_host->this_id = c->unit;
+ scsi_host->sg_tablesize = i2o_sg_tablesize(c, body_size);
+
+ i2o_shost = (struct i2o_scsi_host *)scsi_host->hostdata;
+ i2o_shost->scsi_host = scsi_host;
+ i2o_shost->iop = c;
+ i2o_shost->lun = 1;
+
+ i = 0;
+ list_for_each_entry(i2o_dev, &c->devices, list)
+ if (i2o_dev->lct_data.class_id == I2O_CLASS_BUS_ADAPTER) {
+ if (!i2o_parm_field_get(i2o_dev, 0x0000, 0, &type, 1)
+ && (type == 0x01)) /* only SCSI bus */
+ i2o_shost->channel[i++] = i2o_dev;
+
+ if (i >= max_channel)
+ break;
+ }
+
+ return i2o_shost;
+};
+
+/**
+ * i2o_scsi_get_host - Get an I2O SCSI host
+ * @c: I2O controller to for which to get the SCSI host
+ *
+ * If the I2O controller already exists as SCSI host, the SCSI host
+ * is returned, otherwise the I2O controller is added to the SCSI
+ * core.
+ *
+ * Returns pointer to the I2O SCSI host on success or NULL on failure.
+ */
+static struct i2o_scsi_host *i2o_scsi_get_host(struct i2o_controller *c)
+{
+ return c->driver_data[i2o_scsi_driver.context];
+};
+
+/**
+ * i2o_scsi_remove - Remove I2O device from SCSI core
+ * @dev: device which should be removed
+ *
+ * Removes the I2O device from the SCSI core again.
+ *
+ * Returns 0 on success.
+ */
+static int i2o_scsi_remove(struct device *dev)
+{
+ struct i2o_device *i2o_dev = to_i2o_device(dev);
+ struct i2o_controller *c = i2o_dev->iop;
+ struct i2o_scsi_host *i2o_shost;
+ struct scsi_device *scsi_dev;
+
+ osm_info("device removed (TID: %03x)\n", i2o_dev->lct_data.tid);
+
+ i2o_shost = i2o_scsi_get_host(c);
+
+ shost_for_each_device(scsi_dev, i2o_shost->scsi_host)
+ if (scsi_dev->hostdata == i2o_dev) {
+ sysfs_remove_link(&i2o_dev->device.kobj, "scsi");
+ scsi_remove_device(scsi_dev);
+ scsi_device_put(scsi_dev);
+ break;
+ }
+
+ return 0;
+};
+
+/**
+ * i2o_scsi_probe - verify if dev is a I2O SCSI device and install it
+ * @dev: device to verify if it is a I2O SCSI device
+ *
+ * Retrieve channel, id and lun for I2O device. If everything goes well
+ * register the I2O device as SCSI device on the I2O SCSI controller.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_scsi_probe(struct device *dev)
+{
+ struct i2o_device *i2o_dev = to_i2o_device(dev);
+ struct i2o_controller *c = i2o_dev->iop;
+ struct i2o_scsi_host *i2o_shost;
+ struct Scsi_Host *scsi_host;
+ struct i2o_device *parent;
+ struct scsi_device *scsi_dev;
+ u32 id = -1;
+ u64 lun = -1;
+ int channel = -1;
+ int i, rc;
+
+ i2o_shost = i2o_scsi_get_host(c);
+ if (!i2o_shost)
+ return -EFAULT;
+
+ scsi_host = i2o_shost->scsi_host;
+
+ switch (i2o_dev->lct_data.class_id) {
+ case I2O_CLASS_RANDOM_BLOCK_STORAGE:
+ case I2O_CLASS_EXECUTIVE:
+#ifdef CONFIG_I2O_EXT_ADAPTEC
+ if (c->adaptec) {
+ u8 type;
+ struct i2o_device *d = i2o_shost->channel[0];
+
+ if (!i2o_parm_field_get(d, 0x0000, 0, &type, 1)
+ && (type == 0x01)) /* SCSI bus */
+ if (!i2o_parm_field_get(d, 0x0200, 4, &id, 4)) {
+ channel = 0;
+ if (i2o_dev->lct_data.class_id ==
+ I2O_CLASS_RANDOM_BLOCK_STORAGE)
+ lun =
+ cpu_to_le64(i2o_shost->
+ lun++);
+ else
+ lun = 0;
+ }
+ }
+#endif
+ break;
+
+ case I2O_CLASS_SCSI_PERIPHERAL:
+ if (i2o_parm_field_get(i2o_dev, 0x0000, 3, &id, 4))
+ return -EFAULT;
+
+ if (i2o_parm_field_get(i2o_dev, 0x0000, 4, &lun, 8))
+ return -EFAULT;
+
+ parent = i2o_iop_find_device(c, i2o_dev->lct_data.parent_tid);
+ if (!parent) {
+ osm_warn("can not find parent of device %03x\n",
+ i2o_dev->lct_data.tid);
+ return -EFAULT;
+ }
+
+ for (i = 0; i <= i2o_shost->scsi_host->max_channel; i++)
+ if (i2o_shost->channel[i] == parent)
+ channel = i;
+ break;
+
+ default:
+ return -EFAULT;
+ }
+
+ if (channel == -1) {
+ osm_warn("can not find channel of device %03x\n",
+ i2o_dev->lct_data.tid);
+ return -EFAULT;
+ }
+
+ if (le32_to_cpu(id) >= scsi_host->max_id) {
+ osm_warn("SCSI device id (%d) >= max_id of I2O host (%d)",
+ le32_to_cpu(id), scsi_host->max_id);
+ return -EFAULT;
+ }
+
+ if (le64_to_cpu(lun) >= scsi_host->max_lun) {
+ osm_warn("SCSI device lun (%llu) >= max_lun of I2O host (%llu)",
+ le64_to_cpu(lun), scsi_host->max_lun);
+ return -EFAULT;
+ }
+
+ scsi_dev =
+ __scsi_add_device(i2o_shost->scsi_host, channel, le32_to_cpu(id),
+ le64_to_cpu(lun), i2o_dev);
+
+ if (IS_ERR(scsi_dev)) {
+ osm_warn("can not add SCSI device %03x\n",
+ i2o_dev->lct_data.tid);
+ return PTR_ERR(scsi_dev);
+ }
+
+ rc = sysfs_create_link(&i2o_dev->device.kobj,
+ &scsi_dev->sdev_gendev.kobj, "scsi");
+ if (rc)
+ goto err;
+
+ osm_info("device added (TID: %03x) channel: %d, id: %d, lun: %llu\n",
+ i2o_dev->lct_data.tid, channel, le32_to_cpu(id),
+ le64_to_cpu(lun));
+
+ return 0;
+
+err:
+ scsi_remove_device(scsi_dev);
+ return rc;
+};
+
+static const char *i2o_scsi_info(struct Scsi_Host *SChost)
+{
+ struct i2o_scsi_host *hostdata;
+ hostdata = (struct i2o_scsi_host *)SChost->hostdata;
+ return hostdata->iop->name;
+}
+
+/**
+ * i2o_scsi_reply - SCSI OSM message reply handler
+ * @c: controller issuing the reply
+ * @m: message id for flushing
+ * @msg: the message from the controller
+ *
+ * Process reply messages (interrupts in normal scsi controller think).
+ * We can get a variety of messages to process. The normal path is
+ * scsi command completions. We must also deal with IOP failures,
+ * the reply to a bus reset and the reply to a LUN query.
+ *
+ * Returns 0 on success and if the reply should not be flushed or > 0
+ * on success and if the reply should be flushed. Returns negative error
+ * code on failure and if the reply should be flushed.
+ */
+static int i2o_scsi_reply(struct i2o_controller *c, u32 m,
+ struct i2o_message *msg)
+{
+ struct scsi_cmnd *cmd;
+ u32 error;
+ struct device *dev;
+
+ cmd = i2o_cntxt_list_get(c, le32_to_cpu(msg->u.s.tcntxt));
+ if (unlikely(!cmd)) {
+ osm_err("NULL reply received!\n");
+ return -1;
+ }
+
+ /*
+ * Low byte is device status, next is adapter status,
+ * (then one byte reserved), then request status.
+ */
+ error = le32_to_cpu(msg->body[0]);
+
+ osm_debug("Completed %0x%p\n", cmd);
+
+ cmd->result = error & 0xff;
+ /*
+ * if DeviceStatus is not SCSI_SUCCESS copy over the sense data and let
+ * the SCSI layer handle the error
+ */
+ if (cmd->result)
+ memcpy(cmd->sense_buffer, &msg->body[3],
+ min(SCSI_SENSE_BUFFERSIZE, 40));
+
+ /* only output error code if AdapterStatus is not HBA_SUCCESS */
+ if ((error >> 8) & 0xff)
+ osm_err("SCSI error %08x\n", error);
+
+ dev = &c->pdev->dev;
+
+ scsi_dma_unmap(cmd);
+
+ cmd->scsi_done(cmd);
+
+ return 1;
+};
+
+/**
+ * i2o_scsi_notify_device_add - Retrieve notifications of added devices
+ * @i2o_dev: the I2O device which was added
+ *
+ * If a I2O device is added we catch the notification, because I2O classes
+ * other than SCSI peripheral will not be received through
+ * i2o_scsi_probe().
+ */
+static void i2o_scsi_notify_device_add(struct i2o_device *i2o_dev)
+{
+ switch (i2o_dev->lct_data.class_id) {
+ case I2O_CLASS_EXECUTIVE:
+ case I2O_CLASS_RANDOM_BLOCK_STORAGE:
+ i2o_scsi_probe(&i2o_dev->device);
+ break;
+
+ default:
+ break;
+ }
+};
+
+/**
+ * i2o_scsi_notify_device_remove - Retrieve notifications of removed devices
+ * @i2o_dev: the I2O device which was removed
+ *
+ * If a I2O device is removed, we catch the notification to remove the
+ * corresponding SCSI device.
+ */
+static void i2o_scsi_notify_device_remove(struct i2o_device *i2o_dev)
+{
+ switch (i2o_dev->lct_data.class_id) {
+ case I2O_CLASS_EXECUTIVE:
+ case I2O_CLASS_RANDOM_BLOCK_STORAGE:
+ i2o_scsi_remove(&i2o_dev->device);
+ break;
+
+ default:
+ break;
+ }
+};
+
+/**
+ * i2o_scsi_notify_controller_add - Retrieve notifications of added controllers
+ * @c: the controller which was added
+ *
+ * If a I2O controller is added, we catch the notification to add a
+ * corresponding Scsi_Host.
+ */
+static void i2o_scsi_notify_controller_add(struct i2o_controller *c)
+{
+ struct i2o_scsi_host *i2o_shost;
+ int rc;
+
+ i2o_shost = i2o_scsi_host_alloc(c);
+ if (IS_ERR(i2o_shost)) {
+ osm_err("Could not initialize SCSI host\n");
+ return;
+ }
+
+ rc = scsi_add_host(i2o_shost->scsi_host, &c->device);
+ if (rc) {
+ osm_err("Could not add SCSI host\n");
+ scsi_host_put(i2o_shost->scsi_host);
+ return;
+ }
+
+ c->driver_data[i2o_scsi_driver.context] = i2o_shost;
+
+ osm_debug("new I2O SCSI host added\n");
+};
+
+/**
+ * i2o_scsi_notify_controller_remove - Retrieve notifications of removed controllers
+ * @c: the controller which was removed
+ *
+ * If a I2O controller is removed, we catch the notification to remove the
+ * corresponding Scsi_Host.
+ */
+static void i2o_scsi_notify_controller_remove(struct i2o_controller *c)
+{
+ struct i2o_scsi_host *i2o_shost;
+ i2o_shost = i2o_scsi_get_host(c);
+ if (!i2o_shost)
+ return;
+
+ c->driver_data[i2o_scsi_driver.context] = NULL;
+
+ scsi_remove_host(i2o_shost->scsi_host);
+ scsi_host_put(i2o_shost->scsi_host);
+ osm_debug("I2O SCSI host removed\n");
+};
+
+/* SCSI OSM driver struct */
+static struct i2o_driver i2o_scsi_driver = {
+ .name = OSM_NAME,
+ .reply = i2o_scsi_reply,
+ .classes = i2o_scsi_class_id,
+ .notify_device_add = i2o_scsi_notify_device_add,
+ .notify_device_remove = i2o_scsi_notify_device_remove,
+ .notify_controller_add = i2o_scsi_notify_controller_add,
+ .notify_controller_remove = i2o_scsi_notify_controller_remove,
+ .driver = {
+ .probe = i2o_scsi_probe,
+ .remove = i2o_scsi_remove,
+ },
+};
+
+/**
+ * i2o_scsi_queuecommand - queue a SCSI command
+ * @SCpnt: scsi command pointer
+ * @done: callback for completion
+ *
+ * Issue a scsi command asynchronously. Return 0 on success or 1 if
+ * we hit an error (normally message queue congestion). The only
+ * minor complication here is that I2O deals with the device addressing
+ * so we have to map the bus/dev/lun back to an I2O handle as well
+ * as faking absent devices ourself.
+ *
+ * Locks: takes the controller lock on error path only
+ */
+
+static int i2o_scsi_queuecommand_lck(struct scsi_cmnd *SCpnt,
+ void (*done) (struct scsi_cmnd *))
+{
+ struct i2o_controller *c;
+ struct i2o_device *i2o_dev;
+ int tid;
+ struct i2o_message *msg;
+ /*
+ * ENABLE_DISCONNECT
+ * SIMPLE_TAG
+ * RETURN_SENSE_DATA_IN_REPLY_MESSAGE_FRAME
+ */
+ u32 scsi_flags = 0x20a00000;
+ u32 sgl_offset;
+ u32 *mptr;
+ u32 cmd = I2O_CMD_SCSI_EXEC << 24;
+ int rc = 0;
+
+ /*
+ * Do the incoming paperwork
+ */
+ i2o_dev = SCpnt->device->hostdata;
+
+ SCpnt->scsi_done = done;
+
+ if (unlikely(!i2o_dev)) {
+ osm_warn("no I2O device in request\n");
+ SCpnt->result = DID_NO_CONNECT << 16;
+ done(SCpnt);
+ goto exit;
+ }
+ c = i2o_dev->iop;
+ tid = i2o_dev->lct_data.tid;
+
+ osm_debug("qcmd: Tid = %03x\n", tid);
+ osm_debug("Real scsi messages.\n");
+
+ /*
+ * Put together a scsi execscb message
+ */
+ switch (SCpnt->sc_data_direction) {
+ case PCI_DMA_NONE:
+ /* DATA NO XFER */
+ sgl_offset = SGL_OFFSET_0;
+ break;
+
+ case PCI_DMA_TODEVICE:
+ /* DATA OUT (iop-->dev) */
+ scsi_flags |= 0x80000000;
+ sgl_offset = SGL_OFFSET_10;
+ break;
+
+ case PCI_DMA_FROMDEVICE:
+ /* DATA IN (iop<--dev) */
+ scsi_flags |= 0x40000000;
+ sgl_offset = SGL_OFFSET_10;
+ break;
+
+ default:
+ /* Unknown - kill the command */
+ SCpnt->result = DID_NO_CONNECT << 16;
+ done(SCpnt);
+ goto exit;
+ }
+
+ /*
+ * Obtain an I2O message. If there are none free then
+ * throw it back to the scsi layer
+ */
+
+ msg = i2o_msg_get(c);
+ if (IS_ERR(msg)) {
+ rc = SCSI_MLQUEUE_HOST_BUSY;
+ goto exit;
+ }
+
+ mptr = &msg->body[0];
+
+#if 0 /* this code can't work */
+#ifdef CONFIG_I2O_EXT_ADAPTEC
+ if (c->adaptec) {
+ u32 adpt_flags = 0;
+
+ if (SCpnt->sc_request && SCpnt->sc_request->upper_private_data) {
+ i2o_sg_io_hdr_t __user *usr_ptr =
+ ((Sg_request *) (SCpnt->sc_request->
+ upper_private_data))->header.
+ usr_ptr;
+
+ if (usr_ptr)
+ get_user(adpt_flags, &usr_ptr->flags);
+ }
+
+ switch (i2o_dev->lct_data.class_id) {
+ case I2O_CLASS_EXECUTIVE:
+ case I2O_CLASS_RANDOM_BLOCK_STORAGE:
+ /* interpret flag has to be set for executive */
+ adpt_flags ^= I2O_DPT_SG_FLAG_INTERPRET;
+ break;
+
+ default:
+ break;
+ }
+
+ /*
+ * for Adaptec controllers we use the PRIVATE command, because
+ * the normal SCSI EXEC doesn't support all SCSI commands on
+ * all controllers (for example READ CAPACITY).
+ */
+ if (sgl_offset == SGL_OFFSET_10)
+ sgl_offset = SGL_OFFSET_12;
+ cmd = I2O_CMD_PRIVATE << 24;
+ *mptr++ = cpu_to_le32(I2O_VENDOR_DPT << 16 | I2O_CMD_SCSI_EXEC);
+ *mptr++ = cpu_to_le32(adpt_flags | tid);
+ }
+#endif
+#endif
+
+ msg->u.head[1] = cpu_to_le32(cmd | HOST_TID << 12 | tid);
+ msg->u.s.icntxt = cpu_to_le32(i2o_scsi_driver.context);
+
+ /* We want the SCSI control block back */
+ msg->u.s.tcntxt = cpu_to_le32(i2o_cntxt_list_add(c, SCpnt));
+
+ /* LSI_920_PCI_QUIRK
+ *
+ * Intermittant observations of msg frame word data corruption
+ * observed on msg[4] after:
+ * WRITE, READ-MODIFY-WRITE
+ * operations. 19990606 -sralston
+ *
+ * (Hence we build this word via tag. Its good practice anyway
+ * we don't want fetches over PCI needlessly)
+ */
+
+ /* Attach tags to the devices */
+ /* FIXME: implement
+ if(SCpnt->device->tagged_supported) {
+ if(SCpnt->tag == HEAD_OF_QUEUE_TAG)
+ scsi_flags |= 0x01000000;
+ else if(SCpnt->tag == ORDERED_QUEUE_TAG)
+ scsi_flags |= 0x01800000;
+ }
+ */
+
+ *mptr++ = cpu_to_le32(scsi_flags | SCpnt->cmd_len);
+
+ /* Write SCSI command into the message - always 16 byte block */
+ memcpy(mptr, SCpnt->cmnd, 16);
+ mptr += 4;
+
+ if (sgl_offset != SGL_OFFSET_0) {
+ /* write size of data addressed by SGL */
+ *mptr++ = cpu_to_le32(scsi_bufflen(SCpnt));
+
+ /* Now fill in the SGList and command */
+
+ if (scsi_sg_count(SCpnt)) {
+ if (!i2o_dma_map_sg(c, scsi_sglist(SCpnt),
+ scsi_sg_count(SCpnt),
+ SCpnt->sc_data_direction, &mptr))
+ goto nomem;
+ }
+ }
+
+ /* Stick the headers on */
+ msg->u.head[0] =
+ cpu_to_le32(I2O_MESSAGE_SIZE(mptr - &msg->u.head[0]) | sgl_offset);
+
+ /* Queue the message */
+ i2o_msg_post(c, msg);
+
+ osm_debug("Issued %0x%p\n", SCpnt);
+
+ return 0;
+
+ nomem:
+ rc = -ENOMEM;
+ i2o_msg_nop(c, msg);
+
+ exit:
+ return rc;
+}
+
+static DEF_SCSI_QCMD(i2o_scsi_queuecommand)
+
+/**
+ * i2o_scsi_abort - abort a running command
+ * @SCpnt: command to abort
+ *
+ * Ask the I2O controller to abort a command. This is an asynchrnous
+ * process and our callback handler will see the command complete with an
+ * aborted message if it succeeds.
+ *
+ * Returns 0 if the command is successfully aborted or negative error code
+ * on failure.
+ */
+static int i2o_scsi_abort(struct scsi_cmnd *SCpnt)
+{
+ struct i2o_device *i2o_dev;
+ struct i2o_controller *c;
+ struct i2o_message *msg;
+ int tid;
+ int status = FAILED;
+
+ osm_warn("Aborting command block.\n");
+
+ i2o_dev = SCpnt->device->hostdata;
+ c = i2o_dev->iop;
+ tid = i2o_dev->lct_data.tid;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ msg->u.head[0] = cpu_to_le32(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_SCSI_ABORT << 24 | HOST_TID << 12 | tid);
+ msg->body[0] = cpu_to_le32(i2o_cntxt_list_get_ptr(c, SCpnt));
+
+ if (!i2o_msg_post_wait(c, msg, I2O_TIMEOUT_SCSI_SCB_ABORT))
+ status = SUCCESS;
+
+ return status;
+}
+
+/**
+ * i2o_scsi_bios_param - Invent disk geometry
+ * @sdev: scsi device
+ * @dev: block layer device
+ * @capacity: size in sectors
+ * @ip: geometry array
+ *
+ * This is anyone's guess quite frankly. We use the same rules everyone
+ * else appears to and hope. It seems to work.
+ */
+
+static int i2o_scsi_bios_param(struct scsi_device *sdev,
+ struct block_device *dev, sector_t capacity,
+ int *ip)
+{
+ int size;
+
+ size = capacity;
+ ip[0] = 64; /* heads */
+ ip[1] = 32; /* sectors */
+ if ((ip[2] = size >> 11) > 1024) { /* cylinders, test for big disk */
+ ip[0] = 255; /* heads */
+ ip[1] = 63; /* sectors */
+ ip[2] = size / (255 * 63); /* cylinders */
+ }
+ return 0;
+}
+
+static struct scsi_host_template i2o_scsi_host_template = {
+ .proc_name = OSM_NAME,
+ .name = OSM_DESCRIPTION,
+ .info = i2o_scsi_info,
+ .queuecommand = i2o_scsi_queuecommand,
+ .eh_abort_handler = i2o_scsi_abort,
+ .bios_param = i2o_scsi_bios_param,
+ .can_queue = I2O_SCSI_CAN_QUEUE,
+ .sg_tablesize = 8,
+ .cmd_per_lun = 6,
+ .use_clustering = ENABLE_CLUSTERING,
+};
+
+/**
+ * i2o_scsi_init - SCSI OSM initialization function
+ *
+ * Register SCSI OSM into I2O core.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int __init i2o_scsi_init(void)
+{
+ int rc;
+
+ printk(KERN_INFO OSM_DESCRIPTION " v" OSM_VERSION "\n");
+
+ /* Register SCSI OSM into I2O core */
+ rc = i2o_driver_register(&i2o_scsi_driver);
+ if (rc) {
+ osm_err("Could not register SCSI driver\n");
+ return rc;
+ }
+
+ return 0;
+};
+
+/**
+ * i2o_scsi_exit - SCSI OSM exit function
+ *
+ * Unregisters SCSI OSM from I2O core.
+ */
+static void __exit i2o_scsi_exit(void)
+{
+ /* Unregister I2O SCSI OSM from I2O core */
+ i2o_driver_unregister(&i2o_scsi_driver);
+};
+
+MODULE_AUTHOR("Red Hat Software");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(OSM_DESCRIPTION);
+MODULE_VERSION(OSM_VERSION);
+
+module_init(i2o_scsi_init);
+module_exit(i2o_scsi_exit);
diff --git a/drivers/staging/i2o/iop.c b/drivers/staging/i2o/iop.c
new file mode 100644
index 0000000..52334fc
--- /dev/null
+++ b/drivers/staging/i2o/iop.c
@@ -0,0 +1,1247 @@
+/*
+ * Functions to handle I2O controllers and I2O message handling
+ *
+ * Copyright (C) 1999-2002 Red Hat Software
+ *
+ * Written by Alan Cox, Building Number Three Ltd
+ *
+ * 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.
+ *
+ * A lot of the I2O message side code from this is taken from the
+ * Red Creek RCPCI45 adapter driver by Red Creek Communications
+ *
+ * Fixes/additions:
+ * Philipp Rumpf
+ * Juha Sievänen <Juha.Sievanen@cs.Helsinki.FI>
+ * Auvo Häkkinen <Auvo.Hakkinen@cs.Helsinki.FI>
+ * Deepak Saxena <deepak@plexity.net>
+ * Boji T Kannanthanam <boji.t.kannanthanam@intel.com>
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>:
+ * Ported to Linux 2.5.
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>:
+ * Minor fixes for 2.6.
+ */
+
+#include <linux/module.h>
+#include "i2o.h"
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include "core.h"
+
+#define OSM_NAME "i2o"
+#define OSM_VERSION "1.325"
+#define OSM_DESCRIPTION "I2O subsystem"
+
+/* global I2O controller list */
+LIST_HEAD(i2o_controllers);
+
+/*
+ * global I2O System Table. Contains information about all the IOPs in the
+ * system. Used to inform IOPs about each others existence.
+ */
+static struct i2o_dma i2o_systab;
+
+static int i2o_hrt_get(struct i2o_controller *c);
+
+/**
+ * i2o_msg_get_wait - obtain an I2O message from the IOP
+ * @c: I2O controller
+ * @wait: how long to wait until timeout
+ *
+ * This function waits up to wait seconds for a message slot to be
+ * available.
+ *
+ * On a success the message is returned and the pointer to the message is
+ * set in msg. The returned message is the physical page frame offset
+ * address from the read port (see the i2o spec). If no message is
+ * available returns I2O_QUEUE_EMPTY and msg is leaved untouched.
+ */
+struct i2o_message *i2o_msg_get_wait(struct i2o_controller *c, int wait)
+{
+ unsigned long timeout = jiffies + wait * HZ;
+ struct i2o_message *msg;
+
+ while (IS_ERR(msg = i2o_msg_get(c))) {
+ if (time_after(jiffies, timeout)) {
+ osm_debug("%s: Timeout waiting for message frame.\n",
+ c->name);
+ return ERR_PTR(-ETIMEDOUT);
+ }
+ schedule_timeout_uninterruptible(1);
+ }
+
+ return msg;
+};
+
+#if BITS_PER_LONG == 64
+/**
+ * i2o_cntxt_list_add - Append a pointer to context list and return a id
+ * @c: controller to which the context list belong
+ * @ptr: pointer to add to the context list
+ *
+ * Because the context field in I2O is only 32-bit large, on 64-bit the
+ * pointer is to large to fit in the context field. The i2o_cntxt_list
+ * functions therefore map pointers to context fields.
+ *
+ * Returns context id > 0 on success or 0 on failure.
+ */
+u32 i2o_cntxt_list_add(struct i2o_controller * c, void *ptr)
+{
+ struct i2o_context_list_element *entry;
+ unsigned long flags;
+
+ if (!ptr)
+ osm_err("%s: couldn't add NULL pointer to context list!\n",
+ c->name);
+
+ entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+ if (!entry) {
+ osm_err("%s: Could not allocate memory for context list element"
+ "\n", c->name);
+ return 0;
+ }
+
+ entry->ptr = ptr;
+ entry->timestamp = jiffies;
+ INIT_LIST_HEAD(&entry->list);
+
+ spin_lock_irqsave(&c->context_list_lock, flags);
+
+ if (unlikely(atomic_inc_and_test(&c->context_list_counter)))
+ atomic_inc(&c->context_list_counter);
+
+ entry->context = atomic_read(&c->context_list_counter);
+
+ list_add(&entry->list, &c->context_list);
+
+ spin_unlock_irqrestore(&c->context_list_lock, flags);
+
+ osm_debug("%s: Add context to list %p -> %d\n", c->name, ptr, context);
+
+ return entry->context;
+};
+
+/**
+ * i2o_cntxt_list_remove - Remove a pointer from the context list
+ * @c: controller to which the context list belong
+ * @ptr: pointer which should be removed from the context list
+ *
+ * Removes a previously added pointer from the context list and returns
+ * the matching context id.
+ *
+ * Returns context id on success or 0 on failure.
+ */
+u32 i2o_cntxt_list_remove(struct i2o_controller * c, void *ptr)
+{
+ struct i2o_context_list_element *entry;
+ u32 context = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&c->context_list_lock, flags);
+ list_for_each_entry(entry, &c->context_list, list)
+ if (entry->ptr == ptr) {
+ list_del(&entry->list);
+ context = entry->context;
+ kfree(entry);
+ break;
+ }
+ spin_unlock_irqrestore(&c->context_list_lock, flags);
+
+ if (!context)
+ osm_warn("%s: Could not remove nonexistent ptr %p\n", c->name,
+ ptr);
+
+ osm_debug("%s: remove ptr from context list %d -> %p\n", c->name,
+ context, ptr);
+
+ return context;
+};
+
+/**
+ * i2o_cntxt_list_get - Get a pointer from the context list and remove it
+ * @c: controller to which the context list belong
+ * @context: context id to which the pointer belong
+ *
+ * Returns pointer to the matching context id on success or NULL on
+ * failure.
+ */
+void *i2o_cntxt_list_get(struct i2o_controller *c, u32 context)
+{
+ struct i2o_context_list_element *entry;
+ unsigned long flags;
+ void *ptr = NULL;
+
+ spin_lock_irqsave(&c->context_list_lock, flags);
+ list_for_each_entry(entry, &c->context_list, list)
+ if (entry->context == context) {
+ list_del(&entry->list);
+ ptr = entry->ptr;
+ kfree(entry);
+ break;
+ }
+ spin_unlock_irqrestore(&c->context_list_lock, flags);
+
+ if (!ptr)
+ osm_warn("%s: context id %d not found\n", c->name, context);
+
+ osm_debug("%s: get ptr from context list %d -> %p\n", c->name, context,
+ ptr);
+
+ return ptr;
+};
+
+/**
+ * i2o_cntxt_list_get_ptr - Get a context id from the context list
+ * @c: controller to which the context list belong
+ * @ptr: pointer to which the context id should be fetched
+ *
+ * Returns context id which matches to the pointer on success or 0 on
+ * failure.
+ */
+u32 i2o_cntxt_list_get_ptr(struct i2o_controller * c, void *ptr)
+{
+ struct i2o_context_list_element *entry;
+ u32 context = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&c->context_list_lock, flags);
+ list_for_each_entry(entry, &c->context_list, list)
+ if (entry->ptr == ptr) {
+ context = entry->context;
+ break;
+ }
+ spin_unlock_irqrestore(&c->context_list_lock, flags);
+
+ if (!context)
+ osm_warn("%s: Could not find nonexistent ptr %p\n", c->name,
+ ptr);
+
+ osm_debug("%s: get context id from context list %p -> %d\n", c->name,
+ ptr, context);
+
+ return context;
+};
+#endif
+
+/**
+ * i2o_iop_find - Find an I2O controller by id
+ * @unit: unit number of the I2O controller to search for
+ *
+ * Lookup the I2O controller on the controller list.
+ *
+ * Returns pointer to the I2O controller on success or NULL if not found.
+ */
+struct i2o_controller *i2o_find_iop(int unit)
+{
+ struct i2o_controller *c;
+
+ list_for_each_entry(c, &i2o_controllers, list) {
+ if (c->unit == unit)
+ return c;
+ }
+
+ return NULL;
+};
+
+/**
+ * i2o_iop_find_device - Find a I2O device on an I2O controller
+ * @c: I2O controller where the I2O device hangs on
+ * @tid: TID of the I2O device to search for
+ *
+ * Searches the devices of the I2O controller for a device with TID tid and
+ * returns it.
+ *
+ * Returns a pointer to the I2O device if found, otherwise NULL.
+ */
+struct i2o_device *i2o_iop_find_device(struct i2o_controller *c, u16 tid)
+{
+ struct i2o_device *dev;
+
+ list_for_each_entry(dev, &c->devices, list)
+ if (dev->lct_data.tid == tid)
+ return dev;
+
+ return NULL;
+};
+
+/**
+ * i2o_quiesce_controller - quiesce controller
+ * @c: controller
+ *
+ * Quiesce an IOP. Causes IOP to make external operation quiescent
+ * (i2o 'READY' state). Internal operation of the IOP continues normally.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_iop_quiesce(struct i2o_controller *c)
+{
+ struct i2o_message *msg;
+ i2o_status_block *sb = c->status_block.virt;
+ int rc;
+
+ i2o_status_get(c);
+
+ /* SysQuiesce discarded if IOP not in READY or OPERATIONAL state */
+ if ((sb->iop_state != ADAPTER_STATE_READY) &&
+ (sb->iop_state != ADAPTER_STATE_OPERATIONAL))
+ return 0;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_SYS_QUIESCE << 24 | HOST_TID << 12 |
+ ADAPTER_TID);
+
+ /* Long timeout needed for quiesce if lots of devices */
+ if ((rc = i2o_msg_post_wait(c, msg, 240)))
+ osm_info("%s: Unable to quiesce (status=%#x).\n", c->name, -rc);
+ else
+ osm_debug("%s: Quiesced.\n", c->name);
+
+ i2o_status_get(c); // Entered READY state
+
+ return rc;
+};
+
+/**
+ * i2o_iop_enable - move controller from ready to OPERATIONAL
+ * @c: I2O controller
+ *
+ * Enable IOP. This allows the IOP to resume external operations and
+ * reverses the effect of a quiesce. Returns zero or an error code if
+ * an error occurs.
+ */
+static int i2o_iop_enable(struct i2o_controller *c)
+{
+ struct i2o_message *msg;
+ i2o_status_block *sb = c->status_block.virt;
+ int rc;
+
+ i2o_status_get(c);
+
+ /* Enable only allowed on READY state */
+ if (sb->iop_state != ADAPTER_STATE_READY)
+ return -EINVAL;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_SYS_ENABLE << 24 | HOST_TID << 12 |
+ ADAPTER_TID);
+
+ /* How long of a timeout do we need? */
+ if ((rc = i2o_msg_post_wait(c, msg, 240)))
+ osm_err("%s: Could not enable (status=%#x).\n", c->name, -rc);
+ else
+ osm_debug("%s: Enabled.\n", c->name);
+
+ i2o_status_get(c); // entered OPERATIONAL state
+
+ return rc;
+};
+
+/**
+ * i2o_iop_quiesce_all - Quiesce all I2O controllers on the system
+ *
+ * Quiesce all I2O controllers which are connected to the system.
+ */
+static inline void i2o_iop_quiesce_all(void)
+{
+ struct i2o_controller *c, *tmp;
+
+ list_for_each_entry_safe(c, tmp, &i2o_controllers, list) {
+ if (!c->no_quiesce)
+ i2o_iop_quiesce(c);
+ }
+};
+
+/**
+ * i2o_iop_enable_all - Enables all controllers on the system
+ *
+ * Enables all I2O controllers which are connected to the system.
+ */
+static inline void i2o_iop_enable_all(void)
+{
+ struct i2o_controller *c, *tmp;
+
+ list_for_each_entry_safe(c, tmp, &i2o_controllers, list)
+ i2o_iop_enable(c);
+};
+
+/**
+ * i2o_clear_controller - Bring I2O controller into HOLD state
+ * @c: controller
+ *
+ * Clear an IOP to HOLD state, ie. terminate external operations, clear all
+ * input queues and prepare for a system restart. IOP's internal operation
+ * continues normally and the outbound queue is alive. The IOP is not
+ * expected to rebuild its LCT.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_iop_clear(struct i2o_controller *c)
+{
+ struct i2o_message *msg;
+ int rc;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ /* Quiesce all IOPs first */
+ i2o_iop_quiesce_all();
+
+ msg->u.head[0] = cpu_to_le32(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_ADAPTER_CLEAR << 24 | HOST_TID << 12 |
+ ADAPTER_TID);
+
+ if ((rc = i2o_msg_post_wait(c, msg, 30)))
+ osm_info("%s: Unable to clear (status=%#x).\n", c->name, -rc);
+ else
+ osm_debug("%s: Cleared.\n", c->name);
+
+ /* Enable all IOPs */
+ i2o_iop_enable_all();
+
+ return rc;
+}
+
+/**
+ * i2o_iop_init_outbound_queue - setup the outbound message queue
+ * @c: I2O controller
+ *
+ * Clear and (re)initialize IOP's outbound queue and post the message
+ * frames to the IOP.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_iop_init_outbound_queue(struct i2o_controller *c)
+{
+ u32 m;
+ volatile u8 *status = c->status.virt;
+ struct i2o_message *msg;
+ ulong timeout;
+ int i;
+
+ osm_debug("%s: Initializing Outbound Queue...\n", c->name);
+
+ memset(c->status.virt, 0, 4);
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(EIGHT_WORD_MSG_SIZE | SGL_OFFSET_6);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_OUTBOUND_INIT << 24 | HOST_TID << 12 |
+ ADAPTER_TID);
+ msg->u.s.icntxt = cpu_to_le32(i2o_exec_driver.context);
+ msg->u.s.tcntxt = cpu_to_le32(0x00000000);
+ msg->body[0] = cpu_to_le32(PAGE_SIZE);
+ /* Outbound msg frame size in words and Initcode */
+ msg->body[1] = cpu_to_le32(I2O_OUTBOUND_MSG_FRAME_SIZE << 16 | 0x80);
+ msg->body[2] = cpu_to_le32(0xd0000004);
+ msg->body[3] = cpu_to_le32(i2o_dma_low(c->status.phys));
+ msg->body[4] = cpu_to_le32(i2o_dma_high(c->status.phys));
+
+ i2o_msg_post(c, msg);
+
+ timeout = jiffies + I2O_TIMEOUT_INIT_OUTBOUND_QUEUE * HZ;
+ while (*status <= I2O_CMD_IN_PROGRESS) {
+ if (time_after(jiffies, timeout)) {
+ osm_warn("%s: Timeout Initializing\n", c->name);
+ return -ETIMEDOUT;
+ }
+ schedule_timeout_uninterruptible(1);
+ }
+
+ m = c->out_queue.phys;
+
+ /* Post frames */
+ for (i = 0; i < I2O_MAX_OUTBOUND_MSG_FRAMES; i++) {
+ i2o_flush_reply(c, m);
+ udelay(1); /* Promise */
+ m += I2O_OUTBOUND_MSG_FRAME_SIZE * sizeof(u32);
+ }
+
+ return 0;
+}
+
+/**
+ * i2o_iop_reset - reset an I2O controller
+ * @c: controller to reset
+ *
+ * Reset the IOP into INIT state and wait until IOP gets into RESET state.
+ * Terminate all external operations, clear IOP's inbound and outbound
+ * queues, terminate all DDMs, and reload the IOP's operating environment
+ * and all local DDMs. The IOP rebuilds its LCT.
+ */
+static int i2o_iop_reset(struct i2o_controller *c)
+{
+ volatile u8 *status = c->status.virt;
+ struct i2o_message *msg;
+ unsigned long timeout;
+ i2o_status_block *sb = c->status_block.virt;
+ int rc = 0;
+
+ osm_debug("%s: Resetting controller\n", c->name);
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ memset(c->status_block.virt, 0, 8);
+
+ /* Quiesce all IOPs first */
+ i2o_iop_quiesce_all();
+
+ msg->u.head[0] = cpu_to_le32(EIGHT_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_ADAPTER_RESET << 24 | HOST_TID << 12 |
+ ADAPTER_TID);
+ msg->u.s.icntxt = cpu_to_le32(i2o_exec_driver.context);
+ msg->u.s.tcntxt = cpu_to_le32(0x00000000);
+ msg->body[0] = cpu_to_le32(0x00000000);
+ msg->body[1] = cpu_to_le32(0x00000000);
+ msg->body[2] = cpu_to_le32(i2o_dma_low(c->status.phys));
+ msg->body[3] = cpu_to_le32(i2o_dma_high(c->status.phys));
+
+ i2o_msg_post(c, msg);
+
+ /* Wait for a reply */
+ timeout = jiffies + I2O_TIMEOUT_RESET * HZ;
+ while (!*status) {
+ if (time_after(jiffies, timeout))
+ break;
+
+ schedule_timeout_uninterruptible(1);
+ }
+
+ switch (*status) {
+ case I2O_CMD_REJECTED:
+ osm_warn("%s: IOP reset rejected\n", c->name);
+ rc = -EPERM;
+ break;
+
+ case I2O_CMD_IN_PROGRESS:
+ /*
+ * Once the reset is sent, the IOP goes into the INIT state
+ * which is indeterminate. We need to wait until the IOP has
+ * rebooted before we can let the system talk to it. We read
+ * the inbound Free_List until a message is available. If we
+ * can't read one in the given amount of time, we assume the
+ * IOP could not reboot properly.
+ */
+ osm_debug("%s: Reset in progress, waiting for reboot...\n",
+ c->name);
+
+ while (IS_ERR(msg = i2o_msg_get_wait(c, I2O_TIMEOUT_RESET))) {
+ if (time_after(jiffies, timeout)) {
+ osm_err("%s: IOP reset timeout.\n", c->name);
+ rc = PTR_ERR(msg);
+ goto exit;
+ }
+ schedule_timeout_uninterruptible(1);
+ }
+ i2o_msg_nop(c, msg);
+
+ /* from here all quiesce commands are safe */
+ c->no_quiesce = 0;
+
+ /* verify if controller is in state RESET */
+ i2o_status_get(c);
+
+ if (!c->promise && (sb->iop_state != ADAPTER_STATE_RESET))
+ osm_warn("%s: reset completed, but adapter not in RESET"
+ " state.\n", c->name);
+ else
+ osm_debug("%s: reset completed.\n", c->name);
+
+ break;
+
+ default:
+ osm_err("%s: IOP reset timeout.\n", c->name);
+ rc = -ETIMEDOUT;
+ break;
+ }
+
+ exit:
+ /* Enable all IOPs */
+ i2o_iop_enable_all();
+
+ return rc;
+};
+
+/**
+ * i2o_iop_activate - Bring controller up to HOLD
+ * @c: controller
+ *
+ * This function brings an I2O controller into HOLD state. The adapter
+ * is reset if necessary and then the queues and resource table are read.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_iop_activate(struct i2o_controller *c)
+{
+ i2o_status_block *sb = c->status_block.virt;
+ int rc;
+ int state;
+
+ /* In INIT state, Wait Inbound Q to initialize (in i2o_status_get) */
+ /* In READY state, Get status */
+
+ rc = i2o_status_get(c);
+ if (rc) {
+ osm_info("%s: Unable to obtain status, attempting a reset.\n",
+ c->name);
+ rc = i2o_iop_reset(c);
+ if (rc)
+ return rc;
+ }
+
+ if (sb->i2o_version > I2OVER15) {
+ osm_err("%s: Not running version 1.5 of the I2O Specification."
+ "\n", c->name);
+ return -ENODEV;
+ }
+
+ switch (sb->iop_state) {
+ case ADAPTER_STATE_FAULTED:
+ osm_err("%s: hardware fault\n", c->name);
+ return -EFAULT;
+
+ case ADAPTER_STATE_READY:
+ case ADAPTER_STATE_OPERATIONAL:
+ case ADAPTER_STATE_HOLD:
+ case ADAPTER_STATE_FAILED:
+ osm_debug("%s: already running, trying to reset...\n", c->name);
+ rc = i2o_iop_reset(c);
+ if (rc)
+ return rc;
+ }
+
+ /* preserve state */
+ state = sb->iop_state;
+
+ rc = i2o_iop_init_outbound_queue(c);
+ if (rc)
+ return rc;
+
+ /* if adapter was not in RESET state clear now */
+ if (state != ADAPTER_STATE_RESET)
+ i2o_iop_clear(c);
+
+ i2o_status_get(c);
+
+ if (sb->iop_state != ADAPTER_STATE_HOLD) {
+ osm_err("%s: failed to bring IOP into HOLD state\n", c->name);
+ return -EIO;
+ }
+
+ return i2o_hrt_get(c);
+};
+
+static void i2o_res_alloc(struct i2o_controller *c, unsigned long flags)
+{
+ i2o_status_block *sb = c->status_block.virt;
+ struct resource *res = &c->mem_resource;
+ resource_size_t size, align;
+ int err;
+
+ res->name = c->pdev->bus->name;
+ res->flags = flags;
+ res->start = 0;
+ res->end = 0;
+ osm_info("%s: requires private memory resources.\n", c->name);
+
+ if (flags & IORESOURCE_MEM) {
+ size = sb->desired_mem_size;
+ align = 1 << 20; /* unspecified, use 1Mb and play safe */
+ } else {
+ size = sb->desired_io_size;
+ align = 1 << 12; /* unspecified, use 4Kb and play safe */
+ }
+
+ err = pci_bus_alloc_resource(c->pdev->bus, res, size, align, 0, 0,
+ NULL, NULL);
+ if (err < 0)
+ return;
+
+ if (flags & IORESOURCE_MEM) {
+ c->mem_alloc = 1;
+ sb->current_mem_size = resource_size(res);
+ sb->current_mem_base = res->start;
+ } else if (flags & IORESOURCE_IO) {
+ c->io_alloc = 1;
+ sb->current_io_size = resource_size(res);
+ sb->current_io_base = res->start;
+ }
+ osm_info("%s: allocated PCI space %pR\n", c->name, res);
+}
+
+/**
+ * i2o_iop_systab_set - Set the I2O System Table of the specified IOP
+ * @c: I2O controller to which the system table should be send
+ *
+ * Before the systab could be set i2o_systab_build() must be called.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_iop_systab_set(struct i2o_controller *c)
+{
+ struct i2o_message *msg;
+ i2o_status_block *sb = c->status_block.virt;
+ struct device *dev = &c->pdev->dev;
+ int rc;
+
+ if (sb->current_mem_size < sb->desired_mem_size)
+ i2o_res_alloc(c, IORESOURCE_MEM);
+
+ if (sb->current_io_size < sb->desired_io_size)
+ i2o_res_alloc(c, IORESOURCE_IO);
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ i2o_systab.phys = dma_map_single(dev, i2o_systab.virt, i2o_systab.len,
+ PCI_DMA_TODEVICE);
+ if (!i2o_systab.phys) {
+ i2o_msg_nop(c, msg);
+ return -ENOMEM;
+ }
+
+ msg->u.head[0] = cpu_to_le32(I2O_MESSAGE_SIZE(12) | SGL_OFFSET_6);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_SYS_TAB_SET << 24 | HOST_TID << 12 |
+ ADAPTER_TID);
+
+ /*
+ * Provide three SGL-elements:
+ * System table (SysTab), Private memory space declaration and
+ * Private i/o space declaration
+ */
+
+ msg->body[0] = cpu_to_le32(c->unit + 2);
+ msg->body[1] = cpu_to_le32(0x00000000);
+ msg->body[2] = cpu_to_le32(0x54000000 | i2o_systab.len);
+ msg->body[3] = cpu_to_le32(i2o_systab.phys);
+ msg->body[4] = cpu_to_le32(0x54000000 | sb->current_mem_size);
+ msg->body[5] = cpu_to_le32(sb->current_mem_base);
+ msg->body[6] = cpu_to_le32(0xd4000000 | sb->current_io_size);
+ msg->body[6] = cpu_to_le32(sb->current_io_base);
+
+ rc = i2o_msg_post_wait(c, msg, 120);
+
+ dma_unmap_single(dev, i2o_systab.phys, i2o_systab.len,
+ PCI_DMA_TODEVICE);
+
+ if (rc < 0)
+ osm_err("%s: Unable to set SysTab (status=%#x).\n", c->name,
+ -rc);
+ else
+ osm_debug("%s: SysTab set.\n", c->name);
+
+ return rc;
+}
+
+/**
+ * i2o_iop_online - Bring a controller online into OPERATIONAL state.
+ * @c: I2O controller
+ *
+ * Send the system table and enable the I2O controller.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_iop_online(struct i2o_controller *c)
+{
+ int rc;
+
+ rc = i2o_iop_systab_set(c);
+ if (rc)
+ return rc;
+
+ /* In READY state */
+ osm_debug("%s: Attempting to enable...\n", c->name);
+ rc = i2o_iop_enable(c);
+ if (rc)
+ return rc;
+
+ return 0;
+};
+
+/**
+ * i2o_iop_remove - Remove the I2O controller from the I2O core
+ * @c: I2O controller
+ *
+ * Remove the I2O controller from the I2O core. If devices are attached to
+ * the controller remove these also and finally reset the controller.
+ */
+void i2o_iop_remove(struct i2o_controller *c)
+{
+ struct i2o_device *dev, *tmp;
+
+ osm_debug("%s: deleting controller\n", c->name);
+
+ i2o_driver_notify_controller_remove_all(c);
+
+ list_del(&c->list);
+
+ list_for_each_entry_safe(dev, tmp, &c->devices, list)
+ i2o_device_remove(dev);
+
+ device_del(&c->device);
+
+ /* Ask the IOP to switch to RESET state */
+ i2o_iop_reset(c);
+}
+
+/**
+ * i2o_systab_build - Build system table
+ *
+ * The system table contains information about all the IOPs in the system
+ * (duh) and is used by the Executives on the IOPs to establish peer2peer
+ * connections. We're not supporting peer2peer at the moment, but this
+ * will be needed down the road for things like lan2lan forwarding.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_systab_build(void)
+{
+ struct i2o_controller *c, *tmp;
+ int num_controllers = 0;
+ u32 change_ind = 0;
+ int count = 0;
+ struct i2o_sys_tbl *systab = i2o_systab.virt;
+
+ list_for_each_entry_safe(c, tmp, &i2o_controllers, list)
+ num_controllers++;
+
+ if (systab) {
+ change_ind = systab->change_ind;
+ kfree(i2o_systab.virt);
+ }
+
+ /* Header + IOPs */
+ i2o_systab.len = sizeof(struct i2o_sys_tbl) + num_controllers *
+ sizeof(struct i2o_sys_tbl_entry);
+
+ systab = i2o_systab.virt = kzalloc(i2o_systab.len, GFP_KERNEL);
+ if (!systab) {
+ osm_err("unable to allocate memory for System Table\n");
+ return -ENOMEM;
+ }
+
+ systab->version = I2OVERSION;
+ systab->change_ind = change_ind + 1;
+
+ list_for_each_entry_safe(c, tmp, &i2o_controllers, list) {
+ i2o_status_block *sb;
+
+ if (count >= num_controllers) {
+ osm_err("controller added while building system table"
+ "\n");
+ break;
+ }
+
+ sb = c->status_block.virt;
+
+ /*
+ * Get updated IOP state so we have the latest information
+ *
+ * We should delete the controller at this point if it
+ * doesn't respond since if it's not on the system table
+ * it is techninically not part of the I2O subsystem...
+ */
+ if (unlikely(i2o_status_get(c))) {
+ osm_err("%s: Deleting b/c could not get status while "
+ "attempting to build system table\n", c->name);
+ i2o_iop_remove(c);
+ continue; // try the next one
+ }
+
+ systab->iops[count].org_id = sb->org_id;
+ systab->iops[count].iop_id = c->unit + 2;
+ systab->iops[count].seg_num = 0;
+ systab->iops[count].i2o_version = sb->i2o_version;
+ systab->iops[count].iop_state = sb->iop_state;
+ systab->iops[count].msg_type = sb->msg_type;
+ systab->iops[count].frame_size = sb->inbound_frame_size;
+ systab->iops[count].last_changed = change_ind;
+ systab->iops[count].iop_capabilities = sb->iop_capabilities;
+ systab->iops[count].inbound_low =
+ i2o_dma_low(c->base.phys + I2O_IN_PORT);
+ systab->iops[count].inbound_high =
+ i2o_dma_high(c->base.phys + I2O_IN_PORT);
+
+ count++;
+ }
+
+ systab->num_entries = count;
+
+ return 0;
+};
+
+/**
+ * i2o_parse_hrt - Parse the hardware resource table.
+ * @c: I2O controller
+ *
+ * We don't do anything with it except dumping it (in debug mode).
+ *
+ * Returns 0.
+ */
+static int i2o_parse_hrt(struct i2o_controller *c)
+{
+ i2o_dump_hrt(c);
+ return 0;
+};
+
+/**
+ * i2o_status_get - Get the status block from the I2O controller
+ * @c: I2O controller
+ *
+ * Issue a status query on the controller. This updates the attached
+ * status block. The status block could then be accessed through
+ * c->status_block.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+int i2o_status_get(struct i2o_controller *c)
+{
+ struct i2o_message *msg;
+ volatile u8 *status_block;
+ unsigned long timeout;
+
+ status_block = (u8 *) c->status_block.virt;
+ memset(c->status_block.virt, 0, sizeof(i2o_status_block));
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(NINE_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_STATUS_GET << 24 | HOST_TID << 12 |
+ ADAPTER_TID);
+ msg->u.s.icntxt = cpu_to_le32(i2o_exec_driver.context);
+ msg->u.s.tcntxt = cpu_to_le32(0x00000000);
+ msg->body[0] = cpu_to_le32(0x00000000);
+ msg->body[1] = cpu_to_le32(0x00000000);
+ msg->body[2] = cpu_to_le32(i2o_dma_low(c->status_block.phys));
+ msg->body[3] = cpu_to_le32(i2o_dma_high(c->status_block.phys));
+ msg->body[4] = cpu_to_le32(sizeof(i2o_status_block)); /* always 88 bytes */
+
+ i2o_msg_post(c, msg);
+
+ /* Wait for a reply */
+ timeout = jiffies + I2O_TIMEOUT_STATUS_GET * HZ;
+ while (status_block[87] != 0xFF) {
+ if (time_after(jiffies, timeout)) {
+ osm_err("%s: Get status timeout.\n", c->name);
+ return -ETIMEDOUT;
+ }
+
+ schedule_timeout_uninterruptible(1);
+ }
+
+#ifdef DEBUG
+ i2o_debug_state(c);
+#endif
+
+ return 0;
+}
+
+/*
+ * i2o_hrt_get - Get the Hardware Resource Table from the I2O controller
+ * @c: I2O controller from which the HRT should be fetched
+ *
+ * The HRT contains information about possible hidden devices but is
+ * mostly useless to us.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_hrt_get(struct i2o_controller *c)
+{
+ int rc;
+ int i;
+ i2o_hrt *hrt = c->hrt.virt;
+ u32 size = sizeof(i2o_hrt);
+ struct device *dev = &c->pdev->dev;
+
+ for (i = 0; i < I2O_HRT_GET_TRIES; i++) {
+ struct i2o_message *msg;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(SIX_WORD_MSG_SIZE | SGL_OFFSET_4);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_HRT_GET << 24 | HOST_TID << 12 |
+ ADAPTER_TID);
+ msg->body[0] = cpu_to_le32(0xd0000000 | c->hrt.len);
+ msg->body[1] = cpu_to_le32(c->hrt.phys);
+
+ rc = i2o_msg_post_wait_mem(c, msg, 20, &c->hrt);
+
+ if (rc < 0) {
+ osm_err("%s: Unable to get HRT (status=%#x)\n", c->name,
+ -rc);
+ return rc;
+ }
+
+ size = hrt->num_entries * hrt->entry_len << 2;
+ if (size > c->hrt.len) {
+ if (i2o_dma_realloc(dev, &c->hrt, size))
+ return -ENOMEM;
+ else
+ hrt = c->hrt.virt;
+ } else
+ return i2o_parse_hrt(c);
+ }
+
+ osm_err("%s: Unable to get HRT after %d tries, giving up\n", c->name,
+ I2O_HRT_GET_TRIES);
+
+ return -EBUSY;
+}
+
+/**
+ * i2o_iop_release - release the memory for a I2O controller
+ * @dev: I2O controller which should be released
+ *
+ * Release the allocated memory. This function is called if refcount of
+ * device reaches 0 automatically.
+ */
+static void i2o_iop_release(struct device *dev)
+{
+ struct i2o_controller *c = to_i2o_controller(dev);
+
+ i2o_iop_free(c);
+};
+
+/**
+ * i2o_iop_alloc - Allocate and initialize a i2o_controller struct
+ *
+ * Allocate the necessary memory for a i2o_controller struct and
+ * initialize the lists and message mempool.
+ *
+ * Returns a pointer to the I2O controller or a negative error code on
+ * failure.
+ */
+struct i2o_controller *i2o_iop_alloc(void)
+{
+ static int unit = 0; /* 0 and 1 are NULL IOP and Local Host */
+ struct i2o_controller *c;
+ char poolname[32];
+
+ c = kzalloc(sizeof(*c), GFP_KERNEL);
+ if (!c) {
+ osm_err("i2o: Insufficient memory to allocate a I2O controller."
+ "\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ c->unit = unit++;
+ sprintf(c->name, "iop%d", c->unit);
+
+ snprintf(poolname, sizeof(poolname), "i2o_%s_msg_inpool", c->name);
+ if (i2o_pool_alloc
+ (&c->in_msg, poolname, I2O_INBOUND_MSG_FRAME_SIZE * 4 + sizeof(u32),
+ I2O_MSG_INPOOL_MIN)) {
+ kfree(c);
+ return ERR_PTR(-ENOMEM);
+ };
+
+ INIT_LIST_HEAD(&c->devices);
+ spin_lock_init(&c->lock);
+ mutex_init(&c->lct_lock);
+
+ device_initialize(&c->device);
+
+ c->device.release = &i2o_iop_release;
+
+ dev_set_name(&c->device, "iop%d", c->unit);
+
+#if BITS_PER_LONG == 64
+ spin_lock_init(&c->context_list_lock);
+ atomic_set(&c->context_list_counter, 0);
+ INIT_LIST_HEAD(&c->context_list);
+#endif
+
+ return c;
+};
+
+/**
+ * i2o_iop_add - Initialize the I2O controller and add him to the I2O core
+ * @c: controller
+ *
+ * Initialize the I2O controller and if no error occurs add him to the I2O
+ * core.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+int i2o_iop_add(struct i2o_controller *c)
+{
+ int rc;
+
+ if ((rc = device_add(&c->device))) {
+ osm_err("%s: could not add controller\n", c->name);
+ goto iop_reset;
+ }
+
+ osm_info("%s: Activating I2O controller...\n", c->name);
+ osm_info("%s: This may take a few minutes if there are many devices\n",
+ c->name);
+
+ if ((rc = i2o_iop_activate(c))) {
+ osm_err("%s: could not activate controller\n", c->name);
+ goto device_del;
+ }
+
+ osm_debug("%s: building sys table...\n", c->name);
+
+ if ((rc = i2o_systab_build()))
+ goto device_del;
+
+ osm_debug("%s: online controller...\n", c->name);
+
+ if ((rc = i2o_iop_online(c)))
+ goto device_del;
+
+ osm_debug("%s: getting LCT...\n", c->name);
+
+ if ((rc = i2o_exec_lct_get(c)))
+ goto device_del;
+
+ list_add(&c->list, &i2o_controllers);
+
+ i2o_driver_notify_controller_add_all(c);
+
+ osm_info("%s: Controller added\n", c->name);
+
+ return 0;
+
+ device_del:
+ device_del(&c->device);
+
+ iop_reset:
+ i2o_iop_reset(c);
+
+ return rc;
+};
+
+/**
+ * i2o_event_register - Turn on/off event notification for a I2O device
+ * @dev: I2O device which should receive the event registration request
+ * @drv: driver which want to get notified
+ * @tcntxt: transaction context to use with this notifier
+ * @evt_mask: mask of events
+ *
+ * Create and posts an event registration message to the task. No reply
+ * is waited for, or expected. If you do not want further notifications,
+ * call the i2o_event_register again with a evt_mask of 0.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+int i2o_event_register(struct i2o_device *dev, struct i2o_driver *drv,
+ int tcntxt, u32 evt_mask)
+{
+ struct i2o_controller *c = dev->iop;
+ struct i2o_message *msg;
+
+ msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET);
+ if (IS_ERR(msg))
+ return PTR_ERR(msg);
+
+ msg->u.head[0] = cpu_to_le32(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0);
+ msg->u.head[1] =
+ cpu_to_le32(I2O_CMD_UTIL_EVT_REGISTER << 24 | HOST_TID << 12 | dev->
+ lct_data.tid);
+ msg->u.s.icntxt = cpu_to_le32(drv->context);
+ msg->u.s.tcntxt = cpu_to_le32(tcntxt);
+ msg->body[0] = cpu_to_le32(evt_mask);
+
+ i2o_msg_post(c, msg);
+
+ return 0;
+};
+
+/**
+ * i2o_iop_init - I2O main initialization function
+ *
+ * Initialize the I2O drivers (OSM) functions, register the Executive OSM,
+ * initialize the I2O PCI part and finally initialize I2O device stuff.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int __init i2o_iop_init(void)
+{
+ int rc = 0;
+
+ printk(KERN_INFO OSM_DESCRIPTION " v" OSM_VERSION "\n");
+
+ if ((rc = i2o_driver_init()))
+ goto exit;
+
+ if ((rc = i2o_exec_init()))
+ goto driver_exit;
+
+ if ((rc = i2o_pci_init()))
+ goto exec_exit;
+
+ return 0;
+
+ exec_exit:
+ i2o_exec_exit();
+
+ driver_exit:
+ i2o_driver_exit();
+
+ exit:
+ return rc;
+}
+
+/**
+ * i2o_iop_exit - I2O main exit function
+ *
+ * Removes I2O controllers from PCI subsystem and shut down OSMs.
+ */
+static void __exit i2o_iop_exit(void)
+{
+ i2o_pci_exit();
+ i2o_exec_exit();
+ i2o_driver_exit();
+};
+
+module_init(i2o_iop_init);
+module_exit(i2o_iop_exit);
+
+MODULE_AUTHOR("Red Hat Software");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(OSM_DESCRIPTION);
+MODULE_VERSION(OSM_VERSION);
+
+#if BITS_PER_LONG == 64
+EXPORT_SYMBOL(i2o_cntxt_list_add);
+EXPORT_SYMBOL(i2o_cntxt_list_get);
+EXPORT_SYMBOL(i2o_cntxt_list_remove);
+EXPORT_SYMBOL(i2o_cntxt_list_get_ptr);
+#endif
+EXPORT_SYMBOL(i2o_msg_get_wait);
+EXPORT_SYMBOL(i2o_find_iop);
+EXPORT_SYMBOL(i2o_iop_find_device);
+EXPORT_SYMBOL(i2o_event_register);
+EXPORT_SYMBOL(i2o_status_get);
+EXPORT_SYMBOL(i2o_controllers);
diff --git a/drivers/staging/i2o/memory.c b/drivers/staging/i2o/memory.c
new file mode 100644
index 0000000..8f9509d
--- /dev/null
+++ b/drivers/staging/i2o/memory.c
@@ -0,0 +1,313 @@
+/*
+ * Functions to handle I2O memory
+ *
+ * Pulled from the inlines in i2o headers and uninlined
+ *
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include "i2o.h"
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "core.h"
+
+/* Protects our 32/64bit mask switching */
+static DEFINE_MUTEX(mem_lock);
+
+/**
+ * i2o_sg_tablesize - Calculate the maximum number of elements in a SGL
+ * @c: I2O controller for which the calculation should be done
+ * @body_size: maximum body size used for message in 32-bit words.
+ *
+ * Return the maximum number of SG elements in a SG list.
+ */
+u16 i2o_sg_tablesize(struct i2o_controller *c, u16 body_size)
+{
+ i2o_status_block *sb = c->status_block.virt;
+ u16 sg_count =
+ (sb->inbound_frame_size - sizeof(struct i2o_message) / 4) -
+ body_size;
+
+ if (c->pae_support) {
+ /*
+ * for 64-bit a SG attribute element must be added and each
+ * SG element needs 12 bytes instead of 8.
+ */
+ sg_count -= 2;
+ sg_count /= 3;
+ } else
+ sg_count /= 2;
+
+ if (c->short_req && (sg_count > 8))
+ sg_count = 8;
+
+ return sg_count;
+}
+EXPORT_SYMBOL_GPL(i2o_sg_tablesize);
+
+
+/**
+ * i2o_dma_map_single - Map pointer to controller and fill in I2O message.
+ * @c: I2O controller
+ * @ptr: pointer to the data which should be mapped
+ * @size: size of data in bytes
+ * @direction: DMA_TO_DEVICE / DMA_FROM_DEVICE
+ * @sg_ptr: pointer to the SG list inside the I2O message
+ *
+ * This function does all necessary DMA handling and also writes the I2O
+ * SGL elements into the I2O message. For details on DMA handling see also
+ * dma_map_single(). The pointer sg_ptr will only be set to the end of the
+ * SG list if the allocation was successful.
+ *
+ * Returns DMA address which must be checked for failures using
+ * dma_mapping_error().
+ */
+dma_addr_t i2o_dma_map_single(struct i2o_controller *c, void *ptr,
+ size_t size,
+ enum dma_data_direction direction,
+ u32 ** sg_ptr)
+{
+ u32 sg_flags;
+ u32 *mptr = *sg_ptr;
+ dma_addr_t dma_addr;
+
+ switch (direction) {
+ case DMA_TO_DEVICE:
+ sg_flags = 0xd4000000;
+ break;
+ case DMA_FROM_DEVICE:
+ sg_flags = 0xd0000000;
+ break;
+ default:
+ return 0;
+ }
+
+ dma_addr = dma_map_single(&c->pdev->dev, ptr, size, direction);
+ if (!dma_mapping_error(&c->pdev->dev, dma_addr)) {
+#ifdef CONFIG_I2O_EXT_ADAPTEC_DMA64
+ if ((sizeof(dma_addr_t) > 4) && c->pae_support) {
+ *mptr++ = cpu_to_le32(0x7C020002);
+ *mptr++ = cpu_to_le32(PAGE_SIZE);
+ }
+#endif
+
+ *mptr++ = cpu_to_le32(sg_flags | size);
+ *mptr++ = cpu_to_le32(i2o_dma_low(dma_addr));
+#ifdef CONFIG_I2O_EXT_ADAPTEC_DMA64
+ if ((sizeof(dma_addr_t) > 4) && c->pae_support)
+ *mptr++ = cpu_to_le32(i2o_dma_high(dma_addr));
+#endif
+ *sg_ptr = mptr;
+ }
+ return dma_addr;
+}
+EXPORT_SYMBOL_GPL(i2o_dma_map_single);
+
+/**
+ * i2o_dma_map_sg - Map a SG List to controller and fill in I2O message.
+ * @c: I2O controller
+ * @sg: SG list to be mapped
+ * @sg_count: number of elements in the SG list
+ * @direction: DMA_TO_DEVICE / DMA_FROM_DEVICE
+ * @sg_ptr: pointer to the SG list inside the I2O message
+ *
+ * This function does all necessary DMA handling and also writes the I2O
+ * SGL elements into the I2O message. For details on DMA handling see also
+ * dma_map_sg(). The pointer sg_ptr will only be set to the end of the SG
+ * list if the allocation was successful.
+ *
+ * Returns 0 on failure or 1 on success.
+ */
+int i2o_dma_map_sg(struct i2o_controller *c, struct scatterlist *sg,
+ int sg_count, enum dma_data_direction direction, u32 ** sg_ptr)
+{
+ u32 sg_flags;
+ u32 *mptr = *sg_ptr;
+
+ switch (direction) {
+ case DMA_TO_DEVICE:
+ sg_flags = 0x14000000;
+ break;
+ case DMA_FROM_DEVICE:
+ sg_flags = 0x10000000;
+ break;
+ default:
+ return 0;
+ }
+
+ sg_count = dma_map_sg(&c->pdev->dev, sg, sg_count, direction);
+ if (!sg_count)
+ return 0;
+
+#ifdef CONFIG_I2O_EXT_ADAPTEC_DMA64
+ if ((sizeof(dma_addr_t) > 4) && c->pae_support) {
+ *mptr++ = cpu_to_le32(0x7C020002);
+ *mptr++ = cpu_to_le32(PAGE_SIZE);
+ }
+#endif
+
+ while (sg_count-- > 0) {
+ if (!sg_count)
+ sg_flags |= 0xC0000000;
+ *mptr++ = cpu_to_le32(sg_flags | sg_dma_len(sg));
+ *mptr++ = cpu_to_le32(i2o_dma_low(sg_dma_address(sg)));
+#ifdef CONFIG_I2O_EXT_ADAPTEC_DMA64
+ if ((sizeof(dma_addr_t) > 4) && c->pae_support)
+ *mptr++ = cpu_to_le32(i2o_dma_high(sg_dma_address(sg)));
+#endif
+ sg = sg_next(sg);
+ }
+ *sg_ptr = mptr;
+
+ return 1;
+}
+EXPORT_SYMBOL_GPL(i2o_dma_map_sg);
+
+/**
+ * i2o_dma_alloc - Allocate DMA memory
+ * @dev: struct device pointer to the PCI device of the I2O controller
+ * @addr: i2o_dma struct which should get the DMA buffer
+ * @len: length of the new DMA memory
+ *
+ * Allocate a coherent DMA memory and write the pointers into addr.
+ *
+ * Returns 0 on success or -ENOMEM on failure.
+ */
+int i2o_dma_alloc(struct device *dev, struct i2o_dma *addr, size_t len)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ int dma_64 = 0;
+
+ mutex_lock(&mem_lock);
+ if ((sizeof(dma_addr_t) > 4) && (pdev->dma_mask == DMA_BIT_MASK(64))) {
+ dma_64 = 1;
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ mutex_unlock(&mem_lock);
+ return -ENOMEM;
+ }
+ }
+
+ addr->virt = dma_alloc_coherent(dev, len, &addr->phys, GFP_KERNEL);
+
+ if ((sizeof(dma_addr_t) > 4) && dma_64)
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)))
+ printk(KERN_WARNING "i2o: unable to set 64-bit DMA");
+ mutex_unlock(&mem_lock);
+
+ if (!addr->virt)
+ return -ENOMEM;
+
+ memset(addr->virt, 0, len);
+ addr->len = len;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(i2o_dma_alloc);
+
+
+/**
+ * i2o_dma_free - Free DMA memory
+ * @dev: struct device pointer to the PCI device of the I2O controller
+ * @addr: i2o_dma struct which contains the DMA buffer
+ *
+ * Free a coherent DMA memory and set virtual address of addr to NULL.
+ */
+void i2o_dma_free(struct device *dev, struct i2o_dma *addr)
+{
+ if (addr->virt) {
+ if (addr->phys)
+ dma_free_coherent(dev, addr->len, addr->virt,
+ addr->phys);
+ else
+ kfree(addr->virt);
+ addr->virt = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(i2o_dma_free);
+
+
+/**
+ * i2o_dma_realloc - Realloc DMA memory
+ * @dev: struct device pointer to the PCI device of the I2O controller
+ * @addr: pointer to a i2o_dma struct DMA buffer
+ * @len: new length of memory
+ *
+ * If there was something allocated in the addr, free it first. If len > 0
+ * than try to allocate it and write the addresses back to the addr
+ * structure. If len == 0 set the virtual address to NULL.
+ *
+ * Returns the 0 on success or negative error code on failure.
+ */
+int i2o_dma_realloc(struct device *dev, struct i2o_dma *addr, size_t len)
+{
+ i2o_dma_free(dev, addr);
+
+ if (len)
+ return i2o_dma_alloc(dev, addr, len);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(i2o_dma_realloc);
+
+/*
+ * i2o_pool_alloc - Allocate an slab cache and mempool
+ * @mempool: pointer to struct i2o_pool to write data into.
+ * @name: name which is used to identify cache
+ * @size: size of each object
+ * @min_nr: minimum number of objects
+ *
+ * First allocates a slab cache with name and size. Then allocates a
+ * mempool which uses the slab cache for allocation and freeing.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+int i2o_pool_alloc(struct i2o_pool *pool, const char *name,
+ size_t size, int min_nr)
+{
+ pool->name = kmalloc(strlen(name) + 1, GFP_KERNEL);
+ if (!pool->name)
+ goto exit;
+ strcpy(pool->name, name);
+
+ pool->slab =
+ kmem_cache_create(pool->name, size, 0, SLAB_HWCACHE_ALIGN, NULL);
+ if (!pool->slab)
+ goto free_name;
+
+ pool->mempool = mempool_create_slab_pool(min_nr, pool->slab);
+ if (!pool->mempool)
+ goto free_slab;
+
+ return 0;
+
+free_slab:
+ kmem_cache_destroy(pool->slab);
+
+free_name:
+ kfree(pool->name);
+
+exit:
+ return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(i2o_pool_alloc);
+
+/*
+ * i2o_pool_free - Free slab cache and mempool again
+ * @mempool: pointer to struct i2o_pool which should be freed
+ *
+ * Note that you have to return all objects to the mempool again before
+ * calling i2o_pool_free().
+ */
+void i2o_pool_free(struct i2o_pool *pool)
+{
+ mempool_destroy(pool->mempool);
+ kmem_cache_destroy(pool->slab);
+ kfree(pool->name);
+};
+EXPORT_SYMBOL_GPL(i2o_pool_free);
diff --git a/drivers/staging/i2o/pci.c b/drivers/staging/i2o/pci.c
new file mode 100644
index 0000000..b3b8a61
--- /dev/null
+++ b/drivers/staging/i2o/pci.c
@@ -0,0 +1,497 @@
+/*
+ * PCI handling of I2O controller
+ *
+ * Copyright (C) 1999-2002 Red Hat Software
+ *
+ * Written by Alan Cox, Building Number Three Ltd
+ *
+ * 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.
+ *
+ * A lot of the I2O message side code from this is taken from the Red
+ * Creek RCPCI45 adapter driver by Red Creek Communications
+ *
+ * Fixes/additions:
+ * Philipp Rumpf
+ * Juha Sievänen <Juha.Sievanen@cs.Helsinki.FI>
+ * Auvo Häkkinen <Auvo.Hakkinen@cs.Helsinki.FI>
+ * Deepak Saxena <deepak@plexity.net>
+ * Boji T Kannanthanam <boji.t.kannanthanam@intel.com>
+ * Alan Cox <alan@lxorguk.ukuu.org.uk>:
+ * Ported to Linux 2.5.
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>:
+ * Minor fixes for 2.6.
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>:
+ * Support for sysfs included.
+ */
+
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include "i2o.h"
+#include <linux/module.h>
+#include "core.h"
+
+#define OSM_DESCRIPTION "I2O-subsystem"
+
+/* PCI device id table for all I2O controllers */
+static struct pci_device_id i2o_pci_ids[] = {
+ {PCI_DEVICE_CLASS(PCI_CLASS_INTELLIGENT_I2O << 8, 0xffff00)},
+ {PCI_DEVICE(PCI_VENDOR_ID_DPT, 0xa511)},
+ {.vendor = PCI_VENDOR_ID_INTEL,.device = 0x1962,
+ .subvendor = PCI_VENDOR_ID_PROMISE,.subdevice = PCI_ANY_ID},
+ {0}
+};
+
+/**
+ * i2o_pci_free - Frees the DMA memory for the I2O controller
+ * @c: I2O controller to free
+ *
+ * Remove all allocated DMA memory and unmap memory IO regions. If MTRR
+ * is enabled, also remove it again.
+ */
+static void i2o_pci_free(struct i2o_controller *c)
+{
+ struct device *dev;
+
+ dev = &c->pdev->dev;
+
+ i2o_dma_free(dev, &c->out_queue);
+ i2o_dma_free(dev, &c->status_block);
+ kfree(c->lct);
+ i2o_dma_free(dev, &c->dlct);
+ i2o_dma_free(dev, &c->hrt);
+ i2o_dma_free(dev, &c->status);
+
+ if (c->raptor && c->in_queue.virt)
+ iounmap(c->in_queue.virt);
+
+ if (c->base.virt)
+ iounmap(c->base.virt);
+
+ pci_release_regions(c->pdev);
+}
+
+/**
+ * i2o_pci_alloc - Allocate DMA memory, map IO memory for I2O controller
+ * @c: I2O controller
+ *
+ * Allocate DMA memory for a PCI (or in theory AGP) I2O controller. All
+ * IO mappings are also done here. If MTRR is enabled, also do add memory
+ * regions here.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_pci_alloc(struct i2o_controller *c)
+{
+ struct pci_dev *pdev = c->pdev;
+ 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)) {
+ if (!c->base.phys) {
+ c->base.phys = pci_resource_start(pdev, i);
+ c->base.len = pci_resource_len(pdev, i);
+
+ /*
+ * If we know what card it is, set the size
+ * correctly. Code is taken from dpt_i2o.c
+ */
+ if (pdev->device == 0xa501) {
+ if (pdev->subsystem_device >= 0xc032 &&
+ pdev->subsystem_device <= 0xc03b) {
+ if (c->base.len > 0x400000)
+ c->base.len = 0x400000;
+ } else {
+ if (c->base.len > 0x100000)
+ c->base.len = 0x100000;
+ }
+ }
+ if (!c->raptor)
+ break;
+ } else {
+ c->in_queue.phys = pci_resource_start(pdev, i);
+ c->in_queue.len = pci_resource_len(pdev, i);
+ break;
+ }
+ }
+ }
+
+ if (i == 6) {
+ printk(KERN_ERR "%s: I2O controller has no memory regions"
+ " defined.\n", c->name);
+ i2o_pci_free(c);
+ return -EINVAL;
+ }
+
+ /* Map the I2O controller */
+ if (c->raptor) {
+ printk(KERN_INFO "%s: PCI I2O controller\n", c->name);
+ printk(KERN_INFO " BAR0 at 0x%08lX size=%ld\n",
+ (unsigned long)c->base.phys, (unsigned long)c->base.len);
+ printk(KERN_INFO " BAR1 at 0x%08lX size=%ld\n",
+ (unsigned long)c->in_queue.phys,
+ (unsigned long)c->in_queue.len);
+ } else
+ printk(KERN_INFO "%s: PCI I2O controller at %08lX size=%ld\n",
+ c->name, (unsigned long)c->base.phys,
+ (unsigned long)c->base.len);
+
+ c->base.virt = ioremap_nocache(c->base.phys, c->base.len);
+ if (!c->base.virt) {
+ printk(KERN_ERR "%s: Unable to map controller.\n", c->name);
+ i2o_pci_free(c);
+ return -ENOMEM;
+ }
+
+ if (c->raptor) {
+ c->in_queue.virt =
+ ioremap_nocache(c->in_queue.phys, c->in_queue.len);
+ if (!c->in_queue.virt) {
+ printk(KERN_ERR "%s: Unable to map controller.\n",
+ c->name);
+ i2o_pci_free(c);
+ return -ENOMEM;
+ }
+ } else
+ c->in_queue = c->base;
+
+ c->irq_status = c->base.virt + I2O_IRQ_STATUS;
+ c->irq_mask = c->base.virt + I2O_IRQ_MASK;
+ 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)) {
+ i2o_pci_free(c);
+ return -ENOMEM;
+ }
+
+ if (i2o_dma_alloc(dev, &c->hrt, sizeof(i2o_hrt))) {
+ i2o_pci_free(c);
+ return -ENOMEM;
+ }
+
+ if (i2o_dma_alloc(dev, &c->dlct, 8192)) {
+ i2o_pci_free(c);
+ return -ENOMEM;
+ }
+
+ if (i2o_dma_alloc(dev, &c->status_block, sizeof(i2o_status_block))) {
+ i2o_pci_free(c);
+ return -ENOMEM;
+ }
+
+ if (i2o_dma_alloc(dev, &c->out_queue,
+ I2O_MAX_OUTBOUND_MSG_FRAMES * I2O_OUTBOUND_MSG_FRAME_SIZE *
+ sizeof(u32))) {
+ i2o_pci_free(c);
+ return -ENOMEM;
+ }
+
+ pci_set_drvdata(pdev, c);
+
+ return 0;
+}
+
+/**
+ * i2o_pci_interrupt - Interrupt handler for I2O controller
+ * @irq: interrupt line
+ * @dev_id: pointer to the I2O controller
+ *
+ * Handle an interrupt from a PCI based I2O controller. This turns out
+ * to be rather simple. We keep the controller pointer in the cookie.
+ */
+static irqreturn_t i2o_pci_interrupt(int irq, void *dev_id)
+{
+ struct i2o_controller *c = dev_id;
+ u32 m;
+ irqreturn_t rc = IRQ_NONE;
+
+ while (readl(c->irq_status) & I2O_IRQ_OUTBOUND_POST) {
+ m = readl(c->out_port);
+ if (m == I2O_QUEUE_EMPTY) {
+ /*
+ * Old 960 steppings had a bug in the I2O unit that
+ * caused the queue to appear empty when it wasn't.
+ */
+ m = readl(c->out_port);
+ if (unlikely(m == I2O_QUEUE_EMPTY))
+ break;
+ }
+
+ /* dispatch it */
+ if (i2o_driver_dispatch(c, m))
+ /* flush it if result != 0 */
+ i2o_flush_reply(c, m);
+
+ rc = IRQ_HANDLED;
+ }
+
+ return rc;
+}
+
+/**
+ * i2o_pci_irq_enable - Allocate interrupt for I2O controller
+ * @c: i2o_controller that the request is for
+ *
+ * Allocate an interrupt for the I2O controller, and activate interrupts
+ * on the I2O controller.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_pci_irq_enable(struct i2o_controller *c)
+{
+ struct pci_dev *pdev = c->pdev;
+ int rc;
+
+ writel(0xffffffff, c->irq_mask);
+
+ if (pdev->irq) {
+ rc = request_irq(pdev->irq, i2o_pci_interrupt, IRQF_SHARED,
+ c->name, c);
+ if (rc < 0) {
+ printk(KERN_ERR "%s: unable to allocate interrupt %d."
+ "\n", c->name, pdev->irq);
+ return rc;
+ }
+ }
+
+ writel(0x00000000, c->irq_mask);
+
+ printk(KERN_INFO "%s: Installed at IRQ %d\n", c->name, pdev->irq);
+
+ return 0;
+}
+
+/**
+ * i2o_pci_irq_disable - Free interrupt for I2O controller
+ * @c: I2O controller
+ *
+ * Disable interrupts in I2O controller and then free interrupt.
+ */
+static void i2o_pci_irq_disable(struct i2o_controller *c)
+{
+ writel(0xffffffff, c->irq_mask);
+
+ if (c->pdev->irq > 0)
+ free_irq(c->pdev->irq, c);
+}
+
+/**
+ * i2o_pci_probe - Probe the PCI device for an I2O controller
+ * @pdev: PCI device to test
+ * @id: id which matched with the PCI device id table
+ *
+ * Probe the PCI device for any device which is a memory of the
+ * Intelligent, I2O class or an Adaptec Zero Channel Controller. We
+ * attempt to set up each such device and register it with the core.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct i2o_controller *c;
+ int rc;
+ struct pci_dev *i960 = NULL;
+
+ printk(KERN_INFO "i2o: Checking for PCI I2O controllers...\n");
+
+ if ((pdev->class & 0xff) > 1) {
+ printk(KERN_WARNING "i2o: %s does not support I2O 1.5 "
+ "(skipping).\n", pci_name(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_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ printk(KERN_WARNING "i2o: no suitable DMA found for %s\n",
+ pci_name(pdev));
+ rc = -ENODEV;
+ goto disable;
+ }
+
+ pci_set_master(pdev);
+
+ c = i2o_iop_alloc();
+ if (IS_ERR(c)) {
+ printk(KERN_ERR "i2o: couldn't allocate memory for %s\n",
+ pci_name(pdev));
+ rc = PTR_ERR(c);
+ goto disable;
+ } else
+ printk(KERN_INFO "%s: controller found (%s)\n", c->name,
+ pci_name(pdev));
+
+ c->pdev = pdev;
+ c->device.parent = &pdev->dev;
+
+ /* Cards that fall apart if you hit them with large I/O loads... */
+ if (pdev->vendor == PCI_VENDOR_ID_NCR && pdev->device == 0x0630) {
+ c->short_req = 1;
+ printk(KERN_INFO "%s: Symbios FC920 workarounds activated.\n",
+ c->name);
+ }
+
+ if (pdev->subsystem_vendor == PCI_VENDOR_ID_PROMISE) {
+ /*
+ * Expose the ship behind i960 for initialization, or it will
+ * failed
+ */
+ i960 = pci_get_slot(c->pdev->bus,
+ PCI_DEVFN(PCI_SLOT(c->pdev->devfn), 0));
+
+ if (i960) {
+ pci_write_config_word(i960, 0x42, 0);
+ pci_dev_put(i960);
+ }
+
+ c->promise = 1;
+ c->limit_sectors = 1;
+ }
+
+ if (pdev->subsystem_vendor == PCI_VENDOR_ID_DPT)
+ c->adaptec = 1;
+
+ /* Cards that go bananas if you quiesce them before you reset them. */
+ if (pdev->vendor == PCI_VENDOR_ID_DPT) {
+ c->no_quiesce = 1;
+ if (pdev->device == 0xa511)
+ c->raptor = 1;
+
+ if (pdev->subsystem_device == 0xc05a) {
+ c->limit_sectors = 1;
+ printk(KERN_INFO
+ "%s: limit sectors per request to %d\n", c->name,
+ I2O_MAX_SECTORS_LIMITED);
+ }
+#ifdef CONFIG_I2O_EXT_ADAPTEC_DMA64
+ if (sizeof(dma_addr_t) > 4) {
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)))
+ printk(KERN_INFO "%s: 64-bit DMA unavailable\n",
+ c->name);
+ else {
+ c->pae_support = 1;
+ printk(KERN_INFO "%s: using 64-bit DMA\n",
+ c->name);
+ }
+ }
+#endif
+ }
+
+ if ((rc = i2o_pci_alloc(c))) {
+ printk(KERN_ERR "%s: DMA / IO allocation for I2O controller "
+ "failed\n", c->name);
+ goto free_controller;
+ }
+
+ if (i2o_pci_irq_enable(c)) {
+ printk(KERN_ERR "%s: unable to enable interrupts for I2O "
+ "controller\n", c->name);
+ goto free_pci;
+ }
+
+ if ((rc = i2o_iop_add(c)))
+ goto uninstall;
+
+ if (i960)
+ pci_write_config_word(i960, 0x42, 0x03ff);
+
+ return 0;
+
+ uninstall:
+ i2o_pci_irq_disable(c);
+
+ free_pci:
+ i2o_pci_free(c);
+
+ free_controller:
+ i2o_iop_free(c);
+
+ disable:
+ pci_disable_device(pdev);
+
+ return rc;
+}
+
+/**
+ * i2o_pci_remove - Removes a I2O controller from the system
+ * @pdev: I2O controller which should be removed
+ *
+ * Reset the I2O controller, disable interrupts and remove all allocated
+ * resources.
+ */
+static void i2o_pci_remove(struct pci_dev *pdev)
+{
+ struct i2o_controller *c;
+ c = pci_get_drvdata(pdev);
+
+ i2o_iop_remove(c);
+ i2o_pci_irq_disable(c);
+ i2o_pci_free(c);
+
+ pci_disable_device(pdev);
+
+ printk(KERN_INFO "%s: Controller removed.\n", c->name);
+
+ put_device(&c->device);
+};
+
+/* PCI driver for I2O controller */
+static struct pci_driver i2o_pci_driver = {
+ .name = "PCI_I2O",
+ .id_table = i2o_pci_ids,
+ .probe = i2o_pci_probe,
+ .remove = i2o_pci_remove,
+};
+
+/**
+ * i2o_pci_init - registers I2O PCI driver in PCI subsystem
+ *
+ * Returns > 0 on success or negative error code on failure.
+ */
+int __init i2o_pci_init(void)
+{
+ return pci_register_driver(&i2o_pci_driver);
+};
+
+/**
+ * i2o_pci_exit - unregisters I2O PCI driver from PCI subsystem
+ */
+void __exit i2o_pci_exit(void)
+{
+ pci_unregister_driver(&i2o_pci_driver);
+};
+
+MODULE_DEVICE_TABLE(pci, i2o_pci_ids);