Merge master.kernel.org:/home/rmk/linux-2.6-mmc
[linux-2.6] / arch / i386 / pci / mmconfig.c
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>
14 #include <linux/acpi.h>
15 #include "pci.h"
16
17 #define mmcfg_virt_addr ((void __iomem *) fix_to_virt(FIX_PCIE_MCFG))
18
19 /* The base address of the last MMCONFIG device accessed */
20 static u32 mmcfg_last_accessed_device;
21
22 /*
23  * Functions for accessing PCI configuration space with MMCONFIG accesses
24  */
25 static u32 get_base_addr(unsigned int seg, int bus)
26 {
27         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) {
33                         /* something bad is going on, no cfg table is found. */
34                         /* so we fall back to the old way we used to do this */
35                         /* and just rely on the first entry to be correct. */
36                         return pci_mmcfg_config[0].base_address;
37                 }
38                 cfg = &pci_mmcfg_config[cfg_num];
39                 if (cfg->pci_segment_group_number != seg)
40                         continue;
41                 if ((cfg->start_bus_number <= bus) &&
42                     (cfg->end_bus_number >= bus))
43                         return cfg->base_address;
44         }
45 }
46
47 static inline void pci_exp_set_dev_base(unsigned int seg, int bus, int devfn)
48 {
49         u32 dev_base = get_base_addr(seg, bus) | (bus << 20) | (devfn << 12);
50         if (dev_base != mmcfg_last_accessed_device) {
51                 mmcfg_last_accessed_device = dev_base;
52                 set_fixmap_nocache(FIX_PCIE_MCFG, dev_base);
53         }
54 }
55
56 static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
57                           unsigned int devfn, int reg, int len, u32 *value)
58 {
59         unsigned long flags;
60
61         if (!value || (bus > 255) || (devfn > 255) || (reg > 4095))
62                 return -EINVAL;
63
64         spin_lock_irqsave(&pci_config_lock, flags);
65
66         pci_exp_set_dev_base(seg, bus, devfn);
67
68         switch (len) {
69         case 1:
70                 *value = readb(mmcfg_virt_addr + reg);
71                 break;
72         case 2:
73                 *value = readw(mmcfg_virt_addr + reg);
74                 break;
75         case 4:
76                 *value = readl(mmcfg_virt_addr + reg);
77                 break;
78         }
79
80         spin_unlock_irqrestore(&pci_config_lock, flags);
81
82         return 0;
83 }
84
85 static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
86                            unsigned int devfn, int reg, int len, u32 value)
87 {
88         unsigned long flags;
89
90         if ((bus > 255) || (devfn > 255) || (reg > 4095)) 
91                 return -EINVAL;
92
93         spin_lock_irqsave(&pci_config_lock, flags);
94
95         pci_exp_set_dev_base(seg, bus, devfn);
96
97         switch (len) {
98         case 1:
99                 writeb(value, mmcfg_virt_addr + reg);
100                 break;
101         case 2:
102                 writew(value, mmcfg_virt_addr + reg);
103                 break;
104         case 4:
105                 writel(value, mmcfg_virt_addr + reg);
106                 break;
107         }
108
109         spin_unlock_irqrestore(&pci_config_lock, flags);
110
111         return 0;
112 }
113
114 static struct pci_raw_ops pci_mmcfg = {
115         .read =         pci_mmcfg_read,
116         .write =        pci_mmcfg_write,
117 };
118
119 static int __init pci_mmcfg_init(void)
120 {
121         if ((pci_probe & PCI_PROBE_MMCONF) == 0)
122                 goto out;
123
124         acpi_table_parse(ACPI_MCFG, acpi_parse_mcfg);
125         if ((pci_mmcfg_config_num == 0) ||
126             (pci_mmcfg_config == NULL) ||
127             (pci_mmcfg_config[0].base_address == 0))
128                 goto out;
129
130         printk(KERN_INFO "PCI: Using MMCONFIG\n");
131         raw_pci_ops = &pci_mmcfg;
132         pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF;
133
134  out:
135         return 0;
136 }
137
138 arch_initcall(pci_mmcfg_init);