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