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/pfn_t.h> 662306a36Sopenharmony_ci#include <linux/dax.h> 762306a36Sopenharmony_ci#include "../bus.h" 862306a36Sopenharmony_ci 962306a36Sopenharmony_cistatic bool region_idle; 1062306a36Sopenharmony_cimodule_param_named(region_idle, region_idle, bool, 0644); 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistatic int dax_hmem_probe(struct platform_device *pdev) 1362306a36Sopenharmony_ci{ 1462306a36Sopenharmony_ci unsigned long flags = IORESOURCE_DAX_KMEM; 1562306a36Sopenharmony_ci struct device *dev = &pdev->dev; 1662306a36Sopenharmony_ci struct dax_region *dax_region; 1762306a36Sopenharmony_ci struct memregion_info *mri; 1862306a36Sopenharmony_ci struct dev_dax_data data; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci /* 2162306a36Sopenharmony_ci * @region_idle == true indicates that an administrative agent 2262306a36Sopenharmony_ci * wants to manipulate the range partitioning before the devices 2362306a36Sopenharmony_ci * are created, so do not send them to the dax_kmem driver by 2462306a36Sopenharmony_ci * default. 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci if (region_idle) 2762306a36Sopenharmony_ci flags = 0; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci mri = dev->platform_data; 3062306a36Sopenharmony_ci dax_region = alloc_dax_region(dev, pdev->id, &mri->range, 3162306a36Sopenharmony_ci mri->target_node, PMD_SIZE, flags); 3262306a36Sopenharmony_ci if (!dax_region) 3362306a36Sopenharmony_ci return -ENOMEM; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci data = (struct dev_dax_data) { 3662306a36Sopenharmony_ci .dax_region = dax_region, 3762306a36Sopenharmony_ci .id = -1, 3862306a36Sopenharmony_ci .size = region_idle ? 0 : range_len(&mri->range), 3962306a36Sopenharmony_ci }; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(devm_create_dev_dax(&data)); 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic struct platform_driver dax_hmem_driver = { 4562306a36Sopenharmony_ci .probe = dax_hmem_probe, 4662306a36Sopenharmony_ci .driver = { 4762306a36Sopenharmony_ci .name = "hmem", 4862306a36Sopenharmony_ci }, 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic void release_memregion(void *data) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci memregion_free((long) data); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic void release_hmem(void *pdev) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci platform_device_unregister(pdev); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic int hmem_register_device(struct device *host, int target_nid, 6262306a36Sopenharmony_ci const struct resource *res) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct platform_device *pdev; 6562306a36Sopenharmony_ci struct memregion_info info; 6662306a36Sopenharmony_ci long id; 6762306a36Sopenharmony_ci int rc; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_CXL_REGION) && 7062306a36Sopenharmony_ci region_intersects(res->start, resource_size(res), IORESOURCE_MEM, 7162306a36Sopenharmony_ci IORES_DESC_CXL) != REGION_DISJOINT) { 7262306a36Sopenharmony_ci dev_dbg(host, "deferring range to CXL: %pr\n", res); 7362306a36Sopenharmony_ci return 0; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci rc = region_intersects(res->start, resource_size(res), IORESOURCE_MEM, 7762306a36Sopenharmony_ci IORES_DESC_SOFT_RESERVED); 7862306a36Sopenharmony_ci if (rc != REGION_INTERSECTS) 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci id = memregion_alloc(GFP_KERNEL); 8262306a36Sopenharmony_ci if (id < 0) { 8362306a36Sopenharmony_ci dev_err(host, "memregion allocation failure for %pr\n", res); 8462306a36Sopenharmony_ci return -ENOMEM; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci rc = devm_add_action_or_reset(host, release_memregion, (void *) id); 8762306a36Sopenharmony_ci if (rc) 8862306a36Sopenharmony_ci return rc; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci pdev = platform_device_alloc("hmem", id); 9162306a36Sopenharmony_ci if (!pdev) { 9262306a36Sopenharmony_ci dev_err(host, "device allocation failure for %pr\n", res); 9362306a36Sopenharmony_ci return -ENOMEM; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci pdev->dev.numa_node = numa_map_to_online_node(target_nid); 9762306a36Sopenharmony_ci info = (struct memregion_info) { 9862306a36Sopenharmony_ci .target_node = target_nid, 9962306a36Sopenharmony_ci .range = { 10062306a36Sopenharmony_ci .start = res->start, 10162306a36Sopenharmony_ci .end = res->end, 10262306a36Sopenharmony_ci }, 10362306a36Sopenharmony_ci }; 10462306a36Sopenharmony_ci rc = platform_device_add_data(pdev, &info, sizeof(info)); 10562306a36Sopenharmony_ci if (rc < 0) { 10662306a36Sopenharmony_ci dev_err(host, "memregion_info allocation failure for %pr\n", 10762306a36Sopenharmony_ci res); 10862306a36Sopenharmony_ci goto out_put; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci rc = platform_device_add(pdev); 11262306a36Sopenharmony_ci if (rc < 0) { 11362306a36Sopenharmony_ci dev_err(host, "%s add failed for %pr\n", dev_name(&pdev->dev), 11462306a36Sopenharmony_ci res); 11562306a36Sopenharmony_ci goto out_put; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return devm_add_action_or_reset(host, release_hmem, pdev); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ciout_put: 12162306a36Sopenharmony_ci platform_device_put(pdev); 12262306a36Sopenharmony_ci return rc; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic int dax_hmem_platform_probe(struct platform_device *pdev) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci return walk_hmem_resources(&pdev->dev, hmem_register_device); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic struct platform_driver dax_hmem_platform_driver = { 13162306a36Sopenharmony_ci .probe = dax_hmem_platform_probe, 13262306a36Sopenharmony_ci .driver = { 13362306a36Sopenharmony_ci .name = "hmem_platform", 13462306a36Sopenharmony_ci }, 13562306a36Sopenharmony_ci}; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic __init int dax_hmem_init(void) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci int rc; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci rc = platform_driver_register(&dax_hmem_platform_driver); 14262306a36Sopenharmony_ci if (rc) 14362306a36Sopenharmony_ci return rc; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci rc = platform_driver_register(&dax_hmem_driver); 14662306a36Sopenharmony_ci if (rc) 14762306a36Sopenharmony_ci platform_driver_unregister(&dax_hmem_platform_driver); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return rc; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic __exit void dax_hmem_exit(void) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci platform_driver_unregister(&dax_hmem_driver); 15562306a36Sopenharmony_ci platform_driver_unregister(&dax_hmem_platform_driver); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cimodule_init(dax_hmem_init); 15962306a36Sopenharmony_cimodule_exit(dax_hmem_exit); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/* Allow for CXL to define its own dax regions */ 16262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_CXL_REGION) 16362306a36Sopenharmony_ci#if IS_MODULE(CONFIG_CXL_ACPI) 16462306a36Sopenharmony_ciMODULE_SOFTDEP("pre: cxl_acpi"); 16562306a36Sopenharmony_ci#endif 16662306a36Sopenharmony_ci#endif 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ciMODULE_ALIAS("platform:hmem*"); 16962306a36Sopenharmony_ciMODULE_ALIAS("platform:hmem_platform*"); 17062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 17162306a36Sopenharmony_ciMODULE_AUTHOR("Intel Corporation"); 172