162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Common code for Intel Running Average Power Limit (RAPL) support. 462306a36Sopenharmony_ci * Copyright (c) 2019, Intel Corporation. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/cleanup.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/list.h> 1262306a36Sopenharmony_ci#include <linux/types.h> 1362306a36Sopenharmony_ci#include <linux/device.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/log2.h> 1662306a36Sopenharmony_ci#include <linux/bitmap.h> 1762306a36Sopenharmony_ci#include <linux/delay.h> 1862306a36Sopenharmony_ci#include <linux/sysfs.h> 1962306a36Sopenharmony_ci#include <linux/cpu.h> 2062306a36Sopenharmony_ci#include <linux/powercap.h> 2162306a36Sopenharmony_ci#include <linux/suspend.h> 2262306a36Sopenharmony_ci#include <linux/intel_rapl.h> 2362306a36Sopenharmony_ci#include <linux/processor.h> 2462306a36Sopenharmony_ci#include <linux/platform_device.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <asm/iosf_mbi.h> 2762306a36Sopenharmony_ci#include <asm/cpu_device_id.h> 2862306a36Sopenharmony_ci#include <asm/intel-family.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* bitmasks for RAPL MSRs, used by primitive access functions */ 3162306a36Sopenharmony_ci#define ENERGY_STATUS_MASK 0xffffffff 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define POWER_LIMIT1_MASK 0x7FFF 3462306a36Sopenharmony_ci#define POWER_LIMIT1_ENABLE BIT(15) 3562306a36Sopenharmony_ci#define POWER_LIMIT1_CLAMP BIT(16) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define POWER_LIMIT2_MASK (0x7FFFULL<<32) 3862306a36Sopenharmony_ci#define POWER_LIMIT2_ENABLE BIT_ULL(47) 3962306a36Sopenharmony_ci#define POWER_LIMIT2_CLAMP BIT_ULL(48) 4062306a36Sopenharmony_ci#define POWER_HIGH_LOCK BIT_ULL(63) 4162306a36Sopenharmony_ci#define POWER_LOW_LOCK BIT(31) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define POWER_LIMIT4_MASK 0x1FFF 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define TIME_WINDOW1_MASK (0x7FULL<<17) 4662306a36Sopenharmony_ci#define TIME_WINDOW2_MASK (0x7FULL<<49) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define POWER_UNIT_OFFSET 0 4962306a36Sopenharmony_ci#define POWER_UNIT_MASK 0x0F 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define ENERGY_UNIT_OFFSET 0x08 5262306a36Sopenharmony_ci#define ENERGY_UNIT_MASK 0x1F00 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define TIME_UNIT_OFFSET 0x10 5562306a36Sopenharmony_ci#define TIME_UNIT_MASK 0xF0000 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define POWER_INFO_MAX_MASK (0x7fffULL<<32) 5862306a36Sopenharmony_ci#define POWER_INFO_MIN_MASK (0x7fffULL<<16) 5962306a36Sopenharmony_ci#define POWER_INFO_MAX_TIME_WIN_MASK (0x3fULL<<48) 6062306a36Sopenharmony_ci#define POWER_INFO_THERMAL_SPEC_MASK 0x7fff 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define PERF_STATUS_THROTTLE_TIME_MASK 0xffffffff 6362306a36Sopenharmony_ci#define PP_POLICY_MASK 0x1F 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* 6662306a36Sopenharmony_ci * SPR has different layout for Psys Domain PowerLimit registers. 6762306a36Sopenharmony_ci * There are 17 bits of PL1 and PL2 instead of 15 bits. 6862306a36Sopenharmony_ci * The Enable bits and TimeWindow bits are also shifted as a result. 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ci#define PSYS_POWER_LIMIT1_MASK 0x1FFFF 7162306a36Sopenharmony_ci#define PSYS_POWER_LIMIT1_ENABLE BIT(17) 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define PSYS_POWER_LIMIT2_MASK (0x1FFFFULL<<32) 7462306a36Sopenharmony_ci#define PSYS_POWER_LIMIT2_ENABLE BIT_ULL(49) 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define PSYS_TIME_WINDOW1_MASK (0x7FULL<<19) 7762306a36Sopenharmony_ci#define PSYS_TIME_WINDOW2_MASK (0x7FULL<<51) 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* bitmasks for RAPL TPMI, used by primitive access functions */ 8062306a36Sopenharmony_ci#define TPMI_POWER_LIMIT_MASK 0x3FFFF 8162306a36Sopenharmony_ci#define TPMI_POWER_LIMIT_ENABLE BIT_ULL(62) 8262306a36Sopenharmony_ci#define TPMI_TIME_WINDOW_MASK (0x7FULL<<18) 8362306a36Sopenharmony_ci#define TPMI_INFO_SPEC_MASK 0x3FFFF 8462306a36Sopenharmony_ci#define TPMI_INFO_MIN_MASK (0x3FFFFULL << 18) 8562306a36Sopenharmony_ci#define TPMI_INFO_MAX_MASK (0x3FFFFULL << 36) 8662306a36Sopenharmony_ci#define TPMI_INFO_MAX_TIME_WIN_MASK (0x7FULL << 54) 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* Non HW constants */ 8962306a36Sopenharmony_ci#define RAPL_PRIMITIVE_DERIVED BIT(1) /* not from raw data */ 9062306a36Sopenharmony_ci#define RAPL_PRIMITIVE_DUMMY BIT(2) 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#define TIME_WINDOW_MAX_MSEC 40000 9362306a36Sopenharmony_ci#define TIME_WINDOW_MIN_MSEC 250 9462306a36Sopenharmony_ci#define ENERGY_UNIT_SCALE 1000 /* scale from driver unit to powercap unit */ 9562306a36Sopenharmony_cienum unit_type { 9662306a36Sopenharmony_ci ARBITRARY_UNIT, /* no translation */ 9762306a36Sopenharmony_ci POWER_UNIT, 9862306a36Sopenharmony_ci ENERGY_UNIT, 9962306a36Sopenharmony_ci TIME_UNIT, 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* per domain data, some are optional */ 10362306a36Sopenharmony_ci#define NR_RAW_PRIMITIVES (NR_RAPL_PRIMITIVES - 2) 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci#define DOMAIN_STATE_INACTIVE BIT(0) 10662306a36Sopenharmony_ci#define DOMAIN_STATE_POWER_LIMIT_SET BIT(1) 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic const char *pl_names[NR_POWER_LIMITS] = { 10962306a36Sopenharmony_ci [POWER_LIMIT1] = "long_term", 11062306a36Sopenharmony_ci [POWER_LIMIT2] = "short_term", 11162306a36Sopenharmony_ci [POWER_LIMIT4] = "peak_power", 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cienum pl_prims { 11562306a36Sopenharmony_ci PL_ENABLE, 11662306a36Sopenharmony_ci PL_CLAMP, 11762306a36Sopenharmony_ci PL_LIMIT, 11862306a36Sopenharmony_ci PL_TIME_WINDOW, 11962306a36Sopenharmony_ci PL_MAX_POWER, 12062306a36Sopenharmony_ci PL_LOCK, 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic bool is_pl_valid(struct rapl_domain *rd, int pl) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci if (pl < POWER_LIMIT1 || pl > POWER_LIMIT4) 12662306a36Sopenharmony_ci return false; 12762306a36Sopenharmony_ci return rd->rpl[pl].name ? true : false; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic int get_pl_lock_prim(struct rapl_domain *rd, int pl) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci if (rd->rp->priv->type == RAPL_IF_TPMI) { 13362306a36Sopenharmony_ci if (pl == POWER_LIMIT1) 13462306a36Sopenharmony_ci return PL1_LOCK; 13562306a36Sopenharmony_ci if (pl == POWER_LIMIT2) 13662306a36Sopenharmony_ci return PL2_LOCK; 13762306a36Sopenharmony_ci if (pl == POWER_LIMIT4) 13862306a36Sopenharmony_ci return PL4_LOCK; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* MSR/MMIO Interface doesn't have Lock bit for PL4 */ 14262306a36Sopenharmony_ci if (pl == POWER_LIMIT4) 14362306a36Sopenharmony_ci return -EINVAL; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci /* 14662306a36Sopenharmony_ci * Power Limit register that supports two power limits has a different 14762306a36Sopenharmony_ci * bit position for the Lock bit. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_ci if (rd->rp->priv->limits[rd->id] & BIT(POWER_LIMIT2)) 15062306a36Sopenharmony_ci return FW_HIGH_LOCK; 15162306a36Sopenharmony_ci return FW_LOCK; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic int get_pl_prim(struct rapl_domain *rd, int pl, enum pl_prims prim) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci switch (pl) { 15762306a36Sopenharmony_ci case POWER_LIMIT1: 15862306a36Sopenharmony_ci if (prim == PL_ENABLE) 15962306a36Sopenharmony_ci return PL1_ENABLE; 16062306a36Sopenharmony_ci if (prim == PL_CLAMP && rd->rp->priv->type != RAPL_IF_TPMI) 16162306a36Sopenharmony_ci return PL1_CLAMP; 16262306a36Sopenharmony_ci if (prim == PL_LIMIT) 16362306a36Sopenharmony_ci return POWER_LIMIT1; 16462306a36Sopenharmony_ci if (prim == PL_TIME_WINDOW) 16562306a36Sopenharmony_ci return TIME_WINDOW1; 16662306a36Sopenharmony_ci if (prim == PL_MAX_POWER) 16762306a36Sopenharmony_ci return THERMAL_SPEC_POWER; 16862306a36Sopenharmony_ci if (prim == PL_LOCK) 16962306a36Sopenharmony_ci return get_pl_lock_prim(rd, pl); 17062306a36Sopenharmony_ci return -EINVAL; 17162306a36Sopenharmony_ci case POWER_LIMIT2: 17262306a36Sopenharmony_ci if (prim == PL_ENABLE) 17362306a36Sopenharmony_ci return PL2_ENABLE; 17462306a36Sopenharmony_ci if (prim == PL_CLAMP && rd->rp->priv->type != RAPL_IF_TPMI) 17562306a36Sopenharmony_ci return PL2_CLAMP; 17662306a36Sopenharmony_ci if (prim == PL_LIMIT) 17762306a36Sopenharmony_ci return POWER_LIMIT2; 17862306a36Sopenharmony_ci if (prim == PL_TIME_WINDOW) 17962306a36Sopenharmony_ci return TIME_WINDOW2; 18062306a36Sopenharmony_ci if (prim == PL_MAX_POWER) 18162306a36Sopenharmony_ci return MAX_POWER; 18262306a36Sopenharmony_ci if (prim == PL_LOCK) 18362306a36Sopenharmony_ci return get_pl_lock_prim(rd, pl); 18462306a36Sopenharmony_ci return -EINVAL; 18562306a36Sopenharmony_ci case POWER_LIMIT4: 18662306a36Sopenharmony_ci if (prim == PL_LIMIT) 18762306a36Sopenharmony_ci return POWER_LIMIT4; 18862306a36Sopenharmony_ci if (prim == PL_ENABLE) 18962306a36Sopenharmony_ci return PL4_ENABLE; 19062306a36Sopenharmony_ci /* PL4 would be around two times PL2, use same prim as PL2. */ 19162306a36Sopenharmony_ci if (prim == PL_MAX_POWER) 19262306a36Sopenharmony_ci return MAX_POWER; 19362306a36Sopenharmony_ci if (prim == PL_LOCK) 19462306a36Sopenharmony_ci return get_pl_lock_prim(rd, pl); 19562306a36Sopenharmony_ci return -EINVAL; 19662306a36Sopenharmony_ci default: 19762306a36Sopenharmony_ci return -EINVAL; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci#define power_zone_to_rapl_domain(_zone) \ 20262306a36Sopenharmony_ci container_of(_zone, struct rapl_domain, power_zone) 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistruct rapl_defaults { 20562306a36Sopenharmony_ci u8 floor_freq_reg_addr; 20662306a36Sopenharmony_ci int (*check_unit)(struct rapl_domain *rd); 20762306a36Sopenharmony_ci void (*set_floor_freq)(struct rapl_domain *rd, bool mode); 20862306a36Sopenharmony_ci u64 (*compute_time_window)(struct rapl_domain *rd, u64 val, 20962306a36Sopenharmony_ci bool to_raw); 21062306a36Sopenharmony_ci unsigned int dram_domain_energy_unit; 21162306a36Sopenharmony_ci unsigned int psys_domain_energy_unit; 21262306a36Sopenharmony_ci bool spr_psys_bits; 21362306a36Sopenharmony_ci}; 21462306a36Sopenharmony_cistatic struct rapl_defaults *defaults_msr; 21562306a36Sopenharmony_cistatic const struct rapl_defaults defaults_tpmi; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic struct rapl_defaults *get_defaults(struct rapl_package *rp) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci return rp->priv->defaults; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci/* Sideband MBI registers */ 22362306a36Sopenharmony_ci#define IOSF_CPU_POWER_BUDGET_CTL_BYT (0x2) 22462306a36Sopenharmony_ci#define IOSF_CPU_POWER_BUDGET_CTL_TNG (0xdf) 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci#define PACKAGE_PLN_INT_SAVED BIT(0) 22762306a36Sopenharmony_ci#define MAX_PRIM_NAME (32) 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/* per domain data. used to describe individual knobs such that access function 23062306a36Sopenharmony_ci * can be consolidated into one instead of many inline functions. 23162306a36Sopenharmony_ci */ 23262306a36Sopenharmony_cistruct rapl_primitive_info { 23362306a36Sopenharmony_ci const char *name; 23462306a36Sopenharmony_ci u64 mask; 23562306a36Sopenharmony_ci int shift; 23662306a36Sopenharmony_ci enum rapl_domain_reg_id id; 23762306a36Sopenharmony_ci enum unit_type unit; 23862306a36Sopenharmony_ci u32 flag; 23962306a36Sopenharmony_ci}; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci#define PRIMITIVE_INFO_INIT(p, m, s, i, u, f) { \ 24262306a36Sopenharmony_ci .name = #p, \ 24362306a36Sopenharmony_ci .mask = m, \ 24462306a36Sopenharmony_ci .shift = s, \ 24562306a36Sopenharmony_ci .id = i, \ 24662306a36Sopenharmony_ci .unit = u, \ 24762306a36Sopenharmony_ci .flag = f \ 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic void rapl_init_domains(struct rapl_package *rp); 25162306a36Sopenharmony_cistatic int rapl_read_data_raw(struct rapl_domain *rd, 25262306a36Sopenharmony_ci enum rapl_primitives prim, 25362306a36Sopenharmony_ci bool xlate, u64 *data); 25462306a36Sopenharmony_cistatic int rapl_write_data_raw(struct rapl_domain *rd, 25562306a36Sopenharmony_ci enum rapl_primitives prim, 25662306a36Sopenharmony_ci unsigned long long value); 25762306a36Sopenharmony_cistatic int rapl_read_pl_data(struct rapl_domain *rd, int pl, 25862306a36Sopenharmony_ci enum pl_prims pl_prim, 25962306a36Sopenharmony_ci bool xlate, u64 *data); 26062306a36Sopenharmony_cistatic int rapl_write_pl_data(struct rapl_domain *rd, int pl, 26162306a36Sopenharmony_ci enum pl_prims pl_prim, 26262306a36Sopenharmony_ci unsigned long long value); 26362306a36Sopenharmony_cistatic u64 rapl_unit_xlate(struct rapl_domain *rd, 26462306a36Sopenharmony_ci enum unit_type type, u64 value, int to_raw); 26562306a36Sopenharmony_cistatic void package_power_limit_irq_save(struct rapl_package *rp); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic LIST_HEAD(rapl_packages); /* guarded by CPU hotplug lock */ 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic const char *const rapl_domain_names[] = { 27062306a36Sopenharmony_ci "package", 27162306a36Sopenharmony_ci "core", 27262306a36Sopenharmony_ci "uncore", 27362306a36Sopenharmony_ci "dram", 27462306a36Sopenharmony_ci "psys", 27562306a36Sopenharmony_ci}; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic int get_energy_counter(struct powercap_zone *power_zone, 27862306a36Sopenharmony_ci u64 *energy_raw) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct rapl_domain *rd; 28162306a36Sopenharmony_ci u64 energy_now; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* prevent CPU hotplug, make sure the RAPL domain does not go 28462306a36Sopenharmony_ci * away while reading the counter. 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_ci cpus_read_lock(); 28762306a36Sopenharmony_ci rd = power_zone_to_rapl_domain(power_zone); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (!rapl_read_data_raw(rd, ENERGY_COUNTER, true, &energy_now)) { 29062306a36Sopenharmony_ci *energy_raw = energy_now; 29162306a36Sopenharmony_ci cpus_read_unlock(); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return 0; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci cpus_read_unlock(); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci return -EIO; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic int get_max_energy_counter(struct powercap_zone *pcd_dev, u64 *energy) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct rapl_domain *rd = power_zone_to_rapl_domain(pcd_dev); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci *energy = rapl_unit_xlate(rd, ENERGY_UNIT, ENERGY_STATUS_MASK, 0); 30562306a36Sopenharmony_ci return 0; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic int release_zone(struct powercap_zone *power_zone) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct rapl_domain *rd = power_zone_to_rapl_domain(power_zone); 31162306a36Sopenharmony_ci struct rapl_package *rp = rd->rp; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* package zone is the last zone of a package, we can free 31462306a36Sopenharmony_ci * memory here since all children has been unregistered. 31562306a36Sopenharmony_ci */ 31662306a36Sopenharmony_ci if (rd->id == RAPL_DOMAIN_PACKAGE) { 31762306a36Sopenharmony_ci kfree(rd); 31862306a36Sopenharmony_ci rp->domains = NULL; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci return 0; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic int find_nr_power_limit(struct rapl_domain *rd) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci int i, nr_pl = 0; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci for (i = 0; i < NR_POWER_LIMITS; i++) { 33062306a36Sopenharmony_ci if (is_pl_valid(rd, i)) 33162306a36Sopenharmony_ci nr_pl++; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return nr_pl; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic int set_domain_enable(struct powercap_zone *power_zone, bool mode) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct rapl_domain *rd = power_zone_to_rapl_domain(power_zone); 34062306a36Sopenharmony_ci struct rapl_defaults *defaults = get_defaults(rd->rp); 34162306a36Sopenharmony_ci int ret; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci cpus_read_lock(); 34462306a36Sopenharmony_ci ret = rapl_write_pl_data(rd, POWER_LIMIT1, PL_ENABLE, mode); 34562306a36Sopenharmony_ci if (!ret && defaults->set_floor_freq) 34662306a36Sopenharmony_ci defaults->set_floor_freq(rd, mode); 34762306a36Sopenharmony_ci cpus_read_unlock(); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return ret; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic int get_domain_enable(struct powercap_zone *power_zone, bool *mode) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct rapl_domain *rd = power_zone_to_rapl_domain(power_zone); 35562306a36Sopenharmony_ci u64 val; 35662306a36Sopenharmony_ci int ret; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (rd->rpl[POWER_LIMIT1].locked) { 35962306a36Sopenharmony_ci *mode = false; 36062306a36Sopenharmony_ci return 0; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci cpus_read_lock(); 36362306a36Sopenharmony_ci ret = rapl_read_pl_data(rd, POWER_LIMIT1, PL_ENABLE, true, &val); 36462306a36Sopenharmony_ci if (!ret) 36562306a36Sopenharmony_ci *mode = val; 36662306a36Sopenharmony_ci cpus_read_unlock(); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci return ret; 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci/* per RAPL domain ops, in the order of rapl_domain_type */ 37262306a36Sopenharmony_cistatic const struct powercap_zone_ops zone_ops[] = { 37362306a36Sopenharmony_ci /* RAPL_DOMAIN_PACKAGE */ 37462306a36Sopenharmony_ci { 37562306a36Sopenharmony_ci .get_energy_uj = get_energy_counter, 37662306a36Sopenharmony_ci .get_max_energy_range_uj = get_max_energy_counter, 37762306a36Sopenharmony_ci .release = release_zone, 37862306a36Sopenharmony_ci .set_enable = set_domain_enable, 37962306a36Sopenharmony_ci .get_enable = get_domain_enable, 38062306a36Sopenharmony_ci }, 38162306a36Sopenharmony_ci /* RAPL_DOMAIN_PP0 */ 38262306a36Sopenharmony_ci { 38362306a36Sopenharmony_ci .get_energy_uj = get_energy_counter, 38462306a36Sopenharmony_ci .get_max_energy_range_uj = get_max_energy_counter, 38562306a36Sopenharmony_ci .release = release_zone, 38662306a36Sopenharmony_ci .set_enable = set_domain_enable, 38762306a36Sopenharmony_ci .get_enable = get_domain_enable, 38862306a36Sopenharmony_ci }, 38962306a36Sopenharmony_ci /* RAPL_DOMAIN_PP1 */ 39062306a36Sopenharmony_ci { 39162306a36Sopenharmony_ci .get_energy_uj = get_energy_counter, 39262306a36Sopenharmony_ci .get_max_energy_range_uj = get_max_energy_counter, 39362306a36Sopenharmony_ci .release = release_zone, 39462306a36Sopenharmony_ci .set_enable = set_domain_enable, 39562306a36Sopenharmony_ci .get_enable = get_domain_enable, 39662306a36Sopenharmony_ci }, 39762306a36Sopenharmony_ci /* RAPL_DOMAIN_DRAM */ 39862306a36Sopenharmony_ci { 39962306a36Sopenharmony_ci .get_energy_uj = get_energy_counter, 40062306a36Sopenharmony_ci .get_max_energy_range_uj = get_max_energy_counter, 40162306a36Sopenharmony_ci .release = release_zone, 40262306a36Sopenharmony_ci .set_enable = set_domain_enable, 40362306a36Sopenharmony_ci .get_enable = get_domain_enable, 40462306a36Sopenharmony_ci }, 40562306a36Sopenharmony_ci /* RAPL_DOMAIN_PLATFORM */ 40662306a36Sopenharmony_ci { 40762306a36Sopenharmony_ci .get_energy_uj = get_energy_counter, 40862306a36Sopenharmony_ci .get_max_energy_range_uj = get_max_energy_counter, 40962306a36Sopenharmony_ci .release = release_zone, 41062306a36Sopenharmony_ci .set_enable = set_domain_enable, 41162306a36Sopenharmony_ci .get_enable = get_domain_enable, 41262306a36Sopenharmony_ci }, 41362306a36Sopenharmony_ci}; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci/* 41662306a36Sopenharmony_ci * Constraint index used by powercap can be different than power limit (PL) 41762306a36Sopenharmony_ci * index in that some PLs maybe missing due to non-existent MSRs. So we 41862306a36Sopenharmony_ci * need to convert here by finding the valid PLs only (name populated). 41962306a36Sopenharmony_ci */ 42062306a36Sopenharmony_cistatic int contraint_to_pl(struct rapl_domain *rd, int cid) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci int i, j; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci for (i = POWER_LIMIT1, j = 0; i < NR_POWER_LIMITS; i++) { 42562306a36Sopenharmony_ci if (is_pl_valid(rd, i) && j++ == cid) { 42662306a36Sopenharmony_ci pr_debug("%s: index %d\n", __func__, i); 42762306a36Sopenharmony_ci return i; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci pr_err("Cannot find matching power limit for constraint %d\n", cid); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return -EINVAL; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic int set_power_limit(struct powercap_zone *power_zone, int cid, 43662306a36Sopenharmony_ci u64 power_limit) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct rapl_domain *rd; 43962306a36Sopenharmony_ci struct rapl_package *rp; 44062306a36Sopenharmony_ci int ret = 0; 44162306a36Sopenharmony_ci int id; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci cpus_read_lock(); 44462306a36Sopenharmony_ci rd = power_zone_to_rapl_domain(power_zone); 44562306a36Sopenharmony_ci id = contraint_to_pl(rd, cid); 44662306a36Sopenharmony_ci rp = rd->rp; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci ret = rapl_write_pl_data(rd, id, PL_LIMIT, power_limit); 44962306a36Sopenharmony_ci if (!ret) 45062306a36Sopenharmony_ci package_power_limit_irq_save(rp); 45162306a36Sopenharmony_ci cpus_read_unlock(); 45262306a36Sopenharmony_ci return ret; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic int get_current_power_limit(struct powercap_zone *power_zone, int cid, 45662306a36Sopenharmony_ci u64 *data) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct rapl_domain *rd; 45962306a36Sopenharmony_ci u64 val; 46062306a36Sopenharmony_ci int ret = 0; 46162306a36Sopenharmony_ci int id; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci cpus_read_lock(); 46462306a36Sopenharmony_ci rd = power_zone_to_rapl_domain(power_zone); 46562306a36Sopenharmony_ci id = contraint_to_pl(rd, cid); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci ret = rapl_read_pl_data(rd, id, PL_LIMIT, true, &val); 46862306a36Sopenharmony_ci if (!ret) 46962306a36Sopenharmony_ci *data = val; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci cpus_read_unlock(); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci return ret; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic int set_time_window(struct powercap_zone *power_zone, int cid, 47762306a36Sopenharmony_ci u64 window) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct rapl_domain *rd; 48062306a36Sopenharmony_ci int ret = 0; 48162306a36Sopenharmony_ci int id; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci cpus_read_lock(); 48462306a36Sopenharmony_ci rd = power_zone_to_rapl_domain(power_zone); 48562306a36Sopenharmony_ci id = contraint_to_pl(rd, cid); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci ret = rapl_write_pl_data(rd, id, PL_TIME_WINDOW, window); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci cpus_read_unlock(); 49062306a36Sopenharmony_ci return ret; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cistatic int get_time_window(struct powercap_zone *power_zone, int cid, 49462306a36Sopenharmony_ci u64 *data) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct rapl_domain *rd; 49762306a36Sopenharmony_ci u64 val; 49862306a36Sopenharmony_ci int ret = 0; 49962306a36Sopenharmony_ci int id; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci cpus_read_lock(); 50262306a36Sopenharmony_ci rd = power_zone_to_rapl_domain(power_zone); 50362306a36Sopenharmony_ci id = contraint_to_pl(rd, cid); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci ret = rapl_read_pl_data(rd, id, PL_TIME_WINDOW, true, &val); 50662306a36Sopenharmony_ci if (!ret) 50762306a36Sopenharmony_ci *data = val; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci cpus_read_unlock(); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci return ret; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic const char *get_constraint_name(struct powercap_zone *power_zone, 51562306a36Sopenharmony_ci int cid) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci struct rapl_domain *rd; 51862306a36Sopenharmony_ci int id; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci rd = power_zone_to_rapl_domain(power_zone); 52162306a36Sopenharmony_ci id = contraint_to_pl(rd, cid); 52262306a36Sopenharmony_ci if (id >= 0) 52362306a36Sopenharmony_ci return rd->rpl[id].name; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci return NULL; 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic int get_max_power(struct powercap_zone *power_zone, int cid, u64 *data) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci struct rapl_domain *rd; 53162306a36Sopenharmony_ci u64 val; 53262306a36Sopenharmony_ci int ret = 0; 53362306a36Sopenharmony_ci int id; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci cpus_read_lock(); 53662306a36Sopenharmony_ci rd = power_zone_to_rapl_domain(power_zone); 53762306a36Sopenharmony_ci id = contraint_to_pl(rd, cid); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci ret = rapl_read_pl_data(rd, id, PL_MAX_POWER, true, &val); 54062306a36Sopenharmony_ci if (!ret) 54162306a36Sopenharmony_ci *data = val; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* As a generalization rule, PL4 would be around two times PL2. */ 54462306a36Sopenharmony_ci if (id == POWER_LIMIT4) 54562306a36Sopenharmony_ci *data = *data * 2; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci cpus_read_unlock(); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci return ret; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic const struct powercap_zone_constraint_ops constraint_ops = { 55362306a36Sopenharmony_ci .set_power_limit_uw = set_power_limit, 55462306a36Sopenharmony_ci .get_power_limit_uw = get_current_power_limit, 55562306a36Sopenharmony_ci .set_time_window_us = set_time_window, 55662306a36Sopenharmony_ci .get_time_window_us = get_time_window, 55762306a36Sopenharmony_ci .get_max_power_uw = get_max_power, 55862306a36Sopenharmony_ci .get_name = get_constraint_name, 55962306a36Sopenharmony_ci}; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci/* Return the id used for read_raw/write_raw callback */ 56262306a36Sopenharmony_cistatic int get_rid(struct rapl_package *rp) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci return rp->lead_cpu >= 0 ? rp->lead_cpu : rp->id; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci/* called after domain detection and package level data are set */ 56862306a36Sopenharmony_cistatic void rapl_init_domains(struct rapl_package *rp) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci enum rapl_domain_type i; 57162306a36Sopenharmony_ci enum rapl_domain_reg_id j; 57262306a36Sopenharmony_ci struct rapl_domain *rd = rp->domains; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci for (i = 0; i < RAPL_DOMAIN_MAX; i++) { 57562306a36Sopenharmony_ci unsigned int mask = rp->domain_map & (1 << i); 57662306a36Sopenharmony_ci int t; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (!mask) 57962306a36Sopenharmony_ci continue; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci rd->rp = rp; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci if (i == RAPL_DOMAIN_PLATFORM && rp->id > 0) { 58462306a36Sopenharmony_ci snprintf(rd->name, RAPL_DOMAIN_NAME_LENGTH, "psys-%d", 58562306a36Sopenharmony_ci rp->lead_cpu >= 0 ? topology_physical_package_id(rp->lead_cpu) : 58662306a36Sopenharmony_ci rp->id); 58762306a36Sopenharmony_ci } else { 58862306a36Sopenharmony_ci snprintf(rd->name, RAPL_DOMAIN_NAME_LENGTH, "%s", 58962306a36Sopenharmony_ci rapl_domain_names[i]); 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci rd->id = i; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci /* PL1 is supported by default */ 59562306a36Sopenharmony_ci rp->priv->limits[i] |= BIT(POWER_LIMIT1); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci for (t = POWER_LIMIT1; t < NR_POWER_LIMITS; t++) { 59862306a36Sopenharmony_ci if (rp->priv->limits[i] & BIT(t)) 59962306a36Sopenharmony_ci rd->rpl[t].name = pl_names[t]; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci for (j = 0; j < RAPL_DOMAIN_REG_MAX; j++) 60362306a36Sopenharmony_ci rd->regs[j] = rp->priv->regs[i][j]; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci rd++; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic u64 rapl_unit_xlate(struct rapl_domain *rd, enum unit_type type, 61062306a36Sopenharmony_ci u64 value, int to_raw) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci u64 units = 1; 61362306a36Sopenharmony_ci struct rapl_defaults *defaults = get_defaults(rd->rp); 61462306a36Sopenharmony_ci u64 scale = 1; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci switch (type) { 61762306a36Sopenharmony_ci case POWER_UNIT: 61862306a36Sopenharmony_ci units = rd->power_unit; 61962306a36Sopenharmony_ci break; 62062306a36Sopenharmony_ci case ENERGY_UNIT: 62162306a36Sopenharmony_ci scale = ENERGY_UNIT_SCALE; 62262306a36Sopenharmony_ci units = rd->energy_unit; 62362306a36Sopenharmony_ci break; 62462306a36Sopenharmony_ci case TIME_UNIT: 62562306a36Sopenharmony_ci return defaults->compute_time_window(rd, value, to_raw); 62662306a36Sopenharmony_ci case ARBITRARY_UNIT: 62762306a36Sopenharmony_ci default: 62862306a36Sopenharmony_ci return value; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci if (to_raw) 63262306a36Sopenharmony_ci return div64_u64(value, units) * scale; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci value *= units; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci return div64_u64(value, scale); 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci/* RAPL primitives for MSR and MMIO I/F */ 64062306a36Sopenharmony_cistatic struct rapl_primitive_info rpi_msr[NR_RAPL_PRIMITIVES] = { 64162306a36Sopenharmony_ci /* name, mask, shift, msr index, unit divisor */ 64262306a36Sopenharmony_ci [POWER_LIMIT1] = PRIMITIVE_INFO_INIT(POWER_LIMIT1, POWER_LIMIT1_MASK, 0, 64362306a36Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, POWER_UNIT, 0), 64462306a36Sopenharmony_ci [POWER_LIMIT2] = PRIMITIVE_INFO_INIT(POWER_LIMIT2, POWER_LIMIT2_MASK, 32, 64562306a36Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, POWER_UNIT, 0), 64662306a36Sopenharmony_ci [POWER_LIMIT4] = PRIMITIVE_INFO_INIT(POWER_LIMIT4, POWER_LIMIT4_MASK, 0, 64762306a36Sopenharmony_ci RAPL_DOMAIN_REG_PL4, POWER_UNIT, 0), 64862306a36Sopenharmony_ci [ENERGY_COUNTER] = PRIMITIVE_INFO_INIT(ENERGY_COUNTER, ENERGY_STATUS_MASK, 0, 64962306a36Sopenharmony_ci RAPL_DOMAIN_REG_STATUS, ENERGY_UNIT, 0), 65062306a36Sopenharmony_ci [FW_LOCK] = PRIMITIVE_INFO_INIT(FW_LOCK, POWER_LOW_LOCK, 31, 65162306a36Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0), 65262306a36Sopenharmony_ci [FW_HIGH_LOCK] = PRIMITIVE_INFO_INIT(FW_LOCK, POWER_HIGH_LOCK, 63, 65362306a36Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0), 65462306a36Sopenharmony_ci [PL1_ENABLE] = PRIMITIVE_INFO_INIT(PL1_ENABLE, POWER_LIMIT1_ENABLE, 15, 65562306a36Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0), 65662306a36Sopenharmony_ci [PL1_CLAMP] = PRIMITIVE_INFO_INIT(PL1_CLAMP, POWER_LIMIT1_CLAMP, 16, 65762306a36Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0), 65862306a36Sopenharmony_ci [PL2_ENABLE] = PRIMITIVE_INFO_INIT(PL2_ENABLE, POWER_LIMIT2_ENABLE, 47, 65962306a36Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0), 66062306a36Sopenharmony_ci [PL2_CLAMP] = PRIMITIVE_INFO_INIT(PL2_CLAMP, POWER_LIMIT2_CLAMP, 48, 66162306a36Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0), 66262306a36Sopenharmony_ci [TIME_WINDOW1] = PRIMITIVE_INFO_INIT(TIME_WINDOW1, TIME_WINDOW1_MASK, 17, 66362306a36Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, TIME_UNIT, 0), 66462306a36Sopenharmony_ci [TIME_WINDOW2] = PRIMITIVE_INFO_INIT(TIME_WINDOW2, TIME_WINDOW2_MASK, 49, 66562306a36Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, TIME_UNIT, 0), 66662306a36Sopenharmony_ci [THERMAL_SPEC_POWER] = PRIMITIVE_INFO_INIT(THERMAL_SPEC_POWER, POWER_INFO_THERMAL_SPEC_MASK, 66762306a36Sopenharmony_ci 0, RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0), 66862306a36Sopenharmony_ci [MAX_POWER] = PRIMITIVE_INFO_INIT(MAX_POWER, POWER_INFO_MAX_MASK, 32, 66962306a36Sopenharmony_ci RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0), 67062306a36Sopenharmony_ci [MIN_POWER] = PRIMITIVE_INFO_INIT(MIN_POWER, POWER_INFO_MIN_MASK, 16, 67162306a36Sopenharmony_ci RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0), 67262306a36Sopenharmony_ci [MAX_TIME_WINDOW] = PRIMITIVE_INFO_INIT(MAX_TIME_WINDOW, POWER_INFO_MAX_TIME_WIN_MASK, 48, 67362306a36Sopenharmony_ci RAPL_DOMAIN_REG_INFO, TIME_UNIT, 0), 67462306a36Sopenharmony_ci [THROTTLED_TIME] = PRIMITIVE_INFO_INIT(THROTTLED_TIME, PERF_STATUS_THROTTLE_TIME_MASK, 0, 67562306a36Sopenharmony_ci RAPL_DOMAIN_REG_PERF, TIME_UNIT, 0), 67662306a36Sopenharmony_ci [PRIORITY_LEVEL] = PRIMITIVE_INFO_INIT(PRIORITY_LEVEL, PP_POLICY_MASK, 0, 67762306a36Sopenharmony_ci RAPL_DOMAIN_REG_POLICY, ARBITRARY_UNIT, 0), 67862306a36Sopenharmony_ci [PSYS_POWER_LIMIT1] = PRIMITIVE_INFO_INIT(PSYS_POWER_LIMIT1, PSYS_POWER_LIMIT1_MASK, 0, 67962306a36Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, POWER_UNIT, 0), 68062306a36Sopenharmony_ci [PSYS_POWER_LIMIT2] = PRIMITIVE_INFO_INIT(PSYS_POWER_LIMIT2, PSYS_POWER_LIMIT2_MASK, 32, 68162306a36Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, POWER_UNIT, 0), 68262306a36Sopenharmony_ci [PSYS_PL1_ENABLE] = PRIMITIVE_INFO_INIT(PSYS_PL1_ENABLE, PSYS_POWER_LIMIT1_ENABLE, 17, 68362306a36Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0), 68462306a36Sopenharmony_ci [PSYS_PL2_ENABLE] = PRIMITIVE_INFO_INIT(PSYS_PL2_ENABLE, PSYS_POWER_LIMIT2_ENABLE, 49, 68562306a36Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0), 68662306a36Sopenharmony_ci [PSYS_TIME_WINDOW1] = PRIMITIVE_INFO_INIT(PSYS_TIME_WINDOW1, PSYS_TIME_WINDOW1_MASK, 19, 68762306a36Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, TIME_UNIT, 0), 68862306a36Sopenharmony_ci [PSYS_TIME_WINDOW2] = PRIMITIVE_INFO_INIT(PSYS_TIME_WINDOW2, PSYS_TIME_WINDOW2_MASK, 51, 68962306a36Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, TIME_UNIT, 0), 69062306a36Sopenharmony_ci /* non-hardware */ 69162306a36Sopenharmony_ci [AVERAGE_POWER] = PRIMITIVE_INFO_INIT(AVERAGE_POWER, 0, 0, 0, POWER_UNIT, 69262306a36Sopenharmony_ci RAPL_PRIMITIVE_DERIVED), 69362306a36Sopenharmony_ci}; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci/* RAPL primitives for TPMI I/F */ 69662306a36Sopenharmony_cistatic struct rapl_primitive_info rpi_tpmi[NR_RAPL_PRIMITIVES] = { 69762306a36Sopenharmony_ci /* name, mask, shift, msr index, unit divisor */ 69862306a36Sopenharmony_ci [POWER_LIMIT1] = PRIMITIVE_INFO_INIT(POWER_LIMIT1, TPMI_POWER_LIMIT_MASK, 0, 69962306a36Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, POWER_UNIT, 0), 70062306a36Sopenharmony_ci [POWER_LIMIT2] = PRIMITIVE_INFO_INIT(POWER_LIMIT2, TPMI_POWER_LIMIT_MASK, 0, 70162306a36Sopenharmony_ci RAPL_DOMAIN_REG_PL2, POWER_UNIT, 0), 70262306a36Sopenharmony_ci [POWER_LIMIT4] = PRIMITIVE_INFO_INIT(POWER_LIMIT4, TPMI_POWER_LIMIT_MASK, 0, 70362306a36Sopenharmony_ci RAPL_DOMAIN_REG_PL4, POWER_UNIT, 0), 70462306a36Sopenharmony_ci [ENERGY_COUNTER] = PRIMITIVE_INFO_INIT(ENERGY_COUNTER, ENERGY_STATUS_MASK, 0, 70562306a36Sopenharmony_ci RAPL_DOMAIN_REG_STATUS, ENERGY_UNIT, 0), 70662306a36Sopenharmony_ci [PL1_LOCK] = PRIMITIVE_INFO_INIT(PL1_LOCK, POWER_HIGH_LOCK, 63, 70762306a36Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0), 70862306a36Sopenharmony_ci [PL2_LOCK] = PRIMITIVE_INFO_INIT(PL2_LOCK, POWER_HIGH_LOCK, 63, 70962306a36Sopenharmony_ci RAPL_DOMAIN_REG_PL2, ARBITRARY_UNIT, 0), 71062306a36Sopenharmony_ci [PL4_LOCK] = PRIMITIVE_INFO_INIT(PL4_LOCK, POWER_HIGH_LOCK, 63, 71162306a36Sopenharmony_ci RAPL_DOMAIN_REG_PL4, ARBITRARY_UNIT, 0), 71262306a36Sopenharmony_ci [PL1_ENABLE] = PRIMITIVE_INFO_INIT(PL1_ENABLE, TPMI_POWER_LIMIT_ENABLE, 62, 71362306a36Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0), 71462306a36Sopenharmony_ci [PL2_ENABLE] = PRIMITIVE_INFO_INIT(PL2_ENABLE, TPMI_POWER_LIMIT_ENABLE, 62, 71562306a36Sopenharmony_ci RAPL_DOMAIN_REG_PL2, ARBITRARY_UNIT, 0), 71662306a36Sopenharmony_ci [PL4_ENABLE] = PRIMITIVE_INFO_INIT(PL4_ENABLE, TPMI_POWER_LIMIT_ENABLE, 62, 71762306a36Sopenharmony_ci RAPL_DOMAIN_REG_PL4, ARBITRARY_UNIT, 0), 71862306a36Sopenharmony_ci [TIME_WINDOW1] = PRIMITIVE_INFO_INIT(TIME_WINDOW1, TPMI_TIME_WINDOW_MASK, 18, 71962306a36Sopenharmony_ci RAPL_DOMAIN_REG_LIMIT, TIME_UNIT, 0), 72062306a36Sopenharmony_ci [TIME_WINDOW2] = PRIMITIVE_INFO_INIT(TIME_WINDOW2, TPMI_TIME_WINDOW_MASK, 18, 72162306a36Sopenharmony_ci RAPL_DOMAIN_REG_PL2, TIME_UNIT, 0), 72262306a36Sopenharmony_ci [THERMAL_SPEC_POWER] = PRIMITIVE_INFO_INIT(THERMAL_SPEC_POWER, TPMI_INFO_SPEC_MASK, 0, 72362306a36Sopenharmony_ci RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0), 72462306a36Sopenharmony_ci [MAX_POWER] = PRIMITIVE_INFO_INIT(MAX_POWER, TPMI_INFO_MAX_MASK, 36, 72562306a36Sopenharmony_ci RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0), 72662306a36Sopenharmony_ci [MIN_POWER] = PRIMITIVE_INFO_INIT(MIN_POWER, TPMI_INFO_MIN_MASK, 18, 72762306a36Sopenharmony_ci RAPL_DOMAIN_REG_INFO, POWER_UNIT, 0), 72862306a36Sopenharmony_ci [MAX_TIME_WINDOW] = PRIMITIVE_INFO_INIT(MAX_TIME_WINDOW, TPMI_INFO_MAX_TIME_WIN_MASK, 54, 72962306a36Sopenharmony_ci RAPL_DOMAIN_REG_INFO, TIME_UNIT, 0), 73062306a36Sopenharmony_ci [THROTTLED_TIME] = PRIMITIVE_INFO_INIT(THROTTLED_TIME, PERF_STATUS_THROTTLE_TIME_MASK, 0, 73162306a36Sopenharmony_ci RAPL_DOMAIN_REG_PERF, TIME_UNIT, 0), 73262306a36Sopenharmony_ci /* non-hardware */ 73362306a36Sopenharmony_ci [AVERAGE_POWER] = PRIMITIVE_INFO_INIT(AVERAGE_POWER, 0, 0, 0, 73462306a36Sopenharmony_ci POWER_UNIT, RAPL_PRIMITIVE_DERIVED), 73562306a36Sopenharmony_ci}; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_cistatic struct rapl_primitive_info *get_rpi(struct rapl_package *rp, int prim) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci struct rapl_primitive_info *rpi = rp->priv->rpi; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (prim < 0 || prim > NR_RAPL_PRIMITIVES || !rpi) 74262306a36Sopenharmony_ci return NULL; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci return &rpi[prim]; 74562306a36Sopenharmony_ci} 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_cistatic int rapl_config(struct rapl_package *rp) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci switch (rp->priv->type) { 75062306a36Sopenharmony_ci /* MMIO I/F shares the same register layout as MSR registers */ 75162306a36Sopenharmony_ci case RAPL_IF_MMIO: 75262306a36Sopenharmony_ci case RAPL_IF_MSR: 75362306a36Sopenharmony_ci rp->priv->defaults = (void *)defaults_msr; 75462306a36Sopenharmony_ci rp->priv->rpi = (void *)rpi_msr; 75562306a36Sopenharmony_ci break; 75662306a36Sopenharmony_ci case RAPL_IF_TPMI: 75762306a36Sopenharmony_ci rp->priv->defaults = (void *)&defaults_tpmi; 75862306a36Sopenharmony_ci rp->priv->rpi = (void *)rpi_tpmi; 75962306a36Sopenharmony_ci break; 76062306a36Sopenharmony_ci default: 76162306a36Sopenharmony_ci return -EINVAL; 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci /* defaults_msr can be NULL on unsupported platforms */ 76562306a36Sopenharmony_ci if (!rp->priv->defaults || !rp->priv->rpi) 76662306a36Sopenharmony_ci return -ENODEV; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci return 0; 76962306a36Sopenharmony_ci} 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_cistatic enum rapl_primitives 77262306a36Sopenharmony_ciprim_fixups(struct rapl_domain *rd, enum rapl_primitives prim) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci struct rapl_defaults *defaults = get_defaults(rd->rp); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (!defaults->spr_psys_bits) 77762306a36Sopenharmony_ci return prim; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci if (rd->id != RAPL_DOMAIN_PLATFORM) 78062306a36Sopenharmony_ci return prim; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci switch (prim) { 78362306a36Sopenharmony_ci case POWER_LIMIT1: 78462306a36Sopenharmony_ci return PSYS_POWER_LIMIT1; 78562306a36Sopenharmony_ci case POWER_LIMIT2: 78662306a36Sopenharmony_ci return PSYS_POWER_LIMIT2; 78762306a36Sopenharmony_ci case PL1_ENABLE: 78862306a36Sopenharmony_ci return PSYS_PL1_ENABLE; 78962306a36Sopenharmony_ci case PL2_ENABLE: 79062306a36Sopenharmony_ci return PSYS_PL2_ENABLE; 79162306a36Sopenharmony_ci case TIME_WINDOW1: 79262306a36Sopenharmony_ci return PSYS_TIME_WINDOW1; 79362306a36Sopenharmony_ci case TIME_WINDOW2: 79462306a36Sopenharmony_ci return PSYS_TIME_WINDOW2; 79562306a36Sopenharmony_ci default: 79662306a36Sopenharmony_ci return prim; 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci/* Read primitive data based on its related struct rapl_primitive_info. 80162306a36Sopenharmony_ci * if xlate flag is set, return translated data based on data units, i.e. 80262306a36Sopenharmony_ci * time, energy, and power. 80362306a36Sopenharmony_ci * RAPL MSRs are non-architectual and are laid out not consistently across 80462306a36Sopenharmony_ci * domains. Here we use primitive info to allow writing consolidated access 80562306a36Sopenharmony_ci * functions. 80662306a36Sopenharmony_ci * For a given primitive, it is processed by MSR mask and shift. Unit conversion 80762306a36Sopenharmony_ci * is pre-assigned based on RAPL unit MSRs read at init time. 80862306a36Sopenharmony_ci * 63-------------------------- 31--------------------------- 0 80962306a36Sopenharmony_ci * | xxxxx (mask) | 81062306a36Sopenharmony_ci * | |<- shift ----------------| 81162306a36Sopenharmony_ci * 63-------------------------- 31--------------------------- 0 81262306a36Sopenharmony_ci */ 81362306a36Sopenharmony_cistatic int rapl_read_data_raw(struct rapl_domain *rd, 81462306a36Sopenharmony_ci enum rapl_primitives prim, bool xlate, u64 *data) 81562306a36Sopenharmony_ci{ 81662306a36Sopenharmony_ci u64 value; 81762306a36Sopenharmony_ci enum rapl_primitives prim_fixed = prim_fixups(rd, prim); 81862306a36Sopenharmony_ci struct rapl_primitive_info *rpi = get_rpi(rd->rp, prim_fixed); 81962306a36Sopenharmony_ci struct reg_action ra; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci if (!rpi || !rpi->name || rpi->flag & RAPL_PRIMITIVE_DUMMY) 82262306a36Sopenharmony_ci return -EINVAL; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci ra.reg = rd->regs[rpi->id]; 82562306a36Sopenharmony_ci if (!ra.reg.val) 82662306a36Sopenharmony_ci return -EINVAL; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci /* non-hardware data are collected by the polling thread */ 82962306a36Sopenharmony_ci if (rpi->flag & RAPL_PRIMITIVE_DERIVED) { 83062306a36Sopenharmony_ci *data = rd->rdd.primitives[prim]; 83162306a36Sopenharmony_ci return 0; 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci ra.mask = rpi->mask; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra)) { 83762306a36Sopenharmony_ci pr_debug("failed to read reg 0x%llx for %s:%s\n", ra.reg.val, rd->rp->name, rd->name); 83862306a36Sopenharmony_ci return -EIO; 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci value = ra.value >> rpi->shift; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci if (xlate) 84462306a36Sopenharmony_ci *data = rapl_unit_xlate(rd, rpi->unit, value, 0); 84562306a36Sopenharmony_ci else 84662306a36Sopenharmony_ci *data = value; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci return 0; 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci/* Similar use of primitive info in the read counterpart */ 85262306a36Sopenharmony_cistatic int rapl_write_data_raw(struct rapl_domain *rd, 85362306a36Sopenharmony_ci enum rapl_primitives prim, 85462306a36Sopenharmony_ci unsigned long long value) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci enum rapl_primitives prim_fixed = prim_fixups(rd, prim); 85762306a36Sopenharmony_ci struct rapl_primitive_info *rpi = get_rpi(rd->rp, prim_fixed); 85862306a36Sopenharmony_ci u64 bits; 85962306a36Sopenharmony_ci struct reg_action ra; 86062306a36Sopenharmony_ci int ret; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci if (!rpi || !rpi->name || rpi->flag & RAPL_PRIMITIVE_DUMMY) 86362306a36Sopenharmony_ci return -EINVAL; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci bits = rapl_unit_xlate(rd, rpi->unit, value, 1); 86662306a36Sopenharmony_ci bits <<= rpi->shift; 86762306a36Sopenharmony_ci bits &= rpi->mask; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci memset(&ra, 0, sizeof(ra)); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci ra.reg = rd->regs[rpi->id]; 87262306a36Sopenharmony_ci ra.mask = rpi->mask; 87362306a36Sopenharmony_ci ra.value = bits; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci ret = rd->rp->priv->write_raw(get_rid(rd->rp), &ra); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci return ret; 87862306a36Sopenharmony_ci} 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_cistatic int rapl_read_pl_data(struct rapl_domain *rd, int pl, 88162306a36Sopenharmony_ci enum pl_prims pl_prim, bool xlate, u64 *data) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci enum rapl_primitives prim = get_pl_prim(rd, pl, pl_prim); 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci if (!is_pl_valid(rd, pl)) 88662306a36Sopenharmony_ci return -EINVAL; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci return rapl_read_data_raw(rd, prim, xlate, data); 88962306a36Sopenharmony_ci} 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_cistatic int rapl_write_pl_data(struct rapl_domain *rd, int pl, 89262306a36Sopenharmony_ci enum pl_prims pl_prim, 89362306a36Sopenharmony_ci unsigned long long value) 89462306a36Sopenharmony_ci{ 89562306a36Sopenharmony_ci enum rapl_primitives prim = get_pl_prim(rd, pl, pl_prim); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci if (!is_pl_valid(rd, pl)) 89862306a36Sopenharmony_ci return -EINVAL; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci if (rd->rpl[pl].locked) { 90162306a36Sopenharmony_ci pr_debug("%s:%s:%s locked by BIOS\n", rd->rp->name, rd->name, pl_names[pl]); 90262306a36Sopenharmony_ci return -EACCES; 90362306a36Sopenharmony_ci } 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci return rapl_write_data_raw(rd, prim, value); 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ci/* 90862306a36Sopenharmony_ci * Raw RAPL data stored in MSRs are in certain scales. We need to 90962306a36Sopenharmony_ci * convert them into standard units based on the units reported in 91062306a36Sopenharmony_ci * the RAPL unit MSRs. This is specific to CPUs as the method to 91162306a36Sopenharmony_ci * calculate units differ on different CPUs. 91262306a36Sopenharmony_ci * We convert the units to below format based on CPUs. 91362306a36Sopenharmony_ci * i.e. 91462306a36Sopenharmony_ci * energy unit: picoJoules : Represented in picoJoules by default 91562306a36Sopenharmony_ci * power unit : microWatts : Represented in milliWatts by default 91662306a36Sopenharmony_ci * time unit : microseconds: Represented in seconds by default 91762306a36Sopenharmony_ci */ 91862306a36Sopenharmony_cistatic int rapl_check_unit_core(struct rapl_domain *rd) 91962306a36Sopenharmony_ci{ 92062306a36Sopenharmony_ci struct reg_action ra; 92162306a36Sopenharmony_ci u32 value; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci ra.reg = rd->regs[RAPL_DOMAIN_REG_UNIT]; 92462306a36Sopenharmony_ci ra.mask = ~0; 92562306a36Sopenharmony_ci if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra)) { 92662306a36Sopenharmony_ci pr_err("Failed to read power unit REG 0x%llx on %s:%s, exit.\n", 92762306a36Sopenharmony_ci ra.reg.val, rd->rp->name, rd->name); 92862306a36Sopenharmony_ci return -ENODEV; 92962306a36Sopenharmony_ci } 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci value = (ra.value & ENERGY_UNIT_MASK) >> ENERGY_UNIT_OFFSET; 93262306a36Sopenharmony_ci rd->energy_unit = ENERGY_UNIT_SCALE * 1000000 / (1 << value); 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci value = (ra.value & POWER_UNIT_MASK) >> POWER_UNIT_OFFSET; 93562306a36Sopenharmony_ci rd->power_unit = 1000000 / (1 << value); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci value = (ra.value & TIME_UNIT_MASK) >> TIME_UNIT_OFFSET; 93862306a36Sopenharmony_ci rd->time_unit = 1000000 / (1 << value); 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci pr_debug("Core CPU %s:%s energy=%dpJ, time=%dus, power=%duW\n", 94162306a36Sopenharmony_ci rd->rp->name, rd->name, rd->energy_unit, rd->time_unit, rd->power_unit); 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci return 0; 94462306a36Sopenharmony_ci} 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_cistatic int rapl_check_unit_atom(struct rapl_domain *rd) 94762306a36Sopenharmony_ci{ 94862306a36Sopenharmony_ci struct reg_action ra; 94962306a36Sopenharmony_ci u32 value; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci ra.reg = rd->regs[RAPL_DOMAIN_REG_UNIT]; 95262306a36Sopenharmony_ci ra.mask = ~0; 95362306a36Sopenharmony_ci if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra)) { 95462306a36Sopenharmony_ci pr_err("Failed to read power unit REG 0x%llx on %s:%s, exit.\n", 95562306a36Sopenharmony_ci ra.reg.val, rd->rp->name, rd->name); 95662306a36Sopenharmony_ci return -ENODEV; 95762306a36Sopenharmony_ci } 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci value = (ra.value & ENERGY_UNIT_MASK) >> ENERGY_UNIT_OFFSET; 96062306a36Sopenharmony_ci rd->energy_unit = ENERGY_UNIT_SCALE * 1 << value; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci value = (ra.value & POWER_UNIT_MASK) >> POWER_UNIT_OFFSET; 96362306a36Sopenharmony_ci rd->power_unit = (1 << value) * 1000; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci value = (ra.value & TIME_UNIT_MASK) >> TIME_UNIT_OFFSET; 96662306a36Sopenharmony_ci rd->time_unit = 1000000 / (1 << value); 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci pr_debug("Atom %s:%s energy=%dpJ, time=%dus, power=%duW\n", 96962306a36Sopenharmony_ci rd->rp->name, rd->name, rd->energy_unit, rd->time_unit, rd->power_unit); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci return 0; 97262306a36Sopenharmony_ci} 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_cistatic void power_limit_irq_save_cpu(void *info) 97562306a36Sopenharmony_ci{ 97662306a36Sopenharmony_ci u32 l, h = 0; 97762306a36Sopenharmony_ci struct rapl_package *rp = (struct rapl_package *)info; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci /* save the state of PLN irq mask bit before disabling it */ 98062306a36Sopenharmony_ci rdmsr_safe(MSR_IA32_PACKAGE_THERM_INTERRUPT, &l, &h); 98162306a36Sopenharmony_ci if (!(rp->power_limit_irq & PACKAGE_PLN_INT_SAVED)) { 98262306a36Sopenharmony_ci rp->power_limit_irq = l & PACKAGE_THERM_INT_PLN_ENABLE; 98362306a36Sopenharmony_ci rp->power_limit_irq |= PACKAGE_PLN_INT_SAVED; 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci l &= ~PACKAGE_THERM_INT_PLN_ENABLE; 98662306a36Sopenharmony_ci wrmsr_safe(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h); 98762306a36Sopenharmony_ci} 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci/* REVISIT: 99062306a36Sopenharmony_ci * When package power limit is set artificially low by RAPL, LVT 99162306a36Sopenharmony_ci * thermal interrupt for package power limit should be ignored 99262306a36Sopenharmony_ci * since we are not really exceeding the real limit. The intention 99362306a36Sopenharmony_ci * is to avoid excessive interrupts while we are trying to save power. 99462306a36Sopenharmony_ci * A useful feature might be routing the package_power_limit interrupt 99562306a36Sopenharmony_ci * to userspace via eventfd. once we have a usecase, this is simple 99662306a36Sopenharmony_ci * to do by adding an atomic notifier. 99762306a36Sopenharmony_ci */ 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_cistatic void package_power_limit_irq_save(struct rapl_package *rp) 100062306a36Sopenharmony_ci{ 100162306a36Sopenharmony_ci if (rp->lead_cpu < 0) 100262306a36Sopenharmony_ci return; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci if (!boot_cpu_has(X86_FEATURE_PTS) || !boot_cpu_has(X86_FEATURE_PLN)) 100562306a36Sopenharmony_ci return; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci smp_call_function_single(rp->lead_cpu, power_limit_irq_save_cpu, rp, 1); 100862306a36Sopenharmony_ci} 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci/* 101162306a36Sopenharmony_ci * Restore per package power limit interrupt enable state. Called from cpu 101262306a36Sopenharmony_ci * hotplug code on package removal. 101362306a36Sopenharmony_ci */ 101462306a36Sopenharmony_cistatic void package_power_limit_irq_restore(struct rapl_package *rp) 101562306a36Sopenharmony_ci{ 101662306a36Sopenharmony_ci u32 l, h; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci if (rp->lead_cpu < 0) 101962306a36Sopenharmony_ci return; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci if (!boot_cpu_has(X86_FEATURE_PTS) || !boot_cpu_has(X86_FEATURE_PLN)) 102262306a36Sopenharmony_ci return; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci /* irq enable state not saved, nothing to restore */ 102562306a36Sopenharmony_ci if (!(rp->power_limit_irq & PACKAGE_PLN_INT_SAVED)) 102662306a36Sopenharmony_ci return; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci rdmsr_safe(MSR_IA32_PACKAGE_THERM_INTERRUPT, &l, &h); 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci if (rp->power_limit_irq & PACKAGE_THERM_INT_PLN_ENABLE) 103162306a36Sopenharmony_ci l |= PACKAGE_THERM_INT_PLN_ENABLE; 103262306a36Sopenharmony_ci else 103362306a36Sopenharmony_ci l &= ~PACKAGE_THERM_INT_PLN_ENABLE; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci wrmsr_safe(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h); 103662306a36Sopenharmony_ci} 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_cistatic void set_floor_freq_default(struct rapl_domain *rd, bool mode) 103962306a36Sopenharmony_ci{ 104062306a36Sopenharmony_ci int i; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci /* always enable clamp such that p-state can go below OS requested 104362306a36Sopenharmony_ci * range. power capping priority over guranteed frequency. 104462306a36Sopenharmony_ci */ 104562306a36Sopenharmony_ci rapl_write_pl_data(rd, POWER_LIMIT1, PL_CLAMP, mode); 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci for (i = POWER_LIMIT2; i < NR_POWER_LIMITS; i++) { 104862306a36Sopenharmony_ci rapl_write_pl_data(rd, i, PL_ENABLE, mode); 104962306a36Sopenharmony_ci rapl_write_pl_data(rd, i, PL_CLAMP, mode); 105062306a36Sopenharmony_ci } 105162306a36Sopenharmony_ci} 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_cistatic void set_floor_freq_atom(struct rapl_domain *rd, bool enable) 105462306a36Sopenharmony_ci{ 105562306a36Sopenharmony_ci static u32 power_ctrl_orig_val; 105662306a36Sopenharmony_ci struct rapl_defaults *defaults = get_defaults(rd->rp); 105762306a36Sopenharmony_ci u32 mdata; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci if (!defaults->floor_freq_reg_addr) { 106062306a36Sopenharmony_ci pr_err("Invalid floor frequency config register\n"); 106162306a36Sopenharmony_ci return; 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci if (!power_ctrl_orig_val) 106562306a36Sopenharmony_ci iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_CR_READ, 106662306a36Sopenharmony_ci defaults->floor_freq_reg_addr, 106762306a36Sopenharmony_ci &power_ctrl_orig_val); 106862306a36Sopenharmony_ci mdata = power_ctrl_orig_val; 106962306a36Sopenharmony_ci if (enable) { 107062306a36Sopenharmony_ci mdata &= ~(0x7f << 8); 107162306a36Sopenharmony_ci mdata |= 1 << 8; 107262306a36Sopenharmony_ci } 107362306a36Sopenharmony_ci iosf_mbi_write(BT_MBI_UNIT_PMC, MBI_CR_WRITE, 107462306a36Sopenharmony_ci defaults->floor_freq_reg_addr, mdata); 107562306a36Sopenharmony_ci} 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_cistatic u64 rapl_compute_time_window_core(struct rapl_domain *rd, u64 value, 107862306a36Sopenharmony_ci bool to_raw) 107962306a36Sopenharmony_ci{ 108062306a36Sopenharmony_ci u64 f, y; /* fraction and exp. used for time unit */ 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci /* 108362306a36Sopenharmony_ci * Special processing based on 2^Y*(1+F/4), refer 108462306a36Sopenharmony_ci * to Intel Software Developer's manual Vol.3B: CH 14.9.3. 108562306a36Sopenharmony_ci */ 108662306a36Sopenharmony_ci if (!to_raw) { 108762306a36Sopenharmony_ci f = (value & 0x60) >> 5; 108862306a36Sopenharmony_ci y = value & 0x1f; 108962306a36Sopenharmony_ci value = (1 << y) * (4 + f) * rd->time_unit / 4; 109062306a36Sopenharmony_ci } else { 109162306a36Sopenharmony_ci if (value < rd->time_unit) 109262306a36Sopenharmony_ci return 0; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci do_div(value, rd->time_unit); 109562306a36Sopenharmony_ci y = ilog2(value); 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci /* 109862306a36Sopenharmony_ci * The target hardware field is 7 bits wide, so return all ones 109962306a36Sopenharmony_ci * if the exponent is too large. 110062306a36Sopenharmony_ci */ 110162306a36Sopenharmony_ci if (y > 0x1f) 110262306a36Sopenharmony_ci return 0x7f; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci f = div64_u64(4 * (value - (1ULL << y)), 1ULL << y); 110562306a36Sopenharmony_ci value = (y & 0x1f) | ((f & 0x3) << 5); 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci return value; 110862306a36Sopenharmony_ci} 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_cistatic u64 rapl_compute_time_window_atom(struct rapl_domain *rd, u64 value, 111162306a36Sopenharmony_ci bool to_raw) 111262306a36Sopenharmony_ci{ 111362306a36Sopenharmony_ci /* 111462306a36Sopenharmony_ci * Atom time unit encoding is straight forward val * time_unit, 111562306a36Sopenharmony_ci * where time_unit is default to 1 sec. Never 0. 111662306a36Sopenharmony_ci */ 111762306a36Sopenharmony_ci if (!to_raw) 111862306a36Sopenharmony_ci return (value) ? value * rd->time_unit : rd->time_unit; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci value = div64_u64(value, rd->time_unit); 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci return value; 112362306a36Sopenharmony_ci} 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci/* TPMI Unit register has different layout */ 112662306a36Sopenharmony_ci#define TPMI_POWER_UNIT_OFFSET POWER_UNIT_OFFSET 112762306a36Sopenharmony_ci#define TPMI_POWER_UNIT_MASK POWER_UNIT_MASK 112862306a36Sopenharmony_ci#define TPMI_ENERGY_UNIT_OFFSET 0x06 112962306a36Sopenharmony_ci#define TPMI_ENERGY_UNIT_MASK 0x7C0 113062306a36Sopenharmony_ci#define TPMI_TIME_UNIT_OFFSET 0x0C 113162306a36Sopenharmony_ci#define TPMI_TIME_UNIT_MASK 0xF000 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_cistatic int rapl_check_unit_tpmi(struct rapl_domain *rd) 113462306a36Sopenharmony_ci{ 113562306a36Sopenharmony_ci struct reg_action ra; 113662306a36Sopenharmony_ci u32 value; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci ra.reg = rd->regs[RAPL_DOMAIN_REG_UNIT]; 113962306a36Sopenharmony_ci ra.mask = ~0; 114062306a36Sopenharmony_ci if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra)) { 114162306a36Sopenharmony_ci pr_err("Failed to read power unit REG 0x%llx on %s:%s, exit.\n", 114262306a36Sopenharmony_ci ra.reg.val, rd->rp->name, rd->name); 114362306a36Sopenharmony_ci return -ENODEV; 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci value = (ra.value & TPMI_ENERGY_UNIT_MASK) >> TPMI_ENERGY_UNIT_OFFSET; 114762306a36Sopenharmony_ci rd->energy_unit = ENERGY_UNIT_SCALE * 1000000 / (1 << value); 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci value = (ra.value & TPMI_POWER_UNIT_MASK) >> TPMI_POWER_UNIT_OFFSET; 115062306a36Sopenharmony_ci rd->power_unit = 1000000 / (1 << value); 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci value = (ra.value & TPMI_TIME_UNIT_MASK) >> TPMI_TIME_UNIT_OFFSET; 115362306a36Sopenharmony_ci rd->time_unit = 1000000 / (1 << value); 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci pr_debug("Core CPU %s:%s energy=%dpJ, time=%dus, power=%duW\n", 115662306a36Sopenharmony_ci rd->rp->name, rd->name, rd->energy_unit, rd->time_unit, rd->power_unit); 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci return 0; 115962306a36Sopenharmony_ci} 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_cistatic const struct rapl_defaults defaults_tpmi = { 116262306a36Sopenharmony_ci .check_unit = rapl_check_unit_tpmi, 116362306a36Sopenharmony_ci /* Reuse existing logic, ignore the PL_CLAMP failures and enable all Power Limits */ 116462306a36Sopenharmony_ci .set_floor_freq = set_floor_freq_default, 116562306a36Sopenharmony_ci .compute_time_window = rapl_compute_time_window_core, 116662306a36Sopenharmony_ci}; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_cistatic const struct rapl_defaults rapl_defaults_core = { 116962306a36Sopenharmony_ci .floor_freq_reg_addr = 0, 117062306a36Sopenharmony_ci .check_unit = rapl_check_unit_core, 117162306a36Sopenharmony_ci .set_floor_freq = set_floor_freq_default, 117262306a36Sopenharmony_ci .compute_time_window = rapl_compute_time_window_core, 117362306a36Sopenharmony_ci}; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_cistatic const struct rapl_defaults rapl_defaults_hsw_server = { 117662306a36Sopenharmony_ci .check_unit = rapl_check_unit_core, 117762306a36Sopenharmony_ci .set_floor_freq = set_floor_freq_default, 117862306a36Sopenharmony_ci .compute_time_window = rapl_compute_time_window_core, 117962306a36Sopenharmony_ci .dram_domain_energy_unit = 15300, 118062306a36Sopenharmony_ci}; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_cistatic const struct rapl_defaults rapl_defaults_spr_server = { 118362306a36Sopenharmony_ci .check_unit = rapl_check_unit_core, 118462306a36Sopenharmony_ci .set_floor_freq = set_floor_freq_default, 118562306a36Sopenharmony_ci .compute_time_window = rapl_compute_time_window_core, 118662306a36Sopenharmony_ci .psys_domain_energy_unit = 1000000000, 118762306a36Sopenharmony_ci .spr_psys_bits = true, 118862306a36Sopenharmony_ci}; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_cistatic const struct rapl_defaults rapl_defaults_byt = { 119162306a36Sopenharmony_ci .floor_freq_reg_addr = IOSF_CPU_POWER_BUDGET_CTL_BYT, 119262306a36Sopenharmony_ci .check_unit = rapl_check_unit_atom, 119362306a36Sopenharmony_ci .set_floor_freq = set_floor_freq_atom, 119462306a36Sopenharmony_ci .compute_time_window = rapl_compute_time_window_atom, 119562306a36Sopenharmony_ci}; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_cistatic const struct rapl_defaults rapl_defaults_tng = { 119862306a36Sopenharmony_ci .floor_freq_reg_addr = IOSF_CPU_POWER_BUDGET_CTL_TNG, 119962306a36Sopenharmony_ci .check_unit = rapl_check_unit_atom, 120062306a36Sopenharmony_ci .set_floor_freq = set_floor_freq_atom, 120162306a36Sopenharmony_ci .compute_time_window = rapl_compute_time_window_atom, 120262306a36Sopenharmony_ci}; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_cistatic const struct rapl_defaults rapl_defaults_ann = { 120562306a36Sopenharmony_ci .floor_freq_reg_addr = 0, 120662306a36Sopenharmony_ci .check_unit = rapl_check_unit_atom, 120762306a36Sopenharmony_ci .set_floor_freq = NULL, 120862306a36Sopenharmony_ci .compute_time_window = rapl_compute_time_window_atom, 120962306a36Sopenharmony_ci}; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_cistatic const struct rapl_defaults rapl_defaults_cht = { 121262306a36Sopenharmony_ci .floor_freq_reg_addr = 0, 121362306a36Sopenharmony_ci .check_unit = rapl_check_unit_atom, 121462306a36Sopenharmony_ci .set_floor_freq = NULL, 121562306a36Sopenharmony_ci .compute_time_window = rapl_compute_time_window_atom, 121662306a36Sopenharmony_ci}; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_cistatic const struct rapl_defaults rapl_defaults_amd = { 121962306a36Sopenharmony_ci .check_unit = rapl_check_unit_core, 122062306a36Sopenharmony_ci}; 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_cistatic const struct x86_cpu_id rapl_ids[] __initconst = { 122362306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(SANDYBRIDGE, &rapl_defaults_core), 122462306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(SANDYBRIDGE_X, &rapl_defaults_core), 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(IVYBRIDGE, &rapl_defaults_core), 122762306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(IVYBRIDGE_X, &rapl_defaults_core), 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(HASWELL, &rapl_defaults_core), 123062306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(HASWELL_L, &rapl_defaults_core), 123162306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(HASWELL_G, &rapl_defaults_core), 123262306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(HASWELL_X, &rapl_defaults_hsw_server), 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(BROADWELL, &rapl_defaults_core), 123562306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_G, &rapl_defaults_core), 123662306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_D, &rapl_defaults_core), 123762306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_X, &rapl_defaults_hsw_server), 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE, &rapl_defaults_core), 124062306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_L, &rapl_defaults_core), 124162306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_X, &rapl_defaults_hsw_server), 124262306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE_L, &rapl_defaults_core), 124362306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE, &rapl_defaults_core), 124462306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(CANNONLAKE_L, &rapl_defaults_core), 124562306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_L, &rapl_defaults_core), 124662306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ICELAKE, &rapl_defaults_core), 124762306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_NNPI, &rapl_defaults_core), 124862306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_X, &rapl_defaults_hsw_server), 124962306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_D, &rapl_defaults_hsw_server), 125062306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE_L, &rapl_defaults_core), 125162306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE, &rapl_defaults_core), 125262306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE_L, &rapl_defaults_core), 125362306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE, &rapl_defaults_core), 125462306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ROCKETLAKE, &rapl_defaults_core), 125562306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, &rapl_defaults_core), 125662306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, &rapl_defaults_core), 125762306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_GRACEMONT, &rapl_defaults_core), 125862306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, &rapl_defaults_core), 125962306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, &rapl_defaults_core), 126062306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, &rapl_defaults_core), 126162306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE, &rapl_defaults_core), 126262306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE_L, &rapl_defaults_core), 126362306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(SAPPHIRERAPIDS_X, &rapl_defaults_spr_server), 126462306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(EMERALDRAPIDS_X, &rapl_defaults_spr_server), 126562306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(LAKEFIELD, &rapl_defaults_core), 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_SILVERMONT, &rapl_defaults_byt), 126862306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_AIRMONT, &rapl_defaults_cht), 126962306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_SILVERMONT_MID, &rapl_defaults_tng), 127062306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_AIRMONT_MID, &rapl_defaults_ann), 127162306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT, &rapl_defaults_core), 127262306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT_PLUS, &rapl_defaults_core), 127362306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT_D, &rapl_defaults_core), 127462306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT, &rapl_defaults_core), 127562306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_D, &rapl_defaults_core), 127662306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_L, &rapl_defaults_core), 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(XEON_PHI_KNL, &rapl_defaults_hsw_server), 127962306a36Sopenharmony_ci X86_MATCH_INTEL_FAM6_MODEL(XEON_PHI_KNM, &rapl_defaults_hsw_server), 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci X86_MATCH_VENDOR_FAM(AMD, 0x17, &rapl_defaults_amd), 128262306a36Sopenharmony_ci X86_MATCH_VENDOR_FAM(AMD, 0x19, &rapl_defaults_amd), 128362306a36Sopenharmony_ci X86_MATCH_VENDOR_FAM(HYGON, 0x18, &rapl_defaults_amd), 128462306a36Sopenharmony_ci {} 128562306a36Sopenharmony_ci}; 128662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(x86cpu, rapl_ids); 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci/* Read once for all raw primitive data for domains */ 128962306a36Sopenharmony_cistatic void rapl_update_domain_data(struct rapl_package *rp) 129062306a36Sopenharmony_ci{ 129162306a36Sopenharmony_ci int dmn, prim; 129262306a36Sopenharmony_ci u64 val; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci for (dmn = 0; dmn < rp->nr_domains; dmn++) { 129562306a36Sopenharmony_ci pr_debug("update %s domain %s data\n", rp->name, 129662306a36Sopenharmony_ci rp->domains[dmn].name); 129762306a36Sopenharmony_ci /* exclude non-raw primitives */ 129862306a36Sopenharmony_ci for (prim = 0; prim < NR_RAW_PRIMITIVES; prim++) { 129962306a36Sopenharmony_ci struct rapl_primitive_info *rpi = get_rpi(rp, prim); 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci if (!rapl_read_data_raw(&rp->domains[dmn], prim, 130262306a36Sopenharmony_ci rpi->unit, &val)) 130362306a36Sopenharmony_ci rp->domains[dmn].rdd.primitives[prim] = val; 130462306a36Sopenharmony_ci } 130562306a36Sopenharmony_ci } 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci} 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_cistatic int rapl_package_register_powercap(struct rapl_package *rp) 131062306a36Sopenharmony_ci{ 131162306a36Sopenharmony_ci struct rapl_domain *rd; 131262306a36Sopenharmony_ci struct powercap_zone *power_zone = NULL; 131362306a36Sopenharmony_ci int nr_pl, ret; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci /* Update the domain data of the new package */ 131662306a36Sopenharmony_ci rapl_update_domain_data(rp); 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci /* first we register package domain as the parent zone */ 131962306a36Sopenharmony_ci for (rd = rp->domains; rd < rp->domains + rp->nr_domains; rd++) { 132062306a36Sopenharmony_ci if (rd->id == RAPL_DOMAIN_PACKAGE) { 132162306a36Sopenharmony_ci nr_pl = find_nr_power_limit(rd); 132262306a36Sopenharmony_ci pr_debug("register package domain %s\n", rp->name); 132362306a36Sopenharmony_ci power_zone = powercap_register_zone(&rd->power_zone, 132462306a36Sopenharmony_ci rp->priv->control_type, rp->name, 132562306a36Sopenharmony_ci NULL, &zone_ops[rd->id], nr_pl, 132662306a36Sopenharmony_ci &constraint_ops); 132762306a36Sopenharmony_ci if (IS_ERR(power_zone)) { 132862306a36Sopenharmony_ci pr_debug("failed to register power zone %s\n", 132962306a36Sopenharmony_ci rp->name); 133062306a36Sopenharmony_ci return PTR_ERR(power_zone); 133162306a36Sopenharmony_ci } 133262306a36Sopenharmony_ci /* track parent zone in per package/socket data */ 133362306a36Sopenharmony_ci rp->power_zone = power_zone; 133462306a36Sopenharmony_ci /* done, only one package domain per socket */ 133562306a36Sopenharmony_ci break; 133662306a36Sopenharmony_ci } 133762306a36Sopenharmony_ci } 133862306a36Sopenharmony_ci if (!power_zone) { 133962306a36Sopenharmony_ci pr_err("no package domain found, unknown topology!\n"); 134062306a36Sopenharmony_ci return -ENODEV; 134162306a36Sopenharmony_ci } 134262306a36Sopenharmony_ci /* now register domains as children of the socket/package */ 134362306a36Sopenharmony_ci for (rd = rp->domains; rd < rp->domains + rp->nr_domains; rd++) { 134462306a36Sopenharmony_ci struct powercap_zone *parent = rp->power_zone; 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci if (rd->id == RAPL_DOMAIN_PACKAGE) 134762306a36Sopenharmony_ci continue; 134862306a36Sopenharmony_ci if (rd->id == RAPL_DOMAIN_PLATFORM) 134962306a36Sopenharmony_ci parent = NULL; 135062306a36Sopenharmony_ci /* number of power limits per domain varies */ 135162306a36Sopenharmony_ci nr_pl = find_nr_power_limit(rd); 135262306a36Sopenharmony_ci power_zone = powercap_register_zone(&rd->power_zone, 135362306a36Sopenharmony_ci rp->priv->control_type, 135462306a36Sopenharmony_ci rd->name, parent, 135562306a36Sopenharmony_ci &zone_ops[rd->id], nr_pl, 135662306a36Sopenharmony_ci &constraint_ops); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci if (IS_ERR(power_zone)) { 135962306a36Sopenharmony_ci pr_debug("failed to register power_zone, %s:%s\n", 136062306a36Sopenharmony_ci rp->name, rd->name); 136162306a36Sopenharmony_ci ret = PTR_ERR(power_zone); 136262306a36Sopenharmony_ci goto err_cleanup; 136362306a36Sopenharmony_ci } 136462306a36Sopenharmony_ci } 136562306a36Sopenharmony_ci return 0; 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_cierr_cleanup: 136862306a36Sopenharmony_ci /* 136962306a36Sopenharmony_ci * Clean up previously initialized domains within the package if we 137062306a36Sopenharmony_ci * failed after the first domain setup. 137162306a36Sopenharmony_ci */ 137262306a36Sopenharmony_ci while (--rd >= rp->domains) { 137362306a36Sopenharmony_ci pr_debug("unregister %s domain %s\n", rp->name, rd->name); 137462306a36Sopenharmony_ci powercap_unregister_zone(rp->priv->control_type, 137562306a36Sopenharmony_ci &rd->power_zone); 137662306a36Sopenharmony_ci } 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci return ret; 137962306a36Sopenharmony_ci} 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_cistatic int rapl_check_domain(int domain, struct rapl_package *rp) 138262306a36Sopenharmony_ci{ 138362306a36Sopenharmony_ci struct reg_action ra; 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci switch (domain) { 138662306a36Sopenharmony_ci case RAPL_DOMAIN_PACKAGE: 138762306a36Sopenharmony_ci case RAPL_DOMAIN_PP0: 138862306a36Sopenharmony_ci case RAPL_DOMAIN_PP1: 138962306a36Sopenharmony_ci case RAPL_DOMAIN_DRAM: 139062306a36Sopenharmony_ci case RAPL_DOMAIN_PLATFORM: 139162306a36Sopenharmony_ci ra.reg = rp->priv->regs[domain][RAPL_DOMAIN_REG_STATUS]; 139262306a36Sopenharmony_ci break; 139362306a36Sopenharmony_ci default: 139462306a36Sopenharmony_ci pr_err("invalid domain id %d\n", domain); 139562306a36Sopenharmony_ci return -EINVAL; 139662306a36Sopenharmony_ci } 139762306a36Sopenharmony_ci /* make sure domain counters are available and contains non-zero 139862306a36Sopenharmony_ci * values, otherwise skip it. 139962306a36Sopenharmony_ci */ 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci ra.mask = ENERGY_STATUS_MASK; 140262306a36Sopenharmony_ci if (rp->priv->read_raw(get_rid(rp), &ra) || !ra.value) 140362306a36Sopenharmony_ci return -ENODEV; 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci return 0; 140662306a36Sopenharmony_ci} 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci/* 140962306a36Sopenharmony_ci * Get per domain energy/power/time unit. 141062306a36Sopenharmony_ci * RAPL Interfaces without per domain unit register will use the package 141162306a36Sopenharmony_ci * scope unit register to set per domain units. 141262306a36Sopenharmony_ci */ 141362306a36Sopenharmony_cistatic int rapl_get_domain_unit(struct rapl_domain *rd) 141462306a36Sopenharmony_ci{ 141562306a36Sopenharmony_ci struct rapl_defaults *defaults = get_defaults(rd->rp); 141662306a36Sopenharmony_ci int ret; 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci if (!rd->regs[RAPL_DOMAIN_REG_UNIT].val) { 141962306a36Sopenharmony_ci if (!rd->rp->priv->reg_unit.val) { 142062306a36Sopenharmony_ci pr_err("No valid Unit register found\n"); 142162306a36Sopenharmony_ci return -ENODEV; 142262306a36Sopenharmony_ci } 142362306a36Sopenharmony_ci rd->regs[RAPL_DOMAIN_REG_UNIT] = rd->rp->priv->reg_unit; 142462306a36Sopenharmony_ci } 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci if (!defaults->check_unit) { 142762306a36Sopenharmony_ci pr_err("missing .check_unit() callback\n"); 142862306a36Sopenharmony_ci return -ENODEV; 142962306a36Sopenharmony_ci } 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci ret = defaults->check_unit(rd); 143262306a36Sopenharmony_ci if (ret) 143362306a36Sopenharmony_ci return ret; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci if (rd->id == RAPL_DOMAIN_DRAM && defaults->dram_domain_energy_unit) 143662306a36Sopenharmony_ci rd->energy_unit = defaults->dram_domain_energy_unit; 143762306a36Sopenharmony_ci if (rd->id == RAPL_DOMAIN_PLATFORM && defaults->psys_domain_energy_unit) 143862306a36Sopenharmony_ci rd->energy_unit = defaults->psys_domain_energy_unit; 143962306a36Sopenharmony_ci return 0; 144062306a36Sopenharmony_ci} 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci/* 144362306a36Sopenharmony_ci * Check if power limits are available. Two cases when they are not available: 144462306a36Sopenharmony_ci * 1. Locked by BIOS, in this case we still provide read-only access so that 144562306a36Sopenharmony_ci * users can see what limit is set by the BIOS. 144662306a36Sopenharmony_ci * 2. Some CPUs make some domains monitoring only which means PLx MSRs may not 144762306a36Sopenharmony_ci * exist at all. In this case, we do not show the constraints in powercap. 144862306a36Sopenharmony_ci * 144962306a36Sopenharmony_ci * Called after domains are detected and initialized. 145062306a36Sopenharmony_ci */ 145162306a36Sopenharmony_cistatic void rapl_detect_powerlimit(struct rapl_domain *rd) 145262306a36Sopenharmony_ci{ 145362306a36Sopenharmony_ci u64 val64; 145462306a36Sopenharmony_ci int i; 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci for (i = POWER_LIMIT1; i < NR_POWER_LIMITS; i++) { 145762306a36Sopenharmony_ci if (!rapl_read_pl_data(rd, i, PL_LOCK, false, &val64)) { 145862306a36Sopenharmony_ci if (val64) { 145962306a36Sopenharmony_ci rd->rpl[i].locked = true; 146062306a36Sopenharmony_ci pr_info("%s:%s:%s locked by BIOS\n", 146162306a36Sopenharmony_ci rd->rp->name, rd->name, pl_names[i]); 146262306a36Sopenharmony_ci } 146362306a36Sopenharmony_ci } 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci if (rapl_read_pl_data(rd, i, PL_LIMIT, false, &val64)) 146662306a36Sopenharmony_ci rd->rpl[i].name = NULL; 146762306a36Sopenharmony_ci } 146862306a36Sopenharmony_ci} 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci/* Detect active and valid domains for the given CPU, caller must 147162306a36Sopenharmony_ci * ensure the CPU belongs to the targeted package and CPU hotlug is disabled. 147262306a36Sopenharmony_ci */ 147362306a36Sopenharmony_cistatic int rapl_detect_domains(struct rapl_package *rp) 147462306a36Sopenharmony_ci{ 147562306a36Sopenharmony_ci struct rapl_domain *rd; 147662306a36Sopenharmony_ci int i; 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci for (i = 0; i < RAPL_DOMAIN_MAX; i++) { 147962306a36Sopenharmony_ci /* use physical package id to read counters */ 148062306a36Sopenharmony_ci if (!rapl_check_domain(i, rp)) { 148162306a36Sopenharmony_ci rp->domain_map |= 1 << i; 148262306a36Sopenharmony_ci pr_info("Found RAPL domain %s\n", rapl_domain_names[i]); 148362306a36Sopenharmony_ci } 148462306a36Sopenharmony_ci } 148562306a36Sopenharmony_ci rp->nr_domains = bitmap_weight(&rp->domain_map, RAPL_DOMAIN_MAX); 148662306a36Sopenharmony_ci if (!rp->nr_domains) { 148762306a36Sopenharmony_ci pr_debug("no valid rapl domains found in %s\n", rp->name); 148862306a36Sopenharmony_ci return -ENODEV; 148962306a36Sopenharmony_ci } 149062306a36Sopenharmony_ci pr_debug("found %d domains on %s\n", rp->nr_domains, rp->name); 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci rp->domains = kcalloc(rp->nr_domains, sizeof(struct rapl_domain), 149362306a36Sopenharmony_ci GFP_KERNEL); 149462306a36Sopenharmony_ci if (!rp->domains) 149562306a36Sopenharmony_ci return -ENOMEM; 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci rapl_init_domains(rp); 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci for (rd = rp->domains; rd < rp->domains + rp->nr_domains; rd++) { 150062306a36Sopenharmony_ci rapl_get_domain_unit(rd); 150162306a36Sopenharmony_ci rapl_detect_powerlimit(rd); 150262306a36Sopenharmony_ci } 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci return 0; 150562306a36Sopenharmony_ci} 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci/* called from CPU hotplug notifier, hotplug lock held */ 150862306a36Sopenharmony_civoid rapl_remove_package_cpuslocked(struct rapl_package *rp) 150962306a36Sopenharmony_ci{ 151062306a36Sopenharmony_ci struct rapl_domain *rd, *rd_package = NULL; 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci package_power_limit_irq_restore(rp); 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci for (rd = rp->domains; rd < rp->domains + rp->nr_domains; rd++) { 151562306a36Sopenharmony_ci int i; 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci for (i = POWER_LIMIT1; i < NR_POWER_LIMITS; i++) { 151862306a36Sopenharmony_ci rapl_write_pl_data(rd, i, PL_ENABLE, 0); 151962306a36Sopenharmony_ci rapl_write_pl_data(rd, i, PL_CLAMP, 0); 152062306a36Sopenharmony_ci } 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci if (rd->id == RAPL_DOMAIN_PACKAGE) { 152362306a36Sopenharmony_ci rd_package = rd; 152462306a36Sopenharmony_ci continue; 152562306a36Sopenharmony_ci } 152662306a36Sopenharmony_ci pr_debug("remove package, undo power limit on %s: %s\n", 152762306a36Sopenharmony_ci rp->name, rd->name); 152862306a36Sopenharmony_ci powercap_unregister_zone(rp->priv->control_type, 152962306a36Sopenharmony_ci &rd->power_zone); 153062306a36Sopenharmony_ci } 153162306a36Sopenharmony_ci /* do parent zone last */ 153262306a36Sopenharmony_ci powercap_unregister_zone(rp->priv->control_type, 153362306a36Sopenharmony_ci &rd_package->power_zone); 153462306a36Sopenharmony_ci list_del(&rp->plist); 153562306a36Sopenharmony_ci kfree(rp); 153662306a36Sopenharmony_ci} 153762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rapl_remove_package_cpuslocked); 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_civoid rapl_remove_package(struct rapl_package *rp) 154062306a36Sopenharmony_ci{ 154162306a36Sopenharmony_ci guard(cpus_read_lock)(); 154262306a36Sopenharmony_ci rapl_remove_package_cpuslocked(rp); 154362306a36Sopenharmony_ci} 154462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rapl_remove_package); 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci/* caller to ensure CPU hotplug lock is held */ 154762306a36Sopenharmony_cistruct rapl_package *rapl_find_package_domain_cpuslocked(int id, struct rapl_if_priv *priv, 154862306a36Sopenharmony_ci bool id_is_cpu) 154962306a36Sopenharmony_ci{ 155062306a36Sopenharmony_ci struct rapl_package *rp; 155162306a36Sopenharmony_ci int uid; 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci if (id_is_cpu) 155462306a36Sopenharmony_ci uid = topology_logical_die_id(id); 155562306a36Sopenharmony_ci else 155662306a36Sopenharmony_ci uid = id; 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci list_for_each_entry(rp, &rapl_packages, plist) { 155962306a36Sopenharmony_ci if (rp->id == uid 156062306a36Sopenharmony_ci && rp->priv->control_type == priv->control_type) 156162306a36Sopenharmony_ci return rp; 156262306a36Sopenharmony_ci } 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci return NULL; 156562306a36Sopenharmony_ci} 156662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rapl_find_package_domain_cpuslocked); 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_cistruct rapl_package *rapl_find_package_domain(int id, struct rapl_if_priv *priv, bool id_is_cpu) 156962306a36Sopenharmony_ci{ 157062306a36Sopenharmony_ci guard(cpus_read_lock)(); 157162306a36Sopenharmony_ci return rapl_find_package_domain_cpuslocked(id, priv, id_is_cpu); 157262306a36Sopenharmony_ci} 157362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rapl_find_package_domain); 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci/* called from CPU hotplug notifier, hotplug lock held */ 157662306a36Sopenharmony_cistruct rapl_package *rapl_add_package_cpuslocked(int id, struct rapl_if_priv *priv, bool id_is_cpu) 157762306a36Sopenharmony_ci{ 157862306a36Sopenharmony_ci struct rapl_package *rp; 157962306a36Sopenharmony_ci int ret; 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci rp = kzalloc(sizeof(struct rapl_package), GFP_KERNEL); 158262306a36Sopenharmony_ci if (!rp) 158362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci if (id_is_cpu) { 158662306a36Sopenharmony_ci rp->id = topology_logical_die_id(id); 158762306a36Sopenharmony_ci rp->lead_cpu = id; 158862306a36Sopenharmony_ci if (topology_max_die_per_package() > 1) 158962306a36Sopenharmony_ci snprintf(rp->name, PACKAGE_DOMAIN_NAME_LENGTH, "package-%d-die-%d", 159062306a36Sopenharmony_ci topology_physical_package_id(id), topology_die_id(id)); 159162306a36Sopenharmony_ci else 159262306a36Sopenharmony_ci snprintf(rp->name, PACKAGE_DOMAIN_NAME_LENGTH, "package-%d", 159362306a36Sopenharmony_ci topology_physical_package_id(id)); 159462306a36Sopenharmony_ci } else { 159562306a36Sopenharmony_ci rp->id = id; 159662306a36Sopenharmony_ci rp->lead_cpu = -1; 159762306a36Sopenharmony_ci snprintf(rp->name, PACKAGE_DOMAIN_NAME_LENGTH, "package-%d", id); 159862306a36Sopenharmony_ci } 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci rp->priv = priv; 160162306a36Sopenharmony_ci ret = rapl_config(rp); 160262306a36Sopenharmony_ci if (ret) 160362306a36Sopenharmony_ci goto err_free_package; 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci /* check if the package contains valid domains */ 160662306a36Sopenharmony_ci if (rapl_detect_domains(rp)) { 160762306a36Sopenharmony_ci ret = -ENODEV; 160862306a36Sopenharmony_ci goto err_free_package; 160962306a36Sopenharmony_ci } 161062306a36Sopenharmony_ci ret = rapl_package_register_powercap(rp); 161162306a36Sopenharmony_ci if (!ret) { 161262306a36Sopenharmony_ci INIT_LIST_HEAD(&rp->plist); 161362306a36Sopenharmony_ci list_add(&rp->plist, &rapl_packages); 161462306a36Sopenharmony_ci return rp; 161562306a36Sopenharmony_ci } 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_cierr_free_package: 161862306a36Sopenharmony_ci kfree(rp->domains); 161962306a36Sopenharmony_ci kfree(rp); 162062306a36Sopenharmony_ci return ERR_PTR(ret); 162162306a36Sopenharmony_ci} 162262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rapl_add_package_cpuslocked); 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_cistruct rapl_package *rapl_add_package(int id, struct rapl_if_priv *priv, bool id_is_cpu) 162562306a36Sopenharmony_ci{ 162662306a36Sopenharmony_ci guard(cpus_read_lock)(); 162762306a36Sopenharmony_ci return rapl_add_package_cpuslocked(id, priv, id_is_cpu); 162862306a36Sopenharmony_ci} 162962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rapl_add_package); 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_cistatic void power_limit_state_save(void) 163262306a36Sopenharmony_ci{ 163362306a36Sopenharmony_ci struct rapl_package *rp; 163462306a36Sopenharmony_ci struct rapl_domain *rd; 163562306a36Sopenharmony_ci int ret, i; 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci cpus_read_lock(); 163862306a36Sopenharmony_ci list_for_each_entry(rp, &rapl_packages, plist) { 163962306a36Sopenharmony_ci if (!rp->power_zone) 164062306a36Sopenharmony_ci continue; 164162306a36Sopenharmony_ci rd = power_zone_to_rapl_domain(rp->power_zone); 164262306a36Sopenharmony_ci for (i = POWER_LIMIT1; i < NR_POWER_LIMITS; i++) { 164362306a36Sopenharmony_ci ret = rapl_read_pl_data(rd, i, PL_LIMIT, true, 164462306a36Sopenharmony_ci &rd->rpl[i].last_power_limit); 164562306a36Sopenharmony_ci if (ret) 164662306a36Sopenharmony_ci rd->rpl[i].last_power_limit = 0; 164762306a36Sopenharmony_ci } 164862306a36Sopenharmony_ci } 164962306a36Sopenharmony_ci cpus_read_unlock(); 165062306a36Sopenharmony_ci} 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_cistatic void power_limit_state_restore(void) 165362306a36Sopenharmony_ci{ 165462306a36Sopenharmony_ci struct rapl_package *rp; 165562306a36Sopenharmony_ci struct rapl_domain *rd; 165662306a36Sopenharmony_ci int i; 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci cpus_read_lock(); 165962306a36Sopenharmony_ci list_for_each_entry(rp, &rapl_packages, plist) { 166062306a36Sopenharmony_ci if (!rp->power_zone) 166162306a36Sopenharmony_ci continue; 166262306a36Sopenharmony_ci rd = power_zone_to_rapl_domain(rp->power_zone); 166362306a36Sopenharmony_ci for (i = POWER_LIMIT1; i < NR_POWER_LIMITS; i++) 166462306a36Sopenharmony_ci if (rd->rpl[i].last_power_limit) 166562306a36Sopenharmony_ci rapl_write_pl_data(rd, i, PL_LIMIT, 166662306a36Sopenharmony_ci rd->rpl[i].last_power_limit); 166762306a36Sopenharmony_ci } 166862306a36Sopenharmony_ci cpus_read_unlock(); 166962306a36Sopenharmony_ci} 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_cistatic int rapl_pm_callback(struct notifier_block *nb, 167262306a36Sopenharmony_ci unsigned long mode, void *_unused) 167362306a36Sopenharmony_ci{ 167462306a36Sopenharmony_ci switch (mode) { 167562306a36Sopenharmony_ci case PM_SUSPEND_PREPARE: 167662306a36Sopenharmony_ci power_limit_state_save(); 167762306a36Sopenharmony_ci break; 167862306a36Sopenharmony_ci case PM_POST_SUSPEND: 167962306a36Sopenharmony_ci power_limit_state_restore(); 168062306a36Sopenharmony_ci break; 168162306a36Sopenharmony_ci } 168262306a36Sopenharmony_ci return NOTIFY_OK; 168362306a36Sopenharmony_ci} 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_cistatic struct notifier_block rapl_pm_notifier = { 168662306a36Sopenharmony_ci .notifier_call = rapl_pm_callback, 168762306a36Sopenharmony_ci}; 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_cistatic struct platform_device *rapl_msr_platdev; 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_cistatic int __init rapl_init(void) 169262306a36Sopenharmony_ci{ 169362306a36Sopenharmony_ci const struct x86_cpu_id *id; 169462306a36Sopenharmony_ci int ret; 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci id = x86_match_cpu(rapl_ids); 169762306a36Sopenharmony_ci if (id) { 169862306a36Sopenharmony_ci defaults_msr = (struct rapl_defaults *)id->driver_data; 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci rapl_msr_platdev = platform_device_alloc("intel_rapl_msr", 0); 170162306a36Sopenharmony_ci if (!rapl_msr_platdev) 170262306a36Sopenharmony_ci return -ENOMEM; 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci ret = platform_device_add(rapl_msr_platdev); 170562306a36Sopenharmony_ci if (ret) { 170662306a36Sopenharmony_ci platform_device_put(rapl_msr_platdev); 170762306a36Sopenharmony_ci return ret; 170862306a36Sopenharmony_ci } 170962306a36Sopenharmony_ci } 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci ret = register_pm_notifier(&rapl_pm_notifier); 171262306a36Sopenharmony_ci if (ret && rapl_msr_platdev) { 171362306a36Sopenharmony_ci platform_device_del(rapl_msr_platdev); 171462306a36Sopenharmony_ci platform_device_put(rapl_msr_platdev); 171562306a36Sopenharmony_ci } 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci return ret; 171862306a36Sopenharmony_ci} 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_cistatic void __exit rapl_exit(void) 172162306a36Sopenharmony_ci{ 172262306a36Sopenharmony_ci platform_device_unregister(rapl_msr_platdev); 172362306a36Sopenharmony_ci unregister_pm_notifier(&rapl_pm_notifier); 172462306a36Sopenharmony_ci} 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_cifs_initcall(rapl_init); 172762306a36Sopenharmony_cimodule_exit(rapl_exit); 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ciMODULE_DESCRIPTION("Intel Runtime Average Power Limit (RAPL) common code"); 173062306a36Sopenharmony_ciMODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com>"); 173162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1732