78fa05c6c04d7e3a25887a2ab176dd8b601d3e00
[firefly-linux-kernel-4.4.55.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 static char __iomem *get_virt(unsigned int seg, unsigned bus)
16 {
17         int i;
18         struct pci_mmcfg_region *cfg;
19
20         for (i = 0; i < pci_mmcfg_config_num; ++i) {
21                 cfg = &pci_mmcfg_config[i];
22                 if (cfg->segment == seg &&
23                     (cfg->start_bus <= bus) &&
24                     (cfg->end_bus >= bus))
25                         return cfg->virt;
26         }
27
28         /* Fall back to type 0 */
29         return NULL;
30 }
31
32 static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn)
33 {
34         char __iomem *addr;
35
36         addr = get_virt(seg, bus);
37         if (!addr)
38                 return NULL;
39         return addr + (PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12));
40 }
41
42 static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
43                           unsigned int devfn, int reg, int len, u32 *value)
44 {
45         char __iomem *addr;
46
47         /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
48         if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) {
49 err:            *value = -1;
50                 return -EINVAL;
51         }
52
53         addr = pci_dev_base(seg, bus, devfn);
54         if (!addr)
55                 goto err;
56
57         switch (len) {
58         case 1:
59                 *value = mmio_config_readb(addr + reg);
60                 break;
61         case 2:
62                 *value = mmio_config_readw(addr + reg);
63                 break;
64         case 4:
65                 *value = mmio_config_readl(addr + reg);
66                 break;
67         }
68
69         return 0;
70 }
71
72 static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
73                            unsigned int devfn, int reg, int len, u32 value)
74 {
75         char __iomem *addr;
76
77         /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
78         if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095)))
79                 return -EINVAL;
80
81         addr = pci_dev_base(seg, bus, devfn);
82         if (!addr)
83                 return -EINVAL;
84
85         switch (len) {
86         case 1:
87                 mmio_config_writeb(addr + reg, value);
88                 break;
89         case 2:
90                 mmio_config_writew(addr + reg, value);
91                 break;
92         case 4:
93                 mmio_config_writel(addr + reg, value);
94                 break;
95         }
96
97         return 0;
98 }
99
100 static struct pci_raw_ops pci_mmcfg = {
101         .read =         pci_mmcfg_read,
102         .write =        pci_mmcfg_write,
103 };
104
105 static void __iomem * __init mcfg_ioremap(struct pci_mmcfg_region *cfg)
106 {
107         void __iomem *addr;
108         u64 start, size;
109         int num_buses;
110
111         start = cfg->address + PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
112         num_buses = cfg->end_bus - cfg->start_bus + 1;
113         size = PCI_MMCFG_BUS_OFFSET(num_buses);
114         addr = ioremap_nocache(start, size);
115         if (addr) {
116                 printk(KERN_INFO "PCI: Using MMCONFIG at %Lx - %Lx\n",
117                        start, start + size - 1);
118                 addr -= PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
119         }
120         return addr;
121 }
122
123 int __init pci_mmcfg_arch_init(void)
124 {
125         int i;
126         struct pci_mmcfg_region *cfg;
127
128         for (i = 0; i < pci_mmcfg_config_num; ++i) {
129                 cfg = &pci_mmcfg_config[i];
130                 cfg->virt = mcfg_ioremap(cfg);
131                 if (!cfg->virt) {
132                         printk(KERN_ERR "PCI: Cannot map mmconfig aperture for "
133                                         "segment %d\n",
134                                 cfg->segment);
135                         pci_mmcfg_arch_free();
136                         return 0;
137                 }
138         }
139         raw_pci_ext_ops = &pci_mmcfg;
140         return 1;
141 }
142
143 void __init pci_mmcfg_arch_free(void)
144 {
145         int i;
146         struct pci_mmcfg_region *cfg;
147
148         for (i = 0; i < pci_mmcfg_config_num; ++i) {
149                 cfg = &pci_mmcfg_config[i];
150                 if (cfg->virt) {
151                         iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus));
152                         cfg->virt = NULL;
153                 }
154         }
155 }