10316f99cSCristian Marussi // SPDX-License-Identifier: GPL-2.0 20316f99cSCristian Marussi /* 30316f99cSCristian Marussi * System Control and Management Interface (SCMI) Powercap Protocol 40316f99cSCristian Marussi * 50316f99cSCristian Marussi * Copyright (C) 2022 ARM Ltd. 60316f99cSCristian Marussi */ 70316f99cSCristian Marussi 80316f99cSCristian Marussi #define pr_fmt(fmt) "SCMI Notifications POWERCAP - " fmt 90316f99cSCristian Marussi 100316f99cSCristian Marussi #include <linux/bitfield.h> 11855aa26eSCristian Marussi #include <linux/io.h> 120316f99cSCristian Marussi #include <linux/module.h> 130316f99cSCristian Marussi #include <linux/scmi_protocol.h> 140316f99cSCristian Marussi 15b27d04d5SCristian Marussi #include <trace/events/scmi.h> 16b27d04d5SCristian Marussi 170316f99cSCristian Marussi #include "protocols.h" 180316f99cSCristian Marussi #include "notify.h" 190316f99cSCristian Marussi 200316f99cSCristian Marussi enum scmi_powercap_protocol_cmd { 210316f99cSCristian Marussi POWERCAP_DOMAIN_ATTRIBUTES = 0x3, 220316f99cSCristian Marussi POWERCAP_CAP_GET = 0x4, 230316f99cSCristian Marussi POWERCAP_CAP_SET = 0x5, 240316f99cSCristian Marussi POWERCAP_PAI_GET = 0x6, 250316f99cSCristian Marussi POWERCAP_PAI_SET = 0x7, 260316f99cSCristian Marussi POWERCAP_DOMAIN_NAME_GET = 0x8, 270316f99cSCristian Marussi POWERCAP_MEASUREMENTS_GET = 0x9, 280316f99cSCristian Marussi POWERCAP_CAP_NOTIFY = 0xa, 290316f99cSCristian Marussi POWERCAP_MEASUREMENTS_NOTIFY = 0xb, 300316f99cSCristian Marussi POWERCAP_DESCRIBE_FASTCHANNEL = 0xc, 310316f99cSCristian Marussi }; 320316f99cSCristian Marussi 33855aa26eSCristian Marussi enum { 34855aa26eSCristian Marussi POWERCAP_FC_CAP, 35855aa26eSCristian Marussi POWERCAP_FC_PAI, 36855aa26eSCristian Marussi POWERCAP_FC_MAX, 37855aa26eSCristian Marussi }; 38855aa26eSCristian Marussi 390316f99cSCristian Marussi struct scmi_msg_resp_powercap_domain_attributes { 400316f99cSCristian Marussi __le32 attributes; 410316f99cSCristian Marussi #define SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(x) ((x) & BIT(31)) 420316f99cSCristian Marussi #define SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(x) ((x) & BIT(30)) 430316f99cSCristian Marussi #define SUPPORTS_ASYNC_POWERCAP_CAP_SET(x) ((x) & BIT(29)) 440316f99cSCristian Marussi #define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(28)) 450316f99cSCristian Marussi #define SUPPORTS_POWERCAP_CAP_CONFIGURATION(x) ((x) & BIT(27)) 460316f99cSCristian Marussi #define SUPPORTS_POWERCAP_MONITORING(x) ((x) & BIT(26)) 470316f99cSCristian Marussi #define SUPPORTS_POWERCAP_PAI_CONFIGURATION(x) ((x) & BIT(25)) 48855aa26eSCristian Marussi #define SUPPORTS_POWERCAP_FASTCHANNELS(x) ((x) & BIT(22)) 490316f99cSCristian Marussi #define POWERCAP_POWER_UNIT(x) \ 500316f99cSCristian Marussi (FIELD_GET(GENMASK(24, 23), (x))) 510316f99cSCristian Marussi #define SUPPORTS_POWER_UNITS_MW(x) \ 520316f99cSCristian Marussi (POWERCAP_POWER_UNIT(x) == 0x2) 530316f99cSCristian Marussi #define SUPPORTS_POWER_UNITS_UW(x) \ 540316f99cSCristian Marussi (POWERCAP_POWER_UNIT(x) == 0x1) 550316f99cSCristian Marussi u8 name[SCMI_SHORT_NAME_MAX_SIZE]; 560316f99cSCristian Marussi __le32 min_pai; 570316f99cSCristian Marussi __le32 max_pai; 580316f99cSCristian Marussi __le32 pai_step; 590316f99cSCristian Marussi __le32 min_power_cap; 600316f99cSCristian Marussi __le32 max_power_cap; 610316f99cSCristian Marussi __le32 power_cap_step; 620316f99cSCristian Marussi __le32 sustainable_power; 630316f99cSCristian Marussi __le32 accuracy; 640316f99cSCristian Marussi __le32 parent_id; 650316f99cSCristian Marussi }; 660316f99cSCristian Marussi 670316f99cSCristian Marussi struct scmi_msg_powercap_set_cap_or_pai { 680316f99cSCristian Marussi __le32 domain; 690316f99cSCristian Marussi __le32 flags; 700316f99cSCristian Marussi #define CAP_SET_ASYNC BIT(1) 710316f99cSCristian Marussi #define CAP_SET_IGNORE_DRESP BIT(0) 720316f99cSCristian Marussi __le32 value; 730316f99cSCristian Marussi }; 740316f99cSCristian Marussi 750316f99cSCristian Marussi struct scmi_msg_resp_powercap_cap_set_complete { 760316f99cSCristian Marussi __le32 domain; 770316f99cSCristian Marussi __le32 power_cap; 780316f99cSCristian Marussi }; 790316f99cSCristian Marussi 800316f99cSCristian Marussi struct scmi_msg_resp_powercap_meas_get { 810316f99cSCristian Marussi __le32 power; 820316f99cSCristian Marussi __le32 pai; 830316f99cSCristian Marussi }; 840316f99cSCristian Marussi 850316f99cSCristian Marussi struct scmi_msg_powercap_notify_cap { 860316f99cSCristian Marussi __le32 domain; 870316f99cSCristian Marussi __le32 notify_enable; 880316f99cSCristian Marussi }; 890316f99cSCristian Marussi 900316f99cSCristian Marussi struct scmi_msg_powercap_notify_thresh { 910316f99cSCristian Marussi __le32 domain; 920316f99cSCristian Marussi __le32 notify_enable; 930316f99cSCristian Marussi __le32 power_thresh_low; 940316f99cSCristian Marussi __le32 power_thresh_high; 950316f99cSCristian Marussi }; 960316f99cSCristian Marussi 970316f99cSCristian Marussi struct scmi_powercap_cap_changed_notify_payld { 980316f99cSCristian Marussi __le32 agent_id; 990316f99cSCristian Marussi __le32 domain_id; 1000316f99cSCristian Marussi __le32 power_cap; 1010316f99cSCristian Marussi __le32 pai; 1020316f99cSCristian Marussi }; 1030316f99cSCristian Marussi 1040316f99cSCristian Marussi struct scmi_powercap_meas_changed_notify_payld { 1050316f99cSCristian Marussi __le32 agent_id; 1060316f99cSCristian Marussi __le32 domain_id; 1070316f99cSCristian Marussi __le32 power; 1080316f99cSCristian Marussi }; 1090316f99cSCristian Marussi 1100316f99cSCristian Marussi struct scmi_powercap_state { 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 316*4e1a53b4SCristian Marussi static int __scmi_powercap_cap_get(const struct scmi_protocol_handle *ph, 317*4e1a53b4SCristian Marussi const struct scmi_powercap_info *dom, 318*4e1a53b4SCristian Marussi u32 *power_cap) 319855aa26eSCristian Marussi { 320855aa26eSCristian Marussi if (dom->fc_info && dom->fc_info[POWERCAP_FC_CAP].get_addr) { 321855aa26eSCristian Marussi *power_cap = ioread32(dom->fc_info[POWERCAP_FC_CAP].get_addr); 322b27d04d5SCristian Marussi trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_GET, 323*4e1a53b4SCristian Marussi dom->id, *power_cap, 0); 324855aa26eSCristian Marussi return 0; 325855aa26eSCristian Marussi } 326855aa26eSCristian Marussi 327*4e1a53b4SCristian Marussi return scmi_powercap_xfer_cap_get(ph, dom->id, power_cap); 328*4e1a53b4SCristian Marussi } 329*4e1a53b4SCristian Marussi 330*4e1a53b4SCristian Marussi static int scmi_powercap_cap_get(const struct scmi_protocol_handle *ph, 331*4e1a53b4SCristian Marussi u32 domain_id, u32 *power_cap) 332*4e1a53b4SCristian Marussi { 333*4e1a53b4SCristian Marussi const struct scmi_powercap_info *dom; 334*4e1a53b4SCristian Marussi 335*4e1a53b4SCristian Marussi if (!power_cap) 336*4e1a53b4SCristian Marussi return -EINVAL; 337*4e1a53b4SCristian Marussi 338*4e1a53b4SCristian Marussi dom = scmi_powercap_dom_info_get(ph, domain_id); 339*4e1a53b4SCristian Marussi if (!dom) 340*4e1a53b4SCristian Marussi return -EINVAL; 341*4e1a53b4SCristian Marussi 342*4e1a53b4SCristian Marussi return __scmi_powercap_cap_get(ph, dom, power_cap); 343855aa26eSCristian Marussi } 344855aa26eSCristian Marussi 345855aa26eSCristian Marussi static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph, 346855aa26eSCristian Marussi const struct scmi_powercap_info *pc, 347855aa26eSCristian Marussi u32 power_cap, bool ignore_dresp) 3480316f99cSCristian Marussi { 3490316f99cSCristian Marussi int ret; 3500316f99cSCristian Marussi struct scmi_xfer *t; 3510316f99cSCristian Marussi struct scmi_msg_powercap_set_cap_or_pai *msg; 3520316f99cSCristian Marussi 3530316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_SET, 3540316f99cSCristian Marussi sizeof(*msg), 0, &t); 3550316f99cSCristian Marussi if (ret) 3560316f99cSCristian Marussi return ret; 3570316f99cSCristian Marussi 3580316f99cSCristian Marussi msg = t->tx.buf; 359855aa26eSCristian Marussi msg->domain = cpu_to_le32(pc->id); 3600316f99cSCristian Marussi msg->flags = 3610316f99cSCristian Marussi cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, !!pc->async_powercap_cap_set) | 3620316f99cSCristian Marussi FIELD_PREP(CAP_SET_IGNORE_DRESP, !!ignore_dresp)); 3630316f99cSCristian Marussi msg->value = cpu_to_le32(power_cap); 3640316f99cSCristian Marussi 3650316f99cSCristian Marussi if (!pc->async_powercap_cap_set || ignore_dresp) { 3660316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 3670316f99cSCristian Marussi } else { 3680316f99cSCristian Marussi ret = ph->xops->do_xfer_with_response(ph, t); 3690316f99cSCristian Marussi if (!ret) { 3700316f99cSCristian Marussi struct scmi_msg_resp_powercap_cap_set_complete *resp; 3710316f99cSCristian Marussi 3720316f99cSCristian Marussi resp = t->rx.buf; 373855aa26eSCristian Marussi if (le32_to_cpu(resp->domain) == pc->id) 3740316f99cSCristian Marussi dev_dbg(ph->dev, 3750316f99cSCristian Marussi "Powercap ID %d CAP set async to %u\n", 376855aa26eSCristian Marussi pc->id, 3770316f99cSCristian Marussi get_unaligned_le32(&resp->power_cap)); 3780316f99cSCristian Marussi else 3790316f99cSCristian Marussi ret = -EPROTO; 3800316f99cSCristian Marussi } 3810316f99cSCristian Marussi } 3820316f99cSCristian Marussi 3830316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 3840316f99cSCristian Marussi return ret; 3850316f99cSCristian Marussi } 3860316f99cSCristian Marussi 387*4e1a53b4SCristian Marussi static int __scmi_powercap_cap_set(const struct scmi_protocol_handle *ph, 388*4e1a53b4SCristian Marussi struct powercap_info *pi, u32 domain_id, 389*4e1a53b4SCristian Marussi u32 power_cap, bool ignore_dresp) 390855aa26eSCristian Marussi { 391*4e1a53b4SCristian Marussi int ret = -EINVAL; 392855aa26eSCristian Marussi const struct scmi_powercap_info *pc; 393855aa26eSCristian Marussi 394855aa26eSCristian Marussi pc = scmi_powercap_dom_info_get(ph, domain_id); 395*4e1a53b4SCristian Marussi if (!pc || !pc->powercap_cap_config) 396*4e1a53b4SCristian Marussi return ret; 397*4e1a53b4SCristian Marussi 398*4e1a53b4SCristian Marussi if (power_cap && 399*4e1a53b4SCristian Marussi (power_cap < pc->min_power_cap || power_cap > pc->max_power_cap)) 400*4e1a53b4SCristian Marussi return ret; 401855aa26eSCristian Marussi 402855aa26eSCristian Marussi if (pc->fc_info && pc->fc_info[POWERCAP_FC_CAP].set_addr) { 403855aa26eSCristian Marussi struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_CAP]; 404855aa26eSCristian Marussi 405855aa26eSCristian Marussi iowrite32(power_cap, fci->set_addr); 406855aa26eSCristian Marussi ph->hops->fastchannel_db_ring(fci->set_db); 407b27d04d5SCristian Marussi trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_SET, 408b27d04d5SCristian Marussi domain_id, power_cap, 0); 409*4e1a53b4SCristian Marussi ret = 0; 410*4e1a53b4SCristian Marussi } else { 411*4e1a53b4SCristian Marussi ret = scmi_powercap_xfer_cap_set(ph, pc, power_cap, 412*4e1a53b4SCristian Marussi ignore_dresp); 413855aa26eSCristian Marussi } 414855aa26eSCristian Marussi 415*4e1a53b4SCristian Marussi return ret; 416*4e1a53b4SCristian Marussi } 417*4e1a53b4SCristian Marussi 418*4e1a53b4SCristian Marussi static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph, 419*4e1a53b4SCristian Marussi u32 domain_id, u32 power_cap, 420*4e1a53b4SCristian Marussi bool ignore_dresp) 421*4e1a53b4SCristian Marussi { 422*4e1a53b4SCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 423*4e1a53b4SCristian Marussi 424*4e1a53b4SCristian Marussi return __scmi_powercap_cap_set(ph, pi, domain_id, 425*4e1a53b4SCristian Marussi power_cap, ignore_dresp); 426855aa26eSCristian Marussi } 427855aa26eSCristian Marussi 428855aa26eSCristian Marussi static int scmi_powercap_xfer_pai_get(const struct scmi_protocol_handle *ph, 4290316f99cSCristian Marussi u32 domain_id, u32 *pai) 4300316f99cSCristian Marussi { 4310316f99cSCristian Marussi int ret; 4320316f99cSCristian Marussi struct scmi_xfer *t; 4330316f99cSCristian Marussi 4340316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_GET, sizeof(u32), 4350316f99cSCristian Marussi sizeof(u32), &t); 4360316f99cSCristian Marussi if (ret) 4370316f99cSCristian Marussi return ret; 4380316f99cSCristian Marussi 4390316f99cSCristian Marussi put_unaligned_le32(domain_id, t->tx.buf); 4400316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 4410316f99cSCristian Marussi if (!ret) 4420316f99cSCristian Marussi *pai = get_unaligned_le32(t->rx.buf); 4430316f99cSCristian Marussi 4440316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 4450316f99cSCristian Marussi 4460316f99cSCristian Marussi return ret; 4470316f99cSCristian Marussi } 4480316f99cSCristian Marussi 449855aa26eSCristian Marussi static int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph, 450855aa26eSCristian Marussi u32 domain_id, u32 *pai) 451855aa26eSCristian Marussi { 452855aa26eSCristian Marussi struct scmi_powercap_info *dom; 453855aa26eSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 454855aa26eSCristian Marussi 455855aa26eSCristian Marussi if (!pai || domain_id >= pi->num_domains) 456855aa26eSCristian Marussi return -EINVAL; 457855aa26eSCristian Marussi 458855aa26eSCristian Marussi dom = pi->powercaps + domain_id; 459855aa26eSCristian Marussi if (dom->fc_info && dom->fc_info[POWERCAP_FC_PAI].get_addr) { 460855aa26eSCristian Marussi *pai = ioread32(dom->fc_info[POWERCAP_FC_PAI].get_addr); 461b27d04d5SCristian Marussi trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_GET, 462b27d04d5SCristian Marussi domain_id, *pai, 0); 463855aa26eSCristian Marussi return 0; 464855aa26eSCristian Marussi } 465855aa26eSCristian Marussi 466855aa26eSCristian Marussi return scmi_powercap_xfer_pai_get(ph, domain_id, pai); 467855aa26eSCristian Marussi } 468855aa26eSCristian Marussi 469855aa26eSCristian Marussi static int scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle *ph, 4700316f99cSCristian Marussi u32 domain_id, u32 pai) 4710316f99cSCristian Marussi { 4720316f99cSCristian Marussi int ret; 4730316f99cSCristian Marussi struct scmi_xfer *t; 4740316f99cSCristian Marussi struct scmi_msg_powercap_set_cap_or_pai *msg; 4750316f99cSCristian Marussi 4760316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_SET, 4770316f99cSCristian Marussi sizeof(*msg), 0, &t); 4780316f99cSCristian Marussi if (ret) 4790316f99cSCristian Marussi return ret; 4800316f99cSCristian Marussi 4810316f99cSCristian Marussi msg = t->tx.buf; 4820316f99cSCristian Marussi msg->domain = cpu_to_le32(domain_id); 4830316f99cSCristian Marussi msg->flags = cpu_to_le32(0); 4840316f99cSCristian Marussi msg->value = cpu_to_le32(pai); 4850316f99cSCristian Marussi 4860316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 4870316f99cSCristian Marussi 4880316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 4890316f99cSCristian Marussi return ret; 4900316f99cSCristian Marussi } 4910316f99cSCristian Marussi 492855aa26eSCristian Marussi static int scmi_powercap_pai_set(const struct scmi_protocol_handle *ph, 493855aa26eSCristian Marussi u32 domain_id, u32 pai) 494855aa26eSCristian Marussi { 495855aa26eSCristian Marussi const struct scmi_powercap_info *pc; 496855aa26eSCristian Marussi 497855aa26eSCristian Marussi pc = scmi_powercap_dom_info_get(ph, domain_id); 498855aa26eSCristian Marussi if (!pc || !pc->powercap_pai_config || !pai || 499855aa26eSCristian Marussi pai < pc->min_pai || pai > pc->max_pai) 500855aa26eSCristian Marussi return -EINVAL; 501855aa26eSCristian Marussi 502855aa26eSCristian Marussi if (pc->fc_info && pc->fc_info[POWERCAP_FC_PAI].set_addr) { 503855aa26eSCristian Marussi struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_PAI]; 504855aa26eSCristian Marussi 505b27d04d5SCristian Marussi trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_SET, 506b27d04d5SCristian Marussi domain_id, pai, 0); 507855aa26eSCristian Marussi iowrite32(pai, fci->set_addr); 508855aa26eSCristian Marussi ph->hops->fastchannel_db_ring(fci->set_db); 509855aa26eSCristian Marussi return 0; 510855aa26eSCristian Marussi } 511855aa26eSCristian Marussi 512855aa26eSCristian Marussi return scmi_powercap_xfer_pai_set(ph, domain_id, pai); 513855aa26eSCristian Marussi } 514855aa26eSCristian Marussi 5150316f99cSCristian Marussi static int scmi_powercap_measurements_get(const struct scmi_protocol_handle *ph, 5160316f99cSCristian Marussi u32 domain_id, u32 *average_power, 5170316f99cSCristian Marussi u32 *pai) 5180316f99cSCristian Marussi { 5190316f99cSCristian Marussi int ret; 5200316f99cSCristian Marussi struct scmi_xfer *t; 5210316f99cSCristian Marussi struct scmi_msg_resp_powercap_meas_get *resp; 5220316f99cSCristian Marussi const struct scmi_powercap_info *pc; 5230316f99cSCristian Marussi 5240316f99cSCristian Marussi pc = scmi_powercap_dom_info_get(ph, domain_id); 5250316f99cSCristian Marussi if (!pc || !pc->powercap_monitoring || !pai || !average_power) 5260316f99cSCristian Marussi return -EINVAL; 5270316f99cSCristian Marussi 5280316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, POWERCAP_MEASUREMENTS_GET, 5290316f99cSCristian Marussi sizeof(u32), sizeof(*resp), &t); 5300316f99cSCristian Marussi if (ret) 5310316f99cSCristian Marussi return ret; 5320316f99cSCristian Marussi 5330316f99cSCristian Marussi resp = t->rx.buf; 5340316f99cSCristian Marussi put_unaligned_le32(domain_id, t->tx.buf); 5350316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 5360316f99cSCristian Marussi if (!ret) { 5370316f99cSCristian Marussi *average_power = le32_to_cpu(resp->power); 5380316f99cSCristian Marussi *pai = le32_to_cpu(resp->pai); 5390316f99cSCristian Marussi } 5400316f99cSCristian Marussi 5410316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 5420316f99cSCristian Marussi return ret; 5430316f99cSCristian Marussi } 5440316f99cSCristian Marussi 5450316f99cSCristian Marussi static int 5460316f99cSCristian Marussi scmi_powercap_measurements_threshold_get(const struct scmi_protocol_handle *ph, 5470316f99cSCristian Marussi u32 domain_id, u32 *power_thresh_low, 5480316f99cSCristian Marussi u32 *power_thresh_high) 5490316f99cSCristian Marussi { 5500316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 5510316f99cSCristian Marussi 5520316f99cSCristian Marussi if (!power_thresh_low || !power_thresh_high || 5530316f99cSCristian Marussi domain_id >= pi->num_domains) 5540316f99cSCristian Marussi return -EINVAL; 5550316f99cSCristian Marussi 5560316f99cSCristian Marussi *power_thresh_low = THRESH_LOW(pi, domain_id); 5570316f99cSCristian Marussi *power_thresh_high = THRESH_HIGH(pi, domain_id); 5580316f99cSCristian Marussi 5590316f99cSCristian Marussi return 0; 5600316f99cSCristian Marussi } 5610316f99cSCristian Marussi 5620316f99cSCristian Marussi static int 5630316f99cSCristian Marussi scmi_powercap_measurements_threshold_set(const struct scmi_protocol_handle *ph, 5640316f99cSCristian Marussi u32 domain_id, u32 power_thresh_low, 5650316f99cSCristian Marussi u32 power_thresh_high) 5660316f99cSCristian Marussi { 5670316f99cSCristian Marussi int ret = 0; 5680316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 5690316f99cSCristian Marussi 5700316f99cSCristian Marussi if (domain_id >= pi->num_domains || 5710316f99cSCristian Marussi power_thresh_low > power_thresh_high) 5720316f99cSCristian Marussi return -EINVAL; 5730316f99cSCristian Marussi 5740316f99cSCristian Marussi /* Anything to do ? */ 5750316f99cSCristian Marussi if (THRESH_LOW(pi, domain_id) == power_thresh_low && 5760316f99cSCristian Marussi THRESH_HIGH(pi, domain_id) == power_thresh_high) 5770316f99cSCristian Marussi return ret; 5780316f99cSCristian Marussi 5790316f99cSCristian Marussi pi->states[domain_id].thresholds = 5800316f99cSCristian Marussi (FIELD_PREP(GENMASK_ULL(31, 0), power_thresh_low) | 5810316f99cSCristian Marussi FIELD_PREP(GENMASK_ULL(63, 32), power_thresh_high)); 5820316f99cSCristian Marussi 5830316f99cSCristian Marussi /* Update thresholds if notification already enabled */ 5840316f99cSCristian Marussi if (pi->states[domain_id].meas_notif_enabled) 5850316f99cSCristian Marussi ret = scmi_powercap_notify(ph, domain_id, 5860316f99cSCristian Marussi POWERCAP_MEASUREMENTS_NOTIFY, 5870316f99cSCristian Marussi true); 5880316f99cSCristian Marussi 5890316f99cSCristian Marussi return ret; 5900316f99cSCristian Marussi } 5910316f99cSCristian Marussi 5920316f99cSCristian Marussi static const struct scmi_powercap_proto_ops powercap_proto_ops = { 5930316f99cSCristian Marussi .num_domains_get = scmi_powercap_num_domains_get, 5940316f99cSCristian Marussi .info_get = scmi_powercap_dom_info_get, 5950316f99cSCristian Marussi .cap_get = scmi_powercap_cap_get, 5960316f99cSCristian Marussi .cap_set = scmi_powercap_cap_set, 5970316f99cSCristian Marussi .pai_get = scmi_powercap_pai_get, 5980316f99cSCristian Marussi .pai_set = scmi_powercap_pai_set, 5990316f99cSCristian Marussi .measurements_get = scmi_powercap_measurements_get, 6000316f99cSCristian Marussi .measurements_threshold_set = scmi_powercap_measurements_threshold_set, 6010316f99cSCristian Marussi .measurements_threshold_get = scmi_powercap_measurements_threshold_get, 6020316f99cSCristian Marussi }; 6030316f99cSCristian Marussi 604855aa26eSCristian Marussi static void scmi_powercap_domain_init_fc(const struct scmi_protocol_handle *ph, 605855aa26eSCristian Marussi u32 domain, struct scmi_fc_info **p_fc) 606855aa26eSCristian Marussi { 607855aa26eSCristian Marussi struct scmi_fc_info *fc; 608855aa26eSCristian Marussi 609855aa26eSCristian Marussi fc = devm_kcalloc(ph->dev, POWERCAP_FC_MAX, sizeof(*fc), GFP_KERNEL); 610855aa26eSCristian Marussi if (!fc) 611855aa26eSCristian Marussi return; 612855aa26eSCristian Marussi 613855aa26eSCristian Marussi ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, 614855aa26eSCristian Marussi POWERCAP_CAP_SET, 4, domain, 615855aa26eSCristian Marussi &fc[POWERCAP_FC_CAP].set_addr, 616855aa26eSCristian Marussi &fc[POWERCAP_FC_CAP].set_db); 617855aa26eSCristian Marussi 618855aa26eSCristian Marussi ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, 619855aa26eSCristian Marussi POWERCAP_CAP_GET, 4, domain, 620855aa26eSCristian Marussi &fc[POWERCAP_FC_CAP].get_addr, NULL); 621855aa26eSCristian Marussi 622855aa26eSCristian Marussi ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, 623855aa26eSCristian Marussi POWERCAP_PAI_SET, 4, domain, 624855aa26eSCristian Marussi &fc[POWERCAP_FC_PAI].set_addr, 625855aa26eSCristian Marussi &fc[POWERCAP_FC_PAI].set_db); 626855aa26eSCristian Marussi 627855aa26eSCristian Marussi ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL, 628855aa26eSCristian Marussi POWERCAP_PAI_GET, 4, domain, 629855aa26eSCristian Marussi &fc[POWERCAP_FC_PAI].get_addr, NULL); 630855aa26eSCristian Marussi 631855aa26eSCristian Marussi *p_fc = fc; 632855aa26eSCristian Marussi } 633855aa26eSCristian Marussi 6340316f99cSCristian Marussi static int scmi_powercap_notify(const struct scmi_protocol_handle *ph, 6350316f99cSCristian Marussi u32 domain, int message_id, bool enable) 6360316f99cSCristian Marussi { 6370316f99cSCristian Marussi int ret; 6380316f99cSCristian Marussi struct scmi_xfer *t; 6390316f99cSCristian Marussi 6400316f99cSCristian Marussi switch (message_id) { 6410316f99cSCristian Marussi case POWERCAP_CAP_NOTIFY: 6420316f99cSCristian Marussi { 6430316f99cSCristian Marussi struct scmi_msg_powercap_notify_cap *notify; 6440316f99cSCristian Marussi 6450316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, message_id, 6460316f99cSCristian Marussi sizeof(*notify), 0, &t); 6470316f99cSCristian Marussi if (ret) 6480316f99cSCristian Marussi return ret; 6490316f99cSCristian Marussi 6500316f99cSCristian Marussi notify = t->tx.buf; 6510316f99cSCristian Marussi notify->domain = cpu_to_le32(domain); 6520316f99cSCristian Marussi notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0); 6530316f99cSCristian Marussi break; 6540316f99cSCristian Marussi } 6550316f99cSCristian Marussi case POWERCAP_MEASUREMENTS_NOTIFY: 6560316f99cSCristian Marussi { 6570316f99cSCristian Marussi u32 low, high; 6580316f99cSCristian Marussi struct scmi_msg_powercap_notify_thresh *notify; 6590316f99cSCristian Marussi 6600316f99cSCristian Marussi /* 6610316f99cSCristian Marussi * Note that we have to pick the most recently configured 6620316f99cSCristian Marussi * thresholds to build a proper POWERCAP_MEASUREMENTS_NOTIFY 6630316f99cSCristian Marussi * enable request and we fail, complaining, if no thresholds 6640316f99cSCristian Marussi * were ever set, since this is an indication the API has been 6650316f99cSCristian Marussi * used wrongly. 6660316f99cSCristian Marussi */ 6670316f99cSCristian Marussi ret = scmi_powercap_measurements_threshold_get(ph, domain, 6680316f99cSCristian Marussi &low, &high); 6690316f99cSCristian Marussi if (ret) 6700316f99cSCristian Marussi return ret; 6710316f99cSCristian Marussi 6720316f99cSCristian Marussi if (enable && !low && !high) { 6730316f99cSCristian Marussi dev_err(ph->dev, 6740316f99cSCristian Marussi "Invalid Measurements Notify thresholds: %u/%u\n", 6750316f99cSCristian Marussi low, high); 6760316f99cSCristian Marussi return -EINVAL; 6770316f99cSCristian Marussi } 6780316f99cSCristian Marussi 6790316f99cSCristian Marussi ret = ph->xops->xfer_get_init(ph, message_id, 6800316f99cSCristian Marussi sizeof(*notify), 0, &t); 6810316f99cSCristian Marussi if (ret) 6820316f99cSCristian Marussi return ret; 6830316f99cSCristian Marussi 6840316f99cSCristian Marussi notify = t->tx.buf; 6850316f99cSCristian Marussi notify->domain = cpu_to_le32(domain); 6860316f99cSCristian Marussi notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0); 6870316f99cSCristian Marussi notify->power_thresh_low = cpu_to_le32(low); 6880316f99cSCristian Marussi notify->power_thresh_high = cpu_to_le32(high); 6890316f99cSCristian Marussi break; 6900316f99cSCristian Marussi } 6910316f99cSCristian Marussi default: 6920316f99cSCristian Marussi return -EINVAL; 6930316f99cSCristian Marussi } 6940316f99cSCristian Marussi 6950316f99cSCristian Marussi ret = ph->xops->do_xfer(ph, t); 6960316f99cSCristian Marussi 6970316f99cSCristian Marussi ph->xops->xfer_put(ph, t); 6980316f99cSCristian Marussi return ret; 6990316f99cSCristian Marussi } 7000316f99cSCristian Marussi 7010316f99cSCristian Marussi static int 7020316f99cSCristian Marussi scmi_powercap_set_notify_enabled(const struct scmi_protocol_handle *ph, 7030316f99cSCristian Marussi u8 evt_id, u32 src_id, bool enable) 7040316f99cSCristian Marussi { 7050316f99cSCristian Marussi int ret, cmd_id; 7060316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 7070316f99cSCristian Marussi 7080316f99cSCristian Marussi if (evt_id >= ARRAY_SIZE(evt_2_cmd) || src_id >= pi->num_domains) 7090316f99cSCristian Marussi return -EINVAL; 7100316f99cSCristian Marussi 7110316f99cSCristian Marussi cmd_id = evt_2_cmd[evt_id]; 7120316f99cSCristian Marussi ret = scmi_powercap_notify(ph, src_id, cmd_id, enable); 7130316f99cSCristian Marussi if (ret) 7140316f99cSCristian Marussi pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n", 7150316f99cSCristian Marussi evt_id, src_id, ret); 7160316f99cSCristian Marussi else if (cmd_id == POWERCAP_MEASUREMENTS_NOTIFY) 7170316f99cSCristian Marussi /* 7180316f99cSCristian Marussi * On success save the current notification enabled state, so 7190316f99cSCristian Marussi * as to be able to properly update the notification thresholds 7200316f99cSCristian Marussi * when they are modified on a domain for which measurement 7210316f99cSCristian Marussi * notifications were currently enabled. 7220316f99cSCristian Marussi * 7230316f99cSCristian Marussi * This is needed because the SCMI Notification core machinery 7240316f99cSCristian Marussi * and API does not support passing per-notification custom 7250316f99cSCristian Marussi * arguments at callback registration time. 7260316f99cSCristian Marussi * 7270316f99cSCristian Marussi * Note that this can be done here with a simple flag since the 7280316f99cSCristian Marussi * SCMI core Notifications code takes care of keeping proper 7290316f99cSCristian Marussi * per-domain enables refcounting, so that this helper function 7300316f99cSCristian Marussi * will be called only once (for enables) when the first user 7310316f99cSCristian Marussi * registers a callback on this domain and once more (disable) 7320316f99cSCristian Marussi * when the last user de-registers its callback. 7330316f99cSCristian Marussi */ 7340316f99cSCristian Marussi pi->states[src_id].meas_notif_enabled = enable; 7350316f99cSCristian Marussi 7360316f99cSCristian Marussi return ret; 7370316f99cSCristian Marussi } 7380316f99cSCristian Marussi 7390316f99cSCristian Marussi static void * 7400316f99cSCristian Marussi scmi_powercap_fill_custom_report(const struct scmi_protocol_handle *ph, 7410316f99cSCristian Marussi u8 evt_id, ktime_t timestamp, 7420316f99cSCristian Marussi const void *payld, size_t payld_sz, 7430316f99cSCristian Marussi void *report, u32 *src_id) 7440316f99cSCristian Marussi { 7450316f99cSCristian Marussi void *rep = NULL; 7460316f99cSCristian Marussi 7470316f99cSCristian Marussi switch (evt_id) { 7480316f99cSCristian Marussi case SCMI_EVENT_POWERCAP_CAP_CHANGED: 7490316f99cSCristian Marussi { 7500316f99cSCristian Marussi const struct scmi_powercap_cap_changed_notify_payld *p = payld; 7510316f99cSCristian Marussi struct scmi_powercap_cap_changed_report *r = report; 7520316f99cSCristian Marussi 7530316f99cSCristian Marussi if (sizeof(*p) != payld_sz) 7540316f99cSCristian Marussi break; 7550316f99cSCristian Marussi 7560316f99cSCristian Marussi r->timestamp = timestamp; 7570316f99cSCristian Marussi r->agent_id = le32_to_cpu(p->agent_id); 7580316f99cSCristian Marussi r->domain_id = le32_to_cpu(p->domain_id); 7590316f99cSCristian Marussi r->power_cap = le32_to_cpu(p->power_cap); 7600316f99cSCristian Marussi r->pai = le32_to_cpu(p->pai); 7610316f99cSCristian Marussi *src_id = r->domain_id; 7620316f99cSCristian Marussi rep = r; 7630316f99cSCristian Marussi break; 7640316f99cSCristian Marussi } 7650316f99cSCristian Marussi case SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED: 7660316f99cSCristian Marussi { 7670316f99cSCristian Marussi const struct scmi_powercap_meas_changed_notify_payld *p = payld; 7680316f99cSCristian Marussi struct scmi_powercap_meas_changed_report *r = report; 7690316f99cSCristian Marussi 7700316f99cSCristian Marussi if (sizeof(*p) != payld_sz) 7710316f99cSCristian Marussi break; 7720316f99cSCristian Marussi 7730316f99cSCristian Marussi r->timestamp = timestamp; 7740316f99cSCristian Marussi r->agent_id = le32_to_cpu(p->agent_id); 7750316f99cSCristian Marussi r->domain_id = le32_to_cpu(p->domain_id); 7760316f99cSCristian Marussi r->power = le32_to_cpu(p->power); 7770316f99cSCristian Marussi *src_id = r->domain_id; 7780316f99cSCristian Marussi rep = r; 7790316f99cSCristian Marussi break; 7800316f99cSCristian Marussi } 7810316f99cSCristian Marussi default: 7820316f99cSCristian Marussi break; 7830316f99cSCristian Marussi } 7840316f99cSCristian Marussi 7850316f99cSCristian Marussi return rep; 7860316f99cSCristian Marussi } 7870316f99cSCristian Marussi 7880316f99cSCristian Marussi static int 7890316f99cSCristian Marussi scmi_powercap_get_num_sources(const struct scmi_protocol_handle *ph) 7900316f99cSCristian Marussi { 7910316f99cSCristian Marussi struct powercap_info *pi = ph->get_priv(ph); 7920316f99cSCristian Marussi 7930316f99cSCristian Marussi if (!pi) 7940316f99cSCristian Marussi return -EINVAL; 7950316f99cSCristian Marussi 7960316f99cSCristian Marussi return pi->num_domains; 7970316f99cSCristian Marussi } 7980316f99cSCristian Marussi 7990316f99cSCristian Marussi static const struct scmi_event powercap_events[] = { 8000316f99cSCristian Marussi { 8010316f99cSCristian Marussi .id = SCMI_EVENT_POWERCAP_CAP_CHANGED, 8020316f99cSCristian Marussi .max_payld_sz = 8030316f99cSCristian Marussi sizeof(struct scmi_powercap_cap_changed_notify_payld), 8040316f99cSCristian Marussi .max_report_sz = 8050316f99cSCristian Marussi sizeof(struct scmi_powercap_cap_changed_report), 8060316f99cSCristian Marussi }, 8070316f99cSCristian Marussi { 8080316f99cSCristian Marussi .id = SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED, 8090316f99cSCristian Marussi .max_payld_sz = 8100316f99cSCristian Marussi sizeof(struct scmi_powercap_meas_changed_notify_payld), 8110316f99cSCristian Marussi .max_report_sz = 8120316f99cSCristian Marussi sizeof(struct scmi_powercap_meas_changed_report), 8130316f99cSCristian Marussi }, 8140316f99cSCristian Marussi }; 8150316f99cSCristian Marussi 8160316f99cSCristian Marussi static const struct scmi_event_ops powercap_event_ops = { 8170316f99cSCristian Marussi .get_num_sources = scmi_powercap_get_num_sources, 8180316f99cSCristian Marussi .set_notify_enabled = scmi_powercap_set_notify_enabled, 8190316f99cSCristian Marussi .fill_custom_report = scmi_powercap_fill_custom_report, 8200316f99cSCristian Marussi }; 8210316f99cSCristian Marussi 8220316f99cSCristian Marussi static const struct scmi_protocol_events powercap_protocol_events = { 8230316f99cSCristian Marussi .queue_sz = SCMI_PROTO_QUEUE_SZ, 8240316f99cSCristian Marussi .ops = &powercap_event_ops, 8250316f99cSCristian Marussi .evts = powercap_events, 8260316f99cSCristian Marussi .num_events = ARRAY_SIZE(powercap_events), 8270316f99cSCristian Marussi }; 8280316f99cSCristian Marussi 8290316f99cSCristian Marussi static int 8300316f99cSCristian Marussi scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph) 8310316f99cSCristian Marussi { 8320316f99cSCristian Marussi int domain, ret; 8330316f99cSCristian Marussi u32 version; 8340316f99cSCristian Marussi struct powercap_info *pinfo; 8350316f99cSCristian Marussi 8360316f99cSCristian Marussi ret = ph->xops->version_get(ph, &version); 8370316f99cSCristian Marussi if (ret) 8380316f99cSCristian Marussi return ret; 8390316f99cSCristian Marussi 8400316f99cSCristian Marussi dev_dbg(ph->dev, "Powercap Version %d.%d\n", 8410316f99cSCristian Marussi PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); 8420316f99cSCristian Marussi 8430316f99cSCristian Marussi pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL); 8440316f99cSCristian Marussi if (!pinfo) 8450316f99cSCristian Marussi return -ENOMEM; 8460316f99cSCristian Marussi 8470316f99cSCristian Marussi ret = scmi_powercap_attributes_get(ph, pinfo); 8480316f99cSCristian Marussi if (ret) 8490316f99cSCristian Marussi return ret; 8500316f99cSCristian Marussi 8510316f99cSCristian Marussi pinfo->powercaps = devm_kcalloc(ph->dev, pinfo->num_domains, 8520316f99cSCristian Marussi sizeof(*pinfo->powercaps), 8530316f99cSCristian Marussi GFP_KERNEL); 8540316f99cSCristian Marussi if (!pinfo->powercaps) 8550316f99cSCristian Marussi return -ENOMEM; 8560316f99cSCristian Marussi 8570316f99cSCristian Marussi /* 8580316f99cSCristian Marussi * Note that any failure in retrieving any domain attribute leads to 8590316f99cSCristian Marussi * the whole Powercap protocol initialization failure: this way the 8600316f99cSCristian Marussi * reported Powercap domains are all assured, when accessed, to be well 8610316f99cSCristian Marussi * formed and correlated by sane parent-child relationship (if any). 8620316f99cSCristian Marussi */ 8630316f99cSCristian Marussi for (domain = 0; domain < pinfo->num_domains; domain++) { 8640316f99cSCristian Marussi ret = scmi_powercap_domain_attributes_get(ph, pinfo, domain); 8650316f99cSCristian Marussi if (ret) 8660316f99cSCristian Marussi return ret; 867855aa26eSCristian Marussi 868855aa26eSCristian Marussi if (pinfo->powercaps[domain].fastchannels) 869855aa26eSCristian Marussi scmi_powercap_domain_init_fc(ph, domain, 870855aa26eSCristian Marussi &pinfo->powercaps[domain].fc_info); 8710316f99cSCristian Marussi } 8720316f99cSCristian Marussi 8730316f99cSCristian Marussi pinfo->states = devm_kcalloc(ph->dev, pinfo->num_domains, 8740316f99cSCristian Marussi sizeof(*pinfo->states), GFP_KERNEL); 8750316f99cSCristian Marussi if (!pinfo->states) 8760316f99cSCristian Marussi return -ENOMEM; 8770316f99cSCristian Marussi 8780316f99cSCristian Marussi pinfo->version = version; 8790316f99cSCristian Marussi 8800316f99cSCristian Marussi return ph->set_priv(ph, pinfo); 8810316f99cSCristian Marussi } 8820316f99cSCristian Marussi 8830316f99cSCristian Marussi static const struct scmi_protocol scmi_powercap = { 8840316f99cSCristian Marussi .id = SCMI_PROTOCOL_POWERCAP, 8850316f99cSCristian Marussi .owner = THIS_MODULE, 8860316f99cSCristian Marussi .instance_init = &scmi_powercap_protocol_init, 8870316f99cSCristian Marussi .ops = &powercap_proto_ops, 8880316f99cSCristian Marussi .events = &powercap_protocol_events, 8890316f99cSCristian Marussi }; 8900316f99cSCristian Marussi 8910316f99cSCristian Marussi DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(powercap, scmi_powercap) 892