162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/platform_device.h>
362306a36Sopenharmony_ci#include <linux/memregion.h>
462306a36Sopenharmony_ci#include <linux/module.h>
562306a36Sopenharmony_ci#include <linux/dax.h>
662306a36Sopenharmony_ci#include <linux/mm.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_cistatic bool nohmem;
962306a36Sopenharmony_cimodule_param_named(disable, nohmem, bool, 0444);
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistatic bool platform_initialized;
1262306a36Sopenharmony_cistatic DEFINE_MUTEX(hmem_resource_lock);
1362306a36Sopenharmony_cistatic struct resource hmem_active = {
1462306a36Sopenharmony_ci	.name = "HMEM devices",
1562306a36Sopenharmony_ci	.start = 0,
1662306a36Sopenharmony_ci	.end = -1,
1762306a36Sopenharmony_ci	.flags = IORESOURCE_MEM,
1862306a36Sopenharmony_ci};
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ciint walk_hmem_resources(struct device *host, walk_hmem_fn fn)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	struct resource *res;
2362306a36Sopenharmony_ci	int rc = 0;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	mutex_lock(&hmem_resource_lock);
2662306a36Sopenharmony_ci	for (res = hmem_active.child; res; res = res->sibling) {
2762306a36Sopenharmony_ci		rc = fn(host, (int) res->desc, res);
2862306a36Sopenharmony_ci		if (rc)
2962306a36Sopenharmony_ci			break;
3062306a36Sopenharmony_ci	}
3162306a36Sopenharmony_ci	mutex_unlock(&hmem_resource_lock);
3262306a36Sopenharmony_ci	return rc;
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(walk_hmem_resources);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic void __hmem_register_resource(int target_nid, struct resource *res)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	struct platform_device *pdev;
3962306a36Sopenharmony_ci	struct resource *new;
4062306a36Sopenharmony_ci	int rc;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	new = __request_region(&hmem_active, res->start, resource_size(res), "",
4362306a36Sopenharmony_ci			       0);
4462306a36Sopenharmony_ci	if (!new) {
4562306a36Sopenharmony_ci		pr_debug("hmem range %pr already active\n", res);
4662306a36Sopenharmony_ci		return;
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	new->desc = target_nid;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	if (platform_initialized)
5262306a36Sopenharmony_ci		return;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	pdev = platform_device_alloc("hmem_platform", 0);
5562306a36Sopenharmony_ci	if (!pdev) {
5662306a36Sopenharmony_ci		pr_err_once("failed to register device-dax hmem_platform device\n");
5762306a36Sopenharmony_ci		return;
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	rc = platform_device_add(pdev);
6162306a36Sopenharmony_ci	if (rc)
6262306a36Sopenharmony_ci		platform_device_put(pdev);
6362306a36Sopenharmony_ci	else
6462306a36Sopenharmony_ci		platform_initialized = true;
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_civoid hmem_register_resource(int target_nid, struct resource *res)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	if (nohmem)
7062306a36Sopenharmony_ci		return;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	mutex_lock(&hmem_resource_lock);
7362306a36Sopenharmony_ci	__hmem_register_resource(target_nid, res);
7462306a36Sopenharmony_ci	mutex_unlock(&hmem_resource_lock);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic __init int hmem_register_one(struct resource *res, void *data)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	hmem_register_resource(phys_to_target_node(res->start), res);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	return 0;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic __init int hmem_init(void)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	walk_iomem_res_desc(IORES_DESC_SOFT_RESERVED,
8762306a36Sopenharmony_ci			IORESOURCE_MEM, 0, -1, NULL, hmem_register_one);
8862306a36Sopenharmony_ci	return 0;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/*
9262306a36Sopenharmony_ci * As this is a fallback for address ranges unclaimed by the ACPI HMAT
9362306a36Sopenharmony_ci * parsing it must be at an initcall level greater than hmat_init().
9462306a36Sopenharmony_ci */
9562306a36Sopenharmony_cidevice_initcall(hmem_init);
96