Skip to content

Commit 4446fcf

Browse files
bnilawarrodrigovivi
authored andcommitted
drm/xe/hwmon: Expose power1_max_interval
Expose power1_max_interval, that is the tau corresponding to PL1, as a custom hwmon attribute. Some bit manipulation is needed because of the format of PKG_PWR_LIM_1_TIME in PACKAGE_RAPL_LIMIT register (1.x * power(2,y)) v2: Get rpm wake ref while accessing power1_max_interval v3: %s/hwmon/xe_hwmon/ v4: - As power1_max_interval is rw attr take lock in read function as well - Refine comment about val to fix point conversion (Andi) - Update kernel version and date in doc v5: Fix review comments (Anshuman) Acked-by: Rodrigo Vivi <[email protected]> Reviewed-by: Himal Prasad Ghimiray <[email protected]> Reviewed-by: Anshuman Gupta <[email protected]> Signed-off-by: Badal Nilawar <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected] Signed-off-by: Rodrigo Vivi <[email protected]>
1 parent fef6dd1 commit 4446fcf

File tree

3 files changed

+169
-1
lines changed

3 files changed

+169
-1
lines changed

Documentation/ABI/testing/sysfs-driver-intel-xe-hwmon

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,12 @@ Contact: [email protected]
5959
Description: RO. Energy input of device in microjoules.
6060

6161
Only supported for particular Intel xe graphics platforms.
62+
63+
What: /sys/devices/.../hwmon/hwmon<i>/power1_max_interval
64+
Date: October 2023
65+
KernelVersion: 6.6
66+
67+
Description: RW. Sustained power limit interval (Tau in PL1/Tau) in
68+
milliseconds over which sustained power is averaged.
69+
70+
Only supported for particular Intel xe graphics platforms.

drivers/gpu/drm/xe/regs/xe_mchbar_regs.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,23 @@
2222
#define PKG_TDP GENMASK_ULL(14, 0)
2323
#define PKG_MIN_PWR GENMASK_ULL(30, 16)
2424
#define PKG_MAX_PWR GENMASK_ULL(46, 32)
25+
#define PKG_MAX_WIN GENMASK_ULL(54, 48)
26+
#define PKG_MAX_WIN_X GENMASK_ULL(54, 53)
27+
#define PKG_MAX_WIN_Y GENMASK_ULL(52, 48)
28+
2529

2630
#define PCU_CR_PACKAGE_POWER_SKU_UNIT XE_REG(MCHBAR_MIRROR_BASE_SNB + 0x5938)
2731
#define PKG_PWR_UNIT REG_GENMASK(3, 0)
2832
#define PKG_ENERGY_UNIT REG_GENMASK(12, 8)
33+
#define PKG_TIME_UNIT REG_GENMASK(19, 16)
2934

3035
#define PCU_CR_PACKAGE_ENERGY_STATUS XE_REG(MCHBAR_MIRROR_BASE_SNB + 0x593c)
3136

3237
#define PCU_CR_PACKAGE_RAPL_LIMIT XE_REG(MCHBAR_MIRROR_BASE_SNB + 0x59a0)
3338
#define PKG_PWR_LIM_1 REG_GENMASK(14, 0)
3439
#define PKG_PWR_LIM_1_EN REG_BIT(15)
40+
#define PKG_PWR_LIM_1_TIME REG_GENMASK(23, 17)
41+
#define PKG_PWR_LIM_1_TIME_X REG_GENMASK(23, 22)
42+
#define PKG_PWR_LIM_1_TIME_Y REG_GENMASK(21, 17)
3543

3644
#endif /* _XE_MCHBAR_REGS_H_ */

drivers/gpu/drm/xe/xe_hwmon.c

Lines changed: 152 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ enum xe_hwmon_reg_operation {
3838
#define SF_CURR 1000 /* milliamperes */
3939
#define SF_VOLTAGE 1000 /* millivolts */
4040
#define SF_ENERGY 1000000 /* microjoules */
41+
#define SF_TIME 1000 /* milliseconds */
4142

4243
/**
4344
* struct xe_hwmon_energy_info - to accumulate energy
@@ -63,6 +64,8 @@ struct xe_hwmon {
6364
int scl_shift_power;
6465
/** @scl_shift_energy: pkg energy unit */
6566
int scl_shift_energy;
67+
/** @scl_shift_time: pkg time unit */
68+
int scl_shift_time;
6669
/** @ei: Energy info for energy1_input */
6770
struct xe_hwmon_energy_info ei;
6871
};
@@ -253,6 +256,152 @@ xe_hwmon_energy_get(struct xe_hwmon *hwmon, long *energy)
253256
hwmon->scl_shift_energy);
254257
}
255258

