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