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: stable/11/sys/dev/smartpqi/smartpqi_intr.c 333019 2018-04-26 16:59:06Z sbruno $ */ 28 29#include "smartpqi_includes.h" 30 31 32/* 33 * Function to get processor count 34 */ 35int 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 */ 47int 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 103void os_eventtaskqueue_enqueue(pqisrc_softstate_t *sc) 104{ 105 taskqueue_enqueue(taskqueue_swi, &sc->os_specific.event_task); 106} 107 108void 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 */ 116static 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 */ 133static 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 146static 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 */ 162int 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 = 0; 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 */ 205int 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; 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 */ 315int 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 */ 340void 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 */ 366void 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 */ 399int 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 */ 424void 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