summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0090-peci-cpupower-driver-1.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0090-peci-cpupower-driver-1.patch')
-rw-r--r--meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0090-peci-cpupower-driver-1.patch405
1 files changed, 405 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0090-peci-cpupower-driver-1.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0090-peci-cpupower-driver-1.patch
new file mode 100644
index 000000000..475e9611f
--- /dev/null
+++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0090-peci-cpupower-driver-1.patch
@@ -0,0 +1,405 @@
+From e40f7d10d5306c075e2fc7e538ce1efc70eeb448 Mon Sep 17 00:00:00 2001
+From: ZhikuiRen <zhikui.ren@intel.com>
+Date: Thu, 9 Jan 2020 10:48:00 -0800
+Subject: [PATCH] Add peci-cpupower driver
+
+peci-cpupower reads CPU energy counter through peci
+and computes average power in mW since last read.
+
+Signed-off-by: ZhikuiRen <zhikui.ren@intel.com>
+---
+ Documentation/hwmon/index.rst | 1 +
+ Documentation/hwmon/peci-cpupower.rst | 52 ++++++++
+ arch/arm/configs/aspeed_g5_defconfig | 1 +
+ drivers/hwmon/Kconfig | 14 +++
+ drivers/hwmon/Makefile | 1 +
+ drivers/hwmon/peci-cpupower.c | 231 ++++++++++++++++++++++++++++++++++
+ drivers/mfd/intel-peci-client.c | 1 +
+ include/uapi/linux/peci-ioctl.h | 1 +
+ 8 files changed, 302 insertions(+)
+ create mode 100644 Documentation/hwmon/peci-cpupower.rst
+ create mode 100644 drivers/hwmon/peci-cpupower.c
+
+diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
+index 7d894c9..0bde0ef 100644
+--- a/Documentation/hwmon/index.rst
++++ b/Documentation/hwmon/index.rst
+@@ -130,6 +130,7 @@ Hardware Monitoring Kernel Drivers
+ pcf8591
+ peci-cputemp
+ peci-dimmtemp
++ peci-cpupower
+ pmbus
+ powr1220
+ pxe1610
+diff --git a/Documentation/hwmon/peci-cpupower.rst b/Documentation/hwmon/peci-cpupower.rst
+new file mode 100644
+index 0000000..4d7bd61
+--- /dev/null
++++ b/Documentation/hwmon/peci-cpupower.rst
+@@ -0,0 +1,52 @@
++.. SPDX-License-Identifier: GPL-2.0
++
++Kernel driver peci-cpupower
++==========================
++
++:Copyright: |copy| 2018-2020 Intel Corporation
++
++Supported chips:
++ One of Intel server CPUs listed below which is connected to a PECI bus.
++ * Intel Xeon E5/E7 v3 server processors
++ Intel Xeon E5-14xx v3 family
++ Intel Xeon E5-24xx v3 family
++ Intel Xeon E5-16xx v3 family
++ Intel Xeon E5-26xx v3 family
++ Intel Xeon E5-46xx v3 family
++ Intel Xeon E7-48xx v3 family
++ Intel Xeon E7-88xx v3 family
++ * Intel Xeon E5/E7 v4 server processors
++ Intel Xeon E5-16xx v4 family
++ Intel Xeon E5-26xx v4 family
++ Intel Xeon E5-46xx v4 family
++ Intel Xeon E7-48xx v4 family
++ Intel Xeon E7-88xx v4 family
++ * Intel Xeon Scalable server processors
++ Intel Xeon D family
++ Intel Xeon Bronze family
++ Intel Xeon Silver family
++ Intel Xeon Gold family
++ Intel Xeon Platinum family
++
++ Addresses scanned: PECI client address 0x30 - 0x37
++ Datasheet: Available from http://www.intel.com/design/literature.htm
++
++Author:
++ Zhikui Ren <zhikui.ren@.intel.com>
++
++Description
++-----------
++
++This driver implements a generic PECI hwmon feature which provides
++average power consumption readings of the CPU package based on energy counter
++accessible using the PECI Client Command Suite via the processor PECI client.
++
++Power values are average power since last measure given in milli Watt and
++will be measurable only when the target CPU is powered on.
++
++``sysfs`` interface
++-------------------
++======================= =======================================================
++power1_average Provides average power since last read in milli Watt.
++power1_label Provides string "Average Power".
++======================= =======================================================
+diff --git a/arch/arm/configs/aspeed_g5_defconfig b/arch/arm/configs/aspeed_g5_defconfig
+index 1e589a0..0e50040 100644
+--- a/arch/arm/configs/aspeed_g5_defconfig
++++ b/arch/arm/configs/aspeed_g5_defconfig
+@@ -177,6 +177,7 @@ CONFIG_SENSORS_OCC_P8_I2C=y
+ CONFIG_SENSORS_OCC_P9_SBE=y
+ CONFIG_SENSORS_PECI_CPUTEMP=y
+ CONFIG_SENSORS_PECI_DIMMTEMP=y
++CONFIG_SENSORS_PECI_CPUPOWER=y
+ CONFIG_PMBUS=y
+ CONFIG_SENSORS_ADM1275=y
+ CONFIG_SENSORS_IBM_CFFPS=y
+diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
+index 8312b37..07d8826 100644
+--- a/drivers/hwmon/Kconfig
++++ b/drivers/hwmon/Kconfig
+@@ -1361,6 +1361,20 @@ config SENSORS_PECI_DIMMTEMP
+ This driver can also be built as a module. If so, the module
+ will be called peci-dimmtemp.
+
++config SENSORS_PECI_CPUPOWER
++ tristate "PECI CPU power monitoring support"
++ depends on PECI
++ select MFD_INTEL_PECI_CLIENT
++ help
++ If you say yes here you get support for the generic Intel PECI
++ cputemp driver which provides average engergy
++ readings of the CPU package using
++ the PECI Client Command Suite via the processor PECI client.
++ Check Documentation/hwmon/peci-cpupower for details.
++
++ This driver can also be built as a module. If so, the module
++ will be called peci-cpupower.
++
+ source "drivers/hwmon/pmbus/Kconfig"
+
+ config SENSORS_PWM_FAN
+diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
+index e74ea92..fab43fd 100644
+--- a/drivers/hwmon/Makefile
++++ b/drivers/hwmon/Makefile
+@@ -144,6 +144,7 @@ obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
+ obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
+ obj-$(CONFIG_SENSORS_PECI_CPUTEMP) += peci-cputemp.o
+ obj-$(CONFIG_SENSORS_PECI_DIMMTEMP) += peci-dimmtemp.o
++obj-$(CONFIG_SENSORS_PECI_CPUPOWER) += peci-cpupower.o
+ obj-$(CONFIG_SENSORS_POWR1220) += powr1220.o
+ obj-$(CONFIG_SENSORS_PWM_FAN) += pwm-fan.o
+ obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON) += raspberrypi-hwmon.o
+diff --git a/drivers/hwmon/peci-cpupower.c b/drivers/hwmon/peci-cpupower.c
+new file mode 100644
+index 0000000..6907696
+--- /dev/null
++++ b/drivers/hwmon/peci-cpupower.c
+@@ -0,0 +1,231 @@
++// SPDX-License-Identifier: GPL-2.0
++// Copyright (c) 2018-2020 Intel Corporation
++
++#include <linux/hwmon.h>
++#include <linux/jiffies.h>
++#include <linux/mfd/intel-peci-client.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++#include "peci-hwmon.h"
++
++#define POWER_DEFAULT_CHANNEL_NUMS 1
++
++struct peci_cpupower {
++ struct peci_client_manager *mgr;
++ struct device *dev;
++ char name[PECI_NAME_SIZE];
++ const struct cpu_gen_info *gen_info;
++ struct peci_sensor_data energy;
++ long avg_power_val;
++ u64 core_mask;
++ u32 power_config[POWER_DEFAULT_CHANNEL_NUMS + 1];
++ uint config_idx;
++ struct hwmon_channel_info power_info;
++ const struct hwmon_channel_info *info[2];
++ struct hwmon_chip_info chip;
++};
++
++enum cpupower_channels {
++ average_power,
++};
++
++static const u32 config_table[POWER_DEFAULT_CHANNEL_NUMS] = {
++ /* average power */
++ HWMON_P_LABEL | HWMON_P_AVERAGE,
++};
++
++static const char *cpupower_label[POWER_DEFAULT_CHANNEL_NUMS] = {
++ "Average Power",
++};
++
++static int get_average_power(struct peci_cpupower *priv)
++{
++ u8 pkg_cfg[4];
++ int ret;
++
++ if (!peci_sensor_need_update(&priv->energy))
++ return 0;
++
++ ret = peci_client_read_package_config(priv->mgr,
++ PECI_MBX_INDEX_TDP_UNITS,
++ PECI_PKG_ID_PKG_ENERGY_STATUS,
++ pkg_cfg);
++
++ u32 power_unit = ((le32_to_cpup((__le32 *)pkg_cfg)) & 0x1f00) >> 8;
++
++ dev_dbg(priv->dev, "cpupower units %d (1J/pow(2, unit))\n",
++ power_unit);
++
++ ret = peci_client_read_package_config(priv->mgr,
++ PECI_MBX_INDEX_ENERGY_COUNTER,
++ PECI_PKG_ID_PKG_ENERGY_STATUS,
++ pkg_cfg);
++ if (!ret) {
++ u32 energy_cnt = le32_to_cpup((__le32 *)pkg_cfg);
++ ulong jif = jiffies;
++ ulong elapsed = (jif - priv->energy.last_updated);
++ long power_val = 0;
++ /*
++ * Don't calculate average power for first counter read or
++ * counter wrapped around or last counter read was more than
++ * 60 minutes ago (jiffies did not wrap and power calculation
++ * does not overflow or underflow
++ */
++ if (priv->energy.last_updated > 0 &&
++ energy_cnt > priv->energy.value &&
++ (elapsed < (HZ * 3600))) {
++ power_val = (long)(energy_cnt - priv->energy.value)
++ / elapsed * HZ;
++ dev_dbg(priv->dev, "countDiff %d, jiffes elapsed %d, raw powerValue %d scale to %d mW\n",
++ (long)(energy_cnt - priv->energy.value),
++ elapsed, power_val,
++ power_val >> (power_unit - 10));
++ } else {
++ dev_dbg(priv->dev, "countDiff %d, jiffes elapsed %d, skipping calculate power, try agin\n",
++ (long)(energy_cnt - priv->energy.value),
++ elapsed);
++ ret = -EAGAIN;
++ }
++
++ priv->energy.value = energy_cnt;
++ priv->avg_power_val = power_val >> ((power_unit - 10));
++ peci_sensor_mark_updated(&priv->energy);
++
++ dev_dbg(priv->dev, "energy counter 0x%8x, average power %dmW, jif %u, HZ is %d jiffies\n",
++ priv->energy.value, priv->avg_power_val,
++ jif, HZ);
++ }
++ return ret;
++}
++
++static int cpupower_read_string(struct device *dev,
++ enum hwmon_sensor_types type,
++ u32 attr, int channel, const char **str)
++{
++ if (attr != hwmon_power_label)
++ return -EOPNOTSUPP;
++ if (channel >= POWER_DEFAULT_CHANNEL_NUMS)
++ return -EOPNOTSUPP;
++ *str = cpupower_label[channel];
++
++ return 0;
++}
++
++static int cpupower_read(struct device *dev,
++ enum hwmon_sensor_types type,
++ u32 attr, int channel, long *val)
++{
++ struct peci_cpupower *priv = dev_get_drvdata(dev);
++ int ret;
++
++ if (channel >= POWER_DEFAULT_CHANNEL_NUMS ||
++ !(priv->power_config[channel] & BIT(attr)))
++ return -EOPNOTSUPP;
++
++ switch (attr) {
++ case hwmon_power_average:
++ switch (channel) {
++ case average_power:
++ ret = get_average_power(priv);
++ if (ret)
++ break;
++
++ *val = priv->avg_power_val;
++ break;
++ default:
++ break;
++ }
++ break;
++ default:
++ ret = -EOPNOTSUPP;
++ break;
++ }
++
++ return ret;
++}
++
++static umode_t cpupower_is_visible(const void *data,
++ enum hwmon_sensor_types type,
++ u32 attr, int channel)
++{
++ const struct peci_cpupower *priv = data;
++
++ if (channel < POWER_DEFAULT_CHANNEL_NUMS ||
++ (priv->power_config[channel] & BIT(attr)))
++ return 0444;
++
++ return 0;
++}
++
++static const struct hwmon_ops cpupower_ops = {
++ .is_visible = cpupower_is_visible,
++ .read_string = cpupower_read_string,
++ .read = cpupower_read,
++};
++
++static int peci_cpupower_probe(struct platform_device *pdev)
++{
++ struct peci_client_manager *mgr = dev_get_drvdata(pdev->dev.parent);
++ struct device *dev = &pdev->dev;
++ struct peci_cpupower *priv;
++ struct device *hwmon_dev;
++
++ if ((mgr->client->adapter->cmd_mask &
++ (BIT(PECI_CMD_RD_PKG_CFG))) !=
++ (BIT(PECI_CMD_RD_PKG_CFG))) {
++ return -ENODEV;
++ }
++
++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ dev_set_drvdata(dev, priv);
++ priv->mgr = mgr;
++ priv->dev = dev;
++ priv->gen_info = mgr->gen_info;
++
++ snprintf(priv->name, PECI_NAME_SIZE, "peci_cpupower.cpu%d",
++ mgr->client->addr - PECI_BASE_ADDR);
++
++ priv->power_config[priv->config_idx++] = config_table[average_power];
++
++ priv->chip.ops = &cpupower_ops;
++ priv->chip.info = priv->info;
++
++ priv->info[0] = &priv->power_info;
++
++ priv->power_info.type = hwmon_power;
++ priv->power_info.config = priv->power_config;
++
++ hwmon_dev = devm_hwmon_device_register_with_info(priv->dev,
++ priv->name,
++ priv,
++ &priv->chip,
++ NULL);
++
++ if (IS_ERR(hwmon_dev))
++ return PTR_ERR(hwmon_dev);
++
++ dev_dbg(dev, "%s: sensor '%s'\n", dev_name(hwmon_dev), priv->name);
++
++ return 0;
++}
++
++static const struct platform_device_id peci_cpupower_ids[] = {
++ { .name = "peci-cpupower", .driver_data = 0 },
++ { }
++};
++MODULE_DEVICE_TABLE(platform, peci_cpupower_ids);
++
++static struct platform_driver peci_cpupower_driver = {
++ .probe = peci_cpupower_probe,
++ .id_table = peci_cpupower_ids,
++ .driver = { .name = KBUILD_MODNAME, },
++};
++module_platform_driver(peci_cpupower_driver);
++
++MODULE_AUTHOR("Zhikui Ren <zhikui.ren@intel.com>");
++MODULE_DESCRIPTION("PECI cpupower driver");
++MODULE_LICENSE("GPL v2");
+diff --git a/drivers/mfd/intel-peci-client.c b/drivers/mfd/intel-peci-client.c
+index 9751b04..0c62ad6 100644
+--- a/drivers/mfd/intel-peci-client.c
++++ b/drivers/mfd/intel-peci-client.c
+@@ -21,6 +21,7 @@
+ static struct mfd_cell peci_functions[] = {
+ { .name = "peci-cputemp", },
+ { .name = "peci-dimmtemp", },
++ { .name = "peci-cpupower", },
+ };
+
+ static const struct cpu_gen_info cpu_gen_info_table[] = {
+diff --git a/include/uapi/linux/peci-ioctl.h b/include/uapi/linux/peci-ioctl.h
+index 843930f..d16f7c9 100644
+--- a/include/uapi/linux/peci-ioctl.h
++++ b/include/uapi/linux/peci-ioctl.h
+@@ -231,6 +231,7 @@ struct peci_rd_pkg_cfg_msg {
+ #define PECI_PKG_ID_MAX_THREAD_ID 0x0003 /* Max Thread ID */
+ #define PECI_PKG_ID_MICROCODE_REV 0x0004 /* CPU Microcode Update Revision */
+ #define PECI_PKG_ID_MACHINE_CHECK_STATUS 0x0005 /* Machine Check Status */
++#define PECI_PKG_ID_PKG_ENERGY_STATUS 0x00ff /* Average Energy */
+
+ __u8 rx_len;
+ __u8 cc;
+--
+2.7.4
+