Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/x86/pci/mmconfig_32.c
10817 views
1
/*
2
* Copyright (C) 2004 Matthew Wilcox <[email protected]>
3
* Copyright (C) 2004 Intel Corp.
4
*
5
* This code is released under the GNU General Public License version 2.
6
*/
7
8
/*
9
* mmconfig.c - Low-level direct PCI config space access via MMCONFIG
10
*/
11
12
#include <linux/pci.h>
13
#include <linux/init.h>
14
#include <asm/e820.h>
15
#include <asm/pci_x86.h>
16
#include <acpi/acpi.h>
17
18
/* Assume systems with more busses have correct MCFG */
19
#define mmcfg_virt_addr ((void __iomem *) fix_to_virt(FIX_PCIE_MCFG))
20
21
/* The base address of the last MMCONFIG device accessed */
22
static u32 mmcfg_last_accessed_device;
23
static int mmcfg_last_accessed_cpu;
24
25
/*
26
* Functions for accessing PCI configuration space with MMCONFIG accesses
27
*/
28
static u32 get_base_addr(unsigned int seg, int bus, unsigned devfn)
29
{
30
struct pci_mmcfg_region *cfg = pci_mmconfig_lookup(seg, bus);
31
32
if (cfg)
33
return cfg->address;
34
return 0;
35
}
36
37
/*
38
* This is always called under pci_config_lock
39
*/
40
static void pci_exp_set_dev_base(unsigned int base, int bus, int devfn)
41
{
42
u32 dev_base = base | PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12);
43
int cpu = smp_processor_id();
44
if (dev_base != mmcfg_last_accessed_device ||
45
cpu != mmcfg_last_accessed_cpu) {
46
mmcfg_last_accessed_device = dev_base;
47
mmcfg_last_accessed_cpu = cpu;
48
set_fixmap_nocache(FIX_PCIE_MCFG, dev_base);
49
}
50
}
51
52
static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
53
unsigned int devfn, int reg, int len, u32 *value)
54
{
55
unsigned long flags;
56
u32 base;
57
58
if ((bus > 255) || (devfn > 255) || (reg > 4095)) {
59
err: *value = -1;
60
return -EINVAL;
61
}
62
63
base = get_base_addr(seg, bus, devfn);
64
if (!base)
65
goto err;
66
67
raw_spin_lock_irqsave(&pci_config_lock, flags);
68
69
pci_exp_set_dev_base(base, bus, devfn);
70
71
switch (len) {
72
case 1:
73
*value = mmio_config_readb(mmcfg_virt_addr + reg);
74
break;
75
case 2:
76
*value = mmio_config_readw(mmcfg_virt_addr + reg);
77
break;
78
case 4:
79
*value = mmio_config_readl(mmcfg_virt_addr + reg);
80
break;
81
}
82
raw_spin_unlock_irqrestore(&pci_config_lock, flags);
83
84
return 0;
85
}
86
87
static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
88
unsigned int devfn, int reg, int len, u32 value)
89
{
90
unsigned long flags;
91
u32 base;
92
93
if ((bus > 255) || (devfn > 255) || (reg > 4095))
94
return -EINVAL;
95
96
base = get_base_addr(seg, bus, devfn);
97
if (!base)
98
return -EINVAL;
99
100
raw_spin_lock_irqsave(&pci_config_lock, flags);
101
102
pci_exp_set_dev_base(base, bus, devfn);
103
104
switch (len) {
105
case 1:
106
mmio_config_writeb(mmcfg_virt_addr + reg, value);
107
break;
108
case 2:
109
mmio_config_writew(mmcfg_virt_addr + reg, value);
110
break;
111
case 4:
112
mmio_config_writel(mmcfg_virt_addr + reg, value);
113
break;
114
}
115
raw_spin_unlock_irqrestore(&pci_config_lock, flags);
116
117
return 0;
118
}
119
120
static struct pci_raw_ops pci_mmcfg = {
121
.read = pci_mmcfg_read,
122
.write = pci_mmcfg_write,
123
};
124
125
int __init pci_mmcfg_arch_init(void)
126
{
127
printk(KERN_INFO "PCI: Using MMCONFIG for extended config space\n");
128
raw_pci_ext_ops = &pci_mmcfg;
129
return 1;
130
}
131
132
void __init pci_mmcfg_arch_free(void)
133
{
134
}
135
136