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> 11*855aa26eSCristian Marussi #include <linux/io.h> 120316f99cSCristian Marussi #include <linux/module.h> 130316f99cSCristian Marussi #include <linux/scmi_protocol.h> 140316f99cSCristian Marussi 150316f99cSCristian Marussi #include "protocols.h" 160316f99cSCristian Marussi #include "notify.h" 170316f99cSCristian Marussi 180316f99cSCristian Marussi enum scmi_powercap_protocol_cmd { 190316f99cSCristian Marussi POWERCAP_DOMAIN_ATTRIBUTES = 0x3, 200316f99cSCristian Marussi POWERCAP_CAP_GET = 0x4, 210316f99cSCristian Marussi POWERCAP_CAP_SET = 0x5, 220316f99cSCristian Marussi POWERCAP_PAI_GET = 0x6, 230316f99cSCristian Marussi POWERCAP_PAI_SET = 0x7, 240316f99cSCristian Marussi POWERCAP_DOMAIN_NAME_GET = 0x8, 250316f99cSCristian Marussi POWERCAP_MEASUREMENTS_GET = 0x9, 260316f99cSCristian Marussi POWERCAP_CAP_NOTIFY = 0xa, 270316f99cSCristian Marussi POWERCAP_MEASUREMENTS_NOTIFY = 0xb, 280316f99cSCristian Marussi POWERCAP_DESCRIBE_FASTCHANNEL = 0xc, 290316f99cSCristian Marussi }; 300316f99cSCristian Marussi 31*855aa26eSCristian Marussi enum { 32*855aa26eSCristian Marussi POWERCAP_FC_CAP, 33*855aa26eSCristian Marussi POWERCAP_FC_PAI, 34*855aa26eSCristian Marussi POWERCAP_FC_MAX, 35*855aa26eSCristian Marussi }; 36*855aa26eSCristian Marussi 370316f99cSCristian Marussi struct scmi_msg_resp_powercap_domain_attributes { 380316f99cSCristian Marussi __le32 attributes; 390316f99cSCristian Marussi #define SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(x) ((x) & BIT(31)) 400316f99cSCristian Marussi #define SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(x) ((x) & BIT(30)) 410316f99cSCristian Marussi #define SUPPORTS_ASYNC_POWERCAP_CAP_SET(x) ((x) & BIT(29)) 420316f99cSCristian Marussi #define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(28)) 430316f99cSCristian Marussi #define SUPPORTS_POWERCAP_CAP_CONFIGURATION(x) ((x) & BIT(27)) 440316f99cSCristian Marussi #define SUPPORTS_POWERCAP_MONITORING(x) ((x) & BIT(26)) 450316f99cSCristian Marussi #define SUPPORTS_POWERCAP_PAI_CONFIGURATION(x) ((x) & BIT(25)) 46*855aa26eSCristian Marussi #define SUPPORTS_POWERCAP_FASTCHANNELS(x) ((x) & BIT(22)) 470316f99cSCristian Marussi #define POWERCAP_POWER_UNIT(x) \ 480316f99cSCristian Marussi (FIELD_GET(GENMASK(24, 23), (x))) 490316f99cSCristian Marussi #define SUPPORTS_POWER_UNITS_MW(x) \ 500316f99cSCristian Marussi (POWERCAP_POWER_UNIT(x) == 0x2) 510316f99cSCristian Marussi #define SUPPORTS_POWER_UNITS_UW(x) \ 520316f99cSCristian Marussi (POWERCAP_POWER_UNIT(x) == 0x1) 530316f99cSCristian Marussi u8 name[SCMI_SHORT_NAME_MAX_SIZE]; 540316f99cSCristian Marussi __le32 min_pai; 550316f99cSCristian Marussi __le32 max_pai; 560316f99cSCristian Marussi __le32 pai_step; 570316f99cSCristian Marussi __le32 min_power_cap; 580316f99cSCristian Marussi __le32 max_power_cap; 590316f99cSCristian Marussi __le32 power_cap_step; 600316f99cSCristian Marussi __le32 sustainable_power; 610316f99cSCristian Marussi __le32 accuracy; 620316f99cSCristian Marussi __le32 parent_id; 630316f99cSCristian Marussi }; 640316f99cSCristian Marussi 650316f99cSCristian Marussi struct scmi_msg_powercap_set_cap_or_pai { 660316f99cSCristian Marussi __le32 domain; 670316f99cSCristian Marussi __le32 flags; 680316f99cSCristian Marussi #define CAP_SET_ASYNC BIT(1) 690316f99cSCristian Marussi #define CAP_SET_IGNORE_DRESP BIT(0) 700316f99cSCristian Marussi __le32 value; 710316f99cSCristian Marussi }; 720316f99cSCristian Marussi 730316f99cSCristian Marussi struct scmi_msg_resp_powercap_cap_set_complete { 740316f99cSCristian Marussi __le32 domain; 750316f99cSCristian Marussi __le32 power_cap; 760316f99cSCristian Marussi }; 770316f99cSCristian Marussi 780316f99cSCristian Marussi struct scmi_msg_resp_powercap_meas_get { 790316f99cSCristian Marussi __le32 power; 800316f99cSCristian Marussi __le32 pai; 810316f99cSCristian Marussi }; 820316f99cSCristian Marussi 830316f99cSCristian Marussi struct scmi_msg_powercap_notify_cap { 840316f99cSCristian Marussi __le32 domain; 850316f99cSCristian Marussi __le32 notify_enable; 860316f99cSCristian Marussi }; 870316f99cSCristian Marussi 880316f99cSCristian Marussi struct scmi_msg_powercap_notify_thresh { 890316f99cSCristian Marussi __le32 domain; 900316f99cSCristian Marussi __le32 notify_enable; 910316f99cSCristian Marussi __le32 power_thresh_low; 920316f99cSCristian Marussi __le32 power_thresh_high; 930316f99cSCristian Marussi }; 940316f99cSCristian Marussi 950316f99cSCristian Marussi struct scmi_powercap_cap_changed_notify_payld { 960316f99cSCristian Marussi __le32 agent_id; 970316f99cSCristian Marussi __le32 domain_id; 980316f99cSCristian Marussi __le32 power_cap; 990316f99cSCristian Marussi __le32 pai; 1000316f99cSCristian Marussi }; 1010316f99cSCristian Marussi 1020316f99cSCristian Marussi struct scmi_powercap_meas_changed_notify_payld { 1030316f99cSCristian Marussi __le32 agent_id; 1040316f99cSCristian Marussi __le32 domain_id; 1050316f99cSCristian Marussi __le32 power; 1060316f99cSCristian Marussi }; 1070316f99cSCristian Marussi 1080316f99cSCristian Marussi struct scmi_powercap_state { 1090316f99cSCristian Marussi bool meas_notif_enabled; 1100316f99cSCristian Marussi u64 thresholds; 1110316f99cSCristian Marussi #define THRESH_LOW(p, id) \ 1120316f99cSCristian Marussi (lower_32_bits((p)->states[(id)].thresholds)) 1130316f99cSCristian Marussi #define THRESH_HIGH(p, id) \ 1140316f99cSCristian Marussi (upper_32_bits((p)->states[(id)].thresholds)) 1150316f99cSCristian Marussi }; 1160316f99cSCristian Marussi 1170316f99cSCristian Marussi struct powercap_info { 1180316f99cSCristian Marussi u32 version; 1190316f99cSCristian Marussi int num_domains; 1200316f99cSCristian Marussi struct scmi_powercap_state *states; 1210316f99cSCristian Marussi struct scmi_powercap_info *powercaps; 1220316f99cSCristian Marussi }; 1230316f99cSCristian Marussi 1240316f99cSCristian Marussi static enum scmi_powercap_protocol_cmd evt_2_cmd[] = { 1250316f99cSCristian Marussi POWERCAP_CAP_NOTIFY, 1260316f99cSCristian Marussi POWERCAP_MEASUREMENTS_NOTIFY, 1270316f99cSCristian Marussi }; 1280316f99cSCristian Marussi 1290316f99cSCristian Marussi static int scmi_powercap_notify(const struct scmi_protocol_handle *ph, 1300316f99cSCristian Marussi u32 domain, int message_id, bool enable); 1310316f99cSCristian Marussi 1320316f99cSCristian Marussi static int 1330316f99cSCristian Marussi scmi_powercap_attributes_get(const struct scmi_protocol_handle *ph, 1340316f99cSCristian Marussi struct powercap_info *pi) 1350316f99cSCristian Marussi { 1360316f99cSCristian Marussi int ret; 1370316f99cSCristian Marussi struct scmi_xfer *t; 1380316f99cSCristian Marussi 1390316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0, 1400316f99cSCristian Marussi sizeof(u32), &t); 1410316f99cSCristian Marussi if (ret) 1420316f99cSCristian Marussi return ret; 1430316f99cSCristian Marussi 1440316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 1450316f99cSCristian Marussi if (!ret) { 1460316f99cSCristian Marussi u32 attributes; 1470316f99cSCristian Marussi 1480316f99cSCristian Marussi attributes = get_unaligned_le32(t->rx.buf); 1490316f99cSCristian Marussi pi->num_domains = FIELD_GET(GENMASK(15, 0), attributes); 1500316f99cSCristian Marussi } 1510316f99cSCristian Marussi 1520316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 1530316f99cSCristian Marussi return ret; 1540316f99cSCristian Marussi } 1550316f99cSCristian Marussi 1560316f99cSCristian Marussi static inline int 1570316f99cSCristian Marussi scmi_powercap_validate(unsigned int min_val, unsigned int max_val, 1580316f99cSCristian Marussi unsigned int step_val, bool configurable) 1590316f99cSCristian Marussi { 1600316f99cSCristian Marussi if (!min_val || !max_val) 1610316f99cSCristian Marussi return -EPROTO; 1620316f99cSCristian Marussi 1630316f99cSCristian Marussi if ((configurable && min_val == max_val) || 1640316f99cSCristian Marussi (!configurable && min_val != max_val)) 1650316f99cSCristian Marussi return -EPROTO; 1660316f99cSCristian Marussi 1670316f99cSCristian Marussi if (min_val != max_val && !step_val) 1680316f99cSCristian Marussi return -EPROTO; 1690316f99cSCristian Marussi 1700316f99cSCristian Marussi return 0; 1710316f99cSCristian Marussi } 1720316f99cSCristian Marussi 1730316f99cSCristian Marussi static int 1740316f99cSCristian Marussi scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph, 1750316f99cSCristian Marussi struct powercap_info *pinfo, u32 domain) 1760316f99cSCristian Marussi { 1770316f99cSCristian Marussi int ret; 1780316f99cSCristian Marussi u32 flags; 1790316f99cSCristian Marussi struct scmi_xfer *t; 1800316f99cSCristian Marussi struct scmi_powercap_info *dom_info = pinfo->powercaps + domain; 1810316f99cSCristian Marussi struct scmi_msg_resp_powercap_domain_attributes *resp; 1820316f99cSCristian Marussi 1830316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_DOMAIN_ATTRIBUTES, 1840316f99cSCristian Marussi sizeof(domain), sizeof(*resp), &t); 1850316f99cSCristian Marussi if (ret) 1860316f99cSCristian Marussi return ret; 1870316f99cSCristian Marussi 1880316f99cSCristian Marussi put_unaligned_le32(domain, t->tx.buf); 1890316f99cSCristian Marussi resp = t->rx.buf; 1900316f99cSCristian Marussi 1910316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 1920316f99cSCristian Marussi if (!ret) { 1930316f99cSCristian Marussi flags = le32_to_cpu(resp->attributes); 1940316f99cSCristian Marussi 1950316f99cSCristian Marussi dom_info->id = domain; 1960316f99cSCristian Marussi dom_info->notify_powercap_cap_change = 1970316f99cSCristian Marussi SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags); 1980316f99cSCristian Marussi dom_info->notify_powercap_measurement_change = 1990316f99cSCristian Marussi SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags); 2000316f99cSCristian Marussi dom_info->async_powercap_cap_set = 2010316f99cSCristian Marussi SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags); 2020316f99cSCristian Marussi dom_info->powercap_cap_config = 2030316f99cSCristian Marussi SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags); 2040316f99cSCristian Marussi dom_info->powercap_monitoring = 2050316f99cSCristian Marussi SUPPORTS_POWERCAP_MONITORING(flags); 2060316f99cSCristian Marussi dom_info->powercap_pai_config = 2070316f99cSCristian Marussi SUPPORTS_POWERCAP_PAI_CONFIGURATION(flags); 2080316f99cSCristian Marussi dom_info->powercap_scale_mw = 2090316f99cSCristian Marussi SUPPORTS_POWER_UNITS_MW(flags); 2100316f99cSCristian Marussi dom_info->powercap_scale_uw = 2110316f99cSCristian Marussi SUPPORTS_POWER_UNITS_UW(flags); 212*855aa26eSCristian Marussi dom_info->fastchannels = 213*855aa26eSCristian Marussi SUPPORTS_POWERCAP_FASTCHANNELS(flags); 2140316f99cSCristian Marussi 2150316f99cSCristian Marussi strscpy(dom_info->name, resp->name, SCMI_SHORT_NAME_MAX_SIZE); 2160316f99cSCristian Marussi 2170316f99cSCristian Marussi dom_info->min_pai = le32_to_cpu(resp->min_pai); 2180316f99cSCristian Marussi dom_info->max_pai = le32_to_cpu(resp->max_pai); 2190316f99cSCristian Marussi dom_info->pai_step = le32_to_cpu(resp->pai_step); 2200316f99cSCristian Marussi ret = scmi_powercap_validate(dom_info->min_pai, 2210316f99cSCristian Marussi dom_info->max_pai, 2220316f99cSCristian Marussi dom_info->pai_step, 2230316f99cSCristian Marussi dom_info->powercap_pai_config); 2240316f99cSCristian Marussi if (ret) { 2250316f99cSCristian Marussi dev_err(ph->dev, 2260316f99cSCristian Marussi "Platform reported inconsistent PAI config for domain %d - %s\n", 2270316f99cSCristian Marussi dom_info->id, dom_info->name); 2280316f99cSCristian Marussi goto clean; 2290316f99cSCristian Marussi } 2300316f99cSCristian Marussi 2310316f99cSCristian Marussi dom_info->min_power_cap = le32_to_cpu(resp->min_power_cap); 2320316f99cSCristian Marussi dom_info->max_power_cap = le32_to_cpu(resp->max_power_cap); 2330316f99cSCristian Marussi dom_info->power_cap_step = le32_to_cpu(resp->power_cap_step); 2340316f99cSCristian Marussi ret = scmi_powercap_validate(dom_info->min_power_cap, 2350316f99cSCristian Marussi dom_info->max_power_cap, 2360316f99cSCristian Marussi dom_info->power_cap_step, 2370316f99cSCristian Marussi dom_info->powercap_cap_config); 2380316f99cSCristian Marussi if (ret) { 2390316f99cSCristian Marussi dev_err(ph->dev, 2400316f99cSCristian Marussi "Platform reported inconsistent CAP config for domain %d - %s\n", 2410316f99cSCristian Marussi dom_info->id, dom_info->name); 2420316f99cSCristian Marussi goto clean; 2430316f99cSCristian Marussi } 2440316f99cSCristian Marussi 2450316f99cSCristian Marussi dom_info->sustainable_power = 2460316f99cSCristian Marussi le32_to_cpu(resp->sustainable_power); 2470316f99cSCristian Marussi dom_info->accuracy = le32_to_cpu(resp->accuracy); 2480316f99cSCristian Marussi 2490316f99cSCristian Marussi dom_info->parent_id = le32_to_cpu(resp->parent_id); 2500316f99cSCristian Marussi if (dom_info->parent_id != SCMI_POWERCAP_ROOT_ZONE_ID && 2510316f99cSCristian Marussi (dom_info->parent_id >= pinfo->num_domains || 2520316f99cSCristian Marussi dom_info->parent_id == dom_info->id)) { 2530316f99cSCristian Marussi dev_err(ph->dev, 2540316f99cSCristian Marussi "Platform reported inconsistent parent ID for domain %d - %s\n", 2550316f99cSCristian Marussi dom_info->id, dom_info->name); 2560316f99cSCristian Marussi ret = -ENODEV; 2570316f99cSCristian Marussi } 2580316f99cSCristian Marussi } 2590316f99cSCristian Marussi 2600316f99cSCristian Marussi clean: 2610316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 2620316f99cSCristian Marussi 2630316f99cSCristian Marussi /* 2640316f99cSCristian Marussi * If supported overwrite short name with the extended one; 2650316f99cSCristian Marussi * on error just carry on and use already provided short name. 2660316f99cSCristian Marussi */ 2670316f99cSCristian Marussi if (!ret && SUPPORTS_EXTENDED_NAMES(flags)) 2680316f99cSCristian Marussi ph->hops->extended_name_get(ph, POWERCAP_DOMAIN_NAME_GET, 2690316f99cSCristian Marussi domain, dom_info->name, 2700316f99cSCristian Marussi SCMI_MAX_STR_SIZE); 2710316f99cSCristian Marussi 2720316f99cSCristian Marussi return ret; 2730316f99cSCristian Marussi } 2740316f99cSCristian Marussi 2750316f99cSCristian Marussi static int scmi_powercap_num_domains_get(const struct scmi_protocol_handle *ph) 2760316f99cSCristian Marussi { 2770316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 2780316f99cSCristian Marussi 2790316f99cSCristian Marussi return pi->num_domains; 2800316f99cSCristian Marussi } 2810316f99cSCristian Marussi 2820316f99cSCristian Marussi static const struct scmi_powercap_info * 2830316f99cSCristian Marussi scmi_powercap_dom_info_get(const struct scmi_protocol_handle *ph, u32 domain_id) 2840316f99cSCristian Marussi { 2850316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 2860316f99cSCristian Marussi 2870316f99cSCristian Marussi if (domain_id >= pi->num_domains) 2880316f99cSCristian Marussi return NULL; 2890316f99cSCristian Marussi 2900316f99cSCristian Marussi return pi->powercaps + domain_id; 2910316f99cSCristian Marussi } 2920316f99cSCristian Marussi 293*855aa26eSCristian Marussi static int scmi_powercap_xfer_cap_get(const struct scmi_protocol_handle *ph, 2940316f99cSCristian Marussi u32 domain_id, u32 *power_cap) 2950316f99cSCristian Marussi { 2960316f99cSCristian Marussi int ret; 2970316f99cSCristian Marussi struct scmi_xfer *t; 2980316f99cSCristian Marussi 2990316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_GET, sizeof(u32), 3000316f99cSCristian Marussi sizeof(u32), &t); 3010316f99cSCristian Marussi if (ret) 3020316f99cSCristian Marussi return ret; 3030316f99cSCristian Marussi 3040316f99cSCristian Marussi put_unaligned_le32(domain_id, t->tx.buf); 3050316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 3060316f99cSCristian Marussi if (!ret) 3070316f99cSCristian Marussi *power_cap = get_unaligned_le32(t->rx.buf); 3080316f99cSCristian Marussi 3090316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 3100316f99cSCristian Marussi 3110316f99cSCristian Marussi return ret; 3120316f99cSCristian Marussi } 3130316f99cSCristian Marussi 314*855aa26eSCristian Marussi static int scmi_powercap_cap_get(const struct scmi_protocol_handle *ph, 315*855aa26eSCristian Marussi u32 domain_id, u32 *power_cap) 316*855aa26eSCristian Marussi { 317*855aa26eSCristian Marussi struct scmi_powercap_info *dom; 318*855aa26eSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 319*855aa26eSCristian Marussi 320*855aa26eSCristian Marussi if (!power_cap || domain_id >= pi->num_domains) 321*855aa26eSCristian Marussi return -EINVAL; 322*855aa26eSCristian Marussi 323*855aa26eSCristian Marussi dom = pi->powercaps + domain_id; 324*855aa26eSCristian Marussi if (dom->fc_info && dom->fc_info[POWERCAP_FC_CAP].get_addr) { 325*855aa26eSCristian Marussi *power_cap = ioread32(dom->fc_info[POWERCAP_FC_CAP].get_addr); 326*855aa26eSCristian Marussi return 0; 327*855aa26eSCristian Marussi } 328*855aa26eSCristian Marussi 329*855aa26eSCristian Marussi return scmi_powercap_xfer_cap_get(ph, domain_id, power_cap); 330*855aa26eSCristian Marussi } 331*855aa26eSCristian Marussi 332*855aa26eSCristian Marussi static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph, 333*855aa26eSCristian Marussi const struct scmi_powercap_info *pc, 334*855aa26eSCristian Marussi u32 power_cap, bool ignore_dresp) 3350316f99cSCristian Marussi { 3360316f99cSCristian Marussi int ret; 3370316f99cSCristian Marussi struct scmi_xfer *t; 3380316f99cSCristian Marussi struct scmi_msg_powercap_set_cap_or_pai *msg; 3390316f99cSCristian Marussi 3400316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_SET, 3410316f99cSCristian Marussi sizeof(*msg), 0, &t); 3420316f99cSCristian Marussi if (ret) 3430316f99cSCristian Marussi return ret; 3440316f99cSCristian Marussi 3450316f99cSCristian Marussi msg = t->tx.buf; 346*855aa26eSCristian Marussi msg->domain = cpu_to_le32(pc->id); 3470316f99cSCristian Marussi msg->flags = 3480316f99cSCristian Marussi cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, !!pc->async_powercap_cap_set) | 3490316f99cSCristian Marussi FIELD_PREP(CAP_SET_IGNORE_DRESP, !!ignore_dresp)); 3500316f99cSCristian Marussi msg->value = cpu_to_le32(power_cap); 3510316f99cSCristian Marussi 3520316f99cSCristian Marussi if (!pc->async_powercap_cap_set || ignore_dresp) { 3530316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 3540316f99cSCristian Marussi } else { 3550316f99cSCristian Marussi ret = ph->xops->do_xfer_with_response(ph, t); 3560316f99cSCristian Marussi if (!ret) { 3570316f99cSCristian Marussi struct scmi_msg_resp_powercap_cap_set_complete *resp; 3580316f99cSCristian Marussi 3590316f99cSCristian Marussi resp = t->rx.buf; 360*855aa26eSCristian Marussi if (le32_to_cpu(resp->domain) == pc->id) 3610316f99cSCristian Marussi dev_dbg(ph->dev, 3620316f99cSCristian Marussi "Powercap ID %d CAP set async to %u\n", 363*855aa26eSCristian Marussi pc->id, 3640316f99cSCristian Marussi get_unaligned_le32(&resp->power_cap)); 3650316f99cSCristian Marussi else 3660316f99cSCristian Marussi ret = -EPROTO; 3670316f99cSCristian Marussi } 3680316f99cSCristian Marussi } 3690316f99cSCristian Marussi 3700316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 3710316f99cSCristian Marussi return ret; 3720316f99cSCristian Marussi } 3730316f99cSCristian Marussi 374*855aa26eSCristian Marussi static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph, 375*855aa26eSCristian Marussi u32 domain_id, u32 power_cap, 376*855aa26eSCristian Marussi bool ignore_dresp) 377*855aa26eSCristian Marussi { 378*855aa26eSCristian Marussi const struct scmi_powercap_info *pc; 379*855aa26eSCristian Marussi 380*855aa26eSCristian Marussi pc = scmi_powercap_dom_info_get(ph, domain_id); 381*855aa26eSCristian Marussi if (!pc || !pc->powercap_cap_config || !power_cap || 382*855aa26eSCristian Marussi power_cap < pc->min_power_cap || 383*855aa26eSCristian Marussi power_cap > pc->max_power_cap) 384*855aa26eSCristian Marussi return -EINVAL; 385*855aa26eSCristian Marussi 386*855aa26eSCristian Marussi if (pc->fc_info && pc->fc_info[POWERCAP_FC_CAP].set_addr) { 387*855aa26eSCristian Marussi struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_CAP]; 388*855aa26eSCristian Marussi 389*855aa26eSCristian Marussi iowrite32(power_cap, fci->set_addr); 390*855aa26eSCristian Marussi ph->hops->fastchannel_db_ring(fci->set_db); 391*855aa26eSCristian Marussi return 0; 392*855aa26eSCristian Marussi } 393*855aa26eSCristian Marussi 394*855aa26eSCristian Marussi return scmi_powercap_xfer_cap_set(ph, pc, power_cap, ignore_dresp); 395*855aa26eSCristian Marussi } 396*855aa26eSCristian Marussi 397*855aa26eSCristian Marussi static int scmi_powercap_xfer_pai_get(const struct scmi_protocol_handle *ph, 3980316f99cSCristian Marussi u32 domain_id, u32 *pai) 3990316f99cSCristian Marussi { 4000316f99cSCristian Marussi int ret; 4010316f99cSCristian Marussi struct scmi_xfer *t; 4020316f99cSCristian Marussi 4030316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_GET, sizeof(u32), 4040316f99cSCristian Marussi sizeof(u32), &t); 4050316f99cSCristian Marussi if (ret) 4060316f99cSCristian Marussi return ret; 4070316f99cSCristian Marussi 4080316f99cSCristian Marussi put_unaligned_le32(domain_id, t->tx.buf); 4090316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 4100316f99cSCristian Marussi if (!ret) 4110316f99cSCristian Marussi *pai = get_unaligned_le32(t->rx.buf); 4120316f99cSCristian Marussi 4130316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 4140316f99cSCristian Marussi 4150316f99cSCristian Marussi return ret; 4160316f99cSCristian Marussi } 4170316f99cSCristian Marussi 418*855aa26eSCristian Marussi static int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph, 419*855aa26eSCristian Marussi u32 domain_id, u32 *pai) 420*855aa26eSCristian Marussi { 421*855aa26eSCristian Marussi struct scmi_powercap_info *dom; 422*855aa26eSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 423*855aa26eSCristian Marussi 424*855aa26eSCristian Marussi if (!pai || domain_id >= pi->num_domains) 425*855aa26eSCristian Marussi return -EINVAL; 426*855aa26eSCristian Marussi 427*855aa26eSCristian Marussi dom = pi->powercaps + domain_id; 428*855aa26eSCristian Marussi if (dom->fc_info && dom->fc_info[POWERCAP_FC_PAI].get_addr) { 429*855aa26eSCristian Marussi *pai = ioread32(dom->fc_info[POWERCAP_FC_PAI].get_addr); 430*855aa26eSCristian Marussi return 0; 431*855aa26eSCristian Marussi } 432*855aa26eSCristian Marussi 433*855aa26eSCristian Marussi return scmi_powercap_xfer_pai_get(ph, domain_id, pai); 434*855aa26eSCristian Marussi } 435*855aa26eSCristian Marussi 436*855aa26eSCristian Marussi static int scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle *ph, 4370316f99cSCristian Marussi u32 domain_id, u32 pai) 4380316f99cSCristian Marussi { 4390316f99cSCristian Marussi int ret; 4400316f99cSCristian Marussi struct scmi_xfer *t; 4410316f99cSCristian Marussi struct scmi_msg_powercap_set_cap_or_pai *msg; 4420316f99cSCristian Marussi 4430316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_SET, 4440316f99cSCristian Marussi sizeof(*msg), 0, &t); 4450316f99cSCristian Marussi if (ret) 4460316f99cSCristian Marussi return ret; 4470316f99cSCristian Marussi 4480316f99cSCristian Marussi msg = t->tx.buf; 4490316f99cSCristian Marussi msg->domain = cpu_to_le32(domain_id); 4500316f99cSCristian Marussi msg->flags = cpu_to_le32(0); 4510316f99cSCristian Marussi msg->value = cpu_to_le32(pai); 4520316f99cSCristian Marussi 4530316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 4540316f99cSCristian Marussi 4550316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 4560316f99cSCristian Marussi return ret; 4570316f99cSCristian Marussi } 4580316f99cSCristian Marussi 459*855aa26eSCristian Marussi static int scmi_powercap_pai_set(const struct scmi_protocol_handle *ph, 460*855aa26eSCristian Marussi u32 domain_id, u32 pai) 461*855aa26eSCristian Marussi { 462*855aa26eSCristian Marussi const struct scmi_powercap_info *pc; 463*855aa26eSCristian Marussi 464*855aa26eSCristian Marussi pc = scmi_powercap_dom_info_get(ph, domain_id); 465*855aa26eSCristian Marussi if (!pc || !pc->powercap_pai_config || !pai || 466*855aa26eSCristian Marussi pai < pc->min_pai || pai > pc->max_pai) 467*855aa26eSCristian Marussi return -EINVAL; 468*855aa26eSCristian Marussi 469*855aa26eSCristian Marussi if (pc->fc_info && pc->fc_info[POWERCAP_FC_PAI].set_addr) { 470*855aa26eSCristian Marussi struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_PAI]; 471*855aa26eSCristian Marussi 472*855aa26eSCristian Marussi iowrite32(pai, fci->set_addr); 473*855aa26eSCristian Marussi ph->hops->fastchannel_db_ring(fci->set_db); 474*855aa26eSCristian Marussi return 0; 475*855aa26eSCristian Marussi } 476*855aa26eSCristian Marussi 477*855aa26eSCristian Marussi return scmi_powercap_xfer_pai_set(ph, domain_id, pai); 478*855aa26eSCristian Marussi } 479*855aa26eSCristian Marussi 4800316f99cSCristian Marussi static int scmi_powercap_measurements_get(const struct scmi_protocol_handle *ph, 4810316f99cSCristian Marussi u32 domain_id, u32 *average_power, 4820316f99cSCristian Marussi u32 *pai) 4830316f99cSCristian Marussi { 4840316f99cSCristian Marussi int ret; 4850316f99cSCristian Marussi struct scmi_xfer *t; 4860316f99cSCristian Marussi struct scmi_msg_resp_powercap_meas_get *resp; 4870316f99cSCristian Marussi const struct scmi_powercap_info *pc; 4880316f99cSCristian Marussi 4890316f99cSCristian Marussi pc = scmi_powercap_dom_info_get(ph, domain_id); 4900316f99cSCristian Marussi if (!pc || !pc->powercap_monitoring || !pai || !average_power) 4910316f99cSCristian Marussi return -EINVAL; 4920316f99cSCristian Marussi 4930316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_MEASUREMENTS_GET, 4940316f99cSCristian Marussi sizeof(u32), sizeof(*resp), &t); 4950316f99cSCristian Marussi if (ret) 4960316f99cSCristian Marussi return ret; 4970316f99cSCristian Marussi 4980316f99cSCristian Marussi resp = t->rx.buf; 4990316f99cSCristian Marussi put_unaligned_le32(domain_id, t->tx.buf); 5000316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 5010316f99cSCristian Marussi if (!ret) { 5020316f99cSCristian Marussi *average_power = le32_to_cpu(resp->power); 5030316f99cSCristian Marussi *pai = le32_to_cpu(resp->pai); 5040316f99cSCristian Marussi } 5050316f99cSCristian Marussi 5060316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 5070316f99cSCristian Marussi return ret; 5080316f99cSCristian Marussi } 5090316f99cSCristian Marussi 5100316f99cSCristian Marussi static int 5110316f99cSCristian Marussi scmi_powercap_measurements_threshold_get(const struct scmi_protocol_handle *ph, 5120316f99cSCristian Marussi u32 domain_id, u32 *power_thresh_low, 5130316f99cSCristian Marussi u32 *power_thresh_high) 5140316f99cSCristian Marussi { 5150316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 5160316f99cSCristian Marussi 5170316f99cSCristian Marussi if (!power_thresh_low || !power_thresh_high || 5180316f99cSCristian Marussi domain_id >= pi->num_domains) 5190316f99cSCristian Marussi return -EINVAL; 5200316f99cSCristian Marussi 5210316f99cSCristian Marussi *power_thresh_low = THRESH_LOW(pi, domain_id); 5220316f99cSCristian Marussi *power_thresh_high = THRESH_HIGH(pi, domain_id); 5230316f99cSCristian Marussi 5240316f99cSCristian Marussi return 0; 5250316f99cSCristian Marussi } 5260316f99cSCristian Marussi 5270316f99cSCristian Marussi static int 5280316f99cSCristian Marussi scmi_powercap_measurements_threshold_set(const struct scmi_protocol_handle *ph, 5290316f99cSCristian Marussi u32 domain_id, u32 power_thresh_low, 5300316f99cSCristian Marussi u32 power_thresh_high) 5310316f99cSCristian Marussi { 5320316f99cSCristian Marussi int ret = 0; 5330316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 5340316f99cSCristian Marussi 5350316f99cSCristian Marussi if (domain_id >= pi->num_domains || 5360316f99cSCristian Marussi power_thresh_low > power_thresh_high) 5370316f99cSCristian Marussi return -EINVAL; 5380316f99cSCristian Marussi 5390316f99cSCristian Marussi /* Anything to do ? */ 5400316f99cSCristian Marussi if (THRESH_LOW(pi, domain_id) == power_thresh_low && 5410316f99cSCristian Marussi THRESH_HIGH(pi, domain_id) == power_thresh_high) 5420316f99cSCristian Marussi return ret; 5430316f99cSCristian Marussi 5440316f99cSCristian Marussi pi->states[domain_id].thresholds = 5450316f99cSCristian Marussi (FIELD_PREP(GENMASK_ULL(31, 0), power_thresh_low) | 5460316f99cSCristian Marussi FIELD_PREP(GENMASK_ULL(63, 32), power_thresh_high)); 5470316f99cSCristian Marussi 5480316f99cSCristian Marussi /* Update thresholds if notification already enabled */ 5490316f99cSCristian Marussi if (pi->states[domain_id].meas_notif_enabled) 5500316f99cSCristian Marussi ret = scmi_powercap_notify(ph, domain_id, 5510316f99cSCristian Marussi POWERCAP_MEASUREMENTS_NOTIFY, 5520316f99cSCristian Marussi true); 5530316f99cSCristian Marussi 5540316f99cSCristian Marussi return ret; 5550316f99cSCristian Marussi } 5560316f99cSCristian Marussi 5570316f99cSCristian Marussi static const struct scmi_powercap_proto_ops powercap_proto_ops = { 5580316f99cSCristian Marussi .num_domains_get = scmi_powercap_num_domains_get, 5590316f99cSCristian Marussi .info_get = scmi_powercap_dom_info_get, 5600316f99cSCristian Marussi .cap_get = scmi_powercap_cap_get, 5610316f99cSCristian Marussi .cap_set = scmi_powercap_cap_set, 5620316f99cSCristian Marussi .pai_get = scmi_powercap_pai_get, 5630316f99cSCristian Marussi .pai_set = scmi_powercap_pai_set, 5640316f99cSCristian Marussi .measurements_get = scmi_powercap_measurements_get, 5650316f99cSCristian Marussi .measurements_threshold_set = scmi_powercap_measurements_threshold_set, 5660316f99cSCristian Marussi .measurements_threshold_get = scmi_powercap_measurements_threshold_get, 5670316f99cSCristian Marussi }; 5680316f99cSCristian Marussi 569*855aa26eSCristian Marussi static void scmi_powercap_domain_init_fc(const struct scmi_protocol_handle *ph, 570*855aa26eSCristian Marussi u32 domain, struct scmi_fc_info **p_fc) 571*855aa26eSCristian Marussi { 572*855aa26eSCristian Marussi struct scmi_fc_info *fc; 573*855aa26eSCristian Marussi 574*855aa26eSCristian Marussi fc = devm_kcalloc(ph->dev, POWERCAP_FC_MAX, sizeof(*fc), GFP_KERNEL); 575*855aa26eSCristian Marussi if (!fc) 576*855aa26eSCristian Marussi return; 577*855aa26eSCristian Marussi 578*855aa26eSCristian Marussi ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, 579*855aa26eSCristian Marussi POWERCAP_CAP_SET, 4, domain, 580*855aa26eSCristian Marussi &fc[POWERCAP_FC_CAP].set_addr, 581*855aa26eSCristian Marussi &fc[POWERCAP_FC_CAP].set_db); 582*855aa26eSCristian Marussi 583*855aa26eSCristian Marussi ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, 584*855aa26eSCristian Marussi POWERCAP_CAP_GET, 4, domain, 585*855aa26eSCristian Marussi &fc[POWERCAP_FC_CAP].get_addr, NULL); 586*855aa26eSCristian Marussi 587*855aa26eSCristian Marussi ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, 588*855aa26eSCristian Marussi POWERCAP_PAI_SET, 4, domain, 589*855aa26eSCristian Marussi &fc[POWERCAP_FC_PAI].set_addr, 590*855aa26eSCristian Marussi &fc[POWERCAP_FC_PAI].set_db); 591*855aa26eSCristian Marussi 592*855aa26eSCristian Marussi ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, 593*855aa26eSCristian Marussi POWERCAP_PAI_GET, 4, domain, 594*855aa26eSCristian Marussi &fc[POWERCAP_FC_PAI].get_addr, NULL); 595*855aa26eSCristian Marussi 596*855aa26eSCristian Marussi *p_fc = fc; 597*855aa26eSCristian Marussi } 598*855aa26eSCristian Marussi 5990316f99cSCristian Marussi static int scmi_powercap_notify(const struct scmi_protocol_handle *ph, 6000316f99cSCristian Marussi u32 domain, int message_id, bool enable) 6010316f99cSCristian Marussi { 6020316f99cSCristian Marussi int ret; 6030316f99cSCristian Marussi struct scmi_xfer *t; 6040316f99cSCristian Marussi 6050316f99cSCristian Marussi switch (message_id) { 6060316f99cSCristian Marussi case POWERCAP_CAP_NOTIFY: 6070316f99cSCristian Marussi { 6080316f99cSCristian Marussi struct scmi_msg_powercap_notify_cap *notify; 6090316f99cSCristian Marussi 6100316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, message_id, 6110316f99cSCristian Marussi sizeof(*notify), 0, &t); 6120316f99cSCristian Marussi if (ret) 6130316f99cSCristian Marussi return ret; 6140316f99cSCristian Marussi 6150316f99cSCristian Marussi notify = t->tx.buf; 6160316f99cSCristian Marussi notify->domain = cpu_to_le32(domain); 6170316f99cSCristian Marussi notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0); 6180316f99cSCristian Marussi break; 6190316f99cSCristian Marussi } 6200316f99cSCristian Marussi case POWERCAP_MEASUREMENTS_NOTIFY: 6210316f99cSCristian Marussi { 6220316f99cSCristian Marussi u32 low, high; 6230316f99cSCristian Marussi struct scmi_msg_powercap_notify_thresh *notify; 6240316f99cSCristian Marussi 6250316f99cSCristian Marussi /* 6260316f99cSCristian Marussi * Note that we have to pick the most recently configured 6270316f99cSCristian Marussi * thresholds to build a proper POWERCAP_MEASUREMENTS_NOTIFY 6280316f99cSCristian Marussi * enable request and we fail, complaining, if no thresholds 6290316f99cSCristian Marussi * were ever set, since this is an indication the API has been 6300316f99cSCristian Marussi * used wrongly. 6310316f99cSCristian Marussi */ 6320316f99cSCristian Marussi ret = scmi_powercap_measurements_threshold_get(ph, domain, 6330316f99cSCristian Marussi &low, &high); 6340316f99cSCristian Marussi if (ret) 6350316f99cSCristian Marussi return ret; 6360316f99cSCristian Marussi 6370316f99cSCristian Marussi if (enable && !low && !high) { 6380316f99cSCristian Marussi dev_err(ph->dev, 6390316f99cSCristian Marussi "Invalid Measurements Notify thresholds: %u/%u\n", 6400316f99cSCristian Marussi low, high); 6410316f99cSCristian Marussi return -EINVAL; 6420316f99cSCristian Marussi } 6430316f99cSCristian Marussi 6440316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, message_id, 6450316f99cSCristian Marussi sizeof(*notify), 0, &t); 6460316f99cSCristian Marussi if (ret) 6470316f99cSCristian Marussi return ret; 6480316f99cSCristian Marussi 6490316f99cSCristian Marussi notify = t->tx.buf; 6500316f99cSCristian Marussi notify->domain = cpu_to_le32(domain); 6510316f99cSCristian Marussi notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0); 6520316f99cSCristian Marussi notify->power_thresh_low = cpu_to_le32(low); 6530316f99cSCristian Marussi notify->power_thresh_high = cpu_to_le32(high); 6540316f99cSCristian Marussi break; 6550316f99cSCristian Marussi } 6560316f99cSCristian Marussi default: 6570316f99cSCristian Marussi return -EINVAL; 6580316f99cSCristian Marussi } 6590316f99cSCristian Marussi 6600316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 6610316f99cSCristian Marussi 6620316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 6630316f99cSCristian Marussi return ret; 6640316f99cSCristian Marussi } 6650316f99cSCristian Marussi 6660316f99cSCristian Marussi static int 6670316f99cSCristian Marussi scmi_powercap_set_notify_enabled(const struct scmi_protocol_handle *ph, 6680316f99cSCristian Marussi u8 evt_id, u32 src_id, bool enable) 6690316f99cSCristian Marussi { 6700316f99cSCristian Marussi int ret, cmd_id; 6710316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 6720316f99cSCristian Marussi 6730316f99cSCristian Marussi if (evt_id >= ARRAY_SIZE(evt_2_cmd) || src_id >= pi->num_domains) 6740316f99cSCristian Marussi return -EINVAL; 6750316f99cSCristian Marussi 6760316f99cSCristian Marussi cmd_id = evt_2_cmd[evt_id]; 6770316f99cSCristian Marussi ret = scmi_powercap_notify(ph, src_id, cmd_id, enable); 6780316f99cSCristian Marussi if (ret) 6790316f99cSCristian Marussi pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n", 6800316f99cSCristian Marussi evt_id, src_id, ret); 6810316f99cSCristian Marussi else if (cmd_id == POWERCAP_MEASUREMENTS_NOTIFY) 6820316f99cSCristian Marussi /* 6830316f99cSCristian Marussi * On success save the current notification enabled state, so 6840316f99cSCristian Marussi * as to be able to properly update the notification thresholds 6850316f99cSCristian Marussi * when they are modified on a domain for which measurement 6860316f99cSCristian Marussi * notifications were currently enabled. 6870316f99cSCristian Marussi * 6880316f99cSCristian Marussi * This is needed because the SCMI Notification core machinery 6890316f99cSCristian Marussi * and API does not support passing per-notification custom 6900316f99cSCristian Marussi * arguments at callback registration time. 6910316f99cSCristian Marussi * 6920316f99cSCristian Marussi * Note that this can be done here with a simple flag since the 6930316f99cSCristian Marussi * SCMI core Notifications code takes care of keeping proper 6940316f99cSCristian Marussi * per-domain enables refcounting, so that this helper function 6950316f99cSCristian Marussi * will be called only once (for enables) when the first user 6960316f99cSCristian Marussi * registers a callback on this domain and once more (disable) 6970316f99cSCristian Marussi * when the last user de-registers its callback. 6980316f99cSCristian Marussi */ 6990316f99cSCristian Marussi pi->states[src_id].meas_notif_enabled = enable; 7000316f99cSCristian Marussi 7010316f99cSCristian Marussi return ret; 7020316f99cSCristian Marussi } 7030316f99cSCristian Marussi 7040316f99cSCristian Marussi static void * 7050316f99cSCristian Marussi scmi_powercap_fill_custom_report(const struct scmi_protocol_handle *ph, 7060316f99cSCristian Marussi u8 evt_id, ktime_t timestamp, 7070316f99cSCristian Marussi const void *payld, size_t payld_sz, 7080316f99cSCristian Marussi void *report, u32 *src_id) 7090316f99cSCristian Marussi { 7100316f99cSCristian Marussi void *rep = NULL; 7110316f99cSCristian Marussi 7120316f99cSCristian Marussi switch (evt_id) { 7130316f99cSCristian Marussi case SCMI_EVENT_POWERCAP_CAP_CHANGED: 7140316f99cSCristian Marussi { 7150316f99cSCristian Marussi const struct scmi_powercap_cap_changed_notify_payld *p = payld; 7160316f99cSCristian Marussi struct scmi_powercap_cap_changed_report *r = report; 7170316f99cSCristian Marussi 7180316f99cSCristian Marussi if (sizeof(*p) != payld_sz) 7190316f99cSCristian Marussi break; 7200316f99cSCristian Marussi 7210316f99cSCristian Marussi r->timestamp = timestamp; 7220316f99cSCristian Marussi r->agent_id = le32_to_cpu(p->agent_id); 7230316f99cSCristian Marussi r->domain_id = le32_to_cpu(p->domain_id); 7240316f99cSCristian Marussi r->power_cap = le32_to_cpu(p->power_cap); 7250316f99cSCristian Marussi r->pai = le32_to_cpu(p->pai); 7260316f99cSCristian Marussi *src_id = r->domain_id; 7270316f99cSCristian Marussi rep = r; 7280316f99cSCristian Marussi break; 7290316f99cSCristian Marussi } 7300316f99cSCristian Marussi case SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED: 7310316f99cSCristian Marussi { 7320316f99cSCristian Marussi const struct scmi_powercap_meas_changed_notify_payld *p = payld; 7330316f99cSCristian Marussi struct scmi_powercap_meas_changed_report *r = report; 7340316f99cSCristian Marussi 7350316f99cSCristian Marussi if (sizeof(*p) != payld_sz) 7360316f99cSCristian Marussi break; 7370316f99cSCristian Marussi 7380316f99cSCristian Marussi r->timestamp = timestamp; 7390316f99cSCristian Marussi r->agent_id = le32_to_cpu(p->agent_id); 7400316f99cSCristian Marussi r->domain_id = le32_to_cpu(p->domain_id); 7410316f99cSCristian Marussi r->power = le32_to_cpu(p->power); 7420316f99cSCristian Marussi *src_id = r->domain_id; 7430316f99cSCristian Marussi rep = r; 7440316f99cSCristian Marussi break; 7450316f99cSCristian Marussi } 7460316f99cSCristian Marussi default: 7470316f99cSCristian Marussi break; 7480316f99cSCristian Marussi } 7490316f99cSCristian Marussi 7500316f99cSCristian Marussi return rep; 7510316f99cSCristian Marussi } 7520316f99cSCristian Marussi 7530316f99cSCristian Marussi static int 7540316f99cSCristian Marussi scmi_powercap_get_num_sources(const struct scmi_protocol_handle *ph) 7550316f99cSCristian Marussi { 7560316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 7570316f99cSCristian Marussi 7580316f99cSCristian Marussi if (!pi) 7590316f99cSCristian Marussi return -EINVAL; 7600316f99cSCristian Marussi 7610316f99cSCristian Marussi return pi->num_domains; 7620316f99cSCristian Marussi } 7630316f99cSCristian Marussi 7640316f99cSCristian Marussi static const struct scmi_event powercap_events[] = { 7650316f99cSCristian Marussi { 7660316f99cSCristian Marussi .id = SCMI_EVENT_POWERCAP_CAP_CHANGED, 7670316f99cSCristian Marussi .max_payld_sz = 7680316f99cSCristian Marussi sizeof(struct scmi_powercap_cap_changed_notify_payld), 7690316f99cSCristian Marussi .max_report_sz = 7700316f99cSCristian Marussi sizeof(struct scmi_powercap_cap_changed_report), 7710316f99cSCristian Marussi }, 7720316f99cSCristian Marussi { 7730316f99cSCristian Marussi .id = SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED, 7740316f99cSCristian Marussi .max_payld_sz = 7750316f99cSCristian Marussi sizeof(struct scmi_powercap_meas_changed_notify_payld), 7760316f99cSCristian Marussi .max_report_sz = 7770316f99cSCristian Marussi sizeof(struct scmi_powercap_meas_changed_report), 7780316f99cSCristian Marussi }, 7790316f99cSCristian Marussi }; 7800316f99cSCristian Marussi 7810316f99cSCristian Marussi static const struct scmi_event_ops powercap_event_ops = { 7820316f99cSCristian Marussi .get_num_sources = scmi_powercap_get_num_sources, 7830316f99cSCristian Marussi .set_notify_enabled = scmi_powercap_set_notify_enabled, 7840316f99cSCristian Marussi .fill_custom_report = scmi_powercap_fill_custom_report, 7850316f99cSCristian Marussi }; 7860316f99cSCristian Marussi 7870316f99cSCristian Marussi static const struct scmi_protocol_events powercap_protocol_events = { 7880316f99cSCristian Marussi .queue_sz = SCMI_PROTO_QUEUE_SZ, 7890316f99cSCristian Marussi .ops = &powercap_event_ops, 7900316f99cSCristian Marussi .evts = powercap_events, 7910316f99cSCristian Marussi .num_events = ARRAY_SIZE(powercap_events), 7920316f99cSCristian Marussi }; 7930316f99cSCristian Marussi 7940316f99cSCristian Marussi static int 7950316f99cSCristian Marussi scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph) 7960316f99cSCristian Marussi { 7970316f99cSCristian Marussi int domain, ret; 7980316f99cSCristian Marussi u32 version; 7990316f99cSCristian Marussi struct powercap_info *pinfo; 8000316f99cSCristian Marussi 8010316f99cSCristian Marussi ret = ph->xops->version_get(ph, &version); 8020316f99cSCristian Marussi if (ret) 8030316f99cSCristian Marussi return ret; 8040316f99cSCristian Marussi 8050316f99cSCristian Marussi dev_dbg(ph->dev, "Powercap Version %d.%d\n", 8060316f99cSCristian Marussi PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); 8070316f99cSCristian Marussi 8080316f99cSCristian Marussi pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL); 8090316f99cSCristian Marussi if (!pinfo) 8100316f99cSCristian Marussi return -ENOMEM; 8110316f99cSCristian Marussi 8120316f99cSCristian Marussi ret = scmi_powercap_attributes_get(ph, pinfo); 8130316f99cSCristian Marussi if (ret) 8140316f99cSCristian Marussi return ret; 8150316f99cSCristian Marussi 8160316f99cSCristian Marussi pinfo->powercaps = devm_kcalloc(ph->dev, pinfo->num_domains, 8170316f99cSCristian Marussi sizeof(*pinfo->powercaps), 8180316f99cSCristian Marussi GFP_KERNEL); 8190316f99cSCristian Marussi if (!pinfo->powercaps) 8200316f99cSCristian Marussi return -ENOMEM; 8210316f99cSCristian Marussi 8220316f99cSCristian Marussi /* 8230316f99cSCristian Marussi * Note that any failure in retrieving any domain attribute leads to 8240316f99cSCristian Marussi * the whole Powercap protocol initialization failure: this way the 8250316f99cSCristian Marussi * reported Powercap domains are all assured, when accessed, to be well 8260316f99cSCristian Marussi * formed and correlated by sane parent-child relationship (if any). 8270316f99cSCristian Marussi */ 8280316f99cSCristian Marussi for (domain = 0; domain < pinfo->num_domains; domain++) { 8290316f99cSCristian Marussi ret = scmi_powercap_domain_attributes_get(ph, pinfo, domain); 8300316f99cSCristian Marussi if (ret) 8310316f99cSCristian Marussi return ret; 832*855aa26eSCristian Marussi 833*855aa26eSCristian Marussi if (pinfo->powercaps[domain].fastchannels) 834*855aa26eSCristian Marussi scmi_powercap_domain_init_fc(ph, domain, 835*855aa26eSCristian Marussi &pinfo->powercaps[domain].fc_info); 8360316f99cSCristian Marussi } 8370316f99cSCristian Marussi 8380316f99cSCristian Marussi pinfo->states = devm_kcalloc(ph->dev, pinfo->num_domains, 8390316f99cSCristian Marussi sizeof(*pinfo->states), GFP_KERNEL); 8400316f99cSCristian Marussi if (!pinfo->states) 8410316f99cSCristian Marussi return -ENOMEM; 8420316f99cSCristian Marussi 8430316f99cSCristian Marussi pinfo->version = version; 8440316f99cSCristian Marussi 8450316f99cSCristian Marussi return ph->set_priv(ph, pinfo); 8460316f99cSCristian Marussi } 8470316f99cSCristian Marussi 8480316f99cSCristian Marussi static const struct scmi_protocol scmi_powercap = { 8490316f99cSCristian Marussi .id = SCMI_PROTOCOL_POWERCAP, 8500316f99cSCristian Marussi .owner = THIS_MODULE, 8510316f99cSCristian Marussi .instance_init = &scmi_powercap_protocol_init, 8520316f99cSCristian Marussi .ops = &powercap_proto_ops, 8530316f99cSCristian Marussi .events = &powercap_protocol_events, 8540316f99cSCristian Marussi }; 8550316f99cSCristian Marussi 8560316f99cSCristian Marussi DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(powercap, scmi_powercap) 857