x86: Work around mmio config space quirk on AMD Fam10h
[deliverable/linux.git] / arch / i386 / pci / mmconfig.c
CommitLineData
1da177e4
LT
1/*
2 * Copyright (C) 2004 Matthew Wilcox <matthew@wil.cx>
3 * Copyright (C) 2004 Intel Corp.
4 *
5 * This code is released under the GNU General Public License version 2.
6 */
7
8/*
9 * mmconfig.c - Low-level direct PCI config space access via MMCONFIG
10 */
11
12#include <linux/pci.h>
13#include <linux/init.h>
54549391 14#include <linux/acpi.h>
946f2ee5 15#include <asm/e820.h>
1da177e4
LT
16#include "pci.h"
17
8c30b1a7 18/* Assume systems with more busses have correct MCFG */
1da177e4
LT
19#define mmcfg_virt_addr ((void __iomem *) fix_to_virt(FIX_PCIE_MCFG))
20
21/* The base address of the last MMCONFIG device accessed */
22static u32 mmcfg_last_accessed_device;
8d1c4819 23static int mmcfg_last_accessed_cpu;
1da177e4
LT
24
25/*
26 * Functions for accessing PCI configuration space with MMCONFIG accesses
27 */
d6ece549 28static u32 get_base_addr(unsigned int seg, int bus, unsigned devfn)
d57e26ce 29{
15a58ed1 30 struct acpi_mcfg_allocation *cfg;
429d512e 31 int cfg_num;
d57e26ce 32
b7867394
OG
33 if (seg == 0 && bus < PCI_MMCFG_MAX_CHECK_BUS &&
34 test_bit(PCI_SLOT(devfn) + 32*bus, pci_mmcfg_fallback_slots))
d6ece549
AK
35 return 0;
36
429d512e 37 for (cfg_num = 0; cfg_num < pci_mmcfg_config_num; cfg_num++) {
d57e26ce 38 cfg = &pci_mmcfg_config[cfg_num];
429d512e
OH
39 if (cfg->pci_segment == seg &&
40 (cfg->start_bus_number <= bus) &&
d57e26ce 41 (cfg->end_bus_number >= bus))
15a58ed1 42 return cfg->address;
d57e26ce 43 }
3103039c 44
3103039c
AK
45 /* Fall back to type 0 */
46 return 0;
d57e26ce 47}
1da177e4 48
be5b7a89
AM
49/*
50 * This is always called under pci_config_lock
51 */
52static void pci_exp_set_dev_base(unsigned int base, int bus, int devfn)
1da177e4 53{
928cf8c6 54 u32 dev_base = base | (bus << 20) | (devfn << 12);
8d1c4819
OH
55 int cpu = smp_processor_id();
56 if (dev_base != mmcfg_last_accessed_device ||
57 cpu != mmcfg_last_accessed_cpu) {
1da177e4 58 mmcfg_last_accessed_device = dev_base;
8d1c4819 59 mmcfg_last_accessed_cpu = cpu;
1da177e4
LT
60 set_fixmap_nocache(FIX_PCIE_MCFG, dev_base);
61 }
62}
63
64static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
65 unsigned int devfn, int reg, int len, u32 *value)
66{
67 unsigned long flags;
928cf8c6 68 u32 base;
1da177e4 69
ecc16ba9 70 if ((bus > 255) || (devfn > 255) || (reg > 4095)) {
49c93e84 71 *value = -1;
1da177e4 72 return -EINVAL;
49c93e84 73 }
1da177e4 74
d6ece549 75 base = get_base_addr(seg, bus, devfn);
928cf8c6
AK
76 if (!base)
77 return pci_conf1_read(seg,bus,devfn,reg,len,value);
78
1da177e4
LT
79 spin_lock_irqsave(&pci_config_lock, flags);
80
928cf8c6 81 pci_exp_set_dev_base(base, bus, devfn);
1da177e4
LT
82
83 switch (len) {
84 case 1:
3320ad99 85 *value = mmio_config_readb(mmcfg_virt_addr + reg);
1da177e4
LT
86 break;
87 case 2:
3320ad99 88 *value = mmio_config_readw(mmcfg_virt_addr + reg);
1da177e4
LT
89 break;
90 case 4:
3320ad99 91 *value = mmio_config_readl(mmcfg_virt_addr + reg);
1da177e4
LT
92 break;
93 }
1da177e4
LT
94 spin_unlock_irqrestore(&pci_config_lock, flags);
95
96 return 0;
97}
98
99static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
100 unsigned int devfn, int reg, int len, u32 value)
101{
102 unsigned long flags;
928cf8c6 103 u32 base;
1da177e4 104
15a58ed1 105 if ((bus > 255) || (devfn > 255) || (reg > 4095))
1da177e4
LT
106 return -EINVAL;
107
d6ece549 108 base = get_base_addr(seg, bus, devfn);
928cf8c6
AK
109 if (!base)
110 return pci_conf1_write(seg,bus,devfn,reg,len,value);
111
1da177e4
LT
112 spin_lock_irqsave(&pci_config_lock, flags);
113
928cf8c6 114 pci_exp_set_dev_base(base, bus, devfn);
1da177e4
LT
115
116 switch (len) {
117 case 1:
3320ad99 118 mmio_config_writeb(mmcfg_virt_addr, value);
1da177e4
LT
119 break;
120 case 2:
3320ad99 121 mmio_config_writew(mmcfg_virt_addr, value);
1da177e4
LT
122 break;
123 case 4:
3320ad99 124 mmio_config_writel(mmcfg_virt_addr, value);
1da177e4
LT
125 break;
126 }
1da177e4
LT
127 spin_unlock_irqrestore(&pci_config_lock, flags);
128
129 return 0;
130}
131
132static struct pci_raw_ops pci_mmcfg = {
133 .read = pci_mmcfg_read,
134 .write = pci_mmcfg_write,
135};
136
56829d19
OH
137int __init pci_mmcfg_arch_reachable(unsigned int seg, unsigned int bus,
138 unsigned int devfn)
139{
140 return get_base_addr(seg, bus, devfn) != 0;
141}
142
b7867394 143int __init pci_mmcfg_arch_init(void)
d6ece549 144{
1da177e4
LT
145 printk(KERN_INFO "PCI: Using MMCONFIG\n");
146 raw_pci_ops = &pci_mmcfg;
b7867394 147 return 1;
1da177e4 148}
This page took 0.261185 seconds and 5 git commands to generate.