| // SPDX-License-Identifier: GPL-2.0 | |
| #include <linux/platform_device.h> | |
| #include <linux/memregion.h> | |
| #include <linux/module.h> | |
| #include <linux/dax.h> | |
| #include <linux/mm.h> | |
| static bool nohmem; | |
| module_param_named(disable, nohmem, bool, 0444); | |
| void hmem_register_device(int target_nid, struct resource *r) | |
| { | |
| /* define a clean / non-busy resource for the platform device */ | |
| struct resource res = { | |
| .start = r->start, | |
| .end = r->end, | |
| .flags = IORESOURCE_MEM, | |
| }; | |
| struct platform_device *pdev; | |
| struct memregion_info info; | |
| int rc, id; | |
| if (nohmem) | |
| return; | |
| rc = region_intersects(res.start, resource_size(&res), IORESOURCE_MEM, | |
| IORES_DESC_SOFT_RESERVED); | |
| if (rc != REGION_INTERSECTS) | |
| return; | |
| id = memregion_alloc(GFP_KERNEL); | |
| if (id < 0) { | |
| pr_err("memregion allocation failure for %pr\n", &res); | |
| return; | |
| } | |
| pdev = platform_device_alloc("hmem", id); | |
| if (!pdev) { | |
| pr_err("hmem device allocation failure for %pr\n", &res); | |
| goto out_pdev; | |
| } | |
| pdev->dev.numa_node = numa_map_to_online_node(target_nid); | |
| info = (struct memregion_info) { | |
| .target_node = target_nid, | |
| }; | |
| rc = platform_device_add_data(pdev, &info, sizeof(info)); | |
| if (rc < 0) { | |
| pr_err("hmem memregion_info allocation failure for %pr\n", &res); | |
| goto out_pdev; | |
| } | |
| rc = platform_device_add_resources(pdev, &res, 1); | |
| if (rc < 0) { | |
| pr_err("hmem resource allocation failure for %pr\n", &res); | |
| goto out_resource; | |
| } | |
| rc = platform_device_add(pdev); | |
| if (rc < 0) { | |
| dev_err(&pdev->dev, "device add failed for %pr\n", &res); | |
| goto out_resource; | |
| } | |
| return; | |
| out_resource: | |
| put_device(&pdev->dev); | |
| out_pdev: | |
| memregion_free(id); | |
| } | |
| static __init int hmem_register_one(struct resource *res, void *data) | |
| { | |
| /* | |
| * If the resource is not a top-level resource it was already | |
| * assigned to a device by the HMAT parsing. | |
| */ | |
| if (res->parent != &iomem_resource) { | |
| pr_info("HMEM: skip %pr, already claimed\n", res); | |
| return 0; | |
| } | |
| hmem_register_device(phys_to_target_node(res->start), res); | |
| return 0; | |
| } | |
| static __init int hmem_init(void) | |
| { | |
| walk_iomem_res_desc(IORES_DESC_SOFT_RESERVED, | |
| IORESOURCE_MEM, 0, -1, NULL, hmem_register_one); | |
| return 0; | |
| } | |
| /* | |
| * As this is a fallback for address ranges unclaimed by the ACPI HMAT | |
| * parsing it must be at an initcall level greater than hmat_init(). | |
| */ | |
| late_initcall(hmem_init); |