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/pfn_t.h> 6 #include <linux/dax.h> 7 #include "../bus.h" 8 9 static bool region_idle; 10 module_param_named(region_idle, region_idle, bool, 0644); 11 12 static int dax_hmem_probe(struct platform_device *pdev) 13 { 14 unsigned long flags = IORESOURCE_DAX_KMEM; 15 struct device *dev = &pdev->dev; 16 struct dax_region *dax_region; 17 struct memregion_info *mri; 18 struct dev_dax_data data; 19 struct dev_dax *dev_dax; 20 21 /* 22 * @region_idle == true indicates that an administrative agent 23 * wants to manipulate the range partitioning before the devices 24 * are created, so do not send them to the dax_kmem driver by 25 * default. 26 */ 27 if (region_idle) 28 flags = 0; 29 30 mri = dev->platform_data; 31 dax_region = alloc_dax_region(dev, pdev->id, &mri->range, 32 mri->target_node, PMD_SIZE, flags); 33 if (!dax_region) 34 return -ENOMEM; 35 36 data = (struct dev_dax_data) { 37 .dax_region = dax_region, 38 .id = -1, 39 .size = region_idle ? 0 : range_len(&mri->range), 40 }; 41 dev_dax = devm_create_dev_dax(&data); 42 if (IS_ERR(dev_dax)) 43 return PTR_ERR(dev_dax); 44 45 /* child dev_dax instances now own the lifetime of the dax_region */ 46 dax_region_put(dax_region); 47 return 0; 48 } 49 50 static struct platform_driver dax_hmem_driver = { 51 .probe = dax_hmem_probe, 52 .driver = { 53 .name = "hmem", 54 }, 55 }; 56 57 static void release_memregion(void *data) 58 { 59 memregion_free((long) data); 60 } 61 62 static void release_hmem(void *pdev) 63 { 64 platform_device_unregister(pdev); 65 } 66 67 static int hmem_register_device(struct device *host, int target_nid, 68 const struct resource *res) 69 { 70 struct platform_device *pdev; 71 struct memregion_info info; 72 long id; 73 int rc; 74 75 if (IS_ENABLED(CONFIG_CXL_REGION) && 76 region_intersects(res->start, resource_size(res), IORESOURCE_MEM, 77 IORES_DESC_CXL) != REGION_DISJOINT) { 78 dev_dbg(host, "deferring range to CXL: %pr\n", res); 79 return 0; 80 } 81 82 rc = region_intersects(res->start, resource_size(res), IORESOURCE_MEM, 83 IORES_DESC_SOFT_RESERVED); 84 if (rc != REGION_INTERSECTS) 85 return 0; 86 87 id = memregion_alloc(GFP_KERNEL); 88 if (id < 0) { 89 dev_err(host, "memregion allocation failure for %pr\n", res); 90 return -ENOMEM; 91 } 92 rc = devm_add_action_or_reset(host, release_memregion, (void *) id); 93 if (rc) 94 return rc; 95 96 pdev = platform_device_alloc("hmem", id); 97 if (!pdev) { 98 dev_err(host, "device allocation failure for %pr\n", res); 99 return -ENOMEM; 100 } 101 102 pdev->dev.numa_node = numa_map_to_online_node(target_nid); 103 info = (struct memregion_info) { 104 .target_node = target_nid, 105 .range = { 106 .start = res->start, 107 .end = res->end, 108 }, 109 }; 110 rc = platform_device_add_data(pdev, &info, sizeof(info)); 111 if (rc < 0) { 112 dev_err(host, "memregion_info allocation failure for %pr\n", 113 res); 114 goto out_put; 115 } 116 117 rc = platform_device_add(pdev); 118 if (rc < 0) { 119 dev_err(host, "%s add failed for %pr\n", dev_name(&pdev->dev), 120 res); 121 goto out_put; 122 } 123 124 return devm_add_action_or_reset(host, release_hmem, pdev); 125 126 out_put: 127 platform_device_put(pdev); 128 return rc; 129 } 130 131 static int dax_hmem_platform_probe(struct platform_device *pdev) 132 { 133 return walk_hmem_resources(&pdev->dev, hmem_register_device); 134 } 135 136 static struct platform_driver dax_hmem_platform_driver = { 137 .probe = dax_hmem_platform_probe, 138 .driver = { 139 .name = "hmem_platform", 140 }, 141 }; 142 143 static __init int dax_hmem_init(void) 144 { 145 int rc; 146 147 rc = platform_driver_register(&dax_hmem_platform_driver); 148 if (rc) 149 return rc; 150 151 rc = platform_driver_register(&dax_hmem_driver); 152 if (rc) 153 platform_driver_unregister(&dax_hmem_platform_driver); 154 155 return rc; 156 } 157 158 static __exit void dax_hmem_exit(void) 159 { 160 platform_driver_unregister(&dax_hmem_driver); 161 platform_driver_unregister(&dax_hmem_platform_driver); 162 } 163 164 module_init(dax_hmem_init); 165 module_exit(dax_hmem_exit); 166 167 /* Allow for CXL to define its own dax regions */ 168 #if IS_ENABLED(CONFIG_CXL_REGION) 169 #if IS_MODULE(CONFIG_CXL_ACPI) 170 MODULE_SOFTDEP("pre: cxl_acpi"); 171 #endif 172 #endif 173 174 MODULE_ALIAS("platform:hmem*"); 175 MODULE_ALIAS("platform:hmem_platform*"); 176 MODULE_LICENSE("GPL v2"); 177 MODULE_AUTHOR("Intel Corporation"); 178