/* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright (C) 2013 Hewlett-Packard Development Company, L.P. */ /* * This module contains routines that program the controller. All * operations viz., initialization of controller, submision & * retrieval of commands, enabling & disabling of interrupts, * checking interrupt status are performed here. */ #include #include "cpqary3.h" /* * Local Functions Definitions */ uint8_t cpqary3_check_simple_ctlr_intr(cpqary3_t *cpqary3p); uint8_t cpqary3_check_perf_ctlr_intr(cpqary3_t *cpqary3p); uint8_t cpqary3_check_perf_e200_intr(cpqary3_t *cpqary3p); uint8_t cpqary3_check_ctlr_init(cpqary3_t *); /* * Function : cpqary3_check_simple_ctlr_intr * Description : This routine determines if the controller did interrupt. * Called By : cpqary3_hw_isr() * Parameters : per-controller * Calls : None * Return Values: SUCCESS : This controller did interrupt. * FAILURE : It did not. */ uint8_t cpqary3_check_simple_ctlr_intr(cpqary3_t *cpqary3p) { uint32_t intr_pending_mask = 0; /* * Read the Interrupt Status Register and * if bit 3 is set, it indicates that we have completed commands * in the controller */ intr_pending_mask = cpqary3p->bddef->bd_intrpendmask; if (intr_pending_mask & (ddi_get32(cpqary3p->isr_handle, (uint32_t *)cpqary3p->isr))) return (CPQARY3_SUCCESS); return (CPQARY3_FAILURE); } /* * Function : cpqary3_check_perf_ctlr_intr * Description : This routine determines if the * controller did interrupt. * Called By : cpqary3_hw_isr() * Parameters : per-controller * Calls : None * Return Values: SUCCESS : This controller did interrupt. * FAILURE : It did not. */ uint8_t cpqary3_check_perf_ctlr_intr(cpqary3_t *cpqary3p) { /* * Read the Interrupt Status Register and * if bit 3 is set, it indicates that we have completed commands * in the controller */ if (0x1 & (ddi_get32(cpqary3p->isr_handle, (uint32_t *)cpqary3p->isr))) { return (CPQARY3_SUCCESS); } return (CPQARY3_FAILURE); } /* * Function : cpqary3_check_perf_e200_intr * Description : This routine determines if the controller * did interrupt. * Called By : cpqary3_hw_isr() * Parameters : per-controller * Calls : None * Return Values: SUCCESS : This controller did interrupt. * FAILURE : It did not. */ uint8_t cpqary3_check_perf_e200_intr(cpqary3_t *cpqary3p) { /* * Read the Interrupt Status Register and * if bit 3 is set, it indicates that we have completed commands * in the controller */ if (0x4 & (ddi_get32(cpqary3p->isr_handle, (uint32_t *)cpqary3p->isr))) { return (CPQARY3_SUCCESS); } return (CPQARY3_FAILURE); } /* * Function : cpqary3_retrieve * Description : This routine retrieves the completed command from the * controller reply queue. * and processes the completed commands. * Called By : cpqary3_sw_isr(), cpqary3_handle_flag_nointr() * Parameters : per-controller * Calls : packet completion routines * Return Values: SUCCESS : A completed command has been retrieved * and processed. * FAILURE : No completed command was in the controller. */ uint8_t cpqary3_retrieve(cpqary3_t *cpqary3p) { uint32_t tag; uint32_t CmdsOutMax; cpqary3_cmdpvt_t *cpqary3_cmdpvtp; cpqary3_drvr_replyq_t *replyq_ptr; /* * Get the Reply Command List Addr * Update the returned Tag in that particular command structure. * If a valid one, de-q that from the SUBMITTED Q and * enqueue that to the RETRIEVED Q. */ RETURN_FAILURE_IF_NULL(cpqary3p); /* PERF */ replyq_ptr = (cpqary3_drvr_replyq_t *)cpqary3p->drvr_replyq; CmdsOutMax = cpqary3p->ctlr_maxcmds; while ((replyq_ptr->replyq_headptr[0] & 0x01) == replyq_ptr->cyclic_indicator) { /* command has completed */ /* Get the tag */ tag = replyq_ptr->replyq_headptr[0]; if ((tag >> CPQARY3_GET_MEM_TAG) >= (CmdsOutMax / 3) * 3) { cmn_err(CE_WARN, "CPQary3 : HBA returned Spurious Tag"); return (CPQARY3_FAILURE); } cpqary3_cmdpvtp = &cpqary3p->cmdmemlistp->pool[ tag >> CPQARY3_GET_MEM_TAG]; cpqary3_cmdpvtp->cmdlist_memaddr-> Header.Tag.drvinfo_n_err = (tag & 0xF) >> 1; mutex_enter(&cpqary3p->sw_mutex); cpqary3_cmdpvtp->complete(cpqary3_cmdpvtp); mutex_exit(&cpqary3p->sw_mutex); /* Traverse to the next command in reply queue */ ++replyq_ptr->index; if (replyq_ptr->index == replyq_ptr->max_index) { replyq_ptr->index = 0; /* Toggle at wraparound */ replyq_ptr->cyclic_indicator = (replyq_ptr->cyclic_indicator == 0) ? 1 : 0; replyq_ptr->replyq_headptr = /* LINTED: alignment */ (uint32_t *)(replyq_ptr->replyq_start_addr); } else { replyq_ptr->replyq_headptr += 2; } } /* PERF */ return (CPQARY3_SUCCESS); } /* * Function : cpqary3_poll_retrieve * Description : This routine retrieves the completed command from the * controller reply queue in poll mode. * and processes the completed commands. * Called By : cpqary3_poll * Parameters : per-controller * Calls : packet completion routines * Return Values: If the polled command is completed, send back a success. * If not return failure. */ uint8_t cpqary3_poll_retrieve(cpqary3_t *cpqary3p, uint32_t poll_tag) { uint32_t tag; uint32_t CmdsOutMax; cpqary3_cmdpvt_t *cpqary3_cmdpvtp; cpqary3_drvr_replyq_t *replyq_ptr; uint32_t temp_tag; uint8_t tag_flag = 0; RETURN_FAILURE_IF_NULL(cpqary3p); /* PERF */ replyq_ptr = (cpqary3_drvr_replyq_t *)cpqary3p->drvr_replyq; CmdsOutMax = cpqary3p->cmdmemlistp->max_memcnt; if (!(cpqary3p->bddef->bd_flags & SA_BD_SAS)) { while ((tag = ddi_get32(cpqary3p->opq_handle, (uint32_t *)cpqary3p->opq)) != 0xFFFFFFFF) { cpqary3_cmdpvtp = &cpqary3p->cmdmemlistp->pool[ tag >> CPQARY3_GET_MEM_TAG]; cpqary3_cmdpvtp->cmdlist_memaddr-> Header.Tag.drvinfo_n_err = (tag & 0xF) >> 1; temp_tag = cpqary3_cmdpvtp->tag.tag_value; if (temp_tag == poll_tag) tag_flag = 1; cpqary3_cmdpvtp->complete(cpqary3_cmdpvtp); } } else { while ((replyq_ptr->replyq_headptr[0] & 0x01) == replyq_ptr->cyclic_indicator) { /* command has completed */ /* Get the tag */ tag = replyq_ptr->replyq_headptr[0]; if ((tag >> CPQARY3_GET_MEM_TAG) >= (CmdsOutMax/3)*3) { cmn_err(CE_WARN, "CPQary3 : HBA returned Spurious Tag"); return (CPQARY3_FAILURE); } cpqary3_cmdpvtp = &cpqary3p->cmdmemlistp->pool[ tag >> CPQARY3_GET_MEM_TAG]; cpqary3_cmdpvtp->cmdlist_memaddr-> Header.Tag.drvinfo_n_err = (tag & 0xF) >> 1; temp_tag = cpqary3_cmdpvtp->tag.tag_value; if (temp_tag == poll_tag) tag_flag = 1; cpqary3_cmdpvtp->complete(cpqary3_cmdpvtp); /* Traverse to the next command in reply queue */ ++replyq_ptr->index; if (replyq_ptr->index == replyq_ptr->max_index) { replyq_ptr->index = 0; /* Toggle at wraparound */ replyq_ptr->cyclic_indicator = (replyq_ptr->cyclic_indicator == 0) ? 1 : 0; replyq_ptr->replyq_headptr = /* LINTED: alignment */ (uint32_t *)(replyq_ptr->replyq_start_addr); } else { replyq_ptr->replyq_headptr += 2; } } } /* PERF */ if (tag_flag) { return (CPQARY3_SUCCESS); } return (CPQARY3_FAILURE); } /* * Function : cpqary3_submit * Description : This routine submits the command to the Inbound Post Q. * Called By : cpqary3_transport(), cpqary3_send_NOE_command(), * cpqary3_disable_NOE_command(), * cpqary3_handle_flag_nointr(), * cpqary3_tick_hdlr(), cpqary3_synccmd_send() * Parameters : per-controller, physical address * Calls : None * Return Values: None */ int32_t cpqary3_submit(cpqary3_t *cpqary3p, uint32_t cmd_phyaddr) { uint32_t phys_addr = 0; uint8_t retval = 0; /* * Write the Physical Address of the command-to-be-submitted * into the Controller's Inbound Post Q. */ ASSERT(cpqary3p != NULL); #ifdef AMD64_DEBUG { char debug_char; uint32_t tmp_cmd_phyaddr; tmp_cmd_phyaddr = (uint32_t)(cmd_phyaddr & 0XFFFFFFFF); cmn_err(CE_WARN, "CPQary3: cmd_phyaddr = %lX\n tmp_cmd_phyaddr = %lX", cmd_phyaddr, tmp_cmd_phyaddr); debug_enter(&debug_char); ddi_put32(cpqary3p->ipq_handle, (uint32_t *)cpqary3p->ipq, cmd_phyaddr); } #endif /* CONTROLLER_LOCKUP */ if (cpqary3p->controller_lockup == CPQARY3_TRUE) { retval = EIO; return (retval); } /* CONTROLLER_LOCKUP */ if (!(cpqary3p->bddef->bd_flags & SA_BD_SAS)) { ddi_put32(cpqary3p->ipq_handle, (uint32_t *)cpqary3p->ipq, cmd_phyaddr); } else { /* The driver always uses the 0th block fetch count always */ phys_addr = cmd_phyaddr | 0 | 0x1; ddi_put32(cpqary3p->ipq_handle, (uint32_t *)cpqary3p->ipq, phys_addr); } /* PERF */ /* * Command submission can NEVER FAIL since the number of commands that * can reside in the controller at any time is 1024 and our memory * allocation is for 225 commands ONLY. Thus, at any given time the * maximum number of commands in the controller is 225. */ /* CONTROLLER_LOCKUP */ return (retval); /* CONTROLLER_LOCKUP */ } /* * Function : cpqary3_intr_onoff * Description : This routine enables/disables the HBA interrupt. * Called By : cpqary3_attach(), ry3_handle_flag_nointr(), * cpqary3_tick_hdlr(), cpqary3_init_ctlr_resource() * Parameters : per-controller, flag stating enable/disable * Calls : None * Return Values: None */ void cpqary3_intr_onoff(cpqary3_t *cpqary3p, uint8_t flag) { uint32_t intr = 0; uint32_t intr_mask = 0; /* * Enable or disable the interrupt based on the flag * Read the Interrupt Mask Register first and then update it * accordingly */ ASSERT(cpqary3p != NULL); intr = ddi_get32(cpqary3p->imr_handle, (uint32_t *)cpqary3p->imr); intr_mask = cpqary3p->bddef->bd_intrmask; if (flag == CPQARY3_INTR_ENABLE) { ddi_put32(cpqary3p->imr_handle, (uint32_t *)cpqary3p->imr, intr & ~(intr_mask)); } else { ddi_put32(cpqary3p->imr_handle, (uint32_t *)cpqary3p->imr, (intr | intr_mask)); } } /* * Function : cpqary3_lockup_intr_onoff * Description : This routine enables/disables the lockup interrupt. * Called By : cpqary3_attach(), cpqary3_handle_flag_nointr(), * cpqary3_tick_hdlr(), cpqary3_hw_isr, * cpqary3_init_ctlr_resource() * Parameters : per-controller, flag stating enable/disable * Calls : None * Return Values: None */ void cpqary3_lockup_intr_onoff(cpqary3_t *cpqary3p, uint8_t flag) { uint32_t intr = 0; uint32_t intr_lockup_mask = 0; /* * Enable or disable the interrupt based on the flag * Read the Interrupt Mask Register first and then update it * accordingly */ ASSERT(cpqary3p != NULL); intr = ddi_get32(cpqary3p->imr_handle, (uint32_t *)cpqary3p->imr); intr_lockup_mask = cpqary3p->bddef->bd_lockup_intrmask; if (flag == CPQARY3_INTR_ENABLE) { ddi_put32(cpqary3p->imr_handle, (uint32_t *)cpqary3p->imr, intr & ~(intr_lockup_mask)); } else { ddi_put32(cpqary3p->imr_handle, (uint32_t *)cpqary3p->imr, (intr | intr_lockup_mask)); } } /* * Function : cpqary3_init_ctlr * Description : This routine initialises the HBA to Simple Transport * Method. Refer to CISS for more information. * It checks the readiness of the HBA. * Called By : cpqary3_init_ctlr_resource() * Parameters : per-controller(), physical address() * Calls : cpqary3_check_ctlr_init * Return Values: SUCCESS / FAILURE * [Shall return failure if the initialization of the * controller to the Simple Transport Method fails] */ uint8_t cpqary3_init_ctlr(cpqary3_t *cpqary3p) { uint8_t cntr; uint8_t signature[4] = { 'C', 'I', 'S', 'S' }; volatile CfgTable_t *ctp; volatile CfgTrans_Perf_t *perf_cfg; cpqary3_phyctg_t *cpqary3_phyctgp; uint32_t phy_addr; size_t cmd_size; uint32_t queue_depth; uint32_t CmdsOutMax; uint32_t BlockFetchCnt[8]; caddr_t replyq_start_addr = NULL; /* SG */ uint32_t max_blk_fetch_cnt = 0; uint32_t max_sg_cnt = 0; uint32_t optimal_sg = 0; uint32_t optimal_sg_size = 0; /* Header + Request + Error */ uint32_t size_of_HRE = 0; uint32_t size_of_cmdlist = 0; /* SG */ RETURN_FAILURE_IF_NULL(cpqary3p); ctp = (CfgTable_t *)cpqary3p->ct; perf_cfg = (CfgTrans_Perf_t *)cpqary3p->cp; /* QUEUE CHANGES */ cpqary3p->drvr_replyq = (cpqary3_drvr_replyq_t *)MEM_ZALLOC(sizeof (cpqary3_drvr_replyq_t)); /* QUEUE CHANGES */ if (!cpqary3_check_ctlr_init(cpqary3p)) return (CPQARY3_FAILURE); DTRACE_PROBE1(ctlr_init_start, CfgTable_t *, ctp); /* * Validate the signature - should be "CISS" * Use of cntr in the for loop does not suggest a counter - it just * saves declaration of another variable. */ for (cntr = 0; cntr < 4; cntr++) { if (DDI_GET8(cpqary3p, &ctp->Signature[cntr]) != signature[cntr]) { cmn_err(CE_WARN, "CPQary3 : Controller NOT ready"); cmn_err(CE_WARN, "CPQary3 : _cpqary3_init_ctlr : " "Signature not stamped"); return (CPQARY3_FAILURE); } } if (!(cpqary3p->bddef->bd_flags & SA_BD_SAS)) { CmdsOutMax = DDI_GET32(cpqary3p, &ctp->CmdsOutMax); if (CmdsOutMax == 0) { cmn_err(CE_CONT, "CPQary3 : HBA Maximum Outstanding " "Commands set to Zero\n"); cmn_err(CE_CONT, "CPQary3 : Cannot continue driver " "initialization \n"); return (CPQARY3_FAILURE); } cpqary3p->ctlr_maxcmds = CmdsOutMax; cpqary3p->sg_cnt = CPQARY3_SG_CNT; queue_depth = cpqary3p->ctlr_maxcmds; cmd_size = (8 * queue_depth); /* QUEUE CHANGES */ cpqary3p->drvr_replyq->cyclic_indicator = CPQARY3_REPLYQ_INIT_CYCLIC_IND; cpqary3p->drvr_replyq->simple_cyclic_indicator = CPQARY3_REPLYQ_INIT_CYCLIC_IND; cpqary3p->drvr_replyq->max_index = cpqary3p->ctlr_maxcmds; cpqary3p->drvr_replyq->simple_index = 0; replyq_start_addr = MEM_ZALLOC(cmd_size); bzero(replyq_start_addr, cmd_size); cpqary3p->drvr_replyq->replyq_headptr = /* LINTED: alignment */ (uint32_t *)replyq_start_addr; cpqary3p->drvr_replyq->replyq_simple_ptr = /* LINTED: alignment */ (uint32_t *)replyq_start_addr; cpqary3p->drvr_replyq->replyq_start_addr = replyq_start_addr; /* PERF */ /* * Check for support of SIMPLE Transport Method */ if (!(DDI_GET32(cpqary3p, &ctp->TransportSupport) & CFGTBL_XPORT_SIMPLE)) { cmn_err(CE_WARN, "CPQary3 : Controller " "NOT YET INITIALIZED"); cmn_err(CE_CONT, "CPQary3 : For Hot Plug Operations, " "try again later \n"); return (CPQARY3_FAILURE); } /* * Configuration Table Initialization * Set bit 0 of InBound Door Bell Reg to inform the controller * about the changes related to the Configuration table */ DTRACE_PROBE(cfgtable_init_start); DDI_PUT32(cpqary3p, &ctp->HostWrite.TransportRequest, CFGTBL_XPORT_SIMPLE); ddi_put32(cpqary3p->idr_handle, (uint32_t *)cpqary3p->idr, ddi_get32(cpqary3p->idr_handle, (uint32_t *)cpqary3p->idr) | CFGTBL_CHANGE_REQ); /* * Check whether the controller is ready */ cntr = 0; while (ddi_get32(cpqary3p->idr_handle, (uint32_t *)cpqary3p->idr) & CFGTBL_ACC_CMDS) { drv_usecwait(1000000); /* Wait for 1 Sec. */ cntr++; /* * Wait for a maximum of 90 seconds. No f/w should take * more than 90 secs to initialize. If the controller * is not ready even after 90 secs, it suggests that * something is wrong * (wrt the controller, what else) !!! */ if (cntr > CISS_INIT_TIME) /* 1.30 Mins */ { cmn_err(CE_CONT, "CPQary3 : Controller " "Initialization Failed \n"); return (CPQARY3_FAILURE); } } DTRACE_PROBE(cfgtable_init_done); /* * Check whether controller accepts the requested method of * transport */ if (!(DDI_GET32(cpqary3p, &ctp->TransportActive) & CFGTBL_XPORT_SIMPLE)) { cmn_err(CE_CONT, "CPQary3 : Failed to Initialize " "Controller \n"); cmn_err(CE_CONT, "CPQary3 : For Hot Plug Operations, " "try again later\n"); return (CPQARY3_FAILURE); } DTRACE_PROBE(ctlr_init_simple); /* * Check if Controller is ready to accept Commands */ if (!(DDI_GET32(cpqary3p, &ctp->TransportActive) & CFGTBL_ACC_CMDS)) { cmn_err(CE_CONT, "CPQary3: Controller NOT ready to " "accept Commands \n"); return (CPQARY3_FAILURE); } DTRACE_PROBE(ctlr_init_ready); /* * Check if the maximum number of oustanding commands for the * initialized controller is something greater than Zero. */ CmdsOutMax = DDI_GET32(cpqary3p, &ctp->CmdsOutMax); if (CmdsOutMax == 0) { cmn_err(CE_CONT, "CPQary3 : HBA Maximum Outstanding " "Commands set to Zero\n"); cmn_err(CE_CONT, "CPQary3 : Cannot continue driver " "initialization \n"); return (CPQARY3_FAILURE); } cpqary3p->ctlr_maxcmds = CmdsOutMax; /* * Zero the Upper 32 Address in the Controller */ DDI_PUT32(cpqary3p, &ctp->HostWrite.Upper32Addr, 0x00000000); cpqary3p->heartbeat = DDI_GET32(cpqary3p, &ctp->HeartBeat); /* Set the controller interrupt check routine */ cpqary3p->check_ctlr_intr = cpqary3_check_simple_ctlr_intr; cpqary3p->host_support = DDI_GET32(cpqary3p, &ctp->HostDrvrSupport); DDI_PUT32(cpqary3p, &ctp->HostDrvrSupport, (cpqary3p->host_support | 0x4)); cpqary3p->host_support = DDI_GET32(cpqary3p, &ctp->HostDrvrSupport); cpqary3p->lockup_logged = CPQARY3_FALSE; } else { /* PERF */ /* * Check for support of PERF Transport Method */ if (!(DDI_GET32(cpqary3p, &ctp->TransportSupport) & CFGTBL_XPORT_PERFORMANT)) { cmn_err(CE_WARN, "CPQary3 : Controller " "NOT YET INITIALIZED"); cmn_err(CE_CONT, "CPQary3 : For Hot Plug Operations, " "try again later \n"); return (CPQARY3_FAILURE); } CmdsOutMax = DDI_GET32(cpqary3p, &ctp->MaxPerfModeCmdsOutMax); if (CmdsOutMax == 0) CmdsOutMax = DDI_GET32(cpqary3p, &ctp->CmdsOutMax); if (CmdsOutMax == 0) { cmn_err(CE_CONT, "CPQary3 : HBA Maximum Outstanding " "Commands set to Zero\n"); cmn_err(CE_CONT, "CPQary3 : Cannot continue driver " "initialization \n"); return (CPQARY3_FAILURE); } cpqary3p->ctlr_maxcmds = CmdsOutMax; /* Initialize the Performant Method Transport Method Table */ queue_depth = cpqary3p->ctlr_maxcmds; DDI_PUT32_CP(cpqary3p, &perf_cfg->ReplyQSize, queue_depth); DDI_PUT32_CP(cpqary3p, &perf_cfg->ReplyQCount, 1); DDI_PUT32_CP(cpqary3p, &perf_cfg->ReplyQCntrAddrLow32, 0); DDI_PUT32_CP(cpqary3p, &perf_cfg->ReplyQCntrAddrHigh32, 0); cpqary3_phyctgp = (cpqary3_phyctg_t *)MEM_ZALLOC(sizeof (cpqary3_phyctg_t)); if (!cpqary3_phyctgp) { cmn_err(CE_NOTE, "CPQary3: Initial mem zalloc failed"); return (CPQARY3_FAILURE); } cmd_size = (8 * queue_depth); phy_addr = 0; replyq_start_addr = cpqary3_alloc_phyctgs_mem(cpqary3p, cmd_size, &phy_addr, cpqary3_phyctgp); if (!replyq_start_addr) { cmn_err(CE_WARN, "MEMALLOC returned failure"); return (CPQARY3_FAILURE); } bzero(replyq_start_addr, cmd_size); cpqary3p->drvr_replyq->replyq_headptr = /* LINTED: alignment */ (uint32_t *)replyq_start_addr; cpqary3p->drvr_replyq->index = 0; cpqary3p->drvr_replyq->max_index = queue_depth; cpqary3p->drvr_replyq->replyq_start_addr = replyq_start_addr; cpqary3p->drvr_replyq->cyclic_indicator = CPQARY3_REPLYQ_INIT_CYCLIC_IND; cpqary3p->drvr_replyq->replyq_start_paddr = phy_addr; DDI_PUT32_CP(cpqary3p, &perf_cfg->ReplyQAddr0Low32, phy_addr); DDI_PUT32_CP(cpqary3p, &perf_cfg->ReplyQAddr0High32, 0); max_blk_fetch_cnt = DDI_GET32(cpqary3p, &ctp->MaxBlockFetchCount); /* * For non-proton FW controllers, max_blk_fetch_count is not * implemented in the firmware */ /* * When blk fetch count is 0, FW auto fetches 564 bytes * corresponding to an optimal S/G of 31 */ if (max_blk_fetch_cnt == 0) { BlockFetchCnt[0] = 35; } else { /* * With MAX_PERF_SG_CNT set to 64, block fetch count * is got by:(sizeof (CommandList_t) + 15)/16 */ if (max_blk_fetch_cnt > 68) BlockFetchCnt[0] = 68; else BlockFetchCnt[0] = max_blk_fetch_cnt; } DDI_PUT32_CP(cpqary3p, &perf_cfg->BlockFetchCnt[0], BlockFetchCnt[0]); DDI_PUT32(cpqary3p, &ctp->HostWrite.TransportRequest, CFGTBL_XPORT_PERFORMANT); ddi_put32(cpqary3p->idr_handle, (uint32_t *)cpqary3p->idr, ddi_get32(cpqary3p->idr_handle, (uint32_t *)cpqary3p->idr) | CFGTBL_CHANGE_REQ); /* * Check whether the controller is ready */ cntr = 0; while (ddi_get32(cpqary3p->idr_handle, (uint32_t *)cpqary3p->idr) & CFGTBL_ACC_CMDS) { drv_usecwait(1000000); /* Wait for 1 Sec. */ cntr++; /* * Wait for a maximum of 90 seconds. No f/w should take * more than 90 secs to initialize. If the controller * is not ready even after 90 secs, it suggests that * something is wrong * (wrt the controller, what else) !!! */ if (cntr > CISS_INIT_TIME) /* 1.30 Mins */ { cmn_err(CE_CONT, "CPQary3 : Controller " "Initialization Failed \n"); return (CPQARY3_FAILURE); } } /* * Check whether controller accepts the requested method of * transport */ if (!(DDI_GET32(cpqary3p, &ctp->TransportActive) & CFGTBL_XPORT_PERFORMANT)) { cmn_err(CE_NOTE, "CPQary3 : Failed to Initialize " "Controller"); cmn_err(CE_CONT, "CPQary3 : For Hot Plug Operations, " "try again later\n"); DTRACE_PROBE1(ctlr_init_perf_fail, CfgTable_t *, ctp); return (CPQARY3_FAILURE); } DTRACE_PROBE(ctlr_init_simple); /* * Check if Controller is ready to accept Commands */ if (!(DDI_GET32(cpqary3p, &ctp->TransportActive) & CFGTBL_ACC_CMDS)) { cmn_err(CE_NOTE, "CPQary3: Controller NOT ready to " "accept Commands"); return (CPQARY3_FAILURE); } /* * Check if the maximum number of oustanding commands for the * initialized controller is something greater than Zero. */ CmdsOutMax = DDI_GET32(cpqary3p, &ctp->MaxPerfModeCmdsOutMax); if (CmdsOutMax == 0) CmdsOutMax = DDI_GET32(cpqary3p, &ctp->CmdsOutMax); if (CmdsOutMax == 0) { cmn_err(CE_NOTE, "CPQary3 : HBA Maximum Outstanding " "Commands set to Zero"); cmn_err(CE_NOTE, "CPQary3 : Cannot continue driver " "initialization"); return (CPQARY3_FAILURE); } cpqary3p->ctlr_maxcmds = CmdsOutMax; /* SG */ max_sg_cnt = DDI_GET32(cpqary3p, &ctp->MaxSGElements); max_blk_fetch_cnt = DDI_GET32(cpqary3p, &ctp->MaxBlockFetchCount); /* 32 byte aligned - size_of_cmdlist */ size_of_cmdlist = ((sizeof (CommandList_t) + 31) / 32) * 32; size_of_HRE = size_of_cmdlist - (sizeof (SGDescriptor_t) * CISS_MAXSGENTRIES); if ((max_blk_fetch_cnt == 0) || (max_sg_cnt == 0) || ((max_blk_fetch_cnt * 16) <= size_of_HRE)) { cpqary3p->sg_cnt = CPQARY3_PERF_SG_CNT; } else { /* * Get the optimal_sg - no of the SG's that will fit * into the max_blk_fetch_cnt */ optimal_sg_size = (max_blk_fetch_cnt * 16) - size_of_HRE; if (optimal_sg_size < sizeof (SGDescriptor_t)) { optimal_sg = CPQARY3_PERF_SG_CNT; } else { optimal_sg = optimal_sg_size / sizeof (SGDescriptor_t); } cpqary3p->sg_cnt = MIN(max_sg_cnt, optimal_sg); if (cpqary3p->sg_cnt > MAX_PERF_SG_CNT) cpqary3p->sg_cnt = MAX_PERF_SG_CNT; } /* SG */ /* * Zero the Upper 32 Address in the Controller */ DDI_PUT32(cpqary3p, &ctp->HostWrite.Upper32Addr, 0x00000000); cpqary3p->heartbeat = DDI_GET32(cpqary3p, &ctp->HeartBeat); /* Set the controller interrupt check routine */ if (cpqary3p->bddef->bd_is_e200) { cpqary3p->check_ctlr_intr = cpqary3_check_perf_e200_intr; } else { cpqary3p->check_ctlr_intr = cpqary3_check_perf_ctlr_intr; } if ((!cpqary3p->bddef->bd_is_e200) && (!cpqary3p->bddef->bd_is_ssll)) { cpqary3p->host_support = DDI_GET32(cpqary3p, &ctp->HostDrvrSupport); DDI_PUT32(cpqary3p, &ctp->HostDrvrSupport, (cpqary3p->host_support | 0x4)); } cpqary3p->host_support = DDI_GET32(cpqary3p, &ctp->HostDrvrSupport); cpqary3p->lockup_logged = CPQARY3_FALSE; } return (CPQARY3_SUCCESS); } /* * Function : cpqary3_check_ctlr_init * Description : This routine checks to see if the HBA is initialized. * Called By : cpqary3_init_ctlr() * Parameters : per-controller * Calls : None * Return Values: SUCCESS / FAILURE */ uint8_t cpqary3_check_ctlr_init(cpqary3_t *cpqary3p) { int8_t retvalue; uint16_t i; uint32_t *ctlr_init; ddi_acc_handle_t ctlr_init_handle; extern ddi_device_acc_attr_t cpqary3_dev_attributes; RETURN_FAILURE_IF_NULL(cpqary3p); /* * Set up the mapping for a Register at offset 0xB0 from I2O Bar * The value 0xB0 taken from the CONFIGM utility. * It should read 0xffff0000 if the controller is initialized. * if not yet initialized, read it every second for 300 secs. * If not set even after 300 secs, return FAILURE. * If set, free the mapping and continue */ retvalue = ddi_regs_map_setup(cpqary3p->dip, INDEX_PCI_BASE0, (caddr_t *)&ctlr_init, (offset_t)I2O_CTLR_INIT, 4, &cpqary3_dev_attributes, &ctlr_init_handle); if (retvalue != DDI_SUCCESS) { if (DDI_REGS_ACC_CONFLICT == retvalue) cmn_err(CE_WARN, "CPQary3 : HBA Init Register Mapping Conflict"); cmn_err(CE_WARN, "CPQary3 : HBA Init Regsiter Mapping Failed"); return (CPQARY3_FAILURE); } for (i = 0; i < 300; i++) { /* loop for 300 seconds */ if (CISS_CTLR_INIT == ddi_get32(ctlr_init_handle, ctlr_init)) { DTRACE_PROBE(ctlr_init_check_ready); ddi_regs_map_free(&ctlr_init_handle); break; } else { DTRACE_PROBE(ctlr_init_check_notready); delay(drv_usectohz(1000000)); } } if (i >= 300) { /* HBA not initialized even after 300 seconds !!! */ ddi_regs_map_free(&ctlr_init_handle); cmn_err(CE_WARN, "CPQary3 : %s NOT initialized !!! HBA may not " "function properly. Please replace the hardware or check " "the connections", cpqary3p->hba_name); return (CPQARY3_FAILURE); } return (CPQARY3_SUCCESS); }