/* * 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. */ #include "cpqary3.h" /* * Function : cpqary3_hw_isr * Description : This routine determines if this instance of the * HBA interrupted and if positive triggers a software * interrupt. * For SAS controllers which operate in performant mode * we clear the interrupt. * For CISS controllers which operate in simple mode * we get the tag value. * Called By : kernel * Parameters : per-controller * Calls : cpqary3_check_ctlr_intr() * Return Values: DDI_INTR_CLAIMED/UNCLAIMED * [We either CLAIM the interrupt or Discard it] */ uint_t cpqary3_hw_isr(caddr_t per_ctlr) { uint8_t need_swintr; cpqary3_t *cpqary3p; cpqary3_drvr_replyq_t *replyq_ptr; volatile CfgTable_t *ctp; uint32_t spr0; uint32_t doorbell_status; uint32_t tag; cpqary3p = (void *)per_ctlr; ctp = (CfgTable_t *)cpqary3p->ct; replyq_ptr = (cpqary3_drvr_replyq_t *)cpqary3p->drvr_replyq; if (CPQARY3_FAILURE == cpqary3p->check_ctlr_intr(cpqary3p)) { if (cpqary3p->heartbeat == DDI_GET32(cpqary3p, &ctp->HeartBeat)) { if (0x2 & ddi_get32(cpqary3p->odr_handle, (uint32_t *)cpqary3p->odr)) { spr0 = ddi_get32(cpqary3p->spr0_handle, (uint32_t *)cpqary3p->spr0); spr0 = spr0 >> 16; cmn_err(CE_WARN, "CPQary3 : %s HBA firmware " "Locked !!! Lockup Code: 0x%x", cpqary3p->hba_name, spr0); cmn_err(CE_WARN, "CPQary3 : Please reboot " "the system"); ddi_put32(cpqary3p->odr_cl_handle, (uint32_t *)cpqary3p->odr_cl, 0x2); cpqary3_intr_onoff(cpqary3p, CPQARY3_INTR_DISABLE); if (cpqary3p->host_support & 0x4) { cpqary3_lockup_intr_onoff(cpqary3p, CPQARY3_LOCKUP_INTR_DISABLE); } cpqary3p->controller_lockup = CPQARY3_TRUE; } return (DDI_INTR_CLAIMED); } return (DDI_INTR_UNCLAIMED); } /* PERF */ /* * We decided that we will have only one retrieve function for * both simple and performant mode. To achieve this we have to mimic * what controller does for performant mode in simple mode. * For simple mode we are making replq_simple_ptr and * replq_headptr of performant * mode point to the same location in the reply queue. * For the performant mode, we clear the interrupt */ if (!(cpqary3p->bddef->bd_flags & SA_BD_SAS)) { while ((tag = ddi_get32(cpqary3p->opq_handle, (uint32_t *)cpqary3p->opq)) != 0xFFFFFFFF) { replyq_ptr->replyq_simple_ptr[0] = tag; replyq_ptr->replyq_simple_ptr[0] |= replyq_ptr->simple_cyclic_indicator; ++replyq_ptr->simple_index; if (replyq_ptr->simple_index == replyq_ptr->max_index) { replyq_ptr->simple_index = 0; /* Toggle at wraparound */ replyq_ptr->simple_cyclic_indicator = (replyq_ptr->simple_cyclic_indicator == 0) ? 1 : 0; replyq_ptr->replyq_simple_ptr = /* LINTED: alignment */ (uint32_t *)(replyq_ptr->replyq_start_addr); } else { replyq_ptr->replyq_simple_ptr += 2; } } } else { doorbell_status = ddi_get32(cpqary3p->odr_handle, (uint32_t *)cpqary3p->odr); if (doorbell_status & 0x1) { ddi_put32(cpqary3p->odr_cl_handle, (uint32_t *)cpqary3p->odr_cl, (ddi_get32(cpqary3p->odr_cl_handle, (uint32_t *)cpqary3p->odr_cl) | 0x1)); doorbell_status = ddi_get32(cpqary3p->odr_handle, (uint32_t *)cpqary3p->odr); } } /* PERF */ /* * If s/w interrupt handler is already running, do not trigger another * since packets have already been transferred to Retrieved Q. * Else, Set swintr_flag to state to the s/w interrupt handler * that it has a job to do. * trigger the s/w interrupt handler * Claim the interrupt */ mutex_enter(&cpqary3p->hw_mutex); if (cpqary3p->swintr_flag == CPQARY3_TRUE) { need_swintr = CPQARY3_FALSE; } else { need_swintr = CPQARY3_TRUE; cpqary3p->swintr_flag = CPQARY3_TRUE; } mutex_exit(&cpqary3p->hw_mutex); if (CPQARY3_TRUE == need_swintr) ddi_trigger_softintr(cpqary3p->cpqary3_softintr_id); return (DDI_INTR_CLAIMED); } /* * Function : cpqary3_sw_isr * Description : This routine determines if this instance of the * software interrupt handler was triggered by its * respective h/w interrupt handler and if affermative * processes the completed commands. * Called By : kernel (Triggered by : cpqary3_hw_isr) * Parameters : per-controller * Calls : cpqary3_retrieve() * Return Values: DDI_INTR_CLAIMED/UNCLAIMED * [We either CLAIM the interrupr or DON'T] */ uint_t cpqary3_sw_isr(caddr_t per_ctlr) { cpqary3_t *cpqary3p; cpqary3p = (void *)per_ctlr; if (!cpqary3p) { cmn_err(CE_PANIC, "CPQary3 : Software Interrupt Service " "Routine invoked with NULL pointer argument \n"); } /* * Ensure that our hardware routine actually triggered this routine. * If it was not the case, do NOT CLAIM the interrupt */ mutex_enter(&cpqary3p->hw_mutex); if (CPQARY3_TRUE != cpqary3p->swintr_flag) { mutex_exit(&cpqary3p->hw_mutex); return (DDI_INTR_UNCLAIMED); } cpqary3p->swintr_flag = CPQARY3_FALSE; /* PERF */ mutex_exit(&cpqary3p->hw_mutex); (void) cpqary3_retrieve(cpqary3p); /* PERF */ return (DDI_INTR_CLAIMED); }