blob: 4095e4d66a1d9345b236ee514260db23cf94c64b [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * mmconfig.c - Low-level direct PCI config space access via MMCONFIG
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +03003 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * This is an 64bit optimized version that always keeps the full mmconfig
5 * space mapped. This allows lockless config space operation.
6 */
7
8#include <linux/pci.h>
9#include <linux/init.h>
Greg Kroah-Hartman54549392005-06-23 17:35:56 -070010#include <linux/acpi.h>
Andi Kleend6ece542005-12-12 22:17:11 -080011#include <linux/bitmap.h>
Arjan van de Ven946f2ee2006-04-07 19:49:30 +020012#include <asm/e820.h>
13
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#include "pci.h"
15
Linus Torvalds1da177e2005-04-16 15:20:36 -070016/* Static virtual mapping of the MMCONFIG aperture */
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -070017struct mmcfg_virt {
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +030018 struct acpi_mcfg_allocation *cfg;
Al Viro8b8a4e32005-12-15 09:17:44 +000019 char __iomem *virt;
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -070020};
21static struct mmcfg_virt *pci_mmcfg_virt;
Linus Torvalds1da177e2005-04-16 15:20:36 -070022
Al Viro8b8a4e32005-12-15 09:17:44 +000023static char __iomem *get_virt(unsigned int seg, unsigned bus)
Linus Torvalds1da177e2005-04-16 15:20:36 -070024{
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +030025 struct acpi_mcfg_allocation *cfg;
OGAWA Hirofumi429d5122007-02-13 13:26:20 +010026 int cfg_num;
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -070027
OGAWA Hirofumi429d5122007-02-13 13:26:20 +010028 for (cfg_num = 0; cfg_num < pci_mmcfg_config_num; cfg_num++) {
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -070029 cfg = pci_mmcfg_virt[cfg_num].cfg;
OGAWA Hirofumi429d5122007-02-13 13:26:20 +010030 if (cfg->pci_segment == seg &&
31 (cfg->start_bus_number <= bus) &&
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -070032 (cfg->end_bus_number >= bus))
33 return pci_mmcfg_virt[cfg_num].virt;
34 }
Andi Kleen31030392006-01-27 02:03:50 +010035
Andi Kleen31030392006-01-27 02:03:50 +010036 /* Fall back to type 0 */
Al Virocc598532006-02-03 20:28:01 -050037 return NULL;
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -070038}
39
Al Viro8b8a4e32005-12-15 09:17:44 +000040static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn)
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -070041{
Al Viro8b8a4e32005-12-15 09:17:44 +000042 char __iomem *addr;
Olivier Galibertb7867392007-02-13 13:26:20 +010043 if (seg == 0 && bus < PCI_MMCFG_MAX_CHECK_BUS &&
44 test_bit(32*bus + PCI_SLOT(devfn), pci_mmcfg_fallback_slots))
Andi Kleend6ece542005-12-12 22:17:11 -080045 return NULL;
46 addr = get_virt(seg, bus);
Andi Kleen928cf8c2005-12-12 22:17:10 -080047 if (!addr)
48 return NULL;
49 return addr + ((bus << 20) | (devfn << 12));
Linus Torvalds1da177e2005-04-16 15:20:36 -070050}
51
52static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
53 unsigned int devfn, int reg, int len, u32 *value)
54{
Al Viro8b8a4e32005-12-15 09:17:44 +000055 char __iomem *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -070056
Andi Kleen928cf8c2005-12-12 22:17:10 -080057 /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
Andi Kleenecc16ba2006-04-11 12:54:48 +020058 if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) {
Andi Kleen49c93e82006-04-07 19:50:15 +020059 *value = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070060 return -EINVAL;
Andi Kleen49c93e82006-04-07 19:50:15 +020061 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070062
Andi Kleen928cf8c2005-12-12 22:17:10 -080063 addr = pci_dev_base(seg, bus, devfn);
64 if (!addr)
65 return pci_conf1_read(seg,bus,devfn,reg,len,value);
66
Linus Torvalds1da177e2005-04-16 15:20:36 -070067 switch (len) {
68 case 1:
dean gaudet3320ad92007-08-10 22:30:59 +020069 *value = mmio_config_readb(addr + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -070070 break;
71 case 2:
dean gaudet3320ad92007-08-10 22:30:59 +020072 *value = mmio_config_readw(addr + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -070073 break;
74 case 4:
dean gaudet3320ad92007-08-10 22:30:59 +020075 *value = mmio_config_readl(addr + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -070076 break;
77 }
78
79 return 0;
80}
81
82static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
83 unsigned int devfn, int reg, int len, u32 value)
84{
Al Viro8b8a4e32005-12-15 09:17:44 +000085 char __iomem *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -070086
Andi Kleen928cf8c2005-12-12 22:17:10 -080087 /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095)))
89 return -EINVAL;
90
Andi Kleen928cf8c2005-12-12 22:17:10 -080091 addr = pci_dev_base(seg, bus, devfn);
92 if (!addr)
93 return pci_conf1_write(seg,bus,devfn,reg,len,value);
94
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 switch (len) {
96 case 1:
dean gaudet3320ad92007-08-10 22:30:59 +020097 mmio_config_writeb(addr + reg, value);
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 break;
99 case 2:
dean gaudet3320ad92007-08-10 22:30:59 +0200100 mmio_config_writew(addr + reg, value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 break;
102 case 4:
dean gaudet3320ad92007-08-10 22:30:59 +0200103 mmio_config_writel(addr + reg, value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104 break;
105 }
106
107 return 0;
108}
109
110static struct pci_raw_ops pci_mmcfg = {
111 .read = pci_mmcfg_read,
112 .write = pci_mmcfg_write,
113};
114
OGAWA Hirofumi44de0202007-02-13 13:26:20 +0100115static void __iomem * __init mcfg_ioremap(struct acpi_mcfg_allocation *cfg)
116{
117 void __iomem *addr;
118 u32 size;
119
120 size = (cfg->end_bus_number + 1) << 20;
121 addr = ioremap_nocache(cfg->address, size);
122 if (addr) {
123 printk(KERN_INFO "PCI: Using MMCONFIG at %Lx - %Lx\n",
124 cfg->address, cfg->address + size - 1);
125 }
126 return addr;
127}
128
OGAWA Hirofumi56829d12007-02-13 13:26:20 +0100129int __init pci_mmcfg_arch_reachable(unsigned int seg, unsigned int bus,
130 unsigned int devfn)
131{
132 return pci_dev_base(seg, bus, devfn) != NULL;
133}
134
Olivier Galibertb7867392007-02-13 13:26:20 +0100135int __init pci_mmcfg_arch_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136{
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700137 int i;
Olivier Galibertb7867392007-02-13 13:26:20 +0100138 pci_mmcfg_virt = kmalloc(sizeof(*pci_mmcfg_virt) *
139 pci_mmcfg_config_num, GFP_KERNEL);
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700140 if (pci_mmcfg_virt == NULL) {
Dave Jones3095fc02006-10-20 14:45:33 -0700141 printk(KERN_ERR "PCI: Can not allocate memory for mmconfig structures\n");
Olivier Galibertb7867392007-02-13 13:26:20 +0100142 return 0;
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700143 }
Olivier Galibertb7867392007-02-13 13:26:20 +0100144
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700145 for (i = 0; i < pci_mmcfg_config_num; ++i) {
146 pci_mmcfg_virt[i].cfg = &pci_mmcfg_config[i];
OGAWA Hirofumifaed1972007-02-13 13:26:20 +0100147 pci_mmcfg_virt[i].virt = mcfg_ioremap(&pci_mmcfg_config[i]);
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700148 if (!pci_mmcfg_virt[i].virt) {
Dave Jones3095fc02006-10-20 14:45:33 -0700149 printk(KERN_ERR "PCI: Cannot map mmconfig aperture for "
150 "segment %d\n",
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +0300151 pci_mmcfg_config[i].pci_segment);
Olivier Galibertb7867392007-02-13 13:26:20 +0100152 return 0;
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700153 }
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700154 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 raw_pci_ops = &pci_mmcfg;
Olivier Galibertb7867392007-02-13 13:26:20 +0100156 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157}