1 // SPDX-License-Identifier: GPL-2.0-only
2 // Copyright(c) 2021 Intel Corporation. All rights reserved.
4 #include <linux/platform_device.h>
5 #include <linux/mod_devicetable.h>
6 #include <linux/module.h>
7 #include <linux/delay.h>
8 #include <linux/sizes.h>
9 #include <linux/bits.h>
12 #define LSA_SIZE SZ_128K
13 #define DEV_SIZE SZ_2G
14 #define EFFECT(x) (1U << x)
16 static struct cxl_cel_entry mock_cel[] = {
18 .opcode = cpu_to_le16(CXL_MBOX_OP_GET_SUPPORTED_LOGS),
19 .effect = cpu_to_le16(0),
22 .opcode = cpu_to_le16(CXL_MBOX_OP_IDENTIFY),
23 .effect = cpu_to_le16(0),
26 .opcode = cpu_to_le16(CXL_MBOX_OP_GET_LSA),
27 .effect = cpu_to_le16(0),
30 .opcode = cpu_to_le16(CXL_MBOX_OP_GET_PARTITION_INFO),
31 .effect = cpu_to_le16(0),
34 .opcode = cpu_to_le16(CXL_MBOX_OP_SET_LSA),
35 .effect = cpu_to_le16(EFFECT(1) | EFFECT(2)),
38 .opcode = cpu_to_le16(CXL_MBOX_OP_GET_HEALTH_INFO),
39 .effect = cpu_to_le16(0),
43 /* See CXL 2.0 Table 181 Get Health Info Output Payload */
44 struct cxl_mbox_health_info {
50 __le32 dirty_shutdowns;
51 __le32 volatile_errors;
56 struct cxl_mbox_get_supported_logs gsl;
57 struct cxl_gsl_entry entry;
58 } mock_gsl_payload = {
60 .entries = cpu_to_le16(1),
63 .uuid = DEFINE_CXL_CEL_UUID,
64 .size = cpu_to_le32(sizeof(mock_cel)),
68 struct cxl_mockmem_data {
73 static int mock_gsl(struct cxl_mbox_cmd *cmd)
75 if (cmd->size_out < sizeof(mock_gsl_payload))
78 memcpy(cmd->payload_out, &mock_gsl_payload, sizeof(mock_gsl_payload));
79 cmd->size_out = sizeof(mock_gsl_payload);
84 static int mock_get_log(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
86 struct cxl_mbox_get_log *gl = cmd->payload_in;
87 u32 offset = le32_to_cpu(gl->offset);
88 u32 length = le32_to_cpu(gl->length);
89 uuid_t uuid = DEFINE_CXL_CEL_UUID;
90 void *data = &mock_cel;
92 if (cmd->size_in < sizeof(*gl))
94 if (length > cxlds->payload_size)
96 if (offset + length > sizeof(mock_cel))
98 if (!uuid_equal(&gl->uuid, &uuid))
100 if (length > cmd->size_out)
103 memcpy(cmd->payload_out, data + offset, length);
108 static int mock_id(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
110 struct cxl_mbox_identify id = {
111 .fw_revision = { "mock fw v1 " },
112 .lsa_size = cpu_to_le32(LSA_SIZE),
114 cpu_to_le64(SZ_256M / CXL_CAPACITY_MULTIPLIER),
116 cpu_to_le64(DEV_SIZE / CXL_CAPACITY_MULTIPLIER),
119 if (cmd->size_out < sizeof(id))
122 memcpy(cmd->payload_out, &id, sizeof(id));
127 static int mock_partition_info(struct cxl_dev_state *cxlds,
128 struct cxl_mbox_cmd *cmd)
130 struct cxl_mbox_get_partition_info pi = {
131 .active_volatile_cap =
132 cpu_to_le64(DEV_SIZE / 2 / CXL_CAPACITY_MULTIPLIER),
133 .active_persistent_cap =
134 cpu_to_le64(DEV_SIZE / 2 / CXL_CAPACITY_MULTIPLIER),
137 if (cmd->size_out < sizeof(pi))
140 memcpy(cmd->payload_out, &pi, sizeof(pi));
145 static int mock_get_security_state(struct cxl_dev_state *cxlds,
146 struct cxl_mbox_cmd *cmd)
148 struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
153 if (cmd->size_out != sizeof(u32))
156 memcpy(cmd->payload_out, &mdata->security_state, sizeof(u32));
161 static int mock_get_lsa(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
163 struct cxl_mbox_get_lsa *get_lsa = cmd->payload_in;
164 struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
165 void *lsa = mdata->lsa;
168 if (sizeof(*get_lsa) > cmd->size_in)
170 offset = le32_to_cpu(get_lsa->offset);
171 length = le32_to_cpu(get_lsa->length);
172 if (offset + length > LSA_SIZE)
174 if (length > cmd->size_out)
177 memcpy(cmd->payload_out, lsa + offset, length);
181 static int mock_set_lsa(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
183 struct cxl_mbox_set_lsa *set_lsa = cmd->payload_in;
184 struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
185 void *lsa = mdata->lsa;
188 if (sizeof(*set_lsa) > cmd->size_in)
190 offset = le32_to_cpu(set_lsa->offset);
191 length = cmd->size_in - sizeof(*set_lsa);
192 if (offset + length > LSA_SIZE)
195 memcpy(lsa + offset, &set_lsa->data[0], length);
199 static int mock_health_info(struct cxl_dev_state *cxlds,
200 struct cxl_mbox_cmd *cmd)
202 struct cxl_mbox_health_info health_info = {
203 /* set flags for maint needed, perf degraded, hw replacement */
204 .health_status = 0x7,
205 /* set media status to "All Data Lost" */
208 * set ext_status flags for:
209 * ext_life_used: normal,
210 * ext_temperature: critical,
211 * ext_corrected_volatile: warning,
212 * ext_corrected_persistent: normal,
216 .temperature = cpu_to_le16(25),
217 .dirty_shutdowns = cpu_to_le32(10),
218 .volatile_errors = cpu_to_le32(20),
219 .pmem_errors = cpu_to_le32(30),
222 if (cmd->size_out < sizeof(health_info))
225 memcpy(cmd->payload_out, &health_info, sizeof(health_info));
229 static int cxl_mock_mbox_send(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
231 struct device *dev = cxlds->dev;
234 switch (cmd->opcode) {
235 case CXL_MBOX_OP_GET_SUPPORTED_LOGS:
238 case CXL_MBOX_OP_GET_LOG:
239 rc = mock_get_log(cxlds, cmd);
241 case CXL_MBOX_OP_IDENTIFY:
242 rc = mock_id(cxlds, cmd);
244 case CXL_MBOX_OP_GET_LSA:
245 rc = mock_get_lsa(cxlds, cmd);
247 case CXL_MBOX_OP_GET_PARTITION_INFO:
248 rc = mock_partition_info(cxlds, cmd);
250 case CXL_MBOX_OP_SET_LSA:
251 rc = mock_set_lsa(cxlds, cmd);
253 case CXL_MBOX_OP_GET_HEALTH_INFO:
254 rc = mock_health_info(cxlds, cmd);
256 case CXL_MBOX_OP_GET_SECURITY_STATE:
257 rc = mock_get_security_state(cxlds, cmd);
263 dev_dbg(dev, "opcode: %#x sz_in: %zd sz_out: %zd rc: %d\n", cmd->opcode,
264 cmd->size_in, cmd->size_out, rc);
269 static void label_area_release(void *lsa)
274 static int cxl_mock_mem_probe(struct platform_device *pdev)
276 struct device *dev = &pdev->dev;
277 struct cxl_memdev *cxlmd;
278 struct cxl_dev_state *cxlds;
279 struct cxl_mockmem_data *mdata;
282 mdata = devm_kzalloc(dev, sizeof(*mdata), GFP_KERNEL);
285 dev_set_drvdata(dev, mdata);
287 mdata->lsa = vmalloc(LSA_SIZE);
290 rc = devm_add_action_or_reset(dev, label_area_release, mdata->lsa);
294 cxlds = cxl_dev_state_create(dev);
296 return PTR_ERR(cxlds);
298 cxlds->serial = pdev->id;
299 cxlds->mbox_send = cxl_mock_mbox_send;
300 cxlds->payload_size = SZ_4K;
302 rc = cxl_enumerate_cmds(cxlds);
306 rc = cxl_dev_state_identify(cxlds);
310 rc = cxl_mem_create_range_info(cxlds);
314 cxlmd = devm_cxl_add_memdev(cxlds);
316 return PTR_ERR(cxlmd);
318 if (resource_size(&cxlds->pmem_res) && IS_ENABLED(CONFIG_CXL_PMEM))
319 rc = devm_cxl_add_nvdimm(dev, cxlmd);
324 static const struct platform_device_id cxl_mock_mem_ids[] = {
325 { .name = "cxl_mem", },
328 MODULE_DEVICE_TABLE(platform, cxl_mock_mem_ids);
330 static struct platform_driver cxl_mock_mem_driver = {
331 .probe = cxl_mock_mem_probe,
332 .id_table = cxl_mock_mem_ids,
334 .name = KBUILD_MODNAME,
338 module_platform_driver(cxl_mock_mem_driver);
339 MODULE_LICENSE("GPL v2");
340 MODULE_IMPORT_NS(CXL);