diff options
Diffstat (limited to 'drivers/pci/msi.c')
-rw-r--r-- | drivers/pci/msi.c | 212 |
1 files changed, 131 insertions, 81 deletions
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 9855c4c920b8..55ff52df5fe7 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c | |||
@@ -23,8 +23,6 @@ | |||
23 | #include "pci.h" | 23 | #include "pci.h" |
24 | #include "msi.h" | 24 | #include "msi.h" |
25 | 25 | ||
26 | #define MSI_TARGET_CPU first_cpu(cpu_online_map) | ||
27 | |||
28 | static DEFINE_SPINLOCK(msi_lock); | 26 | static DEFINE_SPINLOCK(msi_lock); |
29 | static struct msi_desc* msi_desc[NR_IRQS] = { [0 ... NR_IRQS-1] = NULL }; | 27 | static struct msi_desc* msi_desc[NR_IRQS] = { [0 ... NR_IRQS-1] = NULL }; |
30 | static kmem_cache_t* msi_cachep; | 28 | static kmem_cache_t* msi_cachep; |
@@ -40,6 +38,15 @@ int vector_irq[NR_VECTORS] = { [0 ... NR_VECTORS - 1] = -1}; | |||
40 | u8 irq_vector[NR_IRQ_VECTORS] = { FIRST_DEVICE_VECTOR , 0 }; | 38 | u8 irq_vector[NR_IRQ_VECTORS] = { FIRST_DEVICE_VECTOR , 0 }; |
41 | #endif | 39 | #endif |
42 | 40 | ||
41 | static struct msi_ops *msi_ops; | ||
42 | |||
43 | int | ||
44 | msi_register(struct msi_ops *ops) | ||
45 | { | ||
46 | msi_ops = ops; | ||
47 | return 0; | ||
48 | } | ||
49 | |||
43 | static void msi_cache_ctor(void *p, kmem_cache_t *cache, unsigned long flags) | 50 | static void msi_cache_ctor(void *p, kmem_cache_t *cache, unsigned long flags) |
44 | { | 51 | { |
45 | memset(p, 0, NR_IRQS * sizeof(struct msi_desc)); | 52 | memset(p, 0, NR_IRQS * sizeof(struct msi_desc)); |
@@ -92,7 +99,7 @@ static void msi_set_mask_bit(unsigned int vector, int flag) | |||
92 | static void set_msi_affinity(unsigned int vector, cpumask_t cpu_mask) | 99 | static void set_msi_affinity(unsigned int vector, cpumask_t cpu_mask) |
93 | { | 100 | { |
94 | struct msi_desc *entry; | 101 | struct msi_desc *entry; |
95 | struct msg_address address; | 102 | u32 address_hi, address_lo; |
96 | unsigned int irq = vector; | 103 | unsigned int irq = vector; |
97 | unsigned int dest_cpu = first_cpu(cpu_mask); | 104 | unsigned int dest_cpu = first_cpu(cpu_mask); |
98 | 105 | ||
@@ -108,28 +115,36 @@ static void set_msi_affinity(unsigned int vector, cpumask_t cpu_mask) | |||
108 | if (!pos) | 115 | if (!pos) |
109 | return; | 116 | return; |
110 | 117 | ||
118 | pci_read_config_dword(entry->dev, msi_upper_address_reg(pos), | ||
119 | &address_hi); | ||
111 | pci_read_config_dword(entry->dev, msi_lower_address_reg(pos), | 120 | pci_read_config_dword(entry->dev, msi_lower_address_reg(pos), |
112 | &address.lo_address.value); | 121 | &address_lo); |
113 | address.lo_address.value &= MSI_ADDRESS_DEST_ID_MASK; | 122 | |
114 | address.lo_address.value |= (cpu_physical_id(dest_cpu) << | 123 | msi_ops->target(vector, dest_cpu, &address_hi, &address_lo); |
115 | MSI_TARGET_CPU_SHIFT); | 124 | |
116 | entry->msi_attrib.current_cpu = cpu_physical_id(dest_cpu); | 125 | pci_write_config_dword(entry->dev, msi_upper_address_reg(pos), |
126 | address_hi); | ||
117 | pci_write_config_dword(entry->dev, msi_lower_address_reg(pos), | 127 | pci_write_config_dword(entry->dev, msi_lower_address_reg(pos), |
118 | address.lo_address.value); | 128 | address_lo); |
119 | set_native_irq_info(irq, cpu_mask); | 129 | set_native_irq_info(irq, cpu_mask); |
120 | break; | 130 | break; |
121 | } | 131 | } |
122 | case PCI_CAP_ID_MSIX: | 132 | case PCI_CAP_ID_MSIX: |
123 | { | 133 | { |
124 | int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + | 134 | int offset_hi = |
125 | PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET; | 135 | entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + |
126 | 136 | PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET; | |
127 | address.lo_address.value = readl(entry->mask_base + offset); | 137 | int offset_lo = |
128 | address.lo_address.value &= MSI_ADDRESS_DEST_ID_MASK; | 138 | entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + |
129 | address.lo_address.value |= (cpu_physical_id(dest_cpu) << | 139 | PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET; |
130 | MSI_TARGET_CPU_SHIFT); | 140 | |
131 | entry->msi_attrib.current_cpu = cpu_physical_id(dest_cpu); | 141 | address_hi = readl(entry->mask_base + offset_hi); |
132 | writel(address.lo_address.value, entry->mask_base + offset); | 142 | address_lo = readl(entry->mask_base + offset_lo); |
143 | |||
144 | msi_ops->target(vector, dest_cpu, &address_hi, &address_lo); | ||
145 | |||
146 | writel(address_hi, entry->mask_base + offset_hi); | ||
147 | writel(address_lo, entry->mask_base + offset_lo); | ||
133 | set_native_irq_info(irq, cpu_mask); | 148 | set_native_irq_info(irq, cpu_mask); |
134 | break; | 149 | break; |
135 | } | 150 | } |
@@ -251,30 +266,6 @@ static struct hw_interrupt_type msi_irq_wo_maskbit_type = { | |||
251 | .set_affinity = set_msi_affinity | 266 | .set_affinity = set_msi_affinity |
252 | }; | 267 | }; |
253 | 268 | ||
254 | static void msi_data_init(struct msg_data *msi_data, | ||
255 | unsigned int vector) | ||
256 | { | ||
257 | memset(msi_data, 0, sizeof(struct msg_data)); | ||
258 | msi_data->vector = (u8)vector; | ||
259 | msi_data->delivery_mode = MSI_DELIVERY_MODE; | ||
260 | msi_data->level = MSI_LEVEL_MODE; | ||
261 | msi_data->trigger = MSI_TRIGGER_MODE; | ||
262 | } | ||
263 | |||
264 | static void msi_address_init(struct msg_address *msi_address) | ||
265 | { | ||
266 | unsigned int dest_id; | ||
267 | unsigned long dest_phys_id = cpu_physical_id(MSI_TARGET_CPU); | ||
268 | |||
269 | memset(msi_address, 0, sizeof(struct msg_address)); | ||
270 | msi_address->hi_address = (u32)0; | ||
271 | dest_id = (MSI_ADDRESS_HEADER << MSI_ADDRESS_HEADER_SHIFT); | ||
272 | msi_address->lo_address.u.dest_mode = MSI_PHYSICAL_MODE; | ||
273 | msi_address->lo_address.u.redirection_hint = MSI_REDIRECTION_HINT_MODE; | ||
274 | msi_address->lo_address.u.dest_id = dest_id; | ||
275 | msi_address->lo_address.value |= (dest_phys_id << MSI_TARGET_CPU_SHIFT); | ||
276 | } | ||
277 | |||
278 | static int msi_free_vector(struct pci_dev* dev, int vector, int reassign); | 269 | static int msi_free_vector(struct pci_dev* dev, int vector, int reassign); |
279 | static int assign_msi_vector(void) | 270 | static int assign_msi_vector(void) |
280 | { | 271 | { |
@@ -369,13 +360,29 @@ static int msi_init(void) | |||
369 | return status; | 360 | return status; |
370 | } | 361 | } |
371 | 362 | ||
363 | status = msi_arch_init(); | ||
364 | if (status < 0) { | ||
365 | pci_msi_enable = 0; | ||
366 | printk(KERN_WARNING | ||
367 | "PCI: MSI arch init failed. MSI disabled.\n"); | ||
368 | return status; | ||
369 | } | ||
370 | |||
371 | if (! msi_ops) { | ||
372 | printk(KERN_WARNING | ||
373 | "PCI: MSI ops not registered. MSI disabled.\n"); | ||
374 | status = -EINVAL; | ||
375 | return status; | ||
376 | } | ||
377 | |||
378 | last_alloc_vector = assign_irq_vector(AUTO_ASSIGN); | ||
372 | status = msi_cache_init(); | 379 | status = msi_cache_init(); |
373 | if (status < 0) { | 380 | if (status < 0) { |
374 | pci_msi_enable = 0; | 381 | pci_msi_enable = 0; |
375 | printk(KERN_WARNING "PCI: MSI cache init failed\n"); | 382 | printk(KERN_WARNING "PCI: MSI cache init failed\n"); |
376 | return status; | 383 | return status; |
377 | } | 384 | } |
378 | last_alloc_vector = assign_irq_vector(AUTO_ASSIGN); | 385 | |
379 | if (last_alloc_vector < 0) { | 386 | if (last_alloc_vector < 0) { |
380 | pci_msi_enable = 0; | 387 | pci_msi_enable = 0; |
381 | printk(KERN_WARNING "PCI: No interrupt vectors available for MSI\n"); | 388 | printk(KERN_WARNING "PCI: No interrupt vectors available for MSI\n"); |
@@ -575,6 +582,8 @@ void pci_restore_msi_state(struct pci_dev *dev) | |||
575 | int pci_save_msix_state(struct pci_dev *dev) | 582 | int pci_save_msix_state(struct pci_dev *dev) |
576 | { | 583 | { |
577 | int pos; | 584 | int pos; |
585 | int temp; | ||
586 | int vector, head, tail = 0; | ||
578 | u16 control; | 587 | u16 control; |
579 | struct pci_cap_saved_state *save_state; | 588 | struct pci_cap_saved_state *save_state; |
580 | 589 | ||
@@ -582,6 +591,7 @@ int pci_save_msix_state(struct pci_dev *dev) | |||
582 | if (pos <= 0 || dev->no_msi) | 591 | if (pos <= 0 || dev->no_msi) |
583 | return 0; | 592 | return 0; |
584 | 593 | ||
594 | /* save the capability */ | ||
585 | pci_read_config_word(dev, msi_control_reg(pos), &control); | 595 | pci_read_config_word(dev, msi_control_reg(pos), &control); |
586 | if (!(control & PCI_MSIX_FLAGS_ENABLE)) | 596 | if (!(control & PCI_MSIX_FLAGS_ENABLE)) |
587 | return 0; | 597 | return 0; |
@@ -593,6 +603,38 @@ int pci_save_msix_state(struct pci_dev *dev) | |||
593 | } | 603 | } |
594 | *((u16 *)&save_state->data[0]) = control; | 604 | *((u16 *)&save_state->data[0]) = control; |
595 | 605 | ||
606 | /* save the table */ | ||
607 | temp = dev->irq; | ||
608 | if (msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) { | ||
609 | kfree(save_state); | ||
610 | return -EINVAL; | ||
611 | } | ||
612 | |||
613 | vector = head = dev->irq; | ||
614 | while (head != tail) { | ||
615 | int j; | ||
616 | void __iomem *base; | ||
617 | struct msi_desc *entry; | ||
618 | |||
619 | entry = msi_desc[vector]; | ||
620 | base = entry->mask_base; | ||
621 | j = entry->msi_attrib.entry_nr; | ||
622 | |||
623 | entry->address_lo_save = | ||
624 | readl(base + j * PCI_MSIX_ENTRY_SIZE + | ||
625 | PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); | ||
626 | entry->address_hi_save = | ||
627 | readl(base + j * PCI_MSIX_ENTRY_SIZE + | ||
628 | PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); | ||
629 | entry->data_save = | ||
630 | readl(base + j * PCI_MSIX_ENTRY_SIZE + | ||
631 | PCI_MSIX_ENTRY_DATA_OFFSET); | ||
632 | |||
633 | tail = msi_desc[vector]->link.tail; | ||
634 | vector = tail; | ||
635 | } | ||
636 | dev->irq = temp; | ||
637 | |||
596 | disable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); | 638 | disable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); |
597 | save_state->cap_nr = PCI_CAP_ID_MSIX; | 639 | save_state->cap_nr = PCI_CAP_ID_MSIX; |
598 | pci_add_saved_cap(dev, save_state); | 640 | pci_add_saved_cap(dev, save_state); |
@@ -606,8 +648,6 @@ void pci_restore_msix_state(struct pci_dev *dev) | |||
606 | int vector, head, tail = 0; | 648 | int vector, head, tail = 0; |
607 | void __iomem *base; | 649 | void __iomem *base; |
608 | int j; | 650 | int j; |
609 | struct msg_address address; | ||
610 | struct msg_data data; | ||
611 | struct msi_desc *entry; | 651 | struct msi_desc *entry; |
612 | int temp; | 652 | int temp; |
613 | struct pci_cap_saved_state *save_state; | 653 | struct pci_cap_saved_state *save_state; |
@@ -633,20 +673,13 @@ void pci_restore_msix_state(struct pci_dev *dev) | |||
633 | base = entry->mask_base; | 673 | base = entry->mask_base; |
634 | j = entry->msi_attrib.entry_nr; | 674 | j = entry->msi_attrib.entry_nr; |
635 | 675 | ||
636 | msi_address_init(&address); | 676 | writel(entry->address_lo_save, |
637 | msi_data_init(&data, vector); | ||
638 | |||
639 | address.lo_address.value &= MSI_ADDRESS_DEST_ID_MASK; | ||
640 | address.lo_address.value |= entry->msi_attrib.current_cpu << | ||
641 | MSI_TARGET_CPU_SHIFT; | ||
642 | |||
643 | writel(address.lo_address.value, | ||
644 | base + j * PCI_MSIX_ENTRY_SIZE + | 677 | base + j * PCI_MSIX_ENTRY_SIZE + |
645 | PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); | 678 | PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); |
646 | writel(address.hi_address, | 679 | writel(entry->address_hi_save, |
647 | base + j * PCI_MSIX_ENTRY_SIZE + | 680 | base + j * PCI_MSIX_ENTRY_SIZE + |
648 | PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); | 681 | PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); |
649 | writel(*(u32*)&data, | 682 | writel(entry->data_save, |
650 | base + j * PCI_MSIX_ENTRY_SIZE + | 683 | base + j * PCI_MSIX_ENTRY_SIZE + |
651 | PCI_MSIX_ENTRY_DATA_OFFSET); | 684 | PCI_MSIX_ENTRY_DATA_OFFSET); |
652 | 685 | ||
@@ -660,30 +693,32 @@ void pci_restore_msix_state(struct pci_dev *dev) | |||
660 | } | 693 | } |
661 | #endif | 694 | #endif |
662 | 695 | ||
663 | static void msi_register_init(struct pci_dev *dev, struct msi_desc *entry) | 696 | static int msi_register_init(struct pci_dev *dev, struct msi_desc *entry) |
664 | { | 697 | { |
665 | struct msg_address address; | 698 | int status; |
666 | struct msg_data data; | 699 | u32 address_hi; |
700 | u32 address_lo; | ||
701 | u32 data; | ||
667 | int pos, vector = dev->irq; | 702 | int pos, vector = dev->irq; |
668 | u16 control; | 703 | u16 control; |
669 | 704 | ||
670 | pos = pci_find_capability(dev, PCI_CAP_ID_MSI); | 705 | pos = pci_find_capability(dev, PCI_CAP_ID_MSI); |
671 | pci_read_config_word(dev, msi_control_reg(pos), &control); | 706 | pci_read_config_word(dev, msi_control_reg(pos), &control); |
707 | |||
672 | /* Configure MSI capability structure */ | 708 | /* Configure MSI capability structure */ |
673 | msi_address_init(&address); | 709 | status = msi_ops->setup(dev, vector, &address_hi, &address_lo, &data); |
674 | msi_data_init(&data, vector); | 710 | if (status < 0) |
675 | entry->msi_attrib.current_cpu = ((address.lo_address.u.dest_id >> | 711 | return status; |
676 | MSI_TARGET_CPU_SHIFT) & MSI_TARGET_CPU_MASK); | 712 | |
677 | pci_write_config_dword(dev, msi_lower_address_reg(pos), | 713 | pci_write_config_dword(dev, msi_lower_address_reg(pos), address_lo); |
678 | address.lo_address.value); | ||
679 | if (is_64bit_address(control)) { | 714 | if (is_64bit_address(control)) { |
680 | pci_write_config_dword(dev, | 715 | pci_write_config_dword(dev, |
681 | msi_upper_address_reg(pos), address.hi_address); | 716 | msi_upper_address_reg(pos), address_hi); |
682 | pci_write_config_word(dev, | 717 | pci_write_config_word(dev, |
683 | msi_data_reg(pos, 1), *((u32*)&data)); | 718 | msi_data_reg(pos, 1), data); |
684 | } else | 719 | } else |
685 | pci_write_config_word(dev, | 720 | pci_write_config_word(dev, |
686 | msi_data_reg(pos, 0), *((u32*)&data)); | 721 | msi_data_reg(pos, 0), data); |
687 | if (entry->msi_attrib.maskbit) { | 722 | if (entry->msi_attrib.maskbit) { |
688 | unsigned int maskbits, temp; | 723 | unsigned int maskbits, temp; |
689 | /* All MSIs are unmasked by default, Mask them all */ | 724 | /* All MSIs are unmasked by default, Mask them all */ |
@@ -697,6 +732,8 @@ static void msi_register_init(struct pci_dev *dev, struct msi_desc *entry) | |||
697 | msi_mask_bits_reg(pos, is_64bit_address(control)), | 732 | msi_mask_bits_reg(pos, is_64bit_address(control)), |
698 | maskbits); | 733 | maskbits); |
699 | } | 734 | } |
735 | |||
736 | return 0; | ||
700 | } | 737 | } |
701 | 738 | ||
702 | /** | 739 | /** |
@@ -710,6 +747,7 @@ static void msi_register_init(struct pci_dev *dev, struct msi_desc *entry) | |||
710 | **/ | 747 | **/ |
711 | static int msi_capability_init(struct pci_dev *dev) | 748 | static int msi_capability_init(struct pci_dev *dev) |
712 | { | 749 | { |
750 | int status; | ||
713 | struct msi_desc *entry; | 751 | struct msi_desc *entry; |
714 | int pos, vector; | 752 | int pos, vector; |
715 | u16 control; | 753 | u16 control; |
@@ -742,7 +780,12 @@ static int msi_capability_init(struct pci_dev *dev) | |||
742 | /* Replace with MSI handler */ | 780 | /* Replace with MSI handler */ |
743 | irq_handler_init(PCI_CAP_ID_MSI, vector, entry->msi_attrib.maskbit); | 781 | irq_handler_init(PCI_CAP_ID_MSI, vector, entry->msi_attrib.maskbit); |
744 | /* Configure MSI capability structure */ | 782 | /* Configure MSI capability structure */ |
745 | msi_register_init(dev, entry); | 783 | status = msi_register_init(dev, entry); |
784 | if (status != 0) { | ||
785 | dev->irq = entry->msi_attrib.default_vector; | ||
786 | kmem_cache_free(msi_cachep, entry); | ||
787 | return status; | ||
788 | } | ||
746 | 789 | ||
747 | attach_msi_entry(entry, vector); | 790 | attach_msi_entry(entry, vector); |
748 | /* Set MSI enabled bits */ | 791 | /* Set MSI enabled bits */ |
@@ -765,8 +808,10 @@ static int msix_capability_init(struct pci_dev *dev, | |||
765 | struct msix_entry *entries, int nvec) | 808 | struct msix_entry *entries, int nvec) |
766 | { | 809 | { |
767 | struct msi_desc *head = NULL, *tail = NULL, *entry = NULL; | 810 | struct msi_desc *head = NULL, *tail = NULL, *entry = NULL; |
768 | struct msg_address address; | 811 | u32 address_hi; |
769 | struct msg_data data; | 812 | u32 address_lo; |
813 | u32 data; | ||
814 | int status; | ||
770 | int vector, pos, i, j, nr_entries, temp = 0; | 815 | int vector, pos, i, j, nr_entries, temp = 0; |
771 | unsigned long phys_addr; | 816 | unsigned long phys_addr; |
772 | u32 table_offset; | 817 | u32 table_offset; |
@@ -822,18 +867,20 @@ static int msix_capability_init(struct pci_dev *dev, | |||
822 | /* Replace with MSI-X handler */ | 867 | /* Replace with MSI-X handler */ |
823 | irq_handler_init(PCI_CAP_ID_MSIX, vector, 1); | 868 | irq_handler_init(PCI_CAP_ID_MSIX, vector, 1); |
824 | /* Configure MSI-X capability structure */ | 869 | /* Configure MSI-X capability structure */ |
825 | msi_address_init(&address); | 870 | status = msi_ops->setup(dev, vector, |
826 | msi_data_init(&data, vector); | 871 | &address_hi, |
827 | entry->msi_attrib.current_cpu = | 872 | &address_lo, |
828 | ((address.lo_address.u.dest_id >> | 873 | &data); |
829 | MSI_TARGET_CPU_SHIFT) & MSI_TARGET_CPU_MASK); | 874 | if (status < 0) |
830 | writel(address.lo_address.value, | 875 | break; |
876 | |||
877 | writel(address_lo, | ||
831 | base + j * PCI_MSIX_ENTRY_SIZE + | 878 | base + j * PCI_MSIX_ENTRY_SIZE + |
832 | PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); | 879 | PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); |
833 | writel(address.hi_address, | 880 | writel(address_hi, |
834 | base + j * PCI_MSIX_ENTRY_SIZE + | 881 | base + j * PCI_MSIX_ENTRY_SIZE + |
835 | PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); | 882 | PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); |
836 | writel(*(u32*)&data, | 883 | writel(data, |
837 | base + j * PCI_MSIX_ENTRY_SIZE + | 884 | base + j * PCI_MSIX_ENTRY_SIZE + |
838 | PCI_MSIX_ENTRY_DATA_OFFSET); | 885 | PCI_MSIX_ENTRY_DATA_OFFSET); |
839 | attach_msi_entry(entry, vector); | 886 | attach_msi_entry(entry, vector); |
@@ -901,9 +948,10 @@ int pci_enable_msi(struct pci_dev* dev) | |||
901 | vector_irq[dev->irq] = -1; | 948 | vector_irq[dev->irq] = -1; |
902 | nr_released_vectors--; | 949 | nr_released_vectors--; |
903 | spin_unlock_irqrestore(&msi_lock, flags); | 950 | spin_unlock_irqrestore(&msi_lock, flags); |
904 | msi_register_init(dev, msi_desc[dev->irq]); | 951 | status = msi_register_init(dev, msi_desc[dev->irq]); |
905 | enable_msi_mode(dev, pos, PCI_CAP_ID_MSI); | 952 | if (status == 0) |
906 | return 0; | 953 | enable_msi_mode(dev, pos, PCI_CAP_ID_MSI); |
954 | return status; | ||
907 | } | 955 | } |
908 | spin_unlock_irqrestore(&msi_lock, flags); | 956 | spin_unlock_irqrestore(&msi_lock, flags); |
909 | dev->irq = temp; | 957 | dev->irq = temp; |
@@ -980,6 +1028,8 @@ static int msi_free_vector(struct pci_dev* dev, int vector, int reassign) | |||
980 | void __iomem *base; | 1028 | void __iomem *base; |
981 | unsigned long flags; | 1029 | unsigned long flags; |
982 | 1030 | ||
1031 | msi_ops->teardown(vector); | ||
1032 | |||
983 | spin_lock_irqsave(&msi_lock, flags); | 1033 | spin_lock_irqsave(&msi_lock, flags); |
984 | entry = msi_desc[vector]; | 1034 | entry = msi_desc[vector]; |
985 | if (!entry || entry->dev != dev) { | 1035 | if (!entry || entry->dev != dev) { |