1 /*- 2 * Copyright 2016-2021 Microchip Technology, Inc. and/or its subsidiaries. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 27 #include "smartpqi_includes.h" 28 29 /* 30 * Function to get processor count 31 */ 32 int 33 os_get_processor_config(pqisrc_softstate_t *softs) 34 { 35 DBG_FUNC("IN\n"); 36 softs->num_cpus_online = mp_ncpus; 37 DBG_FUNC("OUT\n"); 38 39 return PQI_STATUS_SUCCESS; 40 } 41 42 /* 43 * Function to get interrupt count and type supported 44 */ 45 int 46 os_get_intr_config(pqisrc_softstate_t *softs) 47 { 48 device_t dev = softs->os_specific.pqi_dev; 49 int msi_count = pci_msix_count(dev); 50 int error = BSD_SUCCESS; 51 52 DBG_FUNC("IN\n"); 53 54 if (msi_count > softs->num_cpus_online) 55 msi_count = softs->num_cpus_online; 56 if (msi_count > PQI_MAX_MSIX) 57 msi_count = PQI_MAX_MSIX; 58 if (msi_count == 0 || (error = pci_alloc_msix(dev, &msi_count)) != 0) { 59 device_printf(dev, "alloc msix failed - msi_count=%d, err=%d; " 60 "will try MSI\n", msi_count, error); 61 pci_release_msi(dev); 62 } else { 63 softs->intr_count = msi_count; 64 softs->intr_type = INTR_TYPE_MSIX; 65 softs->os_specific.msi_enabled = TRUE; 66 device_printf(dev, "using MSI-X interrupts (%d vectors)\n", 67 msi_count); 68 } 69 if (!softs->intr_type) { 70 msi_count = 1; 71 if ((error = pci_alloc_msi(dev, &msi_count)) != 0) { 72 device_printf(dev, "alloc msi failed - err=%d; " 73 "will use INTx\n", error); 74 pci_release_msi(dev); 75 } else { 76 softs->os_specific.msi_enabled = TRUE; 77 softs->intr_count = msi_count; 78 softs->intr_type = INTR_TYPE_MSI; 79 device_printf(dev, "using MSI interrupts\n"); 80 } 81 } 82 83 if (!softs->intr_type) { 84 device_printf(dev, "using legacy interrupts\n"); 85 softs->intr_type = INTR_TYPE_FIXED; 86 softs->intr_count = 1; 87 } 88 89 DBG_FUNC("OUT\n"); 90 91 error = bsd_status_to_pqi_status(BSD_SUCCESS); 92 93 return error; 94 } 95 96 void 97 os_eventtaskqueue_enqueue(pqisrc_softstate_t *sc) 98 { 99 taskqueue_enqueue(taskqueue_swi, &sc->os_specific.event_task); 100 } 101 102 void 103 pqisrc_event_worker(void *arg1, int arg2) 104 { 105 pqisrc_ack_all_events(arg1); 106 } 107 108 /* 109 * ithread routine to handle uniprocessor systems 110 */ 111 static void 112 shared_ithread_routine(void *arg) 113 { 114 pqi_intr_ctx_t *intr_ctx = (pqi_intr_ctx_t *)arg; 115 pqisrc_softstate_t *softs = device_get_softc(intr_ctx->pqi_dev); 116 int oq_id = intr_ctx->oq_id; 117 118 DBG_FUNC("IN\n"); 119 120 if (softs == NULL) 121 return; 122 123 pqisrc_process_response_queue(softs, oq_id); 124 pqisrc_process_event_intr_src(softs, oq_id - 1); 125 126 DBG_FUNC("OUT\n"); 127 } 128 129 /* 130 * ithread routine to process non event response 131 */ 132 static void 133 common_ithread_routine(void *arg) 134 { 135 pqi_intr_ctx_t *intr_ctx = (pqi_intr_ctx_t *)arg; 136 pqisrc_softstate_t *softs = device_get_softc(intr_ctx->pqi_dev); 137 int oq_id = intr_ctx->oq_id; 138 139 DBG_FUNC("IN\n"); 140 141 if (softs == NULL) 142 return; 143 144 pqisrc_process_response_queue(softs, oq_id); 145 146 DBG_FUNC("OUT\n"); 147 } 148 149 static void 150 event_ithread_routine(void *arg) 151 { 152 pqi_intr_ctx_t *intr_ctx = (pqi_intr_ctx_t *)arg; 153 pqisrc_softstate_t *softs = device_get_softc(intr_ctx->pqi_dev); 154 int oq_id = intr_ctx->oq_id; 155 156 DBG_FUNC("IN\n"); 157 158 if (softs == NULL) 159 return; 160 161 pqisrc_process_event_intr_src(softs, oq_id); 162 163 DBG_FUNC("OUT\n"); 164 } 165 166 /* 167 * Registration of legacy interrupt in case MSI is unsupported 168 */ 169 int 170 register_legacy_intr(pqisrc_softstate_t *softs) 171 { 172 int error = BSD_SUCCESS; 173 device_t dev = softs->os_specific.pqi_dev; 174 175 DBG_FUNC("IN\n"); 176 177 softs->os_specific.pqi_irq_rid[0] = 0; 178 softs->os_specific.pqi_irq[0] = bus_alloc_resource_any(dev, \ 179 SYS_RES_IRQ, &softs->os_specific.pqi_irq_rid[0], 180 RF_ACTIVE | RF_SHAREABLE); 181 if (NULL == softs->os_specific.pqi_irq[0]) { 182 DBG_ERR("Failed to allocate resource for interrupt\n"); 183 return ENXIO; 184 } 185 if ((softs->os_specific.msi_ctx = os_mem_alloc(softs,sizeof(pqi_intr_ctx_t))) == NULL) { 186 DBG_ERR("Failed to allocate memory for msi_ctx\n"); 187 return ENXIO; 188 } 189 softs->os_specific.msi_ctx[0].pqi_dev = dev; 190 /* For Legacy support oq_id should be one */ 191 softs->os_specific.msi_ctx[0].oq_id = 1; 192 193 error = bus_setup_intr(dev, softs->os_specific.pqi_irq[0], 194 INTR_TYPE_CAM | INTR_MPSAFE, \ 195 NULL, shared_ithread_routine, 196 &softs->os_specific.msi_ctx[0], 197 &softs->os_specific.intrcookie[0]); 198 if (error) { 199 DBG_ERR("Failed to setup legacy interrupt err = %d\n", error); 200 return error; 201 } 202 softs->os_specific.intr_registered[0] = TRUE; 203 204 DBG_FUNC("OUT error = %d\n", error); 205 206 return error; 207 } 208 209 /* 210 * Registration of MSIx 211 */ 212 int 213 register_msix_intr(pqisrc_softstate_t *softs) 214 { 215 int error = BSD_SUCCESS; 216 int i = 0; 217 device_t dev = softs->os_specific.pqi_dev; 218 int msix_count = softs->intr_count; 219 220 DBG_FUNC("IN\n"); 221 222 softs->os_specific.msi_ctx = os_mem_alloc(softs, sizeof(pqi_intr_ctx_t) * msix_count); 223 if (!softs->os_specific.msi_ctx) { 224 DBG_ERR("Memory allocation failed\n"); 225 return ENXIO; 226 } 227 228 /*Add shared handler */ 229 if (softs->share_opq_and_eventq) { 230 softs->os_specific.pqi_irq_rid[i] = i+1; 231 softs->os_specific.pqi_irq[i] = bus_alloc_resource_any(dev, \ 232 SYS_RES_IRQ, 233 &softs->os_specific.pqi_irq_rid[i], 234 RF_SHAREABLE | RF_ACTIVE); 235 if (NULL == softs->os_specific.pqi_irq[i]) { 236 DBG_ERR("Failed to allocate \ 237 event interrupt resource\n"); 238 return ENXIO; 239 } 240 241 softs->os_specific.msi_ctx[i].pqi_dev = dev; 242 softs->os_specific.msi_ctx[i].oq_id = i+1; 243 244 error = bus_setup_intr(dev,softs->os_specific.pqi_irq[i], 245 INTR_TYPE_CAM | INTR_MPSAFE,\ 246 NULL, 247 shared_ithread_routine, 248 &softs->os_specific.msi_ctx[i], 249 &softs->os_specific.intrcookie[i]); 250 251 if (error) { 252 DBG_ERR("Failed to setup interrupt for events r=%d\n", 253 error); 254 return error; 255 } 256 softs->os_specific.intr_registered[i] = TRUE; 257 } 258 else { 259 /* Add event handler */ 260 softs->os_specific.pqi_irq_rid[i] = i+1; 261 softs->os_specific.pqi_irq[i] = bus_alloc_resource_any(dev, \ 262 SYS_RES_IRQ, 263 &softs->os_specific.pqi_irq_rid[i], 264 RF_SHAREABLE | RF_ACTIVE); 265 if (NULL == softs->os_specific.pqi_irq[i]) { 266 DBG_ERR("Failed to allocate event interrupt resource\n"); 267 return ENXIO; 268 } 269 270 softs->os_specific.msi_ctx[i].pqi_dev = dev; 271 softs->os_specific.msi_ctx[i].oq_id = i; 272 273 error = bus_setup_intr(dev,softs->os_specific.pqi_irq[i], 274 INTR_TYPE_CAM | INTR_MPSAFE,\ 275 NULL, 276 event_ithread_routine, 277 &softs->os_specific.msi_ctx[i], 278 &softs->os_specific.intrcookie[i]); 279 if (error) { 280 DBG_ERR("Failed to setup interrupt for events err=%d\n", 281 error); 282 return error; 283 } 284 softs->os_specific.intr_registered[i] = TRUE; 285 /* Add interrupt handlers*/ 286 for (i = 1; i < msix_count; ++i) { 287 softs->os_specific.pqi_irq_rid[i] = i+1; 288 softs->os_specific.pqi_irq[i] = \ 289 bus_alloc_resource_any(dev, 290 SYS_RES_IRQ, 291 &softs->os_specific.pqi_irq_rid[i], 292 RF_SHAREABLE | RF_ACTIVE); 293 if (NULL == softs->os_specific.pqi_irq[i]) { 294 DBG_ERR("Failed to allocate \ 295 msi/x interrupt resource\n"); 296 return ENXIO; 297 } 298 softs->os_specific.msi_ctx[i].pqi_dev = dev; 299 softs->os_specific.msi_ctx[i].oq_id = i; 300 error = bus_setup_intr(dev, 301 softs->os_specific.pqi_irq[i], 302 INTR_TYPE_CAM | INTR_MPSAFE,\ 303 NULL, 304 common_ithread_routine, 305 &softs->os_specific.msi_ctx[i], 306 &softs->os_specific.intrcookie[i]); 307 if (error) { 308 DBG_ERR("Failed to setup \ 309 msi/x interrupt error = %d\n", error); 310 return error; 311 } 312 softs->os_specific.intr_registered[i] = TRUE; 313 } 314 } 315 316 DBG_FUNC("OUT error = %d\n", error); 317 318 return error; 319 } 320 321 /* 322 * Setup interrupt depending on the configuration 323 */ 324 int 325 os_setup_intr(pqisrc_softstate_t *softs) 326 { 327 int bsd_status, pqi_status; 328 329 DBG_FUNC("IN\n"); 330 331 if (softs->intr_type == INTR_TYPE_FIXED) { 332 bsd_status = register_legacy_intr(softs); 333 } 334 else { 335 bsd_status = register_msix_intr(softs); 336 } 337 338 if(bsd_status) 339 DBG_WARN("interrupt registration is failed, error = %d\n", bsd_status); 340 341 pqi_status = bsd_status_to_pqi_status(bsd_status); 342 343 DBG_FUNC("OUT\n"); 344 345 return pqi_status; 346 } 347 348 /* 349 * Deregistration of legacy interrupt 350 */ 351 void 352 deregister_pqi_intx(pqisrc_softstate_t *softs) 353 { 354 device_t dev = softs->os_specific.pqi_dev; 355 356 DBG_FUNC("IN\n"); 357 358 if (softs->os_specific.pqi_irq[0] != NULL) { 359 if (softs->os_specific.intr_registered[0]) { 360 bus_teardown_intr(dev, softs->os_specific.pqi_irq[0], 361 softs->os_specific.intrcookie[0]); 362 softs->os_specific.intr_registered[0] = FALSE; 363 } 364 bus_release_resource(dev, SYS_RES_IRQ, 365 softs->os_specific.pqi_irq_rid[0], 366 softs->os_specific.pqi_irq[0]); 367 softs->os_specific.pqi_irq[0] = NULL; 368 os_mem_free(softs, (char*)softs->os_specific.msi_ctx, sizeof(pqi_intr_ctx_t)); 369 } 370 371 DBG_FUNC("OUT\n"); 372 } 373 374 /* 375 * Deregistration of MSIx interrupt 376 */ 377 void 378 deregister_pqi_msix(pqisrc_softstate_t *softs) 379 { 380 device_t dev = softs->os_specific.pqi_dev; 381 int msix_count = softs->intr_count; 382 int i = 0; 383 384 DBG_FUNC("IN\n"); 385 386 os_mem_free(softs, (char*)softs->os_specific.msi_ctx, sizeof(pqi_intr_ctx_t) * msix_count); 387 softs->os_specific.msi_ctx = NULL; 388 389 for (; i < msix_count; ++i) { 390 if (softs->os_specific.pqi_irq[i] != NULL) { 391 if (softs->os_specific.intr_registered[i]) { 392 bus_teardown_intr(dev, 393 softs->os_specific.pqi_irq[i], 394 softs->os_specific.intrcookie[i]); 395 softs->os_specific.intr_registered[i] = FALSE; 396 } 397 bus_release_resource(dev, SYS_RES_IRQ, 398 softs->os_specific.pqi_irq_rid[i], 399 softs->os_specific.pqi_irq[i]); 400 softs->os_specific.pqi_irq[i] = NULL; 401 } 402 } 403 404 DBG_FUNC("OUT\n"); 405 } 406 407 /* 408 * Function to destroy interrupts registered 409 */ 410 int 411 os_destroy_intr(pqisrc_softstate_t *softs) 412 { 413 device_t dev = softs->os_specific.pqi_dev; 414 415 DBG_FUNC("IN\n"); 416 417 if (softs->intr_type == INTR_TYPE_FIXED) { 418 deregister_pqi_intx(softs); 419 } else if (softs->intr_type == INTR_TYPE_MSIX) { 420 deregister_pqi_msix(softs); 421 } 422 if (softs->os_specific.msi_enabled) { 423 pci_release_msi(dev); 424 softs->os_specific.msi_enabled = FALSE; 425 } 426 427 DBG_FUNC("OUT\n"); 428 429 return PQI_STATUS_SUCCESS; 430 } 431 432 /* 433 * Free interrupt related resources for the adapter 434 */ 435 void 436 os_free_intr_config(pqisrc_softstate_t *softs) 437 { 438 device_t dev = softs->os_specific.pqi_dev; 439 440 DBG_FUNC("IN\n"); 441 442 if (softs->os_specific.msi_enabled) { 443 pci_release_msi(dev); 444 softs->os_specific.msi_enabled = FALSE; 445 } 446 447 DBG_FUNC("OUT\n"); 448 } 449