xref: /linux/drivers/nvdimm/ramdax.c (revision 56a1a04dc9bf252641c622aad525894dadc61a07)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2025, Mike Rapoport, Microsoft
4  *
5  * Based on e820 pmem driver:
6  * Copyright (c) 2015, Christoph Hellwig.
7  * Copyright (c) 2015, Intel Corporation.
8  */
9 #include <linux/platform_device.h>
10 #include <linux/memory_hotplug.h>
11 #include <linux/libnvdimm.h>
12 #include <linux/module.h>
13 #include <linux/numa.h>
14 #include <linux/slab.h>
15 #include <linux/io.h>
16 #include <linux/of.h>
17 
18 #include <uapi/linux/ndctl.h>
19 
20 #define LABEL_AREA_SIZE	SZ_128K
21 
22 struct ramdax_dimm {
23 	struct nvdimm *nvdimm;
24 	void *label_area;
25 };
26 
ramdax_remove(struct platform_device * pdev)27 static void ramdax_remove(struct platform_device *pdev)
28 {
29 	struct nvdimm_bus *nvdimm_bus = platform_get_drvdata(pdev);
30 
31 	nvdimm_bus_unregister(nvdimm_bus);
32 }
33 
ramdax_register_region(struct resource * res,struct nvdimm * nvdimm,struct nvdimm_bus * nvdimm_bus)34 static int ramdax_register_region(struct resource *res,
35 		struct nvdimm *nvdimm,
36 		struct nvdimm_bus *nvdimm_bus)
37 {
38 	struct nd_mapping_desc mapping;
39 	struct nd_region_desc ndr_desc;
40 	struct nd_interleave_set *nd_set;
41 	int nid = phys_to_target_node(res->start);
42 
43 	nd_set = kzalloc(sizeof(*nd_set), GFP_KERNEL);
44 	if (!nd_set)
45 		return -ENOMEM;
46 
47 	nd_set->cookie1 = 0xcafebeefcafebeef;
48 	nd_set->cookie2 = nd_set->cookie1;
49 	nd_set->altcookie = nd_set->cookie1;
50 
51 	memset(&mapping, 0, sizeof(mapping));
52 	mapping.nvdimm = nvdimm;
53 	mapping.start = 0;
54 	mapping.size = resource_size(res) - LABEL_AREA_SIZE;
55 
56 	memset(&ndr_desc, 0, sizeof(ndr_desc));
57 	ndr_desc.res = res;
58 	ndr_desc.numa_node = numa_map_to_online_node(nid);
59 	ndr_desc.target_node = nid;
60 	ndr_desc.num_mappings = 1;
61 	ndr_desc.mapping = &mapping;
62 	ndr_desc.nd_set = nd_set;
63 
64 	if (!nvdimm_pmem_region_create(nvdimm_bus, &ndr_desc))
65 		goto err_free_nd_set;
66 
67 	return 0;
68 
69 err_free_nd_set:
70 	kfree(nd_set);
71 	return -ENXIO;
72 }
73 
ramdax_register_dimm(struct resource * res,void * data)74 static int ramdax_register_dimm(struct resource *res, void *data)
75 {
76 	resource_size_t start = res->start;
77 	resource_size_t size = resource_size(res);
78 	unsigned long flags = 0, cmd_mask = 0;
79 	struct nvdimm_bus *nvdimm_bus = data;
80 	struct ramdax_dimm *dimm;
81 	int err;
82 
83 	dimm = kzalloc(sizeof(*dimm), GFP_KERNEL);
84 	if (!dimm)
85 		return -ENOMEM;
86 
87 	dimm->label_area = memremap(start + size - LABEL_AREA_SIZE,
88 				    LABEL_AREA_SIZE, MEMREMAP_WB);
89 	if (!dimm->label_area) {
90 		err = -ENOMEM;
91 		goto err_free_dimm;
92 	}
93 
94 	set_bit(NDD_LABELING, &flags);
95 	set_bit(NDD_REGISTER_SYNC, &flags);
96 	set_bit(ND_CMD_GET_CONFIG_SIZE, &cmd_mask);
97 	set_bit(ND_CMD_GET_CONFIG_DATA, &cmd_mask);
98 	set_bit(ND_CMD_SET_CONFIG_DATA, &cmd_mask);
99 	dimm->nvdimm = nvdimm_create(nvdimm_bus, dimm,
100 				     /* dimm_attribute_groups */ NULL,
101 				     flags, cmd_mask, 0, NULL);
102 	if (!dimm->nvdimm) {
103 		err = -ENOMEM;
104 		goto err_unmap_label;
105 	}
106 
107 	err = ramdax_register_region(res, dimm->nvdimm, nvdimm_bus);
108 	if (err)
109 		goto err_remove_nvdimm;
110 
111 	return 0;
112 
113 err_remove_nvdimm:
114 	nvdimm_delete(dimm->nvdimm);
115 err_unmap_label:
116 	memunmap(dimm->label_area);
117 err_free_dimm:
118 	kfree(dimm);
119 	return err;
120 }
121 
ramdax_get_config_size(struct nvdimm * nvdimm,int buf_len,struct nd_cmd_get_config_size * cmd)122 static int ramdax_get_config_size(struct nvdimm *nvdimm, int buf_len,
123 		struct nd_cmd_get_config_size *cmd)
124 {
125 	if (sizeof(*cmd) > buf_len)
126 		return -EINVAL;
127 
128 	*cmd = (struct nd_cmd_get_config_size){
129 		.status = 0,
130 		.config_size = LABEL_AREA_SIZE,
131 		.max_xfer = 8,
132 	};
133 
134 	return 0;
135 }
136 
ramdax_get_config_data(struct nvdimm * nvdimm,int buf_len,struct nd_cmd_get_config_data_hdr * cmd)137 static int ramdax_get_config_data(struct nvdimm *nvdimm, int buf_len,
138 		struct nd_cmd_get_config_data_hdr *cmd)
139 {
140 	struct ramdax_dimm *dimm = nvdimm_provider_data(nvdimm);
141 
142 	if (sizeof(*cmd) > buf_len)
143 		return -EINVAL;
144 	if (struct_size(cmd, out_buf, cmd->in_length) > buf_len)
145 		return -EINVAL;
146 	if (size_add(cmd->in_offset, cmd->in_length) > LABEL_AREA_SIZE)
147 		return -EINVAL;
148 
149 	memcpy(cmd->out_buf, dimm->label_area + cmd->in_offset, cmd->in_length);
150 
151 	return 0;
152 }
153 
ramdax_set_config_data(struct nvdimm * nvdimm,int buf_len,struct nd_cmd_set_config_hdr * cmd)154 static int ramdax_set_config_data(struct nvdimm *nvdimm, int buf_len,
155 		struct nd_cmd_set_config_hdr *cmd)
156 {
157 	struct ramdax_dimm *dimm = nvdimm_provider_data(nvdimm);
158 
159 	if (sizeof(*cmd) > buf_len)
160 		return -EINVAL;
161 	if (struct_size(cmd, in_buf, cmd->in_length) > buf_len)
162 		return -EINVAL;
163 	if (size_add(cmd->in_offset, cmd->in_length) > LABEL_AREA_SIZE)
164 		return -EINVAL;
165 
166 	memcpy(dimm->label_area + cmd->in_offset, cmd->in_buf, cmd->in_length);
167 
168 	return 0;
169 }
170 
ramdax_nvdimm_ctl(struct nvdimm * nvdimm,unsigned int cmd,void * buf,unsigned int buf_len)171 static int ramdax_nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd,
172 		void *buf, unsigned int buf_len)
173 {
174 	unsigned long cmd_mask = nvdimm_cmd_mask(nvdimm);
175 
176 	if (!test_bit(cmd, &cmd_mask))
177 		return -ENOTTY;
178 
179 	switch (cmd) {
180 	case ND_CMD_GET_CONFIG_SIZE:
181 		return ramdax_get_config_size(nvdimm, buf_len, buf);
182 	case ND_CMD_GET_CONFIG_DATA:
183 		return ramdax_get_config_data(nvdimm, buf_len, buf);
184 	case ND_CMD_SET_CONFIG_DATA:
185 		return ramdax_set_config_data(nvdimm, buf_len, buf);
186 	default:
187 		return -ENOTTY;
188 	}
189 }
190 
ramdax_ctl(struct nvdimm_bus_descriptor * nd_desc,struct nvdimm * nvdimm,unsigned int cmd,void * buf,unsigned int buf_len,int * cmd_rc)191 static int ramdax_ctl(struct nvdimm_bus_descriptor *nd_desc,
192 		struct nvdimm *nvdimm, unsigned int cmd, void *buf,
193 		unsigned int buf_len, int *cmd_rc)
194 {
195 	/*
196 	 * No firmware response to translate, let the transport error
197 	 * code take precedence.
198 	 */
199 	*cmd_rc = 0;
200 
201 	if (!nvdimm)
202 		return -ENOTTY;
203 	return ramdax_nvdimm_ctl(nvdimm, cmd, buf, buf_len);
204 }
205 
206 #ifdef CONFIG_OF
207 static const struct of_device_id ramdax_of_matches[] = {
208 	{ .compatible = "pmem-region", },
209 	{ },
210 };
211 #endif
212 
ramdax_probe_of(struct platform_device * pdev,struct nvdimm_bus * bus,struct device_node * np)213 static int ramdax_probe_of(struct platform_device *pdev,
214 		struct nvdimm_bus *bus, struct device_node *np)
215 {
216 	int err;
217 
218 	if (!of_match_node(ramdax_of_matches, np))
219 		return -ENODEV;
220 
221 	for (int i = 0; i < pdev->num_resources; i++) {
222 		err = ramdax_register_dimm(&pdev->resource[i], bus);
223 		if (err)
224 			goto err_unregister;
225 	}
226 
227 	return 0;
228 
229 err_unregister:
230 	/*
231 	 * FIXME: should we unregister the dimms that were registered
232 	 * successfully
233 	 */
234 	return err;
235 }
236 
ramdax_probe(struct platform_device * pdev)237 static int ramdax_probe(struct platform_device *pdev)
238 {
239 	static struct nvdimm_bus_descriptor nd_desc;
240 	struct device *dev = &pdev->dev;
241 	struct nvdimm_bus *nvdimm_bus;
242 	struct device_node *np;
243 	int rc = -ENXIO;
244 
245 	nd_desc.provider_name = "ramdax";
246 	nd_desc.module = THIS_MODULE;
247 	nd_desc.ndctl = ramdax_ctl;
248 	nvdimm_bus = nvdimm_bus_register(dev, &nd_desc);
249 	if (!nvdimm_bus)
250 		goto err;
251 
252 	np = dev_of_node(&pdev->dev);
253 	if (np)
254 		rc = ramdax_probe_of(pdev, nvdimm_bus, np);
255 	else
256 		rc = walk_iomem_res_desc(IORES_DESC_PERSISTENT_MEMORY_LEGACY,
257 					 IORESOURCE_MEM, 0, -1, nvdimm_bus,
258 					 ramdax_register_dimm);
259 	if (rc)
260 		goto err;
261 
262 	platform_set_drvdata(pdev, nvdimm_bus);
263 
264 	return 0;
265 err:
266 	nvdimm_bus_unregister(nvdimm_bus);
267 	return rc;
268 }
269 
270 static struct platform_driver ramdax_driver = {
271 	.probe = ramdax_probe,
272 	.remove = ramdax_remove,
273 	.driver = {
274 		.name = "ramdax",
275 	},
276 };
277 
278 module_platform_driver(ramdax_driver);
279 
280 MODULE_DESCRIPTION("NVDIMM support for e820 type-12 memory and OF pmem-region");
281 MODULE_LICENSE("GPL");
282 MODULE_AUTHOR("Microsoft Corporation");
283