xref: /linux/drivers/firmware/arm_scmi/vendors/imx/imx-sm-bbm.c (revision 55d0969c451159cff86949b38c39171cab962069)
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 
23 enum 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 
50 struct scmi_imx_bbm_info {
51 	u32 version;
52 	int nr_rtc;
53 	int nr_gpr;
54 };
55 
56 struct scmi_msg_imx_bbm_protocol_attributes {
57 	__le32 attributes;
58 };
59 
60 struct scmi_imx_bbm_set_time {
61 	__le32 id;
62 	__le32 flags;
63 	__le32 value_low;
64 	__le32 value_high;
65 };
66 
67 struct scmi_imx_bbm_get_time {
68 	__le32 id;
69 	__le32 flags;
70 };
71 
72 struct scmi_imx_bbm_alarm_time {
73 	__le32 id;
74 	__le32 flags;
75 	__le32 value_low;
76 	__le32 value_high;
77 };
78 
79 struct scmi_msg_imx_bbm_rtc_notify {
80 	__le32 rtc_id;
81 	__le32 flags;
82 };
83 
84 struct scmi_msg_imx_bbm_button_notify {
85 	__le32 flags;
86 };
87 
88 struct scmi_imx_bbm_notify_payld {
89 	__le32 flags;
90 };
91 
92 static 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 
116 static 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 
154 static enum scmi_imx_bbm_protocol_cmd evt_2_cmd[] = {
155 	IMX_BBM_RTC_NOTIFY,
156 	IMX_BBM_BUTTON_NOTIFY
157 };
158 
159 static 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 
176 static 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 
209 static 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 
222 static 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 
227 static 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 
235 static 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 
263 static 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 
292 static 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 
321 static 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 
339 static 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 
346 static 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 
370 static 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 = "NXP",
378 	.sub_vendor_id = "IMX",
379 };
380 module_scmi_protocol(scmi_imx_bbm);
381 
382 MODULE_DESCRIPTION("i.MX SCMI BBM driver");
383 MODULE_LICENSE("GPL");
384