GNU Linux-libre 6.1.24-gnu
[releases.git] / drivers / dax / hmem / device.c
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 struct resource hmem_active = {
12         .name = "HMEM devices",
13         .start = 0,
14         .end = -1,
15         .flags = IORESOURCE_MEM,
16 };
17
18 void hmem_register_device(int target_nid, struct resource *r)
19 {
20         /* define a clean / non-busy resource for the platform device */
21         struct resource res = {
22                 .start = r->start,
23                 .end = r->end,
24                 .flags = IORESOURCE_MEM,
25                 .desc = IORES_DESC_SOFT_RESERVED,
26         };
27         struct platform_device *pdev;
28         struct memregion_info info;
29         int rc, id;
30
31         if (nohmem)
32                 return;
33
34         rc = region_intersects(res.start, resource_size(&res), IORESOURCE_MEM,
35                         IORES_DESC_SOFT_RESERVED);
36         if (rc != REGION_INTERSECTS)
37                 return;
38
39         id = memregion_alloc(GFP_KERNEL);
40         if (id < 0) {
41                 pr_err("memregion allocation failure for %pr\n", &res);
42                 return;
43         }
44
45         pdev = platform_device_alloc("hmem", id);
46         if (!pdev) {
47                 pr_err("hmem device allocation failure for %pr\n", &res);
48                 goto out_pdev;
49         }
50
51         if (!__request_region(&hmem_active, res.start, resource_size(&res),
52                               dev_name(&pdev->dev), 0)) {
53                 dev_dbg(&pdev->dev, "hmem range %pr already active\n", &res);
54                 goto out_active;
55         }
56
57         pdev->dev.numa_node = numa_map_to_online_node(target_nid);
58         info = (struct memregion_info) {
59                 .target_node = target_nid,
60         };
61         rc = platform_device_add_data(pdev, &info, sizeof(info));
62         if (rc < 0) {
63                 pr_err("hmem memregion_info allocation failure for %pr\n", &res);
64                 goto out_resource;
65         }
66
67         rc = platform_device_add_resources(pdev, &res, 1);
68         if (rc < 0) {
69                 pr_err("hmem resource allocation failure for %pr\n", &res);
70                 goto out_resource;
71         }
72
73         rc = platform_device_add(pdev);
74         if (rc < 0) {
75                 dev_err(&pdev->dev, "device add failed for %pr\n", &res);
76                 goto out_resource;
77         }
78
79         return;
80
81 out_resource:
82         __release_region(&hmem_active, res.start, resource_size(&res));
83 out_active:
84         platform_device_put(pdev);
85 out_pdev:
86         memregion_free(id);
87 }
88
89 static __init int hmem_register_one(struct resource *res, void *data)
90 {
91         hmem_register_device(phys_to_target_node(res->start), res);
92
93         return 0;
94 }
95
96 static __init int hmem_init(void)
97 {
98         walk_iomem_res_desc(IORES_DESC_SOFT_RESERVED,
99                         IORESOURCE_MEM, 0, -1, NULL, hmem_register_one);
100         return 0;
101 }
102
103 /*
104  * As this is a fallback for address ranges unclaimed by the ACPI HMAT
105  * parsing it must be at an initcall level greater than hmat_init().
106  */
107 late_initcall(hmem_init);