All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
To: Guenter Roeck <linux@roeck-us.net>
Cc: Jean Delvare <jdelvare@suse.com>,
	Jason M Biils <jason.m.bills@linux.intel.com>,
	linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org,
	Alan Cox <alan@linux.intel.com>, Andrew Jeffery <andrew@aj.id.au>,
	Andy Shevchenko <andriy.shevchenko@linux.intel.com>,
	Arnd Bergmann <arnd@arndb.de>, Joel Stanley <joel@jms.id.au>,
	Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>,
	Andrew Lunn <andrew@lunn.ch>,
	Stef van Os <stef.van.os@prodrive-technologies.com>
Subject: Re: [v4 09/11] drivers/hwmon: Add PECI cputemp driver
Date: Mon, 21 May 2018 13:52:16 -0700	[thread overview]
Message-ID: <759ab5b1-e93b-c536-b567-42622d91baca@linux.intel.com> (raw)
In-Reply-To: <f8824489-a0d7-6a13-baab-a6fc50164b67@linux.intel.com>

On 5/21/2018 1:50 PM, Jae Hyun Yoo wrote:
> On 5/21/2018 1:42 PM, Guenter Roeck wrote:
>> On Mon, May 21, 2018 at 12:59:52PM -0700, Jae Hyun Yoo wrote:
>>> This commit adds PECI cputemp hwmon driver.
>>>
>>> Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
>>> Reviewed-by: Haiyue Wang <haiyue.wang@linux.intel.com>
>>> Reviewed-by: James Feist <james.feist@linux.intel.com>
>>> Reviewed-by: Vernon Mauery <vernon.mauery@linux.intel.com>
>>> Cc: Alan Cox <alan@linux.intel.com>
>>> Cc: Andrew Jeffery <andrew@aj.id.au>
>>> Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
>>> Cc: Arnd Bergmann <arnd@arndb.de>
>>> Cc: Jason M Biils <jason.m.bills@linux.intel.com>
>>> Cc: Joel Stanley <joel@jms.id.au>
>>> Cc: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
>>> Cc: Andrew Lunn <andrew@lunn.ch>
>>> Cc: Stef van Os <stef.van.os@prodrive-technologies.com>
>>> ---
>>>   drivers/hwmon/Kconfig        |  14 ++
>>>   drivers/hwmon/Makefile       |   1 +
>>>   drivers/hwmon/peci-cputemp.c | 407 +++++++++++++++++++++++++++++++++++
>>>   drivers/hwmon/peci-hwmon.c   | 124 +++++++++++
>>>   drivers/hwmon/peci-hwmon.h   |  51 +++++
>>>   5 files changed, 597 insertions(+)
>>>   create mode 100644 drivers/hwmon/peci-cputemp.c
>>>   create mode 100644 drivers/hwmon/peci-hwmon.c
>>>   create mode 100644 drivers/hwmon/peci-hwmon.h
>>>
>>> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
>>> index f10840ad465c..8492586bb1e4 100644
>>> --- a/drivers/hwmon/Kconfig
>>> +++ b/drivers/hwmon/Kconfig
>>> @@ -1256,6 +1256,20 @@ config SENSORS_NCT7904
>>>         This driver can also be built as a module.  If so, the module
>>>         will be called nct7904.
>>> +config SENSORS_PECI_CPUTEMP
>>> +    tristate "PECI CPU temperature monitoring support"
>>> +    depends on OF
>>> +    depends on PECI
>>> +    help
>>> +      If you say yes here you get support for the generic Intel PECI
>>> +      cputemp driver which provides Digital Thermal Sensor (DTS) 
>>> thermal
>>> +      readings of the CPU package and CPU cores that are accessible 
>>> using
>>> +      the PECI Client Command Suite via the processor PECI client.
>>> +      Check Documentation/hwmon/peci-cputemp for details.
>>> +
>>> +      This driver can also be built as a module.  If so, the module
>>> +      will be called peci-cputemp.
>>> +
>>>   config SENSORS_NSA320
>>>       tristate "ZyXEL NSA320 and compatible fan speed and temperature 
>>> sensors"
>>>       depends on GPIOLIB && OF
>>> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
>>> index e7d52a36e6c4..d18b374a9000 100644
>>> --- a/drivers/hwmon/Makefile
>>> +++ b/drivers/hwmon/Makefile
>>> @@ -136,6 +136,7 @@ obj-$(CONFIG_SENSORS_NCT7802)    += nct7802.o
>>>   obj-$(CONFIG_SENSORS_NCT7904)    += nct7904.o
>>>   obj-$(CONFIG_SENSORS_NSA320)    += nsa320-hwmon.o
>>>   obj-$(CONFIG_SENSORS_NTC_THERMISTOR)    += ntc_thermistor.o
>>> +obj-$(CONFIG_SENSORS_PECI_CPUTEMP)    += peci-cputemp.o peci-hwmon.o
>>>   obj-$(CONFIG_SENSORS_PC87360)    += pc87360.o
>>>   obj-$(CONFIG_SENSORS_PC87427)    += pc87427.o
>>>   obj-$(CONFIG_SENSORS_PCF8591)    += pcf8591.o
>>> diff --git a/drivers/hwmon/peci-cputemp.c b/drivers/hwmon/peci-cputemp.c
>>> new file mode 100644
>>> index 000000000000..49c5a862bb58
>>> --- /dev/null
>>> +++ b/drivers/hwmon/peci-cputemp.c
>>> @@ -0,0 +1,407 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +// Copyright (c) 2018 Intel Corporation
>>> +
>>> +#include <linux/hwmon.h>
>>> +#include <linux/jiffies.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of_device.h>
>>> +
>>> +#include "peci-hwmon.h"
>>> +
>>> +#define DEFAULT_CHANNEL_NUMS   4
>>> +#define CORETEMP_CHANNEL_NUMS  CORE_NUMS_MAX
>>> +#define CPUTEMP_CHANNEL_NUMS   (DEFAULT_CHANNEL_NUMS + 
>>> CORETEMP_CHANNEL_NUMS)
>>> +
>>> +/* The RESOLVED_CORES register in PCU of a client CPU */
>>> +#define REG_RESOLVED_CORES_BUS      1
>>> +#define REG_RESOLVED_CORES_DEVICE   30
>>> +#define REG_RESOLVED_CORES_FUNCTION 3
>>> +#define REG_RESOLVED_CORES_OFFSET   0xB4
>>> +
>>> +struct temp_group {
>>> +    struct temp_data die;
>>> +    struct temp_data tcontrol;
>>> +    struct temp_data tthrottle;
>>> +    struct temp_data tjmax;
>>> +    struct temp_data core[CORETEMP_CHANNEL_NUMS];
>>> +};
>>> +
>>> +struct peci_cputemp {
>>> +    struct peci_client *client;
>>> +    struct device *dev;
>>> +    char name[PECI_NAME_SIZE];
>>> +    struct temp_group temp;
>>> +    u8 addr;
>>> +    uint cpu_no;
>>> +    const struct cpu_gen_info *gen_info;
>>> +    u32 core_mask;
>>> +    u32 temp_config[CPUTEMP_CHANNEL_NUMS + 1];
>>> +    uint config_idx;
>>> +    struct hwmon_channel_info temp_info;
>>> +    const struct hwmon_channel_info *info[2];
>>> +    struct hwmon_chip_info chip;
>>> +};
>>> +
>>> +enum cputemp_channels {
>>> +    channel_die,
>>> +    channel_tcontrol,
>>> +    channel_tthrottle,
>>> +    channel_tjmax,
>>> +    channel_core,
>>> +};
>>> +
>>> +static const u32 config_table[DEFAULT_CHANNEL_NUMS + 1] = {
>>> +    /* Die temperature */
>>> +    HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
>>> +    HWMON_T_CRIT_HYST,
>>> +
>>> +    /* Tcontrol temperature */
>>> +    HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_CRIT,
>>> +
>>> +    /* Tthrottle temperature */
>>> +    HWMON_T_LABEL | HWMON_T_INPUT,
>>> +
>>> +    /* Tjmax temperature */
>>> +    HWMON_T_LABEL | HWMON_T_INPUT,
>>> +
>>> +    /* Core temperature - for all core channels */
>>> +    HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
>>> +    HWMON_T_CRIT_HYST,
>>> +};
>>> +
>>> +static const char *cputemp_label[CPUTEMP_CHANNEL_NUMS] = {
>>> +    "Die",
>>> +    "Tcontrol",
>>> +    "Tthrottle",
>>> +    "Tjmax",
>>> +    "Core 0", "Core 1", "Core 2", "Core 3",
>>> +    "Core 4", "Core 5", "Core 6", "Core 7",
>>> +    "Core 8", "Core 9", "Core 10", "Core 11",
>>> +    "Core 12", "Core 13", "Core 14", "Core 15",
>>> +    "Core 16", "Core 17", "Core 18", "Core 19",
>>> +    "Core 20", "Core 21", "Core 22", "Core 23",
>>> +};
>>> +
>>> +static s32 ten_dot_six_to_millidegree(s32 val)
>>> +{
>>> +    return ((val ^ 0x8000) - 0x8000) * 1000 / 64;
>>> +}
>>> +
>>> +static int get_temp_targets(struct peci_cputemp *priv)
>>> +{
>>> +    s32 tthrottle_offset;
>>> +    s32 tcontrol_margin;
>>> +    u8  pkg_cfg[4];
>>> +    int rc;
>>> +
>>> +    /**
>>> +     * Just use only the tcontrol marker to determine if target 
>>> values need
>>> +     * update.
>>> +     */
>>> +    if (!peci_hwmon_need_update(&priv->temp.tcontrol))
>>> +        return 0;
>>> +
>>> +    rc = peci_hwmon_rd_pkg_cfg_cmd(priv->client->adapter, priv->addr,
>>> +                       MBX_INDEX_TEMP_TARGET, 0, pkg_cfg);
>>> +    if (rc)
>>> +        return rc;
>>> +
>>> +    priv->temp.tjmax.value = pkg_cfg[2] * 1000;
>>> +
>>> +    tcontrol_margin = pkg_cfg[1];
>>> +    tcontrol_margin = ((tcontrol_margin ^ 0x80) - 0x80) * 1000;
>>> +    priv->temp.tcontrol.value = priv->temp.tjmax.value - 
>>> tcontrol_margin;
>>> +
>>> +    tthrottle_offset = (pkg_cfg[3] & 0x2f) * 1000;
>>> +    priv->temp.tthrottle.value = priv->temp.tjmax.value - 
>>> tthrottle_offset;
>>> +
>>> +    peci_hwmon_mark_updated(&priv->temp.tcontrol);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int get_die_temp(struct peci_cputemp *priv)
>>> +{
>>> +    struct peci_get_temp_msg msg;
>>> +    int rc;
>>> +
>>> +    if (!peci_hwmon_need_update(&priv->temp.die))
>>> +        return 0;
>>> +
>>> +    msg.addr = priv->addr;
>>> +
>>> +    rc = peci_command(priv->client->adapter, PECI_CMD_GET_TEMP, &msg);
>>> +    if (rc)
>>> +        return rc;
>>> +
>>> +    /* Note that the tjmax should be available before calling it */
>>> +    priv->temp.die.value = priv->temp.tjmax.value +
>>> +                   (msg.temp_raw * 1000 / 64);
>>> +
>>> +    peci_hwmon_mark_updated(&priv->temp.die);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int get_core_temp(struct peci_cputemp *priv, int core_index)
>>> +{
>>> +    s32 core_dts_margin;
>>> +    u8  pkg_cfg[4];
>>> +    int rc;
>>> +
>>> +    if (!peci_hwmon_need_update(&priv->temp.core[core_index]))
>>> +        return 0;
>>> +
>>> +    rc = peci_hwmon_rd_pkg_cfg_cmd(priv->client->adapter, priv->addr,
>>> +                       MBX_INDEX_PER_CORE_DTS_TEMP,
>>> +                       core_index, pkg_cfg);
>>> +    if (rc)
>>> +        return rc;
>>> +
>>> +    core_dts_margin = le16_to_cpup((__le16 *)pkg_cfg);
>>> +
>>> +    /**
>>> +     * Processors return a value of the core DTS reading in 10.6 format
>>> +     * (10 bits signed decimal, 6 bits fractional).
>>> +     * Error codes:
>>> +     *   0x8000: General sensor error
>>> +     *   0x8001: Reserved
>>> +     *   0x8002: Underflow on reading value
>>> +     *   0x8003-0x81ff: Reserved
>>> +     */
>>> +    if (core_dts_margin >= 0x8000 && core_dts_margin <= 0x81ff)
>>> +        return -EIO;
>>> +
>>> +    core_dts_margin = ten_dot_six_to_millidegree(core_dts_margin);
>>> +
>>> +    /* Note that the tjmax should be available before calling it */
>>> +    priv->temp.core[core_index].value = priv->temp.tjmax.value +
>>> +                        core_dts_margin;
>>> +
>>> +    peci_hwmon_mark_updated(&priv->temp.core[core_index]);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int cputemp_read_string(struct device *dev,
>>> +                   enum hwmon_sensor_types type,
>>> +                   u32 attr, int channel, const char **str)
>>> +{
>>> +    if (attr != hwmon_temp_label)
>>> +        return -EOPNOTSUPP;
>>> +
>>> +    *str = cputemp_label[channel];
>>> +    return 0;
>>> +}
>>> +
>>> +static int cputemp_read(struct device *dev,
>>> +            enum hwmon_sensor_types type,
>>> +            u32 attr, int channel, long *val)
>>> +{
>>> +    struct peci_cputemp *priv = dev_get_drvdata(dev);
>>> +    int rc, core_index;
>>> +
>>> +    if (channel >= CPUTEMP_CHANNEL_NUMS ||
>>> +        !(priv->temp_config[channel] & BIT(attr)))
>>> +        return -EOPNOTSUPP;
>>> +
>>> +    rc = get_temp_targets(priv);
>>> +    if (rc)
>>> +        return rc;
>>> +
>>> +    switch (attr) {
>>> +    case hwmon_temp_input:
>>> +        switch (channel) {
>>> +        case channel_die:
>>> +            rc = get_die_temp(priv);
>>> +            if (rc)
>>> +                break;
>>> +
>>> +            *val = priv->temp.die.value;
>>> +            break;
>>> +        case channel_tcontrol:
>>> +            *val = priv->temp.tcontrol.value;
>>> +            break;
>>> +        case channel_tthrottle:
>>> +            *val = priv->temp.tthrottle.value;
>>> +            break;
>>> +        case channel_tjmax:
>>> +            *val = priv->temp.tjmax.value;
>>> +            break;
>>> +        default:
>>> +            core_index = channel - DEFAULT_CHANNEL_NUMS;
>>> +            rc = get_core_temp(priv, core_index);
>>> +            if (rc)
>>> +                break;
>>> +
>>> +            *val = priv->temp.core[core_index].value;
>>> +            break;
>>> +        }
>>> +        break;
>>> +    case hwmon_temp_max:
>>> +        *val = priv->temp.tcontrol.value;
>>> +        break;
>>> +    case hwmon_temp_crit:
>>> +        *val = priv->temp.tjmax.value;
>>> +        break;
>>> +    case hwmon_temp_crit_hyst:
>>> +        *val = priv->temp.tjmax.value - priv->temp.tcontrol.value;
>>> +        break;
>>> +    default:
>>> +        rc = -EOPNOTSUPP;
>>> +        break;
>>> +    }
>>> +
>>> +    return rc;
>>> +}
>>> +
>>> +static umode_t cputemp_is_visible(const void *data,
>>> +                  enum hwmon_sensor_types type,
>>> +                  u32 attr, int channel)
>>> +{
>>> +    const struct peci_cputemp *priv = data;
>>> +
>>> +    if (priv->temp_config[channel] & BIT(attr))
>>> +        if (channel < DEFAULT_CHANNEL_NUMS ||
>>> +            (channel >= DEFAULT_CHANNEL_NUMS &&
>>> +             (priv->core_mask & BIT(channel - DEFAULT_CHANNEL_NUMS))))
>>> +            return 0444;
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static const struct hwmon_ops cputemp_ops = {
>>> +    .is_visible = cputemp_is_visible,
>>> +    .read_string = cputemp_read_string,
>>> +    .read = cputemp_read,
>>> +};
>>> +
>>> +static int check_resolved_cores(struct peci_cputemp *priv)
>>> +{
>>> +    struct peci_rd_pci_cfg_local_msg msg;
>>> +    int rc;
>>> +
>>> +    /* Get the RESOLVED_CORES register value */
>>> +    msg.addr = priv->addr;
>>> +    msg.bus = REG_RESOLVED_CORES_BUS;
>>> +    msg.device = REG_RESOLVED_CORES_DEVICE;
>>> +    msg.function = REG_RESOLVED_CORES_FUNCTION;
>>> +    msg.reg = REG_RESOLVED_CORES_OFFSET;
>>> +    msg.rx_len = 4;
>>> +
>>> +    rc = peci_command(priv->client->adapter, PECI_CMD_RD_PCI_CFG_LOCAL,
>>> +              &msg);
>>> +    if (rc)
>>> +        return rc;
>>> +
>>> +    priv->core_mask = le32_to_cpup((__le32 *)msg.pci_config);
>>> +    if (!priv->core_mask)
>>> +        return -EAGAIN;
>>> +
>>> +    dev_dbg(priv->dev, "Scanned resolved cores: 0x%x\n", 
>>> priv->core_mask);
>>> +    return 0;
>>> +}
>>> +
>>> +static int create_core_temp_info(struct peci_cputemp *priv)
>>> +{
>>> +    int rc, i;
>>> +
>>> +    rc = check_resolved_cores(priv);
>>> +    if (rc)
>>> +        return rc;
>>> +
>>> +    for (i = 0; i < priv->gen_info->core_max; i++)
>>> +        if (priv->core_mask & BIT(i))
>>> +            while (i + DEFAULT_CHANNEL_NUMS >= priv->config_idx)
>>> +                priv->temp_config[priv->config_idx++] =
>>> +                    config_table[channel_core];
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int peci_cputemp_probe(struct peci_client *client)
>>> +{
>>> +    struct device *dev = &client->dev;
>>> +    struct peci_cputemp *priv;
>>> +    struct device *hwmon_dev;
>>> +    int rc;
>>> +
>>> +    if ((client->adapter->cmd_mask &
>>> +        (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) !=
>>> +        (BIT(PECI_CMD_GET_TEMP) | 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->client = client;
>>> +    priv->dev = dev;
>>> +    priv->addr = client->addr;
>>> +    priv->cpu_no = priv->addr - PECI_BASE_ADDR;
>>> +
>>> +    snprintf(priv->name, PECI_NAME_SIZE, "peci_cputemp.cpu%d",
>>> +         priv->cpu_no);
>>> +
>>> +    rc = peci_hwmon_get_cpu_gen_info(client->adapter, priv->addr,
>>> +                     &priv->gen_info);
>>> +    if (rc)
>>> +        return rc;
>>> +
>>> +    priv->temp_config[priv->config_idx++] = config_table[channel_die];
>>> +    priv->temp_config[priv->config_idx++] = 
>>> config_table[channel_tcontrol];
>>> +    priv->temp_config[priv->config_idx++] = 
>>> config_table[channel_tthrottle];
>>> +    priv->temp_config[priv->config_idx++] = 
>>> config_table[channel_tjmax];
>>> +
>>> +    rc = create_core_temp_info(priv);
>>> +    if (rc)
>>> +        dev_dbg(dev, "Skipped creating core temp info\n");
>>> +
>>> +    priv->chip.ops = &cputemp_ops;
>>> +    priv->chip.info = priv->info;
>>> +
>>> +    priv->info[0] = &priv->temp_info;
>>> +
>>> +    priv->temp_info.type = hwmon_temp;
>>> +    priv->temp_info.config = priv->temp_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 of_device_id peci_cputemp_of_table[] = {
>>> +    { .compatible = "intel,peci-cputemp" },
>>> +    { }
>>> +};
>>> +MODULE_DEVICE_TABLE(of, peci_cputemp_of_table);
>>> +
>>> +static const struct peci_device_id peci_cputemp_ids[] = {
>>> +    { "peci-cputemp", 0, },
>>> +    { }
>>> +};
>>> +MODULE_DEVICE_TABLE(peci, peci_cputemp_ids);
>>> +
>>> +static struct peci_driver peci_cputemp_driver = {
>>> +    .probe    = peci_cputemp_probe,
>>> +    .id_table = peci_cputemp_ids,
>>> +    .driver   = {
>>> +            .name           = "peci-cputemp",
>>> +            .of_match_table = of_match_ptr(peci_cputemp_of_table),
>>> +    },
>>> +};
>>> +module_peci_driver(peci_cputemp_driver);
>>> +
>>> +MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
>>> +MODULE_DESCRIPTION("PECI cputemp driver");
>>> +MODULE_LICENSE("GPL v2");
>>> diff --git a/drivers/hwmon/peci-hwmon.c b/drivers/hwmon/peci-hwmon.c
>>> new file mode 100644
>>> index 000000000000..e137e301eb97
>>> --- /dev/null
>>> +++ b/drivers/hwmon/peci-hwmon.c
>>> @@ -0,0 +1,124 @@
>>> +// SPDX-License-Identifier: GPL-2.0
>>> +// Copyright (c) 2018 Intel Corporation
>>> +
>>> +#include <linux/bitfield.h>
>>> +#include <linux/module.h>
>>> +#include <linux/peci.h>
>>> +
>>> +#include "peci-hwmon.h"
>>> +
>>> +#if IS_ENABLED(CONFIG_X86)
>>> +#include <asm/intel-family.h>
>>> +#else
>>> +#define INTEL_FAM6_HASWELL_X   0x3F
>>> +#define INTEL_FAM6_BROADWELL_X 0x4F
>>> +#define INTEL_FAM6_SKYLAKE_X   0x55
>>> +#endif
>>
>> The entire code is very Intel specific. Why this #if instead of
>> making the driver dependent on X86 ?
>>
> 
> This code will be running on ARM kernel at this moment with a purpose
> of monitoring remote x86 CPUs through PECI connection, so it's limited
> on x86 build environment.
> 

I meant, it isn't limited on x86 build environment.

>>> +
>>> +#define LOWER_NIBBLE_MASK      GENMASK(3, 0)
>>> +#define UPPER_NIBBLE_MASK      GENMASK(7, 4)
>>> +
>>> +#define CPU_ID_MODEL_MASK      GENMASK(7, 4)
>>> +#define CPU_ID_FAMILY_MASK     GENMASK(11, 8)
>>> +#define CPU_ID_EXT_MODEL_MASK  GENMASK(19, 16)
>>> +#define CPU_ID_EXT_FAMILY_MASK GENMASK(27, 20)
>>> +
>>> +enum cpu_gens {
>>> +    CPU_GEN_HSX = 0, /* Haswell Xeon */
>>> +    CPU_GEN_BRX,     /* Broadwell Xeon */
>>> +    CPU_GEN_SKX,     /* Skylake Xeon */
>>> +};
>>> +
>>> +static const struct cpu_gen_info cpu_gen_info_table[] = {
>>> +    [CPU_GEN_HSX] = {
>>> +        .family        = 6, /* Family code */
>>> +        .model         = INTEL_FAM6_HASWELL_X,
>>> +        .core_max      = CORE_MAX_ON_HSX,
>>> +        .chan_rank_max = CHAN_RANK_MAX_ON_HSX,
>>> +        .dimm_idx_max  = DIMM_IDX_MAX_ON_HSX },
>>> +    [CPU_GEN_BRX] = {
>>> +        .family        = 6, /* Family code */
>>> +        .model         = INTEL_FAM6_BROADWELL_X,
>>> +        .core_max      = CORE_MAX_ON_BDX,
>>> +        .chan_rank_max = CHAN_RANK_MAX_ON_BDX,
>>> +        .dimm_idx_max  = DIMM_IDX_MAX_ON_BDX },
>>> +    [CPU_GEN_SKX] = {
>>> +        .family        = 6, /* Family code */
>>> +        .model         = INTEL_FAM6_SKYLAKE_X,
>>> +        .core_max      = CORE_MAX_ON_SKX,
>>> +        .chan_rank_max = CHAN_RANK_MAX_ON_SKX,
>>> +        .dimm_idx_max  = DIMM_IDX_MAX_ON_SKX },
>>> +};
>>> +
>>> +int peci_hwmon_get_cpu_gen_info(struct peci_adapter *adapter, u8 addr,
>>> +                const struct cpu_gen_info **gen_info)
>>> +{
>>> +    u32 cpu_id;
>>> +    int i, rc;
>>> +
>>> +    rc = peci_get_cpu_id(adapter, addr, &cpu_id);
>>> +    if (rc)
>>> +        return rc;
>>> +
>>> +    for (i = 0; i < ARRAY_SIZE(cpu_gen_info_table); i++) {
>>> +        if (FIELD_GET(CPU_ID_FAMILY_MASK, cpu_id) +
>>> +            FIELD_GET(CPU_ID_EXT_FAMILY_MASK, cpu_id) ==
>>> +                cpu_gen_info_table[i].family &&
>>> +            FIELD_GET(CPU_ID_MODEL_MASK, cpu_id) ==
>>> +            FIELD_GET(LOWER_NIBBLE_MASK,
>>> +                  cpu_gen_info_table[i].model) &&
>>> +            FIELD_GET(CPU_ID_EXT_MODEL_MASK, cpu_id) ==
>>> +            FIELD_GET(UPPER_NIBBLE_MASK,
>>> +                  cpu_gen_info_table[i].model)) {
>>> +            break;
>>> +        }
>>> +    }
>>> +
>>> +    if (i >= ARRAY_SIZE(cpu_gen_info_table))
>>> +        return -ENODEV;
>>> +
>>> +    *gen_info = &cpu_gen_info_table[i];
>>> +
>>> +    return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(peci_hwmon_get_cpu_gen_info);
>>> +
>>> +bool peci_hwmon_need_update(struct temp_data *temp)
>>> +{
>>> +    if (temp->valid &&
>>> +        time_before(jiffies, temp->last_updated + UPDATE_INTERVAL))
>>> +        return false;
>>> +
>>> +    return true;
>>> +}
>>> +EXPORT_SYMBOL_GPL(peci_hwmon_need_update);
>>> +
>>> +void peci_hwmon_mark_updated(struct temp_data *temp)
>>> +{
>>> +    temp->valid = 1;
>>> +    temp->last_updated = jiffies;
>>> +}
>>> +EXPORT_SYMBOL_GPL(peci_hwmon_mark_updated);
>>> +
>>> +int peci_hwmon_rd_pkg_cfg_cmd(struct peci_adapter *adapter, u8 addr,
>>> +                  u8 mbx_idx, u16 param, u8 *data)
>>> +{
>>> +    struct peci_rd_pkg_cfg_msg msg;
>>> +    int rc;
>>> +
>>> +    msg.addr = addr;
>>> +    msg.index = mbx_idx;
>>> +    msg.param = param;
>>> +    msg.rx_len = 4;
>>> +
>>> +    rc = peci_command(adapter, PECI_CMD_RD_PKG_CFG, &msg);
>>> +    if (!rc)
>>> +        memcpy(data, msg.pkg_config, 4);
>>> +
>>> +    return rc;
>>> +}
>>> +EXPORT_SYMBOL_GPL(peci_hwmon_rd_pkg_cfg_cmd);
>>> +
>>> +MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
>>> +MODULE_DESCRIPTION("PECI hwmon module");
>>> +MODULE_LICENSE("GPL v2");
>>> diff --git a/drivers/hwmon/peci-hwmon.h b/drivers/hwmon/peci-hwmon.h
>>> new file mode 100644
>>> index 000000000000..0896c27f33d8
>>> --- /dev/null
>>> +++ b/drivers/hwmon/peci-hwmon.h
>>> @@ -0,0 +1,51 @@
>>> +/* SPDX-License-Identifier: GPL-2.0 */
>>> +/* Copyright (c) 2018 Intel Corporation */
>>> +
>>> +#ifndef __PECI_HWMON_H
>>> +#define __PECI_HWMON_H
>>> +
>>> +#include <linux/peci.h>
>>> +
>>> +#define TEMP_TYPE_PECI         6 /* Sensor type 6: Intel PECI */
>>> +
>>> +#define UPDATE_INTERVAL        HZ
>>> +
>>> +struct temp_data {
>>> +    uint  valid;
>>> +    s32   value;
>>> +    ulong last_updated;
>>> +};
>>> +
>>> +struct cpu_gen_info {
>>> +    u16  family;
>>> +    u8   model;
>>> +    uint core_max;
>>> +    uint chan_rank_max;
>>> +    uint dimm_idx_max;
>>> +};
>>> +
>>> +#define CORE_MAX_ON_HSX        18 /* Max number of cores on Haswell */
>>> +#define CHAN_RANK_MAX_ON_HSX   8  /* Max number of channel ranks on 
>>> Haswell */
>>> +#define DIMM_IDX_MAX_ON_HSX    3  /* Max DIMM index per channel on 
>>> Haswell */
>>> +
>>> +#define CORE_MAX_ON_BDX        24 /* Max number of cores on 
>>> Broadwell */
>>> +#define CHAN_RANK_MAX_ON_BDX   4  /* Max number of channel ranks on 
>>> Broadwell */
>>> +#define DIMM_IDX_MAX_ON_BDX    3  /* Max DIMM index per channel on 
>>> Broadwell */
>>> +
>>> +#define CORE_MAX_ON_SKX        28 /* Max number of cores on Skylake */
>>> +#define CHAN_RANK_MAX_ON_SKX   6  /* Max number of channel ranks on 
>>> Skylake */
>>> +#define DIMM_IDX_MAX_ON_SKX    2  /* Max DIMM index per channel on 
>>> Skylake */
>>> +
>>> +#define CORE_NUMS_MAX          CORE_MAX_ON_SKX
>>> +#define CHAN_RANK_MAX          CHAN_RANK_MAX_ON_HSX
>>> +#define DIMM_IDX_MAX           DIMM_IDX_MAX_ON_HSX
>>> +#define DIMM_NUMS_MAX          (CHAN_RANK_MAX * DIMM_IDX_MAX)
>>> +
>>> +int  peci_hwmon_get_cpu_gen_info(struct peci_adapter *adapter, u8 addr,
>>> +                 const struct cpu_gen_info **gen_info);
>>> +bool peci_hwmon_need_update(struct temp_data *temp);
>>> +void peci_hwmon_mark_updated(struct temp_data *temp);
>>> +int  peci_hwmon_rd_pkg_cfg_cmd(struct peci_adapter *adapter, u8 addr,
>>> +                   u8 mbx_idx, u16 param, u8 *data);
>>> +
>>> +#endif /* __PECI_HWMON_H */
>>> -- 
>>> 2.17.0
>>>

  reply	other threads:[~2018-05-21 20:52 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-05-21 19:59 [v4 09/11] drivers/hwmon: Add PECI cputemp driver Jae Hyun Yoo
2018-05-21 20:42 ` Guenter Roeck
2018-05-21 20:50   ` Jae Hyun Yoo
2018-05-21 20:52     ` Jae Hyun Yoo [this message]
2018-05-21 21:01     ` Andrew Lunn
2018-05-21 21:06       ` Guenter Roeck
2018-05-21 21:36         ` Jae Hyun Yoo
2018-05-21 21:04     ` Guenter Roeck
2018-05-21 21:24       ` Jae Hyun Yoo

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=759ab5b1-e93b-c536-b567-42622d91baca@linux.intel.com \
    --to=jae.hyun.yoo@linux.intel.com \
    --cc=alan@linux.intel.com \
    --cc=andrew@aj.id.au \
    --cc=andrew@lunn.ch \
    --cc=andriy.shevchenko@linux.intel.com \
    --cc=arnd@arndb.de \
    --cc=jason.m.bills@linux.intel.com \
    --cc=jdelvare@suse.com \
    --cc=joel@jms.id.au \
    --cc=linux-hwmon@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux@roeck-us.net \
    --cc=miguel.ojeda.sandonis@gmail.com \
    --cc=stef.van.os@prodrive-technologies.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.