1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * System control and Management Interface (SCMI) NXP MISC Protocol 4 * 5 * Copyright 2024 NXP 6 */ 7 8 #define pr_fmt(fmt) "SCMI Notifications MISC - " fmt 9 10 #include <linux/bits.h> 11 #include <linux/io.h> 12 #include <linux/module.h> 13 #include <linux/of.h> 14 #include <linux/platform_device.h> 15 #include <linux/scmi_protocol.h> 16 #include <linux/scmi_imx_protocol.h> 17 18 #include "../../protocols.h" 19 #include "../../notify.h" 20 21 #define SCMI_PROTOCOL_SUPPORTED_VERSION 0x10000 22 23 #define MAX_MISC_CTRL_SOURCES GENMASK(15, 0) 24 25 enum scmi_imx_misc_protocol_cmd { 26 SCMI_IMX_MISC_CTRL_SET = 0x3, 27 SCMI_IMX_MISC_CTRL_GET = 0x4, 28 SCMI_IMX_MISC_DISCOVER_BUILD_INFO = 0x6, 29 SCMI_IMX_MISC_CTRL_NOTIFY = 0x8, 30 SCMI_IMX_MISC_CFG_INFO_GET = 0xC, 31 SCMI_IMX_MISC_BOARD_INFO = 0xE, 32 }; 33 34 struct scmi_imx_misc_info { 35 u32 version; 36 u32 nr_dev_ctrl; 37 u32 nr_brd_ctrl; 38 u32 nr_reason; 39 }; 40 41 struct scmi_msg_imx_misc_protocol_attributes { 42 __le32 attributes; 43 }; 44 45 #define GET_BRD_CTRLS_NR(x) le32_get_bits((x), GENMASK(31, 24)) 46 #define GET_REASONS_NR(x) le32_get_bits((x), GENMASK(23, 16)) 47 #define GET_DEV_CTRLS_NR(x) le32_get_bits((x), GENMASK(15, 0)) 48 #define BRD_CTRL_START_ID BIT(15) 49 50 struct scmi_imx_misc_ctrl_set_in { 51 __le32 id; 52 __le32 num; 53 __le32 value[]; 54 }; 55 56 struct scmi_imx_misc_ctrl_notify_in { 57 __le32 ctrl_id; 58 __le32 flags; 59 }; 60 61 struct scmi_imx_misc_ctrl_notify_payld { 62 __le32 ctrl_id; 63 __le32 flags; 64 }; 65 66 struct scmi_imx_misc_ctrl_get_out { 67 __le32 num; 68 __le32 val[]; 69 }; 70 71 struct scmi_imx_misc_buildinfo_out { 72 __le32 buildnum; 73 __le32 buildcommit; 74 #define MISC_MAX_BUILDDATE 16 75 u8 builddate[MISC_MAX_BUILDDATE]; 76 #define MISC_MAX_BUILDTIME 16 77 u8 buildtime[MISC_MAX_BUILDTIME]; 78 }; 79 80 struct scmi_imx_misc_board_info_out { 81 __le32 attributes; 82 #define MISC_MAX_BRDNAME 16 83 u8 brdname[MISC_MAX_BRDNAME]; 84 }; 85 86 struct scmi_imx_misc_cfg_info_out { 87 __le32 msel; 88 #define MISC_MAX_CFGNAME 16 89 u8 cfgname[MISC_MAX_CFGNAME]; 90 }; 91 92 static int scmi_imx_misc_attributes_get(const struct scmi_protocol_handle *ph, 93 struct scmi_imx_misc_info *mi) 94 { 95 int ret; 96 struct scmi_xfer *t; 97 struct scmi_msg_imx_misc_protocol_attributes *attr; 98 99 ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0, 100 sizeof(*attr), &t); 101 if (ret) 102 return ret; 103 104 attr = t->rx.buf; 105 106 ret = ph->xops->do_xfer(ph, t); 107 if (!ret) { 108 mi->nr_dev_ctrl = GET_DEV_CTRLS_NR(attr->attributes); 109 mi->nr_brd_ctrl = GET_BRD_CTRLS_NR(attr->attributes); 110 mi->nr_reason = GET_REASONS_NR(attr->attributes); 111 dev_info(ph->dev, "i.MX MISC NUM DEV CTRL: %d, NUM BRD CTRL: %d,NUM Reason: %d\n", 112 mi->nr_dev_ctrl, mi->nr_brd_ctrl, mi->nr_reason); 113 } 114 115 ph->xops->xfer_put(ph, t); 116 117 return ret; 118 } 119 120 static int scmi_imx_misc_ctrl_validate_id(const struct scmi_protocol_handle *ph, 121 u32 ctrl_id) 122 { 123 struct scmi_imx_misc_info *mi = ph->get_priv(ph); 124 125 /* 126 * [0, BRD_CTRL_START_ID) is for Dev Ctrl which is SOC related 127 * [BRD_CTRL_START_ID, 0xffff) is for Board Ctrl which is board related 128 */ 129 if (ctrl_id < BRD_CTRL_START_ID && ctrl_id > mi->nr_dev_ctrl) 130 return -EINVAL; 131 if (ctrl_id >= BRD_CTRL_START_ID + mi->nr_brd_ctrl) 132 return -EINVAL; 133 134 return 0; 135 } 136 137 static int scmi_imx_misc_ctrl_notify(const struct scmi_protocol_handle *ph, 138 u32 ctrl_id, u32 evt_id, u32 flags) 139 { 140 struct scmi_imx_misc_ctrl_notify_in *in; 141 struct scmi_xfer *t; 142 int ret; 143 144 ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id); 145 if (ret) 146 return ret; 147 148 ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_NOTIFY, 149 sizeof(*in), 0, &t); 150 if (ret) 151 return ret; 152 153 in = t->tx.buf; 154 in->ctrl_id = cpu_to_le32(ctrl_id); 155 in->flags = cpu_to_le32(flags); 156 157 ret = ph->xops->do_xfer(ph, t); 158 159 ph->xops->xfer_put(ph, t); 160 161 return ret; 162 } 163 164 static int 165 scmi_imx_misc_ctrl_set_notify_enabled(const struct scmi_protocol_handle *ph, 166 u8 evt_id, u32 src_id, bool enable) 167 { 168 int ret; 169 170 /* misc_ctrl_req_notify is for enablement */ 171 if (enable) 172 return 0; 173 174 ret = scmi_imx_misc_ctrl_notify(ph, src_id, evt_id, 0); 175 if (ret) 176 dev_err(ph->dev, "FAIL_ENABLED - evt[%X] src[%d] - ret:%d\n", 177 evt_id, src_id, ret); 178 179 return ret; 180 } 181 182 static void * 183 scmi_imx_misc_ctrl_fill_custom_report(const struct scmi_protocol_handle *ph, 184 u8 evt_id, ktime_t timestamp, 185 const void *payld, size_t payld_sz, 186 void *report, u32 *src_id) 187 { 188 const struct scmi_imx_misc_ctrl_notify_payld *p = payld; 189 struct scmi_imx_misc_ctrl_notify_report *r = report; 190 191 if (sizeof(*p) != payld_sz) 192 return NULL; 193 194 r->timestamp = timestamp; 195 r->ctrl_id = le32_to_cpu(p->ctrl_id); 196 r->flags = le32_to_cpu(p->flags); 197 if (src_id) 198 *src_id = r->ctrl_id; 199 dev_dbg(ph->dev, "%s: ctrl_id: %d flags: %d\n", __func__, 200 r->ctrl_id, r->flags); 201 202 return r; 203 } 204 205 static const struct scmi_event_ops scmi_imx_misc_event_ops = { 206 .set_notify_enabled = scmi_imx_misc_ctrl_set_notify_enabled, 207 .fill_custom_report = scmi_imx_misc_ctrl_fill_custom_report, 208 }; 209 210 static const struct scmi_event scmi_imx_misc_events[] = { 211 { 212 .id = SCMI_EVENT_IMX_MISC_CONTROL, 213 .max_payld_sz = sizeof(struct scmi_imx_misc_ctrl_notify_payld), 214 .max_report_sz = sizeof(struct scmi_imx_misc_ctrl_notify_report), 215 }, 216 }; 217 218 static struct scmi_protocol_events scmi_imx_misc_protocol_events = { 219 .queue_sz = SCMI_PROTO_QUEUE_SZ, 220 .ops = &scmi_imx_misc_event_ops, 221 .evts = scmi_imx_misc_events, 222 .num_events = ARRAY_SIZE(scmi_imx_misc_events), 223 .num_sources = MAX_MISC_CTRL_SOURCES, 224 }; 225 226 static int scmi_imx_misc_ctrl_get(const struct scmi_protocol_handle *ph, 227 u32 ctrl_id, u32 *num, u32 *val) 228 { 229 struct scmi_imx_misc_ctrl_get_out *out; 230 struct scmi_xfer *t; 231 int ret, i; 232 int max_msg_size = ph->hops->get_max_msg_size(ph); 233 int max_num = (max_msg_size - sizeof(*out)) / sizeof(__le32); 234 235 ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id); 236 if (ret) 237 return ret; 238 239 ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_GET, sizeof(u32), 240 0, &t); 241 if (ret) 242 return ret; 243 244 put_unaligned_le32(ctrl_id, t->tx.buf); 245 ret = ph->xops->do_xfer(ph, t); 246 if (!ret) { 247 out = t->rx.buf; 248 *num = le32_to_cpu(out->num); 249 250 if (*num >= max_num || 251 *num * sizeof(__le32) > t->rx.len - sizeof(__le32)) { 252 ph->xops->xfer_put(ph, t); 253 return -EINVAL; 254 } 255 256 for (i = 0; i < *num; i++) 257 val[i] = le32_to_cpu(out->val[i]); 258 } 259 260 ph->xops->xfer_put(ph, t); 261 262 return ret; 263 } 264 265 static int scmi_imx_misc_ctrl_set(const struct scmi_protocol_handle *ph, 266 u32 ctrl_id, u32 num, u32 *val) 267 { 268 struct scmi_imx_misc_ctrl_set_in *in; 269 struct scmi_xfer *t; 270 int ret, i; 271 int max_msg_size = ph->hops->get_max_msg_size(ph); 272 int max_num = (max_msg_size - sizeof(*in)) / sizeof(__le32); 273 274 ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id); 275 if (ret) 276 return ret; 277 278 if (num > max_num) 279 return -EINVAL; 280 281 ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_SET, 282 sizeof(*in) + num * sizeof(__le32), 0, &t); 283 if (ret) 284 return ret; 285 286 in = t->tx.buf; 287 in->id = cpu_to_le32(ctrl_id); 288 in->num = cpu_to_le32(num); 289 for (i = 0; i < num; i++) 290 in->value[i] = cpu_to_le32(val[i]); 291 292 ret = ph->xops->do_xfer(ph, t); 293 294 ph->xops->xfer_put(ph, t); 295 296 return ret; 297 } 298 299 static int scmi_imx_misc_build_info_discover(const struct scmi_protocol_handle *ph) 300 { 301 char date[MISC_MAX_BUILDDATE], time[MISC_MAX_BUILDTIME]; 302 struct scmi_imx_misc_buildinfo_out *out; 303 struct scmi_xfer *t; 304 int ret; 305 306 ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_DISCOVER_BUILD_INFO, 0, 307 sizeof(*out), &t); 308 if (ret) 309 return ret; 310 311 ret = ph->xops->do_xfer(ph, t); 312 if (!ret) { 313 out = t->rx.buf; 314 strscpy(date, out->builddate, MISC_MAX_BUILDDATE); 315 strscpy(time, out->buildtime, MISC_MAX_BUILDTIME); 316 dev_info(ph->dev, "SM Version\t= Build %u, Commit %08x %s %s\n", 317 le32_to_cpu(out->buildnum), le32_to_cpu(out->buildcommit), 318 date, time); 319 } 320 321 ph->xops->xfer_put(ph, t); 322 323 return ret; 324 } 325 326 static int scmi_imx_misc_board_info(const struct scmi_protocol_handle *ph) 327 { 328 struct scmi_imx_misc_board_info_out *out; 329 char name[MISC_MAX_BRDNAME]; 330 struct scmi_xfer *t; 331 int ret; 332 333 ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_BOARD_INFO, 0, sizeof(*out), &t); 334 if (ret) 335 return ret; 336 337 ret = ph->xops->do_xfer(ph, t); 338 if (!ret) { 339 out = t->rx.buf; 340 strscpy(name, out->brdname, MISC_MAX_BRDNAME); 341 dev_info(ph->dev, "Board\t\t= %s, attr=0x%08x\n", 342 name, le32_to_cpu(out->attributes)); 343 } 344 345 ph->xops->xfer_put(ph, t); 346 347 return ret; 348 } 349 350 static int scmi_imx_misc_cfg_info_get(const struct scmi_protocol_handle *ph) 351 { 352 struct scmi_imx_misc_cfg_info_out *out; 353 char name[MISC_MAX_CFGNAME]; 354 struct scmi_xfer *t; 355 int ret; 356 357 ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CFG_INFO_GET, 0, sizeof(*out), &t); 358 if (ret) 359 return ret; 360 361 ret = ph->xops->do_xfer(ph, t); 362 if (!ret) { 363 out = t->rx.buf; 364 strscpy(name, out->cfgname, MISC_MAX_CFGNAME); 365 dev_info(ph->dev, "SM Config\t= %s, mSel = %u\n", 366 name, le32_to_cpu(out->msel)); 367 } 368 369 ph->xops->xfer_put(ph, t); 370 371 return ret; 372 } 373 374 static const struct scmi_imx_misc_proto_ops scmi_imx_misc_proto_ops = { 375 .misc_ctrl_set = scmi_imx_misc_ctrl_set, 376 .misc_ctrl_get = scmi_imx_misc_ctrl_get, 377 .misc_ctrl_req_notify = scmi_imx_misc_ctrl_notify, 378 }; 379 380 static int scmi_imx_misc_protocol_init(const struct scmi_protocol_handle *ph) 381 { 382 struct scmi_imx_misc_info *minfo; 383 u32 version; 384 int ret; 385 386 ret = ph->xops->version_get(ph, &version); 387 if (ret) 388 return ret; 389 390 dev_info(ph->dev, "NXP SM MISC Version %d.%d\n", 391 PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); 392 393 minfo = devm_kzalloc(ph->dev, sizeof(*minfo), GFP_KERNEL); 394 if (!minfo) 395 return -ENOMEM; 396 397 ret = scmi_imx_misc_attributes_get(ph, minfo); 398 if (ret) 399 return ret; 400 401 ret = scmi_imx_misc_build_info_discover(ph); 402 if (ret && ret != -EOPNOTSUPP) 403 return ret; 404 405 ret = scmi_imx_misc_board_info(ph); 406 if (ret && ret != -EOPNOTSUPP) 407 return ret; 408 409 ret = scmi_imx_misc_cfg_info_get(ph); 410 if (ret && ret != -EOPNOTSUPP) 411 return ret; 412 413 return ph->set_priv(ph, minfo, version); 414 } 415 416 static const struct scmi_protocol scmi_imx_misc = { 417 .id = SCMI_PROTOCOL_IMX_MISC, 418 .owner = THIS_MODULE, 419 .instance_init = &scmi_imx_misc_protocol_init, 420 .ops = &scmi_imx_misc_proto_ops, 421 .events = &scmi_imx_misc_protocol_events, 422 .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION, 423 .vendor_id = SCMI_IMX_VENDOR, 424 .sub_vendor_id = SCMI_IMX_SUBVENDOR, 425 }; 426 module_scmi_protocol(scmi_imx_misc); 427 428 MODULE_ALIAS("scmi-protocol-" __stringify(SCMI_PROTOCOL_IMX_MISC) "-" SCMI_IMX_VENDOR); 429 MODULE_DESCRIPTION("i.MX SCMI MISC driver"); 430 MODULE_LICENSE("GPL"); 431