drm/xe/hwmon: Add infra to support card power and energy attributes

Add infra to support card power and energy attributes through channel 0.
Package attributes will be now exposed through channel 1 rather than
channel 0 as shown below.

Channel 0 i.e power1/energy1_xxx used for card and
channel 1 i.e power2/energy2_xxx used for package power,energy attributes.

power1/curr1_crit and in0_input are moved to channel 1, i.e.
power2/curr2_crit and in1_input as these are available for package only.

This would be needed for future platforms where they might be
separate registers for package and card power and energy.

Each discrete GPU supported by Xe driver, would have a directory in
/sys/class/hwmon/ with multiple channels under it.
Each channel would have attributes for power, energy etc.

Ex: /sys/class/hwmon/hwmon2/power1_max
                           /power1_label
                           /energy1_input
                           /energy1_label

Attributes will have a label to get more description of it.
Labelling is as below.
		power1_label/energy1_label - "card",
		power2_label/energy2_label - "pkg".

v2: Fix checkpatch errors.

v3:
 - Update intel-xe-hwmon documentation. (Riana, Badal)
 - Rename hwmon card channel enum from CHANNEL_PLATFORM
   to CHANNEL_CARD. (Riana)

v4:
 - Remove unrelated changes from patch. (Anshuman)
 - Fix typo in commit msg.

v5:
 - Update commit message and intel-xe-hwmon documentation with "Xe"
   instead of xe when using it as a name. (Rodrigo)

Signed-off-by: Karthik Poosa <karthik.poosa@intel.com>
Reviewed-by: Badal Nilawar <badal.nilawar@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240328175435.3870957-1-karthik.poosa@intel.com
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
This commit is contained in:
Karthik Poosa 2024-03-28 23:24:35 +05:30 committed by Rodrigo Vivi
parent c54eb24f71
commit 345dadc4f6
No known key found for this signature in database
GPG Key ID: FA625F640EEB13CA
2 changed files with 207 additions and 119 deletions

View File

