blob: 30007ffc8e11d203922d2c8304c5d0bdb909ef33 [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>
Jaswinder Singh Rajput82487712008-12-27 18:32:28 +053013#include <asm/pci_x86.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070014
Linus Torvalds1da177e2005-04-16 15:20:36 -070015/* Static virtual mapping of the MMCONFIG aperture */
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -070016struct mmcfg_virt {
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +030017 struct acpi_mcfg_allocation *cfg;
Al Viro8b8a4e32005-12-15 09:17:44 +000018 char __iomem *virt;
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -070019};
20static struct mmcfg_virt *pci_mmcfg_virt;
Linus Torvalds1da177e2005-04-16 15:20:36 -070021
Al Viro8b8a4e32005-12-15 09:17:44 +000022static char __iomem *get_virt(unsigned int seg, unsigned bus)
Linus Torvalds1da177e2005-04-16 15:20:36 -070023{
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +030024 struct acpi_mcfg_allocation *cfg;
OGAWA Hirofumi429d5122007-02-13 13:26:20 +010025 int cfg_num;
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -070026
OGAWA Hirofumi429d5122007-02-13 13:26:20 +010027 for (cfg_num = 0; cfg_num < pci_mmcfg_config_num; cfg_num++) {
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -070028 cfg = pci_mmcfg_virt[cfg_num].cfg;
OGAWA Hirofumi429d5122007-02-13 13:26:20 +010029 if (cfg->pci_segment == seg &&
30 (cfg->start_bus_number <= bus) &&
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -070031 (cfg->end_bus_number >= bus))
32 return pci_mmcfg_virt[cfg_num].virt;
33 }
Andi Kleen31030392006-01-27 02:03:50 +010034
Andi Kleen31030392006-01-27 02:03:50 +010035 /* Fall back to type 0 */
Al Virocc598532006-02-03 20:28:01 -050036 return NULL;
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -070037}
38
Al Viro8b8a4e32005-12-15 09:17:44 +000039static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn)
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -070040{
Al Viro8b8a4e32005-12-15 09:17:44 +000041 char __iomem *addr;
Ivan Kokshayskya0ca9902008-01-14 17:31:09 -050042
Andi Kleend6ece542005-12-12 22:17:11 -080043 addr = get_virt(seg, bus);
Andi Kleen928cf8c2005-12-12 22:17:10 -080044 if (!addr)
45 return NULL;
46 return addr + ((bus << 20) | (devfn << 12));
Linus Torvalds1da177e2005-04-16 15:20:36 -070047}
48
49static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
50 unsigned int devfn, int reg, int len, u32 *value)
51{
Al Viro8b8a4e32005-12-15 09:17:44 +000052 char __iomem *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -070053
Andi Kleen928cf8c2005-12-12 22:17:10 -080054 /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
Andi Kleenecc16ba2006-04-11 12:54:48 +020055 if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) {
Ivan Kokshayskya0ca9902008-01-14 17:31:09 -050056err: *value = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070057 return -EINVAL;
Andi Kleen49c93e82006-04-07 19:50:15 +020058 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070059
Andi Kleen928cf8c2005-12-12 22:17:10 -080060 addr = pci_dev_base(seg, bus, devfn);
61 if (!addr)
Ivan Kokshayskya0ca9902008-01-14 17:31:09 -050062 goto err;
Andi Kleen928cf8c2005-12-12 22:17:10 -080063
Linus Torvalds1da177e2005-04-16 15:20:36 -070064 switch (len) {
65 case 1:
dean gaudet3320ad92007-08-10 22:30:59 +020066 *value = mmio_config_readb(addr + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -070067 break;
68 case 2:
dean gaudet3320ad92007-08-10 22:30:59 +020069 *value = mmio_config_readw(addr + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -070070 break;
71 case 4:
dean gaudet3320ad92007-08-10 22:30:59 +020072 *value = mmio_config_readl(addr + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -070073 break;
74 }
75
76 return 0;
77}
78
79static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
80 unsigned int devfn, int reg, int len, u32 value)
81{
Al Viro8b8a4e32005-12-15 09:17:44 +000082 char __iomem *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -070083
Andi Kleen928cf8c2005-12-12 22:17:10 -080084 /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095)))
86 return -EINVAL;
87
Andi Kleen928cf8c2005-12-12 22:17:10 -080088 addr = pci_dev_base(seg, bus, devfn);
89 if (!addr)
Ivan Kokshayskya0ca9902008-01-14 17:31:09 -050090 return -EINVAL;
Andi Kleen928cf8c2005-12-12 22:17:10 -080091
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 switch (len) {
93 case 1:
dean gaudet3320ad92007-08-10 22:30:59 +020094 mmio_config_writeb(addr + reg, value);
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 break;
96 case 2:
dean gaudet3320ad92007-08-10 22:30:59 +020097 mmio_config_writew(addr + reg, value);
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 break;
99 case 4:
dean gaudet3320ad92007-08-10 22:30:59 +0200100 mmio_config_writel(addr + reg, value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 break;
102 }
103
104 return 0;
105}
106
107static struct pci_raw_ops pci_mmcfg = {
108 .read = pci_mmcfg_read,
109 .write = pci_mmcfg_write,
110};
111
OGAWA Hirofumi44de0202007-02-13 13:26:20 +0100112static void __iomem * __init mcfg_ioremap(struct acpi_mcfg_allocation *cfg)
113{
114 void __iomem *addr;
115 u32 size;
116
117 size = (cfg->end_bus_number + 1) << 20;
118 addr = ioremap_nocache(cfg->address, size);
119 if (addr) {
120 printk(KERN_INFO "PCI: Using MMCONFIG at %Lx - %Lx\n",
121 cfg->address, cfg->address + size - 1);
122 }
123 return addr;
124}
125
Olivier Galibertb7867392007-02-13 13:26:20 +0100126int __init pci_mmcfg_arch_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127{
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700128 int i;
Yinghai Lu0b64ad72008-02-15 01:28:41 -0800129 pci_mmcfg_virt = kzalloc(sizeof(*pci_mmcfg_virt) *
Olivier Galibertb7867392007-02-13 13:26:20 +0100130 pci_mmcfg_config_num, GFP_KERNEL);
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700131 if (pci_mmcfg_virt == NULL) {
Dave Jones3095fc02006-10-20 14:45:33 -0700132 printk(KERN_ERR "PCI: Can not allocate memory for mmconfig structures\n");
Olivier Galibertb7867392007-02-13 13:26:20 +0100133 return 0;
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700134 }
Olivier Galibertb7867392007-02-13 13:26:20 +0100135
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700136 for (i = 0; i < pci_mmcfg_config_num; ++i) {
137 pci_mmcfg_virt[i].cfg = &pci_mmcfg_config[i];
OGAWA Hirofumifaed1972007-02-13 13:26:20 +0100138 pci_mmcfg_virt[i].virt = mcfg_ioremap(&pci_mmcfg_config[i]);
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700139 if (!pci_mmcfg_virt[i].virt) {
Dave Jones3095fc02006-10-20 14:45:33 -0700140 printk(KERN_ERR "PCI: Cannot map mmconfig aperture for "
141 "segment %d\n",
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +0300142 pci_mmcfg_config[i].pci_segment);
Yinghai Lu0b64ad72008-02-15 01:28:41 -0800143 pci_mmcfg_arch_free();
Olivier Galibertb7867392007-02-13 13:26:20 +0100144 return 0;
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700145 }
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700146 }
Matthew Wilcoxb6ce0682008-02-10 09:45:28 -0500147 raw_pci_ext_ops = &pci_mmcfg;
Olivier Galibertb7867392007-02-13 13:26:20 +0100148 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149}
Yinghai Lu0b64ad72008-02-15 01:28:41 -0800150
151void __init pci_mmcfg_arch_free(void)
152{
153 int i;
154
155 if (pci_mmcfg_virt == NULL)
156 return;
157
158 for (i = 0; i < pci_mmcfg_config_num; ++i) {
159 if (pci_mmcfg_virt[i].virt) {
160 iounmap(pci_mmcfg_virt[i].virt);
161 pci_mmcfg_virt[i].virt = NULL;
162 pci_mmcfg_virt[i].cfg = NULL;
163 }
164 }
165
166 kfree(pci_mmcfg_virt);
167 pci_mmcfg_virt = NULL;
168}