pci-host-generic.c 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Simple, generic PCI host controller driver targetting firmware-initialised
  4. * systems and virtual machines (e.g. the PCI emulation provided by kvmtool).
  5. *
  6. * Copyright (C) 2014 ARM Limited
  7. *
  8. * Author: Will Deacon <will.deacon@arm.com>
  9. */
  10. #include <linux/kernel.h>
  11. #include <linux/init.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 struct pci_ecam_ops gen_pci_cfg_cam_bus_ops = {
  17. .bus_shift = 16,
  18. .pci_ops = {
  19. .map_bus = pci_ecam_map_bus,
  20. .read = pci_generic_config_read,
  21. .write = pci_generic_config_write,
  22. }
  23. };
  24. static bool pci_dw_valid_device(struct pci_bus *bus, unsigned int devfn)
  25. {
  26. struct pci_config_window *cfg = bus->sysdata;
  27. /*
  28. * The Synopsys DesignWare PCIe controller in ECAM mode will not filter
  29. * type 0 config TLPs sent to devices 1 and up on its downstream port,
  30. * resulting in devices appearing multiple times on bus 0 unless we
  31. * filter out those accesses here.
  32. */
  33. if (bus->number == cfg->busr.start && PCI_SLOT(devfn) > 0)
  34. return false;
  35. return true;
  36. }
  37. static void __iomem *pci_dw_ecam_map_bus(struct pci_bus *bus,
  38. unsigned int devfn, int where)
  39. {
  40. if (!pci_dw_valid_device(bus, devfn))
  41. return NULL;
  42. return pci_ecam_map_bus(bus, devfn, where);
  43. }
  44. static struct pci_ecam_ops pci_dw_ecam_bus_ops = {
  45. .bus_shift = 20,
  46. .pci_ops = {
  47. .map_bus = pci_dw_ecam_map_bus,
  48. .read = pci_generic_config_read,
  49. .write = pci_generic_config_write,
  50. }
  51. };
  52. static const struct of_device_id gen_pci_of_match[] = {
  53. { .compatible = "pci-host-cam-generic",
  54. .data = &gen_pci_cfg_cam_bus_ops },
  55. { .compatible = "pci-host-ecam-generic",
  56. .data = &pci_generic_ecam_ops },
  57. { .compatible = "marvell,armada8k-pcie-ecam",
  58. .data = &pci_dw_ecam_bus_ops },
  59. { .compatible = "socionext,synquacer-pcie-ecam",
  60. .data = &pci_dw_ecam_bus_ops },
  61. { .compatible = "snps,dw-pcie-ecam",
  62. .data = &pci_dw_ecam_bus_ops },
  63. { },
  64. };
  65. static int gen_pci_probe(struct platform_device *pdev)
  66. {
  67. const struct of_device_id *of_id;
  68. struct pci_ecam_ops *ops;
  69. of_id = of_match_node(gen_pci_of_match, pdev->dev.of_node);
  70. ops = (struct pci_ecam_ops *)of_id->data;
  71. return pci_host_common_probe(pdev, ops);
  72. }
  73. static struct platform_driver gen_pci_driver = {
  74. .driver = {
  75. .name = "pci-host-generic",
  76. .of_match_table = gen_pci_of_match,
  77. .suppress_bind_attrs = true,
  78. },
  79. .probe = gen_pci_probe,
  80. .remove = pci_host_common_remove,
  81. };
  82. builtin_platform_driver(gen_pci_driver);