mmconfig_64.c 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. /*
  2. * mmconfig.c - Low-level direct PCI config space access via MMCONFIG
  3. *
  4. * This is an 64bit optimized version that always keeps the full mmconfig
  5. * space mapped. This allows lockless config space operation.
  6. */
  7. #include <linux/pci.h>
  8. #include <linux/init.h>
  9. #include <linux/acpi.h>
  10. #include <linux/bitmap.h>
  11. #include <linux/rcupdate.h>
  12. #include <asm/e820.h>
  13. #include <asm/pci_x86.h>
  14. #define PREFIX "PCI: "
  15. static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn)
  16. {
  17. struct pci_mmcfg_region *cfg = pci_mmconfig_lookup(seg, bus);
  18. if (cfg && cfg->virt)
  19. return cfg->virt + (PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12));
  20. return NULL;
  21. }
  22. static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
  23. unsigned int devfn, int reg, int len, u32 *value)
  24. {
  25. char __iomem *addr;
  26. /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
  27. if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) {
  28. err: *value = -1;
  29. return -EINVAL;
  30. }
  31. rcu_read_lock();
  32. addr = pci_dev_base(seg, bus, devfn);
  33. if (!addr) {
  34. rcu_read_unlock();
  35. goto err;
  36. }
  37. switch (len) {
  38. case 1:
  39. *value = mmio_config_readb(addr + reg);
  40. break;
  41. case 2:
  42. *value = mmio_config_readw(addr + reg);
  43. break;
  44. case 4:
  45. *value = mmio_config_readl(addr + reg);
  46. break;
  47. }
  48. rcu_read_unlock();
  49. return 0;
  50. }
  51. static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
  52. unsigned int devfn, int reg, int len, u32 value)
  53. {
  54. char __iomem *addr;
  55. /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
  56. if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095)))
  57. return -EINVAL;
  58. rcu_read_lock();
  59. addr = pci_dev_base(seg, bus, devfn);
  60. if (!addr) {
  61. rcu_read_unlock();
  62. return -EINVAL;
  63. }
  64. switch (len) {
  65. case 1:
  66. mmio_config_writeb(addr + reg, value);
  67. break;
  68. case 2:
  69. mmio_config_writew(addr + reg, value);
  70. break;
  71. case 4:
  72. mmio_config_writel(addr + reg, value);
  73. break;
  74. }
  75. rcu_read_unlock();
  76. return 0;
  77. }
  78. const struct pci_raw_ops pci_mmcfg = {
  79. .read = pci_mmcfg_read,
  80. .write = pci_mmcfg_write,
  81. };
  82. static void __iomem *mcfg_ioremap(struct pci_mmcfg_region *cfg)
  83. {
  84. void __iomem *addr;
  85. u64 start, size;
  86. int num_buses;
  87. start = cfg->address + PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
  88. num_buses = cfg->end_bus - cfg->start_bus + 1;
  89. size = PCI_MMCFG_BUS_OFFSET(num_buses);
  90. addr = ioremap_nocache(start, size);
  91. if (addr)
  92. addr -= PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
  93. return addr;
  94. }
  95. int __init pci_mmcfg_arch_init(void)
  96. {
  97. struct pci_mmcfg_region *cfg;
  98. list_for_each_entry(cfg, &pci_mmcfg_list, list)
  99. if (pci_mmcfg_arch_map(cfg)) {
  100. pci_mmcfg_arch_free();
  101. return 0;
  102. }
  103. raw_pci_ext_ops = &pci_mmcfg;
  104. return 1;
  105. }
  106. void __init pci_mmcfg_arch_free(void)
  107. {
  108. struct pci_mmcfg_region *cfg;
  109. list_for_each_entry(cfg, &pci_mmcfg_list, list)
  110. pci_mmcfg_arch_unmap(cfg);
  111. }
  112. int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg)
  113. {
  114. cfg->virt = mcfg_ioremap(cfg);
  115. if (!cfg->virt) {
  116. pr_err(PREFIX "can't map MMCONFIG at %pR\n", &cfg->res);
  117. return -ENOMEM;
  118. }
  119. return 0;
  120. }
  121. void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg)
  122. {
  123. if (cfg && cfg->virt) {
  124. iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus));
  125. cfg->virt = NULL;
  126. }
  127. }