/* * Function name: twa_interrupt * Description: Interrupt handler. Determines the kind of interrupt, * and returns TW_CL_TRUE if it recognizes the interrupt. * * Input: ctlr_handle -- controller handle * Output: None * Return value: TW_CL_TRUE -- interrupt recognized * TW_CL_FALSE-- interrupt not recognized */ TW_INT32 tw_cl_interrupt(struct tw_cl_ctlr_handle *ctlr_handle) { struct tw_cli_ctlr_context *ctlr = (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt); TW_UINT32 status_reg; TW_INT32 rc = TW_CL_FALSE; tw_cli_dbg_printf(10, ctlr_handle, tw_osl_cur_func(), "entered"); /* If we don't have controller context, bail */ if (ctlr == NULL) goto out; /* * Bail If we get an interrupt while resetting, or shutting down. */ if (ctlr->reset_in_progress || !(ctlr->active)) goto out; /* Read the status register to determine the type of interrupt. */ status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr_handle); if (tw_cli_check_ctlr_state(ctlr, status_reg)) goto out; /* Clear the interrupt. */ if (status_reg & TWA_STATUS_HOST_INTERRUPT) { tw_cli_dbg_printf(6, ctlr_handle, tw_osl_cur_func(), "Host interrupt"); TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle, TWA_CONTROL_CLEAR_HOST_INTERRUPT); } if (status_reg & TWA_STATUS_ATTENTION_INTERRUPT) { tw_cli_dbg_printf(6, ctlr_handle, tw_osl_cur_func(), "Attention interrupt"); rc |= TW_CL_TRUE; /* request for a deferred isr call */ tw_cli_process_attn_intr(ctlr); TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle, TWA_CONTROL_CLEAR_ATTENTION_INTERRUPT); } if (status_reg & TWA_STATUS_COMMAND_INTERRUPT) { tw_cli_dbg_printf(6, ctlr_handle, tw_osl_cur_func(), "Command interrupt"); rc |= TW_CL_TRUE; /* request for a deferred isr call */ tw_cli_process_cmd_intr(ctlr); if ((TW_CL_Q_FIRST_ITEM(&(ctlr->req_q_head[TW_CLI_PENDING_Q]))) == TW_CL_NULL) TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle, TWA_CONTROL_MASK_COMMAND_INTERRUPT); } if (status_reg & TWA_STATUS_RESPONSE_INTERRUPT) { tw_cli_dbg_printf(10, ctlr_handle, tw_osl_cur_func(), "Response interrupt"); rc |= TW_CL_TRUE; /* request for a deferred isr call */ tw_cli_process_resp_intr(ctlr); } out: return(rc); }
/* * Function name: twa_setup * Description: Disables interrupts on the controller * * Input: ctlr -- ptr to CL internal ctlr context * Output: None * Return value: None */ TW_VOID tw_cli_disable_interrupts(struct tw_cli_ctlr_context *ctlr) { tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle, TWA_CONTROL_DISABLE_INTERRUPTS); ctlr->interrupts_enabled = TW_CL_FALSE; }
/* * Function name: tw_cli_enable_interrupts * Description: Enables interrupts on the controller * * Input: ctlr -- ptr to CL internal ctlr context * Output: None * Return value: None */ TW_VOID tw_cli_enable_interrupts(struct tw_cli_ctlr_context *ctlr) { tw_cli_dbg_printf(3, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); ctlr->interrupts_enabled = TW_CL_TRUE; TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle, TWA_CONTROL_CLEAR_ATTENTION_INTERRUPT | TWA_CONTROL_UNMASK_RESPONSE_INTERRUPT | TWA_CONTROL_ENABLE_INTERRUPTS); }
/* * Function name: tw_cl_start_io * Description: Interface to OS Layer for accepting SCSI requests. * * Input: ctlr_handle -- controller handle * req_pkt -- OSL built request packet * req_handle -- request handle * Output: None * Return value: 0 -- success * non-zero-- failure */ TW_INT32 tw_cl_start_io(struct tw_cl_ctlr_handle *ctlr_handle, struct tw_cl_req_packet *req_pkt, struct tw_cl_req_handle *req_handle) { struct tw_cli_ctlr_context *ctlr; struct tw_cli_req_context *req; struct tw_cl_command_9k *cmd; struct tw_cl_scsi_req_packet *scsi_req; TW_INT32 error = TW_CL_ERR_REQ_SUCCESS; tw_cli_dbg_printf(10, ctlr_handle, tw_osl_cur_func(), "entered"); ctlr = (struct tw_cli_ctlr_context *)(ctlr_handle->cl_ctlr_ctxt); /* * If working with a firmware version that does not support multiple * luns, and this request is directed at a non-zero lun, error it * back right away. */ if ((req_pkt->gen_req_pkt.scsi_req.lun) && (ctlr->working_srl < TWA_MULTI_LUN_FW_SRL)) { req_pkt->status |= (TW_CL_ERR_REQ_INVALID_LUN | TW_CL_ERR_REQ_SCSI_ERROR); req_pkt->tw_osl_callback(req_handle); return(TW_CL_ERR_REQ_SUCCESS); } if ((req = tw_cli_get_request(ctlr )) == TW_CL_NULL) { tw_cli_dbg_printf(2, ctlr_handle, tw_osl_cur_func(), "Out of request context packets: returning busy"); return(TW_OSL_EBUSY); } req_handle->cl_req_ctxt = req; req->req_handle = req_handle; req->orig_req = req_pkt; req->tw_cli_callback = tw_cli_complete_io; req->flags |= TW_CLI_REQ_FLAGS_EXTERNAL; req->flags |= TW_CLI_REQ_FLAGS_9K; scsi_req = &(req_pkt->gen_req_pkt.scsi_req); /* Build the cmd pkt. */ cmd = &(req->cmd_pkt->command.cmd_pkt_9k); req->cmd_pkt->cmd_hdr.header_desc.size_header = 128; cmd->res__opcode = BUILD_RES__OPCODE(0, TWA_FW_CMD_EXECUTE_SCSI); cmd->unit = (TW_UINT8)(scsi_req->unit); cmd->lun_l4__req_id = TW_CL_SWAP16( BUILD_LUN_L4__REQ_ID(scsi_req->lun, req->request_id)); cmd->status = 0; cmd->sgl_offset = 16; /* offset from end of hdr = max cdb len */ tw_osl_memcpy(cmd->cdb, scsi_req->cdb, scsi_req->cdb_len); if (req_pkt->flags & TW_CL_REQ_CALLBACK_FOR_SGLIST) { TW_UINT32 num_sgl_entries; req_pkt->tw_osl_sgl_callback(req_handle, cmd->sg_list, &num_sgl_entries); cmd->lun_h4__sgl_entries = TW_CL_SWAP16(BUILD_LUN_H4__SGL_ENTRIES(scsi_req->lun, num_sgl_entries)); } else { cmd->lun_h4__sgl_entries = TW_CL_SWAP16(BUILD_LUN_H4__SGL_ENTRIES(scsi_req->lun, scsi_req->sgl_entries)); tw_cli_fill_sg_list(ctlr, scsi_req->sg_list, cmd->sg_list, scsi_req->sgl_entries); } if (((TW_CL_Q_FIRST_ITEM(&(ctlr->req_q_head[TW_CLI_PENDING_Q]))) != TW_CL_NULL) || (ctlr->reset_in_progress)) { tw_cli_req_q_insert_tail(req, TW_CLI_PENDING_Q); TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle, TWA_CONTROL_UNMASK_COMMAND_INTERRUPT); } else if ((error = tw_cli_submit_cmd(req))) { tw_cli_dbg_printf(2, ctlr_handle, tw_osl_cur_func(), "Could not start request. request = %p, error = %d", req, error); tw_cli_req_q_insert_tail(req, TW_CLI_FREE_Q); } return(error); }
/* * Function name: tw_cli_submit_cmd * Description: Submits a cmd to firmware. * * Input: req -- ptr to CL internal request context * Output: None * Return value: 0 -- success * non-zero-- failure */ TW_INT32 tw_cli_submit_cmd(struct tw_cli_req_context *req) { struct tw_cli_ctlr_context *ctlr = req->ctlr; struct tw_cl_ctlr_handle *ctlr_handle = ctlr->ctlr_handle; TW_UINT32 status_reg; TW_INT32 error = 0; tw_cli_dbg_printf(10, ctlr_handle, tw_osl_cur_func(), "entered"); /* Serialize access to the controller cmd queue. */ tw_osl_get_lock(ctlr_handle, ctlr->io_lock); /* For 9650SE first write low 4 bytes */ if ((ctlr->device_id == TW_CL_DEVICE_ID_9K_E) || (ctlr->device_id == TW_CL_DEVICE_ID_9K_SA)) tw_osl_write_reg(ctlr_handle, TWA_COMMAND_QUEUE_OFFSET_LOW, (TW_UINT32)(req->cmd_pkt_phys + sizeof(struct tw_cl_command_header)), 4); status_reg = TW_CLI_READ_STATUS_REGISTER(ctlr_handle); if (status_reg & TWA_STATUS_COMMAND_QUEUE_FULL) { struct tw_cl_req_packet *req_pkt = (struct tw_cl_req_packet *)(req->orig_req); tw_cli_dbg_printf(7, ctlr_handle, tw_osl_cur_func(), "Cmd queue full"); if ((req->flags & TW_CLI_REQ_FLAGS_INTERNAL) || ((req_pkt) && (req_pkt->flags & TW_CL_REQ_RETRY_ON_BUSY)) ) { if (req->state != TW_CLI_REQ_STATE_PENDING) { tw_cli_dbg_printf(2, ctlr_handle, tw_osl_cur_func(), "pending internal/ioctl request"); req->state = TW_CLI_REQ_STATE_PENDING; tw_cli_req_q_insert_tail(req, TW_CLI_PENDING_Q); /* Unmask command interrupt. */ TW_CLI_WRITE_CONTROL_REGISTER(ctlr_handle, TWA_CONTROL_UNMASK_COMMAND_INTERRUPT); } else error = TW_OSL_EBUSY; } else { error = TW_OSL_EBUSY; } } else { tw_cli_dbg_printf(10, ctlr_handle, tw_osl_cur_func(), "Submitting command"); /* Insert command into busy queue */ req->state = TW_CLI_REQ_STATE_BUSY; tw_cli_req_q_insert_tail(req, TW_CLI_BUSY_Q); if ((ctlr->device_id == TW_CL_DEVICE_ID_9K_E) || (ctlr->device_id == TW_CL_DEVICE_ID_9K_SA)) { /* Now write the high 4 bytes */ tw_osl_write_reg(ctlr_handle, TWA_COMMAND_QUEUE_OFFSET_HIGH, (TW_UINT32)(((TW_UINT64)(req->cmd_pkt_phys + sizeof(struct tw_cl_command_header)))>>32), 4); } else { if (ctlr->flags & TW_CL_64BIT_ADDRESSES) {
/* * Function name: tw_cli_check_ctlr_state * Description: Makes sure that the fw status register reports a * proper status. * * Input: ctlr -- ptr to CL internal ctlr context * status_reg-- value in the status register * Output: None * Return value: 0 -- no errors * non-zero-- errors */ TW_INT32 tw_cli_check_ctlr_state(struct tw_cli_ctlr_context *ctlr, TW_UINT32 status_reg) { struct tw_cl_ctlr_handle *ctlr_handle = ctlr->ctlr_handle; TW_INT32 error = TW_OSL_ESUCCESS; tw_cli_dbg_printf(8, ctlr->ctlr_handle, tw_osl_cur_func(), "entered"); /* Check if the 'micro-controller ready' bit is not set. */ if (!(status_reg & TWA_STATUS_MICROCONTROLLER_READY)) { TW_INT8 desc[200]; tw_osl_memzero(desc, 200); if (!(ctlr->reset_phase1_in_progress)) { tw_cl_create_event(ctlr_handle, TW_CL_FALSE, TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT, 0x1301, 0x1, TW_CL_SEVERITY_ERROR_STRING, "Missing expected status bit(s)", "status reg = 0x%x; Missing bits: %s", status_reg, tw_cli_describe_bits( TWA_STATUS_MICROCONTROLLER_READY, desc)); error = TW_OSL_EGENFAILURE; } } /* Check if any error bits are set. */ if ((status_reg & TWA_STATUS_UNEXPECTED_BITS) != 0) { TW_INT8 desc[200]; tw_osl_memzero(desc, 200); /* Skip queue error msgs during 9650SE/9690SA reset */ if (((ctlr->device_id != TW_CL_DEVICE_ID_9K_E) && (ctlr->device_id != TW_CL_DEVICE_ID_9K_SA)) || (!(ctlr->reset_in_progress)) || ((status_reg & TWA_STATUS_QUEUE_ERROR_INTERRUPT) == 0)) tw_cl_create_event(ctlr_handle, TW_CL_FALSE, TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT, 0x1302, 0x1, TW_CL_SEVERITY_ERROR_STRING, "Unexpected status bit(s)", "status reg = 0x%x Unexpected bits: %s", status_reg & TWA_STATUS_UNEXPECTED_BITS, tw_cli_describe_bits(status_reg & TWA_STATUS_UNEXPECTED_BITS, desc)); if (status_reg & TWA_STATUS_PCI_PARITY_ERROR_INTERRUPT) { tw_cl_create_event(ctlr_handle, TW_CL_FALSE, TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT, 0x1303, 0x1, TW_CL_SEVERITY_ERROR_STRING, "PCI parity error: clearing... " "Re-seat/move/replace card", "status reg = 0x%x %s", status_reg, tw_cli_describe_bits(status_reg, desc)); TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle, TWA_CONTROL_CLEAR_PARITY_ERROR); #ifdef TW_OSL_PCI_CONFIG_ACCESSIBLE tw_osl_write_pci_config(ctlr->ctlr_handle, TW_CLI_PCI_CONFIG_STATUS_OFFSET, TWA_PCI_CONFIG_CLEAR_PARITY_ERROR, 2); #endif /* TW_OSL_PCI_CONFIG_ACCESSIBLE */ } if (status_reg & TWA_STATUS_PCI_ABORT_INTERRUPT) { tw_cl_create_event(ctlr_handle, TW_CL_FALSE, TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT, 0x1304, 0x1, TW_CL_SEVERITY_ERROR_STRING, "PCI abort: clearing... ", "status reg = 0x%x %s", status_reg, tw_cli_describe_bits(status_reg, desc)); TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle, TWA_CONTROL_CLEAR_PCI_ABORT); #ifdef TW_OSL_PCI_CONFIG_ACCESSIBLE tw_osl_write_pci_config(ctlr->ctlr_handle, TW_CLI_PCI_CONFIG_STATUS_OFFSET, TWA_PCI_CONFIG_CLEAR_PCI_ABORT, 2); #endif /* TW_OSL_PCI_CONFIG_ACCESSIBLE */ } if (status_reg & TWA_STATUS_QUEUE_ERROR_INTERRUPT) { /* Skip queue error msgs during 9650SE/9690SA reset */ if (((ctlr->device_id != TW_CL_DEVICE_ID_9K_E) && (ctlr->device_id != TW_CL_DEVICE_ID_9K_SA)) || (!(ctlr->reset_in_progress))) tw_cl_create_event(ctlr_handle, TW_CL_FALSE, TW_CL_MESSAGE_SOURCE_COMMON_LAYER_EVENT, 0x1305, 0x1, TW_CL_SEVERITY_ERROR_STRING, "Controller queue error: clearing... ", "status reg = 0x%x %s", status_reg, tw_cli_describe_bits(status_reg, desc)); TW_CLI_WRITE_CONTROL_REGISTER(ctlr->ctlr_handle, TWA_CONTROL_CLEAR_QUEUE_ERROR); } } return(error); }