Blame view
arch/x86/pci/mmconfig_64.c
2.83 KB
1da177e4c
|
1 2 |
/* * mmconfig.c - Low-level direct PCI config space access via MMCONFIG |
15a58ed12
|
3 |
* |
1da177e4c
|
4 5 6 7 8 9 |
* This is an 64bit optimized version that always keeps the full mmconfig * space mapped. This allows lockless config space operation. */ #include <linux/pci.h> #include <linux/init.h> |
545493917
|
10 |
#include <linux/acpi.h> |
d6ece5491
|
11 |
#include <linux/bitmap.h> |
946f2ee5c
|
12 |
#include <asm/e820.h> |
824877111
|
13 |
#include <asm/pci_x86.h> |
1da177e4c
|
14 |
|
8c57786ad
|
15 |
#define PREFIX "PCI: " |
8b8a4e33e
|
16 |
static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn) |
1cde8a168
|
17 |
{ |
f6e1d8cc3
|
18 |
struct pci_mmcfg_region *cfg = pci_mmconfig_lookup(seg, bus); |
a0ca99096
|
19 |
|
f6e1d8cc3
|
20 21 22 |
if (cfg && cfg->virt) return cfg->virt + (PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12)); return NULL; |
1da177e4c
|
23 24 25 26 27 |
} static int pci_mmcfg_read(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 *value) { |
8b8a4e33e
|
28 |
char __iomem *addr; |
1da177e4c
|
29 |
|
928cf8c62
|
30 |
/* Why do we have this when nobody checks it. How about a BUG()!? -AK */ |
ecc16ba96
|
31 |
if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) { |
a0ca99096
|
32 |
err: *value = -1; |
1da177e4c
|
33 |
return -EINVAL; |
49c93e84d
|
34 |
} |
1da177e4c
|
35 |
|
928cf8c62
|
36 37 |
addr = pci_dev_base(seg, bus, devfn); if (!addr) |
a0ca99096
|
38 |
goto err; |
928cf8c62
|
39 |
|
1da177e4c
|
40 41 |
switch (len) { case 1: |
3320ad994
|
42 |
*value = mmio_config_readb(addr + reg); |
1da177e4c
|
43 44 |
break; case 2: |
3320ad994
|
45 |
*value = mmio_config_readw(addr + reg); |
1da177e4c
|
46 47 |
break; case 4: |
3320ad994
|
48 |
*value = mmio_config_readl(addr + reg); |
1da177e4c
|
49 50 51 52 53 54 55 56 57 |
break; } return 0; } static int pci_mmcfg_write(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 value) { |
8b8a4e33e
|
58 |
char __iomem *addr; |
1da177e4c
|
59 |
|
928cf8c62
|
60 |
/* Why do we have this when nobody checks it. How about a BUG()!? -AK */ |
1da177e4c
|
61 62 |
if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) return -EINVAL; |
928cf8c62
|
63 64 |
addr = pci_dev_base(seg, bus, devfn); if (!addr) |
a0ca99096
|
65 |
return -EINVAL; |
928cf8c62
|
66 |
|
1da177e4c
|
67 68 |
switch (len) { case 1: |
3320ad994
|
69 |
mmio_config_writeb(addr + reg, value); |
1da177e4c
|
70 71 |
break; case 2: |
3320ad994
|
72 |
mmio_config_writew(addr + reg, value); |
1da177e4c
|
73 74 |
break; case 4: |
3320ad994
|
75 |
mmio_config_writel(addr + reg, value); |
1da177e4c
|
76 77 78 79 80 |
break; } return 0; } |
72da0b07b
|
81 |
static const struct pci_raw_ops pci_mmcfg = { |
1da177e4c
|
82 83 84 |
.read = pci_mmcfg_read, .write = pci_mmcfg_write, }; |
d215a9c8b
|
85 |
static void __iomem * __init mcfg_ioremap(struct pci_mmcfg_region *cfg) |
44de0203f
|
86 87 |
{ void __iomem *addr; |
068258bc1
|
88 |
u64 start, size; |
df5eb1d67
|
89 |
int num_buses; |
068258bc1
|
90 |
|
d7e6b66fe
|
91 92 |
start = cfg->address + PCI_MMCFG_BUS_OFFSET(cfg->start_bus); num_buses = cfg->end_bus - cfg->start_bus + 1; |
df5eb1d67
|
93 |
size = PCI_MMCFG_BUS_OFFSET(num_buses); |
068258bc1
|
94 |
addr = ioremap_nocache(start, size); |
8c57786ad
|
95 |
if (addr) |
d7e6b66fe
|
96 |
addr -= PCI_MMCFG_BUS_OFFSET(cfg->start_bus); |
44de0203f
|
97 98 |
return addr; } |
b78673944
|
99 |
int __init pci_mmcfg_arch_init(void) |
1da177e4c
|
100 |
{ |
3f0f55039
|
101 |
struct pci_mmcfg_region *cfg; |
b78673944
|
102 |
|
ff097ddd4
|
103 |
list_for_each_entry(cfg, &pci_mmcfg_list, list) { |
3f0f55039
|
104 105 |
cfg->virt = mcfg_ioremap(cfg); if (!cfg->virt) { |
8c57786ad
|
106 107 108 |
printk(KERN_ERR PREFIX "can't map MMCONFIG at %pR ", &cfg->res); |
0b64ad712
|
109 |
pci_mmcfg_arch_free(); |
b78673944
|
110 |
return 0; |
1cde8a168
|
111 |
} |
1cde8a168
|
112 |
} |
b6ce068a1
|
113 |
raw_pci_ext_ops = &pci_mmcfg; |
b78673944
|
114 |
return 1; |
1da177e4c
|
115 |
} |
0b64ad712
|
116 117 118 |
void __init pci_mmcfg_arch_free(void) { |
3f0f55039
|
119 |
struct pci_mmcfg_region *cfg; |
0b64ad712
|
120 |
|
ff097ddd4
|
121 |
list_for_each_entry(cfg, &pci_mmcfg_list, list) { |
3f0f55039
|
122 123 124 |
if (cfg->virt) { iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus)); cfg->virt = NULL; |
0b64ad712
|
125 126 |
} } |
0b64ad712
|
127 |
} |