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