drm/xe/hwmon: Expose card reactive critical power

Expose the card reactive critical (I1) power. I1 is exposed as
power1_crit in microwatts (typically for client products) or as
curr1_crit in milliamperes (typically for server).

v2: Move PCODE_MBOX macro to pcode file (Riana)
v3: s/IS_DG2/(gt_to_xe(gt)->info.platform == XE_DG2)
v4: Fix review comments (Andi)

Acked-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
Reviewed-by: Riana Tauro <riana.tauro@intel.com>
Signed-off-by: Badal Nilawar <badal.nilawar@intel.com>
Reviewed-by: Andi Shyti <andi.shyti@linux.intel.com>
Link: https://lore.kernel.org/r/20230925081842.3566834-3-badal.nilawar@intel.com
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
This commit is contained in:
Badal Nilawar 2023-09-25 13:48:39 +05:30 committed by Rodrigo Vivi
parent fb1b70607f
commit 92d44a422d
4 changed files with 142 additions and 1 deletions

View file

@ -20,3 +20,29 @@ Description: RO. Card default power limit (default TDP setting).
Only supported for particular Intel xe graphics platforms.
What: /sys/devices/.../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/devices/.../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.
Only supported for particular Intel xe graphics platforms.

View file

@ -12,6 +12,8 @@
#include "xe_gt.h"
#include "xe_hwmon.h"
#include "xe_mmio.h"
#include "xe_pcode.h"
#include "xe_pcode_api.h"
enum xe_hwmon_reg {
REG_PKG_RAPL_LIMIT,
@ -29,6 +31,7 @@ enum xe_hwmon_reg_operation {
* SF_* - scale factors for particular quantities according to hwmon spec.
*/
#define SF_POWER 1000000 /* microwatts */
#define SF_CURR 1000 /* milliamperes */
struct xe_hwmon {
struct device *hwmon_dev;
@ -184,18 +187,43 @@ static int xe_hwmon_power_rated_max_read(struct xe_hwmon *hwmon, long *value)
}
static const struct hwmon_channel_info *hwmon_info[] = {
HWMON_CHANNEL_INFO(power, HWMON_P_MAX | HWMON_P_RATED_MAX),
HWMON_CHANNEL_INFO(power, HWMON_P_MAX | HWMON_P_RATED_MAX | HWMON_P_CRIT),
HWMON_CHANNEL_INFO(curr, HWMON_C_CRIT),
NULL
};
/* I1 is exposed as power_crit or as curr_crit depending on bit 31 */
static int xe_hwmon_pcode_read_i1(struct xe_gt *gt, u32 *uval)
{
/* Avoid Illegal Subcommand error */
if (gt_to_xe(gt)->info.platform == XE_DG2)
return -ENXIO;
return xe_pcode_read(gt, PCODE_MBOX(PCODE_POWER_SETUP,
POWER_SETUP_SUBCOMMAND_READ_I1, 0),
uval, 0);
}
static int xe_hwmon_pcode_write_i1(struct xe_gt *gt, u32 uval)
{
return xe_pcode_write(gt, PCODE_MBOX(PCODE_POWER_SETUP,
POWER_SETUP_SUBCOMMAND_WRITE_I1, 0),
uval);
}
static umode_t
xe_hwmon_power_is_visible(struct xe_hwmon *hwmon, u32 attr, int chan)
{
u32 uval;
switch (attr) {
case hwmon_power_max:
return xe_hwmon_get_reg(hwmon, REG_PKG_RAPL_LIMIT) ? 0664 : 0;
case hwmon_power_rated_max:
return xe_hwmon_get_reg(hwmon, REG_PKG_POWER_SKU) ? 0444 : 0;
case hwmon_power_crit:
return (xe_hwmon_pcode_read_i1(hwmon->gt, &uval) ||
!(uval & POWER_SETUP_I1_WATTS)) ? 0 : 0644;
default:
return 0;
}
@ -204,11 +232,23 @@ xe_hwmon_power_is_visible(struct xe_hwmon *hwmon, u32 attr, int chan)
static int
xe_hwmon_power_read(struct xe_hwmon *hwmon, u32 attr, int chan, long *val)
{
int ret;
u32 uval;
switch (attr) {
case hwmon_power_max:
return xe_hwmon_power_max_read(hwmon, val);
case hwmon_power_rated_max:
return xe_hwmon_power_rated_max_read(hwmon, val);
case hwmon_power_crit:
ret = xe_hwmon_pcode_read_i1(hwmon->gt, &uval);
if (ret)
return ret;
if (!(uval & POWER_SETUP_I1_WATTS))
return -ENODEV;
*val = mul_u64_u32_shr(REG_FIELD_GET(POWER_SETUP_I1_DATA_MASK, uval),
SF_POWER, POWER_SETUP_I1_SHIFT);
return 0;
default:
return -EOPNOTSUPP;
}
@ -217,9 +257,63 @@ xe_hwmon_power_read(struct xe_hwmon *hwmon, u32 attr, int chan, long *val)
static int
xe_hwmon_power_write(struct xe_hwmon *hwmon, u32 attr, int chan, long val)
{
u32 uval;
switch (attr) {
case hwmon_power_max:
return xe_hwmon_power_max_write(hwmon, val);
case hwmon_power_crit:
uval = DIV_ROUND_CLOSEST_ULL(val << POWER_SETUP_I1_SHIFT, SF_POWER);
return xe_hwmon_pcode_write_i1(hwmon->gt, uval);
default:
return -EOPNOTSUPP;
}
}
static umode_t
xe_hwmon_curr_is_visible(const struct xe_hwmon *hwmon, u32 attr)
{
u32 uval;
switch (attr) {
case hwmon_curr_crit:
return (xe_hwmon_pcode_read_i1(hwmon->gt, &uval) ||
(uval & POWER_SETUP_I1_WATTS)) ? 0 : 0644;
default:
return 0;
}
}
static int
xe_hwmon_curr_read(struct xe_hwmon *hwmon, u32 attr, long *val)
{
int ret;
u32 uval;
switch (attr) {
case hwmon_curr_crit:
ret = xe_hwmon_pcode_read_i1(hwmon->gt, &uval);
if (ret)
return ret;
if (uval & POWER_SETUP_I1_WATTS)
return -ENODEV;
*val = mul_u64_u32_shr(REG_FIELD_GET(POWER_SETUP_I1_DATA_MASK, uval),
SF_CURR, POWER_SETUP_I1_SHIFT);
return 0;
default:
return -EOPNOTSUPP;
}
}
static int
xe_hwmon_curr_write(struct xe_hwmon *hwmon, u32 attr, long val)
{
u32 uval;
switch (attr) {
case hwmon_curr_crit:
uval = DIV_ROUND_CLOSEST_ULL(val << POWER_SETUP_I1_SHIFT, SF_CURR);
return xe_hwmon_pcode_write_i1(hwmon->gt, uval);
default:
return -EOPNOTSUPP;
}
@ -238,6 +332,9 @@ xe_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
case hwmon_power:
ret = xe_hwmon_power_is_visible(hwmon, attr, channel);
break;
case hwmon_curr:
ret = xe_hwmon_curr_is_visible(hwmon, attr);
break;
default:
ret = 0;
break;
@ -261,6 +358,9 @@ xe_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
case hwmon_power:
ret = xe_hwmon_power_read(hwmon, attr, channel, val);
break;
case hwmon_curr:
ret = xe_hwmon_curr_read(hwmon, attr, val);
break;
default:
ret = -EOPNOTSUPP;
break;
@ -284,6 +384,9 @@ xe_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
case hwmon_power:
ret = xe_hwmon_power_write(hwmon, attr, channel, val);
break;
case hwmon_curr:
ret = xe_hwmon_curr_write(hwmon, attr, val);
break;
default:
ret = -EOPNOTSUPP;
break;

View file

@ -22,4 +22,9 @@ int xe_pcode_write_timeout(struct xe_gt *gt, u32 mbox, u32 val,
int xe_pcode_request(struct xe_gt *gt, u32 mbox, u32 request,
u32 reply_mask, u32 reply, int timeout_ms);
#define PCODE_MBOX(mbcmd, param1, param2)\
(FIELD_PREP(PCODE_MB_COMMAND, mbcmd)\
| FIELD_PREP(PCODE_MB_PARAM1, param1)\
| FIELD_PREP(PCODE_MB_PARAM2, param2))
#endif

View file

@ -35,6 +35,13 @@
#define DGFX_GET_INIT_STATUS 0x0
#define DGFX_INIT_STATUS_COMPLETE 0x1
#define PCODE_POWER_SETUP 0x7C
#define POWER_SETUP_SUBCOMMAND_READ_I1 0x4
#define POWER_SETUP_SUBCOMMAND_WRITE_I1 0x5
#define POWER_SETUP_I1_WATTS REG_BIT(31)
#define POWER_SETUP_I1_SHIFT 6 /* 10.6 fixed point format */
#define POWER_SETUP_I1_DATA_MASK REG_GENMASK(15, 0)
struct pcode_err_decode {
int errno;
const char *str;