diff options
author | Marco Peereboom <marco@cvs.openbsd.org> | 2009-10-27 02:22:00 +0000 |
---|---|---|
committer | Marco Peereboom <marco@cvs.openbsd.org> | 2009-10-27 02:22:00 +0000 |
commit | 4a3bfd77f17b7172c349c6688aac672325d7415e (patch) | |
tree | a23ed96961e9169703a632ddb47124f9ea280a37 /sys/dev | |
parent | a5e2ca2be1e2a0471b4ec50fe029c65dba338e0c (diff) |
Add driver for MPI2 SAS HBAs. This covers LSI MPT2 and Dell H200 HBAs.
IO works fine but it will remain disabled for now.
From James Giannoules
dlg: go go go
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/pci/files.pci | 7 | ||||
-rwxr-xr-x | sys/dev/pci/mpii_pci.c | 3974 |
2 files changed, 3980 insertions, 1 deletions
diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index 930e01243cd..d2698b6b126 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $OpenBSD: files.pci,v 1.263 2009/08/29 21:12:55 kettenis Exp $ +# $OpenBSD: files.pci,v 1.264 2009/10/27 02:21:59 marco Exp $ # $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $ # # Config file and device description for machine-independent PCI code. @@ -222,6 +222,11 @@ file dev/pci/isp_pci.c isp_pci attach mpi at pci with mpi_pci file dev/pci/mpi_pci.c mpi_pci +# LSI Logic Fusion-MPT Message Passing Interface 2.0 +device mpii: scsi +attach mpii at pci with mpii_pci +file dev/pci/mpii_pci.c mpii_pci + # Emulex Light Pulse Fibre Channel (Service Level Interface) attach sli at pci with sli_pci file dev/pci/sli_pci.c sli_pci diff --git a/sys/dev/pci/mpii_pci.c b/sys/dev/pci/mpii_pci.c new file mode 100755 index 00000000000..504bd43b2fb --- /dev/null +++ b/sys/dev/pci/mpii_pci.c @@ -0,0 +1,3974 @@ +/* $OpenBSD: mpii_pci.c,v 1.1 2009/10/27 02:21:59 marco Exp $ */ +/* + * Copyright (c) James Giannoules + * Copyright (c) 2005 David Gwynne <dlg@openbsd.org> + * Copyright (c) 2005 Marco Peereboom <marco@openbsd.org> + * + * + * 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 "bio.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/device.h> +#include <sys/ioctl.h> +#include <sys/proc.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/rwlock.h> +#include <sys/sensors.h> + +#include <machine/bus.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcidevs.h> + +#ifdef __sparc64__ +#include <dev/ofw/openfirm.h> +#endif + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + +#include <dev/biovar.h> + +#define MPII_DOORBELL 0x00 +/* doorbell read bits */ +#define MPII_DOORBELL_STATE (0xf<<28) /* ioc state */ +#define MPII_DOORBELL_STATE_RESET (0x0<<28) +#define MPII_DOORBELL_STATE_READY (0x1<<28) +#define MPII_DOORBELL_STATE_OPER (0x2<<28) +#define MPII_DOORBELL_STATE_FAULT (0x4<<28) +#define MPII_DOORBELL_INUSE (0x1<<27) /* doorbell used */ +#define MPII_DOORBELL_WHOINIT (0x7<<24) /* last to reset ioc */ +#define MPII_DOORBELL_WHOINIT_NOONE (0x0<<24) /* not initialized */ +#define MPII_DOORBELL_WHOINIT_SYSBIOS (0x1<<24) /* system bios */ +#define MPII_DOORBELL_WHOINIT_ROMBIOS (0x2<<24) /* rom bios */ +#define MPII_DOORBELL_WHOINIT_PCIPEER (0x3<<24) /* pci peer */ +#define MPII_DOORBELL_WHOINIT_DRIVER (0x4<<24) /* host driver */ +#define MPII_DOORBELL_WHOINIT_MANUFACT (0x5<<24) /* manufacturing */ +#define MPII_DOORBELL_FAULT (0xffff<<0) /* fault code */ +/* doorbell write bits */ +#define MPII_DOORBELL_FUNCTION_SHIFT 24 +#define MPII_DOORBELL_FUNCTION_MASK (0xff << MPII_DOORBELL_FUNCTION_SHIFT) +#define MPII_DOORBELL_FUNCTION(x) \ + (((x) << MPII_DOORBELL_FUNCTION_SHIFT) & MPII_DOORBELL_FUNCTION_MASK) +#define MPII_DOORBELL_DWORDS_SHIFT 16 +#define MPII_DOORBELL_DWORDS_MASK (0xff << MPII_DOORBELL_DWORDS_SHIFT) +#define MPII_DOORBELL_DWORDS(x) \ + (((x) << MPII_DOORBELL_DWORDS_SHIFT) & MPII_DOORBELL_DWORDS_MASK) +#define MPII_DOORBELL_DATA_MASK 0xffff + +#define MPII_WRITESEQ 0x04 +#define MPII_WRITESEQ_KEY_VALUE_MASK 0x0000000f /* key value */ +#define MPII_WRITESEQ_FLUSH 0x00 +#define MPII_WRITESEQ_1 0x0f +#define MPII_WRITESEQ_2 0x04 +#define MPII_WRITESEQ_3 0x0b +#define MPII_WRITESEQ_4 0x03 +#define MPII_WRITESEQ_5 0x07 +#define MPII_WRITESEQ_6 0x0d + +#define MPII_HOSTDIAG 0x08 +#define MPII_HOSTDIAG_BDS_MASK 0x00001800 /* boot device select */ +#define MPII_HOSTDIAG_BDS_DEFAULT (0<<11) /* default address map, flash */ +#define MPII_HOSTDIAG_BDS_HCDW (1<<11) /* host code and data window */ +#define MPII_HOSTDIAG_CLEARFBS (1<<10) /* clear flash bad sig */ +#define MPII_HOSTDIAG_FORCE_HCB_ONBOOT (1<<9) /* force host controlled boot */ +#define MPII_HOSTDIAG_HCB_MODE (1<<8) /* host controlled boot mode */ +#define MPII_HOSTDIAG_DWRE (1<<7) /* diag reg write enabled */ +#define MPII_HOSTDIAG_FBS (1<<6) /* flash bad sig */ +#define MPII_HOSTDIAG_RESET_HIST (1<<5) /* reset history */ +#define MPII_HOSTDIAG_DIAGWR_EN (1<<4) /* diagnostic write enabled */ +#define MPII_HOSTDIAG_RESET_ADAPTER (1<<2) /* reset adapter */ +#define MPII_HOSTDIAG_HOLD_IOC_RESET (1<<1) /* hold ioc in reset */ +#define MPII_HOSTDIAG_DIAGMEM_EN (1<<0) /* diag mem enable */ + +#define MPII_DIAGRWDATA 0x10 + +#define MPII_DIAGRWADDRLOW 0x14 + +#define MPII_DIAGRWADDRHIGH 0x18 + +#define MPII_INTR_STATUS 0x30 +#define MPII_INTR_STATUS_SYS2IOCDB (1<<31) /* ioc written to by host */ +#define MPII_INTR_STATUS_RESET (1<<30) /* physical ioc reset */ +#define MPII_INTR_STATUS_REPLY (1<<3) /* reply message interrupt */ +#define MPII_INTR_STATUS_IOC2SYSDB (1<<0) /* ioc write to doorbell */ + +#define MPII_INTR_MASK 0x34 +#define MPII_INTR_MASK_RESET (1<<30) /* ioc reset intr mask */ +#define MPII_INTR_MASK_REPLY (1<<3) /* reply message intr mask */ +#define MPII_INTR_MASK_DOORBELL (1<<0) /* doorbell interrupt mask */ + +#define MPII_DCR_DATA 0x38 + +#define MPII_DCR_ADDRESS 0x3C + +#define MPII_REPLY_FREE_HOST_INDEX 0x48 + +#define MPII_REPLY_POST_HOST_INDEX 0x6C + +#define MPII_HCB_SIZE 0x74 + +#define MPII_HCB_ADDRESS_LOW 0x78 +#define MPII_HCB_ADDRESS_HIGH 0x7C + +#define MPII_REQ_DESC_POST_LOW 0xC0 +#define MPII_REQ_DESC_POST_HIGH 0xC4 + +/* + * Scatter Gather Lists + */ + +#define MPII_SGE_FL_LAST (0x1<<31) /* last element in segment */ +#define MPII_SGE_FL_EOB (0x1<<30) /* last element of buffer */ +#define MPII_SGE_FL_TYPE (0x3<<28) /* element type */ + #define MPII_SGE_FL_TYPE_SIMPLE (0x1<<28) /* simple element */ + #define MPII_SGE_FL_TYPE_CHAIN (0x3<<28) /* chain element */ + #define MPII_SGE_FL_TYPE_XACTCTX (0x0<<28) /* transaction context */ +#define MPII_SGE_FL_LOCAL (0x1<<27) /* local address */ +#define MPII_SGE_FL_DIR (0x1<<26) /* direction */ + #define MPII_SGE_FL_DIR_OUT (0x1<<26) + #define MPII_SGE_FL_DIR_IN (0x0<<26) +#define MPII_SGE_FL_SIZE (0x1<<25) /* address size */ + #define MPII_SGE_FL_SIZE_32 (0x0<<25) + #define MPII_SGE_FL_SIZE_64 (0x1<<25) +#define MPII_SGE_FL_EOL (0x1<<24) /* end of list */ + +struct mpii_sge { + u_int32_t sg_hdr; + u_int32_t sg_lo_addr; + u_int32_t sg_hi_addr; +} __packed; + +struct mpii_fw_tce { + u_int8_t reserved1; + u_int8_t context_size; + u_int8_t details_length; + u_int8_t flags; + + u_int32_t reserved2; + + u_int32_t image_offset; + + u_int32_t image_size; +} __packed; + +/* + * Messages + */ + +/* functions */ +#define MPII_FUNCTION_SCSI_IO_REQUEST (0x00) +#define MPII_FUNCTION_SCSI_TASK_MGMT (0x01) +#define MPII_FUNCTION_IOC_INIT (0x02) +#define MPII_FUNCTION_IOC_FACTS (0x03) +#define MPII_FUNCTION_CONFIG (0x04) +#define MPII_FUNCTION_PORT_FACTS (0x05) +#define MPII_FUNCTION_PORT_ENABLE (0x06) +#define MPII_FUNCTION_EVENT_NOTIFICATION (0x07) +#define MPII_FUNCTION_EVENT_ACK (0x08) +#define MPII_FUNCTION_FW_DOWNLOAD (0x09) +#define MPII_FUNCTION_TARGET_CMD_BUFFER_POST (0x0A) +#define MPII_FUNCTION_TARGET_ASSIST (0x0B) +#define MPII_FUNCTION_TARGET_STATUS_SEND (0x0C) +#define MPII_FUNCTION_TARGET_MODE_ABORT (0x0D) +#define MPII_FUNCTION_FW_UPLOAD (0x12) + +#define MPII_FUNCTION_RAID_ACTION (0x15) +#define MPII_FUNCTION_RAID_SCSI_IO_PASSTHROUGH (0x16) + +#define MPII_FUNCTION_TOOLBOX (0x17) + +#define MPII_FUNCTION_SCSI_ENCLOSURE_PROCESSOR (0x18) + +#define MPII_FUNCTION_SMP_PASSTHROUGH (0x1A) +#define MPII_FUNCTION_SAS_IO_UNIT_CONTROL (0x1B) +#define MPII_FUNCTION_SATA_PASSTHROUGH (0x1C) + +#define MPII_FUNCTION_DIAG_BUFFER_POST (0x1D) +#define MPII_FUNCTION_DIAG_RELEASE (0x1E) + +#define MPII_FUNCTION_TARGET_CMD_BUF_BASE_POST (0x24) +#define MPII_FUNCTION_TARGET_CMD_BUF_LIST_POST (0x25) + +#define MPII_FUNCTION_IOC_MESSAGE_UNIT_RESET (0x40) +#define MPII_FUNCTION_IO_UNIT_RESET (0x41) +#define MPII_FUNCTION_HANDSHAKE (0x42) + +/* request flags fields for request descriptors */ +#define MPII_REQ_DESCRIPTOR_FLAGS_TYPE_SHIFT (0x01) +#define MPII_REQ_DESCRIPTOR_FLAGS_TYPE_MASK (0x07 << \ + MPII_REQ_DESCRIPTOR_FLAGS_TYPE_SHIFT) +#define MPII_REQ_DESCRIPTOR_FLAGS_SCSI_IO (0x00) +#define MPII_REQ_DESCRIPTOR_FLAGS_SCSI_TARGET (0x02) +#define MPII_REQ_DESCRIPTOR_FLAGS_HIGH_PRIORITY (0x06) +#define MPII_REQ_DESCRIPTOR_FLAGS_DEFAULT (0x08) + +#define MPII_REQ_DESCRIPTOR_FLAGS_IOC_FIFO_MARKER (0x01) + +/* reply flags */ +#define MPII_REP_FLAGS_CONT (1<<7) /* continuation reply */ + +#define MPII_REP_IOCSTATUS_AVAIL (1<<15) /* logging info available */ +#define MPII_REP_IOCSTATUS (0x7fff) /* status */ + +/* Common IOCStatus values for all replies */ +#define MPII_IOCSTATUS_SUCCESS (0x0000) +#define MPII_IOCSTATUS_INVALID_FUNCTION (0x0001) +#define MPII_IOCSTATUS_BUSY (0x0002) +#define MPII_IOCSTATUS_INVALID_SGL (0x0003) +#define MPII_IOCSTATUS_INTERNAL_ERROR (0x0004) +#define MPII_IOCSTATUS_INVALID_VPID (0x0005) +#define MPII_IOCSTATUS_INSUFFICIENT_RESOURCES (0x0006) +#define MPII_IOCSTATUS_INVALID_FIELD (0x0007) +#define MPII_IOCSTATUS_INVALID_STATE (0x0008) +#define MPII_IOCSTATUS_OP_STATE_NOT_SUPPORTED (0x0009) +/* Config IOCStatus values */ +#define MPII_IOCSTATUS_CONFIG_INVALID_ACTION (0x0020) +#define MPII_IOCSTATUS_CONFIG_INVALID_TYPE (0x0021) +#define MPII_IOCSTATUS_CONFIG_INVALID_PAGE (0x0022) +#define MPII_IOCSTATUS_CONFIG_INVALID_DATA (0x0023) +#define MPII_IOCSTATUS_CONFIG_NO_DEFAULTS (0x0024) +#define MPII_IOCSTATUS_CONFIG_CANT_COMMIT (0x0025) +/* SCSIIO Reply initiator values */ +#define MPII_IOCSTATUS_SCSI_RECOVERED_ERROR (0x0040) +#define MPII_IOCSTATUS_SCSI_INVALID_DEVHANDLE (0x0042) +#define MPII_IOCSTATUS_SCSI_DEVICE_NOT_THERE (0x0043) +#define MPII_IOCSTATUS_SCSI_DATA_OVERRUN (0x0044) +#define MPII_IOCSTATUS_SCSI_DATA_UNDERRUN (0x0045) +#define MPII_IOCSTATUS_SCSI_IO_DATA_ERROR (0x0046) +#define MPII_IOCSTATUS_SCSI_PROTOCOL_ERROR (0x0047) +#define MPII_IOCSTATUS_SCSI_TASK_TERMINATED (0x0048) +#define MPII_IOCSTATUS_SCSI_RESIDUAL_MISMATCH (0x0049) +#define MPII_IOCSTATUS_SCSI_TASK_MGMT_FAILED (0x004A) +#define MPII_IOCSTATUS_SCSI_IOC_TERMINATED (0x004B) +#define MPII_IOCSTATUS_SCSI_EXT_TERMINATED (0x004C) +/* For use by SCSI Initiator and SCSI Target end-to-end data protection */ +#define MPII_IOCSTATUS_EEDP_GUARD_ERROR (0x004D) +#define MPII_IOCSTATUS_EEDP_REF_TAG_ERROR (0x004E) +#define MPII_IOCSTATUS_EEDP_APP_TAG_ERROR (0x004F) +/* SCSI (SPI & FCP) target values */ +#define MPII_IOCSTATUS_TARGET_INVALID_IO_INDEX (0x0062) +#define MPII_IOCSTATUS_TARGET_ABORTED (0x0063) +#define MPII_IOCSTATUS_TARGET_NO_CONN_RETRYABLE (0x0064) +#define MPII_IOCSTATUS_TARGET_NO_CONNECTION (0x0065) +#define MPII_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH (0x006A) +#define MPII_IOCSTATUS_TARGET_DATA_OFFSET_ERROR (0x006D) +#define MPII_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA (0x006E) +#define MPII_IOCSTATUS_TARGET_IU_TOO_SHORT (0x006F) +#define MPII_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT (0x0070) +#define MPII_IOCSTATUS_TARGET_NAK_RECEIVED (0x0071) +/* Serial Attached SCSI values */ +#define MPII_IOCSTATUS_SAS_SMP_REQUEST_FAILED (0x0090) +#define MPII_IOCSTATUS_SAS_SMP_DATA_OVERRUN (0x0091) +/* Diagnostic Tools values */ +#define MPII_IOCSTATUS_DIAGNOSTIC_RELEASED (0x00A0) + +#define MPII_REP_IOCLOGINFO_TYPE (0xf<<28) /* logging info type */ +#define MPII_REP_IOCLOGINFO_TYPE_NONE (0x0<<28) +#define MPII_REP_IOCLOGINFO_TYPE_SCSI (0x1<<28) +#define MPII_REP_IOCLOGINFO_TYPE_FC (0x2<<28) +#define MPII_REP_IOCLOGINFO_TYPE_SAS (0x3<<28) +#define MPII_REP_IOCLOGINFO_TYPE_ISCSI (0x4<<28) +#define MPII_REP_IOCLOGINFO_DATA (0x0fffffff) /* logging info data */ + +/* event notification types */ +#define MPII_EVENT_NONE 0x00 +#define MPII_EVENT_LOG_DATA 0x01 +#define MPII_EVENT_STATE_CHANGE 0x02 +#define MPII_EVENT_UNIT_ATTENTION 0x03 +#define MPII_EVENT_IOC_BUS_RESET 0x04 +#define MPII_EVENT_EXT_BUS_RESET 0x05 +#define MPII_EVENT_RESCAN 0x06 +#define MPII_EVENT_LINK_STATUS_CHANGE 0x07 +#define MPII_EVENT_LOOP_STATE_CHANGE 0x08 +#define MPII_EVENT_LOGOUT 0x09 +#define MPII_EVENT_EVENT_CHANGE 0x0a +#define MPII_EVENT_INTEGRATED_RAID 0x0b +#define MPII_EVENT_SCSI_DEVICE_STATUS_CHANGE 0x0c +#define MPII_EVENT_ON_BUS_TIMER_EXPIRED 0x0d +#define MPII_EVENT_QUEUE_FULL 0x0e +#define MPII_EVENT_SAS_DEVICE_STATUS_CHANGE 0x0f +#define MPII_EVENT_SAS_SES 0x10 +#define MPII_EVENT_PERSISTENT_TABLE_FULL 0x11 +#define MPII_EVENT_SAS_PHY_LINK_STATUS 0x12 +#define MPII_EVENT_SAS_DISCOVERY_ERROR 0x13 +#define MPII_EVENT_IR_RESYNC_UPDATE 0x14 +#define MPII_EVENT_IR2 0x15 +#define MPII_EVENT_SAS_DISCOVERY 0x16 +#define MPII_EVENT_LOG_ENTRY_ADDED 0x21 + +/* messages */ + +#define MPII_WHOINIT_NOONE 0x00 +#define MPII_WHOINIT_SYSTEM_BIOS 0x01 +#define MPII_WHOINIT_ROM_BIOS 0x02 +#define MPII_WHOINIT_PCI_PEER 0x03 +#define MPII_WHOINIT_HOST_DRIVER 0x04 +#define MPII_WHOINIT_MANUFACTURER 0x05 + +/* page address fields */ +#define MPII_PAGE_ADDRESS_FC_BTID (1<<24) /* Bus Target ID */ + +/* default messages */ + +struct mpii_msg_request { + u_int8_t reserved1; + u_int8_t reserved2; + u_int8_t chain_offset; + u_int8_t function; + + u_int8_t reserved3; + u_int8_t reserved4; + u_int8_t reserved5; + u_int8_t msg_flags; + + u_int8_t vp_id; + u_int8_t vf_id; + u_int16_t reserved6; +} __packed; + +struct mpii_msg_reply { + u_int16_t reserved1; + u_int8_t msg_length; + u_int8_t function; + + u_int16_t reserved2; + u_int8_t reserved3; + u_int8_t msg_flags; + + u_int8_t vp_id; + u_int8_t vf_if; + u_int16_t reserved4; + + u_int16_t reserved5; + u_int16_t ioc_status; + + u_int32_t ioc_loginfo; +} __packed; + +/* ioc init */ + +struct mpii_msg_iocinit_request { + u_int8_t whoinit; + u_int8_t reserved1; + u_int8_t chain_offset; + u_int8_t function; + + u_int16_t reserved2; + u_int8_t reserved3; + u_int8_t msg_flags; + + u_int8_t vp_id; + u_int8_t vf_id; + u_int16_t reserved4; + + u_int8_t msg_version_min; + u_int8_t msg_version_maj; + u_int8_t hdr_version_unit; + u_int8_t hdr_version_dev; + + u_int32_t reserved5; + + u_int32_t reserved6; + + u_int16_t reserved7; + u_int16_t system_request_frame_size; + + u_int16_t reply_descriptor_post_queue_depth; + u_int16_t reply_free_queue_depth; + + u_int32_t sense_buffer_address_high; + + u_int32_t system_reply_address_high; + + u_int64_t system_request_frame_base_address; + + u_int64_t reply_descriptor_post_queue_address; + + u_int64_t reply_free_queue_address; + + u_int64_t timestamp; + +} __packed; + +struct mpii_msg_iocinit_reply { + u_int8_t whoinit; + u_int8_t reserved1; + u_int8_t msg_length; + u_int8_t function; + + u_int16_t reserved2; + u_int8_t reserved3; + u_int8_t msg_flags; + + u_int8_t vp_id; + u_int8_t vf_id; + u_int16_t reserved4; + + u_int16_t reserved5; + u_int16_t ioc_status; + + u_int32_t ioc_loginfo; +} __packed; + +struct mpii_msg_iocfacts_request { + u_int16_t reserved1; + u_int8_t chain_offset; + u_int8_t function; + + u_int16_t reserved2; + u_int8_t reserved3; + u_int8_t msg_flags; + + u_int8_t vp_id; + u_int8_t vf_id; + u_int16_t reserved4; +} __packed; + +struct mpii_msg_iocfacts_reply { + u_int8_t msg_version_min; + u_int8_t msg_version_maj; + u_int8_t msg_length; + u_int8_t function; + + u_int8_t header_version_dev; + u_int8_t header_version_unit; + u_int8_t ioc_number; + u_int8_t msg_flags; + + u_int8_t vp_id; + u_int8_t vf_id; + u_int16_t reserved1; + + u_int16_t ioc_exceptions; +#define MPII_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL (1<<0) +#define MPII_IOCFACTS_EXCEPT_RAID_CONFIG_INVALID (1<<1) +#define MPII_IOCFACTS_EXCEPT_FW_CHECKSUM_FAIL (1<<2) +#define MPII_IOCFACTS_EXCEPT_MANUFACT_CHECKSUM_FAIL (1<<3) +#define MPII_IOCFACTS_EXCEPT_METADATA_UNSUPPORTED (1<<4) +#define MPII_IOCFACTS_EXCEPT_IR_FOREIGN_CONFIG_MAC (1<<8) + /* XXX JPG BOOT_STATUS in bits[7:5] */ + /* XXX JPG all these #defines need to be fixed up */ + u_int16_t ioc_status; + + u_int32_t ioc_loginfo; + + u_int8_t max_chain_depth; + u_int8_t whoinit; + u_int8_t number_of_ports; + u_int8_t reserved2; + + u_int16_t request_credit; + u_int16_t product_id; + + u_int32_t ioc_capabilities; +#define MPII_IOCFACTS_CAPABILITY_EVENT_REPLAY (1<<13) +#define MPII_IOCFACTS_CAPABILITY_INTEGRATED_RAID (1<<12) +#define MPII_IOCFACTS_CAPABILITY_TLR (1<<11) +#define MPII_IOCFACTS_CAPABILITY_MULTICAST (1<<8) +#define MPII_IOCFACTS_CAPABILITY_BIDIRECTIONAL_TARGET (1<<7) +#define MPII_IOCFACTS_CAPABILITY_EEDP (1<<6) +#define MPII_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER (1<<4) +#define MPII_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER (1<<3) +#define MPII_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING (1<<2) + + u_int8_t fw_version_dev; + u_int8_t fw_version_unit; + u_int8_t fw_version_min; + u_int8_t fw_version_maj; + + u_int16_t ioc_request_frame_size; + u_int16_t reserved3; + + u_int16_t max_initiators; + u_int16_t max_targets; + + u_int16_t max_sas_expanders; + u_int16_t max_enclosures; + + u_int16_t protocol_flags; + u_int16_t high_priority_credit; + + u_int16_t max_reply_descriptor_post_queue_depth; + u_int8_t reply_frame_size; + u_int8_t max_volumes; + + u_int16_t max_dev_handle; + u_int16_t max_persistent_entries; + + u_int32_t reserved4; + +} __packed; + +struct mpii_msg_portfacts_request { + u_int16_t reserved1; + u_int8_t chain_offset; + u_int8_t function; + + u_int16_t reserved2; + u_int8_t port_number; + u_int8_t msg_flags; + + u_int8_t vp_id; + u_int8_t vf_id; + u_int16_t reserved3; +} __packed; + +struct mpii_msg_portfacts_reply { + u_int16_t reserved1; + u_int8_t msg_length; + u_int8_t function; + + u_int16_t reserved2; + u_int8_t port_number; + u_int8_t msg_flags; + + u_int8_t vp_id; + u_int8_t vf_id; + u_int16_t reserved3; + + u_int16_t reserved4; + u_int16_t ioc_status; + + u_int32_t ioc_loginfo; + + u_int8_t reserved5; + u_int8_t port_type; +#define MPII_PORTFACTS_PORTTYPE_INACTIVE 0x00 +#define MPII_PORTFACTS_PORTTYPE_FC 0x10 +#define MPII_PORTFACTS_PORTTYPE_ISCSI 0x20 +#define MPII_PORTFACTS_PORTTYPE_SAS_PHYSICAL 0x30 +#define MPII_PORTFACTS_PORTTYPE_SAS_VIRTUAL 0x31 + u_int16_t reserved6; + + u_int16_t max_posted_cmd_buffers; + u_int16_t reserved7; +} __packed; + +struct mpii_msg_portenable_request { + u_int16_t reserved1; + u_int8_t chain_offset; + u_int8_t function; + + u_int8_t reserved2; + u_int8_t port_flags; + u_int8_t reserved3; + u_int8_t msg_flags; + + u_int8_t vp_id; + u_int8_t vf_id; + u_int16_t reserved4; +} __packed; + +struct mpii_msg_portenable_reply { + u_int16_t reserved1; + u_int8_t msg_length; + u_int8_t function; + + u_int8_t reserved2; + u_int8_t port_flags; + u_int8_t reserved3; + u_int8_t msg_flags; + + u_int8_t vp_id; + u_int8_t vf_id; + u_int16_t reserved4; + + u_int16_t reserved5; + u_int16_t ioc_status; + + u_int32_t ioc_loginfo; +} __packed; + +struct mpii_msg_event_request { + u_int16_t reserved1; + u_int8_t chain_offset; + u_int8_t function; + + u_int16_t reserved2; + u_int8_t reserved3; + u_int8_t msg_flags; + + u_int8_t vp_id; + u_int8_t vf_id; + u_int16_t reserved4; + + u_int32_t reserved5; + + u_int32_t reserved6; + + u_int32_t event_masks[4]; + + u_int16_t sas_broadcase_primitive_masks; + u_int16_t reserved7; + + u_int32_t reserved8; +} __packed; + +struct mpii_msg_event_reply { + u_int16_t event_data_length; + u_int8_t msg_length; + u_int8_t function; + + u_int16_t reserved1; + u_int8_t ack_required; +#define MPII_EVENT_ACK_REQUIRED (0x01) + u_int8_t msg_flags; +#define MPII_EVENT_FLAGS_REPLY_KEPT (1<<7) + + u_int8_t vp_id; + u_int8_t vf_id; + u_int16_t reserved2; + + u_int16_t reserved3; + u_int16_t ioc_status; + + u_int32_t ioc_loginfo; + + u_int16_t event; + u_int16_t reserved4; + + u_int32_t event_context; + + /* event data follows */ +} __packed; + +/* XXX JPG */ +struct mpii_evt_change { + u_int8_t event_state; + u_int8_t reserved[3]; +} __packed; + +/* XXX JPG */ +struct mpii_evt_sas_phy { + u_int8_t phy_num; + u_int8_t link_rates; +#define MPII_EVT_SASPHY_LINK_CUR(x) (((x) & 0xf0) >> 4) +#define MPII_EVT_SASPHY_LINK_PREV(x) ((x) & 0x0f) +#define MPII_EVT_SASPHY_LINK_ENABLED 0x0 +#define MPII_EVT_SASPHY_LINK_DISABLED 0x1 +#define MPII_EVT_SASPHY_LINK_NEGFAIL 0x2 +#define MPII_EVT_SASPHY_LINK_SATAOOB 0x3 +#define MPII_EVT_SASPHY_LINK_1_5GBPS 0x8 +#define MPII_EVT_SASPHY_LINK_3_0GBPS 0x9 + u_int16_t dev_handle; + + u_int64_t sas_addr; +} __packed; + +struct mpii_evt_sas_change { + u_int16_t task_tag; + u_int8_t reason; +#define MPII_EVT_SASCH_REASON_SMART_DATA 0x05 +#define MPII_EVT_SASCH_REASON_UNSUPPORTED 0x07 +#define MPII_EVT_SASCH_REASON_INTERNAL_RESET 0x08 +#define MPII_EVT_SASCH_REASON_TASK_ABORT_INTERVAL 0x09 +#define MPII_EVT_SASCH_REASON_ABORT_TASK_SET_INTERVAL 0x0A +#define MPII_EVT_SASCH_REASON_CLEAR_TASK_SET_INTERVAL 0x0B +#define MPII_EVT_SASCH_REASON_QUERY_TASK_INTERVAL 0x0C +#define MPII_EVT_SASCH_REASON_ASYNC_NOTIFICATION 0x0D +#define MPII_EVT_SASCH_REASON_CMP_INTERNAL_DEV_RESET 0x0E +#define MPII_EVT_SASCH_REASON_CMP_TASK_ABORT_INTERNAL 0x0F +#define MPII_EVT_SASCH_REASON_SATA_INIT_FAILURE 0x10 + u_int8_t reserved1; + + u_int8_t asc; + u_int8_t ascq; + u_int16_t dev_handle; + + u_int32_t reserved2; + + u_int64_t sas_addr; + + u_int16_t lun[4]; +} __packed; + +struct mpii_msg_eventack_request { + u_int16_t reserved1; + u_int8_t chain_offset; + u_int8_t function; + + u_int8_t reserved2[3]; + u_int8_t msg_flags; + + u_int8_t vp_id; + u_int8_t vf_id; + u_int16_t reserved3; + + u_int16_t event; + u_int16_t reserved4; + + u_int32_t event_context; +} __packed; + +struct mpii_msg_eventack_reply { + u_int16_t reserved1; + u_int8_t msg_length; + u_int8_t function; + + u_int8_t reserved2[3]; + u_int8_t msg_flags; + + u_int8_t vp_id; + u_int8_t vf_id; + u_int16_t reserved3; + + u_int16_t reserved4; + u_int16_t ioc_status; + + u_int32_t ioc_loginfo; +} __packed; + +struct mpii_msg_fwupload_request { + u_int8_t image_type; +#define MPII_FWUPLOAD_IMAGETYPE_IOC_FW (0x00) +#define MPII_FWUPLOAD_IMAGETYPE_NV_FW (0x01) +#define MPII_FWUPLOAD_IMAGETYPE_NV_BACKUP (0x05) +#define MPII_FWUPLOAD_IMAGETYPE_NV_MANUFACTURING (0x06) +#define MPII_FWUPLOAD_IMAGETYPE_NV_CONFIG_1 (0x07) +#define MPII_FWUPLOAD_IMAGETYPE_NV_CONFIG_2 (0x08) +#define MPII_FWUPLOAD_IMAGETYPE_NV_MEGARAID (0x09) +#define MPII_FWUPLOAD_IMAGETYPE_NV_COMPLETE (0x0A) +#define MPII_FWUPLOAD_IMAGETYPE_COMMON_BOOT_BLOCK (0x0B) + u_int8_t reserved1; + u_int8_t chain_offset; + u_int8_t function; + + u_int8_t reserved2[3]; + u_int8_t msg_flags; + + u_int8_t vp_id; + u_int8_t vf_id; + u_int16_t reserved3; + + u_int32_t reserved4; + + u_int32_t reserved5; + + struct mpii_fw_tce tce; + + /* followed by an sgl */ +} __packed; + +struct mpii_msg_fwupload_reply { + u_int8_t image_type; + u_int8_t reserved1; + u_int8_t msg_length; + u_int8_t function; + + u_int8_t reserved2[3]; + u_int8_t msg_flags; + + u_int8_t vp_id; + u_int8_t vf_id; + u_int16_t reserved3; + + u_int16_t reserved4; + u_int16_t ioc_status; + + u_int32_t ioc_loginfo; + + u_int32_t actual_image_size; +} __packed; + +struct mpii_msg_scsi_io { + u_int16_t dev_handle; + u_int8_t chain_offset; + u_int8_t function; + + u_int16_t reserved1; + u_int8_t reserved2; + u_int8_t msg_flags; + + u_int8_t vp_id; + u_int8_t vf_id; + u_int16_t reserved3; + + u_int32_t sense_buffer_low_address; + + u_int16_t sgl_flags; + u_int8_t sense_buffer_length; + u_int8_t reserved4; + + u_int8_t sgl_offset0; + u_int8_t sgl_offset1; + u_int8_t sgl_offset2; + u_int8_t sgl_offset3; + + u_int32_t skip_count; + + u_int32_t data_length; + + u_int32_t bidirectional_data_length; + + u_int16_t io_flags; + u_int16_t eedp_flags; + + u_int32_t eedp_block_size; + + u_int32_t secondary_reference_tag; + + u_int16_t secondary_application_tag; + u_int16_t application_tag_translation_mask; + + u_int16_t lun[4]; + +/* the following 16 bits are defined in MPI2 as the control field */ + u_int8_t reserved5; + u_int8_t tagging; +#define MPII_SCSIIO_ATTR_SIMPLE_Q (0x0) +#define MPII_SCSIIO_ATTR_HEAD_OF_Q (0x1) +#define MPII_SCSIIO_ATTR_ORDERED_Q (0x2) +#define MPII_SCSIIO_ATTR_ACA_Q (0x4) +#define MPII_SCSIIO_ATTR_UNTAGGED (0x5) +#define MPII_SCSIIO_ATTR_NO_DISCONNECT (0x7) + u_int8_t reserved6; + u_int8_t direction; +#define MPII_SCSIIO_DIR_NONE (0x0) +#define MPII_SCSIIO_DIR_WRITE (0x1) +#define MPII_SCSIIO_DIR_READ (0x2) + +#define MPII_CDB_LEN 32 + u_int8_t cdb[MPII_CDB_LEN]; + + /* followed by an sgl */ +} __packed; + +struct mpii_msg_scsi_io_error { + u_int16_t dev_handle; + u_int8_t msg_length; + u_int8_t function; + + u_int16_t reserved1; + u_int8_t reserved2; + u_int8_t msg_flags; + + u_int8_t vp_id; + u_int8_t vf_id; + u_int16_t reserved3; + + u_int8_t scsi_status; + /* XXX JPG validate this */ +#if notyet +#define MPII_SCSIIO_ERR_STATUS_SUCCESS +#define MPII_SCSIIO_ERR_STATUS_CHECK_COND +#define MPII_SCSIIO_ERR_STATUS_BUSY +#define MPII_SCSIIO_ERR_STATUS_INTERMEDIATE +#define MPII_SCSIIO_ERR_STATUS_INTERMEDIATE_CONDMET +#define MPII_SCSIIO_ERR_STATUS_RESERVATION_CONFLICT +#define MPII_SCSIIO_ERR_STATUS_CMD_TERM +#define MPII_SCSIIO_ERR_STATUS_TASK_SET_FULL +#define MPII_SCSIIO_ERR_STATUS_ACA_ACTIVE +#endif + u_int8_t scsi_state; +#define MPII_SCSIIO_ERR_STATE_AUTOSENSE_VALID (1<<0) +#define MPII_SCSIIO_ERR_STATE_AUTOSENSE_FAILED (1<<1) +#define MPII_SCSIIO_ERR_STATE_NO_SCSI_STATUS (1<<2) +#define MPII_SCSIIO_ERR_STATE_TERMINATED (1<<3) +#define MPII_SCSIIO_ERR_STATE_RESPONSE_INFO_VALID (1<<4) +#define MPII_SCSIIO_ERR_STATE_QUEUE_TAG_REJECTED (0xffff) + u_int16_t ioc_status; + + u_int32_t ioc_loginfo; + + u_int32_t transfer_count; + + u_int32_t sense_count; + + u_int32_t response_info; + + u_int16_t task_tag; + u_int16_t reserved4; + + u_int32_t bidirectional_transfer_count; + + u_int32_t reserved5; + + u_int32_t reserved6; +} __packed; + +struct mpii_request_descriptor { + u_int8_t request_flags; + u_int8_t vf_id; + u_int16_t smid; + + u_int16_t lmid; + /* + * the following field is descriptor type dependent + * default - undefined + * scsi io - device handle + * high priority - reserved + */ + u_int16_t type_dependent; +} __packed; + +struct mpii_reply_descriptor { + u_int8_t reply_flags; +#define MPII_REPLY_DESCR_FLAGS_TYPE_MASK (0x0F) +#define MPII_REPLY_DESCR_FLAGS_SCSI_IO_SUCCESS (0x00) +#define MPII_REPLY_DESCR_FLAGS_ADDRESS_REPLY (0x01) +#define MPII_REPLY_DESCR_FLAGS_TARGETASSIST_SUCCESS (0x02) +#define MPII_REPLY_DESCR_FLAGS_TARGET_COMMAND_BUFFER (0x03) +#define MPII_REPLY_DESCR_FLAGS_UNUSED (0x0F) + u_int8_t vf_id; + /* + * the following field is reply descriptor type dependent + * default - undefined + * scsi io success - smid + * address reply - smid + */ + u_int16_t type_dependent1; + + /* + * the following field is reply descriptor type dependent + * default - undefined + * scsi io success - bottom 16 bits is task tag + * address reply - reply frame address + */ + u_int32_t type_dependent2; +} __packed; + +struct mpii_request_header { + u_int16_t function_dependent1; + u_int8_t chain_offset; + u_int8_t function; + + u_int16_t function_dependent2; + u_int8_t function_dependent3; + u_int8_t message_flags; + + u_int8_t vp_id; + u_int8_t vf_id; + u_int16_t reserved; +} __packed; + +/* XXX JPG delete this? */ +struct mpii_msg_scsi_task_request { + u_int8_t target_id; + u_int8_t bus; + u_int8_t chain_offset; + u_int8_t function; + + u_int8_t reserved1; + u_int8_t task_type; +#define MPII_MSG_SCSI_TASK_TYPE_ABORT_TASK (0x01) +#define MPII_MSG_SCSI_TASK_TYPE_ABRT_TASK_SET (0x02) +#define MPII_MSG_SCSI_TASK_TYPE_TARGET_RESET (0x03) +#define MPII_MSG_SCSI_TASK_TYPE_RESET_BUS (0x04) +#define MPII_MSG_SCSI_TASK_TYPE_LOGICAL_UNIT_RESET (0x05) + u_int8_t reserved2; + u_int8_t msg_flags; + + u_int32_t msg_context; + + u_int16_t lun[4]; + + u_int32_t reserved3[7]; + + u_int32_t target_msg_context; +} __packed; + +/* XXX JPG delete this? */ +struct mpii_msg_scsi_task_reply { + u_int8_t target_id; + u_int8_t bus; + u_int8_t msg_length; + u_int8_t function; + + u_int8_t response_code; + u_int8_t task_type; + u_int8_t reserved1; + u_int8_t msg_flags; + + u_int32_t msg_context; + + u_int16_t reserved2; + u_int16_t ioc_status; + + u_int32_t ioc_loginfo; + + u_int32_t termination_count; +} __packed; + +struct mpii_cfg_hdr { + u_int8_t page_version; + u_int8_t page_length; + u_int8_t page_number; + u_int8_t page_type; +#define MPII_CONFIG_REQ_PAGE_TYPE_ATTRIBUTE (0xf0) +#define MPI2_CONFIG_PAGEATTR_READ_ONLY (0x00) +#define MPI2_CONFIG_PAGEATTR_CHANGEABLE (0x10) +#define MPI2_CONFIG_PAGEATTR_PERSISTENT (0x20) + +#define MPII_CONFIG_REQ_PAGE_TYPE_MASK (0x0f) +#define MPII_CONFIG_REQ_PAGE_TYPE_IO_UNIT (0x00) +#define MPII_CONFIG_REQ_PAGE_TYPE_IOC (0x01) +#define MPII_CONFIG_REQ_PAGE_TYPE_BIOS (0x02) +#define MPII_CONFIG_REQ_PAGE_TYPE_RAID_VOL (0x08) +#define MPII_CONFIG_REQ_PAGE_TYPE_MANUFACTURING (0x09) +#define MPII_CONFIG_REQ_PAGE_TYPE_RAID_PD (0x0A) +#define MPII_CONFIG_REQ_PAGE_TYPE_EXTENDED (0x0F) +} __packed; + +struct mpii_ecfg_hdr { + u_int8_t page_version; + u_int8_t reserved1; + u_int8_t page_number; + u_int8_t page_type; + + u_int16_t ext_page_length; + u_int8_t ext_page_type; + u_int8_t reserved2; +} __packed; + +struct mpii_msg_config_request { + u_int8_t action; +#define MPII_CONFIG_REQ_ACTION_PAGE_HEADER (0x00) +#define MPII_CONFIG_REQ_ACTION_PAGE_READ_CURRENT (0x01) +#define MPII_CONFIG_REQ_ACTION_PAGE_WRITE_CURRENT (0x02) +#define MPII_CONFIG_REQ_ACTION_PAGE_DEFAULT (0x03) +#define MPII_CONFIG_REQ_ACTION_PAGE_WRITE_NVRAM (0x04) +#define MPII_CONFIG_REQ_ACTION_PAGE_READ_DEFAULT (0x05) +#define MPII_CONFIG_REQ_ACTION_PAGE_READ_NVRAM (0x06) + u_int8_t sgl_flags; + u_int8_t chain_offset; + u_int8_t function; + + u_int16_t ext_page_len; + u_int8_t ext_page_type; +#define MPII_CONFIG_REQ_EXTPAGE_TYPE_SAS_IO_UNIT (0x10) +#define MPII_CONFIG_REQ_EXTPAGE_TYPE_SAS_EXPANDER (0x11) +#define MPII_CONFIG_REQ_EXTPAGE_TYPE_SAS_DEVICE (0x12) +#define MPII_CONFIG_REQ_EXTPAGE_TYPE_SAS_PHY (0x13) +#define MPII_CONFIG_REQ_EXTPAGE_TYPE_LOG (0x14) +#define MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE (0x15) +#define MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG (0x16) +#define MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING (0x17) +#define MPI2_CONFIG_EXTPAGETYPE_SAS_PORT (0x18) + u_int8_t msg_flags; + + u_int8_t vp_id; + u_int8_t vf_id; + u_int16_t reserved1; + + u_int32_t reserved2[2]; + + struct mpii_cfg_hdr config_header; + + u_int32_t page_address; +/* XXX lots of defns here */ + + struct mpii_sge page_buffer; +} __packed; + +struct mpii_msg_config_reply { + u_int8_t action; + u_int8_t sgl_flags; + u_int8_t msg_length; + u_int8_t function; + + u_int16_t ext_page_length; + u_int8_t ext_page_type; + u_int8_t msg_flags; + + u_int8_t vp_id; + u_int8_t vf_id; + u_int16_t reserved1; + + u_int16_t reserved2; + u_int16_t ioc_status; + + u_int32_t ioc_loginfo; + + struct mpii_cfg_hdr config_header; +} __packed; + +struct mpii_cfg_manufacturing_pg0 { + struct mpii_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]; +} __packed; + +struct mpii_cfg_ioc_pg1 { + struct mpii_cfg_hdr config_header; + + u_int32_t flags; + + u_int32_t coalescing_timeout; +#define MPII_CFG_IOC_1_REPLY_COALESCING (1<<0) + + u_int8_t coalescing_depth; + u_int8_t pci_slot_num; + u_int8_t pci_bus_num; + u_int8_t pci_domain_segment; + + u_int32_t reserved1; + + u_int32_t reserved2; +} __packed; + +/* +struct mpii_cfg_raid_vol_pg0 { + u_int8_t vol_id; + u_int8_t vol_bus; + u_int8_t vol_ioc; + u_int8_t vol_page; + + u_int8_t vol_type; +#define MPII_CFG_RAID_TYPE_RAID_IS (0x00) +#define MPII_CFG_RAID_TYPE_RAID_IME (0x01) +#define MPII_CFG_RAID_TYPE_RAID_IM (0x02) +#define MPII_CFG_RAID_TYPE_RAID_5 (0x03) +#define MPII_CFG_RAID_TYPE_RAID_6 (0x04) +#define MPII_CFG_RAID_TYPE_RAID_10 (0x05) +#define MPII_CFG_RAID_TYPE_RAID_50 (0x06) + u_int8_t flags; +#define MPII_CFG_RAID_VOL_INACTIVE (1<<3) + u_int16_t reserved; +} __packed; +*/ +struct mpii_cfg_ioc_pg3 { + struct mpii_cfg_hdr config_header; + + u_int8_t no_phys_disks; + u_int8_t reserved[3]; + + /* followed by a list of mpii_cfg_raid_physdisk structs */ +} __packed; + +struct mpii_cfg_ioc_pg8 { + struct mpii_cfg_hdr config_header; + + u_int8_t num_devs_per_enclosure; + u_int8_t reserved1; + u_int16_t reserved2; + + u_int16_t max_persistent_entries; + u_int16_t max_num_physical_mapped_ids; + + u_int16_t flags; + u_int16_t reserved3; + + u_int16_t ir_volume_mapping_flags; + u_int16_t reserved4; + + u_int32_t reserved5; +} __packed; + +struct mpii_cfg_raid_physdisk { + u_int8_t phys_disk_id; + u_int8_t phys_disk_bus; + u_int8_t phys_disk_ioc; + u_int8_t phys_disk_num; +} __packed; + +struct mpii_cfg_fc_port_pg0 { + struct mpii_cfg_hdr config_header; + + u_int32_t flags; + + u_int8_t mpii_port_nr; + u_int8_t link_type; + u_int8_t port_state; + u_int8_t reserved1; + + u_int32_t port_id; + + u_int64_t wwnn; + + u_int64_t wwpn; + + u_int32_t supported_service_class; + + u_int32_t supported_speeds; + + u_int32_t current_speed; + + u_int32_t max_frame_size; + + u_int64_t fabric_wwnn; + + u_int64_t fabric_wwpn; + + u_int32_t discovered_port_count; + + u_int32_t max_initiators; + + u_int8_t max_aliases_supported; + u_int8_t max_hard_aliases_supported; + u_int8_t num_current_aliases; + u_int8_t reserved2; +} __packed; + +struct mpii_cfg_fc_port_pg1 { + struct mpii_cfg_hdr config_header; + + u_int32_t flags; + + u_int64_t noseepromwwnn; + + u_int64_t noseepromwwpn; + + u_int8_t hard_alpa; + u_int8_t link_config; + u_int8_t topology_config; + u_int8_t alt_connector; + + u_int8_t num_req_aliases; + u_int8_t rr_tov; + u_int8_t initiator_dev_to; + u_int8_t initiator_lo_pend_to; +} __packed; + +struct mpii_cfg_fc_device_pg0 { + struct mpii_cfg_hdr config_header; + + u_int64_t wwnn; + + u_int64_t wwpn; + + u_int32_t port_id; + + u_int8_t protocol; + u_int8_t flags; + u_int16_t bb_credit; + + u_int16_t max_rx_frame_size; + u_int8_t adisc_hard_alpa; + u_int8_t port_nr; + + u_int8_t fc_ph_low_version; + u_int8_t fc_ph_high_version; + u_int8_t current_target_id; + u_int8_t current_bus; +} __packed; + +struct mpii_cfg_raid_vol_pg0 { + struct mpii_cfg_hdr config_header; + + u_int8_t volume_id; + u_int8_t volume_bus; + u_int8_t volume_ioc; + u_int8_t volume_type; + + u_int8_t volume_status; +#define MPII_CFG_RAID_VOL_0_STATUS_ENABLED (1<<0) +#define MPII_CFG_RAID_VOL_0_STATUS_QUIESCED (1<<1) +#define MPII_CFG_RAID_VOL_0_STATUS_RESYNCING (1<<2) +#define MPII_CFG_RAID_VOL_0_STATUS_ACTIVE (1<<3) +#define MPII_CFG_RAID_VOL_0_STATUS_BADBLOCK_FULL (1<<4) + u_int8_t volume_state; +#define MPII_CFG_RAID_VOL_0_STATE_OPTIMAL (0x00) +#define MPII_CFG_RAID_VOL_0_STATE_DEGRADED (0x01) +#define MPII_CFG_RAID_VOL_0_STATE_FAILED (0x02) +#define MPII_CFG_RAID_VOL_0_STATE_MISSING (0x03) + u_int16_t reserved1; + + u_int16_t volume_settings; +#define MPII_CFG_RAID_VOL_0_SETTINGS_WRITE_CACHE_EN (1<<0) +#define MPII_CFG_RAID_VOL_0_SETTINGS_OFFLINE_SMART_ERR (1<<1) +#define MPII_CFG_RAID_VOL_0_SETTINGS_OFFLINE_SMART (1<<2) +#define MPII_CFG_RAID_VOL_0_SETTINGS_AUTO_SWAP (1<<3) +#define MPII_CFG_RAID_VOL_0_SETTINGS_HI_PRI_RESYNC (1<<4) +#define MPII_CFG_RAID_VOL_0_SETTINGS_PROD_SUFFIX (1<<5) +#define MPII_CFG_RAID_VOL_0_SETTINGS_FAST_SCRUB (1<<6) /* obsolete */ +#define MPII_CFG_RAID_VOL_0_SETTINGS_DEFAULTS (1<<15) + u_int8_t hot_spare_pool; + u_int8_t reserved2; + + u_int32_t max_lba; + + u_int32_t reserved3; + + u_int32_t stripe_size; + + u_int32_t reserved4; + + u_int32_t reserved5; + + u_int8_t num_phys_disks; + u_int8_t data_scrub_rate; + u_int8_t resync_rate; + u_int8_t inactive_status; +#define MPII_CFG_RAID_VOL_0_INACTIVE_UNKNOWN (0x00) +#define MPII_CFG_RAID_VOL_0_INACTIVE_STALE_META (0x01) +#define MPII_CFG_RAID_VOL_0_INACTIVE_FOREIGN_VOL (0x02) +#define MPII_CFG_RAID_VOL_0_INACTIVE_NO_RESOURCES (0x03) +#define MPII_CFG_RAID_VOL_0_INACTIVE_CLONED_VOL (0x04) +#define MPII_CFG_RAID_VOL_0_INACTIVE_INSUF_META (0x05) + + /* followed by a list of mpii_cfg_raid_vol_pg0_physdisk structs */ +} __packed; + +struct mpii_cfg_raid_vol_pg0_physdisk { + u_int16_t reserved; + u_int8_t phys_disk_map; + u_int8_t phys_disk_num; +} __packed; + +struct mpii_cfg_raid_vol_pg1 { + struct mpii_cfg_hdr config_header; + + u_int8_t volume_id; + u_int8_t volume_bus; + u_int8_t volume_ioc; + u_int8_t reserved1; + + u_int8_t guid[24]; + + u_int8_t name[32]; + + u_int64_t wwid; + + u_int32_t reserved2; + + u_int32_t reserved3; +} __packed; + +struct mpii_cfg_raid_physdisk_pg0 { + struct mpii_cfg_hdr config_header; + + u_int8_t phys_disk_id; + u_int8_t phys_disk_bus; + u_int8_t phys_disk_ioc; + u_int8_t phys_disk_num; + + u_int8_t enc_id; + u_int8_t enc_bus; + u_int8_t hot_spare_pool; + u_int8_t enc_type; +#define MPII_CFG_RAID_PHYDISK_0_ENCTYPE_NONE (0x0) +#define MPII_CFG_RAID_PHYDISK_0_ENCTYPE_SAFTE (0x1) +#define MPII_CFG_RAID_PHYDISK_0_ENCTYPE_SES (0x2) + + u_int32_t reserved1; + + u_int8_t ext_disk_id[8]; + + u_int8_t disk_id[16]; + + u_int8_t vendor_id[8]; + + u_int8_t product_id[16]; + + u_int8_t product_rev[4]; + + u_int8_t info[32]; + + u_int8_t phys_disk_status; +#define MPII_CFG_RAID_PHYDISK_0_STATUS_OUTOFSYNC (1<<0) +#define MPII_CFG_RAID_PHYDISK_0_STATUS_QUIESCED (1<<1) + u_int8_t phys_disk_state; +#define MPII_CFG_RAID_PHYDISK_0_STATE_ONLINE (0x00) +#define MPII_CFG_RAID_PHYDISK_0_STATE_MISSING (0x01) +#define MPII_CFG_RAID_PHYDISK_0_STATE_INCOMPAT (0x02) +#define MPII_CFG_RAID_PHYDISK_0_STATE_FAILED (0x03) +#define MPII_CFG_RAID_PHYDISK_0_STATE_INIT (0x04) +#define MPII_CFG_RAID_PHYDISK_0_STATE_OFFLINE (0x05) +#define MPII_CFG_RAID_PHYDISK_0_STATE_HOSTFAIL (0x06) +#define MPII_CFG_RAID_PHYDISK_0_STATE_OTHER (0xff) + u_int16_t reserved2; + + u_int32_t max_lba; + + u_int8_t error_cdb_byte; + u_int8_t error_sense_key; + u_int16_t reserved3; + + u_int16_t error_count; + u_int8_t error_asc; + u_int8_t error_ascq; + + u_int16_t smart_count; + u_int8_t smart_asc; + u_int8_t smart_ascq; +} __packed; + +struct mpii_cfg_raid_physdisk_pg1 { + struct mpii_cfg_hdr config_header; + + u_int8_t num_phys_disk_paths; + u_int8_t phys_disk_num; + u_int16_t reserved1; + + u_int32_t reserved2; + + /* followed by mpii_cfg_raid_physdisk_path structs */ +} __packed; + +struct mpii_cfg_raid_physdisk_path { + u_int8_t phys_disk_id; + u_int8_t phys_disk_bus; + u_int16_t reserved1; + + u_int64_t wwwid; + + u_int64_t owner_wwid; + + u_int8_t ownder_id; + u_int8_t reserved2; + u_int16_t flags; +#define MPII_CFG_RAID_PHYDISK_PATH_INVALID (1<<0) +#define MPII_CFG_RAID_PHYDISK_PATH_BROKEN (1<<1) +} __packed; + +#define MPII_CFG_SAS_DEV_ADDR_NEXT (0<<28) +#define MPII_CFG_SAS_DEV_ADDR_BUS (1<<28) +#define MPII_CFG_SAS_DEV_ADDR_HANDLE (2<<28) + +struct mpii_cfg_sas_dev_pg0 { + struct mpii_ecfg_hdr config_header; + + u_int16_t slot; + u_int16_t enc_handle; + + u_int64_t sas_addr; + + u_int16_t parent_dev_handle; + u_int8_t phy_num; + u_int8_t access_status; + + u_int16_t dev_handle; + u_int8_t target; + u_int8_t bus; + + u_int32_t device_info; +#define MPII_CFG_SAS_DEV_0_DEVINFO_TYPE (0x7) +#define MPII_CFG_SAS_DEV_0_DEVINFO_TYPE_NONE (0x0) +#define MPII_CFG_SAS_DEV_0_DEVINFO_TYPE_END (0x1) +#define MPII_CFG_SAS_DEV_0_DEVINFO_TYPE_EDGE_EXPANDER (0x2) +#define MPII_CFG_SAS_DEV_0_DEVINFO_TYPE_FANOUT_EXPANDER (0x3) +#define MPII_CFG_SAS_DEV_0_DEVINFO_SATA_HOST (1<<3) +#define MPII_CFG_SAS_DEV_0_DEVINFO_SMP_INITIATOR (1<<4) +#define MPII_CFG_SAS_DEV_0_DEVINFO_STP_INITIATOR (1<<5) +#define MPII_CFG_SAS_DEV_0_DEVINFO_SSP_INITIATOR (1<<6) +#define MPII_CFG_SAS_DEV_0_DEVINFO_SATA_DEVICE (1<<7) +#define MPII_CFG_SAS_DEV_0_DEVINFO_SMP_TARGET (1<<8) +#define MPII_CFG_SAS_DEV_0_DEVINFO_STP_TARGET (1<<9) +#define MPII_CFG_SAS_DEV_0_DEVINFO_SSP_TARGET (1<<10) +#define MPII_CFG_SAS_DEV_0_DEVINFO_DIRECT_ATTACHED (1<<11) +#define MPII_CFG_SAS_DEV_0_DEVINFO_LSI_DEVICE (1<<12) +#define MPII_CFG_SAS_DEV_0_DEVINFO_ATAPI_DEVICE (1<<13) +#define MPII_CFG_SAS_DEV_0_DEVINFO_SEP_DEVICE (1<<14) + + u_int16_t flags; +#define MPII_CFG_SAS_DEV_0_FLAGS_DEV_PRESENT (1<<0) +#define MPII_CFG_SAS_DEV_0_FLAGS_DEV_MAPPED (1<<1) +#define MPII_CFG_SAS_DEV_0_FLAGS_DEV_MAPPED_PERSISTENT (1<<2) +#define MPII_CFG_SAS_DEV_0_FLAGS_SATA_PORT_SELECTOR (1<<3) +#define MPII_CFG_SAS_DEV_0_FLAGS_SATA_FUA (1<<4) +#define MPII_CFG_SAS_DEV_0_FLAGS_SATA_NCQ (1<<5) +#define MPII_CFG_SAS_DEV_0_FLAGS_SATA_SMART (1<<6) +#define MPII_CFG_SAS_DEV_0_FLAGS_SATA_LBA48 (1<<7) +#define MPII_CFG_SAS_DEV_0_FLAGS_UNSUPPORTED (1<<8) +#define MPII_CFG_SAS_DEV_0_FLAGS_SATA_SETTINGS (1<<9) + u_int8_t physical_port; + u_int8_t reserved; +} __packed; + +struct mpii_cfg_raid_config_pg0 { + struct mpii_ecfg_hdr config_header; + + u_int8_t num_hot_spares; + u_int8_t num_phys_disks; + u_int8_t num_volumes; + u_int8_t config_num; + + u_int32_t flags; +#define MPII_CFG_RAID_CONFIG_0_FLAGS_NATIVE (0<<0) +#define MPII_CFG_RAID_CONFIG_0_FLAGS_FOREIGN (1<<0) + + u_int32_t config_guid[6]; + + u_int32_t reserved1; + + u_int8_t num_elements; + u_int8_t reserved2[3]; + + /* followed by struct mpii_raid_config_element structs */ +} __packed; + +struct mpii_raid_config_element { + u_int16_t element_flags; +#define MPII_RAID_CONFIG_ELEMENT_FLAG_VOLUME (0x0) +#define MPII_RAID_CONFIG_ELEMENT_FLAG_VOLUME_PHYS_DISK (0x1) +#define MPII_RAID_CONFIG_ELEMENT_FLAG_HSP_PHYS_DISK (0x2) +#define MPII_RAID_CONFIG_ELEMENT_ONLINE_CE_PHYS_DISK (0x3) + u_int16_t vol_dev_handle; + + u_int8_t hot_spare_pool; + u_int8_t phys_disk_num; + u_int16_t phys_disk_dev_handle; +} __packed; + +/*#define MPII_DEBUG*/ +#ifdef MPII_DEBUG +#define DPRINTF(x...) do { if (mpii_debug) printf(x); } while(0) +#define DNPRINTF(n,x...) do { if (mpii_debug & (n)) printf(x); } while(0) +#define MPII_D_CMD 0x0001 +#define MPII_D_INTR 0x0002 +#define MPII_D_MISC 0x0004 +#define MPII_D_DMA 0x0008 +#define MPII_D_IOCTL 0x0010 +#define MPII_D_RW 0x0020 +#define MPII_D_MEM 0x0040 +#define MPII_D_CCB 0x0080 +#define MPII_D_PPR 0x0100 +#define MPII_D_RAID 0x0200 +#define MPII_D_EVT 0x0400 +#define MPII_D_CFG 0x0800 + +uint32_t mpii_debug = 0 + | MPII_D_CMD + | MPII_D_INTR + | MPII_D_MISC + | MPII_D_DMA + | MPII_D_IOCTL + | MPII_D_RW + | MPII_D_MEM + | MPII_D_CCB + | MPII_D_PPR + | MPII_D_RAID + | MPII_D_EVT + | MPII_D_CFG + ; +#else +#define DPRINTF(x...) +#define DNPRINTF(n,x...) +#endif + +#define MPII_REQUEST_SIZE 512 +#define MPII_REPLY_SIZE 128 +#define MPII_REPLY_COUNT (PAGE_SIZE / MPII_REPLY_SIZE) + +/* + * this is the max number of sge's we can stuff in a request frame: + * sizeof(scsi_io) + sizeof(sense) + sizeof(sge) * 32 = MPII_REQUEST_SIZE + */ +#define MPII_MAX_SGL 32 + +#define MPII_MAX_REQUEST_CREDIT 500 +#define MPII_MAX_REPLY_POST_QDEPTH 128 + +struct mpii_dmamem { + bus_dmamap_t mdm_map; + bus_dma_segment_t mdm_seg; + size_t mdm_size; + caddr_t mdm_kva; +}; +#define MPII_DMA_MAP(_mdm) ((_mdm)->mdm_map) +#define MPII_DMA_DVA(_mdm) ((_mdm)->mdm_map->dm_segs[0].ds_addr) +#define MPII_DMA_KVA(_mdm) ((void *)(_mdm)->mdm_kva) + +struct mpii_ccb_bundle { + struct mpii_msg_scsi_io mcb_io; /* sgl must follow */ + struct mpii_sge mcb_sgl[MPII_MAX_SGL]; + struct scsi_sense_data mcb_sense; +} __packed; + +struct mpii_softc; + +struct mpii_rcb { + void *rcb_reply; + u_int32_t rcb_reply_dva; +}; + +struct mpii_ccb { + struct mpii_softc *ccb_sc; + int ccb_smid; + + struct scsi_xfer *ccb_xs; + bus_dmamap_t ccb_dmamap; + + bus_addr_t ccb_offset; + void *ccb_cmd; + bus_addr_t ccb_cmd_dva; + u_int16_t ccb_dev_handle; + + volatile enum { + MPII_CCB_FREE, + MPII_CCB_READY, + MPII_CCB_QUEUED + } ccb_state; + + void (*ccb_done)(struct mpii_ccb *); + struct mpii_rcb *ccb_rcb; + + TAILQ_ENTRY(mpii_ccb) ccb_link; +}; + +TAILQ_HEAD(mpii_ccb_list, mpii_ccb); + +struct mpii_softc { + struct device sc_dev; + struct scsi_link sc_link; + + int sc_flags; +#define MPII_F_RAID (1<<1) + + struct scsibus_softc *sc_scsibus; + + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + bus_size_t sc_ios; + bus_dma_tag_t sc_dmat; + + u_int8_t sc_porttype; + int sc_request_depth; + int sc_num_reply_frames; + int sc_reply_free_qdepth; + int sc_reply_post_qdepth; + int sc_maxchdepth; + int sc_first_sgl_len; + int sc_chain_len; + int sc_max_sgl_len; + u_int8_t sc_ir_firmware; + + int sc_buswidth; + int sc_target; + int sc_ioc_number; + u_int8_t sc_vf_id; + u_int8_t sc_num_ports; + + struct mpii_ccb *sc_ccbs; + struct mpii_ccb_list sc_ccb_free; + + struct mpii_dmamem *sc_requests; + + struct mpii_dmamem *sc_replies; + struct mpii_rcb *sc_rcbs; + + struct mpii_dmamem *sc_reply_postq; + struct mpii_reply_descriptor *sc_reply_postq_kva; + int sc_reply_post_host_index; + + struct mpii_dmamem *sc_reply_freeq; + int sc_reply_free_host_index; + + size_t sc_fw_len; + struct mpii_dmamem *sc_fw; + + /* scsi ioctl from sd device */ + int (*sc_ioctl)(struct device *, u_long, caddr_t); + + struct rwlock sc_lock; + struct mpii_cfg_hdr sc_cfg_hdr; + struct mpii_cfg_ioc_pg2 *sc_vol_page; + struct mpii_cfg_raid_vol *sc_vol_list; + struct mpii_cfg_raid_vol_pg0 *sc_rpg0; + + struct ksensor *sc_sensors; + struct ksensordev sc_sensordev; +}; + +int mpii_attach(struct mpii_softc *); +void mpii_detach(struct mpii_softc *); +int mpii_intr(void *); + +int mpii_pci_match(struct device *, void *, void *); +void mpii_pci_attach(struct device *, struct device *, void *); +int mpii_pci_detach(struct device *, int); + +struct mpii_pci_softc { + struct mpii_softc psc_mpii; + + pci_chipset_tag_t psc_pc; + pcitag_t psc_tag; + + void *psc_ih; +}; + +struct cfattach mpii_pci_ca = { + sizeof(struct mpii_pci_softc), mpii_pci_match, mpii_pci_attach, + mpii_pci_detach +}; + +#define PREAD(s, r) pci_conf_read((s)->psc_pc, (s)->psc_tag, (r)) +#define PWRITE(s, r, v) pci_conf_write((s)->psc_pc, (s)->psc_tag, (r), (v)) + +static const struct pci_matchid mpii_devices[] = { + { PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_SAS2008 } +}; + +int +mpii_pci_match(struct device *parent, void *match, void *aux) +{ + return (pci_matchbyid(aux, mpii_devices, nitems(mpii_devices))); +} + +void +mpii_pci_attach(struct device *parent, struct device *self, void *aux) +{ + struct mpii_pci_softc *psc = (void *)self; + struct mpii_softc *sc = &psc->psc_mpii; + struct pci_attach_args *pa = aux; + pcireg_t memtype; + int r; + pci_intr_handle_t ih; + const char *intrstr; +#ifdef __sparc64__ + int node; +#endif + + psc->psc_pc = pa->pa_pc; + psc->psc_tag = pa->pa_tag; + psc->psc_ih = NULL; + sc->sc_dmat = pa->pa_dmat; + sc->sc_ios = 0; + sc->sc_target = -1; + + /* find the appropriate memory base */ + for (r = PCI_MAPREG_START; r < PCI_MAPREG_END; r += sizeof(memtype)) { + memtype = pci_mapreg_type(psc->psc_pc, psc->psc_tag, r); + if ((memtype & PCI_MAPREG_TYPE_MASK) == PCI_MAPREG_TYPE_MEM) + break; + } + if (r >= PCI_MAPREG_END) { + printf(": unable to locate system interface registers\n"); + return; + } + + if (pci_mapreg_map(pa, r, memtype, 0, &sc->sc_iot, &sc->sc_ioh, + NULL, &sc->sc_ios, 0xFF) != 0) { + printf(": unable to map system interface registers\n"); + return; + } + + /* disable the expansion rom */ + PWRITE(psc, PCI_ROM_REG, PREAD(psc, PCI_ROM_REG) & ~PCI_ROM_ENABLE); + + /* hook up the interrupt */ + if (pci_intr_map(pa, &ih)) { + printf(": unable to map interrupt\n"); + goto unmap; + } + intrstr = pci_intr_string(psc->psc_pc, ih); + psc->psc_ih = pci_intr_establish(psc->psc_pc, ih, IPL_BIO, + mpii_intr, sc, sc->sc_dev.dv_xname); + if (psc->psc_ih == NULL) { + printf(": unable to map interrupt%s%s\n", + intrstr == NULL ? "" : " at ", + intrstr == NULL ? "" : intrstr); + goto unmap; + } + printf(": %s", intrstr); + +#ifdef __sparc64__ + /* + * Walk up the Open Firmware device tree until we find a + * "scsi-initiator-id" property. + */ + node = PCITAG_NODE(pa->pa_tag); + while (node) { + if (OF_getprop(node, "scsi-initiator-id", + &sc->sc_target, sizeof(sc->sc_target)) == + sizeof(sc->sc_target)) + break; + node = OF_parent(node); +#endif + + if (mpii_attach(sc) != 0) { + /* error printed by mpii_attach */ + goto deintr; + } + + return; + +deintr: + pci_intr_disestablish(psc->psc_pc, psc->psc_ih); + psc->psc_ih = NULL; +unmap: + bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios); + sc->sc_ios = 0; +} + +int +mpii_pci_detach(struct device *self, int flags) +{ + struct mpii_pci_softc *psc = (struct mpii_pci_softc *)self; + struct mpii_softc *sc = &psc->psc_mpii; + + mpii_detach(sc); + + if (psc->psc_ih != NULL) { + pci_intr_disestablish(psc->psc_pc, psc->psc_ih); + psc->psc_ih = NULL; + } + if (sc->sc_ios != 0) { + bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios); + sc->sc_ios = 0; + } + + return (0); +} + +struct cfdriver mpii_cd = { + NULL, + "mpii", + DV_DULL +}; + +int mpii_scsi_cmd(struct scsi_xfer *); +void mpii_scsi_cmd_done(struct mpii_ccb *); +void mpii_minphys(struct buf *bp, struct scsi_link *sl); +int mpii_scsi_probe(struct scsi_link *); +int mpii_scsi_ioctl(struct scsi_link *, u_long, caddr_t, + int, struct proc *); + +struct scsi_adapter mpii_switch = { + mpii_scsi_cmd, + mpii_minphys, + NULL, + NULL, + NULL +}; + +struct scsi_device mpii_dev = { + NULL, + NULL, + NULL, + NULL +}; + +struct mpii_dmamem *mpii_dmamem_alloc(struct mpii_softc *, size_t); +void mpii_dmamem_free(struct mpii_softc *, + struct mpii_dmamem *); +int mpii_alloc_ccbs(struct mpii_softc *); +struct mpii_ccb *mpii_get_ccb(struct mpii_softc *); +void mpii_put_ccb(struct mpii_softc *, struct mpii_ccb *); +int mpii_alloc_replies(struct mpii_softc *); +int mpii_alloc_queues(struct mpii_softc *); +void mpii_push_reply(struct mpii_softc *, u_int32_t); +void mpii_push_replies(struct mpii_softc *); + +void mpii_start(struct mpii_softc *, struct mpii_ccb *); +int mpii_complete(struct mpii_softc *, struct mpii_ccb *, int); +int mpii_poll(struct mpii_softc *, struct mpii_ccb *, int); +int mpii_reply(struct mpii_softc *); + +void mpii_init_queues(struct mpii_softc *); + +void mpii_timeout_xs(void *); +int mpii_load_xs(struct mpii_ccb *); + +u_int32_t mpii_read(struct mpii_softc *, bus_size_t); +void mpii_write(struct mpii_softc *, bus_size_t, u_int32_t); +int mpii_wait_eq(struct mpii_softc *, bus_size_t, u_int32_t, + u_int32_t); +int mpii_wait_ne(struct mpii_softc *, bus_size_t, u_int32_t, + u_int32_t); + +int mpii_init(struct mpii_softc *); +int mpii_reset_soft(struct mpii_softc *); +int mpii_reset_hard(struct mpii_softc *); + +int mpii_handshake_send(struct mpii_softc *, void *, size_t); +int mpii_handshake_recv_dword(struct mpii_softc *, + u_int32_t *); +int mpii_handshake_recv(struct mpii_softc *, void *, size_t); + +void mpii_empty_done(struct mpii_ccb *); + +int mpii_iocinit(struct mpii_softc *); +int mpii_iocfacts(struct mpii_softc *); +int mpii_portfacts(struct mpii_softc *); +int mpii_portenable(struct mpii_softc *); +int mpii_cfg_coalescing(struct mpii_softc *); +/* +void mpii_get_raid(struct mpii_softc *); +int mpii_fwupload(struct mpii_softc *); + +int mpii_eventnotify(struct mpii_softc *); +void mpii_eventnotify_done(struct mpii_ccb *); +void mpii_eventack(struct mpii_softc *, + struct mpii_msg_event_reply *); +void mpii_eventack_done(struct mpii_ccb *); +void mpii_evt_sas(void *, void *); +*/ +int mpii_req_cfg_header(struct mpii_softc *, u_int8_t, + u_int8_t, u_int32_t, int, void *); +int mpii_req_cfg_page(struct mpii_softc *, u_int32_t, int, + void *, int, void *, size_t); + +void mpii_get_ioc_pg8(struct mpii_softc *); +void mpii_get_raid_config_pg0(struct mpii_softc *); + +#if NBIO > 0 +int mpii_bio_get_pg0_raid(struct mpii_softc *, int); +int mpii_ioctl(struct device *, u_long, caddr_t); +int mpii_ioctl_inq(struct mpii_softc *, struct bioc_inq *); +int mpii_ioctl_vol(struct mpii_softc *, struct bioc_vol *); +int mpii_ioctl_disk(struct mpii_softc *, struct bioc_disk *); +int mpii_ioctl_setstate(struct mpii_softc *, struct bioc_setstate *); +#ifndef SMALL_KERNEL +int mpii_create_sensors(struct mpii_softc *); +void mpii_refresh_sensors(void *); +#endif /* SMALL_KERNEL */ +#endif /* NBIO > 0 */ + +#define DEVNAME(s) ((s)->sc_dev.dv_xname) + +#define dwordsof(s) (sizeof(s) / sizeof(u_int32_t)) +#define dwordn(p, n) (((u_int32_t *)(p))[(n)]) + +#define mpii_read_db(s) mpii_read((s), MPII_DOORBELL) +#define mpii_write_db(s, v) mpii_write((s), MPII_DOORBELL, (v)) +#define mpii_read_intr(s) mpii_read((s), MPII_INTR_STATUS) +#define mpii_write_intr(s, v) mpii_write((s), MPII_INTR_STATUS, (v)) +#define mpii_reply_waiting(s) ((mpii_read_intr((s)) & MPII_INTR_STATUS_REPLY)\ + == MPII_INTR_STATUS_REPLY) + +#define mpii_read_reply_free(s, v) mpii_read((s), \ + MPII_REPLY_FREE_HOST_INDEX) +#define mpii_write_reply_free(s, v) mpii_write((s), \ + MPII_REPLY_FREE_HOST_INDEX, (v)) +#define mpii_read_reply_post(s, v) mpii_read((s), \ + MPII_REPLY_POST_HOST_INDEX) +#define mpii_write_reply_post(s, v) mpii_write((s), \ + MPII_REPLY_POST_HOST_INDEX, (v)) + +#define mpii_wait_db_int(s) mpii_wait_ne((s), MPII_INTR_STATUS, \ + MPII_INTR_STATUS_IOC2SYSDB, 0) +#define mpii_wait_db_ack(s) mpii_wait_eq((s), MPII_INTR_STATUS, \ + MPII_INTR_STATUS_SYS2IOCDB, 0) + +#define mpii_cfg_header(_s, _t, _n, _a, _h) \ + mpii_req_cfg_header((_s), (_t), (_n), (_a), 0, (_h)) +#define mpii_ecfg_header(_s, _t, _n, _a, _h) \ + mpii_req_cfg_header((_s), (_t), (_n), (_a), 1, (_h)) + +#define mpii_cfg_page(_s, _a, _h, _r, _p, _l) \ + mpii_req_cfg_page((_s), (_a), 0, (_h), (_r), (_p), (_l)) +#define mpii_ecfg_page(_s, _a, _h, _r, _p, _l) \ + mpii_req_cfg_page((_s), (_a), 1, (_h), (_r), (_p), (_l)) + +int +mpii_attach(struct mpii_softc *sc) +{ + struct scsibus_attach_args saa; + struct mpii_ccb *ccb; + + printf("\n"); + + /* disable interrupts */ + mpii_write(sc, MPII_INTR_MASK, + MPII_INTR_MASK_RESET | MPII_INTR_MASK_REPLY + | MPII_INTR_MASK_DOORBELL); + + if (mpii_init(sc) != 0) { + printf("%s: unable to initialize ioc\n", DEVNAME(sc)); + return (1); + } + + if (mpii_iocfacts(sc) != 0) { + printf("%s: unable to get iocfacts\n", DEVNAME(sc)); + return (1); + } + + if (mpii_alloc_ccbs(sc) != 0) { + /* error already printed */ + return(1); + } + + if (mpii_alloc_replies(sc) != 0) { + printf("%s: unable to allocated reply space\n", DEVNAME(sc)); + goto free_ccbs; + } + + if (mpii_alloc_queues(sc) != 0) { + printf("%s: unable to allocate reply queues\n", DEVNAME(sc)); + goto free_replies; + } + + if (mpii_iocinit(sc) != 0) { + printf("%s: unable to send iocinit\n", DEVNAME(sc)); + goto free_ccbs; + } + + if (mpii_wait_eq(sc, MPII_DOORBELL, MPII_DOORBELL_STATE, + MPII_DOORBELL_STATE_OPER) != 0) { + printf("%s: state: 0x%08x\n", DEVNAME(sc), + mpii_read_db(sc) & MPII_DOORBELL_STATE); + printf("%s: operational state timeout\n", DEVNAME(sc)); + goto free_ccbs; + } + + mpii_push_replies(sc); + mpii_init_queues(sc); + + if (mpii_portfacts(sc) != 0) { + printf("%s: unable to get portfacts\n", DEVNAME(sc)); + goto free_replies; + } + + if (mpii_portenable(sc) != 0) { + printf("%s: unable to enable port\n", DEVNAME(sc)); + goto free_replies; + } + + mpii_get_ioc_pg8(sc); + mpii_get_raid_config_pg0(sc); + + if (mpii_cfg_coalescing(sc) != 0) + /* this is fatal in mpi */ + printf("%s: unable to configure coalescing\n", DEVNAME(sc)); + + /* XXX JPG not yet implemented + if (mpii_fwupload(sc) != 0) { + printf("%s: unabel to upload firmware\n", DEVNAME(sc)); + goto free_replies; + }*/ + + rw_init(&sc->sc_lock, "mpii_lock"); + + /* we should be good to go now, attach scsibus */ + sc->sc_link.device = &mpii_dev; + sc->sc_link.adapter = &mpii_switch; + sc->sc_link.adapter_softc = sc; + sc->sc_link.adapter_target = sc->sc_target; + sc->sc_link.adapter_buswidth = sc->sc_buswidth; + sc->sc_link.openings = sc->sc_request_depth / sc->sc_buswidth; + + bzero(&saa, sizeof(saa)); + saa.saa_sc_link = &sc->sc_link; + + /* config_found() returns the scsibus attached to us */ + sc->sc_scsibus = (struct scsibus_softc *) config_found(&sc->sc_dev, + &saa, scsiprint); + + /* XXX JPG here we need to: + * + * get raid pages + * do whatever is necessary for MPI2 host mapping + * update our array/RB tree for target -> dev_handle mapping + */ + + /* enable interrupts */ + mpii_write(sc, MPII_INTR_MASK, MPII_INTR_MASK_DOORBELL + | MPII_INTR_MASK_RESET); + + return (0); + +free_replies: + bus_dmamap_sync(sc->sc_dmat, MPII_DMA_MAP(sc->sc_replies), + 0, PAGE_SIZE, BUS_DMASYNC_POSTREAD); + mpii_dmamem_free(sc, sc->sc_replies); + +free_ccbs: + while ((ccb = mpii_get_ccb(sc)) != NULL) + bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap); + mpii_dmamem_free(sc, sc->sc_requests); + free(sc->sc_ccbs, M_DEVBUF); + + return(1); +} + +void +mpii_detach(struct mpii_softc *sc) +{ + +} + +int +mpii_intr(void *arg) +{ + struct mpii_softc *sc = arg; + int rv = 0, count = 0; + int max_processed_replies = 0; + +#ifdef MPII_DEBUG + int disable_interrupt = 0; + u_int32_t db; + + if (disable_interrupt) + return (0); + + db = mpii_read_db(sc); + if ((db & MPII_DOORBELL_STATE) != MPII_DOORBELL_STATE_OPER) { + printf("db not operational\n"); + disable_interrupt = 1; + return (0); + } +#endif + while (mpii_reply(sc) >= 0) { + rv = 1; + if (count++ == 100) { + /* disable interrupts */ + mpii_write(sc, MPII_INTR_MASK, + MPII_INTR_MASK_RESET | MPII_INTR_MASK_REPLY + | MPII_INTR_MASK_DOORBELL); + mpii_write_reply_post(sc, sc->sc_reply_post_host_index); + return (0); + } + } + + mpii_write_reply_post(sc, sc->sc_reply_post_host_index); + + if (count > max_processed_replies) + max_processed_replies = count; + + return (rv); +} + +void +mpii_timeout_xs(void *arg) +{ +/* XXX */ +} + +int +mpii_load_xs(struct mpii_ccb *ccb) +{ + struct mpii_softc *sc = ccb->ccb_sc; + struct scsi_xfer *xs = ccb->ccb_xs; + struct mpii_ccb_bundle *mcb = ccb->ccb_cmd; + struct mpii_msg_scsi_io *io = &mcb->mcb_io; + struct mpii_sge *sge, *nsge = &mcb->mcb_sgl[0]; + struct mpii_sge *ce = NULL, *nce; + u_int64_t ce_dva; + bus_dmamap_t dmap = ccb->ccb_dmamap; + u_int32_t addr, flags; + int i, error; + + /* zero length transfer still requires an SGE */ + if (xs->datalen == 0) { + nsge->sg_hdr = htole32(MPII_SGE_FL_TYPE_SIMPLE | + MPII_SGE_FL_LAST | MPII_SGE_FL_EOB | MPII_SGE_FL_EOL); + return (0); + } + + error = bus_dmamap_load(sc->sc_dmat, dmap, + xs->data, xs->datalen, NULL, + (xs->flags & SCSI_NOSLEEP) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK); + if (error) { + printf("%s: error %d loading dmamap\n", DEVNAME(sc), error); + return (1); + } + + /* safe default staring flags */ + flags = MPII_SGE_FL_TYPE_SIMPLE | MPII_SGE_FL_SIZE_64; + /* if data out */ + if (xs->flags & SCSI_DATA_OUT) + flags |= MPII_SGE_FL_DIR_OUT; + + /* we will have to exceed the SGEs we can cram into the request frame */ + if (dmap->dm_nsegs > sc->sc_first_sgl_len) { + ce = &mcb->mcb_sgl[sc->sc_first_sgl_len - 1]; + io->chain_offset = ((u_int8_t *)ce - (u_int8_t *)io) / 4; + } + + for (i = 0; i < dmap->dm_nsegs; i++) { + + if (nsge == ce) { + nsge++; + sge->sg_hdr |= htole32(MPII_SGE_FL_LAST); + + DNPRINTF(MPII_D_DMA, "%s: - 0x%08x 0x%08x 0x%08x\n", + DEVNAME(sc), sge->sg_hdr, + sge->sg_hi_addr, sge->sg_lo_addr); + + if ((dmap->dm_nsegs - i) > sc->sc_chain_len) { + nce = &nsge[sc->sc_chain_len - 1]; + addr = ((u_int8_t *)nce - (u_int8_t *)nsge) / 4; + addr = addr << 16 | + sizeof(struct mpii_sge) * sc->sc_chain_len; + } else { + nce = NULL; + addr = sizeof(struct mpii_sge) * + (dmap->dm_nsegs - i); + } + + ce->sg_hdr = htole32(MPII_SGE_FL_TYPE_CHAIN | + MPII_SGE_FL_SIZE_64 | addr); + + ce_dva = ccb->ccb_cmd_dva + + ((u_int8_t *)nsge - (u_int8_t *)mcb); + + addr = (u_int32_t)(ce_dva >> 32); + ce->sg_hi_addr = htole32(addr); + addr = (u_int32_t)ce_dva; + ce->sg_lo_addr = htole32(addr); + + DNPRINTF(MPII_D_DMA, "%s: ce: 0x%08x 0x%08x 0x%08x\n", + DEVNAME(sc), ce->sg_hdr, ce->sg_hi_addr, + ce->sg_lo_addr); + + ce = nce; + } + + DNPRINTF(MPII_D_DMA, "%s: %d: %d 0x%016llx\n", DEVNAME(sc), + i, dmap->dm_segs[i].ds_len, + (u_int64_t)dmap->dm_segs[i].ds_addr); + + sge = nsge; + + sge->sg_hdr = htole32(flags | dmap->dm_segs[i].ds_len); + addr = (u_int32_t)((u_int64_t)dmap->dm_segs[i].ds_addr >> 32); + sge->sg_hi_addr = htole32(addr); + addr = (u_int32_t)dmap->dm_segs[i].ds_addr; + sge->sg_lo_addr = htole32(addr); + + DNPRINTF(MPII_D_DMA, "%s: %d: 0x%08x 0x%08x 0x%08x\n", + DEVNAME(sc), i, sge->sg_hdr, sge->sg_hi_addr, + sge->sg_lo_addr); + + nsge = sge + 1; + } + + /* terminate list */ + sge->sg_hdr |= htole32(MPII_SGE_FL_LAST | MPII_SGE_FL_EOB | + MPII_SGE_FL_EOL); + + bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize, + (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_PREREAD : + BUS_DMASYNC_PREWRITE); + + return (0); +} + +void +mpii_minphys(struct buf *bp, struct scsi_link *sl) +{ + /* XXX */ + minphys(bp); +} + +u_int32_t +mpii_read(struct mpii_softc *sc, bus_size_t r) +{ + u_int32_t rv; + + bus_space_barrier(sc->sc_iot, sc->sc_ioh, r, 4, + BUS_SPACE_BARRIER_READ); + rv = bus_space_read_4(sc->sc_iot, sc->sc_ioh, r); + + DNPRINTF(MPII_D_RW, "%s: mpii_read %#x %#x\n", DEVNAME(sc), r, rv); + + return (rv); +} + +void +mpii_write(struct mpii_softc *sc, bus_size_t r, u_int32_t v) +{ + DNPRINTF(MPII_D_RW, "%s: mpii_write %#x %#x\n", DEVNAME(sc), r, v); + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, r, v); + bus_space_barrier(sc->sc_iot, sc->sc_ioh, r, 4, + BUS_SPACE_BARRIER_WRITE); +} + + +int +mpii_wait_eq(struct mpii_softc *sc, bus_size_t r, u_int32_t mask, + u_int32_t target) +{ + int i; + + DNPRINTF(MPII_D_RW, "%s: mpii_wait_eq %#x %#x %#x\n", DEVNAME(sc), r, + mask, target); + + for (i = 0; i < 10000; i++) { + if ((mpii_read(sc, r) & mask) == target) + return (0); + delay(1000); + } + + return (1); +} + +int +mpii_wait_ne(struct mpii_softc *sc, bus_size_t r, u_int32_t mask, + u_int32_t target) +{ + int i; + + DNPRINTF(MPII_D_RW, "%s: mpii_wait_ne %#x %#x %#x\n", DEVNAME(sc), r, + mask, target); + + for (i = 0; i < 10000; i++) { + if ((mpii_read(sc, r) & mask) != target) + return (0); + delay(1000); + } + + return (1); +} + + +int +mpii_init(struct mpii_softc *sc) +{ + u_int32_t db; + int i; + + /* spin until the ioc leaves the reset state */ + if (mpii_wait_ne(sc, MPII_DOORBELL, MPII_DOORBELL_STATE, + MPII_DOORBELL_STATE_RESET) != 0) { + DNPRINTF(MPII_D_MISC, "%s: mpii_init timeout waiting to leave " + "reset state\n", DEVNAME(sc)); + return (1); + } + + /* check current ownership */ + db = mpii_read_db(sc); + if ((db & MPII_DOORBELL_WHOINIT) == MPII_DOORBELL_WHOINIT_PCIPEER) { + DNPRINTF(MPII_D_MISC, "%s: mpii_init initialised by pci peer\n", + DEVNAME(sc)); + return (0); + } + + for (i = 0; i < 5; i++) { + switch (db & MPII_DOORBELL_STATE) { + case MPII_DOORBELL_STATE_READY: + DNPRINTF(MPII_D_MISC, "%s: mpii_init ioc is ready\n", + DEVNAME(sc)); + return (0); + + case MPII_DOORBELL_STATE_OPER: + case MPII_DOORBELL_STATE_FAULT: + DNPRINTF(MPII_D_MISC, "%s: mpii_init ioc is being " + "reset\n" , DEVNAME(sc)); + if (mpii_reset_soft(sc) != 0) + mpii_reset_hard(sc); + break; + + case MPII_DOORBELL_STATE_RESET: + DNPRINTF(MPII_D_MISC, "%s: mpii_init waiting to come " + "out of reset\n", DEVNAME(sc)); + if (mpii_wait_ne(sc, MPII_DOORBELL, MPII_DOORBELL_STATE, + MPII_DOORBELL_STATE_RESET) != 0) + return (1); + break; + } + db = mpii_read_db(sc); + } + + return (1); +} + +int +mpii_reset_soft(struct mpii_softc *sc) +{ + DNPRINTF(MPII_D_MISC, "%s: mpii_reset_soft\n", DEVNAME(sc)); + + if (mpii_read_db(sc) & MPII_DOORBELL_INUSE) { + printf("db in use\n"); + return (1); + } + + mpii_write_db(sc, + MPII_DOORBELL_FUNCTION(MPII_FUNCTION_IOC_MESSAGE_UNIT_RESET)); + + /* XXX LSI waits 15 sec */ + if (mpii_wait_db_ack(sc) != 0) + return (1); + + /* XXX LSI waits 15 sec */ + if (mpii_wait_eq(sc, MPII_DOORBELL, MPII_DOORBELL_STATE, + MPII_DOORBELL_STATE_READY) != 0) + return (1); + + /* XXX wait for Sys2IOCDB bit to clear in HIS?? */ + + return (0); +} + +int +mpii_reset_hard(struct mpii_softc *sc) +{ + u_int16_t i; + + DNPRINTF(MPII_D_MISC, "%s: mpii_reset_hard\n", DEVNAME(sc)); + + mpii_write_intr(sc, 0); + + /* enable diagnostic register */ + mpii_write(sc, MPII_WRITESEQ, MPII_WRITESEQ_FLUSH); + mpii_write(sc, MPII_WRITESEQ, MPII_WRITESEQ_1); + mpii_write(sc, MPII_WRITESEQ, MPII_WRITESEQ_2); + mpii_write(sc, MPII_WRITESEQ, MPII_WRITESEQ_3); + mpii_write(sc, MPII_WRITESEQ, MPII_WRITESEQ_4); + mpii_write(sc, MPII_WRITESEQ, MPII_WRITESEQ_5); + mpii_write(sc, MPII_WRITESEQ, MPII_WRITESEQ_6); + + + delay(100); + + if ((mpii_read(sc, MPII_HOSTDIAG) & MPII_HOSTDIAG_DWRE) == 0) + return(1); + + /* reset ioc */ + mpii_write(sc, MPII_HOSTDIAG, MPII_HOSTDIAG_RESET_ADAPTER); + + /* 200 milliseconds */ + delay(200000); + + + /* XXX this whole function should be more robust */ + + /* XXX read the host diagnostic reg until reset adapter bit clears ? */ + for (i = 0; i < 30000; i++) { + if ((mpii_read(sc, MPII_HOSTDIAG) & + MPII_HOSTDIAG_RESET_ADAPTER) == 0) + break; + delay(10000); + } + + /* disable diagnostic register */ + mpii_write(sc, MPII_WRITESEQ, 0xff); + + /* XXX what else? */ + + DNPRINTF(MPII_D_MISC, "%s: done with mpii_reset_hard\n", DEVNAME(sc)); + + return(0); +} + +int +mpii_handshake_send(struct mpii_softc *sc, void *buf, size_t dwords) +{ + u_int32_t *query = buf; + int i; + + /* make sure the doorbell is not in use. */ + if (mpii_read_db(sc) & MPII_DOORBELL_INUSE) + return (1); + + /* clear pending doorbell interrupts */ + if (mpii_read_intr(sc) & MPII_INTR_STATUS_IOC2SYSDB) + mpii_write_intr(sc, 0); + + /* + * first write the doorbell with the handshake function and the + * dword count. + */ + mpii_write_db(sc, MPII_DOORBELL_FUNCTION(MPII_FUNCTION_HANDSHAKE) | + MPII_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 (mpii_wait_db_int(sc) != 0) + return (1); + mpii_write_intr(sc, 0); + + /* poll for the acknowledgement. */ + if (mpii_wait_db_ack(sc) != 0) + return (1); + + /* write the query through the doorbell. */ + for (i = 0; i < dwords; i++) { + mpii_write_db(sc, htole32(query[i])); + if (mpii_wait_db_ack(sc) != 0) + return (1); + } + + return (0); +} + +int +mpii_handshake_recv_dword(struct mpii_softc *sc, u_int32_t *dword) +{ + u_int16_t *words = (u_int16_t *)dword; + int i; + + for (i = 0; i < 2; i++) { + if (mpii_wait_db_int(sc) != 0) + return (1); + words[i] = letoh16(mpii_read_db(sc) & MPII_DOORBELL_DATA_MASK); + mpii_write_intr(sc, 0); + } + + return (0); +} + +int +mpii_handshake_recv(struct mpii_softc *sc, void *buf, size_t dwords) +{ + struct mpii_msg_reply *reply = buf; + u_int32_t *dbuf = buf, dummy; + int i; + + /* get the first dword so we can read the length out of the header. */ + if (mpii_handshake_recv_dword(sc, &dbuf[0]) != 0) + return (1); + + DNPRINTF(MPII_D_CMD, "%s: mpii_handshake_recv dwords: %d reply: %d\n", + DEVNAME(sc), 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 (mpii_handshake_recv_dword(sc, &dbuf[i]) != 0) + return (1); + } + + /* if there's extra stuff to come off the ioc, discard it */ + while (i++ < reply->msg_length) { + if (mpii_handshake_recv_dword(sc, &dummy) != 0) + return (1); + DNPRINTF(MPII_D_CMD, "%s: mpii_handshake_recv dummy read: " + "0x%08x\n", DEVNAME(sc), dummy); + } + + /* wait for the doorbell used bit to be reset and clear the intr */ + if (mpii_wait_db_int(sc) != 0) + return (1); + + if (mpii_wait_eq(sc, MPII_DOORBELL, MPII_DOORBELL_INUSE, 0) != 0) + return (1); + + mpii_write_intr(sc, 0); + + return (0); +} + +void +mpii_empty_done(struct mpii_ccb *ccb) +{ + /* nothing to do */ +} + +int +mpii_iocfacts(struct mpii_softc *sc) +{ + struct mpii_msg_iocfacts_request ifq; + struct mpii_msg_iocfacts_reply ifp; + + DNPRINTF(MPII_D_MISC, "%s: mpii_iocfacts\n", DEVNAME(sc)); + + bzero(&ifq, sizeof(ifq)); + bzero(&ifp, sizeof(ifp)); + + ifq.function = MPII_FUNCTION_IOC_FACTS; + + if (mpii_handshake_send(sc, &ifq, dwordsof(ifq)) != 0) { + DNPRINTF(MPII_D_MISC, "%s: mpii_iocfacts send failed\n", + DEVNAME(sc)); + return (1); + } + + if (mpii_handshake_recv(sc, &ifp, dwordsof(ifp)) != 0) { + DNPRINTF(MPII_D_MISC, "%s: mpii_iocfacts recv failed\n", + DEVNAME(sc)); + return (1); + } + + DNPRINTF(MPII_D_MISC, "%s: func: 0x%02x length: %d msgver: %d.%d\n", + DEVNAME(sc), ifp.function, ifp.msg_length, + ifp.msg_version_maj, ifp.msg_version_min); + DNPRINTF(MPII_D_MISC, "%s: msgflags: 0x%02x iocnumber: 0x%02x " + "headerver: %d.%d\n", DEVNAME(sc), ifp.msg_flags, + ifp.ioc_number, ifp.header_version_unit, + ifp.header_version_dev); + DNPRINTF(MPII_D_MISC, "%s: vp_id: 0x%02x vf_id: 0x%02x\n", DEVNAME(sc), + ifp.vp_id, ifp.vf_id); + DNPRINTF(MPII_D_MISC, "%s: iocstatus: 0x%04x ioexceptions: 0x%04x\n", + DEVNAME(sc), letoh16(ifp.ioc_status), + letoh16(ifp.ioc_exceptions)); + DNPRINTF(MPII_D_MISC, "%s: iocloginfo: 0x%08x\n", DEVNAME(sc), + letoh32(ifp.ioc_loginfo)); + DNPRINTF(MPII_D_MISC, "%s: numberofports: 0x%02x whoinit: 0x%02x " + "maxchaindepth: %d\n", DEVNAME(sc), ifp.number_of_ports, + ifp.whoinit, ifp.max_chain_depth); + DNPRINTF(MPII_D_MISC, "%s: productid: 0x%04x requestcredit: 0x%04x\n", + DEVNAME(sc), letoh16(ifp.product_id), letoh16(ifp.request_credit)); + DNPRINTF(MPII_D_MISC, "%s: ioc_capabilities: 0x%08x\n", DEVNAME(sc), + letoh32(ifp.ioc_capabilities)); + DNPRINTF(MPII_D_MISC, "%s: fw_version: %d.%d fw_version_unit: 0x%02x " + "fw_version_dev: 0x%02x\n", DEVNAME(sc), + ifp.fw_version_maj, ifp.fw_version_min, + ifp.fw_version_unit, ifp.fw_version_dev); + DNPRINTF(MPII_D_MISC, "%s: iocrequestframesize: 0x%04x\n", + DEVNAME(sc), letoh16(ifp.ioc_request_frame_size)); + DNPRINTF(MPII_D_MISC, "%s: maxtargets: 0x%04x " + "maxinitiators: 0x%04x\n", DEVNAME(sc), + letoh16(ifp.max_targets), letoh16(ifp.max_initiators)); + DNPRINTF(MPII_D_MISC, "%s: maxenclosures: 0x%04x " + "maxsasexpanders: 0x%04x\n", DEVNAME(sc), + letoh16(ifp.max_enclosures), letoh16(ifp.max_sas_expanders)); + DNPRINTF(MPII_D_MISC, "%s: highprioritycredit: 0x%04x " + "protocolflags: 0x%02x\n", DEVNAME(sc), + letoh16(ifp.high_priority_credit), letoh16(ifp.protocol_flags)); + DNPRINTF(MPII_D_MISC, "%s: maxvolumes: 0x%02x replyframesize: 0x%02x " + "mrdpqd: 0x%04x\n", DEVNAME(sc), ifp.max_volumes, + ifp.reply_frame_size, + letoh16(ifp.max_reply_descriptor_post_queue_depth)); + DNPRINTF(MPII_D_MISC, "%s: maxpersistententries: 0x%04x " + "maxdevhandle: 0x%02x\n", DEVNAME(sc), + letoh16(ifp.max_persistent_entries), letoh16(ifp.max_dev_handle)); + + sc->sc_maxchdepth = ifp.max_chain_depth; + sc->sc_ioc_number = ifp.ioc_number; + sc->sc_vf_id = ifp.vf_id; + + /* XXX JPG should this be max targets + max vol from cfg page ?? */ + sc->sc_buswidth = (ifp.max_targets == 0) ? 256 : + letoh16(ifp.max_targets); + sc->sc_num_ports = ifp.number_of_ports; + sc->sc_ir_firmware = (letoh32(ifp.ioc_capabilities) & + MPII_IOCFACTS_CAPABILITY_INTEGRATED_RAID) ? 1 : 0; + + sc->sc_request_depth = MIN(letoh16(ifp.request_credit), + MPII_MAX_REQUEST_CREDIT); + + /* should not be multiple of 16 */ + sc->sc_num_reply_frames = sc->sc_request_depth + 32; + if (!(sc->sc_num_reply_frames % 16)) + sc->sc_num_reply_frames--; + + /* must be multiple of 16 */ + sc->sc_reply_free_qdepth = sc->sc_num_reply_frames + + (16 - (sc->sc_num_reply_frames % 16)); + + sc->sc_reply_post_qdepth = sc->sc_request_depth + + sc->sc_num_reply_frames + 1; + + if (sc->sc_reply_post_qdepth > MPII_MAX_REPLY_POST_QDEPTH) + sc->sc_reply_post_qdepth = MPII_MAX_REPLY_POST_QDEPTH; + + if (sc->sc_reply_post_qdepth > + ifp.max_reply_descriptor_post_queue_depth) + sc->sc_reply_post_qdepth = + ifp.max_reply_descriptor_post_queue_depth; + + /* XXX JPG temporary override of calculated values. + * need to think this through as the specs + * and other existing drivers contradict + */ + sc->sc_reply_post_qdepth = 128; + sc->sc_request_depth = 128; + sc->sc_num_reply_frames = 63; + sc->sc_reply_free_qdepth = 64; + + DNPRINTF(MPII_D_MISC, "%s: sc_request_depth: %d " + "sc_num_reply_frames: %d sc_reply_free_qdepth: %d " + "sc_reply_post_qdepth: %d\n", DEVNAME(sc), sc->sc_request_depth, + sc->sc_num_reply_frames, sc->sc_reply_free_qdepth, + sc->sc_reply_post_qdepth); + + /* + * 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 = ((letoh16(ifp.ioc_request_frame_size) * 4) - + sizeof(struct mpii_msg_scsi_io)) / sizeof(struct mpii_sge); + DNPRINTF(MPII_D_MISC, "%s: first sgl len: %d\n", DEVNAME(sc), + sc->sc_first_sgl_len); + + sc->sc_chain_len = (letoh16(ifp.ioc_request_frame_size) * 4) / + sizeof(struct mpii_sge); + DNPRINTF(MPII_D_MISC, "%s: chain len: %d\n", DEVNAME(sc), + sc->sc_chain_len); + + /* the sgl tailing the io cmd loses an entry to the chain element. */ + sc->sc_max_sgl_len = MPII_MAX_SGL - 1; + /* the sgl chains lose an entry for each chain element */ + sc->sc_max_sgl_len -= (MPII_MAX_SGL - sc->sc_first_sgl_len) / + sc->sc_chain_len; + DNPRINTF(MPII_D_MISC, "%s: max sgl len: %d\n", DEVNAME(sc), + sc->sc_max_sgl_len); + + /* XXX we're ignoring the max chain depth */ + + return(0); + +} + +int +mpii_iocinit(struct mpii_softc *sc) +{ + struct mpii_msg_iocinit_request iiq; + struct mpii_msg_iocinit_reply iip; + u_int32_t hi_addr; + + DNPRINTF(MPII_D_MISC, "%s: mpii_iocinit\n", DEVNAME(sc)); + + bzero(&iiq, sizeof(iiq)); + bzero(&iip, sizeof(iip)); + + iiq.function = MPII_FUNCTION_IOC_INIT; + iiq.whoinit = MPII_WHOINIT_HOST_DRIVER; + + /* XXX JPG do something about vf_id */ + iiq.vf_id = 0; + + iiq.msg_version_maj = 0x02; + iiq.msg_version_min = 0x00; + + /* XXX JPG ensure compliance with some level and hard-code? */ + iiq.hdr_version_unit = 0x00; + iiq.hdr_version_dev = 0x00; + + iiq.system_request_frame_size = htole16(MPII_REQUEST_SIZE / 4); + + iiq.reply_descriptor_post_queue_depth = + htole16(sc->sc_reply_post_qdepth); + + iiq.reply_free_queue_depth = htole16(sc->sc_reply_free_qdepth); + + hi_addr = (u_int32_t)((u_int64_t)MPII_DMA_DVA(sc->sc_requests) >> 32); + iiq.sense_buffer_address_high = htole32(hi_addr); + + hi_addr = (u_int32_t) + ((u_int64_t)MPII_DMA_DVA(sc->sc_replies) >> 32); + iiq.system_reply_address_high = hi_addr; + + iiq.system_request_frame_base_address = + (u_int64_t)MPII_DMA_DVA(sc->sc_requests); + + iiq.reply_descriptor_post_queue_address = + (u_int64_t)MPII_DMA_DVA(sc->sc_reply_postq); + + iiq.reply_free_queue_address = + (u_int64_t)MPII_DMA_DVA(sc->sc_reply_freeq); + + if (mpii_handshake_send(sc, &iiq, dwordsof(iiq)) != 0) { + DNPRINTF(MPII_D_MISC, "%s: mpii_iocinit send failed\n", + DEVNAME(sc)); + return (1); + } + + if (mpii_handshake_recv(sc, &iip, dwordsof(iip)) != 0) { + DNPRINTF(MPII_D_MISC, "%s: mpii_iocinit recv failed\n", + DEVNAME(sc)); + return (1); + } + + DNPRINTF(MPII_D_MISC, "%s: function: 0x%02x msg_length: %d " + "whoinit: 0x%02x\n", DEVNAME(sc), iip.function, + iip.msg_length, iip.whoinit); + DNPRINTF(MPII_D_MISC, "%s: msg_flags: 0x%02x\n", DEVNAME(sc), + iip.msg_flags); + DNPRINTF(MPII_D_MISC, "%s: vf_id: 0x%02x vp_id: 0x%02x\n", DEVNAME(sc), + iip.vf_id, iip.vp_id); + DNPRINTF(MPII_D_MISC, "%s: ioc_status: 0x%04x\n", DEVNAME(sc), + letoh16(iip.ioc_status)); + DNPRINTF(MPII_D_MISC, "%s: ioc_loginfo: 0x%08x\n", DEVNAME(sc), + letoh32(iip.ioc_loginfo)); + + if ((iip.ioc_status != MPII_IOCSTATUS_SUCCESS) || (iip.ioc_loginfo)) + return (1); + + return (0); +} + +void +mpii_push_reply(struct mpii_softc *sc, u_int32_t rdva) +{ + u_int32_t *rfp; + + rfp = MPII_DMA_KVA(sc->sc_reply_freeq); + rfp[sc->sc_reply_free_host_index] = rdva; + + sc->sc_reply_free_host_index = (sc->sc_reply_free_host_index + 1) % + sc->sc_reply_free_qdepth; + + mpii_write_reply_free(sc, sc->sc_reply_free_host_index); +} + +int +mpii_portfacts(struct mpii_softc *sc) +{ + struct mpii_msg_portfacts_request *pfq; + struct mpii_msg_portfacts_reply *pfp; + int rv = 1, s; + struct mpii_ccb *ccb; + + DNPRINTF(MPII_D_MISC, "%s: mpii_portfacts\n", DEVNAME(sc)); + + s = splbio(); + ccb = mpii_get_ccb(sc); + splx(s); + if (ccb == NULL) { + DNPRINTF(MPII_D_MISC, "%s: mpii_portfacts mpii_get_ccb fail\n", + DEVNAME(sc)); + return (rv); + } + + ccb->ccb_done = mpii_empty_done; + pfq = ccb->ccb_cmd; + + bzero(pfq, sizeof(struct mpii_msg_portfacts_request)); + + pfq->function = MPII_FUNCTION_PORT_FACTS; + pfq->chain_offset = 0; + pfq->msg_flags = 0; + pfq->port_number = 0; + pfq->vp_id = 0; + pfq->vf_id = 0; + + if (mpii_poll(sc, ccb, 50000) != 0) { + DNPRINTF(MPII_D_MISC, "%s: mpii_portfacts poll\n", DEVNAME(sc)); + goto err; + } + + if (ccb->ccb_rcb == NULL) { + DNPRINTF(MPII_D_MISC, "%s: empty portfacts reply\n", + DEVNAME(sc)); + goto err; + } + + pfp = ccb->ccb_rcb->rcb_reply; + DNPRINTF(MPII_D_MISC, "%s pfp: 0x%04x\n", DEVNAME(sc), pfp); + + DNPRINTF(MPII_D_MISC, "%s: function: 0x%02x msg_length: %d\n", + DEVNAME(sc), pfp->function, pfp->msg_length); + DNPRINTF(MPII_D_MISC, "%s: msg_flags: 0x%02x port_number: %d\n", + DEVNAME(sc), pfp->msg_flags, pfp->port_number); + DNPRINTF(MPII_D_MISC, "%s: vf_id: 0x%02x vp_id: 0x%02x\n", DEVNAME(sc), + pfp->vf_id, pfp->vp_id); + DNPRINTF(MPII_D_MISC, "%s: ioc_status: 0x%04x\n", DEVNAME(sc), + letoh16(pfp->ioc_status)); + DNPRINTF(MPII_D_MISC, "%s: ioc_loginfo: 0x%08x\n", DEVNAME(sc), + letoh32(pfp->ioc_loginfo)); + DNPRINTF(MPII_D_MISC, "%s: port_type: 0x%02x\n", DEVNAME(sc), + pfp->port_type); + DNPRINTF(MPII_D_MISC, "%s: max_posted_cmd_buffers: %d\n", DEVNAME(sc), + letoh16(pfp->max_posted_cmd_buffers)); + + sc->sc_porttype = pfp->port_type; + /* no such field in MPI2 .... but the dilemma is what + * to return to sc_link.adapter_target ... is -1 acceptable? fake 255? + if (sc->sc_target == -1) + sc->sc_target = letoh16(pfp->port_scsi_id); + */ + + mpii_push_reply(sc, ccb->ccb_rcb->rcb_reply_dva); + rv = 0; +err: + mpii_put_ccb(sc, ccb); + + return (rv); +} + +int +mpii_portenable(struct mpii_softc *sc) +{ + struct mpii_msg_portenable_request *peq; + struct mpii_msg_portenable_repy *pep; + struct mpii_ccb *ccb; + int s; + + DNPRINTF(MPII_D_MISC, "%s: mpii_portenable\n", DEVNAME(sc)); + + s = splbio(); + ccb = mpii_get_ccb(sc); + splx(s); + if (ccb == NULL) { + DNPRINTF(MPII_D_MISC, "%s: mpii_portenable ccb_get\n", + DEVNAME(sc)); + return (1); + } + + ccb->ccb_done = mpii_empty_done; + peq = ccb->ccb_cmd; + + peq->function = MPII_FUNCTION_PORT_ENABLE; + peq->vf_id = sc->sc_vf_id; + + if (mpii_poll(sc, ccb, 50000) != 0) { + DNPRINTF(MPII_D_MISC, "%s: mpii_portenable poll\n", DEVNAME(sc)); + return (1); + } + + if (ccb->ccb_rcb == NULL) { + DNPRINTF(MPII_D_MISC, "%s: empty portenable reply\n", + DEVNAME(sc)); + return (1); + } + pep = ccb->ccb_rcb->rcb_reply; + + mpii_push_reply(sc, ccb->ccb_rcb->rcb_reply_dva); + mpii_put_ccb(sc, ccb); + + return (0); +} + +int +mpii_cfg_coalescing(struct mpii_softc *sc) +{ + struct mpii_cfg_hdr hdr; + struct mpii_cfg_ioc_pg1 pg; + u_int32_t flags; + + if (mpii_cfg_header(sc, MPII_CONFIG_REQ_PAGE_TYPE_IOC, 1, 0, &hdr) != 0) { + DNPRINTF(MPII_D_MISC, "%s: unable to fetch IOC page 1 header\n", + DEVNAME(sc)); + return (1); + } + + if (mpii_cfg_page(sc, 0, &hdr, 1, &pg, sizeof(pg)) != 0) { + DNPRINTF(MPII_D_MISC, "%s: unable to fetch IOC page 1\n" + "page 1\n", DEVNAME(sc)); + return (1); + } + + DNPRINTF(MPII_D_MISC, "%s: IOC page 1\n", DEVNAME(sc)); + DNPRINTF(MPII_D_MISC, "%s: flags: 0x08%x\n", DEVNAME(sc), + letoh32(pg.flags)); + DNPRINTF(MPII_D_MISC, "%s: coalescing_timeout: %d\n", DEVNAME(sc), + letoh32(pg.coalescing_timeout)); + DNPRINTF(MPII_D_MISC, "%s: coalescing_depth: %d pci_slot_num: %d\n", + DEVNAME(sc), pg.coalescing_timeout, pg.pci_slot_num); + + flags = letoh32(pg.flags); + if (!ISSET(flags, MPII_CFG_IOC_1_REPLY_COALESCING)) + return (0); + + CLR(pg.flags, htole32(MPII_CFG_IOC_1_REPLY_COALESCING)); + if (mpii_cfg_page(sc, 0, &hdr, 0, &pg, sizeof(pg)) != 0) { + DNPRINTF(MPII_D_MISC, "%s: unable to clear coalescing\n", + DEVNAME(sc)); + return (1); + } + + return (0); +} + +void +mpii_get_raid_config_pg0(struct mpii_softc *sc) +{ + struct mpii_cfg_raid_config_pg0 *config_page; + struct mpii_raid_config_element *element_list, *element; + struct mpii_ecfg_hdr hdr; + size_t pagelen; + struct scsi_link *link; + int i; + + DNPRINTF(MPII_D_RAID, "%s: mpii_get_raid_config_pg0\n", DEVNAME(sc)); + + if (mpii_cfg_header(sc, MPII_CONFIG_REQ_PAGE_TYPE_RAID_VOL, 0, 0, &hdr) != 0) { + DNPRINTF(MPII_D_RAID, "%s: mpii_get_raid_config_pg0 unable to " + "fetch header for IOC page 2\n", DEVNAME(sc)); + return; + } + + pagelen = hdr.ext_page_length * 4; /* dwords to bytes */ + config_page = malloc(pagelen, M_TEMP, M_WAITOK|M_CANFAIL); + if (config_page == NULL) { + DNPRINTF(MPII_D_RAID, "%s: mpii_get_raid_config_pg0 unable to " + "allocate space for raid config page 0\n", DEVNAME(sc)); + return; + } + element_list = (struct mpii_raid_config_element *)(config_page + 1); + + if (mpii_cfg_page(sc, 0, &hdr, 1, config_page, pagelen) != 0) { + DNPRINTF(MPII_D_RAID, "%s: mpii_get_raid_config_pg0 unable to " + "fetch raid config page 0\n", DEVNAME(sc)); + goto out; + } + + DNPRINTF(MPII_D_RAID, "%s: numhotspares: 0x%2x numphysdisks: 0x%02x " + "numvolumes: 0x%02x confignum: 0x%02x\n", DEVNAME(sc), + config_page->num_hot_spares, config_page->num_phys_disks, + config_page->num_volumes, config_page->config_num); + DNPRINTF(MPII_D_RAID, "%s: flags: 0x%08x\n", DEVNAME(sc), + config_page->flags); + DNPRINTF(MPII_D_RAID, "%s: configguid: 0x%08x\n", DEVNAME(sc), + config_page->config_guid[0]); + DNPRINTF(MPII_D_RAID, "%s: 0x%08x\n", DEVNAME(sc), + config_page->config_guid[1]); + DNPRINTF(MPII_D_RAID, "%s: 0x%08x\n", DEVNAME(sc), + config_page->config_guid[2]); + DNPRINTF(MPII_D_RAID, "%s: 0x%08x\n", DEVNAME(sc), + config_page->config_guid[3]); + DNPRINTF(MPII_D_RAID, "%s numelements: 0x%02x\n", DEVNAME(sc), + config_page->num_elements); + + /* don't walk list if there are no RAID capability */ + /* XXX anything like this for MPI2? + if (capabilities == 0xdeadbeef) { + printf("%s: deadbeef in raid configuration\n", DEVNAME(sc)); + goto out; + } + */ + + if (config_page->num_volumes == 0) + goto out; + + sc->sc_flags |= MPII_F_RAID; + + for (i = 0; i < config_page->num_volumes; i++) { + element = &element_list[i]; + + DNPRINTF(MPII_D_RAID, "%s: elementflags: 0x%04x voldevhandle: " + "0x%04x\n", DEVNAME(sc), letoh16(element->element_flags), + letoh16(element->vol_dev_handle)); + DNPRINTF(MPII_D_RAID, "%s: hotsparepool: 0x%02x physdisknum: " + "0x%02x physdiskdevhandle: 0x%04x\n", DEVNAME(sc), + element->hot_spare_pool, element->phys_disk_num, + letoh16(element->phys_disk_dev_handle)); + + link = sc->sc_scsibus->sc_link[element->vol_dev_handle][0]; + if (link == NULL) + continue; + + link->flags |= SDEV_VIRTUAL; + } + +out: + free(config_page, M_TEMP); +} + +void +mpii_get_ioc_pg8(struct mpii_softc *sc) +{ + struct mpii_cfg_raid_vol *vol_list; + struct mpii_cfg_hdr hdr; + struct mpii_cfg_ioc_pg8 *vol_page; + size_t pagelen; + + DNPRINTF(MPII_D_RAID, "%s: mpii_get_ioc_pg8\n", DEVNAME(sc)); + + if (mpii_cfg_header(sc, MPII_CONFIG_REQ_PAGE_TYPE_IOC, 8, 0, &hdr) != 0) { + DNPRINTF(MPII_D_RAID, "%s: mpii_get_ioc_pg8 unable to fetch header" + "for IOC page 8\n", DEVNAME(sc)); + return; + } + + pagelen = hdr.page_length * 4; /* dwords to bytes */ + vol_page = malloc(pagelen, M_TEMP, M_WAITOK|M_CANFAIL); + if (vol_page == NULL) { + DNPRINTF(MPII_D_RAID, "%s: mpii_get_ioc_pg8 unable to allocate " + "space for ioc config page 8\n", DEVNAME(sc)); + return; + } + vol_list = (struct mpii_cfg_raid_vol *)(vol_page + 1); + + if (mpii_cfg_page(sc, 0, &hdr, 1, vol_page, pagelen) != 0) { + DNPRINTF(MPII_D_RAID, "%s: mpii_get_raid unable to fetch IOC " + "page 8\n", DEVNAME(sc)); + goto out; + } + + DNPRINTF(MPII_D_RAID, "%s: numdevsperenclosure: 0x%02x\n", DEVNAME(sc), + vol_page->num_devs_per_enclosure); + DNPRINTF(MPII_D_RAID, "%s: maxpersistententries: 0x%04x " + "maxnumphysicalmappedids: 0x%04x\n", DEVNAME(sc), + letoh16(vol_page->max_persistent_entries), + letoh16(vol_page->max_num_physical_mapped_ids)); + DNPRINTF(MPII_D_RAID, "%s: flags: 0x%04x\n", DEVNAME(sc), + letoh16(vol_page->flags)); + DNPRINTF(MPII_D_RAID, "%s: irvolumemappingflags: 0x%04x\n", + DEVNAME(sc), letoh16(vol_page->ir_volume_mapping_flags)); + +out: + free(vol_page, M_TEMP); +} + +int +mpii_req_cfg_header(struct mpii_softc *sc, u_int8_t type, u_int8_t number, + u_int32_t address, int extended, void *p) +{ + struct mpii_msg_config_request *cq; + struct mpii_msg_config_reply *cp; + struct mpii_ccb *ccb; + struct mpii_cfg_hdr *hdr = p; + struct mpii_ecfg_hdr *ehdr = p; + int etype = 0; + int rv = 0; + int s; + + DNPRINTF(MPII_D_MISC, "%s: mpii_req_cfg_header type: %#x number: %x " + "address: 0x%08x extended: %d\n", DEVNAME(sc), type, number, + address, extended); + + s = splbio(); + ccb = mpii_get_ccb(sc); + splx(s); + if (ccb == NULL) { + DNPRINTF(MPII_D_MISC, "%s: mpii_cfg_header ccb_get\n", + DEVNAME(sc)); + return (1); + } + + if (extended) { + etype = type; + type = MPII_CONFIG_REQ_PAGE_TYPE_EXTENDED; + } + + ccb->ccb_done = mpii_empty_done; + cq = ccb->ccb_cmd; + + cq->function = MPII_FUNCTION_CONFIG; + + cq->action = MPII_CONFIG_REQ_ACTION_PAGE_HEADER; + + cq->config_header.page_number = number; + cq->config_header.page_type = type; + cq->ext_page_type = etype; + cq->page_address = htole32(address); + cq->page_buffer.sg_hdr = htole32(MPII_SGE_FL_TYPE_SIMPLE | + MPII_SGE_FL_LAST | MPII_SGE_FL_EOB | MPII_SGE_FL_EOL); + + if (mpii_poll(sc, ccb, 50000) != 0) { + DNPRINTF(MPII_D_MISC, "%s: mpii_cfg_header poll\n", DEVNAME(sc)); + return (1); + } + + if (ccb->ccb_rcb == NULL) + panic("%s: unable to fetch config header\n", DEVNAME(sc)); + cp = ccb->ccb_rcb->rcb_reply; + + DNPRINTF(MPII_D_MISC, "%s: action: 0x%02x sgl_flags: 0x%02x " + "msg_length: %d function: 0x%02x\n", DEVNAME(sc), cp->action, + cp->sgl_flags, cp->msg_length, cp->function); + DNPRINTF(MPII_D_MISC, "%s: ext_page_length: %d ext_page_type: 0x%02x " + "msg_flags: 0x%02x\n", DEVNAME(sc), + letoh16(cp->ext_page_length), cp->ext_page_type, + cp->msg_flags); + DNPRINTF(MPII_D_MISC, "%s: vp_id: 0x%02x vf_id: 0x%02x\n", DEVNAME(sc), + cp->vp_id, cp->vf_id); + DNPRINTF(MPII_D_MISC, "%s: ioc_status: 0x%04x\n", DEVNAME(sc), + letoh16(cp->ioc_status)); + DNPRINTF(MPII_D_MISC, "%s: ioc_loginfo: 0x%08x\n", DEVNAME(sc), + letoh32(cp->ioc_loginfo)); + DNPRINTF(MPII_D_MISC, "%s: page_version: 0x%02x page_length: %d " + "page_number: 0x%02x page_type: 0x%02x\n", DEVNAME(sc), + cp->config_header.page_version, + cp->config_header.page_length, + cp->config_header.page_number, + cp->config_header.page_type); + + if (letoh16(cp->ioc_status) != MPII_IOCSTATUS_SUCCESS) + rv = 1; + else if (extended) { + bzero(ehdr, sizeof(*ehdr)); + ehdr->page_version = cp->config_header.page_version; + ehdr->page_number = cp->config_header.page_number; + ehdr->page_type = cp->config_header.page_type; + ehdr->ext_page_length = cp->ext_page_length; + ehdr->ext_page_type = cp->ext_page_type; + } else + *hdr = cp->config_header; + + mpii_push_reply(sc, ccb->ccb_rcb->rcb_reply_dva); + mpii_put_ccb(sc, ccb); + + return (rv); +} + +int +mpii_req_cfg_page(struct mpii_softc *sc, u_int32_t address, int extended, + void *p, int read, void *page, size_t len) +{ + struct mpii_msg_config_request *cq; + struct mpii_msg_config_reply *cp; + struct mpii_ccb *ccb; + struct mpii_cfg_hdr *hdr = p; + struct mpii_ecfg_hdr *ehdr = p; + u_int64_t dva; + char *kva; + int page_length; + int rv = 0; + int s; + + DNPRINTF(MPII_D_MISC, "%s: mpii_req_cfg_page address: %d read: %d type: %x\n", + DEVNAME(sc), address, read, hdr->page_type); + + page_length = extended ? + letoh16(ehdr->ext_page_length) : hdr->page_length; + + if (len > MPII_REQUEST_SIZE - sizeof(struct mpii_msg_config_request) || + len < page_length * 4) + return (1); + + s = splbio(); + ccb = mpii_get_ccb(sc); + splx(s); + if (ccb == NULL) { + DNPRINTF(MPII_D_MISC, "%s: mpii_cfg_page ccb_get\n", DEVNAME(sc)); + return (1); + } + + ccb->ccb_done = mpii_empty_done; + cq = ccb->ccb_cmd; + + cq->function = MPII_FUNCTION_CONFIG; + + cq->action = (read ? MPII_CONFIG_REQ_ACTION_PAGE_READ_CURRENT : + MPII_CONFIG_REQ_ACTION_PAGE_WRITE_CURRENT); + + if (extended) { + cq->config_header.page_version = ehdr->page_version; + cq->config_header.page_number = ehdr->page_number; + cq->config_header.page_type = ehdr->page_type; + cq->ext_page_len = ehdr->ext_page_length; + cq->ext_page_type = ehdr->ext_page_type; + } else + cq->config_header = *hdr; + cq->config_header.page_type &= MPII_CONFIG_REQ_PAGE_TYPE_MASK; + cq->page_address = htole32(address); + cq->page_buffer.sg_hdr = htole32(MPII_SGE_FL_TYPE_SIMPLE | + MPII_SGE_FL_LAST | MPII_SGE_FL_EOB | MPII_SGE_FL_EOL | + (page_length * 4) | + (read ? MPII_SGE_FL_DIR_IN : MPII_SGE_FL_DIR_OUT)); + + /* bounce the page via the request space to avoid more bus_dma games */ + dva = ccb->ccb_cmd_dva + sizeof(struct mpii_msg_config_request); + + cq->page_buffer.sg_hi_addr = htole32((u_int32_t)(dva >> 32)); + cq->page_buffer.sg_lo_addr = htole32((u_int32_t)dva); + + kva = ccb->ccb_cmd; + kva += sizeof(struct mpii_msg_config_request); + if (!read) + bcopy(page, kva, len); + + if (mpii_poll(sc, ccb, 50000) != 0) { + DNPRINTF(MPII_D_MISC, "%s: mpii_cfg_page poll\n", DEVNAME(sc)); + return (1); + } + + if (ccb->ccb_rcb == NULL) { + mpii_put_ccb(sc, ccb); + return (1); + } + cp = ccb->ccb_rcb->rcb_reply; + + DNPRINTF(MPII_D_MISC, "%s: action: 0x%02x sglflags: 0x%02x " + "msg_length: %d function: 0x%02x\n", DEVNAME(sc), cp->action, + cp->msg_length, cp->function); + DNPRINTF(MPII_D_MISC, "%s: ext_page_length: %d ext_page_type: 0x%02x " + "msg_flags: 0x%02x\n", DEVNAME(sc), + letoh16(cp->ext_page_length), cp->ext_page_type, + cp->msg_flags); + DNPRINTF(MPII_D_MISC, "%s: vp_id: 0x%02x vf_id: 0x%02x\n", DEVNAME(sc), + cp->vp_id, cp->vf_id); + DNPRINTF(MPII_D_MISC, "%s: ioc_status: 0x%04x\n", DEVNAME(sc), + letoh16(cp->ioc_status)); + DNPRINTF(MPII_D_MISC, "%s: ioc_loginfo: 0x%08x\n", DEVNAME(sc), + letoh32(cp->ioc_loginfo)); + DNPRINTF(MPII_D_MISC, "%s: page_version: 0x%02x page_length: %d " + "page_number: 0x%02x page_type: 0x%02x\n", DEVNAME(sc), + cp->config_header.page_version, + cp->config_header.page_length, + cp->config_header.page_number, + cp->config_header.page_type); + + if (letoh16(cp->ioc_status) != MPII_IOCSTATUS_SUCCESS) + rv = 1; + else if (read) + bcopy(kva, page, len); + + mpii_push_reply(sc, ccb->ccb_rcb->rcb_reply_dva); + mpii_put_ccb(sc, ccb); + + return (rv); +} + +int +mpii_reply(struct mpii_softc *sc) +{ + struct mpii_reply_descriptor *rdp; + struct mpii_ccb *ccb; + struct mpii_rcb *rcb = NULL; + struct mpii_msg_reply *reply = NULL; + u_int8_t reply_flags; + u_int32_t reply_dva, i; + int smid; + + + DNPRINTF(MPII_D_INTR, "%s: mpii_reply\n", DEVNAME(sc)); + + /* XXX need to change to to be just the reply we expect to read */ + bus_dmamap_sync(sc->sc_dmat, MPII_DMA_MAP(sc->sc_reply_postq), 0, + 8 * sc->sc_reply_post_qdepth, BUS_DMASYNC_POSTWRITE); + + rdp = &sc->sc_reply_postq_kva[sc->sc_reply_post_host_index]; + + reply_flags = (u_int8_t)(rdp->reply_flags) & + MPII_REPLY_DESCR_FLAGS_TYPE_MASK; + + if ((reply_flags == MPII_REPLY_DESCR_FLAGS_UNUSED)) + return (-1); + + if (dwordn(rdp, 1) == 0xffffffff) + /* + * ioc is still writing to the reply post queue + * race condition - bail! + */ + return (0); + + DNPRINTF(MPII_D_INTR, "%s: dword[0]: 0x%08x\n", DEVNAME(sc), + letoh32(dwordn(rdp, 0))); + DNPRINTF(MPII_D_INTR, "%s: dword[1]: 0x%08x\n", DEVNAME(sc), + letoh32(dwordn(rdp, 1))); + + switch (reply_flags) { + case MPII_REPLY_DESCR_FLAGS_ADDRESS_REPLY: + /* reply frame address */ + reply_dva = letoh32(rdp->type_dependent2); + i = (reply_dva - (u_int32_t)MPII_DMA_DVA(sc->sc_replies)) / + MPII_REPLY_SIZE; + + bus_dmamap_sync(sc->sc_dmat, + MPII_DMA_MAP(sc->sc_replies), MPII_REPLY_SIZE * i, + MPII_REPLY_SIZE, BUS_DMASYNC_POSTREAD); + + rcb = &sc->sc_rcbs[i]; + reply = rcb->rcb_reply; + /* fall through */ + default: + /* smid */ + smid = letoh16(rdp->type_dependent1); + } + + DNPRINTF(MPII_D_INTR, "%s: mpii_reply reply_flags: %d smid: %d reply: %p\n", + DEVNAME(sc), reply_flags, smid, reply); + + if (smid == 0) { + printf("smid == 0 !!\n"); + goto end; + } + ccb = &sc->sc_ccbs[smid - 1]; + + /* XXX why is this necessary ? */ + bus_dmamap_sync(sc->sc_dmat, MPII_DMA_MAP(sc->sc_requests), + ccb->ccb_offset, MPII_REQUEST_SIZE, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + ccb->ccb_state = MPII_CCB_READY; + ccb->ccb_rcb = rcb; + + DNPRINTF(MPII_D_INTR, " rcb: 0x%04x\n", rcb); + + dwordn(rdp, 0) = 0xffffffff; + dwordn(rdp, 1) = 0xffffffff; + + bus_dmamap_sync(sc->sc_dmat, MPII_DMA_MAP(sc->sc_reply_postq), + 8 * sc->sc_reply_post_host_index, 8, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + sc->sc_reply_post_host_index = (sc->sc_reply_post_host_index + 1) % + sc->sc_reply_post_qdepth; + ccb->ccb_done(ccb); + +end: + + return (smid); +} + +struct mpii_dmamem * +mpii_dmamem_alloc(struct mpii_softc *sc, size_t size) +{ + struct mpii_dmamem *mdm; + int nsegs; + + mdm = malloc(sizeof(struct mpii_dmamem), M_DEVBUF, M_NOWAIT | M_ZERO); + if (mdm == NULL) + return (NULL); + + mdm->mdm_size = size; + + if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, + BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &mdm->mdm_map) != 0) + goto mdmfree; + + if (bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &mdm->mdm_seg, + 1, &nsegs, BUS_DMA_NOWAIT) != 0) + goto destroy; + + if (bus_dmamem_map(sc->sc_dmat, &mdm->mdm_seg, nsegs, size, + &mdm->mdm_kva, BUS_DMA_NOWAIT) != 0) + goto free; + + if (bus_dmamap_load(sc->sc_dmat, mdm->mdm_map, mdm->mdm_kva, size, + NULL, BUS_DMA_NOWAIT) != 0) + goto unmap; + + bzero(mdm->mdm_kva, size); + + DNPRINTF(MPII_D_MEM, "%s: mpii_dmamem_alloc size: %d mdm: %#x " + "map: %#x nsegs: %d segs: %#x kva: %x\n", + DEVNAME(sc), size, mdm->mdm_map, nsegs, mdm->mdm_seg, mdm->mdm_kva); + + return (mdm); + +unmap: + bus_dmamem_unmap(sc->sc_dmat, mdm->mdm_kva, size); +free: + bus_dmamem_free(sc->sc_dmat, &mdm->mdm_seg, 1); +destroy: + bus_dmamap_destroy(sc->sc_dmat, mdm->mdm_map); +mdmfree: + free(mdm, M_DEVBUF); + + return (NULL); +} + +void +mpii_dmamem_free(struct mpii_softc *sc, struct mpii_dmamem *mdm) +{ + DNPRINTF(MPII_D_MEM, "%s: mpii_dmamem_free %#x\n", DEVNAME(sc), mdm); + + bus_dmamap_unload(sc->sc_dmat, mdm->mdm_map); + bus_dmamem_unmap(sc->sc_dmat, mdm->mdm_kva, mdm->mdm_size); + bus_dmamem_free(sc->sc_dmat, &mdm->mdm_seg, 1); + bus_dmamap_destroy(sc->sc_dmat, mdm->mdm_map); + free(mdm, M_DEVBUF); +} + +int +mpii_alloc_ccbs(struct mpii_softc *sc) +{ + struct mpii_ccb *ccb; + u_int8_t *cmd; + int i; + + TAILQ_INIT(&sc->sc_ccb_free); + + sc->sc_ccbs = malloc(sizeof(struct mpii_ccb) * (sc->sc_request_depth-1), + M_DEVBUF, M_WAITOK | M_CANFAIL | M_ZERO); + if (sc->sc_ccbs == NULL) { + printf("%s: unable to allocate ccbs\n", DEVNAME(sc)); + return (1); + } + + sc->sc_requests = mpii_dmamem_alloc(sc, + MPII_REQUEST_SIZE * sc->sc_request_depth); + if (sc->sc_requests == NULL) { + printf("%s: unable to allocate ccb dmamem\n", DEVNAME(sc)); + goto free_ccbs; + } + cmd = MPII_DMA_KVA(sc->sc_requests); + bzero(cmd, MPII_REQUEST_SIZE * sc->sc_request_depth); + + /* + * we have sc->sc_request_depth system request message frames, but + * smid zero cannot be used. + * + * we then have (sc->sc_request_depth - 1) number of ccbs + */ + for (i = 1; i < sc->sc_request_depth; i++) { + ccb = &sc->sc_ccbs[i - 1]; + + if (bus_dmamap_create(sc->sc_dmat, MAXPHYS, + sc->sc_max_sgl_len, MAXPHYS, 0, + BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, + &ccb->ccb_dmamap) != 0) { + printf("%s: unable to create dma map\n", DEVNAME(sc)); + goto free_maps; + } + + ccb->ccb_sc = sc; + ccb->ccb_smid = i; + ccb->ccb_offset = MPII_REQUEST_SIZE * i; + + ccb->ccb_cmd = &cmd[ccb->ccb_offset]; + ccb->ccb_cmd_dva = (u_int32_t)MPII_DMA_DVA(sc->sc_requests) + + ccb->ccb_offset; + + DNPRINTF(MPII_D_CCB, "%s: mpii_alloc_ccbs(%d) ccb: %#x map: %#x " + "sc: %#x smid: %#x offs: %#x cmd: %#x dva: %#x\n", + DEVNAME(sc), i, ccb, ccb->ccb_dmamap, ccb->ccb_sc, + ccb->ccb_smid, ccb->ccb_offset, ccb->ccb_cmd, + ccb->ccb_cmd_dva); + + mpii_put_ccb(sc, ccb); + } + + return (0); + +free_maps: + while ((ccb = mpii_get_ccb(sc)) != NULL) + bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap); + + mpii_dmamem_free(sc, sc->sc_requests); +free_ccbs: + free(sc->sc_ccbs, M_DEVBUF); + + return (1); +} + +void +mpii_put_ccb(struct mpii_softc *sc, struct mpii_ccb *ccb) +{ + DNPRINTF(MPII_D_CCB, "%s: mpii_put_ccb %#x\n", DEVNAME(sc), ccb); + + ccb->ccb_state = MPII_CCB_FREE; + ccb->ccb_xs = NULL; + ccb->ccb_done = NULL; + bzero(ccb->ccb_cmd, MPII_REQUEST_SIZE); + TAILQ_INSERT_TAIL(&sc->sc_ccb_free, ccb, ccb_link); +} + +struct mpii_ccb * +mpii_get_ccb(struct mpii_softc *sc) +{ + struct mpii_ccb *ccb; + + ccb = TAILQ_FIRST(&sc->sc_ccb_free); + if (ccb == NULL) { + DNPRINTF(MPII_D_CCB, "%s: mpii_get_ccb == NULL\n", DEVNAME(sc)); + return (NULL); + } + + TAILQ_REMOVE(&sc->sc_ccb_free, ccb, ccb_link); + + ccb->ccb_state = MPII_CCB_READY; + + DNPRINTF(MPII_D_CCB, "%s: mpii_get_ccb %#x\n", DEVNAME(sc), ccb); + + return (ccb); +} + +int +mpii_alloc_replies(struct mpii_softc *sc) +{ + DNPRINTF(MPII_D_MISC, "%s: mpii_alloc_replies\n", DEVNAME(sc)); + + sc->sc_rcbs = malloc(sc->sc_num_reply_frames * sizeof(struct mpii_rcb), + M_DEVBUF, M_WAITOK|M_CANFAIL); + if (sc->sc_rcbs == NULL) + return (1); + + sc->sc_replies = mpii_dmamem_alloc(sc, + MPII_REPLY_SIZE * sc->sc_num_reply_frames); + if (sc->sc_replies == NULL) { + free(sc->sc_rcbs, M_DEVBUF); + return (1); + } + + return (0); +} + +void +mpii_push_replies(struct mpii_softc *sc) +{ + struct mpii_rcb *rcb; + char *kva = MPII_DMA_KVA(sc->sc_replies); + int i; + + bus_dmamap_sync(sc->sc_dmat, MPII_DMA_MAP(sc->sc_replies), + 0, MPII_REPLY_SIZE * sc->sc_num_reply_frames, BUS_DMASYNC_PREREAD); + + for (i = 0; i < sc->sc_num_reply_frames; i++) { + rcb = &sc->sc_rcbs[i]; + + rcb->rcb_reply = kva + MPII_REPLY_SIZE * i; + rcb->rcb_reply_dva = (u_int32_t)MPII_DMA_DVA(sc->sc_replies) + + MPII_REPLY_SIZE * i; + mpii_push_reply(sc, rcb->rcb_reply_dva); + } +} +void +mpii_start(struct mpii_softc *sc, struct mpii_ccb *ccb) +{ + struct mpii_request_header *rhp; + struct mpii_request_descriptor descriptor; + + DNPRINTF(MPII_D_RW, "%s: mpii_start %#x\n", DEVNAME(sc), + ccb->ccb_cmd_dva); + + rhp = ccb->ccb_cmd; + + bzero(&descriptor, sizeof(struct mpii_request_descriptor)); + + switch (rhp->function) { + case MPII_FUNCTION_SCSI_IO_REQUEST: + descriptor.request_flags = + MPII_REQ_DESCRIPTOR_FLAGS_SCSI_IO; + /* device handle */ + descriptor.type_dependent = + htole16(ccb->ccb_dev_handle); + break; + case MPII_FUNCTION_SCSI_TASK_MGMT: + descriptor.request_flags = + MPII_REQ_DESCRIPTOR_FLAGS_HIGH_PRIORITY; + break; + default: + descriptor.request_flags = + MPII_REQ_DESCRIPTOR_FLAGS_DEFAULT; + } + + descriptor.vf_id = sc->sc_vf_id; + descriptor.smid = htole16(ccb->ccb_smid); + + bus_dmamap_sync(sc->sc_dmat, MPII_DMA_MAP(sc->sc_requests), + ccb->ccb_offset, MPII_REQUEST_SIZE, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + ccb->ccb_state = MPII_CCB_QUEUED; + + DNPRINTF(MPII_D_RW, "%s: MPII_REQ_DESC_POST_LOW (0x%08x) write " + "0x%08x\n", DEVNAME(sc), MPII_REQ_DESC_POST_LOW, + dwordn(&descriptor, 0)); + + DNPRINTF(MPII_D_RW, "%s: MPII_REQ_DESC_POST_HIGH (0x%08x) write " + "0x%08x\n", DEVNAME(sc), MPII_REQ_DESC_POST_HIGH, + dwordn(&descriptor, 1)); + + /* XXX make this 64 bit? */ + mpii_write(sc, MPII_REQ_DESC_POST_LOW, htole32(dwordn(&descriptor, 0))); + mpii_write(sc, MPII_REQ_DESC_POST_HIGH, + htole32(dwordn(&descriptor, 1))); +} + +int +mpii_complete(struct mpii_softc *sc, struct mpii_ccb *ccb, int timeout) +{ + int smid = -1; + + DNPRINTF(MPII_D_INTR, "%s: mpii_complete timeout %d\n", DEVNAME(sc), + timeout); + + do { + /* avoid excessive polling */ + if (!mpii_reply_waiting(sc)) { + if (timeout-- == 0) + return (1); + + delay(1000); + continue; + } + + smid = mpii_reply(sc); + + /* generates PCI write every completed reply, but + * prevents deadlock waiting for specific smid + */ + mpii_write_reply_post(sc, sc->sc_reply_post_host_index); + + DNPRINTF(MPII_D_INTR, "%s: mpii_complete call to mpii_reply returned: %d\n", + DEVNAME(sc), smid); + + } while (ccb->ccb_smid != smid); + + return (0); +} + +int +mpii_alloc_queues(struct mpii_softc *sc) +{ + u_int32_t *kva; + u_int64_t *kva64; + int i; + + DNPRINTF(MPII_D_MISC, "%s: mpii_alloc_queues\n", DEVNAME(sc)); + + sc->sc_reply_freeq = mpii_dmamem_alloc(sc, + sc->sc_reply_free_qdepth * 4); + if (sc->sc_reply_freeq == NULL) + return (1); + + kva = MPII_DMA_KVA(sc->sc_reply_freeq); + for (i = 0; i < sc->sc_num_reply_frames; i++) { + kva[i] = (u_int32_t)MPII_DMA_DVA(sc->sc_replies) + + MPII_REPLY_SIZE * i; + + DNPRINTF(MPII_D_MISC, "%s: %d: 0x%08x = 0x%08x\n", + DEVNAME(sc), i, + &kva[i], (u_int32_t)MPII_DMA_DVA(sc->sc_replies) + + MPII_REPLY_SIZE * i); + } + + sc->sc_reply_postq = + mpii_dmamem_alloc(sc, sc->sc_reply_post_qdepth * 8); + if (sc->sc_reply_postq == NULL) + goto free_reply_freeq; + sc->sc_reply_postq_kva = MPII_DMA_KVA(sc->sc_reply_postq); + + DNPRINTF(MPII_D_MISC, "%s: populating reply post descriptor queue\n", + DEVNAME(sc)); + kva64 = (u_int64_t *)MPII_DMA_KVA(sc->sc_reply_postq); + for (i = 0; i < sc->sc_reply_post_qdepth; i++) { + kva64[i] = 0xffffffffffffffff; + DNPRINTF(MPII_D_MISC, "%s: %d: 0x%08x = 0x%lx\n", + DEVNAME(sc), i, &kva64[i], kva64[i]); + } + + return (0); + +free_reply_freeq: + + mpii_dmamem_free(sc, sc->sc_reply_freeq); + return (1); +} + +void +mpii_init_queues(struct mpii_softc *sc) +{ + DNPRINTF(MPII_D_MISC, "%s: mpii_init_queues\n", DEVNAME(sc)); + + sc->sc_reply_free_host_index = sc->sc_reply_free_qdepth - 1; + sc->sc_reply_post_host_index = 0; + mpii_write_reply_free(sc, sc->sc_reply_free_host_index); + mpii_write_reply_post(sc, sc->sc_reply_post_host_index); +} + + + + +int +mpii_poll(struct mpii_softc *sc, struct mpii_ccb *ccb, int timeout) +{ + int error; + int s; + + DNPRINTF(MPII_D_CMD, "%s: mpii_poll\n", DEVNAME(sc)); + + DNPRINTF(MPII_D_CMD, " ccb->ccb_cmd: 0x%08x\n", DEVNAME(sc), ccb->ccb_cmd); + s = splbio(); + mpii_start(sc, ccb); + error = mpii_complete(sc, ccb, timeout); + splx(s); + + return (error); +} + +int +mpii_scsi_cmd(struct scsi_xfer *xs) +{ + struct scsi_link *link = xs->sc_link; + struct mpii_softc *sc = link->adapter_softc; + struct mpii_ccb *ccb; + struct mpii_ccb_bundle *mcb; + struct mpii_msg_scsi_io *io; + int s; + + DNPRINTF(MPII_D_CMD, "%s: mpii_scsi_cmd\n", DEVNAME(sc)); + + if (xs->cmdlen > MPII_CDB_LEN) { + DNPRINTF(MPII_D_CMD, "%s: CBD too big %d\n", + DEVNAME(sc), xs->cmdlen); + bzero(&xs->sense, sizeof(xs->sense)); + xs->sense.error_code = SSD_ERRCODE_VALID | 0x70; + xs->sense.flags = SKEY_ILLEGAL_REQUEST; + xs->sense.add_sense_code = 0x20; + xs->error = XS_SENSE; + xs->flags |= ITSDONE; + s = splbio(); + scsi_done(xs); + splx(s); + return (COMPLETE); + } + + s = splbio(); + ccb = mpii_get_ccb(sc); + splx(s); + if (ccb == NULL) + return (NO_CCB); + + DNPRINTF(MPII_D_CMD, "%s: ccb_smid: %d xs->flags: 0x%x\n", + DEVNAME(sc), ccb->ccb_smid, xs->flags); + + ccb->ccb_xs = xs; + ccb->ccb_done = mpii_scsi_cmd_done; + + mcb = ccb->ccb_cmd; + io = &mcb->mcb_io; + + io->function = MPII_FUNCTION_SCSI_IO_REQUEST; + io->sense_buffer_length = sizeof(xs->sense); + io->sgl_offset0 = 24; /* XXX fix this */ + io->io_flags = htole16(xs->cmdlen); + io->dev_handle = htole32(link->target); + io->lun[0] = htobe16(link->lun); + + switch (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) { + case SCSI_DATA_IN: + io->direction = MPII_SCSIIO_DIR_READ; + break; + case SCSI_DATA_OUT: + io->direction = MPII_SCSIIO_DIR_WRITE; + break; + default: + io->direction = MPII_SCSIIO_DIR_NONE; + break; + } + + io->tagging = MPII_SCSIIO_ATTR_SIMPLE_Q; + + bcopy(xs->cmd, io->cdb, xs->cmdlen); + + io->data_length = htole32(xs->datalen); + + io->sense_buffer_low_address = htole32(ccb->ccb_cmd_dva + + ((u_int8_t *)&mcb->mcb_sense - (u_int8_t *)mcb)); + + if (mpii_load_xs(ccb) != 0) { + xs->error = XS_DRIVER_STUFFUP; + xs->flags |= ITSDONE; + s = splbio(); + mpii_put_ccb(sc, ccb); + scsi_done(xs); + splx(s); + return (COMPLETE); + } + + timeout_set(&xs->stimeout, mpii_timeout_xs, ccb); + + DNPRINTF(MPII_D_CMD, "%s: sizeof(mpii_msg_scsi_io): %d " + "sizeof(mpii_ccb_bundle): %d sge offset: 0x%02x\n", + DEVNAME(sc), sizeof(struct mpii_msg_scsi_io), + sizeof(struct mpii_ccb_bundle), + (u_int8_t *)&mcb->mcb_sgl[0] - (u_int8_t *)mcb); + + DNPRINTF(MPII_D_CMD, "%s sgl[0]: 0x%04x 0%04x 0x%04x\n", + DEVNAME(sc), mcb->mcb_sgl[0].sg_hdr, mcb->mcb_sgl[0].sg_lo_addr, + mcb->mcb_sgl[0].sg_hi_addr); + + DNPRINTF(MPII_D_CMD, "%s: Offset0: 0x%02x\n", DEVNAME(sc), + io->sgl_offset0); + + if (xs->flags & SCSI_POLL) { + if (mpii_poll(sc, ccb, xs->timeout) != 0) { + xs->error = XS_DRIVER_STUFFUP; + xs->flags |= ITSDONE; + s = splbio(); + scsi_done(xs); + splx(s); + } + return (COMPLETE); + } + + DNPRINTF(MPII_D_CMD, "%s: mpii_scsi_cmd(): opcode: %02x datalen: %d " + "req_sense_len: %d\n", DEVNAME(sc), xs->cmd->opcode, + xs->datalen, xs->req_sense_length); + + s = splbio(); + mpii_start(sc, ccb); + splx(s); + return (SUCCESSFULLY_QUEUED); +} + +void +mpii_scsi_cmd_done(struct mpii_ccb *ccb) +{ + struct mpii_msg_scsi_io_error *sie; + struct mpii_softc *sc = ccb->ccb_sc; + struct scsi_xfer *xs = ccb->ccb_xs; + struct mpii_ccb_bundle *mcb = ccb->ccb_cmd; + bus_dmamap_t dmap = ccb->ccb_dmamap; + + if (xs->datalen != 0) { + bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize, + (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_POSTREAD : + BUS_DMASYNC_POSTWRITE); + + bus_dmamap_unload(sc->sc_dmat, dmap); + } + + /* timeout_del */ + xs->error = XS_NOERROR; + xs->resid = 0; + xs->flags |= ITSDONE; + + if (ccb->ccb_rcb == NULL) { + /* no scsi error, we're ok so drop out early */ + xs->status = SCSI_OK; + mpii_put_ccb(sc, ccb); + scsi_done(xs); + return; + } + + sie = ccb->ccb_rcb->rcb_reply; + + DNPRINTF(MPII_D_CMD, "%s: mpii_scsi_cmd_done xs cmd: 0x%02x len: %d " + "flags 0x%x\n", DEVNAME(sc), xs->cmd->opcode, xs->datalen, + xs->flags); + DNPRINTF(MPII_D_CMD, "%s: dev_handle: %d msg_length: %d " + "function: 0x%02x\n", DEVNAME(sc), letoh16(sie->dev_handle), + sie->msg_length, sie->function); + DNPRINTF(MPII_D_CMD, "%s: vp_id: 0x%02x vf_id: 0x%02x\n", DEVNAME(sc), + sie->vp_id, sie->vf_id); + DNPRINTF(MPII_D_CMD, "%s: scsi_status: 0x%02x scsi_state: 0x%02x " + "ioc_status: 0x%04x\n", DEVNAME(sc), sie->scsi_status, + sie->scsi_state, letoh16(sie->ioc_status)); + DNPRINTF(MPII_D_CMD, "%s: ioc_loginfo: 0x%08x\n", DEVNAME(sc), + letoh32(sie->ioc_loginfo)); + DNPRINTF(MPII_D_CMD, "%s: transfer_count: %d\n", DEVNAME(sc), + letoh32(sie->transfer_count)); + DNPRINTF(MPII_D_CMD, "%s: sense_count: %d\n", DEVNAME(sc), + letoh32(sie->sense_count)); + DNPRINTF(MPII_D_CMD, "%s: response_info: 0x%08x\n", DEVNAME(sc), + letoh32(sie->response_info)); + DNPRINTF(MPII_D_CMD, "%s: task_tag: 0x%04x\n", DEVNAME(sc), + letoh16(sie->task_tag)); + DNPRINTF(MPII_D_CMD, "%s: bidirectional_transfer_count: 0x%08x\n", + DEVNAME(sc), letoh32(sie->bidirectional_transfer_count)); + + xs->status = sie->scsi_status; + switch (letoh16(sie->ioc_status)) { + case MPII_IOCSTATUS_SCSI_DATA_UNDERRUN: + xs->resid = xs->datalen - letoh32(sie->transfer_count); + if (sie->scsi_state & MPII_SCSIIO_ERR_STATE_NO_SCSI_STATUS) { + xs->error = XS_DRIVER_STUFFUP; + break; + } + /* FALLTHROUGH */ + case MPII_IOCSTATUS_SUCCESS: + case MPII_IOCSTATUS_SCSI_RECOVERED_ERROR: + switch (xs->status) { + case SCSI_OK: + xs->resid = 0; + break; + + case SCSI_CHECK: + xs->error = XS_SENSE; + break; + + case SCSI_BUSY: + case SCSI_QUEUE_FULL: + xs->error = XS_BUSY; + break; + + default: + xs->error = XS_DRIVER_STUFFUP; + break; + } + break; + + case MPII_IOCSTATUS_BUSY: + case MPII_IOCSTATUS_INSUFFICIENT_RESOURCES: + xs->error = XS_BUSY; + break; + + case MPII_IOCSTATUS_SCSI_INVALID_DEVHANDLE: + case MPII_IOCSTATUS_SCSI_DEVICE_NOT_THERE: + xs->error = XS_SELTIMEOUT; + break; + + default: + xs->error = XS_DRIVER_STUFFUP; + break; + } + + if (sie->scsi_state & MPII_SCSIIO_ERR_STATE_AUTOSENSE_VALID) + bcopy(&mcb->mcb_sense, &xs->sense, sizeof(xs->sense)); + + + DNPRINTF(MPII_D_CMD, "%s: xs err: 0x%02x status: %d\n", DEVNAME(sc), + xs->error, xs->status); + + mpii_push_reply(sc, ccb->ccb_rcb->rcb_reply_dva); + mpii_put_ccb(sc, ccb); + scsi_done(xs); +} |