diff options
Diffstat (limited to 'arch/x86/pci/mmconfig_64.c')
-rw-r--r-- | arch/x86/pci/mmconfig_64.c | 52 |
1 files changed, 37 insertions, 15 deletions
diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c index 915a493502cb..d4ebd07c306d 100644 --- a/arch/x86/pci/mmconfig_64.c +++ b/arch/x86/pci/mmconfig_64.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include <linux/init.h> | 9 | #include <linux/init.h> |
10 | #include <linux/acpi.h> | 10 | #include <linux/acpi.h> |
11 | #include <linux/bitmap.h> | 11 | #include <linux/bitmap.h> |
12 | #include <linux/rcupdate.h> | ||
12 | #include <asm/e820.h> | 13 | #include <asm/e820.h> |
13 | #include <asm/pci_x86.h> | 14 | #include <asm/pci_x86.h> |
14 | 15 | ||
@@ -34,9 +35,12 @@ err: *value = -1; | |||
34 | return -EINVAL; | 35 | return -EINVAL; |
35 | } | 36 | } |
36 | 37 | ||
38 | rcu_read_lock(); | ||
37 | addr = pci_dev_base(seg, bus, devfn); | 39 | addr = pci_dev_base(seg, bus, devfn); |
38 | if (!addr) | 40 | if (!addr) { |
41 | rcu_read_unlock(); | ||
39 | goto err; | 42 | goto err; |
43 | } | ||
40 | 44 | ||
41 | switch (len) { | 45 | switch (len) { |
42 | case 1: | 46 | case 1: |
@@ -49,6 +53,7 @@ err: *value = -1; | |||
49 | *value = mmio_config_readl(addr + reg); | 53 | *value = mmio_config_readl(addr + reg); |
50 | break; | 54 | break; |
51 | } | 55 | } |
56 | rcu_read_unlock(); | ||
52 | 57 | ||
53 | return 0; | 58 | return 0; |
54 | } | 59 | } |
@@ -62,9 +67,12 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus, | |||
62 | if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) | 67 | if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) |
63 | return -EINVAL; | 68 | return -EINVAL; |
64 | 69 | ||
70 | rcu_read_lock(); | ||
65 | addr = pci_dev_base(seg, bus, devfn); | 71 | addr = pci_dev_base(seg, bus, devfn); |
66 | if (!addr) | 72 | if (!addr) { |
73 | rcu_read_unlock(); | ||
67 | return -EINVAL; | 74 | return -EINVAL; |
75 | } | ||
68 | 76 | ||
69 | switch (len) { | 77 | switch (len) { |
70 | case 1: | 78 | case 1: |
@@ -77,16 +85,17 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus, | |||
77 | mmio_config_writel(addr + reg, value); | 85 | mmio_config_writel(addr + reg, value); |
78 | break; | 86 | break; |
79 | } | 87 | } |
88 | rcu_read_unlock(); | ||
80 | 89 | ||
81 | return 0; | 90 | return 0; |
82 | } | 91 | } |
83 | 92 | ||
84 | static const struct pci_raw_ops pci_mmcfg = { | 93 | const struct pci_raw_ops pci_mmcfg = { |
85 | .read = pci_mmcfg_read, | 94 | .read = pci_mmcfg_read, |
86 | .write = pci_mmcfg_write, | 95 | .write = pci_mmcfg_write, |
87 | }; | 96 | }; |
88 | 97 | ||
89 | static void __iomem * __init mcfg_ioremap(struct pci_mmcfg_region *cfg) | 98 | static void __iomem * __devinit mcfg_ioremap(struct pci_mmcfg_region *cfg) |
90 | { | 99 | { |
91 | void __iomem *addr; | 100 | void __iomem *addr; |
92 | u64 start, size; | 101 | u64 start, size; |
@@ -105,16 +114,14 @@ int __init pci_mmcfg_arch_init(void) | |||
105 | { | 114 | { |
106 | struct pci_mmcfg_region *cfg; | 115 | struct pci_mmcfg_region *cfg; |
107 | 116 | ||
108 | list_for_each_entry(cfg, &pci_mmcfg_list, list) { | 117 | list_for_each_entry(cfg, &pci_mmcfg_list, list) |
109 | cfg->virt = mcfg_ioremap(cfg); | 118 | if (pci_mmcfg_arch_map(cfg)) { |
110 | if (!cfg->virt) { | ||
111 | printk(KERN_ERR PREFIX "can't map MMCONFIG at %pR\n", | ||
112 | &cfg->res); | ||
113 | pci_mmcfg_arch_free(); | 119 | pci_mmcfg_arch_free(); |
114 | return 0; | 120 | return 0; |
115 | } | 121 | } |
116 | } | 122 | |
117 | raw_pci_ext_ops = &pci_mmcfg; | 123 | raw_pci_ext_ops = &pci_mmcfg; |
124 | |||
118 | return 1; | 125 | return 1; |
119 | } | 126 | } |
120 | 127 | ||
@@ -122,10 +129,25 @@ void __init pci_mmcfg_arch_free(void) | |||
122 | { | 129 | { |
123 | struct pci_mmcfg_region *cfg; | 130 | struct pci_mmcfg_region *cfg; |
124 | 131 | ||
125 | list_for_each_entry(cfg, &pci_mmcfg_list, list) { | 132 | list_for_each_entry(cfg, &pci_mmcfg_list, list) |
126 | if (cfg->virt) { | 133 | pci_mmcfg_arch_unmap(cfg); |
127 | iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus)); | 134 | } |
128 | cfg->virt = NULL; | 135 | |
129 | } | 136 | int __devinit pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg) |
137 | { | ||
138 | cfg->virt = mcfg_ioremap(cfg); | ||
139 | if (!cfg->virt) { | ||
140 | pr_err(PREFIX "can't map MMCONFIG at %pR\n", &cfg->res); | ||
141 | return -ENOMEM; | ||
142 | } | ||
143 | |||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg) | ||
148 | { | ||
149 | if (cfg && cfg->virt) { | ||
150 | iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus)); | ||
151 | cfg->virt = NULL; | ||
130 | } | 152 | } |
131 | } | 153 | } |