Skip to content

Commit 2d0d3a1

Browse files
committed
Merge tag 'scmi-updates-6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux into soc/drivers
Arm SCMI updates for v6.5 Couple of main additions :- 1. Support for multiple SMC/HVC transports for SCMI: Some platforms need to support multiple SCMI instances within a platform(more commonly in a VM). The same SMC/HVC FID is used with all the instances. The platform or the hypervisor needs a way to distinguish among SMC/HVC calls made from different instances. This change adds support for passing shmem channel address as the parameters in the SMC/HVC call. The address is split into 4KB-page and offset for simiplicity. 2. Addition od SCMI v3.2 explicit powercap enable/disable support: SCMI v3.2 specification introduces support to disable powercapping as a whole on the desired zones. This change adds the needed support to the core SCMI powercap protocol, exposing enable/disable protocol operations and then wiring up the new operartions in the related powercap framework helpers. * tag 'scmi-updates-6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux: powercap: arm_scmi: Add support for disabling powercaps on a zone firmware: arm_scmi: Add Powercap protocol enable support firmware: arm_scmi: Refactor the internal powercap get/set helpers firmware: arm_scmi: Augment SMC/HVC to allow optional parameters dt-bindings: firmware: arm,scmi: support for parameter in smc/hvc call Link: https://lore.kernel.org/r/20230612121017.4108104-1-sudeep.holla@arm.com Signed-off-by: Arnd Bergmann <arnd@arndb.de>
2 parents 618d129 + aaffb4c commit 2d0d3a1

6 files changed

Lines changed: 219 additions & 27 deletions

File tree

Documentation/devicetree/bindings/firmware/arm,scmi.yaml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ properties:
3434
- description: SCMI compliant firmware with ARM SMC/HVC transport
3535
items:
3636
- const: arm,scmi-smc
37+
- description: SCMI compliant firmware with ARM SMC/HVC transport
38+
with shmem address(4KB-page, offset) as parameters
39+
items:
40+
- const: arm,scmi-smc-param
3741
- description: SCMI compliant firmware with SCMI Virtio transport.
3842
The virtio transport only supports a single device.
3943
items:
@@ -299,7 +303,9 @@ else:
299303
properties:
300304
compatible:
301305
contains:
302-
const: arm,scmi-smc
306+
enum:
307+
- arm,scmi-smc
308+
- arm,scmi-smc-param
303309
then:
304310
required:
305311
- arm,smc-id

drivers/firmware/arm_scmi/driver.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2914,6 +2914,7 @@ static const struct of_device_id scmi_of_match[] = {
29142914
#endif
29152915
#ifdef CONFIG_ARM_SCMI_TRANSPORT_SMC
29162916
{ .compatible = "arm,scmi-smc", .data = &scmi_smc_desc},
2917+
{ .compatible = "arm,scmi-smc-param", .data = &scmi_smc_desc},
29172918
#endif
29182919
#ifdef CONFIG_ARM_SCMI_TRANSPORT_VIRTIO
29192920
{ .compatible = "arm,scmi-virtio", .data = &scmi_virtio_desc},

drivers/firmware/arm_scmi/powercap.c

Lines changed: 148 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ struct scmi_powercap_meas_changed_notify_payld {
108108
};
109109

110110
struct scmi_powercap_state {
111+
bool enabled;
112+
u32 last_pcap;
111113
bool meas_notif_enabled;
112114
u64 thresholds;
113115
#define THRESH_LOW(p, id) \
@@ -313,24 +315,33 @@ static int scmi_powercap_xfer_cap_get(const struct scmi_protocol_handle *ph,
313315
return ret;
314316
}
315317

316-
static int scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
317-
u32 domain_id, u32 *power_cap)
318+
static int __scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
319+
const struct scmi_powercap_info *dom,
320+
u32 *power_cap)
318321
{
319-
struct scmi_powercap_info *dom;
320-
struct powercap_info *pi = ph->get_priv(ph);
321-
322-
if (!power_cap || domain_id >= pi->num_domains)
323-
return -EINVAL;
324-
325-
dom = pi->powercaps + domain_id;
326322
if (dom->fc_info && dom->fc_info[POWERCAP_FC_CAP].get_addr) {
327323
*power_cap = ioread32(dom->fc_info[POWERCAP_FC_CAP].get_addr);
328324
trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_GET,
329-
domain_id, *power_cap, 0);
325+
dom->id, *power_cap, 0);
330326
return 0;
331327
}
332328

