diff options
author | Jiang Liu <jiang.liu@linux.intel.com> | 2015-10-14 02:29:42 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2015-10-16 16:18:52 -0400 |
commit | 02715e86b21955f107f376d84d165424ba9cd372 (patch) | |
tree | f125cf50969446a7fc15c965997aff919d4e7937 | |
parent | 4d6b4e69a245e9df4b84dba387596086cb66887d (diff) |
ia64/PCI/ACPI: Use common interface to support PCI host bridge
Use common interface to simplify PCI host bridge implementation.
Tested-by: Tony Luck <tony.luck@intel.com>
Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r-- | arch/ia64/pci/pci.c | 235 |
1 files changed, 48 insertions, 187 deletions
diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index b1846b891ea5..8f6ac2f8ae4c 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c | |||
@@ -116,15 +116,12 @@ struct pci_ops pci_root_ops = { | |||
116 | }; | 116 | }; |
117 | 117 | ||
118 | struct pci_root_info { | 118 | struct pci_root_info { |
119 | struct acpi_pci_root_info common; | ||
119 | struct pci_controller controller; | 120 | struct pci_controller controller; |
120 | struct acpi_device *bridge; | ||
121 | struct list_head resources; | ||
122 | struct list_head io_resources; | 121 | struct list_head io_resources; |
123 | char name[16]; | ||
124 | }; | 122 | }; |
125 | 123 | ||
126 | static unsigned int | 124 | static unsigned int new_space(u64 phys_base, int sparse) |
127 | new_space (u64 phys_base, int sparse) | ||
128 | { | 125 | { |
129 | u64 mmio_base; | 126 | u64 mmio_base; |
130 | int i; | 127 | int i; |
@@ -160,11 +157,11 @@ static int add_io_space(struct device *dev, struct pci_root_info *info, | |||
160 | unsigned long base, min, max, base_port; | 157 | unsigned long base, min, max, base_port; |
161 | unsigned int sparse = 0, space_nr, len; | 158 | unsigned int sparse = 0, space_nr, len; |
162 | 159 | ||
163 | len = strlen(info->name) + 32; | 160 | len = strlen(info->common.name) + 32; |
164 | iospace = resource_list_create_entry(NULL, len); | 161 | iospace = resource_list_create_entry(NULL, len); |
165 | if (!iospace) { | 162 | if (!iospace) { |
166 | dev_err(dev, "PCI: No memory for %s I/O port space\n", | 163 | dev_err(dev, "PCI: No memory for %s I/O port space\n", |
167 | info->name); | 164 | info->common.name); |
168 | return -ENOMEM; | 165 | return -ENOMEM; |
169 | } | 166 | } |
170 | 167 | ||
@@ -179,7 +176,7 @@ static int add_io_space(struct device *dev, struct pci_root_info *info, | |||
179 | max = res->end - entry->offset; | 176 | max = res->end - entry->offset; |
180 | base = __pa(io_space[space_nr].mmio_base); | 177 | base = __pa(io_space[space_nr].mmio_base); |
181 | base_port = IO_SPACE_BASE(space_nr); | 178 | base_port = IO_SPACE_BASE(space_nr); |
182 | snprintf(name, len, "%s I/O Ports %08lx-%08lx", info->name, | 179 | snprintf(name, len, "%s I/O Ports %08lx-%08lx", info->common.name, |
183 | base_port + min, base_port + max); | 180 | base_port + min, base_port + max); |
184 | 181 | ||
185 | /* | 182 | /* |
@@ -234,217 +231,81 @@ static bool resource_is_pcicfg_ioport(struct resource *res) | |||
234 | res->start == 0xCF8 && res->end == 0xCFF; | 231 | res->start == 0xCF8 && res->end == 0xCFF; |
235 | } | 232 | } |
236 | 233 | ||
237 | static int | 234 | static int pci_acpi_root_prepare_resources(struct acpi_pci_root_info *ci) |
238 | probe_pci_root_info(struct pci_root_info *info, struct acpi_device *device, | ||
239 | int busnum, int domain) | ||
240 | { | 235 | { |
241 | int ret; | 236 | struct device *dev = &ci->bridge->dev; |
242 | struct list_head *list = &info->resources; | 237 | struct pci_root_info *info; |
238 | struct resource *res; | ||
243 | struct resource_entry *entry, *tmp; | 239 | struct resource_entry *entry, *tmp; |
244 | 240 | int status; | |
245 | ret = acpi_dev_get_resources(device, list, | 241 | |
246 | acpi_dev_filter_resource_type_cb, | 242 | status = acpi_pci_probe_root_resources(ci); |
247 | (void *)(IORESOURCE_IO | IORESOURCE_MEM)); | 243 | if (status > 0) { |
248 | if (ret < 0) | 244 | info = container_of(ci, struct pci_root_info, common); |
249 | dev_warn(&device->dev, | 245 | resource_list_for_each_entry_safe(entry, tmp, &ci->resources) { |
250 | "failed to parse _CRS method, error code %d\n", ret); | 246 | res = entry->res; |
251 | else if (ret == 0) | 247 | if (res->flags & IORESOURCE_MEM) { |
252 | dev_dbg(&device->dev, | 248 | /* |
253 | "no IO and memory resources present in _CRS\n"); | 249 | * HP's firmware has a hack to work around a |
254 | else | 250 | * Windows bug. Ignore these tiny memory ranges. |
255 | resource_list_for_each_entry_safe(entry, tmp, list) { | 251 | */ |
256 | if ((entry->res->flags & IORESOURCE_DISABLED) || | 252 | if (resource_size(res) <= 16) { |
257 | resource_is_pcicfg_ioport(entry->res)) | 253 | resource_list_del(entry); |
258 | resource_list_destroy_entry(entry); | 254 | insert_resource(&iomem_resource, |
259 | else | 255 | entry->res); |
260 | entry->res->name = info->name; | 256 | resource_list_add_tail(entry, |
261 | } | 257 | &info->io_resources); |
262 | 258 | } | |
263 | return ret; | 259 | } else if (res->flags & IORESOURCE_IO) { |
264 | } | 260 | if (resource_is_pcicfg_ioport(entry->res)) |
265 | 261 | resource_list_destroy_entry(entry); | |
266 | static void validate_resources(struct device *dev, struct list_head *resources, | 262 | else if (add_io_space(dev, info, entry)) |
267 | unsigned long type) | 263 | resource_list_destroy_entry(entry); |
268 | { | ||
269 | LIST_HEAD(list); | ||
270 | struct resource *res1, *res2, *root = NULL; | ||
271 | struct resource_entry *tmp, *entry, *entry2; | ||
272 | |||
273 | BUG_ON((type & (IORESOURCE_MEM | IORESOURCE_IO)) == 0); | ||
274 | root = (type & IORESOURCE_MEM) ? &iomem_resource : &ioport_resource; | ||
275 | |||
276 | list_splice_init(resources, &list); | ||
277 | resource_list_for_each_entry_safe(entry, tmp, &list) { | ||
278 | bool free = false; | ||
279 | resource_size_t end; | ||
280 | |||
281 | res1 = entry->res; | ||
282 | if (!(res1->flags & type)) | ||
283 | goto next; | ||
284 | |||
285 | /* Exclude non-addressable range or non-addressable portion */ | ||
286 | end = min(res1->end, root->end); | ||
287 | if (end <= res1->start) { | ||
288 | dev_info(dev, "host bridge window %pR (ignored, not CPU addressable)\n", | ||
289 | res1); | ||
290 | free = true; | ||
291 | goto next; | ||
292 | } else if (res1->end != end) { | ||
293 | dev_info(dev, "host bridge window %pR ([%#llx-%#llx] ignored, not CPU addressable)\n", | ||
294 | res1, (unsigned long long)end + 1, | ||
295 | (unsigned long long)res1->end); | ||
296 | res1->end = end; | ||
297 | } | ||
298 | |||
299 | resource_list_for_each_entry(entry2, resources) { | ||
300 | res2 = entry2->res; | ||
301 | if (!(res2->flags & type)) | ||
302 | continue; | ||
303 | |||
304 | /* | ||
305 | * I don't like throwing away windows because then | ||
306 | * our resources no longer match the ACPI _CRS, but | ||
307 | * the kernel resource tree doesn't allow overlaps. | ||
308 | */ | ||
309 | if (resource_overlaps(res1, res2)) { | ||
310 | res2->start = min(res1->start, res2->start); | ||
311 | res2->end = max(res1->end, res2->end); | ||
312 | dev_info(dev, "host bridge window expanded to %pR; %pR ignored\n", | ||
313 | res2, res1); | ||
314 | free = true; | ||
315 | goto next; | ||
316 | } | 264 | } |
317 | } | 265 | } |
318 | |||
319 | next: | ||
320 | resource_list_del(entry); | ||
321 | if (free) | ||
322 | resource_list_free_entry(entry); | ||
323 | else | ||
324 | resource_list_add_tail(entry, resources); | ||
325 | } | 266 | } |
326 | } | ||
327 | 267 | ||
328 | static void add_resources(struct pci_root_info *info, struct device *dev) | 268 | return status; |
329 | { | ||
330 | struct resource_entry *entry, *tmp; | ||
331 | struct resource *res, *conflict, *root = NULL; | ||
332 | struct list_head *list = &info->resources; | ||
333 | |||
334 | validate_resources(dev, list, IORESOURCE_MEM); | ||
335 | validate_resources(dev, list, IORESOURCE_IO); | ||
336 | |||
337 | resource_list_for_each_entry_safe(entry, tmp, list) { | ||
338 | res = entry->res; | ||
339 | if (res->flags & IORESOURCE_MEM) { | ||
340 | root = &iomem_resource; | ||
341 | /* | ||
342 | * HP's firmware has a hack to work around a Windows | ||
343 | * bug. Ignore these tiny memory ranges. | ||
344 | */ | ||
345 | if (resource_size(res) <= 16) { | ||
346 | resource_list_destroy_entry(entry); | ||
347 | continue; | ||
348 | } | ||
349 | } else if (res->flags & IORESOURCE_IO) { | ||
350 | root = &ioport_resource; | ||
351 | if (add_io_space(&info->bridge->dev, info, entry)) { | ||
352 | resource_list_destroy_entry(entry); | ||
353 | continue; | ||
354 | } | ||
355 | } else { | ||
356 | BUG_ON(res); | ||
357 | } | ||
358 | |||
359 | conflict = insert_resource_conflict(root, res); | ||
360 | if (conflict) { | ||
361 | dev_info(dev, | ||
362 | "ignoring host bridge window %pR (conflicts with %s %pR)\n", | ||
363 | res, conflict->name, conflict); | ||
364 | resource_list_destroy_entry(entry); | ||
365 | } | ||
366 | } | ||
367 | } | 269 | } |
368 | 270 | ||
369 | static void __release_pci_root_info(struct pci_root_info *info) | 271 | static void pci_acpi_root_release_info(struct acpi_pci_root_info *ci) |
370 | { | 272 | { |
371 | struct resource *res; | 273 | struct pci_root_info *info; |
372 | struct resource_entry *entry, *tentry; | 274 | struct resource_entry *entry, *tmp; |
373 | 275 | ||
374 | resource_list_for_each_entry_safe(entry, tentry, &info->io_resources) { | 276 | info = container_of(ci, struct pci_root_info, common); |
277 | resource_list_for_each_entry_safe(entry, tmp, &info->io_resources) { | ||
375 | release_resource(entry->res); | 278 | release_resource(entry->res); |
376 | resource_list_destroy_entry(entry); | 279 | resource_list_destroy_entry(entry); |
377 | } | 280 | } |
378 | |||
379 | resource_list_for_each_entry_safe(entry, tentry, &info->resources) { | ||
380 | res = entry->res; | ||
381 | if (res->parent && | ||
382 | (res->flags & (IORESOURCE_MEM | IORESOURCE_IO))) | ||
383 | release_resource(res); | ||
384 | resource_list_destroy_entry(entry); | ||
385 | } | ||
386 | |||
387 | kfree(info); | 281 | kfree(info); |
388 | } | 282 | } |
389 | 283 | ||
390 | static void release_pci_root_info(struct pci_host_bridge *bridge) | 284 | static struct acpi_pci_root_ops pci_acpi_root_ops = { |
391 | { | 285 | .pci_ops = &pci_root_ops, |
392 | struct pci_root_info *info = bridge->release_data; | 286 | .release_info = pci_acpi_root_release_info, |
393 | 287 | .prepare_resources = pci_acpi_root_prepare_resources, | |
394 | __release_pci_root_info(info); | 288 | }; |
395 | } | ||
396 | 289 | ||
397 | struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) | 290 | struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) |
398 | { | 291 | { |
399 | struct acpi_device *device = root->device; | 292 | struct acpi_device *device = root->device; |
400 | int domain = root->segment; | ||
401 | int bus = root->secondary.start; | ||
402 | struct pci_root_info *info; | 293 | struct pci_root_info *info; |
403 | struct pci_bus *pbus; | ||
404 | int ret; | ||
405 | 294 | ||
406 | info = kzalloc(sizeof(*info), GFP_KERNEL); | 295 | info = kzalloc(sizeof(*info), GFP_KERNEL); |
407 | if (!info) { | 296 | if (!info) { |
408 | dev_err(&device->dev, | 297 | dev_err(&device->dev, |
409 | "pci_bus %04x:%02x: ignored (out of memory)\n", | 298 | "pci_bus %04x:%02x: ignored (out of memory)\n", |
410 | domain, bus); | 299 | root->segment, (int)root->secondary.start); |
411 | return NULL; | 300 | return NULL; |
412 | } | 301 | } |
413 | 302 | ||
414 | info->controller.segment = domain; | 303 | info->controller.segment = root->segment; |
415 | info->controller.companion = device; | 304 | info->controller.companion = device; |
416 | info->controller.node = acpi_get_node(device->handle); | 305 | info->controller.node = acpi_get_node(device->handle); |
417 | info->bridge = device; | ||
418 | INIT_LIST_HEAD(&info->resources); | ||
419 | INIT_LIST_HEAD(&info->io_resources); | 306 | INIT_LIST_HEAD(&info->io_resources); |
420 | snprintf(info->name, sizeof(info->name), | 307 | return acpi_pci_root_create(root, &pci_acpi_root_ops, |
421 | "PCI Bus %04x:%02x", domain, bus); | 308 | &info->common, &info->controller); |
422 | |||
423 | ret = probe_pci_root_info(info, device, bus, domain); | ||
424 | if (ret <= 0) { | ||
425 | kfree(info); | ||
426 | return NULL; | ||
427 | } | ||
428 | add_resources(info, &info->bridge->dev); | ||
429 | pci_add_resource(&info->resources, &root->secondary); | ||
430 | |||
431 | /* | ||
432 | * See arch/x86/pci/acpi.c. | ||
433 | * The desired pci bus might already be scanned in a quirk. We | ||
434 | * should handle the case here, but it appears that IA64 hasn't | ||
435 | * such quirk. So we just ignore the case now. | ||
436 | */ | ||
437 | pbus = pci_create_root_bus(NULL, bus, &pci_root_ops, | ||
438 | &info->controller, &info->resources); | ||
439 | if (!pbus) { | ||
440 | __release_pci_root_info(info); | ||
441 | return NULL; | ||
442 | } | ||
443 | |||
444 | pci_set_host_bridge_release(to_pci_host_bridge(pbus->bridge), | ||
445 | release_pci_root_info, info); | ||
446 | pci_scan_child_bus(pbus); | ||
447 | return pbus; | ||
448 | } | 309 | } |
449 | 310 | ||
450 | int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) | 311 | int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) |