diff options
Diffstat (limited to 'drivers/pci')
36 files changed, 2480 insertions, 206 deletions
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 4d762fc4878c..c27e782e6df9 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig | |||
@@ -17,6 +17,31 @@ config PCI_MSI | |||
17 | 17 | ||
18 | If you don't know what to do here, say N. | 18 | If you don't know what to do here, say N. |
19 | 19 | ||
20 | config PCI_MULTITHREAD_PROBE | ||
21 | bool "PCI Multi-threaded probe (EXPERIMENTAL)" | ||
22 | depends on PCI && EXPERIMENTAL | ||
23 | help | ||
24 | Say Y here if you want the PCI core to spawn a new thread for | ||
25 | every PCI device that is probed. This can cause a huge | ||
26 | speedup in boot times on multiprocessor machines, and even a | ||
27 | smaller speedup on single processor machines. | ||
28 | |||
29 | But it can also cause lots of bad things to happen. A number | ||
30 | of PCI drivers can not properly handle running in this way, | ||
31 | some will just not work properly at all, while others might | ||
32 | decide to blow up power supplies with a huge load all at once, | ||
33 | so use this option at your own risk. | ||
34 | |||
35 | It is very unwise to use this option if you are not using a | ||
36 | boot process that can handle devices being created in any | ||
37 | order. A program that can create persistant block and network | ||
38 | device names (like udev) is a good idea if you wish to use | ||
39 | this option. | ||
40 | |||
41 | Again, use this option at your own risk, you have been warned! | ||
42 | |||
43 | When in doubt, say N. | ||
44 | |||
20 | config PCI_DEBUG | 45 | config PCI_DEBUG |
21 | bool "PCI Debugging" | 46 | bool "PCI Debugging" |
22 | depends on PCI && DEBUG_KERNEL | 47 | depends on PCI && DEBUG_KERNEL |
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 5f7db9d2436e..aadaa3c8096b 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c | |||
@@ -77,9 +77,12 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, | |||
77 | * This adds a single pci device to the global | 77 | * This adds a single pci device to the global |
78 | * device list and adds sysfs and procfs entries | 78 | * device list and adds sysfs and procfs entries |
79 | */ | 79 | */ |
80 | void __devinit pci_bus_add_device(struct pci_dev *dev) | 80 | int __devinit pci_bus_add_device(struct pci_dev *dev) |
81 | { | 81 | { |
82 | device_add(&dev->dev); | 82 | int retval; |
83 | retval = device_add(&dev->dev); | ||
84 | if (retval) | ||
85 | return retval; | ||
83 | 86 | ||
84 | down_write(&pci_bus_sem); | 87 | down_write(&pci_bus_sem); |
85 | list_add_tail(&dev->global_list, &pci_devices); | 88 | list_add_tail(&dev->global_list, &pci_devices); |
@@ -87,6 +90,7 @@ void __devinit pci_bus_add_device(struct pci_dev *dev) | |||
87 | 90 | ||
88 | pci_proc_attach_device(dev); | 91 | pci_proc_attach_device(dev); |
89 | pci_create_sysfs_dev_files(dev); | 92 | pci_create_sysfs_dev_files(dev); |
93 | return 0; | ||
90 | } | 94 | } |
91 | 95 | ||
92 | /** | 96 | /** |
@@ -104,6 +108,7 @@ void __devinit pci_bus_add_device(struct pci_dev *dev) | |||
104 | void __devinit pci_bus_add_devices(struct pci_bus *bus) | 108 | void __devinit pci_bus_add_devices(struct pci_bus *bus) |
105 | { | 109 | { |
106 | struct pci_dev *dev; | 110 | struct pci_dev *dev; |
111 | int retval; | ||
107 | 112 | ||
108 | list_for_each_entry(dev, &bus->devices, bus_list) { | 113 | list_for_each_entry(dev, &bus->devices, bus_list) { |
109 | /* | 114 | /* |
@@ -112,7 +117,9 @@ void __devinit pci_bus_add_devices(struct pci_bus *bus) | |||
112 | */ | 117 | */ |
113 | if (!list_empty(&dev->global_list)) | 118 | if (!list_empty(&dev->global_list)) |
114 | continue; | 119 | continue; |
115 | pci_bus_add_device(dev); | 120 | retval = pci_bus_add_device(dev); |
121 | if (retval) | ||
122 | dev_err(&dev->dev, "Error adding device, continuing\n"); | ||
116 | } | 123 | } |
117 | 124 | ||
118 | list_for_each_entry(dev, &bus->devices, bus_list) { | 125 | list_for_each_entry(dev, &bus->devices, bus_list) { |
@@ -129,10 +136,13 @@ void __devinit pci_bus_add_devices(struct pci_bus *bus) | |||
129 | list_add_tail(&dev->subordinate->node, | 136 | list_add_tail(&dev->subordinate->node, |
130 | &dev->bus->children); | 137 | &dev->bus->children); |
131 | up_write(&pci_bus_sem); | 138 | up_write(&pci_bus_sem); |
132 | } | 139 | } |
133 | pci_bus_add_devices(dev->subordinate); | 140 | pci_bus_add_devices(dev->subordinate); |
134 | 141 | retval = sysfs_create_link(&dev->subordinate->class_dev.kobj, | |
135 | sysfs_create_link(&dev->subordinate->class_dev.kobj, &dev->dev.kobj, "bridge"); | 142 | &dev->dev.kobj, "bridge"); |
143 | if (retval) | ||
144 | dev_err(&dev->dev, "Error creating sysfs " | ||
145 | "bridge symlink, continuing...\n"); | ||
136 | } | 146 | } |
137 | } | 147 | } |
138 | } | 148 | } |
diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index be104eced34c..7fff07e877c7 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h | |||
@@ -150,6 +150,11 @@ struct acpiphp_attention_info | |||
150 | struct module *owner; | 150 | struct module *owner; |
151 | }; | 151 | }; |
152 | 152 | ||
153 | struct acpiphp_ioapic { | ||
154 | struct pci_dev *dev; | ||
155 | u32 gsi_base; | ||
156 | struct list_head list; | ||
157 | }; | ||
153 | 158 | ||
154 | /* PCI bus bridge HID */ | 159 | /* PCI bus bridge HID */ |
155 | #define ACPI_PCI_HOST_HID "PNP0A03" | 160 | #define ACPI_PCI_HOST_HID "PNP0A03" |
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index ae67a8f55ba1..83e8e4412de5 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c | |||
@@ -53,6 +53,8 @@ | |||
53 | #include "acpiphp.h" | 53 | #include "acpiphp.h" |
54 | 54 | ||
55 | static LIST_HEAD(bridge_list); | 55 | static LIST_HEAD(bridge_list); |
56 | static LIST_HEAD(ioapic_list); | ||
57 | static DEFINE_SPINLOCK(ioapic_list_lock); | ||
56 | 58 | ||
57 | #define MY_NAME "acpiphp_glue" | 59 | #define MY_NAME "acpiphp_glue" |
58 | 60 | ||
@@ -797,6 +799,7 @@ ioapic_add(acpi_handle handle, u32 lvl, void *context, void **rv) | |||
797 | struct pci_dev *pdev; | 799 | struct pci_dev *pdev; |
798 | u32 gsi_base; | 800 | u32 gsi_base; |
799 | u64 phys_addr; | 801 | u64 phys_addr; |
802 | struct acpiphp_ioapic *ioapic; | ||
800 | 803 | ||
801 | /* Evaluate _STA if present */ | 804 | /* Evaluate _STA if present */ |
802 | status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); | 805 | status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); |
@@ -811,41 +814,107 @@ ioapic_add(acpi_handle handle, u32 lvl, void *context, void **rv) | |||
811 | if (get_gsi_base(handle, &gsi_base)) | 814 | if (get_gsi_base(handle, &gsi_base)) |
812 | return AE_OK; | 815 | return AE_OK; |
813 | 816 | ||
817 | ioapic = kmalloc(sizeof(*ioapic), GFP_KERNEL); | ||
818 | if (!ioapic) | ||
819 | return AE_NO_MEMORY; | ||
820 | |||
814 | pdev = get_apic_pci_info(handle); | 821 | pdev = get_apic_pci_info(handle); |
815 | if (!pdev) | 822 | if (!pdev) |
816 | return AE_OK; | 823 | goto exit_kfree; |
817 | 824 | ||
818 | if (pci_enable_device(pdev)) { | 825 | if (pci_enable_device(pdev)) |
819 | pci_dev_put(pdev); | 826 | goto exit_pci_dev_put; |
820 | return AE_OK; | ||
821 | } | ||
822 | 827 | ||
823 | pci_set_master(pdev); | 828 | pci_set_master(pdev); |
824 | 829 | ||
825 | if (pci_request_region(pdev, 0, "I/O APIC(acpiphp)")) { | 830 | if (pci_request_region(pdev, 0, "I/O APIC(acpiphp)")) |
826 | pci_disable_device(pdev); | 831 | goto exit_pci_disable_device; |
827 | pci_dev_put(pdev); | ||
828 | return AE_OK; | ||
829 | } | ||
830 | 832 | ||
831 | phys_addr = pci_resource_start(pdev, 0); | 833 | phys_addr = pci_resource_start(pdev, 0); |
832 | if (acpi_register_ioapic(handle, phys_addr, gsi_base)) { | 834 | if (acpi_register_ioapic(handle, phys_addr, gsi_base)) |
833 | pci_release_region(pdev, 0); | 835 | goto exit_pci_release_region; |
834 | pci_disable_device(pdev); | 836 | |
835 | pci_dev_put(pdev); | 837 | ioapic->gsi_base = gsi_base; |
838 | ioapic->dev = pdev; | ||
839 | spin_lock(&ioapic_list_lock); | ||
840 | list_add_tail(&ioapic->list, &ioapic_list); | ||
841 | spin_unlock(&ioapic_list_lock); | ||
842 | |||
843 | return AE_OK; | ||
844 | |||
845 | exit_pci_release_region: | ||
846 | pci_release_region(pdev, 0); | ||
847 | exit_pci_disable_device: | ||
848 | pci_disable_device(pdev); | ||
849 | exit_pci_dev_put: | ||
850 | pci_dev_put(pdev); | ||
851 | exit_kfree: | ||
852 | kfree(ioapic); | ||
853 | |||
854 | return AE_OK; | ||
855 | } | ||
856 | |||
857 | static acpi_status | ||
858 | ioapic_remove(acpi_handle handle, u32 lvl, void *context, void **rv) | ||
859 | { | ||
860 | acpi_status status; | ||
861 | unsigned long sta; | ||
862 | acpi_handle tmp; | ||
863 | u32 gsi_base; | ||
864 | struct acpiphp_ioapic *pos, *n, *ioapic = NULL; | ||
865 | |||
866 | /* Evaluate _STA if present */ | ||
867 | status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); | ||
868 | if (ACPI_SUCCESS(status) && sta != ACPI_STA_ALL) | ||
869 | return AE_CTRL_DEPTH; | ||
870 | |||
871 | /* Scan only PCI bus scope */ | ||
872 | status = acpi_get_handle(handle, "_HID", &tmp); | ||
873 | if (ACPI_SUCCESS(status)) | ||
874 | return AE_CTRL_DEPTH; | ||
875 | |||
876 | if (get_gsi_base(handle, &gsi_base)) | ||
836 | return AE_OK; | 877 | return AE_OK; |
878 | |||
879 | acpi_unregister_ioapic(handle, gsi_base); | ||
880 | |||
881 | spin_lock(&ioapic_list_lock); | ||
882 | list_for_each_entry_safe(pos, n, &ioapic_list, list) { | ||
883 | if (pos->gsi_base != gsi_base) | ||
884 | continue; | ||
885 | ioapic = pos; | ||
886 | list_del(&ioapic->list); | ||
887 | break; | ||
837 | } | 888 | } |
889 | spin_unlock(&ioapic_list_lock); | ||
890 | |||
891 | if (!ioapic) | ||
892 | return AE_OK; | ||
893 | |||
894 | pci_release_region(ioapic->dev, 0); | ||
895 | pci_disable_device(ioapic->dev); | ||
896 | pci_dev_put(ioapic->dev); | ||
897 | kfree(ioapic); | ||
838 | 898 | ||
839 | return AE_OK; | 899 | return AE_OK; |
840 | } | 900 | } |
841 | 901 | ||
842 | static int acpiphp_configure_ioapics(acpi_handle handle) | 902 | static int acpiphp_configure_ioapics(acpi_handle handle) |
843 | { | 903 | { |
904 | ioapic_add(handle, 0, NULL, NULL); | ||
844 | acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, | 905 | acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, |
845 | ACPI_UINT32_MAX, ioapic_add, NULL, NULL); | 906 | ACPI_UINT32_MAX, ioapic_add, NULL, NULL); |
846 | return 0; | 907 | return 0; |
847 | } | 908 | } |
848 | 909 | ||
910 | static int acpiphp_unconfigure_ioapics(acpi_handle handle) | ||
911 | { | ||
912 | ioapic_remove(handle, 0, NULL, NULL); | ||
913 | acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, | ||
914 | ACPI_UINT32_MAX, ioapic_remove, NULL, NULL); | ||
915 | return 0; | ||
916 | } | ||
917 | |||
849 | static int power_on_slot(struct acpiphp_slot *slot) | 918 | static int power_on_slot(struct acpiphp_slot *slot) |
850 | { | 919 | { |
851 | acpi_status status; | 920 | acpi_status status; |
@@ -997,7 +1066,7 @@ acpiphp_bus_add_out: | |||
997 | * @handle: handle to acpi namespace | 1066 | * @handle: handle to acpi namespace |
998 | * | 1067 | * |
999 | */ | 1068 | */ |
1000 | int acpiphp_bus_trim(acpi_handle handle) | 1069 | static int acpiphp_bus_trim(acpi_handle handle) |
1001 | { | 1070 | { |
1002 | struct acpi_device *device; | 1071 | struct acpi_device *device; |
1003 | int retval; | 1072 | int retval; |
@@ -1074,10 +1143,11 @@ static int enable_device(struct acpiphp_slot *slot) | |||
1074 | 1143 | ||
1075 | pci_bus_assign_resources(bus); | 1144 | pci_bus_assign_resources(bus); |
1076 | acpiphp_sanitize_bus(bus); | 1145 | acpiphp_sanitize_bus(bus); |
1146 | acpiphp_set_hpp_values(slot->bridge->handle, bus); | ||
1147 | list_for_each_entry(func, &slot->funcs, sibling) | ||
1148 | acpiphp_configure_ioapics(func->handle); | ||
1077 | pci_enable_bridges(bus); | 1149 | pci_enable_bridges(bus); |
1078 | pci_bus_add_devices(bus); | 1150 | pci_bus_add_devices(bus); |
1079 | acpiphp_set_hpp_values(slot->bridge->handle, bus); | ||
1080 | acpiphp_configure_ioapics(slot->bridge->handle); | ||
1081 | 1151 | ||
1082 | /* associate pci_dev to our representation */ | 1152 | /* associate pci_dev to our representation */ |
1083 | list_for_each (l, &slot->funcs) { | 1153 | list_for_each (l, &slot->funcs) { |
@@ -1103,6 +1173,16 @@ static int enable_device(struct acpiphp_slot *slot) | |||
1103 | return retval; | 1173 | return retval; |
1104 | } | 1174 | } |
1105 | 1175 | ||
1176 | static void disable_bridges(struct pci_bus *bus) | ||
1177 | { | ||
1178 | struct pci_dev *dev; | ||
1179 | list_for_each_entry(dev, &bus->devices, bus_list) { | ||
1180 | if (dev->subordinate) { | ||
1181 | disable_bridges(dev->subordinate); | ||
1182 | pci_disable_device(dev); | ||
1183 | } | ||
1184 | } | ||
1185 | } | ||
1106 | 1186 | ||
1107 | /** | 1187 | /** |
1108 | * disable_device - disable a slot | 1188 | * disable_device - disable a slot |
@@ -1127,6 +1207,19 @@ static int disable_device(struct acpiphp_slot *slot) | |||
1127 | func->bridge = NULL; | 1207 | func->bridge = NULL; |
1128 | } | 1208 | } |
1129 | 1209 | ||
1210 | if (func->pci_dev) { | ||
1211 | pci_stop_bus_device(func->pci_dev); | ||
1212 | if (func->pci_dev->subordinate) { | ||
1213 | disable_bridges(func->pci_dev->subordinate); | ||
1214 | pci_disable_device(func->pci_dev); | ||
1215 | } | ||
1216 | } | ||
1217 | } | ||
1218 | |||
1219 | list_for_each (l, &slot->funcs) { | ||
1220 | func = list_entry(l, struct acpiphp_func, sibling); | ||
1221 | |||
1222 | acpiphp_unconfigure_ioapics(func->handle); | ||
1130 | acpiphp_bus_trim(func->handle); | 1223 | acpiphp_bus_trim(func->handle); |
1131 | /* try to remove anyway. | 1224 | /* try to remove anyway. |
1132 | * acpiphp_bus_add might have been failed */ | 1225 | * acpiphp_bus_add might have been failed */ |
diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c index 317457dd4014..d0a07d9ab30c 100644 --- a/drivers/pci/hotplug/acpiphp_ibm.c +++ b/drivers/pci/hotplug/acpiphp_ibm.c | |||
@@ -487,9 +487,7 @@ static void __exit ibm_acpiphp_exit(void) | |||
487 | if (ACPI_FAILURE(status)) | 487 | if (ACPI_FAILURE(status)) |
488 | err("%s: Notification handler removal failed\n", __FUNCTION__); | 488 | err("%s: Notification handler removal failed\n", __FUNCTION__); |
489 | /* remove the /sys entries */ | 489 | /* remove the /sys entries */ |
490 | if (sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr)) | 490 | sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr); |
491 | err("%s: removal of sysfs file apci_table failed\n", | ||
492 | __FUNCTION__); | ||
493 | } | 491 | } |
494 | 492 | ||
495 | module_init(ibm_acpiphp_init); | 493 | module_init(ibm_acpiphp_init); |
diff --git a/drivers/pci/hotplug/cpqphp_sysfs.c b/drivers/pci/hotplug/cpqphp_sysfs.c index 8b3da007e859..5bab666cd67e 100644 --- a/drivers/pci/hotplug/cpqphp_sysfs.c +++ b/drivers/pci/hotplug/cpqphp_sysfs.c | |||
@@ -140,7 +140,7 @@ struct ctrl_dbg { | |||
140 | 140 | ||
141 | static int open(struct inode *inode, struct file *file) | 141 | static int open(struct inode *inode, struct file *file) |
142 | { | 142 | { |
143 | struct controller *ctrl = inode->u.generic_ip; | 143 | struct controller *ctrl = inode->i_private; |
144 | struct ctrl_dbg *dbg; | 144 | struct ctrl_dbg *dbg; |
145 | int retval = -ENOMEM; | 145 | int retval = -ENOMEM; |
146 | 146 | ||
diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c index dd2b762777c4..05a4f0f90186 100644 --- a/drivers/pci/hotplug/fakephp.c +++ b/drivers/pci/hotplug/fakephp.c | |||
@@ -176,7 +176,9 @@ static void pci_rescan_slot(struct pci_dev *temp) | |||
176 | struct pci_bus *bus = temp->bus; | 176 | struct pci_bus *bus = temp->bus; |
177 | struct pci_dev *dev; | 177 | struct pci_dev *dev; |
178 | int func; | 178 | int func; |
179 | int retval; | ||
179 | u8 hdr_type; | 180 | u8 hdr_type; |
181 | |||
180 | if (!pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) { | 182 | if (!pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) { |
181 | temp->hdr_type = hdr_type & 0x7f; | 183 | temp->hdr_type = hdr_type & 0x7f; |
182 | if (!pci_find_slot(bus->number, temp->devfn)) { | 184 | if (!pci_find_slot(bus->number, temp->devfn)) { |
@@ -185,8 +187,12 @@ static void pci_rescan_slot(struct pci_dev *temp) | |||
185 | dbg("New device on %s function %x:%x\n", | 187 | dbg("New device on %s function %x:%x\n", |
186 | bus->name, temp->devfn >> 3, | 188 | bus->name, temp->devfn >> 3, |
187 | temp->devfn & 7); | 189 | temp->devfn & 7); |
188 | pci_bus_add_device(dev); | 190 | retval = pci_bus_add_device(dev); |
189 | add_slot(dev); | 191 | if (retval) |
192 | dev_err(&dev->dev, "error adding " | ||
193 | "device, continuing.\n"); | ||
194 | else | ||
195 | add_slot(dev); | ||
190 | } | 196 | } |
191 | } | 197 | } |
192 | /* multifunction device? */ | 198 | /* multifunction device? */ |
@@ -205,8 +211,12 @@ static void pci_rescan_slot(struct pci_dev *temp) | |||
205 | dbg("New device on %s function %x:%x\n", | 211 | dbg("New device on %s function %x:%x\n", |
206 | bus->name, temp->devfn >> 3, | 212 | bus->name, temp->devfn >> 3, |
207 | temp->devfn & 7); | 213 | temp->devfn & 7); |
208 | pci_bus_add_device(dev); | 214 | retval = pci_bus_add_device(dev); |
209 | add_slot(dev); | 215 | if (retval) |
216 | dev_err(&dev->dev, "error adding " | ||
217 | "device, continuing.\n"); | ||
218 | else | ||
219 | add_slot(dev); | ||
210 | } | 220 | } |
211 | } | 221 | } |
212 | } | 222 | } |
diff --git a/drivers/pci/hotplug/pci_hotplug.h b/drivers/pci/hotplug/pci_hotplug.h index e929b7c11429..772523dc3860 100644 --- a/drivers/pci/hotplug/pci_hotplug.h +++ b/drivers/pci/hotplug/pci_hotplug.h | |||
@@ -172,8 +172,8 @@ struct hotplug_slot { | |||
172 | 172 | ||
173 | extern int pci_hp_register (struct hotplug_slot *slot); | 173 | extern int pci_hp_register (struct hotplug_slot *slot); |
174 | extern int pci_hp_deregister (struct hotplug_slot *slot); | 174 | extern int pci_hp_deregister (struct hotplug_slot *slot); |
175 | extern int pci_hp_change_slot_info (struct hotplug_slot *slot, | 175 | extern int __must_check pci_hp_change_slot_info (struct hotplug_slot *slot, |
176 | struct hotplug_slot_info *info); | 176 | struct hotplug_slot_info *info); |
177 | extern struct subsystem pci_hotplug_slots_subsys; | 177 | extern struct subsystem pci_hotplug_slots_subsys; |
178 | 178 | ||
179 | /* PCI Setting Record (Type 0) */ | 179 | /* PCI Setting Record (Type 0) */ |
diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c index b7b378df89e3..e2823ea9c4ed 100644 --- a/drivers/pci/hotplug/pci_hotplug_core.c +++ b/drivers/pci/hotplug/pci_hotplug_core.c | |||
@@ -482,31 +482,95 @@ static int has_test_file (struct hotplug_slot *slot) | |||
482 | 482 | ||
483 | static int fs_add_slot (struct hotplug_slot *slot) | 483 | static int fs_add_slot (struct hotplug_slot *slot) |
484 | { | 484 | { |
485 | if (has_power_file(slot) == 0) | 485 | int retval = 0; |
486 | sysfs_create_file(&slot->kobj, &hotplug_slot_attr_power.attr); | ||
487 | 486 | ||
488 | if (has_attention_file(slot) == 0) | 487 | if (has_power_file(slot) == 0) { |
489 | sysfs_create_file(&slot->kobj, &hotplug_slot_attr_attention.attr); | 488 | retval = sysfs_create_file(&slot->kobj, &hotplug_slot_attr_power.attr); |
489 | if (retval) | ||
490 | goto exit_power; | ||
491 | } | ||
490 | 492 | ||
491 | if (has_latch_file(slot) == 0) | 493 | if (has_attention_file(slot) == 0) { |
492 | sysfs_create_file(&slot->kobj, &hotplug_slot_attr_latch.attr); | 494 | retval = sysfs_create_file(&slot->kobj, |
495 | &hotplug_slot_attr_attention.attr); | ||
496 | if (retval) | ||
497 | goto exit_attention; | ||
498 | } | ||
493 | 499 | ||
494 | if (has_adapter_file(slot) == 0) | 500 | if (has_latch_file(slot) == 0) { |
495 | sysfs_create_file(&slot->kobj, &hotplug_slot_attr_presence.attr); | 501 | retval = sysfs_create_file(&slot->kobj, |
502 | &hotplug_slot_attr_latch.attr); | ||
503 | if (retval) | ||
504 | goto exit_latch; | ||
505 | } | ||
496 | 506 | ||
497 | if (has_address_file(slot) == 0) | 507 | if (has_adapter_file(slot) == 0) { |
498 | sysfs_create_file(&slot->kobj, &hotplug_slot_attr_address.attr); | 508 | retval = sysfs_create_file(&slot->kobj, |
509 | &hotplug_slot_attr_presence.attr); | ||
510 | if (retval) | ||
511 | goto exit_adapter; | ||
512 | } | ||
499 | 513 | ||
500 | if (has_max_bus_speed_file(slot) == 0) | 514 | if (has_address_file(slot) == 0) { |
501 | sysfs_create_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr); | 515 | retval = sysfs_create_file(&slot->kobj, |
516 | &hotplug_slot_attr_address.attr); | ||
517 | if (retval) | ||
518 | goto exit_address; | ||
519 | } | ||
502 | 520 | ||
521 | if (has_max_bus_speed_file(slot) == 0) { | ||
522 | retval = sysfs_create_file(&slot->kobj, | ||
523 | &hotplug_slot_attr_max_bus_speed.attr); | ||
524 | if (retval) | ||
525 | goto exit_max_speed; | ||
526 | } | ||
527 | |||
528 | if (has_cur_bus_speed_file(slot) == 0) { | ||
529 | retval = sysfs_create_file(&slot->kobj, | ||
530 | &hotplug_slot_attr_cur_bus_speed.attr); | ||
531 | if (retval) | ||
532 | goto exit_cur_speed; | ||
533 | } | ||
534 | |||
535 | if (has_test_file(slot) == 0) { | ||
536 | retval = sysfs_create_file(&slot->kobj, | ||
537 | &hotplug_slot_attr_test.attr); | ||
538 | if (retval) | ||
539 | goto exit_test; | ||
540 | } | ||
541 | |||
542 | goto exit; | ||
543 | |||
544 | exit_test: | ||
503 | if (has_cur_bus_speed_file(slot) == 0) | 545 | if (has_cur_bus_speed_file(slot) == 0) |
504 | sysfs_create_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr); | 546 | sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr); |
505 | 547 | ||
506 | if (has_test_file(slot) == 0) | 548 | exit_cur_speed: |
507 | sysfs_create_file(&slot->kobj, &hotplug_slot_attr_test.attr); | 549 | if (has_max_bus_speed_file(slot) == 0) |
550 | sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr); | ||
508 | 551 | ||
509 | return 0; | 552 | exit_max_speed: |
553 | if (has_address_file(slot) == 0) | ||
554 | sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr); | ||
555 | |||
556 | exit_address: | ||
557 | if (has_adapter_file(slot) == 0) | ||
558 | sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr); | ||
559 | |||
560 | exit_adapter: | ||
561 | if (has_latch_file(slot) == 0) | ||
562 | sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr); | ||
563 | |||
564 | exit_latch: | ||
565 | if (has_attention_file(slot) == 0) | ||
566 | sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr); | ||
567 | |||
568 | exit_attention: | ||
569 | if (has_power_file(slot) == 0) | ||
570 | sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr); | ||
571 | exit_power: | ||
572 | exit: | ||
573 | return retval; | ||
510 | } | 574 | } |
511 | 575 | ||
512 | static void fs_remove_slot (struct hotplug_slot *slot) | 576 | static void fs_remove_slot (struct hotplug_slot *slot) |
@@ -626,8 +690,11 @@ int pci_hp_deregister (struct hotplug_slot *slot) | |||
626 | * | 690 | * |
627 | * Returns 0 if successful, anything else for an error. | 691 | * Returns 0 if successful, anything else for an error. |
628 | */ | 692 | */ |
629 | int pci_hp_change_slot_info (struct hotplug_slot *slot, struct hotplug_slot_info *info) | 693 | int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot, |
694 | struct hotplug_slot_info *info) | ||
630 | { | 695 | { |
696 | int retval; | ||
697 | |||
631 | if ((slot == NULL) || (info == NULL)) | 698 | if ((slot == NULL) || (info == NULL)) |
632 | return -ENODEV; | 699 | return -ENODEV; |
633 | 700 | ||
@@ -636,32 +703,60 @@ int pci_hp_change_slot_info (struct hotplug_slot *slot, struct hotplug_slot_info | |||
636 | * for the files referring to the fields that have now changed. | 703 | * for the files referring to the fields that have now changed. |
637 | */ | 704 | */ |
638 | if ((has_power_file(slot) == 0) && | 705 | if ((has_power_file(slot) == 0) && |
639 | (slot->info->power_status != info->power_status)) | 706 | (slot->info->power_status != info->power_status)) { |
640 | sysfs_update_file(&slot->kobj, &hotplug_slot_attr_power.attr); | 707 | retval = sysfs_update_file(&slot->kobj, |
708 | &hotplug_slot_attr_power.attr); | ||
709 | if (retval) | ||
710 | return retval; | ||
711 | } | ||
641 | 712 | ||
642 | if ((has_attention_file(slot) == 0) && | 713 | if ((has_attention_file(slot) == 0) && |
643 | (slot->info->attention_status != info->attention_status)) | 714 | (slot->info->attention_status != info->attention_status)) { |
644 | sysfs_update_file(&slot->kobj, &hotplug_slot_attr_attention.attr); | 715 | retval = sysfs_update_file(&slot->kobj, |
716 | &hotplug_slot_attr_attention.attr); | ||
717 | if (retval) | ||
718 | return retval; | ||
719 | } | ||
645 | 720 | ||
646 | if ((has_latch_file(slot) == 0) && | 721 | if ((has_latch_file(slot) == 0) && |
647 | (slot->info->latch_status != info->latch_status)) | 722 | (slot->info->latch_status != info->latch_status)) { |
648 | sysfs_update_file(&slot->kobj, &hotplug_slot_attr_latch.attr); | 723 | retval = sysfs_update_file(&slot->kobj, |
724 | &hotplug_slot_attr_latch.attr); | ||
725 | if (retval) | ||
726 | return retval; | ||
727 | } | ||
649 | 728 | ||
650 | if ((has_adapter_file(slot) == 0) && | 729 | if ((has_adapter_file(slot) == 0) && |
651 | (slot->info->adapter_status != info->adapter_status)) | 730 | (slot->info->adapter_status != info->adapter_status)) { |
652 | sysfs_update_file(&slot->kobj, &hotplug_slot_attr_presence.attr); | 731 | retval = sysfs_update_file(&slot->kobj, |
732 | &hotplug_slot_attr_presence.attr); | ||
733 | if (retval) | ||
734 | return retval; | ||
735 | } | ||
653 | 736 | ||
654 | if ((has_address_file(slot) == 0) && | 737 | if ((has_address_file(slot) == 0) && |
655 | (slot->info->address != info->address)) | 738 | (slot->info->address != info->address)) { |
656 | sysfs_update_file(&slot->kobj, &hotplug_slot_attr_address.attr); | 739 | retval = sysfs_update_file(&slot->kobj, |
740 | &hotplug_slot_attr_address.attr); | ||
741 | if (retval) | ||
742 | return retval; | ||
743 | } | ||
657 | 744 | ||
658 | if ((has_max_bus_speed_file(slot) == 0) && | 745 | if ((has_max_bus_speed_file(slot) == 0) && |
659 | (slot->info->max_bus_speed != info->max_bus_speed)) | 746 | (slot->info->max_bus_speed != info->max_bus_speed)) { |
660 | sysfs_update_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr); | 747 | retval = sysfs_update_file(&slot->kobj, |
748 | &hotplug_slot_attr_max_bus_speed.attr); | ||
749 | if (retval) | ||
750 | return retval; | ||
751 | } | ||
661 | 752 | ||
662 | if ((has_cur_bus_speed_file(slot) == 0) && | 753 | if ((has_cur_bus_speed_file(slot) == 0) && |
663 | (slot->info->cur_bus_speed != info->cur_bus_speed)) | 754 | (slot->info->cur_bus_speed != info->cur_bus_speed)) { |
664 | sysfs_update_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr); | 755 | retval = sysfs_update_file(&slot->kobj, |
756 | &hotplug_slot_attr_cur_bus_speed.attr); | ||
757 | if (retval) | ||
758 | return retval; | ||
759 | } | ||
665 | 760 | ||
666 | memcpy (slot->info, info, sizeof (struct hotplug_slot_info)); | 761 | memcpy (slot->info, info, sizeof (struct hotplug_slot_info)); |
667 | 762 | ||
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 33d198768356..41290a106bd8 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c | |||
@@ -762,14 +762,14 @@ int pciehp_enable_slot(struct slot *p_slot) | |||
762 | if (rc || !getstatus) { | 762 | if (rc || !getstatus) { |
763 | info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number); | 763 | info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number); |
764 | mutex_unlock(&p_slot->ctrl->crit_sect); | 764 | mutex_unlock(&p_slot->ctrl->crit_sect); |
765 | return 1; | 765 | return -ENODEV; |
766 | } | 766 | } |
767 | if (MRL_SENS(p_slot->ctrl->ctrlcap)) { | 767 | if (MRL_SENS(p_slot->ctrl->ctrlcap)) { |
768 | rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | 768 | rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); |
769 | if (rc || getstatus) { | 769 | if (rc || getstatus) { |
770 | info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number); | 770 | info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number); |
771 | mutex_unlock(&p_slot->ctrl->crit_sect); | 771 | mutex_unlock(&p_slot->ctrl->crit_sect); |
772 | return 1; | 772 | return -ENODEV; |
773 | } | 773 | } |
774 | } | 774 | } |
775 | 775 | ||
@@ -778,7 +778,7 @@ int pciehp_enable_slot(struct slot *p_slot) | |||
778 | if (rc || getstatus) { | 778 | if (rc || getstatus) { |
779 | info("%s: already enabled on slot(%x)\n", __FUNCTION__, p_slot->number); | 779 | info("%s: already enabled on slot(%x)\n", __FUNCTION__, p_slot->number); |
780 | mutex_unlock(&p_slot->ctrl->crit_sect); | 780 | mutex_unlock(&p_slot->ctrl->crit_sect); |
781 | return 1; | 781 | return -EINVAL; |
782 | } | 782 | } |
783 | } | 783 | } |
784 | mutex_unlock(&p_slot->ctrl->crit_sect); | 784 | mutex_unlock(&p_slot->ctrl->crit_sect); |
@@ -813,7 +813,7 @@ int pciehp_disable_slot(struct slot *p_slot) | |||
813 | if (ret || !getstatus) { | 813 | if (ret || !getstatus) { |
814 | info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number); | 814 | info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number); |
815 | mutex_unlock(&p_slot->ctrl->crit_sect); | 815 | mutex_unlock(&p_slot->ctrl->crit_sect); |
816 | return 1; | 816 | return -ENODEV; |
817 | } | 817 | } |
818 | } | 818 | } |
819 | 819 | ||
@@ -822,7 +822,7 @@ int pciehp_disable_slot(struct slot *p_slot) | |||
822 | if (ret || getstatus) { | 822 | if (ret || getstatus) { |
823 | info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number); | 823 | info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number); |
824 | mutex_unlock(&p_slot->ctrl->crit_sect); | 824 | mutex_unlock(&p_slot->ctrl->crit_sect); |
825 | return 1; | 825 | return -ENODEV; |
826 | } | 826 | } |
827 | } | 827 | } |
828 | 828 | ||
@@ -831,7 +831,7 @@ int pciehp_disable_slot(struct slot *p_slot) | |||
831 | if (ret || !getstatus) { | 831 | if (ret || !getstatus) { |
832 | info("%s: already disabled slot(%x)\n", __FUNCTION__, p_slot->number); | 832 | info("%s: already disabled slot(%x)\n", __FUNCTION__, p_slot->number); |
833 | mutex_unlock(&p_slot->ctrl->crit_sect); | 833 | mutex_unlock(&p_slot->ctrl->crit_sect); |
834 | return 1; | 834 | return -EINVAL; |
835 | } | 835 | } |
836 | } | 836 | } |
837 | 837 | ||
diff --git a/drivers/pci/hotplug/pcihp_skeleton.c b/drivers/pci/hotplug/pcihp_skeleton.c index 8ad446605f75..2b9e10e38613 100644 --- a/drivers/pci/hotplug/pcihp_skeleton.c +++ b/drivers/pci/hotplug/pcihp_skeleton.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * PCI Hot Plug Controller Skeleton Driver - 0.2 | 2 | * PCI Hot Plug Controller Skeleton Driver - 0.3 |
3 | * | 3 | * |
4 | * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com) | 4 | * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com) |
5 | * Copyright (C) 2001,2003 IBM Corp. | 5 | * Copyright (C) 2001,2003 IBM Corp. |
@@ -21,7 +21,7 @@ | |||
21 | * along with this program; if not, write to the Free Software | 21 | * along with this program; if not, write to the Free Software |
22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
23 | * | 23 | * |
24 | * This driver is to be used as a skeleton driver to be show how to interface | 24 | * This driver is to be used as a skeleton driver to show how to interface |
25 | * with the pci hotplug core easily. | 25 | * with the pci hotplug core easily. |
26 | * | 26 | * |
27 | * Send feedback to <greg@kroah.com> | 27 | * Send feedback to <greg@kroah.com> |
@@ -58,8 +58,6 @@ static LIST_HEAD(slot_list); | |||
58 | #define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg) | 58 | #define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg) |
59 | #define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg) | 59 | #define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg) |
60 | 60 | ||
61 | |||
62 | |||
63 | /* local variables */ | 61 | /* local variables */ |
64 | static int debug; | 62 | static int debug; |
65 | static int num_slots; | 63 | static int num_slots; |
@@ -109,7 +107,6 @@ static int enable_slot(struct hotplug_slot *hotplug_slot) | |||
109 | return retval; | 107 | return retval; |
110 | } | 108 | } |
111 | 109 | ||
112 | |||
113 | static int disable_slot(struct hotplug_slot *hotplug_slot) | 110 | static int disable_slot(struct hotplug_slot *hotplug_slot) |
114 | { | 111 | { |
115 | struct slot *slot = hotplug_slot->private; | 112 | struct slot *slot = hotplug_slot->private; |
@@ -342,7 +339,7 @@ static int __init pcihp_skel_init(void) | |||
342 | info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); | 339 | info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); |
343 | /* | 340 | /* |
344 | * Do specific initialization stuff for your driver here | 341 | * Do specific initialization stuff for your driver here |
345 | * Like initializing your controller hardware (if any) and | 342 | * like initializing your controller hardware (if any) and |
346 | * determining the number of slots you have in the system | 343 | * determining the number of slots you have in the system |
347 | * right now. | 344 | * right now. |
348 | */ | 345 | */ |
diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h index 7208b95c6ee7..c7103ac5cd06 100644 --- a/drivers/pci/hotplug/shpchp.h +++ b/drivers/pci/hotplug/shpchp.h | |||
@@ -173,7 +173,7 @@ struct controller { | |||
173 | #define msg_button_cancel "PCI slot #%s - action canceled due to button press.\n" | 173 | #define msg_button_cancel "PCI slot #%s - action canceled due to button press.\n" |
174 | 174 | ||
175 | /* sysfs functions for the hotplug controller info */ | 175 | /* sysfs functions for the hotplug controller info */ |
176 | extern void shpchp_create_ctrl_files (struct controller *ctrl); | 176 | extern int __must_check shpchp_create_ctrl_files(struct controller *ctrl); |
177 | 177 | ||
178 | extern int shpchp_sysfs_enable_slot(struct slot *slot); | 178 | extern int shpchp_sysfs_enable_slot(struct slot *slot); |
179 | extern int shpchp_sysfs_disable_slot(struct slot *slot); | 179 | extern int shpchp_sysfs_disable_slot(struct slot *slot); |
diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c index a14e7de19846..235c18a22393 100644 --- a/drivers/pci/hotplug/shpchp_core.c +++ b/drivers/pci/hotplug/shpchp_core.c | |||
@@ -449,10 +449,14 @@ static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
449 | ctrl->speed = PCI_SPEED_33MHz; | 449 | ctrl->speed = PCI_SPEED_33MHz; |
450 | } | 450 | } |
451 | 451 | ||
452 | shpchp_create_ctrl_files(ctrl); | 452 | rc = shpchp_create_ctrl_files(ctrl); |
453 | if (rc) | ||
454 | goto err_cleanup_slots; | ||
453 | 455 | ||
454 | return 0; | 456 | return 0; |
455 | 457 | ||
458 | err_cleanup_slots: | ||
459 | cleanup_slots(ctrl); | ||
456 | err_out_release_ctlr: | 460 | err_out_release_ctlr: |
457 | ctrl->hpc_ops->release_ctlr(ctrl); | 461 | ctrl->hpc_ops->release_ctlr(ctrl); |
458 | err_out_free_ctrl: | 462 | err_out_free_ctrl: |
diff --git a/drivers/pci/hotplug/shpchp_sysfs.c b/drivers/pci/hotplug/shpchp_sysfs.c index 620e1139e607..29fa9d26adae 100644 --- a/drivers/pci/hotplug/shpchp_sysfs.c +++ b/drivers/pci/hotplug/shpchp_sysfs.c | |||
@@ -91,9 +91,9 @@ static ssize_t show_ctrl (struct device *dev, struct device_attribute *attr, cha | |||
91 | } | 91 | } |
92 | static DEVICE_ATTR (ctrl, S_IRUGO, show_ctrl, NULL); | 92 | static DEVICE_ATTR (ctrl, S_IRUGO, show_ctrl, NULL); |
93 | 93 | ||
94 | void shpchp_create_ctrl_files (struct controller *ctrl) | 94 | int __must_check shpchp_create_ctrl_files (struct controller *ctrl) |
95 | { | 95 | { |
96 | device_create_file (&ctrl->pci_dev->dev, &dev_attr_ctrl); | 96 | return device_create_file (&ctrl->pci_dev->dev, &dev_attr_ctrl); |
97 | } | 97 | } |
98 | 98 | ||
99 | void shpchp_remove_ctrl_files(struct controller *ctrl) | 99 | void shpchp_remove_ctrl_files(struct controller *ctrl) |
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index a83c1f5735d6..27a057409eca 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c | |||
@@ -45,16 +45,10 @@ msi_register(struct msi_ops *ops) | |||
45 | return 0; | 45 | return 0; |
46 | } | 46 | } |
47 | 47 | ||
48 | static void msi_cache_ctor(void *p, kmem_cache_t *cache, unsigned long flags) | ||
49 | { | ||
50 | memset(p, 0, sizeof(struct msi_desc)); | ||
51 | } | ||
52 | |||
53 | static int msi_cache_init(void) | 48 | static int msi_cache_init(void) |
54 | { | 49 | { |
55 | msi_cachep = kmem_cache_create("msi_cache", | 50 | msi_cachep = kmem_cache_create("msi_cache", sizeof(struct msi_desc), |
56 | sizeof(struct msi_desc), | 51 | 0, SLAB_HWCACHE_ALIGN, NULL, NULL); |
57 | 0, SLAB_HWCACHE_ALIGN, msi_cache_ctor, NULL); | ||
58 | if (!msi_cachep) | 52 | if (!msi_cachep) |
59 | return -ENOMEM; | 53 | return -ENOMEM; |
60 | 54 | ||
@@ -402,11 +396,10 @@ static struct msi_desc* alloc_msi_entry(void) | |||
402 | { | 396 | { |
403 | struct msi_desc *entry; | 397 | struct msi_desc *entry; |
404 | 398 | ||
405 | entry = kmem_cache_alloc(msi_cachep, SLAB_KERNEL); | 399 | entry = kmem_cache_zalloc(msi_cachep, GFP_KERNEL); |
406 | if (!entry) | 400 | if (!entry) |
407 | return NULL; | 401 | return NULL; |
408 | 402 | ||
409 | memset(entry, 0, sizeof(struct msi_desc)); | ||
410 | entry->link.tail = entry->link.head = 0; /* single message */ | 403 | entry->link.tail = entry->link.head = 0; /* single message */ |
411 | entry->dev = NULL; | 404 | entry->dev = NULL; |
412 | 405 | ||
@@ -901,6 +894,33 @@ static int msix_capability_init(struct pci_dev *dev, | |||
901 | } | 894 | } |
902 | 895 | ||
903 | /** | 896 | /** |
897 | * pci_msi_supported - check whether MSI may be enabled on device | ||
898 | * @dev: pointer to the pci_dev data structure of MSI device function | ||
899 | * | ||
900 | * MSI must be globally enabled and supported by the device and its root | ||
901 | * bus. But, the root bus is not easy to find since some architectures | ||
902 | * have virtual busses on top of the PCI hierarchy (for instance the | ||
903 | * hypertransport bus), while the actual bus where MSI must be supported | ||
904 | * is below. So we test the MSI flag on all parent busses and assume | ||
905 | * that no quirk will ever set the NO_MSI flag on a non-root bus. | ||
906 | **/ | ||
907 | static | ||
908 | int pci_msi_supported(struct pci_dev * dev) | ||
909 | { | ||
910 | struct pci_bus *bus; | ||
911 | |||
912 | if (!pci_msi_enable || !dev || dev->no_msi) | ||
913 | return -EINVAL; | ||
914 | |||
915 | /* check MSI flags of all parent busses */ | ||
916 | for (bus = dev->bus; bus; bus = bus->parent) | ||
917 | if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI) | ||
918 | return -EINVAL; | ||
919 | |||
920 | return 0; | ||
921 | } | ||
922 | |||
923 | /** | ||
904 | * pci_enable_msi - configure device's MSI capability structure | 924 | * pci_enable_msi - configure device's MSI capability structure |
905 | * @dev: pointer to the pci_dev data structure of MSI device function | 925 | * @dev: pointer to the pci_dev data structure of MSI device function |
906 | * | 926 | * |
@@ -912,19 +932,11 @@ static int msix_capability_init(struct pci_dev *dev, | |||
912 | **/ | 932 | **/ |
913 | int pci_enable_msi(struct pci_dev* dev) | 933 | int pci_enable_msi(struct pci_dev* dev) |
914 | { | 934 | { |
915 | struct pci_bus *bus; | 935 | int pos, temp, status; |
916 | int pos, temp, status = -EINVAL; | ||
917 | u16 control; | 936 | u16 control; |
918 | 937 | ||
919 | if (!pci_msi_enable || !dev) | 938 | if (pci_msi_supported(dev) < 0) |
920 | return status; | 939 | return -EINVAL; |
921 | |||
922 | if (dev->no_msi) | ||
923 | return status; | ||
924 | |||
925 | for (bus = dev->bus; bus; bus = bus->parent) | ||
926 | if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI) | ||
927 | return -EINVAL; | ||
928 | 940 | ||
929 | temp = dev->irq; | 941 | temp = dev->irq; |
930 | 942 | ||
@@ -1134,22 +1146,14 @@ static int reroute_msix_table(int head, struct msix_entry *entries, int *nvec) | |||
1134 | **/ | 1146 | **/ |
1135 | int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) | 1147 | int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) |
1136 | { | 1148 | { |
1137 | struct pci_bus *bus; | ||
1138 | int status, pos, nr_entries, free_vectors; | 1149 | int status, pos, nr_entries, free_vectors; |
1139 | int i, j, temp; | 1150 | int i, j, temp; |
1140 | u16 control; | 1151 | u16 control; |
1141 | unsigned long flags; | 1152 | unsigned long flags; |
1142 | 1153 | ||
1143 | if (!pci_msi_enable || !dev || !entries) | 1154 | if (!entries || pci_msi_supported(dev) < 0) |
1144 | return -EINVAL; | 1155 | return -EINVAL; |
1145 | 1156 | ||
1146 | if (dev->no_msi) | ||
1147 | return -EINVAL; | ||
1148 | |||
1149 | for (bus = dev->bus; bus; bus = bus->parent) | ||
1150 | if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI) | ||
1151 | return -EINVAL; | ||
1152 | |||
1153 | status = msi_init(); | 1157 | status = msi_init(); |
1154 | if (status < 0) | 1158 | if (status < 0) |
1155 | return status; | 1159 | return status; |
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 474e9cd0e9e4..b1c0c707d96c 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c | |||
@@ -17,6 +17,16 @@ | |||
17 | * Registration of PCI drivers and handling of hot-pluggable devices. | 17 | * Registration of PCI drivers and handling of hot-pluggable devices. |
18 | */ | 18 | */ |
19 | 19 | ||
20 | /* multithreaded probe logic */ | ||
21 | static int pci_multithread_probe = | ||
22 | #ifdef CONFIG_PCI_MULTITHREAD_PROBE | ||
23 | 1; | ||
24 | #else | ||
25 | 0; | ||
26 | #endif | ||
27 | __module_param_call("", pci_multithread_probe, param_set_bool, param_get_bool, &pci_multithread_probe, 0644); | ||
28 | |||
29 | |||
20 | /* | 30 | /* |
21 | * Dynamic device IDs are disabled for !CONFIG_HOTPLUG | 31 | * Dynamic device IDs are disabled for !CONFIG_HOTPLUG |
22 | */ | 32 | */ |
@@ -46,6 +56,7 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count) | |||
46 | subdevice=PCI_ANY_ID, class=0, class_mask=0; | 56 | subdevice=PCI_ANY_ID, class=0, class_mask=0; |
47 | unsigned long driver_data=0; | 57 | unsigned long driver_data=0; |
48 | int fields=0; | 58 | int fields=0; |
59 | int retval = 0; | ||
49 | 60 | ||
50 | fields = sscanf(buf, "%x %x %x %x %x %x %lux", | 61 | fields = sscanf(buf, "%x %x %x %x %x %x %lux", |
51 | &vendor, &device, &subvendor, &subdevice, | 62 | &vendor, &device, &subvendor, &subdevice, |
@@ -72,10 +83,12 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count) | |||
72 | spin_unlock(&pdrv->dynids.lock); | 83 | spin_unlock(&pdrv->dynids.lock); |
73 | 84 | ||
74 | if (get_driver(&pdrv->driver)) { | 85 | if (get_driver(&pdrv->driver)) { |
75 | driver_attach(&pdrv->driver); | 86 | retval = driver_attach(&pdrv->driver); |
76 | put_driver(&pdrv->driver); | 87 | put_driver(&pdrv->driver); |
77 | } | 88 | } |
78 | 89 | ||
90 | if (retval) | ||
91 | return retval; | ||
79 | return count; | 92 | return count; |
80 | } | 93 | } |
81 | static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); | 94 | static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); |
@@ -279,6 +292,18 @@ static int pci_device_suspend(struct device * dev, pm_message_t state) | |||
279 | return i; | 292 | return i; |
280 | } | 293 | } |
281 | 294 | ||
295 | static int pci_device_suspend_late(struct device * dev, pm_message_t state) | ||
296 | { | ||
297 | struct pci_dev * pci_dev = to_pci_dev(dev); | ||
298 | struct pci_driver * drv = pci_dev->driver; | ||
299 | int i = 0; | ||
300 | |||
301 | if (drv && drv->suspend_late) { | ||
302 | i = drv->suspend_late(pci_dev, state); | ||
303 | suspend_report_result(drv->suspend_late, i); | ||
304 | } | ||
305 | return i; | ||
306 | } | ||
282 | 307 | ||
283 | /* | 308 | /* |
284 | * Default resume method for devices that have no driver provided resume, | 309 | * Default resume method for devices that have no driver provided resume, |
@@ -313,6 +338,17 @@ static int pci_device_resume(struct device * dev) | |||
313 | return error; | 338 | return error; |
314 | } | 339 | } |
315 | 340 | ||
341 | static int pci_device_resume_early(struct device * dev) | ||
342 | { | ||
343 | int error = 0; | ||
344 | struct pci_dev * pci_dev = to_pci_dev(dev); | ||
345 | struct pci_driver * drv = pci_dev->driver; | ||
346 | |||
347 | if (drv && drv->resume_early) | ||
348 | error = drv->resume_early(pci_dev); | ||
349 | return error; | ||
350 | } | ||
351 | |||
316 | static void pci_device_shutdown(struct device *dev) | 352 | static void pci_device_shutdown(struct device *dev) |
317 | { | 353 | { |
318 | struct pci_dev *pci_dev = to_pci_dev(dev); | 354 | struct pci_dev *pci_dev = to_pci_dev(dev); |
@@ -386,6 +422,11 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner) | |||
386 | drv->driver.owner = owner; | 422 | drv->driver.owner = owner; |
387 | drv->driver.kobj.ktype = &pci_driver_kobj_type; | 423 | drv->driver.kobj.ktype = &pci_driver_kobj_type; |
388 | 424 | ||
425 | if (pci_multithread_probe) | ||
426 | drv->driver.multithread_probe = pci_multithread_probe; | ||
427 | else | ||
428 | drv->driver.multithread_probe = drv->multithread_probe; | ||
429 | |||
389 | spin_lock_init(&drv->dynids.lock); | 430 | spin_lock_init(&drv->dynids.lock); |
390 | INIT_LIST_HEAD(&drv->dynids.list); | 431 | INIT_LIST_HEAD(&drv->dynids.list); |
391 | 432 | ||
@@ -509,8 +550,10 @@ struct bus_type pci_bus_type = { | |||
509 | .probe = pci_device_probe, | 550 | .probe = pci_device_probe, |
510 | .remove = pci_device_remove, | 551 | .remove = pci_device_remove, |
511 | .suspend = pci_device_suspend, | 552 | .suspend = pci_device_suspend, |
512 | .shutdown = pci_device_shutdown, | 553 | .suspend_late = pci_device_suspend_late, |
554 | .resume_early = pci_device_resume_early, | ||
513 | .resume = pci_device_resume, | 555 | .resume = pci_device_resume, |
556 | .shutdown = pci_device_shutdown, | ||
514 | .dev_attrs = pci_dev_attrs, | 557 | .dev_attrs = pci_dev_attrs, |
515 | }; | 558 | }; |
516 | 559 | ||
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index fdefa7dcd156..a1d2e979b17f 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c | |||
@@ -117,6 +117,7 @@ is_enabled_store(struct device *dev, struct device_attribute *attr, | |||
117 | const char *buf, size_t count) | 117 | const char *buf, size_t count) |
118 | { | 118 | { |
119 | struct pci_dev *pdev = to_pci_dev(dev); | 119 | struct pci_dev *pdev = to_pci_dev(dev); |
120 | int retval = 0; | ||
120 | 121 | ||
121 | /* this can crash the machine when done on the "wrong" device */ | 122 | /* this can crash the machine when done on the "wrong" device */ |
122 | if (!capable(CAP_SYS_ADMIN)) | 123 | if (!capable(CAP_SYS_ADMIN)) |
@@ -126,11 +127,53 @@ is_enabled_store(struct device *dev, struct device_attribute *attr, | |||
126 | pci_disable_device(pdev); | 127 | pci_disable_device(pdev); |
127 | 128 | ||
128 | if (*buf == '1') | 129 | if (*buf == '1') |
129 | pci_enable_device(pdev); | 130 | retval = pci_enable_device(pdev); |
130 | 131 | ||
132 | if (retval) | ||
133 | return retval; | ||
131 | return count; | 134 | return count; |
132 | } | 135 | } |
133 | 136 | ||
137 | static ssize_t | ||
138 | msi_bus_show(struct device *dev, struct device_attribute *attr, char *buf) | ||
139 | { | ||
140 | struct pci_dev *pdev = to_pci_dev(dev); | ||
141 | |||
142 | if (!pdev->subordinate) | ||
143 | return 0; | ||
144 | |||
145 | return sprintf (buf, "%u\n", | ||
146 | !(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI)); | ||
147 | } | ||
148 | |||
149 | static ssize_t | ||
150 | msi_bus_store(struct device *dev, struct device_attribute *attr, | ||
151 | const char *buf, size_t count) | ||
152 | { | ||
153 | struct pci_dev *pdev = to_pci_dev(dev); | ||
154 | |||
155 | /* bad things may happen if the no_msi flag is changed | ||
156 | * while some drivers are loaded */ | ||
157 | if (!capable(CAP_SYS_ADMIN)) | ||
158 | return count; | ||
159 | |||
160 | if (!pdev->subordinate) | ||
161 | return count; | ||
162 | |||
163 | if (*buf == '0') { | ||
164 | pdev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI; | ||
165 | dev_warn(&pdev->dev, "forced subordinate bus to not support MSI," | ||
166 | " bad things could happen.\n"); | ||
167 | } | ||
168 | |||
169 | if (*buf == '1') { | ||
170 | pdev->subordinate->bus_flags &= ~PCI_BUS_FLAGS_NO_MSI; | ||
171 | dev_warn(&pdev->dev, "forced subordinate bus to support MSI," | ||
172 | " bad things could happen.\n"); | ||
173 | } | ||
174 | |||
175 | return count; | ||
176 | } | ||
134 | 177 | ||
135 | struct device_attribute pci_dev_attrs[] = { | 178 | struct device_attribute pci_dev_attrs[] = { |
136 | __ATTR_RO(resource), | 179 | __ATTR_RO(resource), |
@@ -145,6 +188,7 @@ struct device_attribute pci_dev_attrs[] = { | |||
145 | __ATTR(enable, 0600, is_enabled_show, is_enabled_store), | 188 | __ATTR(enable, 0600, is_enabled_show, is_enabled_store), |
146 | __ATTR(broken_parity_status,(S_IRUGO|S_IWUSR), | 189 | __ATTR(broken_parity_status,(S_IRUGO|S_IWUSR), |
147 | broken_parity_status_show,broken_parity_status_store), | 190 | broken_parity_status_show,broken_parity_status_store), |
191 | __ATTR(msi_bus, 0644, msi_bus_show, msi_bus_store), | ||
148 | __ATTR_NULL, | 192 | __ATTR_NULL, |
149 | }; | 193 | }; |
150 | 194 | ||
@@ -385,15 +429,38 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, | |||
385 | } | 429 | } |
386 | 430 | ||
387 | /** | 431 | /** |
432 | * pci_remove_resource_files - cleanup resource files | ||
433 | * @dev: dev to cleanup | ||
434 | * | ||
435 | * If we created resource files for @dev, remove them from sysfs and | ||
436 | * free their resources. | ||
437 | */ | ||
438 | static void | ||
439 | pci_remove_resource_files(struct pci_dev *pdev) | ||
440 | { | ||
441 | int i; | ||
442 | |||
443 | for (i = 0; i < PCI_ROM_RESOURCE; i++) { | ||
444 | struct bin_attribute *res_attr; | ||
445 | |||
446 | res_attr = pdev->res_attr[i]; | ||
447 | if (res_attr) { | ||
448 | sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); | ||
449 | kfree(res_attr); | ||
450 | } | ||
451 | } | ||
452 | } | ||
453 | |||
454 | /** | ||
388 | * pci_create_resource_files - create resource files in sysfs for @dev | 455 | * pci_create_resource_files - create resource files in sysfs for @dev |
389 | * @dev: dev in question | 456 | * @dev: dev in question |
390 | * | 457 | * |
391 | * Walk the resources in @dev creating files for each resource available. | 458 | * Walk the resources in @dev creating files for each resource available. |
392 | */ | 459 | */ |
393 | static void | 460 | static int pci_create_resource_files(struct pci_dev *pdev) |
394 | pci_create_resource_files(struct pci_dev *pdev) | ||
395 | { | 461 | { |
396 | int i; | 462 | int i; |
463 | int retval; | ||
397 | 464 | ||
398 | /* Expose the PCI resources from this device as files */ | 465 | /* Expose the PCI resources from this device as files */ |
399 | for (i = 0; i < PCI_ROM_RESOURCE; i++) { | 466 | for (i = 0; i < PCI_ROM_RESOURCE; i++) { |
@@ -416,35 +483,19 @@ pci_create_resource_files(struct pci_dev *pdev) | |||
416 | res_attr->size = pci_resource_len(pdev, i); | 483 | res_attr->size = pci_resource_len(pdev, i); |
417 | res_attr->mmap = pci_mmap_resource; | 484 | res_attr->mmap = pci_mmap_resource; |
418 | res_attr->private = &pdev->resource[i]; | 485 | res_attr->private = &pdev->resource[i]; |
419 | sysfs_create_bin_file(&pdev->dev.kobj, res_attr); | 486 | retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr); |
420 | } | 487 | if (retval) { |
421 | } | 488 | pci_remove_resource_files(pdev); |
422 | } | 489 | return retval; |
423 | 490 | } | |
424 | /** | 491 | } else { |
425 | * pci_remove_resource_files - cleanup resource files | 492 | return -ENOMEM; |
426 | * @dev: dev to cleanup | ||
427 | * | ||
428 | * If we created resource files for @dev, remove them from sysfs and | ||
429 | * free their resources. | ||
430 | */ | ||
431 | static void | ||
432 | pci_remove_resource_files(struct pci_dev *pdev) | ||
433 | { | ||
434 | int i; | ||
435 | |||
436 | for (i = 0; i < PCI_ROM_RESOURCE; i++) { | ||
437 | struct bin_attribute *res_attr; | ||
438 | |||
439 | res_attr = pdev->res_attr[i]; | ||
440 | if (res_attr) { | ||
441 | sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); | ||
442 | kfree(res_attr); | ||
443 | } | 493 | } |
444 | } | 494 | } |
495 | return 0; | ||
445 | } | 496 | } |
446 | #else /* !HAVE_PCI_MMAP */ | 497 | #else /* !HAVE_PCI_MMAP */ |
447 | static inline void pci_create_resource_files(struct pci_dev *dev) { return; } | 498 | static inline int pci_create_resource_files(struct pci_dev *dev) { return 0; } |
448 | static inline void pci_remove_resource_files(struct pci_dev *dev) { return; } | 499 | static inline void pci_remove_resource_files(struct pci_dev *dev) { return; } |
449 | #endif /* HAVE_PCI_MMAP */ | 500 | #endif /* HAVE_PCI_MMAP */ |
450 | 501 | ||
@@ -529,22 +580,27 @@ static struct bin_attribute pcie_config_attr = { | |||
529 | .write = pci_write_config, | 580 | .write = pci_write_config, |
530 | }; | 581 | }; |
531 | 582 | ||
532 | int pci_create_sysfs_dev_files (struct pci_dev *pdev) | 583 | int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) |
533 | { | 584 | { |
585 | struct bin_attribute *rom_attr = NULL; | ||
586 | int retval; | ||
587 | |||
534 | if (!sysfs_initialized) | 588 | if (!sysfs_initialized) |
535 | return -EACCES; | 589 | return -EACCES; |
536 | 590 | ||
537 | if (pdev->cfg_size < 4096) | 591 | if (pdev->cfg_size < 4096) |
538 | sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr); | 592 | retval = sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr); |
539 | else | 593 | else |
540 | sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr); | 594 | retval = sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr); |
595 | if (retval) | ||
596 | goto err; | ||
541 | 597 | ||
542 | pci_create_resource_files(pdev); | 598 | retval = pci_create_resource_files(pdev); |
599 | if (retval) | ||
600 | goto err_bin_file; | ||
543 | 601 | ||
544 | /* If the device has a ROM, try to expose it in sysfs. */ | 602 | /* If the device has a ROM, try to expose it in sysfs. */ |
545 | if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) { | 603 | if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) { |
546 | struct bin_attribute *rom_attr; | ||
547 | |||
548 | rom_attr = kzalloc(sizeof(*rom_attr), GFP_ATOMIC); | 604 | rom_attr = kzalloc(sizeof(*rom_attr), GFP_ATOMIC); |
549 | if (rom_attr) { | 605 | if (rom_attr) { |
550 | pdev->rom_attr = rom_attr; | 606 | pdev->rom_attr = rom_attr; |
@@ -554,13 +610,28 @@ int pci_create_sysfs_dev_files (struct pci_dev *pdev) | |||
554 | rom_attr->attr.owner = THIS_MODULE; | 610 | rom_attr->attr.owner = THIS_MODULE; |
555 | rom_attr->read = pci_read_rom; | 611 | rom_attr->read = pci_read_rom; |
556 | rom_attr->write = pci_write_rom; | 612 | rom_attr->write = pci_write_rom; |
557 | sysfs_create_bin_file(&pdev->dev.kobj, rom_attr); | 613 | retval = sysfs_create_bin_file(&pdev->dev.kobj, rom_attr); |
614 | if (retval) | ||
615 | goto err_rom; | ||
616 | } else { | ||
617 | retval = -ENOMEM; | ||
618 | goto err_bin_file; | ||
558 | } | 619 | } |
559 | } | 620 | } |
560 | /* add platform-specific attributes */ | 621 | /* add platform-specific attributes */ |
561 | pcibios_add_platform_entries(pdev); | 622 | pcibios_add_platform_entries(pdev); |
562 | 623 | ||
563 | return 0; | 624 | return 0; |
625 | |||
626 | err_rom: | ||
627 | kfree(rom_attr); | ||
628 | err_bin_file: | ||
629 | if (pdev->cfg_size < 4096) | ||
630 | sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr); | ||
631 | else | ||
632 | sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr); | ||
633 | err: | ||
634 | return retval; | ||
564 | } | 635 | } |
565 | 636 | ||
566 | /** | 637 | /** |
@@ -589,10 +660,14 @@ void pci_remove_sysfs_dev_files(struct pci_dev *pdev) | |||
589 | static int __init pci_sysfs_init(void) | 660 | static int __init pci_sysfs_init(void) |
590 | { | 661 | { |
591 | struct pci_dev *pdev = NULL; | 662 | struct pci_dev *pdev = NULL; |
592 | 663 | int retval; | |
664 | |||
593 | sysfs_initialized = 1; | 665 | sysfs_initialized = 1; |
594 | for_each_pci_dev(pdev) | 666 | for_each_pci_dev(pdev) { |
595 | pci_create_sysfs_dev_files(pdev); | 667 | retval = pci_create_sysfs_dev_files(pdev); |
668 | if (retval) | ||
669 | return retval; | ||
670 | } | ||
596 | 671 | ||
597 | return 0; | 672 | return 0; |
598 | } | 673 | } |
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 9f79dd6d51ab..a544997399b3 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
@@ -432,10 +432,12 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state) | |||
432 | case PM_EVENT_ON: | 432 | case PM_EVENT_ON: |
433 | return PCI_D0; | 433 | return PCI_D0; |
434 | case PM_EVENT_FREEZE: | 434 | case PM_EVENT_FREEZE: |
435 | case PM_EVENT_PRETHAW: | ||
436 | /* REVISIT both freeze and pre-thaw "should" use D0 */ | ||
435 | case PM_EVENT_SUSPEND: | 437 | case PM_EVENT_SUSPEND: |
436 | return PCI_D3hot; | 438 | return PCI_D3hot; |
437 | default: | 439 | default: |
438 | printk("They asked me for state %d\n", state.event); | 440 | printk("Unrecognized suspend event %d\n", state.event); |
439 | BUG(); | 441 | BUG(); |
440 | } | 442 | } |
441 | return PCI_D0; | 443 | return PCI_D0; |
@@ -443,6 +445,51 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state) | |||
443 | 445 | ||
444 | EXPORT_SYMBOL(pci_choose_state); | 446 | EXPORT_SYMBOL(pci_choose_state); |
445 | 447 | ||
448 | static int pci_save_pcie_state(struct pci_dev *dev) | ||
449 | { | ||
450 | int pos, i = 0; | ||
451 | struct pci_cap_saved_state *save_state; | ||
452 | u16 *cap; | ||
453 | |||
454 | pos = pci_find_capability(dev, PCI_CAP_ID_EXP); | ||
455 | if (pos <= 0) | ||
456 | return 0; | ||
457 | |||
458 | save_state = kzalloc(sizeof(*save_state) + sizeof(u16) * 4, GFP_KERNEL); | ||
459 | if (!save_state) { | ||
460 | dev_err(&dev->dev, "Out of memory in pci_save_pcie_state\n"); | ||
461 | return -ENOMEM; | ||
462 | } | ||
463 | cap = (u16 *)&save_state->data[0]; | ||
464 | |||
465 | pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &cap[i++]); | ||
466 | pci_read_config_word(dev, pos + PCI_EXP_LNKCTL, &cap[i++]); | ||
467 | pci_read_config_word(dev, pos + PCI_EXP_SLTCTL, &cap[i++]); | ||
468 | pci_read_config_word(dev, pos + PCI_EXP_RTCTL, &cap[i++]); | ||
469 | pci_add_saved_cap(dev, save_state); | ||
470 | return 0; | ||
471 | } | ||
472 | |||
473 | static void pci_restore_pcie_state(struct pci_dev *dev) | ||
474 | { | ||
475 | int i = 0, pos; | ||
476 | struct pci_cap_saved_state *save_state; | ||
477 | u16 *cap; | ||
478 | |||
479 | save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP); | ||
480 | pos = pci_find_capability(dev, PCI_CAP_ID_EXP); | ||
481 | if (!save_state || pos <= 0) | ||
482 | return; | ||
483 | cap = (u16 *)&save_state->data[0]; | ||
484 | |||
485 | pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, cap[i++]); | ||
486 | pci_write_config_word(dev, pos + PCI_EXP_LNKCTL, cap[i++]); | ||
487 | pci_write_config_word(dev, pos + PCI_EXP_SLTCTL, cap[i++]); | ||
488 | pci_write_config_word(dev, pos + PCI_EXP_RTCTL, cap[i++]); | ||
489 | pci_remove_saved_cap(save_state); | ||
490 | kfree(save_state); | ||
491 | } | ||
492 | |||
446 | /** | 493 | /** |
447 | * pci_save_state - save the PCI configuration space of a device before suspending | 494 | * pci_save_state - save the PCI configuration space of a device before suspending |
448 | * @dev: - PCI device that we're dealing with | 495 | * @dev: - PCI device that we're dealing with |
@@ -458,6 +505,8 @@ pci_save_state(struct pci_dev *dev) | |||
458 | return i; | 505 | return i; |
459 | if ((i = pci_save_msix_state(dev)) != 0) | 506 | if ((i = pci_save_msix_state(dev)) != 0) |
460 | return i; | 507 | return i; |
508 | if ((i = pci_save_pcie_state(dev)) != 0) | ||
509 | return i; | ||
461 | return 0; | 510 | return 0; |
462 | } | 511 | } |
463 | 512 | ||
@@ -471,6 +520,9 @@ pci_restore_state(struct pci_dev *dev) | |||
471 | int i; | 520 | int i; |
472 | int val; | 521 | int val; |
473 | 522 | ||
523 | /* PCI Express register must be restored first */ | ||
524 | pci_restore_pcie_state(dev); | ||
525 | |||
474 | /* | 526 | /* |
475 | * The Base Address register should be programmed before the command | 527 | * The Base Address register should be programmed before the command |
476 | * register(s) | 528 | * register(s) |
@@ -953,13 +1005,12 @@ static int __devinit pci_setup(char *str) | |||
953 | } | 1005 | } |
954 | str = k; | 1006 | str = k; |
955 | } | 1007 | } |
956 | return 1; | 1008 | return 0; |
957 | } | 1009 | } |
1010 | early_param("pci", pci_setup); | ||
958 | 1011 | ||
959 | device_initcall(pci_init); | 1012 | device_initcall(pci_init); |
960 | 1013 | ||
961 | __setup("pci=", pci_setup); | ||
962 | |||
963 | #if defined(CONFIG_ISA) || defined(CONFIG_EISA) | 1014 | #if defined(CONFIG_ISA) || defined(CONFIG_EISA) |
964 | /* FIXME: Some boxes have multiple ISA bridges! */ | 1015 | /* FIXME: Some boxes have multiple ISA bridges! */ |
965 | struct pci_dev *isa_bridge; | 1016 | struct pci_dev *isa_bridge; |
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 08d58fc78ee1..6bf327db5c5e 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h | |||
@@ -42,7 +42,7 @@ extern void pci_remove_legacy_files(struct pci_bus *bus); | |||
42 | /* Lock for read/write access to pci device and bus lists */ | 42 | /* Lock for read/write access to pci device and bus lists */ |
43 | extern struct rw_semaphore pci_bus_sem; | 43 | extern struct rw_semaphore pci_bus_sem; |
44 | 44 | ||
45 | #ifdef CONFIG_X86_IO_APIC | 45 | #ifdef CONFIG_PCI_MSI |
46 | extern int pci_msi_quirk; | 46 | extern int pci_msi_quirk; |
47 | #else | 47 | #else |
48 | #define pci_msi_quirk 0 | 48 | #define pci_msi_quirk 0 |
diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig index 1012db8b8b2c..0ad92a8ad8b1 100644 --- a/drivers/pci/pcie/Kconfig +++ b/drivers/pci/pcie/Kconfig | |||
@@ -34,3 +34,4 @@ config HOTPLUG_PCI_PCIE_POLL_EVENT_MODE | |||
34 | 34 | ||
35 | When in doubt, say N. | 35 | When in doubt, say N. |
36 | 36 | ||
37 | source "drivers/pci/pcie/aer/Kconfig" | ||
diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile index 984fa87283e3..e00fb99acf44 100644 --- a/drivers/pci/pcie/Makefile +++ b/drivers/pci/pcie/Makefile | |||
@@ -5,3 +5,6 @@ | |||
5 | pcieportdrv-y := portdrv_core.o portdrv_pci.o portdrv_bus.o | 5 | pcieportdrv-y := portdrv_core.o portdrv_pci.o portdrv_bus.o |
6 | 6 | ||
7 | obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o | 7 | obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o |
8 | |||
9 | # Build PCI Express AER if needed | ||
10 | obj-$(CONFIG_PCIEAER) += aer/ | ||
diff --git a/drivers/pci/pcie/aer/Kconfig b/drivers/pci/pcie/aer/Kconfig new file mode 100644 index 000000000000..3f37a60a6438 --- /dev/null +++ b/drivers/pci/pcie/aer/Kconfig | |||
@@ -0,0 +1,12 @@ | |||
1 | # | ||
2 | # PCI Express Root Port Device AER Configuration | ||
3 | # | ||
4 | |||
5 | config PCIEAER | ||
6 | boolean "Root Port Advanced Error Reporting support" | ||
7 | depends on PCIEPORTBUS && ACPI | ||
8 | default y | ||
9 | help | ||
10 | This enables PCI Express Root Port Advanced Error Reporting | ||
11 | (AER) driver support. Error reporting messages sent to Root | ||
12 | Port will be handled by PCI Express AER driver. | ||
diff --git a/drivers/pci/pcie/aer/Makefile b/drivers/pci/pcie/aer/Makefile new file mode 100644 index 000000000000..15a4f40d520b --- /dev/null +++ b/drivers/pci/pcie/aer/Makefile | |||
@@ -0,0 +1,8 @@ | |||
1 | # | ||
2 | # Makefile for PCI-Express Root Port Advanced Error Reporting Driver | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_PCIEAER) += aerdriver.o | ||
6 | |||
7 | aerdriver-objs := aerdrv_errprint.o aerdrv_core.o aerdrv.o aerdrv_acpi.o | ||
8 | |||
diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c new file mode 100644 index 000000000000..0d4ac027d53e --- /dev/null +++ b/drivers/pci/pcie/aer/aerdrv.c | |||
@@ -0,0 +1,346 @@ | |||
1 | /* | ||
2 | * drivers/pci/pcie/aer/aerdrv.c | ||
3 | * | ||
4 | * This file is subject to the terms and conditions of the GNU General Public | ||
5 | * License. See the file "COPYING" in the main directory of this archive | ||
6 | * for more details. | ||
7 | * | ||
8 | * This file implements the AER root port service driver. The driver will | ||
9 | * register an irq handler. When root port triggers an AER interrupt, the irq | ||
10 | * handler will collect root port status and schedule a work. | ||
11 | * | ||
12 | * Copyright (C) 2006 Intel Corp. | ||
13 | * Tom Long Nguyen (tom.l.nguyen@intel.com) | ||
14 | * Zhang Yanmin (yanmin.zhang@intel.com) | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include <linux/module.h> | ||
19 | #include <linux/pci.h> | ||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/errno.h> | ||
22 | #include <linux/pm.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/pcieport_if.h> | ||
27 | |||
28 | #include "aerdrv.h" | ||
29 | |||
30 | /* | ||
31 | * Version Information | ||
32 | */ | ||
33 | #define DRIVER_VERSION "v1.0" | ||
34 | #define DRIVER_AUTHOR "tom.l.nguyen@intel.com" | ||
35 | #define DRIVER_DESC "Root Port Advanced Error Reporting Driver" | ||
36 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
37 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
38 | MODULE_LICENSE("GPL"); | ||
39 | |||
40 | static int __devinit aer_probe (struct pcie_device *dev, | ||
41 | const struct pcie_port_service_id *id ); | ||
42 | static void aer_remove(struct pcie_device *dev); | ||
43 | static int aer_suspend(struct pcie_device *dev, pm_message_t state) | ||
44 | {return 0;} | ||
45 | static int aer_resume(struct pcie_device *dev) {return 0;} | ||
46 | static pci_ers_result_t aer_error_detected(struct pci_dev *dev, | ||
47 | enum pci_channel_state error); | ||
48 | static void aer_error_resume(struct pci_dev *dev); | ||
49 | static pci_ers_result_t aer_root_reset(struct pci_dev *dev); | ||
50 | |||
51 | /* | ||
52 | * PCI Express bus's AER Root service driver data structure | ||
53 | */ | ||
54 | static struct pcie_port_service_id aer_id[] = { | ||
55 | { | ||
56 | .vendor = PCI_ANY_ID, | ||
57 | .device = PCI_ANY_ID, | ||
58 | .port_type = PCIE_RC_PORT, | ||
59 | .service_type = PCIE_PORT_SERVICE_AER, | ||
60 | }, | ||
61 | { /* end: all zeroes */ } | ||
62 | }; | ||
63 | |||
64 | static struct pci_error_handlers aer_error_handlers = { | ||
65 | .error_detected = aer_error_detected, | ||
66 | .resume = aer_error_resume, | ||
67 | }; | ||
68 | |||
69 | static struct pcie_port_service_driver aerdrv = { | ||
70 | .name = "aer", | ||
71 | .id_table = &aer_id[0], | ||
72 | |||
73 | .probe = aer_probe, | ||
74 | .remove = aer_remove, | ||
75 | |||
76 | .suspend = aer_suspend, | ||
77 | .resume = aer_resume, | ||
78 | |||
79 | .err_handler = &aer_error_handlers, | ||
80 | |||
81 | .reset_link = aer_root_reset, | ||
82 | }; | ||
83 | |||
84 | /** | ||
85 | * aer_irq - Root Port's ISR | ||
86 | * @irq: IRQ assigned to Root Port | ||
87 | * @context: pointer to Root Port data structure | ||
88 | * @r: pointer struct pt_regs | ||
89 | * | ||
90 | * Invoked when Root Port detects AER messages. | ||
91 | **/ | ||
92 | static irqreturn_t aer_irq(int irq, void *context, struct pt_regs * r) | ||
93 | { | ||
94 | unsigned int status, id; | ||
95 | struct pcie_device *pdev = (struct pcie_device *)context; | ||
96 | struct aer_rpc *rpc = get_service_data(pdev); | ||
97 | int next_prod_idx; | ||
98 | unsigned long flags; | ||
99 | int pos; | ||
100 | |||
101 | pos = pci_find_aer_capability(pdev->port); | ||
102 | /* | ||
103 | * Must lock access to Root Error Status Reg, Root Error ID Reg, | ||
104 | * and Root error producer/consumer index | ||
105 | */ | ||
106 | spin_lock_irqsave(&rpc->e_lock, flags); | ||
107 | |||
108 | /* Read error status */ | ||
109 | pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, &status); | ||
110 | if (!(status & ROOT_ERR_STATUS_MASKS)) { | ||
111 | spin_unlock_irqrestore(&rpc->e_lock, flags); | ||
112 | return IRQ_NONE; | ||
113 | } | ||
114 | |||
115 | /* Read error source and clear error status */ | ||
116 | pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_COR_SRC, &id); | ||
117 | pci_write_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, status); | ||
118 | |||
119 | /* Store error source for later DPC handler */ | ||
120 | next_prod_idx = rpc->prod_idx + 1; | ||
121 | if (next_prod_idx == AER_ERROR_SOURCES_MAX) | ||
122 | next_prod_idx = 0; | ||
123 | if (next_prod_idx == rpc->cons_idx) { | ||
124 | /* | ||
125 | * Error Storm Condition - possibly the same error occurred. | ||
126 | * Drop the error. | ||
127 | */ | ||
128 | spin_unlock_irqrestore(&rpc->e_lock, flags); | ||
129 | return IRQ_HANDLED; | ||
130 | } | ||
131 | rpc->e_sources[rpc->prod_idx].status = status; | ||
132 | rpc->e_sources[rpc->prod_idx].id = id; | ||
133 | rpc->prod_idx = next_prod_idx; | ||
134 | spin_unlock_irqrestore(&rpc->e_lock, flags); | ||
135 | |||
136 | /* Invoke DPC handler */ | ||
137 | schedule_work(&rpc->dpc_handler); | ||
138 | |||
139 | return IRQ_HANDLED; | ||
140 | } | ||
141 | |||
142 | /** | ||
143 | * aer_alloc_rpc - allocate Root Port data structure | ||
144 | * @dev: pointer to the pcie_dev data structure | ||
145 | * | ||
146 | * Invoked when Root Port's AER service is loaded. | ||
147 | **/ | ||
148 | static struct aer_rpc* aer_alloc_rpc(struct pcie_device *dev) | ||
149 | { | ||
150 | struct aer_rpc *rpc; | ||
151 | |||
152 | if (!(rpc = (struct aer_rpc *)kmalloc(sizeof(struct aer_rpc), | ||
153 | GFP_KERNEL))) | ||
154 | return NULL; | ||
155 | |||
156 | memset(rpc, 0, sizeof(struct aer_rpc)); | ||
157 | /* | ||
158 | * Initialize Root lock access, e_lock, to Root Error Status Reg, | ||
159 | * Root Error ID Reg, and Root error producer/consumer index. | ||
160 | */ | ||
161 | rpc->e_lock = SPIN_LOCK_UNLOCKED; | ||
162 | |||
163 | rpc->rpd = dev; | ||
164 | INIT_WORK(&rpc->dpc_handler, aer_isr, (void *)dev); | ||
165 | rpc->prod_idx = rpc->cons_idx = 0; | ||
166 | mutex_init(&rpc->rpc_mutex); | ||
167 | init_waitqueue_head(&rpc->wait_release); | ||
168 | |||
169 | /* Use PCIE bus function to store rpc into PCIE device */ | ||
170 | set_service_data(dev, rpc); | ||
171 | |||
172 | return rpc; | ||
173 | } | ||
174 | |||
175 | /** | ||
176 | * aer_remove - clean up resources | ||
177 | * @dev: pointer to the pcie_dev data structure | ||
178 | * | ||
179 | * Invoked when PCI Express bus unloads or AER probe fails. | ||
180 | **/ | ||
181 | static void aer_remove(struct pcie_device *dev) | ||
182 | { | ||
183 | struct aer_rpc *rpc = get_service_data(dev); | ||
184 | |||
185 | if (rpc) { | ||
186 | /* If register interrupt service, it must be free. */ | ||
187 | if (rpc->isr) | ||
188 | free_irq(dev->irq, dev); | ||
189 | |||
190 | wait_event(rpc->wait_release, rpc->prod_idx == rpc->cons_idx); | ||
191 | |||
192 | aer_delete_rootport(rpc); | ||
193 | set_service_data(dev, NULL); | ||
194 | } | ||
195 | } | ||
196 | |||
197 | /** | ||
198 | * aer_probe - initialize resources | ||
199 | * @dev: pointer to the pcie_dev data structure | ||
200 | * @id: pointer to the service id data structure | ||
201 | * | ||
202 | * Invoked when PCI Express bus loads AER service driver. | ||
203 | **/ | ||
204 | static int __devinit aer_probe (struct pcie_device *dev, | ||
205 | const struct pcie_port_service_id *id ) | ||
206 | { | ||
207 | int status; | ||
208 | struct aer_rpc *rpc; | ||
209 | struct device *device = &dev->device; | ||
210 | |||
211 | /* Init */ | ||
212 | if ((status = aer_init(dev))) | ||
213 | return status; | ||
214 | |||
215 | /* Alloc rpc data structure */ | ||
216 | if (!(rpc = aer_alloc_rpc(dev))) { | ||
217 | printk(KERN_DEBUG "%s: Alloc rpc fails on PCIE device[%s]\n", | ||
218 | __FUNCTION__, device->bus_id); | ||
219 | aer_remove(dev); | ||
220 | return -ENOMEM; | ||
221 | } | ||
222 | |||
223 | /* Request IRQ ISR */ | ||
224 | if ((status = request_irq(dev->irq, aer_irq, SA_SHIRQ, "aerdrv", | ||
225 | dev))) { | ||
226 | printk(KERN_DEBUG "%s: Request ISR fails on PCIE device[%s]\n", | ||
227 | __FUNCTION__, device->bus_id); | ||
228 | aer_remove(dev); | ||
229 | return status; | ||
230 | } | ||
231 | |||
232 | rpc->isr = 1; | ||
233 | |||
234 | aer_enable_rootport(rpc); | ||
235 | |||
236 | return status; | ||
237 | } | ||
238 | |||
239 | /** | ||
240 | * aer_root_reset - reset link on Root Port | ||
241 | * @dev: pointer to Root Port's pci_dev data structure | ||
242 | * | ||
243 | * Invoked by Port Bus driver when performing link reset at Root Port. | ||
244 | **/ | ||
245 | static pci_ers_result_t aer_root_reset(struct pci_dev *dev) | ||
246 | { | ||
247 | u16 p2p_ctrl; | ||
248 | u32 status; | ||
249 | int pos; | ||
250 | |||
251 | pos = pci_find_aer_capability(dev); | ||
252 | |||
253 | /* Disable Root's interrupt in response to error messages */ | ||
254 | pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, 0); | ||
255 | |||
256 | /* Assert Secondary Bus Reset */ | ||
257 | pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &p2p_ctrl); | ||
258 | p2p_ctrl |= PCI_CB_BRIDGE_CTL_CB_RESET; | ||
259 | pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl); | ||
260 | |||
261 | /* De-assert Secondary Bus Reset */ | ||
262 | p2p_ctrl &= ~PCI_CB_BRIDGE_CTL_CB_RESET; | ||
263 | pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl); | ||
264 | |||
265 | /* | ||
266 | * System software must wait for at least 100ms from the end | ||
267 | * of a reset of one or more device before it is permitted | ||
268 | * to issue Configuration Requests to those devices. | ||
269 | */ | ||
270 | msleep(200); | ||
271 | printk(KERN_DEBUG "Complete link reset at Root[%s]\n", dev->dev.bus_id); | ||
272 | |||
273 | /* Enable Root Port's interrupt in response to error messages */ | ||
274 | pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &status); | ||
275 | pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, status); | ||
276 | pci_write_config_dword(dev, | ||
277 | pos + PCI_ERR_ROOT_COMMAND, | ||
278 | ROOT_PORT_INTR_ON_MESG_MASK); | ||
279 | |||
280 | return PCI_ERS_RESULT_RECOVERED; | ||
281 | } | ||
282 | |||
283 | /** | ||
284 | * aer_error_detected - update severity status | ||
285 | * @dev: pointer to Root Port's pci_dev data structure | ||
286 | * @error: error severity being notified by port bus | ||
287 | * | ||
288 | * Invoked by Port Bus driver during error recovery. | ||
289 | **/ | ||
290 | static pci_ers_result_t aer_error_detected(struct pci_dev *dev, | ||
291 | enum pci_channel_state error) | ||
292 | { | ||
293 | /* Root Port has no impact. Always recovers. */ | ||
294 | return PCI_ERS_RESULT_CAN_RECOVER; | ||
295 | } | ||
296 | |||
297 | /** | ||
298 | * aer_error_resume - clean up corresponding error status bits | ||
299 | * @dev: pointer to Root Port's pci_dev data structure | ||
300 | * | ||
301 | * Invoked by Port Bus driver during nonfatal recovery. | ||
302 | **/ | ||
303 | static void aer_error_resume(struct pci_dev *dev) | ||
304 | { | ||
305 | int pos; | ||
306 | u32 status, mask; | ||
307 | u16 reg16; | ||
308 | |||
309 | /* Clean up Root device status */ | ||
310 | pos = pci_find_capability(dev, PCI_CAP_ID_EXP); | ||
311 | pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, ®16); | ||
312 | pci_write_config_word(dev, pos + PCI_EXP_DEVSTA, reg16); | ||
313 | |||
314 | /* Clean AER Root Error Status */ | ||
315 | pos = pci_find_aer_capability(dev); | ||
316 | pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); | ||
317 | pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask); | ||
318 | if (dev->error_state == pci_channel_io_normal) | ||
319 | status &= ~mask; /* Clear corresponding nonfatal bits */ | ||
320 | else | ||
321 | status &= mask; /* Clear corresponding fatal bits */ | ||
322 | pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status); | ||
323 | } | ||
324 | |||
325 | /** | ||
326 | * aer_service_init - register AER root service driver | ||
327 | * | ||
328 | * Invoked when AER root service driver is loaded. | ||
329 | **/ | ||
330 | static int __init aer_service_init(void) | ||
331 | { | ||
332 | return pcie_port_service_register(&aerdrv); | ||
333 | } | ||
334 | |||
335 | /** | ||
336 | * aer_service_exit - unregister AER root service driver | ||
337 | * | ||
338 | * Invoked when AER root service driver is unloaded. | ||
339 | **/ | ||
340 | static void __exit aer_service_exit(void) | ||
341 | { | ||
342 | pcie_port_service_unregister(&aerdrv); | ||
343 | } | ||
344 | |||
345 | module_init(aer_service_init); | ||
346 | module_exit(aer_service_exit); | ||
diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h new file mode 100644 index 000000000000..daf0cad88fc8 --- /dev/null +++ b/drivers/pci/pcie/aer/aerdrv.h | |||
@@ -0,0 +1,125 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2006 Intel Corp. | ||
3 | * Tom Long Nguyen (tom.l.nguyen@intel.com) | ||
4 | * Zhang Yanmin (yanmin.zhang@intel.com) | ||
5 | * | ||
6 | */ | ||
7 | |||
8 | #ifndef _AERDRV_H_ | ||
9 | #define _AERDRV_H_ | ||
10 | |||
11 | #include <linux/pcieport_if.h> | ||
12 | #include <linux/aer.h> | ||
13 | |||
14 | #define AER_NONFATAL 0 | ||
15 | #define AER_FATAL 1 | ||
16 | #define AER_CORRECTABLE 2 | ||
17 | #define AER_UNCORRECTABLE 4 | ||
18 | #define AER_ERROR_MASK 0x001fffff | ||
19 | #define AER_ERROR(d) (d & AER_ERROR_MASK) | ||
20 | |||
21 | #define OSC_METHOD_RUN_SUCCESS 0 | ||
22 | #define OSC_METHOD_NOT_SUPPORTED 1 | ||
23 | #define OSC_METHOD_RUN_FAILURE 2 | ||
24 | |||
25 | /* Root Error Status Register Bits */ | ||
26 | #define ROOT_ERR_STATUS_MASKS 0x0f | ||
27 | |||
28 | #define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \ | ||
29 | PCI_EXP_RTCTL_SENFEE| \ | ||
30 | PCI_EXP_RTCTL_SEFEE) | ||
31 | #define ROOT_PORT_INTR_ON_MESG_MASK (PCI_ERR_ROOT_CMD_COR_EN| \ | ||
32 | PCI_ERR_ROOT_CMD_NONFATAL_EN| \ | ||
33 | PCI_ERR_ROOT_CMD_FATAL_EN) | ||
34 | #define ERR_COR_ID(d) (d & 0xffff) | ||
35 | #define ERR_UNCOR_ID(d) (d >> 16) | ||
36 | |||
37 | #define AER_SUCCESS 0 | ||
38 | #define AER_UNSUCCESS 1 | ||
39 | #define AER_ERROR_SOURCES_MAX 100 | ||
40 | |||
41 | #define AER_LOG_TLP_MASKS (PCI_ERR_UNC_POISON_TLP| \ | ||
42 | PCI_ERR_UNC_ECRC| \ | ||
43 | PCI_ERR_UNC_UNSUP| \ | ||
44 | PCI_ERR_UNC_COMP_ABORT| \ | ||
45 | PCI_ERR_UNC_UNX_COMP| \ | ||
46 | PCI_ERR_UNC_MALF_TLP) | ||
47 | |||
48 | /* AER Error Info Flags */ | ||
49 | #define AER_TLP_HEADER_VALID_FLAG 0x00000001 | ||
50 | #define AER_MULTI_ERROR_VALID_FLAG 0x00000002 | ||
51 | |||
52 | #define ERR_CORRECTABLE_ERROR_MASK 0x000031c1 | ||
53 | #define ERR_UNCORRECTABLE_ERROR_MASK 0x001ff010 | ||
54 | |||
55 | struct header_log_regs { | ||
56 | unsigned int dw0; | ||
57 | unsigned int dw1; | ||
58 | unsigned int dw2; | ||
59 | unsigned int dw3; | ||
60 | }; | ||
61 | |||
62 | struct aer_err_info { | ||
63 | int severity; /* 0:NONFATAL | 1:FATAL | 2:COR */ | ||
64 | int flags; | ||
65 | unsigned int status; /* COR/UNCOR Error Status */ | ||
66 | struct header_log_regs tlp; /* TLP Header */ | ||
67 | }; | ||
68 | |||
69 | struct aer_err_source { | ||
70 | unsigned int status; | ||
71 | unsigned int id; | ||
72 | }; | ||
73 | |||
74 | struct aer_rpc { | ||
75 | struct pcie_device *rpd; /* Root Port device */ | ||
76 | struct work_struct dpc_handler; | ||
77 | struct aer_err_source e_sources[AER_ERROR_SOURCES_MAX]; | ||
78 | unsigned short prod_idx; /* Error Producer Index */ | ||
79 | unsigned short cons_idx; /* Error Consumer Index */ | ||
80 | int isr; | ||
81 | spinlock_t e_lock; /* | ||
82 | * Lock access to Error Status/ID Regs | ||
83 | * and error producer/consumer index | ||
84 | */ | ||
85 | struct mutex rpc_mutex; /* | ||
86 | * only one thread could do | ||
87 | * recovery on the same | ||
88 | * root port hierachy | ||
89 | */ | ||
90 | wait_queue_head_t wait_release; | ||
91 | }; | ||
92 | |||
93 | struct aer_broadcast_data { | ||
94 | enum pci_channel_state state; | ||
95 | enum pci_ers_result result; | ||
96 | }; | ||
97 | |||
98 | static inline pci_ers_result_t merge_result(enum pci_ers_result orig, | ||
99 | enum pci_ers_result new) | ||
100 | { | ||
101 | switch (orig) { | ||
102 | case PCI_ERS_RESULT_CAN_RECOVER: | ||
103 | case PCI_ERS_RESULT_RECOVERED: | ||
104 | orig = new; | ||
105 | break; | ||
106 | case PCI_ERS_RESULT_DISCONNECT: | ||
107 | if (new == PCI_ERS_RESULT_NEED_RESET) | ||
108 | orig = new; | ||
109 | break; | ||
110 | default: | ||
111 | break; | ||
112 | } | ||
113 | |||
114 | return orig; | ||
115 | } | ||
116 | |||
117 | extern struct bus_type pcie_port_bus_type; | ||
118 | extern void aer_enable_rootport(struct aer_rpc *rpc); | ||
119 | extern void aer_delete_rootport(struct aer_rpc *rpc); | ||
120 | extern int aer_init(struct pcie_device *dev); | ||
121 | extern void aer_isr(void *context); | ||
122 | extern void aer_print_error(struct pci_dev *dev, struct aer_err_info *info); | ||
123 | extern int aer_osc_setup(struct pci_dev *dev); | ||
124 | |||
125 | #endif //_AERDRV_H_ | ||
diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c new file mode 100644 index 000000000000..fa68e89ebec9 --- /dev/null +++ b/drivers/pci/pcie/aer/aerdrv_acpi.c | |||
@@ -0,0 +1,68 @@ | |||
1 | /* | ||
2 | * Access ACPI _OSC method | ||
3 | * | ||
4 | * Copyright (C) 2006 Intel Corp. | ||
5 | * Tom Long Nguyen (tom.l.nguyen@intel.com) | ||
6 | * Zhang Yanmin (yanmin.zhang@intel.com) | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | #include <linux/module.h> | ||
11 | #include <linux/pci.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/errno.h> | ||
14 | #include <linux/pm.h> | ||
15 | #include <linux/suspend.h> | ||
16 | #include <linux/acpi.h> | ||
17 | #include <linux/pci-acpi.h> | ||
18 | #include <linux/delay.h> | ||
19 | #include "aerdrv.h" | ||
20 | |||
21 | /** | ||
22 | * aer_osc_setup - run ACPI _OSC method | ||
23 | * | ||
24 | * Return: | ||
25 | * Zero if success. Nonzero for otherwise. | ||
26 | * | ||
27 | * Invoked when PCIE bus loads AER service driver. To avoid conflict with | ||
28 | * BIOS AER support requires BIOS to yield AER control to OS native driver. | ||
29 | **/ | ||
30 | int aer_osc_setup(struct pci_dev *dev) | ||
31 | { | ||
32 | int retval = OSC_METHOD_RUN_SUCCESS; | ||
33 | acpi_status status; | ||
34 | acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev); | ||
35 | struct pci_dev *pdev = dev; | ||
36 | struct pci_bus *parent; | ||
37 | |||
38 | while (!handle) { | ||
39 | if (!pdev || !pdev->bus->parent) | ||
40 | break; | ||
41 | parent = pdev->bus->parent; | ||
42 | if (!parent->self) | ||
43 | /* Parent must be a host bridge */ | ||
44 | handle = acpi_get_pci_rootbridge_handle( | ||
45 | pci_domain_nr(parent), | ||
46 | parent->number); | ||
47 | else | ||
48 | handle = DEVICE_ACPI_HANDLE( | ||
49 | &(parent->self->dev)); | ||
50 | pdev = parent->self; | ||
51 | } | ||
52 | |||
53 | if (!handle) | ||
54 | return OSC_METHOD_NOT_SUPPORTED; | ||
55 | |||
56 | pci_osc_support_set(OSC_EXT_PCI_CONFIG_SUPPORT); | ||
57 | status = pci_osc_control_set(handle, OSC_PCI_EXPRESS_AER_CONTROL | | ||
58 | OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL); | ||
59 | if (ACPI_FAILURE(status)) { | ||
60 | if (status == AE_SUPPORT) | ||
61 | retval = OSC_METHOD_NOT_SUPPORTED; | ||
62 | else | ||
63 | retval = OSC_METHOD_RUN_FAILURE; | ||
64 | } | ||
65 | |||
66 | return retval; | ||
67 | } | ||
68 | |||
diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c new file mode 100644 index 000000000000..1c7e660d6535 --- /dev/null +++ b/drivers/pci/pcie/aer/aerdrv_core.c | |||
@@ -0,0 +1,758 @@ | |||
1 | /* | ||
2 | * drivers/pci/pcie/aer/aerdrv_core.c | ||
3 | * | ||
4 | * This file is subject to the terms and conditions of the GNU General Public | ||
5 | * License. See the file "COPYING" in the main directory of this archive | ||
6 | * for more details. | ||
7 | * | ||
8 | * This file implements the core part of PCI-Express AER. When an pci-express | ||
9 | * error is delivered, an error message will be collected and printed to | ||
10 | * console, then, an error recovery procedure will be executed by following | ||
11 | * the pci error recovery rules. | ||
12 | * | ||
13 | * Copyright (C) 2006 Intel Corp. | ||
14 | * Tom Long Nguyen (tom.l.nguyen@intel.com) | ||
15 | * Zhang Yanmin (yanmin.zhang@intel.com) | ||
16 | * | ||
17 | */ | ||
18 | |||
19 | #include <linux/module.h> | ||
20 | #include <linux/pci.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/errno.h> | ||
23 | #include <linux/pm.h> | ||
24 | #include <linux/suspend.h> | ||
25 | #include <linux/acpi.h> | ||
26 | #include <linux/pci-acpi.h> | ||
27 | #include <linux/delay.h> | ||
28 | #include "aerdrv.h" | ||
29 | |||
30 | static int forceload; | ||
31 | module_param(forceload, bool, 0); | ||
32 | |||
33 | #define PCI_CFG_SPACE_SIZE (0x100) | ||
34 | int pci_find_aer_capability(struct pci_dev *dev) | ||
35 | { | ||
36 | int pos; | ||
37 | u32 reg32 = 0; | ||
38 | |||
39 | /* Check if it's a pci-express device */ | ||
40 | pos = pci_find_capability(dev, PCI_CAP_ID_EXP); | ||
41 | if (!pos) | ||
42 | return 0; | ||
43 | |||
44 | /* Check if it supports pci-express AER */ | ||
45 | pos = PCI_CFG_SPACE_SIZE; | ||
46 | while (pos) { | ||
47 | if (pci_read_config_dword(dev, pos, ®32)) | ||
48 | return 0; | ||
49 | |||
50 | /* some broken boards return ~0 */ | ||
51 | if (reg32 == 0xffffffff) | ||
52 | return 0; | ||
53 | |||
54 | if (PCI_EXT_CAP_ID(reg32) == PCI_EXT_CAP_ID_ERR) | ||
55 | break; | ||
56 | |||
57 | pos = reg32 >> 20; | ||
58 | } | ||
59 | |||
60 | return pos; | ||
61 | } | ||
62 | |||
63 | int pci_enable_pcie_error_reporting(struct pci_dev *dev) | ||
64 | { | ||
65 | u16 reg16 = 0; | ||
66 | int pos; | ||
67 | |||
68 | pos = pci_find_capability(dev, PCI_CAP_ID_EXP); | ||
69 | if (!pos) | ||
70 | return -EIO; | ||
71 | |||
72 | pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, ®16); | ||
73 | reg16 = reg16 | | ||
74 | PCI_EXP_DEVCTL_CERE | | ||
75 | PCI_EXP_DEVCTL_NFERE | | ||
76 | PCI_EXP_DEVCTL_FERE | | ||
77 | PCI_EXP_DEVCTL_URRE; | ||
78 | pci_write_config_word(dev, pos+PCI_EXP_DEVCTL, | ||
79 | reg16); | ||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | int pci_disable_pcie_error_reporting(struct pci_dev *dev) | ||
84 | { | ||
85 | u16 reg16 = 0; | ||
86 | int pos; | ||
87 | |||
88 | pos = pci_find_capability(dev, PCI_CAP_ID_EXP); | ||
89 | if (!pos) | ||
90 | return -EIO; | ||
91 | |||
92 | pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, ®16); | ||
93 | reg16 = reg16 & ~(PCI_EXP_DEVCTL_CERE | | ||
94 | PCI_EXP_DEVCTL_NFERE | | ||
95 | PCI_EXP_DEVCTL_FERE | | ||
96 | PCI_EXP_DEVCTL_URRE); | ||
97 | pci_write_config_word(dev, pos+PCI_EXP_DEVCTL, | ||
98 | reg16); | ||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev) | ||
103 | { | ||
104 | int pos; | ||
105 | u32 status, mask; | ||
106 | |||
107 | pos = pci_find_aer_capability(dev); | ||
108 | if (!pos) | ||
109 | return -EIO; | ||
110 | |||
111 | pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); | ||
112 | pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask); | ||
113 | if (dev->error_state == pci_channel_io_normal) | ||
114 | status &= ~mask; /* Clear corresponding nonfatal bits */ | ||
115 | else | ||
116 | status &= mask; /* Clear corresponding fatal bits */ | ||
117 | pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status); | ||
118 | |||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | static int find_device_iter(struct device *device, void *data) | ||
123 | { | ||
124 | struct pci_dev *dev; | ||
125 | u16 id = *(unsigned long *)data; | ||
126 | u8 secondary, subordinate, d_bus = id >> 8; | ||
127 | |||
128 | if (device->bus == &pci_bus_type) { | ||
129 | dev = to_pci_dev(device); | ||
130 | if (id == ((dev->bus->number << 8) | dev->devfn)) { | ||
131 | /* | ||
132 | * Device ID match | ||
133 | */ | ||
134 | *(unsigned long*)data = (unsigned long)device; | ||
135 | return 1; | ||
136 | } | ||
137 | |||
138 | /* | ||
139 | * If device is P2P, check if it is an upstream? | ||
140 | */ | ||
141 | if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) { | ||
142 | pci_read_config_byte(dev, PCI_SECONDARY_BUS, | ||
143 | &secondary); | ||
144 | pci_read_config_byte(dev, PCI_SUBORDINATE_BUS, | ||
145 | &subordinate); | ||
146 | if (d_bus >= secondary && d_bus <= subordinate) { | ||
147 | *(unsigned long*)data = (unsigned long)device; | ||
148 | return 1; | ||
149 | } | ||
150 | } | ||
151 | } | ||
152 | |||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | /** | ||
157 | * find_source_device - search through device hierarchy for source device | ||
158 | * @p_dev: pointer to Root Port pci_dev data structure | ||
159 | * @id: device ID of agent who sends an error message to this Root Port | ||
160 | * | ||
161 | * Invoked when error is detected at the Root Port. | ||
162 | **/ | ||
163 | static struct device* find_source_device(struct pci_dev *parent, u16 id) | ||
164 | { | ||
165 | struct pci_dev *dev = parent; | ||
166 | struct device *device; | ||
167 | unsigned long device_addr; | ||
168 | int status; | ||
169 | |||
170 | /* Is Root Port an agent that sends error message? */ | ||
171 | if (id == ((dev->bus->number << 8) | dev->devfn)) | ||
172 | return &dev->dev; | ||
173 | |||
174 | do { | ||
175 | device_addr = id; | ||
176 | if ((status = device_for_each_child(&dev->dev, | ||
177 | &device_addr, find_device_iter))) { | ||
178 | device = (struct device*)device_addr; | ||
179 | dev = to_pci_dev(device); | ||
180 | if (id == ((dev->bus->number << 8) | dev->devfn)) | ||
181 | return device; | ||
182 | } | ||
183 | }while (status); | ||
184 | |||
185 | return NULL; | ||
186 | } | ||
187 | |||
188 | static void report_error_detected(struct pci_dev *dev, void *data) | ||
189 | { | ||
190 | pci_ers_result_t vote; | ||
191 | struct pci_error_handlers *err_handler; | ||
192 | struct aer_broadcast_data *result_data; | ||
193 | result_data = (struct aer_broadcast_data *) data; | ||
194 | |||
195 | dev->error_state = result_data->state; | ||
196 | |||
197 | if (!dev->driver || | ||
198 | !dev->driver->err_handler || | ||
199 | !dev->driver->err_handler->error_detected) { | ||
200 | if (result_data->state == pci_channel_io_frozen && | ||
201 | !(dev->hdr_type & PCI_HEADER_TYPE_BRIDGE)) { | ||
202 | /* | ||
203 | * In case of fatal recovery, if one of down- | ||
204 | * stream device has no driver. We might be | ||
205 | * unable to recover because a later insmod | ||
206 | * of a driver for this device is unaware of | ||
207 | * its hw state. | ||
208 | */ | ||
209 | printk(KERN_DEBUG "Device ID[%s] has %s\n", | ||
210 | dev->dev.bus_id, (dev->driver) ? | ||
211 | "no AER-aware driver" : "no driver"); | ||
212 | } | ||
213 | return; | ||
214 | } | ||
215 | |||
216 | err_handler = dev->driver->err_handler; | ||
217 | vote = err_handler->error_detected(dev, result_data->state); | ||
218 | result_data->result = merge_result(result_data->result, vote); | ||
219 | return; | ||
220 | } | ||
221 | |||
222 | static void report_mmio_enabled(struct pci_dev *dev, void *data) | ||
223 | { | ||
224 | pci_ers_result_t vote; | ||
225 | struct pci_error_handlers *err_handler; | ||
226 | struct aer_broadcast_data *result_data; | ||
227 | result_data = (struct aer_broadcast_data *) data; | ||
228 | |||
229 | if (!dev->driver || | ||
230 | !dev->driver->err_handler || | ||
231 | !dev->driver->err_handler->mmio_enabled) | ||
232 | return; | ||
233 | |||
234 | err_handler = dev->driver->err_handler; | ||
235 | vote = err_handler->mmio_enabled(dev); | ||
236 | result_data->result = merge_result(result_data->result, vote); | ||
237 | return; | ||
238 | } | ||
239 | |||
240 | static void report_slot_reset(struct pci_dev *dev, void *data) | ||
241 | { | ||
242 | pci_ers_result_t vote; | ||
243 | struct pci_error_handlers *err_handler; | ||
244 | struct aer_broadcast_data *result_data; | ||
245 | result_data = (struct aer_broadcast_data *) data; | ||
246 | |||
247 | if (!dev->driver || | ||
248 | !dev->driver->err_handler || | ||
249 | !dev->driver->err_handler->slot_reset) | ||
250 | return; | ||
251 | |||
252 | err_handler = dev->driver->err_handler; | ||
253 | vote = err_handler->slot_reset(dev); | ||
254 | result_data->result = merge_result(result_data->result, vote); | ||
255 | return; | ||
256 | } | ||
257 | |||
258 | static void report_resume(struct pci_dev *dev, void *data) | ||
259 | { | ||
260 | struct pci_error_handlers *err_handler; | ||
261 | |||
262 | dev->error_state = pci_channel_io_normal; | ||
263 | |||
264 | if (!dev->driver || | ||
265 | !dev->driver->err_handler || | ||
266 | !dev->driver->err_handler->slot_reset) | ||
267 | return; | ||
268 | |||
269 | err_handler = dev->driver->err_handler; | ||
270 | err_handler->resume(dev); | ||
271 | return; | ||
272 | } | ||
273 | |||
274 | /** | ||
275 | * broadcast_error_message - handle message broadcast to downstream drivers | ||
276 | * @device: pointer to from where in a hierarchy message is broadcasted down | ||
277 | * @api: callback to be broadcasted | ||
278 | * @state: error state | ||
279 | * | ||
280 | * Invoked during error recovery process. Once being invoked, the content | ||
281 | * of error severity will be broadcasted to all downstream drivers in a | ||
282 | * hierarchy in question. | ||
283 | **/ | ||
284 | static pci_ers_result_t broadcast_error_message(struct pci_dev *dev, | ||
285 | enum pci_channel_state state, | ||
286 | char *error_mesg, | ||
287 | void (*cb)(struct pci_dev *, void *)) | ||
288 | { | ||
289 | struct aer_broadcast_data result_data; | ||
290 | |||
291 | printk(KERN_DEBUG "Broadcast %s message\n", error_mesg); | ||
292 | result_data.state = state; | ||
293 | if (cb == report_error_detected) | ||
294 | result_data.result = PCI_ERS_RESULT_CAN_RECOVER; | ||
295 | else | ||
296 | result_data.result = PCI_ERS_RESULT_RECOVERED; | ||
297 | |||
298 | if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) { | ||
299 | /* | ||
300 | * If the error is reported by a bridge, we think this error | ||
301 | * is related to the downstream link of the bridge, so we | ||
302 | * do error recovery on all subordinates of the bridge instead | ||
303 | * of the bridge and clear the error status of the bridge. | ||
304 | */ | ||
305 | if (cb == report_error_detected) | ||
306 | dev->error_state = state; | ||
307 | pci_walk_bus(dev->subordinate, cb, &result_data); | ||
308 | if (cb == report_resume) { | ||
309 | pci_cleanup_aer_uncorrect_error_status(dev); | ||
310 | dev->error_state = pci_channel_io_normal; | ||
311 | } | ||
312 | } | ||
313 | else { | ||
314 | /* | ||
315 | * If the error is reported by an end point, we think this | ||
316 | * error is related to the upstream link of the end point. | ||
317 | */ | ||
318 | pci_walk_bus(dev->bus, cb, &result_data); | ||
319 | } | ||
320 | |||
321 | return result_data.result; | ||
322 | } | ||
323 | |||
324 | struct find_aer_service_data { | ||
325 | struct pcie_port_service_driver *aer_driver; | ||
326 | int is_downstream; | ||
327 | }; | ||
328 | |||
329 | static int find_aer_service_iter(struct device *device, void *data) | ||
330 | { | ||
331 | struct device_driver *driver; | ||
332 | struct pcie_port_service_driver *service_driver; | ||
333 | struct pcie_device *pcie_dev; | ||
334 | struct find_aer_service_data *result; | ||
335 | |||
336 | result = (struct find_aer_service_data *) data; | ||
337 | |||
338 | if (device->bus == &pcie_port_bus_type) { | ||
339 | pcie_dev = to_pcie_device(device); | ||
340 | if (pcie_dev->id.port_type == PCIE_SW_DOWNSTREAM_PORT) | ||
341 | result->is_downstream = 1; | ||
342 | |||
343 | driver = device->driver; | ||
344 | if (driver) { | ||
345 | service_driver = to_service_driver(driver); | ||
346 | if (service_driver->id_table->service_type == | ||
347 | PCIE_PORT_SERVICE_AER) { | ||
348 | result->aer_driver = service_driver; | ||
349 | return 1; | ||
350 | } | ||
351 | } | ||
352 | } | ||
353 | |||
354 | return 0; | ||
355 | } | ||
356 | |||
357 | static void find_aer_service(struct pci_dev *dev, | ||
358 | struct find_aer_service_data *data) | ||
359 | { | ||
360 | int retval; | ||
361 | retval = device_for_each_child(&dev->dev, data, find_aer_service_iter); | ||
362 | } | ||
363 | |||
364 | static pci_ers_result_t reset_link(struct pcie_device *aerdev, | ||
365 | struct pci_dev *dev) | ||
366 | { | ||
367 | struct pci_dev *udev; | ||
368 | pci_ers_result_t status; | ||
369 | struct find_aer_service_data data; | ||
370 | |||
371 | if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) | ||
372 | udev = dev; | ||
373 | else | ||
374 | udev= dev->bus->self; | ||
375 | |||
376 | data.is_downstream = 0; | ||
377 | data.aer_driver = NULL; | ||
378 | find_aer_service(udev, &data); | ||
379 | |||
380 | /* | ||
381 | * Use the aer driver of the error agent firstly. | ||
382 | * If it hasn't the aer driver, use the root port's | ||
383 | */ | ||
384 | if (!data.aer_driver || !data.aer_driver->reset_link) { | ||
385 | if (data.is_downstream && | ||
386 | aerdev->device.driver && | ||
387 | to_service_driver(aerdev->device.driver)->reset_link) { | ||
388 | data.aer_driver = | ||
389 | to_service_driver(aerdev->device.driver); | ||
390 | } else { | ||
391 | printk(KERN_DEBUG "No link-reset support to Device ID" | ||
392 | "[%s]\n", | ||
393 | dev->dev.bus_id); | ||
394 | return PCI_ERS_RESULT_DISCONNECT; | ||
395 | } | ||
396 | } | ||
397 | |||
398 | status = data.aer_driver->reset_link(udev); | ||
399 | if (status != PCI_ERS_RESULT_RECOVERED) { | ||
400 | printk(KERN_DEBUG "Link reset at upstream Device ID" | ||
401 | "[%s] failed\n", | ||
402 | udev->dev.bus_id); | ||
403 | return PCI_ERS_RESULT_DISCONNECT; | ||
404 | } | ||
405 | |||
406 | return status; | ||
407 | } | ||
408 | |||
409 | /** | ||
410 | * do_recovery - handle nonfatal/fatal error recovery process | ||
411 | * @aerdev: pointer to a pcie_device data structure of root port | ||
412 | * @dev: pointer to a pci_dev data structure of agent detecting an error | ||
413 | * @severity: error severity type | ||
414 | * | ||
415 | * Invoked when an error is nonfatal/fatal. Once being invoked, broadcast | ||
416 | * error detected message to all downstream drivers within a hierarchy in | ||
417 | * question and return the returned code. | ||
418 | **/ | ||
419 | static pci_ers_result_t do_recovery(struct pcie_device *aerdev, | ||
420 | struct pci_dev *dev, | ||
421 | int severity) | ||
422 | { | ||
423 | pci_ers_result_t status, result = PCI_ERS_RESULT_RECOVERED; | ||
424 | enum pci_channel_state state; | ||
425 | |||
426 | if (severity == AER_FATAL) | ||
427 | state = pci_channel_io_frozen; | ||
428 | else | ||
429 | state = pci_channel_io_normal; | ||
430 | |||
431 | status = broadcast_error_message(dev, | ||
432 | state, | ||
433 | "error_detected", | ||
434 | report_error_detected); | ||
435 | |||
436 | if (severity == AER_FATAL) { | ||
437 | result = reset_link(aerdev, dev); | ||
438 | if (result != PCI_ERS_RESULT_RECOVERED) { | ||
439 | /* TODO: Should panic here? */ | ||
440 | return result; | ||
441 | } | ||
442 | } | ||
443 | |||
444 | if (status == PCI_ERS_RESULT_CAN_RECOVER) | ||
445 | status = broadcast_error_message(dev, | ||
446 | state, | ||
447 | "mmio_enabled", | ||
448 | report_mmio_enabled); | ||
449 | |||
450 | if (status == PCI_ERS_RESULT_NEED_RESET) { | ||
451 | /* | ||
452 | * TODO: Should call platform-specific | ||
453 | * functions to reset slot before calling | ||
454 | * drivers' slot_reset callbacks? | ||
455 | */ | ||
456 | status = broadcast_error_message(dev, | ||
457 | state, | ||
458 | "slot_reset", | ||
459 | report_slot_reset); | ||
460 | } | ||
461 | |||
462 | if (status == PCI_ERS_RESULT_RECOVERED) | ||
463 | broadcast_error_message(dev, | ||
464 | state, | ||
465 | "resume", | ||
466 | report_resume); | ||
467 | |||
468 | return status; | ||
469 | } | ||
470 | |||
471 | /** | ||
472 | * handle_error_source - handle logging error into an event log | ||
473 | * @aerdev: pointer to pcie_device data structure of the root port | ||
474 | * @dev: pointer to pci_dev data structure of error source device | ||
475 | * @info: comprehensive error information | ||
476 | * | ||
477 | * Invoked when an error being detected by Root Port. | ||
478 | **/ | ||
479 | static void handle_error_source(struct pcie_device * aerdev, | ||
480 | struct pci_dev *dev, | ||
481 | struct aer_err_info info) | ||
482 | { | ||
483 | pci_ers_result_t status = 0; | ||
484 | int pos; | ||
485 | |||
486 | if (info.severity == AER_CORRECTABLE) { | ||
487 | /* | ||
488 | * Correctable error does not need software intevention. | ||
489 | * No need to go through error recovery process. | ||
490 | */ | ||
491 | pos = pci_find_aer_capability(dev); | ||
492 | if (pos) | ||
493 | pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, | ||
494 | info.status); | ||
495 | } else { | ||
496 | status = do_recovery(aerdev, dev, info.severity); | ||
497 | if (status == PCI_ERS_RESULT_RECOVERED) { | ||
498 | printk(KERN_DEBUG "AER driver successfully recovered\n"); | ||
499 | } else { | ||
500 | /* TODO: Should kernel panic here? */ | ||
501 | printk(KERN_DEBUG "AER driver didn't recover\n"); | ||
502 | } | ||
503 | } | ||
504 | } | ||
505 | |||
506 | /** | ||
507 | * aer_enable_rootport - enable Root Port's interrupts when receiving messages | ||
508 | * @rpc: pointer to a Root Port data structure | ||
509 | * | ||
510 | * Invoked when PCIE bus loads AER service driver. | ||
511 | **/ | ||
512 | void aer_enable_rootport(struct aer_rpc *rpc) | ||
513 | { | ||
514 | struct pci_dev *pdev = rpc->rpd->port; | ||
515 | int pos, aer_pos; | ||
516 | u16 reg16; | ||
517 | u32 reg32; | ||
518 | |||
519 | pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); | ||
520 | /* Clear PCIE Capability's Device Status */ | ||
521 | pci_read_config_word(pdev, pos+PCI_EXP_DEVSTA, ®16); | ||
522 | pci_write_config_word(pdev, pos+PCI_EXP_DEVSTA, reg16); | ||
523 | |||
524 | /* Disable system error generation in response to error messages */ | ||
525 | pci_read_config_word(pdev, pos + PCI_EXP_RTCTL, ®16); | ||
526 | reg16 &= ~(SYSTEM_ERROR_INTR_ON_MESG_MASK); | ||
527 | pci_write_config_word(pdev, pos + PCI_EXP_RTCTL, reg16); | ||
528 | |||
529 | aer_pos = pci_find_aer_capability(pdev); | ||
530 | /* Clear error status */ | ||
531 | pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, ®32); | ||
532 | pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32); | ||
533 | pci_read_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, ®32); | ||
534 | pci_write_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, reg32); | ||
535 | pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, ®32); | ||
536 | pci_write_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, reg32); | ||
537 | |||
538 | /* Enable Root Port device reporting error itself */ | ||
539 | pci_read_config_word(pdev, pos+PCI_EXP_DEVCTL, ®16); | ||
540 | reg16 = reg16 | | ||
541 | PCI_EXP_DEVCTL_CERE | | ||
542 | PCI_EXP_DEVCTL_NFERE | | ||
543 | PCI_EXP_DEVCTL_FERE | | ||
544 | PCI_EXP_DEVCTL_URRE; | ||
545 | pci_write_config_word(pdev, pos+PCI_EXP_DEVCTL, | ||
546 | reg16); | ||
547 | |||
548 | /* Enable Root Port's interrupt in response to error messages */ | ||
549 | pci_write_config_dword(pdev, | ||
550 | aer_pos + PCI_ERR_ROOT_COMMAND, | ||
551 | ROOT_PORT_INTR_ON_MESG_MASK); | ||
552 | } | ||
553 | |||
554 | /** | ||
555 | * disable_root_aer - disable Root Port's interrupts when receiving messages | ||
556 | * @rpc: pointer to a Root Port data structure | ||
557 | * | ||
558 | * Invoked when PCIE bus unloads AER service driver. | ||
559 | **/ | ||
560 | static void disable_root_aer(struct aer_rpc *rpc) | ||
561 | { | ||
562 | struct pci_dev *pdev = rpc->rpd->port; | ||
563 | u32 reg32; | ||
564 | int pos; | ||
565 | |||
566 | pos = pci_find_aer_capability(pdev); | ||
567 | /* Disable Root's interrupt in response to error messages */ | ||
568 | pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, 0); | ||
569 | |||
570 | /* Clear Root's error status reg */ | ||
571 | pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, ®32); | ||
572 | pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32); | ||
573 | } | ||
574 | |||
575 | /** | ||
576 | * get_e_source - retrieve an error source | ||
577 | * @rpc: pointer to the root port which holds an error | ||
578 | * | ||
579 | * Invoked by DPC handler to consume an error. | ||
580 | **/ | ||
581 | static struct aer_err_source* get_e_source(struct aer_rpc *rpc) | ||
582 | { | ||
583 | struct aer_err_source *e_source; | ||
584 | unsigned long flags; | ||
585 | |||
586 | /* Lock access to Root error producer/consumer index */ | ||
587 | spin_lock_irqsave(&rpc->e_lock, flags); | ||
588 | if (rpc->prod_idx == rpc->cons_idx) { | ||
589 | spin_unlock_irqrestore(&rpc->e_lock, flags); | ||
590 | return NULL; | ||
591 | } | ||
592 | e_source = &rpc->e_sources[rpc->cons_idx]; | ||
593 | rpc->cons_idx++; | ||
594 | if (rpc->cons_idx == AER_ERROR_SOURCES_MAX) | ||
595 | rpc->cons_idx = 0; | ||
596 | spin_unlock_irqrestore(&rpc->e_lock, flags); | ||
597 | |||
598 | return e_source; | ||
599 | } | ||
600 | |||
601 | static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) | ||
602 | { | ||
603 | int pos; | ||
604 | |||
605 | pos = pci_find_aer_capability(dev); | ||
606 | |||
607 | /* The device might not support AER */ | ||
608 | if (!pos) | ||
609 | return AER_SUCCESS; | ||
610 | |||
611 | if (info->severity == AER_CORRECTABLE) { | ||
612 | pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, | ||
613 | &info->status); | ||
614 | if (!(info->status & ERR_CORRECTABLE_ERROR_MASK)) | ||
615 | return AER_UNSUCCESS; | ||
616 | } else if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE || | ||
617 | info->severity == AER_NONFATAL) { | ||
618 | |||
619 | /* Link is still healthy for IO reads */ | ||
620 | pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, | ||
621 | &info->status); | ||
622 | if (!(info->status & ERR_UNCORRECTABLE_ERROR_MASK)) | ||
623 | return AER_UNSUCCESS; | ||
624 | |||
625 | if (info->status & AER_LOG_TLP_MASKS) { | ||
626 | info->flags |= AER_TLP_HEADER_VALID_FLAG; | ||
627 | pci_read_config_dword(dev, | ||
628 | pos + PCI_ERR_HEADER_LOG, &info->tlp.dw0); | ||
629 | pci_read_config_dword(dev, | ||
630 | pos + PCI_ERR_HEADER_LOG + 4, &info->tlp.dw1); | ||
631 | pci_read_config_dword(dev, | ||
632 | pos + PCI_ERR_HEADER_LOG + 8, &info->tlp.dw2); | ||
633 | pci_read_config_dword(dev, | ||
634 | pos + PCI_ERR_HEADER_LOG + 12, &info->tlp.dw3); | ||
635 | } | ||
636 | } | ||
637 | |||
638 | return AER_SUCCESS; | ||
639 | } | ||
640 | |||
641 | /** | ||
642 | * aer_isr_one_error - consume an error detected by root port | ||
643 | * @p_device: pointer to error root port service device | ||
644 | * @e_src: pointer to an error source | ||
645 | **/ | ||
646 | static void aer_isr_one_error(struct pcie_device *p_device, | ||
647 | struct aer_err_source *e_src) | ||
648 | { | ||
649 | struct device *s_device; | ||
650 | struct aer_err_info e_info = {0, 0, 0,}; | ||
651 | int i; | ||
652 | u16 id; | ||
653 | |||
654 | /* | ||
655 | * There is a possibility that both correctable error and | ||
656 | * uncorrectable error being logged. Report correctable error first. | ||
657 | */ | ||
658 | for (i = 1; i & ROOT_ERR_STATUS_MASKS ; i <<= 2) { | ||
659 | if (i > 4) | ||
660 | break; | ||
661 | if (!(e_src->status & i)) | ||
662 | continue; | ||
663 | |||
664 | /* Init comprehensive error information */ | ||
665 | if (i & PCI_ERR_ROOT_COR_RCV) { | ||
666 | id = ERR_COR_ID(e_src->id); | ||
667 | e_info.severity = AER_CORRECTABLE; | ||
668 | } else { | ||
669 | id = ERR_UNCOR_ID(e_src->id); | ||
670 | e_info.severity = ((e_src->status >> 6) & 1); | ||
671 | } | ||
672 | if (e_src->status & | ||
673 | (PCI_ERR_ROOT_MULTI_COR_RCV | | ||
674 | PCI_ERR_ROOT_MULTI_UNCOR_RCV)) | ||
675 | e_info.flags |= AER_MULTI_ERROR_VALID_FLAG; | ||
676 | if (!(s_device = find_source_device(p_device->port, id))) { | ||
677 | printk(KERN_DEBUG "%s->can't find device of ID%04x\n", | ||
678 | __FUNCTION__, id); | ||
679 | continue; | ||
680 | } | ||
681 | if (get_device_error_info(to_pci_dev(s_device), &e_info) == | ||
682 | AER_SUCCESS) { | ||
683 | aer_print_error(to_pci_dev(s_device), &e_info); | ||
684 | handle_error_source(p_device, | ||
685 | to_pci_dev(s_device), | ||
686 | e_info); | ||
687 | } | ||
688 | } | ||
689 | } | ||
690 | |||
691 | /** | ||
692 | * aer_isr - consume errors detected by root port | ||
693 | * @context: pointer to a private data of pcie device | ||
694 | * | ||
695 | * Invoked, as DPC, when root port records new detected error | ||
696 | **/ | ||
697 | void aer_isr(void *context) | ||
698 | { | ||
699 | struct pcie_device *p_device = (struct pcie_device *) context; | ||
700 | struct aer_rpc *rpc = get_service_data(p_device); | ||
701 | struct aer_err_source *e_src; | ||
702 | |||
703 | mutex_lock(&rpc->rpc_mutex); | ||
704 | e_src = get_e_source(rpc); | ||
705 | while (e_src) { | ||
706 | aer_isr_one_error(p_device, e_src); | ||
707 | e_src = get_e_source(rpc); | ||
708 | } | ||
709 | mutex_unlock(&rpc->rpc_mutex); | ||
710 | |||
711 | wake_up(&rpc->wait_release); | ||
712 | } | ||
713 | |||
714 | /** | ||
715 | * aer_delete_rootport - disable root port aer and delete service data | ||
716 | * @rpc: pointer to a root port device being deleted | ||
717 | * | ||
718 | * Invoked when AER service unloaded on a specific Root Port | ||
719 | **/ | ||
720 | void aer_delete_rootport(struct aer_rpc *rpc) | ||
721 | { | ||
722 | /* Disable root port AER itself */ | ||
723 | disable_root_aer(rpc); | ||
724 | |||
725 | kfree(rpc); | ||
726 | } | ||
727 | |||
728 | /** | ||
729 | * aer_init - provide AER initialization | ||
730 | * @dev: pointer to AER pcie device | ||
731 | * | ||
732 | * Invoked when AER service driver is loaded. | ||
733 | **/ | ||
734 | int aer_init(struct pcie_device *dev) | ||
735 | { | ||
736 | int status; | ||
737 | |||
738 | /* Run _OSC Method */ | ||
739 | status = aer_osc_setup(dev->port); | ||
740 | |||
741 | if(status != OSC_METHOD_RUN_SUCCESS) { | ||
742 | printk(KERN_DEBUG "%s: AER service init fails - %s\n", | ||
743 | __FUNCTION__, | ||
744 | (status == OSC_METHOD_NOT_SUPPORTED) ? | ||
745 | "No ACPI _OSC support" : "Run ACPI _OSC fails"); | ||
746 | |||
747 | if (!forceload) | ||
748 | return status; | ||
749 | } | ||
750 | |||
751 | return AER_SUCCESS; | ||
752 | } | ||
753 | |||
754 | EXPORT_SYMBOL_GPL(pci_find_aer_capability); | ||
755 | EXPORT_SYMBOL_GPL(pci_enable_pcie_error_reporting); | ||
756 | EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting); | ||
757 | EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status); | ||
758 | |||
diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c new file mode 100644 index 000000000000..3933d4f30e8c --- /dev/null +++ b/drivers/pci/pcie/aer/aerdrv_errprint.c | |||
@@ -0,0 +1,248 @@ | |||
1 | /* | ||
2 | * drivers/pci/pcie/aer/aerdrv_errprint.c | ||
3 | * | ||
4 | * This file is subject to the terms and conditions of the GNU General Public | ||
5 | * License. See the file "COPYING" in the main directory of this archive | ||
6 | * for more details. | ||
7 | * | ||
8 | * Format error messages and print them to console. | ||
9 | * | ||
10 | * Copyright (C) 2006 Intel Corp. | ||
11 | * Tom Long Nguyen (tom.l.nguyen@intel.com) | ||
12 | * Zhang Yanmin (yanmin.zhang@intel.com) | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/pci.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/errno.h> | ||
20 | #include <linux/pm.h> | ||
21 | #include <linux/suspend.h> | ||
22 | |||
23 | #include "aerdrv.h" | ||
24 | |||
25 | #define AER_AGENT_RECEIVER 0 | ||
26 | #define AER_AGENT_REQUESTER 1 | ||
27 | #define AER_AGENT_COMPLETER 2 | ||
28 | #define AER_AGENT_TRANSMITTER 3 | ||
29 | |||
30 | #define AER_AGENT_REQUESTER_MASK (PCI_ERR_UNC_COMP_TIME| \ | ||
31 | PCI_ERR_UNC_UNSUP) | ||
32 | |||
33 | #define AER_AGENT_COMPLETER_MASK PCI_ERR_UNC_COMP_ABORT | ||
34 | |||
35 | #define AER_AGENT_TRANSMITTER_MASK(t, e) (e & (PCI_ERR_COR_REP_ROLL| \ | ||
36 | ((t == AER_CORRECTABLE) ? PCI_ERR_COR_REP_TIMER: 0))) | ||
37 | |||
38 | #define AER_GET_AGENT(t, e) \ | ||
39 | ((e & AER_AGENT_COMPLETER_MASK) ? AER_AGENT_COMPLETER : \ | ||
40 | (e & AER_AGENT_REQUESTER_MASK) ? AER_AGENT_REQUESTER : \ | ||
41 | (AER_AGENT_TRANSMITTER_MASK(t, e)) ? AER_AGENT_TRANSMITTER : \ | ||
42 | AER_AGENT_RECEIVER) | ||
43 | |||
44 | #define AER_PHYSICAL_LAYER_ERROR_MASK PCI_ERR_COR_RCVR | ||
45 | #define AER_DATA_LINK_LAYER_ERROR_MASK(t, e) \ | ||
46 | (PCI_ERR_UNC_DLP| \ | ||
47 | PCI_ERR_COR_BAD_TLP| \ | ||
48 | PCI_ERR_COR_BAD_DLLP| \ | ||
49 | PCI_ERR_COR_REP_ROLL| \ | ||
50 | ((t == AER_CORRECTABLE) ? \ | ||
51 | PCI_ERR_COR_REP_TIMER: 0)) | ||
52 | |||
53 | #define AER_PHYSICAL_LAYER_ERROR 0 | ||
54 | #define AER_DATA_LINK_LAYER_ERROR 1 | ||
55 | #define AER_TRANSACTION_LAYER_ERROR 2 | ||
56 | |||
57 | #define AER_GET_LAYER_ERROR(t, e) \ | ||
58 | ((e & AER_PHYSICAL_LAYER_ERROR_MASK) ? \ | ||
59 | AER_PHYSICAL_LAYER_ERROR : \ | ||
60 | (e & AER_DATA_LINK_LAYER_ERROR_MASK(t, e)) ? \ | ||
61 | AER_DATA_LINK_LAYER_ERROR : \ | ||
62 | AER_TRANSACTION_LAYER_ERROR) | ||
63 | |||
64 | /* | ||
65 | * AER error strings | ||
66 | */ | ||
67 | static char* aer_error_severity_string[] = { | ||
68 | "Uncorrected (Non-Fatal)", | ||
69 | "Uncorrected (Fatal)", | ||
70 | "Corrected" | ||
71 | }; | ||
72 | |||
73 | static char* aer_error_layer[] = { | ||
74 | "Physical Layer", | ||
75 | "Data Link Layer", | ||
76 | "Transaction Layer" | ||
77 | }; | ||
78 | static char* aer_correctable_error_string[] = { | ||
79 | "Receiver Error ", /* Bit Position 0 */ | ||
80 | NULL, | ||
81 | NULL, | ||
82 | NULL, | ||
83 | NULL, | ||
84 | NULL, | ||
85 | "Bad TLP ", /* Bit Position 6 */ | ||
86 | "Bad DLLP ", /* Bit Position 7 */ | ||
87 | "RELAY_NUM Rollover ", /* Bit Position 8 */ | ||
88 | NULL, | ||
89 | NULL, | ||
90 | NULL, | ||
91 | "Replay Timer Timeout ", /* Bit Position 12 */ | ||
92 | "Advisory Non-Fatal ", /* Bit Position 13 */ | ||
93 | NULL, | ||
94 | NULL, | ||
95 | NULL, | ||
96 | NULL, | ||
97 | NULL, | ||
98 | NULL, | ||
99 | NULL, | ||
100 | NULL, | ||
101 | NULL, | ||
102 | NULL, | ||
103 | NULL, | ||
104 | NULL, | ||
105 | NULL, | ||
106 | NULL, | ||
107 | NULL, | ||
108 | NULL, | ||
109 | NULL, | ||
110 | NULL, | ||
111 | }; | ||
112 | |||
113 | static char* aer_uncorrectable_error_string[] = { | ||
114 | NULL, | ||
115 | NULL, | ||
116 | NULL, | ||
117 | NULL, | ||
118 | "Data Link Protocol ", /* Bit Position 4 */ | ||
119 | NULL, | ||
120 | NULL, | ||
121 | NULL, | ||
122 | NULL, | ||
123 | NULL, | ||
124 | NULL, | ||
125 | NULL, | ||
126 | "Poisoned TLP ", /* Bit Position 12 */ | ||
127 | "Flow Control Protocol ", /* Bit Position 13 */ | ||
128 | "Completion Timeout ", /* Bit Position 14 */ | ||
129 | "Completer Abort ", /* Bit Position 15 */ | ||
130 | "Unexpected Completion ", /* Bit Position 16 */ | ||
131 | "Receiver Overflow ", /* Bit Position 17 */ | ||
132 | "Malformed TLP ", /* Bit Position 18 */ | ||
133 | "ECRC ", /* Bit Position 19 */ | ||
134 | "Unsupported Request ", /* Bit Position 20 */ | ||
135 | NULL, | ||
136 | NULL, | ||
137 | NULL, | ||
138 | NULL, | ||
139 | NULL, | ||
140 | NULL, | ||
141 | NULL, | ||
142 | NULL, | ||
143 | NULL, | ||
144 | NULL, | ||
145 | NULL, | ||
146 | }; | ||
147 | |||
148 | static char* aer_agent_string[] = { | ||
149 | "Receiver ID", | ||
150 | "Requester ID", | ||
151 | "Completer ID", | ||
152 | "Transmitter ID" | ||
153 | }; | ||
154 | |||
155 | static char * aer_get_error_source_name(int severity, | ||
156 | unsigned int status, | ||
157 | char errmsg_buff[]) | ||
158 | { | ||
159 | int i; | ||
160 | char * errmsg = NULL; | ||
161 | |||
162 | for (i = 0; i < 32; i++) { | ||
163 | if (!(status & (1 << i))) | ||
164 | continue; | ||
165 | |||
166 | if (severity == AER_CORRECTABLE) | ||
167 | errmsg = aer_correctable_error_string[i]; | ||
168 | else | ||
169 | errmsg = aer_uncorrectable_error_string[i]; | ||
170 | |||
171 | if (!errmsg) { | ||
172 | sprintf(errmsg_buff, "Unknown Error Bit %2d ", i); | ||
173 | errmsg = errmsg_buff; | ||
174 | } | ||
175 | |||
176 | break; | ||
177 | } | ||
178 | |||
179 | return errmsg; | ||
180 | } | ||
181 | |||
182 | static DEFINE_SPINLOCK(logbuf_lock); | ||
183 | static char errmsg_buff[100]; | ||
184 | void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) | ||
185 | { | ||
186 | char * errmsg; | ||
187 | int err_layer, agent; | ||
188 | char * loglevel; | ||
189 | |||
190 | if (info->severity == AER_CORRECTABLE) | ||
191 | loglevel = KERN_WARNING; | ||
192 | else | ||
193 | loglevel = KERN_ERR; | ||
194 | |||
195 | printk("%s+------ PCI-Express Device Error ------+\n", loglevel); | ||
196 | printk("%sError Severity\t\t: %s\n", loglevel, | ||
197 | aer_error_severity_string[info->severity]); | ||
198 | |||
199 | if ( info->status == 0) { | ||
200 | printk("%sPCIE Bus Error type\t: (Unaccessible)\n", loglevel); | ||
201 | printk("%sUnaccessible Received\t: %s\n", loglevel, | ||
202 | info->flags & AER_MULTI_ERROR_VALID_FLAG ? | ||
203 | "Multiple" : "First"); | ||
204 | printk("%sUnregistered Agent ID\t: %04x\n", loglevel, | ||
205 | (dev->bus->number << 8) | dev->devfn); | ||
206 | } else { | ||
207 | err_layer = AER_GET_LAYER_ERROR(info->severity, info->status); | ||
208 | printk("%sPCIE Bus Error type\t: %s\n", loglevel, | ||
209 | aer_error_layer[err_layer]); | ||
210 | |||
211 | spin_lock(&logbuf_lock); | ||
212 | errmsg = aer_get_error_source_name(info->severity, | ||
213 | info->status, | ||
214 | errmsg_buff); | ||
215 | printk("%s%s\t: %s\n", loglevel, errmsg, | ||
216 | info->flags & AER_MULTI_ERROR_VALID_FLAG ? | ||
217 | "Multiple" : "First"); | ||
218 | spin_unlock(&logbuf_lock); | ||
219 | |||
220 | agent = AER_GET_AGENT(info->severity, info->status); | ||
221 | printk("%s%s\t\t: %04x\n", loglevel, | ||
222 | aer_agent_string[agent], | ||
223 | (dev->bus->number << 8) | dev->devfn); | ||
224 | |||
225 | printk("%sVendorID=%04xh, DeviceID=%04xh," | ||
226 | " Bus=%02xh, Device=%02xh, Function=%02xh\n", | ||
227 | loglevel, | ||
228 | dev->vendor, | ||
229 | dev->device, | ||
230 | dev->bus->number, | ||
231 | PCI_SLOT(dev->devfn), | ||
232 | PCI_FUNC(dev->devfn)); | ||
233 | |||
234 | if (info->flags & AER_TLP_HEADER_VALID_FLAG) { | ||
235 | unsigned char *tlp = (unsigned char *) &info->tlp; | ||
236 | printk("%sTLB Header:\n", loglevel); | ||
237 | printk("%s%02x%02x%02x%02x %02x%02x%02x%02x" | ||
238 | " %02x%02x%02x%02x %02x%02x%02x%02x\n", | ||
239 | loglevel, | ||
240 | *(tlp + 3), *(tlp + 2), *(tlp + 1), *tlp, | ||
241 | *(tlp + 7), *(tlp + 6), *(tlp + 5), *(tlp + 4), | ||
242 | *(tlp + 11), *(tlp + 10), *(tlp + 9), | ||
243 | *(tlp + 8), *(tlp + 15), *(tlp + 14), | ||
244 | *(tlp + 13), *(tlp + 12)); | ||
245 | } | ||
246 | } | ||
247 | } | ||
248 | |||
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index 1d317d22ee89..67fcd176babd 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h | |||
@@ -39,7 +39,7 @@ extern int pcie_port_device_suspend(struct pci_dev *dev, pm_message_t state); | |||
39 | extern int pcie_port_device_resume(struct pci_dev *dev); | 39 | extern int pcie_port_device_resume(struct pci_dev *dev); |
40 | #endif | 40 | #endif |
41 | extern void pcie_port_device_remove(struct pci_dev *dev); | 41 | extern void pcie_port_device_remove(struct pci_dev *dev); |
42 | extern void pcie_port_bus_register(void); | 42 | extern int pcie_port_bus_register(void); |
43 | extern void pcie_port_bus_unregister(void); | 43 | extern void pcie_port_bus_unregister(void); |
44 | 44 | ||
45 | #endif /* _PORTDRV_H_ */ | 45 | #endif /* _PORTDRV_H_ */ |
diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c index 3e84b501e6a4..3f0976868eda 100644 --- a/drivers/pci/pcie/portdrv_bus.c +++ b/drivers/pci/pcie/portdrv_bus.c | |||
@@ -24,6 +24,7 @@ struct bus_type pcie_port_bus_type = { | |||
24 | .suspend = pcie_port_bus_suspend, | 24 | .suspend = pcie_port_bus_suspend, |
25 | .resume = pcie_port_bus_resume, | 25 | .resume = pcie_port_bus_resume, |
26 | }; | 26 | }; |
27 | EXPORT_SYMBOL_GPL(pcie_port_bus_type); | ||
27 | 28 | ||
28 | static int pcie_port_bus_match(struct device *dev, struct device_driver *drv) | 29 | static int pcie_port_bus_match(struct device *dev, struct device_driver *drv) |
29 | { | 30 | { |
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 55c662267868..bd6615b4d40e 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c | |||
@@ -6,6 +6,7 @@ | |||
6 | * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) | 6 | * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <linux/compiler.h> | ||
9 | #include <linux/module.h> | 10 | #include <linux/module.h> |
10 | #include <linux/pci.h> | 11 | #include <linux/pci.h> |
11 | #include <linux/kernel.h> | 12 | #include <linux/kernel.h> |
@@ -339,8 +340,7 @@ static int suspend_iter(struct device *dev, void *data) | |||
339 | 340 | ||
340 | int pcie_port_device_suspend(struct pci_dev *dev, pm_message_t state) | 341 | int pcie_port_device_suspend(struct pci_dev *dev, pm_message_t state) |
341 | { | 342 | { |
342 | device_for_each_child(&dev->dev, &state, suspend_iter); | 343 | return device_for_each_child(&dev->dev, &state, suspend_iter); |
343 | return 0; | ||
344 | } | 344 | } |
345 | 345 | ||
346 | static int resume_iter(struct device *dev, void *data) | 346 | static int resume_iter(struct device *dev, void *data) |
@@ -358,8 +358,7 @@ static int resume_iter(struct device *dev, void *data) | |||
358 | 358 | ||
359 | int pcie_port_device_resume(struct pci_dev *dev) | 359 | int pcie_port_device_resume(struct pci_dev *dev) |
360 | { | 360 | { |
361 | device_for_each_child(&dev->dev, NULL, resume_iter); | 361 | return device_for_each_child(&dev->dev, NULL, resume_iter); |
362 | return 0; | ||
363 | } | 362 | } |
364 | #endif | 363 | #endif |
365 | 364 | ||
@@ -402,9 +401,9 @@ void pcie_port_device_remove(struct pci_dev *dev) | |||
402 | pci_disable_msi(dev); | 401 | pci_disable_msi(dev); |
403 | } | 402 | } |
404 | 403 | ||
405 | void pcie_port_bus_register(void) | 404 | int __must_check pcie_port_bus_register(void) |
406 | { | 405 | { |
407 | bus_register(&pcie_port_bus_type); | 406 | return bus_register(&pcie_port_bus_type); |
408 | } | 407 | } |
409 | 408 | ||
410 | void pcie_port_bus_unregister(void) | 409 | void pcie_port_bus_unregister(void) |
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 478d0d28f7ad..037690e08f5f 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c | |||
@@ -14,8 +14,10 @@ | |||
14 | #include <linux/init.h> | 14 | #include <linux/init.h> |
15 | #include <linux/slab.h> | 15 | #include <linux/slab.h> |
16 | #include <linux/pcieport_if.h> | 16 | #include <linux/pcieport_if.h> |
17 | #include <linux/aer.h> | ||
17 | 18 | ||
18 | #include "portdrv.h" | 19 | #include "portdrv.h" |
20 | #include "aer/aerdrv.h" | ||
19 | 21 | ||
20 | /* | 22 | /* |
21 | * Version Information | 23 | * Version Information |
@@ -30,6 +32,43 @@ MODULE_LICENSE("GPL"); | |||
30 | /* global data */ | 32 | /* global data */ |
31 | static const char device_name[] = "pcieport-driver"; | 33 | static const char device_name[] = "pcieport-driver"; |
32 | 34 | ||
35 | static int pcie_portdrv_save_config(struct pci_dev *dev) | ||
36 | { | ||
37 | return pci_save_state(dev); | ||
38 | } | ||
39 | |||
40 | #ifdef CONFIG_PM | ||
41 | static int pcie_portdrv_restore_config(struct pci_dev *dev) | ||
42 | { | ||
43 | int retval; | ||
44 | |||
45 | pci_restore_state(dev); | ||
46 | retval = pci_enable_device(dev); | ||
47 | if (retval) | ||
48 | return retval; | ||
49 | pci_set_master(dev); | ||
50 | return 0; | ||
51 | } | ||
52 | |||
53 | static int pcie_portdrv_suspend(struct pci_dev *dev, pm_message_t state) | ||
54 | { | ||
55 | int ret = pcie_port_device_suspend(dev, state); | ||
56 | |||
57 | if (!ret) | ||
58 | ret = pcie_portdrv_save_config(dev); | ||
59 | return ret; | ||
60 | } | ||
61 | |||
62 | static int pcie_portdrv_resume(struct pci_dev *dev) | ||
63 | { | ||
64 | pcie_portdrv_restore_config(dev); | ||
65 | return pcie_port_device_resume(dev); | ||
66 | } | ||
67 | #else | ||
68 | #define pcie_portdrv_suspend NULL | ||
69 | #define pcie_portdrv_resume NULL | ||
70 | #endif | ||
71 | |||
33 | /* | 72 | /* |
34 | * pcie_portdrv_probe - Probe PCI-Express port devices | 73 | * pcie_portdrv_probe - Probe PCI-Express port devices |
35 | * @dev: PCI-Express port device being probed | 74 | * @dev: PCI-Express port device being probed |
@@ -61,6 +100,10 @@ static int __devinit pcie_portdrv_probe (struct pci_dev *dev, | |||
61 | return -ENOMEM; | 100 | return -ENOMEM; |
62 | } | 101 | } |
63 | 102 | ||
103 | pcie_portdrv_save_config(dev); | ||
104 | |||
105 | pci_enable_pcie_error_reporting(dev); | ||
106 | |||
64 | return 0; | 107 | return 0; |
65 | } | 108 | } |
66 | 109 | ||
@@ -70,39 +113,151 @@ static void pcie_portdrv_remove (struct pci_dev *dev) | |||
70 | kfree(pci_get_drvdata(dev)); | 113 | kfree(pci_get_drvdata(dev)); |
71 | } | 114 | } |
72 | 115 | ||
73 | #ifdef CONFIG_PM | 116 | static int error_detected_iter(struct device *device, void *data) |
74 | static int pcie_portdrv_save_config(struct pci_dev *dev) | ||
75 | { | 117 | { |
76 | return pci_save_state(dev); | 118 | struct pcie_device *pcie_device; |
119 | struct pcie_port_service_driver *driver; | ||
120 | struct aer_broadcast_data *result_data; | ||
121 | pci_ers_result_t status; | ||
122 | |||
123 | result_data = (struct aer_broadcast_data *) data; | ||
124 | |||
125 | if (device->bus == &pcie_port_bus_type && device->driver) { | ||
126 | driver = to_service_driver(device->driver); | ||
127 | if (!driver || | ||
128 | !driver->err_handler || | ||
129 | !driver->err_handler->error_detected) | ||
130 | return 0; | ||
131 | |||
132 | pcie_device = to_pcie_device(device); | ||
133 | |||
134 | /* Forward error detected message to service drivers */ | ||
135 | status = driver->err_handler->error_detected( | ||
136 | pcie_device->port, | ||
137 | result_data->state); | ||
138 | result_data->result = | ||
139 | merge_result(result_data->result, status); | ||
140 | } | ||
141 | |||
142 | return 0; | ||
77 | } | 143 | } |
78 | 144 | ||
79 | static int pcie_portdrv_restore_config(struct pci_dev *dev) | 145 | static pci_ers_result_t pcie_portdrv_error_detected(struct pci_dev *dev, |
146 | enum pci_channel_state error) | ||
80 | { | 147 | { |
148 | struct aer_broadcast_data result_data = | ||
149 | {error, PCI_ERS_RESULT_CAN_RECOVER}; | ||
81 | int retval; | 150 | int retval; |
82 | 151 | ||
83 | pci_restore_state(dev); | 152 | /* can not fail */ |
84 | retval = pci_enable_device(dev); | 153 | retval = device_for_each_child(&dev->dev, &result_data, error_detected_iter); |
85 | if (retval) | 154 | |
86 | return retval; | 155 | return result_data.result; |
87 | pci_set_master(dev); | 156 | } |
157 | |||
158 | static int mmio_enabled_iter(struct device *device, void *data) | ||
159 | { | ||
160 | struct pcie_device *pcie_device; | ||
161 | struct pcie_port_service_driver *driver; | ||
162 | pci_ers_result_t status, *result; | ||
163 | |||
164 | result = (pci_ers_result_t *) data; | ||
165 | |||
166 | if (device->bus == &pcie_port_bus_type && device->driver) { | ||
167 | driver = to_service_driver(device->driver); | ||
168 | if (driver && | ||
169 | driver->err_handler && | ||
170 | driver->err_handler->mmio_enabled) { | ||
171 | pcie_device = to_pcie_device(device); | ||
172 | |||
173 | /* Forward error message to service drivers */ | ||
174 | status = driver->err_handler->mmio_enabled( | ||
175 | pcie_device->port); | ||
176 | *result = merge_result(*result, status); | ||
177 | } | ||
178 | } | ||
179 | |||
88 | return 0; | 180 | return 0; |
89 | } | 181 | } |
90 | 182 | ||
91 | static int pcie_portdrv_suspend (struct pci_dev *dev, pm_message_t state) | 183 | static pci_ers_result_t pcie_portdrv_mmio_enabled(struct pci_dev *dev) |
92 | { | 184 | { |
93 | int ret = pcie_port_device_suspend(dev, state); | 185 | pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED; |
186 | int retval; | ||
94 | 187 | ||
95 | if (!ret) | 188 | /* get true return value from &status */ |
96 | ret = pcie_portdrv_save_config(dev); | 189 | retval = device_for_each_child(&dev->dev, &status, mmio_enabled_iter); |
97 | return ret; | 190 | return status; |
98 | } | 191 | } |
99 | 192 | ||
100 | static int pcie_portdrv_resume (struct pci_dev *dev) | 193 | static int slot_reset_iter(struct device *device, void *data) |
101 | { | 194 | { |
102 | pcie_portdrv_restore_config(dev); | 195 | struct pcie_device *pcie_device; |
103 | return pcie_port_device_resume(dev); | 196 | struct pcie_port_service_driver *driver; |
197 | pci_ers_result_t status, *result; | ||
198 | |||
199 | result = (pci_ers_result_t *) data; | ||
200 | |||
201 | if (device->bus == &pcie_port_bus_type && device->driver) { | ||
202 | driver = to_service_driver(device->driver); | ||
203 | if (driver && | ||
204 | driver->err_handler && | ||
205 | driver->err_handler->slot_reset) { | ||
206 | pcie_device = to_pcie_device(device); | ||
207 | |||
208 | /* Forward error message to service drivers */ | ||
209 | status = driver->err_handler->slot_reset( | ||
210 | pcie_device->port); | ||
211 | *result = merge_result(*result, status); | ||
212 | } | ||
213 | } | ||
214 | |||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | static pci_ers_result_t pcie_portdrv_slot_reset(struct pci_dev *dev) | ||
219 | { | ||
220 | pci_ers_result_t status; | ||
221 | int retval; | ||
222 | |||
223 | /* If fatal, restore cfg space for possible link reset at upstream */ | ||
224 | if (dev->error_state == pci_channel_io_frozen) { | ||
225 | pcie_portdrv_restore_config(dev); | ||
226 | pci_enable_pcie_error_reporting(dev); | ||
227 | } | ||
228 | |||
229 | /* get true return value from &status */ | ||
230 | retval = device_for_each_child(&dev->dev, &status, slot_reset_iter); | ||
231 | |||
232 | return status; | ||
233 | } | ||
234 | |||
235 | static int resume_iter(struct device *device, void *data) | ||
236 | { | ||
237 | struct pcie_device *pcie_device; | ||
238 | struct pcie_port_service_driver *driver; | ||
239 | |||
240 | if (device->bus == &pcie_port_bus_type && device->driver) { | ||
241 | driver = to_service_driver(device->driver); | ||
242 | if (driver && | ||
243 | driver->err_handler && | ||
244 | driver->err_handler->resume) { | ||
245 | pcie_device = to_pcie_device(device); | ||
246 | |||
247 | /* Forward error message to service drivers */ | ||
248 | driver->err_handler->resume(pcie_device->port); | ||
249 | } | ||
250 | } | ||
251 | |||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | static void pcie_portdrv_err_resume(struct pci_dev *dev) | ||
256 | { | ||
257 | int retval; | ||
258 | /* nothing to do with error value, if it ever happens */ | ||
259 | retval = device_for_each_child(&dev->dev, NULL, resume_iter); | ||
104 | } | 260 | } |
105 | #endif | ||
106 | 261 | ||
107 | /* | 262 | /* |
108 | * LINUX Device Driver Model | 263 | * LINUX Device Driver Model |
@@ -114,6 +269,13 @@ static const struct pci_device_id port_pci_ids[] = { { | |||
114 | }; | 269 | }; |
115 | MODULE_DEVICE_TABLE(pci, port_pci_ids); | 270 | MODULE_DEVICE_TABLE(pci, port_pci_ids); |
116 | 271 | ||
272 | static struct pci_error_handlers pcie_portdrv_err_handler = { | ||
273 | .error_detected = pcie_portdrv_error_detected, | ||
274 | .mmio_enabled = pcie_portdrv_mmio_enabled, | ||
275 | .slot_reset = pcie_portdrv_slot_reset, | ||
276 | .resume = pcie_portdrv_err_resume, | ||
277 | }; | ||
278 | |||
117 | static struct pci_driver pcie_portdrv = { | 279 | static struct pci_driver pcie_portdrv = { |
118 | .name = (char *)device_name, | 280 | .name = (char *)device_name, |
119 | .id_table = &port_pci_ids[0], | 281 | .id_table = &port_pci_ids[0], |
@@ -121,20 +283,25 @@ static struct pci_driver pcie_portdrv = { | |||
121 | .probe = pcie_portdrv_probe, | 283 | .probe = pcie_portdrv_probe, |
122 | .remove = pcie_portdrv_remove, | 284 | .remove = pcie_portdrv_remove, |
123 | 285 | ||
124 | #ifdef CONFIG_PM | ||
125 | .suspend = pcie_portdrv_suspend, | 286 | .suspend = pcie_portdrv_suspend, |
126 | .resume = pcie_portdrv_resume, | 287 | .resume = pcie_portdrv_resume, |
127 | #endif /* PM */ | 288 | |
289 | .err_handler = &pcie_portdrv_err_handler, | ||
128 | }; | 290 | }; |
129 | 291 | ||
130 | static int __init pcie_portdrv_init(void) | 292 | static int __init pcie_portdrv_init(void) |
131 | { | 293 | { |
132 | int retval = 0; | 294 | int retval; |
133 | 295 | ||
134 | pcie_port_bus_register(); | 296 | retval = pcie_port_bus_register(); |
297 | if (retval) { | ||
298 | printk(KERN_WARNING "PCIE: bus_register error: %d\n", retval); | ||
299 | goto out; | ||
300 | } | ||
135 | retval = pci_register_driver(&pcie_portdrv); | 301 | retval = pci_register_driver(&pcie_portdrv); |
136 | if (retval) | 302 | if (retval) |
137 | pcie_port_bus_unregister(); | 303 | pcie_port_bus_unregister(); |
304 | out: | ||
138 | return retval; | 305 | return retval; |
139 | } | 306 | } |
140 | 307 | ||
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index c5a58d1c6c1c..a3b0a5eb5054 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c | |||
@@ -339,6 +339,7 @@ pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr) | |||
339 | { | 339 | { |
340 | struct pci_bus *child; | 340 | struct pci_bus *child; |
341 | int i; | 341 | int i; |
342 | int retval; | ||
342 | 343 | ||
343 | /* | 344 | /* |
344 | * Allocate a new bus, and inherit stuff from the parent.. | 345 | * Allocate a new bus, and inherit stuff from the parent.. |
@@ -356,8 +357,13 @@ pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr) | |||
356 | 357 | ||
357 | child->class_dev.class = &pcibus_class; | 358 | child->class_dev.class = &pcibus_class; |
358 | sprintf(child->class_dev.class_id, "%04x:%02x", pci_domain_nr(child), busnr); | 359 | sprintf(child->class_dev.class_id, "%04x:%02x", pci_domain_nr(child), busnr); |
359 | class_device_register(&child->class_dev); | 360 | retval = class_device_register(&child->class_dev); |
360 | class_device_create_file(&child->class_dev, &class_device_attr_cpuaffinity); | 361 | if (retval) |
362 | goto error_register; | ||
363 | retval = class_device_create_file(&child->class_dev, | ||
364 | &class_device_attr_cpuaffinity); | ||
365 | if (retval) | ||
366 | goto error_file_create; | ||
361 | 367 | ||
362 | /* | 368 | /* |
363 | * Set up the primary, secondary and subordinate | 369 | * Set up the primary, secondary and subordinate |
@@ -375,6 +381,12 @@ pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr) | |||
375 | bridge->subordinate = child; | 381 | bridge->subordinate = child; |
376 | 382 | ||
377 | return child; | 383 | return child; |
384 | |||
385 | error_file_create: | ||
386 | class_device_unregister(&child->class_dev); | ||
387 | error_register: | ||
388 | kfree(child); | ||
389 | return NULL; | ||
378 | } | 390 | } |
379 | 391 | ||
380 | struct pci_bus * __devinit pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr) | 392 | struct pci_bus * __devinit pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr) |
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index def78a2a7c15..08cd86a6dd66 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c | |||
@@ -577,8 +577,6 @@ static void __init quirk_ioapic_rmw(struct pci_dev *dev) | |||
577 | } | 577 | } |
578 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SI, PCI_ANY_ID, quirk_ioapic_rmw ); | 578 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SI, PCI_ANY_ID, quirk_ioapic_rmw ); |
579 | 579 | ||
580 | int pci_msi_quirk; | ||
581 | |||
582 | #define AMD8131_revA0 0x01 | 580 | #define AMD8131_revA0 0x01 |
583 | #define AMD8131_revB0 0x11 | 581 | #define AMD8131_revB0 0x11 |
584 | #define AMD8131_MISC 0x40 | 582 | #define AMD8131_MISC 0x40 |
@@ -587,12 +585,6 @@ static void __init quirk_amd_8131_ioapic(struct pci_dev *dev) | |||
587 | { | 585 | { |
588 | unsigned char revid, tmp; | 586 | unsigned char revid, tmp; |
589 | 587 | ||
590 | if (dev->subordinate) { | ||
591 | printk(KERN_WARNING "PCI: MSI quirk detected. " | ||
592 | "PCI_BUS_FLAGS_NO_MSI set for subordinate bus.\n"); | ||
593 | dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI; | ||
594 | } | ||
595 | |||
596 | if (nr_ioapics == 0) | 588 | if (nr_ioapics == 0) |
597 | return; | 589 | return; |
598 | 590 | ||
@@ -605,13 +597,6 @@ static void __init quirk_amd_8131_ioapic(struct pci_dev *dev) | |||
605 | } | 597 | } |
606 | } | 598 | } |
607 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic); | 599 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic); |
608 | |||
609 | static void __init quirk_svw_msi(struct pci_dev *dev) | ||
610 | { | ||
611 | pci_msi_quirk = 1; | ||
612 | printk(KERN_WARNING "PCI: MSI quirk detected. pci_msi_quirk set.\n"); | ||
613 | } | ||
614 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_GCNB_LE, quirk_svw_msi ); | ||
615 | #endif /* CONFIG_X86_IO_APIC */ | 600 | #endif /* CONFIG_X86_IO_APIC */ |
616 | 601 | ||
617 | 602 | ||
@@ -1690,6 +1675,95 @@ static void __devinit quirk_nvidia_ck804_pcie_aer_ext_cap(struct pci_dev *dev) | |||
1690 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE, | 1675 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE, |
1691 | quirk_nvidia_ck804_pcie_aer_ext_cap); | 1676 | quirk_nvidia_ck804_pcie_aer_ext_cap); |
1692 | 1677 | ||
1678 | #ifdef CONFIG_PCI_MSI | ||
1679 | /* To disable MSI globally */ | ||
1680 | int pci_msi_quirk; | ||
1681 | |||
1682 | /* The Serverworks PCI-X chipset does not support MSI. We cannot easily rely | ||
1683 | * on setting PCI_BUS_FLAGS_NO_MSI in its bus flags because there are actually | ||
1684 | * some other busses controlled by the chipset even if Linux is not aware of it. | ||
1685 | * Instead of setting the flag on all busses in the machine, simply disable MSI | ||
1686 | * globally. | ||
1687 | */ | ||
1688 | static void __init quirk_svw_msi(struct pci_dev *dev) | ||
1689 | { | ||
1690 | pci_msi_quirk = 1; | ||
1691 | printk(KERN_WARNING "PCI: MSI quirk detected. pci_msi_quirk set.\n"); | ||
1692 | } | ||
1693 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_GCNB_LE, quirk_svw_msi); | ||
1694 | |||
1695 | /* Disable MSI on chipsets that are known to not support it */ | ||
1696 | static void __devinit quirk_disable_msi(struct pci_dev *dev) | ||
1697 | { | ||
1698 | if (dev->subordinate) { | ||
1699 | printk(KERN_WARNING "PCI: MSI quirk detected. " | ||
1700 | "PCI_BUS_FLAGS_NO_MSI set for %s subordinate bus.\n", | ||
1701 | pci_name(dev)); | ||
1702 | dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI; | ||
1703 | } | ||
1704 | } | ||
1705 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_disable_msi); | ||
1706 | |||
1707 | /* Go through the list of Hypertransport capabilities and | ||
1708 | * return 1 if a HT MSI capability is found and enabled */ | ||
1709 | static int __devinit msi_ht_cap_enabled(struct pci_dev *dev) | ||
1710 | { | ||
1711 | u8 pos; | ||
1712 | int ttl; | ||
1713 | for (pos = pci_find_capability(dev, PCI_CAP_ID_HT), ttl = 48; | ||
1714 | pos && ttl; | ||
1715 | pos = pci_find_next_capability(dev, pos, PCI_CAP_ID_HT), ttl--) { | ||
1716 | u32 cap_hdr; | ||
1717 | /* MSI mapping section according to Hypertransport spec */ | ||
1718 | if (pci_read_config_dword(dev, pos, &cap_hdr) == 0 | ||
1719 | && (cap_hdr & 0xf8000000) == 0xa8000000 /* MSI mapping */) { | ||
1720 | printk(KERN_INFO "PCI: Found HT MSI mapping on %s with capability %s\n", | ||
1721 | pci_name(dev), cap_hdr & 0x10000 ? "enabled" : "disabled"); | ||
1722 | return (cap_hdr & 0x10000) != 0; /* MSI mapping cap enabled */ | ||
1723 | } | ||
1724 | } | ||
1725 | return 0; | ||
1726 | } | ||
1727 | |||
1728 | /* Check the hypertransport MSI mapping to know whether MSI is enabled or not */ | ||
1729 | static void __devinit quirk_msi_ht_cap(struct pci_dev *dev) | ||
1730 | { | ||
1731 | if (dev->subordinate && !msi_ht_cap_enabled(dev)) { | ||
1732 | printk(KERN_WARNING "PCI: MSI quirk detected. " | ||
1733 | "MSI disabled on chipset %s.\n", | ||
1734 | pci_name(dev)); | ||
1735 | dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI; | ||
1736 | } | ||
1737 | } | ||
1738 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT2000_PCIE, | ||
1739 | quirk_msi_ht_cap); | ||
1740 | |||
1741 | /* The nVidia CK804 chipset may have 2 HT MSI mappings. | ||
1742 | * MSI are supported if the MSI capability set in any of these mappings. | ||
1743 | */ | ||
1744 | static void __devinit quirk_nvidia_ck804_msi_ht_cap(struct pci_dev *dev) | ||
1745 | { | ||
1746 | struct pci_dev *pdev; | ||
1747 | |||
1748 | if (!dev->subordinate) | ||
1749 | return; | ||
1750 | |||
1751 | /* check HT MSI cap on this chipset and the root one. | ||
1752 | * a single one having MSI is enough to be sure that MSI are supported. | ||
1753 | */ | ||
1754 | pdev = pci_find_slot(dev->bus->number, 0); | ||
1755 | if (dev->subordinate && !msi_ht_cap_enabled(dev) | ||
1756 | && !msi_ht_cap_enabled(pdev)) { | ||
1757 | printk(KERN_WARNING "PCI: MSI quirk detected. " | ||
1758 | "MSI disabled on chipset %s.\n", | ||
1759 | pci_name(dev)); | ||
1760 | dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI; | ||
1761 | } | ||
1762 | } | ||
1763 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE, | ||
1764 | quirk_nvidia_ck804_msi_ht_cap); | ||
1765 | #endif /* CONFIG_PCI_MSI */ | ||
1766 | |||
1693 | EXPORT_SYMBOL(pcie_mch_quirk); | 1767 | EXPORT_SYMBOL(pcie_mch_quirk); |
1694 | #ifdef CONFIG_HOTPLUG | 1768 | #ifdef CONFIG_HOTPLUG |
1695 | EXPORT_SYMBOL(pci_fixup_device); | 1769 | EXPORT_SYMBOL(pci_fixup_device); |
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 99ffbd478b29..430281b2e921 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c | |||
@@ -16,8 +16,11 @@ static void pci_free_resources(struct pci_dev *dev) | |||
16 | } | 16 | } |
17 | } | 17 | } |
18 | 18 | ||
19 | static void pci_destroy_dev(struct pci_dev *dev) | 19 | static void pci_stop_dev(struct pci_dev *dev) |
20 | { | 20 | { |
21 | if (!dev->global_list.next) | ||
22 | return; | ||
23 | |||
21 | if (!list_empty(&dev->global_list)) { | 24 | if (!list_empty(&dev->global_list)) { |
22 | pci_proc_detach_device(dev); | 25 | pci_proc_detach_device(dev); |
23 | pci_remove_sysfs_dev_files(dev); | 26 | pci_remove_sysfs_dev_files(dev); |
@@ -27,6 +30,11 @@ static void pci_destroy_dev(struct pci_dev *dev) | |||
27 | dev->global_list.next = dev->global_list.prev = NULL; | 30 | dev->global_list.next = dev->global_list.prev = NULL; |
28 | up_write(&pci_bus_sem); | 31 | up_write(&pci_bus_sem); |
29 | } | 32 | } |
33 | } | ||
34 | |||
35 | static void pci_destroy_dev(struct pci_dev *dev) | ||
36 | { | ||
37 | pci_stop_dev(dev); | ||
30 | 38 | ||
31 | /* Remove the device from the device lists, and prevent any further | 39 | /* Remove the device from the device lists, and prevent any further |
32 | * list accesses from this device */ | 40 | * list accesses from this device */ |
@@ -119,5 +127,32 @@ void pci_remove_behind_bridge(struct pci_dev *dev) | |||
119 | } | 127 | } |
120 | } | 128 | } |
121 | 129 | ||
130 | static void pci_stop_bus_devices(struct pci_bus *bus) | ||
131 | { | ||
132 | struct list_head *l, *n; | ||
133 | |||
134 | list_for_each_safe(l, n, &bus->devices) { | ||
135 | struct pci_dev *dev = pci_dev_b(l); | ||
136 | pci_stop_bus_device(dev); | ||
137 | } | ||
138 | } | ||
139 | |||
140 | /** | ||
141 | * pci_stop_bus_device - stop a PCI device and any children | ||
142 | * @dev: the device to stop | ||
143 | * | ||
144 | * Stop a PCI device (detach the driver, remove from the global list | ||
145 | * and so on). This also stop any subordinate buses and children in a | ||
146 | * depth-first manner. | ||
147 | */ | ||
148 | void pci_stop_bus_device(struct pci_dev *dev) | ||
149 | { | ||
150 | if (dev->subordinate) | ||
151 | pci_stop_bus_devices(dev->subordinate); | ||
152 | |||
153 | pci_stop_dev(dev); | ||
154 | } | ||
155 | |||
122 | EXPORT_SYMBOL(pci_remove_bus_device); | 156 | EXPORT_SYMBOL(pci_remove_bus_device); |
123 | EXPORT_SYMBOL(pci_remove_behind_bridge); | 157 | EXPORT_SYMBOL(pci_remove_behind_bridge); |
158 | EXPORT_SYMBOL_GPL(pci_stop_bus_device); | ||
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 47c1071ad84e..54404917be9a 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c | |||
@@ -55,12 +55,19 @@ pbus_assign_resources_sorted(struct pci_bus *bus) | |||
55 | list_for_each_entry(dev, &bus->devices, bus_list) { | 55 | list_for_each_entry(dev, &bus->devices, bus_list) { |
56 | u16 class = dev->class >> 8; | 56 | u16 class = dev->class >> 8; |
57 | 57 | ||
58 | /* Don't touch classless devices or host bridges or ioapics. */ | 58 | /* Don't touch classless devices or host bridges. */ |
59 | if (class == PCI_CLASS_NOT_DEFINED || | 59 | if (class == PCI_CLASS_NOT_DEFINED || |
60 | class == PCI_CLASS_BRIDGE_HOST || | 60 | class == PCI_CLASS_BRIDGE_HOST) |
61 | class == PCI_CLASS_SYSTEM_PIC) | ||
62 | continue; | 61 | continue; |
63 | 62 | ||
63 | /* Don't touch ioapics if it has the assigned resources. */ | ||
64 | if (class == PCI_CLASS_SYSTEM_PIC) { | ||
65 | res = &dev->resource[0]; | ||
66 | if (res[0].start || res[1].start || res[2].start || | ||
67 | res[3].start || res[4].start || res[5].start) | ||
68 | continue; | ||
69 | } | ||
70 | |||
64 | pdev_sort_resources(dev, &head); | 71 | pdev_sort_resources(dev, &head); |
65 | } | 72 | } |
66 | 73 | ||