Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 // Copyright(c) 2021 Intel Corporation. All rights reserved.
0003 
0004 #include <linux/platform_device.h>
0005 #include <linux/mod_devicetable.h>
0006 #include <linux/module.h>
0007 #include <linux/delay.h>
0008 #include <linux/sizes.h>
0009 #include <linux/bits.h>
0010 #include <cxlmem.h>
0011 
0012 #define LSA_SIZE SZ_128K
0013 #define DEV_SIZE SZ_2G
0014 #define EFFECT(x) (1U << x)
0015 
0016 static struct cxl_cel_entry mock_cel[] = {
0017     {
0018         .opcode = cpu_to_le16(CXL_MBOX_OP_GET_SUPPORTED_LOGS),
0019         .effect = cpu_to_le16(0),
0020     },
0021     {
0022         .opcode = cpu_to_le16(CXL_MBOX_OP_IDENTIFY),
0023         .effect = cpu_to_le16(0),
0024     },
0025     {
0026         .opcode = cpu_to_le16(CXL_MBOX_OP_GET_LSA),
0027         .effect = cpu_to_le16(0),
0028     },
0029     {
0030         .opcode = cpu_to_le16(CXL_MBOX_OP_GET_PARTITION_INFO),
0031         .effect = cpu_to_le16(0),
0032     },
0033     {
0034         .opcode = cpu_to_le16(CXL_MBOX_OP_SET_LSA),
0035         .effect = cpu_to_le16(EFFECT(1) | EFFECT(2)),
0036     },
0037     {
0038         .opcode = cpu_to_le16(CXL_MBOX_OP_GET_HEALTH_INFO),
0039         .effect = cpu_to_le16(0),
0040     },
0041 };
0042 
0043 /* See CXL 2.0 Table 181 Get Health Info Output Payload */
0044 struct cxl_mbox_health_info {
0045     u8 health_status;
0046     u8 media_status;
0047     u8 ext_status;
0048     u8 life_used;
0049     __le16 temperature;
0050     __le32 dirty_shutdowns;
0051     __le32 volatile_errors;
0052     __le32 pmem_errors;
0053 } __packed;
0054 
0055 static struct {
0056     struct cxl_mbox_get_supported_logs gsl;
0057     struct cxl_gsl_entry entry;
0058 } mock_gsl_payload = {
0059     .gsl = {
0060         .entries = cpu_to_le16(1),
0061     },
0062     .entry = {
0063         .uuid = DEFINE_CXL_CEL_UUID,
0064         .size = cpu_to_le32(sizeof(mock_cel)),
0065     },
0066 };
0067 
0068 static int mock_gsl(struct cxl_mbox_cmd *cmd)
0069 {
0070     if (cmd->size_out < sizeof(mock_gsl_payload))
0071         return -EINVAL;
0072 
0073     memcpy(cmd->payload_out, &mock_gsl_payload, sizeof(mock_gsl_payload));
0074     cmd->size_out = sizeof(mock_gsl_payload);
0075 
0076     return 0;
0077 }
0078 
0079 static int mock_get_log(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
0080 {
0081     struct cxl_mbox_get_log *gl = cmd->payload_in;
0082     u32 offset = le32_to_cpu(gl->offset);
0083     u32 length = le32_to_cpu(gl->length);
0084     uuid_t uuid = DEFINE_CXL_CEL_UUID;
0085     void *data = &mock_cel;
0086 
0087     if (cmd->size_in < sizeof(*gl))
0088         return -EINVAL;
0089     if (length > cxlds->payload_size)
0090         return -EINVAL;
0091     if (offset + length > sizeof(mock_cel))
0092         return -EINVAL;
0093     if (!uuid_equal(&gl->uuid, &uuid))
0094         return -EINVAL;
0095     if (length > cmd->size_out)
0096         return -EINVAL;
0097 
0098     memcpy(cmd->payload_out, data + offset, length);
0099 
0100     return 0;
0101 }
0102 
0103 static int mock_id(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
0104 {
0105     struct cxl_mbox_identify id = {
0106         .fw_revision = { "mock fw v1 " },
0107         .lsa_size = cpu_to_le32(LSA_SIZE),
0108         .partition_align =
0109             cpu_to_le64(SZ_256M / CXL_CAPACITY_MULTIPLIER),
0110         .total_capacity =
0111             cpu_to_le64(DEV_SIZE / CXL_CAPACITY_MULTIPLIER),
0112     };
0113 
0114     if (cmd->size_out < sizeof(id))
0115         return -EINVAL;
0116 
0117     memcpy(cmd->payload_out, &id, sizeof(id));
0118 
0119     return 0;
0120 }
0121 
0122 static int mock_partition_info(struct cxl_dev_state *cxlds,
0123                    struct cxl_mbox_cmd *cmd)
0124 {
0125     struct cxl_mbox_get_partition_info pi = {
0126         .active_volatile_cap =
0127             cpu_to_le64(DEV_SIZE / 2 / CXL_CAPACITY_MULTIPLIER),
0128         .active_persistent_cap =
0129             cpu_to_le64(DEV_SIZE / 2 / CXL_CAPACITY_MULTIPLIER),
0130     };
0131 
0132     if (cmd->size_out < sizeof(pi))
0133         return -EINVAL;
0134 
0135     memcpy(cmd->payload_out, &pi, sizeof(pi));
0136 
0137     return 0;
0138 }
0139 
0140 static int mock_get_lsa(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
0141 {
0142     struct cxl_mbox_get_lsa *get_lsa = cmd->payload_in;
0143     void *lsa = dev_get_drvdata(cxlds->dev);
0144     u32 offset, length;
0145 
0146     if (sizeof(*get_lsa) > cmd->size_in)
0147         return -EINVAL;
0148     offset = le32_to_cpu(get_lsa->offset);
0149     length = le32_to_cpu(get_lsa->length);
0150     if (offset + length > LSA_SIZE)
0151         return -EINVAL;
0152     if (length > cmd->size_out)
0153         return -EINVAL;
0154 
0155     memcpy(cmd->payload_out, lsa + offset, length);
0156     return 0;
0157 }
0158 
0159 static int mock_set_lsa(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
0160 {
0161     struct cxl_mbox_set_lsa *set_lsa = cmd->payload_in;
0162     void *lsa = dev_get_drvdata(cxlds->dev);
0163     u32 offset, length;
0164 
0165     if (sizeof(*set_lsa) > cmd->size_in)
0166         return -EINVAL;
0167     offset = le32_to_cpu(set_lsa->offset);
0168     length = cmd->size_in - sizeof(*set_lsa);
0169     if (offset + length > LSA_SIZE)
0170         return -EINVAL;
0171 
0172     memcpy(lsa + offset, &set_lsa->data[0], length);
0173     return 0;
0174 }
0175 
0176 static int mock_health_info(struct cxl_dev_state *cxlds,
0177                 struct cxl_mbox_cmd *cmd)
0178 {
0179     struct cxl_mbox_health_info health_info = {
0180         /* set flags for maint needed, perf degraded, hw replacement */
0181         .health_status = 0x7,
0182         /* set media status to "All Data Lost" */
0183         .media_status = 0x3,
0184         /*
0185          * set ext_status flags for:
0186          *  ext_life_used: normal,
0187          *  ext_temperature: critical,
0188          *  ext_corrected_volatile: warning,
0189          *  ext_corrected_persistent: normal,
0190          */
0191         .ext_status = 0x18,
0192         .life_used = 15,
0193         .temperature = cpu_to_le16(25),
0194         .dirty_shutdowns = cpu_to_le32(10),
0195         .volatile_errors = cpu_to_le32(20),
0196         .pmem_errors = cpu_to_le32(30),
0197     };
0198 
0199     if (cmd->size_out < sizeof(health_info))
0200         return -EINVAL;
0201 
0202     memcpy(cmd->payload_out, &health_info, sizeof(health_info));
0203     return 0;
0204 }
0205 
0206 static int cxl_mock_mbox_send(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
0207 {
0208     struct device *dev = cxlds->dev;
0209     int rc = -EIO;
0210 
0211     switch (cmd->opcode) {
0212     case CXL_MBOX_OP_GET_SUPPORTED_LOGS:
0213         rc = mock_gsl(cmd);
0214         break;
0215     case CXL_MBOX_OP_GET_LOG:
0216         rc = mock_get_log(cxlds, cmd);
0217         break;
0218     case CXL_MBOX_OP_IDENTIFY:
0219         rc = mock_id(cxlds, cmd);
0220         break;
0221     case CXL_MBOX_OP_GET_LSA:
0222         rc = mock_get_lsa(cxlds, cmd);
0223         break;
0224     case CXL_MBOX_OP_GET_PARTITION_INFO:
0225         rc = mock_partition_info(cxlds, cmd);
0226         break;
0227     case CXL_MBOX_OP_SET_LSA:
0228         rc = mock_set_lsa(cxlds, cmd);
0229         break;
0230     case CXL_MBOX_OP_GET_HEALTH_INFO:
0231         rc = mock_health_info(cxlds, cmd);
0232         break;
0233     default:
0234         break;
0235     }
0236 
0237     dev_dbg(dev, "opcode: %#x sz_in: %zd sz_out: %zd rc: %d\n", cmd->opcode,
0238         cmd->size_in, cmd->size_out, rc);
0239 
0240     return rc;
0241 }
0242 
0243 static void label_area_release(void *lsa)
0244 {
0245     vfree(lsa);
0246 }
0247 
0248 static int cxl_mock_mem_probe(struct platform_device *pdev)
0249 {
0250     struct device *dev = &pdev->dev;
0251     struct cxl_memdev *cxlmd;
0252     struct cxl_dev_state *cxlds;
0253     void *lsa;
0254     int rc;
0255 
0256     lsa = vmalloc(LSA_SIZE);
0257     if (!lsa)
0258         return -ENOMEM;
0259     rc = devm_add_action_or_reset(dev, label_area_release, lsa);
0260     if (rc)
0261         return rc;
0262     dev_set_drvdata(dev, lsa);
0263 
0264     cxlds = cxl_dev_state_create(dev);
0265     if (IS_ERR(cxlds))
0266         return PTR_ERR(cxlds);
0267 
0268     cxlds->serial = pdev->id;
0269     cxlds->mbox_send = cxl_mock_mbox_send;
0270     cxlds->payload_size = SZ_4K;
0271 
0272     rc = cxl_enumerate_cmds(cxlds);
0273     if (rc)
0274         return rc;
0275 
0276     rc = cxl_dev_state_identify(cxlds);
0277     if (rc)
0278         return rc;
0279 
0280     rc = cxl_mem_create_range_info(cxlds);
0281     if (rc)
0282         return rc;
0283 
0284     cxlmd = devm_cxl_add_memdev(cxlds);
0285     if (IS_ERR(cxlmd))
0286         return PTR_ERR(cxlmd);
0287 
0288     if (resource_size(&cxlds->pmem_res) && IS_ENABLED(CONFIG_CXL_PMEM))
0289         rc = devm_cxl_add_nvdimm(dev, cxlmd);
0290 
0291     return 0;
0292 }
0293 
0294 static const struct platform_device_id cxl_mock_mem_ids[] = {
0295     { .name = "cxl_mem", },
0296     { },
0297 };
0298 MODULE_DEVICE_TABLE(platform, cxl_mock_mem_ids);
0299 
0300 static struct platform_driver cxl_mock_mem_driver = {
0301     .probe = cxl_mock_mem_probe,
0302     .id_table = cxl_mock_mem_ids,
0303     .driver = {
0304         .name = KBUILD_MODNAME,
0305     },
0306 };
0307 
0308 module_platform_driver(cxl_mock_mem_driver);
0309 MODULE_LICENSE("GPL v2");
0310 MODULE_IMPORT_NS(CXL);