diff options
Diffstat (limited to 'drivers/xen/arm-device.c')
-rw-r--r-- | drivers/xen/arm-device.c | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/drivers/xen/arm-device.c b/drivers/xen/arm-device.c new file mode 100644 index 000000000000..778acf80aacb --- /dev/null +++ b/drivers/xen/arm-device.c | |||
@@ -0,0 +1,196 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2015, Linaro Limited, Shannon Zhao | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms and conditions of the GNU General Public License, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | */ | ||
16 | |||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/acpi.h> | ||
19 | #include <xen/xen.h> | ||
20 | #include <xen/page.h> | ||
21 | #include <xen/interface/memory.h> | ||
22 | #include <asm/xen/hypervisor.h> | ||
23 | #include <asm/xen/hypercall.h> | ||
24 | |||
25 | static int xen_unmap_device_mmio(const struct resource *resources, | ||
26 | unsigned int count) | ||
27 | { | ||
28 | unsigned int i, j, nr; | ||
29 | int rc = 0; | ||
30 | const struct resource *r; | ||
31 | struct xen_remove_from_physmap xrp; | ||
32 | |||
33 | for (i = 0; i < count; i++) { | ||
34 | r = &resources[i]; | ||
35 | nr = DIV_ROUND_UP(resource_size(r), XEN_PAGE_SIZE); | ||
36 | if ((resource_type(r) != IORESOURCE_MEM) || (nr == 0)) | ||
37 | continue; | ||
38 | |||
39 | for (j = 0; j < nr; j++) { | ||
40 | xrp.domid = DOMID_SELF; | ||
41 | xrp.gpfn = XEN_PFN_DOWN(r->start) + j; | ||
42 | rc = HYPERVISOR_memory_op(XENMEM_remove_from_physmap, | ||
43 | &xrp); | ||
44 | if (rc) | ||
45 | return rc; | ||
46 | } | ||
47 | } | ||
48 | |||
49 | return rc; | ||
50 | } | ||
51 | |||
52 | static int xen_map_device_mmio(const struct resource *resources, | ||
53 | unsigned int count) | ||
54 | { | ||
55 | unsigned int i, j, nr; | ||
56 | int rc = 0; | ||
57 | const struct resource *r; | ||
58 | xen_pfn_t *gpfns; | ||
59 | xen_ulong_t *idxs; | ||
60 | int *errs; | ||
61 | struct xen_add_to_physmap_range xatp; | ||
62 | |||
63 | for (i = 0; i < count; i++) { | ||
64 | r = &resources[i]; | ||
65 | nr = DIV_ROUND_UP(resource_size(r), XEN_PAGE_SIZE); | ||
66 | if ((resource_type(r) != IORESOURCE_MEM) || (nr == 0)) | ||
67 | continue; | ||
68 | |||
69 | gpfns = kzalloc(sizeof(xen_pfn_t) * nr, GFP_KERNEL); | ||
70 | idxs = kzalloc(sizeof(xen_ulong_t) * nr, GFP_KERNEL); | ||
71 | errs = kzalloc(sizeof(int) * nr, GFP_KERNEL); | ||
72 | if (!gpfns || !idxs || !errs) { | ||
73 | kfree(gpfns); | ||
74 | kfree(idxs); | ||
75 | kfree(errs); | ||
76 | rc = -ENOMEM; | ||
77 | goto unmap; | ||
78 | } | ||
79 | |||
80 | for (j = 0; j < nr; j++) { | ||
81 | /* | ||
82 | * The regions are always mapped 1:1 to DOM0 and this is | ||
83 | * fine because the memory map for DOM0 is the same as | ||
84 | * the host (except for the RAM). | ||
85 | */ | ||
86 | gpfns[j] = XEN_PFN_DOWN(r->start) + j; | ||
87 | idxs[j] = XEN_PFN_DOWN(r->start) + j; | ||
88 | } | ||
89 | |||
90 | xatp.domid = DOMID_SELF; | ||
91 | xatp.size = nr; | ||
92 | xatp.space = XENMAPSPACE_dev_mmio; | ||
93 | |||
94 | set_xen_guest_handle(xatp.gpfns, gpfns); | ||
95 | set_xen_guest_handle(xatp.idxs, idxs); | ||
96 | set_xen_guest_handle(xatp.errs, errs); | ||
97 | |||
98 | rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap_range, &xatp); | ||
99 | kfree(gpfns); | ||
100 | kfree(idxs); | ||
101 | kfree(errs); | ||
102 | if (rc) | ||
103 | goto unmap; | ||
104 | } | ||
105 | |||
106 | return rc; | ||
107 | |||
108 | unmap: | ||
109 | xen_unmap_device_mmio(resources, i); | ||
110 | return rc; | ||
111 | } | ||
112 | |||
113 | static int xen_platform_notifier(struct notifier_block *nb, | ||
114 | unsigned long action, void *data) | ||
115 | { | ||
116 | struct platform_device *pdev = to_platform_device(data); | ||
117 | int r = 0; | ||
118 | |||
119 | if (pdev->num_resources == 0 || pdev->resource == NULL) | ||
120 | return NOTIFY_OK; | ||
121 | |||
122 | switch (action) { | ||
123 | case BUS_NOTIFY_ADD_DEVICE: | ||
124 | r = xen_map_device_mmio(pdev->resource, pdev->num_resources); | ||
125 | break; | ||
126 | case BUS_NOTIFY_DEL_DEVICE: | ||
127 | r = xen_unmap_device_mmio(pdev->resource, pdev->num_resources); | ||
128 | break; | ||
129 | default: | ||
130 | return NOTIFY_DONE; | ||
131 | } | ||
132 | if (r) | ||
133 | dev_err(&pdev->dev, "Platform: Failed to %s device %s MMIO!\n", | ||
134 | action == BUS_NOTIFY_ADD_DEVICE ? "map" : | ||
135 | (action == BUS_NOTIFY_DEL_DEVICE ? "unmap" : "?"), | ||
136 | pdev->name); | ||
137 | |||
138 | return NOTIFY_OK; | ||
139 | } | ||
140 | |||
141 | static struct notifier_block platform_device_nb = { | ||
142 | .notifier_call = xen_platform_notifier, | ||
143 | }; | ||
144 | |||
145 | static int __init register_xen_platform_notifier(void) | ||
146 | { | ||
147 | if (!xen_initial_domain() || acpi_disabled) | ||
148 | return 0; | ||
149 | |||
150 | return bus_register_notifier(&platform_bus_type, &platform_device_nb); | ||
151 | } | ||
152 | |||
153 | arch_initcall(register_xen_platform_notifier); | ||
154 | |||
155 | #ifdef CONFIG_ARM_AMBA | ||
156 | #include <linux/amba/bus.h> | ||
157 | |||
158 | static int xen_amba_notifier(struct notifier_block *nb, | ||
159 | unsigned long action, void *data) | ||
160 | { | ||
161 | struct amba_device *adev = to_amba_device(data); | ||
162 | int r = 0; | ||
163 | |||
164 | switch (action) { | ||
165 | case BUS_NOTIFY_ADD_DEVICE: | ||
166 | r = xen_map_device_mmio(&adev->res, 1); | ||
167 | break; | ||
168 | case BUS_NOTIFY_DEL_DEVICE: | ||
169 | r = xen_unmap_device_mmio(&adev->res, 1); | ||
170 | break; | ||
171 | default: | ||
172 | return NOTIFY_DONE; | ||
173 | } | ||
174 | if (r) | ||
175 | dev_err(&adev->dev, "AMBA: Failed to %s device %s MMIO!\n", | ||
176 | action == BUS_NOTIFY_ADD_DEVICE ? "map" : | ||
177 | (action == BUS_NOTIFY_DEL_DEVICE ? "unmap" : "?"), | ||
178 | adev->dev.init_name); | ||
179 | |||
180 | return NOTIFY_OK; | ||
181 | } | ||
182 | |||
183 | static struct notifier_block amba_device_nb = { | ||
184 | .notifier_call = xen_amba_notifier, | ||
185 | }; | ||
186 | |||
187 | static int __init register_xen_amba_notifier(void) | ||
188 | { | ||
189 | if (!xen_initial_domain() || acpi_disabled) | ||
190 | return 0; | ||
191 | |||
192 | return bus_register_notifier(&amba_bustype, &amba_device_nb); | ||
193 | } | ||
194 | |||
195 | arch_initcall(register_xen_amba_notifier); | ||
196 | #endif | ||