1 // SPDX-License-Identifier: GPL-2.0
3 * System Control and Management Interface (SCMI) Performance Protocol
5 * Copyright (C) 2018 ARM Ltd.
9 #include <linux/platform_device.h>
10 #include <linux/pm_opp.h>
11 #include <linux/sort.h>
15 enum scmi_performance_protocol_cmd
{
16 PERF_DOMAIN_ATTRIBUTES
= 0x3,
17 PERF_DESCRIBE_LEVELS
= 0x4,
18 PERF_LIMITS_SET
= 0x5,
19 PERF_LIMITS_GET
= 0x6,
22 PERF_NOTIFY_LIMITS
= 0x9,
23 PERF_NOTIFY_LEVEL
= 0xa,
32 struct scmi_msg_resp_perf_attributes
{
35 #define POWER_SCALE_IN_MILLIWATT(x) ((x) & BIT(0))
36 __le32 stats_addr_low
;
37 __le32 stats_addr_high
;
41 struct scmi_msg_resp_perf_domain_attributes
{
43 #define SUPPORTS_SET_LIMITS(x) ((x) & BIT(31))
44 #define SUPPORTS_SET_PERF_LVL(x) ((x) & BIT(30))
45 #define SUPPORTS_PERF_LIMIT_NOTIFY(x) ((x) & BIT(29))
46 #define SUPPORTS_PERF_LEVEL_NOTIFY(x) ((x) & BIT(28))
48 __le32 sustained_freq_khz
;
49 __le32 sustained_perf_level
;
50 u8 name
[SCMI_MAX_STR_SIZE
];
53 struct scmi_msg_perf_describe_levels
{
58 struct scmi_perf_set_limits
{
64 struct scmi_perf_get_limits
{
69 struct scmi_perf_set_level
{
74 struct scmi_perf_notify_level_or_limits
{
79 struct scmi_msg_resp_perf_describe_levels
{
85 __le16 transition_latency_us
;
90 struct perf_dom_info
{
93 bool perf_limit_notify
;
94 bool perf_level_notify
;
96 u32 sustained_freq_khz
;
97 u32 sustained_perf_level
;
99 char name
[SCMI_MAX_STR_SIZE
];
100 struct scmi_opp opp
[MAX_OPPS
];
103 struct scmi_perf_info
{
108 struct perf_dom_info
*dom_info
;
111 static int scmi_perf_attributes_get(const struct scmi_handle
*handle
,
112 struct scmi_perf_info
*pi
)
116 struct scmi_msg_resp_perf_attributes
*attr
;
118 ret
= scmi_xfer_get_init(handle
, PROTOCOL_ATTRIBUTES
,
119 SCMI_PROTOCOL_PERF
, 0, sizeof(*attr
), &t
);
125 ret
= scmi_do_xfer(handle
, t
);
127 u16 flags
= le16_to_cpu(attr
->flags
);
129 pi
->num_domains
= le16_to_cpu(attr
->num_domains
);
130 pi
->power_scale_mw
= POWER_SCALE_IN_MILLIWATT(flags
);
131 pi
->stats_addr
= le32_to_cpu(attr
->stats_addr_low
) |
132 (u64
)le32_to_cpu(attr
->stats_addr_high
) << 32;
133 pi
->stats_size
= le32_to_cpu(attr
->stats_size
);
136 scmi_xfer_put(handle
, t
);
141 scmi_perf_domain_attributes_get(const struct scmi_handle
*handle
, u32 domain
,
142 struct perf_dom_info
*dom_info
)
146 struct scmi_msg_resp_perf_domain_attributes
*attr
;
148 ret
= scmi_xfer_get_init(handle
, PERF_DOMAIN_ATTRIBUTES
,
149 SCMI_PROTOCOL_PERF
, sizeof(domain
),
154 *(__le32
*)t
->tx
.buf
= cpu_to_le32(domain
);
157 ret
= scmi_do_xfer(handle
, t
);
159 u32 flags
= le32_to_cpu(attr
->flags
);
161 dom_info
->set_limits
= SUPPORTS_SET_LIMITS(flags
);
162 dom_info
->set_perf
= SUPPORTS_SET_PERF_LVL(flags
);
163 dom_info
->perf_limit_notify
= SUPPORTS_PERF_LIMIT_NOTIFY(flags
);
164 dom_info
->perf_level_notify
= SUPPORTS_PERF_LEVEL_NOTIFY(flags
);
165 dom_info
->sustained_freq_khz
=
166 le32_to_cpu(attr
->sustained_freq_khz
);
167 dom_info
->sustained_perf_level
=
168 le32_to_cpu(attr
->sustained_perf_level
);
169 if (!dom_info
->sustained_freq_khz
||
170 !dom_info
->sustained_perf_level
)
171 /* CPUFreq converts to kHz, hence default 1000 */
172 dom_info
->mult_factor
= 1000;
174 dom_info
->mult_factor
=
175 (dom_info
->sustained_freq_khz
* 1000) /
176 dom_info
->sustained_perf_level
;
177 strlcpy(dom_info
->name
, attr
->name
, SCMI_MAX_STR_SIZE
);
180 scmi_xfer_put(handle
, t
);
184 static int opp_cmp_func(const void *opp1
, const void *opp2
)
186 const struct scmi_opp
*t1
= opp1
, *t2
= opp2
;
188 return t1
->perf
- t2
->perf
;
192 scmi_perf_describe_levels_get(const struct scmi_handle
*handle
, u32 domain
,
193 struct perf_dom_info
*perf_dom
)
197 u16 num_returned
, num_remaining
;
199 struct scmi_opp
*opp
;
200 struct scmi_msg_perf_describe_levels
*dom_info
;
201 struct scmi_msg_resp_perf_describe_levels
*level_info
;
203 ret
= scmi_xfer_get_init(handle
, PERF_DESCRIBE_LEVELS
,
204 SCMI_PROTOCOL_PERF
, sizeof(*dom_info
), 0, &t
);
208 dom_info
= t
->tx
.buf
;
209 level_info
= t
->rx
.buf
;
212 dom_info
->domain
= cpu_to_le32(domain
);
213 /* Set the number of OPPs to be skipped/already read */
214 dom_info
->level_index
= cpu_to_le32(tot_opp_cnt
);
216 ret
= scmi_do_xfer(handle
, t
);
220 num_returned
= le16_to_cpu(level_info
->num_returned
);
221 num_remaining
= le16_to_cpu(level_info
->num_remaining
);
222 if (tot_opp_cnt
+ num_returned
> MAX_OPPS
) {
223 dev_err(handle
->dev
, "No. of OPPs exceeded MAX_OPPS");
227 opp
= &perf_dom
->opp
[tot_opp_cnt
];
228 for (cnt
= 0; cnt
< num_returned
; cnt
++, opp
++) {
229 opp
->perf
= le32_to_cpu(level_info
->opp
[cnt
].perf_val
);
230 opp
->power
= le32_to_cpu(level_info
->opp
[cnt
].power
);
231 opp
->trans_latency_us
= le16_to_cpu
232 (level_info
->opp
[cnt
].transition_latency_us
);
234 dev_dbg(handle
->dev
, "Level %d Power %d Latency %dus\n",
235 opp
->perf
, opp
->power
, opp
->trans_latency_us
);
238 tot_opp_cnt
+= num_returned
;
240 * check for both returned and remaining to avoid infinite
241 * loop due to buggy firmware
243 } while (num_returned
&& num_remaining
);
245 perf_dom
->opp_count
= tot_opp_cnt
;
246 scmi_xfer_put(handle
, t
);
248 sort(perf_dom
->opp
, tot_opp_cnt
, sizeof(*opp
), opp_cmp_func
, NULL
);
252 static int scmi_perf_limits_set(const struct scmi_handle
*handle
, u32 domain
,
253 u32 max_perf
, u32 min_perf
)
257 struct scmi_perf_set_limits
*limits
;
259 ret
= scmi_xfer_get_init(handle
, PERF_LIMITS_SET
, SCMI_PROTOCOL_PERF
,
260 sizeof(*limits
), 0, &t
);
265 limits
->domain
= cpu_to_le32(domain
);
266 limits
->max_level
= cpu_to_le32(max_perf
);
267 limits
->min_level
= cpu_to_le32(min_perf
);
269 ret
= scmi_do_xfer(handle
, t
);
271 scmi_xfer_put(handle
, t
);
275 static int scmi_perf_limits_get(const struct scmi_handle
*handle
, u32 domain
,
276 u32
*max_perf
, u32
*min_perf
)
280 struct scmi_perf_get_limits
*limits
;
282 ret
= scmi_xfer_get_init(handle
, PERF_LIMITS_GET
, SCMI_PROTOCOL_PERF
,
283 sizeof(__le32
), 0, &t
);
287 *(__le32
*)t
->tx
.buf
= cpu_to_le32(domain
);
289 ret
= scmi_do_xfer(handle
, t
);
293 *max_perf
= le32_to_cpu(limits
->max_level
);
294 *min_perf
= le32_to_cpu(limits
->min_level
);
297 scmi_xfer_put(handle
, t
);
301 static int scmi_perf_level_set(const struct scmi_handle
*handle
, u32 domain
,
302 u32 level
, bool poll
)
306 struct scmi_perf_set_level
*lvl
;
308 ret
= scmi_xfer_get_init(handle
, PERF_LEVEL_SET
, SCMI_PROTOCOL_PERF
,
309 sizeof(*lvl
), 0, &t
);
313 t
->hdr
.poll_completion
= poll
;
315 lvl
->domain
= cpu_to_le32(domain
);
316 lvl
->level
= cpu_to_le32(level
);
318 ret
= scmi_do_xfer(handle
, t
);
320 scmi_xfer_put(handle
, t
);
324 static int scmi_perf_level_get(const struct scmi_handle
*handle
, u32 domain
,
325 u32
*level
, bool poll
)
330 ret
= scmi_xfer_get_init(handle
, PERF_LEVEL_GET
, SCMI_PROTOCOL_PERF
,
331 sizeof(u32
), sizeof(u32
), &t
);
335 t
->hdr
.poll_completion
= poll
;
336 *(__le32
*)t
->tx
.buf
= cpu_to_le32(domain
);
338 ret
= scmi_do_xfer(handle
, t
);
340 *level
= le32_to_cpu(*(__le32
*)t
->rx
.buf
);
342 scmi_xfer_put(handle
, t
);
346 /* Device specific ops */
347 static int scmi_dev_domain_id(struct device
*dev
)
349 struct of_phandle_args clkspec
;
351 if (of_parse_phandle_with_args(dev
->of_node
, "clocks", "#clock-cells",
355 return clkspec
.args
[0];
358 static int scmi_dvfs_device_opps_add(const struct scmi_handle
*handle
,
361 int idx
, ret
, domain
;
363 struct scmi_opp
*opp
;
364 struct perf_dom_info
*dom
;
365 struct scmi_perf_info
*pi
= handle
->perf_priv
;
367 domain
= scmi_dev_domain_id(dev
);
371 dom
= pi
->dom_info
+ domain
;
373 for (opp
= dom
->opp
, idx
= 0; idx
< dom
->opp_count
; idx
++, opp
++) {
374 freq
= opp
->perf
* dom
->mult_factor
;
376 ret
= dev_pm_opp_add(dev
, freq
, 0);
378 dev_warn(dev
, "failed to add opp %luHz\n", freq
);
381 freq
= (--opp
)->perf
* dom
->mult_factor
;
382 dev_pm_opp_remove(dev
, freq
);
390 static int scmi_dvfs_transition_latency_get(const struct scmi_handle
*handle
,
393 struct perf_dom_info
*dom
;
394 struct scmi_perf_info
*pi
= handle
->perf_priv
;
395 int domain
= scmi_dev_domain_id(dev
);
400 dom
= pi
->dom_info
+ domain
;
402 return dom
->opp
[dom
->opp_count
- 1].trans_latency_us
* 1000;
405 static int scmi_dvfs_freq_set(const struct scmi_handle
*handle
, u32 domain
,
406 unsigned long freq
, bool poll
)
408 struct scmi_perf_info
*pi
= handle
->perf_priv
;
409 struct perf_dom_info
*dom
= pi
->dom_info
+ domain
;
411 return scmi_perf_level_set(handle
, domain
, freq
/ dom
->mult_factor
,
415 static int scmi_dvfs_freq_get(const struct scmi_handle
*handle
, u32 domain
,
416 unsigned long *freq
, bool poll
)
420 struct scmi_perf_info
*pi
= handle
->perf_priv
;
421 struct perf_dom_info
*dom
= pi
->dom_info
+ domain
;
423 ret
= scmi_perf_level_get(handle
, domain
, &level
, poll
);
425 *freq
= level
* dom
->mult_factor
;
430 static struct scmi_perf_ops perf_ops
= {
431 .limits_set
= scmi_perf_limits_set
,
432 .limits_get
= scmi_perf_limits_get
,
433 .level_set
= scmi_perf_level_set
,
434 .level_get
= scmi_perf_level_get
,
435 .device_domain_id
= scmi_dev_domain_id
,
436 .transition_latency_get
= scmi_dvfs_transition_latency_get
,
437 .device_opps_add
= scmi_dvfs_device_opps_add
,
438 .freq_set
= scmi_dvfs_freq_set
,
439 .freq_get
= scmi_dvfs_freq_get
,
442 static int scmi_perf_protocol_init(struct scmi_handle
*handle
)
446 struct scmi_perf_info
*pinfo
;
448 scmi_version_get(handle
, SCMI_PROTOCOL_PERF
, &version
);
450 dev_dbg(handle
->dev
, "Performance Version %d.%d\n",
451 PROTOCOL_REV_MAJOR(version
), PROTOCOL_REV_MINOR(version
));
453 pinfo
= devm_kzalloc(handle
->dev
, sizeof(*pinfo
), GFP_KERNEL
);
457 scmi_perf_attributes_get(handle
, pinfo
);
459 pinfo
->dom_info
= devm_kcalloc(handle
->dev
, pinfo
->num_domains
,
460 sizeof(*pinfo
->dom_info
), GFP_KERNEL
);
461 if (!pinfo
->dom_info
)
464 for (domain
= 0; domain
< pinfo
->num_domains
; domain
++) {
465 struct perf_dom_info
*dom
= pinfo
->dom_info
+ domain
;
467 scmi_perf_domain_attributes_get(handle
, domain
, dom
);
468 scmi_perf_describe_levels_get(handle
, domain
, dom
);
471 handle
->perf_ops
= &perf_ops
;
472 handle
->perf_priv
= pinfo
;
477 static int __init
scmi_perf_init(void)
479 return scmi_protocol_register(SCMI_PROTOCOL_PERF
,
480 &scmi_perf_protocol_init
);
482 subsys_initcall(scmi_perf_init
);