18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Common code for Intel Running Average Power Limit (RAPL) support. 48c2ecf20Sopenharmony_ci * Copyright (c) 2019, Intel Corporation. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/list.h> 118c2ecf20Sopenharmony_ci#include <linux/types.h> 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/log2.h> 158c2ecf20Sopenharmony_ci#include <linux/bitmap.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 188c2ecf20Sopenharmony_ci#include <linux/cpu.h> 198c2ecf20Sopenharmony_ci#include <linux/powercap.h> 208c2ecf20Sopenharmony_ci#include <linux/suspend.h> 218c2ecf20Sopenharmony_ci#include <linux/intel_rapl.h> 228c2ecf20Sopenharmony_ci#include <linux/processor.h> 238c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <asm/iosf_mbi.h> 268c2ecf20Sopenharmony_ci#include <asm/cpu_device_id.h> 278c2ecf20Sopenharmony_ci#include <asm/intel-family.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* bitmasks for RAPL MSRs, used by primitive access functions */ 308c2ecf20Sopenharmony_ci#define ENERGY_STATUS_MASK 0xffffffff 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define POWER_LIMIT1_MASK 0x7FFF 338c2ecf20Sopenharmony_ci#define POWER_LIMIT1_ENABLE BIT(15) 348c2ecf20Sopenharmony_ci#define POWER_LIMIT1_CLAMP BIT(16) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define POWER_LIMIT2_MASK (0x7FFFULL<<32) 378c2ecf20Sopenharmony_ci#define POWER_LIMIT2_ENABLE BIT_ULL(47) 388c2ecf20Sopenharmony_ci#define POWER_LIMIT2_CLAMP BIT_ULL(48) 398c2ecf20Sopenharmony_ci#define POWER_HIGH_LOCK BIT_ULL(63) 408c2ecf20Sopenharmony_ci#define POWER_LOW_LOCK BIT(31) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define POWER_LIMIT4_MASK 0x1FFF 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define TIME_WINDOW1_MASK (0x7FULL<<17) 458c2ecf20Sopenharmony_ci#define TIME_WINDOW2_MASK (0x7FULL<<49) 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define POWER_UNIT_OFFSET 0 488c2ecf20Sopenharmony_ci#define POWER_UNIT_MASK 0x0F 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define ENERGY_UNIT_OFFSET 0x08 518c2ecf20Sopenharmony_ci#define ENERGY_UNIT_MASK 0x1F00 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define TIME_UNIT_OFFSET 0x10 548c2ecf20Sopenharmony_ci#define TIME_UNIT_MASK 0xF0000 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define POWER_INFO_MAX_MASK (0x7fffULL<<32) 578c2ecf20Sopenharmony_ci#define POWER_INFO_MIN_MASK (0x7fffULL<<16) 588c2ecf20Sopenharmony_ci#define POWER_INFO_MAX_TIME_WIN_MASK (0x3fULL<<48) 598c2ecf20Sopenharmony_ci#define POWER_INFO_THERMAL_SPEC_MASK 0x7fff 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define PERF_STATUS_THROTTLE_TIME_MASK 0xffffffff 628c2ecf20Sopenharmony_ci#define PP_POLICY_MASK 0x1F 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* Non HW constants */ 658c2ecf20Sopenharmony_ci#define RAPL_PRIMITIVE_DERIVED BIT(1) /* not from raw data */ 668c2ecf20Sopenharmony_ci#define RAPL_PRIMITIVE_DUMMY BIT(2) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define TIME_WINDOW_MAX_MSEC 40000 698c2ecf20Sopenharmony_ci#define TIME_WINDOW_MIN_MSEC 250 708c2ecf20Sopenharmony_ci#define ENERGY_UNIT_SCALE 1000 /* scale from driver unit to powercap unit */ 718c2ecf20Sopenharmony_cienum unit_type { 728c2ecf20Sopenharmony_ci ARBITRARY_UNIT, /* no translation */ 738c2ecf20Sopenharmony_ci POWER_UNIT, 748c2ecf20Sopenharmony_ci ENERGY_UNIT, 758c2ecf20Sopenharmony_ci TIME_UNIT, 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* per domain data, some are optional */ 798c2ecf20Sopenharmony_ci#define NR_RAW_PRIMITIVES (NR_RAPL_PRIMITIVES - 2) 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#define DOMAIN_STATE_INACTIVE BIT(0) 828c2ecf20Sopenharmony_ci#define DOMAIN_STATE_POWER_LIMIT_SET BIT(1) 838c2ecf20Sopenharmony_ci#define DOMAIN_STATE_BIOS_LOCKED BIT(2) 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic const char pl1_name[] = "long_term"; 868c2ecf20Sopenharmony_cistatic const char pl2_name[] = "short_term"; 878c2ecf20Sopenharmony_cistatic const char pl4_name[] = "peak_power"; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#define power_zone_to_rapl_domain(_zone) \ 908c2ecf20Sopenharmony_ci container_of(_zone, struct rapl_domain, power_zone) 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistruct rapl_defaults { 938c2ecf20Sopenharmony_ci u8 floor_freq_reg_addr; 948c2ecf20Sopenharmony_ci int (*check_unit)(struct rapl_package *rp, int cpu); 958c2ecf20Sopenharmony_ci void (*set_floor_freq)(struct rapl_domain *rd, bool mode); 968c2ecf20Sopenharmony_ci u64 (*compute_time_window)(struct rapl_package *rp, u64 val, 978c2ecf20Sopenharmony_ci bool to_raw); 988c2ecf20Sopenharmony_ci unsigned int dram_domain_energy_unit; 998c2ecf20Sopenharmony_ci unsigned int psys_domain_energy_unit; 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_cistatic struct rapl_defaults *rapl_defaults; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* Sideband MBI registers */ 1048c2ecf20Sopenharmony_ci#define IOSF_CPU_POWER_BUDGET_CTL_BYT (0x2) 1058c2ecf20Sopenharmony_ci#define IOSF_CPU_POWER_BUDGET_CTL_TNG (0xdf) 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci#define PACKAGE_PLN_INT_SAVED BIT(0) 1088c2ecf20Sopenharmony_ci#define MAX_PRIM_NAME (32) 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/* per domain data. used to describe individual knobs such that access function 1118c2ecf20Sopenharmony_ci * can be consolidated into one instead of many inline functions. 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_cistruct rapl_primitive_info { 1148c2ecf20Sopenharmony_ci const char *name; 1158c2ecf20Sopenharmony_ci u64 mask; 1168c2ecf20Sopenharmony_ci int shift; 1178c2ecf20Sopenharmony_ci enum rapl_domain_reg_id id; 1188c2ecf20Sopenharmony_ci enum unit_type unit; 1198c2ecf20Sopenharmony_ci u32 flag; 1208c2ecf20Sopenharmony_ci}; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci#define PRIMITIVE_INFO_INIT(p, m, s, i, u, f) { \ 1238c2ecf20Sopenharmony_ci .name = #p, \ 1248c2ecf20Sopenharmony_ci .mask = m, \ 1258c2ecf20Sopenharmony_ci .shift = s, \ 1268c2ecf20Sopenharmony_ci .id = i, \ 1278c2ecf20Sopenharmony_ci .unit = u, \ 1288c2ecf20Sopenharmony_ci .flag = f \ 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic void rapl_init_domains(struct rapl_package *rp); 1328c2ecf20Sopenharmony_cistatic int rapl_read_data_raw(struct rapl_domain *rd, 1338c2ecf20Sopenharmony_ci enum rapl_primitives prim, 1348c2ecf20Sopenharmony_ci bool xlate, u64 *data); 1358c2ecf20Sopenharmony_cistatic int rapl_write_data_raw(struct rapl_domain *rd, 1368c2ecf20Sopenharmony_ci enum rapl_primitives prim, 1378c2ecf20Sopenharmony_ci unsigned long long value); 1388c2ecf20Sopenharmony_cistatic u64 rapl_unit_xlate(struct rapl_domain *rd, 1398c2ecf20Sopenharmony_ci enum unit_type type, u64 value, int to_raw); 1408c2ecf20Sopenharmony_cistatic void package_power_limit_irq_save(struct rapl_package *rp); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic LIST_HEAD(rapl_packages); /* guarded by CPU hotplug lock */ 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic const char *const rapl_domain_names[] = { 1458c2ecf20Sopenharmony_ci "package", 1468c2ecf20Sopenharmony_ci "core", 1478c2ecf20Sopenharmony_ci "uncore", 1488c2ecf20Sopenharmony_ci "dram", 1498c2ecf20Sopenharmony_ci "psys", 1508c2ecf20Sopenharmony_ci}; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic int get_energy_counter(struct powercap_zone *power_zone, 1538c2ecf20Sopenharmony_ci u64 *energy_raw) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct rapl_domain *rd; 1568c2ecf20Sopenharmony_ci u64 energy_now; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* prevent CPU hotplug, make sure the RAPL domain does not go 1598c2ecf20Sopenharmony_ci * away while reading the counter. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_ci get_online_cpus(); 1628c2ecf20Sopenharmony_ci rd = power_zone_to_rapl_domain(power_zone); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (!rapl_read_data_raw(rd, ENERGY_COUNTER, true, &energy_now)) { 1658c2ecf20Sopenharmony_ci *energy_raw = energy_now; 1668c2ecf20Sopenharmony_ci put_online_cpus(); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return 0; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci put_online_cpus(); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return -EIO; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic int get_max_energy_counter(struct powercap_zone *pcd_dev, u64 *energy) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct rapl_domain *rd = power_zone_to_rapl_domain(pcd_dev); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci *energy = rapl_unit_xlate(rd, ENERGY_UNIT, ENERGY_STATUS_MASK, 0); 1808c2ecf20Sopenharmony_ci return 0; 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic int release_zone(struct powercap_zone *power_zone) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct rapl_domain *rd = power_zone_to_rapl_domain(power_zone); 1868c2ecf20Sopenharmony_ci struct rapl_package *rp = rd->rp; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* package zone is the last zone of a package, we can free 1898c2ecf20Sopenharmony_ci * memory here since all children has been unregistered. 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_ci if (rd->id == RAPL_DOMAIN_PACKAGE) { 1928c2ecf20Sopenharmony_ci kfree(rd); 1938c2ecf20Sopenharmony_ci rp->domains = NULL; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci return 0; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic int find_nr_power_limit(struct rapl_domain *rd) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci int i, nr_pl = 0; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci for (i = 0; i < NR_POWER_LIMITS; i++) { 2058c2ecf20Sopenharmony_ci if (rd->rpl[i].name) 2068c2ecf20Sopenharmony_ci nr_pl++; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return nr_pl; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic int set_domain_enable(struct powercap_zone *power_zone, bool mode) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct rapl_domain *rd = power_zone_to_rapl_domain(power_zone); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (rd->state & DOMAIN_STATE_BIOS_LOCKED) 2178c2ecf20Sopenharmony_ci return -EACCES; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci get_online_cpus(); 2208c2ecf20Sopenharmony_ci rapl_write_data_raw(rd, PL1_ENABLE, mode); 2218c2ecf20Sopenharmony_ci if (rapl_defaults->set_floor_freq) 2228c2ecf20Sopenharmony_ci rapl_defaults->set_floor_freq(rd, mode); 2238c2ecf20Sopenharmony_ci put_online_cpus(); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return 0; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic int get_domain_enable(struct powercap_zone *power_zone, bool *mode) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct rapl_domain *rd = power_zone_to_rapl_domain(power_zone); 2318c2ecf20Sopenharmony_ci u64 val; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (rd->state & DOMAIN_STATE_BIOS_LOCKED) { 2348c2ecf20Sopenharmony_ci *mode = false; 2358c2ecf20Sopenharmony_ci return 0; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci get_online_cpus(); 2388c2ecf20Sopenharmony_ci if (rapl_read_data_raw(rd, PL1_ENABLE, true, &val)) { 2398c2ecf20Sopenharmony_ci put_online_cpus(); 2408c2ecf20Sopenharmony_ci return -EIO; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci *mode = val; 2438c2ecf20Sopenharmony_ci put_online_cpus(); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci return 0; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci/* per RAPL domain ops, in the order of rapl_domain_type */ 2498c2ecf20Sopenharmony_cistatic const struct powercap_zone_ops zone_ops[] = { 2508c2ecf20Sopenharmony_ci /* RAPL_DOMAIN_PACKAGE */ 2518c2ecf20Sopenharmony_ci { 2528c2ecf20Sopenharmony_ci .get_energy_uj = get_energy_counter, 2538c2ecf20Sopenharmony_ci .get_max_energy_range_uj = get_max_energy_counter, 2548c2ecf20Sopenharmony_ci .release = release_zone, 2558c2ecf20Sopenharmony_ci .set_enable = set_domain_enable, 2568c2ecf20Sopenharmony_ci .get_enable = get_domain_enable, 2578c2ecf20Sopenharmony_ci }, 2588c2ecf20Sopenharmony_ci /* RAPL_DOMAIN_PP0 */ 2598c2ecf20Sopenharmony_ci { 2608c2ecf20Sopenharmony_ci .get_energy_uj = get_energy_counter, 2618c2ecf20Sopenharmony_ci .get_max_energy_range_uj = get_max_energy_counter, 2628c2ecf20Sopenharmony_ci .release = release_zone, 2638c2ecf20Sopenharmony_ci .set_enable = set_domain_enable, 2648c2ecf20Sopenharmony_ci .get_enable = get_domain_enable, 2658c2ecf20Sopenharmony_ci }, 2668c2ecf20Sopenharmony_ci /* RAPL_DOMAIN_PP1 */ 2678c2ecf20Sopenharmony_ci { 2688c2ecf20Sopenharmony_ci .get_energy_uj = get_energy_counter, 2698c2ecf20Sopenharmony_ci .get_max_energy_range_uj = get_max_energy_counter, 2708c2ecf20Sopenharmony_ci .release = release_zone, 2718c2ecf20Sopenharmony_ci .set_enable = set_domain_enable, 2728c2ecf20Sopenharmony_ci .get_enable = get_domain_enable, 2738c2ecf20Sopenharmony_ci }, 2748c2ecf20Sopenharmony_ci /* RAPL_DOMAIN_DRAM */ 2758c2ecf20Sopenharmony_ci { 2768c2ecf20Sopenharmony_ci .get_energy_uj = get_energy_counter, 2778c2ecf20Sopenharmony_ci .get_max_energy_range_uj = get_max_energy_counter, 2788c2ecf20Sopenharmony_ci .release = release_zone, 2798c2ecf20Sopenharmony_ci .set_enable = set_domain_enable, 2808c2ecf20Sopenharmony_ci .get_enable = get_domain_enable, 2818c2ecf20Sopenharmony_ci }, 2828c2ecf20Sopenharmony_ci /* RAPL_DOMAIN_PLATFORM */ 2838c2ecf20Sopenharmony_ci { 2848c2ecf20Sopenharmony_ci .get_energy_uj = get_energy_counter, 2858c2ecf20Sopenharmony_ci .get_max_energy_range_uj = get_max_energy_counter, 2868c2ecf20Sopenharmony_ci .release = release_zone, 2878c2ecf20Sopenharmony_ci .set_enable = set_domain_enable, 2888c2ecf20Sopenharmony_ci .get_enable = get_domain_enable, 2898c2ecf20Sopenharmony_ci }, 2908c2ecf20Sopenharmony_ci}; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci/* 2938c2ecf20Sopenharmony_ci * Constraint index used by powercap can be different than power limit (PL) 2948c2ecf20Sopenharmony_ci * index in that some PLs maybe missing due to non-existent MSRs. So we 2958c2ecf20Sopenharmony_ci * need to convert here by finding the valid PLs only (name populated). 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_cistatic int contraint_to_pl(struct rapl_domain *rd, int cid) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci int i, j; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci for (i = 0, j = 0; i < NR_POWER_LIMITS; i++) { 3028c2ecf20Sopenharmony_ci if ((rd->rpl[i].name) && j++ == cid) { 3038c2ecf20Sopenharmony_ci pr_debug("%s: index %d\n", __func__, i); 3048c2ecf20Sopenharmony_ci return i; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci pr_err("Cannot find matching power limit for constraint %d\n", cid); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci return -EINVAL; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic int set_power_limit(struct powercap_zone *power_zone, int cid, 3138c2ecf20Sopenharmony_ci u64 power_limit) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct rapl_domain *rd; 3168c2ecf20Sopenharmony_ci struct rapl_package *rp; 3178c2ecf20Sopenharmony_ci int ret = 0; 3188c2ecf20Sopenharmony_ci int id; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci get_online_cpus(); 3218c2ecf20Sopenharmony_ci rd = power_zone_to_rapl_domain(power_zone); 3228c2ecf20Sopenharmony_ci id = contraint_to_pl(rd, cid); 3238c2ecf20Sopenharmony_ci if (id < 0) { 3248c2ecf20Sopenharmony_ci ret = id; 3258c2ecf20Sopenharmony_ci goto set_exit; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci rp = rd->rp; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (rd->state & DOMAIN_STATE_BIOS_LOCKED) { 3318c2ecf20Sopenharmony_ci dev_warn(&power_zone->dev, 3328c2ecf20Sopenharmony_ci "%s locked by BIOS, monitoring only\n", rd->name); 3338c2ecf20Sopenharmony_ci ret = -EACCES; 3348c2ecf20Sopenharmony_ci goto set_exit; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci switch (rd->rpl[id].prim_id) { 3388c2ecf20Sopenharmony_ci case PL1_ENABLE: 3398c2ecf20Sopenharmony_ci rapl_write_data_raw(rd, POWER_LIMIT1, power_limit); 3408c2ecf20Sopenharmony_ci break; 3418c2ecf20Sopenharmony_ci case PL2_ENABLE: 3428c2ecf20Sopenharmony_ci rapl_write_data_raw(rd, POWER_LIMIT2, power_limit); 3438c2ecf20Sopenharmony_ci break; 3448c2ecf20Sopenharmony_ci case PL4_ENABLE: 3458c2ecf20Sopenharmony_ci rapl_write_data_raw(rd, POWER_LIMIT4, power_limit); 3468c2ecf20Sopenharmony_ci break; 3478c2ecf20Sopenharmony_ci default: 3488c2ecf20Sopenharmony_ci ret = -EINVAL; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci if (!ret) 3518c2ecf20Sopenharmony_ci package_power_limit_irq_save(rp); 3528c2ecf20Sopenharmony_ciset_exit: 3538c2ecf20Sopenharmony_ci put_online_cpus(); 3548c2ecf20Sopenharmony_ci return ret; 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic int get_current_power_limit(struct powercap_zone *power_zone, int cid, 3588c2ecf20Sopenharmony_ci u64 *data) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct rapl_domain *rd; 3618c2ecf20Sopenharmony_ci u64 val; 3628c2ecf20Sopenharmony_ci int prim; 3638c2ecf20Sopenharmony_ci int ret = 0; 3648c2ecf20Sopenharmony_ci int id; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci get_online_cpus(); 3678c2ecf20Sopenharmony_ci rd = power_zone_to_rapl_domain(power_zone); 3688c2ecf20Sopenharmony_ci id = contraint_to_pl(rd, cid); 3698c2ecf20Sopenharmony_ci if (id < 0) { 3708c2ecf20Sopenharmony_ci ret = id; 3718c2ecf20Sopenharmony_ci goto get_exit; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci switch (rd->rpl[id].prim_id) { 3758c2ecf20Sopenharmony_ci case PL1_ENABLE: 3768c2ecf20Sopenharmony_ci prim = POWER_LIMIT1; 3778c2ecf20Sopenharmony_ci break; 3788c2ecf20Sopenharmony_ci case PL2_ENABLE: 3798c2ecf20Sopenharmony_ci prim = POWER_LIMIT2; 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci case PL4_ENABLE: 3828c2ecf20Sopenharmony_ci prim = POWER_LIMIT4; 3838c2ecf20Sopenharmony_ci break; 3848c2ecf20Sopenharmony_ci default: 3858c2ecf20Sopenharmony_ci put_online_cpus(); 3868c2ecf20Sopenharmony_ci return -EINVAL; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci if (rapl_read_data_raw(rd, prim, true, &val)) 3898c2ecf20Sopenharmony_ci ret = -EIO; 3908c2ecf20Sopenharmony_ci else 3918c2ecf20Sopenharmony_ci *data = val; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ciget_exit: 3948c2ecf20Sopenharmony_ci put_online_cpus(); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci return ret; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic int set_time_window(struct powercap_zone *power_zone, int cid, 4008c2ecf20Sopenharmony_ci u64 window) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci struct rapl_domain *rd; 4038c2ecf20Sopenharmony_ci int ret = 0; 4048c2ecf20Sopenharmony_ci int id; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci get_online_cpus(); 4078c2ecf20Sopenharmony_ci rd = power_zone_to_rapl_domain(power_zone); 4088c2ecf20Sopenharmony_ci id = contraint_to_pl(rd, cid); 4098c2ecf20Sopenharmony_ci if (id < 0) { 4108c2ecf20Sopenharmony_ci ret = id; 4118c2ecf20Sopenharmony_ci goto set_time_exit; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci switch (rd->rpl[id].prim_id) { 4158c2ecf20Sopenharmony_ci case PL1_ENABLE: 4168c2ecf20Sopenharmony_ci rapl_write_data_raw(rd, TIME_WINDOW1, window); 4178c2ecf20Sopenharmony_ci break; 4188c2ecf20Sopenharmony_ci case PL2_ENABLE: 4198c2ecf20Sopenharmony_ci rapl_write_data_raw(rd, TIME_WINDOW2, window); 4208c2ecf20Sopenharmony_ci break; 4218c2ecf20Sopenharmony_ci default: 4228c2ecf20Sopenharmony_ci ret = -EINVAL; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ciset_time_exit: 4268c2ecf20Sopenharmony_ci put_online_cpus(); 4278c2ecf20Sopenharmony_ci return ret; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic int get_time_window(struct powercap_zone *power_zone, int cid, 4318c2ecf20Sopenharmony_ci u64 *data) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct rapl_domain *rd; 4348c2ecf20Sopenharmony_ci u64 val; 4358c2ecf20Sopenharmony_ci int ret = 0; 4368c2ecf20Sopenharmony_ci int id; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci get_online_cpus(); 4398c2ecf20Sopenharmony_ci rd = power_zone_to_rapl_domain(power_zone); 4408c2ecf20Sopenharmony_ci id = contraint_to_pl(rd, cid); 4418c2ecf20Sopenharmony_ci if (id < 0) { 4428c2ecf20Sopenharmony_ci ret = id; 4438c2ecf20Sopenharmony_ci goto get_time_exit; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci switch (rd->rpl[id].prim_id) { 4478c2ecf20Sopenharmony_ci case PL1_ENABLE: 4488c2ecf20Sopenharmony_ci ret = rapl_read_data_raw(rd, TIME_WINDOW1, true, &val); 4498c2ecf20Sopenharmony_ci break; 4508c2ecf20Sopenharmony_ci case PL2_ENABLE: 4518c2ecf20Sopenharmony_ci ret = rapl_read_data_raw(rd, TIME_WINDOW2, true, &val); 4528c2ecf20Sopenharmony_ci break; 4538c2ecf20Sopenharmony_ci case PL4_ENABLE: 4548c2ecf20Sopenharmony_ci /* 4558c2ecf20Sopenharmony_ci * Time window parameter is not applicable for PL4 entry 4568c2ecf20Sopenharmony_ci * so assigining '0' as default value. 4578c2ecf20Sopenharmony_ci */ 4588c2ecf20Sopenharmony_ci val = 0; 4598c2ecf20Sopenharmony_ci break; 4608c2ecf20Sopenharmony_ci default: 4618c2ecf20Sopenharmony_ci put_online_cpus(); 4628c2ecf20Sopenharmony_ci return -EINVAL; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci if (!ret) 4658c2ecf20Sopenharmony_ci *data = val; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ciget_time_exit: 4688c2ecf20Sopenharmony_ci put_online_cpus(); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci return ret; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic const char *get_constraint_name(struct powercap_zone *power_zone, 4748c2ecf20Sopenharmony_ci int cid) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct rapl_domain *rd; 4778c2ecf20Sopenharmony_ci int id; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci rd = power_zone_to_rapl_domain(power_zone); 4808c2ecf20Sopenharmony_ci id = contraint_to_pl(rd, cid); 4818c2ecf20Sopenharmony_ci if (id >= 0) 4828c2ecf20Sopenharmony_ci return rd->rpl[id].name; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci return NULL; 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic int get_max_power(struct powercap_zone *power_zone, int id, u64 *data) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci struct rapl_domain *rd; 4908c2ecf20Sopenharmony_ci u64 val; 4918c2ecf20Sopenharmony_ci int prim; 4928c2ecf20Sopenharmony_ci int ret = 0; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci get_online_cpus(); 4958c2ecf20Sopenharmony_ci rd = power_zone_to_rapl_domain(power_zone); 4968c2ecf20Sopenharmony_ci switch (rd->rpl[id].prim_id) { 4978c2ecf20Sopenharmony_ci case PL1_ENABLE: 4988c2ecf20Sopenharmony_ci prim = THERMAL_SPEC_POWER; 4998c2ecf20Sopenharmony_ci break; 5008c2ecf20Sopenharmony_ci case PL2_ENABLE: 5018c2ecf20Sopenharmony_ci prim = MAX_POWER; 5028c2ecf20Sopenharmony_ci break; 5038c2ecf20Sopenharmony_ci case PL4_ENABLE: 5048c2ecf20Sopenharmony_ci prim = MAX_POWER; 5058c2ecf20Sopenharmony_ci break; 5068c2ecf20Sopenharmony_ci default: 5078c2ecf20Sopenharmony_ci put_online_cpus(); 5088c2ecf20Sopenharmony_ci return -EINVAL; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci if (rapl_read_data_raw(rd, prim, true, &val)) 5118c2ecf20Sopenharmony_ci ret = -EIO; 5128c2ecf20Sopenharmony_ci else 5138c2ecf20Sopenharmony_ci *data = val; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* As a generalization rule, PL4 would be around two times PL2. */ 5168c2ecf20Sopenharmony_ci if (rd->rpl[id].prim_id == PL4_ENABLE) 5178c2ecf20Sopenharmony_ci *data = *data * 2; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci put_online_cpus(); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci return ret; 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cistatic const struct powercap_zone_constraint_ops constraint_ops = { 5258c2ecf20Sopenharmony_ci .set_power_limit_uw = set_power_limit, 5268c2ecf20Sopenharmony_ci .get_power_limit_uw = get_current_power_limit, 5278c2ecf20Sopenharmony_ci .set_time_window_us = set_time_window, 5288c2ecf20Sopenharmony_ci .get_time_window_us = get_time_window, 5298c2ecf20Sopenharmony_ci .get_max_power_uw = get_max_power, 5308c2ecf20Sopenharmony_ci .get_name = get_constraint_name, 5318c2ecf20Sopenharmony_ci}; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci/* called after domain detection and package level data are set */ 5348c2ecf20Sopenharmony_cistatic void rapl_init_domains(struct rapl_package *rp) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci enum rapl_domain_type i; 5378c2ecf20Sopenharmony_ci enum rapl_domain_reg_id j; 5388c2ecf20Sopenharmony_ci struct rapl_domain *rd = rp->domains; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci for (i = 0; i < RAPL_DOMAIN_MAX; i++) { 5418c2ecf20Sopenharmony_ci unsigned int mask = rp->domain_map & (1 << i); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (!mask) 5448c2ecf20Sopenharmony_ci continue; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci rd->rp = rp; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci if (i == RAPL_DOMAIN_PLATFORM && rp->id > 0) { 5498c2ecf20Sopenharmony_ci snprintf(rd->name, RAPL_DOMAIN_NAME_LENGTH, "psys-%d", 5508c2ecf20Sopenharmony_ci cpu_data(rp->lead_cpu).phys_proc_id); 5518c2ecf20Sopenharmony_ci } else 5528c2ecf20Sopenharmony_ci snprintf(rd->name, RAPL_DOMAIN_NAME_LENGTH, "%s", 5538c2ecf20Sopenharmony_ci rapl_domain_names[i]); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci rd->id = i; 5568c2ecf20Sopenharmony_ci rd->rpl[0].prim_id = PL1_ENABLE; 5578c2ecf20Sopenharmony_ci rd->rpl[0].name = pl1_name; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* 5608c2ecf20Sopenharmony_ci * The PL2 power domain is applicable for limits two 5618c2ecf20Sopenharmony_ci * and limits three 5628c2ecf20Sopenharmony_ci */ 5638c2ecf20Sopenharmony_ci if (rp->priv->limits[i] >= 2) { 5648c2ecf20Sopenharmony_ci rd->rpl[1].prim_id = PL2_ENABLE; 5658c2ecf20Sopenharmony_ci rd->rpl[1].name = pl2_name; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci /* Enable PL4 domain if the total power limits are three */ 5698c2ecf20Sopenharmony_ci if (rp->priv->limits[i] == 3) { 5708c2ecf20Sopenharmony_ci rd->rpl[2].prim_id = PL4_ENABLE; 5718c2ecf20Sopenharmony_ci rd->rpl[2].name = pl4_name; 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci for (j = 0; j < RAPL_DOMAIN_REG_MAX; j++) 5758c2ecf20Sopenharmony_ci rd->regs[j] = rp->priv->regs[i][j]; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci switch (i) { 5788c2ecf20Sopenharmony_ci case RAPL_DOMAIN_DRAM: 5798c2ecf20Sopenharmony_ci rd->domain_energy_unit = 5808c2ecf20Sopenharmony_ci rapl_defaults->dram_domain_energy_unit; 5818c2ecf20Sopenharmony_ci if (rd->domain_energy_unit) 5828c2ecf20Sopenharmony_ci pr_info("DRAM domain energy unit %dpj\n", 5838c2ecf20Sopenharmony_ci rd->domain_energy_unit); 5848c2ecf20Sopenharmony_ci break; 5858c2ecf20Sopenharmony_ci case RAPL_DOMAIN_PLATFORM: 5868c2ecf20Sopenharmony_ci rd->domain_energy_unit = 5878c2ecf20Sopenharmony_ci rapl_defaults->psys_domain_energy_unit; 5888c2ecf20Sopenharmony_ci if (rd->domain_energy_unit) 5898c2ecf20Sopenharmony_ci pr_info("Platform domain energy unit %dpj\n", 5908c2ecf20Sopenharmony_ci rd->domain_energy_unit); 5918c2ecf20Sopenharmony_ci break; 5928c2ecf20Sopenharmony_ci default: 5938c2ecf20Sopenharmony_ci break; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci rd++; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cistatic u64 rapl_unit_xlate(struct rapl_domain *rd, enum unit_type type, 6008c2ecf20Sopenharmony_ci u64 value, int to_raw) 6018c2ecf20Sopenharmony_ci{ 6028c2ecf20Sopenharmony_ci u64 units = 1; 6038c2ecf20Sopenharmony_ci struct rapl_package *rp = rd->rp; 6048c2ecf20Sopenharmony_ci u64 scale = 1; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci switch (type) { 6078c2ecf20Sopenharmony_ci case POWER_UNIT: 6088c2ecf20Sopenharmony_ci units = rp->power_unit; 6098c2ecf20Sopenharmony_ci break; 6108c2ecf20Sopenharmony_ci case ENERGY_UNIT: 6118c2ecf20Sopenharmony_ci scale = ENERGY_UNIT_SCALE; 6128c2ecf20Sopenharmony_ci /* per domain unit takes precedence */ 6138c2ecf20Sopenharmony_ci if (rd->domain_energy_unit) 6148c2ecf20Sopenharmony_ci units = rd->domain_energy_unit; 6158c2ecf20Sopenharmony_ci else 6168c2ecf20Sopenharmony_ci units = rp->energy_unit; 6178c2ecf20Sopenharmony_ci break; 6188c2ecf20Sopenharmony_ci case TIME_UNIT: 6198c2ecf20Sopenharmony_ci return rapl_defaults->compute_time_window(rp, value, to_raw); 6208c2ecf20Sopenharmony_ci case ARBITRARY_UNIT: 6218c2ecf20Sopenharmony_ci default: 6228c2ecf20Sopenharmony_ci return value; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci if (to_raw) 6268c2ecf20Sopenharmony_ci return div64_u64(value, units) * scale; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci value *= units; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci return div64_u64(value, scale); 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci/* in the order of enum rapl_primitives */ 6348c2ecf20Sopenharmony_cistatic struct rapl_primitive_info rpi[] = { 6358c2ecf20Sopenharmony_ci /* name, mask, shift, msr index, unit divisor */ 6368c2ecf20Sopenharmony_ci PRIMITIVE_INFO_INIT(ENERGY_COUNTER, ENERGY_STATUS_MASK, 0, 6378c2ecf20Sopenharmony_ci RAPL_DOMAIN_REG_STATUS, ENERGY_UNIT, 0), 6388c2ecf20Sopenharmony_ci PRIMITIVE_INFO_INIT(POWER_LIMIT1, POWER_LIMIT1_MASK, 0, 6398c2ecf20Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, POWER_UNIT, 0), 6408c2ecf20Sopenharmony_ci PRIMITIVE_INFO_INIT(POWER_LIMIT2, POWER_LIMIT2_MASK, 32, 6418c2ecf20Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, POWER_UNIT, 0), 6428c2ecf20Sopenharmony_ci PRIMITIVE_INFO_INIT(POWER_LIMIT4, POWER_LIMIT4_MASK, 0, 6438c2ecf20Sopenharmony_ci RAPL_DOMAIN_REG_PL4, POWER_UNIT, 0), 6448c2ecf20Sopenharmony_ci PRIMITIVE_INFO_INIT(FW_LOCK, POWER_LOW_LOCK, 31, 6458c2ecf20Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0), 6468c2ecf20Sopenharmony_ci PRIMITIVE_INFO_INIT(PL1_ENABLE, POWER_LIMIT1_ENABLE, 15, 6478c2ecf20Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0), 6488c2ecf20Sopenharmony_ci PRIMITIVE_INFO_INIT(PL1_CLAMP, POWER_LIMIT1_CLAMP, 16, 6498c2ecf20Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0), 6508c2ecf20Sopenharmony_ci PRIMITIVE_INFO_INIT(PL2_ENABLE, POWER_LIMIT2_ENABLE, 47, 6518c2ecf20Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0), 6528c2ecf20Sopenharmony_ci PRIMITIVE_INFO_INIT(PL2_CLAMP, POWER_LIMIT2_CLAMP, 48, 6538c2ecf20Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0), 6548c2ecf20Sopenharmony_ci PRIMITIVE_INFO_INIT(PL4_ENABLE, POWER_LIMIT4_MASK, 0, 6558c2ecf20Sopenharmony_ci RAPL_DOMAIN_REG_PL4, ARBITRARY_UNIT, 0), 6568c2ecf20Sopenharmony_ci PRIMITIVE_INFO_INIT(TIME_WINDOW1, TIME_WINDOW1_MASK, 17, 6578c2ecf20Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, TIME_UNIT, 0), 6588c2ecf20Sopenharmony_ci PRIMITIVE_INFO_INIT(TIME_WINDOW2, TIME_WINDOW2_MASK, 49, 6598c2ecf20Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, TIME_UNIT, 0), 6608c2ecf20Sopenharmony_ci PRIMITIVE_INFO_INIT(THERMAL_SPEC_POWER, POWER_INFO_THERMAL_SPEC_MASK, 6618c2ecf20Sopenharmony_ci 0, RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0), 6628c2ecf20Sopenharmony_ci PRIMITIVE_INFO_INIT(MAX_POWER, POWER_INFO_MAX_MASK, 32, 6638c2ecf20Sopenharmony_ci RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0), 6648c2ecf20Sopenharmony_ci PRIMITIVE_INFO_INIT(MIN_POWER, POWER_INFO_MIN_MASK, 16, 6658c2ecf20Sopenharmony_ci RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0), 6668c2ecf20Sopenharmony_ci PRIMITIVE_INFO_INIT(MAX_TIME_WINDOW, POWER_INFO_MAX_TIME_WIN_MASK, 48, 6678c2ecf20Sopenharmony_ci RAPL_DOMAIN_REG_INFO, TIME_UNIT, 0), 6688c2ecf20Sopenharmony_ci PRIMITIVE_INFO_INIT(THROTTLED_TIME, PERF_STATUS_THROTTLE_TIME_MASK, 0, 6698c2ecf20Sopenharmony_ci RAPL_DOMAIN_REG_PERF, TIME_UNIT, 0), 6708c2ecf20Sopenharmony_ci PRIMITIVE_INFO_INIT(PRIORITY_LEVEL, PP_POLICY_MASK, 0, 6718c2ecf20Sopenharmony_ci RAPL_DOMAIN_REG_POLICY, ARBITRARY_UNIT, 0), 6728c2ecf20Sopenharmony_ci /* non-hardware */ 6738c2ecf20Sopenharmony_ci PRIMITIVE_INFO_INIT(AVERAGE_POWER, 0, 0, 0, POWER_UNIT, 6748c2ecf20Sopenharmony_ci RAPL_PRIMITIVE_DERIVED), 6758c2ecf20Sopenharmony_ci {NULL, 0, 0, 0}, 6768c2ecf20Sopenharmony_ci}; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci/* Read primitive data based on its related struct rapl_primitive_info. 6798c2ecf20Sopenharmony_ci * if xlate flag is set, return translated data based on data units, i.e. 6808c2ecf20Sopenharmony_ci * time, energy, and power. 6818c2ecf20Sopenharmony_ci * RAPL MSRs are non-architectual and are laid out not consistently across 6828c2ecf20Sopenharmony_ci * domains. Here we use primitive info to allow writing consolidated access 6838c2ecf20Sopenharmony_ci * functions. 6848c2ecf20Sopenharmony_ci * For a given primitive, it is processed by MSR mask and shift. Unit conversion 6858c2ecf20Sopenharmony_ci * is pre-assigned based on RAPL unit MSRs read at init time. 6868c2ecf20Sopenharmony_ci * 63-------------------------- 31--------------------------- 0 6878c2ecf20Sopenharmony_ci * | xxxxx (mask) | 6888c2ecf20Sopenharmony_ci * | |<- shift ----------------| 6898c2ecf20Sopenharmony_ci * 63-------------------------- 31--------------------------- 0 6908c2ecf20Sopenharmony_ci */ 6918c2ecf20Sopenharmony_cistatic int rapl_read_data_raw(struct rapl_domain *rd, 6928c2ecf20Sopenharmony_ci enum rapl_primitives prim, bool xlate, u64 *data) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci u64 value; 6958c2ecf20Sopenharmony_ci struct rapl_primitive_info *rp = &rpi[prim]; 6968c2ecf20Sopenharmony_ci struct reg_action ra; 6978c2ecf20Sopenharmony_ci int cpu; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci if (!rp->name || rp->flag & RAPL_PRIMITIVE_DUMMY) 7008c2ecf20Sopenharmony_ci return -EINVAL; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci ra.reg = rd->regs[rp->id]; 7038c2ecf20Sopenharmony_ci if (!ra.reg) 7048c2ecf20Sopenharmony_ci return -EINVAL; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci cpu = rd->rp->lead_cpu; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci /* domain with 2 limits has different bit */ 7098c2ecf20Sopenharmony_ci if (prim == FW_LOCK && rd->rp->priv->limits[rd->id] == 2) { 7108c2ecf20Sopenharmony_ci rp->mask = POWER_HIGH_LOCK; 7118c2ecf20Sopenharmony_ci rp->shift = 63; 7128c2ecf20Sopenharmony_ci } 7138c2ecf20Sopenharmony_ci /* non-hardware data are collected by the polling thread */ 7148c2ecf20Sopenharmony_ci if (rp->flag & RAPL_PRIMITIVE_DERIVED) { 7158c2ecf20Sopenharmony_ci *data = rd->rdd.primitives[prim]; 7168c2ecf20Sopenharmony_ci return 0; 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci ra.mask = rp->mask; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci if (rd->rp->priv->read_raw(cpu, &ra)) { 7228c2ecf20Sopenharmony_ci pr_debug("failed to read reg 0x%llx on cpu %d\n", ra.reg, cpu); 7238c2ecf20Sopenharmony_ci return -EIO; 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci value = ra.value >> rp->shift; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci if (xlate) 7298c2ecf20Sopenharmony_ci *data = rapl_unit_xlate(rd, rp->unit, value, 0); 7308c2ecf20Sopenharmony_ci else 7318c2ecf20Sopenharmony_ci *data = value; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci return 0; 7348c2ecf20Sopenharmony_ci} 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci/* Similar use of primitive info in the read counterpart */ 7378c2ecf20Sopenharmony_cistatic int rapl_write_data_raw(struct rapl_domain *rd, 7388c2ecf20Sopenharmony_ci enum rapl_primitives prim, 7398c2ecf20Sopenharmony_ci unsigned long long value) 7408c2ecf20Sopenharmony_ci{ 7418c2ecf20Sopenharmony_ci struct rapl_primitive_info *rp = &rpi[prim]; 7428c2ecf20Sopenharmony_ci int cpu; 7438c2ecf20Sopenharmony_ci u64 bits; 7448c2ecf20Sopenharmony_ci struct reg_action ra; 7458c2ecf20Sopenharmony_ci int ret; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci cpu = rd->rp->lead_cpu; 7488c2ecf20Sopenharmony_ci bits = rapl_unit_xlate(rd, rp->unit, value, 1); 7498c2ecf20Sopenharmony_ci bits <<= rp->shift; 7508c2ecf20Sopenharmony_ci bits &= rp->mask; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci memset(&ra, 0, sizeof(ra)); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci ra.reg = rd->regs[rp->id]; 7558c2ecf20Sopenharmony_ci ra.mask = rp->mask; 7568c2ecf20Sopenharmony_ci ra.value = bits; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci ret = rd->rp->priv->write_raw(cpu, &ra); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci return ret; 7618c2ecf20Sopenharmony_ci} 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci/* 7648c2ecf20Sopenharmony_ci * Raw RAPL data stored in MSRs are in certain scales. We need to 7658c2ecf20Sopenharmony_ci * convert them into standard units based on the units reported in 7668c2ecf20Sopenharmony_ci * the RAPL unit MSRs. This is specific to CPUs as the method to 7678c2ecf20Sopenharmony_ci * calculate units differ on different CPUs. 7688c2ecf20Sopenharmony_ci * We convert the units to below format based on CPUs. 7698c2ecf20Sopenharmony_ci * i.e. 7708c2ecf20Sopenharmony_ci * energy unit: picoJoules : Represented in picoJoules by default 7718c2ecf20Sopenharmony_ci * power unit : microWatts : Represented in milliWatts by default 7728c2ecf20Sopenharmony_ci * time unit : microseconds: Represented in seconds by default 7738c2ecf20Sopenharmony_ci */ 7748c2ecf20Sopenharmony_cistatic int rapl_check_unit_core(struct rapl_package *rp, int cpu) 7758c2ecf20Sopenharmony_ci{ 7768c2ecf20Sopenharmony_ci struct reg_action ra; 7778c2ecf20Sopenharmony_ci u32 value; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci ra.reg = rp->priv->reg_unit; 7808c2ecf20Sopenharmony_ci ra.mask = ~0; 7818c2ecf20Sopenharmony_ci if (rp->priv->read_raw(cpu, &ra)) { 7828c2ecf20Sopenharmony_ci pr_err("Failed to read power unit REG 0x%llx on CPU %d, exit.\n", 7838c2ecf20Sopenharmony_ci rp->priv->reg_unit, cpu); 7848c2ecf20Sopenharmony_ci return -ENODEV; 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci value = (ra.value & ENERGY_UNIT_MASK) >> ENERGY_UNIT_OFFSET; 7888c2ecf20Sopenharmony_ci rp->energy_unit = ENERGY_UNIT_SCALE * 1000000 / (1 << value); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci value = (ra.value & POWER_UNIT_MASK) >> POWER_UNIT_OFFSET; 7918c2ecf20Sopenharmony_ci rp->power_unit = 1000000 / (1 << value); 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci value = (ra.value & TIME_UNIT_MASK) >> TIME_UNIT_OFFSET; 7948c2ecf20Sopenharmony_ci rp->time_unit = 1000000 / (1 << value); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci pr_debug("Core CPU %s energy=%dpJ, time=%dus, power=%duW\n", 7978c2ecf20Sopenharmony_ci rp->name, rp->energy_unit, rp->time_unit, rp->power_unit); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci return 0; 8008c2ecf20Sopenharmony_ci} 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_cistatic int rapl_check_unit_atom(struct rapl_package *rp, int cpu) 8038c2ecf20Sopenharmony_ci{ 8048c2ecf20Sopenharmony_ci struct reg_action ra; 8058c2ecf20Sopenharmony_ci u32 value; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci ra.reg = rp->priv->reg_unit; 8088c2ecf20Sopenharmony_ci ra.mask = ~0; 8098c2ecf20Sopenharmony_ci if (rp->priv->read_raw(cpu, &ra)) { 8108c2ecf20Sopenharmony_ci pr_err("Failed to read power unit REG 0x%llx on CPU %d, exit.\n", 8118c2ecf20Sopenharmony_ci rp->priv->reg_unit, cpu); 8128c2ecf20Sopenharmony_ci return -ENODEV; 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci value = (ra.value & ENERGY_UNIT_MASK) >> ENERGY_UNIT_OFFSET; 8168c2ecf20Sopenharmony_ci rp->energy_unit = ENERGY_UNIT_SCALE * 1 << value; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci value = (ra.value & POWER_UNIT_MASK) >> POWER_UNIT_OFFSET; 8198c2ecf20Sopenharmony_ci rp->power_unit = (1 << value) * 1000; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci value = (ra.value & TIME_UNIT_MASK) >> TIME_UNIT_OFFSET; 8228c2ecf20Sopenharmony_ci rp->time_unit = 1000000 / (1 << value); 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci pr_debug("Atom %s energy=%dpJ, time=%dus, power=%duW\n", 8258c2ecf20Sopenharmony_ci rp->name, rp->energy_unit, rp->time_unit, rp->power_unit); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci return 0; 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_cistatic void power_limit_irq_save_cpu(void *info) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci u32 l, h = 0; 8338c2ecf20Sopenharmony_ci struct rapl_package *rp = (struct rapl_package *)info; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci /* save the state of PLN irq mask bit before disabling it */ 8368c2ecf20Sopenharmony_ci rdmsr_safe(MSR_IA32_PACKAGE_THERM_INTERRUPT, &l, &h); 8378c2ecf20Sopenharmony_ci if (!(rp->power_limit_irq & PACKAGE_PLN_INT_SAVED)) { 8388c2ecf20Sopenharmony_ci rp->power_limit_irq = l & PACKAGE_THERM_INT_PLN_ENABLE; 8398c2ecf20Sopenharmony_ci rp->power_limit_irq |= PACKAGE_PLN_INT_SAVED; 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci l &= ~PACKAGE_THERM_INT_PLN_ENABLE; 8428c2ecf20Sopenharmony_ci wrmsr_safe(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h); 8438c2ecf20Sopenharmony_ci} 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci/* REVISIT: 8468c2ecf20Sopenharmony_ci * When package power limit is set artificially low by RAPL, LVT 8478c2ecf20Sopenharmony_ci * thermal interrupt for package power limit should be ignored 8488c2ecf20Sopenharmony_ci * since we are not really exceeding the real limit. The intention 8498c2ecf20Sopenharmony_ci * is to avoid excessive interrupts while we are trying to save power. 8508c2ecf20Sopenharmony_ci * A useful feature might be routing the package_power_limit interrupt 8518c2ecf20Sopenharmony_ci * to userspace via eventfd. once we have a usecase, this is simple 8528c2ecf20Sopenharmony_ci * to do by adding an atomic notifier. 8538c2ecf20Sopenharmony_ci */ 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_cistatic void package_power_limit_irq_save(struct rapl_package *rp) 8568c2ecf20Sopenharmony_ci{ 8578c2ecf20Sopenharmony_ci if (!boot_cpu_has(X86_FEATURE_PTS) || !boot_cpu_has(X86_FEATURE_PLN)) 8588c2ecf20Sopenharmony_ci return; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci smp_call_function_single(rp->lead_cpu, power_limit_irq_save_cpu, rp, 1); 8618c2ecf20Sopenharmony_ci} 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci/* 8648c2ecf20Sopenharmony_ci * Restore per package power limit interrupt enable state. Called from cpu 8658c2ecf20Sopenharmony_ci * hotplug code on package removal. 8668c2ecf20Sopenharmony_ci */ 8678c2ecf20Sopenharmony_cistatic void package_power_limit_irq_restore(struct rapl_package *rp) 8688c2ecf20Sopenharmony_ci{ 8698c2ecf20Sopenharmony_ci u32 l, h; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci if (!boot_cpu_has(X86_FEATURE_PTS) || !boot_cpu_has(X86_FEATURE_PLN)) 8728c2ecf20Sopenharmony_ci return; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci /* irq enable state not saved, nothing to restore */ 8758c2ecf20Sopenharmony_ci if (!(rp->power_limit_irq & PACKAGE_PLN_INT_SAVED)) 8768c2ecf20Sopenharmony_ci return; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci rdmsr_safe(MSR_IA32_PACKAGE_THERM_INTERRUPT, &l, &h); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci if (rp->power_limit_irq & PACKAGE_THERM_INT_PLN_ENABLE) 8818c2ecf20Sopenharmony_ci l |= PACKAGE_THERM_INT_PLN_ENABLE; 8828c2ecf20Sopenharmony_ci else 8838c2ecf20Sopenharmony_ci l &= ~PACKAGE_THERM_INT_PLN_ENABLE; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci wrmsr_safe(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h); 8868c2ecf20Sopenharmony_ci} 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_cistatic void set_floor_freq_default(struct rapl_domain *rd, bool mode) 8898c2ecf20Sopenharmony_ci{ 8908c2ecf20Sopenharmony_ci int nr_powerlimit = find_nr_power_limit(rd); 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci /* always enable clamp such that p-state can go below OS requested 8938c2ecf20Sopenharmony_ci * range. power capping priority over guranteed frequency. 8948c2ecf20Sopenharmony_ci */ 8958c2ecf20Sopenharmony_ci rapl_write_data_raw(rd, PL1_CLAMP, mode); 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci /* some domains have pl2 */ 8988c2ecf20Sopenharmony_ci if (nr_powerlimit > 1) { 8998c2ecf20Sopenharmony_ci rapl_write_data_raw(rd, PL2_ENABLE, mode); 9008c2ecf20Sopenharmony_ci rapl_write_data_raw(rd, PL2_CLAMP, mode); 9018c2ecf20Sopenharmony_ci } 9028c2ecf20Sopenharmony_ci} 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_cistatic void set_floor_freq_atom(struct rapl_domain *rd, bool enable) 9058c2ecf20Sopenharmony_ci{ 9068c2ecf20Sopenharmony_ci static u32 power_ctrl_orig_val; 9078c2ecf20Sopenharmony_ci u32 mdata; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci if (!rapl_defaults->floor_freq_reg_addr) { 9108c2ecf20Sopenharmony_ci pr_err("Invalid floor frequency config register\n"); 9118c2ecf20Sopenharmony_ci return; 9128c2ecf20Sopenharmony_ci } 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci if (!power_ctrl_orig_val) 9158c2ecf20Sopenharmony_ci iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_CR_READ, 9168c2ecf20Sopenharmony_ci rapl_defaults->floor_freq_reg_addr, 9178c2ecf20Sopenharmony_ci &power_ctrl_orig_val); 9188c2ecf20Sopenharmony_ci mdata = power_ctrl_orig_val; 9198c2ecf20Sopenharmony_ci if (enable) { 9208c2ecf20Sopenharmony_ci mdata &= ~(0x7f << 8); 9218c2ecf20Sopenharmony_ci mdata |= 1 << 8; 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_CR_WRITE, 9248c2ecf20Sopenharmony_ci rapl_defaults->floor_freq_reg_addr, mdata); 9258c2ecf20Sopenharmony_ci} 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_cistatic u64 rapl_compute_time_window_core(struct rapl_package *rp, u64 value, 9288c2ecf20Sopenharmony_ci bool to_raw) 9298c2ecf20Sopenharmony_ci{ 9308c2ecf20Sopenharmony_ci u64 f, y; /* fraction and exp. used for time unit */ 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci /* 9338c2ecf20Sopenharmony_ci * Special processing based on 2^Y*(1+F/4), refer 9348c2ecf20Sopenharmony_ci * to Intel Software Developer's manual Vol.3B: CH 14.9.3. 9358c2ecf20Sopenharmony_ci */ 9368c2ecf20Sopenharmony_ci if (!to_raw) { 9378c2ecf20Sopenharmony_ci f = (value & 0x60) >> 5; 9388c2ecf20Sopenharmony_ci y = value & 0x1f; 9398c2ecf20Sopenharmony_ci value = (1 << y) * (4 + f) * rp->time_unit / 4; 9408c2ecf20Sopenharmony_ci } else { 9418c2ecf20Sopenharmony_ci if (value < rp->time_unit) 9428c2ecf20Sopenharmony_ci return 0; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci do_div(value, rp->time_unit); 9458c2ecf20Sopenharmony_ci y = ilog2(value); 9468c2ecf20Sopenharmony_ci f = div64_u64(4 * (value - (1 << y)), 1 << y); 9478c2ecf20Sopenharmony_ci value = (y & 0x1f) | ((f & 0x3) << 5); 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci return value; 9508c2ecf20Sopenharmony_ci} 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_cistatic u64 rapl_compute_time_window_atom(struct rapl_package *rp, u64 value, 9538c2ecf20Sopenharmony_ci bool to_raw) 9548c2ecf20Sopenharmony_ci{ 9558c2ecf20Sopenharmony_ci /* 9568c2ecf20Sopenharmony_ci * Atom time unit encoding is straight forward val * time_unit, 9578c2ecf20Sopenharmony_ci * where time_unit is default to 1 sec. Never 0. 9588c2ecf20Sopenharmony_ci */ 9598c2ecf20Sopenharmony_ci if (!to_raw) 9608c2ecf20Sopenharmony_ci return (value) ? value *= rp->time_unit : rp->time_unit; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci value = div64_u64(value, rp->time_unit); 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci return value; 9658c2ecf20Sopenharmony_ci} 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_cistatic const struct rapl_defaults rapl_defaults_core = { 9688c2ecf20Sopenharmony_ci .floor_freq_reg_addr = 0, 9698c2ecf20Sopenharmony_ci .check_unit = rapl_check_unit_core, 9708c2ecf20Sopenharmony_ci .set_floor_freq = set_floor_freq_default, 9718c2ecf20Sopenharmony_ci .compute_time_window = rapl_compute_time_window_core, 9728c2ecf20Sopenharmony_ci}; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_cistatic const struct rapl_defaults rapl_defaults_hsw_server = { 9758c2ecf20Sopenharmony_ci .check_unit = rapl_check_unit_core, 9768c2ecf20Sopenharmony_ci .set_floor_freq = set_floor_freq_default, 9778c2ecf20Sopenharmony_ci .compute_time_window = rapl_compute_time_window_core, 9788c2ecf20Sopenharmony_ci .dram_domain_energy_unit = 15300, 9798c2ecf20Sopenharmony_ci}; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_cistatic const struct rapl_defaults rapl_defaults_spr_server = { 9828c2ecf20Sopenharmony_ci .check_unit = rapl_check_unit_core, 9838c2ecf20Sopenharmony_ci .set_floor_freq = set_floor_freq_default, 9848c2ecf20Sopenharmony_ci .compute_time_window = rapl_compute_time_window_core, 9858c2ecf20Sopenharmony_ci .psys_domain_energy_unit = 1000000000, 9868c2ecf20Sopenharmony_ci}; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_cistatic const struct rapl_defaults rapl_defaults_byt = { 9898c2ecf20Sopenharmony_ci .floor_freq_reg_addr = IOSF_CPU_POWER_BUDGET_CTL_BYT, 9908c2ecf20Sopenharmony_ci .check_unit = rapl_check_unit_atom, 9918c2ecf20Sopenharmony_ci .set_floor_freq = set_floor_freq_atom, 9928c2ecf20Sopenharmony_ci .compute_time_window = rapl_compute_time_window_atom, 9938c2ecf20Sopenharmony_ci}; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_cistatic const struct rapl_defaults rapl_defaults_tng = { 9968c2ecf20Sopenharmony_ci .floor_freq_reg_addr = IOSF_CPU_POWER_BUDGET_CTL_TNG, 9978c2ecf20Sopenharmony_ci .check_unit = rapl_check_unit_atom, 9988c2ecf20Sopenharmony_ci .set_floor_freq = set_floor_freq_atom, 9998c2ecf20Sopenharmony_ci .compute_time_window = rapl_compute_time_window_atom, 10008c2ecf20Sopenharmony_ci}; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_cistatic const struct rapl_defaults rapl_defaults_ann = { 10038c2ecf20Sopenharmony_ci .floor_freq_reg_addr = 0, 10048c2ecf20Sopenharmony_ci .check_unit = rapl_check_unit_atom, 10058c2ecf20Sopenharmony_ci .set_floor_freq = NULL, 10068c2ecf20Sopenharmony_ci .compute_time_window = rapl_compute_time_window_atom, 10078c2ecf20Sopenharmony_ci}; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_cistatic const struct rapl_defaults rapl_defaults_cht = { 10108c2ecf20Sopenharmony_ci .floor_freq_reg_addr = 0, 10118c2ecf20Sopenharmony_ci .check_unit = rapl_check_unit_atom, 10128c2ecf20Sopenharmony_ci .set_floor_freq = NULL, 10138c2ecf20Sopenharmony_ci .compute_time_window = rapl_compute_time_window_atom, 10148c2ecf20Sopenharmony_ci}; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_cistatic const struct x86_cpu_id rapl_ids[] __initconst = { 10178c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(SANDYBRIDGE, &rapl_defaults_core), 10188c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(SANDYBRIDGE_X, &rapl_defaults_core), 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(IVYBRIDGE, &rapl_defaults_core), 10218c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(IVYBRIDGE_X, &rapl_defaults_core), 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(HASWELL, &rapl_defaults_core), 10248c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(HASWELL_L, &rapl_defaults_core), 10258c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(HASWELL_G, &rapl_defaults_core), 10268c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(HASWELL_X, &rapl_defaults_hsw_server), 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(BROADWELL, &rapl_defaults_core), 10298c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_G, &rapl_defaults_core), 10308c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_D, &rapl_defaults_core), 10318c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_X, &rapl_defaults_hsw_server), 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE, &rapl_defaults_core), 10348c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_L, &rapl_defaults_core), 10358c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_X, &rapl_defaults_hsw_server), 10368c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE_L, &rapl_defaults_core), 10378c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE, &rapl_defaults_core), 10388c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(CANNONLAKE_L, &rapl_defaults_core), 10398c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_L, &rapl_defaults_core), 10408c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ICELAKE, &rapl_defaults_core), 10418c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_NNPI, &rapl_defaults_core), 10428c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_X, &rapl_defaults_hsw_server), 10438c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_D, &rapl_defaults_hsw_server), 10448c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE_L, &rapl_defaults_core), 10458c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE, &rapl_defaults_core), 10468c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE_L, &rapl_defaults_core), 10478c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE, &rapl_defaults_core), 10488c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ROCKETLAKE, &rapl_defaults_core), 10498c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, &rapl_defaults_core), 10508c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(SAPPHIRERAPIDS_X, &rapl_defaults_spr_server), 10518c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(LAKEFIELD, &rapl_defaults_core), 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_SILVERMONT, &rapl_defaults_byt), 10548c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_AIRMONT, &rapl_defaults_cht), 10558c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_SILVERMONT_MID, &rapl_defaults_tng), 10568c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_AIRMONT_MID, &rapl_defaults_ann), 10578c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT, &rapl_defaults_core), 10588c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT_PLUS, &rapl_defaults_core), 10598c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT_D, &rapl_defaults_core), 10608c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT, &rapl_defaults_core), 10618c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_D, &rapl_defaults_core), 10628c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_L, &rapl_defaults_core), 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(XEON_PHI_KNL, &rapl_defaults_hsw_server), 10658c2ecf20Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(XEON_PHI_KNM, &rapl_defaults_hsw_server), 10668c2ecf20Sopenharmony_ci {} 10678c2ecf20Sopenharmony_ci}; 10688c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(x86cpu, rapl_ids); 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci/* Read once for all raw primitive data for domains */ 10718c2ecf20Sopenharmony_cistatic void rapl_update_domain_data(struct rapl_package *rp) 10728c2ecf20Sopenharmony_ci{ 10738c2ecf20Sopenharmony_ci int dmn, prim; 10748c2ecf20Sopenharmony_ci u64 val; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci for (dmn = 0; dmn < rp->nr_domains; dmn++) { 10778c2ecf20Sopenharmony_ci pr_debug("update %s domain %s data\n", rp->name, 10788c2ecf20Sopenharmony_ci rp->domains[dmn].name); 10798c2ecf20Sopenharmony_ci /* exclude non-raw primitives */ 10808c2ecf20Sopenharmony_ci for (prim = 0; prim < NR_RAW_PRIMITIVES; prim++) { 10818c2ecf20Sopenharmony_ci if (!rapl_read_data_raw(&rp->domains[dmn], prim, 10828c2ecf20Sopenharmony_ci rpi[prim].unit, &val)) 10838c2ecf20Sopenharmony_ci rp->domains[dmn].rdd.primitives[prim] = val; 10848c2ecf20Sopenharmony_ci } 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci} 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_cistatic int rapl_package_register_powercap(struct rapl_package *rp) 10908c2ecf20Sopenharmony_ci{ 10918c2ecf20Sopenharmony_ci struct rapl_domain *rd; 10928c2ecf20Sopenharmony_ci struct powercap_zone *power_zone = NULL; 10938c2ecf20Sopenharmony_ci int nr_pl, ret; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci /* Update the domain data of the new package */ 10968c2ecf20Sopenharmony_ci rapl_update_domain_data(rp); 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci /* first we register package domain as the parent zone */ 10998c2ecf20Sopenharmony_ci for (rd = rp->domains; rd < rp->domains + rp->nr_domains; rd++) { 11008c2ecf20Sopenharmony_ci if (rd->id == RAPL_DOMAIN_PACKAGE) { 11018c2ecf20Sopenharmony_ci nr_pl = find_nr_power_limit(rd); 11028c2ecf20Sopenharmony_ci pr_debug("register package domain %s\n", rp->name); 11038c2ecf20Sopenharmony_ci power_zone = powercap_register_zone(&rd->power_zone, 11048c2ecf20Sopenharmony_ci rp->priv->control_type, rp->name, 11058c2ecf20Sopenharmony_ci NULL, &zone_ops[rd->id], nr_pl, 11068c2ecf20Sopenharmony_ci &constraint_ops); 11078c2ecf20Sopenharmony_ci if (IS_ERR(power_zone)) { 11088c2ecf20Sopenharmony_ci pr_debug("failed to register power zone %s\n", 11098c2ecf20Sopenharmony_ci rp->name); 11108c2ecf20Sopenharmony_ci return PTR_ERR(power_zone); 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci /* track parent zone in per package/socket data */ 11138c2ecf20Sopenharmony_ci rp->power_zone = power_zone; 11148c2ecf20Sopenharmony_ci /* done, only one package domain per socket */ 11158c2ecf20Sopenharmony_ci break; 11168c2ecf20Sopenharmony_ci } 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci if (!power_zone) { 11198c2ecf20Sopenharmony_ci pr_err("no package domain found, unknown topology!\n"); 11208c2ecf20Sopenharmony_ci return -ENODEV; 11218c2ecf20Sopenharmony_ci } 11228c2ecf20Sopenharmony_ci /* now register domains as children of the socket/package */ 11238c2ecf20Sopenharmony_ci for (rd = rp->domains; rd < rp->domains + rp->nr_domains; rd++) { 11248c2ecf20Sopenharmony_ci struct powercap_zone *parent = rp->power_zone; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci if (rd->id == RAPL_DOMAIN_PACKAGE) 11278c2ecf20Sopenharmony_ci continue; 11288c2ecf20Sopenharmony_ci if (rd->id == RAPL_DOMAIN_PLATFORM) 11298c2ecf20Sopenharmony_ci parent = NULL; 11308c2ecf20Sopenharmony_ci /* number of power limits per domain varies */ 11318c2ecf20Sopenharmony_ci nr_pl = find_nr_power_limit(rd); 11328c2ecf20Sopenharmony_ci power_zone = powercap_register_zone(&rd->power_zone, 11338c2ecf20Sopenharmony_ci rp->priv->control_type, 11348c2ecf20Sopenharmony_ci rd->name, parent, 11358c2ecf20Sopenharmony_ci &zone_ops[rd->id], nr_pl, 11368c2ecf20Sopenharmony_ci &constraint_ops); 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci if (IS_ERR(power_zone)) { 11398c2ecf20Sopenharmony_ci pr_debug("failed to register power_zone, %s:%s\n", 11408c2ecf20Sopenharmony_ci rp->name, rd->name); 11418c2ecf20Sopenharmony_ci ret = PTR_ERR(power_zone); 11428c2ecf20Sopenharmony_ci goto err_cleanup; 11438c2ecf20Sopenharmony_ci } 11448c2ecf20Sopenharmony_ci } 11458c2ecf20Sopenharmony_ci return 0; 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_cierr_cleanup: 11488c2ecf20Sopenharmony_ci /* 11498c2ecf20Sopenharmony_ci * Clean up previously initialized domains within the package if we 11508c2ecf20Sopenharmony_ci * failed after the first domain setup. 11518c2ecf20Sopenharmony_ci */ 11528c2ecf20Sopenharmony_ci while (--rd >= rp->domains) { 11538c2ecf20Sopenharmony_ci pr_debug("unregister %s domain %s\n", rp->name, rd->name); 11548c2ecf20Sopenharmony_ci powercap_unregister_zone(rp->priv->control_type, 11558c2ecf20Sopenharmony_ci &rd->power_zone); 11568c2ecf20Sopenharmony_ci } 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci return ret; 11598c2ecf20Sopenharmony_ci} 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_cistatic int rapl_check_domain(int cpu, int domain, struct rapl_package *rp) 11628c2ecf20Sopenharmony_ci{ 11638c2ecf20Sopenharmony_ci struct reg_action ra; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci switch (domain) { 11668c2ecf20Sopenharmony_ci case RAPL_DOMAIN_PACKAGE: 11678c2ecf20Sopenharmony_ci case RAPL_DOMAIN_PP0: 11688c2ecf20Sopenharmony_ci case RAPL_DOMAIN_PP1: 11698c2ecf20Sopenharmony_ci case RAPL_DOMAIN_DRAM: 11708c2ecf20Sopenharmony_ci case RAPL_DOMAIN_PLATFORM: 11718c2ecf20Sopenharmony_ci ra.reg = rp->priv->regs[domain][RAPL_DOMAIN_REG_STATUS]; 11728c2ecf20Sopenharmony_ci break; 11738c2ecf20Sopenharmony_ci default: 11748c2ecf20Sopenharmony_ci pr_err("invalid domain id %d\n", domain); 11758c2ecf20Sopenharmony_ci return -EINVAL; 11768c2ecf20Sopenharmony_ci } 11778c2ecf20Sopenharmony_ci /* make sure domain counters are available and contains non-zero 11788c2ecf20Sopenharmony_ci * values, otherwise skip it. 11798c2ecf20Sopenharmony_ci */ 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci ra.mask = ENERGY_STATUS_MASK; 11828c2ecf20Sopenharmony_ci if (rp->priv->read_raw(cpu, &ra) || !ra.value) 11838c2ecf20Sopenharmony_ci return -ENODEV; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci return 0; 11868c2ecf20Sopenharmony_ci} 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci/* 11898c2ecf20Sopenharmony_ci * Check if power limits are available. Two cases when they are not available: 11908c2ecf20Sopenharmony_ci * 1. Locked by BIOS, in this case we still provide read-only access so that 11918c2ecf20Sopenharmony_ci * users can see what limit is set by the BIOS. 11928c2ecf20Sopenharmony_ci * 2. Some CPUs make some domains monitoring only which means PLx MSRs may not 11938c2ecf20Sopenharmony_ci * exist at all. In this case, we do not show the constraints in powercap. 11948c2ecf20Sopenharmony_ci * 11958c2ecf20Sopenharmony_ci * Called after domains are detected and initialized. 11968c2ecf20Sopenharmony_ci */ 11978c2ecf20Sopenharmony_cistatic void rapl_detect_powerlimit(struct rapl_domain *rd) 11988c2ecf20Sopenharmony_ci{ 11998c2ecf20Sopenharmony_ci u64 val64; 12008c2ecf20Sopenharmony_ci int i; 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci /* check if the domain is locked by BIOS, ignore if MSR doesn't exist */ 12038c2ecf20Sopenharmony_ci if (!rapl_read_data_raw(rd, FW_LOCK, false, &val64)) { 12048c2ecf20Sopenharmony_ci if (val64) { 12058c2ecf20Sopenharmony_ci pr_info("RAPL %s domain %s locked by BIOS\n", 12068c2ecf20Sopenharmony_ci rd->rp->name, rd->name); 12078c2ecf20Sopenharmony_ci rd->state |= DOMAIN_STATE_BIOS_LOCKED; 12088c2ecf20Sopenharmony_ci } 12098c2ecf20Sopenharmony_ci } 12108c2ecf20Sopenharmony_ci /* check if power limit MSR exists, otherwise domain is monitoring only */ 12118c2ecf20Sopenharmony_ci for (i = 0; i < NR_POWER_LIMITS; i++) { 12128c2ecf20Sopenharmony_ci int prim = rd->rpl[i].prim_id; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci if (rapl_read_data_raw(rd, prim, false, &val64)) 12158c2ecf20Sopenharmony_ci rd->rpl[i].name = NULL; 12168c2ecf20Sopenharmony_ci } 12178c2ecf20Sopenharmony_ci} 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci/* Detect active and valid domains for the given CPU, caller must 12208c2ecf20Sopenharmony_ci * ensure the CPU belongs to the targeted package and CPU hotlug is disabled. 12218c2ecf20Sopenharmony_ci */ 12228c2ecf20Sopenharmony_cistatic int rapl_detect_domains(struct rapl_package *rp, int cpu) 12238c2ecf20Sopenharmony_ci{ 12248c2ecf20Sopenharmony_ci struct rapl_domain *rd; 12258c2ecf20Sopenharmony_ci int i; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci for (i = 0; i < RAPL_DOMAIN_MAX; i++) { 12288c2ecf20Sopenharmony_ci /* use physical package id to read counters */ 12298c2ecf20Sopenharmony_ci if (!rapl_check_domain(cpu, i, rp)) { 12308c2ecf20Sopenharmony_ci rp->domain_map |= 1 << i; 12318c2ecf20Sopenharmony_ci pr_info("Found RAPL domain %s\n", rapl_domain_names[i]); 12328c2ecf20Sopenharmony_ci } 12338c2ecf20Sopenharmony_ci } 12348c2ecf20Sopenharmony_ci rp->nr_domains = bitmap_weight(&rp->domain_map, RAPL_DOMAIN_MAX); 12358c2ecf20Sopenharmony_ci if (!rp->nr_domains) { 12368c2ecf20Sopenharmony_ci pr_debug("no valid rapl domains found in %s\n", rp->name); 12378c2ecf20Sopenharmony_ci return -ENODEV; 12388c2ecf20Sopenharmony_ci } 12398c2ecf20Sopenharmony_ci pr_debug("found %d domains on %s\n", rp->nr_domains, rp->name); 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci rp->domains = kcalloc(rp->nr_domains + 1, sizeof(struct rapl_domain), 12428c2ecf20Sopenharmony_ci GFP_KERNEL); 12438c2ecf20Sopenharmony_ci if (!rp->domains) 12448c2ecf20Sopenharmony_ci return -ENOMEM; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci rapl_init_domains(rp); 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci for (rd = rp->domains; rd < rp->domains + rp->nr_domains; rd++) 12498c2ecf20Sopenharmony_ci rapl_detect_powerlimit(rd); 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci return 0; 12528c2ecf20Sopenharmony_ci} 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci/* called from CPU hotplug notifier, hotplug lock held */ 12558c2ecf20Sopenharmony_civoid rapl_remove_package(struct rapl_package *rp) 12568c2ecf20Sopenharmony_ci{ 12578c2ecf20Sopenharmony_ci struct rapl_domain *rd, *rd_package = NULL; 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci package_power_limit_irq_restore(rp); 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci for (rd = rp->domains; rd < rp->domains + rp->nr_domains; rd++) { 12628c2ecf20Sopenharmony_ci rapl_write_data_raw(rd, PL1_ENABLE, 0); 12638c2ecf20Sopenharmony_ci rapl_write_data_raw(rd, PL1_CLAMP, 0); 12648c2ecf20Sopenharmony_ci if (find_nr_power_limit(rd) > 1) { 12658c2ecf20Sopenharmony_ci rapl_write_data_raw(rd, PL2_ENABLE, 0); 12668c2ecf20Sopenharmony_ci rapl_write_data_raw(rd, PL2_CLAMP, 0); 12678c2ecf20Sopenharmony_ci rapl_write_data_raw(rd, PL4_ENABLE, 0); 12688c2ecf20Sopenharmony_ci } 12698c2ecf20Sopenharmony_ci if (rd->id == RAPL_DOMAIN_PACKAGE) { 12708c2ecf20Sopenharmony_ci rd_package = rd; 12718c2ecf20Sopenharmony_ci continue; 12728c2ecf20Sopenharmony_ci } 12738c2ecf20Sopenharmony_ci pr_debug("remove package, undo power limit on %s: %s\n", 12748c2ecf20Sopenharmony_ci rp->name, rd->name); 12758c2ecf20Sopenharmony_ci powercap_unregister_zone(rp->priv->control_type, 12768c2ecf20Sopenharmony_ci &rd->power_zone); 12778c2ecf20Sopenharmony_ci } 12788c2ecf20Sopenharmony_ci /* do parent zone last */ 12798c2ecf20Sopenharmony_ci powercap_unregister_zone(rp->priv->control_type, 12808c2ecf20Sopenharmony_ci &rd_package->power_zone); 12818c2ecf20Sopenharmony_ci list_del(&rp->plist); 12828c2ecf20Sopenharmony_ci kfree(rp); 12838c2ecf20Sopenharmony_ci} 12848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rapl_remove_package); 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci/* caller to ensure CPU hotplug lock is held */ 12878c2ecf20Sopenharmony_cistruct rapl_package *rapl_find_package_domain(int cpu, struct rapl_if_priv *priv) 12888c2ecf20Sopenharmony_ci{ 12898c2ecf20Sopenharmony_ci int id = topology_logical_die_id(cpu); 12908c2ecf20Sopenharmony_ci struct rapl_package *rp; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci list_for_each_entry(rp, &rapl_packages, plist) { 12938c2ecf20Sopenharmony_ci if (rp->id == id 12948c2ecf20Sopenharmony_ci && rp->priv->control_type == priv->control_type) 12958c2ecf20Sopenharmony_ci return rp; 12968c2ecf20Sopenharmony_ci } 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci return NULL; 12998c2ecf20Sopenharmony_ci} 13008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rapl_find_package_domain); 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci/* called from CPU hotplug notifier, hotplug lock held */ 13038c2ecf20Sopenharmony_cistruct rapl_package *rapl_add_package(int cpu, struct rapl_if_priv *priv) 13048c2ecf20Sopenharmony_ci{ 13058c2ecf20Sopenharmony_ci int id = topology_logical_die_id(cpu); 13068c2ecf20Sopenharmony_ci struct rapl_package *rp; 13078c2ecf20Sopenharmony_ci struct cpuinfo_x86 *c = &cpu_data(cpu); 13088c2ecf20Sopenharmony_ci int ret; 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci if (!rapl_defaults) 13118c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci rp = kzalloc(sizeof(struct rapl_package), GFP_KERNEL); 13148c2ecf20Sopenharmony_ci if (!rp) 13158c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci /* add the new package to the list */ 13188c2ecf20Sopenharmony_ci rp->id = id; 13198c2ecf20Sopenharmony_ci rp->lead_cpu = cpu; 13208c2ecf20Sopenharmony_ci rp->priv = priv; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci if (topology_max_die_per_package() > 1) 13238c2ecf20Sopenharmony_ci snprintf(rp->name, PACKAGE_DOMAIN_NAME_LENGTH, 13248c2ecf20Sopenharmony_ci "package-%d-die-%d", c->phys_proc_id, c->cpu_die_id); 13258c2ecf20Sopenharmony_ci else 13268c2ecf20Sopenharmony_ci snprintf(rp->name, PACKAGE_DOMAIN_NAME_LENGTH, "package-%d", 13278c2ecf20Sopenharmony_ci c->phys_proc_id); 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci /* check if the package contains valid domains */ 13308c2ecf20Sopenharmony_ci if (rapl_detect_domains(rp, cpu) || rapl_defaults->check_unit(rp, cpu)) { 13318c2ecf20Sopenharmony_ci ret = -ENODEV; 13328c2ecf20Sopenharmony_ci goto err_free_package; 13338c2ecf20Sopenharmony_ci } 13348c2ecf20Sopenharmony_ci ret = rapl_package_register_powercap(rp); 13358c2ecf20Sopenharmony_ci if (!ret) { 13368c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rp->plist); 13378c2ecf20Sopenharmony_ci list_add(&rp->plist, &rapl_packages); 13388c2ecf20Sopenharmony_ci return rp; 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_cierr_free_package: 13428c2ecf20Sopenharmony_ci kfree(rp->domains); 13438c2ecf20Sopenharmony_ci kfree(rp); 13448c2ecf20Sopenharmony_ci return ERR_PTR(ret); 13458c2ecf20Sopenharmony_ci} 13468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rapl_add_package); 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_cistatic void power_limit_state_save(void) 13498c2ecf20Sopenharmony_ci{ 13508c2ecf20Sopenharmony_ci struct rapl_package *rp; 13518c2ecf20Sopenharmony_ci struct rapl_domain *rd; 13528c2ecf20Sopenharmony_ci int nr_pl, ret, i; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci get_online_cpus(); 13558c2ecf20Sopenharmony_ci list_for_each_entry(rp, &rapl_packages, plist) { 13568c2ecf20Sopenharmony_ci if (!rp->power_zone) 13578c2ecf20Sopenharmony_ci continue; 13588c2ecf20Sopenharmony_ci rd = power_zone_to_rapl_domain(rp->power_zone); 13598c2ecf20Sopenharmony_ci nr_pl = find_nr_power_limit(rd); 13608c2ecf20Sopenharmony_ci for (i = 0; i < nr_pl; i++) { 13618c2ecf20Sopenharmony_ci switch (rd->rpl[i].prim_id) { 13628c2ecf20Sopenharmony_ci case PL1_ENABLE: 13638c2ecf20Sopenharmony_ci ret = rapl_read_data_raw(rd, 13648c2ecf20Sopenharmony_ci POWER_LIMIT1, true, 13658c2ecf20Sopenharmony_ci &rd->rpl[i].last_power_limit); 13668c2ecf20Sopenharmony_ci if (ret) 13678c2ecf20Sopenharmony_ci rd->rpl[i].last_power_limit = 0; 13688c2ecf20Sopenharmony_ci break; 13698c2ecf20Sopenharmony_ci case PL2_ENABLE: 13708c2ecf20Sopenharmony_ci ret = rapl_read_data_raw(rd, 13718c2ecf20Sopenharmony_ci POWER_LIMIT2, true, 13728c2ecf20Sopenharmony_ci &rd->rpl[i].last_power_limit); 13738c2ecf20Sopenharmony_ci if (ret) 13748c2ecf20Sopenharmony_ci rd->rpl[i].last_power_limit = 0; 13758c2ecf20Sopenharmony_ci break; 13768c2ecf20Sopenharmony_ci case PL4_ENABLE: 13778c2ecf20Sopenharmony_ci ret = rapl_read_data_raw(rd, 13788c2ecf20Sopenharmony_ci POWER_LIMIT4, true, 13798c2ecf20Sopenharmony_ci &rd->rpl[i].last_power_limit); 13808c2ecf20Sopenharmony_ci if (ret) 13818c2ecf20Sopenharmony_ci rd->rpl[i].last_power_limit = 0; 13828c2ecf20Sopenharmony_ci break; 13838c2ecf20Sopenharmony_ci } 13848c2ecf20Sopenharmony_ci } 13858c2ecf20Sopenharmony_ci } 13868c2ecf20Sopenharmony_ci put_online_cpus(); 13878c2ecf20Sopenharmony_ci} 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_cistatic void power_limit_state_restore(void) 13908c2ecf20Sopenharmony_ci{ 13918c2ecf20Sopenharmony_ci struct rapl_package *rp; 13928c2ecf20Sopenharmony_ci struct rapl_domain *rd; 13938c2ecf20Sopenharmony_ci int nr_pl, i; 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci get_online_cpus(); 13968c2ecf20Sopenharmony_ci list_for_each_entry(rp, &rapl_packages, plist) { 13978c2ecf20Sopenharmony_ci if (!rp->power_zone) 13988c2ecf20Sopenharmony_ci continue; 13998c2ecf20Sopenharmony_ci rd = power_zone_to_rapl_domain(rp->power_zone); 14008c2ecf20Sopenharmony_ci nr_pl = find_nr_power_limit(rd); 14018c2ecf20Sopenharmony_ci for (i = 0; i < nr_pl; i++) { 14028c2ecf20Sopenharmony_ci switch (rd->rpl[i].prim_id) { 14038c2ecf20Sopenharmony_ci case PL1_ENABLE: 14048c2ecf20Sopenharmony_ci if (rd->rpl[i].last_power_limit) 14058c2ecf20Sopenharmony_ci rapl_write_data_raw(rd, POWER_LIMIT1, 14068c2ecf20Sopenharmony_ci rd->rpl[i].last_power_limit); 14078c2ecf20Sopenharmony_ci break; 14088c2ecf20Sopenharmony_ci case PL2_ENABLE: 14098c2ecf20Sopenharmony_ci if (rd->rpl[i].last_power_limit) 14108c2ecf20Sopenharmony_ci rapl_write_data_raw(rd, POWER_LIMIT2, 14118c2ecf20Sopenharmony_ci rd->rpl[i].last_power_limit); 14128c2ecf20Sopenharmony_ci break; 14138c2ecf20Sopenharmony_ci case PL4_ENABLE: 14148c2ecf20Sopenharmony_ci if (rd->rpl[i].last_power_limit) 14158c2ecf20Sopenharmony_ci rapl_write_data_raw(rd, POWER_LIMIT4, 14168c2ecf20Sopenharmony_ci rd->rpl[i].last_power_limit); 14178c2ecf20Sopenharmony_ci break; 14188c2ecf20Sopenharmony_ci } 14198c2ecf20Sopenharmony_ci } 14208c2ecf20Sopenharmony_ci } 14218c2ecf20Sopenharmony_ci put_online_cpus(); 14228c2ecf20Sopenharmony_ci} 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_cistatic int rapl_pm_callback(struct notifier_block *nb, 14258c2ecf20Sopenharmony_ci unsigned long mode, void *_unused) 14268c2ecf20Sopenharmony_ci{ 14278c2ecf20Sopenharmony_ci switch (mode) { 14288c2ecf20Sopenharmony_ci case PM_SUSPEND_PREPARE: 14298c2ecf20Sopenharmony_ci power_limit_state_save(); 14308c2ecf20Sopenharmony_ci break; 14318c2ecf20Sopenharmony_ci case PM_POST_SUSPEND: 14328c2ecf20Sopenharmony_ci power_limit_state_restore(); 14338c2ecf20Sopenharmony_ci break; 14348c2ecf20Sopenharmony_ci } 14358c2ecf20Sopenharmony_ci return NOTIFY_OK; 14368c2ecf20Sopenharmony_ci} 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_cistatic struct notifier_block rapl_pm_notifier = { 14398c2ecf20Sopenharmony_ci .notifier_call = rapl_pm_callback, 14408c2ecf20Sopenharmony_ci}; 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_cistatic struct platform_device *rapl_msr_platdev; 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_cistatic int __init rapl_init(void) 14458c2ecf20Sopenharmony_ci{ 14468c2ecf20Sopenharmony_ci const struct x86_cpu_id *id; 14478c2ecf20Sopenharmony_ci int ret; 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci id = x86_match_cpu(rapl_ids); 14508c2ecf20Sopenharmony_ci if (!id) { 14518c2ecf20Sopenharmony_ci pr_err("driver does not support CPU family %d model %d\n", 14528c2ecf20Sopenharmony_ci boot_cpu_data.x86, boot_cpu_data.x86_model); 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci return -ENODEV; 14558c2ecf20Sopenharmony_ci } 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci rapl_defaults = (struct rapl_defaults *)id->driver_data; 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci ret = register_pm_notifier(&rapl_pm_notifier); 14608c2ecf20Sopenharmony_ci if (ret) 14618c2ecf20Sopenharmony_ci return ret; 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci rapl_msr_platdev = platform_device_alloc("intel_rapl_msr", 0); 14648c2ecf20Sopenharmony_ci if (!rapl_msr_platdev) { 14658c2ecf20Sopenharmony_ci ret = -ENOMEM; 14668c2ecf20Sopenharmony_ci goto end; 14678c2ecf20Sopenharmony_ci } 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci ret = platform_device_add(rapl_msr_platdev); 14708c2ecf20Sopenharmony_ci if (ret) 14718c2ecf20Sopenharmony_ci platform_device_put(rapl_msr_platdev); 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ciend: 14748c2ecf20Sopenharmony_ci if (ret) 14758c2ecf20Sopenharmony_ci unregister_pm_notifier(&rapl_pm_notifier); 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci return ret; 14788c2ecf20Sopenharmony_ci} 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_cistatic void __exit rapl_exit(void) 14818c2ecf20Sopenharmony_ci{ 14828c2ecf20Sopenharmony_ci platform_device_unregister(rapl_msr_platdev); 14838c2ecf20Sopenharmony_ci unregister_pm_notifier(&rapl_pm_notifier); 14848c2ecf20Sopenharmony_ci} 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_cifs_initcall(rapl_init); 14878c2ecf20Sopenharmony_cimodule_exit(rapl_exit); 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel Runtime Average Power Limit (RAPL) common code"); 14908c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com>"); 14918c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1492