/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2012 Nexenta Systems, Inc. All rights reserved. * Copyright 2014 OmniTI Computer Consulting, Inc. All rights reserved. * Copyright (c) 2014, Tegile Systems Inc. All rights reserved. * Copyright (c) 2019, Joyent, Inc. * Copyright 2023 Racktop Systems, Inc. */ /* * Copyright (c) 2000 to 2010, LSI Corporation. * All rights reserved. * * Redistribution and use in source and binary forms of all code within * this file that is exclusively owned by LSI, with or without * modification, is permitted provided that, in addition to the CDDL 1.0 * License requirements, the following conditions are met: * * Neither the name of the author nor the names of its contributors may be * used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. */ /* * mptsas_impl - This file contains all the basic functions for communicating * to MPT based hardware. */ #if defined(lint) || defined(DEBUG) #define MPTSAS_DEBUG #endif /* * standard header files */ #include #include #include #pragma pack(1) #include #include #include #include #include #include #include #pragma pack() /* * private header files. */ #include #include /* * FMA header files. */ #include /* * prototypes */ static void mptsas_ioc_event_cmdq_add(mptsas_t *mpt, m_event_struct_t *cmd); static void mptsas_ioc_event_cmdq_delete(mptsas_t *mpt, m_event_struct_t *cmd); static m_event_struct_t *mptsas_ioc_event_find_by_cmd(mptsas_t *mpt, struct mptsas_cmd *cmd); /* * add ioc evnet cmd into the queue */ static void mptsas_ioc_event_cmdq_add(mptsas_t *mpt, m_event_struct_t *cmd) { if ((cmd->m_event_linkp = mpt->m_ioc_event_cmdq) == NULL) { mpt->m_ioc_event_cmdtail = &cmd->m_event_linkp; mpt->m_ioc_event_cmdq = cmd; } else { cmd->m_event_linkp = NULL; *(mpt->m_ioc_event_cmdtail) = cmd; mpt->m_ioc_event_cmdtail = &cmd->m_event_linkp; } } /* * remove specified cmd from the ioc event queue */ static void mptsas_ioc_event_cmdq_delete(mptsas_t *mpt, m_event_struct_t *cmd) { m_event_struct_t *prev = mpt->m_ioc_event_cmdq; if (prev == cmd) { if ((mpt->m_ioc_event_cmdq = cmd->m_event_linkp) == NULL) { mpt->m_ioc_event_cmdtail = &mpt->m_ioc_event_cmdq; } cmd->m_event_linkp = NULL; return; } while (prev != NULL) { if (prev->m_event_linkp == cmd) { prev->m_event_linkp = cmd->m_event_linkp; if (cmd->m_event_linkp == NULL) { mpt->m_ioc_event_cmdtail = &prev->m_event_linkp; } cmd->m_event_linkp = NULL; return; } prev = prev->m_event_linkp; } } static m_event_struct_t * mptsas_ioc_event_find_by_cmd(mptsas_t *mpt, struct mptsas_cmd *cmd) { m_event_struct_t *ioc_cmd = NULL; ioc_cmd = mpt->m_ioc_event_cmdq; while (ioc_cmd != NULL) { if (&(ioc_cmd->m_event_cmd) == cmd) { return (ioc_cmd); } ioc_cmd = ioc_cmd->m_event_linkp; } ioc_cmd = NULL; return (ioc_cmd); } void mptsas_destroy_ioc_event_cmd(mptsas_t *mpt) { m_event_struct_t *ioc_cmd = NULL; m_event_struct_t *ioc_cmd_tmp = NULL; ioc_cmd = mpt->m_ioc_event_cmdq; /* * because the IOC event queue is resource of per instance for driver, * it's not only ACK event commands used it, but also some others used * it. We need destroy all ACK event commands when IOC reset, but can't * disturb others.So we use filter to clear the ACK event cmd in ioc * event queue, and other requests should be reserved, and they would * be free by its owner. */ while (ioc_cmd != NULL) { if (ioc_cmd->m_event_cmd.cmd_flags & CFLAG_CMDACK) { NDBG20(("destroy!! remove Ack Flag ioc_cmd\n")); if ((mpt->m_ioc_event_cmdq = ioc_cmd->m_event_linkp) == NULL) mpt->m_ioc_event_cmdtail = &mpt->m_ioc_event_cmdq; ioc_cmd_tmp = ioc_cmd; ioc_cmd = ioc_cmd->m_event_linkp; kmem_free(ioc_cmd_tmp, M_EVENT_STRUCT_SIZE); } else { /* * it's not ack cmd, so continue to check next one */ NDBG20(("destroy!! it's not Ack Flag, continue\n")); ioc_cmd = ioc_cmd->m_event_linkp; } } } void mptsas_start_config_page_access(mptsas_t *mpt, mptsas_cmd_t *cmd) { pMpi2ConfigRequest_t request; pMpi2SGESimple64_t sge; struct scsi_pkt *pkt = cmd->cmd_pkt; mptsas_config_request_t *config = pkt->pkt_ha_private; uint8_t direction; uint32_t length, flagslength; uint64_t request_desc; ASSERT(mutex_owned(&mpt->m_mutex)); /* * Point to the correct message and clear it as well as the global * config page memory. */ request = (pMpi2ConfigRequest_t)(mpt->m_req_frame + (mpt->m_req_frame_size * cmd->cmd_slot)); bzero(request, mpt->m_req_frame_size); /* * Form the request message. */ ddi_put8(mpt->m_acc_req_frame_hdl, &request->Function, MPI2_FUNCTION_CONFIG); ddi_put8(mpt->m_acc_req_frame_hdl, &request->Action, config->action); direction = MPI2_SGE_FLAGS_IOC_TO_HOST; length = 0; sge = (pMpi2SGESimple64_t)&request->PageBufferSGE; if (config->action == MPI2_CONFIG_ACTION_PAGE_HEADER) { if (config->page_type > MPI2_CONFIG_PAGETYPE_MASK) { ddi_put8(mpt->m_acc_req_frame_hdl, &request->Header.PageType, MPI2_CONFIG_PAGETYPE_EXTENDED); ddi_put8(mpt->m_acc_req_frame_hdl, &request->ExtPageType, config->page_type); } else { ddi_put8(mpt->m_acc_req_frame_hdl, &request->Header.PageType, config->page_type); } } else { ddi_put8(mpt->m_acc_req_frame_hdl, &request->ExtPageType, config->ext_page_type); ddi_put16(mpt->m_acc_req_frame_hdl, &request->ExtPageLength, config->ext_page_length); ddi_put8(mpt->m_acc_req_frame_hdl, &request->Header.PageType, config->page_type); ddi_put8(mpt->m_acc_req_frame_hdl, &request->Header.PageLength, config->page_length); ddi_put8(mpt->m_acc_req_frame_hdl, &request->Header.PageVersion, config->page_version); if ((config->page_type & MPI2_CONFIG_PAGETYPE_MASK) == MPI2_CONFIG_PAGETYPE_EXTENDED) { length = config->ext_page_length * 4; } else { length = config->page_length * 4; } if (config->action == MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM) { direction = MPI2_SGE_FLAGS_HOST_TO_IOC; } ddi_put32(mpt->m_acc_req_frame_hdl, &sge->Address.Low, (uint32_t)cmd->cmd_dma_addr); ddi_put32(mpt->m_acc_req_frame_hdl, &sge->Address.High, (uint32_t)(cmd->cmd_dma_addr >> 32)); } ddi_put8(mpt->m_acc_req_frame_hdl, &request->Header.PageNumber, config->page_number); ddi_put32(mpt->m_acc_req_frame_hdl, &request->PageAddress, config->page_address); flagslength = ((uint32_t)(MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_SIMPLE_ELEMENT | MPI2_SGE_FLAGS_SYSTEM_ADDRESS | MPI2_SGE_FLAGS_64_BIT_ADDRESSING | direction | MPI2_SGE_FLAGS_END_OF_LIST) << MPI2_SGE_FLAGS_SHIFT); flagslength |= length; ddi_put32(mpt->m_acc_req_frame_hdl, &sge->FlagsLength, flagslength); (void) ddi_dma_sync(mpt->m_dma_req_frame_hdl, 0, 0, DDI_DMA_SYNC_FORDEV); request_desc = (cmd->cmd_slot << 16) + MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; cmd->cmd_rfm = 0; MPTSAS_START_CMD(mpt, request_desc); if ((mptsas_check_dma_handle(mpt->m_dma_req_frame_hdl) != DDI_SUCCESS) || (mptsas_check_acc_handle(mpt->m_acc_req_frame_hdl) != DDI_SUCCESS)) { ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_UNAFFECTED); } } int mptsas_access_config_page(mptsas_t *mpt, uint8_t action, uint8_t page_type, uint8_t page_number, uint32_t page_address, int (*callback) (mptsas_t *, caddr_t, ddi_acc_handle_t, uint16_t, uint32_t, va_list), ...) { va_list ap; ddi_dma_attr_t attrs; ddi_dma_cookie_t cookie; ddi_acc_handle_t accessp; size_t len = 0; mptsas_config_request_t config; int rval = DDI_SUCCESS, config_flags = 0; mptsas_cmd_t *cmd; struct scsi_pkt *pkt; pMpi2ConfigReply_t reply; uint16_t iocstatus = 0; uint32_t iocloginfo; caddr_t page_memp; boolean_t free_dma = B_FALSE; va_start(ap, callback); ASSERT(mutex_owned(&mpt->m_mutex)); /* * Get a command from the pool. */ if ((rval = (mptsas_request_from_pool(mpt, &cmd, &pkt))) == -1) { mptsas_log(mpt, CE_NOTE, "command pool is full for config " "page request"); rval = DDI_FAILURE; goto page_done; } config_flags |= MPTSAS_REQUEST_POOL_CMD; bzero((caddr_t)cmd, sizeof (*cmd)); bzero((caddr_t)pkt, scsi_pkt_size()); bzero((caddr_t)&config, sizeof (config)); /* * Save the data for this request to be used in the call to start the * config header request. */ config.action = MPI2_CONFIG_ACTION_PAGE_HEADER; config.page_type = page_type; config.page_number = page_number; config.page_address = page_address; /* * Form a blank cmd/pkt to store the acknowledgement message */ pkt->pkt_ha_private = (opaque_t)&config; pkt->pkt_flags = FLAG_HEAD; pkt->pkt_time = 60; cmd->cmd_pkt = pkt; cmd->cmd_flags = CFLAG_CMDIOC | CFLAG_CONFIG; /* * Save the config header request message in a slot. */ if (mptsas_save_cmd(mpt, cmd) == TRUE) { cmd->cmd_flags |= CFLAG_PREPARED; mptsas_start_config_page_access(mpt, cmd); } else { mptsas_waitq_add(mpt, cmd); } /* * If this is a request for a RAID info page, or any page called during * the RAID info page request, poll because these config page requests * are nested. Poll to avoid data corruption due to one page's data * overwriting the outer page request's data. This can happen when * the mutex is released in cv_wait. */ if ((page_type == MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG) || (page_type == MPI2_CONFIG_PAGETYPE_RAID_VOLUME) || (page_type == MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK)) { (void) mptsas_poll(mpt, cmd, pkt->pkt_time * 1000); } else { while ((cmd->cmd_flags & CFLAG_FINISHED) == 0) { cv_wait(&mpt->m_config_cv, &mpt->m_mutex); } } /* * Check if the header request completed without timing out */ if (cmd->cmd_flags & CFLAG_TIMEOUT) { config_flags |= MPTSAS_CMD_TIMEOUT; mptsas_log(mpt, CE_WARN, "config header request timeout"); rval = DDI_FAILURE; goto page_done; } /* * cmd_rfm points to the reply message if a reply was given. Check the * IOCStatus to make sure everything went OK with the header request. */ if (cmd->cmd_rfm) { config_flags |= MPTSAS_ADDRESS_REPLY; (void) ddi_dma_sync(mpt->m_dma_reply_frame_hdl, 0, 0, DDI_DMA_SYNC_FORCPU); reply = (pMpi2ConfigReply_t)(mpt->m_reply_frame + (cmd->cmd_rfm - (mpt->m_reply_frame_dma_addr & 0xffffffffu))); config.page_type = ddi_get8(mpt->m_acc_reply_frame_hdl, &reply->Header.PageType); config.page_number = ddi_get8(mpt->m_acc_reply_frame_hdl, &reply->Header.PageNumber); config.page_length = ddi_get8(mpt->m_acc_reply_frame_hdl, &reply->Header.PageLength); config.page_version = ddi_get8(mpt->m_acc_reply_frame_hdl, &reply->Header.PageVersion); config.ext_page_type = ddi_get8(mpt->m_acc_reply_frame_hdl, &reply->ExtPageType); config.ext_page_length = ddi_get16(mpt->m_acc_reply_frame_hdl, &reply->ExtPageLength); iocstatus = ddi_get16(mpt->m_acc_reply_frame_hdl, &reply->IOCStatus); iocloginfo = ddi_get32(mpt->m_acc_reply_frame_hdl, &reply->IOCLogInfo); if (iocstatus) { NDBG13(("mptsas_access_config_page header: " "IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus, iocloginfo)); rval = DDI_FAILURE; goto page_done; } if ((config.page_type & MPI2_CONFIG_PAGETYPE_MASK) == MPI2_CONFIG_PAGETYPE_EXTENDED) len = (config.ext_page_length * 4); else len = (config.page_length * 4); } if (pkt->pkt_reason == CMD_RESET) { mptsas_log(mpt, CE_WARN, "ioc reset abort config header " "request"); rval = DDI_FAILURE; goto page_done; } /* * Put the reply frame back on the free queue, increment the free * index, and write the new index to the free index register. But only * if this reply is an ADDRESS reply. */ if (config_flags & MPTSAS_ADDRESS_REPLY) { ddi_put32(mpt->m_acc_free_queue_hdl, &((uint32_t *)(void *)mpt->m_free_queue)[mpt->m_free_index], cmd->cmd_rfm); (void) ddi_dma_sync(mpt->m_dma_free_queue_hdl, 0, 0, DDI_DMA_SYNC_FORDEV); if (++mpt->m_free_index == mpt->m_free_queue_depth) { mpt->m_free_index = 0; } ddi_put32(mpt->m_datap, &mpt->m_reg->ReplyFreeHostIndex, mpt->m_free_index); config_flags &= (~MPTSAS_ADDRESS_REPLY); } /* * Allocate DMA buffer here. Store the info regarding this buffer in * the cmd struct so that it can be used for this specific command and * de-allocated after the command completes. The size of the reply * will not be larger than the reply frame size. */ attrs = mpt->m_msg_dma_attr; attrs.dma_attr_sgllen = 1; attrs.dma_attr_granular = (uint32_t)len; if (mptsas_dma_addr_create(mpt, attrs, &cmd->cmd_dmahandle, &accessp, &page_memp, len, &cookie) == FALSE) { rval = DDI_FAILURE; mptsas_log(mpt, CE_WARN, "mptsas_dma_addr_create(len=0x%x) failed", (int)len); goto page_done; } /* NOW we can safely call mptsas_dma_addr_destroy(). */ free_dma = B_TRUE; cmd->cmd_dma_addr = cookie.dmac_laddress; bzero(page_memp, len); /* * Save the data for this request to be used in the call to start the * config page read */ config.action = action; config.page_address = page_address; /* * Re-use the cmd that was used to get the header. Reset some of the * values. */ bzero((caddr_t)pkt, scsi_pkt_size()); pkt->pkt_ha_private = (opaque_t)&config; pkt->pkt_flags = FLAG_HEAD; pkt->pkt_time = 60; cmd->cmd_flags = CFLAG_PREPARED | CFLAG_CMDIOC | CFLAG_CONFIG; /* * Send the config page request. cmd is re-used from header request. */ mptsas_start_config_page_access(mpt, cmd); /* * If this is a request for a RAID info page, or any page called during * the RAID info page request, poll because these config page requests * are nested. Poll to avoid data corruption due to one page's data * overwriting the outer page request's data. This can happen when * the mutex is released in cv_wait. */ if ((page_type == MPI2_CONFIG_EXTPAGETYPE_RAID_CONFIG) || (page_type == MPI2_CONFIG_PAGETYPE_RAID_VOLUME) || (page_type == MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK)) { (void) mptsas_poll(mpt, cmd, pkt->pkt_time * 1000); } else { while ((cmd->cmd_flags & CFLAG_FINISHED) == 0) { cv_wait(&mpt->m_config_cv, &mpt->m_mutex); } } /* * Check if the request completed without timing out */ if (cmd->cmd_flags & CFLAG_TIMEOUT) { config_flags |= MPTSAS_CMD_TIMEOUT; mptsas_log(mpt, CE_WARN, "config page request timeout"); rval = DDI_FAILURE; goto page_done; } /* * cmd_rfm points to the reply message if a reply was given. The reply * frame and the config page are returned from this function in the * param list. */ if (cmd->cmd_rfm) { config_flags |= MPTSAS_ADDRESS_REPLY; (void) ddi_dma_sync(mpt->m_dma_reply_frame_hdl, 0, 0, DDI_DMA_SYNC_FORCPU); (void) ddi_dma_sync(cmd->cmd_dmahandle, 0, 0, DDI_DMA_SYNC_FORCPU); reply = (pMpi2ConfigReply_t)(mpt->m_reply_frame + (cmd->cmd_rfm - (mpt->m_reply_frame_dma_addr & 0xffffffffu))); iocstatus = ddi_get16(mpt->m_acc_reply_frame_hdl, &reply->IOCStatus); iocstatus = MPTSAS_IOCSTATUS(iocstatus); iocloginfo = ddi_get32(mpt->m_acc_reply_frame_hdl, &reply->IOCLogInfo); } if (callback(mpt, page_memp, accessp, iocstatus, iocloginfo, ap)) { rval = DDI_FAILURE; goto page_done; } mptsas_fma_check(mpt, cmd); /* * Check the DMA/ACC handles and then free the DMA buffer. */ if ((mptsas_check_dma_handle(cmd->cmd_dmahandle) != DDI_SUCCESS) || (mptsas_check_acc_handle(accessp) != DDI_SUCCESS)) { ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_UNAFFECTED); rval = DDI_FAILURE; } if (pkt->pkt_reason == CMD_TRAN_ERR) { mptsas_log(mpt, CE_WARN, "config fma error"); rval = DDI_FAILURE; goto page_done; } if (pkt->pkt_reason == CMD_RESET) { mptsas_log(mpt, CE_WARN, "ioc reset abort config request"); rval = DDI_FAILURE; goto page_done; } page_done: va_end(ap); /* * Put the reply frame back on the free queue, increment the free * index, and write the new index to the free index register. But only * if this reply is an ADDRESS reply. */ if (config_flags & MPTSAS_ADDRESS_REPLY) { ddi_put32(mpt->m_acc_free_queue_hdl, &((uint32_t *)(void *)mpt->m_free_queue)[mpt->m_free_index], cmd->cmd_rfm); (void) ddi_dma_sync(mpt->m_dma_free_queue_hdl, 0, 0, DDI_DMA_SYNC_FORDEV); if (++mpt->m_free_index == mpt->m_free_queue_depth) { mpt->m_free_index = 0; } ddi_put32(mpt->m_datap, &mpt->m_reg->ReplyFreeHostIndex, mpt->m_free_index); } if (free_dma) mptsas_dma_addr_destroy(&cmd->cmd_dmahandle, &accessp); if (cmd && (cmd->cmd_flags & CFLAG_PREPARED)) { mptsas_remove_cmd(mpt, cmd); config_flags &= (~MPTSAS_REQUEST_POOL_CMD); } if (config_flags & MPTSAS_REQUEST_POOL_CMD) mptsas_return_to_pool(mpt, cmd); if (config_flags & MPTSAS_CMD_TIMEOUT) { mpt->m_softstate &= ~MPTSAS_SS_MSG_UNIT_RESET; if ((mptsas_restart_ioc(mpt)) == DDI_FAILURE) { mptsas_log(mpt, CE_WARN, "mptsas_restart_ioc failed"); } } return (rval); } int mptsas_send_config_request_msg(mptsas_t *mpt, uint8_t action, uint8_t pagetype, uint32_t pageaddress, uint8_t pagenumber, uint8_t pageversion, uint8_t pagelength, uint32_t SGEflagslength, uint64_t SGEaddress) { pMpi2ConfigRequest_t config; int send_numbytes; bzero(mpt->m_hshk_memp, sizeof (MPI2_CONFIG_REQUEST)); config = (pMpi2ConfigRequest_t)mpt->m_hshk_memp; ddi_put8(mpt->m_hshk_acc_hdl, &config->Function, MPI2_FUNCTION_CONFIG); ddi_put8(mpt->m_hshk_acc_hdl, &config->Action, action); ddi_put8(mpt->m_hshk_acc_hdl, &config->Header.PageNumber, pagenumber); ddi_put8(mpt->m_hshk_acc_hdl, &config->Header.PageType, pagetype); ddi_put32(mpt->m_hshk_acc_hdl, &config->PageAddress, pageaddress); ddi_put8(mpt->m_hshk_acc_hdl, &config->Header.PageVersion, pageversion); ddi_put8(mpt->m_hshk_acc_hdl, &config->Header.PageLength, pagelength); ddi_put32(mpt->m_hshk_acc_hdl, &config->PageBufferSGE.MpiSimple.FlagsLength, SGEflagslength); ddi_put32(mpt->m_hshk_acc_hdl, &config->PageBufferSGE.MpiSimple.u.Address64.Low, SGEaddress); ddi_put32(mpt->m_hshk_acc_hdl, &config->PageBufferSGE.MpiSimple.u.Address64.High, SGEaddress >> 32); send_numbytes = sizeof (MPI2_CONFIG_REQUEST); /* * Post message via handshake */ if (mptsas_send_handshake_msg(mpt, (caddr_t)config, send_numbytes, mpt->m_hshk_acc_hdl)) { return (-1); } return (0); } int mptsas_send_extended_config_request_msg(mptsas_t *mpt, uint8_t action, uint8_t extpagetype, uint32_t pageaddress, uint8_t pagenumber, uint8_t pageversion, uint16_t extpagelength, uint32_t SGEflagslength, uint64_t SGEaddress) { pMpi2ConfigRequest_t config; int send_numbytes; bzero(mpt->m_hshk_memp, sizeof (MPI2_CONFIG_REQUEST)); config = (pMpi2ConfigRequest_t)mpt->m_hshk_memp; ddi_put8(mpt->m_hshk_acc_hdl, &config->Function, MPI2_FUNCTION_CONFIG); ddi_put8(mpt->m_hshk_acc_hdl, &config->Action, action); ddi_put8(mpt->m_hshk_acc_hdl, &config->Header.PageNumber, pagenumber); ddi_put8(mpt->m_hshk_acc_hdl, &config->Header.PageType, MPI2_CONFIG_PAGETYPE_EXTENDED); ddi_put8(mpt->m_hshk_acc_hdl, &config->ExtPageType, extpagetype); ddi_put32(mpt->m_hshk_acc_hdl, &config->PageAddress, pageaddress); ddi_put8(mpt->m_hshk_acc_hdl, &config->Header.PageVersion, pageversion); ddi_put16(mpt->m_hshk_acc_hdl, &config->ExtPageLength, extpagelength); ddi_put32(mpt->m_hshk_acc_hdl, &config->PageBufferSGE.MpiSimple.FlagsLength, SGEflagslength); ddi_put32(mpt->m_hshk_acc_hdl, &config->PageBufferSGE.MpiSimple.u.Address64.Low, SGEaddress); ddi_put32(mpt->m_hshk_acc_hdl, &config->PageBufferSGE.MpiSimple.u.Address64.High, SGEaddress >> 32); send_numbytes = sizeof (MPI2_CONFIG_REQUEST); /* * Post message via handshake */ if (mptsas_send_handshake_msg(mpt, (caddr_t)config, send_numbytes, mpt->m_hshk_acc_hdl)) { return (-1); } return (0); } int mptsas_ioc_wait_for_response(mptsas_t *mpt) { int polls = 0; while ((ddi_get32(mpt->m_datap, &mpt->m_reg->HostInterruptStatus) & MPI2_HIS_IOP_DOORBELL_STATUS)) { drv_usecwait(1000); if (polls++ > 60000) { return (-1); } } return (0); } int mptsas_ioc_wait_for_doorbell(mptsas_t *mpt) { int polls = 0; while ((ddi_get32(mpt->m_datap, &mpt->m_reg->HostInterruptStatus) & MPI2_HIM_DIM) == 0) { drv_usecwait(1000); if (polls++ > 300000) { return (-1); } } return (0); } int mptsas_send_handshake_msg(mptsas_t *mpt, caddr_t memp, int numbytes, ddi_acc_handle_t accessp) { int i; /* * clean pending doorbells */ ddi_put32(mpt->m_datap, &mpt->m_reg->HostInterruptStatus, 0); ddi_put32(mpt->m_datap, &mpt->m_reg->Doorbell, ((MPI2_FUNCTION_HANDSHAKE << MPI2_DOORBELL_FUNCTION_SHIFT) | ((numbytes / 4) << MPI2_DOORBELL_ADD_DWORDS_SHIFT))); if (mptsas_ioc_wait_for_doorbell(mpt)) { NDBG19(("mptsas_send_handshake failed. Doorbell not ready\n")); return (-1); } /* * clean pending doorbells again */ ddi_put32(mpt->m_datap, &mpt->m_reg->HostInterruptStatus, 0); if (mptsas_ioc_wait_for_response(mpt)) { NDBG19(("mptsas_send_handshake failed. Doorbell not " "cleared\n")); return (-1); } /* * post handshake message */ for (i = 0; (i < numbytes / 4); i++, memp += 4) { ddi_put32(mpt->m_datap, &mpt->m_reg->Doorbell, ddi_get32(accessp, (uint32_t *)((void *)(memp)))); if (mptsas_ioc_wait_for_response(mpt)) { NDBG19(("mptsas_send_handshake failed posting " "message\n")); return (-1); } } if (mptsas_check_acc_handle(mpt->m_datap) != DDI_SUCCESS) { ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_UNAFFECTED); ddi_fm_acc_err_clear(mpt->m_datap, DDI_FME_VER0); return (-1); } return (0); } int mptsas_get_handshake_msg(mptsas_t *mpt, caddr_t memp, int numbytes, ddi_acc_handle_t accessp) { int i, totalbytes, bytesleft; uint16_t val; /* * wait for doorbell */ if (mptsas_ioc_wait_for_doorbell(mpt)) { NDBG19(("mptsas_get_handshake failed. Doorbell not ready\n")); return (-1); } /* * get first 2 bytes of handshake message to determine how much * data we will be getting */ for (i = 0; i < 2; i++, memp += 2) { val = (ddi_get32(mpt->m_datap, &mpt->m_reg->Doorbell) & MPI2_DOORBELL_DATA_MASK); ddi_put32(mpt->m_datap, &mpt->m_reg->HostInterruptStatus, 0); if (mptsas_ioc_wait_for_doorbell(mpt)) { NDBG19(("mptsas_get_handshake failure getting initial" " data\n")); return (-1); } ddi_put16(accessp, (uint16_t *)((void *)(memp)), val); if (i == 1) { totalbytes = (val & 0xFF) * 2; } } /* * If we are expecting less bytes than the message wants to send * we simply save as much as we expected and then throw out the rest * later */ if (totalbytes > (numbytes / 2)) { bytesleft = ((numbytes / 2) - 2); } else { bytesleft = (totalbytes - 2); } /* * Get the rest of the data */ for (i = 0; i < bytesleft; i++, memp += 2) { val = (ddi_get32(mpt->m_datap, &mpt->m_reg->Doorbell) & MPI2_DOORBELL_DATA_MASK); ddi_put32(mpt->m_datap, &mpt->m_reg->HostInterruptStatus, 0); if (mptsas_ioc_wait_for_doorbell(mpt)) { NDBG19(("mptsas_get_handshake failure getting" " main data\n")); return (-1); } ddi_put16(accessp, (uint16_t *)((void *)(memp)), val); } /* * Sometimes the device will send more data than is expected * This data is not used by us but needs to be cleared from * ioc doorbell. So we just read the values and throw * them out. */ if (totalbytes > (numbytes / 2)) { for (i = (numbytes / 2); i < totalbytes; i++) { val = (ddi_get32(mpt->m_datap, &mpt->m_reg->Doorbell) & MPI2_DOORBELL_DATA_MASK); ddi_put32(mpt->m_datap, &mpt->m_reg->HostInterruptStatus, 0); if (mptsas_ioc_wait_for_doorbell(mpt)) { NDBG19(("mptsas_get_handshake failure getting " "extra garbage data\n")); return (-1); } } } ddi_put32(mpt->m_datap, &mpt->m_reg->HostInterruptStatus, 0); if (mptsas_check_acc_handle(mpt->m_datap) != DDI_SUCCESS) { ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_UNAFFECTED); ddi_fm_acc_err_clear(mpt->m_datap, DDI_FME_VER0); return (-1); } return (0); } int mptsas_kick_start(mptsas_t *mpt) { int polls = 0; uint32_t diag_reg, ioc_state, saved_HCB_size; /* * Start a hard reset. Write magic number and wait 500 mSeconds. */ MPTSAS_ENABLE_DRWE(mpt); drv_usecwait(500000); /* * Read the current Diag Reg and save the Host Controlled Boot size. */ diag_reg = ddi_get32(mpt->m_datap, &mpt->m_reg->HostDiagnostic); saved_HCB_size = ddi_get32(mpt->m_datap, &mpt->m_reg->HCBSize); /* * Set Reset Adapter bit and wait 50 mSeconds. */ diag_reg |= MPI2_DIAG_RESET_ADAPTER; ddi_put32(mpt->m_datap, &mpt->m_reg->HostDiagnostic, diag_reg); drv_usecwait(50000); /* * Poll, waiting for Reset Adapter bit to clear. 300 Seconds max * (600000 * 500 = 300,000,000 uSeconds, 300 seconds). * If no more adapter (all FF's), just return failure. */ for (polls = 0; polls < 600000; polls++) { diag_reg = ddi_get32(mpt->m_datap, &mpt->m_reg->HostDiagnostic); if (diag_reg == 0xFFFFFFFF) { mptsas_fm_ereport(mpt, DDI_FM_DEVICE_NO_RESPONSE); ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_LOST); return (DDI_FAILURE); } if (!(diag_reg & MPI2_DIAG_RESET_ADAPTER)) { break; } drv_usecwait(500); } if (polls == 600000) { mptsas_fm_ereport(mpt, DDI_FM_DEVICE_NO_RESPONSE); ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_LOST); return (DDI_FAILURE); } /* * Check if adapter is in Host Boot Mode. If so, restart adapter * assuming the HCB points to good FW. * Set BootDeviceSel to HCDW (Host Code and Data Window). */ if (diag_reg & MPI2_DIAG_HCB_MODE) { diag_reg &= ~MPI2_DIAG_BOOT_DEVICE_SELECT_MASK; diag_reg |= MPI2_DIAG_BOOT_DEVICE_SELECT_HCDW; ddi_put32(mpt->m_datap, &mpt->m_reg->HostDiagnostic, diag_reg); /* * Re-enable the HCDW. */ ddi_put32(mpt->m_datap, &mpt->m_reg->HCBSize, (saved_HCB_size | MPI2_HCB_SIZE_HCB_ENABLE)); } /* * Restart the adapter. */ diag_reg &= ~MPI2_DIAG_HOLD_IOC_RESET; ddi_put32(mpt->m_datap, &mpt->m_reg->HostDiagnostic, diag_reg); /* * Disable writes to the Host Diag register. */ ddi_put32(mpt->m_datap, &mpt->m_reg->WriteSequence, MPI2_WRSEQ_FLUSH_KEY_VALUE); /* * Wait 60 seconds max for FW to come to ready state. */ for (polls = 0; polls < 60000; polls++) { ioc_state = ddi_get32(mpt->m_datap, &mpt->m_reg->Doorbell); if (ioc_state == 0xFFFFFFFF) { mptsas_fm_ereport(mpt, DDI_FM_DEVICE_NO_RESPONSE); ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_LOST); return (DDI_FAILURE); } if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_READY) { break; } drv_usecwait(1000); } if (polls == 60000) { mptsas_fm_ereport(mpt, DDI_FM_DEVICE_NO_RESPONSE); ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_LOST); return (DDI_FAILURE); } /* * Clear the ioc ack events queue. */ mptsas_destroy_ioc_event_cmd(mpt); return (DDI_SUCCESS); } int mptsas_ioc_reset(mptsas_t *mpt, int first_time) { int polls = 0; uint32_t reset_msg; uint32_t ioc_state; ioc_state = ddi_get32(mpt->m_datap, &mpt->m_reg->Doorbell); /* * If chip is already in ready state then there is nothing to do. */ if (ioc_state == MPI2_IOC_STATE_READY) { return (MPTSAS_NO_RESET); } /* * If the chip is already operational, we just need to send * it a message unit reset to put it back in the ready state */ if (ioc_state & MPI2_IOC_STATE_OPERATIONAL) { /* * If the first time, try MUR anyway, because we haven't even * queried the card for m_event_replay and other capabilities. * Other platforms do it this way, we can still do a hard * reset if we need to, MUR takes less time than a full * adapter reset, and there are reports that some HW * combinations will lock up when receiving a hard reset. */ if ((first_time || mpt->m_event_replay) && (mpt->m_softstate & MPTSAS_SS_MSG_UNIT_RESET)) { mpt->m_softstate &= ~MPTSAS_SS_MSG_UNIT_RESET; reset_msg = MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET; ddi_put32(mpt->m_datap, &mpt->m_reg->Doorbell, (reset_msg << MPI2_DOORBELL_FUNCTION_SHIFT)); if (mptsas_ioc_wait_for_response(mpt)) { NDBG19(("mptsas_ioc_reset failure sending " "message_unit_reset\n")); goto hard_reset; } /* * Wait no more than 60 seconds for chip to become * ready. */ while ((ddi_get32(mpt->m_datap, &mpt->m_reg->Doorbell) & MPI2_IOC_STATE_READY) == 0x0) { drv_usecwait(1000); if (polls++ > 60000) { goto hard_reset; } } /* * Save the last reset mode done on IOC which will be * helpful while resuming from suspension. */ mpt->m_softstate |= MPTSAS_DID_MSG_UNIT_RESET; /* * the message unit reset would do reset operations * clear reply and request queue, so we should clear * ACK event cmd. */ mptsas_destroy_ioc_event_cmd(mpt); return (MPTSAS_SUCCESS_MUR); } } hard_reset: mpt->m_softstate &= ~MPTSAS_DID_MSG_UNIT_RESET; if (mptsas_kick_start(mpt) == DDI_FAILURE) { mptsas_fm_ereport(mpt, DDI_FM_DEVICE_NO_RESPONSE); ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_LOST); return (MPTSAS_RESET_FAIL); } return (MPTSAS_SUCCESS_HARDRESET); } int mptsas_request_from_pool(mptsas_t *mpt, mptsas_cmd_t **cmd, struct scsi_pkt **pkt) { m_event_struct_t *ioc_cmd = NULL; ioc_cmd = kmem_zalloc(M_EVENT_STRUCT_SIZE, KM_SLEEP); if (ioc_cmd == NULL) { return (DDI_FAILURE); } ioc_cmd->m_event_linkp = NULL; mptsas_ioc_event_cmdq_add(mpt, ioc_cmd); *cmd = &(ioc_cmd->m_event_cmd); *pkt = &(ioc_cmd->m_event_pkt); return (DDI_SUCCESS); } void mptsas_return_to_pool(mptsas_t *mpt, mptsas_cmd_t *cmd) { m_event_struct_t *ioc_cmd = NULL; ioc_cmd = mptsas_ioc_event_find_by_cmd(mpt, cmd); if (ioc_cmd == NULL) { return; } mptsas_ioc_event_cmdq_delete(mpt, ioc_cmd); kmem_free(ioc_cmd, M_EVENT_STRUCT_SIZE); ioc_cmd = NULL; } /* * NOTE: We should be able to queue TM requests in the controller to make this * a lot faster. If resetting all targets, for example, we can load the hi * priority queue with its limit and the controller will reply as they are * completed. This way, we don't have to poll for one reply at a time. * Think about enhancing this later. */ int mptsas_ioc_task_management(mptsas_t *mpt, int task_type, uint16_t dev_handle, int lun, uint8_t *reply, uint32_t reply_size, int mode) { /* * In order to avoid allocating variables on the stack, * we make use of the pre-existing mptsas_cmd_t and * scsi_pkt which are included in the mptsas_t which * is passed to this routine. */ pMpi2SCSITaskManagementRequest_t task; int rval = FALSE; mptsas_cmd_t *cmd; struct scsi_pkt *pkt; mptsas_slots_t *slots = mpt->m_active; uint64_t request_desc, i; pMPI2DefaultReply_t reply_msg; /* * Can't start another task management routine. */ if (slots->m_slot[MPTSAS_TM_SLOT(mpt)] != NULL) { mptsas_log(mpt, CE_WARN, "Can only start 1 task management" " command at a time\n"); return (FALSE); } cmd = &(mpt->m_event_task_mgmt.m_event_cmd); pkt = &(mpt->m_event_task_mgmt.m_event_pkt); bzero((caddr_t)cmd, sizeof (*cmd)); bzero((caddr_t)pkt, scsi_pkt_size()); pkt->pkt_cdbp = (opaque_t)&cmd->cmd_cdb[0]; pkt->pkt_scbp = (opaque_t)&cmd->cmd_scb; pkt->pkt_ha_private = (opaque_t)cmd; pkt->pkt_flags = (FLAG_NOINTR | FLAG_HEAD); pkt->pkt_time = 60; pkt->pkt_address.a_target = dev_handle; pkt->pkt_address.a_lun = (uchar_t)lun; cmd->cmd_pkt = pkt; cmd->cmd_scblen = 1; cmd->cmd_flags = CFLAG_TM_CMD; cmd->cmd_slot = MPTSAS_TM_SLOT(mpt); slots->m_slot[MPTSAS_TM_SLOT(mpt)] = cmd; /* * Store the TM message in memory location corresponding to the TM slot * number. */ task = (pMpi2SCSITaskManagementRequest_t)(mpt->m_req_frame + (mpt->m_req_frame_size * cmd->cmd_slot)); bzero(task, mpt->m_req_frame_size); /* * form message for requested task */ mptsas_init_std_hdr(mpt->m_acc_req_frame_hdl, task, dev_handle, lun, 0, MPI2_FUNCTION_SCSI_TASK_MGMT); /* * Set the task type */ ddi_put8(mpt->m_acc_req_frame_hdl, &task->TaskType, task_type); /* * Send TM request using High Priority Queue. */ (void) ddi_dma_sync(mpt->m_dma_req_frame_hdl, 0, 0, DDI_DMA_SYNC_FORDEV); request_desc = (cmd->cmd_slot << 16) + MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY; MPTSAS_START_CMD(mpt, request_desc); rval = mptsas_poll(mpt, cmd, MPTSAS_POLL_TIME); if (pkt->pkt_reason == CMD_INCOMPLETE) rval = FALSE; /* * If a reply frame was used and there is a reply buffer to copy the * reply data into, copy it. If this fails, log a message, but don't * fail the TM request. */ if (cmd->cmd_rfm && reply) { (void) ddi_dma_sync(mpt->m_dma_reply_frame_hdl, 0, 0, DDI_DMA_SYNC_FORCPU); reply_msg = (pMPI2DefaultReply_t) (mpt->m_reply_frame + (cmd->cmd_rfm - (mpt->m_reply_frame_dma_addr & 0xffffffffu))); if (reply_size > sizeof (MPI2_SCSI_TASK_MANAGE_REPLY)) { reply_size = sizeof (MPI2_SCSI_TASK_MANAGE_REPLY); } mutex_exit(&mpt->m_mutex); for (i = 0; i < reply_size; i++) { if (ddi_copyout((uint8_t *)reply_msg + i, reply + i, 1, mode)) { mptsas_log(mpt, CE_WARN, "failed to copy out " "reply data for TM request"); break; } } mutex_enter(&mpt->m_mutex); } /* * clear the TM slot before returning */ slots->m_slot[MPTSAS_TM_SLOT(mpt)] = NULL; /* * If we lost our task management command * we need to reset the ioc */ if (rval == FALSE) { mptsas_log(mpt, CE_WARN, "mptsas_ioc_task_management failed " "try to reset ioc to recovery!"); mpt->m_softstate &= ~MPTSAS_SS_MSG_UNIT_RESET; if (mptsas_restart_ioc(mpt)) { mptsas_log(mpt, CE_WARN, "mptsas_restart_ioc failed"); rval = FAILED; } } return (rval); } /* * Complete firmware download frame for v2.0 cards. */ static void mptsas_uflash2(pMpi2FWDownloadRequest fwdownload, ddi_acc_handle_t acc_hdl, uint32_t size, uint8_t type, ddi_dma_cookie_t flsh_cookie) { pMpi2FWDownloadTCSGE_t tcsge; pMpi2SGESimple64_t sge; uint32_t flagslength; ddi_put8(acc_hdl, &fwdownload->Function, MPI2_FUNCTION_FW_DOWNLOAD); ddi_put8(acc_hdl, &fwdownload->ImageType, type); ddi_put8(acc_hdl, &fwdownload->MsgFlags, MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT); ddi_put32(acc_hdl, &fwdownload->TotalImageSize, size); tcsge = (pMpi2FWDownloadTCSGE_t)&fwdownload->SGL; ddi_put8(acc_hdl, &tcsge->ContextSize, 0); ddi_put8(acc_hdl, &tcsge->DetailsLength, 12); ddi_put8(acc_hdl, &tcsge->Flags, 0); ddi_put32(acc_hdl, &tcsge->ImageOffset, 0); ddi_put32(acc_hdl, &tcsge->ImageSize, size); sge = (pMpi2SGESimple64_t)(tcsge + 1); flagslength = size; flagslength |= ((uint32_t)(MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_SIMPLE_ELEMENT | MPI2_SGE_FLAGS_SYSTEM_ADDRESS | MPI2_SGE_FLAGS_64_BIT_ADDRESSING | MPI2_SGE_FLAGS_HOST_TO_IOC | MPI2_SGE_FLAGS_END_OF_LIST) << MPI2_SGE_FLAGS_SHIFT); ddi_put32(acc_hdl, &sge->FlagsLength, flagslength); ddi_put32(acc_hdl, &sge->Address.Low, flsh_cookie.dmac_address); ddi_put32(acc_hdl, &sge->Address.High, (uint32_t)(flsh_cookie.dmac_laddress >> 32)); } /* * Complete firmware download frame for v2.5 cards. */ static void mptsas_uflash25(pMpi25FWDownloadRequest fwdownload, ddi_acc_handle_t acc_hdl, uint32_t size, uint8_t type, ddi_dma_cookie_t flsh_cookie) { pMpi2IeeeSgeSimple64_t sge; uint8_t flags; ddi_put8(acc_hdl, &fwdownload->Function, MPI2_FUNCTION_FW_DOWNLOAD); ddi_put8(acc_hdl, &fwdownload->ImageType, type); ddi_put8(acc_hdl, &fwdownload->MsgFlags, MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT); ddi_put32(acc_hdl, &fwdownload->TotalImageSize, size); ddi_put32(acc_hdl, &fwdownload->ImageOffset, 0); ddi_put32(acc_hdl, &fwdownload->ImageSize, size); sge = (pMpi2IeeeSgeSimple64_t)&fwdownload->SGL; flags = MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT | MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR | MPI25_IEEE_SGE_FLAGS_END_OF_LIST; ddi_put8(acc_hdl, &sge->Flags, flags); ddi_put32(acc_hdl, &sge->Length, size); ddi_put32(acc_hdl, &sge->Address.Low, flsh_cookie.dmac_address); ddi_put32(acc_hdl, &sge->Address.High, (uint32_t)(flsh_cookie.dmac_laddress >> 32)); } static int mptsas_enable_mpi25_flashupdate = 0; int mptsas_update_flash(mptsas_t *mpt, caddr_t ptrbuffer, uint32_t size, uint8_t type, int mode) { /* * In order to avoid allocating variables on the stack, * we make use of the pre-existing mptsas_cmd_t and * scsi_pkt which are included in the mptsas_t which * is passed to this routine. */ ddi_dma_attr_t flsh_dma_attrs; ddi_dma_cookie_t flsh_cookie; ddi_dma_handle_t flsh_dma_handle; ddi_acc_handle_t flsh_accessp; caddr_t memp, flsh_memp; mptsas_cmd_t *cmd; struct scsi_pkt *pkt; int i; int rvalue = 0; uint64_t request_desc; if (mpt->m_MPI25 && !mptsas_enable_mpi25_flashupdate) { /* * The code is there but not tested yet. * User has to know there are risks here. */ mptsas_log(mpt, CE_WARN, "mptsas_update_flash(): " "Updating firmware through MPI 2.5 has not been " "tested yet!\n" "To enable set mptsas_enable_mpi25_flashupdate to 1\n"); return (-1); } /* Otherwise, you pay your money and you take your chances. */ if ((rvalue = (mptsas_request_from_pool(mpt, &cmd, &pkt))) == -1) { mptsas_log(mpt, CE_WARN, "mptsas_update_flash(): allocation " "failed. event ack command pool is full\n"); return (rvalue); } bzero((caddr_t)cmd, sizeof (*cmd)); bzero((caddr_t)pkt, scsi_pkt_size()); cmd->ioc_cmd_slot = (uint32_t)rvalue; /* * dynamically create a customized dma attribute structure * that describes the flash file. */ flsh_dma_attrs = mpt->m_msg_dma_attr; flsh_dma_attrs.dma_attr_sgllen = 1; if (mptsas_dma_addr_create(mpt, flsh_dma_attrs, &flsh_dma_handle, &flsh_accessp, &flsh_memp, size, &flsh_cookie) == FALSE) { mptsas_log(mpt, CE_WARN, "(unable to allocate dma resource."); mptsas_return_to_pool(mpt, cmd); return (-1); } bzero(flsh_memp, size); for (i = 0; i < size; i++) { (void) ddi_copyin(ptrbuffer + i, flsh_memp + i, 1, mode); } (void) ddi_dma_sync(flsh_dma_handle, 0, 0, DDI_DMA_SYNC_FORDEV); /* * form a cmd/pkt to store the fw download message */ pkt->pkt_cdbp = (opaque_t)&cmd->cmd_cdb[0]; pkt->pkt_scbp = (opaque_t)&cmd->cmd_scb; pkt->pkt_ha_private = (opaque_t)cmd; pkt->pkt_flags = FLAG_HEAD; pkt->pkt_time = 60; cmd->cmd_pkt = pkt; cmd->cmd_scblen = 1; cmd->cmd_flags = CFLAG_CMDIOC | CFLAG_FW_CMD; /* * Save the command in a slot */ if (mptsas_save_cmd(mpt, cmd) == FALSE) { mptsas_dma_addr_destroy(&flsh_dma_handle, &flsh_accessp); mptsas_return_to_pool(mpt, cmd); return (-1); } /* * Fill in fw download message */ ASSERT(cmd->cmd_slot != 0); memp = mpt->m_req_frame + (mpt->m_req_frame_size * cmd->cmd_slot); bzero(memp, mpt->m_req_frame_size); if (mpt->m_MPI25) mptsas_uflash25((pMpi25FWDownloadRequest)memp, mpt->m_acc_req_frame_hdl, size, type, flsh_cookie); else mptsas_uflash2((pMpi2FWDownloadRequest)memp, mpt->m_acc_req_frame_hdl, size, type, flsh_cookie); /* * Start command */ (void) ddi_dma_sync(mpt->m_dma_req_frame_hdl, 0, 0, DDI_DMA_SYNC_FORDEV); request_desc = (cmd->cmd_slot << 16) + MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; cmd->cmd_rfm = 0; MPTSAS_START_CMD(mpt, request_desc); rvalue = 0; (void) cv_reltimedwait(&mpt->m_fw_cv, &mpt->m_mutex, drv_usectohz(60 * MICROSEC), TR_CLOCK_TICK); if (!(cmd->cmd_flags & CFLAG_FINISHED)) { mpt->m_softstate &= ~MPTSAS_SS_MSG_UNIT_RESET; if ((mptsas_restart_ioc(mpt)) == DDI_FAILURE) { mptsas_log(mpt, CE_WARN, "mptsas_restart_ioc failed"); } rvalue = -1; } mptsas_remove_cmd(mpt, cmd); mptsas_dma_addr_destroy(&flsh_dma_handle, &flsh_accessp); return (rvalue); } static int mptsas_sasdevpage_0_cb(mptsas_t *mpt, caddr_t page_memp, ddi_acc_handle_t accessp, uint16_t iocstatus, uint32_t iocloginfo, va_list ap) { #ifndef __lock_lint _NOTE(ARGUNUSED(ap)) #endif pMpi2SasDevicePage0_t sasdevpage; int rval = DDI_SUCCESS, i; uint8_t *sas_addr = NULL; uint8_t tmp_sas_wwn[SAS_WWN_BYTE_SIZE]; uint16_t *devhdl, *bay_num, *enclosure; uint64_t *sas_wwn; uint32_t *dev_info; uint8_t *physport, *phynum; uint16_t *pdevhdl, *io_flags; uint32_t page_address; if ((iocstatus != MPI2_IOCSTATUS_SUCCESS) && (iocstatus != MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)) { mptsas_log(mpt, CE_WARN, "mptsas_get_sas_device_page0 " "header: IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus, iocloginfo); rval = DDI_FAILURE; return (rval); } page_address = va_arg(ap, uint32_t); /* * The INVALID_PAGE status is normal if using GET_NEXT_HANDLE and there * are no more pages. If everything is OK up to this point but the * status is INVALID_PAGE, change rval to FAILURE and quit. Also, * signal that device traversal is complete. */ if (iocstatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) { if ((page_address & MPI2_SAS_DEVICE_PGAD_FORM_MASK) == MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE) { mpt->m_done_traverse_dev = 1; } rval = DDI_FAILURE; return (rval); } devhdl = va_arg(ap, uint16_t *); sas_wwn = va_arg(ap, uint64_t *); dev_info = va_arg(ap, uint32_t *); physport = va_arg(ap, uint8_t *); phynum = va_arg(ap, uint8_t *); pdevhdl = va_arg(ap, uint16_t *); bay_num = va_arg(ap, uint16_t *); enclosure = va_arg(ap, uint16_t *); io_flags = va_arg(ap, uint16_t *); sasdevpage = (pMpi2SasDevicePage0_t)page_memp; *dev_info = ddi_get32(accessp, &sasdevpage->DeviceInfo); *devhdl = ddi_get16(accessp, &sasdevpage->DevHandle); sas_addr = (uint8_t *)(&sasdevpage->SASAddress); for (i = 0; i < SAS_WWN_BYTE_SIZE; i++) { tmp_sas_wwn[i] = ddi_get8(accessp, sas_addr + i); } bcopy(tmp_sas_wwn, sas_wwn, SAS_WWN_BYTE_SIZE); *sas_wwn = LE_64(*sas_wwn); *physport = ddi_get8(accessp, &sasdevpage->PhysicalPort); *phynum = ddi_get8(accessp, &sasdevpage->PhyNum); *pdevhdl = ddi_get16(accessp, &sasdevpage->ParentDevHandle); *bay_num = ddi_get16(accessp, &sasdevpage->Slot); *enclosure = ddi_get16(accessp, &sasdevpage->EnclosureHandle); *io_flags = ddi_get16(accessp, &sasdevpage->Flags); if (*io_flags & MPI25_SAS_DEVICE0_FLAGS_FAST_PATH_CAPABLE) { /* * Leave a messages about FP cabability in the log. */ mptsas_log(mpt, CE_CONT, "!w%016"PRIx64" FastPath Capable%s", *sas_wwn, (*io_flags & MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH)? " and Enabled":" but Disabled"); } return (rval); } /* * Request MPI configuration page SAS device page 0 to get DevHandle, device * info and SAS address. */ int mptsas_get_sas_device_page0(mptsas_t *mpt, uint32_t page_address, uint16_t *dev_handle, uint64_t *sas_wwn, uint32_t *dev_info, uint8_t *physport, uint8_t *phynum, uint16_t *pdev_handle, uint16_t *bay_num, uint16_t *enclosure, uint16_t *io_flags) { int rval = DDI_SUCCESS; ASSERT(mutex_owned(&mpt->m_mutex)); /* * Get the header and config page. reply contains the reply frame, * which holds status info for the request. */ rval = mptsas_access_config_page(mpt, MPI2_CONFIG_ACTION_PAGE_READ_CURRENT, MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE, 0, page_address, mptsas_sasdevpage_0_cb, page_address, dev_handle, sas_wwn, dev_info, physport, phynum, pdev_handle, bay_num, enclosure, io_flags); return (rval); } static int mptsas_sasexpdpage_0_cb(mptsas_t *mpt, caddr_t page_memp, ddi_acc_handle_t accessp, uint16_t iocstatus, uint32_t iocloginfo, va_list ap) { #ifndef __lock_lint _NOTE(ARGUNUSED(ap)) #endif pMpi2ExpanderPage0_t expddevpage; int rval = DDI_SUCCESS, i; uint8_t *sas_addr = NULL; uint8_t tmp_sas_wwn[SAS_WWN_BYTE_SIZE]; uint16_t *devhdl; uint64_t *sas_wwn; uint8_t physport; mptsas_phymask_t *phymask; uint16_t *pdevhdl; uint32_t page_address; if ((iocstatus != MPI2_IOCSTATUS_SUCCESS) && (iocstatus != MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)) { mptsas_log(mpt, CE_WARN, "mptsas_get_sas_expander_page0 " "config: IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus, iocloginfo); rval = DDI_FAILURE; return (rval); } page_address = va_arg(ap, uint32_t); /* * The INVALID_PAGE status is normal if using GET_NEXT_HANDLE and there * are no more pages. If everything is OK up to this point but the * status is INVALID_PAGE, change rval to FAILURE and quit. Also, * signal that device traversal is complete. */ if (iocstatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) { if ((page_address & MPI2_SAS_EXPAND_PGAD_FORM_MASK) == MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL) { mpt->m_done_traverse_smp = 1; } rval = DDI_FAILURE; return (rval); } devhdl = va_arg(ap, uint16_t *); sas_wwn = va_arg(ap, uint64_t *); phymask = va_arg(ap, mptsas_phymask_t *); pdevhdl = va_arg(ap, uint16_t *); expddevpage = (pMpi2ExpanderPage0_t)page_memp; *devhdl = ddi_get16(accessp, &expddevpage->DevHandle); physport = ddi_get8(accessp, &expddevpage->PhysicalPort); *phymask = mptsas_physport_to_phymask(mpt, physport); *pdevhdl = ddi_get16(accessp, &expddevpage->ParentDevHandle); sas_addr = (uint8_t *)(&expddevpage->SASAddress); for (i = 0; i < SAS_WWN_BYTE_SIZE; i++) { tmp_sas_wwn[i] = ddi_get8(accessp, sas_addr + i); } bcopy(tmp_sas_wwn, sas_wwn, SAS_WWN_BYTE_SIZE); *sas_wwn = LE_64(*sas_wwn); return (rval); } /* * Request MPI configuration page SAS device page 0 to get DevHandle, phymask * and SAS address. */ int mptsas_get_sas_expander_page0(mptsas_t *mpt, uint32_t page_address, mptsas_smp_t *info) { int rval = DDI_SUCCESS; ASSERT(mutex_owned(&mpt->m_mutex)); /* * Get the header and config page. reply contains the reply frame, * which holds status info for the request. */ rval = mptsas_access_config_page(mpt, MPI2_CONFIG_ACTION_PAGE_READ_CURRENT, MPI2_CONFIG_EXTPAGETYPE_SAS_EXPANDER, 0, page_address, mptsas_sasexpdpage_0_cb, page_address, &info->m_devhdl, &info->m_addr.mta_wwn, &info->m_addr.mta_phymask, &info->m_pdevhdl); return (rval); } static int mptsas_sasportpage_0_cb(mptsas_t *mpt, caddr_t page_memp, ddi_acc_handle_t accessp, uint16_t iocstatus, uint32_t iocloginfo, va_list ap) { #ifndef __lock_lint _NOTE(ARGUNUSED(ap)) #endif int rval = DDI_SUCCESS, i; uint8_t *sas_addr = NULL; uint64_t *sas_wwn; uint8_t tmp_sas_wwn[SAS_WWN_BYTE_SIZE]; uint8_t *portwidth; pMpi2SasPortPage0_t sasportpage; if (iocstatus != MPI2_IOCSTATUS_SUCCESS) { mptsas_log(mpt, CE_WARN, "mptsas_get_sas_port_page0 " "config: IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus, iocloginfo); rval = DDI_FAILURE; return (rval); } sas_wwn = va_arg(ap, uint64_t *); portwidth = va_arg(ap, uint8_t *); sasportpage = (pMpi2SasPortPage0_t)page_memp; sas_addr = (uint8_t *)(&sasportpage->SASAddress); for (i = 0; i < SAS_WWN_BYTE_SIZE; i++) { tmp_sas_wwn[i] = ddi_get8(accessp, sas_addr + i); } bcopy(tmp_sas_wwn, sas_wwn, SAS_WWN_BYTE_SIZE); *sas_wwn = LE_64(*sas_wwn); *portwidth = ddi_get8(accessp, &sasportpage->PortWidth); return (rval); } /* * Request MPI configuration page SAS port page 0 to get initiator SAS address * and port width. */ int mptsas_get_sas_port_page0(mptsas_t *mpt, uint32_t page_address, uint64_t *sas_wwn, uint8_t *portwidth) { int rval = DDI_SUCCESS; ASSERT(mutex_owned(&mpt->m_mutex)); /* * Get the header and config page. reply contains the reply frame, * which holds status info for the request. */ rval = mptsas_access_config_page(mpt, MPI2_CONFIG_ACTION_PAGE_READ_CURRENT, MPI2_CONFIG_EXTPAGETYPE_SAS_PORT, 0, page_address, mptsas_sasportpage_0_cb, sas_wwn, portwidth); return (rval); } static int mptsas_sasiou_page_0_cb(mptsas_t *mpt, caddr_t page_memp, ddi_acc_handle_t accessp, uint16_t iocstatus, uint32_t iocloginfo, va_list ap) { #ifndef __lock_lint _NOTE(ARGUNUSED(ap)) #endif int rval = DDI_SUCCESS; pMpi2SasIOUnitPage0_t sasioupage0; int i, num_phys; uint32_t cpdi[MPTSAS_MAX_PHYS], *retrypage0, *readpage1; uint8_t port_flags; if (iocstatus != MPI2_IOCSTATUS_SUCCESS) { mptsas_log(mpt, CE_WARN, "mptsas_get_sas_io_unit_page0 " "config: IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus, iocloginfo); rval = DDI_FAILURE; return (rval); } readpage1 = va_arg(ap, uint32_t *); retrypage0 = va_arg(ap, uint32_t *); sasioupage0 = (pMpi2SasIOUnitPage0_t)page_memp; num_phys = ddi_get8(accessp, &sasioupage0->NumPhys); /* * ASSERT that the num_phys value in SAS IO Unit Page 0 is the same as * was initially set. This should never change throughout the life of * the driver. Note, due to cases where we've seen page zero have more * phys than the reported manufacturing information, we limit the number * of phys here to what we got from the manufacturing information. */ ASSERT3U(num_phys, >=, mpt->m_num_phys); num_phys = mpt->m_num_phys; for (i = 0; i < num_phys; i++) { cpdi[i] = ddi_get32(accessp, &sasioupage0->PhyData[i]. ControllerPhyDeviceInfo); port_flags = ddi_get8(accessp, &sasioupage0->PhyData[i].PortFlags); mpt->m_phy_info[i].port_num = ddi_get8(accessp, &sasioupage0->PhyData[i].Port); mpt->m_phy_info[i].ctrl_devhdl = ddi_get16(accessp, &sasioupage0-> PhyData[i].ControllerDevHandle); mpt->m_phy_info[i].attached_devhdl = ddi_get16(accessp, &sasioupage0-> PhyData[i].AttachedDevHandle); mpt->m_phy_info[i].phy_device_type = cpdi[i]; mpt->m_phy_info[i].port_flags = port_flags; if (port_flags & DISCOVERY_IN_PROGRESS) { *retrypage0 = *retrypage0 + 1; break; } else { *retrypage0 = 0; } if (!(port_flags & AUTO_PORT_CONFIGURATION)) { /* * some PHY configuration described in * SAS IO Unit Page1 */ *readpage1 = 1; } } return (rval); } static int mptsas_sasiou_page_1_cb(mptsas_t *mpt, caddr_t page_memp, ddi_acc_handle_t accessp, uint16_t iocstatus, uint32_t iocloginfo, va_list ap) { #ifndef __lock_lint _NOTE(ARGUNUSED(ap)) #endif int rval = DDI_SUCCESS; pMpi2SasIOUnitPage1_t sasioupage1; int i, num_phys; uint32_t cpdi[MPTSAS_MAX_PHYS]; uint8_t port_flags; if (iocstatus != MPI2_IOCSTATUS_SUCCESS) { mptsas_log(mpt, CE_WARN, "mptsas_get_sas_io_unit_page1 " "config: IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus, iocloginfo); rval = DDI_FAILURE; return (rval); } sasioupage1 = (pMpi2SasIOUnitPage1_t)page_memp; num_phys = ddi_get8(accessp, &sasioupage1->NumPhys); /* * ASSERT that the num_phys value in SAS IO Unit Page 0 is the same as * was initially set. This should never change throughout the life of * the driver. Note, due to cases where we've seen page zero have more * phys than the reported manufacturing information, we limit the number * of phys here to what we got from the manufacturing information. */ ASSERT3U(num_phys, >=, mpt->m_num_phys); num_phys = mpt->m_num_phys; for (i = 0; i < num_phys; i++) { cpdi[i] = ddi_get32(accessp, &sasioupage1->PhyData[i]. ControllerPhyDeviceInfo); port_flags = ddi_get8(accessp, &sasioupage1->PhyData[i].PortFlags); mpt->m_phy_info[i].port_num = ddi_get8(accessp, &sasioupage1->PhyData[i].Port); mpt->m_phy_info[i].port_flags = port_flags; mpt->m_phy_info[i].phy_device_type = cpdi[i]; } return (rval); } /* * Read IO unit page 0 to get information for each PHY. If needed, Read IO Unit * page1 to update the PHY information. This is the message passing method of * this function which should be called except during initialization. */ int mptsas_get_sas_io_unit_page(mptsas_t *mpt) { int rval = DDI_SUCCESS, state; uint32_t readpage1 = 0, retrypage0 = 0; ASSERT(mutex_owned(&mpt->m_mutex)); /* * Now we cycle through the state machine. Here's what happens: * 1. Read IO unit page 0 and set phy information * 2. See if Read IO unit page1 is needed because of port configuration * 3. Read IO unit page 1 and update phy information. */ state = IOUC_READ_PAGE0; while (state != IOUC_DONE) { if (state == IOUC_READ_PAGE0) { rval = mptsas_access_config_page(mpt, MPI2_CONFIG_ACTION_PAGE_READ_CURRENT, MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0, 0, mptsas_sasiou_page_0_cb, &readpage1, &retrypage0); } else if (state == IOUC_READ_PAGE1) { rval = mptsas_access_config_page(mpt, MPI2_CONFIG_ACTION_PAGE_READ_CURRENT, MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 1, 0, mptsas_sasiou_page_1_cb); } if (rval == DDI_SUCCESS) { switch (state) { case IOUC_READ_PAGE0: /* * retry 30 times if discovery is in process */ if (retrypage0 && (retrypage0 < 30)) { drv_usecwait(1000 * 100); state = IOUC_READ_PAGE0; break; } else if (retrypage0 == 30) { mptsas_log(mpt, CE_WARN, "!Discovery in progress, can't " "verify IO unit config, then " "after 30 times retry, give " "up!"); state = IOUC_DONE; rval = DDI_FAILURE; break; } if (readpage1 == 0) { state = IOUC_DONE; rval = DDI_SUCCESS; break; } state = IOUC_READ_PAGE1; break; case IOUC_READ_PAGE1: state = IOUC_DONE; rval = DDI_SUCCESS; break; } } else { return (rval); } } return (rval); } static int mptsas_biospage_3_cb(mptsas_t *mpt, caddr_t page_memp, ddi_acc_handle_t accessp, uint16_t iocstatus, uint32_t iocloginfo, va_list ap) { #ifndef __lock_lint _NOTE(ARGUNUSED(ap)) #endif pMpi2BiosPage3_t sasbiospage; int rval = DDI_SUCCESS; uint32_t *bios_version; if ((iocstatus != MPI2_IOCSTATUS_SUCCESS) && (iocstatus != MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)) { mptsas_log(mpt, CE_WARN, "mptsas_get_bios_page3 header: " "IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus, iocloginfo); rval = DDI_FAILURE; return (rval); } bios_version = va_arg(ap, uint32_t *); sasbiospage = (pMpi2BiosPage3_t)page_memp; *bios_version = ddi_get32(accessp, &sasbiospage->BiosVersion); return (rval); } /* * Request MPI configuration page BIOS page 3 to get BIOS version. Since all * other information in this page is not needed, just ignore it. */ int mptsas_get_bios_page3(mptsas_t *mpt, uint32_t *bios_version) { int rval = DDI_SUCCESS; ASSERT(mutex_owned(&mpt->m_mutex)); /* * Get the header and config page. reply contains the reply frame, * which holds status info for the request. */ rval = mptsas_access_config_page(mpt, MPI2_CONFIG_ACTION_PAGE_READ_CURRENT, MPI2_CONFIG_PAGETYPE_BIOS, 3, 0, mptsas_biospage_3_cb, bios_version); return (rval); } /* * Read IO unit page 0 to get information for each PHY. If needed, Read IO Unit * page1 to update the PHY information. This is the handshaking version of * this function, which should be called during initialization only. */ int mptsas_get_sas_io_unit_page_hndshk(mptsas_t *mpt) { ddi_dma_attr_t recv_dma_attrs, page_dma_attrs; ddi_dma_cookie_t page_cookie; ddi_dma_handle_t recv_dma_handle, page_dma_handle; ddi_acc_handle_t recv_accessp, page_accessp; pMpi2ConfigReply_t configreply; pMpi2SasIOUnitPage0_t sasioupage0; pMpi2SasIOUnitPage1_t sasioupage1; int recv_numbytes; caddr_t recv_memp, page_memp; uint_t i, num_phys, start_phy = 0; int page0_size = sizeof (MPI2_CONFIG_PAGE_SASIOUNIT_0) + (sizeof (MPI2_SAS_IO_UNIT0_PHY_DATA) * (MPTSAS_MAX_PHYS - 1)); int page1_size = sizeof (MPI2_CONFIG_PAGE_SASIOUNIT_1) + (sizeof (MPI2_SAS_IO_UNIT1_PHY_DATA) * (MPTSAS_MAX_PHYS - 1)); uint32_t flags_length; uint32_t cpdi[MPTSAS_MAX_PHYS]; uint32_t readpage1 = 0, retrypage0 = 0; uint16_t iocstatus; uint8_t port_flags, page_number, action; uint32_t reply_size; uint_t state; int rval = DDI_FAILURE; boolean_t free_recv = B_FALSE, free_page = B_FALSE; /* * We want to find a reply_size that's large enough for the page0 and * page1 sizes and resistant to increase in the number of phys. */ reply_size = MAX(page0_size, page1_size); if (P2ROUNDUP(reply_size, 256) <= reply_size) { mptsas_log(mpt, CE_WARN, "mptsas_get_sas_io_unit_page_hndsk: " "cannot size reply size"); goto cleanup; } /* * Initialize our "state machine". This is a bit convoluted, * but it keeps us from having to do the ddi allocations numerous * times. */ NDBG20(("mptsas_get_sas_io_unit_page_hndshk enter")); ASSERT(mutex_owned(&mpt->m_mutex)); state = IOUC_READ_PAGE0; /* * dynamically create a customized dma attribute structure * that describes mpt's config reply page request structure. */ recv_dma_attrs = mpt->m_msg_dma_attr; recv_dma_attrs.dma_attr_sgllen = 1; recv_dma_attrs.dma_attr_granular = (sizeof (MPI2_CONFIG_REPLY)); if (mptsas_dma_addr_create(mpt, recv_dma_attrs, &recv_dma_handle, &recv_accessp, &recv_memp, (sizeof (MPI2_CONFIG_REPLY)), NULL) == FALSE) { mptsas_log(mpt, CE_WARN, "mptsas_get_sas_io_unit_page_hndshk: recv dma failed"); goto cleanup; } /* Now safe to call mptsas_dma_addr_destroy(recv_dma_handle). */ free_recv = B_TRUE; page_dma_attrs = mpt->m_msg_dma_attr; page_dma_attrs.dma_attr_sgllen = 1; page_dma_attrs.dma_attr_granular = reply_size; if (mptsas_dma_addr_create(mpt, page_dma_attrs, &page_dma_handle, &page_accessp, &page_memp, reply_size, &page_cookie) == FALSE) { mptsas_log(mpt, CE_WARN, "mptsas_get_sas_io_unit_page_hndshk: page dma failed"); goto cleanup; } /* Now safe to call mptsas_dma_addr_destroy(page_dma_handle). */ free_page = B_TRUE; /* * Now we cycle through the state machine. Here's what happens: * 1. Read IO unit page 0 and set phy information * 2. See if Read IO unit page1 is needed because of port configuration * 3. Read IO unit page 1 and update phy information. */ sasioupage0 = (pMpi2SasIOUnitPage0_t)page_memp; sasioupage1 = (pMpi2SasIOUnitPage1_t)page_memp; while (state != IOUC_DONE) { switch (state) { case IOUC_READ_PAGE0: page_number = 0; action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; flags_length = (uint32_t)page0_size; flags_length |= ((uint32_t)( MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_SIMPLE_ELEMENT | MPI2_SGE_FLAGS_SYSTEM_ADDRESS | MPI2_SGE_FLAGS_64_BIT_ADDRESSING | MPI2_SGE_FLAGS_IOC_TO_HOST | MPI2_SGE_FLAGS_END_OF_LIST) << MPI2_SGE_FLAGS_SHIFT); break; case IOUC_READ_PAGE1: page_number = 1; action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; flags_length = (uint32_t)page1_size; flags_length |= ((uint32_t)( MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_SIMPLE_ELEMENT | MPI2_SGE_FLAGS_SYSTEM_ADDRESS | MPI2_SGE_FLAGS_64_BIT_ADDRESSING | MPI2_SGE_FLAGS_IOC_TO_HOST | MPI2_SGE_FLAGS_END_OF_LIST) << MPI2_SGE_FLAGS_SHIFT); break; default: break; } bzero(recv_memp, sizeof (MPI2_CONFIG_REPLY)); configreply = (pMpi2ConfigReply_t)recv_memp; recv_numbytes = sizeof (MPI2_CONFIG_REPLY); if (mptsas_send_extended_config_request_msg(mpt, MPI2_CONFIG_ACTION_PAGE_HEADER, MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0, page_number, 0, 0, 0, 0)) { goto cleanup; } if (mptsas_get_handshake_msg(mpt, recv_memp, recv_numbytes, recv_accessp)) { goto cleanup; } iocstatus = ddi_get16(recv_accessp, &configreply->IOCStatus); iocstatus = MPTSAS_IOCSTATUS(iocstatus); if (iocstatus != MPI2_IOCSTATUS_SUCCESS) { mptsas_log(mpt, CE_WARN, "mptsas_get_sas_io_unit_page_hndshk: read page " "header iocstatus = 0x%x", iocstatus); goto cleanup; } if (action != MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM) { bzero(page_memp, reply_size); } if (mptsas_send_extended_config_request_msg(mpt, action, MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT, 0, page_number, ddi_get8(recv_accessp, &configreply->Header.PageVersion), ddi_get16(recv_accessp, &configreply->ExtPageLength), flags_length, page_cookie.dmac_laddress)) { goto cleanup; } if (mptsas_get_handshake_msg(mpt, recv_memp, recv_numbytes, recv_accessp)) { goto cleanup; } iocstatus = ddi_get16(recv_accessp, &configreply->IOCStatus); iocstatus = MPTSAS_IOCSTATUS(iocstatus); if (iocstatus != MPI2_IOCSTATUS_SUCCESS) { mptsas_log(mpt, CE_WARN, "mptsas_get_sas_io_unit_page_hndshk: IO unit " "config failed for action %d, iocstatus = 0x%x", action, iocstatus); goto cleanup; } switch (state) { case IOUC_READ_PAGE0: if ((ddi_dma_sync(page_dma_handle, 0, 0, DDI_DMA_SYNC_FORCPU)) != DDI_SUCCESS) { goto cleanup; } num_phys = ddi_get8(page_accessp, &sasioupage0->NumPhys); if (num_phys > MPTSAS_MAX_PHYS) { mptsas_log(mpt, CE_WARN, "Number of phys " "supported by HBA (%d) is more than max " "supported by driver (%d). Driver will " "not attach.", num_phys, MPTSAS_MAX_PHYS); rval = DDI_FAILURE; goto cleanup; } if (num_phys > mpt->m_num_phys) { mptsas_log(mpt, CE_WARN, "Number of phys " "reported by HBA SAS IO Unit Page 0 (%u) " "is greater than that reported by the " "manufacturing information (%u). Driver " "phy count limited to %u. Please contact " "the firmware vendor about this.", num_phys, mpt->m_num_phys, mpt->m_num_phys); num_phys = mpt->m_num_phys; } else if (num_phys < mpt->m_num_phys) { mptsas_log(mpt, CE_WARN, "Number of phys " "reported by HBA SAS IO Unit Page 0 (%u) " "is less than that reported by the " "manufacturing information (%u). Driver " "will not attach. Please contact the " "firmware vendor about this.", num_phys, mpt->m_num_phys); rval = DDI_FAILURE; goto cleanup; } for (i = start_phy; i < num_phys; i++, start_phy = i) { cpdi[i] = ddi_get32(page_accessp, &sasioupage0->PhyData[i]. ControllerPhyDeviceInfo); port_flags = ddi_get8(page_accessp, &sasioupage0->PhyData[i].PortFlags); mpt->m_phy_info[i].port_num = ddi_get8(page_accessp, &sasioupage0->PhyData[i].Port); mpt->m_phy_info[i].ctrl_devhdl = ddi_get16(page_accessp, &sasioupage0-> PhyData[i].ControllerDevHandle); mpt->m_phy_info[i].attached_devhdl = ddi_get16(page_accessp, &sasioupage0-> PhyData[i].AttachedDevHandle); mpt->m_phy_info[i].phy_device_type = cpdi[i]; mpt->m_phy_info[i].port_flags = port_flags; if (port_flags & DISCOVERY_IN_PROGRESS) { retrypage0++; NDBG20(("Discovery in progress, can't " "verify IO unit config, then NO.%d" " times retry", retrypage0)); break; } else { retrypage0 = 0; } if (!(port_flags & AUTO_PORT_CONFIGURATION)) { /* * some PHY configuration described in * SAS IO Unit Page1 */ readpage1 = 1; } } /* * retry 30 times if discovery is in process */ if (retrypage0 && (retrypage0 < 30)) { drv_usecwait(1000 * 100); state = IOUC_READ_PAGE0; break; } else if (retrypage0 == 30) { mptsas_log(mpt, CE_WARN, "!Discovery in progress, can't " "verify IO unit config, then after" " 30 times retry, give up!"); state = IOUC_DONE; rval = DDI_FAILURE; break; } if (readpage1 == 0) { state = IOUC_DONE; rval = DDI_SUCCESS; break; } state = IOUC_READ_PAGE1; break; case IOUC_READ_PAGE1: if ((ddi_dma_sync(page_dma_handle, 0, 0, DDI_DMA_SYNC_FORCPU)) != DDI_SUCCESS) { goto cleanup; } num_phys = ddi_get8(page_accessp, &sasioupage1->NumPhys); if (num_phys > MPTSAS_MAX_PHYS) { mptsas_log(mpt, CE_WARN, "Number of phys " "supported by HBA (%d) is more than max " "supported by driver (%d). Driver will " "not attach.", num_phys, MPTSAS_MAX_PHYS); rval = DDI_FAILURE; goto cleanup; } if (num_phys > mpt->m_num_phys) { mptsas_log(mpt, CE_WARN, "Number of phys " "reported by HBA SAS IO Unit Page 1 (%u) " "is greater than that reported by the " "manufacturing information (%u). Limiting " "phy count to %u. Please contact the " "firmware vendor about this.", num_phys, mpt->m_num_phys, mpt->m_num_phys); num_phys = mpt->m_num_phys; } else if (num_phys < mpt->m_num_phys) { mptsas_log(mpt, CE_WARN, "Number of phys " "reported by HBA SAS IO Unit Page 1 (%u) " "is less than that reported by the " "manufacturing information (%u). Driver " "will not attach. Please contact the " "firmware vendor about this.", num_phys, mpt->m_num_phys); rval = DDI_FAILURE; goto cleanup; } for (i = 0; i < num_phys; i++) { cpdi[i] = ddi_get32(page_accessp, &sasioupage1->PhyData[i]. ControllerPhyDeviceInfo); port_flags = ddi_get8(page_accessp, &sasioupage1->PhyData[i].PortFlags); mpt->m_phy_info[i].port_num = ddi_get8(page_accessp, &sasioupage1->PhyData[i].Port); mpt->m_phy_info[i].port_flags = port_flags; mpt->m_phy_info[i].phy_device_type = cpdi[i]; } state = IOUC_DONE; rval = DDI_SUCCESS; break; } } if ((mptsas_check_dma_handle(recv_dma_handle) != DDI_SUCCESS) || (mptsas_check_dma_handle(page_dma_handle) != DDI_SUCCESS)) { ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_UNAFFECTED); rval = DDI_FAILURE; goto cleanup; } if ((mptsas_check_acc_handle(recv_accessp) != DDI_SUCCESS) || (mptsas_check_acc_handle(page_accessp) != DDI_SUCCESS)) { ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_UNAFFECTED); rval = DDI_FAILURE; goto cleanup; } cleanup: if (free_recv) mptsas_dma_addr_destroy(&recv_dma_handle, &recv_accessp); if (free_page) mptsas_dma_addr_destroy(&page_dma_handle, &page_accessp); if (rval != DDI_SUCCESS) { mptsas_fm_ereport(mpt, DDI_FM_DEVICE_NO_RESPONSE); ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_LOST); } return (rval); } /* * mptsas_get_manufacture_page5 * * This function will retrieve the base WWID from the adapter. Since this * function is only called during the initialization process, use handshaking. */ int mptsas_get_manufacture_page5(mptsas_t *mpt) { ddi_dma_attr_t recv_dma_attrs, page_dma_attrs; ddi_dma_cookie_t page_cookie; ddi_dma_handle_t recv_dma_handle, page_dma_handle; ddi_acc_handle_t recv_accessp, page_accessp; pMpi2ConfigReply_t configreply; caddr_t recv_memp, page_memp; int recv_numbytes; pMpi2ManufacturingPage5_t m5; uint32_t flagslength; int rval = DDI_SUCCESS; uint_t iocstatus; boolean_t free_recv = B_FALSE, free_page = B_FALSE; MPTSAS_DISABLE_INTR(mpt); if (mptsas_send_config_request_msg(mpt, MPI2_CONFIG_ACTION_PAGE_HEADER, MPI2_CONFIG_PAGETYPE_MANUFACTURING, 0, 5, 0, 0, 0, 0)) { rval = DDI_FAILURE; goto done; } /* * dynamically create a customized dma attribute structure * that describes the MPT's config reply page request structure. */ recv_dma_attrs = mpt->m_msg_dma_attr; recv_dma_attrs.dma_attr_sgllen = 1; recv_dma_attrs.dma_attr_granular = (sizeof (MPI2_CONFIG_REPLY)); if (mptsas_dma_addr_create(mpt, recv_dma_attrs, &recv_dma_handle, &recv_accessp, &recv_memp, (sizeof (MPI2_CONFIG_REPLY)), NULL) == FALSE) { rval = DDI_FAILURE; goto done; } /* Now safe to call mptsas_dma_addr_destroy(recv_dma_handle). */ free_recv = B_TRUE; bzero(recv_memp, sizeof (MPI2_CONFIG_REPLY)); configreply = (pMpi2ConfigReply_t)recv_memp; recv_numbytes = sizeof (MPI2_CONFIG_REPLY); /* * get config reply message */ if (mptsas_get_handshake_msg(mpt, recv_memp, recv_numbytes, recv_accessp)) { rval = DDI_FAILURE; goto done; } if ((iocstatus = ddi_get16(recv_accessp, &configreply->IOCStatus)) != 0) { mptsas_log(mpt, CE_WARN, "mptsas_get_manufacture_page5 update: " "IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus, ddi_get32(recv_accessp, &configreply->IOCLogInfo)); goto done; } /* * dynamically create a customized dma attribute structure * that describes the MPT's config page structure. */ page_dma_attrs = mpt->m_msg_dma_attr; page_dma_attrs.dma_attr_sgllen = 1; page_dma_attrs.dma_attr_granular = (sizeof (MPI2_CONFIG_PAGE_MAN_5)); if (mptsas_dma_addr_create(mpt, page_dma_attrs, &page_dma_handle, &page_accessp, &page_memp, (sizeof (MPI2_CONFIG_PAGE_MAN_5)), &page_cookie) == FALSE) { rval = DDI_FAILURE; goto done; } /* Now safe to call mptsas_dma_addr_destroy(page_dma_handle). */ free_page = B_TRUE; bzero(page_memp, sizeof (MPI2_CONFIG_PAGE_MAN_5)); m5 = (pMpi2ManufacturingPage5_t)page_memp; NDBG20(("mptsas_get_manufacture_page5: paddr 0x%p", (void *)(uintptr_t)page_cookie.dmac_laddress)); /* * Give reply address to IOC to store config page in and send * config request out. */ flagslength = sizeof (MPI2_CONFIG_PAGE_MAN_5); flagslength |= ((uint32_t)(MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_SIMPLE_ELEMENT | MPI2_SGE_FLAGS_SYSTEM_ADDRESS | MPI2_SGE_FLAGS_64_BIT_ADDRESSING | MPI2_SGE_FLAGS_IOC_TO_HOST | MPI2_SGE_FLAGS_END_OF_LIST) << MPI2_SGE_FLAGS_SHIFT); if (mptsas_send_config_request_msg(mpt, MPI2_CONFIG_ACTION_PAGE_READ_CURRENT, MPI2_CONFIG_PAGETYPE_MANUFACTURING, 0, 5, ddi_get8(recv_accessp, &configreply->Header.PageVersion), ddi_get8(recv_accessp, &configreply->Header.PageLength), flagslength, page_cookie.dmac_laddress)) { rval = DDI_FAILURE; goto done; } /* * get reply view handshake */ if (mptsas_get_handshake_msg(mpt, recv_memp, recv_numbytes, recv_accessp)) { rval = DDI_FAILURE; goto done; } if ((iocstatus = ddi_get16(recv_accessp, &configreply->IOCStatus)) != 0) { mptsas_log(mpt, CE_WARN, "mptsas_get_manufacture_page5 config: " "IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus, ddi_get32(recv_accessp, &configreply->IOCLogInfo)); goto done; } (void) ddi_dma_sync(page_dma_handle, 0, 0, DDI_DMA_SYNC_FORCPU); /* * Fusion-MPT stores fields in little-endian format. This is * why the low-order 32 bits are stored first. */ mpt->un.sasaddr.m_base_wwid_lo = ddi_get32(page_accessp, (uint32_t *)(void *)&m5->Phy[0].WWID); mpt->un.sasaddr.m_base_wwid_hi = ddi_get32(page_accessp, (uint32_t *)(void *)&m5->Phy[0].WWID + 1); if (ddi_prop_update_int64(DDI_DEV_T_NONE, mpt->m_dip, "base-wwid", mpt->un.m_base_wwid) != DDI_PROP_SUCCESS) { NDBG2(("%s%d: failed to create base-wwid property", ddi_driver_name(mpt->m_dip), ddi_get_instance(mpt->m_dip))); } /* * Set the number of PHYs present. */ mpt->m_num_phys = ddi_get8(page_accessp, (uint8_t *)&m5->NumPhys); if (ddi_prop_update_int(DDI_DEV_T_NONE, mpt->m_dip, "num-phys", mpt->m_num_phys) != DDI_PROP_SUCCESS) { NDBG2(("%s%d: failed to create num-phys property", ddi_driver_name(mpt->m_dip), ddi_get_instance(mpt->m_dip))); } mptsas_log(mpt, CE_NOTE, "!mpt%d: Initiator WWNs: 0x%016llx-0x%016llx", mpt->m_instance, (unsigned long long)mpt->un.m_base_wwid, (unsigned long long)mpt->un.m_base_wwid + mpt->m_num_phys - 1); if ((mptsas_check_dma_handle(recv_dma_handle) != DDI_SUCCESS) || (mptsas_check_dma_handle(page_dma_handle) != DDI_SUCCESS)) { ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_UNAFFECTED); rval = DDI_FAILURE; goto done; } if ((mptsas_check_acc_handle(recv_accessp) != DDI_SUCCESS) || (mptsas_check_acc_handle(page_accessp) != DDI_SUCCESS)) { ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_UNAFFECTED); rval = DDI_FAILURE; } done: /* * free up memory */ if (free_recv) mptsas_dma_addr_destroy(&recv_dma_handle, &recv_accessp); if (free_page) mptsas_dma_addr_destroy(&page_dma_handle, &page_accessp); MPTSAS_ENABLE_INTR(mpt); return (rval); } static int mptsas_sasphypage_0_cb(mptsas_t *mpt, caddr_t page_memp, ddi_acc_handle_t accessp, uint16_t iocstatus, uint32_t iocloginfo, va_list ap) { #ifndef __lock_lint _NOTE(ARGUNUSED(ap)) #endif pMpi2SasPhyPage0_t sasphypage; int rval = DDI_SUCCESS; uint16_t *owner_devhdl, *attached_devhdl; uint8_t *attached_phy_identify; uint32_t *attached_phy_info; uint8_t *programmed_link_rate; uint8_t *hw_link_rate; uint8_t *change_count; uint32_t *phy_info; uint8_t *negotiated_link_rate; uint32_t page_address; if ((iocstatus != MPI2_IOCSTATUS_SUCCESS) && (iocstatus != MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)) { mptsas_log(mpt, CE_WARN, "mptsas_get_sas_expander_page0 " "config: IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus, iocloginfo); rval = DDI_FAILURE; return (rval); } page_address = va_arg(ap, uint32_t); /* * The INVALID_PAGE status is normal if using GET_NEXT_HANDLE and there * are no more pages. If everything is OK up to this point but the * status is INVALID_PAGE, change rval to FAILURE and quit. Also, * signal that device traversal is complete. */ if (iocstatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) { if ((page_address & MPI2_SAS_EXPAND_PGAD_FORM_MASK) == MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL) { mpt->m_done_traverse_smp = 1; } rval = DDI_FAILURE; return (rval); } owner_devhdl = va_arg(ap, uint16_t *); attached_devhdl = va_arg(ap, uint16_t *); attached_phy_identify = va_arg(ap, uint8_t *); attached_phy_info = va_arg(ap, uint32_t *); programmed_link_rate = va_arg(ap, uint8_t *); hw_link_rate = va_arg(ap, uint8_t *); change_count = va_arg(ap, uint8_t *); phy_info = va_arg(ap, uint32_t *); negotiated_link_rate = va_arg(ap, uint8_t *); sasphypage = (pMpi2SasPhyPage0_t)page_memp; *owner_devhdl = ddi_get16(accessp, &sasphypage->OwnerDevHandle); *attached_devhdl = ddi_get16(accessp, &sasphypage->AttachedDevHandle); *attached_phy_identify = ddi_get8(accessp, &sasphypage->AttachedPhyIdentifier); *attached_phy_info = ddi_get32(accessp, &sasphypage->AttachedPhyInfo); *programmed_link_rate = ddi_get8(accessp, &sasphypage->ProgrammedLinkRate); *hw_link_rate = ddi_get8(accessp, &sasphypage->HwLinkRate); *change_count = ddi_get8(accessp, &sasphypage->ChangeCount); *phy_info = ddi_get32(accessp, &sasphypage->PhyInfo); *negotiated_link_rate = ddi_get8(accessp, &sasphypage->NegotiatedLinkRate); return (rval); } /* * Request MPI configuration page SAS phy page 0 to get DevHandle, phymask * and SAS address. */ int mptsas_get_sas_phy_page0(mptsas_t *mpt, uint32_t page_address, smhba_info_t *info) { int rval = DDI_SUCCESS; ASSERT(mutex_owned(&mpt->m_mutex)); /* * Get the header and config page. reply contains the reply frame, * which holds status info for the request. */ rval = mptsas_access_config_page(mpt, MPI2_CONFIG_ACTION_PAGE_READ_CURRENT, MPI2_CONFIG_EXTPAGETYPE_SAS_PHY, 0, page_address, mptsas_sasphypage_0_cb, page_address, &info->owner_devhdl, &info->attached_devhdl, &info->attached_phy_identify, &info->attached_phy_info, &info->programmed_link_rate, &info->hw_link_rate, &info->change_count, &info->phy_info, &info->negotiated_link_rate); return (rval); } static int mptsas_sasphypage_1_cb(mptsas_t *mpt, caddr_t page_memp, ddi_acc_handle_t accessp, uint16_t iocstatus, uint32_t iocloginfo, va_list ap) { #ifndef __lock_lint _NOTE(ARGUNUSED(ap)) #endif pMpi2SasPhyPage1_t sasphypage; int rval = DDI_SUCCESS; uint32_t *invalid_dword_count; uint32_t *running_disparity_error_count; uint32_t *loss_of_dword_sync_count; uint32_t *phy_reset_problem_count; uint32_t page_address; if ((iocstatus != MPI2_IOCSTATUS_SUCCESS) && (iocstatus != MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)) { mptsas_log(mpt, CE_WARN, "mptsas_get_sas_expander_page1 " "config: IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus, iocloginfo); rval = DDI_FAILURE; return (rval); } page_address = va_arg(ap, uint32_t); /* * The INVALID_PAGE status is normal if using GET_NEXT_HANDLE and there * are no more pages. If everything is OK up to this point but the * status is INVALID_PAGE, change rval to FAILURE and quit. Also, * signal that device traversal is complete. */ if (iocstatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) { if ((page_address & MPI2_SAS_EXPAND_PGAD_FORM_MASK) == MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL) { mpt->m_done_traverse_smp = 1; } rval = DDI_FAILURE; return (rval); } invalid_dword_count = va_arg(ap, uint32_t *); running_disparity_error_count = va_arg(ap, uint32_t *); loss_of_dword_sync_count = va_arg(ap, uint32_t *); phy_reset_problem_count = va_arg(ap, uint32_t *); sasphypage = (pMpi2SasPhyPage1_t)page_memp; *invalid_dword_count = ddi_get32(accessp, &sasphypage->InvalidDwordCount); *running_disparity_error_count = ddi_get32(accessp, &sasphypage->RunningDisparityErrorCount); *loss_of_dword_sync_count = ddi_get32(accessp, &sasphypage->LossDwordSynchCount); *phy_reset_problem_count = ddi_get32(accessp, &sasphypage->PhyResetProblemCount); return (rval); } /* * Request MPI configuration page SAS phy page 0 to get DevHandle, phymask * and SAS address. */ int mptsas_get_sas_phy_page1(mptsas_t *mpt, uint32_t page_address, smhba_info_t *info) { int rval = DDI_SUCCESS; ASSERT(mutex_owned(&mpt->m_mutex)); /* * Get the header and config page. reply contains the reply frame, * which holds status info for the request. */ rval = mptsas_access_config_page(mpt, MPI2_CONFIG_ACTION_PAGE_READ_CURRENT, MPI2_CONFIG_EXTPAGETYPE_SAS_PHY, 1, page_address, mptsas_sasphypage_1_cb, page_address, &info->invalid_dword_count, &info->running_disparity_error_count, &info->loss_of_dword_sync_count, &info->phy_reset_problem_count); return (rval); } /* * mptsas_get_manufacture_page0 * * This function will retrieve the base * Chip name, Board Name,Board Trace number from the adapter. * Since this function is only called during the * initialization process, use handshaking. */ int mptsas_get_manufacture_page0(mptsas_t *mpt) { ddi_dma_attr_t recv_dma_attrs, page_dma_attrs; ddi_dma_cookie_t page_cookie; ddi_dma_handle_t recv_dma_handle, page_dma_handle; ddi_acc_handle_t recv_accessp, page_accessp; pMpi2ConfigReply_t configreply; caddr_t recv_memp, page_memp; int recv_numbytes; pMpi2ManufacturingPage0_t m0; uint32_t flagslength; int rval = DDI_SUCCESS; uint_t iocstatus; uint8_t i = 0; boolean_t free_recv = B_FALSE, free_page = B_FALSE; MPTSAS_DISABLE_INTR(mpt); if (mptsas_send_config_request_msg(mpt, MPI2_CONFIG_ACTION_PAGE_HEADER, MPI2_CONFIG_PAGETYPE_MANUFACTURING, 0, 0, 0, 0, 0, 0)) { rval = DDI_FAILURE; goto done; } /* * dynamically create a customized dma attribute structure * that describes the MPT's config reply page request structure. */ recv_dma_attrs = mpt->m_msg_dma_attr; recv_dma_attrs.dma_attr_sgllen = 1; recv_dma_attrs.dma_attr_granular = (sizeof (MPI2_CONFIG_REPLY)); if (mptsas_dma_addr_create(mpt, recv_dma_attrs, &recv_dma_handle, &recv_accessp, &recv_memp, (sizeof (MPI2_CONFIG_REPLY)), NULL) == FALSE) { rval = DDI_FAILURE; goto done; } /* Now safe to call mptsas_dma_addr_destroy(recv_dma_handle). */ free_recv = B_TRUE; bzero(recv_memp, sizeof (MPI2_CONFIG_REPLY)); configreply = (pMpi2ConfigReply_t)recv_memp; recv_numbytes = sizeof (MPI2_CONFIG_REPLY); /* * get config reply message */ if (mptsas_get_handshake_msg(mpt, recv_memp, recv_numbytes, recv_accessp)) { rval = DDI_FAILURE; goto done; } if ((iocstatus = ddi_get16(recv_accessp, &configreply->IOCStatus)) != 0) { mptsas_log(mpt, CE_WARN, "mptsas_get_manufacture_page5 update: " "IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus, ddi_get32(recv_accessp, &configreply->IOCLogInfo)); goto done; } /* * dynamically create a customized dma attribute structure * that describes the MPT's config page structure. */ page_dma_attrs = mpt->m_msg_dma_attr; page_dma_attrs.dma_attr_sgllen = 1; page_dma_attrs.dma_attr_granular = (sizeof (MPI2_CONFIG_PAGE_MAN_0)); if (mptsas_dma_addr_create(mpt, page_dma_attrs, &page_dma_handle, &page_accessp, &page_memp, (sizeof (MPI2_CONFIG_PAGE_MAN_0)), &page_cookie) == FALSE) { rval = DDI_FAILURE; goto done; } /* Now safe to call mptsas_dma_addr_destroy(page_dma_handle). */ free_page = B_TRUE; bzero(page_memp, sizeof (MPI2_CONFIG_PAGE_MAN_0)); m0 = (pMpi2ManufacturingPage0_t)page_memp; /* * Give reply address to IOC to store config page in and send * config request out. */ flagslength = sizeof (MPI2_CONFIG_PAGE_MAN_0); flagslength |= ((uint32_t)(MPI2_SGE_FLAGS_LAST_ELEMENT | MPI2_SGE_FLAGS_END_OF_BUFFER | MPI2_SGE_FLAGS_SIMPLE_ELEMENT | MPI2_SGE_FLAGS_SYSTEM_ADDRESS | MPI2_SGE_FLAGS_64_BIT_ADDRESSING | MPI2_SGE_FLAGS_IOC_TO_HOST | MPI2_SGE_FLAGS_END_OF_LIST) << MPI2_SGE_FLAGS_SHIFT); if (mptsas_send_config_request_msg(mpt, MPI2_CONFIG_ACTION_PAGE_READ_CURRENT, MPI2_CONFIG_PAGETYPE_MANUFACTURING, 0, 0, ddi_get8(recv_accessp, &configreply->Header.PageVersion), ddi_get8(recv_accessp, &configreply->Header.PageLength), flagslength, page_cookie.dmac_laddress)) { rval = DDI_FAILURE; goto done; } /* * get reply view handshake */ if (mptsas_get_handshake_msg(mpt, recv_memp, recv_numbytes, recv_accessp)) { rval = DDI_FAILURE; goto done; } if ((iocstatus = ddi_get16(recv_accessp, &configreply->IOCStatus)) != 0) { mptsas_log(mpt, CE_WARN, "mptsas_get_manufacture_page0 config: " "IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus, ddi_get32(recv_accessp, &configreply->IOCLogInfo)); goto done; } (void) ddi_dma_sync(page_dma_handle, 0, 0, DDI_DMA_SYNC_FORCPU); /* * Fusion-MPT stores fields in little-endian format. This is * why the low-order 32 bits are stored first. */ for (i = 0; i < 16; i++) { mpt->m_MANU_page0.ChipName[i] = ddi_get8(page_accessp, (uint8_t *)(void *)&m0->ChipName[i]); } for (i = 0; i < 8; i++) { mpt->m_MANU_page0.ChipRevision[i] = ddi_get8(page_accessp, (uint8_t *)(void *)&m0->ChipRevision[i]); } for (i = 0; i < 16; i++) { mpt->m_MANU_page0.BoardName[i] = ddi_get8(page_accessp, (uint8_t *)(void *)&m0->BoardName[i]); } for (i = 0; i < 16; i++) { mpt->m_MANU_page0.BoardAssembly[i] = ddi_get8(page_accessp, (uint8_t *)(void *)&m0->BoardAssembly[i]); } for (i = 0; i < 16; i++) { mpt->m_MANU_page0.BoardTracerNumber[i] = ddi_get8(page_accessp, (uint8_t *)(void *)&m0->BoardTracerNumber[i]); } if ((mptsas_check_dma_handle(recv_dma_handle) != DDI_SUCCESS) || (mptsas_check_dma_handle(page_dma_handle) != DDI_SUCCESS)) { ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_UNAFFECTED); rval = DDI_FAILURE; goto done; } if ((mptsas_check_acc_handle(recv_accessp) != DDI_SUCCESS) || (mptsas_check_acc_handle(page_accessp) != DDI_SUCCESS)) { ddi_fm_service_impact(mpt->m_dip, DDI_SERVICE_UNAFFECTED); rval = DDI_FAILURE; } done: /* * free up memory */ if (free_recv) mptsas_dma_addr_destroy(&recv_dma_handle, &recv_accessp); if (free_page) mptsas_dma_addr_destroy(&page_dma_handle, &page_accessp); MPTSAS_ENABLE_INTR(mpt); return (rval); } static int mptsas_enclosurepage_0_cb(mptsas_t *mpt, caddr_t page_memp, ddi_acc_handle_t accessp, uint16_t iocstatus, uint32_t iocloginfo, va_list ap) { uint32_t page_address; pMpi2SasEnclosurePage0_t encpage, encout; if ((iocstatus != MPI2_IOCSTATUS_SUCCESS) && (iocstatus != MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)) { mptsas_log(mpt, CE_WARN, "mptsas_get_enclsourepage0 " "header: IOCStatus=0x%x, IOCLogInfo=0x%x", iocstatus, iocloginfo); return (DDI_FAILURE); } page_address = va_arg(ap, uint32_t); encout = va_arg(ap, pMpi2SasEnclosurePage0_t); encpage = (pMpi2SasEnclosurePage0_t)page_memp; /* * The INVALID_PAGE status is normal if using GET_NEXT_HANDLE and there * are no more pages. If everything is OK up to this point but the * status is INVALID_PAGE, change rval to FAILURE and quit. Also, * signal that enclosure traversal is complete. */ if (iocstatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) { if ((page_address & MPI2_SAS_DEVICE_PGAD_FORM_MASK) == MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE) { mpt->m_done_traverse_enc = 1; } return (DDI_FAILURE); } encout->Header.PageVersion = ddi_get8(accessp, &encpage->Header.PageVersion); encout->Header.PageNumber = ddi_get8(accessp, &encpage->Header.PageNumber); encout->Header.PageType = ddi_get8(accessp, &encpage->Header.PageType); encout->Header.ExtPageLength = ddi_get16(accessp, &encpage->Header.ExtPageLength); encout->Header.ExtPageType = ddi_get8(accessp, &encpage->Header.ExtPageType); encout->EnclosureLogicalID.Low = ddi_get32(accessp, &encpage->EnclosureLogicalID.Low); encout->EnclosureLogicalID.High = ddi_get32(accessp, &encpage->EnclosureLogicalID.High); encout->Flags = ddi_get16(accessp, &encpage->Flags); encout->EnclosureHandle = ddi_get16(accessp, &encpage->EnclosureHandle); encout->NumSlots = ddi_get16(accessp, &encpage->NumSlots); encout->StartSlot = ddi_get16(accessp, &encpage->StartSlot); encout->EnclosureLevel = ddi_get8(accessp, &encpage->EnclosureLevel); encout->SEPDevHandle = ddi_get16(accessp, &encpage->SEPDevHandle); return (DDI_SUCCESS); } /* * Request information about the SES enclosures. */ int mptsas_get_enclosure_page0(mptsas_t *mpt, uint32_t page_address, mptsas_enclosure_t *mep) { int rval = DDI_SUCCESS; Mpi2SasEnclosurePage0_t encpage; ASSERT(MUTEX_HELD(&mpt->m_mutex)); bzero(&encpage, sizeof (encpage)); rval = mptsas_access_config_page(mpt, MPI2_CONFIG_ACTION_PAGE_READ_CURRENT, MPI2_CONFIG_EXTPAGETYPE_ENCLOSURE, 0, page_address, mptsas_enclosurepage_0_cb, page_address, &encpage); if (rval == DDI_SUCCESS) { mep->me_enchdl = encpage.EnclosureHandle; mep->me_flags = encpage.Flags; mep->me_nslots = encpage.NumSlots; mep->me_fslot = encpage.StartSlot; mep->me_slotleds = NULL; } return (rval); }