1 /* $NetBSD: pci_msi_machdep.c,v 1.9 2015/08/17 06:16:03 knakahara Exp $ */ 2 3 /* 4 * Copyright (c) 2015 Internet Initiative Japan Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * TODO 31 * 32 * - PBA (Pending Bit Array) support 33 * - HyperTransport mapping support 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: pci_msi_machdep.c,v 1.9 2015/08/17 06:16:03 knakahara Exp $"); 38 39 #include "opt_intrdebug.h" 40 41 #include <sys/types.h> 42 #include <sys/param.h> 43 #include <sys/time.h> 44 #include <sys/systm.h> 45 #include <sys/cpu.h> 46 #include <sys/errno.h> 47 #include <sys/device.h> 48 #include <sys/intr.h> 49 #include <sys/kmem.h> 50 #include <sys/malloc.h> 51 52 #include <dev/pci/pcivar.h> 53 54 #include <machine/i82093var.h> 55 #include <machine/pic.h> 56 57 #include <x86/pci/msipic.h> 58 #include <x86/pci/pci_msi_machdep.h> 59 60 #ifdef INTRDEBUG 61 #define MSIDEBUG 62 #endif 63 64 #ifdef MSIDEBUG 65 #define DPRINTF(msg) printf msg 66 #else 67 #define DPRINTF(msg) 68 #endif 69 70 static pci_intr_handle_t 71 pci_msi_calculate_handle(struct pic *msi_pic, int vector) 72 { 73 pci_intr_handle_t pih; 74 75 KASSERT(msipic_is_msi_pic(msi_pic)); 76 77 pih = __SHIFTIN((uint64_t)msipic_get_devid(msi_pic), MSI_INT_DEV_MASK) 78 | __SHIFTIN((uint64_t)vector, MSI_INT_VEC_MASK) 79 | APIC_INT_VIA_MSI; 80 if (msi_pic->pic_type == PIC_MSI) 81 MSI_INT_MAKE_MSI(pih); 82 else if (msi_pic->pic_type == PIC_MSIX) 83 MSI_INT_MAKE_MSIX(pih); 84 else 85 panic("%s: Unexpected pic_type: %d\n", __func__, 86 msi_pic->pic_type); 87 88 return pih; 89 } 90 91 static pci_intr_handle_t * 92 pci_msi_alloc_vectors(struct pic *msi_pic, uint *table_indexes, int *count) 93 { 94 struct intrsource *isp; 95 pci_intr_handle_t *vectors, pih; 96 int i; 97 const char *intrstr; 98 char intrstr_buf[INTRIDBUF]; 99 100 vectors = kmem_zalloc(sizeof(vectors[0]) * (*count), KM_SLEEP); 101 if (vectors == NULL) { 102 DPRINTF(("cannot allocate vectors\n")); 103 return NULL; 104 } 105 106 mutex_enter(&cpu_lock); 107 for (i = 0; i < *count; i++) { 108 u_int table_index; 109 110 if (table_indexes == NULL) 111 table_index = i; 112 else 113 table_index = table_indexes[i]; 114 115 pih = pci_msi_calculate_handle(msi_pic, table_index); 116 117 intrstr = x86_pci_msi_string(NULL, pih, intrstr_buf, 118 sizeof(intrstr_buf)); 119 isp = intr_allocate_io_intrsource(intrstr); 120 if (isp == NULL) { 121 mutex_exit(&cpu_lock); 122 DPRINTF(("can't allocate io_intersource\n")); 123 kmem_free(vectors, sizeof(vectors[0]) * (*count)); 124 return NULL; 125 } 126 127 vectors[i] = pih; 128 } 129 mutex_exit(&cpu_lock); 130 131 return vectors; 132 } 133 134 static void 135 pci_msi_free_vectors(struct pic *msi_pic, pci_intr_handle_t *pihs, int count) 136 { 137 pci_intr_handle_t pih; 138 int i; 139 const char *intrstr; 140 char intrstr_buf[INTRIDBUF]; 141 142 mutex_enter(&cpu_lock); 143 for (i = 0; i < count; i++) { 144 pih = pci_msi_calculate_handle(msi_pic, i); 145 intrstr = x86_pci_msi_string(NULL, pih, intrstr_buf, 146 sizeof(intrstr_buf)); 147 intr_free_io_intrsource(intrstr); 148 } 149 mutex_exit(&cpu_lock); 150 151 kmem_free(pihs, sizeof(pihs[0]) * count); 152 } 153 154 static int 155 pci_msi_alloc_common(pci_intr_handle_t **ihps, int *count, 156 const struct pci_attach_args *pa, bool exact) 157 { 158 struct pic *msi_pic; 159 pci_intr_handle_t *vectors; 160 int error, i; 161 162 if ((pa->pa_flags & PCI_FLAGS_MSI_OKAY) == 0) { 163 DPRINTF(("PCI host bridge does not support MSI.\n")); 164 return ENODEV; 165 } 166 167 msi_pic = msipic_construct_msi_pic(pa); 168 if (msi_pic == NULL) { 169 DPRINTF(("cannot allocate MSI pic.\n")); 170 return EINVAL; 171 } 172 173 vectors = NULL; 174 while (*count > 0) { 175 vectors = pci_msi_alloc_vectors(msi_pic, NULL, count); 176 if (vectors != NULL) 177 break; 178 179 if (exact) { 180 DPRINTF(("cannot allocate MSI vectors.\n")); 181 msipic_destruct_msi_pic(msi_pic); 182 return ENOMEM; 183 } else { 184 (*count) >>= 1; /* must be power of 2. */ 185 continue; 186 } 187 } 188 if (vectors == NULL) { 189 DPRINTF(("cannot allocate MSI vectors.\n")); 190 msipic_destruct_msi_pic(msi_pic); 191 return ENOMEM; 192 } 193 194 for (i = 0; i < *count; i++) { 195 MSI_INT_MAKE_MSI(vectors[i]); 196 } 197 198 error = msipic_set_msi_vectors(msi_pic, NULL, *count); 199 if (error) { 200 pci_msi_free_vectors(msi_pic, vectors, *count); 201 msipic_destruct_msi_pic(msi_pic); 202 return error; 203 } 204 205 *ihps = vectors; 206 return 0; 207 } 208 209 static void * 210 pci_msi_common_establish(pci_chipset_tag_t pc, pci_intr_handle_t ih, 211 int level, int (*func)(void *), void *arg, struct pic *pic, 212 const char *xname) 213 { 214 int irq, pin; 215 bool mpsafe; 216 217 KASSERT(INT_VIA_MSI(ih)); 218 219 irq = -1; 220 pin = MSI_INT_VEC(ih); 221 mpsafe = ((ih & MPSAFE_MASK) != 0); 222 223 return intr_establish_xname(irq, pic, pin, IST_EDGE, level, func, arg, 224 mpsafe, xname); 225 } 226 227 static void 228 pci_msi_common_disestablish(pci_chipset_tag_t pc, void *cookie) 229 { 230 231 intr_disestablish(cookie); 232 } 233 234 static int 235 pci_msix_alloc_common(pci_intr_handle_t **ihps, u_int *table_indexes, 236 int *count, const struct pci_attach_args *pa, bool exact) 237 { 238 struct pic *msix_pic; 239 pci_intr_handle_t *vectors; 240 int error, i; 241 242 if ((pa->pa_flags & PCI_FLAGS_MSIX_OKAY) == 0) { 243 DPRINTF(("PCI host bridge does not support MSI-X.\n")); 244 return ENODEV; 245 } 246 247 msix_pic = msipic_construct_msix_pic(pa); 248 if (msix_pic == NULL) 249 return EINVAL; 250 251 vectors = NULL; 252 while (*count > 0) { 253 vectors = pci_msi_alloc_vectors(msix_pic, table_indexes, count); 254 if (vectors != NULL) 255 break; 256 257 if (exact) { 258 DPRINTF(("cannot allocate MSI-X vectors.\n")); 259 msipic_destruct_msix_pic(msix_pic); 260 return ENOMEM; 261 } else { 262 (*count)--; 263 continue; 264 } 265 } 266 if (vectors == NULL) { 267 DPRINTF(("cannot allocate MSI-X vectors.\n")); 268 msipic_destruct_msix_pic(msix_pic); 269 return ENOMEM; 270 } 271 272 for (i = 0; i < *count; i++) { 273 MSI_INT_MAKE_MSIX(vectors[i]); 274 } 275 276 error = msipic_set_msi_vectors(msix_pic, vectors, *count); 277 if (error) { 278 pci_msi_free_vectors(msix_pic, vectors, *count); 279 msipic_destruct_msix_pic(msix_pic); 280 return error; 281 } 282 283 *ihps = vectors; 284 return 0; 285 } 286 287 static int 288 x86_pci_msi_alloc(pci_intr_handle_t **ihps, int *count, 289 const struct pci_attach_args *pa) 290 { 291 292 return pci_msi_alloc_common(ihps, count, pa, false); 293 } 294 295 static int 296 x86_pci_msi_alloc_exact(pci_intr_handle_t **ihps, int count, 297 const struct pci_attach_args *pa) 298 { 299 300 return pci_msi_alloc_common(ihps, &count, pa, true); 301 } 302 303 static void 304 x86_pci_msi_release_internal(pci_intr_handle_t *pihs, int count) 305 { 306 struct pic *pic; 307 308 pic = msipic_find_msi_pic(MSI_INT_DEV(pihs[0])); 309 if (pic == NULL) 310 return; 311 312 pci_msi_free_vectors(pic, pihs, count); 313 msipic_destruct_msi_pic(pic); 314 } 315 316 static int 317 x86_pci_msix_alloc(pci_intr_handle_t **ihps, int *count, 318 const struct pci_attach_args *pa) 319 { 320 321 return pci_msix_alloc_common(ihps, NULL, count, pa, false); 322 } 323 324 static int 325 x86_pci_msix_alloc_exact(pci_intr_handle_t **ihps, int count, 326 const struct pci_attach_args *pa) 327 { 328 329 return pci_msix_alloc_common(ihps, NULL, &count, pa, true); 330 } 331 332 static int 333 x86_pci_msix_alloc_map(pci_intr_handle_t **ihps, u_int *table_indexes, 334 int count, const struct pci_attach_args *pa) 335 { 336 337 return pci_msix_alloc_common(ihps, table_indexes, &count, pa, true); 338 } 339 340 static void 341 x86_pci_msix_release_internal(pci_intr_handle_t *pihs, int count) 342 { 343 struct pic *pic; 344 345 pic = msipic_find_msi_pic(MSI_INT_DEV(pihs[0])); 346 if (pic == NULL) 347 return; 348 349 pci_msi_free_vectors(pic, pihs, count); 350 msipic_destruct_msix_pic(pic); 351 } 352 353 /*****************************************************************************/ 354 /* 355 * extern for MD code. 356 */ 357 358 /* 359 * Return intrid for a MSI/MSI-X device. 360 * "buf" must be allocated by caller. 361 */ 362 const char * 363 x86_pci_msi_string(pci_chipset_tag_t pc, pci_intr_handle_t ih, char *buf, 364 size_t len) 365 { 366 int dev, vec; 367 368 KASSERT(INT_VIA_MSI(ih)); 369 370 dev = MSI_INT_DEV(ih); 371 vec = MSI_INT_VEC(ih); 372 if (MSI_INT_IS_MSIX(ih)) 373 snprintf(buf, len, "msix%d vec %d", dev, vec); 374 else 375 snprintf(buf, len, "msi%d vec %d", dev, vec); 376 377 return buf; 378 } 379 380 /* 381 * Release MSI handles. 382 */ 383 void 384 x86_pci_msi_release(pci_chipset_tag_t pc, pci_intr_handle_t *pihs, int count) 385 { 386 387 if (count < 1) 388 return; 389 390 return x86_pci_msi_release_internal(pihs, count); 391 } 392 393 /* 394 * Establish a MSI handle. 395 * If multiple MSI handle is requied to establish, device driver must call 396 * this function for each handle. 397 */ 398 void * 399 x86_pci_msi_establish(pci_chipset_tag_t pc, pci_intr_handle_t ih, 400 int level, int (*func)(void *), void *arg, const char *xname) 401 { 402 struct pic *pic; 403 404 pic = msipic_find_msi_pic(MSI_INT_DEV(ih)); 405 if (pic == NULL) { 406 DPRINTF(("pci_intr_handler has no msi_pic\n")); 407 return NULL; 408 } 409 410 return pci_msi_common_establish(pc, ih, level, func, arg, pic, xname); 411 } 412 413 /* 414 * Disestablish a MSI handle. 415 * If multiple MSI handle is requied to disestablish, device driver must call 416 * this function for each handle. 417 */ 418 void 419 x86_pci_msi_disestablish(pci_chipset_tag_t pc, void *cookie) 420 { 421 422 pci_msi_common_disestablish(pc, cookie); 423 } 424 425 /* 426 * Release MSI-X handles. 427 */ 428 void 429 x86_pci_msix_release(pci_chipset_tag_t pc, pci_intr_handle_t *pihs, int count) 430 { 431 432 if (count < 1) 433 return; 434 435 return x86_pci_msix_release_internal(pihs, count); 436 } 437 438 /* 439 * Establish a MSI-X handle. 440 * If multiple MSI-X handle is requied to establish, device driver must call 441 * this function for each handle. 442 */ 443 void * 444 x86_pci_msix_establish(pci_chipset_tag_t pc, pci_intr_handle_t ih, 445 int level, int (*func)(void *), void *arg, const char *xname) 446 { 447 struct pic *pic; 448 449 pic = msipic_find_msi_pic(MSI_INT_DEV(ih)); 450 if (pic == NULL) { 451 DPRINTF(("pci_intr_handler has no msi_pic\n")); 452 return NULL; 453 } 454 455 return pci_msi_common_establish(pc, ih, level, func, arg, pic, xname); 456 } 457 458 /* 459 * Disestablish a MSI-X handle. 460 * If multiple MSI-X handle is requied to disestablish, device driver must call 461 * this function for each handle. 462 */ 463 void 464 x86_pci_msix_disestablish(pci_chipset_tag_t pc, void *cookie) 465 { 466 467 pci_msi_common_disestablish(pc, cookie); 468 } 469 470 /*****************************************************************************/ 471 /* 472 * extern for MI code. 473 */ 474 475 /* 476 * This function is used by device drivers like pci_intr_map(). 477 * 478 * "ihps" is the array of vector numbers which MSI used instead of IRQ number. 479 * "count" must be power of 2. 480 * "count" can decrease if struct intrsource cannot be allocated. 481 * if count == 0, return non-zero value. 482 */ 483 int 484 pci_msi_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, 485 int *count) 486 { 487 int hw_max; 488 489 /* MSI vector count must be power of 2. */ 490 KASSERT(*count > 0); 491 KASSERT(((*count - 1) & *count) == 0); 492 493 hw_max = pci_msi_count(pa->pa_pc, pa->pa_tag); 494 if (hw_max == 0) 495 return ENODEV; 496 497 if (*count > hw_max) { 498 DPRINTF(("cut off MSI count to %d\n", hw_max)); 499 *count = hw_max; /* cut off hw_max */ 500 } 501 502 return x86_pci_msi_alloc(ihps, count, pa); 503 } 504 505 /* 506 * This function is used by device drivers like pci_intr_map(). 507 * 508 * "ihps" is the array of vector numbers which MSI used instead of IRQ number. 509 * "count" must be power of 2. 510 * "count" can not decrease. 511 * If "count" struct intrsources cannot be allocated, return non-zero value. 512 */ 513 int 514 pci_msi_alloc_exact(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, 515 int count) 516 { 517 int hw_max; 518 519 /* MSI vector count must be power of 2. */ 520 KASSERT(count > 0); 521 KASSERT(((count - 1) & count) == 0); 522 523 hw_max = pci_msi_count(pa->pa_pc, pa->pa_tag); 524 if (hw_max == 0) 525 return ENODEV; 526 527 if (count > hw_max) { 528 DPRINTF(("over hardware max MSI count %d\n", hw_max)); 529 return EINVAL; 530 } 531 532 return x86_pci_msi_alloc_exact(ihps, count, pa); 533 } 534 535 /* 536 * This function is used by device drivers like pci_intr_map(). 537 * 538 * "ihps" is the array of vector numbers which MSI-X used instead of IRQ number. 539 * "count" can decrease if enough struct intrsources cannot be allocated. 540 * if count == 0, return non-zero value. 541 */ 542 int 543 pci_msix_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, 544 int *count) 545 { 546 int hw_max; 547 548 KASSERT(*count > 0); 549 550 hw_max = pci_msix_count(pa->pa_pc, pa->pa_tag); 551 if (hw_max == 0) 552 return ENODEV; 553 554 if (*count > hw_max) { 555 DPRINTF(("cut off MSI-X count to %d\n", hw_max)); 556 *count = hw_max; /* cut off hw_max */ 557 } 558 559 return x86_pci_msix_alloc(ihps, count, pa); 560 } 561 562 /* 563 * This function is used by device drivers like pci_intr_map(). 564 * 565 * "ihps" is the array of vector numbers which MSI-X used instead of IRQ number. 566 * "count" can not decrease. 567 * If "count" struct intrsource cannot be allocated, return non-zero value. 568 */ 569 int 570 pci_msix_alloc_exact(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, 571 int count) 572 { 573 int hw_max; 574 575 KASSERT(count > 0); 576 577 hw_max = pci_msix_count(pa->pa_pc, pa->pa_tag); 578 if (hw_max == 0) 579 return ENODEV; 580 581 if (count > hw_max) { 582 DPRINTF(("over hardware max MSI-X count %d\n", hw_max)); 583 return EINVAL; 584 } 585 586 return x86_pci_msix_alloc_exact(ihps, count, pa); 587 } 588 589 /* 590 * This function is used by device drivers like pci_intr_map(). 591 * Futhermore, this function can map each handle to a MSI-X table index. 592 * 593 * "ihps" is the array of vector numbers which MSI-X used instead of IRQ number. 594 * "count" can not decrease. 595 * "map" size must be equal to "count". 596 * If "count" struct intrsource cannot be allocated, return non-zero value. 597 * e.g. 598 * If "map" = { 1, 4, 0 }, 599 * 1st handle is bound to MSI-X index 1 600 * 2nd handle is bound to MSI-X index 4 601 * 3rd handle is bound to MSI-X index 0 602 */ 603 int 604 pci_msix_alloc_map(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, 605 u_int *table_indexes, int count) 606 { 607 int hw_max, i, j; 608 609 KASSERT(count > 0); 610 611 hw_max = pci_msix_count(pa->pa_pc, pa->pa_tag); 612 if (hw_max == 0) 613 return ENODEV; 614 615 if (count > hw_max) { 616 DPRINTF(("over hardware max MSI-X count %d\n", hw_max)); 617 return EINVAL; 618 } 619 620 /* check not to duplicate table_index */ 621 for (i = 0; i < count; i++) { 622 u_int basing = table_indexes[i]; 623 624 KASSERT(table_indexes[i] < PCI_MSIX_MAX_VECTORS); 625 if (basing >= hw_max) { 626 DPRINTF(("table index is over hardware max MSI-X index %d\n", 627 hw_max - 1)); 628 return EINVAL; 629 } 630 631 for (j = i + 1; j < count; j++) { 632 if (basing == table_indexes[j]) { 633 DPRINTF(("MSI-X table index duplicated\n")); 634 return EINVAL; 635 } 636 } 637 } 638 639 return x86_pci_msix_alloc_map(ihps, table_indexes, count, pa); 640 } 641