diff options
Diffstat (limited to 'drivers/pci/msi.c')
-rw-r--r-- | drivers/pci/msi.c | 354 |
1 files changed, 233 insertions, 121 deletions
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 3fcd67a16677..955ab7990c5b 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c | |||
@@ -116,7 +116,7 @@ void __weak arch_teardown_msi_irqs(struct pci_dev *dev) | |||
116 | return default_teardown_msi_irqs(dev); | 116 | return default_teardown_msi_irqs(dev); |
117 | } | 117 | } |
118 | 118 | ||
119 | void default_restore_msi_irqs(struct pci_dev *dev, int irq) | 119 | static void default_restore_msi_irq(struct pci_dev *dev, int irq) |
120 | { | 120 | { |
121 | struct msi_desc *entry; | 121 | struct msi_desc *entry; |
122 | 122 | ||
@@ -134,9 +134,9 @@ void default_restore_msi_irqs(struct pci_dev *dev, int irq) | |||
134 | write_msi_msg(irq, &entry->msg); | 134 | write_msi_msg(irq, &entry->msg); |
135 | } | 135 | } |
136 | 136 | ||
137 | void __weak arch_restore_msi_irqs(struct pci_dev *dev, int irq) | 137 | void __weak arch_restore_msi_irqs(struct pci_dev *dev) |
138 | { | 138 | { |
139 | return default_restore_msi_irqs(dev, irq); | 139 | return default_restore_msi_irqs(dev); |
140 | } | 140 | } |
141 | 141 | ||
142 | static void msi_set_enable(struct pci_dev *dev, int enable) | 142 | static void msi_set_enable(struct pci_dev *dev, int enable) |
@@ -262,6 +262,15 @@ void unmask_msi_irq(struct irq_data *data) | |||
262 | msi_set_mask_bit(data, 0); | 262 | msi_set_mask_bit(data, 0); |
263 | } | 263 | } |
264 | 264 | ||
265 | void default_restore_msi_irqs(struct pci_dev *dev) | ||
266 | { | ||
267 | struct msi_desc *entry; | ||
268 | |||
269 | list_for_each_entry(entry, &dev->msi_list, list) { | ||
270 | default_restore_msi_irq(dev, entry->irq); | ||
271 | } | ||
272 | } | ||
273 | |||
265 | void __read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) | 274 | void __read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) |
266 | { | 275 | { |
267 | BUG_ON(entry->dev->current_state != PCI_D0); | 276 | BUG_ON(entry->dev->current_state != PCI_D0); |
@@ -363,6 +372,9 @@ void write_msi_msg(unsigned int irq, struct msi_msg *msg) | |||
363 | static void free_msi_irqs(struct pci_dev *dev) | 372 | static void free_msi_irqs(struct pci_dev *dev) |
364 | { | 373 | { |
365 | struct msi_desc *entry, *tmp; | 374 | struct msi_desc *entry, *tmp; |
375 | struct attribute **msi_attrs; | ||
376 | struct device_attribute *dev_attr; | ||
377 | int count = 0; | ||
366 | 378 | ||
367 | list_for_each_entry(entry, &dev->msi_list, list) { | 379 | list_for_each_entry(entry, &dev->msi_list, list) { |
368 | int i, nvec; | 380 | int i, nvec; |
@@ -398,6 +410,22 @@ static void free_msi_irqs(struct pci_dev *dev) | |||
398 | list_del(&entry->list); | 410 | list_del(&entry->list); |
399 | kfree(entry); | 411 | kfree(entry); |
400 | } | 412 | } |
413 | |||
414 | if (dev->msi_irq_groups) { | ||
415 | sysfs_remove_groups(&dev->dev.kobj, dev->msi_irq_groups); | ||
416 | msi_attrs = dev->msi_irq_groups[0]->attrs; | ||
417 | list_for_each_entry(entry, &dev->msi_list, list) { | ||
418 | dev_attr = container_of(msi_attrs[count], | ||
419 | struct device_attribute, attr); | ||
420 | kfree(dev_attr->attr.name); | ||
421 | kfree(dev_attr); | ||
422 | ++count; | ||
423 | } | ||
424 | kfree(msi_attrs); | ||
425 | kfree(dev->msi_irq_groups[0]); | ||
426 | kfree(dev->msi_irq_groups); | ||
427 | dev->msi_irq_groups = NULL; | ||
428 | } | ||
401 | } | 429 | } |
402 | 430 | ||
403 | static struct msi_desc *alloc_msi_entry(struct pci_dev *dev) | 431 | static struct msi_desc *alloc_msi_entry(struct pci_dev *dev) |
@@ -430,7 +458,7 @@ static void __pci_restore_msi_state(struct pci_dev *dev) | |||
430 | 458 | ||
431 | pci_intx_for_msi(dev, 0); | 459 | pci_intx_for_msi(dev, 0); |
432 | msi_set_enable(dev, 0); | 460 | msi_set_enable(dev, 0); |
433 | arch_restore_msi_irqs(dev, dev->irq); | 461 | arch_restore_msi_irqs(dev); |
434 | 462 | ||
435 | pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control); | 463 | pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control); |
436 | msi_mask_irq(entry, msi_capable_mask(control), entry->masked); | 464 | msi_mask_irq(entry, msi_capable_mask(control), entry->masked); |
@@ -455,8 +483,8 @@ static void __pci_restore_msix_state(struct pci_dev *dev) | |||
455 | control |= PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL; | 483 | control |= PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL; |
456 | pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, control); | 484 | pci_write_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, control); |
457 | 485 | ||
486 | arch_restore_msi_irqs(dev); | ||
458 | list_for_each_entry(entry, &dev->msi_list, list) { | 487 | list_for_each_entry(entry, &dev->msi_list, list) { |
459 | arch_restore_msi_irqs(dev, entry->irq); | ||
460 | msix_mask_irq(entry, entry->masked); | 488 | msix_mask_irq(entry, entry->masked); |
461 | } | 489 | } |
462 | 490 | ||
@@ -471,95 +499,103 @@ void pci_restore_msi_state(struct pci_dev *dev) | |||
471 | } | 499 | } |
472 | EXPORT_SYMBOL_GPL(pci_restore_msi_state); | 500 | EXPORT_SYMBOL_GPL(pci_restore_msi_state); |
473 | 501 | ||
474 | 502 | static ssize_t msi_mode_show(struct device *dev, struct device_attribute *attr, | |
475 | #define to_msi_attr(obj) container_of(obj, struct msi_attribute, attr) | ||
476 | #define to_msi_desc(obj) container_of(obj, struct msi_desc, kobj) | ||
477 | |||
478 | struct msi_attribute { | ||
479 | struct attribute attr; | ||
480 | ssize_t (*show)(struct msi_desc *entry, struct msi_attribute *attr, | ||
481 | char *buf); | ||
482 | ssize_t (*store)(struct msi_desc *entry, struct msi_attribute *attr, | ||
483 | const char *buf, size_t count); | ||
484 | }; | ||
485 | |||
486 | static ssize_t show_msi_mode(struct msi_desc *entry, struct msi_attribute *atr, | ||
487 | char *buf) | 503 | char *buf) |
488 | { | 504 | { |
489 | return sprintf(buf, "%s\n", entry->msi_attrib.is_msix ? "msix" : "msi"); | 505 | struct pci_dev *pdev = to_pci_dev(dev); |
490 | } | 506 | struct msi_desc *entry; |
491 | 507 | unsigned long irq; | |
492 | static ssize_t msi_irq_attr_show(struct kobject *kobj, | 508 | int retval; |
493 | struct attribute *attr, char *buf) | ||
494 | { | ||
495 | struct msi_attribute *attribute = to_msi_attr(attr); | ||
496 | struct msi_desc *entry = to_msi_desc(kobj); | ||
497 | |||
498 | if (!attribute->show) | ||
499 | return -EIO; | ||
500 | |||
501 | return attribute->show(entry, attribute, buf); | ||
502 | } | ||
503 | |||
504 | static const struct sysfs_ops msi_irq_sysfs_ops = { | ||
505 | .show = msi_irq_attr_show, | ||
506 | }; | ||
507 | |||
508 | static struct msi_attribute mode_attribute = | ||
509 | __ATTR(mode, S_IRUGO, show_msi_mode, NULL); | ||
510 | |||
511 | 509 | ||
512 | static struct attribute *msi_irq_default_attrs[] = { | 510 | retval = kstrtoul(attr->attr.name, 10, &irq); |
513 | &mode_attribute.attr, | 511 | if (retval) |
514 | NULL | 512 | return retval; |
515 | }; | ||
516 | 513 | ||
517 | static void msi_kobj_release(struct kobject *kobj) | 514 | list_for_each_entry(entry, &pdev->msi_list, list) { |
518 | { | 515 | if (entry->irq == irq) { |
519 | struct msi_desc *entry = to_msi_desc(kobj); | 516 | return sprintf(buf, "%s\n", |
520 | 517 | entry->msi_attrib.is_msix ? "msix" : "msi"); | |
521 | pci_dev_put(entry->dev); | 518 | } |
519 | } | ||
520 | return -ENODEV; | ||
522 | } | 521 | } |
523 | 522 | ||
524 | static struct kobj_type msi_irq_ktype = { | ||
525 | .release = msi_kobj_release, | ||
526 | .sysfs_ops = &msi_irq_sysfs_ops, | ||
527 | .default_attrs = msi_irq_default_attrs, | ||
528 | }; | ||
529 | |||
530 | static int populate_msi_sysfs(struct pci_dev *pdev) | 523 | static int populate_msi_sysfs(struct pci_dev *pdev) |
531 | { | 524 | { |
525 | struct attribute **msi_attrs; | ||
526 | struct attribute *msi_attr; | ||
527 | struct device_attribute *msi_dev_attr; | ||
528 | struct attribute_group *msi_irq_group; | ||
529 | const struct attribute_group **msi_irq_groups; | ||
532 | struct msi_desc *entry; | 530 | struct msi_desc *entry; |
533 | struct kobject *kobj; | 531 | int ret = -ENOMEM; |
534 | int ret; | 532 | int num_msi = 0; |
535 | int count = 0; | 533 | int count = 0; |
536 | 534 | ||
537 | pdev->msi_kset = kset_create_and_add("msi_irqs", NULL, &pdev->dev.kobj); | 535 | /* Determine how many msi entries we have */ |
538 | if (!pdev->msi_kset) | 536 | list_for_each_entry(entry, &pdev->msi_list, list) { |
539 | return -ENOMEM; | 537 | ++num_msi; |
538 | } | ||
539 | if (!num_msi) | ||
540 | return 0; | ||
540 | 541 | ||
542 | /* Dynamically create the MSI attributes for the PCI device */ | ||
543 | msi_attrs = kzalloc(sizeof(void *) * (num_msi + 1), GFP_KERNEL); | ||
544 | if (!msi_attrs) | ||
545 | return -ENOMEM; | ||
541 | list_for_each_entry(entry, &pdev->msi_list, list) { | 546 | list_for_each_entry(entry, &pdev->msi_list, list) { |
542 | kobj = &entry->kobj; | 547 | char *name = kmalloc(20, GFP_KERNEL); |
543 | kobj->kset = pdev->msi_kset; | 548 | if (!name) |
544 | pci_dev_get(pdev); | 549 | goto error_attrs; |
545 | ret = kobject_init_and_add(kobj, &msi_irq_ktype, NULL, | 550 | |
546 | "%u", entry->irq); | 551 | msi_dev_attr = kzalloc(sizeof(*msi_dev_attr), GFP_KERNEL); |
547 | if (ret) | 552 | if (!msi_dev_attr) { |
548 | goto out_unroll; | 553 | kfree(name); |
549 | 554 | goto error_attrs; | |
550 | count++; | 555 | } |
556 | |||
557 | sprintf(name, "%d", entry->irq); | ||
558 | sysfs_attr_init(&msi_dev_attr->attr); | ||
559 | msi_dev_attr->attr.name = name; | ||
560 | msi_dev_attr->attr.mode = S_IRUGO; | ||
561 | msi_dev_attr->show = msi_mode_show; | ||
562 | msi_attrs[count] = &msi_dev_attr->attr; | ||
563 | ++count; | ||
551 | } | 564 | } |
552 | 565 | ||
566 | msi_irq_group = kzalloc(sizeof(*msi_irq_group), GFP_KERNEL); | ||
567 | if (!msi_irq_group) | ||
568 | goto error_attrs; | ||
569 | msi_irq_group->name = "msi_irqs"; | ||
570 | msi_irq_group->attrs = msi_attrs; | ||
571 | |||
572 | msi_irq_groups = kzalloc(sizeof(void *) * 2, GFP_KERNEL); | ||
573 | if (!msi_irq_groups) | ||
574 | goto error_irq_group; | ||
575 | msi_irq_groups[0] = msi_irq_group; | ||
576 | |||
577 | ret = sysfs_create_groups(&pdev->dev.kobj, msi_irq_groups); | ||
578 | if (ret) | ||
579 | goto error_irq_groups; | ||
580 | pdev->msi_irq_groups = msi_irq_groups; | ||
581 | |||
553 | return 0; | 582 | return 0; |
554 | 583 | ||
555 | out_unroll: | 584 | error_irq_groups: |
556 | list_for_each_entry(entry, &pdev->msi_list, list) { | 585 | kfree(msi_irq_groups); |
557 | if (!count) | 586 | error_irq_group: |
558 | break; | 587 | kfree(msi_irq_group); |
559 | kobject_del(&entry->kobj); | 588 | error_attrs: |
560 | kobject_put(&entry->kobj); | 589 | count = 0; |
561 | count--; | 590 | msi_attr = msi_attrs[count]; |
591 | while (msi_attr) { | ||
592 | msi_dev_attr = container_of(msi_attr, struct device_attribute, attr); | ||
593 | kfree(msi_attr->name); | ||
594 | kfree(msi_dev_attr); | ||
595 | ++count; | ||
596 | msi_attr = msi_attrs[count]; | ||
562 | } | 597 | } |
598 | kfree(msi_attrs); | ||
563 | return ret; | 599 | return ret; |
564 | } | 600 | } |
565 | 601 | ||
@@ -729,7 +765,7 @@ static int msix_capability_init(struct pci_dev *dev, | |||
729 | 765 | ||
730 | ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX); | 766 | ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX); |
731 | if (ret) | 767 | if (ret) |
732 | goto error; | 768 | goto out_avail; |
733 | 769 | ||
734 | /* | 770 | /* |
735 | * Some devices require MSI-X to be enabled before we can touch the | 771 | * Some devices require MSI-X to be enabled before we can touch the |
@@ -742,10 +778,8 @@ static int msix_capability_init(struct pci_dev *dev, | |||
742 | msix_program_entries(dev, entries); | 778 | msix_program_entries(dev, entries); |
743 | 779 | ||
744 | ret = populate_msi_sysfs(dev); | 780 | ret = populate_msi_sysfs(dev); |
745 | if (ret) { | 781 | if (ret) |
746 | ret = 0; | 782 | goto out_free; |
747 | goto error; | ||
748 | } | ||
749 | 783 | ||
750 | /* Set MSI-X enabled bits and unmask the function */ | 784 | /* Set MSI-X enabled bits and unmask the function */ |
751 | pci_intx_for_msi(dev, 0); | 785 | pci_intx_for_msi(dev, 0); |
@@ -756,7 +790,7 @@ static int msix_capability_init(struct pci_dev *dev, | |||
756 | 790 | ||
757 | return 0; | 791 | return 0; |
758 | 792 | ||
759 | error: | 793 | out_avail: |
760 | if (ret < 0) { | 794 | if (ret < 0) { |
761 | /* | 795 | /* |
762 | * If we had some success, report the number of irqs | 796 | * If we had some success, report the number of irqs |
@@ -773,6 +807,7 @@ error: | |||
773 | ret = avail; | 807 | ret = avail; |
774 | } | 808 | } |
775 | 809 | ||
810 | out_free: | ||
776 | free_msi_irqs(dev); | 811 | free_msi_irqs(dev); |
777 | 812 | ||
778 | return ret; | 813 | return ret; |
@@ -824,6 +859,31 @@ static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type) | |||
824 | } | 859 | } |
825 | 860 | ||
826 | /** | 861 | /** |
862 | * pci_msi_vec_count - Return the number of MSI vectors a device can send | ||
863 | * @dev: device to report about | ||
864 | * | ||
865 | * This function returns the number of MSI vectors a device requested via | ||
866 | * Multiple Message Capable register. It returns a negative errno if the | ||
867 | * device is not capable sending MSI interrupts. Otherwise, the call succeeds | ||
868 | * and returns a power of two, up to a maximum of 2^5 (32), according to the | ||
869 | * MSI specification. | ||
870 | **/ | ||
871 | int pci_msi_vec_count(struct pci_dev *dev) | ||
872 | { | ||
873 | int ret; | ||
874 | u16 msgctl; | ||
875 | |||
876 | if (!dev->msi_cap) | ||
877 | return -EINVAL; | ||
878 | |||
879 | pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &msgctl); | ||
880 | ret = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1); | ||
881 | |||
882 | return ret; | ||
883 | } | ||
884 | EXPORT_SYMBOL(pci_msi_vec_count); | ||
885 | |||
886 | /** | ||
827 | * pci_enable_msi_block - configure device's MSI capability structure | 887 | * pci_enable_msi_block - configure device's MSI capability structure |
828 | * @dev: device to configure | 888 | * @dev: device to configure |
829 | * @nvec: number of interrupts to configure | 889 | * @nvec: number of interrupts to configure |
@@ -836,16 +896,16 @@ static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type) | |||
836 | * updates the @dev's irq member to the lowest new interrupt number; the | 896 | * updates the @dev's irq member to the lowest new interrupt number; the |
837 | * other interrupt numbers allocated to this device are consecutive. | 897 | * other interrupt numbers allocated to this device are consecutive. |
838 | */ | 898 | */ |
839 | int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec) | 899 | int pci_enable_msi_block(struct pci_dev *dev, int nvec) |
840 | { | 900 | { |
841 | int status, maxvec; | 901 | int status, maxvec; |
842 | u16 msgctl; | ||
843 | 902 | ||
844 | if (!dev->msi_cap || dev->current_state != PCI_D0) | 903 | if (dev->current_state != PCI_D0) |
845 | return -EINVAL; | 904 | return -EINVAL; |
846 | 905 | ||
847 | pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &msgctl); | 906 | maxvec = pci_msi_vec_count(dev); |
848 | maxvec = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1); | 907 | if (maxvec < 0) |
908 | return maxvec; | ||
849 | if (nvec > maxvec) | 909 | if (nvec > maxvec) |
850 | return maxvec; | 910 | return maxvec; |
851 | 911 | ||
@@ -867,31 +927,6 @@ int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec) | |||
867 | } | 927 | } |
868 | EXPORT_SYMBOL(pci_enable_msi_block); | 928 | EXPORT_SYMBOL(pci_enable_msi_block); |
869 | 929 | ||
870 | int pci_enable_msi_block_auto(struct pci_dev *dev, unsigned int *maxvec) | ||
871 | { | ||
872 | int ret, nvec; | ||
873 | u16 msgctl; | ||
874 | |||
875 | if (!dev->msi_cap || dev->current_state != PCI_D0) | ||
876 | return -EINVAL; | ||
877 | |||
878 | pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &msgctl); | ||
879 | ret = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1); | ||
880 | |||
881 | if (maxvec) | ||
882 | *maxvec = ret; | ||
883 | |||
884 | do { | ||
885 | nvec = ret; | ||
886 | ret = pci_enable_msi_block(dev, nvec); | ||
887 | } while (ret > 0); | ||
888 | |||
889 | if (ret < 0) | ||
890 | return ret; | ||
891 | return nvec; | ||
892 | } | ||
893 | EXPORT_SYMBOL(pci_enable_msi_block_auto); | ||
894 | |||
895 | void pci_msi_shutdown(struct pci_dev *dev) | 930 | void pci_msi_shutdown(struct pci_dev *dev) |
896 | { | 931 | { |
897 | struct msi_desc *desc; | 932 | struct msi_desc *desc; |
@@ -925,25 +960,28 @@ void pci_disable_msi(struct pci_dev *dev) | |||
925 | 960 | ||
926 | pci_msi_shutdown(dev); | 961 | pci_msi_shutdown(dev); |
927 | free_msi_irqs(dev); | 962 | free_msi_irqs(dev); |
928 | kset_unregister(dev->msi_kset); | ||
929 | dev->msi_kset = NULL; | ||
930 | } | 963 | } |
931 | EXPORT_SYMBOL(pci_disable_msi); | 964 | EXPORT_SYMBOL(pci_disable_msi); |
932 | 965 | ||
933 | /** | 966 | /** |
934 | * pci_msix_table_size - return the number of device's MSI-X table entries | 967 | * pci_msix_vec_count - return the number of device's MSI-X table entries |
935 | * @dev: pointer to the pci_dev data structure of MSI-X device function | 968 | * @dev: pointer to the pci_dev data structure of MSI-X device function |
936 | */ | 969 | * This function returns the number of device's MSI-X table entries and |
937 | int pci_msix_table_size(struct pci_dev *dev) | 970 | * therefore the number of MSI-X vectors device is capable of sending. |
971 | * It returns a negative errno if the device is not capable of sending MSI-X | ||
972 | * interrupts. | ||
973 | **/ | ||
974 | int pci_msix_vec_count(struct pci_dev *dev) | ||
938 | { | 975 | { |
939 | u16 control; | 976 | u16 control; |
940 | 977 | ||
941 | if (!dev->msix_cap) | 978 | if (!dev->msix_cap) |
942 | return 0; | 979 | return -EINVAL; |
943 | 980 | ||
944 | pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &control); | 981 | pci_read_config_word(dev, dev->msix_cap + PCI_MSIX_FLAGS, &control); |
945 | return msix_table_size(control); | 982 | return msix_table_size(control); |
946 | } | 983 | } |
984 | EXPORT_SYMBOL(pci_msix_vec_count); | ||
947 | 985 | ||
948 | /** | 986 | /** |
949 | * pci_enable_msix - configure device's MSI-X capability structure | 987 | * pci_enable_msix - configure device's MSI-X capability structure |
@@ -972,7 +1010,9 @@ int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec) | |||
972 | if (status) | 1010 | if (status) |
973 | return status; | 1011 | return status; |
974 | 1012 | ||
975 | nr_entries = pci_msix_table_size(dev); | 1013 | nr_entries = pci_msix_vec_count(dev); |
1014 | if (nr_entries < 0) | ||
1015 | return nr_entries; | ||
976 | if (nvec > nr_entries) | 1016 | if (nvec > nr_entries) |
977 | return nr_entries; | 1017 | return nr_entries; |
978 | 1018 | ||
@@ -1023,8 +1063,6 @@ void pci_disable_msix(struct pci_dev *dev) | |||
1023 | 1063 | ||
1024 | pci_msix_shutdown(dev); | 1064 | pci_msix_shutdown(dev); |
1025 | free_msi_irqs(dev); | 1065 | free_msi_irqs(dev); |
1026 | kset_unregister(dev->msi_kset); | ||
1027 | dev->msi_kset = NULL; | ||
1028 | } | 1066 | } |
1029 | EXPORT_SYMBOL(pci_disable_msix); | 1067 | EXPORT_SYMBOL(pci_disable_msix); |
1030 | 1068 | ||
@@ -1079,3 +1117,77 @@ void pci_msi_init_pci_dev(struct pci_dev *dev) | |||
1079 | if (dev->msix_cap) | 1117 | if (dev->msix_cap) |
1080 | msix_set_enable(dev, 0); | 1118 | msix_set_enable(dev, 0); |
1081 | } | 1119 | } |
1120 | |||
1121 | /** | ||
1122 | * pci_enable_msi_range - configure device's MSI capability structure | ||
1123 | * @dev: device to configure | ||
1124 | * @minvec: minimal number of interrupts to configure | ||
1125 | * @maxvec: maximum number of interrupts to configure | ||
1126 | * | ||
1127 | * This function tries to allocate a maximum possible number of interrupts in a | ||
1128 | * range between @minvec and @maxvec. It returns a negative errno if an error | ||
1129 | * occurs. If it succeeds, it returns the actual number of interrupts allocated | ||
1130 | * and updates the @dev's irq member to the lowest new interrupt number; | ||
1131 | * the other interrupt numbers allocated to this device are consecutive. | ||
1132 | **/ | ||
1133 | int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec) | ||
1134 | { | ||
1135 | int nvec = maxvec; | ||
1136 | int rc; | ||
1137 | |||
1138 | if (maxvec < minvec) | ||
1139 | return -ERANGE; | ||
1140 | |||
1141 | do { | ||
1142 | rc = pci_enable_msi_block(dev, nvec); | ||
1143 | if (rc < 0) { | ||
1144 | return rc; | ||
1145 | } else if (rc > 0) { | ||
1146 | if (rc < minvec) | ||
1147 | return -ENOSPC; | ||
1148 | nvec = rc; | ||
1149 | } | ||
1150 | } while (rc); | ||
1151 | |||
1152 | return nvec; | ||
1153 | } | ||
1154 | EXPORT_SYMBOL(pci_enable_msi_range); | ||
1155 | |||
1156 | /** | ||
1157 | * pci_enable_msix_range - configure device's MSI-X capability structure | ||
1158 | * @dev: pointer to the pci_dev data structure of MSI-X device function | ||
1159 | * @entries: pointer to an array of MSI-X entries | ||
1160 | * @minvec: minimum number of MSI-X irqs requested | ||
1161 | * @maxvec: maximum number of MSI-X irqs requested | ||
1162 | * | ||
1163 | * Setup the MSI-X capability structure of device function with a maximum | ||
1164 | * possible number of interrupts in the range between @minvec and @maxvec | ||
1165 | * upon its software driver call to request for MSI-X mode enabled on its | ||
1166 | * hardware device function. It returns a negative errno if an error occurs. | ||
1167 | * If it succeeds, it returns the actual number of interrupts allocated and | ||
1168 | * indicates the successful configuration of MSI-X capability structure | ||
1169 | * with new allocated MSI-X interrupts. | ||
1170 | **/ | ||
1171 | int pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries, | ||
1172 | int minvec, int maxvec) | ||
1173 | { | ||
1174 | int nvec = maxvec; | ||
1175 | int rc; | ||
1176 | |||
1177 | if (maxvec < minvec) | ||
1178 | return -ERANGE; | ||
1179 | |||
1180 | do { | ||
1181 | rc = pci_enable_msix(dev, entries, nvec); | ||
1182 | if (rc < 0) { | ||
1183 | return rc; | ||
1184 | } else if (rc > 0) { | ||
1185 | if (rc < minvec) | ||
1186 | return -ENOSPC; | ||
1187 | nvec = rc; | ||
1188 | } | ||
1189 | } while (rc); | ||
1190 | |||
1191 | return nvec; | ||
1192 | } | ||
1193 | EXPORT_SYMBOL(pci_enable_msix_range); | ||