1 // SPDX-License-Identifier: GPL-2.0+ 2 3 #define pr_fmt(fmt) "of_pmem: " fmt 4 5 #include <linux/of.h> 6 #include <linux/libnvdimm.h> 7 #include <linux/module.h> 8 #include <linux/ioport.h> 9 #include <linux/platform_device.h> 10 #include <linux/slab.h> 11 12 struct of_pmem_private { 13 struct nvdimm_bus_descriptor bus_desc; 14 struct nvdimm_bus *bus; 15 }; 16 17 static int of_pmem_region_probe(struct platform_device *pdev) 18 { 19 struct of_pmem_private *priv; 20 struct device_node *np; 21 struct nvdimm_bus *bus; 22 bool is_volatile; 23 int i; 24 25 np = dev_of_node(&pdev->dev); 26 if (!np) 27 return -ENXIO; 28 29 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 30 if (!priv) 31 return -ENOMEM; 32 33 priv->bus_desc.provider_name = devm_kstrdup(&pdev->dev, pdev->name, 34 GFP_KERNEL); 35 if (!priv->bus_desc.provider_name) { 36 kfree(priv); 37 return -ENOMEM; 38 } 39 40 priv->bus_desc.module = THIS_MODULE; 41 priv->bus_desc.of_node = np; 42 43 priv->bus = bus = nvdimm_bus_register(&pdev->dev, &priv->bus_desc); 44 if (!bus) { 45 kfree(priv); 46 return -ENODEV; 47 } 48 platform_set_drvdata(pdev, priv); 49 50 is_volatile = of_property_read_bool(np, "volatile"); 51 dev_dbg(&pdev->dev, "Registering %s regions from %pOF\n", 52 is_volatile ? "volatile" : "non-volatile", np); 53 54 for (i = 0; i < pdev->num_resources; i++) { 55 struct nd_region_desc ndr_desc; 56 struct nd_region *region; 57 58 /* 59 * NB: libnvdimm copies the data from ndr_desc into it's own 60 * structures so passing a stack pointer is fine. 61 */ 62 memset(&ndr_desc, 0, sizeof(ndr_desc)); 63 ndr_desc.numa_node = dev_to_node(&pdev->dev); 64 ndr_desc.target_node = ndr_desc.numa_node; 65 ndr_desc.res = &pdev->resource[i]; 66 ndr_desc.of_node = np; 67 set_bit(ND_REGION_PAGEMAP, &ndr_desc.flags); 68 69 if (is_volatile) 70 region = nvdimm_volatile_region_create(bus, &ndr_desc); 71 else { 72 set_bit(ND_REGION_PERSIST_MEMCTRL, &ndr_desc.flags); 73 region = nvdimm_pmem_region_create(bus, &ndr_desc); 74 } 75 76 if (!region) 77 dev_warn(&pdev->dev, "Unable to register region %pR from %pOF\n", 78 ndr_desc.res, np); 79 else 80 dev_dbg(&pdev->dev, "Registered region %pR from %pOF\n", 81 ndr_desc.res, np); 82 } 83 84 return 0; 85 } 86 87 static void of_pmem_region_remove(struct platform_device *pdev) 88 { 89 struct of_pmem_private *priv = platform_get_drvdata(pdev); 90 91 nvdimm_bus_unregister(priv->bus); 92 kfree(priv); 93 } 94 95 static const struct of_device_id of_pmem_region_match[] = { 96 { .compatible = "pmem-region" }, 97 { .compatible = "pmem-region-v2" }, 98 { }, 99 }; 100 101 static struct platform_driver of_pmem_region_driver = { 102 .probe = of_pmem_region_probe, 103 .remove_new = of_pmem_region_remove, 104 .driver = { 105 .name = "of_pmem", 106 .of_match_table = of_pmem_region_match, 107 }, 108 }; 109 110 module_platform_driver(of_pmem_region_driver); 111 MODULE_DEVICE_TABLE(of, of_pmem_region_match); 112 MODULE_DESCRIPTION("NVDIMM Device Tree support"); 113 MODULE_LICENSE("GPL"); 114 MODULE_AUTHOR("IBM Corporation"); 115