xref: /openbmc/linux/arch/x86/pci/direct.c (revision c13aca79ff3c4af5fd31a5b2743a90eba6e36a26)
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