diff options
Diffstat (limited to 'arch/x86/pci/irq.c')
| -rw-r--r-- | arch/x86/pci/irq.c | 120 |
1 files changed, 82 insertions, 38 deletions
diff --git a/arch/x86/pci/irq.c b/arch/x86/pci/irq.c index dc568c6b83f8..6a06a2eb0597 100644 --- a/arch/x86/pci/irq.c +++ b/arch/x86/pci/irq.c | |||
| @@ -45,7 +45,8 @@ struct irq_router { | |||
| 45 | char *name; | 45 | char *name; |
| 46 | u16 vendor, device; | 46 | u16 vendor, device; |
| 47 | int (*get)(struct pci_dev *router, struct pci_dev *dev, int pirq); | 47 | int (*get)(struct pci_dev *router, struct pci_dev *dev, int pirq); |
| 48 | int (*set)(struct pci_dev *router, struct pci_dev *dev, int pirq, int new); | 48 | int (*set)(struct pci_dev *router, struct pci_dev *dev, int pirq, |
| 49 | int new); | ||
| 49 | }; | 50 | }; |
| 50 | 51 | ||
| 51 | struct irq_router_handler { | 52 | struct irq_router_handler { |
| @@ -77,7 +78,8 @@ static inline struct irq_routing_table *pirq_check_routing_table(u8 *addr) | |||
| 77 | for (i = 0; i < rt->size; i++) | 78 | for (i = 0; i < rt->size; i++) |
| 78 | sum += addr[i]; | 79 | sum += addr[i]; |
| 79 | if (!sum) { | 80 | if (!sum) { |
| 80 | DBG(KERN_DEBUG "PCI: Interrupt Routing Table found at 0x%p\n", rt); | 81 | DBG(KERN_DEBUG "PCI: Interrupt Routing Table found at 0x%p\n", |
| 82 | rt); | ||
| 81 | return rt; | 83 | return rt; |
| 82 | } | 84 | } |
| 83 | return NULL; | 85 | return NULL; |
| @@ -183,7 +185,8 @@ static unsigned int read_config_nybble(struct pci_dev *router, unsigned offset, | |||
| 183 | return (nr & 1) ? (x >> 4) : (x & 0xf); | 185 | return (nr & 1) ? (x >> 4) : (x & 0xf); |
| 184 | } | 186 | } |
| 185 | 187 | ||
| 186 | static void write_config_nybble(struct pci_dev *router, unsigned offset, unsigned nr, unsigned int val) | 188 | static void write_config_nybble(struct pci_dev *router, unsigned offset, |
| 189 | unsigned nr, unsigned int val) | ||
| 187 | { | 190 | { |
| 188 | u8 x; | 191 | u8 x; |
| 189 | unsigned reg = offset + (nr >> 1); | 192 | unsigned reg = offset + (nr >> 1); |
| @@ -467,7 +470,8 @@ static int pirq_serverworks_get(struct pci_dev *router, struct pci_dev *dev, int | |||
| 467 | return inb(0xc01) & 0xf; | 470 | return inb(0xc01) & 0xf; |
| 468 | } | 471 | } |
| 469 | 472 | ||
| 470 | static int pirq_serverworks_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq) | 473 | static int pirq_serverworks_set(struct pci_dev *router, struct pci_dev *dev, |
| 474 | int pirq, int irq) | ||
| 471 | { | 475 | { |
| 472 | outb(pirq, 0xc00); | 476 | outb(pirq, 0xc00); |
| 473 | outb(irq, 0xc01); | 477 | outb(irq, 0xc01); |
| @@ -660,7 +664,8 @@ static __init int vlsi_router_probe(struct irq_router *r, struct pci_dev *router | |||
| 660 | } | 664 | } |
| 661 | 665 | ||
| 662 | 666 | ||
| 663 | static __init int serverworks_router_probe(struct irq_router *r, struct pci_dev *router, u16 device) | 667 | static __init int serverworks_router_probe(struct irq_router *r, |
| 668 | struct pci_dev *router, u16 device) | ||
| 664 | { | 669 | { |
| 665 | switch (device) { | 670 | switch (device) { |
| 666 | case PCI_DEVICE_ID_SERVERWORKS_OSB4: | 671 | case PCI_DEVICE_ID_SERVERWORKS_OSB4: |
| @@ -827,10 +832,12 @@ static void __init pirq_find_router(struct irq_router *r) | |||
| 827 | 832 | ||
| 828 | for (h = pirq_routers; h->vendor; h++) { | 833 | for (h = pirq_routers; h->vendor; h++) { |
| 829 | /* First look for a router match */ | 834 | /* First look for a router match */ |
| 830 | if (rt->rtr_vendor == h->vendor && h->probe(r, pirq_router_dev, rt->rtr_device)) | 835 | if (rt->rtr_vendor == h->vendor && |
| 836 | h->probe(r, pirq_router_dev, rt->rtr_device)) | ||
| 831 | break; | 837 | break; |
| 832 | /* Fall back to a device match */ | 838 | /* Fall back to a device match */ |
| 833 | if (pirq_router_dev->vendor == h->vendor && h->probe(r, pirq_router_dev, pirq_router_dev->device)) | 839 | if (pirq_router_dev->vendor == h->vendor && |
| 840 | h->probe(r, pirq_router_dev, pirq_router_dev->device)) | ||
| 834 | break; | 841 | break; |
| 835 | } | 842 | } |
| 836 | printk(KERN_INFO "PCI: Using IRQ router %s [%04x/%04x] at %s\n", | 843 | printk(KERN_INFO "PCI: Using IRQ router %s [%04x/%04x] at %s\n", |
| @@ -845,11 +852,13 @@ static void __init pirq_find_router(struct irq_router *r) | |||
| 845 | static struct irq_info *pirq_get_info(struct pci_dev *dev) | 852 | static struct irq_info *pirq_get_info(struct pci_dev *dev) |
| 846 | { | 853 | { |
| 847 | struct irq_routing_table *rt = pirq_table; | 854 | struct irq_routing_table *rt = pirq_table; |
| 848 | int entries = (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); | 855 | int entries = (rt->size - sizeof(struct irq_routing_table)) / |
| 856 | sizeof(struct irq_info); | ||
| 849 | struct irq_info *info; | 857 | struct irq_info *info; |
| 850 | 858 | ||
| 851 | for (info = rt->slots; entries--; info++) | 859 | for (info = rt->slots; entries--; info++) |
| 852 | if (info->bus == dev->bus->number && PCI_SLOT(info->devfn) == PCI_SLOT(dev->devfn)) | 860 | if (info->bus == dev->bus->number && |
| 861 | PCI_SLOT(info->devfn) == PCI_SLOT(dev->devfn)) | ||
| 853 | return info; | 862 | return info; |
| 854 | return NULL; | 863 | return NULL; |
| 855 | } | 864 | } |
| @@ -890,7 +899,8 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign) | |||
| 890 | DBG(" -> not routed\n" KERN_DEBUG); | 899 | DBG(" -> not routed\n" KERN_DEBUG); |
| 891 | return 0; | 900 | return 0; |
| 892 | } | 901 | } |
| 893 | DBG(" -> PIRQ %02x, mask %04x, excl %04x", pirq, mask, pirq_table->exclusive_irqs); | 902 | DBG(" -> PIRQ %02x, mask %04x, excl %04x", pirq, mask, |
| 903 | pirq_table->exclusive_irqs); | ||
| 894 | mask &= pcibios_irq_mask; | 904 | mask &= pcibios_irq_mask; |
| 895 | 905 | ||
| 896 | /* Work around broken HP Pavilion Notebooks which assign USB to | 906 | /* Work around broken HP Pavilion Notebooks which assign USB to |
| @@ -903,7 +913,8 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign) | |||
| 903 | } | 913 | } |
| 904 | 914 | ||
| 905 | /* same for Acer Travelmate 360, but with CB and irq 11 -> 10 */ | 915 | /* same for Acer Travelmate 360, but with CB and irq 11 -> 10 */ |
| 906 | if (acer_tm360_irqrouting && dev->irq == 11 && dev->vendor == PCI_VENDOR_ID_O2) { | 916 | if (acer_tm360_irqrouting && dev->irq == 11 && |
| 917 | dev->vendor == PCI_VENDOR_ID_O2) { | ||
| 907 | pirq = 0x68; | 918 | pirq = 0x68; |
| 908 | mask = 0x400; | 919 | mask = 0x400; |
| 909 | dev->irq = r->get(pirq_router_dev, dev, pirq); | 920 | dev->irq = r->get(pirq_router_dev, dev, pirq); |
| @@ -920,15 +931,16 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign) | |||
| 920 | newirq = 0; | 931 | newirq = 0; |
| 921 | else | 932 | else |
| 922 | printk("\n" KERN_WARNING | 933 | printk("\n" KERN_WARNING |
| 923 | "PCI: IRQ %i for device %s doesn't match PIRQ mask " | 934 | "PCI: IRQ %i for device %s doesn't match PIRQ mask - try pci=usepirqmask\n" |
| 924 | "- try pci=usepirqmask\n" KERN_DEBUG, newirq, | 935 | KERN_DEBUG, newirq, |
| 925 | pci_name(dev)); | 936 | pci_name(dev)); |
| 926 | } | 937 | } |
| 927 | if (!newirq && assign) { | 938 | if (!newirq && assign) { |
| 928 | for (i = 0; i < 16; i++) { | 939 | for (i = 0; i < 16; i++) { |
| 929 | if (!(mask & (1 << i))) | 940 | if (!(mask & (1 << i))) |
| 930 | continue; | 941 | continue; |
| 931 | if (pirq_penalty[i] < pirq_penalty[newirq] && can_request_irq(i, IRQF_SHARED)) | 942 | if (pirq_penalty[i] < pirq_penalty[newirq] && |
| 943 | can_request_irq(i, IRQF_SHARED)) | ||
| 932 | newirq = i; | 944 | newirq = i; |
| 933 | } | 945 | } |
| 934 | } | 946 | } |
| @@ -944,7 +956,8 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign) | |||
| 944 | DBG(" -> got IRQ %d\n", irq); | 956 | DBG(" -> got IRQ %d\n", irq); |
| 945 | msg = "Found"; | 957 | msg = "Found"; |
| 946 | eisa_set_level_irq(irq); | 958 | eisa_set_level_irq(irq); |
| 947 | } else if (newirq && r->set && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) { | 959 | } else if (newirq && r->set && |
| 960 | (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) { | ||
| 948 | DBG(" -> assigning IRQ %d", newirq); | 961 | DBG(" -> assigning IRQ %d", newirq); |
| 949 | if (r->set(pirq_router_dev, dev, pirq, newirq)) { | 962 | if (r->set(pirq_router_dev, dev, pirq, newirq)) { |
| 950 | eisa_set_level_irq(newirq); | 963 | eisa_set_level_irq(newirq); |
| @@ -962,7 +975,8 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign) | |||
| 962 | } else | 975 | } else |
| 963 | return 0; | 976 | return 0; |
| 964 | } | 977 | } |
| 965 | printk(KERN_INFO "PCI: %s IRQ %d for device %s\n", msg, irq, pci_name(dev)); | 978 | printk(KERN_INFO "PCI: %s IRQ %d for device %s\n", msg, irq, |
| 979 | pci_name(dev)); | ||
| 966 | 980 | ||
| 967 | /* Update IRQ for all devices with the same pirq value */ | 981 | /* Update IRQ for all devices with the same pirq value */ |
| 968 | while ((dev2 = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev2)) != NULL) { | 982 | while ((dev2 = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev2)) != NULL) { |
| @@ -974,7 +988,10 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign) | |||
| 974 | if (!info) | 988 | if (!info) |
| 975 | continue; | 989 | continue; |
| 976 | if (info->irq[pin].link == pirq) { | 990 | if (info->irq[pin].link == pirq) { |
| 977 | /* We refuse to override the dev->irq information. Give a warning! */ | 991 | /* |
| 992 | * We refuse to override the dev->irq | ||
| 993 | * information. Give a warning! | ||
| 994 | */ | ||
| 978 | if (dev2->irq && dev2->irq != irq && \ | 995 | if (dev2->irq && dev2->irq != irq && \ |
| 979 | (!(pci_probe & PCI_USE_PIRQ_MASK) || \ | 996 | (!(pci_probe & PCI_USE_PIRQ_MASK) || \ |
| 980 | ((1 << dev2->irq) & mask))) { | 997 | ((1 << dev2->irq) & mask))) { |
| @@ -987,7 +1004,9 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign) | |||
| 987 | dev2->irq = irq; | 1004 | dev2->irq = irq; |
| 988 | pirq_penalty[irq]++; | 1005 | pirq_penalty[irq]++; |
| 989 | if (dev != dev2) | 1006 | if (dev != dev2) |
| 990 | printk(KERN_INFO "PCI: Sharing IRQ %d with %s\n", irq, pci_name(dev2)); | 1007 | printk(KERN_INFO |
| 1008 | "PCI: Sharing IRQ %d with %s\n", | ||
| 1009 | irq, pci_name(dev2)); | ||
| 991 | } | 1010 | } |
| 992 | } | 1011 | } |
| 993 | return 1; | 1012 | return 1; |
| @@ -1001,15 +1020,21 @@ static void __init pcibios_fixup_irqs(void) | |||
| 1001 | DBG(KERN_DEBUG "PCI: IRQ fixup\n"); | 1020 | DBG(KERN_DEBUG "PCI: IRQ fixup\n"); |
| 1002 | while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { | 1021 | while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { |
| 1003 | /* | 1022 | /* |
| 1004 | * If the BIOS has set an out of range IRQ number, just ignore it. | 1023 | * If the BIOS has set an out of range IRQ number, just |
| 1005 | * Also keep track of which IRQ's are already in use. | 1024 | * ignore it. Also keep track of which IRQ's are |
| 1025 | * already in use. | ||
| 1006 | */ | 1026 | */ |
| 1007 | if (dev->irq >= 16) { | 1027 | if (dev->irq >= 16) { |
| 1008 | DBG(KERN_DEBUG "%s: ignoring bogus IRQ %d\n", pci_name(dev), dev->irq); | 1028 | DBG(KERN_DEBUG "%s: ignoring bogus IRQ %d\n", |
| 1029 | pci_name(dev), dev->irq); | ||
| 1009 | dev->irq = 0; | 1030 | dev->irq = 0; |
| 1010 | } | 1031 | } |
| 1011 | /* If the IRQ is already assigned to a PCI device, ignore its ISA use penalty */ | 1032 | /* |
| 1012 | if (pirq_penalty[dev->irq] >= 100 && pirq_penalty[dev->irq] < 100000) | 1033 | * If the IRQ is already assigned to a PCI device, |
| 1034 | * ignore its ISA use penalty | ||
| 1035 | */ | ||
| 1036 | if (pirq_penalty[dev->irq] >= 100 && | ||
| 1037 | pirq_penalty[dev->irq] < 100000) | ||
| 1013 | pirq_penalty[dev->irq] = 0; | 1038 | pirq_penalty[dev->irq] = 0; |
| 1014 | pirq_penalty[dev->irq]++; | 1039 | pirq_penalty[dev->irq]++; |
| 1015 | } | 1040 | } |
| @@ -1025,8 +1050,13 @@ static void __init pcibios_fixup_irqs(void) | |||
| 1025 | int irq; | 1050 | int irq; |
| 1026 | 1051 | ||
| 1027 | if (pin) { | 1052 | if (pin) { |
| 1028 | pin--; /* interrupt pins are numbered starting from 1 */ | 1053 | /* |
| 1029 | irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin); | 1054 | * interrupt pins are numbered starting |
| 1055 | * from 1 | ||
| 1056 | */ | ||
| 1057 | pin--; | ||
| 1058 | irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, | ||
| 1059 | PCI_SLOT(dev->devfn), pin); | ||
| 1030 | /* | 1060 | /* |
| 1031 | * Busses behind bridges are typically not listed in the MP-table. | 1061 | * Busses behind bridges are typically not listed in the MP-table. |
| 1032 | * In this case we have to look up the IRQ based on the parent bus, | 1062 | * In this case we have to look up the IRQ based on the parent bus, |
| @@ -1067,7 +1097,8 @@ static int __init fix_broken_hp_bios_irq9(const struct dmi_system_id *d) | |||
| 1067 | { | 1097 | { |
| 1068 | if (!broken_hp_bios_irq9) { | 1098 | if (!broken_hp_bios_irq9) { |
| 1069 | broken_hp_bios_irq9 = 1; | 1099 | broken_hp_bios_irq9 = 1; |
| 1070 | printk(KERN_INFO "%s detected - fixing broken IRQ routing\n", d->ident); | 1100 | printk(KERN_INFO "%s detected - fixing broken IRQ routing\n", |
| 1101 | d->ident); | ||
| 1071 | } | 1102 | } |
| 1072 | return 0; | 1103 | return 0; |
| 1073 | } | 1104 | } |
| @@ -1080,7 +1111,8 @@ static int __init fix_acer_tm360_irqrouting(const struct dmi_system_id *d) | |||
| 1080 | { | 1111 | { |
| 1081 | if (!acer_tm360_irqrouting) { | 1112 | if (!acer_tm360_irqrouting) { |
| 1082 | acer_tm360_irqrouting = 1; | 1113 | acer_tm360_irqrouting = 1; |
| 1083 | printk(KERN_INFO "%s detected - fixing broken IRQ routing\n", d->ident); | 1114 | printk(KERN_INFO "%s detected - fixing broken IRQ routing\n", |
| 1115 | d->ident); | ||
| 1084 | } | 1116 | } |
| 1085 | return 0; | 1117 | return 0; |
| 1086 | } | 1118 | } |
| @@ -1092,7 +1124,8 @@ static struct dmi_system_id __initdata pciirq_dmi_table[] = { | |||
| 1092 | .matches = { | 1124 | .matches = { |
| 1093 | DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), | 1125 | DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), |
| 1094 | DMI_MATCH(DMI_BIOS_VERSION, "GE.M1.03"), | 1126 | DMI_MATCH(DMI_BIOS_VERSION, "GE.M1.03"), |
| 1095 | DMI_MATCH(DMI_PRODUCT_VERSION, "HP Pavilion Notebook Model GE"), | 1127 | DMI_MATCH(DMI_PRODUCT_VERSION, |
| 1128 | "HP Pavilion Notebook Model GE"), | ||
| 1096 | DMI_MATCH(DMI_BOARD_VERSION, "OmniBook N32N-736"), | 1129 | DMI_MATCH(DMI_BOARD_VERSION, "OmniBook N32N-736"), |
| 1097 | }, | 1130 | }, |
| 1098 | }, | 1131 | }, |
| @@ -1131,7 +1164,10 @@ int __init pcibios_irq_init(void) | |||
| 1131 | if (!(pirq_table->exclusive_irqs & (1 << i))) | 1164 | if (!(pirq_table->exclusive_irqs & (1 << i))) |
| 1132 | pirq_penalty[i] += 100; | 1165 | pirq_penalty[i] += 100; |
| 1133 | } | 1166 | } |
| 1134 | /* If we're using the I/O APIC, avoid using the PCI IRQ routing table */ | 1167 | /* |
| 1168 | * If we're using the I/O APIC, avoid using the PCI IRQ | ||
| 1169 | * routing table | ||
| 1170 | */ | ||
| 1135 | if (io_apic_assign_pci_irqs) | 1171 | if (io_apic_assign_pci_irqs) |
| 1136 | pirq_table = NULL; | 1172 | pirq_table = NULL; |
| 1137 | } | 1173 | } |
| @@ -1175,7 +1211,7 @@ static int pirq_enable_irq(struct pci_dev *dev) | |||
| 1175 | if (pin && !pcibios_lookup_irq(dev, 1) && !dev->irq) { | 1211 | if (pin && !pcibios_lookup_irq(dev, 1) && !dev->irq) { |
| 1176 | char *msg = ""; | 1212 | char *msg = ""; |
| 1177 | 1213 | ||
| 1178 | pin--; /* interrupt pins are numbered starting from 1 */ | 1214 | pin--; /* interrupt pins are numbered starting from 1 */ |
| 1179 | 1215 | ||
| 1180 | if (io_apic_assign_pci_irqs) { | 1216 | if (io_apic_assign_pci_irqs) { |
| 1181 | int irq; | 1217 | int irq; |
| @@ -1195,13 +1231,16 @@ static int pirq_enable_irq(struct pci_dev *dev) | |||
| 1195 | irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number, | 1231 | irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number, |
| 1196 | PCI_SLOT(bridge->devfn), pin); | 1232 | PCI_SLOT(bridge->devfn), pin); |
| 1197 | if (irq >= 0) | 1233 | if (irq >= 0) |
| 1198 | printk(KERN_WARNING "PCI: using PPB %s[%c] to get irq %d\n", | 1234 | printk(KERN_WARNING |
| 1199 | pci_name(bridge), 'A' + pin, irq); | 1235 | "PCI: using PPB %s[%c] to get irq %d\n", |
| 1236 | pci_name(bridge), | ||
| 1237 | 'A' + pin, irq); | ||
| 1200 | dev = bridge; | 1238 | dev = bridge; |
| 1201 | } | 1239 | } |
| 1202 | dev = temp_dev; | 1240 | dev = temp_dev; |
| 1203 | if (irq >= 0) { | 1241 | if (irq >= 0) { |
| 1204 | printk(KERN_INFO "PCI->APIC IRQ transform: %s[%c] -> IRQ %d\n", | 1242 | printk(KERN_INFO |
| 1243 | "PCI->APIC IRQ transform: %s[%c] -> IRQ %d\n", | ||
| 1205 | pci_name(dev), 'A' + pin, irq); | 1244 | pci_name(dev), 'A' + pin, irq); |
| 1206 | dev->irq = irq; | 1245 | dev->irq = irq; |
| 1207 | return 0; | 1246 | return 0; |
| @@ -1212,12 +1251,17 @@ static int pirq_enable_irq(struct pci_dev *dev) | |||
| 1212 | else | 1251 | else |
| 1213 | msg = " Please try using pci=biosirq."; | 1252 | msg = " Please try using pci=biosirq."; |
| 1214 | 1253 | ||
| 1215 | /* With IDE legacy devices the IRQ lookup failure is not a problem.. */ | 1254 | /* |
| 1216 | if (dev->class >> 8 == PCI_CLASS_STORAGE_IDE && !(dev->class & 0x5)) | 1255 | * With IDE legacy devices the IRQ lookup failure is not |
| 1256 | * a problem.. | ||
| 1257 | */ | ||
| 1258 | if (dev->class >> 8 == PCI_CLASS_STORAGE_IDE && | ||
| 1259 | !(dev->class & 0x5)) | ||
| 1217 | return 0; | 1260 | return 0; |
| 1218 | 1261 | ||
| 1219 | printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s.%s\n", | 1262 | printk(KERN_WARNING |
| 1220 | 'A' + pin, pci_name(dev), msg); | 1263 | "PCI: No IRQ known for interrupt pin %c of device %s.%s\n", |
| 1264 | 'A' + pin, pci_name(dev), msg); | ||
| 1221 | } | 1265 | } |
| 1222 | return 0; | 1266 | return 0; |
| 1223 | } | 1267 | } |
