]> bbs.cooldavid.org Git - net-next-2.6.git/blob - arch/x86/pci/mmconfig_64.c
x86/PCI: MMCONFIG: clean up printks
[net-next-2.6.git] / arch / x86 / pci / mmconfig_64.c
1 /*
2  * mmconfig.c - Low-level direct PCI config space access via MMCONFIG
3  *
4  * This is an 64bit optimized version that always keeps the full mmconfig
5  * space mapped. This allows lockless config space operation.
6  */
7
8 #include <linux/pci.h>
9 #include <linux/init.h>
10 #include <linux/acpi.h>
11 #include <linux/bitmap.h>
12 #include <asm/e820.h>
13 #include <asm/pci_x86.h>
14
15 #define PREFIX "PCI: "
16
17 static char __iomem *get_virt(unsigned int seg, unsigned bus)
18 {
19         struct pci_mmcfg_region *cfg;
20
21         list_for_each_entry(cfg, &pci_mmcfg_list, list)
22                 if (cfg->segment == seg &&
23                     (cfg->start_bus <= bus) &&
24                     (cfg->end_bus >= bus))
25                         return cfg->virt;
26
27         /* Fall back to type 0 */
28         return NULL;
29 }
30
31 static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn)
32 {
33         char __iomem *addr;
34
35         addr = get_virt(seg, bus);
36         if (!addr)
37                 return NULL;
38         return addr + (PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12));
39 }
40
41 static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
42                           unsigned int devfn, int reg, int len, u32 *value)
43 {
44         char __iomem *addr;
45
46         /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
47         if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) {
48 err:            *value = -1;
49                 return -EINVAL;
50         }
51
52         addr = pci_dev_base(seg, bus, devfn);
53         if (!addr)
54                 goto err;
55
56         switch (len) {
57         case 1:
58                 *value = mmio_config_readb(addr + reg);
59                 break;
60         case 2:
61                 *value = mmio_config_readw(addr + reg);
62                 break;
63         case 4:
64                 *value = mmio_config_readl(addr + reg);
65                 break;
66         }
67
68         return 0;
69 }
70
71 static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
72                            unsigned int devfn, int reg, int len, u32 value)
73 {
74         char __iomem *addr;
75
76         /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
77         if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095)))
78                 return -EINVAL;
79
80         addr = pci_dev_base(seg, bus, devfn);
81         if (!addr)
82                 return -EINVAL;
83
84         switch (len) {
85         case 1:
86                 mmio_config_writeb(addr + reg, value);
87                 break;
88         case 2:
89                 mmio_config_writew(addr + reg, value);
90                 break;
91         case 4:
92                 mmio_config_writel(addr + reg, value);
93                 break;
94         }
95
96         return 0;
97 }
98
99 static struct pci_raw_ops pci_mmcfg = {
100         .read =         pci_mmcfg_read,
101         .write =        pci_mmcfg_write,
102 };
103
104 static void __iomem * __init mcfg_ioremap(struct pci_mmcfg_region *cfg)
105 {
106         void __iomem *addr;
107         u64 start, size;
108         int num_buses;
109
110         start = cfg->address + PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
111         num_buses = cfg->end_bus - cfg->start_bus + 1;
112         size = PCI_MMCFG_BUS_OFFSET(num_buses);
113         addr = ioremap_nocache(start, size);
114         if (addr)
115                 addr -= PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
116         return addr;
117 }
118
119 int __init pci_mmcfg_arch_init(void)
120 {
121         struct pci_mmcfg_region *cfg;
122
123         list_for_each_entry(cfg, &pci_mmcfg_list, list) {
124                 cfg->virt = mcfg_ioremap(cfg);
125                 if (!cfg->virt) {
126                         printk(KERN_ERR PREFIX "can't map MMCONFIG at %pR\n",
127                                &cfg->res);
128                         pci_mmcfg_arch_free();
129                         return 0;
130                 }
131         }
132         raw_pci_ext_ops = &pci_mmcfg;
133         return 1;
134 }
135
136 void __init pci_mmcfg_arch_free(void)
137 {
138         struct pci_mmcfg_region *cfg;
139
140         list_for_each_entry(cfg, &pci_mmcfg_list, list) {
141                 if (cfg->virt) {
142                         iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus));
143                         cfg->virt = NULL;
144                 }
145         }
146 }