1 /*- 2 * Copyright 2016-2023 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 /* 31 * Function to get processor count 32 */ 33 int 34 os_get_processor_config(pqisrc_softstate_t *softs) 35 { 36 DBG_FUNC("IN\n"); 37 softs->num_cpus_online = mp_ncpus; 38 bsd_set_hint_adapter_cpu_config(softs); 39 DBG_FUNC("OUT\n"); 40 41 return PQI_STATUS_SUCCESS; 42 } 43 44 /* 45 * Function to get interrupt count and type supported 46 */ 47 int 48 os_get_intr_config(pqisrc_softstate_t *softs) 49 { 50 device_t dev = softs->os_specific.pqi_dev; 51 int msi_count = pci_msix_count(dev); 52 int error = BSD_SUCCESS; 53 54 DBG_FUNC("IN\n"); 55 56 if (msi_count > softs->num_cpus_online) 57 msi_count = softs->num_cpus_online; 58 if (msi_count > PQI_MAX_MSIX) 59 msi_count = PQI_MAX_MSIX; 60 if (msi_count == 0 || (error = pci_alloc_msix(dev, &msi_count)) != 0) { 61 device_printf(dev, "alloc msix failed - msi_count=%d, err=%d; " 62 "will try MSI\n", msi_count, error); 63 pci_release_msi(dev); 64 } else { 65 softs->intr_count = msi_count; 66 softs->intr_type = INTR_TYPE_MSIX; 67 softs->os_specific.msi_enabled = TRUE; 68 device_printf(dev, "using MSI-X interrupts (%d vectors)\n", 69 msi_count); 70 } 71 if (!softs->intr_type) { 72 msi_count = 1; 73 if ((error = pci_alloc_msi(dev, &msi_count)) != 0) { 74 device_printf(dev, "alloc msi failed - err=%d; " 75 "will use INTx\n", error); 76 pci_release_msi(dev); 77 } else { 78 softs->os_specific.msi_enabled = TRUE; 79 softs->intr_count = msi_count; 80 softs->intr_type = INTR_TYPE_MSI; 81 device_printf(dev, "using MSI interrupts\n"); 82 } 83 } 84 85 if (!softs->intr_type) { 86 device_printf(dev, "using legacy interrupts\n"); 87 softs->intr_type = INTR_TYPE_FIXED; 88 softs->intr_count = 1; 89 } 90 91 error = bsd_status_to_pqi_status(BSD_SUCCESS); 92 93 DBG_FUNC("OUT\n"); 94 95 return error; 96 } 97 98 void 99 os_eventtaskqueue_enqueue(pqisrc_softstate_t *sc) 100 { 101 taskqueue_enqueue(taskqueue_swi, &sc->os_specific.event_task); 102 } 103 104 void 105 pqisrc_event_worker(void *arg1, int arg2) 106 { 107 pqisrc_ack_all_events(arg1); 108 } 109 110 /* 111 * ithread routine to handle uniprocessor systems 112 */ 113 static void 114 shared_ithread_routine(void *arg) 115 { 116 pqi_intr_ctx_t *intr_ctx = (pqi_intr_ctx_t *)arg; 117 pqisrc_softstate_t *softs = device_get_softc(intr_ctx->pqi_dev); 118 int oq_id = intr_ctx->oq_id; 119 120 DBG_FUNC("IN\n"); 121 122 if (!softs) 123 return; 124 125 pqisrc_process_response_queue(softs, oq_id); 126 pqisrc_process_event_intr_src(softs, oq_id - 1); 127 128 DBG_FUNC("OUT\n"); 129 } 130 131 /* 132 * ithread routine to process non event response 133 */ 134 static void 135 common_ithread_routine(void *arg) 136 { 137 pqi_intr_ctx_t *intr_ctx = (pqi_intr_ctx_t *)arg; 138 pqisrc_softstate_t *softs = device_get_softc(intr_ctx->pqi_dev); 139 int oq_id = intr_ctx->oq_id; 140 141 DBG_FUNC("IN\n"); 142 143 if (!softs) 144 return; 145 146 pqisrc_process_response_queue(softs, oq_id); 147 148 DBG_FUNC("OUT\n"); 149 } 150 151 static void 152 event_ithread_routine(void *arg) 153 { 154 pqi_intr_ctx_t *intr_ctx = (pqi_intr_ctx_t *)arg; 155 pqisrc_softstate_t *softs = device_get_softc(intr_ctx->pqi_dev); 156 int oq_id = intr_ctx->oq_id; 157 158 DBG_FUNC("IN\n"); 159 160 if (!softs) 161 return; 162 163 pqisrc_process_event_intr_src(softs, oq_id); 164 165 DBG_FUNC("OUT\n"); 166 } 167 168 /* 169 * Registration of legacy interrupt in case MSI is unsupported 170 */ 171 int 172 register_legacy_intr(pqisrc_softstate_t *softs) 173 { 174 int error = BSD_SUCCESS; 175 device_t dev; 176 177 DBG_FUNC("IN\n"); 178 179 dev = softs->os_specific.pqi_dev; 180 181 softs->os_specific.pqi_irq_rid[0] = 0; 182 softs->os_specific.pqi_irq[0] = bus_alloc_resource_any(dev, \ 183 SYS_RES_IRQ, &softs->os_specific.pqi_irq_rid[0], 184 RF_ACTIVE | RF_SHAREABLE); 185 if (NULL == softs->os_specific.pqi_irq[0]) { 186 DBG_ERR("Failed to allocate resource for interrupt\n"); 187 return ENXIO; 188 } 189 if ((softs->os_specific.msi_ctx = os_mem_alloc(softs,sizeof(pqi_intr_ctx_t))) == NULL) { 190 DBG_ERR("Failed to allocate memory for msi_ctx\n"); 191 return ENXIO; 192 } 193 softs->os_specific.msi_ctx[0].pqi_dev = dev; 194 /* For Legacy support oq_id should be one */ 195 softs->os_specific.msi_ctx[0].oq_id = 1; 196 197 error = bus_setup_intr(dev, softs->os_specific.pqi_irq[0], 198 INTR_TYPE_CAM | INTR_MPSAFE, \ 199 NULL, shared_ithread_routine, 200 &softs->os_specific.msi_ctx[0], 201 &softs->os_specific.intrcookie[0]); 202 if (error) { 203 DBG_ERR("Failed to setup legacy interrupt err = %d\n", error); 204 return error; 205 } 206 softs->os_specific.intr_registered[0] = TRUE; 207 208 DBG_FUNC("OUT error = %d\n", error); 209 210 return error; 211 } 212 213 /* 214 * Registration of MSIx 215 */ 216 int 217 register_msix_intr(pqisrc_softstate_t *softs) 218 { 219 int error = BSD_SUCCESS; 220 int i = 0; 221 device_t dev = softs->os_specific.pqi_dev; 222 int msix_count = softs->intr_count; 223 size_t msix_size = sizeof(pqi_intr_ctx_t) * msix_count; 224 225 DBG_FUNC("IN\n"); 226 227 softs->os_specific.msi_ctx = os_mem_alloc(softs, msix_size); 228 if (!softs->os_specific.msi_ctx) { 229 DBG_ERR("Memory allocation failed, Requested memory:%lu bytes\n", (unsigned long)msix_size); 230 return ENXIO; 231 } 232 233 /*Add shared handler */ 234 if (softs->share_opq_and_eventq) { 235 softs->os_specific.pqi_irq_rid[i] = i+1; 236 softs->os_specific.pqi_irq[i] = bus_alloc_resource_any(dev, \ 237 SYS_RES_IRQ, 238 &softs->os_specific.pqi_irq_rid[i], 239 RF_SHAREABLE | RF_ACTIVE); 240 if (NULL == softs->os_specific.pqi_irq[i]) { 241 DBG_ERR("Failed to allocate \ 242 event interrupt resource\n"); 243 return ENXIO; 244 } 245 246 softs->os_specific.msi_ctx[i].pqi_dev = dev; 247 softs->os_specific.msi_ctx[i].oq_id = i+1; 248 249 error = bus_setup_intr(dev,softs->os_specific.pqi_irq[i], 250 INTR_TYPE_CAM | INTR_MPSAFE,\ 251 NULL, 252 shared_ithread_routine, 253 &softs->os_specific.msi_ctx[i], 254 &softs->os_specific.intrcookie[i]); 255 256 if (error) { 257 DBG_ERR("Failed to setup interrupt for events r=%d\n", 258 error); 259 return error; 260 } 261 softs->os_specific.intr_registered[i] = TRUE; 262 } 263 else { 264 /* Add event handler */ 265 softs->os_specific.pqi_irq_rid[i] = i+1; 266 softs->os_specific.pqi_irq[i] = bus_alloc_resource_any(dev, \ 267 SYS_RES_IRQ, 268 &softs->os_specific.pqi_irq_rid[i], 269 RF_SHAREABLE | RF_ACTIVE); 270 if (NULL == softs->os_specific.pqi_irq[i]) { 271 DBG_ERR("Failed to allocate event interrupt resource\n"); 272 return ENXIO; 273 } 274 275 softs->os_specific.msi_ctx[i].pqi_dev = dev; 276 softs->os_specific.msi_ctx[i].oq_id = i; 277 278 error = bus_setup_intr(dev,softs->os_specific.pqi_irq[i], 279 INTR_TYPE_CAM | INTR_MPSAFE,\ 280 NULL, 281 event_ithread_routine, 282 &softs->os_specific.msi_ctx[i], 283 &softs->os_specific.intrcookie[i]); 284 if (error) { 285 DBG_ERR("Failed to setup interrupt for events err=%d\n", 286 error); 287 return error; 288 } 289 softs->os_specific.intr_registered[i] = TRUE; 290 /* Add interrupt handlers*/ 291 for (i = 1; i < msix_count; ++i) { 292 softs->os_specific.pqi_irq_rid[i] = i+1; 293 softs->os_specific.pqi_irq[i] = \ 294 bus_alloc_resource_any(dev, 295 SYS_RES_IRQ, 296 &softs->os_specific.pqi_irq_rid[i], 297 RF_SHAREABLE | RF_ACTIVE); 298 if (NULL == softs->os_specific.pqi_irq[i]) { 299 DBG_ERR("Failed to allocate \ 300 msi/x interrupt resource\n"); 301 return ENXIO; 302 } 303 softs->os_specific.msi_ctx[i].pqi_dev = dev; 304 softs->os_specific.msi_ctx[i].oq_id = i; 305 error = bus_setup_intr(dev, 306 softs->os_specific.pqi_irq[i], 307 INTR_TYPE_CAM | INTR_MPSAFE,\ 308 NULL, 309 common_ithread_routine, 310 &softs->os_specific.msi_ctx[i], 311 &softs->os_specific.intrcookie[i]); 312 if (error) { 313 DBG_ERR("Failed to setup \ 314 msi/x interrupt error = %d\n", error); 315 return error; 316 } 317 softs->os_specific.intr_registered[i] = TRUE; 318 } 319 } 320 321 DBG_FUNC("OUT error = %d\n", error); 322 323 return error; 324 } 325 326 /* 327 * Setup interrupt depending on the configuration 328 */ 329 int 330 os_setup_intr(pqisrc_softstate_t *softs) 331 { 332 int bsd_status, pqi_status; 333 334 DBG_FUNC("IN\n"); 335 336 if (softs->intr_type == INTR_TYPE_FIXED) { 337 bsd_status = register_legacy_intr(softs); 338 } 339 else { 340 bsd_status = register_msix_intr(softs); 341 } 342 343 if (bsd_status) 344 DBG_WARN("interrupt registration is failed, error = %d\n", bsd_status); 345 346 pqi_status = bsd_status_to_pqi_status(bsd_status); 347 348 DBG_FUNC("OUT\n"); 349 350 return pqi_status; 351 } 352 353 /* 354 * Deregistration of legacy interrupt 355 */ 356 void 357 deregister_pqi_intx(pqisrc_softstate_t *softs) 358 { 359 device_t dev = softs->os_specific.pqi_dev; 360 361 DBG_FUNC("IN\n"); 362 363 if (softs->os_specific.pqi_irq[0] != NULL) { 364 if (softs->os_specific.intr_registered[0]) { 365 bus_teardown_intr(dev, softs->os_specific.pqi_irq[0], 366 softs->os_specific.intrcookie[0]); 367 softs->os_specific.intr_registered[0] = FALSE; 368 } 369 bus_release_resource(dev, SYS_RES_IRQ, 370 softs->os_specific.pqi_irq_rid[0], 371 softs->os_specific.pqi_irq[0]); 372 softs->os_specific.pqi_irq[0] = NULL; 373 os_mem_free(softs, (char*)softs->os_specific.msi_ctx, sizeof(pqi_intr_ctx_t)); 374 } 375 376 DBG_FUNC("OUT\n"); 377 } 378 379 /* 380 * Deregistration of MSIx interrupt 381 */ 382 void 383 deregister_pqi_msix(pqisrc_softstate_t *softs) 384 { 385 device_t dev = softs->os_specific.pqi_dev; 386 int msix_count = softs->intr_count; 387 int i = 0; 388 389 DBG_FUNC("IN\n"); 390 391 os_mem_free(softs, (char*)softs->os_specific.msi_ctx, sizeof(pqi_intr_ctx_t) * msix_count); 392 softs->os_specific.msi_ctx = NULL; 393 394 for (; i < msix_count; ++i) { 395 if (softs->os_specific.pqi_irq[i] != NULL) { 396 if (softs->os_specific.intr_registered[i]) { 397 bus_teardown_intr(dev, 398 softs->os_specific.pqi_irq[i], 399 softs->os_specific.intrcookie[i]); 400 softs->os_specific.intr_registered[i] = FALSE; 401 } 402 bus_release_resource(dev, SYS_RES_IRQ, 403 softs->os_specific.pqi_irq_rid[i], 404 softs->os_specific.pqi_irq[i]); 405 softs->os_specific.pqi_irq[i] = NULL; 406 } 407 } 408 409 DBG_FUNC("OUT\n"); 410 } 411 412 /* 413 * Function to destroy interrupts registered 414 */ 415 int 416 os_destroy_intr(pqisrc_softstate_t *softs) 417 { 418 device_t dev = softs->os_specific.pqi_dev; 419 420 DBG_FUNC("IN\n"); 421 422 if (softs->intr_type == INTR_TYPE_FIXED) { 423 deregister_pqi_intx(softs); 424 } else if (softs->intr_type == INTR_TYPE_MSIX) { 425 deregister_pqi_msix(softs); 426 } 427 if (softs->os_specific.msi_enabled) { 428 pci_release_msi(dev); 429 softs->os_specific.msi_enabled = FALSE; 430 } 431 432 DBG_FUNC("OUT\n"); 433 434 return PQI_STATUS_SUCCESS; 435 } 436 437 /* 438 * Free interrupt related resources for the adapter 439 */ 440 void 441 os_free_intr_config(pqisrc_softstate_t *softs) 442 { 443 device_t dev = softs->os_specific.pqi_dev; 444 445 DBG_FUNC("IN\n"); 446 447 if (softs->os_specific.msi_enabled) { 448 pci_release_msi(dev); 449 softs->os_specific.msi_enabled = FALSE; 450 } 451 452 DBG_FUNC("OUT\n"); 453 } 454