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