/* * mr_sas_tbolt.c: source for mr_sas driver for New Generation. * i.e. Thunderbolt and Invader * * Solaris MegaRAID device driver for SAS2.0 controllers * Copyright (c) 2008-2012, LSI Logic Corporation. * All rights reserved. * * Version: * Author: * Swaminathan K S * Arun Chandrashekhar * Manju R * Rasheed * Shakeel Bukhari */ /* * Copyright 2018 Nexenta Systems, Inc. * Copyright 2015, 2017 Citrus IT Limited. All rights reserved. * Copyright 2015 Garrett D'Amore */ #include #include #include #include #include #include #include "ld_pd_map.h" #include "mr_sas.h" #include "fusion.h" /* * FMA header files */ #include #include #include #include /* Pre-TB command size and TB command size. */ #define MR_COMMAND_SIZE (64*20) /* 1280 bytes */ MR_LD_RAID *MR_LdRaidGet(U32 ld, MR_FW_RAID_MAP_ALL *map); U16 MR_TargetIdToLdGet(U32 ldTgtId, MR_FW_RAID_MAP_ALL *map); U16 MR_GetLDTgtId(U32 ld, MR_FW_RAID_MAP_ALL *map); U16 get_updated_dev_handle(PLD_LOAD_BALANCE_INFO, struct IO_REQUEST_INFO *); extern ddi_dma_attr_t mrsas_generic_dma_attr; extern uint32_t mrsas_tbolt_max_cap_maxxfer; extern struct ddi_device_acc_attr endian_attr; extern int debug_level_g; extern unsigned int enable_fp; volatile int dump_io_wait_time = 900; extern volatile int debug_timeout_g; extern int mrsas_issue_pending_cmds(struct mrsas_instance *); extern int mrsas_complete_pending_cmds(struct mrsas_instance *instance); extern void push_pending_mfi_pkt(struct mrsas_instance *, struct mrsas_cmd *); extern U8 MR_BuildRaidContext(struct mrsas_instance *, struct IO_REQUEST_INFO *, MPI2_SCSI_IO_VENDOR_UNIQUE *, MR_FW_RAID_MAP_ALL *); /* Local static prototypes. */ static struct mrsas_cmd *mrsas_tbolt_build_cmd(struct mrsas_instance *, struct scsi_address *, struct scsi_pkt *, uchar_t *); static void mrsas_tbolt_set_pd_lba(U8 *, size_t, uint8_t *, U64, U32); static int mrsas_tbolt_check_map_info(struct mrsas_instance *); static int mrsas_tbolt_sync_map_info(struct mrsas_instance *); static int mrsas_tbolt_prepare_pkt(struct scsa_cmd *); static int mrsas_tbolt_ioc_init(struct mrsas_instance *, dma_obj_t *); static void mrsas_tbolt_get_pd_info(struct mrsas_instance *, struct mrsas_tbolt_pd_info *, int); static int mrsas_debug_tbolt_fw_faults_after_ocr = 0; /* * destroy_mfi_mpi_frame_pool */ void destroy_mfi_mpi_frame_pool(struct mrsas_instance *instance) { int i; struct mrsas_cmd *cmd; /* return all mfi frames to pool */ for (i = 0; i < MRSAS_APP_RESERVED_CMDS; i++) { cmd = instance->cmd_list[i]; if (cmd->frame_dma_obj_status == DMA_OBJ_ALLOCATED) { (void) mrsas_free_dma_obj(instance, cmd->frame_dma_obj); } cmd->frame_dma_obj_status = DMA_OBJ_FREED; } } /* * destroy_mpi2_frame_pool */ void destroy_mpi2_frame_pool(struct mrsas_instance *instance) { if (instance->mpi2_frame_pool_dma_obj.status == DMA_OBJ_ALLOCATED) { (void) mrsas_free_dma_obj(instance, instance->mpi2_frame_pool_dma_obj); instance->mpi2_frame_pool_dma_obj.status |= DMA_OBJ_FREED; } } /* * mrsas_tbolt_free_additional_dma_buffer */ void mrsas_tbolt_free_additional_dma_buffer(struct mrsas_instance *instance) { int i; if (instance->mfi_internal_dma_obj.status == DMA_OBJ_ALLOCATED) { (void) mrsas_free_dma_obj(instance, instance->mfi_internal_dma_obj); instance->mfi_internal_dma_obj.status = DMA_OBJ_FREED; } if (instance->mfi_evt_detail_obj.status == DMA_OBJ_ALLOCATED) { (void) mrsas_free_dma_obj(instance, instance->mfi_evt_detail_obj); instance->mfi_evt_detail_obj.status = DMA_OBJ_FREED; } for (i = 0; i < 2; i++) { if (instance->ld_map_obj[i].status == DMA_OBJ_ALLOCATED) { (void) mrsas_free_dma_obj(instance, instance->ld_map_obj[i]); instance->ld_map_obj[i].status = DMA_OBJ_FREED; } } } /* * free_req_desc_pool */ void free_req_rep_desc_pool(struct mrsas_instance *instance) { if (instance->request_desc_dma_obj.status == DMA_OBJ_ALLOCATED) { (void) mrsas_free_dma_obj(instance, instance->request_desc_dma_obj); instance->request_desc_dma_obj.status = DMA_OBJ_FREED; } if (instance->reply_desc_dma_obj.status == DMA_OBJ_ALLOCATED) { (void) mrsas_free_dma_obj(instance, instance->reply_desc_dma_obj); instance->reply_desc_dma_obj.status = DMA_OBJ_FREED; } } /* * ThunderBolt(TB) Request Message Frame Pool */ int create_mpi2_frame_pool(struct mrsas_instance *instance) { int i = 0; uint16_t max_cmd; uint32_t sgl_sz; uint32_t raid_msg_size; uint32_t total_size; uint32_t offset; uint32_t io_req_base_phys; uint8_t *io_req_base; struct mrsas_cmd *cmd; max_cmd = instance->max_fw_cmds; sgl_sz = 1024; raid_msg_size = MRSAS_THUNDERBOLT_MSG_SIZE; /* Allocating additional 256 bytes to accomodate SMID 0. */ total_size = MRSAS_THUNDERBOLT_MSG_SIZE + (max_cmd * raid_msg_size) + (max_cmd * sgl_sz) + (max_cmd * SENSE_LENGTH); con_log(CL_ANN1, (CE_NOTE, "create_mpi2_frame_pool: " "max_cmd %x", max_cmd)); con_log(CL_DLEVEL3, (CE_NOTE, "create_mpi2_frame_pool: " "request message frame pool size %x", total_size)); /* * ThunderBolt(TB) We need to create a single chunk of DMA'ble memory * and then split the memory to 1024 commands. Each command should be * able to contain a RAID MESSAGE FRAME which will embed a MFI_FRAME * within it. Further refer the "alloc_req_rep_desc" function where * we allocate request/reply descriptors queues for a clue. */ instance->mpi2_frame_pool_dma_obj.size = total_size; instance->mpi2_frame_pool_dma_obj.dma_attr = mrsas_generic_dma_attr; instance->mpi2_frame_pool_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU; instance->mpi2_frame_pool_dma_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU; instance->mpi2_frame_pool_dma_obj.dma_attr.dma_attr_sgllen = 1; instance->mpi2_frame_pool_dma_obj.dma_attr.dma_attr_align = 256; if (mrsas_alloc_dma_obj(instance, &instance->mpi2_frame_pool_dma_obj, (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) { dev_err(instance->dip, CE_WARN, "could not alloc mpi2 frame pool"); return (DDI_FAILURE); } bzero(instance->mpi2_frame_pool_dma_obj.buffer, total_size); instance->mpi2_frame_pool_dma_obj.status |= DMA_OBJ_ALLOCATED; instance->io_request_frames = (uint8_t *)instance->mpi2_frame_pool_dma_obj.buffer; instance->io_request_frames_phy = (uint32_t) instance->mpi2_frame_pool_dma_obj.dma_cookie[0].dmac_address; con_log(CL_DLEVEL3, (CE_NOTE, "io_request_frames 0x%p", (void *)instance->io_request_frames)); con_log(CL_DLEVEL3, (CE_NOTE, "io_request_frames_phy 0x%x", instance->io_request_frames_phy)); io_req_base = (uint8_t *)instance->io_request_frames + MRSAS_THUNDERBOLT_MSG_SIZE; io_req_base_phys = instance->io_request_frames_phy + MRSAS_THUNDERBOLT_MSG_SIZE; con_log(CL_DLEVEL3, (CE_NOTE, "io req_base_phys 0x%x", io_req_base_phys)); for (i = 0; i < max_cmd; i++) { cmd = instance->cmd_list[i]; offset = i * MRSAS_THUNDERBOLT_MSG_SIZE; cmd->scsi_io_request = (Mpi2RaidSCSIIORequest_t *) ((uint8_t *)io_req_base + offset); cmd->scsi_io_request_phys_addr = io_req_base_phys + offset; cmd->sgl = (Mpi2SGEIOUnion_t *)((uint8_t *)io_req_base + (max_cmd * raid_msg_size) + i * sgl_sz); cmd->sgl_phys_addr = (io_req_base_phys + (max_cmd * raid_msg_size) + i * sgl_sz); cmd->sense1 = (uint8_t *)((uint8_t *)io_req_base + (max_cmd * raid_msg_size) + (max_cmd * sgl_sz) + (i * SENSE_LENGTH)); cmd->sense_phys_addr1 = (io_req_base_phys + (max_cmd * raid_msg_size) + (max_cmd * sgl_sz) + (i * SENSE_LENGTH)); cmd->SMID = i + 1; con_log(CL_DLEVEL3, (CE_NOTE, "Frame Pool Addr [%x]0x%p", cmd->index, (void *)cmd->scsi_io_request)); con_log(CL_DLEVEL3, (CE_NOTE, "Frame Pool Phys Addr [%x]0x%x", cmd->index, cmd->scsi_io_request_phys_addr)); con_log(CL_DLEVEL3, (CE_NOTE, "Sense Addr [%x]0x%p", cmd->index, (void *)cmd->sense1)); con_log(CL_DLEVEL3, (CE_NOTE, "Sense Addr Phys [%x]0x%x", cmd->index, cmd->sense_phys_addr1)); con_log(CL_DLEVEL3, (CE_NOTE, "Sgl bufffers [%x]0x%p", cmd->index, (void *)cmd->sgl)); con_log(CL_DLEVEL3, (CE_NOTE, "Sgl bufffers phys [%x]0x%x", cmd->index, cmd->sgl_phys_addr)); } return (DDI_SUCCESS); } /* * alloc_additional_dma_buffer for AEN */ int mrsas_tbolt_alloc_additional_dma_buffer(struct mrsas_instance *instance) { uint32_t internal_buf_size = PAGESIZE*2; int i; /* Initialize buffer status as free */ instance->mfi_internal_dma_obj.status = DMA_OBJ_FREED; instance->mfi_evt_detail_obj.status = DMA_OBJ_FREED; instance->ld_map_obj[0].status = DMA_OBJ_FREED; instance->ld_map_obj[1].status = DMA_OBJ_FREED; instance->mfi_internal_dma_obj.size = internal_buf_size; instance->mfi_internal_dma_obj.dma_attr = mrsas_generic_dma_attr; instance->mfi_internal_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU; instance->mfi_internal_dma_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU; instance->mfi_internal_dma_obj.dma_attr.dma_attr_sgllen = 1; if (mrsas_alloc_dma_obj(instance, &instance->mfi_internal_dma_obj, (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) { dev_err(instance->dip, CE_WARN, "could not alloc reply queue"); return (DDI_FAILURE); } bzero(instance->mfi_internal_dma_obj.buffer, internal_buf_size); instance->mfi_internal_dma_obj.status |= DMA_OBJ_ALLOCATED; instance->internal_buf = (caddr_t)(((unsigned long)instance->mfi_internal_dma_obj.buffer)); instance->internal_buf_dmac_add = instance->mfi_internal_dma_obj.dma_cookie[0].dmac_address; instance->internal_buf_size = internal_buf_size; /* allocate evt_detail */ instance->mfi_evt_detail_obj.size = sizeof (struct mrsas_evt_detail); instance->mfi_evt_detail_obj.dma_attr = mrsas_generic_dma_attr; instance->mfi_evt_detail_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU; instance->mfi_evt_detail_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU; instance->mfi_evt_detail_obj.dma_attr.dma_attr_sgllen = 1; instance->mfi_evt_detail_obj.dma_attr.dma_attr_align = 8; if (mrsas_alloc_dma_obj(instance, &instance->mfi_evt_detail_obj, (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) { dev_err(instance->dip, CE_WARN, "mrsas_tbolt_alloc_additional_dma_buffer: " "could not allocate data transfer buffer."); goto fail_tbolt_additional_buff; } bzero(instance->mfi_evt_detail_obj.buffer, sizeof (struct mrsas_evt_detail)); instance->mfi_evt_detail_obj.status |= DMA_OBJ_ALLOCATED; instance->size_map_info = sizeof (MR_FW_RAID_MAP) + (sizeof (MR_LD_SPAN_MAP) * (MAX_LOGICAL_DRIVES - 1)); for (i = 0; i < 2; i++) { /* allocate the data transfer buffer */ instance->ld_map_obj[i].size = instance->size_map_info; instance->ld_map_obj[i].dma_attr = mrsas_generic_dma_attr; instance->ld_map_obj[i].dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU; instance->ld_map_obj[i].dma_attr.dma_attr_count_max = 0xFFFFFFFFU; instance->ld_map_obj[i].dma_attr.dma_attr_sgllen = 1; instance->ld_map_obj[i].dma_attr.dma_attr_align = 1; if (mrsas_alloc_dma_obj(instance, &instance->ld_map_obj[i], (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) { dev_err(instance->dip, CE_WARN, "could not allocate data transfer buffer."); goto fail_tbolt_additional_buff; } instance->ld_map_obj[i].status |= DMA_OBJ_ALLOCATED; bzero(instance->ld_map_obj[i].buffer, instance->size_map_info); instance->ld_map[i] = (MR_FW_RAID_MAP_ALL *)instance->ld_map_obj[i].buffer; instance->ld_map_phy[i] = (uint32_t)instance-> ld_map_obj[i].dma_cookie[0].dmac_address; con_log(CL_DLEVEL3, (CE_NOTE, "ld_map Addr Phys 0x%x", instance->ld_map_phy[i])); con_log(CL_DLEVEL3, (CE_NOTE, "size_map_info 0x%x", instance->size_map_info)); } return (DDI_SUCCESS); fail_tbolt_additional_buff: mrsas_tbolt_free_additional_dma_buffer(instance); return (DDI_FAILURE); } MRSAS_REQUEST_DESCRIPTOR_UNION * mr_sas_get_request_descriptor(struct mrsas_instance *instance, uint16_t index) { MRSAS_REQUEST_DESCRIPTOR_UNION *req_desc; if (index > instance->max_fw_cmds) { con_log(CL_ANN1, (CE_NOTE, "Invalid SMID 0x%x request for descriptor", index)); con_log(CL_ANN1, (CE_NOTE, "max_fw_cmds : 0x%x", instance->max_fw_cmds)); return (NULL); } req_desc = (MRSAS_REQUEST_DESCRIPTOR_UNION *) ((char *)instance->request_message_pool + (sizeof (MRSAS_REQUEST_DESCRIPTOR_UNION) * index)); con_log(CL_ANN1, (CE_NOTE, "request descriptor : 0x%08lx", (unsigned long)req_desc)); con_log(CL_ANN1, (CE_NOTE, "request descriptor base phy : 0x%08lx", (unsigned long)instance->request_message_pool_phy)); return ((MRSAS_REQUEST_DESCRIPTOR_UNION *)req_desc); } /* * Allocate Request and Reply Queue Descriptors. */ int alloc_req_rep_desc(struct mrsas_instance *instance) { uint32_t request_q_sz, reply_q_sz; int i, max_reply_q_sz; MPI2_REPLY_DESCRIPTORS_UNION *reply_desc; /* * ThunderBolt(TB) There's no longer producer consumer mechanism. * Once we have an interrupt we are supposed to scan through the list of * reply descriptors and process them accordingly. We would be needing * to allocate memory for 1024 reply descriptors */ /* Allocate Reply Descriptors */ con_log(CL_ANN1, (CE_NOTE, " reply q desc len = %x", (uint_t)sizeof (MPI2_REPLY_DESCRIPTORS_UNION))); /* reply queue size should be multiple of 16 */ max_reply_q_sz = ((instance->max_fw_cmds + 1 + 15)/16)*16; reply_q_sz = 8 * max_reply_q_sz; con_log(CL_ANN1, (CE_NOTE, " reply q desc len = %x", (uint_t)sizeof (MPI2_REPLY_DESCRIPTORS_UNION))); instance->reply_desc_dma_obj.size = reply_q_sz; instance->reply_desc_dma_obj.dma_attr = mrsas_generic_dma_attr; instance->reply_desc_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU; instance->reply_desc_dma_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU; instance->reply_desc_dma_obj.dma_attr.dma_attr_sgllen = 1; instance->reply_desc_dma_obj.dma_attr.dma_attr_align = 16; if (mrsas_alloc_dma_obj(instance, &instance->reply_desc_dma_obj, (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) { dev_err(instance->dip, CE_WARN, "could not alloc reply queue"); return (DDI_FAILURE); } bzero(instance->reply_desc_dma_obj.buffer, reply_q_sz); instance->reply_desc_dma_obj.status |= DMA_OBJ_ALLOCATED; /* virtual address of reply queue */ instance->reply_frame_pool = (MPI2_REPLY_DESCRIPTORS_UNION *)( instance->reply_desc_dma_obj.buffer); instance->reply_q_depth = max_reply_q_sz; con_log(CL_ANN1, (CE_NOTE, "[reply queue depth]0x%x", instance->reply_q_depth)); con_log(CL_ANN1, (CE_NOTE, "[reply queue virt addr]0x%p", (void *)instance->reply_frame_pool)); /* initializing reply address to 0xFFFFFFFF */ reply_desc = instance->reply_frame_pool; for (i = 0; i < instance->reply_q_depth; i++) { reply_desc->Words = (uint64_t)~0; reply_desc++; } instance->reply_frame_pool_phy = (uint32_t)instance->reply_desc_dma_obj.dma_cookie[0].dmac_address; con_log(CL_ANN1, (CE_NOTE, "[reply queue phys addr]0x%x", instance->reply_frame_pool_phy)); instance->reply_pool_limit_phy = (instance->reply_frame_pool_phy + reply_q_sz); con_log(CL_ANN1, (CE_NOTE, "[reply pool limit phys addr]0x%x", instance->reply_pool_limit_phy)); con_log(CL_ANN1, (CE_NOTE, " request q desc len = %x", (int)sizeof (MRSAS_REQUEST_DESCRIPTOR_UNION))); /* Allocate Request Descriptors */ con_log(CL_ANN1, (CE_NOTE, " request q desc len = %x", (int)sizeof (MRSAS_REQUEST_DESCRIPTOR_UNION))); request_q_sz = 8 * (instance->max_fw_cmds); instance->request_desc_dma_obj.size = request_q_sz; instance->request_desc_dma_obj.dma_attr = mrsas_generic_dma_attr; instance->request_desc_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU; instance->request_desc_dma_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU; instance->request_desc_dma_obj.dma_attr.dma_attr_sgllen = 1; instance->request_desc_dma_obj.dma_attr.dma_attr_align = 16; if (mrsas_alloc_dma_obj(instance, &instance->request_desc_dma_obj, (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) { dev_err(instance->dip, CE_WARN, "could not alloc request queue desc"); goto fail_undo_reply_queue; } bzero(instance->request_desc_dma_obj.buffer, request_q_sz); instance->request_desc_dma_obj.status |= DMA_OBJ_ALLOCATED; /* virtual address of request queue desc */ instance->request_message_pool = (MRSAS_REQUEST_DESCRIPTOR_UNION *) (instance->request_desc_dma_obj.buffer); instance->request_message_pool_phy = (uint32_t)instance->request_desc_dma_obj.dma_cookie[0].dmac_address; return (DDI_SUCCESS); fail_undo_reply_queue: if (instance->reply_desc_dma_obj.status == DMA_OBJ_ALLOCATED) { (void) mrsas_free_dma_obj(instance, instance->reply_desc_dma_obj); instance->reply_desc_dma_obj.status = DMA_OBJ_FREED; } return (DDI_FAILURE); } /* * mrsas_alloc_cmd_pool_tbolt * * TODO: merge tbolt-specific code into mrsas_alloc_cmd_pool() to have single * routine */ int mrsas_alloc_cmd_pool_tbolt(struct mrsas_instance *instance) { int i; int count; uint32_t max_cmd; uint32_t reserve_cmd; size_t sz; struct mrsas_cmd *cmd; max_cmd = instance->max_fw_cmds; con_log(CL_ANN1, (CE_NOTE, "mrsas_alloc_cmd_pool: " "max_cmd %x", max_cmd)); sz = sizeof (struct mrsas_cmd *) * max_cmd; /* * instance->cmd_list is an array of struct mrsas_cmd pointers. * Allocate the dynamic array first and then allocate individual * commands. */ instance->cmd_list = kmem_zalloc(sz, KM_SLEEP); /* create a frame pool and assign one frame to each cmd */ for (count = 0; count < max_cmd; count++) { instance->cmd_list[count] = kmem_zalloc(sizeof (struct mrsas_cmd), KM_SLEEP); } /* add all the commands to command pool */ INIT_LIST_HEAD(&instance->cmd_pool_list); INIT_LIST_HEAD(&instance->cmd_pend_list); INIT_LIST_HEAD(&instance->cmd_app_pool_list); reserve_cmd = MRSAS_APP_RESERVED_CMDS; /* cmd index 0 reservered for IOC INIT */ for (i = 1; i < reserve_cmd; i++) { cmd = instance->cmd_list[i]; cmd->index = i; mlist_add_tail(&cmd->list, &instance->cmd_app_pool_list); } for (i = reserve_cmd; i < max_cmd; i++) { cmd = instance->cmd_list[i]; cmd->index = i; mlist_add_tail(&cmd->list, &instance->cmd_pool_list); } return (DDI_SUCCESS); mrsas_undo_cmds: if (count > 0) { /* free each cmd */ for (i = 0; i < count; i++) { if (instance->cmd_list[i] != NULL) { kmem_free(instance->cmd_list[i], sizeof (struct mrsas_cmd)); } instance->cmd_list[i] = NULL; } } mrsas_undo_cmd_list: if (instance->cmd_list != NULL) kmem_free(instance->cmd_list, sz); instance->cmd_list = NULL; return (DDI_FAILURE); } /* * free_space_for_mpi2 */ void free_space_for_mpi2(struct mrsas_instance *instance) { /* already freed */ if (instance->cmd_list == NULL) { return; } /* First free the additional DMA buffer */ mrsas_tbolt_free_additional_dma_buffer(instance); /* Free the request/reply descriptor pool */ free_req_rep_desc_pool(instance); /* Free the MPI message pool */ destroy_mpi2_frame_pool(instance); /* Free the MFI frame pool */ destroy_mfi_frame_pool(instance); /* Free all the commands in the cmd_list */ /* Free the cmd_list buffer itself */ mrsas_free_cmd_pool(instance); } /* * ThunderBolt(TB) memory allocations for commands/messages/frames. */ int alloc_space_for_mpi2(struct mrsas_instance *instance) { /* Allocate command pool (memory for cmd_list & individual commands) */ if (mrsas_alloc_cmd_pool_tbolt(instance)) { dev_err(instance->dip, CE_WARN, "Error creating cmd pool"); return (DDI_FAILURE); } /* Initialize single reply size and Message size */ instance->reply_size = MRSAS_THUNDERBOLT_REPLY_SIZE; instance->raid_io_msg_size = MRSAS_THUNDERBOLT_MSG_SIZE; instance->max_sge_in_main_msg = (MRSAS_THUNDERBOLT_MSG_SIZE - (sizeof (MPI2_RAID_SCSI_IO_REQUEST) - sizeof (MPI2_SGE_IO_UNION)))/ sizeof (MPI2_SGE_IO_UNION); instance->max_sge_in_chain = (MR_COMMAND_SIZE - MRSAS_THUNDERBOLT_MSG_SIZE) / sizeof (MPI2_SGE_IO_UNION); /* Reduce SG count by 1 to take care of group cmds feature in FW */ instance->max_num_sge = (instance->max_sge_in_main_msg + instance->max_sge_in_chain - 2); instance->chain_offset_mpt_msg = offsetof(MPI2_RAID_SCSI_IO_REQUEST, SGL) / 16; instance->chain_offset_io_req = (MRSAS_THUNDERBOLT_MSG_SIZE - sizeof (MPI2_SGE_IO_UNION)) / 16; instance->reply_read_index = 0; /* Allocate Request and Reply descriptors Array */ /* Make sure the buffer is aligned to 8 for req/rep descriptor Pool */ if (alloc_req_rep_desc(instance)) { dev_err(instance->dip, CE_WARN, "Error, allocating memory for descripter-pool"); goto mpi2_undo_cmd_pool; } con_log(CL_ANN1, (CE_NOTE, "[request message pool phys addr]0x%x", instance->request_message_pool_phy)); /* Allocate MFI Frame pool - for MPI-MFI passthru commands */ if (create_mfi_frame_pool(instance)) { dev_err(instance->dip, CE_WARN, "Error, allocating memory for MFI frame-pool"); goto mpi2_undo_descripter_pool; } /* Allocate MPI2 Message pool */ /* * Make sure the buffer is alligned to 256 for raid message packet * create a io request pool and assign one frame to each cmd */ if (create_mpi2_frame_pool(instance)) { dev_err(instance->dip, CE_WARN, "Error, allocating memory for MPI2 Message-pool"); goto mpi2_undo_mfi_frame_pool; } #ifdef DEBUG con_log(CL_ANN1, (CE_CONT, "[max_sge_in_main_msg]0x%x", instance->max_sge_in_main_msg)); con_log(CL_ANN1, (CE_CONT, "[max_sge_in_chain]0x%x", instance->max_sge_in_chain)); con_log(CL_ANN1, (CE_CONT, "[max_sge]0x%x", instance->max_num_sge)); con_log(CL_ANN1, (CE_CONT, "[chain_offset_mpt_msg]0x%x", instance->chain_offset_mpt_msg)); con_log(CL_ANN1, (CE_CONT, "[chain_offset_io_req]0x%x", instance->chain_offset_io_req)); #endif /* Allocate additional dma buffer */ if (mrsas_tbolt_alloc_additional_dma_buffer(instance)) { dev_err(instance->dip, CE_WARN, "Error, allocating tbolt additional DMA buffer"); goto mpi2_undo_message_pool; } return (DDI_SUCCESS); mpi2_undo_message_pool: destroy_mpi2_frame_pool(instance); mpi2_undo_mfi_frame_pool: destroy_mfi_frame_pool(instance); mpi2_undo_descripter_pool: free_req_rep_desc_pool(instance); mpi2_undo_cmd_pool: mrsas_free_cmd_pool(instance); return (DDI_FAILURE); } /* * mrsas_init_adapter_tbolt - Initialize fusion interface adapter. */ int mrsas_init_adapter_tbolt(struct mrsas_instance *instance) { /* * Reduce the max supported cmds by 1. This is to ensure that the * reply_q_sz (1 more than the max cmd that driver may send) * does not exceed max cmds that the FW can support */ if (instance->max_fw_cmds > 1008) { instance->max_fw_cmds = 1008; instance->max_fw_cmds = instance->max_fw_cmds-1; } con_log(CL_ANN, (CE_NOTE, "mrsas_init_adapter_tbolt: " "instance->max_fw_cmds 0x%X.", instance->max_fw_cmds)); /* create a pool of commands */ if (alloc_space_for_mpi2(instance) != DDI_SUCCESS) { dev_err(instance->dip, CE_WARN, "alloc_space_for_mpi2() failed."); return (DDI_FAILURE); } /* Send ioc init message */ /* NOTE: the issue_init call does FMA checking already. */ if (mrsas_issue_init_mpi2(instance) != DDI_SUCCESS) { dev_err(instance->dip, CE_WARN, "mrsas_issue_init_mpi2() failed."); goto fail_init_fusion; } instance->unroll.alloc_space_mpi2 = 1; con_log(CL_ANN, (CE_NOTE, "mrsas_init_adapter_tbolt: SUCCESSFUL")); return (DDI_SUCCESS); fail_init_fusion: free_space_for_mpi2(instance); return (DDI_FAILURE); } /* * init_mpi2 */ int mrsas_issue_init_mpi2(struct mrsas_instance *instance) { dma_obj_t init2_dma_obj; int ret_val = DDI_SUCCESS; /* allocate DMA buffer for IOC INIT message */ init2_dma_obj.size = sizeof (Mpi2IOCInitRequest_t); init2_dma_obj.dma_attr = mrsas_generic_dma_attr; init2_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU; init2_dma_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU; init2_dma_obj.dma_attr.dma_attr_sgllen = 1; init2_dma_obj.dma_attr.dma_attr_align = 256; if (mrsas_alloc_dma_obj(instance, &init2_dma_obj, (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) { dev_err(instance->dip, CE_WARN, "mr_sas_issue_init_mpi2 " "could not allocate data transfer buffer."); return (DDI_FAILURE); } (void) memset(init2_dma_obj.buffer, 2, sizeof (Mpi2IOCInitRequest_t)); con_log(CL_ANN1, (CE_NOTE, "mrsas_issue_init_mpi2 _phys adr: %x", init2_dma_obj.dma_cookie[0].dmac_address)); /* Initialize and send ioc init message */ ret_val = mrsas_tbolt_ioc_init(instance, &init2_dma_obj); if (ret_val == DDI_FAILURE) { con_log(CL_ANN1, (CE_WARN, "mrsas_issue_init_mpi2: Failed")); goto fail_init_mpi2; } /* free IOC init DMA buffer */ if (mrsas_free_dma_obj(instance, init2_dma_obj) != DDI_SUCCESS) { con_log(CL_ANN1, (CE_WARN, "mrsas_issue_init_mpi2: Free Failed")); return (DDI_FAILURE); } /* Get/Check and sync ld_map info */ instance->map_id = 0; if (mrsas_tbolt_check_map_info(instance) == DDI_SUCCESS) (void) mrsas_tbolt_sync_map_info(instance); /* No mrsas_cmd to send, so send NULL. */ if (mrsas_common_check(instance, NULL) != DDI_SUCCESS) goto fail_init_mpi2; con_log(CL_ANN, (CE_NOTE, "mrsas_issue_init_mpi2: SUCCESSFUL")); return (DDI_SUCCESS); fail_init_mpi2: (void) mrsas_free_dma_obj(instance, init2_dma_obj); return (DDI_FAILURE); } static int mrsas_tbolt_ioc_init(struct mrsas_instance *instance, dma_obj_t *mpi2_dma_obj) { int numbytes; uint16_t flags; struct mrsas_init_frame2 *mfiFrameInit2; struct mrsas_header *frame_hdr; Mpi2IOCInitRequest_t *init; struct mrsas_cmd *cmd = NULL; struct mrsas_drv_ver drv_ver_info; MRSAS_REQUEST_DESCRIPTOR_UNION req_desc; uint32_t timeout; con_log(CL_ANN, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); #ifdef DEBUG con_log(CL_ANN1, (CE_CONT, " mfiFrameInit2 len = %x\n", (int)sizeof (*mfiFrameInit2))); con_log(CL_ANN1, (CE_CONT, " MPI len = %x\n", (int)sizeof (*init))); con_log(CL_ANN1, (CE_CONT, " mfiFrameInit2 len = %x\n", (int)sizeof (struct mrsas_init_frame2))); con_log(CL_ANN1, (CE_CONT, " MPI len = %x\n", (int)sizeof (Mpi2IOCInitRequest_t))); #endif init = (Mpi2IOCInitRequest_t *)mpi2_dma_obj->buffer; numbytes = sizeof (*init); bzero(init, numbytes); ddi_put8(mpi2_dma_obj->acc_handle, &init->Function, MPI2_FUNCTION_IOC_INIT); ddi_put8(mpi2_dma_obj->acc_handle, &init->WhoInit, MPI2_WHOINIT_HOST_DRIVER); /* set MsgVersion and HeaderVersion host driver was built with */ ddi_put16(mpi2_dma_obj->acc_handle, &init->MsgVersion, MPI2_VERSION); ddi_put16(mpi2_dma_obj->acc_handle, &init->HeaderVersion, MPI2_HEADER_VERSION); ddi_put16(mpi2_dma_obj->acc_handle, &init->SystemRequestFrameSize, instance->raid_io_msg_size / 4); ddi_put16(mpi2_dma_obj->acc_handle, &init->ReplyFreeQueueDepth, 0); ddi_put16(mpi2_dma_obj->acc_handle, &init->ReplyDescriptorPostQueueDepth, instance->reply_q_depth); /* * These addresses are set using the DMA cookie addresses from when the * memory was allocated. Sense buffer hi address should be 0. * ddi_put32(accessp, &init->SenseBufferAddressHigh, 0); */ ddi_put32(mpi2_dma_obj->acc_handle, &init->SenseBufferAddressHigh, 0); ddi_put64(mpi2_dma_obj->acc_handle, (uint64_t *)&init->SystemRequestFrameBaseAddress, instance->io_request_frames_phy); ddi_put64(mpi2_dma_obj->acc_handle, &init->ReplyDescriptorPostQueueAddress, instance->reply_frame_pool_phy); ddi_put64(mpi2_dma_obj->acc_handle, &init->ReplyFreeQueueAddress, 0); cmd = instance->cmd_list[0]; if (cmd == NULL) { return (DDI_FAILURE); } cmd->retry_count_for_ocr = 0; cmd->pkt = NULL; cmd->drv_pkt_time = 0; mfiFrameInit2 = (struct mrsas_init_frame2 *)cmd->scsi_io_request; con_log(CL_ANN1, (CE_CONT, "[mfi vaddr]%p", (void *)mfiFrameInit2)); frame_hdr = &cmd->frame->hdr; ddi_put8(cmd->frame_dma_obj.acc_handle, &frame_hdr->cmd_status, MFI_CMD_STATUS_POLL_MODE); flags = ddi_get16(cmd->frame_dma_obj.acc_handle, &frame_hdr->flags); flags |= MFI_FRAME_DONT_POST_IN_REPLY_QUEUE; ddi_put16(cmd->frame_dma_obj.acc_handle, &frame_hdr->flags, flags); con_log(CL_ANN, (CE_CONT, "mrsas_tbolt_ioc_init: SMID:%x\n", cmd->SMID)); /* Init the MFI Header */ ddi_put8(instance->mpi2_frame_pool_dma_obj.acc_handle, &mfiFrameInit2->cmd, MFI_CMD_OP_INIT); con_log(CL_ANN1, (CE_CONT, "[CMD]%x", mfiFrameInit2->cmd)); ddi_put8(instance->mpi2_frame_pool_dma_obj.acc_handle, &mfiFrameInit2->cmd_status, MFI_STAT_INVALID_STATUS); con_log(CL_ANN1, (CE_CONT, "[Status]%x", mfiFrameInit2->cmd_status)); ddi_put32(instance->mpi2_frame_pool_dma_obj.acc_handle, &mfiFrameInit2->queue_info_new_phys_addr_lo, mpi2_dma_obj->dma_cookie[0].dmac_address); ddi_put32(instance->mpi2_frame_pool_dma_obj.acc_handle, &mfiFrameInit2->data_xfer_len, sizeof (Mpi2IOCInitRequest_t)); con_log(CL_ANN1, (CE_CONT, "[reply q desc addr]%x", (int)init->ReplyDescriptorPostQueueAddress)); /* fill driver version information */ fill_up_drv_ver(&drv_ver_info); /* allocate the driver version data transfer buffer */ instance->drv_ver_dma_obj.size = sizeof (drv_ver_info.drv_ver); instance->drv_ver_dma_obj.dma_attr = mrsas_generic_dma_attr; instance->drv_ver_dma_obj.dma_attr.dma_attr_addr_hi = 0xFFFFFFFFU; instance->drv_ver_dma_obj.dma_attr.dma_attr_count_max = 0xFFFFFFFFU; instance->drv_ver_dma_obj.dma_attr.dma_attr_sgllen = 1; instance->drv_ver_dma_obj.dma_attr.dma_attr_align = 1; if (mrsas_alloc_dma_obj(instance, &instance->drv_ver_dma_obj, (uchar_t)DDI_STRUCTURE_LE_ACC) != 1) { dev_err(instance->dip, CE_WARN, "fusion init: Could not allocate driver version buffer."); return (DDI_FAILURE); } /* copy driver version to dma buffer */ bzero(instance->drv_ver_dma_obj.buffer, sizeof (drv_ver_info.drv_ver)); ddi_rep_put8(cmd->frame_dma_obj.acc_handle, (uint8_t *)drv_ver_info.drv_ver, (uint8_t *)instance->drv_ver_dma_obj.buffer, sizeof (drv_ver_info.drv_ver), DDI_DEV_AUTOINCR); /* send driver version physical address to firmware */ ddi_put64(cmd->frame_dma_obj.acc_handle, &mfiFrameInit2->driverversion, instance->drv_ver_dma_obj.dma_cookie[0].dmac_address); con_log(CL_ANN1, (CE_CONT, "[MPIINIT2 frame Phys addr ]0x%x len = %x", mfiFrameInit2->queue_info_new_phys_addr_lo, (int)sizeof (Mpi2IOCInitRequest_t))); con_log(CL_ANN1, (CE_CONT, "[Length]%x", mfiFrameInit2->data_xfer_len)); con_log(CL_ANN1, (CE_CONT, "[MFI frame Phys Address]%x len = %x", cmd->scsi_io_request_phys_addr, (int)sizeof (struct mrsas_init_frame2))); /* disable interrupts before sending INIT2 frame */ instance->func_ptr->disable_intr(instance); req_desc.Words = cmd->scsi_io_request_phys_addr; req_desc.MFAIo.RequestFlags = (MPI2_REQ_DESCRIPT_FLAGS_MFA << MPI2_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); cmd->request_desc = &req_desc; /* issue the init frame */ mutex_enter(&instance->reg_write_mtx); WR_IB_LOW_QPORT((uint32_t)(req_desc.Words), instance); WR_IB_HIGH_QPORT((uint32_t)(req_desc.Words >> 32), instance); mutex_exit(&instance->reg_write_mtx); con_log(CL_ANN1, (CE_CONT, "[cmd = %d] ", frame_hdr->cmd)); con_log(CL_ANN1, (CE_CONT, "[cmd Status= %x] ", frame_hdr->cmd_status)); timeout = drv_usectohz(MFI_POLL_TIMEOUT_SECS * MICROSEC); do { if (ddi_get8(cmd->frame_dma_obj.acc_handle, &mfiFrameInit2->cmd_status) != MFI_CMD_STATUS_POLL_MODE) break; delay(1); timeout--; } while (timeout > 0); if (ddi_get8(instance->mpi2_frame_pool_dma_obj.acc_handle, &mfiFrameInit2->cmd_status) == 0) { con_log(CL_ANN, (CE_NOTE, "INIT2 Success")); } else { con_log(CL_ANN, (CE_WARN, "INIT2 Fail")); mrsas_dump_reply_desc(instance); goto fail_ioc_init; } mrsas_dump_reply_desc(instance); instance->unroll.verBuff = 1; con_log(CL_ANN, (CE_NOTE, "mrsas_tbolt_ioc_init: SUCCESSFUL")); return (DDI_SUCCESS); fail_ioc_init: (void) mrsas_free_dma_obj(instance, instance->drv_ver_dma_obj); return (DDI_FAILURE); } int wait_for_outstanding_poll_io(struct mrsas_instance *instance) { int i; uint32_t wait_time = dump_io_wait_time; for (i = 0; i < wait_time; i++) { /* * Check For Outstanding poll Commands * except ldsync command and aen command */ if (instance->fw_outstanding <= 2) { break; } drv_usecwait(MILLISEC); /* complete commands from reply queue */ (void) mr_sas_tbolt_process_outstanding_cmd(instance); } if (instance->fw_outstanding > 2) { return (1); } return (0); } /* * scsi_pkt handling * * Visible to the external world via the transport structure. */ int mrsas_tbolt_tran_start(struct scsi_address *ap, struct scsi_pkt *pkt) { struct mrsas_instance *instance = ADDR2MR(ap); struct scsa_cmd *acmd = PKT2CMD(pkt); struct mrsas_cmd *cmd = NULL; uchar_t cmd_done = 0; con_log(CL_DLEVEL1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__)); if (instance->deadadapter == 1) { dev_err(instance->dip, CE_WARN, "mrsas_tran_start:TBOLT return TRAN_FATAL_ERROR " "for IO, as the HBA doesnt take any more IOs"); if (pkt) { pkt->pkt_reason = CMD_DEV_GONE; pkt->pkt_statistics = STAT_DISCON; } return (TRAN_FATAL_ERROR); } if (instance->adapterresetinprogress) { con_log(CL_ANN, (CE_NOTE, "Reset flag set, " "returning mfi_pkt and setting TRAN_BUSY\n")); return (TRAN_BUSY); } (void) mrsas_tbolt_prepare_pkt(acmd); cmd = mrsas_tbolt_build_cmd(instance, ap, pkt, &cmd_done); /* * Check if the command is already completed by the mrsas_build_cmd() * routine. In which case the busy_flag would be clear and scb will be * NULL and appropriate reason provided in pkt_reason field */ if (cmd_done) { pkt->pkt_reason = CMD_CMPLT; pkt->pkt_scbp[0] = STATUS_GOOD; pkt->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET | STATE_SENT_CMD; if (((pkt->pkt_flags & FLAG_NOINTR) == 0) && pkt->pkt_comp) { (*pkt->pkt_comp)(pkt); } return (TRAN_ACCEPT); } if (cmd == NULL) { return (TRAN_BUSY); } if ((pkt->pkt_flags & FLAG_NOINTR) == 0) { if (instance->fw_outstanding > instance->max_fw_cmds) { dev_err(instance->dip, CE_WARN, "Command Queue Full... Returning BUSY"); DTRACE_PROBE2(tbolt_start_tran_err, uint16_t, instance->fw_outstanding, uint16_t, instance->max_fw_cmds); return_raid_msg_pkt(instance, cmd); return (TRAN_BUSY); } /* Synchronize the Cmd frame for the controller */ (void) ddi_dma_sync(cmd->frame_dma_obj.dma_handle, 0, 0, DDI_DMA_SYNC_FORDEV); con_log(CL_ANN, (CE_CONT, "tbolt_issue_cmd: SCSI CDB[0]=0x%x " "cmd->index:0x%x SMID 0x%x\n", pkt->pkt_cdbp[0], cmd->index, cmd->SMID)); instance->func_ptr->issue_cmd(cmd, instance); } else { instance->func_ptr->issue_cmd(cmd, instance); (void) wait_for_outstanding_poll_io(instance); (void) mrsas_common_check(instance, cmd); DTRACE_PROBE2(tbolt_start_nointr_done, uint8_t, cmd->frame->hdr.cmd, uint8_t, cmd->frame->hdr.cmd_status); } return (TRAN_ACCEPT); } /* * prepare the pkt: * the pkt may have been resubmitted or just reused so * initialize some fields and do some checks. */ static int mrsas_tbolt_prepare_pkt(struct scsa_cmd *acmd) { struct scsi_pkt *pkt = CMD2PKT(acmd); /* * Reinitialize some fields that need it; the packet may * have been resubmitted */ pkt->pkt_reason = CMD_CMPLT; pkt->pkt_state = 0; pkt->pkt_statistics = 0; pkt->pkt_resid = 0; /* * zero status byte. */ *(pkt->pkt_scbp) = 0; return (0); } int mr_sas_tbolt_build_sgl(struct mrsas_instance *instance, struct scsa_cmd *acmd, struct mrsas_cmd *cmd, Mpi2RaidSCSIIORequest_t *scsi_raid_io, uint32_t *datalen) { uint32_t MaxSGEs; int sg_to_process; uint32_t i, j; uint32_t numElements, endElement; Mpi25IeeeSgeChain64_t *ieeeChainElement = NULL; Mpi25IeeeSgeChain64_t *scsi_raid_io_sgl_ieee = NULL; ddi_acc_handle_t acc_handle = instance->mpi2_frame_pool_dma_obj.acc_handle; con_log(CL_ANN1, (CE_NOTE, "chkpnt: Building Chained SGL :%d", __LINE__)); /* Calulate SGE size in number of Words(32bit) */ /* Clear the datalen before updating it. */ *datalen = 0; MaxSGEs = instance->max_sge_in_main_msg; ddi_put16(acc_handle, &scsi_raid_io->SGLFlags, MPI2_SGE_FLAGS_64_BIT_ADDRESSING); /* set data transfer flag. */ if (acmd->cmd_flags & CFLAG_DMASEND) { ddi_put32(acc_handle, &scsi_raid_io->Control, MPI2_SCSIIO_CONTROL_WRITE); } else { ddi_put32(acc_handle, &scsi_raid_io->Control, MPI2_SCSIIO_CONTROL_READ); } numElements = acmd->cmd_cookiecnt; con_log(CL_DLEVEL1, (CE_NOTE, "[SGE Count]:%x", numElements)); if (numElements > instance->max_num_sge) { con_log(CL_ANN, (CE_NOTE, "[Max SGE Count Exceeded]:%x", numElements)); return (numElements); } ddi_put8(acc_handle, &scsi_raid_io->RaidContext.numSGE, (uint8_t)numElements); /* set end element in main message frame */ endElement = (numElements <= MaxSGEs) ? numElements : (MaxSGEs - 1); /* prepare the scatter-gather list for the firmware */ scsi_raid_io_sgl_ieee = (Mpi25IeeeSgeChain64_t *)&scsi_raid_io->SGL.IeeeChain; if (instance->gen3) { Mpi25IeeeSgeChain64_t *sgl_ptr_end = scsi_raid_io_sgl_ieee; sgl_ptr_end += instance->max_sge_in_main_msg - 1; ddi_put8(acc_handle, &sgl_ptr_end->Flags, 0); } for (i = 0; i < endElement; i++, scsi_raid_io_sgl_ieee++) { ddi_put64(acc_handle, &scsi_raid_io_sgl_ieee->Address, acmd->cmd_dmacookies[i].dmac_laddress); ddi_put32(acc_handle, &scsi_raid_io_sgl_ieee->Length, acmd->cmd_dmacookies[i].dmac_size); ddi_put8(acc_handle, &scsi_raid_io_sgl_ieee->Flags, 0); if (instance->gen3) { if (i == (numElements - 1)) { ddi_put8(acc_handle, &scsi_raid_io_sgl_ieee->Flags, IEEE_SGE_FLAGS_END_OF_LIST); } } *datalen += acmd->cmd_dmacookies[i].dmac_size; #ifdef DEBUG con_log(CL_DLEVEL1, (CE_NOTE, "[SGL Address]: %" PRIx64, scsi_raid_io_sgl_ieee->Address)); con_log(CL_DLEVEL1, (CE_NOTE, "[SGL Length]:%x", scsi_raid_io_sgl_ieee->Length)); con_log(CL_DLEVEL1, (CE_NOTE, "[SGL Flags]:%x", scsi_raid_io_sgl_ieee->Flags)); #endif } ddi_put8(acc_handle, &scsi_raid_io->ChainOffset, 0); /* check if chained SGL required */ if (i < numElements) { con_log(CL_ANN1, (CE_NOTE, "[Chain Element index]:%x", i)); if (instance->gen3) { uint16_t ioFlags = ddi_get16(acc_handle, &scsi_raid_io->IoFlags); if ((ioFlags & MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH) != MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH) { ddi_put8(acc_handle, &scsi_raid_io->ChainOffset, (U8)instance->chain_offset_io_req); } else { ddi_put8(acc_handle, &scsi_raid_io->ChainOffset, 0); } } else { ddi_put8(acc_handle, &scsi_raid_io->ChainOffset, (U8)instance->chain_offset_io_req); } /* prepare physical chain element */ ieeeChainElement = scsi_raid_io_sgl_ieee; ddi_put8(acc_handle, &ieeeChainElement->NextChainOffset, 0); if (instance->gen3) { ddi_put8(acc_handle, &ieeeChainElement->Flags, IEEE_SGE_FLAGS_CHAIN_ELEMENT); } else { ddi_put8(acc_handle, &ieeeChainElement->Flags, (IEEE_SGE_FLAGS_CHAIN_ELEMENT | MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR)); } ddi_put32(acc_handle, &ieeeChainElement->Length, (sizeof (MPI2_SGE_IO_UNION) * (numElements - i))); ddi_put64(acc_handle, &ieeeChainElement->Address, (U64)cmd->sgl_phys_addr); sg_to_process = numElements - i; con_log(CL_ANN1, (CE_NOTE, "[Additional SGE Count]:%x", endElement)); /* point to the chained SGL buffer */ scsi_raid_io_sgl_ieee = (Mpi25IeeeSgeChain64_t *)cmd->sgl; /* build rest of the SGL in chained buffer */ for (j = 0; j < sg_to_process; j++, scsi_raid_io_sgl_ieee++) { con_log(CL_DLEVEL3, (CE_NOTE, "[remaining SGL]:%x", i)); ddi_put64(acc_handle, &scsi_raid_io_sgl_ieee->Address, acmd->cmd_dmacookies[i].dmac_laddress); ddi_put32(acc_handle, &scsi_raid_io_sgl_ieee->Length, acmd->cmd_dmacookies[i].dmac_size); ddi_put8(acc_handle, &scsi_raid_io_sgl_ieee->Flags, 0); if (instance->gen3) { if (i == (numElements - 1)) { ddi_put8(acc_handle, &scsi_raid_io_sgl_ieee->Flags, IEEE_SGE_FLAGS_END_OF_LIST); } } *datalen += acmd->cmd_dmacookies[i].dmac_size; #if DEBUG con_log(CL_DLEVEL1, (CE_NOTE, "[SGL Address]: %" PRIx64, scsi_raid_io_sgl_ieee->Address)); con_log(CL_DLEVEL1, (CE_NOTE, "[SGL Length]:%x", scsi_raid_io_sgl_ieee->Length)); con_log(CL_DLEVEL1, (CE_NOTE, "[SGL Flags]:%x", scsi_raid_io_sgl_ieee->Flags)); #endif i++; } } return (0); } /*end of BuildScatterGather */ /* * build_cmd */ static struct mrsas_cmd * mrsas_tbolt_build_cmd(struct mrsas_instance *instance, struct scsi_address *ap, struct scsi_pkt *pkt, uchar_t *cmd_done) { uint8_t fp_possible = 0; uint32_t index; uint32_t lba_count = 0; uint32_t start_lba_hi = 0; uint32_t start_lba_lo = 0; ddi_acc_handle_t acc_handle = instance->mpi2_frame_pool_dma_obj.acc_handle; struct mrsas_cmd *cmd = NULL; struct scsa_cmd *acmd = PKT2CMD(pkt); MRSAS_REQUEST_DESCRIPTOR_UNION *ReqDescUnion; Mpi2RaidSCSIIORequest_t *scsi_raid_io; uint32_t datalen; struct IO_REQUEST_INFO io_info; MR_FW_RAID_MAP_ALL *local_map_ptr; uint16_t pd_cmd_cdblen; con_log(CL_DLEVEL1, (CE_NOTE, "chkpnt: Entered mrsas_tbolt_build_cmd:%d", __LINE__)); /* find out if this is logical or physical drive command. */ acmd->islogical = MRDRV_IS_LOGICAL(ap); acmd->device_id = MAP_DEVICE_ID(instance, ap); *cmd_done = 0; /* get the command packet */ if (!(cmd = get_raid_msg_pkt(instance))) { DTRACE_PROBE2(tbolt_build_cmd_mfi_err, uint16_t, instance->fw_outstanding, uint16_t, instance->max_fw_cmds); return (NULL); } index = cmd->index; ReqDescUnion = mr_sas_get_request_descriptor(instance, index); ReqDescUnion->Words = 0; ReqDescUnion->SCSIIO.SMID = cmd->SMID; ReqDescUnion->SCSIIO.RequestFlags = (MPI2_REQ_DESCRIPT_FLAGS_LD_IO << MPI2_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); cmd->request_desc = ReqDescUnion; cmd->pkt = pkt; cmd->cmd = acmd; DTRACE_PROBE4(tbolt_build_cmd, uint8_t, pkt->pkt_cdbp[0], ulong_t, acmd->cmd_dmacount, ulong_t, acmd->cmd_dma_len, uint16_t, acmd->device_id); /* lets get the command directions */ if (acmd->cmd_flags & CFLAG_DMASEND) { if (acmd->cmd_flags & CFLAG_CONSISTENT) { (void) ddi_dma_sync(acmd->cmd_dmahandle, acmd->cmd_dma_offset, acmd->cmd_dma_len, DDI_DMA_SYNC_FORDEV); } } else if (acmd->cmd_flags & ~CFLAG_DMASEND) { if (acmd->cmd_flags & CFLAG_CONSISTENT) { (void) ddi_dma_sync(acmd->cmd_dmahandle, acmd->cmd_dma_offset, acmd->cmd_dma_len, DDI_DMA_SYNC_FORCPU); } } else { con_log(CL_ANN, (CE_NOTE, "NO DMA")); } /* get SCSI_IO raid message frame pointer */ scsi_raid_io = (Mpi2RaidSCSIIORequest_t *)cmd->scsi_io_request; /* zero out SCSI_IO raid message frame */ bzero(scsi_raid_io, sizeof (Mpi2RaidSCSIIORequest_t)); /* Set the ldTargetId set by BuildRaidContext() */ ddi_put16(acc_handle, &scsi_raid_io->RaidContext.ldTargetId, acmd->device_id); /* Copy CDB to scsi_io_request message frame */ ddi_rep_put8(acc_handle, (uint8_t *)pkt->pkt_cdbp, (uint8_t *)scsi_raid_io->CDB.CDB32, acmd->cmd_cdblen, DDI_DEV_AUTOINCR); /* * Just the CDB length, rest of the Flags are zero * This will be modified later. */ ddi_put16(acc_handle, &scsi_raid_io->IoFlags, acmd->cmd_cdblen); pd_cmd_cdblen = acmd->cmd_cdblen; if (acmd->islogical) { switch (pkt->pkt_cdbp[0]) { case SCMD_READ: case SCMD_WRITE: case SCMD_READ_G1: case SCMD_WRITE_G1: case SCMD_READ_G4: case SCMD_WRITE_G4: case SCMD_READ_G5: case SCMD_WRITE_G5: /* Initialize sense Information */ if (cmd->sense1 == NULL) { con_log(CL_ANN, (CE_NOTE, "tbolt_build_cmd: " "Sense buffer ptr NULL ")); } bzero(cmd->sense1, SENSE_LENGTH); con_log(CL_DLEVEL2, (CE_NOTE, "tbolt_build_cmd " "CDB[0] = %x\n", pkt->pkt_cdbp[0])); if (acmd->cmd_cdblen == CDB_GROUP0) { /* 6-byte cdb */ lba_count = (uint16_t)(pkt->pkt_cdbp[4]); start_lba_lo = ((uint32_t)(pkt->pkt_cdbp[3]) | ((uint32_t)(pkt->pkt_cdbp[2]) << 8) | ((uint32_t)((pkt->pkt_cdbp[1]) & 0x1F) << 16)); } else if (acmd->cmd_cdblen == CDB_GROUP1) { /* 10-byte cdb */ lba_count = (((uint16_t)(pkt->pkt_cdbp[8])) | ((uint16_t)(pkt->pkt_cdbp[7]) << 8)); start_lba_lo = (((uint32_t)(pkt->pkt_cdbp[5])) | ((uint32_t)(pkt->pkt_cdbp[4]) << 8) | ((uint32_t)(pkt->pkt_cdbp[3]) << 16) | ((uint32_t)(pkt->pkt_cdbp[2]) << 24)); } else if (acmd->cmd_cdblen == CDB_GROUP5) { /* 12-byte cdb */ lba_count = ( ((uint32_t)(pkt->pkt_cdbp[9])) | ((uint32_t)(pkt->pkt_cdbp[8]) << 8) | ((uint32_t)(pkt->pkt_cdbp[7]) << 16) | ((uint32_t)(pkt->pkt_cdbp[6]) << 24)); start_lba_lo = (((uint32_t)(pkt->pkt_cdbp[5])) | ((uint32_t)(pkt->pkt_cdbp[4]) << 8) | ((uint32_t)(pkt->pkt_cdbp[3]) << 16) | ((uint32_t)(pkt->pkt_cdbp[2]) << 24)); } else if (acmd->cmd_cdblen == CDB_GROUP4) { /* 16-byte cdb */ lba_count = ( ((uint32_t)(pkt->pkt_cdbp[13])) | ((uint32_t)(pkt->pkt_cdbp[12]) << 8) | ((uint32_t)(pkt->pkt_cdbp[11]) << 16) | ((uint32_t)(pkt->pkt_cdbp[10]) << 24)); start_lba_lo = ( ((uint32_t)(pkt->pkt_cdbp[9])) | ((uint32_t)(pkt->pkt_cdbp[8]) << 8) | ((uint32_t)(pkt->pkt_cdbp[7]) << 16) | ((uint32_t)(pkt->pkt_cdbp[6]) << 24)); start_lba_hi = ( ((uint32_t)(pkt->pkt_cdbp[5])) | ((uint32_t)(pkt->pkt_cdbp[4]) << 8) | ((uint32_t)(pkt->pkt_cdbp[3]) << 16) | ((uint32_t)(pkt->pkt_cdbp[2]) << 24)); } if (instance->tbolt && ((lba_count * 512) > mrsas_tbolt_max_cap_maxxfer)) { dev_err(instance->dip, CE_WARN, "IO SECTOR COUNT exceeds " "controller limit 0x%x sectors", lba_count); } bzero(&io_info, sizeof (struct IO_REQUEST_INFO)); io_info.ldStartBlock = ((uint64_t)start_lba_hi << 32) | start_lba_lo; io_info.numBlocks = lba_count; io_info.ldTgtId = acmd->device_id; if (acmd->cmd_flags & CFLAG_DMASEND) io_info.isRead = 0; else io_info.isRead = 1; /* Acquire SYNC MAP UPDATE lock */ mutex_enter(&instance->sync_map_mtx); local_map_ptr = instance->ld_map[(instance->map_id & 1)]; if ((MR_TargetIdToLdGet( acmd->device_id, local_map_ptr) >= MAX_LOGICAL_DRIVES) || !instance->fast_path_io) { dev_err(instance->dip, CE_NOTE, "Fast Path NOT Possible, " "targetId >= MAX_LOGICAL_DRIVES || " "!instance->fast_path_io"); fp_possible = 0; /* Set Regionlock flags to BYPASS */ /* io_request->RaidContext.regLockFlags = 0; */ ddi_put8(acc_handle, &scsi_raid_io->RaidContext.regLockFlags, 0); } else { if (MR_BuildRaidContext(instance, &io_info, &scsi_raid_io->RaidContext, local_map_ptr)) fp_possible = io_info.fpOkForIo; } if (!enable_fp) fp_possible = 0; con_log(CL_ANN1, (CE_NOTE, "enable_fp %d " "instance->fast_path_io %d fp_possible %d", enable_fp, instance->fast_path_io, fp_possible)); if (fp_possible) { /* Check for DIF enabled LD */ if (MR_CheckDIF(acmd->device_id, local_map_ptr)) { /* Prepare 32 Byte CDB for DIF capable Disk */ mrsas_tbolt_prepare_cdb(instance, scsi_raid_io->CDB.CDB32, &io_info, scsi_raid_io, start_lba_lo); } else { mrsas_tbolt_set_pd_lba(scsi_raid_io->CDB.CDB32, sizeof (scsi_raid_io->CDB.CDB32), (uint8_t *)&pd_cmd_cdblen, io_info.pdBlock, io_info.numBlocks); ddi_put16(acc_handle, &scsi_raid_io->IoFlags, pd_cmd_cdblen); } ddi_put8(acc_handle, &scsi_raid_io->Function, MPI2_FUNCTION_SCSI_IO_REQUEST); ReqDescUnion->SCSIIO.RequestFlags = (MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY << MPI2_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); if (instance->gen3) { uint8_t regLockFlags = ddi_get8(acc_handle, &scsi_raid_io->RaidContext.regLockFlags); uint16_t IoFlags = ddi_get16(acc_handle, &scsi_raid_io->IoFlags); if (regLockFlags == REGION_TYPE_UNUSED) ReqDescUnion->SCSIIO.RequestFlags = (MPI2_REQ_DESCRIPT_FLAGS_NO_LOCK << MPI2_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); IoFlags |= MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH; regLockFlags |= (MR_RL_FLAGS_GRANT_DESTINATION_CUDA | MR_RL_FLAGS_SEQ_NUM_ENABLE); ddi_put8(acc_handle, &scsi_raid_io->ChainOffset, 0); ddi_put8(acc_handle, &scsi_raid_io->RaidContext.nsegType, ((0x01 << MPI2_NSEG_FLAGS_SHIFT) | MPI2_TYPE_CUDA)); ddi_put8(acc_handle, &scsi_raid_io->RaidContext.regLockFlags, regLockFlags); ddi_put16(acc_handle, &scsi_raid_io->IoFlags, IoFlags); } if ((instance->load_balance_info[ acmd->device_id].loadBalanceFlag) && (io_info.isRead)) { io_info.devHandle = get_updated_dev_handle(&instance-> load_balance_info[acmd->device_id], &io_info); cmd->load_balance_flag |= MEGASAS_LOAD_BALANCE_FLAG; } else { cmd->load_balance_flag &= ~MEGASAS_LOAD_BALANCE_FLAG; } ReqDescUnion->SCSIIO.DevHandle = io_info.devHandle; ddi_put16(acc_handle, &scsi_raid_io->DevHandle, io_info.devHandle); } else { /* FP Not Possible */ ddi_put8(acc_handle, &scsi_raid_io->Function, MPI2_FUNCTION_LD_IO_REQUEST); ddi_put16(acc_handle, &scsi_raid_io->DevHandle, acmd->device_id); ReqDescUnion->SCSIIO.RequestFlags = (MPI2_REQ_DESCRIPT_FLAGS_LD_IO << MPI2_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); ddi_put16(acc_handle, &scsi_raid_io->RaidContext.timeoutValue, local_map_ptr->raidMap.fpPdIoTimeoutSec); if (instance->gen3) { uint8_t regLockFlags = ddi_get8(acc_handle, &scsi_raid_io->RaidContext.regLockFlags); if (regLockFlags == REGION_TYPE_UNUSED) { ReqDescUnion->SCSIIO.RequestFlags = (MPI2_REQ_DESCRIPT_FLAGS_NO_LOCK << MPI2_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); } regLockFlags |= (MR_RL_FLAGS_GRANT_DESTINATION_CPU0 | MR_RL_FLAGS_SEQ_NUM_ENABLE); ddi_put8(acc_handle, &scsi_raid_io->RaidContext.nsegType, ((0x01 << MPI2_NSEG_FLAGS_SHIFT) | MPI2_TYPE_CUDA)); ddi_put8(acc_handle, &scsi_raid_io->RaidContext.regLockFlags, regLockFlags); } } /* Not FP */ /* Release SYNC MAP UPDATE lock */ mutex_exit(&instance->sync_map_mtx); break; case 0x35: { /* SCMD_SYNCHRONIZE_CACHE */ return_raid_msg_pkt(instance, cmd); *cmd_done = 1; return (NULL); } case SCMD_MODE_SENSE: case SCMD_MODE_SENSE_G1: { union scsi_cdb *cdbp; uint16_t page_code; cdbp = (void *)pkt->pkt_cdbp; page_code = (uint16_t)cdbp->cdb_un.sg.scsi[0]; switch (page_code) { case 0x3: case 0x4: (void) mrsas_mode_sense_build(pkt); return_raid_msg_pkt(instance, cmd); *cmd_done = 1; return (NULL); } return (cmd); } default: /* Pass-through command to logical drive */ ddi_put8(acc_handle, &scsi_raid_io->Function, MPI2_FUNCTION_LD_IO_REQUEST); ddi_put8(acc_handle, &scsi_raid_io->LUN[1], acmd->lun); ddi_put16(acc_handle, &scsi_raid_io->DevHandle, acmd->device_id); ReqDescUnion->SCSIIO.RequestFlags = (MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO << MPI2_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); break; } } else { /* Physical */ /* Pass-through command to physical drive */ /* Acquire SYNC MAP UPDATE lock */ mutex_enter(&instance->sync_map_mtx); local_map_ptr = instance->ld_map[instance->map_id & 1]; ddi_put8(acc_handle, &scsi_raid_io->Function, MPI2_FUNCTION_SCSI_IO_REQUEST); ReqDescUnion->SCSIIO.RequestFlags = (MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY << MPI2_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); ddi_put16(acc_handle, &scsi_raid_io->DevHandle, local_map_ptr->raidMap. devHndlInfo[acmd->device_id].curDevHdl); /* Set regLockFlasgs to REGION_TYPE_BYPASS */ ddi_put8(acc_handle, &scsi_raid_io->RaidContext.regLockFlags, 0); ddi_put64(acc_handle, &scsi_raid_io->RaidContext.regLockRowLBA, 0); ddi_put32(acc_handle, &scsi_raid_io->RaidContext.regLockLength, 0); ddi_put8(acc_handle, &scsi_raid_io->RaidContext.RAIDFlags, MR_RAID_FLAGS_IO_SUB_TYPE_SYSTEM_PD << MR_RAID_CTX_RAID_FLAGS_IO_SUB_TYPE_SHIFT); ddi_put16(acc_handle, &scsi_raid_io->RaidContext.timeoutValue, local_map_ptr->raidMap.fpPdIoTimeoutSec); ddi_put16(acc_handle, &scsi_raid_io->RaidContext.ldTargetId, acmd->device_id); ddi_put8(acc_handle, &scsi_raid_io->LUN[1], acmd->lun); if (instance->fast_path_io && instance->gen3) { uint16_t IoFlags = ddi_get16(acc_handle, &scsi_raid_io->IoFlags); IoFlags |= MPI25_SAS_DEVICE0_FLAGS_ENABLED_FAST_PATH; ddi_put16(acc_handle, &scsi_raid_io->IoFlags, IoFlags); } ddi_put16(acc_handle, &ReqDescUnion->SCSIIO.DevHandle, local_map_ptr->raidMap. devHndlInfo[acmd->device_id].curDevHdl); /* Release SYNC MAP UPDATE lock */ mutex_exit(&instance->sync_map_mtx); } /* Set sense buffer physical address/length in scsi_io_request. */ ddi_put32(acc_handle, &scsi_raid_io->SenseBufferLowAddress, cmd->sense_phys_addr1); ddi_put8(acc_handle, &scsi_raid_io->SenseBufferLength, SENSE_LENGTH); /* Construct SGL */ ddi_put8(acc_handle, &scsi_raid_io->SGLOffset0, offsetof(MPI2_RAID_SCSI_IO_REQUEST, SGL) / 4); (void) mr_sas_tbolt_build_sgl(instance, acmd, cmd, scsi_raid_io, &datalen); ddi_put32(acc_handle, &scsi_raid_io->DataLength, datalen); con_log(CL_ANN, (CE_CONT, "tbolt_build_cmd CDB[0] =%x, TargetID =%x\n", pkt->pkt_cdbp[0], acmd->device_id)); con_log(CL_DLEVEL1, (CE_CONT, "data length = %x\n", scsi_raid_io->DataLength)); con_log(CL_DLEVEL1, (CE_CONT, "cdb length = %x\n", acmd->cmd_cdblen)); return (cmd); } uint32_t tbolt_read_fw_status_reg(struct mrsas_instance *instance) { return ((uint32_t)RD_OB_SCRATCH_PAD_0(instance)); } void tbolt_issue_cmd(struct mrsas_cmd *cmd, struct mrsas_instance *instance) { MRSAS_REQUEST_DESCRIPTOR_UNION *req_desc = cmd->request_desc; atomic_inc_16(&instance->fw_outstanding); struct scsi_pkt *pkt; con_log(CL_ANN1, (CE_NOTE, "tbolt_issue_cmd: cmd->[SMID]=0x%X", cmd->SMID)); con_log(CL_DLEVEL1, (CE_CONT, " [req desc Words] %" PRIx64 " \n", req_desc->Words)); con_log(CL_DLEVEL1, (CE_CONT, " [req desc low part] %x \n", (uint_t)(req_desc->Words & 0xffffffffff))); con_log(CL_DLEVEL1, (CE_CONT, " [req desc high part] %x \n", (uint_t)(req_desc->Words >> 32))); pkt = cmd->pkt; if (pkt) { con_log(CL_ANN1, (CE_CONT, "%llx :TBOLT issue_cmd_ppc:" "ISSUED CMD TO FW : called : cmd:" ": %p instance : %p pkt : %p pkt_time : %x\n", gethrtime(), (void *)cmd, (void *)instance, (void *)pkt, cmd->drv_pkt_time)); if (instance->adapterresetinprogress) { cmd->drv_pkt_time = (uint16_t)debug_timeout_g; con_log(CL_ANN, (CE_NOTE, "TBOLT Reset the scsi_pkt timer")); } else { push_pending_mfi_pkt(instance, cmd); } } else { con_log(CL_ANN1, (CE_CONT, "%llx :TBOLT issue_cmd_ppc:" "ISSUED CMD TO FW : called : cmd : %p, instance: %p" "(NO PKT)\n", gethrtime(), (void *)cmd, (void *)instance)); } /* Issue the command to the FW */ mutex_enter(&instance->reg_write_mtx); WR_IB_LOW_QPORT((uint32_t)(req_desc->Words), instance); WR_IB_HIGH_QPORT((uint32_t)(req_desc->Words >> 32), instance); mutex_exit(&instance->reg_write_mtx); } /* * issue_cmd_in_sync_mode */ int tbolt_issue_cmd_in_sync_mode(struct mrsas_instance *instance, struct mrsas_cmd *cmd) { int i; uint32_t msecs = MFI_POLL_TIMEOUT_SECS * MILLISEC; MRSAS_REQUEST_DESCRIPTOR_UNION *req_desc = cmd->request_desc; struct mrsas_header *hdr; hdr = (struct mrsas_header *)&cmd->frame->hdr; con_log(CL_ANN, (CE_NOTE, "tbolt_issue_cmd_in_sync_mode: cmd->[SMID]=0x%X", cmd->SMID)); if (instance->adapterresetinprogress) { cmd->drv_pkt_time = ddi_get16 (cmd->frame_dma_obj.acc_handle, &hdr->timeout); if (cmd->drv_pkt_time < debug_timeout_g) cmd->drv_pkt_time = (uint16_t)debug_timeout_g; con_log(CL_ANN, (CE_NOTE, "tbolt_issue_cmd_in_sync_mode:" "RESET-IN-PROGRESS, issue cmd & return.")); mutex_enter(&instance->reg_write_mtx); WR_IB_LOW_QPORT((uint32_t)(req_desc->Words), instance); WR_IB_HIGH_QPORT((uint32_t)(req_desc->Words >> 32), instance); mutex_exit(&instance->reg_write_mtx); return (DDI_SUCCESS); } else { con_log(CL_ANN1, (CE_NOTE, "tbolt_issue_cmd_in_sync_mode: pushing the pkt")); push_pending_mfi_pkt(instance, cmd); } con_log(CL_DLEVEL2, (CE_NOTE, "HighQport offset :%p", (void *)((uintptr_t)(instance)->regmap + IB_HIGH_QPORT))); con_log(CL_DLEVEL2, (CE_NOTE, "LowQport offset :%p", (void *)((uintptr_t)(instance)->regmap + IB_LOW_QPORT))); cmd->sync_cmd = MRSAS_TRUE; cmd->cmd_status = ENODATA; mutex_enter(&instance->reg_write_mtx); WR_IB_LOW_QPORT((uint32_t)(req_desc->Words), instance); WR_IB_HIGH_QPORT((uint32_t)(req_desc->Words >> 32), instance); mutex_exit(&instance->reg_write_mtx); con_log(CL_ANN1, (CE_NOTE, " req desc high part %x", (uint_t)(req_desc->Words >> 32))); con_log(CL_ANN1, (CE_NOTE, " req desc low part %x", (uint_t)(req_desc->Words & 0xffffffff))); mutex_enter(&instance->int_cmd_mtx); for (i = 0; i < msecs && (cmd->cmd_status == ENODATA); i++) { cv_wait(&instance->int_cmd_cv, &instance->int_cmd_mtx); } mutex_exit(&instance->int_cmd_mtx); if (i < (msecs -1)) { return (DDI_SUCCESS); } else { return (DDI_FAILURE); } } /* * issue_cmd_in_poll_mode */ int tbolt_issue_cmd_in_poll_mode(struct mrsas_instance *instance, struct mrsas_cmd *cmd) { int i; uint16_t flags; uint32_t msecs = MFI_POLL_TIMEOUT_SECS * MILLISEC; struct mrsas_header *frame_hdr; con_log(CL_ANN, (CE_NOTE, "tbolt_issue_cmd_in_poll_mode: cmd->[SMID]=0x%X", cmd->SMID)); MRSAS_REQUEST_DESCRIPTOR_UNION *req_desc = cmd->request_desc; frame_hdr = (struct mrsas_header *)&cmd->frame->hdr; ddi_put8(cmd->frame_dma_obj.acc_handle, &frame_hdr->cmd_status, MFI_CMD_STATUS_POLL_MODE); flags = ddi_get16(cmd->frame_dma_obj.acc_handle, &frame_hdr->flags); flags |= MFI_FRAME_DONT_POST_IN_REPLY_QUEUE; ddi_put16(cmd->frame_dma_obj.acc_handle, &frame_hdr->flags, flags); con_log(CL_ANN1, (CE_NOTE, " req desc low part %x", (uint_t)(req_desc->Words & 0xffffffff))); con_log(CL_ANN1, (CE_NOTE, " req desc high part %x", (uint_t)(req_desc->Words >> 32))); /* issue the frame using inbound queue port */ mutex_enter(&instance->reg_write_mtx); WR_IB_LOW_QPORT((uint32_t)(req_desc->Words), instance); WR_IB_HIGH_QPORT((uint32_t)(req_desc->Words >> 32), instance); mutex_exit(&instance->reg_write_mtx); for (i = 0; i < msecs && ( ddi_get8(cmd->frame_dma_obj.acc_handle, &frame_hdr->cmd_status) == MFI_CMD_STATUS_POLL_MODE); i++) { /* wait for cmd_status to change from 0xFF */ drv_usecwait(MILLISEC); /* wait for 1000 usecs */ } DTRACE_PROBE1(tbolt_complete_poll_cmd, uint8_t, i); if (ddi_get8(cmd->frame_dma_obj.acc_handle, &frame_hdr->cmd_status) == MFI_CMD_STATUS_POLL_MODE) { con_log(CL_ANN1, (CE_NOTE, " cmd failed %" PRIx64, (req_desc->Words))); return (DDI_FAILURE); } return (DDI_SUCCESS); } void tbolt_enable_intr(struct mrsas_instance *instance) { /* TODO: For Thunderbolt/Invader also clear intr on enable */ /* writel(~0, ®s->outbound_intr_status); */ /* readl(®s->outbound_intr_status); */ WR_OB_INTR_MASK(~(MFI_FUSION_ENABLE_INTERRUPT_MASK), instance); /* dummy read to force PCI flush */ (void) RD_OB_INTR_MASK(instance); } void tbolt_disable_intr(struct mrsas_instance *instance) { uint32_t mask = 0xFFFFFFFF; WR_OB_INTR_MASK(mask, instance); /* Dummy readl to force pci flush */ (void) RD_OB_INTR_MASK(instance); } int tbolt_intr_ack(struct mrsas_instance *instance) { uint32_t status; /* check if it is our interrupt */ status = RD_OB_INTR_STATUS(instance); con_log(CL_ANN1, (CE_NOTE, "chkpnt: Entered tbolt_intr_ack status = %d", status)); if (!(status & MFI_FUSION_ENABLE_INTERRUPT_MASK)) { return (DDI_INTR_UNCLAIMED); } if (mrsas_check_acc_handle(instance->regmap_handle) != DDI_SUCCESS) { ddi_fm_service_impact(instance->dip, DDI_SERVICE_LOST); return (DDI_INTR_UNCLAIMED); } if ((status & 1) || (status & MFI_FUSION_ENABLE_INTERRUPT_MASK)) { /* clear the interrupt by writing back the same value */ WR_OB_INTR_STATUS(status, instance); /* dummy READ */ (void) RD_OB_INTR_STATUS(instance); } return (DDI_INTR_CLAIMED); } /* * get_raid_msg_pkt : Get a command from the free pool * After successful allocation, the caller of this routine * must clear the frame buffer (memset to zero) before * using the packet further. * * ***** Note ***** * After clearing the frame buffer the context id of the * frame buffer SHOULD be restored back. */ struct mrsas_cmd * get_raid_msg_pkt(struct mrsas_instance *instance) { mlist_t *head = &instance->cmd_pool_list; struct mrsas_cmd *cmd = NULL; mutex_enter(&instance->cmd_pool_mtx); ASSERT(mutex_owned(&instance->cmd_pool_mtx)); if (!mlist_empty(head)) { cmd = mlist_entry(head->next, struct mrsas_cmd, list); mlist_del_init(head->next); } if (cmd != NULL) { cmd->pkt = NULL; cmd->retry_count_for_ocr = 0; cmd->drv_pkt_time = 0; } mutex_exit(&instance->cmd_pool_mtx); if (cmd != NULL) bzero(cmd->scsi_io_request, sizeof (Mpi2RaidSCSIIORequest_t)); return (cmd); } struct mrsas_cmd * get_raid_msg_mfi_pkt(struct mrsas_instance *instance) { mlist_t *head = &instance->cmd_app_pool_list; struct mrsas_cmd *cmd = NULL; mutex_enter(&instance->cmd_app_pool_mtx); ASSERT(mutex_owned(&instance->cmd_app_pool_mtx)); if (!mlist_empty(head)) { cmd = mlist_entry(head->next, struct mrsas_cmd, list); mlist_del_init(head->next); } if (cmd != NULL) { cmd->retry_count_for_ocr = 0; cmd->drv_pkt_time = 0; cmd->pkt = NULL; cmd->request_desc = NULL; } mutex_exit(&instance->cmd_app_pool_mtx); if (cmd != NULL) { bzero(cmd->scsi_io_request, sizeof (Mpi2RaidSCSIIORequest_t)); } return (cmd); } /* * return_raid_msg_pkt : Return a cmd to free command pool */ void return_raid_msg_pkt(struct mrsas_instance *instance, struct mrsas_cmd *cmd) { mutex_enter(&instance->cmd_pool_mtx); ASSERT(mutex_owned(&instance->cmd_pool_mtx)); mlist_add_tail(&cmd->list, &instance->cmd_pool_list); mutex_exit(&instance->cmd_pool_mtx); } void return_raid_msg_mfi_pkt(struct mrsas_instance *instance, struct mrsas_cmd *cmd) { mutex_enter(&instance->cmd_app_pool_mtx); ASSERT(mutex_owned(&instance->cmd_app_pool_mtx)); mlist_add_tail(&cmd->list, &instance->cmd_app_pool_list); mutex_exit(&instance->cmd_app_pool_mtx); } void mr_sas_tbolt_build_mfi_cmd(struct mrsas_instance *instance, struct mrsas_cmd *cmd) { Mpi2RaidSCSIIORequest_t *scsi_raid_io; Mpi25IeeeSgeChain64_t *scsi_raid_io_sgl_ieee; MRSAS_REQUEST_DESCRIPTOR_UNION *ReqDescUnion; uint32_t index; ddi_acc_handle_t acc_handle = instance->mpi2_frame_pool_dma_obj.acc_handle; if (!instance->tbolt) { con_log(CL_ANN, (CE_NOTE, "Not MFA enabled.")); return; } index = cmd->index; ReqDescUnion = mr_sas_get_request_descriptor(instance, index); if (!ReqDescUnion) { con_log(CL_ANN1, (CE_NOTE, "[NULL REQDESC]")); return; } con_log(CL_ANN1, (CE_NOTE, "[SMID]%x", cmd->SMID)); ReqDescUnion->Words = 0; ReqDescUnion->SCSIIO.RequestFlags = (MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO << MPI2_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); ReqDescUnion->SCSIIO.SMID = cmd->SMID; cmd->request_desc = ReqDescUnion; /* get raid message frame pointer */ scsi_raid_io = (Mpi2RaidSCSIIORequest_t *)cmd->scsi_io_request; if (instance->gen3) { Mpi25IeeeSgeChain64_t *sgl_ptr_end = (Mpi25IeeeSgeChain64_t *) &scsi_raid_io->SGL.IeeeChain; sgl_ptr_end += instance->max_sge_in_main_msg - 1; ddi_put8(acc_handle, &sgl_ptr_end->Flags, 0); } ddi_put8(acc_handle, &scsi_raid_io->Function, MPI2_FUNCTION_PASSTHRU_IO_REQUEST); ddi_put8(acc_handle, &scsi_raid_io->SGLOffset0, offsetof(MPI2_RAID_SCSI_IO_REQUEST, SGL) / 4); ddi_put8(acc_handle, &scsi_raid_io->ChainOffset, (U8)offsetof(MPI2_RAID_SCSI_IO_REQUEST, SGL) / 16); ddi_put32(acc_handle, &scsi_raid_io->SenseBufferLowAddress, cmd->sense_phys_addr1); scsi_raid_io_sgl_ieee = (Mpi25IeeeSgeChain64_t *)&scsi_raid_io->SGL.IeeeChain; ddi_put64(acc_handle, &scsi_raid_io_sgl_ieee->Address, (U64)cmd->frame_phys_addr); ddi_put8(acc_handle, &scsi_raid_io_sgl_ieee->Flags, (IEEE_SGE_FLAGS_CHAIN_ELEMENT | MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR)); /* LSI put hardcoded 1024 instead of MEGASAS_MAX_SZ_CHAIN_FRAME. */ ddi_put32(acc_handle, &scsi_raid_io_sgl_ieee->Length, 1024); con_log(CL_ANN1, (CE_NOTE, "[MFI CMD PHY ADDRESS]:%" PRIx64, scsi_raid_io_sgl_ieee->Address)); con_log(CL_ANN1, (CE_NOTE, "[SGL Length]:%x", scsi_raid_io_sgl_ieee->Length)); con_log(CL_ANN1, (CE_NOTE, "[SGL Flags]:%x", scsi_raid_io_sgl_ieee->Flags)); } void tbolt_complete_cmd(struct mrsas_instance *instance, struct mrsas_cmd *cmd) { uint8_t status; uint8_t extStatus; uint8_t function; uint8_t arm; struct scsa_cmd *acmd; struct scsi_pkt *pkt; struct scsi_arq_status *arqstat; Mpi2RaidSCSIIORequest_t *scsi_raid_io; LD_LOAD_BALANCE_INFO *lbinfo; ddi_acc_handle_t acc_handle = instance->mpi2_frame_pool_dma_obj.acc_handle; scsi_raid_io = (Mpi2RaidSCSIIORequest_t *)cmd->scsi_io_request; status = ddi_get8(acc_handle, &scsi_raid_io->RaidContext.status); extStatus = ddi_get8(acc_handle, &scsi_raid_io->RaidContext.extStatus); con_log(CL_DLEVEL3, (CE_NOTE, "status %x", status)); con_log(CL_DLEVEL3, (CE_NOTE, "extStatus %x", extStatus)); if (status != MFI_STAT_OK) { con_log(CL_ANN, (CE_WARN, "IO Cmd Failed SMID %x", cmd->SMID)); } else { con_log(CL_ANN, (CE_NOTE, "IO Cmd Success SMID %x", cmd->SMID)); } /* regular commands */ function = ddi_get8(acc_handle, &scsi_raid_io->Function); DTRACE_PROBE3(tbolt_complete_cmd, uint8_t, function, uint8_t, status, uint8_t, extStatus); switch (function) { case MPI2_FUNCTION_SCSI_IO_REQUEST : /* Fast Path IO. */ acmd = (struct scsa_cmd *)cmd->cmd; lbinfo = &instance->load_balance_info[acmd->device_id]; if (cmd->load_balance_flag & MEGASAS_LOAD_BALANCE_FLAG) { arm = lbinfo->raid1DevHandle[0] == scsi_raid_io->DevHandle ? 0 : 1; lbinfo->scsi_pending_cmds[arm]--; cmd->load_balance_flag &= ~MEGASAS_LOAD_BALANCE_FLAG; } con_log(CL_DLEVEL3, (CE_NOTE, "FastPath IO Completion Success ")); /* FALLTHRU */ case MPI2_FUNCTION_LD_IO_REQUEST : { /* Regular Path IO. */ acmd = (struct scsa_cmd *)cmd->cmd; pkt = (struct scsi_pkt *)CMD2PKT(acmd); if (acmd->cmd_flags & CFLAG_DMAVALID) { if (acmd->cmd_flags & CFLAG_CONSISTENT) { (void) ddi_dma_sync(acmd->cmd_dmahandle, acmd->cmd_dma_offset, acmd->cmd_dma_len, DDI_DMA_SYNC_FORCPU); } } pkt->pkt_reason = CMD_CMPLT; pkt->pkt_statistics = 0; pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET | STATE_SENT_CMD | STATE_XFERRED_DATA | STATE_GOT_STATUS; con_log(CL_ANN, (CE_CONT, " CDB[0] = %x completed for %s: " "size %lx SMID %x cmd_status %x", pkt->pkt_cdbp[0], ((acmd->islogical) ? "LD" : "PD"), acmd->cmd_dmacount, cmd->SMID, status)); if (pkt->pkt_cdbp[0] == SCMD_INQUIRY) { struct scsi_inquiry *inq; if (acmd->cmd_dmacount != 0) { bp_mapin(acmd->cmd_buf); inq = (struct scsi_inquiry *) acmd->cmd_buf->b_un.b_addr; /* don't expose physical drives to OS */ if (acmd->islogical && (status == MFI_STAT_OK)) { display_scsi_inquiry((caddr_t)inq); } else if ((status == MFI_STAT_OK) && inq->inq_dtype == DTYPE_DIRECT) { display_scsi_inquiry((caddr_t)inq); } else { /* for physical disk */ status = MFI_STAT_DEVICE_NOT_FOUND; } } } switch (status) { case MFI_STAT_OK: pkt->pkt_scbp[0] = STATUS_GOOD; break; case MFI_STAT_LD_CC_IN_PROGRESS: case MFI_STAT_LD_RECON_IN_PROGRESS: pkt->pkt_scbp[0] = STATUS_GOOD; break; case MFI_STAT_LD_INIT_IN_PROGRESS: pkt->pkt_reason = CMD_TRAN_ERR; break; case MFI_STAT_SCSI_IO_FAILED: dev_err(instance->dip, CE_WARN, "tbolt_complete_cmd: scsi_io failed"); pkt->pkt_reason = CMD_TRAN_ERR; break; case MFI_STAT_SCSI_DONE_WITH_ERROR: con_log(CL_ANN, (CE_WARN, "tbolt_complete_cmd: scsi_done with error")); pkt->pkt_reason = CMD_CMPLT; ((struct scsi_status *)pkt->pkt_scbp)->sts_chk = 1; if (pkt->pkt_cdbp[0] == SCMD_TEST_UNIT_READY) { con_log(CL_ANN, (CE_WARN, "TEST_UNIT_READY fail")); } else { pkt->pkt_state |= STATE_ARQ_DONE; arqstat = (void *)(pkt->pkt_scbp); arqstat->sts_rqpkt_reason = CMD_CMPLT; arqstat->sts_rqpkt_resid = 0; arqstat->sts_rqpkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET | STATE_SENT_CMD | STATE_XFERRED_DATA; *(uint8_t *)&arqstat->sts_rqpkt_status = STATUS_GOOD; con_log(CL_ANN1, (CE_NOTE, "Copying Sense data %x", cmd->SMID)); ddi_rep_get8(acc_handle, (uint8_t *)&(arqstat->sts_sensedata), cmd->sense1, sizeof (struct scsi_extended_sense), DDI_DEV_AUTOINCR); } break; case MFI_STAT_LD_OFFLINE: dev_err(instance->dip, CE_WARN, "tbolt_complete_cmd: ld offline " "CDB[0]=0x%x targetId=0x%x devhandle=0x%x", /* UNDO: */ ddi_get8(acc_handle, &scsi_raid_io->CDB.CDB32[0]), ddi_get16(acc_handle, &scsi_raid_io->RaidContext.ldTargetId), ddi_get16(acc_handle, &scsi_raid_io->DevHandle)); pkt->pkt_reason = CMD_DEV_GONE; pkt->pkt_statistics = STAT_DISCON; break; case MFI_STAT_DEVICE_NOT_FOUND: con_log(CL_ANN, (CE_CONT, "tbolt_complete_cmd: device not found error")); pkt->pkt_reason = CMD_DEV_GONE; pkt->pkt_statistics = STAT_DISCON; break; case MFI_STAT_LD_LBA_OUT_OF_RANGE: pkt->pkt_state |= STATE_ARQ_DONE; pkt->pkt_reason = CMD_CMPLT; ((struct scsi_status *)pkt->pkt_scbp)->sts_chk = 1; arqstat = (void *)(pkt->pkt_scbp); arqstat->sts_rqpkt_reason = CMD_CMPLT; arqstat->sts_rqpkt_resid = 0; arqstat->sts_rqpkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET | STATE_SENT_CMD | STATE_XFERRED_DATA; *(uint8_t *)&arqstat->sts_rqpkt_status = STATUS_GOOD; arqstat->sts_sensedata.es_valid = 1; arqstat->sts_sensedata.es_key = KEY_ILLEGAL_REQUEST; arqstat->sts_sensedata.es_class = CLASS_EXTENDED_SENSE; /* * LOGICAL BLOCK ADDRESS OUT OF RANGE: * ASC: 0x21h; ASCQ: 0x00h; */ arqstat->sts_sensedata.es_add_code = 0x21; arqstat->sts_sensedata.es_qual_code = 0x00; break; case MFI_STAT_INVALID_CMD: case MFI_STAT_INVALID_DCMD: case MFI_STAT_INVALID_PARAMETER: case MFI_STAT_INVALID_SEQUENCE_NUMBER: default: dev_err(instance->dip, CE_WARN, "tbolt_complete_cmd: Unknown status!"); pkt->pkt_reason = CMD_TRAN_ERR; break; } atomic_add_16(&instance->fw_outstanding, (-1)); (void) mrsas_common_check(instance, cmd); if (acmd->cmd_dmahandle) { if (mrsas_check_dma_handle(acmd->cmd_dmahandle) != DDI_SUCCESS) { ddi_fm_service_impact(instance->dip, DDI_SERVICE_UNAFFECTED); pkt->pkt_reason = CMD_TRAN_ERR; pkt->pkt_statistics = 0; } } /* Call the callback routine */ if (((pkt->pkt_flags & FLAG_NOINTR) == 0) && pkt->pkt_comp) (*pkt->pkt_comp)(pkt); con_log(CL_ANN1, (CE_NOTE, "Free smid %x", cmd->SMID)); ddi_put8(acc_handle, &scsi_raid_io->RaidContext.status, 0); ddi_put8(acc_handle, &scsi_raid_io->RaidContext.extStatus, 0); return_raid_msg_pkt(instance, cmd); break; } case MPI2_FUNCTION_PASSTHRU_IO_REQUEST: /* MFA command. */ if (cmd->frame->dcmd.opcode == MR_DCMD_LD_MAP_GET_INFO && cmd->frame->dcmd.mbox.b[1] == 1) { mutex_enter(&instance->sync_map_mtx); con_log(CL_ANN, (CE_NOTE, "LDMAP sync command SMID RECEIVED 0x%X", cmd->SMID)); if (cmd->frame->hdr.cmd_status != 0) { dev_err(instance->dip, CE_WARN, "map sync failed, status = 0x%x.", cmd->frame->hdr.cmd_status); } else { instance->map_id++; con_log(CL_ANN1, (CE_NOTE, "map sync received, switched map_id to %" PRIu64, instance->map_id)); } if (MR_ValidateMapInfo( instance->ld_map[instance->map_id & 1], instance->load_balance_info)) { instance->fast_path_io = 1; } else { instance->fast_path_io = 0; } con_log(CL_ANN, (CE_NOTE, "instance->fast_path_io %d", instance->fast_path_io)); instance->unroll.syncCmd = 0; if (instance->map_update_cmd == cmd) { return_raid_msg_pkt(instance, cmd); atomic_add_16(&instance->fw_outstanding, (-1)); (void) mrsas_tbolt_sync_map_info(instance); } con_log(CL_ANN1, (CE_NOTE, "LDMAP sync completed, ldcount=%d", instance->ld_map[instance->map_id & 1] ->raidMap.ldCount)); mutex_exit(&instance->sync_map_mtx); break; } if (cmd->frame->dcmd.opcode == MR_DCMD_CTRL_EVENT_WAIT) { con_log(CL_ANN1, (CE_CONT, "AEN command SMID RECEIVED 0x%X", cmd->SMID)); if ((instance->aen_cmd == cmd) && (instance->aen_cmd->abort_aen)) { con_log(CL_ANN, (CE_WARN, "mrsas_softintr: " "aborted_aen returned")); } else { atomic_add_16(&instance->fw_outstanding, (-1)); service_mfi_aen(instance, cmd); } } if (cmd->sync_cmd == MRSAS_TRUE) { con_log(CL_ANN1, (CE_CONT, "Sync-mode Command Response SMID RECEIVED 0x%X", cmd->SMID)); tbolt_complete_cmd_in_sync_mode(instance, cmd); } else { con_log(CL_ANN, (CE_CONT, "tbolt_complete_cmd: Wrong SMID RECEIVED 0x%X", cmd->SMID)); } break; default: mrsas_fm_ereport(instance, DDI_FM_DEVICE_NO_RESPONSE); ddi_fm_service_impact(instance->dip, DDI_SERVICE_LOST); /* free message */ con_log(CL_ANN, (CE_NOTE, "tbolt_complete_cmd: Unknown Type!!!!!!!!")); break; } } uint_t mr_sas_tbolt_process_outstanding_cmd(struct mrsas_instance *instance) { uint8_t replyType; Mpi2SCSIIOSuccessReplyDescriptor_t *replyDesc; Mpi2ReplyDescriptorsUnion_t *desc; uint16_t smid; union desc_value d_val; struct mrsas_cmd *cmd; struct mrsas_header *hdr; struct scsi_pkt *pkt; (void) ddi_dma_sync(instance->reply_desc_dma_obj.dma_handle, 0, 0, DDI_DMA_SYNC_FORDEV); (void) ddi_dma_sync(instance->reply_desc_dma_obj.dma_handle, 0, 0, DDI_DMA_SYNC_FORCPU); desc = instance->reply_frame_pool; desc += instance->reply_read_index; replyDesc = (MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR *)desc; replyType = replyDesc->ReplyFlags & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK; if (replyType == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) return (DDI_INTR_UNCLAIMED); if (mrsas_check_dma_handle(instance->mfi_internal_dma_obj.dma_handle) != DDI_SUCCESS) { mrsas_fm_ereport(instance, DDI_FM_DEVICE_NO_RESPONSE); ddi_fm_service_impact(instance->dip, DDI_SERVICE_LOST); con_log(CL_ANN1, (CE_WARN, "mr_sas_tbolt_process_outstanding_cmd(): " "FMA check, returning DDI_INTR_UNCLAIMED")); return (DDI_INTR_CLAIMED); } con_log(CL_ANN1, (CE_NOTE, "Reply Desc = %p Words = %" PRIx64, (void *)desc, desc->Words)); d_val.word = desc->Words; /* Read Reply descriptor */ while ((d_val.u1.low != 0xffffffff) && (d_val.u1.high != 0xffffffff)) { (void) ddi_dma_sync(instance->reply_desc_dma_obj.dma_handle, 0, 0, DDI_DMA_SYNC_FORCPU); smid = replyDesc->SMID; if (!smid || smid > instance->max_fw_cmds + 1) { con_log(CL_ANN1, (CE_NOTE, "Reply Desc at Break = %p Words = %" PRIx64, (void *)desc, desc->Words)); break; } cmd = instance->cmd_list[smid - 1]; if (!cmd) { con_log(CL_ANN1, (CE_NOTE, "mr_sas_tbolt_process_" "outstanding_cmd: Invalid command " " or Poll commad Received in completion path")); } else { mutex_enter(&instance->cmd_pend_mtx); if (cmd->sync_cmd == MRSAS_TRUE) { hdr = (struct mrsas_header *)&cmd->frame->hdr; if (hdr) { con_log(CL_ANN1, (CE_NOTE, "mr_sas_" "tbolt_process_outstanding_cmd:" " mlist_del_init(&cmd->list).")); mlist_del_init(&cmd->list); } } else { pkt = cmd->pkt; if (pkt) { con_log(CL_ANN1, (CE_NOTE, "mr_sas_" "tbolt_process_outstanding_cmd:" "mlist_del_init(&cmd->list).")); mlist_del_init(&cmd->list); } } mutex_exit(&instance->cmd_pend_mtx); tbolt_complete_cmd(instance, cmd); } /* set it back to all 1s. */ desc->Words = -1LL; instance->reply_read_index++; if (instance->reply_read_index >= (instance->reply_q_depth)) { con_log(CL_ANN1, (CE_NOTE, "wrap around")); instance->reply_read_index = 0; } /* Get the next reply descriptor */ if (!instance->reply_read_index) desc = instance->reply_frame_pool; else desc++; replyDesc = (MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR *)desc; d_val.word = desc->Words; con_log(CL_ANN1, (CE_NOTE, "Next Reply Desc = %p Words = %" PRIx64, (void *)desc, desc->Words)); replyType = replyDesc->ReplyFlags & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK; if (replyType == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) break; } /* End of while loop. */ /* update replyIndex to FW */ WR_MPI2_REPLY_POST_INDEX(instance->reply_read_index, instance); (void) ddi_dma_sync(instance->reply_desc_dma_obj.dma_handle, 0, 0, DDI_DMA_SYNC_FORDEV); (void) ddi_dma_sync(instance->reply_desc_dma_obj.dma_handle, 0, 0, DDI_DMA_SYNC_FORCPU); return (DDI_INTR_CLAIMED); } /* * complete_cmd_in_sync_mode - Completes an internal command * @instance: Adapter soft state * @cmd: Command to be completed * * The issue_cmd_in_sync_mode() function waits for a command to complete * after it issues a command. This function wakes up that waiting routine by * calling wake_up() on the wait queue. */ void tbolt_complete_cmd_in_sync_mode(struct mrsas_instance *instance, struct mrsas_cmd *cmd) { cmd->cmd_status = ddi_get8(cmd->frame_dma_obj.acc_handle, &cmd->frame->io.cmd_status); cmd->sync_cmd = MRSAS_FALSE; mutex_enter(&instance->int_cmd_mtx); if (cmd->cmd_status == ENODATA) { cmd->cmd_status = 0; } cv_broadcast(&instance->int_cmd_cv); mutex_exit(&instance->int_cmd_mtx); } /* * mrsas_tbolt_get_ld_map_info - Returns ld_map structure * instance: Adapter soft state * * Issues an internal command (DCMD) to get the FW's controller PD * list structure. This information is mainly used to find out SYSTEM * supported by the FW. */ int mrsas_tbolt_get_ld_map_info(struct mrsas_instance *instance) { int ret = 0; struct mrsas_cmd *cmd = NULL; struct mrsas_dcmd_frame *dcmd; MR_FW_RAID_MAP_ALL *ci; uint32_t ci_h = 0; U32 size_map_info; cmd = get_raid_msg_pkt(instance); if (cmd == NULL) { dev_err(instance->dip, CE_WARN, "Failed to get a cmd from free-pool in get_ld_map_info()"); return (DDI_FAILURE); } dcmd = &cmd->frame->dcmd; size_map_info = sizeof (MR_FW_RAID_MAP) + (sizeof (MR_LD_SPAN_MAP) * (MAX_LOGICAL_DRIVES - 1)); con_log(CL_ANN, (CE_NOTE, "size_map_info : 0x%x", size_map_info)); ci = instance->ld_map[instance->map_id & 1]; ci_h = instance->ld_map_phy[instance->map_id & 1]; if (!ci) { dev_err(instance->dip, CE_WARN, "Failed to alloc mem for ld_map_info"); return_raid_msg_pkt(instance, cmd); return (-1); } bzero(ci, sizeof (*ci)); bzero(dcmd->mbox.b, DCMD_MBOX_SZ); dcmd->cmd = MFI_CMD_OP_DCMD; dcmd->cmd_status = 0xFF; dcmd->sge_count = 1; dcmd->flags = MFI_FRAME_DIR_READ; dcmd->timeout = 0; dcmd->pad_0 = 0; dcmd->data_xfer_len = size_map_info; dcmd->opcode = MR_DCMD_LD_MAP_GET_INFO; dcmd->sgl.sge32[0].phys_addr = ci_h; dcmd->sgl.sge32[0].length = size_map_info; mr_sas_tbolt_build_mfi_cmd(instance, cmd); if (!instance->func_ptr->issue_cmd_in_poll_mode(instance, cmd)) { ret = 0; con_log(CL_ANN1, (CE_NOTE, "Get LD Map Info success")); } else { dev_err(instance->dip, CE_WARN, "Get LD Map Info failed"); ret = -1; } return_raid_msg_pkt(instance, cmd); return (ret); } void mrsas_dump_reply_desc(struct mrsas_instance *instance) { uint32_t i; MPI2_REPLY_DESCRIPTORS_UNION *reply_desc; union desc_value d_val; reply_desc = instance->reply_frame_pool; for (i = 0; i < instance->reply_q_depth; i++, reply_desc++) { d_val.word = reply_desc->Words; con_log(CL_DLEVEL3, (CE_NOTE, "i=%d, %x:%x", i, d_val.u1.high, d_val.u1.low)); } } /* * mrsas_tbolt_command_create - Create command for fast path. * @io_info: MegaRAID IO request packet pointer. * @ref_tag: Reference tag for RD/WRPROTECT * * Create the command for fast path. */ void mrsas_tbolt_prepare_cdb(struct mrsas_instance *instance, U8 cdb[], struct IO_REQUEST_INFO *io_info, Mpi2RaidSCSIIORequest_t *scsi_io_request, U32 ref_tag) { uint16_t EEDPFlags; uint32_t Control; ddi_acc_handle_t acc_handle = instance->mpi2_frame_pool_dma_obj.acc_handle; /* Prepare 32-byte CDB if DIF is supported on this device */ con_log(CL_ANN, (CE_NOTE, "Prepare DIF CDB")); bzero(cdb, 32); cdb[0] = MRSAS_SCSI_VARIABLE_LENGTH_CMD; cdb[7] = MRSAS_SCSI_ADDL_CDB_LEN; if (io_info->isRead) cdb[9] = MRSAS_SCSI_SERVICE_ACTION_READ32; else cdb[9] = MRSAS_SCSI_SERVICE_ACTION_WRITE32; /* Verify within linux driver, set to MEGASAS_RD_WR_PROTECT_CHECK_ALL */ cdb[10] = MRSAS_RD_WR_PROTECT; /* LOGICAL BLOCK ADDRESS */ cdb[12] = (U8)(((io_info->pdBlock) >> 56) & 0xff); cdb[13] = (U8)(((io_info->pdBlock) >> 48) & 0xff); cdb[14] = (U8)(((io_info->pdBlock) >> 40) & 0xff); cdb[15] = (U8)(((io_info->pdBlock) >> 32) & 0xff); cdb[16] = (U8)(((io_info->pdBlock) >> 24) & 0xff); cdb[17] = (U8)(((io_info->pdBlock) >> 16) & 0xff); cdb[18] = (U8)(((io_info->pdBlock) >> 8) & 0xff); cdb[19] = (U8)((io_info->pdBlock) & 0xff); /* Logical block reference tag */ ddi_put32(acc_handle, &scsi_io_request->CDB.EEDP32.PrimaryReferenceTag, BE_32(ref_tag)); ddi_put16(acc_handle, &scsi_io_request->CDB.EEDP32.PrimaryApplicationTagMask, 0xffff); ddi_put32(acc_handle, &scsi_io_request->DataLength, ((io_info->numBlocks)*512)); /* Specify 32-byte cdb */ ddi_put16(acc_handle, &scsi_io_request->IoFlags, 32); /* Transfer length */ cdb[28] = (U8)(((io_info->numBlocks) >> 24) & 0xff); cdb[29] = (U8)(((io_info->numBlocks) >> 16) & 0xff); cdb[30] = (U8)(((io_info->numBlocks) >> 8) & 0xff); cdb[31] = (U8)((io_info->numBlocks) & 0xff); /* set SCSI IO EEDPFlags */ EEDPFlags = ddi_get16(acc_handle, &scsi_io_request->EEDPFlags); Control = ddi_get32(acc_handle, &scsi_io_request->Control); /* set SCSI IO EEDPFlags bits */ if (io_info->isRead) { /* * For READ commands, the EEDPFlags shall be set to specify to * Increment the Primary Reference Tag, to Check the Reference * Tag, and to Check and Remove the Protection Information * fields. */ EEDPFlags = MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG | MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG | MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP | MPI2_SCSIIO_EEDPFLAGS_CHECK_APPTAG | MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD; } else { /* * For WRITE commands, the EEDPFlags shall be set to specify to * Increment the Primary Reference Tag, and to Insert * Protection Information fields. */ EEDPFlags = MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG | MPI2_SCSIIO_EEDPFLAGS_INSERT_OP; } Control |= (0x4 << 26); ddi_put16(acc_handle, &scsi_io_request->EEDPFlags, EEDPFlags); ddi_put32(acc_handle, &scsi_io_request->Control, Control); ddi_put32(acc_handle, &scsi_io_request->EEDPBlockSize, MRSAS_EEDPBLOCKSIZE); } /* * mrsas_tbolt_set_pd_lba - Sets PD LBA * @cdb: CDB * @cdb_size: CDB size * @cdb_len_ptr: cdb length * @start_blk: Start block of IO * @num_blocks: Number of blocks * * Used to set the PD LBA in CDB for FP IOs */ static void mrsas_tbolt_set_pd_lba(U8 *cdb, size_t cdb_size, uint8_t *cdb_len_ptr, U64 start_blk, U32 num_blocks) { U8 cdb_len = *cdb_len_ptr; U8 flagvals = 0, opcode = 0, groupnum = 0, control = 0; /* Some drives don't support 16/12 byte CDB's, convert to 10 */ if (((cdb_len == 12) || (cdb_len == 16)) && (start_blk <= 0xffffffff)) { if (cdb_len == 16) { con_log(CL_ANN, (CE_NOTE, "Converting READ/WRITE(16) to READ10")); opcode = cdb[0] == READ_16 ? READ_10 : WRITE_10; flagvals = cdb[1]; groupnum = cdb[14]; control = cdb[15]; } else { con_log(CL_ANN, (CE_NOTE, "Converting READ/WRITE(12) to READ10")); opcode = cdb[0] == READ_12 ? READ_10 : WRITE_10; flagvals = cdb[1]; groupnum = cdb[10]; control = cdb[11]; } bzero(cdb, cdb_size); cdb[0] = opcode; cdb[1] = flagvals; cdb[6] = groupnum; cdb[9] = control; /* Set transfer length */ cdb[8] = (U8)(num_blocks & 0xff); cdb[7] = (U8)((num_blocks >> 8) & 0xff); cdb_len = 10; } else if ((cdb_len < 16) && (start_blk > 0xffffffff)) { /* Convert to 16 byte CDB for large LBA's */ con_log(CL_ANN, (CE_NOTE, "Converting 6/10/12 CDB to 16 byte CDB")); switch (cdb_len) { case 6: opcode = cdb[0] == READ_6 ? READ_16 : WRITE_16; control = cdb[5]; break; case 10: opcode = cdb[0] == READ_10 ? READ_16 : WRITE_16; flagvals = cdb[1]; groupnum = cdb[6]; control = cdb[9]; break; case 12: opcode = cdb[0] == READ_12 ? READ_16 : WRITE_16; flagvals = cdb[1]; groupnum = cdb[10]; control = cdb[11]; break; } bzero(cdb, cdb_size); cdb[0] = opcode; cdb[1] = flagvals; cdb[14] = groupnum; cdb[15] = control; /* Transfer length */ cdb[13] = (U8)(num_blocks & 0xff); cdb[12] = (U8)((num_blocks >> 8) & 0xff); cdb[11] = (U8)((num_blocks >> 16) & 0xff); cdb[10] = (U8)((num_blocks >> 24) & 0xff); /* Specify 16-byte cdb */ cdb_len = 16; } else if ((cdb_len == 6) && (start_blk > 0x1fffff)) { /* convert to 10 byte CDB */ opcode = cdb[0] == READ_6 ? READ_10 : WRITE_10; control = cdb[5]; bzero(cdb, cdb_size); cdb[0] = opcode; cdb[9] = control; /* Set transfer length */ cdb[8] = (U8)(num_blocks & 0xff); cdb[7] = (U8)((num_blocks >> 8) & 0xff); /* Specify 10-byte cdb */ cdb_len = 10; } /* Fall through Normal case, just load LBA here */ switch (cdb_len) { case 6: { U8 val = cdb[1] & 0xE0; cdb[3] = (U8)(start_blk & 0xff); cdb[2] = (U8)((start_blk >> 8) & 0xff); cdb[1] = val | ((U8)(start_blk >> 16) & 0x1f); break; } case 10: cdb[5] = (U8)(start_blk & 0xff); cdb[4] = (U8)((start_blk >> 8) & 0xff); cdb[3] = (U8)((start_blk >> 16) & 0xff); cdb[2] = (U8)((start_blk >> 24) & 0xff); break; case 12: cdb[5] = (U8)(start_blk & 0xff); cdb[4] = (U8)((start_blk >> 8) & 0xff); cdb[3] = (U8)((start_blk >> 16) & 0xff); cdb[2] = (U8)((start_blk >> 24) & 0xff); break; case 16: cdb[9] = (U8)(start_blk & 0xff); cdb[8] = (U8)((start_blk >> 8) & 0xff); cdb[7] = (U8)((start_blk >> 16) & 0xff); cdb[6] = (U8)((start_blk >> 24) & 0xff); cdb[5] = (U8)((start_blk >> 32) & 0xff); cdb[4] = (U8)((start_blk >> 40) & 0xff); cdb[3] = (U8)((start_blk >> 48) & 0xff); cdb[2] = (U8)((start_blk >> 56) & 0xff); break; } *cdb_len_ptr = cdb_len; } static int mrsas_tbolt_check_map_info(struct mrsas_instance *instance) { MR_FW_RAID_MAP_ALL *ld_map; if (!mrsas_tbolt_get_ld_map_info(instance)) { ld_map = instance->ld_map[instance->map_id & 1]; con_log(CL_ANN1, (CE_NOTE, "ldCount=%d, map size=%d", ld_map->raidMap.ldCount, ld_map->raidMap.totalSize)); if (MR_ValidateMapInfo( instance->ld_map[instance->map_id & 1], instance->load_balance_info)) { con_log(CL_ANN, (CE_CONT, "MR_ValidateMapInfo success")); instance->fast_path_io = 1; con_log(CL_ANN, (CE_NOTE, "instance->fast_path_io %d", instance->fast_path_io)); return (DDI_SUCCESS); } } instance->fast_path_io = 0; dev_err(instance->dip, CE_WARN, "MR_ValidateMapInfo failed"); con_log(CL_ANN, (CE_NOTE, "instance->fast_path_io %d", instance->fast_path_io)); return (DDI_FAILURE); } /* * Marks HBA as bad. This will be called either when an * IO packet times out even after 3 FW resets * or FW is found to be fault even after 3 continuous resets. */ void mrsas_tbolt_kill_adapter(struct mrsas_instance *instance) { dev_err(instance->dip, CE_NOTE, "TBOLT Kill adapter called"); if (instance->deadadapter == 1) return; con_log(CL_ANN1, (CE_NOTE, "tbolt_kill_adapter: " "Writing to doorbell with MFI_STOP_ADP ")); mutex_enter(&instance->ocr_flags_mtx); instance->deadadapter = 1; mutex_exit(&instance->ocr_flags_mtx); instance->func_ptr->disable_intr(instance); WR_RESERVED0_REGISTER(MFI_STOP_ADP, instance); /* Flush */ (void) RD_RESERVED0_REGISTER(instance); (void) mrsas_print_pending_cmds(instance); (void) mrsas_complete_pending_cmds(instance); } void mrsas_reset_reply_desc(struct mrsas_instance *instance) { int i; MPI2_REPLY_DESCRIPTORS_UNION *reply_desc; instance->reply_read_index = 0; /* initializing reply address to 0xFFFFFFFF */ reply_desc = instance->reply_frame_pool; for (i = 0; i < instance->reply_q_depth; i++) { reply_desc->Words = (uint64_t)~0; reply_desc++; } } int mrsas_tbolt_reset_ppc(struct mrsas_instance *instance) { uint32_t status = 0x00; uint32_t retry = 0; uint32_t cur_abs_reg_val; uint32_t fw_state; uint32_t abs_state; uint32_t i; if (instance->deadadapter == 1) { dev_err(instance->dip, CE_WARN, "mrsas_tbolt_reset_ppc: " "no more resets as HBA has been marked dead"); return (DDI_FAILURE); } mutex_enter(&instance->ocr_flags_mtx); instance->adapterresetinprogress = 1; mutex_exit(&instance->ocr_flags_mtx); instance->func_ptr->disable_intr(instance); /* Add delay in order to complete the ioctl & io cmds in-flight */ for (i = 0; i < 3000; i++) drv_usecwait(MILLISEC); /* wait for 1000 usecs */ instance->reply_read_index = 0; retry_reset: con_log(CL_ANN, (CE_NOTE, "mrsas_tbolt_reset_ppc: Resetting TBOLT")); /* Flush */ WR_TBOLT_IB_WRITE_SEQ(0x0, instance); /* Write magic number */ WR_TBOLT_IB_WRITE_SEQ(0xF, instance); WR_TBOLT_IB_WRITE_SEQ(0x4, instance); WR_TBOLT_IB_WRITE_SEQ(0xb, instance); WR_TBOLT_IB_WRITE_SEQ(0x2, instance); WR_TBOLT_IB_WRITE_SEQ(0x7, instance); WR_TBOLT_IB_WRITE_SEQ(0xd, instance); con_log(CL_ANN1, (CE_NOTE, "mrsas_tbolt_reset_ppc: magic number written " "to write sequence register")); /* Wait for the diag write enable (DRWE) bit to be set */ retry = 0; status = RD_TBOLT_HOST_DIAG(instance); while (!(status & DIAG_WRITE_ENABLE)) { delay(100 * drv_usectohz(MILLISEC)); status = RD_TBOLT_HOST_DIAG(instance); if (retry++ >= 100) { dev_err(instance->dip, CE_WARN, "%s(): timeout waiting for DRWE.", __func__); return (DDI_FAILURE); } } /* Send reset command */ WR_TBOLT_HOST_DIAG(status | DIAG_TBOLT_RESET_ADAPTER, instance); delay(100 * drv_usectohz(MILLISEC)); /* Wait for reset bit to clear */ retry = 0; status = RD_TBOLT_HOST_DIAG(instance); while ((status & DIAG_TBOLT_RESET_ADAPTER)) { delay(100 * drv_usectohz(MILLISEC)); status = RD_TBOLT_HOST_DIAG(instance); if (retry++ == 100) { /* Dont call kill adapter here */ /* RESET BIT ADAPTER is cleared by firmare */ /* mrsas_tbolt_kill_adapter(instance); */ dev_err(instance->dip, CE_WARN, "%s(): RESET FAILED; return failure!!!", __func__); return (DDI_FAILURE); } } con_log(CL_ANN, (CE_NOTE, "mrsas_tbolt_reset_ppc: Adapter reset complete")); abs_state = instance->func_ptr->read_fw_status_reg(instance); retry = 0; while ((abs_state <= MFI_STATE_FW_INIT) && (retry++ < 1000)) { delay(100 * drv_usectohz(MILLISEC)); abs_state = instance->func_ptr->read_fw_status_reg(instance); } if (abs_state <= MFI_STATE_FW_INIT) { dev_err(instance->dip, CE_WARN, "mrsas_tbolt_reset_ppc: firmware state < MFI_STATE_FW_INIT" "state = 0x%x, RETRY RESET.", abs_state); goto retry_reset; } /* Mark HBA as bad, if FW is fault after 3 continuous resets */ if (mfi_state_transition_to_ready(instance) || mrsas_debug_tbolt_fw_faults_after_ocr == 1) { cur_abs_reg_val = instance->func_ptr->read_fw_status_reg(instance); fw_state = cur_abs_reg_val & MFI_STATE_MASK; con_log(CL_ANN1, (CE_NOTE, "mrsas_tbolt_reset_ppc :before fake: FW is not ready " "FW state = 0x%x", fw_state)); if (mrsas_debug_tbolt_fw_faults_after_ocr == 1) fw_state = MFI_STATE_FAULT; con_log(CL_ANN, (CE_NOTE, "mrsas_tbolt_reset_ppc : FW is not ready " "FW state = 0x%x", fw_state)); if (fw_state == MFI_STATE_FAULT) { /* increment the count */ instance->fw_fault_count_after_ocr++; if (instance->fw_fault_count_after_ocr < MAX_FW_RESET_COUNT) { dev_err(instance->dip, CE_WARN, "mrsas_tbolt_reset_ppc: " "FW is in fault after OCR count %d " "Retry Reset", instance->fw_fault_count_after_ocr); goto retry_reset; } else { dev_err(instance->dip, CE_WARN, "%s:" "Max Reset Count exceeded >%d" "Mark HBA as bad, KILL adapter", __func__, MAX_FW_RESET_COUNT); mrsas_tbolt_kill_adapter(instance); return (DDI_FAILURE); } } } /* reset the counter as FW is up after OCR */ instance->fw_fault_count_after_ocr = 0; mrsas_reset_reply_desc(instance); abs_state = mrsas_issue_init_mpi2(instance); if (abs_state == (uint32_t)DDI_FAILURE) { dev_err(instance->dip, CE_WARN, "mrsas_tbolt_reset_ppc: " "INIT failed Retrying Reset"); goto retry_reset; } (void) mrsas_print_pending_cmds(instance); instance->func_ptr->enable_intr(instance); instance->fw_outstanding = 0; (void) mrsas_issue_pending_cmds(instance); instance->aen_cmd->retry_count_for_ocr = 0; instance->aen_cmd->drv_pkt_time = 0; instance->func_ptr->issue_cmd(instance->aen_cmd, instance); mutex_enter(&instance->ocr_flags_mtx); instance->adapterresetinprogress = 0; mutex_exit(&instance->ocr_flags_mtx); dev_err(instance->dip, CE_NOTE, "TBOLT adapter reset successfully"); return (DDI_SUCCESS); } /* * mrsas_sync_map_info - Returns FW's ld_map structure * @instance: Adapter soft state * * Issues an internal command (DCMD) to get the FW's controller PD * list structure. This information is mainly used to find out SYSTEM * supported by the FW. */ static int mrsas_tbolt_sync_map_info(struct mrsas_instance *instance) { int ret = 0, i; struct mrsas_cmd *cmd = NULL; struct mrsas_dcmd_frame *dcmd; uint32_t size_sync_info, num_lds; LD_TARGET_SYNC *ci = NULL; MR_FW_RAID_MAP_ALL *map; MR_LD_RAID *raid; LD_TARGET_SYNC *ld_sync; uint32_t ci_h = 0; uint32_t size_map_info; cmd = get_raid_msg_pkt(instance); if (cmd == NULL) { dev_err(instance->dip, CE_WARN, "Failed to get a cmd from free-pool in " "mrsas_tbolt_sync_map_info()."); return (DDI_FAILURE); } /* Clear the frame buffer and assign back the context id */ bzero((char *)&cmd->frame[0], sizeof (union mrsas_frame)); ddi_put32(cmd->frame_dma_obj.acc_handle, &cmd->frame->hdr.context, cmd->index); bzero(cmd->scsi_io_request, sizeof (Mpi2RaidSCSIIORequest_t)); map = instance->ld_map[instance->map_id & 1]; num_lds = map->raidMap.ldCount; dcmd = &cmd->frame->dcmd; size_sync_info = sizeof (LD_TARGET_SYNC) * num_lds; con_log(CL_ANN, (CE_NOTE, "size_sync_info =0x%x ; ld count = 0x%x", size_sync_info, num_lds)); ci = (LD_TARGET_SYNC *)instance->ld_map[(instance->map_id - 1) & 1]; bzero(ci, sizeof (MR_FW_RAID_MAP_ALL)); ci_h = instance->ld_map_phy[(instance->map_id - 1) & 1]; bzero(dcmd->mbox.b, DCMD_MBOX_SZ); ld_sync = (LD_TARGET_SYNC *)ci; for (i = 0; i < num_lds; i++, ld_sync++) { raid = MR_LdRaidGet(i, map); con_log(CL_ANN1, (CE_NOTE, "i : 0x%x, Seq Num : 0x%x, Sync Reqd : 0x%x", i, raid->seqNum, raid->flags.ldSyncRequired)); ld_sync->ldTargetId = MR_GetLDTgtId(i, map); con_log(CL_ANN1, (CE_NOTE, "i : 0x%x, tgt : 0x%x", i, ld_sync->ldTargetId)); ld_sync->seqNum = raid->seqNum; } size_map_info = sizeof (MR_FW_RAID_MAP) + (sizeof (MR_LD_SPAN_MAP) * (MAX_LOGICAL_DRIVES - 1)); dcmd->cmd = MFI_CMD_OP_DCMD; dcmd->cmd_status = 0xFF; dcmd->sge_count = 1; dcmd->flags = MFI_FRAME_DIR_WRITE; dcmd->timeout = 0; dcmd->pad_0 = 0; dcmd->data_xfer_len = size_map_info; ASSERT(num_lds <= 255); dcmd->mbox.b[0] = (U8)num_lds; dcmd->mbox.b[1] = 1; /* Pend */ dcmd->opcode = MR_DCMD_LD_MAP_GET_INFO; dcmd->sgl.sge32[0].phys_addr = ci_h; dcmd->sgl.sge32[0].length = size_map_info; instance->map_update_cmd = cmd; mr_sas_tbolt_build_mfi_cmd(instance, cmd); instance->func_ptr->issue_cmd(cmd, instance); instance->unroll.syncCmd = 1; con_log(CL_ANN1, (CE_NOTE, "sync cmd issued. [SMID]:%x", cmd->SMID)); return (ret); } /* * abort_syncmap_cmd */ int abort_syncmap_cmd(struct mrsas_instance *instance, struct mrsas_cmd *cmd_to_abort) { int ret = 0; struct mrsas_cmd *cmd; struct mrsas_abort_frame *abort_fr; con_log(CL_ANN1, (CE_NOTE, "chkpnt: abort_ldsync:%d", __LINE__)); cmd = get_raid_msg_mfi_pkt(instance); if (!cmd) { dev_err(instance->dip, CE_WARN, "Failed to get a cmd from free-pool abort_syncmap_cmd()."); return (DDI_FAILURE); } /* Clear the frame buffer and assign back the context id */ bzero((char *)&cmd->frame[0], sizeof (union mrsas_frame)); ddi_put32(cmd->frame_dma_obj.acc_handle, &cmd->frame->hdr.context, cmd->index); abort_fr = &cmd->frame->abort; /* prepare and issue the abort frame */ ddi_put8(cmd->frame_dma_obj.acc_handle, &abort_fr->cmd, MFI_CMD_OP_ABORT); ddi_put8(cmd->frame_dma_obj.acc_handle, &abort_fr->cmd_status, MFI_CMD_STATUS_SYNC_MODE); ddi_put16(cmd->frame_dma_obj.acc_handle, &abort_fr->flags, 0); ddi_put32(cmd->frame_dma_obj.acc_handle, &abort_fr->abort_context, cmd_to_abort->index); ddi_put32(cmd->frame_dma_obj.acc_handle, &abort_fr->abort_mfi_phys_addr_lo, cmd_to_abort->frame_phys_addr); ddi_put32(cmd->frame_dma_obj.acc_handle, &abort_fr->abort_mfi_phys_addr_hi, 0); cmd->frame_count = 1; mr_sas_tbolt_build_mfi_cmd(instance, cmd); if (instance->func_ptr->issue_cmd_in_poll_mode(instance, cmd)) { con_log(CL_ANN1, (CE_WARN, "abort_ldsync_cmd: issue_cmd_in_poll_mode failed")); ret = -1; } else { ret = 0; } return_raid_msg_mfi_pkt(instance, cmd); atomic_add_16(&instance->fw_outstanding, (-1)); return (ret); } /* * Even though these functions were originally intended for 2208 only, it * turns out they're useful for "Skinny" support as well. In a perfect world, * these two functions would be either in mr_sas.c, or in their own new source * file. Since this driver needs some cleanup anyway, keep this portion in * mind as well. */ int mrsas_tbolt_config_pd(struct mrsas_instance *instance, uint16_t tgt, uint8_t lun, dev_info_t **ldip) { struct scsi_device *sd; dev_info_t *child; int rval, dtype; struct mrsas_tbolt_pd_info *pds = NULL; con_log(CL_ANN1, (CE_NOTE, "mrsas_tbolt_config_pd: t = %d l = %d", tgt, lun)); if ((child = mrsas_find_child(instance, tgt, lun)) != NULL) { if (ldip) { *ldip = child; } if (instance->mr_tbolt_pd_list[tgt].flag != MRDRV_TGT_VALID) { rval = mrsas_service_evt(instance, tgt, 1, MRSAS_EVT_UNCONFIG_TGT, 0); con_log(CL_ANN1, (CE_WARN, "mr_sas:DELETING STALE ENTRY rval = %d " "tgt id = %d", rval, tgt)); return (NDI_FAILURE); } return (NDI_SUCCESS); } pds = (struct mrsas_tbolt_pd_info *) kmem_zalloc(sizeof (struct mrsas_tbolt_pd_info), KM_SLEEP); mrsas_tbolt_get_pd_info(instance, pds, tgt); dtype = pds->scsiDevType; /* Check for Disk */ if ((dtype == DTYPE_DIRECT)) { if ((dtype == DTYPE_DIRECT) && (LE_16(pds->fwState) != PD_SYSTEM)) { kmem_free(pds, sizeof (struct mrsas_tbolt_pd_info)); return (NDI_FAILURE); } sd = kmem_zalloc(sizeof (struct scsi_device), KM_SLEEP); sd->sd_address.a_hba_tran = instance->tran; sd->sd_address.a_target = (uint16_t)tgt; sd->sd_address.a_lun = (uint8_t)lun; if (scsi_hba_probe(sd, NULL) == SCSIPROBE_EXISTS) { rval = mrsas_config_scsi_device(instance, sd, ldip); dev_err(instance->dip, CE_CONT, "?Phys. device found: tgt %d dtype %d: %s\n", tgt, dtype, sd->sd_inq->inq_vid); } else { rval = NDI_FAILURE; con_log(CL_DLEVEL1, (CE_NOTE, "Phys. device Not found " "scsi_hba_probe Failed: tgt %d dtype %d: %s", tgt, dtype, sd->sd_inq->inq_vid)); } /* sd_unprobe is blank now. Free buffer manually */ if (sd->sd_inq) { kmem_free(sd->sd_inq, SUN_INQSIZE); sd->sd_inq = (struct scsi_inquiry *)NULL; } kmem_free(sd, sizeof (struct scsi_device)); } else { con_log(CL_ANN1, (CE_NOTE, "?Device not supported: tgt %d lun %d dtype %d", tgt, lun, dtype)); rval = NDI_FAILURE; } kmem_free(pds, sizeof (struct mrsas_tbolt_pd_info)); con_log(CL_ANN1, (CE_NOTE, "mrsas_config_pd: return rval = %d", rval)); return (rval); } static void mrsas_tbolt_get_pd_info(struct mrsas_instance *instance, struct mrsas_tbolt_pd_info *pds, int tgt) { struct mrsas_cmd *cmd; struct mrsas_dcmd_frame *dcmd; dma_obj_t dcmd_dma_obj; ASSERT(instance->tbolt || instance->skinny); if (instance->tbolt) cmd = get_raid_msg_pkt(instance); else cmd = mrsas_get_mfi_pkt(instance); if (!cmd) { con_log(CL_ANN1, (CE_WARN, "Failed to get a cmd for get pd info")); return; } /* Clear the frame buffer and assign back the context id */ bzero((char *)&cmd->frame[0], sizeof (union mrsas_frame)); ddi_put32(cmd->frame_dma_obj.acc_handle, &cmd->frame->hdr.context, cmd->index); dcmd = &cmd->frame->dcmd; dcmd_dma_obj.size = sizeof (struct mrsas_tbolt_pd_info); dcmd_dma_obj.dma_attr = mrsas_generic_dma_attr; dcmd_dma_obj.dma_attr.dma_attr_addr_hi = 0xffffffff; dcmd_dma_obj.dma_attr.dma_attr_count_max = 0xffffffff; dcmd_dma_obj.dma_attr.dma_attr_sgllen = 1; dcmd_dma_obj.dma_attr.dma_attr_align = 1; (void) mrsas_alloc_dma_obj(instance, &dcmd_dma_obj, DDI_STRUCTURE_LE_ACC); bzero(dcmd_dma_obj.buffer, sizeof (struct mrsas_tbolt_pd_info)); bzero(dcmd->mbox.b, 12); ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->cmd, MFI_CMD_OP_DCMD); ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->cmd_status, 0); ddi_put8(cmd->frame_dma_obj.acc_handle, &dcmd->sge_count, 1); ddi_put16(cmd->frame_dma_obj.acc_handle, &dcmd->flags, MFI_FRAME_DIR_READ); ddi_put16(cmd->frame_dma_obj.acc_handle, &dcmd->timeout, 0); ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->data_xfer_len, sizeof (struct mrsas_tbolt_pd_info)); ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->opcode, MR_DCMD_PD_GET_INFO); ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->mbox.w[0], tgt); ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->sgl.sge32[0].length, sizeof (struct mrsas_tbolt_pd_info)); ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->sgl.sge32[0].phys_addr, dcmd_dma_obj.dma_cookie[0].dmac_address); cmd->sync_cmd = MRSAS_TRUE; cmd->frame_count = 1; if (instance->tbolt) mr_sas_tbolt_build_mfi_cmd(instance, cmd); instance->func_ptr->issue_cmd_in_sync_mode(instance, cmd); ddi_rep_get8(cmd->frame_dma_obj.acc_handle, (uint8_t *)pds, (uint8_t *)dcmd_dma_obj.buffer, sizeof (struct mrsas_tbolt_pd_info), DDI_DEV_AUTOINCR); (void) mrsas_free_dma_obj(instance, dcmd_dma_obj); if (instance->tbolt) return_raid_msg_pkt(instance, cmd); else mrsas_return_mfi_pkt(instance, cmd); }