diff options
author | Jiang Liu <jiang.liu@huawei.com> | 2012-06-22 02:55:17 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2012-06-22 17:17:00 -0400 |
commit | c0fa40784cce9cc66b54499a3762cfe07e35353f (patch) | |
tree | 4dbb2c3b42a04be757ff44705af56e047aa66862 /arch/x86/pci | |
parent | f4b57a3b4352f72e461e362cb25917e28bdba80f (diff) |
x86/PCI: update MMCONFIG information when hot-plugging PCI host bridges
This patch enhances x86 arch-specific code to update MMCONFIG information
when PCI host bridge hotplug event happens.
Reviewed-by: Yinghai Lu <yinghai@kernel.org>
Signed-off-by: Jiang Liu <liuj97@gmail.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Diffstat (limited to 'arch/x86/pci')
-rw-r--r-- | arch/x86/pci/acpi.c | 93 | ||||
-rw-r--r-- | arch/x86/pci/mmconfig_32.c | 2 | ||||
-rw-r--r-- | arch/x86/pci/mmconfig_64.c | 2 |
3 files changed, 92 insertions, 5 deletions
diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 2bb885afe103..912b54b26d6a 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c | |||
@@ -13,6 +13,12 @@ struct pci_root_info { | |||
13 | unsigned int res_num; | 13 | unsigned int res_num; |
14 | struct resource *res; | 14 | struct resource *res; |
15 | 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 | ||
16 | }; | 22 | }; |
17 | 23 | ||
18 | static bool pci_use_crs = true; | 24 | static bool pci_use_crs = true; |
@@ -119,6 +125,81 @@ void __init pci_acpi_crs_quirks(void) | |||
119 | pci_use_crs ? "nocrs" : "use_crs"); | 125 | pci_use_crs ? "nocrs" : "use_crs"); |
120 | } | 126 | } |
121 | 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 | |||
122 | static acpi_status | 203 | static acpi_status |
123 | resource_to_addr(struct acpi_resource *resource, | 204 | resource_to_addr(struct acpi_resource *resource, |
124 | struct acpi_resource_address64 *addr) | 205 | struct acpi_resource_address64 *addr) |
@@ -331,8 +412,11 @@ static void __release_pci_root_info(struct pci_root_info *info) | |||
331 | 412 | ||
332 | free_pci_root_info_res(info); | 413 | free_pci_root_info_res(info); |
333 | 414 | ||
415 | teardown_mcfg_map(info); | ||
416 | |||
334 | kfree(info); | 417 | kfree(info); |
335 | } | 418 | } |
419 | |||
336 | static void release_pci_root_info(struct pci_host_bridge *bridge) | 420 | static void release_pci_root_info(struct pci_host_bridge *bridge) |
337 | { | 421 | { |
338 | struct pci_root_info *info = bridge->release_data; | 422 | struct pci_root_info *info = bridge->release_data; |
@@ -372,7 +456,7 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) | |||
372 | int domain = root->segment; | 456 | int domain = root->segment; |
373 | int busnum = root->secondary.start; | 457 | int busnum = root->secondary.start; |
374 | LIST_HEAD(resources); | 458 | LIST_HEAD(resources); |
375 | struct pci_bus *bus; | 459 | struct pci_bus *bus = NULL; |
376 | struct pci_sysdata *sd; | 460 | struct pci_sysdata *sd; |
377 | int node; | 461 | int node; |
378 | #ifdef CONFIG_ACPI_NUMA | 462 | #ifdef CONFIG_ACPI_NUMA |
@@ -438,8 +522,11 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) | |||
438 | x86_pci_root_bus_resources(busnum, &resources); | 522 | x86_pci_root_bus_resources(busnum, &resources); |
439 | } | 523 | } |
440 | 524 | ||
441 | bus = pci_create_root_bus(NULL, busnum, &pci_root_ops, sd, | 525 | if (!setup_mcfg_map(info, domain, (u8)root->secondary.start, |
442 | &resources); | 526 | (u8)root->secondary.end, root->mcfg_addr)) |
527 | bus = pci_create_root_bus(NULL, busnum, &pci_root_ops, | ||
528 | sd, &resources); | ||
529 | |||
443 | if (bus) { | 530 | if (bus) { |
444 | pci_scan_child_bus(bus); | 531 | pci_scan_child_bus(bus); |
445 | pci_set_host_bridge_release( | 532 | pci_set_host_bridge_release( |
diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c index a22785deb50e..db63ac23e3d9 100644 --- a/arch/x86/pci/mmconfig_32.c +++ b/arch/x86/pci/mmconfig_32.c | |||
@@ -126,7 +126,7 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus, | |||
126 | return 0; | 126 | return 0; |
127 | } | 127 | } |
128 | 128 | ||
129 | static const struct pci_raw_ops pci_mmcfg = { | 129 | const struct pci_raw_ops pci_mmcfg = { |
130 | .read = pci_mmcfg_read, | 130 | .read = pci_mmcfg_read, |
131 | .write = pci_mmcfg_write, | 131 | .write = pci_mmcfg_write, |
132 | }; | 132 | }; |
diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c index ebefea5107a7..c206521fe98e 100644 --- a/arch/x86/pci/mmconfig_64.c +++ b/arch/x86/pci/mmconfig_64.c | |||
@@ -90,7 +90,7 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus, | |||
90 | return 0; | 90 | return 0; |
91 | } | 91 | } |
92 | 92 | ||
93 | static const struct pci_raw_ops pci_mmcfg = { | 93 | const struct pci_raw_ops pci_mmcfg = { |
94 | .read = pci_mmcfg_read, | 94 | .read = pci_mmcfg_read, |
95 | .write = pci_mmcfg_write, | 95 | .write = pci_mmcfg_write, |
96 | }; | 96 | }; |