File Index
Files and directories
Sign in
FILE

imx-sm-bbm.c

File preview with syntax highlighting.

linux-6.19.14/drivers/firmware/arm_scmi/vendors/imx/imx-sm-bbm.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * System Control and Management Interface (SCMI) NXP BBM Protocol
  4 *
  5 * Copyright 2024 NXP
  6 */
  7
  8#define pr_fmt(fmt) "SCMI Notifications BBM - " 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
 23enum scmi_imx_bbm_protocol_cmd {
 24	IMX_BBM_GPR_SET = 0x3,
 25	IMX_BBM_GPR_GET = 0x4,
 26	IMX_BBM_RTC_ATTRIBUTES = 0x5,
 27	IMX_BBM_RTC_TIME_SET = 0x6,
 28	IMX_BBM_RTC_TIME_GET = 0x7,
 29	IMX_BBM_RTC_ALARM_SET = 0x8,
 30	IMX_BBM_BUTTON_GET = 0x9,
 31	IMX_BBM_RTC_NOTIFY = 0xA,
 32	IMX_BBM_BUTTON_NOTIFY = 0xB,
 33};
 34
 35#define GET_RTCS_NR(x)	le32_get_bits((x), GENMASK(23, 16))
 36#define GET_GPRS_NR(x)	le32_get_bits((x), GENMASK(15, 0))
 37
 38#define SCMI_IMX_BBM_NOTIFY_RTC_UPDATED		BIT(2)
 39#define SCMI_IMX_BBM_NOTIFY_RTC_ROLLOVER	BIT(1)
 40#define SCMI_IMX_BBM_NOTIFY_RTC_ALARM		BIT(0)
 41
 42#define SCMI_IMX_BBM_RTC_ALARM_ENABLE_FLAG	BIT(0)
 43
 44#define SCMI_IMX_BBM_NOTIFY_RTC_FLAG	\
 45	(SCMI_IMX_BBM_NOTIFY_RTC_UPDATED | SCMI_IMX_BBM_NOTIFY_RTC_ROLLOVER | \
 46	 SCMI_IMX_BBM_NOTIFY_RTC_ALARM)
 47
 48#define SCMI_IMX_BBM_EVENT_RTC_MASK		GENMASK(31, 24)
 49
 50struct scmi_imx_bbm_info {
 51	u32 version;
 52	int nr_rtc;
 53	int nr_gpr;
 54};
 55
 56struct scmi_msg_imx_bbm_protocol_attributes {
 57	__le32 attributes;
 58};
 59
 60struct scmi_imx_bbm_set_time {
 61	__le32 id;
 62	__le32 flags;
 63	__le32 value_low;
 64	__le32 value_high;
 65};
 66
 67struct scmi_imx_bbm_get_time {
 68	__le32 id;
 69	__le32 flags;
 70};
 71
 72struct scmi_imx_bbm_alarm_time {
 73	__le32 id;
 74	__le32 flags;
 75	__le32 value_low;
 76	__le32 value_high;
 77};
 78
 79struct scmi_msg_imx_bbm_rtc_notify {
 80	__le32 rtc_id;
 81	__le32 flags;
 82};
 83
 84struct scmi_msg_imx_bbm_button_notify {
 85	__le32 flags;
 86};
 87
 88struct scmi_imx_bbm_notify_payld {
 89	__le32 flags;
 90};
 91
 92static int scmi_imx_bbm_attributes_get(const struct scmi_protocol_handle *ph,
 93				       struct scmi_imx_bbm_info *pi)
 94{
 95	int ret;
 96	struct scmi_xfer *t;
 97	struct scmi_msg_imx_bbm_protocol_attributes *attr;
 98
 99	ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0, sizeof(*attr), &t);