333-
return scmi_powercap_xfer_cap_get(ph, domain_id, power_cap);
329+
return scmi_powercap_xfer_cap_get(ph, dom->id, power_cap);
330+
}
331+
332+
static int scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
333+
u32 domain_id, u32 *power_cap)
334+
{
335+
const struct scmi_powercap_info *dom;
336+
337+
if (!power_cap)
338+
return -EINVAL;
339+
340+
dom = scmi_powercap_dom_info_get(ph, domain_id);
341+
if (!dom)
342+
return -EINVAL;
343+
344+
return __scmi_powercap_cap_get(ph, dom, power_cap);
334345
}
335346

336347
static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph,
@@ -375,17 +386,20 @@ static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph,
375386
return ret;
376387
}
377388

378-
static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
379-
u32 domain_id, u32 power_cap,
380-
bool ignore_dresp)
389+
static int __scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
390+
struct powercap_info *pi, u32 domain_id,
391+
u32 power_cap, bool ignore_dresp)
381392
{
393+
int ret = -EINVAL;
382394
const struct scmi_powercap_info *pc;
383395

384396
pc = scmi_powercap_dom_info_get(ph, domain_id);
385-
if (!pc || !pc->powercap_cap_config || !power_cap ||
386-
power_cap < pc->min_power_cap ||
387-
power_cap > pc->max_power_cap)
388-
return -EINVAL;
397+
if (!pc || !pc->powercap_cap_config)
398+
return ret;
399+
400+
if (power_cap &&
401+
(power_cap < pc->min_power_cap || power_cap > pc->max_power_cap))
402+
return ret;
389403

390404
if (pc->fc_info && pc->fc_info[POWERCAP_FC_CAP].set_addr) {
391405
struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_CAP];
@@ -394,10 +408,41 @@ static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
394408
ph->hops->fastchannel_db_ring(fci->set_db);
395409
trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_SET,
396410
domain_id, power_cap, 0);
411+
ret = 0;
412+
} else {
413+
ret = scmi_powercap_xfer_cap_set(ph, pc, power_cap,
414+
ignore_dresp);
415+
}
416+
417+
/* Save the last explicitly set non-zero powercap value */
418+
if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 && !ret && power_cap)
419+
pi->states[domain_id].last_pcap = power_cap;
420+
421+
return ret;
422+
}
423+
424+
static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
425+
u32 domain_id, u32 power_cap,
426+
bool ignore_dresp)
427+
{
428+
struct powercap_info *pi = ph->get_priv(ph);
429+
430+
/*
431+
* Disallow zero as a possible explicitly requested powercap:
432+
* there are enable/disable operations for this.
433+
*/
434+
if (!power_cap)
435+
return -EINVAL;
436+
437+
/* Just log the last set request if acting on a disabled domain */
438+
if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 &&
439+
!pi->states[domain_id].enabled) {
440+
pi->states[domain_id].last_pcap = power_cap;
397441
return 0;
398442
}
399443

400-
return scmi_powercap_xfer_cap_set(ph, pc, power_cap, ignore_dresp);
444+
return __scmi_powercap_cap_set(ph, pi, domain_id,
445+
power_cap, ignore_dresp);
401446
}
402447

