drm/xe/hwmon: Add infra to support card power and energy attributes
authorKarthik Poosa <karthik.poosa@intel.com>
Thu, 28 Mar 2024 17:54:35 +0000 (23:24 +0530)
committerRodrigo Vivi <rodrigo.vivi@intel.com>
Fri, 29 Mar 2024 15:27:21 +0000 (11:27 -0400)
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>
Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon
drivers/gpu/drm/xe/xe_hwmon.c

index 023fd82..d792a56 100644 (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
+
+What:          /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/energy1_input
 Date:          September 2023
 KernelVersion: 6.5
 Contact:       intel-xe@lists.freedesktop.org
-Description:   RW. Card reactive critical (I1) power limit in microwatts.
+Description:   RO. Card energy input of device in microjoules.
+
+               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. 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.
+
+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).
 
-               Card reactive critical (I1) power limit in microwatts is exposed
+               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.
+               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
+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. Card reactive critical (I1) power limit in milliamperes.
+Description:   RW. Package reactive critical (I1) power limit in milliamperes.
 
-               Card reactive critical (I1) power limit in milliamperes is
+               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>/in0_input
-Date:          September 2023
-KernelVersion: 6.5
+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. Current Voltage in millivolt.
+Description:   RO. Package 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>/energy1_input
-Date:          September 2023
-KernelVersion: 6.5
+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:   RO. Energy input of device in microjoules.
+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.
+               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
+What:          /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/in1_input
+Date:          February 2024
+KernelVersion: 6.8
 Contact:       intel-xe@lists.freedesktop.org
-Description:   RW. Sustained power limit interval (Tau in PL1/Tau) in
-               milliseconds over which sustained power is averaged.
+Description:   RO. Package current voltage in millivolt.
 
-               Only supported for particular Intel xe graphics platforms.
+               Only supported for particular Intel Xe graphics platforms.
index a256af8..7e8caac 100644 (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)