Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/firmware/arm_scmi/powercap.c
26444 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* System Control and Management Interface (SCMI) Powercap Protocol
4
*
5
* Copyright (C) 2022 ARM Ltd.
6
*/
7
8
#define pr_fmt(fmt) "SCMI Notifications POWERCAP - " fmt
9
10
#include <linux/bitfield.h>
11
#include <linux/io.h>
12
#include <linux/module.h>
13
#include <linux/scmi_protocol.h>
14
15
#include <trace/events/scmi.h>
16
17
#include "protocols.h"
18
#include "notify.h"
19
20
/* Updated only after ALL the mandatory features for that version are merged */
21
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20000
22
23
enum scmi_powercap_protocol_cmd {
24
POWERCAP_DOMAIN_ATTRIBUTES = 0x3,
25
POWERCAP_CAP_GET = 0x4,
26
POWERCAP_CAP_SET = 0x5,
27
POWERCAP_PAI_GET = 0x6,
28
POWERCAP_PAI_SET = 0x7,
29
POWERCAP_DOMAIN_NAME_GET = 0x8,
30
POWERCAP_MEASUREMENTS_GET = 0x9,
31
POWERCAP_CAP_NOTIFY = 0xa,
32
POWERCAP_MEASUREMENTS_NOTIFY = 0xb,
33
POWERCAP_DESCRIBE_FASTCHANNEL = 0xc,
34
};
35
36
enum {
37
POWERCAP_FC_CAP,
38
POWERCAP_FC_PAI,
39
POWERCAP_FC_MAX,
40
};
41
42
struct scmi_msg_resp_powercap_domain_attributes {
43
__le32 attributes;
44
#define SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(x) ((x) & BIT(31))
45
#define SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(x) ((x) & BIT(30))
46
#define SUPPORTS_ASYNC_POWERCAP_CAP_SET(x) ((x) & BIT(29))
47
#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(28))
48
#define SUPPORTS_POWERCAP_CAP_CONFIGURATION(x) ((x) & BIT(27))
49
#define SUPPORTS_POWERCAP_MONITORING(x) ((x) & BIT(26))
50
#define SUPPORTS_POWERCAP_PAI_CONFIGURATION(x) ((x) & BIT(25))
51
#define SUPPORTS_POWERCAP_FASTCHANNELS(x) ((x) & BIT(22))
52
#define POWERCAP_POWER_UNIT(x) \
53
(FIELD_GET(GENMASK(24, 23), (x)))
54
#define SUPPORTS_POWER_UNITS_MW(x) \
55
(POWERCAP_POWER_UNIT(x) == 0x2)
56
#define SUPPORTS_POWER_UNITS_UW(x) \
57
(POWERCAP_POWER_UNIT(x) == 0x1)
58
u8 name[SCMI_SHORT_NAME_MAX_SIZE];
59
__le32 min_pai;
60
__le32 max_pai;
61
__le32 pai_step;
62
__le32 min_power_cap;
63
__le32 max_power_cap;
64
__le32 power_cap_step;
65
__le32 sustainable_power;
66
__le32 accuracy;
67
__le32 parent_id;
68
};
69
70
struct scmi_msg_powercap_set_cap_or_pai {
71
__le32 domain;
72
__le32 flags;
73
#define CAP_SET_ASYNC BIT(1)
74
#define CAP_SET_IGNORE_DRESP BIT(0)
75
__le32 value;
76
};
77
78
struct scmi_msg_resp_powercap_cap_set_complete {
79
__le32 domain;
80
__le32 power_cap;
81
};
82
83
struct scmi_msg_resp_powercap_meas_get {
84
__le32 power;
85
__le32 pai;
86
};
87
88
struct scmi_msg_powercap_notify_cap {
89
__le32 domain;
90
__le32 notify_enable;
91
};
92
93
struct scmi_msg_powercap_notify_thresh {
94
__le32 domain;
95
__le32 notify_enable;
96
__le32 power_thresh_low;
97
__le32 power_thresh_high;
98
};
99
100
struct scmi_powercap_cap_changed_notify_payld {
101
__le32 agent_id;
102
__le32 domain_id;
103
__le32 power_cap;
104
__le32 pai;
105
};
106
107
struct scmi_powercap_meas_changed_notify_payld {
108
__le32 agent_id;
109
__le32 domain_id;
110
__le32 power;
111
};
112
113
struct scmi_powercap_state {
114
bool enabled;
115
u32 last_pcap;
116
bool meas_notif_enabled;
117
u64 thresholds;
118
#define THRESH_LOW(p, id) \
119
(lower_32_bits((p)->states[(id)].thresholds))
120
#define THRESH_HIGH(p, id) \
121
(upper_32_bits((p)->states[(id)].thresholds))
122
};
123
124
struct powercap_info {
125
u32 version;
126
int num_domains;
127
bool notify_cap_cmd;
128
bool notify_measurements_cmd;
129
struct scmi_powercap_state *states;
130
struct scmi_powercap_info *powercaps;
131
};
132
133
static enum scmi_powercap_protocol_cmd evt_2_cmd[] = {
134
POWERCAP_CAP_NOTIFY,
135
POWERCAP_MEASUREMENTS_NOTIFY,
136
};
137
138
static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,
139
u32 domain, int message_id, bool enable);
140
141
static int
142
scmi_powercap_attributes_get(const struct scmi_protocol_handle *ph,
143
struct powercap_info *pi)
144
{
145
int ret;
146
struct scmi_xfer *t;
147
148
ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0,
149
sizeof(u32), &t);
150
if (ret)
151
return ret;
152
153
ret = ph->xops->do_xfer(ph, t);
154
if (!ret) {
155
u32 attributes;
156
157
attributes = get_unaligned_le32(t->rx.buf);
158
pi->num_domains = FIELD_GET(GENMASK(15, 0), attributes);
159
}
160
161
ph->xops->xfer_put(ph, t);
162
163
if (!ret) {
164
if (!ph->hops->protocol_msg_check(ph,
165
POWERCAP_CAP_NOTIFY, NULL))
166
pi->notify_cap_cmd = true;
167
168
if (!ph->hops->protocol_msg_check(ph,
169
POWERCAP_MEASUREMENTS_NOTIFY,
170
NULL))
171
pi->notify_measurements_cmd = true;
172
}
173
174
return ret;
175
}
176
177
static inline int
178
scmi_powercap_validate(unsigned int min_val, unsigned int max_val,
179
unsigned int step_val, bool configurable)
180
{
181
if (!min_val || !max_val)
182
return -EPROTO;
183
184
if ((configurable && min_val == max_val) ||
185
(!configurable && min_val != max_val))
186
return -EPROTO;
187
188
if (min_val != max_val && !step_val)
189
return -EPROTO;
190
191
return 0;
192
}
193
194
static int
195
scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
196
struct powercap_info *pinfo, u32 domain)
197
{
198
int ret;
199
u32 flags;
200
struct scmi_xfer *t;
201
struct scmi_powercap_info *dom_info = pinfo->powercaps + domain;
202
struct scmi_msg_resp_powercap_domain_attributes *resp;
203
204
ret = ph->xops->xfer_get_init(ph, POWERCAP_DOMAIN_ATTRIBUTES,
205
sizeof(domain), sizeof(*resp), &t);
206
if (ret)
207
return ret;
208
209
put_unaligned_le32(domain, t->tx.buf);
210
resp = t->rx.buf;
211
212
ret = ph->xops->do_xfer(ph, t);
213
if (!ret) {
214
flags = le32_to_cpu(resp->attributes);
215
216
dom_info->id = domain;
217
if (pinfo->notify_cap_cmd)
218
dom_info->notify_powercap_cap_change =
219
SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags);
220
if (pinfo->notify_measurements_cmd)
221
dom_info->notify_powercap_measurement_change =
222
SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
223
dom_info->async_powercap_cap_set =
224
SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags);
225
dom_info->powercap_cap_config =
226
SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags);
227
dom_info->powercap_monitoring =
228
SUPPORTS_POWERCAP_MONITORING(flags);
229
dom_info->powercap_pai_config =
230
SUPPORTS_POWERCAP_PAI_CONFIGURATION(flags);
231
dom_info->powercap_scale_mw =
232
SUPPORTS_POWER_UNITS_MW(flags);
233
dom_info->powercap_scale_uw =
234
SUPPORTS_POWER_UNITS_UW(flags);
235
dom_info->fastchannels =
236
SUPPORTS_POWERCAP_FASTCHANNELS(flags);
237
238
strscpy(dom_info->name, resp->name, SCMI_SHORT_NAME_MAX_SIZE);
239
240
dom_info->min_pai = le32_to_cpu(resp->min_pai);
241
dom_info->max_pai = le32_to_cpu(resp->max_pai);
242
dom_info->pai_step = le32_to_cpu(resp->pai_step);
243
ret = scmi_powercap_validate(dom_info->min_pai,
244
dom_info->max_pai,
245
dom_info->pai_step,
246
dom_info->powercap_pai_config);
247
if (ret) {
248
dev_err(ph->dev,
249
"Platform reported inconsistent PAI config for domain %d - %s\n",
250
dom_info->id, dom_info->name);
251
goto clean;
252
}
253
254
dom_info->min_power_cap = le32_to_cpu(resp->min_power_cap);
255
dom_info->max_power_cap = le32_to_cpu(resp->max_power_cap);
256
dom_info->power_cap_step = le32_to_cpu(resp->power_cap_step);
257
ret = scmi_powercap_validate(dom_info->min_power_cap,
258
dom_info->max_power_cap,
259
dom_info->power_cap_step,
260
dom_info->powercap_cap_config);
261
if (ret) {
262
dev_err(ph->dev,
263
"Platform reported inconsistent CAP config for domain %d - %s\n",
264
dom_info->id, dom_info->name);
265
goto clean;
266
}
267
268
dom_info->sustainable_power =
269
le32_to_cpu(resp->sustainable_power);
270
dom_info->accuracy = le32_to_cpu(resp->accuracy);
271
272
dom_info->parent_id = le32_to_cpu(resp->parent_id);
273
if (dom_info->parent_id != SCMI_POWERCAP_ROOT_ZONE_ID &&
274
(dom_info->parent_id >= pinfo->num_domains ||
275
dom_info->parent_id == dom_info->id)) {
276
dev_err(ph->dev,
277
"Platform reported inconsistent parent ID for domain %d - %s\n",
278
dom_info->id, dom_info->name);
279
ret = -ENODEV;
280
}
281
}
282
283
clean:
284
ph->xops->xfer_put(ph, t);
285
286
/*
287
* If supported overwrite short name with the extended one;
288
* on error just carry on and use already provided short name.
289
*/
290
if (!ret && SUPPORTS_EXTENDED_NAMES(flags))
291
ph->hops->extended_name_get(ph, POWERCAP_DOMAIN_NAME_GET,
292
domain, NULL, dom_info->name,
293
SCMI_MAX_STR_SIZE);
294
295
return ret;
296
}
297
298
static int scmi_powercap_num_domains_get(const struct scmi_protocol_handle *ph)
299
{
300
struct powercap_info *pi = ph->get_priv(ph);
301
302
return pi->num_domains;
303
}
304
305
static const struct scmi_powercap_info *
306
scmi_powercap_dom_info_get(const struct scmi_protocol_handle *ph, u32 domain_id)
307
{
308
struct powercap_info *pi = ph->get_priv(ph);
309
310
if (domain_id >= pi->num_domains)
311
return NULL;
312
313
return pi->powercaps + domain_id;
314
}
315
316
static int scmi_powercap_xfer_cap_get(const struct scmi_protocol_handle *ph,
317
u32 domain_id, u32 *power_cap)
318
{
319
int ret;
320
struct scmi_xfer *t;
321
322
ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_GET, sizeof(u32),
323
sizeof(u32), &t);
324
if (ret)
325
return ret;
326
327
put_unaligned_le32(domain_id, t->tx.buf);
328
ret = ph->xops->do_xfer(ph, t);
329
if (!ret)
330
*power_cap = get_unaligned_le32(t->rx.buf);
331
332
ph->xops->xfer_put(ph, t);
333
334
return ret;
335
}
336
337
static int __scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
338
const struct scmi_powercap_info *dom,
339
u32 *power_cap)
340
{
341
if (dom->fc_info && dom->fc_info[POWERCAP_FC_CAP].get_addr) {
342
*power_cap = ioread32(dom->fc_info[POWERCAP_FC_CAP].get_addr);
343
trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_GET,
344
dom->id, *power_cap, 0);
345
return 0;
346
}
347
348
return scmi_powercap_xfer_cap_get(ph, dom->id, power_cap);
349
}
350
351
static int scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
352
u32 domain_id, u32 *power_cap)
353
{
354
const struct scmi_powercap_info *dom;
355
356
if (!power_cap)
357
return -EINVAL;
358
359
dom = scmi_powercap_dom_info_get(ph, domain_id);
360
if (!dom)
361
return -EINVAL;
362
363
return __scmi_powercap_cap_get(ph, dom, power_cap);
364
}
365
366
static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph,
367
const struct scmi_powercap_info *pc,
368
u32 power_cap, bool ignore_dresp)
369
{
370
int ret;
371
struct scmi_xfer *t;
372
struct scmi_msg_powercap_set_cap_or_pai *msg;
373
374
ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_SET,
375
sizeof(*msg), 0, &t);
376
if (ret)
377
return ret;
378
379
msg = t->tx.buf;
380
msg->domain = cpu_to_le32(pc->id);
381
msg->flags =
382
cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, pc->async_powercap_cap_set) |
383
FIELD_PREP(CAP_SET_IGNORE_DRESP, ignore_dresp));
384
msg->value = cpu_to_le32(power_cap);
385
386
if (!pc->async_powercap_cap_set || ignore_dresp) {
387
ret = ph->xops->do_xfer(ph, t);
388
} else {
389
ret = ph->xops->do_xfer_with_response(ph, t);
390
if (!ret) {
391
struct scmi_msg_resp_powercap_cap_set_complete *resp;
392
393
resp = t->rx.buf;
394
if (le32_to_cpu(resp->domain) == pc->id)
395
dev_dbg(ph->dev,
396
"Powercap ID %d CAP set async to %u\n",
397
pc->id,
398
get_unaligned_le32(&resp->power_cap));
399
else
400
ret = -EPROTO;
401
}
402
}
403
404
ph->xops->xfer_put(ph, t);
405
return ret;
406
}
407
408
static int __scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
409
struct powercap_info *pi, u32 domain_id,
410
u32 power_cap, bool ignore_dresp)
411
{
412
int ret = -EINVAL;
413
const struct scmi_powercap_info *pc;
414
415
pc = scmi_powercap_dom_info_get(ph, domain_id);
416
if (!pc || !pc->powercap_cap_config)
417
return ret;
418
419
if (power_cap &&
420
(power_cap < pc->min_power_cap || power_cap > pc->max_power_cap))
421
return ret;
422
423
if (pc->fc_info && pc->fc_info[POWERCAP_FC_CAP].set_addr) {
424
struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_CAP];
425
426
iowrite32(power_cap, fci->set_addr);
427
ph->hops->fastchannel_db_ring(fci->set_db);
428
trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_SET,
429
domain_id, power_cap, 0);
430
ret = 0;
431
} else {
432
ret = scmi_powercap_xfer_cap_set(ph, pc, power_cap,
433
ignore_dresp);
434
}
435
436
/* Save the last explicitly set non-zero powercap value */
437
if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 && !ret && power_cap)
438
pi->states[domain_id].last_pcap = power_cap;
439
440
return ret;
441
}
442
443
static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
444
u32 domain_id, u32 power_cap,
445
bool ignore_dresp)
446
{
447
struct powercap_info *pi = ph->get_priv(ph);
448
449
/*
450
* Disallow zero as a possible explicitly requested powercap:
451
* there are enable/disable operations for this.
452
*/
453
if (!power_cap)
454
return -EINVAL;
455
456
/* Just log the last set request if acting on a disabled domain */
457
if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 &&
458
!pi->states[domain_id].enabled) {
459
pi->states[domain_id].last_pcap = power_cap;
460
return 0;
461
}
462
463
return __scmi_powercap_cap_set(ph, pi, domain_id,
464
power_cap, ignore_dresp);
465
}
466
467
static int scmi_powercap_xfer_pai_get(const struct scmi_protocol_handle *ph,
468
u32 domain_id, u32 *pai)
469
{
470
int ret;
471
struct scmi_xfer *t;
472
473
ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_GET, sizeof(u32),
474
sizeof(u32), &t);
475
if (ret)
476
return ret;
477
478
put_unaligned_le32(domain_id, t->tx.buf);
479
ret = ph->xops->do_xfer(ph, t);
480
if (!ret)
481
*pai = get_unaligned_le32(t->rx.buf);
482
483
ph->xops->xfer_put(ph, t);
484
485
return ret;
486
}
487
488
static int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph,
489
u32 domain_id, u32 *pai)
490
{
491
struct scmi_powercap_info *dom;
492
struct powercap_info *pi = ph->get_priv(ph);
493
494
if (!pai || domain_id >= pi->num_domains)
495
return -EINVAL;
496
497
dom = pi->powercaps + domain_id;
498
if (dom->fc_info && dom->fc_info[POWERCAP_FC_PAI].get_addr) {
499
*pai = ioread32(dom->fc_info[POWERCAP_FC_PAI].get_addr);
500
trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_GET,
501
domain_id, *pai, 0);
502
return 0;
503
}
504
505
return scmi_powercap_xfer_pai_get(ph, domain_id, pai);
506
}
507
508
static int scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle *ph,
509
u32 domain_id, u32 pai)
510
{
511
int ret;
512
struct scmi_xfer *t;
513
struct scmi_msg_powercap_set_cap_or_pai *msg;
514
515
ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_SET,
516
sizeof(*msg), 0, &t);
517
if (ret)
518
return ret;
519
520
msg = t->tx.buf;
521
msg->domain = cpu_to_le32(domain_id);
522
msg->flags = cpu_to_le32(0);
523
msg->value = cpu_to_le32(pai);
524
525
ret = ph->xops->do_xfer(ph, t);
526
527
ph->xops->xfer_put(ph, t);
528
return ret;
529
}
530
531
static int scmi_powercap_pai_set(const struct scmi_protocol_handle *ph,
532
u32 domain_id, u32 pai)
533
{
534
const struct scmi_powercap_info *pc;
535
536
pc = scmi_powercap_dom_info_get(ph, domain_id);
537
if (!pc || !pc->powercap_pai_config || !pai ||
538
pai < pc->min_pai || pai > pc->max_pai)
539
return -EINVAL;
540
541
if (pc->fc_info && pc->fc_info[POWERCAP_FC_PAI].set_addr) {
542
struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_PAI];
543
544
trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_SET,
545
domain_id, pai, 0);
546
iowrite32(pai, fci->set_addr);
547
ph->hops->fastchannel_db_ring(fci->set_db);
548
return 0;
549
}
550
551
return scmi_powercap_xfer_pai_set(ph, domain_id, pai);
552
}
553
554
static int scmi_powercap_measurements_get(const struct scmi_protocol_handle *ph,
555
u32 domain_id, u32 *average_power,
556
u32 *pai)
557
{
558
int ret;
559
struct scmi_xfer *t;
560
struct scmi_msg_resp_powercap_meas_get *resp;
561
const struct scmi_powercap_info *pc;
562
563
pc = scmi_powercap_dom_info_get(ph, domain_id);
564
if (!pc || !pc->powercap_monitoring || !pai || !average_power)
565
return -EINVAL;
566
567
ret = ph->xops->xfer_get_init(ph, POWERCAP_MEASUREMENTS_GET,
568
sizeof(u32), sizeof(*resp), &t);
569
if (ret)
570
return ret;
571
572
resp = t->rx.buf;
573
put_unaligned_le32(domain_id, t->tx.buf);
574
ret = ph->xops->do_xfer(ph, t);
575
if (!ret) {
576
*average_power = le32_to_cpu(resp->power);
577
*pai = le32_to_cpu(resp->pai);
578
}
579
580
ph->xops->xfer_put(ph, t);
581
return ret;
582
}
583
584
static int
585
scmi_powercap_measurements_threshold_get(const struct scmi_protocol_handle *ph,
586
u32 domain_id, u32 *power_thresh_low,
587
u32 *power_thresh_high)
588
{
589
struct powercap_info *pi = ph->get_priv(ph);
590
591
if (!power_thresh_low || !power_thresh_high ||
592
domain_id >= pi->num_domains)
593
return -EINVAL;
594
595
*power_thresh_low = THRESH_LOW(pi, domain_id);
596
*power_thresh_high = THRESH_HIGH(pi, domain_id);
597
598
return 0;
599
}
600
601
static int
602
scmi_powercap_measurements_threshold_set(const struct scmi_protocol_handle *ph,
603
u32 domain_id, u32 power_thresh_low,
604
u32 power_thresh_high)
605
{
606
int ret = 0;
607
struct powercap_info *pi = ph->get_priv(ph);
608
609
if (domain_id >= pi->num_domains ||
610
power_thresh_low > power_thresh_high)
611
return -EINVAL;
612
613
/* Anything to do ? */
614
if (THRESH_LOW(pi, domain_id) == power_thresh_low &&
615
THRESH_HIGH(pi, domain_id) == power_thresh_high)
616
return ret;
617
618
pi->states[domain_id].thresholds =
619
(FIELD_PREP(GENMASK_ULL(31, 0), power_thresh_low) |
620
FIELD_PREP(GENMASK_ULL(63, 32), power_thresh_high));
621
622
/* Update thresholds if notification already enabled */
623
if (pi->states[domain_id].meas_notif_enabled)
624
ret = scmi_powercap_notify(ph, domain_id,
625
POWERCAP_MEASUREMENTS_NOTIFY,
626
true);
627
628
return ret;
629
}
630
631
static int scmi_powercap_cap_enable_set(const struct scmi_protocol_handle *ph,
632
u32 domain_id, bool enable)
633
{
634
int ret;
635
u32 power_cap;
636
struct powercap_info *pi = ph->get_priv(ph);
637
638
if (PROTOCOL_REV_MAJOR(pi->version) < 0x2)
639
return -EINVAL;
640
641
if (enable == pi->states[domain_id].enabled)
642
return 0;
643
644
if (enable) {
645
/* Cannot enable with a zero powercap. */
646
if (!pi->states[domain_id].last_pcap)
647
return -EINVAL;
648
649
ret = __scmi_powercap_cap_set(ph, pi, domain_id,
650
pi->states[domain_id].last_pcap,
651
true);
652
} else {
653
ret = __scmi_powercap_cap_set(ph, pi, domain_id, 0, true);
654
}
655
656
if (ret)
657
return ret;
658
659
/*
660
* Update our internal state to reflect final platform state: the SCMI
661
* server could have ignored a disable request and kept enforcing some
662
* powercap limit requested by other agents.
663
*/
664
ret = scmi_powercap_cap_get(ph, domain_id, &power_cap);
665
if (!ret)
666
pi->states[domain_id].enabled = !!power_cap;
667
668
return ret;
669
}
670
671
static int scmi_powercap_cap_enable_get(const struct scmi_protocol_handle *ph,
672
u32 domain_id, bool *enable)
673
{
674
int ret;
675
u32 power_cap;
676
struct powercap_info *pi = ph->get_priv(ph);
677
678
*enable = true;
679
if (PROTOCOL_REV_MAJOR(pi->version) < 0x2)
680
return 0;
681
682
/*
683
* Report always real platform state; platform could have ignored
684
* a previous disable request. Default true on any error.
685
*/
686
ret = scmi_powercap_cap_get(ph, domain_id, &power_cap);
687
if (!ret)
688
*enable = !!power_cap;
689
690
/* Update internal state with current real platform state */
691
pi->states[domain_id].enabled = *enable;
692
693
return 0;
694
}
695
696
static const struct scmi_powercap_proto_ops powercap_proto_ops = {
697
.num_domains_get = scmi_powercap_num_domains_get,
698
.info_get = scmi_powercap_dom_info_get,
699
.cap_get = scmi_powercap_cap_get,
700
.cap_set = scmi_powercap_cap_set,
701
.cap_enable_set = scmi_powercap_cap_enable_set,
702
.cap_enable_get = scmi_powercap_cap_enable_get,
703
.pai_get = scmi_powercap_pai_get,
704
.pai_set = scmi_powercap_pai_set,
705
.measurements_get = scmi_powercap_measurements_get,
706
.measurements_threshold_set = scmi_powercap_measurements_threshold_set,
707
.measurements_threshold_get = scmi_powercap_measurements_threshold_get,
708
};
709
710
static void scmi_powercap_domain_init_fc(const struct scmi_protocol_handle *ph,
711
u32 domain, struct scmi_fc_info **p_fc)
712
{
713
struct scmi_fc_info *fc;
714
715
fc = devm_kcalloc(ph->dev, POWERCAP_FC_MAX, sizeof(*fc), GFP_KERNEL);
716
if (!fc)
717
return;
718
719
ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
720
POWERCAP_CAP_SET, 4, domain,
721
&fc[POWERCAP_FC_CAP].set_addr,
722
&fc[POWERCAP_FC_CAP].set_db,
723
&fc[POWERCAP_FC_CAP].rate_limit);
724
725
ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
726
POWERCAP_CAP_GET, 4, domain,
727
&fc[POWERCAP_FC_CAP].get_addr, NULL,
728
&fc[POWERCAP_FC_CAP].rate_limit);
729
730
ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
731
POWERCAP_PAI_SET, 4, domain,
732
&fc[POWERCAP_FC_PAI].set_addr,
733
&fc[POWERCAP_FC_PAI].set_db,
734
&fc[POWERCAP_FC_PAI].rate_limit);
735
736
ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
737
POWERCAP_PAI_GET, 4, domain,
738
&fc[POWERCAP_FC_PAI].get_addr, NULL,
739
&fc[POWERCAP_FC_PAI].rate_limit);
740
741
*p_fc = fc;
742
}
743
744
static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,
745
u32 domain, int message_id, bool enable)
746
{
747
int ret;
748
struct scmi_xfer *t;
749
750
switch (message_id) {
751
case POWERCAP_CAP_NOTIFY:
752
{
753
struct scmi_msg_powercap_notify_cap *notify;
754
755
ret = ph->xops->xfer_get_init(ph, message_id,
756
sizeof(*notify), 0, &t);
757
if (ret)
758
return ret;
759
760
notify = t->tx.buf;
761
notify->domain = cpu_to_le32(domain);
762
notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0);
763
break;
764
}
765
case POWERCAP_MEASUREMENTS_NOTIFY:
766
{
767
u32 low, high;
768
struct scmi_msg_powercap_notify_thresh *notify;
769
770
/*
771
* Note that we have to pick the most recently configured
772
* thresholds to build a proper POWERCAP_MEASUREMENTS_NOTIFY
773
* enable request and we fail, complaining, if no thresholds
774
* were ever set, since this is an indication the API has been
775
* used wrongly.
776
*/
777
ret = scmi_powercap_measurements_threshold_get(ph, domain,
778
&low, &high);
779
if (ret)
780
return ret;
781
782
if (enable && !low && !high) {
783
dev_err(ph->dev,
784
"Invalid Measurements Notify thresholds: %u/%u\n",
785
low, high);
786
return -EINVAL;
787
}
788
789
ret = ph->xops->xfer_get_init(ph, message_id,
790
sizeof(*notify), 0, &t);
791
if (ret)
792
return ret;
793
794
notify = t->tx.buf;
795
notify->domain = cpu_to_le32(domain);
796
notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0);
797
notify->power_thresh_low = cpu_to_le32(low);
798
notify->power_thresh_high = cpu_to_le32(high);
799
break;
800
}
801
default:
802
return -EINVAL;
803
}
804
805
ret = ph->xops->do_xfer(ph, t);
806
807
ph->xops->xfer_put(ph, t);
808
return ret;
809
}
810
811
static bool
812
scmi_powercap_notify_supported(const struct scmi_protocol_handle *ph,
813
u8 evt_id, u32 src_id)
814
{
815
bool supported = false;
816
const struct scmi_powercap_info *dom_info;
817
struct powercap_info *pi = ph->get_priv(ph);
818
819
if (evt_id >= ARRAY_SIZE(evt_2_cmd) || src_id >= pi->num_domains)
820
return false;
821
822
dom_info = pi->powercaps + src_id;
823
if (evt_id == SCMI_EVENT_POWERCAP_CAP_CHANGED)
824
supported = dom_info->notify_powercap_cap_change;
825
else if (evt_id == SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED)
826
supported = dom_info->notify_powercap_measurement_change;
827
828
return supported;
829
}
830
831
static int
832
scmi_powercap_set_notify_enabled(const struct scmi_protocol_handle *ph,
833
u8 evt_id, u32 src_id, bool enable)
834
{
835
int ret, cmd_id;
836
struct powercap_info *pi = ph->get_priv(ph);
837
838
if (evt_id >= ARRAY_SIZE(evt_2_cmd) || src_id >= pi->num_domains)
839
return -EINVAL;
840
841
cmd_id = evt_2_cmd[evt_id];
842
ret = scmi_powercap_notify(ph, src_id, cmd_id, enable);
843
if (ret)
844
pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n",
845
evt_id, src_id, ret);
846
else if (cmd_id == POWERCAP_MEASUREMENTS_NOTIFY)
847
/*
848
* On success save the current notification enabled state, so
849
* as to be able to properly update the notification thresholds
850
* when they are modified on a domain for which measurement
851
* notifications were currently enabled.
852
*
853
* This is needed because the SCMI Notification core machinery
854
* and API does not support passing per-notification custom
855
* arguments at callback registration time.
856
*
857
* Note that this can be done here with a simple flag since the
858
* SCMI core Notifications code takes care of keeping proper
859
* per-domain enables refcounting, so that this helper function
860
* will be called only once (for enables) when the first user
861
* registers a callback on this domain and once more (disable)
862
* when the last user de-registers its callback.
863
*/
864
pi->states[src_id].meas_notif_enabled = enable;
865
866
return ret;
867
}
868
869
static void *
870
scmi_powercap_fill_custom_report(const struct scmi_protocol_handle *ph,
871
u8 evt_id, ktime_t timestamp,
872
const void *payld, size_t payld_sz,
873
void *report, u32 *src_id)
874
{
875
void *rep = NULL;
876
877
switch (evt_id) {
878
case SCMI_EVENT_POWERCAP_CAP_CHANGED:
879
{
880
const struct scmi_powercap_cap_changed_notify_payld *p = payld;
881
struct scmi_powercap_cap_changed_report *r = report;
882
883
if (sizeof(*p) != payld_sz)
884
break;
885
886
r->timestamp = timestamp;
887
r->agent_id = le32_to_cpu(p->agent_id);
888
r->domain_id = le32_to_cpu(p->domain_id);
889
r->power_cap = le32_to_cpu(p->power_cap);
890
r->pai = le32_to_cpu(p->pai);
891
*src_id = r->domain_id;
892
rep = r;
893
break;
894
}
895
case SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED:
896
{
897
const struct scmi_powercap_meas_changed_notify_payld *p = payld;
898
struct scmi_powercap_meas_changed_report *r = report;
899
900
if (sizeof(*p) != payld_sz)
901
break;
902
903
r->timestamp = timestamp;
904
r->agent_id = le32_to_cpu(p->agent_id);
905
r->domain_id = le32_to_cpu(p->domain_id);
906
r->power = le32_to_cpu(p->power);
907
*src_id = r->domain_id;
908
rep = r;
909
break;
910
}
911
default:
912
break;
913
}
914
915
return rep;
916
}
917
918
static int
919
scmi_powercap_get_num_sources(const struct scmi_protocol_handle *ph)
920
{
921
struct powercap_info *pi = ph->get_priv(ph);
922
923
if (!pi)
924
return -EINVAL;
925
926
return pi->num_domains;
927
}
928
929
static const struct scmi_event powercap_events[] = {
930
{
931
.id = SCMI_EVENT_POWERCAP_CAP_CHANGED,
932
.max_payld_sz =
933
sizeof(struct scmi_powercap_cap_changed_notify_payld),
934
.max_report_sz =
935
sizeof(struct scmi_powercap_cap_changed_report),
936
},
937
{
938
.id = SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED,
939
.max_payld_sz =
940
sizeof(struct scmi_powercap_meas_changed_notify_payld),
941
.max_report_sz =
942
sizeof(struct scmi_powercap_meas_changed_report),
943
},
944
};
945
946
static const struct scmi_event_ops powercap_event_ops = {
947
.is_notify_supported = scmi_powercap_notify_supported,
948
.get_num_sources = scmi_powercap_get_num_sources,
949
.set_notify_enabled = scmi_powercap_set_notify_enabled,
950
.fill_custom_report = scmi_powercap_fill_custom_report,
951
};
952
953
static const struct scmi_protocol_events powercap_protocol_events = {
954
.queue_sz = SCMI_PROTO_QUEUE_SZ,
955
.ops = &powercap_event_ops,
956
.evts = powercap_events,
957
.num_events = ARRAY_SIZE(powercap_events),
958
};
959
960
static int
961
scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
962
{
963
int domain, ret;
964
u32 version;
965
struct powercap_info *pinfo;
966
967
ret = ph->xops->version_get(ph, &version);
968
if (ret)
969
return ret;
970
971
dev_dbg(ph->dev, "Powercap Version %d.%d\n",
972
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
973
974
pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
975
if (!pinfo)
976
return -ENOMEM;
977
978
ret = scmi_powercap_attributes_get(ph, pinfo);
979
if (ret)
980
return ret;
981
982
pinfo->powercaps = devm_kcalloc(ph->dev, pinfo->num_domains,
983
sizeof(*pinfo->powercaps),
984
GFP_KERNEL);
985
if (!pinfo->powercaps)
986
return -ENOMEM;
987
988
pinfo->states = devm_kcalloc(ph->dev, pinfo->num_domains,
989
sizeof(*pinfo->states), GFP_KERNEL);
990
if (!pinfo->states)
991
return -ENOMEM;
992
993
/*
994
* Note that any failure in retrieving any domain attribute leads to
995
* the whole Powercap protocol initialization failure: this way the
996
* reported Powercap domains are all assured, when accessed, to be well
997
* formed and correlated by sane parent-child relationship (if any).
998
*/
999
for (domain = 0; domain < pinfo->num_domains; domain++) {
1000
ret = scmi_powercap_domain_attributes_get(ph, pinfo, domain);
1001
if (ret)
1002
return ret;
1003
1004
if (pinfo->powercaps[domain].fastchannels)
1005
scmi_powercap_domain_init_fc(ph, domain,
1006
&pinfo->powercaps[domain].fc_info);
1007
1008
/* Grab initial state when disable is supported. */
1009
if (PROTOCOL_REV_MAJOR(version) >= 0x2) {
1010
ret = __scmi_powercap_cap_get(ph,
1011
&pinfo->powercaps[domain],
1012
&pinfo->states[domain].last_pcap);
1013
if (ret)
1014
return ret;
1015
1016
pinfo->states[domain].enabled =
1017
!!pinfo->states[domain].last_pcap;
1018
}
1019
}
1020
1021
pinfo->version = version;
1022
return ph->set_priv(ph, pinfo, version);
1023
}
1024
1025
static const struct scmi_protocol scmi_powercap = {
1026
.id = SCMI_PROTOCOL_POWERCAP,
1027
.owner = THIS_MODULE,
1028
.instance_init = &scmi_powercap_protocol_init,
1029
.ops = &powercap_proto_ops,
1030
.events = &powercap_protocol_events,
1031
.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
1032
};
1033
1034
DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(powercap, scmi_powercap)
1035
1036