| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * System Control and Management Interface (SCMI) Powercap Protocol
- *
- * Copyright (C) 2022 ARM Ltd.
- */
- #define pr_fmt(fmt) "SCMI Notifications POWERCAP - " fmt
- #include <linux/bitfield.h>
- #include <linux/io.h>
- #include <linux/module.h>
- #include <linux/scmi_protocol.h>
- #include <trace/events/scmi.h>
- #include "protocols.h"
- #include "notify.h"
- /* Updated only after ALL the mandatory features for that version are merged */
- #define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20000
- enum scmi_powercap_protocol_cmd {
- POWERCAP_DOMAIN_ATTRIBUTES = 0x3,
- POWERCAP_CAP_GET = 0x4,
- POWERCAP_CAP_SET = 0x5,
- POWERCAP_PAI_GET = 0x6,
- POWERCAP_PAI_SET = 0x7,
- POWERCAP_DOMAIN_NAME_GET = 0x8,
- POWERCAP_MEASUREMENTS_GET = 0x9,
- POWERCAP_CAP_NOTIFY = 0xa,
- POWERCAP_MEASUREMENTS_NOTIFY = 0xb,
- POWERCAP_DESCRIBE_FASTCHANNEL = 0xc,
- };
- enum {
- POWERCAP_FC_CAP,
- POWERCAP_FC_PAI,
- POWERCAP_FC_MAX,
- };
- struct scmi_msg_resp_powercap_domain_attributes {
- __le32 attributes;
- #define SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(x) ((x) & BIT(31))
- #define SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(x) ((x) & BIT(30))
- #define SUPPORTS_ASYNC_POWERCAP_CAP_SET(x) ((x) & BIT(29))
- #define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(28))
- #define SUPPORTS_POWERCAP_CAP_CONFIGURATION(x) ((x) & BIT(27))
- #define SUPPORTS_POWERCAP_MONITORING(x) ((x) & BIT(26))
- #define SUPPORTS_POWERCAP_PAI_CONFIGURATION(x) ((x) & BIT(25))
- #define SUPPORTS_POWERCAP_FASTCHANNELS(x) ((x) & BIT(22))
- #define POWERCAP_POWER_UNIT(x) \
- (FIELD_GET(GENMASK(24, 23), (x)))
- #define SUPPORTS_POWER_UNITS_MW(x) \
- (POWERCAP_POWER_UNIT(x) == 0x2)
- #define SUPPORTS_POWER_UNITS_UW(x) \
- (POWERCAP_POWER_UNIT(x) == 0x1)
- u8 name[SCMI_SHORT_NAME_MAX_SIZE];
- __le32 min_pai;
- __le32 max_pai;
- __le32 pai_step;
- __le32 min_power_cap;
- __le32 max_power_cap;
- __le32 power_cap_step;
- __le32 sustainable_power;
- __le32 accuracy;
- __le32 parent_id;
- };
- struct scmi_msg_powercap_set_cap_or_pai {
- __le32 domain;
- __le32 flags;
- #define CAP_SET_ASYNC BIT(1)
- #define CAP_SET_IGNORE_DRESP BIT(0)
- __le32 value;
- };
- struct scmi_msg_resp_powercap_cap_set_complete {
- __le32 domain;
- __le32 power_cap;
- };
- struct scmi_msg_resp_powercap_meas_get {
- __le32 power;
- __le32 pai;
- };
- struct scmi_msg_powercap_notify_cap {
- __le32 domain;
- __le32 notify_enable;
- };
- struct scmi_msg_powercap_notify_thresh {
- __le32 domain;
- __le32 notify_enable;
- __le32 power_thresh_low;
- __le32 power_thresh_high;
- };
- struct scmi_powercap_cap_changed_notify_payld {
- __le32 agent_id;
- __le32 domain_id;
- __le32 power_cap;
- __le32 pai;
- };
- struct scmi_powercap_meas_changed_notify_payld {
- __le32 agent_id;
- __le32 domain_id;
- __le32 power;
- };
- struct scmi_powercap_state {
- bool enabled;
- u32 last_pcap;
- bool meas_notif_enabled;
- u64 thresholds;
- #define THRESH_LOW(p, id) \
- (lower_32_bits((p)->states[(id)].thresholds))
- #define THRESH_HIGH(p, id) \
- (upper_32_bits((p)->states[(id)].thresholds))
- };
- struct powercap_info {
- int num_domains;
- bool notify_cap_cmd;
- bool notify_measurements_cmd;
- struct scmi_powercap_state *states;
- struct scmi_powercap_info *powercaps;
- };
- static enum scmi_powercap_protocol_cmd evt_2_cmd[] = {
- POWERCAP_CAP_NOTIFY,
- POWERCAP_MEASUREMENTS_NOTIFY,
- };
- static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,
- u32 domain, int message_id, bool enable);
- static int
- scmi_powercap_attributes_get(const struct scmi_protocol_handle *ph,
- struct powercap_info *pi)
- {
- int ret;
- struct scmi_xfer *t;
- ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0,
- sizeof(u32), &t);
- if (ret)
- return ret;
- ret = ph->xops->do_xfer(ph, t);
- if (!ret) {
- u32 attributes;
- attributes = get_unaligned_le32(t->rx.buf);
- pi->num_domains = FIELD_GET(GENMASK(15, 0), attributes);
- }
- ph->xops->xfer_put(ph, t);
- if (!ret) {
- if (!ph->hops->protocol_msg_check(ph,
- POWERCAP_CAP_NOTIFY, NULL))
- pi->notify_cap_cmd = true;
- if (!ph->hops->protocol_msg_check(ph,
- POWERCAP_MEASUREMENTS_NOTIFY,
- NULL))
- pi->notify_measurements_cmd = true;
- }
- return ret;
- }
- static inline int
- scmi_powercap_validate(unsigned int min_val, unsigned int max_val,
- unsigned int step_val, bool configurable)
- {
- if (!min_val || !max_val)
- return -EPROTO;
- if ((configurable && min_val == max_val) ||
- (!configurable && min_val != max_val))
- return -EPROTO;
- if (min_val != max_val && !step_val)
- return -EPROTO;
- return 0;
- }
- static int
- scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
- struct powercap_info *pinfo, u32 domain)
- {
- int ret;
- u32 flags;
- struct scmi_xfer *t;
- struct scmi_powercap_info *dom_info = pinfo->powercaps + domain;
- struct scmi_msg_resp_powercap_domain_attributes *resp;
- ret = ph->xops->xfer_get_init(ph, POWERCAP_DOMAIN_ATTRIBUTES,
- sizeof(domain), sizeof(*resp), &t);
- if (ret)
- return ret;
- put_unaligned_le32(domain, t->tx.buf);
- resp = t->rx.buf;
- ret = ph->xops->do_xfer(ph, t);
- if (!ret) {
- flags = le32_to_cpu(resp->attributes);
- dom_info->id = domain;
- if (pinfo->notify_cap_cmd)
- dom_info->notify_powercap_cap_change =
- SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags);
- if (pinfo->notify_measurements_cmd)
- dom_info->notify_powercap_measurement_change =
- SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
- dom_info->async_powercap_cap_set =
- SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags);
- dom_info->powercap_cap_config =
- SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags);
- dom_info->powercap_monitoring =
- SUPPORTS_POWERCAP_MONITORING(flags);
- dom_info->powercap_pai_config =
- SUPPORTS_POWERCAP_PAI_CONFIGURATION(flags);
- dom_info->powercap_scale_mw =
- SUPPORTS_POWER_UNITS_MW(flags);
- dom_info->powercap_scale_uw =
- SUPPORTS_POWER_UNITS_UW(flags);
- dom_info->fastchannels =
- SUPPORTS_POWERCAP_FASTCHANNELS(flags);
- strscpy(dom_info->name, resp->name, SCMI_SHORT_NAME_MAX_SIZE);
- dom_info->min_pai = le32_to_cpu(resp->min_pai);
- dom_info->max_pai = le32_to_cpu(resp->max_pai);
- dom_info->pai_step = le32_to_cpu(resp->pai_step);
- ret = scmi_powercap_validate(dom_info->min_pai,
- dom_info->max_pai,
- dom_info->pai_step,
- dom_info->powercap_pai_config);
- if (ret) {
- dev_err(ph->dev,
- "Platform reported inconsistent PAI config for domain %d - %s\n",
- dom_info->id, dom_info->name);
- goto clean;
- }
- dom_info->min_power_cap = le32_to_cpu(resp->min_power_cap);
- dom_info->max_power_cap = le32_to_cpu(resp->max_power_cap);
- dom_info->power_cap_step = le32_to_cpu(resp->power_cap_step);
- ret = scmi_powercap_validate(dom_info->min_power_cap,
- dom_info->max_power_cap,
- dom_info->power_cap_step,
- dom_info->powercap_cap_config);
- if (ret) {
- dev_err(ph->dev,
- "Platform reported inconsistent CAP config for domain %d - %s\n",
- dom_info->id, dom_info->name);
- goto clean;
- }
- dom_info->sustainable_power =
- le32_to_cpu(resp->sustainable_power);
- dom_info->accuracy = le32_to_cpu(resp->accuracy);
- dom_info->parent_id = le32_to_cpu(resp->parent_id);
- if (dom_info->parent_id != SCMI_POWERCAP_ROOT_ZONE_ID &&
- (dom_info->parent_id >= pinfo->num_domains ||
- dom_info->parent_id == dom_info->id)) {
- dev_err(ph->dev,
- "Platform reported inconsistent parent ID for domain %d - %s\n",
- dom_info->id, dom_info->name);
- ret = -ENODEV;
- }
- }
- clean:
- ph->xops->xfer_put(ph, t);
- /*
- * If supported overwrite short name with the extended one;
- * on error just carry on and use already provided short name.
- */
- if (!ret && SUPPORTS_EXTENDED_NAMES(flags))
- ph->hops->extended_name_get(ph, POWERCAP_DOMAIN_NAME_GET,
- domain, NULL, dom_info->name,
- SCMI_MAX_STR_SIZE);
- return ret;
- }
- static int scmi_powercap_num_domains_get(const struct scmi_protocol_handle *ph)
- {
- struct powercap_info *pi = ph->get_priv(ph);
- return pi->num_domains;
- }
- static const struct scmi_powercap_info *
- scmi_powercap_dom_info_get(const struct scmi_protocol_handle *ph, u32 domain_id)
- {
- struct powercap_info *pi = ph->get_priv(ph);
- if (domain_id >= pi->num_domains)
- return NULL;
- return pi->powercaps + domain_id;
- }
- static int scmi_powercap_xfer_cap_get(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 *power_cap)
- {
- int ret;
- struct scmi_xfer *t;
- ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_GET, sizeof(u32),
- sizeof(u32), &t);
- if (ret)
- return ret;
- put_unaligned_le32(domain_id, t->tx.buf);
- ret = ph->xops->do_xfer(ph, t);
- if (!ret)
- *power_cap = get_unaligned_le32(t->rx.buf);
- ph->xops->xfer_put(ph, t);
- return ret;
- }
- static int __scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
- const struct scmi_powercap_info *dom,
- u32 *power_cap)
- {
- if (dom->fc_info && dom->fc_info[POWERCAP_FC_CAP].get_addr) {
- *power_cap = ioread32(dom->fc_info[POWERCAP_FC_CAP].get_addr);
- trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_GET,
- dom->id, *power_cap, 0);
- return 0;
- }
- return scmi_powercap_xfer_cap_get(ph, dom->id, power_cap);
- }
- static int scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 *power_cap)
- {
- const struct scmi_powercap_info *dom;
- if (!power_cap)
- return -EINVAL;
- dom = scmi_powercap_dom_info_get(ph, domain_id);
- if (!dom)
- return -EINVAL;
- return __scmi_powercap_cap_get(ph, dom, power_cap);
- }
- static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph,
- const struct scmi_powercap_info *pc,
- u32 power_cap, bool ignore_dresp)
- {
- int ret;
- struct scmi_xfer *t;
- struct scmi_msg_powercap_set_cap_or_pai *msg;
- ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_SET,
- sizeof(*msg), 0, &t);
- if (ret)
- return ret;
- msg = t->tx.buf;
- msg->domain = cpu_to_le32(pc->id);
- msg->flags =
- cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, pc->async_powercap_cap_set) |
- FIELD_PREP(CAP_SET_IGNORE_DRESP, ignore_dresp));
- msg->value = cpu_to_le32(power_cap);
- if (!pc->async_powercap_cap_set || ignore_dresp) {
- ret = ph->xops->do_xfer(ph, t);
- } else {
- ret = ph->xops->do_xfer_with_response(ph, t);
- if (!ret) {
- struct scmi_msg_resp_powercap_cap_set_complete *resp;
- resp = t->rx.buf;
- if (le32_to_cpu(resp->domain) == pc->id)
- dev_dbg(ph->dev,
- "Powercap ID %d CAP set async to %u\n",
- pc->id,
- get_unaligned_le32(&resp->power_cap));
- else
- ret = -EPROTO;
- }
- }
- ph->xops->xfer_put(ph, t);
- return ret;
- }
- static int __scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
- struct powercap_info *pi, u32 domain_id,
- u32 power_cap, bool ignore_dresp)
- {
- int ret = -EINVAL;
- const struct scmi_powercap_info *pc;
- pc = scmi_powercap_dom_info_get(ph, domain_id);
- if (!pc || !pc->powercap_cap_config)
- return ret;
- if (power_cap &&
- (power_cap < pc->min_power_cap || power_cap > pc->max_power_cap))
- return ret;
- if (pc->fc_info && pc->fc_info[POWERCAP_FC_CAP].set_addr) {
- struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_CAP];
- iowrite32(power_cap, fci->set_addr);
- ph->hops->fastchannel_db_ring(fci->set_db);
- trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_SET,
- domain_id, power_cap, 0);
- ret = 0;
- } else {
- ret = scmi_powercap_xfer_cap_set(ph, pc, power_cap,
- ignore_dresp);
- }
- /* Save the last explicitly set non-zero powercap value */
- if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2 && !ret && power_cap)
- pi->states[domain_id].last_pcap = power_cap;
- return ret;
- }
- static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 power_cap,
- bool ignore_dresp)
- {
- struct powercap_info *pi = ph->get_priv(ph);
- /*
- * Disallow zero as a possible explicitly requested powercap:
- * there are enable/disable operations for this.
- */
- if (!power_cap)
- return -EINVAL;
- /* Just log the last set request if acting on a disabled domain */
- if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2 &&
- !pi->states[domain_id].enabled) {
- pi->states[domain_id].last_pcap = power_cap;
- return 0;
- }
- return __scmi_powercap_cap_set(ph, pi, domain_id,
- power_cap, ignore_dresp);
- }
- static int scmi_powercap_xfer_pai_get(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 *pai)
- {
- int ret;
- struct scmi_xfer *t;
- ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_GET, sizeof(u32),
- sizeof(u32), &t);
- if (ret)
- return ret;
- put_unaligned_le32(domain_id, t->tx.buf);
- ret = ph->xops->do_xfer(ph, t);
- if (!ret)
- *pai = get_unaligned_le32(t->rx.buf);
- ph->xops->xfer_put(ph, t);
- return ret;
- }
- static int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 *pai)
- {
- struct scmi_powercap_info *dom;
- struct powercap_info *pi = ph->get_priv(ph);
- if (!pai || domain_id >= pi->num_domains)
- return -EINVAL;
- dom = pi->powercaps + domain_id;
- if (dom->fc_info && dom->fc_info[POWERCAP_FC_PAI].get_addr) {
- *pai = ioread32(dom->fc_info[POWERCAP_FC_PAI].get_addr);
- trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_GET,
- domain_id, *pai, 0);
- return 0;
- }
- return scmi_powercap_xfer_pai_get(ph, domain_id, pai);
- }
- static int scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 pai)
- {
- int ret;
- struct scmi_xfer *t;
- struct scmi_msg_powercap_set_cap_or_pai *msg;
- ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_SET,
- sizeof(*msg), 0, &t);
- if (ret)
- return ret;
- msg = t->tx.buf;
- msg->domain = cpu_to_le32(domain_id);
- msg->flags = cpu_to_le32(0);
- msg->value = cpu_to_le32(pai);
- ret = ph->xops->do_xfer(ph, t);
- ph->xops->xfer_put(ph, t);
- return ret;
- }
- static int scmi_powercap_pai_set(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 pai)
- {
- const struct scmi_powercap_info *pc;
- pc = scmi_powercap_dom_info_get(ph, domain_id);
- if (!pc || !pc->powercap_pai_config || !pai ||
- pai < pc->min_pai || pai > pc->max_pai)
- return -EINVAL;
- if (pc->fc_info && pc->fc_info[POWERCAP_FC_PAI].set_addr) {
- struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_PAI];
- trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_SET,
- domain_id, pai, 0);
- iowrite32(pai, fci->set_addr);
- ph->hops->fastchannel_db_ring(fci->set_db);
- return 0;
- }
- return scmi_powercap_xfer_pai_set(ph, domain_id, pai);
- }
- static int scmi_powercap_measurements_get(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 *average_power,
- u32 *pai)
- {
- int ret;
- struct scmi_xfer *t;
- struct scmi_msg_resp_powercap_meas_get *resp;
- const struct scmi_powercap_info *pc;
- pc = scmi_powercap_dom_info_get(ph, domain_id);
- if (!pc || !pc->powercap_monitoring || !pai || !average_power)
- return -EINVAL;
- ret = ph->xops->xfer_get_init(ph, POWERCAP_MEASUREMENTS_GET,
- sizeof(u32), sizeof(*resp), &t);
- if (ret)
- return ret;
- resp = t->rx.buf;
- put_unaligned_le32(domain_id, t->tx.buf);
- ret = ph->xops->do_xfer(ph, t);
- if (!ret) {
- *average_power = le32_to_cpu(resp->power);
- *pai = le32_to_cpu(resp->pai);
- }
- ph->xops->xfer_put(ph, t);
- return ret;
- }
- static int
- scmi_powercap_measurements_threshold_get(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 *power_thresh_low,
- u32 *power_thresh_high)
- {
- struct powercap_info *pi = ph->get_priv(ph);
- if (!power_thresh_low || !power_thresh_high ||
- domain_id >= pi->num_domains)
- return -EINVAL;
- *power_thresh_low = THRESH_LOW(pi, domain_id);
- *power_thresh_high = THRESH_HIGH(pi, domain_id);
- return 0;
- }
- static int
- scmi_powercap_measurements_threshold_set(const struct scmi_protocol_handle *ph,
- u32 domain_id, u32 power_thresh_low,
- u32 power_thresh_high)
- {
- int ret = 0;
- struct powercap_info *pi = ph->get_priv(ph);
- if (domain_id >= pi->num_domains ||
- power_thresh_low > power_thresh_high)
- return -EINVAL;
- /* Anything to do ? */
- if (THRESH_LOW(pi, domain_id) == power_thresh_low &&
- THRESH_HIGH(pi, domain_id) == power_thresh_high)
- return ret;
- pi->states[domain_id].thresholds =
- (FIELD_PREP(GENMASK_ULL(31, 0), power_thresh_low) |
- FIELD_PREP(GENMASK_ULL(63, 32), power_thresh_high));
- /* Update thresholds if notification already enabled */
- if (pi->states[domain_id].meas_notif_enabled)
- ret = scmi_powercap_notify(ph, domain_id,
- POWERCAP_MEASUREMENTS_NOTIFY,
- true);
- return ret;
- }
- static int scmi_powercap_cap_enable_set(const struct scmi_protocol_handle *ph,
- u32 domain_id, bool enable)
- {
- int ret;
- u32 power_cap;
- struct powercap_info *pi = ph->get_priv(ph);
- if (PROTOCOL_REV_MAJOR(ph->version) < 0x2)
- return -EINVAL;
- if (enable == pi->states[domain_id].enabled)
- return 0;
- if (enable) {
- /* Cannot enable with a zero powercap. */
- if (!pi->states[domain_id].last_pcap)
- return -EINVAL;
- ret = __scmi_powercap_cap_set(ph, pi, domain_id,
- pi->states[domain_id].last_pcap,
- true);
- } else {
- ret = __scmi_powercap_cap_set(ph, pi, domain_id, 0, true);
- }
- if (ret)
- return ret;
- /*
- * Update our internal state to reflect final platform state: the SCMI
- * server could have ignored a disable request and kept enforcing some
- * powercap limit requested by other agents.
- */
- ret = scmi_powercap_cap_get(ph, domain_id, &power_cap);
- if (!ret)
- pi->states[domain_id].enabled = !!power_cap;
- return ret;
- }
- static int scmi_powercap_cap_enable_get(const struct scmi_protocol_handle *ph,
- u32 domain_id, bool *enable)
- {
- int ret;
- u32 power_cap;
- struct powercap_info *pi = ph->get_priv(ph);
- *enable = true;
- if (PROTOCOL_REV_MAJOR(ph->version) < 0x2)
- return 0;
- /*
- * Report always real platform state; platform could have ignored
- * a previous disable request. Default true on any error.
- */
- ret = scmi_powercap_cap_get(ph, domain_id, &power_cap);
- if (!ret)
- *enable = !!power_cap;
- /* Update internal state with current real platform state */
- pi->states[domain_id].enabled = *enable;
- return 0;
- }
- static const struct scmi_powercap_proto_ops powercap_proto_ops = {
- .num_domains_get = scmi_powercap_num_domains_get,
- .info_get = scmi_powercap_dom_info_get,
- .cap_get = scmi_powercap_cap_get,
- .cap_set = scmi_powercap_cap_set,
- .cap_enable_set = scmi_powercap_cap_enable_set,
- .cap_enable_get = scmi_powercap_cap_enable_get,
- .pai_get = scmi_powercap_pai_get,
- .pai_set = scmi_powercap_pai_set,
- .measurements_get = scmi_powercap_measurements_get,
- .measurements_threshold_set = scmi_powercap_measurements_threshold_set,
- .measurements_threshold_get = scmi_powercap_measurements_threshold_get,
- };
- static void scmi_powercap_domain_init_fc(const struct scmi_protocol_handle *ph,
- u32 domain, struct scmi_fc_info **p_fc)
- {
- struct scmi_fc_info *fc;
- fc = devm_kcalloc(ph->dev, POWERCAP_FC_MAX, sizeof(*fc), GFP_KERNEL);
- if (!fc)
- return;
- ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
- POWERCAP_CAP_SET, 4, domain,
- &fc[POWERCAP_FC_CAP].set_addr,
- &fc[POWERCAP_FC_CAP].set_db,
- &fc[POWERCAP_FC_CAP].rate_limit);
- ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
- POWERCAP_CAP_GET, 4, domain,
- &fc[POWERCAP_FC_CAP].get_addr, NULL,
- &fc[POWERCAP_FC_CAP].rate_limit);
- ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
- POWERCAP_PAI_SET, 4, domain,
- &fc[POWERCAP_FC_PAI].set_addr,
- &fc[POWERCAP_FC_PAI].set_db,
- &fc[POWERCAP_FC_PAI].rate_limit);
- ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
- POWERCAP_PAI_GET, 4, domain,
- &fc[POWERCAP_FC_PAI].get_addr, NULL,
- &fc[POWERCAP_FC_PAI].rate_limit);
- *p_fc = fc;
- }
- static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,
- u32 domain, int message_id, bool enable)
- {
- int ret;
- struct scmi_xfer *t;
- switch (message_id) {
- case POWERCAP_CAP_NOTIFY:
- {
- struct scmi_msg_powercap_notify_cap *notify;
- ret = ph->xops->xfer_get_init(ph, message_id,
- sizeof(*notify), 0, &t);
- if (ret)
- return ret;
- notify = t->tx.buf;
- notify->domain = cpu_to_le32(domain);
- notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0);
- break;
- }
- case POWERCAP_MEASUREMENTS_NOTIFY:
- {
- u32 low, high;
- struct scmi_msg_powercap_notify_thresh *notify;
- /*
- * Note that we have to pick the most recently configured
- * thresholds to build a proper POWERCAP_MEASUREMENTS_NOTIFY
- * enable request and we fail, complaining, if no thresholds
- * were ever set, since this is an indication the API has been
- * used wrongly.
- */
- ret = scmi_powercap_measurements_threshold_get(ph, domain,
- &low, &high);
- if (ret)
- return ret;
- if (enable && !low && !high) {
- dev_err(ph->dev,
- "Invalid Measurements Notify thresholds: %u/%u\n",
- low, high);
- return -EINVAL;
- }
- ret = ph->xops->xfer_get_init(ph, message_id,
- sizeof(*notify), 0, &t);
- if (ret)
- return ret;
- notify = t->tx.buf;
- notify->domain = cpu_to_le32(domain);
- notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0);
- notify->power_thresh_low = cpu_to_le32(low);
- notify->power_thresh_high = cpu_to_le32(high);
- break;
- }
- default:
- return -EINVAL;
- }
- ret = ph->xops->do_xfer(ph, t);
- ph->xops->xfer_put(ph, t);
- return ret;
- }
- static bool
- scmi_powercap_notify_supported(const struct scmi_protocol_handle *ph,
- u8 evt_id, u32 src_id)
- {
- bool supported = false;
- const struct scmi_powercap_info *dom_info;
- struct powercap_info *pi = ph->get_priv(ph);
- if (evt_id >= ARRAY_SIZE(evt_2_cmd) || src_id >= pi->num_domains)
- return false;
- dom_info = pi->powercaps + src_id;
- if (evt_id == SCMI_EVENT_POWERCAP_CAP_CHANGED)
- supported = dom_info->notify_powercap_cap_change;
- else if (evt_id == SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED)
- supported = dom_info->notify_powercap_measurement_change;
- return supported;
- }
- static int
- scmi_powercap_set_notify_enabled(const struct scmi_protocol_handle *ph,
- u8 evt_id, u32 src_id, bool enable)
- {
- int ret, cmd_id;
- struct powercap_info *pi = ph->get_priv(ph);
- if (evt_id >= ARRAY_SIZE(evt_2_cmd) || src_id >= pi->num_domains)
- return -EINVAL;
- cmd_id = evt_2_cmd[evt_id];
- ret = scmi_powercap_notify(ph, src_id, cmd_id, enable);
- if (ret)
- pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n",
- evt_id, src_id, ret);
- else if (cmd_id == POWERCAP_MEASUREMENTS_NOTIFY)
- /*
- * On success save the current notification enabled state, so
- * as to be able to properly update the notification thresholds
- * when they are modified on a domain for which measurement
- * notifications were currently enabled.
- *
- * This is needed because the SCMI Notification core machinery
- * and API does not support passing per-notification custom
- * arguments at callback registration time.
- *
- * Note that this can be done here with a simple flag since the
- * SCMI core Notifications code takes care of keeping proper
- * per-domain enables refcounting, so that this helper function
- * will be called only once (for enables) when the first user
- * registers a callback on this domain and once more (disable)
- * when the last user de-registers its callback.
- */
- pi->states[src_id].meas_notif_enabled = enable;
- return ret;
- }
- static void *
- scmi_powercap_fill_custom_report(const struct scmi_protocol_handle *ph,
- u8 evt_id, ktime_t timestamp,
- const void *payld, size_t payld_sz,
- void *report, u32 *src_id)
- {
- void *rep = NULL;
- switch (evt_id) {
- case SCMI_EVENT_POWERCAP_CAP_CHANGED:
- {
- const struct scmi_powercap_cap_changed_notify_payld *p = payld;
- struct scmi_powercap_cap_changed_report *r = report;
- if (sizeof(*p) != payld_sz)
- break;
- r->timestamp = timestamp;
- r->agent_id = le32_to_cpu(p->agent_id);
- r->domain_id = le32_to_cpu(p->domain_id);
- r->power_cap = le32_to_cpu(p->power_cap);
- r->pai = le32_to_cpu(p->pai);
- *src_id = r->domain_id;
- rep = r;
- break;
- }
- case SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED:
- {
- const struct scmi_powercap_meas_changed_notify_payld *p = payld;
- struct scmi_powercap_meas_changed_report *r = report;
- if (sizeof(*p) != payld_sz)
- break;
- r->timestamp = timestamp;
- r->agent_id = le32_to_cpu(p->agent_id);
- r->domain_id = le32_to_cpu(p->domain_id);
- r->power = le32_to_cpu(p->power);
- *src_id = r->domain_id;
- rep = r;
- break;
- }
- default:
- break;
- }
- return rep;
- }
- static int
- scmi_powercap_get_num_sources(const struct scmi_protocol_handle *ph)
- {
- struct powercap_info *pi = ph->get_priv(ph);
- if (!pi)
- return -EINVAL;
- return pi->num_domains;
- }
- static const struct scmi_event powercap_events[] = {
- {
- .id = SCMI_EVENT_POWERCAP_CAP_CHANGED,
- .max_payld_sz =
- sizeof(struct scmi_powercap_cap_changed_notify_payld),
- .max_report_sz =
- sizeof(struct scmi_powercap_cap_changed_report),
- },
- {
- .id = SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED,
- .max_payld_sz =
- sizeof(struct scmi_powercap_meas_changed_notify_payld),
- .max_report_sz =
- sizeof(struct scmi_powercap_meas_changed_report),
- },
- };
- static const struct scmi_event_ops powercap_event_ops = {
- .is_notify_supported = scmi_powercap_notify_supported,
- .get_num_sources = scmi_powercap_get_num_sources,
- .set_notify_enabled = scmi_powercap_set_notify_enabled,
- .fill_custom_report = scmi_powercap_fill_custom_report,
- };
- static const struct scmi_protocol_events powercap_protocol_events = {
- .queue_sz = SCMI_PROTO_QUEUE_SZ,
- .ops = &powercap_event_ops,
- .evts = powercap_events,
- .num_events = ARRAY_SIZE(powercap_events),
- };
- static int
- scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
- {
- int domain, ret;
- struct powercap_info *pinfo;
- dev_dbg(ph->dev, "Powercap Version %d.%d\n",
- PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
- pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
- if (!pinfo)
- return -ENOMEM;
- ret = scmi_powercap_attributes_get(ph, pinfo);
- if (ret)
- return ret;
- pinfo->powercaps = devm_kcalloc(ph->dev, pinfo->num_domains,
- sizeof(*pinfo->powercaps),
- GFP_KERNEL);
- if (!pinfo->powercaps)
- return -ENOMEM;
- pinfo->states = devm_kcalloc(ph->dev, pinfo->num_domains,
- sizeof(*pinfo->states), GFP_KERNEL);
- if (!pinfo->states)
- return -ENOMEM;
- /*
- * Note that any failure in retrieving any domain attribute leads to
- * the whole Powercap protocol initialization failure: this way the
- * reported Powercap domains are all assured, when accessed, to be well
- * formed and correlated by sane parent-child relationship (if any).
- */
- for (domain = 0; domain < pinfo->num_domains; domain++) {
- ret = scmi_powercap_domain_attributes_get(ph, pinfo, domain);
- if (ret)
- return ret;
- if (pinfo->powercaps[domain].fastchannels)
- scmi_powercap_domain_init_fc(ph, domain,
- &pinfo->powercaps[domain].fc_info);
- /* Grab initial state when disable is supported. */
- if (PROTOCOL_REV_MAJOR(ph->version) >= 0x2) {
- ret = __scmi_powercap_cap_get(ph,
- &pinfo->powercaps[domain],
- &pinfo->states[domain].last_pcap);
- if (ret)
- return ret;
- pinfo->states[domain].enabled =
- !!pinfo->states[domain].last_pcap;
- }
- }
- return ph->set_priv(ph, pinfo);
- }
- static const struct scmi_protocol scmi_powercap = {
- .id = SCMI_PROTOCOL_POWERCAP,
- .owner = THIS_MODULE,
- .instance_init = &scmi_powercap_protocol_init,
- .ops = &powercap_proto_ops,
- .events = &powercap_protocol_events,
- .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
- };
- DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(powercap, scmi_powercap)
|