100	if (ret)
101		return ret;
102
103	attr = t->rx.buf;
104
105	ret = ph->xops->do_xfer(ph, t);
106	if (!ret) {
107		pi->nr_rtc = GET_RTCS_NR(attr->attributes);
108		pi->nr_gpr = GET_GPRS_NR(attr->attributes);
109	}
110
111	ph->xops->xfer_put(ph, t);
112
113	return ret;
114}
115
116static int scmi_imx_bbm_notify(const struct scmi_protocol_handle *ph,
117			       u32 src_id, int message_id, bool enable)
118{
119	int ret;
120	struct scmi_xfer *t;
121
122	if (message_id == IMX_BBM_RTC_NOTIFY) {
123		struct scmi_msg_imx_bbm_rtc_notify *rtc_notify;
124
125		ret = ph->xops->xfer_get_init(ph, message_id,
126					      sizeof(*rtc_notify), 0, &t);
127		if (ret)
128			return ret;
129
130		rtc_notify = t->tx.buf;
131		rtc_notify->rtc_id = cpu_to_le32(0);
132		rtc_notify->flags =
133			cpu_to_le32(enable ? SCMI_IMX_BBM_NOTIFY_RTC_FLAG : 0);
134	} else if (message_id == IMX_BBM_BUTTON_NOTIFY) {
135		struct scmi_msg_imx_bbm_button_notify *button_notify;
136
137		ret = ph->xops->xfer_get_init(ph, message_id,
138					      sizeof(*button_notify), 0, &t);
139		if (ret)
140			return ret;
141
142		button_notify = t->tx.buf;
143		button_notify->flags = cpu_to_le32(enable ? 1 : 0);
144	} else {
145		return -EINVAL;
146	}
147
148	ret = ph->xops->do_xfer(ph, t);
149
150	ph->xops->xfer_put(ph, t);
151	return ret;
152}
153
154static enum scmi_imx_bbm_protocol_cmd evt_2_cmd[] = {
155	IMX_BBM_RTC_NOTIFY,
156	IMX_BBM_BUTTON_NOTIFY
157};
158
159static int scmi_imx_bbm_set_notify_enabled(const struct scmi_protocol_handle *ph,
160					   u8 evt_id, u32 src_id, bool enable)
161{
162	int ret, cmd_id;
163
164	if (evt_id >= ARRAY_SIZE(evt_2_cmd))
165		return -EINVAL;
166
167	cmd_id = evt_2_cmd[evt_id];
168	ret = scmi_imx_bbm_notify(ph, src_id, cmd_id, enable);
169	if (ret)
170		pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n",
171			 evt_id, src_id, ret);
172
173	return ret;
174}
175
176static void *scmi_imx_bbm_fill_custom_report(const struct scmi_protocol_handle *ph,
177					     u8 evt_id, ktime_t timestamp,
178					     const void *payld, size_t payld_sz,
179					     void *report, u32 *src_id)
180{
181	const struct scmi_imx_bbm_notify_payld *p = payld;
182	struct scmi_imx_bbm_notif_report *r = report;
183
184	if (sizeof(*p) != payld_sz)
185		return NULL;
186
187	if (evt_id == SCMI_EVENT_IMX_BBM_RTC) {
188		r->is_rtc = true;
189		r->is_button = false;
190		r->timestamp = timestamp;
191		r->rtc_id = le32_get_bits(p->flags, SCMI_IMX_BBM_EVENT_RTC_MASK);
192		r->rtc_evt = le32_get_bits(p->flags, SCMI_IMX_BBM_NOTIFY_RTC_FLAG);
193		dev_dbg(ph->dev, "RTC: %d evt: %x\n", r->rtc_id, r->rtc_evt);
194		*src_id = r->rtc_evt;
195	} else if (evt_id == SCMI_EVENT_IMX_BBM_BUTTON) {
196		r->is_rtc = false;
197		r->is_button = true;
198		r->timestamp = timestamp;
199		dev_dbg(ph->dev, "BBM Button\n");
200		*src_id = 0;
201	} else {
202		WARN_ON_ONCE(1);
203		return NULL;
204	}
205
206	return r;
207}
208
209static const struct scmi_event scmi_imx_bbm_events[] = {
210	{
211		.id = SCMI_EVENT_IMX_BBM_RTC,
212		.max_payld_sz = sizeof(struct scmi_imx_bbm_notify_payld),
213		.max_report_sz = sizeof(struct scmi_imx_bbm_notif_report),
214	},
215	{
216		.id = SCMI_EVENT_IMX_BBM_BUTTON,
217		.max_payld_sz = sizeof(struct scmi_imx_bbm_notify_payld),
218		.max_report_sz = sizeof(struct scmi_imx_bbm_notif_report),
219	},
220};
221
222static const struct scmi_event_ops scmi_imx_bbm_event_ops = {
223	.set_notify_enabled = scmi_imx_bbm_set_notify_enabled,
224	.fill_custom_report = scmi_imx_bbm_fill_custom_report,
225};
226
227static const struct scmi_protocol_events scmi_imx_bbm_protocol_events = {
228	.queue_sz = SCMI_PROTO_QUEUE_SZ,
229	.ops = &scmi_imx_bbm_event_ops,
230	.evts = scmi_imx_bbm_events,
231	.num_events = ARRAY_SIZE(scmi_imx_bbm_events),
232	.num_sources = 1,
233};
234
235static int scmi_imx_bbm_rtc_time_set(const struct scmi_protocol_handle *ph,
236				     u32 rtc_id, u64 sec)
237{
238	struct scmi_imx_bbm_info *pi = ph->get_priv(ph);
239	struct scmi_imx_bbm_set_time *cfg;
240	struct scmi_xfer *t;
241	int ret;
242
243	if (rtc_id >= pi->nr_rtc)
244		return -EINVAL;
245
246	ret = ph->xops->xfer_get_init(ph, IMX_BBM_RTC_TIME_SET, sizeof(*cfg), 0, &t);
247	if (ret)
248		return ret;
249
250	cfg = t->tx.buf;
251	cfg->id = cpu_to_le32(rtc_id);
252	cfg->flags = 0;
253	cfg->value_low = cpu_to_le32(lower_32_bits(sec));
254	cfg->value_high = cpu_to_le32(upper_32_bits(sec));
255
256	ret = ph->xops->do_xfer(ph, t);
257
258	ph->xops->xfer_put(ph, t);
259
260	return ret;
261}
262
263static int scmi_imx_bbm_rtc_time_get(const struct scmi_protocol_handle *ph,
264				     u32 rtc_id, u64 *value)
265{
266	struct scmi_imx_bbm_info *pi = ph->get_priv(ph);
267	struct scmi_imx_bbm_get_time *cfg;
268	struct scmi_xfer *t;
269	int ret;
270
271	if (rtc_id >= pi->nr_rtc)
272		return -EINVAL;
273
274	ret = ph->xops->xfer_get_init(ph, IMX_BBM_RTC_TIME_GET, sizeof(*cfg),
275				      sizeof(u64), &t);
276	if (ret)
277		return ret;
278
279	cfg = t->tx.buf;
280	cfg->id = cpu_to_le32(rtc_id);
281	cfg->flags = 0;
282
283	ret = ph->xops->do_xfer(ph, t);
284	if (!ret)
285		*value = get_unaligned_le64(t->rx.buf);
286
287	ph->xops->xfer_put(ph, t);
288
289	return ret;
290}
291
292static int scmi_imx_bbm_rtc_alarm_set(const struct scmi_protocol_handle *ph,
293				      u32 rtc_id, bool enable, u64 sec)
294{
295	struct scmi_imx_bbm_info *pi = ph->get_priv(ph);
296	struct scmi_imx_bbm_alarm_time *cfg;
297	struct scmi_xfer *t;
298	int ret;
299
300	if (rtc_id >= pi->nr_rtc)
301		return -EINVAL;
302
303	ret = ph->xops->xfer_get_init(ph, IMX_BBM_RTC_ALARM_SET, sizeof(*cfg), 0, &t);
304	if (ret)
305		return ret;
306
307	cfg = t->tx.buf;
308	cfg->id = cpu_to_le32(rtc_id);
309	cfg->flags = enable ?
310		     cpu_to_le32(SCMI_IMX_BBM_RTC_ALARM_ENABLE_FLAG) : 0;
311	cfg->value_low = cpu_to_le32(lower_32_bits(sec));
312	cfg->value_high = cpu_to_le32(upper_32_bits(sec));
313
314	ret = ph->xops->do_xfer(ph, t);
315
316	ph->xops->xfer_put(ph, t);
317
318	return ret;
319}
320
321static int scmi_imx_bbm_button_get(const struct scmi_protocol_handle *ph, u32 *state)
322{
323	struct scmi_xfer *t;
324	int ret;
325
326	ret = ph->xops->xfer_get_init(ph, IMX_BBM_BUTTON_GET, 0, sizeof(u32), &t);
327	if (ret)
328		return ret;
329
330	ret = ph->xops->do_xfer(ph, t);
331	if (!ret)
332		*state = get_unaligned_le32(t->rx.buf);
333
334	ph->xops->xfer_put(ph, t);
335
336	return ret;
337}
338
339static const struct scmi_imx_bbm_proto_ops scmi_imx_bbm_proto_ops = {
340	.rtc_time_get = scmi_imx_bbm_rtc_time_get,
341	.rtc_time_set = scmi_imx_bbm_rtc_time_set,
342	.rtc_alarm_set = scmi_imx_bbm_rtc_alarm_set,
343	.button_get = scmi_imx_bbm_button_get,
344};
345
346static int scmi_imx_bbm_protocol_init(const struct scmi_protocol_handle *ph)
347{
348	u32 version;
349	int ret;
350	struct scmi_imx_bbm_info *binfo;
351
352	ret = ph->xops->version_get(ph, &version);
353	if (ret)
354		return ret;
355
356	dev_info(ph->dev, "NXP SM BBM Version %d.%d\n",
357		 PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
358
359	binfo = devm_kzalloc(ph->dev, sizeof(*binfo), GFP_KERNEL);
360	if (!binfo)
361		return -ENOMEM;
362
363	ret = scmi_imx_bbm_attributes_get(ph, binfo);
364	if (ret)
365		return ret;
366
367	return ph->set_priv(ph, binfo, version);
368}
369
370static const struct scmi_protocol scmi_imx_bbm = {
371	.id = SCMI_PROTOCOL_IMX_BBM,
372	.owner = THIS_MODULE,
373	.instance_init = &scmi_imx_bbm_protocol_init,
374	.ops = &scmi_imx_bbm_proto_ops,
375	.events = &scmi_imx_bbm_protocol_events,
376	.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
377	.vendor_id = SCMI_IMX_VENDOR,
378	.sub_vendor_id = SCMI_IMX_SUBVENDOR,
379};
380module_scmi_protocol(scmi_imx_bbm);
381
382MODULE_ALIAS("scmi-protocol-" __stringify(SCMI_PROTOCOL_IMX_BBM) "-" SCMI_IMX_VENDOR);
383MODULE_DESCRIPTION("i.MX SCMI BBM driver");
384MODULE_LICENSE("GPL");
~/ Read-only file preview.