10316f99cSCristian Marussi // SPDX-License-Identifier: GPL-2.0
20316f99cSCristian Marussi /*
30316f99cSCristian Marussi * System Control and Management Interface (SCMI) Powercap Protocol
40316f99cSCristian Marussi *
50316f99cSCristian Marussi * Copyright (C) 2022 ARM Ltd.
60316f99cSCristian Marussi */
70316f99cSCristian Marussi
80316f99cSCristian Marussi #define pr_fmt(fmt) "SCMI Notifications POWERCAP - " fmt
90316f99cSCristian Marussi
100316f99cSCristian Marussi #include <linux/bitfield.h>
11855aa26eSCristian Marussi #include <linux/io.h>
120316f99cSCristian Marussi #include <linux/module.h>
130316f99cSCristian Marussi #include <linux/scmi_protocol.h>
140316f99cSCristian Marussi
15b27d04d5SCristian Marussi #include <trace/events/scmi.h>
16b27d04d5SCristian Marussi
170316f99cSCristian Marussi #include "protocols.h"
180316f99cSCristian Marussi #include "notify.h"
190316f99cSCristian Marussi
200316f99cSCristian Marussi enum scmi_powercap_protocol_cmd {
210316f99cSCristian Marussi POWERCAP_DOMAIN_ATTRIBUTES = 0x3,
220316f99cSCristian Marussi POWERCAP_CAP_GET = 0x4,
230316f99cSCristian Marussi POWERCAP_CAP_SET = 0x5,
240316f99cSCristian Marussi POWERCAP_PAI_GET = 0x6,
250316f99cSCristian Marussi POWERCAP_PAI_SET = 0x7,
260316f99cSCristian Marussi POWERCAP_DOMAIN_NAME_GET = 0x8,
270316f99cSCristian Marussi POWERCAP_MEASUREMENTS_GET = 0x9,
280316f99cSCristian Marussi POWERCAP_CAP_NOTIFY = 0xa,
290316f99cSCristian Marussi POWERCAP_MEASUREMENTS_NOTIFY = 0xb,
300316f99cSCristian Marussi POWERCAP_DESCRIBE_FASTCHANNEL = 0xc,
310316f99cSCristian Marussi };
320316f99cSCristian Marussi
33855aa26eSCristian Marussi enum {
34855aa26eSCristian Marussi POWERCAP_FC_CAP,
35855aa26eSCristian Marussi POWERCAP_FC_PAI,
36855aa26eSCristian Marussi POWERCAP_FC_MAX,
37855aa26eSCristian Marussi };
38855aa26eSCristian Marussi
390316f99cSCristian Marussi struct scmi_msg_resp_powercap_domain_attributes {
400316f99cSCristian Marussi __le32 attributes;
410316f99cSCristian Marussi #define SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(x) ((x) & BIT(31))
420316f99cSCristian Marussi #define SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(x) ((x) & BIT(30))
430316f99cSCristian Marussi #define SUPPORTS_ASYNC_POWERCAP_CAP_SET(x) ((x) & BIT(29))
440316f99cSCristian Marussi #define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(28))
450316f99cSCristian Marussi #define SUPPORTS_POWERCAP_CAP_CONFIGURATION(x) ((x) & BIT(27))
460316f99cSCristian Marussi #define SUPPORTS_POWERCAP_MONITORING(x) ((x) & BIT(26))
470316f99cSCristian Marussi #define SUPPORTS_POWERCAP_PAI_CONFIGURATION(x) ((x) & BIT(25))
48855aa26eSCristian Marussi #define SUPPORTS_POWERCAP_FASTCHANNELS(x) ((x) & BIT(22))
490316f99cSCristian Marussi #define POWERCAP_POWER_UNIT(x) \
500316f99cSCristian Marussi (FIELD_GET(GENMASK(24, 23), (x)))
510316f99cSCristian Marussi #define SUPPORTS_POWER_UNITS_MW(x) \
520316f99cSCristian Marussi (POWERCAP_POWER_UNIT(x) == 0x2)
530316f99cSCristian Marussi #define SUPPORTS_POWER_UNITS_UW(x) \
540316f99cSCristian Marussi (POWERCAP_POWER_UNIT(x) == 0x1)
550316f99cSCristian Marussi u8 name[SCMI_SHORT_NAME_MAX_SIZE];
560316f99cSCristian Marussi __le32 min_pai;
570316f99cSCristian Marussi __le32 max_pai;
580316f99cSCristian Marussi __le32 pai_step;
590316f99cSCristian Marussi __le32 min_power_cap;
600316f99cSCristian Marussi __le32 max_power_cap;
610316f99cSCristian Marussi __le32 power_cap_step;
620316f99cSCristian Marussi __le32 sustainable_power;
630316f99cSCristian Marussi __le32 accuracy;
640316f99cSCristian Marussi __le32 parent_id;
650316f99cSCristian Marussi };
660316f99cSCristian Marussi
670316f99cSCristian Marussi struct scmi_msg_powercap_set_cap_or_pai {
680316f99cSCristian Marussi __le32 domain;
690316f99cSCristian Marussi __le32 flags;
700316f99cSCristian Marussi #define CAP_SET_ASYNC BIT(1)
710316f99cSCristian Marussi #define CAP_SET_IGNORE_DRESP BIT(0)
720316f99cSCristian Marussi __le32 value;
730316f99cSCristian Marussi };
740316f99cSCristian Marussi
750316f99cSCristian Marussi struct scmi_msg_resp_powercap_cap_set_complete {
760316f99cSCristian Marussi __le32 domain;
770316f99cSCristian Marussi __le32 power_cap;
780316f99cSCristian Marussi };
790316f99cSCristian Marussi
800316f99cSCristian Marussi struct scmi_msg_resp_powercap_meas_get {
810316f99cSCristian Marussi __le32 power;
820316f99cSCristian Marussi __le32 pai;
830316f99cSCristian Marussi };
840316f99cSCristian Marussi
850316f99cSCristian Marussi struct scmi_msg_powercap_notify_cap {
860316f99cSCristian Marussi __le32 domain;
870316f99cSCristian Marussi __le32 notify_enable;
880316f99cSCristian Marussi };
890316f99cSCristian Marussi
900316f99cSCristian Marussi struct scmi_msg_powercap_notify_thresh {
910316f99cSCristian Marussi __le32 domain;
920316f99cSCristian Marussi __le32 notify_enable;
930316f99cSCristian Marussi __le32 power_thresh_low;
940316f99cSCristian Marussi __le32 power_thresh_high;
950316f99cSCristian Marussi };
960316f99cSCristian Marussi
970316f99cSCristian Marussi struct scmi_powercap_cap_changed_notify_payld {
980316f99cSCristian Marussi __le32 agent_id;
990316f99cSCristian Marussi __le32 domain_id;
1000316f99cSCristian Marussi __le32 power_cap;
1010316f99cSCristian Marussi __le32 pai;
1020316f99cSCristian Marussi };
1030316f99cSCristian Marussi
1040316f99cSCristian Marussi struct scmi_powercap_meas_changed_notify_payld {
1050316f99cSCristian Marussi __le32 agent_id;
1060316f99cSCristian Marussi __le32 domain_id;
1070316f99cSCristian Marussi __le32 power;
1080316f99cSCristian Marussi };
1090316f99cSCristian Marussi
1100316f99cSCristian Marussi struct scmi_powercap_state {
111*758cd5fcSCristian Marussi bool enabled;
112*758cd5fcSCristian Marussi u32 last_pcap;
1130316f99cSCristian Marussi bool meas_notif_enabled;
1140316f99cSCristian Marussi u64 thresholds;
1150316f99cSCristian Marussi #define THRESH_LOW(p, id) \
1160316f99cSCristian Marussi (lower_32_bits((p)->states[(id)].thresholds))
1170316f99cSCristian Marussi #define THRESH_HIGH(p, id) \
1180316f99cSCristian Marussi (upper_32_bits((p)->states[(id)].thresholds))
1190316f99cSCristian Marussi };
1200316f99cSCristian Marussi
1210316f99cSCristian Marussi struct powercap_info {
1220316f99cSCristian Marussi u32 version;
1230316f99cSCristian Marussi int num_domains;
1240316f99cSCristian Marussi struct scmi_powercap_state *states;
1250316f99cSCristian Marussi struct scmi_powercap_info *powercaps;
1260316f99cSCristian Marussi };
1270316f99cSCristian Marussi
1280316f99cSCristian Marussi static enum scmi_powercap_protocol_cmd evt_2_cmd[] = {
1290316f99cSCristian Marussi POWERCAP_CAP_NOTIFY,
1300316f99cSCristian Marussi POWERCAP_MEASUREMENTS_NOTIFY,
1310316f99cSCristian Marussi };
1320316f99cSCristian Marussi
1330316f99cSCristian Marussi static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,
1340316f99cSCristian Marussi u32 domain, int message_id, bool enable);
1350316f99cSCristian Marussi
1360316f99cSCristian Marussi static int
scmi_powercap_attributes_get(const struct scmi_protocol_handle * ph,struct powercap_info * pi)1370316f99cSCristian Marussi scmi_powercap_attributes_get(const struct scmi_protocol_handle *ph,
1380316f99cSCristian Marussi struct powercap_info *pi)
1390316f99cSCristian Marussi {
1400316f99cSCristian Marussi int ret;
1410316f99cSCristian Marussi struct scmi_xfer *t;
1420316f99cSCristian Marussi
1430316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0,
1440316f99cSCristian Marussi sizeof(u32), &t);
1450316f99cSCristian Marussi if (ret)
1460316f99cSCristian Marussi return ret;
1470316f99cSCristian Marussi
1480316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t);
1490316f99cSCristian Marussi if (!ret) {
1500316f99cSCristian Marussi u32 attributes;
1510316f99cSCristian Marussi
1520316f99cSCristian Marussi attributes = get_unaligned_le32(t->rx.buf);
1530316f99cSCristian Marussi pi->num_domains = FIELD_GET(GENMASK(15, 0), attributes);
1540316f99cSCristian Marussi }
1550316f99cSCristian Marussi
1560316f99cSCristian Marussi ph->xops->xfer_put(ph, t);
1570316f99cSCristian Marussi return ret;
1580316f99cSCristian Marussi }
1590316f99cSCristian Marussi
1600316f99cSCristian Marussi static inline int
scmi_powercap_validate(unsigned int min_val,unsigned int max_val,unsigned int step_val,bool configurable)1610316f99cSCristian Marussi scmi_powercap_validate(unsigned int min_val, unsigned int max_val,
1620316f99cSCristian Marussi unsigned int step_val, bool configurable)
1630316f99cSCristian Marussi {
1640316f99cSCristian Marussi if (!min_val || !max_val)
1650316f99cSCristian Marussi return -EPROTO;
1660316f99cSCristian Marussi
1670316f99cSCristian Marussi if ((configurable && min_val == max_val) ||
1680316f99cSCristian Marussi (!configurable && min_val != max_val))
1690316f99cSCristian Marussi return -EPROTO;
1700316f99cSCristian Marussi
1710316f99cSCristian Marussi if (min_val != max_val && !step_val)
1720316f99cSCristian Marussi return -EPROTO;
1730316f99cSCristian Marussi
1740316f99cSCristian Marussi return 0;
1750316f99cSCristian Marussi }
1760316f99cSCristian Marussi
1770316f99cSCristian Marussi static int
scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle * ph,struct powercap_info * pinfo,u32 domain)1780316f99cSCristian Marussi scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
1790316f99cSCristian Marussi struct powercap_info *pinfo, u32 domain)
1800316f99cSCristian Marussi {
1810316f99cSCristian Marussi int ret;
1820316f99cSCristian Marussi u32 flags;
1830316f99cSCristian Marussi struct scmi_xfer *t;
1840316f99cSCristian Marussi struct scmi_powercap_info *dom_info = pinfo->powercaps + domain;
1850316f99cSCristian Marussi struct scmi_msg_resp_powercap_domain_attributes *resp;
1860316f99cSCristian Marussi
1870316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_DOMAIN_ATTRIBUTES,
1880316f99cSCristian Marussi sizeof(domain), sizeof(*resp), &t);
1890316f99cSCristian Marussi if (ret)
1900316f99cSCristian Marussi return ret;
1910316f99cSCristian Marussi
1920316f99cSCristian Marussi put_unaligned_le32(domain, t->tx.buf);
1930316f99cSCristian Marussi resp = t->rx.buf;
1940316f99cSCristian Marussi
1950316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t);
1960316f99cSCristian Marussi if (!ret) {
1970316f99cSCristian Marussi flags = le32_to_cpu(resp->attributes);
1980316f99cSCristian Marussi
1990316f99cSCristian Marussi dom_info->id = domain;
2000316f99cSCristian Marussi dom_info->notify_powercap_cap_change =
2010316f99cSCristian Marussi SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags);
2020316f99cSCristian Marussi dom_info->notify_powercap_measurement_change =
2030316f99cSCristian Marussi SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
2040316f99cSCristian Marussi dom_info->async_powercap_cap_set =
2050316f99cSCristian Marussi SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags);
2060316f99cSCristian Marussi dom_info->powercap_cap_config =
2070316f99cSCristian Marussi SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags);
2080316f99cSCristian Marussi dom_info->powercap_monitoring =
2090316f99cSCristian Marussi SUPPORTS_POWERCAP_MONITORING(flags);
2100316f99cSCristian Marussi dom_info->powercap_pai_config =
2110316f99cSCristian Marussi SUPPORTS_POWERCAP_PAI_CONFIGURATION(flags);
2120316f99cSCristian Marussi dom_info->powercap_scale_mw =
2130316f99cSCristian Marussi SUPPORTS_POWER_UNITS_MW(flags);
2140316f99cSCristian Marussi dom_info->powercap_scale_uw =
2150316f99cSCristian Marussi SUPPORTS_POWER_UNITS_UW(flags);
216855aa26eSCristian Marussi dom_info->fastchannels =
217855aa26eSCristian Marussi SUPPORTS_POWERCAP_FASTCHANNELS(flags);
2180316f99cSCristian Marussi
2190316f99cSCristian Marussi strscpy(dom_info->name, resp->name, SCMI_SHORT_NAME_MAX_SIZE);
2200316f99cSCristian Marussi
2210316f99cSCristian Marussi dom_info->min_pai = le32_to_cpu(resp->min_pai);
2220316f99cSCristian Marussi dom_info->max_pai = le32_to_cpu(resp->max_pai);
2230316f99cSCristian Marussi dom_info->pai_step = le32_to_cpu(resp->pai_step);
2240316f99cSCristian Marussi ret = scmi_powercap_validate(dom_info->min_pai,
2250316f99cSCristian Marussi dom_info->max_pai,
2260316f99cSCristian Marussi dom_info->pai_step,
2270316f99cSCristian Marussi dom_info->powercap_pai_config);
2280316f99cSCristian Marussi if (ret) {
2290316f99cSCristian Marussi dev_err(ph->dev,
2300316f99cSCristian Marussi "Platform reported inconsistent PAI config for domain %d - %s\n",
2310316f99cSCristian Marussi dom_info->id, dom_info->name);
2320316f99cSCristian Marussi goto clean;
2330316f99cSCristian Marussi }
2340316f99cSCristian Marussi
2350316f99cSCristian Marussi dom_info->min_power_cap = le32_to_cpu(resp->min_power_cap);
2360316f99cSCristian Marussi dom_info->max_power_cap = le32_to_cpu(resp->max_power_cap);
2370316f99cSCristian Marussi dom_info->power_cap_step = le32_to_cpu(resp->power_cap_step);
2380316f99cSCristian Marussi ret = scmi_powercap_validate(dom_info->min_power_cap,
2390316f99cSCristian Marussi dom_info->max_power_cap,
2400316f99cSCristian Marussi dom_info->power_cap_step,
2410316f99cSCristian Marussi dom_info->powercap_cap_config);
2420316f99cSCristian Marussi if (ret) {
2430316f99cSCristian Marussi dev_err(ph->dev,
2440316f99cSCristian Marussi "Platform reported inconsistent CAP config for domain %d - %s\n",
2450316f99cSCristian Marussi dom_info->id, dom_info->name);
2460316f99cSCristian Marussi goto clean;
2470316f99cSCristian Marussi }
2480316f99cSCristian Marussi
2490316f99cSCristian Marussi dom_info->sustainable_power =
2500316f99cSCristian Marussi le32_to_cpu(resp->sustainable_power);
2510316f99cSCristian Marussi dom_info->accuracy = le32_to_cpu(resp->accuracy);
2520316f99cSCristian Marussi
2530316f99cSCristian Marussi dom_info->parent_id = le32_to_cpu(resp->parent_id);
2540316f99cSCristian Marussi if (dom_info->parent_id != SCMI_POWERCAP_ROOT_ZONE_ID &&
2550316f99cSCristian Marussi (dom_info->parent_id >= pinfo->num_domains ||
2560316f99cSCristian Marussi dom_info->parent_id == dom_info->id)) {
2570316f99cSCristian Marussi dev_err(ph->dev,
2580316f99cSCristian Marussi "Platform reported inconsistent parent ID for domain %d - %s\n",
2590316f99cSCristian Marussi dom_info->id, dom_info->name);
2600316f99cSCristian Marussi ret = -ENODEV;
2610316f99cSCristian Marussi }
2620316f99cSCristian Marussi }
2630316f99cSCristian Marussi
2640316f99cSCristian Marussi clean:
2650316f99cSCristian Marussi ph->xops->xfer_put(ph, t);
2660316f99cSCristian Marussi
2670316f99cSCristian Marussi /*
2680316f99cSCristian Marussi * If supported overwrite short name with the extended one;
2690316f99cSCristian Marussi * on error just carry on and use already provided short name.
2700316f99cSCristian Marussi */
2710316f99cSCristian Marussi if (!ret && SUPPORTS_EXTENDED_NAMES(flags))
2720316f99cSCristian Marussi ph->hops->extended_name_get(ph, POWERCAP_DOMAIN_NAME_GET,
2730316f99cSCristian Marussi domain, dom_info->name,
2740316f99cSCristian Marussi SCMI_MAX_STR_SIZE);
2750316f99cSCristian Marussi
2760316f99cSCristian Marussi return ret;
2770316f99cSCristian Marussi }
2780316f99cSCristian Marussi
scmi_powercap_num_domains_get(const struct scmi_protocol_handle * ph)2790316f99cSCristian Marussi static int scmi_powercap_num_domains_get(const struct scmi_protocol_handle *ph)
2800316f99cSCristian Marussi {
2810316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph);
2820316f99cSCristian Marussi
2830316f99cSCristian Marussi return pi->num_domains;
2840316f99cSCristian Marussi }
2850316f99cSCristian Marussi
2860316f99cSCristian Marussi static const struct scmi_powercap_info *
scmi_powercap_dom_info_get(const struct scmi_protocol_handle * ph,u32 domain_id)2870316f99cSCristian Marussi scmi_powercap_dom_info_get(const struct scmi_protocol_handle *ph, u32 domain_id)
2880316f99cSCristian Marussi {
2890316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph);
2900316f99cSCristian Marussi
2910316f99cSCristian Marussi if (domain_id >= pi->num_domains)
2920316f99cSCristian Marussi return NULL;
2930316f99cSCristian Marussi
2940316f99cSCristian Marussi return pi->powercaps + domain_id;
2950316f99cSCristian Marussi }
2960316f99cSCristian Marussi
scmi_powercap_xfer_cap_get(const struct scmi_protocol_handle * ph,u32 domain_id,u32 * power_cap)297855aa26eSCristian Marussi static int scmi_powercap_xfer_cap_get(const struct scmi_protocol_handle *ph,
2980316f99cSCristian Marussi u32 domain_id, u32 *power_cap)
2990316f99cSCristian Marussi {
3000316f99cSCristian Marussi int ret;
3010316f99cSCristian Marussi struct scmi_xfer *t;
3020316f99cSCristian Marussi
3030316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_GET, sizeof(u32),
3040316f99cSCristian Marussi sizeof(u32), &t);
3050316f99cSCristian Marussi if (ret)
3060316f99cSCristian Marussi return ret;
3070316f99cSCristian Marussi
3080316f99cSCristian Marussi put_unaligned_le32(domain_id, t->tx.buf);
3090316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t);
3100316f99cSCristian Marussi if (!ret)
3110316f99cSCristian Marussi *power_cap = get_unaligned_le32(t->rx.buf);
3120316f99cSCristian Marussi
3130316f99cSCristian Marussi ph->xops->xfer_put(ph, t);
3140316f99cSCristian Marussi
3150316f99cSCristian Marussi return ret;
3160316f99cSCristian Marussi }
3170316f99cSCristian Marussi
__scmi_powercap_cap_get(const struct scmi_protocol_handle * ph,const struct scmi_powercap_info * dom,u32 * power_cap)3184e1a53b4SCristian Marussi static int __scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
3194e1a53b4SCristian Marussi const struct scmi_powercap_info *dom,
3204e1a53b4SCristian Marussi u32 *power_cap)
321855aa26eSCristian Marussi {
322855aa26eSCristian Marussi if (dom->fc_info && dom->fc_info[POWERCAP_FC_CAP].get_addr) {
323855aa26eSCristian Marussi *power_cap = ioread32(dom->fc_info[POWERCAP_FC_CAP].get_addr);
324b27d04d5SCristian Marussi trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_GET,
3254e1a53b4SCristian Marussi dom->id, *power_cap, 0);
326855aa26eSCristian Marussi return 0;
327855aa26eSCristian Marussi }
328855aa26eSCristian Marussi
3294e1a53b4SCristian Marussi return scmi_powercap_xfer_cap_get(ph, dom->id, power_cap);
3304e1a53b4SCristian Marussi }
3314e1a53b4SCristian Marussi
scmi_powercap_cap_get(const struct scmi_protocol_handle * ph,u32 domain_id,u32 * power_cap)3324e1a53b4SCristian Marussi static int scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
3334e1a53b4SCristian Marussi u32 domain_id, u32 *power_cap)
3344e1a53b4SCristian Marussi {
3354e1a53b4SCristian Marussi const struct scmi_powercap_info *dom;
3364e1a53b4SCristian Marussi
3374e1a53b4SCristian Marussi if (!power_cap)
3384e1a53b4SCristian Marussi return -EINVAL;
3394e1a53b4SCristian Marussi
3404e1a53b4SCristian Marussi dom = scmi_powercap_dom_info_get(ph, domain_id);
3414e1a53b4SCristian Marussi if (!dom)
3424e1a53b4SCristian Marussi return -EINVAL;
3434e1a53b4SCristian Marussi
3444e1a53b4SCristian Marussi return __scmi_powercap_cap_get(ph, dom, power_cap);
345855aa26eSCristian Marussi }
346855aa26eSCristian Marussi
scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle * ph,const struct scmi_powercap_info * pc,u32 power_cap,bool ignore_dresp)347855aa26eSCristian Marussi static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph,
348855aa26eSCristian Marussi const struct scmi_powercap_info *pc,
349855aa26eSCristian Marussi u32 power_cap, bool ignore_dresp)
3500316f99cSCristian Marussi {
3510316f99cSCristian Marussi int ret;
3520316f99cSCristian Marussi struct scmi_xfer *t;
3530316f99cSCristian Marussi struct scmi_msg_powercap_set_cap_or_pai *msg;
3540316f99cSCristian Marussi
3550316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_SET,
3560316f99cSCristian Marussi sizeof(*msg), 0, &t);
3570316f99cSCristian Marussi if (ret)
3580316f99cSCristian Marussi return ret;
3590316f99cSCristian Marussi
3600316f99cSCristian Marussi msg = t->tx.buf;
361855aa26eSCristian Marussi msg->domain = cpu_to_le32(pc->id);
3620316f99cSCristian Marussi msg->flags =
3630316f99cSCristian Marussi cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, !!pc->async_powercap_cap_set) |
3640316f99cSCristian Marussi FIELD_PREP(CAP_SET_IGNORE_DRESP, !!ignore_dresp));
3650316f99cSCristian Marussi msg->value = cpu_to_le32(power_cap);
3660316f99cSCristian Marussi
3670316f99cSCristian Marussi if (!pc->async_powercap_cap_set || ignore_dresp) {
3680316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t);
3690316f99cSCristian Marussi } else {
3700316f99cSCristian Marussi ret = ph->xops->do_xfer_with_response(ph, t);
3710316f99cSCristian Marussi if (!ret) {
3720316f99cSCristian Marussi struct scmi_msg_resp_powercap_cap_set_complete *resp;
3730316f99cSCristian Marussi
3740316f99cSCristian Marussi resp = t->rx.buf;
375855aa26eSCristian Marussi if (le32_to_cpu(resp->domain) == pc->id)
3760316f99cSCristian Marussi dev_dbg(ph->dev,
3770316f99cSCristian Marussi "Powercap ID %d CAP set async to %u\n",
378855aa26eSCristian Marussi pc->id,
3790316f99cSCristian Marussi get_unaligned_le32(&resp->power_cap));
3800316f99cSCristian Marussi else
3810316f99cSCristian Marussi ret = -EPROTO;
3820316f99cSCristian Marussi }
3830316f99cSCristian Marussi }
3840316f99cSCristian Marussi
3850316f99cSCristian Marussi ph->xops->xfer_put(ph, t);
3860316f99cSCristian Marussi return ret;
3870316f99cSCristian Marussi }
3880316f99cSCristian Marussi
__scmi_powercap_cap_set(const struct scmi_protocol_handle * ph,struct powercap_info * pi,u32 domain_id,u32 power_cap,bool ignore_dresp)3894e1a53b4SCristian Marussi static int __scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
3904e1a53b4SCristian Marussi struct powercap_info *pi, u32 domain_id,
3914e1a53b4SCristian Marussi u32 power_cap, bool ignore_dresp)
392855aa26eSCristian Marussi {
3934e1a53b4SCristian Marussi int ret = -EINVAL;
394855aa26eSCristian Marussi const struct scmi_powercap_info *pc;
395855aa26eSCristian Marussi
396855aa26eSCristian Marussi pc = scmi_powercap_dom_info_get(ph, domain_id);
3974e1a53b4SCristian Marussi if (!pc || !pc->powercap_cap_config)
3984e1a53b4SCristian Marussi return ret;
3994e1a53b4SCristian Marussi
4004e1a53b4SCristian Marussi if (power_cap &&
4014e1a53b4SCristian Marussi (power_cap < pc->min_power_cap || power_cap > pc->max_power_cap))
4024e1a53b4SCristian Marussi return ret;
403855aa26eSCristian Marussi
404855aa26eSCristian Marussi if (pc->fc_info && pc->fc_info[POWERCAP_FC_CAP].set_addr) {
405855aa26eSCristian Marussi struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_CAP];
406855aa26eSCristian Marussi
407855aa26eSCristian Marussi iowrite32(power_cap, fci->set_addr);
408855aa26eSCristian Marussi ph->hops->fastchannel_db_ring(fci->set_db);
409b27d04d5SCristian Marussi trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_SET,
410b27d04d5SCristian Marussi domain_id, power_cap, 0);
4114e1a53b4SCristian Marussi ret = 0;
4124e1a53b4SCristian Marussi } else {
4134e1a53b4SCristian Marussi ret = scmi_powercap_xfer_cap_set(ph, pc, power_cap,
4144e1a53b4SCristian Marussi ignore_dresp);
415855aa26eSCristian Marussi }
416855aa26eSCristian Marussi
417*758cd5fcSCristian Marussi /* Save the last explicitly set non-zero powercap value */
418*758cd5fcSCristian Marussi if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 && !ret && power_cap)
419*758cd5fcSCristian Marussi pi->states[domain_id].last_pcap = power_cap;
420*758cd5fcSCristian Marussi
4214e1a53b4SCristian Marussi return ret;
4224e1a53b4SCristian Marussi }
4234e1a53b4SCristian Marussi
scmi_powercap_cap_set(const struct scmi_protocol_handle * ph,u32 domain_id,u32 power_cap,bool ignore_dresp)4244e1a53b4SCristian Marussi static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
4254e1a53b4SCristian Marussi u32 domain_id, u32 power_cap,
4264e1a53b4SCristian Marussi bool ignore_dresp)
4274e1a53b4SCristian Marussi {
4284e1a53b4SCristian Marussi struct powercap_info *pi = ph->get_priv(ph);
4294e1a53b4SCristian Marussi
430*758cd5fcSCristian Marussi /*
431*758cd5fcSCristian Marussi * Disallow zero as a possible explicitly requested powercap:
432*758cd5fcSCristian Marussi * there are enable/disable operations for this.
433*758cd5fcSCristian Marussi */
434*758cd5fcSCristian Marussi if (!power_cap)
435*758cd5fcSCristian Marussi return -EINVAL;
436*758cd5fcSCristian Marussi
437*758cd5fcSCristian Marussi /* Just log the last set request if acting on a disabled domain */
438*758cd5fcSCristian Marussi if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 &&
439*758cd5fcSCristian Marussi !pi->states[domain_id].enabled) {
440*758cd5fcSCristian Marussi pi->states[domain_id].last_pcap = power_cap;
441*758cd5fcSCristian Marussi return 0;
442*758cd5fcSCristian Marussi }
443*758cd5fcSCristian Marussi
4444e1a53b4SCristian Marussi return __scmi_powercap_cap_set(ph, pi, domain_id,
4454e1a53b4SCristian Marussi power_cap, ignore_dresp);
446855aa26eSCristian Marussi }
447855aa26eSCristian Marussi
scmi_powercap_xfer_pai_get(const struct scmi_protocol_handle * ph,u32 domain_id,u32 * pai)448855aa26eSCristian Marussi static int scmi_powercap_xfer_pai_get(const struct scmi_protocol_handle *ph,
4490316f99cSCristian Marussi u32 domain_id, u32 *pai)
4500316f99cSCristian Marussi {
4510316f99cSCristian Marussi int ret;
4520316f99cSCristian Marussi struct scmi_xfer *t;
4530316f99cSCristian Marussi
4540316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_GET, sizeof(u32),
4550316f99cSCristian Marussi sizeof(u32), &t);
4560316f99cSCristian Marussi if (ret)
4570316f99cSCristian Marussi return ret;
4580316f99cSCristian Marussi
4590316f99cSCristian Marussi put_unaligned_le32(domain_id, t->tx.buf);
4600316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t);
4610316f99cSCristian Marussi if (!ret)
4620316f99cSCristian Marussi *pai = get_unaligned_le32(t->rx.buf);
4630316f99cSCristian Marussi
4640316f99cSCristian Marussi ph->xops->xfer_put(ph, t);
4650316f99cSCristian Marussi
4660316f99cSCristian Marussi return ret;
4670316f99cSCristian Marussi }
4680316f99cSCristian Marussi
scmi_powercap_pai_get(const struct scmi_protocol_handle * ph,u32 domain_id,u32 * pai)469855aa26eSCristian Marussi static int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph,
470855aa26eSCristian Marussi u32 domain_id, u32 *pai)
471855aa26eSCristian Marussi {
472855aa26eSCristian Marussi struct scmi_powercap_info *dom;
473855aa26eSCristian Marussi struct powercap_info *pi = ph->get_priv(ph);
474855aa26eSCristian Marussi
475855aa26eSCristian Marussi if (!pai || domain_id >= pi->num_domains)
476855aa26eSCristian Marussi return -EINVAL;
477855aa26eSCristian Marussi
478855aa26eSCristian Marussi dom = pi->powercaps + domain_id;
479855aa26eSCristian Marussi if (dom->fc_info && dom->fc_info[POWERCAP_FC_PAI].get_addr) {
480855aa26eSCristian Marussi *pai = ioread32(dom->fc_info[POWERCAP_FC_PAI].get_addr);
481b27d04d5SCristian Marussi trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_GET,
482b27d04d5SCristian Marussi domain_id, *pai, 0);
483855aa26eSCristian Marussi return 0;
484855aa26eSCristian Marussi }
485855aa26eSCristian Marussi
486855aa26eSCristian Marussi return scmi_powercap_xfer_pai_get(ph, domain_id, pai);
487855aa26eSCristian Marussi }
488855aa26eSCristian Marussi
scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle * ph,u32 domain_id,u32 pai)489855aa26eSCristian Marussi static int scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle *ph,
4900316f99cSCristian Marussi u32 domain_id, u32 pai)
4910316f99cSCristian Marussi {
4920316f99cSCristian Marussi int ret;
4930316f99cSCristian Marussi struct scmi_xfer *t;
4940316f99cSCristian Marussi struct scmi_msg_powercap_set_cap_or_pai *msg;
4950316f99cSCristian Marussi
4960316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_SET,
4970316f99cSCristian Marussi sizeof(*msg), 0, &t);
4980316f99cSCristian Marussi if (ret)
4990316f99cSCristian Marussi return ret;
5000316f99cSCristian Marussi
5010316f99cSCristian Marussi msg = t->tx.buf;
5020316f99cSCristian Marussi msg->domain = cpu_to_le32(domain_id);
5030316f99cSCristian Marussi msg->flags = cpu_to_le32(0);
5040316f99cSCristian Marussi msg->value = cpu_to_le32(pai);
5050316f99cSCristian Marussi
5060316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t);
5070316f99cSCristian Marussi
5080316f99cSCristian Marussi ph->xops->xfer_put(ph, t);
5090316f99cSCristian Marussi return ret;
5100316f99cSCristian Marussi }
5110316f99cSCristian Marussi
scmi_powercap_pai_set(const struct scmi_protocol_handle * ph,u32 domain_id,u32 pai)512855aa26eSCristian Marussi static int scmi_powercap_pai_set(const struct scmi_protocol_handle *ph,
513855aa26eSCristian Marussi u32 domain_id, u32 pai)
514855aa26eSCristian Marussi {
515855aa26eSCristian Marussi const struct scmi_powercap_info *pc;
516855aa26eSCristian Marussi
517855aa26eSCristian Marussi pc = scmi_powercap_dom_info_get(ph, domain_id);
518855aa26eSCristian Marussi if (!pc || !pc->powercap_pai_config || !pai ||
519855aa26eSCristian Marussi pai < pc->min_pai || pai > pc->max_pai)
520855aa26eSCristian Marussi return -EINVAL;
521855aa26eSCristian Marussi
522855aa26eSCristian Marussi if (pc->fc_info && pc->fc_info[POWERCAP_FC_PAI].set_addr) {
523855aa26eSCristian Marussi struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_PAI];
524855aa26eSCristian Marussi
525b27d04d5SCristian Marussi trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_SET,
526b27d04d5SCristian Marussi domain_id, pai, 0);
527855aa26eSCristian Marussi iowrite32(pai, fci->set_addr);
528855aa26eSCristian Marussi ph->hops->fastchannel_db_ring(fci->set_db);
529855aa26eSCristian Marussi return 0;
530855aa26eSCristian Marussi }
531855aa26eSCristian Marussi
532855aa26eSCristian Marussi return scmi_powercap_xfer_pai_set(ph, domain_id, pai);
533855aa26eSCristian Marussi }
534855aa26eSCristian Marussi
scmi_powercap_measurements_get(const struct scmi_protocol_handle * ph,u32 domain_id,u32 * average_power,u32 * pai)5350316f99cSCristian Marussi static int scmi_powercap_measurements_get(const struct scmi_protocol_handle *ph,
5360316f99cSCristian Marussi u32 domain_id, u32 *average_power,
5370316f99cSCristian Marussi u32 *pai)
5380316f99cSCristian Marussi {
5390316f99cSCristian Marussi int ret;
5400316f99cSCristian Marussi struct scmi_xfer *t;
5410316f99cSCristian Marussi struct scmi_msg_resp_powercap_meas_get *resp;
5420316f99cSCristian Marussi const struct scmi_powercap_info *pc;
5430316f99cSCristian Marussi
5440316f99cSCristian Marussi pc = scmi_powercap_dom_info_get(ph, domain_id);
5450316f99cSCristian Marussi if (!pc || !pc->powercap_monitoring || !pai || !average_power)
5460316f99cSCristian Marussi return -EINVAL;
5470316f99cSCristian Marussi
5480316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_MEASUREMENTS_GET,
5490316f99cSCristian Marussi sizeof(u32), sizeof(*resp), &t);
5500316f99cSCristian Marussi if (ret)
5510316f99cSCristian Marussi return ret;
5520316f99cSCristian Marussi
5530316f99cSCristian Marussi resp = t->rx.buf;
5540316f99cSCristian Marussi put_unaligned_le32(domain_id, t->tx.buf);
5550316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t);
5560316f99cSCristian Marussi if (!ret) {
5570316f99cSCristian Marussi *average_power = le32_to_cpu(resp->power);
5580316f99cSCristian Marussi *pai = le32_to_cpu(resp->pai);
5590316f99cSCristian Marussi }
5600316f99cSCristian Marussi
5610316f99cSCristian Marussi ph->xops->xfer_put(ph, t);
5620316f99cSCristian Marussi return ret;
5630316f99cSCristian Marussi }
5640316f99cSCristian Marussi
5650316f99cSCristian Marussi static int
scmi_powercap_measurements_threshold_get(const struct scmi_protocol_handle * ph,u32 domain_id,u32 * power_thresh_low,u32 * power_thresh_high)5660316f99cSCristian Marussi scmi_powercap_measurements_threshold_get(const struct scmi_protocol_handle *ph,
5670316f99cSCristian Marussi u32 domain_id, u32 *power_thresh_low,
5680316f99cSCristian Marussi u32 *power_thresh_high)
5690316f99cSCristian Marussi {
5700316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph);
5710316f99cSCristian Marussi
5720316f99cSCristian Marussi if (!power_thresh_low || !power_thresh_high ||
5730316f99cSCristian Marussi domain_id >= pi->num_domains)
5740316f99cSCristian Marussi return -EINVAL;
5750316f99cSCristian Marussi
5760316f99cSCristian Marussi *power_thresh_low = THRESH_LOW(pi, domain_id);
5770316f99cSCristian Marussi *power_thresh_high = THRESH_HIGH(pi, domain_id);
5780316f99cSCristian Marussi
5790316f99cSCristian Marussi return 0;
5800316f99cSCristian Marussi }
5810316f99cSCristian Marussi
5820316f99cSCristian Marussi static int
scmi_powercap_measurements_threshold_set(const struct scmi_protocol_handle * ph,u32 domain_id,u32 power_thresh_low,u32 power_thresh_high)5830316f99cSCristian Marussi scmi_powercap_measurements_threshold_set(const struct scmi_protocol_handle *ph,
5840316f99cSCristian Marussi u32 domain_id, u32 power_thresh_low,
5850316f99cSCristian Marussi u32 power_thresh_high)
5860316f99cSCristian Marussi {
5870316f99cSCristian Marussi int ret = 0;
5880316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph);
5890316f99cSCristian Marussi
5900316f99cSCristian Marussi if (domain_id >= pi->num_domains ||
5910316f99cSCristian Marussi power_thresh_low > power_thresh_high)
5920316f99cSCristian Marussi return -EINVAL;
5930316f99cSCristian Marussi
5940316f99cSCristian Marussi /* Anything to do ? */
5950316f99cSCristian Marussi if (THRESH_LOW(pi, domain_id) == power_thresh_low &&
5960316f99cSCristian Marussi THRESH_HIGH(pi, domain_id) == power_thresh_high)
5970316f99cSCristian Marussi return ret;
5980316f99cSCristian Marussi
5990316f99cSCristian Marussi pi->states[domain_id].thresholds =
6000316f99cSCristian Marussi (FIELD_PREP(GENMASK_ULL(31, 0), power_thresh_low) |
6010316f99cSCristian Marussi FIELD_PREP(GENMASK_ULL(63, 32), power_thresh_high));
6020316f99cSCristian Marussi
6030316f99cSCristian Marussi /* Update thresholds if notification already enabled */
6040316f99cSCristian Marussi if (pi->states[domain_id].meas_notif_enabled)
6050316f99cSCristian Marussi ret = scmi_powercap_notify(ph, domain_id,
6060316f99cSCristian Marussi POWERCAP_MEASUREMENTS_NOTIFY,
6070316f99cSCristian Marussi true);
6080316f99cSCristian Marussi
6090316f99cSCristian Marussi return ret;
6100316f99cSCristian Marussi }
6110316f99cSCristian Marussi
scmi_powercap_cap_enable_set(const struct scmi_protocol_handle * ph,u32 domain_id,bool enable)612*758cd5fcSCristian Marussi static int scmi_powercap_cap_enable_set(const struct scmi_protocol_handle *ph,
613*758cd5fcSCristian Marussi u32 domain_id, bool enable)
614*758cd5fcSCristian Marussi {
615*758cd5fcSCristian Marussi int ret;
616*758cd5fcSCristian Marussi u32 power_cap;
617*758cd5fcSCristian Marussi struct powercap_info *pi = ph->get_priv(ph);
618*758cd5fcSCristian Marussi
619*758cd5fcSCristian Marussi if (PROTOCOL_REV_MAJOR(pi->version) < 0x2)
620*758cd5fcSCristian Marussi return -EINVAL;
621*758cd5fcSCristian Marussi
622*758cd5fcSCristian Marussi if (enable == pi->states[domain_id].enabled)
623*758cd5fcSCristian Marussi return 0;
624*758cd5fcSCristian Marussi
625*758cd5fcSCristian Marussi if (enable) {
626*758cd5fcSCristian Marussi /* Cannot enable with a zero powercap. */
627*758cd5fcSCristian Marussi if (!pi->states[domain_id].last_pcap)
628*758cd5fcSCristian Marussi return -EINVAL;
629*758cd5fcSCristian Marussi
630*758cd5fcSCristian Marussi ret = __scmi_powercap_cap_set(ph, pi, domain_id,
631*758cd5fcSCristian Marussi pi->states[domain_id].last_pcap,
632*758cd5fcSCristian Marussi true);
633*758cd5fcSCristian Marussi } else {
634*758cd5fcSCristian Marussi ret = __scmi_powercap_cap_set(ph, pi, domain_id, 0, true);
635*758cd5fcSCristian Marussi }
636*758cd5fcSCristian Marussi
637*758cd5fcSCristian Marussi if (ret)
638*758cd5fcSCristian Marussi return ret;
639*758cd5fcSCristian Marussi
640*758cd5fcSCristian Marussi /*
641*758cd5fcSCristian Marussi * Update our internal state to reflect final platform state: the SCMI
642*758cd5fcSCristian Marussi * server could have ignored a disable request and kept enforcing some
643*758cd5fcSCristian Marussi * powercap limit requested by other agents.
644*758cd5fcSCristian Marussi */
645*758cd5fcSCristian Marussi ret = scmi_powercap_cap_get(ph, domain_id, &power_cap);
646*758cd5fcSCristian Marussi if (!ret)
647*758cd5fcSCristian Marussi pi->states[domain_id].enabled = !!power_cap;
648*758cd5fcSCristian Marussi
649*758cd5fcSCristian Marussi return ret;
650*758cd5fcSCristian Marussi }
651*758cd5fcSCristian Marussi
scmi_powercap_cap_enable_get(const struct scmi_protocol_handle * ph,u32 domain_id,bool * enable)652*758cd5fcSCristian Marussi static int scmi_powercap_cap_enable_get(const struct scmi_protocol_handle *ph,
653*758cd5fcSCristian Marussi u32 domain_id, bool *enable)
654*758cd5fcSCristian Marussi {
655*758cd5fcSCristian Marussi int ret;
656*758cd5fcSCristian Marussi u32 power_cap;
657*758cd5fcSCristian Marussi struct powercap_info *pi = ph->get_priv(ph);
658*758cd5fcSCristian Marussi
659*758cd5fcSCristian Marussi *enable = true;
660*758cd5fcSCristian Marussi if (PROTOCOL_REV_MAJOR(pi->version) < 0x2)
661*758cd5fcSCristian Marussi return 0;
662*758cd5fcSCristian Marussi
663*758cd5fcSCristian Marussi /*
664*758cd5fcSCristian Marussi * Report always real platform state; platform could have ignored
665*758cd5fcSCristian Marussi * a previous disable request. Default true on any error.
666*758cd5fcSCristian Marussi */
667*758cd5fcSCristian Marussi ret = scmi_powercap_cap_get(ph, domain_id, &power_cap);
668*758cd5fcSCristian Marussi if (!ret)
669*758cd5fcSCristian Marussi *enable = !!power_cap;
670*758cd5fcSCristian Marussi
671*758cd5fcSCristian Marussi /* Update internal state with current real platform state */
672*758cd5fcSCristian Marussi pi->states[domain_id].enabled = *enable;
673*758cd5fcSCristian Marussi
674*758cd5fcSCristian Marussi return 0;
675*758cd5fcSCristian Marussi }
676*758cd5fcSCristian Marussi
6770316f99cSCristian Marussi static const struct scmi_powercap_proto_ops powercap_proto_ops = {
6780316f99cSCristian Marussi .num_domains_get = scmi_powercap_num_domains_get,
6790316f99cSCristian Marussi .info_get = scmi_powercap_dom_info_get,
6800316f99cSCristian Marussi .cap_get = scmi_powercap_cap_get,
6810316f99cSCristian Marussi .cap_set = scmi_powercap_cap_set,
682*758cd5fcSCristian Marussi .cap_enable_set = scmi_powercap_cap_enable_set,
683*758cd5fcSCristian Marussi .cap_enable_get = scmi_powercap_cap_enable_get,
6840316f99cSCristian Marussi .pai_get = scmi_powercap_pai_get,
6850316f99cSCristian Marussi .pai_set = scmi_powercap_pai_set,
6860316f99cSCristian Marussi .measurements_get = scmi_powercap_measurements_get,
6870316f99cSCristian Marussi .measurements_threshold_set = scmi_powercap_measurements_threshold_set,
6880316f99cSCristian Marussi .measurements_threshold_get = scmi_powercap_measurements_threshold_get,
6890316f99cSCristian Marussi };
6900316f99cSCristian Marussi
scmi_powercap_domain_init_fc(const struct scmi_protocol_handle * ph,u32 domain,struct scmi_fc_info ** p_fc)691855aa26eSCristian Marussi static void scmi_powercap_domain_init_fc(const struct scmi_protocol_handle *ph,
692855aa26eSCristian Marussi u32 domain, struct scmi_fc_info **p_fc)
693855aa26eSCristian Marussi {
694855aa26eSCristian Marussi struct scmi_fc_info *fc;
695855aa26eSCristian Marussi
696855aa26eSCristian Marussi fc = devm_kcalloc(ph->dev, POWERCAP_FC_MAX, sizeof(*fc), GFP_KERNEL);
697855aa26eSCristian Marussi if (!fc)
698855aa26eSCristian Marussi return;
699855aa26eSCristian Marussi
700855aa26eSCristian Marussi ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
701855aa26eSCristian Marussi POWERCAP_CAP_SET, 4, domain,
702855aa26eSCristian Marussi &fc[POWERCAP_FC_CAP].set_addr,
703855aa26eSCristian Marussi &fc[POWERCAP_FC_CAP].set_db);
704855aa26eSCristian Marussi
705855aa26eSCristian Marussi ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
706855aa26eSCristian Marussi POWERCAP_CAP_GET, 4, domain,
707855aa26eSCristian Marussi &fc[POWERCAP_FC_CAP].get_addr, NULL);
708855aa26eSCristian Marussi
709855aa26eSCristian Marussi ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
710855aa26eSCristian Marussi POWERCAP_PAI_SET, 4, domain,
711855aa26eSCristian Marussi &fc[POWERCAP_FC_PAI].set_addr,
712855aa26eSCristian Marussi &fc[POWERCAP_FC_PAI].set_db);
713855aa26eSCristian Marussi
714855aa26eSCristian Marussi ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
715855aa26eSCristian Marussi POWERCAP_PAI_GET, 4, domain,
716855aa26eSCristian Marussi &fc[POWERCAP_FC_PAI].get_addr, NULL);
717855aa26eSCristian Marussi
718855aa26eSCristian Marussi *p_fc = fc;
719855aa26eSCristian Marussi }
720855aa26eSCristian Marussi
scmi_powercap_notify(const struct scmi_protocol_handle * ph,u32 domain,int message_id,bool enable)7210316f99cSCristian Marussi static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,
7220316f99cSCristian Marussi u32 domain, int message_id, bool enable)
7230316f99cSCristian Marussi {
7240316f99cSCristian Marussi int ret;
7250316f99cSCristian Marussi struct scmi_xfer *t;
7260316f99cSCristian Marussi
7270316f99cSCristian Marussi switch (message_id) {
7280316f99cSCristian Marussi case POWERCAP_CAP_NOTIFY:
7290316f99cSCristian Marussi {
7300316f99cSCristian Marussi struct scmi_msg_powercap_notify_cap *notify;
7310316f99cSCristian Marussi
7320316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, message_id,
7330316f99cSCristian Marussi sizeof(*notify), 0, &t);
7340316f99cSCristian Marussi if (ret)
7350316f99cSCristian Marussi return ret;
7360316f99cSCristian Marussi
7370316f99cSCristian Marussi notify = t->tx.buf;
7380316f99cSCristian Marussi notify->domain = cpu_to_le32(domain);
7390316f99cSCristian Marussi notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0);
7400316f99cSCristian Marussi break;
7410316f99cSCristian Marussi }
7420316f99cSCristian Marussi case POWERCAP_MEASUREMENTS_NOTIFY:
7430316f99cSCristian Marussi {
7440316f99cSCristian Marussi u32 low, high;
7450316f99cSCristian Marussi struct scmi_msg_powercap_notify_thresh *notify;
7460316f99cSCristian Marussi
7470316f99cSCristian Marussi /*
7480316f99cSCristian Marussi * Note that we have to pick the most recently configured
7490316f99cSCristian Marussi * thresholds to build a proper POWERCAP_MEASUREMENTS_NOTIFY
7500316f99cSCristian Marussi * enable request and we fail, complaining, if no thresholds
7510316f99cSCristian Marussi * were ever set, since this is an indication the API has been
7520316f99cSCristian Marussi * used wrongly.
7530316f99cSCristian Marussi */
7540316f99cSCristian Marussi ret = scmi_powercap_measurements_threshold_get(ph, domain,
7550316f99cSCristian Marussi &low, &high);
7560316f99cSCristian Marussi if (ret)
7570316f99cSCristian Marussi return ret;
7580316f99cSCristian Marussi
7590316f99cSCristian Marussi if (enable && !low && !high) {
7600316f99cSCristian Marussi dev_err(ph->dev,
7610316f99cSCristian Marussi "Invalid Measurements Notify thresholds: %u/%u\n",
7620316f99cSCristian Marussi low, high);
7630316f99cSCristian Marussi return -EINVAL;
7640316f99cSCristian Marussi }
7650316f99cSCristian Marussi
7660316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, message_id,
7670316f99cSCristian Marussi sizeof(*notify), 0, &t);
7680316f99cSCristian Marussi if (ret)
7690316f99cSCristian Marussi return ret;
7700316f99cSCristian Marussi
7710316f99cSCristian Marussi notify = t->tx.buf;
7720316f99cSCristian Marussi notify->domain = cpu_to_le32(domain);
7730316f99cSCristian Marussi notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0);
7740316f99cSCristian Marussi notify->power_thresh_low = cpu_to_le32(low);
7750316f99cSCristian Marussi notify->power_thresh_high = cpu_to_le32(high);
7760316f99cSCristian Marussi break;
7770316f99cSCristian Marussi }
7780316f99cSCristian Marussi default:
7790316f99cSCristian Marussi return -EINVAL;
7800316f99cSCristian Marussi }
7810316f99cSCristian Marussi
7820316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t);
7830316f99cSCristian Marussi
7840316f99cSCristian Marussi ph->xops->xfer_put(ph, t);
7850316f99cSCristian Marussi return ret;
7860316f99cSCristian Marussi }
7870316f99cSCristian Marussi
7880316f99cSCristian Marussi static int
scmi_powercap_set_notify_enabled(const struct scmi_protocol_handle * ph,u8 evt_id,u32 src_id,bool enable)7890316f99cSCristian Marussi scmi_powercap_set_notify_enabled(const struct scmi_protocol_handle *ph,
7900316f99cSCristian Marussi u8 evt_id, u32 src_id, bool enable)
7910316f99cSCristian Marussi {
7920316f99cSCristian Marussi int ret, cmd_id;
7930316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph);
7940316f99cSCristian Marussi
7950316f99cSCristian Marussi if (evt_id >= ARRAY_SIZE(evt_2_cmd) || src_id >= pi->num_domains)
7960316f99cSCristian Marussi return -EINVAL;
7970316f99cSCristian Marussi
7980316f99cSCristian Marussi cmd_id = evt_2_cmd[evt_id];
7990316f99cSCristian Marussi ret = scmi_powercap_notify(ph, src_id, cmd_id, enable);
8000316f99cSCristian Marussi if (ret)
8010316f99cSCristian Marussi pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n",
8020316f99cSCristian Marussi evt_id, src_id, ret);
8030316f99cSCristian Marussi else if (cmd_id == POWERCAP_MEASUREMENTS_NOTIFY)
8040316f99cSCristian Marussi /*
8050316f99cSCristian Marussi * On success save the current notification enabled state, so
8060316f99cSCristian Marussi * as to be able to properly update the notification thresholds
8070316f99cSCristian Marussi * when they are modified on a domain for which measurement
8080316f99cSCristian Marussi * notifications were currently enabled.
8090316f99cSCristian Marussi *
8100316f99cSCristian Marussi * This is needed because the SCMI Notification core machinery
8110316f99cSCristian Marussi * and API does not support passing per-notification custom
8120316f99cSCristian Marussi * arguments at callback registration time.
8130316f99cSCristian Marussi *
8140316f99cSCristian Marussi * Note that this can be done here with a simple flag since the
8150316f99cSCristian Marussi * SCMI core Notifications code takes care of keeping proper
8160316f99cSCristian Marussi * per-domain enables refcounting, so that this helper function
8170316f99cSCristian Marussi * will be called only once (for enables) when the first user
8180316f99cSCristian Marussi * registers a callback on this domain and once more (disable)
8190316f99cSCristian Marussi * when the last user de-registers its callback.
8200316f99cSCristian Marussi */
8210316f99cSCristian Marussi pi->states[src_id].meas_notif_enabled = enable;
8220316f99cSCristian Marussi
8230316f99cSCristian Marussi return ret;
8240316f99cSCristian Marussi }
8250316f99cSCristian Marussi
8260316f99cSCristian Marussi 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)8270316f99cSCristian Marussi scmi_powercap_fill_custom_report(const struct scmi_protocol_handle *ph,
8280316f99cSCristian Marussi u8 evt_id, ktime_t timestamp,
8290316f99cSCristian Marussi const void *payld, size_t payld_sz,
8300316f99cSCristian Marussi void *report, u32 *src_id)
8310316f99cSCristian Marussi {
8320316f99cSCristian Marussi void *rep = NULL;
8330316f99cSCristian Marussi
8340316f99cSCristian Marussi switch (evt_id) {
8350316f99cSCristian Marussi case SCMI_EVENT_POWERCAP_CAP_CHANGED:
8360316f99cSCristian Marussi {
8370316f99cSCristian Marussi const struct scmi_powercap_cap_changed_notify_payld *p = payld;
8380316f99cSCristian Marussi struct scmi_powercap_cap_changed_report *r = report;
8390316f99cSCristian Marussi
8400316f99cSCristian Marussi if (sizeof(*p) != payld_sz)
8410316f99cSCristian Marussi break;
8420316f99cSCristian Marussi
8430316f99cSCristian Marussi r->timestamp = timestamp;
8440316f99cSCristian Marussi r->agent_id = le32_to_cpu(p->agent_id);
8450316f99cSCristian Marussi r->domain_id = le32_to_cpu(p->domain_id);
8460316f99cSCristian Marussi r->power_cap = le32_to_cpu(p->power_cap);
8470316f99cSCristian Marussi r->pai = le32_to_cpu(p->pai);
8480316f99cSCristian Marussi *src_id = r->domain_id;
8490316f99cSCristian Marussi rep = r;
8500316f99cSCristian Marussi break;
8510316f99cSCristian Marussi }
8520316f99cSCristian Marussi case SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED:
8530316f99cSCristian Marussi {
8540316f99cSCristian Marussi const struct scmi_powercap_meas_changed_notify_payld *p = payld;
8550316f99cSCristian Marussi struct scmi_powercap_meas_changed_report *r = report;
8560316f99cSCristian Marussi
8570316f99cSCristian Marussi if (sizeof(*p) != payld_sz)
8580316f99cSCristian Marussi break;
8590316f99cSCristian Marussi
8600316f99cSCristian Marussi r->timestamp = timestamp;
8610316f99cSCristian Marussi r->agent_id = le32_to_cpu(p->agent_id);
8620316f99cSCristian Marussi r->domain_id = le32_to_cpu(p->domain_id);
8630316f99cSCristian Marussi r->power = le32_to_cpu(p->power);
8640316f99cSCristian Marussi *src_id = r->domain_id;
8650316f99cSCristian Marussi rep = r;
8660316f99cSCristian Marussi break;
8670316f99cSCristian Marussi }
8680316f99cSCristian Marussi default:
8690316f99cSCristian Marussi break;
8700316f99cSCristian Marussi }
8710316f99cSCristian Marussi
8720316f99cSCristian Marussi return rep;
8730316f99cSCristian Marussi }
8740316f99cSCristian Marussi
8750316f99cSCristian Marussi static int
scmi_powercap_get_num_sources(const struct scmi_protocol_handle * ph)8760316f99cSCristian Marussi scmi_powercap_get_num_sources(const struct scmi_protocol_handle *ph)
8770316f99cSCristian Marussi {
8780316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph);
8790316f99cSCristian Marussi
8800316f99cSCristian Marussi if (!pi)
8810316f99cSCristian Marussi return -EINVAL;
8820316f99cSCristian Marussi
8830316f99cSCristian Marussi return pi->num_domains;
8840316f99cSCristian Marussi }
8850316f99cSCristian Marussi
8860316f99cSCristian Marussi static const struct scmi_event powercap_events[] = {
8870316f99cSCristian Marussi {
8880316f99cSCristian Marussi .id = SCMI_EVENT_POWERCAP_CAP_CHANGED,
8890316f99cSCristian Marussi .max_payld_sz =
8900316f99cSCristian Marussi sizeof(struct scmi_powercap_cap_changed_notify_payld),
8910316f99cSCristian Marussi .max_report_sz =
8920316f99cSCristian Marussi sizeof(struct scmi_powercap_cap_changed_report),
8930316f99cSCristian Marussi },
8940316f99cSCristian Marussi {
8950316f99cSCristian Marussi .id = SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED,
8960316f99cSCristian Marussi .max_payld_sz =
8970316f99cSCristian Marussi sizeof(struct scmi_powercap_meas_changed_notify_payld),
8980316f99cSCristian Marussi .max_report_sz =
8990316f99cSCristian Marussi sizeof(struct scmi_powercap_meas_changed_report),
9000316f99cSCristian Marussi },
9010316f99cSCristian Marussi };
9020316f99cSCristian Marussi
9030316f99cSCristian Marussi static const struct scmi_event_ops powercap_event_ops = {
9040316f99cSCristian Marussi .get_num_sources = scmi_powercap_get_num_sources,
9050316f99cSCristian Marussi .set_notify_enabled = scmi_powercap_set_notify_enabled,
9060316f99cSCristian Marussi .fill_custom_report = scmi_powercap_fill_custom_report,
9070316f99cSCristian Marussi };
9080316f99cSCristian Marussi
9090316f99cSCristian Marussi static const struct scmi_protocol_events powercap_protocol_events = {
9100316f99cSCristian Marussi .queue_sz = SCMI_PROTO_QUEUE_SZ,
9110316f99cSCristian Marussi .ops = &powercap_event_ops,
9120316f99cSCristian Marussi .evts = powercap_events,
9130316f99cSCristian Marussi .num_events = ARRAY_SIZE(powercap_events),
9140316f99cSCristian Marussi };
9150316f99cSCristian Marussi
9160316f99cSCristian Marussi static int
scmi_powercap_protocol_init(const struct scmi_protocol_handle * ph)9170316f99cSCristian Marussi scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
9180316f99cSCristian Marussi {
9190316f99cSCristian Marussi int domain, ret;
9200316f99cSCristian Marussi u32 version;
9210316f99cSCristian Marussi struct powercap_info *pinfo;
9220316f99cSCristian Marussi
9230316f99cSCristian Marussi ret = ph->xops->version_get(ph, &version);
9240316f99cSCristian Marussi if (ret)
9250316f99cSCristian Marussi return ret;
9260316f99cSCristian Marussi
9270316f99cSCristian Marussi dev_dbg(ph->dev, "Powercap Version %d.%d\n",
9280316f99cSCristian Marussi PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
9290316f99cSCristian Marussi
9300316f99cSCristian Marussi pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
9310316f99cSCristian Marussi if (!pinfo)
9320316f99cSCristian Marussi return -ENOMEM;
9330316f99cSCristian Marussi
9340316f99cSCristian Marussi ret = scmi_powercap_attributes_get(ph, pinfo);
9350316f99cSCristian Marussi if (ret)
9360316f99cSCristian Marussi return ret;
9370316f99cSCristian Marussi
9380316f99cSCristian Marussi pinfo->powercaps = devm_kcalloc(ph->dev, pinfo->num_domains,
9390316f99cSCristian Marussi sizeof(*pinfo->powercaps),
9400316f99cSCristian Marussi GFP_KERNEL);
9410316f99cSCristian Marussi if (!pinfo->powercaps)
9420316f99cSCristian Marussi return -ENOMEM;
9430316f99cSCristian Marussi
944*758cd5fcSCristian Marussi pinfo->states = devm_kcalloc(ph->dev, pinfo->num_domains,
945*758cd5fcSCristian Marussi sizeof(*pinfo->states), GFP_KERNEL);
946*758cd5fcSCristian Marussi if (!pinfo->states)
947*758cd5fcSCristian Marussi return -ENOMEM;
948*758cd5fcSCristian Marussi
9490316f99cSCristian Marussi /*
9500316f99cSCristian Marussi * Note that any failure in retrieving any domain attribute leads to
9510316f99cSCristian Marussi * the whole Powercap protocol initialization failure: this way the
9520316f99cSCristian Marussi * reported Powercap domains are all assured, when accessed, to be well
9530316f99cSCristian Marussi * formed and correlated by sane parent-child relationship (if any).
9540316f99cSCristian Marussi */
9550316f99cSCristian Marussi for (domain = 0; domain < pinfo->num_domains; domain++) {
9560316f99cSCristian Marussi ret = scmi_powercap_domain_attributes_get(ph, pinfo, domain);
9570316f99cSCristian Marussi if (ret)
9580316f99cSCristian Marussi return ret;
959855aa26eSCristian Marussi
960855aa26eSCristian Marussi if (pinfo->powercaps[domain].fastchannels)
961855aa26eSCristian Marussi scmi_powercap_domain_init_fc(ph, domain,
962855aa26eSCristian Marussi &pinfo->powercaps[domain].fc_info);
963*758cd5fcSCristian Marussi
964*758cd5fcSCristian Marussi /* Grab initial state when disable is supported. */
965*758cd5fcSCristian Marussi if (PROTOCOL_REV_MAJOR(version) >= 0x2) {
966*758cd5fcSCristian Marussi ret = __scmi_powercap_cap_get(ph,
967*758cd5fcSCristian Marussi &pinfo->powercaps[domain],
968*758cd5fcSCristian Marussi &pinfo->states[domain].last_pcap);
969*758cd5fcSCristian Marussi if (ret)
970*758cd5fcSCristian Marussi return ret;
971*758cd5fcSCristian Marussi
972*758cd5fcSCristian Marussi pinfo->states[domain].enabled =
973*758cd5fcSCristian Marussi !!pinfo->states[domain].last_pcap;
974*758cd5fcSCristian Marussi }
9750316f99cSCristian Marussi }
9760316f99cSCristian Marussi
9770316f99cSCristian Marussi pinfo->version = version;
9780316f99cSCristian Marussi return ph->set_priv(ph, pinfo);
9790316f99cSCristian Marussi }
9800316f99cSCristian Marussi
9810316f99cSCristian Marussi static const struct scmi_protocol scmi_powercap = {
9820316f99cSCristian Marussi .id = SCMI_PROTOCOL_POWERCAP,
9830316f99cSCristian Marussi .owner = THIS_MODULE,
9840316f99cSCristian Marussi .instance_init = &scmi_powercap_protocol_init,
9850316f99cSCristian Marussi .ops = &powercap_proto_ops,
9860316f99cSCristian Marussi .events = &powercap_protocol_events,
9870316f99cSCristian Marussi };
9880316f99cSCristian Marussi
9890316f99cSCristian Marussi DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(powercap, scmi_powercap)
990