xref: /kernel/linux/linux-6.6/arch/x86/pci/direct.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * direct.c - Low-level direct PCI config space access
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/pci.h>
762306a36Sopenharmony_ci#include <linux/init.h>
862306a36Sopenharmony_ci#include <linux/dmi.h>
962306a36Sopenharmony_ci#include <asm/pci_x86.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/*
1262306a36Sopenharmony_ci * Functions for accessing PCI base (first 256 bytes) and extended
1362306a36Sopenharmony_ci * (4096 bytes per PCI function) configuration space with type 1
1462306a36Sopenharmony_ci * accesses.
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define PCI_CONF1_ADDRESS(bus, devfn, reg) \
1862306a36Sopenharmony_ci	(0x80000000 | ((reg & 0xF00) << 16) | (bus << 16) \
1962306a36Sopenharmony_ci	| (devfn << 8) | (reg & 0xFC))
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic int pci_conf1_read(unsigned int seg, unsigned int bus,
2262306a36Sopenharmony_ci			  unsigned int devfn, int reg, int len, u32 *value)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	unsigned long flags;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	if (seg || (bus > 255) || (devfn > 255) || (reg > 4095)) {
2762306a36Sopenharmony_ci		*value = -1;
2862306a36Sopenharmony_ci		return -EINVAL;
2962306a36Sopenharmony_ci	}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	raw_spin_lock_irqsave(&pci_config_lock, flags);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	switch (len) {
3662306a36Sopenharmony_ci	case 1:
3762306a36Sopenharmony_ci		*value = inb(0xCFC + (reg & 3));
3862306a36Sopenharmony_ci		break;
3962306a36Sopenharmony_ci	case 2:
4062306a36Sopenharmony_ci		*value = inw(0xCFC + (reg & 2));
4162306a36Sopenharmony_ci		break;
4262306a36Sopenharmony_ci	case 4:
4362306a36Sopenharmony_ci		*value = inl(0xCFC);
4462306a36Sopenharmony_ci		break;
4562306a36Sopenharmony_ci	}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&pci_config_lock, flags);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	return 0;
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic int pci_conf1_write(unsigned int seg, unsigned int bus,
5362306a36Sopenharmony_ci			   unsigned int devfn, int reg, int len, u32 value)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	unsigned long flags;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	if (seg || (bus > 255) || (devfn > 255) || (reg > 4095))
5862306a36Sopenharmony_ci		return -EINVAL;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	raw_spin_lock_irqsave(&pci_config_lock, flags);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	switch (len) {
6562306a36Sopenharmony_ci	case 1:
6662306a36Sopenharmony_ci		outb((u8)value, 0xCFC + (reg & 3));
6762306a36Sopenharmony_ci		break;
6862306a36Sopenharmony_ci	case 2:
6962306a36Sopenharmony_ci		outw((u16)value, 0xCFC + (reg & 2));
7062306a36Sopenharmony_ci		break;
7162306a36Sopenharmony_ci	case 4:
7262306a36Sopenharmony_ci		outl((u32)value, 0xCFC);
7362306a36Sopenharmony_ci		break;
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&pci_config_lock, flags);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	return 0;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#undef PCI_CONF1_ADDRESS
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ciconst struct pci_raw_ops pci_direct_conf1 = {
8462306a36Sopenharmony_ci	.read =		pci_conf1_read,
8562306a36Sopenharmony_ci	.write =	pci_conf1_write,
8662306a36Sopenharmony_ci};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/*
9062306a36Sopenharmony_ci * Functions for accessing PCI configuration space with type 2 accesses
9162306a36Sopenharmony_ci */
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define PCI_CONF2_ADDRESS(dev, reg)	(u16)(0xC000 | (dev << 8) | reg)
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic int pci_conf2_read(unsigned int seg, unsigned int bus,
9662306a36Sopenharmony_ci			  unsigned int devfn, int reg, int len, u32 *value)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	unsigned long flags;
9962306a36Sopenharmony_ci	int dev, fn;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	WARN_ON(seg);
10262306a36Sopenharmony_ci	if ((bus > 255) || (devfn > 255) || (reg > 255)) {
10362306a36Sopenharmony_ci		*value = -1;
10462306a36Sopenharmony_ci		return -EINVAL;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	dev = PCI_SLOT(devfn);
10862306a36Sopenharmony_ci	fn = PCI_FUNC(devfn);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	if (dev & 0x10)
11162306a36Sopenharmony_ci		return PCIBIOS_DEVICE_NOT_FOUND;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	raw_spin_lock_irqsave(&pci_config_lock, flags);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	outb((u8)(0xF0 | (fn << 1)), 0xCF8);
11662306a36Sopenharmony_ci	outb((u8)bus, 0xCFA);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	switch (len) {
11962306a36Sopenharmony_ci	case 1:
12062306a36Sopenharmony_ci		*value = inb(PCI_CONF2_ADDRESS(dev, reg));
12162306a36Sopenharmony_ci		break;
12262306a36Sopenharmony_ci	case 2:
12362306a36Sopenharmony_ci		*value = inw(PCI_CONF2_ADDRESS(dev, reg));
12462306a36Sopenharmony_ci		break;
12562306a36Sopenharmony_ci	case 4:
12662306a36Sopenharmony_ci		*value = inl(PCI_CONF2_ADDRESS(dev, reg));
12762306a36Sopenharmony_ci		break;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	outb(0, 0xCF8);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&pci_config_lock, flags);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	return 0;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic int pci_conf2_write(unsigned int seg, unsigned int bus,
13862306a36Sopenharmony_ci			   unsigned int devfn, int reg, int len, u32 value)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	unsigned long flags;
14162306a36Sopenharmony_ci	int dev, fn;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	WARN_ON(seg);
14462306a36Sopenharmony_ci	if ((bus > 255) || (devfn > 255) || (reg > 255))
14562306a36Sopenharmony_ci		return -EINVAL;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	dev = PCI_SLOT(devfn);
14862306a36Sopenharmony_ci	fn = PCI_FUNC(devfn);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	if (dev & 0x10)
15162306a36Sopenharmony_ci		return PCIBIOS_DEVICE_NOT_FOUND;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	raw_spin_lock_irqsave(&pci_config_lock, flags);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	outb((u8)(0xF0 | (fn << 1)), 0xCF8);
15662306a36Sopenharmony_ci	outb((u8)bus, 0xCFA);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	switch (len) {
15962306a36Sopenharmony_ci	case 1:
16062306a36Sopenharmony_ci		outb((u8)value, PCI_CONF2_ADDRESS(dev, reg));
16162306a36Sopenharmony_ci		break;
16262306a36Sopenharmony_ci	case 2:
16362306a36Sopenharmony_ci		outw((u16)value, PCI_CONF2_ADDRESS(dev, reg));
16462306a36Sopenharmony_ci		break;
16562306a36Sopenharmony_ci	case 4:
16662306a36Sopenharmony_ci		outl((u32)value, PCI_CONF2_ADDRESS(dev, reg));
16762306a36Sopenharmony_ci		break;
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	outb(0, 0xCF8);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	raw_spin_unlock_irqrestore(&pci_config_lock, flags);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return 0;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci#undef PCI_CONF2_ADDRESS
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic const struct pci_raw_ops pci_direct_conf2 = {
18062306a36Sopenharmony_ci	.read =		pci_conf2_read,
18162306a36Sopenharmony_ci	.write =	pci_conf2_write,
18262306a36Sopenharmony_ci};
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci/*
18662306a36Sopenharmony_ci * Before we decide to use direct hardware access mechanisms, we try to do some
18762306a36Sopenharmony_ci * trivial checks to ensure it at least _seems_ to be working -- we just test
18862306a36Sopenharmony_ci * whether bus 00 contains a host bridge (this is similar to checking
18962306a36Sopenharmony_ci * techniques used in XFree86, but ours should be more reliable since we
19062306a36Sopenharmony_ci * attempt to make use of direct access hints provided by the PCI BIOS).
19162306a36Sopenharmony_ci *
19262306a36Sopenharmony_ci * This should be close to trivial, but it isn't, because there are buggy
19362306a36Sopenharmony_ci * chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID.
19462306a36Sopenharmony_ci */
19562306a36Sopenharmony_cistatic int __init pci_sanity_check(const struct pci_raw_ops *o)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	u32 x = 0;
19862306a36Sopenharmony_ci	int devfn;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (pci_probe & PCI_NO_CHECKS)
20162306a36Sopenharmony_ci		return 1;
20262306a36Sopenharmony_ci	/* Assume Type 1 works for newer systems.
20362306a36Sopenharmony_ci	   This handles machines that don't have anything on PCI Bus 0. */
20462306a36Sopenharmony_ci	if (dmi_get_bios_year() >= 2001)
20562306a36Sopenharmony_ci		return 1;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	for (devfn = 0; devfn < 0x100; devfn++) {
20862306a36Sopenharmony_ci		if (o->read(0, 0, devfn, PCI_CLASS_DEVICE, 2, &x))
20962306a36Sopenharmony_ci			continue;
21062306a36Sopenharmony_ci		if (x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA)
21162306a36Sopenharmony_ci			return 1;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci		if (o->read(0, 0, devfn, PCI_VENDOR_ID, 2, &x))
21462306a36Sopenharmony_ci			continue;
21562306a36Sopenharmony_ci		if (x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ)
21662306a36Sopenharmony_ci			return 1;
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	DBG(KERN_WARNING "PCI: Sanity check failed\n");
22062306a36Sopenharmony_ci	return 0;
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic int __init pci_check_type1(void)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	unsigned long flags;
22662306a36Sopenharmony_ci	unsigned int tmp;
22762306a36Sopenharmony_ci	int works = 0;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	local_irq_save(flags);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	outb(0x01, 0xCFB);
23262306a36Sopenharmony_ci	tmp = inl(0xCF8);
23362306a36Sopenharmony_ci	outl(0x80000000, 0xCF8);
23462306a36Sopenharmony_ci	if (inl(0xCF8) == 0x80000000 && pci_sanity_check(&pci_direct_conf1)) {
23562306a36Sopenharmony_ci		works = 1;
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci	outl(tmp, 0xCF8);
23862306a36Sopenharmony_ci	local_irq_restore(flags);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	return works;
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic int __init pci_check_type2(void)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	unsigned long flags;
24662306a36Sopenharmony_ci	int works = 0;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	local_irq_save(flags);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	outb(0x00, 0xCFB);
25162306a36Sopenharmony_ci	outb(0x00, 0xCF8);
25262306a36Sopenharmony_ci	outb(0x00, 0xCFA);
25362306a36Sopenharmony_ci	if (inb(0xCF8) == 0x00 && inb(0xCFA) == 0x00 &&
25462306a36Sopenharmony_ci	    pci_sanity_check(&pci_direct_conf2)) {
25562306a36Sopenharmony_ci		works = 1;
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	local_irq_restore(flags);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	return works;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_civoid __init pci_direct_init(int type)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	if (type == 0)
26662306a36Sopenharmony_ci		return;
26762306a36Sopenharmony_ci	printk(KERN_INFO "PCI: Using configuration type %d for base access\n",
26862306a36Sopenharmony_ci		 type);
26962306a36Sopenharmony_ci	if (type == 1) {
27062306a36Sopenharmony_ci		raw_pci_ops = &pci_direct_conf1;
27162306a36Sopenharmony_ci		if (raw_pci_ext_ops)
27262306a36Sopenharmony_ci			return;
27362306a36Sopenharmony_ci		if (!(pci_probe & PCI_HAS_IO_ECS))
27462306a36Sopenharmony_ci			return;
27562306a36Sopenharmony_ci		printk(KERN_INFO "PCI: Using configuration type 1 "
27662306a36Sopenharmony_ci		       "for extended access\n");
27762306a36Sopenharmony_ci		raw_pci_ext_ops = &pci_direct_conf1;
27862306a36Sopenharmony_ci		return;
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci	raw_pci_ops = &pci_direct_conf2;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ciint __init pci_direct_probe(void)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	if ((pci_probe & PCI_PROBE_CONF1) == 0)
28662306a36Sopenharmony_ci		goto type2;
28762306a36Sopenharmony_ci	if (!request_region(0xCF8, 8, "PCI conf1"))
28862306a36Sopenharmony_ci		goto type2;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	if (pci_check_type1()) {
29162306a36Sopenharmony_ci		raw_pci_ops = &pci_direct_conf1;
29262306a36Sopenharmony_ci		port_cf9_safe = true;
29362306a36Sopenharmony_ci		return 1;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci	release_region(0xCF8, 8);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci type2:
29862306a36Sopenharmony_ci	if ((pci_probe & PCI_PROBE_CONF2) == 0)
29962306a36Sopenharmony_ci		return 0;
30062306a36Sopenharmony_ci	if (!request_region(0xCF8, 4, "PCI conf2"))
30162306a36Sopenharmony_ci		return 0;
30262306a36Sopenharmony_ci	if (!request_region(0xC000, 0x1000, "PCI conf2"))
30362306a36Sopenharmony_ci		goto fail2;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if (pci_check_type2()) {
30662306a36Sopenharmony_ci		raw_pci_ops = &pci_direct_conf2;
30762306a36Sopenharmony_ci		port_cf9_safe = true;
30862306a36Sopenharmony_ci		return 2;
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	release_region(0xC000, 0x1000);
31262306a36Sopenharmony_ci fail2:
31362306a36Sopenharmony_ci	release_region(0xCF8, 4);
31462306a36Sopenharmony_ci	return 0;
31562306a36Sopenharmony_ci}
316