1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/platform_device.h> |
3 | #include <linux/memregion.h> |
4 | #include <linux/module.h> |
5 | #include <linux/dax.h> |
6 | #include <linux/mm.h> |
7 | |
8 | static bool nohmem; |
9 | module_param_named(disable, nohmem, bool, 0444); |
10 | |
11 | static bool platform_initialized; |
12 | static DEFINE_MUTEX(hmem_resource_lock); |
13 | static struct resource hmem_active = { |
14 | .name = "HMEM devices" , |
15 | .start = 0, |
16 | .end = -1, |
17 | .flags = IORESOURCE_MEM, |
18 | }; |
19 | |
20 | int walk_hmem_resources(struct device *host, walk_hmem_fn fn) |
21 | { |
22 | struct resource *res; |
23 | int rc = 0; |
24 | |
25 | mutex_lock(&hmem_resource_lock); |
26 | for (res = hmem_active.child; res; res = res->sibling) { |
27 | rc = fn(host, (int) res->desc, res); |
28 | if (rc) |
29 | break; |
30 | } |
31 | mutex_unlock(lock: &hmem_resource_lock); |
32 | return rc; |
33 | } |
34 | EXPORT_SYMBOL_GPL(walk_hmem_resources); |
35 | |
36 | static void __hmem_register_resource(int target_nid, struct resource *res) |
37 | { |
38 | struct platform_device *pdev; |
39 | struct resource *new; |
40 | int rc; |
41 | |
42 | new = __request_region(&hmem_active, start: res->start, n: resource_size(res), name: "" , |
43 | flags: 0); |
44 | if (!new) { |
45 | pr_debug("hmem range %pr already active\n" , res); |
46 | return; |
47 | } |
48 | |
49 | new->desc = target_nid; |
50 | |
51 | if (platform_initialized) |
52 | return; |
53 | |
54 | pdev = platform_device_alloc(name: "hmem_platform" , id: 0); |
55 | if (!pdev) { |
56 | pr_err_once("failed to register device-dax hmem_platform device\n" ); |
57 | return; |
58 | } |
59 | |
60 | rc = platform_device_add(pdev); |
61 | if (rc) |
62 | platform_device_put(pdev); |
63 | else |
64 | platform_initialized = true; |
65 | } |
66 | |
67 | void hmem_register_resource(int target_nid, struct resource *res) |
68 | { |
69 | if (nohmem) |
70 | return; |
71 | |
72 | mutex_lock(&hmem_resource_lock); |
73 | __hmem_register_resource(target_nid, res); |
74 | mutex_unlock(lock: &hmem_resource_lock); |
75 | } |
76 | |
77 | static __init int hmem_register_one(struct resource *res, void *data) |
78 | { |
79 | hmem_register_resource(phys_to_target_node(start: res->start), res); |
80 | |
81 | return 0; |
82 | } |
83 | |
84 | static __init int hmem_init(void) |
85 | { |
86 | walk_iomem_res_desc(desc: IORES_DESC_SOFT_RESERVED, |
87 | IORESOURCE_MEM, start: 0, end: -1, NULL, func: hmem_register_one); |
88 | return 0; |
89 | } |
90 | |
91 | /* |
92 | * As this is a fallback for address ranges unclaimed by the ACPI HMAT |
93 | * parsing it must be at an initcall level greater than hmat_init(). |
94 | */ |
95 | device_initcall(hmem_init); |
96 | |