From: "Dixit, Ashutosh" <ashutosh.dixit@intel.com>
To: Badal Nilawar <badal.nilawar@intel.com>
Cc: linux-hwmon@vger.kernel.org, linux@roeck-us.net,
rodrigo.vivi@intel.com, intel-xe@lists.freedesktop.org
Subject: Re: [Intel-xe] [PATCH v6 1/5] drm/xe/hwmon: Expose power attributes
Date: Wed, 27 Sep 2023 21:55:52 -0700 [thread overview]
Message-ID: <87sf6yuc9z.wl-ashutosh.dixit@intel.com> (raw)
In-Reply-To: <20230925081842.3566834-2-badal.nilawar@intel.com>
On Mon, 25 Sep 2023 01:18:38 -0700, Badal Nilawar wrote:
>
Hi Badal,
Here's how I think this we should change this patch.
> diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
> new file mode 100644
> index 000000000000..44d814e111c6
> --- /dev/null
> +++ b/drivers/gpu/drm/xe/xe_hwmon.c
> @@ -0,0 +1,357 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2023 Intel Corporation
> + */
> +
> +#include <linux/hwmon.h>
> +
> +#include <drm/drm_managed.h>
> +#include "regs/xe_gt_regs.h"
> +#include "regs/xe_mchbar_regs.h"
> +#include "xe_device.h"
> +#include "xe_gt.h"
> +#include "xe_hwmon.h"
> +#include "xe_mmio.h"
> +
> +enum xe_hwmon_reg {
> + REG_PKG_RAPL_LIMIT,
> + REG_PKG_POWER_SKU,
> + REG_PKG_POWER_SKU_UNIT,
> +};
> +
> +enum xe_hwmon_reg_operation {
enum xe_hwmon_reg_op
> + REG_READ,
> + REG_WRITE,
> + REG_RMW,
> +};
> +
> +/*
> + * SF_* - scale factors for particular quantities according to hwmon spec.
> + */
> +#define SF_POWER 1000000 /* microwatts */
> +
> +struct xe_hwmon {
> + struct device *hwmon_dev;
> + struct xe_gt *gt;
> + struct mutex hwmon_lock; /* rmw operations*/
> + int scl_shift_power;
> +};
> +
> +static u32 xe_hwmon_get_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg hwmon_reg)
Return 'struct xe_reg' from here. Caller does .raw if needed, so caller can
do xe_hwmon_get_reg(...).raw to check when needed.
So basically this function can return a NULL register if say a particular
register does not exist on a platorm.
Also this is the function which should be called from the is_visible()
functions (as has already been done) (so if this function returns NULL the
sysfs entries will not be visible). This allows for other functions to be
void.
> +{
> + struct xe_device *xe = gt_to_xe(hwmon->gt);
> + struct xe_reg reg = XE_REG(0);
> +
> + switch (hwmon_reg) {
> + case REG_PKG_RAPL_LIMIT:
> + if (xe->info.platform == XE_DG2)
> + reg = PCU_CR_PACKAGE_RAPL_LIMIT;
> + else if (xe->info.platform == XE_PVC)
> + reg = PVC_GT0_PACKAGE_RAPL_LIMIT;
> + break;
> + case REG_PKG_POWER_SKU:
> + if (xe->info.platform == XE_DG2)
> + reg = PCU_CR_PACKAGE_POWER_SKU;
> + else if (xe->info.platform == XE_PVC)
> + reg = PVC_GT0_PACKAGE_POWER_SKU;
> + break;
> + case REG_PKG_POWER_SKU_UNIT:
> + if (xe->info.platform == XE_DG2)
> + reg = PCU_CR_PACKAGE_POWER_SKU_UNIT;
> + else if (xe->info.platform == XE_PVC)
> + reg = PVC_GT0_PACKAGE_POWER_SKU_UNIT;
> + break;
> + default:
> + drm_warn(&xe->drm, "Unknown xe hwmon reg id: %d\n", hwmon_reg);
> + break;
> + }
> +
> + return reg.raw;
> +}
> +
> +static int xe_hwmon_process_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg hwmon_reg,
Should be void. As described above this should never get called if a
particular register does not exist because the sysfs entries will not be
visible.
> + enum xe_hwmon_reg_operation operation, u32 *value,
> + u32 clr, u32 set)
I would also make the 'op' the second argument, so it is a little bit
easier to see if we are reading or writing (as I said elsewhere we can skip
adding read/write wrappers).
> +{
> + struct xe_reg reg;
> +
> + reg.raw = xe_hwmon_get_reg(hwmon, hwmon_reg);
> +
> + if (!reg.raw)
> + return -EOPNOTSUPP;
If register doesn't exist is a WARN_ON.
> +
> + switch (operation) {
> + case REG_READ:
> + *value = xe_mmio_read32(hwmon->gt, reg);
> + return 0;
> + case REG_WRITE:
> + xe_mmio_write32(hwmon->gt, reg, *value);
> + return 0;
> + case REG_RMW:
> + *value = xe_mmio_rmw32(hwmon->gt, reg, clr, set);
> + return 0;
> + default:
> + drm_warn(>_to_xe(hwmon->gt)->drm, "Invalid xe hwmon reg operation: %d\n",
> + operation);
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +int xe_hwmon_process_reg_read64(struct xe_hwmon *hwmon, enum xe_hwmon_reg hwmon_reg, u64 *value)
Why not just xe_hwmon_reg_read64?
> +{
> + struct xe_reg reg;
> +
> + reg.raw = xe_hwmon_get_reg(hwmon, hwmon_reg);
> +
> + if (!reg.raw)
> + return -EOPNOTSUPP;
> +
> + *value = xe_mmio_read64_2x32(hwmon->gt, reg);
> +
> + return 0;
Again should be void, for the same reason as xe_hwmon_process_reg.
> +}
> +
> +#define PL1_DISABLE 0
> +
> +/*
> + * HW allows arbitrary PL1 limits to be set but silently clamps these values to
> + * "typical but not guaranteed" min/max values in REG_PKG_POWER_SKU. Follow the
> + * same pattern for sysfs, allow arbitrary PL1 limits to be set but display
> + * clamped values when read.
> + */
> +static int xe_hwmon_power_max_read(struct xe_hwmon *hwmon, long *value)
> +{
> + u32 reg_val;
> + u64 reg_val64, min, max;
> +
> + xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT, REG_READ, ®_val, 0, 0);
> + /* Check if PL1 limit is disabled */
> + if (!(reg_val & PKG_PWR_LIM_1_EN)) {
> + *value = PL1_DISABLE;
> + return 0;
> + }
> +
> + reg_val = REG_FIELD_GET(PKG_PWR_LIM_1, reg_val);
> + *value = mul_u64_u32_shr(reg_val, SF_POWER, hwmon->scl_shift_power);
> +
> + xe_hwmon_process_reg_read64(hwmon, REG_PKG_POWER_SKU, ®_val64);
> + min = REG_FIELD_GET(PKG_MIN_PWR, reg_val64);
> + min = mul_u64_u32_shr(min, SF_POWER, hwmon->scl_shift_power);
> + max = REG_FIELD_GET(PKG_MAX_PWR, reg_val64);
> + max = mul_u64_u32_shr(max, SF_POWER, hwmon->scl_shift_power);
> +
> + if (min && max)
> + *value = clamp_t(u64, *value, min, max);
> +
> + return 0;
> +}
Should be void.
> +
> +static int xe_hwmon_power_max_write(struct xe_hwmon *hwmon, long value)
> +{
> + u32 reg_val;
> +
> + /* Disable PL1 limit and verify, as limit cannot be disabled on all platforms */
> + if (value == PL1_DISABLE) {
> + xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT, REG_RMW, ®_val,
> + PKG_PWR_LIM_1_EN, 0);
> + xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT, REG_READ, ®_val,
> + PKG_PWR_LIM_1_EN, 0);
> +
> + if (reg_val & PKG_PWR_LIM_1_EN)
> + return -EOPNOTSUPP;
This function cannot be void since we return an error here, so it's fine.
> + }
> +
> + /* Computation in 64-bits to avoid overflow. Round to nearest. */
> + reg_val = DIV_ROUND_CLOSEST_ULL((u64)value << hwmon->scl_shift_power, SF_POWER);
> + reg_val = PKG_PWR_LIM_1_EN | REG_FIELD_PREP(PKG_PWR_LIM_1, reg_val);
> +
> + xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT, REG_RMW, ®_val,
> + PKG_PWR_LIM_1_EN | PKG_PWR_LIM_1, reg_val);
> +
> + return 0;
> +}
> +
> +static int xe_hwmon_power_rated_max_read(struct xe_hwmon *hwmon, long *value)
> +{
> + u32 reg_val;
> +
> + xe_hwmon_process_reg(hwmon, REG_PKG_POWER_SKU, REG_READ, ®_val, 0, 0);
> + reg_val = REG_FIELD_GET(PKG_TDP, reg_val);
> + *value = mul_u64_u32_shr(reg_val, SF_POWER, hwmon->scl_shift_power);
> +
> + return 0;
> +}
Should be void.
> +
> +static const struct hwmon_channel_info *hwmon_info[] = {
> + HWMON_CHANNEL_INFO(power, HWMON_P_MAX | HWMON_P_RATED_MAX),
> + NULL
> +};
> +
> +static umode_t
> +xe_hwmon_power_is_visible(struct xe_hwmon *hwmon, u32 attr, int chan)
> +{
> + switch (attr) {
> + case hwmon_power_max:
> + return xe_hwmon_get_reg(hwmon, REG_PKG_RAPL_LIMIT) ? 0664 : 0;
> + case hwmon_power_rated_max:
> + return xe_hwmon_get_reg(hwmon, REG_PKG_POWER_SKU) ? 0444 : 0;
> + default:
> + return 0;
> + }
> +}
This is fine.
> +
> +static int
> +xe_hwmon_power_read(struct xe_hwmon *hwmon, u32 attr, int chan, long *val)
> +{
> + switch (attr) {
> + case hwmon_power_max:
> + return xe_hwmon_power_max_read(hwmon, val);
> + case hwmon_power_rated_max:
> + return xe_hwmon_power_rated_max_read(hwmon, val);
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
Fine, just take care of void returns.
> +
> +static int
> +xe_hwmon_power_write(struct xe_hwmon *hwmon, u32 attr, int chan, long val)
> +{
> + switch (attr) {
> + case hwmon_power_max:
> + return xe_hwmon_power_max_write(hwmon, val);
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +static umode_t
> +xe_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
> + u32 attr, int channel)
> +{
> + struct xe_hwmon *hwmon = (struct xe_hwmon *)drvdata;
> + int ret;
> +
> + xe_device_mem_access_get(gt_to_xe(hwmon->gt));
Let's move xe_device_mem_access_get() to xe_hwmon_process_reg().
> +
> + switch (type) {
> + case hwmon_power:
> + ret = xe_hwmon_power_is_visible(hwmon, attr, channel);
> + break;
> + default:
> + ret = 0;
> + break;
return 0;
> + }
> +
> + xe_device_mem_access_put(gt_to_xe(hwmon->gt));
> +
> + return ret;
> +}
> +
> +static int
> +xe_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
> + int channel, long *val)
> +{
> + struct xe_hwmon *hwmon = dev_get_drvdata(dev);
> + int ret;
> +
> + xe_device_mem_access_get(gt_to_xe(hwmon->gt));
Move xe_device_mem_access_get() to xe_hwmon_process_reg().
> +
> + switch (type) {
> + case hwmon_power:
> + ret = xe_hwmon_power_read(hwmon, attr, channel, val);
> + break;
> + default:
> + ret = -EOPNOTSUPP;
> + break;
return -EOPNOTSUPP;
> + }
> +
> + xe_device_mem_access_put(gt_to_xe(hwmon->gt));
> +
> + return ret;
> +}
> +
> +static int
> +xe_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
> + int channel, long val)
> +{
> + struct xe_hwmon *hwmon = dev_get_drvdata(dev);
> + int ret;
> +
> + xe_device_mem_access_get(gt_to_xe(hwmon->gt));
Move xe_device_mem_access_get() to xe_hwmon_process_reg().
> +
> + switch (type) {
> + case hwmon_power:
> + ret = xe_hwmon_power_write(hwmon, attr, channel, val);
> + break;
> + default:
> + ret = -EOPNOTSUPP;
> + break;
return -EOPNOTSUPP;
> + }
> +
> + xe_device_mem_access_put(gt_to_xe(hwmon->gt));
> +
> + return ret;
> +}
> +
> +static const struct hwmon_ops hwmon_ops = {
> + .is_visible = xe_hwmon_is_visible,
> + .read = xe_hwmon_read,
> + .write = xe_hwmon_write,
> +};
> +
> +static const struct hwmon_chip_info hwmon_chip_info = {
> + .ops = &hwmon_ops,
> + .info = hwmon_info,
> +};
> +
> +static void
> +xe_hwmon_get_preregistration_info(struct xe_device *xe)
> +{
> + struct xe_hwmon *hwmon = xe->hwmon;
> + u32 val_sku_unit = 0;
> + int ret;
> +
> + ret = xe_hwmon_process_reg(hwmon, REG_PKG_POWER_SKU_UNIT, REG_READ, &val_sku_unit, 0, 0);
> + /*
> + * The contents of register PKG_POWER_SKU_UNIT do not change,
> + * so read it once and store the shift values.
> + */
> + if (!ret)
> + hwmon->scl_shift_power = REG_FIELD_GET(PKG_PWR_UNIT, val_sku_unit);
if xe_hwmon_is_visible(... hwmon_power ...) {
xe_hwmon_process_reg();
hwmon->scl_shift_power = REG_FIELD_GET(PKG_PWR_UNIT, val_sku_unit);
}
Please let me know if any of this is not possible. I will look at the other
patches after you respond, though it looks like they will also need almost
identical changes.
Thanks.
--
Ashutosh
WARNING: multiple messages have this Message-ID (diff)
From: "Dixit, Ashutosh" <ashutosh.dixit@intel.com>
To: Badal Nilawar <badal.nilawar@intel.com>
Cc: <intel-xe@lists.freedesktop.org>, <linux-hwmon@vger.kernel.org>,
<anshuman.gupta@intel.com>, <linux@roeck-us.net>,
<andi.shyti@linux.intel.com>, <riana.tauro@intel.com>,
<matthew.brost@intel.com>, <rodrigo.vivi@intel.com>
Subject: Re: [PATCH v6 1/5] drm/xe/hwmon: Expose power attributes
Date: Wed, 27 Sep 2023 21:55:52 -0700 [thread overview]
Message-ID: <87sf6yuc9z.wl-ashutosh.dixit@intel.com> (raw)
In-Reply-To: <20230925081842.3566834-2-badal.nilawar@intel.com>
On Mon, 25 Sep 2023 01:18:38 -0700, Badal Nilawar wrote:
>
Hi Badal,
Here's how I think this we should change this patch.
> diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
> new file mode 100644
> index 000000000000..44d814e111c6
> --- /dev/null
> +++ b/drivers/gpu/drm/xe/xe_hwmon.c
> @@ -0,0 +1,357 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2023 Intel Corporation
> + */
> +
> +#include <linux/hwmon.h>
> +
> +#include <drm/drm_managed.h>
> +#include "regs/xe_gt_regs.h"
> +#include "regs/xe_mchbar_regs.h"
> +#include "xe_device.h"
> +#include "xe_gt.h"
> +#include "xe_hwmon.h"
> +#include "xe_mmio.h"
> +
> +enum xe_hwmon_reg {
> + REG_PKG_RAPL_LIMIT,
> + REG_PKG_POWER_SKU,
> + REG_PKG_POWER_SKU_UNIT,
> +};
> +
> +enum xe_hwmon_reg_operation {
enum xe_hwmon_reg_op
> + REG_READ,
> + REG_WRITE,
> + REG_RMW,
> +};
> +
> +/*
> + * SF_* - scale factors for particular quantities according to hwmon spec.
> + */
> +#define SF_POWER 1000000 /* microwatts */
> +
> +struct xe_hwmon {
> + struct device *hwmon_dev;
> + struct xe_gt *gt;
> + struct mutex hwmon_lock; /* rmw operations*/
> + int scl_shift_power;
> +};
> +
> +static u32 xe_hwmon_get_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg hwmon_reg)
Return 'struct xe_reg' from here. Caller does .raw if needed, so caller can
do xe_hwmon_get_reg(...).raw to check when needed.
So basically this function can return a NULL register if say a particular
register does not exist on a platorm.
Also this is the function which should be called from the is_visible()
functions (as has already been done) (so if this function returns NULL the
sysfs entries will not be visible). This allows for other functions to be
void.
> +{
> + struct xe_device *xe = gt_to_xe(hwmon->gt);
> + struct xe_reg reg = XE_REG(0);
> +
> + switch (hwmon_reg) {
> + case REG_PKG_RAPL_LIMIT:
> + if (xe->info.platform == XE_DG2)
> + reg = PCU_CR_PACKAGE_RAPL_LIMIT;
> + else if (xe->info.platform == XE_PVC)
> + reg = PVC_GT0_PACKAGE_RAPL_LIMIT;
> + break;
> + case REG_PKG_POWER_SKU:
> + if (xe->info.platform == XE_DG2)
> + reg = PCU_CR_PACKAGE_POWER_SKU;
> + else if (xe->info.platform == XE_PVC)
> + reg = PVC_GT0_PACKAGE_POWER_SKU;
> + break;
> + case REG_PKG_POWER_SKU_UNIT:
> + if (xe->info.platform == XE_DG2)
> + reg = PCU_CR_PACKAGE_POWER_SKU_UNIT;
> + else if (xe->info.platform == XE_PVC)
> + reg = PVC_GT0_PACKAGE_POWER_SKU_UNIT;
> + break;
> + default:
> + drm_warn(&xe->drm, "Unknown xe hwmon reg id: %d\n", hwmon_reg);
> + break;
> + }
> +
> + return reg.raw;
> +}
> +
> +static int xe_hwmon_process_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg hwmon_reg,
Should be void. As described above this should never get called if a
particular register does not exist because the sysfs entries will not be
visible.
> + enum xe_hwmon_reg_operation operation, u32 *value,
> + u32 clr, u32 set)
I would also make the 'op' the second argument, so it is a little bit
easier to see if we are reading or writing (as I said elsewhere we can skip
adding read/write wrappers).
> +{
> + struct xe_reg reg;
> +
> + reg.raw = xe_hwmon_get_reg(hwmon, hwmon_reg);
> +
> + if (!reg.raw)
> + return -EOPNOTSUPP;
If register doesn't exist is a WARN_ON.
> +
> + switch (operation) {
> + case REG_READ:
> + *value = xe_mmio_read32(hwmon->gt, reg);
> + return 0;
> + case REG_WRITE:
> + xe_mmio_write32(hwmon->gt, reg, *value);
> + return 0;
> + case REG_RMW:
> + *value = xe_mmio_rmw32(hwmon->gt, reg, clr, set);
> + return 0;
> + default:
> + drm_warn(>_to_xe(hwmon->gt)->drm, "Invalid xe hwmon reg operation: %d\n",
> + operation);
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +int xe_hwmon_process_reg_read64(struct xe_hwmon *hwmon, enum xe_hwmon_reg hwmon_reg, u64 *value)
Why not just xe_hwmon_reg_read64?
> +{
> + struct xe_reg reg;
> +
> + reg.raw = xe_hwmon_get_reg(hwmon, hwmon_reg);
> +
> + if (!reg.raw)
> + return -EOPNOTSUPP;
> +
> + *value = xe_mmio_read64_2x32(hwmon->gt, reg);
> +
> + return 0;
Again should be void, for the same reason as xe_hwmon_process_reg.
> +}
> +
> +#define PL1_DISABLE 0
> +
> +/*
> + * HW allows arbitrary PL1 limits to be set but silently clamps these values to
> + * "typical but not guaranteed" min/max values in REG_PKG_POWER_SKU. Follow the
> + * same pattern for sysfs, allow arbitrary PL1 limits to be set but display
> + * clamped values when read.
> + */
> +static int xe_hwmon_power_max_read(struct xe_hwmon *hwmon, long *value)
> +{
> + u32 reg_val;
> + u64 reg_val64, min, max;
> +
> + xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT, REG_READ, ®_val, 0, 0);
> + /* Check if PL1 limit is disabled */
> + if (!(reg_val & PKG_PWR_LIM_1_EN)) {
> + *value = PL1_DISABLE;
> + return 0;
> + }
> +
> + reg_val = REG_FIELD_GET(PKG_PWR_LIM_1, reg_val);
> + *value = mul_u64_u32_shr(reg_val, SF_POWER, hwmon->scl_shift_power);
> +
> + xe_hwmon_process_reg_read64(hwmon, REG_PKG_POWER_SKU, ®_val64);
> + min = REG_FIELD_GET(PKG_MIN_PWR, reg_val64);
> + min = mul_u64_u32_shr(min, SF_POWER, hwmon->scl_shift_power);
> + max = REG_FIELD_GET(PKG_MAX_PWR, reg_val64);
> + max = mul_u64_u32_shr(max, SF_POWER, hwmon->scl_shift_power);
> +
> + if (min && max)
> + *value = clamp_t(u64, *value, min, max);
> +
> + return 0;
> +}
Should be void.
> +
> +static int xe_hwmon_power_max_write(struct xe_hwmon *hwmon, long value)
> +{
> + u32 reg_val;
> +
> + /* Disable PL1 limit and verify, as limit cannot be disabled on all platforms */
> + if (value == PL1_DISABLE) {
> + xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT, REG_RMW, ®_val,
> + PKG_PWR_LIM_1_EN, 0);
> + xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT, REG_READ, ®_val,
> + PKG_PWR_LIM_1_EN, 0);
> +
> + if (reg_val & PKG_PWR_LIM_1_EN)
> + return -EOPNOTSUPP;
This function cannot be void since we return an error here, so it's fine.
> + }
> +
> + /* Computation in 64-bits to avoid overflow. Round to nearest. */
> + reg_val = DIV_ROUND_CLOSEST_ULL((u64)value << hwmon->scl_shift_power, SF_POWER);
> + reg_val = PKG_PWR_LIM_1_EN | REG_FIELD_PREP(PKG_PWR_LIM_1, reg_val);
> +
> + xe_hwmon_process_reg(hwmon, REG_PKG_RAPL_LIMIT, REG_RMW, ®_val,
> + PKG_PWR_LIM_1_EN | PKG_PWR_LIM_1, reg_val);
> +
> + return 0;
> +}
> +
> +static int xe_hwmon_power_rated_max_read(struct xe_hwmon *hwmon, long *value)
> +{
> + u32 reg_val;
> +
> + xe_hwmon_process_reg(hwmon, REG_PKG_POWER_SKU, REG_READ, ®_val, 0, 0);
> + reg_val = REG_FIELD_GET(PKG_TDP, reg_val);
> + *value = mul_u64_u32_shr(reg_val, SF_POWER, hwmon->scl_shift_power);
> +
> + return 0;
> +}
Should be void.
> +
> +static const struct hwmon_channel_info *hwmon_info[] = {
> + HWMON_CHANNEL_INFO(power, HWMON_P_MAX | HWMON_P_RATED_MAX),
> + NULL
> +};
> +
> +static umode_t
> +xe_hwmon_power_is_visible(struct xe_hwmon *hwmon, u32 attr, int chan)
> +{
> + switch (attr) {
> + case hwmon_power_max:
> + return xe_hwmon_get_reg(hwmon, REG_PKG_RAPL_LIMIT) ? 0664 : 0;
> + case hwmon_power_rated_max:
> + return xe_hwmon_get_reg(hwmon, REG_PKG_POWER_SKU) ? 0444 : 0;
> + default:
> + return 0;
> + }
> +}
This is fine.
> +
> +static int
> +xe_hwmon_power_read(struct xe_hwmon *hwmon, u32 attr, int chan, long *val)
> +{
> + switch (attr) {
> + case hwmon_power_max:
> + return xe_hwmon_power_max_read(hwmon, val);
> + case hwmon_power_rated_max:
> + return xe_hwmon_power_rated_max_read(hwmon, val);
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
Fine, just take care of void returns.
> +
> +static int
> +xe_hwmon_power_write(struct xe_hwmon *hwmon, u32 attr, int chan, long val)
> +{
> + switch (attr) {
> + case hwmon_power_max:
> + return xe_hwmon_power_max_write(hwmon, val);
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +static umode_t
> +xe_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
> + u32 attr, int channel)
> +{
> + struct xe_hwmon *hwmon = (struct xe_hwmon *)drvdata;
> + int ret;
> +
> + xe_device_mem_access_get(gt_to_xe(hwmon->gt));
Let's move xe_device_mem_access_get() to xe_hwmon_process_reg().
> +
> + switch (type) {
> + case hwmon_power:
> + ret = xe_hwmon_power_is_visible(hwmon, attr, channel);
> + break;
> + default:
> + ret = 0;
> + break;
return 0;
> + }
> +
> + xe_device_mem_access_put(gt_to_xe(hwmon->gt));
> +
> + return ret;
> +}
> +
> +static int
> +xe_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
> + int channel, long *val)
> +{
> + struct xe_hwmon *hwmon = dev_get_drvdata(dev);
> + int ret;
> +
> + xe_device_mem_access_get(gt_to_xe(hwmon->gt));
Move xe_device_mem_access_get() to xe_hwmon_process_reg().
> +
> + switch (type) {
> + case hwmon_power:
> + ret = xe_hwmon_power_read(hwmon, attr, channel, val);
> + break;
> + default:
> + ret = -EOPNOTSUPP;
> + break;
return -EOPNOTSUPP;
> + }
> +
> + xe_device_mem_access_put(gt_to_xe(hwmon->gt));
> +
> + return ret;
> +}
> +
> +static int
> +xe_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
> + int channel, long val)
> +{
> + struct xe_hwmon *hwmon = dev_get_drvdata(dev);
> + int ret;
> +
> + xe_device_mem_access_get(gt_to_xe(hwmon->gt));
Move xe_device_mem_access_get() to xe_hwmon_process_reg().
> +
> + switch (type) {
> + case hwmon_power:
> + ret = xe_hwmon_power_write(hwmon, attr, channel, val);
> + break;
> + default:
> + ret = -EOPNOTSUPP;
> + break;
return -EOPNOTSUPP;
> + }
> +
> + xe_device_mem_access_put(gt_to_xe(hwmon->gt));
> +
> + return ret;
> +}
> +
> +static const struct hwmon_ops hwmon_ops = {
> + .is_visible = xe_hwmon_is_visible,
> + .read = xe_hwmon_read,
> + .write = xe_hwmon_write,
> +};
> +
> +static const struct hwmon_chip_info hwmon_chip_info = {
> + .ops = &hwmon_ops,
> + .info = hwmon_info,
> +};
> +
> +static void
> +xe_hwmon_get_preregistration_info(struct xe_device *xe)
> +{
> + struct xe_hwmon *hwmon = xe->hwmon;
> + u32 val_sku_unit = 0;
> + int ret;
> +
> + ret = xe_hwmon_process_reg(hwmon, REG_PKG_POWER_SKU_UNIT, REG_READ, &val_sku_unit, 0, 0);
> + /*
> + * The contents of register PKG_POWER_SKU_UNIT do not change,
> + * so read it once and store the shift values.
> + */
> + if (!ret)
> + hwmon->scl_shift_power = REG_FIELD_GET(PKG_PWR_UNIT, val_sku_unit);
if xe_hwmon_is_visible(... hwmon_power ...) {
xe_hwmon_process_reg();
hwmon->scl_shift_power = REG_FIELD_GET(PKG_PWR_UNIT, val_sku_unit);
}
Please let me know if any of this is not possible. I will look at the other
patches after you respond, though it looks like they will also need almost
identical changes.
Thanks.
--
Ashutosh
next prev parent reply other threads:[~2023-09-28 4:56 UTC|newest]
Thread overview: 71+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-09-25 8:18 [Intel-xe] [PATCH v6 0/5] Add HWMON support for DGFX Badal Nilawar
2023-09-25 8:18 ` Badal Nilawar
2023-09-25 8:18 ` [Intel-xe] [PATCH v6 1/5] drm/xe/hwmon: Expose power attributes Badal Nilawar
2023-09-25 8:18 ` Badal Nilawar
2023-09-25 8:58 ` [Intel-xe] " Andi Shyti
2023-09-25 8:58 ` Andi Shyti
2023-09-27 4:45 ` [Intel-xe] " Dixit, Ashutosh
2023-09-27 4:45 ` Dixit, Ashutosh
2023-09-27 10:28 ` [Intel-xe] " Nilawar, Badal
2023-09-27 10:28 ` Nilawar, Badal
2023-09-28 4:54 ` [Intel-xe] " Dixit, Ashutosh
2023-09-28 4:54 ` Dixit, Ashutosh
2023-09-27 4:53 ` [Intel-xe] " Dixit, Ashutosh
2023-09-27 4:53 ` Dixit, Ashutosh
2023-09-27 8:39 ` [Intel-xe] " Nilawar, Badal
2023-09-27 8:39 ` Nilawar, Badal
2023-09-28 4:55 ` [Intel-xe] " Dixit, Ashutosh
2023-09-28 4:55 ` Dixit, Ashutosh
2023-09-29 6:37 ` [Intel-xe] " Nilawar, Badal
2023-09-29 6:37 ` Nilawar, Badal
2023-09-29 16:48 ` [Intel-xe] " Dixit, Ashutosh
2023-09-29 16:48 ` Dixit, Ashutosh
2023-09-29 21:41 ` [Intel-xe] " Dixit, Ashutosh
2023-09-29 21:41 ` Dixit, Ashutosh
2023-10-04 0:52 ` [Intel-xe] " Dixit, Ashutosh
2023-10-04 0:52 ` Dixit, Ashutosh
2023-10-04 6:43 ` Nilawar, Badal
2023-10-04 6:43 ` Nilawar, Badal
2023-10-04 15:56 ` Rodrigo Vivi
2023-10-04 15:56 ` Rodrigo Vivi
2023-10-04 16:11 ` Rodrigo Vivi
2023-10-04 16:11 ` Rodrigo Vivi
2023-10-04 10:18 ` Nilawar, Badal
2023-10-04 10:18 ` Nilawar, Badal
2023-09-28 4:55 ` Dixit, Ashutosh [this message]
2023-09-28 4:55 ` Dixit, Ashutosh
2023-09-25 8:18 ` [Intel-xe] [PATCH v6 2/5] drm/xe/hwmon: Expose card reactive critical power Badal Nilawar
2023-09-25 8:18 ` Badal Nilawar
2023-09-25 9:03 ` [Intel-xe] " Andi Shyti
2023-09-25 9:03 ` Andi Shyti
2023-09-25 8:18 ` [Intel-xe] [PATCH v6 3/5] drm/xe/hwmon: Expose input voltage attribute Badal Nilawar
2023-09-25 8:18 ` Badal Nilawar
2023-09-25 9:04 ` [Intel-xe] " Andi Shyti
2023-09-25 9:04 ` Andi Shyti
2023-09-25 8:18 ` [Intel-xe] [PATCH v6 4/5] drm/xe/hwmon: Expose hwmon energy attribute Badal Nilawar
2023-09-25 8:18 ` Badal Nilawar
2023-09-25 11:49 ` [Intel-xe] " Andi Shyti
2023-09-25 11:49 ` Andi Shyti
2023-09-25 8:18 ` [Intel-xe] [PATCH v6 5/5] drm/xe/hwmon: Expose power1_max_interval Badal Nilawar
2023-09-25 8:18 ` Badal Nilawar
2023-09-25 11:56 ` [Intel-xe] " Andi Shyti
2023-09-25 11:56 ` Andi Shyti
[not found] ` <e5801f36-2f9a-6d24-7af2-1e7174f2e0b4@intel.com>
2023-09-26 8:01 ` [Intel-xe] " Andi Shyti
2023-09-26 8:01 ` Andi Shyti
2023-09-26 9:00 ` [Intel-xe] " Nilawar, Badal
2023-09-26 9:00 ` Nilawar, Badal
2023-09-26 21:01 ` [Intel-xe] " Andi Shyti
2023-09-26 21:01 ` Andi Shyti
2023-09-27 3:32 ` [Intel-xe] " Dixit, Ashutosh
2023-09-27 3:32 ` Dixit, Ashutosh
2023-09-27 9:04 ` [Intel-xe] " Nilawar, Badal
2023-09-27 9:04 ` Nilawar, Badal
2023-09-27 9:31 ` [Intel-xe] " Gupta, Anshuman
2023-09-27 9:31 ` Gupta, Anshuman
2023-09-25 8:20 ` [Intel-xe] ✓ CI.Patch_applied: success for Add HWMON support for DGFX (rev6) Patchwork
2023-09-25 8:20 ` [Intel-xe] ✗ CI.checkpatch: warning " Patchwork
2023-09-25 8:21 ` [Intel-xe] ✓ CI.KUnit: success " Patchwork
2023-09-25 8:28 ` [Intel-xe] ✓ CI.Build: " Patchwork
2023-09-25 8:28 ` [Intel-xe] ✗ CI.Hooks: failure " Patchwork
2023-09-25 8:30 ` [Intel-xe] ✓ CI.checksparse: success " Patchwork
2023-09-25 9:04 ` [Intel-xe] ✗ CI.BAT: failure " Patchwork
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87sf6yuc9z.wl-ashutosh.dixit@intel.com \
--to=ashutosh.dixit@intel.com \
--cc=badal.nilawar@intel.com \
--cc=intel-xe@lists.freedesktop.org \
--cc=linux-hwmon@vger.kernel.org \
--cc=linux@roeck-us.net \
--cc=rodrigo.vivi@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.