403448
static int scmi_powercap_xfer_pai_get(const struct scmi_protocol_handle *ph,
@@ -564,11 +609,78 @@ scmi_powercap_measurements_threshold_set(const struct scmi_protocol_handle *ph,
564609
return ret;
565610
}
566611

612+
static int scmi_powercap_cap_enable_set(const struct scmi_protocol_handle *ph,
613+
u32 domain_id, bool enable)
614+
{
615+
int ret;
616+
u32 power_cap;
617+
struct powercap_info *pi = ph->get_priv(ph);
618+
619+
if (PROTOCOL_REV_MAJOR(pi->version) < 0x2)
620+
return -EINVAL;
621+
622+
if (enable == pi->states[domain_id].enabled)
623+
return 0;
624+
625+
if (enable) {
626+
/* Cannot enable with a zero powercap. */
627+
if (!pi->states[domain_id].last_pcap)
628+
return -EINVAL;
629+
630+
ret = __scmi_powercap_cap_set(ph, pi, domain_id,
631+
pi->states[domain_id].last_pcap,
632+
true);
633+
} else {
634+
ret = __scmi_powercap_cap_set(ph, pi, domain_id, 0, true);
635+
}
636+
637+
if (ret)
638+
return ret;
639+
640+
/*
641+
* Update our internal state to reflect final platform state: the SCMI
642+
* server could have ignored a disable request and kept enforcing some
643+
* powercap limit requested by other agents.
644+
*/
645+
ret = scmi_powercap_cap_get(ph, domain_id, &power_cap);
646+
if (!ret)
647+
pi->states[domain_id].enabled = !!power_cap;
648+
649+
return ret;
650+
}
651+
652+
static int scmi_powercap_cap_enable_get(const struct scmi_protocol_handle *ph,
653+
u32 domain_id, bool *enable)
654+
{
655+
int ret;
656+
u32 power_cap;
657+
struct powercap_info *pi = ph->get_priv(ph);
658+
659+
*enable = true;
660+
if (PROTOCOL_REV_MAJOR(pi->version) < 0x2)
661+
return 0;
662+
663+
/*
664+
* Report always real platform state; platform could have ignored
665+
* a previous disable request. Default true on any error.
666+
*/
667+
ret = scmi_powercap_cap_get(ph, domain_id, &power_cap);
668+
if (!ret)
669+
*enable = !!power_cap;
670+
671+
/* Update internal state with current real platform state */
672+
pi->states[domain_id].enabled = *enable;
673+
674+
return 0;
675+
}
676+
567677
static const struct scmi_powercap_proto_ops powercap_proto_ops = {
568678
.num_domains_get = scmi_powercap_num_domains_get,
569679
.info_get = scmi_powercap_dom_info_get,
570680
.cap_get = scmi_powercap_cap_get,
571681
.cap_set = scmi_powercap_cap_set,
682+
.cap_enable_set = scmi_powercap_cap_enable_set,
683+
.cap_enable_get = scmi_powercap_cap_enable_get,
572684
.pai_get = scmi_powercap_pai_get,
573685
.pai_set = scmi_powercap_pai_set,
574686
.measurements_get = scmi_powercap_measurements_get,
@@ -829,6 +941,11 @@ scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
829941
if (!pinfo->powercaps)
830942
return -ENOMEM;
831943

944+
pinfo->states = devm_kcalloc(ph->dev, pinfo->num_domains,
945+
sizeof(*pinfo->states), GFP_KERNEL);
946+
if (!pinfo->states)
947+
return -ENOMEM;
948+
832949
/*
833950
* Note that any failure in retrieving any domain attribute leads to
834951
* the whole Powercap protocol initialization failure: this way the
@@ -843,15 +960,21 @@ scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
843960
if (pinfo->powercaps[domain].fastchannels)
844961
scmi_powercap_domain_init_fc(ph, domain,
845962
&pinfo->powercaps[domain].fc_info);
846-
}
847963

848-
pinfo->states = devm_kcalloc(ph->dev, pinfo->num_domains,
849-
sizeof(*pinfo->states), GFP_KERNEL);
850-
if (!pinfo->states)
851-
return -ENOMEM;
964+
/* Grab initial state when disable is supported. */
965+
if (PROTOCOL_REV_MAJOR(version) >= 0x2) {
966+
ret = __scmi_powercap_cap_get(ph,
967+
&pinfo->powercaps[domain],
968+
&pinfo->states[domain].last_pcap);
969+
if (ret)
970+
return ret;
852971

853-
pinfo->version = version;
972+
pinfo->states[domain].enabled =
973+
!!pinfo->states[domain].last_pcap;
974+
}
975+
}
854976

977+
pinfo->version = version;
855978
return ph->set_priv(ph, pinfo);
856979
}
857980

drivers/firmware/arm_scmi/smc.c

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,23 @@
2020

2121
#include "common.h"
2222

