summaryrefslogtreecommitdiffstats
path: root/drivers/message/i2o
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/message/i2o
downloadblackbird-op-linux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.gz
blackbird-op-linux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/message/i2o')
-rw-r--r--drivers/message/i2o/Kconfig75
-rw-r--r--drivers/message/i2o/Makefile13
-rw-r--r--drivers/message/i2o/README98
-rw-r--r--drivers/message/i2o/README.ioctl394
-rw-r--r--drivers/message/i2o/debug.c481
-rw-r--r--drivers/message/i2o/device.c634
-rw-r--r--drivers/message/i2o/driver.c374
-rw-r--r--drivers/message/i2o/exec-osm.c507
-rw-r--r--drivers/message/i2o/i2o_block.c1247
-rw-r--r--drivers/message/i2o/i2o_block.h99
-rw-r--r--drivers/message/i2o/i2o_config.c1160
-rw-r--r--drivers/message/i2o/i2o_lan.h159
-rw-r--r--drivers/message/i2o/i2o_proc.c2112
-rw-r--r--drivers/message/i2o/i2o_scsi.c830
-rw-r--r--drivers/message/i2o/iop.c1327
-rw-r--r--drivers/message/i2o/pci.c528
16 files changed, 10038 insertions, 0 deletions
diff --git a/drivers/message/i2o/Kconfig b/drivers/message/i2o/Kconfig
new file mode 100644
index 000000000000..8d132b0d6b12
--- /dev/null
+++ b/drivers/message/i2o/Kconfig
@@ -0,0 +1,75 @@
+
+menu "I2O device support"
+
+config I2O
+ tristate "I2O 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.
+
+config I2O_CONFIG
+ tristate "I2O Configuration support"
+ depends on PCI && I2O
+ 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.
+
+config I2O_BLOCK
+ tristate "I2O Block OSM"
+ depends on I2O
+ 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 I2O && 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"
+ depends on I2O
+ 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.
+
+endmenu
+
diff --git a/drivers/message/i2o/Makefile b/drivers/message/i2o/Makefile
new file mode 100644
index 000000000000..aabc6cdc3fce
--- /dev/null
+++ b/drivers/message/i2o/Makefile
@@ -0,0 +1,13 @@
+#
+# 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
+obj-$(CONFIG_I2O) += i2o_core.o
+obj-$(CONFIG_I2O_CONFIG)+= i2o_config.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/message/i2o/README b/drivers/message/i2o/README
new file mode 100644
index 000000000000..a81f851f7b5d
--- /dev/null
+++ b/drivers/message/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 Comission
+ 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/message/i2o/README.ioctl b/drivers/message/i2o/README.ioctl
new file mode 100644
index 000000000000..73dd084c0e98
--- /dev/null
+++ b/drivers/message/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. Settting 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 appropriatly:
+
+ 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 retreived. 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 appropriatly:
+
+ 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 appropriatly:
+
+ 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 appropriatly:
+
+ 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 appropriatly:
+
+ 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 appropriatly:
+
+ 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/message/i2o/debug.c b/drivers/message/i2o/debug.c
new file mode 100644
index 000000000000..2a5d478fc60e
--- /dev/null
+++ b/drivers/message/i2o/debug.c
@@ -0,0 +1,481 @@
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/i2o.h>
+
+extern struct i2o_driver **i2o_drivers;
+extern unsigned int i2o_max_drivers;
+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;
+ //struct i2o_driver *h = i2o_drivers[msg[2] & (i2o_max_drivers-1)];
+
+ if (cmd == I2O_CMD_UTIL_EVT_REGISTER)
+ return; // No status in this reply
+
+ printk(KERN_DEBUG "%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(KERN_DEBUG "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(KERN_DEBUG " / 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(KERN_DEBUG "TRANSPORT_UNKNOWN_FAILURE (%0#2x)\n.",
+ req_status);
+ else
+ printk(KERN_DEBUG "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 ", (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(KERN_DEBUG "RequestStatus = %0#2x", req_status);
+ else
+ printk(KERN_DEBUG "%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(KERN_DEBUG " / DetailedStatus = %0#4x.\n",
+ detailed_status);
+ else
+ printk(KERN_DEBUG " / %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(KERN_DEBUG "UTIL_NOP, ");
+ break;
+ case I2O_CMD_UTIL_ABORT:
+ printk(KERN_DEBUG "UTIL_ABORT, ");
+ break;
+ case I2O_CMD_UTIL_CLAIM:
+ printk(KERN_DEBUG "UTIL_CLAIM, ");
+ break;
+ case I2O_CMD_UTIL_RELEASE:
+ printk(KERN_DEBUG "UTIL_CLAIM_RELEASE, ");
+ break;
+ case I2O_CMD_UTIL_CONFIG_DIALOG:
+ printk(KERN_DEBUG "UTIL_CONFIG_DIALOG, ");
+ break;
+ case I2O_CMD_UTIL_DEVICE_RESERVE:
+ printk(KERN_DEBUG "UTIL_DEVICE_RESERVE, ");
+ break;
+ case I2O_CMD_UTIL_DEVICE_RELEASE:
+ printk(KERN_DEBUG "UTIL_DEVICE_RELEASE, ");
+ break;
+ case I2O_CMD_UTIL_EVT_ACK:
+ printk(KERN_DEBUG "UTIL_EVENT_ACKNOWLEDGE, ");
+ break;
+ case I2O_CMD_UTIL_EVT_REGISTER:
+ printk(KERN_DEBUG "UTIL_EVENT_REGISTER, ");
+ break;
+ case I2O_CMD_UTIL_LOCK:
+ printk(KERN_DEBUG "UTIL_LOCK, ");
+ break;
+ case I2O_CMD_UTIL_LOCK_RELEASE:
+ printk(KERN_DEBUG "UTIL_LOCK_RELEASE, ");
+ break;
+ case I2O_CMD_UTIL_PARAMS_GET:
+ printk(KERN_DEBUG "UTIL_PARAMS_GET, ");
+ break;
+ case I2O_CMD_UTIL_PARAMS_SET:
+ printk(KERN_DEBUG "UTIL_PARAMS_SET, ");
+ break;
+ case I2O_CMD_UTIL_REPLY_FAULT_NOTIFY:
+ printk(KERN_DEBUG "UTIL_REPLY_FAULT_NOTIFY, ");
+ break;
+ default:
+ printk(KERN_DEBUG "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(KERN_DEBUG "EXEC_ADAPTER_ASSIGN, ");
+ break;
+ case I2O_CMD_ADAPTER_READ:
+ printk(KERN_DEBUG "EXEC_ADAPTER_READ, ");
+ break;
+ case I2O_CMD_ADAPTER_RELEASE:
+ printk(KERN_DEBUG "EXEC_ADAPTER_RELEASE, ");
+ break;
+ case I2O_CMD_BIOS_INFO_SET:
+ printk(KERN_DEBUG "EXEC_BIOS_INFO_SET, ");
+ break;
+ case I2O_CMD_BOOT_DEVICE_SET:
+ printk(KERN_DEBUG "EXEC_BOOT_DEVICE_SET, ");
+ break;
+ case I2O_CMD_CONFIG_VALIDATE:
+ printk(KERN_DEBUG "EXEC_CONFIG_VALIDATE, ");
+ break;
+ case I2O_CMD_CONN_SETUP:
+ printk(KERN_DEBUG "EXEC_CONN_SETUP, ");
+ break;
+ case I2O_CMD_DDM_DESTROY:
+ printk(KERN_DEBUG "EXEC_DDM_DESTROY, ");
+ break;
+ case I2O_CMD_DDM_ENABLE:
+ printk(KERN_DEBUG "EXEC_DDM_ENABLE, ");
+ break;
+ case I2O_CMD_DDM_QUIESCE:
+ printk(KERN_DEBUG "EXEC_DDM_QUIESCE, ");
+ break;
+ case I2O_CMD_DDM_RESET:
+ printk(KERN_DEBUG "EXEC_DDM_RESET, ");
+ break;
+ case I2O_CMD_DDM_SUSPEND:
+ printk(KERN_DEBUG "EXEC_DDM_SUSPEND, ");
+ break;
+ case I2O_CMD_DEVICE_ASSIGN:
+ printk(KERN_DEBUG "EXEC_DEVICE_ASSIGN, ");
+ break;
+ case I2O_CMD_DEVICE_RELEASE:
+ printk(KERN_DEBUG "EXEC_DEVICE_RELEASE, ");
+ break;
+ case I2O_CMD_HRT_GET:
+ printk(KERN_DEBUG "EXEC_HRT_GET, ");
+ break;
+ case I2O_CMD_ADAPTER_CLEAR:
+ printk(KERN_DEBUG "EXEC_IOP_CLEAR, ");
+ break;
+ case I2O_CMD_ADAPTER_CONNECT:
+ printk(KERN_DEBUG "EXEC_IOP_CONNECT, ");
+ break;
+ case I2O_CMD_ADAPTER_RESET:
+ printk(KERN_DEBUG "EXEC_IOP_RESET, ");
+ break;
+ case I2O_CMD_LCT_NOTIFY:
+ printk(KERN_DEBUG "EXEC_LCT_NOTIFY, ");
+ break;
+ case I2O_CMD_OUTBOUND_INIT:
+ printk(KERN_DEBUG "EXEC_OUTBOUND_INIT, ");
+ break;
+ case I2O_CMD_PATH_ENABLE:
+ printk(KERN_DEBUG "EXEC_PATH_ENABLE, ");
+ break;
+ case I2O_CMD_PATH_QUIESCE:
+ printk(KERN_DEBUG "EXEC_PATH_QUIESCE, ");
+ break;
+ case I2O_CMD_PATH_RESET:
+ printk(KERN_DEBUG "EXEC_PATH_RESET, ");
+ break;
+ case I2O_CMD_STATIC_MF_CREATE:
+ printk(KERN_DEBUG "EXEC_STATIC_MF_CREATE, ");
+ break;
+ case I2O_CMD_STATIC_MF_RELEASE:
+ printk(KERN_DEBUG "EXEC_STATIC_MF_RELEASE, ");
+ break;
+ case I2O_CMD_STATUS_GET:
+ printk(KERN_DEBUG "EXEC_STATUS_GET, ");
+ break;
+ case I2O_CMD_SW_DOWNLOAD:
+ printk(KERN_DEBUG "EXEC_SW_DOWNLOAD, ");
+ break;
+ case I2O_CMD_SW_UPLOAD:
+ printk(KERN_DEBUG "EXEC_SW_UPLOAD, ");
+ break;
+ case I2O_CMD_SW_REMOVE:
+ printk(KERN_DEBUG "EXEC_SW_REMOVE, ");
+ break;
+ case I2O_CMD_SYS_ENABLE:
+ printk(KERN_DEBUG "EXEC_SYS_ENABLE, ");
+ break;
+ case I2O_CMD_SYS_MODIFY:
+ printk(KERN_DEBUG "EXEC_SYS_MODIFY, ");
+ break;
+ case I2O_CMD_SYS_QUIESCE:
+ printk(KERN_DEBUG "EXEC_SYS_QUIESCE, ");
+ break;
+ case I2O_CMD_SYS_TAB_SET:
+ printk(KERN_DEBUG "EXEC_SYS_TAB_SET, ");
+ break;
+ default:
+ printk(KERN_DEBUG "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(KERN_DEBUG "INIT\n");
+ break;
+ case 0x02:
+ printk(KERN_DEBUG "RESET\n");
+ break;
+ case 0x04:
+ printk(KERN_DEBUG "HOLD\n");
+ break;
+ case 0x05:
+ printk(KERN_DEBUG "READY\n");
+ break;
+ case 0x08:
+ printk(KERN_DEBUG "OPERATIONAL\n");
+ break;
+ case 0x10:
+ printk(KERN_DEBUG "FAILED\n");
+ break;
+ case 0x11:
+ printk(KERN_DEBUG "FAULTED\n");
+ break;
+ default:
+ printk(KERN_DEBUG "%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(KERN_DEBUG "TID %04X:[", state & 0xFFF);
+ state >>= 12;
+ if (state & (1 << 0))
+ printk(KERN_DEBUG "H"); /* Hidden */
+ if (state & (1 << 2)) {
+ printk(KERN_DEBUG "P"); /* Present */
+ if (state & (1 << 1))
+ printk(KERN_DEBUG "C"); /* Controlled */
+ }
+ if (state > 9)
+ printk(KERN_DEBUG "*"); /* Hard */
+
+ printk(KERN_DEBUG "]:");
+
+ switch (p[3] & 0xFFFF) {
+ case 0:
+ /* Adapter private bus - easy */
+ printk(KERN_DEBUG
+ "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(KERN_DEBUG
+ "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(KERN_DEBUG
+ "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(KERN_DEBUG
+ "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(KERN_DEBUG
+ "PCI %d: Bus %d Device %d Function %d", p[2],
+ d[2], d[1], d[0]);
+ break;
+
+ case 0x80: /* Other */
+ default:
+ printk(KERN_DEBUG "Unsupported bus type.");
+ break;
+ }
+ printk(KERN_DEBUG "\n");
+ rows += length;
+ }
+}
+
+EXPORT_SYMBOL(i2o_dump_message);
diff --git a/drivers/message/i2o/device.c b/drivers/message/i2o/device.c
new file mode 100644
index 000000000000..eb907e87bc7b
--- /dev/null
+++ b/drivers/message/i2o/device.c
@@ -0,0 +1,634 @@
+/*
+ * 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 <linux/i2o.h>
+#include <linux/delay.h>
+
+/* Exec OSM functions */
+extern struct bus_type i2o_bus_type;
+
+/**
+ * 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 __iomem *msg;
+ u32 m;
+
+ m = i2o_msg_get_wait(dev->iop, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY)
+ return -ETIMEDOUT;
+
+ writel(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]);
+ writel(cmd << 24 | HOST_TID << 12 | dev->lct_data.tid, &msg->u.head[1]);
+ writel(type, &msg->body[0]);
+
+ return i2o_msg_post_wait(dev->iop, m, 60);
+};
+
+/**
+ * i2o_device_claim - claim a device for use by an OSM
+ * @dev: I2O device to claim
+ * @drv: I2O driver which wants to claim the device
+ *
+ * Do the leg work to assign a device to a given OSM. If the claim succeed
+ * the owner of the rimary. 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;
+
+ down(&dev->lock);
+
+ rc = i2o_device_issue_claim(dev, I2O_CMD_UTIL_CLAIM, I2O_CLAIM_PRIMARY);
+ if (!rc)
+ pr_debug("i2o: claim of device %d succeded\n",
+ dev->lct_data.tid);
+ else
+ pr_debug("i2o: claim of device %d failed %d\n",
+ dev->lct_data.tid, rc);
+
+ up(&dev->lock);
+
+ return rc;
+};
+
+/**
+ * i2o_device_claim_release - release a device that the OSM is using
+ * @dev: device to release
+ * @drv: driver which claimed the device
+ *
+ * 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;
+
+ down(&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 succeded\n",
+ dev->lct_data.tid);
+ else
+ pr_debug("i2o: claim release of device %d failed %d\n",
+ dev->lct_data.tid, rc);
+
+ up(&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->bus_id);
+
+ kfree(i2o_dev);
+};
+
+/**
+ * i2o_device_class_release - Remove I2O device attributes
+ * @cd: I2O class device which is added to the I2O device class
+ *
+ * Removes attributes from the I2O device again. Also search each device
+ * on the controller for I2O devices which refert to this device as parent
+ * or user and remove this links also.
+ */
+static void i2o_device_class_release(struct class_device *cd)
+{
+ struct i2o_device *i2o_dev, *tmp;
+ struct i2o_controller *c;
+
+ i2o_dev = to_i2o_device(cd->dev);
+ c = i2o_dev->iop;
+
+ 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");
+ }
+};
+
+/* I2O device class */
+static struct class i2o_device_class = {
+ .name = "i2o_device",
+ .release = i2o_device_class_release
+};
+
+/**
+ * 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 = kmalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ memset(dev, 0, sizeof(*dev));
+
+ INIT_LIST_HEAD(&dev->list);
+ init_MUTEX(&dev->lock);
+
+ dev->device.bus = &i2o_bus_type;
+ dev->device.release = &i2o_device_release;
+ dev->classdev.class = &i2o_device_class;
+ dev->classdev.dev = &dev->device;
+
+ return dev;
+};
+
+/**
+ * i2o_device_add - allocate a new I2O device and add it to the IOP
+ * @iop: I2O controller where 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 a pointer to the I2O device on success or negative error code
+ * on failure.
+ */
+static struct i2o_device *i2o_device_add(struct i2o_controller *c,
+ i2o_lct_entry * entry)
+{
+ struct i2o_device *dev;
+
+ dev = i2o_device_alloc();
+ if (IS_ERR(dev)) {
+ printk(KERN_ERR "i2o: unable to allocate i2o device\n");
+ return dev;
+ }
+
+ dev->lct_data = *entry;
+
+ snprintf(dev->device.bus_id, BUS_ID_SIZE, "%d:%03x", c->unit,
+ dev->lct_data.tid);
+
+ snprintf(dev->classdev.class_id, BUS_ID_SIZE, "%d:%03x", c->unit,
+ dev->lct_data.tid);
+
+ dev->iop = c;
+ dev->device.parent = &c->device;
+
+ device_register(&dev->device);
+
+ list_add_tail(&dev->list, &c->devices);
+
+ class_device_register(&dev->classdev);
+
+ i2o_driver_notify_device_add_all(dev);
+
+ pr_debug("i2o: device %s added\n", dev->device.bus_id);
+
+ return dev;
+};
+
+/**
+ * i2o_device_remove - remove an I2O device from the I2O core
+ * @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)
+{
+ i2o_driver_notify_device_remove_all(i2o_dev);
+ class_device_unregister(&i2o_dev->classdev);
+ 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;
+ int i;
+ int max;
+
+ down(&c->lct_lock);
+
+ if (c->lct)
+ kfree(c->lct);
+
+ lct = c->dlct.virt;
+
+ c->lct = kmalloc(lct->table_size * 4, GFP_KERNEL);
+ if (!c->lct) {
+ up(&c->lct_lock);
+ return -ENOMEM;
+ }
+
+ if (lct->table_size * 4 > c->dlct.len) {
+ memcpy_fromio(c->lct, c->dlct.virt, c->dlct.len);
+ up(&c->lct_lock);
+ return -EAGAIN;
+ }
+
+ memcpy_fromio(c->lct, c->dlct.virt, lct->table_size * 4);
+
+ lct = c->lct;
+
+ max = (lct->table_size - 3) / 9;
+
+ pr_debug("%s: LCT has %d entries (LCT size: %d)\n", c->name, max,
+ lct->table_size);
+
+ /* 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);
+ }
+
+ /* add new devices, which are new in the LCT */
+ for (i = 0; i < max; i++) {
+ int found = 0;
+
+ list_for_each_entry_safe(dev, tmp, &c->devices, list) {
+ if (lct->lct_entry[i].tid == dev->lct_data.tid) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ i2o_device_add(c, &lct->lct_entry[i]);
+ }
+ up(&c->lct_lock);
+
+ return 0;
+};
+
+/**
+ * i2o_device_class_show_class_id - Displays class id of I2O device
+ * @cd: class device of which the class id should be displayed
+ * @buf: buffer into which the class id should be printed
+ *
+ * Returns the number of bytes which are printed into the buffer.
+ */
+static ssize_t i2o_device_class_show_class_id(struct class_device *cd,
+ char *buf)
+{
+ struct i2o_device *dev = to_i2o_device(cd->dev);
+
+ sprintf(buf, "%03x\n", dev->lct_data.class_id);
+ return strlen(buf) + 1;
+};
+
+/**
+ * i2o_device_class_show_tid - Displays TID of I2O device
+ * @cd: class device of which the TID should be displayed
+ * @buf: buffer into which the class id should be printed
+ *
+ * Returns the number of bytes which are printed into the buffer.
+ */
+static ssize_t i2o_device_class_show_tid(struct class_device *cd, char *buf)
+{
+ struct i2o_device *dev = to_i2o_device(cd->dev);
+
+ sprintf(buf, "%03x\n", dev->lct_data.tid);
+ return strlen(buf) + 1;
+};
+
+/* I2O device class attributes */
+static CLASS_DEVICE_ATTR(class_id, S_IRUGO, i2o_device_class_show_class_id,
+ NULL);
+static CLASS_DEVICE_ATTR(tid, S_IRUGO, i2o_device_class_show_tid, NULL);
+
+/**
+ * i2o_device_class_add - Adds attributes to the I2O device
+ * @cd: I2O class device which is added to the I2O device class
+ *
+ * This function get called when a I2O device is added to the class. It
+ * creates the attributes for each device and creates user/parent symlink
+ * if necessary.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_device_class_add(struct class_device *cd)
+{
+ struct i2o_device *i2o_dev, *tmp;
+ struct i2o_controller *c;
+
+ i2o_dev = to_i2o_device(cd->dev);
+ c = i2o_dev->iop;
+
+ class_device_create_file(cd, &class_device_attr_class_id);
+ class_device_create_file(cd, &class_device_attr_tid);
+
+ /* create user entries for this device */
+ tmp = i2o_iop_find_device(i2o_dev->iop, i2o_dev->lct_data.user_tid);
+ if (tmp)
+ sysfs_create_link(&i2o_dev->device.kobj, &tmp->device.kobj,
+ "user");
+
+ /* create user entries refering to this device */
+ list_for_each_entry(tmp, &c->devices, list)
+ if (tmp->lct_data.user_tid == i2o_dev->lct_data.tid)
+ sysfs_create_link(&tmp->device.kobj,
+ &i2o_dev->device.kobj, "user");
+
+ /* create parent entries for this device */
+ tmp = i2o_iop_find_device(i2o_dev->iop, i2o_dev->lct_data.parent_tid);
+ if (tmp)
+ sysfs_create_link(&i2o_dev->device.kobj, &tmp->device.kobj,
+ "parent");
+
+ /* create parent entries refering to this device */
+ list_for_each_entry(tmp, &c->devices, list)
+ if (tmp->lct_data.parent_tid == i2o_dev->lct_data.tid)
+ sysfs_create_link(&tmp->device.kobj,
+ &i2o_dev->device.kobj, "parent");
+
+ return 0;
+};
+
+/* I2O device class interface */
+static struct class_interface i2o_device_class_interface = {
+ .class = &i2o_device_class,
+ .add = i2o_device_class_add
+};
+
+/*
+ * 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 __iomem *msg;
+ u32 m;
+ u32 *res32 = (u32 *) reslist;
+ u32 *restmp = (u32 *) reslist;
+ int len = 0;
+ 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, GFP_KERNEL))
+ return -ENOMEM;
+
+ m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY) {
+ i2o_dma_free(dev, &res);
+ return -ETIMEDOUT;
+ }
+
+ i = 0;
+ writel(cmd << 24 | HOST_TID << 12 | i2o_dev->lct_data.tid,
+ &msg->u.head[1]);
+ writel(0, &msg->body[i++]);
+ writel(0x4C000000 | oplen, &msg->body[i++]); /* OperationList */
+ memcpy_toio(&msg->body[i], oplist, oplen);
+ i += (oplen / 4 + (oplen % 4 ? 1 : 0));
+ writel(0xD0000000 | res.len, &msg->body[i++]); /* ResultList */
+ writel(res.phys, &msg->body[i++]);
+
+ writel(I2O_MESSAGE_SIZE(i + sizeof(struct i2o_message) / 4) |
+ SGL_OFFSET_5, &msg->u.head[0]);
+
+ rc = i2o_msg_post_wait_mem(c, m, 10, &res);
+
+ /* This only looks like a memory leak - don't "fix" it. */
+ if (rc == -ETIMEDOUT)
+ return rc;
+
+ memcpy_fromio(reslist, res.virt, res.len);
+ i2o_dma_free(dev, &res);
+
+ /* Query failed */
+ if (rc)
+ return rc;
+ /*
+ * Calculate number of bytes of Result LIST
+ * We need to loop through each Result BLOCK and grab the length
+ */
+ restmp = res32 + 1;
+ len = 1;
+ for (i = 0; i < (res32[0] & 0X0000FFFF); i++) {
+ if (restmp[0] & 0x00FF0000) { /* BlockStatus != SUCCESS */
+ printk(KERN_WARNING
+ "%s - Error:\n ErrorInfoSize = 0x%02x, "
+ "BlockStatus = 0x%02x, BlockSize = 0x%04x\n",
+ (cmd ==
+ I2O_CMD_UTIL_PARAMS_SET) ? "PARAMS_SET" :
+ "PARAMS_GET", res32[1] >> 24,
+ (res32[1] >> 16) & 0xFF, res32[1] & 0xFFFF);
+
+ /*
+ * If this is the only request,than we return an error
+ */
+ if ((res32[0] & 0x0000FFFF) == 1) {
+ return -((res32[1] >> 16) & 0xFF); /* -BlockStatus */
+ }
+ }
+ len += restmp[0] & 0x0000FFFF; /* Length of res BLOCK */
+ restmp += restmp[0] & 0x0000FFFF; /* Skip to next BLOCK */
+ }
+ return (len << 2); /* bytes used by result list */
+}
+
+/*
+ * 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)
+{
+ u16 opblk[] = { 1, 0, I2O_PARAMS_FIELD_GET, group, 1, field };
+ u8 resblk[8 + buflen]; /* 8 bytes for header */
+ int size;
+
+ if (field == -1) /* whole group */
+ opblk[4] = -1;
+
+ size = i2o_parm_issue(i2o_dev, I2O_CMD_UTIL_PARAMS_GET, opblk,
+ sizeof(opblk), resblk, sizeof(resblk));
+
+ memcpy(buf, resblk + 8, buflen); /* cut off header */
+
+ if (size > buflen)
+ return buflen;
+
+ return size;
+}
+
+/*
+ * 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;
+}
+
+/**
+ * i2o_device_init - Initialize I2O devices
+ *
+ * Registers the I2O device class.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+int i2o_device_init(void)
+{
+ int rc;
+
+ rc = class_register(&i2o_device_class);
+ if (rc)
+ return rc;
+
+ return class_interface_register(&i2o_device_class_interface);
+};
+
+/**
+ * i2o_device_exit - I2O devices exit function
+ *
+ * Unregisters the I2O device class.
+ */
+void i2o_device_exit(void)
+{
+ class_interface_register(&i2o_device_class_interface);
+ class_unregister(&i2o_device_class);
+};
+
+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/message/i2o/driver.c b/drivers/message/i2o/driver.c
new file mode 100644
index 000000000000..91f4edbb2a27
--- /dev/null
+++ b/drivers/message/i2o/driver.c
@@ -0,0 +1,374 @@
+/*
+ * 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 <linux/i2o.h>
+
+/* max_drivers - Maximum I2O drivers (OSMs) which could be registered */
+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 a I2O device class id match 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,
+};
+
+/**
+ * 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;
+
+ pr_debug("i2o: Register driver %s\n", drv->name);
+
+ if (drv->event) {
+ drv->event_queue = create_workqueue(drv->name);
+ if (!drv->event_queue) {
+ printk(KERN_ERR "i2o: Could not initialize event queue "
+ "for driver %s\n", drv->name);
+ return -EFAULT;
+ }
+ pr_debug("i2o: 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) {
+ printk(KERN_ERR "i2o: too many drivers registered, "
+ "increase max_drivers\n");
+ spin_unlock_irqrestore(&i2o_drivers_lock, flags);
+ return -EFAULT;
+ }
+
+ drv->context = i;
+ i2o_drivers[i] = drv;
+
+ spin_unlock_irqrestore(&i2o_drivers_lock, flags);
+
+ pr_debug("i2o: 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)
+ destroy_workqueue(drv->event_queue);
+
+ 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;
+
+ pr_debug("i2o: 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;
+ pr_debug("i2o: 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
+ * @msg: I2O message to be delivered
+ *
+ * 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_message __iomem *msg)
+{
+ struct i2o_driver *drv;
+ u32 context = readl(&msg->u.s.icntxt);
+
+ if (likely(context < i2o_max_drivers)) {
+ spin_lock(&i2o_drivers_lock);
+ drv = i2o_drivers[context];
+ spin_unlock(&i2o_drivers_lock);
+
+ if (unlikely(!drv)) {
+ printk(KERN_WARNING "%s: Spurious reply to unknown "
+ "driver %d\n", c->name, context);
+ return -EIO;
+ }
+
+ if ((readl(&msg->u.head[1]) >> 24) == I2O_CMD_UTIL_EVT_REGISTER) {
+ struct i2o_device *dev, *tmp;
+ struct i2o_event *evt;
+ u16 size;
+ u16 tid;
+
+ tid = readl(&msg->u.head[1]) & 0x1fff;
+
+ pr_debug("%s: event received from device %d\n", c->name,
+ tid);
+
+ /* cut of header from message size (in 32-bit words) */
+ size = (readl(&msg->u.head[0]) >> 16) - 5;
+
+ evt = kmalloc(size * 4 + sizeof(*evt), GFP_ATOMIC);
+ if (!evt)
+ return -ENOMEM;
+ memset(evt, 0, size * 4 + sizeof(*evt));
+
+ evt->size = size;
+ memcpy_fromio(&evt->tcntxt, &msg->u.s.tcntxt,
+ (size + 2) * 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, (void (*)(void *))drv->event,
+ evt);
+ queue_work(drv->event_queue, &evt->work);
+ return 1;
+ }
+
+ if (likely(drv->reply))
+ return drv->reply(c, m, msg);
+ else
+ pr_debug("%s: Reply to driver %s, but no reply function"
+ " defined!\n", c->name, drv->name);
+ return -EIO;
+ } else
+ printk(KERN_WARNING "%s: Spurious reply to unknown driver "
+ "%d\n", c->name, readl(&msg->u.s.icntxt));
+ return -EIO;
+}
+
+/**
+ * i2o_driver_notify_controller_add_all - Send notify of added controller
+ * to all I2O drivers
+ *
+ * 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 to all I2O drivers
+ *
+ * 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 to all
+ * I2O drivers
+ *
+ * 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 to
+ * all I2O drivers
+ *
+ * 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) ||
+ ((i2o_max_drivers ^ (i2o_max_drivers - 1)) !=
+ (2 * i2o_max_drivers - 1))) {
+ printk(KERN_WARNING "i2o: max_drivers set to %d, but must be "
+ ">=2 and <= 64 and a power of 2\n", i2o_max_drivers);
+ i2o_max_drivers = I2O_MAX_DRIVERS;
+ }
+ printk(KERN_INFO "i2o: max drivers = %d\n", i2o_max_drivers);
+
+ i2o_drivers =
+ kmalloc(i2o_max_drivers * sizeof(*i2o_drivers), GFP_KERNEL);
+ if (!i2o_drivers)
+ return -ENOMEM;
+
+ memset(i2o_drivers, 0, i2o_max_drivers * sizeof(*i2o_drivers));
+
+ 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 free driver array.
+ */
+void __exit 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/message/i2o/exec-osm.c b/drivers/message/i2o/exec-osm.c
new file mode 100644
index 000000000000..79c1cbfb8f44
--- /dev/null
+++ b/drivers/message/i2o/exec-osm.c
@@ -0,0 +1,507 @@
+/*
+ * 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@redhat.com>:
+ * 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 <linux/i2o.h>
+#include <linux/delay.h>
+
+#define OSM_NAME "exec-osm"
+
+struct i2o_driver i2o_exec_driver;
+
+static int i2o_exec_lct_notify(struct i2o_controller *c, u32 change_ind);
+
+/* Module internal functions from other sources */
+extern int i2o_device_parse_lct(struct i2o_controller *);
+
+/* 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 __iomem *msg; /* pointer to the reply message */
+ struct list_head list; /* node in global wait list */
+};
+
+/* 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 = kmalloc(sizeof(*wait), GFP_KERNEL);
+ if (!wait)
+ return ERR_PTR(-ENOMEM);
+
+ memset(wait, 0, sizeof(*wait));
+
+ INIT_LIST_HEAD(&wait->list);
+
+ return wait;
+};
+
+/**
+ * i2o_exec_wait_free - Free a i2o_exec_wait struct
+ * @i2o_exec_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
+ * @m: 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 or negative error code on failure.
+ */
+int i2o_msg_post_wait_mem(struct i2o_controller *c, u32 m, unsigned long
+ timeout, struct i2o_dma *dma)
+{
+ DECLARE_WAIT_QUEUE_HEAD(wq);
+ struct i2o_exec_wait *wait;
+ static u32 tcntxt = 0x80000000;
+ struct i2o_message __iomem *msg = c->in_queue.virt + m;
+ int rc = 0;
+
+ wait = i2o_exec_wait_alloc();
+ if (!wait)
+ 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.
+ */
+ writel(i2o_exec_driver.context, &msg->u.s.icntxt);
+ wait->tcntxt = tcntxt++;
+ writel(wait->tcntxt, &msg->u.s.tcntxt);
+
+ /*
+ * 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, m);
+
+ if (!wait->complete) {
+ wait->wq = &wq;
+ /*
+ * we add elements add 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);
+
+ wait_event_interruptible_timeout(wq, wait->complete,
+ timeout * HZ);
+
+ wait->wq = NULL;
+ }
+
+ barrier();
+
+ if (wait->complete) {
+ if (readl(&wait->msg->body[0]) >> 24)
+ rc = readl(&wait->msg->body[0]) & 0xff;
+ i2o_flush_reply(c, wait->m);
+ i2o_exec_wait_free(wait);
+ } 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;
+ }
+
+ 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
+ *
+ * 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 __iomem *msg)
+{
+ struct i2o_exec_wait *wait, *tmp;
+ static spinlock_t lock;
+ int rc = 1;
+ u32 context;
+
+ spin_lock_init(&lock);
+
+ context = readl(&msg->u.s.tcntxt);
+
+ /*
+ * 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.
+ */
+ spin_lock(&lock);
+ list_for_each_entry_safe(wait, tmp, &i2o_exec_wait_list, list) {
+ if (wait->tcntxt == context) {
+ list_del(&wait->list);
+
+ wait->m = m;
+ wait->msg = msg;
+ wait->complete = 1;
+
+ barrier();
+
+ if (wait->wq) {
+ wake_up_interruptible(wait->wq);
+ rc = 0;
+ } else {
+ 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);
+ rc = -1;
+ }
+
+ spin_unlock(&lock);
+
+ return rc;
+ }
+ }
+
+ spin_unlock(&lock);
+
+ pr_debug("%s: Bogus reply in POST WAIT (tr-context: %08x)!\n", c->name,
+ context);
+
+ return -1;
+};
+
+/**
+ * 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);
+
+ i2o_event_register(i2o_dev, &i2o_exec_driver, 0, 0xffffffff);
+
+ i2o_dev->iop->exec = i2o_dev;
+
+ return 0;
+};
+
+/**
+ * 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)
+{
+ i2o_event_register(to_i2o_device(dev), &i2o_exec_driver, 0, 0);
+
+ return 0;
+};
+
+/**
+ * i2o_exec_lct_modified - Called on LCT NOTIFY reply
+ * @c: I2O controller on which the LCT has modified
+ *
+ * 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.
+ */
+static void i2o_exec_lct_modified(struct i2o_controller *c)
+{
+ if (i2o_device_parse_lct(c) == -EAGAIN)
+ i2o_exec_lct_notify(c, 0);
+};
+
+/**
+ * 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)
+{
+ if (le32_to_cpu(msg->u.head[0]) & MSG_FAIL) { // Fail bit is set
+ struct i2o_message __iomem *pmsg; /* preserved message */
+ u32 pm;
+
+ pm = le32_to_cpu(msg->body[3]);
+
+ pmsg = i2o_msg_in_to_virt(c, pm);
+
+ i2o_report_status(KERN_INFO, "i2o_core", msg);
+
+ /* Release the preserved msg by resubmitting it as a NOP */
+ i2o_msg_nop(c, pm);
+
+ /* If reply to i2o_post_wait failed, return causes a timeout */
+ return -1;
+ }
+
+ if (le32_to_cpu(msg->u.s.tcntxt) & 0x80000000)
+ return i2o_msg_post_wait_complete(c, m, msg);
+
+ if ((le32_to_cpu(msg->u.head[1]) >> 24) == I2O_CMD_LCT_NOTIFY) {
+ struct work_struct *work;
+
+ pr_debug("%s: LCT notify received\n", c->name);
+
+ work = kmalloc(sizeof(*work), GFP_ATOMIC);
+ if (!work)
+ return -ENOMEM;
+
+ INIT_WORK(work, (void (*)(void *))i2o_exec_lct_modified, c);
+ queue_work(i2o_exec_driver.event_queue, 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
+ * @evt: Event which occurs
+ *
+ * Handles events send by the Executive device. At the moment does not do
+ * anything useful.
+ */
+static void i2o_exec_event(struct i2o_event *evt)
+{
+ osm_info("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 __iomem *msg;
+ u32 m;
+ int i = 0;
+ int rc = -EAGAIN;
+
+ for (i = 1; i <= I2O_LCT_GET_TRIES; i++) {
+ m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY)
+ return -ETIMEDOUT;
+
+ writel(EIGHT_WORD_MSG_SIZE | SGL_OFFSET_6, &msg->u.head[0]);
+ writel(I2O_CMD_LCT_NOTIFY << 24 | HOST_TID << 12 | ADAPTER_TID,
+ &msg->u.head[1]);
+ writel(0xffffffff, &msg->body[0]);
+ writel(0x00000000, &msg->body[1]);
+ writel(0xd0000000 | c->dlct.len, &msg->body[2]);
+ writel(c->dlct.phys, &msg->body[3]);
+
+ rc = i2o_msg_post_wait(c, m, I2O_TIMEOUT_LCT_GET);
+ if (rc < 0)
+ break;
+
+ rc = i2o_device_parse_lct(c);
+ if (rc != -EAGAIN)
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * 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 __iomem *msg;
+ u32 m;
+
+ dev = &c->pdev->dev;
+
+ if (i2o_dma_realloc(dev, &c->dlct, sb->expected_lct_size, GFP_KERNEL))
+ return -ENOMEM;
+
+ m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY)
+ return -ETIMEDOUT;
+
+ writel(EIGHT_WORD_MSG_SIZE | SGL_OFFSET_6, &msg->u.head[0]);
+ writel(I2O_CMD_LCT_NOTIFY << 24 | HOST_TID << 12 | ADAPTER_TID,
+ &msg->u.head[1]);
+ writel(i2o_exec_driver.context, &msg->u.s.icntxt);
+ writel(0, &msg->u.s.tcntxt); /* FIXME */
+ writel(0xffffffff, &msg->body[0]);
+ writel(change_ind, &msg->body[1]);
+ writel(0xd0000000 | c->dlct.len, &msg->body[2]);
+ writel(c->dlct.phys, &msg->body[3]);
+
+ i2o_msg_post(c, m);
+
+ return 0;
+};
+
+/* 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 __exit 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/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c
new file mode 100644
index 000000000000..7b74c87b569e
--- /dev/null
+++ b/drivers/message/i2o/i2o_block.c
@@ -0,0 +1,1247 @@
+/*
+ * 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/i2o.h>
+
+#include <linux/mempool.h>
+
+#include <linux/genhd.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+
+#include "i2o_block.h"
+
+#define OSM_NAME "block-osm"
+#define OSM_VERSION "$Rev$"
+#define OSM_DESCRIPTION "I2O Block Device OSM"
+
+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 %s\n", 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 __iomem *msg;
+ u32 m;
+
+ m = i2o_msg_get_wait(dev->iop, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY)
+ return -ETIMEDOUT;
+
+ writel(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]);
+ writel(I2O_CMD_BLOCK_CFLUSH << 24 | HOST_TID << 12 | dev->lct_data.tid,
+ &msg->u.head[1]);
+ writel(60 << 16, &msg->body[0]);
+ osm_debug("Flushing...\n");
+
+ return i2o_msg_post_wait(dev->iop, m, 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 __iomem *msg;
+ u32 m;
+
+ m = i2o_msg_get_wait(dev->iop, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY)
+ return -ETIMEDOUT;
+
+ writel(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]);
+ writel(I2O_CMD_BLOCK_MMOUNT << 24 | HOST_TID << 12 | dev->lct_data.tid,
+ &msg->u.head[1]);
+ writel(-1, &msg->body[0]);
+ writel(0, &msg->body[1]);
+ osm_debug("Mounting...\n");
+
+ return i2o_msg_post_wait(dev->iop, m, 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 __iomem *msg;
+ u32 m;
+
+ m = i2o_msg_get_wait(dev->iop, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY)
+ return -ETIMEDOUT;
+
+ writel(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]);
+ writel(I2O_CMD_BLOCK_MLOCK << 24 | HOST_TID << 12 | dev->lct_data.tid,
+ &msg->u.head[1]);
+ writel(-1, &msg->body[0]);
+ osm_debug("Locking...\n");
+
+ return i2o_msg_post_wait(dev->iop, m, 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 __iomem *msg;
+ u32 m;
+
+ m = i2o_msg_get_wait(dev->iop, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY)
+ return -ETIMEDOUT;
+
+ writel(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]);
+ writel(I2O_CMD_BLOCK_MUNLOCK << 24 | HOST_TID << 12 | dev->lct_data.tid,
+ &msg->u.head[1]);
+ writel(media_id, &msg->body[0]);
+ osm_debug("Unlocking...\n");
+
+ return i2o_msg_post_wait(dev->iop, m, 2);
+};
+
+/**
+ * i2o_block_device_power - Power management for device dev
+ * @dev: I2O device which should receive the power management request
+ * @operation: Operation which should be 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 __iomem *msg;
+ u32 m;
+ int rc;
+
+ m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY)
+ return -ETIMEDOUT;
+
+ writel(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]);
+ writel(I2O_CMD_BLOCK_POWER << 24 | HOST_TID << 12 | i2o_dev->lct_data.
+ tid, &msg->u.head[1]);
+ writel(op << 24, &msg->body[0]);
+ osm_debug("Power...\n");
+
+ rc = i2o_msg_post_wait(c, m, 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);
+
+ return ireq;
+};
+
+/**
+ * i2o_block_request_free - Frees a I2O block request
+ * @ireq: I2O block request which should be freed
+ *
+ * Fres 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
+ * @ireq: I2O block request
+ *
+ * Builds the SG list and map it into to be accessable by the controller.
+ *
+ * Returns the number of elements in the SG list or 0 on failure.
+ */
+static inline int i2o_block_sglist_alloc(struct i2o_block_request *ireq)
+{
+ struct device *dev = &ireq->i2o_blk_dev->i2o_dev->iop->pdev->dev;
+ int nents;
+
+ nents = blk_rq_map_sg(ireq->req->q, ireq->req, ireq->sg_table);
+
+ if (rq_data_dir(ireq->req) == READ)
+ ireq->sg_dma_direction = PCI_DMA_FROMDEVICE;
+ else
+ ireq->sg_dma_direction = PCI_DMA_TODEVICE;
+
+ ireq->sg_nents = dma_map_sg(dev, ireq->sg_table, nents,
+ ireq->sg_dma_direction);
+
+ return ireq->sg_nents;
+};
+
+/**
+ * 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)
+{
+ struct device *dev = &ireq->i2o_blk_dev->i2o_dev->iop->pdev->dev;
+
+ dma_unmap_sg(dev, ireq->sg_table, ireq->sg_nents,
+ ireq->sg_dma_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 loose 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;
+
+ /* request is already processed by us, so return */
+ if (req->flags & REQ_SPECIAL) {
+ osm_debug("REQ_SPECIAL already set!\n");
+ req->flags |= REQ_DONTPREP;
+ return BLKPREP_OK;
+ }
+
+ /* connect the i2o_block_request to the request */
+ if (!req->special) {
+ ireq = i2o_block_request_alloc();
+ if (unlikely(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;
+ } else
+ ireq = req->special;
+
+ /* do not come back here */
+ req->flags |= REQ_DONTPREP | REQ_SPECIAL;
+
+ return BLKPREP_OK;
+};
+
+/**
+ * i2o_block_delayed_request_fn - delayed request queue function
+ * delayed_request: 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(void *delayed_request)
+{
+ struct i2o_block_delayed_request *dreq = delayed_request;
+ 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_reply - Block OSM reply handler.
+ * @c: I2O controller from which the message arrives
+ * @m: message id of reply
+ * qmsg: the actuall 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 i2o_block_request *ireq;
+ struct request *req;
+ struct i2o_block_device *dev;
+ struct request_queue *q;
+ u8 st;
+ unsigned long flags;
+
+ /* FAILed message */
+ if (unlikely(le32_to_cpu(msg->u.head[0]) & (1 << 13))) {
+ struct i2o_message *pmsg;
+ u32 pm;
+
+ /*
+ * FAILed message from controller
+ * We increment the error count and abort it
+ *
+ * In theory this will never happen. The I2O block class
+ * specification states that block devices never return
+ * FAILs but instead use the REQ status field...but
+ * better be on the safe side since no one really follows
+ * the spec to the book :)
+ */
+ pm = le32_to_cpu(msg->body[3]);
+ pmsg = i2o_msg_in_to_virt(c, pm);
+
+ req = i2o_cntxt_list_get(c, le32_to_cpu(pmsg->u.s.tcntxt));
+ if (unlikely(!req)) {
+ osm_err("NULL reply received!\n");
+ return -1;
+ }
+
+ ireq = req->special;
+ dev = ireq->i2o_blk_dev;
+ q = dev->gd->queue;
+
+ req->errors++;
+
+ spin_lock_irqsave(q->queue_lock, flags);
+
+ while (end_that_request_chunk(req, !req->errors,
+ le32_to_cpu(pmsg->body[1]))) ;
+ end_that_request_last(req);
+
+ dev->open_queue_depth--;
+ list_del(&ireq->queue);
+ blk_start_queue(q);
+
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ /* Now flush the message by making it a NOP */
+ i2o_msg_nop(c, pm);
+
+ return -1;
+ }
+
+ req = i2o_cntxt_list_get(c, le32_to_cpu(msg->u.s.tcntxt));
+ if (unlikely(!req)) {
+ osm_err("NULL reply received!\n");
+ return -1;
+ }
+
+ ireq = req->special;
+ dev = ireq->i2o_blk_dev;
+ q = dev->gd->queue;
+
+ if (unlikely(!dev->i2o_dev)) {
+ /*
+ * This is HACK, but Intel Integrated RAID allows user
+ * to delete a volume that is claimed, locked, and in use
+ * by the OS. We have to check for a reply from a
+ * non-existent device and flag it as an error or the system
+ * goes kaput...
+ */
+ req->errors++;
+ osm_warn("Data transfer to deleted device!\n");
+ spin_lock_irqsave(q->queue_lock, flags);
+ while (end_that_request_chunk
+ (req, !req->errors, le32_to_cpu(msg->body[1]))) ;
+ end_that_request_last(req);
+
+ dev->open_queue_depth--;
+ list_del(&ireq->queue);
+ blk_start_queue(q);
+
+ spin_unlock_irqrestore(q->queue_lock, flags);
+ return -1;
+ }
+
+ /*
+ * Lets see what is cooking. We stuffed the
+ * request in the context.
+ */
+
+ st = le32_to_cpu(msg->body[0]) >> 24;
+
+ if (st != 0) {
+ int err;
+ char *bsa_errors[] = {
+ "Success",
+ "Media Error",
+ "Failure communicating to device",
+ "Device Failure",
+ "Device is not ready",
+ "Media not present",
+ "Media is locked by another user",
+ "Media has failed",
+ "Failure communicating to device",
+ "Device bus failure",
+ "Device is locked by another user",
+ "Device is write protected",
+ "Device has reset",
+ "Volume has changed, waiting for acknowledgement"
+ };
+
+ err = le32_to_cpu(msg->body[0]) & 0xffff;
+
+ /*
+ * 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("block-osm: /dev/%s error: %s", dev->gd->disk_name,
+ bsa_errors[le32_to_cpu(msg->body[0]) & 0xffff]);
+ if (le32_to_cpu(msg->body[0]) & 0x00ff0000)
+ printk(KERN_ERR " - DDM attempted %d retries",
+ (le32_to_cpu(msg->body[0]) >> 16) & 0x00ff);
+ printk(KERN_ERR ".\n");
+ req->errors++;
+ } else
+ req->errors = 0;
+
+ if (!end_that_request_chunk
+ (req, !req->errors, le32_to_cpu(msg->body[1]))) {
+ add_disk_randomness(req->rq_disk);
+ spin_lock_irqsave(q->queue_lock, flags);
+
+ end_that_request_last(req);
+
+ 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);
+ } else
+ osm_err("still remaining chunks\n");
+
+ return 1;
+};
+
+static void i2o_block_event(struct i2o_event *evt)
+{
+ osm_info("block-osm: event received\n");
+};
+
+/*
+ * 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
+ *
+ * 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 inode *inode, struct file *file)
+{
+ struct i2o_block_device *dev = inode->i_bdev->bd_disk->private_data;
+
+ if (!dev->i2o_dev)
+ return -ENODEV;
+
+ 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");
+
+ return 0;
+};
+
+/**
+ * i2o_block_release - Release the I2O block device
+ *
+ * Unlock and unmount the media, and power down the device. Gets called if
+ * the block device is closed.
+ *
+ * Returns 0 on success or negative error code on failure.
+ */
+static int i2o_block_release(struct inode *inode, struct file *file)
+{
+ struct gendisk *disk = inode->i_bdev->bd_disk;
+ struct i2o_block_device *dev = disk->private_data;
+ u8 operation;
+
+ /*
+ * This is to deail with the case of an application
+ * opening a device and then the device dissapears 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 0;
+
+ 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);
+
+ return 0;
+}
+
+/**
+ * i2o_block_ioctl - Issue device specific ioctl calls.
+ * @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 inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct gendisk *disk = inode->i_bdev->bd_disk;
+ struct i2o_block_device *dev = disk->private_data;
+ void __user *argp = (void __user *)arg;
+
+ /* Anyone capable of this syscall can do *real bad* things */
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ switch (cmd) {
+ case HDIO_GETGEO:
+ {
+ struct hd_geometry g;
+ i2o_block_biosparam(get_capacity(disk),
+ &g.cylinders, &g.heads, &g.sectors);
+ g.start = get_start_sect(inode->i_bdev);
+ return copy_to_user(argp, &g, sizeof(g)) ? -EFAULT : 0;
+ }
+
+ case BLKI2OGRSTRAT:
+ return put_user(dev->rcache, (int __user *)arg);
+ case BLKI2OGWSTRAT:
+ return put_user(dev->wcache, (int __user *)arg);
+ case BLKI2OSRSTRAT:
+ if (arg < 0 || arg > CACHE_SMARTFETCH)
+ return -EINVAL;
+ dev->rcache = arg;
+ break;
+ case BLKI2OSWSTRAT:
+ if (arg != 0
+ && (arg < CACHE_WRITETHROUGH || arg > CACHE_SMARTBACK))
+ return -EINVAL;
+ dev->wcache = arg;
+ break;
+ }
+ return -ENOTTY;
+};
+
+/**
+ * i2o_block_media_changed - Have we seen a media change?
+ * @disk: gendisk which should be verified
+ *
+ * Verifies if the media has changed.
+ *
+ * Returns 1 if the media was changed or 0 otherwise.
+ */
+static int i2o_block_media_changed(struct gendisk *disk)
+{
+ struct i2o_block_device *p = disk->private_data;
+
+ if (p->media_change_flag) {
+ p->media_change_flag = 0;
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * i2o_block_transfer - Transfer a request to/from the I2O controller
+ * @req: the request which should be transfered
+ *
+ * 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 = dev->i2o_dev->iop;
+ int tid = dev->i2o_dev->lct_data.tid;
+ struct i2o_message __iomem *msg;
+ void __iomem *mptr;
+ struct i2o_block_request *ireq = req->special;
+ struct scatterlist *sg;
+ int sgnum;
+ int i;
+ u32 m;
+ u32 tcntxt;
+ u32 sg_flags;
+ int rc;
+
+ m = i2o_msg_get(c, &msg);
+ if (m == I2O_QUEUE_EMPTY) {
+ rc = -EBUSY;
+ goto exit;
+ }
+
+ tcntxt = i2o_cntxt_list_add(c, req);
+ if (!tcntxt) {
+ rc = -ENOMEM;
+ goto nop_msg;
+ }
+
+ if ((sgnum = i2o_block_sglist_alloc(ireq)) <= 0) {
+ rc = -ENOMEM;
+ goto context_remove;
+ }
+
+ /* Build the message based on the request. */
+ writel(i2o_block_driver.context, &msg->u.s.icntxt);
+ writel(tcntxt, &msg->u.s.tcntxt);
+ writel(req->nr_sectors << 9, &msg->body[1]);
+
+ writel((((u64) req->sector) << 9) & 0xffffffff, &msg->body[2]);
+ writel(req->sector >> 23, &msg->body[3]);
+
+ mptr = &msg->body[4];
+
+ sg = ireq->sg_table;
+
+ if (rq_data_dir(req) == READ) {
+ writel(I2O_CMD_BLOCK_READ << 24 | HOST_TID << 12 | tid,
+ &msg->u.head[1]);
+ sg_flags = 0x10000000;
+ switch (dev->rcache) {
+ case CACHE_NULL:
+ writel(0, &msg->body[0]);
+ break;
+ case CACHE_PREFETCH:
+ writel(0x201F0008, &msg->body[0]);
+ break;
+ case CACHE_SMARTFETCH:
+ if (req->nr_sectors > 16)
+ writel(0x201F0008, &msg->body[0]);
+ else
+ writel(0x001F0000, &msg->body[0]);
+ break;
+ }
+ } else {
+ writel(I2O_CMD_BLOCK_WRITE << 24 | HOST_TID << 12 | tid,
+ &msg->u.head[1]);
+ sg_flags = 0x14000000;
+ switch (dev->wcache) {
+ case CACHE_NULL:
+ writel(0, &msg->body[0]);
+ break;
+ case CACHE_WRITETHROUGH:
+ writel(0x001F0008, &msg->body[0]);
+ break;
+ case CACHE_WRITEBACK:
+ writel(0x001F0010, &msg->body[0]);
+ break;
+ case CACHE_SMARTBACK:
+ if (req->nr_sectors > 16)
+ writel(0x001F0004, &msg->body[0]);
+ else
+ writel(0x001F0010, &msg->body[0]);
+ break;
+ case CACHE_SMARTTHROUGH:
+ if (req->nr_sectors > 16)
+ writel(0x001F0004, &msg->body[0]);
+ else
+ writel(0x001F0010, &msg->body[0]);
+ }
+ }
+
+ for (i = sgnum; i > 0; i--) {
+ if (i == 1)
+ sg_flags |= 0x80000000;
+ writel(sg_flags | sg_dma_len(sg), mptr);
+ writel(sg_dma_address(sg), mptr + 4);
+ mptr += 8;
+ sg++;
+ }
+
+ writel(I2O_MESSAGE_SIZE
+ (((unsigned long)mptr -
+ (unsigned long)&msg->u.head[0]) >> 2) | SGL_OFFSET_8,
+ &msg->u.head[0]);
+
+ list_add_tail(&ireq->queue, &dev->open_queue);
+ dev->open_queue_depth++;
+
+ i2o_msg_post(c, m);
+
+ return 0;
+
+ context_remove:
+ i2o_cntxt_list_remove(c, req);
+
+ nop_msg:
+ i2o_msg_nop(c, m);
+
+ 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 (!blk_queue_plugged(q)) {
+ req = elv_next_request(q);
+ if (!req)
+ break;
+
+ if (blk_fs_request(req)) {
+ 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)) {
+ blkdev_dequeue_request(req);
+ continue;
+ }
+
+ if (queue_depth)
+ break;
+
+ /* stop the queue and retry later */
+ dreq = kmalloc(sizeof(*dreq), GFP_ATOMIC);
+ if (!dreq)
+ continue;
+
+ dreq->queue = q;
+ INIT_WORK(&dreq->work, i2o_block_delayed_request_fn,
+ dreq);
+
+ osm_info("transfer error\n");
+ if (!queue_delayed_work(i2o_block_driver.event_queue,
+ &dreq->work,
+ I2O_BLOCK_RETRY_TIME))
+ kfree(dreq);
+ else {
+ blk_stop_queue(q);
+ break;
+ }
+ } else
+ end_request(req, 0);
+ }
+};
+
+/* I2O Block device operations definition */
+static struct block_device_operations i2o_block_fops = {
+ .owner = THIS_MODULE,
+ .open = i2o_block_open,
+ .release = i2o_block_release,
+ .ioctl = i2o_block_ioctl,
+ .media_changed = i2o_block_media_changed
+};
+
+/**
+ * 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 succes 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 = kmalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ osm_err("Insufficient memory to allocate I2O Block disk.\n");
+ rc = -ENOMEM;
+ goto exit;
+ }
+ memset(dev, 0, sizeof(*dev));
+
+ 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_block_device *i2o_blk_dev;
+ struct i2o_controller *c = i2o_dev->iop;
+ struct gendisk *gd;
+ struct request_queue *queue;
+ static int unit = 0;
+ int rc;
+ u64 size;
+ u32 blocksize;
+ u16 power;
+ u32 flags, status;
+ int segments;
+
+ /* 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;
+ }
+
+ osm_info("New device detected (TID: %03x)\n", i2o_dev->lct_data.tid);
+
+ 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);
+ sprintf(gd->devfs_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_phys_segments(queue, I2O_MAX_SEGMENTS);
+ blk_queue_max_sectors(queue, I2O_MAX_SECTORS);
+
+ if (c->short_req)
+ segments = 8;
+ else {
+ i2o_status_block *sb;
+
+ sb = c->status_block.virt;
+
+ segments = (sb->inbound_frame_size -
+ sizeof(struct i2o_message) / 4 - 4) / 2;
+ }
+
+ blk_queue_max_hw_segments(queue, segments);
+
+ osm_debug("max sectors = %d\n", I2O_MAX_SECTORS);
+ osm_debug("phys segments = %d\n", I2O_MAX_SEGMENTS);
+ osm_debug("hw segments = %d\n", 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) != 0
+ || i2o_parm_field_get(i2o_dev, 0x0004, 0, &size, 8) != 0) {
+ i2o_parm_field_get(i2o_dev, 0x0000, 3, &blocksize, 4);
+ i2o_parm_field_get(i2o_dev, 0x0000, 4, &size, 8);
+ }
+ osm_debug("blocksize = %d\n", blocksize);
+
+ if (i2o_parm_field_get(i2o_dev, 0x0000, 2, &power, 2))
+ power = 0;
+ i2o_parm_field_get(i2o_dev, 0x0000, 5, &flags, 4);
+ i2o_parm_field_get(i2o_dev, 0x0000, 6, &status, 4);
+
+ set_capacity(gd, size >> 9);
+
+ i2o_event_register(i2o_dev, &i2o_block_driver, 0, 0xffffffff);
+
+ add_disk(gd);
+
+ unit++;
+
+ 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,
+ 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(I2O_REQ_MEMPOOL_SIZE,
+ mempool_alloc_slab,
+ mempool_free_slab,
+ 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/message/i2o/i2o_block.h b/drivers/message/i2o/i2o_block.h
new file mode 100644
index 000000000000..ddd9a15679c0
--- /dev/null
+++ b/drivers/message/i2o/i2o_block.h
@@ -0,0 +1,99 @@
+/*
+ * 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
+
+/* I2O Block OSM mempool struct */
+struct i2o_block_mempool {
+ kmem_cache_t *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 transfered, 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;
+ int 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 */
+ int sg_dma_direction; /* direction of DMA buffer read/write */
+ int sg_nents; /* number of SG elements */
+ struct scatterlist sg_table[I2O_MAX_SEGMENTS]; /* SG table */
+};
+
+/* I2O Block device delayed request */
+struct i2o_block_delayed_request
+{
+ struct work_struct work;
+ struct request_queue *queue;
+};
+
+#endif
diff --git a/drivers/message/i2o/i2o_config.c b/drivers/message/i2o/i2o_config.c
new file mode 100644
index 000000000000..5fc5004ea07a
--- /dev/null
+++ b/drivers/message/i2o/i2o_config.c
@@ -0,0 +1,1160 @@
+/*
+ * 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@redhat.com>:
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/i2o.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/miscdevice.h>
+#include <linux/mm.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+#include <linux/ioctl32.h>
+#include <linux/compat.h>
+#include <linux/syscalls.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#define OSM_NAME "config-osm"
+#define OSM_VERSION "$Rev$"
+#define OSM_DESCRIPTION "I2O Configuration OSM"
+
+extern int i2o_parm_issue(struct i2o_device *, int, void *, int, void *, int);
+
+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;
+
+/*
+ * Each of these describes an i2o message handler. They are
+ * multiplexed by the i2o_core code
+ */
+
+static struct i2o_driver i2o_config_driver = {
+ .name = OSM_NAME
+};
+
+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);
+
+ /* We did a get user...so assuming mem is ok...is this bad? */
+ put_user(len, kcmd.reslen);
+ if (len > reslen)
+ ret = -ENOBUFS;
+ 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;
+ put_user(len, kcmd.reslen);
+ 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;
+
+ ops = (u8 *) kmalloc(kcmd.oplen, GFP_KERNEL);
+ if (!ops)
+ return -ENOMEM;
+
+ if (copy_from_user(ops, kcmd.opbuf, kcmd.oplen)) {
+ kfree(ops);
+ return -EFAULT;
+ }
+
+ /*
+ * It's possible to have a _very_ large table
+ * and that the user asks for all of it at once...
+ */
+ res = (u8 *) 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;
+ }
+
+ put_user(len, kcmd.reslen);
+ 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 __iomem *msg;
+ u32 m;
+ 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;
+
+ m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY)
+ return -EBUSY;
+
+ if (i2o_dma_alloc(&c->pdev->dev, &buffer, fragsize, GFP_KERNEL)) {
+ i2o_msg_nop(c, m);
+ return -ENOMEM;
+ }
+
+ __copy_from_user(buffer.virt, kxfer.buf, fragsize);
+
+ writel(NINE_WORD_MSG_SIZE | SGL_OFFSET_7, &msg->u.head[0]);
+ writel(I2O_CMD_SW_DOWNLOAD << 24 | HOST_TID << 12 | ADAPTER_TID,
+ &msg->u.head[1]);
+ writel(i2o_config_driver.context, &msg->u.head[2]);
+ writel(0, &msg->u.head[3]);
+ writel((((u32) kxfer.flags) << 24) | (((u32) kxfer.sw_type) << 16) |
+ (((u32) maxfrag) << 8) | (((u32) curfrag)), &msg->body[0]);
+ writel(swlen, &msg->body[1]);
+ writel(kxfer.sw_id, &msg->body[2]);
+ writel(0xD0000000 | fragsize, &msg->body[3]);
+ writel(buffer.phys, &msg->body[4]);
+
+ osm_debug("swdl frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize);
+ status = i2o_msg_post_wait_mem(c, m, 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 __iomem *msg;
+ u32 m;
+ 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)))
+ goto return_fault;
+
+ if (get_user(swlen, kxfer.swlen) < 0)
+ goto return_fault;
+
+ if (get_user(maxfrag, kxfer.maxfrag) < 0)
+ goto return_fault;
+
+ if (get_user(curfrag, kxfer.curfrag) < 0)
+ goto return_fault;
+
+ if (curfrag == maxfrag)
+ fragsize = swlen - (maxfrag - 1) * 8192;
+
+ if (!kxfer.buf)
+ goto return_fault;
+
+ c = i2o_find_iop(kxfer.iop);
+ if (!c)
+ return -ENXIO;
+
+ m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY)
+ return -EBUSY;
+
+ if (i2o_dma_alloc(&c->pdev->dev, &buffer, fragsize, GFP_KERNEL)) {
+ i2o_msg_nop(c, m);
+ return -ENOMEM;
+ }
+
+ writel(NINE_WORD_MSG_SIZE | SGL_OFFSET_7, &msg->u.head[0]);
+ writel(I2O_CMD_SW_UPLOAD << 24 | HOST_TID << 12 | ADAPTER_TID,
+ &msg->u.head[1]);
+ writel(i2o_config_driver.context, &msg->u.head[2]);
+ writel(0, &msg->u.head[3]);
+ writel((u32) kxfer.flags << 24 | (u32) kxfer.
+ sw_type << 16 | (u32) maxfrag << 8 | (u32) curfrag,
+ &msg->body[0]);
+ writel(swlen, &msg->body[1]);
+ writel(kxfer.sw_id, &msg->body[2]);
+ writel(0xD0000000 | fragsize, &msg->body[3]);
+ writel(buffer.phys, &msg->body[4]);
+
+ osm_debug("swul frag %d/%d (size %d)\n", curfrag, maxfrag, fragsize);
+ status = i2o_msg_post_wait_mem(c, m, 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:
+ return ret;
+return_fault:
+ ret = -EFAULT;
+ goto 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 __iomem *msg;
+ u32 m;
+ 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;
+
+ m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY)
+ return -EBUSY;
+
+ writel(SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]);
+ writel(I2O_CMD_SW_REMOVE << 24 | HOST_TID << 12 | ADAPTER_TID,
+ &msg->u.head[1]);
+ writel(i2o_config_driver.context, &msg->u.head[2]);
+ writel(0, &msg->u.head[3]);
+ writel((u32) kxfer.flags << 24 | (u32) kxfer.sw_type << 16,
+ &msg->body[0]);
+ writel(swlen, &msg->body[1]);
+ writel(kxfer.sw_id, &msg->body[2]);
+
+ token = i2o_msg_post_wait(c, m, 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 __iomem *msg;
+ u32 m;
+ struct i2o_controller *c;
+
+ c = i2o_find_iop(iop);
+ if (!c)
+ return -ENXIO;
+
+ m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY)
+ return -EBUSY;
+
+ writel(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]);
+ writel(I2O_CMD_CONFIG_VALIDATE << 24 | HOST_TID << 12 | iop,
+ &msg->u.head[1]);
+ writel(i2o_config_driver.context, &msg->u.head[2]);
+ writel(0, &msg->u.head[3]);
+
+ token = i2o_msg_post_wait(c, m, 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 __iomem *msg;
+ u32 m;
+ 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;
+
+ m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY)
+ return -EBUSY;
+
+ writel(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]);
+ writel(I2O_CMD_UTIL_EVT_REGISTER << 24 | HOST_TID << 12 | kdesc.tid,
+ &msg->u.head[1]);
+ writel(i2o_config_driver.context, &msg->u.head[2]);
+ writel(i2o_cntxt_list_add(c, fp->private_data), &msg->u.head[3]);
+ writel(kdesc.evt_mask, &msg->body[0]);
+
+ i2o_msg_post(c, m);
+
+ 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(unsigned fd, unsigned cmnd, unsigned long arg,
+ struct file *file)
+{
+ 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;
+ i2o_status_block *sb;
+ struct i2o_message *msg;
+ u32 m;
+ 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;
+ }
+
+ m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
+
+ 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
+
+ /* Copy in the user's I2O command */
+ if (copy_from_user(msg, user_msg, size)) {
+ osm_warn("unable to copy user message\n");
+ return -EFAULT;
+ }
+ i2o_dump_message(msg);
+
+ if (get_user(reply_size, &user_reply[0]) < 0)
+ return -EFAULT;
+
+ reply_size >>= 16;
+ reply_size <<= 2;
+
+ reply = kmalloc(reply_size, GFP_KERNEL);
+ if (!reply) {
+ printk(KERN_WARNING "%s: Could not allocate reply buffer\n",
+ c->name);
+ return -ENOMEM;
+ }
+ memset(reply, 0, reply_size);
+
+ sg_offset = (msg->u.head[0] >> 4) & 0x0f;
+
+ writel(i2o_config_driver.context, &msg->u.s.icntxt);
+ writel(i2o_cntxt_list_add(c, reply), &msg->u.s.tcntxt);
+
+ 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);
+ kfree(reply);
+ return -EINVAL;
+ }
+
+ 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[i]);
+ /* Allocate memory for the transfer */
+ if (i2o_dma_alloc
+ (&c->pdev->dev, p, sg_size,
+ PCI_DMA_BIDIRECTIONAL)) {
+ 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 cleanup;
+ }
+ /* 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 cleanup;
+ }
+ }
+ //TODO 64bit fix
+ sg[i].addr_bus = (u32) p->phys;
+ }
+ }
+
+ rcode = i2o_msg_post_wait(c, m, 60);
+ if (rcode)
+ goto cleanup;
+
+ if (sg_offset) {
+ u32 msg[128];
+ /* 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(&msg, 0, MSG_FRAME_SIZE * 4);
+ // get user msg size in u32s
+ if (get_user(size, &user_msg[0])) {
+ rcode = -EFAULT;
+ goto cleanup;
+ }
+ size = size >> 16;
+ size *= 4;
+ /* Copy in the user's I2O command */
+ if (copy_from_user(msg, user_msg, size)) {
+ rcode = -EFAULT;
+ goto cleanup;
+ }
+ sg_count =
+ (size - sg_offset * 4) / sizeof(struct sg_simple_element);
+
+ // TODO 64bit fix
+ sg = (struct sg_simple_element *)(msg + 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 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;
+ }
+ }
+
+ cleanup:
+ kfree(reply);
+ return rcode;
+}
+
+#else
+
+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;
+ void *sg_list[SG_TABLESIZE];
+ u32 sg_offset = 0;
+ u32 sg_count = 0;
+ int sg_index = 0;
+ u32 i = 0;
+ void *p = NULL;
+ i2o_status_block *sb;
+ struct i2o_message __iomem *msg;
+ u32 m;
+ 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;
+ }
+
+ m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
+
+ 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
+
+ /* Copy in the user's I2O command */
+ if (copy_from_user(msg, user_msg, size))
+ return -EFAULT;
+
+ if (get_user(reply_size, &user_reply[0]) < 0)
+ return -EFAULT;
+
+ reply_size >>= 16;
+ reply_size <<= 2;
+
+ reply = kmalloc(reply_size, GFP_KERNEL);
+ if (!reply) {
+ printk(KERN_WARNING "%s: Could not allocate reply buffer\n",
+ c->name);
+ return -ENOMEM;
+ }
+ memset(reply, 0, reply_size);
+
+ sg_offset = (msg->u.head[0] >> 4) & 0x0f;
+
+ writel(i2o_config_driver.context, &msg->u.s.icntxt);
+ writel(i2o_cntxt_list_add(c, reply), &msg->u.s.tcntxt);
+
+ 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);
+ kfree(reply);
+ return -EINVAL;
+ }
+
+ 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 cleanup;
+ }
+ sg_size = sg[i].flag_count & 0xffffff;
+ /* Allocate memory for the transfer */
+ p = kmalloc(sg_size, GFP_KERNEL);
+ if (!p) {
+ 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 cleanup;
+ }
+ sg_list[sg_index++] = p; // sglist indexed with input frame, not our internal frame.
+ /* 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, (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 cleanup;
+ }
+ }
+ //TODO 64bit fix
+ sg[i].addr_bus = virt_to_bus(p);
+ }
+ }
+
+ rcode = i2o_msg_post_wait(c, m, 60);
+ if (rcode)
+ goto cleanup;
+
+ if (sg_offset) {
+ u32 msg[128];
+ /* 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(&msg, 0, MSG_FRAME_SIZE * 4);
+ // get user msg size in u32s
+ if (get_user(size, &user_msg[0])) {
+ rcode = -EFAULT;
+ goto cleanup;
+ }
+ size = size >> 16;
+ size *= 4;
+ /* Copy in the user's I2O command */
+ if (copy_from_user(msg, user_msg, size)) {
+ rcode = -EFAULT;
+ goto cleanup;
+ }
+ sg_count =
+ (size - sg_offset * 4) / sizeof(struct sg_simple_element);
+
+ // TODO 64bit fix
+ sg = (struct sg_simple_element *)(msg + 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],
+ sg_size)) {
+ printk(KERN_WARNING
+ "%s: Could not copy %p TO user %x\n",
+ c->name, sg_list[j],
+ sg[j].addr_bus);
+ rcode = -EFAULT;
+ goto 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;
+ }
+ }
+
+ cleanup:
+ kfree(reply);
+ return rcode;
+}
+#endif
+
+/*
+ * IOCTL Handler
+ */
+static int i2o_cfg_ioctl(struct inode *inode, struct file *fp, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret;
+
+ 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;
+
+#ifndef CONFIG_COMPAT
+ case I2OPASSTHRU:
+ ret = i2o_cfg_passthru(arg);
+ break;
+#endif
+
+ default:
+ osm_debug("unknown ioctl called!\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int cfg_open(struct inode *inode, struct file *file)
+{
+ struct i2o_cfg_info *tmp =
+ (struct i2o_cfg_info *)kmalloc(sizeof(struct i2o_cfg_info),
+ GFP_KERNEL);
+ unsigned long flags;
+
+ if (!tmp)
+ return -ENOMEM;
+
+ 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);
+
+ return 0;
+}
+
+static int cfg_fasync(int fd, struct file *fp, int on)
+{
+ ulong id = (ulong) fp->private_data;
+ struct i2o_cfg_info *p;
+
+ for (p = open_files; p; p = p->next)
+ if (p->q_id == id)
+ break;
+
+ if (!p)
+ return -EBADF;
+
+ return fasync_helper(fd, fp, on, &p->fasync);
+}
+
+static int cfg_release(struct inode *inode, struct file *file)
+{
+ ulong id = (ulong) file->private_data;
+ struct i2o_cfg_info *p1, *p2;
+ unsigned long flags;
+
+ lock_kernel();
+ p1 = p2 = NULL;
+
+ spin_lock_irqsave(&i2o_config_lock, flags);
+ for (p1 = open_files; p1;) {
+ if (p1->q_id == id) {
+
+ if (p1->fasync)
+ cfg_fasync(-1, file, 0);
+ if (p2)
+ p2->next = p1->next;
+ else
+ open_files = p1->next;
+
+ kfree(p1);
+ break;
+ }
+ p2 = p1;
+ p1 = p1->next;
+ }
+ spin_unlock_irqrestore(&i2o_config_lock, flags);
+ unlock_kernel();
+
+ return 0;
+}
+
+static struct file_operations config_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .ioctl = i2o_cfg_ioctl,
+ .open = cfg_open,
+ .release = cfg_release,
+ .fasync = cfg_fasync,
+};
+
+static struct miscdevice i2o_miscdev = {
+ I2O_MINOR,
+ "i2octl",
+ &config_fops
+};
+
+static int __init i2o_config_init(void)
+{
+ printk(KERN_INFO OSM_DESCRIPTION " v" OSM_VERSION "\n");
+
+ spin_lock_init(&i2o_config_lock);
+
+ if (misc_register(&i2o_miscdev) < 0) {
+ osm_err("can't register device.\n");
+ return -EBUSY;
+ }
+ /*
+ * Install our handler
+ */
+ if (i2o_driver_register(&i2o_config_driver)) {
+ osm_err("handler register failed.\n");
+ misc_deregister(&i2o_miscdev);
+ return -EBUSY;
+ }
+#ifdef CONFIG_COMPAT
+ register_ioctl32_conversion(I2OPASSTHRU32, i2o_cfg_passthru32);
+ register_ioctl32_conversion(I2OGETIOPS, (void *)sys_ioctl);
+#endif
+ return 0;
+}
+
+static void i2o_config_exit(void)
+{
+#ifdef CONFIG_COMPAT
+ unregister_ioctl32_conversion(I2OPASSTHRU32);
+ unregister_ioctl32_conversion(I2OGETIOPS);
+#endif
+ misc_deregister(&i2o_miscdev);
+ i2o_driver_unregister(&i2o_config_driver);
+}
+
+MODULE_AUTHOR("Red Hat Software");
+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/message/i2o/i2o_lan.h b/drivers/message/i2o/i2o_lan.h
new file mode 100644
index 000000000000..561d63304d7e
--- /dev/null
+++ b/drivers/message/i2o/i2o_lan.h
@@ -0,0 +1,159 @@
+/*
+ * i2o_lan.h I2O LAN Class definitions
+ *
+ * I2O LAN CLASS OSM May 26th 2000
+ *
+ * (C) Copyright 1999, 2000 University of Helsinki,
+ * Department of Computer Science
+ *
+ * This code is still under development / test.
+ *
+ * Author: Auvo Häkkinen <Auvo.Hakkinen@cs.Helsinki.FI>
+ * Juha Sievänen <Juha.Sievanen@cs.Helsinki.FI>
+ * Taneli Vähäkangas <Taneli.Vahakangas@cs.Helsinki.FI>
+ */
+
+#ifndef _I2O_LAN_H
+#define _I2O_LAN_H
+
+/* Default values for tunable parameters first */
+
+#define I2O_LAN_MAX_BUCKETS_OUT 96
+#define I2O_LAN_BUCKET_THRESH 18 /* 9 buckets in one message */
+#define I2O_LAN_RX_COPYBREAK 200
+#define I2O_LAN_TX_TIMEOUT (1*HZ)
+#define I2O_LAN_TX_BATCH_MODE 2 /* 2=automatic, 1=on, 0=off */
+#define I2O_LAN_EVENT_MASK 0 /* 0=None, 0xFFC00002=All */
+
+/* LAN types */
+#define I2O_LAN_ETHERNET 0x0030
+#define I2O_LAN_100VG 0x0040
+#define I2O_LAN_TR 0x0050
+#define I2O_LAN_FDDI 0x0060
+#define I2O_LAN_FIBRE_CHANNEL 0x0070
+#define I2O_LAN_UNKNOWN 0x00000000
+
+/* Connector types */
+
+/* Ethernet */
+#define I2O_LAN_AUI (I2O_LAN_ETHERNET << 4) + 0x00000001
+#define I2O_LAN_10BASE5 (I2O_LAN_ETHERNET << 4) + 0x00000002
+#define I2O_LAN_FIORL (I2O_LAN_ETHERNET << 4) + 0x00000003
+#define I2O_LAN_10BASE2 (I2O_LAN_ETHERNET << 4) + 0x00000004
+#define I2O_LAN_10BROAD36 (I2O_LAN_ETHERNET << 4) + 0x00000005
+#define I2O_LAN_10BASE_T (I2O_LAN_ETHERNET << 4) + 0x00000006
+#define I2O_LAN_10BASE_FP (I2O_LAN_ETHERNET << 4) + 0x00000007
+#define I2O_LAN_10BASE_FB (I2O_LAN_ETHERNET << 4) + 0x00000008
+#define I2O_LAN_10BASE_FL (I2O_LAN_ETHERNET << 4) + 0x00000009
+#define I2O_LAN_100BASE_TX (I2O_LAN_ETHERNET << 4) + 0x0000000A
+#define I2O_LAN_100BASE_FX (I2O_LAN_ETHERNET << 4) + 0x0000000B
+#define I2O_LAN_100BASE_T4 (I2O_LAN_ETHERNET << 4) + 0x0000000C
+#define I2O_LAN_1000BASE_SX (I2O_LAN_ETHERNET << 4) + 0x0000000D
+#define I2O_LAN_1000BASE_LX (I2O_LAN_ETHERNET << 4) + 0x0000000E
+#define I2O_LAN_1000BASE_CX (I2O_LAN_ETHERNET << 4) + 0x0000000F
+#define I2O_LAN_1000BASE_T (I2O_LAN_ETHERNET << 4) + 0x00000010
+
+/* AnyLAN */
+#define I2O_LAN_100VG_ETHERNET (I2O_LAN_100VG << 4) + 0x00000001
+#define I2O_LAN_100VG_TR (I2O_LAN_100VG << 4) + 0x00000002
+
+/* Token Ring */
+#define I2O_LAN_4MBIT (I2O_LAN_TR << 4) + 0x00000001
+#define I2O_LAN_16MBIT (I2O_LAN_TR << 4) + 0x00000002
+
+/* FDDI */
+#define I2O_LAN_125MBAUD (I2O_LAN_FDDI << 4) + 0x00000001
+
+/* Fibre Channel */
+#define I2O_LAN_POINT_POINT (I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000001
+#define I2O_LAN_ARB_LOOP (I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000002
+#define I2O_LAN_PUBLIC_LOOP (I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000003
+#define I2O_LAN_FABRIC (I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000004
+
+#define I2O_LAN_EMULATION 0x00000F00
+#define I2O_LAN_OTHER 0x00000F01
+#define I2O_LAN_DEFAULT 0xFFFFFFFF
+
+/* LAN class functions */
+
+#define LAN_PACKET_SEND 0x3B
+#define LAN_SDU_SEND 0x3D
+#define LAN_RECEIVE_POST 0x3E
+#define LAN_RESET 0x35
+#define LAN_SUSPEND 0x37
+
+/* LAN DetailedStatusCode defines */
+#define I2O_LAN_DSC_SUCCESS 0x00
+#define I2O_LAN_DSC_DEVICE_FAILURE 0x01
+#define I2O_LAN_DSC_DESTINATION_NOT_FOUND 0x02
+#define I2O_LAN_DSC_TRANSMIT_ERROR 0x03
+#define I2O_LAN_DSC_TRANSMIT_ABORTED 0x04
+#define I2O_LAN_DSC_RECEIVE_ERROR 0x05
+#define I2O_LAN_DSC_RECEIVE_ABORTED 0x06
+#define I2O_LAN_DSC_DMA_ERROR 0x07
+#define I2O_LAN_DSC_BAD_PACKET_DETECTED 0x08
+#define I2O_LAN_DSC_OUT_OF_MEMORY 0x09
+#define I2O_LAN_DSC_BUCKET_OVERRUN 0x0A
+#define I2O_LAN_DSC_IOP_INTERNAL_ERROR 0x0B
+#define I2O_LAN_DSC_CANCELED 0x0C
+#define I2O_LAN_DSC_INVALID_TRANSACTION_CONTEXT 0x0D
+#define I2O_LAN_DSC_DEST_ADDRESS_DETECTED 0x0E
+#define I2O_LAN_DSC_DEST_ADDRESS_OMITTED 0x0F
+#define I2O_LAN_DSC_PARTIAL_PACKET_RETURNED 0x10
+#define I2O_LAN_DSC_SUSPENDED 0x11
+
+struct i2o_packet_info {
+ u32 offset : 24;
+ u32 flags : 8;
+ u32 len : 24;
+ u32 status : 8;
+};
+
+struct i2o_bucket_descriptor {
+ u32 context; /* FIXME: 64bit support */
+ struct i2o_packet_info packet_info[1];
+};
+
+/* Event Indicator Mask Flags for LAN OSM */
+
+#define I2O_LAN_EVT_LINK_DOWN 0x01
+#define I2O_LAN_EVT_LINK_UP 0x02
+#define I2O_LAN_EVT_MEDIA_CHANGE 0x04
+
+#include <linux/netdevice.h>
+#include <linux/fddidevice.h>
+
+struct i2o_lan_local {
+ u8 unit;
+ struct i2o_device *i2o_dev;
+
+ struct fddi_statistics stats; /* see also struct net_device_stats */
+ unsigned short (*type_trans)(struct sk_buff *, struct net_device *);
+ atomic_t buckets_out; /* nbr of unused buckets on DDM */
+ atomic_t tx_out; /* outstanding TXes */
+ u8 tx_count; /* packets in one TX message frame */
+ u16 tx_max_out; /* DDM's Tx queue len */
+ u8 sgl_max; /* max SGLs in one message frame */
+ u32 m; /* IOP address of the batch msg frame */
+
+ struct work_struct i2o_batch_send_task;
+ int send_active;
+ struct sk_buff **i2o_fbl; /* Free bucket list (to reuse skbs) */
+ int i2o_fbl_tail;
+ spinlock_t fbl_lock;
+
+ spinlock_t tx_lock;
+
+ u32 max_size_mc_table; /* max number of multicast addresses */
+
+ /* LAN OSM configurable parameters are here: */
+
+ u16 max_buckets_out; /* max nbr of buckets to send to DDM */
+ u16 bucket_thresh; /* send more when this many used */
+ u16 rx_copybreak;
+
+ u8 tx_batch_mode; /* Set when using batch mode sends */
+ u32 i2o_event_mask; /* To turn on interesting event flags */
+};
+
+#endif /* _I2O_LAN_H */
diff --git a/drivers/message/i2o/i2o_proc.c b/drivers/message/i2o/i2o_proc.c
new file mode 100644
index 000000000000..b176d0eeff7f
--- /dev/null
+++ b/drivers/message/i2o/i2o_proc.c
@@ -0,0 +1,2112 @@
+/*
+ * 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 "$Rev$"
+#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 <linux/i2o.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 */
+ mode_t mode; /* mode */
+ 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 @ %02X:%02X:%02X:%02X:%02X:%02X",
+ serialno[2], serialno[3],
+ serialno[4], serialno[5], serialno[6], serialno[7]);
+ 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:?] %02X:%02X:%02X:%02X:%02X:%02X",
+ serialno[8], serialno[9],
+ serialno[2], serialno[3],
+ serialno[4], serialno[5], serialno[6], serialno[7]);
+ 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_PORT:
+ 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(u8 * chars, int n)
+{
+ char tmp[256];
+ 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",
+ "MCA",
+ "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_MCA:
+ seq_printf(seq, " IOBase: %0#6x,",
+ hrt->hrt_entry[i].bus.mca_bus.
+ McaBaseIOPort);
+ seq_printf(seq, " MemoryBase: %0#10x,",
+ hrt->hrt_entry[i].bus.mca_bus.
+ McaBaseMemoryAddress);
+ seq_printf(seq, " Slot: %0#4x,",
+ hrt->hrt_entry[i].bus.mca_bus.
+ McaSlotNumber);
+ 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_PORT:
+ 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;
+
+ 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(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;
+
+ 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(dst->module_name_version, 28));
+ seq_printf(seq, "%-9s", chtostr(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;
+
+ 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((u8 *) (work32 + 2), 16));
+ seq_printf(seq, "Product info : %s\n",
+ chtostr((u8 *) (work32 + 6), 16));
+ seq_printf(seq, "Description : %s\n",
+ chtostr((u8 *) (work32 + 10), 16));
+ seq_printf(seq, "Product rev. : %s\n",
+ chtostr((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", d->device.bus_id);
+
+ 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;
+
+ 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(result.module_name, 24));
+ seq_printf(seq, "Module revision : %s\n",
+ chtostr(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;
+
+ 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(result.device_name, 64));
+ seq_printf(seq, "Service name : %s\n",
+ chtostr(result.service_name, 64));
+ seq_printf(seq, "Physical name : %s\n",
+ chtostr(result.physical_location, 64));
+ seq_printf(seq, "Instance number : %s\n",
+ chtostr(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(inode)->data);
+};
+
+static int i2o_seq_open_lct(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_lct, PDE(inode)->data);
+};
+
+static int i2o_seq_open_status(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_status, PDE(inode)->data);
+};
+
+static int i2o_seq_open_hw(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_hw, PDE(inode)->data);
+};
+
+static int i2o_seq_open_ddm_table(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_ddm_table, PDE(inode)->data);
+};
+
+static int i2o_seq_open_driver_store(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_driver_store, PDE(inode)->data);
+};
+
+static int i2o_seq_open_drivers_stored(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_drivers_stored, PDE(inode)->data);
+};
+
+static int i2o_seq_open_groups(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_groups, PDE(inode)->data);
+};
+
+static int i2o_seq_open_phys_device(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_phys_device, PDE(inode)->data);
+};
+
+static int i2o_seq_open_claimed(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_claimed, PDE(inode)->data);
+};
+
+static int i2o_seq_open_users(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_users, PDE(inode)->data);
+};
+
+static int i2o_seq_open_priv_msgs(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_priv_msgs, PDE(inode)->data);
+};
+
+static int i2o_seq_open_authorized_users(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_authorized_users,
+ PDE(inode)->data);
+};
+
+static int i2o_seq_open_dev_identity(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_dev_identity, PDE(inode)->data);
+};
+
+static int i2o_seq_open_ddm_identity(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_ddm_identity, PDE(inode)->data);
+};
+
+static int i2o_seq_open_uinfo(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_uinfo, PDE(inode)->data);
+};
+
+static int i2o_seq_open_sgl_limits(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_sgl_limits, PDE(inode)->data);
+};
+
+static int i2o_seq_open_sensors(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_sensors, PDE(inode)->data);
+};
+
+static int i2o_seq_open_dev_name(struct inode *inode, struct file *file)
+{
+ return single_open(file, i2o_seq_show_dev_name, PDE(inode)->data);
+};
+
+static struct file_operations i2o_seq_fops_lct = {
+ .open = i2o_seq_open_lct,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct file_operations i2o_seq_fops_hrt = {
+ .open = i2o_seq_open_hrt,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct file_operations i2o_seq_fops_status = {
+ .open = i2o_seq_open_status,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct file_operations i2o_seq_fops_hw = {
+ .open = i2o_seq_open_hw,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct file_operations i2o_seq_fops_ddm_table = {
+ .open = i2o_seq_open_ddm_table,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct file_operations i2o_seq_fops_driver_store = {
+ .open = i2o_seq_open_driver_store,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct file_operations i2o_seq_fops_drivers_stored = {
+ .open = i2o_seq_open_drivers_stored,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct file_operations i2o_seq_fops_groups = {
+ .open = i2o_seq_open_groups,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct file_operations i2o_seq_fops_phys_device = {
+ .open = i2o_seq_open_phys_device,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct file_operations i2o_seq_fops_claimed = {
+ .open = i2o_seq_open_claimed,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct file_operations i2o_seq_fops_users = {
+ .open = i2o_seq_open_users,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct file_operations i2o_seq_fops_priv_msgs = {
+ .open = i2o_seq_open_priv_msgs,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct file_operations i2o_seq_fops_authorized_users = {
+ .open = i2o_seq_open_authorized_users,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct file_operations i2o_seq_fops_dev_name = {
+ .open = i2o_seq_open_dev_name,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct file_operations i2o_seq_fops_dev_identity = {
+ .open = i2o_seq_open_dev_identity,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct file_operations i2o_seq_fops_ddm_identity = {
+ .open = i2o_seq_open_ddm_identity,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct file_operations i2o_seq_fops_uinfo = {
+ .open = i2o_seq_open_uinfo,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct file_operations i2o_seq_fops_sgl_limits = {
+ .open = i2o_seq_open_sgl_limits,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static 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 = create_proc_entry(i2o_pe->name, i2o_pe->mode, dir);
+ if (!tmp)
+ return -1;
+
+ tmp->data = data;
+ tmp->proc_fops = i2o_pe->fops;
+
+ i2o_pe++;
+ }
+
+ return 0;
+}
+
+/**
+ * i2o_proc_subdir_remove - Remove child entries from a proc entry
+ * @dir: proc dir entry from which the childs should be removed
+ *
+ * Iterate over each i2o proc entry under dir and remove it. If the child
+ * also has entries, remove them too.
+ */
+static void i2o_proc_subdir_remove(struct proc_dir_entry *dir)
+{
+ struct proc_dir_entry *pe, *tmp;
+ pe = dir->subdir;
+ while (pe) {
+ tmp = pe->next;
+ i2o_proc_subdir_remove(pe);
+ remove_proc_entry(pe->name, dir);
+ pe = tmp;
+ }
+};
+
+/**
+ * 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(buff, dir);
+ if (!devdir) {
+ osm_warn("Could not allocate procdir!\n");
+ return;
+ }
+
+ devdir->data = dev;
+
+ 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(c->name, dir);
+ if (!iopdir)
+ return -1;
+
+ iopdir->data = c;
+
+ 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_iop_remove - Removes an I2O controller from the i2o proc tree
+ * @dir: parent proc dir entry
+ * @c: I2O controller which should be removed
+ *
+ * Iterate over each i2o proc entry and search controller c. If it is found
+ * remove it from the tree.
+ */
+static void i2o_proc_iop_remove(struct proc_dir_entry *dir,
+ struct i2o_controller *c)
+{
+ struct proc_dir_entry *pe, *tmp;
+
+ pe = dir->subdir;
+ while (pe) {
+ tmp = pe->next;
+ if (pe->data == c) {
+ i2o_proc_subdir_remove(pe);
+ remove_proc_entry(pe->name, dir);
+ }
+ osm_debug("removing IOP /proc/i2o/%s\n", c->name);
+ pe = tmp;
+ }
+}
+
+/**
+ * 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;
+
+ i2o_proc_dir_root->owner = THIS_MODULE;
+
+ 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)
+{
+ struct i2o_controller *c;
+
+ list_for_each_entry(c, &i2o_controllers, list)
+ i2o_proc_iop_remove(i2o_proc_dir_root, c);
+
+ remove_proc_entry("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/message/i2o/i2o_scsi.c b/drivers/message/i2o/i2o_scsi.c
new file mode 100644
index 000000000000..43f5875e0be5
--- /dev/null
+++ b/drivers/message/i2o/i2o_scsi.c
@@ -0,0 +1,830 @@
+/*
+ * 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 <linux/i2o.h>
+
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+
+#define OSM_NAME "scsi-osm"
+#define OSM_VERSION "$Rev$"
+#define OSM_DESCRIPTION "I2O SCSI Peripheral OSM"
+
+static struct i2o_driver i2o_scsi_driver;
+
+static int i2o_scsi_max_id = 16;
+static int i2o_scsi_max_lun = 8;
+
+struct i2o_scsi_host {
+ struct Scsi_Host *scsi_host; /* pointer to the SCSI host */
+ struct i2o_controller *iop; /* pointer to the I2O controller */
+ 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;
+ i2o_status_block *sb;
+
+ list_for_each_entry(i2o_dev, &c->devices, list)
+ if (i2o_dev->lct_data.class_id == I2O_CLASS_BUS_ADAPTER_PORT) {
+ if (i2o_parm_field_get(i2o_dev, 0x0000, 0, &type, 1) || (type == 1)) /* 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;
+
+ sb = c->status_block.virt;
+
+ scsi_host->sg_tablesize = (sb->inbound_frame_size -
+ sizeof(struct i2o_message) / 4 - 6) / 2;
+
+ i2o_shost = (struct i2o_scsi_host *)scsi_host->hostdata;
+ i2o_shost->scsi_host = scsi_host;
+ i2o_shost->iop = c;
+
+ i = 0;
+ list_for_each_entry(i2o_dev, &c->devices, list)
+ if (i2o_dev->lct_data.class_id == I2O_CLASS_BUS_ADAPTER_PORT) {
+ if (i2o_parm_field_get(i2o_dev, 0x0000, 0, &type, 1) || (type == 1)) /* 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;
+
+ i2o_shost = i2o_scsi_get_host(c);
+
+ shost_for_each_device(scsi_dev, i2o_shost->scsi_host)
+ if (scsi_dev->hostdata == i2o_dev) {
+ 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 everthing 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;
+ u64 lun;
+ int channel = -1;
+ int i;
+
+ i2o_shost = i2o_scsi_get_host(c);
+ if (!i2o_shost)
+ return -EFAULT;
+
+ scsi_host = i2o_shost->scsi_host;
+
+ if (i2o_parm_field_get(i2o_dev, 0, 3, &id, 4) < 0)
+ return -EFAULT;
+
+ if (id >= scsi_host->max_id) {
+ osm_warn("SCSI device id (%d) >= max_id of I2O host (%d)", id,
+ scsi_host->max_id);
+ return -EFAULT;
+ }
+
+ if (i2o_parm_field_get(i2o_dev, 0, 4, &lun, 8) < 0)
+ return -EFAULT;
+ if (lun >= scsi_host->max_lun) {
+ osm_warn("SCSI device id (%d) >= max_lun of I2O host (%d)",
+ (unsigned int)lun, scsi_host->max_lun);
+ 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;
+
+ if (channel == -1) {
+ osm_warn("can not find channel of device %03x\n",
+ i2o_dev->lct_data.tid);
+ return -EFAULT;
+ }
+
+ scsi_dev =
+ __scsi_add_device(i2o_shost->scsi_host, channel, id, lun, i2o_dev);
+
+ if (!scsi_dev) {
+ osm_warn("can not add SCSI device %03x\n",
+ i2o_dev->lct_data.tid);
+ return -EFAULT;
+ }
+
+ osm_debug("added new SCSI device %03x (cannel: %d, id: %d, lun: %d)\n",
+ i2o_dev->lct_data.tid, channel, id, (unsigned int)lun);
+
+ return 0;
+};
+
+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;
+ struct device *dev;
+ u8 as, ds, st;
+
+ cmd = i2o_cntxt_list_get(c, le32_to_cpu(msg->u.s.tcntxt));
+
+ if (msg->u.head[0] & (1 << 13)) {
+ struct i2o_message __iomem *pmsg; /* preserved message */
+ u32 pm;
+ int err = DID_ERROR;
+
+ pm = le32_to_cpu(msg->body[3]);
+
+ pmsg = i2o_msg_in_to_virt(c, pm);
+
+ osm_err("IOP fail.\n");
+ osm_err("From %d To %d Cmd %d.\n",
+ (msg->u.head[1] >> 12) & 0xFFF,
+ msg->u.head[1] & 0xFFF, msg->u.head[1] >> 24);
+ osm_err("Failure Code %d.\n", msg->body[0] >> 24);
+ if (msg->body[0] & (1 << 16))
+ osm_err("Format error.\n");
+ if (msg->body[0] & (1 << 17))
+ osm_err("Path error.\n");
+ if (msg->body[0] & (1 << 18))
+ osm_err("Path State.\n");
+ if (msg->body[0] & (1 << 18))
+ {
+ osm_err("Congestion.\n");
+ err = DID_BUS_BUSY;
+ }
+
+ osm_debug("Failing message is %p.\n", pmsg);
+
+ cmd = i2o_cntxt_list_get(c, readl(&pmsg->u.s.tcntxt));
+ if (!cmd)
+ return 1;
+
+ cmd->result = err << 16;
+ cmd->scsi_done(cmd);
+
+ /* Now flush the message by making it a NOP */
+ i2o_msg_nop(c, pm);
+
+ return 1;
+ }
+
+ /*
+ * Low byte is device status, next is adapter status,
+ * (then one byte reserved), then request status.
+ */
+ ds = (u8) le32_to_cpu(msg->body[0]);
+ as = (u8) (le32_to_cpu(msg->body[0]) >> 8);
+ st = (u8) (le32_to_cpu(msg->body[0]) >> 24);
+
+ /*
+ * Is this a control request coming back - eg an abort ?
+ */
+
+ if (!cmd) {
+ if (st)
+ osm_warn("SCSI abort: %08X", le32_to_cpu(msg->body[0]));
+ osm_info("SCSI abort completed.\n");
+ return -EFAULT;
+ }
+
+ osm_debug("Completed %ld\n", cmd->serial_number);
+
+ if (st) {
+ u32 count, error;
+ /* An error has occurred */
+
+ switch (st) {
+ case 0x06:
+ count = le32_to_cpu(msg->body[1]);
+ if (count < cmd->underflow) {
+ int i;
+
+ osm_err("SCSI underflow 0x%08X 0x%08X\n", count,
+ cmd->underflow);
+ osm_debug("Cmd: ");
+ for (i = 0; i < 15; i++)
+ pr_debug("%02X ", cmd->cmnd[i]);
+ pr_debug(".\n");
+ cmd->result = (DID_ERROR << 16);
+ }
+ break;
+
+ default:
+ error = le32_to_cpu(msg->body[0]);
+
+ osm_err("SCSI error %08x\n", error);
+
+ if ((error & 0xff) == 0x02 /*CHECK_CONDITION */ ) {
+ int i;
+ u32 len = sizeof(cmd->sense_buffer);
+ len = (len > 40) ? 40 : len;
+ // Copy over the sense data
+ memcpy(cmd->sense_buffer, (void *)&msg->body[3],
+ len);
+ for (i = 0; i <= len; i++)
+ osm_info("%02x\n",
+ cmd->sense_buffer[i]);
+ if (cmd->sense_buffer[0] == 0x70
+ && cmd->sense_buffer[2] == DATA_PROTECT) {
+ /* This is to handle an array failed */
+ cmd->result = (DID_TIME_OUT << 16);
+ printk(KERN_WARNING "%s: SCSI Data "
+ "Protect-Device (%d,%d,%d) "
+ "hba_status=0x%x, dev_status="
+ "0x%x, cmd=0x%x\n", c->name,
+ (u32) cmd->device->channel,
+ (u32) cmd->device->id,
+ (u32) cmd->device->lun,
+ (error >> 8) & 0xff,
+ error & 0xff, cmd->cmnd[0]);
+ } else
+ cmd->result = (DID_ERROR << 16);
+
+ break;
+ }
+
+ switch (as) {
+ case 0x0E:
+ /* SCSI Reset */
+ cmd->result = DID_RESET << 16;
+ break;
+
+ case 0x0F:
+ cmd->result = DID_PARITY << 16;
+ break;
+
+ default:
+ cmd->result = DID_ERROR << 16;
+ break;
+ }
+
+ break;
+ }
+
+ cmd->scsi_done(cmd);
+ return 1;
+ }
+
+ cmd->result = DID_OK << 16 | ds;
+
+ cmd->scsi_done(cmd);
+
+ dev = &c->pdev->dev;
+ if (cmd->use_sg)
+ dma_unmap_sg(dev, (struct scatterlist *)cmd->buffer,
+ cmd->use_sg, cmd->sc_data_direction);
+ else if (cmd->request_bufflen)
+ dma_unmap_single(dev, (dma_addr_t) ((long)cmd->SCp.ptr),
+ cmd->request_bufflen, cmd->sc_data_direction);
+
+ return 1;
+};
+
+/**
+ * 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);
+ pr_info("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_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(struct scsi_cmnd *SCpnt,
+ void (*done) (struct scsi_cmnd *))
+{
+ struct i2o_controller *c;
+ struct Scsi_Host *host;
+ struct i2o_device *i2o_dev;
+ struct device *dev;
+ int tid;
+ struct i2o_message __iomem *msg;
+ u32 m;
+ u32 scsi_flags, sg_flags;
+ u32 __iomem *mptr;
+ u32 __iomem *lenptr;
+ u32 len, reqlen;
+ int i;
+
+ /*
+ * Do the incoming paperwork
+ */
+
+ i2o_dev = SCpnt->device->hostdata;
+ host = SCpnt->device->host;
+ c = i2o_dev->iop;
+ dev = &c->pdev->dev;
+
+ SCpnt->scsi_done = done;
+
+ if (unlikely(!i2o_dev)) {
+ osm_warn("no I2O device in request\n");
+ SCpnt->result = DID_NO_CONNECT << 16;
+ done(SCpnt);
+ return 0;
+ }
+
+ tid = i2o_dev->lct_data.tid;
+
+ osm_debug("qcmd: Tid = %03x\n", tid);
+ osm_debug("Real scsi messages.\n");
+
+ /*
+ * Obtain an I2O message. If there are none free then
+ * throw it back to the scsi layer
+ */
+
+ m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY)
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ /*
+ * Put together a scsi execscb message
+ */
+
+ len = SCpnt->request_bufflen;
+
+ switch (SCpnt->sc_data_direction) {
+ case PCI_DMA_NONE:
+ scsi_flags = 0x00000000; // DATA NO XFER
+ sg_flags = 0x00000000;
+ break;
+
+ case PCI_DMA_TODEVICE:
+ scsi_flags = 0x80000000; // DATA OUT (iop-->dev)
+ sg_flags = 0x14000000;
+ break;
+
+ case PCI_DMA_FROMDEVICE:
+ scsi_flags = 0x40000000; // DATA IN (iop<--dev)
+ sg_flags = 0x10000000;
+ break;
+
+ default:
+ /* Unknown - kill the command */
+ SCpnt->result = DID_NO_CONNECT << 16;
+ done(SCpnt);
+ return 0;
+ }
+
+ writel(I2O_CMD_SCSI_EXEC << 24 | HOST_TID << 12 | tid, &msg->u.head[1]);
+ writel(i2o_scsi_driver.context, &msg->u.s.icntxt);
+
+ /* We want the SCSI control block back */
+ writel(i2o_cntxt_list_add(c, SCpnt), &msg->u.s.tcntxt);
+
+ /* 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 */
+ /*
+ 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;
+ }
+ */
+
+ /* Direction, disconnect ok, tag, CDBLen */
+ writel(scsi_flags | 0x20200000 | SCpnt->cmd_len, &msg->body[0]);
+
+ mptr = &msg->body[1];
+
+ /* Write SCSI command into the message - always 16 byte block */
+ memcpy_toio(mptr, SCpnt->cmnd, 16);
+ mptr += 4;
+ lenptr = mptr++; /* Remember me - fill in when we know */
+
+ reqlen = 12; // SINGLE SGE
+
+ /* Now fill in the SGList and command */
+ if (SCpnt->use_sg) {
+ struct scatterlist *sg;
+ int sg_count;
+
+ sg = SCpnt->request_buffer;
+ len = 0;
+
+ sg_count = dma_map_sg(dev, sg, SCpnt->use_sg,
+ SCpnt->sc_data_direction);
+
+ if (unlikely(sg_count <= 0))
+ return -ENOMEM;
+
+ for (i = SCpnt->use_sg; i > 0; i--) {
+ if (i == 1)
+ sg_flags |= 0xC0000000;
+ writel(sg_flags | sg_dma_len(sg), mptr++);
+ writel(sg_dma_address(sg), mptr++);
+ len += sg_dma_len(sg);
+ sg++;
+ }
+
+ reqlen = mptr - &msg->u.head[0];
+ writel(len, lenptr);
+ } else {
+ len = SCpnt->request_bufflen;
+
+ writel(len, lenptr);
+
+ if (len > 0) {
+ dma_addr_t dma_addr;
+
+ dma_addr = dma_map_single(dev, SCpnt->request_buffer,
+ SCpnt->request_bufflen,
+ SCpnt->sc_data_direction);
+ if (!dma_addr)
+ return -ENOMEM;
+
+ SCpnt->SCp.ptr = (void *)(unsigned long)dma_addr;
+ sg_flags |= 0xC0000000;
+ writel(sg_flags | SCpnt->request_bufflen, mptr++);
+ writel(dma_addr, mptr++);
+ } else
+ reqlen = 9;
+ }
+
+ /* Stick the headers on */
+ writel(reqlen << 16 | SGL_OFFSET_10, &msg->u.head[0]);
+
+ /* Queue the message */
+ i2o_msg_post(c, m);
+
+ osm_debug("Issued %ld\n", SCpnt->serial_number);
+
+ return 0;
+};
+
+/**
+ * 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 __iomem *msg;
+ u32 m;
+ 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;
+
+ m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY)
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ writel(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]);
+ writel(I2O_CMD_SCSI_ABORT << 24 | HOST_TID << 12 | tid,
+ &msg->u.head[1]);
+ writel(i2o_cntxt_list_get_ptr(c, SCpnt), &msg->body[0]);
+
+ if (i2o_msg_post_wait(c, m, 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 anyones 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/message/i2o/iop.c b/drivers/message/i2o/iop.c
new file mode 100644
index 000000000000..50c8cedf7a2d
--- /dev/null
+++ b/drivers/message/i2o/iop.c
@@ -0,0 +1,1327 @@
+/*
+ * 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@redhat.com>:
+ * Ported to Linux 2.5.
+ * Markus Lidel <Markus.Lidel@shadowconnect.com>:
+ * Minor fixes for 2.6.
+ */
+
+#include <linux/module.h>
+#include <linux/i2o.h>
+#include <linux/delay.h>
+
+#define OSM_VERSION "$Rev$"
+#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);
+
+/* Module internal functions from other sources */
+extern struct i2o_driver i2o_exec_driver;
+extern int i2o_exec_lct_get(struct i2o_controller *);
+extern void i2o_device_remove(struct i2o_device *);
+
+extern int __init i2o_driver_init(void);
+extern void __exit i2o_driver_exit(void);
+extern int __init i2o_exec_init(void);
+extern void __exit i2o_exec_exit(void);
+extern int __init i2o_pci_init(void);
+extern void __exit i2o_pci_exit(void);
+extern int i2o_device_init(void);
+extern void i2o_device_exit(void);
+
+/**
+ * i2o_msg_nop - Returns a message which is not used
+ * @c: I2O controller from which the message was created
+ * @m: 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 message frame
+ * is lost.
+ */
+void i2o_msg_nop(struct i2o_controller *c, u32 m)
+{
+ struct i2o_message __iomem *msg = c->in_queue.virt + m;
+
+ writel(THREE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]);
+ writel(I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | ADAPTER_TID,
+ &msg->u.head[1]);
+ writel(0, &msg->u.head[2]);
+ writel(0, &msg->u.head[3]);
+ i2o_msg_post(c, m);
+};
+
+/**
+ * i2o_msg_get_wait - obtain an I2O message from the IOP
+ * @c: I2O controller
+ * @msg: pointer to a I2O message pointer
+ * @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.
+ */
+u32 i2o_msg_get_wait(struct i2o_controller *c, struct i2o_message __iomem **msg,
+ int wait)
+{
+ unsigned long timeout = jiffies + wait * HZ;
+ u32 m;
+
+ while ((m = i2o_msg_get(c, msg)) == I2O_QUEUE_EMPTY) {
+ if (time_after(jiffies, timeout)) {
+ pr_debug("%s: Timeout waiting for message frame.\n",
+ c->name);
+ return I2O_QUEUE_EMPTY;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+
+ return m;
+};
+
+#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)
+ printk(KERN_ERR "%s: couldn't add NULL pointer to context list!"
+ "\n", c->name);
+
+ entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+ if (!entry) {
+ printk(KERN_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);
+
+ pr_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 succes 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)
+ printk(KERN_WARNING "%s: Could not remove nonexistent ptr "
+ "%p\n", c->name, ptr);
+
+ pr_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)
+ printk(KERN_WARNING "%s: context id %d not found\n", c->name,
+ context);
+
+ pr_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 succes 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)
+ printk(KERN_WARNING "%s: Could not find nonexistent ptr "
+ "%p\n", c->name, ptr);
+
+ pr_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 __iomem *msg;
+ u32 m;
+ 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;
+
+ m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY)
+ return -ETIMEDOUT;
+
+ writel(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]);
+ writel(I2O_CMD_SYS_QUIESCE << 24 | HOST_TID << 12 | ADAPTER_TID,
+ &msg->u.head[1]);
+
+ /* Long timeout needed for quiesce if lots of devices */
+ if ((rc = i2o_msg_post_wait(c, m, 240)))
+ printk(KERN_INFO "%s: Unable to quiesce (status=%#x).\n",
+ c->name, -rc);
+ else
+ pr_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 __iomem *msg;
+ u32 m;
+ 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;
+
+ m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY)
+ return -ETIMEDOUT;
+
+ writel(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]);
+ writel(I2O_CMD_SYS_ENABLE << 24 | HOST_TID << 12 | ADAPTER_TID,
+ &msg->u.head[1]);
+
+ /* How long of a timeout do we need? */
+ if ((rc = i2o_msg_post_wait(c, m, 240)))
+ printk(KERN_ERR "%s: Could not enable (status=%#x).\n",
+ c->name, -rc);
+ else
+ pr_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 __iomem *msg;
+ u32 m;
+ int rc;
+
+ m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY)
+ return -ETIMEDOUT;
+
+ /* Quiesce all IOPs first */
+ i2o_iop_quiesce_all();
+
+ writel(FOUR_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]);
+ writel(I2O_CMD_ADAPTER_CLEAR << 24 | HOST_TID << 12 | ADAPTER_TID,
+ &msg->u.head[1]);
+
+ if ((rc = i2o_msg_post_wait(c, m, 30)))
+ printk(KERN_INFO "%s: Unable to clear (status=%#x).\n",
+ c->name, -rc);
+ else
+ pr_debug("%s: Cleared.\n", c->name);
+
+ /* Enable all IOPs */
+ i2o_iop_enable_all();
+
+ i2o_status_get(c);
+
+ return rc;
+}
+
+/**
+ * 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)
+{
+ u8 *status = c->status.virt;
+ struct i2o_message __iomem *msg;
+ u32 m;
+ unsigned long timeout;
+ i2o_status_block *sb = c->status_block.virt;
+ int rc = 0;
+
+ pr_debug("%s: Resetting controller\n", c->name);
+
+ m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY)
+ return -ETIMEDOUT;
+
+ memset(status, 0, 8);
+
+ /* Quiesce all IOPs first */
+ i2o_iop_quiesce_all();
+
+ writel(EIGHT_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]);
+ writel(I2O_CMD_ADAPTER_RESET << 24 | HOST_TID << 12 | ADAPTER_TID,
+ &msg->u.head[1]);
+ writel(i2o_exec_driver.context, &msg->u.s.icntxt);
+ writel(0, &msg->u.s.tcntxt); //FIXME: use reasonable transaction context
+ writel(0, &msg->body[0]);
+ writel(0, &msg->body[1]);
+ writel(i2o_ptr_low((void *)c->status.phys), &msg->body[2]);
+ writel(i2o_ptr_high((void *)c->status.phys), &msg->body[3]);
+
+ i2o_msg_post(c, m);
+
+ /* Wait for a reply */
+ timeout = jiffies + I2O_TIMEOUT_RESET * HZ;
+ while (!*status) {
+ if (time_after(jiffies, timeout)) {
+ printk(KERN_ERR "%s: IOP reset timeout.\n", c->name);
+ rc = -ETIMEDOUT;
+ goto exit;
+ }
+
+ /* Promise bug */
+ if (status[1] || status[4]) {
+ *status = 0;
+ break;
+ }
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+
+ rmb();
+ }
+
+ if (*status == 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 ammount of
+ * time, we assume the IOP could not reboot properly.
+ */
+ pr_debug("%s: Reset in progress, waiting for reboot...\n",
+ c->name);
+
+ m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_RESET);
+ while (m == I2O_QUEUE_EMPTY) {
+ if (time_after(jiffies, timeout)) {
+ printk(KERN_ERR "%s: IOP reset timeout.\n",
+ c->name);
+ rc = -ETIMEDOUT;
+ goto exit;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+
+ m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_RESET);
+ }
+ i2o_msg_nop(c, m);
+ }
+
+ /* from here all quiesce commands are safe */
+ c->no_quiesce = 0;
+
+ /* If IopReset was rejected or didn't perform reset, try IopClear */
+ i2o_status_get(c);
+ if (*status == I2O_CMD_REJECTED || sb->iop_state != ADAPTER_STATE_RESET) {
+ printk(KERN_WARNING "%s: Reset rejected, trying to clear\n",
+ c->name);
+ i2o_iop_clear(c);
+ } else
+ pr_debug("%s: Reset completed.\n", c->name);
+
+ exit:
+ /* 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 a negative errno code on failure.
+ */
+static int i2o_iop_init_outbound_queue(struct i2o_controller *c)
+{
+ u8 *status = c->status.virt;
+ u32 m;
+ struct i2o_message __iomem *msg;
+ ulong timeout;
+ int i;
+
+ pr_debug("%s: Initializing Outbound Queue...\n", c->name);
+
+ memset(status, 0, 4);
+
+ m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY)
+ return -ETIMEDOUT;
+
+ writel(EIGHT_WORD_MSG_SIZE | TRL_OFFSET_6, &msg->u.head[0]);
+ writel(I2O_CMD_OUTBOUND_INIT << 24 | HOST_TID << 12 | ADAPTER_TID,
+ &msg->u.head[1]);
+ writel(i2o_exec_driver.context, &msg->u.s.icntxt);
+ writel(0x0106, &msg->u.s.tcntxt); /* FIXME: why 0x0106, maybe in
+ Spec? */
+ writel(PAGE_SIZE, &msg->body[0]);
+ writel(MSG_FRAME_SIZE << 16 | 0x80, &msg->body[1]); /* Outbound msg frame
+ size in words and Initcode */
+ writel(0xd0000004, &msg->body[2]);
+ writel(i2o_ptr_low((void *)c->status.phys), &msg->body[3]);
+ writel(i2o_ptr_high((void *)c->status.phys), &msg->body[4]);
+
+ i2o_msg_post(c, m);
+
+ timeout = jiffies + I2O_TIMEOUT_INIT_OUTBOUND_QUEUE * HZ;
+ while (*status <= I2O_CMD_IN_PROGRESS) {
+ if (time_after(jiffies, timeout)) {
+ printk(KERN_WARNING "%s: Timeout Initializing\n",
+ c->name);
+ return -ETIMEDOUT;
+ }
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+
+ rmb();
+ }
+
+ m = c->out_queue.phys;
+
+ /* Post frames */
+ for (i = 0; i < NMBR_MSG_FRAMES; i++) {
+ i2o_flush_reply(c, m);
+ udelay(1); /* Promise */
+ m += MSG_FRAME_SIZE * 4;
+ }
+
+ return 0;
+}
+
+/**
+ * i2o_iop_send_nop - send a core NOP message
+ * @c: controller
+ *
+ * Send a no-operation message with a reply set to cause no
+ * action either. Needed for bringing up promise controllers.
+ */
+static int i2o_iop_send_nop(struct i2o_controller *c)
+{
+ struct i2o_message __iomem *msg;
+ u32 m = i2o_msg_get_wait(c, &msg, HZ);
+ if (m == I2O_QUEUE_EMPTY)
+ return -ETIMEDOUT;
+ i2o_msg_nop(c, m);
+ return 0;
+}
+
+/**
+ * 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)
+{
+ struct pci_dev *i960 = NULL;
+ i2o_status_block *sb = c->status_block.virt;
+ int rc;
+
+ if (c->promise) {
+ /* Beat up the hardware first of all */
+ i960 =
+ pci_find_slot(c->pdev->bus->number,
+ PCI_DEVFN(PCI_SLOT(c->pdev->devfn), 0));
+ if (i960)
+ pci_write_config_word(i960, 0x42, 0);
+
+ /* Follow this sequence precisely or the controller
+ ceases to perform useful functions until reboot */
+ if ((rc = i2o_iop_send_nop(c)))
+ return rc;
+
+ if ((rc = i2o_iop_reset(c)))
+ return rc;
+ }
+
+ /* In INIT state, Wait Inbound Q to initialize (in i2o_status_get) */
+ /* In READY state, Get status */
+
+ rc = i2o_status_get(c);
+ if (rc) {
+ printk(KERN_INFO "%s: Unable to obtain status, "
+ "attempting a reset.\n", c->name);
+ if (i2o_iop_reset(c))
+ return rc;
+ }
+
+ if (sb->i2o_version > I2OVER15) {
+ printk(KERN_ERR "%s: Not running version 1.5 of the I2O "
+ "Specification.\n", c->name);
+ return -ENODEV;
+ }
+
+ switch (sb->iop_state) {
+ case ADAPTER_STATE_FAULTED:
+ printk(KERN_CRIT "%s: hardware fault\n", c->name);
+ return -ENODEV;
+
+ case ADAPTER_STATE_READY:
+ case ADAPTER_STATE_OPERATIONAL:
+ case ADAPTER_STATE_HOLD:
+ case ADAPTER_STATE_FAILED:
+ pr_debug("%s: already running, trying to reset...\n", c->name);
+ if (i2o_iop_reset(c))
+ return -ENODEV;
+ }
+
+ rc = i2o_iop_init_outbound_queue(c);
+ if (rc)
+ return rc;
+
+ if (c->promise) {
+ if ((rc = i2o_iop_send_nop(c)))
+ return rc;
+
+ if ((rc = i2o_status_get(c)))
+ return rc;
+
+ if (i960)
+ pci_write_config_word(i960, 0x42, 0x3FF);
+ }
+
+ /* In HOLD state */
+
+ rc = i2o_hrt_get(c);
+
+ return rc;
+};
+
+/**
+ * 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 __iomem *msg;
+ u32 m;
+ i2o_status_block *sb = c->status_block.virt;
+ struct device *dev = &c->pdev->dev;
+ struct resource *root;
+ int rc;
+
+ if (sb->current_mem_size < sb->desired_mem_size) {
+ struct resource *res = &c->mem_resource;
+ res->name = c->pdev->bus->name;
+ res->flags = IORESOURCE_MEM;
+ res->start = 0;
+ res->end = 0;
+ printk(KERN_INFO "%s: requires private memory resources.\n",
+ c->name);
+ root = pci_find_parent_resource(c->pdev, res);
+ if (root == NULL)
+ printk(KERN_WARNING "%s: Can't find parent resource!\n",
+ c->name);
+ if (root && allocate_resource(root, res, sb->desired_mem_size, sb->desired_mem_size, sb->desired_mem_size, 1 << 20, /* Unspecified, so use 1Mb and play safe */
+ NULL, NULL) >= 0) {
+ c->mem_alloc = 1;
+ sb->current_mem_size = 1 + res->end - res->start;
+ sb->current_mem_base = res->start;
+ printk(KERN_INFO "%s: allocated %ld bytes of PCI memory"
+ " at 0x%08lX.\n", c->name,
+ 1 + res->end - res->start, res->start);
+ }
+ }
+
+ if (sb->current_io_size < sb->desired_io_size) {
+ struct resource *res = &c->io_resource;
+ res->name = c->pdev->bus->name;
+ res->flags = IORESOURCE_IO;
+ res->start = 0;
+ res->end = 0;
+ printk(KERN_INFO "%s: requires private memory resources.\n",
+ c->name);
+ root = pci_find_parent_resource(c->pdev, res);
+ if (root == NULL)
+ printk(KERN_WARNING "%s: Can't find parent resource!\n",
+ c->name);
+ if (root && allocate_resource(root, res, sb->desired_io_size, sb->desired_io_size, sb->desired_io_size, 1 << 20, /* Unspecified, so use 1Mb and play safe */
+ NULL, NULL) >= 0) {
+ c->io_alloc = 1;
+ sb->current_io_size = 1 + res->end - res->start;
+ sb->current_mem_base = res->start;
+ printk(KERN_INFO "%s: allocated %ld bytes of PCI I/O at"
+ " 0x%08lX.\n", c->name,
+ 1 + res->end - res->start, res->start);
+ }
+ }
+
+ m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY)
+ return -ETIMEDOUT;
+
+ i2o_systab.phys = dma_map_single(dev, i2o_systab.virt, i2o_systab.len,
+ PCI_DMA_TODEVICE);
+ if (!i2o_systab.phys) {
+ i2o_msg_nop(c, m);
+ return -ENOMEM;
+ }
+
+ writel(I2O_MESSAGE_SIZE(12) | SGL_OFFSET_6, &msg->u.head[0]);
+ writel(I2O_CMD_SYS_TAB_SET << 24 | HOST_TID << 12 | ADAPTER_TID,
+ &msg->u.head[1]);
+
+ /*
+ * Provide three SGL-elements:
+ * System table (SysTab), Private memory space declaration and
+ * Private i/o space declaration
+ *
+ * FIXME: is this still true?
+ * Nasty one here. We can't use dma_alloc_coherent to send the
+ * same table to everyone. We have to go remap it for them all
+ */
+
+ writel(c->unit + 2, &msg->body[0]);
+ writel(0, &msg->body[1]);
+ writel(0x54000000 | i2o_systab.len, &msg->body[2]);
+ writel(i2o_systab.phys, &msg->body[3]);
+ writel(0x54000000 | sb->current_mem_size, &msg->body[4]);
+ writel(sb->current_mem_base, &msg->body[5]);
+ writel(0xd4000000 | sb->current_io_size, &msg->body[6]);
+ writel(sb->current_io_base, &msg->body[6]);
+
+ rc = i2o_msg_post_wait(c, m, 120);
+
+ dma_unmap_single(dev, i2o_systab.phys, i2o_systab.len,
+ PCI_DMA_TODEVICE);
+
+ if (rc < 0)
+ printk(KERN_ERR "%s: Unable to set SysTab (status=%#x).\n",
+ c->name, -rc);
+ else
+ pr_debug("%s: SysTab set.\n", c->name);
+
+ i2o_status_get(c); // Entered READY state
+
+ 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 negativer 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 */
+ pr_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;
+
+ pr_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);
+
+ /* 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 = kmalloc(i2o_systab.len, GFP_KERNEL);
+ if (!systab) {
+ printk(KERN_ERR "i2o: unable to allocate memory for System "
+ "Table\n");
+ return -ENOMEM;
+ }
+ memset(systab, 0, i2o_systab.len);
+
+ 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) {
+ printk(KERN_ERR "i2o: 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))) {
+ printk(KERN_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_ptr_low(c->post_port);
+ systab->iops[count].inbound_high = i2o_ptr_high(c->post_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 sucess or negative error code on failure.
+ */
+int i2o_status_get(struct i2o_controller *c)
+{
+ struct i2o_message __iomem *msg;
+ u32 m;
+ u8 *status_block;
+ unsigned long timeout;
+
+ status_block = (u8 *) c->status_block.virt;
+ memset(status_block, 0, sizeof(i2o_status_block));
+
+ m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY)
+ return -ETIMEDOUT;
+
+ writel(NINE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]);
+ writel(I2O_CMD_STATUS_GET << 24 | HOST_TID << 12 | ADAPTER_TID,
+ &msg->u.head[1]);
+ writel(i2o_exec_driver.context, &msg->u.s.icntxt);
+ writel(0, &msg->u.s.tcntxt); // FIXME: use resonable transaction context
+ writel(0, &msg->body[0]);
+ writel(0, &msg->body[1]);
+ writel(i2o_ptr_low((void *)c->status_block.phys), &msg->body[2]);
+ writel(i2o_ptr_high((void *)c->status_block.phys), &msg->body[3]);
+ writel(sizeof(i2o_status_block), &msg->body[4]); /* always 88 bytes */
+
+ i2o_msg_post(c, m);
+
+ /* Wait for a reply */
+ timeout = jiffies + I2O_TIMEOUT_STATUS_GET * HZ;
+ while (status_block[87] != 0xFF) {
+ if (time_after(jiffies, timeout)) {
+ printk(KERN_ERR "%s: Get status timeout.\n", c->name);
+ return -ETIMEDOUT;
+ }
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(1);
+
+ rmb();
+ }
+
+#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 negativer 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 __iomem *msg;
+ u32 m;
+
+ m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY)
+ return -ETIMEDOUT;
+
+ writel(SIX_WORD_MSG_SIZE | SGL_OFFSET_4, &msg->u.head[0]);
+ writel(I2O_CMD_HRT_GET << 24 | HOST_TID << 12 | ADAPTER_TID,
+ &msg->u.head[1]);
+ writel(0xd0000000 | c->hrt.len, &msg->body[0]);
+ writel(c->hrt.phys, &msg->body[1]);
+
+ rc = i2o_msg_post_wait_mem(c, m, 20, &c->hrt);
+
+ if (rc < 0) {
+ printk(KERN_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, GFP_KERNEL))
+ return -ENOMEM;
+ else
+ hrt = c->hrt.virt;
+ } else
+ return i2o_parse_hrt(c);
+ }
+
+ printk(KERN_ERR "%s: Unable to get HRT after %d tries, giving up\n",
+ c->name, I2O_HRT_GET_TRIES);
+
+ return -EBUSY;
+}
+
+/**
+ * i2o_iop_alloc - Allocate and initialize a i2o_controller struct
+ *
+ * Allocate the necessary memory for a i2o_controller struct and
+ * initialize the lists.
+ *
+ * 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;
+
+ c = kmalloc(sizeof(*c), GFP_KERNEL);
+ if (!c) {
+ printk(KERN_ERR "i2o: Insufficient memory to allocate a I2O "
+ "controller.\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ memset(c, 0, sizeof(*c));
+
+ INIT_LIST_HEAD(&c->devices);
+ spin_lock_init(&c->lock);
+ init_MUTEX(&c->lct_lock);
+ c->unit = unit++;
+ sprintf(c->name, "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_free - Free the i2o_controller struct
+ * @c: I2O controller to free
+ */
+void i2o_iop_free(struct i2o_controller *c)
+{
+ kfree(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;
+
+ printk(KERN_INFO "%s: Activating I2O controller...\n", c->name);
+ printk(KERN_INFO "%s: This may take a few minutes if there are many "
+ "devices\n", c->name);
+
+ if ((rc = i2o_iop_activate(c))) {
+ printk(KERN_ERR "%s: could not activate controller\n",
+ c->name);
+ i2o_iop_reset(c);
+ return rc;
+ }
+
+ pr_debug("%s: building sys table...\n", c->name);
+
+ if ((rc = i2o_systab_build())) {
+ i2o_iop_reset(c);
+ return rc;
+ }
+
+ pr_debug("%s: online controller...\n", c->name);
+
+ if ((rc = i2o_iop_online(c))) {
+ i2o_iop_reset(c);
+ return rc;
+ }
+
+ pr_debug("%s: getting LCT...\n", c->name);
+
+ if ((rc = i2o_exec_lct_get(c))) {
+ i2o_iop_reset(c);
+ return rc;
+ }
+
+ list_add(&c->list, &i2o_controllers);
+
+ i2o_driver_notify_controller_add_all(c);
+
+ printk(KERN_INFO "%s: Controller added\n", c->name);
+
+ return 0;
+};
+
+/**
+ * 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 -ETIMEDOUT if no message could be fetched for
+ * sending the request.
+ */
+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 __iomem *msg;
+ u32 m;
+
+ m = i2o_msg_get_wait(c, &msg, I2O_TIMEOUT_MESSAGE_GET);
+ if (m == I2O_QUEUE_EMPTY)
+ return -ETIMEDOUT;
+
+ writel(FIVE_WORD_MSG_SIZE | SGL_OFFSET_0, &msg->u.head[0]);
+ writel(I2O_CMD_UTIL_EVT_REGISTER << 24 | HOST_TID << 12 | dev->lct_data.
+ tid, &msg->u.head[1]);
+ writel(drv->context, &msg->u.s.icntxt);
+ writel(tcntxt, &msg->u.s.tcntxt);
+ writel(evt_mask, &msg->body[0]);
+
+ i2o_msg_post(c, m);
+
+ 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");
+
+ rc = i2o_device_init();
+ if (rc)
+ goto exit;
+
+ rc = i2o_driver_init();
+ if (rc)
+ goto device_exit;
+
+ rc = i2o_exec_init();
+ if (rc)
+ goto driver_exit;
+
+ rc = i2o_pci_init();
+ if (rc < 0)
+ goto exec_exit;
+
+ return 0;
+
+ exec_exit:
+ i2o_exec_exit();
+
+ driver_exit:
+ i2o_driver_exit();
+
+ device_exit:
+ i2o_device_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();
+ i2o_device_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_msg_nop);
+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/message/i2o/pci.c b/drivers/message/i2o/pci.c
new file mode 100644
index 000000000000..e772752f056d
--- /dev/null
+++ b/drivers/message/i2o/pci.c
@@ -0,0 +1,528 @@
+/*
+ * 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@redhat.com>:
+ * 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/i2o.h>
+
+#ifdef CONFIG_MTRR
+#include <asm/mtrr.h>
+#endif // CONFIG_MTRR
+
+/* Module internal functions from other sources */
+extern struct i2o_controller *i2o_iop_alloc(void);
+extern void i2o_iop_free(struct i2o_controller *);
+
+extern int i2o_iop_add(struct i2o_controller *);
+extern void i2o_iop_remove(struct i2o_controller *);
+
+extern int i2o_driver_dispatch(struct i2o_controller *, u32,
+ struct i2o_message *);
+
+/* PCI device id table for all I2O controllers */
+static struct pci_device_id __devinitdata i2o_pci_ids[] = {
+ {PCI_DEVICE_CLASS(PCI_CLASS_INTELLIGENT_I2O << 8, 0xffff00)},
+ {PCI_DEVICE(PCI_VENDOR_ID_DPT, 0xa511)},
+ {0}
+};
+
+/**
+ * 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
+ * @gfp_mask: GFP mask
+ *
+ * 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,
+ unsigned int gfp_mask)
+{
+ i2o_dma_free(dev, addr);
+
+ if (len)
+ return i2o_dma_alloc(dev, addr, len, gfp_mask);
+
+ return 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);
+ if (c->lct)
+ kfree(c->lct);
+ i2o_dma_free(dev, &c->dlct);
+ i2o_dma_free(dev, &c->hrt);
+ i2o_dma_free(dev, &c->status);
+
+#ifdef CONFIG_MTRR
+ if (c->mtrr_reg0 >= 0)
+ mtrr_del(c->mtrr_reg0, 0, 0);
+ if (c->mtrr_reg1 >= 0)
+ mtrr_del(c->mtrr_reg1, 0, 0);
+#endif
+
+ if (c->raptor && c->in_queue.virt)
+ iounmap(c->in_queue.virt);
+
+ if (c->base.virt)
+ iounmap(c->base.virt);
+}
+
+/**
+ * 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 __devinit i2o_pci_alloc(struct i2o_controller *c)
+{
+ struct pci_dev *pdev = c->pdev;
+ struct device *dev = &pdev->dev;
+ int i;
+
+ 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(c->base.phys, c->base.len);
+ if (!c->base.virt) {
+ printk(KERN_ERR "%s: Unable to map controller.\n", c->name);
+ return -ENOMEM;
+ }
+
+ if (c->raptor) {
+ c->in_queue.virt = ioremap(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_mask = c->base.virt + 0x34;
+ c->post_port = c->base.virt + 0x40;
+ c->reply_port = c->base.virt + 0x44;
+
+#ifdef CONFIG_MTRR
+ /* Enable Write Combining MTRR for IOP's memory region */
+ c->mtrr_reg0 = mtrr_add(c->in_queue.phys, c->in_queue.len,
+ MTRR_TYPE_WRCOMB, 1);
+ c->mtrr_reg1 = -1;
+
+ if (c->mtrr_reg0 < 0)
+ printk(KERN_WARNING "%s: could not enable write combining "
+ "MTRR\n", c->name);
+ else
+ printk(KERN_INFO "%s: using write combining MTRR\n", c->name);
+
+ /*
+ * If it is an INTEL i960 I/O processor then set the first 64K to
+ * Uncacheable since the region contains the messaging unit which
+ * shouldn't be cached.
+ */
+ if ((pdev->vendor == PCI_VENDOR_ID_INTEL ||
+ pdev->vendor == PCI_VENDOR_ID_DPT) && !c->raptor) {
+ printk(KERN_INFO "%s: MTRR workaround for Intel i960 processor"
+ "\n", c->name);
+ c->mtrr_reg1 = mtrr_add(c->base.phys, 0x10000,
+ MTRR_TYPE_UNCACHABLE, 1);
+
+ if (c->mtrr_reg1 < 0) {
+ printk(KERN_WARNING "%s: Error in setting "
+ "MTRR_TYPE_UNCACHABLE\n", c->name);
+ mtrr_del(c->mtrr_reg0, c->in_queue.phys,
+ c->in_queue.len);
+ c->mtrr_reg0 = -1;
+ }
+ }
+#endif
+
+ if (i2o_dma_alloc(dev, &c->status, 8, GFP_KERNEL)) {
+ i2o_pci_free(c);
+ return -ENOMEM;
+ }
+
+ if (i2o_dma_alloc(dev, &c->hrt, sizeof(i2o_hrt), GFP_KERNEL)) {
+ i2o_pci_free(c);
+ return -ENOMEM;
+ }
+
+ if (i2o_dma_alloc(dev, &c->dlct, 8192, GFP_KERNEL)) {
+ i2o_pci_free(c);
+ return -ENOMEM;
+ }
+
+ if (i2o_dma_alloc(dev, &c->status_block, sizeof(i2o_status_block),
+ GFP_KERNEL)) {
+ i2o_pci_free(c);
+ return -ENOMEM;
+ }
+
+ if (i2o_dma_alloc(dev, &c->out_queue, MSG_POOL_SIZE, GFP_KERNEL)) {
+ 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
+ * @r: pointer to registers
+ *
+ * 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 pt_regs *r)
+{
+ struct i2o_controller *c = dev_id;
+ struct device *dev = &c->pdev->dev;
+ struct i2o_message *m;
+ u32 mv;
+
+ /*
+ * Old 960 steppings had a bug in the I2O unit that caused
+ * the queue to appear empty when it wasn't.
+ */
+ mv = I2O_REPLY_READ32(c);
+ if (mv == I2O_QUEUE_EMPTY) {
+ mv = I2O_REPLY_READ32(c);
+ if (unlikely(mv == I2O_QUEUE_EMPTY)) {
+ return IRQ_NONE;
+ } else
+ pr_debug("%s: 960 bug detected\n", c->name);
+ }
+
+ while (mv != I2O_QUEUE_EMPTY) {
+ /*
+ * Map the message from the page frame map to kernel virtual.
+ * Because bus_to_virt is deprecated, we have calculate the
+ * location by ourself!
+ */
+ m = i2o_msg_out_to_virt(c, mv);
+
+ /*
+ * Ensure this message is seen coherently but cachably by
+ * the processor
+ */
+ dma_sync_single_for_cpu(dev, mv, MSG_FRAME_SIZE * 4,
+ PCI_DMA_FROMDEVICE);
+
+ /* dispatch it */
+ if (i2o_driver_dispatch(c, mv, m))
+ /* flush it if result != 0 */
+ i2o_flush_reply(c, mv);
+
+ /*
+ * That 960 bug again...
+ */
+ mv = I2O_REPLY_READ32(c);
+ if (mv == I2O_QUEUE_EMPTY)
+ mv = I2O_REPLY_READ32(c);
+ }
+ return IRQ_HANDLED;
+}
+
+/**
+ * i2o_pci_irq_enable - Allocate interrupt for I2O controller
+ *
+ * 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;
+
+ I2O_IRQ_WRITE32(c, 0xffffffff);
+
+ if (pdev->irq) {
+ rc = request_irq(pdev->irq, i2o_pci_interrupt, SA_SHIRQ,
+ c->name, c);
+ if (rc < 0) {
+ printk(KERN_ERR "%s: unable to allocate interrupt %d."
+ "\n", c->name, pdev->irq);
+ return rc;
+ }
+ }
+
+ I2O_IRQ_WRITE32(c, 0x00000000);
+
+ 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)
+{
+ I2O_IRQ_WRITE32(c, 0xffffffff);
+
+ if (c->pdev->irq > 0)
+ free_irq(c->pdev->irq, c);
+}
+
+/**
+ * i2o_pci_probe - Probe the PCI device for an I2O controller
+ * @dev: 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 __devinit i2o_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct i2o_controller *c;
+ int rc;
+
+ printk(KERN_INFO "i2o: Checking for PCI I2O controllers...\n");
+
+ if ((pdev->class & 0xff) > 1) {
+ printk(KERN_WARNING "i2o: I2O controller found but does not "
+ "support I2O 1.5 (skipping).\n");
+ return -ENODEV;
+ }
+
+ if ((rc = pci_enable_device(pdev))) {
+ printk(KERN_WARNING "i2o: I2O controller found but could not be"
+ " enabled.\n");
+ return rc;
+ }
+
+ printk(KERN_INFO "i2o: I2O controller found on bus %d at %d.\n",
+ pdev->bus->number, pdev->devfn);
+
+ if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
+ printk(KERN_WARNING "i2o: I2O controller on bus %d at %d: No "
+ "suitable DMA available!\n", pdev->bus->number,
+ pdev->devfn);
+ rc = -ENODEV;
+ goto disable;
+ }
+
+ pci_set_master(pdev);
+
+ c = i2o_iop_alloc();
+ if (IS_ERR(c)) {
+ printk(KERN_ERR "i2o: memory for I2O controller could not be "
+ "allocated\n");
+ rc = PTR_ERR(c);
+ goto disable;
+ }
+
+ c->pdev = pdev;
+ c->device = 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) {
+ c->promise = 1;
+ printk(KERN_INFO "%s: Promise workarounds activated.\n",
+ c->name);
+ }
+
+ /* 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 ((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;
+
+ 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 __devexit 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);
+
+ printk(KERN_INFO "%s: Controller removed.\n", c->name);
+
+ i2o_iop_free(c);
+ pci_disable_device(pdev);
+};
+
+/* PCI driver for I2O controller */
+static struct pci_driver i2o_pci_driver = {
+ .name = "I2O controller",
+ .id_table = i2o_pci_ids,
+ .probe = i2o_pci_probe,
+ .remove = __devexit_p(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);
+};
+
+EXPORT_SYMBOL(i2o_dma_realloc);
+MODULE_DEVICE_TABLE(pci, i2o_pci_ids);
OpenPOWER on IntegriCloud