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 15*b27d04d5SCristian Marussi #include <trace/events/scmi.h> 16*b27d04d5SCristian 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 { 1110316f99cSCristian Marussi bool meas_notif_enabled; 1120316f99cSCristian Marussi u64 thresholds; 1130316f99cSCristian Marussi #define THRESH_LOW(p, id) \ 1140316f99cSCristian Marussi (lower_32_bits((p)->states[(id)].thresholds)) 1150316f99cSCristian Marussi #define THRESH_HIGH(p, id) \ 1160316f99cSCristian Marussi (upper_32_bits((p)->states[(id)].thresholds)) 1170316f99cSCristian Marussi }; 1180316f99cSCristian Marussi 1190316f99cSCristian Marussi struct powercap_info { 1200316f99cSCristian Marussi u32 version; 1210316f99cSCristian Marussi int num_domains; 1220316f99cSCristian Marussi struct scmi_powercap_state *states; 1230316f99cSCristian Marussi struct scmi_powercap_info *powercaps; 1240316f99cSCristian Marussi }; 1250316f99cSCristian Marussi 1260316f99cSCristian Marussi static enum scmi_powercap_protocol_cmd evt_2_cmd[] = { 1270316f99cSCristian Marussi POWERCAP_CAP_NOTIFY, 1280316f99cSCristian Marussi POWERCAP_MEASUREMENTS_NOTIFY, 1290316f99cSCristian Marussi }; 1300316f99cSCristian Marussi 1310316f99cSCristian Marussi static int scmi_powercap_notify(const struct scmi_protocol_handle *ph, 1320316f99cSCristian Marussi u32 domain, int message_id, bool enable); 1330316f99cSCristian Marussi 1340316f99cSCristian Marussi static int 1350316f99cSCristian Marussi scmi_powercap_attributes_get(const struct scmi_protocol_handle *ph, 1360316f99cSCristian Marussi struct powercap_info *pi) 1370316f99cSCristian Marussi { 1380316f99cSCristian Marussi int ret; 1390316f99cSCristian Marussi struct scmi_xfer *t; 1400316f99cSCristian Marussi 1410316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0, 1420316f99cSCristian Marussi sizeof(u32), &t); 1430316f99cSCristian Marussi if (ret) 1440316f99cSCristian Marussi return ret; 1450316f99cSCristian Marussi 1460316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 1470316f99cSCristian Marussi if (!ret) { 1480316f99cSCristian Marussi u32 attributes; 1490316f99cSCristian Marussi 1500316f99cSCristian Marussi attributes = get_unaligned_le32(t->rx.buf); 1510316f99cSCristian Marussi pi->num_domains = FIELD_GET(GENMASK(15, 0), attributes); 1520316f99cSCristian Marussi } 1530316f99cSCristian Marussi 1540316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 1550316f99cSCristian Marussi return ret; 1560316f99cSCristian Marussi } 1570316f99cSCristian Marussi 1580316f99cSCristian Marussi static inline int 1590316f99cSCristian Marussi scmi_powercap_validate(unsigned int min_val, unsigned int max_val, 1600316f99cSCristian Marussi unsigned int step_val, bool configurable) 1610316f99cSCristian Marussi { 1620316f99cSCristian Marussi if (!min_val || !max_val) 1630316f99cSCristian Marussi return -EPROTO; 1640316f99cSCristian Marussi 1650316f99cSCristian Marussi if ((configurable && min_val == max_val) || 1660316f99cSCristian Marussi (!configurable && min_val != max_val)) 1670316f99cSCristian Marussi return -EPROTO; 1680316f99cSCristian Marussi 1690316f99cSCristian Marussi if (min_val != max_val && !step_val) 1700316f99cSCristian Marussi return -EPROTO; 1710316f99cSCristian Marussi 1720316f99cSCristian Marussi return 0; 1730316f99cSCristian Marussi } 1740316f99cSCristian Marussi 1750316f99cSCristian Marussi static int 1760316f99cSCristian Marussi scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph, 1770316f99cSCristian Marussi struct powercap_info *pinfo, u32 domain) 1780316f99cSCristian Marussi { 1790316f99cSCristian Marussi int ret; 1800316f99cSCristian Marussi u32 flags; 1810316f99cSCristian Marussi struct scmi_xfer *t; 1820316f99cSCristian Marussi struct scmi_powercap_info *dom_info = pinfo->powercaps + domain; 1830316f99cSCristian Marussi struct scmi_msg_resp_powercap_domain_attributes *resp; 1840316f99cSCristian Marussi 1850316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_DOMAIN_ATTRIBUTES, 1860316f99cSCristian Marussi sizeof(domain), sizeof(*resp), &t); 1870316f99cSCristian Marussi if (ret) 1880316f99cSCristian Marussi return ret; 1890316f99cSCristian Marussi 1900316f99cSCristian Marussi put_unaligned_le32(domain, t->tx.buf); 1910316f99cSCristian Marussi resp = t->rx.buf; 1920316f99cSCristian Marussi 1930316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 1940316f99cSCristian Marussi if (!ret) { 1950316f99cSCristian Marussi flags = le32_to_cpu(resp->attributes); 1960316f99cSCristian Marussi 1970316f99cSCristian Marussi dom_info->id = domain; 1980316f99cSCristian Marussi dom_info->notify_powercap_cap_change = 1990316f99cSCristian Marussi SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags); 2000316f99cSCristian Marussi dom_info->notify_powercap_measurement_change = 2010316f99cSCristian Marussi SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags); 2020316f99cSCristian Marussi dom_info->async_powercap_cap_set = 2030316f99cSCristian Marussi SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags); 2040316f99cSCristian Marussi dom_info->powercap_cap_config = 2050316f99cSCristian Marussi SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags); 2060316f99cSCristian Marussi dom_info->powercap_monitoring = 2070316f99cSCristian Marussi SUPPORTS_POWERCAP_MONITORING(flags); 2080316f99cSCristian Marussi dom_info->powercap_pai_config = 2090316f99cSCristian Marussi SUPPORTS_POWERCAP_PAI_CONFIGURATION(flags); 2100316f99cSCristian Marussi dom_info->powercap_scale_mw = 2110316f99cSCristian Marussi SUPPORTS_POWER_UNITS_MW(flags); 2120316f99cSCristian Marussi dom_info->powercap_scale_uw = 2130316f99cSCristian Marussi SUPPORTS_POWER_UNITS_UW(flags); 214855aa26eSCristian Marussi dom_info->fastchannels = 215855aa26eSCristian Marussi SUPPORTS_POWERCAP_FASTCHANNELS(flags); 2160316f99cSCristian Marussi 2170316f99cSCristian Marussi strscpy(dom_info->name, resp->name, SCMI_SHORT_NAME_MAX_SIZE); 2180316f99cSCristian Marussi 2190316f99cSCristian Marussi dom_info->min_pai = le32_to_cpu(resp->min_pai); 2200316f99cSCristian Marussi dom_info->max_pai = le32_to_cpu(resp->max_pai); 2210316f99cSCristian Marussi dom_info->pai_step = le32_to_cpu(resp->pai_step); 2220316f99cSCristian Marussi ret = scmi_powercap_validate(dom_info->min_pai, 2230316f99cSCristian Marussi dom_info->max_pai, 2240316f99cSCristian Marussi dom_info->pai_step, 2250316f99cSCristian Marussi dom_info->powercap_pai_config); 2260316f99cSCristian Marussi if (ret) { 2270316f99cSCristian Marussi dev_err(ph->dev, 2280316f99cSCristian Marussi "Platform reported inconsistent PAI config for domain %d - %s\n", 2290316f99cSCristian Marussi dom_info->id, dom_info->name); 2300316f99cSCristian Marussi goto clean; 2310316f99cSCristian Marussi } 2320316f99cSCristian Marussi 2330316f99cSCristian Marussi dom_info->min_power_cap = le32_to_cpu(resp->min_power_cap); 2340316f99cSCristian Marussi dom_info->max_power_cap = le32_to_cpu(resp->max_power_cap); 2350316f99cSCristian Marussi dom_info->power_cap_step = le32_to_cpu(resp->power_cap_step); 2360316f99cSCristian Marussi ret = scmi_powercap_validate(dom_info->min_power_cap, 2370316f99cSCristian Marussi dom_info->max_power_cap, 2380316f99cSCristian Marussi dom_info->power_cap_step, 2390316f99cSCristian Marussi dom_info->powercap_cap_config); 2400316f99cSCristian Marussi if (ret) { 2410316f99cSCristian Marussi dev_err(ph->dev, 2420316f99cSCristian Marussi "Platform reported inconsistent CAP config for domain %d - %s\n", 2430316f99cSCristian Marussi dom_info->id, dom_info->name); 2440316f99cSCristian Marussi goto clean; 2450316f99cSCristian Marussi } 2460316f99cSCristian Marussi 2470316f99cSCristian Marussi dom_info->sustainable_power = 2480316f99cSCristian Marussi le32_to_cpu(resp->sustainable_power); 2490316f99cSCristian Marussi dom_info->accuracy = le32_to_cpu(resp->accuracy); 2500316f99cSCristian Marussi 2510316f99cSCristian Marussi dom_info->parent_id = le32_to_cpu(resp->parent_id); 2520316f99cSCristian Marussi if (dom_info->parent_id != SCMI_POWERCAP_ROOT_ZONE_ID && 2530316f99cSCristian Marussi (dom_info->parent_id >= pinfo->num_domains || 2540316f99cSCristian Marussi dom_info->parent_id == dom_info->id)) { 2550316f99cSCristian Marussi dev_err(ph->dev, 2560316f99cSCristian Marussi "Platform reported inconsistent parent ID for domain %d - %s\n", 2570316f99cSCristian Marussi dom_info->id, dom_info->name); 2580316f99cSCristian Marussi ret = -ENODEV; 2590316f99cSCristian Marussi } 2600316f99cSCristian Marussi } 2610316f99cSCristian Marussi 2620316f99cSCristian Marussi clean: 2630316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 2640316f99cSCristian Marussi 2650316f99cSCristian Marussi /* 2660316f99cSCristian Marussi * If supported overwrite short name with the extended one; 2670316f99cSCristian Marussi * on error just carry on and use already provided short name. 2680316f99cSCristian Marussi */ 2690316f99cSCristian Marussi if (!ret && SUPPORTS_EXTENDED_NAMES(flags)) 2700316f99cSCristian Marussi ph->hops->extended_name_get(ph, POWERCAP_DOMAIN_NAME_GET, 2710316f99cSCristian Marussi domain, dom_info->name, 2720316f99cSCristian Marussi SCMI_MAX_STR_SIZE); 2730316f99cSCristian Marussi 2740316f99cSCristian Marussi return ret; 2750316f99cSCristian Marussi } 2760316f99cSCristian Marussi 2770316f99cSCristian Marussi static int scmi_powercap_num_domains_get(const struct scmi_protocol_handle *ph) 2780316f99cSCristian Marussi { 2790316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 2800316f99cSCristian Marussi 2810316f99cSCristian Marussi return pi->num_domains; 2820316f99cSCristian Marussi } 2830316f99cSCristian Marussi 2840316f99cSCristian Marussi static const struct scmi_powercap_info * 2850316f99cSCristian Marussi scmi_powercap_dom_info_get(const struct scmi_protocol_handle *ph, u32 domain_id) 2860316f99cSCristian Marussi { 2870316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 2880316f99cSCristian Marussi 2890316f99cSCristian Marussi if (domain_id >= pi->num_domains) 2900316f99cSCristian Marussi return NULL; 2910316f99cSCristian Marussi 2920316f99cSCristian Marussi return pi->powercaps + domain_id; 2930316f99cSCristian Marussi } 2940316f99cSCristian Marussi 295855aa26eSCristian Marussi static int scmi_powercap_xfer_cap_get(const struct scmi_protocol_handle *ph, 2960316f99cSCristian Marussi u32 domain_id, u32 *power_cap) 2970316f99cSCristian Marussi { 2980316f99cSCristian Marussi int ret; 2990316f99cSCristian Marussi struct scmi_xfer *t; 3000316f99cSCristian Marussi 3010316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_GET, sizeof(u32), 3020316f99cSCristian Marussi sizeof(u32), &t); 3030316f99cSCristian Marussi if (ret) 3040316f99cSCristian Marussi return ret; 3050316f99cSCristian Marussi 3060316f99cSCristian Marussi put_unaligned_le32(domain_id, t->tx.buf); 3070316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 3080316f99cSCristian Marussi if (!ret) 3090316f99cSCristian Marussi *power_cap = get_unaligned_le32(t->rx.buf); 3100316f99cSCristian Marussi 3110316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 3120316f99cSCristian Marussi 3130316f99cSCristian Marussi return ret; 3140316f99cSCristian Marussi } 3150316f99cSCristian Marussi 316855aa26eSCristian Marussi static int scmi_powercap_cap_get(const struct scmi_protocol_handle *ph, 317855aa26eSCristian Marussi u32 domain_id, u32 *power_cap) 318855aa26eSCristian Marussi { 319855aa26eSCristian Marussi struct scmi_powercap_info *dom; 320855aa26eSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 321855aa26eSCristian Marussi 322855aa26eSCristian Marussi if (!power_cap || domain_id >= pi->num_domains) 323855aa26eSCristian Marussi return -EINVAL; 324855aa26eSCristian Marussi 325855aa26eSCristian Marussi dom = pi->powercaps + domain_id; 326855aa26eSCristian Marussi if (dom->fc_info && dom->fc_info[POWERCAP_FC_CAP].get_addr) { 327855aa26eSCristian Marussi *power_cap = ioread32(dom->fc_info[POWERCAP_FC_CAP].get_addr); 328*b27d04d5SCristian Marussi trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_GET, 329*b27d04d5SCristian Marussi domain_id, *power_cap, 0); 330855aa26eSCristian Marussi return 0; 331855aa26eSCristian Marussi } 332855aa26eSCristian Marussi 333855aa26eSCristian Marussi return scmi_powercap_xfer_cap_get(ph, domain_id, power_cap); 334855aa26eSCristian Marussi } 335855aa26eSCristian Marussi 336855aa26eSCristian Marussi static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph, 337855aa26eSCristian Marussi const struct scmi_powercap_info *pc, 338855aa26eSCristian Marussi u32 power_cap, bool ignore_dresp) 3390316f99cSCristian Marussi { 3400316f99cSCristian Marussi int ret; 3410316f99cSCristian Marussi struct scmi_xfer *t; 3420316f99cSCristian Marussi struct scmi_msg_powercap_set_cap_or_pai *msg; 3430316f99cSCristian Marussi 3440316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_SET, 3450316f99cSCristian Marussi sizeof(*msg), 0, &t); 3460316f99cSCristian Marussi if (ret) 3470316f99cSCristian Marussi return ret; 3480316f99cSCristian Marussi 3490316f99cSCristian Marussi msg = t->tx.buf; 350855aa26eSCristian Marussi msg->domain = cpu_to_le32(pc->id); 3510316f99cSCristian Marussi msg->flags = 3520316f99cSCristian Marussi cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, !!pc->async_powercap_cap_set) | 3530316f99cSCristian Marussi FIELD_PREP(CAP_SET_IGNORE_DRESP, !!ignore_dresp)); 3540316f99cSCristian Marussi msg->value = cpu_to_le32(power_cap); 3550316f99cSCristian Marussi 3560316f99cSCristian Marussi if (!pc->async_powercap_cap_set || ignore_dresp) { 3570316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 3580316f99cSCristian Marussi } else { 3590316f99cSCristian Marussi ret = ph->xops->do_xfer_with_response(ph, t); 3600316f99cSCristian Marussi if (!ret) { 3610316f99cSCristian Marussi struct scmi_msg_resp_powercap_cap_set_complete *resp; 3620316f99cSCristian Marussi 3630316f99cSCristian Marussi resp = t->rx.buf; 364855aa26eSCristian Marussi if (le32_to_cpu(resp->domain) == pc->id) 3650316f99cSCristian Marussi dev_dbg(ph->dev, 3660316f99cSCristian Marussi "Powercap ID %d CAP set async to %u\n", 367855aa26eSCristian Marussi pc->id, 3680316f99cSCristian Marussi get_unaligned_le32(&resp->power_cap)); 3690316f99cSCristian Marussi else 3700316f99cSCristian Marussi ret = -EPROTO; 3710316f99cSCristian Marussi } 3720316f99cSCristian Marussi } 3730316f99cSCristian Marussi 3740316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 3750316f99cSCristian Marussi return ret; 3760316f99cSCristian Marussi } 3770316f99cSCristian Marussi 378855aa26eSCristian Marussi static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph, 379855aa26eSCristian Marussi u32 domain_id, u32 power_cap, 380855aa26eSCristian Marussi bool ignore_dresp) 381855aa26eSCristian Marussi { 382855aa26eSCristian Marussi const struct scmi_powercap_info *pc; 383855aa26eSCristian Marussi 384855aa26eSCristian Marussi pc = scmi_powercap_dom_info_get(ph, domain_id); 385855aa26eSCristian Marussi if (!pc || !pc->powercap_cap_config || !power_cap || 386855aa26eSCristian Marussi power_cap < pc->min_power_cap || 387855aa26eSCristian Marussi power_cap > pc->max_power_cap) 388855aa26eSCristian Marussi return -EINVAL; 389855aa26eSCristian Marussi 390855aa26eSCristian Marussi if (pc->fc_info && pc->fc_info[POWERCAP_FC_CAP].set_addr) { 391855aa26eSCristian Marussi struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_CAP]; 392855aa26eSCristian Marussi 393855aa26eSCristian Marussi iowrite32(power_cap, fci->set_addr); 394855aa26eSCristian Marussi ph->hops->fastchannel_db_ring(fci->set_db); 395*b27d04d5SCristian Marussi trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_SET, 396*b27d04d5SCristian Marussi domain_id, power_cap, 0); 397855aa26eSCristian Marussi return 0; 398855aa26eSCristian Marussi } 399855aa26eSCristian Marussi 400855aa26eSCristian Marussi return scmi_powercap_xfer_cap_set(ph, pc, power_cap, ignore_dresp); 401855aa26eSCristian Marussi } 402855aa26eSCristian Marussi 403855aa26eSCristian Marussi static int scmi_powercap_xfer_pai_get(const struct scmi_protocol_handle *ph, 4040316f99cSCristian Marussi u32 domain_id, u32 *pai) 4050316f99cSCristian Marussi { 4060316f99cSCristian Marussi int ret; 4070316f99cSCristian Marussi struct scmi_xfer *t; 4080316f99cSCristian Marussi 4090316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_GET, sizeof(u32), 4100316f99cSCristian Marussi sizeof(u32), &t); 4110316f99cSCristian Marussi if (ret) 4120316f99cSCristian Marussi return ret; 4130316f99cSCristian Marussi 4140316f99cSCristian Marussi put_unaligned_le32(domain_id, t->tx.buf); 4150316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 4160316f99cSCristian Marussi if (!ret) 4170316f99cSCristian Marussi *pai = get_unaligned_le32(t->rx.buf); 4180316f99cSCristian Marussi 4190316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 4200316f99cSCristian Marussi 4210316f99cSCristian Marussi return ret; 4220316f99cSCristian Marussi } 4230316f99cSCristian Marussi 424855aa26eSCristian Marussi static int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph, 425855aa26eSCristian Marussi u32 domain_id, u32 *pai) 426855aa26eSCristian Marussi { 427855aa26eSCristian Marussi struct scmi_powercap_info *dom; 428855aa26eSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 429855aa26eSCristian Marussi 430855aa26eSCristian Marussi if (!pai || domain_id >= pi->num_domains) 431855aa26eSCristian Marussi return -EINVAL; 432855aa26eSCristian Marussi 433855aa26eSCristian Marussi dom = pi->powercaps + domain_id; 434855aa26eSCristian Marussi if (dom->fc_info && dom->fc_info[POWERCAP_FC_PAI].get_addr) { 435855aa26eSCristian Marussi *pai = ioread32(dom->fc_info[POWERCAP_FC_PAI].get_addr); 436*b27d04d5SCristian Marussi trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_GET, 437*b27d04d5SCristian Marussi domain_id, *pai, 0); 438855aa26eSCristian Marussi return 0; 439855aa26eSCristian Marussi } 440855aa26eSCristian Marussi 441855aa26eSCristian Marussi return scmi_powercap_xfer_pai_get(ph, domain_id, pai); 442855aa26eSCristian Marussi } 443855aa26eSCristian Marussi 444855aa26eSCristian Marussi static int scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle *ph, 4450316f99cSCristian Marussi u32 domain_id, u32 pai) 4460316f99cSCristian Marussi { 4470316f99cSCristian Marussi int ret; 4480316f99cSCristian Marussi struct scmi_xfer *t; 4490316f99cSCristian Marussi struct scmi_msg_powercap_set_cap_or_pai *msg; 4500316f99cSCristian Marussi 4510316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_SET, 4520316f99cSCristian Marussi sizeof(*msg), 0, &t); 4530316f99cSCristian Marussi if (ret) 4540316f99cSCristian Marussi return ret; 4550316f99cSCristian Marussi 4560316f99cSCristian Marussi msg = t->tx.buf; 4570316f99cSCristian Marussi msg->domain = cpu_to_le32(domain_id); 4580316f99cSCristian Marussi msg->flags = cpu_to_le32(0); 4590316f99cSCristian Marussi msg->value = cpu_to_le32(pai); 4600316f99cSCristian Marussi 4610316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 4620316f99cSCristian Marussi 4630316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 4640316f99cSCristian Marussi return ret; 4650316f99cSCristian Marussi } 4660316f99cSCristian Marussi 467855aa26eSCristian Marussi static int scmi_powercap_pai_set(const struct scmi_protocol_handle *ph, 468855aa26eSCristian Marussi u32 domain_id, u32 pai) 469855aa26eSCristian Marussi { 470855aa26eSCristian Marussi const struct scmi_powercap_info *pc; 471855aa26eSCristian Marussi 472855aa26eSCristian Marussi pc = scmi_powercap_dom_info_get(ph, domain_id); 473855aa26eSCristian Marussi if (!pc || !pc->powercap_pai_config || !pai || 474855aa26eSCristian Marussi pai < pc->min_pai || pai > pc->max_pai) 475855aa26eSCristian Marussi return -EINVAL; 476855aa26eSCristian Marussi 477855aa26eSCristian Marussi if (pc->fc_info && pc->fc_info[POWERCAP_FC_PAI].set_addr) { 478855aa26eSCristian Marussi struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_PAI]; 479855aa26eSCristian Marussi 480*b27d04d5SCristian Marussi trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_SET, 481*b27d04d5SCristian Marussi domain_id, pai, 0); 482855aa26eSCristian Marussi iowrite32(pai, fci->set_addr); 483855aa26eSCristian Marussi ph->hops->fastchannel_db_ring(fci->set_db); 484855aa26eSCristian Marussi return 0; 485855aa26eSCristian Marussi } 486855aa26eSCristian Marussi 487855aa26eSCristian Marussi return scmi_powercap_xfer_pai_set(ph, domain_id, pai); 488855aa26eSCristian Marussi } 489855aa26eSCristian Marussi 4900316f99cSCristian Marussi static int scmi_powercap_measurements_get(const struct scmi_protocol_handle *ph, 4910316f99cSCristian Marussi u32 domain_id, u32 *average_power, 4920316f99cSCristian Marussi u32 *pai) 4930316f99cSCristian Marussi { 4940316f99cSCristian Marussi int ret; 4950316f99cSCristian Marussi struct scmi_xfer *t; 4960316f99cSCristian Marussi struct scmi_msg_resp_powercap_meas_get *resp; 4970316f99cSCristian Marussi const struct scmi_powercap_info *pc; 4980316f99cSCristian Marussi 4990316f99cSCristian Marussi pc = scmi_powercap_dom_info_get(ph, domain_id); 5000316f99cSCristian Marussi if (!pc || !pc->powercap_monitoring || !pai || !average_power) 5010316f99cSCristian Marussi return -EINVAL; 5020316f99cSCristian Marussi 5030316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_MEASUREMENTS_GET, 5040316f99cSCristian Marussi sizeof(u32), sizeof(*resp), &t); 5050316f99cSCristian Marussi if (ret) 5060316f99cSCristian Marussi return ret; 5070316f99cSCristian Marussi 5080316f99cSCristian Marussi resp = t->rx.buf; 5090316f99cSCristian Marussi put_unaligned_le32(domain_id, t->tx.buf); 5100316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 5110316f99cSCristian Marussi if (!ret) { 5120316f99cSCristian Marussi *average_power = le32_to_cpu(resp->power); 5130316f99cSCristian Marussi *pai = le32_to_cpu(resp->pai); 5140316f99cSCristian Marussi } 5150316f99cSCristian Marussi 5160316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 5170316f99cSCristian Marussi return ret; 5180316f99cSCristian Marussi } 5190316f99cSCristian Marussi 5200316f99cSCristian Marussi static int 5210316f99cSCristian Marussi scmi_powercap_measurements_threshold_get(const struct scmi_protocol_handle *ph, 5220316f99cSCristian Marussi u32 domain_id, u32 *power_thresh_low, 5230316f99cSCristian Marussi u32 *power_thresh_high) 5240316f99cSCristian Marussi { 5250316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 5260316f99cSCristian Marussi 5270316f99cSCristian Marussi if (!power_thresh_low || !power_thresh_high || 5280316f99cSCristian Marussi domain_id >= pi->num_domains) 5290316f99cSCristian Marussi return -EINVAL; 5300316f99cSCristian Marussi 5310316f99cSCristian Marussi *power_thresh_low = THRESH_LOW(pi, domain_id); 5320316f99cSCristian Marussi *power_thresh_high = THRESH_HIGH(pi, domain_id); 5330316f99cSCristian Marussi 5340316f99cSCristian Marussi return 0; 5350316f99cSCristian Marussi } 5360316f99cSCristian Marussi 5370316f99cSCristian Marussi static int 5380316f99cSCristian Marussi scmi_powercap_measurements_threshold_set(const struct scmi_protocol_handle *ph, 5390316f99cSCristian Marussi u32 domain_id, u32 power_thresh_low, 5400316f99cSCristian Marussi u32 power_thresh_high) 5410316f99cSCristian Marussi { 5420316f99cSCristian Marussi int ret = 0; 5430316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 5440316f99cSCristian Marussi 5450316f99cSCristian Marussi if (domain_id >= pi->num_domains || 5460316f99cSCristian Marussi power_thresh_low > power_thresh_high) 5470316f99cSCristian Marussi return -EINVAL; 5480316f99cSCristian Marussi 5490316f99cSCristian Marussi /* Anything to do ? */ 5500316f99cSCristian Marussi if (THRESH_LOW(pi, domain_id) == power_thresh_low && 5510316f99cSCristian Marussi THRESH_HIGH(pi, domain_id) == power_thresh_high) 5520316f99cSCristian Marussi return ret; 5530316f99cSCristian Marussi 5540316f99cSCristian Marussi pi->states[domain_id].thresholds = 5550316f99cSCristian Marussi (FIELD_PREP(GENMASK_ULL(31, 0), power_thresh_low) | 5560316f99cSCristian Marussi FIELD_PREP(GENMASK_ULL(63, 32), power_thresh_high)); 5570316f99cSCristian Marussi 5580316f99cSCristian Marussi /* Update thresholds if notification already enabled */ 5590316f99cSCristian Marussi if (pi->states[domain_id].meas_notif_enabled) 5600316f99cSCristian Marussi ret = scmi_powercap_notify(ph, domain_id, 5610316f99cSCristian Marussi POWERCAP_MEASUREMENTS_NOTIFY, 5620316f99cSCristian Marussi true); 5630316f99cSCristian Marussi 5640316f99cSCristian Marussi return ret; 5650316f99cSCristian Marussi } 5660316f99cSCristian Marussi 5670316f99cSCristian Marussi static const struct scmi_powercap_proto_ops powercap_proto_ops = { 5680316f99cSCristian Marussi .num_domains_get = scmi_powercap_num_domains_get, 5690316f99cSCristian Marussi .info_get = scmi_powercap_dom_info_get, 5700316f99cSCristian Marussi .cap_get = scmi_powercap_cap_get, 5710316f99cSCristian Marussi .cap_set = scmi_powercap_cap_set, 5720316f99cSCristian Marussi .pai_get = scmi_powercap_pai_get, 5730316f99cSCristian Marussi .pai_set = scmi_powercap_pai_set, 5740316f99cSCristian Marussi .measurements_get = scmi_powercap_measurements_get, 5750316f99cSCristian Marussi .measurements_threshold_set = scmi_powercap_measurements_threshold_set, 5760316f99cSCristian Marussi .measurements_threshold_get = scmi_powercap_measurements_threshold_get, 5770316f99cSCristian Marussi }; 5780316f99cSCristian Marussi 579855aa26eSCristian Marussi static void scmi_powercap_domain_init_fc(const struct scmi_protocol_handle *ph, 580855aa26eSCristian Marussi u32 domain, struct scmi_fc_info **p_fc) 581855aa26eSCristian Marussi { 582855aa26eSCristian Marussi struct scmi_fc_info *fc; 583855aa26eSCristian Marussi 584855aa26eSCristian Marussi fc = devm_kcalloc(ph->dev, POWERCAP_FC_MAX, sizeof(*fc), GFP_KERNEL); 585855aa26eSCristian Marussi if (!fc) 586855aa26eSCristian Marussi return; 587855aa26eSCristian Marussi 588855aa26eSCristian Marussi ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, 589855aa26eSCristian Marussi POWERCAP_CAP_SET, 4, domain, 590855aa26eSCristian Marussi &fc[POWERCAP_FC_CAP].set_addr, 591855aa26eSCristian Marussi &fc[POWERCAP_FC_CAP].set_db); 592855aa26eSCristian Marussi 593855aa26eSCristian Marussi ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, 594855aa26eSCristian Marussi POWERCAP_CAP_GET, 4, domain, 595855aa26eSCristian Marussi &fc[POWERCAP_FC_CAP].get_addr, NULL); 596855aa26eSCristian Marussi 597855aa26eSCristian Marussi ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, 598855aa26eSCristian Marussi POWERCAP_PAI_SET, 4, domain, 599855aa26eSCristian Marussi &fc[POWERCAP_FC_PAI].set_addr, 600855aa26eSCristian Marussi &fc[POWERCAP_FC_PAI].set_db); 601855aa26eSCristian Marussi 602855aa26eSCristian Marussi ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, 603855aa26eSCristian Marussi POWERCAP_PAI_GET, 4, domain, 604855aa26eSCristian Marussi &fc[POWERCAP_FC_PAI].get_addr, NULL); 605855aa26eSCristian Marussi 606855aa26eSCristian Marussi *p_fc = fc; 607855aa26eSCristian Marussi } 608855aa26eSCristian Marussi 6090316f99cSCristian Marussi static int scmi_powercap_notify(const struct scmi_protocol_handle *ph, 6100316f99cSCristian Marussi u32 domain, int message_id, bool enable) 6110316f99cSCristian Marussi { 6120316f99cSCristian Marussi int ret; 6130316f99cSCristian Marussi struct scmi_xfer *t; 6140316f99cSCristian Marussi 6150316f99cSCristian Marussi switch (message_id) { 6160316f99cSCristian Marussi case POWERCAP_CAP_NOTIFY: 6170316f99cSCristian Marussi { 6180316f99cSCristian Marussi struct scmi_msg_powercap_notify_cap *notify; 6190316f99cSCristian Marussi 6200316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, message_id, 6210316f99cSCristian Marussi sizeof(*notify), 0, &t); 6220316f99cSCristian Marussi if (ret) 6230316f99cSCristian Marussi return ret; 6240316f99cSCristian Marussi 6250316f99cSCristian Marussi notify = t->tx.buf; 6260316f99cSCristian Marussi notify->domain = cpu_to_le32(domain); 6270316f99cSCristian Marussi notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0); 6280316f99cSCristian Marussi break; 6290316f99cSCristian Marussi } 6300316f99cSCristian Marussi case POWERCAP_MEASUREMENTS_NOTIFY: 6310316f99cSCristian Marussi { 6320316f99cSCristian Marussi u32 low, high; 6330316f99cSCristian Marussi struct scmi_msg_powercap_notify_thresh *notify; 6340316f99cSCristian Marussi 6350316f99cSCristian Marussi /* 6360316f99cSCristian Marussi * Note that we have to pick the most recently configured 6370316f99cSCristian Marussi * thresholds to build a proper POWERCAP_MEASUREMENTS_NOTIFY 6380316f99cSCristian Marussi * enable request and we fail, complaining, if no thresholds 6390316f99cSCristian Marussi * were ever set, since this is an indication the API has been 6400316f99cSCristian Marussi * used wrongly. 6410316f99cSCristian Marussi */ 6420316f99cSCristian Marussi ret = scmi_powercap_measurements_threshold_get(ph, domain, 6430316f99cSCristian Marussi &low, &high); 6440316f99cSCristian Marussi if (ret) 6450316f99cSCristian Marussi return ret; 6460316f99cSCristian Marussi 6470316f99cSCristian Marussi if (enable && !low && !high) { 6480316f99cSCristian Marussi dev_err(ph->dev, 6490316f99cSCristian Marussi "Invalid Measurements Notify thresholds: %u/%u\n", 6500316f99cSCristian Marussi low, high); 6510316f99cSCristian Marussi return -EINVAL; 6520316f99cSCristian Marussi } 6530316f99cSCristian Marussi 6540316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, message_id, 6550316f99cSCristian Marussi sizeof(*notify), 0, &t); 6560316f99cSCristian Marussi if (ret) 6570316f99cSCristian Marussi return ret; 6580316f99cSCristian Marussi 6590316f99cSCristian Marussi notify = t->tx.buf; 6600316f99cSCristian Marussi notify->domain = cpu_to_le32(domain); 6610316f99cSCristian Marussi notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0); 6620316f99cSCristian Marussi notify->power_thresh_low = cpu_to_le32(low); 6630316f99cSCristian Marussi notify->power_thresh_high = cpu_to_le32(high); 6640316f99cSCristian Marussi break; 6650316f99cSCristian Marussi } 6660316f99cSCristian Marussi default: 6670316f99cSCristian Marussi return -EINVAL; 6680316f99cSCristian Marussi } 6690316f99cSCristian Marussi 6700316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 6710316f99cSCristian Marussi 6720316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 6730316f99cSCristian Marussi return ret; 6740316f99cSCristian Marussi } 6750316f99cSCristian Marussi 6760316f99cSCristian Marussi static int 6770316f99cSCristian Marussi scmi_powercap_set_notify_enabled(const struct scmi_protocol_handle *ph, 6780316f99cSCristian Marussi u8 evt_id, u32 src_id, bool enable) 6790316f99cSCristian Marussi { 6800316f99cSCristian Marussi int ret, cmd_id; 6810316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 6820316f99cSCristian Marussi 6830316f99cSCristian Marussi if (evt_id >= ARRAY_SIZE(evt_2_cmd) || src_id >= pi->num_domains) 6840316f99cSCristian Marussi return -EINVAL; 6850316f99cSCristian Marussi 6860316f99cSCristian Marussi cmd_id = evt_2_cmd[evt_id]; 6870316f99cSCristian Marussi ret = scmi_powercap_notify(ph, src_id, cmd_id, enable); 6880316f99cSCristian Marussi if (ret) 6890316f99cSCristian Marussi pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n", 6900316f99cSCristian Marussi evt_id, src_id, ret); 6910316f99cSCristian Marussi else if (cmd_id == POWERCAP_MEASUREMENTS_NOTIFY) 6920316f99cSCristian Marussi /* 6930316f99cSCristian Marussi * On success save the current notification enabled state, so 6940316f99cSCristian Marussi * as to be able to properly update the notification thresholds 6950316f99cSCristian Marussi * when they are modified on a domain for which measurement 6960316f99cSCristian Marussi * notifications were currently enabled. 6970316f99cSCristian Marussi * 6980316f99cSCristian Marussi * This is needed because the SCMI Notification core machinery 6990316f99cSCristian Marussi * and API does not support passing per-notification custom 7000316f99cSCristian Marussi * arguments at callback registration time. 7010316f99cSCristian Marussi * 7020316f99cSCristian Marussi * Note that this can be done here with a simple flag since the 7030316f99cSCristian Marussi * SCMI core Notifications code takes care of keeping proper 7040316f99cSCristian Marussi * per-domain enables refcounting, so that this helper function 7050316f99cSCristian Marussi * will be called only once (for enables) when the first user 7060316f99cSCristian Marussi * registers a callback on this domain and once more (disable) 7070316f99cSCristian Marussi * when the last user de-registers its callback. 7080316f99cSCristian Marussi */ 7090316f99cSCristian Marussi pi->states[src_id].meas_notif_enabled = enable; 7100316f99cSCristian Marussi 7110316f99cSCristian Marussi return ret; 7120316f99cSCristian Marussi } 7130316f99cSCristian Marussi 7140316f99cSCristian Marussi static void * 7150316f99cSCristian Marussi scmi_powercap_fill_custom_report(const struct scmi_protocol_handle *ph, 7160316f99cSCristian Marussi u8 evt_id, ktime_t timestamp, 7170316f99cSCristian Marussi const void *payld, size_t payld_sz, 7180316f99cSCristian Marussi void *report, u32 *src_id) 7190316f99cSCristian Marussi { 7200316f99cSCristian Marussi void *rep = NULL; 7210316f99cSCristian Marussi 7220316f99cSCristian Marussi switch (evt_id) { 7230316f99cSCristian Marussi case SCMI_EVENT_POWERCAP_CAP_CHANGED: 7240316f99cSCristian Marussi { 7250316f99cSCristian Marussi const struct scmi_powercap_cap_changed_notify_payld *p = payld; 7260316f99cSCristian Marussi struct scmi_powercap_cap_changed_report *r = report; 7270316f99cSCristian Marussi 7280316f99cSCristian Marussi if (sizeof(*p) != payld_sz) 7290316f99cSCristian Marussi break; 7300316f99cSCristian Marussi 7310316f99cSCristian Marussi r->timestamp = timestamp; 7320316f99cSCristian Marussi r->agent_id = le32_to_cpu(p->agent_id); 7330316f99cSCristian Marussi r->domain_id = le32_to_cpu(p->domain_id); 7340316f99cSCristian Marussi r->power_cap = le32_to_cpu(p->power_cap); 7350316f99cSCristian Marussi r->pai = le32_to_cpu(p->pai); 7360316f99cSCristian Marussi *src_id = r->domain_id; 7370316f99cSCristian Marussi rep = r; 7380316f99cSCristian Marussi break; 7390316f99cSCristian Marussi } 7400316f99cSCristian Marussi case SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED: 7410316f99cSCristian Marussi { 7420316f99cSCristian Marussi const struct scmi_powercap_meas_changed_notify_payld *p = payld; 7430316f99cSCristian Marussi struct scmi_powercap_meas_changed_report *r = report; 7440316f99cSCristian Marussi 7450316f99cSCristian Marussi if (sizeof(*p) != payld_sz) 7460316f99cSCristian Marussi break; 7470316f99cSCristian Marussi 7480316f99cSCristian Marussi r->timestamp = timestamp; 7490316f99cSCristian Marussi r->agent_id = le32_to_cpu(p->agent_id); 7500316f99cSCristian Marussi r->domain_id = le32_to_cpu(p->domain_id); 7510316f99cSCristian Marussi r->power = le32_to_cpu(p->power); 7520316f99cSCristian Marussi *src_id = r->domain_id; 7530316f99cSCristian Marussi rep = r; 7540316f99cSCristian Marussi break; 7550316f99cSCristian Marussi } 7560316f99cSCristian Marussi default: 7570316f99cSCristian Marussi break; 7580316f99cSCristian Marussi } 7590316f99cSCristian Marussi 7600316f99cSCristian Marussi return rep; 7610316f99cSCristian Marussi } 7620316f99cSCristian Marussi 7630316f99cSCristian Marussi static int 7640316f99cSCristian Marussi scmi_powercap_get_num_sources(const struct scmi_protocol_handle *ph) 7650316f99cSCristian Marussi { 7660316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 7670316f99cSCristian Marussi 7680316f99cSCristian Marussi if (!pi) 7690316f99cSCristian Marussi return -EINVAL; 7700316f99cSCristian Marussi 7710316f99cSCristian Marussi return pi->num_domains; 7720316f99cSCristian Marussi } 7730316f99cSCristian Marussi 7740316f99cSCristian Marussi static const struct scmi_event powercap_events[] = { 7750316f99cSCristian Marussi { 7760316f99cSCristian Marussi .id = SCMI_EVENT_POWERCAP_CAP_CHANGED, 7770316f99cSCristian Marussi .max_payld_sz = 7780316f99cSCristian Marussi sizeof(struct scmi_powercap_cap_changed_notify_payld), 7790316f99cSCristian Marussi .max_report_sz = 7800316f99cSCristian Marussi sizeof(struct scmi_powercap_cap_changed_report), 7810316f99cSCristian Marussi }, 7820316f99cSCristian Marussi { 7830316f99cSCristian Marussi .id = SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED, 7840316f99cSCristian Marussi .max_payld_sz = 7850316f99cSCristian Marussi sizeof(struct scmi_powercap_meas_changed_notify_payld), 7860316f99cSCristian Marussi .max_report_sz = 7870316f99cSCristian Marussi sizeof(struct scmi_powercap_meas_changed_report), 7880316f99cSCristian Marussi }, 7890316f99cSCristian Marussi }; 7900316f99cSCristian Marussi 7910316f99cSCristian Marussi static const struct scmi_event_ops powercap_event_ops = { 7920316f99cSCristian Marussi .get_num_sources = scmi_powercap_get_num_sources, 7930316f99cSCristian Marussi .set_notify_enabled = scmi_powercap_set_notify_enabled, 7940316f99cSCristian Marussi .fill_custom_report = scmi_powercap_fill_custom_report, 7950316f99cSCristian Marussi }; 7960316f99cSCristian Marussi 7970316f99cSCristian Marussi static const struct scmi_protocol_events powercap_protocol_events = { 7980316f99cSCristian Marussi .queue_sz = SCMI_PROTO_QUEUE_SZ, 7990316f99cSCristian Marussi .ops = &powercap_event_ops, 8000316f99cSCristian Marussi .evts = powercap_events, 8010316f99cSCristian Marussi .num_events = ARRAY_SIZE(powercap_events), 8020316f99cSCristian Marussi }; 8030316f99cSCristian Marussi 8040316f99cSCristian Marussi static int 8050316f99cSCristian Marussi scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph) 8060316f99cSCristian Marussi { 8070316f99cSCristian Marussi int domain, ret; 8080316f99cSCristian Marussi u32 version; 8090316f99cSCristian Marussi struct powercap_info *pinfo; 8100316f99cSCristian Marussi 8110316f99cSCristian Marussi ret = ph->xops->version_get(ph, &version); 8120316f99cSCristian Marussi if (ret) 8130316f99cSCristian Marussi return ret; 8140316f99cSCristian Marussi 8150316f99cSCristian Marussi dev_dbg(ph->dev, "Powercap Version %d.%d\n", 8160316f99cSCristian Marussi PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); 8170316f99cSCristian Marussi 8180316f99cSCristian Marussi pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL); 8190316f99cSCristian Marussi if (!pinfo) 8200316f99cSCristian Marussi return -ENOMEM; 8210316f99cSCristian Marussi 8220316f99cSCristian Marussi ret = scmi_powercap_attributes_get(ph, pinfo); 8230316f99cSCristian Marussi if (ret) 8240316f99cSCristian Marussi return ret; 8250316f99cSCristian Marussi 8260316f99cSCristian Marussi pinfo->powercaps = devm_kcalloc(ph->dev, pinfo->num_domains, 8270316f99cSCristian Marussi sizeof(*pinfo->powercaps), 8280316f99cSCristian Marussi GFP_KERNEL); 8290316f99cSCristian Marussi if (!pinfo->powercaps) 8300316f99cSCristian Marussi return -ENOMEM; 8310316f99cSCristian Marussi 8320316f99cSCristian Marussi /* 8330316f99cSCristian Marussi * Note that any failure in retrieving any domain attribute leads to 8340316f99cSCristian Marussi * the whole Powercap protocol initialization failure: this way the 8350316f99cSCristian Marussi * reported Powercap domains are all assured, when accessed, to be well 8360316f99cSCristian Marussi * formed and correlated by sane parent-child relationship (if any). 8370316f99cSCristian Marussi */ 8380316f99cSCristian Marussi for (domain = 0; domain < pinfo->num_domains; domain++) { 8390316f99cSCristian Marussi ret = scmi_powercap_domain_attributes_get(ph, pinfo, domain); 8400316f99cSCristian Marussi if (ret) 8410316f99cSCristian Marussi return ret; 842855aa26eSCristian Marussi 843855aa26eSCristian Marussi if (pinfo->powercaps[domain].fastchannels) 844855aa26eSCristian Marussi scmi_powercap_domain_init_fc(ph, domain, 845855aa26eSCristian Marussi &pinfo->powercaps[domain].fc_info); 8460316f99cSCristian Marussi } 8470316f99cSCristian Marussi 8480316f99cSCristian Marussi pinfo->states = devm_kcalloc(ph->dev, pinfo->num_domains, 8490316f99cSCristian Marussi sizeof(*pinfo->states), GFP_KERNEL); 8500316f99cSCristian Marussi if (!pinfo->states) 8510316f99cSCristian Marussi return -ENOMEM; 8520316f99cSCristian Marussi 8530316f99cSCristian Marussi pinfo->version = version; 8540316f99cSCristian Marussi 8550316f99cSCristian Marussi return ph->set_priv(ph, pinfo); 8560316f99cSCristian Marussi } 8570316f99cSCristian Marussi 8580316f99cSCristian Marussi static const struct scmi_protocol scmi_powercap = { 8590316f99cSCristian Marussi .id = SCMI_PROTOCOL_POWERCAP, 8600316f99cSCristian Marussi .owner = THIS_MODULE, 8610316f99cSCristian Marussi .instance_init = &scmi_powercap_protocol_init, 8620316f99cSCristian Marussi .ops = &powercap_proto_ops, 8630316f99cSCristian Marussi .events = &powercap_protocol_events, 8640316f99cSCristian Marussi }; 8650316f99cSCristian Marussi 8660316f99cSCristian Marussi DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(powercap, scmi_powercap) 867