@ -10,7 +10,7 @@ Description: RW. Card reactive sustained (PL1) power limit in microwatts.
power limit is disabled, writing 0 disables the
limit. Writing values > 0 and <= TDP will enable the power limit.
Only supported for particular Intel xe graphics platforms.
Only supported for particular Intel Xe graphics platforms.
What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/power1_rated_max
Date: September 2023
@ -18,53 +18,93 @@ KernelVersion: 6.5
Contact: intel-xe@lists.freedesktop.org
Description: RO. Card default power limit (default TDP setting).
Only supported for particular Intel xe graphics platforms.
Only supported for particular Intel Xe graphics platforms.
What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/power1_crit
Date: September 2023
KernelVersion: 6.5
Contact: intel-xe@lists.freedesktop.org
Description: RW. Card reactive critical (I1) power limit in microwatts.
Card reactive critical (I1) power limit in microwatts is exposed
for client products. The power controller will throttle the
operating frequency if the power averaged over a window exceeds
this limit.
Only supported for particular Intel xe graphics platforms.
What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/curr1_crit
Date: September 2023
KernelVersion: 6.5
Contact: intel-xe@lists.freedesktop.org
Description: RW. Card reactive critical (I1) power limit in milliamperes.
Card reactive critical (I1) power limit in milliamperes is
exposed for server products. The power controller will throttle
the operating frequency if the power averaged over a window
exceeds this limit.
What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/in0_input
Date: September 2023
KernelVersion: 6.5
Contact: intel-xe@lists.freedesktop.org
Description: RO. Current Voltage in millivolt.
Only supported for particular Intel xe graphics platforms.
What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/energy1_input
Date: September 2023
KernelVersion: 6.5
Contact: intel-xe@lists.freedesktop.org
Description: RO. Energy input of device in microjoules.
Description: RO. Card energy input of device in microjoules.
Only supported for particular Intel xe graphics platforms.
Only supported for particular Intel Xe graphics platforms.
What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/power1_max_interval
Date: October 2023
KernelVersion: 6.6
Contact: intel-xe@lists.freedesktop.org
Description: RW. Sustained power limit interval (Tau in PL1/Tau) in
Description: RW. Card sustained power limit interval (Tau in PL1/Tau) in
milliseconds over which sustained power is averaged.
Only supported for particular Intel xe graphics platforms.
Only supported for particular Intel Xe graphics platforms.
What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/power2_max
Date: February 2024
KernelVersion: 6.8
Contact: intel-xe@lists.freedesktop.org
Description: RW. Package reactive sustained (PL1) power limit in microwatts.
The power controller will throttle the operating frequency
if the power averaged over a window (typically seconds)
exceeds this limit. A read value of 0 means that the PL1
power limit is disabled, writing 0 disables the
limit. Writing values > 0 and <= TDP will enable the power limit.
Only supported for particular Intel Xe graphics platforms.
What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/power2_rated_max
Date: February 2024
KernelVersion: 6.8
Contact: intel-xe@lists.freedesktop.org
Description: RO. Package default power limit (default TDP setting).
Only supported for particular Intel Xe graphics platforms.
What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/power2_crit
Date: February 2024
KernelVersion: 6.8
Contact: intel-xe@lists.freedesktop.org
Description: RW. Package reactive critical (I1) power limit in microwatts.
Package reactive critical (I1) power limit in microwatts is exposed
for client products. The power controller will throttle the
operating frequency if the power averaged over a window exceeds
this limit.
Only supported for particular Intel Xe graphics platforms.
What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/curr2_crit
Date: February 2024
KernelVersion: 6.8
Contact: intel-xe@lists.freedesktop.org
Description: RW. Package reactive critical (I1) power limit in milliamperes.
Package reactive critical (I1) power limit in milliamperes is
exposed for server products. The power controller will throttle
the operating frequency if the power averaged over a window
exceeds this limit.
What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/energy2_input
Date: February 2024
KernelVersion: 6.8
Contact: intel-xe@lists.freedesktop.org
Description: RO. Package energy input of device in microjoules.
Only supported for particular Intel Xe graphics platforms.
What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/power2_max_interval
Date: February 2024
KernelVersion: 6.8
Contact: intel-xe@lists.freedesktop.org
Description: RW. Package sustained power limit interval (Tau in PL1/Tau) in
milliseconds over which sustained power is averaged.
Only supported for particular Intel Xe graphics platforms.
What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/in1_input
Date: February 2024
KernelVersion: 6.8
Contact: intel-xe@lists.freedesktop.org
Description: RO. Package current voltage in millivolt.
Only supported for particular Intel Xe graphics platforms.

View File

