#if defined(lint) || defined(DEBUG)
#define MPTSAS_DEBUG
#endif
#include <sys/note.h>
#include <sys/scsi/scsi.h>
#pragma pack(1)
#include <sys/scsi/adapters/mpi/mpi2_type.h>
#include <sys/scsi/adapters/mpi/mpi2.h>
#include <sys/scsi/adapters/mpi/mpi2_cnfg.h>
#include <sys/scsi/adapters/mpi/mpi2_init.h>
#include <sys/scsi/adapters/mpi/mpi2_ioc.h>
#include <sys/scsi/adapters/mpi/mpi2_tool.h>
#pragma pack()
#include <sys/scsi/adapters/mpt_sas/mptsas_var.h>
static int mptsas_ioc_do_get_facts(mptsas_t *mpt, caddr_t memp, int var,
ddi_acc_handle_t accessp);
static int mptsas_ioc_do_get_facts_reply(mptsas_t *mpt, caddr_t memp, int var,
ddi_acc_handle_t accessp);
static int mptsas_ioc_do_get_port_facts(mptsas_t *mpt, caddr_t memp, int var,
ddi_acc_handle_t accessp);
static int mptsas_ioc_do_get_port_facts_reply(mptsas_t *mpt, caddr_t memp,
int var, ddi_acc_handle_t accessp);
static int mptsas_ioc_do_enable_port(mptsas_t *mpt, caddr_t memp, int var,
ddi_acc_handle_t accessp);
static int mptsas_ioc_do_enable_port_reply(mptsas_t *mpt, caddr_t memp, int var,
ddi_acc_handle_t accessp);
static int mptsas_ioc_do_enable_event_notification(mptsas_t *mpt, caddr_t memp,
int var, ddi_acc_handle_t accessp);
static int mptsas_ioc_do_enable_event_notification_reply(mptsas_t *mpt,
caddr_t memp, int var, ddi_acc_handle_t accessp);
static int mptsas_do_ioc_init(mptsas_t *mpt, caddr_t memp, int var,
ddi_acc_handle_t accessp);
static int mptsas_do_ioc_init_reply(mptsas_t *mpt, caddr_t memp, int var,
ddi_acc_handle_t accessp);
static const char *
mptsas_devid_type_string(mptsas_t *mpt)
{
switch (mpt->m_devid) {
case MPI2_MFGPAGE_DEVID_SAS2008:
return ("SAS2008");
case MPI2_MFGPAGE_DEVID_SAS2004:
return ("SAS2004");
case MPI2_MFGPAGE_DEVID_SAS2108_1:
case MPI2_MFGPAGE_DEVID_SAS2108_2:
case MPI2_MFGPAGE_DEVID_SAS2108_3:
return ("SAS2108");
case MPI2_MFGPAGE_DEVID_SAS2116_1:
case MPI2_MFGPAGE_DEVID_SAS2116_2:
return ("SAS2116");
case MPI2_MFGPAGE_DEVID_SAS2208_1:
case MPI2_MFGPAGE_DEVID_SAS2208_2:
case MPI2_MFGPAGE_DEVID_SAS2208_3:
case MPI2_MFGPAGE_DEVID_SAS2208_4:
case MPI2_MFGPAGE_DEVID_SAS2208_5:
case MPI2_MFGPAGE_DEVID_SAS2208_6:
return ("SAS2208");
case MPI2_MFGPAGE_DEVID_SAS2308_1:
case MPI2_MFGPAGE_DEVID_SAS2308_2:
case MPI2_MFGPAGE_DEVID_SAS2308_3:
return ("SAS2308");
case MPI25_MFGPAGE_DEVID_SAS3004:
return ("SAS3004");
case MPI25_MFGPAGE_DEVID_SAS3008:
return ("SAS3008");
case MPI25_MFGPAGE_DEVID_SAS3108_1:
case MPI25_MFGPAGE_DEVID_SAS3108_2:
case MPI25_MFGPAGE_DEVID_SAS3108_5:
case MPI25_MFGPAGE_DEVID_SAS3108_6:
return ("SAS3108");
case MPI26_MFGPAGE_DEVID_SAS3216:
case MPI26_MFGPAGE_DEVID_SAS3316_1:
case MPI26_MFGPAGE_DEVID_SAS3316_2:
case MPI26_MFGPAGE_DEVID_SAS3316_3:
case MPI26_MFGPAGE_DEVID_SAS3316_4:
return ("SAS3216");
case MPI26_MFGPAGE_DEVID_SAS3224:
case MPI26_MFGPAGE_DEVID_SAS3324_1:
case MPI26_MFGPAGE_DEVID_SAS3324_2:
case MPI26_MFGPAGE_DEVID_SAS3324_3:
case MPI26_MFGPAGE_DEVID_SAS3324_4:
return ("SAS3224");
case MPI26_MFGPAGE_DEVID_SAS3408:
return ("SAS3408");
case MPI26_MFGPAGE_DEVID_SAS3416:
return ("SAS3416");
case MPI26_MFGPAGE_DEVID_SAS3508:
case MPI26_MFGPAGE_DEVID_SAS3508_1:
return ("SAS3508");
case MPI26_MFGPAGE_DEVID_SAS3516:
case MPI26_MFGPAGE_DEVID_SAS3516_1:
return ("SAS3516");
case MPI26_MFGPAGE_DEVID_SAS3616:
return ("SAS3616");
case MPI26_MFGPAGE_DEVID_SAS3708:
return ("SAS3708");
case MPI26_MFGPAGE_DEVID_SAS3716:
return ("SAS3716");
case MPI26_MFGPAGE_DEVID_SAS3816:
case MPI26_MFGPAGE_DEVID_SAS3816_1:
return ("SAS3816");
case MPI26_MFGPAGE_DEVID_SAS4008:
return ("SAS4008");
default:
return ("?");
}
}
int
mptsas_ioc_get_facts(mptsas_t *mpt)
{
if (mptsas_do_dma(mpt, sizeof (MPI2_IOC_FACTS_REQUEST), 0,
mptsas_ioc_do_get_facts)) {
return (DDI_FAILURE);
}
if (mptsas_do_dma(mpt, sizeof (MPI2_IOC_FACTS_REPLY), 0,
mptsas_ioc_do_get_facts_reply)) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
mptsas_ioc_do_get_facts(mptsas_t *mpt, caddr_t memp, int var,
ddi_acc_handle_t accessp)
{
#ifndef __lock_lint
_NOTE(ARGUNUSED(var))
#endif
pMpi2IOCFactsRequest_t facts;
int numbytes;
bzero(memp, sizeof (*facts));
facts = (void *)memp;
ddi_put8(accessp, &facts->Function, MPI2_FUNCTION_IOC_FACTS);
numbytes = sizeof (*facts);
if (mptsas_send_handshake_msg(mpt, memp, numbytes, accessp)) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
mptsas_ioc_do_get_facts_reply(mptsas_t *mpt, caddr_t memp, int var,
ddi_acc_handle_t accessp)
{
#ifndef __lock_lint
_NOTE(ARGUNUSED(var))
#endif
pMpi2IOCFactsReply_t factsreply;
int numbytes;
uint_t iocstatus;
char buf[32];
uint16_t numReplyFrames;
uint16_t queueSize, queueDiff;
int simple_sge_main;
int simple_sge_next;
uint32_t capabilities;
uint16_t msgversion;
bzero(memp, sizeof (*factsreply));
factsreply = (void *)memp;
numbytes = sizeof (*factsreply);
if (mptsas_get_handshake_msg(mpt, memp, numbytes, accessp)) {
return (DDI_FAILURE);
}
if ((iocstatus = ddi_get16(accessp, &factsreply->IOCStatus)) != 0) {
mptsas_log(mpt, CE_WARN, "mptsas_ioc_do_get_facts_reply: "
"IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus,
ddi_get32(accessp, &factsreply->IOCLogInfo));
return (DDI_FAILURE);
}
mpt->m_fwversion = ddi_get32(accessp, &factsreply->FWVersion.Word);
mpt->m_productid = ddi_get16(accessp, &factsreply->ProductID);
(void) sprintf(buf, "%u.%u.%u.%u",
ddi_get8(accessp, &factsreply->FWVersion.Struct.Major),
ddi_get8(accessp, &factsreply->FWVersion.Struct.Minor),
ddi_get8(accessp, &factsreply->FWVersion.Struct.Unit),
ddi_get8(accessp, &factsreply->FWVersion.Struct.Dev));
mptsas_log(mpt, CE_NOTE, "?MPT Firmware version v%s (%s)\n",
buf, mptsas_devid_type_string(mpt));
(void) ddi_prop_update_string(DDI_DEV_T_NONE, mpt->m_dip,
"firmware-version", buf);
mpt->m_max_requests = ddi_get16(accessp,
&factsreply->RequestCredit) - 1;
mpt->m_req_frame_size = ddi_get16(accessp,
&factsreply->IOCRequestFrameSize) * 4;
mpt->m_reply_frame_size = ddi_get8(accessp,
&factsreply->ReplyFrameSize) * 4;
numReplyFrames = mpt->m_max_requests + 32;
if (!(numReplyFrames % 16)) {
numReplyFrames--;
}
mpt->m_max_replies = numReplyFrames;
queueSize = numReplyFrames;
queueSize += 16 - (queueSize % 16);
mpt->m_free_queue_depth = queueSize;
queueSize = mpt->m_max_requests + numReplyFrames + 1;
if (queueSize % 16) {
queueSize += 16 - (queueSize % 16);
}
mpt->m_post_queue_depth = ddi_get16(accessp,
&factsreply->MaxReplyDescriptorPostQueueDepth);
if (queueSize > mpt->m_post_queue_depth) {
queueDiff = queueSize - mpt->m_post_queue_depth;
if (queueDiff % 16) {
queueDiff += 16 - (queueDiff % 16);
}
mpt->m_max_requests -= queueDiff;
mpt->m_max_replies -= queueDiff;
mpt->m_free_queue_depth -= queueDiff;
queueSize -= queueDiff;
}
mpt->m_post_queue_depth = queueSize;
mpt->m_max_chain_depth = ddi_get8(accessp,
&factsreply->MaxChainDepth);
mpt->m_ioc_capabilities = ddi_get32(accessp,
&factsreply->IOCCapabilities);
msgversion = ddi_get16(accessp, &factsreply->MsgVersion);
if (msgversion >= MPI2_VERSION_02_05) {
mptsas_log(mpt, CE_NOTE, "?mpt_sas%d SAS 3 Supported\n",
mpt->m_instance);
mpt->m_MPI25 = TRUE;
} else {
mptsas_log(mpt, CE_NOTE, "?mpt_sas%d MPI Version 0x%x\n",
mpt->m_instance, msgversion);
}
simple_sge_main = MPTSAS_MAX_FRAME_SGES64(mpt) - 1;
simple_sge_next = mpt->m_req_frame_size / MPTSAS_SGE_SIZE(mpt) - 1;
mpt->m_max_request_frames = (MPTSAS_MAX_DMA_SEGS -
simple_sge_main) / simple_sge_next + 1;
if (((MPTSAS_MAX_DMA_SEGS - simple_sge_main) %
simple_sge_next) > 1) {
mpt->m_max_request_frames++;
}
capabilities = ddi_get32(accessp, &factsreply->IOCCapabilities);
if (capabilities & MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER) {
mpt->m_fw_diag_buffer_list[MPI2_DIAG_BUF_TYPE_TRACE].enabled =
TRUE;
}
if (capabilities & MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER) {
mpt->m_fw_diag_buffer_list[MPI2_DIAG_BUF_TYPE_SNAPSHOT].
enabled = TRUE;
}
if (capabilities & MPI2_IOCFACTS_CAPABILITY_EXTENDED_BUFFER) {
mpt->m_fw_diag_buffer_list[MPI2_DIAG_BUF_TYPE_EXTENDED].
enabled = TRUE;
}
if (capabilities & MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY) {
mpt->m_event_replay = TRUE;
}
if (capabilities & MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID) {
mpt->m_ir_capable = TRUE;
}
return (DDI_SUCCESS);
}
int
mptsas_ioc_get_port_facts(mptsas_t *mpt, int port)
{
if (mptsas_do_dma(mpt, sizeof (MPI2_PORT_FACTS_REQUEST), port,
mptsas_ioc_do_get_port_facts)) {
return (DDI_FAILURE);
}
if (mptsas_do_dma(mpt, sizeof (MPI2_PORT_FACTS_REPLY), port,
mptsas_ioc_do_get_port_facts_reply)) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
mptsas_ioc_do_get_port_facts(mptsas_t *mpt, caddr_t memp, int var,
ddi_acc_handle_t accessp)
{
pMpi2PortFactsRequest_t facts;
int numbytes;
bzero(memp, sizeof (*facts));
facts = (void *)memp;
ddi_put8(accessp, &facts->Function, MPI2_FUNCTION_PORT_FACTS);
ddi_put8(accessp, &facts->PortNumber, var);
numbytes = sizeof (*facts);
if (mptsas_send_handshake_msg(mpt, memp, numbytes, accessp)) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
mptsas_ioc_do_get_port_facts_reply(mptsas_t *mpt, caddr_t memp, int var,
ddi_acc_handle_t accessp)
{
#ifndef __lock_lint
_NOTE(ARGUNUSED(var))
#endif
pMpi2PortFactsReply_t factsreply;
int numbytes;
uint_t iocstatus;
bzero(memp, sizeof (*factsreply));
factsreply = (void *)memp;
numbytes = sizeof (*factsreply);
if (mptsas_get_handshake_msg(mpt, memp, numbytes, accessp)) {
return (DDI_FAILURE);
}
if ((iocstatus = ddi_get16(accessp, &factsreply->IOCStatus)) != 0) {
mptsas_log(mpt, CE_WARN, "mptsas_ioc_do_get_port_facts_reply: "
"IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus,
ddi_get32(accessp, &factsreply->IOCLogInfo));
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
int
mptsas_ioc_enable_port(mptsas_t *mpt)
{
if (mptsas_do_dma(mpt, sizeof (MPI2_PORT_ENABLE_REQUEST), 0,
mptsas_ioc_do_enable_port)) {
return (DDI_FAILURE);
}
if (mptsas_do_dma(mpt, sizeof (MPI2_PORT_ENABLE_REPLY), 0,
mptsas_ioc_do_enable_port_reply)) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
mptsas_ioc_do_enable_port(mptsas_t *mpt, caddr_t memp, int var,
ddi_acc_handle_t accessp)
{
#ifndef __lock_lint
_NOTE(ARGUNUSED(var))
#endif
pMpi2PortEnableRequest_t enable;
int numbytes;
bzero(memp, sizeof (*enable));
enable = (void *)memp;
ddi_put8(accessp, &enable->Function, MPI2_FUNCTION_PORT_ENABLE);
numbytes = sizeof (*enable);
if (mptsas_send_handshake_msg(mpt, memp, numbytes, accessp)) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
mptsas_ioc_do_enable_port_reply(mptsas_t *mpt, caddr_t memp, int var,
ddi_acc_handle_t accessp)
{
#ifndef __lock_lint
_NOTE(ARGUNUSED(var))
#endif
int numbytes;
uint_t iocstatus;
pMpi2PortEnableReply_t portreply;
numbytes = sizeof (MPI2_PORT_ENABLE_REPLY);
bzero(memp, numbytes);
portreply = (void *)memp;
if (mptsas_get_handshake_msg(mpt, memp, numbytes, accessp)) {
return (DDI_FAILURE);
}
if ((iocstatus = ddi_get16(accessp, &portreply->IOCStatus)) != 0) {
mptsas_log(mpt, CE_WARN, "mptsas_ioc_do_enable_port_reply: "
"IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus,
ddi_get32(accessp, &portreply->IOCLogInfo));
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
int
mptsas_ioc_enable_event_notification(mptsas_t *mpt)
{
ASSERT(mutex_owned(&mpt->m_mutex));
if (mptsas_do_dma(mpt, sizeof (MPI2_EVENT_NOTIFICATION_REQUEST), 0,
mptsas_ioc_do_enable_event_notification)) {
return (DDI_FAILURE);
}
if (mptsas_do_dma(mpt, sizeof (MPI2_EVENT_NOTIFICATION_REPLY), 0,
mptsas_ioc_do_enable_event_notification_reply)) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
mptsas_ioc_do_enable_event_notification(mptsas_t *mpt, caddr_t memp, int var,
ddi_acc_handle_t accessp)
{
#ifndef __lock_lint
_NOTE(ARGUNUSED(var))
#endif
pMpi2EventNotificationRequest_t event;
int numbytes;
bzero(memp, sizeof (*event));
event = (void *)memp;
ddi_put8(accessp, &event->Function, MPI2_FUNCTION_EVENT_NOTIFICATION);
numbytes = sizeof (*event);
if (mptsas_send_handshake_msg(mpt, memp, numbytes, accessp)) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
mptsas_ioc_do_enable_event_notification_reply(mptsas_t *mpt, caddr_t memp,
int var, ddi_acc_handle_t accessp)
{
#ifndef __lock_lint
_NOTE(ARGUNUSED(var))
#endif
int numbytes;
uint_t iocstatus;
pMpi2EventNotificationReply_t eventsreply;
numbytes = sizeof (MPI2_EVENT_NOTIFICATION_REPLY);
bzero(memp, numbytes);
eventsreply = (void *)memp;
if (mptsas_get_handshake_msg(mpt, memp, numbytes, accessp)) {
return (DDI_FAILURE);
}
if ((iocstatus = ddi_get16(accessp, &eventsreply->IOCStatus)) != 0) {
mptsas_log(mpt, CE_WARN,
"mptsas_ioc_do_enable_event_notification_reply: "
"IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus,
ddi_get32(accessp, &eventsreply->IOCLogInfo));
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
int
mptsas_ioc_init(mptsas_t *mpt)
{
if (mptsas_do_dma(mpt, sizeof (MPI2_IOC_INIT_REQUEST), 0,
mptsas_do_ioc_init)) {
return (DDI_FAILURE);
}
if (mptsas_do_dma(mpt, sizeof (MPI2_IOC_INIT_REPLY), 0,
mptsas_do_ioc_init_reply)) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
mptsas_do_ioc_init(mptsas_t *mpt, caddr_t memp, int var,
ddi_acc_handle_t accessp)
{
#ifndef __lock_lint
_NOTE(ARGUNUSED(var))
#endif
pMpi2IOCInitRequest_t init;
int numbytes;
timespec_t time;
uint64_t mSec;
bzero(memp, sizeof (*init));
init = (void *)memp;
ddi_put8(accessp, &init->Function, MPI2_FUNCTION_IOC_INIT);
ddi_put8(accessp, &init->WhoInit, MPI2_WHOINIT_HOST_DRIVER);
ddi_put16(accessp, &init->MsgVersion, MPI2_VERSION);
ddi_put16(accessp, &init->HeaderVersion, MPI2_HEADER_VERSION);
ddi_put16(accessp, &init->SystemRequestFrameSize,
mpt->m_req_frame_size / 4);
ddi_put16(accessp, &init->ReplyDescriptorPostQueueDepth,
mpt->m_post_queue_depth);
ddi_put16(accessp, &init->ReplyFreeQueueDepth,
mpt->m_free_queue_depth);
ddi_put32(accessp, &init->SenseBufferAddressHigh,
(uint32_t)(mpt->m_req_sense_dma_addr >> 32));
ddi_put32(accessp, &init->SystemReplyAddressHigh,
(uint32_t)(mpt->m_reply_frame_dma_addr >> 32));
ddi_put32(accessp, &init->SystemRequestFrameBaseAddress.High,
(uint32_t)(mpt->m_req_frame_dma_addr >> 32));
ddi_put32(accessp, &init->SystemRequestFrameBaseAddress.Low,
(uint32_t)mpt->m_req_frame_dma_addr);
ddi_put32(accessp, &init->ReplyDescriptorPostQueueAddress.High,
(uint32_t)(mpt->m_post_queue_dma_addr >> 32));
ddi_put32(accessp, &init->ReplyDescriptorPostQueueAddress.Low,
(uint32_t)mpt->m_post_queue_dma_addr);
ddi_put32(accessp, &init->ReplyFreeQueueAddress.High,
(uint32_t)(mpt->m_free_queue_dma_addr >> 32));
ddi_put32(accessp, &init->ReplyFreeQueueAddress.Low,
(uint32_t)mpt->m_free_queue_dma_addr);
gethrestime(&time);
mSec = time.tv_sec * MILLISEC;
mSec += (time.tv_nsec / MICROSEC);
ddi_put32(accessp, &init->TimeStamp.High, (uint32_t)(mSec >> 32));
ddi_put32(accessp, &init->TimeStamp.Low, (uint32_t)mSec);
numbytes = sizeof (*init);
if (mptsas_send_handshake_msg(mpt, memp, numbytes, accessp)) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
mptsas_do_ioc_init_reply(mptsas_t *mpt, caddr_t memp, int var,
ddi_acc_handle_t accessp)
{
#ifndef __lock_lint
_NOTE(ARGUNUSED(var))
#endif
pMpi2IOCInitReply_t initreply;
int numbytes;
uint_t iocstatus;
numbytes = sizeof (MPI2_IOC_INIT_REPLY);
bzero(memp, numbytes);
initreply = (void *)memp;
if (mptsas_get_handshake_msg(mpt, memp, numbytes, accessp)) {
return (DDI_FAILURE);
}
if ((iocstatus = ddi_get16(accessp, &initreply->IOCStatus)) != 0) {
mptsas_log(mpt, CE_WARN, "mptsas_do_ioc_init_reply: "
"IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus,
ddi_get32(accessp, &initreply->IOCLogInfo));
return (DDI_FAILURE);
}
if ((mptsas_hirrd(mpt, &mpt->m_reg->Doorbell)) &
MPI2_IOC_STATE_OPERATIONAL) {
mptsas_log(mpt, CE_NOTE,
"?mpt%d: IOC Operational.\n", mpt->m_instance);
} else {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}