pci-host-common.c 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Common library for PCI host controller drivers
  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. #include "pci-host-common.h"
  17. static void gen_pci_unmap_cfg(void *ptr)
  18. {
  19. pci_ecam_free((struct pci_config_window *)ptr);
  20. }
  21. struct pci_config_window *pci_host_common_ecam_create(struct device *dev,
  22. struct pci_host_bridge *bridge, const struct pci_ecam_ops *ops)
  23. {
  24. int err;
  25. struct resource cfgres;
  26. struct resource_entry *bus;
  27. struct pci_config_window *cfg;
  28. err = of_address_to_resource(dev->of_node, 0, &cfgres);
  29. if (err) {
  30. dev_err(dev, "missing or malformed \"reg\" property\n");
  31. return ERR_PTR(err);
  32. }
  33. bus = resource_list_first_type(&bridge->windows, IORESOURCE_BUS);
  34. if (!bus)
  35. return ERR_PTR(-ENODEV);
  36. cfg = pci_ecam_create(dev, &cfgres, bus->res, ops);
  37. if (IS_ERR(cfg))
  38. return cfg;
  39. err = devm_add_action_or_reset(dev, gen_pci_unmap_cfg, cfg);
  40. if (err)
  41. return ERR_PTR(err);
  42. return cfg;
  43. }
  44. EXPORT_SYMBOL_GPL(pci_host_common_ecam_create);
  45. int pci_host_common_init(struct platform_device *pdev,
  46. struct pci_host_bridge *bridge,
  47. const struct pci_ecam_ops *ops)
  48. {
  49. struct device *dev = &pdev->dev;
  50. struct pci_config_window *cfg;
  51. of_pci_check_probe_only();
  52. platform_set_drvdata(pdev, bridge);
  53. /* Parse and map our Configuration Space windows */
  54. cfg = pci_host_common_ecam_create(dev, bridge, ops);
  55. if (IS_ERR(cfg))
  56. return PTR_ERR(cfg);
  57. bridge->sysdata = cfg;
  58. bridge->ops = (struct pci_ops *)&ops->pci_ops;
  59. bridge->enable_device = ops->enable_device;
  60. bridge->disable_device = ops->disable_device;
  61. bridge->msi_domain = true;
  62. return pci_host_probe(bridge);
  63. }
  64. EXPORT_SYMBOL_GPL(pci_host_common_init);
  65. int pci_host_common_probe(struct platform_device *pdev)
  66. {
  67. const struct pci_ecam_ops *ops;
  68. struct pci_host_bridge *bridge;
  69. ops = of_device_get_match_data(&pdev->dev);
  70. if (!ops)
  71. return -ENODEV;
  72. bridge = devm_pci_alloc_host_bridge(&pdev->dev, 0);
  73. if (!bridge)
  74. return -ENOMEM;
  75. return pci_host_common_init(pdev, bridge, ops);
  76. }
  77. EXPORT_SYMBOL_GPL(pci_host_common_probe);
  78. void pci_host_common_remove(struct platform_device *pdev)
  79. {
  80. struct pci_host_bridge *bridge = platform_get_drvdata(pdev);
  81. pci_lock_rescan_remove();
  82. pci_stop_root_bus(bridge->bus);
  83. pci_remove_root_bus(bridge->bus);
  84. pci_unlock_rescan_remove();
  85. }
  86. EXPORT_SYMBOL_GPL(pci_host_common_remove);
  87. MODULE_DESCRIPTION("Common library for PCI host controller drivers");
  88. MODULE_LICENSE("GPL v2");