blob: c4cf318e44a90cffcb3d5c1d1e53c5cbef09cb6d [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
Ivan Kokshayskya0ca9902008-01-14 17:31:09 -050061 if (reg < 256)
62 return pci_conf1_read(seg,bus,devfn,reg,len,value);
63
Andi Kleen928cf8c2005-12-12 22:17:10 -080064 addr = pci_dev_base(seg, bus, devfn);
65 if (!addr)
Ivan Kokshayskya0ca9902008-01-14 17:31:09 -050066 goto err;
Andi Kleen928cf8c2005-12-12 22:17:10 -080067
Linus Torvalds1da177e2005-04-16 15:20:36 -070068 switch (len) {
69 case 1:
dean gaudet3320ad92007-08-10 22:30:59 +020070 *value = mmio_config_readb(addr + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -070071 break;
72 case 2:
dean gaudet3320ad92007-08-10 22:30:59 +020073 *value = mmio_config_readw(addr + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -070074 break;
75 case 4:
dean gaudet3320ad92007-08-10 22:30:59 +020076 *value = mmio_config_readl(addr + reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -070077 break;
78 }
79
80 return 0;
81}
82
83static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
84 unsigned int devfn, int reg, int len, u32 value)
85{
Al Viro8b8a4e32005-12-15 09:17:44 +000086 char __iomem *addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -070087
Andi Kleen928cf8c2005-12-12 22:17:10 -080088 /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095)))
90 return -EINVAL;
91
Ivan Kokshayskya0ca9902008-01-14 17:31:09 -050092 if (reg < 256)
93 return pci_conf1_write(seg,bus,devfn,reg,len,value);
94
Andi Kleen928cf8c2005-12-12 22:17:10 -080095 addr = pci_dev_base(seg, bus, devfn);
96 if (!addr)
Ivan Kokshayskya0ca9902008-01-14 17:31:09 -050097 return -EINVAL;
Andi Kleen928cf8c2005-12-12 22:17:10 -080098
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 switch (len) {
100 case 1:
dean gaudet3320ad92007-08-10 22:30:59 +0200101 mmio_config_writeb(addr + reg, value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 break;
103 case 2:
dean gaudet3320ad92007-08-10 22:30:59 +0200104 mmio_config_writew(addr + reg, value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 break;
106 case 4:
dean gaudet3320ad92007-08-10 22:30:59 +0200107 mmio_config_writel(addr + reg, value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 break;
109 }
110
111 return 0;
112}
113
114static struct pci_raw_ops pci_mmcfg = {
115 .read = pci_mmcfg_read,
116 .write = pci_mmcfg_write,
117};
118
OGAWA Hirofumi44de0202007-02-13 13:26:20 +0100119static void __iomem * __init mcfg_ioremap(struct acpi_mcfg_allocation *cfg)
120{
121 void __iomem *addr;
122 u32 size;
123
124 size = (cfg->end_bus_number + 1) << 20;
125 addr = ioremap_nocache(cfg->address, size);
126 if (addr) {
127 printk(KERN_INFO "PCI: Using MMCONFIG at %Lx - %Lx\n",
128 cfg->address, cfg->address + size - 1);
129 }
130 return addr;
131}
132
Olivier Galibertb7867392007-02-13 13:26:20 +0100133int __init pci_mmcfg_arch_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134{
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700135 int i;
Olivier Galibertb7867392007-02-13 13:26:20 +0100136 pci_mmcfg_virt = kmalloc(sizeof(*pci_mmcfg_virt) *
137 pci_mmcfg_config_num, GFP_KERNEL);
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700138 if (pci_mmcfg_virt == NULL) {
Dave Jones3095fc02006-10-20 14:45:33 -0700139 printk(KERN_ERR "PCI: Can not allocate memory for mmconfig structures\n");
Olivier Galibertb7867392007-02-13 13:26:20 +0100140 return 0;
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700141 }
Olivier Galibertb7867392007-02-13 13:26:20 +0100142
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700143 for (i = 0; i < pci_mmcfg_config_num; ++i) {
144 pci_mmcfg_virt[i].cfg = &pci_mmcfg_config[i];
OGAWA Hirofumifaed1972007-02-13 13:26:20 +0100145 pci_mmcfg_virt[i].virt = mcfg_ioremap(&pci_mmcfg_config[i]);
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700146 if (!pci_mmcfg_virt[i].virt) {
Dave Jones3095fc02006-10-20 14:45:33 -0700147 printk(KERN_ERR "PCI: Cannot map mmconfig aperture for "
148 "segment %d\n",
Alexey Starikovskiy15a58ed2007-02-02 19:48:22 +0300149 pci_mmcfg_config[i].pci_segment);
Olivier Galibertb7867392007-02-13 13:26:20 +0100150 return 0;
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700151 }
Greg Kroah-Hartman1cde8a12005-06-23 17:35:56 -0700152 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 raw_pci_ops = &pci_mmcfg;
Olivier Galibertb7867392007-02-13 13:26:20 +0100154 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155}