| // 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); | |
| static bool platform_initialized; | |
| static DEFINE_MUTEX(hmem_resource_lock); | |
| static struct resource hmem_active = { | |
| .name = "HMEM devices", | |
| .start = 0, | |
| .end = -1, | |
| .flags = IORESOURCE_MEM, | |
| }; | |
| int walk_hmem_resources(struct device *host, walk_hmem_fn fn) | |
| { | |
| struct resource *res; | |
| int rc = 0; | |
| mutex_lock(&hmem_resource_lock); | |
| for (res = hmem_active.child; res; res = res->sibling) { | |
| rc = fn(host, (int) res->desc, res); | |
| if (rc) | |
| break; | |
| } | |
| mutex_unlock(&hmem_resource_lock); | |
| return rc; | |
| } | |
| EXPORT_SYMBOL_GPL(walk_hmem_resources); | |
| static void __hmem_register_resource(int target_nid, struct resource *res) | |
| { | |
| struct platform_device *pdev; | |
| struct resource *new; | |
| int rc; | |
| new = __request_region(&hmem_active, res->start, resource_size(res), "", | |
| 0); | |
| if (!new) { | |
| pr_debug("hmem range %pr already active\n", res); | |
| return; | |
| } | |
| new->desc = target_nid; | |
| if (platform_initialized) | |
| return; | |
| pdev = platform_device_alloc("hmem_platform", 0); | |
| if (!pdev) { | |
| pr_err_once("failed to register device-dax hmem_platform device\n"); | |
| return; | |
| } | |
| rc = platform_device_add(pdev); | |
| if (rc) | |
| platform_device_put(pdev); | |
| else | |
| platform_initialized = true; | |
| } | |
| void hmem_register_resource(int target_nid, struct resource *res) | |
| { | |
| if (nohmem) | |
| return; | |
| mutex_lock(&hmem_resource_lock); | |
| __hmem_register_resource(target_nid, res); | |
| mutex_unlock(&hmem_resource_lock); | |
| } | |
| static __init int hmem_register_one(struct resource *res, void *data) | |
| { | |
| hmem_register_resource(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(). | |
| */ | |
| device_initcall(hmem_init); |