xref: /linux/drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c (revision 566ab427f827b0256d3e8ce0235d088e6a9c28bd)
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_CTRL_NOTIFY = 0x8,
29 };
30 
31 struct scmi_imx_misc_info {
32 	u32 version;
33 	u32 nr_dev_ctrl;
34 	u32 nr_brd_ctrl;
35 	u32 nr_reason;
36 };
37 
38 struct scmi_msg_imx_misc_protocol_attributes {
39 	__le32 attributes;
40 };
41 
42 #define GET_BRD_CTRLS_NR(x)	le32_get_bits((x), GENMASK(31, 24))
43 #define GET_REASONS_NR(x)	le32_get_bits((x), GENMASK(23, 16))
44 #define GET_DEV_CTRLS_NR(x)	le32_get_bits((x), GENMASK(15, 0))
45 #define BRD_CTRL_START_ID	BIT(15)
46 
47 struct scmi_imx_misc_ctrl_set_in {
48 	__le32 id;
49 	__le32 num;
50 	__le32 value[];
51 };
52 
53 struct scmi_imx_misc_ctrl_notify_in {
54 	__le32 ctrl_id;
55 	__le32 flags;
56 };
57 
58 struct scmi_imx_misc_ctrl_notify_payld {
59 	__le32 ctrl_id;
60 	__le32 flags;
61 };
62 
63 struct scmi_imx_misc_ctrl_get_out {
64 	__le32 num;
65 	__le32 val[];
66 };
67 
68 static int scmi_imx_misc_attributes_get(const struct scmi_protocol_handle *ph,
69 					struct scmi_imx_misc_info *mi)
70 {
71 	int ret;
72 	struct scmi_xfer *t;
73 	struct scmi_msg_imx_misc_protocol_attributes *attr;
74 
75 	ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0,
76 				      sizeof(*attr), &t);
77 	if (ret)
78 		return ret;
79 
80 	attr = t->rx.buf;
81 
82 	ret = ph->xops->do_xfer(ph, t);
83 	if (!ret) {
84 		mi->nr_dev_ctrl = GET_DEV_CTRLS_NR(attr->attributes);
85 		mi->nr_brd_ctrl = GET_BRD_CTRLS_NR(attr->attributes);
86 		mi->nr_reason = GET_REASONS_NR(attr->attributes);
87 		dev_info(ph->dev, "i.MX MISC NUM DEV CTRL: %d, NUM BRD CTRL: %d,NUM Reason: %d\n",
88 			 mi->nr_dev_ctrl, mi->nr_brd_ctrl, mi->nr_reason);
89 	}
90 
91 	ph->xops->xfer_put(ph, t);
92 
93 	return ret;
94 }
95 
96 static int scmi_imx_misc_ctrl_validate_id(const struct scmi_protocol_handle *ph,
97 					  u32 ctrl_id)
98 {
99 	struct scmi_imx_misc_info *mi = ph->get_priv(ph);
100 
101 	/*
102 	 * [0,      BRD_CTRL_START_ID) is for Dev Ctrl which is SOC related
103 	 * [BRD_CTRL_START_ID, 0xffff) is for Board Ctrl which is board related
104 	 */
105 	if (ctrl_id < BRD_CTRL_START_ID && ctrl_id > mi->nr_dev_ctrl)
106 		return -EINVAL;
107 	if (ctrl_id >= BRD_CTRL_START_ID + mi->nr_brd_ctrl)
108 		return -EINVAL;
109 
110 	return 0;
111 }
112 
113 static int scmi_imx_misc_ctrl_notify(const struct scmi_protocol_handle *ph,
114 				     u32 ctrl_id, u32 evt_id, u32 flags)
115 {
116 	struct scmi_imx_misc_ctrl_notify_in *in;
117 	struct scmi_xfer *t;
118 	int ret;
119 
120 	ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
121 	if (ret)
122 		return ret;
123 
124 	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_NOTIFY,
125 				      sizeof(*in), 0, &t);
126 	if (ret)
127 		return ret;
128 
129 	in = t->tx.buf;
130 	in->ctrl_id = cpu_to_le32(ctrl_id);
131 	in->flags = cpu_to_le32(flags);
132 
133 	ret = ph->xops->do_xfer(ph, t);
134 
135 	ph->xops->xfer_put(ph, t);
136 
137 	return ret;
138 }
139 
140 static int
141 scmi_imx_misc_ctrl_set_notify_enabled(const struct scmi_protocol_handle *ph,
142 				      u8 evt_id, u32 src_id, bool enable)
143 {
144 	int ret;
145 
146 	/* misc_ctrl_req_notify is for enablement */
147 	if (enable)
148 		return 0;
149 
150 	ret = scmi_imx_misc_ctrl_notify(ph, src_id, evt_id, 0);
151 	if (ret)
152 		dev_err(ph->dev, "FAIL_ENABLED - evt[%X] src[%d] - ret:%d\n",
153 			evt_id, src_id, ret);
154 
155 	return ret;
156 }
157 
158 static void *
159 scmi_imx_misc_ctrl_fill_custom_report(const struct scmi_protocol_handle *ph,
160 				      u8 evt_id, ktime_t timestamp,
161 				      const void *payld, size_t payld_sz,
162 				      void *report, u32 *src_id)
163 {
164 	const struct scmi_imx_misc_ctrl_notify_payld *p = payld;
165 	struct scmi_imx_misc_ctrl_notify_report *r = report;
166 
167 	if (sizeof(*p) != payld_sz)
168 		return NULL;
169 
170 	r->timestamp = timestamp;
171 	r->ctrl_id = le32_to_cpu(p->ctrl_id);
172 	r->flags = le32_to_cpu(p->flags);
173 	if (src_id)
174 		*src_id = r->ctrl_id;
175 	dev_dbg(ph->dev, "%s: ctrl_id: %d flags: %d\n", __func__,
176 		r->ctrl_id, r->flags);
177 
178 	return r;
179 }
180 
181 static const struct scmi_event_ops scmi_imx_misc_event_ops = {
182 	.set_notify_enabled = scmi_imx_misc_ctrl_set_notify_enabled,
183 	.fill_custom_report = scmi_imx_misc_ctrl_fill_custom_report,
184 };
185 
186 static const struct scmi_event scmi_imx_misc_events[] = {
187 	{
188 		.id = SCMI_EVENT_IMX_MISC_CONTROL,
189 		.max_payld_sz = sizeof(struct scmi_imx_misc_ctrl_notify_payld),
190 		.max_report_sz = sizeof(struct scmi_imx_misc_ctrl_notify_report),
191 	},
192 };
193 
194 static struct scmi_protocol_events scmi_imx_misc_protocol_events = {
195 	.queue_sz = SCMI_PROTO_QUEUE_SZ,
196 	.ops = &scmi_imx_misc_event_ops,
197 	.evts = scmi_imx_misc_events,
198 	.num_events = ARRAY_SIZE(scmi_imx_misc_events),
199 	.num_sources = MAX_MISC_CTRL_SOURCES,
200 };
201 
202 static int scmi_imx_misc_ctrl_get(const struct scmi_protocol_handle *ph,
203 				  u32 ctrl_id, u32 *num, u32 *val)
204 {
205 	struct scmi_imx_misc_ctrl_get_out *out;
206 	struct scmi_xfer *t;
207 	int ret, i;
208 	int max_msg_size = ph->hops->get_max_msg_size(ph);
209 	int max_num = (max_msg_size - sizeof(*out)) / sizeof(__le32);
210 
211 	ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
212 	if (ret)
213 		return ret;
214 
215 	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_GET, sizeof(u32),
216 				      0, &t);
217 	if (ret)
218 		return ret;
219 
220 	put_unaligned_le32(ctrl_id, t->tx.buf);
221 	ret = ph->xops->do_xfer(ph, t);
222 	if (!ret) {
223 		out = t->rx.buf;
224 		*num = le32_to_cpu(out->num);
225 
226 		if (*num >= max_num ||
227 		    *num * sizeof(__le32) > t->rx.len - sizeof(__le32)) {
228 			ph->xops->xfer_put(ph, t);
229 			return -EINVAL;
230 		}
231 
232 		for (i = 0; i < *num; i++)
233 			val[i] = le32_to_cpu(out->val[i]);
234 	}
235 
236 	ph->xops->xfer_put(ph, t);
237 
238 	return ret;
239 }
240 
241 static int scmi_imx_misc_ctrl_set(const struct scmi_protocol_handle *ph,
242 				  u32 ctrl_id, u32 num, u32 *val)
243 {
244 	struct scmi_imx_misc_ctrl_set_in *in;
245 	struct scmi_xfer *t;
246 	int ret, i;
247 	int max_msg_size = ph->hops->get_max_msg_size(ph);
248 	int max_num = (max_msg_size - sizeof(*in)) / sizeof(__le32);
249 
250 	ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
251 	if (ret)
252 		return ret;
253 
254 	if (num > max_num)
255 		return -EINVAL;
256 
257 	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_SET, sizeof(*in),
258 				      0, &t);
259 	if (ret)
260 		return ret;
261 
262 	in = t->tx.buf;
263 	in->id = cpu_to_le32(ctrl_id);
264 	in->num = cpu_to_le32(num);
265 	for (i = 0; i < num; i++)
266 		in->value[i] = cpu_to_le32(val[i]);
267 
268 	ret = ph->xops->do_xfer(ph, t);
269 
270 	ph->xops->xfer_put(ph, t);
271 
272 	return ret;
273 }
274 
275 static const struct scmi_imx_misc_proto_ops scmi_imx_misc_proto_ops = {
276 	.misc_ctrl_set = scmi_imx_misc_ctrl_set,
277 	.misc_ctrl_get = scmi_imx_misc_ctrl_get,
278 	.misc_ctrl_req_notify = scmi_imx_misc_ctrl_notify,
279 };
280 
281 static int scmi_imx_misc_protocol_init(const struct scmi_protocol_handle *ph)
282 {
283 	struct scmi_imx_misc_info *minfo;
284 	u32 version;
285 	int ret;
286 
287 	ret = ph->xops->version_get(ph, &version);
288 	if (ret)
289 		return ret;
290 
291 	dev_info(ph->dev, "NXP SM MISC Version %d.%d\n",
292 		 PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
293 
294 	minfo = devm_kzalloc(ph->dev, sizeof(*minfo), GFP_KERNEL);
295 	if (!minfo)
296 		return -ENOMEM;
297 
298 	ret = scmi_imx_misc_attributes_get(ph, minfo);
299 	if (ret)
300 		return ret;
301 
302 	return ph->set_priv(ph, minfo, version);
303 }
304 
305 static const struct scmi_protocol scmi_imx_misc = {
306 	.id = SCMI_PROTOCOL_IMX_MISC,
307 	.owner = THIS_MODULE,
308 	.instance_init = &scmi_imx_misc_protocol_init,
309 	.ops = &scmi_imx_misc_proto_ops,
310 	.events = &scmi_imx_misc_protocol_events,
311 	.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
312 	.vendor_id = "NXP",
313 	.sub_vendor_id = "IMX",
314 };
315 module_scmi_protocol(scmi_imx_misc);
316 
317 MODULE_DESCRIPTION("i.MX SCMI MISC driver");
318 MODULE_LICENSE("GPL");
319