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 2016 Joyent, Inc. 14 */ 15 16 #include <sys/scsi/adapters/smrt/smrt.h> 17 18 uint_t 19 smrt_isr_hw_simple(caddr_t arg1, caddr_t arg2) 20 { 21 _NOTE(ARGUNUSED(arg2)) 22 23 /* LINTED: E_BAD_PTR_CAST_ALIGN */ 24 smrt_t *smrt = (smrt_t *)arg1; 25 uint32_t isr = smrt_get32(smrt, CISS_I2O_INTERRUPT_STATUS); 26 hrtime_t now = gethrtime(); 27 28 mutex_enter(&smrt->smrt_mutex); 29 if (!(smrt->smrt_status & SMRT_CTLR_STATUS_RUNNING)) { 30 smrt->smrt_stats.smrts_unclaimed_interrupts++; 31 smrt->smrt_last_interrupt_unclaimed = now; 32 33 /* 34 * We should not be receiving interrupts from the controller 35 * while the driver is not running. 36 */ 37 mutex_exit(&smrt->smrt_mutex); 38 return (DDI_INTR_UNCLAIMED); 39 } 40 41 /* 42 * Check to see if this interrupt came from the device: 43 */ 44 if ((isr & CISS_ISR_BIT_SIMPLE_INTR) == 0) { 45 smrt->smrt_stats.smrts_unclaimed_interrupts++; 46 smrt->smrt_last_interrupt_unclaimed = now; 47 48 /* 49 * Check to see if the firmware has come to rest. If it has, 50 * this routine will panic the system. 51 */ 52 smrt_lockup_check(smrt); 53 54 mutex_exit(&smrt->smrt_mutex); 55 return (DDI_INTR_UNCLAIMED); 56 } 57 58 smrt->smrt_stats.smrts_claimed_interrupts++; 59 smrt->smrt_last_interrupt_claimed = now; 60 61 /* 62 * The interrupt was from our controller, so collect any pending 63 * command completions. 64 */ 65 smrt_retrieve_simple(smrt); 66 67 /* 68 * Process any commands in the completion queue. 69 */ 70 smrt_process_finishq(smrt); 71 72 mutex_exit(&smrt->smrt_mutex); 73 return (DDI_INTR_CLAIMED); 74 } 75 76 /* 77 * Read tags and process completion of the associated command until the supply 78 * of tags is exhausted. 79 */ 80 void 81 smrt_retrieve_simple(smrt_t *smrt) 82 { 83 uint32_t opq; 84 uint32_t none = 0xffffffff; 85 86 VERIFY(MUTEX_HELD(&smrt->smrt_mutex)); 87 88 while ((opq = smrt_get32(smrt, CISS_I2O_OUTBOUND_POST_Q)) != none) { 89 uint32_t tag = CISS_OPQ_READ_TAG(opq); 90 smrt_command_t *smcm; 91 92 if ((smcm = smrt_lookup_inflight(smrt, tag)) == NULL) { 93 dev_err(smrt->smrt_dip, CE_WARN, "spurious tag %x", 94 tag); 95 continue; 96 } 97 98 avl_remove(&smrt->smrt_inflight, smcm); 99 smcm->smcm_status &= ~SMRT_CMD_STATUS_INFLIGHT; 100 if (CISS_OPQ_READ_ERROR(opq) != 0) { 101 smcm->smcm_status |= SMRT_CMD_STATUS_ERROR; 102 } 103 smcm->smcm_time_complete = gethrtime(); 104 105 /* 106 * Push this command onto the completion queue. 107 */ 108 list_insert_tail(&smrt->smrt_finishq, smcm); 109 } 110 } 111 112 /* 113 * Submit a command to the controller by posting it to the Inbound Post Queue 114 * Register. 115 */ 116 void 117 smrt_submit_simple(smrt_t *smrt, smrt_command_t *smcm) 118 { 119 smrt_put32(smrt, CISS_I2O_INBOUND_POST_Q, smcm->smcm_pa_cmd); 120 } 121 122 /* 123 * Submit a command to the controller by posting it to the Inbound Post Queue 124 * Register. Immediately begin polling on the completion of that command. 125 * 126 * NOTE: This function is for controller initialisation only. It discards 127 * completions of commands other than the expected command as spurious, and 128 * will not interact correctly with the rest of the driver once it is running. 129 */ 130 int 131 smrt_preinit_command_simple(smrt_t *smrt, smrt_command_t *smcm) 132 { 133 /* 134 * The controller must be initialised to use the Simple Transport 135 * Method, but not be marked RUNNING. The command to process must be a 136 * PREINIT command with the expected tag number, marked for polling. 137 */ 138 VERIFY(smrt->smrt_ctlr_mode == SMRT_CTLR_MODE_SIMPLE); 139 VERIFY(!(smrt->smrt_status & SMRT_CTLR_STATUS_RUNNING)); 140 VERIFY(smcm->smcm_type == SMRT_CMDTYPE_PREINIT); 141 VERIFY(smcm->smcm_status & SMRT_CMD_STATUS_POLLED); 142 VERIFY3U(smcm->smcm_tag, ==, SMRT_PRE_TAG_NUMBER); 143 144 /* 145 * Submit this command to the controller. 146 */ 147 smcm->smcm_status |= SMRT_CMD_STATUS_INFLIGHT; 148 smrt_put32(smrt, CISS_I2O_INBOUND_POST_Q, smcm->smcm_pa_cmd); 149 150 /* 151 * Poll the controller for completions until we see the command we just 152 * sent, or the timeout expires. 153 */ 154 for (;;) { 155 uint32_t none = 0xffffffff; 156 uint32_t opq = smrt_get32(smrt, CISS_I2O_OUTBOUND_POST_Q); 157 uint32_t tag; 158 159 if (smcm->smcm_expiry != 0) { 160 /* 161 * This command has an expiry time. Check to see 162 * if it has already passed: 163 */ 164 if (smcm->smcm_expiry < gethrtime()) { 165 return (ETIMEDOUT); 166 } 167 } 168 169 if (opq == none) { 170 delay(drv_usectohz(10 * 1000)); 171 continue; 172 } 173 174 if ((tag = CISS_OPQ_READ_TAG(opq)) != SMRT_PRE_TAG_NUMBER) { 175 dev_err(smrt->smrt_dip, CE_WARN, "unexpected tag 0x%x" 176 " completed during driver init", tag); 177 delay(drv_usectohz(10 * 1000)); 178 continue; 179 } 180 181 smcm->smcm_status &= ~SMRT_CMD_STATUS_INFLIGHT; 182 if (CISS_OPQ_READ_ERROR(opq) != 0) { 183 smcm->smcm_status |= SMRT_CMD_STATUS_ERROR; 184 } 185 smcm->smcm_time_complete = gethrtime(); 186 smcm->smcm_status |= SMRT_CMD_STATUS_POLL_COMPLETE; 187 188 return (0); 189 } 190 } 191 192 int 193 smrt_ctlr_init_simple(smrt_t *smrt) 194 { 195 VERIFY(smrt->smrt_ctlr_mode == SMRT_CTLR_MODE_UNKNOWN); 196 197 if (smrt_cfgtbl_transport_has_support(smrt, 198 CISS_CFGTBL_XPORT_SIMPLE) != DDI_SUCCESS) { 199 return (DDI_FAILURE); 200 } 201 smrt->smrt_ctlr_mode = SMRT_CTLR_MODE_SIMPLE; 202 203 /* 204 * Disable device interrupts while we are setting up. 205 */ 206 smrt_intr_set(smrt, B_FALSE); 207 208 if ((smrt->smrt_maxcmds = smrt_ctlr_get_cmdsoutmax(smrt)) == 0) { 209 dev_err(smrt->smrt_dip, CE_WARN, "maximum outstanding " 210 "commands set to zero"); 211 return (DDI_FAILURE); 212 } 213 214 /* 215 * Determine the number of Scatter/Gather List entries this controller 216 * supports. The maximum number we allow is CISS_MAXSGENTRIES: the 217 * number of elements in the static struct we use for command 218 * submission. 219 */ 220 if ((smrt->smrt_sg_cnt = smrt_ctlr_get_maxsgelements(smrt)) == 0) { 221 /* 222 * The CISS specification states that if this value is 223 * zero, we should assume a value of 31 for compatibility 224 * with older firmware. 225 */ 226 smrt->smrt_sg_cnt = CISS_SGCNT_FALLBACK; 227 228 } else if (smrt->smrt_sg_cnt > CISS_MAXSGENTRIES) { 229 /* 230 * If the controller supports more than we have allocated, 231 * just cap the count at the allocation size. 232 */ 233 smrt->smrt_sg_cnt = CISS_MAXSGENTRIES; 234 } 235 236 /* 237 * Zero the upper 32 bits of the address in the Controller. 238 */ 239 ddi_put32(smrt->smrt_ct_handle, &smrt->smrt_ct->Upper32Addr, 0); 240 241 /* 242 * Set the Transport Method and flush the changes to the 243 * Configuration Table. 244 */ 245 smrt_cfgtbl_transport_set(smrt, CISS_CFGTBL_XPORT_SIMPLE); 246 if (smrt_cfgtbl_flush(smrt) != DDI_SUCCESS) { 247 return (DDI_FAILURE); 248 } 249 250 if (smrt_cfgtbl_transport_confirm(smrt, 251 CISS_CFGTBL_XPORT_SIMPLE) != DDI_SUCCESS) { 252 return (DDI_FAILURE); 253 } 254 255 /* 256 * Check the outstanding command cap a second time now that we have 257 * flushed out the new Transport Method. This is entirely defensive; 258 * we do not expect this value to change. 259 */ 260 uint32_t check_again = smrt_ctlr_get_cmdsoutmax(smrt); 261 if (check_again != smrt->smrt_maxcmds) { 262 dev_err(smrt->smrt_dip, CE_WARN, "maximum outstanding commands " 263 "changed during initialisation (was %u, now %u)", 264 smrt->smrt_maxcmds, check_again); 265 return (DDI_FAILURE); 266 } 267 268 return (DDI_SUCCESS); 269 } 270 271 void 272 smrt_ctlr_teardown_simple(smrt_t *smrt) 273 { 274 VERIFY(smrt->smrt_ctlr_mode == SMRT_CTLR_MODE_SIMPLE); 275 276 /* 277 * Due to the nominal simplicity of the simple mode, we have no 278 * particular teardown to perform as we do not allocate anything 279 * on the way up. 280 */ 281 smrt->smrt_ctlr_mode = SMRT_CTLR_MODE_UNKNOWN; 282 } 283