5578 lines
146 KiB
C
5578 lines
146 KiB
C
/* $NetBSD: mpii.c,v 1.8 2016/05/02 19:18:29 christos Exp $ */
|
|
/* OpenBSD: mpii.c,v 1.51 2012/04/11 13:29:14 naddy Exp */
|
|
/*
|
|
* Copyright (c) 2010 Mike Belopuhov <mkb@crypt.org.ru>
|
|
* Copyright (c) 2009 James Giannoules
|
|
* Copyright (c) 2005 - 2010 David Gwynne <dlg@openbsd.org>
|
|
* Copyright (c) 2005 - 2010 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 <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: mpii.c,v 1.8 2016/05/02 19:18:29 christos Exp $");
|
|
|
|
#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/malloc.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/mutex.h>
|
|
#include <sys/condvar.h>
|
|
#include <sys/dkio.h>
|
|
#include <sys/tree.h>
|
|
|
|
#include <dev/pci/pcireg.h>
|
|
#include <dev/pci/pcivar.h>
|
|
#include <dev/pci/pcidevs.h>
|
|
|
|
#include <dev/scsipi/scsipi_all.h>
|
|
#include <dev/scsipi/scsi_all.h>
|
|
#include <dev/scsipi/scsiconf.h>
|
|
|
|
#include <dev/biovar.h>
|
|
#include <dev/sysmon/sysmonvar.h>
|
|
#include <sys/envsys.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 (0x02)
|
|
#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_DESCR_POST_LOW (0xc0)
|
|
#define MPII_REQ_DESCR_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)
|
|
|
|
/* Common IOCStatus values for all replies */
|
|
#define MPII_IOCSTATUS_MASK (0x7fff)
|
|
#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)
|
|
#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)
|
|
|
|
/* event notification types */
|
|
#define MPII_EVENT_NONE (0x00)
|
|
#define MPII_EVENT_LOG_DATA (0x01)
|
|
#define MPII_EVENT_STATE_CHANGE (0x02)
|
|
#define MPII_EVENT_HARD_RESET_RECEIVED (0x05)
|
|
#define MPII_EVENT_EVENT_CHANGE (0x0a)
|
|
#define MPII_EVENT_TASK_SET_FULL (0x0e)
|
|
#define MPII_EVENT_SAS_DEVICE_STATUS_CHANGE (0x0f)
|
|
#define MPII_EVENT_IR_OPERATION_STATUS (0x14)
|
|
#define MPII_EVENT_SAS_DISCOVERY (0x16)
|
|
#define MPII_EVENT_SAS_BROADCAST_PRIMITIVE (0x17)
|
|
#define MPII_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE (0x18)
|
|
#define MPII_EVENT_SAS_INIT_TABLE_OVERFLOW (0x19)
|
|
#define MPII_EVENT_SAS_TOPOLOGY_CHANGE_LIST (0x1c)
|
|
#define MPII_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE (0x1d)
|
|
#define MPII_EVENT_IR_VOLUME (0x1e)
|
|
#define MPII_EVENT_IR_PHYSICAL_DISK (0x1f)
|
|
#define MPII_EVENT_IR_CONFIGURATION_CHANGE_LIST (0x20)
|
|
#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)
|
|
|
|
/* 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;
|
|
|
|
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;
|
|
|
|
#define MPII_SCSIIO_ERR_STATUS_SUCCESS (0x00)
|
|
#define MPII_SCSIIO_ERR_STATUS_CHECK_COND (0x02)
|
|
#define MPII_SCSIIO_ERR_STATUS_BUSY (0x04)
|
|
#define MPII_SCSIIO_ERR_STATUS_INTERMEDIATE (0x08)
|
|
#define MPII_SCSIIO_ERR_STATUS_INTERMEDIATE_CONDMET (0x10)
|
|
#define MPII_SCSIIO_ERR_STATUS_RESERVATION_CONFLICT (0x14)
|
|
#define MPII_SCSIIO_ERR_STATUS_CMD_TERM (0x22)
|
|
#define MPII_SCSIIO_ERR_STATUS_TASK_SET_FULL (0x28)
|
|
#define MPII_SCSIIO_ERR_STATUS_ACA_ACTIVE (0x30)
|
|
#define MPII_SCSIIO_ERR_STATUS_TASK_ABORTED (0x40)
|
|
|
|
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_descr {
|
|
u_int8_t request_flags;
|
|
#define MPII_REQ_DESCR_TYPE_MASK (0x0e)
|
|
#define MPII_REQ_DESCR_SCSI_IO (0x00)
|
|
#define MPII_REQ_DESCR_SCSI_TARGET (0x02)
|
|
#define MPII_REQ_DESCR_HIGH_PRIORITY (0x06)
|
|
#define MPII_REQ_DESCR_DEFAULT (0x08)
|
|
u_int8_t vf_id;
|
|
u_int16_t smid;
|
|
|
|
u_int16_t lmid;
|
|
u_int16_t dev_handle;
|
|
} __packed;
|
|
|
|
struct mpii_reply_descr {
|
|
u_int8_t reply_flags;
|
|
#define MPII_REPLY_DESCR_TYPE_MASK (0x0f)
|
|
#define MPII_REPLY_DESCR_SCSI_IO_SUCCESS (0x00)
|
|
#define MPII_REPLY_DESCR_ADDRESS_REPLY (0x01)
|
|
#define MPII_REPLY_DESCR_TARGET_ASSIST_SUCCESS (0x02)
|
|
#define MPII_REPLY_DESCR_TARGET_COMMAND_BUFFER (0x03)
|
|
#define MPII_REPLY_DESCR_UNUSED (0x0f)
|
|
u_int8_t vf_id;
|
|
u_int16_t smid;
|
|
|
|
union {
|
|
u_int32_t data;
|
|
u_int32_t frame_addr; /* Address Reply */
|
|
};
|
|
} __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;
|
|
|
|
struct mpii_msg_scsi_task_request {
|
|
u_int16_t dev_handle;
|
|
u_int8_t chain_offset;
|
|
u_int8_t function;
|
|
|
|
u_int8_t reserved1;
|
|
u_int8_t task_type;
|
|
#define MPII_SCSI_TASK_ABORT_TASK (0x01)
|
|
#define MPII_SCSI_TASK_ABRT_TASK_SET (0x02)
|
|
#define MPII_SCSI_TASK_TARGET_RESET (0x03)
|
|
#define MPII_SCSI_TASK_RESET_BUS (0x04)
|
|
#define MPII_SCSI_TASK_LOGICAL_UNIT_RESET (0x05)
|
|
u_int8_t reserved2;
|
|
u_int8_t msg_flags;
|
|
|
|
u_int8_t vp_id;
|
|
u_int8_t vf_id;
|
|
u_int16_t reserved3;
|
|
|
|
u_int16_t lun[4];
|
|
|
|
u_int32_t reserved4[7];
|
|
|
|
u_int16_t task_mid;
|
|
u_int16_t reserved5;
|
|
} __packed;
|
|
|
|
struct mpii_msg_scsi_task_reply {
|
|
u_int16_t dev_handle;
|
|
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_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_int32_t termination_count;
|
|
} __packed;
|
|
|
|
struct mpii_msg_sas_oper_request {
|
|
u_int8_t operation;
|
|
#define MPII_SAS_OP_CLEAR_PERSISTENT (0x02)
|
|
#define MPII_SAS_OP_PHY_LINK_RESET (0x06)
|
|
#define MPII_SAS_OP_PHY_HARD_RESET (0x07)
|
|
#define MPII_SAS_OP_PHY_CLEAR_ERROR_LOG (0x08)
|
|
#define MPII_SAS_OP_SEND_PRIMITIVE (0x0a)
|
|
#define MPII_SAS_OP_FORCE_FULL_DISCOVERY (0x0b)
|
|
#define MPII_SAS_OP_TRANSMIT_PORT_SELECT (0x0c)
|
|
#define MPII_SAS_OP_REMOVE_DEVICE (0x0d)
|
|
#define MPII_SAS_OP_LOOKUP_MAPPING (0x0e)
|
|
#define MPII_SAS_OP_SET_IOC_PARAM (0x0f)
|
|
u_int8_t reserved1;
|
|
u_int8_t chain_offset;
|
|
u_int8_t function;
|
|
|
|
u_int16_t dev_handle;
|
|
u_int8_t ioc_param;
|
|
u_int8_t msg_flags;
|
|
|
|
u_int8_t vp_id;
|
|
u_int8_t vf_id;
|
|
u_int16_t reserved2;
|
|
|
|
u_int16_t reserved3;
|
|
u_int8_t phy_num;
|
|
u_int8_t prim_flags;
|
|
|
|
u_int32_t primitive;
|
|
|
|
u_int8_t lookup_method;
|
|
#define MPII_SAS_LOOKUP_METHOD_SAS_ADDR (0x01)
|
|
#define MPII_SAS_LOOKUP_METHOD_SAS_ENCL (0x02)
|
|
#define MPII_SAS_LOOKUP_METHOD_SAS_DEVNAME (0x03)
|
|
u_int8_t reserved4;
|
|
u_int16_t slot_num;
|
|
|
|
u_int64_t lookup_addr;
|
|
|
|
u_int32_t ioc_param_value;
|
|
|
|
u_int64_t reserved5;
|
|
} __packed;
|
|
|
|
struct mpii_msg_sas_oper_reply {
|
|
u_int8_t operation;
|
|
u_int8_t reserved1;
|
|
u_int8_t chain_offset;
|
|
u_int8_t function;
|
|
|
|
u_int16_t dev_handle;
|
|
u_int8_t ioc_param;
|
|
u_int8_t msg_flags;
|
|
|
|
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;
|
|
} __packed;
|
|
|
|
struct mpii_msg_raid_action_request {
|
|
u_int8_t action;
|
|
#define MPII_RAID_ACTION_CHANGE_VOL_WRITE_CACHE (0x17)
|
|
u_int8_t reserved1;
|
|
u_int8_t chain_offset;
|
|
u_int8_t function;
|
|
|
|
u_int16_t vol_dev_handle;
|
|
u_int8_t phys_disk_num;
|
|
u_int8_t msg_flags;
|
|
|
|
u_int8_t vp_id;
|
|
u_int8_t vf_if;
|
|
u_int16_t reserved2;
|
|
|
|
u_int32_t reserved3;
|
|
|
|
u_int32_t action_data;
|
|
#define MPII_RAID_VOL_WRITE_CACHE_MASK (0x03)
|
|
#define MPII_RAID_VOL_WRITE_CACHE_DISABLE (0x01)
|
|
#define MPII_RAID_VOL_WRITE_CACHE_ENABLE (0x02)
|
|
|
|
struct mpii_sge action_sge;
|
|
} __packed;
|
|
|
|
struct mpii_msg_raid_action_reply {
|
|
u_int8_t action;
|
|
u_int8_t reserved1;
|
|
u_int8_t chain_offset;
|
|
u_int8_t function;
|
|
|
|
u_int16_t vol_dev_handle;
|
|
u_int8_t phys_disk_num;
|
|
u_int8_t msg_flags;
|
|
|
|
u_int8_t vp_id;
|
|
u_int8_t vf_if;
|
|
u_int16_t reserved2;
|
|
|
|
u_int16_t reserved3;
|
|
u_int16_t ioc_status;
|
|
|
|
u_int32_t action_data[5];
|
|
} __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;
|
|
#define MPII_CONFIG_REQ_PAGE_TYPE_SAS_DEVICE (0x12)
|
|
#define MPII_CONFIG_REQ_PAGE_TYPE_RAID_CONFIG (0x16)
|
|
#define MPII_CONFIG_REQ_PAGE_TYPE_DRIVER_MAPPING (0x17)
|
|
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_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;
|
|
#define MPII_IOC_PG8_FLAGS_DA_START_SLOT_1 (1<<5)
|
|
#define MPII_IOC_PG8_FLAGS_RESERVED_TARGETID_0 (1<<4)
|
|
#define MPII_IOC_PG8_FLAGS_MAPPING_MODE_MASK (0x0000000e)
|
|
#define MPII_IOC_PG8_FLAGS_DEVICE_PERSISTENCE_MAPPING (0<<1)
|
|
#define MPII_IOC_PG8_FLAGS_ENCLOSURE_SLOT_MAPPING (1<<1)
|
|
#define MPII_IOC_PG8_FLAGS_DISABLE_PERSISTENT_MAPPING (1<<0)
|
|
#define MPII_IOC_PG8_FLAGS_ENABLE_PERSISTENT_MAPPING (0<<0)
|
|
u_int16_t reserved3;
|
|
|
|
u_int16_t ir_volume_mapping_flags;
|
|
#define MPII_IOC_PG8_IRFLAGS_VOLUME_MAPPING_MODE_MASK (0x00000003)
|
|
#define MPII_IOC_PG8_IRFLAGS_LOW_VOLUME_MAPPING (0<<0)
|
|
#define MPII_IOC_PG8_IRFLAGS_HIGH_VOLUME_MAPPING (1<<0)
|
|
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;
|
|
|
|
#define MPII_CFG_RAID_VOL_ADDR_HANDLE (1<<28)
|
|
|
|
struct mpii_cfg_raid_vol_pg0 {
|
|
struct mpii_cfg_hdr config_header;
|
|
|
|
u_int16_t volume_handle;
|
|
u_int8_t volume_state;
|
|
#define MPII_CFG_RAID_VOL_0_STATE_MISSING (0x00)
|
|
#define MPII_CFG_RAID_VOL_0_STATE_FAILED (0x01)
|
|
#define MPII_CFG_RAID_VOL_0_STATE_INITIALIZING (0x02)
|
|
#define MPII_CFG_RAID_VOL_0_STATE_ONLINE (0x03)
|
|
#define MPII_CFG_RAID_VOL_0_STATE_DEGRADED (0x04)
|
|
#define MPII_CFG_RAID_VOL_0_STATE_OPTIMAL (0x05)
|
|
u_int8_t volume_type;
|
|
#define MPII_CFG_RAID_VOL_0_TYPE_RAID0 (0x00)
|
|
#define MPII_CFG_RAID_VOL_0_TYPE_RAID1E (0x01)
|
|
#define MPII_CFG_RAID_VOL_0_TYPE_RAID1 (0x02)
|
|
#define MPII_CFG_RAID_VOL_0_TYPE_RAID10 (0x05)
|
|
#define MPII_CFG_RAID_VOL_0_TYPE_UNKNOWN (0xff)
|
|
|
|
u_int32_t volume_status;
|
|
#define MPII_CFG_RAID_VOL_0_STATUS_SCRUB (1<<20)
|
|
#define MPII_CFG_RAID_VOL_0_STATUS_RESYNC (1<<16)
|
|
|
|
u_int16_t volume_settings;
|
|
#define MPII_CFG_RAID_VOL_0_SETTINGS_CACHE_MASK (0x3<<0)
|
|
#define MPII_CFG_RAID_VOL_0_SETTINGS_CACHE_UNCHANGED (0x0<<0)
|
|
#define MPII_CFG_RAID_VOL_0_SETTINGS_CACHE_DISABLED (0x1<<0)
|
|
#define MPII_CFG_RAID_VOL_0_SETTINGS_CACHE_ENABLED (0x2<<0)
|
|
|
|
u_int8_t hot_spare_pool;
|
|
u_int8_t reserved1;
|
|
|
|
u_int64_t max_lba;
|
|
|
|
u_int32_t stripe_size;
|
|
|
|
u_int16_t block_size;
|
|
u_int16_t reserved2;
|
|
|
|
u_int8_t phys_disk_types;
|
|
u_int8_t resync_rate;
|
|
u_int16_t data_scrub_rate;
|
|
|
|
u_int8_t num_phys_disks;
|
|
u_int16_t reserved3;
|
|
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_int8_t raid_set_num;
|
|
u_int8_t phys_disk_map;
|
|
u_int8_t phys_disk_num;
|
|
u_int8_t reserved;
|
|
} __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;
|
|
|
|
#define MPII_CFG_RAID_PHYS_DISK_ADDR_NUMBER (1<<28)
|
|
|
|
struct mpii_cfg_raid_physdisk_pg0 {
|
|
struct mpii_cfg_hdr config_header;
|
|
|
|
u_int16_t dev_handle;
|
|
u_int8_t reserved1;
|
|
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 reserved2;
|
|
|
|
u_int8_t vendor_id[8];
|
|
|
|
u_int8_t product_id[16];
|
|
|
|
u_int8_t product_rev[4];
|
|
|
|
u_int8_t serial[32];
|
|
|
|
u_int32_t reserved3;
|
|
|
|
u_int8_t phys_disk_state;
|
|
#define MPII_CFG_RAID_PHYDISK_0_STATE_NOTCONFIGURED (0x00)
|
|
#define MPII_CFG_RAID_PHYDISK_0_STATE_NOTCOMPATIBLE (0x01)
|
|
#define MPII_CFG_RAID_PHYDISK_0_STATE_OFFLINE (0x02)
|
|
#define MPII_CFG_RAID_PHYDISK_0_STATE_ONLINE (0x03)
|
|
#define MPII_CFG_RAID_PHYDISK_0_STATE_HOTSPARE (0x04)
|
|
#define MPII_CFG_RAID_PHYDISK_0_STATE_DEGRADED (0x05)
|
|
#define MPII_CFG_RAID_PHYDISK_0_STATE_REBUILDING (0x06)
|
|
#define MPII_CFG_RAID_PHYDISK_0_STATE_OPTIMAL (0x07)
|
|
u_int8_t offline_reason;
|
|
#define MPII_CFG_RAID_PHYDISK_0_OFFLINE_MISSING (0x01)
|
|
#define MPII_CFG_RAID_PHYDISK_0_OFFLINE_FAILED (0x03)
|
|
#define MPII_CFG_RAID_PHYDISK_0_OFFLINE_INITIALIZING (0x04)
|
|
#define MPII_CFG_RAID_PHYDISK_0_OFFLINE_REQUESTED (0x05)
|
|
#define MPII_CFG_RAID_PHYDISK_0_OFFLINE_FAILEDREQ (0x06)
|
|
#define MPII_CFG_RAID_PHYDISK_0_OFFLINE_OTHER (0xff)
|
|
|
|
u_int8_t incompat_reason;
|
|
u_int8_t phys_disk_attrs;
|
|
|
|
u_int32_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_int64_t dev_max_lba;
|
|
|
|
u_int64_t host_max_lba;
|
|
|
|
u_int64_t coerced_max_lba;
|
|
|
|
u_int16_t block_size;
|
|
u_int16_t reserved4;
|
|
|
|
u_int32_t reserved5;
|
|
} __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 max_port_conn;
|
|
|
|
u_int64_t device_name;
|
|
|
|
u_int8_t port_groups;
|
|
u_int8_t dma_group;
|
|
u_int8_t ctrl_group;
|
|
u_int8_t reserved1;
|
|
|
|
u_int64_t reserved2;
|
|
} __packed;
|
|
|
|
#define MPII_CFG_RAID_CONFIG_ACTIVE_CONFIG (2<<28)
|
|
|
|
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;
|
|
|
|
struct mpii_cfg_dpm_pg0 {
|
|
struct mpii_ecfg_hdr config_header;
|
|
#define MPII_DPM_ADDRESS_FORM_MASK (0xf0000000)
|
|
#define MPII_DPM_ADDRESS_FORM_ENTRY_RANGE (0x00000000)
|
|
#define MPII_DPM_ADDRESS_ENTRY_COUNT_MASK (0x0fff0000)
|
|
#define MPII_DPM_ADDRESS_ENTRY_COUNT_SHIFT (16)
|
|
#define MPII_DPM_ADDRESS_START_ENTRY_MASK (0x0000ffff)
|
|
|
|
/* followed by struct mpii_dpm_entry structs */
|
|
} __packed;
|
|
|
|
struct mpii_dpm_entry {
|
|
u_int64_t physical_identifier;
|
|
|
|
u_int16_t mapping_information;
|
|
u_int16_t device_index;
|
|
|
|
u_int32_t physical_bits_mapping;
|
|
|
|
u_int32_t reserved1;
|
|
} __packed;
|
|
|
|
struct mpii_evt_sas_discovery {
|
|
u_int8_t flags;
|
|
#define MPII_EVENT_SAS_DISC_FLAGS_DEV_CHANGE_MASK (1<<1)
|
|
#define MPII_EVENT_SAS_DISC_FLAGS_DEV_CHANGE_NO_CHANGE (0<<1)
|
|
#define MPII_EVENT_SAS_DISC_FLAGS_DEV_CHANGE_CHANGE (1<<1)
|
|
#define MPII_EVENT_SAS_DISC_FLAGS_DISC_IN_PROG_MASK (1<<0)
|
|
#define MPII_EVENT_SAS_DISC_FLAGS_DISC_NOT_IN_PROGRESS (1<<0)
|
|
#define MPII_EVENT_SAS_DISC_FLAGS_DISC_IN_PROGRESS (0<<0)
|
|
u_int8_t reason_code;
|
|
#define MPII_EVENT_SAS_DISC_REASON_CODE_STARTED (0x01)
|
|
#define MPII_EVENT_SAS_DISC_REASON_CODE_COMPLETED (0x02)
|
|
u_int8_t physical_port;
|
|
u_int8_t reserved1;
|
|
|
|
u_int32_t discovery_status;
|
|
} __packed;
|
|
|
|
struct mpii_evt_ir_status {
|
|
u_int16_t vol_dev_handle;
|
|
u_int16_t reserved1;
|
|
|
|
u_int8_t operation;
|
|
#define MPII_EVENT_IR_RAIDOP_RESYNC (0x00)
|
|
#define MPII_EVENT_IR_RAIDOP_OCE (0x01)
|
|
#define MPII_EVENT_IR_RAIDOP_CONS_CHECK (0x02)
|
|
#define MPII_EVENT_IR_RAIDOP_BG_INIT (0x03)
|
|
#define MPII_EVENT_IR_RAIDOP_MAKE_CONS (0x04)
|
|
u_int8_t percent;
|
|
u_int16_t reserved2;
|
|
|
|
u_int32_t reserved3;
|
|
};
|
|
|
|
struct mpii_evt_ir_volume {
|
|
u_int16_t vol_dev_handle;
|
|
u_int8_t reason_code;
|
|
#define MPII_EVENT_IR_VOL_RC_SETTINGS_CHANGED (0x01)
|
|
#define MPII_EVENT_IR_VOL_RC_STATUS_CHANGED (0x02)
|
|
#define MPII_EVENT_IR_VOL_RC_STATE_CHANGED (0x03)
|
|
u_int8_t reserved1;
|
|
|
|
u_int32_t new_value;
|
|
u_int32_t prev_value;
|
|
} __packed;
|
|
|
|
struct mpii_evt_ir_physical_disk {
|
|
u_int16_t reserved1;
|
|
u_int8_t reason_code;
|
|
#define MPII_EVENT_IR_PD_RC_SETTINGS_CHANGED (0x01)
|
|
#define MPII_EVENT_IR_PD_RC_STATUS_FLAGS_CHANGED (0x02)
|
|
#define MPII_EVENT_IR_PD_RC_STATUS_CHANGED (0x03)
|
|
u_int8_t phys_disk_num;
|
|
|
|
u_int16_t phys_disk_dev_handle;
|
|
u_int16_t reserved2;
|
|
|
|
u_int16_t slot;
|
|
u_int16_t enclosure_handle;
|
|
|
|
u_int32_t new_value;
|
|
u_int32_t previous_value;
|
|
} __packed;
|
|
|
|
struct mpii_evt_sas_tcl {
|
|
u_int16_t enclosure_handle;
|
|
u_int16_t expander_handle;
|
|
|
|
u_int8_t num_phys;
|
|
u_int8_t reserved1[3];
|
|
|
|
u_int8_t num_entries;
|
|
u_int8_t start_phy_num;
|
|
u_int8_t expn_status;
|
|
#define MPII_EVENT_SAS_TOPO_ES_ADDED (0x01)
|
|
#define MPII_EVENT_SAS_TOPO_ES_NOT_RESPONDING (0x02)
|
|
#define MPII_EVENT_SAS_TOPO_ES_RESPONDING (0x03)
|
|
#define MPII_EVENT_SAS_TOPO_ES_DELAY_NOT_RESPONDING (0x04)
|
|
u_int8_t physical_port;
|
|
|
|
/* followed by num_entries number of struct mpii_evt_phy_entry */
|
|
} __packed;
|
|
|
|
struct mpii_evt_phy_entry {
|
|
u_int16_t dev_handle;
|
|
u_int8_t link_rate;
|
|
u_int8_t phy_status;
|
|
#define MPII_EVENT_SAS_TOPO_PS_RC_MASK (0x0f)
|
|
#define MPII_EVENT_SAS_TOPO_PS_RC_ADDED (0x01)
|
|
#define MPII_EVENT_SAS_TOPO_PS_RC_MISSING (0x02)
|
|
} __packed;
|
|
|
|
struct mpii_evt_ir_cfg_change_list {
|
|
u_int8_t num_elements;
|
|
u_int16_t reserved;
|
|
u_int8_t config_num;
|
|
|
|
u_int32_t flags;
|
|
#define MPII_EVT_IR_CFG_CHANGE_LIST_FOREIGN (0x1)
|
|
|
|
/* followed by num_elements struct mpii_evt_ir_cfg_elements */
|
|
} __packed;
|
|
|
|
struct mpii_evt_ir_cfg_element {
|
|
u_int16_t element_flags;
|
|
#define MPII_EVT_IR_CFG_ELEMENT_TYPE_MASK (0xf)
|
|
#define MPII_EVT_IR_CFG_ELEMENT_TYPE_VOLUME (0x0)
|
|
#define MPII_EVT_IR_CFG_ELEMENT_TYPE_VOLUME_DISK (0x1)
|
|
#define MPII_EVT_IR_CFG_ELEMENT_TYPE_HOT_SPARE (0x2)
|
|
u_int16_t vol_dev_handle;
|
|
|
|
u_int8_t reason_code;
|
|
#define MPII_EVT_IR_CFG_ELEMENT_RC_ADDED (0x01)
|
|
#define MPII_EVT_IR_CFG_ELEMENT_RC_REMOVED (0x02)
|
|
#define MPII_EVT_IR_CFG_ELEMENT_RC_NO_CHANGE (0x03)
|
|
#define MPII_EVT_IR_CFG_ELEMENT_RC_HIDE (0x04)
|
|
#define MPII_EVT_IR_CFG_ELEMENT_RC_UNHIDE (0x05)
|
|
#define MPII_EVT_IR_CFG_ELEMENT_RC_VOLUME_CREATED (0x06)
|
|
#define MPII_EVT_IR_CFG_ELEMENT_RC_VOLUME_DELETED (0x07)
|
|
#define MPII_EVT_IR_CFG_ELEMENT_RC_PD_CREATED (0x08)
|
|
#define MPII_EVT_IR_CFG_ELEMENT_RC_PD_DELETED (0x09)
|
|
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)
|
|
#define MPII_D_MAP (0x1000)
|
|
|
|
#if 0
|
|
u_int32_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
|
|
| MPII_D_MAP
|
|
;
|
|
#endif
|
|
u_int32_t mpii_debug = MPII_D_MISC;
|
|
#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 (128)
|
|
|
|
#define MPII_MAXFER MAXPHYS /* XXX bogus */
|
|
|
|
struct mpii_dmamem {
|
|
bus_dmamap_t mdm_map;
|
|
bus_dma_segment_t mdm_seg;
|
|
size_t mdm_size;
|
|
void *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 {
|
|
union {
|
|
struct work rcb_wk; /* has to be first in struct */
|
|
SIMPLEQ_ENTRY(mpii_rcb) rcb_link;
|
|
} u;
|
|
void *rcb_reply;
|
|
u_int32_t rcb_reply_dva;
|
|
};
|
|
|
|
SIMPLEQ_HEAD(mpii_rcb_list, mpii_rcb);
|
|
|
|
struct mpii_device {
|
|
int flags;
|
|
#define MPII_DF_ATTACH (0x0001)
|
|
#define MPII_DF_DETACH (0x0002)
|
|
#define MPII_DF_HIDDEN (0x0004)
|
|
#define MPII_DF_UNUSED (0x0008)
|
|
#define MPII_DF_VOLUME (0x0010)
|
|
#define MPII_DF_VOLUME_DISK (0x0020)
|
|
#define MPII_DF_HOT_SPARE (0x0040)
|
|
short slot;
|
|
short percent;
|
|
u_int16_t dev_handle;
|
|
u_int16_t enclosure;
|
|
u_int16_t expander;
|
|
u_int8_t phy_num;
|
|
u_int8_t physical_port;
|
|
};
|
|
|
|
struct mpii_ccb {
|
|
union {
|
|
struct work ccb_wk; /* has to be first in struct */
|
|
SIMPLEQ_ENTRY(mpii_ccb) ccb_link;
|
|
} u;
|
|
struct mpii_softc *ccb_sc;
|
|
int ccb_smid;
|
|
|
|
void * ccb_cookie;
|
|
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,
|
|
MPII_CCB_TIMEOUT
|
|
} ccb_state;
|
|
|
|
void (*ccb_done)(struct mpii_ccb *);
|
|
struct mpii_rcb *ccb_rcb;
|
|
|
|
};
|
|
|
|
struct mpii_ccb_wait {
|
|
kmutex_t mpii_ccbw_mtx;
|
|
kcondvar_t mpii_ccbw_cv;
|
|
};
|
|
|
|
SIMPLEQ_HEAD(mpii_ccb_list, mpii_ccb);
|
|
|
|
struct mpii_softc {
|
|
device_t sc_dev;
|
|
|
|
pci_chipset_tag_t sc_pc;
|
|
pcitag_t sc_tag;
|
|
|
|
void *sc_ih;
|
|
|
|
int sc_flags;
|
|
#define MPII_F_RAID (1<<1)
|
|
|
|
struct scsipi_adapter sc_adapt;
|
|
struct scsipi_channel sc_chan;
|
|
device_t sc_child; /* our scsibus */
|
|
|
|
struct mpii_device **sc_devs;
|
|
|
|
bus_space_tag_t sc_iot;
|
|
bus_space_handle_t sc_ioh;
|
|
bus_size_t sc_ios;
|
|
bus_dma_tag_t sc_dmat;
|
|
|
|
kmutex_t sc_req_mtx;
|
|
kmutex_t sc_rep_mtx;
|
|
|
|
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_ioc_event_replay;
|
|
u_int16_t sc_max_enclosures;
|
|
u_int16_t sc_max_expanders;
|
|
u_int8_t sc_max_volumes;
|
|
u_int16_t sc_max_devices;
|
|
u_int16_t sc_max_dpm_entries;
|
|
u_int16_t sc_vd_count;
|
|
u_int16_t sc_vd_id_low;
|
|
u_int16_t sc_pd_id_start;
|
|
u_int8_t sc_num_channels;
|
|
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;
|
|
kmutex_t sc_ccb_free_mtx;
|
|
kcondvar_t sc_ccb_free_cv;
|
|
|
|
kmutex_t sc_ccb_mtx;
|
|
/*
|
|
* this protects the ccb state and list entry
|
|
* between mpii_scsi_cmd and scsidone.
|
|
*/
|
|
|
|
struct workqueue *sc_ssb_tmowk;
|
|
|
|
struct mpii_dmamem *sc_requests;
|
|
|
|
struct mpii_dmamem *sc_replies;
|
|
struct mpii_rcb *sc_rcbs;
|
|
|
|
struct mpii_dmamem *sc_reply_postq;
|
|
struct mpii_reply_descr *sc_reply_postq_kva;
|
|
int sc_reply_post_host_index;
|
|
|
|
struct mpii_dmamem *sc_reply_freeq;
|
|
int sc_reply_free_host_index;
|
|
|
|
struct workqueue *sc_ssb_evt_ackwk;
|
|
|
|
struct sysmon_envsys *sc_sme;
|
|
envsys_data_t *sc_sensors;
|
|
};
|
|
|
|
static int mpii_match(device_t, cfdata_t, void *);
|
|
static void mpii_attach(device_t, device_t, void *);
|
|
static int mpii_detach(device_t, int);
|
|
static void mpii_childdetached(device_t, device_t);
|
|
static int mpii_rescan(device_t, const char *, const int *);
|
|
|
|
static int mpii_intr(void *);
|
|
|
|
CFATTACH_DECL3_NEW(mpii, sizeof(struct mpii_softc),
|
|
mpii_match, mpii_attach, mpii_detach, NULL, mpii_rescan,
|
|
mpii_childdetached, DVF_DETACH_SHUTDOWN);
|
|
|
|
#define PREAD(s, r) pci_conf_read((s)->sc_pc, (s)->sc_tag, (r))
|
|
#define PWRITE(s, r, v) pci_conf_write((s)->sc_pc, (s)->sc_tag, (r), (v))
|
|
|
|
static void mpii_scsipi_request(struct scsipi_channel *,
|
|
scsipi_adapter_req_t, void *);
|
|
static void mpii_scsi_cmd_done(struct mpii_ccb *);
|
|
static void mpii_minphys(struct buf *bp);
|
|
|
|
static struct mpii_dmamem *mpii_dmamem_alloc(struct mpii_softc *, size_t);
|
|
static void mpii_dmamem_free(struct mpii_softc *, struct mpii_dmamem *);
|
|
static int mpii_alloc_ccbs(struct mpii_softc *);
|
|
static struct mpii_ccb *mpii_get_ccb(struct mpii_softc *, int);
|
|
#define MPII_NOSLEEP 0x0001
|
|
static void mpii_put_ccb(struct mpii_softc *, struct mpii_ccb *);
|
|
static int mpii_alloc_replies(struct mpii_softc *);
|
|
static int mpii_alloc_queues(struct mpii_softc *);
|
|
static void mpii_push_reply(struct mpii_softc *, struct mpii_rcb *);
|
|
static void mpii_push_replies(struct mpii_softc *);
|
|
|
|
static void mpii_scsi_cmd_tmo(void *);
|
|
static void mpii_scsi_cmd_tmo_handler(struct work *, void *);
|
|
static void mpii_scsi_cmd_tmo_done(struct mpii_ccb *);
|
|
|
|
static int mpii_alloc_dev(struct mpii_softc *);
|
|
static int mpii_insert_dev(struct mpii_softc *, struct mpii_device *);
|
|
static int mpii_remove_dev(struct mpii_softc *, struct mpii_device *);
|
|
static struct mpii_device *mpii_find_dev(struct mpii_softc *, u_int16_t);
|
|
|
|
static void mpii_start(struct mpii_softc *, struct mpii_ccb *);
|
|
static int mpii_poll(struct mpii_softc *, struct mpii_ccb *);
|
|
static void mpii_poll_done(struct mpii_ccb *);
|
|
static struct mpii_rcb *mpii_reply(struct mpii_softc *,
|
|
struct mpii_reply_descr *);
|
|
|
|
static void mpii_wait(struct mpii_softc *, struct mpii_ccb *);
|
|
static void mpii_wait_done(struct mpii_ccb *);
|
|
|
|
static void mpii_init_queues(struct mpii_softc *);
|
|
|
|
static int mpii_load_xs(struct mpii_ccb *);
|
|
|
|
static u_int32_t mpii_read(struct mpii_softc *, bus_size_t);
|
|
static void mpii_write(struct mpii_softc *, bus_size_t, u_int32_t);
|
|
static int mpii_wait_eq(struct mpii_softc *, bus_size_t, u_int32_t,
|
|
u_int32_t);
|
|
static int mpii_wait_ne(struct mpii_softc *, bus_size_t, u_int32_t,
|
|
u_int32_t);
|
|
|
|
static int mpii_init(struct mpii_softc *);
|
|
static int mpii_reset_soft(struct mpii_softc *);
|
|
static int mpii_reset_hard(struct mpii_softc *);
|
|
|
|
static int mpii_handshake_send(struct mpii_softc *, void *, size_t);
|
|
static int mpii_handshake_recv_dword(struct mpii_softc *,
|
|
u_int32_t *);
|
|
static int mpii_handshake_recv(struct mpii_softc *, void *, size_t);
|
|
|
|
static void mpii_empty_done(struct mpii_ccb *);
|
|
|
|
static int mpii_iocinit(struct mpii_softc *);
|
|
static int mpii_iocfacts(struct mpii_softc *);
|
|
static int mpii_portfacts(struct mpii_softc *);
|
|
static int mpii_portenable(struct mpii_softc *);
|
|
static int mpii_cfg_coalescing(struct mpii_softc *);
|
|
|
|
static int mpii_eventnotify(struct mpii_softc *);
|
|
static void mpii_eventnotify_done(struct mpii_ccb *);
|
|
static void mpii_eventack(struct work *, void *);
|
|
static void mpii_eventack_done(struct mpii_ccb *);
|
|
static void mpii_event_process(struct mpii_softc *, struct mpii_rcb *);
|
|
static void mpii_event_sas(struct mpii_softc *,
|
|
struct mpii_msg_event_reply *);
|
|
static void mpii_event_raid(struct mpii_softc *,
|
|
struct mpii_msg_event_reply *);
|
|
static void mpii_event_defer(void *, void *);
|
|
|
|
static void mpii_sas_remove_device(struct mpii_softc *, u_int16_t);
|
|
|
|
static int mpii_req_cfg_header(struct mpii_softc *, u_int8_t,
|
|
u_int8_t, u_int32_t, int, void *);
|
|
static int mpii_req_cfg_page(struct mpii_softc *, u_int32_t, int,
|
|
void *, int, void *, size_t);
|
|
|
|
static int mpii_get_ioc_pg8(struct mpii_softc *);
|
|
|
|
#if 0
|
|
static int mpii_ioctl_cache(struct scsi_link *, u_long, struct dk_cache *);
|
|
#endif
|
|
static int mpii_cache_enable(struct mpii_softc *, struct mpii_device *);
|
|
|
|
#if NBIO > 0
|
|
static int mpii_ioctl(device_t, u_long, void *);
|
|
static int mpii_ioctl_inq(struct mpii_softc *, struct bioc_inq *);
|
|
static int mpii_ioctl_vol(struct mpii_softc *, struct bioc_vol *);
|
|
static int mpii_ioctl_disk(struct mpii_softc *, struct bioc_disk *);
|
|
static int mpii_bio_hs(struct mpii_softc *, struct bioc_disk *, int,
|
|
int, int *);
|
|
static int mpii_bio_disk(struct mpii_softc *, struct bioc_disk *,
|
|
u_int8_t);
|
|
static struct mpii_device *mpii_find_vol(struct mpii_softc *, int);
|
|
static int mpii_bio_volstate(struct mpii_softc *, struct bioc_vol *);
|
|
static int mpii_create_sensors(struct mpii_softc *);
|
|
static int mpii_destroy_sensors(struct mpii_softc *);
|
|
static void mpii_refresh_sensors(struct sysmon_envsys *, envsys_data_t *);
|
|
#endif /* NBIO > 0 */
|
|
|
|
#define DEVNAME(_s) (device_xname((_s)->sc_dev))
|
|
|
|
#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) 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) 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_PG_EXTENDED (1<<0)
|
|
#define MPII_PG_POLL (1<<1)
|
|
#define MPII_PG_FMT "\020" "\002POLL" "\001EXTENDED"
|
|
|
|
#define mpii_cfg_header(_s, _t, _n, _a, _h) \
|
|
mpii_req_cfg_header((_s), (_t), (_n), (_a), \
|
|
MPII_PG_POLL, (_h))
|
|
#define mpii_ecfg_header(_s, _t, _n, _a, _h) \
|
|
mpii_req_cfg_header((_s), (_t), (_n), (_a), \
|
|
MPII_PG_POLL|MPII_PG_EXTENDED, (_h))
|
|
|
|
#define mpii_cfg_page(_s, _a, _h, _r, _p, _l) \
|
|
mpii_req_cfg_page((_s), (_a), MPII_PG_POLL, \
|
|
(_h), (_r), (_p), (_l))
|
|
#define mpii_ecfg_page(_s, _a, _h, _r, _p, _l) \
|
|
mpii_req_cfg_page((_s), (_a), MPII_PG_POLL|MPII_PG_EXTENDED, \
|
|
(_h), (_r), (_p), (_l))
|
|
|
|
|
|
static const struct mpii_pci_product {
|
|
pci_vendor_id_t mpii_vendor;
|
|
pci_product_id_t mpii_product;
|
|
} mpii_devices[] = {
|
|
{ PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_SAS2004 },
|
|
{ PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_SAS2008 },
|
|
{ PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_SAS2108_3 },
|
|
{ PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_SAS2108_4 },
|
|
{ PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_SAS2108_5 },
|
|
{ PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_SAS2116_1 },
|
|
{ PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_SAS2116_2 },
|
|
{ PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_SAS2208_1 },
|
|
{ PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_SAS2208_2 },
|
|
{ PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_SAS2208_3 },
|
|
{ PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_SAS2208_4 },
|
|
{ PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_SAS2208_5 },
|
|
{ PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_SAS2208_6 },
|
|
{ PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_SAS2308_1 },
|
|
{ PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_SAS2308_2 },
|
|
{ PCI_VENDOR_SYMBIOS, PCI_PRODUCT_SYMBIOS_SAS2308_3 },
|
|
{ 0, 0 }
|
|
};
|
|
|
|
static int
|
|
mpii_match(device_t parent, cfdata_t match, void *aux)
|
|
{
|
|
struct pci_attach_args *pa = aux;
|
|
const struct mpii_pci_product *mpii;
|
|
|
|
for (mpii = mpii_devices; mpii->mpii_vendor != 0; mpii++) {
|
|
if (PCI_VENDOR(pa->pa_id) == mpii->mpii_vendor &&
|
|
PCI_PRODUCT(pa->pa_id) == mpii->mpii_product)
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
mpii_attach(device_t parent, device_t self, void *aux)
|
|
{
|
|
struct mpii_softc *sc = device_private(self);
|
|
struct pci_attach_args *pa = aux;
|
|
pcireg_t memtype;
|
|
int r;
|
|
pci_intr_handle_t ih;
|
|
const char *intrstr;
|
|
struct mpii_ccb *ccb;
|
|
struct scsipi_adapter *adapt = &sc->sc_adapt;
|
|
struct scsipi_channel *chan = &sc->sc_chan;
|
|
char wkname[15];
|
|
char intrbuf[PCI_INTRSTR_LEN];
|
|
|
|
pci_aprint_devinfo(pa, NULL);
|
|
|
|
sc->sc_pc = pa->pa_pc;
|
|
sc->sc_tag = pa->pa_tag;
|
|
sc->sc_dmat = pa->pa_dmat;
|
|
sc->sc_dev = self;
|
|
|
|
mutex_init(&sc->sc_req_mtx, MUTEX_DEFAULT, IPL_BIO);
|
|
mutex_init(&sc->sc_rep_mtx, MUTEX_DEFAULT, IPL_BIO);
|
|
mutex_init(&sc->sc_ccb_free_mtx, MUTEX_DEFAULT, IPL_BIO);
|
|
cv_init(&sc->sc_ccb_free_cv, "mpii_ccbs");
|
|
mutex_init(&sc->sc_ccb_mtx, MUTEX_DEFAULT, IPL_BIO);
|
|
|
|
snprintf(wkname, sizeof(wkname), "%s_tmo", DEVNAME(sc));
|
|
if (workqueue_create(&sc->sc_ssb_tmowk, wkname,
|
|
mpii_scsi_cmd_tmo_handler, sc, PRI_NONE, IPL_BIO, WQ_MPSAFE) != 0) {
|
|
aprint_error_dev(self, "can't create %s workqueue\n", wkname);
|
|
return;
|
|
}
|
|
|
|
snprintf(wkname, sizeof(wkname), "%s_evt", DEVNAME(sc));
|
|
if (workqueue_create(&sc->sc_ssb_evt_ackwk, wkname,
|
|
mpii_eventack, sc, PRI_NONE, IPL_BIO, WQ_MPSAFE) != 0) {
|
|
aprint_error_dev(self, "can't create %s workqueue\n", wkname);
|
|
return;
|
|
}
|
|
|
|
/* find the appropriate memory base */
|
|
for (r = PCI_MAPREG_START; r < PCI_MAPREG_END; r += sizeof(memtype)) {
|
|
memtype = pci_mapreg_type(sc->sc_pc, sc->sc_tag, r);
|
|
if ((memtype & PCI_MAPREG_TYPE_MASK) == PCI_MAPREG_TYPE_MEM)
|
|
break;
|
|
}
|
|
if (r >= PCI_MAPREG_END) {
|
|
aprint_error_dev(self,
|
|
"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) != 0) {
|
|
aprint_error_dev(self,
|
|
"unable to map system interface registers\n");
|
|
return;
|
|
}
|
|
|
|
/* disable the expansion rom */
|
|
PWRITE(sc, PCI_MAPREG_ROM,
|
|
PREAD(sc, PCI_MAPREG_ROM) & ~PCI_MAPREG_ROM_ENABLE);
|
|
|
|
/* disable interrupts */
|
|
mpii_write(sc, MPII_INTR_MASK,
|
|
MPII_INTR_MASK_RESET | MPII_INTR_MASK_REPLY |
|
|
MPII_INTR_MASK_DOORBELL);
|
|
|
|
/* hook up the interrupt */
|
|
if (pci_intr_map(pa, &ih) != 0) {
|
|
aprint_error_dev(self, "unable to map interrupt\n");
|
|
goto unmap;
|
|
}
|
|
intrstr = pci_intr_string(pa->pa_pc, ih, intrbuf, sizeof(intrbuf));
|
|
|
|
if (mpii_init(sc) != 0) {
|
|
aprint_error_dev(self, "unable to initialize ioc\n");
|
|
goto unmap;
|
|
}
|
|
|
|
if (mpii_iocfacts(sc) != 0) {
|
|
aprint_error_dev(self, "unable to get iocfacts\n");
|
|
goto unmap;
|
|
}
|
|
|
|
if (mpii_alloc_ccbs(sc) != 0) {
|
|
/* error already printed */
|
|
goto unmap;
|
|
}
|
|
|
|
if (mpii_alloc_replies(sc) != 0) {
|
|
aprint_error_dev(self, "unable to allocated reply space\n");
|
|
goto free_ccbs;
|
|
}
|
|
|
|
if (mpii_alloc_queues(sc) != 0) {
|
|
aprint_error_dev(self, "unable to allocate reply queues\n");
|
|
goto free_replies;
|
|
}
|
|
|
|
if (mpii_iocinit(sc) != 0) {
|
|
aprint_error_dev(self, "unable to send iocinit\n");
|
|
goto free_queues;
|
|
}
|
|
|
|
if (mpii_wait_eq(sc, MPII_DOORBELL, MPII_DOORBELL_STATE,
|
|
MPII_DOORBELL_STATE_OPER) != 0) {
|
|
aprint_error_dev(self, "state: 0x%08x\n",
|
|
mpii_read_db(sc) & MPII_DOORBELL_STATE);
|
|
aprint_error_dev(self, "operational state timeout\n");
|
|
goto free_queues;
|
|
}
|
|
|
|
mpii_push_replies(sc);
|
|
mpii_init_queues(sc);
|
|
|
|
if (mpii_portfacts(sc) != 0) {
|
|
aprint_error_dev(self, "unable to get portfacts\n");
|
|
goto free_queues;
|
|
}
|
|
|
|
if (mpii_get_ioc_pg8(sc) != 0) {
|
|
aprint_error_dev(self, "unable to get ioc page 8\n");
|
|
goto free_queues;
|
|
}
|
|
|
|
if (mpii_cfg_coalescing(sc) != 0) {
|
|
aprint_error_dev(self, "unable to configure coalescing\n");
|
|
goto free_queues;
|
|
}
|
|
|
|
/* XXX bail on unsupported porttype? */
|
|
if ((sc->sc_porttype == MPII_PORTFACTS_PORTTYPE_SAS_PHYSICAL) ||
|
|
(sc->sc_porttype == MPII_PORTFACTS_PORTTYPE_SAS_VIRTUAL)) {
|
|
if (mpii_eventnotify(sc) != 0) {
|
|
aprint_error_dev(self, "unable to enable events\n");
|
|
goto free_queues;
|
|
}
|
|
}
|
|
|
|
if (mpii_alloc_dev(sc) != 0) {
|
|
aprint_error_dev(self,
|
|
"unable to allocate memory for mpii_device\n");
|
|
goto free_queues;
|
|
}
|
|
|
|
if (mpii_portenable(sc) != 0) {
|
|
aprint_error_dev(self, "unable to enable port\n");
|
|
goto free_dev;
|
|
}
|
|
|
|
sc->sc_ih = pci_intr_establish(sc->sc_pc, ih, IPL_BIO,
|
|
mpii_intr, sc);
|
|
if (sc->sc_ih == NULL) {
|
|
aprint_error_dev(self, "can't establish interrupt");
|
|
if (intrstr)
|
|
aprint_error(" at %s", intrstr);
|
|
aprint_error("\n");
|
|
goto free_dev;
|
|
}
|
|
|
|
memset(adapt, 0, sizeof(*adapt));
|
|
adapt->adapt_dev = sc->sc_dev;
|
|
adapt->adapt_nchannels = 1;
|
|
adapt->adapt_openings = sc->sc_request_depth - 1;
|
|
adapt->adapt_max_periph = adapt->adapt_openings;
|
|
adapt->adapt_request = mpii_scsipi_request;
|
|
adapt->adapt_minphys = mpii_minphys;
|
|
|
|
memset(chan, 0, sizeof(*chan));
|
|
chan->chan_adapter = adapt;
|
|
chan->chan_bustype = &scsi_sas_bustype;
|
|
chan->chan_channel = 0;
|
|
chan->chan_flags = 0;
|
|
chan->chan_nluns = 8;
|
|
chan->chan_ntargets = sc->sc_max_devices;
|
|
chan->chan_id = -1;
|
|
|
|
mpii_rescan(self, "scsi", NULL);
|
|
|
|
/* enable interrupts */
|
|
mpii_write(sc, MPII_INTR_MASK, MPII_INTR_MASK_DOORBELL
|
|
| MPII_INTR_MASK_RESET);
|
|
|
|
#if NBIO > 0
|
|
if (ISSET(sc->sc_flags, MPII_F_RAID)) {
|
|
if (bio_register(sc->sc_dev, mpii_ioctl) != 0)
|
|
panic("%s: controller registration failed",
|
|
DEVNAME(sc));
|
|
|
|
if (mpii_create_sensors(sc) != 0)
|
|
aprint_error_dev(self, "unable to create sensors\n");
|
|
}
|
|
#endif
|
|
|
|
return;
|
|
|
|
free_dev:
|
|
if (sc->sc_devs)
|
|
free(sc->sc_devs, M_DEVBUF);
|
|
|
|
free_queues:
|
|
bus_dmamap_sync(sc->sc_dmat, MPII_DMA_MAP(sc->sc_reply_freeq),
|
|
0, sc->sc_reply_free_qdepth * 4, BUS_DMASYNC_POSTREAD);
|
|
mpii_dmamem_free(sc, sc->sc_reply_freeq);
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, MPII_DMA_MAP(sc->sc_reply_postq),
|
|
0, sc->sc_reply_post_qdepth * 8, BUS_DMASYNC_POSTREAD);
|
|
mpii_dmamem_free(sc, sc->sc_reply_postq);
|
|
|
|
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, MPII_NOSLEEP)) != NULL)
|
|
bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap);
|
|
mpii_dmamem_free(sc, sc->sc_requests);
|
|
free(sc->sc_ccbs, M_DEVBUF);
|
|
|
|
unmap:
|
|
bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
|
|
sc->sc_ios = 0;
|
|
}
|
|
|
|
static int
|
|
mpii_detach(device_t self, int flags)
|
|
{
|
|
struct mpii_softc *sc = device_private(self);
|
|
int error;
|
|
struct mpii_ccb *ccb;
|
|
|
|
if ((error = config_detach_children(sc->sc_dev, flags)) != 0)
|
|
return error;
|
|
|
|
#if NBIO > 0
|
|
mpii_destroy_sensors(sc);
|
|
bio_unregister(sc->sc_dev);
|
|
#endif /* NBIO > 0 */
|
|
|
|
if (sc->sc_ih != NULL) {
|
|
if (sc->sc_devs)
|
|
free(sc->sc_devs, M_DEVBUF);
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, MPII_DMA_MAP(sc->sc_reply_freeq),
|
|
0, sc->sc_reply_free_qdepth * 4, BUS_DMASYNC_POSTREAD);
|
|
mpii_dmamem_free(sc, sc->sc_reply_freeq);
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, MPII_DMA_MAP(sc->sc_reply_postq),
|
|
0, sc->sc_reply_post_qdepth * 8, BUS_DMASYNC_POSTREAD);
|
|
mpii_dmamem_free(sc, sc->sc_reply_postq);
|
|
|
|
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);
|
|
|
|
while ((ccb = mpii_get_ccb(sc, MPII_NOSLEEP)) != NULL)
|
|
bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap);
|
|
mpii_dmamem_free(sc, sc->sc_requests);
|
|
free(sc->sc_ccbs, M_DEVBUF);
|
|
|
|
pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
|
|
sc->sc_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);
|
|
}
|
|
|
|
static int
|
|
mpii_rescan(device_t self, const char *ifattr, const int *locators)
|
|
{
|
|
struct mpii_softc *sc = device_private(self);
|
|
|
|
if (sc->sc_child != NULL)
|
|
return 0;
|
|
|
|
sc->sc_child = config_found_sm_loc(self, ifattr, locators, &sc->sc_chan,
|
|
scsiprint, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
mpii_childdetached(device_t self, device_t child)
|
|
{
|
|
struct mpii_softc *sc = device_private(self);
|
|
|
|
KASSERT(self == sc->sc_dev);
|
|
KASSERT(child == sc->sc_child);
|
|
|
|
if (child == sc->sc_child)
|
|
sc->sc_child = NULL;
|
|
}
|
|
|
|
static int
|
|
mpii_intr(void *arg)
|
|
{
|
|
struct mpii_rcb_list evts = SIMPLEQ_HEAD_INITIALIZER(evts);
|
|
struct mpii_ccb_list ccbs = SIMPLEQ_HEAD_INITIALIZER(ccbs);
|
|
struct mpii_softc *sc = arg;
|
|
struct mpii_reply_descr *postq = sc->sc_reply_postq_kva, *rdp;
|
|
struct mpii_ccb *ccb;
|
|
struct mpii_rcb *rcb;
|
|
int smid;
|
|
int rv = 0;
|
|
|
|
mutex_enter(&sc->sc_rep_mtx);
|
|
bus_dmamap_sync(sc->sc_dmat,
|
|
MPII_DMA_MAP(sc->sc_reply_postq),
|
|
0, 8 * sc->sc_reply_post_qdepth,
|
|
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
|
|
|
|
for (;;) {
|
|
rdp = &postq[sc->sc_reply_post_host_index];
|
|
if ((rdp->reply_flags & MPII_REPLY_DESCR_TYPE_MASK) ==
|
|
MPII_REPLY_DESCR_UNUSED)
|
|
break;
|
|
if (rdp->data == 0xffffffff) {
|
|
/*
|
|
* ioc is still writing to the reply post queue
|
|
* race condition - bail!
|
|
*/
|
|
break;
|
|
}
|
|
|
|
smid = le16toh(rdp->smid);
|
|
rcb = mpii_reply(sc, rdp);
|
|
|
|
if (smid) {
|
|
ccb = &sc->sc_ccbs[smid - 1];
|
|
ccb->ccb_state = MPII_CCB_READY;
|
|
ccb->ccb_rcb = rcb;
|
|
SIMPLEQ_INSERT_TAIL(&ccbs, ccb, u.ccb_link);
|
|
} else
|
|
SIMPLEQ_INSERT_TAIL(&evts, rcb, u.rcb_link);
|
|
|
|
sc->sc_reply_post_host_index++;
|
|
sc->sc_reply_post_host_index %= sc->sc_reply_post_qdepth;
|
|
rv = 1;
|
|
}
|
|
|
|
bus_dmamap_sync(sc->sc_dmat,
|
|
MPII_DMA_MAP(sc->sc_reply_postq),
|
|
0, 8 * sc->sc_reply_post_qdepth,
|
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
|
|
|
if (rv)
|
|
mpii_write_reply_post(sc, sc->sc_reply_post_host_index);
|
|
|
|
mutex_exit(&sc->sc_rep_mtx);
|
|
|
|
if (rv == 0)
|
|
return (0);
|
|
|
|
while ((ccb = SIMPLEQ_FIRST(&ccbs)) != NULL) {
|
|
SIMPLEQ_REMOVE_HEAD(&ccbs, u.ccb_link);
|
|
ccb->ccb_done(ccb);
|
|
}
|
|
while ((rcb = SIMPLEQ_FIRST(&evts)) != NULL) {
|
|
SIMPLEQ_REMOVE_HEAD(&evts, u.rcb_link);
|
|
mpii_event_process(sc, rcb);
|
|
}
|
|
|
|
return (1);
|
|
}
|
|
|
|
static int
|
|
mpii_load_xs(struct mpii_ccb *ccb)
|
|
{
|
|
struct mpii_softc *sc = ccb->ccb_sc;
|
|
struct scsipi_xfer *xs = ccb->ccb_cookie;
|
|
struct mpii_ccb_bundle *mcb = ccb->ccb_cmd;
|
|
struct mpii_msg_scsi_io *io = &mcb->mcb_io;
|
|
struct mpii_sge *sge = NULL, *nsge = &mcb->mcb_sgl[0];
|
|
struct mpii_sge *ce = NULL, *nce = NULL;
|
|
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->xs_control & XS_CTL_NOSLEEP) ?
|
|
BUS_DMA_NOWAIT : BUS_DMA_WAITOK);
|
|
if (error) {
|
|
aprint_error_dev(sc->sc_dev, "error %d loading dmamap\n",
|
|
error);
|
|
return (1);
|
|
}
|
|
|
|
/* safe default staring flags */
|
|
flags = MPII_SGE_FL_TYPE_SIMPLE | MPII_SGE_FL_SIZE_64;
|
|
/* if data out */
|
|
if (xs->xs_control & XS_CTL_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: %" PRId64 " 0x%016" PRIx64 "\n",
|
|
DEVNAME(sc), i, (int64_t)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->xs_control & XS_CTL_DATA_IN) ? BUS_DMASYNC_PREREAD :
|
|
BUS_DMASYNC_PREWRITE);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static 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 %#" PRIx64 " %#x\n", DEVNAME(sc),
|
|
(uint64_t)r, rv);
|
|
|
|
return (rv);
|
|
}
|
|
|
|
static void
|
|
mpii_write(struct mpii_softc *sc, bus_size_t r, u_int32_t v)
|
|
{
|
|
DNPRINTF(MPII_D_RW, "%s: mpii_write %#" PRIx64 " %#x\n", DEVNAME(sc),
|
|
(uint64_t)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);
|
|
}
|
|
|
|
|
|
static 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 %#" PRIx64 " %#x %#x\n",
|
|
DEVNAME(sc), (uint64_t)r, mask, target);
|
|
|
|
for (i = 0; i < 15000; i++) {
|
|
if ((mpii_read(sc, r) & mask) == target)
|
|
return (0);
|
|
delay(1000);
|
|
}
|
|
|
|
return (1);
|
|
}
|
|
|
|
static 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 %#" PRIx64 " %#x %#x\n",
|
|
DEVNAME(sc), (uint64_t)r, mask, target);
|
|
|
|
for (i = 0; i < 15000; i++) {
|
|
if ((mpii_read(sc, r) & mask) != target)
|
|
return (0);
|
|
delay(1000);
|
|
}
|
|
|
|
return (1);
|
|
}
|
|
|
|
|
|
static 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:
|
|
DNPRINTF(MPII_D_MISC, "%s: mpii_init ioc is oper\n",
|
|
DEVNAME(sc));
|
|
if (sc->sc_ioc_event_replay)
|
|
mpii_reset_soft(sc);
|
|
else
|
|
mpii_reset_hard(sc);
|
|
break;
|
|
|
|
case MPII_DOORBELL_STATE_FAULT:
|
|
DNPRINTF(MPII_D_MISC, "%s: mpii_init ioc is being "
|
|
"reset hard\n" , DEVNAME(sc));
|
|
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);
|
|
}
|
|
|
|
static 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) {
|
|
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);
|
|
}
|
|
|
|
static 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) {
|
|
DNPRINTF(MPII_D_MISC, "%s: mpii_reset_hard failure to enable "
|
|
"diagnostic read/write\n", DEVNAME(sc));
|
|
return(1);
|
|
}
|
|
|
|
/* reset ioc */
|
|
mpii_write(sc, MPII_HOSTDIAG, MPII_HOSTDIAG_RESET_ADAPTER);
|
|
|
|
/* 240 milliseconds */
|
|
delay(240000);
|
|
|
|
|
|
/* 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);
|
|
}
|
|
|
|
static 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);
|
|
}
|
|
|
|
static 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] = le16toh(mpii_read_db(sc) & MPII_DOORBELL_DATA_MASK);
|
|
mpii_write_intr(sc, 0);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static 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: %zd 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);
|
|
}
|
|
|
|
static void
|
|
mpii_empty_done(struct mpii_ccb *ccb)
|
|
{
|
|
/* nothing to do */
|
|
}
|
|
|
|
static 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), le16toh(ifp.ioc_status),
|
|
le16toh(ifp.ioc_exceptions));
|
|
DNPRINTF(MPII_D_MISC, "%s: iocloginfo: 0x%08x\n", DEVNAME(sc),
|
|
le32toh(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), le16toh(ifp.product_id), le16toh(ifp.request_credit));
|
|
DNPRINTF(MPII_D_MISC, "%s: ioc_capabilities: 0x%08x\n", DEVNAME(sc),
|
|
le32toh(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), le16toh(ifp.ioc_request_frame_size));
|
|
DNPRINTF(MPII_D_MISC, "%s: maxtargets: 0x%04x "
|
|
"maxinitiators: 0x%04x\n", DEVNAME(sc),
|
|
le16toh(ifp.max_targets), le16toh(ifp.max_initiators));
|
|
DNPRINTF(MPII_D_MISC, "%s: maxenclosures: 0x%04x "
|
|
"maxsasexpanders: 0x%04x\n", DEVNAME(sc),
|
|
le16toh(ifp.max_enclosures), le16toh(ifp.max_sas_expanders));
|
|
DNPRINTF(MPII_D_MISC, "%s: highprioritycredit: 0x%04x "
|
|
"protocolflags: 0x%02x\n", DEVNAME(sc),
|
|
le16toh(ifp.high_priority_credit), le16toh(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,
|
|
le16toh(ifp.max_reply_descriptor_post_queue_depth));
|
|
DNPRINTF(MPII_D_MISC, "%s: maxpersistententries: 0x%04x "
|
|
"maxdevhandle: 0x%02x\n", DEVNAME(sc),
|
|
le16toh(ifp.max_persistent_entries), le16toh(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;
|
|
|
|
sc->sc_num_ports = ifp.number_of_ports;
|
|
sc->sc_ioc_event_replay = (le32toh(ifp.ioc_capabilities) &
|
|
MPII_IOCFACTS_CAPABILITY_EVENT_REPLAY) ? 1 : 0;
|
|
sc->sc_max_enclosures = le16toh(ifp.max_enclosures);
|
|
sc->sc_max_expanders = le16toh(ifp.max_sas_expanders);
|
|
sc->sc_max_volumes = ifp.max_volumes;
|
|
sc->sc_max_devices = ifp.max_volumes + le16toh(ifp.max_targets);
|
|
sc->sc_num_channels = 1;
|
|
|
|
if (ISSET(le32toh(ifp.ioc_capabilities),
|
|
MPII_IOCFACTS_CAPABILITY_INTEGRATED_RAID))
|
|
SET(sc->sc_flags, MPII_F_RAID);
|
|
|
|
sc->sc_request_depth = MIN(le16toh(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 + 15) / 16) * 16;
|
|
|
|
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;
|
|
|
|
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 = ((le16toh(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 = (le16toh(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);
|
|
|
|
}
|
|
|
|
static 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 = htole32(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),
|
|
le16toh(iip.ioc_status));
|
|
DNPRINTF(MPII_D_MISC, "%s: ioc_loginfo: 0x%08x\n", DEVNAME(sc),
|
|
le32toh(iip.ioc_loginfo));
|
|
|
|
if ((iip.ioc_status != MPII_IOCSTATUS_SUCCESS) || (iip.ioc_loginfo))
|
|
return (1);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
mpii_push_reply(struct mpii_softc *sc, struct mpii_rcb *rcb)
|
|
{
|
|
u_int32_t *rfp;
|
|
|
|
if (rcb == NULL)
|
|
return;
|
|
|
|
rfp = MPII_DMA_KVA(sc->sc_reply_freeq);
|
|
rfp[sc->sc_reply_free_host_index] = rcb->rcb_reply_dva;
|
|
|
|
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);
|
|
}
|
|
|
|
static int
|
|
mpii_portfacts(struct mpii_softc *sc)
|
|
{
|
|
struct mpii_msg_portfacts_request *pfq;
|
|
struct mpii_msg_portfacts_reply *pfp;
|
|
struct mpii_ccb *ccb;
|
|
int rv = 1;
|
|
|
|
DNPRINTF(MPII_D_MISC, "%s: mpii_portfacts\n", DEVNAME(sc));
|
|
|
|
ccb = mpii_get_ccb(sc, 0);
|
|
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(*pfq));
|
|
|
|
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) != 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: %p\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),
|
|
le16toh(pfp->ioc_status));
|
|
DNPRINTF(MPII_D_MISC, "%s: ioc_loginfo: 0x%08x\n", DEVNAME(sc),
|
|
le32toh(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),
|
|
le16toh(pfp->max_posted_cmd_buffers));
|
|
|
|
sc->sc_porttype = pfp->port_type;
|
|
|
|
mpii_push_reply(sc, ccb->ccb_rcb);
|
|
rv = 0;
|
|
err:
|
|
mpii_put_ccb(sc, ccb);
|
|
|
|
return (rv);
|
|
}
|
|
|
|
static void
|
|
mpii_eventack(struct work *wk, void *cookie)
|
|
{
|
|
struct mpii_softc *sc = cookie;
|
|
struct mpii_ccb *ccb;
|
|
struct mpii_rcb *rcb = (void *)wk;
|
|
struct mpii_msg_event_reply *enp;
|
|
struct mpii_msg_eventack_request *eaq;
|
|
|
|
ccb = mpii_get_ccb(sc, 0);
|
|
|
|
enp = (struct mpii_msg_event_reply *)rcb->rcb_reply;
|
|
|
|
ccb->ccb_done = mpii_eventack_done;
|
|
eaq = ccb->ccb_cmd;
|
|
|
|
eaq->function = MPII_FUNCTION_EVENT_ACK;
|
|
|
|
eaq->event = enp->event;
|
|
eaq->event_context = enp->event_context;
|
|
|
|
mpii_push_reply(sc, rcb);
|
|
|
|
mpii_start(sc, ccb);
|
|
|
|
}
|
|
|
|
static void
|
|
mpii_eventack_done(struct mpii_ccb *ccb)
|
|
{
|
|
struct mpii_softc *sc = ccb->ccb_sc;
|
|
|
|
DNPRINTF(MPII_D_EVT, "%s: event ack done\n", DEVNAME(sc));
|
|
|
|
mpii_push_reply(sc, ccb->ccb_rcb);
|
|
mpii_put_ccb(sc, ccb);
|
|
}
|
|
|
|
static int
|
|
mpii_portenable(struct mpii_softc *sc)
|
|
{
|
|
struct mpii_msg_portenable_request *peq;
|
|
struct mpii_ccb *ccb;
|
|
|
|
DNPRINTF(MPII_D_MISC, "%s: mpii_portenable\n", DEVNAME(sc));
|
|
|
|
ccb = mpii_get_ccb(sc, 0);
|
|
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) != 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);
|
|
}
|
|
|
|
mpii_push_reply(sc, ccb->ccb_rcb);
|
|
mpii_put_ccb(sc, ccb);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
mpii_cfg_coalescing(struct mpii_softc *sc)
|
|
{
|
|
struct mpii_cfg_hdr hdr;
|
|
struct mpii_cfg_ioc_pg1 pg;
|
|
|
|
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),
|
|
le32toh(pg.flags));
|
|
DNPRINTF(MPII_D_MISC, "%s: coalescing_timeout: %d\n", DEVNAME(sc),
|
|
le32toh(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);
|
|
|
|
if (!ISSET(le32toh(pg.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);
|
|
}
|
|
|
|
#define MPII_EVENT_MASKALL(enq) do { \
|
|
enq->event_masks[0] = 0xffffffff; \
|
|
enq->event_masks[1] = 0xffffffff; \
|
|
enq->event_masks[2] = 0xffffffff; \
|
|
enq->event_masks[3] = 0xffffffff; \
|
|
} while (0)
|
|
|
|
#define MPII_EVENT_UNMASK(enq, evt) do { \
|
|
enq->event_masks[evt / 32] &= \
|
|
htole32(~(1 << (evt % 32))); \
|
|
} while (0)
|
|
|
|
static int
|
|
mpii_eventnotify(struct mpii_softc *sc)
|
|
{
|
|
struct mpii_msg_event_request *enq;
|
|
struct mpii_ccb *ccb;
|
|
|
|
ccb = mpii_get_ccb(sc, 0);
|
|
if (ccb == NULL) {
|
|
DNPRINTF(MPII_D_MISC, "%s: mpii_eventnotify ccb_get\n",
|
|
DEVNAME(sc));
|
|
return (1);
|
|
}
|
|
|
|
ccb->ccb_done = mpii_eventnotify_done;
|
|
enq = ccb->ccb_cmd;
|
|
|
|
enq->function = MPII_FUNCTION_EVENT_NOTIFICATION;
|
|
|
|
/*
|
|
* Enable reporting of the following events:
|
|
*
|
|
* MPII_EVENT_SAS_DISCOVERY
|
|
* MPII_EVENT_SAS_TOPOLOGY_CHANGE_LIST
|
|
* MPII_EVENT_SAS_DEVICE_STATUS_CHANGE
|
|
* MPII_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE
|
|
* MPII_EVENT_IR_CONFIGURATION_CHANGE_LIST
|
|
* MPII_EVENT_IR_VOLUME
|
|
* MPII_EVENT_IR_PHYSICAL_DISK
|
|
* MPII_EVENT_IR_OPERATION_STATUS
|
|
*/
|
|
|
|
MPII_EVENT_MASKALL(enq);
|
|
MPII_EVENT_UNMASK(enq, MPII_EVENT_SAS_DISCOVERY);
|
|
MPII_EVENT_UNMASK(enq, MPII_EVENT_SAS_TOPOLOGY_CHANGE_LIST);
|
|
MPII_EVENT_UNMASK(enq, MPII_EVENT_SAS_DEVICE_STATUS_CHANGE);
|
|
MPII_EVENT_UNMASK(enq, MPII_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE);
|
|
MPII_EVENT_UNMASK(enq, MPII_EVENT_IR_CONFIGURATION_CHANGE_LIST);
|
|
MPII_EVENT_UNMASK(enq, MPII_EVENT_IR_VOLUME);
|
|
MPII_EVENT_UNMASK(enq, MPII_EVENT_IR_PHYSICAL_DISK);
|
|
MPII_EVENT_UNMASK(enq, MPII_EVENT_IR_OPERATION_STATUS);
|
|
|
|
mpii_start(sc, ccb);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
mpii_eventnotify_done(struct mpii_ccb *ccb)
|
|
{
|
|
struct mpii_softc *sc = ccb->ccb_sc;
|
|
struct mpii_rcb *rcb = ccb->ccb_rcb;
|
|
|
|
DNPRINTF(MPII_D_EVT, "%s: mpii_eventnotify_done\n", DEVNAME(sc));
|
|
|
|
mpii_put_ccb(sc, ccb);
|
|
mpii_event_process(sc, rcb);
|
|
}
|
|
|
|
static void
|
|
mpii_event_raid(struct mpii_softc *sc, struct mpii_msg_event_reply *enp)
|
|
{
|
|
struct mpii_evt_ir_cfg_change_list *ccl;
|
|
struct mpii_evt_ir_cfg_element *ce;
|
|
struct mpii_device *dev;
|
|
u_int16_t type;
|
|
int i;
|
|
|
|
ccl = (struct mpii_evt_ir_cfg_change_list *)(enp + 1);
|
|
|
|
if (ccl->num_elements == 0)
|
|
return;
|
|
if (ISSET(le32toh(ccl->flags), MPII_EVT_IR_CFG_CHANGE_LIST_FOREIGN))
|
|
/* bail on foreign configurations */
|
|
return;
|
|
|
|
ce = (struct mpii_evt_ir_cfg_element *)(ccl + 1);
|
|
|
|
for (i = 0; i < ccl->num_elements; i++, ce++) {
|
|
type = (le16toh(ce->element_flags) &
|
|
MPII_EVT_IR_CFG_ELEMENT_TYPE_MASK);
|
|
|
|
switch (type) {
|
|
case MPII_EVT_IR_CFG_ELEMENT_TYPE_VOLUME:
|
|
switch (ce->reason_code) {
|
|
case MPII_EVT_IR_CFG_ELEMENT_RC_ADDED:
|
|
case MPII_EVT_IR_CFG_ELEMENT_RC_VOLUME_CREATED:
|
|
if (mpii_find_dev(sc,
|
|
le16toh(ce->vol_dev_handle))) {
|
|
printf("%s: device %#x is already "
|
|
"configured\n", DEVNAME(sc),
|
|
le16toh(ce->vol_dev_handle));
|
|
break;
|
|
}
|
|
dev = malloc(sizeof(*dev), M_DEVBUF,
|
|
M_NOWAIT | M_ZERO);
|
|
if (!dev) {
|
|
printf("%s: failed to allocate a "
|
|
"device structure\n", DEVNAME(sc));
|
|
break;
|
|
}
|
|
SET(dev->flags, MPII_DF_VOLUME);
|
|
dev->slot = sc->sc_vd_id_low;
|
|
dev->dev_handle = le16toh(ce->vol_dev_handle);
|
|
if (mpii_insert_dev(sc, dev)) {
|
|
free(dev, M_DEVBUF);
|
|
break;
|
|
}
|
|
mpii_cache_enable(sc, dev);
|
|
sc->sc_vd_count++;
|
|
break;
|
|
case MPII_EVT_IR_CFG_ELEMENT_RC_REMOVED:
|
|
case MPII_EVT_IR_CFG_ELEMENT_RC_VOLUME_DELETED:
|
|
if (!(dev = mpii_find_dev(sc,
|
|
le16toh(ce->vol_dev_handle))))
|
|
break;
|
|
mpii_remove_dev(sc, dev);
|
|
sc->sc_vd_count--;
|
|
break;
|
|
}
|
|
break;
|
|
case MPII_EVT_IR_CFG_ELEMENT_TYPE_VOLUME_DISK:
|
|
if (ce->reason_code ==
|
|
MPII_EVT_IR_CFG_ELEMENT_RC_PD_CREATED ||
|
|
ce->reason_code ==
|
|
MPII_EVT_IR_CFG_ELEMENT_RC_HIDE) {
|
|
/* there should be an underlying sas drive */
|
|
if (!(dev = mpii_find_dev(sc,
|
|
le16toh(ce->phys_disk_dev_handle))))
|
|
break;
|
|
/* promoted from a hot spare? */
|
|
CLR(dev->flags, MPII_DF_HOT_SPARE);
|
|
SET(dev->flags, MPII_DF_VOLUME_DISK |
|
|
MPII_DF_HIDDEN);
|
|
}
|
|
break;
|
|
case MPII_EVT_IR_CFG_ELEMENT_TYPE_HOT_SPARE:
|
|
if (ce->reason_code ==
|
|
MPII_EVT_IR_CFG_ELEMENT_RC_HIDE) {
|
|
/* there should be an underlying sas drive */
|
|
if (!(dev = mpii_find_dev(sc,
|
|
le16toh(ce->phys_disk_dev_handle))))
|
|
break;
|
|
SET(dev->flags, MPII_DF_HOT_SPARE |
|
|
MPII_DF_HIDDEN);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
mpii_event_sas(struct mpii_softc *sc, struct mpii_msg_event_reply *enp)
|
|
{
|
|
struct mpii_evt_sas_tcl *tcl;
|
|
struct mpii_evt_phy_entry *pe;
|
|
struct mpii_device *dev;
|
|
int i;
|
|
|
|
tcl = (struct mpii_evt_sas_tcl *)(enp + 1);
|
|
|
|
if (tcl->num_entries == 0)
|
|
return;
|
|
|
|
pe = (struct mpii_evt_phy_entry *)(tcl + 1);
|
|
|
|
for (i = 0; i < tcl->num_entries; i++, pe++) {
|
|
switch (pe->phy_status & MPII_EVENT_SAS_TOPO_PS_RC_MASK) {
|
|
case MPII_EVENT_SAS_TOPO_PS_RC_ADDED:
|
|
if (mpii_find_dev(sc, le16toh(pe->dev_handle))) {
|
|
printf("%s: device %#x is already "
|
|
"configured\n", DEVNAME(sc),
|
|
le16toh(pe->dev_handle));
|
|
break;
|
|
}
|
|
dev = malloc(sizeof(*dev), M_DEVBUF, M_NOWAIT | M_ZERO);
|
|
if (!dev) {
|
|
printf("%s: failed to allocate a "
|
|
"device structure\n", DEVNAME(sc));
|
|
break;
|
|
}
|
|
dev->slot = sc->sc_pd_id_start + tcl->start_phy_num + i;
|
|
dev->dev_handle = le16toh(pe->dev_handle);
|
|
dev->phy_num = tcl->start_phy_num + i;
|
|
if (tcl->enclosure_handle)
|
|
dev->physical_port = tcl->physical_port;
|
|
dev->enclosure = le16toh(tcl->enclosure_handle);
|
|
dev->expander = le16toh(tcl->expander_handle);
|
|
if (mpii_insert_dev(sc, dev)) {
|
|
free(dev, M_DEVBUF);
|
|
break;
|
|
}
|
|
break;
|
|
case MPII_EVENT_SAS_TOPO_PS_RC_MISSING:
|
|
if (!(dev = mpii_find_dev(sc,
|
|
le16toh(pe->dev_handle))))
|
|
break;
|
|
mpii_remove_dev(sc, dev);
|
|
#if 0
|
|
if (sc->sc_scsibus) {
|
|
SET(dev->flags, MPII_DF_DETACH);
|
|
scsi_activate(sc->sc_scsibus, dev->slot, -1,
|
|
DVACT_DEACTIVATE);
|
|
if (scsi_task(mpii_event_defer, sc,
|
|
dev, 0) != 0)
|
|
printf("%s: unable to run device "
|
|
"detachment routine\n",
|
|
DEVNAME(sc));
|
|
}
|
|
#else
|
|
mpii_event_defer(sc, dev);
|
|
#endif /* XXX */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
mpii_event_process(struct mpii_softc *sc, struct mpii_rcb *rcb)
|
|
{
|
|
struct mpii_msg_event_reply *enp;
|
|
|
|
enp = (struct mpii_msg_event_reply *)rcb->rcb_reply;
|
|
|
|
DNPRINTF(MPII_D_EVT, "%s: mpii_event_process: %#x\n", DEVNAME(sc),
|
|
le32toh(enp->event));
|
|
|
|
switch (le32toh(enp->event)) {
|
|
case MPII_EVENT_EVENT_CHANGE:
|
|
/* should be properly ignored */
|
|
break;
|
|
case MPII_EVENT_SAS_DISCOVERY: {
|
|
struct mpii_evt_sas_discovery *esd =
|
|
(struct mpii_evt_sas_discovery *)(enp + 1);
|
|
|
|
if (esd->reason_code ==
|
|
MPII_EVENT_SAS_DISC_REASON_CODE_COMPLETED &&
|
|
esd->discovery_status != 0)
|
|
printf("%s: sas discovery completed with status %#x\n",
|
|
DEVNAME(sc), esd->discovery_status);
|
|
}
|
|
break;
|
|
case MPII_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
|
|
mpii_event_sas(sc, enp);
|
|
break;
|
|
case MPII_EVENT_SAS_DEVICE_STATUS_CHANGE:
|
|
break;
|
|
case MPII_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE:
|
|
break;
|
|
case MPII_EVENT_IR_VOLUME: {
|
|
struct mpii_evt_ir_volume *evd =
|
|
(struct mpii_evt_ir_volume *)(enp + 1);
|
|
struct mpii_device *dev;
|
|
#if NBIO > 0
|
|
const char *vol_states[] = {
|
|
BIOC_SVINVALID_S,
|
|
BIOC_SVOFFLINE_S,
|
|
BIOC_SVBUILDING_S,
|
|
BIOC_SVONLINE_S,
|
|
BIOC_SVDEGRADED_S,
|
|
BIOC_SVONLINE_S,
|
|
};
|
|
#endif
|
|
|
|
if (cold)
|
|
break;
|
|
if (!(dev = mpii_find_dev(sc, le16toh(evd->vol_dev_handle))))
|
|
break;
|
|
#if NBIO > 0
|
|
if (evd->reason_code == MPII_EVENT_IR_VOL_RC_STATE_CHANGED)
|
|
printf("%s: volume %d state changed from %s to %s\n",
|
|
DEVNAME(sc), dev->slot - sc->sc_vd_id_low,
|
|
vol_states[evd->prev_value],
|
|
vol_states[evd->new_value]);
|
|
#endif
|
|
if (evd->reason_code == MPII_EVENT_IR_VOL_RC_STATUS_CHANGED &&
|
|
ISSET(evd->new_value, MPII_CFG_RAID_VOL_0_STATUS_RESYNC) &&
|
|
!ISSET(evd->prev_value, MPII_CFG_RAID_VOL_0_STATUS_RESYNC))
|
|
printf("%s: started resync on a volume %d\n",
|
|
DEVNAME(sc), dev->slot - sc->sc_vd_id_low);
|
|
}
|
|
break;
|
|
case MPII_EVENT_IR_PHYSICAL_DISK:
|
|
break;
|
|
case MPII_EVENT_IR_CONFIGURATION_CHANGE_LIST:
|
|
mpii_event_raid(sc, enp);
|
|
break;
|
|
case MPII_EVENT_IR_OPERATION_STATUS: {
|
|
struct mpii_evt_ir_status *evs =
|
|
(struct mpii_evt_ir_status *)(enp + 1);
|
|
struct mpii_device *dev;
|
|
|
|
if (!(dev = mpii_find_dev(sc, le16toh(evs->vol_dev_handle))))
|
|
break;
|
|
if (evs->operation == MPII_EVENT_IR_RAIDOP_RESYNC)
|
|
dev->percent = evs->percent;
|
|
break;
|
|
}
|
|
default:
|
|
DNPRINTF(MPII_D_EVT, "%s: unhandled event 0x%02x\n",
|
|
DEVNAME(sc), le32toh(enp->event));
|
|
}
|
|
|
|
if (enp->ack_required)
|
|
workqueue_enqueue(sc->sc_ssb_evt_ackwk, &rcb->u.rcb_wk, NULL);
|
|
else
|
|
mpii_push_reply(sc, rcb);
|
|
}
|
|
|
|
static void
|
|
mpii_event_defer(void *xsc, void *arg)
|
|
{
|
|
struct mpii_softc *sc = xsc;
|
|
struct mpii_device *dev = arg;
|
|
|
|
if (ISSET(dev->flags, MPII_DF_DETACH)) {
|
|
mpii_sas_remove_device(sc, dev->dev_handle);
|
|
#if 0
|
|
if (!ISSET(dev->flags, MPII_DF_HIDDEN)) {
|
|
scsi_detach_target(sc->sc_scsibus, dev->slot,
|
|
DETACH_FORCE);
|
|
}
|
|
#endif /* XXX */
|
|
free(dev, M_DEVBUF);
|
|
|
|
} else if (ISSET(dev->flags, MPII_DF_ATTACH)) {
|
|
CLR(dev->flags, MPII_DF_ATTACH);
|
|
#if 0
|
|
if (!ISSET(dev->flags, MPII_DF_HIDDEN))
|
|
scsi_probe_target(sc->sc_scsibus, dev->slot);
|
|
#endif /* XXX */
|
|
}
|
|
}
|
|
|
|
static void
|
|
mpii_sas_remove_device(struct mpii_softc *sc, u_int16_t handle)
|
|
{
|
|
struct mpii_msg_scsi_task_request *stq;
|
|
struct mpii_msg_sas_oper_request *soq;
|
|
struct mpii_ccb *ccb;
|
|
|
|
ccb = mpii_get_ccb(sc, 0);
|
|
if (ccb == NULL)
|
|
return;
|
|
|
|
stq = ccb->ccb_cmd;
|
|
stq->function = MPII_FUNCTION_SCSI_TASK_MGMT;
|
|
stq->task_type = MPII_SCSI_TASK_TARGET_RESET;
|
|
stq->dev_handle = htole16(handle);
|
|
|
|
ccb->ccb_done = mpii_empty_done;
|
|
mpii_wait(sc, ccb);
|
|
|
|
if (ccb->ccb_rcb != NULL)
|
|
mpii_push_reply(sc, ccb->ccb_rcb);
|
|
|
|
/* reuse a ccb */
|
|
ccb->ccb_state = MPII_CCB_READY;
|
|
ccb->ccb_rcb = NULL;
|
|
|
|
soq = ccb->ccb_cmd;
|
|
bzero(soq, sizeof(*soq));
|
|
soq->function = MPII_FUNCTION_SAS_IO_UNIT_CONTROL;
|
|
soq->operation = MPII_SAS_OP_REMOVE_DEVICE;
|
|
soq->dev_handle = htole16(handle);
|
|
|
|
ccb->ccb_done = mpii_empty_done;
|
|
mpii_wait(sc, ccb);
|
|
if (ccb->ccb_rcb != NULL)
|
|
mpii_push_reply(sc, ccb->ccb_rcb);
|
|
}
|
|
|
|
static int
|
|
mpii_get_ioc_pg8(struct mpii_softc *sc)
|
|
{
|
|
struct mpii_cfg_hdr hdr;
|
|
struct mpii_cfg_ioc_pg8 *page;
|
|
size_t pagelen;
|
|
u_int16_t flags;
|
|
int pad = 0, rv = 0;
|
|
|
|
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_CFG, "%s: mpii_get_ioc_pg8 unable to fetch "
|
|
"header for IOC page 8\n", DEVNAME(sc));
|
|
return (1);
|
|
}
|
|
|
|
pagelen = hdr.page_length * 4; /* dwords to bytes */
|
|
|
|
page = malloc(pagelen, M_TEMP, M_NOWAIT);
|
|
if (page == NULL) {
|
|
DNPRINTF(MPII_D_CFG, "%s: mpii_get_ioc_pg8 unable to allocate "
|
|
"space for ioc config page 8\n", DEVNAME(sc));
|
|
return (1);
|
|
}
|
|
|
|
if (mpii_cfg_page(sc, 0, &hdr, 1, page, pagelen) != 0) {
|
|
DNPRINTF(MPII_D_CFG, "%s: mpii_get_raid unable to fetch IOC "
|
|
"page 8\n", DEVNAME(sc));
|
|
rv = 1;
|
|
goto out;
|
|
}
|
|
|
|
DNPRINTF(MPII_D_CFG, "%s: numdevsperenclosure: 0x%02x\n", DEVNAME(sc),
|
|
page->num_devs_per_enclosure);
|
|
DNPRINTF(MPII_D_CFG, "%s: maxpersistententries: 0x%04x "
|
|
"maxnumphysicalmappedids: 0x%04x\n", DEVNAME(sc),
|
|
le16toh(page->max_persistent_entries),
|
|
le16toh(page->max_num_physical_mapped_ids));
|
|
DNPRINTF(MPII_D_CFG, "%s: flags: 0x%04x\n", DEVNAME(sc),
|
|
le16toh(page->flags));
|
|
DNPRINTF(MPII_D_CFG, "%s: irvolumemappingflags: 0x%04x\n",
|
|
DEVNAME(sc), le16toh(page->ir_volume_mapping_flags));
|
|
|
|
if (page->flags & MPII_IOC_PG8_FLAGS_RESERVED_TARGETID_0)
|
|
pad = 1;
|
|
|
|
flags = page->ir_volume_mapping_flags &
|
|
MPII_IOC_PG8_IRFLAGS_VOLUME_MAPPING_MODE_MASK;
|
|
if (ISSET(sc->sc_flags, MPII_F_RAID)) {
|
|
if (flags == MPII_IOC_PG8_IRFLAGS_LOW_VOLUME_MAPPING) {
|
|
sc->sc_vd_id_low += pad;
|
|
pad = sc->sc_max_volumes; /* for sc_pd_id_start */
|
|
} else
|
|
sc->sc_vd_id_low = sc->sc_max_devices -
|
|
sc->sc_max_volumes;
|
|
}
|
|
|
|
sc->sc_pd_id_start += pad;
|
|
|
|
DNPRINTF(MPII_D_MAP, "%s: mpii_get_ioc_pg8 mapping: sc_pd_id_start: %d "
|
|
"sc_vd_id_low: %d sc_max_volumes: %d\n", DEVNAME(sc),
|
|
sc->sc_pd_id_start, sc->sc_vd_id_low, sc->sc_max_volumes);
|
|
|
|
out:
|
|
free(page, M_TEMP);
|
|
|
|
return(rv);
|
|
}
|
|
|
|
static int
|
|
mpii_req_cfg_header(struct mpii_softc *sc, u_int8_t type, u_int8_t number,
|
|
u_int32_t address, int flags, void *p)
|
|
{
|
|
struct mpii_msg_config_request *cq;
|
|
struct mpii_msg_config_reply *cp;
|
|
struct mpii_cfg_hdr *hdr = p;
|
|
struct mpii_ccb *ccb;
|
|
struct mpii_ecfg_hdr *ehdr = p;
|
|
int etype = 0;
|
|
int rv = 0;
|
|
|
|
DNPRINTF(MPII_D_MISC, "%s: mpii_req_cfg_header type: %#x number: %x "
|
|
"address: 0x%08x flags: 0x%x\n", DEVNAME(sc), type, number,
|
|
address, flags);
|
|
|
|
ccb = mpii_get_ccb(sc, ISSET(flags, MPII_PG_POLL) ? MPII_NOSLEEP : 0);
|
|
if (ccb == NULL) {
|
|
DNPRINTF(MPII_D_MISC, "%s: mpii_cfg_header ccb_get\n",
|
|
DEVNAME(sc));
|
|
return (1);
|
|
}
|
|
|
|
if (ISSET(flags, MPII_PG_EXTENDED)) {
|
|
etype = type;
|
|
type = MPII_CONFIG_REQ_PAGE_TYPE_EXTENDED;
|
|
}
|
|
|
|
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);
|
|
|
|
ccb->ccb_done = mpii_empty_done;
|
|
if (ISSET(flags, MPII_PG_POLL)) {
|
|
if (mpii_poll(sc, ccb) != 0) {
|
|
DNPRINTF(MPII_D_MISC, "%s: mpii_cfg_header poll\n",
|
|
DEVNAME(sc));
|
|
return (1);
|
|
}
|
|
} else
|
|
mpii_wait(sc, ccb);
|
|
|
|
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 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),
|
|
le16toh(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),
|
|
le16toh(cp->ioc_status));
|
|
DNPRINTF(MPII_D_MISC, "%s: ioc_loginfo: 0x%08x\n", DEVNAME(sc),
|
|
le32toh(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 (le16toh(cp->ioc_status) != MPII_IOCSTATUS_SUCCESS)
|
|
rv = 1;
|
|
else if (ISSET(flags, MPII_PG_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);
|
|
mpii_put_ccb(sc, ccb);
|
|
|
|
return (rv);
|
|
}
|
|
|
|
static int
|
|
mpii_req_cfg_page(struct mpii_softc *sc, u_int32_t address, int flags,
|
|
void *p, int read, void *page, size_t len)
|
|
{
|
|
struct mpii_msg_config_request *cq;
|
|
struct mpii_msg_config_reply *cp;
|
|
struct mpii_cfg_hdr *hdr = p;
|
|
struct mpii_ccb *ccb;
|
|
struct mpii_ecfg_hdr *ehdr = p;
|
|
u_int64_t dva;
|
|
char *kva;
|
|
int page_length;
|
|
int rv = 0;
|
|
|
|
DNPRINTF(MPII_D_MISC, "%s: mpii_cfg_page address: %d read: %d "
|
|
"type: %x\n", DEVNAME(sc), address, read, hdr->page_type);
|
|
|
|
page_length = ISSET(flags, MPII_PG_EXTENDED) ?
|
|
le16toh(ehdr->ext_page_length) : hdr->page_length;
|
|
|
|
if (len > MPII_REQUEST_SIZE - sizeof(struct mpii_msg_config_request) ||
|
|
len < page_length * 4)
|
|
return (1);
|
|
|
|
ccb = mpii_get_ccb(sc,
|
|
ISSET(flags, MPII_PG_POLL) ? MPII_NOSLEEP : 0);
|
|
if (ccb == NULL) {
|
|
DNPRINTF(MPII_D_MISC, "%s: mpii_cfg_page ccb_get\n",
|
|
DEVNAME(sc));
|
|
return (1);
|
|
}
|
|
|
|
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 (ISSET(flags, MPII_PG_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 |
|
|
MPII_SGE_FL_SIZE_64 | (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);
|
|
|
|
ccb->ccb_done = mpii_empty_done;
|
|
if (ISSET(flags, MPII_PG_POLL)) {
|
|
if (mpii_poll(sc, ccb) != 0) {
|
|
DNPRINTF(MPII_D_MISC, "%s: mpii_cfg_header poll\n",
|
|
DEVNAME(sc));
|
|
return (1);
|
|
}
|
|
} else
|
|
mpii_wait(sc, ccb);
|
|
|
|
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 "
|
|
"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),
|
|
le16toh(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),
|
|
le16toh(cp->ioc_status));
|
|
DNPRINTF(MPII_D_MISC, "%s: ioc_loginfo: 0x%08x\n", DEVNAME(sc),
|
|
le32toh(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 (le16toh(cp->ioc_status) != MPII_IOCSTATUS_SUCCESS)
|
|
rv = 1;
|
|
else if (read)
|
|
bcopy(kva, page, len);
|
|
|
|
mpii_push_reply(sc, ccb->ccb_rcb);
|
|
mpii_put_ccb(sc, ccb);
|
|
|
|
return (rv);
|
|
}
|
|
|
|
static struct mpii_rcb *
|
|
mpii_reply(struct mpii_softc *sc, struct mpii_reply_descr *rdp)
|
|
{
|
|
struct mpii_rcb *rcb = NULL;
|
|
u_int32_t rfid;
|
|
|
|
DNPRINTF(MPII_D_INTR, "%s: mpii_reply\n", DEVNAME(sc));
|
|
|
|
if ((rdp->reply_flags & MPII_REPLY_DESCR_TYPE_MASK) ==
|
|
MPII_REPLY_DESCR_ADDRESS_REPLY) {
|
|
rfid = (le32toh(rdp->frame_addr) -
|
|
(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 * rfid,
|
|
MPII_REPLY_SIZE, BUS_DMASYNC_POSTREAD);
|
|
|
|
rcb = &sc->sc_rcbs[rfid];
|
|
}
|
|
|
|
memset(rdp, 0xff, sizeof(*rdp));
|
|
|
|
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);
|
|
|
|
return (rcb);
|
|
}
|
|
|
|
static struct mpii_dmamem *
|
|
mpii_dmamem_alloc(struct mpii_softc *sc, size_t size)
|
|
{
|
|
struct mpii_dmamem *mdm;
|
|
int nsegs;
|
|
|
|
mdm = malloc(sizeof(*mdm), 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;
|
|
|
|
DNPRINTF(MPII_D_MEM,
|
|
" kva: %p dva: 0x%" PRIx64 " map: %p size: %" PRId64 "\n",
|
|
mdm->mdm_kva, (uint64_t)mdm->mdm_map->dm_segs[0].ds_addr,
|
|
mdm->mdm_map, (uint64_t)size);
|
|
|
|
bzero(mdm->mdm_kva, size);
|
|
|
|
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);
|
|
}
|
|
|
|
static void
|
|
mpii_dmamem_free(struct mpii_softc *sc, struct mpii_dmamem *mdm)
|
|
{
|
|
DNPRINTF(MPII_D_MEM, "%s: mpii_dmamem_free %p\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);
|
|
}
|
|
|
|
static int
|
|
mpii_alloc_dev(struct mpii_softc *sc)
|
|
{
|
|
sc->sc_devs = malloc(sc->sc_max_devices *
|
|
sizeof(struct mpii_device *), M_DEVBUF, M_NOWAIT | M_ZERO);
|
|
if (sc->sc_devs == NULL)
|
|
return (1);
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
mpii_insert_dev(struct mpii_softc *sc, struct mpii_device *dev)
|
|
{
|
|
int slot = dev->slot; /* initial hint */
|
|
|
|
if (!dev || slot < 0)
|
|
return (1);
|
|
while (slot < sc->sc_max_devices && sc->sc_devs[slot] != NULL)
|
|
slot++;
|
|
if (slot >= sc->sc_max_devices)
|
|
return (1);
|
|
dev->slot = slot;
|
|
sc->sc_devs[slot] = dev;
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
mpii_remove_dev(struct mpii_softc *sc, struct mpii_device *dev)
|
|
{
|
|
int i;
|
|
|
|
if (!dev)
|
|
return (1);
|
|
for (i = 0; i < sc->sc_max_devices; i++)
|
|
if (sc->sc_devs[i] &&
|
|
sc->sc_devs[i]->dev_handle == dev->dev_handle) {
|
|
sc->sc_devs[i] = NULL;
|
|
return (0);
|
|
}
|
|
return (1);
|
|
}
|
|
|
|
static struct mpii_device *
|
|
mpii_find_dev(struct mpii_softc *sc, u_int16_t handle)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < sc->sc_max_devices; i++)
|
|
if (sc->sc_devs[i] && sc->sc_devs[i]->dev_handle == handle)
|
|
return (sc->sc_devs[i]);
|
|
return (NULL);
|
|
}
|
|
|
|
static int
|
|
mpii_alloc_ccbs(struct mpii_softc *sc)
|
|
{
|
|
struct mpii_ccb *ccb;
|
|
u_int8_t *cmd;
|
|
int i;
|
|
|
|
SIMPLEQ_INIT(&sc->sc_ccb_free);
|
|
|
|
sc->sc_ccbs = malloc(sizeof(*ccb) * (sc->sc_request_depth-1),
|
|
M_DEVBUF, M_NOWAIT | 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. so 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: %p map: %p "
|
|
"sc: %p smid: %#x offs: %#" PRIx64 " cmd: %#" PRIx64 " dva: %#" PRIx64 "\n",
|
|
DEVNAME(sc), i, ccb, ccb->ccb_dmamap, ccb->ccb_sc,
|
|
ccb->ccb_smid, (uint64_t)ccb->ccb_offset,
|
|
(uint64_t)ccb->ccb_cmd, (uint64_t)ccb->ccb_cmd_dva);
|
|
|
|
mpii_put_ccb(sc, ccb);
|
|
}
|
|
|
|
return (0);
|
|
|
|
free_maps:
|
|
while ((ccb = mpii_get_ccb(sc, MPII_NOSLEEP)) != 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);
|
|
}
|
|
|
|
static void
|
|
mpii_put_ccb(struct mpii_softc *sc, struct mpii_ccb *ccb)
|
|
{
|
|
KASSERT(ccb->ccb_sc == sc);
|
|
DNPRINTF(MPII_D_CCB, "%s: mpii_put_ccb %p\n", DEVNAME(sc), ccb);
|
|
|
|
ccb->ccb_state = MPII_CCB_FREE;
|
|
ccb->ccb_cookie = NULL;
|
|
ccb->ccb_done = NULL;
|
|
ccb->ccb_rcb = NULL;
|
|
bzero(ccb->ccb_cmd, MPII_REQUEST_SIZE);
|
|
|
|
mutex_enter(&sc->sc_ccb_free_mtx);
|
|
SIMPLEQ_INSERT_HEAD(&sc->sc_ccb_free, ccb, u.ccb_link);
|
|
cv_signal(&sc->sc_ccb_free_cv);
|
|
mutex_exit(&sc->sc_ccb_free_mtx);
|
|
}
|
|
|
|
static struct mpii_ccb *
|
|
mpii_get_ccb(struct mpii_softc *sc, int flags)
|
|
{
|
|
struct mpii_ccb *ccb;
|
|
|
|
mutex_enter(&sc->sc_ccb_free_mtx);
|
|
while ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_free)) == NULL) {
|
|
if (flags & MPII_NOSLEEP)
|
|
break;
|
|
cv_wait(&sc->sc_ccb_free_cv, &sc->sc_ccb_free_mtx);
|
|
}
|
|
|
|
if (ccb != NULL) {
|
|
SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_free, u.ccb_link);
|
|
ccb->ccb_state = MPII_CCB_READY;
|
|
KASSERT(ccb->ccb_sc == sc);
|
|
}
|
|
mutex_exit(&sc->sc_ccb_free_mtx);
|
|
|
|
DNPRINTF(MPII_D_CCB, "%s: mpii_get_ccb %p\n", DEVNAME(sc), ccb);
|
|
|
|
return (ccb);
|
|
}
|
|
|
|
static 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_NOWAIT);
|
|
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);
|
|
}
|
|
|
|
static 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);
|
|
}
|
|
}
|
|
|
|
static void
|
|
mpii_start(struct mpii_softc *sc, struct mpii_ccb *ccb)
|
|
{
|
|
struct mpii_request_header *rhp;
|
|
struct mpii_request_descr descr;
|
|
u_int32_t *rdp = (u_int32_t *)&descr;
|
|
|
|
DNPRINTF(MPII_D_RW, "%s: mpii_start %#" PRIx64 "\n", DEVNAME(sc),
|
|
(uint64_t)ccb->ccb_cmd_dva);
|
|
|
|
rhp = ccb->ccb_cmd;
|
|
|
|
bzero(&descr, sizeof(descr));
|
|
|
|
switch (rhp->function) {
|
|
case MPII_FUNCTION_SCSI_IO_REQUEST:
|
|
descr.request_flags = MPII_REQ_DESCR_SCSI_IO;
|
|
descr.dev_handle = htole16(ccb->ccb_dev_handle);
|
|
break;
|
|
case MPII_FUNCTION_SCSI_TASK_MGMT:
|
|
descr.request_flags = MPII_REQ_DESCR_HIGH_PRIORITY;
|
|
break;
|
|
default:
|
|
descr.request_flags = MPII_REQ_DESCR_DEFAULT;
|
|
}
|
|
|
|
descr.vf_id = sc->sc_vf_id;
|
|
descr.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_DESCR_POST_LOW (0x%08x) write "
|
|
"0x%08x\n", DEVNAME(sc), MPII_REQ_DESCR_POST_LOW, *rdp);
|
|
|
|
DNPRINTF(MPII_D_RW, "%s: MPII_REQ_DESCR_POST_HIGH (0x%08x) write "
|
|
"0x%08x\n", DEVNAME(sc), MPII_REQ_DESCR_POST_HIGH, *(rdp+1));
|
|
|
|
mutex_enter(&sc->sc_req_mtx);
|
|
mpii_write(sc, MPII_REQ_DESCR_POST_LOW, htole32(*rdp));
|
|
mpii_write(sc, MPII_REQ_DESCR_POST_HIGH, htole32(*(rdp+1)));
|
|
mutex_exit(&sc->sc_req_mtx);
|
|
}
|
|
|
|
static int
|
|
mpii_poll(struct mpii_softc *sc, struct mpii_ccb *ccb)
|
|
{
|
|
void (*done)(struct mpii_ccb *);
|
|
void *cookie;
|
|
int rv = 1;
|
|
|
|
DNPRINTF(MPII_D_INTR, "%s: mpii_complete\n", DEVNAME(sc));
|
|
|
|
done = ccb->ccb_done;
|
|
cookie = ccb->ccb_cookie;
|
|
|
|
ccb->ccb_done = mpii_poll_done;
|
|
ccb->ccb_cookie = &rv;
|
|
|
|
mpii_start(sc, ccb);
|
|
|
|
while (rv == 1) {
|
|
/* avoid excessive polling */
|
|
if (mpii_reply_waiting(sc))
|
|
mpii_intr(sc);
|
|
else
|
|
delay(10);
|
|
}
|
|
|
|
ccb->ccb_cookie = cookie;
|
|
done(ccb);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
mpii_poll_done(struct mpii_ccb *ccb)
|
|
{
|
|
int *rv = ccb->ccb_cookie;
|
|
|
|
*rv = 0;
|
|
}
|
|
|
|
static 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: %p = 0x%08x\n",
|
|
DEVNAME(sc), i,
|
|
&kva[i], (u_int)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] = 0xffffffffffffffffllu;
|
|
DNPRINTF(MPII_D_MISC, "%s: %d: %p = 0x%" PRIx64 "\n",
|
|
DEVNAME(sc), i, &kva64[i], kva64[i]);
|
|
}
|
|
|
|
return (0);
|
|
|
|
free_reply_freeq:
|
|
|
|
mpii_dmamem_free(sc, sc->sc_reply_freeq);
|
|
return (1);
|
|
}
|
|
|
|
static 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);
|
|
}
|
|
|
|
static void
|
|
mpii_wait(struct mpii_softc *sc, struct mpii_ccb *ccb)
|
|
{
|
|
struct mpii_ccb_wait mpii_ccb_wait;
|
|
void (*done)(struct mpii_ccb *);
|
|
void *cookie;
|
|
|
|
done = ccb->ccb_done;
|
|
cookie = ccb->ccb_cookie;
|
|
|
|
ccb->ccb_done = mpii_wait_done;
|
|
ccb->ccb_cookie = &mpii_ccb_wait;
|
|
|
|
mutex_init(&mpii_ccb_wait.mpii_ccbw_mtx, MUTEX_DEFAULT, IPL_BIO);
|
|
cv_init(&mpii_ccb_wait.mpii_ccbw_cv, "mpii_wait");
|
|
|
|
/* XXX this will wait forever for the ccb to complete */
|
|
|
|
mpii_start(sc, ccb);
|
|
|
|
mutex_enter(&mpii_ccb_wait.mpii_ccbw_mtx);
|
|
while (ccb->ccb_cookie != NULL) {
|
|
cv_wait(&mpii_ccb_wait.mpii_ccbw_cv,
|
|
&mpii_ccb_wait.mpii_ccbw_mtx);
|
|
}
|
|
mutex_exit(&mpii_ccb_wait.mpii_ccbw_mtx);
|
|
mutex_destroy(&mpii_ccb_wait.mpii_ccbw_mtx);
|
|
cv_destroy(&mpii_ccb_wait.mpii_ccbw_cv);
|
|
|
|
ccb->ccb_cookie = cookie;
|
|
done(ccb);
|
|
}
|
|
|
|
static void
|
|
mpii_wait_done(struct mpii_ccb *ccb)
|
|
{
|
|
struct mpii_ccb_wait *mpii_ccb_waitp = ccb->ccb_cookie;
|
|
|
|
mutex_enter(&mpii_ccb_waitp->mpii_ccbw_mtx);
|
|
ccb->ccb_cookie = NULL;
|
|
cv_signal(&mpii_ccb_waitp->mpii_ccbw_cv);
|
|
mutex_exit(&mpii_ccb_waitp->mpii_ccbw_mtx);
|
|
}
|
|
|
|
static void
|
|
mpii_minphys(struct buf *bp)
|
|
{
|
|
DNPRINTF(MPII_D_MISC, "mpii_minphys: %d\n", bp->b_bcount);
|
|
|
|
/* XXX currently using MPII_MAXFER = MAXPHYS */
|
|
if (bp->b_bcount > MPII_MAXFER) {
|
|
bp->b_bcount = MPII_MAXFER;
|
|
minphys(bp);
|
|
}
|
|
}
|
|
|
|
static void
|
|
mpii_scsipi_request(struct scsipi_channel *chan, scsipi_adapter_req_t req,
|
|
void *arg)
|
|
{
|
|
struct scsipi_periph *periph;
|
|
struct scsipi_xfer *xs;
|
|
struct scsipi_adapter *adapt = chan->chan_adapter;
|
|
struct mpii_softc *sc = device_private(adapt->adapt_dev);
|
|
struct mpii_ccb *ccb;
|
|
struct mpii_ccb_bundle *mcb;
|
|
struct mpii_msg_scsi_io *io;
|
|
struct mpii_device *dev;
|
|
int target;
|
|
int timeout;
|
|
|
|
DNPRINTF(MPII_D_CMD, "%s: mpii_scsipi_request\n", DEVNAME(sc));
|
|
switch (req) {
|
|
case ADAPTER_REQ_GROW_RESOURCES:
|
|
/* Not supported. */
|
|
return;
|
|
case ADAPTER_REQ_SET_XFER_MODE:
|
|
{
|
|
struct scsipi_xfer_mode *xm = arg;
|
|
xm->xm_mode = PERIPH_CAP_TQING;
|
|
xm->xm_period = 0;
|
|
xm->xm_offset = 0;
|
|
scsipi_async_event(&sc->sc_chan, ASYNC_EVENT_XFER_MODE, xm);
|
|
return;
|
|
}
|
|
case ADAPTER_REQ_RUN_XFER:
|
|
break;
|
|
}
|
|
|
|
xs = arg;
|
|
periph = xs->xs_periph;
|
|
target = periph->periph_target;
|
|
|
|
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.scsi_sense.response_code =
|
|
SSD_RCODE_VALID | SSD_RCODE_CURRENT;
|
|
xs->sense.scsi_sense.flags = SKEY_ILLEGAL_REQUEST;
|
|
xs->sense.scsi_sense.asc = 0x20;
|
|
xs->error = XS_SENSE;
|
|
scsipi_done(xs);
|
|
return;
|
|
}
|
|
|
|
if ((dev = sc->sc_devs[target]) == NULL) {
|
|
/* device no longer exists */
|
|
xs->error = XS_SELTIMEOUT;
|
|
scsipi_done(xs);
|
|
return;
|
|
}
|
|
|
|
ccb = mpii_get_ccb(sc, MPII_NOSLEEP);
|
|
if (ccb == NULL) {
|
|
xs->error = XS_RESOURCE_SHORTAGE;
|
|
scsipi_done(xs);
|
|
return;
|
|
}
|
|
|
|
DNPRINTF(MPII_D_CMD, "%s: ccb_smid: %d xs->xs_control: 0x%x\n",
|
|
DEVNAME(sc), ccb->ccb_smid, xs->xs_control);
|
|
|
|
ccb->ccb_cookie = xs;
|
|
ccb->ccb_done = mpii_scsi_cmd_done;
|
|
ccb->ccb_dev_handle = dev->dev_handle;
|
|
|
|
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 = htole16(ccb->ccb_dev_handle);
|
|
io->lun[0] = htobe16(periph->periph_lun);
|
|
|
|
switch (xs->xs_control & (XS_CTL_DATA_IN | XS_CTL_DATA_OUT)) {
|
|
case XS_CTL_DATA_IN:
|
|
io->direction = MPII_SCSIIO_DIR_READ;
|
|
break;
|
|
case XS_CTL_DATA_OUT:
|
|
io->direction = MPII_SCSIIO_DIR_WRITE;
|
|
break;
|
|
default:
|
|
io->direction = MPII_SCSIIO_DIR_NONE;
|
|
}
|
|
|
|
io->tagging = MPII_SCSIIO_ATTR_SIMPLE_Q;
|
|
|
|
memcpy(io->cdb, xs->cmd, 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;
|
|
mpii_put_ccb(sc, ccb);
|
|
scsipi_done(xs);
|
|
return;
|
|
}
|
|
|
|
DNPRINTF(MPII_D_CMD, "%s: sizeof(mpii_msg_scsi_io): %ld "
|
|
"sizeof(mpii_ccb_bundle): %ld sge offset: 0x%02lx\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->xs_control & XS_CTL_POLL) {
|
|
if (mpii_poll(sc, ccb) != 0) {
|
|
xs->error = XS_DRIVER_STUFFUP;
|
|
mpii_put_ccb(sc, ccb);
|
|
scsipi_done(xs);
|
|
}
|
|
return;
|
|
}
|
|
timeout = mstohz(xs->timeout);
|
|
if (timeout == 0)
|
|
timeout = 1;
|
|
callout_reset(&xs->xs_callout, timeout, mpii_scsi_cmd_tmo, ccb);
|
|
|
|
DNPRINTF(MPII_D_CMD, "%s: mpii_scsipi_request(): opcode: %02x "
|
|
"datalen: %d\n", DEVNAME(sc), xs->cmd->opcode, xs->datalen);
|
|
|
|
mpii_start(sc, ccb);
|
|
}
|
|
|
|
static void
|
|
mpii_scsi_cmd_tmo(void *xccb)
|
|
{
|
|
struct mpii_ccb *ccb = xccb;
|
|
struct mpii_softc *sc = ccb->ccb_sc;
|
|
|
|
printf("%s: mpii_scsi_cmd_tmo\n", DEVNAME(sc));
|
|
|
|
mutex_enter(&sc->sc_ccb_mtx);
|
|
if (ccb->ccb_state == MPII_CCB_QUEUED) {
|
|
ccb->ccb_state = MPII_CCB_TIMEOUT;
|
|
workqueue_enqueue(sc->sc_ssb_tmowk, &ccb->u.ccb_wk, NULL);
|
|
}
|
|
mutex_exit(&sc->sc_ccb_mtx);
|
|
}
|
|
|
|
static void
|
|
mpii_scsi_cmd_tmo_handler(struct work *wk, void *cookie)
|
|
{
|
|
struct mpii_softc *sc = cookie;
|
|
struct mpii_ccb *tccb;
|
|
struct mpii_ccb *ccb;
|
|
struct mpii_msg_scsi_task_request *stq;
|
|
|
|
ccb = (void *)wk;
|
|
tccb = mpii_get_ccb(sc, 0);
|
|
|
|
mutex_enter(&sc->sc_ccb_mtx);
|
|
if (ccb->ccb_state != MPII_CCB_TIMEOUT) {
|
|
mpii_put_ccb(sc, tccb);
|
|
}
|
|
/* should remove any other ccbs for the same dev handle */
|
|
mutex_exit(&sc->sc_ccb_mtx);
|
|
|
|
stq = tccb->ccb_cmd;
|
|
stq->function = MPII_FUNCTION_SCSI_TASK_MGMT;
|
|
stq->task_type = MPII_SCSI_TASK_TARGET_RESET;
|
|
stq->dev_handle = htole16(ccb->ccb_dev_handle);
|
|
|
|
tccb->ccb_done = mpii_scsi_cmd_tmo_done;
|
|
mpii_start(sc, tccb);
|
|
}
|
|
|
|
static void
|
|
mpii_scsi_cmd_tmo_done(struct mpii_ccb *tccb)
|
|
{
|
|
mpii_put_ccb(tccb->ccb_sc, tccb);
|
|
}
|
|
|
|
static u_int8_t
|
|
map_scsi_status(u_int8_t mpii_scsi_status)
|
|
{
|
|
u_int8_t scsi_status;
|
|
|
|
switch (mpii_scsi_status)
|
|
{
|
|
case MPII_SCSIIO_ERR_STATUS_SUCCESS:
|
|
scsi_status = SCSI_OK;
|
|
break;
|
|
|
|
case MPII_SCSIIO_ERR_STATUS_CHECK_COND:
|
|
scsi_status = SCSI_CHECK;
|
|
break;
|
|
|
|
case MPII_SCSIIO_ERR_STATUS_BUSY:
|
|
scsi_status = SCSI_BUSY;
|
|
break;
|
|
|
|
case MPII_SCSIIO_ERR_STATUS_INTERMEDIATE:
|
|
scsi_status = SCSI_INTERM;
|
|
break;
|
|
|
|
case MPII_SCSIIO_ERR_STATUS_INTERMEDIATE_CONDMET:
|
|
scsi_status = SCSI_INTERM;
|
|
break;
|
|
|
|
case MPII_SCSIIO_ERR_STATUS_RESERVATION_CONFLICT:
|
|
scsi_status = SCSI_RESV_CONFLICT;
|
|
break;
|
|
|
|
case MPII_SCSIIO_ERR_STATUS_CMD_TERM:
|
|
case MPII_SCSIIO_ERR_STATUS_TASK_ABORTED:
|
|
scsi_status = SCSI_TERMINATED;
|
|
break;
|
|
|
|
case MPII_SCSIIO_ERR_STATUS_TASK_SET_FULL:
|
|
scsi_status = SCSI_QUEUE_FULL;
|
|
break;
|
|
|
|
case MPII_SCSIIO_ERR_STATUS_ACA_ACTIVE:
|
|
scsi_status = SCSI_ACA_ACTIVE;
|
|
break;
|
|
|
|
default:
|
|
/* XXX: for the lack of anything better and other than OK */
|
|
scsi_status = 0xFF;
|
|
break;
|
|
}
|
|
|
|
return scsi_status;
|
|
}
|
|
|
|
static void
|
|
mpii_scsi_cmd_done(struct mpii_ccb *ccb)
|
|
{
|
|
struct mpii_msg_scsi_io_error *sie;
|
|
struct mpii_softc *sc = ccb->ccb_sc;
|
|
struct scsipi_xfer *xs = ccb->ccb_cookie;
|
|
struct mpii_ccb_bundle *mcb = ccb->ccb_cmd;
|
|
bus_dmamap_t dmap = ccb->ccb_dmamap;
|
|
bool timeout = 0;
|
|
|
|
callout_stop(&xs->xs_callout);
|
|
mutex_enter(&sc->sc_ccb_mtx);
|
|
if (ccb->ccb_state == MPII_CCB_TIMEOUT)
|
|
timeout = 1;
|
|
ccb->ccb_state = MPII_CCB_READY;
|
|
mutex_exit(&sc->sc_ccb_mtx);
|
|
|
|
if (xs->datalen != 0) {
|
|
bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize,
|
|
(xs->xs_control & XS_CTL_DATA_IN) ? BUS_DMASYNC_POSTREAD :
|
|
BUS_DMASYNC_POSTWRITE);
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, dmap);
|
|
}
|
|
|
|
xs->error = XS_NOERROR;
|
|
xs->resid = 0;
|
|
|
|
if (ccb->ccb_rcb == NULL) {
|
|
/* no scsi error, we're ok so drop out early */
|
|
xs->status = SCSI_OK;
|
|
mpii_put_ccb(sc, ccb);
|
|
scsipi_done(xs);
|
|
return;
|
|
}
|
|
|
|
sie = ccb->ccb_rcb->rcb_reply;
|
|
|
|
DNPRINTF(MPII_D_CMD, "%s: mpii_scsi_cmd_done xs cmd: 0x%02x len: %d "
|
|
"xs_control 0x%x\n", DEVNAME(sc), xs->cmd->opcode, xs->datalen,
|
|
xs->xs_control);
|
|
DNPRINTF(MPII_D_CMD, "%s: dev_handle: %d msg_length: %d "
|
|
"function: 0x%02x\n", DEVNAME(sc), le16toh(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, le16toh(sie->ioc_status));
|
|
DNPRINTF(MPII_D_CMD, "%s: ioc_loginfo: 0x%08x\n", DEVNAME(sc),
|
|
le32toh(sie->ioc_loginfo));
|
|
DNPRINTF(MPII_D_CMD, "%s: transfer_count: %d\n", DEVNAME(sc),
|
|
le32toh(sie->transfer_count));
|
|
DNPRINTF(MPII_D_CMD, "%s: sense_count: %d\n", DEVNAME(sc),
|
|
le32toh(sie->sense_count));
|
|
DNPRINTF(MPII_D_CMD, "%s: response_info: 0x%08x\n", DEVNAME(sc),
|
|
le32toh(sie->response_info));
|
|
DNPRINTF(MPII_D_CMD, "%s: task_tag: 0x%04x\n", DEVNAME(sc),
|
|
le16toh(sie->task_tag));
|
|
DNPRINTF(MPII_D_CMD, "%s: bidirectional_transfer_count: 0x%08x\n",
|
|
DEVNAME(sc), le32toh(sie->bidirectional_transfer_count));
|
|
|
|
xs->status = map_scsi_status(sie->scsi_status);
|
|
|
|
switch (le16toh(sie->ioc_status) & MPII_IOCSTATUS_MASK) {
|
|
case MPII_IOCSTATUS_SCSI_DATA_UNDERRUN:
|
|
switch (sie->scsi_status) {
|
|
case MPII_SCSIIO_ERR_STATUS_CHECK_COND:
|
|
xs->error = XS_SENSE;
|
|
/*FALLTHROUGH*/
|
|
case MPII_SCSIIO_ERR_STATUS_SUCCESS:
|
|
xs->resid = xs->datalen - le32toh(sie->transfer_count);
|
|
break;
|
|
|
|
default:
|
|
xs->error = XS_DRIVER_STUFFUP;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case MPII_IOCSTATUS_SUCCESS:
|
|
case MPII_IOCSTATUS_SCSI_RECOVERED_ERROR:
|
|
switch (sie->scsi_status) {
|
|
case MPII_SCSIIO_ERR_STATUS_SUCCESS:
|
|
/*
|
|
* xs->resid = 0; - already set above
|
|
*
|
|
* XXX: check whether UNDERUN strategy
|
|
* would be appropriate here too.
|
|
* that would allow joining these cases.
|
|
*/
|
|
break;
|
|
|
|
case MPII_SCSIIO_ERR_STATUS_CHECK_COND:
|
|
xs->error = XS_SENSE;
|
|
break;
|
|
|
|
case MPII_SCSIIO_ERR_STATUS_BUSY:
|
|
case MPII_SCSIIO_ERR_STATUS_TASK_SET_FULL:
|
|
xs->error = XS_BUSY;
|
|
break;
|
|
|
|
default:
|
|
xs->error = XS_DRIVER_STUFFUP;
|
|
}
|
|
break;
|
|
|
|
case MPII_IOCSTATUS_BUSY:
|
|
case MPII_IOCSTATUS_INSUFFICIENT_RESOURCES:
|
|
xs->error = XS_BUSY;
|
|
break;
|
|
|
|
case MPII_IOCSTATUS_SCSI_IOC_TERMINATED:
|
|
case MPII_IOCSTATUS_SCSI_TASK_TERMINATED:
|
|
xs->error = timeout ? XS_TIMEOUT : XS_RESET;
|
|
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)
|
|
memcpy(&xs->sense, &mcb->mcb_sense, sizeof(xs->sense));
|
|
|
|
DNPRINTF(MPII_D_CMD, "%s: xs err: %d status: %#x\n", DEVNAME(sc),
|
|
xs->error, xs->status);
|
|
|
|
mpii_push_reply(sc, ccb->ccb_rcb);
|
|
mpii_put_ccb(sc, ccb);
|
|
scsipi_done(xs);
|
|
}
|
|
|
|
#if 0
|
|
static int
|
|
mpii_ioctl_cache(struct scsi_link *link, u_long cmd, struct dk_cache *dc)
|
|
{
|
|
struct mpii_softc *sc = (struct mpii_softc *)link->adapter_softc;
|
|
struct mpii_device *dev = sc->sc_devs[link->target];
|
|
struct mpii_cfg_raid_vol_pg0 *vpg;
|
|
struct mpii_msg_raid_action_request *req;
|
|
struct mpii_msg_raid_action_reply *rep;
|
|
struct mpii_cfg_hdr hdr;
|
|
struct mpii_ccb *ccb;
|
|
u_int32_t addr = MPII_CFG_RAID_VOL_ADDR_HANDLE | dev->dev_handle;
|
|
size_t pagelen;
|
|
int rv = 0;
|
|
int enabled;
|
|
|
|
if (mpii_req_cfg_header(sc, MPII_CONFIG_REQ_PAGE_TYPE_RAID_VOL, 0,
|
|
addr, MPII_PG_POLL, &hdr) != 0)
|
|
return (EINVAL);
|
|
|
|
pagelen = hdr.page_length * 4;
|
|
vpg = malloc(pagelen, M_TEMP, M_WAITOK | M_CANFAIL | M_ZERO);
|
|
if (vpg == NULL)
|
|
return (ENOMEM);
|
|
|
|
if (mpii_req_cfg_page(sc, addr, MPII_PG_POLL, &hdr, 1,
|
|
vpg, pagelen) != 0) {
|
|
rv = EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
enabled = ((le16toh(vpg->volume_settings) &
|
|
MPII_CFG_RAID_VOL_0_SETTINGS_CACHE_MASK) ==
|
|
MPII_CFG_RAID_VOL_0_SETTINGS_CACHE_ENABLED) ? 1 : 0;
|
|
|
|
if (cmd == DIOCGCACHE) {
|
|
dc->wrcache = enabled;
|
|
dc->rdcache = 0;
|
|
goto done;
|
|
} /* else DIOCSCACHE */
|
|
|
|
if (dc->rdcache) {
|
|
rv = EOPNOTSUPP;
|
|
goto done;
|
|
}
|
|
|
|
if (((dc->wrcache) ? 1 : 0) == enabled)
|
|
goto done;
|
|
|
|
ccb = mpii_get_ccb(sc, MPII_NOSLEEP);
|
|
if (ccb == NULL) {
|
|
rv = ENOMEM;
|
|
goto done;
|
|
}
|
|
|
|
ccb->ccb_done = mpii_empty_done;
|
|
|
|
req = ccb->ccb_cmd;
|
|
bzero(req, sizeof(*req));
|
|
req->function = MPII_FUNCTION_RAID_ACTION;
|
|
req->action = MPII_RAID_ACTION_CHANGE_VOL_WRITE_CACHE;
|
|
req->vol_dev_handle = htole16(dev->dev_handle);
|
|
req->action_data = htole32(dc->wrcache ?
|
|
MPII_RAID_VOL_WRITE_CACHE_ENABLE :
|
|
MPII_RAID_VOL_WRITE_CACHE_DISABLE);
|
|
|
|
if (mpii_poll(sc, ccb) != 0) {
|
|
rv = EIO;
|
|
goto done;
|
|
}
|
|
|
|
if (ccb->ccb_rcb != NULL) {
|
|
rep = ccb->ccb_rcb->rcb_reply;
|
|
if ((rep->ioc_status != MPII_IOCSTATUS_SUCCESS) ||
|
|
((rep->action_data[0] &
|
|
MPII_RAID_VOL_WRITE_CACHE_MASK) !=
|
|
(dc->wrcache ? MPII_RAID_VOL_WRITE_CACHE_ENABLE :
|
|
MPII_RAID_VOL_WRITE_CACHE_DISABLE)))
|
|
rv = EINVAL;
|
|
mpii_push_reply(sc, ccb->ccb_rcb);
|
|
}
|
|
|
|
mpii_put_ccb(sc, ccb);
|
|
|
|
done:
|
|
free(vpg, M_TEMP);
|
|
return (rv);
|
|
}
|
|
#endif
|
|
static int
|
|
mpii_cache_enable(struct mpii_softc *sc, struct mpii_device *dev)
|
|
{
|
|
struct mpii_cfg_raid_vol_pg0 *vpg;
|
|
struct mpii_msg_raid_action_request *req;
|
|
struct mpii_msg_raid_action_reply *rep;
|
|
struct mpii_cfg_hdr hdr;
|
|
struct mpii_ccb *ccb;
|
|
u_int32_t addr = MPII_CFG_RAID_VOL_ADDR_HANDLE | dev->dev_handle;
|
|
size_t pagelen;
|
|
int rv = 0;
|
|
int enabled;
|
|
|
|
if (mpii_req_cfg_header(sc, MPII_CONFIG_REQ_PAGE_TYPE_RAID_VOL, 0,
|
|
addr, MPII_PG_POLL, &hdr) != 0)
|
|
return (EINVAL);
|
|
|
|
pagelen = hdr.page_length * 4;
|
|
vpg = malloc(pagelen, M_TEMP, M_WAITOK | M_CANFAIL | M_ZERO);
|
|
if (vpg == NULL)
|
|
return (ENOMEM);
|
|
|
|
if (mpii_req_cfg_page(sc, addr, MPII_PG_POLL, &hdr, 1,
|
|
vpg, pagelen) != 0) {
|
|
rv = EINVAL;
|
|
goto done;
|
|
free(vpg, M_TEMP);
|
|
return (EINVAL);
|
|
}
|
|
|
|
enabled = ((le16toh(vpg->volume_settings) &
|
|
MPII_CFG_RAID_VOL_0_SETTINGS_CACHE_MASK) ==
|
|
MPII_CFG_RAID_VOL_0_SETTINGS_CACHE_ENABLED) ? 1 : 0;
|
|
aprint_normal_dev(sc->sc_dev, "target %d cache %s", dev->slot,
|
|
enabled ? "enabled" : "disabled, enabling");
|
|
aprint_normal("\n");
|
|
|
|
if (enabled == 0)
|
|
goto done;
|
|
|
|
ccb = mpii_get_ccb(sc, MPII_NOSLEEP);
|
|
if (ccb == NULL) {
|
|
rv = ENOMEM;
|
|
goto done;
|
|
}
|
|
|
|
ccb->ccb_done = mpii_empty_done;
|
|
|
|
req = ccb->ccb_cmd;
|
|
bzero(req, sizeof(*req));
|
|
req->function = MPII_FUNCTION_RAID_ACTION;
|
|
req->action = MPII_RAID_ACTION_CHANGE_VOL_WRITE_CACHE;
|
|
req->vol_dev_handle = htole16(dev->dev_handle);
|
|
req->action_data = htole32(
|
|
MPII_RAID_VOL_WRITE_CACHE_ENABLE);
|
|
|
|
if (mpii_poll(sc, ccb) != 0) {
|
|
rv = EIO;
|
|
goto done;
|
|
}
|
|
|
|
if (ccb->ccb_rcb != NULL) {
|
|
rep = ccb->ccb_rcb->rcb_reply;
|
|
if ((rep->ioc_status != MPII_IOCSTATUS_SUCCESS) ||
|
|
((rep->action_data[0] &
|
|
MPII_RAID_VOL_WRITE_CACHE_MASK) !=
|
|
MPII_RAID_VOL_WRITE_CACHE_ENABLE))
|
|
rv = EINVAL;
|
|
mpii_push_reply(sc, ccb->ccb_rcb);
|
|
}
|
|
|
|
mpii_put_ccb(sc, ccb);
|
|
|
|
done:
|
|
free(vpg, M_TEMP);
|
|
if (rv) {
|
|
aprint_error_dev(sc->sc_dev,
|
|
"enabling cache on target %d failed (%d)\n",
|
|
dev->slot, rv);
|
|
}
|
|
return (rv);
|
|
}
|
|
|
|
#if NBIO > 0
|
|
static int
|
|
mpii_ioctl(device_t dev, u_long cmd, void *addr)
|
|
{
|
|
struct mpii_softc *sc = device_private(dev);
|
|
int s, error = 0;
|
|
|
|
DNPRINTF(MPII_D_IOCTL, "%s: mpii_ioctl ", DEVNAME(sc));
|
|
KERNEL_LOCK(1, curlwp);
|
|
s = splbio();
|
|
|
|
switch (cmd) {
|
|
case BIOCINQ:
|
|
DNPRINTF(MPII_D_IOCTL, "inq\n");
|
|
error = mpii_ioctl_inq(sc, (struct bioc_inq *)addr);
|
|
break;
|
|
case BIOCVOL:
|
|
DNPRINTF(MPII_D_IOCTL, "vol\n");
|
|
error = mpii_ioctl_vol(sc, (struct bioc_vol *)addr);
|
|
break;
|
|
case BIOCDISK:
|
|
DNPRINTF(MPII_D_IOCTL, "disk\n");
|
|
error = mpii_ioctl_disk(sc, (struct bioc_disk *)addr);
|
|
break;
|
|
default:
|
|
DNPRINTF(MPII_D_IOCTL, " invalid ioctl\n");
|
|
error = EINVAL;
|
|
}
|
|
|
|
splx(s);
|
|
KERNEL_UNLOCK_ONE(curlwp);
|
|
return (error);
|
|
}
|
|
|
|
static int
|
|
mpii_ioctl_inq(struct mpii_softc *sc, struct bioc_inq *bi)
|
|
{
|
|
int i;
|
|
|
|
DNPRINTF(MPII_D_IOCTL, "%s: mpii_ioctl_inq\n", DEVNAME(sc));
|
|
|
|
strlcpy(bi->bi_dev, DEVNAME(sc), sizeof(bi->bi_dev));
|
|
for (i = 0; i < sc->sc_max_devices; i++)
|
|
if (sc->sc_devs[i] &&
|
|
ISSET(sc->sc_devs[i]->flags, MPII_DF_VOLUME))
|
|
bi->bi_novol++;
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
mpii_ioctl_vol(struct mpii_softc *sc, struct bioc_vol *bv)
|
|
{
|
|
struct mpii_cfg_raid_vol_pg0 *vpg;
|
|
struct mpii_cfg_hdr hdr;
|
|
struct mpii_device *dev;
|
|
struct scsipi_periph *periph;
|
|
size_t pagelen;
|
|
u_int16_t volh;
|
|
int rv, hcnt = 0;
|
|
|
|
DNPRINTF(MPII_D_IOCTL, "%s: mpii_ioctl_vol %d\n",
|
|
DEVNAME(sc), bv->bv_volid);
|
|
|
|
if ((dev = mpii_find_vol(sc, bv->bv_volid)) == NULL)
|
|
return (ENODEV);
|
|
volh = dev->dev_handle;
|
|
|
|
if (mpii_req_cfg_header(sc, MPII_CONFIG_REQ_PAGE_TYPE_RAID_VOL, 0,
|
|
MPII_CFG_RAID_VOL_ADDR_HANDLE | volh, 0, &hdr) != 0) {
|
|
printf("%s: unable to fetch header for raid volume page 0\n",
|
|
DEVNAME(sc));
|
|
return (EINVAL);
|
|
}
|
|
|
|
pagelen = hdr.page_length * 4;
|
|
vpg = malloc(pagelen, M_TEMP, M_WAITOK | M_CANFAIL | M_ZERO);
|
|
if (vpg == NULL) {
|
|
printf("%s: unable to allocate space for raid "
|
|
"volume page 0\n", DEVNAME(sc));
|
|
return (ENOMEM);
|
|
}
|
|
|
|
if (mpii_req_cfg_page(sc, MPII_CFG_RAID_VOL_ADDR_HANDLE | volh, 0,
|
|
&hdr, 1, vpg, pagelen) != 0) {
|
|
printf("%s: unable to fetch raid volume page 0\n",
|
|
DEVNAME(sc));
|
|
free(vpg, M_TEMP);
|
|
return (EINVAL);
|
|
}
|
|
|
|
switch (vpg->volume_state) {
|
|
case MPII_CFG_RAID_VOL_0_STATE_ONLINE:
|
|
case MPII_CFG_RAID_VOL_0_STATE_OPTIMAL:
|
|
bv->bv_status = BIOC_SVONLINE;
|
|
break;
|
|
case MPII_CFG_RAID_VOL_0_STATE_DEGRADED:
|
|
if (ISSET(le32toh(vpg->volume_status),
|
|
MPII_CFG_RAID_VOL_0_STATUS_RESYNC)) {
|
|
bv->bv_status = BIOC_SVREBUILD;
|
|
bv->bv_percent = dev->percent;
|
|
} else
|
|
bv->bv_status = BIOC_SVDEGRADED;
|
|
break;
|
|
case MPII_CFG_RAID_VOL_0_STATE_FAILED:
|
|
bv->bv_status = BIOC_SVOFFLINE;
|
|
break;
|
|
case MPII_CFG_RAID_VOL_0_STATE_INITIALIZING:
|
|
bv->bv_status = BIOC_SVBUILDING;
|
|
break;
|
|
case MPII_CFG_RAID_VOL_0_STATE_MISSING:
|
|
default:
|
|
bv->bv_status = BIOC_SVINVALID;
|
|
break;
|
|
}
|
|
|
|
switch (vpg->volume_type) {
|
|
case MPII_CFG_RAID_VOL_0_TYPE_RAID0:
|
|
bv->bv_level = 0;
|
|
break;
|
|
case MPII_CFG_RAID_VOL_0_TYPE_RAID1:
|
|
bv->bv_level = 1;
|
|
break;
|
|
case MPII_CFG_RAID_VOL_0_TYPE_RAID1E:
|
|
case MPII_CFG_RAID_VOL_0_TYPE_RAID10:
|
|
bv->bv_level = 10;
|
|
break;
|
|
default:
|
|
bv->bv_level = -1;
|
|
}
|
|
|
|
if ((rv = mpii_bio_hs(sc, NULL, 0, vpg->hot_spare_pool, &hcnt)) != 0) {
|
|
free(vpg, M_TEMP);
|
|
return (rv);
|
|
}
|
|
|
|
bv->bv_nodisk = vpg->num_phys_disks + hcnt;
|
|
|
|
bv->bv_size = le64toh(vpg->max_lba) * le16toh(vpg->block_size);
|
|
|
|
periph = scsipi_lookup_periph(&sc->sc_chan, dev->slot, 0);
|
|
if (periph != NULL) {
|
|
if (periph->periph_dev == NULL) {
|
|
snprintf(bv->bv_dev, sizeof(bv->bv_dev), "%s:%d",
|
|
DEVNAME(sc), dev->slot);
|
|
} else {
|
|
strlcpy(bv->bv_dev, device_xname(periph->periph_dev),
|
|
sizeof(bv->bv_dev));
|
|
}
|
|
}
|
|
|
|
free(vpg, M_TEMP);
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
mpii_ioctl_disk(struct mpii_softc *sc, struct bioc_disk *bd)
|
|
{
|
|
struct mpii_cfg_raid_vol_pg0 *vpg;
|
|
struct mpii_cfg_raid_vol_pg0_physdisk *pd;
|
|
struct mpii_cfg_hdr hdr;
|
|
struct mpii_device *dev;
|
|
size_t pagelen;
|
|
u_int16_t volh;
|
|
u_int8_t dn;
|
|
|
|
DNPRINTF(MPII_D_IOCTL, "%s: mpii_ioctl_disk %d/%d\n",
|
|
DEVNAME(sc), bd->bd_volid, bd->bd_diskid);
|
|
|
|
if ((dev = mpii_find_vol(sc, bd->bd_volid)) == NULL)
|
|
return (ENODEV);
|
|
volh = dev->dev_handle;
|
|
|
|
if (mpii_req_cfg_header(sc, MPII_CONFIG_REQ_PAGE_TYPE_RAID_VOL, 0,
|
|
MPII_CFG_RAID_VOL_ADDR_HANDLE | volh, 0, &hdr) != 0) {
|
|
printf("%s: unable to fetch header for raid volume page 0\n",
|
|
DEVNAME(sc));
|
|
return (EINVAL);
|
|
}
|
|
|
|
pagelen = hdr.page_length * 4;
|
|
vpg = malloc(pagelen, M_TEMP, M_WAITOK | M_CANFAIL | M_ZERO);
|
|
if (vpg == NULL) {
|
|
printf("%s: unable to allocate space for raid "
|
|
"volume page 0\n", DEVNAME(sc));
|
|
return (ENOMEM);
|
|
}
|
|
|
|
if (mpii_req_cfg_page(sc, MPII_CFG_RAID_VOL_ADDR_HANDLE | volh, 0,
|
|
&hdr, 1, vpg, pagelen) != 0) {
|
|
printf("%s: unable to fetch raid volume page 0\n",
|
|
DEVNAME(sc));
|
|
free(vpg, M_TEMP);
|
|
return (EINVAL);
|
|
}
|
|
|
|
if (bd->bd_diskid >= vpg->num_phys_disks) {
|
|
int nvdsk = vpg->num_phys_disks;
|
|
int hsmap = vpg->hot_spare_pool;
|
|
|
|
free(vpg, M_TEMP);
|
|
return (mpii_bio_hs(sc, bd, nvdsk, hsmap, NULL));
|
|
}
|
|
|
|
pd = (struct mpii_cfg_raid_vol_pg0_physdisk *)(vpg + 1) +
|
|
bd->bd_diskid;
|
|
dn = pd->phys_disk_num;
|
|
|
|
free(vpg, M_TEMP);
|
|
return (mpii_bio_disk(sc, bd, dn));
|
|
}
|
|
|
|
static int
|
|
mpii_bio_hs(struct mpii_softc *sc, struct bioc_disk *bd, int nvdsk,
|
|
int hsmap, int *hscnt)
|
|
{
|
|
struct mpii_cfg_raid_config_pg0 *cpg;
|
|
struct mpii_raid_config_element *el;
|
|
struct mpii_ecfg_hdr ehdr;
|
|
size_t pagelen;
|
|
int i, nhs = 0;
|
|
|
|
if (bd) {
|
|
DNPRINTF(MPII_D_IOCTL, "%s: mpii_bio_hs %d\n", DEVNAME(sc),
|
|
bd->bd_diskid - nvdsk);
|
|
} else {
|
|
DNPRINTF(MPII_D_IOCTL, "%s: mpii_bio_hs\n", DEVNAME(sc));
|
|
}
|
|
|
|
if (mpii_req_cfg_header(sc, MPII_CONFIG_REQ_PAGE_TYPE_RAID_CONFIG,
|
|
0, MPII_CFG_RAID_CONFIG_ACTIVE_CONFIG, MPII_PG_EXTENDED,
|
|
&ehdr) != 0) {
|
|
printf("%s: unable to fetch header for raid config page 0\n",
|
|
DEVNAME(sc));
|
|
return (EINVAL);
|
|
}
|
|
|
|
pagelen = le16toh(ehdr.ext_page_length) * 4;
|
|
cpg = malloc(pagelen, M_TEMP, M_WAITOK | M_CANFAIL | M_ZERO);
|
|
if (cpg == NULL) {
|
|
printf("%s: unable to allocate space for raid config page 0\n",
|
|
DEVNAME(sc));
|
|
return (ENOMEM);
|
|
}
|
|
|
|
if (mpii_req_cfg_page(sc, MPII_CFG_RAID_CONFIG_ACTIVE_CONFIG,
|
|
MPII_PG_EXTENDED, &ehdr, 1, cpg, pagelen) != 0) {
|
|
printf("%s: unable to fetch raid config page 0\n",
|
|
DEVNAME(sc));
|
|
free(cpg, M_TEMP);
|
|
return (EINVAL);
|
|
}
|
|
|
|
el = (struct mpii_raid_config_element *)(cpg + 1);
|
|
for (i = 0; i < cpg->num_elements; i++, el++) {
|
|
if (ISSET(le16toh(el->element_flags),
|
|
MPII_RAID_CONFIG_ELEMENT_FLAG_HSP_PHYS_DISK) &&
|
|
el->hot_spare_pool == hsmap) {
|
|
/*
|
|
* diskid comparison is based on the idea that all
|
|
* disks are counted by the bio(4) in sequence, thus
|
|
* substracting the number of disks in the volume
|
|
* from the diskid yields us a "relative" hotspare
|
|
* number, which is good enough for us.
|
|
*/
|
|
if (bd != NULL && bd->bd_diskid == nhs + nvdsk) {
|
|
u_int8_t dn = el->phys_disk_num;
|
|
|
|
free(cpg, M_TEMP);
|
|
return (mpii_bio_disk(sc, bd, dn));
|
|
}
|
|
nhs++;
|
|
}
|
|
}
|
|
|
|
if (hscnt)
|
|
*hscnt = nhs;
|
|
|
|
free(cpg, M_TEMP);
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
mpii_bio_disk(struct mpii_softc *sc, struct bioc_disk *bd, u_int8_t dn)
|
|
{
|
|
struct mpii_cfg_raid_physdisk_pg0 *ppg;
|
|
struct mpii_cfg_hdr hdr;
|
|
struct mpii_device *dev;
|
|
int len;
|
|
|
|
DNPRINTF(MPII_D_IOCTL, "%s: mpii_bio_disk %d\n", DEVNAME(sc),
|
|
bd->bd_diskid);
|
|
|
|
ppg = malloc(sizeof(*ppg), M_TEMP, M_WAITOK | M_CANFAIL | M_ZERO);
|
|
if (ppg == NULL) {
|
|
printf("%s: unable to allocate space for raid physical disk "
|
|
"page 0\n", DEVNAME(sc));
|
|
return (ENOMEM);
|
|
}
|
|
|
|
hdr.page_version = 0;
|
|
hdr.page_length = sizeof(*ppg) / 4;
|
|
hdr.page_number = 0;
|
|
hdr.page_type = MPII_CONFIG_REQ_PAGE_TYPE_RAID_PD;
|
|
|
|
if (mpii_req_cfg_page(sc, MPII_CFG_RAID_PHYS_DISK_ADDR_NUMBER | dn, 0,
|
|
&hdr, 1, ppg, sizeof(*ppg)) != 0) {
|
|
printf("%s: unable to fetch raid drive page 0\n",
|
|
DEVNAME(sc));
|
|
free(ppg, M_TEMP);
|
|
return (EINVAL);
|
|
}
|
|
|
|
bd->bd_target = ppg->phys_disk_num;
|
|
|
|
if ((dev = mpii_find_dev(sc, le16toh(ppg->dev_handle))) == NULL) {
|
|
bd->bd_status = BIOC_SDINVALID;
|
|
free(ppg, M_TEMP);
|
|
return (0);
|
|
}
|
|
|
|
switch (ppg->phys_disk_state) {
|
|
case MPII_CFG_RAID_PHYDISK_0_STATE_ONLINE:
|
|
case MPII_CFG_RAID_PHYDISK_0_STATE_OPTIMAL:
|
|
bd->bd_status = BIOC_SDONLINE;
|
|
break;
|
|
case MPII_CFG_RAID_PHYDISK_0_STATE_OFFLINE:
|
|
if (ppg->offline_reason ==
|
|
MPII_CFG_RAID_PHYDISK_0_OFFLINE_FAILED ||
|
|
ppg->offline_reason ==
|
|
MPII_CFG_RAID_PHYDISK_0_OFFLINE_FAILEDREQ)
|
|
bd->bd_status = BIOC_SDFAILED;
|
|
else
|
|
bd->bd_status = BIOC_SDOFFLINE;
|
|
break;
|
|
case MPII_CFG_RAID_PHYDISK_0_STATE_DEGRADED:
|
|
bd->bd_status = BIOC_SDFAILED;
|
|
break;
|
|
case MPII_CFG_RAID_PHYDISK_0_STATE_REBUILDING:
|
|
bd->bd_status = BIOC_SDREBUILD;
|
|
break;
|
|
case MPII_CFG_RAID_PHYDISK_0_STATE_HOTSPARE:
|
|
bd->bd_status = BIOC_SDHOTSPARE;
|
|
break;
|
|
case MPII_CFG_RAID_PHYDISK_0_STATE_NOTCONFIGURED:
|
|
bd->bd_status = BIOC_SDUNUSED;
|
|
break;
|
|
case MPII_CFG_RAID_PHYDISK_0_STATE_NOTCOMPATIBLE:
|
|
default:
|
|
bd->bd_status = BIOC_SDINVALID;
|
|
break;
|
|
}
|
|
|
|
bd->bd_size = le64toh(ppg->dev_max_lba) * le16toh(ppg->block_size);
|
|
|
|
strnvisx(bd->bd_vendor, sizeof(bd->bd_vendor),
|
|
ppg->vendor_id, sizeof(ppg->vendor_id),
|
|
VIS_TRIM|VIS_SAFE|VIS_OCTAL);
|
|
len = strlen(bd->bd_vendor);
|
|
bd->bd_vendor[len] = ' ';
|
|
strnvisx(&bd->bd_vendor[len + 1], sizeof(ppg->vendor_id) - len - 1,
|
|
ppg->product_id, sizeof(ppg->product_id),
|
|
VIS_TRIM|VIS_SAFE|VIS_OCTAL);
|
|
strnvisx(bd->bd_serial, sizeof(bd->bd_serial),
|
|
ppg->serial, sizeof(ppg->serial), VIS_TRIM|VIS_SAFE|VIS_OCTAL);
|
|
|
|
free(ppg, M_TEMP);
|
|
return (0);
|
|
}
|
|
|
|
static struct mpii_device *
|
|
mpii_find_vol(struct mpii_softc *sc, int volid)
|
|
{
|
|
struct mpii_device *dev = NULL;
|
|
|
|
if (sc->sc_vd_id_low + volid >= sc->sc_max_devices)
|
|
return (NULL);
|
|
dev = sc->sc_devs[sc->sc_vd_id_low + volid];
|
|
if (dev && ISSET(dev->flags, MPII_DF_VOLUME))
|
|
return (dev);
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* Non-sleeping lightweight version of the mpii_ioctl_vol
|
|
*/
|
|
static int
|
|
mpii_bio_volstate(struct mpii_softc *sc, struct bioc_vol *bv)
|
|
{
|
|
struct mpii_cfg_raid_vol_pg0 *vpg;
|
|
struct mpii_cfg_hdr hdr;
|
|
struct mpii_device *dev = NULL;
|
|
size_t pagelen;
|
|
u_int16_t volh;
|
|
|
|
if ((dev = mpii_find_vol(sc, bv->bv_volid)) == NULL)
|
|
return (ENODEV);
|
|
volh = dev->dev_handle;
|
|
|
|
if (mpii_cfg_header(sc, MPII_CONFIG_REQ_PAGE_TYPE_RAID_VOL, 0,
|
|
MPII_CFG_RAID_VOL_ADDR_HANDLE | volh, &hdr) != 0) {
|
|
DNPRINTF(MPII_D_MISC, "%s: unable to fetch header for raid "
|
|
"volume page 0\n", DEVNAME(sc));
|
|
return (EINVAL);
|
|
}
|
|
|
|
pagelen = hdr.page_length * 4;
|
|
vpg = malloc(pagelen, M_TEMP, M_NOWAIT | M_ZERO);
|
|
if (vpg == NULL) {
|
|
DNPRINTF(MPII_D_MISC, "%s: unable to allocate space for raid "
|
|
"volume page 0\n", DEVNAME(sc));
|
|
return (ENOMEM);
|
|
}
|
|
|
|
if (mpii_cfg_page(sc, MPII_CFG_RAID_VOL_ADDR_HANDLE | volh,
|
|
&hdr, 1, vpg, pagelen) != 0) {
|
|
DNPRINTF(MPII_D_MISC, "%s: unable to fetch raid volume "
|
|
"page 0\n", DEVNAME(sc));
|
|
free(vpg, M_TEMP);
|
|
return (EINVAL);
|
|
}
|
|
|
|
switch (vpg->volume_state) {
|
|
case MPII_CFG_RAID_VOL_0_STATE_ONLINE:
|
|
case MPII_CFG_RAID_VOL_0_STATE_OPTIMAL:
|
|
bv->bv_status = BIOC_SVONLINE;
|
|
break;
|
|
case MPII_CFG_RAID_VOL_0_STATE_DEGRADED:
|
|
if (ISSET(le32toh(vpg->volume_status),
|
|
MPII_CFG_RAID_VOL_0_STATUS_RESYNC))
|
|
bv->bv_status = BIOC_SVREBUILD;
|
|
else
|
|
bv->bv_status = BIOC_SVDEGRADED;
|
|
break;
|
|
case MPII_CFG_RAID_VOL_0_STATE_FAILED:
|
|
bv->bv_status = BIOC_SVOFFLINE;
|
|
break;
|
|
case MPII_CFG_RAID_VOL_0_STATE_INITIALIZING:
|
|
bv->bv_status = BIOC_SVBUILDING;
|
|
break;
|
|
case MPII_CFG_RAID_VOL_0_STATE_MISSING:
|
|
default:
|
|
bv->bv_status = BIOC_SVINVALID;
|
|
break;
|
|
}
|
|
|
|
free(vpg, M_TEMP);
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
mpii_create_sensors(struct mpii_softc *sc)
|
|
{
|
|
int i, rv;
|
|
|
|
sc->sc_sme = sysmon_envsys_create();
|
|
sc->sc_sensors = malloc(sizeof(envsys_data_t) * sc->sc_vd_count,
|
|
M_DEVBUF, M_NOWAIT | M_ZERO);
|
|
if (sc->sc_sensors == NULL) {
|
|
aprint_error_dev(sc->sc_dev,
|
|
"can't allocate envsys_data_t\n");
|
|
return (1);
|
|
}
|
|
|
|
for (i = 0; i < sc->sc_vd_count; i++) {
|
|
sc->sc_sensors[i].units = ENVSYS_DRIVE;
|
|
sc->sc_sensors[i].state = ENVSYS_SINVALID;
|
|
sc->sc_sensors[i].value_cur = ENVSYS_DRIVE_EMPTY;
|
|
/* Enable monitoring for drive state changes */
|
|
sc->sc_sensors[i].flags |= ENVSYS_FMONSTCHANGED;
|
|
|
|
/* logical drives */
|
|
snprintf(sc->sc_sensors[i].desc,
|
|
sizeof(sc->sc_sensors[i].desc), "%s:%d",
|
|
DEVNAME(sc), i);
|
|
if ((rv = sysmon_envsys_sensor_attach(sc->sc_sme,
|
|
&sc->sc_sensors[i])) != 0) {
|
|
aprint_error_dev(sc->sc_dev,
|
|
"unable to attach sensor (rv = %d)\n", rv);
|
|
goto out;
|
|
}
|
|
}
|
|
sc->sc_sme->sme_name = DEVNAME(sc);
|
|
sc->sc_sme->sme_cookie = sc;
|
|
sc->sc_sme->sme_refresh = mpii_refresh_sensors;
|
|
|
|
rv = sysmon_envsys_register(sc->sc_sme);
|
|
|
|
if (rv != 0) {
|
|
aprint_error_dev(sc->sc_dev,
|
|
"unable to register with sysmon (rv = %d)\n", rv);
|
|
goto out;
|
|
}
|
|
return 0;
|
|
|
|
out:
|
|
free(sc->sc_sensors, M_DEVBUF);
|
|
sysmon_envsys_destroy(sc->sc_sme);
|
|
sc->sc_sme = NULL;
|
|
return EINVAL;
|
|
}
|
|
|
|
static int
|
|
mpii_destroy_sensors(struct mpii_softc *sc)
|
|
{
|
|
if (sc->sc_sme == NULL)
|
|
return 0;
|
|
sysmon_envsys_unregister(sc->sc_sme);
|
|
sc->sc_sme = NULL;
|
|
free(sc->sc_sensors, M_DEVBUF);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
mpii_refresh_sensors(struct sysmon_envsys *sme, envsys_data_t *edata)
|
|
{
|
|
struct mpii_softc *sc = sc = sme->sme_cookie;
|
|
struct bioc_vol bv;
|
|
int s, error;
|
|
|
|
bzero(&bv, sizeof(bv));
|
|
bv.bv_volid = edata->sensor;
|
|
KERNEL_LOCK(1, curlwp);
|
|
s = splbio();
|
|
error = mpii_bio_volstate(sc, &bv);
|
|
splx(s);
|
|
KERNEL_UNLOCK_ONE(curlwp);
|
|
if (error)
|
|
bv.bv_status = BIOC_SVINVALID;
|
|
bio_vol_to_envsys(edata, &bv);
|
|
}
|
|
#endif /* NBIO > 0 */
|