pci-host-common.c 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  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 <[email protected]>
  8. */
  9. #include <linux/kernel.h>
  10. #include <linux/module.h>
  11. #include <linux/of_address.h>
  12. #include <linux/of_device.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. /* Do not reassign resources if probe only */
  62. if (!pci_has_flag(PCI_PROBE_ONLY))
  63. pci_add_flags(PCI_REASSIGN_ALL_BUS);
  64. bridge->sysdata = cfg;
  65. bridge->ops = (struct pci_ops *)&ops->pci_ops;
  66. bridge->msi_domain = true;
  67. return pci_host_probe(bridge);
  68. }
  69. EXPORT_SYMBOL_GPL(pci_host_common_probe);
  70. int pci_host_common_remove(struct platform_device *pdev)
  71. {
  72. struct pci_host_bridge *bridge = platform_get_drvdata(pdev);
  73. pci_lock_rescan_remove();
  74. pci_stop_root_bus(bridge->bus);
  75. pci_remove_root_bus(bridge->bus);
  76. pci_unlock_rescan_remove();
  77. return 0;
  78. }
  79. EXPORT_SYMBOL_GPL(pci_host_common_remove);
  80. MODULE_LICENSE("GPL v2");