[PATCH] mmconfig: Share parts of mmconfig code between i386 and x86-64
[linux-block.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
GKH
29{
30 int cfg_num = -1;
15a58ed1 31 struct acpi_mcfg_allocation *cfg;
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
d57e26ce
GKH
37 while (1) {
38 ++cfg_num;
39 if (cfg_num >= pci_mmcfg_config_num) {
3103039c 40 break;
d57e26ce
GKH
41 }
42 cfg = &pci_mmcfg_config[cfg_num];
15a58ed1 43 if (cfg->pci_segment != seg)
d57e26ce
GKH
44 continue;
45 if ((cfg->start_bus_number <= bus) &&
46 (cfg->end_bus_number >= bus))
15a58ed1 47 return cfg->address;
d57e26ce 48 }
3103039c
AK
49
50 /* Handle more broken MCFG tables on Asus etc.
51 They only contain a single entry for bus 0-0. Assume
52 this applies to all busses. */
53 cfg = &pci_mmcfg_config[0];
54 if (pci_mmcfg_config_num == 1 &&
15a58ed1 55 cfg->pci_segment == 0 &&
3103039c 56 (cfg->start_bus_number | cfg->end_bus_number) == 0)
15a58ed1 57 return cfg->address;
3103039c
AK
58
59 /* Fall back to type 0 */
60 return 0;
d57e26ce 61}
1da177e4 62
be5b7a89
AM
63/*
64 * This is always called under pci_config_lock
65 */
66static void pci_exp_set_dev_base(unsigned int base, int bus, int devfn)
1da177e4 67{
928cf8c6 68 u32 dev_base = base | (bus << 20) | (devfn << 12);
8d1c4819
OH
69 int cpu = smp_processor_id();
70 if (dev_base != mmcfg_last_accessed_device ||
71 cpu != mmcfg_last_accessed_cpu) {
1da177e4 72 mmcfg_last_accessed_device = dev_base;
8d1c4819 73 mmcfg_last_accessed_cpu = cpu;
1da177e4
LT
74 set_fixmap_nocache(FIX_PCIE_MCFG, dev_base);
75 }
76}
77
78static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
79 unsigned int devfn, int reg, int len, u32 *value)
80{
81 unsigned long flags;
928cf8c6 82 u32 base;
1da177e4 83
ecc16ba9 84 if ((bus > 255) || (devfn > 255) || (reg > 4095)) {
49c93e84 85 *value = -1;
1da177e4 86 return -EINVAL;
49c93e84 87 }
1da177e4 88
d6ece549 89 base = get_base_addr(seg, bus, devfn);
928cf8c6
AK
90 if (!base)
91 return pci_conf1_read(seg,bus,devfn,reg,len,value);
92
1da177e4
LT
93 spin_lock_irqsave(&pci_config_lock, flags);
94
928cf8c6 95 pci_exp_set_dev_base(base, bus, devfn);
1da177e4
LT
96
97 switch (len) {
98 case 1:
99 *value = readb(mmcfg_virt_addr + reg);
100 break;
101 case 2:
102 *value = readw(mmcfg_virt_addr + reg);
103 break;
104 case 4:
105 *value = readl(mmcfg_virt_addr + reg);
106 break;
107 }
108
109 spin_unlock_irqrestore(&pci_config_lock, flags);
110
111 return 0;
112}
113
114static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
115 unsigned int devfn, int reg, int len, u32 value)
116{
117 unsigned long flags;
928cf8c6 118 u32 base;
1da177e4 119
15a58ed1 120 if ((bus > 255) || (devfn > 255) || (reg > 4095))
1da177e4
LT
121 return -EINVAL;
122
d6ece549 123 base = get_base_addr(seg, bus, devfn);
928cf8c6
AK
124 if (!base)
125 return pci_conf1_write(seg,bus,devfn,reg,len,value);
126
1da177e4
LT
127 spin_lock_irqsave(&pci_config_lock, flags);
128
928cf8c6 129 pci_exp_set_dev_base(base, bus, devfn);
1da177e4
LT
130
131 switch (len) {
132 case 1:
133 writeb(value, mmcfg_virt_addr + reg);
134 break;
135 case 2:
136 writew(value, mmcfg_virt_addr + reg);
137 break;
138 case 4:
139 writel(value, mmcfg_virt_addr + reg);
140 break;
141 }
142
143 spin_unlock_irqrestore(&pci_config_lock, flags);
144
145 return 0;
146}
147
148static struct pci_raw_ops pci_mmcfg = {
149 .read = pci_mmcfg_read,
150 .write = pci_mmcfg_write,
151};
152
b7867394 153int __init pci_mmcfg_arch_init(void)
d6ece549 154{
1da177e4
LT
155 printk(KERN_INFO "PCI: Using MMCONFIG\n");
156 raw_pci_ops = &pci_mmcfg;
b7867394 157 return 1;
1da177e4 158}