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 (!ph) 83 return -ENODEV; 84 85 ret = imx_misc_ctrl_ops->misc_syslog(ph, &size, syslog); 86 if (ret) 87 return ret; 88 89 seq_hex_dump(file, " ", DUMP_PREFIX_NONE, 16, sizeof(u32), syslog, size * 4, false); 90 seq_putc(file, '\n'); 91 92 return 0; 93 } 94 DEFINE_SHOW_ATTRIBUTE(syslog); 95 96 static void scmi_imx_misc_put(void *p) 97 { 98 debugfs_remove((struct dentry *)p); 99 } 100 101 static int scmi_imx_misc_get_reason(struct scmi_device *sdev) 102 { 103 struct scmi_imx_misc_reset_reason boot, shutdown; 104 const char **rst; 105 bool system = true; 106 int ret; 107 108 if (!of_machine_device_match(allowlist)) 109 return 0; 110 111 rst = (const char **)of_machine_get_match_data(allowlist); 112 113 ret = imx_misc_ctrl_ops->misc_reset_reason(ph, system, &boot, &shutdown, NULL); 114 if (!ret) { 115 if (boot.valid) 116 dev_info(&sdev->dev, "%s Boot reason: %s, origin: %d, errid: %d\n", 117 system ? "SYS" : "LM", rst[boot.reason], 118 boot.orig_valid ? boot.origin : -1, 119 boot.err_valid ? boot.errid : -1); 120 if (shutdown.valid) 121 dev_info(&sdev->dev, "%s shutdown reason: %s, origin: %d, errid: %d\n", 122 system ? "SYS" : "LM", rst[shutdown.reason], 123 shutdown.orig_valid ? shutdown.origin : -1, 124 shutdown.err_valid ? shutdown.errid : -1); 125 } else { 126 dev_err(&sdev->dev, "Failed to get system reset reason: %d\n", ret); 127 } 128 129 system = false; 130 ret = imx_misc_ctrl_ops->misc_reset_reason(ph, system, &boot, &shutdown, NULL); 131 if (!ret) { 132 if (boot.valid) 133 dev_info(&sdev->dev, "%s Boot reason: %s, origin: %d, errid: %d\n", 134 system ? "SYS" : "LM", rst[boot.reason], 135 boot.orig_valid ? boot.origin : -1, 136 boot.err_valid ? boot.errid : -1); 137 if (shutdown.valid) 138 dev_info(&sdev->dev, "%s shutdown reason: %s, origin: %d, errid: %d\n", 139 system ? "SYS" : "LM", rst[shutdown.reason], 140 shutdown.orig_valid ? shutdown.origin : -1, 141 shutdown.err_valid ? shutdown.errid : -1); 142 } else { 143 dev_err(&sdev->dev, "Failed to get lm reset reason: %d\n", ret); 144 } 145 146 return 0; 147 } 148 149 static int scmi_imx_misc_ctrl_probe(struct scmi_device *sdev) 150 { 151 const struct scmi_handle *handle = sdev->handle; 152 struct device_node *np = sdev->dev.of_node; 153 struct dentry *scmi_imx_dentry; 154 u32 src_id, flags; 155 int ret, i, num; 156 157 if (!handle) 158 return -ENODEV; 159 160 if (imx_misc_ctrl_ops) { 161 dev_err(&sdev->dev, "misc ctrl already initialized\n"); 162 return -EEXIST; 163 } 164 165 imx_misc_ctrl_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_IMX_MISC, &ph); 166 if (IS_ERR(imx_misc_ctrl_ops)) 167 return PTR_ERR(imx_misc_ctrl_ops); 168 169 num = of_property_count_u32_elems(np, "nxp,ctrl-ids"); 170 if (num % 2) { 171 dev_err(&sdev->dev, "Invalid wakeup-sources\n"); 172 return -EINVAL; 173 } 174 175 scmi_imx_misc_ctrl_nb.notifier_call = &scmi_imx_misc_ctrl_notifier; 176 for (i = 0; i < num; i += 2) { 177 ret = of_property_read_u32_index(np, "nxp,ctrl-ids", i, &src_id); 178 if (ret) { 179 dev_err(&sdev->dev, "Failed to read ctrl-id: %i\n", i); 180 continue; 181 } 182 183 ret = of_property_read_u32_index(np, "nxp,ctrl-ids", i + 1, &flags); 184 if (ret) { 185 dev_err(&sdev->dev, "Failed to read ctrl-id value: %d\n", i + 1); 186 continue; 187 } 188 189 ret = handle->notify_ops->devm_event_notifier_register(sdev, SCMI_PROTOCOL_IMX_MISC, 190 SCMI_EVENT_IMX_MISC_CONTROL, 191 &src_id, 192 &scmi_imx_misc_ctrl_nb); 193 if (ret) { 194 dev_err(&sdev->dev, "Failed to register scmi misc event: %d\n", src_id); 195 } else { 196 ret = imx_misc_ctrl_ops->misc_ctrl_req_notify(ph, src_id, 197 SCMI_EVENT_IMX_MISC_CONTROL, 198 flags); 199 if (ret) 200 dev_err(&sdev->dev, "Failed to req notify: %d\n", src_id); 201 } 202 } 203 204 scmi_imx_dentry = debugfs_create_dir("scmi_imx", NULL); 205 debugfs_create_file("syslog", 0444, scmi_imx_dentry, &sdev->dev, &syslog_fops); 206 207 scmi_imx_misc_get_reason(sdev); 208 209 return devm_add_action_or_reset(&sdev->dev, scmi_imx_misc_put, scmi_imx_dentry); 210 } 211 212 static const struct scmi_device_id scmi_id_table[] = { 213 { SCMI_PROTOCOL_IMX_MISC, "imx-misc-ctrl" }, 214 { }, 215 }; 216 MODULE_DEVICE_TABLE(scmi, scmi_id_table); 217 218 static struct scmi_driver scmi_imx_misc_ctrl_driver = { 219 .name = "scmi-imx-misc-ctrl", 220 .probe = scmi_imx_misc_ctrl_probe, 221 .id_table = scmi_id_table, 222 }; 223 module_scmi_driver(scmi_imx_misc_ctrl_driver); 224 225 MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>"); 226 MODULE_DESCRIPTION("IMX SM MISC driver"); 227 MODULE_LICENSE("GPL"); 228