162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * System Control and Management Interface (SCMI) Powercap Protocol 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2022 ARM Ltd. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define pr_fmt(fmt) "SCMI Notifications POWERCAP - " fmt 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/bitfield.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/scmi_protocol.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <trace/events/scmi.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "protocols.h" 1862306a36Sopenharmony_ci#include "notify.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cienum scmi_powercap_protocol_cmd { 2162306a36Sopenharmony_ci POWERCAP_DOMAIN_ATTRIBUTES = 0x3, 2262306a36Sopenharmony_ci POWERCAP_CAP_GET = 0x4, 2362306a36Sopenharmony_ci POWERCAP_CAP_SET = 0x5, 2462306a36Sopenharmony_ci POWERCAP_PAI_GET = 0x6, 2562306a36Sopenharmony_ci POWERCAP_PAI_SET = 0x7, 2662306a36Sopenharmony_ci POWERCAP_DOMAIN_NAME_GET = 0x8, 2762306a36Sopenharmony_ci POWERCAP_MEASUREMENTS_GET = 0x9, 2862306a36Sopenharmony_ci POWERCAP_CAP_NOTIFY = 0xa, 2962306a36Sopenharmony_ci POWERCAP_MEASUREMENTS_NOTIFY = 0xb, 3062306a36Sopenharmony_ci POWERCAP_DESCRIBE_FASTCHANNEL = 0xc, 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cienum { 3462306a36Sopenharmony_ci POWERCAP_FC_CAP, 3562306a36Sopenharmony_ci POWERCAP_FC_PAI, 3662306a36Sopenharmony_ci POWERCAP_FC_MAX, 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistruct scmi_msg_resp_powercap_domain_attributes { 4062306a36Sopenharmony_ci __le32 attributes; 4162306a36Sopenharmony_ci#define SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(x) ((x) & BIT(31)) 4262306a36Sopenharmony_ci#define SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(x) ((x) & BIT(30)) 4362306a36Sopenharmony_ci#define SUPPORTS_ASYNC_POWERCAP_CAP_SET(x) ((x) & BIT(29)) 4462306a36Sopenharmony_ci#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(28)) 4562306a36Sopenharmony_ci#define SUPPORTS_POWERCAP_CAP_CONFIGURATION(x) ((x) & BIT(27)) 4662306a36Sopenharmony_ci#define SUPPORTS_POWERCAP_MONITORING(x) ((x) & BIT(26)) 4762306a36Sopenharmony_ci#define SUPPORTS_POWERCAP_PAI_CONFIGURATION(x) ((x) & BIT(25)) 4862306a36Sopenharmony_ci#define SUPPORTS_POWERCAP_FASTCHANNELS(x) ((x) & BIT(22)) 4962306a36Sopenharmony_ci#define POWERCAP_POWER_UNIT(x) \ 5062306a36Sopenharmony_ci (FIELD_GET(GENMASK(24, 23), (x))) 5162306a36Sopenharmony_ci#define SUPPORTS_POWER_UNITS_MW(x) \ 5262306a36Sopenharmony_ci (POWERCAP_POWER_UNIT(x) == 0x2) 5362306a36Sopenharmony_ci#define SUPPORTS_POWER_UNITS_UW(x) \ 5462306a36Sopenharmony_ci (POWERCAP_POWER_UNIT(x) == 0x1) 5562306a36Sopenharmony_ci u8 name[SCMI_SHORT_NAME_MAX_SIZE]; 5662306a36Sopenharmony_ci __le32 min_pai; 5762306a36Sopenharmony_ci __le32 max_pai; 5862306a36Sopenharmony_ci __le32 pai_step; 5962306a36Sopenharmony_ci __le32 min_power_cap; 6062306a36Sopenharmony_ci __le32 max_power_cap; 6162306a36Sopenharmony_ci __le32 power_cap_step; 6262306a36Sopenharmony_ci __le32 sustainable_power; 6362306a36Sopenharmony_ci __le32 accuracy; 6462306a36Sopenharmony_ci __le32 parent_id; 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistruct scmi_msg_powercap_set_cap_or_pai { 6862306a36Sopenharmony_ci __le32 domain; 6962306a36Sopenharmony_ci __le32 flags; 7062306a36Sopenharmony_ci#define CAP_SET_ASYNC BIT(1) 7162306a36Sopenharmony_ci#define CAP_SET_IGNORE_DRESP BIT(0) 7262306a36Sopenharmony_ci __le32 value; 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistruct scmi_msg_resp_powercap_cap_set_complete { 7662306a36Sopenharmony_ci __le32 domain; 7762306a36Sopenharmony_ci __le32 power_cap; 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistruct scmi_msg_resp_powercap_meas_get { 8162306a36Sopenharmony_ci __le32 power; 8262306a36Sopenharmony_ci __le32 pai; 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistruct scmi_msg_powercap_notify_cap { 8662306a36Sopenharmony_ci __le32 domain; 8762306a36Sopenharmony_ci __le32 notify_enable; 8862306a36Sopenharmony_ci}; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistruct scmi_msg_powercap_notify_thresh { 9162306a36Sopenharmony_ci __le32 domain; 9262306a36Sopenharmony_ci __le32 notify_enable; 9362306a36Sopenharmony_ci __le32 power_thresh_low; 9462306a36Sopenharmony_ci __le32 power_thresh_high; 9562306a36Sopenharmony_ci}; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistruct scmi_powercap_cap_changed_notify_payld { 9862306a36Sopenharmony_ci __le32 agent_id; 9962306a36Sopenharmony_ci __le32 domain_id; 10062306a36Sopenharmony_ci __le32 power_cap; 10162306a36Sopenharmony_ci __le32 pai; 10262306a36Sopenharmony_ci}; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistruct scmi_powercap_meas_changed_notify_payld { 10562306a36Sopenharmony_ci __le32 agent_id; 10662306a36Sopenharmony_ci __le32 domain_id; 10762306a36Sopenharmony_ci __le32 power; 10862306a36Sopenharmony_ci}; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistruct scmi_powercap_state { 11162306a36Sopenharmony_ci bool enabled; 11262306a36Sopenharmony_ci u32 last_pcap; 11362306a36Sopenharmony_ci bool meas_notif_enabled; 11462306a36Sopenharmony_ci u64 thresholds; 11562306a36Sopenharmony_ci#define THRESH_LOW(p, id) \ 11662306a36Sopenharmony_ci (lower_32_bits((p)->states[(id)].thresholds)) 11762306a36Sopenharmony_ci#define THRESH_HIGH(p, id) \ 11862306a36Sopenharmony_ci (upper_32_bits((p)->states[(id)].thresholds)) 11962306a36Sopenharmony_ci}; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistruct powercap_info { 12262306a36Sopenharmony_ci u32 version; 12362306a36Sopenharmony_ci int num_domains; 12462306a36Sopenharmony_ci struct scmi_powercap_state *states; 12562306a36Sopenharmony_ci struct scmi_powercap_info *powercaps; 12662306a36Sopenharmony_ci}; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic enum scmi_powercap_protocol_cmd evt_2_cmd[] = { 12962306a36Sopenharmony_ci POWERCAP_CAP_NOTIFY, 13062306a36Sopenharmony_ci POWERCAP_MEASUREMENTS_NOTIFY, 13162306a36Sopenharmony_ci}; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic int scmi_powercap_notify(const struct scmi_protocol_handle *ph, 13462306a36Sopenharmony_ci u32 domain, int message_id, bool enable); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic int 13762306a36Sopenharmony_ciscmi_powercap_attributes_get(const struct scmi_protocol_handle *ph, 13862306a36Sopenharmony_ci struct powercap_info *pi) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci int ret; 14162306a36Sopenharmony_ci struct scmi_xfer *t; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0, 14462306a36Sopenharmony_ci sizeof(u32), &t); 14562306a36Sopenharmony_ci if (ret) 14662306a36Sopenharmony_ci return ret; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci ret = ph->xops->do_xfer(ph, t); 14962306a36Sopenharmony_ci if (!ret) { 15062306a36Sopenharmony_ci u32 attributes; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci attributes = get_unaligned_le32(t->rx.buf); 15362306a36Sopenharmony_ci pi->num_domains = FIELD_GET(GENMASK(15, 0), attributes); 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci ph->xops->xfer_put(ph, t); 15762306a36Sopenharmony_ci return ret; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic inline int 16162306a36Sopenharmony_ciscmi_powercap_validate(unsigned int min_val, unsigned int max_val, 16262306a36Sopenharmony_ci unsigned int step_val, bool configurable) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci if (!min_val || !max_val) 16562306a36Sopenharmony_ci return -EPROTO; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if ((configurable && min_val == max_val) || 16862306a36Sopenharmony_ci (!configurable && min_val != max_val)) 16962306a36Sopenharmony_ci return -EPROTO; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (min_val != max_val && !step_val) 17262306a36Sopenharmony_ci return -EPROTO; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return 0; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic int 17862306a36Sopenharmony_ciscmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph, 17962306a36Sopenharmony_ci struct powercap_info *pinfo, u32 domain) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci int ret; 18262306a36Sopenharmony_ci u32 flags; 18362306a36Sopenharmony_ci struct scmi_xfer *t; 18462306a36Sopenharmony_ci struct scmi_powercap_info *dom_info = pinfo->powercaps + domain; 18562306a36Sopenharmony_ci struct scmi_msg_resp_powercap_domain_attributes *resp; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci ret = ph->xops->xfer_get_init(ph, POWERCAP_DOMAIN_ATTRIBUTES, 18862306a36Sopenharmony_ci sizeof(domain), sizeof(*resp), &t); 18962306a36Sopenharmony_ci if (ret) 19062306a36Sopenharmony_ci return ret; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci put_unaligned_le32(domain, t->tx.buf); 19362306a36Sopenharmony_ci resp = t->rx.buf; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci ret = ph->xops->do_xfer(ph, t); 19662306a36Sopenharmony_ci if (!ret) { 19762306a36Sopenharmony_ci flags = le32_to_cpu(resp->attributes); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci dom_info->id = domain; 20062306a36Sopenharmony_ci dom_info->notify_powercap_cap_change = 20162306a36Sopenharmony_ci SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags); 20262306a36Sopenharmony_ci dom_info->notify_powercap_measurement_change = 20362306a36Sopenharmony_ci SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags); 20462306a36Sopenharmony_ci dom_info->async_powercap_cap_set = 20562306a36Sopenharmony_ci SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags); 20662306a36Sopenharmony_ci dom_info->powercap_cap_config = 20762306a36Sopenharmony_ci SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags); 20862306a36Sopenharmony_ci dom_info->powercap_monitoring = 20962306a36Sopenharmony_ci SUPPORTS_POWERCAP_MONITORING(flags); 21062306a36Sopenharmony_ci dom_info->powercap_pai_config = 21162306a36Sopenharmony_ci SUPPORTS_POWERCAP_PAI_CONFIGURATION(flags); 21262306a36Sopenharmony_ci dom_info->powercap_scale_mw = 21362306a36Sopenharmony_ci SUPPORTS_POWER_UNITS_MW(flags); 21462306a36Sopenharmony_ci dom_info->powercap_scale_uw = 21562306a36Sopenharmony_ci SUPPORTS_POWER_UNITS_UW(flags); 21662306a36Sopenharmony_ci dom_info->fastchannels = 21762306a36Sopenharmony_ci SUPPORTS_POWERCAP_FASTCHANNELS(flags); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci strscpy(dom_info->name, resp->name, SCMI_SHORT_NAME_MAX_SIZE); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci dom_info->min_pai = le32_to_cpu(resp->min_pai); 22262306a36Sopenharmony_ci dom_info->max_pai = le32_to_cpu(resp->max_pai); 22362306a36Sopenharmony_ci dom_info->pai_step = le32_to_cpu(resp->pai_step); 22462306a36Sopenharmony_ci ret = scmi_powercap_validate(dom_info->min_pai, 22562306a36Sopenharmony_ci dom_info->max_pai, 22662306a36Sopenharmony_ci dom_info->pai_step, 22762306a36Sopenharmony_ci dom_info->powercap_pai_config); 22862306a36Sopenharmony_ci if (ret) { 22962306a36Sopenharmony_ci dev_err(ph->dev, 23062306a36Sopenharmony_ci "Platform reported inconsistent PAI config for domain %d - %s\n", 23162306a36Sopenharmony_ci dom_info->id, dom_info->name); 23262306a36Sopenharmony_ci goto clean; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci dom_info->min_power_cap = le32_to_cpu(resp->min_power_cap); 23662306a36Sopenharmony_ci dom_info->max_power_cap = le32_to_cpu(resp->max_power_cap); 23762306a36Sopenharmony_ci dom_info->power_cap_step = le32_to_cpu(resp->power_cap_step); 23862306a36Sopenharmony_ci ret = scmi_powercap_validate(dom_info->min_power_cap, 23962306a36Sopenharmony_ci dom_info->max_power_cap, 24062306a36Sopenharmony_ci dom_info->power_cap_step, 24162306a36Sopenharmony_ci dom_info->powercap_cap_config); 24262306a36Sopenharmony_ci if (ret) { 24362306a36Sopenharmony_ci dev_err(ph->dev, 24462306a36Sopenharmony_ci "Platform reported inconsistent CAP config for domain %d - %s\n", 24562306a36Sopenharmony_ci dom_info->id, dom_info->name); 24662306a36Sopenharmony_ci goto clean; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci dom_info->sustainable_power = 25062306a36Sopenharmony_ci le32_to_cpu(resp->sustainable_power); 25162306a36Sopenharmony_ci dom_info->accuracy = le32_to_cpu(resp->accuracy); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci dom_info->parent_id = le32_to_cpu(resp->parent_id); 25462306a36Sopenharmony_ci if (dom_info->parent_id != SCMI_POWERCAP_ROOT_ZONE_ID && 25562306a36Sopenharmony_ci (dom_info->parent_id >= pinfo->num_domains || 25662306a36Sopenharmony_ci dom_info->parent_id == dom_info->id)) { 25762306a36Sopenharmony_ci dev_err(ph->dev, 25862306a36Sopenharmony_ci "Platform reported inconsistent parent ID for domain %d - %s\n", 25962306a36Sopenharmony_ci dom_info->id, dom_info->name); 26062306a36Sopenharmony_ci ret = -ENODEV; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ciclean: 26562306a36Sopenharmony_ci ph->xops->xfer_put(ph, t); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* 26862306a36Sopenharmony_ci * If supported overwrite short name with the extended one; 26962306a36Sopenharmony_ci * on error just carry on and use already provided short name. 27062306a36Sopenharmony_ci */ 27162306a36Sopenharmony_ci if (!ret && SUPPORTS_EXTENDED_NAMES(flags)) 27262306a36Sopenharmony_ci ph->hops->extended_name_get(ph, POWERCAP_DOMAIN_NAME_GET, 27362306a36Sopenharmony_ci domain, dom_info->name, 27462306a36Sopenharmony_ci SCMI_MAX_STR_SIZE); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci return ret; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic int scmi_powercap_num_domains_get(const struct scmi_protocol_handle *ph) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct powercap_info *pi = ph->get_priv(ph); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci return pi->num_domains; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic const struct scmi_powercap_info * 28762306a36Sopenharmony_ciscmi_powercap_dom_info_get(const struct scmi_protocol_handle *ph, u32 domain_id) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct powercap_info *pi = ph->get_priv(ph); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (domain_id >= pi->num_domains) 29262306a36Sopenharmony_ci return NULL; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci return pi->powercaps + domain_id; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic int scmi_powercap_xfer_cap_get(const struct scmi_protocol_handle *ph, 29862306a36Sopenharmony_ci u32 domain_id, u32 *power_cap) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci int ret; 30162306a36Sopenharmony_ci struct scmi_xfer *t; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_GET, sizeof(u32), 30462306a36Sopenharmony_ci sizeof(u32), &t); 30562306a36Sopenharmony_ci if (ret) 30662306a36Sopenharmony_ci return ret; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci put_unaligned_le32(domain_id, t->tx.buf); 30962306a36Sopenharmony_ci ret = ph->xops->do_xfer(ph, t); 31062306a36Sopenharmony_ci if (!ret) 31162306a36Sopenharmony_ci *power_cap = get_unaligned_le32(t->rx.buf); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci ph->xops->xfer_put(ph, t); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return ret; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic int __scmi_powercap_cap_get(const struct scmi_protocol_handle *ph, 31962306a36Sopenharmony_ci const struct scmi_powercap_info *dom, 32062306a36Sopenharmony_ci u32 *power_cap) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci if (dom->fc_info && dom->fc_info[POWERCAP_FC_CAP].get_addr) { 32362306a36Sopenharmony_ci *power_cap = ioread32(dom->fc_info[POWERCAP_FC_CAP].get_addr); 32462306a36Sopenharmony_ci trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_GET, 32562306a36Sopenharmony_ci dom->id, *power_cap, 0); 32662306a36Sopenharmony_ci return 0; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci return scmi_powercap_xfer_cap_get(ph, dom->id, power_cap); 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic int scmi_powercap_cap_get(const struct scmi_protocol_handle *ph, 33362306a36Sopenharmony_ci u32 domain_id, u32 *power_cap) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci const struct scmi_powercap_info *dom; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (!power_cap) 33862306a36Sopenharmony_ci return -EINVAL; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci dom = scmi_powercap_dom_info_get(ph, domain_id); 34162306a36Sopenharmony_ci if (!dom) 34262306a36Sopenharmony_ci return -EINVAL; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci return __scmi_powercap_cap_get(ph, dom, power_cap); 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph, 34862306a36Sopenharmony_ci const struct scmi_powercap_info *pc, 34962306a36Sopenharmony_ci u32 power_cap, bool ignore_dresp) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci int ret; 35262306a36Sopenharmony_ci struct scmi_xfer *t; 35362306a36Sopenharmony_ci struct scmi_msg_powercap_set_cap_or_pai *msg; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_SET, 35662306a36Sopenharmony_ci sizeof(*msg), 0, &t); 35762306a36Sopenharmony_ci if (ret) 35862306a36Sopenharmony_ci return ret; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci msg = t->tx.buf; 36162306a36Sopenharmony_ci msg->domain = cpu_to_le32(pc->id); 36262306a36Sopenharmony_ci msg->flags = 36362306a36Sopenharmony_ci cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, !!pc->async_powercap_cap_set) | 36462306a36Sopenharmony_ci FIELD_PREP(CAP_SET_IGNORE_DRESP, !!ignore_dresp)); 36562306a36Sopenharmony_ci msg->value = cpu_to_le32(power_cap); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (!pc->async_powercap_cap_set || ignore_dresp) { 36862306a36Sopenharmony_ci ret = ph->xops->do_xfer(ph, t); 36962306a36Sopenharmony_ci } else { 37062306a36Sopenharmony_ci ret = ph->xops->do_xfer_with_response(ph, t); 37162306a36Sopenharmony_ci if (!ret) { 37262306a36Sopenharmony_ci struct scmi_msg_resp_powercap_cap_set_complete *resp; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci resp = t->rx.buf; 37562306a36Sopenharmony_ci if (le32_to_cpu(resp->domain) == pc->id) 37662306a36Sopenharmony_ci dev_dbg(ph->dev, 37762306a36Sopenharmony_ci "Powercap ID %d CAP set async to %u\n", 37862306a36Sopenharmony_ci pc->id, 37962306a36Sopenharmony_ci get_unaligned_le32(&resp->power_cap)); 38062306a36Sopenharmony_ci else 38162306a36Sopenharmony_ci ret = -EPROTO; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci ph->xops->xfer_put(ph, t); 38662306a36Sopenharmony_ci return ret; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic int __scmi_powercap_cap_set(const struct scmi_protocol_handle *ph, 39062306a36Sopenharmony_ci struct powercap_info *pi, u32 domain_id, 39162306a36Sopenharmony_ci u32 power_cap, bool ignore_dresp) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci int ret = -EINVAL; 39462306a36Sopenharmony_ci const struct scmi_powercap_info *pc; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci pc = scmi_powercap_dom_info_get(ph, domain_id); 39762306a36Sopenharmony_ci if (!pc || !pc->powercap_cap_config) 39862306a36Sopenharmony_ci return ret; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (power_cap && 40162306a36Sopenharmony_ci (power_cap < pc->min_power_cap || power_cap > pc->max_power_cap)) 40262306a36Sopenharmony_ci return ret; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (pc->fc_info && pc->fc_info[POWERCAP_FC_CAP].set_addr) { 40562306a36Sopenharmony_ci struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_CAP]; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci iowrite32(power_cap, fci->set_addr); 40862306a36Sopenharmony_ci ph->hops->fastchannel_db_ring(fci->set_db); 40962306a36Sopenharmony_ci trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_SET, 41062306a36Sopenharmony_ci domain_id, power_cap, 0); 41162306a36Sopenharmony_ci ret = 0; 41262306a36Sopenharmony_ci } else { 41362306a36Sopenharmony_ci ret = scmi_powercap_xfer_cap_set(ph, pc, power_cap, 41462306a36Sopenharmony_ci ignore_dresp); 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* Save the last explicitly set non-zero powercap value */ 41862306a36Sopenharmony_ci if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 && !ret && power_cap) 41962306a36Sopenharmony_ci pi->states[domain_id].last_pcap = power_cap; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return ret; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph, 42562306a36Sopenharmony_ci u32 domain_id, u32 power_cap, 42662306a36Sopenharmony_ci bool ignore_dresp) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct powercap_info *pi = ph->get_priv(ph); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* 43162306a36Sopenharmony_ci * Disallow zero as a possible explicitly requested powercap: 43262306a36Sopenharmony_ci * there are enable/disable operations for this. 43362306a36Sopenharmony_ci */ 43462306a36Sopenharmony_ci if (!power_cap) 43562306a36Sopenharmony_ci return -EINVAL; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* Just log the last set request if acting on a disabled domain */ 43862306a36Sopenharmony_ci if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 && 43962306a36Sopenharmony_ci !pi->states[domain_id].enabled) { 44062306a36Sopenharmony_ci pi->states[domain_id].last_pcap = power_cap; 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci return __scmi_powercap_cap_set(ph, pi, domain_id, 44562306a36Sopenharmony_ci power_cap, ignore_dresp); 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic int scmi_powercap_xfer_pai_get(const struct scmi_protocol_handle *ph, 44962306a36Sopenharmony_ci u32 domain_id, u32 *pai) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci int ret; 45262306a36Sopenharmony_ci struct scmi_xfer *t; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_GET, sizeof(u32), 45562306a36Sopenharmony_ci sizeof(u32), &t); 45662306a36Sopenharmony_ci if (ret) 45762306a36Sopenharmony_ci return ret; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci put_unaligned_le32(domain_id, t->tx.buf); 46062306a36Sopenharmony_ci ret = ph->xops->do_xfer(ph, t); 46162306a36Sopenharmony_ci if (!ret) 46262306a36Sopenharmony_ci *pai = get_unaligned_le32(t->rx.buf); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci ph->xops->xfer_put(ph, t); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci return ret; 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph, 47062306a36Sopenharmony_ci u32 domain_id, u32 *pai) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci struct scmi_powercap_info *dom; 47362306a36Sopenharmony_ci struct powercap_info *pi = ph->get_priv(ph); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (!pai || domain_id >= pi->num_domains) 47662306a36Sopenharmony_ci return -EINVAL; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci dom = pi->powercaps + domain_id; 47962306a36Sopenharmony_ci if (dom->fc_info && dom->fc_info[POWERCAP_FC_PAI].get_addr) { 48062306a36Sopenharmony_ci *pai = ioread32(dom->fc_info[POWERCAP_FC_PAI].get_addr); 48162306a36Sopenharmony_ci trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_GET, 48262306a36Sopenharmony_ci domain_id, *pai, 0); 48362306a36Sopenharmony_ci return 0; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci return scmi_powercap_xfer_pai_get(ph, domain_id, pai); 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic int scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle *ph, 49062306a36Sopenharmony_ci u32 domain_id, u32 pai) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci int ret; 49362306a36Sopenharmony_ci struct scmi_xfer *t; 49462306a36Sopenharmony_ci struct scmi_msg_powercap_set_cap_or_pai *msg; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_SET, 49762306a36Sopenharmony_ci sizeof(*msg), 0, &t); 49862306a36Sopenharmony_ci if (ret) 49962306a36Sopenharmony_ci return ret; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci msg = t->tx.buf; 50262306a36Sopenharmony_ci msg->domain = cpu_to_le32(domain_id); 50362306a36Sopenharmony_ci msg->flags = cpu_to_le32(0); 50462306a36Sopenharmony_ci msg->value = cpu_to_le32(pai); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci ret = ph->xops->do_xfer(ph, t); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci ph->xops->xfer_put(ph, t); 50962306a36Sopenharmony_ci return ret; 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_cistatic int scmi_powercap_pai_set(const struct scmi_protocol_handle *ph, 51362306a36Sopenharmony_ci u32 domain_id, u32 pai) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci const struct scmi_powercap_info *pc; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci pc = scmi_powercap_dom_info_get(ph, domain_id); 51862306a36Sopenharmony_ci if (!pc || !pc->powercap_pai_config || !pai || 51962306a36Sopenharmony_ci pai < pc->min_pai || pai > pc->max_pai) 52062306a36Sopenharmony_ci return -EINVAL; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (pc->fc_info && pc->fc_info[POWERCAP_FC_PAI].set_addr) { 52362306a36Sopenharmony_ci struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_PAI]; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_SET, 52662306a36Sopenharmony_ci domain_id, pai, 0); 52762306a36Sopenharmony_ci iowrite32(pai, fci->set_addr); 52862306a36Sopenharmony_ci ph->hops->fastchannel_db_ring(fci->set_db); 52962306a36Sopenharmony_ci return 0; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci return scmi_powercap_xfer_pai_set(ph, domain_id, pai); 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic int scmi_powercap_measurements_get(const struct scmi_protocol_handle *ph, 53662306a36Sopenharmony_ci u32 domain_id, u32 *average_power, 53762306a36Sopenharmony_ci u32 *pai) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci int ret; 54062306a36Sopenharmony_ci struct scmi_xfer *t; 54162306a36Sopenharmony_ci struct scmi_msg_resp_powercap_meas_get *resp; 54262306a36Sopenharmony_ci const struct scmi_powercap_info *pc; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci pc = scmi_powercap_dom_info_get(ph, domain_id); 54562306a36Sopenharmony_ci if (!pc || !pc->powercap_monitoring || !pai || !average_power) 54662306a36Sopenharmony_ci return -EINVAL; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci ret = ph->xops->xfer_get_init(ph, POWERCAP_MEASUREMENTS_GET, 54962306a36Sopenharmony_ci sizeof(u32), sizeof(*resp), &t); 55062306a36Sopenharmony_ci if (ret) 55162306a36Sopenharmony_ci return ret; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci resp = t->rx.buf; 55462306a36Sopenharmony_ci put_unaligned_le32(domain_id, t->tx.buf); 55562306a36Sopenharmony_ci ret = ph->xops->do_xfer(ph, t); 55662306a36Sopenharmony_ci if (!ret) { 55762306a36Sopenharmony_ci *average_power = le32_to_cpu(resp->power); 55862306a36Sopenharmony_ci *pai = le32_to_cpu(resp->pai); 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci ph->xops->xfer_put(ph, t); 56262306a36Sopenharmony_ci return ret; 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_cistatic int 56662306a36Sopenharmony_ciscmi_powercap_measurements_threshold_get(const struct scmi_protocol_handle *ph, 56762306a36Sopenharmony_ci u32 domain_id, u32 *power_thresh_low, 56862306a36Sopenharmony_ci u32 *power_thresh_high) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci struct powercap_info *pi = ph->get_priv(ph); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci if (!power_thresh_low || !power_thresh_high || 57362306a36Sopenharmony_ci domain_id >= pi->num_domains) 57462306a36Sopenharmony_ci return -EINVAL; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci *power_thresh_low = THRESH_LOW(pi, domain_id); 57762306a36Sopenharmony_ci *power_thresh_high = THRESH_HIGH(pi, domain_id); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci return 0; 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic int 58362306a36Sopenharmony_ciscmi_powercap_measurements_threshold_set(const struct scmi_protocol_handle *ph, 58462306a36Sopenharmony_ci u32 domain_id, u32 power_thresh_low, 58562306a36Sopenharmony_ci u32 power_thresh_high) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci int ret = 0; 58862306a36Sopenharmony_ci struct powercap_info *pi = ph->get_priv(ph); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (domain_id >= pi->num_domains || 59162306a36Sopenharmony_ci power_thresh_low > power_thresh_high) 59262306a36Sopenharmony_ci return -EINVAL; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci /* Anything to do ? */ 59562306a36Sopenharmony_ci if (THRESH_LOW(pi, domain_id) == power_thresh_low && 59662306a36Sopenharmony_ci THRESH_HIGH(pi, domain_id) == power_thresh_high) 59762306a36Sopenharmony_ci return ret; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci pi->states[domain_id].thresholds = 60062306a36Sopenharmony_ci (FIELD_PREP(GENMASK_ULL(31, 0), power_thresh_low) | 60162306a36Sopenharmony_ci FIELD_PREP(GENMASK_ULL(63, 32), power_thresh_high)); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* Update thresholds if notification already enabled */ 60462306a36Sopenharmony_ci if (pi->states[domain_id].meas_notif_enabled) 60562306a36Sopenharmony_ci ret = scmi_powercap_notify(ph, domain_id, 60662306a36Sopenharmony_ci POWERCAP_MEASUREMENTS_NOTIFY, 60762306a36Sopenharmony_ci true); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci return ret; 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_cistatic int scmi_powercap_cap_enable_set(const struct scmi_protocol_handle *ph, 61362306a36Sopenharmony_ci u32 domain_id, bool enable) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci int ret; 61662306a36Sopenharmony_ci u32 power_cap; 61762306a36Sopenharmony_ci struct powercap_info *pi = ph->get_priv(ph); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (PROTOCOL_REV_MAJOR(pi->version) < 0x2) 62062306a36Sopenharmony_ci return -EINVAL; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (enable == pi->states[domain_id].enabled) 62362306a36Sopenharmony_ci return 0; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (enable) { 62662306a36Sopenharmony_ci /* Cannot enable with a zero powercap. */ 62762306a36Sopenharmony_ci if (!pi->states[domain_id].last_pcap) 62862306a36Sopenharmony_ci return -EINVAL; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci ret = __scmi_powercap_cap_set(ph, pi, domain_id, 63162306a36Sopenharmony_ci pi->states[domain_id].last_pcap, 63262306a36Sopenharmony_ci true); 63362306a36Sopenharmony_ci } else { 63462306a36Sopenharmony_ci ret = __scmi_powercap_cap_set(ph, pi, domain_id, 0, true); 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci if (ret) 63862306a36Sopenharmony_ci return ret; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* 64162306a36Sopenharmony_ci * Update our internal state to reflect final platform state: the SCMI 64262306a36Sopenharmony_ci * server could have ignored a disable request and kept enforcing some 64362306a36Sopenharmony_ci * powercap limit requested by other agents. 64462306a36Sopenharmony_ci */ 64562306a36Sopenharmony_ci ret = scmi_powercap_cap_get(ph, domain_id, &power_cap); 64662306a36Sopenharmony_ci if (!ret) 64762306a36Sopenharmony_ci pi->states[domain_id].enabled = !!power_cap; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci return ret; 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cistatic int scmi_powercap_cap_enable_get(const struct scmi_protocol_handle *ph, 65362306a36Sopenharmony_ci u32 domain_id, bool *enable) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci int ret; 65662306a36Sopenharmony_ci u32 power_cap; 65762306a36Sopenharmony_ci struct powercap_info *pi = ph->get_priv(ph); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci *enable = true; 66062306a36Sopenharmony_ci if (PROTOCOL_REV_MAJOR(pi->version) < 0x2) 66162306a36Sopenharmony_ci return 0; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci /* 66462306a36Sopenharmony_ci * Report always real platform state; platform could have ignored 66562306a36Sopenharmony_ci * a previous disable request. Default true on any error. 66662306a36Sopenharmony_ci */ 66762306a36Sopenharmony_ci ret = scmi_powercap_cap_get(ph, domain_id, &power_cap); 66862306a36Sopenharmony_ci if (!ret) 66962306a36Sopenharmony_ci *enable = !!power_cap; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci /* Update internal state with current real platform state */ 67262306a36Sopenharmony_ci pi->states[domain_id].enabled = *enable; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci return 0; 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic const struct scmi_powercap_proto_ops powercap_proto_ops = { 67862306a36Sopenharmony_ci .num_domains_get = scmi_powercap_num_domains_get, 67962306a36Sopenharmony_ci .info_get = scmi_powercap_dom_info_get, 68062306a36Sopenharmony_ci .cap_get = scmi_powercap_cap_get, 68162306a36Sopenharmony_ci .cap_set = scmi_powercap_cap_set, 68262306a36Sopenharmony_ci .cap_enable_set = scmi_powercap_cap_enable_set, 68362306a36Sopenharmony_ci .cap_enable_get = scmi_powercap_cap_enable_get, 68462306a36Sopenharmony_ci .pai_get = scmi_powercap_pai_get, 68562306a36Sopenharmony_ci .pai_set = scmi_powercap_pai_set, 68662306a36Sopenharmony_ci .measurements_get = scmi_powercap_measurements_get, 68762306a36Sopenharmony_ci .measurements_threshold_set = scmi_powercap_measurements_threshold_set, 68862306a36Sopenharmony_ci .measurements_threshold_get = scmi_powercap_measurements_threshold_get, 68962306a36Sopenharmony_ci}; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic void scmi_powercap_domain_init_fc(const struct scmi_protocol_handle *ph, 69262306a36Sopenharmony_ci u32 domain, struct scmi_fc_info **p_fc) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci struct scmi_fc_info *fc; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci fc = devm_kcalloc(ph->dev, POWERCAP_FC_MAX, sizeof(*fc), GFP_KERNEL); 69762306a36Sopenharmony_ci if (!fc) 69862306a36Sopenharmony_ci return; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, 70162306a36Sopenharmony_ci POWERCAP_CAP_SET, 4, domain, 70262306a36Sopenharmony_ci &fc[POWERCAP_FC_CAP].set_addr, 70362306a36Sopenharmony_ci &fc[POWERCAP_FC_CAP].set_db); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, 70662306a36Sopenharmony_ci POWERCAP_CAP_GET, 4, domain, 70762306a36Sopenharmony_ci &fc[POWERCAP_FC_CAP].get_addr, NULL); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, 71062306a36Sopenharmony_ci POWERCAP_PAI_SET, 4, domain, 71162306a36Sopenharmony_ci &fc[POWERCAP_FC_PAI].set_addr, 71262306a36Sopenharmony_ci &fc[POWERCAP_FC_PAI].set_db); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, 71562306a36Sopenharmony_ci POWERCAP_PAI_GET, 4, domain, 71662306a36Sopenharmony_ci &fc[POWERCAP_FC_PAI].get_addr, NULL); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci *p_fc = fc; 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_cistatic int scmi_powercap_notify(const struct scmi_protocol_handle *ph, 72262306a36Sopenharmony_ci u32 domain, int message_id, bool enable) 72362306a36Sopenharmony_ci{ 72462306a36Sopenharmony_ci int ret; 72562306a36Sopenharmony_ci struct scmi_xfer *t; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci switch (message_id) { 72862306a36Sopenharmony_ci case POWERCAP_CAP_NOTIFY: 72962306a36Sopenharmony_ci { 73062306a36Sopenharmony_ci struct scmi_msg_powercap_notify_cap *notify; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci ret = ph->xops->xfer_get_init(ph, message_id, 73362306a36Sopenharmony_ci sizeof(*notify), 0, &t); 73462306a36Sopenharmony_ci if (ret) 73562306a36Sopenharmony_ci return ret; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci notify = t->tx.buf; 73862306a36Sopenharmony_ci notify->domain = cpu_to_le32(domain); 73962306a36Sopenharmony_ci notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0); 74062306a36Sopenharmony_ci break; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci case POWERCAP_MEASUREMENTS_NOTIFY: 74362306a36Sopenharmony_ci { 74462306a36Sopenharmony_ci u32 low, high; 74562306a36Sopenharmony_ci struct scmi_msg_powercap_notify_thresh *notify; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci /* 74862306a36Sopenharmony_ci * Note that we have to pick the most recently configured 74962306a36Sopenharmony_ci * thresholds to build a proper POWERCAP_MEASUREMENTS_NOTIFY 75062306a36Sopenharmony_ci * enable request and we fail, complaining, if no thresholds 75162306a36Sopenharmony_ci * were ever set, since this is an indication the API has been 75262306a36Sopenharmony_ci * used wrongly. 75362306a36Sopenharmony_ci */ 75462306a36Sopenharmony_ci ret = scmi_powercap_measurements_threshold_get(ph, domain, 75562306a36Sopenharmony_ci &low, &high); 75662306a36Sopenharmony_ci if (ret) 75762306a36Sopenharmony_ci return ret; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci if (enable && !low && !high) { 76062306a36Sopenharmony_ci dev_err(ph->dev, 76162306a36Sopenharmony_ci "Invalid Measurements Notify thresholds: %u/%u\n", 76262306a36Sopenharmony_ci low, high); 76362306a36Sopenharmony_ci return -EINVAL; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci ret = ph->xops->xfer_get_init(ph, message_id, 76762306a36Sopenharmony_ci sizeof(*notify), 0, &t); 76862306a36Sopenharmony_ci if (ret) 76962306a36Sopenharmony_ci return ret; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci notify = t->tx.buf; 77262306a36Sopenharmony_ci notify->domain = cpu_to_le32(domain); 77362306a36Sopenharmony_ci notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0); 77462306a36Sopenharmony_ci notify->power_thresh_low = cpu_to_le32(low); 77562306a36Sopenharmony_ci notify->power_thresh_high = cpu_to_le32(high); 77662306a36Sopenharmony_ci break; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci default: 77962306a36Sopenharmony_ci return -EINVAL; 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci ret = ph->xops->do_xfer(ph, t); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci ph->xops->xfer_put(ph, t); 78562306a36Sopenharmony_ci return ret; 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_cistatic int 78962306a36Sopenharmony_ciscmi_powercap_set_notify_enabled(const struct scmi_protocol_handle *ph, 79062306a36Sopenharmony_ci u8 evt_id, u32 src_id, bool enable) 79162306a36Sopenharmony_ci{ 79262306a36Sopenharmony_ci int ret, cmd_id; 79362306a36Sopenharmony_ci struct powercap_info *pi = ph->get_priv(ph); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci if (evt_id >= ARRAY_SIZE(evt_2_cmd) || src_id >= pi->num_domains) 79662306a36Sopenharmony_ci return -EINVAL; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci cmd_id = evt_2_cmd[evt_id]; 79962306a36Sopenharmony_ci ret = scmi_powercap_notify(ph, src_id, cmd_id, enable); 80062306a36Sopenharmony_ci if (ret) 80162306a36Sopenharmony_ci pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n", 80262306a36Sopenharmony_ci evt_id, src_id, ret); 80362306a36Sopenharmony_ci else if (cmd_id == POWERCAP_MEASUREMENTS_NOTIFY) 80462306a36Sopenharmony_ci /* 80562306a36Sopenharmony_ci * On success save the current notification enabled state, so 80662306a36Sopenharmony_ci * as to be able to properly update the notification thresholds 80762306a36Sopenharmony_ci * when they are modified on a domain for which measurement 80862306a36Sopenharmony_ci * notifications were currently enabled. 80962306a36Sopenharmony_ci * 81062306a36Sopenharmony_ci * This is needed because the SCMI Notification core machinery 81162306a36Sopenharmony_ci * and API does not support passing per-notification custom 81262306a36Sopenharmony_ci * arguments at callback registration time. 81362306a36Sopenharmony_ci * 81462306a36Sopenharmony_ci * Note that this can be done here with a simple flag since the 81562306a36Sopenharmony_ci * SCMI core Notifications code takes care of keeping proper 81662306a36Sopenharmony_ci * per-domain enables refcounting, so that this helper function 81762306a36Sopenharmony_ci * will be called only once (for enables) when the first user 81862306a36Sopenharmony_ci * registers a callback on this domain and once more (disable) 81962306a36Sopenharmony_ci * when the last user de-registers its callback. 82062306a36Sopenharmony_ci */ 82162306a36Sopenharmony_ci pi->states[src_id].meas_notif_enabled = enable; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci return ret; 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_cistatic void * 82762306a36Sopenharmony_ciscmi_powercap_fill_custom_report(const struct scmi_protocol_handle *ph, 82862306a36Sopenharmony_ci u8 evt_id, ktime_t timestamp, 82962306a36Sopenharmony_ci const void *payld, size_t payld_sz, 83062306a36Sopenharmony_ci void *report, u32 *src_id) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci void *rep = NULL; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci switch (evt_id) { 83562306a36Sopenharmony_ci case SCMI_EVENT_POWERCAP_CAP_CHANGED: 83662306a36Sopenharmony_ci { 83762306a36Sopenharmony_ci const struct scmi_powercap_cap_changed_notify_payld *p = payld; 83862306a36Sopenharmony_ci struct scmi_powercap_cap_changed_report *r = report; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci if (sizeof(*p) != payld_sz) 84162306a36Sopenharmony_ci break; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci r->timestamp = timestamp; 84462306a36Sopenharmony_ci r->agent_id = le32_to_cpu(p->agent_id); 84562306a36Sopenharmony_ci r->domain_id = le32_to_cpu(p->domain_id); 84662306a36Sopenharmony_ci r->power_cap = le32_to_cpu(p->power_cap); 84762306a36Sopenharmony_ci r->pai = le32_to_cpu(p->pai); 84862306a36Sopenharmony_ci *src_id = r->domain_id; 84962306a36Sopenharmony_ci rep = r; 85062306a36Sopenharmony_ci break; 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci case SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED: 85362306a36Sopenharmony_ci { 85462306a36Sopenharmony_ci const struct scmi_powercap_meas_changed_notify_payld *p = payld; 85562306a36Sopenharmony_ci struct scmi_powercap_meas_changed_report *r = report; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci if (sizeof(*p) != payld_sz) 85862306a36Sopenharmony_ci break; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci r->timestamp = timestamp; 86162306a36Sopenharmony_ci r->agent_id = le32_to_cpu(p->agent_id); 86262306a36Sopenharmony_ci r->domain_id = le32_to_cpu(p->domain_id); 86362306a36Sopenharmony_ci r->power = le32_to_cpu(p->power); 86462306a36Sopenharmony_ci *src_id = r->domain_id; 86562306a36Sopenharmony_ci rep = r; 86662306a36Sopenharmony_ci break; 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci default: 86962306a36Sopenharmony_ci break; 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci return rep; 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic int 87662306a36Sopenharmony_ciscmi_powercap_get_num_sources(const struct scmi_protocol_handle *ph) 87762306a36Sopenharmony_ci{ 87862306a36Sopenharmony_ci struct powercap_info *pi = ph->get_priv(ph); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci if (!pi) 88162306a36Sopenharmony_ci return -EINVAL; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci return pi->num_domains; 88462306a36Sopenharmony_ci} 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_cistatic const struct scmi_event powercap_events[] = { 88762306a36Sopenharmony_ci { 88862306a36Sopenharmony_ci .id = SCMI_EVENT_POWERCAP_CAP_CHANGED, 88962306a36Sopenharmony_ci .max_payld_sz = 89062306a36Sopenharmony_ci sizeof(struct scmi_powercap_cap_changed_notify_payld), 89162306a36Sopenharmony_ci .max_report_sz = 89262306a36Sopenharmony_ci sizeof(struct scmi_powercap_cap_changed_report), 89362306a36Sopenharmony_ci }, 89462306a36Sopenharmony_ci { 89562306a36Sopenharmony_ci .id = SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED, 89662306a36Sopenharmony_ci .max_payld_sz = 89762306a36Sopenharmony_ci sizeof(struct scmi_powercap_meas_changed_notify_payld), 89862306a36Sopenharmony_ci .max_report_sz = 89962306a36Sopenharmony_ci sizeof(struct scmi_powercap_meas_changed_report), 90062306a36Sopenharmony_ci }, 90162306a36Sopenharmony_ci}; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_cistatic const struct scmi_event_ops powercap_event_ops = { 90462306a36Sopenharmony_ci .get_num_sources = scmi_powercap_get_num_sources, 90562306a36Sopenharmony_ci .set_notify_enabled = scmi_powercap_set_notify_enabled, 90662306a36Sopenharmony_ci .fill_custom_report = scmi_powercap_fill_custom_report, 90762306a36Sopenharmony_ci}; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_cistatic const struct scmi_protocol_events powercap_protocol_events = { 91062306a36Sopenharmony_ci .queue_sz = SCMI_PROTO_QUEUE_SZ, 91162306a36Sopenharmony_ci .ops = &powercap_event_ops, 91262306a36Sopenharmony_ci .evts = powercap_events, 91362306a36Sopenharmony_ci .num_events = ARRAY_SIZE(powercap_events), 91462306a36Sopenharmony_ci}; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cistatic int 91762306a36Sopenharmony_ciscmi_powercap_protocol_init(const struct scmi_protocol_handle *ph) 91862306a36Sopenharmony_ci{ 91962306a36Sopenharmony_ci int domain, ret; 92062306a36Sopenharmony_ci u32 version; 92162306a36Sopenharmony_ci struct powercap_info *pinfo; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci ret = ph->xops->version_get(ph, &version); 92462306a36Sopenharmony_ci if (ret) 92562306a36Sopenharmony_ci return ret; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci dev_dbg(ph->dev, "Powercap Version %d.%d\n", 92862306a36Sopenharmony_ci PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL); 93162306a36Sopenharmony_ci if (!pinfo) 93262306a36Sopenharmony_ci return -ENOMEM; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci ret = scmi_powercap_attributes_get(ph, pinfo); 93562306a36Sopenharmony_ci if (ret) 93662306a36Sopenharmony_ci return ret; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci pinfo->powercaps = devm_kcalloc(ph->dev, pinfo->num_domains, 93962306a36Sopenharmony_ci sizeof(*pinfo->powercaps), 94062306a36Sopenharmony_ci GFP_KERNEL); 94162306a36Sopenharmony_ci if (!pinfo->powercaps) 94262306a36Sopenharmony_ci return -ENOMEM; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci pinfo->states = devm_kcalloc(ph->dev, pinfo->num_domains, 94562306a36Sopenharmony_ci sizeof(*pinfo->states), GFP_KERNEL); 94662306a36Sopenharmony_ci if (!pinfo->states) 94762306a36Sopenharmony_ci return -ENOMEM; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci /* 95062306a36Sopenharmony_ci * Note that any failure in retrieving any domain attribute leads to 95162306a36Sopenharmony_ci * the whole Powercap protocol initialization failure: this way the 95262306a36Sopenharmony_ci * reported Powercap domains are all assured, when accessed, to be well 95362306a36Sopenharmony_ci * formed and correlated by sane parent-child relationship (if any). 95462306a36Sopenharmony_ci */ 95562306a36Sopenharmony_ci for (domain = 0; domain < pinfo->num_domains; domain++) { 95662306a36Sopenharmony_ci ret = scmi_powercap_domain_attributes_get(ph, pinfo, domain); 95762306a36Sopenharmony_ci if (ret) 95862306a36Sopenharmony_ci return ret; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci if (pinfo->powercaps[domain].fastchannels) 96162306a36Sopenharmony_ci scmi_powercap_domain_init_fc(ph, domain, 96262306a36Sopenharmony_ci &pinfo->powercaps[domain].fc_info); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci /* Grab initial state when disable is supported. */ 96562306a36Sopenharmony_ci if (PROTOCOL_REV_MAJOR(version) >= 0x2) { 96662306a36Sopenharmony_ci ret = __scmi_powercap_cap_get(ph, 96762306a36Sopenharmony_ci &pinfo->powercaps[domain], 96862306a36Sopenharmony_ci &pinfo->states[domain].last_pcap); 96962306a36Sopenharmony_ci if (ret) 97062306a36Sopenharmony_ci return ret; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci pinfo->states[domain].enabled = 97362306a36Sopenharmony_ci !!pinfo->states[domain].last_pcap; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci pinfo->version = version; 97862306a36Sopenharmony_ci return ph->set_priv(ph, pinfo); 97962306a36Sopenharmony_ci} 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_cistatic const struct scmi_protocol scmi_powercap = { 98262306a36Sopenharmony_ci .id = SCMI_PROTOCOL_POWERCAP, 98362306a36Sopenharmony_ci .owner = THIS_MODULE, 98462306a36Sopenharmony_ci .instance_init = &scmi_powercap_protocol_init, 98562306a36Sopenharmony_ci .ops = &powercap_proto_ops, 98662306a36Sopenharmony_ci .events = &powercap_protocol_events, 98762306a36Sopenharmony_ci}; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ciDEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(powercap, scmi_powercap) 990