blob: cc787a7c030cbee585f59efe0d87b3be8e7abae7 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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>
Greg Kroah-Hartman54549392005-06-23 17:35:56 -070014#include <linux/acpi.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include "pci.h"
16
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#define mmcfg_virt_addr ((void __iomem *) fix_to_virt(FIX_PCIE_MCFG))
18
19/* The base address of the last MMCONFIG device accessed */
20static u32 mmcfg_last_accessed_device;
21
22/*
23 * Functions for accessing PCI configuration space with MMCONFIG accesses
24 */
Greg Kroah-Hartmand57e26c2005-06-23 17:35:56 -070025static u32 get_base_addr(unsigned int seg, int bus)
Linus Torvalds1da177e2005-04-16 15:20:36 -070026{
Greg Kroah-Hartmand57e26c2005-06-23 17:35:56 -070027 int cfg_num = -1;
28 struct acpi_table_mcfg_config *cfg;
29
30 while (1) {
31 ++cfg_num;
32 if (cfg_num >= pci_mmcfg_config_num) {
Andi Kleen928cf8c2005-12-12 22:17:10 -080033 /* Not found - fallback to type 1 */
34 return 0;
Greg Kroah-Hartmand57e26c2005-06-23 17:35:56 -070035 }
36 cfg = &pci_mmcfg_config[cfg_num];
37 if (cfg->pci_segment_group_number != seg)
38 continue;
39 if ((cfg->start_bus_number <= bus) &&
40 (cfg->end_bus_number >= bus))
41 return cfg->base_address;
42 }
43}
44
Andi Kleen928cf8c2005-12-12 22:17:10 -080045static inline void pci_exp_set_dev_base(unsigned int base, int bus, int devfn)
Greg Kroah-Hartmand57e26c2005-06-23 17:35:56 -070046{
Andi Kleen928cf8c2005-12-12 22:17:10 -080047 u32 dev_base = base | (bus << 20) | (devfn << 12);
Linus Torvalds1da177e2005-04-16 15:20:36 -070048 if (dev_base != mmcfg_last_accessed_device) {
49 mmcfg_last_accessed_device = dev_base;
50 set_fixmap_nocache(FIX_PCIE_MCFG, dev_base);
51 }
52}
53
54static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
55 unsigned int devfn, int reg, int len, u32 *value)
56{
57 unsigned long flags;
Andi Kleen928cf8c2005-12-12 22:17:10 -080058 u32 base;
Linus Torvalds1da177e2005-04-16 15:20:36 -070059
60 if (!value || (bus > 255) || (devfn > 255) || (reg > 4095))
61 return -EINVAL;
62
Andi Kleen928cf8c2005-12-12 22:17:10 -080063 base = get_base_addr(seg, bus);
64 if (!base)
65 return pci_conf1_read(seg,bus,devfn,reg,len,value);
66
Linus Torvalds1da177e2005-04-16 15:20:36 -070067 spin_lock_irqsave(&pci_config_lock, flags);
68
Andi Kleen928cf8c2005-12-12 22:17:10 -080069 pci_exp_set_dev_base(base, bus, devfn);
Linus Torvalds1da177e2005-04-16 15:20:36 -070070
71 switch (len) {
72 case 1:
73 *value = readb(mmcfg_virt_addr + reg);
74 break;
75 case 2:
76 *value = readw(mmcfg_virt_addr + reg);
77 break;
78 case 4:
79 *value = readl(mmcfg_virt_addr + reg);
80 break;
81 }
82
83 spin_unlock_irqrestore(&pci_config_lock, flags);
84
85 return 0;
86}
87
88static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
89 unsigned int devfn, int reg, int len, u32 value)
90{
91 unsigned long flags;
Andi Kleen928cf8c2005-12-12 22:17:10 -080092 u32 base;
Linus Torvalds1da177e2005-04-16 15:20:36 -070093
94 if ((bus > 255) || (devfn > 255) || (reg > 4095))
95 return -EINVAL;
96
Andi Kleen928cf8c2005-12-12 22:17:10 -080097 base = get_base_addr(seg, bus);
98 if (!base)
99 return pci_conf1_write(seg,bus,devfn,reg,len,value);
100
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 spin_lock_irqsave(&pci_config_lock, flags);
102
Andi Kleen928cf8c2005-12-12 22:17:10 -0800103 pci_exp_set_dev_base(base, bus, devfn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104
105 switch (len) {
106 case 1:
107 writeb(value, mmcfg_virt_addr + reg);
108 break;
109 case 2:
110 writew(value, mmcfg_virt_addr + reg);
111 break;
112 case 4:
113 writel(value, mmcfg_virt_addr + reg);
114 break;
115 }
116
117 spin_unlock_irqrestore(&pci_config_lock, flags);
118
119 return 0;
120}
121
122static struct pci_raw_ops pci_mmcfg = {
123 .read = pci_mmcfg_read,
124 .write = pci_mmcfg_write,
125};
126
127static int __init pci_mmcfg_init(void)
128{
129 if ((pci_probe & PCI_PROBE_MMCONF) == 0)
130 goto out;
Greg Kroah-Hartman54549392005-06-23 17:35:56 -0700131
132 acpi_table_parse(ACPI_MCFG, acpi_parse_mcfg);
133 if ((pci_mmcfg_config_num == 0) ||
134 (pci_mmcfg_config == NULL) ||
135 (pci_mmcfg_config[0].base_address == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 goto out;
137
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 printk(KERN_INFO "PCI: Using MMCONFIG\n");
139 raw_pci_ops = &pci_mmcfg;
140 pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF;
141
142 out:
143 return 0;
144}
145
146arch_initcall(pci_mmcfg_init);