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 struct device *dev = &pdev->dev; 15 struct dax_region *dax_region; 16 struct memregion_info *mri; 17 struct dev_dax_data data; 18 struct dev_dax *dev_dax; 19 20 mri = dev->platform_data; 21 dax_region = alloc_dax_region(dev, pdev->id, &mri->range, 22 mri->target_node, PMD_SIZE, 0); 23 if (!dax_region) 24 return -ENOMEM; 25 26 data = (struct dev_dax_data) { 27 .dax_region = dax_region, 28 .id = -1, 29 .size = region_idle ? 0 : range_len(&mri->range), 30 }; 31 dev_dax = devm_create_dev_dax(&data); 32 if (IS_ERR(dev_dax)) 33 return PTR_ERR(dev_dax); 34 35 /* child dev_dax instances now own the lifetime of the dax_region */ 36 dax_region_put(dax_region); 37 return 0; 38 } 39 40 static struct platform_driver dax_hmem_driver = { 41 .probe = dax_hmem_probe, 42 .driver = { 43 .name = "hmem", 44 }, 45 }; 46 47 static void release_memregion(void *data) 48 { 49 memregion_free((long) data); 50 } 51 52 static void release_hmem(void *pdev) 53 { 54 platform_device_unregister(pdev); 55 } 56 57 static int hmem_register_device(struct device *host, int target_nid, 58 const struct resource *res) 59 { 60 struct platform_device *pdev; 61 struct memregion_info info; 62 long id; 63 int rc; 64 65 rc = region_intersects(res->start, resource_size(res), IORESOURCE_MEM, 66 IORES_DESC_SOFT_RESERVED); 67 if (rc != REGION_INTERSECTS) 68 return 0; 69 70 id = memregion_alloc(GFP_KERNEL); 71 if (id < 0) { 72 dev_err(host, "memregion allocation failure for %pr\n", res); 73 return -ENOMEM; 74 } 75 rc = devm_add_action_or_reset(host, release_memregion, (void *) id); 76 if (rc) 77 return rc; 78 79 pdev = platform_device_alloc("hmem", id); 80 if (!pdev) { 81 dev_err(host, "device allocation failure for %pr\n", res); 82 return -ENOMEM; 83 } 84 85 pdev->dev.numa_node = numa_map_to_online_node(target_nid); 86 info = (struct memregion_info) { 87 .target_node = target_nid, 88 .range = { 89 .start = res->start, 90 .end = res->end, 91 }, 92 }; 93 rc = platform_device_add_data(pdev, &info, sizeof(info)); 94 if (rc < 0) { 95 dev_err(host, "memregion_info allocation failure for %pr\n", 96 res); 97 goto out_put; 98 } 99 100 rc = platform_device_add(pdev); 101 if (rc < 0) { 102 dev_err(host, "%s add failed for %pr\n", dev_name(&pdev->dev), 103 res); 104 goto out_put; 105 } 106 107 return devm_add_action_or_reset(host, release_hmem, pdev); 108 109 out_put: 110 platform_device_put(pdev); 111 return rc; 112 } 113 114 static int dax_hmem_platform_probe(struct platform_device *pdev) 115 { 116 return walk_hmem_resources(&pdev->dev, hmem_register_device); 117 } 118 119 static struct platform_driver dax_hmem_platform_driver = { 120 .probe = dax_hmem_platform_probe, 121 .driver = { 122 .name = "hmem_platform", 123 }, 124 }; 125 126 static __init int dax_hmem_init(void) 127 { 128 int rc; 129 130 rc = platform_driver_register(&dax_hmem_platform_driver); 131 if (rc) 132 return rc; 133 134 rc = platform_driver_register(&dax_hmem_driver); 135 if (rc) 136 platform_driver_unregister(&dax_hmem_platform_driver); 137 138 return rc; 139 } 140 141 static __exit void dax_hmem_exit(void) 142 { 143 platform_driver_unregister(&dax_hmem_driver); 144 platform_driver_unregister(&dax_hmem_platform_driver); 145 } 146 147 module_init(dax_hmem_init); 148 module_exit(dax_hmem_exit); 149 150 MODULE_ALIAS("platform:hmem*"); 151 MODULE_ALIAS("platform:hmem_platform*"); 152 MODULE_LICENSE("GPL v2"); 153 MODULE_AUTHOR("Intel Corporation"); 154