1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright (C) 2013 Hewlett-Packard Development Company, L.P. 14 */ 15 16 #include "cpqary3.h" 17 18 /* 19 * Function : cpqary3_hw_isr 20 * Description : This routine determines if this instance of the 21 * HBA interrupted and if positive triggers a software 22 * interrupt. 23 * For SAS controllers which operate in performant mode 24 * we clear the interrupt. 25 * For CISS controllers which operate in simple mode 26 * we get the tag value. 27 * Called By : kernel 28 * Parameters : per-controller 29 * Calls : cpqary3_check_ctlr_intr() 30 * Return Values: DDI_INTR_CLAIMED/UNCLAIMED 31 * [We either CLAIM the interrupt or Discard it] 32 */ 33 uint_t 34 cpqary3_hw_isr(caddr_t per_ctlr) 35 { 36 uint8_t need_swintr; 37 cpqary3_t *cpqary3p; 38 cpqary3_drvr_replyq_t *replyq_ptr; 39 volatile CfgTable_t *ctp; 40 uint32_t spr0; 41 uint32_t doorbell_status; 42 uint32_t tag; 43 44 cpqary3p = (void *)per_ctlr; 45 ctp = (CfgTable_t *)cpqary3p->ct; 46 replyq_ptr = (cpqary3_drvr_replyq_t *)cpqary3p->drvr_replyq; 47 48 if (CPQARY3_FAILURE == cpqary3p->check_ctlr_intr(cpqary3p)) { 49 if (cpqary3p->heartbeat == 50 DDI_GET32(cpqary3p, &ctp->HeartBeat)) { 51 if (0x2 & ddi_get32(cpqary3p->odr_handle, 52 (uint32_t *)cpqary3p->odr)) { 53 spr0 = ddi_get32(cpqary3p->spr0_handle, 54 (uint32_t *)cpqary3p->spr0); 55 spr0 = spr0 >> 16; 56 cmn_err(CE_WARN, "CPQary3 : %s HBA firmware " 57 "Locked !!! Lockup Code: 0x%x", 58 cpqary3p->hba_name, spr0); 59 cmn_err(CE_WARN, "CPQary3 : Please reboot " 60 "the system"); 61 ddi_put32(cpqary3p->odr_cl_handle, 62 (uint32_t *)cpqary3p->odr_cl, 0x2); 63 cpqary3_intr_onoff(cpqary3p, 64 CPQARY3_INTR_DISABLE); 65 if (cpqary3p->host_support & 0x4) { 66 cpqary3_lockup_intr_onoff(cpqary3p, 67 CPQARY3_LOCKUP_INTR_DISABLE); 68 } 69 cpqary3p->controller_lockup = CPQARY3_TRUE; 70 } 71 return (DDI_INTR_CLAIMED); 72 } 73 return (DDI_INTR_UNCLAIMED); 74 } 75 76 /* PERF */ 77 78 /* 79 * We decided that we will have only one retrieve function for 80 * both simple and performant mode. To achieve this we have to mimic 81 * what controller does for performant mode in simple mode. 82 * For simple mode we are making replq_simple_ptr and 83 * replq_headptr of performant 84 * mode point to the same location in the reply queue. 85 * For the performant mode, we clear the interrupt 86 */ 87 88 if (!(cpqary3p->bddef->bd_flags & SA_BD_SAS)) { 89 while ((tag = ddi_get32(cpqary3p->opq_handle, 90 (uint32_t *)cpqary3p->opq)) != 0xFFFFFFFF) { 91 replyq_ptr->replyq_simple_ptr[0] = tag; 92 replyq_ptr->replyq_simple_ptr[0] |= 93 replyq_ptr->simple_cyclic_indicator; 94 ++replyq_ptr->simple_index; 95 96 if (replyq_ptr->simple_index == replyq_ptr->max_index) { 97 replyq_ptr->simple_index = 0; 98 /* Toggle at wraparound */ 99 replyq_ptr->simple_cyclic_indicator = 100 (replyq_ptr->simple_cyclic_indicator == 0) ? 101 1 : 0; 102 replyq_ptr->replyq_simple_ptr = 103 /* LINTED: alignment */ 104 (uint32_t *)(replyq_ptr->replyq_start_addr); 105 } else { 106 replyq_ptr->replyq_simple_ptr += 2; 107 } 108 } 109 } else { 110 doorbell_status = ddi_get32(cpqary3p->odr_handle, 111 (uint32_t *)cpqary3p->odr); 112 if (doorbell_status & 0x1) { 113 ddi_put32(cpqary3p->odr_cl_handle, 114 (uint32_t *)cpqary3p->odr_cl, 115 (ddi_get32(cpqary3p->odr_cl_handle, 116 (uint32_t *)cpqary3p->odr_cl) | 0x1)); 117 doorbell_status = ddi_get32(cpqary3p->odr_handle, 118 (uint32_t *)cpqary3p->odr); 119 } 120 } 121 122 /* PERF */ 123 124 /* 125 * If s/w interrupt handler is already running, do not trigger another 126 * since packets have already been transferred to Retrieved Q. 127 * Else, Set swintr_flag to state to the s/w interrupt handler 128 * that it has a job to do. 129 * trigger the s/w interrupt handler 130 * Claim the interrupt 131 */ 132 133 mutex_enter(&cpqary3p->hw_mutex); 134 135 if (cpqary3p->swintr_flag == CPQARY3_TRUE) { 136 need_swintr = CPQARY3_FALSE; 137 } else { 138 need_swintr = CPQARY3_TRUE; 139 cpqary3p->swintr_flag = CPQARY3_TRUE; 140 } 141 142 mutex_exit(&cpqary3p->hw_mutex); 143 144 if (CPQARY3_TRUE == need_swintr) 145 ddi_trigger_softintr(cpqary3p->cpqary3_softintr_id); 146 147 return (DDI_INTR_CLAIMED); 148 } 149 150 /* 151 * Function : cpqary3_sw_isr 152 * Description : This routine determines if this instance of the 153 * software interrupt handler was triggered by its 154 * respective h/w interrupt handler and if affermative 155 * processes the completed commands. 156 * Called By : kernel (Triggered by : cpqary3_hw_isr) 157 * Parameters : per-controller 158 * Calls : cpqary3_retrieve() 159 * Return Values: DDI_INTR_CLAIMED/UNCLAIMED 160 * [We either CLAIM the interrupr or DON'T] 161 */ 162 uint_t 163 cpqary3_sw_isr(caddr_t per_ctlr) 164 { 165 cpqary3_t *cpqary3p; 166 167 cpqary3p = (void *)per_ctlr; 168 if (!cpqary3p) { 169 cmn_err(CE_PANIC, "CPQary3 : Software Interrupt Service " 170 "Routine invoked with NULL pointer argument \n"); 171 } 172 173 /* 174 * Ensure that our hardware routine actually triggered this routine. 175 * If it was not the case, do NOT CLAIM the interrupt 176 */ 177 178 mutex_enter(&cpqary3p->hw_mutex); 179 if (CPQARY3_TRUE != cpqary3p->swintr_flag) { 180 mutex_exit(&cpqary3p->hw_mutex); 181 return (DDI_INTR_UNCLAIMED); 182 } 183 184 cpqary3p->swintr_flag = CPQARY3_FALSE; 185 186 /* PERF */ 187 mutex_exit(&cpqary3p->hw_mutex); 188 (void) cpqary3_retrieve(cpqary3p); 189 /* PERF */ 190 191 return (DDI_INTR_CLAIMED); 192 } 193