blob: a1994163c99dd328e3c542d1bc0f5dd246e583cf [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;
Ivan Kokshayskya0ca9902008-01-14 17:31:09 -050043
Andi Kleend6ece542005-12-12 22:17:11 -080044 addr = get_virt(seg, bus);
Andi Kleen928cf8c2005-12-12 22:17:10 -080045 if (!addr)
46 return NULL;
47 return addr + ((bus << 20) | (devfn << 12));
Linus Torvalds1da177e2005-04-16 15:20:36 -070048}
49
50static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
51 unsigned int devfn, int reg, int len, u32 *value)
52{
Al Viro8b8a4e32005-12-15 09:17:44 +000053 char __iomem *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -070054
Andi Kleen928cf8c2005-12-12 22:17:10 -080055 /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
Andi Kleenecc16ba2006-04-11 12:54:48 +020056 if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) {
Ivan Kokshayskya0ca9902008-01-14 17:31:09 -050057err: *value = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070058 return -EINVAL;
Andi Kleen49c93e82006-04-07 19:50:15 +020059 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070060
Andi Kleen928cf8c2005-12-12 22:17:10 -080061 addr = pci_dev_base(seg, bus, devfn);
62 if (!addr)
Ivan Kokshayskya0ca9902008-01-14 17:31:09 -050063 goto err;
Andi Kleen928cf8c2005-12-12 22:17:10 -080064
Linus Torvalds1da177e2005-04-16 15:20:36 -070065 switch (len) {
66 case 1:
dean gaudet3320ad92007-08-10 22:30:59 +020067 *value = mmio_config_readb(addr + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -070068 break;
69 case 2:
dean gaudet3320ad92007-08-10 22:30:59 +020070 *value = mmio_config_readw(addr + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -070071 break;
72 case 4:
dean gaudet3320ad92007-08-10 22:30:59 +020073 *value = mmio_config_readl(addr + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -070074 break;
75 }
76
77 return 0;
78}
79
80static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
81 unsigned int devfn, int reg, int len, u32 value)
82{
Al Viro8b8a4e32005-12-15 09:17:44 +000083 char __iomem *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -070084
Andi Kleen928cf8c2005-12-12 22:17:10 -080085 /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
Linus Torvalds1da177e2005-04-16 15:20:36 -070086 if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095)))
87 return -EINVAL;
88
Andi Kleen928cf8c2005-12-12 22:17:10 -080089 addr = pci_dev_base(seg, bus, devfn);
90 if (!addr)
Ivan Kokshayskya0ca9902008-01-14 17:31:09 -050091 return -EINVAL;
Andi Kleen928cf8c2005-12-12 22:17:10 -080092
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 switch (len) {
94 case 1:
dean gaudet3320ad92007-08-10 22:30:59 +020095 mmio_config_writeb(addr + reg, value);
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 break;
97 case 2:
dean gaudet3320ad92007-08-10 22:30:59 +020098 mmio_config_writew(addr + reg, value);
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 break;
100 case 4:
dean gaudet3320ad92007-08-10 22:30:59 +0200101 mmio_config_writel(addr + reg, value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 break;
103 }
104
105 return 0;
106}
107
108static struct pci_raw_ops pci_mmcfg = {
109 .read = pci_mmcfg_read,
110 .write = pci_mmcfg_write,
111};
112
OGAWA Hirofumi44de0202007-02-13 13:26:20 +0100113static void __iomem * __init mcfg_ioremap(struct acpi_mcfg_allocation *cfg)
114{
115 void __iomem *addr;
116 u32 size;
117
118 size = (cfg->end_bus_number + 1) << 20;
119 addr = ioremap_nocache(cfg->address, size);
120 if (addr) {
121 printk(KERN_INFO "PCI: Using MMCONFIG at %Lx - %Lx\n",
122 cfg->address, cfg->address + size - 1);
123 }
124 return addr;
125}
126
Olivier Galibertb7867392007-02-13 13:26:20 +0100127int __init pci_mmcfg_arch_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128{
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700129 int i;
Yinghai Lu0b64ad72008-02-15 01:28:41 -0800130 pci_mmcfg_virt = kzalloc(sizeof(*pci_mmcfg_virt) *
Olivier Galibertb7867392007-02-13 13:26:20 +0100131 pci_mmcfg_config_num, GFP_KERNEL);
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700132 if (pci_mmcfg_virt == NULL) {
Dave Jones3095fc02006-10-20 14:45:33 -0700133 printk(KERN_ERR "PCI: Can not allocate memory for mmconfig structures\n");
Olivier Galibertb7867392007-02-13 13:26:20 +0100134 return 0;
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700135 }
Olivier Galibertb7867392007-02-13 13:26:20 +0100136
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700137 for (i = 0; i < pci_mmcfg_config_num; ++i) {
138 pci_mmcfg_virt[i].cfg = &pci_mmcfg_config[i];
OGAWA Hirofumifaed1972007-02-13 13:26:20 +0100139 pci_mmcfg_virt[i].virt = mcfg_ioremap(&pci_mmcfg_config[i]);
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700140 if (!pci_mmcfg_virt[i].virt) {
Dave Jones3095fc02006-10-20 14:45:33 -0700141 printk(KERN_ERR "PCI: Cannot map mmconfig aperture for "
142 "segment %d\n",
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +0300143 pci_mmcfg_config[i].pci_segment);
Yinghai Lu0b64ad72008-02-15 01:28:41 -0800144 pci_mmcfg_arch_free();
Olivier Galibertb7867392007-02-13 13:26:20 +0100145 return 0;
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700146 }
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700147 }
Matthew Wilcoxb6ce0682008-02-10 09:45:28 -0500148 raw_pci_ext_ops = &pci_mmcfg;
Olivier Galibertb7867392007-02-13 13:26:20 +0100149 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150}
Yinghai Lu0b64ad72008-02-15 01:28:41 -0800151
152void __init pci_mmcfg_arch_free(void)
153{
154 int i;
155
156 if (pci_mmcfg_virt == NULL)
157 return;
158
159 for (i = 0; i < pci_mmcfg_config_num; ++i) {
160 if (pci_mmcfg_virt[i].virt) {
161 iounmap(pci_mmcfg_virt[i].virt);
162 pci_mmcfg_virt[i].virt = NULL;
163 pci_mmcfg_virt[i].cfg = NULL;
164 }
165 }
166
167 kfree(pci_mmcfg_virt);
168 pci_mmcfg_virt = NULL;
169}