@ -34,6 +34,12 @@ enum xe_hwmon_reg_operation {
REG_READ64,
};
enum xe_hwmon_channel {
CHANNEL_CARD,
CHANNEL_PKG,
CHANNEL_MAX,
};
/*
* SF_* - scale factors for particular quantities according to hwmon spec.
*/
@ -69,26 +75,26 @@ struct xe_hwmon {
int scl_shift_energy;
/** @scl_shift_time: pkg time unit */
int scl_shift_time;
/** @ei: Energy info for energy1_input */
struct xe_hwmon_energy_info ei;
/** @ei: Energy info for energyN_input */
struct xe_hwmon_energy_info ei[CHANNEL_MAX];
};
static u32 xe_hwmon_get_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg hwmon_reg)
static u32 xe_hwmon_get_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg hwmon_reg, int channel)
{
struct xe_device *xe = gt_to_xe(hwmon->gt);
struct xe_reg reg = XE_REG(0);
switch (hwmon_reg) {
case REG_PKG_RAPL_LIMIT:
if (xe->info.platform == XE_PVC)
if (xe->info.platform == XE_PVC && channel == CHANNEL_PKG)
reg = PVC_GT0_PACKAGE_RAPL_LIMIT;
else if (xe->info.platform == XE_DG2)
else if ((xe->info.platform == XE_DG2) && (channel == CHANNEL_PKG))
reg = PCU_CR_PACKAGE_RAPL_LIMIT;
break;
case REG_PKG_POWER_SKU:
if (xe->info.platform == XE_PVC)
if (xe->info.platform == XE_PVC && channel == CHANNEL_PKG)
reg = PVC_GT0_PACKAGE_POWER_SKU;
else if (xe->info.platform == XE_DG2)
else if ((xe->info.platform == XE_DG2) && (channel == CHANNEL_PKG))
reg = PCU_CR_PACKAGE_POWER_SKU;
break;
case REG_PKG_POWER_SKU_UNIT:
@ -98,13 +104,13 @@ static u32 xe_hwmon_get_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg hwmon_reg)
reg = PCU_CR_PACKAGE_POWER_SKU_UNIT;
break;
case REG_GT_PERF_STATUS:
if (xe->info.platform == XE_DG2)
if (xe->info.platform == XE_DG2 && channel == CHANNEL_PKG)
reg = GT_PERF_STATUS;
break;
case REG_PKG_ENERGY_STATUS:
if (xe->info.platform == XE_PVC)
if (xe->info.platform == XE_PVC && channel == CHANNEL_PKG)
reg = PVC_GT0_PLATFORM_ENERGY_STATUS;
else if (xe->info.platform == XE_DG2)
else if ((xe->info.platform == XE_DG2) && (channel == CHANNEL_PKG))
reg = PCU_CR_PACKAGE_ENERGY_STATUS;
break;
default:
@ -117,11 +123,11 @@ static u32 xe_hwmon_get_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg hwmon_reg)
static void xe_hwmon_process_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg hwmon_reg,
enum xe_hwmon_reg_operation operation, u64 *value,
u32 clr, u32 set)
u32 clr, u32 set, int channel)
{
struct xe_reg reg;
reg.raw = xe_hwmon_get_reg(hwmon, hwmon_reg);
reg.raw = xe_hwmon_get_reg(hwmon, hwmon_reg, channel);
if (!reg.raw)
return;
@ -151,13 +157,13 @@ static void xe_hwmon_process_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg hwmon
* same pattern for sysfs, allow arbitrary PL1 limits to be set but display
* clamped values when read.
*/
static void xe_hwmon_power_max_read(struct xe_hwmon *hwmon, long *value)
static void xe_hwmon_power_max_read(struct xe_hwmon *hwmon, int channel, long *value)
{
u64 reg_val, min, max;
mutex_lock(&hwmon->hwmon_lock);
xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT, REG_READ32, &reg_val, 0, 0);
xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT, REG_READ32, &reg_val, 0, 0, channel);
/* Check if PL1 limit is disabled */
if (!(reg_val & PKG_PWR_LIM_1_EN)) {
*value = PL1_DISABLE;
@ -167,7 +173,7 @@ static void xe_hwmon_power_max_read(struct xe_hwmon *hwmon, long *value)
reg_val = REG_FIELD_GET(PKG_PWR_LIM_1, reg_val);
*value = mul_u64_u32_shr(reg_val, SF_POWER, hwmon->scl_shift_power);
xe_hwmon_process_reg(hwmon, REG_PKG_POWER_SKU, REG_READ64, &reg_val, 0, 0);
xe_hwmon_process_reg(hwmon, REG_PKG_POWER_SKU, REG_READ64, &reg_val, 0, 0, channel);
min = REG_FIELD_GET(PKG_MIN_PWR, reg_val);
min = mul_u64_u32_shr(min, SF_POWER, hwmon->scl_shift_power);
max = REG_FIELD_GET(PKG_MAX_PWR, reg_val);
@ -179,7 +185,7 @@ unlock:
mutex_unlock(&hwmon->hwmon_lock);
}
static int xe_hwmon_power_max_write(struct xe_hwmon *hwmon, long value)
static int xe_hwmon_power_max_write(struct xe_hwmon *hwmon, int channel, long value)
{
int ret = 0;
u64 reg_val;
@ -189,9 +195,9 @@ static int xe_hwmon_power_max_write(struct xe_hwmon *hwmon, long value)
/* Disable PL1 limit and verify, as limit cannot be disabled on all platforms */
if (value == PL1_DISABLE) {
xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT, REG_RMW32, &reg_val,
PKG_PWR_LIM_1_EN, 0);
PKG_PWR_LIM_1_EN, 0, channel);
xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT, REG_READ32, &reg_val,
PKG_PWR_LIM_1_EN, 0);
PKG_PWR_LIM_1_EN, 0, channel);
if (reg_val & PKG_PWR_LIM_1_EN) {
ret = -EOPNOTSUPP;
@ -204,17 +210,17 @@ static int xe_hwmon_power_max_write(struct xe_hwmon *hwmon, long value)
reg_val = PKG_PWR_LIM_1_EN | REG_FIELD_PREP(PKG_PWR_LIM_1, reg_val);
xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT, REG_RMW32, &reg_val,
PKG_PWR_LIM_1_EN | PKG_PWR_LIM_1, reg_val);
PKG_PWR_LIM_1_EN | PKG_PWR_LIM_1, reg_val, channel);
unlock:
mutex_unlock(&hwmon->hwmon_lock);
return ret;
}
static void xe_hwmon_power_rated_max_read(struct xe_hwmon *hwmon, long *value)
static void xe_hwmon_power_rated_max_read(struct xe_hwmon *hwmon, int channel, long *value)
{
u64 reg_val;
xe_hwmon_process_reg(hwmon, REG_PKG_POWER_SKU, REG_READ32, &reg_val, 0, 0);
xe_hwmon_process_reg(hwmon, REG_PKG_POWER_SKU, REG_READ32, &reg_val, 0, 0, channel);
reg_val = REG_FIELD_GET(PKG_TDP, reg_val);
*value = mul_u64_u32_shr(reg_val, SF_POWER, hwmon->scl_shift_power);
}
@ -237,16 +243,16 @@ static void xe_hwmon_power_rated_max_read(struct xe_hwmon *hwmon, long *value)
* the hwmon API. Using x86_64 128 bit arithmetic (see mul_u64_u32_shr()),
* a 'long' of 63 bits, SF_ENERGY of 1e6 (~20 bits) and
* hwmon->scl_shift_energy of 14 bits we have 57 (63 - 20 + 14) bits before
* energy1_input overflows. This at 1000 W is an overflow duration of 278 years.
* energyN_input overflows. This at 1000 W is an overflow duration of 278 years.
*/
static void
xe_hwmon_energy_get(struct xe_hwmon *hwmon, long *energy)
xe_hwmon_energy_get(struct xe_hwmon *hwmon, int channel, long *energy)
{
struct xe_hwmon_energy_info *ei = &hwmon->ei;
struct xe_hwmon_energy_info *ei = &hwmon->ei[channel];
u64 reg_val;
xe_hwmon_process_reg(hwmon, REG_PKG_ENERGY_STATUS, REG_READ32,
&reg_val, 0, 0);
&reg_val, 0, 0, channel);
if (reg_val >= ei->reg_val_prev)
ei->accum_energy += reg_val - ei->reg_val_prev;
@ -260,19 +266,20 @@ xe_hwmon_energy_get(struct xe_hwmon *hwmon, long *energy)
}
static ssize_t
xe_hwmon_power1_max_interval_show(struct device *dev, struct device_attribute *attr,
char *buf)
xe_hwmon_power_max_interval_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct xe_hwmon *hwmon = dev_get_drvdata(dev);
u32 x, y, x_w = 2; /* 2 bits */
u64 r, tau4, out;
int sensor_index = to_sensor_dev_attr(attr)->index;
xe_pm_runtime_get(gt_to_xe(hwmon->gt));
mutex_lock(&hwmon->hwmon_lock);
xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT,
REG_READ32, &r, 0, 0);
REG_READ32, &r, 0, 0, sensor_index);
mutex_unlock(&hwmon->hwmon_lock);
@ -300,14 +307,15 @@ xe_hwmon_power1_max_interval_show(struct device *dev, struct device_attribute *a
}
static ssize_t
xe_hwmon_power1_max_interval_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
xe_hwmon_power_max_interval_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct xe_hwmon *hwmon = dev_get_drvdata(dev);
u32 x, y, rxy, x_w = 2; /* 2 bits */
u64 tau4, r, max_win;
unsigned long val;
int ret;
int sensor_index = to_sensor_dev_attr(attr)->index;
ret = kstrtoul(buf, 0, &val);
if (ret)
@ -326,7 +334,7 @@ xe_hwmon_power1_max_interval_store(struct device *dev, struct device_attribute *
/*
* val must be < max in hwmon interface units. The steps below are
* explained in xe_hwmon_power1_max_interval_show()
* explained in xe_hwmon_power_max_interval_show()
*/
r = FIELD_PREP(PKG_MAX_WIN, PKG_MAX_WIN_DEFAULT);
x = REG_FIELD_GET(PKG_MAX_WIN_X, r);
@ -360,7 +368,7 @@ xe_hwmon_power1_max_interval_store(struct device *dev, struct device_attribute *
mutex_lock(&hwmon->hwmon_lock);
xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT, REG_RMW32, (u64 *)&r,
PKG_PWR_LIM_1_TIME, rxy);
PKG_PWR_LIM_1_TIME, rxy, sensor_index);
mutex_unlock(&hwmon->hwmon_lock);
@ -370,11 +378,16 @@ xe_hwmon_power1_max_interval_store(struct device *dev, struct device_attribute *
}
static SENSOR_DEVICE_ATTR(power1_max_interval, 0664,
xe_hwmon_power1_max_interval_show,
xe_hwmon_power1_max_interval_store, 0);
xe_hwmon_power_max_interval_show,
xe_hwmon_power_max_interval_store, CHANNEL_CARD);
static SENSOR_DEVICE_ATTR(power2_max_interval, 0664,
xe_hwmon_power_max_interval_show,
xe_hwmon_power_max_interval_store, CHANNEL_PKG);
static struct attribute *hwmon_attributes[] = {
&sensor_dev_attr_power1_max_interval.dev_attr.attr,
&sensor_dev_attr_power2_max_interval.dev_attr.attr,
NULL
};
@ -387,8 +400,7 @@ static umode_t xe_hwmon_attributes_visible(struct kobject *kobj,
xe_pm_runtime_get(gt_to_xe(hwmon->gt));
if (attr == &sensor_dev_attr_power1_max_interval.dev_attr.attr)
ret = xe_hwmon_get_reg(hwmon, REG_PKG_RAPL_LIMIT) ? attr->mode : 0;
ret = xe_hwmon_get_reg(hwmon, REG_PKG_RAPL_LIMIT, index) ? attr->mode : 0;
xe_pm_runtime_put(gt_to_xe(hwmon->gt));
@ -406,10 +418,11 @@ static const struct attribute_group *hwmon_groups[] = {
};
static const struct hwmon_channel_info * const hwmon_info[] = {
HWMON_CHANNEL_INFO(power, HWMON_P_MAX | HWMON_P_RATED_MAX | HWMON_P_CRIT),
HWMON_CHANNEL_INFO(curr, HWMON_C_CRIT),
HWMON_CHANNEL_INFO(in, HWMON_I_INPUT),
HWMON_CHANNEL_INFO(energy, HWMON_E_INPUT),
HWMON_CHANNEL_INFO(power, HWMON_P_MAX | HWMON_P_RATED_MAX | HWMON_P_LABEL,
HWMON_P_MAX | HWMON_P_RATED_MAX | HWMON_P_CRIT | HWMON_P_LABEL),
HWMON_CHANNEL_INFO(curr, HWMON_C_LABEL, HWMON_C_CRIT | HWMON_C_LABEL),
HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL),
HWMON_CHANNEL_INFO(energy, HWMON_E_INPUT | HWMON_E_LABEL, HWMON_E_INPUT | HWMON_E_LABEL),
NULL
};
@ -432,7 +445,8 @@ static int xe_hwmon_pcode_write_i1(struct xe_gt *gt, u32 uval)
uval);
}
static int xe_hwmon_power_curr_crit_read(struct xe_hwmon *hwmon, long *value, u32 scale_factor)
static int xe_hwmon_power_curr_crit_read(struct xe_hwmon *hwmon, int channel,
long *value, u32 scale_factor)
{
int ret;
u32 uval;
@ -450,7 +464,8 @@ unlock:
return ret;
}
static int xe_hwmon_power_curr_crit_write(struct xe_hwmon *hwmon, long value, u32 scale_factor)
static int xe_hwmon_power_curr_crit_write(struct xe_hwmon *hwmon, int channel,
long value, u32 scale_factor)
{
int ret;
u32 uval;
@ -464,117 +479,127 @@ static int xe_hwmon_power_curr_crit_write(struct xe_hwmon *hwmon, long value, u3
return ret;
}
static void xe_hwmon_get_voltage(struct xe_hwmon *hwmon, long *value)
static void xe_hwmon_get_voltage(struct xe_hwmon *hwmon, int channel, long *value)
{
u64 reg_val;
xe_hwmon_process_reg(hwmon, REG_GT_PERF_STATUS,
REG_READ32, &reg_val, 0, 0);
REG_READ32, &reg_val, 0, 0, channel);
/* HW register value in units of 2.5 millivolt */
*value = DIV_ROUND_CLOSEST(REG_FIELD_GET(VOLTAGE_MASK, reg_val) * 2500, SF_VOLTAGE);
}
static umode_t
xe_hwmon_power_is_visible(struct xe_hwmon *hwmon, u32 attr, int chan)
xe_hwmon_power_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
{
u32 uval;
switch (attr) {
case hwmon_power_max:
return xe_hwmon_get_reg(hwmon, REG_PKG_RAPL_LIMIT) ? 0664 : 0;
return xe_hwmon_get_reg(hwmon, REG_PKG_RAPL_LIMIT, channel) ? 0664 : 0;
case hwmon_power_rated_max:
return xe_hwmon_get_reg(hwmon, REG_PKG_POWER_SKU) ? 0444 : 0;
return xe_hwmon_get_reg(hwmon, REG_PKG_POWER_SKU, channel) ? 0444 : 0;
case hwmon_power_crit:
return (xe_hwmon_pcode_read_i1(hwmon->gt, &uval) ||
!(uval & POWER_SETUP_I1_WATTS)) ? 0 : 0644;
if (channel == CHANNEL_PKG)
return (xe_hwmon_pcode_read_i1(hwmon->gt, &uval) ||
!(uval & POWER_SETUP_I1_WATTS)) ? 0 : 0644;
break;
case hwmon_power_label:
return xe_hwmon_get_reg(hwmon, REG_PKG_POWER_SKU_UNIT, channel) ? 0444 : 0;
default:
return 0;
}
return 0;
}
static int
xe_hwmon_power_read(struct xe_hwmon *hwmon, u32 attr, int chan, long *val)
xe_hwmon_power_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
{
switch (attr) {
case hwmon_power_max:
xe_hwmon_power_max_read(hwmon, val);
xe_hwmon_power_max_read(hwmon, channel, val);
return 0;
case hwmon_power_rated_max:
xe_hwmon_power_rated_max_read(hwmon, val);
xe_hwmon_power_rated_max_read(hwmon, channel, val);
return 0;
case hwmon_power_crit:
return xe_hwmon_power_curr_crit_read(hwmon, val, SF_POWER);
return xe_hwmon_power_curr_crit_read(hwmon, channel, val, SF_POWER);
default:
return -EOPNOTSUPP;
}
}
static int
xe_hwmon_power_write(struct xe_hwmon *hwmon, u32 attr, int chan, long val)
xe_hwmon_power_write(struct xe_hwmon *hwmon, u32 attr, int channel, long val)
{
switch (attr) {
case hwmon_power_max:
return xe_hwmon_power_max_write(hwmon, val);
return xe_hwmon_power_max_write(hwmon, channel, val);
case hwmon_power_crit:
return xe_hwmon_power_curr_crit_write(hwmon, val, SF_POWER);
return xe_hwmon_power_curr_crit_write(hwmon, channel, val, SF_POWER);
default:
return -EOPNOTSUPP;
}
}
static umode_t
xe_hwmon_curr_is_visible(const struct xe_hwmon *hwmon, u32 attr)
xe_hwmon_curr_is_visible(const struct xe_hwmon *hwmon, u32 attr, int channel)
{
u32 uval;
switch (attr) {
case hwmon_curr_crit:
return (xe_hwmon_pcode_read_i1(hwmon->gt, &uval) ||
(uval & POWER_SETUP_I1_WATTS)) ? 0 : 0644;
case hwmon_curr_label:
if (channel == CHANNEL_PKG)
return (xe_hwmon_pcode_read_i1(hwmon->gt, &uval) ||
(uval & POWER_SETUP_I1_WATTS)) ? 0 : 0644;
break;
default:
return 0;
}
return 0;
}
static int
xe_hwmon_curr_read(struct xe_hwmon *hwmon, u32 attr, long *val)
xe_hwmon_curr_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
{
switch (attr) {
case hwmon_curr_crit:
return xe_hwmon_power_curr_crit_read(hwmon, val, SF_CURR);
return xe_hwmon_power_curr_crit_read(hwmon, channel, val, SF_CURR);
default:
return -EOPNOTSUPP;
}
}
static int
xe_hwmon_curr_write(struct xe_hwmon *hwmon, u32 attr, long val)
xe_hwmon_curr_write(struct xe_hwmon *hwmon, u32 attr, int channel, long val)
{
switch (attr) {
case hwmon_curr_crit:
return xe_hwmon_power_curr_crit_write(hwmon, val, SF_CURR);
return xe_hwmon_power_curr_crit_write(hwmon, channel, val, SF_CURR);
default:
return -EOPNOTSUPP;
}
}
static umode_t
xe_hwmon_in_is_visible(struct xe_hwmon *hwmon, u32 attr)
xe_hwmon_in_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
{
switch (attr) {
case hwmon_in_input:
return xe_hwmon_get_reg(hwmon, REG_GT_PERF_STATUS) ? 0444 : 0;
case hwmon_in_label:
return xe_hwmon_get_reg(hwmon, REG_GT_PERF_STATUS, channel) ? 0444 : 0;
default:
return 0;
}
}
static int
xe_hwmon_in_read(struct xe_hwmon *hwmon, u32 attr, long *val)
xe_hwmon_in_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
{
switch (attr) {
case hwmon_in_input:
xe_hwmon_get_voltage(hwmon, val);
xe_hwmon_get_voltage(hwmon, channel, val);
return 0;
default:
return -EOPNOTSUPP;
@ -582,22 +607,23 @@ xe_hwmon_in_read(struct xe_hwmon *hwmon, u32 attr, long *val)
}
static umode_t
xe_hwmon_energy_is_visible(struct xe_hwmon *hwmon, u32 attr)
xe_hwmon_energy_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
{
switch (attr) {
case hwmon_energy_input:
return xe_hwmon_get_reg(hwmon, REG_PKG_ENERGY_STATUS) ? 0444 : 0;
case hwmon_energy_label:
return xe_hwmon_get_reg(hwmon, REG_PKG_ENERGY_STATUS, channel) ? 0444 : 0;
default:
return 0;
}
}
static int
xe_hwmon_energy_read(struct xe_hwmon *hwmon, u32 attr, long *val)
xe_hwmon_energy_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
{
switch (attr) {
case hwmon_energy_input:
xe_hwmon_energy_get(hwmon, val);
xe_hwmon_energy_get(hwmon, channel, val);
return 0;
default:
return -EOPNOTSUPP;
@ -618,13 +644,13 @@ xe_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
ret = xe_hwmon_power_is_visible(hwmon, attr, channel);
break;
case hwmon_curr:
ret = xe_hwmon_curr_is_visible(hwmon, attr);
ret = xe_hwmon_curr_is_visible(hwmon, attr, channel);
break;
case hwmon_in:
ret = xe_hwmon_in_is_visible(hwmon, attr);
ret = xe_hwmon_in_is_visible(hwmon, attr, channel);
break;
case hwmon_energy:
ret = xe_hwmon_energy_is_visible(hwmon, attr);
ret = xe_hwmon_energy_is_visible(hwmon, attr, channel);
break;
default:
ret = 0;
@ -650,13 +676,13 @@ xe_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
ret = xe_hwmon_power_read(hwmon, attr, channel, val);
break;
case hwmon_curr:
ret = xe_hwmon_curr_read(hwmon, attr, val);
ret = xe_hwmon_curr_read(hwmon, attr, channel, val);
break;
case hwmon_in:
ret = xe_hwmon_in_read(hwmon, attr, val);
ret = xe_hwmon_in_read(hwmon, attr, channel, val);
break;
case hwmon_energy:
ret = xe_hwmon_energy_read(hwmon, attr, val);
ret = xe_hwmon_energy_read(hwmon, attr, channel, val);
break;
default:
ret = -EOPNOTSUPP;
@ -682,7 +708,7 @@ xe_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
ret = xe_hwmon_power_write(hwmon, attr, channel, val);
break;
case hwmon_curr:
ret = xe_hwmon_curr_write(hwmon, attr, val);
ret = xe_hwmon_curr_write(hwmon, attr, channel, val);
break;
default:
ret = -EOPNOTSUPP;
@ -694,10 +720,30 @@ xe_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
return ret;
}
static int xe_hwmon_read_label(struct device *dev,
enum hwmon_sensor_types type,
u32 attr, int channel, const char **str)
{
switch (type) {
case hwmon_power:
case hwmon_energy:
case hwmon_curr:
case hwmon_in:
if (channel == CHANNEL_CARD)
*str = "card";
else if (channel == CHANNEL_PKG)
*str = "pkg";
return 0;
default:
return -EOPNOTSUPP;
}
}
static const struct hwmon_ops hwmon_ops = {
.is_visible = xe_hwmon_is_visible,
.read = xe_hwmon_read,
.write = xe_hwmon_write,
.read_string = xe_hwmon_read_label,
};
static const struct hwmon_chip_info hwmon_chip_info = {
@ -711,14 +757,15 @@ xe_hwmon_get_preregistration_info(struct xe_device *xe)
struct xe_hwmon *hwmon = xe->hwmon;
long energy;
u64 val_sku_unit = 0;
int channel;
/*
* The contents of register PKG_POWER_SKU_UNIT do not change,
* so read it once and store the shift values.
*/
if (xe_hwmon_get_reg(hwmon, REG_PKG_POWER_SKU_UNIT)) {
if (xe_hwmon_get_reg(hwmon, REG_PKG_POWER_SKU_UNIT, 0)) {
xe_hwmon_process_reg(hwmon, REG_PKG_POWER_SKU_UNIT,
REG_READ32, &val_sku_unit, 0, 0);
REG_READ32, &val_sku_unit, 0, 0, 0);
hwmon->scl_shift_power = REG_FIELD_GET(PKG_PWR_UNIT, val_sku_unit);
hwmon->scl_shift_energy = REG_FIELD_GET(PKG_ENERGY_UNIT, val_sku_unit);
hwmon->scl_shift_time = REG_FIELD_GET(PKG_TIME_UNIT, val_sku_unit);
@ -728,8 +775,9 @@ xe_hwmon_get_preregistration_info(struct xe_device *xe)
* Initialize 'struct xe_hwmon_energy_info', i.e. set fields to the
* first value of the energy register read
*/
if (xe_hwmon_is_visible(hwmon, hwmon_energy, hwmon_energy_input, 0))
xe_hwmon_energy_get(hwmon, &energy);
for (channel = 0; channel < CHANNEL_MAX; channel++)
if (xe_hwmon_is_visible(hwmon, hwmon_energy, hwmon_energy_input, channel))
xe_hwmon_energy_get(hwmon, channel, &energy);
}
static void xe_hwmon_mutex_destroy(void *arg)