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