23+
/*
24+
* The shmem address is split into 4K page and offset.
25+
* This is to make sure the parameters fit in 32bit arguments of the
26+
* smc/hvc call to keep it uniform across smc32/smc64 conventions.
27+
* This however limits the shmem address to 44 bit.
28+
*
29+
* These optional parameters can be used to distinguish among multiple
30+
* scmi instances that are using the same smc-id.
31+
* The page parameter is passed in r1/x1/w1 register and the offset parameter
32+
* is passed in r2/x2/w2 register.
33+
*/
34+
35+
#define SHMEM_SIZE (SZ_4K)
36+
#define SHMEM_SHIFT 12
37+
#define SHMEM_PAGE(x) (_UL((x) >> SHMEM_SHIFT))
38+
#define SHMEM_OFFSET(x) ((x) & (SHMEM_SIZE - 1))
39+
2340
/**
2441
* struct scmi_smc - Structure representing a SCMI smc transport
2542
*
@@ -30,6 +47,8 @@
3047
* @inflight: Atomic flag to protect access to Tx/Rx shared memory area.
3148
* Used when operating in atomic mode.
3249
* @func_id: smc/hvc call function id
50+
* @param_page: 4K page number of the shmem channel
51+
* @param_offset: Offset within the 4K page of the shmem channel
3352
*/
3453

3554
struct scmi_smc {
@@ -40,6 +59,8 @@ struct scmi_smc {
4059
#define INFLIGHT_NONE MSG_TOKEN_MAX
4160
atomic_t inflight;
4261
u32 func_id;
62+
u32 param_page;
63+
u32 param_offset;
4364
};
4465

4566
static irqreturn_t smc_msg_done_isr(int irq, void *data)
@@ -137,6 +158,10 @@ static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
137158
if (ret < 0)
138159
return ret;
139160

161+
if (of_device_is_compatible(dev->of_node, "arm,scmi-smc-param")) {
162+
scmi_info->param_page = SHMEM_PAGE(res.start);
163+
scmi_info->param_offset = SHMEM_OFFSET(res.start);
164+
}
140165
/*
141166
* If there is an interrupt named "a2p", then the service and
142167
* completion of a message is signaled by an interrupt rather than by
@@ -179,6 +204,8 @@ static int smc_send_message(struct scmi_chan_info *cinfo,
179204
{
180205
struct scmi_smc *scmi_info = cinfo->transport_info;
181206
struct arm_smccc_res res;
207+
unsigned long page = scmi_info->param_page;
208+
unsigned long offset = scmi_info->param_offset;
182209

183210
/*
184211
* Channel will be released only once response has been
@@ -188,7 +215,8 @@ static int smc_send_message(struct scmi_chan_info *cinfo,
188215

189216
shmem_tx_prepare(scmi_info->shmem, xfer, cinfo);
190217

191-
arm_smccc_1_1_invoke(scmi_info->func_id, 0, 0, 0, 0, 0, 0, 0, &res);
218+
arm_smccc_1_1_invoke(scmi_info->func_id, page, offset, 0, 0, 0, 0, 0,
219+
&res);
192220

193221
/* Only SMCCC_RET_NOT_SUPPORTED is valid error code */
194222
if (res.a0) {

drivers/powercap/arm_scmi_powercap.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,26 @@ static int scmi_powercap_get_power_uw(struct powercap_zone *pz,
7070
return 0;
7171
}
7272

73+
static int scmi_powercap_zone_enable_set(struct powercap_zone *pz, bool mode)
74+
{
75+
struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz);
76+
77+
return powercap_ops->cap_enable_set(spz->ph, spz->info->id, mode);
78+
}
79+
80+
static int scmi_powercap_zone_enable_get(struct powercap_zone *pz, bool *mode)
81+
{
82+
struct scmi_powercap_zone *spz = to_scmi_powercap_zone(pz);
83+
84+
return powercap_ops->cap_enable_get(spz->ph, spz->info->id, mode);
85+
}
86+
7387
static const struct powercap_zone_ops zone_ops = {
7488
.get_max_power_range_uw = scmi_powercap_get_max_power_range_uw,
7589
.get_power_uw = scmi_powercap_get_power_uw,
7690
.release = scmi_powercap_zone_release,
91+
.set_enable = scmi_powercap_zone_enable_set,
92+
.get_enable = scmi_powercap_zone_enable_get,
7793
};
7894

7995
static void scmi_powercap_normalize_cap(const struct scmi_powercap_zone *spz,

0 commit comments

Comments
 (0)