mmconfig_32.c (3450B)
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 */ 21static u32 mmcfg_last_accessed_device; 22static int mmcfg_last_accessed_cpu; 23 24/* 25 * Functions for accessing PCI configuration space with MMCONFIG accesses 26 */ 27static u32 get_base_addr(unsigned int seg, int bus, unsigned devfn) 28{ 29 struct pci_mmcfg_region *cfg = pci_mmconfig_lookup(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 */ 39static 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 51static 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)) { 58err: *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 90static 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 127const struct pci_raw_ops pci_mmcfg = { 128 .read = pci_mmcfg_read, 129 .write = pci_mmcfg_write, 130}; 131 132int __init pci_mmcfg_arch_init(void) 133{ 134 printk(KERN_INFO "PCI: Using MMCONFIG for extended config space\n"); 135 raw_pci_ext_ops = &pci_mmcfg; 136 return 1; 137} 138 139void __init pci_mmcfg_arch_free(void) 140{ 141} 142 143int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg) 144{ 145 return 0; 146} 147 148void 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}