cpqary3_talk2ctlr.c revision 80c94ecd7a524eb933a4bb221a9618b9dc490e76
/*
* 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 <sys/sdt.h>
#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);
}