blob: 78e50b2c5cc574b476c80d1cdffed3f158fbad3e [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
Chuck Ebbertead2bfe2006-06-15 04:41:52 -040016/* aperture is up to 256MB but BIOS may reserve less */
17#define MMCONFIG_APER_MIN (2 * 1024*1024)
18#define MMCONFIG_APER_MAX (256 * 1024*1024)
19
Andi Kleen8c30b1a742006-04-07 19:50:12 +020020/* Verify the first 16 busses. We assume that systems with more busses
21 get MCFG right. */
Olivier Galibertb7867392007-02-13 13:26:20 +010022#define PCI_MMCFG_MAX_CHECK_BUS 16
Andi Kleend6ece542005-12-12 22:17:11 -080023
Linus Torvalds1da177e2005-04-16 15:20:36 -070024/* Static virtual mapping of the MMCONFIG aperture */
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -070025struct mmcfg_virt {
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +030026 struct acpi_mcfg_allocation *cfg;
Al Viro8b8a4e32005-12-15 09:17:44 +000027 char __iomem *virt;
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -070028};
29static struct mmcfg_virt *pci_mmcfg_virt;
Linus Torvalds1da177e2005-04-16 15:20:36 -070030
Al Viro8b8a4e32005-12-15 09:17:44 +000031static char __iomem *get_virt(unsigned int seg, unsigned bus)
Linus Torvalds1da177e2005-04-16 15:20:36 -070032{
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -070033 int cfg_num = -1;
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +030034 struct acpi_mcfg_allocation *cfg;
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -070035
36 while (1) {
37 ++cfg_num;
Andi Kleen31030392006-01-27 02:03:50 +010038 if (cfg_num >= pci_mmcfg_config_num)
39 break;
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -070040 cfg = pci_mmcfg_virt[cfg_num].cfg;
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +030041 if (cfg->pci_segment != seg)
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -070042 continue;
43 if ((cfg->start_bus_number <= bus) &&
44 (cfg->end_bus_number >= bus))
45 return pci_mmcfg_virt[cfg_num].virt;
46 }
Andi Kleen31030392006-01-27 02:03:50 +010047
Andi Kleen31030392006-01-27 02:03:50 +010048 /* Fall back to type 0 */
Al Virocc598532006-02-03 20:28:01 -050049 return NULL;
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -070050}
51
Al Viro8b8a4e32005-12-15 09:17:44 +000052static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn)
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -070053{
Al Viro8b8a4e32005-12-15 09:17:44 +000054 char __iomem *addr;
Olivier Galibertb7867392007-02-13 13:26:20 +010055 if (seg == 0 && bus < PCI_MMCFG_MAX_CHECK_BUS &&
56 test_bit(32*bus + PCI_SLOT(devfn), pci_mmcfg_fallback_slots))
Andi Kleend6ece542005-12-12 22:17:11 -080057 return NULL;
58 addr = get_virt(seg, bus);
Andi Kleen928cf8c2005-12-12 22:17:10 -080059 if (!addr)
60 return NULL;
61 return addr + ((bus << 20) | (devfn << 12));
Linus Torvalds1da177e2005-04-16 15:20:36 -070062}
63
64static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
65 unsigned int devfn, int reg, int len, u32 *value)
66{
Al Viro8b8a4e32005-12-15 09:17:44 +000067 char __iomem *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
Andi Kleen928cf8c2005-12-12 22:17:10 -080069 /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
Andi Kleenecc16ba2006-04-11 12:54:48 +020070 if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) {
Andi Kleen49c93e82006-04-07 19:50:15 +020071 *value = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070072 return -EINVAL;
Andi Kleen49c93e82006-04-07 19:50:15 +020073 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070074
Andi Kleen928cf8c2005-12-12 22:17:10 -080075 addr = pci_dev_base(seg, bus, devfn);
76 if (!addr)
77 return pci_conf1_read(seg,bus,devfn,reg,len,value);
78
Linus Torvalds1da177e2005-04-16 15:20:36 -070079 switch (len) {
80 case 1:
81 *value = readb(addr + reg);
82 break;
83 case 2:
84 *value = readw(addr + reg);
85 break;
86 case 4:
87 *value = readl(addr + reg);
88 break;
89 }
90
91 return 0;
92}
93
94static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
95 unsigned int devfn, int reg, int len, u32 value)
96{
Al Viro8b8a4e32005-12-15 09:17:44 +000097 char __iomem *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -070098
Andi Kleen928cf8c2005-12-12 22:17:10 -080099 /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095)))
101 return -EINVAL;
102
Andi Kleen928cf8c2005-12-12 22:17:10 -0800103 addr = pci_dev_base(seg, bus, devfn);
104 if (!addr)
105 return pci_conf1_write(seg,bus,devfn,reg,len,value);
106
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 switch (len) {
108 case 1:
109 writeb(value, addr + reg);
110 break;
111 case 2:
112 writew(value, addr + reg);
113 break;
114 case 4:
115 writel(value, addr + reg);
116 break;
117 }
118
119 return 0;
120}
121
122static struct pci_raw_ops pci_mmcfg = {
123 .read = pci_mmcfg_read,
124 .write = pci_mmcfg_write,
125};
126
OGAWA Hirofumi44de0202007-02-13 13:26:20 +0100127static void __iomem * __init mcfg_ioremap(struct acpi_mcfg_allocation *cfg)
128{
129 void __iomem *addr;
130 u32 size;
131
132 size = (cfg->end_bus_number + 1) << 20;
133 addr = ioremap_nocache(cfg->address, size);
134 if (addr) {
135 printk(KERN_INFO "PCI: Using MMCONFIG at %Lx - %Lx\n",
136 cfg->address, cfg->address + size - 1);
137 }
138 return addr;
139}
140
Olivier Galibertb7867392007-02-13 13:26:20 +0100141int __init pci_mmcfg_arch_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142{
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700143 int i;
Olivier Galibertb7867392007-02-13 13:26:20 +0100144 pci_mmcfg_virt = kmalloc(sizeof(*pci_mmcfg_virt) *
145 pci_mmcfg_config_num, GFP_KERNEL);
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700146 if (pci_mmcfg_virt == NULL) {
Dave Jones3095fc02006-10-20 14:45:33 -0700147 printk(KERN_ERR "PCI: Can not allocate memory for mmconfig structures\n");
Olivier Galibertb7867392007-02-13 13:26:20 +0100148 return 0;
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700149 }
Olivier Galibertb7867392007-02-13 13:26:20 +0100150
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700151 for (i = 0; i < pci_mmcfg_config_num; ++i) {
152 pci_mmcfg_virt[i].cfg = &pci_mmcfg_config[i];
OGAWA Hirofumifaed1972007-02-13 13:26:20 +0100153 pci_mmcfg_virt[i].virt = mcfg_ioremap(&pci_mmcfg_config[i]);
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700154 if (!pci_mmcfg_virt[i].virt) {
Dave Jones3095fc02006-10-20 14:45:33 -0700155 printk(KERN_ERR "PCI: Cannot map mmconfig aperture for "
156 "segment %d\n",
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +0300157 pci_mmcfg_config[i].pci_segment);
Olivier Galibertb7867392007-02-13 13:26:20 +0100158 return 0;
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700159 }
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700160 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 raw_pci_ops = &pci_mmcfg;
Olivier Galibertb7867392007-02-13 13:26:20 +0100162 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163}