1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2004 Matthew Wilcox <matthew@wil.cx> |
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(segment: 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 | |