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
scmi_imx_bbm_attributes_get(const struct scmi_protocol_handle * ph,struct scmi_imx_bbm_info * pi)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
scmi_imx_bbm_notify(const struct scmi_protocol_handle * ph,u32 src_id,int message_id,bool enable)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
scmi_imx_bbm_set_notify_enabled(const struct scmi_protocol_handle * ph,u8 evt_id,u32 src_id,bool enable)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
scmi_imx_bbm_fill_custom_report(const struct scmi_protocol_handle * ph,u8 evt_id,ktime_t timestamp,const void * payld,size_t payld_sz,void * report,u32 * src_id)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
scmi_imx_bbm_rtc_time_set(const struct scmi_protocol_handle * ph,u32 rtc_id,u64 sec)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
scmi_imx_bbm_rtc_time_get(const struct scmi_protocol_handle * ph,u32 rtc_id,u64 * value)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
scmi_imx_bbm_rtc_alarm_set(const struct scmi_protocol_handle * ph,u32 rtc_id,bool enable,u64 sec)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
scmi_imx_bbm_button_get(const struct scmi_protocol_handle * ph,u32 * state)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
scmi_imx_bbm_protocol_init(const struct scmi_protocol_handle * ph)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