1*0316f99cSCristian Marussi // SPDX-License-Identifier: GPL-2.0 2*0316f99cSCristian Marussi /* 3*0316f99cSCristian Marussi * System Control and Management Interface (SCMI) Powercap Protocol 4*0316f99cSCristian Marussi * 5*0316f99cSCristian Marussi * Copyright (C) 2022 ARM Ltd. 6*0316f99cSCristian Marussi */ 7*0316f99cSCristian Marussi 8*0316f99cSCristian Marussi #define pr_fmt(fmt) "SCMI Notifications POWERCAP - " fmt 9*0316f99cSCristian Marussi 10*0316f99cSCristian Marussi #include <linux/bitfield.h> 11*0316f99cSCristian Marussi #include <linux/module.h> 12*0316f99cSCristian Marussi #include <linux/scmi_protocol.h> 13*0316f99cSCristian Marussi 14*0316f99cSCristian Marussi #include "protocols.h" 15*0316f99cSCristian Marussi #include "notify.h" 16*0316f99cSCristian Marussi 17*0316f99cSCristian Marussi enum scmi_powercap_protocol_cmd { 18*0316f99cSCristian Marussi POWERCAP_DOMAIN_ATTRIBUTES = 0x3, 19*0316f99cSCristian Marussi POWERCAP_CAP_GET = 0x4, 20*0316f99cSCristian Marussi POWERCAP_CAP_SET = 0x5, 21*0316f99cSCristian Marussi POWERCAP_PAI_GET = 0x6, 22*0316f99cSCristian Marussi POWERCAP_PAI_SET = 0x7, 23*0316f99cSCristian Marussi POWERCAP_DOMAIN_NAME_GET = 0x8, 24*0316f99cSCristian Marussi POWERCAP_MEASUREMENTS_GET = 0x9, 25*0316f99cSCristian Marussi POWERCAP_CAP_NOTIFY = 0xa, 26*0316f99cSCristian Marussi POWERCAP_MEASUREMENTS_NOTIFY = 0xb, 27*0316f99cSCristian Marussi POWERCAP_DESCRIBE_FASTCHANNEL = 0xc, 28*0316f99cSCristian Marussi }; 29*0316f99cSCristian Marussi 30*0316f99cSCristian Marussi struct scmi_msg_resp_powercap_domain_attributes { 31*0316f99cSCristian Marussi __le32 attributes; 32*0316f99cSCristian Marussi #define SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(x) ((x) & BIT(31)) 33*0316f99cSCristian Marussi #define SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(x) ((x) & BIT(30)) 34*0316f99cSCristian Marussi #define SUPPORTS_ASYNC_POWERCAP_CAP_SET(x) ((x) & BIT(29)) 35*0316f99cSCristian Marussi #define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(28)) 36*0316f99cSCristian Marussi #define SUPPORTS_POWERCAP_CAP_CONFIGURATION(x) ((x) & BIT(27)) 37*0316f99cSCristian Marussi #define SUPPORTS_POWERCAP_MONITORING(x) ((x) & BIT(26)) 38*0316f99cSCristian Marussi #define SUPPORTS_POWERCAP_PAI_CONFIGURATION(x) ((x) & BIT(25)) 39*0316f99cSCristian Marussi #define POWERCAP_POWER_UNIT(x) \ 40*0316f99cSCristian Marussi (FIELD_GET(GENMASK(24, 23), (x))) 41*0316f99cSCristian Marussi #define SUPPORTS_POWER_UNITS_MW(x) \ 42*0316f99cSCristian Marussi (POWERCAP_POWER_UNIT(x) == 0x2) 43*0316f99cSCristian Marussi #define SUPPORTS_POWER_UNITS_UW(x) \ 44*0316f99cSCristian Marussi (POWERCAP_POWER_UNIT(x) == 0x1) 45*0316f99cSCristian Marussi u8 name[SCMI_SHORT_NAME_MAX_SIZE]; 46*0316f99cSCristian Marussi __le32 min_pai; 47*0316f99cSCristian Marussi __le32 max_pai; 48*0316f99cSCristian Marussi __le32 pai_step; 49*0316f99cSCristian Marussi __le32 min_power_cap; 50*0316f99cSCristian Marussi __le32 max_power_cap; 51*0316f99cSCristian Marussi __le32 power_cap_step; 52*0316f99cSCristian Marussi __le32 sustainable_power; 53*0316f99cSCristian Marussi __le32 accuracy; 54*0316f99cSCristian Marussi __le32 parent_id; 55*0316f99cSCristian Marussi }; 56*0316f99cSCristian Marussi 57*0316f99cSCristian Marussi struct scmi_msg_powercap_set_cap_or_pai { 58*0316f99cSCristian Marussi __le32 domain; 59*0316f99cSCristian Marussi __le32 flags; 60*0316f99cSCristian Marussi #define CAP_SET_ASYNC BIT(1) 61*0316f99cSCristian Marussi #define CAP_SET_IGNORE_DRESP BIT(0) 62*0316f99cSCristian Marussi __le32 value; 63*0316f99cSCristian Marussi }; 64*0316f99cSCristian Marussi 65*0316f99cSCristian Marussi struct scmi_msg_resp_powercap_cap_set_complete { 66*0316f99cSCristian Marussi __le32 domain; 67*0316f99cSCristian Marussi __le32 power_cap; 68*0316f99cSCristian Marussi }; 69*0316f99cSCristian Marussi 70*0316f99cSCristian Marussi struct scmi_msg_resp_powercap_meas_get { 71*0316f99cSCristian Marussi __le32 power; 72*0316f99cSCristian Marussi __le32 pai; 73*0316f99cSCristian Marussi }; 74*0316f99cSCristian Marussi 75*0316f99cSCristian Marussi struct scmi_msg_powercap_notify_cap { 76*0316f99cSCristian Marussi __le32 domain; 77*0316f99cSCristian Marussi __le32 notify_enable; 78*0316f99cSCristian Marussi }; 79*0316f99cSCristian Marussi 80*0316f99cSCristian Marussi struct scmi_msg_powercap_notify_thresh { 81*0316f99cSCristian Marussi __le32 domain; 82*0316f99cSCristian Marussi __le32 notify_enable; 83*0316f99cSCristian Marussi __le32 power_thresh_low; 84*0316f99cSCristian Marussi __le32 power_thresh_high; 85*0316f99cSCristian Marussi }; 86*0316f99cSCristian Marussi 87*0316f99cSCristian Marussi struct scmi_powercap_cap_changed_notify_payld { 88*0316f99cSCristian Marussi __le32 agent_id; 89*0316f99cSCristian Marussi __le32 domain_id; 90*0316f99cSCristian Marussi __le32 power_cap; 91*0316f99cSCristian Marussi __le32 pai; 92*0316f99cSCristian Marussi }; 93*0316f99cSCristian Marussi 94*0316f99cSCristian Marussi struct scmi_powercap_meas_changed_notify_payld { 95*0316f99cSCristian Marussi __le32 agent_id; 96*0316f99cSCristian Marussi __le32 domain_id; 97*0316f99cSCristian Marussi __le32 power; 98*0316f99cSCristian Marussi }; 99*0316f99cSCristian Marussi 100*0316f99cSCristian Marussi struct scmi_powercap_state { 101*0316f99cSCristian Marussi bool meas_notif_enabled; 102*0316f99cSCristian Marussi u64 thresholds; 103*0316f99cSCristian Marussi #define THRESH_LOW(p, id) \ 104*0316f99cSCristian Marussi (lower_32_bits((p)->states[(id)].thresholds)) 105*0316f99cSCristian Marussi #define THRESH_HIGH(p, id) \ 106*0316f99cSCristian Marussi (upper_32_bits((p)->states[(id)].thresholds)) 107*0316f99cSCristian Marussi }; 108*0316f99cSCristian Marussi 109*0316f99cSCristian Marussi struct powercap_info { 110*0316f99cSCristian Marussi u32 version; 111*0316f99cSCristian Marussi int num_domains; 112*0316f99cSCristian Marussi struct scmi_powercap_state *states; 113*0316f99cSCristian Marussi struct scmi_powercap_info *powercaps; 114*0316f99cSCristian Marussi }; 115*0316f99cSCristian Marussi 116*0316f99cSCristian Marussi static enum scmi_powercap_protocol_cmd evt_2_cmd[] = { 117*0316f99cSCristian Marussi POWERCAP_CAP_NOTIFY, 118*0316f99cSCristian Marussi POWERCAP_MEASUREMENTS_NOTIFY, 119*0316f99cSCristian Marussi }; 120*0316f99cSCristian Marussi 121*0316f99cSCristian Marussi static int scmi_powercap_notify(const struct scmi_protocol_handle *ph, 122*0316f99cSCristian Marussi u32 domain, int message_id, bool enable); 123*0316f99cSCristian Marussi 124*0316f99cSCristian Marussi static int 125*0316f99cSCristian Marussi scmi_powercap_attributes_get(const struct scmi_protocol_handle *ph, 126*0316f99cSCristian Marussi struct powercap_info *pi) 127*0316f99cSCristian Marussi { 128*0316f99cSCristian Marussi int ret; 129*0316f99cSCristian Marussi struct scmi_xfer *t; 130*0316f99cSCristian Marussi 131*0316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0, 132*0316f99cSCristian Marussi sizeof(u32), &t); 133*0316f99cSCristian Marussi if (ret) 134*0316f99cSCristian Marussi return ret; 135*0316f99cSCristian Marussi 136*0316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 137*0316f99cSCristian Marussi if (!ret) { 138*0316f99cSCristian Marussi u32 attributes; 139*0316f99cSCristian Marussi 140*0316f99cSCristian Marussi attributes = get_unaligned_le32(t->rx.buf); 141*0316f99cSCristian Marussi pi->num_domains = FIELD_GET(GENMASK(15, 0), attributes); 142*0316f99cSCristian Marussi } 143*0316f99cSCristian Marussi 144*0316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 145*0316f99cSCristian Marussi return ret; 146*0316f99cSCristian Marussi } 147*0316f99cSCristian Marussi 148*0316f99cSCristian Marussi static inline int 149*0316f99cSCristian Marussi scmi_powercap_validate(unsigned int min_val, unsigned int max_val, 150*0316f99cSCristian Marussi unsigned int step_val, bool configurable) 151*0316f99cSCristian Marussi { 152*0316f99cSCristian Marussi if (!min_val || !max_val) 153*0316f99cSCristian Marussi return -EPROTO; 154*0316f99cSCristian Marussi 155*0316f99cSCristian Marussi if ((configurable && min_val == max_val) || 156*0316f99cSCristian Marussi (!configurable && min_val != max_val)) 157*0316f99cSCristian Marussi return -EPROTO; 158*0316f99cSCristian Marussi 159*0316f99cSCristian Marussi if (min_val != max_val && !step_val) 160*0316f99cSCristian Marussi return -EPROTO; 161*0316f99cSCristian Marussi 162*0316f99cSCristian Marussi return 0; 163*0316f99cSCristian Marussi } 164*0316f99cSCristian Marussi 165*0316f99cSCristian Marussi static int 166*0316f99cSCristian Marussi scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph, 167*0316f99cSCristian Marussi struct powercap_info *pinfo, u32 domain) 168*0316f99cSCristian Marussi { 169*0316f99cSCristian Marussi int ret; 170*0316f99cSCristian Marussi u32 flags; 171*0316f99cSCristian Marussi struct scmi_xfer *t; 172*0316f99cSCristian Marussi struct scmi_powercap_info *dom_info = pinfo->powercaps + domain; 173*0316f99cSCristian Marussi struct scmi_msg_resp_powercap_domain_attributes *resp; 174*0316f99cSCristian Marussi 175*0316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_DOMAIN_ATTRIBUTES, 176*0316f99cSCristian Marussi sizeof(domain), sizeof(*resp), &t); 177*0316f99cSCristian Marussi if (ret) 178*0316f99cSCristian Marussi return ret; 179*0316f99cSCristian Marussi 180*0316f99cSCristian Marussi put_unaligned_le32(domain, t->tx.buf); 181*0316f99cSCristian Marussi resp = t->rx.buf; 182*0316f99cSCristian Marussi 183*0316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 184*0316f99cSCristian Marussi if (!ret) { 185*0316f99cSCristian Marussi flags = le32_to_cpu(resp->attributes); 186*0316f99cSCristian Marussi 187*0316f99cSCristian Marussi dom_info->id = domain; 188*0316f99cSCristian Marussi dom_info->notify_powercap_cap_change = 189*0316f99cSCristian Marussi SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags); 190*0316f99cSCristian Marussi dom_info->notify_powercap_measurement_change = 191*0316f99cSCristian Marussi SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags); 192*0316f99cSCristian Marussi dom_info->async_powercap_cap_set = 193*0316f99cSCristian Marussi SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags); 194*0316f99cSCristian Marussi dom_info->powercap_cap_config = 195*0316f99cSCristian Marussi SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags); 196*0316f99cSCristian Marussi dom_info->powercap_monitoring = 197*0316f99cSCristian Marussi SUPPORTS_POWERCAP_MONITORING(flags); 198*0316f99cSCristian Marussi dom_info->powercap_pai_config = 199*0316f99cSCristian Marussi SUPPORTS_POWERCAP_PAI_CONFIGURATION(flags); 200*0316f99cSCristian Marussi dom_info->powercap_scale_mw = 201*0316f99cSCristian Marussi SUPPORTS_POWER_UNITS_MW(flags); 202*0316f99cSCristian Marussi dom_info->powercap_scale_uw = 203*0316f99cSCristian Marussi SUPPORTS_POWER_UNITS_UW(flags); 204*0316f99cSCristian Marussi 205*0316f99cSCristian Marussi strscpy(dom_info->name, resp->name, SCMI_SHORT_NAME_MAX_SIZE); 206*0316f99cSCristian Marussi 207*0316f99cSCristian Marussi dom_info->min_pai = le32_to_cpu(resp->min_pai); 208*0316f99cSCristian Marussi dom_info->max_pai = le32_to_cpu(resp->max_pai); 209*0316f99cSCristian Marussi dom_info->pai_step = le32_to_cpu(resp->pai_step); 210*0316f99cSCristian Marussi ret = scmi_powercap_validate(dom_info->min_pai, 211*0316f99cSCristian Marussi dom_info->max_pai, 212*0316f99cSCristian Marussi dom_info->pai_step, 213*0316f99cSCristian Marussi dom_info->powercap_pai_config); 214*0316f99cSCristian Marussi if (ret) { 215*0316f99cSCristian Marussi dev_err(ph->dev, 216*0316f99cSCristian Marussi "Platform reported inconsistent PAI config for domain %d - %s\n", 217*0316f99cSCristian Marussi dom_info->id, dom_info->name); 218*0316f99cSCristian Marussi goto clean; 219*0316f99cSCristian Marussi } 220*0316f99cSCristian Marussi 221*0316f99cSCristian Marussi dom_info->min_power_cap = le32_to_cpu(resp->min_power_cap); 222*0316f99cSCristian Marussi dom_info->max_power_cap = le32_to_cpu(resp->max_power_cap); 223*0316f99cSCristian Marussi dom_info->power_cap_step = le32_to_cpu(resp->power_cap_step); 224*0316f99cSCristian Marussi ret = scmi_powercap_validate(dom_info->min_power_cap, 225*0316f99cSCristian Marussi dom_info->max_power_cap, 226*0316f99cSCristian Marussi dom_info->power_cap_step, 227*0316f99cSCristian Marussi dom_info->powercap_cap_config); 228*0316f99cSCristian Marussi if (ret) { 229*0316f99cSCristian Marussi dev_err(ph->dev, 230*0316f99cSCristian Marussi "Platform reported inconsistent CAP config for domain %d - %s\n", 231*0316f99cSCristian Marussi dom_info->id, dom_info->name); 232*0316f99cSCristian Marussi goto clean; 233*0316f99cSCristian Marussi } 234*0316f99cSCristian Marussi 235*0316f99cSCristian Marussi dom_info->sustainable_power = 236*0316f99cSCristian Marussi le32_to_cpu(resp->sustainable_power); 237*0316f99cSCristian Marussi dom_info->accuracy = le32_to_cpu(resp->accuracy); 238*0316f99cSCristian Marussi 239*0316f99cSCristian Marussi dom_info->parent_id = le32_to_cpu(resp->parent_id); 240*0316f99cSCristian Marussi if (dom_info->parent_id != SCMI_POWERCAP_ROOT_ZONE_ID && 241*0316f99cSCristian Marussi (dom_info->parent_id >= pinfo->num_domains || 242*0316f99cSCristian Marussi dom_info->parent_id == dom_info->id)) { 243*0316f99cSCristian Marussi dev_err(ph->dev, 244*0316f99cSCristian Marussi "Platform reported inconsistent parent ID for domain %d - %s\n", 245*0316f99cSCristian Marussi dom_info->id, dom_info->name); 246*0316f99cSCristian Marussi ret = -ENODEV; 247*0316f99cSCristian Marussi } 248*0316f99cSCristian Marussi } 249*0316f99cSCristian Marussi 250*0316f99cSCristian Marussi clean: 251*0316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 252*0316f99cSCristian Marussi 253*0316f99cSCristian Marussi /* 254*0316f99cSCristian Marussi * If supported overwrite short name with the extended one; 255*0316f99cSCristian Marussi * on error just carry on and use already provided short name. 256*0316f99cSCristian Marussi */ 257*0316f99cSCristian Marussi if (!ret && SUPPORTS_EXTENDED_NAMES(flags)) 258*0316f99cSCristian Marussi ph->hops->extended_name_get(ph, POWERCAP_DOMAIN_NAME_GET, 259*0316f99cSCristian Marussi domain, dom_info->name, 260*0316f99cSCristian Marussi SCMI_MAX_STR_SIZE); 261*0316f99cSCristian Marussi 262*0316f99cSCristian Marussi return ret; 263*0316f99cSCristian Marussi } 264*0316f99cSCristian Marussi 265*0316f99cSCristian Marussi static int scmi_powercap_num_domains_get(const struct scmi_protocol_handle *ph) 266*0316f99cSCristian Marussi { 267*0316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 268*0316f99cSCristian Marussi 269*0316f99cSCristian Marussi return pi->num_domains; 270*0316f99cSCristian Marussi } 271*0316f99cSCristian Marussi 272*0316f99cSCristian Marussi static const struct scmi_powercap_info * 273*0316f99cSCristian Marussi scmi_powercap_dom_info_get(const struct scmi_protocol_handle *ph, u32 domain_id) 274*0316f99cSCristian Marussi { 275*0316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 276*0316f99cSCristian Marussi 277*0316f99cSCristian Marussi if (domain_id >= pi->num_domains) 278*0316f99cSCristian Marussi return NULL; 279*0316f99cSCristian Marussi 280*0316f99cSCristian Marussi return pi->powercaps + domain_id; 281*0316f99cSCristian Marussi } 282*0316f99cSCristian Marussi 283*0316f99cSCristian Marussi static int scmi_powercap_cap_get(const struct scmi_protocol_handle *ph, 284*0316f99cSCristian Marussi u32 domain_id, u32 *power_cap) 285*0316f99cSCristian Marussi { 286*0316f99cSCristian Marussi int ret; 287*0316f99cSCristian Marussi struct scmi_xfer *t; 288*0316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 289*0316f99cSCristian Marussi 290*0316f99cSCristian Marussi if (!power_cap || domain_id >= pi->num_domains) 291*0316f99cSCristian Marussi return -EINVAL; 292*0316f99cSCristian Marussi 293*0316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_GET, sizeof(u32), 294*0316f99cSCristian Marussi sizeof(u32), &t); 295*0316f99cSCristian Marussi if (ret) 296*0316f99cSCristian Marussi return ret; 297*0316f99cSCristian Marussi 298*0316f99cSCristian Marussi put_unaligned_le32(domain_id, t->tx.buf); 299*0316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 300*0316f99cSCristian Marussi if (!ret) 301*0316f99cSCristian Marussi *power_cap = get_unaligned_le32(t->rx.buf); 302*0316f99cSCristian Marussi 303*0316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 304*0316f99cSCristian Marussi 305*0316f99cSCristian Marussi return ret; 306*0316f99cSCristian Marussi } 307*0316f99cSCristian Marussi 308*0316f99cSCristian Marussi static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph, 309*0316f99cSCristian Marussi u32 domain_id, u32 power_cap, 310*0316f99cSCristian Marussi bool ignore_dresp) 311*0316f99cSCristian Marussi { 312*0316f99cSCristian Marussi int ret; 313*0316f99cSCristian Marussi struct scmi_xfer *t; 314*0316f99cSCristian Marussi struct scmi_msg_powercap_set_cap_or_pai *msg; 315*0316f99cSCristian Marussi const struct scmi_powercap_info *pc; 316*0316f99cSCristian Marussi 317*0316f99cSCristian Marussi pc = scmi_powercap_dom_info_get(ph, domain_id); 318*0316f99cSCristian Marussi if (!pc || !pc->powercap_cap_config || !power_cap || 319*0316f99cSCristian Marussi power_cap < pc->min_power_cap || 320*0316f99cSCristian Marussi power_cap > pc->max_power_cap) 321*0316f99cSCristian Marussi return -EINVAL; 322*0316f99cSCristian Marussi 323*0316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_SET, 324*0316f99cSCristian Marussi sizeof(*msg), 0, &t); 325*0316f99cSCristian Marussi if (ret) 326*0316f99cSCristian Marussi return ret; 327*0316f99cSCristian Marussi 328*0316f99cSCristian Marussi msg = t->tx.buf; 329*0316f99cSCristian Marussi msg->domain = cpu_to_le32(domain_id); 330*0316f99cSCristian Marussi msg->flags = 331*0316f99cSCristian Marussi cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, !!pc->async_powercap_cap_set) | 332*0316f99cSCristian Marussi FIELD_PREP(CAP_SET_IGNORE_DRESP, !!ignore_dresp)); 333*0316f99cSCristian Marussi msg->value = cpu_to_le32(power_cap); 334*0316f99cSCristian Marussi 335*0316f99cSCristian Marussi if (!pc->async_powercap_cap_set || ignore_dresp) { 336*0316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 337*0316f99cSCristian Marussi } else { 338*0316f99cSCristian Marussi ret = ph->xops->do_xfer_with_response(ph, t); 339*0316f99cSCristian Marussi if (!ret) { 340*0316f99cSCristian Marussi struct scmi_msg_resp_powercap_cap_set_complete *resp; 341*0316f99cSCristian Marussi 342*0316f99cSCristian Marussi resp = t->rx.buf; 343*0316f99cSCristian Marussi if (le32_to_cpu(resp->domain) == domain_id) 344*0316f99cSCristian Marussi dev_dbg(ph->dev, 345*0316f99cSCristian Marussi "Powercap ID %d CAP set async to %u\n", 346*0316f99cSCristian Marussi domain_id, 347*0316f99cSCristian Marussi get_unaligned_le32(&resp->power_cap)); 348*0316f99cSCristian Marussi else 349*0316f99cSCristian Marussi ret = -EPROTO; 350*0316f99cSCristian Marussi } 351*0316f99cSCristian Marussi } 352*0316f99cSCristian Marussi 353*0316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 354*0316f99cSCristian Marussi return ret; 355*0316f99cSCristian Marussi } 356*0316f99cSCristian Marussi 357*0316f99cSCristian Marussi static int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph, 358*0316f99cSCristian Marussi u32 domain_id, u32 *pai) 359*0316f99cSCristian Marussi { 360*0316f99cSCristian Marussi int ret; 361*0316f99cSCristian Marussi struct scmi_xfer *t; 362*0316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 363*0316f99cSCristian Marussi 364*0316f99cSCristian Marussi if (!pai || domain_id >= pi->num_domains) 365*0316f99cSCristian Marussi return -EINVAL; 366*0316f99cSCristian Marussi 367*0316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_GET, sizeof(u32), 368*0316f99cSCristian Marussi sizeof(u32), &t); 369*0316f99cSCristian Marussi if (ret) 370*0316f99cSCristian Marussi return ret; 371*0316f99cSCristian Marussi 372*0316f99cSCristian Marussi put_unaligned_le32(domain_id, t->tx.buf); 373*0316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 374*0316f99cSCristian Marussi if (!ret) 375*0316f99cSCristian Marussi *pai = get_unaligned_le32(t->rx.buf); 376*0316f99cSCristian Marussi 377*0316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 378*0316f99cSCristian Marussi 379*0316f99cSCristian Marussi return ret; 380*0316f99cSCristian Marussi } 381*0316f99cSCristian Marussi 382*0316f99cSCristian Marussi static int scmi_powercap_pai_set(const struct scmi_protocol_handle *ph, 383*0316f99cSCristian Marussi u32 domain_id, u32 pai) 384*0316f99cSCristian Marussi { 385*0316f99cSCristian Marussi int ret; 386*0316f99cSCristian Marussi struct scmi_xfer *t; 387*0316f99cSCristian Marussi struct scmi_msg_powercap_set_cap_or_pai *msg; 388*0316f99cSCristian Marussi const struct scmi_powercap_info *pc; 389*0316f99cSCristian Marussi 390*0316f99cSCristian Marussi pc = scmi_powercap_dom_info_get(ph, domain_id); 391*0316f99cSCristian Marussi if (!pc || !pc->powercap_pai_config || !pai || 392*0316f99cSCristian Marussi pai < pc->min_pai || pai > pc->max_pai) 393*0316f99cSCristian Marussi return -EINVAL; 394*0316f99cSCristian Marussi 395*0316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_SET, 396*0316f99cSCristian Marussi sizeof(*msg), 0, &t); 397*0316f99cSCristian Marussi if (ret) 398*0316f99cSCristian Marussi return ret; 399*0316f99cSCristian Marussi 400*0316f99cSCristian Marussi msg = t->tx.buf; 401*0316f99cSCristian Marussi msg->domain = cpu_to_le32(domain_id); 402*0316f99cSCristian Marussi msg->flags = cpu_to_le32(0); 403*0316f99cSCristian Marussi msg->value = cpu_to_le32(pai); 404*0316f99cSCristian Marussi 405*0316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 406*0316f99cSCristian Marussi 407*0316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 408*0316f99cSCristian Marussi return ret; 409*0316f99cSCristian Marussi } 410*0316f99cSCristian Marussi 411*0316f99cSCristian Marussi static int scmi_powercap_measurements_get(const struct scmi_protocol_handle *ph, 412*0316f99cSCristian Marussi u32 domain_id, u32 *average_power, 413*0316f99cSCristian Marussi u32 *pai) 414*0316f99cSCristian Marussi { 415*0316f99cSCristian Marussi int ret; 416*0316f99cSCristian Marussi struct scmi_xfer *t; 417*0316f99cSCristian Marussi struct scmi_msg_resp_powercap_meas_get *resp; 418*0316f99cSCristian Marussi const struct scmi_powercap_info *pc; 419*0316f99cSCristian Marussi 420*0316f99cSCristian Marussi pc = scmi_powercap_dom_info_get(ph, domain_id); 421*0316f99cSCristian Marussi if (!pc || !pc->powercap_monitoring || !pai || !average_power) 422*0316f99cSCristian Marussi return -EINVAL; 423*0316f99cSCristian Marussi 424*0316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_MEASUREMENTS_GET, 425*0316f99cSCristian Marussi sizeof(u32), sizeof(*resp), &t); 426*0316f99cSCristian Marussi if (ret) 427*0316f99cSCristian Marussi return ret; 428*0316f99cSCristian Marussi 429*0316f99cSCristian Marussi resp = t->rx.buf; 430*0316f99cSCristian Marussi put_unaligned_le32(domain_id, t->tx.buf); 431*0316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 432*0316f99cSCristian Marussi if (!ret) { 433*0316f99cSCristian Marussi *average_power = le32_to_cpu(resp->power); 434*0316f99cSCristian Marussi *pai = le32_to_cpu(resp->pai); 435*0316f99cSCristian Marussi } 436*0316f99cSCristian Marussi 437*0316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 438*0316f99cSCristian Marussi return ret; 439*0316f99cSCristian Marussi } 440*0316f99cSCristian Marussi 441*0316f99cSCristian Marussi static int 442*0316f99cSCristian Marussi scmi_powercap_measurements_threshold_get(const struct scmi_protocol_handle *ph, 443*0316f99cSCristian Marussi u32 domain_id, u32 *power_thresh_low, 444*0316f99cSCristian Marussi u32 *power_thresh_high) 445*0316f99cSCristian Marussi { 446*0316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 447*0316f99cSCristian Marussi 448*0316f99cSCristian Marussi if (!power_thresh_low || !power_thresh_high || 449*0316f99cSCristian Marussi domain_id >= pi->num_domains) 450*0316f99cSCristian Marussi return -EINVAL; 451*0316f99cSCristian Marussi 452*0316f99cSCristian Marussi *power_thresh_low = THRESH_LOW(pi, domain_id); 453*0316f99cSCristian Marussi *power_thresh_high = THRESH_HIGH(pi, domain_id); 454*0316f99cSCristian Marussi 455*0316f99cSCristian Marussi return 0; 456*0316f99cSCristian Marussi } 457*0316f99cSCristian Marussi 458*0316f99cSCristian Marussi static int 459*0316f99cSCristian Marussi scmi_powercap_measurements_threshold_set(const struct scmi_protocol_handle *ph, 460*0316f99cSCristian Marussi u32 domain_id, u32 power_thresh_low, 461*0316f99cSCristian Marussi u32 power_thresh_high) 462*0316f99cSCristian Marussi { 463*0316f99cSCristian Marussi int ret = 0; 464*0316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 465*0316f99cSCristian Marussi 466*0316f99cSCristian Marussi if (domain_id >= pi->num_domains || 467*0316f99cSCristian Marussi power_thresh_low > power_thresh_high) 468*0316f99cSCristian Marussi return -EINVAL; 469*0316f99cSCristian Marussi 470*0316f99cSCristian Marussi /* Anything to do ? */ 471*0316f99cSCristian Marussi if (THRESH_LOW(pi, domain_id) == power_thresh_low && 472*0316f99cSCristian Marussi THRESH_HIGH(pi, domain_id) == power_thresh_high) 473*0316f99cSCristian Marussi return ret; 474*0316f99cSCristian Marussi 475*0316f99cSCristian Marussi pi->states[domain_id].thresholds = 476*0316f99cSCristian Marussi (FIELD_PREP(GENMASK_ULL(31, 0), power_thresh_low) | 477*0316f99cSCristian Marussi FIELD_PREP(GENMASK_ULL(63, 32), power_thresh_high)); 478*0316f99cSCristian Marussi 479*0316f99cSCristian Marussi /* Update thresholds if notification already enabled */ 480*0316f99cSCristian Marussi if (pi->states[domain_id].meas_notif_enabled) 481*0316f99cSCristian Marussi ret = scmi_powercap_notify(ph, domain_id, 482*0316f99cSCristian Marussi POWERCAP_MEASUREMENTS_NOTIFY, 483*0316f99cSCristian Marussi true); 484*0316f99cSCristian Marussi 485*0316f99cSCristian Marussi return ret; 486*0316f99cSCristian Marussi } 487*0316f99cSCristian Marussi 488*0316f99cSCristian Marussi static const struct scmi_powercap_proto_ops powercap_proto_ops = { 489*0316f99cSCristian Marussi .num_domains_get = scmi_powercap_num_domains_get, 490*0316f99cSCristian Marussi .info_get = scmi_powercap_dom_info_get, 491*0316f99cSCristian Marussi .cap_get = scmi_powercap_cap_get, 492*0316f99cSCristian Marussi .cap_set = scmi_powercap_cap_set, 493*0316f99cSCristian Marussi .pai_get = scmi_powercap_pai_get, 494*0316f99cSCristian Marussi .pai_set = scmi_powercap_pai_set, 495*0316f99cSCristian Marussi .measurements_get = scmi_powercap_measurements_get, 496*0316f99cSCristian Marussi .measurements_threshold_set = scmi_powercap_measurements_threshold_set, 497*0316f99cSCristian Marussi .measurements_threshold_get = scmi_powercap_measurements_threshold_get, 498*0316f99cSCristian Marussi }; 499*0316f99cSCristian Marussi 500*0316f99cSCristian Marussi static int scmi_powercap_notify(const struct scmi_protocol_handle *ph, 501*0316f99cSCristian Marussi u32 domain, int message_id, bool enable) 502*0316f99cSCristian Marussi { 503*0316f99cSCristian Marussi int ret; 504*0316f99cSCristian Marussi struct scmi_xfer *t; 505*0316f99cSCristian Marussi 506*0316f99cSCristian Marussi switch (message_id) { 507*0316f99cSCristian Marussi case POWERCAP_CAP_NOTIFY: 508*0316f99cSCristian Marussi { 509*0316f99cSCristian Marussi struct scmi_msg_powercap_notify_cap *notify; 510*0316f99cSCristian Marussi 511*0316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, message_id, 512*0316f99cSCristian Marussi sizeof(*notify), 0, &t); 513*0316f99cSCristian Marussi if (ret) 514*0316f99cSCristian Marussi return ret; 515*0316f99cSCristian Marussi 516*0316f99cSCristian Marussi notify = t->tx.buf; 517*0316f99cSCristian Marussi notify->domain = cpu_to_le32(domain); 518*0316f99cSCristian Marussi notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0); 519*0316f99cSCristian Marussi break; 520*0316f99cSCristian Marussi } 521*0316f99cSCristian Marussi case POWERCAP_MEASUREMENTS_NOTIFY: 522*0316f99cSCristian Marussi { 523*0316f99cSCristian Marussi u32 low, high; 524*0316f99cSCristian Marussi struct scmi_msg_powercap_notify_thresh *notify; 525*0316f99cSCristian Marussi 526*0316f99cSCristian Marussi /* 527*0316f99cSCristian Marussi * Note that we have to pick the most recently configured 528*0316f99cSCristian Marussi * thresholds to build a proper POWERCAP_MEASUREMENTS_NOTIFY 529*0316f99cSCristian Marussi * enable request and we fail, complaining, if no thresholds 530*0316f99cSCristian Marussi * were ever set, since this is an indication the API has been 531*0316f99cSCristian Marussi * used wrongly. 532*0316f99cSCristian Marussi */ 533*0316f99cSCristian Marussi ret = scmi_powercap_measurements_threshold_get(ph, domain, 534*0316f99cSCristian Marussi &low, &high); 535*0316f99cSCristian Marussi if (ret) 536*0316f99cSCristian Marussi return ret; 537*0316f99cSCristian Marussi 538*0316f99cSCristian Marussi if (enable && !low && !high) { 539*0316f99cSCristian Marussi dev_err(ph->dev, 540*0316f99cSCristian Marussi "Invalid Measurements Notify thresholds: %u/%u\n", 541*0316f99cSCristian Marussi low, high); 542*0316f99cSCristian Marussi return -EINVAL; 543*0316f99cSCristian Marussi } 544*0316f99cSCristian Marussi 545*0316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, message_id, 546*0316f99cSCristian Marussi sizeof(*notify), 0, &t); 547*0316f99cSCristian Marussi if (ret) 548*0316f99cSCristian Marussi return ret; 549*0316f99cSCristian Marussi 550*0316f99cSCristian Marussi notify = t->tx.buf; 551*0316f99cSCristian Marussi notify->domain = cpu_to_le32(domain); 552*0316f99cSCristian Marussi notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0); 553*0316f99cSCristian Marussi notify->power_thresh_low = cpu_to_le32(low); 554*0316f99cSCristian Marussi notify->power_thresh_high = cpu_to_le32(high); 555*0316f99cSCristian Marussi break; 556*0316f99cSCristian Marussi } 557*0316f99cSCristian Marussi default: 558*0316f99cSCristian Marussi return -EINVAL; 559*0316f99cSCristian Marussi } 560*0316f99cSCristian Marussi 561*0316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 562*0316f99cSCristian Marussi 563*0316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 564*0316f99cSCristian Marussi return ret; 565*0316f99cSCristian Marussi } 566*0316f99cSCristian Marussi 567*0316f99cSCristian Marussi static int 568*0316f99cSCristian Marussi scmi_powercap_set_notify_enabled(const struct scmi_protocol_handle *ph, 569*0316f99cSCristian Marussi u8 evt_id, u32 src_id, bool enable) 570*0316f99cSCristian Marussi { 571*0316f99cSCristian Marussi int ret, cmd_id; 572*0316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 573*0316f99cSCristian Marussi 574*0316f99cSCristian Marussi if (evt_id >= ARRAY_SIZE(evt_2_cmd) || src_id >= pi->num_domains) 575*0316f99cSCristian Marussi return -EINVAL; 576*0316f99cSCristian Marussi 577*0316f99cSCristian Marussi cmd_id = evt_2_cmd[evt_id]; 578*0316f99cSCristian Marussi ret = scmi_powercap_notify(ph, src_id, cmd_id, enable); 579*0316f99cSCristian Marussi if (ret) 580*0316f99cSCristian Marussi pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n", 581*0316f99cSCristian Marussi evt_id, src_id, ret); 582*0316f99cSCristian Marussi else if (cmd_id == POWERCAP_MEASUREMENTS_NOTIFY) 583*0316f99cSCristian Marussi /* 584*0316f99cSCristian Marussi * On success save the current notification enabled state, so 585*0316f99cSCristian Marussi * as to be able to properly update the notification thresholds 586*0316f99cSCristian Marussi * when they are modified on a domain for which measurement 587*0316f99cSCristian Marussi * notifications were currently enabled. 588*0316f99cSCristian Marussi * 589*0316f99cSCristian Marussi * This is needed because the SCMI Notification core machinery 590*0316f99cSCristian Marussi * and API does not support passing per-notification custom 591*0316f99cSCristian Marussi * arguments at callback registration time. 592*0316f99cSCristian Marussi * 593*0316f99cSCristian Marussi * Note that this can be done here with a simple flag since the 594*0316f99cSCristian Marussi * SCMI core Notifications code takes care of keeping proper 595*0316f99cSCristian Marussi * per-domain enables refcounting, so that this helper function 596*0316f99cSCristian Marussi * will be called only once (for enables) when the first user 597*0316f99cSCristian Marussi * registers a callback on this domain and once more (disable) 598*0316f99cSCristian Marussi * when the last user de-registers its callback. 599*0316f99cSCristian Marussi */ 600*0316f99cSCristian Marussi pi->states[src_id].meas_notif_enabled = enable; 601*0316f99cSCristian Marussi 602*0316f99cSCristian Marussi return ret; 603*0316f99cSCristian Marussi } 604*0316f99cSCristian Marussi 605*0316f99cSCristian Marussi static void * 606*0316f99cSCristian Marussi scmi_powercap_fill_custom_report(const struct scmi_protocol_handle *ph, 607*0316f99cSCristian Marussi u8 evt_id, ktime_t timestamp, 608*0316f99cSCristian Marussi const void *payld, size_t payld_sz, 609*0316f99cSCristian Marussi void *report, u32 *src_id) 610*0316f99cSCristian Marussi { 611*0316f99cSCristian Marussi void *rep = NULL; 612*0316f99cSCristian Marussi 613*0316f99cSCristian Marussi switch (evt_id) { 614*0316f99cSCristian Marussi case SCMI_EVENT_POWERCAP_CAP_CHANGED: 615*0316f99cSCristian Marussi { 616*0316f99cSCristian Marussi const struct scmi_powercap_cap_changed_notify_payld *p = payld; 617*0316f99cSCristian Marussi struct scmi_powercap_cap_changed_report *r = report; 618*0316f99cSCristian Marussi 619*0316f99cSCristian Marussi if (sizeof(*p) != payld_sz) 620*0316f99cSCristian Marussi break; 621*0316f99cSCristian Marussi 622*0316f99cSCristian Marussi r->timestamp = timestamp; 623*0316f99cSCristian Marussi r->agent_id = le32_to_cpu(p->agent_id); 624*0316f99cSCristian Marussi r->domain_id = le32_to_cpu(p->domain_id); 625*0316f99cSCristian Marussi r->power_cap = le32_to_cpu(p->power_cap); 626*0316f99cSCristian Marussi r->pai = le32_to_cpu(p->pai); 627*0316f99cSCristian Marussi *src_id = r->domain_id; 628*0316f99cSCristian Marussi rep = r; 629*0316f99cSCristian Marussi break; 630*0316f99cSCristian Marussi } 631*0316f99cSCristian Marussi case SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED: 632*0316f99cSCristian Marussi { 633*0316f99cSCristian Marussi const struct scmi_powercap_meas_changed_notify_payld *p = payld; 634*0316f99cSCristian Marussi struct scmi_powercap_meas_changed_report *r = report; 635*0316f99cSCristian Marussi 636*0316f99cSCristian Marussi if (sizeof(*p) != payld_sz) 637*0316f99cSCristian Marussi break; 638*0316f99cSCristian Marussi 639*0316f99cSCristian Marussi r->timestamp = timestamp; 640*0316f99cSCristian Marussi r->agent_id = le32_to_cpu(p->agent_id); 641*0316f99cSCristian Marussi r->domain_id = le32_to_cpu(p->domain_id); 642*0316f99cSCristian Marussi r->power = le32_to_cpu(p->power); 643*0316f99cSCristian Marussi *src_id = r->domain_id; 644*0316f99cSCristian Marussi rep = r; 645*0316f99cSCristian Marussi break; 646*0316f99cSCristian Marussi } 647*0316f99cSCristian Marussi default: 648*0316f99cSCristian Marussi break; 649*0316f99cSCristian Marussi } 650*0316f99cSCristian Marussi 651*0316f99cSCristian Marussi return rep; 652*0316f99cSCristian Marussi } 653*0316f99cSCristian Marussi 654*0316f99cSCristian Marussi static int 655*0316f99cSCristian Marussi scmi_powercap_get_num_sources(const struct scmi_protocol_handle *ph) 656*0316f99cSCristian Marussi { 657*0316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 658*0316f99cSCristian Marussi 659*0316f99cSCristian Marussi if (!pi) 660*0316f99cSCristian Marussi return -EINVAL; 661*0316f99cSCristian Marussi 662*0316f99cSCristian Marussi return pi->num_domains; 663*0316f99cSCristian Marussi } 664*0316f99cSCristian Marussi 665*0316f99cSCristian Marussi static const struct scmi_event powercap_events[] = { 666*0316f99cSCristian Marussi { 667*0316f99cSCristian Marussi .id = SCMI_EVENT_POWERCAP_CAP_CHANGED, 668*0316f99cSCristian Marussi .max_payld_sz = 669*0316f99cSCristian Marussi sizeof(struct scmi_powercap_cap_changed_notify_payld), 670*0316f99cSCristian Marussi .max_report_sz = 671*0316f99cSCristian Marussi sizeof(struct scmi_powercap_cap_changed_report), 672*0316f99cSCristian Marussi }, 673*0316f99cSCristian Marussi { 674*0316f99cSCristian Marussi .id = SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED, 675*0316f99cSCristian Marussi .max_payld_sz = 676*0316f99cSCristian Marussi sizeof(struct scmi_powercap_meas_changed_notify_payld), 677*0316f99cSCristian Marussi .max_report_sz = 678*0316f99cSCristian Marussi sizeof(struct scmi_powercap_meas_changed_report), 679*0316f99cSCristian Marussi }, 680*0316f99cSCristian Marussi }; 681*0316f99cSCristian Marussi 682*0316f99cSCristian Marussi static const struct scmi_event_ops powercap_event_ops = { 683*0316f99cSCristian Marussi .get_num_sources = scmi_powercap_get_num_sources, 684*0316f99cSCristian Marussi .set_notify_enabled = scmi_powercap_set_notify_enabled, 685*0316f99cSCristian Marussi .fill_custom_report = scmi_powercap_fill_custom_report, 686*0316f99cSCristian Marussi }; 687*0316f99cSCristian Marussi 688*0316f99cSCristian Marussi static const struct scmi_protocol_events powercap_protocol_events = { 689*0316f99cSCristian Marussi .queue_sz = SCMI_PROTO_QUEUE_SZ, 690*0316f99cSCristian Marussi .ops = &powercap_event_ops, 691*0316f99cSCristian Marussi .evts = powercap_events, 692*0316f99cSCristian Marussi .num_events = ARRAY_SIZE(powercap_events), 693*0316f99cSCristian Marussi }; 694*0316f99cSCristian Marussi 695*0316f99cSCristian Marussi static int 696*0316f99cSCristian Marussi scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph) 697*0316f99cSCristian Marussi { 698*0316f99cSCristian Marussi int domain, ret; 699*0316f99cSCristian Marussi u32 version; 700*0316f99cSCristian Marussi struct powercap_info *pinfo; 701*0316f99cSCristian Marussi 702*0316f99cSCristian Marussi ret = ph->xops->version_get(ph, &version); 703*0316f99cSCristian Marussi if (ret) 704*0316f99cSCristian Marussi return ret; 705*0316f99cSCristian Marussi 706*0316f99cSCristian Marussi dev_dbg(ph->dev, "Powercap Version %d.%d\n", 707*0316f99cSCristian Marussi PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); 708*0316f99cSCristian Marussi 709*0316f99cSCristian Marussi pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL); 710*0316f99cSCristian Marussi if (!pinfo) 711*0316f99cSCristian Marussi return -ENOMEM; 712*0316f99cSCristian Marussi 713*0316f99cSCristian Marussi ret = scmi_powercap_attributes_get(ph, pinfo); 714*0316f99cSCristian Marussi if (ret) 715*0316f99cSCristian Marussi return ret; 716*0316f99cSCristian Marussi 717*0316f99cSCristian Marussi pinfo->powercaps = devm_kcalloc(ph->dev, pinfo->num_domains, 718*0316f99cSCristian Marussi sizeof(*pinfo->powercaps), 719*0316f99cSCristian Marussi GFP_KERNEL); 720*0316f99cSCristian Marussi if (!pinfo->powercaps) 721*0316f99cSCristian Marussi return -ENOMEM; 722*0316f99cSCristian Marussi 723*0316f99cSCristian Marussi /* 724*0316f99cSCristian Marussi * Note that any failure in retrieving any domain attribute leads to 725*0316f99cSCristian Marussi * the whole Powercap protocol initialization failure: this way the 726*0316f99cSCristian Marussi * reported Powercap domains are all assured, when accessed, to be well 727*0316f99cSCristian Marussi * formed and correlated by sane parent-child relationship (if any). 728*0316f99cSCristian Marussi */ 729*0316f99cSCristian Marussi for (domain = 0; domain < pinfo->num_domains; domain++) { 730*0316f99cSCristian Marussi ret = scmi_powercap_domain_attributes_get(ph, pinfo, domain); 731*0316f99cSCristian Marussi if (ret) 732*0316f99cSCristian Marussi return ret; 733*0316f99cSCristian Marussi } 734*0316f99cSCristian Marussi 735*0316f99cSCristian Marussi pinfo->states = devm_kcalloc(ph->dev, pinfo->num_domains, 736*0316f99cSCristian Marussi sizeof(*pinfo->states), GFP_KERNEL); 737*0316f99cSCristian Marussi if (!pinfo->states) 738*0316f99cSCristian Marussi return -ENOMEM; 739*0316f99cSCristian Marussi 740*0316f99cSCristian Marussi pinfo->version = version; 741*0316f99cSCristian Marussi 742*0316f99cSCristian Marussi return ph->set_priv(ph, pinfo); 743*0316f99cSCristian Marussi } 744*0316f99cSCristian Marussi 745*0316f99cSCristian Marussi static const struct scmi_protocol scmi_powercap = { 746*0316f99cSCristian Marussi .id = SCMI_PROTOCOL_POWERCAP, 747*0316f99cSCristian Marussi .owner = THIS_MODULE, 748*0316f99cSCristian Marussi .instance_init = &scmi_powercap_protocol_init, 749*0316f99cSCristian Marussi .ops = &powercap_proto_ops, 750*0316f99cSCristian Marussi .events = &powercap_protocol_events, 751*0316f99cSCristian Marussi }; 752*0316f99cSCristian Marussi 753*0316f99cSCristian Marussi DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(powercap, scmi_powercap) 754