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