259+
static ssize_t
260+
xe_hwmon_power1_max_interval_show(struct device *dev, struct device_attribute *attr,
261+
char *buf)
262+
{
263+
struct xe_hwmon *hwmon = dev_get_drvdata(dev);
264+
u32 x, y, x_w = 2; /* 2 bits */
265+
u64 r, tau4, out;
266+
267+
xe_device_mem_access_get(gt_to_xe(hwmon->gt));
268+
269+
mutex_lock(&hwmon->hwmon_lock);
270+
271+
xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT,
272+
REG_READ32, &r, 0, 0);
273+
274+
mutex_unlock(&hwmon->hwmon_lock);
275+
276+
xe_device_mem_access_put(gt_to_xe(hwmon->gt));
277+
278+
x = REG_FIELD_GET(PKG_PWR_LIM_1_TIME_X, r);
279+
y = REG_FIELD_GET(PKG_PWR_LIM_1_TIME_Y, r);
280+
281+
/*
282+
* tau = 1.x * power(2,y), x = bits(23:22), y = bits(21:17)
283+
* = (4 | x) << (y - 2)
284+
*
285+
* Here (y - 2) ensures a 1.x fixed point representation of 1.x
286+
* As x is 2 bits so 1.x can be 1.0, 1.25, 1.50, 1.75
287+
*
288+
* As y can be < 2, we compute tau4 = (4 | x) << y
289+
* and then add 2 when doing the final right shift to account for units
290+
*/
291+
tau4 = ((1 << x_w) | x) << y;
292+
293+
/* val in hwmon interface units (millisec) */
294+
out = mul_u64_u32_shr(tau4, SF_TIME, hwmon->scl_shift_time + x_w);
295+
296+
return sysfs_emit(buf, "%llu\n", out);
297+
}
298+
299+
static ssize_t
300+
xe_hwmon_power1_max_interval_store(struct device *dev, struct device_attribute *attr,
301+
const char *buf, size_t count)
302+
{
303+
struct xe_hwmon *hwmon = dev_get_drvdata(dev);
304+
u32 x, y, rxy, x_w = 2; /* 2 bits */
305+
u64 tau4, r, max_win;
306+
unsigned long val;
307+
int ret;
308+
309+
ret = kstrtoul(buf, 0, &val);
310+
if (ret)
311+
return ret;
312+
313+
/*
314+
* Max HW supported tau in '1.x * power(2,y)' format, x = 0, y = 0x12.
315+
* The hwmon->scl_shift_time default of 0xa results in a max tau of 256 seconds.
316+
*
317+
* The ideal scenario is for PKG_MAX_WIN to be read from the PKG_PWR_SKU register.
318+
* However, it is observed that existing discrete GPUs does not provide correct
319+
* PKG_MAX_WIN value, therefore a using default constant value. For future discrete GPUs
320+
* this may get resolved, in which case PKG_MAX_WIN should be obtained from PKG_PWR_SKU.
321+
*/
322+
#define PKG_MAX_WIN_DEFAULT 0x12ull
323+
324+
/*
325+
* val must be < max in hwmon interface units. The steps below are
326+
* explained in xe_hwmon_power1_max_interval_show()
327+
*/
328+
r = FIELD_PREP(PKG_MAX_WIN, PKG_MAX_WIN_DEFAULT);
329+
x = REG_FIELD_GET(PKG_MAX_WIN_X, r);
330+
y = REG_FIELD_GET(PKG_MAX_WIN_Y, r);
331+
tau4 = ((1 << x_w) | x) << y;
332+
max_win = mul_u64_u32_shr(tau4, SF_TIME, hwmon->scl_shift_time + x_w);
333+
334+
if (val > max_win)
335+
return -EINVAL;
336+
337+
/* val in hw units */
338+
val = DIV_ROUND_CLOSEST_ULL((u64)val << hwmon->scl_shift_time, SF_TIME);
339+
340+
/*
341+
* Convert val to 1.x * power(2,y)
342+
* y = ilog2(val)
343+
* x = (val - (1 << y)) >> (y - 2)
344+
*/
345+
if (!val) {
346+
y = 0;
347+
x = 0;
348+
} else {
349+
y = ilog2(val);
350+
x = (val - (1ul << y)) << x_w >> y;
351+
}
352+
353+
rxy = REG_FIELD_PREP(PKG_PWR_LIM_1_TIME_X, x) | REG_FIELD_PREP(PKG_PWR_LIM_1_TIME_Y, y);
354+
355+
xe_device_mem_access_get(gt_to_xe(hwmon->gt));
356+
357+
mutex_lock(&hwmon->hwmon_lock);
358+
359+
xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT, REG_RMW32, (u64 *)&r,
360+
PKG_PWR_LIM_1_TIME, rxy);
361+
362+
mutex_unlock(&hwmon->hwmon_lock);
363+
364+
xe_device_mem_access_put(gt_to_xe(hwmon->gt));
365+
366+
return count;
367+
}
368+
369+
static SENSOR_DEVICE_ATTR(power1_max_interval, 0664,
370+
xe_hwmon_power1_max_interval_show,
371+
xe_hwmon_power1_max_interval_store, 0);
372+
373+
static struct attribute *hwmon_attributes[] = {
374+
&sensor_dev_attr_power1_max_interval.dev_attr.attr,
375+
NULL
376+
};
377+
378+
static umode_t xe_hwmon_attributes_visible(struct kobject *kobj,
379+
struct attribute *attr, int index)
380+
{
381+
struct device *dev = kobj_to_dev(kobj);
382+
struct xe_hwmon *hwmon = dev_get_drvdata(dev);
383+
int ret = 0;
384+
385+
xe_device_mem_access_get(gt_to_xe(hwmon->gt));
386+
387+
if (attr == &sensor_dev_attr_power1_max_interval.dev_attr.attr)
388+
ret = xe_hwmon_get_reg(hwmon, REG_PKG_RAPL_LIMIT) ? attr->mode : 0;
389+
390+
xe_device_mem_access_put(gt_to_xe(hwmon->gt));
391+
392+
return ret;
393+
}
394+
395+
static const struct attribute_group hwmon_attrgroup = {
396+
.attrs = hwmon_attributes,
397+
.is_visible = xe_hwmon_attributes_visible,
398+
};
399+
400+
static const struct attribute_group *hwmon_groups[] = {
401+
&hwmon_attrgroup,
402+
NULL
403+
};
404+
256405
static const struct hwmon_channel_info *hwmon_info[] = {
257406
HWMON_CHANNEL_INFO(power, HWMON_P_MAX | HWMON_P_RATED_MAX | HWMON_P_CRIT),
258407
HWMON_CHANNEL_INFO(curr, HWMON_C_CRIT),
@@ -569,6 +718,7 @@ xe_hwmon_get_preregistration_info(struct xe_device *xe)
569718
REG_READ32, &val_sku_unit, 0, 0);
570719
hwmon->scl_shift_power = REG_FIELD_GET(PKG_PWR_UNIT, val_sku_unit);
571720
hwmon->scl_shift_energy = REG_FIELD_GET(PKG_ENERGY_UNIT, val_sku_unit);
721+
hwmon->scl_shift_time = REG_FIELD_GET(PKG_TIME_UNIT, val_sku_unit);
572722
}
573723

574724
/*
@@ -615,7 +765,8 @@ void xe_hwmon_register(struct xe_device *xe)
615765
/* hwmon_dev points to device hwmon<i> */
616766
hwmon->hwmon_dev = devm_hwmon_device_register_with_info(dev, "xe", hwmon,
617767
&hwmon_chip_info,
618-
NULL);
768+
hwmon_groups);
769+
619770
if (IS_ERR(hwmon->hwmon_dev)) {
620771
drm_warn(&xe->drm, "Failed to register xe hwmon (%pe)\n", hwmon->hwmon_dev);
621772
xe->hwmon = NULL;

0 commit comments

Comments
 (0)