diff options
Diffstat (limited to 'arch/x86')
-rw-r--r-- | arch/x86/include/asm/pci_x86.h | 7 | ||||
-rw-r--r-- | arch/x86/kernel/quirks.c | 2 | ||||
-rw-r--r-- | arch/x86/pci/acpi.c | 109 | ||||
-rw-r--r-- | arch/x86/pci/amd_bus.c | 7 | ||||
-rw-r--r-- | arch/x86/pci/bus_numa.c | 22 | ||||
-rw-r--r-- | arch/x86/pci/bus_numa.h | 3 | ||||
-rw-r--r-- | arch/x86/pci/common.c | 2 | ||||
-rw-r--r-- | arch/x86/pci/mmconfig-shared.c | 372 | ||||
-rw-r--r-- | arch/x86/pci/mmconfig_32.c | 30 | ||||
-rw-r--r-- | arch/x86/pci/mmconfig_64.c | 52 |
10 files changed, 439 insertions, 167 deletions
diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h index b3a531746026..b2652e95b3d7 100644 --- a/arch/x86/include/asm/pci_x86.h +++ b/arch/x86/include/asm/pci_x86.h | |||
@@ -100,6 +100,7 @@ struct pci_raw_ops { | |||
100 | extern const struct pci_raw_ops *raw_pci_ops; | 100 | extern const struct pci_raw_ops *raw_pci_ops; |
101 | extern const struct pci_raw_ops *raw_pci_ext_ops; | 101 | extern const struct pci_raw_ops *raw_pci_ext_ops; |
102 | 102 | ||
103 | extern const struct pci_raw_ops pci_mmcfg; | ||
103 | extern const struct pci_raw_ops pci_direct_conf1; | 104 | extern const struct pci_raw_ops pci_direct_conf1; |
104 | extern bool port_cf9_safe; | 105 | extern bool port_cf9_safe; |
105 | 106 | ||
@@ -135,6 +136,12 @@ struct pci_mmcfg_region { | |||
135 | 136 | ||
136 | extern int __init pci_mmcfg_arch_init(void); | 137 | extern int __init pci_mmcfg_arch_init(void); |
137 | extern void __init pci_mmcfg_arch_free(void); | 138 | extern void __init pci_mmcfg_arch_free(void); |
139 | extern int __devinit pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg); | ||
140 | extern void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg); | ||
141 | extern int __devinit pci_mmconfig_insert(struct device *dev, | ||
142 | u16 seg, u8 start, | ||
143 | u8 end, phys_addr_t addr); | ||
144 | extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end); | ||
138 | extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus); | 145 | extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus); |
139 | 146 | ||
140 | extern struct list_head pci_mmcfg_list; | 147 | extern struct list_head pci_mmcfg_list; |
diff --git a/arch/x86/kernel/quirks.c b/arch/x86/kernel/quirks.c index 03920a15a632..1b27de563561 100644 --- a/arch/x86/kernel/quirks.c +++ b/arch/x86/kernel/quirks.c | |||
@@ -512,7 +512,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, | |||
512 | 512 | ||
513 | #if defined(CONFIG_PCI) && defined(CONFIG_NUMA) | 513 | #if defined(CONFIG_PCI) && defined(CONFIG_NUMA) |
514 | /* Set correct numa_node information for AMD NB functions */ | 514 | /* Set correct numa_node information for AMD NB functions */ |
515 | static void __init quirk_amd_nb_node(struct pci_dev *dev) | 515 | static void __devinit quirk_amd_nb_node(struct pci_dev *dev) |
516 | { | 516 | { |
517 | struct pci_dev *nb_ht; | 517 | struct pci_dev *nb_ht; |
518 | unsigned int devfn; | 518 | unsigned int devfn; |
diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index fc09c2754e08..505acdd6d600 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c | |||
@@ -12,8 +12,13 @@ struct pci_root_info { | |||
12 | char name[16]; | 12 | char name[16]; |
13 | unsigned int res_num; | 13 | unsigned int res_num; |
14 | struct resource *res; | 14 | struct resource *res; |
15 | int busnum; | ||
16 | struct pci_sysdata sd; | 15 | struct pci_sysdata sd; |
16 | #ifdef CONFIG_PCI_MMCONFIG | ||
17 | bool mcfg_added; | ||
18 | u16 segment; | ||
19 | u8 start_bus; | ||
20 | u8 end_bus; | ||
21 | #endif | ||
17 | }; | 22 | }; |
18 | 23 | ||
19 | static bool pci_use_crs = true; | 24 | static bool pci_use_crs = true; |
@@ -120,6 +125,81 @@ void __init pci_acpi_crs_quirks(void) | |||
120 | pci_use_crs ? "nocrs" : "use_crs"); | 125 | pci_use_crs ? "nocrs" : "use_crs"); |
121 | } | 126 | } |
122 | 127 | ||
128 | #ifdef CONFIG_PCI_MMCONFIG | ||
129 | static int __devinit check_segment(u16 seg, struct device *dev, char *estr) | ||
130 | { | ||
131 | if (seg) { | ||
132 | dev_err(dev, | ||
133 | "%s can't access PCI configuration " | ||
134 | "space under this host bridge.\n", | ||
135 | estr); | ||
136 | return -EIO; | ||
137 | } | ||
138 | |||
139 | /* | ||
140 | * Failure in adding MMCFG information is not fatal, | ||
141 | * just can't access extended configuration space of | ||
142 | * devices under this host bridge. | ||
143 | */ | ||
144 | dev_warn(dev, | ||
145 | "%s can't access extended PCI configuration " | ||
146 | "space under this bridge.\n", | ||
147 | estr); | ||
148 | |||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | static int __devinit setup_mcfg_map(struct pci_root_info *info, | ||
153 | u16 seg, u8 start, u8 end, | ||
154 | phys_addr_t addr) | ||
155 | { | ||
156 | int result; | ||
157 | struct device *dev = &info->bridge->dev; | ||
158 | |||
159 | info->start_bus = start; | ||
160 | info->end_bus = end; | ||
161 | info->mcfg_added = false; | ||
162 | |||
163 | /* return success if MMCFG is not in use */ | ||
164 | if (raw_pci_ext_ops && raw_pci_ext_ops != &pci_mmcfg) | ||
165 | return 0; | ||
166 | |||
167 | if (!(pci_probe & PCI_PROBE_MMCONF)) | ||
168 | return check_segment(seg, dev, "MMCONFIG is disabled,"); | ||
169 | |||
170 | result = pci_mmconfig_insert(dev, seg, start, end, addr); | ||
171 | if (result == 0) { | ||
172 | /* enable MMCFG if it hasn't been enabled yet */ | ||
173 | if (raw_pci_ext_ops == NULL) | ||
174 | raw_pci_ext_ops = &pci_mmcfg; | ||
175 | info->mcfg_added = true; | ||
176 | } else if (result != -EEXIST) | ||
177 | return check_segment(seg, dev, | ||
178 | "fail to add MMCONFIG information,"); | ||
179 | |||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | static void teardown_mcfg_map(struct pci_root_info *info) | ||
184 | { | ||
185 | if (info->mcfg_added) { | ||
186 | pci_mmconfig_delete(info->segment, info->start_bus, | ||
187 | info->end_bus); | ||
188 | info->mcfg_added = false; | ||
189 | } | ||
190 | } | ||
191 | #else | ||
192 | static int __devinit setup_mcfg_map(struct pci_root_info *info, | ||
193 | u16 seg, u8 start, u8 end, | ||
194 | phys_addr_t addr) | ||
195 | { | ||
196 | return 0; | ||
197 | } | ||
198 | static void teardown_mcfg_map(struct pci_root_info *info) | ||
199 | { | ||
200 | } | ||
201 | #endif | ||
202 | |||
123 | static acpi_status | 203 | static acpi_status |
124 | resource_to_addr(struct acpi_resource *resource, | 204 | resource_to_addr(struct acpi_resource *resource, |
125 | struct acpi_resource_address64 *addr) | 205 | struct acpi_resource_address64 *addr) |
@@ -234,13 +314,6 @@ setup_resource(struct acpi_resource *acpi_res, void *data) | |||
234 | } | 314 | } |
235 | 315 | ||
236 | info->res_num++; | 316 | info->res_num++; |
237 | if (addr.translation_offset) | ||
238 | dev_info(&info->bridge->dev, "host bridge window %pR " | ||
239 | "(PCI address [%#llx-%#llx])\n", | ||
240 | res, res->start - addr.translation_offset, | ||
241 | res->end - addr.translation_offset); | ||
242 | else | ||
243 | dev_info(&info->bridge->dev, "host bridge window %pR\n", res); | ||
244 | 317 | ||
245 | return AE_OK; | 318 | return AE_OK; |
246 | } | 319 | } |
@@ -332,8 +405,11 @@ static void __release_pci_root_info(struct pci_root_info *info) | |||
332 | 405 | ||
333 | free_pci_root_info_res(info); | 406 | free_pci_root_info_res(info); |
334 | 407 | ||
408 | teardown_mcfg_map(info); | ||
409 | |||
335 | kfree(info); | 410 | kfree(info); |
336 | } | 411 | } |
412 | |||
337 | static void release_pci_root_info(struct pci_host_bridge *bridge) | 413 | static void release_pci_root_info(struct pci_host_bridge *bridge) |
338 | { | 414 | { |
339 | struct pci_root_info *info = bridge->release_data; | 415 | struct pci_root_info *info = bridge->release_data; |
@@ -347,7 +423,9 @@ probe_pci_root_info(struct pci_root_info *info, struct acpi_device *device, | |||
347 | { | 423 | { |
348 | size_t size; | 424 | size_t size; |
349 | 425 | ||
426 | sprintf(info->name, "PCI Bus %04x:%02x", domain, busnum); | ||
350 | info->bridge = device; | 427 | info->bridge = device; |
428 | |||
351 | info->res_num = 0; | 429 | info->res_num = 0; |
352 | acpi_walk_resources(device->handle, METHOD_NAME__CRS, count_resource, | 430 | acpi_walk_resources(device->handle, METHOD_NAME__CRS, count_resource, |
353 | info); | 431 | info); |
@@ -360,8 +438,6 @@ probe_pci_root_info(struct pci_root_info *info, struct acpi_device *device, | |||
360 | if (!info->res) | 438 | if (!info->res) |
361 | return; | 439 | return; |
362 | 440 | ||
363 | sprintf(info->name, "PCI Bus %04x:%02x", domain, busnum); | ||
364 | |||
365 | acpi_walk_resources(device->handle, METHOD_NAME__CRS, setup_resource, | 441 | acpi_walk_resources(device->handle, METHOD_NAME__CRS, setup_resource, |
366 | info); | 442 | info); |
367 | } | 443 | } |
@@ -373,7 +449,7 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) | |||
373 | int domain = root->segment; | 449 | int domain = root->segment; |
374 | int busnum = root->secondary.start; | 450 | int busnum = root->secondary.start; |
375 | LIST_HEAD(resources); | 451 | LIST_HEAD(resources); |
376 | struct pci_bus *bus; | 452 | struct pci_bus *bus = NULL; |
377 | struct pci_sysdata *sd; | 453 | struct pci_sysdata *sd; |
378 | int node; | 454 | int node; |
379 | #ifdef CONFIG_ACPI_NUMA | 455 | #ifdef CONFIG_ACPI_NUMA |
@@ -426,6 +502,8 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) | |||
426 | } else { | 502 | } else { |
427 | probe_pci_root_info(info, device, busnum, domain); | 503 | probe_pci_root_info(info, device, busnum, domain); |
428 | 504 | ||
505 | /* insert busn res at first */ | ||
506 | pci_add_resource(&resources, &root->secondary); | ||
429 | /* | 507 | /* |
430 | * _CRS with no apertures is normal, so only fall back to | 508 | * _CRS with no apertures is normal, so only fall back to |
431 | * defaults or native bridge info if we're ignoring _CRS. | 509 | * defaults or native bridge info if we're ignoring _CRS. |
@@ -437,10 +515,13 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) | |||
437 | x86_pci_root_bus_resources(busnum, &resources); | 515 | x86_pci_root_bus_resources(busnum, &resources); |
438 | } | 516 | } |
439 | 517 | ||
440 | bus = pci_create_root_bus(NULL, busnum, &pci_root_ops, sd, | 518 | if (!setup_mcfg_map(info, domain, (u8)root->secondary.start, |
441 | &resources); | 519 | (u8)root->secondary.end, root->mcfg_addr)) |
520 | bus = pci_create_root_bus(NULL, busnum, &pci_root_ops, | ||
521 | sd, &resources); | ||
522 | |||
442 | if (bus) { | 523 | if (bus) { |
443 | bus->subordinate = pci_scan_child_bus(bus); | 524 | pci_scan_child_bus(bus); |
444 | pci_set_host_bridge_release( | 525 | pci_set_host_bridge_release( |
445 | to_pci_host_bridge(bus->bridge), | 526 | to_pci_host_bridge(bus->bridge), |
446 | release_pci_root_info, info); | 527 | release_pci_root_info, info); |
diff --git a/arch/x86/pci/amd_bus.c b/arch/x86/pci/amd_bus.c index 5aed49bff058..e9e6ed5cdf94 100644 --- a/arch/x86/pci/amd_bus.c +++ b/arch/x86/pci/amd_bus.c | |||
@@ -121,7 +121,6 @@ static int __init early_fill_mp_bus_info(void) | |||
121 | link = (reg >> 8) & 0x03; | 121 | link = (reg >> 8) & 0x03; |
122 | 122 | ||
123 | info = alloc_pci_root_info(min_bus, max_bus, node, link); | 123 | info = alloc_pci_root_info(min_bus, max_bus, node, link); |
124 | sprintf(info->name, "PCI Bus #%02x", min_bus); | ||
125 | } | 124 | } |
126 | 125 | ||
127 | /* get the default node and link for left over res */ | 126 | /* get the default node and link for left over res */ |
@@ -300,9 +299,9 @@ static int __init early_fill_mp_bus_info(void) | |||
300 | int busnum; | 299 | int busnum; |
301 | struct pci_root_res *root_res; | 300 | struct pci_root_res *root_res; |
302 | 301 | ||
303 | busnum = info->bus_min; | 302 | busnum = info->busn.start; |
304 | printk(KERN_DEBUG "bus: [%02x, %02x] on node %x link %x\n", | 303 | printk(KERN_DEBUG "bus: %pR on node %x link %x\n", |
305 | info->bus_min, info->bus_max, info->node, info->link); | 304 | &info->busn, info->node, info->link); |
306 | list_for_each_entry(root_res, &info->resources, list) | 305 | list_for_each_entry(root_res, &info->resources, list) |
307 | printk(KERN_DEBUG "bus: %02x %pR\n", | 306 | printk(KERN_DEBUG "bus: %02x %pR\n", |
308 | busnum, &root_res->res); | 307 | busnum, &root_res->res); |
diff --git a/arch/x86/pci/bus_numa.c b/arch/x86/pci/bus_numa.c index 306579f7d0fd..d37e2fec97e5 100644 --- a/arch/x86/pci/bus_numa.c +++ b/arch/x86/pci/bus_numa.c | |||
@@ -14,7 +14,7 @@ static struct pci_root_info *x86_find_pci_root_info(int bus) | |||
14 | return NULL; | 14 | return NULL; |
15 | 15 | ||
16 | list_for_each_entry(info, &pci_root_infos, list) | 16 | list_for_each_entry(info, &pci_root_infos, list) |
17 | if (info->bus_min == bus) | 17 | if (info->busn.start == bus) |
18 | return info; | 18 | return info; |
19 | 19 | ||
20 | return NULL; | 20 | return NULL; |
@@ -24,6 +24,8 @@ void x86_pci_root_bus_resources(int bus, struct list_head *resources) | |||
24 | { | 24 | { |
25 | struct pci_root_info *info = x86_find_pci_root_info(bus); | 25 | struct pci_root_info *info = x86_find_pci_root_info(bus); |
26 | struct pci_root_res *root_res; | 26 | struct pci_root_res *root_res; |
27 | struct pci_host_bridge_window *window; | ||
28 | bool found = false; | ||
27 | 29 | ||
28 | if (!info) | 30 | if (!info) |
29 | goto default_resources; | 31 | goto default_resources; |
@@ -31,6 +33,16 @@ void x86_pci_root_bus_resources(int bus, struct list_head *resources) | |||
31 | printk(KERN_DEBUG "PCI: root bus %02x: hardware-probed resources\n", | 33 | printk(KERN_DEBUG "PCI: root bus %02x: hardware-probed resources\n", |
32 | bus); | 34 | bus); |
33 | 35 | ||
36 | /* already added by acpi ? */ | ||
37 | list_for_each_entry(window, resources, list) | ||
38 | if (window->res->flags & IORESOURCE_BUS) { | ||
39 | found = true; | ||
40 | break; | ||
41 | } | ||
42 | |||
43 | if (!found) | ||
44 | pci_add_resource(resources, &info->busn); | ||
45 | |||
34 | list_for_each_entry(root_res, &info->resources, list) { | 46 | list_for_each_entry(root_res, &info->resources, list) { |
35 | struct resource *res; | 47 | struct resource *res; |
36 | struct resource *root; | 48 | struct resource *root; |
@@ -66,9 +78,13 @@ struct pci_root_info __init *alloc_pci_root_info(int bus_min, int bus_max, | |||
66 | if (!info) | 78 | if (!info) |
67 | return info; | 79 | return info; |
68 | 80 | ||
81 | sprintf(info->name, "PCI Bus #%02x", bus_min); | ||
82 | |||
69 | INIT_LIST_HEAD(&info->resources); | 83 | INIT_LIST_HEAD(&info->resources); |
70 | info->bus_min = bus_min; | 84 | info->busn.name = info->name; |
71 | info->bus_max = bus_max; | 85 | info->busn.start = bus_min; |
86 | info->busn.end = bus_max; | ||
87 | info->busn.flags = IORESOURCE_BUS; | ||
72 | info->node = node; | 88 | info->node = node; |
73 | info->link = link; | 89 | info->link = link; |
74 | 90 | ||
diff --git a/arch/x86/pci/bus_numa.h b/arch/x86/pci/bus_numa.h index 226a466b2b2b..ff8f65b04574 100644 --- a/arch/x86/pci/bus_numa.h +++ b/arch/x86/pci/bus_numa.h | |||
@@ -13,8 +13,7 @@ struct pci_root_info { | |||
13 | struct list_head list; | 13 | struct list_head list; |
14 | char name[12]; | 14 | char name[12]; |
15 | struct list_head resources; | 15 | struct list_head resources; |
16 | int bus_min; | 16 | struct resource busn; |
17 | int bus_max; | ||
18 | int node; | 17 | int node; |
19 | int link; | 18 | int link; |
20 | }; | 19 | }; |
diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c index 0ad990a20d4a..720e973fc34a 100644 --- a/arch/x86/pci/common.c +++ b/arch/x86/pci/common.c | |||
@@ -494,7 +494,7 @@ int __init pcibios_init(void) | |||
494 | return 0; | 494 | return 0; |
495 | } | 495 | } |
496 | 496 | ||
497 | char * __devinit pcibios_setup(char *str) | 497 | char * __init pcibios_setup(char *str) |
498 | { | 498 | { |
499 | if (!strcmp(str, "off")) { | 499 | if (!strcmp(str, "off")) { |
500 | pci_probe = 0; | 500 | pci_probe = 0; |
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index 301e325992f6..937bcece7006 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c | |||
@@ -17,6 +17,8 @@ | |||
17 | #include <linux/bitmap.h> | 17 | #include <linux/bitmap.h> |
18 | #include <linux/dmi.h> | 18 | #include <linux/dmi.h> |
19 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
20 | #include <linux/mutex.h> | ||
21 | #include <linux/rculist.h> | ||
20 | #include <asm/e820.h> | 22 | #include <asm/e820.h> |
21 | #include <asm/pci_x86.h> | 23 | #include <asm/pci_x86.h> |
22 | #include <asm/acpi.h> | 24 | #include <asm/acpi.h> |
@@ -24,7 +26,9 @@ | |||
24 | #define PREFIX "PCI: " | 26 | #define PREFIX "PCI: " |
25 | 27 | ||
26 | /* Indicate if the mmcfg resources have been placed into the resource table. */ | 28 | /* Indicate if the mmcfg resources have been placed into the resource table. */ |
27 | static int __initdata pci_mmcfg_resources_inserted; | 29 | static bool pci_mmcfg_running_state; |
30 | static bool pci_mmcfg_arch_init_failed; | ||
31 | static DEFINE_MUTEX(pci_mmcfg_lock); | ||
28 | 32 | ||
29 | LIST_HEAD(pci_mmcfg_list); | 33 | LIST_HEAD(pci_mmcfg_list); |
30 | 34 | ||
@@ -45,24 +49,25 @@ static __init void free_all_mmcfg(void) | |||
45 | pci_mmconfig_remove(cfg); | 49 | pci_mmconfig_remove(cfg); |
46 | } | 50 | } |
47 | 51 | ||
48 | static __init void list_add_sorted(struct pci_mmcfg_region *new) | 52 | static __devinit void list_add_sorted(struct pci_mmcfg_region *new) |
49 | { | 53 | { |
50 | struct pci_mmcfg_region *cfg; | 54 | struct pci_mmcfg_region *cfg; |
51 | 55 | ||
52 | /* keep list sorted by segment and starting bus number */ | 56 | /* keep list sorted by segment and starting bus number */ |
53 | list_for_each_entry(cfg, &pci_mmcfg_list, list) { | 57 | list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) { |
54 | if (cfg->segment > new->segment || | 58 | if (cfg->segment > new->segment || |
55 | (cfg->segment == new->segment && | 59 | (cfg->segment == new->segment && |
56 | cfg->start_bus >= new->start_bus)) { | 60 | cfg->start_bus >= new->start_bus)) { |
57 | list_add_tail(&new->list, &cfg->list); | 61 | list_add_tail_rcu(&new->list, &cfg->list); |
58 | return; | 62 | return; |
59 | } | 63 | } |
60 | } | 64 | } |
61 | list_add_tail(&new->list, &pci_mmcfg_list); | 65 | list_add_tail_rcu(&new->list, &pci_mmcfg_list); |
62 | } | 66 | } |
63 | 67 | ||
64 | static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, | 68 | static __devinit struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, |
65 | int end, u64 addr) | 69 | int start, |
70 | int end, u64 addr) | ||
66 | { | 71 | { |
67 | struct pci_mmcfg_region *new; | 72 | struct pci_mmcfg_region *new; |
68 | struct resource *res; | 73 | struct resource *res; |
@@ -79,8 +84,6 @@ static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, | |||
79 | new->start_bus = start; | 84 | new->start_bus = start; |
80 | new->end_bus = end; | 85 | new->end_bus = end; |
81 | 86 | ||
82 | list_add_sorted(new); | ||
83 | |||
84 | res = &new->res; | 87 | res = &new->res; |
85 | res->start = addr + PCI_MMCFG_BUS_OFFSET(start); | 88 | res->start = addr + PCI_MMCFG_BUS_OFFSET(start); |
86 | res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1; | 89 | res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1; |
@@ -89,9 +92,25 @@ static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, | |||
89 | "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end); | 92 | "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end); |
90 | res->name = new->name; | 93 | res->name = new->name; |
91 | 94 | ||
92 | printk(KERN_INFO PREFIX "MMCONFIG for domain %04x [bus %02x-%02x] at " | 95 | return new; |
93 | "%pR (base %#lx)\n", segment, start, end, &new->res, | 96 | } |
94 | (unsigned long) addr); | 97 | |
98 | static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, | ||
99 | int end, u64 addr) | ||
100 | { | ||
101 | struct pci_mmcfg_region *new; | ||
102 | |||
103 | new = pci_mmconfig_alloc(segment, start, end, addr); | ||
104 | if (new) { | ||
105 | mutex_lock(&pci_mmcfg_lock); | ||
106 | list_add_sorted(new); | ||
107 | mutex_unlock(&pci_mmcfg_lock); | ||
108 | |||
109 | pr_info(PREFIX | ||
110 | "MMCONFIG for domain %04x [bus %02x-%02x] at %pR " | ||
111 | "(base %#lx)\n", | ||
112 | segment, start, end, &new->res, (unsigned long)addr); | ||
113 | } | ||
95 | 114 | ||
96 | return new; | 115 | return new; |
97 | } | 116 | } |
@@ -100,7 +119,7 @@ struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus) | |||
100 | { | 119 | { |
101 | struct pci_mmcfg_region *cfg; | 120 | struct pci_mmcfg_region *cfg; |
102 | 121 | ||
103 | list_for_each_entry(cfg, &pci_mmcfg_list, list) | 122 | list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) |
104 | if (cfg->segment == segment && | 123 | if (cfg->segment == segment && |
105 | cfg->start_bus <= bus && bus <= cfg->end_bus) | 124 | cfg->start_bus <= bus && bus <= cfg->end_bus) |
106 | return cfg; | 125 | return cfg; |
@@ -343,8 +362,7 @@ static int __init pci_mmcfg_check_hostbridge(void) | |||
343 | name = pci_mmcfg_probes[i].probe(); | 362 | name = pci_mmcfg_probes[i].probe(); |
344 | 363 | ||
345 | if (name) | 364 | if (name) |
346 | printk(KERN_INFO PREFIX "%s with MMCONFIG support\n", | 365 | pr_info(PREFIX "%s with MMCONFIG support\n", name); |
347 | name); | ||
348 | } | 366 | } |
349 | 367 | ||
350 | /* some end_bus_number is crazy, fix it */ | 368 | /* some end_bus_number is crazy, fix it */ |
@@ -353,19 +371,8 @@ static int __init pci_mmcfg_check_hostbridge(void) | |||
353 | return !list_empty(&pci_mmcfg_list); | 371 | return !list_empty(&pci_mmcfg_list); |
354 | } | 372 | } |
355 | 373 | ||
356 | static void __init pci_mmcfg_insert_resources(void) | 374 | static acpi_status __devinit check_mcfg_resource(struct acpi_resource *res, |
357 | { | 375 | void *data) |
358 | struct pci_mmcfg_region *cfg; | ||
359 | |||
360 | list_for_each_entry(cfg, &pci_mmcfg_list, list) | ||
361 | insert_resource(&iomem_resource, &cfg->res); | ||
362 | |||
363 | /* Mark that the resources have been inserted. */ | ||
364 | pci_mmcfg_resources_inserted = 1; | ||
365 | } | ||
366 | |||
367 | static acpi_status __init check_mcfg_resource(struct acpi_resource *res, | ||
368 | void *data) | ||
369 | { | 376 | { |
370 | struct resource *mcfg_res = data; | 377 | struct resource *mcfg_res = data; |
371 | struct acpi_resource_address64 address; | 378 | struct acpi_resource_address64 address; |
@@ -401,8 +408,8 @@ static acpi_status __init check_mcfg_resource(struct acpi_resource *res, | |||
401 | return AE_OK; | 408 | return AE_OK; |
402 | } | 409 | } |
403 | 410 | ||
404 | static acpi_status __init find_mboard_resource(acpi_handle handle, u32 lvl, | 411 | static acpi_status __devinit find_mboard_resource(acpi_handle handle, u32 lvl, |
405 | void *context, void **rv) | 412 | void *context, void **rv) |
406 | { | 413 | { |
407 | struct resource *mcfg_res = context; | 414 | struct resource *mcfg_res = context; |
408 | 415 | ||
@@ -415,7 +422,7 @@ static acpi_status __init find_mboard_resource(acpi_handle handle, u32 lvl, | |||
415 | return AE_OK; | 422 | return AE_OK; |
416 | } | 423 | } |
417 | 424 | ||
418 | static int __init is_acpi_reserved(u64 start, u64 end, unsigned not_used) | 425 | static int __devinit is_acpi_reserved(u64 start, u64 end, unsigned not_used) |
419 | { | 426 | { |
420 | struct resource mcfg_res; | 427 | struct resource mcfg_res; |
421 | 428 | ||
@@ -434,13 +441,15 @@ static int __init is_acpi_reserved(u64 start, u64 end, unsigned not_used) | |||
434 | 441 | ||
435 | typedef int (*check_reserved_t)(u64 start, u64 end, unsigned type); | 442 | typedef int (*check_reserved_t)(u64 start, u64 end, unsigned type); |
436 | 443 | ||
437 | static int __init is_mmconf_reserved(check_reserved_t is_reserved, | 444 | static int __ref is_mmconf_reserved(check_reserved_t is_reserved, |
438 | struct pci_mmcfg_region *cfg, int with_e820) | 445 | struct pci_mmcfg_region *cfg, |
446 | struct device *dev, int with_e820) | ||
439 | { | 447 | { |
440 | u64 addr = cfg->res.start; | 448 | u64 addr = cfg->res.start; |
441 | u64 size = resource_size(&cfg->res); | 449 | u64 size = resource_size(&cfg->res); |
442 | u64 old_size = size; | 450 | u64 old_size = size; |
443 | int valid = 0, num_buses; | 451 | int num_buses; |
452 | char *method = with_e820 ? "E820" : "ACPI motherboard resources"; | ||
444 | 453 | ||
445 | while (!is_reserved(addr, addr + size, E820_RESERVED)) { | 454 | while (!is_reserved(addr, addr + size, E820_RESERVED)) { |
446 | size >>= 1; | 455 | size >>= 1; |
@@ -448,30 +457,76 @@ static int __init is_mmconf_reserved(check_reserved_t is_reserved, | |||
448 | break; | 457 | break; |
449 | } | 458 | } |
450 | 459 | ||
451 | if (size >= (16UL<<20) || size == old_size) { | 460 | if (size < (16UL<<20) && size != old_size) |
452 | printk(KERN_INFO PREFIX "MMCONFIG at %pR reserved in %s\n", | 461 | return 0; |
453 | &cfg->res, | 462 | |
454 | with_e820 ? "E820" : "ACPI motherboard resources"); | 463 | if (dev) |
455 | valid = 1; | 464 | dev_info(dev, "MMCONFIG at %pR reserved in %s\n", |
456 | 465 | &cfg->res, method); | |
457 | if (old_size != size) { | 466 | else |
458 | /* update end_bus */ | 467 | pr_info(PREFIX "MMCONFIG at %pR reserved in %s\n", |
459 | cfg->end_bus = cfg->start_bus + ((size>>20) - 1); | 468 | &cfg->res, method); |
460 | num_buses = cfg->end_bus - cfg->start_bus + 1; | 469 | |
461 | cfg->res.end = cfg->res.start + | 470 | if (old_size != size) { |
462 | PCI_MMCFG_BUS_OFFSET(num_buses) - 1; | 471 | /* update end_bus */ |
463 | snprintf(cfg->name, PCI_MMCFG_RESOURCE_NAME_LEN, | 472 | cfg->end_bus = cfg->start_bus + ((size>>20) - 1); |
464 | "PCI MMCONFIG %04x [bus %02x-%02x]", | 473 | num_buses = cfg->end_bus - cfg->start_bus + 1; |
465 | cfg->segment, cfg->start_bus, cfg->end_bus); | 474 | cfg->res.end = cfg->res.start + |
466 | printk(KERN_INFO PREFIX | 475 | PCI_MMCFG_BUS_OFFSET(num_buses) - 1; |
467 | "MMCONFIG for %04x [bus%02x-%02x] " | 476 | snprintf(cfg->name, PCI_MMCFG_RESOURCE_NAME_LEN, |
468 | "at %pR (base %#lx) (size reduced!)\n", | 477 | "PCI MMCONFIG %04x [bus %02x-%02x]", |
469 | cfg->segment, cfg->start_bus, cfg->end_bus, | 478 | cfg->segment, cfg->start_bus, cfg->end_bus); |
470 | &cfg->res, (unsigned long) cfg->address); | 479 | |
471 | } | 480 | if (dev) |
481 | dev_info(dev, | ||
482 | "MMCONFIG " | ||
483 | "at %pR (base %#lx) (size reduced!)\n", | ||
484 | &cfg->res, (unsigned long) cfg->address); | ||
485 | else | ||
486 | pr_info(PREFIX | ||
487 | "MMCONFIG for %04x [bus%02x-%02x] " | ||
488 | "at %pR (base %#lx) (size reduced!)\n", | ||
489 | cfg->segment, cfg->start_bus, cfg->end_bus, | ||
490 | &cfg->res, (unsigned long) cfg->address); | ||
472 | } | 491 | } |
473 | 492 | ||
474 | return valid; | 493 | return 1; |
494 | } | ||
495 | |||
496 | static int __ref pci_mmcfg_check_reserved(struct device *dev, | ||
497 | struct pci_mmcfg_region *cfg, int early) | ||
498 | { | ||
499 | if (!early && !acpi_disabled) { | ||
500 | if (is_mmconf_reserved(is_acpi_reserved, cfg, dev, 0)) | ||
501 | return 1; | ||
502 | |||
503 | if (dev) | ||
504 | dev_info(dev, FW_INFO | ||
505 | "MMCONFIG at %pR not reserved in " | ||
506 | "ACPI motherboard resources\n", | ||
507 | &cfg->res); | ||
508 | else | ||
509 | pr_info(FW_INFO PREFIX | ||
510 | "MMCONFIG at %pR not reserved in " | ||
511 | "ACPI motherboard resources\n", | ||
512 | &cfg->res); | ||
513 | } | ||
514 | |||
515 | /* | ||
516 | * e820_all_mapped() is marked as __init. | ||
517 | * All entries from ACPI MCFG table have been checked at boot time. | ||
518 | * For MCFG information constructed from hotpluggable host bridge's | ||
519 | * _CBA method, just assume it's reserved. | ||
520 | */ | ||
521 | if (pci_mmcfg_running_state) | ||
522 | return 1; | ||
523 | |||
524 | /* Don't try to do this check unless configuration | ||
525 | type 1 is available. how about type 2 ?*/ | ||
526 | if (raw_pci_ops) | ||
527 | return is_mmconf_reserved(e820_all_mapped, cfg, dev, 1); | ||
528 | |||
529 | return 0; | ||
475 | } | 530 | } |
476 | 531 | ||
477 | static void __init pci_mmcfg_reject_broken(int early) | 532 | static void __init pci_mmcfg_reject_broken(int early) |
@@ -479,38 +534,14 @@ static void __init pci_mmcfg_reject_broken(int early) | |||
479 | struct pci_mmcfg_region *cfg; | 534 | struct pci_mmcfg_region *cfg; |
480 | 535 | ||
481 | list_for_each_entry(cfg, &pci_mmcfg_list, list) { | 536 | list_for_each_entry(cfg, &pci_mmcfg_list, list) { |
482 | int valid = 0; | 537 | if (pci_mmcfg_check_reserved(NULL, cfg, early) == 0) { |
483 | 538 | pr_info(PREFIX "not using MMCONFIG\n"); | |
484 | if (!early && !acpi_disabled) { | 539 | free_all_mmcfg(); |
485 | valid = is_mmconf_reserved(is_acpi_reserved, cfg, 0); | 540 | return; |
486 | |||
487 | if (valid) | ||
488 | continue; | ||
489 | else | ||
490 | printk(KERN_ERR FW_BUG PREFIX | ||
491 | "MMCONFIG at %pR not reserved in " | ||
492 | "ACPI motherboard resources\n", | ||
493 | &cfg->res); | ||
494 | } | 541 | } |
495 | |||
496 | /* Don't try to do this check unless configuration | ||
497 | type 1 is available. how about type 2 ?*/ | ||
498 | if (raw_pci_ops) | ||
499 | valid = is_mmconf_reserved(e820_all_mapped, cfg, 1); | ||
500 | |||
501 | if (!valid) | ||
502 | goto reject; | ||
503 | } | 542 | } |
504 | |||
505 | return; | ||
506 | |||
507 | reject: | ||
508 | printk(KERN_INFO PREFIX "not using MMCONFIG\n"); | ||
509 | free_all_mmcfg(); | ||
510 | } | 543 | } |
511 | 544 | ||
512 | static int __initdata known_bridge; | ||
513 | |||
514 | static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, | 545 | static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, |
515 | struct acpi_mcfg_allocation *cfg) | 546 | struct acpi_mcfg_allocation *cfg) |
516 | { | 547 | { |
@@ -529,7 +560,7 @@ static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, | |||
529 | return 0; | 560 | return 0; |
530 | } | 561 | } |
531 | 562 | ||
532 | printk(KERN_ERR PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " | 563 | pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " |
533 | "is above 4GB, ignored\n", cfg->pci_segment, | 564 | "is above 4GB, ignored\n", cfg->pci_segment, |
534 | cfg->start_bus_number, cfg->end_bus_number, cfg->address); | 565 | cfg->start_bus_number, cfg->end_bus_number, cfg->address); |
535 | return -EINVAL; | 566 | return -EINVAL; |
@@ -556,7 +587,7 @@ static int __init pci_parse_mcfg(struct acpi_table_header *header) | |||
556 | i -= sizeof(struct acpi_mcfg_allocation); | 587 | i -= sizeof(struct acpi_mcfg_allocation); |
557 | }; | 588 | }; |
558 | if (entries == 0) { | 589 | if (entries == 0) { |
559 | printk(KERN_ERR PREFIX "MMCONFIG has no entries\n"); | 590 | pr_err(PREFIX "MMCONFIG has no entries\n"); |
560 | return -ENODEV; | 591 | return -ENODEV; |
561 | } | 592 | } |
562 | 593 | ||
@@ -570,8 +601,7 @@ static int __init pci_parse_mcfg(struct acpi_table_header *header) | |||
570 | 601 | ||
571 | if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number, | 602 | if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number, |
572 | cfg->end_bus_number, cfg->address) == NULL) { | 603 | cfg->end_bus_number, cfg->address) == NULL) { |
573 | printk(KERN_WARNING PREFIX | 604 | pr_warn(PREFIX "no memory for MCFG entries\n"); |
574 | "no memory for MCFG entries\n"); | ||
575 | free_all_mmcfg(); | 605 | free_all_mmcfg(); |
576 | return -ENOMEM; | 606 | return -ENOMEM; |
577 | } | 607 | } |
@@ -582,28 +612,7 @@ static int __init pci_parse_mcfg(struct acpi_table_header *header) | |||
582 | 612 | ||
583 | static void __init __pci_mmcfg_init(int early) | 613 | static void __init __pci_mmcfg_init(int early) |
584 | { | 614 | { |
585 | /* MMCONFIG disabled */ | ||
586 | if ((pci_probe & PCI_PROBE_MMCONF) == 0) | ||
587 | return; | ||
588 | |||
589 | /* MMCONFIG already enabled */ | ||
590 | if (!early && !(pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF)) | ||
591 | return; | ||
592 | |||
593 | /* for late to exit */ | ||
594 | if (known_bridge) | ||
595 | return; | ||
596 | |||
597 | if (early) { | ||
598 | if (pci_mmcfg_check_hostbridge()) | ||
599 | known_bridge = 1; | ||
600 | } | ||
601 | |||
602 | if (!known_bridge) | ||
603 | acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); | ||
604 | |||
605 | pci_mmcfg_reject_broken(early); | 615 | pci_mmcfg_reject_broken(early); |
606 | |||
607 | if (list_empty(&pci_mmcfg_list)) | 616 | if (list_empty(&pci_mmcfg_list)) |
608 | return; | 617 | return; |
609 | 618 | ||
@@ -620,33 +629,48 @@ static void __init __pci_mmcfg_init(int early) | |||
620 | if (pci_mmcfg_arch_init()) | 629 | if (pci_mmcfg_arch_init()) |
621 | pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF; | 630 | pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF; |
622 | else { | 631 | else { |
623 | /* | 632 | free_all_mmcfg(); |
624 | * Signal not to attempt to insert mmcfg resources because | 633 | pci_mmcfg_arch_init_failed = true; |
625 | * the architecture mmcfg setup could not initialize. | ||
626 | */ | ||
627 | pci_mmcfg_resources_inserted = 1; | ||
628 | } | 634 | } |
629 | } | 635 | } |
630 | 636 | ||
637 | static int __initdata known_bridge; | ||
638 | |||
631 | void __init pci_mmcfg_early_init(void) | 639 | void __init pci_mmcfg_early_init(void) |
632 | { | 640 | { |
633 | __pci_mmcfg_init(1); | 641 | if (pci_probe & PCI_PROBE_MMCONF) { |
642 | if (pci_mmcfg_check_hostbridge()) | ||
643 | known_bridge = 1; | ||
644 | else | ||
645 | acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); | ||
646 | __pci_mmcfg_init(1); | ||
647 | } | ||
634 | } | 648 | } |
635 | 649 | ||
636 | void __init pci_mmcfg_late_init(void) | 650 | void __init pci_mmcfg_late_init(void) |
637 | { | 651 | { |
638 | __pci_mmcfg_init(0); | 652 | /* MMCONFIG disabled */ |
653 | if ((pci_probe & PCI_PROBE_MMCONF) == 0) | ||
654 | return; | ||
655 | |||
656 | if (known_bridge) | ||
657 | return; | ||
658 | |||
659 | /* MMCONFIG hasn't been enabled yet, try again */ | ||
660 | if (pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF) { | ||
661 | acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); | ||
662 | __pci_mmcfg_init(0); | ||
663 | } | ||
639 | } | 664 | } |
640 | 665 | ||
641 | static int __init pci_mmcfg_late_insert_resources(void) | 666 | static int __init pci_mmcfg_late_insert_resources(void) |
642 | { | 667 | { |
643 | /* | 668 | struct pci_mmcfg_region *cfg; |
644 | * If resources are already inserted or we are not using MMCONFIG, | 669 | |
645 | * don't insert the resources. | 670 | pci_mmcfg_running_state = true; |
646 | */ | 671 | |
647 | if ((pci_mmcfg_resources_inserted == 1) || | 672 | /* If we are not using MMCONFIG, don't insert the resources. */ |
648 | (pci_probe & PCI_PROBE_MMCONF) == 0 || | 673 | if ((pci_probe & PCI_PROBE_MMCONF) == 0) |
649 | list_empty(&pci_mmcfg_list)) | ||
650 | return 1; | 674 | return 1; |
651 | 675 | ||
652 | /* | 676 | /* |
@@ -654,7 +678,9 @@ static int __init pci_mmcfg_late_insert_resources(void) | |||
654 | * marked so it won't cause request errors when __request_region is | 678 | * marked so it won't cause request errors when __request_region is |
655 | * called. | 679 | * called. |
656 | */ | 680 | */ |
657 | pci_mmcfg_insert_resources(); | 681 | list_for_each_entry(cfg, &pci_mmcfg_list, list) |
682 | if (!cfg->res.parent) | ||
683 | insert_resource(&iomem_resource, &cfg->res); | ||
658 | 684 | ||
659 | return 0; | 685 | return 0; |
660 | } | 686 | } |
@@ -665,3 +691,101 @@ static int __init pci_mmcfg_late_insert_resources(void) | |||
665 | * with other system resources. | 691 | * with other system resources. |
666 | */ | 692 | */ |
667 | late_initcall(pci_mmcfg_late_insert_resources); | 693 | late_initcall(pci_mmcfg_late_insert_resources); |
694 | |||
695 | /* Add MMCFG information for host bridges */ | ||
696 | int __devinit pci_mmconfig_insert(struct device *dev, | ||
697 | u16 seg, u8 start, u8 end, | ||
698 | phys_addr_t addr) | ||
699 | { | ||
700 | int rc; | ||
701 | struct resource *tmp = NULL; | ||
702 | struct pci_mmcfg_region *cfg; | ||
703 | |||
704 | if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed) | ||
705 | return -ENODEV; | ||
706 | |||
707 | if (start > end) | ||
708 | return -EINVAL; | ||
709 | |||
710 | mutex_lock(&pci_mmcfg_lock); | ||
711 | cfg = pci_mmconfig_lookup(seg, start); | ||
712 | if (cfg) { | ||
713 | if (cfg->end_bus < end) | ||
714 | dev_info(dev, FW_INFO | ||
715 | "MMCONFIG for " | ||
716 | "domain %04x [bus %02x-%02x] " | ||
717 | "only partially covers this bridge\n", | ||
718 | cfg->segment, cfg->start_bus, cfg->end_bus); | ||
719 | mutex_unlock(&pci_mmcfg_lock); | ||
720 | return -EEXIST; | ||
721 | } | ||
722 | |||
723 | if (!addr) { | ||
724 | mutex_unlock(&pci_mmcfg_lock); | ||
725 | return -EINVAL; | ||
726 | } | ||
727 | |||
728 | rc = -EBUSY; | ||
729 | cfg = pci_mmconfig_alloc(seg, start, end, addr); | ||
730 | if (cfg == NULL) { | ||
731 | dev_warn(dev, "fail to add MMCONFIG (out of memory)\n"); | ||
732 | rc = -ENOMEM; | ||
733 | } else if (!pci_mmcfg_check_reserved(dev, cfg, 0)) { | ||
734 | dev_warn(dev, FW_BUG "MMCONFIG %pR isn't reserved\n", | ||
735 | &cfg->res); | ||
736 | } else { | ||
737 | /* Insert resource if it's not in boot stage */ | ||
738 | if (pci_mmcfg_running_state) | ||
739 | tmp = insert_resource_conflict(&iomem_resource, | ||
740 | &cfg->res); | ||
741 | |||
742 | if (tmp) { | ||
743 | dev_warn(dev, | ||
744 | "MMCONFIG %pR conflicts with " | ||
745 | "%s %pR\n", | ||
746 | &cfg->res, tmp->name, tmp); | ||
747 | } else if (pci_mmcfg_arch_map(cfg)) { | ||
748 | dev_warn(dev, "fail to map MMCONFIG %pR.\n", | ||
749 | &cfg->res); | ||
750 | } else { | ||
751 | list_add_sorted(cfg); | ||
752 | dev_info(dev, "MMCONFIG at %pR (base %#lx)\n", | ||
753 | &cfg->res, (unsigned long)addr); | ||
754 | cfg = NULL; | ||
755 | rc = 0; | ||
756 | } | ||
757 | } | ||
758 | |||
759 | if (cfg) { | ||
760 | if (cfg->res.parent) | ||
761 | release_resource(&cfg->res); | ||
762 | kfree(cfg); | ||
763 | } | ||
764 | |||
765 | mutex_unlock(&pci_mmcfg_lock); | ||
766 | |||
767 | return rc; | ||
768 | } | ||
769 | |||
770 | /* Delete MMCFG information for host bridges */ | ||
771 | int pci_mmconfig_delete(u16 seg, u8 start, u8 end) | ||
772 | { | ||
773 | struct pci_mmcfg_region *cfg; | ||
774 | |||
775 | mutex_lock(&pci_mmcfg_lock); | ||
776 | list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) | ||
777 | if (cfg->segment == seg && cfg->start_bus == start && | ||
778 | cfg->end_bus == end) { | ||
779 | list_del_rcu(&cfg->list); | ||
780 | synchronize_rcu(); | ||
781 | pci_mmcfg_arch_unmap(cfg); | ||
782 | if (cfg->res.parent) | ||
783 | release_resource(&cfg->res); | ||
784 | mutex_unlock(&pci_mmcfg_lock); | ||
785 | kfree(cfg); | ||
786 | return 0; | ||
787 | } | ||
788 | mutex_unlock(&pci_mmcfg_lock); | ||
789 | |||
790 | return -ENOENT; | ||
791 | } | ||
diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c index 5372e86834c0..db63ac23e3d9 100644 --- a/arch/x86/pci/mmconfig_32.c +++ b/arch/x86/pci/mmconfig_32.c | |||
@@ -11,6 +11,7 @@ | |||
11 | 11 | ||
12 | #include <linux/pci.h> | 12 | #include <linux/pci.h> |
13 | #include <linux/init.h> | 13 | #include <linux/init.h> |
14 | #include <linux/rcupdate.h> | ||
14 | #include <asm/e820.h> | 15 | #include <asm/e820.h> |
15 | #include <asm/pci_x86.h> | 16 | #include <asm/pci_x86.h> |
16 | #include <acpi/acpi.h> | 17 | #include <acpi/acpi.h> |
@@ -60,9 +61,12 @@ err: *value = -1; | |||
60 | return -EINVAL; | 61 | return -EINVAL; |
61 | } | 62 | } |
62 | 63 | ||
64 | rcu_read_lock(); | ||
63 | base = get_base_addr(seg, bus, devfn); | 65 | base = get_base_addr(seg, bus, devfn); |
64 | if (!base) | 66 | if (!base) { |
67 | rcu_read_unlock(); | ||
65 | goto err; | 68 | goto err; |
69 | } | ||
66 | 70 | ||
67 | raw_spin_lock_irqsave(&pci_config_lock, flags); | 71 | raw_spin_lock_irqsave(&pci_config_lock, flags); |
68 | 72 | ||
@@ -80,6 +84,7 @@ err: *value = -1; | |||
80 | break; | 84 | break; |
81 | } | 85 | } |
82 | raw_spin_unlock_irqrestore(&pci_config_lock, flags); | 86 | raw_spin_unlock_irqrestore(&pci_config_lock, flags); |
87 | rcu_read_unlock(); | ||
83 | 88 | ||
84 | return 0; | 89 | return 0; |
85 | } | 90 | } |
@@ -93,9 +98,12 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus, | |||
93 | if ((bus > 255) || (devfn > 255) || (reg > 4095)) | 98 | if ((bus > 255) || (devfn > 255) || (reg > 4095)) |
94 | return -EINVAL; | 99 | return -EINVAL; |
95 | 100 | ||
101 | rcu_read_lock(); | ||
96 | base = get_base_addr(seg, bus, devfn); | 102 | base = get_base_addr(seg, bus, devfn); |
97 | if (!base) | 103 | if (!base) { |
104 | rcu_read_unlock(); | ||
98 | return -EINVAL; | 105 | return -EINVAL; |
106 | } | ||
99 | 107 | ||
100 | raw_spin_lock_irqsave(&pci_config_lock, flags); | 108 | raw_spin_lock_irqsave(&pci_config_lock, flags); |
101 | 109 | ||
@@ -113,11 +121,12 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus, | |||
113 | break; | 121 | break; |
114 | } | 122 | } |
115 | raw_spin_unlock_irqrestore(&pci_config_lock, flags); | 123 | raw_spin_unlock_irqrestore(&pci_config_lock, flags); |
124 | rcu_read_unlock(); | ||
116 | 125 | ||
117 | return 0; | 126 | return 0; |
118 | } | 127 | } |
119 | 128 | ||
120 | static const struct pci_raw_ops pci_mmcfg = { | 129 | const struct pci_raw_ops pci_mmcfg = { |
121 | .read = pci_mmcfg_read, | 130 | .read = pci_mmcfg_read, |
122 | .write = pci_mmcfg_write, | 131 | .write = pci_mmcfg_write, |
123 | }; | 132 | }; |
@@ -132,3 +141,18 @@ int __init pci_mmcfg_arch_init(void) | |||
132 | void __init pci_mmcfg_arch_free(void) | 141 | void __init pci_mmcfg_arch_free(void) |
133 | { | 142 | { |
134 | } | 143 | } |
144 | |||
145 | int __devinit pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg) | ||
146 | { | ||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg) | ||
151 | { | ||
152 | unsigned long flags; | ||
153 | |||
154 | /* Invalidate the cached mmcfg map entry. */ | ||
155 | raw_spin_lock_irqsave(&pci_config_lock, flags); | ||
156 | mmcfg_last_accessed_device = 0; | ||
157 | raw_spin_unlock_irqrestore(&pci_config_lock, flags); | ||
158 | } | ||
diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c index 915a493502cb..d4ebd07c306d 100644 --- a/arch/x86/pci/mmconfig_64.c +++ b/arch/x86/pci/mmconfig_64.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include <linux/init.h> | 9 | #include <linux/init.h> |
10 | #include <linux/acpi.h> | 10 | #include <linux/acpi.h> |
11 | #include <linux/bitmap.h> | 11 | #include <linux/bitmap.h> |
12 | #include <linux/rcupdate.h> | ||
12 | #include <asm/e820.h> | 13 | #include <asm/e820.h> |
13 | #include <asm/pci_x86.h> | 14 | #include <asm/pci_x86.h> |
14 | 15 | ||
@@ -34,9 +35,12 @@ err: *value = -1; | |||
34 | return -EINVAL; | 35 | return -EINVAL; |
35 | } | 36 | } |
36 | 37 | ||
38 | rcu_read_lock(); | ||
37 | addr = pci_dev_base(seg, bus, devfn); | 39 | addr = pci_dev_base(seg, bus, devfn); |
38 | if (!addr) | 40 | if (!addr) { |
41 | rcu_read_unlock(); | ||
39 | goto err; | 42 | goto err; |
43 | } | ||
40 | 44 | ||
41 | switch (len) { | 45 | switch (len) { |
42 | case 1: | 46 | case 1: |
@@ -49,6 +53,7 @@ err: *value = -1; | |||
49 | *value = mmio_config_readl(addr + reg); | 53 | *value = mmio_config_readl(addr + reg); |
50 | break; | 54 | break; |
51 | } | 55 | } |
56 | rcu_read_unlock(); | ||
52 | 57 | ||
53 | return 0; | 58 | return 0; |
54 | } | 59 | } |
@@ -62,9 +67,12 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus, | |||
62 | if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) | 67 | if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) |
63 | return -EINVAL; | 68 | return -EINVAL; |
64 | 69 | ||
70 | rcu_read_lock(); | ||
65 | addr = pci_dev_base(seg, bus, devfn); | 71 | addr = pci_dev_base(seg, bus, devfn); |
66 | if (!addr) | 72 | if (!addr) { |
73 | rcu_read_unlock(); | ||
67 | return -EINVAL; | 74 | return -EINVAL; |
75 | } | ||
68 | 76 | ||
69 | switch (len) { | 77 | switch (len) { |
70 | case 1: | 78 | case 1: |
@@ -77,16 +85,17 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus, | |||
77 | mmio_config_writel(addr + reg, value); | 85 | mmio_config_writel(addr + reg, value); |
78 | break; | 86 | break; |
79 | } | 87 | } |
88 | rcu_read_unlock(); | ||
80 | 89 | ||
81 | return 0; | 90 | return 0; |
82 | } | 91 | } |
83 | 92 | ||
84 | static const struct pci_raw_ops pci_mmcfg = { | 93 | const struct pci_raw_ops pci_mmcfg = { |
85 | .read = pci_mmcfg_read, | 94 | .read = pci_mmcfg_read, |
86 | .write = pci_mmcfg_write, | 95 | .write = pci_mmcfg_write, |
87 | }; | 96 | }; |
88 | 97 | ||
89 | static void __iomem * __init mcfg_ioremap(struct pci_mmcfg_region *cfg) | 98 | static void __iomem * __devinit mcfg_ioremap(struct pci_mmcfg_region *cfg) |
90 | { | 99 | { |
91 | void __iomem *addr; | 100 | void __iomem *addr; |
92 | u64 start, size; | 101 | u64 start, size; |
@@ -105,16 +114,14 @@ int __init pci_mmcfg_arch_init(void) | |||
105 | { | 114 | { |
106 | struct pci_mmcfg_region *cfg; | 115 | struct pci_mmcfg_region *cfg; |
107 | 116 | ||
108 | list_for_each_entry(cfg, &pci_mmcfg_list, list) { | 117 | list_for_each_entry(cfg, &pci_mmcfg_list, list) |
109 | cfg->virt = mcfg_ioremap(cfg); | 118 | if (pci_mmcfg_arch_map(cfg)) { |
110 | if (!cfg->virt) { | ||
111 | printk(KERN_ERR PREFIX "can't map MMCONFIG at %pR\n", | ||
112 | &cfg->res); | ||
113 | pci_mmcfg_arch_free(); | 119 | pci_mmcfg_arch_free(); |
114 | return 0; | 120 | return 0; |
115 | } | 121 | } |
116 | } | 122 | |
117 | raw_pci_ext_ops = &pci_mmcfg; | 123 | raw_pci_ext_ops = &pci_mmcfg; |
124 | |||
118 | return 1; | 125 | return 1; |
119 | } | 126 | } |
120 | 127 | ||
@@ -122,10 +129,25 @@ void __init pci_mmcfg_arch_free(void) | |||
122 | { | 129 | { |
123 | struct pci_mmcfg_region *cfg; | 130 | struct pci_mmcfg_region *cfg; |
124 | 131 | ||
125 | list_for_each_entry(cfg, &pci_mmcfg_list, list) { | 132 | list_for_each_entry(cfg, &pci_mmcfg_list, list) |
126 | if (cfg->virt) { | 133 | pci_mmcfg_arch_unmap(cfg); |
127 | iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus)); | 134 | } |
128 | cfg->virt = NULL; | 135 | |
129 | } | 136 | int __devinit pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg) |
137 | { | ||
138 | cfg->virt = mcfg_ioremap(cfg); | ||
139 | if (!cfg->virt) { | ||
140 | pr_err(PREFIX "can't map MMCONFIG at %pR\n", &cfg->res); | ||
141 | return -ENOMEM; | ||
142 | } | ||
143 | |||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg) | ||
148 | { | ||
149 | if (cfg && cfg->virt) { | ||
150 | iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus)); | ||
151 | cfg->virt = NULL; | ||
130 | } | 152 | } |
131 | } | 153 | } |