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