162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright © 2022 Intel Corporation
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/hwmon.h>
762306a36Sopenharmony_ci#include <linux/hwmon-sysfs.h>
862306a36Sopenharmony_ci#include <linux/types.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "i915_drv.h"
1162306a36Sopenharmony_ci#include "i915_hwmon.h"
1262306a36Sopenharmony_ci#include "i915_reg.h"
1362306a36Sopenharmony_ci#include "intel_mchbar_regs.h"
1462306a36Sopenharmony_ci#include "intel_pcode.h"
1562306a36Sopenharmony_ci#include "gt/intel_gt.h"
1662306a36Sopenharmony_ci#include "gt/intel_gt_regs.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/*
1962306a36Sopenharmony_ci * SF_* - scale factors for particular quantities according to hwmon spec.
2062306a36Sopenharmony_ci * - voltage  - millivolts
2162306a36Sopenharmony_ci * - power  - microwatts
2262306a36Sopenharmony_ci * - curr   - milliamperes
2362306a36Sopenharmony_ci * - energy - microjoules
2462306a36Sopenharmony_ci * - time   - milliseconds
2562306a36Sopenharmony_ci */
2662306a36Sopenharmony_ci#define SF_VOLTAGE	1000
2762306a36Sopenharmony_ci#define SF_POWER	1000000
2862306a36Sopenharmony_ci#define SF_CURR		1000
2962306a36Sopenharmony_ci#define SF_ENERGY	1000000
3062306a36Sopenharmony_ci#define SF_TIME		1000
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistruct hwm_reg {
3362306a36Sopenharmony_ci	i915_reg_t gt_perf_status;
3462306a36Sopenharmony_ci	i915_reg_t pkg_power_sku_unit;
3562306a36Sopenharmony_ci	i915_reg_t pkg_power_sku;
3662306a36Sopenharmony_ci	i915_reg_t pkg_rapl_limit;
3762306a36Sopenharmony_ci	i915_reg_t energy_status_all;
3862306a36Sopenharmony_ci	i915_reg_t energy_status_tile;
3962306a36Sopenharmony_ci};
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistruct hwm_energy_info {
4262306a36Sopenharmony_ci	u32 reg_val_prev;
4362306a36Sopenharmony_ci	long accum_energy;			/* Accumulated energy for energy1_input */
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistruct hwm_drvdata {
4762306a36Sopenharmony_ci	struct i915_hwmon *hwmon;
4862306a36Sopenharmony_ci	struct intel_uncore *uncore;
4962306a36Sopenharmony_ci	struct device *hwmon_dev;
5062306a36Sopenharmony_ci	struct hwm_energy_info ei;		/*  Energy info for energy1_input */
5162306a36Sopenharmony_ci	char name[12];
5262306a36Sopenharmony_ci	int gt_n;
5362306a36Sopenharmony_ci	bool reset_in_progress;
5462306a36Sopenharmony_ci	wait_queue_head_t waitq;
5562306a36Sopenharmony_ci};
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistruct i915_hwmon {
5862306a36Sopenharmony_ci	struct hwm_drvdata ddat;
5962306a36Sopenharmony_ci	struct hwm_drvdata ddat_gt[I915_MAX_GT];
6062306a36Sopenharmony_ci	struct mutex hwmon_lock;		/* counter overflow logic and rmw */
6162306a36Sopenharmony_ci	struct hwm_reg rg;
6262306a36Sopenharmony_ci	int scl_shift_power;
6362306a36Sopenharmony_ci	int scl_shift_energy;
6462306a36Sopenharmony_ci	int scl_shift_time;
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic void
6862306a36Sopenharmony_cihwm_locked_with_pm_intel_uncore_rmw(struct hwm_drvdata *ddat,
6962306a36Sopenharmony_ci				    i915_reg_t reg, u32 clear, u32 set)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	struct i915_hwmon *hwmon = ddat->hwmon;
7262306a36Sopenharmony_ci	struct intel_uncore *uncore = ddat->uncore;
7362306a36Sopenharmony_ci	intel_wakeref_t wakeref;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	mutex_lock(&hwmon->hwmon_lock);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	with_intel_runtime_pm(uncore->rpm, wakeref)
7862306a36Sopenharmony_ci		intel_uncore_rmw(uncore, reg, clear, set);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	mutex_unlock(&hwmon->hwmon_lock);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/*
8462306a36Sopenharmony_ci * This function's return type of u64 allows for the case where the scaling
8562306a36Sopenharmony_ci * of the field taken from the 32-bit register value might cause a result to
8662306a36Sopenharmony_ci * exceed 32 bits.
8762306a36Sopenharmony_ci */
8862306a36Sopenharmony_cistatic u64
8962306a36Sopenharmony_cihwm_field_read_and_scale(struct hwm_drvdata *ddat, i915_reg_t rgadr,
9062306a36Sopenharmony_ci			 u32 field_msk, int nshift, u32 scale_factor)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	struct intel_uncore *uncore = ddat->uncore;
9362306a36Sopenharmony_ci	intel_wakeref_t wakeref;
9462306a36Sopenharmony_ci	u32 reg_value;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	with_intel_runtime_pm(uncore->rpm, wakeref)
9762306a36Sopenharmony_ci		reg_value = intel_uncore_read(uncore, rgadr);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	reg_value = REG_FIELD_GET(field_msk, reg_value);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	return mul_u64_u32_shr(reg_value, scale_factor, nshift);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/*
10562306a36Sopenharmony_ci * hwm_energy - Obtain energy value
10662306a36Sopenharmony_ci *
10762306a36Sopenharmony_ci * The underlying energy hardware register is 32-bits and is subject to
10862306a36Sopenharmony_ci * overflow. How long before overflow? For example, with an example
10962306a36Sopenharmony_ci * scaling bit shift of 14 bits (see register *PACKAGE_POWER_SKU_UNIT) and
11062306a36Sopenharmony_ci * a power draw of 1000 watts, the 32-bit counter will overflow in
11162306a36Sopenharmony_ci * approximately 4.36 minutes.
11262306a36Sopenharmony_ci *
11362306a36Sopenharmony_ci * Examples:
11462306a36Sopenharmony_ci *    1 watt:  (2^32 >> 14) /    1 W / (60 * 60 * 24) secs/day -> 3 days
11562306a36Sopenharmony_ci * 1000 watts: (2^32 >> 14) / 1000 W / 60             secs/min -> 4.36 minutes
11662306a36Sopenharmony_ci *
11762306a36Sopenharmony_ci * The function significantly increases overflow duration (from 4.36
11862306a36Sopenharmony_ci * minutes) by accumulating the energy register into a 'long' as allowed by
11962306a36Sopenharmony_ci * the hwmon API. Using x86_64 128 bit arithmetic (see mul_u64_u32_shr()),
12062306a36Sopenharmony_ci * a 'long' of 63 bits, SF_ENERGY of 1e6 (~20 bits) and
12162306a36Sopenharmony_ci * hwmon->scl_shift_energy of 14 bits we have 57 (63 - 20 + 14) bits before
12262306a36Sopenharmony_ci * energy1_input overflows. This at 1000 W is an overflow duration of 278 years.
12362306a36Sopenharmony_ci */
12462306a36Sopenharmony_cistatic void
12562306a36Sopenharmony_cihwm_energy(struct hwm_drvdata *ddat, long *energy)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	struct intel_uncore *uncore = ddat->uncore;
12862306a36Sopenharmony_ci	struct i915_hwmon *hwmon = ddat->hwmon;
12962306a36Sopenharmony_ci	struct hwm_energy_info *ei = &ddat->ei;
13062306a36Sopenharmony_ci	intel_wakeref_t wakeref;
13162306a36Sopenharmony_ci	i915_reg_t rgaddr;
13262306a36Sopenharmony_ci	u32 reg_val;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	if (ddat->gt_n >= 0)
13562306a36Sopenharmony_ci		rgaddr = hwmon->rg.energy_status_tile;
13662306a36Sopenharmony_ci	else
13762306a36Sopenharmony_ci		rgaddr = hwmon->rg.energy_status_all;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	mutex_lock(&hwmon->hwmon_lock);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	with_intel_runtime_pm(uncore->rpm, wakeref)
14262306a36Sopenharmony_ci		reg_val = intel_uncore_read(uncore, rgaddr);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	if (reg_val >= ei->reg_val_prev)
14562306a36Sopenharmony_ci		ei->accum_energy += reg_val - ei->reg_val_prev;
14662306a36Sopenharmony_ci	else
14762306a36Sopenharmony_ci		ei->accum_energy += UINT_MAX - ei->reg_val_prev + reg_val;
14862306a36Sopenharmony_ci	ei->reg_val_prev = reg_val;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	*energy = mul_u64_u32_shr(ei->accum_energy, SF_ENERGY,
15162306a36Sopenharmony_ci				  hwmon->scl_shift_energy);
15262306a36Sopenharmony_ci	mutex_unlock(&hwmon->hwmon_lock);
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic ssize_t
15662306a36Sopenharmony_cihwm_power1_max_interval_show(struct device *dev, struct device_attribute *attr,
15762306a36Sopenharmony_ci			     char *buf)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	struct hwm_drvdata *ddat = dev_get_drvdata(dev);
16062306a36Sopenharmony_ci	struct i915_hwmon *hwmon = ddat->hwmon;
16162306a36Sopenharmony_ci	intel_wakeref_t wakeref;
16262306a36Sopenharmony_ci	u32 r, x, y, x_w = 2; /* 2 bits */
16362306a36Sopenharmony_ci	u64 tau4, out;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	with_intel_runtime_pm(ddat->uncore->rpm, wakeref)
16662306a36Sopenharmony_ci		r = intel_uncore_read(ddat->uncore, hwmon->rg.pkg_rapl_limit);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	x = REG_FIELD_GET(PKG_PWR_LIM_1_TIME_X, r);
16962306a36Sopenharmony_ci	y = REG_FIELD_GET(PKG_PWR_LIM_1_TIME_Y, r);
17062306a36Sopenharmony_ci	/*
17162306a36Sopenharmony_ci	 * tau = 1.x * power(2,y), x = bits(23:22), y = bits(21:17)
17262306a36Sopenharmony_ci	 *     = (4 | x) << (y - 2)
17362306a36Sopenharmony_ci	 * where (y - 2) ensures a 1.x fixed point representation of 1.x
17462306a36Sopenharmony_ci	 * However because y can be < 2, we compute
17562306a36Sopenharmony_ci	 *     tau4 = (4 | x) << y
17662306a36Sopenharmony_ci	 * but add 2 when doing the final right shift to account for units
17762306a36Sopenharmony_ci	 */
17862306a36Sopenharmony_ci	tau4 = (u64)((1 << x_w) | x) << y;
17962306a36Sopenharmony_ci	/* val in hwmon interface units (millisec) */
18062306a36Sopenharmony_ci	out = mul_u64_u32_shr(tau4, SF_TIME, hwmon->scl_shift_time + x_w);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	return sysfs_emit(buf, "%llu\n", out);
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic ssize_t
18662306a36Sopenharmony_cihwm_power1_max_interval_store(struct device *dev,
18762306a36Sopenharmony_ci			      struct device_attribute *attr,
18862306a36Sopenharmony_ci			      const char *buf, size_t count)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	struct hwm_drvdata *ddat = dev_get_drvdata(dev);
19162306a36Sopenharmony_ci	struct i915_hwmon *hwmon = ddat->hwmon;
19262306a36Sopenharmony_ci	u32 x, y, rxy, x_w = 2; /* 2 bits */
19362306a36Sopenharmony_ci	u64 tau4, r, max_win;
19462306a36Sopenharmony_ci	unsigned long val;
19562306a36Sopenharmony_ci	int ret;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	ret = kstrtoul(buf, 0, &val);
19862306a36Sopenharmony_ci	if (ret)
19962306a36Sopenharmony_ci		return ret;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	/*
20262306a36Sopenharmony_ci	 * Max HW supported tau in '1.x * power(2,y)' format, x = 0, y = 0x12
20362306a36Sopenharmony_ci	 * The hwmon->scl_shift_time default of 0xa results in a max tau of 256 seconds
20462306a36Sopenharmony_ci	 */
20562306a36Sopenharmony_ci#define PKG_MAX_WIN_DEFAULT 0x12ull
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	/*
20862306a36Sopenharmony_ci	 * val must be < max in hwmon interface units. The steps below are
20962306a36Sopenharmony_ci	 * explained in i915_power1_max_interval_show()
21062306a36Sopenharmony_ci	 */
21162306a36Sopenharmony_ci	r = FIELD_PREP(PKG_MAX_WIN, PKG_MAX_WIN_DEFAULT);
21262306a36Sopenharmony_ci	x = REG_FIELD_GET(PKG_MAX_WIN_X, r);
21362306a36Sopenharmony_ci	y = REG_FIELD_GET(PKG_MAX_WIN_Y, r);
21462306a36Sopenharmony_ci	tau4 = (u64)((1 << x_w) | x) << y;
21562306a36Sopenharmony_ci	max_win = mul_u64_u32_shr(tau4, SF_TIME, hwmon->scl_shift_time + x_w);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	if (val > max_win)
21862306a36Sopenharmony_ci		return -EINVAL;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/* val in hw units */
22162306a36Sopenharmony_ci	val = DIV_ROUND_CLOSEST_ULL((u64)val << hwmon->scl_shift_time, SF_TIME);
22262306a36Sopenharmony_ci	/* Convert to 1.x * power(2,y) */
22362306a36Sopenharmony_ci	if (!val) {
22462306a36Sopenharmony_ci		/* Avoid ilog2(0) */
22562306a36Sopenharmony_ci		y = 0;
22662306a36Sopenharmony_ci		x = 0;
22762306a36Sopenharmony_ci	} else {
22862306a36Sopenharmony_ci		y = ilog2(val);
22962306a36Sopenharmony_ci		/* x = (val - (1 << y)) >> (y - 2); */
23062306a36Sopenharmony_ci		x = (val - (1ul << y)) << x_w >> y;
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	rxy = REG_FIELD_PREP(PKG_PWR_LIM_1_TIME_X, x) | REG_FIELD_PREP(PKG_PWR_LIM_1_TIME_Y, y);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	hwm_locked_with_pm_intel_uncore_rmw(ddat, hwmon->rg.pkg_rapl_limit,
23662306a36Sopenharmony_ci					    PKG_PWR_LIM_1_TIME, rxy);
23762306a36Sopenharmony_ci	return count;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(power1_max_interval, 0664,
24162306a36Sopenharmony_ci			  hwm_power1_max_interval_show,
24262306a36Sopenharmony_ci			  hwm_power1_max_interval_store, 0);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic struct attribute *hwm_attributes[] = {
24562306a36Sopenharmony_ci	&sensor_dev_attr_power1_max_interval.dev_attr.attr,
24662306a36Sopenharmony_ci	NULL
24762306a36Sopenharmony_ci};
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic umode_t hwm_attributes_visible(struct kobject *kobj,
25062306a36Sopenharmony_ci				      struct attribute *attr, int index)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj);
25362306a36Sopenharmony_ci	struct hwm_drvdata *ddat = dev_get_drvdata(dev);
25462306a36Sopenharmony_ci	struct i915_hwmon *hwmon = ddat->hwmon;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	if (attr == &sensor_dev_attr_power1_max_interval.dev_attr.attr)
25762306a36Sopenharmony_ci		return i915_mmio_reg_valid(hwmon->rg.pkg_rapl_limit) ? attr->mode : 0;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	return 0;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic const struct attribute_group hwm_attrgroup = {
26362306a36Sopenharmony_ci	.attrs = hwm_attributes,
26462306a36Sopenharmony_ci	.is_visible = hwm_attributes_visible,
26562306a36Sopenharmony_ci};
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic const struct attribute_group *hwm_groups[] = {
26862306a36Sopenharmony_ci	&hwm_attrgroup,
26962306a36Sopenharmony_ci	NULL
27062306a36Sopenharmony_ci};
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic const struct hwmon_channel_info * const hwm_info[] = {
27362306a36Sopenharmony_ci	HWMON_CHANNEL_INFO(in, HWMON_I_INPUT),
27462306a36Sopenharmony_ci	HWMON_CHANNEL_INFO(power, HWMON_P_MAX | HWMON_P_RATED_MAX | HWMON_P_CRIT),
27562306a36Sopenharmony_ci	HWMON_CHANNEL_INFO(energy, HWMON_E_INPUT),
27662306a36Sopenharmony_ci	HWMON_CHANNEL_INFO(curr, HWMON_C_CRIT),
27762306a36Sopenharmony_ci	NULL
27862306a36Sopenharmony_ci};
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic const struct hwmon_channel_info * const hwm_gt_info[] = {
28162306a36Sopenharmony_ci	HWMON_CHANNEL_INFO(energy, HWMON_E_INPUT),
28262306a36Sopenharmony_ci	NULL
28362306a36Sopenharmony_ci};
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci/* I1 is exposed as power_crit or as curr_crit depending on bit 31 */
28662306a36Sopenharmony_cistatic int hwm_pcode_read_i1(struct drm_i915_private *i915, u32 *uval)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	/* Avoid ILLEGAL_SUBCOMMAND "mailbox access failed" warning in snb_pcode_read */
28962306a36Sopenharmony_ci	if (IS_DG1(i915) || IS_DG2(i915))
29062306a36Sopenharmony_ci		return -ENXIO;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	return snb_pcode_read_p(&i915->uncore, PCODE_POWER_SETUP,
29362306a36Sopenharmony_ci				POWER_SETUP_SUBCOMMAND_READ_I1, 0, uval);
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic int hwm_pcode_write_i1(struct drm_i915_private *i915, u32 uval)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	return  snb_pcode_write_p(&i915->uncore, PCODE_POWER_SETUP,
29962306a36Sopenharmony_ci				  POWER_SETUP_SUBCOMMAND_WRITE_I1, 0, uval);
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic umode_t
30362306a36Sopenharmony_cihwm_in_is_visible(const struct hwm_drvdata *ddat, u32 attr)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	struct drm_i915_private *i915 = ddat->uncore->i915;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	switch (attr) {
30862306a36Sopenharmony_ci	case hwmon_in_input:
30962306a36Sopenharmony_ci		return IS_DG1(i915) || IS_DG2(i915) ? 0444 : 0;
31062306a36Sopenharmony_ci	default:
31162306a36Sopenharmony_ci		return 0;
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic int
31662306a36Sopenharmony_cihwm_in_read(struct hwm_drvdata *ddat, u32 attr, long *val)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	struct i915_hwmon *hwmon = ddat->hwmon;
31962306a36Sopenharmony_ci	intel_wakeref_t wakeref;
32062306a36Sopenharmony_ci	u32 reg_value;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	switch (attr) {
32362306a36Sopenharmony_ci	case hwmon_in_input:
32462306a36Sopenharmony_ci		with_intel_runtime_pm(ddat->uncore->rpm, wakeref)
32562306a36Sopenharmony_ci			reg_value = intel_uncore_read(ddat->uncore, hwmon->rg.gt_perf_status);
32662306a36Sopenharmony_ci		/* HW register value in units of 2.5 millivolt */
32762306a36Sopenharmony_ci		*val = DIV_ROUND_CLOSEST(REG_FIELD_GET(GEN12_VOLTAGE_MASK, reg_value) * 25, 10);
32862306a36Sopenharmony_ci		return 0;
32962306a36Sopenharmony_ci	default:
33062306a36Sopenharmony_ci		return -EOPNOTSUPP;
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic umode_t
33562306a36Sopenharmony_cihwm_power_is_visible(const struct hwm_drvdata *ddat, u32 attr, int chan)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	struct drm_i915_private *i915 = ddat->uncore->i915;
33862306a36Sopenharmony_ci	struct i915_hwmon *hwmon = ddat->hwmon;
33962306a36Sopenharmony_ci	u32 uval;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	switch (attr) {
34262306a36Sopenharmony_ci	case hwmon_power_max:
34362306a36Sopenharmony_ci		return i915_mmio_reg_valid(hwmon->rg.pkg_rapl_limit) ? 0664 : 0;
34462306a36Sopenharmony_ci	case hwmon_power_rated_max:
34562306a36Sopenharmony_ci		return i915_mmio_reg_valid(hwmon->rg.pkg_power_sku) ? 0444 : 0;
34662306a36Sopenharmony_ci	case hwmon_power_crit:
34762306a36Sopenharmony_ci		return (hwm_pcode_read_i1(i915, &uval) ||
34862306a36Sopenharmony_ci			!(uval & POWER_SETUP_I1_WATTS)) ? 0 : 0644;
34962306a36Sopenharmony_ci	default:
35062306a36Sopenharmony_ci		return 0;
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci#define PL1_DISABLE 0
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci/*
35762306a36Sopenharmony_ci * HW allows arbitrary PL1 limits to be set but silently clamps these values to
35862306a36Sopenharmony_ci * "typical but not guaranteed" min/max values in rg.pkg_power_sku. Follow the
35962306a36Sopenharmony_ci * same pattern for sysfs, allow arbitrary PL1 limits to be set but display
36062306a36Sopenharmony_ci * clamped values when read. Write/read I1 also follows the same pattern.
36162306a36Sopenharmony_ci */
36262306a36Sopenharmony_cistatic int
36362306a36Sopenharmony_cihwm_power_max_read(struct hwm_drvdata *ddat, long *val)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	struct i915_hwmon *hwmon = ddat->hwmon;
36662306a36Sopenharmony_ci	intel_wakeref_t wakeref;
36762306a36Sopenharmony_ci	u64 r, min, max;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	/* Check if PL1 limit is disabled */
37062306a36Sopenharmony_ci	with_intel_runtime_pm(ddat->uncore->rpm, wakeref)
37162306a36Sopenharmony_ci		r = intel_uncore_read(ddat->uncore, hwmon->rg.pkg_rapl_limit);
37262306a36Sopenharmony_ci	if (!(r & PKG_PWR_LIM_1_EN)) {
37362306a36Sopenharmony_ci		*val = PL1_DISABLE;
37462306a36Sopenharmony_ci		return 0;
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	*val = hwm_field_read_and_scale(ddat,
37862306a36Sopenharmony_ci					hwmon->rg.pkg_rapl_limit,
37962306a36Sopenharmony_ci					PKG_PWR_LIM_1,
38062306a36Sopenharmony_ci					hwmon->scl_shift_power,
38162306a36Sopenharmony_ci					SF_POWER);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	with_intel_runtime_pm(ddat->uncore->rpm, wakeref)
38462306a36Sopenharmony_ci		r = intel_uncore_read64(ddat->uncore, hwmon->rg.pkg_power_sku);
38562306a36Sopenharmony_ci	min = REG_FIELD_GET(PKG_MIN_PWR, r);
38662306a36Sopenharmony_ci	min = mul_u64_u32_shr(min, SF_POWER, hwmon->scl_shift_power);
38762306a36Sopenharmony_ci	max = REG_FIELD_GET(PKG_MAX_PWR, r);
38862306a36Sopenharmony_ci	max = mul_u64_u32_shr(max, SF_POWER, hwmon->scl_shift_power);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	if (min && max)
39162306a36Sopenharmony_ci		*val = clamp_t(u64, *val, min, max);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	return 0;
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cistatic int
39762306a36Sopenharmony_cihwm_power_max_write(struct hwm_drvdata *ddat, long val)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	struct i915_hwmon *hwmon = ddat->hwmon;
40062306a36Sopenharmony_ci	intel_wakeref_t wakeref;
40162306a36Sopenharmony_ci	DEFINE_WAIT(wait);
40262306a36Sopenharmony_ci	int ret = 0;
40362306a36Sopenharmony_ci	u32 nval;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	/* Block waiting for GuC reset to complete when needed */
40662306a36Sopenharmony_ci	for (;;) {
40762306a36Sopenharmony_ci		mutex_lock(&hwmon->hwmon_lock);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci		prepare_to_wait(&ddat->waitq, &wait, TASK_INTERRUPTIBLE);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci		if (!hwmon->ddat.reset_in_progress)
41262306a36Sopenharmony_ci			break;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci		if (signal_pending(current)) {
41562306a36Sopenharmony_ci			ret = -EINTR;
41662306a36Sopenharmony_ci			break;
41762306a36Sopenharmony_ci		}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci		mutex_unlock(&hwmon->hwmon_lock);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci		schedule();
42262306a36Sopenharmony_ci	}
42362306a36Sopenharmony_ci	finish_wait(&ddat->waitq, &wait);
42462306a36Sopenharmony_ci	if (ret)
42562306a36Sopenharmony_ci		goto unlock;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	wakeref = intel_runtime_pm_get(ddat->uncore->rpm);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	/* Disable PL1 limit and verify, because the limit cannot be disabled on all platforms */
43062306a36Sopenharmony_ci	if (val == PL1_DISABLE) {
43162306a36Sopenharmony_ci		intel_uncore_rmw(ddat->uncore, hwmon->rg.pkg_rapl_limit,
43262306a36Sopenharmony_ci				 PKG_PWR_LIM_1_EN, 0);
43362306a36Sopenharmony_ci		nval = intel_uncore_read(ddat->uncore, hwmon->rg.pkg_rapl_limit);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci		if (nval & PKG_PWR_LIM_1_EN)
43662306a36Sopenharmony_ci			ret = -ENODEV;
43762306a36Sopenharmony_ci		goto exit;
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	/* Computation in 64-bits to avoid overflow. Round to nearest. */
44162306a36Sopenharmony_ci	nval = DIV_ROUND_CLOSEST_ULL((u64)val << hwmon->scl_shift_power, SF_POWER);
44262306a36Sopenharmony_ci	nval = PKG_PWR_LIM_1_EN | REG_FIELD_PREP(PKG_PWR_LIM_1, nval);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	intel_uncore_rmw(ddat->uncore, hwmon->rg.pkg_rapl_limit,
44562306a36Sopenharmony_ci			 PKG_PWR_LIM_1_EN | PKG_PWR_LIM_1, nval);
44662306a36Sopenharmony_ciexit:
44762306a36Sopenharmony_ci	intel_runtime_pm_put(ddat->uncore->rpm, wakeref);
44862306a36Sopenharmony_ciunlock:
44962306a36Sopenharmony_ci	mutex_unlock(&hwmon->hwmon_lock);
45062306a36Sopenharmony_ci	return ret;
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_cistatic int
45462306a36Sopenharmony_cihwm_power_read(struct hwm_drvdata *ddat, u32 attr, int chan, long *val)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	struct i915_hwmon *hwmon = ddat->hwmon;
45762306a36Sopenharmony_ci	int ret;
45862306a36Sopenharmony_ci	u32 uval;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	switch (attr) {
46162306a36Sopenharmony_ci	case hwmon_power_max:
46262306a36Sopenharmony_ci		return hwm_power_max_read(ddat, val);
46362306a36Sopenharmony_ci	case hwmon_power_rated_max:
46462306a36Sopenharmony_ci		*val = hwm_field_read_and_scale(ddat,
46562306a36Sopenharmony_ci						hwmon->rg.pkg_power_sku,
46662306a36Sopenharmony_ci						PKG_PKG_TDP,
46762306a36Sopenharmony_ci						hwmon->scl_shift_power,
46862306a36Sopenharmony_ci						SF_POWER);
46962306a36Sopenharmony_ci		return 0;
47062306a36Sopenharmony_ci	case hwmon_power_crit:
47162306a36Sopenharmony_ci		ret = hwm_pcode_read_i1(ddat->uncore->i915, &uval);
47262306a36Sopenharmony_ci		if (ret)
47362306a36Sopenharmony_ci			return ret;
47462306a36Sopenharmony_ci		if (!(uval & POWER_SETUP_I1_WATTS))
47562306a36Sopenharmony_ci			return -ENODEV;
47662306a36Sopenharmony_ci		*val = mul_u64_u32_shr(REG_FIELD_GET(POWER_SETUP_I1_DATA_MASK, uval),
47762306a36Sopenharmony_ci				       SF_POWER, POWER_SETUP_I1_SHIFT);
47862306a36Sopenharmony_ci		return 0;
47962306a36Sopenharmony_ci	default:
48062306a36Sopenharmony_ci		return -EOPNOTSUPP;
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic int
48562306a36Sopenharmony_cihwm_power_write(struct hwm_drvdata *ddat, u32 attr, int chan, long val)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	u32 uval;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	switch (attr) {
49062306a36Sopenharmony_ci	case hwmon_power_max:
49162306a36Sopenharmony_ci		return hwm_power_max_write(ddat, val);
49262306a36Sopenharmony_ci	case hwmon_power_crit:
49362306a36Sopenharmony_ci		uval = DIV_ROUND_CLOSEST_ULL(val << POWER_SETUP_I1_SHIFT, SF_POWER);
49462306a36Sopenharmony_ci		return hwm_pcode_write_i1(ddat->uncore->i915, uval);
49562306a36Sopenharmony_ci	default:
49662306a36Sopenharmony_ci		return -EOPNOTSUPP;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_civoid i915_hwmon_power_max_disable(struct drm_i915_private *i915, bool *old)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	struct i915_hwmon *hwmon = i915->hwmon;
50362306a36Sopenharmony_ci	u32 r;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	if (!hwmon || !i915_mmio_reg_valid(hwmon->rg.pkg_rapl_limit))
50662306a36Sopenharmony_ci		return;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	mutex_lock(&hwmon->hwmon_lock);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	hwmon->ddat.reset_in_progress = true;
51162306a36Sopenharmony_ci	r = intel_uncore_rmw(hwmon->ddat.uncore, hwmon->rg.pkg_rapl_limit,
51262306a36Sopenharmony_ci			     PKG_PWR_LIM_1_EN, 0);
51362306a36Sopenharmony_ci	*old = !!(r & PKG_PWR_LIM_1_EN);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	mutex_unlock(&hwmon->hwmon_lock);
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_civoid i915_hwmon_power_max_restore(struct drm_i915_private *i915, bool old)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	struct i915_hwmon *hwmon = i915->hwmon;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if (!hwmon || !i915_mmio_reg_valid(hwmon->rg.pkg_rapl_limit))
52362306a36Sopenharmony_ci		return;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	mutex_lock(&hwmon->hwmon_lock);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	intel_uncore_rmw(hwmon->ddat.uncore, hwmon->rg.pkg_rapl_limit,
52862306a36Sopenharmony_ci			 PKG_PWR_LIM_1_EN, old ? PKG_PWR_LIM_1_EN : 0);
52962306a36Sopenharmony_ci	hwmon->ddat.reset_in_progress = false;
53062306a36Sopenharmony_ci	wake_up_all(&hwmon->ddat.waitq);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	mutex_unlock(&hwmon->hwmon_lock);
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic umode_t
53662306a36Sopenharmony_cihwm_energy_is_visible(const struct hwm_drvdata *ddat, u32 attr)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	struct i915_hwmon *hwmon = ddat->hwmon;
53962306a36Sopenharmony_ci	i915_reg_t rgaddr;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	switch (attr) {
54262306a36Sopenharmony_ci	case hwmon_energy_input:
54362306a36Sopenharmony_ci		if (ddat->gt_n >= 0)
54462306a36Sopenharmony_ci			rgaddr = hwmon->rg.energy_status_tile;
54562306a36Sopenharmony_ci		else
54662306a36Sopenharmony_ci			rgaddr = hwmon->rg.energy_status_all;
54762306a36Sopenharmony_ci		return i915_mmio_reg_valid(rgaddr) ? 0444 : 0;
54862306a36Sopenharmony_ci	default:
54962306a36Sopenharmony_ci		return 0;
55062306a36Sopenharmony_ci	}
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_cistatic int
55462306a36Sopenharmony_cihwm_energy_read(struct hwm_drvdata *ddat, u32 attr, long *val)
55562306a36Sopenharmony_ci{
55662306a36Sopenharmony_ci	switch (attr) {
55762306a36Sopenharmony_ci	case hwmon_energy_input:
55862306a36Sopenharmony_ci		hwm_energy(ddat, val);
55962306a36Sopenharmony_ci		return 0;
56062306a36Sopenharmony_ci	default:
56162306a36Sopenharmony_ci		return -EOPNOTSUPP;
56262306a36Sopenharmony_ci	}
56362306a36Sopenharmony_ci}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_cistatic umode_t
56662306a36Sopenharmony_cihwm_curr_is_visible(const struct hwm_drvdata *ddat, u32 attr)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	struct drm_i915_private *i915 = ddat->uncore->i915;
56962306a36Sopenharmony_ci	u32 uval;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	switch (attr) {
57262306a36Sopenharmony_ci	case hwmon_curr_crit:
57362306a36Sopenharmony_ci		return (hwm_pcode_read_i1(i915, &uval) ||
57462306a36Sopenharmony_ci			(uval & POWER_SETUP_I1_WATTS)) ? 0 : 0644;
57562306a36Sopenharmony_ci	default:
57662306a36Sopenharmony_ci		return 0;
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic int
58162306a36Sopenharmony_cihwm_curr_read(struct hwm_drvdata *ddat, u32 attr, long *val)
58262306a36Sopenharmony_ci{
58362306a36Sopenharmony_ci	int ret;
58462306a36Sopenharmony_ci	u32 uval;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	switch (attr) {
58762306a36Sopenharmony_ci	case hwmon_curr_crit:
58862306a36Sopenharmony_ci		ret = hwm_pcode_read_i1(ddat->uncore->i915, &uval);
58962306a36Sopenharmony_ci		if (ret)
59062306a36Sopenharmony_ci			return ret;
59162306a36Sopenharmony_ci		if (uval & POWER_SETUP_I1_WATTS)
59262306a36Sopenharmony_ci			return -ENODEV;
59362306a36Sopenharmony_ci		*val = mul_u64_u32_shr(REG_FIELD_GET(POWER_SETUP_I1_DATA_MASK, uval),
59462306a36Sopenharmony_ci				       SF_CURR, POWER_SETUP_I1_SHIFT);
59562306a36Sopenharmony_ci		return 0;
59662306a36Sopenharmony_ci	default:
59762306a36Sopenharmony_ci		return -EOPNOTSUPP;
59862306a36Sopenharmony_ci	}
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_cistatic int
60262306a36Sopenharmony_cihwm_curr_write(struct hwm_drvdata *ddat, u32 attr, long val)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	u32 uval;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	switch (attr) {
60762306a36Sopenharmony_ci	case hwmon_curr_crit:
60862306a36Sopenharmony_ci		uval = DIV_ROUND_CLOSEST_ULL(val << POWER_SETUP_I1_SHIFT, SF_CURR);
60962306a36Sopenharmony_ci		return hwm_pcode_write_i1(ddat->uncore->i915, uval);
61062306a36Sopenharmony_ci	default:
61162306a36Sopenharmony_ci		return -EOPNOTSUPP;
61262306a36Sopenharmony_ci	}
61362306a36Sopenharmony_ci}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_cistatic umode_t
61662306a36Sopenharmony_cihwm_is_visible(const void *drvdata, enum hwmon_sensor_types type,
61762306a36Sopenharmony_ci	       u32 attr, int channel)
61862306a36Sopenharmony_ci{
61962306a36Sopenharmony_ci	struct hwm_drvdata *ddat = (struct hwm_drvdata *)drvdata;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	switch (type) {
62262306a36Sopenharmony_ci	case hwmon_in:
62362306a36Sopenharmony_ci		return hwm_in_is_visible(ddat, attr);
62462306a36Sopenharmony_ci	case hwmon_power:
62562306a36Sopenharmony_ci		return hwm_power_is_visible(ddat, attr, channel);
62662306a36Sopenharmony_ci	case hwmon_energy:
62762306a36Sopenharmony_ci		return hwm_energy_is_visible(ddat, attr);
62862306a36Sopenharmony_ci	case hwmon_curr:
62962306a36Sopenharmony_ci		return hwm_curr_is_visible(ddat, attr);
63062306a36Sopenharmony_ci	default:
63162306a36Sopenharmony_ci		return 0;
63262306a36Sopenharmony_ci	}
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_cistatic int
63662306a36Sopenharmony_cihwm_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
63762306a36Sopenharmony_ci	 int channel, long *val)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	struct hwm_drvdata *ddat = dev_get_drvdata(dev);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	switch (type) {
64262306a36Sopenharmony_ci	case hwmon_in:
64362306a36Sopenharmony_ci		return hwm_in_read(ddat, attr, val);
64462306a36Sopenharmony_ci	case hwmon_power:
64562306a36Sopenharmony_ci		return hwm_power_read(ddat, attr, channel, val);
64662306a36Sopenharmony_ci	case hwmon_energy:
64762306a36Sopenharmony_ci		return hwm_energy_read(ddat, attr, val);
64862306a36Sopenharmony_ci	case hwmon_curr:
64962306a36Sopenharmony_ci		return hwm_curr_read(ddat, attr, val);
65062306a36Sopenharmony_ci	default:
65162306a36Sopenharmony_ci		return -EOPNOTSUPP;
65262306a36Sopenharmony_ci	}
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_cistatic int
65662306a36Sopenharmony_cihwm_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
65762306a36Sopenharmony_ci	  int channel, long val)
65862306a36Sopenharmony_ci{
65962306a36Sopenharmony_ci	struct hwm_drvdata *ddat = dev_get_drvdata(dev);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	switch (type) {
66262306a36Sopenharmony_ci	case hwmon_power:
66362306a36Sopenharmony_ci		return hwm_power_write(ddat, attr, channel, val);
66462306a36Sopenharmony_ci	case hwmon_curr:
66562306a36Sopenharmony_ci		return hwm_curr_write(ddat, attr, val);
66662306a36Sopenharmony_ci	default:
66762306a36Sopenharmony_ci		return -EOPNOTSUPP;
66862306a36Sopenharmony_ci	}
66962306a36Sopenharmony_ci}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_cistatic const struct hwmon_ops hwm_ops = {
67262306a36Sopenharmony_ci	.is_visible = hwm_is_visible,
67362306a36Sopenharmony_ci	.read = hwm_read,
67462306a36Sopenharmony_ci	.write = hwm_write,
67562306a36Sopenharmony_ci};
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_cistatic const struct hwmon_chip_info hwm_chip_info = {
67862306a36Sopenharmony_ci	.ops = &hwm_ops,
67962306a36Sopenharmony_ci	.info = hwm_info,
68062306a36Sopenharmony_ci};
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cistatic umode_t
68362306a36Sopenharmony_cihwm_gt_is_visible(const void *drvdata, enum hwmon_sensor_types type,
68462306a36Sopenharmony_ci		  u32 attr, int channel)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	struct hwm_drvdata *ddat = (struct hwm_drvdata *)drvdata;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	switch (type) {
68962306a36Sopenharmony_ci	case hwmon_energy:
69062306a36Sopenharmony_ci		return hwm_energy_is_visible(ddat, attr);
69162306a36Sopenharmony_ci	default:
69262306a36Sopenharmony_ci		return 0;
69362306a36Sopenharmony_ci	}
69462306a36Sopenharmony_ci}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_cistatic int
69762306a36Sopenharmony_cihwm_gt_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
69862306a36Sopenharmony_ci	    int channel, long *val)
69962306a36Sopenharmony_ci{
70062306a36Sopenharmony_ci	struct hwm_drvdata *ddat = dev_get_drvdata(dev);
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	switch (type) {
70362306a36Sopenharmony_ci	case hwmon_energy:
70462306a36Sopenharmony_ci		return hwm_energy_read(ddat, attr, val);
70562306a36Sopenharmony_ci	default:
70662306a36Sopenharmony_ci		return -EOPNOTSUPP;
70762306a36Sopenharmony_ci	}
70862306a36Sopenharmony_ci}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_cistatic const struct hwmon_ops hwm_gt_ops = {
71162306a36Sopenharmony_ci	.is_visible = hwm_gt_is_visible,
71262306a36Sopenharmony_ci	.read = hwm_gt_read,
71362306a36Sopenharmony_ci};
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_cistatic const struct hwmon_chip_info hwm_gt_chip_info = {
71662306a36Sopenharmony_ci	.ops = &hwm_gt_ops,
71762306a36Sopenharmony_ci	.info = hwm_gt_info,
71862306a36Sopenharmony_ci};
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_cistatic void
72162306a36Sopenharmony_cihwm_get_preregistration_info(struct drm_i915_private *i915)
72262306a36Sopenharmony_ci{
72362306a36Sopenharmony_ci	struct i915_hwmon *hwmon = i915->hwmon;
72462306a36Sopenharmony_ci	struct intel_uncore *uncore = &i915->uncore;
72562306a36Sopenharmony_ci	struct hwm_drvdata *ddat = &hwmon->ddat;
72662306a36Sopenharmony_ci	intel_wakeref_t wakeref;
72762306a36Sopenharmony_ci	u32 val_sku_unit = 0;
72862306a36Sopenharmony_ci	struct intel_gt *gt;
72962306a36Sopenharmony_ci	long energy;
73062306a36Sopenharmony_ci	int i;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	/* Available for all Gen12+/dGfx */
73362306a36Sopenharmony_ci	hwmon->rg.gt_perf_status = GEN12_RPSTAT1;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	if (IS_DG1(i915) || IS_DG2(i915)) {
73662306a36Sopenharmony_ci		hwmon->rg.pkg_power_sku_unit = PCU_PACKAGE_POWER_SKU_UNIT;
73762306a36Sopenharmony_ci		hwmon->rg.pkg_power_sku = PCU_PACKAGE_POWER_SKU;
73862306a36Sopenharmony_ci		hwmon->rg.pkg_rapl_limit = PCU_PACKAGE_RAPL_LIMIT;
73962306a36Sopenharmony_ci		hwmon->rg.energy_status_all = PCU_PACKAGE_ENERGY_STATUS;
74062306a36Sopenharmony_ci		hwmon->rg.energy_status_tile = INVALID_MMIO_REG;
74162306a36Sopenharmony_ci	} else if (IS_XEHPSDV(i915)) {
74262306a36Sopenharmony_ci		hwmon->rg.pkg_power_sku_unit = GT0_PACKAGE_POWER_SKU_UNIT;
74362306a36Sopenharmony_ci		hwmon->rg.pkg_power_sku = INVALID_MMIO_REG;
74462306a36Sopenharmony_ci		hwmon->rg.pkg_rapl_limit = GT0_PACKAGE_RAPL_LIMIT;
74562306a36Sopenharmony_ci		hwmon->rg.energy_status_all = GT0_PLATFORM_ENERGY_STATUS;
74662306a36Sopenharmony_ci		hwmon->rg.energy_status_tile = GT0_PACKAGE_ENERGY_STATUS;
74762306a36Sopenharmony_ci	} else {
74862306a36Sopenharmony_ci		hwmon->rg.pkg_power_sku_unit = INVALID_MMIO_REG;
74962306a36Sopenharmony_ci		hwmon->rg.pkg_power_sku = INVALID_MMIO_REG;
75062306a36Sopenharmony_ci		hwmon->rg.pkg_rapl_limit = INVALID_MMIO_REG;
75162306a36Sopenharmony_ci		hwmon->rg.energy_status_all = INVALID_MMIO_REG;
75262306a36Sopenharmony_ci		hwmon->rg.energy_status_tile = INVALID_MMIO_REG;
75362306a36Sopenharmony_ci	}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	with_intel_runtime_pm(uncore->rpm, wakeref) {
75662306a36Sopenharmony_ci		/*
75762306a36Sopenharmony_ci		 * The contents of register hwmon->rg.pkg_power_sku_unit do not change,
75862306a36Sopenharmony_ci		 * so read it once and store the shift values.
75962306a36Sopenharmony_ci		 */
76062306a36Sopenharmony_ci		if (i915_mmio_reg_valid(hwmon->rg.pkg_power_sku_unit))
76162306a36Sopenharmony_ci			val_sku_unit = intel_uncore_read(uncore,
76262306a36Sopenharmony_ci							 hwmon->rg.pkg_power_sku_unit);
76362306a36Sopenharmony_ci	}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	hwmon->scl_shift_power = REG_FIELD_GET(PKG_PWR_UNIT, val_sku_unit);
76662306a36Sopenharmony_ci	hwmon->scl_shift_energy = REG_FIELD_GET(PKG_ENERGY_UNIT, val_sku_unit);
76762306a36Sopenharmony_ci	hwmon->scl_shift_time = REG_FIELD_GET(PKG_TIME_UNIT, val_sku_unit);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	/*
77062306a36Sopenharmony_ci	 * Initialize 'struct hwm_energy_info', i.e. set fields to the
77162306a36Sopenharmony_ci	 * first value of the energy register read
77262306a36Sopenharmony_ci	 */
77362306a36Sopenharmony_ci	if (i915_mmio_reg_valid(hwmon->rg.energy_status_all))
77462306a36Sopenharmony_ci		hwm_energy(ddat, &energy);
77562306a36Sopenharmony_ci	if (i915_mmio_reg_valid(hwmon->rg.energy_status_tile)) {
77662306a36Sopenharmony_ci		for_each_gt(gt, i915, i)
77762306a36Sopenharmony_ci			hwm_energy(&hwmon->ddat_gt[i], &energy);
77862306a36Sopenharmony_ci	}
77962306a36Sopenharmony_ci}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_civoid i915_hwmon_register(struct drm_i915_private *i915)
78262306a36Sopenharmony_ci{
78362306a36Sopenharmony_ci	struct device *dev = i915->drm.dev;
78462306a36Sopenharmony_ci	struct i915_hwmon *hwmon;
78562306a36Sopenharmony_ci	struct device *hwmon_dev;
78662306a36Sopenharmony_ci	struct hwm_drvdata *ddat;
78762306a36Sopenharmony_ci	struct hwm_drvdata *ddat_gt;
78862306a36Sopenharmony_ci	struct intel_gt *gt;
78962306a36Sopenharmony_ci	int i;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	/* hwmon is available only for dGfx */
79262306a36Sopenharmony_ci	if (!IS_DGFX(i915))
79362306a36Sopenharmony_ci		return;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	hwmon = devm_kzalloc(dev, sizeof(*hwmon), GFP_KERNEL);
79662306a36Sopenharmony_ci	if (!hwmon)
79762306a36Sopenharmony_ci		return;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	i915->hwmon = hwmon;
80062306a36Sopenharmony_ci	mutex_init(&hwmon->hwmon_lock);
80162306a36Sopenharmony_ci	ddat = &hwmon->ddat;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	ddat->hwmon = hwmon;
80462306a36Sopenharmony_ci	ddat->uncore = &i915->uncore;
80562306a36Sopenharmony_ci	snprintf(ddat->name, sizeof(ddat->name), "i915");
80662306a36Sopenharmony_ci	ddat->gt_n = -1;
80762306a36Sopenharmony_ci	init_waitqueue_head(&ddat->waitq);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	for_each_gt(gt, i915, i) {
81062306a36Sopenharmony_ci		ddat_gt = hwmon->ddat_gt + i;
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci		ddat_gt->hwmon = hwmon;
81362306a36Sopenharmony_ci		ddat_gt->uncore = gt->uncore;
81462306a36Sopenharmony_ci		snprintf(ddat_gt->name, sizeof(ddat_gt->name), "i915_gt%u", i);
81562306a36Sopenharmony_ci		ddat_gt->gt_n = i;
81662306a36Sopenharmony_ci	}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	hwm_get_preregistration_info(i915);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	/*  hwmon_dev points to device hwmon<i> */
82162306a36Sopenharmony_ci	hwmon_dev = devm_hwmon_device_register_with_info(dev, ddat->name,
82262306a36Sopenharmony_ci							 ddat,
82362306a36Sopenharmony_ci							 &hwm_chip_info,
82462306a36Sopenharmony_ci							 hwm_groups);
82562306a36Sopenharmony_ci	if (IS_ERR(hwmon_dev)) {
82662306a36Sopenharmony_ci		i915->hwmon = NULL;
82762306a36Sopenharmony_ci		return;
82862306a36Sopenharmony_ci	}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	ddat->hwmon_dev = hwmon_dev;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	for_each_gt(gt, i915, i) {
83362306a36Sopenharmony_ci		ddat_gt = hwmon->ddat_gt + i;
83462306a36Sopenharmony_ci		/*
83562306a36Sopenharmony_ci		 * Create per-gt directories only if a per-gt attribute is
83662306a36Sopenharmony_ci		 * visible. Currently this is only energy
83762306a36Sopenharmony_ci		 */
83862306a36Sopenharmony_ci		if (!hwm_gt_is_visible(ddat_gt, hwmon_energy, hwmon_energy_input, 0))
83962306a36Sopenharmony_ci			continue;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci		hwmon_dev = devm_hwmon_device_register_with_info(dev, ddat_gt->name,
84262306a36Sopenharmony_ci								 ddat_gt,
84362306a36Sopenharmony_ci								 &hwm_gt_chip_info,
84462306a36Sopenharmony_ci								 NULL);
84562306a36Sopenharmony_ci		if (!IS_ERR(hwmon_dev))
84662306a36Sopenharmony_ci			ddat_gt->hwmon_dev = hwmon_dev;
84762306a36Sopenharmony_ci	}
84862306a36Sopenharmony_ci}
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_civoid i915_hwmon_unregister(struct drm_i915_private *i915)
85162306a36Sopenharmony_ci{
85262306a36Sopenharmony_ci	fetch_and_zero(&i915->hwmon);
85362306a36Sopenharmony_ci}
854