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