xref: /linux/drivers/firmware/imx/sm-misc.c (revision f4cdf7ca9a1fdcca413157df19753f388a5a224e)
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