1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2fb9aa6f1SThomas Gleixner /*
3fb9aa6f1SThomas Gleixner * direct.c - Low-level direct PCI config space access
4fb9aa6f1SThomas Gleixner */
5fb9aa6f1SThomas Gleixner
6fb9aa6f1SThomas Gleixner #include <linux/pci.h>
7fb9aa6f1SThomas Gleixner #include <linux/init.h>
8fb9aa6f1SThomas Gleixner #include <linux/dmi.h>
982487711SJaswinder Singh Rajput #include <asm/pci_x86.h>
10fb9aa6f1SThomas Gleixner
11fb9aa6f1SThomas Gleixner /*
12831d9918SRobert Richter * Functions for accessing PCI base (first 256 bytes) and extended
13831d9918SRobert Richter * (4096 bytes per PCI function) configuration space with type 1
14831d9918SRobert Richter * accesses.
15fb9aa6f1SThomas Gleixner */
16fb9aa6f1SThomas Gleixner
17fb9aa6f1SThomas Gleixner #define PCI_CONF1_ADDRESS(bus, devfn, reg) \
18831d9918SRobert Richter (0x80000000 | ((reg & 0xF00) << 16) | (bus << 16) \
19831d9918SRobert Richter | (devfn << 8) | (reg & 0xFC))
20fb9aa6f1SThomas Gleixner
pci_conf1_read(unsigned int seg,unsigned int bus,unsigned int devfn,int reg,int len,u32 * value)21b6ce068aSMatthew Wilcox static int pci_conf1_read(unsigned int seg, unsigned int bus,
22fb9aa6f1SThomas Gleixner unsigned int devfn, int reg, int len, u32 *value)
23fb9aa6f1SThomas Gleixner {
24fb9aa6f1SThomas Gleixner unsigned long flags;
25fb9aa6f1SThomas Gleixner
26db34a363SJan Beulich if (seg || (bus > 255) || (devfn > 255) || (reg > 4095)) {
27fb9aa6f1SThomas Gleixner *value = -1;
28fb9aa6f1SThomas Gleixner return -EINVAL;
29fb9aa6f1SThomas Gleixner }
30fb9aa6f1SThomas Gleixner
31d19f61f0SThomas Gleixner raw_spin_lock_irqsave(&pci_config_lock, flags);
32fb9aa6f1SThomas Gleixner
33fb9aa6f1SThomas Gleixner outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);
34fb9aa6f1SThomas Gleixner
35fb9aa6f1SThomas Gleixner switch (len) {
36fb9aa6f1SThomas Gleixner case 1:
37fb9aa6f1SThomas Gleixner *value = inb(0xCFC + (reg & 3));
38fb9aa6f1SThomas Gleixner break;
39fb9aa6f1SThomas Gleixner case 2:
40fb9aa6f1SThomas Gleixner *value = inw(0xCFC + (reg & 2));
41fb9aa6f1SThomas Gleixner break;
42fb9aa6f1SThomas Gleixner case 4:
43fb9aa6f1SThomas Gleixner *value = inl(0xCFC);
44fb9aa6f1SThomas Gleixner break;
45fb9aa6f1SThomas Gleixner }
46fb9aa6f1SThomas Gleixner
47d19f61f0SThomas Gleixner raw_spin_unlock_irqrestore(&pci_config_lock, flags);
48fb9aa6f1SThomas Gleixner
49fb9aa6f1SThomas Gleixner return 0;
50fb9aa6f1SThomas Gleixner }
51fb9aa6f1SThomas Gleixner
pci_conf1_write(unsigned int seg,unsigned int bus,unsigned int devfn,int reg,int len,u32 value)52b6ce068aSMatthew Wilcox static int pci_conf1_write(unsigned int seg, unsigned int bus,
53fb9aa6f1SThomas Gleixner unsigned int devfn, int reg, int len, u32 value)
54fb9aa6f1SThomas Gleixner {
55fb9aa6f1SThomas Gleixner unsigned long flags;
56fb9aa6f1SThomas Gleixner
57db34a363SJan Beulich if (seg || (bus > 255) || (devfn > 255) || (reg > 4095))
58fb9aa6f1SThomas Gleixner return -EINVAL;
59fb9aa6f1SThomas Gleixner
60d19f61f0SThomas Gleixner raw_spin_lock_irqsave(&pci_config_lock, flags);
61fb9aa6f1SThomas Gleixner
62fb9aa6f1SThomas Gleixner outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);
63fb9aa6f1SThomas Gleixner
64fb9aa6f1SThomas Gleixner switch (len) {
65fb9aa6f1SThomas Gleixner case 1:
66fb9aa6f1SThomas Gleixner outb((u8)value, 0xCFC + (reg & 3));
67fb9aa6f1SThomas Gleixner break;
68fb9aa6f1SThomas Gleixner case 2:
69fb9aa6f1SThomas Gleixner outw((u16)value, 0xCFC + (reg & 2));
70fb9aa6f1SThomas Gleixner break;
71fb9aa6f1SThomas Gleixner case 4:
72fb9aa6f1SThomas Gleixner outl((u32)value, 0xCFC);
73fb9aa6f1SThomas Gleixner break;
74fb9aa6f1SThomas Gleixner }
75fb9aa6f1SThomas Gleixner
76d19f61f0SThomas Gleixner raw_spin_unlock_irqrestore(&pci_config_lock, flags);
77fb9aa6f1SThomas Gleixner
78fb9aa6f1SThomas Gleixner return 0;
79fb9aa6f1SThomas Gleixner }
80fb9aa6f1SThomas Gleixner
81fb9aa6f1SThomas Gleixner #undef PCI_CONF1_ADDRESS
82fb9aa6f1SThomas Gleixner
8372da0b07SJan Beulich const struct pci_raw_ops pci_direct_conf1 = {
84fb9aa6f1SThomas Gleixner .read = pci_conf1_read,
85fb9aa6f1SThomas Gleixner .write = pci_conf1_write,
86fb9aa6f1SThomas Gleixner };
87fb9aa6f1SThomas Gleixner
88fb9aa6f1SThomas Gleixner
89fb9aa6f1SThomas Gleixner /*
90fb9aa6f1SThomas Gleixner * Functions for accessing PCI configuration space with type 2 accesses
91fb9aa6f1SThomas Gleixner */
92fb9aa6f1SThomas Gleixner
93fb9aa6f1SThomas Gleixner #define PCI_CONF2_ADDRESS(dev, reg) (u16)(0xC000 | (dev << 8) | reg)
94fb9aa6f1SThomas Gleixner
pci_conf2_read(unsigned int seg,unsigned int bus,unsigned int devfn,int reg,int len,u32 * value)95fb9aa6f1SThomas Gleixner static int pci_conf2_read(unsigned int seg, unsigned int bus,
96fb9aa6f1SThomas Gleixner unsigned int devfn, int reg, int len, u32 *value)
97fb9aa6f1SThomas Gleixner {
98fb9aa6f1SThomas Gleixner unsigned long flags;
99fb9aa6f1SThomas Gleixner int dev, fn;
100fb9aa6f1SThomas Gleixner
101db34a363SJan Beulich WARN_ON(seg);
102fb9aa6f1SThomas Gleixner if ((bus > 255) || (devfn > 255) || (reg > 255)) {
103fb9aa6f1SThomas Gleixner *value = -1;
104fb9aa6f1SThomas Gleixner return -EINVAL;
105fb9aa6f1SThomas Gleixner }
106fb9aa6f1SThomas Gleixner
107fb9aa6f1SThomas Gleixner dev = PCI_SLOT(devfn);
108fb9aa6f1SThomas Gleixner fn = PCI_FUNC(devfn);
109fb9aa6f1SThomas Gleixner
110fb9aa6f1SThomas Gleixner if (dev & 0x10)
111fb9aa6f1SThomas Gleixner return PCIBIOS_DEVICE_NOT_FOUND;
112fb9aa6f1SThomas Gleixner
113d19f61f0SThomas Gleixner raw_spin_lock_irqsave(&pci_config_lock, flags);
114fb9aa6f1SThomas Gleixner
115fb9aa6f1SThomas Gleixner outb((u8)(0xF0 | (fn << 1)), 0xCF8);
116fb9aa6f1SThomas Gleixner outb((u8)bus, 0xCFA);
117fb9aa6f1SThomas Gleixner
118fb9aa6f1SThomas Gleixner switch (len) {
119fb9aa6f1SThomas Gleixner case 1:
120fb9aa6f1SThomas Gleixner *value = inb(PCI_CONF2_ADDRESS(dev, reg));
121fb9aa6f1SThomas Gleixner break;
122fb9aa6f1SThomas Gleixner case 2:
123fb9aa6f1SThomas Gleixner *value = inw(PCI_CONF2_ADDRESS(dev, reg));
124fb9aa6f1SThomas Gleixner break;
125fb9aa6f1SThomas Gleixner case 4:
126fb9aa6f1SThomas Gleixner *value = inl(PCI_CONF2_ADDRESS(dev, reg));
127fb9aa6f1SThomas Gleixner break;
128fb9aa6f1SThomas Gleixner }
129fb9aa6f1SThomas Gleixner
130fb9aa6f1SThomas Gleixner outb(0, 0xCF8);
131fb9aa6f1SThomas Gleixner
132d19f61f0SThomas Gleixner raw_spin_unlock_irqrestore(&pci_config_lock, flags);
133fb9aa6f1SThomas Gleixner
134fb9aa6f1SThomas Gleixner return 0;
135fb9aa6f1SThomas Gleixner }
136fb9aa6f1SThomas Gleixner
pci_conf2_write(unsigned int seg,unsigned int bus,unsigned int devfn,int reg,int len,u32 value)137fb9aa6f1SThomas Gleixner static int pci_conf2_write(unsigned int seg, unsigned int bus,
138fb9aa6f1SThomas Gleixner unsigned int devfn, int reg, int len, u32 value)
139fb9aa6f1SThomas Gleixner {
140fb9aa6f1SThomas Gleixner unsigned long flags;
141fb9aa6f1SThomas Gleixner int dev, fn;
142fb9aa6f1SThomas Gleixner
143db34a363SJan Beulich WARN_ON(seg);
144fb9aa6f1SThomas Gleixner if ((bus > 255) || (devfn > 255) || (reg > 255))
145fb9aa6f1SThomas Gleixner return -EINVAL;
146fb9aa6f1SThomas Gleixner
147fb9aa6f1SThomas Gleixner dev = PCI_SLOT(devfn);
148fb9aa6f1SThomas Gleixner fn = PCI_FUNC(devfn);
149fb9aa6f1SThomas Gleixner
150fb9aa6f1SThomas Gleixner if (dev & 0x10)
151fb9aa6f1SThomas Gleixner return PCIBIOS_DEVICE_NOT_FOUND;
152fb9aa6f1SThomas Gleixner
153d19f61f0SThomas Gleixner raw_spin_lock_irqsave(&pci_config_lock, flags);
154fb9aa6f1SThomas Gleixner
155fb9aa6f1SThomas Gleixner outb((u8)(0xF0 | (fn << 1)), 0xCF8);
156fb9aa6f1SThomas Gleixner outb((u8)bus, 0xCFA);
157fb9aa6f1SThomas Gleixner
158fb9aa6f1SThomas Gleixner switch (len) {
159fb9aa6f1SThomas Gleixner case 1:
160fb9aa6f1SThomas Gleixner outb((u8)value, PCI_CONF2_ADDRESS(dev, reg));
161fb9aa6f1SThomas Gleixner break;
162fb9aa6f1SThomas Gleixner case 2:
163fb9aa6f1SThomas Gleixner outw((u16)value, PCI_CONF2_ADDRESS(dev, reg));
164fb9aa6f1SThomas Gleixner break;
165fb9aa6f1SThomas Gleixner case 4:
166fb9aa6f1SThomas Gleixner outl((u32)value, PCI_CONF2_ADDRESS(dev, reg));
167fb9aa6f1SThomas Gleixner break;
168fb9aa6f1SThomas Gleixner }
169fb9aa6f1SThomas Gleixner
170fb9aa6f1SThomas Gleixner outb(0, 0xCF8);
171fb9aa6f1SThomas Gleixner
172d19f61f0SThomas Gleixner raw_spin_unlock_irqrestore(&pci_config_lock, flags);
173fb9aa6f1SThomas Gleixner
174fb9aa6f1SThomas Gleixner return 0;
175fb9aa6f1SThomas Gleixner }
176fb9aa6f1SThomas Gleixner
177fb9aa6f1SThomas Gleixner #undef PCI_CONF2_ADDRESS
178fb9aa6f1SThomas Gleixner
17972da0b07SJan Beulich static const struct pci_raw_ops pci_direct_conf2 = {
180fb9aa6f1SThomas Gleixner .read = pci_conf2_read,
181fb9aa6f1SThomas Gleixner .write = pci_conf2_write,
182fb9aa6f1SThomas Gleixner };
183fb9aa6f1SThomas Gleixner
184fb9aa6f1SThomas Gleixner
185fb9aa6f1SThomas Gleixner /*
186fb9aa6f1SThomas Gleixner * Before we decide to use direct hardware access mechanisms, we try to do some
187fb9aa6f1SThomas Gleixner * trivial checks to ensure it at least _seems_ to be working -- we just test
188fb9aa6f1SThomas Gleixner * whether bus 00 contains a host bridge (this is similar to checking
189fb9aa6f1SThomas Gleixner * techniques used in XFree86, but ours should be more reliable since we
190fb9aa6f1SThomas Gleixner * attempt to make use of direct access hints provided by the PCI BIOS).
191fb9aa6f1SThomas Gleixner *
192fb9aa6f1SThomas Gleixner * This should be close to trivial, but it isn't, because there are buggy
193fb9aa6f1SThomas Gleixner * chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID.
194fb9aa6f1SThomas Gleixner */
pci_sanity_check(const struct pci_raw_ops * o)19572da0b07SJan Beulich static int __init pci_sanity_check(const struct pci_raw_ops *o)
196fb9aa6f1SThomas Gleixner {
197fb9aa6f1SThomas Gleixner u32 x = 0;
198*69c42d49SAndy Shevchenko int devfn;
199fb9aa6f1SThomas Gleixner
200fb9aa6f1SThomas Gleixner if (pci_probe & PCI_NO_CHECKS)
201fb9aa6f1SThomas Gleixner return 1;
202fb9aa6f1SThomas Gleixner /* Assume Type 1 works for newer systems.
203fb9aa6f1SThomas Gleixner This handles machines that don't have anything on PCI Bus 0. */
204*69c42d49SAndy Shevchenko if (dmi_get_bios_year() >= 2001)
205fb9aa6f1SThomas Gleixner return 1;
206fb9aa6f1SThomas Gleixner
207fb9aa6f1SThomas Gleixner for (devfn = 0; devfn < 0x100; devfn++) {
208fb9aa6f1SThomas Gleixner if (o->read(0, 0, devfn, PCI_CLASS_DEVICE, 2, &x))
209fb9aa6f1SThomas Gleixner continue;
210fb9aa6f1SThomas Gleixner if (x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA)
211fb9aa6f1SThomas Gleixner return 1;
212fb9aa6f1SThomas Gleixner
213fb9aa6f1SThomas Gleixner if (o->read(0, 0, devfn, PCI_VENDOR_ID, 2, &x))
214fb9aa6f1SThomas Gleixner continue;
215fb9aa6f1SThomas Gleixner if (x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ)
216fb9aa6f1SThomas Gleixner return 1;
217fb9aa6f1SThomas Gleixner }
218fb9aa6f1SThomas Gleixner
219fb9aa6f1SThomas Gleixner DBG(KERN_WARNING "PCI: Sanity check failed\n");
220fb9aa6f1SThomas Gleixner return 0;
221fb9aa6f1SThomas Gleixner }
222fb9aa6f1SThomas Gleixner
pci_check_type1(void)223fb9aa6f1SThomas Gleixner static int __init pci_check_type1(void)
224fb9aa6f1SThomas Gleixner {
225fb9aa6f1SThomas Gleixner unsigned long flags;
226fb9aa6f1SThomas Gleixner unsigned int tmp;
227fb9aa6f1SThomas Gleixner int works = 0;
228fb9aa6f1SThomas Gleixner
229fb9aa6f1SThomas Gleixner local_irq_save(flags);
230fb9aa6f1SThomas Gleixner
231fb9aa6f1SThomas Gleixner outb(0x01, 0xCFB);
232fb9aa6f1SThomas Gleixner tmp = inl(0xCF8);
233fb9aa6f1SThomas Gleixner outl(0x80000000, 0xCF8);
234fb9aa6f1SThomas Gleixner if (inl(0xCF8) == 0x80000000 && pci_sanity_check(&pci_direct_conf1)) {
235fb9aa6f1SThomas Gleixner works = 1;
236fb9aa6f1SThomas Gleixner }
237fb9aa6f1SThomas Gleixner outl(tmp, 0xCF8);
238fb9aa6f1SThomas Gleixner local_irq_restore(flags);
239fb9aa6f1SThomas Gleixner
240fb9aa6f1SThomas Gleixner return works;
241fb9aa6f1SThomas Gleixner }
242fb9aa6f1SThomas Gleixner
pci_check_type2(void)243fb9aa6f1SThomas Gleixner static int __init pci_check_type2(void)
244fb9aa6f1SThomas Gleixner {
245fb9aa6f1SThomas Gleixner unsigned long flags;
246fb9aa6f1SThomas Gleixner int works = 0;
247fb9aa6f1SThomas Gleixner
248fb9aa6f1SThomas Gleixner local_irq_save(flags);
249fb9aa6f1SThomas Gleixner
250fb9aa6f1SThomas Gleixner outb(0x00, 0xCFB);
251fb9aa6f1SThomas Gleixner outb(0x00, 0xCF8);
252fb9aa6f1SThomas Gleixner outb(0x00, 0xCFA);
253fb9aa6f1SThomas Gleixner if (inb(0xCF8) == 0x00 && inb(0xCFA) == 0x00 &&
254fb9aa6f1SThomas Gleixner pci_sanity_check(&pci_direct_conf2)) {
255fb9aa6f1SThomas Gleixner works = 1;
256fb9aa6f1SThomas Gleixner }
257fb9aa6f1SThomas Gleixner
258fb9aa6f1SThomas Gleixner local_irq_restore(flags);
259fb9aa6f1SThomas Gleixner
260fb9aa6f1SThomas Gleixner return works;
261fb9aa6f1SThomas Gleixner }
262fb9aa6f1SThomas Gleixner
pci_direct_init(int type)263fb9aa6f1SThomas Gleixner void __init pci_direct_init(int type)
264fb9aa6f1SThomas Gleixner {
265fb9aa6f1SThomas Gleixner if (type == 0)
266fb9aa6f1SThomas Gleixner return;
267bb63b421SYinghai Lu printk(KERN_INFO "PCI: Using configuration type %d for base access\n",
268bb63b421SYinghai Lu type);
269831d9918SRobert Richter if (type == 1) {
270fb9aa6f1SThomas Gleixner raw_pci_ops = &pci_direct_conf1;
2713a27dd1cSRobert Richter if (raw_pci_ext_ops)
2723a27dd1cSRobert Richter return;
2733a27dd1cSRobert Richter if (!(pci_probe & PCI_HAS_IO_ECS))
2743a27dd1cSRobert Richter return;
275831d9918SRobert Richter printk(KERN_INFO "PCI: Using configuration type 1 "
276831d9918SRobert Richter "for extended access\n");
277831d9918SRobert Richter raw_pci_ext_ops = &pci_direct_conf1;
2783a27dd1cSRobert Richter return;
279831d9918SRobert Richter }
280fb9aa6f1SThomas Gleixner raw_pci_ops = &pci_direct_conf2;
281fb9aa6f1SThomas Gleixner }
282fb9aa6f1SThomas Gleixner
pci_direct_probe(void)283fb9aa6f1SThomas Gleixner int __init pci_direct_probe(void)
284fb9aa6f1SThomas Gleixner {
285fb9aa6f1SThomas Gleixner if ((pci_probe & PCI_PROBE_CONF1) == 0)
286fb9aa6f1SThomas Gleixner goto type2;
2870e8ede53SJulia Lawall if (!request_region(0xCF8, 8, "PCI conf1"))
288fb9aa6f1SThomas Gleixner goto type2;
289fb9aa6f1SThomas Gleixner
290bb63b421SYinghai Lu if (pci_check_type1()) {
291bb63b421SYinghai Lu raw_pci_ops = &pci_direct_conf1;
29214d7ca5cSH. Peter Anvin port_cf9_safe = true;
293fb9aa6f1SThomas Gleixner return 1;
294bb63b421SYinghai Lu }
2950e8ede53SJulia Lawall release_region(0xCF8, 8);
296fb9aa6f1SThomas Gleixner
297fb9aa6f1SThomas Gleixner type2:
298fb9aa6f1SThomas Gleixner if ((pci_probe & PCI_PROBE_CONF2) == 0)
299fb9aa6f1SThomas Gleixner return 0;
3000e8ede53SJulia Lawall if (!request_region(0xCF8, 4, "PCI conf2"))
301fb9aa6f1SThomas Gleixner return 0;
3020e8ede53SJulia Lawall if (!request_region(0xC000, 0x1000, "PCI conf2"))
303fb9aa6f1SThomas Gleixner goto fail2;
304fb9aa6f1SThomas Gleixner
305fb9aa6f1SThomas Gleixner if (pci_check_type2()) {
306fb9aa6f1SThomas Gleixner raw_pci_ops = &pci_direct_conf2;
30714d7ca5cSH. Peter Anvin port_cf9_safe = true;
308fb9aa6f1SThomas Gleixner return 2;
309fb9aa6f1SThomas Gleixner }
310fb9aa6f1SThomas Gleixner
3110e8ede53SJulia Lawall release_region(0xC000, 0x1000);
312fb9aa6f1SThomas Gleixner fail2:
3130e8ede53SJulia Lawall release_region(0xCF8, 4);
314fb9aa6f1SThomas Gleixner return 0;
315fb9aa6f1SThomas Gleixner }
316