From david@gwynne.id.au Sat May 7 22:37:18 2011 Return-Path: Received: from animata.net ([130.102.64.64]) by mx.google.com with ESMTPS id q10sm2805506pbs.24.2011.05.07.05.37.14 (version=TLSv1/SSLv3 cipher=OTHER); Sat, 07 May 2011 05:37:17 -0700 (PDT) Date: Sat, 7 May 2011 22:37:10 +1000 From: David Gwynne To: Steve Gonczi Cc: Garett D'Amore Subject: Re: mpt driver inquiry + offer of help Message-ID: <20110507123710.GT23492@animata.net> References: <2078171320.307462.1304694294639.JavaMail.root@sz0069a.westchester.pa.mail.comcast.net> <1015683238.308546.1304695330499.JavaMail.root@sz0069a.westchester.pa.mail.comcast.net> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <1015683238.308546.1304695330499.JavaMail.root@sz0069a.westchester.pa.mail.comcast.net> User-Agent: Mutt/1.5.21 (2010-09-15) Status: RO Content-Length: 102476 Lines: 3707 On Fri, May 06, 2011 at 03:22:10PM +0000, Steve Gonczi wrote: > > Hi Dave, > I would like to get involved. cool :) > I do not see any trace of the work you have done in the Illumos gate > checkins. i think a branch or something was set up for me, but i didnt get round to committing to it. > Maybe it is part of another checking with a non-obvious name. > I did find some scsi resources, so I am slowly coming up to speed. you'd be surprised how little you need to know about actual scsi to do this. your best references are going to be the linux mpt driver and the writing device drivers book combined with something like the mpt_sas driver. anyway, im attaching a diff with my code in it. there's only a few days work in this, but it should be usable. diff -r 96016f1d9837 -r a0892fb45a66 usr/src/uts/common/io/scsi/adapters/mpi/mpi.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/scsi/adapters/mpi/mpi.c Sat Jul 31 18:01:44 2010 +1000 @@ -0,0 +1,3678 @@ +/* + * Copyright (c) 2010 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MPI_DEBUG + +#ifdef MPI_DEBUG +#define MPI_D_CCB (1<<0) +#define MPI_D_HBA (1<<1) +#define MPI_D_CAP (1<<2) +#define MPI_D_MISC (1<<3) +#define MPI_D_CMD (1<<4) +static int mpidebug = MPI_D_MISC | MPI_D_CMD; +#define DPRINTF(mask, ...) \ +do { \ + if ((mask) & mpidebug) \ + cmn_err(CE_NOTE, __VA_ARGS__); \ +} while (0) +#else +#define DPRINTF(m, v...) +#endif + +#pragma pack(1) + +/* + * System Interface Register Set + */ + +#define MPI_DOORBELL 0x00 +/* doorbell read bits */ +#define MPI_DOORBELL_STATE (0xfUL<<28) /* ioc state */ +#define MPI_DOORBELL_STATE_RESET (0x0UL<<28) +#define MPI_DOORBELL_STATE_READY (0x1UL<<28) +#define MPI_DOORBELL_STATE_OPER (0x2UL<<28) +#define MPI_DOORBELL_STATE_FAULT (0x4UL<<28) +#define MPI_DOORBELL_INUSE (0x1UL<<27) /* doorbell used */ +#define MPI_DOORBELL_WHOINIT (0x7UL<<24) /* last to reset ioc */ +#define MPI_DOORBELL_WHOINIT_NOONE (0x0UL<<24) /* not initialized */ +#define MPI_DOORBELL_WHOINIT_SYSBIOS (0x1UL<<24) /* system bios */ +#define MPI_DOORBELL_WHOINIT_ROMBIOS (0x2UL<<24) /* rom bios */ +#define MPI_DOORBELL_WHOINIT_PCIPEER (0x3UL<<24) /* pci peer */ +#define MPI_DOORBELL_WHOINIT_DRIVER (0x4UL<<24) /* host driver */ +#define MPI_DOORBELL_WHOINIT_MANUFACT (0x5UL<<24) /* manufacturing */ +#define MPI_DOORBELL_FAULT (0xffff) /* fault code */ +#define MPI_DOORBELL_FAULT_REQ_PCIPAR 0x8111 /* req msg pci parity err */ +#define MPI_DOORBELL_FAULT_REQ_PCIBUS 0x8112 /* req msg pci bus err */ +#define MPI_DOORBELL_FAULT_REP_PCIPAR 0x8113 /* reply msg pci parity err */ +#define MPI_DOORBELL_FAULT_REP_PCIBUS 0x8114 /* reply msg pci bus err */ +#define MPI_DOORBELL_FAULT_SND_PCIPAR 0x8115 /* data send pci parity err */ +#define MPI_DOORBELL_FAULT_SND_PCIBUS 0x8116 /* data send pci bus err */ +#define MPI_DOORBELL_FAULT_RCV_PCIPAR 0x8117 /* data recv pci parity err */ +#define MPI_DOORBELL_FAULT_RCV_PCIBUS 0x8118 /* data recv pci bus err */ +/* doorbell write bits */ +#define MPI_DOORBELL_FUNCTION_SHIFT 24 +#define MPI_DOORBELL_FUNCTION_MASK (0xffUL << MPI_DOORBELL_FUNCTION_SHIFT) +#define MPI_DOORBELL_FUNCTION(x) \ + (((x) << MPI_DOORBELL_FUNCTION_SHIFT) & MPI_DOORBELL_FUNCTION_MASK) +#define MPI_DOORBELL_DWORDS_SHIFT 16 +#define MPI_DOORBELL_DWORDS_MASK (0xffUL << MPI_DOORBELL_DWORDS_SHIFT) +#define MPI_DOORBELL_DWORDS(x) \ + (((x) << MPI_DOORBELL_DWORDS_SHIFT) & MPI_DOORBELL_DWORDS_MASK) +#define MPI_DOORBELL_DATA_MASK 0xffff + +#define MPI_WRITESEQ 0x04 +#define MPI_WRITESEQ_VALUE 0x0000000f /* key value */ +#define MPI_WRITESEQ_1 0x04 +#define MPI_WRITESEQ_2 0x0b +#define MPI_WRITESEQ_3 0x02 +#define MPI_WRITESEQ_4 0x07 +#define MPI_WRITESEQ_5 0x0d + +#define MPI_HOSTDIAG 0x08 +#define MPI_HOSTDIAG_CLEARFBS (1UL<<10) /* clear flash bad sig */ +#define MPI_HOSTDIAG_POICB (1UL<<9) /* prevent ioc boot */ +#define MPI_HOSTDIAG_DWRE (1UL<<7) /* diag reg write enabled */ +#define MPI_HOSTDIAG_FBS (1UL<<6) /* flash bad sig */ +#define MPI_HOSTDIAG_RESET_HIST (1UL<<5) /* reset history */ +#define MPI_HOSTDIAG_DIAGWR_EN (1UL<<4) /* diagnostic write enabled */ +#define MPI_HOSTDIAG_RESET_ADAPTER (1UL<<2) /* reset adapter */ +#define MPI_HOSTDIAG_DISABLE_ARM (1UL<<1) /* disable arm */ +#define MPI_HOSTDIAG_DIAGMEM_EN (1UL<<0) /* diag mem enable */ + +#define MPI_TESTBASE 0x0c + +#define MPI_DIAGRWDATA 0x10 + +#define MPI_DIAGRWADDR 0x18 + +#define MPI_INTR_STATUS 0x30 +#define MPI_INTR_STATUS_IOCDOORBELL (1UL<<31) /* ioc doorbell status */ +#define MPI_INTR_STATUS_REPLY (1UL<<3) /* reply message interrupt */ +#define MPI_INTR_STATUS_DOORBELL (1UL<<0) /* doorbell interrupt */ + +#define MPI_INTR_MASK 0x34 +#define MPI_INTR_MASK_REPLY (1UL<<3) /* reply message intr mask */ +#define MPI_INTR_MASK_DOORBELL (1UL<<0) /* doorbell interrupt mask */ + +#define MPI_REQ_QUEUE 0x40 + +#define MPI_REPLY_QUEUE 0x44 +#define MPI_REPLY_QUEUE_ADDRESS (1UL<<31) /* address reply */ +#define MPI_REPLY_QUEUE_ADDRESS_MASK 0x7fffffff +#define MPI_REPLY_QUEUE_TYPE_MASK (3UL<<29) +#define MPI_REPLY_QUEUE_TYPE_INIT (0UL<<29) /* scsi initiator reply */ +#define MPI_REPLY_QUEUE_TYPE_TARGET (1UL<<29) /* scsi target reply */ +#define MPI_REPLY_QUEUE_TYPE_LAN (2UL<<29) /* lan reply */ +#define MPI_REPLY_QUEUE_CONTEXT 0x1fffffff /* not address and type */ + +#define MPI_PRIREQ_QUEUE 0x48 + +/* + * Scatter Gather Lists + */ + +#define MPI_SGE_FL_LAST (0x1UL<<31) /* last element in segment */ +#define MPI_SGE_FL_EOB (0x1UL<<30) /* last element of buffer */ +#define MPI_SGE_FL_TYPE (0x3UL<<28) /* element type */ +#define MPI_SGE_FL_TYPE_SIMPLE (0x1UL<<28) /* simple element */ +#define MPI_SGE_FL_TYPE_CHAIN (0x3UL<<28) /* chain element */ +#define MPI_SGE_FL_TYPE_XACTCTX (0x0UL<<28) /* transaction context */ +#define MPI_SGE_FL_LOCAL (0x1UL<<27) /* local address */ +#define MPI_SGE_FL_DIR (0x1UL<<26) /* direction */ +#define MPI_SGE_FL_DIR_OUT (0x1UL<<26) +#define MPI_SGE_FL_DIR_IN (0x0UL<<26) +#define MPI_SGE_FL_SIZE (0x1UL<<25) /* address size */ +#define MPI_SGE_FL_SIZE_32 (0x0UL<<25) +#define MPI_SGE_FL_SIZE_64 (0x1UL<<25) +#define MPI_SGE_FL_EOL (0x1UL<<24) /* end of list */ +#define MPI_SGE_FLAGS_IOC_TO_HOST (0x00) +#define MPI_SGE_FLAGS_HOST_TO_IOC (0x04) + +struct mpi_sge { + uint32_t sg_hdr; + uint32_t sg_lo_addr; + uint32_t sg_hi_addr; +}; + +struct mpi_fw_tce { + uint8_t reserved1; + uint8_t context_size; + uint8_t details_length; + uint8_t flags; + + uint32_t reserved2; + + uint32_t image_offset; + + uint32_t image_size; +}; + +/* + * Messages + */ + +/* functions */ +#define MPI_FUNCTION_SCSI_IO_REQUEST (0x00) +#define MPI_FUNCTION_SCSI_TASK_MGMT (0x01) +#define MPI_FUNCTION_IOC_INIT (0x02) +#define MPI_FUNCTION_IOC_FACTS (0x03) +#define MPI_FUNCTION_CONFIG (0x04) +#define MPI_FUNCTION_PORT_FACTS (0x05) +#define MPI_FUNCTION_PORT_ENABLE (0x06) +#define MPI_FUNCTION_EVENT_NOTIFICATION (0x07) +#define MPI_FUNCTION_EVENT_ACK (0x08) +#define MPI_FUNCTION_FW_DOWNLOAD (0x09) +#define MPI_FUNCTION_TARGET_CMD_BUFFER_POST (0x0A) +#define MPI_FUNCTION_TARGET_ASSIST (0x0B) +#define MPI_FUNCTION_TARGET_STATUS_SEND (0x0C) +#define MPI_FUNCTION_TARGET_MODE_ABORT (0x0D) +#define MPI_FUNCTION_TARGET_FC_BUF_POST_LINK_SRVC (0x0E) /* obsolete */ +#define MPI_FUNCTION_TARGET_FC_RSP_LINK_SRVC (0x0F) /* obsolete */ +#define MPI_FUNCTION_TARGET_FC_EX_SEND_LINK_SRVC (0x10) /* obsolete */ +#define MPI_FUNCTION_TARGET_FC_ABORT (0x11) /* obsolete */ +#define MPI_FUNCTION_FC_LINK_SRVC_BUF_POST (0x0E) +#define MPI_FUNCTION_FC_LINK_SRVC_RSP (0x0F) +#define MPI_FUNCTION_FC_EX_LINK_SRVC_SEND (0x10) +#define MPI_FUNCTION_FC_ABORT (0x11) +#define MPI_FUNCTION_FW_UPLOAD (0x12) +#define MPI_FUNCTION_FC_COMMON_TRANSPORT_SEND (0x13) +#define MPI_FUNCTION_FC_PRIMITIVE_SEND (0x14) + +#define MPI_FUNCTION_RAID_ACTION (0x15) +#define MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH (0x16) + +#define MPI_FUNCTION_TOOLBOX (0x17) + +#define MPI_FUNCTION_SCSI_ENCLOSURE_PROCESSOR (0x18) + +#define MPI_FUNCTION_MAILBOX (0x19) + +#define MPI_FUNCTION_LAN_SEND (0x20) +#define MPI_FUNCTION_LAN_RECEIVE (0x21) +#define MPI_FUNCTION_LAN_RESET (0x22) + +#define MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET (0x40UL) +#define MPI_FUNCTION_IO_UNIT_RESET (0x41UL) +#define MPI_FUNCTION_HANDSHAKE (0x42UL) +#define MPI_FUNCTION_REPLY_FRAME_REMOVAL (0x43UL) + +/* reply flags */ +#define MPI_REP_FLAGS_CONT (1UL<<7) /* continuation reply */ + +#define MPI_REP_IOCSTATUS_AVAIL (1UL<<15) /* logging info available */ +#define MPI_REP_IOCSTATUS (0x7fff) /* status */ + +/* Common IOCStatus values for all replies */ +#define MPI_IOCSTATUS_SUCCESS (0x0000) +#define MPI_IOCSTATUS_INVALID_FUNCTION (0x0001) +#define MPI_IOCSTATUS_BUSY (0x0002) +#define MPI_IOCSTATUS_INVALID_SGL (0x0003) +#define MPI_IOCSTATUS_INTERNAL_ERROR (0x0004) +#define MPI_IOCSTATUS_RESERVED (0x0005) +#define MPI_IOCSTATUS_INSUFFICIENT_RESOURCES (0x0006) +#define MPI_IOCSTATUS_INVALID_FIELD (0x0007) +#define MPI_IOCSTATUS_INVALID_STATE (0x0008) +#define MPI_IOCSTATUS_OP_STATE_NOT_SUPPORTED (0x0009) +/* Config IOCStatus values */ +#define MPI_IOCSTATUS_CONFIG_INVALID_ACTION (0x0020) +#define MPI_IOCSTATUS_CONFIG_INVALID_TYPE (0x0021) +#define MPI_IOCSTATUS_CONFIG_INVALID_PAGE (0x0022) +#define MPI_IOCSTATUS_CONFIG_INVALID_DATA (0x0023) +#define MPI_IOCSTATUS_CONFIG_NO_DEFAULTS (0x0024) +#define MPI_IOCSTATUS_CONFIG_CANT_COMMIT (0x0025) +/* SCSIIO Reply (SPI & FCP) initiator values */ +#define MPI_IOCSTATUS_SCSI_RECOVERED_ERROR (0x0040) +#define MPI_IOCSTATUS_SCSI_INVALID_BUS (0x0041) +#define MPI_IOCSTATUS_SCSI_INVALID_TARGETID (0x0042) +#define MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE (0x0043) +#define MPI_IOCSTATUS_SCSI_DATA_OVERRUN (0x0044) +#define MPI_IOCSTATUS_SCSI_DATA_UNDERRUN (0x0045) +#define MPI_IOCSTATUS_SCSI_IO_DATA_ERROR (0x0046) +#define MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR (0x0047) +#define MPI_IOCSTATUS_SCSI_TASK_TERMINATED (0x0048) +#define MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH (0x0049) +#define MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED (0x004A) +#define MPI_IOCSTATUS_SCSI_IOC_TERMINATED (0x004B) +#define MPI_IOCSTATUS_SCSI_EXT_TERMINATED (0x004C) +/* For use by SCSI Initiator and SCSI Target end-to-end data protection */ +#define MPI_IOCSTATUS_EEDP_GUARD_ERROR (0x004D) +#define MPI_IOCSTATUS_EEDP_REF_TAG_ERROR (0x004E) +#define MPI_IOCSTATUS_EEDP_APP_TAG_ERROR (0x004F) +/* SCSI (SPI & FCP) target values */ +#define MPI_IOCSTATUS_TARGET_PRIORITY_IO (0x0060) +#define MPI_IOCSTATUS_TARGET_INVALID_PORT (0x0061) +#define MPI_IOCSTATUS_TARGET_INVALID_IOCINDEX (0x0062) /* obsolete */ +#define MPI_IOCSTATUS_TARGET_INVALID_IO_INDEX (0x0062) +#define MPI_IOCSTATUS_TARGET_ABORTED (0x0063) +#define MPI_IOCSTATUS_TARGET_NO_CONN_RETRYABLE (0x0064) +#define MPI_IOCSTATUS_TARGET_NO_CONNECTION (0x0065) +#define MPI_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH (0x006A) +#define MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT (0x006B) +#define MPI_IOCSTATUS_TARGET_DATA_OFFSET_ERROR (0x006D) +#define MPI_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA (0x006E) +#define MPI_IOCSTATUS_TARGET_IU_TOO_SHORT (0x006F) +/* Additional FCP target values */ +#define MPI_IOCSTATUS_TARGET_FC_ABORTED (0x0066) /* obsolete */ +#define MPI_IOCSTATUS_TARGET_FC_RX_ID_INVALID (0x0067) /* obsolete */ +#define MPI_IOCSTATUS_TARGET_FC_DID_INVALID (0x0068) /* obsolete */ +#define MPI_IOCSTATUS_TARGET_FC_NODE_LOGGED_OUT (0x0069) /* obsolete */ +/* Fibre Channel Direct Access values */ +#define MPI_IOCSTATUS_FC_ABORTED (0x0066) +#define MPI_IOCSTATUS_FC_RX_ID_INVALID (0x0067) +#define MPI_IOCSTATUS_FC_DID_INVALID (0x0068) +#define MPI_IOCSTATUS_FC_NODE_LOGGED_OUT (0x0069) +#define MPI_IOCSTATUS_FC_EXCHANGE_CANCELED (0x006C) +/* LAN values */ +#define MPI_IOCSTATUS_LAN_DEVICE_NOT_FOUND (0x0080) +#define MPI_IOCSTATUS_LAN_DEVICE_FAILURE (0x0081) +#define MPI_IOCSTATUS_LAN_TRANSMIT_ERROR (0x0082) +#define MPI_IOCSTATUS_LAN_TRANSMIT_ABORTED (0x0083) +#define MPI_IOCSTATUS_LAN_RECEIVE_ERROR (0x0084) +#define MPI_IOCSTATUS_LAN_RECEIVE_ABORTED (0x0085) +#define MPI_IOCSTATUS_LAN_PARTIAL_PACKET (0x0086) +#define MPI_IOCSTATUS_LAN_CANCELED (0x0087) +/* Serial Attached SCSI values */ +#define MPI_IOCSTATUS_SAS_SMP_REQUEST_FAILED (0x0090) +#define MPI_IOCSTATUS_SAS_SMP_DATA_OVERRUN (0x0091) +/* Inband values */ +#define MPI_IOCSTATUS_INBAND_ABORTED (0x0098) +#define MPI_IOCSTATUS_INBAND_NO_CONNECTION (0x0099) +/* Diagnostic Tools values */ +#define MPI_IOCSTATUS_DIAGNOSTIC_RELEASED (0x00A0) + +#define MPI_REP_IOCLOGINFO_TYPE (0xfUL<<28) /* logging info type */ +#define MPI_REP_IOCLOGINFO_TYPE_NONE (0x0UL<<28) +#define MPI_REP_IOCLOGINFO_TYPE_SCSI (0x1UL<<28) +#define MPI_REP_IOCLOGINFO_TYPE_FC (0x2UL<<28) +#define MPI_REP_IOCLOGINFO_TYPE_SAS (0x3UL<<28) +#define MPI_REP_IOCLOGINFO_TYPE_ISCSI (0x4UL<<28) +#define MPI_REP_IOCLOGINFO_DATA (0x0fffffff) /* logging info data */ + +/* event notification types */ +#define MPI_EVENT_NONE 0x00 +#define MPI_EVENT_LOG_DATA 0x01 +#define MPI_EVENT_STATE_CHANGE 0x02 +#define MPI_EVENT_UNIT_ATTENTION 0x03 +#define MPI_EVENT_IOC_BUS_RESET 0x04 +#define MPI_EVENT_EXT_BUS_RESET 0x05 +#define MPI_EVENT_RESCAN 0x06 +#define MPI_EVENT_LINK_STATUS_CHANGE 0x07 +#define MPI_EVENT_LOOP_STATE_CHANGE 0x08 +#define MPI_EVENT_LOGOUT 0x09 +#define MPI_EVENT_EVENT_CHANGE 0x0a +#define MPI_EVENT_INTEGRATED_RAID 0x0b +#define MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE 0x0c +#define MPI_EVENT_ON_BUS_TIMER_EXPIRED 0x0d +#define MPI_EVENT_QUEUE_FULL 0x0e +#define MPI_EVENT_SAS_DEVICE_STATUS_CHANGE 0x0f +#define MPI_EVENT_SAS_SES 0x10 +#define MPI_EVENT_PERSISTENT_TABLE_FULL 0x11 +#define MPI_EVENT_SAS_PHY_LINK_STATUS 0x12 +#define MPI_EVENT_SAS_DISCOVERY_ERROR 0x13 +#define MPI_EVENT_IR_RESYNC_UPDATE 0x14 +#define MPI_EVENT_IR2 0x15 +#define MPI_EVENT_SAS_DISCOVERY 0x16 +#define MPI_EVENT_LOG_ENTRY_ADDED 0x21 + +/* messages */ + +#define MPI_WHOINIT_NOONE 0x00 +#define MPI_WHOINIT_SYSTEM_BIOS 0x01 +#define MPI_WHOINIT_ROM_BIOS 0x02 +#define MPI_WHOINIT_PCI_PEER 0x03 +#define MPI_WHOINIT_HOST_DRIVER 0x04 +#define MPI_WHOINIT_MANUFACTURER 0x05 + +/* page address fields */ +#define MPI_PAGE_ADDRESS_FC_BTID (1UL<<24) /* Bus Target ID */ + +/* default messages */ + +struct mpi_msg_request { + uint8_t reserved1; + uint8_t reserved2; + uint8_t chain_offset; + uint8_t function; + + uint8_t reserved3; + uint8_t reserved4; + uint8_t reserved5; + uint8_t msg_flags; + + uint32_t msg_context; +}; + +struct mpi_msg_reply { + uint8_t reserved1; + uint8_t reserved2; + uint8_t msg_length; + uint8_t function; + + uint8_t reserved3; + uint8_t reserved4; + uint8_t reserved5; + uint8_t msg_flags; + + uint32_t msg_context; + + uint8_t reserved6; + uint8_t reserved7; + uint16_t ioc_status; + + uint32_t ioc_loginfo; +}; + +/* ioc init */ + +struct mpi_msg_iocinit_request { + uint8_t whoinit; + uint8_t reserved1; + uint8_t chain_offset; + uint8_t function; + + uint8_t flags; +#define MPI_IOCINIT_F_DISCARD_FW (1UL<<0) +#define MPI_IOCINIT_F_ENABLE_HOST_FIFO (1UL<<1) +#define MPI_IOCINIT_F_HOST_PG_BUF_PERSIST (1UL<<2) + uint8_t max_devices; + uint8_t max_buses; + uint8_t msg_flags; + + uint32_t msg_context; + + uint16_t reply_frame_size; + uint16_t reserved2; + + uint32_t host_mfa_hi_addr; + + uint32_t sense_buffer_hi_addr; + + uint32_t reply_fifo_host_signalling_addr; + + struct mpi_sge host_page_buffer_sge; + + uint8_t msg_version_min; + uint8_t msg_version_maj; + + uint8_t hdr_version_unit; + uint8_t hdr_version_dev; +}; + +struct mpi_msg_iocinit_reply { + uint8_t whoinit; + uint8_t reserved1; + uint8_t msg_length; + uint8_t function; + + uint8_t flags; + uint8_t max_devices; + uint8_t max_buses; + uint8_t msg_flags; + + uint32_t msg_context; + + uint16_t reserved2; + uint16_t ioc_status; + + uint32_t ioc_loginfo; +}; + + +/* ioc facts */ +struct mpi_msg_iocfacts_request { + uint8_t reserved1; + uint8_t reserved2; + uint8_t chain_offset; + uint8_t function; + + uint8_t reserved3; + uint8_t reserved4; + uint8_t reserved5; + uint8_t msg_flags; + + uint32_t msg_context; +}; + +struct mpi_msg_iocfacts_reply { + uint8_t msg_version_min; + uint8_t msg_version_maj; + uint8_t msg_length; + uint8_t function; + + uint8_t header_version_min; + uint8_t header_version_maj; + uint8_t ioc_number; + uint8_t msg_flags; + + uint32_t msg_context; + + uint16_t ioc_exceptions; +#define MPI_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL (1UL<<0) +#define MPI_IOCFACTS_EXCEPT_RAID_CONFIG_INVALID (1UL<<1) +#define MPI_IOCFACTS_EXCEPT_FW_CHECKSUM_FAIL (1UL<<2) +#define MPI_IOCFACTS_EXCEPT_PERSISTENT_TABLE_FULL (1UL<<3) + uint16_t ioc_status; + + uint32_t ioc_loginfo; + + uint8_t max_chain_depth; + uint8_t whoinit; + uint8_t block_size; + uint8_t flags; +#define MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT (1UL<<0) +#define MPI_IOCFACTS_FLAGS_REPLY_FIFO_HOST_SIGNAL (1UL<<1) +#define MPI_IOCFACTS_FLAGS_HOST_PAGE_BUFFER_PERSISTENT (1UL<<2) + + uint16_t reply_queue_depth; + uint16_t request_frame_size; + + uint16_t reserved1; + uint16_t product_id; /* product id */ + + uint32_t current_host_mfa_hi_addr; + + uint16_t global_credits; + uint8_t number_of_ports; + uint8_t event_state; + + uint32_t current_sense_buffer_hi_addr; + + uint16_t current_reply_frame_size; + uint8_t max_devices; + uint8_t max_buses; + + uint32_t fw_image_size; + + uint32_t ioc_capabilities; +#define MPI_IOCFACTS_CAPABILITY_HIGH_PRI_Q (1UL<<0) +#define MPI_IOCFACTS_CAPABILITY_REPLY_HOST_SIGNAL (1UL<<1) +#define MPI_IOCFACTS_CAPABILITY_QUEUE_FULL_HANDLING (1UL<<2) +#define MPI_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER (1UL<<3) +#define MPI_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER (1UL<<4) +#define MPI_IOCFACTS_CAPABILITY_EXTENDED_BUFFER (1UL<<5) +#define MPI_IOCFACTS_CAPABILITY_EEDP (1UL<<6) +#define MPI_IOCFACTS_CAPABILITY_BIDIRECTIONAL (1UL<<7) +#define MPI_IOCFACTS_CAPABILITY_MULTICAST (1UL<<8) +#define MPI_IOCFACTS_CAPABILITY_SCSIIO32 (1UL<<9) +#define MPI_IOCFACTS_CAPABILITY_NO_SCSIIO16 (1UL<<10) + + uint8_t fw_version_dev; + uint8_t fw_version_unit; + uint8_t fw_version_min; + uint8_t fw_version_maj; + + uint16_t hi_priority_queue_depth; + uint16_t reserved2; + + struct mpi_sge host_page_buffer_sge; + + uint32_t reply_fifo_host_signalling_addr; +}; + +struct mpi_msg_portfacts_request { + uint8_t reserved1; + uint8_t reserved2; + uint8_t chain_offset; + uint8_t function; + + uint8_t reserved3; + uint8_t reserved4; + uint8_t port_number; + uint8_t msg_flags; + + uint32_t msg_context; + +}; + +struct mpi_msg_portfacts_reply { + uint16_t reserved1; + uint8_t msg_length; + uint8_t function; + + uint16_t reserved2; + uint8_t port_number; + uint8_t msg_flags; + + uint32_t msg_context; + + uint16_t reserved3; + uint16_t ioc_status; + + uint32_t ioc_loginfo; + + uint8_t reserved4; + uint8_t port_type; +#define MPI_PORTFACTS_PORTTYPE_INACTIVE 0x00 +#define MPI_PORTFACTS_PORTTYPE_SCSI 0x01 +#define MPI_PORTFACTS_PORTTYPE_FC 0x10 +#define MPI_PORTFACTS_PORTTYPE_ISCSI 0x20 +#define MPI_PORTFACTS_PORTTYPE_SAS 0x30 + + uint16_t max_devices; + + uint16_t port_scsi_id; + uint16_t protocol_flags; +#define MPI_PORTFACTS_PROTOCOL_LOGBUSADDR (1UL<<0) +#define MPI_PORTFACTS_PROTOCOL_LAN (1UL<<1) +#define MPI_PORTFACTS_PROTOCOL_TARGET (1UL<<2) +#define MPI_PORTFACTS_PROTOCOL_INITIATOR (1UL<<3) + + uint16_t max_posted_cmd_buffers; + uint16_t max_persistent_ids; + + uint16_t max_lan_buckets; + uint16_t reserved5; + + uint32_t reserved6; +}; + +struct mpi_msg_portenable_request { + uint16_t reserved1; + uint8_t chain_offset; + uint8_t function; + + uint16_t reserved2; + uint8_t port_number; + uint8_t msg_flags; + + uint32_t msg_context; +}; + +struct mpi_msg_portenable_reply { + uint16_t reserved1; + uint8_t msg_length; + uint8_t function; + + uint16_t reserved2; + uint8_t port_number; + uint8_t msg_flags; + + uint32_t msg_context; + + uint16_t reserved3; + uint16_t ioc_status; + + uint32_t ioc_loginfo; +}; + +struct mpi_msg_event_request { + uint8_t event_switch; +#define MPI_EVENT_SWITCH_ON (0x01) +#define MPI_EVENT_SWITCH_OFF (0x00) + uint8_t reserved1; + uint8_t chain_offset; + uint8_t function; + + uint8_t reserved2[3]; + uint8_t msg_flags; + + uint32_t msg_context; +}; + +struct mpi_msg_event_reply { + uint16_t data_length; + uint8_t msg_length; + uint8_t function; + + uint16_t reserved1; + uint8_t ack_required; +#define MPI_EVENT_ACK_REQUIRED (0x01) + uint8_t msg_flags; +#define MPI_EVENT_FLAGS_REPLY_KEPT (1UL<<7) + + uint32_t msg_context; + + uint16_t reserved2; + uint16_t ioc_status; + + uint32_t ioc_loginfo; + + uint32_t event; + + uint32_t event_context; + + /* event data follows */ +}; + +struct mpi_evt_change { + uint8_t event_state; + uint8_t reserved[3]; +}; + +struct mpi_evt_link_status_change { + uint8_t state; +#define MPI_EVT_LINK_STATUS_CHANGE_OFFLINE 0x00 +#define MPI_EVT_LINK_STATUS_CHANGE_ACTIVE 0x01 + uint8_t _reserved1[3]; + + uint8_t _reserved2[1]; + uint8_t port; + uint8_t _reserved3[2]; +}; + +struct mpi_evt_loop_status_change { + uint8_t character4; + uint8_t character3; + uint8_t type; +#define MPI_EVT_LOOP_STATUS_CHANGE_TYPE_LIP 0x01 +#define MPI_EVT_LOOP_STATUS_CHANGE_TYPE_LPE 0x02 +#define MPI_EVT_LOOP_STATUS_CHANGE_TYPE_LPB 0x03 + uint8_t _reserved1[1]; + + uint8_t _reserved2[1]; + uint8_t port; + uint8_t _reserved3[2]; +}; + +struct mpi_evt_logout { + uint32_t n_portid; + + uint8_t alias_index; + uint8_t port; + uint8_t _reserved[2]; +}; + +struct mpi_evt_sas_phy { + uint8_t phy_num; + uint8_t link_rates; +#define MPI_EVT_SASPHY_LINK_CUR(x) (((x) & 0xf0) >> 4) +#define MPI_EVT_SASPHY_LINK_PREV(x) ((x) & 0x0f) +#define MPI_EVT_SASPHY_LINK_ENABLED 0x0 +#define MPI_EVT_SASPHY_LINK_DISABLED 0x1 +#define MPI_EVT_SASPHY_LINK_NEGFAIL 0x2 +#define MPI_EVT_SASPHY_LINK_SATAOOB 0x3 +#define MPI_EVT_SASPHY_LINK_1_5GBPS 0x8 +#define MPI_EVT_SASPHY_LINK_3_0GBPS 0x9 + uint16_t dev_handle; + + uint64_t sas_addr; +}; + +struct mpi_evt_sas_change { + uint8_t target; + uint8_t bus; + uint8_t reason; +#define MPI_EVT_SASCH_REASON_ADDED 0x03 +#define MPI_EVT_SASCH_REASON_NOT_RESPONDING 0x04 +#define MPI_EVT_SASCH_REASON_SMART_DATA 0x05 +#define MPI_EVT_SASCH_REASON_NO_PERSIST_ADDED 0x06 +#define MPI_EVT_SASCH_REASON_UNSUPPORTED 0x07 +#define MPI_EVT_SASCH_REASON_INTERNAL_RESET 0x08 + uint8_t reserved1; + + uint8_t asc; + uint8_t ascq; + uint16_t dev_handle; + + uint32_t device_info; +#define MPI_EVT_SASCH_INFO_ATAPI (1UL<<13) +#define MPI_EVT_SASCH_INFO_LSI (1UL<<12) +#define MPI_EVT_SASCH_INFO_DIRECT_ATTACHED (1UL<<11) +#define MPI_EVT_SASCH_INFO_SSP (1UL<<10) +#define MPI_EVT_SASCH_INFO_STP (1UL<<9) +#define MPI_EVT_SASCH_INFO_SMP (1UL<<8) +#define MPI_EVT_SASCH_INFO_SATA (1UL<<7) +#define MPI_EVT_SASCH_INFO_SSP_INITIATOR (1UL<<6) +#define MPI_EVT_SASCH_INFO_STP_INITIATOR (1UL<<5) +#define MPI_EVT_SASCH_INFO_SMP_INITIATOR (1UL<<4) +#define MPI_EVT_SASCH_INFO_SATA_HOST (1UL<<3) +#define MPI_EVT_SASCH_INFO_TYPE_MASK 0x7 +#define MPI_EVT_SASCH_INFO_TYPE_NONE 0x0 +#define MPI_EVT_SASCH_INFO_TYPE_END 0x1 +#define MPI_EVT_SASCH_INFO_TYPE_EDGE 0x2 +#define MPI_EVT_SASCH_INFO_TYPE_FANOUT 0x3 + + uint16_t parent_dev_handle; + uint8_t phy_num; + uint8_t reserved2; + + uint64_t sas_addr; +}; + +struct mpi_msg_eventack_request { + uint16_t reserved1; + uint8_t chain_offset; + uint8_t function; + + uint8_t reserved2[3]; + uint8_t msg_flags; + + uint32_t msg_context; + + uint32_t event; + + uint32_t event_context; +}; + +struct mpi_msg_eventack_reply { + uint16_t reserved1; + uint8_t msg_length; + uint8_t function; + + uint8_t reserved2[3]; + uint8_t msg_flags; + + uint32_t msg_context; + + uint16_t reserved3; + uint32_t ioc_status; + + uint32_t ioc_loginfo; +}; + +struct mpi_msg_fwupload_request { + uint8_t image_type; +#define MPI_FWUPLOAD_IMAGETYPE_IOC_FW (0x00) +#define MPI_FWUPLOAD_IMAGETYPE_NV_FW (0x01) +#define MPI_FWUPLOAD_IMAGETYPE_MPI_NV_FW (0x02) +#define MPI_FWUPLOAD_IMAGETYPE_NV_DATA (0x03) +#define MPI_FWUPLOAD_IMAGETYPE_BOOT (0x04) +#define MPI_FWUPLOAD_IMAGETYPE_NV_BACKUP (0x05) + uint8_t reserved1; + uint8_t chain_offset; + uint8_t function; + + uint8_t reserved2[3]; + uint8_t msg_flags; + + uint32_t msg_context; + + struct mpi_fw_tce tce; + + /* followed by an sgl */ +}; + +struct mpi_msg_fwupload_reply { + uint8_t image_type; + uint8_t reserved1; + uint8_t msg_length; + uint8_t function; + + uint8_t reserved2[3]; + uint8_t msg_flags; + + uint32_t msg_context; + + uint16_t reserved3; + uint16_t ioc_status; + + uint32_t ioc_loginfo; + + uint32_t actual_image_size; +}; + +struct mpi_msg_scsi_io { + uint8_t target_id; + uint8_t bus; + uint8_t chain_offset; + uint8_t function; + + uint8_t cdb_length; + uint8_t sense_buf_len; + uint8_t reserved1; + uint8_t msg_flags; +#define MPI_SCSIIO_EEDP 0xf0 +#define MPI_SCSIIO_CMD_DATA_DIR (1UL<<2) +#define MPI_SCSIIO_SENSE_BUF_LOC (1UL<<1) +#define MPI_SCSIIO_SENSE_BUF_ADDR_WIDTH (1UL<<0) +#define MPI_SCSIIO_SENSE_BUF_ADDR_WIDTH_32 (0UL<<0) +#define MPI_SCSIIO_SENSE_BUF_ADDR_WIDTH_64 (1UL<<0) + + uint32_t msg_context; + + uint8_t lun[8]; + + uint8_t reserved2; + uint8_t tagging; +#define MPI_SCSIIO_ATTR_SIMPLE_Q (0x0) +#define MPI_SCSIIO_ATTR_HEAD_OF_Q (0x1) +#define MPI_SCSIIO_ATTR_ORDERED_Q (0x2) +#define MPI_SCSIIO_ATTR_ACA_Q (0x4) +#define MPI_SCSIIO_ATTR_UNTAGGED (0x5) +#define MPI_SCSIIO_ATTR_NO_DISCONNECT (0x7) + uint8_t reserved3; + uint8_t direction; +#define MPI_SCSIIO_DIR_NONE (0x0) +#define MPI_SCSIIO_DIR_WRITE (0x1) +#define MPI_SCSIIO_DIR_READ (0x2) + +#define MPI_CDB_LEN 16 + uint8_t cdb[MPI_CDB_LEN]; + + uint32_t data_length; + + uint32_t sense_buf_low_addr; + + /* followed by an sgl */ +}; + +struct mpi_msg_scsi_io_error { + uint8_t target_id; + uint8_t bus; + uint8_t msg_length; + uint8_t function; + + uint8_t cdb_length; + uint8_t sense_buf_len; + uint8_t reserved1; + uint8_t msg_flags; + + uint32_t msg_context; + + uint8_t scsi_status; +#define MPI_SCSIIO_ERR_STATUS_SUCCESS +#define MPI_SCSIIO_ERR_STATUS_CHECK_COND +#define MPI_SCSIIO_ERR_STATUS_BUSY +#define MPI_SCSIIO_ERR_STATUS_INTERMEDIATE +#define MPI_SCSIIO_ERR_STATUS_INTERMEDIATE_CONDMET +#define MPI_SCSIIO_ERR_STATUS_RESERVATION_CONFLICT +#define MPI_SCSIIO_ERR_STATUS_CMD_TERM +#define MPI_SCSIIO_ERR_STATUS_TASK_SET_FULL +#define MPI_SCSIIO_ERR_STATUS_ACA_ACTIVE + uint8_t scsi_state; +#define MPI_SCSIIO_ERR_STATE_AUTOSENSE_VALID (1UL<<0) +#define MPI_SCSIIO_ERR_STATE_AUTOSENSE_FAILED (1UL<<2) +#define MPI_SCSIIO_ERR_STATE_NO_SCSI_STATUS (1UL<<3) +#define MPI_SCSIIO_ERR_STATE_TERMINATED (1UL<<4) +#define MPI_SCSIIO_ERR_STATE_RESPONSE_INFO_VALID (1UL<<5) +#define MPI_SCSIIO_ERR_STATE_QUEUE_TAG_REJECTED (1UL<<6) + uint16_t ioc_status; + + uint32_t ioc_loginfo; + + uint32_t transfer_count; + + uint32_t sense_count; + + uint32_t response_info; + + uint16_t tag; + uint16_t reserved2; +}; + +struct mpi_msg_scsi_task_request { + uint8_t target_id; + uint8_t bus; + uint8_t chain_offset; + uint8_t function; + + uint8_t reserved1; + uint8_t task_type; +#define MPI_MSG_SCSI_TASK_TYPE_ABORT_TASK (0x01) +#define MPI_MSG_SCSI_TASK_TYPE_ABRT_TASK_SET (0x02) +#define MPI_MSG_SCSI_TASK_TYPE_TARGET_RESET (0x03) +#define MPI_MSG_SCSI_TASK_TYPE_RESET_BUS (0x04) +#define MPI_MSG_SCSI_TASK_TYPE_LOGICAL_UNIT_RESET (0x05) + uint8_t reserved2; + uint8_t msg_flags; + + uint32_t msg_context; + + uint8_t lun[8]; + + uint32_t reserved3[7]; /* wtf? */ + + uint32_t target_msg_context; +}; + +struct mpi_msg_scsi_task_reply { + uint8_t target_id; + uint8_t bus; + uint8_t msg_length; + uint8_t function; + + uint8_t response_code; + uint8_t task_type; + uint8_t reserved1; + uint8_t msg_flags; + + uint32_t msg_context; + + uint16_t reserved2; + uint16_t ioc_status; + + uint32_t ioc_loginfo; + + uint32_t termination_count; +}; + +struct mpi_msg_raid_action_request { + uint8_t action; +#define MPI_MSG_RAID_ACTION_STATUS (0x00) +#define MPI_MSG_RAID_ACTION_INDICATOR_STRUCT (0x01) +#define MPI_MSG_RAID_ACTION_CREATE_VOLUME (0x02) +#define MPI_MSG_RAID_ACTION_DELETE_VOLUME (0x03) +#define MPI_MSG_RAID_ACTION_DISABLE_VOLUME (0x04) +#define MPI_MSG_RAID_ACTION_ENABLE_VOLUME (0x05) +#define MPI_MSG_RAID_ACTION_QUIESCE_PHYSIO (0x06) +#define MPI_MSG_RAID_ACTION_ENABLE_PHYSIO (0x07) +#define MPI_MSG_RAID_ACTION_CH_VOL_SETTINGS (0x08) +#define MPI_MSG_RAID_ACTION_PHYSDISK_OFFLINE (0x0a) +#define MPI_MSG_RAID_ACTION_PHYSDISK_ONLINE (0x0b) +#define MPI_MSG_RAID_ACTION_CH_PHYSDISK_SETTINGS (0x0c) +#define MPI_MSG_RAID_ACTION_CREATE_PHYSDISK (0x0d) +#define MPI_MSG_RAID_ACTION_DELETE_PHYSDISK (0x0e) +#define MPI_MSG_RAID_ACTION_PHYSDISK_FAIL (0x0f) +#define MPI_MSG_RAID_ACTION_ACTIVATE_VOLUME (0x11) +#define MPI_MSG_RAID_ACTION_DEACTIVATE_VOLUME (0x12) +#define MPI_MSG_RAID_ACTION_SET_RESYNC_RATE (0x13) +#define MPI_MSG_RAID_ACTION_SET_SCRUB_RATE (0x14) +#define MPI_MSG_RAID_ACTION_DEVICE_FW_UPDATE_MODE (0x15) +#define MPI_MSG_RAID_ACTION_SET_VOL_NAME (0x16) + uint8_t _reserved1; + uint8_t chain_offset; + uint8_t function; + + uint8_t vol_id; + uint8_t vol_bus; + uint8_t phys_disk_num; + uint8_t message_flags; + + uint32_t msg_context; + + uint32_t _reserved2; + + uint32_t data_word; + uint32_t data_sge; +}; + +struct mpi_msg_raid_action_reply { + uint8_t action; + uint8_t _reserved1; + uint8_t message_length; + uint8_t function; + + uint8_t vol_id; + uint8_t vol_bus; + uint8_t phys_disk_num; + uint8_t message_flags; + + uint32_t message_context; + + uint16_t action_status; +#define MPI_RAID_ACTION_STATUS_OK (0x0000) +#define MPI_RAID_ACTION_STATUS_INVALID (0x0001) +#define MPI_RAID_ACTION_STATUS_FAILURE (0x0002) +#define MPI_RAID_ACTION_STATUS_IN_PROGRESS (0x0004) + uint16_t ioc_status; + + uint32_t ioc_log_info; + + uint32_t volume_status; + + uint32_t action_data; +}; + +struct mpi_cfg_hdr { + uint8_t page_version; + uint8_t page_length; + uint8_t page_number; + uint8_t page_type; +#define MPI_CONFIG_REQ_PAGE_TYPE_ATTRIBUTE (0xf0) +#define MPI_CONFIG_REQ_PAGE_TYPE_MASK (0x0f) +#define MPI_CONFIG_REQ_PAGE_TYPE_IO_UNIT (0x00) +#define MPI_CONFIG_REQ_PAGE_TYPE_IOC (0x01) +#define MPI_CONFIG_REQ_PAGE_TYPE_BIOS (0x02) +#define MPI_CONFIG_REQ_PAGE_TYPE_SCSI_SPI_PORT (0x03) +#define MPI_CONFIG_REQ_PAGE_TYPE_SCSI_SPI_DEV (0x04) +#define MPI_CONFIG_REQ_PAGE_TYPE_FC_PORT (0x05) +#define MPI_CONFIG_REQ_PAGE_TYPE_FC_DEV (0x06) +#define MPI_CONFIG_REQ_PAGE_TYPE_LAN (0x07) +#define MPI_CONFIG_REQ_PAGE_TYPE_RAID_VOL (0x08) +#define MPI_CONFIG_REQ_PAGE_TYPE_MANUFACTURING (0x09) +#define MPI_CONFIG_REQ_PAGE_TYPE_RAID_PD (0x0A) +#define MPI_CONFIG_REQ_PAGE_TYPE_INBAND (0x0B) +#define MPI_CONFIG_REQ_PAGE_TYPE_EXTENDED (0x0F) +}; + +struct mpi_ecfg_hdr { + uint8_t page_version; + uint8_t reserved1; + uint8_t page_number; + uint8_t page_type; + + uint16_t ext_page_length; + uint8_t ext_page_type; + uint8_t reserved2; +}; + +struct mpi_msg_config_request { + uint8_t action; +#define MPI_CONFIG_REQ_ACTION_PAGE_HEADER (0x00) +#define MPI_CONFIG_REQ_ACTION_PAGE_READ_CURRENT (0x01) +#define MPI_CONFIG_REQ_ACTION_PAGE_WRITE_CURRENT (0x02) +#define MPI_CONFIG_REQ_ACTION_PAGE_DEFAULT (0x03) +#define MPI_CONFIG_REQ_ACTION_PAGE_WRITE_NVRAM (0x04) +#define MPI_CONFIG_REQ_ACTION_PAGE_READ_DEFAULT (0x05) +#define MPI_CONFIG_REQ_ACTION_PAGE_READ_NVRAM (0x06) + uint8_t reserved1; + uint8_t chain_offset; + uint8_t function; + + uint16_t ext_page_len; + uint8_t ext_page_type; +#define MPI_CONFIG_REQ_EXTPAGE_TYPE_SAS_IO_UNIT (0x10) +#define MPI_CONFIG_REQ_EXTPAGE_TYPE_SAS_EXPANDER (0x11) +#define MPI_CONFIG_REQ_EXTPAGE_TYPE_SAS_DEVICE (0x12) +#define MPI_CONFIG_REQ_EXTPAGE_TYPE_SAS_PHY (0x13) +#define MPI_CONFIG_REQ_EXTPAGE_TYPE_LOG (0x14) + uint8_t msg_flags; + + uint32_t msg_context; + + uint32_t reserved2[2]; + + struct mpi_cfg_hdr config_header; + + uint32_t page_address; +/* XXX lots of defns here */ + + struct mpi_sge page_buffer; +}; + +struct mpi_msg_config_reply { + uint8_t action; + uint8_t reserved1; + uint8_t msg_length; + uint8_t function; + + uint16_t ext_page_length; + uint8_t ext_page_type; + uint8_t msg_flags; + + uint32_t msg_context; + + uint16_t reserved2; + uint16_t ioc_status; + + uint32_t ioc_loginfo; + + struct mpi_cfg_hdr config_header; +}; + +struct mpi_cfg_spi_port_pg0 { + struct mpi_cfg_hdr config_header; + + uint8_t capabilities1; +#define MPI_CFG_SPI_PORT_0_CAPABILITIES_PACKETIZED (1UL<<0) +#define MPI_CFG_SPI_PORT_0_CAPABILITIES_DT (1UL<<1) +#define MPI_CFG_SPI_PORT_0_CAPABILITIES_QAS (1UL<<2) + uint8_t min_period; + uint8_t max_offset; + uint8_t capabilities2; +#define MPI_CFG_SPI_PORT_0_CAPABILITIES_IDP (1UL<<3) +#define MPI_CFG_SPI_PORT_0_CAPABILITIES_WIDTH (1UL<<5) +#define MPI_CFG_SPI_PORT_0_CAPABILITIES_WIDTH_NARROW (0UL<<5) +#define MPI_CFG_SPI_PORT_0_CAPABILITIES_WIDTH_WIDE (1UL<<5) +#define MPI_CFG_SPI_PORT_0_CAPABILITIES_AIP (1UL<<7) + + uint8_t signalling_type; +#define MPI_CFG_SPI_PORT_0_SIGNAL_HVD (0x1) +#define MPI_CFG_SPI_PORT_0_SIGNAL_SE (0x2) +#define MPI_CFG_SPI_PORT_0_SIGNAL_LVD (0x3) + uint16_t reserved; + uint8_t connected_id; +#define MPI_CFG_SPI_PORT_0_CONNECTEDID_BUSFREE (0xfe) +#define MPI_CFG_SPI_PORT_0_CONNECTEDID_UNKNOWN (0xff) +}; + +struct mpi_cfg_spi_port_pg1 { + struct mpi_cfg_hdr config_header; + + /* configuration */ + uint8_t port_scsi_id; + uint8_t reserved1; + uint16_t port_resp_ids; + + uint32_t on_bus_timer_value; + + uint8_t target_config; +#define MPI_CFG_SPI_PORT_1_TARGCFG_TARGET_ONLY (0x01) +#define MPI_CFG_SPI_PORT_1_TARGCFG_INIT_TARGET (0x02) + uint8_t reserved2; + uint16_t id_config; +}; + +struct mpi_cfg_spi_port_pg2 { + struct mpi_cfg_hdr config_header; + + uint32_t port_flags; +#define MPI_CFG_SPI_PORT_2_PORT_FLAGS_SCAN_HI2LOW (1UL<<0) +#define MPI_CFG_SPI_PORT_2_PORT_FLAGS_AVOID_RESET (1UL<<2) +#define MPI_CFG_SPI_PORT_2_PORT_FLAGS_ALT_CHS (1UL<<3) +#define MPI_CFG_SPI_PORT_2_PORT_FLAGS_TERM_DISABLED (1UL<<4) +#define MPI_CFG_SPI_PORT_2_PORT_FLAGS_DV_CTL (0x3UL<<5) +#define MPI_CFG_SPI_PORT_2_PORT_FLAGS_DV_HOST_BE (0x0UL<<5) +#define MPI_CFG_SPI_PORT_2_PORT_FLAGS_DV_HOST_B (0x1UL<<5) +#define MPI_CFG_SPI_PORT_2_PORT_FLAGS_DV_HOST_NONE (0x3UL<<5) + + uint32_t port_settings; +#define MPI_CFG_SPI_PORT_2_PORT_SET_HOST_ID (0x7UL<<0) +#define MPI_CFG_SPI_PORT_2_PORT_SET_INIT_HBA (0x3UL<<4) +#define MPI_CFG_SPI_PORT_2_PORT_SET_INIT_HBA_DISABLED (0x0UL<<4) +#define MPI_CFG_SPI_PORT_2_PORT_SET_INIT_HBA_BIOS (0x1UL<<4) +#define MPI_CFG_SPI_PORT_2_PORT_SET_INIT_HBA_OS (0x2UL<<4) +#define MPI_CFG_SPI_PORT_2_PORT_SET_INIT_HBA_BIOS_OS (0x3UL<<4) +#define MPI_CFG_SPI_PORT_2_PORT_SET_REMOVABLE (0x3UL<<6) +#define MPI_CFG_SPI_PORT_2_PORT_SET_SPINUP_DELAY (0xfUL<<8) +#define MPI_CFG_SPI_PORT_2_PORT_SET_SYNC (0x3UL<<12) +#define MPI_CFG_SPI_PORT_2_PORT_SET_NEG_SUPPORTED (0x0UL<<12) +#define MPI_CFG_SPI_PORT_2_PORT_SET_NEG_NONE (0x1UL<<12) +#define MPI_CFG_SPI_PORT_2_PORT_SET_NEG_ALL (0x3UL<<12) + + struct { + uint8_t timeout; + uint8_t sync_factor; + uint16_t device_flags; +#define MPI_CFG_SPI_PORT_2_DEV_FLAG_DISCONNECT_EN (1UL<<0) +#define MPI_CFG_SPI_PORT_2_DEV_FLAG_SCAN_ID_EN (1UL<<1) +#define MPI_CFG_SPI_PORT_2_DEV_FLAG_SCAN_LUN_EN (1UL<<2) +#define MPI_CFG_SPI_PORT_2_DEV_FLAG_TAQ_Q_EN (1UL<<3) +#define MPI_CFG_SPI_PORT_2_DEV_FLAG_WIDE_DIS (1UL<<4) +#define MPI_CFG_SPI_PORT_2_DEV_FLAG_BOOT_CHOICE (1UL<<5) + } device_settings[16]; +}; + +struct mpi_cfg_spi_dev_pg0 { + struct mpi_cfg_hdr config_header; + + uint8_t neg_params1; +#define MPI_CFG_SPI_DEV_0_NEGPARAMS_PACKETIZED (1UL<<0) +#define MPI_CFG_SPI_DEV_0_NEGPARAMS_DUALXFERS (1UL<<1) +#define MPI_CFG_SPI_DEV_0_NEGPARAMS_QAS (1UL<<2) +#define MPI_CFG_SPI_DEV_0_NEGPARAMS_HOLD_MCS (1UL<<3) +#define MPI_CFG_SPI_DEV_0_NEGPARAMS_WR_FLOW (1UL<<4) +#define MPI_CFG_SPI_DEV_0_NEGPARAMS_RD_STRM (1UL<<5) +#define MPI_CFG_SPI_DEV_0_NEGPARAMS_RTI (1UL<<6) +#define MPI_CFG_SPI_DEV_0_NEGPARAMS_PCOMP_EN (1UL<<7) + uint8_t neg_period; + uint8_t neg_offset; + uint8_t neg_params2; +#define MPI_CFG_SPI_DEV_0_NEGPARAMS_IDP_EN (1UL<<3) +#define MPI_CFG_SPI_DEV_0_NEGPARAMS_WIDTH (1UL<<5) +#define MPI_CFG_SPI_DEV_0_NEGPARAMS_WIDTH_NARROW (0UL<<5) +#define MPI_CFG_SPI_DEV_0_NEGPARAMS_WIDTH_WIDE (1UL<<5) +#define MPI_CFG_SPI_DEV_0_NEGPARAMS_AIP (1UL<<7) + + uint32_t information; +#define MPI_CFG_SPI_DEV_0_INFO_NEG_OCCURRED (1UL<<0) +#define MPI_CFG_SPI_DEV_0_INFO_SDTR_REJECTED (1UL<<1) +#define MPI_CFG_SPI_DEV_0_INFO_WDTR_REJECTED (1UL<<2) +#define MPI_CFG_SPI_DEV_0_INFO_PPR_REJECTED (1UL<<3) +}; + +struct mpi_cfg_spi_dev_pg1 { + struct mpi_cfg_hdr config_header; + + uint8_t req_params1; +#define MPI_CFG_SPI_DEV_1_REQPARAMS_PACKETIZED (1UL<<0) +#define MPI_CFG_SPI_DEV_1_REQPARAMS_DUALXFERS (1UL<<1) +#define MPI_CFG_SPI_DEV_1_REQPARAMS_QAS (1UL<<2) +#define MPI_CFG_SPI_DEV_1_REQPARAMS_HOLD_MCS (1UL<<3) +#define MPI_CFG_SPI_DEV_1_REQPARAMS_WR_FLOW (1UL<<4) +#define MPI_CFG_SPI_DEV_1_REQPARAMS_RD_STRM (1UL<<5) +#define MPI_CFG_SPI_DEV_1_REQPARAMS_RTI (1UL<<6) +#define MPI_CFG_SPI_DEV_1_REQPARAMS_PCOMP_EN (1UL<<7) + uint8_t req_period; + uint8_t req_offset; + uint8_t req_params2; +#define MPI_CFG_SPI_DEV_1_REQPARAMS_IDP_EN (1UL<<3) +#define MPI_CFG_SPI_DEV_1_REQPARAMS_WIDTH (1UL<<5) +#define MPI_CFG_SPI_DEV_1_REQPARAMS_WIDTH_NARROW (0UL<<5) +#define MPI_CFG_SPI_DEV_1_REQPARAMS_WIDTH_WIDE (1UL<<5) +#define MPI_CFG_SPI_DEV_1_REQPARAMS_AIP (1UL<<7) + + uint32_t reserved; + + uint32_t configuration; +#define MPI_CFG_SPI_DEV_1_CONF_WDTR_DISALLOWED (1UL<<1) +#define MPI_CFG_SPI_DEV_1_CONF_SDTR_DISALLOWED (1UL<<2) +#define MPI_CFG_SPI_DEV_1_CONF_EXTPARAMS (1UL<<3) +#define MPI_CFG_SPI_DEV_1_CONF_FORCE_PPR (1UL<<4) +}; + +struct mpi_cfg_spi_dev_pg2 { + struct mpi_cfg_hdr config_header; + + uint32_t domain_validation; +#define MPI_CFG_SPI_DEV_2_DV_ISI_ENABLED (1UL<<4) +#define MPI_CFG_SPI_DEV_2_DV_SECONDARY_DRV_EN (1UL<<5) +#define MPI_CFG_SPI_DEV_2_DV_SLEW_RATE_CTL (0x7UL<<7) +#define MPI_CFG_SPI_DEV_2_DV_PRIMARY_DRV_STRENGTH (0x7UL<<10) +#define MPI_CFG_SPI_DEV_2_DV_XCLKH_ST (1UL<<28) +#define MPI_CFG_SPI_DEV_2_DV_XCLKS_ST (1UL<<29) +#define MPI_CFG_SPI_DEV_2_DV_XCLKH_DT (1UL<<30) +#define MPI_CFG_SPI_DEV_2_DV_XCLKS_DT (1UL<<31) + + uint32_t parity_pipe_select; +#define MPI_CFG_SPI_DEV_2_PARITY_PIPE_SELECT (0x3) + + uint32_t data_pipe_select; +#define MPI_CFG_SPI_DEV_2_DATA_PIPE_SELECT(x) (0x3UL<<((x)*2)) + +}; + +struct mpi_cfg_spi_dev_pg3 { + struct mpi_cfg_hdr config_header; + + uint16_t msg_reject_count; + uint16_t phase_error_count; + + uint16_t parity_error_count; + uint16_t reserved; +}; + +struct mpi_cfg_manufacturing_pg0 { + struct mpi_cfg_hdr config_header; + + char chip_name[16]; + char chip_revision[8]; + char board_name[16]; + char board_assembly[16]; + char board_tracer_number[16]; +}; + +struct mpi_cfg_ioc_pg1 { + struct mpi_cfg_hdr config_header; + + uint32_t flags; +#define MPI_CFG_IOC_1_REPLY_COALESCING (1UL<<0) +#define MPI_CFG_IOC_1_CTX_REPLY_DISABLE (1UL<<4) + + uint32_t coalescing_timeout; + + uint8_t coalescing_depth; + uint8_t pci_slot_num; + uint8_t _reserved[2]; +}; + +struct mpi_cfg_ioc_pg2 { + struct mpi_cfg_hdr config_header; + + uint32_t capabilities; +#define MPI_CFG_IOC_2_CAPABILITIES_IS (1UL<<0) +#define MPI_CFG_IOC_2_CAPABILITIES_IME (1UL<<1) +#define MPI_CFG_IOC_2_CAPABILITIES_IM (1UL<<2) +#define MPI_CFG_IOC_2_CAPABILITIES_RAID ( \ + MPI_CFG_IOC_2_CAPABILITIES_IS | MPI_CFG_IOC_2_CAPABILITIES_IME | \ + MPI_CFG_IOC_2_CAPABILITIES_IM) +#define MPI_CFG_IOC_2_CAPABILITIES_SES (1UL<<29) +#define MPI_CFG_IOC_2_CAPABILITIES_SAFTE (1UL<<30) +#define MPI_CFG_IOC_2_CAPABILITIES_XCHANNEL (1UL<<31) + + uint8_t active_vols; + uint8_t max_vols; + uint8_t active_physdisks; + uint8_t max_physdisks; + + /* followed by a list of mpi_cfg_raid_vol structs */ +}; + +struct mpi_cfg_raid_vol { + uint8_t vol_id; + uint8_t vol_bus; + uint8_t vol_ioc; + uint8_t vol_page; + + uint8_t vol_type; +#define MPI_CFG_RAID_TYPE_RAID_IS (0x00) +#define MPI_CFG_RAID_TYPE_RAID_IME (0x01) +#define MPI_CFG_RAID_TYPE_RAID_IM (0x02) +#define MPI_CFG_RAID_TYPE_RAID_5 (0x03) +#define MPI_CFG_RAID_TYPE_RAID_6 (0x04) +#define MPI_CFG_RAID_TYPE_RAID_10 (0x05) +#define MPI_CFG_RAID_TYPE_RAID_50 (0x06) + uint8_t flags; +#define MPI_CFG_RAID_VOL_INACTIVE (1UL<<3) + uint16_t reserved; +}; + +struct mpi_cfg_ioc_pg3 { + struct mpi_cfg_hdr config_header; + + uint8_t no_phys_disks; + uint8_t reserved[3]; + + /* followed by a list of mpi_cfg_raid_physdisk structs */ +}; + +struct mpi_cfg_raid_physdisk { + uint8_t phys_disk_id; + uint8_t phys_disk_bus; + uint8_t phys_disk_ioc; + uint8_t phys_disk_num; +}; + +struct mpi_cfg_fc_port_pg0 { + struct mpi_cfg_hdr config_header; + + uint32_t flags; + + uint8_t mpi_port_nr; + uint8_t link_type; + uint8_t port_state; + uint8_t reserved1; + + uint32_t port_id; + + uint64_t wwnn; + + uint64_t wwpn; + + uint32_t supported_service_class; + + uint32_t supported_speeds; + + uint32_t current_speed; + + uint32_t max_frame_size; + + uint64_t fabric_wwnn; + + uint64_t fabric_wwpn; + + uint32_t discovered_port_count; + + uint32_t max_initiators; + + uint8_t max_aliases_supported; + uint8_t max_hard_aliases_supported; + uint8_t num_current_aliases; + uint8_t reserved2; +}; + +struct mpi_cfg_fc_port_pg1 { + struct mpi_cfg_hdr config_header; + + uint32_t flags; + + uint64_t noseepromwwnn; + + uint64_t noseepromwwpn; + + uint8_t hard_alpa; + uint8_t link_config; + uint8_t topology_config; + uint8_t alt_connector; + + uint8_t num_req_aliases; + uint8_t rr_tov; + uint8_t initiator_dev_to; + uint8_t initiator_lo_pend_to; +}; + +struct mpi_cfg_fc_device_pg0 { + struct mpi_cfg_hdr config_header; + + uint64_t wwnn; + + uint64_t wwpn; + + uint32_t port_id; + + uint8_t protocol; + uint8_t flags; + uint16_t bb_credit; + + uint16_t max_rx_frame_size; + uint8_t adisc_hard_alpa; + uint8_t port_nr; + + uint8_t fc_ph_low_version; + uint8_t fc_ph_high_version; + uint8_t current_target_id; + uint8_t current_bus; +}; + +struct mpi_raid_settings { + uint16_t volume_settings; +#define MPI_CFG_RAID_VOL_0_SETTINGS_WRITE_CACHE_EN (1UL<<0) +#define MPI_CFG_RAID_VOL_0_SETTINGS_OFFLINE_SMART_ERR (1UL<<1) +#define MPI_CFG_RAID_VOL_0_SETTINGS_OFFLINE_SMART (1UL<<2) +#define MPI_CFG_RAID_VOL_0_SETTINGS_AUTO_SWAP (1UL<<3) +#define MPI_CFG_RAID_VOL_0_SETTINGS_HI_PRI_RESYNC (1UL<<4) +#define MPI_CFG_RAID_VOL_0_SETTINGS_PROD_SUFFIX (1UL<<5) +#define MPI_CFG_RAID_VOL_0_SETTINGS_FAST_SCRUB (1UL<<6) /* obsolete */ +#define MPI_CFG_RAID_VOL_0_SETTINGS_DEFAULTS (1UL<<15) + uint8_t hot_spare_pool; + uint8_t reserved2; +}; + +struct mpi_cfg_raid_vol_pg0 { + struct mpi_cfg_hdr config_header; + + uint8_t volume_id; + uint8_t volume_bus; + uint8_t volume_ioc; + uint8_t volume_type; + + uint8_t volume_status; +#define MPI_CFG_RAID_VOL_0_STATUS_ENABLED (1UL<<0) +#define MPI_CFG_RAID_VOL_0_STATUS_QUIESCED (1UL<<1) +#define MPI_CFG_RAID_VOL_0_STATUS_RESYNCING (1UL<<2) +#define MPI_CFG_RAID_VOL_0_STATUS_ACTIVE (1UL<<3) +#define MPI_CFG_RAID_VOL_0_STATUS_BADBLOCK_FULL (1UL<<4) + uint8_t volume_state; +#define MPI_CFG_RAID_VOL_0_STATE_OPTIMAL (0x00) +#define MPI_CFG_RAID_VOL_0_STATE_DEGRADED (0x01) +#define MPI_CFG_RAID_VOL_0_STATE_FAILED (0x02) +#define MPI_CFG_RAID_VOL_0_STATE_MISSING (0x03) + uint16_t _reserved1; + + struct mpi_raid_settings settings; + + uint32_t max_lba; + + uint32_t _reserved2; + + uint32_t stripe_size; + + uint32_t _reserved3; + + uint32_t _reserved4; + + uint8_t num_phys_disks; + uint8_t data_scrub_rate; + uint8_t resync_rate; + uint8_t inactive_status; +#define MPI_CFG_RAID_VOL_0_INACTIVE_UNKNOWN (0x00) +#define MPI_CFG_RAID_VOL_0_INACTIVE_STALE_META (0x01) +#define MPI_CFG_RAID_VOL_0_INACTIVE_FOREIGN_VOL (0x02) +#define MPI_CFG_RAID_VOL_0_INACTIVE_NO_RESOURCES (0x03) +#define MPI_CFG_RAID_VOL_0_INACTIVE_CLONED_VOL (0x04) +#define MPI_CFG_RAID_VOL_0_INACTIVE_INSUF_META (0x05) + + /* followed by a list of mpi_cfg_raid_vol_pg0_physdisk structs */ +}; + +struct mpi_cfg_raid_vol_pg0_physdisk { + uint16_t reserved; + uint8_t phys_disk_map; + uint8_t phys_disk_num; +}; + +struct mpi_cfg_raid_vol_pg1 { + struct mpi_cfg_hdr config_header; + + uint8_t volume_id; + uint8_t volume_bus; + uint8_t volume_ioc; + uint8_t reserved1; + + uint8_t guid[24]; + + uint8_t name[32]; + + uint64_t wwid; + + uint32_t reserved2; + + uint32_t reserved3; +}; + +struct mpi_cfg_raid_physdisk_pg0 { + struct mpi_cfg_hdr config_header; + + uint8_t phys_disk_id; + uint8_t phys_disk_bus; + uint8_t phys_disk_ioc; + uint8_t phys_disk_num; + + uint8_t enc_id; + uint8_t enc_bus; + uint8_t hot_spare_pool; + uint8_t enc_type; +#define MPI_CFG_RAID_PHYDISK_0_ENCTYPE_NONE (0x0) +#define MPI_CFG_RAID_PHYDISK_0_ENCTYPE_SAFTE (0x1) +#define MPI_CFG_RAID_PHYDISK_0_ENCTYPE_SES (0x2) + + uint32_t reserved1; + + uint8_t ext_disk_id[8]; + + uint8_t disk_id[16]; + + uint8_t vendor_id[8]; + + uint8_t product_id[16]; + + uint8_t product_rev[4]; + + uint8_t info[32]; + + uint8_t phys_disk_status; +#define MPI_CFG_RAID_PHYDISK_0_STATUS_OUTOFSYNC (1UL<<0) +#define MPI_CFG_RAID_PHYDISK_0_STATUS_QUIESCED (1UL<<1) + uint8_t phys_disk_state; +#define MPI_CFG_RAID_PHYDISK_0_STATE_ONLINE (0x00) +#define MPI_CFG_RAID_PHYDISK_0_STATE_MISSING (0x01) +#define MPI_CFG_RAID_PHYDISK_0_STATE_INCOMPAT (0x02) +#define MPI_CFG_RAID_PHYDISK_0_STATE_FAILED (0x03) +#define MPI_CFG_RAID_PHYDISK_0_STATE_INIT (0x04) +#define MPI_CFG_RAID_PHYDISK_0_STATE_OFFLINE (0x05) +#define MPI_CFG_RAID_PHYDISK_0_STATE_HOSTFAIL (0x06) +#define MPI_CFG_RAID_PHYDISK_0_STATE_OTHER (0xff) + uint16_t reserved2; + + uint32_t max_lba; + + uint8_t error_cdb_byte; + uint8_t error_sense_key; + uint16_t reserved3; + + uint16_t error_count; + uint8_t error_asc; + uint8_t error_ascq; + + uint16_t smart_count; + uint8_t smart_asc; + uint8_t smart_ascq; +}; + +struct mpi_cfg_raid_physdisk_pg1 { + struct mpi_cfg_hdr config_header; + + uint8_t num_phys_disk_paths; + uint8_t phys_disk_num; + uint16_t reserved1; + + uint32_t reserved2; + + /* followed by mpi_cfg_raid_physdisk_path structs */ +}; + +struct mpi_cfg_raid_physdisk_path { + uint8_t phys_disk_id; + uint8_t phys_disk_bus; + uint16_t reserved1; + + uint64_t wwwid; + + uint64_t owner_wwid; + + uint8_t ownder_id; + uint8_t reserved2; + uint16_t flags; +#define MPI_CFG_RAID_PHYDISK_PATH_INVALID (1UL<<0) +#define MPI_CFG_RAID_PHYDISK_PATH_BROKEN (1UL<<1) +}; + +#define MPI_CFG_SAS_DEV_ADDR_NEXT (0UL<<28) +#define MPI_CFG_SAS_DEV_ADDR_BUS (1UL<<28) +#define MPI_CFG_SAS_DEV_ADDR_HANDLE (2UL<<28) + +struct mpi_cfg_sas_dev_pg0 { + struct mpi_ecfg_hdr config_header; + + uint16_t slot; + uint16_t enc_handle; + + uint64_t sas_addr; + + uint16_t parent_dev_handle; + uint8_t phy_num; + uint8_t access_status; + + uint16_t dev_handle; + uint8_t target; + uint8_t bus; + + uint32_t device_info; +#define MPI_CFG_SAS_DEV_0_DEVINFO_TYPE (0x7) +#define MPI_CFG_SAS_DEV_0_DEVINFO_TYPE_NONE (0x0) +#define MPI_CFG_SAS_DEV_0_DEVINFO_TYPE_END (0x1) +#define MPI_CFG_SAS_DEV_0_DEVINFO_TYPE_EDGE_EXPANDER (0x2) +#define MPI_CFG_SAS_DEV_0_DEVINFO_TYPE_FANOUT_EXPANDER (0x3) +#define MPI_CFG_SAS_DEV_0_DEVINFO_SATA_HOST (1UL<<3) +#define MPI_CFG_SAS_DEV_0_DEVINFO_SMP_INITIATOR (1UL<<4) +#define MPI_CFG_SAS_DEV_0_DEVINFO_STP_INITIATOR (1UL<<5) +#define MPI_CFG_SAS_DEV_0_DEVINFO_SSP_INITIATOR (1UL<<6) +#define MPI_CFG_SAS_DEV_0_DEVINFO_SATA_DEVICE (1UL<<7) +#define MPI_CFG_SAS_DEV_0_DEVINFO_SMP_TARGET (1UL<<8) +#define MPI_CFG_SAS_DEV_0_DEVINFO_STP_TARGET (1UL<<9) +#define MPI_CFG_SAS_DEV_0_DEVINFO_SSP_TARGET (1UL<<10) +#define MPI_CFG_SAS_DEV_0_DEVINFO_DIRECT_ATTACHED (1UL<<11) +#define MPI_CFG_SAS_DEV_0_DEVINFO_LSI_DEVICE (1UL<<12) +#define MPI_CFG_SAS_DEV_0_DEVINFO_ATAPI_DEVICE (1UL<<13) +#define MPI_CFG_SAS_DEV_0_DEVINFO_SEP_DEVICE (1UL<<14) + + uint16_t flags; +#define MPI_CFG_SAS_DEV_0_FLAGS_DEV_PRESENT (1UL<<0) +#define MPI_CFG_SAS_DEV_0_FLAGS_DEV_MAPPED (1UL<<1) +#define MPI_CFG_SAS_DEV_0_FLAGS_DEV_MAPPED_PERSISTENT (1UL<<2) +#define MPI_CFG_SAS_DEV_0_FLAGS_SATA_PORT_SELECTOR (1UL<<3) +#define MPI_CFG_SAS_DEV_0_FLAGS_SATA_FUA (1UL<<4) +#define MPI_CFG_SAS_DEV_0_FLAGS_SATA_NCQ (1UL<<5) +#define MPI_CFG_SAS_DEV_0_FLAGS_SATA_SMART (1UL<<6) +#define MPI_CFG_SAS_DEV_0_FLAGS_SATA_LBA48 (1UL<<7) +#define MPI_CFG_SAS_DEV_0_FLAGS_UNSUPPORTED (1UL<<8) +#define MPI_CFG_SAS_DEV_0_FLAGS_SATA_SETTINGS (1UL<<9) + uint8_t physical_port; + uint8_t reserved; +}; + +#pragma pack() + +char _depends_on[] = "misc/scsi"; + +static int mpi_attach(dev_info_t *, ddi_attach_cmd_t); +static int mpi_detach(dev_info_t *, ddi_detach_cmd_t); + +static struct dev_ops mpi_ops = { + DEVO_REV, /* devo_rev */ + 0, /* refcnt */ + nodev, /* info */ + nulldev, /* identify */ + nulldev, /* probe */ + mpi_attach, /* attach */ + mpi_detach, /* detach */ + nodev, /* reset */ + NULL, /* driver ops */ + NULL, /* bus ops */ + 0 /* power */ +}; + +static void *mpi_softc_p; + +static struct modldrv md = { + &mod_driverops, + "MPI Driver", + &mpi_ops +}; + +static struct modlinkage ml = { + MODREV_1, + &md, + NULL +}; + +static ddi_dma_attr_t mpi_cb_dma_attr = { + DMA_ATTR_V0, /* version of this structure */ + 0, /* lowest usable address */ + 0xffffffffull, /* highest usable address */ + 0x7fffffff, /* maximum DMAable byte count */ + 8, /* alignment */ + 1, /* burst sizes */ + 1, /* minimum transfer */ + 0xffffffffull, /* maximum transfer */ + 0xffffffffull, /* maximum segment length */ + 1, /* maximum number of segments */ + 1, /* granularity */ + 0 /* flags (reserved) */ +}; + +/* XXX 64bit? */ +static ddi_dma_attr_t mpi_io_dma_attr = { + DMA_ATTR_V0, /* version of this structure */ + 0, /* lowest usable address */ + 0xffffffffull, /* highest usable address */ + 0x7fffffff, /* maximum DMAable byte count */ + 1, /* alignment */ + 0x7, /* burst sizes */ + 1, /* minimum transfer */ + 0xffffffffull, /* maximum transfer */ + 0xffffffffull, /* maximum segment length */ + 0x0, /* maximum number of segments */ + 512, /* granularity */ + 0 /* flags (reserved) */ +}; + + +struct mpi_softc; + +struct mpi_rcb { + void *rcb_reply; + off_t rcb_offset; + uint32_t rcb_reply_dva; +}; + +struct mpi_ce { + SIMPLEQ_ENTRY(mpi_ce) ce_entry; + struct mpi_sge *ce_sgl; + ddi_acc_handle_t ce_acc_handle; + ddi_dma_handle_t ce_dma_handle; + ddi_dma_cookie_t ce_dma_cookie; +}; +SIMPLEQ_HEAD(mpi_ce_list, mpi_ce); + +struct mpi_ccb { + SIMPLEQ_ENTRY(mpi_ccb) ccb_entry; + void *ccb_cmd; + uint64_t ccb_cmd_dva; + struct mpi_ce_list ccb_chain; + + ddi_acc_handle_t ccb_acc_handle; + ddi_dma_handle_t ccb_dma_handle; + ddi_dma_cookie_t ccb_dma_cookie; + + struct mpi_rcb *ccb_rcb; + void (*ccb_done)(struct mpi_softc *, + struct mpi_ccb *); + void *ccb_cookie; +}; +SIMPLEQ_HEAD(mpi_ccb_list, mpi_ccb); + +#define MPI_CCB_EXTRALEN 32 /* mostly for sense data */ + +struct mpi_context { + union { + SLIST_ENTRY(mpi_context) entry; + struct mpi_ccb *ccb; + } link; + uint32_t id; +}; +SLIST_HEAD(mpi_ctx_list, mpi_context); + +struct mpi_pkt_data { + struct mpi_ccb *mpd_ccb; + struct scsi_pkt *mpd_pkt; + + ddi_dma_handle_t mpd_dma_handle; + ddi_dma_cookie_t mpd_cookies; + uint_t mpd_ncookies; + uint_t mpd_curcookie; + uint_t mpd_window; + uint_t mpd_datalen; + uint_t mpd_totallen; + + int mpd_dma_mapped; + int mpd_read; + int mpd_cdblen; + int mpd_senselen; + + struct { + uint64_t sg_addr; + size_t sg_len; + } *mpd_sgl; + u_int mpd_sgllen; +}; + +struct mpi_softc { + dev_info_t * sc_dev; + scsi_hba_tran_t * sc_tran; + + ddi_acc_handle_t sc_regs; + char * sc_reg_baseaddr; + ddi_iblock_cookie_t sc_iblock_cookie; + ddi_dma_attr_t sc_io_dma_attr; + + uint8_t sc_porttype; + size_t sc_maxcmdlen; + int sc_maxcmds; + int sc_maxchdepth; + int sc_first_sgl_len; + int sc_chain_len; + int sc_max_sgl_len; + + int sc_buswidth; + int sc_target; + int sc_ioc_number; + + int sc_repq; + + size_t sc_fw_len; + + struct mpi_rcb *sc_rcbs; + caddr_t sc_replies; + ddi_acc_handle_t sc_replies_acc_handle; + ddi_dma_handle_t sc_replies_dma_handle; + ddi_dma_cookie_t sc_replies_dma_cookie; + + struct mpi_context *sc_contexts; + kmem_cache_t *sc_ccb_cache; + kmem_cache_t *sc_ce_cache; + + kmutex_t sc_runq_mtx; + struct mpi_ccb_list sc_ccb_list; + struct mpi_ctx_list sc_ctx_list; +}; + +#define mpi_read(_s, _r) ddi_get32((_s)->sc_regs, \ + (void *)((_s)->sc_reg_baseaddr + (_r))) +#define mpi_write(_s, _r, _v) ddi_put32((_s)->sc_regs, \ + (void *)((_s)->sc_reg_baseaddr + (_r)), (_v)) + +static int mpi_wait_eq(struct mpi_softc *, off_t, uint32_t, uint32_t); +static int mpi_wait_ne(struct mpi_softc *, off_t, uint32_t, uint32_t); + +#define mpi_read_db(s) mpi_read((s), MPI_DOORBELL) +#define mpi_write_db(s, v) mpi_write((s), MPI_DOORBELL, (v)) +#define mpi_read_intr(s) mpi_read((s), MPI_INTR_STATUS) +#define mpi_write_intr(s, v) mpi_write((s), MPI_INTR_STATUS, (v)) +#define mpi_pop_reply(s) mpi_read((s), MPI_REPLY_QUEUE) +#define mpi_push_reply_db(s, v) mpi_write((s), MPI_REPLY_QUEUE, (v)) + +#define mpi_wait_db_int(s) mpi_wait_ne((s), MPI_INTR_STATUS, \ + MPI_INTR_STATUS_DOORBELL, 0) +#define mpi_wait_db_ack(s) mpi_wait_eq((s), MPI_INTR_STATUS, \ + MPI_INTR_STATUS_IOCDOORBELL, 0) + +#define mpi_push_reply(_s, _r) mpi_push_reply_db((_s), (_r)->rcb_reply_dva) + +#define MPI_REQUEST_SIZE 512 +#define MPI_REPLY_SIZE 80 +#define MPI_REPLYQ_DEPTH 128 + +/* + * this is the max number of sge's we can stuff in a request frame: + * sizeof (scsi_io) + sizeof (sense) + sizeof (sge) * 32 = MPI_REQUEST_SIZE + */ +#define MPI_MAX_SGL 36 + +#define htole32(_x) LE_32(_x) +#define letoh32(_x) LE_32(_x) +#define htole16(_x) LE_16(_x) +#define letoh16(_x) LE_16(_x) +#define dwordsof(s) (sizeof (s) / sizeof (uint32_t)) + +static int mpi_pci_config(dev_info_t *, uint_t *); + +static int mpi_init(struct mpi_softc *); +static int mpi_reset_soft(struct mpi_softc *sc); +static int mpi_reset_hard(struct mpi_softc *sc); + +static int mpi_handshake_send(struct mpi_softc *, void *, size_t); +static int mpi_handshake_recv_dword(struct mpi_softc *, uint32_t *); +static int mpi_handshake_recv(struct mpi_softc *, void *, size_t); + +static int mpi_iocfacts(struct mpi_softc *); +static int mpi_iocinit(struct mpi_softc *); +static int mpi_portfacts(struct mpi_softc *); +static int mpi_portenable(struct mpi_softc *); + +static int mpi_alloc_contexts(struct mpi_softc *); +static void mpi_free_contexts(struct mpi_softc *); +static int mpi_alloc_replies(struct mpi_softc *); +static void mpi_push_replies(struct mpi_softc *); +static void mpi_free_replies(struct mpi_softc *); + +static int mpi_init_caches(struct mpi_softc *); +static void mpi_destroy_caches(struct mpi_softc *); + +static int mpi_ccb_ctor(void *, void *, int); +static void mpi_ccb_dtor(void *, void *); +static struct mpi_ccb *mpi_ccb_get(struct mpi_softc *, int); +static void mpi_ccb_put(struct mpi_softc *, struct mpi_ccb *); +static int mpi_ce_ctor(void *, void *, int); +static void mpi_ce_dtor(void *, void *); + +static uint_t mpi_intr(caddr_t); +static void mpi_reply(struct mpi_softc *, uint32_t); +static void mpi_start(struct mpi_softc *, struct mpi_ccb *); +static void mpi_restart(struct mpi_softc *, struct mpi_context *); +static void mpi_post(struct mpi_softc *, struct mpi_ccb *, + struct mpi_context *); + +static void mpi_wait(struct mpi_softc *, struct mpi_ccb *); +static void mpi_wait_done(struct mpi_softc *, struct mpi_ccb *); + + +static int mpi_hba_attach(struct mpi_softc *); + +static int mpi_tran_tgt_init(dev_info_t *, dev_info_t *, + scsi_hba_tran_t *, struct scsi_device *); +static int mpi_tran_start(struct scsi_address *, struct scsi_pkt *); +static int mpi_tran_reset(struct scsi_address *, int); +static int mpi_tran_getcap(struct scsi_address *, char *, int); +static int mpi_tran_setcap(struct scsi_address *, char *, int, int); +static struct scsi_pkt *mpi_tran_init_pkt(struct scsi_address *, + struct scsi_pkt *, struct buf *, int, int, int, + int, int (*)(), caddr_t); +static void mpi_tran_destroy_pkt(struct scsi_address *, struct scsi_pkt *); +static void mpi_tran_dmafree(struct scsi_address *, struct scsi_pkt *); +static void mpi_tran_sync_pkt(struct scsi_address *, struct scsi_pkt *); + +static void mpi_load_sgl(struct mpi_softc *, struct mpi_pkt_data *, + struct mpi_sge *); + +static void mpi_tran_done(struct mpi_softc *, struct mpi_ccb *); +static int mpi_dma_map(struct mpi_softc *, struct mpi_pkt_data *, + struct buf *, int, int (*)(caddr_t)); + +int +_init(void) +{ + int error; + + error = ddi_soft_state_init(&mpi_softc_p, sizeof (struct mpi_softc), 1); + if (error != 0) { + cmn_err(CE_NOTE, "unable to init soft state"); + goto err; + } + + error = scsi_hba_init(&ml); + if (error != 0) { + cmn_err(CE_NOTE, "unable init hba state"); + goto state_fini; + } + + error = mod_install(&ml); + if (error != 0) { + cmn_err(CE_NOTE, "mod_install error"); + goto hba_fini; + } + + return (error); + +hba_fini: + scsi_hba_fini(&ml); +state_fini: + ddi_soft_state_fini(mpi_softc_p); +err: + return (error); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&ml, modinfop)); +} + +int +_fini(void) +{ + int error; + + error = mod_remove(&ml); + if (error) + return (error); + + scsi_hba_fini(&ml); + + ddi_soft_state_fini(&mpi_softc_p); + + return (error); +} + +static ddi_device_acc_attr_t mpi_acc_attr = { + DDI_DEVICE_ATTR_V0, + DDI_STRUCTURE_LE_ACC, + DDI_STRICTORDER_ACC +}; + +static int +mpi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + struct mpi_softc *sc; + int instance; + uint_t membar; + + switch (cmd) { + case DDI_ATTACH: + break; + case DDI_RESUME: + default: + goto err; + } + + instance = ddi_get_instance(dip); + + if (ddi_soft_state_zalloc(mpi_softc_p, instance) != DDI_SUCCESS) { + cmn_err(CE_NOTE, "unable to alloc softc"); + goto err; + } + + sc = ddi_get_soft_state(mpi_softc_p, instance); + sc->sc_dev = dip; + + /* fumble around in pci config space */ + if (mpi_pci_config(dip, &membar) != DDI_SUCCESS) { + /* error printed by mpi_pci_config */ + goto free_sc; + } + + if (ddi_regs_map_setup(dip, 1, (caddr_t *)&sc->sc_reg_baseaddr, 0, 0, + &mpi_acc_attr, &sc->sc_regs) != DDI_SUCCESS) { + cmn_err(CE_NOTE, "unable to map register space"); + goto free_sc; + } + + /* disable interrupts */ + mpi_write(sc, MPI_INTR_MASK, + MPI_INTR_MASK_REPLY | MPI_INTR_MASK_DOORBELL); + + /* hook up interrupt */ + if (ddi_intr_hilevel(dip, 0) != 0) { + cmn_err(CE_NOTE, "high level interrupt is not supported"); + goto free_sc; + } + + if (ddi_get_iblock_cookie(dip, 0, + &sc->sc_iblock_cookie) != DDI_SUCCESS) { + cmn_err(CE_NOTE, "unable to get iblock cookie"); + goto free_sc; + } + + mutex_init(&sc->sc_runq_mtx, NULL, MUTEX_DRIVER, sc->sc_iblock_cookie); + SLIST_INIT(&sc->sc_ctx_list); + SIMPLEQ_INIT(&sc->sc_ccb_list); + sc->sc_io_dma_attr = mpi_io_dma_attr; + + if (mpi_init(sc) != DDI_SUCCESS) { + cmn_err(CE_NOTE, "unable to initialise"); + goto free_sc; + } + + if (mpi_iocfacts(sc) != DDI_SUCCESS) { + /* error printed by mpi_iocfacts */ + goto free_sc; + } + + if (mpi_alloc_contexts(sc) != DDI_SUCCESS) { + cmn_err(CE_NOTE, "unable to allocate contexts"); + goto free_sc; + } + + if (mpi_alloc_replies(sc) != DDI_SUCCESS) { + /* error printed by mpi_alloc_replies */ + goto free_contexts; + } + + if (mpi_init_caches(sc) != DDI_SUCCESS) { + /* error printed by mpi_init_caches */ + goto free_replies; + } + + if (mpi_iocinit(sc) != DDI_SUCCESS) { + cmn_err(CE_NOTE, "unable to send iocinit"); + goto destroy_caches; + } + + /* spin until we're operational */ + if (mpi_wait_eq(sc, MPI_DOORBELL, MPI_DOORBELL_STATE, + MPI_DOORBELL_STATE_OPER) != 0) { + cmn_err(CE_NOTE, "state: 0x%08lx", + mpi_read_db(sc) & MPI_DOORBELL_STATE); + goto destroy_caches; + } + + mpi_push_replies(sc); + + if (ddi_add_intr(sc->sc_dev, 0, &sc->sc_iblock_cookie, NULL, + mpi_intr, (caddr_t)sc) != DDI_SUCCESS) { + cmn_err(CE_NOTE, "unable to establish interrupt"); + goto destroy_caches; + } + + /* enable interrupts */ + mpi_write(sc, MPI_INTR_MASK, MPI_INTR_MASK_DOORBELL); + + if (mpi_portfacts(sc) != DDI_SUCCESS) { + /* error printed by mpi_portfacts */ + goto del_intr; + } + + if (mpi_portenable(sc) != DDI_SUCCESS) { + /* error printed by mpi_portfacts */ + goto del_intr; + } + + if (mpi_hba_attach(sc) != DDI_SUCCESS) { + cmn_err(CE_NOTE, "unable to attach scsi"); + goto del_intr; + } + + cmn_err(CE_NOTE, "success!"); + + return (DDI_SUCCESS); + +del_intr: + mpi_write(sc, MPI_INTR_MASK, + MPI_INTR_MASK_REPLY | MPI_INTR_MASK_DOORBELL); + ddi_remove_intr(sc->sc_dev, 0, sc->sc_iblock_cookie); +destroy_caches: + mpi_destroy_caches(sc); +free_replies: + mpi_free_replies(sc); +free_contexts: + mpi_free_contexts(sc); +free_sc: + ddi_soft_state_free(mpi_softc_p, instance); +err: + return (DDI_FAILURE); +} + +static int +mpi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + struct mpi_softc *sc; + int instance; + + switch (cmd) { + case DDI_DETACH: + break; + case DDI_SUSPEND: + default: + return (DDI_FAILURE); + } + + instance = ddi_get_instance(dip); + sc = ddi_get_soft_state(mpi_softc_p, instance); + + if (scsi_hba_detach(sc->sc_dev) != DDI_SUCCESS) + return (DDI_FAILURE); + scsi_hba_tran_free(sc->sc_tran); + + /* disable interrupts */ + mpi_write(sc, MPI_INTR_MASK, + MPI_INTR_MASK_REPLY | MPI_INTR_MASK_DOORBELL); + + ddi_remove_intr(sc->sc_dev, 0, sc->sc_iblock_cookie); + + mpi_destroy_caches(sc); + + mpi_free_replies(sc); + + mpi_free_contexts(sc); + + ddi_regs_map_free(&sc->sc_regs); + + ddi_soft_state_free(mpi_softc_p, instance); + + return (DDI_SUCCESS); +} + +static int +mpi_pci_config(dev_info_t *dip, uint_t *membar) +{ + ddi_acc_handle_t pci_conf; + uint32_t base, rom; + off_t reg; + uint16_t command; + int rv = DDI_FAILURE; + + if (pci_config_setup(dip, &pci_conf) != DDI_SUCCESS) { + cmn_err(CE_NOTE, "unable to map pci config space"); + return (DDI_FAILURE); + } + + /* force the busmaster enable bit on */ + command = pci_config_get16(pci_conf, PCI_CONF_COMM); + if ((command & PCI_COMM_ME) == 0) { + command |= PCI_COMM_ME; + pci_config_put16(pci_conf, PCI_CONF_COMM, command); + + /* check if it is enabled */ + command = pci_config_get16(pci_conf, PCI_CONF_COMM); + if ((command & PCI_COMM_ME) == 0) { + cmn_err(CE_NOTE, "unable to enable bus mastering"); + goto teardown; + } + } + + /* disable the expansion rom */ + rom = pci_config_get32(pci_conf, PCI_CONF_ROM); + rom &= ~PCI_BASE_ROM_ENABLE; + pci_config_put32(pci_conf, PCI_CONF_ROM, rom); + + /* locate the right base address register */ + for (reg = PCI_CONF_BASE0; reg <= PCI_CONF_BASE5; reg += 4) { + base = pci_config_get32(pci_conf, reg); + if ((base & PCI_BASE_SPACE_M) == PCI_BASE_SPACE_MEM) { + *membar = ((reg - PCI_CONF_BASE0) / 4) + 1; + rv = DDI_SUCCESS; + break; + } + if ((base & PCI_BASE_TYPE_M) == PCI_BASE_TYPE_ALL) + reg++; + } + if (rv == DDI_FAILURE) + cmn_err(CE_NOTE, "unable to locate register base addres"); + +teardown: + pci_config_teardown(&pci_conf); + return (rv); +} + +static int +mpi_init(struct mpi_softc *sc) +{ + uint32_t db; + int i; + + /* spin until the IOC leaves the RESET state */ + if (mpi_wait_ne(sc, MPI_DOORBELL, MPI_DOORBELL_STATE, + MPI_DOORBELL_STATE_RESET) != 0) { + DPRINTF(MPI_D_MISC, "mpi_init timeout waiting to leave " + "reset state"); + return (DDI_FAILURE); + } + + /* check current ownership */ + db = mpi_read_db(sc); + if ((db & MPI_DOORBELL_WHOINIT) == MPI_DOORBELL_WHOINIT_PCIPEER) { + DPRINTF(MPI_D_MISC, "mpi_init initialised by pci peer"); + return (DDI_SUCCESS); + } + + for (i = 0; i < 5; i++) { + switch (db & MPI_DOORBELL_STATE) { + case MPI_DOORBELL_STATE_READY: + DPRINTF(MPI_D_MISC, "mpi_init ioc is ready"); + return (DDI_SUCCESS); + + case MPI_DOORBELL_STATE_OPER: + case MPI_DOORBELL_STATE_FAULT: + DPRINTF(MPI_D_MISC, "mpi_init ioc is being reset"); + if (mpi_reset_soft(sc) != 0) + (void) mpi_reset_hard(sc); + break; + + case MPI_DOORBELL_STATE_RESET: + DPRINTF(MPI_D_MISC, "mpi_init waiting to come " + "out of reset"); + if (mpi_wait_ne(sc, MPI_DOORBELL, MPI_DOORBELL_STATE, + MPI_DOORBELL_STATE_RESET) != 0) + return (DDI_FAILURE); + break; + } + db = mpi_read_db(sc); + } + + return (DDI_FAILURE); +} + +static int +mpi_reset_soft(struct mpi_softc *sc) +{ + DPRINTF(MPI_D_MISC, "%s", __func__); + + if (mpi_read_db(sc) & MPI_DOORBELL_INUSE) + return (DDI_FAILURE); + + mpi_write_db(sc, + MPI_DOORBELL_FUNCTION(MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET)); + if (mpi_wait_eq(sc, MPI_INTR_STATUS, + MPI_INTR_STATUS_IOCDOORBELL, 0) != 0) + return (DDI_FAILURE); + + if (mpi_wait_eq(sc, MPI_DOORBELL, MPI_DOORBELL_STATE, + MPI_DOORBELL_STATE_READY) != 0) + return (DDI_FAILURE); + + return (DDI_SUCCESS); +} + +static int +mpi_reset_hard(struct mpi_softc *sc) +{ + DPRINTF(MPI_D_MISC, "%s", __func__); + + /* enable diagnostic register */ + mpi_write(sc, MPI_WRITESEQ, 0xff); + mpi_write(sc, MPI_WRITESEQ, MPI_WRITESEQ_1); + mpi_write(sc, MPI_WRITESEQ, MPI_WRITESEQ_2); + mpi_write(sc, MPI_WRITESEQ, MPI_WRITESEQ_3); + mpi_write(sc, MPI_WRITESEQ, MPI_WRITESEQ_4); + mpi_write(sc, MPI_WRITESEQ, MPI_WRITESEQ_5); + + /* reset ioc */ + mpi_write(sc, MPI_HOSTDIAG, MPI_HOSTDIAG_RESET_ADAPTER); + + delay(drv_usectohz(10000)); + + /* disable diagnostic register */ + mpi_write(sc, MPI_WRITESEQ, 0xff); + + /* restore pci bits? */ + + /* firmware bits? */ + return (DDI_SUCCESS); +} + +int +mpi_iocfacts(struct mpi_softc *sc) +{ + struct mpi_msg_iocfacts_request ifq; + struct mpi_msg_iocfacts_reply ifp; + + DPRINTF(MPI_D_MISC, "%s", __func__); + + bzero(&ifq, sizeof (ifq)); + bzero(&ifp, sizeof (ifp)); + + ifq.function = MPI_FUNCTION_IOC_FACTS; + ifq.chain_offset = 0; + ifq.msg_flags = 0; + ifq.msg_context = htole32(0xdeadbeef); + + if (mpi_handshake_send(sc, &ifq, dwordsof(ifq)) != DDI_SUCCESS) { + DPRINTF(MPI_D_MISC, "mpi_iocfacts send failed"); + return (DDI_FAILURE); + } + + if (mpi_handshake_recv(sc, &ifp, dwordsof(ifp)) != DDI_SUCCESS) { + DPRINTF(MPI_D_MISC, "mpi_iocfacts recv failed"); + return (DDI_FAILURE); + } + + DPRINTF(MPI_D_MISC, " func: 0x%02x len: %d msgver: %d.%d", + ifp.function, ifp.msg_length, + ifp.msg_version_maj, ifp.msg_version_min); + DPRINTF(MPI_D_MISC, " msgflags: 0x%02x iocnumber: 0x%02x " + "hdrver: %d.%d\n", ifp.msg_flags, ifp.ioc_number, + ifp.header_version_maj, ifp.header_version_min); + DPRINTF(MPI_D_MISC, " message context: 0x%08x", + letoh32(ifp.msg_context)); + DPRINTF(MPI_D_MISC, " iocstatus: 0x%04x ioexcept: 0x%04x", + letoh16(ifp.ioc_status), letoh16(ifp.ioc_exceptions)); + DPRINTF(MPI_D_MISC, " iocloginfo: 0x%08x", letoh32(ifp.ioc_loginfo)); + DPRINTF(MPI_D_MISC, " flags: 0x%02x blocksize: %d whoinit: 0x%02x " + "maxchdepth: %d", ifp.flags, ifp.block_size, ifp.whoinit, + ifp.max_chain_depth); + DPRINTF(MPI_D_MISC, " reqfrsize: %d replyqdepth: %d", + letoh16(ifp.request_frame_size), + letoh16(ifp.reply_queue_depth)); + DPRINTF(MPI_D_MISC, " productid: 0x%04x", letoh16(ifp.product_id)); + DPRINTF(MPI_D_MISC, " hostmfahiaddr: 0x%08x", + letoh32(ifp.current_host_mfa_hi_addr)); + DPRINTF(MPI_D_MISC, " event_state: 0x%02x number_of_ports: %d " + "global_credits: %d", ifp.event_state, ifp.number_of_ports, + letoh16(ifp.global_credits)); + DPRINTF(MPI_D_MISC, " sensebufhiaddr: 0x%08x", + letoh32(ifp.current_sense_buffer_hi_addr)); + DPRINTF(MPI_D_MISC, " maxbus: %d maxdev: %d replyfrsize: %d", + ifp.max_buses, ifp.max_devices, + letoh16(ifp.current_reply_frame_size)); + DPRINTF(MPI_D_MISC, " fw_image_size: %d", letoh32(ifp.fw_image_size)); + DPRINTF(MPI_D_MISC, " ioc_capabilities: 0x%08x", + letoh32(ifp.ioc_capabilities)); + DPRINTF(MPI_D_MISC, " fw_version: %d.%d fw_version_unit: 0x%02x " + "fw_version_dev: 0x%02x", + ifp.fw_version_maj, ifp.fw_version_min, + ifp.fw_version_unit, ifp.fw_version_dev); + DPRINTF(MPI_D_MISC, " hi_priority_queue_depth: 0x%04x", + letoh16(ifp.hi_priority_queue_depth)); + DPRINTF(MPI_D_MISC, " host_page_buffer_sge: hdr: 0x%08x " + "addr 0x%08x %08x", + letoh32(ifp.host_page_buffer_sge.sg_hdr), + letoh32(ifp.host_page_buffer_sge.sg_hi_addr), + letoh32(ifp.host_page_buffer_sge.sg_lo_addr)); + + sc->sc_maxcmdlen = letoh16(ifp.request_frame_size) * 4; + sc->sc_maxcmds = letoh16(ifp.global_credits); + sc->sc_maxchdepth = ifp.max_chain_depth; + sc->sc_ioc_number = ifp.ioc_number; + sc->sc_buswidth = (ifp.max_devices == 0) ? 256 : ifp.max_devices; + if (ifp.flags & MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT) + sc->sc_fw_len = letoh32(ifp.fw_image_size); + + sc->sc_repq = MIN(MPI_REPLYQ_DEPTH, letoh16(ifp.reply_queue_depth)); + + /* + * you can fit sg elements on the end of the io cmd if they fit in the + * request frame size. + */ + sc->sc_first_sgl_len = (sc->sc_maxcmdlen - + sizeof (struct mpi_msg_scsi_io)) / sizeof (struct mpi_sge); + DPRINTF(MPI_D_MISC, " first sgl len: %d\n", sc->sc_first_sgl_len); + sc->sc_io_dma_attr.dma_attr_sgllen = sc->sc_first_sgl_len; + + sc->sc_chain_len = sc->sc_maxcmdlen / sizeof (struct mpi_sge); + DPRINTF(MPI_D_MISC, " chain len: %d\n", sc->sc_chain_len); + + /* the sgl tailing the io cmd loses an entry to the chain element. */ + sc->sc_max_sgl_len = MPI_MAX_SGL - 1; + /* the sgl chains lose an entry for each chain element */ + sc->sc_max_sgl_len -= (MPI_MAX_SGL - sc->sc_first_sgl_len) / + sc->sc_chain_len; + DPRINTF(MPI_D_MISC, " max sgl len: %d\n", sc->sc_max_sgl_len); + + /* XXX we're ignoring the max chain depth */ + + return (DDI_SUCCESS); +} + +static int +mpi_alloc_contexts(struct mpi_softc *sc) +{ + struct mpi_context *ctx; + int i; + + sc->sc_contexts = kmem_alloc(sizeof (*ctx) * sc->sc_maxcmds, KM_SLEEP); + if (sc->sc_contexts == NULL) + return (DDI_FAILURE); + + for (i = 0; i < sc->sc_maxcmds; i++) { + ctx = &sc->sc_contexts[i]; + + ctx->id = i; + SLIST_INSERT_HEAD(&sc->sc_ctx_list, ctx, link.entry); + } + + return (DDI_SUCCESS); +} + +static void +mpi_free_contexts(struct mpi_softc *sc) +{ + kmem_free(sc->sc_contexts, sizeof (*sc->sc_contexts) * sc->sc_maxcmds); +} + +static int +mpi_alloc_replies(struct mpi_softc *sc) +{ + size_t len, rlen; + uint_t ncookies; + + sc->sc_rcbs = kmem_alloc(sizeof (struct mpi_rcb) * sc->sc_repq, + KM_SLEEP); + if (sc->sc_rcbs == NULL) { + cmn_err(CE_NOTE, "unable to allocate rcbs"); + goto err; + } + + len = MPI_REPLY_SIZE * sc->sc_repq; + + if (ddi_dma_alloc_handle(sc->sc_dev, &mpi_cb_dma_attr, DDI_DMA_SLEEP, + NULL, &sc->sc_replies_dma_handle) != DDI_SUCCESS) { + cmn_err(CE_NOTE, "unable to allocate dma handle for replies"); + goto free_rcbs; + } + + if (ddi_dma_mem_alloc(sc->sc_replies_dma_handle, len, &mpi_acc_attr, + DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &sc->sc_replies, &rlen, + &sc->sc_replies_acc_handle) != DDI_SUCCESS) { + cmn_err(CE_NOTE, "unable to alloc dma memory for replies"); + goto free_dma; + } + + if (ddi_dma_addr_bind_handle(sc->sc_replies_dma_handle, NULL, + sc->sc_replies, len, DDI_DMA_READ | DDI_DMA_CONSISTENT, + DDI_DMA_SLEEP, NULL, &sc->sc_replies_dma_cookie, + &ncookies) != DDI_DMA_MAPPED) { + cmn_err(CE_NOTE, "unable to bind replies handle"); + goto free_mem; + } + + if (ncookies != 1) { + cmn_err(CE_NOTE, "unexpected number of cookies for replies"); + goto unbind_handle; + } + + return (DDI_SUCCESS); + +unbind_handle: + (void) ddi_dma_unbind_handle(sc->sc_replies_dma_handle); +free_mem: + ddi_dma_mem_free(&sc->sc_replies_acc_handle); +free_dma: + ddi_dma_free_handle(&sc->sc_replies_dma_handle); +free_rcbs: + kmem_free(sc->sc_rcbs, sizeof (struct mpi_rcb) * sc->sc_repq); +err: + return (DDI_FAILURE); +} + +static void +mpi_push_replies(struct mpi_softc *sc) +{ + struct mpi_rcb *rcb; + uint8_t *kva = (uint8_t *)sc->sc_replies; + uint32_t dva = (uint32_t)sc->sc_replies_dma_cookie.dmac_laddress; + int i; + + for (i = 0; i < sc->sc_repq; i++) { + rcb = &sc->sc_rcbs[i]; + + rcb->rcb_reply = kva + MPI_REPLY_SIZE * i; + rcb->rcb_reply_dva = dva + MPI_REPLY_SIZE * i; + rcb->rcb_offset = MPI_REPLY_SIZE * i; + mpi_push_reply(sc, rcb); + } +} + +static void +mpi_free_replies(struct mpi_softc *sc) +{ + (void) ddi_dma_sync(sc->sc_replies_dma_handle, 0, 0, + DDI_DMA_SYNC_FORCPU); + (void) ddi_dma_unbind_handle(sc->sc_replies_dma_handle); + ddi_dma_mem_free(&sc->sc_replies_acc_handle); + ddi_dma_free_handle(&sc->sc_replies_dma_handle); + kmem_free(sc->sc_rcbs, sizeof (struct mpi_rcb) * sc->sc_repq); +} + +static int +mpi_init_caches(struct mpi_softc *sc) +{ + int instance = ddi_get_instance(sc->sc_dev); + char buf[64]; + + (void) snprintf(buf, sizeof (buf), "mpi%dccb", instance); + sc->sc_ccb_cache = kmem_cache_create(buf, sizeof (struct mpi_ccb), 8, + mpi_ccb_ctor, mpi_ccb_dtor, NULL, sc, NULL, 0); + if (sc->sc_ccb_cache == NULL) { + cmn_err(CE_NOTE, "unable to create ccb cache"); + goto err; + } + + (void) snprintf(buf, sizeof (buf), "mpi%dce", instance); + sc->sc_ce_cache = kmem_cache_create(buf, sizeof (struct mpi_ce), 8, + mpi_ce_ctor, mpi_ce_dtor, NULL, sc, NULL, 0); + if (sc->sc_ce_cache == NULL) { + cmn_err(CE_NOTE, "unable to create ce cache"); + goto destroy_ccbs; + } + + return (DDI_SUCCESS); + +destroy_ccbs: + kmem_cache_destroy(sc->sc_ccb_cache); +err: + return (DDI_FAILURE); +} + +void +mpi_destroy_caches(struct mpi_softc *sc) +{ + kmem_cache_destroy(sc->sc_ce_cache); + kmem_cache_destroy(sc->sc_ccb_cache); +} + +static int +mpi_ccb_ctor(void *buf, void *un, int km_flags) +{ + struct mpi_softc *sc = un; + struct mpi_ccb *ccb = buf; + int (*callback)(caddr_t); + size_t len, rlen; + uint_t ncookies; + + callback = (km_flags == KM_SLEEP) ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT; + len = sc->sc_maxcmdlen + MPI_CCB_EXTRALEN; + + if (ddi_dma_alloc_handle(sc->sc_dev, &mpi_cb_dma_attr, callback, + NULL, &ccb->ccb_dma_handle) != DDI_SUCCESS) + goto err; + + if (ddi_dma_mem_alloc(ccb->ccb_dma_handle, len, &mpi_acc_attr, + DDI_DMA_CONSISTENT, callback, NULL, (caddr_t *)&ccb->ccb_cmd, + &rlen, &ccb->ccb_acc_handle) != DDI_SUCCESS) + goto free_dma; + + if (ddi_dma_addr_bind_handle(ccb->ccb_dma_handle, NULL, + (caddr_t)ccb->ccb_cmd, len, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, + callback, NULL, &ccb->ccb_dma_cookie, &ncookies) != DDI_DMA_MAPPED) + goto free_mem; + + if (ncookies != 1) + goto unbind_handle; + + return (0); + +unbind_handle: + (void) ddi_dma_unbind_handle(ccb->ccb_dma_handle); +free_mem: + ddi_dma_mem_free(&ccb->ccb_acc_handle); +free_dma: + ddi_dma_free_handle(&ccb->ccb_dma_handle); +err: + return (-1); +} + +static void +mpi_ccb_dtor(void *buf, void *un) +{ + _NOTE(ARGUNUSED(un)); + struct mpi_ccb *ccb = buf; + + (void) ddi_dma_unbind_handle(ccb->ccb_dma_handle); + ddi_dma_mem_free(&ccb->ccb_acc_handle); + ddi_dma_free_handle(&ccb->ccb_dma_handle); +} + +static struct mpi_ccb * +mpi_ccb_get(struct mpi_softc *sc, int flags) +{ + struct mpi_ccb *ccb; + + ccb = kmem_cache_alloc(sc->sc_ccb_cache, flags); + + (void) memset(ccb->ccb_cmd, 0, sc->sc_maxcmdlen); + + return (ccb); +} + +static void +mpi_ccb_put(struct mpi_softc *sc, struct mpi_ccb *ccb) +{ + kmem_cache_free(sc->sc_ccb_cache, ccb); +} + +static int +mpi_ce_ctor(void *buf, void *un, int km_flags) +{ + struct mpi_softc *sc = un; + struct mpi_ce *ce = buf; + int (*callback)(caddr_t); + size_t len, rlen; + uint_t ncookies; + + callback = (km_flags == KM_SLEEP) ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT; + len = sc->sc_maxcmdlen + SENSE_LENGTH; + + if (ddi_dma_alloc_handle(sc->sc_dev, &mpi_cb_dma_attr, callback, + NULL, &ce->ce_dma_handle) != DDI_SUCCESS) + goto err; + + if (ddi_dma_mem_alloc(ce->ce_dma_handle, sc->sc_maxcmdlen, + &mpi_acc_attr, DDI_DMA_CONSISTENT, callback, NULL, + (caddr_t *)&ce->ce_sgl, &rlen, &ce->ce_acc_handle) != DDI_SUCCESS) + goto free_dma; + + if (ddi_dma_addr_bind_handle(ce->ce_dma_handle, NULL, + (caddr_t)ce->ce_sgl, len, DDI_DMA_READ | DDI_DMA_CONSISTENT, + callback, NULL, &ce->ce_dma_cookie, &ncookies) != DDI_DMA_MAPPED) + goto free_mem; + + if (ncookies != 1) + goto unbind_handle; + + return (0); + +unbind_handle: + (void) ddi_dma_unbind_handle(ce->ce_dma_handle); +free_mem: + ddi_dma_mem_free(&ce->ce_acc_handle); +free_dma: + ddi_dma_free_handle(&ce->ce_dma_handle); +err: + return (-1); +} + +static void +mpi_ce_dtor(void *buf, void *un) +{ + _NOTE(ARGUNUSED(un)); + struct mpi_ce *ce = buf; + + (void) ddi_dma_unbind_handle(ce->ce_dma_handle); + ddi_dma_mem_free(&ce->ce_acc_handle); + ddi_dma_free_handle(&ce->ce_dma_handle); +} + +static int +mpi_iocinit(struct mpi_softc *sc) +{ + struct mpi_msg_iocinit_request iiq; + struct mpi_msg_iocinit_reply iip; + + DPRINTF(MPI_D_MISC, "mpi_iocinit\n"); + + bzero(&iiq, sizeof (iiq)); + bzero(&iip, sizeof (iip)); + + iiq.function = MPI_FUNCTION_IOC_INIT; + iiq.whoinit = MPI_WHOINIT_HOST_DRIVER; + + iiq.max_devices = (sc->sc_buswidth == 256) ? 0 : sc->sc_buswidth; + iiq.max_buses = 1; + + iiq.msg_context = htole32(0xd00fd00f); + + iiq.reply_frame_size = htole16(MPI_REPLY_SIZE); + + /* mpi_cb_dma_attr keeps these under 4gig */ + iiq.host_mfa_hi_addr = htole32(0x0); + iiq.sense_buffer_hi_addr = htole32(0x0); + + iiq.msg_version_maj = 0x01; + iiq.msg_version_min = 0x02; + + iiq.hdr_version_unit = 0x0d; + iiq.hdr_version_dev = 0x00; + + if (mpi_handshake_send(sc, &iiq, dwordsof(iiq)) != DDI_SUCCESS) { + DPRINTF(MPI_D_MISC, "mpi_iocinit send failed"); + return (DDI_FAILURE); + } + + if (mpi_handshake_recv(sc, &iip, dwordsof(iip)) != 0) { + DPRINTF(MPI_D_MISC, "mpi_iocinit recv failed"); + return (DDI_FAILURE); + } + + DPRINTF(MPI_D_MISC, " function: 0x%02x msg_length: %d " + "whoinit: 0x%02x\n", iip.function, iip.msg_length, iip.whoinit); + DPRINTF(MPI_D_MISC, " msg_flags: 0x%02x max_buses: %d " + "max_devices: %d flags: 0x%02x\n", iip.msg_flags, + iip.max_buses, iip.max_devices, iip.flags); + DPRINTF(MPI_D_MISC, " msg_context: 0x%08x\n", + letoh32(iip.msg_context)); + DPRINTF(MPI_D_MISC, " ioc_status: 0x%04x\n", + letoh16(iip.ioc_status)); + DPRINTF(MPI_D_MISC, " ioc_loginfo: 0x%08x\n", + letoh32(iip.ioc_loginfo)); + + return (DDI_SUCCESS); +} + +static int +mpi_portfacts(struct mpi_softc *sc) +{ + struct mpi_ccb *ccb; + struct mpi_msg_portfacts_request *pfq; + struct mpi_msg_portfacts_reply *pfp; + int rv = DDI_FAILURE; + + ccb = mpi_ccb_get(sc, KM_SLEEP); + if (ccb == NULL) { + cmn_err(CE_NOTE, "portfacts was unable to get a ccb"); + return (rv); + } + + pfq = ccb->ccb_cmd; + + pfq->function = MPI_FUNCTION_PORT_FACTS; + pfq->chain_offset = 0; + pfq->msg_flags = 0; + pfq->port_number = 0; + + mpi_wait(sc, ccb); + if (ccb->ccb_rcb == NULL) { + cmn_err(CE_NOTE, "no reply for portfacts"); + goto err; + } + pfp = ccb->ccb_rcb->rcb_reply; + + DPRINTF(MPI_D_MISC, " function: 0x%02x msg_length: %d\n", + pfp->function, pfp->msg_length); + DPRINTF(MPI_D_MISC, " msg_flags: 0x%02x port_number: %d\n", + pfp->msg_flags, pfp->port_number); + DPRINTF(MPI_D_MISC, " msg_context: 0x%08x\n", + letoh32(pfp->msg_context)); + DPRINTF(MPI_D_MISC, " ioc_status: 0x%04x\n", + letoh16(pfp->ioc_status)); + DPRINTF(MPI_D_MISC, " ioc_loginfo: 0x%08x\n", + letoh32(pfp->ioc_loginfo)); + DPRINTF(MPI_D_MISC, " max_devices: %d port_type: 0x%02x\n", + letoh16(pfp->max_devices), pfp->port_type); + DPRINTF(MPI_D_MISC, " protocol_flags: 0x%04x port_scsi_id: %d\n", + letoh16(pfp->protocol_flags), + letoh16(pfp->port_scsi_id)); + DPRINTF(MPI_D_MISC, " max_persistent_ids: %d " + "max_posted_cmd_buffers: %d\n", + letoh16(pfp->max_persistent_ids), + letoh16(pfp->max_posted_cmd_buffers)); + DPRINTF(MPI_D_MISC, " max_lan_buckets: %d\n", + letoh16(pfp->max_lan_buckets)); + + sc->sc_porttype = pfp->port_type; + sc->sc_target = letoh16(pfp->port_scsi_id); + + mpi_push_reply(sc, ccb->ccb_rcb); + rv = DDI_SUCCESS; + +err: + mpi_ccb_put(sc, ccb); + + return (rv); +} + +static int +mpi_portenable(struct mpi_softc *sc) +{ + struct mpi_ccb *ccb; + struct mpi_msg_portenable_request *peq; + int rv = DDI_FAILURE; + + ccb = mpi_ccb_get(sc, KM_SLEEP); + if (ccb == NULL) { + cmn_err(CE_NOTE, "portenable was unable to get a ccb"); + return (rv); + } + + peq = ccb->ccb_cmd; + + peq->function = MPI_FUNCTION_PORT_ENABLE; + peq->port_number = 0; + + mpi_wait(sc, ccb); + if (ccb->ccb_rcb == NULL) { + cmn_err(CE_NOTE, "no reply for portenable"); + goto err; + } + + mpi_push_reply(sc, ccb->ccb_rcb); + rv = DDI_SUCCESS; +err: + mpi_ccb_put(sc, ccb); + + return (rv); +} + + +struct mpi_wait_cookie { + kcondvar_t cv; + kmutex_t mtx; +}; + +static void +mpi_wait(struct mpi_softc *sc, struct mpi_ccb *ccb) +{ + struct mpi_wait_cookie c; + + ASSERT(ccb->ccb_done == NULL); + ASSERT(ccb->ccb_cookie == NULL); + + cv_init(&c.cv, NULL, CV_DRIVER, NULL); + mutex_init(&c.mtx, NULL, MUTEX_DRIVER, sc->sc_iblock_cookie); + + ccb->ccb_cookie = &c; + ccb->ccb_done = mpi_wait_done; + + mpi_start(sc, ccb); + + mutex_enter(&c.mtx); + while (ccb->ccb_cookie != NULL) + cv_wait(&c.cv, &c.mtx); + mutex_exit(&c.mtx); + + mutex_destroy(&c.mtx); + cv_destroy(&c.cv); +} + +static void +mpi_wait_done(struct mpi_softc *sc, struct mpi_ccb *ccb) +{ + struct mpi_wait_cookie *c = ccb->ccb_cookie; + _NOTE(ARGUNUSED(sc)); + + mutex_enter(&c->mtx); + ccb->ccb_cookie = NULL; + cv_broadcast(&c->cv); + mutex_exit(&c->mtx); +} + +static void +mpi_start(struct mpi_softc *sc, struct mpi_ccb *ccb) +{ + struct mpi_context *ctx; + + mutex_enter(&sc->sc_runq_mtx); + ctx = SLIST_FIRST(&sc->sc_ctx_list); + if (ctx != NULL) + SLIST_REMOVE_HEAD(&sc->sc_ctx_list, link.entry); + else + SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_list, ccb, ccb_entry); + mutex_exit(&sc->sc_runq_mtx); + + if (ctx != NULL) + mpi_post(sc, ccb, ctx); +} + +static void +mpi_restart(struct mpi_softc *sc, struct mpi_context *ctx) +{ + struct mpi_ccb *ccb; + + mutex_enter(&sc->sc_runq_mtx); + ccb = SIMPLEQ_FIRST(&sc->sc_ccb_list); + if (ccb != NULL) + SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_list, ccb_entry); + else + SLIST_INSERT_HEAD(&sc->sc_ctx_list, ctx, link.entry); + mutex_exit(&sc->sc_runq_mtx); + + if (ccb != NULL) + mpi_post(sc, ccb, ctx); +} + +static void +mpi_post(struct mpi_softc *sc, struct mpi_ccb *ccb, struct mpi_context *ctx) +{ + struct mpi_msg_request *r = ccb->ccb_cmd; + + ctx->link.ccb = ccb; + r->msg_context = ctx->id; + + (void) ddi_dma_sync(ccb->ccb_dma_handle, 0, 0, DDI_DMA_SYNC_FORDEV); + mpi_write(sc, MPI_REQ_QUEUE, ccb->ccb_dma_cookie.dmac_laddress); +} + +static uint_t +mpi_intr(caddr_t arg) +{ + struct mpi_softc *sc = (void *)arg; + uint32_t reg; + uint_t rv = DDI_INTR_UNCLAIMED; + + while ((reg = mpi_pop_reply(sc)) != 0xffffffff) { + mpi_reply(sc, reg); + rv = DDI_INTR_CLAIMED; + } + + return (rv); +} + +static void +mpi_reply(struct mpi_softc *sc, uint32_t reg) +{ + struct mpi_context *ctx; + struct mpi_ccb *ccb; + struct mpi_rcb *rcb = NULL; + struct mpi_msg_reply *reply = NULL; + uint32_t dva; + int id; + int i; + + if (reg & MPI_REPLY_QUEUE_ADDRESS) { + uint32_t reply_dva = + (uint32_t)sc->sc_replies_dma_cookie.dmac_laddress; + dva = (reg & MPI_REPLY_QUEUE_ADDRESS_MASK) << 1; + i = (dva - reply_dva) / MPI_REPLY_SIZE; + rcb = &sc->sc_rcbs[i]; + + (void) ddi_dma_sync(sc->sc_replies_dma_handle, rcb->rcb_offset, + MPI_REPLY_SIZE, DDI_DMA_SYNC_FORCPU); + + reply = rcb->rcb_reply; + id = reply->msg_context; + } else { + switch (reg & MPI_REPLY_QUEUE_TYPE_MASK) { + case MPI_REPLY_QUEUE_TYPE_INIT: + id = reg & MPI_REPLY_QUEUE_CONTEXT; + break; + + default: + break; +#if 0 + panic("%s: unsupported context reply\n", + DEVNAME(sc)); +#endif + } + } + + ctx = &sc->sc_contexts[id]; + ccb = ctx->link.ccb; + + mpi_restart(sc, ctx); + + ccb->ccb_rcb = rcb; + ccb->ccb_done(sc, ccb); +} + +static int +mpi_handshake_send(struct mpi_softc *sc, void *buf, size_t dwords) +{ + uint32_t *query = buf; + int i; + + /* make sure the doorbell is not in use. */ + if (mpi_read_db(sc) & MPI_DOORBELL_INUSE) + return (DDI_FAILURE); + + /* clear pending doorbell interrupts */ + if (mpi_read_intr(sc) & MPI_INTR_STATUS_DOORBELL) + mpi_write_intr(sc, 0); + + /* + * first write the doorbell with the handshake function and the + * dword count. + */ + mpi_write_db(sc, MPI_DOORBELL_FUNCTION(MPI_FUNCTION_HANDSHAKE) | + MPI_DOORBELL_DWORDS(dwords)); + + /* + * the doorbell used bit will be set because a doorbell function has + * started. Wait for the interrupt and then ack it. + */ + if (mpi_wait_db_int(sc) != 0) + return (DDI_FAILURE); + mpi_write_intr(sc, 0); + + /* poll for the acknowledgement. */ + if (mpi_wait_db_ack(sc) != 0) + return (DDI_FAILURE); + + /* write the query through the doorbell. */ + for (i = 0; i < dwords; i++) { + mpi_write_db(sc, htole32(query[i])); + if (mpi_wait_db_ack(sc) != 0) + return (DDI_FAILURE); + } + + return (DDI_SUCCESS); +} + +static int +mpi_handshake_recv_dword(struct mpi_softc *sc, uint32_t *dword) +{ + uint16_t *words = (uint16_t *)dword; + int i; + + for (i = 0; i < 2; i++) { + if (mpi_wait_db_int(sc) != 0) + return (DDI_FAILURE); + words[i] = letoh16(mpi_read_db(sc) & MPI_DOORBELL_DATA_MASK); + mpi_write_intr(sc, 0); + } + + return (DDI_SUCCESS); +} + +static int +mpi_handshake_recv(struct mpi_softc *sc, void *buf, size_t dwords) +{ + struct mpi_msg_reply *reply = buf; + uint32_t *dbuf = buf, dummy; + int i; + + /* get the first dword so we can read the length out of the header. */ + if (mpi_handshake_recv_dword(sc, &dbuf[0]) != 0) + return (DDI_FAILURE); + + DPRINTF(MPI_D_CMD, "mpi_handshake_recv dwords: %ld reply: %d\n", + dwords, reply->msg_length); + + /* + * the total length, in dwords, is in the message length field of the + * reply header. + */ + for (i = 1; i < MIN(dwords, reply->msg_length); i++) { + if (mpi_handshake_recv_dword(sc, &dbuf[i]) != 0) + return (DDI_FAILURE); + } + + /* if there's extra stuff to come off the ioc, discard it */ + while (i++ < reply->msg_length) { + if (mpi_handshake_recv_dword(sc, &dummy) != 0) + return (DDI_FAILURE); + DPRINTF(MPI_D_CMD, "mpi_handshake_recv dummy read: " + "0x%08x\n", dummy); + } + + /* wait for the doorbell used bit to be reset and clear the intr */ + if (mpi_wait_db_int(sc) != 0) + return (DDI_FAILURE); + mpi_write_intr(sc, 0); + + if (reply->msg_length < dwords) + return (DDI_FAILURE); + + return (DDI_SUCCESS); +} + +static int +mpi_wait_eq(struct mpi_softc *sc, off_t r, uint32_t mask, uint32_t target) +{ + int i; + + for (i = 0; i < 10000; i++) { + if ((mpi_read(sc, r) & mask) == target) + return (0); + delay(drv_usectohz(1000)); + } + + return (1); +} + +static int +mpi_wait_ne(struct mpi_softc *sc, off_t r, uint32_t mask, uint32_t target) +{ + int i; + + for (i = 0; i < 10000; i++) { + if ((mpi_read(sc, r) & mask) != target) + return (0); + delay(drv_usectohz(1000)); + } + + return (1); +} + +static int +mpi_hba_attach(struct mpi_softc *sc) +{ + scsi_hba_tran_t *tran; + + tran = scsi_hba_tran_alloc(sc->sc_dev, SCSI_HBA_CANSLEEP); + if (tran == NULL) + return (DDI_FAILURE); + + tran->tran_hba_private = sc; + tran->tran_tgt_private = NULL; + tran->tran_tgt_init = mpi_tran_tgt_init; + tran->tran_tgt_probe = scsi_hba_probe; + /* tran->tran_tgt_free */ + + tran->tran_start = mpi_tran_start; + tran->tran_reset = mpi_tran_reset; + tran->tran_getcap = mpi_tran_getcap; + tran->tran_setcap = mpi_tran_setcap; + tran->tran_init_pkt = mpi_tran_init_pkt; + tran->tran_destroy_pkt = mpi_tran_destroy_pkt; + tran->tran_dmafree = mpi_tran_dmafree; + tran->tran_sync_pkt = mpi_tran_sync_pkt; + + tran->tran_abort = NULL; + tran->tran_tgt_free = NULL; + tran->tran_quiesce = NULL; + tran->tran_unquiesce = NULL; + tran->tran_sd = NULL; + + if (scsi_hba_attach_setup(sc->sc_dev, &mpi_cb_dma_attr, tran, + SCSI_HBA_TRAN_CLONE) != DDI_SUCCESS) + goto tran_free; + + sc->sc_tran = tran; + + return (DDI_SUCCESS); + +tran_free: + scsi_hba_tran_free(tran); +err: + return (DDI_FAILURE); +} + +static int +mpi_tran_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip, + scsi_hba_tran_t *tran, struct scsi_device *sd) +{ + struct mpi_softc *sc; + _NOTE(ARGUNUSED(hba_dip)); + _NOTE(ARGUNUSED(tgt_dip)); + + sc = (struct mpi_softc *)tran->tran_hba_private; + + /* mpi_msg_scsi_io only has a byte for the target id */ + if (sd->sd_address.a_target >= sc->sc_buswidth || + sd->sd_address.a_target == sc->sc_target) + return (DDI_FAILURE); + + return (DDI_SUCCESS); +} + +static int +mpi_tran_start(struct scsi_address *ap, struct scsi_pkt *pkt) +{ + struct mpi_softc *sc; + struct mpi_pkt_data *mpd; + struct mpi_ccb *ccb; + struct mpi_msg_scsi_io *io; + + if (pkt->pkt_flags & FLAG_NOINTR) + return (TRAN_FATAL_ERROR); + + sc = (struct mpi_softc *)ap->a_hba_tran->tran_hba_private; + mpd = (struct mpi_pkt_data *)pkt->pkt_ha_private; + ccb = mpd->mpd_ccb; + (void) memset(ccb->ccb_cmd, 0, sc->sc_maxcmdlen + MPI_CCB_EXTRALEN); + + io = ccb->ccb_cmd; + io->function = MPI_FUNCTION_SCSI_IO_REQUEST; + io->target_id = ap->a_target; + /* bus is always 0 */ + io->cdb_length = mpd->mpd_cdblen; + io->sense_buf_len = mpd->mpd_senselen; + io->msg_flags = MPI_SCSIIO_SENSE_BUF_ADDR_WIDTH_64; + + io->lun[0] = ap->a_lun; + + if (mpd->mpd_sgllen == 0) + io->direction = MPI_SCSIIO_DIR_NONE; + else + io->direction = mpd->mpd_read ? + MPI_SCSIIO_DIR_READ : MPI_SCSIIO_DIR_WRITE; + + io->tagging = MPI_SCSIIO_ATTR_SIMPLE_Q; + + (void) memcpy(io->cdb, pkt->pkt_cdbp, mpd->mpd_cdblen); + + io->data_length = htole32(mpd->mpd_datalen); + + io->sense_buf_low_addr = htole32(ccb->ccb_dma_cookie.dmac_laddress + + sc->sc_maxcmdlen); + + mpi_load_sgl(sc, mpd, (struct mpi_sge *)(io + 1)); + + pkt->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET | STATE_SENT_CMD; + mpi_start(sc, ccb); + + return (TRAN_ACCEPT); +} + +static void +mpi_load_sgl(struct mpi_softc *sc, struct mpi_pkt_data *mpd, + struct mpi_sge *sge) +{ + _NOTE(ARGUNUSED(sc)); + struct mpi_sge *nsge = sge; + uint32_t flags; + int i; + + /* XXX */ + + if (mpd->mpd_sgllen == 0) { + sge->sg_hdr = htole32(MPI_SGE_FL_TYPE_SIMPLE | + MPI_SGE_FL_LAST | MPI_SGE_FL_EOB | MPI_SGE_FL_EOL); + return; + } + + flags = MPI_SGE_FL_TYPE_SIMPLE | MPI_SGE_FL_SIZE_64; + if (!mpd->mpd_read) + flags |= MPI_SGE_FL_DIR_OUT; + + for (i = 0; i < mpd->mpd_sgllen; i++) { + sge = nsge; + + sge->sg_hdr = htole32(flags | mpd->mpd_sgl[i].sg_len); + sge->sg_hi_addr = (uint32_t)(mpd->mpd_sgl[i].sg_addr >> 32); + sge->sg_lo_addr = (uint32_t)(mpd->mpd_sgl[i].sg_addr); + + nsge = sge + 1; + } + + /* terminate list */ + sge->sg_hdr |= htole32(MPI_SGE_FL_LAST | MPI_SGE_FL_EOB | + MPI_SGE_FL_EOL); +} + +static int +mpi_tran_reset(struct scsi_address *ap, int level) +{ + _NOTE(ARGUNUSED(ap)); + _NOTE(ARGUNUSED(level)); +#if 0 + cmn_err(CE_NOTE, "mpi_tran_reset"); +#endif + return (0); +} + +static int +mpi_tran_getcap(struct scsi_address *ap, char *cap, int whom) +{ + _NOTE(ARGUNUSED(ap)); +// struct mpi_softc *sc; + + if (cap == NULL || whom == 0) + return (-1); + +// sc = (struct mpi_softc *)ap->a_hba_tran->tran_hba_private; + + switch (scsi_hba_lookup_capstr(cap)) { + case SCSI_CAP_ARQ: + case SCSI_CAP_TAGGED_QING: + case SCSI_CAP_UNTAGGED_QING: + return (1); + default: + break; + } + + return (-1); +} + +static int +mpi_tran_setcap(struct scsi_address *ap, char *cap, int value, int whom) +{ + _NOTE(ARGUNUSED(ap)); + _NOTE(ARGUNUSED(value)); + + if (cap == NULL || whom == 0) + return (-1); + + switch (scsi_hba_lookup_capstr(cap)) { + case SCSI_CAP_ARQ: + case SCSI_CAP_TAGGED_QING: + case SCSI_CAP_UNTAGGED_QING: + return (1); +#if 0 + case SCSI_CAP_TOTAL_SECTORS: + return (1); + case SCSI_CAP_SECTOR_SIZE: + return (1); +#endif + default: + break; + } + + return (0); +} + +static struct scsi_pkt * +mpi_tran_init_pkt(struct scsi_address *ap, struct scsi_pkt *pkt, + struct buf *bp, int cmdlen, int statuslen, int tgtlen, int flags, + int (*callback)(caddr_t), caddr_t arg) +{ + struct mpi_softc *sc; + struct mpi_ccb *ccb; + struct mpi_pkt_data *mpd; + struct scsi_pkt *npkt = NULL; + int (*cb)(caddr_t); + int km; + + sc = (struct mpi_softc *)ap->a_hba_tran->tran_hba_private; + if (callback == SLEEP_FUNC) { + cb = DDI_DMA_SLEEP; + km = KM_SLEEP; + } else { + cb = DDI_DMA_DONTWAIT; + km = KM_NOSLEEP; + } + + /* step 1: packet allocation */ + if (pkt == NULL) { + if (cmdlen > MPI_CDB_LEN || statuslen > MPI_CCB_EXTRALEN) + goto err; + + ccb = mpi_ccb_get(sc, km); + if (ccb == NULL) + goto err; + + pkt = scsi_hba_pkt_alloc(sc->sc_dev, ap, cmdlen, statuslen, + tgtlen, sizeof (struct mpi_pkt_data), callback, arg); + if (pkt == NULL) + goto put_ccb; + + mpd = (struct mpi_pkt_data *)pkt->pkt_ha_private; + ccb->ccb_cookie = mpd; + ccb->ccb_done = mpi_tran_done; + + mpd->mpd_ccb = ccb; + mpd->mpd_pkt = pkt; + mpd->mpd_dma_mapped = 0; + mpd->mpd_cdblen = cmdlen; + mpd->mpd_senselen = statuslen; + + mpd->mpd_sgl = kmem_alloc(sizeof (*mpd->mpd_sgl) * + sc->sc_first_sgl_len, km); + if (mpd->mpd_sgl == NULL) + goto pkt_free; + + if (ddi_dma_alloc_handle(sc->sc_dev, &sc->sc_io_dma_attr, cb, + NULL, &mpd->mpd_dma_handle) != DDI_SUCCESS) + goto sgl_free; + + pkt->pkt_address = *ap; + pkt->pkt_comp = (void (*)(struct scsi_pkt *))NULL; + pkt->pkt_flags = 0; + pkt->pkt_time = 0; + pkt->pkt_resid = 0; + pkt->pkt_statistics = 0; + pkt->pkt_reason = 0; + + npkt = pkt; + } else { + mpd = (struct mpi_pkt_data *)pkt->pkt_ha_private; + ccb = mpd->mpd_ccb; + } + + mpd->mpd_sgllen = 0; + mpd->mpd_datalen = 0; + + /* step 2: dma allocation */ + if (bp == NULL || bp->b_bcount == 0) + return (pkt); + + if (mpi_dma_map(sc, mpd, bp, flags, cb) != DDI_SUCCESS) { + if (npkt != NULL) + goto dma_free; + else + goto err; + } + + pkt->pkt_resid = bp->b_bcount - mpd->mpd_totallen; + + return (pkt); + +dma_free: + ddi_dma_free_handle(&mpd->mpd_dma_handle); +sgl_free: + kmem_free(mpd->mpd_sgl, sizeof (*mpd->mpd_sgl) * sc->sc_first_sgl_len); +pkt_free: + scsi_hba_pkt_free(ap, pkt); +put_ccb: + mpi_ccb_put(sc, ccb); +err: + return (NULL); +} + +static int +mpi_dma_map(struct mpi_softc *sc, struct mpi_pkt_data *mpd, + struct buf *bp, int flags, int (*cb)(caddr_t)) +{ + int dma_flags; + int rv; + int i; + + if (!mpd->mpd_dma_mapped) { + if (bp->b_flags & B_READ) { + dma_flags = DDI_DMA_READ; + mpd->mpd_read = 1; + } else { + dma_flags = DDI_DMA_WRITE; + mpd->mpd_read = 0; + } + + if (flags & PKT_CONSISTENT) + dma_flags |= DDI_DMA_CONSISTENT; + if (flags & PKT_DMA_PARTIAL) + dma_flags |= DDI_DMA_PARTIAL; + + rv = ddi_dma_buf_bind_handle(mpd->mpd_dma_handle, + bp, dma_flags, cb, NULL, + &mpd->mpd_cookies, &mpd->mpd_ncookies); + switch (rv) { + case DDI_DMA_MAPPED: + case DDI_DMA_PARTIAL_MAP: + break; + + case DDI_DMA_NORESOURCES: + bioerror(bp, 0); + return (DDI_FAILURE); + case DDI_DMA_NOMAPPING: + bioerror(bp, EFAULT); + return (DDI_FAILURE); + case DDI_DMA_TOOBIG: + bioerror(bp, EINVAL); + return (DDI_FAILURE); + case DDI_DMA_INUSE: + cmn_err(CE_PANIC, "ddi_dma_buf_bind_handle: " + "DDI_DMA_INUSE can't happen"); + /* NOTREACHED */ + default: + cmn_err(CE_PANIC, "ddi_dma_buf_bind_handle: " + "unknown rv: 0x%x", rv); + /* NOTREACHED */ + } + + mpd->mpd_window = 0; + mpd->mpd_totallen = 0; + mpd->mpd_curcookie = 0; + mpd->mpd_dma_mapped = 1; + } else if (mpd->mpd_curcookie == mpd->mpd_ncookies) { + off_t off; + size_t len; + + /* next window */ + mpd->mpd_window++; + mpd->mpd_curcookie = 0; + rv = ddi_dma_getwin(mpd->mpd_dma_handle, mpd->mpd_window, + &off, &len, &mpd->mpd_cookies, &mpd->mpd_ncookies); + if (rv != DDI_SUCCESS) + return (DDI_FAILURE); + } + + mpd->mpd_datalen = 0; + for (i = 0; i < sc->sc_first_sgl_len; i++) { + if (mpd->mpd_curcookie == mpd->mpd_ncookies) + break; + + mpd->mpd_sgl[i].sg_len = mpd->mpd_cookies.dmac_size; + mpd->mpd_sgl[i].sg_addr = mpd->mpd_cookies.dmac_address; + mpd->mpd_datalen += mpd->mpd_cookies.dmac_size; + + ddi_dma_nextcookie(mpd->mpd_dma_handle, &mpd->mpd_cookies); + mpd->mpd_curcookie++; + } + mpd->mpd_sgllen = i; + mpd->mpd_totallen += mpd->mpd_datalen; + + return (DDI_SUCCESS); +} + +static void +mpi_tran_destroy_pkt(struct scsi_address *ap, struct scsi_pkt *pkt) +{ + struct mpi_softc *sc; + struct mpi_ccb *ccb; + struct mpi_pkt_data *mpd; + + sc = (struct mpi_softc *)ap->a_hba_tran->tran_hba_private; + mpd = (struct mpi_pkt_data *)pkt->pkt_ha_private; + ccb = mpd->mpd_ccb; + + if (mpd->mpd_dma_mapped) + (void) ddi_dma_unbind_handle(mpd->mpd_dma_handle); + + ddi_dma_free_handle(&mpd->mpd_dma_handle); + kmem_free(mpd->mpd_sgl, sizeof (*mpd->mpd_sgl) * sc->sc_first_sgl_len); + scsi_hba_pkt_free(ap, pkt); + mpi_ccb_put(sc, ccb); +} + +static void +mpi_tran_dmafree(struct scsi_address *ap, struct scsi_pkt *pkt) +{ + _NOTE(ARGUNUSED(ap)); + struct mpi_pkt_data *mpd; + + mpd = (struct mpi_pkt_data *)pkt->pkt_ha_private; + + if (mpd->mpd_dma_mapped) { + (void) ddi_dma_unbind_handle(mpd->mpd_dma_handle); + mpd->mpd_dma_mapped = 0; + } +} + +static void +mpi_tran_sync_pkt(struct scsi_address *ap, struct scsi_pkt *pkt) +{ + _NOTE(ARGUNUSED(ap)); + struct mpi_pkt_data *mpd; + + mpd = (struct mpi_pkt_data *)pkt->pkt_ha_private; + + if (!mpd->mpd_dma_mapped) + return; + + (void) ddi_dma_sync(mpd->mpd_dma_handle, 0, 0, + mpd->mpd_read ? DDI_DMA_SYNC_FORDEV : DDI_DMA_SYNC_FORCPU); +} + +static void +mpi_tran_done(struct mpi_softc *sc, struct mpi_ccb *ccb) +{ + struct mpi_pkt_data *mpd = ccb->ccb_cookie; + struct scsi_pkt *pkt = mpd->mpd_pkt; + struct scsi_arq_status *arqstat; + struct mpi_msg_scsi_io_error *sie; + uint8_t *sense; + + if (ccb->ccb_rcb == NULL) { + /* no scsi error, we're ok to drop out early */ + pkt->pkt_reason = CMD_CMPLT; + pkt->pkt_state |= STATE_XFERRED_DATA; + pkt->pkt_resid = 0; + pkt->pkt_comp(pkt); + return; + } + + sie = ccb->ccb_rcb->rcb_reply; + pkt->pkt_resid = mpd->mpd_datalen - letoh32(sie->transfer_count); + + switch(letoh16(sie->ioc_status)) { + case MPI_IOCSTATUS_SUCCESS: + case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: + pkt->pkt_reason = CMD_CMPLT; + pkt->pkt_state |= (STATE_XFERRED_DATA | STATE_GOT_STATUS); + pkt->pkt_scbp[0] = sie->scsi_status; + + if (sie->scsi_status == STATUS_CHECK && + sie->scsi_state & MPI_SCSIIO_ERR_STATE_AUTOSENSE_VALID) { + pkt->pkt_state |= STATE_ARQ_DONE; + arqstat = (void *)pkt->pkt_scbp; + arqstat->sts_rqpkt_reason = CMD_CMPLT; + arqstat->sts_rqpkt_resid = 0; + arqstat->sts_rqpkt_state = STATE_GOT_BUS | + STATE_GOT_TARGET | STATE_SENT_CMD | + STATE_XFERRED_DATA; + arqstat->sts_rqpkt_statistics = 0; + + (void) ddi_dma_sync(ccb->ccb_dma_handle, 0, 0, + DDI_DMA_SYNC_FORKERNEL); + sense = ccb->ccb_cmd; + sense += sc->sc_maxcmdlen; + (void) memcpy(&arqstat->sts_sensedata, sense, + sizeof (arqstat->sts_sensedata)); + } + break; + + case MPI_IOCSTATUS_SCSI_INVALID_BUS: + case MPI_IOCSTATUS_SCSI_INVALID_TARGETID: + case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: + pkt->pkt_reason = CMD_DEV_GONE; + pkt->pkt_state |= STATE_GOT_BUS; + break; + + case MPI_IOCSTATUS_SCSI_DATA_OVERRUN: + pkt->pkt_reason = CMD_DATA_OVR; + pkt->pkt_state |= (STATE_GOT_BUS | STATE_GOT_TARGET + | STATE_SENT_CMD | STATE_GOT_STATUS + | STATE_XFERRED_DATA); + pkt->pkt_resid = 0; + break; + + case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: + case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: + pkt->pkt_state |= (STATE_GOT_BUS | STATE_GOT_TARGET + | STATE_SENT_CMD | STATE_GOT_STATUS); + if (pkt->pkt_resid != mpd->mpd_datalen) + pkt->pkt_state |= STATE_XFERRED_DATA; + break; + + case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR: + case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: + pkt->pkt_state |= (STATE_GOT_BUS | STATE_GOT_TARGET); + pkt->pkt_reason = CMD_TERMINATED; + break; + + case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: + case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: + case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: + pkt->pkt_reason = CMD_RESET; + break; + default: + cmn_err(CE_WARN, "ioc_status=%x", letoh16(sie->ioc_status)); + break; + } + + mpi_push_reply(sc, ccb->ccb_rcb); + + pkt->pkt_comp(pkt); +}