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