File Index
Files and directories
Sign in
FILE

imx-sm-misc.c

File preview with syntax highlighting.

linux-6.19.14/drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c C
  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
 25enum 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
 34struct scmi_imx_misc_info {
 35	u32 version;
 36	u32 nr_dev_ctrl;
 37	u32 nr_brd_ctrl;
 38	u32 nr_reason;
 39};
 40
 41struct 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
 50struct scmi_imx_misc_ctrl_set_in {
 51	__le32 id;
 52	__le32 num;
 53	__le32 value[];
 54};
 55
 56struct scmi_imx_misc_ctrl_notify_in {
 57	__le32 ctrl_id;
 58	__le32 flags;
 59};
 60
 61struct scmi_imx_misc_ctrl_notify_payld {
 62	__le32 ctrl_id;
 63	__le32 flags;
 64};
 65
 66struct scmi_imx_misc_ctrl_get_out {
 67	__le32 num;
 68	__le32 val[];
 69};
 70
 71struct 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
 80struct scmi_imx_misc_board_info_out {
 81	__le32 attributes;
 82#define MISC_MAX_BRDNAME	16
 83	u8 brdname[MISC_MAX_BRDNAME];
 84};
 85
 86struct scmi_imx_misc_cfg_info_out {
 87	__le32 msel;
 88#define MISC_MAX_CFGNAME	16
 89	u8 cfgname[MISC_MAX_CFGNAME];
 90};
 91
 92static 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
120static 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
137static 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
164static int
165scmi_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
182static void *
183scmi_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
205static 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
210static 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
218static 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
226static 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
265static 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
299static 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
326static 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
350static 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
374static 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
380static 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
416static 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};
426module_scmi_protocol(scmi_imx_misc);
427
428MODULE_ALIAS("scmi-protocol-" __stringify(SCMI_PROTOCOL_IMX_MISC) "-" SCMI_IMX_VENDOR);
429MODULE_DESCRIPTION("i.MX SCMI MISC driver");
430MODULE_LICENSE("GPL");
~/ Read-only file preview.