|
14 | 14 | #include <linux/types.h>
|
15 | 15 | #include <linux/io.h>
|
16 | 16 | #include <linux/mm.h>
|
| 17 | +#include <linux/memory_hotplug.h> |
17 | 18 |
|
18 | 19 | #ifndef ioremap_cache
|
19 | 20 | /* temporary while we convert existing ioremap_cache users to memremap */
|
@@ -135,3 +136,55 @@ void devm_memunmap(struct device *dev, void *addr)
|
135 | 136 | memunmap(addr);
|
136 | 137 | }
|
137 | 138 | EXPORT_SYMBOL(devm_memunmap);
|
| 139 | + |
| 140 | +#ifdef CONFIG_ZONE_DEVICE |
| 141 | +struct page_map { |
| 142 | + struct resource res; |
| 143 | +}; |
| 144 | + |
| 145 | +static void devm_memremap_pages_release(struct device *dev, void *res) |
| 146 | +{ |
| 147 | + struct page_map *page_map = res; |
| 148 | + |
| 149 | + /* pages are dead and unused, undo the arch mapping */ |
| 150 | + arch_remove_memory(page_map->res.start, resource_size(&page_map->res)); |
| 151 | +} |
| 152 | + |
| 153 | +void *devm_memremap_pages(struct device *dev, struct resource *res) |
| 154 | +{ |
| 155 | + int is_ram = region_intersects(res->start, resource_size(res), |
| 156 | + "System RAM"); |
| 157 | + struct page_map *page_map; |
| 158 | + int error, nid; |
| 159 | + |
| 160 | + if (is_ram == REGION_MIXED) { |
| 161 | + WARN_ONCE(1, "%s attempted on mixed region %pr\n", |
| 162 | + __func__, res); |
| 163 | + return ERR_PTR(-ENXIO); |
| 164 | + } |
| 165 | + |
| 166 | + if (is_ram == REGION_INTERSECTS) |
| 167 | + return __va(res->start); |
| 168 | + |
| 169 | + page_map = devres_alloc(devm_memremap_pages_release, |
| 170 | + sizeof(*page_map), GFP_KERNEL); |
| 171 | + if (!page_map) |
| 172 | + return ERR_PTR(-ENOMEM); |
| 173 | + |
| 174 | + memcpy(&page_map->res, res, sizeof(*res)); |
| 175 | + |
| 176 | + nid = dev_to_node(dev); |
| 177 | + if (nid < 0) |
| 178 | + nid = 0; |
| 179 | + |
| 180 | + error = arch_add_memory(nid, res->start, resource_size(res), true); |
| 181 | + if (error) { |
| 182 | + devres_free(page_map); |
| 183 | + return ERR_PTR(error); |
| 184 | + } |
| 185 | + |
| 186 | + devres_add(dev, page_map); |
| 187 | + return __va(res->start); |
| 188 | +} |
| 189 | +EXPORT_SYMBOL(devm_memremap_pages); |
| 190 | +#endif /* CONFIG_ZONE_DEVICE */ |
0 commit comments