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