pci-host-common.c 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  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/module.h>
  11. #include <linux/of.h>
  12. #include <linux/of_address.h>
  13. #include <linux/of_pci.h>
  14. #include <linux/pci-ecam.h>
  15. #include <linux/platform_device.h>
  16. static void gen_pci_unmap_cfg(void *ptr)
  17. {
  18. pci_ecam_free((struct pci_config_window *)ptr);
  19. }
  20. static struct pci_config_window *gen_pci_init(struct device *dev,
  21. struct pci_host_bridge *bridge, const struct pci_ecam_ops *ops)
  22. {
  23. int err;
  24. struct resource cfgres;
  25. struct resource_entry *bus;
  26. struct pci_config_window *cfg;
  27. err = of_address_to_resource(dev->of_node, 0, &cfgres);
  28. if (err) {
  29. dev_err(dev, "missing \"reg\" property\n");
  30. return ERR_PTR(err);
  31. }
  32. bus = resource_list_first_type(&bridge->windows, IORESOURCE_BUS);
  33. if (!bus)
  34. return ERR_PTR(-ENODEV);
  35. cfg = pci_ecam_create(dev, &cfgres, bus->res, ops);
  36. if (IS_ERR(cfg))
  37. return cfg;
  38. err = devm_add_action_or_reset(dev, gen_pci_unmap_cfg, cfg);
  39. if (err)
  40. return ERR_PTR(err);
  41. return cfg;
  42. }
  43. int pci_host_common_probe(struct platform_device *pdev)
  44. {
  45. struct device *dev = &pdev->dev;
  46. struct pci_host_bridge *bridge;
  47. struct pci_config_window *cfg;
  48. const struct pci_ecam_ops *ops;
  49. ops = of_device_get_match_data(&pdev->dev);
  50. if (!ops)
  51. return -ENODEV;
  52. bridge = devm_pci_alloc_host_bridge(dev, 0);
  53. if (!bridge)
  54. return -ENOMEM;
  55. platform_set_drvdata(pdev, bridge);
  56. of_pci_check_probe_only();
  57. /* Parse and map our Configuration Space windows */
  58. cfg = gen_pci_init(dev, bridge, ops);
  59. if (IS_ERR(cfg))
  60. return PTR_ERR(cfg);
  61. bridge->sysdata = cfg;
  62. bridge->ops = (struct pci_ops *)&ops->pci_ops;
  63. bridge->msi_domain = true;
  64. return pci_host_probe(bridge);
  65. }
  66. EXPORT_SYMBOL_GPL(pci_host_common_probe);
  67. void pci_host_common_remove(struct platform_device *pdev)
  68. {
  69. struct pci_host_bridge *bridge = platform_get_drvdata(pdev);
  70. pci_lock_rescan_remove();
  71. pci_stop_root_bus(bridge->bus);
  72. pci_remove_root_bus(bridge->bus);
  73. pci_unlock_rescan_remove();
  74. }
  75. EXPORT_SYMBOL_GPL(pci_host_common_remove);
  76. MODULE_DESCRIPTION("Generic PCI host common driver");
  77. MODULE_LICENSE("GPL v2");