1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * System control and Management Interface (SCMI) NXP CPU Protocol 4 * 5 * Copyright 2025 NXP 6 */ 7 8 #include <linux/bits.h> 9 #include <linux/io.h> 10 #include <linux/module.h> 11 #include <linux/of.h> 12 #include <linux/platform_device.h> 13 #include <linux/scmi_protocol.h> 14 #include <linux/scmi_imx_protocol.h> 15 16 #include "../../protocols.h" 17 #include "../../notify.h" 18 19 #define SCMI_PROTOCOL_SUPPORTED_VERSION 0x10000 20 21 enum scmi_imx_cpu_protocol_cmd { 22 SCMI_IMX_CPU_ATTRIBUTES = 0x3, 23 SCMI_IMX_CPU_START = 0x4, 24 SCMI_IMX_CPU_STOP = 0x5, 25 SCMI_IMX_CPU_RESET_VECTOR_SET = 0x6, 26 SCMI_IMX_CPU_INFO_GET = 0xC, 27 }; 28 29 struct scmi_imx_cpu_info { 30 u32 nr_cpu; 31 }; 32 33 #define SCMI_IMX_CPU_NR_CPU_MASK GENMASK(15, 0) 34 struct scmi_msg_imx_cpu_protocol_attributes { 35 __le32 attributes; 36 }; 37 38 struct scmi_msg_imx_cpu_attributes_out { 39 __le32 attributes; 40 #define CPU_MAX_NAME 16 41 u8 name[CPU_MAX_NAME]; 42 }; 43 44 struct scmi_imx_cpu_reset_vector_set_in { 45 __le32 cpuid; 46 #define CPU_VEC_FLAGS_RESUME BIT(31) 47 #define CPU_VEC_FLAGS_START BIT(30) 48 #define CPU_VEC_FLAGS_BOOT BIT(29) 49 __le32 flags; 50 __le32 resetvectorlow; 51 __le32 resetvectorhigh; 52 }; 53 54 struct scmi_imx_cpu_info_get_out { 55 #define CPU_RUN_MODE_START 0 56 #define CPU_RUN_MODE_HOLD 1 57 #define CPU_RUN_MODE_STOP 2 58 #define CPU_RUN_MODE_SLEEP 3 59 __le32 runmode; 60 __le32 sleepmode; 61 __le32 resetvectorlow; 62 __le32 resetvectorhigh; 63 }; 64 65 static int scmi_imx_cpu_validate_cpuid(const struct scmi_protocol_handle *ph, 66 u32 cpuid) 67 { 68 struct scmi_imx_cpu_info *info = ph->get_priv(ph); 69 70 if (cpuid >= info->nr_cpu) 71 return -EINVAL; 72 73 return 0; 74 } 75 76 static int scmi_imx_cpu_start(const struct scmi_protocol_handle *ph, 77 u32 cpuid, bool start) 78 { 79 struct scmi_xfer *t; 80 u8 msg_id; 81 int ret; 82 83 ret = scmi_imx_cpu_validate_cpuid(ph, cpuid); 84 if (ret) 85 return ret; 86 87 if (start) 88 msg_id = SCMI_IMX_CPU_START; 89 else 90 msg_id = SCMI_IMX_CPU_STOP; 91 92 ret = ph->xops->xfer_get_init(ph, msg_id, sizeof(u32), 0, &t); 93 if (ret) 94 return ret; 95 96 put_unaligned_le32(cpuid, t->tx.buf); 97 ret = ph->xops->do_xfer(ph, t); 98 99 ph->xops->xfer_put(ph, t); 100 101 return ret; 102 } 103 104 static int scmi_imx_cpu_reset_vector_set(const struct scmi_protocol_handle *ph, 105 u32 cpuid, u64 vector, bool start, 106 bool boot, bool resume) 107 { 108 struct scmi_imx_cpu_reset_vector_set_in *in; 109 struct scmi_xfer *t; 110 int ret; 111 112 ret = scmi_imx_cpu_validate_cpuid(ph, cpuid); 113 if (ret) 114 return ret; 115 116 ret = ph->xops->xfer_get_init(ph, SCMI_IMX_CPU_RESET_VECTOR_SET, sizeof(*in), 117 0, &t); 118 if (ret) 119 return ret; 120 121 in = t->tx.buf; 122 in->cpuid = cpu_to_le32(cpuid); 123 in->flags = cpu_to_le32(0); 124 if (start) 125 in->flags |= le32_encode_bits(1, CPU_VEC_FLAGS_START); 126 if (boot) 127 in->flags |= le32_encode_bits(1, CPU_VEC_FLAGS_BOOT); 128 if (resume) 129 in->flags |= le32_encode_bits(1, CPU_VEC_FLAGS_RESUME); 130 in->resetvectorlow = cpu_to_le32(lower_32_bits(vector)); 131 in->resetvectorhigh = cpu_to_le32(upper_32_bits(vector)); 132 ret = ph->xops->do_xfer(ph, t); 133 134 ph->xops->xfer_put(ph, t); 135 136 return ret; 137 } 138 139 static int scmi_imx_cpu_started(const struct scmi_protocol_handle *ph, u32 cpuid, 140 bool *started) 141 { 142 struct scmi_imx_cpu_info_get_out *out; 143 struct scmi_xfer *t; 144 u32 mode; 145 int ret; 146 147 if (!started) 148 return -EINVAL; 149 150 *started = false; 151 ret = scmi_imx_cpu_validate_cpuid(ph, cpuid); 152 if (ret) 153 return ret; 154 155 ret = ph->xops->xfer_get_init(ph, SCMI_IMX_CPU_INFO_GET, sizeof(u32), 156 0, &t); 157 if (ret) 158 return ret; 159 160 put_unaligned_le32(cpuid, t->tx.buf); 161 ret = ph->xops->do_xfer(ph, t); 162 if (!ret) { 163 out = t->rx.buf; 164 mode = le32_to_cpu(out->runmode); 165 if (mode == CPU_RUN_MODE_START || mode == CPU_RUN_MODE_SLEEP) 166 *started = true; 167 } 168 169 ph->xops->xfer_put(ph, t); 170 171 return ret; 172 } 173 174 static const struct scmi_imx_cpu_proto_ops scmi_imx_cpu_proto_ops = { 175 .cpu_reset_vector_set = scmi_imx_cpu_reset_vector_set, 176 .cpu_start = scmi_imx_cpu_start, 177 .cpu_started = scmi_imx_cpu_started, 178 }; 179 180 static int scmi_imx_cpu_protocol_attributes_get(const struct scmi_protocol_handle *ph, 181 struct scmi_imx_cpu_info *info) 182 { 183 struct scmi_msg_imx_cpu_protocol_attributes *attr; 184 struct scmi_xfer *t; 185 int ret; 186 187 ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0, 188 sizeof(*attr), &t); 189 if (ret) 190 return ret; 191 192 attr = t->rx.buf; 193 194 ret = ph->xops->do_xfer(ph, t); 195 if (!ret) { 196 info->nr_cpu = le32_get_bits(attr->attributes, SCMI_IMX_CPU_NR_CPU_MASK); 197 dev_info(ph->dev, "i.MX SM CPU: %d cpus\n", 198 info->nr_cpu); 199 } 200 201 ph->xops->xfer_put(ph, t); 202 203 return ret; 204 } 205 206 static int scmi_imx_cpu_attributes_get(const struct scmi_protocol_handle *ph, 207 u32 cpuid) 208 { 209 struct scmi_msg_imx_cpu_attributes_out *out; 210 char name[SCMI_SHORT_NAME_MAX_SIZE] = {'\0'}; 211 struct scmi_xfer *t; 212 int ret; 213 214 ret = ph->xops->xfer_get_init(ph, SCMI_IMX_CPU_ATTRIBUTES, sizeof(u32), 0, &t); 215 if (ret) 216 return ret; 217 218 put_unaligned_le32(cpuid, t->tx.buf); 219 ret = ph->xops->do_xfer(ph, t); 220 if (!ret) { 221 out = t->rx.buf; 222 strscpy(name, out->name, SCMI_SHORT_NAME_MAX_SIZE); 223 dev_info(ph->dev, "i.MX CPU: name: %s\n", name); 224 } else { 225 dev_err(ph->dev, "i.MX cpu: Failed to get info of cpu(%u)\n", cpuid); 226 } 227 228 ph->xops->xfer_put(ph, t); 229 230 return ret; 231 } 232 233 static int scmi_imx_cpu_protocol_init(const struct scmi_protocol_handle *ph) 234 { 235 struct scmi_imx_cpu_info *info; 236 u32 version; 237 int ret, i; 238 239 ret = ph->xops->version_get(ph, &version); 240 if (ret) 241 return ret; 242 243 dev_info(ph->dev, "NXP SM CPU Protocol Version %d.%d\n", 244 PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); 245 246 info = devm_kzalloc(ph->dev, sizeof(*info), GFP_KERNEL); 247 if (!info) 248 return -ENOMEM; 249 250 ret = scmi_imx_cpu_protocol_attributes_get(ph, info); 251 if (ret) 252 return ret; 253 254 for (i = 0; i < info->nr_cpu; i++) { 255 ret = scmi_imx_cpu_attributes_get(ph, i); 256 if (ret) 257 return ret; 258 } 259 260 return ph->set_priv(ph, info, version); 261 } 262 263 static const struct scmi_protocol scmi_imx_cpu = { 264 .id = SCMI_PROTOCOL_IMX_CPU, 265 .owner = THIS_MODULE, 266 .instance_init = &scmi_imx_cpu_protocol_init, 267 .ops = &scmi_imx_cpu_proto_ops, 268 .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION, 269 .vendor_id = SCMI_IMX_VENDOR, 270 .sub_vendor_id = SCMI_IMX_SUBVENDOR, 271 }; 272 module_scmi_protocol(scmi_imx_cpu); 273 274 MODULE_ALIAS("scmi-protocol-" __stringify(SCMI_PROTOCOL_IMX_CPU) "-" SCMI_IMX_VENDOR); 275 MODULE_DESCRIPTION("i.MX SCMI CPU driver"); 276 MODULE_LICENSE("GPL"); 277