1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright 2024 NXP 4 */ 5 6 #include <linux/debugfs.h> 7 #include <linux/device/devres.h> 8 #include <linux/firmware/imx/sm.h> 9 #include <linux/module.h> 10 #include <linux/of.h> 11 #include <linux/platform_device.h> 12 #include <linux/scmi_protocol.h> 13 #include <linux/scmi_imx_protocol.h> 14 #include <linux/seq_file.h> 15 #include <linux/sizes.h> 16 17 static const struct scmi_imx_misc_proto_ops *imx_misc_ctrl_ops; 18 static struct scmi_protocol_handle *ph; 19 static struct notifier_block scmi_imx_misc_ctrl_nb; 20 21 static const char * const rst_imx95[] = { 22 "cm33_lockup", "cm33_swreq", "cm7_lockup", "cm7_swreq", "fccu", 23 "jtag_sw", "ele", "tempsense", "wdog1", "wdog2", "wdog3", "wdog4", 24 "wdog5", "jtag", "cm33_exc", "bbm", "sw", "sm_err", "fusa_sreco", 25 "pmic", "unused", "unused", "unused", "unused", "unused", "unused", 26 "unused", "unused", "unused", "unused", "unused", "por", 27 }; 28 29 static const char * const rst_imx94[] = { 30 "cm33_lockup", "cm33_swreq", "cm70_lockup", "cm70_swreq", "fccu", 31 "jtag_sw", "ele", "tempsense", "wdog1", "wdog2", "wdog3", "wdog4", 32 "wdog5", "jtag", "wdog6", "wdog7", "wdog8", "wo_netc", "cm33s_lockup", 33 "cm33s_swreq", "cm71_lockup", "cm71_swreq", "cm33_exc", "bbm", "sw", 34 "sm_err", "fusa_sreco", "pmic", "unused", "unused", "unused", "por", 35 }; 36 37 static const struct of_device_id allowlist[] = { 38 { .compatible = "fsl,imx952", .data = rst_imx95 }, 39 { .compatible = "fsl,imx95", .data = rst_imx95 }, 40 { .compatible = "fsl,imx94", .data = rst_imx94 }, 41 { /* Sentinel */ } 42 }; 43 44 int scmi_imx_misc_ctrl_set(u32 id, u32 val) 45 { 46 if (!ph) 47 return -EPROBE_DEFER; 48 49 return imx_misc_ctrl_ops->misc_ctrl_set(ph, id, 1, &val); 50 }; 51 EXPORT_SYMBOL(scmi_imx_misc_ctrl_set); 52 53 int scmi_imx_misc_ctrl_get(u32 id, u32 *num, u32 *val) 54 { 55 if (!ph) 56 return -EPROBE_DEFER; 57 58 return imx_misc_ctrl_ops->misc_ctrl_get(ph, id, num, val); 59 } 60 EXPORT_SYMBOL(scmi_imx_misc_ctrl_get); 61 62 static int scmi_imx_misc_ctrl_notifier(struct notifier_block *nb, 63 unsigned long event, void *data) 64 { 65 /* 66 * notifier_chain_register requires a valid notifier_block and 67 * valid notifier_call. SCMI_EVENT_IMX_MISC_CONTROL is needed 68 * to let SCMI firmware enable control events, but the hook here 69 * is just a dummy function to avoid kernel panic as of now. 70 */ 71 return 0; 72 } 73 74 static int syslog_show(struct seq_file *file, void *priv) 75 { 76 /* 4KB is large enough for syslog */ 77 void *syslog __free(kfree) = kmalloc(SZ_4K, GFP_KERNEL); 78 /* syslog API use num words, not num bytes */ 79 u16 size = SZ_4K / 4; 80 int ret; 81 82 if (!syslog) 83 return -ENOMEM; 84 85 if (!ph) 86 return -ENODEV; 87 88 ret = imx_misc_ctrl_ops->misc_syslog(ph, &size, syslog); 89 if (ret) 90 return ret; 91 92 seq_hex_dump(file, " ", DUMP_PREFIX_NONE, 16, sizeof(u32), syslog, size * 4, false); 93 seq_putc(file, '\n'); 94 95 return 0; 96 } 97 DEFINE_SHOW_ATTRIBUTE(syslog); 98 99 static void scmi_imx_misc_put(void *p) 100 { 101 debugfs_remove((struct dentry *)p); 102 } 103 104 static int scmi_imx_misc_get_reason(struct scmi_device *sdev) 105 { 106 struct scmi_imx_misc_reset_reason boot, shutdown; 107 const char **rst; 108 bool system = true; 109 int ret; 110 111 if (!of_machine_device_match(allowlist)) 112 return 0; 113 114 rst = (const char **)of_machine_get_match_data(allowlist); 115 116 ret = imx_misc_ctrl_ops->misc_reset_reason(ph, system, &boot, &shutdown, NULL); 117 if (!ret) { 118 if (boot.valid) 119 dev_info(&sdev->dev, "%s Boot reason: %s, origin: %d, errid: %d\n", 120 system ? "SYS" : "LM", rst[boot.reason], 121 boot.orig_valid ? boot.origin : -1, 122 boot.err_valid ? boot.errid : -1); 123 if (shutdown.valid) 124 dev_info(&sdev->dev, "%s shutdown reason: %s, origin: %d, errid: %d\n", 125 system ? "SYS" : "LM", rst[shutdown.reason], 126 shutdown.orig_valid ? shutdown.origin : -1, 127 shutdown.err_valid ? shutdown.errid : -1); 128 } else { 129 dev_err(&sdev->dev, "Failed to get system reset reason: %d\n", ret); 130 } 131 132 system = false; 133 ret = imx_misc_ctrl_ops->misc_reset_reason(ph, system, &boot, &shutdown, NULL); 134 if (!ret) { 135 if (boot.valid) 136 dev_info(&sdev->dev, "%s Boot reason: %s, origin: %d, errid: %d\n", 137 system ? "SYS" : "LM", rst[boot.reason], 138 boot.orig_valid ? boot.origin : -1, 139 boot.err_valid ? boot.errid : -1); 140 if (shutdown.valid) 141 dev_info(&sdev->dev, "%s shutdown reason: %s, origin: %d, errid: %d\n", 142 system ? "SYS" : "LM", rst[shutdown.reason], 143 shutdown.orig_valid ? shutdown.origin : -1, 144 shutdown.err_valid ? shutdown.errid : -1); 145 } else { 146 dev_err(&sdev->dev, "Failed to get lm reset reason: %d\n", ret); 147 } 148 149 return 0; 150 } 151 152 static int scmi_imx_misc_ctrl_probe(struct scmi_device *sdev) 153 { 154 const struct scmi_handle *handle = sdev->handle; 155 struct device_node *np = sdev->dev.of_node; 156 struct dentry *scmi_imx_dentry; 157 u32 src_id, flags; 158 int ret, i, num; 159 160 if (!handle) 161 return -ENODEV; 162 163 if (imx_misc_ctrl_ops) { 164 dev_err(&sdev->dev, "misc ctrl already initialized\n"); 165 return -EEXIST; 166 } 167 168 imx_misc_ctrl_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_IMX_MISC, &ph); 169 if (IS_ERR(imx_misc_ctrl_ops)) 170 return PTR_ERR(imx_misc_ctrl_ops); 171 172 num = of_property_count_u32_elems(np, "nxp,ctrl-ids"); 173 if (num % 2) { 174 dev_err(&sdev->dev, "Invalid wakeup-sources\n"); 175 return -EINVAL; 176 } 177 178 scmi_imx_misc_ctrl_nb.notifier_call = &scmi_imx_misc_ctrl_notifier; 179 for (i = 0; i < num; i += 2) { 180 ret = of_property_read_u32_index(np, "nxp,ctrl-ids", i, &src_id); 181 if (ret) { 182 dev_err(&sdev->dev, "Failed to read ctrl-id: %i\n", i); 183 continue; 184 } 185 186 ret = of_property_read_u32_index(np, "nxp,ctrl-ids", i + 1, &flags); 187 if (ret) { 188 dev_err(&sdev->dev, "Failed to read ctrl-id value: %d\n", i + 1); 189 continue; 190 } 191 192 ret = handle->notify_ops->devm_event_notifier_register(sdev, SCMI_PROTOCOL_IMX_MISC, 193 SCMI_EVENT_IMX_MISC_CONTROL, 194 &src_id, 195 &scmi_imx_misc_ctrl_nb); 196 if (ret) { 197 dev_err(&sdev->dev, "Failed to register scmi misc event: %d\n", src_id); 198 } else { 199 ret = imx_misc_ctrl_ops->misc_ctrl_req_notify(ph, src_id, 200 SCMI_EVENT_IMX_MISC_CONTROL, 201 flags); 202 if (ret) 203 dev_err(&sdev->dev, "Failed to req notify: %d\n", src_id); 204 } 205 } 206 207 scmi_imx_dentry = debugfs_create_dir("scmi_imx", NULL); 208 debugfs_create_file("syslog", 0444, scmi_imx_dentry, &sdev->dev, &syslog_fops); 209 210 scmi_imx_misc_get_reason(sdev); 211 212 return devm_add_action_or_reset(&sdev->dev, scmi_imx_misc_put, scmi_imx_dentry); 213 } 214 215 static const struct scmi_device_id scmi_id_table[] = { 216 { SCMI_PROTOCOL_IMX_MISC, "imx-misc-ctrl" }, 217 { }, 218 }; 219 MODULE_DEVICE_TABLE(scmi, scmi_id_table); 220 221 static struct scmi_driver scmi_imx_misc_ctrl_driver = { 222 .name = "scmi-imx-misc-ctrl", 223 .probe = scmi_imx_misc_ctrl_probe, 224 .id_table = scmi_id_table, 225 }; 226 module_scmi_driver(scmi_imx_misc_ctrl_driver); 227 228 MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>"); 229 MODULE_DESCRIPTION("IMX SM MISC driver"); 230 MODULE_LICENSE("GPL"); 231