mmconfig.c 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  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 "pci.h"
  11. #define MMCONFIG_APER_SIZE (256*1024*1024)
  12. /* Static virtual mapping of the MMCONFIG aperture */
  13. struct mmcfg_virt {
  14. struct acpi_table_mcfg_config *cfg;
  15. char *virt;
  16. };
  17. static struct mmcfg_virt *pci_mmcfg_virt;
  18. static char *get_virt(unsigned int seg, int bus)
  19. {
  20. int cfg_num = -1;
  21. struct acpi_table_mcfg_config *cfg;
  22. while (1) {
  23. ++cfg_num;
  24. if (cfg_num >= pci_mmcfg_config_num) {
  25. /* something bad is going on, no cfg table is found. */
  26. /* so we fall back to the old way we used to do this */
  27. /* and just rely on the first entry to be correct. */
  28. return pci_mmcfg_virt[0].virt;
  29. }
  30. cfg = pci_mmcfg_virt[cfg_num].cfg;
  31. if (cfg->pci_segment_group_number != seg)
  32. continue;
  33. if ((cfg->start_bus_number <= bus) &&
  34. (cfg->end_bus_number >= bus))
  35. return pci_mmcfg_virt[cfg_num].virt;
  36. }
  37. }
  38. static inline char *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn)
  39. {
  40. return get_virt(seg, bus) + ((bus << 20) | (devfn << 12));
  41. }
  42. static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
  43. unsigned int devfn, int reg, int len, u32 *value)
  44. {
  45. char *addr = pci_dev_base(seg, bus, devfn);
  46. if (unlikely(!value || (bus > 255) || (devfn > 255) || (reg > 4095)))
  47. return -EINVAL;
  48. switch (len) {
  49. case 1:
  50. *value = readb(addr + reg);
  51. break;
  52. case 2:
  53. *value = readw(addr + reg);
  54. break;
  55. case 4:
  56. *value = readl(addr + reg);
  57. break;
  58. }
  59. return 0;
  60. }
  61. static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
  62. unsigned int devfn, int reg, int len, u32 value)
  63. {
  64. char *addr = pci_dev_base(seg, bus, devfn);
  65. if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095)))
  66. return -EINVAL;
  67. switch (len) {
  68. case 1:
  69. writeb(value, addr + reg);
  70. break;
  71. case 2:
  72. writew(value, addr + reg);
  73. break;
  74. case 4:
  75. writel(value, addr + reg);
  76. break;
  77. }
  78. return 0;
  79. }
  80. static struct pci_raw_ops pci_mmcfg = {
  81. .read = pci_mmcfg_read,
  82. .write = pci_mmcfg_write,
  83. };
  84. static int __init pci_mmcfg_init(void)
  85. {
  86. int i;
  87. if ((pci_probe & PCI_PROBE_MMCONF) == 0)
  88. return 0;
  89. acpi_table_parse(ACPI_MCFG, acpi_parse_mcfg);
  90. if ((pci_mmcfg_config_num == 0) ||
  91. (pci_mmcfg_config == NULL) ||
  92. (pci_mmcfg_config[0].base_address == 0))
  93. return 0;
  94. /* RED-PEN i386 doesn't do _nocache right now */
  95. pci_mmcfg_virt = kmalloc(sizeof(*pci_mmcfg_virt) * pci_mmcfg_config_num, GFP_KERNEL);
  96. if (pci_mmcfg_virt == NULL) {
  97. printk("PCI: Can not allocate memory for mmconfig structures\n");
  98. return 0;
  99. }
  100. for (i = 0; i < pci_mmcfg_config_num; ++i) {
  101. pci_mmcfg_virt[i].cfg = &pci_mmcfg_config[i];
  102. pci_mmcfg_virt[i].virt = ioremap_nocache(pci_mmcfg_config[i].base_address, MMCONFIG_APER_SIZE);
  103. if (!pci_mmcfg_virt[i].virt) {
  104. printk("PCI: Cannot map mmconfig aperture for segment %d\n",
  105. pci_mmcfg_config[i].pci_segment_group_number);
  106. return 0;
  107. }
  108. printk(KERN_INFO "PCI: Using MMCONFIG at %x\n", pci_mmcfg_config[i].base_address);
  109. }
  110. raw_pci_ops = &pci_mmcfg;
  111. pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF;
  112. return 0;
  113. }
  114. arch_initcall(pci_mmcfg_init);