pci-host-common.c 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Generic PCI host driver common code
  4. *
  5. * Copyright (C) 2014 ARM Limited
  6. *
  7. * Author: Will Deacon <will.deacon@arm.com>
  8. */
  9. #include <linux/kernel.h>
  10. #include <linux/of_address.h>
  11. #include <linux/of_pci.h>
  12. #include <linux/pci-ecam.h>
  13. #include <linux/platform_device.h>
  14. static void gen_pci_unmap_cfg(void *ptr)
  15. {
  16. pci_ecam_free((struct pci_config_window *)ptr);
  17. }
  18. static struct pci_config_window *gen_pci_init(struct device *dev,
  19. struct list_head *resources, struct pci_ecam_ops *ops)
  20. {
  21. int err;
  22. struct resource cfgres;
  23. struct resource *bus_range = NULL;
  24. struct pci_config_window *cfg;
  25. /* Parse our PCI ranges and request their resources */
  26. err = pci_parse_request_of_pci_ranges(dev, resources, &bus_range);
  27. if (err)
  28. return ERR_PTR(err);
  29. err = of_address_to_resource(dev->of_node, 0, &cfgres);
  30. if (err) {
  31. dev_err(dev, "missing \"reg\" property\n");
  32. goto err_out;
  33. }
  34. cfg = pci_ecam_create(dev, &cfgres, bus_range, ops);
  35. if (IS_ERR(cfg)) {
  36. err = PTR_ERR(cfg);
  37. goto err_out;
  38. }
  39. err = devm_add_action(dev, gen_pci_unmap_cfg, cfg);
  40. if (err) {
  41. gen_pci_unmap_cfg(cfg);
  42. goto err_out;
  43. }
  44. return cfg;
  45. err_out:
  46. pci_free_resource_list(resources);
  47. return ERR_PTR(err);
  48. }
  49. int pci_host_common_probe(struct platform_device *pdev,
  50. struct pci_ecam_ops *ops)
  51. {
  52. const char *type;
  53. struct device *dev = &pdev->dev;
  54. struct device_node *np = dev->of_node;
  55. struct pci_host_bridge *bridge;
  56. struct pci_config_window *cfg;
  57. struct list_head resources;
  58. int ret;
  59. bridge = devm_pci_alloc_host_bridge(dev, 0);
  60. if (!bridge)
  61. return -ENOMEM;
  62. type = of_get_property(np, "device_type", NULL);
  63. if (!type || strcmp(type, "pci")) {
  64. dev_err(dev, "invalid \"device_type\" %s\n", type);
  65. return -EINVAL;
  66. }
  67. of_pci_check_probe_only();
  68. /* Parse and map our Configuration Space windows */
  69. cfg = gen_pci_init(dev, &resources, ops);
  70. if (IS_ERR(cfg))
  71. return PTR_ERR(cfg);
  72. /* Do not reassign resources if probe only */
  73. if (!pci_has_flag(PCI_PROBE_ONLY))
  74. pci_add_flags(PCI_REASSIGN_ALL_BUS);
  75. list_splice_init(&resources, &bridge->windows);
  76. bridge->dev.parent = dev;
  77. bridge->sysdata = cfg;
  78. bridge->busnr = cfg->busr.start;
  79. bridge->ops = &ops->pci_ops;
  80. bridge->map_irq = of_irq_parse_and_map_pci;
  81. bridge->swizzle_irq = pci_common_swizzle;
  82. ret = pci_host_probe(bridge);
  83. if (ret < 0) {
  84. pci_free_resource_list(&resources);
  85. return ret;
  86. }
  87. platform_set_drvdata(pdev, bridge->bus);
  88. return 0;
  89. }
  90. int pci_host_common_remove(struct platform_device *pdev)
  91. {
  92. struct pci_bus *bus = platform_get_drvdata(pdev);
  93. pci_lock_rescan_remove();
  94. pci_stop_root_bus(bus);
  95. pci_remove_root_bus(bus);
  96. pci_unlock_rescan_remove();
  97. return 0;
  98. }