* [Intel-gfx] [PATCH v1 0/1] drm/i915/dg1: Add HWMON power sensor support
@ 2021-04-13 21:22 Dale B Stimson
2021-04-13 21:22 ` [Intel-gfx] [PATCH v2 1/1] " Dale B Stimson
` (3 more replies)
0 siblings, 4 replies; 8+ messages in thread
From: Dale B Stimson @ 2021-04-13 21:22 UTC (permalink / raw)
To: intel-gfx
As part of the System Managemenent Interface (SMI), use the HWMON
subsystem to display power utilization.
The following standard HWMON power sensors are currently supported
(and appropriately scaled):
/sys/class/drm/card0/device/hwmon/hwmon<i>
- energy1_input
- power1_cap
- power1_max
Some non-standard HWMON power information is also provided, such as
enable bits and intervals.
---------------------
V2 Rename local function parameter field_mask to field_msk in order to avoid
shadowing the name of function field_mask() from include/linux/bitfield.h.
V2 Change a comment introduction from "/**" to "/*", as it is not intended
to match a pattern that triggers documentation.
Reported-by: kernel test robot <lkp@intel.com>
V2 Slight movement of calls:
- i915_hwmon_init slightly later, after call to i915_setup_sysfs()
- i915_hwmon_fini slightly earlier, before i915_teardown_sysfs()
V2 Fixed some strong typing issues with le32 functions.
Detected by sparse in a run by kernel test robot:
Reported-by: kernel test robot <lkp@intel.com>
Dale B Stimson (1):
drm/i915/dg1: Add HWMON power sensor support
drivers/gpu/drm/i915/Kconfig | 1 +
drivers/gpu/drm/i915/Makefile | 1 +
drivers/gpu/drm/i915/i915_drv.c | 9 +
drivers/gpu/drm/i915/i915_drv.h | 3 +
drivers/gpu/drm/i915/i915_hwmon.c | 788 ++++++++++++++++++++++++++++++
drivers/gpu/drm/i915/i915_hwmon.h | 41 ++
drivers/gpu/drm/i915/i915_reg.h | 53 ++
7 files changed, 896 insertions(+)
create mode 100644 drivers/gpu/drm/i915/i915_hwmon.c
create mode 100644 drivers/gpu/drm/i915/i915_hwmon.h
Range-diff against v1:
1: 34631511e00c1 ! 1: 25117970961b4 drm/i915/dg1: Add HWMON power sensor support
@@ drivers/gpu/drm/i915/i915_hwmon.c (new)
+
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
++#include <linux/types.h>
+
+#include "i915_drv.h"
+#include "gt/intel_gt.h"
@@ drivers/gpu/drm/i915/i915_hwmon.c (new)
+ */
+static __always_inline u64
+_field_read_and_scale(struct intel_uncore *uncore, i915_reg_t rgadr,
-+ u32 field_mask, int nshift, unsigned int scale_factor)
++ u32 field_msk, int nshift, unsigned int scale_factor)
+{
+ intel_wakeref_t wakeref;
+ u32 reg_value;
@@ drivers/gpu/drm/i915/i915_hwmon.c (new)
+ with_intel_runtime_pm(uncore->rpm, wakeref)
+ reg_value = intel_uncore_read(uncore, rgadr);
+
-+ reg_value = le32_get_bits(reg_value, field_mask);
++ reg_value = le32_get_bits(cpu_to_le32(reg_value), field_msk);
+ scaled_val = mul_u32_u32(scale_factor, reg_value);
+
+ /* Shift, rounding to nearest */
@@ drivers/gpu/drm/i915/i915_hwmon.c (new)
+ */
+static __always_inline u64
+_field_read64_and_scale(struct intel_uncore *uncore, i915_reg_t rgadr,
-+ u64 field_mask, int nshift, unsigned int scale_factor)
++ u64 field_msk, int nshift, unsigned int scale_factor)
+{
+ intel_wakeref_t wakeref;
+ u64 reg_value;
@@ drivers/gpu/drm/i915/i915_hwmon.c (new)
+ with_intel_runtime_pm(uncore->rpm, wakeref)
+ reg_value = intel_uncore_read64(uncore, rgadr);
+
-+ reg_value = le64_get_bits(reg_value, field_mask);
++ reg_value = le64_get_bits(cpu_to_le64(reg_value), field_msk);
+ scaled_val = scale_factor * reg_value;
+
+ /* Shift, rounding to nearest */
@@ drivers/gpu/drm/i915/i915_hwmon.c (new)
+static __always_inline void
+_field_scale_and_write(struct intel_uncore *uncore,
+ i915_reg_t rgadr,
-+ u32 field_mask, int nshift,
++ u32 field_msk, int nshift,
+ unsigned int scale_factor, long lval)
+{
+ u32 nval;
@@ drivers/gpu/drm/i915/i915_hwmon.c (new)
+ /* Computation in 64-bits to avoid overflow. Round to nearest. */
+ nval = DIV_ROUND_CLOSEST_ULL((u64)lval << nshift, scale_factor);
+
-+ bits_to_clear = field_mask;
-+ bits_to_set = le32_encode_bits(nval, field_mask);
++ bits_to_clear = field_msk;
++ bits_to_set = le32_to_cpu(le32_encode_bits(nval, field_msk));
+
+ _locked_with_pm_intel_uncore_rmw(uncore, rgadr,
+ bits_to_clear, bits_to_set);
@@ drivers/gpu/drm/i915/i915_hwmon.c (new)
+ struct intel_uncore *uncore = &i915->uncore;
+ intel_wakeref_t wakeref;
+ u32 val_sku_unit;
++ __le32 le_sku_unit;
+
+ if (IS_DG1(i915)) {
+ hwmon->rg.pkg_power_sku_unit = PCU_PACKAGE_POWER_SKU_UNIT;
@@ drivers/gpu/drm/i915/i915_hwmon.c (new)
+
+ intel_runtime_pm_put(uncore->rpm, wakeref);
+
-+ hwmon->scl_shift_power = le32_get_bits(val_sku_unit, PKG_PWR_UNIT);
-+ hwmon->scl_shift_energy = le32_get_bits(val_sku_unit, PKG_ENERGY_UNIT);
-+ hwmon->scl_shift_time = le32_get_bits(val_sku_unit, PKG_TIME_UNIT);
++ le_sku_unit = cpu_to_le32(val_sku_unit);
++ hwmon->scl_shift_power = le32_get_bits(le_sku_unit, PKG_PWR_UNIT);
++ hwmon->scl_shift_energy = le32_get_bits(le_sku_unit, PKG_ENERGY_UNIT);
++ hwmon->scl_shift_time = le32_get_bits(le_sku_unit, PKG_TIME_UNIT);
+
+ /*
+ * There is no direct way to obtain the power default_limit.
--
2.31.1
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply [flat|nested] 8+ messages in thread
* [Intel-gfx] [PATCH v2 1/1] drm/i915/dg1: Add HWMON power sensor support
2021-04-13 21:22 [Intel-gfx] [PATCH v1 0/1] drm/i915/dg1: Add HWMON power sensor support Dale B Stimson
@ 2021-04-13 21:22 ` Dale B Stimson
2021-04-21 15:03 ` Jani Nikula
2021-04-13 22:02 ` [Intel-gfx] ✗ Fi.CI.CHECKPATCH: warning for drm/i915/dg1: Add HWMON power sensor support (rev2) Patchwork
` (2 subsequent siblings)
3 siblings, 1 reply; 8+ messages in thread
From: Dale B Stimson @ 2021-04-13 21:22 UTC (permalink / raw)
To: intel-gfx
As part of the System Managemenent Interface (SMI), use the HWMON
subsystem to display power utilization.
The following standard HWMON power sensors are currently supported
(and appropriately scaled):
/sys/class/drm/card0/device/hwmon/hwmon<i>
- energy1_input
- power1_cap
- power1_max
Some non-standard HWMON power information is also provided, such as
enable bits and intervals.
Signed-off-by: Dale B Stimson <dale.b.stimson@intel.com>
---
drivers/gpu/drm/i915/Kconfig | 1 +
drivers/gpu/drm/i915/Makefile | 1 +
drivers/gpu/drm/i915/i915_drv.c | 9 +
drivers/gpu/drm/i915/i915_drv.h | 3 +
drivers/gpu/drm/i915/i915_hwmon.c | 788 ++++++++++++++++++++++++++++++
drivers/gpu/drm/i915/i915_hwmon.h | 41 ++
drivers/gpu/drm/i915/i915_reg.h | 53 ++
7 files changed, 896 insertions(+)
create mode 100644 drivers/gpu/drm/i915/i915_hwmon.c
create mode 100644 drivers/gpu/drm/i915/i915_hwmon.h
diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig
index 1e1cb245fca77..ec8d5a0d7ea96 100644
--- a/drivers/gpu/drm/i915/Kconfig
+++ b/drivers/gpu/drm/i915/Kconfig
@@ -14,6 +14,7 @@ config DRM_I915
select DRM_MIPI_DSI
select RELAY
select IRQ_WORK
+ select HWMON
# i915 depends on ACPI_VIDEO when ACPI is enabled
# but for select to work, need to select ACPI_VIDEO's dependencies, ick
select BACKLIGHT_CLASS_DEVICE if ACPI
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index d0d936d9137bc..e213e2b129e20 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -37,6 +37,7 @@ i915-y += i915_drv.o \
i915_config.o \
i915_irq.o \
i915_getparam.o \
+ i915_hwmon.o \
i915_mitigations.o \
i915_params.o \
i915_pci.o \
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 305557e1942aa..84c7de3b34c7d 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -69,6 +69,7 @@
#include "i915_debugfs.h"
#include "i915_drv.h"
+#include "i915_hwmon.h"
#include "i915_ioc32.h"
#include "i915_irq.h"
#include "i915_memcpy.h"
@@ -675,6 +676,10 @@ static void i915_driver_register(struct drm_i915_private *dev_priv)
i915_debugfs_register(dev_priv);
i915_setup_sysfs(dev_priv);
+ /* Register with hwmon */
+ if (i915_hwmon_init(&dev_priv->drm))
+ drm_err(&dev_priv->drm, "Failed to register driver hwmon!\n");
+
/* Depends on sysfs having been initialized */
i915_perf_register(dev_priv);
@@ -709,9 +714,13 @@ static void i915_driver_unregister(struct drm_i915_private *dev_priv)
intel_gt_driver_unregister(&dev_priv->gt);
i915_perf_unregister(dev_priv);
+
+ i915_hwmon_fini(&dev_priv->drm);
+
i915_pmu_unregister(dev_priv);
i915_teardown_sysfs(dev_priv);
+
drm_dev_unplug(&dev_priv->drm);
i915_gem_driver_unregister(dev_priv);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 69e43bf91a153..7e9b452c77e2b 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -61,6 +61,7 @@
#include <drm/drm_connector.h>
#include <drm/i915_mei_hdcp_interface.h>
+#include "i915_hwmon.h"
#include "i915_params.h"
#include "i915_reg.h"
#include "i915_utils.h"
@@ -1109,6 +1110,8 @@ struct drm_i915_private {
struct i915_perf perf;
+ struct i915_hwmon hwmon;
+
/* Abstract the submission mechanism (legacy ringbuffer or execlists) away */
struct intel_gt gt;
diff --git a/drivers/gpu/drm/i915/i915_hwmon.c b/drivers/gpu/drm/i915/i915_hwmon.c
new file mode 100644
index 0000000000000..ab8f32f7ed1de
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_hwmon.c
@@ -0,0 +1,788 @@
+// SPDX-License-Identifier: MIT
+
+/*
+ * Copyright © 2020 Intel Corporation
+ */
+
+/*
+ * Power-related hwmon entries.
+ */
+
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/types.h>
+
+#include "i915_drv.h"
+#include "gt/intel_gt.h"
+#include "i915_hwmon.h"
+
+/*
+ * SF_* - scale factors for particular quantities.
+ * The hwmon standard says that quantities of the given types are specified
+ * in the given units:
+ * - time - milliseconds
+ * - power - microwatts
+ * - energy - microjoules
+ */
+
+#define SF_TIME 1000
+#define SF_POWER 1000000
+#define SF_ENERGY 1000000
+
+static void
+_locked_with_pm_intel_uncore_rmw(struct intel_uncore *uncore,
+ i915_reg_t reg, u32 clear, u32 set)
+{
+ struct drm_i915_private *i915 = uncore->i915;
+ struct i915_hwmon *hwmon = &i915->hwmon;
+ intel_wakeref_t wakeref;
+
+ mutex_lock(&hwmon->hwmon_lock);
+
+ with_intel_runtime_pm(uncore->rpm, wakeref)
+ intel_uncore_rmw(uncore, reg, clear, set);
+
+ mutex_unlock(&hwmon->hwmon_lock);
+}
+
+/*
+ * _field_read_and_scale()
+ * Return type of u64 allows for the case where the scaling might cause a
+ * result exceeding 32 bits.
+ */
+static __always_inline u64
+_field_read_and_scale(struct intel_uncore *uncore, i915_reg_t rgadr,
+ u32 field_msk, int nshift, unsigned int scale_factor)
+{
+ intel_wakeref_t wakeref;
+ u32 reg_value;
+ u64 scaled_val;
+
+ with_intel_runtime_pm(uncore->rpm, wakeref)
+ reg_value = intel_uncore_read(uncore, rgadr);
+
+ reg_value = le32_get_bits(cpu_to_le32(reg_value), field_msk);
+ scaled_val = mul_u32_u32(scale_factor, reg_value);
+
+ /* Shift, rounding to nearest */
+ if (nshift > 0)
+ scaled_val = (scaled_val + (1 << (nshift - 1))) >> nshift;
+
+ return scaled_val;
+}
+
+/*
+ * _field_read64_and_scale() - read a 64-bit register and scale.
+ */
+static __always_inline u64
+_field_read64_and_scale(struct intel_uncore *uncore, i915_reg_t rgadr,
+ u64 field_msk, int nshift, unsigned int scale_factor)
+{
+ intel_wakeref_t wakeref;
+ u64 reg_value;
+ u64 scaled_val;
+
+ with_intel_runtime_pm(uncore->rpm, wakeref)
+ reg_value = intel_uncore_read64(uncore, rgadr);
+
+ reg_value = le64_get_bits(cpu_to_le64(reg_value), field_msk);
+ scaled_val = scale_factor * reg_value;
+
+ /* Shift, rounding to nearest */
+ if (nshift > 0)
+ scaled_val = (scaled_val + (1 << (nshift - 1))) >> nshift;
+
+ return scaled_val;
+}
+
+/*
+ * _field_scale_and_write()
+ */
+static __always_inline void
+_field_scale_and_write(struct intel_uncore *uncore,
+ i915_reg_t rgadr,
+ u32 field_msk, int nshift,
+ unsigned int scale_factor, long lval)
+{
+ u32 nval;
+ u32 bits_to_clear;
+ u32 bits_to_set;
+
+ /* Computation in 64-bits to avoid overflow. Round to nearest. */
+ nval = DIV_ROUND_CLOSEST_ULL((u64)lval << nshift, scale_factor);
+
+ bits_to_clear = field_msk;
+ bits_to_set = le32_to_cpu(le32_encode_bits(nval, field_msk));
+
+ _locked_with_pm_intel_uncore_rmw(uncore, rgadr,
+ bits_to_clear, bits_to_set);
+}
+
+/*
+ * i915_energy1_input_show - A custom function to obtain energy1_input.
+ * Use a custom function instead of the usual hwmon helpers in order to
+ * guarantee 64-bits of result to user-space.
+ * Units are microjoules.
+ *
+ * The underlying hardware register is 32-bits and is subject to overflow.
+ * This function compensates for overflow of the 32-bit register by detecting
+ * wrap-around and incrementing an overflow counter.
+ * This only works if the register is sampled often enough to avoid
+ * missing an instance of overflow - achieved either by repeated
+ * queries through the API, or via a possible timer (future - TBD) that
+ * ensures values are read often enough to catch all overflows.
+ *
+ * How long before overflow? For example, with an example scaling bit
+ * shift of 14 bits (see register *PACKAGE_POWER_SKU_UNIT) and a power draw of
+ * 1000 watts, the 32-bit counter will overflow in approximately 4.36 minutes.
+ *
+ * Examples:
+ * 1 watt: (2^32 >> 14) / 1 W / (60 * 60 * 24) secs/day -> 3 days
+ * 1000 watts: (2^32 >> 14) / 1000 W / 60 secs/min -> 4.36 minutes
+ */
+static ssize_t
+i915_energy1_input_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_i915_private *i915 = dev_get_drvdata(dev);
+ struct intel_uncore *uncore = &i915->uncore;
+ struct i915_hwmon *hwmon = &i915->hwmon;
+ int nshift = hwmon->scl_shift_energy;
+ ssize_t ret;
+ intel_wakeref_t wakeref;
+ u32 reg_value;
+ u64 vlo;
+ u64 vhi;
+
+ mutex_lock(&hwmon->hwmon_lock);
+
+ with_intel_runtime_pm(uncore->rpm, wakeref)
+ reg_value = intel_uncore_read(uncore,
+ hwmon->rg.reg_energy_status);
+
+ /*
+ * The u32 register concatenated with the u32 overflow counter
+ * gives an effective energy counter size of 64-bits. However, the
+ * computations below are done modulo 2^96 to avoid overflow during
+ * scaling in the conversion to microjoules.
+ *
+ * The low-order 64-bits of the resulting quantity are returned to
+ * the caller in units of microjoules, encoded into a decimal string.
+ *
+ * For a power of 1000 watts, 64 bits in units of microjoules will
+ * overflow after 584 years.
+ */
+
+ if (hwmon->energy_counter_prev > reg_value)
+ hwmon->energy_counter_overflow++;
+
+ hwmon->energy_counter_prev = reg_value;
+
+ /*
+ * 64-bit variables vlo and vhi are used for the scaling process.
+ * The 96-bit counter value is composed from the two 64-bit variables
+ * vhi and vlo thusly: counter == vhi << 32 + vlo .
+ * The 32-bits of overlap between the two variables is convenient for
+ * handling overflows out of vlo.
+ */
+
+ vlo = reg_value;
+ vhi = hwmon->energy_counter_overflow;
+
+ mutex_unlock(&hwmon->hwmon_lock);
+
+ vlo = SF_ENERGY * vlo;
+
+ /* Prepare to round to nearest */
+ if (nshift > 0)
+ vlo += 1 << (nshift - 1);
+
+ /*
+ * Anything in the upper-32 bits of vlo gets added into vhi here,
+ * and then cleared from vlo.
+ */
+ vhi = (SF_ENERGY * vhi) + (vlo >> 32);
+ vlo &= 0xffffffffULL;
+
+ /*
+ * Apply the right shift.
+ * - vlo shifted by itself.
+ * - vlo receiving what's shifted out of vhi.
+ * - vhi shifted by itself
+ */
+ vlo = vlo >> nshift;
+ vlo |= (vhi << (32 - nshift)) & 0xffffffffULL;
+ vhi = vhi >> nshift;
+
+ /* Combined to get a 64-bit result in vlo. */
+ vlo |= (vhi << 32);
+
+ ret = scnprintf(buf, PAGE_SIZE, "%llu\n", vlo);
+
+ return ret;
+}
+
+static ssize_t
+i915_power1_max_enable_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_i915_private *i915 = dev_get_drvdata(dev);
+ struct intel_uncore *uncore = &i915->uncore;
+ intel_wakeref_t wakeref;
+ ssize_t ret;
+ u32 reg_value;
+ bool is_enabled;
+
+ with_intel_runtime_pm(uncore->rpm, wakeref)
+ reg_value = intel_uncore_read(uncore,
+ i915->hwmon.rg.pkg_rapl_limit);
+
+ is_enabled = !!(reg_value & PKG_PWR_LIM_1_EN);
+
+ ret = scnprintf(buf, PAGE_SIZE, "%u\n", is_enabled);
+
+ return ret;
+}
+
+static ssize_t
+i915_power1_max_enable_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct drm_i915_private *i915 = dev_get_drvdata(dev);
+ struct intel_uncore *uncore = &i915->uncore;
+ struct i915_hwmon *hwmon = &i915->hwmon;
+ ssize_t ret;
+ u32 val;
+ u32 bits_to_clear;
+ u32 bits_to_set;
+
+ ret = kstrtou32(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ bits_to_clear = PKG_PWR_LIM_1_EN;
+ if (!val)
+ bits_to_set = 0;
+ else
+ bits_to_set = PKG_PWR_LIM_1_EN;
+
+ _locked_with_pm_intel_uncore_rmw(uncore, hwmon->rg.pkg_rapl_limit,
+ bits_to_clear, bits_to_set);
+
+ return count;
+}
+
+static ssize_t
+i915_power1_max_interval_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_i915_private *i915 = dev_get_drvdata(dev);
+ struct intel_uncore *uncore = &i915->uncore;
+ struct i915_hwmon *hwmon = &i915->hwmon;
+ ssize_t ret;
+ u64 ullval;
+
+ ullval = _field_read_and_scale(uncore, hwmon->rg.pkg_rapl_limit,
+ PKG_PWR_LIM_1_TIME,
+ hwmon->scl_shift_time, SF_TIME);
+
+ ret = scnprintf(buf, PAGE_SIZE, "%llu\n", ullval);
+
+ return ret;
+}
+
+static ssize_t
+i915_power1_max_interval_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct drm_i915_private *i915 = dev_get_drvdata(dev);
+ struct intel_uncore *uncore = &i915->uncore;
+ struct i915_hwmon *hwmon = &i915->hwmon;
+ ssize_t ret;
+ long val;
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ _field_scale_and_write(uncore, hwmon->rg.pkg_rapl_limit,
+ PKG_PWR_LIM_2_TIME,
+ hwmon->scl_shift_time, SF_TIME, val);
+
+ return count;
+}
+
+static ssize_t
+i915_power1_cap_enable_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_i915_private *i915 = dev_get_drvdata(dev);
+ struct intel_uncore *uncore = &i915->uncore;
+ struct i915_hwmon *hwmon = &i915->hwmon;
+ intel_wakeref_t wakeref;
+ ssize_t ret;
+ u32 reg_value;
+ bool is_enabled;
+
+ with_intel_runtime_pm(uncore->rpm, wakeref)
+ reg_value = intel_uncore_read(uncore,
+ hwmon->rg.pkg_rapl_limit_udw);
+
+ is_enabled = !!(reg_value & PKG_PWR_LIM_2_EN);
+
+ ret = scnprintf(buf, PAGE_SIZE, "%u\n", is_enabled);
+
+ return ret;
+}
+
+static ssize_t
+i915_power1_cap_enable_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct drm_i915_private *i915 = dev_get_drvdata(dev);
+ struct intel_uncore *uncore = &i915->uncore;
+ struct i915_hwmon *hwmon = &i915->hwmon;
+ ssize_t ret;
+ u32 val;
+ u32 bits_to_clear;
+ u32 bits_to_set;
+
+ ret = kstrtou32(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ bits_to_clear = PKG_PWR_LIM_2_EN;
+ if (!val)
+ bits_to_set = 0;
+ else
+ bits_to_set = PKG_PWR_LIM_2_EN;
+
+ _locked_with_pm_intel_uncore_rmw(uncore, hwmon->rg.pkg_rapl_limit_udw,
+ bits_to_clear, bits_to_set);
+
+ return count;
+}
+
+static ssize_t
+i915_power_default_limit_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_i915_private *i915 = dev_get_drvdata(dev);
+ struct i915_hwmon *hwmon = &i915->hwmon;
+ ssize_t ret;
+
+ ret = scnprintf(buf, PAGE_SIZE, "%u\n", hwmon->power_max_initial_value);
+
+ return ret;
+}
+
+static ssize_t
+i915_power_min_limit_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_i915_private *i915 = dev_get_drvdata(dev);
+ struct intel_uncore *uncore = &i915->uncore;
+ struct i915_hwmon *hwmon = &i915->hwmon;
+ ssize_t ret;
+ u32 uval;
+
+ /*
+ * This is a 64-bit register but the individual fields are under 32 bits
+ * in size even after scaling.
+ * The UAPI specifies a size of 32 bits.
+ * The UAPI specifies that 0 should be returned if unsupported.
+ * So, using u32 and %u is sufficient.
+ */
+ if (i915_mmio_reg_valid(hwmon->rg.pkg_power_sku))
+ uval = (u32)_field_read64_and_scale(uncore,
+ hwmon->rg.pkg_power_sku,
+ PKG_MIN_PWR,
+ hwmon->scl_shift_power,
+ SF_POWER);
+ else
+ uval = 0;
+
+ ret = scnprintf(buf, PAGE_SIZE, "%u\n", uval);
+
+ return ret;
+}
+
+static ssize_t
+i915_power_max_limit_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct drm_i915_private *i915 = dev_get_drvdata(dev);
+ struct intel_uncore *uncore = &i915->uncore;
+ struct i915_hwmon *hwmon = &i915->hwmon;
+ ssize_t ret;
+ u32 uval;
+
+ /*
+ * This is a 64-bit register but the individual fields are under 32 bits
+ * in size even after scaling.
+ * The UAPI specifies a size of 32 bits.
+ * The UAPI specifies that UINT_MAX should be returned if unsupported.
+ * So, using u32 and %u is sufficient.
+ */
+ if (i915_mmio_reg_valid(hwmon->rg.pkg_power_sku))
+ uval = (u32)_field_read64_and_scale(uncore,
+ hwmon->rg.pkg_power_sku,
+ PKG_MAX_PWR,
+ hwmon->scl_shift_power,
+ SF_POWER);
+ else
+ uval = UINT_MAX;
+
+ ret = scnprintf(buf, PAGE_SIZE, "%u\n", uval);
+
+ return ret;
+}
+
+static SENSOR_DEVICE_ATTR(power1_max_enable, 0664,
+ i915_power1_max_enable_show,
+ i915_power1_max_enable_store, 0);
+static SENSOR_DEVICE_ATTR(power1_max_interval, 0664,
+ i915_power1_max_interval_show,
+ i915_power1_max_interval_store, 0);
+static SENSOR_DEVICE_ATTR(power1_cap_enable, 0664,
+ i915_power1_cap_enable_show,
+ i915_power1_cap_enable_store, 0);
+static SENSOR_DEVICE_ATTR(power_default_limit, 0444,
+ i915_power_default_limit_show, NULL, 0);
+static SENSOR_DEVICE_ATTR(power_min_limit, 0444,
+ i915_power_min_limit_show, NULL, 0);
+static SENSOR_DEVICE_ATTR(power_max_limit, 0444,
+ i915_power_max_limit_show, NULL, 0);
+static SENSOR_DEVICE_ATTR(energy1_input, 0444,
+ i915_energy1_input_show, NULL, 0);
+
+static struct attribute *hwmon_attributes[] = {
+ &sensor_dev_attr_power1_max_enable.dev_attr.attr,
+ &sensor_dev_attr_power1_max_interval.dev_attr.attr,
+ &sensor_dev_attr_power1_cap_enable.dev_attr.attr,
+ &sensor_dev_attr_power_default_limit.dev_attr.attr,
+ &sensor_dev_attr_power_min_limit.dev_attr.attr,
+ &sensor_dev_attr_power_max_limit.dev_attr.attr,
+ &sensor_dev_attr_energy1_input.dev_attr.attr,
+ NULL
+};
+
+static umode_t hwmon_attributes_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct drm_i915_private *i915 = dev_get_drvdata(dev);
+ struct i915_hwmon *hwmon = &i915->hwmon;
+ i915_reg_t rgadr;
+
+ if (attr == &sensor_dev_attr_energy1_input.dev_attr.attr)
+ rgadr = hwmon->rg.reg_energy_status;
+ else if (attr == &sensor_dev_attr_power1_max_enable.dev_attr.attr)
+ rgadr = hwmon->rg.pkg_rapl_limit;
+ else if (attr == &sensor_dev_attr_power1_max_interval.dev_attr.attr)
+ rgadr = hwmon->rg.pkg_rapl_limit;
+ else if (attr == &sensor_dev_attr_power1_cap_enable.dev_attr.attr)
+ rgadr = hwmon->rg.pkg_rapl_limit_udw;
+ else if (attr == &sensor_dev_attr_power_default_limit.dev_attr.attr)
+ rgadr = hwmon->rg.pkg_rapl_limit;
+ else if (attr == &sensor_dev_attr_power_min_limit.dev_attr.attr)
+ return attr->mode;
+ else if (attr == &sensor_dev_attr_power_max_limit.dev_attr.attr)
+ return attr->mode;
+ else
+ return 0;
+
+ if (!i915_mmio_reg_valid(rgadr))
+ return 0;
+
+ return attr->mode;
+}
+
+static const struct attribute_group hwmon_attrgroup = {
+ .attrs = hwmon_attributes,
+ .is_visible = hwmon_attributes_visible,
+};
+
+static const struct attribute_group *hwmon_groups[] = {
+ &hwmon_attrgroup,
+ NULL
+};
+
+/*
+ * HWMON SENSOR TYPE = hwmon_power
+ * - Sustained Power (power1_max)
+ * - Burst power (power1_cap)
+ * - Peak power (power1_crit)
+ */
+static const u32 i915_config_power[] = {
+ HWMON_P_CAP | HWMON_P_MAX,
+ 0
+};
+
+static const struct hwmon_channel_info i915_power = {
+ .type = hwmon_power,
+ .config = i915_config_power,
+};
+
+static const struct hwmon_channel_info *i915_info[] = {
+ &i915_power,
+ NULL
+};
+
+static umode_t
+i915_power_is_visible(const struct drm_i915_private *i915, u32 attr, int chan)
+{
+ i915_reg_t rgadr;
+
+ switch (attr) {
+ case hwmon_power_max:
+ rgadr = i915->hwmon.rg.pkg_rapl_limit;
+ break;
+ case hwmon_power_cap:
+ rgadr = i915->hwmon.rg.pkg_rapl_limit_udw;
+ break;
+ default:
+ return 0;
+ }
+
+ if (!i915_mmio_reg_valid(rgadr))
+ return 0;
+
+ return 0664;
+}
+
+static int
+i915_power_read(struct drm_i915_private *i915, u32 attr, int chan, long *val)
+{
+ struct intel_uncore *uncore = &i915->uncore;
+ struct i915_hwmon *hwmon = &i915->hwmon;
+ int ret = 0;
+
+ switch (attr) {
+ case hwmon_power_max:
+ *val = (long)_field_read_and_scale(uncore,
+ hwmon->rg.pkg_rapl_limit,
+ PKG_PWR_LIM_1,
+ hwmon->scl_shift_power,
+ SF_POWER);
+ break;
+ case hwmon_power_cap:
+ *val = (long)_field_read_and_scale(uncore,
+ hwmon->rg.pkg_rapl_limit_udw,
+ PKG_PWR_LIM_2,
+ hwmon->scl_shift_power,
+ SF_POWER);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ }
+
+ return ret;
+}
+
+static int
+i915_power_write(struct drm_i915_private *i915, u32 attr, int chan, long val)
+{
+ struct intel_uncore *uncore = &i915->uncore;
+ struct i915_hwmon *hwmon = &i915->hwmon;
+ int ret = 0;
+
+ switch (attr) {
+ case hwmon_power_max:
+ _field_scale_and_write(uncore,
+ hwmon->rg.pkg_rapl_limit,
+ PKG_PWR_LIM_1,
+ hwmon->scl_shift_power,
+ SF_POWER, val);
+ break;
+ case hwmon_power_cap:
+ _field_scale_and_write(uncore,
+ hwmon->rg.pkg_rapl_limit_udw,
+ PKG_PWR_LIM_2,
+ hwmon->scl_shift_power,
+ SF_POWER, val);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ }
+
+ return ret;
+}
+
+static umode_t
+i915_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ struct drm_i915_private *i915 = (struct drm_i915_private *)data;
+
+ switch (type) {
+ case hwmon_power:
+ return i915_power_is_visible(i915, attr, channel);
+ default:
+ return 0;
+ }
+}
+
+static int
+i915_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
+ int channel, long *val)
+{
+ struct drm_i915_private *i915 = kdev_to_i915(dev);
+
+ switch (type) {
+ case hwmon_power:
+ return i915_power_read(i915, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int
+i915_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
+ int channel, long val)
+{
+ struct drm_i915_private *i915 = kdev_to_i915(dev);
+
+ switch (type) {
+ case hwmon_power:
+ return i915_power_write(i915, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static const struct hwmon_ops i915_hwmon_ops = {
+ .is_visible = i915_is_visible,
+ .read = i915_read,
+ .write = i915_write,
+};
+
+static const struct hwmon_chip_info i915_chip_info = {
+ .ops = &i915_hwmon_ops,
+ .info = i915_info,
+};
+
+static void
+i915_hwmon_get_preregistration_info(struct drm_i915_private *i915)
+{
+ struct i915_hwmon *hwmon = &i915->hwmon;
+ struct intel_uncore *uncore = &i915->uncore;
+ intel_wakeref_t wakeref;
+ u32 val_sku_unit;
+ __le32 le_sku_unit;
+
+ if (IS_DG1(i915)) {
+ hwmon->rg.pkg_power_sku_unit = PCU_PACKAGE_POWER_SKU_UNIT;
+ hwmon->rg.pkg_power_sku = PCU_PACKAGE_POWER_SKU;
+ hwmon->rg.pkg_energy_status = PCU_PACKAGE_ENERGY_STATUS;
+ hwmon->rg.pkg_rapl_limit = PCU_PACKAGE_RAPL_LIMIT;
+ hwmon->rg.pkg_rapl_limit_udw = PCU_PACKAGE_RAPL_LIMIT_UDW;
+ hwmon->rg.plt_energy_status = PCU_PLATFORM_ENERGY_STATUS;
+ } else {
+ hwmon->rg.pkg_power_sku_unit = INVALID_MMIO_REG;
+ hwmon->rg.pkg_power_sku = INVALID_MMIO_REG;
+ hwmon->rg.pkg_energy_status = INVALID_MMIO_REG;
+ hwmon->rg.pkg_rapl_limit = INVALID_MMIO_REG;
+ hwmon->rg.pkg_rapl_limit_udw = INVALID_MMIO_REG;
+ hwmon->rg.plt_energy_status = INVALID_MMIO_REG;
+ }
+
+ /*
+ * If a platform does not support *_PLATFORM_ENERGY_STATUS,
+ * try *PACKAGE_ENERGY_STATUS.
+ */
+ if (i915_mmio_reg_valid(hwmon->rg.plt_energy_status))
+ hwmon->rg.reg_energy_status = hwmon->rg.plt_energy_status;
+ else
+ hwmon->rg.reg_energy_status = hwmon->rg.pkg_energy_status;
+
+ wakeref = intel_runtime_pm_get(uncore->rpm);
+
+ /*
+ * The contents of register hwmon->rg.pkg_power_sku_unit do not change,
+ * so read it once and store the shift values.
+ */
+ if (i915_mmio_reg_valid(hwmon->rg.pkg_power_sku_unit))
+ val_sku_unit = intel_uncore_read(uncore,
+ hwmon->rg.pkg_power_sku_unit);
+ else
+ val_sku_unit = 0;
+
+ hwmon->energy_counter_overflow = 0;
+
+ if (i915_mmio_reg_valid(hwmon->rg.reg_energy_status))
+ hwmon->energy_counter_prev =
+ intel_uncore_read(uncore, hwmon->rg.reg_energy_status);
+ else
+ hwmon->energy_counter_prev = 0;
+
+ intel_runtime_pm_put(uncore->rpm, wakeref);
+
+ le_sku_unit = cpu_to_le32(val_sku_unit);
+ hwmon->scl_shift_power = le32_get_bits(le_sku_unit, PKG_PWR_UNIT);
+ hwmon->scl_shift_energy = le32_get_bits(le_sku_unit, PKG_ENERGY_UNIT);
+ hwmon->scl_shift_time = le32_get_bits(le_sku_unit, PKG_TIME_UNIT);
+
+ /*
+ * There is no direct way to obtain the power default_limit.
+ * The best known workaround is to use the initial value of power1_max.
+ *
+ * The value of power1_max is reset to the default on reboot, but is
+ * not reset by a module unload/load sequence. To allow proper
+ * functioning after a module reload, the value for power1_max is
+ * restored to its original value at module unload time in
+ * i915_hwmon_fini().
+ */
+ hwmon->power_max_initial_value =
+ (u32)_field_read_and_scale(uncore,
+ hwmon->rg.pkg_rapl_limit,
+ PKG_PWR_LIM_1,
+ hwmon->scl_shift_power, SF_POWER);
+}
+
+int i915_hwmon_init(struct drm_device *drm_dev)
+{
+ struct drm_i915_private *i915 = to_i915(drm_dev);
+ struct i915_hwmon *hwmon = &i915->hwmon;
+ struct device *hwmon_dev;
+
+ mutex_init(&hwmon->hwmon_lock);
+
+ i915_hwmon_get_preregistration_info(i915);
+
+ hwmon_dev = hwmon_device_register_with_info(drm_dev->dev, "i915",
+ drm_dev,
+ &i915_chip_info,
+ hwmon_groups);
+
+ if (IS_ERR(hwmon_dev)) {
+ mutex_destroy(&hwmon->hwmon_lock);
+ return PTR_ERR(hwmon_dev);
+ }
+
+ hwmon->dev = hwmon_dev;
+
+ return 0;
+}
+
+void i915_hwmon_fini(struct drm_device *drm_dev)
+{
+ struct drm_i915_private *i915 = to_i915(drm_dev);
+ struct i915_hwmon *hwmon = &i915->hwmon;
+
+ if (hwmon->power_max_initial_value) {
+ /* Restore power1_max. */
+ _field_scale_and_write(&i915->uncore, hwmon->rg.pkg_rapl_limit,
+ PKG_PWR_LIM_1, hwmon->scl_shift_power,
+ SF_POWER,
+ hwmon->power_max_initial_value);
+ }
+
+ if (hwmon->dev)
+ hwmon_device_unregister(hwmon->dev);
+
+ mutex_destroy(&hwmon->hwmon_lock);
+
+ memset(hwmon, 0, sizeof(*hwmon));
+}
diff --git a/drivers/gpu/drm/i915/i915_hwmon.h b/drivers/gpu/drm/i915/i915_hwmon.h
new file mode 100644
index 0000000000000..0be919f0a463d
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_hwmon.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: MIT */
+
+/*
+ * Copyright © 2020 Intel Corporation
+ */
+
+#ifndef __INTEL_HWMON_H__
+#define __INTEL_HWMON_H__
+
+#include <drm/drm_device.h>
+#include "i915_reg.h"
+
+struct i915_hwmon_reg {
+ i915_reg_t pkg_power_sku_unit;
+ i915_reg_t pkg_power_sku;
+ i915_reg_t pkg_energy_status;
+ i915_reg_t pkg_rapl_limit;
+ i915_reg_t pkg_rapl_limit_udw;
+ i915_reg_t plt_energy_status;
+ i915_reg_t reg_energy_status;
+};
+
+struct i915_hwmon {
+ struct device *dev;
+ struct mutex hwmon_lock; /* counter overflow logic and rmw */
+
+ struct i915_hwmon_reg rg;
+
+ u32 energy_counter_overflow;
+ u32 energy_counter_prev;
+ u32 power_max_initial_value;
+
+ int scl_shift_power;
+ int scl_shift_energy;
+ int scl_shift_time;
+};
+
+int i915_hwmon_init(struct drm_device *drm_dev);
+void i915_hwmon_fini(struct drm_device *drm_dev);
+
+#endif
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index f80d656331f42..62fccf71ddad6 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -4071,6 +4071,59 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
#define BXT_RP_STATE_CAP _MMIO(0x138170)
#define GEN9_RP_STATE_LIMITS _MMIO(0x138148)
+/* DG1 */
+
+/* based on MCHBAR_MIRROR_BASE_SNB == 0x140000 */
+#define PCU_PACKAGE_POWER_SKU_UNIT _MMIO(MCHBAR_MIRROR_BASE_SNB + 0x5938)
+#define PCU_PACKAGE_ENERGY_STATUS _MMIO(MCHBAR_MIRROR_BASE_SNB + 0x593c)
+#define PCU_PACKAGE_RAPL_LIMIT _MMIO(MCHBAR_MIRROR_BASE_SNB + 0x59a0)
+#define PCU_PACKAGE_RAPL_LIMIT_UDW _MMIO(MCHBAR_MIRROR_BASE_SNB + 0x59a4)
+#define PCU_PACKAGE_POWER_SKU INVALID_MMIO_REG
+#define PCU_PLATFORM_ENERGY_STATUS INVALID_MMIO_REG
+
+/* Fields for *_PACKAGE_RAPL_LIMIT: */
+#define PKG_PWR_LIM_1 REG_GENMASK(14, 0)
+#define PKG_PWR_LIM_1_EN REG_BIT(15)
+#define PKG_PWR_LIM_1_TIME REG_GENMASK(23, 17)
+
+/*
+ * Fields for *_PACKAGE_RAPL_LIMIT_UDW:
+ * In docs, these fields may be defined relative to the entire 64-bit
+ * register, but here they are defined relative to the 32-bit boundary.
+ */
+#define PKG_PWR_LIM_2 REG_GENMASK(14, 0) // 46:32
+#define PKG_PWR_LIM_2_EN REG_BIT(15) // 47:47
+#define PKG_PWR_LIM_2_TIME REG_GENMASK(23, 17) // 55:49
+
+/*
+ * *_PACKAGE_POWER_SKU_UNIT - fields specifying scaling for PCU quantities.
+ * - PKG_PWR_UNIT - Power Units used for power control registers. The
+ * actual unit value is calculated by 1 W / Power(2,PKG_PWR_UNIT).
+ * - PKG_ENERGY_UNIT - Energy Units used for power control registers. The
+ * actual unit value is calculated by 1 J / Power(2,PKG_ENERGY_UNIT).
+ * - PKG_TIME_UNIT - Time Units used for power control registers. The
+ * actual unit value is calculated by 1 s / Power(2,PKG_TIME_UNIT).
+ */
+#define PKG_PWR_UNIT REG_GENMASK(3, 0)
+#define PKG_ENERGY_UNIT REG_GENMASK(12, 8)
+#define PKG_TIME_UNIT REG_GENMASK(19, 16)
+
+/*
+ * *_PACKAGE_POWER_SKU - SKU power and timing parameters.
+ * Used herein as a 64-bit bit register.
+ * These masks are defined using GENMASK_ULL as REG_GENMASK is limited to u32
+ * and as GENMASK is "long" and therefore 32-bits on a 32-bit system.
+ * PKG_PKG_TDP, PKG_MIN_PWR, and PKG_MAX_PWR are scaled in the same way as
+ * PKG_PWR_LIM_*, above.
+ * PKG_MAX_WIN has sub-fields for x and y, and has the value: is 1.x * 2^y.
+ */
+#define PKG_PKG_TDP GENMASK_ULL(14, 0)
+#define PKG_MIN_PWR GENMASK_ULL(30, 16)
+#define PKG_MAX_PWR GENMASK_ULL(46, 32)
+#define PKG_MAX_WIN GENMASK_ULL(54, 48)
+#define PKG_MAX_WIN_Y GENMASK_ULL(54, 53)
+#define PKG_MAX_WIN_X GENMASK_ULL(52, 48)
+
/*
* Logical Context regs
*/
--
2.31.1
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [Intel-gfx] ✗ Fi.CI.CHECKPATCH: warning for drm/i915/dg1: Add HWMON power sensor support (rev2)
2021-04-13 21:22 [Intel-gfx] [PATCH v1 0/1] drm/i915/dg1: Add HWMON power sensor support Dale B Stimson
2021-04-13 21:22 ` [Intel-gfx] [PATCH v2 1/1] " Dale B Stimson
@ 2021-04-13 22:02 ` Patchwork
2021-04-13 22:07 ` [Intel-gfx] ✗ Fi.CI.DOCS: " Patchwork
2021-04-13 22:33 ` [Intel-gfx] ✗ Fi.CI.BAT: failure " Patchwork
3 siblings, 0 replies; 8+ messages in thread
From: Patchwork @ 2021-04-13 22:02 UTC (permalink / raw)
To: Dale B Stimson; +Cc: intel-gfx
== Series Details ==
Series: drm/i915/dg1: Add HWMON power sensor support (rev2)
URL : https://patchwork.freedesktop.org/series/88459/
State : warning
== Summary ==
$ dim checkpatch origin/drm-tip
fd48aa4fd9a8 drm/i915/dg1: Add HWMON power sensor support
-:104: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#104:
new file mode 100644
total: 0 errors, 1 warnings, 0 checks, 947 lines checked
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply [flat|nested] 8+ messages in thread
* [Intel-gfx] ✗ Fi.CI.DOCS: warning for drm/i915/dg1: Add HWMON power sensor support (rev2)
2021-04-13 21:22 [Intel-gfx] [PATCH v1 0/1] drm/i915/dg1: Add HWMON power sensor support Dale B Stimson
2021-04-13 21:22 ` [Intel-gfx] [PATCH v2 1/1] " Dale B Stimson
2021-04-13 22:02 ` [Intel-gfx] ✗ Fi.CI.CHECKPATCH: warning for drm/i915/dg1: Add HWMON power sensor support (rev2) Patchwork
@ 2021-04-13 22:07 ` Patchwork
2021-04-13 22:33 ` [Intel-gfx] ✗ Fi.CI.BAT: failure " Patchwork
3 siblings, 0 replies; 8+ messages in thread
From: Patchwork @ 2021-04-13 22:07 UTC (permalink / raw)
To: Dale B Stimson; +Cc: intel-gfx
== Series Details ==
Series: drm/i915/dg1: Add HWMON power sensor support (rev2)
URL : https://patchwork.freedesktop.org/series/88459/
State : warning
== Summary ==
$ make htmldocs 2>&1 > /dev/null | grep i915
./drivers/gpu/drm/i915/gem/i915_gem_shrinker.c:102: warning: Function parameter or member 'ww' not described in 'i915_gem_shrink'
./drivers/gpu/drm/i915/i915_cmd_parser.c:1420: warning: Excess function parameter 'trampoline' description in 'intel_engine_cmd_parser'
./drivers/gpu/drm/i915/i915_cmd_parser.c:1420: warning: Function parameter or member 'jump_whitelist' not described in 'intel_engine_cmd_parser'
./drivers/gpu/drm/i915/i915_cmd_parser.c:1420: warning: Function parameter or member 'shadow_map' not described in 'intel_engine_cmd_parser'
./drivers/gpu/drm/i915/i915_cmd_parser.c:1420: warning: Function parameter or member 'batch_map' not described in 'intel_engine_cmd_parser'
./drivers/gpu/drm/i915/i915_cmd_parser.c:1420: warning: Excess function parameter 'trampoline' description in 'intel_engine_cmd_parser'
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply [flat|nested] 8+ messages in thread
* [Intel-gfx] ✗ Fi.CI.BAT: failure for drm/i915/dg1: Add HWMON power sensor support (rev2)
2021-04-13 21:22 [Intel-gfx] [PATCH v1 0/1] drm/i915/dg1: Add HWMON power sensor support Dale B Stimson
` (2 preceding siblings ...)
2021-04-13 22:07 ` [Intel-gfx] ✗ Fi.CI.DOCS: " Patchwork
@ 2021-04-13 22:33 ` Patchwork
3 siblings, 0 replies; 8+ messages in thread
From: Patchwork @ 2021-04-13 22:33 UTC (permalink / raw)
To: Dale B Stimson; +Cc: intel-gfx
[-- Attachment #1.1: Type: text/plain, Size: 3432 bytes --]
== Series Details ==
Series: drm/i915/dg1: Add HWMON power sensor support (rev2)
URL : https://patchwork.freedesktop.org/series/88459/
State : failure
== Summary ==
CI Bug Log - changes from CI_DRM_9964 -> Patchwork_19927
====================================================
Summary
-------
**FAILURE**
Serious unknown changes coming with Patchwork_19927 absolutely need to be
verified manually.
If you think the reported changes have nothing to do with the changes
introduced in Patchwork_19927, please notify your bug team to allow them
to document this new failure mode, which will reduce false positives in CI.
External URL: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_19927/index.html
Possible new issues
-------------------
Here are the unknown changes that may have been introduced in Patchwork_19927:
### IGT changes ###
#### Possible regressions ####
* igt@core_hotunplug@unbind-rebind:
- fi-bsw-nick: [PASS][1] -> [INCOMPLETE][2]
[1]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_9964/fi-bsw-nick/igt@core_hotunplug@unbind-rebind.html
[2]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_19927/fi-bsw-nick/igt@core_hotunplug@unbind-rebind.html
Known issues
------------
Here are the changes found in Patchwork_19927 that come from known issues:
### IGT changes ###
#### Issues hit ####
* igt@amdgpu/amd_basic@semaphore:
- fi-bdw-5557u: NOTRUN -> [SKIP][3] ([fdo#109271]) +27 similar issues
[3]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_19927/fi-bdw-5557u/igt@amdgpu/amd_basic@semaphore.html
* igt@core_hotunplug@unbind-rebind:
- fi-bdw-5557u: NOTRUN -> [WARN][4] ([i915#2283])
[4]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_19927/fi-bdw-5557u/igt@core_hotunplug@unbind-rebind.html
* igt@gem_exec_suspend@basic-s3:
- fi-tgl-u2: [PASS][5] -> [FAIL][6] ([i915#1888])
[5]: https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_9964/fi-tgl-u2/igt@gem_exec_suspend@basic-s3.html
[6]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_19927/fi-tgl-u2/igt@gem_exec_suspend@basic-s3.html
* igt@kms_chamelium@dp-crc-fast:
- fi-bdw-5557u: NOTRUN -> [SKIP][7] ([fdo#109271] / [fdo#111827]) +8 similar issues
[7]: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_19927/fi-bdw-5557u/igt@kms_chamelium@dp-crc-fast.html
[fdo#109271]: https://bugs.freedesktop.org/show_bug.cgi?id=109271
[fdo#111827]: https://bugs.freedesktop.org/show_bug.cgi?id=111827
[i915#1888]: https://gitlab.freedesktop.org/drm/intel/issues/1888
[i915#2283]: https://gitlab.freedesktop.org/drm/intel/issues/2283
Participating hosts (47 -> 39)
------------------------------
Missing (8): fi-ilk-m540 fi-hsw-4200u fi-bsw-n3050 fi-skl-guc fi-bsw-cyan fi-ctg-p8600 fi-bsw-kefka fi-bdw-samus
Build changes
-------------
* Linux: CI_DRM_9964 -> Patchwork_19927
CI-20190529: 20190529
CI_DRM_9964: be153d28e33003a54242e87c0902a65378b7c562 @ git://anongit.freedesktop.org/gfx-ci/linux
IGT_6064: 48d89e2c65c54883b0776930a884e6d3bcefb45b @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools
Patchwork_19927: fd48aa4fd9a8b56648e1518e7303a8cd0338ebf2 @ git://anongit.freedesktop.org/gfx-ci/linux
== Linux commits ==
fd48aa4fd9a8 drm/i915/dg1: Add HWMON power sensor support
== Logs ==
For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_19927/index.html
[-- Attachment #1.2: Type: text/html, Size: 4220 bytes --]
[-- Attachment #2: Type: text/plain, Size: 160 bytes --]
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Intel-gfx] [PATCH v2 1/1] drm/i915/dg1: Add HWMON power sensor support
2021-04-13 21:22 ` [Intel-gfx] [PATCH v2 1/1] " Dale B Stimson
@ 2021-04-21 15:03 ` Jani Nikula
2021-05-14 23:40 ` Dale B Stimson
0 siblings, 1 reply; 8+ messages in thread
From: Jani Nikula @ 2021-04-21 15:03 UTC (permalink / raw)
To: Dale B Stimson, intel-gfx
On Tue, 13 Apr 2021, Dale B Stimson <dale.b.stimson@intel.com> wrote:
> As part of the System Managemenent Interface (SMI), use the HWMON
> subsystem to display power utilization.
>
> The following standard HWMON power sensors are currently supported
> (and appropriately scaled):
> /sys/class/drm/card0/device/hwmon/hwmon<i>
> - energy1_input
> - power1_cap
> - power1_max
>
> Some non-standard HWMON power information is also provided, such as
> enable bits and intervals.
>
> Signed-off-by: Dale B Stimson <dale.b.stimson@intel.com>
> ---
> drivers/gpu/drm/i915/Kconfig | 1 +
> drivers/gpu/drm/i915/Makefile | 1 +
> drivers/gpu/drm/i915/i915_drv.c | 9 +
> drivers/gpu/drm/i915/i915_drv.h | 3 +
> drivers/gpu/drm/i915/i915_hwmon.c | 788 ++++++++++++++++++++++++++++++
> drivers/gpu/drm/i915/i915_hwmon.h | 41 ++
> drivers/gpu/drm/i915/i915_reg.h | 53 ++
> 7 files changed, 896 insertions(+)
> create mode 100644 drivers/gpu/drm/i915/i915_hwmon.c
> create mode 100644 drivers/gpu/drm/i915/i915_hwmon.h
>
> diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig
> index 1e1cb245fca77..ec8d5a0d7ea96 100644
> --- a/drivers/gpu/drm/i915/Kconfig
> +++ b/drivers/gpu/drm/i915/Kconfig
> @@ -14,6 +14,7 @@ config DRM_I915
> select DRM_MIPI_DSI
> select RELAY
> select IRQ_WORK
> + select HWMON
> # i915 depends on ACPI_VIDEO when ACPI is enabled
> # but for select to work, need to select ACPI_VIDEO's dependencies, ick
> select BACKLIGHT_CLASS_DEVICE if ACPI
> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
> index d0d936d9137bc..e213e2b129e20 100644
> --- a/drivers/gpu/drm/i915/Makefile
> +++ b/drivers/gpu/drm/i915/Makefile
> @@ -37,6 +37,7 @@ i915-y += i915_drv.o \
> i915_config.o \
> i915_irq.o \
> i915_getparam.o \
> + i915_hwmon.o \
> i915_mitigations.o \
> i915_params.o \
> i915_pci.o \
> diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
> index 305557e1942aa..84c7de3b34c7d 100644
> --- a/drivers/gpu/drm/i915/i915_drv.c
> +++ b/drivers/gpu/drm/i915/i915_drv.c
> @@ -69,6 +69,7 @@
>
> #include "i915_debugfs.h"
> #include "i915_drv.h"
> +#include "i915_hwmon.h"
> #include "i915_ioc32.h"
> #include "i915_irq.h"
> #include "i915_memcpy.h"
> @@ -675,6 +676,10 @@ static void i915_driver_register(struct drm_i915_private *dev_priv)
> i915_debugfs_register(dev_priv);
> i915_setup_sysfs(dev_priv);
>
> + /* Register with hwmon */
> + if (i915_hwmon_init(&dev_priv->drm))
Please pass in i915, not struct drm_device.
This is i915_driver_register. Almost all functions being have _register
in them. Why not this one?
> + drm_err(&dev_priv->drm, "Failed to register driver hwmon!\n");
Not sure we want this error message at this level.
> +
> /* Depends on sysfs having been initialized */
> i915_perf_register(dev_priv);
>
> @@ -709,9 +714,13 @@ static void i915_driver_unregister(struct drm_i915_private *dev_priv)
> intel_gt_driver_unregister(&dev_priv->gt);
>
> i915_perf_unregister(dev_priv);
> +
> + i915_hwmon_fini(&dev_priv->drm);
> +
Naming, again _unregister in most places.
> i915_pmu_unregister(dev_priv);
>
> i915_teardown_sysfs(dev_priv);
> +
Stray newline.
> drm_dev_unplug(&dev_priv->drm);
>
> i915_gem_driver_unregister(dev_priv);
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index 69e43bf91a153..7e9b452c77e2b 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -61,6 +61,7 @@
> #include <drm/drm_connector.h>
> #include <drm/i915_mei_hdcp_interface.h>
>
> +#include "i915_hwmon.h"
> #include "i915_params.h"
> #include "i915_reg.h"
> #include "i915_utils.h"
> @@ -1109,6 +1110,8 @@ struct drm_i915_private {
>
> struct i915_perf perf;
>
> + struct i915_hwmon hwmon;
> +
> /* Abstract the submission mechanism (legacy ringbuffer or execlists) away */
> struct intel_gt gt;
>
> diff --git a/drivers/gpu/drm/i915/i915_hwmon.c b/drivers/gpu/drm/i915/i915_hwmon.c
> new file mode 100644
> index 0000000000000..ab8f32f7ed1de
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/i915_hwmon.c
> @@ -0,0 +1,788 @@
> +// SPDX-License-Identifier: MIT
> +
Superfluous newline.
> +/*
> + * Copyright © 2020 Intel Corporation
> + */
> +
> +/*
> + * Power-related hwmon entries.
> + */
> +
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/types.h>
> +
> +#include "i915_drv.h"
> +#include "gt/intel_gt.h"
> +#include "i915_hwmon.h"
> +
> +/*
> + * SF_* - scale factors for particular quantities.
> + * The hwmon standard says that quantities of the given types are specified
> + * in the given units:
> + * - time - milliseconds
> + * - power - microwatts
> + * - energy - microjoules
> + */
> +
> +#define SF_TIME 1000
> +#define SF_POWER 1000000
> +#define SF_ENERGY 1000000
> +
> +static void
> +_locked_with_pm_intel_uncore_rmw(struct intel_uncore *uncore,
> + i915_reg_t reg, u32 clear, u32 set)
> +{
> + struct drm_i915_private *i915 = uncore->i915;
> + struct i915_hwmon *hwmon = &i915->hwmon;
> + intel_wakeref_t wakeref;
> +
> + mutex_lock(&hwmon->hwmon_lock);
> +
> + with_intel_runtime_pm(uncore->rpm, wakeref)
> + intel_uncore_rmw(uncore, reg, clear, set);
> +
> + mutex_unlock(&hwmon->hwmon_lock);
> +}
> +
> +/*
> + * _field_read_and_scale()
Unnecessary if this isn't kernel-doc, and this need not be kernel-doc.
> + * Return type of u64 allows for the case where the scaling might cause a
> + * result exceeding 32 bits.
> + */
> +static __always_inline u64
Why __always_inline? Why not let the compiler decide what makes sense?
> +_field_read_and_scale(struct intel_uncore *uncore, i915_reg_t rgadr,
> + u32 field_msk, int nshift, unsigned int scale_factor)
> +{
> + intel_wakeref_t wakeref;
> + u32 reg_value;
> + u64 scaled_val;
> +
> + with_intel_runtime_pm(uncore->rpm, wakeref)
> + reg_value = intel_uncore_read(uncore, rgadr);
> +
> + reg_value = le32_get_bits(cpu_to_le32(reg_value), field_msk);
> + scaled_val = mul_u32_u32(scale_factor, reg_value);
> +
> + /* Shift, rounding to nearest */
> + if (nshift > 0)
> + scaled_val = (scaled_val + (1 << (nshift - 1))) >> nshift;
> +
> + return scaled_val;
> +}
> +
> +/*
> + * _field_read64_and_scale() - read a 64-bit register and scale.
Ditto for kernel-doc style.
> + */
> +static __always_inline u64
Ditto for __always_inline.
> +_field_read64_and_scale(struct intel_uncore *uncore, i915_reg_t rgadr,
> + u64 field_msk, int nshift, unsigned int scale_factor)
> +{
> + intel_wakeref_t wakeref;
> + u64 reg_value;
> + u64 scaled_val;
> +
> + with_intel_runtime_pm(uncore->rpm, wakeref)
> + reg_value = intel_uncore_read64(uncore, rgadr);
> +
> + reg_value = le64_get_bits(cpu_to_le64(reg_value), field_msk);
> + scaled_val = scale_factor * reg_value;
> +
> + /* Shift, rounding to nearest */
> + if (nshift > 0)
> + scaled_val = (scaled_val + (1 << (nshift - 1))) >> nshift;
> +
> + return scaled_val;
> +}
> +
> +/*
> + * _field_scale_and_write()
> + */
> +static __always_inline void
> +_field_scale_and_write(struct intel_uncore *uncore,
> + i915_reg_t rgadr,
> + u32 field_msk, int nshift,
> + unsigned int scale_factor, long lval)
> +{
> + u32 nval;
> + u32 bits_to_clear;
> + u32 bits_to_set;
> +
> + /* Computation in 64-bits to avoid overflow. Round to nearest. */
> + nval = DIV_ROUND_CLOSEST_ULL((u64)lval << nshift, scale_factor);
> +
> + bits_to_clear = field_msk;
> + bits_to_set = le32_to_cpu(le32_encode_bits(nval, field_msk));
> +
> + _locked_with_pm_intel_uncore_rmw(uncore, rgadr,
> + bits_to_clear, bits_to_set);
> +}
> +
> +/*
> + * i915_energy1_input_show - A custom function to obtain energy1_input.
> + * Use a custom function instead of the usual hwmon helpers in order to
> + * guarantee 64-bits of result to user-space.
> + * Units are microjoules.
> + *
> + * The underlying hardware register is 32-bits and is subject to overflow.
> + * This function compensates for overflow of the 32-bit register by detecting
> + * wrap-around and incrementing an overflow counter.
> + * This only works if the register is sampled often enough to avoid
> + * missing an instance of overflow - achieved either by repeated
> + * queries through the API, or via a possible timer (future - TBD) that
> + * ensures values are read often enough to catch all overflows.
> + *
> + * How long before overflow? For example, with an example scaling bit
> + * shift of 14 bits (see register *PACKAGE_POWER_SKU_UNIT) and a power draw of
> + * 1000 watts, the 32-bit counter will overflow in approximately 4.36 minutes.
> + *
> + * Examples:
> + * 1 watt: (2^32 >> 14) / 1 W / (60 * 60 * 24) secs/day -> 3 days
> + * 1000 watts: (2^32 >> 14) / 1000 W / 60 secs/min -> 4.36 minutes
> + */
> +static ssize_t
> +i915_energy1_input_show(struct device *dev, struct device_attribute *attr,
> + char *buf)
> +{
> + struct drm_i915_private *i915 = dev_get_drvdata(dev);
> + struct intel_uncore *uncore = &i915->uncore;
> + struct i915_hwmon *hwmon = &i915->hwmon;
> + int nshift = hwmon->scl_shift_energy;
> + ssize_t ret;
> + intel_wakeref_t wakeref;
> + u32 reg_value;
> + u64 vlo;
> + u64 vhi;
> +
> + mutex_lock(&hwmon->hwmon_lock);
> +
> + with_intel_runtime_pm(uncore->rpm, wakeref)
> + reg_value = intel_uncore_read(uncore,
> + hwmon->rg.reg_energy_status);
> +
> + /*
> + * The u32 register concatenated with the u32 overflow counter
> + * gives an effective energy counter size of 64-bits. However, the
> + * computations below are done modulo 2^96 to avoid overflow during
> + * scaling in the conversion to microjoules.
> + *
> + * The low-order 64-bits of the resulting quantity are returned to
> + * the caller in units of microjoules, encoded into a decimal string.
> + *
> + * For a power of 1000 watts, 64 bits in units of microjoules will
> + * overflow after 584 years.
> + */
> +
> + if (hwmon->energy_counter_prev > reg_value)
> + hwmon->energy_counter_overflow++;
> +
> + hwmon->energy_counter_prev = reg_value;
> +
> + /*
> + * 64-bit variables vlo and vhi are used for the scaling process.
> + * The 96-bit counter value is composed from the two 64-bit variables
> + * vhi and vlo thusly: counter == vhi << 32 + vlo .
> + * The 32-bits of overlap between the two variables is convenient for
> + * handling overflows out of vlo.
> + */
> +
> + vlo = reg_value;
> + vhi = hwmon->energy_counter_overflow;
> +
> + mutex_unlock(&hwmon->hwmon_lock);
> +
> + vlo = SF_ENERGY * vlo;
> +
> + /* Prepare to round to nearest */
> + if (nshift > 0)
> + vlo += 1 << (nshift - 1);
> +
> + /*
> + * Anything in the upper-32 bits of vlo gets added into vhi here,
> + * and then cleared from vlo.
> + */
> + vhi = (SF_ENERGY * vhi) + (vlo >> 32);
> + vlo &= 0xffffffffULL;
> +
> + /*
> + * Apply the right shift.
> + * - vlo shifted by itself.
> + * - vlo receiving what's shifted out of vhi.
> + * - vhi shifted by itself
> + */
> + vlo = vlo >> nshift;
> + vlo |= (vhi << (32 - nshift)) & 0xffffffffULL;
> + vhi = vhi >> nshift;
> +
> + /* Combined to get a 64-bit result in vlo. */
> + vlo |= (vhi << 32);
> +
> + ret = scnprintf(buf, PAGE_SIZE, "%llu\n", vlo);
sysfs_emit() instead?
> +
> + return ret;
> +}
> +
> +static ssize_t
> +i915_power1_max_enable_show(struct device *dev, struct device_attribute *attr,
> + char *buf)
> +{
> + struct drm_i915_private *i915 = dev_get_drvdata(dev);
> + struct intel_uncore *uncore = &i915->uncore;
> + intel_wakeref_t wakeref;
> + ssize_t ret;
> + u32 reg_value;
> + bool is_enabled;
> +
> + with_intel_runtime_pm(uncore->rpm, wakeref)
> + reg_value = intel_uncore_read(uncore,
> + i915->hwmon.rg.pkg_rapl_limit);
> +
> + is_enabled = !!(reg_value & PKG_PWR_LIM_1_EN);
> +
> + ret = scnprintf(buf, PAGE_SIZE, "%u\n", is_enabled);
> +
> + return ret;
> +}
> +
> +static ssize_t
> +i915_power1_max_enable_store(struct device *dev, struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct drm_i915_private *i915 = dev_get_drvdata(dev);
> + struct intel_uncore *uncore = &i915->uncore;
> + struct i915_hwmon *hwmon = &i915->hwmon;
> + ssize_t ret;
> + u32 val;
> + u32 bits_to_clear;
> + u32 bits_to_set;
> +
> + ret = kstrtou32(buf, 0, &val);
> + if (ret)
> + return ret;
> +
> + bits_to_clear = PKG_PWR_LIM_1_EN;
> + if (!val)
> + bits_to_set = 0;
> + else
> + bits_to_set = PKG_PWR_LIM_1_EN;
> +
> + _locked_with_pm_intel_uncore_rmw(uncore, hwmon->rg.pkg_rapl_limit,
> + bits_to_clear, bits_to_set);
> +
> + return count;
> +}
> +
> +static ssize_t
> +i915_power1_max_interval_show(struct device *dev, struct device_attribute *attr,
> + char *buf)
> +{
> + struct drm_i915_private *i915 = dev_get_drvdata(dev);
> + struct intel_uncore *uncore = &i915->uncore;
> + struct i915_hwmon *hwmon = &i915->hwmon;
> + ssize_t ret;
> + u64 ullval;
> +
> + ullval = _field_read_and_scale(uncore, hwmon->rg.pkg_rapl_limit,
> + PKG_PWR_LIM_1_TIME,
> + hwmon->scl_shift_time, SF_TIME);
> +
> + ret = scnprintf(buf, PAGE_SIZE, "%llu\n", ullval);
> +
> + return ret;
> +}
> +
> +static ssize_t
> +i915_power1_max_interval_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct drm_i915_private *i915 = dev_get_drvdata(dev);
> + struct intel_uncore *uncore = &i915->uncore;
> + struct i915_hwmon *hwmon = &i915->hwmon;
> + ssize_t ret;
> + long val;
> +
> + ret = kstrtoul(buf, 0, &val);
> + if (ret)
> + return ret;
> +
> + _field_scale_and_write(uncore, hwmon->rg.pkg_rapl_limit,
> + PKG_PWR_LIM_2_TIME,
> + hwmon->scl_shift_time, SF_TIME, val);
> +
> + return count;
> +}
> +
> +static ssize_t
> +i915_power1_cap_enable_show(struct device *dev, struct device_attribute *attr,
> + char *buf)
> +{
> + struct drm_i915_private *i915 = dev_get_drvdata(dev);
> + struct intel_uncore *uncore = &i915->uncore;
> + struct i915_hwmon *hwmon = &i915->hwmon;
> + intel_wakeref_t wakeref;
> + ssize_t ret;
> + u32 reg_value;
> + bool is_enabled;
> +
> + with_intel_runtime_pm(uncore->rpm, wakeref)
> + reg_value = intel_uncore_read(uncore,
> + hwmon->rg.pkg_rapl_limit_udw);
> +
> + is_enabled = !!(reg_value & PKG_PWR_LIM_2_EN);
> +
> + ret = scnprintf(buf, PAGE_SIZE, "%u\n", is_enabled);
> +
> + return ret;
> +}
> +
> +static ssize_t
> +i915_power1_cap_enable_store(struct device *dev, struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct drm_i915_private *i915 = dev_get_drvdata(dev);
> + struct intel_uncore *uncore = &i915->uncore;
> + struct i915_hwmon *hwmon = &i915->hwmon;
> + ssize_t ret;
> + u32 val;
> + u32 bits_to_clear;
> + u32 bits_to_set;
> +
> + ret = kstrtou32(buf, 0, &val);
> + if (ret)
> + return ret;
> +
> + bits_to_clear = PKG_PWR_LIM_2_EN;
> + if (!val)
> + bits_to_set = 0;
> + else
> + bits_to_set = PKG_PWR_LIM_2_EN;
> +
> + _locked_with_pm_intel_uncore_rmw(uncore, hwmon->rg.pkg_rapl_limit_udw,
> + bits_to_clear, bits_to_set);
> +
> + return count;
> +}
> +
> +static ssize_t
> +i915_power_default_limit_show(struct device *dev, struct device_attribute *attr,
> + char *buf)
> +{
> + struct drm_i915_private *i915 = dev_get_drvdata(dev);
> + struct i915_hwmon *hwmon = &i915->hwmon;
> + ssize_t ret;
> +
> + ret = scnprintf(buf, PAGE_SIZE, "%u\n", hwmon->power_max_initial_value);
> +
> + return ret;
> +}
> +
> +static ssize_t
> +i915_power_min_limit_show(struct device *dev, struct device_attribute *attr,
> + char *buf)
> +{
> + struct drm_i915_private *i915 = dev_get_drvdata(dev);
> + struct intel_uncore *uncore = &i915->uncore;
> + struct i915_hwmon *hwmon = &i915->hwmon;
> + ssize_t ret;
> + u32 uval;
> +
> + /*
> + * This is a 64-bit register but the individual fields are under 32 bits
> + * in size even after scaling.
> + * The UAPI specifies a size of 32 bits.
> + * The UAPI specifies that 0 should be returned if unsupported.
> + * So, using u32 and %u is sufficient.
> + */
> + if (i915_mmio_reg_valid(hwmon->rg.pkg_power_sku))
> + uval = (u32)_field_read64_and_scale(uncore,
> + hwmon->rg.pkg_power_sku,
> + PKG_MIN_PWR,
> + hwmon->scl_shift_power,
> + SF_POWER);
> + else
> + uval = 0;
> +
> + ret = scnprintf(buf, PAGE_SIZE, "%u\n", uval);
> +
> + return ret;
> +}
> +
> +static ssize_t
> +i915_power_max_limit_show(struct device *dev, struct device_attribute *attr,
> + char *buf)
> +{
> + struct drm_i915_private *i915 = dev_get_drvdata(dev);
> + struct intel_uncore *uncore = &i915->uncore;
> + struct i915_hwmon *hwmon = &i915->hwmon;
> + ssize_t ret;
> + u32 uval;
> +
> + /*
> + * This is a 64-bit register but the individual fields are under 32 bits
> + * in size even after scaling.
> + * The UAPI specifies a size of 32 bits.
> + * The UAPI specifies that UINT_MAX should be returned if unsupported.
> + * So, using u32 and %u is sufficient.
> + */
> + if (i915_mmio_reg_valid(hwmon->rg.pkg_power_sku))
> + uval = (u32)_field_read64_and_scale(uncore,
> + hwmon->rg.pkg_power_sku,
> + PKG_MAX_PWR,
> + hwmon->scl_shift_power,
> + SF_POWER);
> + else
> + uval = UINT_MAX;
> +
> + ret = scnprintf(buf, PAGE_SIZE, "%u\n", uval);
> +
> + return ret;
> +}
> +
> +static SENSOR_DEVICE_ATTR(power1_max_enable, 0664,
> + i915_power1_max_enable_show,
> + i915_power1_max_enable_store, 0);
> +static SENSOR_DEVICE_ATTR(power1_max_interval, 0664,
> + i915_power1_max_interval_show,
> + i915_power1_max_interval_store, 0);
> +static SENSOR_DEVICE_ATTR(power1_cap_enable, 0664,
> + i915_power1_cap_enable_show,
> + i915_power1_cap_enable_store, 0);
> +static SENSOR_DEVICE_ATTR(power_default_limit, 0444,
> + i915_power_default_limit_show, NULL, 0);
> +static SENSOR_DEVICE_ATTR(power_min_limit, 0444,
> + i915_power_min_limit_show, NULL, 0);
> +static SENSOR_DEVICE_ATTR(power_max_limit, 0444,
> + i915_power_max_limit_show, NULL, 0);
> +static SENSOR_DEVICE_ATTR(energy1_input, 0444,
> + i915_energy1_input_show, NULL, 0);
> +
> +static struct attribute *hwmon_attributes[] = {
> + &sensor_dev_attr_power1_max_enable.dev_attr.attr,
> + &sensor_dev_attr_power1_max_interval.dev_attr.attr,
> + &sensor_dev_attr_power1_cap_enable.dev_attr.attr,
> + &sensor_dev_attr_power_default_limit.dev_attr.attr,
> + &sensor_dev_attr_power_min_limit.dev_attr.attr,
> + &sensor_dev_attr_power_max_limit.dev_attr.attr,
> + &sensor_dev_attr_energy1_input.dev_attr.attr,
> + NULL
> +};
> +
> +static umode_t hwmon_attributes_visible(struct kobject *kobj,
> + struct attribute *attr, int index)
> +{
> + struct device *dev = kobj_to_dev(kobj);
> + struct drm_i915_private *i915 = dev_get_drvdata(dev);
> + struct i915_hwmon *hwmon = &i915->hwmon;
> + i915_reg_t rgadr;
> +
> + if (attr == &sensor_dev_attr_energy1_input.dev_attr.attr)
> + rgadr = hwmon->rg.reg_energy_status;
> + else if (attr == &sensor_dev_attr_power1_max_enable.dev_attr.attr)
> + rgadr = hwmon->rg.pkg_rapl_limit;
> + else if (attr == &sensor_dev_attr_power1_max_interval.dev_attr.attr)
> + rgadr = hwmon->rg.pkg_rapl_limit;
> + else if (attr == &sensor_dev_attr_power1_cap_enable.dev_attr.attr)
> + rgadr = hwmon->rg.pkg_rapl_limit_udw;
> + else if (attr == &sensor_dev_attr_power_default_limit.dev_attr.attr)
> + rgadr = hwmon->rg.pkg_rapl_limit;
> + else if (attr == &sensor_dev_attr_power_min_limit.dev_attr.attr)
> + return attr->mode;
> + else if (attr == &sensor_dev_attr_power_max_limit.dev_attr.attr)
> + return attr->mode;
> + else
> + return 0;
> +
> + if (!i915_mmio_reg_valid(rgadr))
> + return 0;
> +
> + return attr->mode;
> +}
> +
> +static const struct attribute_group hwmon_attrgroup = {
> + .attrs = hwmon_attributes,
> + .is_visible = hwmon_attributes_visible,
> +};
> +
> +static const struct attribute_group *hwmon_groups[] = {
> + &hwmon_attrgroup,
> + NULL
> +};
> +
> +/*
> + * HWMON SENSOR TYPE = hwmon_power
> + * - Sustained Power (power1_max)
> + * - Burst power (power1_cap)
> + * - Peak power (power1_crit)
> + */
> +static const u32 i915_config_power[] = {
> + HWMON_P_CAP | HWMON_P_MAX,
> + 0
> +};
> +
> +static const struct hwmon_channel_info i915_power = {
> + .type = hwmon_power,
> + .config = i915_config_power,
> +};
> +
> +static const struct hwmon_channel_info *i915_info[] = {
> + &i915_power,
> + NULL
> +};
> +
> +static umode_t
> +i915_power_is_visible(const struct drm_i915_private *i915, u32 attr, int chan)
> +{
> + i915_reg_t rgadr;
> +
> + switch (attr) {
> + case hwmon_power_max:
> + rgadr = i915->hwmon.rg.pkg_rapl_limit;
> + break;
> + case hwmon_power_cap:
> + rgadr = i915->hwmon.rg.pkg_rapl_limit_udw;
> + break;
> + default:
> + return 0;
> + }
> +
> + if (!i915_mmio_reg_valid(rgadr))
> + return 0;
> +
> + return 0664;
> +}
> +
> +static int
> +i915_power_read(struct drm_i915_private *i915, u32 attr, int chan, long *val)
> +{
> + struct intel_uncore *uncore = &i915->uncore;
> + struct i915_hwmon *hwmon = &i915->hwmon;
> + int ret = 0;
> +
> + switch (attr) {
> + case hwmon_power_max:
> + *val = (long)_field_read_and_scale(uncore,
> + hwmon->rg.pkg_rapl_limit,
> + PKG_PWR_LIM_1,
> + hwmon->scl_shift_power,
> + SF_POWER);
> + break;
> + case hwmon_power_cap:
> + *val = (long)_field_read_and_scale(uncore,
> + hwmon->rg.pkg_rapl_limit_udw,
> + PKG_PWR_LIM_2,
> + hwmon->scl_shift_power,
> + SF_POWER);
> + break;
> + default:
> + ret = -EOPNOTSUPP;
> + }
> +
> + return ret;
> +}
> +
> +static int
> +i915_power_write(struct drm_i915_private *i915, u32 attr, int chan, long val)
> +{
> + struct intel_uncore *uncore = &i915->uncore;
> + struct i915_hwmon *hwmon = &i915->hwmon;
> + int ret = 0;
> +
> + switch (attr) {
> + case hwmon_power_max:
> + _field_scale_and_write(uncore,
> + hwmon->rg.pkg_rapl_limit,
> + PKG_PWR_LIM_1,
> + hwmon->scl_shift_power,
> + SF_POWER, val);
> + break;
> + case hwmon_power_cap:
> + _field_scale_and_write(uncore,
> + hwmon->rg.pkg_rapl_limit_udw,
> + PKG_PWR_LIM_2,
> + hwmon->scl_shift_power,
> + SF_POWER, val);
> + break;
> + default:
> + ret = -EOPNOTSUPP;
> + }
> +
> + return ret;
> +}
> +
> +static umode_t
> +i915_is_visible(const void *data, enum hwmon_sensor_types type,
> + u32 attr, int channel)
> +{
> + struct drm_i915_private *i915 = (struct drm_i915_private *)data;
> +
> + switch (type) {
> + case hwmon_power:
> + return i915_power_is_visible(i915, attr, channel);
> + default:
> + return 0;
> + }
> +}
> +
> +static int
> +i915_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
> + int channel, long *val)
> +{
> + struct drm_i915_private *i915 = kdev_to_i915(dev);
> +
> + switch (type) {
> + case hwmon_power:
> + return i915_power_read(i915, attr, channel, val);
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +static int
> +i915_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
> + int channel, long val)
> +{
> + struct drm_i915_private *i915 = kdev_to_i915(dev);
> +
> + switch (type) {
> + case hwmon_power:
> + return i915_power_write(i915, attr, channel, val);
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +static const struct hwmon_ops i915_hwmon_ops = {
> + .is_visible = i915_is_visible,
> + .read = i915_read,
> + .write = i915_write,
> +};
> +
> +static const struct hwmon_chip_info i915_chip_info = {
> + .ops = &i915_hwmon_ops,
> + .info = i915_info,
> +};
> +
> +static void
> +i915_hwmon_get_preregistration_info(struct drm_i915_private *i915)
> +{
> + struct i915_hwmon *hwmon = &i915->hwmon;
> + struct intel_uncore *uncore = &i915->uncore;
> + intel_wakeref_t wakeref;
> + u32 val_sku_unit;
> + __le32 le_sku_unit;
> +
> + if (IS_DG1(i915)) {
> + hwmon->rg.pkg_power_sku_unit = PCU_PACKAGE_POWER_SKU_UNIT;
> + hwmon->rg.pkg_power_sku = PCU_PACKAGE_POWER_SKU;
> + hwmon->rg.pkg_energy_status = PCU_PACKAGE_ENERGY_STATUS;
> + hwmon->rg.pkg_rapl_limit = PCU_PACKAGE_RAPL_LIMIT;
> + hwmon->rg.pkg_rapl_limit_udw = PCU_PACKAGE_RAPL_LIMIT_UDW;
> + hwmon->rg.plt_energy_status = PCU_PLATFORM_ENERGY_STATUS;
> + } else {
> + hwmon->rg.pkg_power_sku_unit = INVALID_MMIO_REG;
> + hwmon->rg.pkg_power_sku = INVALID_MMIO_REG;
> + hwmon->rg.pkg_energy_status = INVALID_MMIO_REG;
> + hwmon->rg.pkg_rapl_limit = INVALID_MMIO_REG;
> + hwmon->rg.pkg_rapl_limit_udw = INVALID_MMIO_REG;
> + hwmon->rg.plt_energy_status = INVALID_MMIO_REG;
> + }
> +
> + /*
> + * If a platform does not support *_PLATFORM_ENERGY_STATUS,
> + * try *PACKAGE_ENERGY_STATUS.
> + */
> + if (i915_mmio_reg_valid(hwmon->rg.plt_energy_status))
> + hwmon->rg.reg_energy_status = hwmon->rg.plt_energy_status;
> + else
> + hwmon->rg.reg_energy_status = hwmon->rg.pkg_energy_status;
> +
> + wakeref = intel_runtime_pm_get(uncore->rpm);
> +
> + /*
> + * The contents of register hwmon->rg.pkg_power_sku_unit do not change,
> + * so read it once and store the shift values.
> + */
> + if (i915_mmio_reg_valid(hwmon->rg.pkg_power_sku_unit))
> + val_sku_unit = intel_uncore_read(uncore,
> + hwmon->rg.pkg_power_sku_unit);
> + else
> + val_sku_unit = 0;
> +
> + hwmon->energy_counter_overflow = 0;
> +
> + if (i915_mmio_reg_valid(hwmon->rg.reg_energy_status))
> + hwmon->energy_counter_prev =
> + intel_uncore_read(uncore, hwmon->rg.reg_energy_status);
> + else
> + hwmon->energy_counter_prev = 0;
> +
> + intel_runtime_pm_put(uncore->rpm, wakeref);
> +
> + le_sku_unit = cpu_to_le32(val_sku_unit);
> + hwmon->scl_shift_power = le32_get_bits(le_sku_unit, PKG_PWR_UNIT);
> + hwmon->scl_shift_energy = le32_get_bits(le_sku_unit, PKG_ENERGY_UNIT);
> + hwmon->scl_shift_time = le32_get_bits(le_sku_unit, PKG_TIME_UNIT);
> +
> + /*
> + * There is no direct way to obtain the power default_limit.
> + * The best known workaround is to use the initial value of power1_max.
> + *
> + * The value of power1_max is reset to the default on reboot, but is
> + * not reset by a module unload/load sequence. To allow proper
> + * functioning after a module reload, the value for power1_max is
> + * restored to its original value at module unload time in
> + * i915_hwmon_fini().
> + */
> + hwmon->power_max_initial_value =
> + (u32)_field_read_and_scale(uncore,
> + hwmon->rg.pkg_rapl_limit,
> + PKG_PWR_LIM_1,
> + hwmon->scl_shift_power, SF_POWER);
> +}
> +
> +int i915_hwmon_init(struct drm_device *drm_dev)
> +{
> + struct drm_i915_private *i915 = to_i915(drm_dev);
> + struct i915_hwmon *hwmon = &i915->hwmon;
> + struct device *hwmon_dev;
> +
> + mutex_init(&hwmon->hwmon_lock);
> +
> + i915_hwmon_get_preregistration_info(i915);
> +
> + hwmon_dev = hwmon_device_register_with_info(drm_dev->dev, "i915",
> + drm_dev,
> + &i915_chip_info,
> + hwmon_groups);
> +
> + if (IS_ERR(hwmon_dev)) {
> + mutex_destroy(&hwmon->hwmon_lock);
> + return PTR_ERR(hwmon_dev);
> + }
> +
> + hwmon->dev = hwmon_dev;
> +
> + return 0;
> +}
> +
> +void i915_hwmon_fini(struct drm_device *drm_dev)
> +{
> + struct drm_i915_private *i915 = to_i915(drm_dev);
> + struct i915_hwmon *hwmon = &i915->hwmon;
> +
> + if (hwmon->power_max_initial_value) {
> + /* Restore power1_max. */
> + _field_scale_and_write(&i915->uncore, hwmon->rg.pkg_rapl_limit,
> + PKG_PWR_LIM_1, hwmon->scl_shift_power,
> + SF_POWER,
> + hwmon->power_max_initial_value);
> + }
> +
> + if (hwmon->dev)
> + hwmon_device_unregister(hwmon->dev);
> +
> + mutex_destroy(&hwmon->hwmon_lock);
> +
> + memset(hwmon, 0, sizeof(*hwmon));
> +}
> diff --git a/drivers/gpu/drm/i915/i915_hwmon.h b/drivers/gpu/drm/i915/i915_hwmon.h
> new file mode 100644
> index 0000000000000..0be919f0a463d
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/i915_hwmon.h
> @@ -0,0 +1,41 @@
> +/* SPDX-License-Identifier: MIT */
> +
> +/*
> + * Copyright © 2020 Intel Corporation
> + */
> +
> +#ifndef __INTEL_HWMON_H__
> +#define __INTEL_HWMON_H__
> +
> +#include <drm/drm_device.h>
A forward declaration should be enough. Need <linux/types.h> though.
> +#include "i915_reg.h"
> +
> +struct i915_hwmon_reg {
> + i915_reg_t pkg_power_sku_unit;
> + i915_reg_t pkg_power_sku;
> + i915_reg_t pkg_energy_status;
> + i915_reg_t pkg_rapl_limit;
> + i915_reg_t pkg_rapl_limit_udw;
> + i915_reg_t plt_energy_status;
> + i915_reg_t reg_energy_status;
> +};
> +
> +struct i915_hwmon {
> + struct device *dev;
> + struct mutex hwmon_lock; /* counter overflow logic and rmw */
> +
> + struct i915_hwmon_reg rg;
> +
> + u32 energy_counter_overflow;
> + u32 energy_counter_prev;
> + u32 power_max_initial_value;
> +
> + int scl_shift_power;
> + int scl_shift_energy;
> + int scl_shift_time;
> +};
> +
> +int i915_hwmon_init(struct drm_device *drm_dev);
> +void i915_hwmon_fini(struct drm_device *drm_dev);
> +
> +#endif
> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> index f80d656331f42..62fccf71ddad6 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -4071,6 +4071,59 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
> #define BXT_RP_STATE_CAP _MMIO(0x138170)
> #define GEN9_RP_STATE_LIMITS _MMIO(0x138148)
>
> +/* DG1 */
> +
> +/* based on MCHBAR_MIRROR_BASE_SNB == 0x140000 */
> +#define PCU_PACKAGE_POWER_SKU_UNIT _MMIO(MCHBAR_MIRROR_BASE_SNB + 0x5938)
> +#define PCU_PACKAGE_ENERGY_STATUS _MMIO(MCHBAR_MIRROR_BASE_SNB + 0x593c)
> +#define PCU_PACKAGE_RAPL_LIMIT _MMIO(MCHBAR_MIRROR_BASE_SNB + 0x59a0)
> +#define PCU_PACKAGE_RAPL_LIMIT_UDW _MMIO(MCHBAR_MIRROR_BASE_SNB + 0x59a4)
> +#define PCU_PACKAGE_POWER_SKU INVALID_MMIO_REG
> +#define PCU_PLATFORM_ENERGY_STATUS INVALID_MMIO_REG
> +
> +/* Fields for *_PACKAGE_RAPL_LIMIT: */
> +#define PKG_PWR_LIM_1 REG_GENMASK(14, 0)
> +#define PKG_PWR_LIM_1_EN REG_BIT(15)
> +#define PKG_PWR_LIM_1_TIME REG_GENMASK(23, 17)
> +
> +/*
> + * Fields for *_PACKAGE_RAPL_LIMIT_UDW:
> + * In docs, these fields may be defined relative to the entire 64-bit
> + * register, but here they are defined relative to the 32-bit boundary.
> + */
> +#define PKG_PWR_LIM_2 REG_GENMASK(14, 0) // 46:32
> +#define PKG_PWR_LIM_2_EN REG_BIT(15) // 47:47
> +#define PKG_PWR_LIM_2_TIME REG_GENMASK(23, 17) // 55:49
> +
> +/*
> + * *_PACKAGE_POWER_SKU_UNIT - fields specifying scaling for PCU quantities.
> + * - PKG_PWR_UNIT - Power Units used for power control registers. The
> + * actual unit value is calculated by 1 W / Power(2,PKG_PWR_UNIT).
> + * - PKG_ENERGY_UNIT - Energy Units used for power control registers. The
> + * actual unit value is calculated by 1 J / Power(2,PKG_ENERGY_UNIT).
> + * - PKG_TIME_UNIT - Time Units used for power control registers. The
> + * actual unit value is calculated by 1 s / Power(2,PKG_TIME_UNIT).
> + */
> +#define PKG_PWR_UNIT REG_GENMASK(3, 0)
> +#define PKG_ENERGY_UNIT REG_GENMASK(12, 8)
> +#define PKG_TIME_UNIT REG_GENMASK(19, 16)
> +
> +/*
> + * *_PACKAGE_POWER_SKU - SKU power and timing parameters.
> + * Used herein as a 64-bit bit register.
> + * These masks are defined using GENMASK_ULL as REG_GENMASK is limited to u32
> + * and as GENMASK is "long" and therefore 32-bits on a 32-bit system.
> + * PKG_PKG_TDP, PKG_MIN_PWR, and PKG_MAX_PWR are scaled in the same way as
> + * PKG_PWR_LIM_*, above.
> + * PKG_MAX_WIN has sub-fields for x and y, and has the value: is 1.x * 2^y.
> + */
> +#define PKG_PKG_TDP GENMASK_ULL(14, 0)
> +#define PKG_MIN_PWR GENMASK_ULL(30, 16)
> +#define PKG_MAX_PWR GENMASK_ULL(46, 32)
> +#define PKG_MAX_WIN GENMASK_ULL(54, 48)
> +#define PKG_MAX_WIN_Y GENMASK_ULL(54, 53)
> +#define PKG_MAX_WIN_X GENMASK_ULL(52, 48)
> +
> /*
> * Logical Context regs
> */
--
Jani Nikula, Intel Open Source Graphics Center
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Intel-gfx] [PATCH v2 1/1] drm/i915/dg1: Add HWMON power sensor support
2021-04-21 15:03 ` Jani Nikula
@ 2021-05-14 23:40 ` Dale B Stimson
0 siblings, 0 replies; 8+ messages in thread
From: Dale B Stimson @ 2021-05-14 23:40 UTC (permalink / raw)
To: Jani Nikula; +Cc: intel-gfx
On 2021-04-21 18:03:51, Jani Nikula wrote:
> On Tue, 13 Apr 2021, Dale B Stimson <dale.b.stimson@intel.com> wrote:
> > As part of the System Managemenent Interface (SMI), use the HWMON
> > subsystem to display power utilization.
> >
> > The following standard HWMON power sensors are currently supported
> > (and appropriately scaled):
> > /sys/class/drm/card0/device/hwmon/hwmon<i>
> > - energy1_input
> > - power1_cap
> > - power1_max
> >
> > Some non-standard HWMON power information is also provided, such as
> > enable bits and intervals.
> >
> > Signed-off-by: Dale B Stimson <dale.b.stimson@intel.com>
> > ---
> > drivers/gpu/drm/i915/Kconfig | 1 +
> > drivers/gpu/drm/i915/Makefile | 1 +
> > drivers/gpu/drm/i915/i915_drv.c | 9 +
> > drivers/gpu/drm/i915/i915_drv.h | 3 +
> > drivers/gpu/drm/i915/i915_hwmon.c | 788 ++++++++++++++++++++++++++++++
> > drivers/gpu/drm/i915/i915_hwmon.h | 41 ++
> > drivers/gpu/drm/i915/i915_reg.h | 53 ++
> > 7 files changed, 896 insertions(+)
> > create mode 100644 drivers/gpu/drm/i915/i915_hwmon.c
> > create mode 100644 drivers/gpu/drm/i915/i915_hwmon.h
> >
> > diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig
> > index 1e1cb245fca77..ec8d5a0d7ea96 100644
> > --- a/drivers/gpu/drm/i915/Kconfig
> > +++ b/drivers/gpu/drm/i915/Kconfig
> > @@ -14,6 +14,7 @@ config DRM_I915
> > select DRM_MIPI_DSI
> > select RELAY
> > select IRQ_WORK
> > + select HWMON
> > # i915 depends on ACPI_VIDEO when ACPI is enabled
> > # but for select to work, need to select ACPI_VIDEO's dependencies, ick
> > select BACKLIGHT_CLASS_DEVICE if ACPI
> > diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
> > index d0d936d9137bc..e213e2b129e20 100644
> > --- a/drivers/gpu/drm/i915/Makefile
> > +++ b/drivers/gpu/drm/i915/Makefile
> > @@ -37,6 +37,7 @@ i915-y += i915_drv.o \
> > i915_config.o \
> > i915_irq.o \
> > i915_getparam.o \
> > + i915_hwmon.o \
> > i915_mitigations.o \
> > i915_params.o \
> > i915_pci.o \
> > diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
> > index 305557e1942aa..84c7de3b34c7d 100644
> > --- a/drivers/gpu/drm/i915/i915_drv.c
> > +++ b/drivers/gpu/drm/i915/i915_drv.c
> > @@ -69,6 +69,7 @@
> >
> > #include "i915_debugfs.h"
> > #include "i915_drv.h"
> > +#include "i915_hwmon.h"
> > #include "i915_ioc32.h"
> > #include "i915_irq.h"
> > #include "i915_memcpy.h"
> > @@ -675,6 +676,10 @@ static void i915_driver_register(struct drm_i915_private *dev_priv)
> > i915_debugfs_register(dev_priv);
> > i915_setup_sysfs(dev_priv);
> >
> > + /* Register with hwmon */
> > + if (i915_hwmon_init(&dev_priv->drm))
>
> Please pass in i915, not struct drm_device.
Done.
> This is i915_driver_register. Almost all functions being have _register
> in them. Why not this one?
I have changed the function names to get i915_hwmon_register and
i915_hwmon_unregister.
> > + drm_err(&dev_priv->drm, "Failed to register driver hwmon!\n");
>
> Not sure we want this error message at this level.
I have removed this error message and changed i915_hwmon_register so it
returns void instead of int.
>
> > +
> > /* Depends on sysfs having been initialized */
> > i915_perf_register(dev_priv);
> >
> > @@ -709,9 +714,13 @@ static void i915_driver_unregister(struct drm_i915_private *dev_priv)
> > intel_gt_driver_unregister(&dev_priv->gt);
> >
> > i915_perf_unregister(dev_priv);
> > +
> > + i915_hwmon_fini(&dev_priv->drm);
> > +
>
> Naming, again _unregister in most places.
Fixed.
>
> > i915_pmu_unregister(dev_priv);
> >
> > i915_teardown_sysfs(dev_priv);
> > +
>
> Stray newline.
Fixed.
>
> > drm_dev_unplug(&dev_priv->drm);
> >
> > i915_gem_driver_unregister(dev_priv);
> > diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> > index 69e43bf91a153..7e9b452c77e2b 100644
> > --- a/drivers/gpu/drm/i915/i915_drv.h
> > +++ b/drivers/gpu/drm/i915/i915_drv.h
> > @@ -61,6 +61,7 @@
> > #include <drm/drm_connector.h>
> > #include <drm/i915_mei_hdcp_interface.h>
> >
> > +#include "i915_hwmon.h"
> > #include "i915_params.h"
> > #include "i915_reg.h"
> > #include "i915_utils.h"
> > @@ -1109,6 +1110,8 @@ struct drm_i915_private {
> >
> > struct i915_perf perf;
> >
> > + struct i915_hwmon hwmon;
> > +
> > /* Abstract the submission mechanism (legacy ringbuffer or execlists) away */
> > struct intel_gt gt;
> >
> > diff --git a/drivers/gpu/drm/i915/i915_hwmon.c b/drivers/gpu/drm/i915/i915_hwmon.c
> > new file mode 100644
> > index 0000000000000..ab8f32f7ed1de
> > --- /dev/null
> > +++ b/drivers/gpu/drm/i915/i915_hwmon.c
> > @@ -0,0 +1,788 @@
> > +// SPDX-License-Identifier: MIT
> > +
>
> Superfluous newline.
Fixed.
>
> > +/*
> > + * Copyright © 2020 Intel Corporation
> > + */
> > +
> > +/*
> > + * Power-related hwmon entries.
> > + */
> > +
> > +#include <linux/hwmon.h>
> > +#include <linux/hwmon-sysfs.h>
> > +#include <linux/types.h>
> > +
> > +#include "i915_drv.h"
> > +#include "gt/intel_gt.h"
> > +#include "i915_hwmon.h"
> > +
> > +/*
> > + * SF_* - scale factors for particular quantities.
> > + * The hwmon standard says that quantities of the given types are specified
> > + * in the given units:
> > + * - time - milliseconds
> > + * - power - microwatts
> > + * - energy - microjoules
> > + */
> > +
> > +#define SF_TIME 1000
> > +#define SF_POWER 1000000
> > +#define SF_ENERGY 1000000
> > +
> > +static void
> > +_locked_with_pm_intel_uncore_rmw(struct intel_uncore *uncore,
> > + i915_reg_t reg, u32 clear, u32 set)
> > +{
> > + struct drm_i915_private *i915 = uncore->i915;
> > + struct i915_hwmon *hwmon = &i915->hwmon;
> > + intel_wakeref_t wakeref;
> > +
> > + mutex_lock(&hwmon->hwmon_lock);
> > +
> > + with_intel_runtime_pm(uncore->rpm, wakeref)
> > + intel_uncore_rmw(uncore, reg, clear, set);
> > +
> > + mutex_unlock(&hwmon->hwmon_lock);
> > +}
> > +
> > +/*
> > + * _field_read_and_scale()
>
> Unnecessary if this isn't kernel-doc, and this need not be kernel-doc.
I assume your comment is about the inclusion of the function name on the
first line of the comment block and that you are not suggesting removal of
the pre-function comment block altogether, right?
I have removed the function name from the comment, which reads:
/*
* This function's return type of u64 allows for the case where the scaling
* of the 32-bit register value might cause a result to exceed 32 bits.
*/
That seems appropriate to me, and it's information that I want to convey.
What do you think?
> > + * Return type of u64 allows for the case where the scaling might cause a
> > + * result exceeding 32 bits.
> > + */
> > +static __always_inline u64
>
> Why __always_inline? Why not let the compiler decide what makes sense?
Instigated by your comment, I have changed this. First, a word as to why
it was the way it was:
This is the comment that I had written to explain the way that it was:
/*
* This function is declared as __always_inline because it employs macro
* le32_get_bits, which expects certain of its arguments to be constants.
* The compiler would lose the "constant" property of those arguments if
* the function is invoked as non-inline (which the compiler is free to do
* if only declared "inline").
*/
The diagnostic when not inline:
In function ‘field_multiplier’,
inlined from ‘le32_get_bits’ at ./include/linux/bitfield.h:154:1,
inlined from ‘_field_read_and_scale’ at drivers/gpu/drm/i915/i915_hwmon.c:63:12:
./include/linux/bitfield.h:119:3: error: call to ‘__bad_mask’ declared with attribute error: bad bitfield mask
__bad_mask();
Requiring "inline" is a hack. Also, it depends on the compiler implementation.
Accordingly, I've implemented this in a different way.
A new parameter called "field_shift" has been added to each of those three
functions, avoiding the use of the le32* macros. These functions now do
the mask extraction/insertion explicitly.
This approach requires knowing the number of bits by which the mask must
be shifted. The shift value is now obtained using the same method as used
internally by the le32* macros in include/linux/bitfield.h (but invoked from
where the mask value is available as a constant, outside of the function call).
> > +_field_read_and_scale(struct intel_uncore *uncore, i915_reg_t rgadr,
> > + u32 field_msk, int nshift, unsigned int scale_factor)
> > +{
> > + intel_wakeref_t wakeref;
> > + u32 reg_value;
> > + u64 scaled_val;
> > +
> > + with_intel_runtime_pm(uncore->rpm, wakeref)
> > + reg_value = intel_uncore_read(uncore, rgadr);
> > +
> > + reg_value = le32_get_bits(cpu_to_le32(reg_value), field_msk);
> > + scaled_val = mul_u32_u32(scale_factor, reg_value);
> > +
> > + /* Shift, rounding to nearest */
> > + if (nshift > 0)
> > + scaled_val = (scaled_val + (1 << (nshift - 1))) >> nshift;
> > +
> > + return scaled_val;
> > +}
> > +
> > +/*
> > + * _field_read64_and_scale() - read a 64-bit register and scale.
>
> Ditto for kernel-doc style.
Fixed.
>
> > + */
> > +static __always_inline u64
>
> Ditto for __always_inline.
Fixed.
>
> > +_field_read64_and_scale(struct intel_uncore *uncore, i915_reg_t rgadr,
> > + u64 field_msk, int nshift, unsigned int scale_factor)
> > +{
> > + intel_wakeref_t wakeref;
> > + u64 reg_value;
> > + u64 scaled_val;
> > +
> > + with_intel_runtime_pm(uncore->rpm, wakeref)
> > + reg_value = intel_uncore_read64(uncore, rgadr);
> > +
> > + reg_value = le64_get_bits(cpu_to_le64(reg_value), field_msk);
> > + scaled_val = scale_factor * reg_value;
> > +
> > + /* Shift, rounding to nearest */
> > + if (nshift > 0)
> > + scaled_val = (scaled_val + (1 << (nshift - 1))) >> nshift;
> > +
> > + return scaled_val;
> > +}
> > +
> > +/*
> > + * _field_scale_and_write()
> > + */
> > +static __always_inline void
> > +_field_scale_and_write(struct intel_uncore *uncore,
> > + i915_reg_t rgadr,
> > + u32 field_msk, int nshift,
> > + unsigned int scale_factor, long lval)
> > +{
> > + u32 nval;
> > + u32 bits_to_clear;
> > + u32 bits_to_set;
> > +
> > + /* Computation in 64-bits to avoid overflow. Round to nearest. */
> > + nval = DIV_ROUND_CLOSEST_ULL((u64)lval << nshift, scale_factor);
> > +
> > + bits_to_clear = field_msk;
> > + bits_to_set = le32_to_cpu(le32_encode_bits(nval, field_msk));
> > +
> > + _locked_with_pm_intel_uncore_rmw(uncore, rgadr,
> > + bits_to_clear, bits_to_set);
> > +}
> > +
> > +/*
> > + * i915_energy1_input_show - A custom function to obtain energy1_input.
> > + * Use a custom function instead of the usual hwmon helpers in order to
> > + * guarantee 64-bits of result to user-space.
> > + * Units are microjoules.
> > + *
> > + * The underlying hardware register is 32-bits and is subject to overflow.
> > + * This function compensates for overflow of the 32-bit register by detecting
> > + * wrap-around and incrementing an overflow counter.
> > + * This only works if the register is sampled often enough to avoid
> > + * missing an instance of overflow - achieved either by repeated
> > + * queries through the API, or via a possible timer (future - TBD) that
> > + * ensures values are read often enough to catch all overflows.
> > + *
> > + * How long before overflow? For example, with an example scaling bit
> > + * shift of 14 bits (see register *PACKAGE_POWER_SKU_UNIT) and a power draw of
> > + * 1000 watts, the 32-bit counter will overflow in approximately 4.36 minutes.
> > + *
> > + * Examples:
> > + * 1 watt: (2^32 >> 14) / 1 W / (60 * 60 * 24) secs/day -> 3 days
> > + * 1000 watts: (2^32 >> 14) / 1000 W / 60 secs/min -> 4.36 minutes
> > + */
> > +static ssize_t
> > +i915_energy1_input_show(struct device *dev, struct device_attribute *attr,
> > + char *buf)
> > +{
> > + struct drm_i915_private *i915 = dev_get_drvdata(dev);
> > + struct intel_uncore *uncore = &i915->uncore;
> > + struct i915_hwmon *hwmon = &i915->hwmon;
> > + int nshift = hwmon->scl_shift_energy;
> > + ssize_t ret;
> > + intel_wakeref_t wakeref;
> > + u32 reg_value;
> > + u64 vlo;
> > + u64 vhi;
> > +
> > + mutex_lock(&hwmon->hwmon_lock);
> > +
> > + with_intel_runtime_pm(uncore->rpm, wakeref)
> > + reg_value = intel_uncore_read(uncore,
> > + hwmon->rg.reg_energy_status);
> > +
> > + /*
> > + * The u32 register concatenated with the u32 overflow counter
> > + * gives an effective energy counter size of 64-bits. However, the
> > + * computations below are done modulo 2^96 to avoid overflow during
> > + * scaling in the conversion to microjoules.
> > + *
> > + * The low-order 64-bits of the resulting quantity are returned to
> > + * the caller in units of microjoules, encoded into a decimal string.
> > + *
> > + * For a power of 1000 watts, 64 bits in units of microjoules will
> > + * overflow after 584 years.
> > + */
> > +
> > + if (hwmon->energy_counter_prev > reg_value)
> > + hwmon->energy_counter_overflow++;
> > +
> > + hwmon->energy_counter_prev = reg_value;
> > +
> > + /*
> > + * 64-bit variables vlo and vhi are used for the scaling process.
> > + * The 96-bit counter value is composed from the two 64-bit variables
> > + * vhi and vlo thusly: counter == vhi << 32 + vlo .
> > + * The 32-bits of overlap between the two variables is convenient for
> > + * handling overflows out of vlo.
> > + */
> > +
> > + vlo = reg_value;
> > + vhi = hwmon->energy_counter_overflow;
> > +
> > + mutex_unlock(&hwmon->hwmon_lock);
> > +
> > + vlo = SF_ENERGY * vlo;
> > +
> > + /* Prepare to round to nearest */
> > + if (nshift > 0)
> > + vlo += 1 << (nshift - 1);
> > +
> > + /*
> > + * Anything in the upper-32 bits of vlo gets added into vhi here,
> > + * and then cleared from vlo.
> > + */
> > + vhi = (SF_ENERGY * vhi) + (vlo >> 32);
> > + vlo &= 0xffffffffULL;
> > +
> > + /*
> > + * Apply the right shift.
> > + * - vlo shifted by itself.
> > + * - vlo receiving what's shifted out of vhi.
> > + * - vhi shifted by itself
> > + */
> > + vlo = vlo >> nshift;
> > + vlo |= (vhi << (32 - nshift)) & 0xffffffffULL;
> > + vhi = vhi >> nshift;
> > +
> > + /* Combined to get a 64-bit result in vlo. */
> > + vlo |= (vhi << 32);
> > +
> > + ret = scnprintf(buf, PAGE_SIZE, "%llu\n", vlo);
>
> sysfs_emit() instead?
Yes. At the time of writing, sysfs_emit (being relatively new) was not
available on the development branch being used. Now it is. The code has
been switched to sysfs_emit().
>
> > +
> > + return ret;
> > +}
> > +
> > +static ssize_t
> > +i915_power1_max_enable_show(struct device *dev, struct device_attribute *attr,
> > + char *buf)
> > +{
> > + struct drm_i915_private *i915 = dev_get_drvdata(dev);
> > + struct intel_uncore *uncore = &i915->uncore;
> > + intel_wakeref_t wakeref;
> > + ssize_t ret;
> > + u32 reg_value;
> > + bool is_enabled;
> > +
> > + with_intel_runtime_pm(uncore->rpm, wakeref)
> > + reg_value = intel_uncore_read(uncore,
> > + i915->hwmon.rg.pkg_rapl_limit);
> > +
> > + is_enabled = !!(reg_value & PKG_PWR_LIM_1_EN);
> > +
> > + ret = scnprintf(buf, PAGE_SIZE, "%u\n", is_enabled);
> > +
> > + return ret;
> > +}
> > +
> > +static ssize_t
> > +i915_power1_max_enable_store(struct device *dev, struct device_attribute *attr,
> > + const char *buf, size_t count)
> > +{
> > + struct drm_i915_private *i915 = dev_get_drvdata(dev);
> > + struct intel_uncore *uncore = &i915->uncore;
> > + struct i915_hwmon *hwmon = &i915->hwmon;
> > + ssize_t ret;
> > + u32 val;
> > + u32 bits_to_clear;
> > + u32 bits_to_set;
> > +
> > + ret = kstrtou32(buf, 0, &val);
> > + if (ret)
> > + return ret;
> > +
> > + bits_to_clear = PKG_PWR_LIM_1_EN;
> > + if (!val)
> > + bits_to_set = 0;
> > + else
> > + bits_to_set = PKG_PWR_LIM_1_EN;
> > +
> > + _locked_with_pm_intel_uncore_rmw(uncore, hwmon->rg.pkg_rapl_limit,
> > + bits_to_clear, bits_to_set);
> > +
> > + return count;
> > +}
> > +
> > +static ssize_t
> > +i915_power1_max_interval_show(struct device *dev, struct device_attribute *attr,
> > + char *buf)
> > +{
> > + struct drm_i915_private *i915 = dev_get_drvdata(dev);
> > + struct intel_uncore *uncore = &i915->uncore;
> > + struct i915_hwmon *hwmon = &i915->hwmon;
> > + ssize_t ret;
> > + u64 ullval;
> > +
> > + ullval = _field_read_and_scale(uncore, hwmon->rg.pkg_rapl_limit,
> > + PKG_PWR_LIM_1_TIME,
> > + hwmon->scl_shift_time, SF_TIME);
> > +
> > + ret = scnprintf(buf, PAGE_SIZE, "%llu\n", ullval);
> > +
> > + return ret;
> > +}
> > +
> > +static ssize_t
> > +i915_power1_max_interval_store(struct device *dev,
> > + struct device_attribute *attr,
> > + const char *buf, size_t count)
> > +{
> > + struct drm_i915_private *i915 = dev_get_drvdata(dev);
> > + struct intel_uncore *uncore = &i915->uncore;
> > + struct i915_hwmon *hwmon = &i915->hwmon;
> > + ssize_t ret;
> > + long val;
> > +
> > + ret = kstrtoul(buf, 0, &val);
> > + if (ret)
> > + return ret;
> > +
> > + _field_scale_and_write(uncore, hwmon->rg.pkg_rapl_limit,
> > + PKG_PWR_LIM_2_TIME,
> > + hwmon->scl_shift_time, SF_TIME, val);
> > +
> > + return count;
> > +}
> > +
> > +static ssize_t
> > +i915_power1_cap_enable_show(struct device *dev, struct device_attribute *attr,
> > + char *buf)
> > +{
> > + struct drm_i915_private *i915 = dev_get_drvdata(dev);
> > + struct intel_uncore *uncore = &i915->uncore;
> > + struct i915_hwmon *hwmon = &i915->hwmon;
> > + intel_wakeref_t wakeref;
> > + ssize_t ret;
> > + u32 reg_value;
> > + bool is_enabled;
> > +
> > + with_intel_runtime_pm(uncore->rpm, wakeref)
> > + reg_value = intel_uncore_read(uncore,
> > + hwmon->rg.pkg_rapl_limit_udw);
> > +
> > + is_enabled = !!(reg_value & PKG_PWR_LIM_2_EN);
> > +
> > + ret = scnprintf(buf, PAGE_SIZE, "%u\n", is_enabled);
> > +
> > + return ret;
> > +}
> > +
> > +static ssize_t
> > +i915_power1_cap_enable_store(struct device *dev, struct device_attribute *attr,
> > + const char *buf, size_t count)
> > +{
> > + struct drm_i915_private *i915 = dev_get_drvdata(dev);
> > + struct intel_uncore *uncore = &i915->uncore;
> > + struct i915_hwmon *hwmon = &i915->hwmon;
> > + ssize_t ret;
> > + u32 val;
> > + u32 bits_to_clear;
> > + u32 bits_to_set;
> > +
> > + ret = kstrtou32(buf, 0, &val);
> > + if (ret)
> > + return ret;
> > +
> > + bits_to_clear = PKG_PWR_LIM_2_EN;
> > + if (!val)
> > + bits_to_set = 0;
> > + else
> > + bits_to_set = PKG_PWR_LIM_2_EN;
> > +
> > + _locked_with_pm_intel_uncore_rmw(uncore, hwmon->rg.pkg_rapl_limit_udw,
> > + bits_to_clear, bits_to_set);
> > +
> > + return count;
> > +}
> > +
> > +static ssize_t
> > +i915_power_default_limit_show(struct device *dev, struct device_attribute *attr,
> > + char *buf)
> > +{
> > + struct drm_i915_private *i915 = dev_get_drvdata(dev);
> > + struct i915_hwmon *hwmon = &i915->hwmon;
> > + ssize_t ret;
> > +
> > + ret = scnprintf(buf, PAGE_SIZE, "%u\n", hwmon->power_max_initial_value);
> > +
> > + return ret;
> > +}
> > +
> > +static ssize_t
> > +i915_power_min_limit_show(struct device *dev, struct device_attribute *attr,
> > + char *buf)
> > +{
> > + struct drm_i915_private *i915 = dev_get_drvdata(dev);
> > + struct intel_uncore *uncore = &i915->uncore;
> > + struct i915_hwmon *hwmon = &i915->hwmon;
> > + ssize_t ret;
> > + u32 uval;
> > +
> > + /*
> > + * This is a 64-bit register but the individual fields are under 32 bits
> > + * in size even after scaling.
> > + * The UAPI specifies a size of 32 bits.
> > + * The UAPI specifies that 0 should be returned if unsupported.
> > + * So, using u32 and %u is sufficient.
> > + */
> > + if (i915_mmio_reg_valid(hwmon->rg.pkg_power_sku))
> > + uval = (u32)_field_read64_and_scale(uncore,
> > + hwmon->rg.pkg_power_sku,
> > + PKG_MIN_PWR,
> > + hwmon->scl_shift_power,
> > + SF_POWER);
> > + else
> > + uval = 0;
> > +
> > + ret = scnprintf(buf, PAGE_SIZE, "%u\n", uval);
> > +
> > + return ret;
> > +}
> > +
> > +static ssize_t
> > +i915_power_max_limit_show(struct device *dev, struct device_attribute *attr,
> > + char *buf)
> > +{
> > + struct drm_i915_private *i915 = dev_get_drvdata(dev);
> > + struct intel_uncore *uncore = &i915->uncore;
> > + struct i915_hwmon *hwmon = &i915->hwmon;
> > + ssize_t ret;
> > + u32 uval;
> > +
> > + /*
> > + * This is a 64-bit register but the individual fields are under 32 bits
> > + * in size even after scaling.
> > + * The UAPI specifies a size of 32 bits.
> > + * The UAPI specifies that UINT_MAX should be returned if unsupported.
> > + * So, using u32 and %u is sufficient.
> > + */
> > + if (i915_mmio_reg_valid(hwmon->rg.pkg_power_sku))
> > + uval = (u32)_field_read64_and_scale(uncore,
> > + hwmon->rg.pkg_power_sku,
> > + PKG_MAX_PWR,
> > + hwmon->scl_shift_power,
> > + SF_POWER);
> > + else
> > + uval = UINT_MAX;
> > +
> > + ret = scnprintf(buf, PAGE_SIZE, "%u\n", uval);
> > +
> > + return ret;
> > +}
> > +
> > +static SENSOR_DEVICE_ATTR(power1_max_enable, 0664,
> > + i915_power1_max_enable_show,
> > + i915_power1_max_enable_store, 0);
> > +static SENSOR_DEVICE_ATTR(power1_max_interval, 0664,
> > + i915_power1_max_interval_show,
> > + i915_power1_max_interval_store, 0);
> > +static SENSOR_DEVICE_ATTR(power1_cap_enable, 0664,
> > + i915_power1_cap_enable_show,
> > + i915_power1_cap_enable_store, 0);
> > +static SENSOR_DEVICE_ATTR(power_default_limit, 0444,
> > + i915_power_default_limit_show, NULL, 0);
> > +static SENSOR_DEVICE_ATTR(power_min_limit, 0444,
> > + i915_power_min_limit_show, NULL, 0);
> > +static SENSOR_DEVICE_ATTR(power_max_limit, 0444,
> > + i915_power_max_limit_show, NULL, 0);
> > +static SENSOR_DEVICE_ATTR(energy1_input, 0444,
> > + i915_energy1_input_show, NULL, 0);
> > +
> > +static struct attribute *hwmon_attributes[] = {
> > + &sensor_dev_attr_power1_max_enable.dev_attr.attr,
> > + &sensor_dev_attr_power1_max_interval.dev_attr.attr,
> > + &sensor_dev_attr_power1_cap_enable.dev_attr.attr,
> > + &sensor_dev_attr_power_default_limit.dev_attr.attr,
> > + &sensor_dev_attr_power_min_limit.dev_attr.attr,
> > + &sensor_dev_attr_power_max_limit.dev_attr.attr,
> > + &sensor_dev_attr_energy1_input.dev_attr.attr,
> > + NULL
> > +};
> > +
> > +static umode_t hwmon_attributes_visible(struct kobject *kobj,
> > + struct attribute *attr, int index)
> > +{
> > + struct device *dev = kobj_to_dev(kobj);
> > + struct drm_i915_private *i915 = dev_get_drvdata(dev);
> > + struct i915_hwmon *hwmon = &i915->hwmon;
> > + i915_reg_t rgadr;
> > +
> > + if (attr == &sensor_dev_attr_energy1_input.dev_attr.attr)
> > + rgadr = hwmon->rg.reg_energy_status;
> > + else if (attr == &sensor_dev_attr_power1_max_enable.dev_attr.attr)
> > + rgadr = hwmon->rg.pkg_rapl_limit;
> > + else if (attr == &sensor_dev_attr_power1_max_interval.dev_attr.attr)
> > + rgadr = hwmon->rg.pkg_rapl_limit;
> > + else if (attr == &sensor_dev_attr_power1_cap_enable.dev_attr.attr)
> > + rgadr = hwmon->rg.pkg_rapl_limit_udw;
> > + else if (attr == &sensor_dev_attr_power_default_limit.dev_attr.attr)
> > + rgadr = hwmon->rg.pkg_rapl_limit;
> > + else if (attr == &sensor_dev_attr_power_min_limit.dev_attr.attr)
> > + return attr->mode;
> > + else if (attr == &sensor_dev_attr_power_max_limit.dev_attr.attr)
> > + return attr->mode;
> > + else
> > + return 0;
> > +
> > + if (!i915_mmio_reg_valid(rgadr))
> > + return 0;
> > +
> > + return attr->mode;
> > +}
> > +
> > +static const struct attribute_group hwmon_attrgroup = {
> > + .attrs = hwmon_attributes,
> > + .is_visible = hwmon_attributes_visible,
> > +};
> > +
> > +static const struct attribute_group *hwmon_groups[] = {
> > + &hwmon_attrgroup,
> > + NULL
> > +};
> > +
> > +/*
> > + * HWMON SENSOR TYPE = hwmon_power
> > + * - Sustained Power (power1_max)
> > + * - Burst power (power1_cap)
> > + * - Peak power (power1_crit)
> > + */
> > +static const u32 i915_config_power[] = {
> > + HWMON_P_CAP | HWMON_P_MAX,
> > + 0
> > +};
> > +
> > +static const struct hwmon_channel_info i915_power = {
> > + .type = hwmon_power,
> > + .config = i915_config_power,
> > +};
> > +
> > +static const struct hwmon_channel_info *i915_info[] = {
> > + &i915_power,
> > + NULL
> > +};
> > +
> > +static umode_t
> > +i915_power_is_visible(const struct drm_i915_private *i915, u32 attr, int chan)
> > +{
> > + i915_reg_t rgadr;
> > +
> > + switch (attr) {
> > + case hwmon_power_max:
> > + rgadr = i915->hwmon.rg.pkg_rapl_limit;
> > + break;
> > + case hwmon_power_cap:
> > + rgadr = i915->hwmon.rg.pkg_rapl_limit_udw;
> > + break;
> > + default:
> > + return 0;
> > + }
> > +
> > + if (!i915_mmio_reg_valid(rgadr))
> > + return 0;
> > +
> > + return 0664;
> > +}
> > +
> > +static int
> > +i915_power_read(struct drm_i915_private *i915, u32 attr, int chan, long *val)
> > +{
> > + struct intel_uncore *uncore = &i915->uncore;
> > + struct i915_hwmon *hwmon = &i915->hwmon;
> > + int ret = 0;
> > +
> > + switch (attr) {
> > + case hwmon_power_max:
> > + *val = (long)_field_read_and_scale(uncore,
> > + hwmon->rg.pkg_rapl_limit,
> > + PKG_PWR_LIM_1,
> > + hwmon->scl_shift_power,
> > + SF_POWER);
> > + break;
> > + case hwmon_power_cap:
> > + *val = (long)_field_read_and_scale(uncore,
> > + hwmon->rg.pkg_rapl_limit_udw,
> > + PKG_PWR_LIM_2,
> > + hwmon->scl_shift_power,
> > + SF_POWER);
> > + break;
> > + default:
> > + ret = -EOPNOTSUPP;
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +static int
> > +i915_power_write(struct drm_i915_private *i915, u32 attr, int chan, long val)
> > +{
> > + struct intel_uncore *uncore = &i915->uncore;
> > + struct i915_hwmon *hwmon = &i915->hwmon;
> > + int ret = 0;
> > +
> > + switch (attr) {
> > + case hwmon_power_max:
> > + _field_scale_and_write(uncore,
> > + hwmon->rg.pkg_rapl_limit,
> > + PKG_PWR_LIM_1,
> > + hwmon->scl_shift_power,
> > + SF_POWER, val);
> > + break;
> > + case hwmon_power_cap:
> > + _field_scale_and_write(uncore,
> > + hwmon->rg.pkg_rapl_limit_udw,
> > + PKG_PWR_LIM_2,
> > + hwmon->scl_shift_power,
> > + SF_POWER, val);
> > + break;
> > + default:
> > + ret = -EOPNOTSUPP;
> > + }
> > +
> > + return ret;
> > +}
> > +
> > +static umode_t
> > +i915_is_visible(const void *data, enum hwmon_sensor_types type,
> > + u32 attr, int channel)
> > +{
> > + struct drm_i915_private *i915 = (struct drm_i915_private *)data;
> > +
> > + switch (type) {
> > + case hwmon_power:
> > + return i915_power_is_visible(i915, attr, channel);
> > + default:
> > + return 0;
> > + }
> > +}
> > +
> > +static int
> > +i915_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
> > + int channel, long *val)
> > +{
> > + struct drm_i915_private *i915 = kdev_to_i915(dev);
> > +
> > + switch (type) {
> > + case hwmon_power:
> > + return i915_power_read(i915, attr, channel, val);
> > + default:
> > + return -EOPNOTSUPP;
> > + }
> > +}
> > +
> > +static int
> > +i915_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
> > + int channel, long val)
> > +{
> > + struct drm_i915_private *i915 = kdev_to_i915(dev);
> > +
> > + switch (type) {
> > + case hwmon_power:
> > + return i915_power_write(i915, attr, channel, val);
> > + default:
> > + return -EOPNOTSUPP;
> > + }
> > +}
> > +
> > +static const struct hwmon_ops i915_hwmon_ops = {
> > + .is_visible = i915_is_visible,
> > + .read = i915_read,
> > + .write = i915_write,
> > +};
> > +
> > +static const struct hwmon_chip_info i915_chip_info = {
> > + .ops = &i915_hwmon_ops,
> > + .info = i915_info,
> > +};
> > +
> > +static void
> > +i915_hwmon_get_preregistration_info(struct drm_i915_private *i915)
> > +{
> > + struct i915_hwmon *hwmon = &i915->hwmon;
> > + struct intel_uncore *uncore = &i915->uncore;
> > + intel_wakeref_t wakeref;
> > + u32 val_sku_unit;
> > + __le32 le_sku_unit;
> > +
> > + if (IS_DG1(i915)) {
> > + hwmon->rg.pkg_power_sku_unit = PCU_PACKAGE_POWER_SKU_UNIT;
> > + hwmon->rg.pkg_power_sku = PCU_PACKAGE_POWER_SKU;
> > + hwmon->rg.pkg_energy_status = PCU_PACKAGE_ENERGY_STATUS;
> > + hwmon->rg.pkg_rapl_limit = PCU_PACKAGE_RAPL_LIMIT;
> > + hwmon->rg.pkg_rapl_limit_udw = PCU_PACKAGE_RAPL_LIMIT_UDW;
> > + hwmon->rg.plt_energy_status = PCU_PLATFORM_ENERGY_STATUS;
> > + } else {
> > + hwmon->rg.pkg_power_sku_unit = INVALID_MMIO_REG;
> > + hwmon->rg.pkg_power_sku = INVALID_MMIO_REG;
> > + hwmon->rg.pkg_energy_status = INVALID_MMIO_REG;
> > + hwmon->rg.pkg_rapl_limit = INVALID_MMIO_REG;
> > + hwmon->rg.pkg_rapl_limit_udw = INVALID_MMIO_REG;
> > + hwmon->rg.plt_energy_status = INVALID_MMIO_REG;
> > + }
> > +
> > + /*
> > + * If a platform does not support *_PLATFORM_ENERGY_STATUS,
> > + * try *PACKAGE_ENERGY_STATUS.
> > + */
> > + if (i915_mmio_reg_valid(hwmon->rg.plt_energy_status))
> > + hwmon->rg.reg_energy_status = hwmon->rg.plt_energy_status;
> > + else
> > + hwmon->rg.reg_energy_status = hwmon->rg.pkg_energy_status;
> > +
> > + wakeref = intel_runtime_pm_get(uncore->rpm);
> > +
> > + /*
> > + * The contents of register hwmon->rg.pkg_power_sku_unit do not change,
> > + * so read it once and store the shift values.
> > + */
> > + if (i915_mmio_reg_valid(hwmon->rg.pkg_power_sku_unit))
> > + val_sku_unit = intel_uncore_read(uncore,
> > + hwmon->rg.pkg_power_sku_unit);
> > + else
> > + val_sku_unit = 0;
> > +
> > + hwmon->energy_counter_overflow = 0;
> > +
> > + if (i915_mmio_reg_valid(hwmon->rg.reg_energy_status))
> > + hwmon->energy_counter_prev =
> > + intel_uncore_read(uncore, hwmon->rg.reg_energy_status);
> > + else
> > + hwmon->energy_counter_prev = 0;
> > +
> > + intel_runtime_pm_put(uncore->rpm, wakeref);
> > +
> > + le_sku_unit = cpu_to_le32(val_sku_unit);
> > + hwmon->scl_shift_power = le32_get_bits(le_sku_unit, PKG_PWR_UNIT);
> > + hwmon->scl_shift_energy = le32_get_bits(le_sku_unit, PKG_ENERGY_UNIT);
> > + hwmon->scl_shift_time = le32_get_bits(le_sku_unit, PKG_TIME_UNIT);
> > +
> > + /*
> > + * There is no direct way to obtain the power default_limit.
> > + * The best known workaround is to use the initial value of power1_max.
> > + *
> > + * The value of power1_max is reset to the default on reboot, but is
> > + * not reset by a module unload/load sequence. To allow proper
> > + * functioning after a module reload, the value for power1_max is
> > + * restored to its original value at module unload time in
> > + * i915_hwmon_fini().
> > + */
> > + hwmon->power_max_initial_value =
> > + (u32)_field_read_and_scale(uncore,
> > + hwmon->rg.pkg_rapl_limit,
> > + PKG_PWR_LIM_1,
> > + hwmon->scl_shift_power, SF_POWER);
> > +}
> > +
> > +int i915_hwmon_init(struct drm_device *drm_dev)
> > +{
> > + struct drm_i915_private *i915 = to_i915(drm_dev);
> > + struct i915_hwmon *hwmon = &i915->hwmon;
> > + struct device *hwmon_dev;
> > +
> > + mutex_init(&hwmon->hwmon_lock);
> > +
> > + i915_hwmon_get_preregistration_info(i915);
> > +
> > + hwmon_dev = hwmon_device_register_with_info(drm_dev->dev, "i915",
> > + drm_dev,
> > + &i915_chip_info,
> > + hwmon_groups);
> > +
> > + if (IS_ERR(hwmon_dev)) {
> > + mutex_destroy(&hwmon->hwmon_lock);
> > + return PTR_ERR(hwmon_dev);
> > + }
> > +
> > + hwmon->dev = hwmon_dev;
> > +
> > + return 0;
> > +}
> > +
> > +void i915_hwmon_fini(struct drm_device *drm_dev)
> > +{
> > + struct drm_i915_private *i915 = to_i915(drm_dev);
> > + struct i915_hwmon *hwmon = &i915->hwmon;
> > +
> > + if (hwmon->power_max_initial_value) {
> > + /* Restore power1_max. */
> > + _field_scale_and_write(&i915->uncore, hwmon->rg.pkg_rapl_limit,
> > + PKG_PWR_LIM_1, hwmon->scl_shift_power,
> > + SF_POWER,
> > + hwmon->power_max_initial_value);
> > + }
> > +
> > + if (hwmon->dev)
> > + hwmon_device_unregister(hwmon->dev);
> > +
> > + mutex_destroy(&hwmon->hwmon_lock);
> > +
> > + memset(hwmon, 0, sizeof(*hwmon));
> > +}
> > diff --git a/drivers/gpu/drm/i915/i915_hwmon.h b/drivers/gpu/drm/i915/i915_hwmon.h
> > new file mode 100644
> > index 0000000000000..0be919f0a463d
> > --- /dev/null
> > +++ b/drivers/gpu/drm/i915/i915_hwmon.h
> > @@ -0,0 +1,41 @@
> > +/* SPDX-License-Identifier: MIT */
> > +
> > +/*
> > + * Copyright © 2020 Intel Corporation
> > + */
> > +
> > +#ifndef __INTEL_HWMON_H__
> > +#define __INTEL_HWMON_H__
> > +
> > +#include <drm/drm_device.h>
>
> A forward declaration should be enough. Need <linux/types.h> though.
Done.
>
> > +#include "i915_reg.h"
> > +
> > +struct i915_hwmon_reg {
> > + i915_reg_t pkg_power_sku_unit;
> > + i915_reg_t pkg_power_sku;
> > + i915_reg_t pkg_energy_status;
> > + i915_reg_t pkg_rapl_limit;
> > + i915_reg_t pkg_rapl_limit_udw;
> > + i915_reg_t plt_energy_status;
> > + i915_reg_t reg_energy_status;
> > +};
> > +
> > +struct i915_hwmon {
> > + struct device *dev;
> > + struct mutex hwmon_lock; /* counter overflow logic and rmw */
> > +
> > + struct i915_hwmon_reg rg;
> > +
> > + u32 energy_counter_overflow;
> > + u32 energy_counter_prev;
> > + u32 power_max_initial_value;
> > +
> > + int scl_shift_power;
> > + int scl_shift_energy;
> > + int scl_shift_time;
> > +};
> > +
> > +int i915_hwmon_init(struct drm_device *drm_dev);
> > +void i915_hwmon_fini(struct drm_device *drm_dev);
> > +
> > +#endif
> > diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> > index f80d656331f42..62fccf71ddad6 100644
> > --- a/drivers/gpu/drm/i915/i915_reg.h
> > +++ b/drivers/gpu/drm/i915/i915_reg.h
> > @@ -4071,6 +4071,59 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
> > #define BXT_RP_STATE_CAP _MMIO(0x138170)
> > #define GEN9_RP_STATE_LIMITS _MMIO(0x138148)
> >
> > +/* DG1 */
> > +
> > +/* based on MCHBAR_MIRROR_BASE_SNB == 0x140000 */
> > +#define PCU_PACKAGE_POWER_SKU_UNIT _MMIO(MCHBAR_MIRROR_BASE_SNB + 0x5938)
> > +#define PCU_PACKAGE_ENERGY_STATUS _MMIO(MCHBAR_MIRROR_BASE_SNB + 0x593c)
> > +#define PCU_PACKAGE_RAPL_LIMIT _MMIO(MCHBAR_MIRROR_BASE_SNB + 0x59a0)
> > +#define PCU_PACKAGE_RAPL_LIMIT_UDW _MMIO(MCHBAR_MIRROR_BASE_SNB + 0x59a4)
> > +#define PCU_PACKAGE_POWER_SKU INVALID_MMIO_REG
> > +#define PCU_PLATFORM_ENERGY_STATUS INVALID_MMIO_REG
> > +
> > +/* Fields for *_PACKAGE_RAPL_LIMIT: */
> > +#define PKG_PWR_LIM_1 REG_GENMASK(14, 0)
> > +#define PKG_PWR_LIM_1_EN REG_BIT(15)
> > +#define PKG_PWR_LIM_1_TIME REG_GENMASK(23, 17)
> > +
> > +/*
> > + * Fields for *_PACKAGE_RAPL_LIMIT_UDW:
> > + * In docs, these fields may be defined relative to the entire 64-bit
> > + * register, but here they are defined relative to the 32-bit boundary.
> > + */
> > +#define PKG_PWR_LIM_2 REG_GENMASK(14, 0) // 46:32
> > +#define PKG_PWR_LIM_2_EN REG_BIT(15) // 47:47
> > +#define PKG_PWR_LIM_2_TIME REG_GENMASK(23, 17) // 55:49
> > +
> > +/*
> > + * *_PACKAGE_POWER_SKU_UNIT - fields specifying scaling for PCU quantities.
> > + * - PKG_PWR_UNIT - Power Units used for power control registers. The
> > + * actual unit value is calculated by 1 W / Power(2,PKG_PWR_UNIT).
> > + * - PKG_ENERGY_UNIT - Energy Units used for power control registers. The
> > + * actual unit value is calculated by 1 J / Power(2,PKG_ENERGY_UNIT).
> > + * - PKG_TIME_UNIT - Time Units used for power control registers. The
> > + * actual unit value is calculated by 1 s / Power(2,PKG_TIME_UNIT).
> > + */
> > +#define PKG_PWR_UNIT REG_GENMASK(3, 0)
> > +#define PKG_ENERGY_UNIT REG_GENMASK(12, 8)
> > +#define PKG_TIME_UNIT REG_GENMASK(19, 16)
> > +
> > +/*
> > + * *_PACKAGE_POWER_SKU - SKU power and timing parameters.
> > + * Used herein as a 64-bit bit register.
> > + * These masks are defined using GENMASK_ULL as REG_GENMASK is limited to u32
> > + * and as GENMASK is "long" and therefore 32-bits on a 32-bit system.
> > + * PKG_PKG_TDP, PKG_MIN_PWR, and PKG_MAX_PWR are scaled in the same way as
> > + * PKG_PWR_LIM_*, above.
> > + * PKG_MAX_WIN has sub-fields for x and y, and has the value: is 1.x * 2^y.
> > + */
> > +#define PKG_PKG_TDP GENMASK_ULL(14, 0)
> > +#define PKG_MIN_PWR GENMASK_ULL(30, 16)
> > +#define PKG_MAX_PWR GENMASK_ULL(46, 32)
> > +#define PKG_MAX_WIN GENMASK_ULL(54, 48)
> > +#define PKG_MAX_WIN_Y GENMASK_ULL(54, 53)
> > +#define PKG_MAX_WIN_X GENMASK_ULL(52, 48)
> > +
> > /*
> > * Logical Context regs
> > */
>
> --
> Jani Nikula, Intel Open Source Graphics Center
V3 of this patch will arrive on this mail list shortly.
Thanks for your comments.
-Dale
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Intel-gfx] [PATCH v2 1/1] drm/i915/dg1: Add HWMON power sensor support
@ 2021-04-14 20:13 kernel test robot
0 siblings, 0 replies; 8+ messages in thread
From: kernel test robot @ 2021-04-14 20:13 UTC (permalink / raw)
To: kbuild
[-- Attachment #1: Type: text/plain, Size: 24038 bytes --]
CC: kbuild-all(a)lists.01.org
In-Reply-To: <20210413212203.793-2-dale.b.stimson@intel.com>
References: <20210413212203.793-2-dale.b.stimson@intel.com>
TO: Dale B Stimson <dale.b.stimson@intel.com>
TO: intel-gfx(a)lists.freedesktop.org
Hi Dale,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on drm-intel/for-linux-next]
[also build test WARNING on drm-tip/drm-tip next-20210414]
[cannot apply to v5.12-rc7]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Dale-B-Stimson/drm-i915-dg1-Add-HWMON-power-sensor-support/20210414-052742
base: git://anongit.freedesktop.org/drm-intel for-linux-next
:::::: branch date: 23 hours ago
:::::: commit date: 23 hours ago
config: x86_64-randconfig-m001-20210414 (attached as .config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
smatch warnings:
drivers/gpu/drm/i915/i915_hwmon.c:176 i915_energy1_input_show() error: uninitialized symbol 'reg_value'.
drivers/gpu/drm/i915/i915_hwmon.c:198 i915_energy1_input_show() warn: should '1 << (nshift - 1)' be a 64 bit type?
drivers/gpu/drm/i915/i915_hwmon.c:240 i915_power1_max_enable_show() error: uninitialized symbol 'reg_value'.
drivers/gpu/drm/i915/i915_hwmon.c:64 _field_read_and_scale() error: uninitialized symbol 'reg_value'.
drivers/gpu/drm/i915/i915_hwmon.c:332 i915_power1_cap_enable_show() error: uninitialized symbol 'reg_value'.
drivers/gpu/drm/i915/i915_hwmon.c:88 _field_read64_and_scale() error: uninitialized symbol 'reg_value'.
vim +/reg_value +176 drivers/gpu/drm/i915/i915_hwmon.c
22ab3625cabc29 Dale B Stimson 2021-04-13 47
22ab3625cabc29 Dale B Stimson 2021-04-13 48 /*
22ab3625cabc29 Dale B Stimson 2021-04-13 49 * _field_read_and_scale()
22ab3625cabc29 Dale B Stimson 2021-04-13 50 * Return type of u64 allows for the case where the scaling might cause a
22ab3625cabc29 Dale B Stimson 2021-04-13 51 * result exceeding 32 bits.
22ab3625cabc29 Dale B Stimson 2021-04-13 52 */
22ab3625cabc29 Dale B Stimson 2021-04-13 53 static __always_inline u64
22ab3625cabc29 Dale B Stimson 2021-04-13 54 _field_read_and_scale(struct intel_uncore *uncore, i915_reg_t rgadr,
22ab3625cabc29 Dale B Stimson 2021-04-13 55 u32 field_msk, int nshift, unsigned int scale_factor)
22ab3625cabc29 Dale B Stimson 2021-04-13 56 {
22ab3625cabc29 Dale B Stimson 2021-04-13 57 intel_wakeref_t wakeref;
22ab3625cabc29 Dale B Stimson 2021-04-13 58 u32 reg_value;
22ab3625cabc29 Dale B Stimson 2021-04-13 59 u64 scaled_val;
22ab3625cabc29 Dale B Stimson 2021-04-13 60
22ab3625cabc29 Dale B Stimson 2021-04-13 61 with_intel_runtime_pm(uncore->rpm, wakeref)
22ab3625cabc29 Dale B Stimson 2021-04-13 62 reg_value = intel_uncore_read(uncore, rgadr);
22ab3625cabc29 Dale B Stimson 2021-04-13 63
22ab3625cabc29 Dale B Stimson 2021-04-13 @64 reg_value = le32_get_bits(cpu_to_le32(reg_value), field_msk);
22ab3625cabc29 Dale B Stimson 2021-04-13 65 scaled_val = mul_u32_u32(scale_factor, reg_value);
22ab3625cabc29 Dale B Stimson 2021-04-13 66
22ab3625cabc29 Dale B Stimson 2021-04-13 67 /* Shift, rounding to nearest */
22ab3625cabc29 Dale B Stimson 2021-04-13 68 if (nshift > 0)
22ab3625cabc29 Dale B Stimson 2021-04-13 69 scaled_val = (scaled_val + (1 << (nshift - 1))) >> nshift;
22ab3625cabc29 Dale B Stimson 2021-04-13 70
22ab3625cabc29 Dale B Stimson 2021-04-13 71 return scaled_val;
22ab3625cabc29 Dale B Stimson 2021-04-13 72 }
22ab3625cabc29 Dale B Stimson 2021-04-13 73
22ab3625cabc29 Dale B Stimson 2021-04-13 74 /*
22ab3625cabc29 Dale B Stimson 2021-04-13 75 * _field_read64_and_scale() - read a 64-bit register and scale.
22ab3625cabc29 Dale B Stimson 2021-04-13 76 */
22ab3625cabc29 Dale B Stimson 2021-04-13 77 static __always_inline u64
22ab3625cabc29 Dale B Stimson 2021-04-13 78 _field_read64_and_scale(struct intel_uncore *uncore, i915_reg_t rgadr,
22ab3625cabc29 Dale B Stimson 2021-04-13 79 u64 field_msk, int nshift, unsigned int scale_factor)
22ab3625cabc29 Dale B Stimson 2021-04-13 80 {
22ab3625cabc29 Dale B Stimson 2021-04-13 81 intel_wakeref_t wakeref;
22ab3625cabc29 Dale B Stimson 2021-04-13 82 u64 reg_value;
22ab3625cabc29 Dale B Stimson 2021-04-13 83 u64 scaled_val;
22ab3625cabc29 Dale B Stimson 2021-04-13 84
22ab3625cabc29 Dale B Stimson 2021-04-13 85 with_intel_runtime_pm(uncore->rpm, wakeref)
22ab3625cabc29 Dale B Stimson 2021-04-13 86 reg_value = intel_uncore_read64(uncore, rgadr);
22ab3625cabc29 Dale B Stimson 2021-04-13 87
22ab3625cabc29 Dale B Stimson 2021-04-13 @88 reg_value = le64_get_bits(cpu_to_le64(reg_value), field_msk);
22ab3625cabc29 Dale B Stimson 2021-04-13 89 scaled_val = scale_factor * reg_value;
22ab3625cabc29 Dale B Stimson 2021-04-13 90
22ab3625cabc29 Dale B Stimson 2021-04-13 91 /* Shift, rounding to nearest */
22ab3625cabc29 Dale B Stimson 2021-04-13 92 if (nshift > 0)
22ab3625cabc29 Dale B Stimson 2021-04-13 93 scaled_val = (scaled_val + (1 << (nshift - 1))) >> nshift;
22ab3625cabc29 Dale B Stimson 2021-04-13 94
22ab3625cabc29 Dale B Stimson 2021-04-13 95 return scaled_val;
22ab3625cabc29 Dale B Stimson 2021-04-13 96 }
22ab3625cabc29 Dale B Stimson 2021-04-13 97
22ab3625cabc29 Dale B Stimson 2021-04-13 98 /*
22ab3625cabc29 Dale B Stimson 2021-04-13 99 * _field_scale_and_write()
22ab3625cabc29 Dale B Stimson 2021-04-13 100 */
22ab3625cabc29 Dale B Stimson 2021-04-13 101 static __always_inline void
22ab3625cabc29 Dale B Stimson 2021-04-13 102 _field_scale_and_write(struct intel_uncore *uncore,
22ab3625cabc29 Dale B Stimson 2021-04-13 103 i915_reg_t rgadr,
22ab3625cabc29 Dale B Stimson 2021-04-13 104 u32 field_msk, int nshift,
22ab3625cabc29 Dale B Stimson 2021-04-13 105 unsigned int scale_factor, long lval)
22ab3625cabc29 Dale B Stimson 2021-04-13 106 {
22ab3625cabc29 Dale B Stimson 2021-04-13 107 u32 nval;
22ab3625cabc29 Dale B Stimson 2021-04-13 108 u32 bits_to_clear;
22ab3625cabc29 Dale B Stimson 2021-04-13 109 u32 bits_to_set;
22ab3625cabc29 Dale B Stimson 2021-04-13 110
22ab3625cabc29 Dale B Stimson 2021-04-13 111 /* Computation in 64-bits to avoid overflow. Round to nearest. */
22ab3625cabc29 Dale B Stimson 2021-04-13 112 nval = DIV_ROUND_CLOSEST_ULL((u64)lval << nshift, scale_factor);
22ab3625cabc29 Dale B Stimson 2021-04-13 113
22ab3625cabc29 Dale B Stimson 2021-04-13 114 bits_to_clear = field_msk;
22ab3625cabc29 Dale B Stimson 2021-04-13 115 bits_to_set = le32_to_cpu(le32_encode_bits(nval, field_msk));
22ab3625cabc29 Dale B Stimson 2021-04-13 116
22ab3625cabc29 Dale B Stimson 2021-04-13 117 _locked_with_pm_intel_uncore_rmw(uncore, rgadr,
22ab3625cabc29 Dale B Stimson 2021-04-13 118 bits_to_clear, bits_to_set);
22ab3625cabc29 Dale B Stimson 2021-04-13 119 }
22ab3625cabc29 Dale B Stimson 2021-04-13 120
22ab3625cabc29 Dale B Stimson 2021-04-13 121 /*
22ab3625cabc29 Dale B Stimson 2021-04-13 122 * i915_energy1_input_show - A custom function to obtain energy1_input.
22ab3625cabc29 Dale B Stimson 2021-04-13 123 * Use a custom function instead of the usual hwmon helpers in order to
22ab3625cabc29 Dale B Stimson 2021-04-13 124 * guarantee 64-bits of result to user-space.
22ab3625cabc29 Dale B Stimson 2021-04-13 125 * Units are microjoules.
22ab3625cabc29 Dale B Stimson 2021-04-13 126 *
22ab3625cabc29 Dale B Stimson 2021-04-13 127 * The underlying hardware register is 32-bits and is subject to overflow.
22ab3625cabc29 Dale B Stimson 2021-04-13 128 * This function compensates for overflow of the 32-bit register by detecting
22ab3625cabc29 Dale B Stimson 2021-04-13 129 * wrap-around and incrementing an overflow counter.
22ab3625cabc29 Dale B Stimson 2021-04-13 130 * This only works if the register is sampled often enough to avoid
22ab3625cabc29 Dale B Stimson 2021-04-13 131 * missing an instance of overflow - achieved either by repeated
22ab3625cabc29 Dale B Stimson 2021-04-13 132 * queries through the API, or via a possible timer (future - TBD) that
22ab3625cabc29 Dale B Stimson 2021-04-13 133 * ensures values are read often enough to catch all overflows.
22ab3625cabc29 Dale B Stimson 2021-04-13 134 *
22ab3625cabc29 Dale B Stimson 2021-04-13 135 * How long before overflow? For example, with an example scaling bit
22ab3625cabc29 Dale B Stimson 2021-04-13 136 * shift of 14 bits (see register *PACKAGE_POWER_SKU_UNIT) and a power draw of
22ab3625cabc29 Dale B Stimson 2021-04-13 137 * 1000 watts, the 32-bit counter will overflow in approximately 4.36 minutes.
22ab3625cabc29 Dale B Stimson 2021-04-13 138 *
22ab3625cabc29 Dale B Stimson 2021-04-13 139 * Examples:
22ab3625cabc29 Dale B Stimson 2021-04-13 140 * 1 watt: (2^32 >> 14) / 1 W / (60 * 60 * 24) secs/day -> 3 days
22ab3625cabc29 Dale B Stimson 2021-04-13 141 * 1000 watts: (2^32 >> 14) / 1000 W / 60 secs/min -> 4.36 minutes
22ab3625cabc29 Dale B Stimson 2021-04-13 142 */
22ab3625cabc29 Dale B Stimson 2021-04-13 143 static ssize_t
22ab3625cabc29 Dale B Stimson 2021-04-13 144 i915_energy1_input_show(struct device *dev, struct device_attribute *attr,
22ab3625cabc29 Dale B Stimson 2021-04-13 145 char *buf)
22ab3625cabc29 Dale B Stimson 2021-04-13 146 {
22ab3625cabc29 Dale B Stimson 2021-04-13 147 struct drm_i915_private *i915 = dev_get_drvdata(dev);
22ab3625cabc29 Dale B Stimson 2021-04-13 148 struct intel_uncore *uncore = &i915->uncore;
22ab3625cabc29 Dale B Stimson 2021-04-13 149 struct i915_hwmon *hwmon = &i915->hwmon;
22ab3625cabc29 Dale B Stimson 2021-04-13 150 int nshift = hwmon->scl_shift_energy;
22ab3625cabc29 Dale B Stimson 2021-04-13 151 ssize_t ret;
22ab3625cabc29 Dale B Stimson 2021-04-13 152 intel_wakeref_t wakeref;
22ab3625cabc29 Dale B Stimson 2021-04-13 153 u32 reg_value;
22ab3625cabc29 Dale B Stimson 2021-04-13 154 u64 vlo;
22ab3625cabc29 Dale B Stimson 2021-04-13 155 u64 vhi;
22ab3625cabc29 Dale B Stimson 2021-04-13 156
22ab3625cabc29 Dale B Stimson 2021-04-13 157 mutex_lock(&hwmon->hwmon_lock);
22ab3625cabc29 Dale B Stimson 2021-04-13 158
22ab3625cabc29 Dale B Stimson 2021-04-13 159 with_intel_runtime_pm(uncore->rpm, wakeref)
22ab3625cabc29 Dale B Stimson 2021-04-13 160 reg_value = intel_uncore_read(uncore,
22ab3625cabc29 Dale B Stimson 2021-04-13 161 hwmon->rg.reg_energy_status);
22ab3625cabc29 Dale B Stimson 2021-04-13 162
22ab3625cabc29 Dale B Stimson 2021-04-13 163 /*
22ab3625cabc29 Dale B Stimson 2021-04-13 164 * The u32 register concatenated with the u32 overflow counter
22ab3625cabc29 Dale B Stimson 2021-04-13 165 * gives an effective energy counter size of 64-bits. However, the
22ab3625cabc29 Dale B Stimson 2021-04-13 166 * computations below are done modulo 2^96 to avoid overflow during
22ab3625cabc29 Dale B Stimson 2021-04-13 167 * scaling in the conversion to microjoules.
22ab3625cabc29 Dale B Stimson 2021-04-13 168 *
22ab3625cabc29 Dale B Stimson 2021-04-13 169 * The low-order 64-bits of the resulting quantity are returned to
22ab3625cabc29 Dale B Stimson 2021-04-13 170 * the caller in units of microjoules, encoded into a decimal string.
22ab3625cabc29 Dale B Stimson 2021-04-13 171 *
22ab3625cabc29 Dale B Stimson 2021-04-13 172 * For a power of 1000 watts, 64 bits in units of microjoules will
22ab3625cabc29 Dale B Stimson 2021-04-13 173 * overflow after 584 years.
22ab3625cabc29 Dale B Stimson 2021-04-13 174 */
22ab3625cabc29 Dale B Stimson 2021-04-13 175
22ab3625cabc29 Dale B Stimson 2021-04-13 @176 if (hwmon->energy_counter_prev > reg_value)
22ab3625cabc29 Dale B Stimson 2021-04-13 177 hwmon->energy_counter_overflow++;
22ab3625cabc29 Dale B Stimson 2021-04-13 178
22ab3625cabc29 Dale B Stimson 2021-04-13 179 hwmon->energy_counter_prev = reg_value;
22ab3625cabc29 Dale B Stimson 2021-04-13 180
22ab3625cabc29 Dale B Stimson 2021-04-13 181 /*
22ab3625cabc29 Dale B Stimson 2021-04-13 182 * 64-bit variables vlo and vhi are used for the scaling process.
22ab3625cabc29 Dale B Stimson 2021-04-13 183 * The 96-bit counter value is composed from the two 64-bit variables
22ab3625cabc29 Dale B Stimson 2021-04-13 184 * vhi and vlo thusly: counter == vhi << 32 + vlo .
22ab3625cabc29 Dale B Stimson 2021-04-13 185 * The 32-bits of overlap between the two variables is convenient for
22ab3625cabc29 Dale B Stimson 2021-04-13 186 * handling overflows out of vlo.
22ab3625cabc29 Dale B Stimson 2021-04-13 187 */
22ab3625cabc29 Dale B Stimson 2021-04-13 188
22ab3625cabc29 Dale B Stimson 2021-04-13 189 vlo = reg_value;
22ab3625cabc29 Dale B Stimson 2021-04-13 190 vhi = hwmon->energy_counter_overflow;
22ab3625cabc29 Dale B Stimson 2021-04-13 191
22ab3625cabc29 Dale B Stimson 2021-04-13 192 mutex_unlock(&hwmon->hwmon_lock);
22ab3625cabc29 Dale B Stimson 2021-04-13 193
22ab3625cabc29 Dale B Stimson 2021-04-13 194 vlo = SF_ENERGY * vlo;
22ab3625cabc29 Dale B Stimson 2021-04-13 195
22ab3625cabc29 Dale B Stimson 2021-04-13 196 /* Prepare to round to nearest */
22ab3625cabc29 Dale B Stimson 2021-04-13 197 if (nshift > 0)
22ab3625cabc29 Dale B Stimson 2021-04-13 @198 vlo += 1 << (nshift - 1);
22ab3625cabc29 Dale B Stimson 2021-04-13 199
22ab3625cabc29 Dale B Stimson 2021-04-13 200 /*
22ab3625cabc29 Dale B Stimson 2021-04-13 201 * Anything in the upper-32 bits of vlo gets added into vhi here,
22ab3625cabc29 Dale B Stimson 2021-04-13 202 * and then cleared from vlo.
22ab3625cabc29 Dale B Stimson 2021-04-13 203 */
22ab3625cabc29 Dale B Stimson 2021-04-13 204 vhi = (SF_ENERGY * vhi) + (vlo >> 32);
22ab3625cabc29 Dale B Stimson 2021-04-13 205 vlo &= 0xffffffffULL;
22ab3625cabc29 Dale B Stimson 2021-04-13 206
22ab3625cabc29 Dale B Stimson 2021-04-13 207 /*
22ab3625cabc29 Dale B Stimson 2021-04-13 208 * Apply the right shift.
22ab3625cabc29 Dale B Stimson 2021-04-13 209 * - vlo shifted by itself.
22ab3625cabc29 Dale B Stimson 2021-04-13 210 * - vlo receiving what's shifted out of vhi.
22ab3625cabc29 Dale B Stimson 2021-04-13 211 * - vhi shifted by itself
22ab3625cabc29 Dale B Stimson 2021-04-13 212 */
22ab3625cabc29 Dale B Stimson 2021-04-13 213 vlo = vlo >> nshift;
22ab3625cabc29 Dale B Stimson 2021-04-13 214 vlo |= (vhi << (32 - nshift)) & 0xffffffffULL;
22ab3625cabc29 Dale B Stimson 2021-04-13 215 vhi = vhi >> nshift;
22ab3625cabc29 Dale B Stimson 2021-04-13 216
22ab3625cabc29 Dale B Stimson 2021-04-13 217 /* Combined to get a 64-bit result in vlo. */
22ab3625cabc29 Dale B Stimson 2021-04-13 218 vlo |= (vhi << 32);
22ab3625cabc29 Dale B Stimson 2021-04-13 219
22ab3625cabc29 Dale B Stimson 2021-04-13 220 ret = scnprintf(buf, PAGE_SIZE, "%llu\n", vlo);
22ab3625cabc29 Dale B Stimson 2021-04-13 221
22ab3625cabc29 Dale B Stimson 2021-04-13 222 return ret;
22ab3625cabc29 Dale B Stimson 2021-04-13 223 }
22ab3625cabc29 Dale B Stimson 2021-04-13 224
22ab3625cabc29 Dale B Stimson 2021-04-13 225 static ssize_t
22ab3625cabc29 Dale B Stimson 2021-04-13 226 i915_power1_max_enable_show(struct device *dev, struct device_attribute *attr,
22ab3625cabc29 Dale B Stimson 2021-04-13 227 char *buf)
22ab3625cabc29 Dale B Stimson 2021-04-13 228 {
22ab3625cabc29 Dale B Stimson 2021-04-13 229 struct drm_i915_private *i915 = dev_get_drvdata(dev);
22ab3625cabc29 Dale B Stimson 2021-04-13 230 struct intel_uncore *uncore = &i915->uncore;
22ab3625cabc29 Dale B Stimson 2021-04-13 231 intel_wakeref_t wakeref;
22ab3625cabc29 Dale B Stimson 2021-04-13 232 ssize_t ret;
22ab3625cabc29 Dale B Stimson 2021-04-13 233 u32 reg_value;
22ab3625cabc29 Dale B Stimson 2021-04-13 234 bool is_enabled;
22ab3625cabc29 Dale B Stimson 2021-04-13 235
22ab3625cabc29 Dale B Stimson 2021-04-13 236 with_intel_runtime_pm(uncore->rpm, wakeref)
22ab3625cabc29 Dale B Stimson 2021-04-13 237 reg_value = intel_uncore_read(uncore,
22ab3625cabc29 Dale B Stimson 2021-04-13 238 i915->hwmon.rg.pkg_rapl_limit);
22ab3625cabc29 Dale B Stimson 2021-04-13 239
22ab3625cabc29 Dale B Stimson 2021-04-13 @240 is_enabled = !!(reg_value & PKG_PWR_LIM_1_EN);
22ab3625cabc29 Dale B Stimson 2021-04-13 241
22ab3625cabc29 Dale B Stimson 2021-04-13 242 ret = scnprintf(buf, PAGE_SIZE, "%u\n", is_enabled);
22ab3625cabc29 Dale B Stimson 2021-04-13 243
22ab3625cabc29 Dale B Stimson 2021-04-13 244 return ret;
22ab3625cabc29 Dale B Stimson 2021-04-13 245 }
22ab3625cabc29 Dale B Stimson 2021-04-13 246
22ab3625cabc29 Dale B Stimson 2021-04-13 247 static ssize_t
22ab3625cabc29 Dale B Stimson 2021-04-13 248 i915_power1_max_enable_store(struct device *dev, struct device_attribute *attr,
22ab3625cabc29 Dale B Stimson 2021-04-13 249 const char *buf, size_t count)
22ab3625cabc29 Dale B Stimson 2021-04-13 250 {
22ab3625cabc29 Dale B Stimson 2021-04-13 251 struct drm_i915_private *i915 = dev_get_drvdata(dev);
22ab3625cabc29 Dale B Stimson 2021-04-13 252 struct intel_uncore *uncore = &i915->uncore;
22ab3625cabc29 Dale B Stimson 2021-04-13 253 struct i915_hwmon *hwmon = &i915->hwmon;
22ab3625cabc29 Dale B Stimson 2021-04-13 254 ssize_t ret;
22ab3625cabc29 Dale B Stimson 2021-04-13 255 u32 val;
22ab3625cabc29 Dale B Stimson 2021-04-13 256 u32 bits_to_clear;
22ab3625cabc29 Dale B Stimson 2021-04-13 257 u32 bits_to_set;
22ab3625cabc29 Dale B Stimson 2021-04-13 258
22ab3625cabc29 Dale B Stimson 2021-04-13 259 ret = kstrtou32(buf, 0, &val);
22ab3625cabc29 Dale B Stimson 2021-04-13 260 if (ret)
22ab3625cabc29 Dale B Stimson 2021-04-13 261 return ret;
22ab3625cabc29 Dale B Stimson 2021-04-13 262
22ab3625cabc29 Dale B Stimson 2021-04-13 263 bits_to_clear = PKG_PWR_LIM_1_EN;
22ab3625cabc29 Dale B Stimson 2021-04-13 264 if (!val)
22ab3625cabc29 Dale B Stimson 2021-04-13 265 bits_to_set = 0;
22ab3625cabc29 Dale B Stimson 2021-04-13 266 else
22ab3625cabc29 Dale B Stimson 2021-04-13 267 bits_to_set = PKG_PWR_LIM_1_EN;
22ab3625cabc29 Dale B Stimson 2021-04-13 268
22ab3625cabc29 Dale B Stimson 2021-04-13 269 _locked_with_pm_intel_uncore_rmw(uncore, hwmon->rg.pkg_rapl_limit,
22ab3625cabc29 Dale B Stimson 2021-04-13 270 bits_to_clear, bits_to_set);
22ab3625cabc29 Dale B Stimson 2021-04-13 271
22ab3625cabc29 Dale B Stimson 2021-04-13 272 return count;
22ab3625cabc29 Dale B Stimson 2021-04-13 273 }
22ab3625cabc29 Dale B Stimson 2021-04-13 274
22ab3625cabc29 Dale B Stimson 2021-04-13 275 static ssize_t
22ab3625cabc29 Dale B Stimson 2021-04-13 276 i915_power1_max_interval_show(struct device *dev, struct device_attribute *attr,
22ab3625cabc29 Dale B Stimson 2021-04-13 277 char *buf)
22ab3625cabc29 Dale B Stimson 2021-04-13 278 {
22ab3625cabc29 Dale B Stimson 2021-04-13 279 struct drm_i915_private *i915 = dev_get_drvdata(dev);
22ab3625cabc29 Dale B Stimson 2021-04-13 280 struct intel_uncore *uncore = &i915->uncore;
22ab3625cabc29 Dale B Stimson 2021-04-13 281 struct i915_hwmon *hwmon = &i915->hwmon;
22ab3625cabc29 Dale B Stimson 2021-04-13 282 ssize_t ret;
22ab3625cabc29 Dale B Stimson 2021-04-13 283 u64 ullval;
22ab3625cabc29 Dale B Stimson 2021-04-13 284
22ab3625cabc29 Dale B Stimson 2021-04-13 285 ullval = _field_read_and_scale(uncore, hwmon->rg.pkg_rapl_limit,
22ab3625cabc29 Dale B Stimson 2021-04-13 286 PKG_PWR_LIM_1_TIME,
22ab3625cabc29 Dale B Stimson 2021-04-13 287 hwmon->scl_shift_time, SF_TIME);
22ab3625cabc29 Dale B Stimson 2021-04-13 288
22ab3625cabc29 Dale B Stimson 2021-04-13 289 ret = scnprintf(buf, PAGE_SIZE, "%llu\n", ullval);
22ab3625cabc29 Dale B Stimson 2021-04-13 290
22ab3625cabc29 Dale B Stimson 2021-04-13 291 return ret;
22ab3625cabc29 Dale B Stimson 2021-04-13 292 }
22ab3625cabc29 Dale B Stimson 2021-04-13 293
22ab3625cabc29 Dale B Stimson 2021-04-13 294 static ssize_t
22ab3625cabc29 Dale B Stimson 2021-04-13 295 i915_power1_max_interval_store(struct device *dev,
22ab3625cabc29 Dale B Stimson 2021-04-13 296 struct device_attribute *attr,
22ab3625cabc29 Dale B Stimson 2021-04-13 297 const char *buf, size_t count)
22ab3625cabc29 Dale B Stimson 2021-04-13 298 {
22ab3625cabc29 Dale B Stimson 2021-04-13 299 struct drm_i915_private *i915 = dev_get_drvdata(dev);
22ab3625cabc29 Dale B Stimson 2021-04-13 300 struct intel_uncore *uncore = &i915->uncore;
22ab3625cabc29 Dale B Stimson 2021-04-13 301 struct i915_hwmon *hwmon = &i915->hwmon;
22ab3625cabc29 Dale B Stimson 2021-04-13 302 ssize_t ret;
22ab3625cabc29 Dale B Stimson 2021-04-13 303 long val;
22ab3625cabc29 Dale B Stimson 2021-04-13 304
22ab3625cabc29 Dale B Stimson 2021-04-13 305 ret = kstrtoul(buf, 0, &val);
22ab3625cabc29 Dale B Stimson 2021-04-13 306 if (ret)
22ab3625cabc29 Dale B Stimson 2021-04-13 307 return ret;
22ab3625cabc29 Dale B Stimson 2021-04-13 308
22ab3625cabc29 Dale B Stimson 2021-04-13 309 _field_scale_and_write(uncore, hwmon->rg.pkg_rapl_limit,
22ab3625cabc29 Dale B Stimson 2021-04-13 310 PKG_PWR_LIM_2_TIME,
22ab3625cabc29 Dale B Stimson 2021-04-13 311 hwmon->scl_shift_time, SF_TIME, val);
22ab3625cabc29 Dale B Stimson 2021-04-13 312
22ab3625cabc29 Dale B Stimson 2021-04-13 313 return count;
22ab3625cabc29 Dale B Stimson 2021-04-13 314 }
22ab3625cabc29 Dale B Stimson 2021-04-13 315
22ab3625cabc29 Dale B Stimson 2021-04-13 316 static ssize_t
22ab3625cabc29 Dale B Stimson 2021-04-13 317 i915_power1_cap_enable_show(struct device *dev, struct device_attribute *attr,
22ab3625cabc29 Dale B Stimson 2021-04-13 318 char *buf)
22ab3625cabc29 Dale B Stimson 2021-04-13 319 {
22ab3625cabc29 Dale B Stimson 2021-04-13 320 struct drm_i915_private *i915 = dev_get_drvdata(dev);
22ab3625cabc29 Dale B Stimson 2021-04-13 321 struct intel_uncore *uncore = &i915->uncore;
22ab3625cabc29 Dale B Stimson 2021-04-13 322 struct i915_hwmon *hwmon = &i915->hwmon;
22ab3625cabc29 Dale B Stimson 2021-04-13 323 intel_wakeref_t wakeref;
22ab3625cabc29 Dale B Stimson 2021-04-13 324 ssize_t ret;
22ab3625cabc29 Dale B Stimson 2021-04-13 325 u32 reg_value;
22ab3625cabc29 Dale B Stimson 2021-04-13 326 bool is_enabled;
22ab3625cabc29 Dale B Stimson 2021-04-13 327
22ab3625cabc29 Dale B Stimson 2021-04-13 328 with_intel_runtime_pm(uncore->rpm, wakeref)
22ab3625cabc29 Dale B Stimson 2021-04-13 329 reg_value = intel_uncore_read(uncore,
22ab3625cabc29 Dale B Stimson 2021-04-13 330 hwmon->rg.pkg_rapl_limit_udw);
22ab3625cabc29 Dale B Stimson 2021-04-13 331
22ab3625cabc29 Dale B Stimson 2021-04-13 @332 is_enabled = !!(reg_value & PKG_PWR_LIM_2_EN);
22ab3625cabc29 Dale B Stimson 2021-04-13 333
22ab3625cabc29 Dale B Stimson 2021-04-13 334 ret = scnprintf(buf, PAGE_SIZE, "%u\n", is_enabled);
22ab3625cabc29 Dale B Stimson 2021-04-13 335
22ab3625cabc29 Dale B Stimson 2021-04-13 336 return ret;
22ab3625cabc29 Dale B Stimson 2021-04-13 337 }
22ab3625cabc29 Dale B Stimson 2021-04-13 338
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org
[-- Attachment #2: config.gz --]
[-- Type: application/gzip, Size: 34084 bytes --]
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2021-05-14 23:40 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-13 21:22 [Intel-gfx] [PATCH v1 0/1] drm/i915/dg1: Add HWMON power sensor support Dale B Stimson
2021-04-13 21:22 ` [Intel-gfx] [PATCH v2 1/1] " Dale B Stimson
2021-04-21 15:03 ` Jani Nikula
2021-05-14 23:40 ` Dale B Stimson
2021-04-13 22:02 ` [Intel-gfx] ✗ Fi.CI.CHECKPATCH: warning for drm/i915/dg1: Add HWMON power sensor support (rev2) Patchwork
2021-04-13 22:07 ` [Intel-gfx] ✗ Fi.CI.DOCS: " Patchwork
2021-04-13 22:33 ` [Intel-gfx] ✗ Fi.CI.BAT: failure " Patchwork
2021-04-14 20:13 [Intel-gfx] [PATCH v2 1/1] drm/i915/dg1: Add HWMON power sensor support kernel test robot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.