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