diff options
-rw-r--r-- | drivers/scsi/Kconfig | 1 | ||||
-rw-r--r-- | drivers/scsi/Makefile | 1 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/Kconfig | 66 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/Makefile | 7 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpi/mpi2.h | 1067 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h | 2151 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpi/mpi2_init.h | 420 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpi/mpi2_ioc.h | 1295 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpi/mpi2_raid.h | 295 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpi/mpi2_sas.h | 282 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpi/mpi2_tool.h | 249 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpi/mpi2_type.h | 61 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_base.c | 3435 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_base.h | 779 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_config.c | 1873 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_ctl.c | 2516 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_ctl.h | 416 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_debug.h | 181 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_scsih.c | 5687 | ||||
-rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_transport.c | 1211 |
20 files changed, 21993 insertions, 0 deletions
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index e420ad0acebf..e2f44e6c0bcb 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -571,6 +571,7 @@ config SCSI_ARCMSR_AER To enable this function, choose Y here. source "drivers/scsi/megaraid/Kconfig.megaraid" +source "drivers/scsi/mpt2sas/Kconfig" config SCSI_HPTIOP tristate "HighPoint RocketRAID 3xxx/4xxx Controller support" diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 05558b170419..cf7929634668 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -99,6 +99,7 @@ obj-$(CONFIG_SCSI_DC390T) += tmscsim.o obj-$(CONFIG_MEGARAID_LEGACY) += megaraid.o obj-$(CONFIG_MEGARAID_NEWGEN) += megaraid/ obj-$(CONFIG_MEGARAID_SAS) += megaraid/ +obj-$(CONFIG_SCSI_MPT2SAS) += mpt2sas/ obj-$(CONFIG_SCSI_ACARD) += atp870u.o obj-$(CONFIG_SCSI_SUNESP) += esp_scsi.o sun_esp.o obj-$(CONFIG_SCSI_GDTH) += gdth.o diff --git a/drivers/scsi/mpt2sas/Kconfig b/drivers/scsi/mpt2sas/Kconfig new file mode 100644 index 000000000000..4a86855c23b3 --- /dev/null +++ b/drivers/scsi/mpt2sas/Kconfig @@ -0,0 +1,66 @@ +# +# Kernel configuration file for the MPT2SAS +# +# This code is based on drivers/scsi/mpt2sas/Kconfig +# Copyright (C) 2007-2008 LSI Corporation +# (mailto:DL-MPTFusionLinux@lsi.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. + +# 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. + +# NO WARRANTY +# THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT +# LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, +# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is +# solely responsible for determining the appropriateness of using and +# distributing the Program and assumes all risks associated with its +# exercise of rights under this Agreement, including but not limited to +# the risks and costs of program errors, damage to or loss of data, +# programs or equipment, and unavailability or interruption of operations. + +# DISCLAIMER OF LIABILITY +# NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED +# HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +# USA. + +config SCSI_MPT2SAS + tristate "LSI MPT Fusion SAS 2.0 Device Driver" + depends on PCI && SCSI + select SCSI_SAS_ATTRS + ---help--- + This driver supports PCI-Express SAS 6Gb/s Host Adapters. + +config SCSI_MPT2SAS_MAX_SGE + int "LSI MPT Fusion Max number of SG Entries (16 - 128)" + depends on PCI && SCSI && SCSI_MPT2SAS + default "128" + range 16 128 + ---help--- + This option allows you to specify the maximum number of scatter- + gather entries per I/O. The driver default is 128, which matches + SAFE_PHYS_SEGMENTS. However, it may decreased down to 16. + Decreasing this parameter will reduce memory requirements + on a per controller instance. + +config SCSI_MPT2SAS_LOGGING + bool "LSI MPT Fusion logging facility" + depends on PCI && SCSI && SCSI_MPT2SAS + ---help--- + This turns on a logging facility. diff --git a/drivers/scsi/mpt2sas/Makefile b/drivers/scsi/mpt2sas/Makefile new file mode 100644 index 000000000000..728f0475711d --- /dev/null +++ b/drivers/scsi/mpt2sas/Makefile @@ -0,0 +1,7 @@ +# mpt2sas makefile +obj-$(CONFIG_SCSI_MPT2SAS) += mpt2sas.o +mpt2sas-y += mpt2sas_base.o \ + mpt2sas_config.o \ + mpt2sas_scsih.o \ + mpt2sas_transport.o \ + mpt2sas_ctl.o diff --git a/drivers/scsi/mpt2sas/mpi/mpi2.h b/drivers/scsi/mpt2sas/mpi/mpi2.h new file mode 100644 index 000000000000..7bb2ece8b2e4 --- /dev/null +++ b/drivers/scsi/mpt2sas/mpi/mpi2.h @@ -0,0 +1,1067 @@ +/* + * Copyright (c) 2000-2009 LSI Corporation. + * + * + * Name: mpi2.h + * Title: MPI Message independent structures and definitions + * including System Interface Register Set and + * scatter/gather formats. + * Creation Date: June 21, 2006 + * + * mpi2.h Version: 02.00.11 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * 06-04-07 02.00.01 Bumped MPI2_HEADER_VERSION_UNIT. + * 06-26-07 02.00.02 Bumped MPI2_HEADER_VERSION_UNIT. + * 08-31-07 02.00.03 Bumped MPI2_HEADER_VERSION_UNIT. + * Moved ReplyPostHostIndex register to offset 0x6C of the + * MPI2_SYSTEM_INTERFACE_REGS and modified the define for + * MPI2_REPLY_POST_HOST_INDEX_OFFSET. + * Added union of request descriptors. + * Added union of reply descriptors. + * 10-31-07 02.00.04 Bumped MPI2_HEADER_VERSION_UNIT. + * Added define for MPI2_VERSION_02_00. + * Fixed the size of the FunctionDependent5 field in the + * MPI2_DEFAULT_REPLY structure. + * 12-18-07 02.00.05 Bumped MPI2_HEADER_VERSION_UNIT. + * Removed the MPI-defined Fault Codes and extended the + * product specific codes up to 0xEFFF. + * Added a sixth key value for the WriteSequence register + * and changed the flush value to 0x0. + * Added message function codes for Diagnostic Buffer Post + * and Diagnsotic Release. + * New IOCStatus define: MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED + * Moved MPI2_VERSION_UNION from mpi2_ioc.h. + * 02-29-08 02.00.06 Bumped MPI2_HEADER_VERSION_UNIT. + * 03-03-08 02.00.07 Bumped MPI2_HEADER_VERSION_UNIT. + * 05-21-08 02.00.08 Bumped MPI2_HEADER_VERSION_UNIT. + * Added #defines for marking a reply descriptor as unused. + * 06-27-08 02.00.09 Bumped MPI2_HEADER_VERSION_UNIT. + * 10-02-08 02.00.10 Bumped MPI2_HEADER_VERSION_UNIT. + * Moved LUN field defines from mpi2_init.h. + * 01-19-09 02.00.11 Bumped MPI2_HEADER_VERSION_UNIT. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI2_H +#define MPI2_H + + +/***************************************************************************** +* +* MPI Version Definitions +* +*****************************************************************************/ + +#define MPI2_VERSION_MAJOR (0x02) +#define MPI2_VERSION_MINOR (0x00) +#define MPI2_VERSION_MAJOR_MASK (0xFF00) +#define MPI2_VERSION_MAJOR_SHIFT (8) +#define MPI2_VERSION_MINOR_MASK (0x00FF) +#define MPI2_VERSION_MINOR_SHIFT (0) +#define MPI2_VERSION ((MPI2_VERSION_MAJOR << MPI2_VERSION_MAJOR_SHIFT) | \ + MPI2_VERSION_MINOR) + +#define MPI2_VERSION_02_00 (0x0200) + +/* versioning for this MPI header set */ +#define MPI2_HEADER_VERSION_UNIT (0x0B) +#define MPI2_HEADER_VERSION_DEV (0x00) +#define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00) +#define MPI2_HEADER_VERSION_UNIT_SHIFT (8) +#define MPI2_HEADER_VERSION_DEV_MASK (0x00FF) +#define MPI2_HEADER_VERSION_DEV_SHIFT (0) +#define MPI2_HEADER_VERSION ((MPI2_HEADER_VERSION_UNIT << 8) | MPI2_HEADER_VERSION_DEV) + + +/***************************************************************************** +* +* IOC State Definitions +* +*****************************************************************************/ + +#define MPI2_IOC_STATE_RESET (0x00000000) +#define MPI2_IOC_STATE_READY (0x10000000) +#define MPI2_IOC_STATE_OPERATIONAL (0x20000000) +#define MPI2_IOC_STATE_FAULT (0x40000000) + +#define MPI2_IOC_STATE_MASK (0xF0000000) +#define MPI2_IOC_STATE_SHIFT (28) + +/* Fault state range for prodcut specific codes */ +#define MPI2_FAULT_PRODUCT_SPECIFIC_MIN (0x0000) +#define MPI2_FAULT_PRODUCT_SPECIFIC_MAX (0xEFFF) + + +/***************************************************************************** +* +* System Interface Register Definitions +* +*****************************************************************************/ + +typedef volatile struct _MPI2_SYSTEM_INTERFACE_REGS +{ + U32 Doorbell; /* 0x00 */ + U32 WriteSequence; /* 0x04 */ + U32 HostDiagnostic; /* 0x08 */ + U32 Reserved1; /* 0x0C */ + U32 DiagRWData; /* 0x10 */ + U32 DiagRWAddressLow; /* 0x14 */ + U32 DiagRWAddressHigh; /* 0x18 */ + U32 Reserved2[5]; /* 0x1C */ + U32 HostInterruptStatus; /* 0x30 */ + U32 HostInterruptMask; /* 0x34 */ + U32 DCRData; /* 0x38 */ + U32 DCRAddress; /* 0x3C */ + U32 Reserved3[2]; /* 0x40 */ + U32 ReplyFreeHostIndex; /* 0x48 */ + U32 Reserved4[8]; /* 0x4C */ + U32 ReplyPostHostIndex; /* 0x6C */ + U32 Reserved5; /* 0x70 */ + U32 HCBSize; /* 0x74 */ + U32 HCBAddressLow; /* 0x78 */ + U32 HCBAddressHigh; /* 0x7C */ + U32 Reserved6[16]; /* 0x80 */ + U32 RequestDescriptorPostLow; /* 0xC0 */ + U32 RequestDescriptorPostHigh; /* 0xC4 */ + U32 Reserved7[14]; /* 0xC8 */ +} MPI2_SYSTEM_INTERFACE_REGS, MPI2_POINTER PTR_MPI2_SYSTEM_INTERFACE_REGS, + Mpi2SystemInterfaceRegs_t, MPI2_POINTER pMpi2SystemInterfaceRegs_t; + +/* + * Defines for working with the Doorbell register. + */ +#define MPI2_DOORBELL_OFFSET (0x00000000) + +/* IOC --> System values */ +#define MPI2_DOORBELL_USED (0x08000000) +#define MPI2_DOORBELL_WHO_INIT_MASK (0x07000000) +#define MPI2_DOORBELL_WHO_INIT_SHIFT (24) +#define MPI2_DOORBELL_FAULT_CODE_MASK (0x0000FFFF) +#define MPI2_DOORBELL_DATA_MASK (0x0000FFFF) + +/* System --> IOC values */ +#define MPI2_DOORBELL_FUNCTION_MASK (0xFF000000) +#define MPI2_DOORBELL_FUNCTION_SHIFT (24) +#define MPI2_DOORBELL_ADD_DWORDS_MASK (0x00FF0000) +#define MPI2_DOORBELL_ADD_DWORDS_SHIFT (16) + + +/* + * Defines for the WriteSequence register + */ +#define MPI2_WRITE_SEQUENCE_OFFSET (0x00000004) +#define MPI2_WRSEQ_KEY_VALUE_MASK (0x0000000F) +#define MPI2_WRSEQ_FLUSH_KEY_VALUE (0x0) +#define MPI2_WRSEQ_1ST_KEY_VALUE (0xF) +#define MPI2_WRSEQ_2ND_KEY_VALUE (0x4) +#define MPI2_WRSEQ_3RD_KEY_VALUE (0xB) +#define MPI2_WRSEQ_4TH_KEY_VALUE (0x2) +#define MPI2_WRSEQ_5TH_KEY_VALUE (0x7) +#define MPI2_WRSEQ_6TH_KEY_VALUE (0xD) + +/* + * Defines for the HostDiagnostic register + */ +#define MPI2_HOST_DIAGNOSTIC_OFFSET (0x00000008) + +#define MPI2_DIAG_BOOT_DEVICE_SELECT_MASK (0x00001800) +#define MPI2_DIAG_BOOT_DEVICE_SELECT_DEFAULT (0x00000000) +#define MPI2_DIAG_BOOT_DEVICE_SELECT_HCDW (0x00000800) + +#define MPI2_DIAG_CLEAR_FLASH_BAD_SIG (0x00000400) +#define MPI2_DIAG_FORCE_HCB_ON_RESET (0x00000200) +#define MPI2_DIAG_HCB_MODE (0x00000100) +#define MPI2_DIAG_DIAG_WRITE_ENABLE (0x00000080) +#define MPI2_DIAG_FLASH_BAD_SIG (0x00000040) +#define MPI2_DIAG_RESET_HISTORY (0x00000020) +#define MPI2_DIAG_DIAG_RW_ENABLE (0x00000010) +#define MPI2_DIAG_RESET_ADAPTER (0x00000004) +#define MPI2_DIAG_HOLD_IOC_RESET (0x00000002) + +/* + * Offsets for DiagRWData and address + */ +#define MPI2_DIAG_RW_DATA_OFFSET (0x00000010) +#define MPI2_DIAG_RW_ADDRESS_LOW_OFFSET (0x00000014) +#define MPI2_DIAG_RW_ADDRESS_HIGH_OFFSET (0x00000018) + +/* + * Defines for the HostInterruptStatus register + */ +#define MPI2_HOST_INTERRUPT_STATUS_OFFSET (0x00000030) +#define MPI2_HIS_SYS2IOC_DB_STATUS (0x80000000) +#define MPI2_HIS_IOP_DOORBELL_STATUS MPI2_HIS_SYS2IOC_DB_STATUS +#define MPI2_HIS_RESET_IRQ_STATUS (0x40000000) +#define MPI2_HIS_REPLY_DESCRIPTOR_INTERRUPT (0x00000008) +#define MPI2_HIS_IOC2SYS_DB_STATUS (0x00000001) +#define MPI2_HIS_DOORBELL_INTERRUPT MPI2_HIS_IOC2SYS_DB_STATUS + +/* + * Defines for the HostInterruptMask register + */ +#define MPI2_HOST_INTERRUPT_MASK_OFFSET (0x00000034) +#define MPI2_HIM_RESET_IRQ_MASK (0x40000000) +#define MPI2_HIM_REPLY_INT_MASK (0x00000008) +#define MPI2_HIM_RIM MPI2_HIM_REPLY_INT_MASK +#define MPI2_HIM_IOC2SYS_DB_MASK (0x00000001) +#define MPI2_HIM_DIM MPI2_HIM_IOC2SYS_DB_MASK + +/* + * Offsets for DCRData and address + */ +#define MPI2_DCR_DATA_OFFSET (0x00000038) +#define MPI2_DCR_ADDRESS_OFFSET (0x0000003C) + +/* + * Offset for the Reply Free Queue + */ +#define MPI2_REPLY_FREE_HOST_INDEX_OFFSET (0x00000048) + +/* + * Offset for the Reply Descriptor Post Queue + */ +#define MPI2_REPLY_POST_HOST_INDEX_OFFSET (0x0000006C) + +/* + * Defines for the HCBSize and address + */ +#define MPI2_HCB_SIZE_OFFSET (0x00000074) +#define MPI2_HCB_SIZE_SIZE_MASK (0xFFFFF000) +#define MPI2_HCB_SIZE_HCB_ENABLE (0x00000001) + +#define MPI2_HCB_ADDRESS_LOW_OFFSET (0x00000078) +#define MPI2_HCB_ADDRESS_HIGH_OFFSET (0x0000007C) + +/* + * Offsets for the Request Queue + */ +#define MPI2_REQUEST_DESCRIPTOR_POST_LOW_OFFSET (0x000000C0) +#define MPI2_REQUEST_DESCRIPTOR_POST_HIGH_OFFSET (0x000000C4) + + +/***************************************************************************** +* +* Message Descriptors +* +*****************************************************************************/ + +/* Request Descriptors */ + +/* Default Request Descriptor */ +typedef struct _MPI2_DEFAULT_REQUEST_DESCRIPTOR +{ + U8 RequestFlags; /* 0x00 */ + U8 VF_ID; /* 0x01 */ + U16 SMID; /* 0x02 */ + U16 LMID; /* 0x04 */ + U16 DescriptorTypeDependent; /* 0x06 */ +} MPI2_DEFAULT_REQUEST_DESCRIPTOR, + MPI2_POINTER PTR_MPI2_DEFAULT_REQUEST_DESCRIPTOR, + Mpi2DefaultRequestDescriptor_t, MPI2_POINTER pMpi2DefaultRequestDescriptor_t; + +/* defines for the RequestFlags field */ +#define MPI2_REQ_DESCRIPT_FLAGS_TYPE_MASK (0x0E) +#define MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO (0x00) +#define MPI2_REQ_DESCRIPT_FLAGS_SCSI_TARGET (0x02) +#define MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY (0x06) +#define MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE (0x08) + +#define MPI2_REQ_DESCRIPT_FLAGS_IOC_FIFO_MARKER (0x01) + + +/* High Priority Request Descriptor */ +typedef struct _MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR +{ + U8 RequestFlags; /* 0x00 */ + U8 VF_ID; /* 0x01 */ + U16 SMID; /* 0x02 */ + U16 LMID; /* 0x04 */ + U16 Reserved1; /* 0x06 */ +} MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR, + MPI2_POINTER PTR_MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR, + Mpi2HighPriorityRequestDescriptor_t, + MPI2_POINTER pMpi2HighPriorityRequestDescriptor_t; + + +/* SCSI IO Request Descriptor */ +typedef struct _MPI2_SCSI_IO_REQUEST_DESCRIPTOR +{ + U8 RequestFlags; /* 0x00 */ + U8 VF_ID; /* 0x01 */ + U16 SMID; /* 0x02 */ + U16 LMID; /* 0x04 */ + U16 DevHandle; /* 0x06 */ +} MPI2_SCSI_IO_REQUEST_DESCRIPTOR, + MPI2_POINTER PTR_MPI2_SCSI_IO_REQUEST_DESCRIPTOR, + Mpi2SCSIIORequestDescriptor_t, MPI2_POINTER pMpi2SCSIIORequestDescriptor_t; + + +/* SCSI Target Request Descriptor */ +typedef struct _MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR +{ + U8 RequestFlags; /* 0x00 */ + U8 VF_ID; /* 0x01 */ + U16 SMID; /* 0x02 */ + U16 LMID; /* 0x04 */ + U16 IoIndex; /* 0x06 */ +} MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR, + MPI2_POINTER PTR_MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR, + Mpi2SCSITargetRequestDescriptor_t, + MPI2_POINTER pMpi2SCSITargetRequestDescriptor_t; + +/* union of Request Descriptors */ +typedef union _MPI2_REQUEST_DESCRIPTOR_UNION +{ + MPI2_DEFAULT_REQUEST_DESCRIPTOR Default; + MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR HighPriority; + MPI2_SCSI_IO_REQUEST_DESCRIPTOR SCSIIO; + MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR SCSITarget; + U64 Words; +} MPI2_REQUEST_DESCRIPTOR_UNION, MPI2_POINTER PTR_MPI2_REQUEST_DESCRIPTOR_UNION, + Mpi2RequestDescriptorUnion_t, MPI2_POINTER pMpi2RequestDescriptorUnion_t; + + +/* Reply Descriptors */ + +/* Default Reply Descriptor */ +typedef struct _MPI2_DEFAULT_REPLY_DESCRIPTOR +{ + U8 ReplyFlags; /* 0x00 */ + U8 VF_ID; /* 0x01 */ + U16 DescriptorTypeDependent1; /* 0x02 */ + U32 DescriptorTypeDependent2; /* 0x04 */ +} MPI2_DEFAULT_REPLY_DESCRIPTOR, MPI2_POINTER PTR_MPI2_DEFAULT_REPLY_DESCRIPTOR, + Mpi2DefaultReplyDescriptor_t, MPI2_POINTER pMpi2DefaultReplyDescriptor_t; + +/* defines for the ReplyFlags field */ +#define MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK (0x0F) +#define MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS (0x00) +#define MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY (0x01) +#define MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS (0x02) +#define MPI2_RPY_DESCRIPT_FLAGS_TARGET_COMMAND_BUFFER (0x03) +#define MPI2_RPY_DESCRIPT_FLAGS_UNUSED (0x0F) + +/* values for marking a reply descriptor as unused */ +#define MPI2_RPY_DESCRIPT_UNUSED_WORD0_MARK (0xFFFFFFFF) +#define MPI2_RPY_DESCRIPT_UNUSED_WORD1_MARK (0xFFFFFFFF) + +/* Address Reply Descriptor */ +typedef struct _MPI2_ADDRESS_REPLY_DESCRIPTOR +{ + U8 ReplyFlags; /* 0x00 */ + U8 VF_ID; /* 0x01 */ + U16 SMID; /* 0x02 */ + U32 ReplyFrameAddress; /* 0x04 */ +} MPI2_ADDRESS_REPLY_DESCRIPTOR, MPI2_POINTER PTR_MPI2_ADDRESS_REPLY_DESCRIPTOR, + Mpi2AddressReplyDescriptor_t, MPI2_POINTER pMpi2AddressReplyDescriptor_t; + +#define MPI2_ADDRESS_REPLY_SMID_INVALID (0x00) + + +/* SCSI IO Success Reply Descriptor */ +typedef struct _MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR +{ + U8 ReplyFlags; /* 0x00 */ + U8 VF_ID; /* 0x01 */ + U16 SMID; /* 0x02 */ + U16 TaskTag; /* 0x04 */ + U16 DevHandle; /* 0x06 */ +} MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR, + MPI2_POINTER PTR_MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR, + Mpi2SCSIIOSuccessReplyDescriptor_t, + MPI2_POINTER pMpi2SCSIIOSuccessReplyDescriptor_t; + + +/* TargetAssist Success Reply Descriptor */ +typedef struct _MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR +{ + U8 ReplyFlags; /* 0x00 */ + U8 VF_ID; /* 0x01 */ + U16 SMID; /* 0x02 */ + U8 SequenceNumber; /* 0x04 */ + U8 Reserved1; /* 0x05 */ + U16 IoIndex; /* 0x06 */ +} MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR, + MPI2_POINTER PTR_MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR, + Mpi2TargetAssistSuccessReplyDescriptor_t, + MPI2_POINTER pMpi2TargetAssistSuccessReplyDescriptor_t; + + +/* Target Command Buffer Reply Descriptor */ +typedef struct _MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR +{ + U8 ReplyFlags; /* 0x00 */ + U8 VF_ID; /* 0x01 */ + U8 VP_ID; /* 0x02 */ + U8 Flags; /* 0x03 */ + U16 InitiatorDevHandle; /* 0x04 */ + U16 IoIndex; /* 0x06 */ +} MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR, + MPI2_POINTER PTR_MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR, + Mpi2TargetCommandBufferReplyDescriptor_t, + MPI2_POINTER pMpi2TargetCommandBufferReplyDescriptor_t; + +/* defines for Flags field */ +#define MPI2_RPY_DESCRIPT_TCB_FLAGS_PHYNUM_MASK (0x3F) + + +/* union of Reply Descriptors */ +typedef union _MPI2_REPLY_DESCRIPTORS_UNION +{ + MPI2_DEFAULT_REPLY_DESCRIPTOR Default; + MPI2_ADDRESS_REPLY_DESCRIPTOR AddressReply; + MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR SCSIIOSuccess; + MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR TargetAssistSuccess; + MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR TargetCommandBuffer; + U64 Words; +} MPI2_REPLY_DESCRIPTORS_UNION, MPI2_POINTER PTR_MPI2_REPLY_DESCRIPTORS_UNION, + Mpi2ReplyDescriptorsUnion_t, MPI2_POINTER pMpi2ReplyDescriptorsUnion_t; + + + +/***************************************************************************** +* +* Message Functions +* 0x80 -> 0x8F reserved for private message use per product +* +* +*****************************************************************************/ + +#define MPI2_FUNCTION_SCSI_IO_REQUEST (0x00) /* SCSI IO */ +#define MPI2_FUNCTION_SCSI_TASK_MGMT (0x01) /* SCSI Task Management */ +#define MPI2_FUNCTION_IOC_INIT (0x02) /* IOC Init */ +#define MPI2_FUNCTION_IOC_FACTS (0x03) /* IOC Facts */ +#define MPI2_FUNCTION_CONFIG (0x04) /* Configuration */ +#define MPI2_FUNCTION_PORT_FACTS (0x05) /* Port Facts */ +#define MPI2_FUNCTION_PORT_ENABLE (0x06) /* Port Enable */ +#define MPI2_FUNCTION_EVENT_NOTIFICATION (0x07) /* Event Notification */ +#define MPI2_FUNCTION_EVENT_ACK (0x08) /* Event Acknowledge */ +#define MPI2_FUNCTION_FW_DOWNLOAD (0x09) /* FW Download */ +#define MPI2_FUNCTION_TARGET_ASSIST (0x0B) /* Target Assist */ +#define MPI2_FUNCTION_TARGET_STATUS_SEND (0x0C) /* Target Status Send */ +#define MPI2_FUNCTION_TARGET_MODE_ABORT (0x0D) /* Target Mode Abort */ +#define MPI2_FUNCTION_FW_UPLOAD (0x12) /* FW Upload */ +#define MPI2_FUNCTION_RAID_ACTION (0x15) /* RAID Action */ +#define MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH (0x16) /* SCSI IO RAID Passthrough */ +#define MPI2_FUNCTION_TOOLBOX (0x17) /* Toolbox */ +#define MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR (0x18) /* SCSI Enclosure Processor */ +#define MPI2_FUNCTION_SMP_PASSTHROUGH (0x1A) /* SMP Passthrough */ +#define MPI2_FUNCTION_SAS_IO_UNIT_CONTROL (0x1B) /* SAS IO Unit Control */ +#define MPI2_FUNCTION_SATA_PASSTHROUGH (0x1C) /* SATA Passthrough */ +#define MPI2_FUNCTION_DIAG_BUFFER_POST (0x1D) /* Diagnostic Buffer Post */ +#define MPI2_FUNCTION_DIAG_RELEASE (0x1E) /* Diagnostic Release */ +#define MPI2_FUNCTION_TARGET_CMD_BUF_BASE_POST (0x24) /* Target Command Buffer Post Base */ +#define MPI2_FUNCTION_TARGET_CMD_BUF_LIST_POST (0x25) /* Target Command Buffer Post List */ + + + +/* Doorbell functions */ +#define MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET (0x40) +/* #define MPI2_FUNCTION_IO_UNIT_RESET (0x41) */ +#define MPI2_FUNCTION_HANDSHAKE (0x42) + + +/***************************************************************************** +* +* IOC Status Values +* +*****************************************************************************/ + +/* mask for IOCStatus status value */ +#define MPI2_IOCSTATUS_MASK (0x7FFF) + +/**************************************************************************** +* Common IOCStatus values for all replies +****************************************************************************/ + +#define MPI2_IOCSTATUS_SUCCESS (0x0000) +#define MPI2_IOCSTATUS_INVALID_FUNCTION (0x0001) +#define MPI2_IOCSTATUS_BUSY (0x0002) +#define MPI2_IOCSTATUS_INVALID_SGL (0x0003) +#define MPI2_IOCSTATUS_INTERNAL_ERROR (0x0004) +#define MPI2_IOCSTATUS_INVALID_VPID (0x0005) +#define MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES (0x0006) +#define MPI2_IOCSTATUS_INVALID_FIELD (0x0007) +#define MPI2_IOCSTATUS_INVALID_STATE (0x0008) +#define MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED (0x0009) + +/**************************************************************************** +* Config IOCStatus values +****************************************************************************/ + +#define MPI2_IOCSTATUS_CONFIG_INVALID_ACTION (0x0020) +#define MPI2_IOCSTATUS_CONFIG_INVALID_TYPE (0x0021) +#define MPI2_IOCSTATUS_CONFIG_INVALID_PAGE (0x0022) +#define MPI2_IOCSTATUS_CONFIG_INVALID_DATA (0x0023) +#define MPI2_IOCSTATUS_CONFIG_NO_DEFAULTS (0x0024) +#define MPI2_IOCSTATUS_CONFIG_CANT_COMMIT (0x0025) + +/**************************************************************************** +* SCSI IO Reply +****************************************************************************/ + +#define MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR (0x0040) +#define MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE (0x0042) +#define MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE (0x0043) +#define MPI2_IOCSTATUS_SCSI_DATA_OVERRUN (0x0044) +#define MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN (0x0045) +#define MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR (0x0046) +#define MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR (0x0047) +#define MPI2_IOCSTATUS_SCSI_TASK_TERMINATED (0x0048) +#define MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH (0x0049) +#define MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED (0x004A) +#define MPI2_IOCSTATUS_SCSI_IOC_TERMINATED (0x004B) +#define MPI2_IOCSTATUS_SCSI_EXT_TERMINATED (0x004C) + +/**************************************************************************** +* For use by SCSI Initiator and SCSI Target end-to-end data protection +****************************************************************************/ + +#define MPI2_IOCSTATUS_EEDP_GUARD_ERROR (0x004D) +#define MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR (0x004E) +#define MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR (0x004F) + +/**************************************************************************** +* SCSI Target values +****************************************************************************/ + +#define MPI2_IOCSTATUS_TARGET_INVALID_IO_INDEX (0x0062) +#define MPI2_IOCSTATUS_TARGET_ABORTED (0x0063) +#define MPI2_IOCSTATUS_TARGET_NO_CONN_RETRYABLE (0x0064) +#define MPI2_IOCSTATUS_TARGET_NO_CONNECTION (0x0065) +#define MPI2_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH (0x006A) +#define MPI2_IOCSTATUS_TARGET_DATA_OFFSET_ERROR (0x006D) +#define MPI2_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA (0x006E) +#define MPI2_IOCSTATUS_TARGET_IU_TOO_SHORT (0x006F) +#define MPI2_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT (0x0070) +#define MPI2_IOCSTATUS_TARGET_NAK_RECEIVED (0x0071) + +/**************************************************************************** +* Serial Attached SCSI values +****************************************************************************/ + +#define MPI2_IOCSTATUS_SAS_SMP_REQUEST_FAILED (0x0090) +#define MPI2_IOCSTATUS_SAS_SMP_DATA_OVERRUN (0x0091) + +/**************************************************************************** +* Diagnostic Buffer Post / Diagnostic Release values +****************************************************************************/ + +#define MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED (0x00A0) + + +/**************************************************************************** +* IOCStatus flag to indicate that log info is available +****************************************************************************/ + +#define MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE (0x8000) + +/**************************************************************************** +* IOCLogInfo Types +****************************************************************************/ + +#define MPI2_IOCLOGINFO_TYPE_MASK (0xF0000000) +#define MPI2_IOCLOGINFO_TYPE_SHIFT (28) +#define MPI2_IOCLOGINFO_TYPE_NONE (0x0) +#define MPI2_IOCLOGINFO_TYPE_SCSI (0x1) +#define MPI2_IOCLOGINFO_TYPE_FC (0x2) +#define MPI2_IOCLOGINFO_TYPE_SAS (0x3) +#define MPI2_IOCLOGINFO_TYPE_ISCSI (0x4) +#define MPI2_IOCLOGINFO_LOG_DATA_MASK (0x0FFFFFFF) + + +/***************************************************************************** +* +* Standard Message Structures +* +*****************************************************************************/ + +/**************************************************************************** +* Request Message Header for all request messages +****************************************************************************/ + +typedef struct _MPI2_REQUEST_HEADER +{ + U16 FunctionDependent1; /* 0x00 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 FunctionDependent2; /* 0x04 */ + U8 FunctionDependent3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved1; /* 0x0A */ +} MPI2_REQUEST_HEADER, MPI2_POINTER PTR_MPI2_REQUEST_HEADER, + MPI2RequestHeader_t, MPI2_POINTER pMPI2RequestHeader_t; + + +/**************************************************************************** +* Default Reply +****************************************************************************/ + +typedef struct _MPI2_DEFAULT_REPLY +{ + U16 FunctionDependent1; /* 0x00 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 FunctionDependent2; /* 0x04 */ + U8 FunctionDependent3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved1; /* 0x0A */ + U16 FunctionDependent5; /* 0x0C */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ +} MPI2_DEFAULT_REPLY, MPI2_POINTER PTR_MPI2_DEFAULT_REPLY, + MPI2DefaultReply_t, MPI2_POINTER pMPI2DefaultReply_t; + + +/* common version structure/union used in messages and configuration pages */ + +typedef struct _MPI2_VERSION_STRUCT +{ + U8 Dev; /* 0x00 */ + U8 Unit; /* 0x01 */ + U8 Minor; /* 0x02 */ + U8 Major; /* 0x03 */ +} MPI2_VERSION_STRUCT; + +typedef union _MPI2_VERSION_UNION +{ + MPI2_VERSION_STRUCT Struct; + U32 Word; +} MPI2_VERSION_UNION; + + +/* LUN field defines, common to many structures */ +#define MPI2_LUN_FIRST_LEVEL_ADDRESSING (0x0000FFFF) +#define MPI2_LUN_SECOND_LEVEL_ADDRESSING (0xFFFF0000) +#define MPI2_LUN_THIRD_LEVEL_ADDRESSING (0x0000FFFF) +#define MPI2_LUN_FOURTH_LEVEL_ADDRESSING (0xFFFF0000) +#define MPI2_LUN_LEVEL_1_WORD (0xFF00) +#define MPI2_LUN_LEVEL_1_DWORD (0x0000FF00) + + +/***************************************************************************** +* +* Fusion-MPT MPI Scatter Gather Elements +* +*****************************************************************************/ + +/**************************************************************************** +* MPI Simple Element structures +****************************************************************************/ + +typedef struct _MPI2_SGE_SIMPLE32 +{ + U32 FlagsLength; + U32 Address; +} MPI2_SGE_SIMPLE32, MPI2_POINTER PTR_MPI2_SGE_SIMPLE32, + Mpi2SGESimple32_t, MPI2_POINTER pMpi2SGESimple32_t; + +typedef struct _MPI2_SGE_SIMPLE64 +{ + U32 FlagsLength; + U64 Address; +} MPI2_SGE_SIMPLE64, MPI2_POINTER PTR_MPI2_SGE_SIMPLE64, + Mpi2SGESimple64_t, MPI2_POINTER pMpi2SGESimple64_t; + +typedef struct _MPI2_SGE_SIMPLE_UNION +{ + U32 FlagsLength; + union + { + U32 Address32; + U64 Address64; + } u; +} MPI2_SGE_SIMPLE_UNION, MPI2_POINTER PTR_MPI2_SGE_SIMPLE_UNION, + Mpi2SGESimpleUnion_t, MPI2_POINTER pMpi2SGESimpleUnion_t; + + +/**************************************************************************** +* MPI Chain Element structures +****************************************************************************/ + +typedef struct _MPI2_SGE_CHAIN32 +{ + U16 Length; + U8 NextChainOffset; + U8 Flags; + U32 Address; +} MPI2_SGE_CHAIN32, MPI2_POINTER PTR_MPI2_SGE_CHAIN32, + Mpi2SGEChain32_t, MPI2_POINTER pMpi2SGEChain32_t; + +typedef struct _MPI2_SGE_CHAIN64 +{ + U16 Length; + U8 NextChainOffset; + U8 Flags; + U64 Address; +} MPI2_SGE_CHAIN64, MPI2_POINTER PTR_MPI2_SGE_CHAIN64, + Mpi2SGEChain64_t, MPI2_POINTER pMpi2SGEChain64_t; + +typedef struct _MPI2_SGE_CHAIN_UNION +{ + U16 Length; + U8 NextChainOffset; + U8 Flags; + union + { + U32 Address32; + U64 Address64; + } u; +} MPI2_SGE_CHAIN_UNION, MPI2_POINTER PTR_MPI2_SGE_CHAIN_UNION, + Mpi2SGEChainUnion_t, MPI2_POINTER pMpi2SGEChainUnion_t; + + +/**************************************************************************** +* MPI Transaction Context Element structures +****************************************************************************/ + +typedef struct _MPI2_SGE_TRANSACTION32 +{ + U8 Reserved; + U8 ContextSize; + U8 DetailsLength; + U8 Flags; + U32 TransactionContext[1]; + U32 TransactionDetails[1]; +} MPI2_SGE_TRANSACTION32, MPI2_POINTER PTR_MPI2_SGE_TRANSACTION32, + Mpi2SGETransaction32_t, MPI2_POINTER pMpi2SGETransaction32_t; + +typedef struct _MPI2_SGE_TRANSACTION64 +{ + U8 Reserved; + U8 ContextSize; + U8 DetailsLength; + U8 Flags; + U32 TransactionContext[2]; + U32 TransactionDetails[1]; +} MPI2_SGE_TRANSACTION64, MPI2_POINTER PTR_MPI2_SGE_TRANSACTION64, + Mpi2SGETransaction64_t, MPI2_POINTER pMpi2SGETransaction64_t; + +typedef struct _MPI2_SGE_TRANSACTION96 +{ + U8 Reserved; + U8 ContextSize; + U8 DetailsLength; + U8 Flags; + U32 TransactionContext[3]; + U32 TransactionDetails[1]; +} MPI2_SGE_TRANSACTION96, MPI2_POINTER PTR_MPI2_SGE_TRANSACTION96, + Mpi2SGETransaction96_t, MPI2_POINTER pMpi2SGETransaction96_t; + +typedef struct _MPI2_SGE_TRANSACTION128 +{ + U8 Reserved; + U8 ContextSize; + U8 DetailsLength; + U8 Flags; + U32 TransactionContext[4]; + U32 TransactionDetails[1]; +} MPI2_SGE_TRANSACTION128, MPI2_POINTER PTR_MPI2_SGE_TRANSACTION128, + Mpi2SGETransaction_t128, MPI2_POINTER pMpi2SGETransaction_t128; + +typedef struct _MPI2_SGE_TRANSACTION_UNION +{ + U8 Reserved; + U8 ContextSize; + U8 DetailsLength; + U8 Flags; + union + { + U32 TransactionContext32[1]; + U32 TransactionContext64[2]; + U32 TransactionContext96[3]; + U32 TransactionContext128[4]; + } u; + U32 TransactionDetails[1]; +} MPI2_SGE_TRANSACTION_UNION, MPI2_POINTER PTR_MPI2_SGE_TRANSACTION_UNION, + Mpi2SGETransactionUnion_t, MPI2_POINTER pMpi2SGETransactionUnion_t; + + +/**************************************************************************** +* MPI SGE union for IO SGL's +****************************************************************************/ + +typedef struct _MPI2_MPI_SGE_IO_UNION +{ + union + { + MPI2_SGE_SIMPLE_UNION Simple; + MPI2_SGE_CHAIN_UNION Chain; + } u; +} MPI2_MPI_SGE_IO_UNION, MPI2_POINTER PTR_MPI2_MPI_SGE_IO_UNION, + Mpi2MpiSGEIOUnion_t, MPI2_POINTER pMpi2MpiSGEIOUnion_t; + + +/**************************************************************************** +* MPI SGE union for SGL's with Simple and Transaction elements +****************************************************************************/ + +typedef struct _MPI2_SGE_TRANS_SIMPLE_UNION +{ + union + { + MPI2_SGE_SIMPLE_UNION Simple; + MPI2_SGE_TRANSACTION_UNION Transaction; + } u; +} MPI2_SGE_TRANS_SIMPLE_UNION, MPI2_POINTER PTR_MPI2_SGE_TRANS_SIMPLE_UNION, + Mpi2SGETransSimpleUnion_t, MPI2_POINTER pMpi2SGETransSimpleUnion_t; + + +/**************************************************************************** +* All MPI SGE types union +****************************************************************************/ + +typedef struct _MPI2_MPI_SGE_UNION +{ + union + { + MPI2_SGE_SIMPLE_UNION Simple; + MPI2_SGE_CHAIN_UNION Chain; + MPI2_SGE_TRANSACTION_UNION Transaction; + } u; +} MPI2_MPI_SGE_UNION, MPI2_POINTER PTR_MPI2_MPI_SGE_UNION, + Mpi2MpiSgeUnion_t, MPI2_POINTER pMpi2MpiSgeUnion_t; + + +/**************************************************************************** +* MPI SGE field definition and masks +****************************************************************************/ + +/* Flags field bit definitions */ + +#define MPI2_SGE_FLAGS_LAST_ELEMENT (0x80) +#define MPI2_SGE_FLAGS_END_OF_BUFFER (0x40) +#define MPI2_SGE_FLAGS_ELEMENT_TYPE_MASK (0x30) +#define MPI2_SGE_FLAGS_LOCAL_ADDRESS (0x08) +#define MPI2_SGE_FLAGS_DIRECTION (0x04) +#define MPI2_SGE_FLAGS_ADDRESS_SIZE (0x02) +#define MPI2_SGE_FLAGS_END_OF_LIST (0x01) + +#define MPI2_SGE_FLAGS_SHIFT (24) + +#define MPI2_SGE_LENGTH_MASK (0x00FFFFFF) +#define MPI2_SGE_CHAIN_LENGTH_MASK (0x0000FFFF) + +/* Element Type */ + +#define MPI2_SGE_FLAGS_TRANSACTION_ELEMENT (0x00) +#define MPI2_SGE_FLAGS_SIMPLE_ELEMENT (0x10) +#define MPI2_SGE_FLAGS_CHAIN_ELEMENT (0x30) +#define MPI2_SGE_FLAGS_ELEMENT_MASK (0x30) + +/* Address location */ + +#define MPI2_SGE_FLAGS_SYSTEM_ADDRESS (0x00) + +/* Direction */ + +#define MPI2_SGE_FLAGS_IOC_TO_HOST (0x00) +#define MPI2_SGE_FLAGS_HOST_TO_IOC (0x04) + +/* Address Size */ + +#define MPI2_SGE_FLAGS_32_BIT_ADDRESSING (0x00) +#define MPI2_SGE_FLAGS_64_BIT_ADDRESSING (0x02) + +/* Context Size */ + +#define MPI2_SGE_FLAGS_32_BIT_CONTEXT (0x00) +#define MPI2_SGE_FLAGS_64_BIT_CONTEXT (0x02) +#define MPI2_SGE_FLAGS_96_BIT_CONTEXT (0x04) +#define MPI2_SGE_FLAGS_128_BIT_CONTEXT (0x06) + +#define MPI2_SGE_CHAIN_OFFSET_MASK (0x00FF0000) +#define MPI2_SGE_CHAIN_OFFSET_SHIFT (16) + +/**************************************************************************** +* MPI SGE operation Macros +****************************************************************************/ + +/* SIMPLE FlagsLength manipulations... */ +#define MPI2_SGE_SET_FLAGS(f) ((U32)(f) << MPI2_SGE_FLAGS_SHIFT) +#define MPI2_SGE_GET_FLAGS(f) (((f) & ~MPI2_SGE_LENGTH_MASK) >> MPI2_SGE_FLAGS_SHIFT) +#define MPI2_SGE_LENGTH(f) ((f) & MPI2_SGE_LENGTH_MASK) +#define MPI2_SGE_CHAIN_LENGTH(f) ((f) & MPI2_SGE_CHAIN_LENGTH_MASK) + +#define MPI2_SGE_SET_FLAGS_LENGTH(f,l) (MPI2_SGE_SET_FLAGS(f) | MPI2_SGE_LENGTH(l)) + +#define MPI2_pSGE_GET_FLAGS(psg) MPI2_SGE_GET_FLAGS((psg)->FlagsLength) +#define MPI2_pSGE_GET_LENGTH(psg) MPI2_SGE_LENGTH((psg)->FlagsLength) +#define MPI2_pSGE_SET_FLAGS_LENGTH(psg,f,l) (psg)->FlagsLength = MPI2_SGE_SET_FLAGS_LENGTH(f,l) + +/* CAUTION - The following are READ-MODIFY-WRITE! */ +#define MPI2_pSGE_SET_FLAGS(psg,f) (psg)->FlagsLength |= MPI2_SGE_SET_FLAGS(f) +#define MPI2_pSGE_SET_LENGTH(psg,l) (psg)->FlagsLength |= MPI2_SGE_LENGTH(l) + +#define MPI2_GET_CHAIN_OFFSET(x) ((x & MPI2_SGE_CHAIN_OFFSET_MASK) >> MPI2_SGE_CHAIN_OFFSET_SHIFT) + + +/***************************************************************************** +* +* Fusion-MPT IEEE Scatter Gather Elements +* +*****************************************************************************/ + +/**************************************************************************** +* IEEE Simple Element structures +****************************************************************************/ + +typedef struct _MPI2_IEEE_SGE_SIMPLE32 +{ + U32 Address; + U32 FlagsLength; +} MPI2_IEEE_SGE_SIMPLE32, MPI2_POINTER PTR_MPI2_IEEE_SGE_SIMPLE32, + Mpi2IeeeSgeSimple32_t, MPI2_POINTER pMpi2IeeeSgeSimple32_t; + +typedef struct _MPI2_IEEE_SGE_SIMPLE64 +{ + U64 Address; + U32 Length; + U16 Reserved1; + U8 Reserved2; + U8 Flags; +} MPI2_IEEE_SGE_SIMPLE64, MPI2_POINTER PTR_MPI2_IEEE_SGE_SIMPLE64, + Mpi2IeeeSgeSimple64_t, MPI2_POINTER pMpi2IeeeSgeSimple64_t; + +typedef union _MPI2_IEEE_SGE_SIMPLE_UNION +{ + MPI2_IEEE_SGE_SIMPLE32 Simple32; + MPI2_IEEE_SGE_SIMPLE64 Simple64; +} MPI2_IEEE_SGE_SIMPLE_UNION, MPI2_POINTER PTR_MPI2_IEEE_SGE_SIMPLE_UNION, + Mpi2IeeeSgeSimpleUnion_t, MPI2_POINTER pMpi2IeeeSgeSimpleUnion_t; + + +/**************************************************************************** +* IEEE Chain Element structures +****************************************************************************/ + +typedef MPI2_IEEE_SGE_SIMPLE32 MPI2_IEEE_SGE_CHAIN32; + +typedef MPI2_IEEE_SGE_SIMPLE64 MPI2_IEEE_SGE_CHAIN64; + +typedef union _MPI2_IEEE_SGE_CHAIN_UNION +{ + MPI2_IEEE_SGE_CHAIN32 Chain32; + MPI2_IEEE_SGE_CHAIN64 Chain64; +} MPI2_IEEE_SGE_CHAIN_UNION, MPI2_POINTER PTR_MPI2_IEEE_SGE_CHAIN_UNION, + Mpi2IeeeSgeChainUnion_t, MPI2_POINTER pMpi2IeeeSgeChainUnion_t; + + +/**************************************************************************** +* All IEEE SGE types union +****************************************************************************/ + +typedef struct _MPI2_IEEE_SGE_UNION +{ + union + { + MPI2_IEEE_SGE_SIMPLE_UNION Simple; + MPI2_IEEE_SGE_CHAIN_UNION Chain; + } u; +} MPI2_IEEE_SGE_UNION, MPI2_POINTER PTR_MPI2_IEEE_SGE_UNION, + Mpi2IeeeSgeUnion_t, MPI2_POINTER pMpi2IeeeSgeUnion_t; + + +/**************************************************************************** +* IEEE SGE field definitions and masks +****************************************************************************/ + +/* Flags field bit definitions */ + +#define MPI2_IEEE_SGE_FLAGS_ELEMENT_TYPE_MASK (0x80) + +#define MPI2_IEEE32_SGE_FLAGS_SHIFT (24) + +#define MPI2_IEEE32_SGE_LENGTH_MASK (0x00FFFFFF) + +/* Element Type */ + +#define MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT (0x00) +#define MPI2_IEEE_SGE_FLAGS_CHAIN_ELEMENT (0x80) + +/* Data Location Address Space */ + +#define MPI2_IEEE_SGE_FLAGS_ADDR_MASK (0x03) +#define MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR (0x00) +#define MPI2_IEEE_SGE_FLAGS_IOCDDR_ADDR (0x01) +#define MPI2_IEEE_SGE_FLAGS_IOCPLB_ADDR (0x02) +#define MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR (0x03) + + +/**************************************************************************** +* IEEE SGE operation Macros +****************************************************************************/ + +/* SIMPLE FlagsLength manipulations... */ +#define MPI2_IEEE32_SGE_SET_FLAGS(f) ((U32)(f) << MPI2_IEEE32_SGE_FLAGS_SHIFT) +#define MPI2_IEEE32_SGE_GET_FLAGS(f) (((f) & ~MPI2_IEEE32_SGE_LENGTH_MASK) >> MPI2_IEEE32_SGE_FLAGS_SHIFT) +#define MPI2_IEEE32_SGE_LENGTH(f) ((f) & MPI2_IEEE32_SGE_LENGTH_MASK) + +#define MPI2_IEEE32_SGE_SET_FLAGS_LENGTH(f, l) (MPI2_IEEE32_SGE_SET_FLAGS(f) | MPI2_IEEE32_SGE_LENGTH(l)) + +#define MPI2_IEEE32_pSGE_GET_FLAGS(psg) MPI2_IEEE32_SGE_GET_FLAGS((psg)->FlagsLength) +#define MPI2_IEEE32_pSGE_GET_LENGTH(psg) MPI2_IEEE32_SGE_LENGTH((psg)->FlagsLength) +#define MPI2_IEEE32_pSGE_SET_FLAGS_LENGTH(psg,f,l) (psg)->FlagsLength = MPI2_IEEE32_SGE_SET_FLAGS_LENGTH(f,l) + +/* CAUTION - The following are READ-MODIFY-WRITE! */ +#define MPI2_IEEE32_pSGE_SET_FLAGS(psg,f) (psg)->FlagsLength |= MPI2_IEEE32_SGE_SET_FLAGS(f) +#define MPI2_IEEE32_pSGE_SET_LENGTH(psg,l) (psg)->FlagsLength |= MPI2_IEEE32_SGE_LENGTH(l) + + + + +/***************************************************************************** +* +* Fusion-MPT MPI/IEEE Scatter Gather Unions +* +*****************************************************************************/ + +typedef union _MPI2_SIMPLE_SGE_UNION +{ + MPI2_SGE_SIMPLE_UNION MpiSimple; + MPI2_IEEE_SGE_SIMPLE_UNION IeeeSimple; +} MPI2_SIMPLE_SGE_UNION, MPI2_POINTER PTR_MPI2_SIMPLE_SGE_UNION, + Mpi2SimpleSgeUntion_t, MPI2_POINTER pMpi2SimpleSgeUntion_t; + + +typedef union _MPI2_SGE_IO_UNION +{ + MPI2_SGE_SIMPLE_UNION MpiSimple; + MPI2_SGE_CHAIN_UNION MpiChain; + MPI2_IEEE_SGE_SIMPLE_UNION IeeeSimple; + MPI2_IEEE_SGE_CHAIN_UNION IeeeChain; +} MPI2_SGE_IO_UNION, MPI2_POINTER PTR_MPI2_SGE_IO_UNION, + Mpi2SGEIOUnion_t, MPI2_POINTER pMpi2SGEIOUnion_t; + + +/**************************************************************************** +* +* Values for SGLFlags field, used in many request messages with an SGL +* +****************************************************************************/ + +/* values for MPI SGL Data Location Address Space subfield */ +#define MPI2_SGLFLAGS_ADDRESS_SPACE_MASK (0x0C) +#define MPI2_SGLFLAGS_SYSTEM_ADDRESS_SPACE (0x00) +#define MPI2_SGLFLAGS_IOCDDR_ADDRESS_SPACE (0x04) +#define MPI2_SGLFLAGS_IOCPLB_ADDRESS_SPACE (0x08) +#define MPI2_SGLFLAGS_IOCPLBNTA_ADDRESS_SPACE (0x0C) +/* values for SGL Type subfield */ +#define MPI2_SGLFLAGS_SGL_TYPE_MASK (0x03) +#define MPI2_SGLFLAGS_SGL_TYPE_MPI (0x00) +#define MPI2_SGLFLAGS_SGL_TYPE_IEEE32 (0x01) +#define MPI2_SGLFLAGS_SGL_TYPE_IEEE64 (0x02) + + +#endif + diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h new file mode 100644 index 000000000000..2f27cf6d6c65 --- /dev/null +++ b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h @@ -0,0 +1,2151 @@ +/* + * Copyright (c) 2000-2009 LSI Corporation. + * + * + * Name: mpi2_cnfg.h + * Title: MPI Configuration messages and pages + * Creation Date: November 10, 2006 + * + * mpi2_cnfg.h Version: 02.00.10 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * 06-04-07 02.00.01 Added defines for SAS IO Unit Page 2 PhyFlags. + * Added Manufacturing Page 11. + * Added MPI2_SAS_EXPANDER0_FLAGS_CONNECTOR_END_DEVICE + * define. + * 06-26-07 02.00.02 Adding generic structure for product-specific + * Manufacturing pages: MPI2_CONFIG_PAGE_MANUFACTURING_PS. + * Rework of BIOS Page 2 configuration page. + * Fixed MPI2_BIOSPAGE2_BOOT_DEVICE to be a union of the + * forms. + * Added configuration pages IOC Page 8 and Driver + * Persistent Mapping Page 0. + * 08-31-07 02.00.03 Modified configuration pages dealing with Integrated + * RAID (Manufacturing Page 4, RAID Volume Pages 0 and 1, + * RAID Physical Disk Pages 0 and 1, RAID Configuration + * Page 0). + * Added new value for AccessStatus field of SAS Device + * Page 0 (_SATA_NEEDS_INITIALIZATION). + * 10-31-07 02.00.04 Added missing SEPDevHandle field to + * MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0. + * 12-18-07 02.00.05 Modified IO Unit Page 0 to use 32-bit version fields for + * NVDATA. + * Modified IOC Page 7 to use masks and added field for + * SASBroadcastPrimitiveMasks. + * Added MPI2_CONFIG_PAGE_BIOS_4. + * Added MPI2_CONFIG_PAGE_LOG_0. + * 02-29-08 02.00.06 Modified various names to make them 32-character unique. + * Added SAS Device IDs. + * Updated Integrated RAID configuration pages including + * Manufacturing Page 4, IOC Page 6, and RAID Configuration + * Page 0. + * 05-21-08 02.00.07 Added define MPI2_MANPAGE4_MIX_SSD_SAS_SATA. + * Added define MPI2_MANPAGE4_PHYSDISK_128MB_COERCION. + * Fixed define MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING. + * Added missing MaxNumRoutedSasAddresses field to + * MPI2_CONFIG_PAGE_EXPANDER_0. + * Added SAS Port Page 0. + * Modified structure layout for + * MPI2_CONFIG_PAGE_DRIVER_MAPPING_0. + * 06-27-08 02.00.08 Changed MPI2_CONFIG_PAGE_RD_PDISK_1 to use + * MPI2_RAID_PHYS_DISK1_PATH_MAX to size the array. + * 10-02-08 02.00.09 Changed MPI2_RAID_PGAD_CONFIGNUM_MASK from 0x0000FFFF + * to 0x000000FF. + * Added two new values for the Physical Disk Coercion Size + * bits in the Flags field of Manufacturing Page 4. + * Added product-specific Manufacturing pages 16 to 31. + * Modified Flags bits for controlling write cache on SATA + * drives in IO Unit Page 1. + * Added new bit to AdditionalControlFlags of SAS IO Unit + * Page 1 to control Invalid Topology Correction. + * Added additional defines for RAID Volume Page 0 + * VolumeStatusFlags field. + * Modified meaning of RAID Volume Page 0 VolumeSettings + * define for auto-configure of hot-swap drives. + * Added SupportedPhysDisks field to RAID Volume Page 1 and + * added related defines. + * Added PhysDiskAttributes field (and related defines) to + * RAID Physical Disk Page 0. + * Added MPI2_SAS_PHYINFO_PHY_VACANT define. + * Added three new DiscoveryStatus bits for SAS IO Unit + * Page 0 and SAS Expander Page 0. + * Removed multiplexing information from SAS IO Unit pages. + * Added BootDeviceWaitTime field to SAS IO Unit Page 4. + * Removed Zone Address Resolved bit from PhyInfo and from + * Expander Page 0 Flags field. + * Added two new AccessStatus values to SAS Device Page 0 + * for indicating routing problems. Added 3 reserved words + * to this page. + * 01-19-09 02.00.10 Fixed defines for GPIOVal field of IO Unit Page 3. + * Inserted missing reserved field into structure for IOC + * Page 6. + * Added more pending task bits to RAID Volume Page 0 + * VolumeStatusFlags defines. + * Added MPI2_PHYSDISK0_STATUS_FLAG_NOT_CERTIFIED define. + * Added a new DiscoveryStatus bit for SAS IO Unit Page 0 + * and SAS Expander Page 0 to flag a downstream initiator + * when in simplified routing mode. + * Removed SATA Init Failure defines for DiscoveryStatus + * fields of SAS IO Unit Page 0 and SAS Expander Page 0. + * Added MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED define. + * Added PortGroups, DmaGroup, and ControlGroup fields to + * SAS Device Page 0. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI2_CNFG_H +#define MPI2_CNFG_H + +/***************************************************************************** +* Configuration Page Header and defines +*****************************************************************************/ + +/* Config Page Header */ +typedef struct _MPI2_CONFIG_PAGE_HEADER +{ + U8 PageVersion; /* 0x00 */ + U8 PageLength; /* 0x01 */ + U8 PageNumber; /* 0x02 */ + U8 PageType; /* 0x03 */ +} MPI2_CONFIG_PAGE_HEADER, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_HEADER, + Mpi2ConfigPageHeader_t, MPI2_POINTER pMpi2ConfigPageHeader_t; + +typedef union _MPI2_CONFIG_PAGE_HEADER_UNION +{ + MPI2_CONFIG_PAGE_HEADER Struct; + U8 Bytes[4]; + U16 Word16[2]; + U32 Word32; +} MPI2_CONFIG_PAGE_HEADER_UNION, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_HEADER_UNION, + Mpi2ConfigPageHeaderUnion, MPI2_POINTER pMpi2ConfigPageHeaderUnion; + +/* Extended Config Page Header */ +typedef struct _MPI2_CONFIG_EXTENDED_PAGE_HEADER +{ + U8 PageVersion; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 PageNumber; /* 0x02 */ + U8 PageType; /* 0x03 */ + U16 ExtPageLength; /* 0x04 */ + U8 ExtPageType; /* 0x06 */ + U8 Reserved2; /* 0x07 */ +} MPI2_CONFIG_EXTENDED_PAGE_HEADER, + MPI2_POINTER PTR_MPI2_CONFIG_EXTENDED_PAGE_HEADER, + Mpi2ConfigExtendedPageHeader_t, MPI2_POINTER pMpi2ConfigExtendedPageHeader_t; + +typedef union _MPI2_CONFIG_EXT_PAGE_HEADER_UNION +{ + MPI2_CONFIG_PAGE_HEADER Struct; + MPI2_CONFIG_EXTENDED_PAGE_HEADER Ext; + U8 Bytes[8]; + U16 Word16[4]; + U32 Word32[2]; +} MPI2_CONFIG_EXT_PAGE_HEADER_UNION, MPI2_POINTER PTR_MPI2_CONFIG_EXT_PAGE_HEADER_UNION, + Mpi2ConfigPageExtendedHeaderUnion, MPI2_POINTER pMpi2ConfigPageExtendedHeaderUnion; + + +/* PageType field values */ +#define MPI2_CONFIG_PAGEATTR_READ_ONLY (0x00) +#define MPI2_CONFIG_PAGEATTR_CHANGEABLE (0x10) +#define MPI2_CONFIG_PAGEATTR_PERSISTENT (0x20) +#define MPI2_CONFIG_PAGEATTR_MASK (0xF0) + +#define MPI2_CONFIG_PAGETYPE_IO_UNIT (0x00) +#define MPI2_CONFIG_PAGETYPE_IOC (0x01) +#define MPI2_CONFIG_PAGETYPE_BIOS (0x02) +#define MPI2_CONFIG_PAGETYPE_RAID_VOLUME (0x08) +#define MPI2_CONFIG_PAGETYPE_MANUFACTURING (0x09) +#define MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK (0x0A) +#define MPI2_CONFIG_PAGETYPE_EXTENDED (0x0F) +#define MPI2_CONFIG_PAGETYPE_MASK (0x0F) + +#define MPI2_CONFIG_TYPENUM_MASK (0x0FFF) + + +/* ExtPageType field values */ +#define MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT (0x10) +#define MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER (0x11) +#define MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE (0x12) +#define MPI2_CONFIG_EXTPAGETYPE_SAS_PHY (0x13) +#define MPI2_CONFIG_EXTPAGETYPE_LOG (0x14) +#define MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE (0x15) +#define MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG (0x16) +#define MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING (0x17) +#define MPI2_CONFIG_EXTPAGETYPE_SAS_PORT (0x18) + + +/***************************************************************************** +* PageAddress defines +*****************************************************************************/ + +/* RAID Volume PageAddress format */ +#define MPI2_RAID_VOLUME_PGAD_FORM_MASK (0xF0000000) +#define MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE (0x00000000) +#define MPI2_RAID_VOLUME_PGAD_FORM_HANDLE (0x10000000) + +#define MPI2_RAID_VOLUME_PGAD_HANDLE_MASK (0x0000FFFF) + + +/* RAID Physical Disk PageAddress format */ +#define MPI2_PHYSDISK_PGAD_FORM_MASK (0xF0000000) +#define MPI2_PHYSDISK_PGAD_FORM_GET_NEXT_PHYSDISKNUM (0x00000000) +#define MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM (0x10000000) +#define MPI2_PHYSDISK_PGAD_FORM_DEVHANDLE (0x20000000) + +#define MPI2_PHYSDISK_PGAD_PHYSDISKNUM_MASK (0x000000FF) +#define MPI2_PHYSDISK_PGAD_DEVHANDLE_MASK (0x0000FFFF) + + +/* SAS Expander PageAddress format */ +#define MPI2_SAS_EXPAND_PGAD_FORM_MASK (0xF0000000) +#define MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL (0x00000000) +#define MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM (0x10000000) +#define MPI2_SAS_EXPAND_PGAD_FORM_HNDL (0x20000000) + +#define MPI2_SAS_EXPAND_PGAD_HANDLE_MASK (0x0000FFFF) +#define MPI2_SAS_EXPAND_PGAD_PHYNUM_MASK (0x00FF0000) +#define MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT (16) + + +/* SAS Device PageAddress format */ +#define MPI2_SAS_DEVICE_PGAD_FORM_MASK (0xF0000000) +#define MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE (0x00000000) +#define MPI2_SAS_DEVICE_PGAD_FORM_HANDLE (0x20000000) + +#define MPI2_SAS_DEVICE_PGAD_HANDLE_MASK (0x0000FFFF) + + +/* SAS PHY PageAddress format */ +#define MPI2_SAS_PHY_PGAD_FORM_MASK (0xF0000000) +#define MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER (0x00000000) +#define MPI2_SAS_PHY_PGAD_FORM_PHY_TBL_INDEX (0x10000000) + +#define MPI2_SAS_PHY_PGAD_PHY_NUMBER_MASK (0x000000FF) +#define MPI2_SAS_PHY_PGAD_PHY_TBL_INDEX_MASK (0x0000FFFF) + + +/* SAS Port PageAddress format */ +#define MPI2_SASPORT_PGAD_FORM_MASK (0xF0000000) +#define MPI2_SASPORT_PGAD_FORM_GET_NEXT_PORT (0x00000000) +#define MPI2_SASPORT_PGAD_FORM_PORT_NUM (0x10000000) + +#define MPI2_SASPORT_PGAD_PORTNUMBER_MASK (0x00000FFF) + + +/* SAS Enclosure PageAddress format */ +#define MPI2_SAS_ENCLOS_PGAD_FORM_MASK (0xF0000000) +#define MPI2_SAS_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE (0x00000000) +#define MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE (0x10000000) + +#define MPI2_SAS_ENCLOS_PGAD_HANDLE_MASK (0x0000FFFF) + + +/* RAID Configuration PageAddress format */ +#define MPI2_RAID_PGAD_FORM_MASK (0xF0000000) +#define MPI2_RAID_PGAD_FORM_GET_NEXT_CONFIGNUM (0x00000000) +#define MPI2_RAID_PGAD_FORM_CONFIGNUM (0x10000000) +#define MPI2_RAID_PGAD_FORM_ACTIVE_CONFIG (0x20000000) + +#define MPI2_RAID_PGAD_CONFIGNUM_MASK (0x000000FF) + + +/* Driver Persistent Mapping PageAddress format */ +#define MPI2_DPM_PGAD_FORM_MASK (0xF0000000) +#define MPI2_DPM_PGAD_FORM_ENTRY_RANGE (0x00000000) + +#define MPI2_DPM_PGAD_ENTRY_COUNT_MASK (0x0FFF0000) +#define MPI2_DPM_PGAD_ENTRY_COUNT_SHIFT (16) +#define MPI2_DPM_PGAD_START_ENTRY_MASK (0x0000FFFF) + + +/**************************************************************************** +* Configuration messages +****************************************************************************/ + +/* Configuration Request Message */ +typedef struct _MPI2_CONFIG_REQUEST +{ + U8 Action; /* 0x00 */ + U8 SGLFlags; /* 0x01 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 ExtPageLength; /* 0x04 */ + U8 ExtPageType; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved1; /* 0x0A */ + U32 Reserved2; /* 0x0C */ + U32 Reserved3; /* 0x10 */ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x14 */ + U32 PageAddress; /* 0x18 */ + MPI2_SGE_IO_UNION PageBufferSGE; /* 0x1C */ +} MPI2_CONFIG_REQUEST, MPI2_POINTER PTR_MPI2_CONFIG_REQUEST, + Mpi2ConfigRequest_t, MPI2_POINTER pMpi2ConfigRequest_t; + +/* values for the Action field */ +#define MPI2_CONFIG_ACTION_PAGE_HEADER (0x00) +#define MPI2_CONFIG_ACTION_PAGE_READ_CURRENT (0x01) +#define MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT (0x02) +#define MPI2_CONFIG_ACTION_PAGE_DEFAULT (0x03) +#define MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM (0x04) +#define MPI2_CONFIG_ACTION_PAGE_READ_DEFAULT (0x05) +#define MPI2_CONFIG_ACTION_PAGE_READ_NVRAM (0x06) +#define MPI2_CONFIG_ACTION_PAGE_GET_CHANGEABLE (0x07) + +/* values for SGLFlags field are in the SGL section of mpi2.h */ + + +/* Config Reply Message */ +typedef struct _MPI2_CONFIG_REPLY +{ + U8 Action; /* 0x00 */ + U8 SGLFlags; /* 0x01 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 ExtPageLength; /* 0x04 */ + U8 ExtPageType; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved1; /* 0x0A */ + U16 Reserved2; /* 0x0C */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x14 */ +} MPI2_CONFIG_REPLY, MPI2_POINTER PTR_MPI2_CONFIG_REPLY, + Mpi2ConfigReply_t, MPI2_POINTER pMpi2ConfigReply_t; + + + +/***************************************************************************** +* +* C o n f i g u r a t i o n P a g e s +* +*****************************************************************************/ + +/**************************************************************************** +* Manufacturing Config pages +****************************************************************************/ + +#define MPI2_MFGPAGE_VENDORID_LSI (0x1000) + +/* SAS */ +#define MPI2_MFGPAGE_DEVID_SAS2004 (0x0070) +#define MPI2_MFGPAGE_DEVID_SAS2008 (0x0072) +#define MPI2_MFGPAGE_DEVID_SAS2108_1 (0x0074) +#define MPI2_MFGPAGE_DEVID_SAS2108_2 (0x0076) +#define MPI2_MFGPAGE_DEVID_SAS2108_3 (0x0077) +#define MPI2_MFGPAGE_DEVID_SAS2116_1 (0x0064) +#define MPI2_MFGPAGE_DEVID_SAS2116_2 (0x0065) + + +/* Manufacturing Page 0 */ + +typedef struct _MPI2_CONFIG_PAGE_MAN_0 +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U8 ChipName[16]; /* 0x04 */ + U8 ChipRevision[8]; /* 0x14 */ + U8 BoardName[16]; /* 0x1C */ + U8 BoardAssembly[16]; /* 0x2C */ + U8 BoardTracerNumber[16]; /* 0x3C */ +} MPI2_CONFIG_PAGE_MAN_0, + MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_0, + Mpi2ManufacturingPage0_t, MPI2_POINTER pMpi2ManufacturingPage0_t; + +#define MPI2_MANUFACTURING0_PAGEVERSION (0x00) + + +/* Manufacturing Page 1 */ + +typedef struct _MPI2_CONFIG_PAGE_MAN_1 +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U8 VPD[256]; /* 0x04 */ +} MPI2_CONFIG_PAGE_MAN_1, + MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_1, + Mpi2ManufacturingPage1_t, MPI2_POINTER pMpi2ManufacturingPage1_t; + +#define MPI2_MANUFACTURING1_PAGEVERSION (0x00) + + +typedef struct _MPI2_CHIP_REVISION_ID +{ + U16 DeviceID; /* 0x00 */ + U8 PCIRevisionID; /* 0x02 */ + U8 Reserved; /* 0x03 */ +} MPI2_CHIP_REVISION_ID, MPI2_POINTER PTR_MPI2_CHIP_REVISION_ID, + Mpi2ChipRevisionId_t, MPI2_POINTER pMpi2ChipRevisionId_t; + + +/* Manufacturing Page 2 */ + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.PageLength at runtime. + */ +#ifndef MPI2_MAN_PAGE_2_HW_SETTINGS_WORDS +#define MPI2_MAN_PAGE_2_HW_SETTINGS_WORDS (1) +#endif + +typedef struct _MPI2_CONFIG_PAGE_MAN_2 +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + MPI2_CHIP_REVISION_ID ChipId; /* 0x04 */ + U32 HwSettings[MPI2_MAN_PAGE_2_HW_SETTINGS_WORDS];/* 0x08 */ +} MPI2_CONFIG_PAGE_MAN_2, + MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_2, + Mpi2ManufacturingPage2_t, MPI2_POINTER pMpi2ManufacturingPage2_t; + +#define MPI2_MANUFACTURING2_PAGEVERSION (0x00) + + +/* Manufacturing Page 3 */ + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.PageLength at runtime. + */ +#ifndef MPI2_MAN_PAGE_3_INFO_WORDS +#define MPI2_MAN_PAGE_3_INFO_WORDS (1) +#endif + +typedef struct _MPI2_CONFIG_PAGE_MAN_3 +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + MPI2_CHIP_REVISION_ID ChipId; /* 0x04 */ + U32 Info[MPI2_MAN_PAGE_3_INFO_WORDS];/* 0x08 */ +} MPI2_CONFIG_PAGE_MAN_3, + MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_3, + Mpi2ManufacturingPage3_t, MPI2_POINTER pMpi2ManufacturingPage3_t; + +#define MPI2_MANUFACTURING3_PAGEVERSION (0x00) + + +/* Manufacturing Page 4 */ + +typedef struct _MPI2_MANPAGE4_PWR_SAVE_SETTINGS +{ + U8 PowerSaveFlags; /* 0x00 */ + U8 InternalOperationsSleepTime; /* 0x01 */ + U8 InternalOperationsRunTime; /* 0x02 */ + U8 HostIdleTime; /* 0x03 */ +} MPI2_MANPAGE4_PWR_SAVE_SETTINGS, + MPI2_POINTER PTR_MPI2_MANPAGE4_PWR_SAVE_SETTINGS, + Mpi2ManPage4PwrSaveSettings_t, MPI2_POINTER pMpi2ManPage4PwrSaveSettings_t; + +/* defines for the PowerSaveFlags field */ +#define MPI2_MANPAGE4_MASK_POWERSAVE_MODE (0x03) +#define MPI2_MANPAGE4_POWERSAVE_MODE_DISABLED (0x00) +#define MPI2_MANPAGE4_CUSTOM_POWERSAVE_MODE (0x01) +#define MPI2_MANPAGE4_FULL_POWERSAVE_MODE (0x02) + +typedef struct _MPI2_CONFIG_PAGE_MAN_4 +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U32 Reserved1; /* 0x04 */ + U32 Flags; /* 0x08 */ + U8 InquirySize; /* 0x0C */ + U8 Reserved2; /* 0x0D */ + U16 Reserved3; /* 0x0E */ + U8 InquiryData[56]; /* 0x10 */ + U32 RAID0VolumeSettings; /* 0x48 */ + U32 RAID1EVolumeSettings; /* 0x4C */ + U32 RAID1VolumeSettings; /* 0x50 */ + U32 RAID10VolumeSettings; /* 0x54 */ + U32 Reserved4; /* 0x58 */ + U32 Reserved5; /* 0x5C */ + MPI2_MANPAGE4_PWR_SAVE_SETTINGS PowerSaveSettings; /* 0x60 */ + U8 MaxOCEDisks; /* 0x64 */ + U8 ResyncRate; /* 0x65 */ + U16 DataScrubDuration; /* 0x66 */ + U8 MaxHotSpares; /* 0x68 */ + U8 MaxPhysDisksPerVol; /* 0x69 */ + U8 MaxPhysDisks; /* 0x6A */ + U8 MaxVolumes; /* 0x6B */ +} MPI2_CONFIG_PAGE_MAN_4, + MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_4, + Mpi2ManufacturingPage4_t, MPI2_POINTER pMpi2ManufacturingPage4_t; + +#define MPI2_MANUFACTURING4_PAGEVERSION (0x0A) + +/* Manufacturing Page 4 Flags field */ +#define MPI2_MANPAGE4_METADATA_SIZE_MASK (0x00030000) +#define MPI2_MANPAGE4_METADATA_512MB (0x00000000) + +#define MPI2_MANPAGE4_MIX_SSD_SAS_SATA (0x00008000) +#define MPI2_MANPAGE4_MIX_SSD_AND_NON_SSD (0x00004000) +#define MPI2_MANPAGE4_HIDE_PHYSDISK_NON_IR (0x00002000) + +#define MPI2_MANPAGE4_MASK_PHYSDISK_COERCION (0x00001C00) +#define MPI2_MANPAGE4_PHYSDISK_COERCION_1GB (0x00000000) +#define MPI2_MANPAGE4_PHYSDISK_128MB_COERCION (0x00000400) +#define MPI2_MANPAGE4_PHYSDISK_ADAPTIVE_COERCION (0x00000800) +#define MPI2_MANPAGE4_PHYSDISK_ZERO_COERCION (0x00000C00) + +#define MPI2_MANPAGE4_MASK_BAD_BLOCK_MARKING (0x00000300) +#define MPI2_MANPAGE4_DEFAULT_BAD_BLOCK_MARKING (0x00000000) +#define MPI2_MANPAGE4_TABLE_BAD_BLOCK_MARKING (0x00000100) +#define MPI2_MANPAGE4_WRITE_LONG_BAD_BLOCK_MARKING (0x00000200) + +#define MPI2_MANPAGE4_FORCE_OFFLINE_FAILOVER (0x00000080) +#define MPI2_MANPAGE4_RAID10_DISABLE (0x00000040) +#define MPI2_MANPAGE4_RAID1E_DISABLE (0x00000020) +#define MPI2_MANPAGE4_RAID1_DISABLE (0x00000010) +#define MPI2_MANPAGE4_RAID0_DISABLE (0x00000008) +#define MPI2_MANPAGE4_IR_MODEPAGE8_DISABLE (0x00000004) +#define MPI2_MANPAGE4_IM_RESYNC_CACHE_ENABLE (0x00000002) +#define MPI2_MANPAGE4_IR_NO_MIX_SAS_SATA (0x00000001) + + +/* Manufacturing Page 5 */ + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.PageLength or NumPhys at runtime. + */ +#ifndef MPI2_MAN_PAGE_5_PHY_ENTRIES +#define MPI2_MAN_PAGE_5_PHY_ENTRIES (1) +#endif + +typedef struct _MPI2_MANUFACTURING5_ENTRY +{ + U64 WWID; /* 0x00 */ + U64 DeviceName; /* 0x08 */ +} MPI2_MANUFACTURING5_ENTRY, MPI2_POINTER PTR_MPI2_MANUFACTURING5_ENTRY, + Mpi2Manufacturing5Entry_t, MPI2_POINTER pMpi2Manufacturing5Entry_t; + +typedef struct _MPI2_CONFIG_PAGE_MAN_5 +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U8 NumPhys; /* 0x04 */ + U8 Reserved1; /* 0x05 */ + U16 Reserved2; /* 0x06 */ + U32 Reserved3; /* 0x08 */ + U32 Reserved4; /* 0x0C */ + MPI2_MANUFACTURING5_ENTRY Phy[MPI2_MAN_PAGE_5_PHY_ENTRIES];/* 0x08 */ +} MPI2_CONFIG_PAGE_MAN_5, + MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_5, + Mpi2ManufacturingPage5_t, MPI2_POINTER pMpi2ManufacturingPage5_t; + +#define MPI2_MANUFACTURING5_PAGEVERSION (0x03) + + +/* Manufacturing Page 6 */ + +typedef struct _MPI2_CONFIG_PAGE_MAN_6 +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U32 ProductSpecificInfo;/* 0x04 */ +} MPI2_CONFIG_PAGE_MAN_6, + MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_6, + Mpi2ManufacturingPage6_t, MPI2_POINTER pMpi2ManufacturingPage6_t; + +#define MPI2_MANUFACTURING6_PAGEVERSION (0x00) + + +/* Manufacturing Page 7 */ + +typedef struct _MPI2_MANPAGE7_CONNECTOR_INFO +{ + U32 Pinout; /* 0x00 */ + U8 Connector[16]; /* 0x04 */ + U8 Location; /* 0x14 */ + U8 Reserved1; /* 0x15 */ + U16 Slot; /* 0x16 */ + U32 Reserved2; /* 0x18 */ +} MPI2_MANPAGE7_CONNECTOR_INFO, MPI2_POINTER PTR_MPI2_MANPAGE7_CONNECTOR_INFO, + Mpi2ManPage7ConnectorInfo_t, MPI2_POINTER pMpi2ManPage7ConnectorInfo_t; + +/* defines for the Pinout field */ +#define MPI2_MANPAGE7_PINOUT_SFF_8484_L4 (0x00080000) +#define MPI2_MANPAGE7_PINOUT_SFF_8484_L3 (0x00040000) +#define MPI2_MANPAGE7_PINOUT_SFF_8484_L2 (0x00020000) +#define MPI2_MANPAGE7_PINOUT_SFF_8484_L1 (0x00010000) +#define MPI2_MANPAGE7_PINOUT_SFF_8470_L4 (0x00000800) +#define MPI2_MANPAGE7_PINOUT_SFF_8470_L3 (0x00000400) +#define MPI2_MANPAGE7_PINOUT_SFF_8470_L2 (0x00000200) +#define MPI2_MANPAGE7_PINOUT_SFF_8470_L1 (0x00000100) +#define MPI2_MANPAGE7_PINOUT_SFF_8482 (0x00000002) +#define MPI2_MANPAGE7_PINOUT_CONNECTION_UNKNOWN (0x00000001) + +/* defines for the Location field */ +#define MPI2_MANPAGE7_LOCATION_UNKNOWN (0x01) +#define MPI2_MANPAGE7_LOCATION_INTERNAL (0x02) +#define MPI2_MANPAGE7_LOCATION_EXTERNAL (0x04) +#define MPI2_MANPAGE7_LOCATION_SWITCHABLE (0x08) +#define MPI2_MANPAGE7_LOCATION_AUTO (0x10) +#define MPI2_MANPAGE7_LOCATION_NOT_PRESENT (0x20) +#define MPI2_MANPAGE7_LOCATION_NOT_CONNECTED (0x80) + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check NumPhys at runtime. + */ +#ifndef MPI2_MANPAGE7_CONNECTOR_INFO_MAX +#define MPI2_MANPAGE7_CONNECTOR_INFO_MAX (1) +#endif + +typedef struct _MPI2_CONFIG_PAGE_MAN_7 +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U32 Reserved1; /* 0x04 */ + U32 Reserved2; /* 0x08 */ + U32 Flags; /* 0x0C */ + U8 EnclosureName[16]; /* 0x10 */ + U8 NumPhys; /* 0x20 */ + U8 Reserved3; /* 0x21 */ + U16 Reserved4; /* 0x22 */ + MPI2_MANPAGE7_CONNECTOR_INFO ConnectorInfo[MPI2_MANPAGE7_CONNECTOR_INFO_MAX]; /* 0x24 */ +} MPI2_CONFIG_PAGE_MAN_7, + MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_7, + Mpi2ManufacturingPage7_t, MPI2_POINTER pMpi2ManufacturingPage7_t; + +#define MPI2_MANUFACTURING7_PAGEVERSION (0x00) + +/* defines for the Flags field */ +#define MPI2_MANPAGE7_FLAG_USE_SLOT_INFO (0x00000001) + + +/* + * Generic structure to use for product-specific manufacturing pages + * (currently Manufacturing Page 8 through Manufacturing Page 31). + */ + +typedef struct _MPI2_CONFIG_PAGE_MAN_PS +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U32 ProductSpecificInfo;/* 0x04 */ +} MPI2_CONFIG_PAGE_MAN_PS, + MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_PS, + Mpi2ManufacturingPagePS_t, MPI2_POINTER pMpi2ManufacturingPagePS_t; + +#define MPI2_MANUFACTURING8_PAGEVERSION (0x00) +#define MPI2_MANUFACTURING9_PAGEVERSION (0x00) +#define MPI2_MANUFACTURING10_PAGEVERSION (0x00) +#define MPI2_MANUFACTURING11_PAGEVERSION (0x00) +#define MPI2_MANUFACTURING12_PAGEVERSION (0x00) +#define MPI2_MANUFACTURING13_PAGEVERSION (0x00) +#define MPI2_MANUFACTURING14_PAGEVERSION (0x00) +#define MPI2_MANUFACTURING15_PAGEVERSION (0x00) +#define MPI2_MANUFACTURING16_PAGEVERSION (0x00) +#define MPI2_MANUFACTURING17_PAGEVERSION (0x00) +#define MPI2_MANUFACTURING18_PAGEVERSION (0x00) +#define MPI2_MANUFACTURING19_PAGEVERSION (0x00) +#define MPI2_MANUFACTURING20_PAGEVERSION (0x00) +#define MPI2_MANUFACTURING21_PAGEVERSION (0x00) +#define MPI2_MANUFACTURING22_PAGEVERSION (0x00) +#define MPI2_MANUFACTURING23_PAGEVERSION (0x00) +#define MPI2_MANUFACTURING24_PAGEVERSION (0x00) +#define MPI2_MANUFACTURING25_PAGEVERSION (0x00) +#define MPI2_MANUFACTURING26_PAGEVERSION (0x00) +#define MPI2_MANUFACTURING27_PAGEVERSION (0x00) +#define MPI2_MANUFACTURING28_PAGEVERSION (0x00) +#define MPI2_MANUFACTURING29_PAGEVERSION (0x00) +#define MPI2_MANUFACTURING30_PAGEVERSION (0x00) +#define MPI2_MANUFACTURING31_PAGEVERSION (0x00) + + +/**************************************************************************** +* IO Unit Config Pages +****************************************************************************/ + +/* IO Unit Page 0 */ + +typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_0 +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U64 UniqueValue; /* 0x04 */ + MPI2_VERSION_UNION NvdataVersionDefault; /* 0x08 */ + MPI2_VERSION_UNION NvdataVersionPersistent; /* 0x0A */ +} MPI2_CONFIG_PAGE_IO_UNIT_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_0, + Mpi2IOUnitPage0_t, MPI2_POINTER pMpi2IOUnitPage0_t; + +#define MPI2_IOUNITPAGE0_PAGEVERSION (0x02) + + +/* IO Unit Page 1 */ + +typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_1 +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U32 Flags; /* 0x04 */ +} MPI2_CONFIG_PAGE_IO_UNIT_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_1, + Mpi2IOUnitPage1_t, MPI2_POINTER pMpi2IOUnitPage1_t; + +#define MPI2_IOUNITPAGE1_PAGEVERSION (0x04) + +/* IO Unit Page 1 Flags defines */ +#define MPI2_IOUNITPAGE1_MASK_SATA_WRITE_CACHE (0x00000600) +#define MPI2_IOUNITPAGE1_ENABLE_SATA_WRITE_CACHE (0x00000000) +#define MPI2_IOUNITPAGE1_DISABLE_SATA_WRITE_CACHE (0x00000200) +#define MPI2_IOUNITPAGE1_UNCHANGED_SATA_WRITE_CACHE (0x00000400) +#define MPI2_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE (0x00000100) +#define MPI2_IOUNITPAGE1_DISABLE_IR (0x00000040) +#define MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING (0x00000020) +#define MPI2_IOUNITPAGE1_IR_USE_STATIC_VOLUME_ID (0x00000004) +#define MPI2_IOUNITPAGE1_MULTI_PATHING (0x00000002) +#define MPI2_IOUNITPAGE1_SINGLE_PATHING (0x00000000) + + +/* IO Unit Page 3 */ + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.PageLength at runtime. + */ +#ifndef MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX +#define MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX (1) +#endif + +typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_3 +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U8 GPIOCount; /* 0x04 */ + U8 Reserved1; /* 0x05 */ + U16 Reserved2; /* 0x06 */ + U16 GPIOVal[MPI2_IO_UNIT_PAGE_3_GPIO_VAL_MAX];/* 0x08 */ +} MPI2_CONFIG_PAGE_IO_UNIT_3, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IO_UNIT_3, + Mpi2IOUnitPage3_t, MPI2_POINTER pMpi2IOUnitPage3_t; + +#define MPI2_IOUNITPAGE3_PAGEVERSION (0x01) + +/* defines for IO Unit Page 3 GPIOVal field */ +#define MPI2_IOUNITPAGE3_GPIO_FUNCTION_MASK (0xFFFC) +#define MPI2_IOUNITPAGE3_GPIO_FUNCTION_SHIFT (2) +#define MPI2_IOUNITPAGE3_GPIO_SETTING_OFF (0x0000) +#define MPI2_IOUNITPAGE3_GPIO_SETTING_ON (0x0001) + + +/**************************************************************************** +* IOC Config Pages +****************************************************************************/ + +/* IOC Page 0 */ + +typedef struct _MPI2_CONFIG_PAGE_IOC_0 +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U32 Reserved1; /* 0x04 */ + U32 Reserved2; /* 0x08 */ + U16 VendorID; /* 0x0C */ + U16 DeviceID; /* 0x0E */ + U8 RevisionID; /* 0x10 */ + U8 Reserved3; /* 0x11 */ + U16 Reserved4; /* 0x12 */ + U32 ClassCode; /* 0x14 */ + U16 SubsystemVendorID; /* 0x18 */ + U16 SubsystemID; /* 0x1A */ +} MPI2_CONFIG_PAGE_IOC_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IOC_0, + Mpi2IOCPage0_t, MPI2_POINTER pMpi2IOCPage0_t; + +#define MPI2_IOCPAGE0_PAGEVERSION (0x02) + + +/* IOC Page 1 */ + +typedef struct _MPI2_CONFIG_PAGE_IOC_1 +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U32 Flags; /* 0x04 */ + U32 CoalescingTimeout; /* 0x08 */ + U8 CoalescingDepth; /* 0x0C */ + U8 PCISlotNum; /* 0x0D */ + U8 PCIBusNum; /* 0x0E */ + U8 PCIDomainSegment; /* 0x0F */ + U32 Reserved1; /* 0x10 */ + U32 Reserved2; /* 0x14 */ +} MPI2_CONFIG_PAGE_IOC_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IOC_1, + Mpi2IOCPage1_t, MPI2_POINTER pMpi2IOCPage1_t; + +#define MPI2_IOCPAGE1_PAGEVERSION (0x05) + +/* defines for IOC Page 1 Flags field */ +#define MPI2_IOCPAGE1_REPLY_COALESCING (0x00000001) + +#define MPI2_IOCPAGE1_PCISLOTNUM_UNKNOWN (0xFF) +#define MPI2_IOCPAGE1_PCIBUSNUM_UNKNOWN (0xFF) +#define MPI2_IOCPAGE1_PCIDOMAIN_UNKNOWN (0xFF) + +/* IOC Page 6 */ + +typedef struct _MPI2_CONFIG_PAGE_IOC_6 +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U32 CapabilitiesFlags; /* 0x04 */ + U8 MaxDrivesRAID0; /* 0x08 */ + U8 MaxDrivesRAID1; /* 0x09 */ + U8 MaxDrivesRAID1E; /* 0x0A */ + U8 MaxDrivesRAID10; /* 0x0B */ + U8 MinDrivesRAID0; /* 0x0C */ + U8 MinDrivesRAID1; /* 0x0D */ + U8 MinDrivesRAID1E; /* 0x0E */ + U8 MinDrivesRAID10; /* 0x0F */ + U32 Reserved1; /* 0x10 */ + U8 MaxGlobalHotSpares; /* 0x14 */ + U8 MaxPhysDisks; /* 0x15 */ + U8 MaxVolumes; /* 0x16 */ + U8 MaxConfigs; /* 0x17 */ + U8 MaxOCEDisks; /* 0x18 */ + U8 Reserved2; /* 0x19 */ + U16 Reserved3; /* 0x1A */ + U32 SupportedStripeSizeMapRAID0; /* 0x1C */ + U32 SupportedStripeSizeMapRAID1E; /* 0x20 */ + U32 SupportedStripeSizeMapRAID10; /* 0x24 */ + U32 Reserved4; /* 0x28 */ + U32 Reserved5; /* 0x2C */ + U16 DefaultMetadataSize; /* 0x30 */ + U16 Reserved6; /* 0x32 */ + U16 MaxBadBlockTableEntries; /* 0x34 */ + U16 Reserved7; /* 0x36 */ + U32 IRNvsramVersion; /* 0x38 */ +} MPI2_CONFIG_PAGE_IOC_6, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IOC_6, + Mpi2IOCPage6_t, MPI2_POINTER pMpi2IOCPage6_t; + +#define MPI2_IOCPAGE6_PAGEVERSION (0x04) + +/* defines for IOC Page 6 CapabilitiesFlags */ +#define MPI2_IOCPAGE6_CAP_FLAGS_RAID10_SUPPORT (0x00000010) +#define MPI2_IOCPAGE6_CAP_FLAGS_RAID1_SUPPORT (0x00000008) +#define MPI2_IOCPAGE6_CAP_FLAGS_RAID1E_SUPPORT (0x00000004) +#define MPI2_IOCPAGE6_CAP_FLAGS_RAID0_SUPPORT (0x00000002) +#define MPI2_IOCPAGE6_CAP_FLAGS_GLOBAL_HOT_SPARE (0x00000001) + + +/* IOC Page 7 */ + +#define MPI2_IOCPAGE7_EVENTMASK_WORDS (4) + +typedef struct _MPI2_CONFIG_PAGE_IOC_7 +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U32 Reserved1; /* 0x04 */ + U32 EventMasks[MPI2_IOCPAGE7_EVENTMASK_WORDS];/* 0x08 */ + U16 SASBroadcastPrimitiveMasks; /* 0x18 */ + U16 Reserved2; /* 0x1A */ + U32 Reserved3; /* 0x1C */ +} MPI2_CONFIG_PAGE_IOC_7, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IOC_7, + Mpi2IOCPage7_t, MPI2_POINTER pMpi2IOCPage7_t; + +#define MPI2_IOCPAGE7_PAGEVERSION (0x01) + + +/* IOC Page 8 */ + +typedef struct _MPI2_CONFIG_PAGE_IOC_8 +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U8 NumDevsPerEnclosure; /* 0x04 */ + U8 Reserved1; /* 0x05 */ + U16 Reserved2; /* 0x06 */ + U16 MaxPersistentEntries; /* 0x08 */ + U16 MaxNumPhysicalMappedIDs; /* 0x0A */ + U16 Flags; /* 0x0C */ + U16 Reserved3; /* 0x0E */ + U16 IRVolumeMappingFlags; /* 0x10 */ + U16 Reserved4; /* 0x12 */ + U32 Reserved5; /* 0x14 */ +} MPI2_CONFIG_PAGE_IOC_8, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_IOC_8, + Mpi2IOCPage8_t, MPI2_POINTER pMpi2IOCPage8_t; + +#define MPI2_IOCPAGE8_PAGEVERSION (0x00) + +/* defines for IOC Page 8 Flags field */ +#define MPI2_IOCPAGE8_FLAGS_DA_START_SLOT_1 (0x00000020) +#define MPI2_IOCPAGE8_FLAGS_RESERVED_TARGETID_0 (0x00000010) + +#define MPI2_IOCPAGE8_FLAGS_MASK_MAPPING_MODE (0x0000000E) +#define MPI2_IOCPAGE8_FLAGS_DEVICE_PERSISTENCE_MAPPING (0x00000000) +#define MPI2_IOCPAGE8_FLAGS_ENCLOSURE_SLOT_MAPPING (0x00000002) + +#define MPI2_IOCPAGE8_FLAGS_DISABLE_PERSISTENT_MAPPING (0x00000001) +#define MPI2_IOCPAGE8_FLAGS_ENABLE_PERSISTENT_MAPPING (0x00000000) + +/* defines for IOC Page 8 IRVolumeMappingFlags */ +#define MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE (0x00000003) +#define MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING (0x00000000) +#define MPI2_IOCPAGE8_IRFLAGS_HIGH_VOLUME_MAPPING (0x00000001) + + +/**************************************************************************** +* BIOS Config Pages +****************************************************************************/ + +/* BIOS Page 1 */ + +typedef struct _MPI2_CONFIG_PAGE_BIOS_1 +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U32 BiosOptions; /* 0x04 */ + U32 IOCSettings; /* 0x08 */ + U32 Reserved1; /* 0x0C */ + U32 DeviceSettings; /* 0x10 */ + U16 NumberOfDevices; /* 0x14 */ + U16 Reserved2; /* 0x16 */ + U16 IOTimeoutBlockDevicesNonRM; /* 0x18 */ + U16 IOTimeoutSequential; /* 0x1A */ + U16 IOTimeoutOther; /* 0x1C */ + U16 IOTimeoutBlockDevicesRM; /* 0x1E */ +} MPI2_CONFIG_PAGE_BIOS_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_BIOS_1, + Mpi2BiosPage1_t, MPI2_POINTER pMpi2BiosPage1_t; + +#define MPI2_BIOSPAGE1_PAGEVERSION (0x04) + +/* values for BIOS Page 1 BiosOptions field */ +#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_BIOS (0x00000001) + +/* values for BIOS Page 1 IOCSettings field */ +#define MPI2_BIOSPAGE1_IOCSET_MASK_BOOT_PREFERENCE (0x00030000) +#define MPI2_BIOSPAGE1_IOCSET_ENCLOSURE_SLOT_BOOT (0x00000000) +#define MPI2_BIOSPAGE1_IOCSET_SAS_ADDRESS_BOOT (0x00010000) + +#define MPI2_BIOSPAGE1_IOCSET_MASK_RM_SETTING (0x000000C0) +#define MPI2_BIOSPAGE1_IOCSET_NONE_RM_SETTING (0x00000000) +#define MPI2_BIOSPAGE1_IOCSET_BOOT_RM_SETTING (0x00000040) +#define MPI2_BIOSPAGE1_IOCSET_MEDIA_RM_SETTING (0x00000080) + +#define MPI2_BIOSPAGE1_IOCSET_MASK_ADAPTER_SUPPORT (0x00000030) +#define MPI2_BIOSPAGE1_IOCSET_NO_SUPPORT (0x00000000) +#define MPI2_BIOSPAGE1_IOCSET_BIOS_SUPPORT (0x00000010) +#define MPI2_BIOSPAGE1_IOCSET_OS_SUPPORT (0x00000020) +#define MPI2_BIOSPAGE1_IOCSET_ALL_SUPPORT (0x00000030) + +#define MPI2_BIOSPAGE1_IOCSET_ALTERNATE_CHS (0x00000008) + +/* values for BIOS Page 1 DeviceSettings field */ +#define MPI2_BIOSPAGE1_DEVSET_DISABLE_SMART_POLLING (0x00000010) +#define MPI2_BIOSPAGE1_DEVSET_DISABLE_SEQ_LUN (0x00000008) +#define MPI2_BIOSPAGE1_DEVSET_DISABLE_RM_LUN (0x00000004) +#define MPI2_BIOSPAGE1_DEVSET_DISABLE_NON_RM_LUN (0x00000002) +#define MPI2_BIOSPAGE1_DEVSET_DISABLE_OTHER_LUN (0x00000001) + + +/* BIOS Page 2 */ + +typedef struct _MPI2_BOOT_DEVICE_ADAPTER_ORDER +{ + U32 Reserved1; /* 0x00 */ + U32 Reserved2; /* 0x04 */ + U32 Reserved3; /* 0x08 */ + U32 Reserved4; /* 0x0C */ + U32 Reserved5; /* 0x10 */ + U32 Reserved6; /* 0x14 */ +} MPI2_BOOT_DEVICE_ADAPTER_ORDER, + MPI2_POINTER PTR_MPI2_BOOT_DEVICE_ADAPTER_ORDER, + Mpi2BootDeviceAdapterOrder_t, MPI2_POINTER pMpi2BootDeviceAdapterOrder_t; + +typedef struct _MPI2_BOOT_DEVICE_SAS_WWID +{ + U64 SASAddress; /* 0x00 */ + U8 LUN[8]; /* 0x08 */ + U32 Reserved1; /* 0x10 */ + U32 Reserved2; /* 0x14 */ +} MPI2_BOOT_DEVICE_SAS_WWID, MPI2_POINTER PTR_MPI2_BOOT_DEVICE_SAS_WWID, + Mpi2BootDeviceSasWwid_t, MPI2_POINTER pMpi2BootDeviceSasWwid_t; + +typedef struct _MPI2_BOOT_DEVICE_ENCLOSURE_SLOT +{ + U64 EnclosureLogicalID; /* 0x00 */ + U32 Reserved1; /* 0x08 */ + U32 Reserved2; /* 0x0C */ + U16 SlotNumber; /* 0x10 */ + U16 Reserved3; /* 0x12 */ + U32 Reserved4; /* 0x14 */ +} MPI2_BOOT_DEVICE_ENCLOSURE_SLOT, + MPI2_POINTER PTR_MPI2_BOOT_DEVICE_ENCLOSURE_SLOT, + Mpi2BootDeviceEnclosureSlot_t, MPI2_POINTER pMpi2BootDeviceEnclosureSlot_t; + +typedef struct _MPI2_BOOT_DEVICE_DEVICE_NAME +{ + U64 DeviceName; /* 0x00 */ + U8 LUN[8]; /* 0x08 */ + U32 Reserved1; /* 0x10 */ + U32 Reserved2; /* 0x14 */ +} MPI2_BOOT_DEVICE_DEVICE_NAME, MPI2_POINTER PTR_MPI2_BOOT_DEVICE_DEVICE_NAME, + Mpi2BootDeviceDeviceName_t, MPI2_POINTER pMpi2BootDeviceDeviceName_t; + +typedef union _MPI2_MPI2_BIOSPAGE2_BOOT_DEVICE +{ + MPI2_BOOT_DEVICE_ADAPTER_ORDER AdapterOrder; + MPI2_BOOT_DEVICE_SAS_WWID SasWwid; + MPI2_BOOT_DEVICE_ENCLOSURE_SLOT EnclosureSlot; + MPI2_BOOT_DEVICE_DEVICE_NAME DeviceName; +} MPI2_BIOSPAGE2_BOOT_DEVICE, MPI2_POINTER PTR_MPI2_BIOSPAGE2_BOOT_DEVICE, + Mpi2BiosPage2BootDevice_t, MPI2_POINTER pMpi2BiosPage2BootDevice_t; + +typedef struct _MPI2_CONFIG_PAGE_BIOS_2 +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U32 Reserved1; /* 0x04 */ + U32 Reserved2; /* 0x08 */ + U32 Reserved3; /* 0x0C */ + U32 Reserved4; /* 0x10 */ + U32 Reserved5; /* 0x14 */ + U32 Reserved6; /* 0x18 */ + U8 ReqBootDeviceForm; /* 0x1C */ + U8 Reserved7; /* 0x1D */ + U16 Reserved8; /* 0x1E */ + MPI2_BIOSPAGE2_BOOT_DEVICE RequestedBootDevice; /* 0x20 */ + U8 ReqAltBootDeviceForm; /* 0x38 */ + U8 Reserved9; /* 0x39 */ + U16 Reserved10; /* 0x3A */ + MPI2_BIOSPAGE2_BOOT_DEVICE RequestedAltBootDevice; /* 0x3C */ + U8 CurrentBootDeviceForm; /* 0x58 */ + U8 Reserved11; /* 0x59 */ + U16 Reserved12; /* 0x5A */ + MPI2_BIOSPAGE2_BOOT_DEVICE CurrentBootDevice; /* 0x58 */ +} MPI2_CONFIG_PAGE_BIOS_2, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_BIOS_2, + Mpi2BiosPage2_t, MPI2_POINTER pMpi2BiosPage2_t; + +#define MPI2_BIOSPAGE2_PAGEVERSION (0x04) + +/* values for BIOS Page 2 BootDeviceForm fields */ +#define MPI2_BIOSPAGE2_FORM_MASK (0x0F) +#define MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED (0x00) +#define MPI2_BIOSPAGE2_FORM_SAS_WWID (0x05) +#define MPI2_BIOSPAGE2_FORM_ENCLOSURE_SLOT (0x06) +#define MPI2_BIOSPAGE2_FORM_DEVICE_NAME (0x07) + + +/* BIOS Page 3 */ + +typedef struct _MPI2_ADAPTER_INFO +{ + U8 PciBusNumber; /* 0x00 */ + U8 PciDeviceAndFunctionNumber; /* 0x01 */ + U16 AdapterFlags; /* 0x02 */ +} MPI2_ADAPTER_INFO, MPI2_POINTER PTR_MPI2_ADAPTER_INFO, + Mpi2AdapterInfo_t, MPI2_POINTER pMpi2AdapterInfo_t; + +#define MPI2_ADAPTER_INFO_FLAGS_EMBEDDED (0x0001) +#define MPI2_ADAPTER_INFO_FLAGS_INIT_STATUS (0x0002) + +typedef struct _MPI2_CONFIG_PAGE_BIOS_3 +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U32 GlobalFlags; /* 0x04 */ + U32 BiosVersion; /* 0x08 */ + MPI2_ADAPTER_INFO AdapterOrder[4]; /* 0x0C */ + U32 Reserved1; /* 0x1C */ +} MPI2_CONFIG_PAGE_BIOS_3, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_BIOS_3, + Mpi2BiosPage3_t, MPI2_POINTER pMpi2BiosPage3_t; + +#define MPI2_BIOSPAGE3_PAGEVERSION (0x00) + +/* values for BIOS Page 3 GlobalFlags */ +#define MPI2_BIOSPAGE3_FLAGS_PAUSE_ON_ERROR (0x00000002) +#define MPI2_BIOSPAGE3_FLAGS_VERBOSE_ENABLE (0x00000004) +#define MPI2_BIOSPAGE3_FLAGS_HOOK_INT_40_DISABLE (0x00000010) + +#define MPI2_BIOSPAGE3_FLAGS_DEV_LIST_DISPLAY_MASK (0x000000E0) +#define MPI2_BIOSPAGE3_FLAGS_INSTALLED_DEV_DISPLAY (0x00000000) +#define MPI2_BIOSPAGE3_FLAGS_ADAPTER_DISPLAY (0x00000020) +#define MPI2_BIOSPAGE3_FLAGS_ADAPTER_DEV_DISPLAY (0x00000040) + + +/* BIOS Page 4 */ + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.PageLength or NumPhys at runtime. + */ +#ifndef MPI2_BIOS_PAGE_4_PHY_ENTRIES +#define MPI2_BIOS_PAGE_4_PHY_ENTRIES (1) +#endif + +typedef struct _MPI2_BIOS4_ENTRY +{ + U64 ReassignmentWWID; /* 0x00 */ + U64 ReassignmentDeviceName; /* 0x08 */ +} MPI2_BIOS4_ENTRY, MPI2_POINTER PTR_MPI2_BIOS4_ENTRY, + Mpi2MBios4Entry_t, MPI2_POINTER pMpi2Bios4Entry_t; + +typedef struct _MPI2_CONFIG_PAGE_BIOS_4 +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U8 NumPhys; /* 0x04 */ + U8 Reserved1; /* 0x05 */ + U16 Reserved2; /* 0x06 */ + MPI2_BIOS4_ENTRY Phy[MPI2_BIOS_PAGE_4_PHY_ENTRIES]; /* 0x08 */ +} MPI2_CONFIG_PAGE_BIOS_4, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_BIOS_4, + Mpi2BiosPage4_t, MPI2_POINTER pMpi2BiosPage4_t; + +#define MPI2_BIOSPAGE4_PAGEVERSION (0x01) + + +/**************************************************************************** +* RAID Volume Config Pages +****************************************************************************/ + +/* RAID Volume Page 0 */ + +typedef struct _MPI2_RAIDVOL0_PHYS_DISK +{ + U8 RAIDSetNum; /* 0x00 */ + U8 PhysDiskMap; /* 0x01 */ + U8 PhysDiskNum; /* 0x02 */ + U8 Reserved; /* 0x03 */ +} MPI2_RAIDVOL0_PHYS_DISK, MPI2_POINTER PTR_MPI2_RAIDVOL0_PHYS_DISK, + Mpi2RaidVol0PhysDisk_t, MPI2_POINTER pMpi2RaidVol0PhysDisk_t; + +/* defines for the PhysDiskMap field */ +#define MPI2_RAIDVOL0_PHYSDISK_PRIMARY (0x01) +#define MPI2_RAIDVOL0_PHYSDISK_SECONDARY (0x02) + +typedef struct _MPI2_RAIDVOL0_SETTINGS +{ + U16 Settings; /* 0x00 */ + U8 HotSparePool; /* 0x01 */ + U8 Reserved; /* 0x02 */ +} MPI2_RAIDVOL0_SETTINGS, MPI2_POINTER PTR_MPI2_RAIDVOL0_SETTINGS, + Mpi2RaidVol0Settings_t, MPI2_POINTER pMpi2RaidVol0Settings_t; + +/* RAID Volume Page 0 HotSparePool defines, also used in RAID Physical Disk */ +#define MPI2_RAID_HOT_SPARE_POOL_0 (0x01) +#define MPI2_RAID_HOT_SPARE_POOL_1 (0x02) +#define MPI2_RAID_HOT_SPARE_POOL_2 (0x04) +#define MPI2_RAID_HOT_SPARE_POOL_3 (0x08) +#define MPI2_RAID_HOT_SPARE_POOL_4 (0x10) +#define MPI2_RAID_HOT_SPARE_POOL_5 (0x20) +#define MPI2_RAID_HOT_SPARE_POOL_6 (0x40) +#define MPI2_RAID_HOT_SPARE_POOL_7 (0x80) + +/* RAID Volume Page 0 VolumeSettings defines */ +#define MPI2_RAIDVOL0_SETTING_USE_PRODUCT_ID_SUFFIX (0x0008) +#define MPI2_RAIDVOL0_SETTING_AUTO_CONFIG_HSWAP_DISABLE (0x0004) + +#define MPI2_RAIDVOL0_SETTING_MASK_WRITE_CACHING (0x0003) +#define MPI2_RAIDVOL0_SETTING_UNCHANGED (0x0000) +#define MPI2_RAIDVOL0_SETTING_DISABLE_WRITE_CACHING (0x0001) +#define MPI2_RAIDVOL0_SETTING_ENABLE_WRITE_CACHING (0x0002) + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.PageLength at runtime. + */ +#ifndef MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX +#define MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX (1) +#endif + +typedef struct _MPI2_CONFIG_PAGE_RAID_VOL_0 +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U16 DevHandle; /* 0x04 */ + U8 VolumeState; /* 0x06 */ + U8 VolumeType; /* 0x07 */ + U32 VolumeStatusFlags; /* 0x08 */ + MPI2_RAIDVOL0_SETTINGS VolumeSettings; /* 0x0C */ + U64 MaxLBA; /* 0x10 */ + U32 StripeSize; /* 0x18 */ + U16 BlockSize; /* 0x1C */ + U16 Reserved1; /* 0x1E */ + U8 SupportedPhysDisks; /* 0x20 */ + U8 ResyncRate; /* 0x21 */ + U16 DataScrubDuration; /* 0x22 */ + U8 NumPhysDisks; /* 0x24 */ + U8 Reserved2; /* 0x25 */ + U8 Reserved3; /* 0x26 */ + U8 InactiveStatus; /* 0x27 */ + MPI2_RAIDVOL0_PHYS_DISK PhysDisk[MPI2_RAID_VOL_PAGE_0_PHYSDISK_MAX]; /* 0x28 */ +} MPI2_CONFIG_PAGE_RAID_VOL_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_RAID_VOL_0, + Mpi2RaidVolPage0_t, MPI2_POINTER pMpi2RaidVolPage0_t; + +#define MPI2_RAIDVOLPAGE0_PAGEVERSION (0x0A) + +/* values for RAID VolumeState */ +#define MPI2_RAID_VOL_STATE_MISSING (0x00) +#define MPI2_RAID_VOL_STATE_FAILED (0x01) +#define MPI2_RAID_VOL_STATE_INITIALIZING (0x02) +#define MPI2_RAID_VOL_STATE_ONLINE (0x03) +#define MPI2_RAID_VOL_STATE_DEGRADED (0x04) +#define MPI2_RAID_VOL_STATE_OPTIMAL (0x05) + +/* values for RAID VolumeType */ +#define MPI2_RAID_VOL_TYPE_RAID0 (0x00) +#define MPI2_RAID_VOL_TYPE_RAID1E (0x01) +#define MPI2_RAID_VOL_TYPE_RAID1 (0x02) +#define MPI2_RAID_VOL_TYPE_RAID10 (0x05) +#define MPI2_RAID_VOL_TYPE_UNKNOWN (0xFF) + +/* values for RAID Volume Page 0 VolumeStatusFlags field */ +#define MPI2_RAIDVOL0_STATUS_FLAG_PENDING_RESYNC (0x02000000) +#define MPI2_RAIDVOL0_STATUS_FLAG_BACKG_INIT_PENDING (0x01000000) +#define MPI2_RAIDVOL0_STATUS_FLAG_MDC_PENDING (0x00800000) +#define MPI2_RAIDVOL0_STATUS_FLAG_USER_CONSIST_PENDING (0x00400000) +#define MPI2_RAIDVOL0_STATUS_FLAG_MAKE_DATA_CONSISTENT (0x00200000) +#define MPI2_RAIDVOL0_STATUS_FLAG_DATA_SCRUB (0x00100000) +#define MPI2_RAIDVOL0_STATUS_FLAG_CONSISTENCY_CHECK (0x00080000) +#define MPI2_RAIDVOL0_STATUS_FLAG_CAPACITY_EXPANSION (0x00040000) +#define MPI2_RAIDVOL0_STATUS_FLAG_BACKGROUND_INIT (0x00020000) +#define MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS (0x00010000) +#define MPI2_RAIDVOL0_STATUS_FLAG_OCE_ALLOWED (0x00000040) +#define MPI2_RAIDVOL0_STATUS_FLAG_BGI_COMPLETE (0x00000020) +#define MPI2_RAIDVOL0_STATUS_FLAG_1E_OFFSET_MIRROR (0x00000000) +#define MPI2_RAIDVOL0_STATUS_FLAG_1E_ADJACENT_MIRROR (0x00000010) +#define MPI2_RAIDVOL0_STATUS_FLAG_BAD_BLOCK_TABLE_FULL (0x00000008) +#define MPI2_RAIDVOL0_STATUS_FLAG_VOLUME_INACTIVE (0x00000004) +#define MPI2_RAIDVOL0_STATUS_FLAG_QUIESCED (0x00000002) +#define MPI2_RAIDVOL0_STATUS_FLAG_ENABLED (0x00000001) + +/* values for RAID Volume Page 0 SupportedPhysDisks field */ +#define MPI2_RAIDVOL0_SUPPORT_SOLID_STATE_DISKS (0x08) +#define MPI2_RAIDVOL0_SUPPORT_HARD_DISKS (0x04) +#define MPI2_RAIDVOL0_SUPPORT_SAS_PROTOCOL (0x02) +#define MPI2_RAIDVOL0_SUPPORT_SATA_PROTOCOL (0x01) + +/* values for RAID Volume Page 0 InactiveStatus field */ +#define MPI2_RAIDVOLPAGE0_UNKNOWN_INACTIVE (0x00) +#define MPI2_RAIDVOLPAGE0_STALE_METADATA_INACTIVE (0x01) +#define MPI2_RAIDVOLPAGE0_FOREIGN_VOLUME_INACTIVE (0x02) +#define MPI2_RAIDVOLPAGE0_INSUFFICIENT_RESOURCE_INACTIVE (0x03) +#define MPI2_RAIDVOLPAGE0_CLONE_VOLUME_INACTIVE (0x04) +#define MPI2_RAIDVOLPAGE0_INSUFFICIENT_METADATA_INACTIVE (0x05) +#define MPI2_RAIDVOLPAGE0_PREVIOUSLY_DELETED (0x06) + + +/* RAID Volume Page 1 */ + +typedef struct _MPI2_CONFIG_PAGE_RAID_VOL_1 +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U16 DevHandle; /* 0x04 */ + U16 Reserved0; /* 0x06 */ + U8 GUID[24]; /* 0x08 */ + U8 Name[16]; /* 0x20 */ + U64 WWID; /* 0x30 */ + U32 Reserved1; /* 0x38 */ + U32 Reserved2; /* 0x3C */ +} MPI2_CONFIG_PAGE_RAID_VOL_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_RAID_VOL_1, + Mpi2RaidVolPage1_t, MPI2_POINTER pMpi2RaidVolPage1_t; + +#define MPI2_RAIDVOLPAGE1_PAGEVERSION (0x03) + + +/**************************************************************************** +* RAID Physical Disk Config Pages +****************************************************************************/ + +/* RAID Physical Disk Page 0 */ + +typedef struct _MPI2_RAIDPHYSDISK0_SETTINGS +{ + U16 Reserved1; /* 0x00 */ + U8 HotSparePool; /* 0x02 */ + U8 Reserved2; /* 0x03 */ +} MPI2_RAIDPHYSDISK0_SETTINGS, MPI2_POINTER PTR_MPI2_RAIDPHYSDISK0_SETTINGS, + Mpi2RaidPhysDisk0Settings_t, MPI2_POINTER pMpi2RaidPhysDisk0Settings_t; + +/* use MPI2_RAID_HOT_SPARE_POOL_ defines for the HotSparePool field */ + +typedef struct _MPI2_RAIDPHYSDISK0_INQUIRY_DATA +{ + U8 VendorID[8]; /* 0x00 */ + U8 ProductID[16]; /* 0x08 */ + U8 ProductRevLevel[4]; /* 0x18 */ + U8 SerialNum[32]; /* 0x1C */ +} MPI2_RAIDPHYSDISK0_INQUIRY_DATA, + MPI2_POINTER PTR_MPI2_RAIDPHYSDISK0_INQUIRY_DATA, + Mpi2RaidPhysDisk0InquiryData_t, MPI2_POINTER pMpi2RaidPhysDisk0InquiryData_t; + +typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_0 +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U16 DevHandle; /* 0x04 */ + U8 Reserved1; /* 0x06 */ + U8 PhysDiskNum; /* 0x07 */ + MPI2_RAIDPHYSDISK0_SETTINGS PhysDiskSettings; /* 0x08 */ + U32 Reserved2; /* 0x0C */ + MPI2_RAIDPHYSDISK0_INQUIRY_DATA InquiryData; /* 0x10 */ + U32 Reserved3; /* 0x4C */ + U8 PhysDiskState; /* 0x50 */ + U8 OfflineReason; /* 0x51 */ + U8 IncompatibleReason; /* 0x52 */ + U8 PhysDiskAttributes; /* 0x53 */ + U32 PhysDiskStatusFlags; /* 0x54 */ + U64 DeviceMaxLBA; /* 0x58 */ + U64 HostMaxLBA; /* 0x60 */ + U64 CoercedMaxLBA; /* 0x68 */ + U16 BlockSize; /* 0x70 */ + U16 Reserved5; /* 0x72 */ + U32 Reserved6; /* 0x74 */ +} MPI2_CONFIG_PAGE_RD_PDISK_0, + MPI2_POINTER PTR_MPI2_CONFIG_PAGE_RD_PDISK_0, + Mpi2RaidPhysDiskPage0_t, MPI2_POINTER pMpi2RaidPhysDiskPage0_t; + +#define MPI2_RAIDPHYSDISKPAGE0_PAGEVERSION (0x05) + +/* PhysDiskState defines */ +#define MPI2_RAID_PD_STATE_NOT_CONFIGURED (0x00) +#define MPI2_RAID_PD_STATE_NOT_COMPATIBLE (0x01) +#define MPI2_RAID_PD_STATE_OFFLINE (0x02) +#define MPI2_RAID_PD_STATE_ONLINE (0x03) +#define MPI2_RAID_PD_STATE_HOT_SPARE (0x04) +#define MPI2_RAID_PD_STATE_DEGRADED (0x05) +#define MPI2_RAID_PD_STATE_REBUILDING (0x06) +#define MPI2_RAID_PD_STATE_OPTIMAL (0x07) + +/* OfflineReason defines */ +#define MPI2_PHYSDISK0_ONLINE (0x00) +#define MPI2_PHYSDISK0_OFFLINE_MISSING (0x01) +#define MPI2_PHYSDISK0_OFFLINE_FAILED (0x03) +#define MPI2_PHYSDISK0_OFFLINE_INITIALIZING (0x04) +#define MPI2_PHYSDISK0_OFFLINE_REQUESTED (0x05) +#define MPI2_PHYSDISK0_OFFLINE_FAILED_REQUESTED (0x06) +#define MPI2_PHYSDISK0_OFFLINE_OTHER (0xFF) + +/* IncompatibleReason defines */ +#define MPI2_PHYSDISK0_COMPATIBLE (0x00) +#define MPI2_PHYSDISK0_INCOMPATIBLE_PROTOCOL (0x01) +#define MPI2_PHYSDISK0_INCOMPATIBLE_BLOCKSIZE (0x02) +#define MPI2_PHYSDISK0_INCOMPATIBLE_MAX_LBA (0x03) +#define MPI2_PHYSDISK0_INCOMPATIBLE_SATA_EXTENDED_CMD (0x04) +#define MPI2_PHYSDISK0_INCOMPATIBLE_REMOVEABLE_MEDIA (0x05) +#define MPI2_PHYSDISK0_INCOMPATIBLE_UNKNOWN (0xFF) + +/* PhysDiskAttributes defines */ +#define MPI2_PHYSDISK0_ATTRIB_SOLID_STATE_DRIVE (0x08) +#define MPI2_PHYSDISK0_ATTRIB_HARD_DISK_DRIVE (0x04) +#define MPI2_PHYSDISK0_ATTRIB_SAS_PROTOCOL (0x02) +#define MPI2_PHYSDISK0_ATTRIB_SATA_PROTOCOL (0x01) + +/* PhysDiskStatusFlags defines */ +#define MPI2_PHYSDISK0_STATUS_FLAG_NOT_CERTIFIED (0x00000040) +#define MPI2_PHYSDISK0_STATUS_FLAG_OCE_TARGET (0x00000020) +#define MPI2_PHYSDISK0_STATUS_FLAG_WRITE_CACHE_ENABLED (0x00000010) +#define MPI2_PHYSDISK0_STATUS_FLAG_OPTIMAL_PREVIOUS (0x00000000) +#define MPI2_PHYSDISK0_STATUS_FLAG_NOT_OPTIMAL_PREVIOUS (0x00000008) +#define MPI2_PHYSDISK0_STATUS_FLAG_INACTIVE_VOLUME (0x00000004) +#define MPI2_PHYSDISK0_STATUS_FLAG_QUIESCED (0x00000002) +#define MPI2_PHYSDISK0_STATUS_FLAG_OUT_OF_SYNC (0x00000001) + + +/* RAID Physical Disk Page 1 */ + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.PageLength or NumPhysDiskPaths at runtime. + */ +#ifndef MPI2_RAID_PHYS_DISK1_PATH_MAX +#define MPI2_RAID_PHYS_DISK1_PATH_MAX (1) +#endif + +typedef struct _MPI2_RAIDPHYSDISK1_PATH +{ + U16 DevHandle; /* 0x00 */ + U16 Reserved1; /* 0x02 */ + U64 WWID; /* 0x04 */ + U64 OwnerWWID; /* 0x0C */ + U8 OwnerIdentifier; /* 0x14 */ + U8 Reserved2; /* 0x15 */ + U16 Flags; /* 0x16 */ +} MPI2_RAIDPHYSDISK1_PATH, MPI2_POINTER PTR_MPI2_RAIDPHYSDISK1_PATH, + Mpi2RaidPhysDisk1Path_t, MPI2_POINTER pMpi2RaidPhysDisk1Path_t; + +/* RAID Physical Disk Page 1 Physical Disk Path Flags field defines */ +#define MPI2_RAID_PHYSDISK1_FLAG_PRIMARY (0x0004) +#define MPI2_RAID_PHYSDISK1_FLAG_BROKEN (0x0002) +#define MPI2_RAID_PHYSDISK1_FLAG_INVALID (0x0001) + +typedef struct _MPI2_CONFIG_PAGE_RD_PDISK_1 +{ + MPI2_CONFIG_PAGE_HEADER Header; /* 0x00 */ + U8 NumPhysDiskPaths; /* 0x04 */ + U8 PhysDiskNum; /* 0x05 */ + U16 Reserved1; /* 0x06 */ + U32 Reserved2; /* 0x08 */ + MPI2_RAIDPHYSDISK1_PATH PhysicalDiskPath[MPI2_RAID_PHYS_DISK1_PATH_MAX];/* 0x0C */ +} MPI2_CONFIG_PAGE_RD_PDISK_1, + MPI2_POINTER PTR_MPI2_CONFIG_PAGE_RD_PDISK_1, + Mpi2RaidPhysDiskPage1_t, MPI2_POINTER pMpi2RaidPhysDiskPage1_t; + +#define MPI2_RAIDPHYSDISKPAGE1_PAGEVERSION (0x02) + + +/**************************************************************************** +* values for fields used by several types of SAS Config Pages +****************************************************************************/ + +/* values for NegotiatedLinkRates fields */ +#define MPI2_SAS_NEG_LINK_RATE_MASK_LOGICAL (0xF0) +#define MPI2_SAS_NEG_LINK_RATE_SHIFT_LOGICAL (4) +#define MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL (0x0F) +/* link rates used for Negotiated Physical and Logical Link Rate */ +#define MPI2_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE (0x00) +#define MPI2_SAS_NEG_LINK_RATE_PHY_DISABLED (0x01) +#define MPI2_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED (0x02) +#define MPI2_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE (0x03) +#define MPI2_SAS_NEG_LINK_RATE_PORT_SELECTOR (0x04) +#define MPI2_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS (0x05) +#define MPI2_SAS_NEG_LINK_RATE_1_5 (0x08) +#define MPI2_SAS_NEG_LINK_RATE_3_0 (0x09) +#define MPI2_SAS_NEG_LINK_RATE_6_0 (0x0A) + + +/* values for AttachedPhyInfo fields */ +#define MPI2_SAS_APHYINFO_INSIDE_ZPSDS_PERSISTENT (0x00000040) +#define MPI2_SAS_APHYINFO_REQUESTED_INSIDE_ZPSDS (0x00000020) +#define MPI2_SAS_APHYINFO_BREAK_REPLY_CAPABLE (0x00000010) + +#define MPI2_SAS_APHYINFO_REASON_MASK (0x0000000F) +#define MPI2_SAS_APHYINFO_REASON_UNKNOWN (0x00000000) +#define MPI2_SAS_APHYINFO_REASON_POWER_ON (0x00000001) +#define MPI2_SAS_APHYINFO_REASON_HARD_RESET (0x00000002) +#define MPI2_SAS_APHYINFO_REASON_SMP_PHY_CONTROL (0x00000003) +#define MPI2_SAS_APHYINFO_REASON_LOSS_OF_SYNC (0x00000004) +#define MPI2_SAS_APHYINFO_REASON_MULTIPLEXING_SEQ (0x00000005) +#define MPI2_SAS_APHYINFO_REASON_IT_NEXUS_LOSS_TIMER (0x00000006) +#define MPI2_SAS_APHYINFO_REASON_BREAK_TIMEOUT (0x00000007) +#define MPI2_SAS_APHYINFO_REASON_PHY_TEST_STOPPED (0x00000008) + + +/* values for PhyInfo fields */ +#define MPI2_SAS_PHYINFO_PHY_VACANT (0x80000000) +#define MPI2_SAS_PHYINFO_CHANGED_REQ_INSIDE_ZPSDS (0x04000000) +#define MPI2_SAS_PHYINFO_INSIDE_ZPSDS_PERSISTENT (0x02000000) +#define MPI2_SAS_PHYINFO_REQ_INSIDE_ZPSDS (0x01000000) +#define MPI2_SAS_PHYINFO_ZONE_GROUP_PERSISTENT (0x00400000) +#define MPI2_SAS_PHYINFO_INSIDE_ZPSDS (0x00200000) +#define MPI2_SAS_PHYINFO_ZONING_ENABLED (0x00100000) + +#define MPI2_SAS_PHYINFO_REASON_MASK (0x000F0000) +#define MPI2_SAS_PHYINFO_REASON_UNKNOWN (0x00000000) +#define MPI2_SAS_PHYINFO_REASON_POWER_ON (0x00010000) +#define MPI2_SAS_PHYINFO_REASON_HARD_RESET (0x00020000) +#define MPI2_SAS_PHYINFO_REASON_SMP_PHY_CONTROL (0x00030000) +#define MPI2_SAS_PHYINFO_REASON_LOSS_OF_SYNC (0x00040000) +#define MPI2_SAS_PHYINFO_REASON_MULTIPLEXING_SEQ (0x00050000) +#define MPI2_SAS_PHYINFO_REASON_IT_NEXUS_LOSS_TIMER (0x00060000) +#define MPI2_SAS_PHYINFO_REASON_BREAK_TIMEOUT (0x00070000) +#define MPI2_SAS_PHYINFO_REASON_PHY_TEST_STOPPED (0x00080000) + +#define MPI2_SAS_PHYINFO_MULTIPLEXING_SUPPORTED (0x00008000) +#define MPI2_SAS_PHYINFO_SATA_PORT_ACTIVE (0x00004000) +#define MPI2_SAS_PHYINFO_SATA_PORT_SELECTOR_PRESENT (0x00002000) +#define MPI2_SAS_PHYINFO_VIRTUAL_PHY (0x00001000) + +#define MPI2_SAS_PHYINFO_MASK_PARTIAL_PATHWAY_TIME (0x00000F00) +#define MPI2_SAS_PHYINFO_SHIFT_PARTIAL_PATHWAY_TIME (8) + +#define MPI2_SAS_PHYINFO_MASK_ROUTING_ATTRIBUTE (0x000000F0) +#define MPI2_SAS_PHYINFO_DIRECT_ROUTING (0x00000000) +#define MPI2_SAS_PHYINFO_SUBTRACTIVE_ROUTING (0x00000010) +#define MPI2_SAS_PHYINFO_TABLE_ROUTING (0x00000020) + + +/* values for SAS ProgrammedLinkRate fields */ +#define MPI2_SAS_PRATE_MAX_RATE_MASK (0xF0) +#define MPI2_SAS_PRATE_MAX_RATE_NOT_PROGRAMMABLE (0x00) +#define MPI2_SAS_PRATE_MAX_RATE_1_5 (0x80) +#define MPI2_SAS_PRATE_MAX_RATE_3_0 (0x90) +#define MPI2_SAS_PRATE_MAX_RATE_6_0 (0xA0) +#define MPI2_SAS_PRATE_MIN_RATE_MASK (0x0F) +#define MPI2_SAS_PRATE_MIN_RATE_NOT_PROGRAMMABLE (0x00) +#define MPI2_SAS_PRATE_MIN_RATE_1_5 (0x08) +#define MPI2_SAS_PRATE_MIN_RATE_3_0 (0x09) +#define MPI2_SAS_PRATE_MIN_RATE_6_0 (0x0A) + + +/* values for SAS HwLinkRate fields */ +#define MPI2_SAS_HWRATE_MAX_RATE_MASK (0xF0) +#define MPI2_SAS_HWRATE_MAX_RATE_1_5 (0x80) +#define MPI2_SAS_HWRATE_MAX_RATE_3_0 (0x90) +#define MPI2_SAS_HWRATE_MAX_RATE_6_0 (0xA0) +#define MPI2_SAS_HWRATE_MIN_RATE_MASK (0x0F) +#define MPI2_SAS_HWRATE_MIN_RATE_1_5 (0x08) +#define MPI2_SAS_HWRATE_MIN_RATE_3_0 (0x09) +#define MPI2_SAS_HWRATE_MIN_RATE_6_0 (0x0A) + + + +/**************************************************************************** +* SAS IO Unit Config Pages +****************************************************************************/ + +/* SAS IO Unit Page 0 */ + +typedef struct _MPI2_SAS_IO_UNIT0_PHY_DATA +{ + U8 Port; /* 0x00 */ + U8 PortFlags; /* 0x01 */ + U8 PhyFlags; /* 0x02 */ + U8 NegotiatedLinkRate; /* 0x03 */ + U32 ControllerPhyDeviceInfo;/* 0x04 */ + U16 AttachedDevHandle; /* 0x08 */ + U16 ControllerDevHandle; /* 0x0A */ + U32 DiscoveryStatus; /* 0x0C */ + U32 Reserved; /* 0x10 */ +} MPI2_SAS_IO_UNIT0_PHY_DATA, MPI2_POINTER PTR_MPI2_SAS_IO_UNIT0_PHY_DATA, + Mpi2SasIOUnit0PhyData_t, MPI2_POINTER pMpi2SasIOUnit0PhyData_t; + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.ExtPageLength or NumPhys at runtime. + */ +#ifndef MPI2_SAS_IOUNIT0_PHY_MAX +#define MPI2_SAS_IOUNIT0_PHY_MAX (1) +#endif + +typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_0 +{ + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U32 Reserved1; /* 0x08 */ + U8 NumPhys; /* 0x0C */ + U8 Reserved2; /* 0x0D */ + U16 Reserved3; /* 0x0E */ + MPI2_SAS_IO_UNIT0_PHY_DATA PhyData[MPI2_SAS_IOUNIT0_PHY_MAX]; /* 0x10 */ +} MPI2_CONFIG_PAGE_SASIOUNIT_0, + MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_0, + Mpi2SasIOUnitPage0_t, MPI2_POINTER pMpi2SasIOUnitPage0_t; + +#define MPI2_SASIOUNITPAGE0_PAGEVERSION (0x05) + +/* values for SAS IO Unit Page 0 PortFlags */ +#define MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS (0x08) +#define MPI2_SASIOUNIT0_PORTFLAGS_AUTO_PORT_CONFIG (0x01) + +/* values for SAS IO Unit Page 0 PhyFlags */ +#define MPI2_SASIOUNIT0_PHYFLAGS_ZONING_ENABLED (0x10) +#define MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED (0x08) + +/* use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */ + +/* see mpi2_sas.h for values for SAS IO Unit Page 0 ControllerPhyDeviceInfo values */ + +/* values for SAS IO Unit Page 0 DiscoveryStatus */ +#define MPI2_SASIOUNIT0_DS_MAX_ENCLOSURES_EXCEED (0x80000000) +#define MPI2_SASIOUNIT0_DS_MAX_EXPANDERS_EXCEED (0x40000000) +#define MPI2_SASIOUNIT0_DS_MAX_DEVICES_EXCEED (0x20000000) +#define MPI2_SASIOUNIT0_DS_MAX_TOPO_PHYS_EXCEED (0x10000000) +#define MPI2_SASIOUNIT0_DS_DOWNSTREAM_INITIATOR (0x08000000) +#define MPI2_SASIOUNIT0_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE (0x00008000) +#define MPI2_SASIOUNIT0_DS_EXP_MULTI_SUBTRACTIVE (0x00004000) +#define MPI2_SASIOUNIT0_DS_MULTI_PORT_DOMAIN (0x00002000) +#define MPI2_SASIOUNIT0_DS_TABLE_TO_SUBTRACTIVE_LINK (0x00001000) +#define MPI2_SASIOUNIT0_DS_UNSUPPORTED_DEVICE (0x00000800) +#define MPI2_SASIOUNIT0_DS_TABLE_LINK (0x00000400) +#define MPI2_SASIOUNIT0_DS_SUBTRACTIVE_LINK (0x00000200) +#define MPI2_SASIOUNIT0_DS_SMP_CRC_ERROR (0x00000100) +#define MPI2_SASIOUNIT0_DS_SMP_FUNCTION_FAILED (0x00000080) +#define MPI2_SASIOUNIT0_DS_INDEX_NOT_EXIST (0x00000040) +#define MPI2_SASIOUNIT0_DS_OUT_ROUTE_ENTRIES (0x00000020) +#define MPI2_SASIOUNIT0_DS_SMP_TIMEOUT (0x00000010) +#define MPI2_SASIOUNIT0_DS_MULTIPLE_PORTS (0x00000004) +#define MPI2_SASIOUNIT0_DS_UNADDRESSABLE_DEVICE (0x00000002) +#define MPI2_SASIOUNIT0_DS_LOOP_DETECTED (0x00000001) + + +/* SAS IO Unit Page 1 */ + +typedef struct _MPI2_SAS_IO_UNIT1_PHY_DATA +{ + U8 Port; /* 0x00 */ + U8 PortFlags; /* 0x01 */ + U8 PhyFlags; /* 0x02 */ + U8 MaxMinLinkRate; /* 0x03 */ + U32 ControllerPhyDeviceInfo; /* 0x04 */ + U16 MaxTargetPortConnectTime; /* 0x08 */ + U16 Reserved1; /* 0x0A */ +} MPI2_SAS_IO_UNIT1_PHY_DATA, MPI2_POINTER PTR_MPI2_SAS_IO_UNIT1_PHY_DATA, + Mpi2SasIOUnit1PhyData_t, MPI2_POINTER pMpi2SasIOUnit1PhyData_t; + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.ExtPageLength or NumPhys at runtime. + */ +#ifndef MPI2_SAS_IOUNIT1_PHY_MAX +#define MPI2_SAS_IOUNIT1_PHY_MAX (1) +#endif + +typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_1 +{ + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U16 ControlFlags; /* 0x08 */ + U16 SASNarrowMaxQueueDepth; /* 0x0A */ + U16 AdditionalControlFlags; /* 0x0C */ + U16 SASWideMaxQueueDepth; /* 0x0E */ + U8 NumPhys; /* 0x10 */ + U8 SATAMaxQDepth; /* 0x11 */ + U8 ReportDeviceMissingDelay; /* 0x12 */ + U8 IODeviceMissingDelay; /* 0x13 */ + MPI2_SAS_IO_UNIT1_PHY_DATA PhyData[MPI2_SAS_IOUNIT1_PHY_MAX]; /* 0x14 */ +} MPI2_CONFIG_PAGE_SASIOUNIT_1, + MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_1, + Mpi2SasIOUnitPage1_t, MPI2_POINTER pMpi2SasIOUnitPage1_t; + +#define MPI2_SASIOUNITPAGE1_PAGEVERSION (0x09) + +/* values for SAS IO Unit Page 1 ControlFlags */ +#define MPI2_SASIOUNIT1_CONTROL_DEVICE_SELF_TEST (0x8000) +#define MPI2_SASIOUNIT1_CONTROL_SATA_3_0_MAX (0x4000) +#define MPI2_SASIOUNIT1_CONTROL_SATA_1_5_MAX (0x2000) +#define MPI2_SASIOUNIT1_CONTROL_SATA_SW_PRESERVE (0x1000) + +#define MPI2_SASIOUNIT1_CONTROL_MASK_DEV_SUPPORT (0x0600) +#define MPI2_SASIOUNIT1_CONTROL_SHIFT_DEV_SUPPORT (9) +#define MPI2_SASIOUNIT1_CONTROL_DEV_SUPPORT_BOTH (0x0) +#define MPI2_SASIOUNIT1_CONTROL_DEV_SAS_SUPPORT (0x1) +#define MPI2_SASIOUNIT1_CONTROL_DEV_SATA_SUPPORT (0x2) + +#define MPI2_SASIOUNIT1_CONTROL_SATA_48BIT_LBA_REQUIRED (0x0080) +#define MPI2_SASIOUNIT1_CONTROL_SATA_SMART_REQUIRED (0x0040) +#define MPI2_SASIOUNIT1_CONTROL_SATA_NCQ_REQUIRED (0x0020) +#define MPI2_SASIOUNIT1_CONTROL_SATA_FUA_REQUIRED (0x0010) +#define MPI2_SASIOUNIT1_CONTROL_TABLE_SUBTRACTIVE_ILLEGAL (0x0008) +#define MPI2_SASIOUNIT1_CONTROL_SUBTRACTIVE_ILLEGAL (0x0004) +#define MPI2_SASIOUNIT1_CONTROL_FIRST_LVL_DISC_ONLY (0x0002) +#define MPI2_SASIOUNIT1_CONTROL_CLEAR_AFFILIATION (0x0001) + +/* values for SAS IO Unit Page 1 AdditionalControlFlags */ +#define MPI2_SASIOUNIT1_ACONTROL_MULTI_PORT_DOMAIN_ILLEGAL (0x0080) +#define MPI2_SASIOUNIT1_ACONTROL_SATA_ASYNCHROUNOUS_NOTIFICATION (0x0040) +#define MPI2_SASIOUNIT1_ACONTROL_INVALID_TOPOLOGY_CORRECTION (0x0020) +#define MPI2_SASIOUNIT1_ACONTROL_PORT_ENABLE_ONLY_SATA_LINK_RESET (0x0010) +#define MPI2_SASIOUNIT1_ACONTROL_OTHER_AFFILIATION_SATA_LINK_RESET (0x0008) +#define MPI2_SASIOUNIT1_ACONTROL_SELF_AFFILIATION_SATA_LINK_RESET (0x0004) +#define MPI2_SASIOUNIT1_ACONTROL_NO_AFFILIATION_SATA_LINK_RESET (0x0002) +#define MPI2_SASIOUNIT1_ACONTROL_ALLOW_TABLE_TO_TABLE (0x0001) + +/* defines for SAS IO Unit Page 1 ReportDeviceMissingDelay */ +#define MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK (0x7F) +#define MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16 (0x80) + +/* values for SAS IO Unit Page 1 PortFlags */ +#define MPI2_SASIOUNIT1_PORT_FLAGS_AUTO_PORT_CONFIG (0x01) + +/* values for SAS IO Unit Page 2 PhyFlags */ +#define MPI2_SASIOUNIT1_PHYFLAGS_ZONING_ENABLE (0x10) +#define MPI2_SASIOUNIT1_PHYFLAGS_PHY_DISABLE (0x08) + +/* values for SAS IO Unit Page 0 MaxMinLinkRate */ +#define MPI2_SASIOUNIT1_MAX_RATE_MASK (0xF0) +#define MPI2_SASIOUNIT1_MAX_RATE_1_5 (0x80) +#define MPI2_SASIOUNIT1_MAX_RATE_3_0 (0x90) +#define MPI2_SASIOUNIT1_MAX_RATE_6_0 (0xA0) +#define MPI2_SASIOUNIT1_MIN_RATE_MASK (0x0F) +#define MPI2_SASIOUNIT1_MIN_RATE_1_5 (0x08) +#define MPI2_SASIOUNIT1_MIN_RATE_3_0 (0x09) +#define MPI2_SASIOUNIT1_MIN_RATE_6_0 (0x0A) + +/* see mpi2_sas.h for values for SAS IO Unit Page 1 ControllerPhyDeviceInfo values */ + + +/* SAS IO Unit Page 4 */ + +typedef struct _MPI2_SAS_IOUNIT4_SPINUP_GROUP +{ + U8 MaxTargetSpinup; /* 0x00 */ + U8 SpinupDelay; /* 0x01 */ + U16 Reserved1; /* 0x02 */ +} MPI2_SAS_IOUNIT4_SPINUP_GROUP, MPI2_POINTER PTR_MPI2_SAS_IOUNIT4_SPINUP_GROUP, + Mpi2SasIOUnit4SpinupGroup_t, MPI2_POINTER pMpi2SasIOUnit4SpinupGroup_t; + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * four and check Header.ExtPageLength or NumPhys at runtime. + */ +#ifndef MPI2_SAS_IOUNIT4_PHY_MAX +#define MPI2_SAS_IOUNIT4_PHY_MAX (4) +#endif + +typedef struct _MPI2_CONFIG_PAGE_SASIOUNIT_4 +{ + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + MPI2_SAS_IOUNIT4_SPINUP_GROUP SpinupGroupParameters[4]; /* 0x08 */ + U32 Reserved1; /* 0x18 */ + U32 Reserved2; /* 0x1C */ + U32 Reserved3; /* 0x20 */ + U8 BootDeviceWaitTime; /* 0x24 */ + U8 Reserved4; /* 0x25 */ + U16 Reserved5; /* 0x26 */ + U8 NumPhys; /* 0x28 */ + U8 PEInitialSpinupDelay; /* 0x29 */ + U8 PEReplyDelay; /* 0x2A */ + U8 Flags; /* 0x2B */ + U8 PHY[MPI2_SAS_IOUNIT4_PHY_MAX]; /* 0x2C */ +} MPI2_CONFIG_PAGE_SASIOUNIT_4, + MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SASIOUNIT_4, + Mpi2SasIOUnitPage4_t, MPI2_POINTER pMpi2SasIOUnitPage4_t; + +#define MPI2_SASIOUNITPAGE4_PAGEVERSION (0x02) + +/* defines for Flags field */ +#define MPI2_SASIOUNIT4_FLAGS_AUTO_PORTENABLE (0x01) + +/* defines for PHY field */ +#define MPI2_SASIOUNIT4_PHY_SPINUP_GROUP_MASK (0x03) + + +/**************************************************************************** +* SAS Expander Config Pages +****************************************************************************/ + +/* SAS Expander Page 0 */ + +typedef struct _MPI2_CONFIG_PAGE_EXPANDER_0 +{ + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U8 PhysicalPort; /* 0x08 */ + U8 ReportGenLength; /* 0x09 */ + U16 EnclosureHandle; /* 0x0A */ + U64 SASAddress; /* 0x0C */ + U32 DiscoveryStatus; /* 0x14 */ + U16 DevHandle; /* 0x18 */ + U16 ParentDevHandle; /* 0x1A */ + U16 ExpanderChangeCount; /* 0x1C */ + U16 ExpanderRouteIndexes; /* 0x1E */ + U8 NumPhys; /* 0x20 */ + U8 SASLevel; /* 0x21 */ + U16 Flags; /* 0x22 */ + U16 STPBusInactivityTimeLimit; /* 0x24 */ + U16 STPMaxConnectTimeLimit; /* 0x26 */ + U16 STP_SMP_NexusLossTime; /* 0x28 */ + U16 MaxNumRoutedSasAddresses; /* 0x2A */ + U64 ActiveZoneManagerSASAddress;/* 0x2C */ + U16 ZoneLockInactivityLimit; /* 0x34 */ + U16 Reserved1; /* 0x36 */ +} MPI2_CONFIG_PAGE_EXPANDER_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_EXPANDER_0, + Mpi2ExpanderPage0_t, MPI2_POINTER pMpi2ExpanderPage0_t; + +#define MPI2_SASEXPANDER0_PAGEVERSION (0x05) + +/* values for SAS Expander Page 0 DiscoveryStatus field */ +#define MPI2_SAS_EXPANDER0_DS_MAX_ENCLOSURES_EXCEED (0x80000000) +#define MPI2_SAS_EXPANDER0_DS_MAX_EXPANDERS_EXCEED (0x40000000) +#define MPI2_SAS_EXPANDER0_DS_MAX_DEVICES_EXCEED (0x20000000) +#define MPI2_SAS_EXPANDER0_DS_MAX_TOPO_PHYS_EXCEED (0x10000000) +#define MPI2_SAS_EXPANDER0_DS_DOWNSTREAM_INITIATOR (0x08000000) +#define MPI2_SAS_EXPANDER0_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE (0x00008000) +#define MPI2_SAS_EXPANDER0_DS_EXP_MULTI_SUBTRACTIVE (0x00004000) +#define MPI2_SAS_EXPANDER0_DS_MULTI_PORT_DOMAIN (0x00002000) +#define MPI2_SAS_EXPANDER0_DS_TABLE_TO_SUBTRACTIVE_LINK (0x00001000) +#define MPI2_SAS_EXPANDER0_DS_UNSUPPORTED_DEVICE (0x00000800) +#define MPI2_SAS_EXPANDER0_DS_TABLE_LINK (0x00000400) +#define MPI2_SAS_EXPANDER0_DS_SUBTRACTIVE_LINK (0x00000200) +#define MPI2_SAS_EXPANDER0_DS_SMP_CRC_ERROR (0x00000100) +#define MPI2_SAS_EXPANDER0_DS_SMP_FUNCTION_FAILED (0x00000080) +#define MPI2_SAS_EXPANDER0_DS_INDEX_NOT_EXIST (0x00000040) +#define MPI2_SAS_EXPANDER0_DS_OUT_ROUTE_ENTRIES (0x00000020) +#define MPI2_SAS_EXPANDER0_DS_SMP_TIMEOUT (0x00000010) +#define MPI2_SAS_EXPANDER0_DS_MULTIPLE_PORTS (0x00000004) +#define MPI2_SAS_EXPANDER0_DS_UNADDRESSABLE_DEVICE (0x00000002) +#define MPI2_SAS_EXPANDER0_DS_LOOP_DETECTED (0x00000001) + +/* values for SAS Expander Page 0 Flags field */ +#define MPI2_SAS_EXPANDER0_FLAGS_ZONE_LOCKED (0x1000) +#define MPI2_SAS_EXPANDER0_FLAGS_SUPPORTED_PHYSICAL_PRES (0x0800) +#define MPI2_SAS_EXPANDER0_FLAGS_ASSERTED_PHYSICAL_PRES (0x0400) +#define MPI2_SAS_EXPANDER0_FLAGS_ZONING_SUPPORT (0x0200) +#define MPI2_SAS_EXPANDER0_FLAGS_ENABLED_ZONING (0x0100) +#define MPI2_SAS_EXPANDER0_FLAGS_TABLE_TO_TABLE_SUPPORT (0x0080) +#define MPI2_SAS_EXPANDER0_FLAGS_CONNECTOR_END_DEVICE (0x0010) +#define MPI2_SAS_EXPANDER0_FLAGS_OTHERS_CONFIG (0x0004) +#define MPI2_SAS_EXPANDER0_FLAGS_CONFIG_IN_PROGRESS (0x0002) +#define MPI2_SAS_EXPANDER0_FLAGS_ROUTE_TABLE_CONFIG (0x0001) + + +/* SAS Expander Page 1 */ + +typedef struct _MPI2_CONFIG_PAGE_EXPANDER_1 +{ + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U8 PhysicalPort; /* 0x08 */ + U8 Reserved1; /* 0x09 */ + U16 Reserved2; /* 0x0A */ + U8 NumPhys; /* 0x0C */ + U8 Phy; /* 0x0D */ + U16 NumTableEntriesProgrammed; /* 0x0E */ + U8 ProgrammedLinkRate; /* 0x10 */ + U8 HwLinkRate; /* 0x11 */ + U16 AttachedDevHandle; /* 0x12 */ + U32 PhyInfo; /* 0x14 */ + U32 AttachedDeviceInfo; /* 0x18 */ + U16 ExpanderDevHandle; /* 0x1C */ + U8 ChangeCount; /* 0x1E */ + U8 NegotiatedLinkRate; /* 0x1F */ + U8 PhyIdentifier; /* 0x20 */ + U8 AttachedPhyIdentifier; /* 0x21 */ + U8 Reserved3; /* 0x22 */ + U8 DiscoveryInfo; /* 0x23 */ + U32 AttachedPhyInfo; /* 0x24 */ + U8 ZoneGroup; /* 0x28 */ + U8 SelfConfigStatus; /* 0x29 */ + U16 Reserved4; /* 0x2A */ +} MPI2_CONFIG_PAGE_EXPANDER_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_EXPANDER_1, + Mpi2ExpanderPage1_t, MPI2_POINTER pMpi2ExpanderPage1_t; + +#define MPI2_SASEXPANDER1_PAGEVERSION (0x02) + +/* use MPI2_SAS_PRATE_ defines for the ProgrammedLinkRate field */ + +/* use MPI2_SAS_HWRATE_ defines for the HwLinkRate field */ + +/* use MPI2_SAS_PHYINFO_ for the PhyInfo field */ + +/* see mpi2_sas.h for the MPI2_SAS_DEVICE_INFO_ defines used for the AttachedDeviceInfo field */ + +/* use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */ + +/* use MPI2_SAS_APHYINFO_ defines for AttachedPhyInfo field */ + +/* values for SAS Expander Page 1 DiscoveryInfo field */ +#define MPI2_SAS_EXPANDER1_DISCINFO_BAD_PHY_DISABLED (0x04) +#define MPI2_SAS_EXPANDER1_DISCINFO_LINK_STATUS_CHANGE (0x02) +#define MPI2_SAS_EXPANDER1_DISCINFO_NO_ROUTING_ENTRIES (0x01) + + +/**************************************************************************** +* SAS Device Config Pages +****************************************************************************/ + +/* SAS Device Page 0 */ + +typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_0 +{ + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U16 Slot; /* 0x08 */ + U16 EnclosureHandle; /* 0x0A */ + U64 SASAddress; /* 0x0C */ + U16 ParentDevHandle; /* 0x14 */ + U8 PhyNum; /* 0x16 */ + U8 AccessStatus; /* 0x17 */ + U16 DevHandle; /* 0x18 */ + U8 AttachedPhyIdentifier; /* 0x1A */ + U8 ZoneGroup; /* 0x1B */ + U32 DeviceInfo; /* 0x1C */ + U16 Flags; /* 0x20 */ + U8 PhysicalPort; /* 0x22 */ + U8 MaxPortConnections; /* 0x23 */ + U64 DeviceName; /* 0x24 */ + U8 PortGroups; /* 0x2C */ + U8 DmaGroup; /* 0x2D */ + U8 ControlGroup; /* 0x2E */ + U8 Reserved1; /* 0x2F */ + U32 Reserved2; /* 0x30 */ + U32 Reserved3; /* 0x34 */ +} MPI2_CONFIG_PAGE_SAS_DEV_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_DEV_0, + Mpi2SasDevicePage0_t, MPI2_POINTER pMpi2SasDevicePage0_t; + +#define MPI2_SASDEVICE0_PAGEVERSION (0x08) + +/* values for SAS Device Page 0 AccessStatus field */ +#define MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS (0x00) +#define MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED (0x01) +#define MPI2_SAS_DEVICE0_ASTATUS_SATA_CAPABILITY_FAILED (0x02) +#define MPI2_SAS_DEVICE0_ASTATUS_SATA_AFFILIATION_CONFLICT (0x03) +#define MPI2_SAS_DEVICE0_ASTATUS_SATA_NEEDS_INITIALIZATION (0x04) +#define MPI2_SAS_DEVICE0_ASTATUS_ROUTE_NOT_ADDRESSABLE (0x05) +#define MPI2_SAS_DEVICE0_ASTATUS_SMP_ERROR_NOT_ADDRESSABLE (0x06) +#define MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED (0x07) +/* specific values for SATA Init failures */ +#define MPI2_SAS_DEVICE0_ASTATUS_SIF_UNKNOWN (0x10) +#define MPI2_SAS_DEVICE0_ASTATUS_SIF_AFFILIATION_CONFLICT (0x11) +#define MPI2_SAS_DEVICE0_ASTATUS_SIF_DIAG (0x12) +#define MPI2_SAS_DEVICE0_ASTATUS_SIF_IDENTIFICATION (0x13) +#define MPI2_SAS_DEVICE0_ASTATUS_SIF_CHECK_POWER (0x14) +#define MPI2_SAS_DEVICE0_ASTATUS_SIF_PIO_SN (0x15) +#define MPI2_SAS_DEVICE0_ASTATUS_SIF_MDMA_SN (0x16) +#define MPI2_SAS_DEVICE0_ASTATUS_SIF_UDMA_SN (0x17) +#define MPI2_SAS_DEVICE0_ASTATUS_SIF_ZONING_VIOLATION (0x18) +#define MPI2_SAS_DEVICE0_ASTATUS_SIF_NOT_ADDRESSABLE (0x19) +#define MPI2_SAS_DEVICE0_ASTATUS_SIF_MAX (0x1F) + +/* see mpi2_sas.h for values for SAS Device Page 0 DeviceInfo values */ + +/* values for SAS Device Page 0 Flags field */ +#define MPI2_SAS_DEVICE0_FLAGS_SATA_ASYNCHRONOUS_NOTIFY (0x0400) +#define MPI2_SAS_DEVICE0_FLAGS_SATA_SW_PRESERVE (0x0200) +#define MPI2_SAS_DEVICE0_FLAGS_UNSUPPORTED_DEVICE (0x0100) +#define MPI2_SAS_DEVICE0_FLAGS_SATA_48BIT_LBA_SUPPORTED (0x0080) +#define MPI2_SAS_DEVICE0_FLAGS_SATA_SMART_SUPPORTED (0x0040) +#define MPI2_SAS_DEVICE0_FLAGS_SATA_NCQ_SUPPORTED (0x0020) +#define MPI2_SAS_DEVICE0_FLAGS_SATA_FUA_SUPPORTED (0x0010) +#define MPI2_SAS_DEVICE0_FLAGS_PORT_SELECTOR_ATTACH (0x0008) +#define MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT (0x0001) + + +/* SAS Device Page 1 */ + +typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_1 +{ + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U32 Reserved1; /* 0x08 */ + U64 SASAddress; /* 0x0C */ + U32 Reserved2; /* 0x14 */ + U16 DevHandle; /* 0x18 */ + U16 Reserved3; /* 0x1A */ + U8 InitialRegDeviceFIS[20];/* 0x1C */ +} MPI2_CONFIG_PAGE_SAS_DEV_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_DEV_1, + Mpi2SasDevicePage1_t, MPI2_POINTER pMpi2SasDevicePage1_t; + +#define MPI2_SASDEVICE1_PAGEVERSION (0x01) + + +/**************************************************************************** +* SAS PHY Config Pages +****************************************************************************/ + +/* SAS PHY Page 0 */ + +typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_0 +{ + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U16 OwnerDevHandle; /* 0x08 */ + U16 Reserved1; /* 0x0A */ + U16 AttachedDevHandle; /* 0x0C */ + U8 AttachedPhyIdentifier; /* 0x0E */ + U8 Reserved2; /* 0x0F */ + U32 AttachedPhyInfo; /* 0x10 */ + U8 ProgrammedLinkRate; /* 0x14 */ + U8 HwLinkRate; /* 0x15 */ + U8 ChangeCount; /* 0x16 */ + U8 Flags; /* 0x17 */ + U32 PhyInfo; /* 0x18 */ + U8 NegotiatedLinkRate; /* 0x1C */ + U8 Reserved3; /* 0x1D */ + U16 Reserved4; /* 0x1E */ +} MPI2_CONFIG_PAGE_SAS_PHY_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_PHY_0, + Mpi2SasPhyPage0_t, MPI2_POINTER pMpi2SasPhyPage0_t; + +#define MPI2_SASPHY0_PAGEVERSION (0x03) + +/* use MPI2_SAS_PRATE_ defines for the ProgrammedLinkRate field */ + +/* use MPI2_SAS_HWRATE_ defines for the HwLinkRate field */ + +/* values for SAS PHY Page 0 Flags field */ +#define MPI2_SAS_PHY0_FLAGS_SGPIO_DIRECT_ATTACH_ENC (0x01) + +/* use MPI2_SAS_APHYINFO_ defines for AttachedPhyInfo field */ + +/* use MPI2_SAS_NEG_LINK_RATE_ defines for the NegotiatedLinkRate field */ + +/* use MPI2_SAS_PHYINFO_ for the PhyInfo field */ + + +/* SAS PHY Page 1 */ + +typedef struct _MPI2_CONFIG_PAGE_SAS_PHY_1 +{ + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U32 Reserved1; /* 0x08 */ + U32 InvalidDwordCount; /* 0x0C */ + U32 RunningDisparityErrorCount; /* 0x10 */ + U32 LossDwordSynchCount; /* 0x14 */ + U32 PhyResetProblemCount; /* 0x18 */ +} MPI2_CONFIG_PAGE_SAS_PHY_1, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_PHY_1, + Mpi2SasPhyPage1_t, MPI2_POINTER pMpi2SasPhyPage1_t; + +#define MPI2_SASPHY1_PAGEVERSION (0x01) + + +/**************************************************************************** +* SAS Port Config Pages +****************************************************************************/ + +/* SAS Port Page 0 */ + +typedef struct _MPI2_CONFIG_PAGE_SAS_PORT_0 +{ + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U8 PortNumber; /* 0x08 */ + U8 PhysicalPort; /* 0x09 */ + U8 PortWidth; /* 0x0A */ + U8 PhysicalPortWidth; /* 0x0B */ + U8 ZoneGroup; /* 0x0C */ + U8 Reserved1; /* 0x0D */ + U16 Reserved2; /* 0x0E */ + U64 SASAddress; /* 0x10 */ + U32 DeviceInfo; /* 0x18 */ + U32 Reserved3; /* 0x1C */ + U32 Reserved4; /* 0x20 */ +} MPI2_CONFIG_PAGE_SAS_PORT_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_PORT_0, + Mpi2SasPortPage0_t, MPI2_POINTER pMpi2SasPortPage0_t; + +#define MPI2_SASPORT0_PAGEVERSION (0x00) + +/* see mpi2_sas.h for values for SAS Port Page 0 DeviceInfo values */ + + +/**************************************************************************** +* SAS Enclosure Config Pages +****************************************************************************/ + +/* SAS Enclosure Page 0 */ + +typedef struct _MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0 +{ + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U32 Reserved1; /* 0x08 */ + U64 EnclosureLogicalID; /* 0x0C */ + U16 Flags; /* 0x14 */ + U16 EnclosureHandle; /* 0x16 */ + U16 NumSlots; /* 0x18 */ + U16 StartSlot; /* 0x1A */ + U16 Reserved2; /* 0x1C */ + U16 SEPDevHandle; /* 0x1E */ + U32 Reserved3; /* 0x20 */ + U32 Reserved4; /* 0x24 */ +} MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0, + MPI2_POINTER PTR_MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0, + Mpi2SasEnclosurePage0_t, MPI2_POINTER pMpi2SasEnclosurePage0_t; + +#define MPI2_SASENCLOSURE0_PAGEVERSION (0x03) + +/* values for SAS Enclosure Page 0 Flags field */ +#define MPI2_SAS_ENCLS0_FLAGS_MNG_MASK (0x000F) +#define MPI2_SAS_ENCLS0_FLAGS_MNG_UNKNOWN (0x0000) +#define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_SES (0x0001) +#define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_SGPIO (0x0002) +#define MPI2_SAS_ENCLS0_FLAGS_MNG_EXP_SGPIO (0x0003) +#define MPI2_SAS_ENCLS0_FLAGS_MNG_SES_ENCLOSURE (0x0004) +#define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_GPIO (0x0005) + + +/**************************************************************************** +* Log Config Page +****************************************************************************/ + +/* Log Page 0 */ + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.ExtPageLength or NumPhys at runtime. + */ +#ifndef MPI2_LOG_0_NUM_LOG_ENTRIES +#define MPI2_LOG_0_NUM_LOG_ENTRIES (1) +#endif + +#define MPI2_LOG_0_LOG_DATA_LENGTH (0x1C) + +typedef struct _MPI2_LOG_0_ENTRY +{ + U64 TimeStamp; /* 0x00 */ + U32 Reserved1; /* 0x08 */ + U16 LogSequence; /* 0x0C */ + U16 LogEntryQualifier; /* 0x0E */ + U8 VP_ID; /* 0x10 */ + U8 VF_ID; /* 0x11 */ + U16 Reserved2; /* 0x12 */ + U8 LogData[MPI2_LOG_0_LOG_DATA_LENGTH];/* 0x14 */ +} MPI2_LOG_0_ENTRY, MPI2_POINTER PTR_MPI2_LOG_0_ENTRY, + Mpi2Log0Entry_t, MPI2_POINTER pMpi2Log0Entry_t; + +/* values for Log Page 0 LogEntry LogEntryQualifier field */ +#define MPI2_LOG_0_ENTRY_QUAL_ENTRY_UNUSED (0x0000) +#define MPI2_LOG_0_ENTRY_QUAL_POWER_ON_RESET (0x0001) +#define MPI2_LOG_0_ENTRY_QUAL_TIMESTAMP_UPDATE (0x0002) +#define MPI2_LOG_0_ENTRY_QUAL_MIN_IMPLEMENT_SPEC (0x8000) +#define MPI2_LOG_0_ENTRY_QUAL_MAX_IMPLEMENT_SPEC (0xFFFF) + +typedef struct _MPI2_CONFIG_PAGE_LOG_0 +{ + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U32 Reserved1; /* 0x08 */ + U32 Reserved2; /* 0x0C */ + U16 NumLogEntries; /* 0x10 */ + U16 Reserved3; /* 0x12 */ + MPI2_LOG_0_ENTRY LogEntry[MPI2_LOG_0_NUM_LOG_ENTRIES]; /* 0x14 */ +} MPI2_CONFIG_PAGE_LOG_0, MPI2_POINTER PTR_MPI2_CONFIG_PAGE_LOG_0, + Mpi2LogPage0_t, MPI2_POINTER pMpi2LogPage0_t; + +#define MPI2_LOG_0_PAGEVERSION (0x02) + + +/**************************************************************************** +* RAID Config Page +****************************************************************************/ + +/* RAID Page 0 */ + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.ExtPageLength or NumPhys at runtime. + */ +#ifndef MPI2_RAIDCONFIG0_MAX_ELEMENTS +#define MPI2_RAIDCONFIG0_MAX_ELEMENTS (1) +#endif + +typedef struct _MPI2_RAIDCONFIG0_CONFIG_ELEMENT +{ + U16 ElementFlags; /* 0x00 */ + U16 VolDevHandle; /* 0x02 */ + U8 HotSparePool; /* 0x04 */ + U8 PhysDiskNum; /* 0x05 */ + U16 PhysDiskDevHandle; /* 0x06 */ +} MPI2_RAIDCONFIG0_CONFIG_ELEMENT, + MPI2_POINTER PTR_MPI2_RAIDCONFIG0_CONFIG_ELEMENT, + Mpi2RaidConfig0ConfigElement_t, MPI2_POINTER pMpi2RaidConfig0ConfigElement_t; + +/* values for the ElementFlags field */ +#define MPI2_RAIDCONFIG0_EFLAGS_MASK_ELEMENT_TYPE (0x000F) +#define MPI2_RAIDCONFIG0_EFLAGS_VOLUME_ELEMENT (0x0000) +#define MPI2_RAIDCONFIG0_EFLAGS_VOL_PHYS_DISK_ELEMENT (0x0001) +#define MPI2_RAIDCONFIG0_EFLAGS_HOT_SPARE_ELEMENT (0x0002) +#define MPI2_RAIDCONFIG0_EFLAGS_OCE_ELEMENT (0x0003) + + +typedef struct _MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0 +{ + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + U8 NumHotSpares; /* 0x08 */ + U8 NumPhysDisks; /* 0x09 */ + U8 NumVolumes; /* 0x0A */ + U8 ConfigNum; /* 0x0B */ + U32 Flags; /* 0x0C */ + U8 ConfigGUID[24]; /* 0x10 */ + U32 Reserved1; /* 0x28 */ + U8 NumElements; /* 0x2C */ + U8 Reserved2; /* 0x2D */ + U16 Reserved3; /* 0x2E */ + MPI2_RAIDCONFIG0_CONFIG_ELEMENT ConfigElement[MPI2_RAIDCONFIG0_MAX_ELEMENTS]; /* 0x30 */ +} MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0, + MPI2_POINTER PTR_MPI2_CONFIG_PAGE_RAID_CONFIGURATION_0, + Mpi2RaidConfigurationPage0_t, MPI2_POINTER pMpi2RaidConfigurationPage0_t; + +#define MPI2_RAIDCONFIG0_PAGEVERSION (0x00) + +/* values for RAID Configuration Page 0 Flags field */ +#define MPI2_RAIDCONFIG0_FLAG_FOREIGN_CONFIG (0x00000001) + + +/**************************************************************************** +* Driver Persistent Mapping Config Pages +****************************************************************************/ + +/* Driver Persistent Mapping Page 0 */ + +typedef struct _MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY +{ + U64 PhysicalIdentifier; /* 0x00 */ + U16 MappingInformation; /* 0x08 */ + U16 DeviceIndex; /* 0x0A */ + U32 PhysicalBitsMapping; /* 0x0C */ + U32 Reserved1; /* 0x10 */ +} MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY, + MPI2_POINTER PTR_MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY, + Mpi2DriverMap0Entry_t, MPI2_POINTER pMpi2DriverMap0Entry_t; + +typedef struct _MPI2_CONFIG_PAGE_DRIVER_MAPPING_0 +{ + MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /* 0x00 */ + MPI2_CONFIG_PAGE_DRIVER_MAP0_ENTRY Entry; /* 0x08 */ +} MPI2_CONFIG_PAGE_DRIVER_MAPPING_0, + MPI2_POINTER PTR_MPI2_CONFIG_PAGE_DRIVER_MAPPING_0, + Mpi2DriverMappingPage0_t, MPI2_POINTER pMpi2DriverMappingPage0_t; + +#define MPI2_DRIVERMAPPING0_PAGEVERSION (0x00) + +/* values for Driver Persistent Mapping Page 0 MappingInformation field */ +#define MPI2_DRVMAP0_MAPINFO_SLOT_MASK (0x07F0) +#define MPI2_DRVMAP0_MAPINFO_SLOT_SHIFT (4) +#define MPI2_DRVMAP0_MAPINFO_MISSING_MASK (0x000F) + + +#endif + diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_init.h b/drivers/scsi/mpt2sas/mpi/mpi2_init.h new file mode 100644 index 000000000000..f1115f0f0eb2 --- /dev/null +++ b/drivers/scsi/mpt2sas/mpi/mpi2_init.h @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2000-2008 LSI Corporation. + * + * + * Name: mpi2_init.h + * Title: MPI SCSI initiator mode messages and structures + * Creation Date: June 23, 2006 + * + * mpi2_init.h Version: 02.00.06 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * 10-31-07 02.00.01 Fixed name for pMpi2SCSITaskManagementRequest_t. + * 12-18-07 02.00.02 Modified Task Management Target Reset Method defines. + * 02-29-08 02.00.03 Added Query Task Set and Query Unit Attention. + * 03-03-08 02.00.04 Fixed name of struct _MPI2_SCSI_TASK_MANAGE_REPLY. + * 05-21-08 02.00.05 Fixed typo in name of Mpi2SepRequest_t. + * 10-02-08 02.00.06 Removed Untagged and No Disconnect values from SCSI IO + * Control field Task Attribute flags. + * Moved LUN field defines to mpi2.h becasue they are + * common to many structures. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI2_INIT_H +#define MPI2_INIT_H + +/***************************************************************************** +* +* SCSI Initiator Messages +* +*****************************************************************************/ + +/**************************************************************************** +* SCSI IO messages and associated structures +****************************************************************************/ + +typedef struct +{ + U8 CDB[20]; /* 0x00 */ + U32 PrimaryReferenceTag; /* 0x14 */ + U16 PrimaryApplicationTag; /* 0x18 */ + U16 PrimaryApplicationTagMask; /* 0x1A */ + U32 TransferLength; /* 0x1C */ +} MPI2_SCSI_IO_CDB_EEDP32, MPI2_POINTER PTR_MPI2_SCSI_IO_CDB_EEDP32, + Mpi2ScsiIoCdbEedp32_t, MPI2_POINTER pMpi2ScsiIoCdbEedp32_t; + +/* TBD: I don't think this is needed for MPI2/Gen2 */ +#if 0 +typedef struct +{ + U8 CDB[16]; /* 0x00 */ + U32 DataLength; /* 0x10 */ + U32 PrimaryReferenceTag; /* 0x14 */ + U16 PrimaryApplicationTag; /* 0x18 */ + U16 PrimaryApplicationTagMask; /* 0x1A */ + U32 TransferLength; /* 0x1C */ +} MPI2_SCSI_IO32_CDB_EEDP16, MPI2_POINTER PTR_MPI2_SCSI_IO32_CDB_EEDP16, + Mpi2ScsiIo32CdbEedp16_t, MPI2_POINTER pMpi2ScsiIo32CdbEedp16_t; +#endif + +typedef union +{ + U8 CDB32[32]; + MPI2_SCSI_IO_CDB_EEDP32 EEDP32; + MPI2_SGE_SIMPLE_UNION SGE; +} MPI2_SCSI_IO_CDB_UNION, MPI2_POINTER PTR_MPI2_SCSI_IO_CDB_UNION, + Mpi2ScsiIoCdb_t, MPI2_POINTER pMpi2ScsiIoCdb_t; + +/* SCSI IO Request Message */ +typedef struct _MPI2_SCSI_IO_REQUEST +{ + U16 DevHandle; /* 0x00 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved1; /* 0x04 */ + U8 Reserved2; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved3; /* 0x0A */ + U32 SenseBufferLowAddress; /* 0x0C */ + U16 SGLFlags; /* 0x10 */ + U8 SenseBufferLength; /* 0x12 */ + U8 Reserved4; /* 0x13 */ + U8 SGLOffset0; /* 0x14 */ + U8 SGLOffset1; /* 0x15 */ + U8 SGLOffset2; /* 0x16 */ + U8 SGLOffset3; /* 0x17 */ + U32 SkipCount; /* 0x18 */ + U32 DataLength; /* 0x1C */ + U32 BidirectionalDataLength; /* 0x20 */ + U16 IoFlags; /* 0x24 */ + U16 EEDPFlags; /* 0x26 */ + U32 EEDPBlockSize; /* 0x28 */ + U32 SecondaryReferenceTag; /* 0x2C */ + U16 SecondaryApplicationTag; /* 0x30 */ + U16 ApplicationTagTranslationMask; /* 0x32 */ + U8 LUN[8]; /* 0x34 */ + U32 Control; /* 0x3C */ + MPI2_SCSI_IO_CDB_UNION CDB; /* 0x40 */ + MPI2_SGE_IO_UNION SGL; /* 0x60 */ +} MPI2_SCSI_IO_REQUEST, MPI2_POINTER PTR_MPI2_SCSI_IO_REQUEST, + Mpi2SCSIIORequest_t, MPI2_POINTER pMpi2SCSIIORequest_t; + +/* SCSI IO MsgFlags bits */ + +/* MsgFlags for SenseBufferAddressSpace */ +#define MPI2_SCSIIO_MSGFLAGS_MASK_SENSE_ADDR (0x0C) +#define MPI2_SCSIIO_MSGFLAGS_SYSTEM_SENSE_ADDR (0x00) +#define MPI2_SCSIIO_MSGFLAGS_IOCDDR_SENSE_ADDR (0x04) +#define MPI2_SCSIIO_MSGFLAGS_IOCPLB_SENSE_ADDR (0x08) +#define MPI2_SCSIIO_MSGFLAGS_IOCPLBNTA_SENSE_ADDR (0x0C) + +/* SCSI IO SGLFlags bits */ + +/* base values for Data Location Address Space */ +#define MPI2_SCSIIO_SGLFLAGS_ADDR_MASK (0x0C) +#define MPI2_SCSIIO_SGLFLAGS_SYSTEM_ADDR (0x00) +#define MPI2_SCSIIO_SGLFLAGS_IOCDDR_ADDR (0x04) +#define MPI2_SCSIIO_SGLFLAGS_IOCPLB_ADDR (0x08) +#define MPI2_SCSIIO_SGLFLAGS_IOCPLBNTA_ADDR (0x0C) + +/* base values for Type */ +#define MPI2_SCSIIO_SGLFLAGS_TYPE_MASK (0x03) +#define MPI2_SCSIIO_SGLFLAGS_TYPE_MPI (0x00) +#define MPI2_SCSIIO_SGLFLAGS_TYPE_IEEE32 (0x01) +#define MPI2_SCSIIO_SGLFLAGS_TYPE_IEEE64 (0x02) + +/* shift values for each sub-field */ +#define MPI2_SCSIIO_SGLFLAGS_SGL3_SHIFT (12) +#define MPI2_SCSIIO_SGLFLAGS_SGL2_SHIFT (8) +#define MPI2_SCSIIO_SGLFLAGS_SGL1_SHIFT (4) +#define MPI2_SCSIIO_SGLFLAGS_SGL0_SHIFT (0) + +/* SCSI IO IoFlags bits */ + +/* Large CDB Address Space */ +#define MPI2_SCSIIO_CDB_ADDR_MASK (0x6000) +#define MPI2_SCSIIO_CDB_ADDR_SYSTEM (0x0000) +#define MPI2_SCSIIO_CDB_ADDR_IOCDDR (0x2000) +#define MPI2_SCSIIO_CDB_ADDR_IOCPLB (0x4000) +#define MPI2_SCSIIO_CDB_ADDR_IOCPLBNTA (0x6000) + +#define MPI2_SCSIIO_IOFLAGS_LARGE_CDB (0x1000) +#define MPI2_SCSIIO_IOFLAGS_BIDIRECTIONAL (0x0800) +#define MPI2_SCSIIO_IOFLAGS_MULTICAST (0x0400) +#define MPI2_SCSIIO_IOFLAGS_CMD_DETERMINES_DATA_DIR (0x0200) +#define MPI2_SCSIIO_IOFLAGS_CDBLENGTH_MASK (0x01FF) + +/* SCSI IO EEDPFlags bits */ + +#define MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG (0x8000) +#define MPI2_SCSIIO_EEDPFLAGS_INC_SEC_REFTAG (0x4000) +#define MPI2_SCSIIO_EEDPFLAGS_INC_PRI_APPTAG (0x2000) +#define MPI2_SCSIIO_EEDPFLAGS_INC_SEC_APPTAG (0x1000) + +#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG (0x0400) +#define MPI2_SCSIIO_EEDPFLAGS_CHECK_APPTAG (0x0200) +#define MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD (0x0100) + +#define MPI2_SCSIIO_EEDPFLAGS_PASSTHRU_REFTAG (0x0008) + +#define MPI2_SCSIIO_EEDPFLAGS_MASK_OP (0x0007) +#define MPI2_SCSIIO_EEDPFLAGS_NOOP_OP (0x0000) +#define MPI2_SCSIIO_EEDPFLAGS_CHECK_OP (0x0001) +#define MPI2_SCSIIO_EEDPFLAGS_STRIP_OP (0x0002) +#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP (0x0003) +#define MPI2_SCSIIO_EEDPFLAGS_INSERT_OP (0x0004) +#define MPI2_SCSIIO_EEDPFLAGS_REPLACE_OP (0x0006) +#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REGEN_OP (0x0007) + +/* SCSI IO LUN fields: use MPI2_LUN_ from mpi2.h */ + +/* SCSI IO Control bits */ +#define MPI2_SCSIIO_CONTROL_ADDCDBLEN_MASK (0xFC000000) +#define MPI2_SCSIIO_CONTROL_ADDCDBLEN_SHIFT (26) + +#define MPI2_SCSIIO_CONTROL_DATADIRECTION_MASK (0x03000000) +#define MPI2_SCSIIO_CONTROL_NODATATRANSFER (0x00000000) +#define MPI2_SCSIIO_CONTROL_WRITE (0x01000000) +#define MPI2_SCSIIO_CONTROL_READ (0x02000000) +#define MPI2_SCSIIO_CONTROL_BIDIRECTIONAL (0x03000000) + +#define MPI2_SCSIIO_CONTROL_TASKPRI_MASK (0x00007800) +#define MPI2_SCSIIO_CONTROL_TASKPRI_SHIFT (11) + +#define MPI2_SCSIIO_CONTROL_TASKATTRIBUTE_MASK (0x00000700) +#define MPI2_SCSIIO_CONTROL_SIMPLEQ (0x00000000) +#define MPI2_SCSIIO_CONTROL_HEADOFQ (0x00000100) +#define MPI2_SCSIIO_CONTROL_ORDEREDQ (0x00000200) +#define MPI2_SCSIIO_CONTROL_ACAQ (0x00000400) + +#define MPI2_SCSIIO_CONTROL_TLR_MASK (0x000000C0) +#define MPI2_SCSIIO_CONTROL_NO_TLR (0x00000000) +#define MPI2_SCSIIO_CONTROL_TLR_ON (0x00000040) +#define MPI2_SCSIIO_CONTROL_TLR_OFF (0x00000080) + + +/* SCSI IO Error Reply Message */ +typedef struct _MPI2_SCSI_IO_REPLY +{ + U16 DevHandle; /* 0x00 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved1; /* 0x04 */ + U8 Reserved2; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved3; /* 0x0A */ + U8 SCSIStatus; /* 0x0C */ + U8 SCSIState; /* 0x0D */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ + U32 TransferCount; /* 0x14 */ + U32 SenseCount; /* 0x18 */ + U32 ResponseInfo; /* 0x1C */ + U16 TaskTag; /* 0x20 */ + U16 Reserved4; /* 0x22 */ + U32 BidirectionalTransferCount; /* 0x24 */ + U32 Reserved5; /* 0x28 */ + U32 Reserved6; /* 0x2C */ +} MPI2_SCSI_IO_REPLY, MPI2_POINTER PTR_MPI2_SCSI_IO_REPLY, + Mpi2SCSIIOReply_t, MPI2_POINTER pMpi2SCSIIOReply_t; + +/* SCSI IO Reply SCSIStatus values (SAM-4 status codes) */ + +#define MPI2_SCSI_STATUS_GOOD (0x00) +#define MPI2_SCSI_STATUS_CHECK_CONDITION (0x02) +#define MPI2_SCSI_STATUS_CONDITION_MET (0x04) +#define MPI2_SCSI_STATUS_BUSY (0x08) +#define MPI2_SCSI_STATUS_INTERMEDIATE (0x10) +#define MPI2_SCSI_STATUS_INTERMEDIATE_CONDMET (0x14) +#define MPI2_SCSI_STATUS_RESERVATION_CONFLICT (0x18) +#define MPI2_SCSI_STATUS_COMMAND_TERMINATED (0x22) /* obsolete */ +#define MPI2_SCSI_STATUS_TASK_SET_FULL (0x28) +#define MPI2_SCSI_STATUS_ACA_ACTIVE (0x30) +#define MPI2_SCSI_STATUS_TASK_ABORTED (0x40) + +/* SCSI IO Reply SCSIState flags */ + +#define MPI2_SCSI_STATE_RESPONSE_INFO_VALID (0x10) +#define MPI2_SCSI_STATE_TERMINATED (0x08) +#define MPI2_SCSI_STATE_NO_SCSI_STATUS (0x04) +#define MPI2_SCSI_STATE_AUTOSENSE_FAILED (0x02) +#define MPI2_SCSI_STATE_AUTOSENSE_VALID (0x01) + +#define MPI2_SCSI_TASKTAG_UNKNOWN (0xFFFF) + + +/**************************************************************************** +* SCSI Task Management messages +****************************************************************************/ + +/* SCSI Task Management Request Message */ +typedef struct _MPI2_SCSI_TASK_MANAGE_REQUEST +{ + U16 DevHandle; /* 0x00 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U8 Reserved1; /* 0x04 */ + U8 TaskType; /* 0x05 */ + U8 Reserved2; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved3; /* 0x0A */ + U8 LUN[8]; /* 0x0C */ + U32 Reserved4[7]; /* 0x14 */ + U16 TaskMID; /* 0x30 */ + U16 Reserved5; /* 0x32 */ +} MPI2_SCSI_TASK_MANAGE_REQUEST, + MPI2_POINTER PTR_MPI2_SCSI_TASK_MANAGE_REQUEST, + Mpi2SCSITaskManagementRequest_t, + MPI2_POINTER pMpi2SCSITaskManagementRequest_t; + +/* TaskType values */ + +#define MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK (0x01) +#define MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET (0x02) +#define MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET (0x03) +#define MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET (0x05) +#define MPI2_SCSITASKMGMT_TASKTYPE_CLEAR_TASK_SET (0x06) +#define MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK (0x07) +#define MPI2_SCSITASKMGMT_TASKTYPE_CLR_ACA (0x08) +#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_TASK_SET (0x09) +#define MPI2_SCSITASKMGMT_TASKTYPE_QRY_UNIT_ATTENTION (0x0A) + +/* MsgFlags bits */ + +#define MPI2_SCSITASKMGMT_MSGFLAGS_MASK_TARGET_RESET (0x18) +#define MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET (0x00) +#define MPI2_SCSITASKMGMT_MSGFLAGS_NEXUS_RESET_SRST (0x08) +#define MPI2_SCSITASKMGMT_MSGFLAGS_SAS_HARD_LINK_RESET (0x10) + +#define MPI2_SCSITASKMGMT_MSGFLAGS_DO_NOT_SEND_TASK_IU (0x01) + + + +/* SCSI Task Management Reply Message */ +typedef struct _MPI2_SCSI_TASK_MANAGE_REPLY +{ + U16 DevHandle; /* 0x00 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U8 ResponseCode; /* 0x04 */ + U8 TaskType; /* 0x05 */ + U8 Reserved1; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved2; /* 0x0A */ + U16 Reserved3; /* 0x0C */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ + U32 TerminationCount; /* 0x14 */ +} MPI2_SCSI_TASK_MANAGE_REPLY, + MPI2_POINTER PTR_MPI2_SCSI_TASK_MANAGE_REPLY, + Mpi2SCSITaskManagementReply_t, MPI2_POINTER pMpi2SCSIManagementReply_t; + +/* ResponseCode values */ + +#define MPI2_SCSITASKMGMT_RSP_TM_COMPLETE (0x00) +#define MPI2_SCSITASKMGMT_RSP_INVALID_FRAME (0x02) +#define MPI2_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED (0x04) +#define MPI2_SCSITASKMGMT_RSP_TM_FAILED (0x05) +#define MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED (0x08) +#define MPI2_SCSITASKMGMT_RSP_TM_INVALID_LUN (0x09) +#define MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC (0x80) + + +/**************************************************************************** +* SCSI Enclosure Processor messages +****************************************************************************/ + +/* SCSI Enclosure Processor Request Message */ +typedef struct _MPI2_SEP_REQUEST +{ + U16 DevHandle; /* 0x00 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U8 Action; /* 0x04 */ + U8 Flags; /* 0x05 */ + U8 Reserved1; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved2; /* 0x0A */ + U32 SlotStatus; /* 0x0C */ + U32 Reserved3; /* 0x10 */ + U32 Reserved4; /* 0x14 */ + U32 Reserved5; /* 0x18 */ + U16 Slot; /* 0x1C */ + U16 EnclosureHandle; /* 0x1E */ +} MPI2_SEP_REQUEST, MPI2_POINTER PTR_MPI2_SEP_REQUEST, + Mpi2SepRequest_t, MPI2_POINTER pMpi2SepRequest_t; + +/* Action defines */ +#define MPI2_SEP_REQ_ACTION_WRITE_STATUS (0x00) +#define MPI2_SEP_REQ_ACTION_READ_STATUS (0x01) + +/* Flags defines */ +#define MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS (0x00) +#define MPI2_SEP_REQ_FLAGS_ENCLOSURE_SLOT_ADDRESS (0x01) + +/* SlotStatus defines */ +#define MPI2_SEP_REQ_SLOTSTATUS_REQUEST_REMOVE (0x00040000) +#define MPI2_SEP_REQ_SLOTSTATUS_IDENTIFY_REQUEST (0x00020000) +#define MPI2_SEP_REQ_SLOTSTATUS_REBUILD_STOPPED (0x00000200) +#define MPI2_SEP_REQ_SLOTSTATUS_HOT_SPARE (0x00000100) +#define MPI2_SEP_REQ_SLOTSTATUS_UNCONFIGURED (0x00000080) +#define MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT (0x00000040) +#define MPI2_SEP_REQ_SLOTSTATUS_DEV_REBUILDING (0x00000004) +#define MPI2_SEP_REQ_SLOTSTATUS_DEV_FAULTY (0x00000002) +#define MPI2_SEP_REQ_SLOTSTATUS_NO_ERROR (0x00000001) + + +/* SCSI Enclosure Processor Reply Message */ +typedef struct _MPI2_SEP_REPLY +{ + U16 DevHandle; /* 0x00 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U8 Action; /* 0x04 */ + U8 Flags; /* 0x05 */ + U8 Reserved1; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved2; /* 0x0A */ + U16 Reserved3; /* 0x0C */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ + U32 SlotStatus; /* 0x14 */ + U32 Reserved4; /* 0x18 */ + U16 Slot; /* 0x1C */ + U16 EnclosureHandle; /* 0x1E */ +} MPI2_SEP_REPLY, MPI2_POINTER PTR_MPI2_SEP_REPLY, + Mpi2SepReply_t, MPI2_POINTER pMpi2SepReply_t; + +/* SlotStatus defines */ +#define MPI2_SEP_REPLY_SLOTSTATUS_REMOVE_READY (0x00040000) +#define MPI2_SEP_REPLY_SLOTSTATUS_IDENTIFY_REQUEST (0x00020000) +#define MPI2_SEP_REPLY_SLOTSTATUS_REBUILD_STOPPED (0x00000200) +#define MPI2_SEP_REPLY_SLOTSTATUS_HOT_SPARE (0x00000100) +#define MPI2_SEP_REPLY_SLOTSTATUS_UNCONFIGURED (0x00000080) +#define MPI2_SEP_REPLY_SLOTSTATUS_PREDICTED_FAULT (0x00000040) +#define MPI2_SEP_REPLY_SLOTSTATUS_DEV_REBUILDING (0x00000004) +#define MPI2_SEP_REPLY_SLOTSTATUS_DEV_FAULTY (0x00000002) +#define MPI2_SEP_REPLY_SLOTSTATUS_NO_ERROR (0x00000001) + + +#endif + + diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h new file mode 100644 index 000000000000..8c5d81870c03 --- /dev/null +++ b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h @@ -0,0 +1,1295 @@ +/* + * Copyright (c) 2000-2009 LSI Corporation. + * + * + * Name: mpi2_ioc.h + * Title: MPI IOC, Port, Event, FW Download, and FW Upload messages + * Creation Date: October 11, 2006 + * + * mpi2_ioc.h Version: 02.00.10 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * 06-04-07 02.00.01 In IOCFacts Reply structure, renamed MaxDevices to + * MaxTargets. + * Added TotalImageSize field to FWDownload Request. + * Added reserved words to FWUpload Request. + * 06-26-07 02.00.02 Added IR Configuration Change List Event. + * 08-31-07 02.00.03 Removed SystemReplyQueueDepth field from the IOCInit + * request and replaced it with + * ReplyDescriptorPostQueueDepth and ReplyFreeQueueDepth. + * Replaced the MinReplyQueueDepth field of the IOCFacts + * reply with MaxReplyDescriptorPostQueueDepth. + * Added MPI2_RDPQ_DEPTH_MIN define to specify the minimum + * depth for the Reply Descriptor Post Queue. + * Added SASAddress field to Initiator Device Table + * Overflow Event data. + * 10-31-07 02.00.04 Added ReasonCode MPI2_EVENT_SAS_INIT_RC_NOT_RESPONDING + * for SAS Initiator Device Status Change Event data. + * Modified Reason Code defines for SAS Topology Change + * List Event data, including adding a bit for PHY Vacant + * status, and adding a mask for the Reason Code. + * Added define for + * MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING. + * Added define for MPI2_EXT_IMAGE_TYPE_MEGARAID. + * 12-18-07 02.00.05 Added Boot Status defines for the IOCExceptions field of + * the IOCFacts Reply. + * Removed MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER define. + * Moved MPI2_VERSION_UNION to mpi2.h. + * Changed MPI2_EVENT_NOTIFICATION_REQUEST to use masks + * instead of enables, and added SASBroadcastPrimitiveMasks + * field. + * Added Log Entry Added Event and related structure. + * 02-29-08 02.00.06 Added define MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID. + * Removed define MPI2_IOCFACTS_PROTOCOL_SMP_TARGET. + * Added MaxVolumes and MaxPersistentEntries fields to + * IOCFacts reply. + * Added ProtocalFlags and IOCCapabilities fields to + * MPI2_FW_IMAGE_HEADER. + * Removed MPI2_PORTENABLE_FLAGS_ENABLE_SINGLE_PORT. + * 03-03-08 02.00.07 Fixed MPI2_FW_IMAGE_HEADER by changing Reserved26 to + * a U16 (from a U32). + * Removed extra 's' from EventMasks name. + * 06-27-08 02.00.08 Fixed an offset in a comment. + * 10-02-08 02.00.09 Removed SystemReplyFrameSize from MPI2_IOC_INIT_REQUEST. + * Removed CurReplyFrameSize from MPI2_IOC_FACTS_REPLY and + * renamed MinReplyFrameSize to ReplyFrameSize. + * Added MPI2_IOCFACTS_EXCEPT_IR_FOREIGN_CONFIG_MAX. + * Added two new RAIDOperation values for Integrated RAID + * Operations Status Event data. + * Added four new IR Configuration Change List Event data + * ReasonCode values. + * Added two new ReasonCode defines for SAS Device Status + * Change Event data. + * Added three new DiscoveryStatus bits for the SAS + * Discovery event data. + * Added Multiplexing Status Change bit to the PhyStatus + * field of the SAS Topology Change List event data. + * Removed define for MPI2_INIT_IMAGE_BOOTFLAGS_XMEMCOPY. + * BootFlags are now product-specific. + * Added defines for the indivdual signature bytes + * for MPI2_INIT_IMAGE_FOOTER. + * 01-19-09 02.00.10 Added MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY define. + * Added MPI2_EVENT_SAS_DISC_DS_DOWNSTREAM_INITIATOR + * define. + * Added MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE + * define. + * Removed MPI2_EVENT_SAS_DISC_DS_SATA_INIT_FAILURE define. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI2_IOC_H +#define MPI2_IOC_H + +/***************************************************************************** +* +* IOC Messages +* +*****************************************************************************/ + +/**************************************************************************** +* IOCInit message +****************************************************************************/ + +/* IOCInit Request message */ +typedef struct _MPI2_IOC_INIT_REQUEST +{ + U8 WhoInit; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U16 MsgVersion; /* 0x0C */ + U16 HeaderVersion; /* 0x0E */ + U32 Reserved5; /* 0x10 */ + U32 Reserved6; /* 0x14 */ + U16 Reserved7; /* 0x18 */ + U16 SystemRequestFrameSize; /* 0x1A */ + U16 ReplyDescriptorPostQueueDepth; /* 0x1C */ + U16 ReplyFreeQueueDepth; /* 0x1E */ + U32 SenseBufferAddressHigh; /* 0x20 */ + U32 SystemReplyAddressHigh; /* 0x24 */ + U64 SystemRequestFrameBaseAddress; /* 0x28 */ + U64 ReplyDescriptorPostQueueAddress;/* 0x30 */ + U64 ReplyFreeQueueAddress; /* 0x38 */ + U64 TimeStamp; /* 0x40 */ +} MPI2_IOC_INIT_REQUEST, MPI2_POINTER PTR_MPI2_IOC_INIT_REQUEST, + Mpi2IOCInitRequest_t, MPI2_POINTER pMpi2IOCInitRequest_t; + +/* WhoInit values */ +#define MPI2_WHOINIT_NOT_INITIALIZED (0x00) +#define MPI2_WHOINIT_SYSTEM_BIOS (0x01) +#define MPI2_WHOINIT_ROM_BIOS (0x02) +#define MPI2_WHOINIT_PCI_PEER (0x03) +#define MPI2_WHOINIT_HOST_DRIVER (0x04) +#define MPI2_WHOINIT_MANUFACTURER (0x05) + +/* MsgVersion */ +#define MPI2_IOCINIT_MSGVERSION_MAJOR_MASK (0xFF00) +#define MPI2_IOCINIT_MSGVERSION_MAJOR_SHIFT (8) +#define MPI2_IOCINIT_MSGVERSION_MINOR_MASK (0x00FF) +#define MPI2_IOCINIT_MSGVERSION_MINOR_SHIFT (0) + +/* HeaderVersion */ +#define MPI2_IOCINIT_HDRVERSION_UNIT_MASK (0xFF00) +#define MPI2_IOCINIT_HDRVERSION_UNIT_SHIFT (8) +#define MPI2_IOCINIT_HDRVERSION_DEV_MASK (0x00FF) +#define MPI2_IOCINIT_HDRVERSION_DEV_SHIFT (0) + +/* minimum depth for the Reply Descriptor Post Queue */ +#define MPI2_RDPQ_DEPTH_MIN (16) + + +/* IOCInit Reply message */ +typedef struct _MPI2_IOC_INIT_REPLY +{ + U8 WhoInit; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U16 Reserved5; /* 0x0C */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ +} MPI2_IOC_INIT_REPLY, MPI2_POINTER PTR_MPI2_IOC_INIT_REPLY, + Mpi2IOCInitReply_t, MPI2_POINTER pMpi2IOCInitReply_t; + + +/**************************************************************************** +* IOCFacts message +****************************************************************************/ + +/* IOCFacts Request message */ +typedef struct _MPI2_IOC_FACTS_REQUEST +{ + U16 Reserved1; /* 0x00 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ +} MPI2_IOC_FACTS_REQUEST, MPI2_POINTER PTR_MPI2_IOC_FACTS_REQUEST, + Mpi2IOCFactsRequest_t, MPI2_POINTER pMpi2IOCFactsRequest_t; + + +/* IOCFacts Reply message */ +typedef struct _MPI2_IOC_FACTS_REPLY +{ + U16 MsgVersion; /* 0x00 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 HeaderVersion; /* 0x04 */ + U8 IOCNumber; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved1; /* 0x0A */ + U16 IOCExceptions; /* 0x0C */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ + U8 MaxChainDepth; /* 0x14 */ + U8 WhoInit; /* 0x15 */ + U8 NumberOfPorts; /* 0x16 */ + U8 Reserved2; /* 0x17 */ + U16 RequestCredit; /* 0x18 */ + U16 ProductID; /* 0x1A */ + U32 IOCCapabilities; /* 0x1C */ + MPI2_VERSION_UNION FWVersion; /* 0x20 */ + U16 IOCRequestFrameSize; /* 0x24 */ + U16 Reserved3; /* 0x26 */ + U16 MaxInitiators; /* 0x28 */ + U16 MaxTargets; /* 0x2A */ + U16 MaxSasExpanders; /* 0x2C */ + U16 MaxEnclosures; /* 0x2E */ + U16 ProtocolFlags; /* 0x30 */ + U16 HighPriorityCredit; /* 0x32 */ + U16 MaxReplyDescriptorPostQueueDepth; /* 0x34 */ + U8 ReplyFrameSize; /* 0x36 */ + U8 MaxVolumes; /* 0x37 */ + U16 MaxDevHandle; /* 0x38 */ + U16 MaxPersistentEntries; /* 0x3A */ + U32 Reserved4; /* 0x3C */ +} MPI2_IOC_FACTS_REPLY, MPI2_POINTER PTR_MPI2_IOC_FACTS_REPLY, + Mpi2IOCFactsReply_t, MPI2_POINTER pMpi2IOCFactsReply_t; + +/* MsgVersion */ +#define MPI2_IOCFACTS_MSGVERSION_MAJOR_MASK (0xFF00) +#define MPI2_IOCFACTS_MSGVERSION_MAJOR_SHIFT (8) +#define MPI2_IOCFACTS_MSGVERSION_MINOR_MASK (0x00FF) +#define MPI2_IOCFACTS_MSGVERSION_MINOR_SHIFT (0) + +/* HeaderVersion */ +#define MPI2_IOCFACTS_HDRVERSION_UNIT_MASK (0xFF00) +#define MPI2_IOCFACTS_HDRVERSION_UNIT_SHIFT (8) +#define MPI2_IOCFACTS_HDRVERSION_DEV_MASK (0x00FF) +#define MPI2_IOCFACTS_HDRVERSION_DEV_SHIFT (0) + +/* IOCExceptions */ +#define MPI2_IOCFACTS_EXCEPT_IR_FOREIGN_CONFIG_MAX (0x0100) + +#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_MASK (0x00E0) +#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_GOOD (0x0000) +#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_BACKUP (0x0020) +#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_RESTORED (0x0040) +#define MPI2_IOCFACTS_EXCEPT_BOOTSTAT_CORRUPT_BACKUP (0x0060) + +#define MPI2_IOCFACTS_EXCEPT_METADATA_UNSUPPORTED (0x0010) +#define MPI2_IOCFACTS_EXCEPT_MANUFACT_CHECKSUM_FAIL (0x0008) +#define MPI2_IOCFACTS_EXCEPT_FW_CHECKSUM_FAIL (0x0004) +#define MPI2_IOCFACTS_EXCEPT_RAID_CONFIG_INVALID (0x0002) +#define MPI2_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL (0x0001) + +/* defines for WhoInit field are after the IOCInit Request */ + +/* ProductID field uses MPI2_FW_HEADER_PID_ */ + +/* IOCCapabilities */ +#define MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY (0x00002000) +#define MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID (0x00001000) +#define MPI2_IOCFACTS_CAPABILITY_TLR (0x00000800) +#define MPI2_IOCFACTS_CAPABILITY_MULTICAST (0x00000100) +#define MPI2_IOCFACTS_CAPABILITY_BIDIRECTIONAL_TARGET (0x00000080) +#define MPI2_IOCFACTS_CAPABILITY_EEDP (0x00000040) +#define MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER (0x00000010) +#define MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER (0x00000008) +#define MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING (0x00000004) + +/* ProtocolFlags */ +#define MPI2_IOCFACTS_PROTOCOL_SCSI_TARGET (0x0001) +#define MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR (0x0002) + + +/**************************************************************************** +* PortFacts message +****************************************************************************/ + +/* PortFacts Request message */ +typedef struct _MPI2_PORT_FACTS_REQUEST +{ + U16 Reserved1; /* 0x00 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 PortNumber; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved3; /* 0x0A */ +} MPI2_PORT_FACTS_REQUEST, MPI2_POINTER PTR_MPI2_PORT_FACTS_REQUEST, + Mpi2PortFactsRequest_t, MPI2_POINTER pMpi2PortFactsRequest_t; + +/* PortFacts Reply message */ +typedef struct _MPI2_PORT_FACTS_REPLY +{ + U16 Reserved1; /* 0x00 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 PortNumber; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved3; /* 0x0A */ + U16 Reserved4; /* 0x0C */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ + U8 Reserved5; /* 0x14 */ + U8 PortType; /* 0x15 */ + U16 Reserved6; /* 0x16 */ + U16 MaxPostedCmdBuffers; /* 0x18 */ + U16 Reserved7; /* 0x1A */ +} MPI2_PORT_FACTS_REPLY, MPI2_POINTER PTR_MPI2_PORT_FACTS_REPLY, + Mpi2PortFactsReply_t, MPI2_POINTER pMpi2PortFactsReply_t; + +/* PortType values */ +#define MPI2_PORTFACTS_PORTTYPE_INACTIVE (0x00) +#define MPI2_PORTFACTS_PORTTYPE_FC (0x10) +#define MPI2_PORTFACTS_PORTTYPE_ISCSI (0x20) +#define MPI2_PORTFACTS_PORTTYPE_SAS_PHYSICAL (0x30) +#define MPI2_PORTFACTS_PORTTYPE_SAS_VIRTUAL (0x31) + + +/**************************************************************************** +* PortEnable message +****************************************************************************/ + +/* PortEnable Request message */ +typedef struct _MPI2_PORT_ENABLE_REQUEST +{ + U16 Reserved1; /* 0x00 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U8 Reserved2; /* 0x04 */ + U8 PortFlags; /* 0x05 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ +} MPI2_PORT_ENABLE_REQUEST, MPI2_POINTER PTR_MPI2_PORT_ENABLE_REQUEST, + Mpi2PortEnableRequest_t, MPI2_POINTER pMpi2PortEnableRequest_t; + + +/* PortEnable Reply message */ +typedef struct _MPI2_PORT_ENABLE_REPLY +{ + U16 Reserved1; /* 0x00 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U8 Reserved2; /* 0x04 */ + U8 PortFlags; /* 0x05 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U16 Reserved5; /* 0x0C */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ +} MPI2_PORT_ENABLE_REPLY, MPI2_POINTER PTR_MPI2_PORT_ENABLE_REPLY, + Mpi2PortEnableReply_t, MPI2_POINTER pMpi2PortEnableReply_t; + + +/**************************************************************************** +* EventNotification message +****************************************************************************/ + +/* EventNotification Request message */ +#define MPI2_EVENT_NOTIFY_EVENTMASK_WORDS (4) + +typedef struct _MPI2_EVENT_NOTIFICATION_REQUEST +{ + U16 Reserved1; /* 0x00 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U32 Reserved5; /* 0x0C */ + U32 Reserved6; /* 0x10 */ + U32 EventMasks[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS];/* 0x14 */ + U16 SASBroadcastPrimitiveMasks; /* 0x24 */ + U16 Reserved7; /* 0x26 */ + U32 Reserved8; /* 0x28 */ +} MPI2_EVENT_NOTIFICATION_REQUEST, + MPI2_POINTER PTR_MPI2_EVENT_NOTIFICATION_REQUEST, + Mpi2EventNotificationRequest_t, MPI2_POINTER pMpi2EventNotificationRequest_t; + + +/* EventNotification Reply message */ +typedef struct _MPI2_EVENT_NOTIFICATION_REPLY +{ + U16 EventDataLength; /* 0x00 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved1; /* 0x04 */ + U8 AckRequired; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved2; /* 0x0A */ + U16 Reserved3; /* 0x0C */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ + U16 Event; /* 0x14 */ + U16 Reserved4; /* 0x16 */ + U32 EventContext; /* 0x18 */ + U32 EventData[1]; /* 0x1C */ +} MPI2_EVENT_NOTIFICATION_REPLY, MPI2_POINTER PTR_MPI2_EVENT_NOTIFICATION_REPLY, + Mpi2EventNotificationReply_t, MPI2_POINTER pMpi2EventNotificationReply_t; + +/* AckRequired */ +#define MPI2_EVENT_NOTIFICATION_ACK_NOT_REQUIRED (0x00) +#define MPI2_EVENT_NOTIFICATION_ACK_REQUIRED (0x01) + +/* Event */ +#define MPI2_EVENT_LOG_DATA (0x0001) +#define MPI2_EVENT_STATE_CHANGE (0x0002) +#define MPI2_EVENT_HARD_RESET_RECEIVED (0x0005) +#define MPI2_EVENT_EVENT_CHANGE (0x000A) +#define MPI2_EVENT_TASK_SET_FULL (0x000E) +#define MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE (0x000F) +#define MPI2_EVENT_IR_OPERATION_STATUS (0x0014) +#define MPI2_EVENT_SAS_DISCOVERY (0x0016) +#define MPI2_EVENT_SAS_BROADCAST_PRIMITIVE (0x0017) +#define MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE (0x0018) +#define MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW (0x0019) +#define MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST (0x001C) +#define MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE (0x001D) +#define MPI2_EVENT_IR_VOLUME (0x001E) +#define MPI2_EVENT_IR_PHYSICAL_DISK (0x001F) +#define MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST (0x0020) +#define MPI2_EVENT_LOG_ENTRY_ADDED (0x0021) + + +/* Log Entry Added Event data */ + +/* the following structure matches MPI2_LOG_0_ENTRY in mpi2_cnfg.h */ +#define MPI2_EVENT_DATA_LOG_DATA_LENGTH (0x1C) + +typedef struct _MPI2_EVENT_DATA_LOG_ENTRY_ADDED +{ + U64 TimeStamp; /* 0x00 */ + U32 Reserved1; /* 0x08 */ + U16 LogSequence; /* 0x0C */ + U16 LogEntryQualifier; /* 0x0E */ + U8 VP_ID; /* 0x10 */ + U8 VF_ID; /* 0x11 */ + U16 Reserved2; /* 0x12 */ + U8 LogData[MPI2_EVENT_DATA_LOG_DATA_LENGTH];/* 0x14 */ +} MPI2_EVENT_DATA_LOG_ENTRY_ADDED, + MPI2_POINTER PTR_MPI2_EVENT_DATA_LOG_ENTRY_ADDED, + Mpi2EventDataLogEntryAdded_t, MPI2_POINTER pMpi2EventDataLogEntryAdded_t; + +/* Hard Reset Received Event data */ + +typedef struct _MPI2_EVENT_DATA_HARD_RESET_RECEIVED +{ + U8 Reserved1; /* 0x00 */ + U8 Port; /* 0x01 */ + U16 Reserved2; /* 0x02 */ +} MPI2_EVENT_DATA_HARD_RESET_RECEIVED, + MPI2_POINTER PTR_MPI2_EVENT_DATA_HARD_RESET_RECEIVED, + Mpi2EventDataHardResetReceived_t, + MPI2_POINTER pMpi2EventDataHardResetReceived_t; + +/* Task Set Full Event data */ + +typedef struct _MPI2_EVENT_DATA_TASK_SET_FULL +{ + U16 DevHandle; /* 0x00 */ + U16 CurrentDepth; /* 0x02 */ +} MPI2_EVENT_DATA_TASK_SET_FULL, MPI2_POINTER PTR_MPI2_EVENT_DATA_TASK_SET_FULL, + Mpi2EventDataTaskSetFull_t, MPI2_POINTER pMpi2EventDataTaskSetFull_t; + + +/* SAS Device Status Change Event data */ + +typedef struct _MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE +{ + U16 TaskTag; /* 0x00 */ + U8 ReasonCode; /* 0x02 */ + U8 Reserved1; /* 0x03 */ + U8 ASC; /* 0x04 */ + U8 ASCQ; /* 0x05 */ + U16 DevHandle; /* 0x06 */ + U32 Reserved2; /* 0x08 */ + U64 SASAddress; /* 0x0C */ + U8 LUN[8]; /* 0x14 */ +} MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE, + MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE, + Mpi2EventDataSasDeviceStatusChange_t, + MPI2_POINTER pMpi2EventDataSasDeviceStatusChange_t; + +/* SAS Device Status Change Event data ReasonCode values */ +#define MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA (0x05) +#define MPI2_EVENT_SAS_DEV_STAT_RC_UNSUPPORTED (0x07) +#define MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET (0x08) +#define MPI2_EVENT_SAS_DEV_STAT_RC_TASK_ABORT_INTERNAL (0x09) +#define MPI2_EVENT_SAS_DEV_STAT_RC_ABORT_TASK_SET_INTERNAL (0x0A) +#define MPI2_EVENT_SAS_DEV_STAT_RC_CLEAR_TASK_SET_INTERNAL (0x0B) +#define MPI2_EVENT_SAS_DEV_STAT_RC_QUERY_TASK_INTERNAL (0x0C) +#define MPI2_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION (0x0D) +#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET (0x0E) +#define MPI2_EVENT_SAS_DEV_STAT_RC_CMP_TASK_ABORT_INTERNAL (0x0F) +#define MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE (0x10) + + +/* Integrated RAID Operation Status Event data */ + +typedef struct _MPI2_EVENT_DATA_IR_OPERATION_STATUS +{ + U16 VolDevHandle; /* 0x00 */ + U16 Reserved1; /* 0x02 */ + U8 RAIDOperation; /* 0x04 */ + U8 PercentComplete; /* 0x05 */ + U16 Reserved2; /* 0x06 */ + U32 Resereved3; /* 0x08 */ +} MPI2_EVENT_DATA_IR_OPERATION_STATUS, + MPI2_POINTER PTR_MPI2_EVENT_DATA_IR_OPERATION_STATUS, + Mpi2EventDataIrOperationStatus_t, + MPI2_POINTER pMpi2EventDataIrOperationStatus_t; + +/* Integrated RAID Operation Status Event data RAIDOperation values */ +#define MPI2_EVENT_IR_RAIDOP_RESYNC (0x00) +#define MPI2_EVENT_IR_RAIDOP_ONLINE_CAP_EXPANSION (0x01) +#define MPI2_EVENT_IR_RAIDOP_CONSISTENCY_CHECK (0x02) +#define MPI2_EVENT_IR_RAIDOP_BACKGROUND_INIT (0x03) +#define MPI2_EVENT_IR_RAIDOP_MAKE_DATA_CONSISTENT (0x04) + + +/* Integrated RAID Volume Event data */ + +typedef struct _MPI2_EVENT_DATA_IR_VOLUME +{ + U16 VolDevHandle; /* 0x00 */ + U8 ReasonCode; /* 0x02 */ + U8 Reserved1; /* 0x03 */ + U32 NewValue; /* 0x04 */ + U32 PreviousValue; /* 0x08 */ +} MPI2_EVENT_DATA_IR_VOLUME, MPI2_POINTER PTR_MPI2_EVENT_DATA_IR_VOLUME, + Mpi2EventDataIrVolume_t, MPI2_POINTER pMpi2EventDataIrVolume_t; + +/* Integrated RAID Volume Event data ReasonCode values */ +#define MPI2_EVENT_IR_VOLUME_RC_SETTINGS_CHANGED (0x01) +#define MPI2_EVENT_IR_VOLUME_RC_STATUS_FLAGS_CHANGED (0x02) +#define MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED (0x03) + + +/* Integrated RAID Physical Disk Event data */ + +typedef struct _MPI2_EVENT_DATA_IR_PHYSICAL_DISK +{ + U16 Reserved1; /* 0x00 */ + U8 ReasonCode; /* 0x02 */ + U8 PhysDiskNum; /* 0x03 */ + U16 PhysDiskDevHandle; /* 0x04 */ + U16 Reserved2; /* 0x06 */ + U16 Slot; /* 0x08 */ + U16 EnclosureHandle; /* 0x0A */ + U32 NewValue; /* 0x0C */ + U32 PreviousValue; /* 0x10 */ +} MPI2_EVENT_DATA_IR_PHYSICAL_DISK, + MPI2_POINTER PTR_MPI2_EVENT_DATA_IR_PHYSICAL_DISK, + Mpi2EventDataIrPhysicalDisk_t, MPI2_POINTER pMpi2EventDataIrPhysicalDisk_t; + +/* Integrated RAID Physical Disk Event data ReasonCode values */ +#define MPI2_EVENT_IR_PHYSDISK_RC_SETTINGS_CHANGED (0x01) +#define MPI2_EVENT_IR_PHYSDISK_RC_STATUS_FLAGS_CHANGED (0x02) +#define MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED (0x03) + + +/* Integrated RAID Configuration Change List Event data */ + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check NumElements at runtime. + */ +#ifndef MPI2_EVENT_IR_CONFIG_ELEMENT_COUNT +#define MPI2_EVENT_IR_CONFIG_ELEMENT_COUNT (1) +#endif + +typedef struct _MPI2_EVENT_IR_CONFIG_ELEMENT +{ + U16 ElementFlags; /* 0x00 */ + U16 VolDevHandle; /* 0x02 */ + U8 ReasonCode; /* 0x04 */ + U8 PhysDiskNum; /* 0x05 */ + U16 PhysDiskDevHandle; /* 0x06 */ +} MPI2_EVENT_IR_CONFIG_ELEMENT, MPI2_POINTER PTR_MPI2_EVENT_IR_CONFIG_ELEMENT, + Mpi2EventIrConfigElement_t, MPI2_POINTER pMpi2EventIrConfigElement_t; + +/* IR Configuration Change List Event data ElementFlags values */ +#define MPI2_EVENT_IR_CHANGE_EFLAGS_ELEMENT_TYPE_MASK (0x000F) +#define MPI2_EVENT_IR_CHANGE_EFLAGS_VOLUME_ELEMENT (0x0000) +#define MPI2_EVENT_IR_CHANGE_EFLAGS_VOLPHYSDISK_ELEMENT (0x0001) +#define MPI2_EVENT_IR_CHANGE_EFLAGS_HOTSPARE_ELEMENT (0x0002) + +/* IR Configuration Change List Event data ReasonCode values */ +#define MPI2_EVENT_IR_CHANGE_RC_ADDED (0x01) +#define MPI2_EVENT_IR_CHANGE_RC_REMOVED (0x02) +#define MPI2_EVENT_IR_CHANGE_RC_NO_CHANGE (0x03) +#define MPI2_EVENT_IR_CHANGE_RC_HIDE (0x04) +#define MPI2_EVENT_IR_CHANGE_RC_UNHIDE (0x05) +#define MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED (0x06) +#define MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED (0x07) +#define MPI2_EVENT_IR_CHANGE_RC_PD_CREATED (0x08) +#define MPI2_EVENT_IR_CHANGE_RC_PD_DELETED (0x09) + +typedef struct _MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST +{ + U8 NumElements; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 Reserved2; /* 0x02 */ + U8 ConfigNum; /* 0x03 */ + U32 Flags; /* 0x04 */ + MPI2_EVENT_IR_CONFIG_ELEMENT ConfigElement[MPI2_EVENT_IR_CONFIG_ELEMENT_COUNT]; /* 0x08 */ +} MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST, + MPI2_POINTER PTR_MPI2_EVENT_DATA_IR_CONFIG_CHANGE_LIST, + Mpi2EventDataIrConfigChangeList_t, + MPI2_POINTER pMpi2EventDataIrConfigChangeList_t; + +/* IR Configuration Change List Event data Flags values */ +#define MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG (0x00000001) + + +/* SAS Discovery Event data */ + +typedef struct _MPI2_EVENT_DATA_SAS_DISCOVERY +{ + U8 Flags; /* 0x00 */ + U8 ReasonCode; /* 0x01 */ + U8 PhysicalPort; /* 0x02 */ + U8 Reserved1; /* 0x03 */ + U32 DiscoveryStatus; /* 0x04 */ +} MPI2_EVENT_DATA_SAS_DISCOVERY, + MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_DISCOVERY, + Mpi2EventDataSasDiscovery_t, MPI2_POINTER pMpi2EventDataSasDiscovery_t; + +/* SAS Discovery Event data Flags values */ +#define MPI2_EVENT_SAS_DISC_DEVICE_CHANGE (0x02) +#define MPI2_EVENT_SAS_DISC_IN_PROGRESS (0x01) + +/* SAS Discovery Event data ReasonCode values */ +#define MPI2_EVENT_SAS_DISC_RC_STARTED (0x01) +#define MPI2_EVENT_SAS_DISC_RC_COMPLETED (0x02) + +/* SAS Discovery Event data DiscoveryStatus values */ +#define MPI2_EVENT_SAS_DISC_DS_MAX_ENCLOSURES_EXCEED (0x80000000) +#define MPI2_EVENT_SAS_DISC_DS_MAX_EXPANDERS_EXCEED (0x40000000) +#define MPI2_EVENT_SAS_DISC_DS_MAX_DEVICES_EXCEED (0x20000000) +#define MPI2_EVENT_SAS_DISC_DS_MAX_TOPO_PHYS_EXCEED (0x10000000) +#define MPI2_EVENT_SAS_DISC_DS_DOWNSTREAM_INITIATOR (0x08000000) +#define MPI2_EVENT_SAS_DISC_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE (0x00008000) +#define MPI2_EVENT_SAS_DISC_DS_EXP_MULTI_SUBTRACTIVE (0x00004000) +#define MPI2_EVENT_SAS_DISC_DS_MULTI_PORT_DOMAIN (0x00002000) +#define MPI2_EVENT_SAS_DISC_DS_TABLE_TO_SUBTRACTIVE_LINK (0x00001000) +#define MPI2_EVENT_SAS_DISC_DS_UNSUPPORTED_DEVICE (0x00000800) +#define MPI2_EVENT_SAS_DISC_DS_TABLE_LINK (0x00000400) +#define MPI2_EVENT_SAS_DISC_DS_SUBTRACTIVE_LINK (0x00000200) +#define MPI2_EVENT_SAS_DISC_DS_SMP_CRC_ERROR (0x00000100) +#define MPI2_EVENT_SAS_DISC_DS_SMP_FUNCTION_FAILED (0x00000080) +#define MPI2_EVENT_SAS_DISC_DS_INDEX_NOT_EXIST (0x00000040) +#define MPI2_EVENT_SAS_DISC_DS_OUT_ROUTE_ENTRIES (0x00000020) +#define MPI2_EVENT_SAS_DISC_DS_SMP_TIMEOUT (0x00000010) +#define MPI2_EVENT_SAS_DISC_DS_MULTIPLE_PORTS (0x00000004) +#define MPI2_EVENT_SAS_DISC_DS_UNADDRESSABLE_DEVICE (0x00000002) +#define MPI2_EVENT_SAS_DISC_DS_LOOP_DETECTED (0x00000001) + + +/* SAS Broadcast Primitive Event data */ + +typedef struct _MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE +{ + U8 PhyNum; /* 0x00 */ + U8 Port; /* 0x01 */ + U8 PortWidth; /* 0x02 */ + U8 Primitive; /* 0x03 */ +} MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE, + MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_BROADCAST_PRIMITIVE, + Mpi2EventDataSasBroadcastPrimitive_t, + MPI2_POINTER pMpi2EventDataSasBroadcastPrimitive_t; + +/* defines for the Primitive field */ +#define MPI2_EVENT_PRIMITIVE_CHANGE (0x01) +#define MPI2_EVENT_PRIMITIVE_SES (0x02) +#define MPI2_EVENT_PRIMITIVE_EXPANDER (0x03) +#define MPI2_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT (0x04) +#define MPI2_EVENT_PRIMITIVE_RESERVED3 (0x05) +#define MPI2_EVENT_PRIMITIVE_RESERVED4 (0x06) +#define MPI2_EVENT_PRIMITIVE_CHANGE0_RESERVED (0x07) +#define MPI2_EVENT_PRIMITIVE_CHANGE1_RESERVED (0x08) + + +/* SAS Initiator Device Status Change Event data */ + +typedef struct _MPI2_EVENT_DATA_SAS_INIT_DEV_STATUS_CHANGE +{ + U8 ReasonCode; /* 0x00 */ + U8 PhysicalPort; /* 0x01 */ + U16 DevHandle; /* 0x02 */ + U64 SASAddress; /* 0x04 */ +} MPI2_EVENT_DATA_SAS_INIT_DEV_STATUS_CHANGE, + MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_INIT_DEV_STATUS_CHANGE, + Mpi2EventDataSasInitDevStatusChange_t, + MPI2_POINTER pMpi2EventDataSasInitDevStatusChange_t; + +/* SAS Initiator Device Status Change event ReasonCode values */ +#define MPI2_EVENT_SAS_INIT_RC_ADDED (0x01) +#define MPI2_EVENT_SAS_INIT_RC_NOT_RESPONDING (0x02) + + +/* SAS Initiator Device Table Overflow Event data */ + +typedef struct _MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW +{ + U16 MaxInit; /* 0x00 */ + U16 CurrentInit; /* 0x02 */ + U64 SASAddress; /* 0x04 */ +} MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW, + MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_INIT_TABLE_OVERFLOW, + Mpi2EventDataSasInitTableOverflow_t, + MPI2_POINTER pMpi2EventDataSasInitTableOverflow_t; + + +/* SAS Topology Change List Event data */ + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check NumEntries at runtime. + */ +#ifndef MPI2_EVENT_SAS_TOPO_PHY_COUNT +#define MPI2_EVENT_SAS_TOPO_PHY_COUNT (1) +#endif + +typedef struct _MPI2_EVENT_SAS_TOPO_PHY_ENTRY +{ + U16 AttachedDevHandle; /* 0x00 */ + U8 LinkRate; /* 0x02 */ + U8 PhyStatus; /* 0x03 */ +} MPI2_EVENT_SAS_TOPO_PHY_ENTRY, MPI2_POINTER PTR_MPI2_EVENT_SAS_TOPO_PHY_ENTRY, + Mpi2EventSasTopoPhyEntry_t, MPI2_POINTER pMpi2EventSasTopoPhyEntry_t; + +typedef struct _MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST +{ + U16 EnclosureHandle; /* 0x00 */ + U16 ExpanderDevHandle; /* 0x02 */ + U8 NumPhys; /* 0x04 */ + U8 Reserved1; /* 0x05 */ + U16 Reserved2; /* 0x06 */ + U8 NumEntries; /* 0x08 */ + U8 StartPhyNum; /* 0x09 */ + U8 ExpStatus; /* 0x0A */ + U8 PhysicalPort; /* 0x0B */ + MPI2_EVENT_SAS_TOPO_PHY_ENTRY PHY[MPI2_EVENT_SAS_TOPO_PHY_COUNT]; /* 0x0C*/ +} MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST, + MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_TOPOLOGY_CHANGE_LIST, + Mpi2EventDataSasTopologyChangeList_t, + MPI2_POINTER pMpi2EventDataSasTopologyChangeList_t; + +/* values for the ExpStatus field */ +#define MPI2_EVENT_SAS_TOPO_ES_ADDED (0x01) +#define MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING (0x02) +#define MPI2_EVENT_SAS_TOPO_ES_RESPONDING (0x03) +#define MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING (0x04) + +/* defines for the LinkRate field */ +#define MPI2_EVENT_SAS_TOPO_LR_CURRENT_MASK (0xF0) +#define MPI2_EVENT_SAS_TOPO_LR_CURRENT_SHIFT (4) +#define MPI2_EVENT_SAS_TOPO_LR_PREV_MASK (0x0F) +#define MPI2_EVENT_SAS_TOPO_LR_PREV_SHIFT (0) + +#define MPI2_EVENT_SAS_TOPO_LR_UNKNOWN_LINK_RATE (0x00) +#define MPI2_EVENT_SAS_TOPO_LR_PHY_DISABLED (0x01) +#define MPI2_EVENT_SAS_TOPO_LR_NEGOTIATION_FAILED (0x02) +#define MPI2_EVENT_SAS_TOPO_LR_SATA_OOB_COMPLETE (0x03) +#define MPI2_EVENT_SAS_TOPO_LR_PORT_SELECTOR (0x04) +#define MPI2_EVENT_SAS_TOPO_LR_SMP_RESET_IN_PROGRESS (0x05) +#define MPI2_EVENT_SAS_TOPO_LR_RATE_1_5 (0x08) +#define MPI2_EVENT_SAS_TOPO_LR_RATE_3_0 (0x09) +#define MPI2_EVENT_SAS_TOPO_LR_RATE_6_0 (0x0A) + +/* values for the PhyStatus field */ +#define MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT (0x80) +#define MPI2_EVENT_SAS_TOPO_PS_MULTIPLEX_CHANGE (0x10) +/* values for the PhyStatus ReasonCode sub-field */ +#define MPI2_EVENT_SAS_TOPO_RC_MASK (0x0F) +#define MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED (0x01) +#define MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING (0x02) +#define MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED (0x03) +#define MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE (0x04) +#define MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING (0x05) + + +/* SAS Enclosure Device Status Change Event data */ + +typedef struct _MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE +{ + U16 EnclosureHandle; /* 0x00 */ + U8 ReasonCode; /* 0x02 */ + U8 PhysicalPort; /* 0x03 */ + U64 EnclosureLogicalID; /* 0x04 */ + U16 NumSlots; /* 0x0C */ + U16 StartSlot; /* 0x0E */ + U32 PhyBits; /* 0x10 */ +} MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE, + MPI2_POINTER PTR_MPI2_EVENT_DATA_SAS_ENCL_DEV_STATUS_CHANGE, + Mpi2EventDataSasEnclDevStatusChange_t, + MPI2_POINTER pMpi2EventDataSasEnclDevStatusChange_t; + +/* SAS Enclosure Device Status Change event ReasonCode values */ +#define MPI2_EVENT_SAS_ENCL_RC_ADDED (0x01) +#define MPI2_EVENT_SAS_ENCL_RC_NOT_RESPONDING (0x02) + + +/**************************************************************************** +* EventAck message +****************************************************************************/ + +/* EventAck Request message */ +typedef struct _MPI2_EVENT_ACK_REQUEST +{ + U16 Reserved1; /* 0x00 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U16 Event; /* 0x0C */ + U16 Reserved5; /* 0x0E */ + U32 EventContext; /* 0x10 */ +} MPI2_EVENT_ACK_REQUEST, MPI2_POINTER PTR_MPI2_EVENT_ACK_REQUEST, + Mpi2EventAckRequest_t, MPI2_POINTER pMpi2EventAckRequest_t; + + +/* EventAck Reply message */ +typedef struct _MPI2_EVENT_ACK_REPLY +{ + U16 Reserved1; /* 0x00 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U16 Reserved5; /* 0x0C */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ +} MPI2_EVENT_ACK_REPLY, MPI2_POINTER PTR_MPI2_EVENT_ACK_REPLY, + Mpi2EventAckReply_t, MPI2_POINTER pMpi2EventAckReply_t; + + +/**************************************************************************** +* FWDownload message +****************************************************************************/ + +/* FWDownload Request message */ +typedef struct _MPI2_FW_DOWNLOAD_REQUEST +{ + U8 ImageType; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U32 TotalImageSize; /* 0x0C */ + U32 Reserved5; /* 0x10 */ + MPI2_MPI_SGE_UNION SGL; /* 0x14 */ +} MPI2_FW_DOWNLOAD_REQUEST, MPI2_POINTER PTR_MPI2_FW_DOWNLOAD_REQUEST, + Mpi2FWDownloadRequest, MPI2_POINTER pMpi2FWDownloadRequest; + +#define MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT (0x01) + +#define MPI2_FW_DOWNLOAD_ITYPE_FW (0x01) +#define MPI2_FW_DOWNLOAD_ITYPE_BIOS (0x02) +#define MPI2_FW_DOWNLOAD_ITYPE_MANUFACTURING (0x06) +#define MPI2_FW_DOWNLOAD_ITYPE_CONFIG_1 (0x07) +#define MPI2_FW_DOWNLOAD_ITYPE_CONFIG_2 (0x08) +#define MPI2_FW_DOWNLOAD_ITYPE_MEGARAID (0x09) +#define MPI2_FW_DOWNLOAD_ITYPE_COMMON_BOOT_BLOCK (0x0B) + +/* FWDownload TransactionContext Element */ +typedef struct _MPI2_FW_DOWNLOAD_TCSGE +{ + U8 Reserved1; /* 0x00 */ + U8 ContextSize; /* 0x01 */ + U8 DetailsLength; /* 0x02 */ + U8 Flags; /* 0x03 */ + U32 Reserved2; /* 0x04 */ + U32 ImageOffset; /* 0x08 */ + U32 ImageSize; /* 0x0C */ +} MPI2_FW_DOWNLOAD_TCSGE, MPI2_POINTER PTR_MPI2_FW_DOWNLOAD_TCSGE, + Mpi2FWDownloadTCSGE_t, MPI2_POINTER pMpi2FWDownloadTCSGE_t; + +/* FWDownload Reply message */ +typedef struct _MPI2_FW_DOWNLOAD_REPLY +{ + U8 ImageType; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U16 Reserved5; /* 0x0C */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ +} MPI2_FW_DOWNLOAD_REPLY, MPI2_POINTER PTR_MPI2_FW_DOWNLOAD_REPLY, + Mpi2FWDownloadReply_t, MPI2_POINTER pMpi2FWDownloadReply_t; + + +/**************************************************************************** +* FWUpload message +****************************************************************************/ + +/* FWUpload Request message */ +typedef struct _MPI2_FW_UPLOAD_REQUEST +{ + U8 ImageType; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U32 Reserved5; /* 0x0C */ + U32 Reserved6; /* 0x10 */ + MPI2_MPI_SGE_UNION SGL; /* 0x14 */ +} MPI2_FW_UPLOAD_REQUEST, MPI2_POINTER PTR_MPI2_FW_UPLOAD_REQUEST, + Mpi2FWUploadRequest_t, MPI2_POINTER pMpi2FWUploadRequest_t; + +#define MPI2_FW_UPLOAD_ITYPE_FW_CURRENT (0x00) +#define MPI2_FW_UPLOAD_ITYPE_FW_FLASH (0x01) +#define MPI2_FW_UPLOAD_ITYPE_BIOS_FLASH (0x02) +#define MPI2_FW_UPLOAD_ITYPE_FW_BACKUP (0x05) +#define MPI2_FW_UPLOAD_ITYPE_MANUFACTURING (0x06) +#define MPI2_FW_UPLOAD_ITYPE_CONFIG_1 (0x07) +#define MPI2_FW_UPLOAD_ITYPE_CONFIG_2 (0x08) +#define MPI2_FW_UPLOAD_ITYPE_MEGARAID (0x09) +#define MPI2_FW_UPLOAD_ITYPE_COMPLETE (0x0A) +#define MPI2_FW_UPLOAD_ITYPE_COMMON_BOOT_BLOCK (0x0B) + +typedef struct _MPI2_FW_UPLOAD_TCSGE +{ + U8 Reserved1; /* 0x00 */ + U8 ContextSize; /* 0x01 */ + U8 DetailsLength; /* 0x02 */ + U8 Flags; /* 0x03 */ + U32 Reserved2; /* 0x04 */ + U32 ImageOffset; /* 0x08 */ + U32 ImageSize; /* 0x0C */ +} MPI2_FW_UPLOAD_TCSGE, MPI2_POINTER PTR_MPI2_FW_UPLOAD_TCSGE, + Mpi2FWUploadTCSGE_t, MPI2_POINTER pMpi2FWUploadTCSGE_t; + +/* FWUpload Reply message */ +typedef struct _MPI2_FW_UPLOAD_REPLY +{ + U8 ImageType; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U16 Reserved5; /* 0x0C */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ + U32 ActualImageSize; /* 0x14 */ +} MPI2_FW_UPLOAD_REPLY, MPI2_POINTER PTR_MPI2_FW_UPLOAD_REPLY, + Mpi2FWUploadReply_t, MPI2_POINTER pMPi2FWUploadReply_t; + + +/* FW Image Header */ +typedef struct _MPI2_FW_IMAGE_HEADER +{ + U32 Signature; /* 0x00 */ + U32 Signature0; /* 0x04 */ + U32 Signature1; /* 0x08 */ + U32 Signature2; /* 0x0C */ + MPI2_VERSION_UNION MPIVersion; /* 0x10 */ + MPI2_VERSION_UNION FWVersion; /* 0x14 */ + MPI2_VERSION_UNION NVDATAVersion; /* 0x18 */ + MPI2_VERSION_UNION PackageVersion; /* 0x1C */ + U16 VendorID; /* 0x20 */ + U16 ProductID; /* 0x22 */ + U16 ProtocolFlags; /* 0x24 */ + U16 Reserved26; /* 0x26 */ + U32 IOCCapabilities; /* 0x28 */ + U32 ImageSize; /* 0x2C */ + U32 NextImageHeaderOffset; /* 0x30 */ + U32 Checksum; /* 0x34 */ + U32 Reserved38; /* 0x38 */ + U32 Reserved3C; /* 0x3C */ + U32 Reserved40; /* 0x40 */ + U32 Reserved44; /* 0x44 */ + U32 Reserved48; /* 0x48 */ + U32 Reserved4C; /* 0x4C */ + U32 Reserved50; /* 0x50 */ + U32 Reserved54; /* 0x54 */ + U32 Reserved58; /* 0x58 */ + U32 Reserved5C; /* 0x5C */ + U32 Reserved60; /* 0x60 */ + U32 FirmwareVersionNameWhat; /* 0x64 */ + U8 FirmwareVersionName[32]; /* 0x68 */ + U32 VendorNameWhat; /* 0x88 */ + U8 VendorName[32]; /* 0x8C */ + U32 PackageNameWhat; /* 0x88 */ + U8 PackageName[32]; /* 0x8C */ + U32 ReservedD0; /* 0xD0 */ + U32 ReservedD4; /* 0xD4 */ + U32 ReservedD8; /* 0xD8 */ + U32 ReservedDC; /* 0xDC */ + U32 ReservedE0; /* 0xE0 */ + U32 ReservedE4; /* 0xE4 */ + U32 ReservedE8; /* 0xE8 */ + U32 ReservedEC; /* 0xEC */ + U32 ReservedF0; /* 0xF0 */ + U32 ReservedF4; /* 0xF4 */ + U32 ReservedF8; /* 0xF8 */ + U32 ReservedFC; /* 0xFC */ +} MPI2_FW_IMAGE_HEADER, MPI2_POINTER PTR_MPI2_FW_IMAGE_HEADER, + Mpi2FWImageHeader_t, MPI2_POINTER pMpi2FWImageHeader_t; + +/* Signature field */ +#define MPI2_FW_HEADER_SIGNATURE_OFFSET (0x00) +#define MPI2_FW_HEADER_SIGNATURE_MASK (0xFF000000) +#define MPI2_FW_HEADER_SIGNATURE (0xEA000000) + +/* Signature0 field */ +#define MPI2_FW_HEADER_SIGNATURE0_OFFSET (0x04) +#define MPI2_FW_HEADER_SIGNATURE0 (0x5AFAA55A) + +/* Signature1 field */ +#define MPI2_FW_HEADER_SIGNATURE1_OFFSET (0x08) +#define MPI2_FW_HEADER_SIGNATURE1 (0xA55AFAA5) + +/* Signature2 field */ +#define MPI2_FW_HEADER_SIGNATURE2_OFFSET (0x0C) +#define MPI2_FW_HEADER_SIGNATURE2 (0x5AA55AFA) + + +/* defines for using the ProductID field */ +#define MPI2_FW_HEADER_PID_TYPE_MASK (0xF000) +#define MPI2_FW_HEADER_PID_TYPE_SAS (0x2000) + +#define MPI2_FW_HEADER_PID_PROD_MASK (0x0F00) +#define MPI2_FW_HEADER_PID_PROD_A (0x0000) + +#define MPI2_FW_HEADER_PID_FAMILY_MASK (0x00FF) +/* SAS */ +#define MPI2_FW_HEADER_PID_FAMILY_2108_SAS (0x0010) + +/* use MPI2_IOCFACTS_PROTOCOL_ defines for ProtocolFlags field */ + +/* use MPI2_IOCFACTS_CAPABILITY_ defines for IOCCapabilities field */ + + +#define MPI2_FW_HEADER_IMAGESIZE_OFFSET (0x2C) +#define MPI2_FW_HEADER_NEXTIMAGE_OFFSET (0x30) +#define MPI2_FW_HEADER_VERNMHWAT_OFFSET (0x64) + +#define MPI2_FW_HEADER_WHAT_SIGNATURE (0x29232840) + +#define MPI2_FW_HEADER_SIZE (0x100) + + +/* Extended Image Header */ +typedef struct _MPI2_EXT_IMAGE_HEADER + +{ + U8 ImageType; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U16 Reserved2; /* 0x02 */ + U32 Checksum; /* 0x04 */ + U32 ImageSize; /* 0x08 */ + U32 NextImageHeaderOffset; /* 0x0C */ + U32 PackageVersion; /* 0x10 */ + U32 Reserved3; /* 0x14 */ + U32 Reserved4; /* 0x18 */ + U32 Reserved5; /* 0x1C */ + U8 IdentifyString[32]; /* 0x20 */ +} MPI2_EXT_IMAGE_HEADER, MPI2_POINTER PTR_MPI2_EXT_IMAGE_HEADER, + Mpi2ExtImageHeader_t, MPI2_POINTER pMpi2ExtImageHeader_t; + +/* useful offsets */ +#define MPI2_EXT_IMAGE_IMAGETYPE_OFFSET (0x00) +#define MPI2_EXT_IMAGE_IMAGESIZE_OFFSET (0x08) +#define MPI2_EXT_IMAGE_NEXTIMAGE_OFFSET (0x0C) + +#define MPI2_EXT_IMAGE_HEADER_SIZE (0x40) + +/* defines for the ImageType field */ +#define MPI2_EXT_IMAGE_TYPE_UNSPECIFIED (0x00) +#define MPI2_EXT_IMAGE_TYPE_FW (0x01) +#define MPI2_EXT_IMAGE_TYPE_NVDATA (0x03) +#define MPI2_EXT_IMAGE_TYPE_BOOTLOADER (0x04) +#define MPI2_EXT_IMAGE_TYPE_INITIALIZATION (0x05) +#define MPI2_EXT_IMAGE_TYPE_FLASH_LAYOUT (0x06) +#define MPI2_EXT_IMAGE_TYPE_SUPPORTED_DEVICES (0x07) +#define MPI2_EXT_IMAGE_TYPE_MEGARAID (0x08) + +#define MPI2_EXT_IMAGE_TYPE_MAX (MPI2_EXT_IMAGE_TYPE_MEGARAID) + + + +/* FLASH Layout Extended Image Data */ + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check RegionsPerLayout at runtime. + */ +#ifndef MPI2_FLASH_NUMBER_OF_REGIONS +#define MPI2_FLASH_NUMBER_OF_REGIONS (1) +#endif + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check NumberOfLayouts at runtime. + */ +#ifndef MPI2_FLASH_NUMBER_OF_LAYOUTS +#define MPI2_FLASH_NUMBER_OF_LAYOUTS (1) +#endif + +typedef struct _MPI2_FLASH_REGION +{ + U8 RegionType; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U16 Reserved2; /* 0x02 */ + U32 RegionOffset; /* 0x04 */ + U32 RegionSize; /* 0x08 */ + U32 Reserved3; /* 0x0C */ +} MPI2_FLASH_REGION, MPI2_POINTER PTR_MPI2_FLASH_REGION, + Mpi2FlashRegion_t, MPI2_POINTER pMpi2FlashRegion_t; + +typedef struct _MPI2_FLASH_LAYOUT +{ + U32 FlashSize; /* 0x00 */ + U32 Reserved1; /* 0x04 */ + U32 Reserved2; /* 0x08 */ + U32 Reserved3; /* 0x0C */ + MPI2_FLASH_REGION Region[MPI2_FLASH_NUMBER_OF_REGIONS];/* 0x10 */ +} MPI2_FLASH_LAYOUT, MPI2_POINTER PTR_MPI2_FLASH_LAYOUT, + Mpi2FlashLayout_t, MPI2_POINTER pMpi2FlashLayout_t; + +typedef struct _MPI2_FLASH_LAYOUT_DATA +{ + U8 ImageRevision; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 SizeOfRegion; /* 0x02 */ + U8 Reserved2; /* 0x03 */ + U16 NumberOfLayouts; /* 0x04 */ + U16 RegionsPerLayout; /* 0x06 */ + U16 MinimumSectorAlignment; /* 0x08 */ + U16 Reserved3; /* 0x0A */ + U32 Reserved4; /* 0x0C */ + MPI2_FLASH_LAYOUT Layout[MPI2_FLASH_NUMBER_OF_LAYOUTS];/* 0x10 */ +} MPI2_FLASH_LAYOUT_DATA, MPI2_POINTER PTR_MPI2_FLASH_LAYOUT_DATA, + Mpi2FlashLayoutData_t, MPI2_POINTER pMpi2FlashLayoutData_t; + +/* defines for the RegionType field */ +#define MPI2_FLASH_REGION_UNUSED (0x00) +#define MPI2_FLASH_REGION_FIRMWARE (0x01) +#define MPI2_FLASH_REGION_BIOS (0x02) +#define MPI2_FLASH_REGION_NVDATA (0x03) +#define MPI2_FLASH_REGION_FIRMWARE_BACKUP (0x05) +#define MPI2_FLASH_REGION_MFG_INFORMATION (0x06) +#define MPI2_FLASH_REGION_CONFIG_1 (0x07) +#define MPI2_FLASH_REGION_CONFIG_2 (0x08) +#define MPI2_FLASH_REGION_MEGARAID (0x09) +#define MPI2_FLASH_REGION_INIT (0x0A) + +/* ImageRevision */ +#define MPI2_FLASH_LAYOUT_IMAGE_REVISION (0x00) + + + +/* Supported Devices Extended Image Data */ + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check NumberOfDevices at runtime. + */ +#ifndef MPI2_SUPPORTED_DEVICES_IMAGE_NUM_DEVICES +#define MPI2_SUPPORTED_DEVICES_IMAGE_NUM_DEVICES (1) +#endif + +typedef struct _MPI2_SUPPORTED_DEVICE +{ + U16 DeviceID; /* 0x00 */ + U16 VendorID; /* 0x02 */ + U16 DeviceIDMask; /* 0x04 */ + U16 Reserved1; /* 0x06 */ + U8 LowPCIRev; /* 0x08 */ + U8 HighPCIRev; /* 0x09 */ + U16 Reserved2; /* 0x0A */ + U32 Reserved3; /* 0x0C */ +} MPI2_SUPPORTED_DEVICE, MPI2_POINTER PTR_MPI2_SUPPORTED_DEVICE, + Mpi2SupportedDevice_t, MPI2_POINTER pMpi2SupportedDevice_t; + +typedef struct _MPI2_SUPPORTED_DEVICES_DATA +{ + U8 ImageRevision; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 NumberOfDevices; /* 0x02 */ + U8 Reserved2; /* 0x03 */ + U32 Reserved3; /* 0x04 */ + MPI2_SUPPORTED_DEVICE SupportedDevice[MPI2_SUPPORTED_DEVICES_IMAGE_NUM_DEVICES]; /* 0x08 */ +} MPI2_SUPPORTED_DEVICES_DATA, MPI2_POINTER PTR_MPI2_SUPPORTED_DEVICES_DATA, + Mpi2SupportedDevicesData_t, MPI2_POINTER pMpi2SupportedDevicesData_t; + +/* ImageRevision */ +#define MPI2_SUPPORTED_DEVICES_IMAGE_REVISION (0x00) + + +/* Init Extended Image Data */ + +typedef struct _MPI2_INIT_IMAGE_FOOTER + +{ + U32 BootFlags; /* 0x00 */ + U32 ImageSize; /* 0x04 */ + U32 Signature0; /* 0x08 */ + U32 Signature1; /* 0x0C */ + U32 Signature2; /* 0x10 */ + U32 ResetVector; /* 0x14 */ +} MPI2_INIT_IMAGE_FOOTER, MPI2_POINTER PTR_MPI2_INIT_IMAGE_FOOTER, + Mpi2InitImageFooter_t, MPI2_POINTER pMpi2InitImageFooter_t; + +/* defines for the BootFlags field */ +#define MPI2_INIT_IMAGE_BOOTFLAGS_OFFSET (0x00) + +/* defines for the ImageSize field */ +#define MPI2_INIT_IMAGE_IMAGESIZE_OFFSET (0x04) + +/* defines for the Signature0 field */ +#define MPI2_INIT_IMAGE_SIGNATURE0_OFFSET (0x08) +#define MPI2_INIT_IMAGE_SIGNATURE0 (0x5AA55AEA) + +/* defines for the Signature1 field */ +#define MPI2_INIT_IMAGE_SIGNATURE1_OFFSET (0x0C) +#define MPI2_INIT_IMAGE_SIGNATURE1 (0xA55AEAA5) + +/* defines for the Signature2 field */ +#define MPI2_INIT_IMAGE_SIGNATURE2_OFFSET (0x10) +#define MPI2_INIT_IMAGE_SIGNATURE2 (0x5AEAA55A) + +/* Signature fields as individual bytes */ +#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_0 (0xEA) +#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_1 (0x5A) +#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_2 (0xA5) +#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_3 (0x5A) + +#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_4 (0xA5) +#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_5 (0xEA) +#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_6 (0x5A) +#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_7 (0xA5) + +#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_8 (0x5A) +#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_9 (0xA5) +#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_A (0xEA) +#define MPI2_INIT_IMAGE_SIGNATURE_BYTE_B (0x5A) + +/* defines for the ResetVector field */ +#define MPI2_INIT_IMAGE_RESETVECTOR_OFFSET (0x14) + + +#endif + diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_raid.h b/drivers/scsi/mpt2sas/mpi/mpi2_raid.h new file mode 100644 index 000000000000..7134816d9046 --- /dev/null +++ b/drivers/scsi/mpt2sas/mpi/mpi2_raid.h @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2000-2008 LSI Corporation. + * + * + * Name: mpi2_raid.h + * Title: MPI Integrated RAID messages and structures + * Creation Date: April 26, 2007 + * + * mpi2_raid.h Version: 02.00.03 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * 08-31-07 02.00.01 Modifications to RAID Action request and reply, + * including the Actions and ActionData. + * 02-29-08 02.00.02 Added MPI2_RAID_ACTION_ADATA_DISABL_FULL_REBUILD. + * 05-21-08 02.00.03 Added MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS so that + * the PhysDisk array in MPI2_RAID_VOLUME_CREATION_STRUCT + * can be sized by the build environment. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI2_RAID_H +#define MPI2_RAID_H + +/***************************************************************************** +* +* Integrated RAID Messages +* +*****************************************************************************/ + +/**************************************************************************** +* RAID Action messages +****************************************************************************/ + +/* ActionDataWord defines for use with MPI2_RAID_ACTION_DELETE_VOLUME action */ +#define MPI2_RAID_ACTION_ADATA_KEEP_LBA0 (0x00000000) +#define MPI2_RAID_ACTION_ADATA_ZERO_LBA0 (0x00000001) + +/* use MPI2_RAIDVOL0_SETTING_ defines from mpi2_cnfg.h for MPI2_RAID_ACTION_CHANGE_VOL_WRITE_CACHE action */ + +/* ActionDataWord defines for use with MPI2_RAID_ACTION_DISABLE_ALL_VOLUMES action */ +#define MPI2_RAID_ACTION_ADATA_DISABL_FULL_REBUILD (0x00000001) + +/* ActionDataWord for MPI2_RAID_ACTION_SET_RAID_FUNCTION_RATE Action */ +typedef struct _MPI2_RAID_ACTION_RATE_DATA +{ + U8 RateToChange; /* 0x00 */ + U8 RateOrMode; /* 0x01 */ + U16 DataScrubDuration; /* 0x02 */ +} MPI2_RAID_ACTION_RATE_DATA, MPI2_POINTER PTR_MPI2_RAID_ACTION_RATE_DATA, + Mpi2RaidActionRateData_t, MPI2_POINTER pMpi2RaidActionRateData_t; + +#define MPI2_RAID_ACTION_SET_RATE_RESYNC (0x00) +#define MPI2_RAID_ACTION_SET_RATE_DATA_SCRUB (0x01) +#define MPI2_RAID_ACTION_SET_RATE_POWERSAVE_MODE (0x02) + +/* ActionDataWord for MPI2_RAID_ACTION_START_RAID_FUNCTION Action */ +typedef struct _MPI2_RAID_ACTION_START_RAID_FUNCTION +{ + U8 RAIDFunction; /* 0x00 */ + U8 Flags; /* 0x01 */ + U16 Reserved1; /* 0x02 */ +} MPI2_RAID_ACTION_START_RAID_FUNCTION, + MPI2_POINTER PTR_MPI2_RAID_ACTION_START_RAID_FUNCTION, + Mpi2RaidActionStartRaidFunction_t, + MPI2_POINTER pMpi2RaidActionStartRaidFunction_t; + +/* defines for the RAIDFunction field */ +#define MPI2_RAID_ACTION_START_BACKGROUND_INIT (0x00) +#define MPI2_RAID_ACTION_START_ONLINE_CAP_EXPANSION (0x01) +#define MPI2_RAID_ACTION_START_CONSISTENCY_CHECK (0x02) + +/* defines for the Flags field */ +#define MPI2_RAID_ACTION_START_NEW (0x00) +#define MPI2_RAID_ACTION_START_RESUME (0x01) + +/* ActionDataWord for MPI2_RAID_ACTION_STOP_RAID_FUNCTION Action */ +typedef struct _MPI2_RAID_ACTION_STOP_RAID_FUNCTION +{ + U8 RAIDFunction; /* 0x00 */ + U8 Flags; /* 0x01 */ + U16 Reserved1; /* 0x02 */ +} MPI2_RAID_ACTION_STOP_RAID_FUNCTION, + MPI2_POINTER PTR_MPI2_RAID_ACTION_STOP_RAID_FUNCTION, + Mpi2RaidActionStopRaidFunction_t, + MPI2_POINTER pMpi2RaidActionStopRaidFunction_t; + +/* defines for the RAIDFunction field */ +#define MPI2_RAID_ACTION_STOP_BACKGROUND_INIT (0x00) +#define MPI2_RAID_ACTION_STOP_ONLINE_CAP_EXPANSION (0x01) +#define MPI2_RAID_ACTION_STOP_CONSISTENCY_CHECK (0x02) + +/* defines for the Flags field */ +#define MPI2_RAID_ACTION_STOP_ABORT (0x00) +#define MPI2_RAID_ACTION_STOP_PAUSE (0x01) + +/* ActionDataWord for MPI2_RAID_ACTION_CREATE_HOT_SPARE Action */ +typedef struct _MPI2_RAID_ACTION_HOT_SPARE +{ + U8 HotSparePool; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U16 DevHandle; /* 0x02 */ +} MPI2_RAID_ACTION_HOT_SPARE, MPI2_POINTER PTR_MPI2_RAID_ACTION_HOT_SPARE, + Mpi2RaidActionHotSpare_t, MPI2_POINTER pMpi2RaidActionHotSpare_t; + +/* ActionDataWord for MPI2_RAID_ACTION_DEVICE_FW_UPDATE_MODE Action */ +typedef struct _MPI2_RAID_ACTION_FW_UPDATE_MODE +{ + U8 Flags; /* 0x00 */ + U8 DeviceFirmwareUpdateModeTimeout; /* 0x01 */ + U16 Reserved1; /* 0x02 */ +} MPI2_RAID_ACTION_FW_UPDATE_MODE, + MPI2_POINTER PTR_MPI2_RAID_ACTION_FW_UPDATE_MODE, + Mpi2RaidActionFwUpdateMode_t, MPI2_POINTER pMpi2RaidActionFwUpdateMode_t; + +/* ActionDataWord defines for use with MPI2_RAID_ACTION_DEVICE_FW_UPDATE_MODE action */ +#define MPI2_RAID_ACTION_ADATA_DISABLE_FW_UPDATE (0x00) +#define MPI2_RAID_ACTION_ADATA_ENABLE_FW_UPDATE (0x01) + +typedef union _MPI2_RAID_ACTION_DATA +{ + U32 Word; + MPI2_RAID_ACTION_RATE_DATA Rates; + MPI2_RAID_ACTION_START_RAID_FUNCTION StartRaidFunction; + MPI2_RAID_ACTION_STOP_RAID_FUNCTION StopRaidFunction; + MPI2_RAID_ACTION_HOT_SPARE HotSpare; + MPI2_RAID_ACTION_FW_UPDATE_MODE FwUpdateMode; +} MPI2_RAID_ACTION_DATA, MPI2_POINTER PTR_MPI2_RAID_ACTION_DATA, + Mpi2RaidActionData_t, MPI2_POINTER pMpi2RaidActionData_t; + + +/* RAID Action Request Message */ +typedef struct _MPI2_RAID_ACTION_REQUEST +{ + U8 Action; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 VolDevHandle; /* 0x04 */ + U8 PhysDiskNum; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved2; /* 0x0A */ + U32 Reserved3; /* 0x0C */ + MPI2_RAID_ACTION_DATA ActionDataWord; /* 0x10 */ + MPI2_SGE_SIMPLE_UNION ActionDataSGE; /* 0x14 */ +} MPI2_RAID_ACTION_REQUEST, MPI2_POINTER PTR_MPI2_RAID_ACTION_REQUEST, + Mpi2RaidActionRequest_t, MPI2_POINTER pMpi2RaidActionRequest_t; + +/* RAID Action request Action values */ + +#define MPI2_RAID_ACTION_INDICATOR_STRUCT (0x01) +#define MPI2_RAID_ACTION_CREATE_VOLUME (0x02) +#define MPI2_RAID_ACTION_DELETE_VOLUME (0x03) +#define MPI2_RAID_ACTION_DISABLE_ALL_VOLUMES (0x04) +#define MPI2_RAID_ACTION_ENABLE_ALL_VOLUMES (0x05) +#define MPI2_RAID_ACTION_PHYSDISK_OFFLINE (0x0A) +#define MPI2_RAID_ACTION_PHYSDISK_ONLINE (0x0B) +#define MPI2_RAID_ACTION_FAIL_PHYSDISK (0x0F) +#define MPI2_RAID_ACTION_ACTIVATE_VOLUME (0x11) +#define MPI2_RAID_ACTION_DEVICE_FW_UPDATE_MODE (0x15) +#define MPI2_RAID_ACTION_CHANGE_VOL_WRITE_CACHE (0x17) +#define MPI2_RAID_ACTION_SET_VOLUME_NAME (0x18) +#define MPI2_RAID_ACTION_SET_RAID_FUNCTION_RATE (0x19) +#define MPI2_RAID_ACTION_ENABLE_FAILED_VOLUME (0x1C) +#define MPI2_RAID_ACTION_CREATE_HOT_SPARE (0x1D) +#define MPI2_RAID_ACTION_DELETE_HOT_SPARE (0x1E) +#define MPI2_RAID_ACTION_SYSTEM_SHUTDOWN_INITIATED (0x20) +#define MPI2_RAID_ACTION_START_RAID_FUNCTION (0x21) +#define MPI2_RAID_ACTION_STOP_RAID_FUNCTION (0x22) + + +/* RAID Volume Creation Structure */ + +/* + * The following define can be customized for the targeted product. + */ +#ifndef MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS +#define MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS (1) +#endif + +typedef struct _MPI2_RAID_VOLUME_PHYSDISK +{ + U8 RAIDSetNum; /* 0x00 */ + U8 PhysDiskMap; /* 0x01 */ + U16 PhysDiskDevHandle; /* 0x02 */ +} MPI2_RAID_VOLUME_PHYSDISK, MPI2_POINTER PTR_MPI2_RAID_VOLUME_PHYSDISK, + Mpi2RaidVolumePhysDisk_t, MPI2_POINTER pMpi2RaidVolumePhysDisk_t; + +/* defines for the PhysDiskMap field */ +#define MPI2_RAIDACTION_PHYSDISK_PRIMARY (0x01) +#define MPI2_RAIDACTION_PHYSDISK_SECONDARY (0x02) + +typedef struct _MPI2_RAID_VOLUME_CREATION_STRUCT +{ + U8 NumPhysDisks; /* 0x00 */ + U8 VolumeType; /* 0x01 */ + U16 Reserved1; /* 0x02 */ + U32 VolumeCreationFlags; /* 0x04 */ + U32 VolumeSettings; /* 0x08 */ + U8 Reserved2; /* 0x0C */ + U8 ResyncRate; /* 0x0D */ + U16 DataScrubDuration; /* 0x0E */ + U64 VolumeMaxLBA; /* 0x10 */ + U32 StripeSize; /* 0x18 */ + U8 Name[16]; /* 0x1C */ + MPI2_RAID_VOLUME_PHYSDISK PhysDisk[MPI2_RAID_VOL_CREATION_NUM_PHYSDISKS];/* 0x2C */ +} MPI2_RAID_VOLUME_CREATION_STRUCT, + MPI2_POINTER PTR_MPI2_RAID_VOLUME_CREATION_STRUCT, + Mpi2RaidVolumeCreationStruct_t, MPI2_POINTER pMpi2RaidVolumeCreationStruct_t; + +/* use MPI2_RAID_VOL_TYPE_ defines from mpi2_cnfg.h for VolumeType */ + +/* defines for the VolumeCreationFlags field */ +#define MPI2_RAID_VOL_CREATION_USE_DEFAULT_SETTINGS (0x80) +#define MPI2_RAID_VOL_CREATION_BACKGROUND_INIT (0x04) +#define MPI2_RAID_VOL_CREATION_LOW_LEVEL_INIT (0x02) +#define MPI2_RAID_VOL_CREATION_MIGRATE_DATA (0x01) + + +/* RAID Online Capacity Expansion Structure */ + +typedef struct _MPI2_RAID_ONLINE_CAPACITY_EXPANSION +{ + U32 Flags; /* 0x00 */ + U16 DevHandle0; /* 0x04 */ + U16 Reserved1; /* 0x06 */ + U16 DevHandle1; /* 0x08 */ + U16 Reserved2; /* 0x0A */ +} MPI2_RAID_ONLINE_CAPACITY_EXPANSION, + MPI2_POINTER PTR_MPI2_RAID_ONLINE_CAPACITY_EXPANSION, + Mpi2RaidOnlineCapacityExpansion_t, + MPI2_POINTER pMpi2RaidOnlineCapacityExpansion_t; + + +/* RAID Volume Indicator Structure */ + +typedef struct _MPI2_RAID_VOL_INDICATOR +{ + U64 TotalBlocks; /* 0x00 */ + U64 BlocksRemaining; /* 0x08 */ + U32 Flags; /* 0x10 */ +} MPI2_RAID_VOL_INDICATOR, MPI2_POINTER PTR_MPI2_RAID_VOL_INDICATOR, + Mpi2RaidVolIndicator_t, MPI2_POINTER pMpi2RaidVolIndicator_t; + +/* defines for RAID Volume Indicator Flags field */ +#define MPI2_RAID_VOL_FLAGS_OP_MASK (0x0000000F) +#define MPI2_RAID_VOL_FLAGS_OP_BACKGROUND_INIT (0x00000000) +#define MPI2_RAID_VOL_FLAGS_OP_ONLINE_CAP_EXPANSION (0x00000001) +#define MPI2_RAID_VOL_FLAGS_OP_CONSISTENCY_CHECK (0x00000002) +#define MPI2_RAID_VOL_FLAGS_OP_RESYNC (0x00000003) + + +/* RAID Action Reply ActionData union */ +typedef union _MPI2_RAID_ACTION_REPLY_DATA +{ + U32 Word[5]; + MPI2_RAID_VOL_INDICATOR RaidVolumeIndicator; + U16 VolDevHandle; + U8 VolumeState; + U8 PhysDiskNum; +} MPI2_RAID_ACTION_REPLY_DATA, MPI2_POINTER PTR_MPI2_RAID_ACTION_REPLY_DATA, + Mpi2RaidActionReplyData_t, MPI2_POINTER pMpi2RaidActionReplyData_t; + +/* use MPI2_RAIDVOL0_SETTING_ defines from mpi2_cnfg.h for MPI2_RAID_ACTION_CHANGE_VOL_WRITE_CACHE action */ + + +/* RAID Action Reply Message */ +typedef struct _MPI2_RAID_ACTION_REPLY +{ + U8 Action; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 VolDevHandle; /* 0x04 */ + U8 PhysDiskNum; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved2; /* 0x0A */ + U16 Reserved3; /* 0x0C */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ + MPI2_RAID_ACTION_REPLY_DATA ActionData; /* 0x14 */ +} MPI2_RAID_ACTION_REPLY, MPI2_POINTER PTR_MPI2_RAID_ACTION_REPLY, + Mpi2RaidActionReply_t, MPI2_POINTER pMpi2RaidActionReply_t; + + +#endif + diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_sas.h b/drivers/scsi/mpt2sas/mpi/mpi2_sas.h new file mode 100644 index 000000000000..8a42b136cf53 --- /dev/null +++ b/drivers/scsi/mpt2sas/mpi/mpi2_sas.h @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2000-2007 LSI Corporation. + * + * + * Name: mpi2_sas.h + * Title: MPI Serial Attached SCSI structures and definitions + * Creation Date: February 9, 2007 + * + * mpi2.h Version: 02.00.02 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * 06-26-07 02.00.01 Added Clear All Persistent Operation to SAS IO Unit + * Control Request. + * 10-02-08 02.00.02 Added Set IOC Parameter Operation to SAS IO Unit Control + * Request. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI2_SAS_H +#define MPI2_SAS_H + +/* + * Values for SASStatus. + */ +#define MPI2_SASSTATUS_SUCCESS (0x00) +#define MPI2_SASSTATUS_UNKNOWN_ERROR (0x01) +#define MPI2_SASSTATUS_INVALID_FRAME (0x02) +#define MPI2_SASSTATUS_UTC_BAD_DEST (0x03) +#define MPI2_SASSTATUS_UTC_BREAK_RECEIVED (0x04) +#define MPI2_SASSTATUS_UTC_CONNECT_RATE_NOT_SUPPORTED (0x05) +#define MPI2_SASSTATUS_UTC_PORT_LAYER_REQUEST (0x06) +#define MPI2_SASSTATUS_UTC_PROTOCOL_NOT_SUPPORTED (0x07) +#define MPI2_SASSTATUS_UTC_STP_RESOURCES_BUSY (0x08) +#define MPI2_SASSTATUS_UTC_WRONG_DESTINATION (0x09) +#define MPI2_SASSTATUS_SHORT_INFORMATION_UNIT (0x0A) +#define MPI2_SASSTATUS_LONG_INFORMATION_UNIT (0x0B) +#define MPI2_SASSTATUS_XFER_RDY_INCORRECT_WRITE_DATA (0x0C) +#define MPI2_SASSTATUS_XFER_RDY_REQUEST_OFFSET_ERROR (0x0D) +#define MPI2_SASSTATUS_XFER_RDY_NOT_EXPECTED (0x0E) +#define MPI2_SASSTATUS_DATA_INCORRECT_DATA_LENGTH (0x0F) +#define MPI2_SASSTATUS_DATA_TOO_MUCH_READ_DATA (0x10) +#define MPI2_SASSTATUS_DATA_OFFSET_ERROR (0x11) +#define MPI2_SASSTATUS_SDSF_NAK_RECEIVED (0x12) +#define MPI2_SASSTATUS_SDSF_CONNECTION_FAILED (0x13) +#define MPI2_SASSTATUS_INITIATOR_RESPONSE_TIMEOUT (0x14) + + +/* + * Values for the SAS DeviceInfo field used in SAS Device Status Change Event + * data and SAS Configuration pages. + */ +#define MPI2_SAS_DEVICE_INFO_SEP (0x00004000) +#define MPI2_SAS_DEVICE_INFO_ATAPI_DEVICE (0x00002000) +#define MPI2_SAS_DEVICE_INFO_LSI_DEVICE (0x00001000) +#define MPI2_SAS_DEVICE_INFO_DIRECT_ATTACH (0x00000800) +#define MPI2_SAS_DEVICE_INFO_SSP_TARGET (0x00000400) +#define MPI2_SAS_DEVICE_INFO_STP_TARGET (0x00000200) +#define MPI2_SAS_DEVICE_INFO_SMP_TARGET (0x00000100) +#define MPI2_SAS_DEVICE_INFO_SATA_DEVICE (0x00000080) +#define MPI2_SAS_DEVICE_INFO_SSP_INITIATOR (0x00000040) +#define MPI2_SAS_DEVICE_INFO_STP_INITIATOR (0x00000020) +#define MPI2_SAS_DEVICE_INFO_SMP_INITIATOR (0x00000010) +#define MPI2_SAS_DEVICE_INFO_SATA_HOST (0x00000008) + +#define MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE (0x00000007) +#define MPI2_SAS_DEVICE_INFO_NO_DEVICE (0x00000000) +#define MPI2_SAS_DEVICE_INFO_END_DEVICE (0x00000001) +#define MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER (0x00000002) +#define MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER (0x00000003) + + +/***************************************************************************** +* +* SAS Messages +* +*****************************************************************************/ + +/**************************************************************************** +* SMP Passthrough messages +****************************************************************************/ + +/* SMP Passthrough Request Message */ +typedef struct _MPI2_SMP_PASSTHROUGH_REQUEST +{ + U8 PassthroughFlags; /* 0x00 */ + U8 PhysicalPort; /* 0x01 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 RequestDataLength; /* 0x04 */ + U8 SGLFlags; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved1; /* 0x0A */ + U32 Reserved2; /* 0x0C */ + U64 SASAddress; /* 0x10 */ + U32 Reserved3; /* 0x18 */ + U32 Reserved4; /* 0x1C */ + MPI2_SIMPLE_SGE_UNION SGL; /* 0x20 */ +} MPI2_SMP_PASSTHROUGH_REQUEST, MPI2_POINTER PTR_MPI2_SMP_PASSTHROUGH_REQUEST, + Mpi2SmpPassthroughRequest_t, MPI2_POINTER pMpi2SmpPassthroughRequest_t; + +/* values for PassthroughFlags field */ +#define MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE (0x80) + +/* values for SGLFlags field are in the SGL section of mpi2.h */ + + +/* SMP Passthrough Reply Message */ +typedef struct _MPI2_SMP_PASSTHROUGH_REPLY +{ + U8 PassthroughFlags; /* 0x00 */ + U8 PhysicalPort; /* 0x01 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 ResponseDataLength; /* 0x04 */ + U8 SGLFlags; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved1; /* 0x0A */ + U8 Reserved2; /* 0x0C */ + U8 SASStatus; /* 0x0D */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ + U32 Reserved3; /* 0x14 */ + U8 ResponseData[4]; /* 0x18 */ +} MPI2_SMP_PASSTHROUGH_REPLY, MPI2_POINTER PTR_MPI2_SMP_PASSTHROUGH_REPLY, + Mpi2SmpPassthroughReply_t, MPI2_POINTER pMpi2SmpPassthroughReply_t; + +/* values for PassthroughFlags field */ +#define MPI2_SMP_PT_REPLY_PT_FLAGS_IMMEDIATE (0x80) + +/* values for SASStatus field are at the top of this file */ + + +/**************************************************************************** +* SATA Passthrough messages +****************************************************************************/ + +/* SATA Passthrough Request Message */ +typedef struct _MPI2_SATA_PASSTHROUGH_REQUEST +{ + U16 DevHandle; /* 0x00 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 PassthroughFlags; /* 0x04 */ + U8 SGLFlags; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved1; /* 0x0A */ + U32 Reserved2; /* 0x0C */ + U32 Reserved3; /* 0x10 */ + U32 Reserved4; /* 0x14 */ + U32 DataLength; /* 0x18 */ + U8 CommandFIS[20]; /* 0x1C */ + MPI2_SIMPLE_SGE_UNION SGL; /* 0x20 */ +} MPI2_SATA_PASSTHROUGH_REQUEST, MPI2_POINTER PTR_MPI2_SATA_PASSTHROUGH_REQUEST, + Mpi2SataPassthroughRequest_t, MPI2_POINTER pMpi2SataPassthroughRequest_t; + +/* values for PassthroughFlags field */ +#define MPI2_SATA_PT_REQ_PT_FLAGS_EXECUTE_DIAG (0x0100) +#define MPI2_SATA_PT_REQ_PT_FLAGS_DMA (0x0020) +#define MPI2_SATA_PT_REQ_PT_FLAGS_PIO (0x0010) +#define MPI2_SATA_PT_REQ_PT_FLAGS_UNSPECIFIED_VU (0x0004) +#define MPI2_SATA_PT_REQ_PT_FLAGS_WRITE (0x0002) +#define MPI2_SATA_PT_REQ_PT_FLAGS_READ (0x0001) + +/* values for SGLFlags field are in the SGL section of mpi2.h */ + + +/* SATA Passthrough Reply Message */ +typedef struct _MPI2_SATA_PASSTHROUGH_REPLY +{ + U16 DevHandle; /* 0x00 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 PassthroughFlags; /* 0x04 */ + U8 SGLFlags; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved1; /* 0x0A */ + U8 Reserved2; /* 0x0C */ + U8 SASStatus; /* 0x0D */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ + U8 StatusFIS[20]; /* 0x14 */ + U32 StatusControlRegisters; /* 0x28 */ + U32 TransferCount; /* 0x2C */ +} MPI2_SATA_PASSTHROUGH_REPLY, MPI2_POINTER PTR_MPI2_SATA_PASSTHROUGH_REPLY, + Mpi2SataPassthroughReply_t, MPI2_POINTER pMpi2SataPassthroughReply_t; + +/* values for SASStatus field are at the top of this file */ + + +/**************************************************************************** +* SAS IO Unit Control messages +****************************************************************************/ + +/* SAS IO Unit Control Request Message */ +typedef struct _MPI2_SAS_IOUNIT_CONTROL_REQUEST +{ + U8 Operation; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 DevHandle; /* 0x04 */ + U8 IOCParameter; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved3; /* 0x0A */ + U16 Reserved4; /* 0x0C */ + U8 PhyNum; /* 0x0E */ + U8 PrimFlags; /* 0x0F */ + U32 Primitive; /* 0x10 */ + U8 LookupMethod; /* 0x14 */ + U8 Reserved5; /* 0x15 */ + U16 SlotNumber; /* 0x16 */ + U64 LookupAddress; /* 0x18 */ + U32 IOCParameterValue; /* 0x20 */ + U32 Reserved7; /* 0x24 */ + U32 Reserved8; /* 0x28 */ +} MPI2_SAS_IOUNIT_CONTROL_REQUEST, + MPI2_POINTER PTR_MPI2_SAS_IOUNIT_CONTROL_REQUEST, + Mpi2SasIoUnitControlRequest_t, MPI2_POINTER pMpi2SasIoUnitControlRequest_t; + +/* values for the Operation field */ +#define MPI2_SAS_OP_CLEAR_ALL_PERSISTENT (0x02) +#define MPI2_SAS_OP_PHY_LINK_RESET (0x06) +#define MPI2_SAS_OP_PHY_HARD_RESET (0x07) +#define MPI2_SAS_OP_PHY_CLEAR_ERROR_LOG (0x08) +#define MPI2_SAS_OP_SEND_PRIMITIVE (0x0A) +#define MPI2_SAS_OP_FORCE_FULL_DISCOVERY (0x0B) +#define MPI2_SAS_OP_TRANSMIT_PORT_SELECT_SIGNAL (0x0C) +#define MPI2_SAS_OP_REMOVE_DEVICE (0x0D) +#define MPI2_SAS_OP_LOOKUP_MAPPING (0x0E) +#define MPI2_SAS_OP_SET_IOC_PARAMETER (0x0F) +#define MPI2_SAS_OP_PRODUCT_SPECIFIC_MIN (0x80) + +/* values for the PrimFlags field */ +#define MPI2_SAS_PRIMFLAGS_SINGLE (0x08) +#define MPI2_SAS_PRIMFLAGS_TRIPLE (0x02) +#define MPI2_SAS_PRIMFLAGS_REDUNDANT (0x01) + +/* values for the LookupMethod field */ +#define MPI2_SAS_LOOKUP_METHOD_SAS_ADDRESS (0x01) +#define MPI2_SAS_LOOKUP_METHOD_SAS_ENCLOSURE_SLOT (0x02) +#define MPI2_SAS_LOOKUP_METHOD_SAS_DEVICE_NAME (0x03) + + +/* SAS IO Unit Control Reply Message */ +typedef struct _MPI2_SAS_IOUNIT_CONTROL_REPLY +{ + U8 Operation; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 DevHandle; /* 0x04 */ + U8 IOCParameter; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved3; /* 0x0A */ + U16 Reserved4; /* 0x0C */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ +} MPI2_SAS_IOUNIT_CONTROL_REPLY, + MPI2_POINTER PTR_MPI2_SAS_IOUNIT_CONTROL_REPLY, + Mpi2SasIoUnitControlReply_t, MPI2_POINTER pMpi2SasIoUnitControlReply_t; + + +#endif + + diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_tool.h b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h new file mode 100644 index 000000000000..2ff4e936bd39 --- /dev/null +++ b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2000-2008 LSI Corporation. + * + * + * Name: mpi2_tool.h + * Title: MPI diagnostic tool structures and definitions + * Creation Date: March 26, 2007 + * + * mpi2_tool.h Version: 02.00.02 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * 12-18-07 02.00.01 Added Diagnostic Buffer Post and Diagnostic Release + * structures and defines. + * 02-29-08 02.00.02 Modified various names to make them 32-character unique. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI2_TOOL_H +#define MPI2_TOOL_H + +/***************************************************************************** +* +* Toolbox Messages +* +*****************************************************************************/ + +/* defines for the Tools */ +#define MPI2_TOOLBOX_CLEAN_TOOL (0x00) +#define MPI2_TOOLBOX_MEMORY_MOVE_TOOL (0x01) +#define MPI2_TOOLBOX_BEACON_TOOL (0x05) + +/**************************************************************************** +* Toolbox reply +****************************************************************************/ + +typedef struct _MPI2_TOOLBOX_REPLY +{ + U8 Tool; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U16 Reserved5; /* 0x0C */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ +} MPI2_TOOLBOX_REPLY, MPI2_POINTER PTR_MPI2_TOOLBOX_REPLY, + Mpi2ToolboxReply_t, MPI2_POINTER pMpi2ToolboxReply_t; + + +/**************************************************************************** +* Toolbox Clean Tool request +****************************************************************************/ + +typedef struct _MPI2_TOOLBOX_CLEAN_REQUEST +{ + U8 Tool; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U32 Flags; /* 0x0C */ + } MPI2_TOOLBOX_CLEAN_REQUEST, MPI2_POINTER PTR_MPI2_TOOLBOX_CLEAN_REQUEST, + Mpi2ToolboxCleanRequest_t, MPI2_POINTER pMpi2ToolboxCleanRequest_t; + +/* values for the Flags field */ +#define MPI2_TOOLBOX_CLEAN_BOOT_SERVICES (0x80000000) +#define MPI2_TOOLBOX_CLEAN_PERSIST_MANUFACT_PAGES (0x40000000) +#define MPI2_TOOLBOX_CLEAN_OTHER_PERSIST_PAGES (0x20000000) +#define MPI2_TOOLBOX_CLEAN_FW_CURRENT (0x10000000) +#define MPI2_TOOLBOX_CLEAN_FW_BACKUP (0x08000000) +#define MPI2_TOOLBOX_CLEAN_MEGARAID (0x02000000) +#define MPI2_TOOLBOX_CLEAN_INITIALIZATION (0x01000000) +#define MPI2_TOOLBOX_CLEAN_FLASH (0x00000004) +#define MPI2_TOOLBOX_CLEAN_SEEPROM (0x00000002) +#define MPI2_TOOLBOX_CLEAN_NVSRAM (0x00000001) + + +/**************************************************************************** +* Toolbox Memory Move request +****************************************************************************/ + +typedef struct _MPI2_TOOLBOX_MEM_MOVE_REQUEST +{ + U8 Tool; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + MPI2_SGE_SIMPLE_UNION SGL; /* 0x0C */ +} MPI2_TOOLBOX_MEM_MOVE_REQUEST, MPI2_POINTER PTR_MPI2_TOOLBOX_MEM_MOVE_REQUEST, + Mpi2ToolboxMemMoveRequest_t, MPI2_POINTER pMpi2ToolboxMemMoveRequest_t; + + +/**************************************************************************** +* Toolbox Beacon Tool request +****************************************************************************/ + +typedef struct _MPI2_TOOLBOX_BEACON_REQUEST +{ + U8 Tool; /* 0x00 */ + U8 Reserved1; /* 0x01 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U8 Reserved5; /* 0x0C */ + U8 PhysicalPort; /* 0x0D */ + U8 Reserved6; /* 0x0E */ + U8 Flags; /* 0x0F */ +} MPI2_TOOLBOX_BEACON_REQUEST, MPI2_POINTER PTR_MPI2_TOOLBOX_BEACON_REQUEST, + Mpi2ToolboxBeaconRequest_t, MPI2_POINTER pMpi2ToolboxBeaconRequest_t; + +/* values for the Flags field */ +#define MPI2_TOOLBOX_FLAGS_BEACONMODE_OFF (0x00) +#define MPI2_TOOLBOX_FLAGS_BEACONMODE_ON (0x01) + + +/***************************************************************************** +* +* Diagnostic Buffer Messages +* +*****************************************************************************/ + + +/**************************************************************************** +* Diagnostic Buffer Post request +****************************************************************************/ + +typedef struct _MPI2_DIAG_BUFFER_POST_REQUEST +{ + U8 Reserved1; /* 0x00 */ + U8 BufferType; /* 0x01 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U64 BufferAddress; /* 0x0C */ + U32 BufferLength; /* 0x14 */ + U32 Reserved5; /* 0x18 */ + U32 Reserved6; /* 0x1C */ + U32 Flags; /* 0x20 */ + U32 ProductSpecific[23]; /* 0x24 */ +} MPI2_DIAG_BUFFER_POST_REQUEST, MPI2_POINTER PTR_MPI2_DIAG_BUFFER_POST_REQUEST, + Mpi2DiagBufferPostRequest_t, MPI2_POINTER pMpi2DiagBufferPostRequest_t; + +/* values for the BufferType field */ +#define MPI2_DIAG_BUF_TYPE_TRACE (0x00) +#define MPI2_DIAG_BUF_TYPE_SNAPSHOT (0x01) +/* count of the number of buffer types */ +#define MPI2_DIAG_BUF_TYPE_COUNT (0x02) + + +/**************************************************************************** +* Diagnostic Buffer Post reply +****************************************************************************/ + +typedef struct _MPI2_DIAG_BUFFER_POST_REPLY +{ + U8 Reserved1; /* 0x00 */ + U8 BufferType; /* 0x01 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U16 Reserved5; /* 0x0C */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ + U32 TransferLength; /* 0x14 */ +} MPI2_DIAG_BUFFER_POST_REPLY, MPI2_POINTER PTR_MPI2_DIAG_BUFFER_POST_REPLY, + Mpi2DiagBufferPostReply_t, MPI2_POINTER pMpi2DiagBufferPostReply_t; + + +/**************************************************************************** +* Diagnostic Release request +****************************************************************************/ + +typedef struct _MPI2_DIAG_RELEASE_REQUEST +{ + U8 Reserved1; /* 0x00 */ + U8 BufferType; /* 0x01 */ + U8 ChainOffset; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ +} MPI2_DIAG_RELEASE_REQUEST, MPI2_POINTER PTR_MPI2_DIAG_RELEASE_REQUEST, + Mpi2DiagReleaseRequest_t, MPI2_POINTER pMpi2DiagReleaseRequest_t; + + +/**************************************************************************** +* Diagnostic Buffer Post reply +****************************************************************************/ + +typedef struct _MPI2_DIAG_RELEASE_REPLY +{ + U8 Reserved1; /* 0x00 */ + U8 BufferType; /* 0x01 */ + U8 MsgLength; /* 0x02 */ + U8 Function; /* 0x03 */ + U16 Reserved2; /* 0x04 */ + U8 Reserved3; /* 0x06 */ + U8 MsgFlags; /* 0x07 */ + U8 VP_ID; /* 0x08 */ + U8 VF_ID; /* 0x09 */ + U16 Reserved4; /* 0x0A */ + U16 Reserved5; /* 0x0C */ + U16 IOCStatus; /* 0x0E */ + U32 IOCLogInfo; /* 0x10 */ +} MPI2_DIAG_RELEASE_REPLY, MPI2_POINTER PTR_MPI2_DIAG_RELEASE_REPLY, + Mpi2DiagReleaseReply_t, MPI2_POINTER pMpi2DiagReleaseReply_t; + + +#endif + diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_type.h b/drivers/scsi/mpt2sas/mpi/mpi2_type.h new file mode 100644 index 000000000000..cfde017bf16e --- /dev/null +++ b/drivers/scsi/mpt2sas/mpi/mpi2_type.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2000-2007 LSI Corporation. + * + * + * Name: mpi2_type.h + * Title: MPI basic type definitions + * Creation Date: August 16, 2006 + * + * mpi2_type.h Version: 02.00.00 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 04-30-07 02.00.00 Corresponds to Fusion-MPT MPI Specification Rev A. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI2_TYPE_H +#define MPI2_TYPE_H + + +/******************************************************************************* + * Define MPI2_POINTER if it hasn't already been defined. By default + * MPI2_POINTER is defined to be a near pointer. MPI2_POINTER can be defined as + * a far pointer by defining MPI2_POINTER as "far *" before this header file is + * included. + */ +#ifndef MPI2_POINTER +#define MPI2_POINTER * +#endif + +/* the basic types may have already been included by mpi_type.h */ +#ifndef MPI_TYPE_H +/***************************************************************************** +* +* Basic Types +* +*****************************************************************************/ + +typedef u8 U8; +typedef __le16 U16; +typedef __le32 U32; +typedef __le64 U64 __attribute__((aligned(4))); + +/***************************************************************************** +* +* Pointer Types +* +*****************************************************************************/ + +typedef U8 *PU8; +typedef U16 *PU16; +typedef U32 *PU32; +typedef U64 *PU64; + +#endif + +#endif + diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c new file mode 100644 index 000000000000..52427a8324f5 --- /dev/null +++ b/drivers/scsi/mpt2sas/mpt2sas_base.c @@ -0,0 +1,3435 @@ +/* + * This is the Fusion MPT base driver providing common API layer interface + * for access to MPT (Message Passing Technology) firmware. + * + * This code is based on drivers/scsi/mpt2sas/mpt2_base.c + * Copyright (C) 2007-2008 LSI Corporation + * (mailto:DL-MPTFusionLinux@lsi.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. + * + * 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. + * + * NO WARRANTY + * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + * solely responsible for determining the appropriateness of using and + * distributing the Program and assumes all risks associated with its + * exercise of rights under this Agreement, including but not limited to + * the risks and costs of program errors, damage to or loss of data, + * programs or equipment, and unavailability or interruption of operations. + + * DISCLAIMER OF LIABILITY + * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/kdev_t.h> +#include <linux/blkdev.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/dma-mapping.h> +#include <linux/sort.h> +#include <linux/io.h> + +#include "mpt2sas_base.h" + +static MPT_CALLBACK mpt_callbacks[MPT_MAX_CALLBACKS]; + +#define FAULT_POLLING_INTERVAL 1000 /* in milliseconds */ +#define MPT2SAS_MAX_REQUEST_QUEUE 500 /* maximum controller queue depth */ + +static int max_queue_depth = -1; +module_param(max_queue_depth, int, 0); +MODULE_PARM_DESC(max_queue_depth, " max controller queue depth "); + +static int max_sgl_entries = -1; +module_param(max_sgl_entries, int, 0); +MODULE_PARM_DESC(max_sgl_entries, " max sg entries "); + +static int msix_disable = -1; +module_param(msix_disable, int, 0); +MODULE_PARM_DESC(msix_disable, " disable msix routed interrupts (default=0)"); + +/** + * _base_fault_reset_work - workq handling ioc fault conditions + * @work: input argument, used to derive ioc + * Context: sleep. + * + * Return nothing. + */ +static void +_base_fault_reset_work(struct work_struct *work) +{ + struct MPT2SAS_ADAPTER *ioc = + container_of(work, struct MPT2SAS_ADAPTER, fault_reset_work.work); + unsigned long flags; + u32 doorbell; + int rc; + + spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); + if (ioc->ioc_reset_in_progress) + goto rearm_timer; + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); + + doorbell = mpt2sas_base_get_iocstate(ioc, 0); + if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { + rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + printk(MPT2SAS_WARN_FMT "%s: hard reset: %s\n", ioc->name, + __func__, (rc == 0) ? "success" : "failed"); + doorbell = mpt2sas_base_get_iocstate(ioc, 0); + if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) + mpt2sas_base_fault_info(ioc, doorbell & + MPI2_DOORBELL_DATA_MASK); + } + + spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); + rearm_timer: + if (ioc->fault_reset_work_q) + queue_delayed_work(ioc->fault_reset_work_q, + &ioc->fault_reset_work, + msecs_to_jiffies(FAULT_POLLING_INTERVAL)); + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); +} + +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING +/** + * _base_sas_ioc_info - verbose translation of the ioc status + * @ioc: pointer to scsi command object + * @mpi_reply: reply mf payload returned from firmware + * @request_hdr: request mf + * + * Return nothing. + */ +static void +_base_sas_ioc_info(struct MPT2SAS_ADAPTER *ioc, MPI2DefaultReply_t *mpi_reply, + MPI2RequestHeader_t *request_hdr) +{ + u16 ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & + MPI2_IOCSTATUS_MASK; + char *desc = NULL; + u16 frame_sz; + char *func_str = NULL; + + /* SCSI_IO, RAID_PASS are handled from _scsih_scsi_ioc_info */ + if (request_hdr->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || + request_hdr->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH || + request_hdr->Function == MPI2_FUNCTION_EVENT_NOTIFICATION) + return; + + switch (ioc_status) { + +/**************************************************************************** +* Common IOCStatus values for all replies +****************************************************************************/ + + case MPI2_IOCSTATUS_INVALID_FUNCTION: + desc = "invalid function"; + break; + case MPI2_IOCSTATUS_BUSY: + desc = "busy"; + break; + case MPI2_IOCSTATUS_INVALID_SGL: + desc = "invalid sgl"; + break; + case MPI2_IOCSTATUS_INTERNAL_ERROR: + desc = "internal error"; + break; + case MPI2_IOCSTATUS_INVALID_VPID: + desc = "invalid vpid"; + break; + case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES: + desc = "insufficient resources"; + break; + case MPI2_IOCSTATUS_INVALID_FIELD: + desc = "invalid field"; + break; + case MPI2_IOCSTATUS_INVALID_STATE: + desc = "invalid state"; + break; + case MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED: + desc = "op state not supported"; + break; + +/**************************************************************************** +* Config IOCStatus values +****************************************************************************/ + + case MPI2_IOCSTATUS_CONFIG_INVALID_ACTION: + desc = "config invalid action"; + break; + case MPI2_IOCSTATUS_CONFIG_INVALID_TYPE: + desc = "config invalid type"; + break; + case MPI2_IOCSTATUS_CONFIG_INVALID_PAGE: + desc = "config invalid page"; + break; + case MPI2_IOCSTATUS_CONFIG_INVALID_DATA: + desc = "config invalid data"; + break; + case MPI2_IOCSTATUS_CONFIG_NO_DEFAULTS: + desc = "config no defaults"; + break; + case MPI2_IOCSTATUS_CONFIG_CANT_COMMIT: + desc = "config cant commit"; + break; + +/**************************************************************************** +* SCSI IO Reply +****************************************************************************/ + + case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR: + case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE: + case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE: + case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN: + case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN: + case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR: + case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR: + case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED: + case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: + case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED: + case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED: + case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED: + break; + +/**************************************************************************** +* For use by SCSI Initiator and SCSI Target end-to-end data protection +****************************************************************************/ + + case MPI2_IOCSTATUS_EEDP_GUARD_ERROR: + desc = "eedp guard error"; + break; + case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR: + desc = "eedp ref tag error"; + break; + case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR: + desc = "eedp app tag error"; + break; + +/**************************************************************************** +* SCSI Target values +****************************************************************************/ + + case MPI2_IOCSTATUS_TARGET_INVALID_IO_INDEX: + desc = "target invalid io index"; + break; + case MPI2_IOCSTATUS_TARGET_ABORTED: + desc = "target aborted"; + break; + case MPI2_IOCSTATUS_TARGET_NO_CONN_RETRYABLE: + desc = "target no conn retryable"; + break; + case MPI2_IOCSTATUS_TARGET_NO_CONNECTION: + desc = "target no connection"; + break; + case MPI2_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH: + desc = "target xfer count mismatch"; + break; + case MPI2_IOCSTATUS_TARGET_DATA_OFFSET_ERROR: + desc = "target data offset error"; + break; + case MPI2_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA: + desc = "target too much write data"; + break; + case MPI2_IOCSTATUS_TARGET_IU_TOO_SHORT: + desc = "target iu too short"; + break; + case MPI2_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT: + desc = "target ack nak timeout"; + break; + case MPI2_IOCSTATUS_TARGET_NAK_RECEIVED: + desc = "target nak received"; + break; + +/**************************************************************************** +* Serial Attached SCSI values +****************************************************************************/ + + case MPI2_IOCSTATUS_SAS_SMP_REQUEST_FAILED: + desc = "smp request failed"; + break; + case MPI2_IOCSTATUS_SAS_SMP_DATA_OVERRUN: + desc = "smp data overrun"; + break; + +/**************************************************************************** +* Diagnostic Buffer Post / Diagnostic Release values +****************************************************************************/ + + case MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED: + desc = "diagnostic released"; + break; + default: + break; + } + + if (!desc) + return; + + switch (request_hdr->Function) { + case MPI2_FUNCTION_CONFIG: + frame_sz = sizeof(Mpi2ConfigRequest_t) + ioc->sge_size; + func_str = "config_page"; + break; + case MPI2_FUNCTION_SCSI_TASK_MGMT: + frame_sz = sizeof(Mpi2SCSITaskManagementRequest_t); + func_str = "task_mgmt"; + break; + case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL: + frame_sz = sizeof(Mpi2SasIoUnitControlRequest_t); + func_str = "sas_iounit_ctl"; + break; + case MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR: + frame_sz = sizeof(Mpi2SepRequest_t); + func_str = "enclosure"; + break; + case MPI2_FUNCTION_IOC_INIT: + frame_sz = sizeof(Mpi2IOCInitRequest_t); + func_str = "ioc_init"; + break; + case MPI2_FUNCTION_PORT_ENABLE: + frame_sz = sizeof(Mpi2PortEnableRequest_t); + func_str = "port_enable"; + break; + case MPI2_FUNCTION_SMP_PASSTHROUGH: + frame_sz = sizeof(Mpi2SmpPassthroughRequest_t) + ioc->sge_size; + func_str = "smp_passthru"; + break; + default: + frame_sz = 32; + func_str = "unknown"; + break; + } + + printk(MPT2SAS_WARN_FMT "ioc_status: %s(0x%04x), request(0x%p)," + " (%s)\n", ioc->name, desc, ioc_status, request_hdr, func_str); + + _debug_dump_mf(request_hdr, frame_sz/4); +} + +/** + * _base_display_event_data - verbose translation of firmware asyn events + * @ioc: pointer to scsi command object + * @mpi_reply: reply mf payload returned from firmware + * + * Return nothing. + */ +static void +_base_display_event_data(struct MPT2SAS_ADAPTER *ioc, + Mpi2EventNotificationReply_t *mpi_reply) +{ + char *desc = NULL; + u16 event; + + if (!(ioc->logging_level & MPT_DEBUG_EVENTS)) + return; + + event = le16_to_cpu(mpi_reply->Event); + + switch (event) { + case MPI2_EVENT_LOG_DATA: + desc = "Log Data"; + break; + case MPI2_EVENT_STATE_CHANGE: + desc = "Status Change"; + break; + case MPI2_EVENT_HARD_RESET_RECEIVED: + desc = "Hard Reset Received"; + break; + case MPI2_EVENT_EVENT_CHANGE: + desc = "Event Change"; + break; + case MPI2_EVENT_TASK_SET_FULL: + desc = "Task Set Full"; + break; + case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE: + desc = "Device Status Change"; + break; + case MPI2_EVENT_IR_OPERATION_STATUS: + desc = "IR Operation Status"; + break; + case MPI2_EVENT_SAS_DISCOVERY: + desc = "Discovery"; + break; + case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE: + desc = "SAS Broadcast Primitive"; + break; + case MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE: + desc = "SAS Init Device Status Change"; + break; + case MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW: + desc = "SAS Init Table Overflow"; + break; + case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST: + desc = "SAS Topology Change List"; + break; + case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE: + desc = "SAS Enclosure Device Status Change"; + break; + case MPI2_EVENT_IR_VOLUME: + desc = "IR Volume"; + break; + case MPI2_EVENT_IR_PHYSICAL_DISK: + desc = "IR Physical Disk"; + break; + case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST: + desc = "IR Configuration Change List"; + break; + case MPI2_EVENT_LOG_ENTRY_ADDED: + desc = "Log Entry Added"; + break; + } + + if (!desc) + return; + + printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, desc); +} +#endif + +/** + * _base_sas_log_info - verbose translation of firmware log info + * @ioc: pointer to scsi command object + * @log_info: log info + * + * Return nothing. + */ +static void +_base_sas_log_info(struct MPT2SAS_ADAPTER *ioc , u32 log_info) +{ + union loginfo_type { + u32 loginfo; + struct { + u32 subcode:16; + u32 code:8; + u32 originator:4; + u32 bus_type:4; + } dw; + }; + union loginfo_type sas_loginfo; + char *originator_str = NULL; + + sas_loginfo.loginfo = log_info; + if (sas_loginfo.dw.bus_type != 3 /*SAS*/) + return; + + /* eat the loginfos associated with task aborts */ + if (ioc->ignore_loginfos && (log_info == 30050000 || log_info == + 0x31140000 || log_info == 0x31130000)) + return; + + switch (sas_loginfo.dw.originator) { + case 0: + originator_str = "IOP"; + break; + case 1: + originator_str = "PL"; + break; + case 2: + originator_str = "IR"; + break; + } + + printk(MPT2SAS_WARN_FMT "log_info(0x%08x): originator(%s), " + "code(0x%02x), sub_code(0x%04x)\n", ioc->name, log_info, + originator_str, sas_loginfo.dw.code, + sas_loginfo.dw.subcode); +} + +/** + * mpt2sas_base_fault_info - verbose translation of firmware FAULT code + * @ioc: pointer to scsi command object + * @fault_code: fault code + * + * Return nothing. + */ +void +mpt2sas_base_fault_info(struct MPT2SAS_ADAPTER *ioc , u16 fault_code) +{ + printk(MPT2SAS_ERR_FMT "fault_state(0x%04x)!\n", + ioc->name, fault_code); +} + +/** + * _base_display_reply_info - + * @ioc: pointer to scsi command object + * @smid: system request message index + * @VF_ID: virtual function id + * @reply: reply message frame(lower 32bit addr) + * + * Return nothing. + */ +static void +_base_display_reply_info(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, + u32 reply) +{ + MPI2DefaultReply_t *mpi_reply; + u16 ioc_status; + + mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); + ioc_status = le16_to_cpu(mpi_reply->IOCStatus); +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING + if ((ioc_status & MPI2_IOCSTATUS_MASK) && + (ioc->logging_level & MPT_DEBUG_REPLY)) { + _base_sas_ioc_info(ioc , mpi_reply, + mpt2sas_base_get_msg_frame(ioc, smid)); + } +#endif + if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) + _base_sas_log_info(ioc, le32_to_cpu(mpi_reply->IOCLogInfo)); +} + +/** + * mpt2sas_base_done - base internal command completion routine + * @ioc: pointer to scsi command object + * @smid: system request message index + * @VF_ID: virtual function id + * @reply: reply message frame(lower 32bit addr) + * + * Return nothing. + */ +void +mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply) +{ + MPI2DefaultReply_t *mpi_reply; + + mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); + if (mpi_reply && mpi_reply->Function == MPI2_FUNCTION_EVENT_ACK) + return; + + if (ioc->base_cmds.status == MPT2_CMD_NOT_USED) + return; + + ioc->base_cmds.status |= MPT2_CMD_COMPLETE; + if (mpi_reply) { + ioc->base_cmds.status |= MPT2_CMD_REPLY_VALID; + memcpy(ioc->base_cmds.reply, mpi_reply, mpi_reply->MsgLength*4); + } + ioc->base_cmds.status &= ~MPT2_CMD_PENDING; + complete(&ioc->base_cmds.done); +} + +/** + * _base_async_event - main callback handler for firmware asyn events + * @ioc: pointer to scsi command object + * @VF_ID: virtual function id + * @reply: reply message frame(lower 32bit addr) + * + * Return nothing. + */ +static void +_base_async_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply) +{ + Mpi2EventNotificationReply_t *mpi_reply; + Mpi2EventAckRequest_t *ack_request; + u16 smid; + + mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); + if (!mpi_reply) + return; + if (mpi_reply->Function != MPI2_FUNCTION_EVENT_NOTIFICATION) + return; +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING + _base_display_event_data(ioc, mpi_reply); +#endif + if (!(mpi_reply->AckRequired & MPI2_EVENT_NOTIFICATION_ACK_REQUIRED)) + goto out; + smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx); + if (!smid) { + printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", + ioc->name, __func__); + goto out; + } + + ack_request = mpt2sas_base_get_msg_frame(ioc, smid); + memset(ack_request, 0, sizeof(Mpi2EventAckRequest_t)); + ack_request->Function = MPI2_FUNCTION_EVENT_ACK; + ack_request->Event = mpi_reply->Event; + ack_request->EventContext = mpi_reply->EventContext; + ack_request->VF_ID = VF_ID; + mpt2sas_base_put_smid_default(ioc, smid, VF_ID); + + out: + + /* scsih callback handler */ + mpt2sas_scsih_event_callback(ioc, VF_ID, reply); + + /* ctl callback handler */ + mpt2sas_ctl_event_callback(ioc, VF_ID, reply); +} + +/** + * _base_mask_interrupts - disable interrupts + * @ioc: pointer to scsi command object + * + * Disabling ResetIRQ, Reply and Doorbell Interrupts + * + * Return nothing. + */ +static void +_base_mask_interrupts(struct MPT2SAS_ADAPTER *ioc) +{ + u32 him_register; + + ioc->mask_interrupts = 1; + him_register = readl(&ioc->chip->HostInterruptMask); + him_register |= MPI2_HIM_DIM + MPI2_HIM_RIM + MPI2_HIM_RESET_IRQ_MASK; + writel(him_register, &ioc->chip->HostInterruptMask); + readl(&ioc->chip->HostInterruptMask); +} + +/** + * _base_unmask_interrupts - enable interrupts + * @ioc: pointer to scsi command object + * + * Enabling only Reply Interrupts + * + * Return nothing. + */ +static void +_base_unmask_interrupts(struct MPT2SAS_ADAPTER *ioc) +{ + u32 him_register; + + writel(0, &ioc->chip->HostInterruptStatus); + him_register = readl(&ioc->chip->HostInterruptMask); + him_register &= ~MPI2_HIM_RIM; + writel(him_register, &ioc->chip->HostInterruptMask); + ioc->mask_interrupts = 0; +} + +/** + * _base_interrupt - MPT adapter (IOC) specific interrupt handler. + * @irq: irq number (not used) + * @bus_id: bus identifier cookie == pointer to MPT_ADAPTER structure + * @r: pt_regs pointer (not used) + * + * Return IRQ_HANDLE if processed, else IRQ_NONE. + */ +static irqreturn_t +_base_interrupt(int irq, void *bus_id) +{ + u32 post_index, post_index_next, completed_cmds; + u8 request_desript_type; + u16 smid; + u8 cb_idx; + u32 reply; + u8 VF_ID; + int i; + struct MPT2SAS_ADAPTER *ioc = bus_id; + + if (ioc->mask_interrupts) + return IRQ_NONE; + + post_index = ioc->reply_post_host_index; + request_desript_type = ioc->reply_post_free[post_index]. + Default.ReplyFlags & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK; + if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) + return IRQ_NONE; + + completed_cmds = 0; + do { + if (ioc->reply_post_free[post_index].Words == ~0ULL) + goto out; + reply = 0; + cb_idx = 0xFF; + smid = le16_to_cpu(ioc->reply_post_free[post_index]. + Default.DescriptorTypeDependent1); + VF_ID = ioc->reply_post_free[post_index]. + Default.VF_ID; + if (request_desript_type == + MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY) { + reply = le32_to_cpu(ioc->reply_post_free[post_index]. + AddressReply.ReplyFrameAddress); + } else if (request_desript_type == + MPI2_RPY_DESCRIPT_FLAGS_TARGET_COMMAND_BUFFER) + goto next; + else if (request_desript_type == + MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS) + goto next; + if (smid) + cb_idx = ioc->scsi_lookup[smid - 1].cb_idx; + if (smid && cb_idx != 0xFF) { + mpt_callbacks[cb_idx](ioc, smid, VF_ID, reply); + if (reply) + _base_display_reply_info(ioc, smid, VF_ID, + reply); + mpt2sas_base_free_smid(ioc, smid); + } + if (!smid) + _base_async_event(ioc, VF_ID, reply); + + /* reply free queue handling */ + if (reply) { + ioc->reply_free_host_index = + (ioc->reply_free_host_index == + (ioc->reply_free_queue_depth - 1)) ? + 0 : ioc->reply_free_host_index + 1; + ioc->reply_free[ioc->reply_free_host_index] = + cpu_to_le32(reply); + writel(ioc->reply_free_host_index, + &ioc->chip->ReplyFreeHostIndex); + wmb(); + } + + next: + post_index_next = (post_index == (ioc->reply_post_queue_depth - + 1)) ? 0 : post_index + 1; + request_desript_type = + ioc->reply_post_free[post_index_next].Default.ReplyFlags + & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK; + completed_cmds++; + if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) + goto out; + post_index = post_index_next; + } while (1); + + out: + + if (!completed_cmds) + return IRQ_NONE; + + /* reply post descriptor handling */ + post_index_next = ioc->reply_post_host_index; + for (i = 0 ; i < completed_cmds; i++) { + post_index = post_index_next; + /* poison the reply post descriptor */ + ioc->reply_post_free[post_index_next].Words = ~0ULL; + post_index_next = (post_index == + (ioc->reply_post_queue_depth - 1)) + ? 0 : post_index + 1; + } + ioc->reply_post_host_index = post_index_next; + writel(post_index_next, &ioc->chip->ReplyPostHostIndex); + wmb(); + return IRQ_HANDLED; +} + +/** + * mpt2sas_base_release_callback_handler - clear interupt callback handler + * @cb_idx: callback index + * + * Return nothing. + */ +void +mpt2sas_base_release_callback_handler(u8 cb_idx) +{ + mpt_callbacks[cb_idx] = NULL; +} + +/** + * mpt2sas_base_register_callback_handler - obtain index for the interrupt callback handler + * @cb_func: callback function + * + * Returns cb_func. + */ +u8 +mpt2sas_base_register_callback_handler(MPT_CALLBACK cb_func) +{ + u8 cb_idx; + + for (cb_idx = MPT_MAX_CALLBACKS-1; cb_idx; cb_idx--) + if (mpt_callbacks[cb_idx] == NULL) + break; + + mpt_callbacks[cb_idx] = cb_func; + return cb_idx; +} + +/** + * mpt2sas_base_initialize_callback_handler - initialize the interrupt callback handler + * + * Return nothing. + */ +void +mpt2sas_base_initialize_callback_handler(void) +{ + u8 cb_idx; + + for (cb_idx = 0; cb_idx < MPT_MAX_CALLBACKS; cb_idx++) + mpt2sas_base_release_callback_handler(cb_idx); +} + +/** + * mpt2sas_base_build_zero_len_sge - build zero length sg entry + * @ioc: per adapter object + * @paddr: virtual address for SGE + * + * Create a zero length scatter gather entry to insure the IOCs hardware has + * something to use if the target device goes brain dead and tries + * to send data even when none is asked for. + * + * Return nothing. + */ +void +mpt2sas_base_build_zero_len_sge(struct MPT2SAS_ADAPTER *ioc, void *paddr) +{ + u32 flags_length = (u32)((MPI2_SGE_FLAGS_LAST_ELEMENT | + MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_END_OF_LIST | + MPI2_SGE_FLAGS_SIMPLE_ELEMENT) << + MPI2_SGE_FLAGS_SHIFT); + ioc->base_add_sg_single(paddr, flags_length, -1); +} + +/** + * _base_add_sg_single_32 - Place a simple 32 bit SGE at address pAddr. + * @paddr: virtual address for SGE + * @flags_length: SGE flags and data transfer length + * @dma_addr: Physical address + * + * Return nothing. + */ +static void +_base_add_sg_single_32(void *paddr, u32 flags_length, dma_addr_t dma_addr) +{ + Mpi2SGESimple32_t *sgel = paddr; + + flags_length |= (MPI2_SGE_FLAGS_32_BIT_ADDRESSING | + MPI2_SGE_FLAGS_SYSTEM_ADDRESS) << MPI2_SGE_FLAGS_SHIFT; + sgel->FlagsLength = cpu_to_le32(flags_length); + sgel->Address = cpu_to_le32(dma_addr); +} + + +/** + * _base_add_sg_single_64 - Place a simple 64 bit SGE at address pAddr. + * @paddr: virtual address for SGE + * @flags_length: SGE flags and data transfer length + * @dma_addr: Physical address + * + * Return nothing. + */ +static void +_base_add_sg_single_64(void *paddr, u32 flags_length, dma_addr_t dma_addr) +{ + Mpi2SGESimple64_t *sgel = paddr; + + flags_length |= (MPI2_SGE_FLAGS_64_BIT_ADDRESSING | + MPI2_SGE_FLAGS_SYSTEM_ADDRESS) << MPI2_SGE_FLAGS_SHIFT; + sgel->FlagsLength = cpu_to_le32(flags_length); + sgel->Address = cpu_to_le64(dma_addr); +} + +#define convert_to_kb(x) ((x) << (PAGE_SHIFT - 10)) + +/** + * _base_config_dma_addressing - set dma addressing + * @ioc: per adapter object + * @pdev: PCI device struct + * + * Returns 0 for success, non-zero for failure. + */ +static int +_base_config_dma_addressing(struct MPT2SAS_ADAPTER *ioc, struct pci_dev *pdev) +{ + struct sysinfo s; + char *desc = NULL; + + if (sizeof(dma_addr_t) > 4) { + const uint64_t required_mask = + dma_get_required_mask(&pdev->dev); + if ((required_mask > DMA_32BIT_MASK) && !pci_set_dma_mask(pdev, + DMA_64BIT_MASK) && !pci_set_consistent_dma_mask(pdev, + DMA_64BIT_MASK)) { + ioc->base_add_sg_single = &_base_add_sg_single_64; + ioc->sge_size = sizeof(Mpi2SGESimple64_t); + desc = "64"; + goto out; + } + } + + if (!pci_set_dma_mask(pdev, DMA_32BIT_MASK) + && !pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK)) { + ioc->base_add_sg_single = &_base_add_sg_single_32; + ioc->sge_size = sizeof(Mpi2SGESimple32_t); + desc = "32"; + } else + return -ENODEV; + + out: + si_meminfo(&s); + printk(MPT2SAS_INFO_FMT "%s BIT PCI BUS DMA ADDRESSING SUPPORTED, " + "total mem (%ld kB)\n", ioc->name, desc, convert_to_kb(s.totalram)); + + return 0; +} + +/** + * _base_save_msix_table - backup msix vector table + * @ioc: per adapter object + * + * This address an errata where diag reset clears out the table + */ +static void +_base_save_msix_table(struct MPT2SAS_ADAPTER *ioc) +{ + int i; + + if (!ioc->msix_enable || ioc->msix_table_backup == NULL) + return; + + for (i = 0; i < ioc->msix_vector_count; i++) + ioc->msix_table_backup[i] = ioc->msix_table[i]; +} + +/** + * _base_restore_msix_table - this restores the msix vector table + * @ioc: per adapter object + * + */ +static void +_base_restore_msix_table(struct MPT2SAS_ADAPTER *ioc) +{ + int i; + + if (!ioc->msix_enable || ioc->msix_table_backup == NULL) + return; + + for (i = 0; i < ioc->msix_vector_count; i++) + ioc->msix_table[i] = ioc->msix_table_backup[i]; +} + +/** + * _base_check_enable_msix - checks MSIX capabable. + * @ioc: per adapter object + * + * Check to see if card is capable of MSIX, and set number + * of avaliable msix vectors + */ +static int +_base_check_enable_msix(struct MPT2SAS_ADAPTER *ioc) +{ + int base; + u16 message_control; + u32 msix_table_offset; + + base = pci_find_capability(ioc->pdev, PCI_CAP_ID_MSIX); + if (!base) { + dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "msix not " + "supported\n", ioc->name)); + return -EINVAL; + } + + /* get msix vector count */ + pci_read_config_word(ioc->pdev, base + 2, &message_control); + ioc->msix_vector_count = (message_control & 0x3FF) + 1; + + /* get msix table */ + pci_read_config_dword(ioc->pdev, base + 4, &msix_table_offset); + msix_table_offset &= 0xFFFFFFF8; + ioc->msix_table = (u32 *)((void *)ioc->chip + msix_table_offset); + + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "msix is supported, " + "vector_count(%d), table_offset(0x%08x), table(%p)\n", ioc->name, + ioc->msix_vector_count, msix_table_offset, ioc->msix_table)); + return 0; +} + +/** + * _base_disable_msix - disables msix + * @ioc: per adapter object + * + */ +static void +_base_disable_msix(struct MPT2SAS_ADAPTER *ioc) +{ + if (ioc->msix_enable) { + pci_disable_msix(ioc->pdev); + kfree(ioc->msix_table_backup); + ioc->msix_table_backup = NULL; + ioc->msix_enable = 0; + } +} + +/** + * _base_enable_msix - enables msix, failback to io_apic + * @ioc: per adapter object + * + */ +static int +_base_enable_msix(struct MPT2SAS_ADAPTER *ioc) +{ + struct msix_entry entries; + int r; + u8 try_msix = 0; + + if (msix_disable == -1 || msix_disable == 0) + try_msix = 1; + + if (!try_msix) + goto try_ioapic; + + if (_base_check_enable_msix(ioc) != 0) + goto try_ioapic; + + ioc->msix_table_backup = kcalloc(ioc->msix_vector_count, + sizeof(u32), GFP_KERNEL); + if (!ioc->msix_table_backup) { + dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "allocation for " + "msix_table_backup failed!!!\n", ioc->name)); + goto try_ioapic; + } + + memset(&entries, 0, sizeof(struct msix_entry)); + r = pci_enable_msix(ioc->pdev, &entries, 1); + if (r) { + dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "pci_enable_msix " + "failed (r=%d) !!!\n", ioc->name, r)); + goto try_ioapic; + } + + r = request_irq(entries.vector, _base_interrupt, IRQF_SHARED, + ioc->name, ioc); + if (r) { + dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "unable to allocate " + "interrupt %d !!!\n", ioc->name, entries.vector)); + pci_disable_msix(ioc->pdev); + goto try_ioapic; + } + + ioc->pci_irq = entries.vector; + ioc->msix_enable = 1; + return 0; + +/* failback to io_apic interrupt routing */ + try_ioapic: + + r = request_irq(ioc->pdev->irq, _base_interrupt, IRQF_SHARED, + ioc->name, ioc); + if (r) { + printk(MPT2SAS_ERR_FMT "unable to allocate interrupt %d!\n", + ioc->name, ioc->pdev->irq); + r = -EBUSY; + goto out_fail; + } + + ioc->pci_irq = ioc->pdev->irq; + return 0; + + out_fail: + return r; +} + +/** + * mpt2sas_base_map_resources - map in controller resources (io/irq/memap) + * @ioc: per adapter object + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc) +{ + struct pci_dev *pdev = ioc->pdev; + u32 memap_sz; + u32 pio_sz; + int i, r = 0; + + dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", + ioc->name, __func__)); + + ioc->bars = pci_select_bars(pdev, IORESOURCE_MEM); + if (pci_enable_device_mem(pdev)) { + printk(MPT2SAS_WARN_FMT "pci_enable_device_mem: " + "failed\n", ioc->name); + return -ENODEV; + } + + + if (pci_request_selected_regions(pdev, ioc->bars, + MPT2SAS_DRIVER_NAME)) { + printk(MPT2SAS_WARN_FMT "pci_request_selected_regions: " + "failed\n", ioc->name); + r = -ENODEV; + goto out_fail; + } + + pci_set_master(pdev); + + if (_base_config_dma_addressing(ioc, pdev) != 0) { + printk(MPT2SAS_WARN_FMT "no suitable DMA mask for %s\n", + ioc->name, pci_name(pdev)); + r = -ENODEV; + goto out_fail; + } + + for (i = 0, memap_sz = 0, pio_sz = 0 ; i < DEVICE_COUNT_RESOURCE; i++) { + if (pci_resource_flags(pdev, i) & PCI_BASE_ADDRESS_SPACE_IO) { + if (pio_sz) + continue; + ioc->pio_chip = pci_resource_start(pdev, i); + pio_sz = pci_resource_len(pdev, i); + } else { + if (memap_sz) + continue; + ioc->chip_phys = pci_resource_start(pdev, i); + memap_sz = pci_resource_len(pdev, i); + ioc->chip = ioremap(ioc->chip_phys, memap_sz); + if (ioc->chip == NULL) { + printk(MPT2SAS_ERR_FMT "unable to map adapter " + "memory!\n", ioc->name); + r = -EINVAL; + goto out_fail; + } + } + } + + pci_set_drvdata(pdev, ioc->shost); + _base_mask_interrupts(ioc); + r = _base_enable_msix(ioc); + if (r) + goto out_fail; + + printk(MPT2SAS_INFO_FMT "%s: IRQ %d\n", + ioc->name, ((ioc->msix_enable) ? "PCI-MSI-X enabled" : + "IO-APIC enabled"), ioc->pci_irq); + printk(MPT2SAS_INFO_FMT "iomem(0x%lx), mapped(0x%p), size(%d)\n", + ioc->name, ioc->chip_phys, ioc->chip, memap_sz); + printk(MPT2SAS_INFO_FMT "ioport(0x%lx), size(%d)\n", + ioc->name, ioc->pio_chip, pio_sz); + + return 0; + + out_fail: + if (ioc->chip_phys) + iounmap(ioc->chip); + ioc->chip_phys = 0; + ioc->pci_irq = -1; + pci_release_selected_regions(ioc->pdev, ioc->bars); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + return r; +} + +/** + * mpt2sas_base_get_msg_frame_dma - obtain request mf pointer phys addr + * @ioc: per adapter object + * @smid: system request message index(smid zero is invalid) + * + * Returns phys pointer to message frame. + */ +dma_addr_t +mpt2sas_base_get_msg_frame_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid) +{ + return ioc->request_dma + (smid * ioc->request_sz); +} + +/** + * mpt2sas_base_get_msg_frame - obtain request mf pointer + * @ioc: per adapter object + * @smid: system request message index(smid zero is invalid) + * + * Returns virt pointer to message frame. + */ +void * +mpt2sas_base_get_msg_frame(struct MPT2SAS_ADAPTER *ioc, u16 smid) +{ + return (void *)(ioc->request + (smid * ioc->request_sz)); +} + +/** + * mpt2sas_base_get_sense_buffer - obtain a sense buffer assigned to a mf request + * @ioc: per adapter object + * @smid: system request message index + * + * Returns virt pointer to sense buffer. + */ +void * +mpt2sas_base_get_sense_buffer(struct MPT2SAS_ADAPTER *ioc, u16 smid) +{ + return (void *)(ioc->sense + ((smid - 1) * SCSI_SENSE_BUFFERSIZE)); +} + +/** + * mpt2sas_base_get_sense_buffer_dma - obtain a sense buffer assigned to a mf request + * @ioc: per adapter object + * @smid: system request message index + * + * Returns phys pointer to sense buffer. + */ +dma_addr_t +mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid) +{ + return ioc->sense_dma + ((smid - 1) * SCSI_SENSE_BUFFERSIZE); +} + +/** + * mpt2sas_base_get_reply_virt_addr - obtain reply frames virt address + * @ioc: per adapter object + * @phys_addr: lower 32 physical addr of the reply + * + * Converts 32bit lower physical addr into a virt address. + */ +void * +mpt2sas_base_get_reply_virt_addr(struct MPT2SAS_ADAPTER *ioc, u32 phys_addr) +{ + if (!phys_addr) + return NULL; + return ioc->reply + (phys_addr - (u32)ioc->reply_dma); +} + +/** + * mpt2sas_base_get_smid - obtain a free smid + * @ioc: per adapter object + * @cb_idx: callback index + * + * Returns smid (zero is invalid) + */ +u16 +mpt2sas_base_get_smid(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx) +{ + unsigned long flags; + struct request_tracker *request; + u16 smid; + + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + if (list_empty(&ioc->free_list)) { + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + printk(MPT2SAS_ERR_FMT "%s: smid not available\n", + ioc->name, __func__); + return 0; + } + + request = list_entry(ioc->free_list.next, + struct request_tracker, tracker_list); + request->cb_idx = cb_idx; + smid = request->smid; + list_del(&request->tracker_list); + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + return smid; +} + + +/** + * mpt2sas_base_free_smid - put smid back on free_list + * @ioc: per adapter object + * @smid: system request message index + * + * Return nothing. + */ +void +mpt2sas_base_free_smid(struct MPT2SAS_ADAPTER *ioc, u16 smid) +{ + unsigned long flags; + + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + ioc->scsi_lookup[smid - 1].cb_idx = 0xFF; + list_add_tail(&ioc->scsi_lookup[smid - 1].tracker_list, + &ioc->free_list); + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + + /* + * See _wait_for_commands_to_complete() call with regards to this code. + */ + if (ioc->shost_recovery && ioc->pending_io_count) { + if (ioc->pending_io_count == 1) + wake_up(&ioc->reset_wq); + ioc->pending_io_count--; + } +} + +/** + * _base_writeq - 64 bit write to MMIO + * @ioc: per adapter object + * @b: data payload + * @addr: address in MMIO space + * @writeq_lock: spin lock + * + * Glue for handling an atomic 64 bit word to MMIO. This special handling takes + * care of 32 bit environment where its not quarenteed to send the entire word + * in one transfer. + */ +#ifndef writeq +static inline void _base_writeq(__u64 b, volatile void __iomem *addr, + spinlock_t *writeq_lock) +{ + unsigned long flags; + __u64 data_out = cpu_to_le64(b); + + spin_lock_irqsave(writeq_lock, flags); + writel((u32)(data_out), addr); + writel((u32)(data_out >> 32), (addr + 4)); + spin_unlock_irqrestore(writeq_lock, flags); +} +#else +static inline void _base_writeq(__u64 b, volatile void __iomem *addr, + spinlock_t *writeq_lock) +{ + writeq(cpu_to_le64(b), addr); +} +#endif + +/** + * mpt2sas_base_put_smid_scsi_io - send SCSI_IO request to firmware + * @ioc: per adapter object + * @smid: system request message index + * @vf_id: virtual function id + * @handle: device handle + * + * Return nothing. + */ +void +mpt2sas_base_put_smid_scsi_io(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 vf_id, + u16 handle) +{ + Mpi2RequestDescriptorUnion_t descriptor; + u64 *request = (u64 *)&descriptor; + + + descriptor.SCSIIO.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO; + descriptor.SCSIIO.VF_ID = vf_id; + descriptor.SCSIIO.SMID = cpu_to_le16(smid); + descriptor.SCSIIO.DevHandle = cpu_to_le16(handle); + descriptor.SCSIIO.LMID = 0; + _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow, + &ioc->scsi_lookup_lock); +} + + +/** + * mpt2sas_base_put_smid_hi_priority - send Task Managment request to firmware + * @ioc: per adapter object + * @smid: system request message index + * @vf_id: virtual function id + * + * Return nothing. + */ +void +mpt2sas_base_put_smid_hi_priority(struct MPT2SAS_ADAPTER *ioc, u16 smid, + u8 vf_id) +{ + Mpi2RequestDescriptorUnion_t descriptor; + u64 *request = (u64 *)&descriptor; + + descriptor.HighPriority.RequestFlags = + MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY; + descriptor.HighPriority.VF_ID = vf_id; + descriptor.HighPriority.SMID = cpu_to_le16(smid); + descriptor.HighPriority.LMID = 0; + descriptor.HighPriority.Reserved1 = 0; + _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow, + &ioc->scsi_lookup_lock); +} + +/** + * mpt2sas_base_put_smid_default - Default, primarily used for config pages + * @ioc: per adapter object + * @smid: system request message index + * @vf_id: virtual function id + * + * Return nothing. + */ +void +mpt2sas_base_put_smid_default(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 vf_id) +{ + Mpi2RequestDescriptorUnion_t descriptor; + u64 *request = (u64 *)&descriptor; + + descriptor.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; + descriptor.Default.VF_ID = vf_id; + descriptor.Default.SMID = cpu_to_le16(smid); + descriptor.Default.LMID = 0; + descriptor.Default.DescriptorTypeDependent = 0; + _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow, + &ioc->scsi_lookup_lock); +} + +/** + * mpt2sas_base_put_smid_target_assist - send Target Assist/Status to firmware + * @ioc: per adapter object + * @smid: system request message index + * @vf_id: virtual function id + * @io_index: value used to track the IO + * + * Return nothing. + */ +void +mpt2sas_base_put_smid_target_assist(struct MPT2SAS_ADAPTER *ioc, u16 smid, + u8 vf_id, u16 io_index) +{ + Mpi2RequestDescriptorUnion_t descriptor; + u64 *request = (u64 *)&descriptor; + + descriptor.SCSITarget.RequestFlags = + MPI2_REQ_DESCRIPT_FLAGS_SCSI_TARGET; + descriptor.SCSITarget.VF_ID = vf_id; + descriptor.SCSITarget.SMID = cpu_to_le16(smid); + descriptor.SCSITarget.LMID = 0; + descriptor.SCSITarget.IoIndex = cpu_to_le16(io_index); + _base_writeq(*request, &ioc->chip->RequestDescriptorPostLow, + &ioc->scsi_lookup_lock); +} + +/** + * _base_display_ioc_capabilities - Disply IOC's capabilities. + * @ioc: per adapter object + * + * Return nothing. + */ +static void +_base_display_ioc_capabilities(struct MPT2SAS_ADAPTER *ioc) +{ + int i = 0; + char desc[16]; + u8 revision; + u32 iounit_pg1_flags; + + pci_read_config_byte(ioc->pdev, PCI_CLASS_REVISION, &revision); + strncpy(desc, ioc->manu_pg0.ChipName, 16); + printk(MPT2SAS_INFO_FMT "%s: FWVersion(%02d.%02d.%02d.%02d), " + "ChipRevision(0x%02x), BiosVersion(%02d.%02d.%02d.%02d)\n", + ioc->name, desc, + (ioc->facts.FWVersion.Word & 0xFF000000) >> 24, + (ioc->facts.FWVersion.Word & 0x00FF0000) >> 16, + (ioc->facts.FWVersion.Word & 0x0000FF00) >> 8, + ioc->facts.FWVersion.Word & 0x000000FF, + revision, + (ioc->bios_pg3.BiosVersion & 0xFF000000) >> 24, + (ioc->bios_pg3.BiosVersion & 0x00FF0000) >> 16, + (ioc->bios_pg3.BiosVersion & 0x0000FF00) >> 8, + ioc->bios_pg3.BiosVersion & 0x000000FF); + + printk(MPT2SAS_INFO_FMT "Protocol=(", ioc->name); + + if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR) { + printk("Initiator"); + i++; + } + + if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_TARGET) { + printk("%sTarget", i ? "," : ""); + i++; + } + + i = 0; + printk("), "); + printk("Capabilities=("); + + if (ioc->facts.IOCCapabilities & + MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID) { + printk("Raid"); + i++; + } + + if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR) { + printk("%sTLR", i ? "," : ""); + i++; + } + + if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_MULTICAST) { + printk("%sMulticast", i ? "," : ""); + i++; + } + + if (ioc->facts.IOCCapabilities & + MPI2_IOCFACTS_CAPABILITY_BIDIRECTIONAL_TARGET) { + printk("%sBIDI Target", i ? "," : ""); + i++; + } + + if (ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_EEDP) { + printk("%sEEDP", i ? "," : ""); + i++; + } + + if (ioc->facts.IOCCapabilities & + MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER) { + printk("%sSnapshot Buffer", i ? "," : ""); + i++; + } + + if (ioc->facts.IOCCapabilities & + MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER) { + printk("%sDiag Trace Buffer", i ? "," : ""); + i++; + } + + if (ioc->facts.IOCCapabilities & + MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING) { + printk("%sTask Set Full", i ? "," : ""); + i++; + } + + iounit_pg1_flags = le32_to_cpu(ioc->iounit_pg1.Flags); + if (!(iounit_pg1_flags & MPI2_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE)) { + printk("%sNCQ", i ? "," : ""); + i++; + } + + printk(")\n"); +} + +/** + * _base_static_config_pages - static start of day config pages + * @ioc: per adapter object + * + * Return nothing. + */ +static void +_base_static_config_pages(struct MPT2SAS_ADAPTER *ioc) +{ + Mpi2ConfigReply_t mpi_reply; + u32 iounit_pg1_flags; + + mpt2sas_config_get_manufacturing_pg0(ioc, &mpi_reply, &ioc->manu_pg0); + mpt2sas_config_get_bios_pg2(ioc, &mpi_reply, &ioc->bios_pg2); + mpt2sas_config_get_bios_pg3(ioc, &mpi_reply, &ioc->bios_pg3); + mpt2sas_config_get_ioc_pg8(ioc, &mpi_reply, &ioc->ioc_pg8); + mpt2sas_config_get_iounit_pg0(ioc, &mpi_reply, &ioc->iounit_pg0); + mpt2sas_config_get_iounit_pg1(ioc, &mpi_reply, &ioc->iounit_pg1); + _base_display_ioc_capabilities(ioc); + + /* + * Enable task_set_full handling in iounit_pg1 when the + * facts capabilities indicate that its supported. + */ + iounit_pg1_flags = le32_to_cpu(ioc->iounit_pg1.Flags); + if ((ioc->facts.IOCCapabilities & + MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING)) + iounit_pg1_flags &= + ~MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING; + else + iounit_pg1_flags |= + MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING; + ioc->iounit_pg1.Flags = cpu_to_le32(iounit_pg1_flags); + mpt2sas_config_set_iounit_pg1(ioc, &mpi_reply, ioc->iounit_pg1); +} + +/** + * _base_release_memory_pools - release memory + * @ioc: per adapter object + * + * Free memory allocated from _base_allocate_memory_pools. + * + * Return nothing. + */ +static void +_base_release_memory_pools(struct MPT2SAS_ADAPTER *ioc) +{ + dexitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + __func__)); + + if (ioc->request) { + pci_free_consistent(ioc->pdev, ioc->request_dma_sz, + ioc->request, ioc->request_dma); + dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "request_pool(0x%p)" + ": free\n", ioc->name, ioc->request)); + ioc->request = NULL; + } + + if (ioc->sense) { + pci_pool_free(ioc->sense_dma_pool, ioc->sense, ioc->sense_dma); + if (ioc->sense_dma_pool) + pci_pool_destroy(ioc->sense_dma_pool); + dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "sense_pool(0x%p)" + ": free\n", ioc->name, ioc->sense)); + ioc->sense = NULL; + } + + if (ioc->reply) { + pci_pool_free(ioc->reply_dma_pool, ioc->reply, ioc->reply_dma); + if (ioc->reply_dma_pool) + pci_pool_destroy(ioc->reply_dma_pool); + dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply_pool(0x%p)" + ": free\n", ioc->name, ioc->reply)); + ioc->reply = NULL; + } + + if (ioc->reply_free) { + pci_pool_free(ioc->reply_free_dma_pool, ioc->reply_free, + ioc->reply_free_dma); + if (ioc->reply_free_dma_pool) + pci_pool_destroy(ioc->reply_free_dma_pool); + dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply_free_pool" + "(0x%p): free\n", ioc->name, ioc->reply_free)); + ioc->reply_free = NULL; + } + + if (ioc->reply_post_free) { + pci_pool_free(ioc->reply_post_free_dma_pool, + ioc->reply_post_free, ioc->reply_post_free_dma); + if (ioc->reply_post_free_dma_pool) + pci_pool_destroy(ioc->reply_post_free_dma_pool); + dexitprintk(ioc, printk(MPT2SAS_INFO_FMT + "reply_post_free_pool(0x%p): free\n", ioc->name, + ioc->reply_post_free)); + ioc->reply_post_free = NULL; + } + + if (ioc->config_page) { + dexitprintk(ioc, printk(MPT2SAS_INFO_FMT + "config_page(0x%p): free\n", ioc->name, + ioc->config_page)); + pci_free_consistent(ioc->pdev, ioc->config_page_sz, + ioc->config_page, ioc->config_page_dma); + } + + kfree(ioc->scsi_lookup); +} + + +/** + * _base_allocate_memory_pools - allocate start of day memory pools + * @ioc: per adapter object + * @sleep_flag: CAN_SLEEP or NO_SLEEP + * + * Returns 0 success, anything else error + */ +static int +_base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) +{ + Mpi2IOCFactsReply_t *facts; + u32 queue_size, queue_diff; + u16 max_sge_elements; + u16 num_of_reply_frames; + u16 chains_needed_per_io; + u32 sz, total_sz; + u16 i; + u32 retry_sz; + u16 max_request_credit; + + dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + __func__)); + + retry_sz = 0; + facts = &ioc->facts; + + /* command line tunables for max sgl entries */ + if (max_sgl_entries != -1) { + ioc->shost->sg_tablesize = (max_sgl_entries < + MPT2SAS_SG_DEPTH) ? max_sgl_entries : + MPT2SAS_SG_DEPTH; + } else { + ioc->shost->sg_tablesize = MPT2SAS_SG_DEPTH; + } + + /* command line tunables for max controller queue depth */ + if (max_queue_depth != -1) { + max_request_credit = (max_queue_depth < facts->RequestCredit) + ? max_queue_depth : facts->RequestCredit; + } else { + max_request_credit = (facts->RequestCredit > + MPT2SAS_MAX_REQUEST_QUEUE) ? MPT2SAS_MAX_REQUEST_QUEUE : + facts->RequestCredit; + } + ioc->request_depth = max_request_credit; + + /* request frame size */ + ioc->request_sz = facts->IOCRequestFrameSize * 4; + + /* reply frame size */ + ioc->reply_sz = facts->ReplyFrameSize * 4; + + retry_allocation: + total_sz = 0; + /* calculate number of sg elements left over in the 1st frame */ + max_sge_elements = ioc->request_sz - ((sizeof(Mpi2SCSIIORequest_t) - + sizeof(Mpi2SGEIOUnion_t)) + ioc->sge_size); + ioc->max_sges_in_main_message = max_sge_elements/ioc->sge_size; + + /* now do the same for a chain buffer */ + max_sge_elements = ioc->request_sz - ioc->sge_size; + ioc->max_sges_in_chain_message = max_sge_elements/ioc->sge_size; + + ioc->chain_offset_value_for_main_message = + ((sizeof(Mpi2SCSIIORequest_t) - sizeof(Mpi2SGEIOUnion_t)) + + (ioc->max_sges_in_chain_message * ioc->sge_size)) / 4; + + /* + * MPT2SAS_SG_DEPTH = CONFIG_FUSION_MAX_SGE + */ + chains_needed_per_io = ((ioc->shost->sg_tablesize - + ioc->max_sges_in_main_message)/ioc->max_sges_in_chain_message) + + 1; + if (chains_needed_per_io > facts->MaxChainDepth) { + chains_needed_per_io = facts->MaxChainDepth; + ioc->shost->sg_tablesize = min_t(u16, + ioc->max_sges_in_main_message + (ioc->max_sges_in_chain_message + * chains_needed_per_io), ioc->shost->sg_tablesize); + } + ioc->chains_needed_per_io = chains_needed_per_io; + + /* reply free queue sizing - taking into account for events */ + num_of_reply_frames = ioc->request_depth + 32; + + /* number of replies frames can't be a multiple of 16 */ + /* decrease number of reply frames by 1 */ + if (!(num_of_reply_frames % 16)) + num_of_reply_frames--; + + /* calculate number of reply free queue entries + * (must be multiple of 16) + */ + + /* (we know reply_free_queue_depth is not a multiple of 16) */ + queue_size = num_of_reply_frames; + queue_size += 16 - (queue_size % 16); + ioc->reply_free_queue_depth = queue_size; + + /* reply descriptor post queue sizing */ + /* this size should be the number of request frames + number of reply + * frames + */ + + queue_size = ioc->request_depth + num_of_reply_frames + 1; + /* round up to 16 byte boundary */ + if (queue_size % 16) + queue_size += 16 - (queue_size % 16); + + /* check against IOC maximum reply post queue depth */ + if (queue_size > facts->MaxReplyDescriptorPostQueueDepth) { + queue_diff = queue_size - + facts->MaxReplyDescriptorPostQueueDepth; + + /* round queue_diff up to multiple of 16 */ + if (queue_diff % 16) + queue_diff += 16 - (queue_diff % 16); + + /* adjust request_depth, reply_free_queue_depth, + * and queue_size + */ + ioc->request_depth -= queue_diff; + ioc->reply_free_queue_depth -= queue_diff; + queue_size -= queue_diff; + } + ioc->reply_post_queue_depth = queue_size; + + /* max scsi host queue depth */ + ioc->shost->can_queue = ioc->request_depth - INTERNAL_CMDS_COUNT; + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "scsi host queue: depth" + "(%d)\n", ioc->name, ioc->shost->can_queue)); + + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "scatter gather: " + "sge_in_main_msg(%d), sge_per_chain(%d), sge_per_io(%d), " + "chains_per_io(%d)\n", ioc->name, ioc->max_sges_in_main_message, + ioc->max_sges_in_chain_message, ioc->shost->sg_tablesize, + ioc->chains_needed_per_io)); + + /* contiguous pool for request and chains, 16 byte align, one extra " + * "frame for smid=0 + */ + ioc->chain_depth = ioc->chains_needed_per_io * ioc->request_depth; + sz = ((ioc->request_depth + 1 + ioc->chain_depth) * ioc->request_sz); + + ioc->request_dma_sz = sz; + ioc->request = pci_alloc_consistent(ioc->pdev, sz, &ioc->request_dma); + if (!ioc->request) { + printk(MPT2SAS_ERR_FMT "request pool: pci_alloc_consistent " + "failed: req_depth(%d), chains_per_io(%d), frame_sz(%d), " + "total(%d kB)\n", ioc->name, ioc->request_depth, + ioc->chains_needed_per_io, ioc->request_sz, sz/1024); + if (ioc->request_depth < MPT2SAS_SAS_QUEUE_DEPTH) + goto out; + retry_sz += 64; + ioc->request_depth = max_request_credit - retry_sz; + goto retry_allocation; + } + + if (retry_sz) + printk(MPT2SAS_ERR_FMT "request pool: pci_alloc_consistent " + "succeed: req_depth(%d), chains_per_io(%d), frame_sz(%d), " + "total(%d kb)\n", ioc->name, ioc->request_depth, + ioc->chains_needed_per_io, ioc->request_sz, sz/1024); + + ioc->chain = ioc->request + ((ioc->request_depth + 1) * + ioc->request_sz); + ioc->chain_dma = ioc->request_dma + ((ioc->request_depth + 1) * + ioc->request_sz); + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "request pool(0x%p): " + "depth(%d), frame_size(%d), pool_size(%d kB)\n", ioc->name, + ioc->request, ioc->request_depth, ioc->request_sz, + ((ioc->request_depth + 1) * ioc->request_sz)/1024)); + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "chain pool(0x%p): depth" + "(%d), frame_size(%d), pool_size(%d kB)\n", ioc->name, ioc->chain, + ioc->chain_depth, ioc->request_sz, ((ioc->chain_depth * + ioc->request_sz))/1024)); + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "request pool: dma(0x%llx)\n", + ioc->name, (unsigned long long) ioc->request_dma)); + total_sz += sz; + + ioc->scsi_lookup = kcalloc(ioc->request_depth, + sizeof(struct request_tracker), GFP_KERNEL); + if (!ioc->scsi_lookup) { + printk(MPT2SAS_ERR_FMT "scsi_lookup: kcalloc failed\n", + ioc->name); + goto out; + } + + /* initialize some bits */ + for (i = 0; i < ioc->request_depth; i++) + ioc->scsi_lookup[i].smid = i + 1; + + /* sense buffers, 4 byte align */ + sz = ioc->request_depth * SCSI_SENSE_BUFFERSIZE; + ioc->sense_dma_pool = pci_pool_create("sense pool", ioc->pdev, sz, 4, + 0); + if (!ioc->sense_dma_pool) { + printk(MPT2SAS_ERR_FMT "sense pool: pci_pool_create failed\n", + ioc->name); + goto out; + } + ioc->sense = pci_pool_alloc(ioc->sense_dma_pool , GFP_KERNEL, + &ioc->sense_dma); + if (!ioc->sense) { + printk(MPT2SAS_ERR_FMT "sense pool: pci_pool_alloc failed\n", + ioc->name); + goto out; + } + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT + "sense pool(0x%p): depth(%d), element_size(%d), pool_size" + "(%d kB)\n", ioc->name, ioc->sense, ioc->request_depth, + SCSI_SENSE_BUFFERSIZE, sz/1024)); + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "sense_dma(0x%llx)\n", + ioc->name, (unsigned long long)ioc->sense_dma)); + total_sz += sz; + + /* reply pool, 4 byte align */ + sz = ioc->reply_free_queue_depth * ioc->reply_sz; + ioc->reply_dma_pool = pci_pool_create("reply pool", ioc->pdev, sz, 4, + 0); + if (!ioc->reply_dma_pool) { + printk(MPT2SAS_ERR_FMT "reply pool: pci_pool_create failed\n", + ioc->name); + goto out; + } + ioc->reply = pci_pool_alloc(ioc->reply_dma_pool , GFP_KERNEL, + &ioc->reply_dma); + if (!ioc->reply) { + printk(MPT2SAS_ERR_FMT "reply pool: pci_pool_alloc failed\n", + ioc->name); + goto out; + } + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply pool(0x%p): depth" + "(%d), frame_size(%d), pool_size(%d kB)\n", ioc->name, ioc->reply, + ioc->reply_free_queue_depth, ioc->reply_sz, sz/1024)); + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply_dma(0x%llx)\n", + ioc->name, (unsigned long long)ioc->reply_dma)); + total_sz += sz; + + /* reply free queue, 16 byte align */ + sz = ioc->reply_free_queue_depth * 4; + ioc->reply_free_dma_pool = pci_pool_create("reply_free pool", + ioc->pdev, sz, 16, 0); + if (!ioc->reply_free_dma_pool) { + printk(MPT2SAS_ERR_FMT "reply_free pool: pci_pool_create " + "failed\n", ioc->name); + goto out; + } + ioc->reply_free = pci_pool_alloc(ioc->reply_free_dma_pool , GFP_KERNEL, + &ioc->reply_free_dma); + if (!ioc->reply_free) { + printk(MPT2SAS_ERR_FMT "reply_free pool: pci_pool_alloc " + "failed\n", ioc->name); + goto out; + } + memset(ioc->reply_free, 0, sz); + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply_free pool(0x%p): " + "depth(%d), element_size(%d), pool_size(%d kB)\n", ioc->name, + ioc->reply_free, ioc->reply_free_queue_depth, 4, sz/1024)); + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply_free_dma" + "(0x%llx)\n", ioc->name, (unsigned long long)ioc->reply_free_dma)); + total_sz += sz; + + /* reply post queue, 16 byte align */ + sz = ioc->reply_post_queue_depth * sizeof(Mpi2DefaultReplyDescriptor_t); + ioc->reply_post_free_dma_pool = pci_pool_create("reply_post_free pool", + ioc->pdev, sz, 16, 0); + if (!ioc->reply_post_free_dma_pool) { + printk(MPT2SAS_ERR_FMT "reply_post_free pool: pci_pool_create " + "failed\n", ioc->name); + goto out; + } + ioc->reply_post_free = pci_pool_alloc(ioc->reply_post_free_dma_pool , + GFP_KERNEL, &ioc->reply_post_free_dma); + if (!ioc->reply_post_free) { + printk(MPT2SAS_ERR_FMT "reply_post_free pool: pci_pool_alloc " + "failed\n", ioc->name); + goto out; + } + memset(ioc->reply_post_free, 0, sz); + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply post free pool" + "(0x%p): depth(%d), element_size(%d), pool_size(%d kB)\n", + ioc->name, ioc->reply_post_free, ioc->reply_post_queue_depth, 8, + sz/1024)); + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "reply_post_free_dma = " + "(0x%llx)\n", ioc->name, (unsigned long long) + ioc->reply_post_free_dma)); + total_sz += sz; + + ioc->config_page_sz = 512; + ioc->config_page = pci_alloc_consistent(ioc->pdev, + ioc->config_page_sz, &ioc->config_page_dma); + if (!ioc->config_page) { + printk(MPT2SAS_ERR_FMT "config page: pci_pool_alloc " + "failed\n", ioc->name); + goto out; + } + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "config page(0x%p): size" + "(%d)\n", ioc->name, ioc->config_page, ioc->config_page_sz)); + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "config_page_dma" + "(0x%llx)\n", ioc->name, (unsigned long long)ioc->config_page_dma)); + total_sz += ioc->config_page_sz; + + printk(MPT2SAS_INFO_FMT "Allocated physical memory: size(%d kB)\n", + ioc->name, total_sz/1024); + printk(MPT2SAS_INFO_FMT "Current Controller Queue Depth(%d), " + "Max Controller Queue Depth(%d)\n", + ioc->name, ioc->shost->can_queue, facts->RequestCredit); + printk(MPT2SAS_INFO_FMT "Scatter Gather Elements per IO(%d)\n", + ioc->name, ioc->shost->sg_tablesize); + return 0; + + out: + _base_release_memory_pools(ioc); + return -ENOMEM; +} + + +/** + * mpt2sas_base_get_iocstate - Get the current state of a MPT adapter. + * @ioc: Pointer to MPT_ADAPTER structure + * @cooked: Request raw or cooked IOC state + * + * Returns all IOC Doorbell register bits if cooked==0, else just the + * Doorbell bits in MPI_IOC_STATE_MASK. + */ +u32 +mpt2sas_base_get_iocstate(struct MPT2SAS_ADAPTER *ioc, int cooked) +{ + u32 s, sc; + + s = readl(&ioc->chip->Doorbell); + sc = s & MPI2_IOC_STATE_MASK; + return cooked ? sc : s; +} + +/** + * _base_wait_on_iocstate - waiting on a particular ioc state + * @ioc_state: controller state { READY, OPERATIONAL, or RESET } + * @timeout: timeout in second + * @sleep_flag: CAN_SLEEP or NO_SLEEP + * + * Returns 0 for success, non-zero for failure. + */ +static int +_base_wait_on_iocstate(struct MPT2SAS_ADAPTER *ioc, u32 ioc_state, int timeout, + int sleep_flag) +{ + u32 count, cntdn; + u32 current_state; + + count = 0; + cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; + do { + current_state = mpt2sas_base_get_iocstate(ioc, 1); + if (current_state == ioc_state) + return 0; + if (count && current_state == MPI2_IOC_STATE_FAULT) + break; + if (sleep_flag == CAN_SLEEP) + msleep(1); + else + udelay(500); + count++; + } while (--cntdn); + + return current_state; +} + +/** + * _base_wait_for_doorbell_int - waiting for controller interrupt(generated by + * a write to the doorbell) + * @ioc: per adapter object + * @timeout: timeout in second + * @sleep_flag: CAN_SLEEP or NO_SLEEP + * + * Returns 0 for success, non-zero for failure. + * + * Notes: MPI2_HIS_IOC2SYS_DB_STATUS - set to one when IOC writes to doorbell. + */ +static int +_base_wait_for_doorbell_int(struct MPT2SAS_ADAPTER *ioc, int timeout, + int sleep_flag) +{ + u32 cntdn, count; + u32 int_status; + + count = 0; + cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; + do { + int_status = readl(&ioc->chip->HostInterruptStatus); + if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) { + dhsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + "successfull count(%d), timeout(%d)\n", ioc->name, + __func__, count, timeout)); + return 0; + } + if (sleep_flag == CAN_SLEEP) + msleep(1); + else + udelay(500); + count++; + } while (--cntdn); + + printk(MPT2SAS_ERR_FMT "%s: failed due to timeout count(%d), " + "int_status(%x)!\n", ioc->name, __func__, count, int_status); + return -EFAULT; +} + +/** + * _base_wait_for_doorbell_ack - waiting for controller to read the doorbell. + * @ioc: per adapter object + * @timeout: timeout in second + * @sleep_flag: CAN_SLEEP or NO_SLEEP + * + * Returns 0 for success, non-zero for failure. + * + * Notes: MPI2_HIS_SYS2IOC_DB_STATUS - set to one when host writes to + * doorbell. + */ +static int +_base_wait_for_doorbell_ack(struct MPT2SAS_ADAPTER *ioc, int timeout, + int sleep_flag) +{ + u32 cntdn, count; + u32 int_status; + u32 doorbell; + + count = 0; + cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; + do { + int_status = readl(&ioc->chip->HostInterruptStatus); + if (!(int_status & MPI2_HIS_SYS2IOC_DB_STATUS)) { + dhsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + "successfull count(%d), timeout(%d)\n", ioc->name, + __func__, count, timeout)); + return 0; + } else if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) { + doorbell = readl(&ioc->chip->Doorbell); + if ((doorbell & MPI2_IOC_STATE_MASK) == + MPI2_IOC_STATE_FAULT) { + mpt2sas_base_fault_info(ioc , doorbell); + return -EFAULT; + } + } else if (int_status == 0xFFFFFFFF) + goto out; + + if (sleep_flag == CAN_SLEEP) + msleep(1); + else + udelay(500); + count++; + } while (--cntdn); + + out: + printk(MPT2SAS_ERR_FMT "%s: failed due to timeout count(%d), " + "int_status(%x)!\n", ioc->name, __func__, count, int_status); + return -EFAULT; +} + +/** + * _base_wait_for_doorbell_not_used - waiting for doorbell to not be in use + * @ioc: per adapter object + * @timeout: timeout in second + * @sleep_flag: CAN_SLEEP or NO_SLEEP + * + * Returns 0 for success, non-zero for failure. + * + */ +static int +_base_wait_for_doorbell_not_used(struct MPT2SAS_ADAPTER *ioc, int timeout, + int sleep_flag) +{ + u32 cntdn, count; + u32 doorbell_reg; + + count = 0; + cntdn = (sleep_flag == CAN_SLEEP) ? 1000*timeout : 2000*timeout; + do { + doorbell_reg = readl(&ioc->chip->Doorbell); + if (!(doorbell_reg & MPI2_DOORBELL_USED)) { + dhsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + "successfull count(%d), timeout(%d)\n", ioc->name, + __func__, count, timeout)); + return 0; + } + if (sleep_flag == CAN_SLEEP) + msleep(1); + else + udelay(500); + count++; + } while (--cntdn); + + printk(MPT2SAS_ERR_FMT "%s: failed due to timeout count(%d), " + "doorbell_reg(%x)!\n", ioc->name, __func__, count, doorbell_reg); + return -EFAULT; +} + +/** + * _base_send_ioc_reset - send doorbell reset + * @ioc: per adapter object + * @reset_type: currently only supports: MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET + * @timeout: timeout in second + * @sleep_flag: CAN_SLEEP or NO_SLEEP + * + * Returns 0 for success, non-zero for failure. + */ +static int +_base_send_ioc_reset(struct MPT2SAS_ADAPTER *ioc, u8 reset_type, int timeout, + int sleep_flag) +{ + u32 ioc_state; + int r = 0; + + if (reset_type != MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET) { + printk(MPT2SAS_ERR_FMT "%s: unknown reset_type\n", + ioc->name, __func__); + return -EFAULT; + } + + if (!(ioc->facts.IOCCapabilities & + MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY)) + return -EFAULT; + + printk(MPT2SAS_INFO_FMT "sending message unit reset !!\n", ioc->name); + + writel(reset_type << MPI2_DOORBELL_FUNCTION_SHIFT, + &ioc->chip->Doorbell); + if ((_base_wait_for_doorbell_ack(ioc, 15, sleep_flag))) { + r = -EFAULT; + goto out; + } + ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, + timeout, sleep_flag); + if (ioc_state) { + printk(MPT2SAS_ERR_FMT "%s: failed going to ready state " + " (ioc_state=0x%x)\n", ioc->name, __func__, ioc_state); + r = -EFAULT; + goto out; + } + out: + printk(MPT2SAS_INFO_FMT "message unit reset: %s\n", + ioc->name, ((r == 0) ? "SUCCESS" : "FAILED")); + return r; +} + +/** + * _base_handshake_req_reply_wait - send request thru doorbell interface + * @ioc: per adapter object + * @request_bytes: request length + * @request: pointer having request payload + * @reply_bytes: reply length + * @reply: pointer to reply payload + * @timeout: timeout in second + * @sleep_flag: CAN_SLEEP or NO_SLEEP + * + * Returns 0 for success, non-zero for failure. + */ +static int +_base_handshake_req_reply_wait(struct MPT2SAS_ADAPTER *ioc, int request_bytes, + u32 *request, int reply_bytes, u16 *reply, int timeout, int sleep_flag) +{ + MPI2DefaultReply_t *default_reply = (MPI2DefaultReply_t *)reply; + int i; + u8 failed; + u16 dummy; + u32 *mfp; + + /* make sure doorbell is not in use */ + if ((readl(&ioc->chip->Doorbell) & MPI2_DOORBELL_USED)) { + printk(MPT2SAS_ERR_FMT "doorbell is in use " + " (line=%d)\n", ioc->name, __LINE__); + return -EFAULT; + } + + /* clear pending doorbell interrupts from previous state changes */ + if (readl(&ioc->chip->HostInterruptStatus) & + MPI2_HIS_IOC2SYS_DB_STATUS) + writel(0, &ioc->chip->HostInterruptStatus); + + /* send message to ioc */ + writel(((MPI2_FUNCTION_HANDSHAKE<<MPI2_DOORBELL_FUNCTION_SHIFT) | + ((request_bytes/4)<<MPI2_DOORBELL_ADD_DWORDS_SHIFT)), + &ioc->chip->Doorbell); + + if ((_base_wait_for_doorbell_int(ioc, 5, sleep_flag))) { + printk(MPT2SAS_ERR_FMT "doorbell handshake " + "int failed (line=%d)\n", ioc->name, __LINE__); + return -EFAULT; + } + writel(0, &ioc->chip->HostInterruptStatus); + + if ((_base_wait_for_doorbell_ack(ioc, 5, sleep_flag))) { + printk(MPT2SAS_ERR_FMT "doorbell handshake " + "ack failed (line=%d)\n", ioc->name, __LINE__); + return -EFAULT; + } + + /* send message 32-bits at a time */ + for (i = 0, failed = 0; i < request_bytes/4 && !failed; i++) { + writel(cpu_to_le32(request[i]), &ioc->chip->Doorbell); + if ((_base_wait_for_doorbell_ack(ioc, 5, sleep_flag))) + failed = 1; + } + + if (failed) { + printk(MPT2SAS_ERR_FMT "doorbell handshake " + "sending request failed (line=%d)\n", ioc->name, __LINE__); + return -EFAULT; + } + + /* now wait for the reply */ + if ((_base_wait_for_doorbell_int(ioc, timeout, sleep_flag))) { + printk(MPT2SAS_ERR_FMT "doorbell handshake " + "int failed (line=%d)\n", ioc->name, __LINE__); + return -EFAULT; + } + + /* read the first two 16-bits, it gives the total length of the reply */ + reply[0] = le16_to_cpu(readl(&ioc->chip->Doorbell) + & MPI2_DOORBELL_DATA_MASK); + writel(0, &ioc->chip->HostInterruptStatus); + if ((_base_wait_for_doorbell_int(ioc, 5, sleep_flag))) { + printk(MPT2SAS_ERR_FMT "doorbell handshake " + "int failed (line=%d)\n", ioc->name, __LINE__); + return -EFAULT; + } + reply[1] = le16_to_cpu(readl(&ioc->chip->Doorbell) + & MPI2_DOORBELL_DATA_MASK); + writel(0, &ioc->chip->HostInterruptStatus); + + for (i = 2; i < default_reply->MsgLength * 2; i++) { + if ((_base_wait_for_doorbell_int(ioc, 5, sleep_flag))) { + printk(MPT2SAS_ERR_FMT "doorbell " + "handshake int failed (line=%d)\n", ioc->name, + __LINE__); + return -EFAULT; + } + if (i >= reply_bytes/2) /* overflow case */ + dummy = readl(&ioc->chip->Doorbell); + else + reply[i] = le16_to_cpu(readl(&ioc->chip->Doorbell) + & MPI2_DOORBELL_DATA_MASK); + writel(0, &ioc->chip->HostInterruptStatus); + } + + _base_wait_for_doorbell_int(ioc, 5, sleep_flag); + if (_base_wait_for_doorbell_not_used(ioc, 5, sleep_flag) != 0) { + dhsprintk(ioc, printk(MPT2SAS_INFO_FMT "doorbell is in use " + " (line=%d)\n", ioc->name, __LINE__)); + } + writel(0, &ioc->chip->HostInterruptStatus); + + if (ioc->logging_level & MPT_DEBUG_INIT) { + mfp = (u32 *)reply; + printk(KERN_DEBUG "\toffset:data\n"); + for (i = 0; i < reply_bytes/4; i++) + printk(KERN_DEBUG "\t[0x%02x]:%08x\n", i*4, + le32_to_cpu(mfp[i])); + } + return 0; +} + +/** + * mpt2sas_base_sas_iounit_control - send sas iounit control to FW + * @ioc: per adapter object + * @mpi_reply: the reply payload from FW + * @mpi_request: the request payload sent to FW + * + * The SAS IO Unit Control Request message allows the host to perform low-level + * operations, such as resets on the PHYs of the IO Unit, also allows the host + * to obtain the IOC assigned device handles for a device if it has other + * identifying information about the device, in addition allows the host to + * remove IOC resources associated with the device. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_base_sas_iounit_control(struct MPT2SAS_ADAPTER *ioc, + Mpi2SasIoUnitControlReply_t *mpi_reply, + Mpi2SasIoUnitControlRequest_t *mpi_request) +{ + u16 smid; + u32 ioc_state; + unsigned long timeleft; + u8 issue_reset; + int rc; + void *request; + u16 wait_state_count; + + dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + __func__)); + + mutex_lock(&ioc->base_cmds.mutex); + + if (ioc->base_cmds.status != MPT2_CMD_NOT_USED) { + printk(MPT2SAS_ERR_FMT "%s: base_cmd in use\n", + ioc->name, __func__); + rc = -EAGAIN; + goto out; + } + + wait_state_count = 0; + ioc_state = mpt2sas_base_get_iocstate(ioc, 1); + while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { + if (wait_state_count++ == 10) { + printk(MPT2SAS_ERR_FMT + "%s: failed due to ioc not operational\n", + ioc->name, __func__); + rc = -EFAULT; + goto out; + } + ssleep(1); + ioc_state = mpt2sas_base_get_iocstate(ioc, 1); + printk(MPT2SAS_INFO_FMT "%s: waiting for " + "operational state(count=%d)\n", ioc->name, + __func__, wait_state_count); + } + + smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx); + if (!smid) { + printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", + ioc->name, __func__); + rc = -EAGAIN; + goto out; + } + + rc = 0; + ioc->base_cmds.status = MPT2_CMD_PENDING; + request = mpt2sas_base_get_msg_frame(ioc, smid); + ioc->base_cmds.smid = smid; + memcpy(request, mpi_request, sizeof(Mpi2SasIoUnitControlRequest_t)); + if (mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET || + mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET) + ioc->ioc_link_reset_in_progress = 1; + mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID); + timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, + msecs_to_jiffies(10000)); + if ((mpi_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET || + mpi_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET) && + ioc->ioc_link_reset_in_progress) + ioc->ioc_link_reset_in_progress = 0; + if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) { + printk(MPT2SAS_ERR_FMT "%s: timeout\n", + ioc->name, __func__); + _debug_dump_mf(mpi_request, + sizeof(Mpi2SasIoUnitControlRequest_t)/4); + if (!(ioc->base_cmds.status & MPT2_CMD_RESET)) + issue_reset = 1; + goto issue_host_reset; + } + if (ioc->base_cmds.status & MPT2_CMD_REPLY_VALID) + memcpy(mpi_reply, ioc->base_cmds.reply, + sizeof(Mpi2SasIoUnitControlReply_t)); + else + memset(mpi_reply, 0, sizeof(Mpi2SasIoUnitControlReply_t)); + ioc->base_cmds.status = MPT2_CMD_NOT_USED; + goto out; + + issue_host_reset: + if (issue_reset) + mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + ioc->base_cmds.status = MPT2_CMD_NOT_USED; + rc = -EFAULT; + out: + mutex_unlock(&ioc->base_cmds.mutex); + return rc; +} + + +/** + * mpt2sas_base_scsi_enclosure_processor - sending request to sep device + * @ioc: per adapter object + * @mpi_reply: the reply payload from FW + * @mpi_request: the request payload sent to FW + * + * The SCSI Enclosure Processor request message causes the IOC to + * communicate with SES devices to control LED status signals. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_base_scsi_enclosure_processor(struct MPT2SAS_ADAPTER *ioc, + Mpi2SepReply_t *mpi_reply, Mpi2SepRequest_t *mpi_request) +{ + u16 smid; + u32 ioc_state; + unsigned long timeleft; + u8 issue_reset; + int rc; + void *request; + u16 wait_state_count; + + dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + __func__)); + + mutex_lock(&ioc->base_cmds.mutex); + + if (ioc->base_cmds.status != MPT2_CMD_NOT_USED) { + printk(MPT2SAS_ERR_FMT "%s: base_cmd in use\n", + ioc->name, __func__); + rc = -EAGAIN; + goto out; + } + + wait_state_count = 0; + ioc_state = mpt2sas_base_get_iocstate(ioc, 1); + while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { + if (wait_state_count++ == 10) { + printk(MPT2SAS_ERR_FMT + "%s: failed due to ioc not operational\n", + ioc->name, __func__); + rc = -EFAULT; + goto out; + } + ssleep(1); + ioc_state = mpt2sas_base_get_iocstate(ioc, 1); + printk(MPT2SAS_INFO_FMT "%s: waiting for " + "operational state(count=%d)\n", ioc->name, + __func__, wait_state_count); + } + + smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx); + if (!smid) { + printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", + ioc->name, __func__); + rc = -EAGAIN; + goto out; + } + + rc = 0; + ioc->base_cmds.status = MPT2_CMD_PENDING; + request = mpt2sas_base_get_msg_frame(ioc, smid); + ioc->base_cmds.smid = smid; + memcpy(request, mpi_request, sizeof(Mpi2SepReply_t)); + mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID); + timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, + msecs_to_jiffies(10000)); + if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) { + printk(MPT2SAS_ERR_FMT "%s: timeout\n", + ioc->name, __func__); + _debug_dump_mf(mpi_request, + sizeof(Mpi2SepRequest_t)/4); + if (!(ioc->base_cmds.status & MPT2_CMD_RESET)) + issue_reset = 1; + goto issue_host_reset; + } + if (ioc->base_cmds.status & MPT2_CMD_REPLY_VALID) + memcpy(mpi_reply, ioc->base_cmds.reply, + sizeof(Mpi2SepReply_t)); + else + memset(mpi_reply, 0, sizeof(Mpi2SepReply_t)); + ioc->base_cmds.status = MPT2_CMD_NOT_USED; + goto out; + + issue_host_reset: + if (issue_reset) + mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + ioc->base_cmds.status = MPT2_CMD_NOT_USED; + rc = -EFAULT; + out: + mutex_unlock(&ioc->base_cmds.mutex); + return rc; +} + +/** + * _base_get_port_facts - obtain port facts reply and save in ioc + * @ioc: per adapter object + * @sleep_flag: CAN_SLEEP or NO_SLEEP + * + * Returns 0 for success, non-zero for failure. + */ +static int +_base_get_port_facts(struct MPT2SAS_ADAPTER *ioc, int port, int sleep_flag) +{ + Mpi2PortFactsRequest_t mpi_request; + Mpi2PortFactsReply_t mpi_reply, *pfacts; + int mpi_reply_sz, mpi_request_sz, r; + + dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + __func__)); + + mpi_reply_sz = sizeof(Mpi2PortFactsReply_t); + mpi_request_sz = sizeof(Mpi2PortFactsRequest_t); + memset(&mpi_request, 0, mpi_request_sz); + mpi_request.Function = MPI2_FUNCTION_PORT_FACTS; + mpi_request.PortNumber = port; + r = _base_handshake_req_reply_wait(ioc, mpi_request_sz, + (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply, 5, CAN_SLEEP); + + if (r != 0) { + printk(MPT2SAS_ERR_FMT "%s: handshake failed (r=%d)\n", + ioc->name, __func__, r); + return r; + } + + pfacts = &ioc->pfacts[port]; + memset(pfacts, 0, sizeof(Mpi2PortFactsReply_t)); + pfacts->PortNumber = mpi_reply.PortNumber; + pfacts->VP_ID = mpi_reply.VP_ID; + pfacts->VF_ID = mpi_reply.VF_ID; + pfacts->MaxPostedCmdBuffers = + le16_to_cpu(mpi_reply.MaxPostedCmdBuffers); + + return 0; +} + +/** + * _base_get_ioc_facts - obtain ioc facts reply and save in ioc + * @ioc: per adapter object + * @sleep_flag: CAN_SLEEP or NO_SLEEP + * + * Returns 0 for success, non-zero for failure. + */ +static int +_base_get_ioc_facts(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) +{ + Mpi2IOCFactsRequest_t mpi_request; + Mpi2IOCFactsReply_t mpi_reply, *facts; + int mpi_reply_sz, mpi_request_sz, r; + + dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + __func__)); + + mpi_reply_sz = sizeof(Mpi2IOCFactsReply_t); + mpi_request_sz = sizeof(Mpi2IOCFactsRequest_t); + memset(&mpi_request, 0, mpi_request_sz); + mpi_request.Function = MPI2_FUNCTION_IOC_FACTS; + r = _base_handshake_req_reply_wait(ioc, mpi_request_sz, + (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply, 5, CAN_SLEEP); + + if (r != 0) { + printk(MPT2SAS_ERR_FMT "%s: handshake failed (r=%d)\n", + ioc->name, __func__, r); + return r; + } + + facts = &ioc->facts; + memset(facts, 0, sizeof(Mpi2IOCFactsReply_t)); + facts->MsgVersion = le16_to_cpu(mpi_reply.MsgVersion); + facts->HeaderVersion = le16_to_cpu(mpi_reply.HeaderVersion); + facts->VP_ID = mpi_reply.VP_ID; + facts->VF_ID = mpi_reply.VF_ID; + facts->IOCExceptions = le16_to_cpu(mpi_reply.IOCExceptions); + facts->MaxChainDepth = mpi_reply.MaxChainDepth; + facts->WhoInit = mpi_reply.WhoInit; + facts->NumberOfPorts = mpi_reply.NumberOfPorts; + facts->RequestCredit = le16_to_cpu(mpi_reply.RequestCredit); + facts->MaxReplyDescriptorPostQueueDepth = + le16_to_cpu(mpi_reply.MaxReplyDescriptorPostQueueDepth); + facts->ProductID = le16_to_cpu(mpi_reply.ProductID); + facts->IOCCapabilities = le32_to_cpu(mpi_reply.IOCCapabilities); + if ((facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID)) + ioc->ir_firmware = 1; + facts->FWVersion.Word = le32_to_cpu(mpi_reply.FWVersion.Word); + facts->IOCRequestFrameSize = + le16_to_cpu(mpi_reply.IOCRequestFrameSize); + facts->MaxInitiators = le16_to_cpu(mpi_reply.MaxInitiators); + facts->MaxTargets = le16_to_cpu(mpi_reply.MaxTargets); + ioc->shost->max_id = -1; + facts->MaxSasExpanders = le16_to_cpu(mpi_reply.MaxSasExpanders); + facts->MaxEnclosures = le16_to_cpu(mpi_reply.MaxEnclosures); + facts->ProtocolFlags = le16_to_cpu(mpi_reply.ProtocolFlags); + facts->HighPriorityCredit = + le16_to_cpu(mpi_reply.HighPriorityCredit); + facts->ReplyFrameSize = mpi_reply.ReplyFrameSize; + facts->MaxDevHandle = le16_to_cpu(mpi_reply.MaxDevHandle); + + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "hba queue depth(%d), " + "max chains per io(%d)\n", ioc->name, facts->RequestCredit, + facts->MaxChainDepth)); + dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "request frame size(%d), " + "reply frame size(%d)\n", ioc->name, + facts->IOCRequestFrameSize * 4, facts->ReplyFrameSize * 4)); + return 0; +} + +/** + * _base_send_ioc_init - send ioc_init to firmware + * @ioc: per adapter object + * @VF_ID: virtual function id + * @sleep_flag: CAN_SLEEP or NO_SLEEP + * + * Returns 0 for success, non-zero for failure. + */ +static int +_base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, int sleep_flag) +{ + Mpi2IOCInitRequest_t mpi_request; + Mpi2IOCInitReply_t mpi_reply; + int r; + + dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + __func__)); + + memset(&mpi_request, 0, sizeof(Mpi2IOCInitRequest_t)); + mpi_request.Function = MPI2_FUNCTION_IOC_INIT; + mpi_request.WhoInit = MPI2_WHOINIT_HOST_DRIVER; + mpi_request.VF_ID = VF_ID; + mpi_request.MsgVersion = cpu_to_le16(MPI2_VERSION); + mpi_request.HeaderVersion = cpu_to_le16(MPI2_HEADER_VERSION); + + /* In MPI Revision I (0xA), the SystemReplyFrameSize(offset 0x18) was + * removed and made reserved. For those with older firmware will need + * this fix. It was decided that the Reply and Request frame sizes are + * the same. + */ + if ((ioc->facts.HeaderVersion >> 8) < 0xA) { + mpi_request.Reserved7 = cpu_to_le16(ioc->reply_sz); +/* mpi_request.SystemReplyFrameSize = + * cpu_to_le16(ioc->reply_sz); + */ + } + + mpi_request.SystemRequestFrameSize = cpu_to_le16(ioc->request_sz/4); + mpi_request.ReplyDescriptorPostQueueDepth = + cpu_to_le16(ioc->reply_post_queue_depth); + mpi_request.ReplyFreeQueueDepth = + cpu_to_le16(ioc->reply_free_queue_depth); + +#if BITS_PER_LONG > 32 + mpi_request.SenseBufferAddressHigh = + cpu_to_le32(ioc->sense_dma >> 32); + mpi_request.SystemReplyAddressHigh = + cpu_to_le32(ioc->reply_dma >> 32); + mpi_request.SystemRequestFrameBaseAddress = + cpu_to_le64(ioc->request_dma); + mpi_request.ReplyFreeQueueAddress = + cpu_to_le64(ioc->reply_free_dma); + mpi_request.ReplyDescriptorPostQueueAddress = + cpu_to_le64(ioc->reply_post_free_dma); +#else + mpi_request.SystemRequestFrameBaseAddress = + cpu_to_le32(ioc->request_dma); + mpi_request.ReplyFreeQueueAddress = + cpu_to_le32(ioc->reply_free_dma); + mpi_request.ReplyDescriptorPostQueueAddress = + cpu_to_le32(ioc->reply_post_free_dma); +#endif + + if (ioc->logging_level & MPT_DEBUG_INIT) { + u32 *mfp; + int i; + + mfp = (u32 *)&mpi_request; + printk(KERN_DEBUG "\toffset:data\n"); + for (i = 0; i < sizeof(Mpi2IOCInitRequest_t)/4; i++) + printk(KERN_DEBUG "\t[0x%02x]:%08x\n", i*4, + le32_to_cpu(mfp[i])); + } + + r = _base_handshake_req_reply_wait(ioc, + sizeof(Mpi2IOCInitRequest_t), (u32 *)&mpi_request, + sizeof(Mpi2IOCInitReply_t), (u16 *)&mpi_reply, 10, + sleep_flag); + + if (r != 0) { + printk(MPT2SAS_ERR_FMT "%s: handshake failed (r=%d)\n", + ioc->name, __func__, r); + return r; + } + + if (mpi_reply.IOCStatus != MPI2_IOCSTATUS_SUCCESS || + mpi_reply.IOCLogInfo) { + printk(MPT2SAS_ERR_FMT "%s: failed\n", ioc->name, __func__); + r = -EIO; + } + + return 0; +} + +/** + * _base_send_port_enable - send port_enable(discovery stuff) to firmware + * @ioc: per adapter object + * @VF_ID: virtual function id + * @sleep_flag: CAN_SLEEP or NO_SLEEP + * + * Returns 0 for success, non-zero for failure. + */ +static int +_base_send_port_enable(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, int sleep_flag) +{ + Mpi2PortEnableRequest_t *mpi_request; + u32 ioc_state; + unsigned long timeleft; + int r = 0; + u16 smid; + + printk(MPT2SAS_INFO_FMT "sending port enable !!\n", ioc->name); + + if (ioc->base_cmds.status & MPT2_CMD_PENDING) { + printk(MPT2SAS_ERR_FMT "%s: internal command already in use\n", + ioc->name, __func__); + return -EAGAIN; + } + + smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx); + if (!smid) { + printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", + ioc->name, __func__); + return -EAGAIN; + } + + ioc->base_cmds.status = MPT2_CMD_PENDING; + mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); + ioc->base_cmds.smid = smid; + memset(mpi_request, 0, sizeof(Mpi2PortEnableRequest_t)); + mpi_request->Function = MPI2_FUNCTION_PORT_ENABLE; + mpi_request->VF_ID = VF_ID; + + mpt2sas_base_put_smid_default(ioc, smid, VF_ID); + timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, + 300*HZ); + if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) { + printk(MPT2SAS_ERR_FMT "%s: timeout\n", + ioc->name, __func__); + _debug_dump_mf(mpi_request, + sizeof(Mpi2PortEnableRequest_t)/4); + if (ioc->base_cmds.status & MPT2_CMD_RESET) + r = -EFAULT; + else + r = -ETIME; + goto out; + } else + dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: complete\n", + ioc->name, __func__)); + + ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_OPERATIONAL, + 60, sleep_flag); + if (ioc_state) { + printk(MPT2SAS_ERR_FMT "%s: failed going to operational state " + " (ioc_state=0x%x)\n", ioc->name, __func__, ioc_state); + r = -EFAULT; + } + out: + ioc->base_cmds.status = MPT2_CMD_NOT_USED; + printk(MPT2SAS_INFO_FMT "port enable: %s\n", + ioc->name, ((r == 0) ? "SUCCESS" : "FAILED")); + return r; +} + +/** + * _base_unmask_events - turn on notification for this event + * @ioc: per adapter object + * @event: firmware event + * + * The mask is stored in ioc->event_masks. + */ +static void +_base_unmask_events(struct MPT2SAS_ADAPTER *ioc, u16 event) +{ + u32 desired_event; + + if (event >= 128) + return; + + desired_event = (1 << (event % 32)); + + if (event < 32) + ioc->event_masks[0] &= ~desired_event; + else if (event < 64) + ioc->event_masks[1] &= ~desired_event; + else if (event < 96) + ioc->event_masks[2] &= ~desired_event; + else if (event < 128) + ioc->event_masks[3] &= ~desired_event; +} + +/** + * _base_event_notification - send event notification + * @ioc: per adapter object + * @VF_ID: virtual function id + * @sleep_flag: CAN_SLEEP or NO_SLEEP + * + * Returns 0 for success, non-zero for failure. + */ +static int +_base_event_notification(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, int sleep_flag) +{ + Mpi2EventNotificationRequest_t *mpi_request; + unsigned long timeleft; + u16 smid; + int r = 0; + int i; + + dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + __func__)); + + if (ioc->base_cmds.status & MPT2_CMD_PENDING) { + printk(MPT2SAS_ERR_FMT "%s: internal command already in use\n", + ioc->name, __func__); + return -EAGAIN; + } + + smid = mpt2sas_base_get_smid(ioc, ioc->base_cb_idx); + if (!smid) { + printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", + ioc->name, __func__); + return -EAGAIN; + } + ioc->base_cmds.status = MPT2_CMD_PENDING; + mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); + ioc->base_cmds.smid = smid; + memset(mpi_request, 0, sizeof(Mpi2EventNotificationRequest_t)); + mpi_request->Function = MPI2_FUNCTION_EVENT_NOTIFICATION; + mpi_request->VF_ID = VF_ID; + for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) + mpi_request->EventMasks[i] = + le32_to_cpu(ioc->event_masks[i]); + mpt2sas_base_put_smid_default(ioc, smid, VF_ID); + timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, 30*HZ); + if (!(ioc->base_cmds.status & MPT2_CMD_COMPLETE)) { + printk(MPT2SAS_ERR_FMT "%s: timeout\n", + ioc->name, __func__); + _debug_dump_mf(mpi_request, + sizeof(Mpi2EventNotificationRequest_t)/4); + if (ioc->base_cmds.status & MPT2_CMD_RESET) + r = -EFAULT; + else + r = -ETIME; + } else + dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: complete\n", + ioc->name, __func__)); + ioc->base_cmds.status = MPT2_CMD_NOT_USED; + return r; +} + +/** + * mpt2sas_base_validate_event_type - validating event types + * @ioc: per adapter object + * @event: firmware event + * + * This will turn on firmware event notification when application + * ask for that event. We don't mask events that are already enabled. + */ +void +mpt2sas_base_validate_event_type(struct MPT2SAS_ADAPTER *ioc, u32 *event_type) +{ + int i, j; + u32 event_mask, desired_event; + u8 send_update_to_fw; + + for (i = 0, send_update_to_fw = 0; i < + MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) { + event_mask = ~event_type[i]; + desired_event = 1; + for (j = 0; j < 32; j++) { + if (!(event_mask & desired_event) && + (ioc->event_masks[i] & desired_event)) { + ioc->event_masks[i] &= ~desired_event; + send_update_to_fw = 1; + } + desired_event = (desired_event << 1); + } + } + + if (!send_update_to_fw) + return; + + mutex_lock(&ioc->base_cmds.mutex); + _base_event_notification(ioc, 0, CAN_SLEEP); + mutex_unlock(&ioc->base_cmds.mutex); +} + +/** + * _base_diag_reset - the "big hammer" start of day reset + * @ioc: per adapter object + * @sleep_flag: CAN_SLEEP or NO_SLEEP + * + * Returns 0 for success, non-zero for failure. + */ +static int +_base_diag_reset(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) +{ + u32 host_diagnostic; + u32 ioc_state; + u32 count; + u32 hcb_size; + + printk(MPT2SAS_INFO_FMT "sending diag reset !!\n", ioc->name); + + _base_save_msix_table(ioc); + + drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "clear interrupts\n", + ioc->name)); + writel(0, &ioc->chip->HostInterruptStatus); + + count = 0; + do { + /* Write magic sequence to WriteSequence register + * Loop until in diagnostic mode + */ + drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "write magic " + "sequence\n", ioc->name)); + writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence); + writel(MPI2_WRSEQ_1ST_KEY_VALUE, &ioc->chip->WriteSequence); + writel(MPI2_WRSEQ_2ND_KEY_VALUE, &ioc->chip->WriteSequence); + writel(MPI2_WRSEQ_3RD_KEY_VALUE, &ioc->chip->WriteSequence); + writel(MPI2_WRSEQ_4TH_KEY_VALUE, &ioc->chip->WriteSequence); + writel(MPI2_WRSEQ_5TH_KEY_VALUE, &ioc->chip->WriteSequence); + writel(MPI2_WRSEQ_6TH_KEY_VALUE, &ioc->chip->WriteSequence); + + /* wait 100 msec */ + if (sleep_flag == CAN_SLEEP) + msleep(100); + else + mdelay(100); + + if (count++ > 20) + goto out; + + host_diagnostic = readl(&ioc->chip->HostDiagnostic); + drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "wrote magic " + "sequence: count(%d), host_diagnostic(0x%08x)\n", + ioc->name, count, host_diagnostic)); + + } while ((host_diagnostic & MPI2_DIAG_DIAG_WRITE_ENABLE) == 0); + + hcb_size = readl(&ioc->chip->HCBSize); + + drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "diag reset: issued\n", + ioc->name)); + writel(host_diagnostic | MPI2_DIAG_RESET_ADAPTER, + &ioc->chip->HostDiagnostic); + + /* don't access any registers for 50 milliseconds */ + msleep(50); + + /* 300 second max wait */ + for (count = 0; count < 3000000 ; count++) { + + host_diagnostic = readl(&ioc->chip->HostDiagnostic); + + if (host_diagnostic == 0xFFFFFFFF) + goto out; + if (!(host_diagnostic & MPI2_DIAG_RESET_ADAPTER)) + break; + + /* wait 100 msec */ + if (sleep_flag == CAN_SLEEP) + msleep(1); + else + mdelay(1); + } + + if (host_diagnostic & MPI2_DIAG_HCB_MODE) { + + drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "restart the adapter " + "assuming the HCB Address points to good F/W\n", + ioc->name)); + host_diagnostic &= ~MPI2_DIAG_BOOT_DEVICE_SELECT_MASK; + host_diagnostic |= MPI2_DIAG_BOOT_DEVICE_SELECT_HCDW; + writel(host_diagnostic, &ioc->chip->HostDiagnostic); + + drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT + "re-enable the HCDW\n", ioc->name)); + writel(hcb_size | MPI2_HCB_SIZE_HCB_ENABLE, + &ioc->chip->HCBSize); + } + + drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "restart the adapter\n", + ioc->name)); + writel(host_diagnostic & ~MPI2_DIAG_HOLD_IOC_RESET, + &ioc->chip->HostDiagnostic); + + drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "disable writes to the " + "diagnostic register\n", ioc->name)); + writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, &ioc->chip->WriteSequence); + + drsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "Wait for FW to go to the " + "READY state\n", ioc->name)); + ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY, 20, + sleep_flag); + if (ioc_state) { + printk(MPT2SAS_ERR_FMT "%s: failed going to ready state " + " (ioc_state=0x%x)\n", ioc->name, __func__, ioc_state); + goto out; + } + + _base_restore_msix_table(ioc); + printk(MPT2SAS_INFO_FMT "diag reset: SUCCESS\n", ioc->name); + return 0; + + out: + printk(MPT2SAS_ERR_FMT "diag reset: FAILED\n", ioc->name); + return -EFAULT; +} + +/** + * _base_make_ioc_ready - put controller in READY state + * @ioc: per adapter object + * @sleep_flag: CAN_SLEEP or NO_SLEEP + * @type: FORCE_BIG_HAMMER or SOFT_RESET + * + * Returns 0 for success, non-zero for failure. + */ +static int +_base_make_ioc_ready(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, + enum reset_type type) +{ + u32 ioc_state; + + dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + __func__)); + + ioc_state = mpt2sas_base_get_iocstate(ioc, 0); + dhsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: ioc_state(0x%08x)\n", + ioc->name, __func__, ioc_state)); + + if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_READY) + return 0; + + if (ioc_state & MPI2_DOORBELL_USED) { + dhsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "unexpected doorbell " + "active!\n", ioc->name)); + goto issue_diag_reset; + } + + if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { + mpt2sas_base_fault_info(ioc, ioc_state & + MPI2_DOORBELL_DATA_MASK); + goto issue_diag_reset; + } + + if (type == FORCE_BIG_HAMMER) + goto issue_diag_reset; + + if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_OPERATIONAL) + if (!(_base_send_ioc_reset(ioc, + MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET, 15, CAN_SLEEP))) + return 0; + + issue_diag_reset: + return _base_diag_reset(ioc, CAN_SLEEP); +} + +/** + * _base_make_ioc_operational - put controller in OPERATIONAL state + * @ioc: per adapter object + * @VF_ID: virtual function id + * @sleep_flag: CAN_SLEEP or NO_SLEEP + * + * Returns 0 for success, non-zero for failure. + */ +static int +_base_make_ioc_operational(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, + int sleep_flag) +{ + int r, i; + unsigned long flags; + u32 reply_address; + + dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + __func__)); + + /* initialize the scsi lookup free list */ + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + INIT_LIST_HEAD(&ioc->free_list); + for (i = 0; i < ioc->request_depth; i++) { + ioc->scsi_lookup[i].cb_idx = 0xFF; + list_add_tail(&ioc->scsi_lookup[i].tracker_list, + &ioc->free_list); + } + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + + /* initialize Reply Free Queue */ + for (i = 0, reply_address = (u32)ioc->reply_dma ; + i < ioc->reply_free_queue_depth ; i++, reply_address += + ioc->reply_sz) + ioc->reply_free[i] = cpu_to_le32(reply_address); + + /* initialize Reply Post Free Queue */ + for (i = 0; i < ioc->reply_post_queue_depth; i++) + ioc->reply_post_free[i].Words = ~0ULL; + + r = _base_send_ioc_init(ioc, VF_ID, sleep_flag); + if (r) + return r; + + /* initialize the index's */ + ioc->reply_free_host_index = ioc->reply_free_queue_depth - 1; + ioc->reply_post_host_index = 0; + writel(ioc->reply_free_host_index, &ioc->chip->ReplyFreeHostIndex); + writel(0, &ioc->chip->ReplyPostHostIndex); + + _base_unmask_interrupts(ioc); + r = _base_event_notification(ioc, VF_ID, sleep_flag); + if (r) + return r; + + if (sleep_flag == CAN_SLEEP) + _base_static_config_pages(ioc); + + r = _base_send_port_enable(ioc, VF_ID, sleep_flag); + if (r) + return r; + + return r; +} + +/** + * mpt2sas_base_free_resources - free resources controller resources (io/irq/memap) + * @ioc: per adapter object + * + * Return nothing. + */ +void +mpt2sas_base_free_resources(struct MPT2SAS_ADAPTER *ioc) +{ + struct pci_dev *pdev = ioc->pdev; + + dexitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + __func__)); + + _base_mask_interrupts(ioc); + _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET); + if (ioc->pci_irq) { + synchronize_irq(pdev->irq); + free_irq(ioc->pci_irq, ioc); + } + _base_disable_msix(ioc); + if (ioc->chip_phys) + iounmap(ioc->chip); + ioc->pci_irq = -1; + ioc->chip_phys = 0; + pci_release_selected_regions(ioc->pdev, ioc->bars); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + return; +} + +/** + * mpt2sas_base_attach - attach controller instance + * @ioc: per adapter object + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) +{ + int r, i; + unsigned long flags; + + dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + __func__)); + + r = mpt2sas_base_map_resources(ioc); + if (r) + return r; + + r = _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET); + if (r) + goto out_free_resources; + + r = _base_get_ioc_facts(ioc, CAN_SLEEP); + if (r) + goto out_free_resources; + + r = _base_allocate_memory_pools(ioc, CAN_SLEEP); + if (r) + goto out_free_resources; + + init_waitqueue_head(&ioc->reset_wq); + + /* base internal command bits */ + mutex_init(&ioc->base_cmds.mutex); + init_completion(&ioc->base_cmds.done); + ioc->base_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); + ioc->base_cmds.status = MPT2_CMD_NOT_USED; + + /* transport internal command bits */ + ioc->transport_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); + ioc->transport_cmds.status = MPT2_CMD_NOT_USED; + mutex_init(&ioc->transport_cmds.mutex); + init_completion(&ioc->transport_cmds.done); + + /* task management internal command bits */ + ioc->tm_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); + ioc->tm_cmds.status = MPT2_CMD_NOT_USED; + mutex_init(&ioc->tm_cmds.mutex); + init_completion(&ioc->tm_cmds.done); + + /* config page internal command bits */ + ioc->config_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); + ioc->config_cmds.status = MPT2_CMD_NOT_USED; + mutex_init(&ioc->config_cmds.mutex); + init_completion(&ioc->config_cmds.done); + + /* ctl module internal command bits */ + ioc->ctl_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL); + ioc->ctl_cmds.status = MPT2_CMD_NOT_USED; + mutex_init(&ioc->ctl_cmds.mutex); + init_completion(&ioc->ctl_cmds.done); + + for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) + ioc->event_masks[i] = -1; + + /* here we enable the events we care about */ + _base_unmask_events(ioc, MPI2_EVENT_SAS_DISCOVERY); + _base_unmask_events(ioc, MPI2_EVENT_SAS_BROADCAST_PRIMITIVE); + _base_unmask_events(ioc, MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST); + _base_unmask_events(ioc, MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE); + _base_unmask_events(ioc, MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE); + _base_unmask_events(ioc, MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST); + _base_unmask_events(ioc, MPI2_EVENT_IR_VOLUME); + _base_unmask_events(ioc, MPI2_EVENT_IR_PHYSICAL_DISK); + _base_unmask_events(ioc, MPI2_EVENT_IR_OPERATION_STATUS); + _base_unmask_events(ioc, MPI2_EVENT_TASK_SET_FULL); + _base_unmask_events(ioc, MPI2_EVENT_LOG_ENTRY_ADDED); + + ioc->pfacts = kcalloc(ioc->facts.NumberOfPorts, + sizeof(Mpi2PortFactsReply_t), GFP_KERNEL); + if (!ioc->pfacts) + goto out_free_resources; + + for (i = 0 ; i < ioc->facts.NumberOfPorts; i++) { + r = _base_get_port_facts(ioc, i, CAN_SLEEP); + if (r) + goto out_free_resources; + } + r = _base_make_ioc_operational(ioc, 0, CAN_SLEEP); + if (r) + goto out_free_resources; + + /* initialize fault polling */ + INIT_DELAYED_WORK(&ioc->fault_reset_work, _base_fault_reset_work); + snprintf(ioc->fault_reset_work_q_name, + sizeof(ioc->fault_reset_work_q_name), "poll_%d_status", ioc->id); + ioc->fault_reset_work_q = + create_singlethread_workqueue(ioc->fault_reset_work_q_name); + if (!ioc->fault_reset_work_q) { + printk(MPT2SAS_ERR_FMT "%s: failed (line=%d)\n", + ioc->name, __func__, __LINE__); + goto out_free_resources; + } + spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); + if (ioc->fault_reset_work_q) + queue_delayed_work(ioc->fault_reset_work_q, + &ioc->fault_reset_work, + msecs_to_jiffies(FAULT_POLLING_INTERVAL)); + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); + return 0; + + out_free_resources: + + ioc->remove_host = 1; + mpt2sas_base_free_resources(ioc); + _base_release_memory_pools(ioc); + kfree(ioc->tm_cmds.reply); + kfree(ioc->transport_cmds.reply); + kfree(ioc->config_cmds.reply); + kfree(ioc->base_cmds.reply); + kfree(ioc->ctl_cmds.reply); + kfree(ioc->pfacts); + ioc->ctl_cmds.reply = NULL; + ioc->base_cmds.reply = NULL; + ioc->tm_cmds.reply = NULL; + ioc->transport_cmds.reply = NULL; + ioc->config_cmds.reply = NULL; + ioc->pfacts = NULL; + return r; +} + + +/** + * mpt2sas_base_detach - remove controller instance + * @ioc: per adapter object + * + * Return nothing. + */ +void +mpt2sas_base_detach(struct MPT2SAS_ADAPTER *ioc) +{ + unsigned long flags; + struct workqueue_struct *wq; + + dexitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + __func__)); + + spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); + wq = ioc->fault_reset_work_q; + ioc->fault_reset_work_q = NULL; + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); + if (!cancel_delayed_work(&ioc->fault_reset_work)) + flush_workqueue(wq); + destroy_workqueue(wq); + + mpt2sas_base_free_resources(ioc); + _base_release_memory_pools(ioc); + kfree(ioc->pfacts); + kfree(ioc->ctl_cmds.reply); + kfree(ioc->base_cmds.reply); + kfree(ioc->tm_cmds.reply); + kfree(ioc->transport_cmds.reply); + kfree(ioc->config_cmds.reply); +} + +/** + * _base_reset_handler - reset callback handler (for base) + * @ioc: per adapter object + * @reset_phase: phase + * + * The handler for doing any required cleanup or initialization. + * + * The reset phase can be MPT2_IOC_PRE_RESET, MPT2_IOC_AFTER_RESET, + * MPT2_IOC_DONE_RESET + * + * Return nothing. + */ +static void +_base_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase) +{ + switch (reset_phase) { + case MPT2_IOC_PRE_RESET: + dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + "MPT2_IOC_PRE_RESET\n", ioc->name, __func__)); + break; + case MPT2_IOC_AFTER_RESET: + dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + "MPT2_IOC_AFTER_RESET\n", ioc->name, __func__)); + if (ioc->transport_cmds.status & MPT2_CMD_PENDING) { + ioc->transport_cmds.status |= MPT2_CMD_RESET; + mpt2sas_base_free_smid(ioc, ioc->transport_cmds.smid); + complete(&ioc->transport_cmds.done); + } + if (ioc->base_cmds.status & MPT2_CMD_PENDING) { + ioc->base_cmds.status |= MPT2_CMD_RESET; + mpt2sas_base_free_smid(ioc, ioc->base_cmds.smid); + complete(&ioc->base_cmds.done); + } + if (ioc->config_cmds.status & MPT2_CMD_PENDING) { + ioc->config_cmds.status |= MPT2_CMD_RESET; + mpt2sas_base_free_smid(ioc, ioc->config_cmds.smid); + complete(&ioc->config_cmds.done); + } + break; + case MPT2_IOC_DONE_RESET: + dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + "MPT2_IOC_DONE_RESET\n", ioc->name, __func__)); + break; + } + mpt2sas_scsih_reset_handler(ioc, reset_phase); + mpt2sas_ctl_reset_handler(ioc, reset_phase); +} + +/** + * _wait_for_commands_to_complete - reset controller + * @ioc: Pointer to MPT_ADAPTER structure + * @sleep_flag: CAN_SLEEP or NO_SLEEP + * + * This function waiting(3s) for all pending commands to complete + * prior to putting controller in reset. + */ +static void +_wait_for_commands_to_complete(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) +{ + u32 ioc_state; + unsigned long flags; + u16 i; + + ioc->pending_io_count = 0; + if (sleep_flag != CAN_SLEEP) + return; + + ioc_state = mpt2sas_base_get_iocstate(ioc, 0); + if ((ioc_state & MPI2_IOC_STATE_MASK) != MPI2_IOC_STATE_OPERATIONAL) + return; + + /* pending command count */ + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + for (i = 0; i < ioc->request_depth; i++) + if (ioc->scsi_lookup[i].cb_idx != 0xFF) + ioc->pending_io_count++; + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + + if (!ioc->pending_io_count) + return; + + /* wait for pending commands to complete */ + wait_event_timeout(ioc->reset_wq, ioc->pending_io_count == 0, 3 * HZ); +} + +/** + * mpt2sas_base_hard_reset_handler - reset controller + * @ioc: Pointer to MPT_ADAPTER structure + * @sleep_flag: CAN_SLEEP or NO_SLEEP + * @type: FORCE_BIG_HAMMER or SOFT_RESET + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, + enum reset_type type) +{ + int r, i; + unsigned long flags; + + dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name, + __func__)); + + spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); + if (ioc->ioc_reset_in_progress) { + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); + printk(MPT2SAS_ERR_FMT "%s: busy\n", + ioc->name, __func__); + return -EBUSY; + } + ioc->ioc_reset_in_progress = 1; + ioc->shost_recovery = 1; + if (ioc->shost->shost_state == SHOST_RUNNING) { + /* set back to SHOST_RUNNING in mpt2sas_scsih.c */ + scsi_host_set_state(ioc->shost, SHOST_RECOVERY); + printk(MPT2SAS_INFO_FMT "putting controller into " + "SHOST_RECOVERY\n", ioc->name); + } + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); + + _base_reset_handler(ioc, MPT2_IOC_PRE_RESET); + _wait_for_commands_to_complete(ioc, sleep_flag); + _base_mask_interrupts(ioc); + r = _base_make_ioc_ready(ioc, sleep_flag, type); + if (r) + goto out; + _base_reset_handler(ioc, MPT2_IOC_AFTER_RESET); + for (i = 0 ; i < ioc->facts.NumberOfPorts; i++) + r = _base_make_ioc_operational(ioc, ioc->pfacts[i].VF_ID, + sleep_flag); + if (!r) + _base_reset_handler(ioc, MPT2_IOC_DONE_RESET); + out: + dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: %s\n", + ioc->name, __func__, ((r == 0) ? "SUCCESS" : "FAILED"))); + + spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); + ioc->ioc_reset_in_progress = 0; + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); + return r; +} diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h new file mode 100644 index 000000000000..11fc17f218c8 --- /dev/null +++ b/drivers/scsi/mpt2sas/mpt2sas_base.h @@ -0,0 +1,779 @@ +/* + * This is the Fusion MPT base driver providing common API layer interface + * for access to MPT (Message Passing Technology) firmware. + * + * This code is based on drivers/scsi/mpt2sas/mpt2_base.h + * Copyright (C) 2007-2008 LSI Corporation + * (mailto:DL-MPTFusionLinux@lsi.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. + * + * 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. + * + * NO WARRANTY + * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + * solely responsible for determining the appropriateness of using and + * distributing the Program and assumes all risks associated with its + * exercise of rights under this Agreement, including but not limited to + * the risks and costs of program errors, damage to or loss of data, + * programs or equipment, and unavailability or interruption of operations. + + * DISCLAIMER OF LIABILITY + * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#ifndef MPT2SAS_BASE_H_INCLUDED +#define MPT2SAS_BASE_H_INCLUDED + +#include "mpi/mpi2_type.h" +#include "mpi/mpi2.h" +#include "mpi/mpi2_ioc.h" +#include "mpi/mpi2_cnfg.h" +#include "mpi/mpi2_init.h" +#include "mpi/mpi2_raid.h" +#include "mpi/mpi2_tool.h" +#include "mpi/mpi2_sas.h" + +#include <scsi/scsi.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_tcq.h> +#include <scsi/scsi_transport_sas.h> +#include <scsi/scsi_dbg.h> + +#include "mpt2sas_debug.h" + +/* driver versioning info */ +#define MPT2SAS_DRIVER_NAME "mpt2sas" +#define MPT2SAS_AUTHOR "LSI Corporation <DL-MPTFusionLinux@lsi.com>" +#define MPT2SAS_DESCRIPTION "LSI MPT Fusion SAS 2.0 Device Driver" +#define MPT2SAS_DRIVER_VERSION "00.100.11.15" +#define MPT2SAS_MAJOR_VERSION 00 +#define MPT2SAS_MINOR_VERSION 100 +#define MPT2SAS_BUILD_VERSION 11 +#define MPT2SAS_RELEASE_VERSION 15 + +/* + * Set MPT2SAS_SG_DEPTH value based on user input. + */ +#ifdef CONFIG_SCSI_MPT2SAS_MAX_SGE +#if CONFIG_SCSI_MPT2SAS_MAX_SGE < 16 +#define MPT2SAS_SG_DEPTH 16 +#elif CONFIG_SCSI_MPT2SAS_MAX_SGE > 128 +#define MPT2SAS_SG_DEPTH 128 +#else +#define MPT2SAS_SG_DEPTH CONFIG_SCSI_MPT2SAS_MAX_SGE +#endif +#else +#define MPT2SAS_SG_DEPTH 128 /* MAX_HW_SEGMENTS */ +#endif + + +/* + * Generic Defines + */ +#define MPT2SAS_SATA_QUEUE_DEPTH 32 +#define MPT2SAS_SAS_QUEUE_DEPTH 254 +#define MPT2SAS_RAID_QUEUE_DEPTH 128 + +#define MPT_NAME_LENGTH 32 /* generic length of strings */ +#define MPT_STRING_LENGTH 64 + +#define MPT_MAX_CALLBACKS 16 + +#define CAN_SLEEP 1 +#define NO_SLEEP 0 + +#define INTERNAL_CMDS_COUNT 10 /* reserved cmds */ + +#define MPI2_HIM_MASK 0xFFFFFFFF /* mask every bit*/ + +#define MPT2SAS_INVALID_DEVICE_HANDLE 0xFFFF + + +/* + * reset phases + */ +#define MPT2_IOC_PRE_RESET 1 /* prior to host reset */ +#define MPT2_IOC_AFTER_RESET 2 /* just after host reset */ +#define MPT2_IOC_DONE_RESET 3 /* links re-initialized */ + +/* + * logging format + */ +#define MPT2SAS_FMT "%s: " +#define MPT2SAS_DEBUG_FMT KERN_DEBUG MPT2SAS_FMT +#define MPT2SAS_INFO_FMT KERN_INFO MPT2SAS_FMT +#define MPT2SAS_NOTE_FMT KERN_NOTICE MPT2SAS_FMT +#define MPT2SAS_WARN_FMT KERN_WARNING MPT2SAS_FMT +#define MPT2SAS_ERR_FMT KERN_ERR MPT2SAS_FMT + +/* + * per target private data + */ +#define MPT_TARGET_FLAGS_RAID_COMPONENT 0x01 +#define MPT_TARGET_FLAGS_VOLUME 0x02 +#define MPT_TARGET_FLAGS_DELETED 0x04 + +/** + * struct MPT2SAS_TARGET - starget private hostdata + * @starget: starget object + * @sas_address: target sas address + * @handle: device handle + * @num_luns: number luns + * @flags: MPT_TARGET_FLAGS_XXX flags + * @deleted: target flaged for deletion + * @tm_busy: target is busy with TM request. + */ +struct MPT2SAS_TARGET { + struct scsi_target *starget; + u64 sas_address; + u16 handle; + int num_luns; + u32 flags; + u8 deleted; + u8 tm_busy; +}; + +/* + * per device private data + */ +#define MPT_DEVICE_FLAGS_INIT 0x01 +#define MPT_DEVICE_TLR_ON 0x02 + +/** + * struct MPT2SAS_DEVICE - sdev private hostdata + * @sas_target: starget private hostdata + * @lun: lun number + * @flags: MPT_DEVICE_XXX flags + * @configured_lun: lun is configured + * @block: device is in SDEV_BLOCK state + * @tlr_snoop_check: flag used in determining whether to disable TLR + */ +struct MPT2SAS_DEVICE { + struct MPT2SAS_TARGET *sas_target; + unsigned int lun; + u32 flags; + u8 configured_lun; + u8 block; + u8 tlr_snoop_check; +}; + +#define MPT2_CMD_NOT_USED 0x8000 /* free */ +#define MPT2_CMD_COMPLETE 0x0001 /* completed */ +#define MPT2_CMD_PENDING 0x0002 /* pending */ +#define MPT2_CMD_REPLY_VALID 0x0004 /* reply is valid */ +#define MPT2_CMD_RESET 0x0008 /* host reset dropped the command */ + +/** + * struct _internal_cmd - internal commands struct + * @mutex: mutex + * @done: completion + * @reply: reply message pointer + * @status: MPT2_CMD_XXX status + * @smid: system message id + */ +struct _internal_cmd { + struct mutex mutex; + struct completion done; + void *reply; + u16 status; + u16 smid; +}; + +/* + * SAS Topology Structures + */ + +/** + * struct _sas_device - attached device information + * @list: sas device list + * @starget: starget object + * @sas_address: device sas address + * @device_name: retrieved from the SAS IDENTIFY frame. + * @handle: device handle + * @parent_handle: handle to parent device + * @enclosure_handle: enclosure handle + * @enclosure_logical_id: enclosure logical identifier + * @volume_handle: volume handle (valid when hidden raid member) + * @volume_wwid: volume unique identifier + * @device_info: bitfield provides detailed info about the device + * @id: target id + * @channel: target channel + * @slot: number number + * @hidden_raid_component: set to 1 when this is a raid member + * @responding: used in _scsih_sas_device_mark_responding + */ +struct _sas_device { + struct list_head list; + struct scsi_target *starget; + u64 sas_address; + u64 device_name; + u16 handle; + u16 parent_handle; + u16 enclosure_handle; + u64 enclosure_logical_id; + u16 volume_handle; + u64 volume_wwid; + u32 device_info; + int id; + int channel; + u16 slot; + u8 hidden_raid_component; + u8 responding; +}; + +/** + * struct _raid_device - raid volume link list + * @list: sas device list + * @starget: starget object + * @sdev: scsi device struct (volumes are single lun) + * @wwid: unique identifier for the volume + * @handle: device handle + * @id: target id + * @channel: target channel + * @volume_type: the raid level + * @device_info: bitfield provides detailed info about the hidden components + * @num_pds: number of hidden raid components + * @responding: used in _scsih_raid_device_mark_responding + */ +struct _raid_device { + struct list_head list; + struct scsi_target *starget; + struct scsi_device *sdev; + u64 wwid; + u16 handle; + int id; + int channel; + u8 volume_type; + u32 device_info; + u8 num_pds; + u8 responding; +}; + +/** + * struct _boot_device - boot device info + * @is_raid: flag to indicate whether this is volume + * @device: holds pointer for either struct _sas_device or + * struct _raid_device + */ +struct _boot_device { + u8 is_raid; + void *device; +}; + +/** + * struct _sas_port - wide/narrow sas port information + * @port_list: list of ports belonging to expander + * @handle: device handle for this port + * @sas_address: sas address of this port + * @num_phys: number of phys belonging to this port + * @remote_identify: attached device identification + * @rphy: sas transport rphy object + * @port: sas transport wide/narrow port object + * @phy_list: _sas_phy list objects belonging to this port + */ +struct _sas_port { + struct list_head port_list; + u16 handle; + u64 sas_address; + u8 num_phys; + struct sas_identify remote_identify; + struct sas_rphy *rphy; + struct sas_port *port; + struct list_head phy_list; +}; + +/** + * struct _sas_phy - phy information + * @port_siblings: list of phys belonging to a port + * @identify: phy identification + * @remote_identify: attached device identification + * @phy: sas transport phy object + * @phy_id: unique phy id + * @handle: device handle for this phy + * @attached_handle: device handle for attached device + */ +struct _sas_phy { + struct list_head port_siblings; + struct sas_identify identify; + struct sas_identify remote_identify; + struct sas_phy *phy; + u8 phy_id; + u16 handle; + u16 attached_handle; +}; + +/** + * struct _sas_node - sas_host/expander information + * @list: list of expanders + * @parent_dev: parent device class + * @num_phys: number phys belonging to this sas_host/expander + * @sas_address: sas address of this sas_host/expander + * @handle: handle for this sas_host/expander + * @parent_handle: parent handle + * @enclosure_handle: handle for this a member of an enclosure + * @device_info: bitwise defining capabilities of this sas_host/expander + * @responding: used in _scsih_expander_device_mark_responding + * @phy: a list of phys that make up this sas_host/expander + * @sas_port_list: list of ports attached to this sas_host/expander + */ +struct _sas_node { + struct list_head list; + struct device *parent_dev; + u8 num_phys; + u64 sas_address; + u16 handle; + u16 parent_handle; + u16 enclosure_handle; + u64 enclosure_logical_id; + u8 responding; + struct _sas_phy *phy; + struct list_head sas_port_list; +}; + +/** + * enum reset_type - reset state + * @FORCE_BIG_HAMMER: issue diagnostic reset + * @SOFT_RESET: issue message_unit_reset, if fails to to big hammer + */ +enum reset_type { + FORCE_BIG_HAMMER, + SOFT_RESET, +}; + +/** + * struct request_tracker - firmware request tracker + * @smid: system message id + * @scmd: scsi request pointer + * @cb_idx: callback index + * @chain_list: list of chains associated to this IO + * @tracker_list: list of free request (ioc->free_list) + */ +struct request_tracker { + u16 smid; + struct scsi_cmnd *scmd; + u8 cb_idx; + struct list_head tracker_list; +}; + +typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr); + +/** + * struct MPT2SAS_ADAPTER - per adapter struct + * @list: ioc_list + * @shost: shost object + * @id: unique adapter id + * @pci_irq: irq number + * @name: generic ioc string + * @tmp_string: tmp string used for logging + * @pdev: pci pdev object + * @chip: memory mapped register space + * @chip_phys: physical addrss prior to mapping + * @pio_chip: I/O mapped register space + * @logging_level: see mpt2sas_debug.h + * @ir_firmware: IR firmware present + * @bars: bitmask of BAR's that must be configured + * @mask_interrupts: ignore interrupt + * @fault_reset_work_q_name: fw fault work queue + * @fault_reset_work_q: "" + * @fault_reset_work: "" + * @firmware_event_name: fw event work queue + * @firmware_event_thread: "" + * @fw_events_off: flag to turn off fw event handling + * @fw_event_lock: + * @fw_event_list: list of fw events + * @aen_event_read_flag: event log was read + * @broadcast_aen_busy: broadcast aen waiting to be serviced + * @ioc_reset_in_progress: host reset in progress + * @ioc_reset_in_progress_lock: + * @ioc_link_reset_in_progress: phy/hard reset in progress + * @ignore_loginfos: ignore loginfos during task managment + * @remove_host: flag for when driver unloads, to avoid sending dev resets + * @wait_for_port_enable_to_complete: + * @msix_enable: flag indicating msix is enabled + * @msix_vector_count: number msix vectors + * @msix_table: virt address to the msix table + * @msix_table_backup: backup msix table + * @scsi_io_cb_idx: shost generated commands + * @tm_cb_idx: task management commands + * @transport_cb_idx: transport internal commands + * @ctl_cb_idx: clt internal commands + * @base_cb_idx: base internal commands + * @config_cb_idx: base internal commands + * @base_cmds: + * @transport_cmds: + * @tm_cmds: + * @ctl_cmds: + * @config_cmds: + * @base_add_sg_single: handler for either 32/64 bit sgl's + * @event_type: bits indicating which events to log + * @event_context: unique id for each logged event + * @event_log: event log pointer + * @event_masks: events that are masked + * @facts: static facts data + * @pfacts: static port facts data + * @manu_pg0: static manufacturing page 0 + * @bios_pg2: static bios page 2 + * @bios_pg3: static bios page 3 + * @ioc_pg8: static ioc page 8 + * @iounit_pg0: static iounit page 0 + * @iounit_pg1: static iounit page 1 + * @sas_hba: sas host object + * @sas_expander_list: expander object list + * @sas_node_lock: + * @sas_device_list: sas device object list + * @sas_device_init_list: sas device object list (used only at init time) + * @sas_device_lock: + * @io_missing_delay: time for IO completed by fw when PDR enabled + * @device_missing_delay: time for device missing by fw when PDR enabled + * @config_page_sz: config page size + * @config_page: reserve memory for config page payload + * @config_page_dma: + * @sge_size: sg element size for either 32/64 bit + * @request_depth: hba request queue depth + * @request_sz: per request frame size + * @request: pool of request frames + * @request_dma: + * @request_dma_sz: + * @scsi_lookup: firmware request tracker list + * @scsi_lookup_lock: + * @free_list: free list of request + * @chain: pool of chains + * @pending_io_count: + * @reset_wq: + * @chain_dma: + * @max_sges_in_main_message: number sg elements in main message + * @max_sges_in_chain_message: number sg elements per chain + * @chains_needed_per_io: max chains per io + * @chain_offset_value_for_main_message: location 1st sg in main + * @chain_depth: total chains allocated + * @sense: pool of sense + * @sense_dma: + * @sense_dma_pool: + * @reply_depth: hba reply queue depth: + * @reply_sz: per reply frame size: + * @reply: pool of replys: + * @reply_dma: + * @reply_dma_pool: + * @reply_free_queue_depth: reply free depth + * @reply_free: pool for reply free queue (32 bit addr) + * @reply_free_dma: + * @reply_free_dma_pool: + * @reply_free_host_index: tail index in pool to insert free replys + * @reply_post_queue_depth: reply post queue depth + * @reply_post_free: pool for reply post (64bit descriptor) + * @reply_post_free_dma: + * @reply_post_free_dma_pool: + * @reply_post_host_index: head index in the pool where FW completes IO + */ +struct MPT2SAS_ADAPTER { + struct list_head list; + struct Scsi_Host *shost; + u8 id; + u32 pci_irq; + char name[MPT_NAME_LENGTH]; + char tmp_string[MPT_STRING_LENGTH]; + struct pci_dev *pdev; + Mpi2SystemInterfaceRegs_t __iomem *chip; + unsigned long chip_phys; + unsigned long pio_chip; + int logging_level; + u8 ir_firmware; + int bars; + u8 mask_interrupts; + + /* fw fault handler */ + char fault_reset_work_q_name[20]; + struct workqueue_struct *fault_reset_work_q; + struct delayed_work fault_reset_work; + + /* fw event handler */ + char firmware_event_name[20]; + struct workqueue_struct *firmware_event_thread; + u8 fw_events_off; + spinlock_t fw_event_lock; + struct list_head fw_event_list; + + /* misc flags */ + int aen_event_read_flag; + u8 broadcast_aen_busy; + u8 ioc_reset_in_progress; + u8 shost_recovery; + spinlock_t ioc_reset_in_progress_lock; + u8 ioc_link_reset_in_progress; + u8 ignore_loginfos; + u8 remove_host; + u8 wait_for_port_enable_to_complete; + + u8 msix_enable; + u16 msix_vector_count; + u32 *msix_table; + u32 *msix_table_backup; + + /* internal commands, callback index */ + u8 scsi_io_cb_idx; + u8 tm_cb_idx; + u8 transport_cb_idx; + u8 ctl_cb_idx; + u8 base_cb_idx; + u8 config_cb_idx; + struct _internal_cmd base_cmds; + struct _internal_cmd transport_cmds; + struct _internal_cmd tm_cmds; + struct _internal_cmd ctl_cmds; + struct _internal_cmd config_cmds; + + MPT_ADD_SGE base_add_sg_single; + + /* event log */ + u32 event_type[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS]; + u32 event_context; + void *event_log; + u32 event_masks[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS]; + + /* static config pages */ + Mpi2IOCFactsReply_t facts; + Mpi2PortFactsReply_t *pfacts; + Mpi2ManufacturingPage0_t manu_pg0; + Mpi2BiosPage2_t bios_pg2; + Mpi2BiosPage3_t bios_pg3; + Mpi2IOCPage8_t ioc_pg8; + Mpi2IOUnitPage0_t iounit_pg0; + Mpi2IOUnitPage1_t iounit_pg1; + + struct _boot_device req_boot_device; + struct _boot_device req_alt_boot_device; + struct _boot_device current_boot_device; + + /* sas hba, expander, and device list */ + struct _sas_node sas_hba; + struct list_head sas_expander_list; + spinlock_t sas_node_lock; + struct list_head sas_device_list; + struct list_head sas_device_init_list; + spinlock_t sas_device_lock; + struct list_head raid_device_list; + spinlock_t raid_device_lock; + u8 io_missing_delay; + u16 device_missing_delay; + int sas_id; + + /* config page */ + u16 config_page_sz; + void *config_page; + dma_addr_t config_page_dma; + + /* request */ + u16 sge_size; + u16 request_depth; + u16 request_sz; + u8 *request; + dma_addr_t request_dma; + u32 request_dma_sz; + struct request_tracker *scsi_lookup; + spinlock_t scsi_lookup_lock; + struct list_head free_list; + int pending_io_count; + wait_queue_head_t reset_wq; + + /* chain */ + u8 *chain; + dma_addr_t chain_dma; + u16 max_sges_in_main_message; + u16 max_sges_in_chain_message; + u16 chains_needed_per_io; + u16 chain_offset_value_for_main_message; + u16 chain_depth; + + /* sense */ + u8 *sense; + dma_addr_t sense_dma; + struct dma_pool *sense_dma_pool; + + /* reply */ + u16 reply_sz; + u8 *reply; + dma_addr_t reply_dma; + struct dma_pool *reply_dma_pool; + + /* reply free queue */ + u16 reply_free_queue_depth; + u32 *reply_free; + dma_addr_t reply_free_dma; + struct dma_pool *reply_free_dma_pool; + u32 reply_free_host_index; + + /* reply post queue */ + u16 reply_post_queue_depth; + Mpi2ReplyDescriptorsUnion_t *reply_post_free; + dma_addr_t reply_post_free_dma; + struct dma_pool *reply_post_free_dma_pool; + u32 reply_post_host_index; + + /* diag buffer support */ + u8 *diag_buffer[MPI2_DIAG_BUF_TYPE_COUNT]; + u32 diag_buffer_sz[MPI2_DIAG_BUF_TYPE_COUNT]; + dma_addr_t diag_buffer_dma[MPI2_DIAG_BUF_TYPE_COUNT]; + u8 diag_buffer_status[MPI2_DIAG_BUF_TYPE_COUNT]; + u32 unique_id[MPI2_DIAG_BUF_TYPE_COUNT]; + u32 product_specific[MPI2_DIAG_BUF_TYPE_COUNT][23]; + u32 diagnostic_flags[MPI2_DIAG_BUF_TYPE_COUNT]; +}; + +typedef void (*MPT_CALLBACK)(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, + u32 reply); + + +/* base shared API */ +extern struct list_head ioc_list; + +int mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc); +void mpt2sas_base_detach(struct MPT2SAS_ADAPTER *ioc); +int mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc); +void mpt2sas_base_free_resources(struct MPT2SAS_ADAPTER *ioc); +int mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, + enum reset_type type); + +void *mpt2sas_base_get_msg_frame(struct MPT2SAS_ADAPTER *ioc, u16 smid); +void *mpt2sas_base_get_sense_buffer(struct MPT2SAS_ADAPTER *ioc, u16 smid); +void mpt2sas_base_build_zero_len_sge(struct MPT2SAS_ADAPTER *ioc, void *paddr); +dma_addr_t mpt2sas_base_get_msg_frame_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid); +dma_addr_t mpt2sas_base_get_sense_buffer_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid); + +u16 mpt2sas_base_get_smid(struct MPT2SAS_ADAPTER *ioc, u8 cb_idx); +void mpt2sas_base_free_smid(struct MPT2SAS_ADAPTER *ioc, u16 smid); +void mpt2sas_base_put_smid_scsi_io(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 vf_id, + u16 handle); +void mpt2sas_base_put_smid_hi_priority(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 vf_id); +void mpt2sas_base_put_smid_target_assist(struct MPT2SAS_ADAPTER *ioc, u16 smid, + u8 vf_id, u16 io_index); +void mpt2sas_base_put_smid_default(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 vf_id); +void mpt2sas_base_initialize_callback_handler(void); +u8 mpt2sas_base_register_callback_handler(MPT_CALLBACK cb_func); +void mpt2sas_base_release_callback_handler(u8 cb_idx); + +void mpt2sas_base_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply); +void *mpt2sas_base_get_reply_virt_addr(struct MPT2SAS_ADAPTER *ioc, u32 phys_addr); + +u32 mpt2sas_base_get_iocstate(struct MPT2SAS_ADAPTER *ioc, int cooked); + +void mpt2sas_base_fault_info(struct MPT2SAS_ADAPTER *ioc , u16 fault_code); +int mpt2sas_base_sas_iounit_control(struct MPT2SAS_ADAPTER *ioc, + Mpi2SasIoUnitControlReply_t *mpi_reply, Mpi2SasIoUnitControlRequest_t + *mpi_request); +int mpt2sas_base_scsi_enclosure_processor(struct MPT2SAS_ADAPTER *ioc, + Mpi2SepReply_t *mpi_reply, Mpi2SepRequest_t *mpi_request); +void mpt2sas_base_validate_event_type(struct MPT2SAS_ADAPTER *ioc, u32 *event_type); + +/* scsih shared API */ +void mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, + u8 type, u16 smid_task, ulong timeout); +void mpt2sas_scsih_set_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle); +void mpt2sas_scsih_clear_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle); +struct _sas_node *mpt2sas_scsih_expander_find_by_handle(struct MPT2SAS_ADAPTER *ioc, + u16 handle); +struct _sas_node *mpt2sas_scsih_expander_find_by_sas_address(struct MPT2SAS_ADAPTER + *ioc, u64 sas_address); +struct _sas_device *mpt2sas_scsih_sas_device_find_by_sas_address( + struct MPT2SAS_ADAPTER *ioc, u64 sas_address); + +void mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply); +void mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase); + +/* config shared API */ +void mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply); +int mpt2sas_config_get_number_hba_phys(struct MPT2SAS_ADAPTER *ioc, u8 *num_phys); +int mpt2sas_config_get_manufacturing_pg0(struct MPT2SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage0_t *config_page); +int mpt2sas_config_get_bios_pg2(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2BiosPage2_t *config_page); +int mpt2sas_config_get_bios_pg3(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2BiosPage3_t *config_page); +int mpt2sas_config_get_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2IOUnitPage0_t *config_page); +int mpt2sas_config_get_sas_device_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2SasDevicePage0_t *config_page, u32 form, u32 handle); +int mpt2sas_config_get_sas_device_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2SasDevicePage1_t *config_page, u32 form, u32 handle); +int mpt2sas_config_get_sas_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2SasIOUnitPage0_t *config_page, u16 sz); +int mpt2sas_config_get_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2IOUnitPage1_t *config_page); +int mpt2sas_config_set_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2IOUnitPage1_t config_page); +int mpt2sas_config_get_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, u16 sz); +int mpt2sas_config_get_ioc_pg8(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2IOCPage8_t *config_page); +int mpt2sas_config_get_expander_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2ExpanderPage0_t *config_page, u32 form, u32 handle); +int mpt2sas_config_get_expander_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2ExpanderPage1_t *config_page, u32 phy_number, u16 handle); +int mpt2sas_config_get_enclosure_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2SasEnclosurePage0_t *config_page, u32 form, u32 handle); +int mpt2sas_config_get_phy_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2SasPhyPage0_t *config_page, u32 phy_number); +int mpt2sas_config_get_phy_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2SasPhyPage1_t *config_page, u32 phy_number); +int mpt2sas_config_get_raid_volume_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2RaidVolPage1_t *config_page, u32 form, u32 handle); +int mpt2sas_config_get_number_pds(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 *num_pds); +int mpt2sas_config_get_raid_volume_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2RaidVolPage0_t *config_page, u32 form, u32 handle, u16 sz); +int mpt2sas_config_get_phys_disk_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2RaidPhysDiskPage0_t *config_page, u32 form, + u32 form_specific); +int mpt2sas_config_get_volume_handle(struct MPT2SAS_ADAPTER *ioc, u16 pd_handle, + u16 *volume_handle); +int mpt2sas_config_get_volume_wwid(struct MPT2SAS_ADAPTER *ioc, u16 volume_handle, + u64 *wwid); + +/* ctl shared API */ +extern struct device_attribute *mpt2sas_host_attrs[]; +extern struct device_attribute *mpt2sas_dev_attrs[]; +void mpt2sas_ctl_init(void); +void mpt2sas_ctl_exit(void); +void mpt2sas_ctl_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply); +void mpt2sas_ctl_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase); +void mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply); +void mpt2sas_ctl_add_to_event_log(struct MPT2SAS_ADAPTER *ioc, + Mpi2EventNotificationReply_t *mpi_reply); + +/* transport shared API */ +void mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply); +struct _sas_port *mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, + u16 handle, u16 parent_handle); +void mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, + u16 parent_handle); +int mpt2sas_transport_add_host_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy + *mpt2sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev); +int mpt2sas_transport_add_expander_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy + *mpt2sas_phy, Mpi2ExpanderPage1_t expander_pg1, struct device *parent_dev); +void mpt2sas_transport_update_phy_link_change(struct MPT2SAS_ADAPTER *ioc, u16 handle, + u16 attached_handle, u8 phy_number, u8 link_rate); +extern struct sas_function_template mpt2sas_transport_functions; +extern struct scsi_transport_template *mpt2sas_transport_template; + +#endif /* MPT2SAS_BASE_H_INCLUDED */ diff --git a/drivers/scsi/mpt2sas/mpt2sas_config.c b/drivers/scsi/mpt2sas/mpt2sas_config.c new file mode 100644 index 000000000000..58cfb97846f7 --- /dev/null +++ b/drivers/scsi/mpt2sas/mpt2sas_config.c @@ -0,0 +1,1873 @@ +/* + * This module provides common API for accessing firmware configuration pages + * + * This code is based on drivers/scsi/mpt2sas/mpt2_base.c + * Copyright (C) 2007-2008 LSI Corporation + * (mailto:DL-MPTFusionLinux@lsi.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. + * + * 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. + * + * NO WARRANTY + * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + * solely responsible for determining the appropriateness of using and + * distributing the Program and assumes all risks associated with its + * exercise of rights under this Agreement, including but not limited to + * the risks and costs of program errors, damage to or loss of data, + * programs or equipment, and unavailability or interruption of operations. + + * DISCLAIMER OF LIABILITY + * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/blkdev.h> +#include <linux/sched.h> +#include <linux/workqueue.h> +#include <linux/delay.h> +#include <linux/pci.h> + +#include "mpt2sas_base.h" + +/* local definitions */ + +/* Timeout for config page request (in seconds) */ +#define MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT 15 + +/* Common sgl flags for READING a config page. */ +#define MPT2_CONFIG_COMMON_SGLFLAGS ((MPI2_SGE_FLAGS_SIMPLE_ELEMENT | \ + MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER \ + | MPI2_SGE_FLAGS_END_OF_LIST) << MPI2_SGE_FLAGS_SHIFT) + +/* Common sgl flags for WRITING a config page. */ +#define MPT2_CONFIG_COMMON_WRITE_SGLFLAGS ((MPI2_SGE_FLAGS_SIMPLE_ELEMENT | \ + MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER \ + | MPI2_SGE_FLAGS_END_OF_LIST | MPI2_SGE_FLAGS_HOST_TO_IOC) \ + << MPI2_SGE_FLAGS_SHIFT) + +/** + * struct config_request - obtain dma memory via routine + * @config_page_sz: size + * @config_page: virt pointer + * @config_page_dma: phys pointer + * + */ +struct config_request{ + u16 config_page_sz; + void *config_page; + dma_addr_t config_page_dma; +}; + +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING +/** + * _config_display_some_debug - debug routine + * @ioc: per adapter object + * @smid: system request message index + * @calling_function_name: string pass from calling function + * @mpi_reply: reply message frame + * Context: none. + * + * Function for displaying debug info helpfull when debugging issues + * in this module. + */ +static void +_config_display_some_debug(struct MPT2SAS_ADAPTER *ioc, u16 smid, + char *calling_function_name, MPI2DefaultReply_t *mpi_reply) +{ + Mpi2ConfigRequest_t *mpi_request; + char *desc = NULL; + + if (!(ioc->logging_level & MPT_DEBUG_CONFIG)) + return; + + mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); + switch (mpi_request->Header.PageType & MPI2_CONFIG_PAGETYPE_MASK) { + case MPI2_CONFIG_PAGETYPE_IO_UNIT: + desc = "io_unit"; + break; + case MPI2_CONFIG_PAGETYPE_IOC: + desc = "ioc"; + break; + case MPI2_CONFIG_PAGETYPE_BIOS: + desc = "bios"; + break; + case MPI2_CONFIG_PAGETYPE_RAID_VOLUME: + desc = "raid_volume"; + break; + case MPI2_CONFIG_PAGETYPE_MANUFACTURING: + desc = "manufaucturing"; + break; + case MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK: + desc = "physdisk"; + break; + case MPI2_CONFIG_PAGETYPE_EXTENDED: + switch (mpi_request->ExtPageType) { + case MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT: + desc = "sas_io_unit"; + break; + case MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER: + desc = "sas_expander"; + break; + case MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE: + desc = "sas_device"; + break; + case MPI2_CONFIG_EXTPAGETYPE_SAS_PHY: + desc = "sas_phy"; + break; + case MPI2_CONFIG_EXTPAGETYPE_LOG: + desc = "log"; + break; + case MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE: + desc = "enclosure"; + break; + case MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG: + desc = "raid_config"; + break; + case MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING: + desc = "driver_mappping"; + break; + } + break; + } + + if (!desc) + return; + + printk(MPT2SAS_DEBUG_FMT "%s: %s(%d), action(%d), form(0x%08x), " + "smid(%d)\n", ioc->name, calling_function_name, desc, + mpi_request->Header.PageNumber, mpi_request->Action, + le32_to_cpu(mpi_request->PageAddress), smid); + + if (!mpi_reply) + return; + + if (mpi_reply->IOCStatus || mpi_reply->IOCLogInfo) + printk(MPT2SAS_DEBUG_FMT + "\tiocstatus(0x%04x), loginfo(0x%08x)\n", + ioc->name, le16_to_cpu(mpi_reply->IOCStatus), + le32_to_cpu(mpi_reply->IOCLogInfo)); +} +#endif + +/** + * mpt2sas_config_done - config page completion routine + * @ioc: per adapter object + * @smid: system request message index + * @VF_ID: virtual function id + * @reply: reply message frame(lower 32bit addr) + * Context: none. + * + * The callback handler when using _config_request. + * + * Return nothing. + */ +void +mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply) +{ + MPI2DefaultReply_t *mpi_reply; + + if (ioc->config_cmds.status == MPT2_CMD_NOT_USED) + return; + if (ioc->config_cmds.smid != smid) + return; + ioc->config_cmds.status |= MPT2_CMD_COMPLETE; + mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); + if (mpi_reply) { + ioc->config_cmds.status |= MPT2_CMD_REPLY_VALID; + memcpy(ioc->config_cmds.reply, mpi_reply, + mpi_reply->MsgLength*4); + } + ioc->config_cmds.status &= ~MPT2_CMD_PENDING; +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING + _config_display_some_debug(ioc, smid, "config_done", mpi_reply); +#endif + complete(&ioc->config_cmds.done); +} + +/** + * _config_request - main routine for sending config page requests + * @ioc: per adapter object + * @mpi_request: request message frame + * @mpi_reply: reply mf payload returned from firmware + * @timeout: timeout in seconds + * Context: sleep, the calling function needs to acquire the config_cmds.mutex + * + * A generic API for config page requests to firmware. + * + * The ioc->config_cmds.status flag should be MPT2_CMD_NOT_USED before calling + * this API. + * + * The callback index is set inside `ioc->config_cb_idx. + * + * Returns 0 for success, non-zero for failure. + */ +static int +_config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t + *mpi_request, Mpi2ConfigReply_t *mpi_reply, int timeout) +{ + u16 smid; + u32 ioc_state; + unsigned long timeleft; + Mpi2ConfigRequest_t *config_request; + int r; + u8 retry_count; + u8 issue_reset; + u16 wait_state_count; + + if (ioc->config_cmds.status != MPT2_CMD_NOT_USED) { + printk(MPT2SAS_ERR_FMT "%s: config_cmd in use\n", + ioc->name, __func__); + return -EAGAIN; + } + retry_count = 0; + + retry_config: + wait_state_count = 0; + ioc_state = mpt2sas_base_get_iocstate(ioc, 1); + while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { + if (wait_state_count++ == MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT) { + printk(MPT2SAS_ERR_FMT + "%s: failed due to ioc not operational\n", + ioc->name, __func__); + ioc->config_cmds.status = MPT2_CMD_NOT_USED; + return -EFAULT; + } + ssleep(1); + ioc_state = mpt2sas_base_get_iocstate(ioc, 1); + printk(MPT2SAS_INFO_FMT "%s: waiting for " + "operational state(count=%d)\n", ioc->name, + __func__, wait_state_count); + } + if (wait_state_count) + printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n", + ioc->name, __func__); + + smid = mpt2sas_base_get_smid(ioc, ioc->config_cb_idx); + if (!smid) { + printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", + ioc->name, __func__); + ioc->config_cmds.status = MPT2_CMD_NOT_USED; + return -EAGAIN; + } + + r = 0; + memset(mpi_reply, 0, sizeof(Mpi2ConfigReply_t)); + ioc->config_cmds.status = MPT2_CMD_PENDING; + config_request = mpt2sas_base_get_msg_frame(ioc, smid); + ioc->config_cmds.smid = smid; + memcpy(config_request, mpi_request, sizeof(Mpi2ConfigRequest_t)); +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING + _config_display_some_debug(ioc, smid, "config_request", NULL); +#endif + mpt2sas_base_put_smid_default(ioc, smid, config_request->VF_ID); + timeleft = wait_for_completion_timeout(&ioc->config_cmds.done, + timeout*HZ); + if (!(ioc->config_cmds.status & MPT2_CMD_COMPLETE)) { + printk(MPT2SAS_ERR_FMT "%s: timeout\n", + ioc->name, __func__); + _debug_dump_mf(mpi_request, + sizeof(Mpi2ConfigRequest_t)/4); + if (!(ioc->config_cmds.status & MPT2_CMD_RESET)) + issue_reset = 1; + goto issue_host_reset; + } + if (ioc->config_cmds.status & MPT2_CMD_REPLY_VALID) + memcpy(mpi_reply, ioc->config_cmds.reply, + sizeof(Mpi2ConfigReply_t)); + if (retry_count) + printk(MPT2SAS_INFO_FMT "%s: retry completed!!\n", + ioc->name, __func__); + ioc->config_cmds.status = MPT2_CMD_NOT_USED; + return r; + + issue_host_reset: + if (issue_reset) + mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + ioc->config_cmds.status = MPT2_CMD_NOT_USED; + if (!retry_count) { + printk(MPT2SAS_INFO_FMT "%s: attempting retry\n", + ioc->name, __func__); + retry_count++; + goto retry_config; + } + return -EFAULT; +} + +/** + * _config_alloc_config_dma_memory - obtain physical memory + * @ioc: per adapter object + * @mem: struct config_request + * + * A wrapper for obtaining dma-able memory for config page request. + * + * Returns 0 for success, non-zero for failure. + */ +static int +_config_alloc_config_dma_memory(struct MPT2SAS_ADAPTER *ioc, + struct config_request *mem) +{ + int r = 0; + + mem->config_page = pci_alloc_consistent(ioc->pdev, mem->config_page_sz, + &mem->config_page_dma); + if (!mem->config_page) + r = -ENOMEM; + return r; +} + +/** + * _config_free_config_dma_memory - wrapper to free the memory + * @ioc: per adapter object + * @mem: struct config_request + * + * A wrapper to free dma-able memory when using _config_alloc_config_dma_memory. + * + * Returns 0 for success, non-zero for failure. + */ +static void +_config_free_config_dma_memory(struct MPT2SAS_ADAPTER *ioc, + struct config_request *mem) +{ + pci_free_consistent(ioc->pdev, mem->config_page_sz, mem->config_page, + mem->config_page_dma); +} + +/** + * mpt2sas_config_get_manufacturing_pg0 - obtain manufacturing page 0 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_get_manufacturing_pg0(struct MPT2SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage0_t *config_page) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + struct config_request mem; + + mutex_lock(&ioc->config_cmds.mutex); + memset(config_page, 0, sizeof(Mpi2ManufacturingPage0_t)); + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING; + mpi_request.Header.PageNumber = 0; + mpi_request.Header.PageVersion = MPI2_MANUFACTURING0_PAGEVERSION; + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion; + mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber; + mpi_request.Header.PageType = mpi_reply->Header.PageType; + mpi_request.Header.PageLength = mpi_reply->Header.PageLength; + mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4; + if (mem.config_page_sz > ioc->config_page_sz) { + r = _config_alloc_config_dma_memory(ioc, &mem); + if (r) + goto out; + } else { + mem.config_page_dma = ioc->config_page_dma; + mem.config_page = ioc->config_page; + } + ioc->base_add_sg_single(&mpi_request.PageBufferSGE, + MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz, + mem.config_page_dma); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (!r) + memcpy(config_page, mem.config_page, + min_t(u16, mem.config_page_sz, + sizeof(Mpi2ManufacturingPage0_t))); + + if (mem.config_page_sz > ioc->config_page_sz) + _config_free_config_dma_memory(ioc, &mem); + + out: + mutex_unlock(&ioc->config_cmds.mutex); + return r; +} + +/** + * mpt2sas_config_get_bios_pg2 - obtain bios page 2 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_get_bios_pg2(struct MPT2SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi2BiosPage2_t *config_page) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + struct config_request mem; + + mutex_lock(&ioc->config_cmds.mutex); + memset(config_page, 0, sizeof(Mpi2BiosPage2_t)); + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS; + mpi_request.Header.PageNumber = 2; + mpi_request.Header.PageVersion = MPI2_BIOSPAGE2_PAGEVERSION; + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion; + mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber; + mpi_request.Header.PageType = mpi_reply->Header.PageType; + mpi_request.Header.PageLength = mpi_reply->Header.PageLength; + mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4; + if (mem.config_page_sz > ioc->config_page_sz) { + r = _config_alloc_config_dma_memory(ioc, &mem); + if (r) + goto out; + } else { + mem.config_page_dma = ioc->config_page_dma; + mem.config_page = ioc->config_page; + } + ioc->base_add_sg_single(&mpi_request.PageBufferSGE, + MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz, + mem.config_page_dma); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (!r) + memcpy(config_page, mem.config_page, + min_t(u16, mem.config_page_sz, + sizeof(Mpi2BiosPage2_t))); + + if (mem.config_page_sz > ioc->config_page_sz) + _config_free_config_dma_memory(ioc, &mem); + + out: + mutex_unlock(&ioc->config_cmds.mutex); + return r; +} + +/** + * mpt2sas_config_get_bios_pg3 - obtain bios page 3 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_get_bios_pg3(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2BiosPage3_t *config_page) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + struct config_request mem; + + mutex_lock(&ioc->config_cmds.mutex); + memset(config_page, 0, sizeof(Mpi2BiosPage3_t)); + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS; + mpi_request.Header.PageNumber = 3; + mpi_request.Header.PageVersion = MPI2_BIOSPAGE3_PAGEVERSION; + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion; + mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber; + mpi_request.Header.PageType = mpi_reply->Header.PageType; + mpi_request.Header.PageLength = mpi_reply->Header.PageLength; + mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4; + if (mem.config_page_sz > ioc->config_page_sz) { + r = _config_alloc_config_dma_memory(ioc, &mem); + if (r) + goto out; + } else { + mem.config_page_dma = ioc->config_page_dma; + mem.config_page = ioc->config_page; + } + ioc->base_add_sg_single(&mpi_request.PageBufferSGE, + MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz, + mem.config_page_dma); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (!r) + memcpy(config_page, mem.config_page, + min_t(u16, mem.config_page_sz, + sizeof(Mpi2BiosPage3_t))); + + if (mem.config_page_sz > ioc->config_page_sz) + _config_free_config_dma_memory(ioc, &mem); + + out: + mutex_unlock(&ioc->config_cmds.mutex); + return r; +} + +/** + * mpt2sas_config_get_iounit_pg0 - obtain iounit page 0 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_get_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage0_t *config_page) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + struct config_request mem; + + mutex_lock(&ioc->config_cmds.mutex); + memset(config_page, 0, sizeof(Mpi2IOUnitPage0_t)); + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT; + mpi_request.Header.PageNumber = 0; + mpi_request.Header.PageVersion = MPI2_IOUNITPAGE0_PAGEVERSION; + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion; + mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber; + mpi_request.Header.PageType = mpi_reply->Header.PageType; + mpi_request.Header.PageLength = mpi_reply->Header.PageLength; + mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4; + if (mem.config_page_sz > ioc->config_page_sz) { + r = _config_alloc_config_dma_memory(ioc, &mem); + if (r) + goto out; + } else { + mem.config_page_dma = ioc->config_page_dma; + mem.config_page = ioc->config_page; + } + ioc->base_add_sg_single(&mpi_request.PageBufferSGE, + MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz, + mem.config_page_dma); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (!r) + memcpy(config_page, mem.config_page, + min_t(u16, mem.config_page_sz, + sizeof(Mpi2IOUnitPage0_t))); + + if (mem.config_page_sz > ioc->config_page_sz) + _config_free_config_dma_memory(ioc, &mem); + + out: + mutex_unlock(&ioc->config_cmds.mutex); + return r; +} + +/** + * mpt2sas_config_get_iounit_pg1 - obtain iounit page 1 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_get_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage1_t *config_page) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + struct config_request mem; + + mutex_lock(&ioc->config_cmds.mutex); + memset(config_page, 0, sizeof(Mpi2IOUnitPage1_t)); + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT; + mpi_request.Header.PageNumber = 1; + mpi_request.Header.PageVersion = MPI2_IOUNITPAGE1_PAGEVERSION; + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion; + mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber; + mpi_request.Header.PageType = mpi_reply->Header.PageType; + mpi_request.Header.PageLength = mpi_reply->Header.PageLength; + mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4; + if (mem.config_page_sz > ioc->config_page_sz) { + r = _config_alloc_config_dma_memory(ioc, &mem); + if (r) + goto out; + } else { + mem.config_page_dma = ioc->config_page_dma; + mem.config_page = ioc->config_page; + } + ioc->base_add_sg_single(&mpi_request.PageBufferSGE, + MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz, + mem.config_page_dma); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (!r) + memcpy(config_page, mem.config_page, + min_t(u16, mem.config_page_sz, + sizeof(Mpi2IOUnitPage1_t))); + + if (mem.config_page_sz > ioc->config_page_sz) + _config_free_config_dma_memory(ioc, &mem); + + out: + mutex_unlock(&ioc->config_cmds.mutex); + return r; +} + +/** + * mpt2sas_config_set_iounit_pg1 - set iounit page 1 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_set_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage1_t config_page) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + struct config_request mem; + + mutex_lock(&ioc->config_cmds.mutex); + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT; + mpi_request.Header.PageNumber = 1; + mpi_request.Header.PageVersion = MPI2_IOUNITPAGE1_PAGEVERSION; + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT; + mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion; + mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber; + mpi_request.Header.PageType = mpi_reply->Header.PageType; + mpi_request.Header.PageLength = mpi_reply->Header.PageLength; + mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4; + if (mem.config_page_sz > ioc->config_page_sz) { + r = _config_alloc_config_dma_memory(ioc, &mem); + if (r) + goto out; + } else { + mem.config_page_dma = ioc->config_page_dma; + mem.config_page = ioc->config_page; + } + + memset(mem.config_page, 0, mem.config_page_sz); + memcpy(mem.config_page, &config_page, + sizeof(Mpi2IOUnitPage1_t)); + + ioc->base_add_sg_single(&mpi_request.PageBufferSGE, + MPT2_CONFIG_COMMON_WRITE_SGLFLAGS | mem.config_page_sz, + mem.config_page_dma); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + + if (mem.config_page_sz > ioc->config_page_sz) + _config_free_config_dma_memory(ioc, &mem); + + out: + mutex_unlock(&ioc->config_cmds.mutex); + return r; +} + +/** + * mpt2sas_config_get_ioc_pg8 - obtain ioc page 8 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_get_ioc_pg8(struct MPT2SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi2IOCPage8_t *config_page) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + struct config_request mem; + + mutex_lock(&ioc->config_cmds.mutex); + memset(config_page, 0, sizeof(Mpi2IOCPage8_t)); + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IOC; + mpi_request.Header.PageNumber = 8; + mpi_request.Header.PageVersion = MPI2_IOCPAGE8_PAGEVERSION; + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion; + mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber; + mpi_request.Header.PageType = mpi_reply->Header.PageType; + mpi_request.Header.PageLength = mpi_reply->Header.PageLength; + mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4; + if (mem.config_page_sz > ioc->config_page_sz) { + r = _config_alloc_config_dma_memory(ioc, &mem); + if (r) + goto out; + } else { + mem.config_page_dma = ioc->config_page_dma; + mem.config_page = ioc->config_page; + } + ioc->base_add_sg_single(&mpi_request.PageBufferSGE, + MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz, + mem.config_page_dma); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (!r) + memcpy(config_page, mem.config_page, + min_t(u16, mem.config_page_sz, + sizeof(Mpi2IOCPage8_t))); + + if (mem.config_page_sz > ioc->config_page_sz) + _config_free_config_dma_memory(ioc, &mem); + + out: + mutex_unlock(&ioc->config_cmds.mutex); + return r; +} + +/** + * mpt2sas_config_get_sas_device_pg0 - obtain sas device page 0 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * @form: GET_NEXT_HANDLE or HANDLE + * @handle: device handle + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_get_sas_device_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2SasDevicePage0_t *config_page, u32 form, u32 handle) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + struct config_request mem; + + mutex_lock(&ioc->config_cmds.mutex); + memset(config_page, 0, sizeof(Mpi2SasDevicePage0_t)); + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE; + mpi_request.Header.PageVersion = MPI2_SASDEVICE0_PAGEVERSION; + mpi_request.Header.PageNumber = 0; + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (r) + goto out; + + mpi_request.PageAddress = cpu_to_le32(form | handle); + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion; + mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber; + mpi_request.Header.PageType = mpi_reply->Header.PageType; + mpi_request.ExtPageLength = mpi_reply->ExtPageLength; + mpi_request.ExtPageType = mpi_reply->ExtPageType; + mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4; + if (mem.config_page_sz > ioc->config_page_sz) { + r = _config_alloc_config_dma_memory(ioc, &mem); + if (r) + goto out; + } else { + mem.config_page_dma = ioc->config_page_dma; + mem.config_page = ioc->config_page; + } + ioc->base_add_sg_single(&mpi_request.PageBufferSGE, + MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz, + mem.config_page_dma); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (!r) + memcpy(config_page, mem.config_page, + min_t(u16, mem.config_page_sz, + sizeof(Mpi2SasDevicePage0_t))); + + if (mem.config_page_sz > ioc->config_page_sz) + _config_free_config_dma_memory(ioc, &mem); + + out: + mutex_unlock(&ioc->config_cmds.mutex); + return r; +} + +/** + * mpt2sas_config_get_sas_device_pg1 - obtain sas device page 1 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * @form: GET_NEXT_HANDLE or HANDLE + * @handle: device handle + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_get_sas_device_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2SasDevicePage1_t *config_page, u32 form, u32 handle) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + struct config_request mem; + + mutex_lock(&ioc->config_cmds.mutex); + memset(config_page, 0, sizeof(Mpi2SasDevicePage1_t)); + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE; + mpi_request.Header.PageVersion = MPI2_SASDEVICE1_PAGEVERSION; + mpi_request.Header.PageNumber = 1; + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (r) + goto out; + + mpi_request.PageAddress = cpu_to_le32(form | handle); + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion; + mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber; + mpi_request.Header.PageType = mpi_reply->Header.PageType; + mpi_request.ExtPageLength = mpi_reply->ExtPageLength; + mpi_request.ExtPageType = mpi_reply->ExtPageType; + mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4; + if (mem.config_page_sz > ioc->config_page_sz) { + r = _config_alloc_config_dma_memory(ioc, &mem); + if (r) + goto out; + } else { + mem.config_page_dma = ioc->config_page_dma; + mem.config_page = ioc->config_page; + } + ioc->base_add_sg_single(&mpi_request.PageBufferSGE, + MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz, + mem.config_page_dma); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (!r) + memcpy(config_page, mem.config_page, + min_t(u16, mem.config_page_sz, + sizeof(Mpi2SasDevicePage1_t))); + + if (mem.config_page_sz > ioc->config_page_sz) + _config_free_config_dma_memory(ioc, &mem); + + out: + mutex_unlock(&ioc->config_cmds.mutex); + return r; +} + +/** + * mpt2sas_config_get_number_hba_phys - obtain number of phys on the host + * @ioc: per adapter object + * @num_phys: pointer returned with the number of phys + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_get_number_hba_phys(struct MPT2SAS_ADAPTER *ioc, u8 *num_phys) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + struct config_request mem; + u16 ioc_status; + Mpi2ConfigReply_t mpi_reply; + Mpi2SasIOUnitPage0_t config_page; + + mutex_lock(&ioc->config_cmds.mutex); + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT; + mpi_request.Header.PageNumber = 0; + mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE0_PAGEVERSION; + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, &mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + mpi_request.Header.PageVersion = mpi_reply.Header.PageVersion; + mpi_request.Header.PageNumber = mpi_reply.Header.PageNumber; + mpi_request.Header.PageType = mpi_reply.Header.PageType; + mpi_request.ExtPageLength = mpi_reply.ExtPageLength; + mpi_request.ExtPageType = mpi_reply.ExtPageType; + mem.config_page_sz = le16_to_cpu(mpi_reply.ExtPageLength) * 4; + if (mem.config_page_sz > ioc->config_page_sz) { + r = _config_alloc_config_dma_memory(ioc, &mem); + if (r) + goto out; + } else { + mem.config_page_dma = ioc->config_page_dma; + mem.config_page = ioc->config_page; + } + ioc->base_add_sg_single(&mpi_request.PageBufferSGE, + MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz, + mem.config_page_dma); + r = _config_request(ioc, &mpi_request, &mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (!r) { + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { + memcpy(&config_page, mem.config_page, + min_t(u16, mem.config_page_sz, + sizeof(Mpi2SasIOUnitPage0_t))); + *num_phys = config_page.NumPhys; + } + } + + if (mem.config_page_sz > ioc->config_page_sz) + _config_free_config_dma_memory(ioc, &mem); + + out: + mutex_unlock(&ioc->config_cmds.mutex); + return r; +} + +/** + * mpt2sas_config_get_sas_iounit_pg0 - obtain sas iounit page 0 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * @sz: size of buffer passed in config_page + * Context: sleep. + * + * Calling function should call config_get_number_hba_phys prior to + * this function, so enough memory is allocated for config_page. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_get_sas_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2SasIOUnitPage0_t *config_page, u16 sz) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + struct config_request mem; + + mutex_lock(&ioc->config_cmds.mutex); + memset(config_page, 0, sz); + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT; + mpi_request.Header.PageNumber = 0; + mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE0_PAGEVERSION; + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion; + mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber; + mpi_request.Header.PageType = mpi_reply->Header.PageType; + mpi_request.ExtPageLength = mpi_reply->ExtPageLength; + mpi_request.ExtPageType = mpi_reply->ExtPageType; + mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4; + if (mem.config_page_sz > ioc->config_page_sz) { + r = _config_alloc_config_dma_memory(ioc, &mem); + if (r) + goto out; + } else { + mem.config_page_dma = ioc->config_page_dma; + mem.config_page = ioc->config_page; + } + ioc->base_add_sg_single(&mpi_request.PageBufferSGE, + MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz, + mem.config_page_dma); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (!r) + memcpy(config_page, mem.config_page, + min_t(u16, sz, mem.config_page_sz)); + + if (mem.config_page_sz > ioc->config_page_sz) + _config_free_config_dma_memory(ioc, &mem); + + out: + mutex_unlock(&ioc->config_cmds.mutex); + return r; +} + +/** + * mpt2sas_config_get_sas_iounit_pg1 - obtain sas iounit page 0 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * @sz: size of buffer passed in config_page + * Context: sleep. + * + * Calling function should call config_get_number_hba_phys prior to + * this function, so enough memory is allocated for config_page. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_get_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2SasIOUnitPage1_t *config_page, u16 sz) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + struct config_request mem; + + mutex_lock(&ioc->config_cmds.mutex); + memset(config_page, 0, sz); + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT; + mpi_request.Header.PageNumber = 1; + mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE0_PAGEVERSION; + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion; + mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber; + mpi_request.Header.PageType = mpi_reply->Header.PageType; + mpi_request.ExtPageLength = mpi_reply->ExtPageLength; + mpi_request.ExtPageType = mpi_reply->ExtPageType; + mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4; + if (mem.config_page_sz > ioc->config_page_sz) { + r = _config_alloc_config_dma_memory(ioc, &mem); + if (r) + goto out; + } else { + mem.config_page_dma = ioc->config_page_dma; + mem.config_page = ioc->config_page; + } + ioc->base_add_sg_single(&mpi_request.PageBufferSGE, + MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz, + mem.config_page_dma); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (!r) + memcpy(config_page, mem.config_page, + min_t(u16, sz, mem.config_page_sz)); + + if (mem.config_page_sz > ioc->config_page_sz) + _config_free_config_dma_memory(ioc, &mem); + + out: + mutex_unlock(&ioc->config_cmds.mutex); + return r; +} + +/** + * mpt2sas_config_get_expander_pg0 - obtain expander page 0 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * @form: GET_NEXT_HANDLE or HANDLE + * @handle: expander handle + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_get_expander_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2ExpanderPage0_t *config_page, u32 form, u32 handle) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + struct config_request mem; + + mutex_lock(&ioc->config_cmds.mutex); + memset(config_page, 0, sizeof(Mpi2ExpanderPage0_t)); + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER; + mpi_request.Header.PageNumber = 0; + mpi_request.Header.PageVersion = MPI2_SASEXPANDER0_PAGEVERSION; + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (r) + goto out; + + mpi_request.PageAddress = cpu_to_le32(form | handle); + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion; + mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber; + mpi_request.Header.PageType = mpi_reply->Header.PageType; + mpi_request.ExtPageLength = mpi_reply->ExtPageLength; + mpi_request.ExtPageType = mpi_reply->ExtPageType; + mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4; + if (mem.config_page_sz > ioc->config_page_sz) { + r = _config_alloc_config_dma_memory(ioc, &mem); + if (r) + goto out; + } else { + mem.config_page_dma = ioc->config_page_dma; + mem.config_page = ioc->config_page; + } + ioc->base_add_sg_single(&mpi_request.PageBufferSGE, + MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz, + mem.config_page_dma); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (!r) + memcpy(config_page, mem.config_page, + min_t(u16, mem.config_page_sz, + sizeof(Mpi2ExpanderPage0_t))); + + if (mem.config_page_sz > ioc->config_page_sz) + _config_free_config_dma_memory(ioc, &mem); + + out: + mutex_unlock(&ioc->config_cmds.mutex); + return r; +} + +/** + * mpt2sas_config_get_expander_pg1 - obtain expander page 1 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * @phy_number: phy number + * @handle: expander handle + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_get_expander_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2ExpanderPage1_t *config_page, u32 phy_number, + u16 handle) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + struct config_request mem; + + mutex_lock(&ioc->config_cmds.mutex); + memset(config_page, 0, sizeof(Mpi2ExpanderPage1_t)); + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER; + mpi_request.Header.PageNumber = 1; + mpi_request.Header.PageVersion = MPI2_SASEXPANDER1_PAGEVERSION; + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (r) + goto out; + + mpi_request.PageAddress = + cpu_to_le32(MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM | + (phy_number << MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT) | handle); + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion; + mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber; + mpi_request.Header.PageType = mpi_reply->Header.PageType; + mpi_request.ExtPageLength = mpi_reply->ExtPageLength; + mpi_request.ExtPageType = mpi_reply->ExtPageType; + mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4; + if (mem.config_page_sz > ioc->config_page_sz) { + r = _config_alloc_config_dma_memory(ioc, &mem); + if (r) + goto out; + } else { + mem.config_page_dma = ioc->config_page_dma; + mem.config_page = ioc->config_page; + } + ioc->base_add_sg_single(&mpi_request.PageBufferSGE, + MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz, + mem.config_page_dma); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (!r) + memcpy(config_page, mem.config_page, + min_t(u16, mem.config_page_sz, + sizeof(Mpi2ExpanderPage1_t))); + + if (mem.config_page_sz > ioc->config_page_sz) + _config_free_config_dma_memory(ioc, &mem); + + out: + mutex_unlock(&ioc->config_cmds.mutex); + return r; +} + +/** + * mpt2sas_config_get_enclosure_pg0 - obtain enclosure page 0 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * @form: GET_NEXT_HANDLE or HANDLE + * @handle: expander handle + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_get_enclosure_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2SasEnclosurePage0_t *config_page, u32 form, u32 handle) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + struct config_request mem; + + mutex_lock(&ioc->config_cmds.mutex); + memset(config_page, 0, sizeof(Mpi2SasEnclosurePage0_t)); + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE; + mpi_request.Header.PageNumber = 0; + mpi_request.Header.PageVersion = MPI2_SASENCLOSURE0_PAGEVERSION; + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (r) + goto out; + + mpi_request.PageAddress = cpu_to_le32(form | handle); + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion; + mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber; + mpi_request.Header.PageType = mpi_reply->Header.PageType; + mpi_request.ExtPageLength = mpi_reply->ExtPageLength; + mpi_request.ExtPageType = mpi_reply->ExtPageType; + mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4; + if (mem.config_page_sz > ioc->config_page_sz) { + r = _config_alloc_config_dma_memory(ioc, &mem); + if (r) + goto out; + } else { + mem.config_page_dma = ioc->config_page_dma; + mem.config_page = ioc->config_page; + } + ioc->base_add_sg_single(&mpi_request.PageBufferSGE, + MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz, + mem.config_page_dma); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (!r) + memcpy(config_page, mem.config_page, + min_t(u16, mem.config_page_sz, + sizeof(Mpi2SasEnclosurePage0_t))); + + if (mem.config_page_sz > ioc->config_page_sz) + _config_free_config_dma_memory(ioc, &mem); + + out: + mutex_unlock(&ioc->config_cmds.mutex); + return r; +} + +/** + * mpt2sas_config_get_phy_pg0 - obtain phy page 0 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * @phy_number: phy number + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_get_phy_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2SasPhyPage0_t *config_page, u32 phy_number) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + struct config_request mem; + + mutex_lock(&ioc->config_cmds.mutex); + memset(config_page, 0, sizeof(Mpi2SasPhyPage0_t)); + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_PHY; + mpi_request.Header.PageNumber = 0; + mpi_request.Header.PageVersion = MPI2_SASPHY0_PAGEVERSION; + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (r) + goto out; + + mpi_request.PageAddress = + cpu_to_le32(MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | phy_number); + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion; + mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber; + mpi_request.Header.PageType = mpi_reply->Header.PageType; + mpi_request.ExtPageLength = mpi_reply->ExtPageLength; + mpi_request.ExtPageType = mpi_reply->ExtPageType; + mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4; + if (mem.config_page_sz > ioc->config_page_sz) { + r = _config_alloc_config_dma_memory(ioc, &mem); + if (r) + goto out; + } else { + mem.config_page_dma = ioc->config_page_dma; + mem.config_page = ioc->config_page; + } + ioc->base_add_sg_single(&mpi_request.PageBufferSGE, + MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz, + mem.config_page_dma); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (!r) + memcpy(config_page, mem.config_page, + min_t(u16, mem.config_page_sz, + sizeof(Mpi2SasPhyPage0_t))); + + if (mem.config_page_sz > ioc->config_page_sz) + _config_free_config_dma_memory(ioc, &mem); + + out: + mutex_unlock(&ioc->config_cmds.mutex); + return r; +} + +/** + * mpt2sas_config_get_phy_pg1 - obtain phy page 1 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * @phy_number: phy number + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_get_phy_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2SasPhyPage1_t *config_page, u32 phy_number) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + struct config_request mem; + + mutex_lock(&ioc->config_cmds.mutex); + memset(config_page, 0, sizeof(Mpi2SasPhyPage1_t)); + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_PHY; + mpi_request.Header.PageNumber = 1; + mpi_request.Header.PageVersion = MPI2_SASPHY1_PAGEVERSION; + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (r) + goto out; + + mpi_request.PageAddress = + cpu_to_le32(MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | phy_number); + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion; + mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber; + mpi_request.Header.PageType = mpi_reply->Header.PageType; + mpi_request.ExtPageLength = mpi_reply->ExtPageLength; + mpi_request.ExtPageType = mpi_reply->ExtPageType; + mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4; + if (mem.config_page_sz > ioc->config_page_sz) { + r = _config_alloc_config_dma_memory(ioc, &mem); + if (r) + goto out; + } else { + mem.config_page_dma = ioc->config_page_dma; + mem.config_page = ioc->config_page; + } + ioc->base_add_sg_single(&mpi_request.PageBufferSGE, + MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz, + mem.config_page_dma); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (!r) + memcpy(config_page, mem.config_page, + min_t(u16, mem.config_page_sz, + sizeof(Mpi2SasPhyPage1_t))); + + if (mem.config_page_sz > ioc->config_page_sz) + _config_free_config_dma_memory(ioc, &mem); + + out: + mutex_unlock(&ioc->config_cmds.mutex); + return r; +} + +/** + * mpt2sas_config_get_raid_volume_pg1 - obtain raid volume page 1 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * @form: GET_NEXT_HANDLE or HANDLE + * @handle: volume handle + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_get_raid_volume_pg1(struct MPT2SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage1_t *config_page, u32 form, + u32 handle) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + struct config_request mem; + + mutex_lock(&ioc->config_cmds.mutex); + memset(config_page, 0, sizeof(Mpi2RaidVolPage1_t)); + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME; + mpi_request.Header.PageNumber = 1; + mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE1_PAGEVERSION; + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (r) + goto out; + + mpi_request.PageAddress = cpu_to_le32(form | handle); + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion; + mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber; + mpi_request.Header.PageType = mpi_reply->Header.PageType; + mpi_request.Header.PageLength = mpi_reply->Header.PageLength; + mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4; + if (mem.config_page_sz > ioc->config_page_sz) { + r = _config_alloc_config_dma_memory(ioc, &mem); + if (r) + goto out; + } else { + mem.config_page_dma = ioc->config_page_dma; + mem.config_page = ioc->config_page; + } + ioc->base_add_sg_single(&mpi_request.PageBufferSGE, + MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz, + mem.config_page_dma); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (!r) + memcpy(config_page, mem.config_page, + min_t(u16, mem.config_page_sz, + sizeof(Mpi2RaidVolPage1_t))); + + if (mem.config_page_sz > ioc->config_page_sz) + _config_free_config_dma_memory(ioc, &mem); + + out: + mutex_unlock(&ioc->config_cmds.mutex); + return r; +} + +/** + * mpt2sas_config_get_number_pds - obtain number of phys disk assigned to volume + * @ioc: per adapter object + * @handle: volume handle + * @num_pds: returns pds count + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_get_number_pds(struct MPT2SAS_ADAPTER *ioc, u16 handle, + u8 *num_pds) +{ + Mpi2ConfigRequest_t mpi_request; + Mpi2RaidVolPage0_t *config_page; + Mpi2ConfigReply_t mpi_reply; + int r; + struct config_request mem; + u16 ioc_status; + + mutex_lock(&ioc->config_cmds.mutex); + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + *num_pds = 0; + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME; + mpi_request.Header.PageNumber = 0; + mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE0_PAGEVERSION; + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, &mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (r) + goto out; + + mpi_request.PageAddress = + cpu_to_le32(MPI2_RAID_VOLUME_PGAD_FORM_HANDLE | handle); + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + mpi_request.Header.PageVersion = mpi_reply.Header.PageVersion; + mpi_request.Header.PageNumber = mpi_reply.Header.PageNumber; + mpi_request.Header.PageType = mpi_reply.Header.PageType; + mpi_request.Header.PageLength = mpi_reply.Header.PageLength; + mem.config_page_sz = le16_to_cpu(mpi_reply.Header.PageLength) * 4; + if (mem.config_page_sz > ioc->config_page_sz) { + r = _config_alloc_config_dma_memory(ioc, &mem); + if (r) + goto out; + } else { + mem.config_page_dma = ioc->config_page_dma; + mem.config_page = ioc->config_page; + } + ioc->base_add_sg_single(&mpi_request.PageBufferSGE, + MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz, + mem.config_page_dma); + r = _config_request(ioc, &mpi_request, &mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (!r) { + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { + config_page = mem.config_page; + *num_pds = config_page->NumPhysDisks; + } + } + + if (mem.config_page_sz > ioc->config_page_sz) + _config_free_config_dma_memory(ioc, &mem); + + out: + mutex_unlock(&ioc->config_cmds.mutex); + return r; +} + +/** + * mpt2sas_config_get_raid_volume_pg0 - obtain raid volume page 0 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * @form: GET_NEXT_HANDLE or HANDLE + * @handle: volume handle + * @sz: size of buffer passed in config_page + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_get_raid_volume_pg0(struct MPT2SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, Mpi2RaidVolPage0_t *config_page, u32 form, + u32 handle, u16 sz) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + struct config_request mem; + + mutex_lock(&ioc->config_cmds.mutex); + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + memset(config_page, 0, sz); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME; + mpi_request.Header.PageNumber = 0; + mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE0_PAGEVERSION; + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (r) + goto out; + + mpi_request.PageAddress = cpu_to_le32(form | handle); + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion; + mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber; + mpi_request.Header.PageType = mpi_reply->Header.PageType; + mpi_request.Header.PageLength = mpi_reply->Header.PageLength; + mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4; + if (mem.config_page_sz > ioc->config_page_sz) { + r = _config_alloc_config_dma_memory(ioc, &mem); + if (r) + goto out; + } else { + mem.config_page_dma = ioc->config_page_dma; + mem.config_page = ioc->config_page; + } + ioc->base_add_sg_single(&mpi_request.PageBufferSGE, + MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz, + mem.config_page_dma); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (!r) + memcpy(config_page, mem.config_page, + min_t(u16, sz, mem.config_page_sz)); + + if (mem.config_page_sz > ioc->config_page_sz) + _config_free_config_dma_memory(ioc, &mem); + + out: + mutex_unlock(&ioc->config_cmds.mutex); + return r; +} + +/** + * mpt2sas_config_get_phys_disk_pg0 - obtain phys disk page 0 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * @form: GET_NEXT_PHYSDISKNUM, PHYSDISKNUM, DEVHANDLE + * @form_specific: specific to the form + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_get_phys_disk_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t + *mpi_reply, Mpi2RaidPhysDiskPage0_t *config_page, u32 form, + u32 form_specific) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + struct config_request mem; + + mutex_lock(&ioc->config_cmds.mutex); + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + memset(config_page, 0, sizeof(Mpi2RaidPhysDiskPage0_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK; + mpi_request.Header.PageNumber = 0; + mpi_request.Header.PageVersion = MPI2_RAIDPHYSDISKPAGE0_PAGEVERSION; + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (r) + goto out; + + mpi_request.PageAddress = cpu_to_le32(form | form_specific); + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion; + mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber; + mpi_request.Header.PageType = mpi_reply->Header.PageType; + mpi_request.Header.PageLength = mpi_reply->Header.PageLength; + mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4; + if (mem.config_page_sz > ioc->config_page_sz) { + r = _config_alloc_config_dma_memory(ioc, &mem); + if (r) + goto out; + } else { + mem.config_page_dma = ioc->config_page_dma; + mem.config_page = ioc->config_page; + } + ioc->base_add_sg_single(&mpi_request.PageBufferSGE, + MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz, + mem.config_page_dma); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (!r) + memcpy(config_page, mem.config_page, + min_t(u16, mem.config_page_sz, + sizeof(Mpi2RaidPhysDiskPage0_t))); + + if (mem.config_page_sz > ioc->config_page_sz) + _config_free_config_dma_memory(ioc, &mem); + + out: + mutex_unlock(&ioc->config_cmds.mutex); + return r; +} + +/** + * mpt2sas_config_get_volume_handle - returns volume handle for give hidden raid components + * @ioc: per adapter object + * @pd_handle: phys disk handle + * @volume_handle: volume handle + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_get_volume_handle(struct MPT2SAS_ADAPTER *ioc, u16 pd_handle, + u16 *volume_handle) +{ + Mpi2RaidConfigurationPage0_t *config_page; + Mpi2ConfigRequest_t mpi_request; + Mpi2ConfigReply_t mpi_reply; + int r, i; + struct config_request mem; + u16 ioc_status; + + mutex_lock(&ioc->config_cmds.mutex); + *volume_handle = 0; + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED; + mpi_request.ExtPageType = MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG; + mpi_request.Header.PageVersion = MPI2_RAIDCONFIG0_PAGEVERSION; + mpi_request.Header.PageNumber = 0; + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, &mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (r) + goto out; + + mpi_request.PageAddress = + cpu_to_le32(MPI2_RAID_PGAD_FORM_ACTIVE_CONFIG); + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + mpi_request.Header.PageVersion = mpi_reply.Header.PageVersion; + mpi_request.Header.PageNumber = mpi_reply.Header.PageNumber; + mpi_request.Header.PageType = mpi_reply.Header.PageType; + mpi_request.ExtPageLength = mpi_reply.ExtPageLength; + mpi_request.ExtPageType = mpi_reply.ExtPageType; + mem.config_page_sz = le16_to_cpu(mpi_reply.ExtPageLength) * 4; + if (mem.config_page_sz > ioc->config_page_sz) { + r = _config_alloc_config_dma_memory(ioc, &mem); + if (r) + goto out; + } else { + mem.config_page_dma = ioc->config_page_dma; + mem.config_page = ioc->config_page; + } + ioc->base_add_sg_single(&mpi_request.PageBufferSGE, + MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz, + mem.config_page_dma); + r = _config_request(ioc, &mpi_request, &mpi_reply, + MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT); + if (r) + goto out; + + r = -1; + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) + goto done; + config_page = mem.config_page; + for (i = 0; i < config_page->NumElements; i++) { + if ((config_page->ConfigElement[i].ElementFlags & + MPI2_RAIDCONFIG0_EFLAGS_MASK_ELEMENT_TYPE) != + MPI2_RAIDCONFIG0_EFLAGS_VOL_PHYS_DISK_ELEMENT) + continue; + if (config_page->ConfigElement[i].PhysDiskDevHandle == + pd_handle) { + *volume_handle = le16_to_cpu(config_page-> + ConfigElement[i].VolDevHandle); + r = 0; + goto done; + } + } + + done: + if (mem.config_page_sz > ioc->config_page_sz) + _config_free_config_dma_memory(ioc, &mem); + + out: + mutex_unlock(&ioc->config_cmds.mutex); + return r; +} + +/** + * mpt2sas_config_get_volume_wwid - returns wwid given the volume handle + * @ioc: per adapter object + * @volume_handle: volume handle + * @wwid: volume wwid + * Context: sleep. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_config_get_volume_wwid(struct MPT2SAS_ADAPTER *ioc, u16 volume_handle, + u64 *wwid) +{ + Mpi2ConfigReply_t mpi_reply; + Mpi2RaidVolPage1_t raid_vol_pg1; + + *wwid = 0; + if (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply, + &raid_vol_pg1, MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, + volume_handle))) { + *wwid = le64_to_cpu(raid_vol_pg1.WWID); + return 0; + } else + return -1; +} diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c new file mode 100644 index 000000000000..4fbe3f83319b --- /dev/null +++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c @@ -0,0 +1,2516 @@ +/* + * Management Module Support for MPT (Message Passing Technology) based + * controllers + * + * This code is based on drivers/scsi/mpt2sas/mpt2_ctl.c + * Copyright (C) 2007-2008 LSI Corporation + * (mailto:DL-MPTFusionLinux@lsi.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. + * + * 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. + * + * NO WARRANTY + * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + * solely responsible for determining the appropriateness of using and + * distributing the Program and assumes all risks associated with its + * exercise of rights under this Agreement, including but not limited to + * the risks and costs of program errors, damage to or loss of data, + * programs or equipment, and unavailability or interruption of operations. + + * DISCLAIMER OF LIABILITY + * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/smp_lock.h> +#include <linux/compat.h> +#include <linux/poll.h> + +#include <linux/io.h> +#include <linux/uaccess.h> + +#include "mpt2sas_base.h" +#include "mpt2sas_ctl.h" + +static struct fasync_struct *async_queue; +static DECLARE_WAIT_QUEUE_HEAD(ctl_poll_wait); + +/** + * enum block_state - blocking state + * @NON_BLOCKING: non blocking + * @BLOCKING: blocking + * + * These states are for ioctls that need to wait for a response + * from firmware, so they probably require sleep. + */ +enum block_state { + NON_BLOCKING, + BLOCKING, +}; + +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING +/** + * _ctl_display_some_debug - debug routine + * @ioc: per adapter object + * @smid: system request message index + * @calling_function_name: string pass from calling function + * @mpi_reply: reply message frame + * Context: none. + * + * Function for displaying debug info helpfull when debugging issues + * in this module. + */ +static void +_ctl_display_some_debug(struct MPT2SAS_ADAPTER *ioc, u16 smid, + char *calling_function_name, MPI2DefaultReply_t *mpi_reply) +{ + Mpi2ConfigRequest_t *mpi_request; + char *desc = NULL; + + if (!(ioc->logging_level & MPT_DEBUG_IOCTL)) + return; + + mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); + switch (mpi_request->Function) { + case MPI2_FUNCTION_SCSI_IO_REQUEST: + { + Mpi2SCSIIORequest_t *scsi_request = + (Mpi2SCSIIORequest_t *)mpi_request; + + snprintf(ioc->tmp_string, MPT_STRING_LENGTH, + "scsi_io, cmd(0x%02x), cdb_len(%d)", + scsi_request->CDB.CDB32[0], + le16_to_cpu(scsi_request->IoFlags) & 0xF); + desc = ioc->tmp_string; + break; + } + case MPI2_FUNCTION_SCSI_TASK_MGMT: + desc = "task_mgmt"; + break; + case MPI2_FUNCTION_IOC_INIT: + desc = "ioc_init"; + break; + case MPI2_FUNCTION_IOC_FACTS: + desc = "ioc_facts"; + break; + case MPI2_FUNCTION_CONFIG: + { + Mpi2ConfigRequest_t *config_request = + (Mpi2ConfigRequest_t *)mpi_request; + + snprintf(ioc->tmp_string, MPT_STRING_LENGTH, + "config, type(0x%02x), ext_type(0x%02x), number(%d)", + (config_request->Header.PageType & + MPI2_CONFIG_PAGETYPE_MASK), config_request->ExtPageType, + config_request->Header.PageNumber); + desc = ioc->tmp_string; + break; + } + case MPI2_FUNCTION_PORT_FACTS: + desc = "port_facts"; + break; + case MPI2_FUNCTION_PORT_ENABLE: + desc = "port_enable"; + break; + case MPI2_FUNCTION_EVENT_NOTIFICATION: + desc = "event_notification"; + break; + case MPI2_FUNCTION_FW_DOWNLOAD: + desc = "fw_download"; + break; + case MPI2_FUNCTION_FW_UPLOAD: + desc = "fw_upload"; + break; + case MPI2_FUNCTION_RAID_ACTION: + desc = "raid_action"; + break; + case MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH: + { + Mpi2SCSIIORequest_t *scsi_request = + (Mpi2SCSIIORequest_t *)mpi_request; + + snprintf(ioc->tmp_string, MPT_STRING_LENGTH, + "raid_pass, cmd(0x%02x), cdb_len(%d)", + scsi_request->CDB.CDB32[0], + le16_to_cpu(scsi_request->IoFlags) & 0xF); + desc = ioc->tmp_string; + break; + } + case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL: + desc = "sas_iounit_cntl"; + break; + case MPI2_FUNCTION_SATA_PASSTHROUGH: + desc = "sata_pass"; + break; + case MPI2_FUNCTION_DIAG_BUFFER_POST: + desc = "diag_buffer_post"; + break; + case MPI2_FUNCTION_DIAG_RELEASE: + desc = "diag_release"; + break; + case MPI2_FUNCTION_SMP_PASSTHROUGH: + desc = "smp_passthrough"; + break; + } + + if (!desc) + return; + + printk(MPT2SAS_DEBUG_FMT "%s: %s, smid(%d)\n", + ioc->name, calling_function_name, desc, smid); + + if (!mpi_reply) + return; + + if (mpi_reply->IOCStatus || mpi_reply->IOCLogInfo) + printk(MPT2SAS_DEBUG_FMT + "\tiocstatus(0x%04x), loginfo(0x%08x)\n", + ioc->name, le16_to_cpu(mpi_reply->IOCStatus), + le32_to_cpu(mpi_reply->IOCLogInfo)); + + if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || + mpi_request->Function == + MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) { + Mpi2SCSIIOReply_t *scsi_reply = + (Mpi2SCSIIOReply_t *)mpi_reply; + if (scsi_reply->SCSIState || scsi_reply->SCSIStatus) + printk(MPT2SAS_DEBUG_FMT + "\tscsi_state(0x%02x), scsi_status" + "(0x%02x)\n", ioc->name, + scsi_reply->SCSIState, + scsi_reply->SCSIStatus); + } +} +#endif + +/** + * mpt2sas_ctl_done - ctl module completion routine + * @ioc: per adapter object + * @smid: system request message index + * @VF_ID: virtual function id + * @reply: reply message frame(lower 32bit addr) + * Context: none. + * + * The callback handler when using ioc->ctl_cb_idx. + * + * Return nothing. + */ +void +mpt2sas_ctl_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply) +{ + MPI2DefaultReply_t *mpi_reply; + + if (ioc->ctl_cmds.status == MPT2_CMD_NOT_USED) + return; + if (ioc->ctl_cmds.smid != smid) + return; + ioc->ctl_cmds.status |= MPT2_CMD_COMPLETE; + mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); + if (mpi_reply) { + memcpy(ioc->ctl_cmds.reply, mpi_reply, mpi_reply->MsgLength*4); + ioc->ctl_cmds.status |= MPT2_CMD_REPLY_VALID; + } +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING + _ctl_display_some_debug(ioc, smid, "ctl_done", mpi_reply); +#endif + ioc->ctl_cmds.status &= ~MPT2_CMD_PENDING; + complete(&ioc->ctl_cmds.done); +} + +/** + * _ctl_check_event_type - determines when an event needs logging + * @ioc: per adapter object + * @event: firmware event + * + * The bitmask in ioc->event_type[] indicates which events should be + * be saved in the driver event_log. This bitmask is set by application. + * + * Returns 1 when event should be captured, or zero means no match. + */ +static int +_ctl_check_event_type(struct MPT2SAS_ADAPTER *ioc, u16 event) +{ + u16 i; + u32 desired_event; + + if (event >= 128 || !event || !ioc->event_log) + return 0; + + desired_event = (1 << (event % 32)); + if (!desired_event) + desired_event = 1; + i = event / 32; + return desired_event & ioc->event_type[i]; +} + +/** + * mpt2sas_ctl_add_to_event_log - add event + * @ioc: per adapter object + * @mpi_reply: reply message frame + * + * Return nothing. + */ +void +mpt2sas_ctl_add_to_event_log(struct MPT2SAS_ADAPTER *ioc, + Mpi2EventNotificationReply_t *mpi_reply) +{ + struct MPT2_IOCTL_EVENTS *event_log; + u16 event; + int i; + u32 sz, event_data_sz; + u8 send_aen = 0; + + if (!ioc->event_log) + return; + + event = le16_to_cpu(mpi_reply->Event); + + if (_ctl_check_event_type(ioc, event)) { + + /* insert entry into circular event_log */ + i = ioc->event_context % MPT2SAS_CTL_EVENT_LOG_SIZE; + event_log = ioc->event_log; + event_log[i].event = event; + event_log[i].context = ioc->event_context++; + + event_data_sz = le16_to_cpu(mpi_reply->EventDataLength)*4; + sz = min_t(u32, event_data_sz, MPT2_EVENT_DATA_SIZE); + memset(event_log[i].data, 0, MPT2_EVENT_DATA_SIZE); + memcpy(event_log[i].data, mpi_reply->EventData, sz); + send_aen = 1; + } + + /* This aen_event_read_flag flag is set until the + * application has read the event log. + * For MPI2_EVENT_LOG_ENTRY_ADDED, we always notify. + */ + if (event == MPI2_EVENT_LOG_ENTRY_ADDED || + (send_aen && !ioc->aen_event_read_flag)) { + ioc->aen_event_read_flag = 1; + wake_up_interruptible(&ctl_poll_wait); + if (async_queue) + kill_fasync(&async_queue, SIGIO, POLL_IN); + } +} + +/** + * mpt2sas_ctl_event_callback - firmware event handler (called at ISR time) + * @ioc: per adapter object + * @VF_ID: virtual function id + * @reply: reply message frame(lower 32bit addr) + * Context: interrupt. + * + * This function merely adds a new work task into ioc->firmware_event_thread. + * The tasks are worked from _firmware_event_work in user context. + * + * Return nothing. + */ +void +mpt2sas_ctl_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply) +{ + Mpi2EventNotificationReply_t *mpi_reply; + + mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); + mpt2sas_ctl_add_to_event_log(ioc, mpi_reply); +} + +/** + * _ctl_verify_adapter - validates ioc_number passed from application + * @ioc: per adapter object + * @iocpp: The ioc pointer is returned in this. + * + * Return (-1) means error, else ioc_number. + */ +static int +_ctl_verify_adapter(int ioc_number, struct MPT2SAS_ADAPTER **iocpp) +{ + struct MPT2SAS_ADAPTER *ioc; + + list_for_each_entry(ioc, &ioc_list, list) { + if (ioc->id != ioc_number) + continue; + *iocpp = ioc; + return ioc_number; + } + *iocpp = NULL; + return -1; +} + +/** + * mpt2sas_ctl_reset_handler - reset callback handler (for ctl) + * @ioc: per adapter object + * @reset_phase: phase + * + * The handler for doing any required cleanup or initialization. + * + * The reset phase can be MPT2_IOC_PRE_RESET, MPT2_IOC_AFTER_RESET, + * MPT2_IOC_DONE_RESET + */ +void +mpt2sas_ctl_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase) +{ + switch (reset_phase) { + case MPT2_IOC_PRE_RESET: + dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + "MPT2_IOC_PRE_RESET\n", ioc->name, __func__)); + break; + case MPT2_IOC_AFTER_RESET: + dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + "MPT2_IOC_AFTER_RESET\n", ioc->name, __func__)); + if (ioc->ctl_cmds.status & MPT2_CMD_PENDING) { + ioc->ctl_cmds.status |= MPT2_CMD_RESET; + mpt2sas_base_free_smid(ioc, ioc->ctl_cmds.smid); + complete(&ioc->ctl_cmds.done); + } + break; + case MPT2_IOC_DONE_RESET: + dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + "MPT2_IOC_DONE_RESET\n", ioc->name, __func__)); + break; + } +} + +/** + * _ctl_fasync - + * @fd - + * @filep - + * @mode - + * + * Called when application request fasyn callback handler. + */ +static int +_ctl_fasync(int fd, struct file *filep, int mode) +{ + return fasync_helper(fd, filep, mode, &async_queue); +} + +/** + * _ctl_release - + * @inode - + * @filep - + * + * Called when application releases the fasyn callback handler. + */ +static int +_ctl_release(struct inode *inode, struct file *filep) +{ + return fasync_helper(-1, filep, 0, &async_queue); +} + +/** + * _ctl_poll - + * @file - + * @wait - + * + */ +static unsigned int +_ctl_poll(struct file *filep, poll_table *wait) +{ + struct MPT2SAS_ADAPTER *ioc; + + poll_wait(filep, &ctl_poll_wait, wait); + + list_for_each_entry(ioc, &ioc_list, list) { + if (ioc->aen_event_read_flag) + return POLLIN | POLLRDNORM; + } + return 0; +} + +/** + * _ctl_do_task_abort - assign an active smid to the abort_task + * @ioc: per adapter object + * @karg - (struct mpt2_ioctl_command) + * @tm_request - pointer to mf from user space + * + * Returns 0 when an smid if found, else fail. + * during failure, the reply frame is filled. + */ +static int +_ctl_do_task_abort(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command *karg, + Mpi2SCSITaskManagementRequest_t *tm_request) +{ + u8 found = 0; + u16 i; + u16 handle; + struct scsi_cmnd *scmd; + struct MPT2SAS_DEVICE *priv_data; + unsigned long flags; + Mpi2SCSITaskManagementReply_t *tm_reply; + u32 sz; + u32 lun; + + lun = scsilun_to_int((struct scsi_lun *)tm_request->LUN); + + handle = le16_to_cpu(tm_request->DevHandle); + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + for (i = ioc->request_depth; i && !found; i--) { + scmd = ioc->scsi_lookup[i - 1].scmd; + if (scmd == NULL || scmd->device == NULL || + scmd->device->hostdata == NULL) + continue; + if (lun != scmd->device->lun) + continue; + priv_data = scmd->device->hostdata; + if (priv_data->sas_target == NULL) + continue; + if (priv_data->sas_target->handle != handle) + continue; + tm_request->TaskMID = cpu_to_le16(ioc->scsi_lookup[i - 1].smid); + found = 1; + } + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + + if (!found) { + dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "ABORT_TASK: " + "DevHandle(0x%04x), lun(%d), no active mid!!\n", ioc->name, + tm_request->DevHandle, lun)); + tm_reply = ioc->ctl_cmds.reply; + tm_reply->DevHandle = tm_request->DevHandle; + tm_reply->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; + tm_reply->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK; + tm_reply->MsgLength = sizeof(Mpi2SCSITaskManagementReply_t)/4; + tm_reply->VP_ID = tm_request->VP_ID; + tm_reply->VF_ID = tm_request->VF_ID; + sz = min_t(u32, karg->max_reply_bytes, ioc->reply_sz); + if (copy_to_user(karg->reply_frame_buf_ptr, ioc->ctl_cmds.reply, + sz)) + printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, + __LINE__, __func__); + return 1; + } + + dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "ABORT_TASK: " + "DevHandle(0x%04x), lun(%d), smid(%d)\n", ioc->name, + tm_request->DevHandle, lun, tm_request->TaskMID)); + return 0; +} + +/** + * _ctl_do_mpt_command - main handler for MPT2COMMAND opcode + * @ioc: per adapter object + * @karg - (struct mpt2_ioctl_command) + * @mf - pointer to mf in user space + * @state - NON_BLOCKING or BLOCKING + */ +static long +_ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, + struct mpt2_ioctl_command karg, void __user *mf, enum block_state state) +{ + MPI2RequestHeader_t *mpi_request; + MPI2DefaultReply_t *mpi_reply; + u32 ioc_state; + u16 ioc_status; + u16 smid; + unsigned long timeout, timeleft; + u8 issue_reset; + u32 sz; + void *psge; + void *priv_sense = NULL; + void *data_out = NULL; + dma_addr_t data_out_dma; + size_t data_out_sz = 0; + void *data_in = NULL; + dma_addr_t data_in_dma; + size_t data_in_sz = 0; + u32 sgl_flags; + long ret; + u16 wait_state_count; + + issue_reset = 0; + + if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex)) + return -EAGAIN; + else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) + return -ERESTARTSYS; + + if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) { + printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n", + ioc->name, __func__); + ret = -EAGAIN; + goto out; + } + + wait_state_count = 0; + ioc_state = mpt2sas_base_get_iocstate(ioc, 1); + while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { + if (wait_state_count++ == 10) { + printk(MPT2SAS_ERR_FMT + "%s: failed due to ioc not operational\n", + ioc->name, __func__); + ret = -EFAULT; + goto out; + } + ssleep(1); + ioc_state = mpt2sas_base_get_iocstate(ioc, 1); + printk(MPT2SAS_INFO_FMT "%s: waiting for " + "operational state(count=%d)\n", ioc->name, + __func__, wait_state_count); + } + if (wait_state_count) + printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n", + ioc->name, __func__); + + smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx); + if (!smid) { + printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", + ioc->name, __func__); + ret = -EAGAIN; + goto out; + } + + ret = 0; + ioc->ctl_cmds.status = MPT2_CMD_PENDING; + memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); + mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); + ioc->ctl_cmds.smid = smid; + data_out_sz = karg.data_out_size; + data_in_sz = karg.data_in_size; + + /* copy in request message frame from user */ + if (copy_from_user(mpi_request, mf, karg.data_sge_offset*4)) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, + __func__); + ret = -EFAULT; + mpt2sas_base_free_smid(ioc, smid); + goto out; + } + + if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || + mpi_request->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) { + if (!mpi_request->FunctionDependent1 || + mpi_request->FunctionDependent1 > + cpu_to_le16(ioc->facts.MaxDevHandle)) { + ret = -EINVAL; + mpt2sas_base_free_smid(ioc, smid); + goto out; + } + } + + /* obtain dma-able memory for data transfer */ + if (data_out_sz) /* WRITE */ { + data_out = pci_alloc_consistent(ioc->pdev, data_out_sz, + &data_out_dma); + if (!data_out) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, + __LINE__, __func__); + ret = -ENOMEM; + mpt2sas_base_free_smid(ioc, smid); + goto out; + } + if (copy_from_user(data_out, karg.data_out_buf_ptr, + data_out_sz)) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, + __LINE__, __func__); + ret = -EFAULT; + mpt2sas_base_free_smid(ioc, smid); + goto out; + } + } + + if (data_in_sz) /* READ */ { + data_in = pci_alloc_consistent(ioc->pdev, data_in_sz, + &data_in_dma); + if (!data_in) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, + __LINE__, __func__); + ret = -ENOMEM; + mpt2sas_base_free_smid(ioc, smid); + goto out; + } + } + + /* add scatter gather elements */ + psge = (void *)mpi_request + (karg.data_sge_offset*4); + + if (!data_out_sz && !data_in_sz) { + mpt2sas_base_build_zero_len_sge(ioc, psge); + } else if (data_out_sz && data_in_sz) { + /* WRITE sgel first */ + sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | + MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC); + sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; + ioc->base_add_sg_single(psge, sgl_flags | + data_out_sz, data_out_dma); + + /* incr sgel */ + psge += ioc->sge_size; + + /* READ sgel last */ + sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | + MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | + MPI2_SGE_FLAGS_END_OF_LIST); + sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; + ioc->base_add_sg_single(psge, sgl_flags | + data_in_sz, data_in_dma); + } else if (data_out_sz) /* WRITE */ { + sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | + MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | + MPI2_SGE_FLAGS_END_OF_LIST | MPI2_SGE_FLAGS_HOST_TO_IOC); + sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; + ioc->base_add_sg_single(psge, sgl_flags | + data_out_sz, data_out_dma); + } else if (data_in_sz) /* READ */ { + sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | + MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | + MPI2_SGE_FLAGS_END_OF_LIST); + sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; + ioc->base_add_sg_single(psge, sgl_flags | + data_in_sz, data_in_dma); + } + + /* send command to firmware */ +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING + _ctl_display_some_debug(ioc, smid, "ctl_request", NULL); +#endif + + switch (mpi_request->Function) { + case MPI2_FUNCTION_SCSI_IO_REQUEST: + case MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH: + { + Mpi2SCSIIORequest_t *scsiio_request = + (Mpi2SCSIIORequest_t *)mpi_request; + scsiio_request->SenseBufferLowAddress = + (u32)mpt2sas_base_get_sense_buffer_dma(ioc, smid); + priv_sense = mpt2sas_base_get_sense_buffer(ioc, smid); + memset(priv_sense, 0, SCSI_SENSE_BUFFERSIZE); + mpt2sas_base_put_smid_scsi_io(ioc, smid, 0, + le16_to_cpu(mpi_request->FunctionDependent1)); + break; + } + case MPI2_FUNCTION_SCSI_TASK_MGMT: + { + Mpi2SCSITaskManagementRequest_t *tm_request = + (Mpi2SCSITaskManagementRequest_t *)mpi_request; + + if (tm_request->TaskType == + MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK) { + if (_ctl_do_task_abort(ioc, &karg, tm_request)) + goto out; + } + + mutex_lock(&ioc->tm_cmds.mutex); + mpt2sas_scsih_set_tm_flag(ioc, le16_to_cpu( + tm_request->DevHandle)); + mpt2sas_base_put_smid_hi_priority(ioc, smid, + mpi_request->VF_ID); + break; + } + case MPI2_FUNCTION_SMP_PASSTHROUGH: + { + Mpi2SmpPassthroughRequest_t *smp_request = + (Mpi2SmpPassthroughRequest_t *)mpi_request; + u8 *data; + + /* ioc determines which port to use */ + smp_request->PhysicalPort = 0xFF; + if (smp_request->PassthroughFlags & + MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE) + data = (u8 *)&smp_request->SGL; + else + data = data_out; + + if (data[1] == 0x91 && (data[10] == 1 || data[10] == 2)) { + ioc->ioc_link_reset_in_progress = 1; + ioc->ignore_loginfos = 1; + } + mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID); + break; + } + case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL: + { + Mpi2SasIoUnitControlRequest_t *sasiounit_request = + (Mpi2SasIoUnitControlRequest_t *)mpi_request; + + if (sasiounit_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET + || sasiounit_request->Operation == + MPI2_SAS_OP_PHY_LINK_RESET) { + ioc->ioc_link_reset_in_progress = 1; + ioc->ignore_loginfos = 1; + } + mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID); + break; + } + default: + mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID); + break; + } + + if (karg.timeout < MPT2_IOCTL_DEFAULT_TIMEOUT) + timeout = MPT2_IOCTL_DEFAULT_TIMEOUT; + else + timeout = karg.timeout; + timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, + timeout*HZ); + if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) { + Mpi2SCSITaskManagementRequest_t *tm_request = + (Mpi2SCSITaskManagementRequest_t *)mpi_request; + mutex_unlock(&ioc->tm_cmds.mutex); + mpt2sas_scsih_clear_tm_flag(ioc, le16_to_cpu( + tm_request->DevHandle)); + } else if ((mpi_request->Function == MPI2_FUNCTION_SMP_PASSTHROUGH || + mpi_request->Function == MPI2_FUNCTION_SAS_IO_UNIT_CONTROL) && + ioc->ioc_link_reset_in_progress) { + ioc->ioc_link_reset_in_progress = 0; + ioc->ignore_loginfos = 0; + } + if (!(ioc->ctl_cmds.status & MPT2_CMD_COMPLETE)) { + printk(MPT2SAS_ERR_FMT "%s: timeout\n", ioc->name, + __func__); + _debug_dump_mf(mpi_request, karg.data_sge_offset); + if (!(ioc->ctl_cmds.status & MPT2_CMD_RESET)) + issue_reset = 1; + goto issue_host_reset; + } + + mpi_reply = ioc->ctl_cmds.reply; + ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; + +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING + if (mpi_reply->Function == MPI2_FUNCTION_SCSI_TASK_MGMT && + (ioc->logging_level & MPT_DEBUG_TM)) { + Mpi2SCSITaskManagementReply_t *tm_reply = + (Mpi2SCSITaskManagementReply_t *)mpi_reply; + + printk(MPT2SAS_DEBUG_FMT "TASK_MGMT: " + "IOCStatus(0x%04x), IOCLogInfo(0x%08x), " + "TerminationCount(0x%08x)\n", ioc->name, + tm_reply->IOCStatus, tm_reply->IOCLogInfo, + tm_reply->TerminationCount); + } +#endif + /* copy out xdata to user */ + if (data_in_sz) { + if (copy_to_user(karg.data_in_buf_ptr, data_in, + data_in_sz)) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, + __LINE__, __func__); + ret = -ENODATA; + goto out; + } + } + + /* copy out reply message frame to user */ + if (karg.max_reply_bytes) { + sz = min_t(u32, karg.max_reply_bytes, ioc->reply_sz); + if (copy_to_user(karg.reply_frame_buf_ptr, ioc->ctl_cmds.reply, + sz)) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, + __LINE__, __func__); + ret = -ENODATA; + goto out; + } + } + + /* copy out sense to user */ + if (karg.max_sense_bytes && (mpi_request->Function == + MPI2_FUNCTION_SCSI_IO_REQUEST || mpi_request->Function == + MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) { + sz = min_t(u32, karg.max_sense_bytes, SCSI_SENSE_BUFFERSIZE); + if (copy_to_user(karg.sense_data_ptr, priv_sense, sz)) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, + __LINE__, __func__); + ret = -ENODATA; + goto out; + } + } + + issue_host_reset: + if (issue_reset) { + if ((mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || + mpi_request->Function == + MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) { + printk(MPT2SAS_INFO_FMT "issue target reset: handle " + "= (0x%04x)\n", ioc->name, + mpi_request->FunctionDependent1); + mutex_lock(&ioc->tm_cmds.mutex); + mpt2sas_scsih_issue_tm(ioc, + mpi_request->FunctionDependent1, 0, + MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 10); + ioc->tm_cmds.status = MPT2_CMD_NOT_USED; + mutex_unlock(&ioc->tm_cmds.mutex); + } else + mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + } + + out: + + /* free memory associated with sg buffers */ + if (data_in) + pci_free_consistent(ioc->pdev, data_in_sz, data_in, + data_in_dma); + + if (data_out) + pci_free_consistent(ioc->pdev, data_out_sz, data_out, + data_out_dma); + + ioc->ctl_cmds.status = MPT2_CMD_NOT_USED; + mutex_unlock(&ioc->ctl_cmds.mutex); + return ret; +} + +/** + * _ctl_getiocinfo - main handler for MPT2IOCINFO opcode + * @arg - user space buffer containing ioctl content + */ +static long +_ctl_getiocinfo(void __user *arg) +{ + struct mpt2_ioctl_iocinfo karg; + struct MPT2SAS_ADAPTER *ioc; + u8 revision; + + if (copy_from_user(&karg, arg, sizeof(karg))) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return -EFAULT; + } + if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) + return -ENODEV; + + dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name, + __func__)); + + memset(&karg, 0 , sizeof(karg)); + karg.adapter_type = MPT2_IOCTL_INTERFACE_SAS2; + if (ioc->pfacts) + karg.port_number = ioc->pfacts[0].PortNumber; + pci_read_config_byte(ioc->pdev, PCI_CLASS_REVISION, &revision); + karg.hw_rev = revision; + karg.pci_id = ioc->pdev->device; + karg.subsystem_device = ioc->pdev->subsystem_device; + karg.subsystem_vendor = ioc->pdev->subsystem_vendor; + karg.pci_information.u.bits.bus = ioc->pdev->bus->number; + karg.pci_information.u.bits.device = PCI_SLOT(ioc->pdev->devfn); + karg.pci_information.u.bits.function = PCI_FUNC(ioc->pdev->devfn); + karg.pci_information.segment_id = pci_domain_nr(ioc->pdev->bus); + karg.firmware_version = ioc->facts.FWVersion.Word; + strncpy(karg.driver_version, MPT2SAS_DRIVER_VERSION, + MPT2_IOCTL_VERSION_LENGTH); + karg.driver_version[MPT2_IOCTL_VERSION_LENGTH - 1] = '\0'; + karg.bios_version = le32_to_cpu(ioc->bios_pg3.BiosVersion); + + if (copy_to_user(arg, &karg, sizeof(karg))) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return -EFAULT; + } + return 0; +} + +/** + * _ctl_eventquery - main handler for MPT2EVENTQUERY opcode + * @arg - user space buffer containing ioctl content + */ +static long +_ctl_eventquery(void __user *arg) +{ + struct mpt2_ioctl_eventquery karg; + struct MPT2SAS_ADAPTER *ioc; + + if (copy_from_user(&karg, arg, sizeof(karg))) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return -EFAULT; + } + if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) + return -ENODEV; + + dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name, + __func__)); + + karg.event_entries = MPT2SAS_CTL_EVENT_LOG_SIZE; + memcpy(karg.event_types, ioc->event_type, + MPI2_EVENT_NOTIFY_EVENTMASK_WORDS * sizeof(u32)); + + if (copy_to_user(arg, &karg, sizeof(karg))) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return -EFAULT; + } + return 0; +} + +/** + * _ctl_eventenable - main handler for MPT2EVENTENABLE opcode + * @arg - user space buffer containing ioctl content + */ +static long +_ctl_eventenable(void __user *arg) +{ + struct mpt2_ioctl_eventenable karg; + struct MPT2SAS_ADAPTER *ioc; + + if (copy_from_user(&karg, arg, sizeof(karg))) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return -EFAULT; + } + if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) + return -ENODEV; + + dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name, + __func__)); + + if (ioc->event_log) + return 0; + memcpy(ioc->event_type, karg.event_types, + MPI2_EVENT_NOTIFY_EVENTMASK_WORDS * sizeof(u32)); + mpt2sas_base_validate_event_type(ioc, ioc->event_type); + + /* initialize event_log */ + ioc->event_context = 0; + ioc->aen_event_read_flag = 0; + ioc->event_log = kcalloc(MPT2SAS_CTL_EVENT_LOG_SIZE, + sizeof(struct MPT2_IOCTL_EVENTS), GFP_KERNEL); + if (!ioc->event_log) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return -ENOMEM; + } + return 0; +} + +/** + * _ctl_eventreport - main handler for MPT2EVENTREPORT opcode + * @arg - user space buffer containing ioctl content + */ +static long +_ctl_eventreport(void __user *arg) +{ + struct mpt2_ioctl_eventreport karg; + struct MPT2SAS_ADAPTER *ioc; + u32 number_bytes, max_events, max; + struct mpt2_ioctl_eventreport __user *uarg = arg; + + if (copy_from_user(&karg, arg, sizeof(karg))) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return -EFAULT; + } + if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) + return -ENODEV; + + dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name, + __func__)); + + number_bytes = karg.hdr.max_data_size - + sizeof(struct mpt2_ioctl_header); + max_events = number_bytes/sizeof(struct MPT2_IOCTL_EVENTS); + max = min_t(u32, MPT2SAS_CTL_EVENT_LOG_SIZE, max_events); + + /* If fewer than 1 event is requested, there must have + * been some type of error. + */ + if (!max || !ioc->event_log) + return -ENODATA; + + number_bytes = max * sizeof(struct MPT2_IOCTL_EVENTS); + if (copy_to_user(uarg->event_data, ioc->event_log, number_bytes)) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return -EFAULT; + } + + /* reset flag so SIGIO can restart */ + ioc->aen_event_read_flag = 0; + return 0; +} + +/** + * _ctl_do_reset - main handler for MPT2HARDRESET opcode + * @arg - user space buffer containing ioctl content + */ +static long +_ctl_do_reset(void __user *arg) +{ + struct mpt2_ioctl_diag_reset karg; + struct MPT2SAS_ADAPTER *ioc; + int retval; + + if (copy_from_user(&karg, arg, sizeof(karg))) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return -EFAULT; + } + if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) + return -ENODEV; + + dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name, + __func__)); + + retval = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + printk(MPT2SAS_INFO_FMT "host reset: %s\n", + ioc->name, ((!retval) ? "SUCCESS" : "FAILED")); + return 0; +} + +/** + * _ctl_btdh_search_sas_device - searching for sas device + * @ioc: per adapter object + * @btdh: btdh ioctl payload + */ +static int +_ctl_btdh_search_sas_device(struct MPT2SAS_ADAPTER *ioc, + struct mpt2_ioctl_btdh_mapping *btdh) +{ + struct _sas_device *sas_device; + unsigned long flags; + int rc = 0; + + if (list_empty(&ioc->sas_device_list)) + return rc; + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + list_for_each_entry(sas_device, &ioc->sas_device_list, list) { + if (btdh->bus == 0xFFFFFFFF && btdh->id == 0xFFFFFFFF && + btdh->handle == sas_device->handle) { + btdh->bus = sas_device->channel; + btdh->id = sas_device->id; + rc = 1; + goto out; + } else if (btdh->bus == sas_device->channel && btdh->id == + sas_device->id && btdh->handle == 0xFFFF) { + btdh->handle = sas_device->handle; + rc = 1; + goto out; + } + } + out: + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + return rc; +} + +/** + * _ctl_btdh_search_raid_device - searching for raid device + * @ioc: per adapter object + * @btdh: btdh ioctl payload + */ +static int +_ctl_btdh_search_raid_device(struct MPT2SAS_ADAPTER *ioc, + struct mpt2_ioctl_btdh_mapping *btdh) +{ + struct _raid_device *raid_device; + unsigned long flags; + int rc = 0; + + if (list_empty(&ioc->raid_device_list)) + return rc; + + spin_lock_irqsave(&ioc->raid_device_lock, flags); + list_for_each_entry(raid_device, &ioc->raid_device_list, list) { + if (btdh->bus == 0xFFFFFFFF && btdh->id == 0xFFFFFFFF && + btdh->handle == raid_device->handle) { + btdh->bus = raid_device->channel; + btdh->id = raid_device->id; + rc = 1; + goto out; + } else if (btdh->bus == raid_device->channel && btdh->id == + raid_device->id && btdh->handle == 0xFFFF) { + btdh->handle = raid_device->handle; + rc = 1; + goto out; + } + } + out: + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); + return rc; +} + +/** + * _ctl_btdh_mapping - main handler for MPT2BTDHMAPPING opcode + * @arg - user space buffer containing ioctl content + */ +static long +_ctl_btdh_mapping(void __user *arg) +{ + struct mpt2_ioctl_btdh_mapping karg; + struct MPT2SAS_ADAPTER *ioc; + int rc; + + if (copy_from_user(&karg, arg, sizeof(karg))) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return -EFAULT; + } + if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) + return -ENODEV; + + dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + __func__)); + + rc = _ctl_btdh_search_sas_device(ioc, &karg); + if (!rc) + _ctl_btdh_search_raid_device(ioc, &karg); + + if (copy_to_user(arg, &karg, sizeof(karg))) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return -EFAULT; + } + return 0; +} + +/** + * _ctl_diag_capability - return diag buffer capability + * @ioc: per adapter object + * @buffer_type: specifies either TRACE or SNAPSHOT + * + * returns 1 when diag buffer support is enabled in firmware + */ +static u8 +_ctl_diag_capability(struct MPT2SAS_ADAPTER *ioc, u8 buffer_type) +{ + u8 rc = 0; + + switch (buffer_type) { + case MPI2_DIAG_BUF_TYPE_TRACE: + if (ioc->facts.IOCCapabilities & + MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER) + rc = 1; + break; + case MPI2_DIAG_BUF_TYPE_SNAPSHOT: + if (ioc->facts.IOCCapabilities & + MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER) + rc = 1; + break; + } + + return rc; +} + +/** + * _ctl_diag_register - application register with driver + * @arg - user space buffer containing ioctl content + * @state - NON_BLOCKING or BLOCKING + * + * This will allow the driver to setup any required buffers that will be + * needed by firmware to communicate with the driver. + */ +static long +_ctl_diag_register(void __user *arg, enum block_state state) +{ + struct mpt2_diag_register karg; + struct MPT2SAS_ADAPTER *ioc; + int rc, i; + void *request_data = NULL; + dma_addr_t request_data_dma; + u32 request_data_sz = 0; + Mpi2DiagBufferPostRequest_t *mpi_request; + Mpi2DiagBufferPostReply_t *mpi_reply; + u8 buffer_type; + unsigned long timeleft; + u16 smid; + u16 ioc_status; + u8 issue_reset = 0; + + if (copy_from_user(&karg, arg, sizeof(karg))) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return -EFAULT; + } + if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) + return -ENODEV; + + dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + __func__)); + + buffer_type = karg.buffer_type; + if (!_ctl_diag_capability(ioc, buffer_type)) { + printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for " + "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); + return -EPERM; + } + + if (ioc->diag_buffer_status[buffer_type] & + MPT2_DIAG_BUFFER_IS_REGISTERED) { + printk(MPT2SAS_ERR_FMT "%s: already has a registered " + "buffer for buffer_type(0x%02x)\n", ioc->name, __func__, + buffer_type); + return -EINVAL; + } + + if (karg.requested_buffer_size % 4) { + printk(MPT2SAS_ERR_FMT "%s: the requested_buffer_size " + "is not 4 byte aligned\n", ioc->name, __func__); + return -EINVAL; + } + + if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex)) + return -EAGAIN; + else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) + return -ERESTARTSYS; + + if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) { + printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n", + ioc->name, __func__); + rc = -EAGAIN; + goto out; + } + + smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx); + if (!smid) { + printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", + ioc->name, __func__); + rc = -EAGAIN; + goto out; + } + + rc = 0; + ioc->ctl_cmds.status = MPT2_CMD_PENDING; + memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); + mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); + ioc->ctl_cmds.smid = smid; + + request_data = ioc->diag_buffer[buffer_type]; + request_data_sz = karg.requested_buffer_size; + ioc->unique_id[buffer_type] = karg.unique_id; + ioc->diag_buffer_status[buffer_type] = 0; + memcpy(ioc->product_specific[buffer_type], karg.product_specific, + MPT2_PRODUCT_SPECIFIC_DWORDS); + ioc->diagnostic_flags[buffer_type] = karg.diagnostic_flags; + + if (request_data) { + request_data_dma = ioc->diag_buffer_dma[buffer_type]; + if (request_data_sz != ioc->diag_buffer_sz[buffer_type]) { + pci_free_consistent(ioc->pdev, + ioc->diag_buffer_sz[buffer_type], + request_data, request_data_dma); + request_data = NULL; + } + } + + if (request_data == NULL) { + ioc->diag_buffer_sz[buffer_type] = 0; + ioc->diag_buffer_dma[buffer_type] = 0; + request_data = pci_alloc_consistent( + ioc->pdev, request_data_sz, &request_data_dma); + if (request_data == NULL) { + printk(MPT2SAS_ERR_FMT "%s: failed allocating memory" + " for diag buffers, requested size(%d)\n", + ioc->name, __func__, request_data_sz); + mpt2sas_base_free_smid(ioc, smid); + return -ENOMEM; + } + ioc->diag_buffer[buffer_type] = request_data; + ioc->diag_buffer_sz[buffer_type] = request_data_sz; + ioc->diag_buffer_dma[buffer_type] = request_data_dma; + } + + mpi_request->Function = MPI2_FUNCTION_DIAG_BUFFER_POST; + mpi_request->BufferType = karg.buffer_type; + mpi_request->Flags = cpu_to_le32(karg.diagnostic_flags); + mpi_request->BufferAddress = cpu_to_le64(request_data_dma); + mpi_request->BufferLength = cpu_to_le32(request_data_sz); + + dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: diag_buffer(0x%p), " + "dma(0x%llx), sz(%d)\n", ioc->name, __func__, request_data, + (unsigned long long)request_data_dma, mpi_request->BufferLength)); + + for (i = 0; i < MPT2_PRODUCT_SPECIFIC_DWORDS; i++) + mpi_request->ProductSpecific[i] = + cpu_to_le32(ioc->product_specific[buffer_type][i]); + + mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID); + timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, + MPT2_IOCTL_DEFAULT_TIMEOUT*HZ); + + if (!(ioc->ctl_cmds.status & MPT2_CMD_COMPLETE)) { + printk(MPT2SAS_ERR_FMT "%s: timeout\n", ioc->name, + __func__); + _debug_dump_mf(mpi_request, + sizeof(Mpi2DiagBufferPostRequest_t)/4); + if (!(ioc->ctl_cmds.status & MPT2_CMD_RESET)) + issue_reset = 1; + goto issue_host_reset; + } + + /* process the completed Reply Message Frame */ + if ((ioc->ctl_cmds.status & MPT2_CMD_REPLY_VALID) == 0) { + printk(MPT2SAS_ERR_FMT "%s: no reply message\n", + ioc->name, __func__); + rc = -EFAULT; + goto out; + } + + mpi_reply = ioc->ctl_cmds.reply; + ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; + + if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { + ioc->diag_buffer_status[buffer_type] |= + MPT2_DIAG_BUFFER_IS_REGISTERED; + dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: success\n", + ioc->name, __func__)); + } else { + printk(MPT2SAS_DEBUG_FMT "%s: ioc_status(0x%04x) " + "log_info(0x%08x)\n", ioc->name, __func__, + ioc_status, mpi_reply->IOCLogInfo); + rc = -EFAULT; + } + + issue_host_reset: + if (issue_reset) + mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + + out: + + if (rc && request_data) + pci_free_consistent(ioc->pdev, request_data_sz, + request_data, request_data_dma); + + ioc->ctl_cmds.status = MPT2_CMD_NOT_USED; + mutex_unlock(&ioc->ctl_cmds.mutex); + return rc; +} + +/** + * _ctl_diag_unregister - application unregister with driver + * @arg - user space buffer containing ioctl content + * + * This will allow the driver to cleanup any memory allocated for diag + * messages and to free up any resources. + */ +static long +_ctl_diag_unregister(void __user *arg) +{ + struct mpt2_diag_unregister karg; + struct MPT2SAS_ADAPTER *ioc; + void *request_data; + dma_addr_t request_data_dma; + u32 request_data_sz; + u8 buffer_type; + + if (copy_from_user(&karg, arg, sizeof(karg))) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return -EFAULT; + } + if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) + return -ENODEV; + + dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + __func__)); + + buffer_type = karg.unique_id & 0x000000ff; + if (!_ctl_diag_capability(ioc, buffer_type)) { + printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for " + "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); + return -EPERM; + } + + if ((ioc->diag_buffer_status[buffer_type] & + MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) { + printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) is not " + "registered\n", ioc->name, __func__, buffer_type); + return -EINVAL; + } + if ((ioc->diag_buffer_status[buffer_type] & + MPT2_DIAG_BUFFER_IS_RELEASED) == 0) { + printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) has not been " + "released\n", ioc->name, __func__, buffer_type); + return -EINVAL; + } + + if (karg.unique_id != ioc->unique_id[buffer_type]) { + printk(MPT2SAS_ERR_FMT "%s: unique_id(0x%08x) is not " + "registered\n", ioc->name, __func__, karg.unique_id); + return -EINVAL; + } + + request_data = ioc->diag_buffer[buffer_type]; + if (!request_data) { + printk(MPT2SAS_ERR_FMT "%s: doesn't have memory allocated for " + "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); + return -ENOMEM; + } + + request_data_sz = ioc->diag_buffer_sz[buffer_type]; + request_data_dma = ioc->diag_buffer_dma[buffer_type]; + pci_free_consistent(ioc->pdev, request_data_sz, + request_data, request_data_dma); + ioc->diag_buffer[buffer_type] = NULL; + ioc->diag_buffer_status[buffer_type] = 0; + return 0; +} + +/** + * _ctl_diag_query - query relevant info associated with diag buffers + * @arg - user space buffer containing ioctl content + * + * The application will send only buffer_type and unique_id. Driver will + * inspect unique_id first, if valid, fill in all the info. If unique_id is + * 0x00, the driver will return info specified by Buffer Type. + */ +static long +_ctl_diag_query(void __user *arg) +{ + struct mpt2_diag_query karg; + struct MPT2SAS_ADAPTER *ioc; + void *request_data; + int i; + u8 buffer_type; + + if (copy_from_user(&karg, arg, sizeof(karg))) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return -EFAULT; + } + if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) + return -ENODEV; + + dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + __func__)); + + karg.application_flags = 0; + buffer_type = karg.buffer_type; + + if (!_ctl_diag_capability(ioc, buffer_type)) { + printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for " + "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); + return -EPERM; + } + + if ((ioc->diag_buffer_status[buffer_type] & + MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) { + printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) is not " + "registered\n", ioc->name, __func__, buffer_type); + return -EINVAL; + } + + if (karg.unique_id & 0xffffff00) { + if (karg.unique_id != ioc->unique_id[buffer_type]) { + printk(MPT2SAS_ERR_FMT "%s: unique_id(0x%08x) is not " + "registered\n", ioc->name, __func__, + karg.unique_id); + return -EINVAL; + } + } + + request_data = ioc->diag_buffer[buffer_type]; + if (!request_data) { + printk(MPT2SAS_ERR_FMT "%s: doesn't have buffer for " + "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); + return -ENOMEM; + } + + if (ioc->diag_buffer_status[buffer_type] & MPT2_DIAG_BUFFER_IS_RELEASED) + karg.application_flags = (MPT2_APP_FLAGS_APP_OWNED | + MPT2_APP_FLAGS_BUFFER_VALID); + else + karg.application_flags = (MPT2_APP_FLAGS_APP_OWNED | + MPT2_APP_FLAGS_BUFFER_VALID | + MPT2_APP_FLAGS_FW_BUFFER_ACCESS); + + for (i = 0; i < MPT2_PRODUCT_SPECIFIC_DWORDS; i++) + karg.product_specific[i] = + ioc->product_specific[buffer_type][i]; + + karg.total_buffer_size = ioc->diag_buffer_sz[buffer_type]; + karg.driver_added_buffer_size = 0; + karg.unique_id = ioc->unique_id[buffer_type]; + karg.diagnostic_flags = ioc->diagnostic_flags[buffer_type]; + + if (copy_to_user(arg, &karg, sizeof(struct mpt2_diag_query))) { + printk(MPT2SAS_ERR_FMT "%s: unable to write mpt2_diag_query " + "data @ %p\n", ioc->name, __func__, arg); + return -EFAULT; + } + return 0; +} + +/** + * _ctl_diag_release - request to send Diag Release Message to firmware + * @arg - user space buffer containing ioctl content + * @state - NON_BLOCKING or BLOCKING + * + * This allows ownership of the specified buffer to returned to the driver, + * allowing an application to read the buffer without fear that firmware is + * overwritting information in the buffer. + */ +static long +_ctl_diag_release(void __user *arg, enum block_state state) +{ + struct mpt2_diag_release karg; + struct MPT2SAS_ADAPTER *ioc; + void *request_data; + int rc; + Mpi2DiagReleaseRequest_t *mpi_request; + Mpi2DiagReleaseReply_t *mpi_reply; + u8 buffer_type; + unsigned long timeleft; + u16 smid; + u16 ioc_status; + u8 issue_reset = 0; + + if (copy_from_user(&karg, arg, sizeof(karg))) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return -EFAULT; + } + if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) + return -ENODEV; + + dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + __func__)); + + buffer_type = karg.unique_id & 0x000000ff; + if (!_ctl_diag_capability(ioc, buffer_type)) { + printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for " + "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); + return -EPERM; + } + + if ((ioc->diag_buffer_status[buffer_type] & + MPT2_DIAG_BUFFER_IS_REGISTERED) == 0) { + printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) is not " + "registered\n", ioc->name, __func__, buffer_type); + return -EINVAL; + } + + if (karg.unique_id != ioc->unique_id[buffer_type]) { + printk(MPT2SAS_ERR_FMT "%s: unique_id(0x%08x) is not " + "registered\n", ioc->name, __func__, karg.unique_id); + return -EINVAL; + } + + if (ioc->diag_buffer_status[buffer_type] & + MPT2_DIAG_BUFFER_IS_RELEASED) { + printk(MPT2SAS_ERR_FMT "%s: buffer_type(0x%02x) " + "is already released\n", ioc->name, __func__, + buffer_type); + return 0; + } + + request_data = ioc->diag_buffer[buffer_type]; + + if (!request_data) { + printk(MPT2SAS_ERR_FMT "%s: doesn't have memory allocated for " + "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); + return -ENOMEM; + } + + if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex)) + return -EAGAIN; + else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) + return -ERESTARTSYS; + + if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) { + printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n", + ioc->name, __func__); + rc = -EAGAIN; + goto out; + } + + smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx); + if (!smid) { + printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", + ioc->name, __func__); + rc = -EAGAIN; + goto out; + } + + rc = 0; + ioc->ctl_cmds.status = MPT2_CMD_PENDING; + memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); + mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); + ioc->ctl_cmds.smid = smid; + + mpi_request->Function = MPI2_FUNCTION_DIAG_RELEASE; + mpi_request->BufferType = buffer_type; + + mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID); + timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, + MPT2_IOCTL_DEFAULT_TIMEOUT*HZ); + + if (!(ioc->ctl_cmds.status & MPT2_CMD_COMPLETE)) { + printk(MPT2SAS_ERR_FMT "%s: timeout\n", ioc->name, + __func__); + _debug_dump_mf(mpi_request, + sizeof(Mpi2DiagReleaseRequest_t)/4); + if (!(ioc->ctl_cmds.status & MPT2_CMD_RESET)) + issue_reset = 1; + goto issue_host_reset; + } + + /* process the completed Reply Message Frame */ + if ((ioc->ctl_cmds.status & MPT2_CMD_REPLY_VALID) == 0) { + printk(MPT2SAS_ERR_FMT "%s: no reply message\n", + ioc->name, __func__); + rc = -EFAULT; + goto out; + } + + mpi_reply = ioc->ctl_cmds.reply; + ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; + + if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { + ioc->diag_buffer_status[buffer_type] |= + MPT2_DIAG_BUFFER_IS_RELEASED; + dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: success\n", + ioc->name, __func__)); + } else { + printk(MPT2SAS_DEBUG_FMT "%s: ioc_status(0x%04x) " + "log_info(0x%08x)\n", ioc->name, __func__, + ioc_status, mpi_reply->IOCLogInfo); + rc = -EFAULT; + } + + issue_host_reset: + if (issue_reset) + mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + + out: + + ioc->ctl_cmds.status = MPT2_CMD_NOT_USED; + mutex_unlock(&ioc->ctl_cmds.mutex); + return rc; +} + +/** + * _ctl_diag_read_buffer - request for copy of the diag buffer + * @arg - user space buffer containing ioctl content + * @state - NON_BLOCKING or BLOCKING + */ +static long +_ctl_diag_read_buffer(void __user *arg, enum block_state state) +{ + struct mpt2_diag_read_buffer karg; + struct mpt2_diag_read_buffer __user *uarg = arg; + struct MPT2SAS_ADAPTER *ioc; + void *request_data, *diag_data; + Mpi2DiagBufferPostRequest_t *mpi_request; + Mpi2DiagBufferPostReply_t *mpi_reply; + int rc, i; + u8 buffer_type; + unsigned long timeleft; + u16 smid; + u16 ioc_status; + u8 issue_reset = 0; + + if (copy_from_user(&karg, arg, sizeof(karg))) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return -EFAULT; + } + if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || !ioc) + return -ENODEV; + + dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, + __func__)); + + buffer_type = karg.unique_id & 0x000000ff; + if (!_ctl_diag_capability(ioc, buffer_type)) { + printk(MPT2SAS_ERR_FMT "%s: doesn't have capability for " + "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); + return -EPERM; + } + + if (karg.unique_id != ioc->unique_id[buffer_type]) { + printk(MPT2SAS_ERR_FMT "%s: unique_id(0x%08x) is not " + "registered\n", ioc->name, __func__, karg.unique_id); + return -EINVAL; + } + + request_data = ioc->diag_buffer[buffer_type]; + if (!request_data) { + printk(MPT2SAS_ERR_FMT "%s: doesn't have buffer for " + "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type); + return -ENOMEM; + } + + if ((karg.starting_offset % 4) || (karg.bytes_to_read % 4)) { + printk(MPT2SAS_ERR_FMT "%s: either the starting_offset " + "or bytes_to_read are not 4 byte aligned\n", ioc->name, + __func__); + return -EINVAL; + } + + diag_data = (void *)(request_data + karg.starting_offset); + dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: diag_buffer(%p), " + "offset(%d), sz(%d)\n", ioc->name, __func__, + diag_data, karg.starting_offset, karg.bytes_to_read)); + + if (copy_to_user((void __user *)uarg->diagnostic_data, + diag_data, karg.bytes_to_read)) { + printk(MPT2SAS_ERR_FMT "%s: Unable to write " + "mpt_diag_read_buffer_t data @ %p\n", ioc->name, + __func__, diag_data); + return -EFAULT; + } + + if ((karg.flags & MPT2_FLAGS_REREGISTER) == 0) + return 0; + + dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: Reregister " + "buffer_type(0x%02x)\n", ioc->name, __func__, buffer_type)); + if ((ioc->diag_buffer_status[buffer_type] & + MPT2_DIAG_BUFFER_IS_RELEASED) == 0) { + dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + "buffer_type(0x%02x) is still registered\n", ioc->name, + __func__, buffer_type)); + return 0; + } + /* Get a free request frame and save the message context. + */ + if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex)) + return -EAGAIN; + else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) + return -ERESTARTSYS; + + if (ioc->ctl_cmds.status != MPT2_CMD_NOT_USED) { + printk(MPT2SAS_ERR_FMT "%s: ctl_cmd in use\n", + ioc->name, __func__); + rc = -EAGAIN; + goto out; + } + + smid = mpt2sas_base_get_smid(ioc, ioc->ctl_cb_idx); + if (!smid) { + printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", + ioc->name, __func__); + rc = -EAGAIN; + goto out; + } + + rc = 0; + ioc->ctl_cmds.status = MPT2_CMD_PENDING; + memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); + mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); + ioc->ctl_cmds.smid = smid; + + mpi_request->Function = MPI2_FUNCTION_DIAG_BUFFER_POST; + mpi_request->BufferType = buffer_type; + mpi_request->BufferLength = + cpu_to_le32(ioc->diag_buffer_sz[buffer_type]); + mpi_request->BufferAddress = + cpu_to_le64(ioc->diag_buffer_dma[buffer_type]); + for (i = 0; i < MPT2_PRODUCT_SPECIFIC_DWORDS; i++) + mpi_request->ProductSpecific[i] = + cpu_to_le32(ioc->product_specific[buffer_type][i]); + + mpt2sas_base_put_smid_default(ioc, smid, mpi_request->VF_ID); + timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, + MPT2_IOCTL_DEFAULT_TIMEOUT*HZ); + + if (!(ioc->ctl_cmds.status & MPT2_CMD_COMPLETE)) { + printk(MPT2SAS_ERR_FMT "%s: timeout\n", ioc->name, + __func__); + _debug_dump_mf(mpi_request, + sizeof(Mpi2DiagBufferPostRequest_t)/4); + if (!(ioc->ctl_cmds.status & MPT2_CMD_RESET)) + issue_reset = 1; + goto issue_host_reset; + } + + /* process the completed Reply Message Frame */ + if ((ioc->ctl_cmds.status & MPT2_CMD_REPLY_VALID) == 0) { + printk(MPT2SAS_ERR_FMT "%s: no reply message\n", + ioc->name, __func__); + rc = -EFAULT; + goto out; + } + + mpi_reply = ioc->ctl_cmds.reply; + ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; + + if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { + ioc->diag_buffer_status[buffer_type] |= + MPT2_DIAG_BUFFER_IS_REGISTERED; + dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: success\n", + ioc->name, __func__)); + } else { + printk(MPT2SAS_DEBUG_FMT "%s: ioc_status(0x%04x) " + "log_info(0x%08x)\n", ioc->name, __func__, + ioc_status, mpi_reply->IOCLogInfo); + rc = -EFAULT; + } + + issue_host_reset: + if (issue_reset) + mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + + out: + + ioc->ctl_cmds.status = MPT2_CMD_NOT_USED; + mutex_unlock(&ioc->ctl_cmds.mutex); + return rc; +} + +/** + * _ctl_ioctl_main - main ioctl entry point + * @file - (struct file) + * @cmd - ioctl opcode + * @arg - + */ +static long +_ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg) +{ + enum block_state state; + long ret = -EINVAL; + unsigned long flags; + + state = (file->f_flags & O_NONBLOCK) ? NON_BLOCKING : + BLOCKING; + + switch (cmd) { + case MPT2IOCINFO: + if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_iocinfo)) + ret = _ctl_getiocinfo(arg); + break; + case MPT2COMMAND: + { + struct mpt2_ioctl_command karg; + struct mpt2_ioctl_command __user *uarg; + struct MPT2SAS_ADAPTER *ioc; + + if (copy_from_user(&karg, arg, sizeof(karg))) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return -EFAULT; + } + + if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || + !ioc) + return -ENODEV; + + spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); + if (ioc->shost_recovery) { + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, + flags); + return -EAGAIN; + } + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); + + if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_command)) { + uarg = arg; + ret = _ctl_do_mpt_command(ioc, karg, &uarg->mf, state); + } + break; + } + case MPT2EVENTQUERY: + if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_eventquery)) + ret = _ctl_eventquery(arg); + break; + case MPT2EVENTENABLE: + if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_eventenable)) + ret = _ctl_eventenable(arg); + break; + case MPT2EVENTREPORT: + ret = _ctl_eventreport(arg); + break; + case MPT2HARDRESET: + if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_diag_reset)) + ret = _ctl_do_reset(arg); + break; + case MPT2BTDHMAPPING: + if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_btdh_mapping)) + ret = _ctl_btdh_mapping(arg); + break; + case MPT2DIAGREGISTER: + if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_register)) + ret = _ctl_diag_register(arg, state); + break; + case MPT2DIAGUNREGISTER: + if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_unregister)) + ret = _ctl_diag_unregister(arg); + break; + case MPT2DIAGQUERY: + if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_query)) + ret = _ctl_diag_query(arg); + break; + case MPT2DIAGRELEASE: + if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_release)) + ret = _ctl_diag_release(arg, state); + break; + case MPT2DIAGREADBUFFER: + if (_IOC_SIZE(cmd) == sizeof(struct mpt2_diag_read_buffer)) + ret = _ctl_diag_read_buffer(arg, state); + break; + default: + { + struct mpt2_ioctl_command karg; + struct MPT2SAS_ADAPTER *ioc; + + if (copy_from_user(&karg, arg, sizeof(karg))) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return -EFAULT; + } + + if (_ctl_verify_adapter(karg.hdr.ioc_number, &ioc) == -1 || + !ioc) + return -ENODEV; + + dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT + "unsupported ioctl opcode(0x%08x)\n", ioc->name, cmd)); + break; + } + } + return ret; +} + +/** + * _ctl_ioctl - main ioctl entry point (unlocked) + * @file - (struct file) + * @cmd - ioctl opcode + * @arg - + */ +static long +_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + long ret; + lock_kernel(); + ret = _ctl_ioctl_main(file, cmd, (void __user *)arg); + unlock_kernel(); + return ret; +} + +#ifdef CONFIG_COMPAT +/** + * _ctl_compat_mpt_command - convert 32bit pointers to 64bit. + * @file - (struct file) + * @cmd - ioctl opcode + * @arg - (struct mpt2_ioctl_command32) + * + * MPT2COMMAND32 - Handle 32bit applications running on 64bit os. + */ +static long +_ctl_compat_mpt_command(struct file *file, unsigned cmd, unsigned long arg) +{ + struct mpt2_ioctl_command32 karg32; + struct mpt2_ioctl_command32 __user *uarg; + struct mpt2_ioctl_command karg; + struct MPT2SAS_ADAPTER *ioc; + enum block_state state; + unsigned long flags; + + if (_IOC_SIZE(cmd) != sizeof(struct mpt2_ioctl_command32)) + return -EINVAL; + + uarg = (struct mpt2_ioctl_command32 __user *) arg; + + if (copy_from_user(&karg32, (char __user *)arg, sizeof(karg32))) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", + __FILE__, __LINE__, __func__); + return -EFAULT; + } + if (_ctl_verify_adapter(karg32.hdr.ioc_number, &ioc) == -1 || !ioc) + return -ENODEV; + + spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); + if (ioc->shost_recovery) { + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, + flags); + return -EAGAIN; + } + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); + + memset(&karg, 0, sizeof(struct mpt2_ioctl_command)); + karg.hdr.ioc_number = karg32.hdr.ioc_number; + karg.hdr.port_number = karg32.hdr.port_number; + karg.hdr.max_data_size = karg32.hdr.max_data_size; + karg.timeout = karg32.timeout; + karg.max_reply_bytes = karg32.max_reply_bytes; + karg.data_in_size = karg32.data_in_size; + karg.data_out_size = karg32.data_out_size; + karg.max_sense_bytes = karg32.max_sense_bytes; + karg.data_sge_offset = karg32.data_sge_offset; + memcpy(&karg.reply_frame_buf_ptr, &karg32.reply_frame_buf_ptr, + sizeof(uint32_t)); + memcpy(&karg.data_in_buf_ptr, &karg32.data_in_buf_ptr, + sizeof(uint32_t)); + memcpy(&karg.data_out_buf_ptr, &karg32.data_out_buf_ptr, + sizeof(uint32_t)); + memcpy(&karg.sense_data_ptr, &karg32.sense_data_ptr, + sizeof(uint32_t)); + state = (file->f_flags & O_NONBLOCK) ? NON_BLOCKING : BLOCKING; + return _ctl_do_mpt_command(ioc, karg, &uarg->mf, state); +} + +/** + * _ctl_ioctl_compat - main ioctl entry point (compat) + * @file - + * @cmd - + * @arg - + * + * This routine handles 32 bit applications in 64bit os. + */ +static long +_ctl_ioctl_compat(struct file *file, unsigned cmd, unsigned long arg) +{ + long ret; + lock_kernel(); + if (cmd == MPT2COMMAND32) + ret = _ctl_compat_mpt_command(file, cmd, arg); + else + ret = _ctl_ioctl_main(file, cmd, (void __user *)arg); + unlock_kernel(); + return ret; +} +#endif + +/* scsi host attributes */ + +/** + * _ctl_version_fw_show - firmware version + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t +_ctl_version_fw_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + return snprintf(buf, PAGE_SIZE, "%02d.%02d.%02d.%02d\n", + (ioc->facts.FWVersion.Word & 0xFF000000) >> 24, + (ioc->facts.FWVersion.Word & 0x00FF0000) >> 16, + (ioc->facts.FWVersion.Word & 0x0000FF00) >> 8, + ioc->facts.FWVersion.Word & 0x000000FF); +} +static DEVICE_ATTR(version_fw, S_IRUGO, _ctl_version_fw_show, NULL); + +/** + * _ctl_version_bios_show - bios version + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t +_ctl_version_bios_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + u32 version = le32_to_cpu(ioc->bios_pg3.BiosVersion); + + return snprintf(buf, PAGE_SIZE, "%02d.%02d.%02d.%02d\n", + (version & 0xFF000000) >> 24, + (version & 0x00FF0000) >> 16, + (version & 0x0000FF00) >> 8, + version & 0x000000FF); +} +static DEVICE_ATTR(version_bios, S_IRUGO, _ctl_version_bios_show, NULL); + +/** + * _ctl_version_mpi_show - MPI (message passing interface) version + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t +_ctl_version_mpi_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + return snprintf(buf, PAGE_SIZE, "%03x.%02x\n", + ioc->facts.MsgVersion, ioc->facts.HeaderVersion >> 8); +} +static DEVICE_ATTR(version_mpi, S_IRUGO, _ctl_version_mpi_show, NULL); + +/** + * _ctl_version_product_show - product name + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t +_ctl_version_product_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + return snprintf(buf, 16, "%s\n", ioc->manu_pg0.ChipName); +} +static DEVICE_ATTR(version_product, S_IRUGO, + _ctl_version_product_show, NULL); + +/** + * _ctl_version_nvdata_persistent_show - ndvata persistent version + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t +_ctl_version_nvdata_persistent_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + return snprintf(buf, PAGE_SIZE, "%02xh\n", + le16_to_cpu(ioc->iounit_pg0.NvdataVersionPersistent.Word)); +} +static DEVICE_ATTR(version_nvdata_persistent, S_IRUGO, + _ctl_version_nvdata_persistent_show, NULL); + +/** + * _ctl_version_nvdata_default_show - nvdata default version + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t +_ctl_version_nvdata_default_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + return snprintf(buf, PAGE_SIZE, "%02xh\n", + le16_to_cpu(ioc->iounit_pg0.NvdataVersionDefault.Word)); +} +static DEVICE_ATTR(version_nvdata_default, S_IRUGO, + _ctl_version_nvdata_default_show, NULL); + +/** + * _ctl_board_name_show - board name + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t +_ctl_board_name_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardName); +} +static DEVICE_ATTR(board_name, S_IRUGO, _ctl_board_name_show, NULL); + +/** + * _ctl_board_assembly_show - board assembly name + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t +_ctl_board_assembly_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardAssembly); +} +static DEVICE_ATTR(board_assembly, S_IRUGO, + _ctl_board_assembly_show, NULL); + +/** + * _ctl_board_tracer_show - board tracer number + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t +_ctl_board_tracer_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + return snprintf(buf, 16, "%s\n", ioc->manu_pg0.BoardTracerNumber); +} +static DEVICE_ATTR(board_tracer, S_IRUGO, + _ctl_board_tracer_show, NULL); + +/** + * _ctl_io_delay_show - io missing delay + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * This is for firmware implemention for deboucing device + * removal events. + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t +_ctl_io_delay_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->io_missing_delay); +} +static DEVICE_ATTR(io_delay, S_IRUGO, + _ctl_io_delay_show, NULL); + +/** + * _ctl_device_delay_show - device missing delay + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * This is for firmware implemention for deboucing device + * removal events. + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t +_ctl_device_delay_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->device_missing_delay); +} +static DEVICE_ATTR(device_delay, S_IRUGO, + _ctl_device_delay_show, NULL); + +/** + * _ctl_fw_queue_depth_show - global credits + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * This is firmware queue depth limit + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t +_ctl_fw_queue_depth_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + return snprintf(buf, PAGE_SIZE, "%02d\n", ioc->facts.RequestCredit); +} +static DEVICE_ATTR(fw_queue_depth, S_IRUGO, + _ctl_fw_queue_depth_show, NULL); + +/** + * _ctl_sas_address_show - sas address + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * This is the controller sas address + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t +_ctl_host_sas_address_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + return snprintf(buf, PAGE_SIZE, "0x%016llx\n", + (unsigned long long)ioc->sas_hba.sas_address); +} +static DEVICE_ATTR(host_sas_address, S_IRUGO, + _ctl_host_sas_address_show, NULL); + +/** + * _ctl_logging_level_show - logging level + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * A sysfs 'read/write' shost attribute. + */ +static ssize_t +_ctl_logging_level_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + return snprintf(buf, PAGE_SIZE, "%08xh\n", ioc->logging_level); +} +static ssize_t +_ctl_logging_level_store(struct device *cdev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + int val = 0; + + if (sscanf(buf, "%x", &val) != 1) + return -EINVAL; + + ioc->logging_level = val; + printk(MPT2SAS_INFO_FMT "logging_level=%08xh\n", ioc->name, + ioc->logging_level); + return strlen(buf); +} +static DEVICE_ATTR(logging_level, S_IRUGO | S_IWUSR, + _ctl_logging_level_show, _ctl_logging_level_store); + +struct device_attribute *mpt2sas_host_attrs[] = { + &dev_attr_version_fw, + &dev_attr_version_bios, + &dev_attr_version_mpi, + &dev_attr_version_product, + &dev_attr_version_nvdata_persistent, + &dev_attr_version_nvdata_default, + &dev_attr_board_name, + &dev_attr_board_assembly, + &dev_attr_board_tracer, + &dev_attr_io_delay, + &dev_attr_device_delay, + &dev_attr_logging_level, + &dev_attr_fw_queue_depth, + &dev_attr_host_sas_address, + NULL, +}; + +/* device attributes */ + +/** + * _ctl_device_sas_address_show - sas address + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * This is the sas address for the target + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t +_ctl_device_sas_address_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct MPT2SAS_DEVICE *sas_device_priv_data = sdev->hostdata; + + return snprintf(buf, PAGE_SIZE, "0x%016llx\n", + (unsigned long long)sas_device_priv_data->sas_target->sas_address); +} +static DEVICE_ATTR(sas_address, S_IRUGO, _ctl_device_sas_address_show, NULL); + +/** + * _ctl_device_handle_show - device handle + * @cdev - pointer to embedded class device + * @buf - the buffer returned + * + * This is the firmware assigned device handle + * + * A sysfs 'read-only' shost attribute. + */ +static ssize_t +_ctl_device_handle_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct MPT2SAS_DEVICE *sas_device_priv_data = sdev->hostdata; + + return snprintf(buf, PAGE_SIZE, "0x%04x\n", + sas_device_priv_data->sas_target->handle); +} +static DEVICE_ATTR(sas_device_handle, S_IRUGO, _ctl_device_handle_show, NULL); + +struct device_attribute *mpt2sas_dev_attrs[] = { + &dev_attr_sas_address, + &dev_attr_sas_device_handle, + NULL, +}; + +static const struct file_operations ctl_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = _ctl_ioctl, + .release = _ctl_release, + .poll = _ctl_poll, + .fasync = _ctl_fasync, +#ifdef CONFIG_COMPAT + .compat_ioctl = _ctl_ioctl_compat, +#endif +}; + +static struct miscdevice ctl_dev = { + .minor = MPT2SAS_MINOR, + .name = MPT2SAS_DEV_NAME, + .fops = &ctl_fops, +}; + +/** + * mpt2sas_ctl_init - main entry point for ctl. + * + */ +void +mpt2sas_ctl_init(void) +{ + async_queue = NULL; + if (misc_register(&ctl_dev) < 0) + printk(KERN_ERR "%s can't register misc device [minor=%d]\n", + MPT2SAS_DRIVER_NAME, MPT2SAS_MINOR); + + init_waitqueue_head(&ctl_poll_wait); +} + +/** + * mpt2sas_ctl_exit - exit point for ctl + * + */ +void +mpt2sas_ctl_exit(void) +{ + struct MPT2SAS_ADAPTER *ioc; + int i; + + list_for_each_entry(ioc, &ioc_list, list) { + + /* free memory associated to diag buffers */ + for (i = 0; i < MPI2_DIAG_BUF_TYPE_COUNT; i++) { + if (!ioc->diag_buffer[i]) + continue; + pci_free_consistent(ioc->pdev, ioc->diag_buffer_sz[i], + ioc->diag_buffer[i], ioc->diag_buffer_dma[i]); + ioc->diag_buffer[i] = NULL; + ioc->diag_buffer_status[i] = 0; + } + + kfree(ioc->event_log); + } + misc_deregister(&ctl_dev); +} + diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.h b/drivers/scsi/mpt2sas/mpt2sas_ctl.h new file mode 100644 index 000000000000..dbb6c0cf8889 --- /dev/null +++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.h @@ -0,0 +1,416 @@ +/* + * Management Module Support for MPT (Message Passing Technology) based + * controllers + * + * This code is based on drivers/scsi/mpt2sas/mpt2_ctl.h + * Copyright (C) 2007-2008 LSI Corporation + * (mailto:DL-MPTFusionLinux@lsi.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. + * + * 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. + * + * NO WARRANTY + * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + * solely responsible for determining the appropriateness of using and + * distributing the Program and assumes all risks associated with its + * exercise of rights under this Agreement, including but not limited to + * the risks and costs of program errors, damage to or loss of data, + * programs or equipment, and unavailability or interruption of operations. + + * DISCLAIMER OF LIABILITY + * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#ifndef MPT2SAS_CTL_H_INCLUDED +#define MPT2SAS_CTL_H_INCLUDED + +#ifdef __KERNEL__ +#include <linux/miscdevice.h> +#endif + +#define MPT2SAS_DEV_NAME "mpt2ctl" +#define MPT2_MAGIC_NUMBER 'm' +#define MPT2_IOCTL_DEFAULT_TIMEOUT (10) /* in seconds */ + +/** + * IOCTL opcodes + */ +#define MPT2IOCINFO _IOWR(MPT2_MAGIC_NUMBER, 17, \ + struct mpt2_ioctl_iocinfo) +#define MPT2COMMAND _IOWR(MPT2_MAGIC_NUMBER, 20, \ + struct mpt2_ioctl_command) +#ifdef CONFIG_COMPAT +#define MPT2COMMAND32 _IOWR(MPT2_MAGIC_NUMBER, 20, \ + struct mpt2_ioctl_command32) +#endif +#define MPT2EVENTQUERY _IOWR(MPT2_MAGIC_NUMBER, 21, \ + struct mpt2_ioctl_eventquery) +#define MPT2EVENTENABLE _IOWR(MPT2_MAGIC_NUMBER, 22, \ + struct mpt2_ioctl_eventenable) +#define MPT2EVENTREPORT _IOWR(MPT2_MAGIC_NUMBER, 23, \ + struct mpt2_ioctl_eventreport) +#define MPT2HARDRESET _IOWR(MPT2_MAGIC_NUMBER, 24, \ + struct mpt2_ioctl_diag_reset) +#define MPT2BTDHMAPPING _IOWR(MPT2_MAGIC_NUMBER, 31, \ + struct mpt2_ioctl_btdh_mapping) + +/* diag buffer support */ +#define MPT2DIAGREGISTER _IOWR(MPT2_MAGIC_NUMBER, 26, \ + struct mpt2_diag_register) +#define MPT2DIAGRELEASE _IOWR(MPT2_MAGIC_NUMBER, 27, \ + struct mpt2_diag_release) +#define MPT2DIAGUNREGISTER _IOWR(MPT2_MAGIC_NUMBER, 28, \ + struct mpt2_diag_unregister) +#define MPT2DIAGQUERY _IOWR(MPT2_MAGIC_NUMBER, 29, \ + struct mpt2_diag_query) +#define MPT2DIAGREADBUFFER _IOWR(MPT2_MAGIC_NUMBER, 30, \ + struct mpt2_diag_read_buffer) + +/** + * struct mpt2_ioctl_header - main header structure + * @ioc_number - IOC unit number + * @port_number - IOC port number + * @max_data_size - maximum number bytes to transfer on read + */ +struct mpt2_ioctl_header { + uint32_t ioc_number; + uint32_t port_number; + uint32_t max_data_size; +}; + +/** + * struct mpt2_ioctl_diag_reset - diagnostic reset + * @hdr - generic header + */ +struct mpt2_ioctl_diag_reset { + struct mpt2_ioctl_header hdr; +}; + + +/** + * struct mpt2_ioctl_pci_info - pci device info + * @device - pci device id + * @function - pci function id + * @bus - pci bus id + * @segment_id - pci segment id + */ +struct mpt2_ioctl_pci_info { + union { + struct { + uint32_t device:5; + uint32_t function:3; + uint32_t bus:24; + } bits; + uint32_t word; + } u; + uint32_t segment_id; +}; + + +#define MPT2_IOCTL_INTERFACE_SCSI (0x00) +#define MPT2_IOCTL_INTERFACE_FC (0x01) +#define MPT2_IOCTL_INTERFACE_FC_IP (0x02) +#define MPT2_IOCTL_INTERFACE_SAS (0x03) +#define MPT2_IOCTL_INTERFACE_SAS2 (0x04) +#define MPT2_IOCTL_VERSION_LENGTH (32) + +/** + * struct mpt2_ioctl_iocinfo - generic controller info + * @hdr - generic header + * @adapter_type - type of adapter (spi, fc, sas) + * @port_number - port number + * @pci_id - PCI Id + * @hw_rev - hardware revision + * @sub_system_device - PCI subsystem Device ID + * @sub_system_vendor - PCI subsystem Vendor ID + * @rsvd0 - reserved + * @firmware_version - firmware version + * @bios_version - BIOS version + * @driver_version - driver version - 32 ASCII characters + * @rsvd1 - reserved + * @scsi_id - scsi id of adapter 0 + * @rsvd2 - reserved + * @pci_information - pci info (2nd revision) + */ +struct mpt2_ioctl_iocinfo { + struct mpt2_ioctl_header hdr; + uint32_t adapter_type; + uint32_t port_number; + uint32_t pci_id; + uint32_t hw_rev; + uint32_t subsystem_device; + uint32_t subsystem_vendor; + uint32_t rsvd0; + uint32_t firmware_version; + uint32_t bios_version; + uint8_t driver_version[MPT2_IOCTL_VERSION_LENGTH]; + uint8_t rsvd1; + uint8_t scsi_id; + uint16_t rsvd2; + struct mpt2_ioctl_pci_info pci_information; +}; + + +/* number of event log entries */ +#define MPT2SAS_CTL_EVENT_LOG_SIZE (50) + +/** + * struct mpt2_ioctl_eventquery - query event count and type + * @hdr - generic header + * @event_entries - number of events returned by get_event_report + * @rsvd - reserved + * @event_types - type of events currently being captured + */ +struct mpt2_ioctl_eventquery { + struct mpt2_ioctl_header hdr; + uint16_t event_entries; + uint16_t rsvd; + uint32_t event_types[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS]; +}; + +/** + * struct mpt2_ioctl_eventenable - enable/disable event capturing + * @hdr - generic header + * @event_types - toggle off/on type of events to be captured + */ +struct mpt2_ioctl_eventenable { + struct mpt2_ioctl_header hdr; + uint32_t event_types[4]; +}; + +#define MPT2_EVENT_DATA_SIZE (192) +/** + * struct MPT2_IOCTL_EVENTS - + * @event - the event that was reported + * @context - unique value for each event assigned by driver + * @data - event data returned in fw reply message + */ +struct MPT2_IOCTL_EVENTS { + uint32_t event; + uint32_t context; + uint8_t data[MPT2_EVENT_DATA_SIZE]; +}; + +/** + * struct mpt2_ioctl_eventreport - returing event log + * @hdr - generic header + * @event_data - (see struct MPT2_IOCTL_EVENTS) + */ +struct mpt2_ioctl_eventreport { + struct mpt2_ioctl_header hdr; + struct MPT2_IOCTL_EVENTS event_data[1]; +}; + +/** + * struct mpt2_ioctl_command - generic mpt firmware passthru ioclt + * @hdr - generic header + * @timeout - command timeout in seconds. (if zero then use driver default + * value). + * @reply_frame_buf_ptr - reply location + * @data_in_buf_ptr - destination for read + * @data_out_buf_ptr - data source for write + * @sense_data_ptr - sense data location + * @max_reply_bytes - maximum number of reply bytes to be sent to app. + * @data_in_size - number bytes for data transfer in (read) + * @data_out_size - number bytes for data transfer out (write) + * @max_sense_bytes - maximum number of bytes for auto sense buffers + * @data_sge_offset - offset in words from the start of the request message to + * the first SGL + * @mf[1]; + */ +struct mpt2_ioctl_command { + struct mpt2_ioctl_header hdr; + uint32_t timeout; + void __user *reply_frame_buf_ptr; + void __user *data_in_buf_ptr; + void __user *data_out_buf_ptr; + void __user *sense_data_ptr; + uint32_t max_reply_bytes; + uint32_t data_in_size; + uint32_t data_out_size; + uint32_t max_sense_bytes; + uint32_t data_sge_offset; + uint8_t mf[1]; +}; + +#ifdef CONFIG_COMPAT +struct mpt2_ioctl_command32 { + struct mpt2_ioctl_header hdr; + uint32_t timeout; + uint32_t reply_frame_buf_ptr; + uint32_t data_in_buf_ptr; + uint32_t data_out_buf_ptr; + uint32_t sense_data_ptr; + uint32_t max_reply_bytes; + uint32_t data_in_size; + uint32_t data_out_size; + uint32_t max_sense_bytes; + uint32_t data_sge_offset; + uint8_t mf[1]; +}; +#endif + +/** + * struct mpt2_ioctl_btdh_mapping - mapping info + * @hdr - generic header + * @id - target device identification number + * @bus - SCSI bus number that the target device exists on + * @handle - device handle for the target device + * @rsvd - reserved + * + * To obtain a bus/id the application sets + * handle to valid handle, and bus/id to 0xFFFF. + * + * To obtain the device handle the application sets + * bus/id valid value, and the handle to 0xFFFF. + */ +struct mpt2_ioctl_btdh_mapping { + struct mpt2_ioctl_header hdr; + uint32_t id; + uint32_t bus; + uint16_t handle; + uint16_t rsvd; +}; + + +/* status bits for ioc->diag_buffer_status */ +#define MPT2_DIAG_BUFFER_IS_REGISTERED (0x01) +#define MPT2_DIAG_BUFFER_IS_RELEASED (0x02) + +/* application flags for mpt2_diag_register, mpt2_diag_query */ +#define MPT2_APP_FLAGS_APP_OWNED (0x0001) +#define MPT2_APP_FLAGS_BUFFER_VALID (0x0002) +#define MPT2_APP_FLAGS_FW_BUFFER_ACCESS (0x0004) + +/* flags for mpt2_diag_read_buffer */ +#define MPT2_FLAGS_REREGISTER (0x0001) + +#define MPT2_PRODUCT_SPECIFIC_DWORDS 23 + +/** + * struct mpt2_diag_register - application register with driver + * @hdr - generic header + * @reserved - + * @buffer_type - specifies either TRACE or SNAPSHOT + * @application_flags - misc flags + * @diagnostic_flags - specifies flags affecting command processing + * @product_specific - product specific information + * @requested_buffer_size - buffers size in bytes + * @unique_id - tag specified by application that is used to signal ownership + * of the buffer. + * + * This will allow the driver to setup any required buffers that will be + * needed by firmware to communicate with the driver. + */ +struct mpt2_diag_register { + struct mpt2_ioctl_header hdr; + uint8_t reserved; + uint8_t buffer_type; + uint16_t application_flags; + uint32_t diagnostic_flags; + uint32_t product_specific[MPT2_PRODUCT_SPECIFIC_DWORDS]; + uint32_t requested_buffer_size; + uint32_t unique_id; +}; + +/** + * struct mpt2_diag_unregister - application unregister with driver + * @hdr - generic header + * @unique_id - tag uniquely identifies the buffer to be unregistered + * + * This will allow the driver to cleanup any memory allocated for diag + * messages and to free up any resources. + */ +struct mpt2_diag_unregister { + struct mpt2_ioctl_header hdr; + uint32_t unique_id; +}; + +/** + * struct mpt2_diag_query - query relevant info associated with diag buffers + * @hdr - generic header + * @reserved - + * @buffer_type - specifies either TRACE or SNAPSHOT + * @application_flags - misc flags + * @diagnostic_flags - specifies flags affecting command processing + * @product_specific - product specific information + * @total_buffer_size - diag buffer size in bytes + * @driver_added_buffer_size - size of extra space appended to end of buffer + * @unique_id - unique id associated with this buffer. + * + * The application will send only buffer_type and unique_id. Driver will + * inspect unique_id first, if valid, fill in all the info. If unique_id is + * 0x00, the driver will return info specified by Buffer Type. + */ +struct mpt2_diag_query { + struct mpt2_ioctl_header hdr; + uint8_t reserved; + uint8_t buffer_type; + uint16_t application_flags; + uint32_t diagnostic_flags; + uint32_t product_specific[MPT2_PRODUCT_SPECIFIC_DWORDS]; + uint32_t total_buffer_size; + uint32_t driver_added_buffer_size; + uint32_t unique_id; +}; + +/** + * struct mpt2_diag_release - request to send Diag Release Message to firmware + * @hdr - generic header + * @unique_id - tag uniquely identifies the buffer to be released + * + * This allows ownership of the specified buffer to returned to the driver, + * allowing an application to read the buffer without fear that firmware is + * overwritting information in the buffer. + */ +struct mpt2_diag_release { + struct mpt2_ioctl_header hdr; + uint32_t unique_id; +}; + +/** + * struct mpt2_diag_read_buffer - request for copy of the diag buffer + * @hdr - generic header + * @status - + * @reserved - + * @flags - misc flags + * @starting_offset - starting offset within drivers buffer where to start + * reading data at into the specified application buffer + * @bytes_to_read - number of bytes to copy from the drivers buffer into the + * application buffer starting at starting_offset. + * @unique_id - unique id associated with this buffer. + * @diagnostic_data - data payload + */ +struct mpt2_diag_read_buffer { + struct mpt2_ioctl_header hdr; + uint8_t status; + uint8_t reserved; + uint16_t flags; + uint32_t starting_offset; + uint32_t bytes_to_read; + uint32_t unique_id; + uint32_t diagnostic_data[1]; +}; + +#endif /* MPT2SAS_CTL_H_INCLUDED */ diff --git a/drivers/scsi/mpt2sas/mpt2sas_debug.h b/drivers/scsi/mpt2sas/mpt2sas_debug.h new file mode 100644 index 000000000000..ad325096e842 --- /dev/null +++ b/drivers/scsi/mpt2sas/mpt2sas_debug.h @@ -0,0 +1,181 @@ +/* + * Logging Support for MPT (Message Passing Technology) based controllers + * + * This code is based on drivers/scsi/mpt2sas/mpt2_debug.c + * Copyright (C) 2007-2008 LSI Corporation + * (mailto:DL-MPTFusionLinux@lsi.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. + * + * 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. + * + * NO WARRANTY + * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + * solely responsible for determining the appropriateness of using and + * distributing the Program and assumes all risks associated with its + * exercise of rights under this Agreement, including but not limited to + * the risks and costs of program errors, damage to or loss of data, + * programs or equipment, and unavailability or interruption of operations. + + * DISCLAIMER OF LIABILITY + * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#ifndef MPT2SAS_DEBUG_H_INCLUDED +#define MPT2SAS_DEBUG_H_INCLUDED + +#define MPT_DEBUG 0x00000001 +#define MPT_DEBUG_MSG_FRAME 0x00000002 +#define MPT_DEBUG_SG 0x00000004 +#define MPT_DEBUG_EVENTS 0x00000008 +#define MPT_DEBUG_EVENT_WORK_TASK 0x00000010 +#define MPT_DEBUG_INIT 0x00000020 +#define MPT_DEBUG_EXIT 0x00000040 +#define MPT_DEBUG_FAIL 0x00000080 +#define MPT_DEBUG_TM 0x00000100 +#define MPT_DEBUG_REPLY 0x00000200 +#define MPT_DEBUG_HANDSHAKE 0x00000400 +#define MPT_DEBUG_CONFIG 0x00000800 +#define MPT_DEBUG_DL 0x00001000 +#define MPT_DEBUG_RESET 0x00002000 +#define MPT_DEBUG_SCSI 0x00004000 +#define MPT_DEBUG_IOCTL 0x00008000 +#define MPT_DEBUG_CSMISAS 0x00010000 +#define MPT_DEBUG_SAS 0x00020000 +#define MPT_DEBUG_TRANSPORT 0x00040000 +#define MPT_DEBUG_TASK_SET_FULL 0x00080000 + +#define MPT_DEBUG_TARGET_MODE 0x00100000 + + +/* + * CONFIG_SCSI_MPT2SAS_LOGGING - enabled in Kconfig + */ + +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING +#define MPT_CHECK_LOGGING(IOC, CMD, BITS) \ +{ \ + if (IOC->logging_level & BITS) \ + CMD; \ +} +#else +#define MPT_CHECK_LOGGING(IOC, CMD, BITS) +#endif /* CONFIG_SCSI_MPT2SAS_LOGGING */ + + +/* + * debug macros + */ + +#define dprintk(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG) + +#define dsgprintk(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SG) + +#define devtprintk(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_EVENTS) + +#define dewtprintk(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_EVENT_WORK_TASK) + +#define dinitprintk(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_INIT) + +#define dexitprintk(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_EXIT) + +#define dfailprintk(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_FAIL) + +#define dtmprintk(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TM) + +#define dreplyprintk(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_REPLY) + +#define dhsprintk(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_HANDSHAKE) + +#define dcprintk(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_CONFIG) + +#define ddlprintk(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_DL) + +#define drsprintk(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_RESET) + +#define dsprintk(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SCSI) + +#define dctlprintk(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_IOCTL) + +#define dcsmisasprintk(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_CSMISAS) + +#define dsasprintk(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SAS) + +#define dsastransport(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_SAS_WIDE) + +#define dmfprintk(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_MSG_FRAME) + +#define dtsfprintk(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TASK_SET_FULL) + +#define dtransportprintk(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TRANSPORT) + +#define dTMprintk(IOC, CMD) \ + MPT_CHECK_LOGGING(IOC, CMD, MPT_DEBUG_TARGET_MODE) + +/* inline functions for dumping debug data*/ +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING +/** + * _debug_dump_mf - print message frame contents + * @mpi_request: pointer to message frame + * @sz: number of dwords + */ +static inline void +_debug_dump_mf(void *mpi_request, int sz) +{ + int i; + u32 *mfp = (u32 *)mpi_request; + + printk(KERN_INFO "mf:\n\t"); + for (i = 0; i < sz; i++) { + if (i && ((i % 8) == 0)) + printk("\n\t"); + printk("%08x ", le32_to_cpu(mfp[i])); + } + printk("\n"); +} +#else +#define _debug_dump_mf(mpi_request, sz) +#endif /* CONFIG_SCSI_MPT2SAS_LOGGING */ + +#endif /* MPT2SAS_DEBUG_H_INCLUDED */ diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c new file mode 100644 index 000000000000..50865a8e1a4f --- /dev/null +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -0,0 +1,5687 @@ +/* + * Scsi Host Layer for MPT (Message Passing Technology) based controllers + * + * This code is based on drivers/scsi/mpt2sas/mpt2_scsih.c + * Copyright (C) 2007-2008 LSI Corporation + * (mailto:DL-MPTFusionLinux@lsi.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. + * + * 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. + * + * NO WARRANTY + * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + * solely responsible for determining the appropriateness of using and + * distributing the Program and assumes all risks associated with its + * exercise of rights under this Agreement, including but not limited to + * the risks and costs of program errors, damage to or loss of data, + * programs or equipment, and unavailability or interruption of operations. + + * DISCLAIMER OF LIABILITY + * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/blkdev.h> +#include <linux/sched.h> +#include <linux/workqueue.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/interrupt.h> + +#include "mpt2sas_base.h" + +MODULE_AUTHOR(MPT2SAS_AUTHOR); +MODULE_DESCRIPTION(MPT2SAS_DESCRIPTION); +MODULE_LICENSE("GPL"); +MODULE_VERSION(MPT2SAS_DRIVER_VERSION); + +#define RAID_CHANNEL 1 + +/* forward proto's */ +static void _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc, + struct _sas_node *sas_expander); +static void _firmware_event_work(struct work_struct *work); + +/* global parameters */ +LIST_HEAD(ioc_list); + +/* local parameters */ +static u32 logging_level; +static u8 scsi_io_cb_idx = -1; +static u8 tm_cb_idx = -1; +static u8 ctl_cb_idx = -1; +static u8 base_cb_idx = -1; +static u8 transport_cb_idx = -1; +static u8 config_cb_idx = -1; +static int mpt_ids; + +/* command line options */ +MODULE_PARM_DESC(logging_level, " bits for enabling additional logging info " + "(default=0)"); + +/* scsi-mid layer global parmeter is max_report_luns, which is 511 */ +#define MPT2SAS_MAX_LUN (16895) +static int max_lun = MPT2SAS_MAX_LUN; +module_param(max_lun, int, 0); +MODULE_PARM_DESC(max_lun, " max lun, default=16895 "); + +/** + * struct sense_info - common structure for obtaining sense keys + * @skey: sense key + * @asc: additional sense code + * @ascq: additional sense code qualifier + */ +struct sense_info { + u8 skey; + u8 asc; + u8 ascq; +}; + + +#define MPT2SAS_RESCAN_AFTER_HOST_RESET (0xFFFF) +/** + * struct fw_event_work - firmware event struct + * @list: link list framework + * @work: work object (ioc->fault_reset_work_q) + * @ioc: per adapter object + * @VF_ID: virtual function id + * @host_reset_handling: handling events during host reset + * @ignore: flag meaning this event has been marked to ignore + * @event: firmware event MPI2_EVENT_XXX defined in mpt2_ioc.h + * @event_data: reply event data payload follows + * + * This object stored on ioc->fw_event_list. + */ +struct fw_event_work { + struct list_head list; + struct delayed_work work; + struct MPT2SAS_ADAPTER *ioc; + u8 VF_ID; + u8 host_reset_handling; + u8 ignore; + u16 event; + void *event_data; +}; + +/** + * struct _scsi_io_transfer - scsi io transfer + * @handle: sas device handle (assigned by firmware) + * @is_raid: flag set for hidden raid components + * @dir: DMA_TO_DEVICE, DMA_FROM_DEVICE, + * @data_length: data transfer length + * @data_dma: dma pointer to data + * @sense: sense data + * @lun: lun number + * @cdb_length: cdb length + * @cdb: cdb contents + * @valid_reply: flag set for reply message + * @timeout: timeout for this command + * @sense_length: sense length + * @ioc_status: ioc status + * @scsi_state: scsi state + * @scsi_status: scsi staus + * @log_info: log information + * @transfer_length: data length transfer when there is a reply message + * + * Used for sending internal scsi commands to devices within this module. + * Refer to _scsi_send_scsi_io(). + */ +struct _scsi_io_transfer { + u16 handle; + u8 is_raid; + enum dma_data_direction dir; + u32 data_length; + dma_addr_t data_dma; + u8 sense[SCSI_SENSE_BUFFERSIZE]; + u32 lun; + u8 cdb_length; + u8 cdb[32]; + u8 timeout; + u8 valid_reply; + /* the following bits are only valid when 'valid_reply = 1' */ + u32 sense_length; + u16 ioc_status; + u8 scsi_state; + u8 scsi_status; + u32 log_info; + u32 transfer_length; +}; + +/* + * The pci device ids are defined in mpi/mpi2_cnfg.h. + */ +static struct pci_device_id scsih_pci_table[] = { + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2004, + PCI_ANY_ID, PCI_ANY_ID }, + /* Falcon ~ 2008*/ + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2008, + PCI_ANY_ID, PCI_ANY_ID }, + /* Liberator ~ 2108 */ + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_1, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_2, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_3, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_1, + PCI_ANY_ID, PCI_ANY_ID }, + { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_2, + PCI_ANY_ID, PCI_ANY_ID }, + {0} /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(pci, scsih_pci_table); + +/** + * scsih_set_debug_level - global setting of ioc->logging_level. + * + * Note: The logging levels are defined in mpt2sas_debug.h. + */ +static int +scsih_set_debug_level(const char *val, struct kernel_param *kp) +{ + int ret = param_set_int(val, kp); + struct MPT2SAS_ADAPTER *ioc; + + if (ret) + return ret; + + printk(KERN_INFO "setting logging_level(0x%08x)\n", logging_level); + list_for_each_entry(ioc, &ioc_list, list) + ioc->logging_level = logging_level; + return 0; +} +module_param_call(logging_level, scsih_set_debug_level, param_get_int, + &logging_level, 0644); + +/** + * _scsih_srch_boot_sas_address - search based on sas_address + * @sas_address: sas address + * @boot_device: boot device object from bios page 2 + * + * Returns 1 when there's a match, 0 means no match. + */ +static inline int +_scsih_srch_boot_sas_address(u64 sas_address, + Mpi2BootDeviceSasWwid_t *boot_device) +{ + return (sas_address == le64_to_cpu(boot_device->SASAddress)) ? 1 : 0; +} + +/** + * _scsih_srch_boot_device_name - search based on device name + * @device_name: device name specified in INDENTIFY fram + * @boot_device: boot device object from bios page 2 + * + * Returns 1 when there's a match, 0 means no match. + */ +static inline int +_scsih_srch_boot_device_name(u64 device_name, + Mpi2BootDeviceDeviceName_t *boot_device) +{ + return (device_name == le64_to_cpu(boot_device->DeviceName)) ? 1 : 0; +} + +/** + * _scsih_srch_boot_encl_slot - search based on enclosure_logical_id/slot + * @enclosure_logical_id: enclosure logical id + * @slot_number: slot number + * @boot_device: boot device object from bios page 2 + * + * Returns 1 when there's a match, 0 means no match. + */ +static inline int +_scsih_srch_boot_encl_slot(u64 enclosure_logical_id, u16 slot_number, + Mpi2BootDeviceEnclosureSlot_t *boot_device) +{ + return (enclosure_logical_id == le64_to_cpu(boot_device-> + EnclosureLogicalID) && slot_number == le16_to_cpu(boot_device-> + SlotNumber)) ? 1 : 0; +} + +/** + * _scsih_is_boot_device - search for matching boot device. + * @sas_address: sas address + * @device_name: device name specified in INDENTIFY fram + * @enclosure_logical_id: enclosure logical id + * @slot_number: slot number + * @form: specifies boot device form + * @boot_device: boot device object from bios page 2 + * + * Returns 1 when there's a match, 0 means no match. + */ +static int +_scsih_is_boot_device(u64 sas_address, u64 device_name, + u64 enclosure_logical_id, u16 slot, u8 form, + Mpi2BiosPage2BootDevice_t *boot_device) +{ + int rc = 0; + + switch (form) { + case MPI2_BIOSPAGE2_FORM_SAS_WWID: + if (!sas_address) + break; + rc = _scsih_srch_boot_sas_address( + sas_address, &boot_device->SasWwid); + break; + case MPI2_BIOSPAGE2_FORM_ENCLOSURE_SLOT: + if (!enclosure_logical_id) + break; + rc = _scsih_srch_boot_encl_slot( + enclosure_logical_id, + slot, &boot_device->EnclosureSlot); + break; + case MPI2_BIOSPAGE2_FORM_DEVICE_NAME: + if (!device_name) + break; + rc = _scsih_srch_boot_device_name( + device_name, &boot_device->DeviceName); + break; + case MPI2_BIOSPAGE2_FORM_NO_DEVICE_SPECIFIED: + break; + } + + return rc; +} + +/** + * _scsih_determine_boot_device - determine boot device. + * @ioc: per adapter object + * @device: either sas_device or raid_device object + * @is_raid: [flag] 1 = raid object, 0 = sas object + * + * Determines whether this device should be first reported device to + * to scsi-ml or sas transport, this purpose is for persistant boot device. + * There are primary, alternate, and current entries in bios page 2. The order + * priority is primary, alternate, then current. This routine saves + * the corresponding device object and is_raid flag in the ioc object. + * The saved data to be used later in _scsih_probe_boot_devices(). + */ +static void +_scsih_determine_boot_device(struct MPT2SAS_ADAPTER *ioc, + void *device, u8 is_raid) +{ + struct _sas_device *sas_device; + struct _raid_device *raid_device; + u64 sas_address; + u64 device_name; + u64 enclosure_logical_id; + u16 slot; + + /* only process this function when driver loads */ + if (!ioc->wait_for_port_enable_to_complete) + return; + + if (!is_raid) { + sas_device = device; + sas_address = sas_device->sas_address; + device_name = sas_device->device_name; + enclosure_logical_id = sas_device->enclosure_logical_id; + slot = sas_device->slot; + } else { + raid_device = device; + sas_address = raid_device->wwid; + device_name = 0; + enclosure_logical_id = 0; + slot = 0; + } + + if (!ioc->req_boot_device.device) { + if (_scsih_is_boot_device(sas_address, device_name, + enclosure_logical_id, slot, + (ioc->bios_pg2.ReqBootDeviceForm & + MPI2_BIOSPAGE2_FORM_MASK), + &ioc->bios_pg2.RequestedBootDevice)) { + dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT + "%s: req_boot_device(0x%016llx)\n", + ioc->name, __func__, + (unsigned long long)sas_address)); + ioc->req_boot_device.device = device; + ioc->req_boot_device.is_raid = is_raid; + } + } + + if (!ioc->req_alt_boot_device.device) { + if (_scsih_is_boot_device(sas_address, device_name, + enclosure_logical_id, slot, + (ioc->bios_pg2.ReqAltBootDeviceForm & + MPI2_BIOSPAGE2_FORM_MASK), + &ioc->bios_pg2.RequestedAltBootDevice)) { + dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT + "%s: req_alt_boot_device(0x%016llx)\n", + ioc->name, __func__, + (unsigned long long)sas_address)); + ioc->req_alt_boot_device.device = device; + ioc->req_alt_boot_device.is_raid = is_raid; + } + } + + if (!ioc->current_boot_device.device) { + if (_scsih_is_boot_device(sas_address, device_name, + enclosure_logical_id, slot, + (ioc->bios_pg2.CurrentBootDeviceForm & + MPI2_BIOSPAGE2_FORM_MASK), + &ioc->bios_pg2.CurrentBootDevice)) { + dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT + "%s: current_boot_device(0x%016llx)\n", + ioc->name, __func__, + (unsigned long long)sas_address)); + ioc->current_boot_device.device = device; + ioc->current_boot_device.is_raid = is_raid; + } + } +} + +/** + * mpt2sas_scsih_sas_device_find_by_sas_address - sas device search + * @ioc: per adapter object + * @sas_address: sas address + * Context: Calling function should acquire ioc->sas_device_lock + * + * This searches for sas_device based on sas_address, then return sas_device + * object. + */ +struct _sas_device * +mpt2sas_scsih_sas_device_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc, + u64 sas_address) +{ + struct _sas_device *sas_device, *r; + + r = NULL; + /* check the sas_device_init_list */ + list_for_each_entry(sas_device, &ioc->sas_device_init_list, + list) { + if (sas_device->sas_address != sas_address) + continue; + r = sas_device; + goto out; + } + + /* then check the sas_device_list */ + list_for_each_entry(sas_device, &ioc->sas_device_list, list) { + if (sas_device->sas_address != sas_address) + continue; + r = sas_device; + goto out; + } + out: + return r; +} + +/** + * _scsih_sas_device_find_by_handle - sas device search + * @ioc: per adapter object + * @handle: sas device handle (assigned by firmware) + * Context: Calling function should acquire ioc->sas_device_lock + * + * This searches for sas_device based on sas_address, then return sas_device + * object. + */ +static struct _sas_device * +_scsih_sas_device_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + struct _sas_device *sas_device, *r; + + r = NULL; + if (ioc->wait_for_port_enable_to_complete) { + list_for_each_entry(sas_device, &ioc->sas_device_init_list, + list) { + if (sas_device->handle != handle) + continue; + r = sas_device; + goto out; + } + } else { + list_for_each_entry(sas_device, &ioc->sas_device_list, list) { + if (sas_device->handle != handle) + continue; + r = sas_device; + goto out; + } + } + + out: + return r; +} + +/** + * _scsih_sas_device_remove - remove sas_device from list. + * @ioc: per adapter object + * @sas_device: the sas_device object + * Context: This function will acquire ioc->sas_device_lock. + * + * Removing object and freeing associated memory from the ioc->sas_device_list. + */ +static void +_scsih_sas_device_remove(struct MPT2SAS_ADAPTER *ioc, + struct _sas_device *sas_device) +{ + unsigned long flags; + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + list_del(&sas_device->list); + memset(sas_device, 0, sizeof(struct _sas_device)); + kfree(sas_device); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); +} + +/** + * _scsih_sas_device_add - insert sas_device to the list. + * @ioc: per adapter object + * @sas_device: the sas_device object + * Context: This function will acquire ioc->sas_device_lock. + * + * Adding new object to the ioc->sas_device_list. + */ +static void +_scsih_sas_device_add(struct MPT2SAS_ADAPTER *ioc, + struct _sas_device *sas_device) +{ + unsigned long flags; + u16 handle, parent_handle; + u64 sas_address; + + dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: handle" + "(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__, + sas_device->handle, (unsigned long long)sas_device->sas_address)); + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + list_add_tail(&sas_device->list, &ioc->sas_device_list); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + handle = sas_device->handle; + parent_handle = sas_device->parent_handle; + sas_address = sas_device->sas_address; + if (!mpt2sas_transport_port_add(ioc, handle, parent_handle)) { + _scsih_sas_device_remove(ioc, sas_device); + } else if (!sas_device->starget) { + mpt2sas_transport_port_remove(ioc, sas_address, parent_handle); + _scsih_sas_device_remove(ioc, sas_device); + } +} + +/** + * _scsih_sas_device_init_add - insert sas_device to the list. + * @ioc: per adapter object + * @sas_device: the sas_device object + * Context: This function will acquire ioc->sas_device_lock. + * + * Adding new object at driver load time to the ioc->sas_device_init_list. + */ +static void +_scsih_sas_device_init_add(struct MPT2SAS_ADAPTER *ioc, + struct _sas_device *sas_device) +{ + unsigned long flags; + + dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: handle" + "(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__, + sas_device->handle, (unsigned long long)sas_device->sas_address)); + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + list_add_tail(&sas_device->list, &ioc->sas_device_init_list); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + _scsih_determine_boot_device(ioc, sas_device, 0); +} + +/** + * mpt2sas_scsih_expander_find_by_handle - expander device search + * @ioc: per adapter object + * @handle: expander handle (assigned by firmware) + * Context: Calling function should acquire ioc->sas_device_lock + * + * This searches for expander device based on handle, then returns the + * sas_node object. + */ +struct _sas_node * +mpt2sas_scsih_expander_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + struct _sas_node *sas_expander, *r; + + r = NULL; + list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) { + if (sas_expander->handle != handle) + continue; + r = sas_expander; + goto out; + } + out: + return r; +} + +/** + * _scsih_raid_device_find_by_id - raid device search + * @ioc: per adapter object + * @id: sas device target id + * @channel: sas device channel + * Context: Calling function should acquire ioc->raid_device_lock + * + * This searches for raid_device based on target id, then return raid_device + * object. + */ +static struct _raid_device * +_scsih_raid_device_find_by_id(struct MPT2SAS_ADAPTER *ioc, int id, int channel) +{ + struct _raid_device *raid_device, *r; + + r = NULL; + list_for_each_entry(raid_device, &ioc->raid_device_list, list) { + if (raid_device->id == id && raid_device->channel == channel) { + r = raid_device; + goto out; + } + } + + out: + return r; +} + +/** + * _scsih_raid_device_find_by_handle - raid device search + * @ioc: per adapter object + * @handle: sas device handle (assigned by firmware) + * Context: Calling function should acquire ioc->raid_device_lock + * + * This searches for raid_device based on handle, then return raid_device + * object. + */ +static struct _raid_device * +_scsih_raid_device_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + struct _raid_device *raid_device, *r; + + r = NULL; + list_for_each_entry(raid_device, &ioc->raid_device_list, list) { + if (raid_device->handle != handle) + continue; + r = raid_device; + goto out; + } + + out: + return r; +} + +/** + * _scsih_raid_device_find_by_wwid - raid device search + * @ioc: per adapter object + * @handle: sas device handle (assigned by firmware) + * Context: Calling function should acquire ioc->raid_device_lock + * + * This searches for raid_device based on wwid, then return raid_device + * object. + */ +static struct _raid_device * +_scsih_raid_device_find_by_wwid(struct MPT2SAS_ADAPTER *ioc, u64 wwid) +{ + struct _raid_device *raid_device, *r; + + r = NULL; + list_for_each_entry(raid_device, &ioc->raid_device_list, list) { + if (raid_device->wwid != wwid) + continue; + r = raid_device; + goto out; + } + + out: + return r; +} + +/** + * _scsih_raid_device_add - add raid_device object + * @ioc: per adapter object + * @raid_device: raid_device object + * + * This is added to the raid_device_list link list. + */ +static void +_scsih_raid_device_add(struct MPT2SAS_ADAPTER *ioc, + struct _raid_device *raid_device) +{ + unsigned long flags; + + dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: handle" + "(0x%04x), wwid(0x%016llx)\n", ioc->name, __func__, + raid_device->handle, (unsigned long long)raid_device->wwid)); + + spin_lock_irqsave(&ioc->raid_device_lock, flags); + list_add_tail(&raid_device->list, &ioc->raid_device_list); + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); +} + +/** + * _scsih_raid_device_remove - delete raid_device object + * @ioc: per adapter object + * @raid_device: raid_device object + * + * This is removed from the raid_device_list link list. + */ +static void +_scsih_raid_device_remove(struct MPT2SAS_ADAPTER *ioc, + struct _raid_device *raid_device) +{ + unsigned long flags; + + spin_lock_irqsave(&ioc->raid_device_lock, flags); + list_del(&raid_device->list); + memset(raid_device, 0, sizeof(struct _raid_device)); + kfree(raid_device); + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); +} + +/** + * mpt2sas_scsih_expander_find_by_sas_address - expander device search + * @ioc: per adapter object + * @sas_address: sas address + * Context: Calling function should acquire ioc->sas_node_lock. + * + * This searches for expander device based on sas_address, then returns the + * sas_node object. + */ +struct _sas_node * +mpt2sas_scsih_expander_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc, + u64 sas_address) +{ + struct _sas_node *sas_expander, *r; + + r = NULL; + list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) { + if (sas_expander->sas_address != sas_address) + continue; + r = sas_expander; + goto out; + } + out: + return r; +} + +/** + * _scsih_expander_node_add - insert expander device to the list. + * @ioc: per adapter object + * @sas_expander: the sas_device object + * Context: This function will acquire ioc->sas_node_lock. + * + * Adding new object to the ioc->sas_expander_list. + * + * Return nothing. + */ +static void +_scsih_expander_node_add(struct MPT2SAS_ADAPTER *ioc, + struct _sas_node *sas_expander) +{ + unsigned long flags; + + spin_lock_irqsave(&ioc->sas_node_lock, flags); + list_add_tail(&sas_expander->list, &ioc->sas_expander_list); + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); +} + +/** + * _scsih_is_end_device - determines if device is an end device + * @device_info: bitfield providing information about the device. + * Context: none + * + * Returns 1 if end device. + */ +static int +_scsih_is_end_device(u32 device_info) +{ + if (device_info & MPI2_SAS_DEVICE_INFO_END_DEVICE && + ((device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) | + (device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET) | + (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE))) + return 1; + else + return 0; +} + +/** + * _scsih_scsi_lookup_get - returns scmd entry + * @ioc: per adapter object + * @smid: system request message index + * Context: This function will acquire ioc->scsi_lookup_lock. + * + * Returns the smid stored scmd pointer. + */ +static struct scsi_cmnd * +_scsih_scsi_lookup_get(struct MPT2SAS_ADAPTER *ioc, u16 smid) +{ + unsigned long flags; + struct scsi_cmnd *scmd; + + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + scmd = ioc->scsi_lookup[smid - 1].scmd; + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + return scmd; +} + +/** + * mptscsih_getclear_scsi_lookup - returns scmd entry + * @ioc: per adapter object + * @smid: system request message index + * Context: This function will acquire ioc->scsi_lookup_lock. + * + * Returns the smid stored scmd pointer, as well as clearing the scmd pointer. + */ +static struct scsi_cmnd * +_scsih_scsi_lookup_getclear(struct MPT2SAS_ADAPTER *ioc, u16 smid) +{ + unsigned long flags; + struct scsi_cmnd *scmd; + + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + scmd = ioc->scsi_lookup[smid - 1].scmd; + ioc->scsi_lookup[smid - 1].scmd = NULL; + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + return scmd; +} + +/** + * _scsih_scsi_lookup_set - updates scmd entry in lookup + * @ioc: per adapter object + * @smid: system request message index + * @scmd: pointer to scsi command object + * Context: This function will acquire ioc->scsi_lookup_lock. + * + * This will save scmd pointer in the scsi_lookup array. + * + * Return nothing. + */ +static void +_scsih_scsi_lookup_set(struct MPT2SAS_ADAPTER *ioc, u16 smid, + struct scsi_cmnd *scmd) +{ + unsigned long flags; + + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + ioc->scsi_lookup[smid - 1].scmd = scmd; + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); +} + +/** + * _scsih_scsi_lookup_find_by_scmd - scmd lookup + * @ioc: per adapter object + * @smid: system request message index + * @scmd: pointer to scsi command object + * Context: This function will acquire ioc->scsi_lookup_lock. + * + * This will search for a scmd pointer in the scsi_lookup array, + * returning the revelent smid. A returned value of zero means invalid. + */ +static u16 +_scsih_scsi_lookup_find_by_scmd(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd + *scmd) +{ + u16 smid; + unsigned long flags; + int i; + + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + smid = 0; + for (i = 0; i < ioc->request_depth; i++) { + if (ioc->scsi_lookup[i].scmd == scmd) { + smid = i + 1; + goto out; + } + } + out: + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + return smid; +} + +/** + * _scsih_scsi_lookup_find_by_target - search for matching channel:id + * @ioc: per adapter object + * @id: target id + * @channel: channel + * Context: This function will acquire ioc->scsi_lookup_lock. + * + * This will search for a matching channel:id in the scsi_lookup array, + * returning 1 if found. + */ +static u8 +_scsih_scsi_lookup_find_by_target(struct MPT2SAS_ADAPTER *ioc, int id, + int channel) +{ + u8 found; + unsigned long flags; + int i; + + spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); + found = 0; + for (i = 0 ; i < ioc->request_depth; i++) { + if (ioc->scsi_lookup[i].scmd && + (ioc->scsi_lookup[i].scmd->device->id == id && + ioc->scsi_lookup[i].scmd->device->channel == channel)) { + found = 1; + goto out; + } + } + out: + spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + return found; +} + +/** + * _scsih_get_chain_buffer_dma - obtain block of chains (dma address) + * @ioc: per adapter object + * @smid: system request message index + * + * Returns phys pointer to chain buffer. + */ +static dma_addr_t +_scsih_get_chain_buffer_dma(struct MPT2SAS_ADAPTER *ioc, u16 smid) +{ + return ioc->chain_dma + ((smid - 1) * (ioc->request_sz * + ioc->chains_needed_per_io)); +} + +/** + * _scsih_get_chain_buffer - obtain block of chains assigned to a mf request + * @ioc: per adapter object + * @smid: system request message index + * + * Returns virt pointer to chain buffer. + */ +static void * +_scsih_get_chain_buffer(struct MPT2SAS_ADAPTER *ioc, u16 smid) +{ + return (void *)(ioc->chain + ((smid - 1) * (ioc->request_sz * + ioc->chains_needed_per_io))); +} + +/** + * _scsih_build_scatter_gather - main sg creation routine + * @ioc: per adapter object + * @scmd: scsi command + * @smid: system request message index + * Context: none. + * + * The main routine that builds scatter gather table from a given + * scsi request sent via the .queuecommand main handler. + * + * Returns 0 success, anything else error + */ +static int +_scsih_build_scatter_gather(struct MPT2SAS_ADAPTER *ioc, + struct scsi_cmnd *scmd, u16 smid) +{ + Mpi2SCSIIORequest_t *mpi_request; + dma_addr_t chain_dma; + struct scatterlist *sg_scmd; + void *sg_local, *chain; + u32 chain_offset; + u32 chain_length; + u32 chain_flags; + u32 sges_left; + u32 sges_in_segment; + u32 sgl_flags; + u32 sgl_flags_last_element; + u32 sgl_flags_end_buffer; + + mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); + + /* init scatter gather flags */ + sgl_flags = MPI2_SGE_FLAGS_SIMPLE_ELEMENT; + if (scmd->sc_data_direction == DMA_TO_DEVICE) + sgl_flags |= MPI2_SGE_FLAGS_HOST_TO_IOC; + sgl_flags_last_element = (sgl_flags | MPI2_SGE_FLAGS_LAST_ELEMENT) + << MPI2_SGE_FLAGS_SHIFT; + sgl_flags_end_buffer = (sgl_flags | MPI2_SGE_FLAGS_LAST_ELEMENT | + MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_END_OF_LIST) + << MPI2_SGE_FLAGS_SHIFT; + sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; + + sg_scmd = scsi_sglist(scmd); + sges_left = scsi_dma_map(scmd); + if (!sges_left) { + sdev_printk(KERN_ERR, scmd->device, "pci_map_sg" + " failed: request for %d bytes!\n", scsi_bufflen(scmd)); + return -ENOMEM; + } + + sg_local = &mpi_request->SGL; + sges_in_segment = ioc->max_sges_in_main_message; + if (sges_left <= sges_in_segment) + goto fill_in_last_segment; + + mpi_request->ChainOffset = (offsetof(Mpi2SCSIIORequest_t, SGL) + + (sges_in_segment * ioc->sge_size))/4; + + /* fill in main message segment when there is a chain following */ + while (sges_in_segment) { + if (sges_in_segment == 1) + ioc->base_add_sg_single(sg_local, + sgl_flags_last_element | sg_dma_len(sg_scmd), + sg_dma_address(sg_scmd)); + else + ioc->base_add_sg_single(sg_local, sgl_flags | + sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); + sg_scmd = sg_next(sg_scmd); + sg_local += ioc->sge_size; + sges_left--; + sges_in_segment--; + } + + /* initializing the chain flags and pointers */ + chain_flags = MPI2_SGE_FLAGS_CHAIN_ELEMENT << MPI2_SGE_FLAGS_SHIFT; + chain = _scsih_get_chain_buffer(ioc, smid); + chain_dma = _scsih_get_chain_buffer_dma(ioc, smid); + do { + sges_in_segment = (sges_left <= + ioc->max_sges_in_chain_message) ? sges_left : + ioc->max_sges_in_chain_message; + chain_offset = (sges_left == sges_in_segment) ? + 0 : (sges_in_segment * ioc->sge_size)/4; + chain_length = sges_in_segment * ioc->sge_size; + if (chain_offset) { + chain_offset = chain_offset << + MPI2_SGE_CHAIN_OFFSET_SHIFT; + chain_length += ioc->sge_size; + } + ioc->base_add_sg_single(sg_local, chain_flags | chain_offset | + chain_length, chain_dma); + sg_local = chain; + if (!chain_offset) + goto fill_in_last_segment; + + /* fill in chain segments */ + while (sges_in_segment) { + if (sges_in_segment == 1) + ioc->base_add_sg_single(sg_local, + sgl_flags_last_element | + sg_dma_len(sg_scmd), + sg_dma_address(sg_scmd)); + else + ioc->base_add_sg_single(sg_local, sgl_flags | + sg_dma_len(sg_scmd), + sg_dma_address(sg_scmd)); + sg_scmd = sg_next(sg_scmd); + sg_local += ioc->sge_size; + sges_left--; + sges_in_segment--; + } + + chain_dma += ioc->request_sz; + chain += ioc->request_sz; + } while (1); + + + fill_in_last_segment: + + /* fill the last segment */ + while (sges_left) { + if (sges_left == 1) + ioc->base_add_sg_single(sg_local, sgl_flags_end_buffer | + sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); + else + ioc->base_add_sg_single(sg_local, sgl_flags | + sg_dma_len(sg_scmd), sg_dma_address(sg_scmd)); + sg_scmd = sg_next(sg_scmd); + sg_local += ioc->sge_size; + sges_left--; + } + + return 0; +} + +/** + * scsih_change_queue_depth - setting device queue depth + * @sdev: scsi device struct + * @qdepth: requested queue depth + * + * Returns queue depth. + */ +static int +scsih_change_queue_depth(struct scsi_device *sdev, int qdepth) +{ + struct Scsi_Host *shost = sdev->host; + int max_depth; + int tag_type; + + max_depth = shost->can_queue; + if (!sdev->tagged_supported) + max_depth = 1; + if (qdepth > max_depth) + qdepth = max_depth; + tag_type = (qdepth == 1) ? 0 : MSG_SIMPLE_TAG; + scsi_adjust_queue_depth(sdev, tag_type, qdepth); + + if (sdev->inquiry_len > 7) + sdev_printk(KERN_INFO, sdev, "qdepth(%d), tagged(%d), " + "simple(%d), ordered(%d), scsi_level(%d), cmd_que(%d)\n", + sdev->queue_depth, sdev->tagged_supported, sdev->simple_tags, + sdev->ordered_tags, sdev->scsi_level, + (sdev->inquiry[7] & 2) >> 1); + + return sdev->queue_depth; +} + +/** + * scsih_change_queue_depth - changing device queue tag type + * @sdev: scsi device struct + * @tag_type: requested tag type + * + * Returns queue tag type. + */ +static int +scsih_change_queue_type(struct scsi_device *sdev, int tag_type) +{ + if (sdev->tagged_supported) { + scsi_set_tag_type(sdev, tag_type); + if (tag_type) + scsi_activate_tcq(sdev, sdev->queue_depth); + else + scsi_deactivate_tcq(sdev, sdev->queue_depth); + } else + tag_type = 0; + + return tag_type; +} + +/** + * scsih_target_alloc - target add routine + * @starget: scsi target struct + * + * Returns 0 if ok. Any other return is assumed to be an error and + * the device is ignored. + */ +static int +scsih_target_alloc(struct scsi_target *starget) +{ + struct Scsi_Host *shost = dev_to_shost(&starget->dev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + struct MPT2SAS_TARGET *sas_target_priv_data; + struct _sas_device *sas_device; + struct _raid_device *raid_device; + unsigned long flags; + struct sas_rphy *rphy; + + sas_target_priv_data = kzalloc(sizeof(struct scsi_target), GFP_KERNEL); + if (!sas_target_priv_data) + return -ENOMEM; + + starget->hostdata = sas_target_priv_data; + sas_target_priv_data->starget = starget; + sas_target_priv_data->handle = MPT2SAS_INVALID_DEVICE_HANDLE; + + /* RAID volumes */ + if (starget->channel == RAID_CHANNEL) { + spin_lock_irqsave(&ioc->raid_device_lock, flags); + raid_device = _scsih_raid_device_find_by_id(ioc, starget->id, + starget->channel); + if (raid_device) { + sas_target_priv_data->handle = raid_device->handle; + sas_target_priv_data->sas_address = raid_device->wwid; + sas_target_priv_data->flags |= MPT_TARGET_FLAGS_VOLUME; + raid_device->starget = starget; + } + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); + return 0; + } + + /* sas/sata devices */ + spin_lock_irqsave(&ioc->sas_device_lock, flags); + rphy = dev_to_rphy(starget->dev.parent); + sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + rphy->identify.sas_address); + + if (sas_device) { + sas_target_priv_data->handle = sas_device->handle; + sas_target_priv_data->sas_address = sas_device->sas_address; + sas_device->starget = starget; + sas_device->id = starget->id; + sas_device->channel = starget->channel; + if (sas_device->hidden_raid_component) + sas_target_priv_data->flags |= + MPT_TARGET_FLAGS_RAID_COMPONENT; + } + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + return 0; +} + +/** + * scsih_target_destroy - target destroy routine + * @starget: scsi target struct + * + * Returns nothing. + */ +static void +scsih_target_destroy(struct scsi_target *starget) +{ + struct Scsi_Host *shost = dev_to_shost(&starget->dev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + struct MPT2SAS_TARGET *sas_target_priv_data; + struct _sas_device *sas_device; + struct _raid_device *raid_device; + unsigned long flags; + struct sas_rphy *rphy; + + sas_target_priv_data = starget->hostdata; + if (!sas_target_priv_data) + return; + + if (starget->channel == RAID_CHANNEL) { + spin_lock_irqsave(&ioc->raid_device_lock, flags); + raid_device = _scsih_raid_device_find_by_id(ioc, starget->id, + starget->channel); + if (raid_device) { + raid_device->starget = NULL; + raid_device->sdev = NULL; + } + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); + goto out; + } + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + rphy = dev_to_rphy(starget->dev.parent); + sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + rphy->identify.sas_address); + if (sas_device) + sas_device->starget = NULL; + + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + out: + kfree(sas_target_priv_data); + starget->hostdata = NULL; +} + +/** + * scsih_slave_alloc - device add routine + * @sdev: scsi device struct + * + * Returns 0 if ok. Any other return is assumed to be an error and + * the device is ignored. + */ +static int +scsih_slave_alloc(struct scsi_device *sdev) +{ + struct Scsi_Host *shost; + struct MPT2SAS_ADAPTER *ioc; + struct MPT2SAS_TARGET *sas_target_priv_data; + struct MPT2SAS_DEVICE *sas_device_priv_data; + struct scsi_target *starget; + struct _raid_device *raid_device; + struct _sas_device *sas_device; + unsigned long flags; + + sas_device_priv_data = kzalloc(sizeof(struct scsi_device), GFP_KERNEL); + if (!sas_device_priv_data) + return -ENOMEM; + + sas_device_priv_data->lun = sdev->lun; + sas_device_priv_data->flags = MPT_DEVICE_FLAGS_INIT; + + starget = scsi_target(sdev); + sas_target_priv_data = starget->hostdata; + sas_target_priv_data->num_luns++; + sas_device_priv_data->sas_target = sas_target_priv_data; + sdev->hostdata = sas_device_priv_data; + if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT)) + sdev->no_uld_attach = 1; + + shost = dev_to_shost(&starget->dev); + ioc = shost_priv(shost); + if (starget->channel == RAID_CHANNEL) { + spin_lock_irqsave(&ioc->raid_device_lock, flags); + raid_device = _scsih_raid_device_find_by_id(ioc, + starget->id, starget->channel); + if (raid_device) + raid_device->sdev = sdev; /* raid is single lun */ + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); + } else { + /* set TLR bit for SSP devices */ + if (!(ioc->facts.IOCCapabilities & + MPI2_IOCFACTS_CAPABILITY_TLR)) + goto out; + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + sas_device_priv_data->sas_target->sas_address); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + if (sas_device && sas_device->device_info & + MPI2_SAS_DEVICE_INFO_SSP_TARGET) + sas_device_priv_data->flags |= MPT_DEVICE_TLR_ON; + } + + out: + return 0; +} + +/** + * scsih_slave_destroy - device destroy routine + * @sdev: scsi device struct + * + * Returns nothing. + */ +static void +scsih_slave_destroy(struct scsi_device *sdev) +{ + struct MPT2SAS_TARGET *sas_target_priv_data; + struct scsi_target *starget; + + if (!sdev->hostdata) + return; + + starget = scsi_target(sdev); + sas_target_priv_data = starget->hostdata; + sas_target_priv_data->num_luns--; + kfree(sdev->hostdata); + sdev->hostdata = NULL; +} + +/** + * scsih_display_sata_capabilities - sata capabilities + * @ioc: per adapter object + * @sas_device: the sas_device object + * @sdev: scsi device struct + */ +static void +scsih_display_sata_capabilities(struct MPT2SAS_ADAPTER *ioc, + struct _sas_device *sas_device, struct scsi_device *sdev) +{ + Mpi2ConfigReply_t mpi_reply; + Mpi2SasDevicePage0_t sas_device_pg0; + u32 ioc_status; + u16 flags; + u32 device_info; + + if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, + MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, sas_device->handle))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return; + } + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return; + } + + flags = le16_to_cpu(sas_device_pg0.Flags); + device_info = le16_to_cpu(sas_device_pg0.DeviceInfo); + + sdev_printk(KERN_INFO, sdev, + "atapi(%s), ncq(%s), asyn_notify(%s), smart(%s), fua(%s), " + "sw_preserve(%s)\n", + (device_info & MPI2_SAS_DEVICE_INFO_ATAPI_DEVICE) ? "y" : "n", + (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_NCQ_SUPPORTED) ? "y" : "n", + (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_ASYNCHRONOUS_NOTIFY) ? "y" : + "n", + (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_SMART_SUPPORTED) ? "y" : "n", + (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_FUA_SUPPORTED) ? "y" : "n", + (flags & MPI2_SAS_DEVICE0_FLAGS_SATA_SW_PRESERVE) ? "y" : "n"); +} + +/** + * _scsih_get_volume_capabilities - volume capabilities + * @ioc: per adapter object + * @sas_device: the raid_device object + */ +static void +_scsih_get_volume_capabilities(struct MPT2SAS_ADAPTER *ioc, + struct _raid_device *raid_device) +{ + Mpi2RaidVolPage0_t *vol_pg0; + Mpi2RaidPhysDiskPage0_t pd_pg0; + Mpi2SasDevicePage0_t sas_device_pg0; + Mpi2ConfigReply_t mpi_reply; + u16 sz; + u8 num_pds; + + if ((mpt2sas_config_get_number_pds(ioc, raid_device->handle, + &num_pds)) || !num_pds) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return; + } + + raid_device->num_pds = num_pds; + sz = offsetof(Mpi2RaidVolPage0_t, PhysDisk) + (num_pds * + sizeof(Mpi2RaidVol0PhysDisk_t)); + vol_pg0 = kzalloc(sz, GFP_KERNEL); + if (!vol_pg0) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return; + } + + if ((mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, vol_pg0, + MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle, sz))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + kfree(vol_pg0); + return; + } + + raid_device->volume_type = vol_pg0->VolumeType; + + /* figure out what the underlying devices are by + * obtaining the device_info bits for the 1st device + */ + if (!(mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply, + &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM, + vol_pg0->PhysDisk[0].PhysDiskNum))) { + if (!(mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, + &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, + le16_to_cpu(pd_pg0.DevHandle)))) { + raid_device->device_info = + le32_to_cpu(sas_device_pg0.DeviceInfo); + } + } + + kfree(vol_pg0); +} + +/** + * scsih_slave_configure - device configure routine. + * @sdev: scsi device struct + * + * Returns 0 if ok. Any other return is assumed to be an error and + * the device is ignored. + */ +static int +scsih_slave_configure(struct scsi_device *sdev) +{ + struct Scsi_Host *shost = sdev->host; + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + struct MPT2SAS_DEVICE *sas_device_priv_data; + struct MPT2SAS_TARGET *sas_target_priv_data; + struct _sas_device *sas_device; + struct _raid_device *raid_device; + unsigned long flags; + int qdepth; + u8 ssp_target = 0; + char *ds = ""; + char *r_level = ""; + + qdepth = 1; + sas_device_priv_data = sdev->hostdata; + sas_device_priv_data->configured_lun = 1; + sas_device_priv_data->flags &= ~MPT_DEVICE_FLAGS_INIT; + sas_target_priv_data = sas_device_priv_data->sas_target; + + /* raid volume handling */ + if (sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME) { + + spin_lock_irqsave(&ioc->raid_device_lock, flags); + raid_device = _scsih_raid_device_find_by_handle(ioc, + sas_target_priv_data->handle); + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); + if (!raid_device) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return 0; + } + + _scsih_get_volume_capabilities(ioc, raid_device); + + /* RAID Queue Depth Support + * IS volume = underlying qdepth of drive type, either + * MPT2SAS_SAS_QUEUE_DEPTH or MPT2SAS_SATA_QUEUE_DEPTH + * IM/IME/R10 = 128 (MPT2SAS_RAID_QUEUE_DEPTH) + */ + if (raid_device->device_info & + MPI2_SAS_DEVICE_INFO_SSP_TARGET) { + qdepth = MPT2SAS_SAS_QUEUE_DEPTH; + ds = "SSP"; + } else { + qdepth = MPT2SAS_SATA_QUEUE_DEPTH; + if (raid_device->device_info & + MPI2_SAS_DEVICE_INFO_SATA_DEVICE) + ds = "SATA"; + else + ds = "STP"; + } + + switch (raid_device->volume_type) { + case MPI2_RAID_VOL_TYPE_RAID0: + r_level = "RAID0"; + break; + case MPI2_RAID_VOL_TYPE_RAID1E: + qdepth = MPT2SAS_RAID_QUEUE_DEPTH; + r_level = "RAID1E"; + break; + case MPI2_RAID_VOL_TYPE_RAID1: + qdepth = MPT2SAS_RAID_QUEUE_DEPTH; + r_level = "RAID1"; + break; + case MPI2_RAID_VOL_TYPE_RAID10: + qdepth = MPT2SAS_RAID_QUEUE_DEPTH; + r_level = "RAID10"; + break; + case MPI2_RAID_VOL_TYPE_UNKNOWN: + default: + qdepth = MPT2SAS_RAID_QUEUE_DEPTH; + r_level = "RAIDX"; + break; + } + + sdev_printk(KERN_INFO, sdev, "%s: " + "handle(0x%04x), wwid(0x%016llx), pd_count(%d), type(%s)\n", + r_level, raid_device->handle, + (unsigned long long)raid_device->wwid, + raid_device->num_pds, ds); + scsih_change_queue_depth(sdev, qdepth); + return 0; + } + + /* non-raid handling */ + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + sas_device_priv_data->sas_target->sas_address); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + if (sas_device) { + if (sas_target_priv_data->flags & + MPT_TARGET_FLAGS_RAID_COMPONENT) { + mpt2sas_config_get_volume_handle(ioc, + sas_device->handle, &sas_device->volume_handle); + mpt2sas_config_get_volume_wwid(ioc, + sas_device->volume_handle, + &sas_device->volume_wwid); + } + if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) { + qdepth = MPT2SAS_SAS_QUEUE_DEPTH; + ssp_target = 1; + ds = "SSP"; + } else { + qdepth = MPT2SAS_SATA_QUEUE_DEPTH; + if (sas_device->device_info & + MPI2_SAS_DEVICE_INFO_STP_TARGET) + ds = "STP"; + else if (sas_device->device_info & + MPI2_SAS_DEVICE_INFO_SATA_DEVICE) + ds = "SATA"; + } + + sdev_printk(KERN_INFO, sdev, "%s: handle(0x%04x), " + "sas_addr(0x%016llx), device_name(0x%016llx)\n", + ds, sas_device->handle, + (unsigned long long)sas_device->sas_address, + (unsigned long long)sas_device->device_name); + sdev_printk(KERN_INFO, sdev, "%s: " + "enclosure_logical_id(0x%016llx), slot(%d)\n", ds, + (unsigned long long) sas_device->enclosure_logical_id, + sas_device->slot); + + if (!ssp_target) + scsih_display_sata_capabilities(ioc, sas_device, sdev); + } + + scsih_change_queue_depth(sdev, qdepth); + + if (ssp_target) + sas_read_port_mode_page(sdev); + return 0; +} + +/** + * scsih_bios_param - fetch head, sector, cylinder info for a disk + * @sdev: scsi device struct + * @bdev: pointer to block device context + * @capacity: device size (in 512 byte sectors) + * @params: three element array to place output: + * params[0] number of heads (max 255) + * params[1] number of sectors (max 63) + * params[2] number of cylinders + * + * Return nothing. + */ +static int +scsih_bios_param(struct scsi_device *sdev, struct block_device *bdev, + sector_t capacity, int params[]) +{ + int heads; + int sectors; + sector_t cylinders; + ulong dummy; + + heads = 64; + sectors = 32; + + dummy = heads * sectors; + cylinders = capacity; + sector_div(cylinders, dummy); + + /* + * Handle extended translation size for logical drives + * > 1Gb + */ + if ((ulong)capacity >= 0x200000) { + heads = 255; + sectors = 63; + dummy = heads * sectors; + cylinders = capacity; + sector_div(cylinders, dummy); + } + + /* return result */ + params[0] = heads; + params[1] = sectors; + params[2] = cylinders; + + return 0; +} + +/** + * _scsih_response_code - translation of device response code + * @ioc: per adapter object + * @response_code: response code returned by the device + * + * Return nothing. + */ +static void +_scsih_response_code(struct MPT2SAS_ADAPTER *ioc, u8 response_code) +{ + char *desc; + + switch (response_code) { + case MPI2_SCSITASKMGMT_RSP_TM_COMPLETE: + desc = "task management request completed"; + break; + case MPI2_SCSITASKMGMT_RSP_INVALID_FRAME: + desc = "invalid frame"; + break; + case MPI2_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED: + desc = "task management request not supported"; + break; + case MPI2_SCSITASKMGMT_RSP_TM_FAILED: + desc = "task management request failed"; + break; + case MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED: + desc = "task management request succeeded"; + break; + case MPI2_SCSITASKMGMT_RSP_TM_INVALID_LUN: + desc = "invalid lun"; + break; + case 0xA: + desc = "overlapped tag attempted"; + break; + case MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC: + desc = "task queued, however not sent to target"; + break; + default: + desc = "unknown"; + break; + } + printk(MPT2SAS_WARN_FMT "response_code(0x%01x): %s\n", + ioc->name, response_code, desc); +} + +/** + * scsih_tm_done - tm completion routine + * @ioc: per adapter object + * @smid: system request message index + * @VF_ID: virtual function id + * @reply: reply message frame(lower 32bit addr) + * Context: none. + * + * The callback handler when using scsih_issue_tm. + * + * Return nothing. + */ +static void +scsih_tm_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply) +{ + MPI2DefaultReply_t *mpi_reply; + + if (ioc->tm_cmds.status == MPT2_CMD_NOT_USED) + return; + if (ioc->tm_cmds.smid != smid) + return; + ioc->tm_cmds.status |= MPT2_CMD_COMPLETE; + mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); + if (mpi_reply) { + memcpy(ioc->tm_cmds.reply, mpi_reply, mpi_reply->MsgLength*4); + ioc->tm_cmds.status |= MPT2_CMD_REPLY_VALID; + } + ioc->tm_cmds.status &= ~MPT2_CMD_PENDING; + complete(&ioc->tm_cmds.done); +} + +/** + * mpt2sas_scsih_set_tm_flag - set per target tm_busy + * @ioc: per adapter object + * @handle: device handle + * + * During taskmangement request, we need to freeze the device queue. + */ +void +mpt2sas_scsih_set_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + struct MPT2SAS_DEVICE *sas_device_priv_data; + struct scsi_device *sdev; + u8 skip = 0; + + shost_for_each_device(sdev, ioc->shost) { + if (skip) + continue; + sas_device_priv_data = sdev->hostdata; + if (!sas_device_priv_data) + continue; + if (sas_device_priv_data->sas_target->handle == handle) { + sas_device_priv_data->sas_target->tm_busy = 1; + skip = 1; + ioc->ignore_loginfos = 1; + } + } +} + +/** + * mpt2sas_scsih_clear_tm_flag - clear per target tm_busy + * @ioc: per adapter object + * @handle: device handle + * + * During taskmangement request, we need to freeze the device queue. + */ +void +mpt2sas_scsih_clear_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + struct MPT2SAS_DEVICE *sas_device_priv_data; + struct scsi_device *sdev; + u8 skip = 0; + + shost_for_each_device(sdev, ioc->shost) { + if (skip) + continue; + sas_device_priv_data = sdev->hostdata; + if (!sas_device_priv_data) + continue; + if (sas_device_priv_data->sas_target->handle == handle) { + sas_device_priv_data->sas_target->tm_busy = 0; + skip = 1; + ioc->ignore_loginfos = 0; + } + } +} + +/** + * mpt2sas_scsih_issue_tm - main routine for sending tm requests + * @ioc: per adapter struct + * @device_handle: device handle + * @lun: lun number + * @type: MPI2_SCSITASKMGMT_TASKTYPE__XXX (defined in mpi2_init.h) + * @smid_task: smid assigned to the task + * @timeout: timeout in seconds + * Context: The calling function needs to acquire the tm_cmds.mutex + * + * A generic API for sending task management requests to firmware. + * + * The ioc->tm_cmds.status flag should be MPT2_CMD_NOT_USED before calling + * this API. + * + * The callback index is set inside `ioc->tm_cb_idx`. + * + * Return nothing. + */ +void +mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, + u8 type, u16 smid_task, ulong timeout) +{ + Mpi2SCSITaskManagementRequest_t *mpi_request; + Mpi2SCSITaskManagementReply_t *mpi_reply; + u16 smid = 0; + u32 ioc_state; + unsigned long timeleft; + u8 VF_ID = 0; + unsigned long flags; + + spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); + if (ioc->tm_cmds.status != MPT2_CMD_NOT_USED || + ioc->shost_recovery) { + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); + printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", + __func__, ioc->name); + return; + } + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); + + ioc_state = mpt2sas_base_get_iocstate(ioc, 0); + if (ioc_state & MPI2_DOORBELL_USED) { + dhsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "unexpected doorbell " + "active!\n", ioc->name)); + goto issue_host_reset; + } + + if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { + mpt2sas_base_fault_info(ioc, ioc_state & + MPI2_DOORBELL_DATA_MASK); + goto issue_host_reset; + } + + smid = mpt2sas_base_get_smid(ioc, ioc->tm_cb_idx); + if (!smid) { + printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", + ioc->name, __func__); + return; + } + + dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "sending tm: handle(0x%04x)," + " task_type(0x%02x), smid(%d)\n", ioc->name, handle, type, smid)); + ioc->tm_cmds.status = MPT2_CMD_PENDING; + mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); + ioc->tm_cmds.smid = smid; + memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t)); + mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; + mpi_request->DevHandle = cpu_to_le16(handle); + mpi_request->TaskType = type; + mpi_request->TaskMID = cpu_to_le16(smid_task); + int_to_scsilun(lun, (struct scsi_lun *)mpi_request->LUN); + mpt2sas_scsih_set_tm_flag(ioc, handle); + mpt2sas_base_put_smid_hi_priority(ioc, smid, VF_ID); + timeleft = wait_for_completion_timeout(&ioc->tm_cmds.done, timeout*HZ); + mpt2sas_scsih_clear_tm_flag(ioc, handle); + if (!(ioc->tm_cmds.status & MPT2_CMD_COMPLETE)) { + printk(MPT2SAS_ERR_FMT "%s: timeout\n", + ioc->name, __func__); + _debug_dump_mf(mpi_request, + sizeof(Mpi2SCSITaskManagementRequest_t)/4); + if (!(ioc->tm_cmds.status & MPT2_CMD_RESET)) + goto issue_host_reset; + } + + if (ioc->tm_cmds.status & MPT2_CMD_REPLY_VALID) { + mpi_reply = ioc->tm_cmds.reply; + dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "complete tm: " + "ioc_status(0x%04x), loginfo(0x%08x), term_count(0x%08x)\n", + ioc->name, le16_to_cpu(mpi_reply->IOCStatus), + le32_to_cpu(mpi_reply->IOCLogInfo), + le32_to_cpu(mpi_reply->TerminationCount))); + if (ioc->logging_level & MPT_DEBUG_TM) + _scsih_response_code(ioc, mpi_reply->ResponseCode); + } + return; + issue_host_reset: + mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, FORCE_BIG_HAMMER); +} + +/** + * scsih_abort - eh threads main abort routine + * @sdev: scsi device struct + * + * Returns SUCCESS if command aborted else FAILED + */ +static int +scsih_abort(struct scsi_cmnd *scmd) +{ + struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host); + struct MPT2SAS_DEVICE *sas_device_priv_data; + u16 smid; + u16 handle; + int r; + struct scsi_cmnd *scmd_lookup; + + printk(MPT2SAS_INFO_FMT "attempting task abort! scmd(%p)\n", + ioc->name, scmd); + scsi_print_command(scmd); + + sas_device_priv_data = scmd->device->hostdata; + if (!sas_device_priv_data || !sas_device_priv_data->sas_target) { + printk(MPT2SAS_INFO_FMT "device been deleted! scmd(%p)\n", + ioc->name, scmd); + scmd->result = DID_NO_CONNECT << 16; + scmd->scsi_done(scmd); + r = SUCCESS; + goto out; + } + + /* search for the command */ + smid = _scsih_scsi_lookup_find_by_scmd(ioc, scmd); + if (!smid) { + scmd->result = DID_RESET << 16; + r = SUCCESS; + goto out; + } + + /* for hidden raid components and volumes this is not supported */ + if (sas_device_priv_data->sas_target->flags & + MPT_TARGET_FLAGS_RAID_COMPONENT || + sas_device_priv_data->sas_target->flags & MPT_TARGET_FLAGS_VOLUME) { + scmd->result = DID_RESET << 16; + r = FAILED; + goto out; + } + + mutex_lock(&ioc->tm_cmds.mutex); + handle = sas_device_priv_data->sas_target->handle; + mpt2sas_scsih_issue_tm(ioc, handle, sas_device_priv_data->lun, + MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30); + + /* sanity check - see whether command actually completed */ + scmd_lookup = _scsih_scsi_lookup_get(ioc, smid); + if (scmd_lookup && (scmd_lookup->serial_number == scmd->serial_number)) + r = FAILED; + else + r = SUCCESS; + ioc->tm_cmds.status = MPT2_CMD_NOT_USED; + mutex_unlock(&ioc->tm_cmds.mutex); + + out: + printk(MPT2SAS_INFO_FMT "task abort: %s scmd(%p)\n", + ioc->name, ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); + return r; +} + + +/** + * scsih_dev_reset - eh threads main device reset routine + * @sdev: scsi device struct + * + * Returns SUCCESS if command aborted else FAILED + */ +static int +scsih_dev_reset(struct scsi_cmnd *scmd) +{ + struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host); + struct MPT2SAS_DEVICE *sas_device_priv_data; + struct _sas_device *sas_device; + unsigned long flags; + u16 handle; + int r; + + printk(MPT2SAS_INFO_FMT "attempting target reset! scmd(%p)\n", + ioc->name, scmd); + scsi_print_command(scmd); + + sas_device_priv_data = scmd->device->hostdata; + if (!sas_device_priv_data || !sas_device_priv_data->sas_target) { + printk(MPT2SAS_INFO_FMT "device been deleted! scmd(%p)\n", + ioc->name, scmd); + scmd->result = DID_NO_CONNECT << 16; + scmd->scsi_done(scmd); + r = SUCCESS; + goto out; + } + + /* for hidden raid components obtain the volume_handle */ + handle = 0; + if (sas_device_priv_data->sas_target->flags & + MPT_TARGET_FLAGS_RAID_COMPONENT) { + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = _scsih_sas_device_find_by_handle(ioc, + sas_device_priv_data->sas_target->handle); + if (sas_device) + handle = sas_device->volume_handle; + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + } else + handle = sas_device_priv_data->sas_target->handle; + + if (!handle) { + scmd->result = DID_RESET << 16; + r = FAILED; + goto out; + } + + mutex_lock(&ioc->tm_cmds.mutex); + mpt2sas_scsih_issue_tm(ioc, handle, 0, + MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 30); + + /* + * sanity check see whether all commands to this target been + * completed + */ + if (_scsih_scsi_lookup_find_by_target(ioc, scmd->device->id, + scmd->device->channel)) + r = FAILED; + else + r = SUCCESS; + ioc->tm_cmds.status = MPT2_CMD_NOT_USED; + mutex_unlock(&ioc->tm_cmds.mutex); + + out: + printk(MPT2SAS_INFO_FMT "target reset: %s scmd(%p)\n", + ioc->name, ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); + return r; +} + +/** + * scsih_abort - eh threads main host reset routine + * @sdev: scsi device struct + * + * Returns SUCCESS if command aborted else FAILED + */ +static int +scsih_host_reset(struct scsi_cmnd *scmd) +{ + struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host); + int r, retval; + + printk(MPT2SAS_INFO_FMT "attempting host reset! scmd(%p)\n", + ioc->name, scmd); + scsi_print_command(scmd); + + retval = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + r = (retval < 0) ? FAILED : SUCCESS; + printk(MPT2SAS_INFO_FMT "host reset: %s scmd(%p)\n", + ioc->name, ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); + + return r; +} + +/** + * _scsih_fw_event_add - insert and queue up fw_event + * @ioc: per adapter object + * @fw_event: object describing the event + * Context: This function will acquire ioc->fw_event_lock. + * + * This adds the firmware event object into link list, then queues it up to + * be processed from user context. + * + * Return nothing. + */ +static void +_scsih_fw_event_add(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work *fw_event) +{ + unsigned long flags; + + if (ioc->firmware_event_thread == NULL) + return; + + spin_lock_irqsave(&ioc->fw_event_lock, flags); + list_add_tail(&fw_event->list, &ioc->fw_event_list); + INIT_DELAYED_WORK(&fw_event->work, _firmware_event_work); + queue_delayed_work(ioc->firmware_event_thread, &fw_event->work, 1); + spin_unlock_irqrestore(&ioc->fw_event_lock, flags); +} + +/** + * _scsih_fw_event_free - delete fw_event + * @ioc: per adapter object + * @fw_event: object describing the event + * Context: This function will acquire ioc->fw_event_lock. + * + * This removes firmware event object from link list, frees associated memory. + * + * Return nothing. + */ +static void +_scsih_fw_event_free(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work + *fw_event) +{ + unsigned long flags; + + spin_lock_irqsave(&ioc->fw_event_lock, flags); + list_del(&fw_event->list); + kfree(fw_event->event_data); + kfree(fw_event); + spin_unlock_irqrestore(&ioc->fw_event_lock, flags); +} + +/** + * _scsih_fw_event_add - requeue an event + * @ioc: per adapter object + * @fw_event: object describing the event + * Context: This function will acquire ioc->fw_event_lock. + * + * Return nothing. + */ +static void +_scsih_fw_event_requeue(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work + *fw_event, unsigned long delay) +{ + unsigned long flags; + if (ioc->firmware_event_thread == NULL) + return; + + spin_lock_irqsave(&ioc->fw_event_lock, flags); + queue_delayed_work(ioc->firmware_event_thread, &fw_event->work, delay); + spin_unlock_irqrestore(&ioc->fw_event_lock, flags); +} + +/** + * _scsih_fw_event_off - turn flag off preventing event handling + * @ioc: per adapter object + * + * Used to prevent handling of firmware events during adapter reset + * driver unload. + * + * Return nothing. + */ +static void +_scsih_fw_event_off(struct MPT2SAS_ADAPTER *ioc) +{ + unsigned long flags; + + spin_lock_irqsave(&ioc->fw_event_lock, flags); + ioc->fw_events_off = 1; + spin_unlock_irqrestore(&ioc->fw_event_lock, flags); + +} + +/** + * _scsih_fw_event_on - turn flag on allowing firmware event handling + * @ioc: per adapter object + * + * Returns nothing. + */ +static void +_scsih_fw_event_on(struct MPT2SAS_ADAPTER *ioc) +{ + unsigned long flags; + + spin_lock_irqsave(&ioc->fw_event_lock, flags); + ioc->fw_events_off = 0; + spin_unlock_irqrestore(&ioc->fw_event_lock, flags); +} + +/** + * _scsih_ublock_io_device - set the device state to SDEV_RUNNING + * @ioc: per adapter object + * @handle: device handle + * + * During device pull we need to appropiately set the sdev state. + */ +static void +_scsih_ublock_io_device(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + struct MPT2SAS_DEVICE *sas_device_priv_data; + struct scsi_device *sdev; + + shost_for_each_device(sdev, ioc->shost) { + sas_device_priv_data = sdev->hostdata; + if (!sas_device_priv_data) + continue; + if (!sas_device_priv_data->block) + continue; + if (sas_device_priv_data->sas_target->handle == handle) { + dewtprintk(ioc, sdev_printk(KERN_INFO, sdev, + MPT2SAS_INFO_FMT "SDEV_RUNNING: " + "handle(0x%04x)\n", ioc->name, handle)); + sas_device_priv_data->block = 0; + scsi_device_set_state(sdev, SDEV_RUNNING); + } + } +} + +/** + * _scsih_block_io_device - set the device state to SDEV_BLOCK + * @ioc: per adapter object + * @handle: device handle + * + * During device pull we need to appropiately set the sdev state. + */ +static void +_scsih_block_io_device(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + struct MPT2SAS_DEVICE *sas_device_priv_data; + struct scsi_device *sdev; + + shost_for_each_device(sdev, ioc->shost) { + sas_device_priv_data = sdev->hostdata; + if (!sas_device_priv_data) + continue; + if (sas_device_priv_data->block) + continue; + if (sas_device_priv_data->sas_target->handle == handle) { + dewtprintk(ioc, sdev_printk(KERN_INFO, sdev, + MPT2SAS_INFO_FMT "SDEV_BLOCK: " + "handle(0x%04x)\n", ioc->name, handle)); + sas_device_priv_data->block = 1; + scsi_device_set_state(sdev, SDEV_BLOCK); + } + } +} + +/** + * _scsih_block_io_to_children_attached_to_ex + * @ioc: per adapter object + * @sas_expander: the sas_device object + * + * This routine set sdev state to SDEV_BLOCK for all devices + * attached to this expander. This function called when expander is + * pulled. + */ +static void +_scsih_block_io_to_children_attached_to_ex(struct MPT2SAS_ADAPTER *ioc, + struct _sas_node *sas_expander) +{ + struct _sas_port *mpt2sas_port; + struct _sas_device *sas_device; + struct _sas_node *expander_sibling; + unsigned long flags; + + if (!sas_expander) + return; + + list_for_each_entry(mpt2sas_port, + &sas_expander->sas_port_list, port_list) { + if (mpt2sas_port->remote_identify.device_type == + SAS_END_DEVICE) { + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = + mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + mpt2sas_port->remote_identify.sas_address); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + if (!sas_device) + continue; + _scsih_block_io_device(ioc, sas_device->handle); + } + } + + list_for_each_entry(mpt2sas_port, + &sas_expander->sas_port_list, port_list) { + + if (mpt2sas_port->remote_identify.device_type == + MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER || + mpt2sas_port->remote_identify.device_type == + MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER) { + + spin_lock_irqsave(&ioc->sas_node_lock, flags); + expander_sibling = + mpt2sas_scsih_expander_find_by_sas_address( + ioc, mpt2sas_port->remote_identify.sas_address); + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); + _scsih_block_io_to_children_attached_to_ex(ioc, + expander_sibling); + } + } +} + +/** + * _scsih_block_io_to_children_attached_directly + * @ioc: per adapter object + * @event_data: topology change event data + * + * This routine set sdev state to SDEV_BLOCK for all devices + * direct attached during device pull. + */ +static void +_scsih_block_io_to_children_attached_directly(struct MPT2SAS_ADAPTER *ioc, + Mpi2EventDataSasTopologyChangeList_t *event_data) +{ + int i; + u16 handle; + u16 reason_code; + u8 phy_number; + + for (i = 0; i < event_data->NumEntries; i++) { + handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); + if (!handle) + continue; + phy_number = event_data->StartPhyNum + i; + reason_code = event_data->PHY[i].PhyStatus & + MPI2_EVENT_SAS_TOPO_RC_MASK; + if (reason_code == MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING) + _scsih_block_io_device(ioc, handle); + } +} + +/** + * _scsih_check_topo_delete_events - sanity check on topo events + * @ioc: per adapter object + * @event_data: the event data payload + * + * This routine added to better handle cable breaker. + * + * This handles the case where driver recieves multiple expander + * add and delete events in a single shot. When there is a delete event + * the routine will void any pending add events waiting in the event queue. + * + * Return nothing. + */ +static void +_scsih_check_topo_delete_events(struct MPT2SAS_ADAPTER *ioc, + Mpi2EventDataSasTopologyChangeList_t *event_data) +{ + struct fw_event_work *fw_event; + Mpi2EventDataSasTopologyChangeList_t *local_event_data; + u16 expander_handle; + struct _sas_node *sas_expander; + unsigned long flags; + + expander_handle = le16_to_cpu(event_data->ExpanderDevHandle); + if (expander_handle < ioc->sas_hba.num_phys) { + _scsih_block_io_to_children_attached_directly(ioc, event_data); + return; + } + + if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING + || event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING) { + spin_lock_irqsave(&ioc->sas_node_lock, flags); + sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc, + expander_handle); + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); + _scsih_block_io_to_children_attached_to_ex(ioc, sas_expander); + } else if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_RESPONDING) + _scsih_block_io_to_children_attached_directly(ioc, event_data); + + if (event_data->ExpStatus != MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING) + return; + + /* mark ignore flag for pending events */ + spin_lock_irqsave(&ioc->fw_event_lock, flags); + list_for_each_entry(fw_event, &ioc->fw_event_list, list) { + if (fw_event->event != MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST || + fw_event->ignore) + continue; + local_event_data = fw_event->event_data; + if (local_event_data->ExpStatus == + MPI2_EVENT_SAS_TOPO_ES_ADDED || + local_event_data->ExpStatus == + MPI2_EVENT_SAS_TOPO_ES_RESPONDING) { + if (le16_to_cpu(local_event_data->ExpanderDevHandle) == + expander_handle) { + dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT + "setting ignoring flag\n", ioc->name)); + fw_event->ignore = 1; + } + } + } + spin_unlock_irqrestore(&ioc->fw_event_lock, flags); +} + +/** + * _scsih_queue_rescan - queue a topology rescan from user context + * @ioc: per adapter object + * + * Return nothing. + */ +static void +_scsih_queue_rescan(struct MPT2SAS_ADAPTER *ioc) +{ + struct fw_event_work *fw_event; + + if (ioc->wait_for_port_enable_to_complete) + return; + fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); + if (!fw_event) + return; + fw_event->event = MPT2SAS_RESCAN_AFTER_HOST_RESET; + fw_event->ioc = ioc; + _scsih_fw_event_add(ioc, fw_event); +} + +/** + * _scsih_flush_running_cmds - completing outstanding commands. + * @ioc: per adapter object + * + * The flushing out of all pending scmd commands following host reset, + * where all IO is dropped to the floor. + * + * Return nothing. + */ +static void +_scsih_flush_running_cmds(struct MPT2SAS_ADAPTER *ioc) +{ + struct scsi_cmnd *scmd; + u16 smid; + u16 count = 0; + + for (smid = 1; smid <= ioc->request_depth; smid++) { + scmd = _scsih_scsi_lookup_getclear(ioc, smid); + if (!scmd) + continue; + count++; + mpt2sas_base_free_smid(ioc, smid); + scsi_dma_unmap(scmd); + scmd->result = DID_RESET << 16; + scmd->scsi_done(scmd); + } + dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "completing %d cmds\n", + ioc->name, count)); +} + +/** + * mpt2sas_scsih_reset_handler - reset callback handler (for scsih) + * @ioc: per adapter object + * @reset_phase: phase + * + * The handler for doing any required cleanup or initialization. + * + * The reset phase can be MPT2_IOC_PRE_RESET, MPT2_IOC_AFTER_RESET, + * MPT2_IOC_DONE_RESET + * + * Return nothing. + */ +void +mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase) +{ + switch (reset_phase) { + case MPT2_IOC_PRE_RESET: + dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + "MPT2_IOC_PRE_RESET\n", ioc->name, __func__)); + _scsih_fw_event_off(ioc); + break; + case MPT2_IOC_AFTER_RESET: + dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + "MPT2_IOC_AFTER_RESET\n", ioc->name, __func__)); + if (ioc->tm_cmds.status & MPT2_CMD_PENDING) { + ioc->tm_cmds.status |= MPT2_CMD_RESET; + mpt2sas_base_free_smid(ioc, ioc->tm_cmds.smid); + complete(&ioc->tm_cmds.done); + } + _scsih_fw_event_on(ioc); + _scsih_flush_running_cmds(ioc); + break; + case MPT2_IOC_DONE_RESET: + dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " + "MPT2_IOC_DONE_RESET\n", ioc->name, __func__)); + _scsih_queue_rescan(ioc); + break; + } +} + +/** + * scsih_qcmd - main scsi request entry point + * @scmd: pointer to scsi command object + * @done: function pointer to be invoked on completion + * + * The callback index is set inside `ioc->scsi_io_cb_idx`. + * + * Returns 0 on success. If there's a failure, return either: + * SCSI_MLQUEUE_DEVICE_BUSY if the device queue is full, or + * SCSI_MLQUEUE_HOST_BUSY if the entire host queue is full + */ +static int +scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *)) +{ + struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host); + struct MPT2SAS_DEVICE *sas_device_priv_data; + struct MPT2SAS_TARGET *sas_target_priv_data; + Mpi2SCSIIORequest_t *mpi_request; + u32 mpi_control; + u16 smid; + unsigned long flags; + + scmd->scsi_done = done; + sas_device_priv_data = scmd->device->hostdata; + if (!sas_device_priv_data) { + scmd->result = DID_NO_CONNECT << 16; + scmd->scsi_done(scmd); + return 0; + } + + sas_target_priv_data = sas_device_priv_data->sas_target; + if (!sas_target_priv_data || sas_target_priv_data->handle == + MPT2SAS_INVALID_DEVICE_HANDLE || sas_target_priv_data->deleted) { + scmd->result = DID_NO_CONNECT << 16; + scmd->scsi_done(scmd); + return 0; + } + + /* see if we are busy with task managment stuff */ + spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); + if (sas_target_priv_data->tm_busy || + ioc->shost_recovery || ioc->ioc_link_reset_in_progress) { + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); + return SCSI_MLQUEUE_HOST_BUSY; + } + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); + + if (scmd->sc_data_direction == DMA_FROM_DEVICE) + mpi_control = MPI2_SCSIIO_CONTROL_READ; + else if (scmd->sc_data_direction == DMA_TO_DEVICE) + mpi_control = MPI2_SCSIIO_CONTROL_WRITE; + else + mpi_control = MPI2_SCSIIO_CONTROL_NODATATRANSFER; + + /* set tags */ + if (!(sas_device_priv_data->flags & MPT_DEVICE_FLAGS_INIT)) { + if (scmd->device->tagged_supported) { + if (scmd->device->ordered_tags) + mpi_control |= MPI2_SCSIIO_CONTROL_ORDEREDQ; + else + mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; + } else +/* MPI Revision I (UNIT = 0xA) - removed MPI2_SCSIIO_CONTROL_UNTAGGED */ +/* mpi_control |= MPI2_SCSIIO_CONTROL_UNTAGGED; + */ + mpi_control |= (0x500); + + } else + mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; + + if ((sas_device_priv_data->flags & MPT_DEVICE_TLR_ON)) + mpi_control |= MPI2_SCSIIO_CONTROL_TLR_ON; + + smid = mpt2sas_base_get_smid(ioc, ioc->scsi_io_cb_idx); + if (!smid) { + printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", + ioc->name, __func__); + goto out; + } + mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); + memset(mpi_request, 0, sizeof(Mpi2SCSIIORequest_t)); + mpi_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST; + if (sas_device_priv_data->sas_target->flags & + MPT_TARGET_FLAGS_RAID_COMPONENT) + mpi_request->Function = MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH; + else + mpi_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST; + mpi_request->DevHandle = + cpu_to_le16(sas_device_priv_data->sas_target->handle); + mpi_request->DataLength = cpu_to_le32(scsi_bufflen(scmd)); + mpi_request->Control = cpu_to_le32(mpi_control); + mpi_request->IoFlags = cpu_to_le16(scmd->cmd_len); + mpi_request->MsgFlags = MPI2_SCSIIO_MSGFLAGS_SYSTEM_SENSE_ADDR; + mpi_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE; + mpi_request->SenseBufferLowAddress = + (u32)mpt2sas_base_get_sense_buffer_dma(ioc, smid); + mpi_request->SGLOffset0 = offsetof(Mpi2SCSIIORequest_t, SGL) / 4; + mpi_request->SGLFlags = cpu_to_le16(MPI2_SCSIIO_SGLFLAGS_TYPE_MPI + + MPI2_SCSIIO_SGLFLAGS_SYSTEM_ADDR); + + int_to_scsilun(sas_device_priv_data->lun, (struct scsi_lun *) + mpi_request->LUN); + memcpy(mpi_request->CDB.CDB32, scmd->cmnd, scmd->cmd_len); + + if (!mpi_request->DataLength) { + mpt2sas_base_build_zero_len_sge(ioc, &mpi_request->SGL); + } else { + if (_scsih_build_scatter_gather(ioc, scmd, smid)) { + mpt2sas_base_free_smid(ioc, smid); + goto out; + } + } + + _scsih_scsi_lookup_set(ioc, smid, scmd); + mpt2sas_base_put_smid_scsi_io(ioc, smid, 0, + sas_device_priv_data->sas_target->handle); + return 0; + + out: + return SCSI_MLQUEUE_HOST_BUSY; +} + +/** + * _scsih_normalize_sense - normalize descriptor and fixed format sense data + * @sense_buffer: sense data returned by target + * @data: normalized skey/asc/ascq + * + * Return nothing. + */ +static void +_scsih_normalize_sense(char *sense_buffer, struct sense_info *data) +{ + if ((sense_buffer[0] & 0x7F) >= 0x72) { + /* descriptor format */ + data->skey = sense_buffer[1] & 0x0F; + data->asc = sense_buffer[2]; + data->ascq = sense_buffer[3]; + } else { + /* fixed format */ + data->skey = sense_buffer[2] & 0x0F; + data->asc = sense_buffer[12]; + data->ascq = sense_buffer[13]; + } +} + +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING +/** + * _scsih_scsi_ioc_info - translated non-succesfull SCSI_IO request + * @ioc: per adapter object + * @scmd: pointer to scsi command object + * @mpi_reply: reply mf payload returned from firmware + * + * scsi_status - SCSI Status code returned from target device + * scsi_state - state info associated with SCSI_IO determined by ioc + * ioc_status - ioc supplied status info + * + * Return nothing. + */ +static void +_scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, + Mpi2SCSIIOReply_t *mpi_reply, u16 smid) +{ + u32 response_info; + u8 *response_bytes; + u16 ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & + MPI2_IOCSTATUS_MASK; + u8 scsi_state = mpi_reply->SCSIState; + u8 scsi_status = mpi_reply->SCSIStatus; + char *desc_ioc_state = NULL; + char *desc_scsi_status = NULL; + char *desc_scsi_state = ioc->tmp_string; + + switch (ioc_status) { + case MPI2_IOCSTATUS_SUCCESS: + desc_ioc_state = "success"; + break; + case MPI2_IOCSTATUS_INVALID_FUNCTION: + desc_ioc_state = "invalid function"; + break; + case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR: + desc_ioc_state = "scsi recovered error"; + break; + case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE: + desc_ioc_state = "scsi invalid dev handle"; + break; + case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE: + desc_ioc_state = "scsi device not there"; + break; + case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN: + desc_ioc_state = "scsi data overrun"; + break; + case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN: + desc_ioc_state = "scsi data underrun"; + break; + case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR: + desc_ioc_state = "scsi io data error"; + break; + case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR: + desc_ioc_state = "scsi protocol error"; + break; + case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED: + desc_ioc_state = "scsi task terminated"; + break; + case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: + desc_ioc_state = "scsi residual mismatch"; + break; + case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED: + desc_ioc_state = "scsi task mgmt failed"; + break; + case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED: + desc_ioc_state = "scsi ioc terminated"; + break; + case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED: + desc_ioc_state = "scsi ext terminated"; + break; + default: + desc_ioc_state = "unknown"; + break; + } + + switch (scsi_status) { + case MPI2_SCSI_STATUS_GOOD: + desc_scsi_status = "good"; + break; + case MPI2_SCSI_STATUS_CHECK_CONDITION: + desc_scsi_status = "check condition"; + break; + case MPI2_SCSI_STATUS_CONDITION_MET: + desc_scsi_status = "condition met"; + break; + case MPI2_SCSI_STATUS_BUSY: + desc_scsi_status = "busy"; + break; + case MPI2_SCSI_STATUS_INTERMEDIATE: + desc_scsi_status = "intermediate"; + break; + case MPI2_SCSI_STATUS_INTERMEDIATE_CONDMET: + desc_scsi_status = "intermediate condmet"; + break; + case MPI2_SCSI_STATUS_RESERVATION_CONFLICT: + desc_scsi_status = "reservation conflict"; + break; + case MPI2_SCSI_STATUS_COMMAND_TERMINATED: + desc_scsi_status = "command terminated"; + break; + case MPI2_SCSI_STATUS_TASK_SET_FULL: + desc_scsi_status = "task set full"; + break; + case MPI2_SCSI_STATUS_ACA_ACTIVE: + desc_scsi_status = "aca active"; + break; + case MPI2_SCSI_STATUS_TASK_ABORTED: + desc_scsi_status = "task aborted"; + break; + default: + desc_scsi_status = "unknown"; + break; + } + + desc_scsi_state[0] = '\0'; + if (!scsi_state) + desc_scsi_state = " "; + if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) + strcat(desc_scsi_state, "response info "); + if (scsi_state & MPI2_SCSI_STATE_TERMINATED) + strcat(desc_scsi_state, "state terminated "); + if (scsi_state & MPI2_SCSI_STATE_NO_SCSI_STATUS) + strcat(desc_scsi_state, "no status "); + if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_FAILED) + strcat(desc_scsi_state, "autosense failed "); + if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID) + strcat(desc_scsi_state, "autosense valid "); + + scsi_print_command(scmd); + printk(MPT2SAS_WARN_FMT "\tdev handle(0x%04x), " + "ioc_status(%s)(0x%04x), smid(%d)\n", ioc->name, + le16_to_cpu(mpi_reply->DevHandle), desc_ioc_state, + ioc_status, smid); + printk(MPT2SAS_WARN_FMT "\trequest_len(%d), underflow(%d), " + "resid(%d)\n", ioc->name, scsi_bufflen(scmd), scmd->underflow, + scsi_get_resid(scmd)); + printk(MPT2SAS_WARN_FMT "\ttag(%d), transfer_count(%d), " + "sc->result(0x%08x)\n", ioc->name, le16_to_cpu(mpi_reply->TaskTag), + le32_to_cpu(mpi_reply->TransferCount), scmd->result); + printk(MPT2SAS_WARN_FMT "\tscsi_status(%s)(0x%02x), " + "scsi_state(%s)(0x%02x)\n", ioc->name, desc_scsi_status, + scsi_status, desc_scsi_state, scsi_state); + + if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID) { + struct sense_info data; + _scsih_normalize_sense(scmd->sense_buffer, &data); + printk(MPT2SAS_WARN_FMT "\t[sense_key,asc,ascq]: " + "[0x%02x,0x%02x,0x%02x]\n", ioc->name, data.skey, + data.asc, data.ascq); + } + + if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) { + response_info = le32_to_cpu(mpi_reply->ResponseInfo); + response_bytes = (u8 *)&response_info; + _scsih_response_code(ioc, response_bytes[3]); + } +} +#endif + +/** + * _scsih_smart_predicted_fault - illuminate Fault LED + * @ioc: per adapter object + * @handle: device handle + * + * Return nothing. + */ +static void +_scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + Mpi2SepReply_t mpi_reply; + Mpi2SepRequest_t mpi_request; + struct scsi_target *starget; + struct MPT2SAS_TARGET *sas_target_priv_data; + Mpi2EventNotificationReply_t *event_reply; + Mpi2EventDataSasDeviceStatusChange_t *event_data; + struct _sas_device *sas_device; + ssize_t sz; + unsigned long flags; + + /* only handle non-raid devices */ + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + if (!sas_device) { + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + return; + } + starget = sas_device->starget; + sas_target_priv_data = starget->hostdata; + + if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) || + ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME))) { + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + return; + } + starget_printk(KERN_WARNING, starget, "predicted fault\n"); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + if (ioc->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM) { + memset(&mpi_request, 0, sizeof(Mpi2SepRequest_t)); + mpi_request.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR; + mpi_request.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS; + mpi_request.SlotStatus = + MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT; + mpi_request.DevHandle = cpu_to_le16(handle); + mpi_request.Flags = MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS; + if ((mpt2sas_base_scsi_enclosure_processor(ioc, &mpi_reply, + &mpi_request)) != 0) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return; + } + + if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT + "enclosure_processor: ioc_status (0x%04x), " + "loginfo(0x%08x)\n", ioc->name, + le16_to_cpu(mpi_reply.IOCStatus), + le32_to_cpu(mpi_reply.IOCLogInfo))); + return; + } + } + + /* insert into event log */ + sz = offsetof(Mpi2EventNotificationReply_t, EventData) + + sizeof(Mpi2EventDataSasDeviceStatusChange_t); + event_reply = kzalloc(sz, GFP_KERNEL); + if (!event_reply) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return; + } + + event_reply->Function = MPI2_FUNCTION_EVENT_NOTIFICATION; + event_reply->Event = + cpu_to_le16(MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE); + event_reply->MsgLength = sz/4; + event_reply->EventDataLength = + cpu_to_le16(sizeof(Mpi2EventDataSasDeviceStatusChange_t)/4); + event_data = (Mpi2EventDataSasDeviceStatusChange_t *) + event_reply->EventData; + event_data->ReasonCode = MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA; + event_data->ASC = 0x5D; + event_data->DevHandle = cpu_to_le16(handle); + event_data->SASAddress = cpu_to_le64(sas_target_priv_data->sas_address); + mpt2sas_ctl_add_to_event_log(ioc, event_reply); + kfree(event_reply); +} + +/** + * scsih_io_done - scsi request callback + * @ioc: per adapter object + * @smid: system request message index + * @VF_ID: virtual function id + * @reply: reply message frame(lower 32bit addr) + * + * Callback handler when using scsih_qcmd. + * + * Return nothing. + */ +static void +scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply) +{ + Mpi2SCSIIORequest_t *mpi_request; + Mpi2SCSIIOReply_t *mpi_reply; + struct scsi_cmnd *scmd; + u16 ioc_status; + u32 xfer_cnt; + u8 scsi_state; + u8 scsi_status; + u32 log_info; + struct MPT2SAS_DEVICE *sas_device_priv_data; + u32 response_code; + + mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); + scmd = _scsih_scsi_lookup_getclear(ioc, smid); + if (scmd == NULL) + return; + + mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); + + if (mpi_reply == NULL) { + scmd->result = DID_OK << 16; + goto out; + } + + sas_device_priv_data = scmd->device->hostdata; + if (!sas_device_priv_data || !sas_device_priv_data->sas_target || + sas_device_priv_data->sas_target->deleted) { + scmd->result = DID_NO_CONNECT << 16; + goto out; + } + + /* turning off TLR */ + if (!sas_device_priv_data->tlr_snoop_check) { + sas_device_priv_data->tlr_snoop_check++; + if (sas_device_priv_data->flags & MPT_DEVICE_TLR_ON) { + response_code = (le32_to_cpu(mpi_reply->ResponseInfo) + >> 24); + if (response_code == + MPI2_SCSITASKMGMT_RSP_INVALID_FRAME) + sas_device_priv_data->flags &= + ~MPT_DEVICE_TLR_ON; + } + } + + xfer_cnt = le32_to_cpu(mpi_reply->TransferCount); + scsi_set_resid(scmd, scsi_bufflen(scmd) - xfer_cnt); + ioc_status = le16_to_cpu(mpi_reply->IOCStatus); + if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) + log_info = le32_to_cpu(mpi_reply->IOCLogInfo); + else + log_info = 0; + ioc_status &= MPI2_IOCSTATUS_MASK; + scsi_state = mpi_reply->SCSIState; + scsi_status = mpi_reply->SCSIStatus; + + if (ioc_status == MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN && xfer_cnt == 0 && + (scsi_status == MPI2_SCSI_STATUS_BUSY || + scsi_status == MPI2_SCSI_STATUS_RESERVATION_CONFLICT || + scsi_status == MPI2_SCSI_STATUS_TASK_SET_FULL)) { + ioc_status = MPI2_IOCSTATUS_SUCCESS; + } + + if (scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID) { + struct sense_info data; + const void *sense_data = mpt2sas_base_get_sense_buffer(ioc, + smid); + memcpy(scmd->sense_buffer, sense_data, + le32_to_cpu(mpi_reply->SenseCount)); + _scsih_normalize_sense(scmd->sense_buffer, &data); + /* failure prediction threshold exceeded */ + if (data.asc == 0x5D) + _scsih_smart_predicted_fault(ioc, + le16_to_cpu(mpi_reply->DevHandle)); + } + + switch (ioc_status) { + case MPI2_IOCSTATUS_BUSY: + case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES: + scmd->result = SAM_STAT_BUSY; + break; + + case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE: + scmd->result = DID_NO_CONNECT << 16; + break; + + case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED: + if (sas_device_priv_data->block) { + scmd->result = (DID_BUS_BUSY << 16); + break; + } + + case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED: + case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED: + scmd->result = DID_RESET << 16; + break; + + case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: + if ((xfer_cnt == 0) || (scmd->underflow > xfer_cnt)) + scmd->result = DID_SOFT_ERROR << 16; + else + scmd->result = (DID_OK << 16) | scsi_status; + break; + + case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN: + scmd->result = (DID_OK << 16) | scsi_status; + + if ((scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID)) + break; + + if (xfer_cnt < scmd->underflow) { + if (scsi_status == SAM_STAT_BUSY) + scmd->result = SAM_STAT_BUSY; + else + scmd->result = DID_SOFT_ERROR << 16; + } else if (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED | + MPI2_SCSI_STATE_NO_SCSI_STATUS)) + scmd->result = DID_SOFT_ERROR << 16; + else if (scsi_state & MPI2_SCSI_STATE_TERMINATED) + scmd->result = DID_RESET << 16; + else if (!xfer_cnt && scmd->cmnd[0] == REPORT_LUNS) { + mpi_reply->SCSIState = MPI2_SCSI_STATE_AUTOSENSE_VALID; + mpi_reply->SCSIStatus = SAM_STAT_CHECK_CONDITION; + scmd->result = (DRIVER_SENSE << 24) | + SAM_STAT_CHECK_CONDITION; + scmd->sense_buffer[0] = 0x70; + scmd->sense_buffer[2] = ILLEGAL_REQUEST; + scmd->sense_buffer[12] = 0x20; + scmd->sense_buffer[13] = 0; + } + break; + + case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN: + scsi_set_resid(scmd, 0); + case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR: + case MPI2_IOCSTATUS_SUCCESS: + scmd->result = (DID_OK << 16) | scsi_status; + if (scsi_state & (MPI2_SCSI_STATE_AUTOSENSE_FAILED | + MPI2_SCSI_STATE_NO_SCSI_STATUS)) + scmd->result = DID_SOFT_ERROR << 16; + else if (scsi_state & MPI2_SCSI_STATE_TERMINATED) + scmd->result = DID_RESET << 16; + break; + + case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR: + case MPI2_IOCSTATUS_INVALID_FUNCTION: + case MPI2_IOCSTATUS_INVALID_SGL: + case MPI2_IOCSTATUS_INTERNAL_ERROR: + case MPI2_IOCSTATUS_INVALID_FIELD: + case MPI2_IOCSTATUS_INVALID_STATE: + case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR: + case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED: + default: + scmd->result = DID_SOFT_ERROR << 16; + break; + + } + +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING + if (scmd->result && (ioc->logging_level & MPT_DEBUG_REPLY)) + _scsih_scsi_ioc_info(ioc , scmd, mpi_reply, smid); +#endif + + out: + scsi_dma_unmap(scmd); + scmd->scsi_done(scmd); +} + +/** + * _scsih_link_change - process phy link changes + * @ioc: per adapter object + * @handle: phy handle + * @attached_handle: valid for devices attached to link + * @phy_number: phy number + * @link_rate: new link rate + * Context: user. + * + * Return nothing. + */ +static void +_scsih_link_change(struct MPT2SAS_ADAPTER *ioc, u16 handle, u16 attached_handle, + u8 phy_number, u8 link_rate) +{ + mpt2sas_transport_update_phy_link_change(ioc, handle, attached_handle, + phy_number, link_rate); +} + +/** + * _scsih_sas_host_refresh - refreshing sas host object contents + * @ioc: per adapter object + * @update: update link information + * Context: user + * + * During port enable, fw will send topology events for every device. Its + * possible that the handles may change from the previous setting, so this + * code keeping handles updating if changed. + * + * Return nothing. + */ +static void +_scsih_sas_host_refresh(struct MPT2SAS_ADAPTER *ioc, u8 update) +{ + u16 sz; + u16 ioc_status; + int i; + Mpi2ConfigReply_t mpi_reply; + Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL; + + dtmprintk(ioc, printk(MPT2SAS_INFO_FMT + "updating handles for sas_host(0x%016llx)\n", + ioc->name, (unsigned long long)ioc->sas_hba.sas_address)); + + sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys + * sizeof(Mpi2SasIOUnit0PhyData_t)); + sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL); + if (!sas_iounit_pg0) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return; + } + if (!(mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply, + sas_iounit_pg0, sz))) { + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) + goto out; + for (i = 0; i < ioc->sas_hba.num_phys ; i++) { + ioc->sas_hba.phy[i].handle = + le16_to_cpu(sas_iounit_pg0->PhyData[i]. + ControllerDevHandle); + if (update) + _scsih_link_change(ioc, + ioc->sas_hba.phy[i].handle, + le16_to_cpu(sas_iounit_pg0->PhyData[i]. + AttachedDevHandle), i, + sas_iounit_pg0->PhyData[i]. + NegotiatedLinkRate >> 4); + } + } + + out: + kfree(sas_iounit_pg0); +} + +/** + * _scsih_sas_host_add - create sas host object + * @ioc: per adapter object + * + * Creating host side data object, stored in ioc->sas_hba + * + * Return nothing. + */ +static void +_scsih_sas_host_add(struct MPT2SAS_ADAPTER *ioc) +{ + int i; + Mpi2ConfigReply_t mpi_reply; + Mpi2SasIOUnitPage0_t *sas_iounit_pg0 = NULL; + Mpi2SasIOUnitPage1_t *sas_iounit_pg1 = NULL; + Mpi2SasPhyPage0_t phy_pg0; + Mpi2SasDevicePage0_t sas_device_pg0; + Mpi2SasEnclosurePage0_t enclosure_pg0; + u16 ioc_status; + u16 sz; + u16 device_missing_delay; + + mpt2sas_config_get_number_hba_phys(ioc, &ioc->sas_hba.num_phys); + if (!ioc->sas_hba.num_phys) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return; + } + + /* sas_iounit page 0 */ + sz = offsetof(Mpi2SasIOUnitPage0_t, PhyData) + (ioc->sas_hba.num_phys * + sizeof(Mpi2SasIOUnit0PhyData_t)); + sas_iounit_pg0 = kzalloc(sz, GFP_KERNEL); + if (!sas_iounit_pg0) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return; + } + if ((mpt2sas_config_get_sas_iounit_pg0(ioc, &mpi_reply, + sas_iounit_pg0, sz))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + goto out; + } + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + goto out; + } + + /* sas_iounit page 1 */ + sz = offsetof(Mpi2SasIOUnitPage1_t, PhyData) + (ioc->sas_hba.num_phys * + sizeof(Mpi2SasIOUnit1PhyData_t)); + sas_iounit_pg1 = kzalloc(sz, GFP_KERNEL); + if (!sas_iounit_pg1) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + goto out; + } + if ((mpt2sas_config_get_sas_iounit_pg1(ioc, &mpi_reply, + sas_iounit_pg1, sz))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + goto out; + } + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + goto out; + } + + ioc->io_missing_delay = + le16_to_cpu(sas_iounit_pg1->IODeviceMissingDelay); + device_missing_delay = + le16_to_cpu(sas_iounit_pg1->ReportDeviceMissingDelay); + if (device_missing_delay & MPI2_SASIOUNIT1_REPORT_MISSING_UNIT_16) + ioc->device_missing_delay = (device_missing_delay & + MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK) * 16; + else + ioc->device_missing_delay = device_missing_delay & + MPI2_SASIOUNIT1_REPORT_MISSING_TIMEOUT_MASK; + + ioc->sas_hba.parent_dev = &ioc->shost->shost_gendev; + ioc->sas_hba.phy = kcalloc(ioc->sas_hba.num_phys, + sizeof(struct _sas_phy), GFP_KERNEL); + if (!ioc->sas_hba.phy) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + goto out; + } + for (i = 0; i < ioc->sas_hba.num_phys ; i++) { + if ((mpt2sas_config_get_phy_pg0(ioc, &mpi_reply, &phy_pg0, + i))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + goto out; + } + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + goto out; + } + ioc->sas_hba.phy[i].handle = + le16_to_cpu(sas_iounit_pg0->PhyData[i].ControllerDevHandle); + ioc->sas_hba.phy[i].phy_id = i; + mpt2sas_transport_add_host_phy(ioc, &ioc->sas_hba.phy[i], + phy_pg0, ioc->sas_hba.parent_dev); + } + if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, + MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, ioc->sas_hba.phy[0].handle))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + goto out; + } + ioc->sas_hba.handle = le16_to_cpu(sas_device_pg0.DevHandle); + ioc->sas_hba.enclosure_handle = + le16_to_cpu(sas_device_pg0.EnclosureHandle); + ioc->sas_hba.sas_address = le64_to_cpu(sas_device_pg0.SASAddress); + printk(MPT2SAS_INFO_FMT "host_add: handle(0x%04x), " + "sas_addr(0x%016llx), phys(%d)\n", ioc->name, ioc->sas_hba.handle, + (unsigned long long) ioc->sas_hba.sas_address, + ioc->sas_hba.num_phys) ; + + if (ioc->sas_hba.enclosure_handle) { + if (!(mpt2sas_config_get_enclosure_pg0(ioc, &mpi_reply, + &enclosure_pg0, + MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, + ioc->sas_hba.enclosure_handle))) { + ioc->sas_hba.enclosure_logical_id = + le64_to_cpu(enclosure_pg0.EnclosureLogicalID); + } + } + + out: + kfree(sas_iounit_pg1); + kfree(sas_iounit_pg0); +} + +/** + * _scsih_expander_add - creating expander object + * @ioc: per adapter object + * @handle: expander handle + * + * Creating expander object, stored in ioc->sas_expander_list. + * + * Return 0 for success, else error. + */ +static int +_scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + struct _sas_node *sas_expander; + Mpi2ConfigReply_t mpi_reply; + Mpi2ExpanderPage0_t expander_pg0; + Mpi2ExpanderPage1_t expander_pg1; + Mpi2SasEnclosurePage0_t enclosure_pg0; + u32 ioc_status; + u16 parent_handle; + __le64 sas_address; + int i; + unsigned long flags; + struct _sas_port *mpt2sas_port; + int rc = 0; + + if (!handle) + return -1; + + if ((mpt2sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0, + MPI2_SAS_EXPAND_PGAD_FORM_HNDL, handle))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return -1; + } + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return -1; + } + + /* handle out of order topology events */ + parent_handle = le16_to_cpu(expander_pg0.ParentDevHandle); + if (parent_handle >= ioc->sas_hba.num_phys) { + spin_lock_irqsave(&ioc->sas_node_lock, flags); + sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc, + parent_handle); + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); + if (!sas_expander) { + rc = _scsih_expander_add(ioc, parent_handle); + if (rc != 0) + return rc; + } + } + + sas_address = le64_to_cpu(expander_pg0.SASAddress); + + spin_lock_irqsave(&ioc->sas_node_lock, flags); + sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc, + sas_address); + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); + + if (sas_expander) + return 0; + + sas_expander = kzalloc(sizeof(struct _sas_node), + GFP_KERNEL); + if (!sas_expander) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return -1; + } + + sas_expander->handle = handle; + sas_expander->num_phys = expander_pg0.NumPhys; + sas_expander->parent_handle = parent_handle; + sas_expander->enclosure_handle = + le16_to_cpu(expander_pg0.EnclosureHandle); + sas_expander->sas_address = sas_address; + + printk(MPT2SAS_INFO_FMT "expander_add: handle(0x%04x)," + " parent(0x%04x), sas_addr(0x%016llx), phys(%d)\n", ioc->name, + handle, sas_expander->parent_handle, (unsigned long long) + sas_expander->sas_address, sas_expander->num_phys); + + if (!sas_expander->num_phys) + goto out_fail; + sas_expander->phy = kcalloc(sas_expander->num_phys, + sizeof(struct _sas_phy), GFP_KERNEL); + if (!sas_expander->phy) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + rc = -1; + goto out_fail; + } + + INIT_LIST_HEAD(&sas_expander->sas_port_list); + mpt2sas_port = mpt2sas_transport_port_add(ioc, handle, + sas_expander->parent_handle); + if (!mpt2sas_port) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + rc = -1; + goto out_fail; + } + sas_expander->parent_dev = &mpt2sas_port->rphy->dev; + + for (i = 0 ; i < sas_expander->num_phys ; i++) { + if ((mpt2sas_config_get_expander_pg1(ioc, &mpi_reply, + &expander_pg1, i, handle))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + continue; + } + sas_expander->phy[i].handle = handle; + sas_expander->phy[i].phy_id = i; + mpt2sas_transport_add_expander_phy(ioc, &sas_expander->phy[i], + expander_pg1, sas_expander->parent_dev); + } + + if (sas_expander->enclosure_handle) { + if (!(mpt2sas_config_get_enclosure_pg0(ioc, &mpi_reply, + &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, + sas_expander->enclosure_handle))) { + sas_expander->enclosure_logical_id = + le64_to_cpu(enclosure_pg0.EnclosureLogicalID); + } + } + + _scsih_expander_node_add(ioc, sas_expander); + return 0; + + out_fail: + + if (sas_expander) + kfree(sas_expander->phy); + kfree(sas_expander); + return rc; +} + +/** + * _scsih_expander_remove - removing expander object + * @ioc: per adapter object + * @handle: expander handle + * + * Return nothing. + */ +static void +_scsih_expander_remove(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + struct _sas_node *sas_expander; + unsigned long flags; + + spin_lock_irqsave(&ioc->sas_node_lock, flags); + sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc, handle); + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); + _scsih_expander_node_remove(ioc, sas_expander); +} + +/** + * _scsih_add_device - creating sas device object + * @ioc: per adapter object + * @handle: sas device handle + * @phy_num: phy number end device attached to + * @is_pd: is this hidden raid component + * + * Creating end device object, stored in ioc->sas_device_list. + * + * Returns 0 for success, non-zero for failure. + */ +static int +_scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd) +{ + Mpi2ConfigReply_t mpi_reply; + Mpi2SasDevicePage0_t sas_device_pg0; + Mpi2SasEnclosurePage0_t enclosure_pg0; + struct _sas_device *sas_device; + u32 ioc_status; + __le64 sas_address; + u32 device_info; + unsigned long flags; + + if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, + MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return -1; + } + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return -1; + } + + /* check if device is present */ + if (!(le16_to_cpu(sas_device_pg0.Flags) & + MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + printk(MPT2SAS_ERR_FMT "Flags = 0x%04x\n", + ioc->name, le16_to_cpu(sas_device_pg0.Flags)); + return -1; + } + + /* check if there were any issus with discovery */ + if (sas_device_pg0.AccessStatus == + MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + printk(MPT2SAS_ERR_FMT "AccessStatus = 0x%02x\n", + ioc->name, sas_device_pg0.AccessStatus); + return -1; + } + + /* check if this is end device */ + device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); + if (!(_scsih_is_end_device(device_info))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return -1; + } + + sas_address = le64_to_cpu(sas_device_pg0.SASAddress); + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + sas_address); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + if (sas_device) { + _scsih_ublock_io_device(ioc, handle); + return 0; + } + + sas_device = kzalloc(sizeof(struct _sas_device), + GFP_KERNEL); + if (!sas_device) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return -1; + } + + sas_device->handle = handle; + sas_device->parent_handle = + le16_to_cpu(sas_device_pg0.ParentDevHandle); + sas_device->enclosure_handle = + le16_to_cpu(sas_device_pg0.EnclosureHandle); + sas_device->slot = + le16_to_cpu(sas_device_pg0.Slot); + sas_device->device_info = device_info; + sas_device->sas_address = sas_address; + sas_device->hidden_raid_component = is_pd; + + /* get enclosure_logical_id */ + if (!(mpt2sas_config_get_enclosure_pg0(ioc, &mpi_reply, &enclosure_pg0, + MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, + sas_device->enclosure_handle))) { + sas_device->enclosure_logical_id = + le64_to_cpu(enclosure_pg0.EnclosureLogicalID); + } + + /* get device name */ + sas_device->device_name = le64_to_cpu(sas_device_pg0.DeviceName); + + if (ioc->wait_for_port_enable_to_complete) + _scsih_sas_device_init_add(ioc, sas_device); + else + _scsih_sas_device_add(ioc, sas_device); + + return 0; +} + +/** + * _scsih_remove_device - removing sas device object + * @ioc: per adapter object + * @handle: sas device handle + * + * Return nothing. + */ +static void +_scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + struct MPT2SAS_TARGET *sas_target_priv_data; + struct _sas_device *sas_device; + unsigned long flags; + Mpi2SasIoUnitControlReply_t mpi_reply; + Mpi2SasIoUnitControlRequest_t mpi_request; + u16 device_handle; + + /* lookup sas_device */ + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + if (!sas_device) { + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + return; + } + + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter: handle" + "(0x%04x)\n", ioc->name, __func__, handle)); + + if (sas_device->starget && sas_device->starget->hostdata) { + sas_target_priv_data = sas_device->starget->hostdata; + sas_target_priv_data->deleted = 1; + } + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + if (ioc->remove_host) + goto out; + + /* Target Reset to flush out all the outstanding IO */ + device_handle = (sas_device->hidden_raid_component) ? + sas_device->volume_handle : handle; + if (device_handle) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "issue target reset: " + "handle(0x%04x)\n", ioc->name, device_handle)); + mutex_lock(&ioc->tm_cmds.mutex); + mpt2sas_scsih_issue_tm(ioc, device_handle, 0, + MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 10); + ioc->tm_cmds.status = MPT2_CMD_NOT_USED; + mutex_unlock(&ioc->tm_cmds.mutex); + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "issue target reset " + "done: handle(0x%04x)\n", ioc->name, device_handle)); + } + + /* SAS_IO_UNIT_CNTR - send REMOVE_DEVICE */ + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "sas_iounit: handle" + "(0x%04x)\n", ioc->name, handle)); + memset(&mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t)); + mpi_request.Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; + mpi_request.Operation = MPI2_SAS_OP_REMOVE_DEVICE; + mpi_request.DevHandle = handle; + mpi_request.VF_ID = 0; + if ((mpt2sas_base_sas_iounit_control(ioc, &mpi_reply, + &mpi_request)) != 0) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + } + + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "sas_iounit: ioc_status" + "(0x%04x), loginfo(0x%08x)\n", ioc->name, + le16_to_cpu(mpi_reply.IOCStatus), + le32_to_cpu(mpi_reply.IOCLogInfo))); + + out: + mpt2sas_transport_port_remove(ioc, sas_device->sas_address, + sas_device->parent_handle); + + printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), sas_addr" + "(0x%016llx)\n", ioc->name, sas_device->handle, + (unsigned long long) sas_device->sas_address); + _scsih_sas_device_remove(ioc, sas_device); + + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: exit: handle" + "(0x%04x)\n", ioc->name, __func__, handle)); +} + +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING +/** + * _scsih_sas_topology_change_event_debug - debug for topology event + * @ioc: per adapter object + * @event_data: event data payload + * Context: user. + */ +static void +_scsih_sas_topology_change_event_debug(struct MPT2SAS_ADAPTER *ioc, + Mpi2EventDataSasTopologyChangeList_t *event_data) +{ + int i; + u16 handle; + u16 reason_code; + u8 phy_number; + char *status_str = NULL; + char link_rate[25]; + + switch (event_data->ExpStatus) { + case MPI2_EVENT_SAS_TOPO_ES_ADDED: + status_str = "add"; + break; + case MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING: + status_str = "remove"; + break; + case MPI2_EVENT_SAS_TOPO_ES_RESPONDING: + status_str = "responding"; + break; + case MPI2_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING: + status_str = "remove delay"; + break; + default: + status_str = "unknown status"; + break; + } + printk(MPT2SAS_DEBUG_FMT "sas topology change: (%s)\n", + ioc->name, status_str); + printk(KERN_DEBUG "\thandle(0x%04x), enclosure_handle(0x%04x) " + "start_phy(%02d), count(%d)\n", + le16_to_cpu(event_data->ExpanderDevHandle), + le16_to_cpu(event_data->EnclosureHandle), + event_data->StartPhyNum, event_data->NumEntries); + for (i = 0; i < event_data->NumEntries; i++) { + handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); + if (!handle) + continue; + phy_number = event_data->StartPhyNum + i; + reason_code = event_data->PHY[i].PhyStatus & + MPI2_EVENT_SAS_TOPO_RC_MASK; + switch (reason_code) { + case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED: + snprintf(link_rate, 25, ": add, link(0x%02x)", + (event_data->PHY[i].LinkRate >> 4)); + status_str = link_rate; + break; + case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING: + status_str = ": remove"; + break; + case MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING: + status_str = ": remove_delay"; + break; + case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED: + snprintf(link_rate, 25, ": link(0x%02x)", + (event_data->PHY[i].LinkRate >> 4)); + status_str = link_rate; + break; + case MPI2_EVENT_SAS_TOPO_RC_NO_CHANGE: + status_str = ": responding"; + break; + default: + status_str = ": unknown"; + break; + } + printk(KERN_DEBUG "\tphy(%02d), attached_handle(0x%04x)%s\n", + phy_number, handle, status_str); + } +} +#endif + +/** + * _scsih_sas_topology_change_event - handle topology changes + * @ioc: per adapter object + * @VF_ID: + * @event_data: event data payload + * fw_event: + * Context: user. + * + */ +static void +_scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, + Mpi2EventDataSasTopologyChangeList_t *event_data, + struct fw_event_work *fw_event) +{ + int i; + u16 parent_handle, handle; + u16 reason_code; + u8 phy_number; + struct _sas_node *sas_expander; + unsigned long flags; + u8 link_rate_; + +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING + if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) + _scsih_sas_topology_change_event_debug(ioc, event_data); +#endif + + if (!ioc->sas_hba.num_phys) + _scsih_sas_host_add(ioc); + else + _scsih_sas_host_refresh(ioc, 0); + + if (fw_event->ignore) { + dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "ignoring expander " + "event\n", ioc->name)); + return; + } + + parent_handle = le16_to_cpu(event_data->ExpanderDevHandle); + + /* handle expander add */ + if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_ADDED) + if (_scsih_expander_add(ioc, parent_handle) != 0) + return; + + /* handle siblings events */ + for (i = 0; i < event_data->NumEntries; i++) { + if (fw_event->ignore) { + dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "ignoring " + "expander event\n", ioc->name)); + return; + } + if (event_data->PHY[i].PhyStatus & + MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT) + continue; + handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle); + if (!handle) + continue; + phy_number = event_data->StartPhyNum + i; + reason_code = event_data->PHY[i].PhyStatus & + MPI2_EVENT_SAS_TOPO_RC_MASK; + link_rate_ = event_data->PHY[i].LinkRate >> 4; + switch (reason_code) { + case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED: + case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED: + if (!parent_handle) { + if (phy_number < ioc->sas_hba.num_phys) + _scsih_link_change(ioc, + ioc->sas_hba.phy[phy_number].handle, + handle, phy_number, link_rate_); + } else { + spin_lock_irqsave(&ioc->sas_node_lock, flags); + sas_expander = + mpt2sas_scsih_expander_find_by_handle(ioc, + parent_handle); + spin_unlock_irqrestore(&ioc->sas_node_lock, + flags); + if (sas_expander) { + if (phy_number < sas_expander->num_phys) + _scsih_link_change(ioc, + sas_expander-> + phy[phy_number].handle, + handle, phy_number, + link_rate_); + } + } + if (reason_code == MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED) { + if (link_rate_ >= MPI2_SAS_NEG_LINK_RATE_1_5) + _scsih_ublock_io_device(ioc, handle); + } + if (reason_code == MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED) { + if (link_rate_ < MPI2_SAS_NEG_LINK_RATE_1_5) + break; + _scsih_add_device(ioc, handle, phy_number, 0); + } + break; + case MPI2_EVENT_SAS_TOPO_RC_TARG_NOT_RESPONDING: + _scsih_remove_device(ioc, handle); + break; + } + } + + /* handle expander removal */ + if (event_data->ExpStatus == MPI2_EVENT_SAS_TOPO_ES_NOT_RESPONDING) + _scsih_expander_remove(ioc, parent_handle); + +} + +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING +/** + * _scsih_sas_device_status_change_event_debug - debug for device event + * @event_data: event data payload + * Context: user. + * + * Return nothing. + */ +static void +_scsih_sas_device_status_change_event_debug(struct MPT2SAS_ADAPTER *ioc, + Mpi2EventDataSasDeviceStatusChange_t *event_data) +{ + char *reason_str = NULL; + + switch (event_data->ReasonCode) { + case MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA: + reason_str = "smart data"; + break; + case MPI2_EVENT_SAS_DEV_STAT_RC_UNSUPPORTED: + reason_str = "unsupported device discovered"; + break; + case MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET: + reason_str = "internal device reset"; + break; + case MPI2_EVENT_SAS_DEV_STAT_RC_TASK_ABORT_INTERNAL: + reason_str = "internal task abort"; + break; + case MPI2_EVENT_SAS_DEV_STAT_RC_ABORT_TASK_SET_INTERNAL: + reason_str = "internal task abort set"; + break; + case MPI2_EVENT_SAS_DEV_STAT_RC_CLEAR_TASK_SET_INTERNAL: + reason_str = "internal clear task set"; + break; + case MPI2_EVENT_SAS_DEV_STAT_RC_QUERY_TASK_INTERNAL: + reason_str = "internal query task"; + break; + case MPI2_EVENT_SAS_DEV_STAT_RC_SATA_INIT_FAILURE: + reason_str = "sata init failure"; + break; + case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET: + reason_str = "internal device reset complete"; + break; + case MPI2_EVENT_SAS_DEV_STAT_RC_CMP_TASK_ABORT_INTERNAL: + reason_str = "internal task abort complete"; + break; + case MPI2_EVENT_SAS_DEV_STAT_RC_ASYNC_NOTIFICATION: + reason_str = "internal async notification"; + break; + default: + reason_str = "unknown reason"; + break; + } + printk(MPT2SAS_DEBUG_FMT "device status change: (%s)\n" + "\thandle(0x%04x), sas address(0x%016llx)", ioc->name, + reason_str, le16_to_cpu(event_data->DevHandle), + (unsigned long long)le64_to_cpu(event_data->SASAddress)); + if (event_data->ReasonCode == MPI2_EVENT_SAS_DEV_STAT_RC_SMART_DATA) + printk(MPT2SAS_DEBUG_FMT ", ASC(0x%x), ASCQ(0x%x)\n", ioc->name, + event_data->ASC, event_data->ASCQ); + printk(KERN_INFO "\n"); +} +#endif + +/** + * _scsih_sas_device_status_change_event - handle device status change + * @ioc: per adapter object + * @VF_ID: + * @event_data: event data payload + * Context: user. + * + * Return nothing. + */ +static void +_scsih_sas_device_status_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, + Mpi2EventDataSasDeviceStatusChange_t *event_data) +{ +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING + if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) + _scsih_sas_device_status_change_event_debug(ioc, event_data); +#endif +} + +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING +/** + * _scsih_sas_enclosure_dev_status_change_event_debug - debug for enclosure event + * @ioc: per adapter object + * @event_data: event data payload + * Context: user. + * + * Return nothing. + */ +static void +_scsih_sas_enclosure_dev_status_change_event_debug(struct MPT2SAS_ADAPTER *ioc, + Mpi2EventDataSasEnclDevStatusChange_t *event_data) +{ + char *reason_str = NULL; + + switch (event_data->ReasonCode) { + case MPI2_EVENT_SAS_ENCL_RC_ADDED: + reason_str = "enclosure add"; + break; + case MPI2_EVENT_SAS_ENCL_RC_NOT_RESPONDING: + reason_str = "enclosure remove"; + break; + default: + reason_str = "unknown reason"; + break; + } + + printk(MPT2SAS_DEBUG_FMT "enclosure status change: (%s)\n" + "\thandle(0x%04x), enclosure logical id(0x%016llx)" + " number slots(%d)\n", ioc->name, reason_str, + le16_to_cpu(event_data->EnclosureHandle), + (unsigned long long)le64_to_cpu(event_data->EnclosureLogicalID), + le16_to_cpu(event_data->StartSlot)); +} +#endif + +/** + * _scsih_sas_enclosure_dev_status_change_event - handle enclosure events + * @ioc: per adapter object + * @VF_ID: + * @event_data: event data payload + * Context: user. + * + * Return nothing. + */ +static void +_scsih_sas_enclosure_dev_status_change_event(struct MPT2SAS_ADAPTER *ioc, + u8 VF_ID, Mpi2EventDataSasEnclDevStatusChange_t *event_data) +{ +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING + if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) + _scsih_sas_enclosure_dev_status_change_event_debug(ioc, + event_data); +#endif +} + +/** + * _scsih_sas_broadcast_primative_event - handle broadcast events + * @ioc: per adapter object + * @event_data: event data payload + * Context: user. + * + * Return nothing. + */ +static void +_scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, + Mpi2EventDataSasBroadcastPrimitive_t *event_data) +{ + struct scsi_cmnd *scmd; + u16 smid, handle; + u32 lun; + struct MPT2SAS_DEVICE *sas_device_priv_data; + u32 termination_count; + u32 query_count; + Mpi2SCSITaskManagementReply_t *mpi_reply; + + dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "broadcast primative: " + "phy number(%d), width(%d)\n", ioc->name, event_data->PhyNum, + event_data->PortWidth)); + + dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name, + __func__)); + + mutex_lock(&ioc->tm_cmds.mutex); + termination_count = 0; + query_count = 0; + mpi_reply = ioc->tm_cmds.reply; + for (smid = 1; smid <= ioc->request_depth; smid++) { + scmd = _scsih_scsi_lookup_get(ioc, smid); + if (!scmd) + continue; + sas_device_priv_data = scmd->device->hostdata; + if (!sas_device_priv_data || !sas_device_priv_data->sas_target) + continue; + /* skip hidden raid components */ + if (sas_device_priv_data->sas_target->flags & + MPT_TARGET_FLAGS_RAID_COMPONENT) + continue; + /* skip volumes */ + if (sas_device_priv_data->sas_target->flags & + MPT_TARGET_FLAGS_VOLUME) + continue; + + handle = sas_device_priv_data->sas_target->handle; + lun = sas_device_priv_data->lun; + query_count++; + + mpt2sas_scsih_issue_tm(ioc, handle, lun, + MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30); + termination_count += le32_to_cpu(mpi_reply->TerminationCount); + + if ((mpi_reply->IOCStatus == MPI2_IOCSTATUS_SUCCESS) && + (mpi_reply->ResponseCode == + MPI2_SCSITASKMGMT_RSP_TM_SUCCEEDED || + mpi_reply->ResponseCode == + MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC)) + continue; + + mpt2sas_scsih_issue_tm(ioc, handle, lun, + MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET, smid, 30); + termination_count += le32_to_cpu(mpi_reply->TerminationCount); + } + ioc->tm_cmds.status = MPT2_CMD_NOT_USED; + ioc->broadcast_aen_busy = 0; + mutex_unlock(&ioc->tm_cmds.mutex); + + dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT + "%s - exit, query_count = %d termination_count = %d\n", + ioc->name, __func__, query_count, termination_count)); +} + +/** + * _scsih_sas_discovery_event - handle discovery events + * @ioc: per adapter object + * @event_data: event data payload + * Context: user. + * + * Return nothing. + */ +static void +_scsih_sas_discovery_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, + Mpi2EventDataSasDiscovery_t *event_data) +{ +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING + if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) { + printk(MPT2SAS_DEBUG_FMT "discovery event: (%s)", ioc->name, + (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED) ? + "start" : "stop"); + if (event_data->DiscoveryStatus) + printk(MPT2SAS_DEBUG_FMT ", discovery_status(0x%08x)", + ioc->name, le32_to_cpu(event_data->DiscoveryStatus)); + printk("\n"); + } +#endif + + if (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED && + !ioc->sas_hba.num_phys) + _scsih_sas_host_add(ioc); +} + +/** + * _scsih_reprobe_lun - reprobing lun + * @sdev: scsi device struct + * @no_uld_attach: sdev->no_uld_attach flag setting + * + **/ +static void +_scsih_reprobe_lun(struct scsi_device *sdev, void *no_uld_attach) +{ + int rc; + + sdev->no_uld_attach = no_uld_attach ? 1 : 0; + sdev_printk(KERN_INFO, sdev, "%s raid component\n", + sdev->no_uld_attach ? "hidding" : "exposing"); + rc = scsi_device_reprobe(sdev); +} + +/** + * _scsih_reprobe_target - reprobing target + * @starget: scsi target struct + * @no_uld_attach: sdev->no_uld_attach flag setting + * + * Note: no_uld_attach flag determines whether the disk device is attached + * to block layer. A value of `1` means to not attach. + **/ +static void +_scsih_reprobe_target(struct scsi_target *starget, int no_uld_attach) +{ + struct MPT2SAS_TARGET *sas_target_priv_data = starget->hostdata; + + if (no_uld_attach) + sas_target_priv_data->flags |= MPT_TARGET_FLAGS_RAID_COMPONENT; + else + sas_target_priv_data->flags &= ~MPT_TARGET_FLAGS_RAID_COMPONENT; + + starget_for_each_device(starget, no_uld_attach ? (void *)1 : NULL, + _scsih_reprobe_lun); +} +/** + * _scsih_sas_volume_add - add new volume + * @ioc: per adapter object + * @element: IR config element data + * Context: user. + * + * Return nothing. + */ +static void +_scsih_sas_volume_add(struct MPT2SAS_ADAPTER *ioc, + Mpi2EventIrConfigElement_t *element) +{ + struct _raid_device *raid_device; + unsigned long flags; + u64 wwid; + u16 handle = le16_to_cpu(element->VolDevHandle); + int rc; + +#if 0 /* RAID_HACKS */ + if (le32_to_cpu(event_data->Flags) & + MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) + return; +#endif + + mpt2sas_config_get_volume_wwid(ioc, handle, &wwid); + if (!wwid) { + printk(MPT2SAS_ERR_FMT + "failure at %s:%d/%s()!\n", ioc->name, + __FILE__, __LINE__, __func__); + return; + } + + spin_lock_irqsave(&ioc->raid_device_lock, flags); + raid_device = _scsih_raid_device_find_by_wwid(ioc, wwid); + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); + + if (raid_device) + return; + + raid_device = kzalloc(sizeof(struct _raid_device), GFP_KERNEL); + if (!raid_device) { + printk(MPT2SAS_ERR_FMT + "failure at %s:%d/%s()!\n", ioc->name, + __FILE__, __LINE__, __func__); + return; + } + + raid_device->id = ioc->sas_id++; + raid_device->channel = RAID_CHANNEL; + raid_device->handle = handle; + raid_device->wwid = wwid; + _scsih_raid_device_add(ioc, raid_device); + if (!ioc->wait_for_port_enable_to_complete) { + rc = scsi_add_device(ioc->shost, RAID_CHANNEL, + raid_device->id, 0); + if (rc) + _scsih_raid_device_remove(ioc, raid_device); + } else + _scsih_determine_boot_device(ioc, raid_device, 1); +} + +/** + * _scsih_sas_volume_delete - delete volume + * @ioc: per adapter object + * @element: IR config element data + * Context: user. + * + * Return nothing. + */ +static void +_scsih_sas_volume_delete(struct MPT2SAS_ADAPTER *ioc, + Mpi2EventIrConfigElement_t *element) +{ + struct _raid_device *raid_device; + u16 handle = le16_to_cpu(element->VolDevHandle); + unsigned long flags; + struct MPT2SAS_TARGET *sas_target_priv_data; + +#if 0 /* RAID_HACKS */ + if (le32_to_cpu(event_data->Flags) & + MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) + return; +#endif + + spin_lock_irqsave(&ioc->raid_device_lock, flags); + raid_device = _scsih_raid_device_find_by_handle(ioc, handle); + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); + if (!raid_device) + return; + if (raid_device->starget) { + sas_target_priv_data = raid_device->starget->hostdata; + sas_target_priv_data->deleted = 1; + scsi_remove_target(&raid_device->starget->dev); + } + _scsih_raid_device_remove(ioc, raid_device); +} + +/** + * _scsih_sas_pd_expose - expose pd component to /dev/sdX + * @ioc: per adapter object + * @element: IR config element data + * Context: user. + * + * Return nothing. + */ +static void +_scsih_sas_pd_expose(struct MPT2SAS_ADAPTER *ioc, + Mpi2EventIrConfigElement_t *element) +{ + struct _sas_device *sas_device; + unsigned long flags; + u16 handle = le16_to_cpu(element->PhysDiskDevHandle); + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + if (!sas_device) + return; + + /* exposing raid component */ + sas_device->volume_handle = 0; + sas_device->volume_wwid = 0; + sas_device->hidden_raid_component = 0; + _scsih_reprobe_target(sas_device->starget, 0); +} + +/** + * _scsih_sas_pd_hide - hide pd component from /dev/sdX + * @ioc: per adapter object + * @element: IR config element data + * Context: user. + * + * Return nothing. + */ +static void +_scsih_sas_pd_hide(struct MPT2SAS_ADAPTER *ioc, + Mpi2EventIrConfigElement_t *element) +{ + struct _sas_device *sas_device; + unsigned long flags; + u16 handle = le16_to_cpu(element->PhysDiskDevHandle); + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + if (!sas_device) + return; + + /* hiding raid component */ + mpt2sas_config_get_volume_handle(ioc, handle, + &sas_device->volume_handle); + mpt2sas_config_get_volume_wwid(ioc, sas_device->volume_handle, + &sas_device->volume_wwid); + sas_device->hidden_raid_component = 1; + _scsih_reprobe_target(sas_device->starget, 1); +} + +/** + * _scsih_sas_pd_delete - delete pd component + * @ioc: per adapter object + * @element: IR config element data + * Context: user. + * + * Return nothing. + */ +static void +_scsih_sas_pd_delete(struct MPT2SAS_ADAPTER *ioc, + Mpi2EventIrConfigElement_t *element) +{ + struct _sas_device *sas_device; + unsigned long flags; + u16 handle = le16_to_cpu(element->PhysDiskDevHandle); + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + if (!sas_device) + return; + _scsih_remove_device(ioc, handle); +} + +/** + * _scsih_sas_pd_add - remove pd component + * @ioc: per adapter object + * @element: IR config element data + * Context: user. + * + * Return nothing. + */ +static void +_scsih_sas_pd_add(struct MPT2SAS_ADAPTER *ioc, + Mpi2EventIrConfigElement_t *element) +{ + struct _sas_device *sas_device; + unsigned long flags; + u16 handle = le16_to_cpu(element->PhysDiskDevHandle); + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + if (sas_device) + sas_device->hidden_raid_component = 1; + else + _scsih_add_device(ioc, handle, 0, 1); +} + +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING +/** + * _scsih_sas_ir_config_change_event_debug - debug for IR Config Change events + * @ioc: per adapter object + * @event_data: event data payload + * Context: user. + * + * Return nothing. + */ +static void +_scsih_sas_ir_config_change_event_debug(struct MPT2SAS_ADAPTER *ioc, + Mpi2EventDataIrConfigChangeList_t *event_data) +{ + Mpi2EventIrConfigElement_t *element; + u8 element_type; + int i; + char *reason_str = NULL, *element_str = NULL; + + element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; + + printk(MPT2SAS_DEBUG_FMT "raid config change: (%s), elements(%d)\n", + ioc->name, (le32_to_cpu(event_data->Flags) & + MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ? + "foreign" : "native", event_data->NumElements); + for (i = 0; i < event_data->NumElements; i++, element++) { + switch (element->ReasonCode) { + case MPI2_EVENT_IR_CHANGE_RC_ADDED: + reason_str = "add"; + break; + case MPI2_EVENT_IR_CHANGE_RC_REMOVED: + reason_str = "remove"; + break; + case MPI2_EVENT_IR_CHANGE_RC_NO_CHANGE: + reason_str = "no change"; + break; + case MPI2_EVENT_IR_CHANGE_RC_HIDE: + reason_str = "hide"; + break; + case MPI2_EVENT_IR_CHANGE_RC_UNHIDE: + reason_str = "unhide"; + break; + case MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED: + reason_str = "volume_created"; + break; + case MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED: + reason_str = "volume_deleted"; + break; + case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED: + reason_str = "pd_created"; + break; + case MPI2_EVENT_IR_CHANGE_RC_PD_DELETED: + reason_str = "pd_deleted"; + break; + default: + reason_str = "unknown reason"; + break; + } + element_type = le16_to_cpu(element->ElementFlags) & + MPI2_EVENT_IR_CHANGE_EFLAGS_ELEMENT_TYPE_MASK; + switch (element_type) { + case MPI2_EVENT_IR_CHANGE_EFLAGS_VOLUME_ELEMENT: + element_str = "volume"; + break; + case MPI2_EVENT_IR_CHANGE_EFLAGS_VOLPHYSDISK_ELEMENT: + element_str = "phys disk"; + break; + case MPI2_EVENT_IR_CHANGE_EFLAGS_HOTSPARE_ELEMENT: + element_str = "hot spare"; + break; + default: + element_str = "unknown element"; + break; + } + printk(KERN_DEBUG "\t(%s:%s), vol handle(0x%04x), " + "pd handle(0x%04x), pd num(0x%02x)\n", element_str, + reason_str, le16_to_cpu(element->VolDevHandle), + le16_to_cpu(element->PhysDiskDevHandle), + element->PhysDiskNum); + } +} +#endif + +/** + * _scsih_sas_ir_config_change_event - handle ir configuration change events + * @ioc: per adapter object + * @VF_ID: + * @event_data: event data payload + * Context: user. + * + * Return nothing. + */ +static void +_scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, + Mpi2EventDataIrConfigChangeList_t *event_data) +{ + Mpi2EventIrConfigElement_t *element; + int i; + +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING + if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) + _scsih_sas_ir_config_change_event_debug(ioc, event_data); + +#endif + + element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; + for (i = 0; i < event_data->NumElements; i++, element++) { + + switch (element->ReasonCode) { + case MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED: + case MPI2_EVENT_IR_CHANGE_RC_ADDED: + _scsih_sas_volume_add(ioc, element); + break; + case MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED: + case MPI2_EVENT_IR_CHANGE_RC_REMOVED: + _scsih_sas_volume_delete(ioc, element); + break; + case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED: + _scsih_sas_pd_hide(ioc, element); + break; + case MPI2_EVENT_IR_CHANGE_RC_PD_DELETED: + _scsih_sas_pd_expose(ioc, element); + break; + case MPI2_EVENT_IR_CHANGE_RC_HIDE: + _scsih_sas_pd_add(ioc, element); + break; + case MPI2_EVENT_IR_CHANGE_RC_UNHIDE: + _scsih_sas_pd_delete(ioc, element); + break; + } + } +} + +/** + * _scsih_sas_ir_volume_event - IR volume event + * @ioc: per adapter object + * @event_data: event data payload + * Context: user. + * + * Return nothing. + */ +static void +_scsih_sas_ir_volume_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, + Mpi2EventDataIrVolume_t *event_data) +{ + u64 wwid; + unsigned long flags; + struct _raid_device *raid_device; + u16 handle; + u32 state; + int rc; + struct MPT2SAS_TARGET *sas_target_priv_data; + + if (event_data->ReasonCode != MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED) + return; + + handle = le16_to_cpu(event_data->VolDevHandle); + state = le32_to_cpu(event_data->NewValue); + dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: handle(0x%04x), " + "old(0x%08x), new(0x%08x)\n", ioc->name, __func__, handle, + le32_to_cpu(event_data->PreviousValue), state)); + + spin_lock_irqsave(&ioc->raid_device_lock, flags); + raid_device = _scsih_raid_device_find_by_handle(ioc, handle); + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); + + switch (state) { + case MPI2_RAID_VOL_STATE_MISSING: + case MPI2_RAID_VOL_STATE_FAILED: + if (!raid_device) + break; + if (raid_device->starget) { + sas_target_priv_data = raid_device->starget->hostdata; + sas_target_priv_data->deleted = 1; + scsi_remove_target(&raid_device->starget->dev); + } + _scsih_raid_device_remove(ioc, raid_device); + break; + + case MPI2_RAID_VOL_STATE_ONLINE: + case MPI2_RAID_VOL_STATE_DEGRADED: + case MPI2_RAID_VOL_STATE_OPTIMAL: + if (raid_device) + break; + + mpt2sas_config_get_volume_wwid(ioc, handle, &wwid); + if (!wwid) { + printk(MPT2SAS_ERR_FMT + "failure at %s:%d/%s()!\n", ioc->name, + __FILE__, __LINE__, __func__); + break; + } + + raid_device = kzalloc(sizeof(struct _raid_device), GFP_KERNEL); + if (!raid_device) { + printk(MPT2SAS_ERR_FMT + "failure at %s:%d/%s()!\n", ioc->name, + __FILE__, __LINE__, __func__); + break; + } + + raid_device->id = ioc->sas_id++; + raid_device->channel = RAID_CHANNEL; + raid_device->handle = handle; + raid_device->wwid = wwid; + _scsih_raid_device_add(ioc, raid_device); + rc = scsi_add_device(ioc->shost, RAID_CHANNEL, + raid_device->id, 0); + if (rc) + _scsih_raid_device_remove(ioc, raid_device); + break; + + case MPI2_RAID_VOL_STATE_INITIALIZING: + default: + break; + } +} + +/** + * _scsih_sas_ir_physical_disk_event - PD event + * @ioc: per adapter object + * @event_data: event data payload + * Context: user. + * + * Return nothing. + */ +static void +_scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, + Mpi2EventDataIrPhysicalDisk_t *event_data) +{ + u16 handle; + u32 state; + struct _sas_device *sas_device; + unsigned long flags; + + if (event_data->ReasonCode != MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED) + return; + + handle = le16_to_cpu(event_data->PhysDiskDevHandle); + state = le32_to_cpu(event_data->NewValue); + + dewtprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: handle(0x%04x), " + "old(0x%08x), new(0x%08x)\n", ioc->name, __func__, handle, + le32_to_cpu(event_data->PreviousValue), state)); + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + switch (state) { +#if 0 + case MPI2_RAID_PD_STATE_OFFLINE: + if (sas_device) + _scsih_remove_device(ioc, handle); + break; +#endif + case MPI2_RAID_PD_STATE_ONLINE: + case MPI2_RAID_PD_STATE_DEGRADED: + case MPI2_RAID_PD_STATE_REBUILDING: + case MPI2_RAID_PD_STATE_OPTIMAL: + if (sas_device) + sas_device->hidden_raid_component = 1; + else + _scsih_add_device(ioc, handle, 0, 1); + break; + + case MPI2_RAID_PD_STATE_NOT_CONFIGURED: + case MPI2_RAID_PD_STATE_NOT_COMPATIBLE: + case MPI2_RAID_PD_STATE_HOT_SPARE: + default: + break; + } +} + +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING +/** + * _scsih_sas_ir_operation_status_event_debug - debug for IR op event + * @ioc: per adapter object + * @event_data: event data payload + * Context: user. + * + * Return nothing. + */ +static void +_scsih_sas_ir_operation_status_event_debug(struct MPT2SAS_ADAPTER *ioc, + Mpi2EventDataIrOperationStatus_t *event_data) +{ + char *reason_str = NULL; + + switch (event_data->RAIDOperation) { + case MPI2_EVENT_IR_RAIDOP_RESYNC: + reason_str = "resync"; + break; + case MPI2_EVENT_IR_RAIDOP_ONLINE_CAP_EXPANSION: + reason_str = "online capacity expansion"; + break; + case MPI2_EVENT_IR_RAIDOP_CONSISTENCY_CHECK: + reason_str = "consistency check"; + break; + default: + reason_str = "unknown reason"; + break; + } + + printk(MPT2SAS_INFO_FMT "raid operational status: (%s)" + "\thandle(0x%04x), percent complete(%d)\n", + ioc->name, reason_str, + le16_to_cpu(event_data->VolDevHandle), + event_data->PercentComplete); +} +#endif + +/** + * _scsih_sas_ir_operation_status_event - handle RAID operation events + * @ioc: per adapter object + * @VF_ID: + * @event_data: event data payload + * Context: user. + * + * Return nothing. + */ +static void +_scsih_sas_ir_operation_status_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, + Mpi2EventDataIrOperationStatus_t *event_data) +{ +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING + if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) + _scsih_sas_ir_operation_status_event_debug(ioc, event_data); +#endif +} + +/** + * _scsih_task_set_full - handle task set full + * @ioc: per adapter object + * @event_data: event data payload + * Context: user. + * + * Throttle back qdepth. + */ +static void +_scsih_task_set_full(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, + Mpi2EventDataTaskSetFull_t *event_data) +{ + unsigned long flags; + struct _sas_device *sas_device; + static struct _raid_device *raid_device; + struct scsi_device *sdev; + int depth; + u16 current_depth; + u16 handle; + int id, channel; + u64 sas_address; + + current_depth = le16_to_cpu(event_data->CurrentDepth); + handle = le16_to_cpu(event_data->DevHandle); + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = _scsih_sas_device_find_by_handle(ioc, handle); + if (!sas_device) { + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + return; + } + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + id = sas_device->id; + channel = sas_device->channel; + sas_address = sas_device->sas_address; + + /* if hidden raid component, then change to volume characteristics */ + if (sas_device->hidden_raid_component && sas_device->volume_handle) { + spin_lock_irqsave(&ioc->raid_device_lock, flags); + raid_device = _scsih_raid_device_find_by_handle( + ioc, sas_device->volume_handle); + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); + if (raid_device) { + id = raid_device->id; + channel = raid_device->channel; + handle = raid_device->handle; + sas_address = raid_device->wwid; + } + } + + if (ioc->logging_level & MPT_DEBUG_TASK_SET_FULL) + starget_printk(KERN_DEBUG, sas_device->starget, "task set " + "full: handle(0x%04x), sas_addr(0x%016llx), depth(%d)\n", + handle, (unsigned long long)sas_address, current_depth); + + shost_for_each_device(sdev, ioc->shost) { + if (sdev->id == id && sdev->channel == channel) { + if (current_depth > sdev->queue_depth) { + if (ioc->logging_level & + MPT_DEBUG_TASK_SET_FULL) + sdev_printk(KERN_INFO, sdev, "strange " + "observation, the queue depth is" + " (%d) meanwhile fw queue depth " + "is (%d)\n", sdev->queue_depth, + current_depth); + continue; + } + depth = scsi_track_queue_full(sdev, + current_depth - 1); + if (depth > 0) + sdev_printk(KERN_INFO, sdev, "Queue depth " + "reduced to (%d)\n", depth); + else if (depth < 0) + sdev_printk(KERN_INFO, sdev, "Tagged Command " + "Queueing is being disabled\n"); + else if (depth == 0) + if (ioc->logging_level & + MPT_DEBUG_TASK_SET_FULL) + sdev_printk(KERN_INFO, sdev, + "Queue depth not changed yet\n"); + } + } +} + +/** + * _scsih_mark_responding_sas_device - mark a sas_devices as responding + * @ioc: per adapter object + * @sas_address: sas address + * @slot: enclosure slot id + * @handle: device handle + * + * After host reset, find out whether devices are still responding. + * Used in _scsi_remove_unresponsive_sas_devices. + * + * Return nothing. + */ +static void +_scsih_mark_responding_sas_device(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, + u16 slot, u16 handle) +{ + struct MPT2SAS_TARGET *sas_target_priv_data; + struct scsi_target *starget; + struct _sas_device *sas_device; + unsigned long flags; + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + list_for_each_entry(sas_device, &ioc->sas_device_list, list) { + if (sas_device->sas_address == sas_address && + sas_device->slot == slot && sas_device->starget) { + sas_device->responding = 1; + starget_printk(KERN_INFO, sas_device->starget, + "handle(0x%04x), sas_addr(0x%016llx), enclosure " + "logical id(0x%016llx), slot(%d)\n", handle, + (unsigned long long)sas_device->sas_address, + (unsigned long long) + sas_device->enclosure_logical_id, + sas_device->slot); + if (sas_device->handle == handle) + goto out; + printk(KERN_INFO "\thandle changed from(0x%04x)!!!\n", + sas_device->handle); + sas_device->handle = handle; + starget = sas_device->starget; + sas_target_priv_data = starget->hostdata; + sas_target_priv_data->handle = handle; + goto out; + } + } + out: + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); +} + +/** + * _scsih_search_responding_sas_devices - + * @ioc: per adapter object + * + * After host reset, find out whether devices are still responding. + * If not remove. + * + * Return nothing. + */ +static void +_scsih_search_responding_sas_devices(struct MPT2SAS_ADAPTER *ioc) +{ + Mpi2SasDevicePage0_t sas_device_pg0; + Mpi2ConfigReply_t mpi_reply; + u16 ioc_status; + __le64 sas_address; + u16 handle; + u32 device_info; + u16 slot; + + printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__); + + if (list_empty(&ioc->sas_device_list)) + return; + + handle = 0xFFFF; + while (!(mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, + &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE, + handle))) { + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) + break; + handle = le16_to_cpu(sas_device_pg0.DevHandle); + device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); + if (!(_scsih_is_end_device(device_info))) + continue; + sas_address = le64_to_cpu(sas_device_pg0.SASAddress); + slot = le16_to_cpu(sas_device_pg0.Slot); + _scsih_mark_responding_sas_device(ioc, sas_address, slot, + handle); + } +} + +/** + * _scsih_mark_responding_raid_device - mark a raid_device as responding + * @ioc: per adapter object + * @wwid: world wide identifier for raid volume + * @handle: device handle + * + * After host reset, find out whether devices are still responding. + * Used in _scsi_remove_unresponsive_raid_devices. + * + * Return nothing. + */ +static void +_scsih_mark_responding_raid_device(struct MPT2SAS_ADAPTER *ioc, u64 wwid, + u16 handle) +{ + struct MPT2SAS_TARGET *sas_target_priv_data; + struct scsi_target *starget; + struct _raid_device *raid_device; + unsigned long flags; + + spin_lock_irqsave(&ioc->raid_device_lock, flags); + list_for_each_entry(raid_device, &ioc->raid_device_list, list) { + if (raid_device->wwid == wwid && raid_device->starget) { + raid_device->responding = 1; + starget_printk(KERN_INFO, raid_device->starget, + "handle(0x%04x), wwid(0x%016llx)\n", handle, + (unsigned long long)raid_device->wwid); + if (raid_device->handle == handle) + goto out; + printk(KERN_INFO "\thandle changed from(0x%04x)!!!\n", + raid_device->handle); + raid_device->handle = handle; + starget = raid_device->starget; + sas_target_priv_data = starget->hostdata; + sas_target_priv_data->handle = handle; + goto out; + } + } + out: + spin_unlock_irqrestore(&ioc->raid_device_lock, flags); +} + +/** + * _scsih_search_responding_raid_devices - + * @ioc: per adapter object + * + * After host reset, find out whether devices are still responding. + * If not remove. + * + * Return nothing. + */ +static void +_scsih_search_responding_raid_devices(struct MPT2SAS_ADAPTER *ioc) +{ + Mpi2RaidVolPage1_t volume_pg1; + Mpi2ConfigReply_t mpi_reply; + u16 ioc_status; + u16 handle; + + printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__); + + if (list_empty(&ioc->raid_device_list)) + return; + + handle = 0xFFFF; + while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply, + &volume_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) { + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) + break; + handle = le16_to_cpu(volume_pg1.DevHandle); + _scsih_mark_responding_raid_device(ioc, + le64_to_cpu(volume_pg1.WWID), handle); + } +} + +/** + * _scsih_mark_responding_expander - mark a expander as responding + * @ioc: per adapter object + * @sas_address: sas address + * @handle: + * + * After host reset, find out whether devices are still responding. + * Used in _scsi_remove_unresponsive_expanders. + * + * Return nothing. + */ +static void +_scsih_mark_responding_expander(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, + u16 handle) +{ + struct _sas_node *sas_expander; + unsigned long flags; + + spin_lock_irqsave(&ioc->sas_node_lock, flags); + list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) { + if (sas_expander->sas_address == sas_address) { + sas_expander->responding = 1; + if (sas_expander->handle != handle) { + printk(KERN_INFO "old handle(0x%04x)\n", + sas_expander->handle); + sas_expander->handle = handle; + } + goto out; + } + } + out: + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); +} + +/** + * _scsih_search_responding_expanders - + * @ioc: per adapter object + * + * After host reset, find out whether devices are still responding. + * If not remove. + * + * Return nothing. + */ +static void +_scsih_search_responding_expanders(struct MPT2SAS_ADAPTER *ioc) +{ + Mpi2ExpanderPage0_t expander_pg0; + Mpi2ConfigReply_t mpi_reply; + u16 ioc_status; + __le64 sas_address; + u16 handle; + + printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__); + + if (list_empty(&ioc->sas_expander_list)) + return; + + handle = 0xFFFF; + while (!(mpt2sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0, + MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL, handle))) { + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) + break; + + handle = le16_to_cpu(expander_pg0.DevHandle); + sas_address = le64_to_cpu(expander_pg0.SASAddress); + printk(KERN_INFO "\texpander present: handle(0x%04x), " + "sas_addr(0x%016llx)\n", handle, + (unsigned long long)sas_address); + _scsih_mark_responding_expander(ioc, sas_address, handle); + } + +} + +/** + * _scsih_remove_unresponding_devices - removing unresponding devices + * @ioc: per adapter object + * + * Return nothing. + */ +static void +_scsih_remove_unresponding_devices(struct MPT2SAS_ADAPTER *ioc) +{ + struct _sas_device *sas_device, *sas_device_next; + struct _sas_node *sas_expander, *sas_expander_next; + struct _raid_device *raid_device, *raid_device_next; + unsigned long flags; + + _scsih_search_responding_sas_devices(ioc); + _scsih_search_responding_raid_devices(ioc); + _scsih_search_responding_expanders(ioc); + + spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); + ioc->shost_recovery = 0; + if (ioc->shost->shost_state == SHOST_RECOVERY) { + printk(MPT2SAS_INFO_FMT "putting controller into " + "SHOST_RUNNING\n", ioc->name); + scsi_host_set_state(ioc->shost, SHOST_RUNNING); + } + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); + + list_for_each_entry_safe(sas_device, sas_device_next, + &ioc->sas_device_list, list) { + if (sas_device->responding) { + sas_device->responding = 0; + continue; + } + if (sas_device->starget) + starget_printk(KERN_INFO, sas_device->starget, + "removing: handle(0x%04x), sas_addr(0x%016llx), " + "enclosure logical id(0x%016llx), slot(%d)\n", + sas_device->handle, + (unsigned long long)sas_device->sas_address, + (unsigned long long) + sas_device->enclosure_logical_id, + sas_device->slot); + _scsih_remove_device(ioc, sas_device->handle); + } + + list_for_each_entry_safe(raid_device, raid_device_next, + &ioc->raid_device_list, list) { + if (raid_device->responding) { + raid_device->responding = 0; + continue; + } + if (raid_device->starget) { + starget_printk(KERN_INFO, raid_device->starget, + "removing: handle(0x%04x), wwid(0x%016llx)\n", + raid_device->handle, + (unsigned long long)raid_device->wwid); + scsi_remove_target(&raid_device->starget->dev); + } + _scsih_raid_device_remove(ioc, raid_device); + } + + list_for_each_entry_safe(sas_expander, sas_expander_next, + &ioc->sas_expander_list, list) { + if (sas_expander->responding) { + sas_expander->responding = 0; + continue; + } + printk("\tremoving expander: handle(0x%04x), " + " sas_addr(0x%016llx)\n", sas_expander->handle, + (unsigned long long)sas_expander->sas_address); + _scsih_expander_remove(ioc, sas_expander->handle); + } +} + +/** + * _firmware_event_work - delayed task for processing firmware events + * @ioc: per adapter object + * @work: equal to the fw_event_work object + * Context: user. + * + * Return nothing. + */ +static void +_firmware_event_work(struct work_struct *work) +{ + struct fw_event_work *fw_event = container_of(work, + struct fw_event_work, work.work); + unsigned long flags; + struct MPT2SAS_ADAPTER *ioc = fw_event->ioc; + + /* This is invoked by calling _scsih_queue_rescan(). */ + if (fw_event->event == MPT2SAS_RESCAN_AFTER_HOST_RESET) { + _scsih_fw_event_free(ioc, fw_event); + _scsih_sas_host_refresh(ioc, 1); + _scsih_remove_unresponding_devices(ioc); + return; + } + + /* the queue is being flushed so ignore this event */ + spin_lock_irqsave(&ioc->fw_event_lock, flags); + if (ioc->fw_events_off || ioc->remove_host) { + spin_unlock_irqrestore(&ioc->fw_event_lock, flags); + _scsih_fw_event_free(ioc, fw_event); + return; + } + spin_unlock_irqrestore(&ioc->fw_event_lock, flags); + + spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); + if (ioc->shost_recovery) { + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); + _scsih_fw_event_requeue(ioc, fw_event, 1000); + return; + } + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); + + switch (fw_event->event) { + case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST: + _scsih_sas_topology_change_event(ioc, fw_event->VF_ID, + fw_event->event_data, fw_event); + break; + case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE: + _scsih_sas_device_status_change_event(ioc, fw_event->VF_ID, + fw_event->event_data); + break; + case MPI2_EVENT_SAS_DISCOVERY: + _scsih_sas_discovery_event(ioc, fw_event->VF_ID, + fw_event->event_data); + break; + case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE: + _scsih_sas_broadcast_primative_event(ioc, fw_event->VF_ID, + fw_event->event_data); + break; + case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE: + _scsih_sas_enclosure_dev_status_change_event(ioc, + fw_event->VF_ID, fw_event->event_data); + break; + case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST: + _scsih_sas_ir_config_change_event(ioc, fw_event->VF_ID, + fw_event->event_data); + break; + case MPI2_EVENT_IR_VOLUME: + _scsih_sas_ir_volume_event(ioc, fw_event->VF_ID, + fw_event->event_data); + break; + case MPI2_EVENT_IR_PHYSICAL_DISK: + _scsih_sas_ir_physical_disk_event(ioc, fw_event->VF_ID, + fw_event->event_data); + break; + case MPI2_EVENT_IR_OPERATION_STATUS: + _scsih_sas_ir_operation_status_event(ioc, fw_event->VF_ID, + fw_event->event_data); + break; + case MPI2_EVENT_TASK_SET_FULL: + _scsih_task_set_full(ioc, fw_event->VF_ID, + fw_event->event_data); + break; + } + _scsih_fw_event_free(ioc, fw_event); +} + +/** + * mpt2sas_scsih_event_callback - firmware event handler (called at ISR time) + * @ioc: per adapter object + * @VF_ID: virtual function id + * @reply: reply message frame(lower 32bit addr) + * Context: interrupt. + * + * This function merely adds a new work task into ioc->firmware_event_thread. + * The tasks are worked from _firmware_event_work in user context. + * + * Return nothing. + */ +void +mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 reply) +{ + struct fw_event_work *fw_event; + Mpi2EventNotificationReply_t *mpi_reply; + unsigned long flags; + u16 event; + + /* events turned off due to host reset or driver unloading */ + spin_lock_irqsave(&ioc->fw_event_lock, flags); + if (ioc->fw_events_off || ioc->remove_host) { + spin_unlock_irqrestore(&ioc->fw_event_lock, flags); + return; + } + spin_unlock_irqrestore(&ioc->fw_event_lock, flags); + + mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); + event = le16_to_cpu(mpi_reply->Event); + + switch (event) { + /* handle these */ + case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE: + { + Mpi2EventDataSasBroadcastPrimitive_t *baen_data = + (Mpi2EventDataSasBroadcastPrimitive_t *) + mpi_reply->EventData; + + if (baen_data->Primitive != + MPI2_EVENT_PRIMITIVE_ASYNCHRONOUS_EVENT || + ioc->broadcast_aen_busy) + return; + ioc->broadcast_aen_busy = 1; + break; + } + + case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST: + _scsih_check_topo_delete_events(ioc, + (Mpi2EventDataSasTopologyChangeList_t *) + mpi_reply->EventData); + break; + + case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE: + case MPI2_EVENT_IR_OPERATION_STATUS: + case MPI2_EVENT_SAS_DISCOVERY: + case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE: + case MPI2_EVENT_IR_VOLUME: + case MPI2_EVENT_IR_PHYSICAL_DISK: + case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST: + case MPI2_EVENT_TASK_SET_FULL: + break; + + default: /* ignore the rest */ + return; + } + + fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); + if (!fw_event) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return; + } + fw_event->event_data = + kzalloc(mpi_reply->EventDataLength*4, GFP_ATOMIC); + if (!fw_event->event_data) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + kfree(fw_event); + return; + } + + memcpy(fw_event->event_data, mpi_reply->EventData, + mpi_reply->EventDataLength*4); + fw_event->ioc = ioc; + fw_event->VF_ID = VF_ID; + fw_event->event = event; + _scsih_fw_event_add(ioc, fw_event); +} + +/* shost template */ +static struct scsi_host_template scsih_driver_template = { + .module = THIS_MODULE, + .name = "Fusion MPT SAS Host", + .proc_name = MPT2SAS_DRIVER_NAME, + .queuecommand = scsih_qcmd, + .target_alloc = scsih_target_alloc, + .slave_alloc = scsih_slave_alloc, + .slave_configure = scsih_slave_configure, + .target_destroy = scsih_target_destroy, + .slave_destroy = scsih_slave_destroy, + .change_queue_depth = scsih_change_queue_depth, + .change_queue_type = scsih_change_queue_type, + .eh_abort_handler = scsih_abort, + .eh_device_reset_handler = scsih_dev_reset, + .eh_host_reset_handler = scsih_host_reset, + .bios_param = scsih_bios_param, + .can_queue = 1, + .this_id = -1, + .sg_tablesize = MPT2SAS_SG_DEPTH, + .max_sectors = 8192, + .cmd_per_lun = 7, + .use_clustering = ENABLE_CLUSTERING, + .shost_attrs = mpt2sas_host_attrs, + .sdev_attrs = mpt2sas_dev_attrs, +}; + +/** + * _scsih_expander_node_remove - removing expander device from list. + * @ioc: per adapter object + * @sas_expander: the sas_device object + * Context: Calling function should acquire ioc->sas_node_lock. + * + * Removing object and freeing associated memory from the + * ioc->sas_expander_list. + * + * Return nothing. + */ +static void +_scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc, + struct _sas_node *sas_expander) +{ + struct _sas_port *mpt2sas_port; + struct _sas_device *sas_device; + struct _sas_node *expander_sibling; + unsigned long flags; + + if (!sas_expander) + return; + + /* remove sibling ports attached to this expander */ + retry_device_search: + list_for_each_entry(mpt2sas_port, + &sas_expander->sas_port_list, port_list) { + if (mpt2sas_port->remote_identify.device_type == + SAS_END_DEVICE) { + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = + mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + mpt2sas_port->remote_identify.sas_address); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + if (!sas_device) + continue; + _scsih_remove_device(ioc, sas_device->handle); + goto retry_device_search; + } + } + + retry_expander_search: + list_for_each_entry(mpt2sas_port, + &sas_expander->sas_port_list, port_list) { + + if (mpt2sas_port->remote_identify.device_type == + MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER || + mpt2sas_port->remote_identify.device_type == + MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER) { + + spin_lock_irqsave(&ioc->sas_node_lock, flags); + expander_sibling = + mpt2sas_scsih_expander_find_by_sas_address( + ioc, mpt2sas_port->remote_identify.sas_address); + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); + if (!expander_sibling) + continue; + _scsih_expander_remove(ioc, expander_sibling->handle); + goto retry_expander_search; + } + } + + mpt2sas_transport_port_remove(ioc, sas_expander->sas_address, + sas_expander->parent_handle); + + printk(MPT2SAS_INFO_FMT "expander_remove: handle" + "(0x%04x), sas_addr(0x%016llx)\n", ioc->name, + sas_expander->handle, (unsigned long long) + sas_expander->sas_address); + + list_del(&sas_expander->list); + kfree(sas_expander->phy); + kfree(sas_expander); +} + +/** + * scsih_remove - detach and remove add host + * @pdev: PCI device struct + * + * Return nothing. + */ +static void __devexit +scsih_remove(struct pci_dev *pdev) +{ + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + struct _sas_port *mpt2sas_port; + struct _sas_device *sas_device; + struct _sas_node *expander_sibling; + struct workqueue_struct *wq; + unsigned long flags; + + ioc->remove_host = 1; + _scsih_fw_event_off(ioc); + + spin_lock_irqsave(&ioc->fw_event_lock, flags); + wq = ioc->firmware_event_thread; + ioc->firmware_event_thread = NULL; + spin_unlock_irqrestore(&ioc->fw_event_lock, flags); + if (wq) + destroy_workqueue(wq); + + /* free ports attached to the sas_host */ + retry_again: + list_for_each_entry(mpt2sas_port, + &ioc->sas_hba.sas_port_list, port_list) { + if (mpt2sas_port->remote_identify.device_type == + SAS_END_DEVICE) { + sas_device = + mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + mpt2sas_port->remote_identify.sas_address); + if (sas_device) { + _scsih_remove_device(ioc, sas_device->handle); + goto retry_again; + } + } else { + expander_sibling = + mpt2sas_scsih_expander_find_by_sas_address(ioc, + mpt2sas_port->remote_identify.sas_address); + if (expander_sibling) { + _scsih_expander_remove(ioc, + expander_sibling->handle); + goto retry_again; + } + } + } + + /* free phys attached to the sas_host */ + if (ioc->sas_hba.num_phys) { + kfree(ioc->sas_hba.phy); + ioc->sas_hba.phy = NULL; + ioc->sas_hba.num_phys = 0; + } + + sas_remove_host(shost); + mpt2sas_base_detach(ioc); + list_del(&ioc->list); + scsi_remove_host(shost); + scsi_host_put(shost); +} + +/** + * _scsih_probe_boot_devices - reports 1st device + * @ioc: per adapter object + * + * If specified in bios page 2, this routine reports the 1st + * device scsi-ml or sas transport for persistent boot device + * purposes. Please refer to function _scsih_determine_boot_device() + */ +static void +_scsih_probe_boot_devices(struct MPT2SAS_ADAPTER *ioc) +{ + u8 is_raid; + void *device; + struct _sas_device *sas_device; + struct _raid_device *raid_device; + u16 handle, parent_handle; + u64 sas_address; + unsigned long flags; + int rc; + + device = NULL; + if (ioc->req_boot_device.device) { + device = ioc->req_boot_device.device; + is_raid = ioc->req_boot_device.is_raid; + } else if (ioc->req_alt_boot_device.device) { + device = ioc->req_alt_boot_device.device; + is_raid = ioc->req_alt_boot_device.is_raid; + } else if (ioc->current_boot_device.device) { + device = ioc->current_boot_device.device; + is_raid = ioc->current_boot_device.is_raid; + } + + if (!device) + return; + + if (is_raid) { + raid_device = device; + rc = scsi_add_device(ioc->shost, RAID_CHANNEL, + raid_device->id, 0); + if (rc) + _scsih_raid_device_remove(ioc, raid_device); + } else { + sas_device = device; + handle = sas_device->handle; + parent_handle = sas_device->parent_handle; + sas_address = sas_device->sas_address; + spin_lock_irqsave(&ioc->sas_device_lock, flags); + list_move_tail(&sas_device->list, &ioc->sas_device_list); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + if (!mpt2sas_transport_port_add(ioc, sas_device->handle, + sas_device->parent_handle)) { + _scsih_sas_device_remove(ioc, sas_device); + } else if (!sas_device->starget) { + mpt2sas_transport_port_remove(ioc, sas_address, + parent_handle); + _scsih_sas_device_remove(ioc, sas_device); + } + } +} + +/** + * _scsih_probe_raid - reporting raid volumes to scsi-ml + * @ioc: per adapter object + * + * Called during initial loading of the driver. + */ +static void +_scsih_probe_raid(struct MPT2SAS_ADAPTER *ioc) +{ + struct _raid_device *raid_device, *raid_next; + int rc; + + list_for_each_entry_safe(raid_device, raid_next, + &ioc->raid_device_list, list) { + if (raid_device->starget) + continue; + rc = scsi_add_device(ioc->shost, RAID_CHANNEL, + raid_device->id, 0); + if (rc) + _scsih_raid_device_remove(ioc, raid_device); + } +} + +/** + * _scsih_probe_sas - reporting raid volumes to sas transport + * @ioc: per adapter object + * + * Called during initial loading of the driver. + */ +static void +_scsih_probe_sas(struct MPT2SAS_ADAPTER *ioc) +{ + struct _sas_device *sas_device, *next; + unsigned long flags; + u16 handle, parent_handle; + u64 sas_address; + + /* SAS Device List */ + list_for_each_entry_safe(sas_device, next, &ioc->sas_device_init_list, + list) { + spin_lock_irqsave(&ioc->sas_device_lock, flags); + list_move_tail(&sas_device->list, &ioc->sas_device_list); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + handle = sas_device->handle; + parent_handle = sas_device->parent_handle; + sas_address = sas_device->sas_address; + if (!mpt2sas_transport_port_add(ioc, handle, parent_handle)) { + _scsih_sas_device_remove(ioc, sas_device); + } else if (!sas_device->starget) { + mpt2sas_transport_port_remove(ioc, sas_address, + parent_handle); + _scsih_sas_device_remove(ioc, sas_device); + } + } +} + +/** + * _scsih_probe_devices - probing for devices + * @ioc: per adapter object + * + * Called during initial loading of the driver. + */ +static void +_scsih_probe_devices(struct MPT2SAS_ADAPTER *ioc) +{ + u16 volume_mapping_flags = + le16_to_cpu(ioc->ioc_pg8.IRVolumeMappingFlags) & + MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE; + + if (!(ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR)) + return; /* return when IOC doesn't support initiator mode */ + + _scsih_probe_boot_devices(ioc); + + if (ioc->ir_firmware) { + if ((volume_mapping_flags & + MPI2_IOCPAGE8_IRFLAGS_HIGH_VOLUME_MAPPING)) { + _scsih_probe_sas(ioc); + _scsih_probe_raid(ioc); + } else { + _scsih_probe_raid(ioc); + _scsih_probe_sas(ioc); + } + } else + _scsih_probe_sas(ioc); +} + +/** + * scsih_probe - attach and add scsi host + * @pdev: PCI device struct + * @id: pci device id + * + * Returns 0 success, anything else error. + */ +static int +scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct MPT2SAS_ADAPTER *ioc; + struct Scsi_Host *shost; + + shost = scsi_host_alloc(&scsih_driver_template, + sizeof(struct MPT2SAS_ADAPTER)); + if (!shost) + return -ENODEV; + + /* init local params */ + ioc = shost_priv(shost); + memset(ioc, 0, sizeof(struct MPT2SAS_ADAPTER)); + INIT_LIST_HEAD(&ioc->list); + list_add_tail(&ioc->list, &ioc_list); + ioc->shost = shost; + ioc->id = mpt_ids++; + sprintf(ioc->name, "%s%d", MPT2SAS_DRIVER_NAME, ioc->id); + ioc->pdev = pdev; + ioc->scsi_io_cb_idx = scsi_io_cb_idx; + ioc->tm_cb_idx = tm_cb_idx; + ioc->ctl_cb_idx = ctl_cb_idx; + ioc->base_cb_idx = base_cb_idx; + ioc->transport_cb_idx = transport_cb_idx; + ioc->config_cb_idx = config_cb_idx; + ioc->logging_level = logging_level; + /* misc semaphores and spin locks */ + spin_lock_init(&ioc->ioc_reset_in_progress_lock); + spin_lock_init(&ioc->scsi_lookup_lock); + spin_lock_init(&ioc->sas_device_lock); + spin_lock_init(&ioc->sas_node_lock); + spin_lock_init(&ioc->fw_event_lock); + spin_lock_init(&ioc->raid_device_lock); + + INIT_LIST_HEAD(&ioc->sas_device_list); + INIT_LIST_HEAD(&ioc->sas_device_init_list); + INIT_LIST_HEAD(&ioc->sas_expander_list); + INIT_LIST_HEAD(&ioc->fw_event_list); + INIT_LIST_HEAD(&ioc->raid_device_list); + INIT_LIST_HEAD(&ioc->sas_hba.sas_port_list); + + /* init shost parameters */ + shost->max_cmd_len = 16; + shost->max_lun = max_lun; + shost->transportt = mpt2sas_transport_template; + shost->unique_id = ioc->id; + + if ((scsi_add_host(shost, &pdev->dev))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + list_del(&ioc->list); + goto out_add_shost_fail; + } + + /* event thread */ + snprintf(ioc->firmware_event_name, sizeof(ioc->firmware_event_name), + "fw_event%d", ioc->id); + ioc->firmware_event_thread = create_singlethread_workqueue( + ioc->firmware_event_name); + if (!ioc->firmware_event_thread) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + goto out_thread_fail; + } + + ioc->wait_for_port_enable_to_complete = 1; + if ((mpt2sas_base_attach(ioc))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + goto out_attach_fail; + } + + ioc->wait_for_port_enable_to_complete = 0; + _scsih_probe_devices(ioc); + return 0; + + out_attach_fail: + destroy_workqueue(ioc->firmware_event_thread); + out_thread_fail: + list_del(&ioc->list); + scsi_remove_host(shost); + out_add_shost_fail: + return -ENODEV; +} + +#ifdef CONFIG_PM +/** + * scsih_suspend - power management suspend main entry point + * @pdev: PCI device struct + * @state: PM state change to (usually PCI_D3) + * + * Returns 0 success, anything else error. + */ +static int +scsih_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + u32 device_state; + + flush_scheduled_work(); + scsi_block_requests(shost); + device_state = pci_choose_state(pdev, state); + printk(MPT2SAS_INFO_FMT "pdev=0x%p, slot=%s, entering " + "operating state [D%d]\n", ioc->name, pdev, + pci_name(pdev), device_state); + + mpt2sas_base_free_resources(ioc); + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, device_state); + return 0; +} + +/** + * scsih_resume - power management resume main entry point + * @pdev: PCI device struct + * + * Returns 0 success, anything else error. + */ +static int +scsih_resume(struct pci_dev *pdev) +{ + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + u32 device_state = pdev->current_state; + int r; + + printk(MPT2SAS_INFO_FMT "pdev=0x%p, slot=%s, previous " + "operating state [D%d]\n", ioc->name, pdev, + pci_name(pdev), device_state); + + pci_set_power_state(pdev, PCI_D0); + pci_enable_wake(pdev, PCI_D0, 0); + pci_restore_state(pdev); + ioc->pdev = pdev; + r = mpt2sas_base_map_resources(ioc); + if (r) + return r; + + mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, SOFT_RESET); + scsi_unblock_requests(shost); + return 0; +} +#endif /* CONFIG_PM */ + + +static struct pci_driver scsih_driver = { + .name = MPT2SAS_DRIVER_NAME, + .id_table = scsih_pci_table, + .probe = scsih_probe, + .remove = __devexit_p(scsih_remove), +#ifdef CONFIG_PM + .suspend = scsih_suspend, + .resume = scsih_resume, +#endif +}; + + +/** + * scsih_init - main entry point for this driver. + * + * Returns 0 success, anything else error. + */ +static int __init +scsih_init(void) +{ + int error; + + mpt_ids = 0; + printk(KERN_INFO "%s version %s loaded\n", MPT2SAS_DRIVER_NAME, + MPT2SAS_DRIVER_VERSION); + + mpt2sas_transport_template = + sas_attach_transport(&mpt2sas_transport_functions); + if (!mpt2sas_transport_template) + return -ENODEV; + + mpt2sas_base_initialize_callback_handler(); + + /* queuecommand callback hander */ + scsi_io_cb_idx = mpt2sas_base_register_callback_handler(scsih_io_done); + + /* task managment callback handler */ + tm_cb_idx = mpt2sas_base_register_callback_handler(scsih_tm_done); + + /* base internal commands callback handler */ + base_cb_idx = mpt2sas_base_register_callback_handler(mpt2sas_base_done); + + /* transport internal commands callback handler */ + transport_cb_idx = mpt2sas_base_register_callback_handler( + mpt2sas_transport_done); + + /* configuration page API internal commands callback handler */ + config_cb_idx = mpt2sas_base_register_callback_handler( + mpt2sas_config_done); + + /* ctl module callback handler */ + ctl_cb_idx = mpt2sas_base_register_callback_handler(mpt2sas_ctl_done); + + mpt2sas_ctl_init(); + + error = pci_register_driver(&scsih_driver); + if (error) + sas_release_transport(mpt2sas_transport_template); + + return error; +} + +/** + * scsih_exit - exit point for this driver (when it is a module). + * + * Returns 0 success, anything else error. + */ +static void __exit +scsih_exit(void) +{ + printk(KERN_INFO "mpt2sas version %s unloading\n", + MPT2SAS_DRIVER_VERSION); + + pci_unregister_driver(&scsih_driver); + + sas_release_transport(mpt2sas_transport_template); + mpt2sas_base_release_callback_handler(scsi_io_cb_idx); + mpt2sas_base_release_callback_handler(tm_cb_idx); + mpt2sas_base_release_callback_handler(base_cb_idx); + mpt2sas_base_release_callback_handler(transport_cb_idx); + mpt2sas_base_release_callback_handler(config_cb_idx); + mpt2sas_base_release_callback_handler(ctl_cb_idx); + + mpt2sas_ctl_exit(); +} + +module_init(scsih_init); +module_exit(scsih_exit); diff --git a/drivers/scsi/mpt2sas/mpt2sas_transport.c b/drivers/scsi/mpt2sas/mpt2sas_transport.c new file mode 100644 index 000000000000..e03dc0b1e1a0 --- /dev/null +++ b/drivers/scsi/mpt2sas/mpt2sas_transport.c @@ -0,0 +1,1211 @@ +/* + * SAS Transport Layer for MPT (Message Passing Technology) based controllers + * + * This code is based on drivers/scsi/mpt2sas/mpt2_transport.c + * Copyright (C) 2007-2008 LSI Corporation + * (mailto:DL-MPTFusionLinux@lsi.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. + * + * 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. + * + * NO WARRANTY + * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + * solely responsible for determining the appropriateness of using and + * distributing the Program and assumes all risks associated with its + * exercise of rights under this Agreement, including but not limited to + * the risks and costs of program errors, damage to or loss of data, + * programs or equipment, and unavailability or interruption of operations. + + * DISCLAIMER OF LIABILITY + * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/workqueue.h> +#include <linux/delay.h> +#include <linux/pci.h> + +#include <scsi/scsi.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_transport_sas.h> +#include <scsi/scsi_dbg.h> + +#include "mpt2sas_base.h" +/** + * _transport_sas_node_find_by_handle - sas node search + * @ioc: per adapter object + * @handle: expander or hba handle (assigned by firmware) + * Context: Calling function should acquire ioc->sas_node_lock. + * + * Search for either hba phys or expander device based on handle, then returns + * the sas_node object. + */ +static struct _sas_node * +_transport_sas_node_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + int i; + + for (i = 0; i < ioc->sas_hba.num_phys; i++) + if (ioc->sas_hba.phy[i].handle == handle) + return &ioc->sas_hba; + + return mpt2sas_scsih_expander_find_by_handle(ioc, handle); +} + +/** + * _transport_convert_phy_link_rate - + * @link_rate: link rate returned from mpt firmware + * + * Convert link_rate from mpi fusion into sas_transport form. + */ +static enum sas_linkrate +_transport_convert_phy_link_rate(u8 link_rate) +{ + enum sas_linkrate rc; + + switch (link_rate) { + case MPI2_SAS_NEG_LINK_RATE_1_5: + rc = SAS_LINK_RATE_1_5_GBPS; + break; + case MPI2_SAS_NEG_LINK_RATE_3_0: + rc = SAS_LINK_RATE_3_0_GBPS; + break; + case MPI2_SAS_NEG_LINK_RATE_6_0: + rc = SAS_LINK_RATE_6_0_GBPS; + break; + case MPI2_SAS_NEG_LINK_RATE_PHY_DISABLED: + rc = SAS_PHY_DISABLED; + break; + case MPI2_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED: + rc = SAS_LINK_RATE_FAILED; + break; + case MPI2_SAS_NEG_LINK_RATE_PORT_SELECTOR: + rc = SAS_SATA_PORT_SELECTOR; + break; + case MPI2_SAS_NEG_LINK_RATE_SMP_RESET_IN_PROGRESS: + rc = SAS_PHY_RESET_IN_PROGRESS; + break; + default: + case MPI2_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE: + case MPI2_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE: + rc = SAS_LINK_RATE_UNKNOWN; + break; + } + return rc; +} + +/** + * _transport_set_identify - set identify for phys and end devices + * @ioc: per adapter object + * @handle: device handle + * @identify: sas identify info + * + * Populates sas identify info. + * + * Returns 0 for success, non-zero for failure. + */ +static int +_transport_set_identify(struct MPT2SAS_ADAPTER *ioc, u16 handle, + struct sas_identify *identify) +{ + Mpi2SasDevicePage0_t sas_device_pg0; + Mpi2ConfigReply_t mpi_reply; + u32 device_info; + u32 ioc_status; + + if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, + MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return -1; + } + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + printk(MPT2SAS_ERR_FMT "handle(0x%04x), ioc_status(0x%04x)" + "\nfailure at %s:%d/%s()!\n", ioc->name, handle, ioc_status, + __FILE__, __LINE__, __func__); + return -1; + } + + memset(identify, 0, sizeof(identify)); + device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); + + /* sas_address */ + identify->sas_address = le64_to_cpu(sas_device_pg0.SASAddress); + + /* device_type */ + switch (device_info & MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) { + case MPI2_SAS_DEVICE_INFO_NO_DEVICE: + identify->device_type = SAS_PHY_UNUSED; + break; + case MPI2_SAS_DEVICE_INFO_END_DEVICE: + identify->device_type = SAS_END_DEVICE; + break; + case MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER: + identify->device_type = SAS_EDGE_EXPANDER_DEVICE; + break; + case MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER: + identify->device_type = SAS_FANOUT_EXPANDER_DEVICE; + break; + } + + /* initiator_port_protocols */ + if (device_info & MPI2_SAS_DEVICE_INFO_SSP_INITIATOR) + identify->initiator_port_protocols |= SAS_PROTOCOL_SSP; + if (device_info & MPI2_SAS_DEVICE_INFO_STP_INITIATOR) + identify->initiator_port_protocols |= SAS_PROTOCOL_STP; + if (device_info & MPI2_SAS_DEVICE_INFO_SMP_INITIATOR) + identify->initiator_port_protocols |= SAS_PROTOCOL_SMP; + if (device_info & MPI2_SAS_DEVICE_INFO_SATA_HOST) + identify->initiator_port_protocols |= SAS_PROTOCOL_SATA; + + /* target_port_protocols */ + if (device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) + identify->target_port_protocols |= SAS_PROTOCOL_SSP; + if (device_info & MPI2_SAS_DEVICE_INFO_STP_TARGET) + identify->target_port_protocols |= SAS_PROTOCOL_STP; + if (device_info & MPI2_SAS_DEVICE_INFO_SMP_TARGET) + identify->target_port_protocols |= SAS_PROTOCOL_SMP; + if (device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE) + identify->target_port_protocols |= SAS_PROTOCOL_SATA; + + return 0; +} + +/** + * mpt2sas_transport_done - internal transport layer callback handler. + * @ioc: per adapter object + * @smid: system request message index + * @VF_ID: virtual function id + * @reply: reply message frame(lower 32bit addr) + * + * Callback handler when sending internal generated transport cmds. + * The callback index passed is `ioc->transport_cb_idx` + * + * Return nothing. + */ +void +mpt2sas_transport_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, + u32 reply) +{ + MPI2DefaultReply_t *mpi_reply; + + mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); + if (ioc->transport_cmds.status == MPT2_CMD_NOT_USED) + return; + if (ioc->transport_cmds.smid != smid) + return; + ioc->transport_cmds.status |= MPT2_CMD_COMPLETE; + if (mpi_reply) { + memcpy(ioc->transport_cmds.reply, mpi_reply, + mpi_reply->MsgLength*4); + ioc->transport_cmds.status |= MPT2_CMD_REPLY_VALID; + } + ioc->transport_cmds.status &= ~MPT2_CMD_PENDING; + complete(&ioc->transport_cmds.done); +} + +/* report manufacture request structure */ +struct rep_manu_request{ + u8 smp_frame_type; + u8 function; + u8 reserved; + u8 request_length; +}; + +/* report manufacture reply structure */ +struct rep_manu_reply{ + u8 smp_frame_type; /* 0x41 */ + u8 function; /* 0x01 */ + u8 function_result; + u8 response_length; + u16 expander_change_count; + u8 reserved0[2]; + u8 sas_format:1; + u8 reserved1:7; + u8 reserved2[3]; + u8 vendor_id[SAS_EXPANDER_VENDOR_ID_LEN]; + u8 product_id[SAS_EXPANDER_PRODUCT_ID_LEN]; + u8 product_rev[SAS_EXPANDER_PRODUCT_REV_LEN]; + u8 component_vendor_id[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN]; + u16 component_id; + u8 component_revision_id; + u8 reserved3; + u8 vendor_specific[8]; +}; + +/** + * transport_expander_report_manufacture - obtain SMP report_manufacture + * @ioc: per adapter object + * @sas_address: expander sas address + * @edev: the sas_expander_device object + * + * Fills in the sas_expander_device object when SMP port is created. + * + * Returns 0 for success, non-zero for failure. + */ +static int +transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc, + u64 sas_address, struct sas_expander_device *edev) +{ + Mpi2SmpPassthroughRequest_t *mpi_request; + Mpi2SmpPassthroughReply_t *mpi_reply; + struct rep_manu_reply *manufacture_reply; + struct rep_manu_request *manufacture_request; + int rc; + u16 smid; + u32 ioc_state; + unsigned long timeleft; + void *psge; + u32 sgl_flags; + u8 issue_reset = 0; + unsigned long flags; + void *data_out = NULL; + dma_addr_t data_out_dma; + u32 sz; + u64 *sas_address_le; + u16 wait_state_count; + + spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); + if (ioc->ioc_reset_in_progress) { + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); + printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", + __func__, ioc->name); + return -EFAULT; + } + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); + + mutex_lock(&ioc->transport_cmds.mutex); + + if (ioc->transport_cmds.status != MPT2_CMD_NOT_USED) { + printk(MPT2SAS_ERR_FMT "%s: transport_cmds in use\n", + ioc->name, __func__); + rc = -EAGAIN; + goto out; + } + ioc->transport_cmds.status = MPT2_CMD_PENDING; + + wait_state_count = 0; + ioc_state = mpt2sas_base_get_iocstate(ioc, 1); + while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { + if (wait_state_count++ == 10) { + printk(MPT2SAS_ERR_FMT + "%s: failed due to ioc not operational\n", + ioc->name, __func__); + rc = -EFAULT; + goto out; + } + ssleep(1); + ioc_state = mpt2sas_base_get_iocstate(ioc, 1); + printk(MPT2SAS_INFO_FMT "%s: waiting for " + "operational state(count=%d)\n", ioc->name, + __func__, wait_state_count); + } + if (wait_state_count) + printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n", + ioc->name, __func__); + + smid = mpt2sas_base_get_smid(ioc, ioc->transport_cb_idx); + if (!smid) { + printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", + ioc->name, __func__); + rc = -EAGAIN; + goto out; + } + + rc = 0; + mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); + ioc->transport_cmds.smid = smid; + + sz = sizeof(struct rep_manu_request) + sizeof(struct rep_manu_reply); + data_out = pci_alloc_consistent(ioc->pdev, sz, &data_out_dma); + + if (!data_out) { + printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, + __LINE__, __func__); + rc = -ENOMEM; + mpt2sas_base_free_smid(ioc, smid); + goto out; + } + + manufacture_request = data_out; + manufacture_request->smp_frame_type = 0x40; + manufacture_request->function = 1; + manufacture_request->reserved = 0; + manufacture_request->request_length = 0; + + memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); + mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; + mpi_request->PhysicalPort = 0xFF; + sas_address_le = (u64 *)&mpi_request->SASAddress; + *sas_address_le = cpu_to_le64(sas_address); + mpi_request->RequestDataLength = sizeof(struct rep_manu_request); + psge = &mpi_request->SGL; + + /* WRITE sgel first */ + sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | + MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC); + sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; + ioc->base_add_sg_single(psge, sgl_flags | + sizeof(struct rep_manu_request), data_out_dma); + + /* incr sgel */ + psge += ioc->sge_size; + + /* READ sgel last */ + sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | + MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | + MPI2_SGE_FLAGS_END_OF_LIST); + sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; + ioc->base_add_sg_single(psge, sgl_flags | + sizeof(struct rep_manu_reply), data_out_dma + + sizeof(struct rep_manu_request)); + + dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT "report_manufacture - " + "send to sas_addr(0x%016llx)\n", ioc->name, + (unsigned long long)sas_address)); + mpt2sas_base_put_smid_default(ioc, smid, 0 /* VF_ID */); + timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, + 10*HZ); + + if (!(ioc->transport_cmds.status & MPT2_CMD_COMPLETE)) { + printk(MPT2SAS_ERR_FMT "%s: timeout\n", + ioc->name, __func__); + _debug_dump_mf(mpi_request, + sizeof(Mpi2SmpPassthroughRequest_t)/4); + if (!(ioc->transport_cmds.status & MPT2_CMD_RESET)) + issue_reset = 1; + goto issue_host_reset; + } + + dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT "report_manufacture - " + "complete\n", ioc->name)); + + if (ioc->transport_cmds.status & MPT2_CMD_REPLY_VALID) { + u8 *tmp; + + mpi_reply = ioc->transport_cmds.reply; + + dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT + "report_manufacture - reply data transfer size(%d)\n", + ioc->name, le16_to_cpu(mpi_reply->ResponseDataLength))); + + if (le16_to_cpu(mpi_reply->ResponseDataLength) != + sizeof(struct rep_manu_reply)) + goto out; + + manufacture_reply = data_out + sizeof(struct rep_manu_request); + strncpy(edev->vendor_id, manufacture_reply->vendor_id, + SAS_EXPANDER_VENDOR_ID_LEN); + strncpy(edev->product_id, manufacture_reply->product_id, + SAS_EXPANDER_PRODUCT_ID_LEN); + strncpy(edev->product_rev, manufacture_reply->product_rev, + SAS_EXPANDER_PRODUCT_REV_LEN); + edev->level = manufacture_reply->sas_format; + if (manufacture_reply->sas_format) { + strncpy(edev->component_vendor_id, + manufacture_reply->component_vendor_id, + SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN); + tmp = (u8 *)&manufacture_reply->component_id; + edev->component_id = tmp[0] << 8 | tmp[1]; + edev->component_revision_id = + manufacture_reply->component_revision_id; + } + } else + dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT + "report_manufacture - no reply\n", ioc->name)); + + issue_host_reset: + if (issue_reset) + mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + out: + ioc->transport_cmds.status = MPT2_CMD_NOT_USED; + if (data_out) + pci_free_consistent(ioc->pdev, sz, data_out, data_out_dma); + + mutex_unlock(&ioc->transport_cmds.mutex); + return rc; +} + +/** + * mpt2sas_transport_port_add - insert port to the list + * @ioc: per adapter object + * @handle: handle of attached device + * @parent_handle: parent handle(either hba or expander) + * Context: This function will acquire ioc->sas_node_lock. + * + * Adding new port object to the sas_node->sas_port_list. + * + * Returns mpt2sas_port. + */ +struct _sas_port * +mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle, + u16 parent_handle) +{ + struct _sas_phy *mpt2sas_phy, *next; + struct _sas_port *mpt2sas_port; + unsigned long flags; + struct _sas_node *sas_node; + struct sas_rphy *rphy; + int i; + struct sas_port *port; + + if (!parent_handle) + return NULL; + + mpt2sas_port = kzalloc(sizeof(struct _sas_port), + GFP_KERNEL); + if (!mpt2sas_port) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return NULL; + } + + INIT_LIST_HEAD(&mpt2sas_port->port_list); + INIT_LIST_HEAD(&mpt2sas_port->phy_list); + spin_lock_irqsave(&ioc->sas_node_lock, flags); + sas_node = _transport_sas_node_find_by_handle(ioc, parent_handle); + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); + + if (!sas_node) { + printk(MPT2SAS_ERR_FMT "%s: Could not find parent(0x%04x)!\n", + ioc->name, __func__, parent_handle); + goto out_fail; + } + + mpt2sas_port->handle = parent_handle; + mpt2sas_port->sas_address = sas_node->sas_address; + if ((_transport_set_identify(ioc, handle, + &mpt2sas_port->remote_identify))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + goto out_fail; + } + + if (mpt2sas_port->remote_identify.device_type == SAS_PHY_UNUSED) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + goto out_fail; + } + + for (i = 0; i < sas_node->num_phys; i++) { + if (sas_node->phy[i].remote_identify.sas_address != + mpt2sas_port->remote_identify.sas_address) + continue; + list_add_tail(&sas_node->phy[i].port_siblings, + &mpt2sas_port->phy_list); + mpt2sas_port->num_phys++; + } + + if (!mpt2sas_port->num_phys) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + goto out_fail; + } + + port = sas_port_alloc_num(sas_node->parent_dev); + if ((sas_port_add(port))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + goto out_fail; + } + + list_for_each_entry(mpt2sas_phy, &mpt2sas_port->phy_list, + port_siblings) { + if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) + dev_printk(KERN_INFO, &port->dev, "add: handle(0x%04x)" + ", sas_addr(0x%016llx), phy(%d)\n", handle, + (unsigned long long) + mpt2sas_port->remote_identify.sas_address, + mpt2sas_phy->phy_id); + sas_port_add_phy(port, mpt2sas_phy->phy); + } + + mpt2sas_port->port = port; + if (mpt2sas_port->remote_identify.device_type == SAS_END_DEVICE) + rphy = sas_end_device_alloc(port); + else + rphy = sas_expander_alloc(port, + mpt2sas_port->remote_identify.device_type); + + rphy->identify = mpt2sas_port->remote_identify; + if ((sas_rphy_add(rphy))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + } + if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) + dev_printk(KERN_INFO, &rphy->dev, "add: handle(0x%04x), " + "sas_addr(0x%016llx)\n", handle, + (unsigned long long) + mpt2sas_port->remote_identify.sas_address); + mpt2sas_port->rphy = rphy; + spin_lock_irqsave(&ioc->sas_node_lock, flags); + list_add_tail(&mpt2sas_port->port_list, &sas_node->sas_port_list); + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); + + /* fill in report manufacture */ + if (mpt2sas_port->remote_identify.device_type == + MPI2_SAS_DEVICE_INFO_EDGE_EXPANDER || + mpt2sas_port->remote_identify.device_type == + MPI2_SAS_DEVICE_INFO_FANOUT_EXPANDER) + transport_expander_report_manufacture(ioc, + mpt2sas_port->remote_identify.sas_address, + rphy_to_expander_device(rphy)); + + return mpt2sas_port; + + out_fail: + list_for_each_entry_safe(mpt2sas_phy, next, &mpt2sas_port->phy_list, + port_siblings) + list_del(&mpt2sas_phy->port_siblings); + kfree(mpt2sas_port); + return NULL; +} + +/** + * mpt2sas_transport_port_remove - remove port from the list + * @ioc: per adapter object + * @sas_address: sas address of attached device + * @parent_handle: handle to the upstream parent(either hba or expander) + * Context: This function will acquire ioc->sas_node_lock. + * + * Removing object and freeing associated memory from the + * ioc->sas_port_list. + * + * Return nothing. + */ +void +mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, + u16 parent_handle) +{ + int i; + unsigned long flags; + struct _sas_port *mpt2sas_port, *next; + struct _sas_node *sas_node; + u8 found = 0; + struct _sas_phy *mpt2sas_phy, *next_phy; + + spin_lock_irqsave(&ioc->sas_node_lock, flags); + sas_node = _transport_sas_node_find_by_handle(ioc, parent_handle); + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); + if (!sas_node) + return; + list_for_each_entry_safe(mpt2sas_port, next, &sas_node->sas_port_list, + port_list) { + if (mpt2sas_port->remote_identify.sas_address != sas_address) + continue; + found = 1; + list_del(&mpt2sas_port->port_list); + goto out; + } + out: + if (!found) + return; + + for (i = 0; i < sas_node->num_phys; i++) { + if (sas_node->phy[i].remote_identify.sas_address == sas_address) + memset(&sas_node->phy[i].remote_identify, 0 , + sizeof(struct sas_identify)); + } + + list_for_each_entry_safe(mpt2sas_phy, next_phy, + &mpt2sas_port->phy_list, port_siblings) { + if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) + dev_printk(KERN_INFO, &mpt2sas_port->port->dev, + "remove: parent_handle(0x%04x), " + "sas_addr(0x%016llx), phy(%d)\n", parent_handle, + (unsigned long long) + mpt2sas_port->remote_identify.sas_address, + mpt2sas_phy->phy_id); + sas_port_delete_phy(mpt2sas_port->port, mpt2sas_phy->phy); + list_del(&mpt2sas_phy->port_siblings); + } + sas_port_delete(mpt2sas_port->port); + kfree(mpt2sas_port); +} + +/** + * mpt2sas_transport_add_host_phy - report sas_host phy to transport + * @ioc: per adapter object + * @mpt2sas_phy: mpt2sas per phy object + * @phy_pg0: sas phy page 0 + * @parent_dev: parent device class object + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_transport_add_host_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy + *mpt2sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev) +{ + struct sas_phy *phy; + int phy_index = mpt2sas_phy->phy_id; + + + INIT_LIST_HEAD(&mpt2sas_phy->port_siblings); + phy = sas_phy_alloc(parent_dev, phy_index); + if (!phy) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return -1; + } + if ((_transport_set_identify(ioc, mpt2sas_phy->handle, + &mpt2sas_phy->identify))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return -1; + } + phy->identify = mpt2sas_phy->identify; + mpt2sas_phy->attached_handle = le16_to_cpu(phy_pg0.AttachedDevHandle); + if (mpt2sas_phy->attached_handle) + _transport_set_identify(ioc, mpt2sas_phy->attached_handle, + &mpt2sas_phy->remote_identify); + phy->identify.phy_identifier = mpt2sas_phy->phy_id; + phy->negotiated_linkrate = _transport_convert_phy_link_rate( + phy_pg0.NegotiatedLinkRate & MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL); + phy->minimum_linkrate_hw = _transport_convert_phy_link_rate( + phy_pg0.HwLinkRate & MPI2_SAS_HWRATE_MIN_RATE_MASK); + phy->maximum_linkrate_hw = _transport_convert_phy_link_rate( + phy_pg0.HwLinkRate >> 4); + phy->minimum_linkrate = _transport_convert_phy_link_rate( + phy_pg0.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK); + phy->maximum_linkrate = _transport_convert_phy_link_rate( + phy_pg0.ProgrammedLinkRate >> 4); + + if ((sas_phy_add(phy))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + sas_phy_free(phy); + return -1; + } + if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) + dev_printk(KERN_INFO, &phy->dev, + "add: handle(0x%04x), sas_addr(0x%016llx)\n" + "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n", + mpt2sas_phy->handle, (unsigned long long) + mpt2sas_phy->identify.sas_address, + mpt2sas_phy->attached_handle, + (unsigned long long) + mpt2sas_phy->remote_identify.sas_address); + mpt2sas_phy->phy = phy; + return 0; +} + + +/** + * mpt2sas_transport_add_expander_phy - report expander phy to transport + * @ioc: per adapter object + * @mpt2sas_phy: mpt2sas per phy object + * @expander_pg1: expander page 1 + * @parent_dev: parent device class object + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt2sas_transport_add_expander_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy + *mpt2sas_phy, Mpi2ExpanderPage1_t expander_pg1, struct device *parent_dev) +{ + struct sas_phy *phy; + int phy_index = mpt2sas_phy->phy_id; + + INIT_LIST_HEAD(&mpt2sas_phy->port_siblings); + phy = sas_phy_alloc(parent_dev, phy_index); + if (!phy) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return -1; + } + if ((_transport_set_identify(ioc, mpt2sas_phy->handle, + &mpt2sas_phy->identify))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return -1; + } + phy->identify = mpt2sas_phy->identify; + mpt2sas_phy->attached_handle = + le16_to_cpu(expander_pg1.AttachedDevHandle); + if (mpt2sas_phy->attached_handle) + _transport_set_identify(ioc, mpt2sas_phy->attached_handle, + &mpt2sas_phy->remote_identify); + phy->identify.phy_identifier = mpt2sas_phy->phy_id; + phy->negotiated_linkrate = _transport_convert_phy_link_rate( + expander_pg1.NegotiatedLinkRate & + MPI2_SAS_NEG_LINK_RATE_MASK_PHYSICAL); + phy->minimum_linkrate_hw = _transport_convert_phy_link_rate( + expander_pg1.HwLinkRate & MPI2_SAS_HWRATE_MIN_RATE_MASK); + phy->maximum_linkrate_hw = _transport_convert_phy_link_rate( + expander_pg1.HwLinkRate >> 4); + phy->minimum_linkrate = _transport_convert_phy_link_rate( + expander_pg1.ProgrammedLinkRate & MPI2_SAS_PRATE_MIN_RATE_MASK); + phy->maximum_linkrate = _transport_convert_phy_link_rate( + expander_pg1.ProgrammedLinkRate >> 4); + + if ((sas_phy_add(phy))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + sas_phy_free(phy); + return -1; + } + if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) + dev_printk(KERN_INFO, &phy->dev, + "add: handle(0x%04x), sas_addr(0x%016llx)\n" + "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n", + mpt2sas_phy->handle, (unsigned long long) + mpt2sas_phy->identify.sas_address, + mpt2sas_phy->attached_handle, + (unsigned long long) + mpt2sas_phy->remote_identify.sas_address); + mpt2sas_phy->phy = phy; + return 0; +} + +/** + * mpt2sas_transport_update_phy_link_change - refreshing phy link changes and attached devices + * @ioc: per adapter object + * @handle: handle to sas_host or expander + * @attached_handle: attached device handle + * @phy_numberv: phy number + * @link_rate: new link rate + * + * Returns nothing. + */ +void +mpt2sas_transport_update_phy_link_change(struct MPT2SAS_ADAPTER *ioc, + u16 handle, u16 attached_handle, u8 phy_number, u8 link_rate) +{ + unsigned long flags; + struct _sas_node *sas_node; + struct _sas_phy *mpt2sas_phy; + + spin_lock_irqsave(&ioc->sas_node_lock, flags); + sas_node = _transport_sas_node_find_by_handle(ioc, handle); + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); + if (!sas_node) + return; + + mpt2sas_phy = &sas_node->phy[phy_number]; + mpt2sas_phy->attached_handle = attached_handle; + if (attached_handle && (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)) + _transport_set_identify(ioc, mpt2sas_phy->attached_handle, + &mpt2sas_phy->remote_identify); + else + memset(&mpt2sas_phy->remote_identify, 0 , sizeof(struct + sas_identify)); + + if (mpt2sas_phy->phy) + mpt2sas_phy->phy->negotiated_linkrate = + _transport_convert_phy_link_rate(link_rate); + + if ((ioc->logging_level & MPT_DEBUG_TRANSPORT)) + dev_printk(KERN_INFO, &mpt2sas_phy->phy->dev, + "refresh: handle(0x%04x), sas_addr(0x%016llx),\n" + "\tlink_rate(0x%02x), phy(%d)\n" + "\tattached_handle(0x%04x), sas_addr(0x%016llx)\n", + handle, (unsigned long long) + mpt2sas_phy->identify.sas_address, link_rate, + phy_number, attached_handle, + (unsigned long long) + mpt2sas_phy->remote_identify.sas_address); +} + +static inline void * +phy_to_ioc(struct sas_phy *phy) +{ + struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); + return shost_priv(shost); +} + +static inline void * +rphy_to_ioc(struct sas_rphy *rphy) +{ + struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent); + return shost_priv(shost); +} + +/** + * transport_get_linkerrors - + * @phy: The sas phy object + * + * Only support sas_host direct attached phys. + * Returns 0 for success, non-zero for failure. + * + */ +static int +transport_get_linkerrors(struct sas_phy *phy) +{ + struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy); + struct _sas_phy *mpt2sas_phy; + Mpi2ConfigReply_t mpi_reply; + Mpi2SasPhyPage1_t phy_pg1; + int i; + + for (i = 0, mpt2sas_phy = NULL; i < ioc->sas_hba.num_phys && + !mpt2sas_phy; i++) { + if (ioc->sas_hba.phy[i].phy != phy) + continue; + mpt2sas_phy = &ioc->sas_hba.phy[i]; + } + + if (!mpt2sas_phy) /* this phy not on sas_host */ + return -EINVAL; + + if ((mpt2sas_config_get_phy_pg1(ioc, &mpi_reply, &phy_pg1, + mpt2sas_phy->phy_id))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return -ENXIO; + } + + if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) + printk(MPT2SAS_INFO_FMT "phy(%d), ioc_status" + "(0x%04x), loginfo(0x%08x)\n", ioc->name, + mpt2sas_phy->phy_id, + le16_to_cpu(mpi_reply.IOCStatus), + le32_to_cpu(mpi_reply.IOCLogInfo)); + + phy->invalid_dword_count = le32_to_cpu(phy_pg1.InvalidDwordCount); + phy->running_disparity_error_count = + le32_to_cpu(phy_pg1.RunningDisparityErrorCount); + phy->loss_of_dword_sync_count = + le32_to_cpu(phy_pg1.LossDwordSynchCount); + phy->phy_reset_problem_count = + le32_to_cpu(phy_pg1.PhyResetProblemCount); + return 0; +} + +/** + * transport_get_enclosure_identifier - + * @phy: The sas phy object + * + * Obtain the enclosure logical id for an expander. + * Returns 0 for success, non-zero for failure. + */ +static int +transport_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier) +{ + struct MPT2SAS_ADAPTER *ioc = rphy_to_ioc(rphy); + struct _sas_node *sas_expander; + unsigned long flags; + + spin_lock_irqsave(&ioc->sas_node_lock, flags); + sas_expander = mpt2sas_scsih_expander_find_by_sas_address(ioc, + rphy->identify.sas_address); + spin_unlock_irqrestore(&ioc->sas_node_lock, flags); + + if (!sas_expander) + return -ENXIO; + + *identifier = sas_expander->enclosure_logical_id; + return 0; +} + +/** + * transport_get_bay_identifier - + * @phy: The sas phy object + * + * Returns the slot id for a device that resides inside an enclosure. + */ +static int +transport_get_bay_identifier(struct sas_rphy *rphy) +{ + struct MPT2SAS_ADAPTER *ioc = rphy_to_ioc(rphy); + struct _sas_device *sas_device; + unsigned long flags; + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + rphy->identify.sas_address); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + if (!sas_device) + return -ENXIO; + + return sas_device->slot; +} + +/** + * transport_phy_reset - + * @phy: The sas phy object + * @hard_reset: + * + * Only support sas_host direct attached phys. + * Returns 0 for success, non-zero for failure. + */ +static int +transport_phy_reset(struct sas_phy *phy, int hard_reset) +{ + struct MPT2SAS_ADAPTER *ioc = phy_to_ioc(phy); + struct _sas_phy *mpt2sas_phy; + Mpi2SasIoUnitControlReply_t mpi_reply; + Mpi2SasIoUnitControlRequest_t mpi_request; + int i; + + for (i = 0, mpt2sas_phy = NULL; i < ioc->sas_hba.num_phys && + !mpt2sas_phy; i++) { + if (ioc->sas_hba.phy[i].phy != phy) + continue; + mpt2sas_phy = &ioc->sas_hba.phy[i]; + } + + if (!mpt2sas_phy) /* this phy not on sas_host */ + return -EINVAL; + + memset(&mpi_request, 0, sizeof(Mpi2SasIoUnitControlReply_t)); + mpi_request.Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; + mpi_request.Operation = hard_reset ? + MPI2_SAS_OP_PHY_HARD_RESET : MPI2_SAS_OP_PHY_LINK_RESET; + mpi_request.PhyNum = mpt2sas_phy->phy_id; + + if ((mpt2sas_base_sas_iounit_control(ioc, &mpi_reply, &mpi_request))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return -ENXIO; + } + + if (mpi_reply.IOCStatus || mpi_reply.IOCLogInfo) + printk(MPT2SAS_INFO_FMT "phy(%d), ioc_status" + "(0x%04x), loginfo(0x%08x)\n", ioc->name, + mpt2sas_phy->phy_id, + le16_to_cpu(mpi_reply.IOCStatus), + le32_to_cpu(mpi_reply.IOCLogInfo)); + + return 0; +} + +/** + * transport_smp_handler - transport portal for smp passthru + * @shost: shost object + * @rphy: sas transport rphy object + * @req: + * + * This used primarily for smp_utils. + * Example: + * smp_rep_general /sys/class/bsg/expander-5:0 + */ +static int +transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, + struct request *req) +{ + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + Mpi2SmpPassthroughRequest_t *mpi_request; + Mpi2SmpPassthroughReply_t *mpi_reply; + int rc; + u16 smid; + u32 ioc_state; + unsigned long timeleft; + void *psge; + u32 sgl_flags; + u8 issue_reset = 0; + unsigned long flags; + dma_addr_t dma_addr_in = 0; + dma_addr_t dma_addr_out = 0; + u16 wait_state_count; + struct request *rsp = req->next_rq; + + if (!rsp) { + printk(MPT2SAS_ERR_FMT "%s: the smp response space is " + "missing\n", ioc->name, __func__); + return -EINVAL; + } + + /* do we need to support multiple segments? */ + if (req->bio->bi_vcnt > 1 || rsp->bio->bi_vcnt > 1) { + printk(MPT2SAS_ERR_FMT "%s: multiple segments req %u %u, " + "rsp %u %u\n", ioc->name, __func__, req->bio->bi_vcnt, + req->data_len, rsp->bio->bi_vcnt, rsp->data_len); + return -EINVAL; + } + + spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); + if (ioc->ioc_reset_in_progress) { + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); + printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", + __func__, ioc->name); + return -EFAULT; + } + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); + + rc = mutex_lock_interruptible(&ioc->transport_cmds.mutex); + if (rc) + return rc; + + if (ioc->transport_cmds.status != MPT2_CMD_NOT_USED) { + printk(MPT2SAS_ERR_FMT "%s: transport_cmds in use\n", ioc->name, + __func__); + rc = -EAGAIN; + goto out; + } + ioc->transport_cmds.status = MPT2_CMD_PENDING; + + wait_state_count = 0; + ioc_state = mpt2sas_base_get_iocstate(ioc, 1); + while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { + if (wait_state_count++ == 10) { + printk(MPT2SAS_ERR_FMT + "%s: failed due to ioc not operational\n", + ioc->name, __func__); + rc = -EFAULT; + goto out; + } + ssleep(1); + ioc_state = mpt2sas_base_get_iocstate(ioc, 1); + printk(MPT2SAS_INFO_FMT "%s: waiting for " + "operational state(count=%d)\n", ioc->name, + __func__, wait_state_count); + } + if (wait_state_count) + printk(MPT2SAS_INFO_FMT "%s: ioc is operational\n", + ioc->name, __func__); + + smid = mpt2sas_base_get_smid(ioc, ioc->transport_cb_idx); + if (!smid) { + printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", + ioc->name, __func__); + rc = -EAGAIN; + goto out; + } + + rc = 0; + mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); + ioc->transport_cmds.smid = smid; + + memset(mpi_request, 0, sizeof(Mpi2SmpPassthroughRequest_t)); + mpi_request->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; + mpi_request->PhysicalPort = 0xFF; + *((u64 *)&mpi_request->SASAddress) = (rphy) ? + cpu_to_le64(rphy->identify.sas_address) : + cpu_to_le64(ioc->sas_hba.sas_address); + mpi_request->RequestDataLength = cpu_to_le16(req->data_len - 4); + psge = &mpi_request->SGL; + + /* WRITE sgel first */ + sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | + MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_HOST_TO_IOC); + sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; + dma_addr_out = pci_map_single(ioc->pdev, bio_data(req->bio), + req->data_len, PCI_DMA_BIDIRECTIONAL); + if (!dma_addr_out) { + mpt2sas_base_free_smid(ioc, le16_to_cpu(smid)); + goto unmap; + } + + ioc->base_add_sg_single(psge, sgl_flags | (req->data_len - 4), + dma_addr_out); + + /* incr sgel */ + psge += ioc->sge_size; + + /* READ sgel last */ + sgl_flags = (MPI2_SGE_FLAGS_SIMPLE_ELEMENT | + MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | + MPI2_SGE_FLAGS_END_OF_LIST); + sgl_flags = sgl_flags << MPI2_SGE_FLAGS_SHIFT; + dma_addr_in = pci_map_single(ioc->pdev, bio_data(rsp->bio), + rsp->data_len, PCI_DMA_BIDIRECTIONAL); + if (!dma_addr_in) { + mpt2sas_base_free_smid(ioc, le16_to_cpu(smid)); + goto unmap; + } + + ioc->base_add_sg_single(psge, sgl_flags | (rsp->data_len + 4), + dma_addr_in); + + dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s - " + "sending smp request\n", ioc->name, __func__)); + + mpt2sas_base_put_smid_default(ioc, smid, 0 /* VF_ID */); + timeleft = wait_for_completion_timeout(&ioc->transport_cmds.done, + 10*HZ); + + if (!(ioc->transport_cmds.status & MPT2_CMD_COMPLETE)) { + printk(MPT2SAS_ERR_FMT "%s : timeout\n", + __func__, ioc->name); + _debug_dump_mf(mpi_request, + sizeof(Mpi2SmpPassthroughRequest_t)/4); + if (!(ioc->transport_cmds.status & MPT2_CMD_RESET)) + issue_reset = 1; + goto issue_host_reset; + } + + dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s - " + "complete\n", ioc->name, __func__)); + + if (ioc->transport_cmds.status & MPT2_CMD_REPLY_VALID) { + + mpi_reply = ioc->transport_cmds.reply; + + dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT + "%s - reply data transfer size(%d)\n", + ioc->name, __func__, + le16_to_cpu(mpi_reply->ResponseDataLength))); + + memcpy(req->sense, mpi_reply, sizeof(*mpi_reply)); + req->sense_len = sizeof(*mpi_reply); + req->data_len = 0; + rsp->data_len -= mpi_reply->ResponseDataLength; + + } else { + dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT + "%s - no reply\n", ioc->name, __func__)); + rc = -ENXIO; + } + + issue_host_reset: + if (issue_reset) { + mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + rc = -ETIMEDOUT; + } + + unmap: + if (dma_addr_out) + pci_unmap_single(ioc->pdev, dma_addr_out, req->data_len, + PCI_DMA_BIDIRECTIONAL); + if (dma_addr_in) + pci_unmap_single(ioc->pdev, dma_addr_in, rsp->data_len, + PCI_DMA_BIDIRECTIONAL); + + out: + ioc->transport_cmds.status = MPT2_CMD_NOT_USED; + mutex_unlock(&ioc->transport_cmds.mutex); + return rc; +} + +struct sas_function_template mpt2sas_transport_functions = { + .get_linkerrors = transport_get_linkerrors, + .get_enclosure_identifier = transport_get_enclosure_identifier, + .get_bay_identifier = transport_get_bay_identifier, + .phy_reset = transport_phy_reset, + .smp_handler = transport_smp_handler, +}; + +struct scsi_transport_template *mpt2sas_transport_template; |