diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0010-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch')
-rw-r--r-- | meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0010-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch | 4392 |
1 files changed, 4392 insertions, 0 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0010-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0010-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch new file mode 100644 index 000000000..3cd1d9e84 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0010-Update-PECI-drivers-to-sync-with-linux-upstreaming-v.patch @@ -0,0 +1,4392 @@ +From ce7a88017fb2124100c4e5481a205034f34da23c Mon Sep 17 00:00:00 2001 +From: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +Date: Mon, 7 Jan 2019 09:56:10 -0800 +Subject: [PATCH] Update PECI drivers to sync with linux upstreaming version + +Upstreaming is in holding. It's for adding DTS sensor with PECI +subsystem code update. + +Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com> +--- + Documentation/hwmon/peci-cputemp | 34 +- + drivers/hwmon/Kconfig | 4 +- + drivers/hwmon/peci-cputemp.c | 156 ++++-- + drivers/hwmon/peci-dimmtemp.c | 69 +-- + drivers/hwmon/peci-hwmon.h | 9 +- + drivers/mfd/Kconfig | 5 +- + drivers/mfd/intel-peci-client.c | 43 +- + drivers/peci/Kconfig | 35 +- + drivers/peci/Makefile | 6 +- + drivers/peci/busses/Kconfig | 19 + + drivers/peci/busses/Makefile | 6 + + drivers/peci/busses/peci-aspeed.c | 494 +++++++++++++++++++ + drivers/peci/peci-aspeed.c | 505 ------------------- + drivers/peci/peci-core.c | 889 ++++++++++++++++++---------------- + drivers/peci/peci-dev.c | 340 +++++++++++++ + include/linux/mfd/intel-peci-client.h | 6 +- + include/linux/peci.h | 30 +- + include/uapi/linux/peci-ioctl.h | 394 ++++++++------- + 18 files changed, 1805 insertions(+), 1239 deletions(-) + create mode 100644 drivers/peci/busses/Kconfig + create mode 100644 drivers/peci/busses/Makefile + create mode 100644 drivers/peci/busses/peci-aspeed.c + delete mode 100644 drivers/peci/peci-aspeed.c + create mode 100644 drivers/peci/peci-dev.c + +diff --git a/Documentation/hwmon/peci-cputemp b/Documentation/hwmon/peci-cputemp +index 821a9258f2e6..a3a3e465c888 100644 +--- a/Documentation/hwmon/peci-cputemp ++++ b/Documentation/hwmon/peci-cputemp +@@ -51,28 +51,38 @@ temp1_crit Provides shutdown temperature of the CPU package which + temp1_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of + the CPU package. + +-temp2_label "Tcontrol" +-temp2_input Provides current Tcontrol temperature of the CPU ++temp2_label "DTS" ++temp2_input Provides current DTS temperature of the CPU package. ++temp2_max Provides thermal control temperature of the CPU package ++ which is also known as Tcontrol. ++temp2_crit Provides shutdown temperature of the CPU package which ++ is also known as the maximum processor junction ++ temperature, Tjmax or Tprochot. ++temp2_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of ++ the CPU package. ++ ++temp3_label "Tcontrol" ++temp3_input Provides current Tcontrol temperature of the CPU + package which is also known as Fan Temperature target. + Indicates the relative value from thermal monitor trip + temperature at which fans should be engaged. +-temp2_crit Provides Tcontrol critical value of the CPU package ++temp3_crit Provides Tcontrol critical value of the CPU package + which is same to Tjmax. + +-temp3_label "Tthrottle" +-temp3_input Provides current Tthrottle temperature of the CPU ++temp4_label "Tthrottle" ++temp4_input Provides current Tthrottle temperature of the CPU + package. Used for throttling temperature. If this value + is allowed and lower than Tjmax - the throttle will + occur and reported at lower than Tjmax. + +-temp4_label "Tjmax" +-temp4_input Provides the maximum junction temperature, Tjmax of the ++temp5_label "Tjmax" ++temp5_input Provides the maximum junction temperature, Tjmax of the + CPU package. + +-temp[5-*]_label Provides string "Core X", where X is resolved core ++temp[6-*]_label Provides string "Core X", where X is resolved core + number. +-temp[5-*]_input Provides current temperature of each core. +-temp[5-*]_max Provides thermal control temperature of the core. +-temp[5-*]_crit Provides shutdown temperature of the core. +-temp[5-*]_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of ++temp[6-*]_input Provides current temperature of each core. ++temp[6-*]_max Provides thermal control temperature of the core. ++temp[6-*]_crit Provides shutdown temperature of the core. ++temp[6-*]_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of + the core. +diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig +index 9e118d700b48..efe67f7faed3 100644 +--- a/drivers/hwmon/Kconfig ++++ b/drivers/hwmon/Kconfig +@@ -1319,7 +1319,7 @@ config SENSORS_PECI_CPUTEMP + 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 ++ This driver can also be built as a module. If so, the module + will be called peci-cputemp. + + config SENSORS_PECI_DIMMTEMP +@@ -1333,7 +1333,7 @@ config SENSORS_PECI_DIMMTEMP + Suite via the processor PECI client. + Check Documentation/hwmon/peci-dimmtemp for details. + +- This driver can also be built as a module. If so, the module ++ This driver can also be built as a module. If so, the module + will be called peci-dimmtemp. + + source drivers/hwmon/pmbus/Kconfig +diff --git a/drivers/hwmon/peci-cputemp.c b/drivers/hwmon/peci-cputemp.c +index 11880c86a854..30ba1638e358 100644 +--- a/drivers/hwmon/peci-cputemp.c ++++ b/drivers/hwmon/peci-cputemp.c +@@ -1,5 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0 +-// Copyright (c) 2018 Intel Corporation ++// Copyright (c) 2018-2019 Intel Corporation + + #include <linux/hwmon.h> + #include <linux/jiffies.h> +@@ -9,7 +9,7 @@ + #include <linux/platform_device.h> + #include "peci-hwmon.h" + +-#define DEFAULT_CHANNEL_NUMS 4 ++#define DEFAULT_CHANNEL_NUMS 5 + #define CORETEMP_CHANNEL_NUMS CORE_NUMS_MAX + #define CPUTEMP_CHANNEL_NUMS (DEFAULT_CHANNEL_NUMS + CORETEMP_CHANNEL_NUMS) + +@@ -21,6 +21,7 @@ + + struct temp_group { + struct temp_data die; ++ struct temp_data dts; + struct temp_data tcontrol; + struct temp_data tthrottle; + struct temp_data tjmax; +@@ -43,6 +44,7 @@ struct peci_cputemp { + + enum cputemp_channels { + channel_die, ++ channel_dts, + channel_tcontrol, + channel_tthrottle, + channel_tjmax, +@@ -54,6 +56,10 @@ static const u32 config_table[DEFAULT_CHANNEL_NUMS + 1] = { + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + ++ /* DTS margin */ ++ 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, + +@@ -70,6 +76,7 @@ static const u32 config_table[DEFAULT_CHANNEL_NUMS + 1] = { + + static const char *cputemp_label[CPUTEMP_CHANNEL_NUMS] = { + "Die", ++ "DTS", + "Tcontrol", + "Tthrottle", + "Tjmax", +@@ -92,19 +99,20 @@ static int get_temp_targets(struct peci_cputemp *priv) + s32 tthrottle_offset; + s32 tcontrol_margin; + u8 pkg_cfg[4]; +- int rc; ++ int ret; + +- /** ++ /* + * Just use only the tcontrol marker to determine if target values need + * update. + */ + if (!peci_temp_need_update(&priv->temp.tcontrol)) + return 0; + +- rc = peci_client_read_package_config(priv->mgr, +- MBX_INDEX_TEMP_TARGET, 0, pkg_cfg); +- if (rc) +- return rc; ++ ret = peci_client_read_package_config(priv->mgr, ++ PECI_MBX_INDEX_TEMP_TARGET, 0, ++ pkg_cfg); ++ if (ret) ++ return ret; + + priv->temp.tjmax.value = pkg_cfg[2] * 1000; + +@@ -123,17 +131,16 @@ static int get_temp_targets(struct peci_cputemp *priv) + static int get_die_temp(struct peci_cputemp *priv) + { + struct peci_get_temp_msg msg; +- int rc; ++ int ret; + + if (!peci_temp_need_update(&priv->temp.die)) + return 0; + + msg.addr = priv->mgr->client->addr; + +- rc = peci_command(priv->mgr->client->adapter, PECI_CMD_GET_TEMP, +- &msg); +- if (rc) +- return rc; ++ ret = peci_command(priv->mgr->client->adapter, PECI_CMD_GET_TEMP, &msg); ++ if (ret) ++ return ret; + + /* Note that the tjmax should be available before calling it */ + priv->temp.die.value = priv->temp.tjmax.value + +@@ -144,24 +151,67 @@ static int get_die_temp(struct peci_cputemp *priv) + return 0; + } + ++static int get_dts(struct peci_cputemp *priv) ++{ ++ struct peci_rd_pkg_cfg_msg msg; ++ s32 dts_margin; ++ int ret; ++ ++ if (!peci_temp_need_update(&priv->temp.dts)) ++ return 0; ++ ++ msg.addr = priv->mgr->client->addr; ++ msg.index = PECI_MBX_INDEX_DTS_MARGIN; ++ msg.param = 0; ++ msg.rx_len = 4; ++ ++ ret = peci_command(priv->mgr->client->adapter, PECI_CMD_RD_PKG_CFG, ++ &msg); ++ if (ret) ++ return ret; ++ ++ dts_margin = (msg.pkg_config[1] << 8) | msg.pkg_config[0]; ++ ++ /** ++ * Processors return a value of 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 (dts_margin >= 0x8000 && dts_margin <= 0x81ff) ++ return -EIO; ++ ++ dts_margin = ten_dot_six_to_millidegree(dts_margin); ++ ++ /* Note that the tcontrol should be available before calling it */ ++ priv->temp.dts.value = priv->temp.tcontrol.value - dts_margin; ++ ++ peci_temp_mark_updated(&priv->temp.dts); ++ ++ return 0; ++} ++ + static int get_core_temp(struct peci_cputemp *priv, int core_index) + { + s32 core_dts_margin; + u8 pkg_cfg[4]; +- int rc; ++ int ret; + + if (!peci_temp_need_update(&priv->temp.core[core_index])) + return 0; + +- rc = peci_client_read_package_config(priv->mgr, +- MBX_INDEX_PER_CORE_DTS_TEMP, +- core_index, pkg_cfg); +- if (rc) +- return rc; ++ ret = peci_client_read_package_config(priv->mgr, ++ PECI_MBX_INDEX_PER_CORE_DTS_TEMP, ++ core_index, pkg_cfg); ++ if (ret) ++ return ret; + + 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: +@@ -192,6 +242,7 @@ static int cputemp_read_string(struct device *dev, + return -EOPNOTSUPP; + + *str = cputemp_label[channel]; ++ + return 0; + } + +@@ -200,26 +251,33 @@ static int cputemp_read(struct device *dev, + u32 attr, int channel, long *val) + { + struct peci_cputemp *priv = dev_get_drvdata(dev); +- int rc, core_index; ++ int ret, core_index; + + if (channel >= CPUTEMP_CHANNEL_NUMS || + !(priv->temp_config[channel] & BIT(attr))) + return -EOPNOTSUPP; + +- rc = get_temp_targets(priv); +- if (rc) +- return rc; ++ ret = get_temp_targets(priv); ++ if (ret) ++ return ret; + + switch (attr) { + case hwmon_temp_input: + switch (channel) { + case channel_die: +- rc = get_die_temp(priv); +- if (rc) ++ ret = get_die_temp(priv); ++ if (ret) + break; + + *val = priv->temp.die.value; + break; ++ case channel_dts: ++ ret = get_dts(priv); ++ if (ret) ++ break; ++ ++ *val = priv->temp.dts.value; ++ break; + case channel_tcontrol: + *val = priv->temp.tcontrol.value; + break; +@@ -231,8 +289,8 @@ static int cputemp_read(struct device *dev, + break; + default: + core_index = channel - DEFAULT_CHANNEL_NUMS; +- rc = get_core_temp(priv, core_index); +- if (rc) ++ ret = get_core_temp(priv, core_index); ++ if (ret) + break; + + *val = priv->temp.core[core_index].value; +@@ -249,11 +307,11 @@ static int cputemp_read(struct device *dev, + *val = priv->temp.tjmax.value - priv->temp.tcontrol.value; + break; + default: +- rc = -EOPNOTSUPP; ++ ret = -EOPNOTSUPP; + break; + } + +- return rc; ++ return ret; + } + + static umode_t cputemp_is_visible(const void *data, +@@ -262,11 +320,11 @@ static umode_t cputemp_is_visible(const void *data, + { + 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; ++ if ((priv->temp_config[channel] & BIT(attr)) && ++ (channel < DEFAULT_CHANNEL_NUMS || ++ (channel >= DEFAULT_CHANNEL_NUMS && ++ (priv->core_mask & BIT(channel - DEFAULT_CHANNEL_NUMS))))) ++ return 0444; + + return 0; + } +@@ -280,7 +338,7 @@ static const struct hwmon_ops cputemp_ops = { + static int check_resolved_cores(struct peci_cputemp *priv) + { + struct peci_rd_pci_cfg_local_msg msg; +- int rc; ++ int ret; + + /* Get the RESOLVED_CORES register value */ + msg.addr = priv->mgr->client->addr; +@@ -290,30 +348,31 @@ static int check_resolved_cores(struct peci_cputemp *priv) + msg.reg = REG_RESOLVED_CORES_OFFSET; + msg.rx_len = 4; + +- rc = peci_command(priv->mgr->client->adapter, +- PECI_CMD_RD_PCI_CFG_LOCAL, &msg); +- if (rc) +- return rc; ++ ret = peci_command(priv->mgr->client->adapter, ++ PECI_CMD_RD_PCI_CFG_LOCAL, &msg); ++ if (ret) ++ return ret; + + 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; ++ int ret, i; + +- rc = check_resolved_cores(priv); +- if (rc) +- return rc; ++ ret = check_resolved_cores(priv); ++ if (ret) ++ return ret; + + for (i = 0; i < priv->gen_info->core_max; i++) + if (priv->core_mask & BIT(i)) +- while (i + DEFAULT_CHANNEL_NUMS >= priv->config_idx) ++ while (priv->config_idx <= i + DEFAULT_CHANNEL_NUMS) + priv->temp_config[priv->config_idx++] = + config_table[channel_core]; + +@@ -326,7 +385,7 @@ static int peci_cputemp_probe(struct platform_device *pdev) + struct device *dev = &pdev->dev; + struct peci_cputemp *priv; + struct device *hwmon_dev; +- int rc; ++ int ret; + + if ((mgr->client->adapter->cmd_mask & + (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) != +@@ -346,12 +405,13 @@ static int peci_cputemp_probe(struct platform_device *pdev) + mgr->client->addr - PECI_BASE_ADDR); + + priv->temp_config[priv->config_idx++] = config_table[channel_die]; ++ priv->temp_config[priv->config_idx++] = config_table[channel_dts]; + 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) ++ ret = create_core_temp_info(priv); ++ if (ret) + dev_dbg(dev, "Skipped creating core temp info\n"); + + priv->chip.ops = &cputemp_ops; +diff --git a/drivers/hwmon/peci-dimmtemp.c b/drivers/hwmon/peci-dimmtemp.c +index 86a45a90805b..e088366fd138 100644 +--- a/drivers/hwmon/peci-dimmtemp.c ++++ b/drivers/hwmon/peci-dimmtemp.c +@@ -1,5 +1,5 @@ + // SPDX-License-Identifier: GPL-2.0 +-// Copyright (c) 2018 Intel Corporation ++// Copyright (c) 2018-2019 Intel Corporation + + #include <linux/hwmon.h> + #include <linux/jiffies.h> +@@ -45,16 +45,16 @@ static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no) + int dimm_order = dimm_no % priv->gen_info->dimm_idx_max; + int chan_rank = dimm_no / priv->gen_info->dimm_idx_max; + u8 cfg_data[4]; +- int rc; ++ int ret; + + if (!peci_temp_need_update(&priv->temp[dimm_no])) + return 0; + +- rc = peci_client_read_package_config(priv->mgr, +- MBX_INDEX_DDR_DIMM_TEMP, +- chan_rank, cfg_data); +- if (rc) +- return rc; ++ ret = peci_client_read_package_config(priv->mgr, ++ PECI_MBX_INDEX_DDR_DIMM_TEMP, ++ chan_rank, cfg_data); ++ if (ret) ++ return ret; + + priv->temp[dimm_no].value = cfg_data[dimm_order] * 1000; + +@@ -77,6 +77,7 @@ static int dimmtemp_read_string(struct device *dev, + chan_rank = channel / dimm_idx_max; + dimm_idx = channel % dimm_idx_max; + *str = dimmtemp_label[chan_rank][dimm_idx]; ++ + return 0; + } + +@@ -84,16 +85,17 @@ static int dimmtemp_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) + { + struct peci_dimmtemp *priv = dev_get_drvdata(dev); +- int rc; ++ int ret; + + if (attr != hwmon_temp_input) + return -EOPNOTSUPP; + +- rc = get_dimm_temp(priv, channel); +- if (rc) +- return rc; ++ ret = get_dimm_temp(priv, channel); ++ if (ret) ++ return ret; + + *val = priv->temp[channel].value; ++ + return 0; + } + +@@ -120,16 +122,16 @@ static int check_populated_dimms(struct peci_dimmtemp *priv) + { + u32 chan_rank_max = priv->gen_info->chan_rank_max; + u32 dimm_idx_max = priv->gen_info->dimm_idx_max; +- int chan_rank, dimm_idx, rc; ++ int chan_rank, dimm_idx, ret; + u8 cfg_data[4]; + + for (chan_rank = 0; chan_rank < chan_rank_max; chan_rank++) { +- rc = peci_client_read_package_config(priv->mgr, +- MBX_INDEX_DDR_DIMM_TEMP, +- chan_rank, cfg_data); +- if (rc) { ++ ret = peci_client_read_package_config(priv->mgr, ++ PECI_MBX_INDEX_DDR_DIMM_TEMP, ++ chan_rank, cfg_data); ++ if (ret) { + priv->dimm_mask = 0; +- return rc; ++ return ret; + } + + for (dimm_idx = 0; dimm_idx < dimm_idx_max; dimm_idx++) +@@ -143,17 +145,18 @@ static int check_populated_dimms(struct peci_dimmtemp *priv) + return -EAGAIN; + + dev_dbg(priv->dev, "Scanned populated DIMMs: 0x%x\n", priv->dimm_mask); ++ + return 0; + } + + static int create_dimm_temp_info(struct peci_dimmtemp *priv) + { +- int rc, i, config_idx, channels; ++ int ret, i, config_idx, channels; + struct device *hwmon_dev; + +- rc = check_populated_dimms(priv); +- if (rc) { +- if (rc == -EAGAIN) { ++ ret = check_populated_dimms(priv); ++ if (ret) { ++ if (ret == -EAGAIN) { + if (priv->retry_count < DIMM_MASK_CHECK_RETRY_MAX) { + queue_delayed_work(priv->work_queue, + &priv->work_handler, +@@ -164,11 +167,11 @@ static int create_dimm_temp_info(struct peci_dimmtemp *priv) + } else { + dev_err(priv->dev, + "Timeout DIMM temp info creation\n"); +- rc = -ETIMEDOUT; ++ ret = -ETIMEDOUT; + } + } + +- return rc; ++ return ret; + } + + channels = priv->gen_info->chan_rank_max * +@@ -192,12 +195,12 @@ static int create_dimm_temp_info(struct peci_dimmtemp *priv) + priv, + &priv->chip, + NULL); +- rc = PTR_ERR_OR_ZERO(hwmon_dev); +- if (!rc) ++ ret = PTR_ERR_OR_ZERO(hwmon_dev); ++ if (!ret) + dev_dbg(priv->dev, "%s: sensor '%s'\n", + dev_name(hwmon_dev), priv->name); + +- return rc; ++ return ret; + } + + static void create_dimm_temp_info_delayed(struct work_struct *work) +@@ -205,10 +208,10 @@ static void create_dimm_temp_info_delayed(struct work_struct *work) + struct delayed_work *dwork = to_delayed_work(work); + struct peci_dimmtemp *priv = container_of(dwork, struct peci_dimmtemp, + work_handler); +- int rc; ++ int ret; + +- rc = create_dimm_temp_info(priv); +- if (rc && rc != -EAGAIN) ++ ret = create_dimm_temp_info(priv); ++ if (ret && ret != -EAGAIN) + dev_dbg(priv->dev, "Failed to create DIMM temp info\n"); + } + +@@ -217,7 +220,7 @@ static int peci_dimmtemp_probe(struct platform_device *pdev) + struct peci_client_manager *mgr = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct peci_dimmtemp *priv; +- int rc; ++ int ret; + + if ((mgr->client->adapter->cmd_mask & + (BIT(PECI_CMD_GET_TEMP) | BIT(PECI_CMD_RD_PKG_CFG))) != +@@ -242,8 +245,8 @@ static int peci_dimmtemp_probe(struct platform_device *pdev) + + INIT_DELAYED_WORK(&priv->work_handler, create_dimm_temp_info_delayed); + +- rc = create_dimm_temp_info(priv); +- if (rc && rc != -EAGAIN) { ++ ret = create_dimm_temp_info(priv); ++ if (ret && ret != -EAGAIN) { + dev_err(dev, "Failed to create DIMM temp info\n"); + goto err_free_wq; + } +@@ -252,7 +255,7 @@ static int peci_dimmtemp_probe(struct platform_device *pdev) + + err_free_wq: + destroy_workqueue(priv->work_queue); +- return rc; ++ return ret; + } + + static int peci_dimmtemp_remove(struct platform_device *pdev) +diff --git a/drivers/hwmon/peci-hwmon.h b/drivers/hwmon/peci-hwmon.h +index 6ca1855a86bb..ce6b470eae63 100644 +--- a/drivers/hwmon/peci-hwmon.h ++++ b/drivers/hwmon/peci-hwmon.h +@@ -1,5 +1,5 @@ + /* SPDX-License-Identifier: GPL-2.0 */ +-/* Copyright (c) 2018 Intel Corporation */ ++/* Copyright (c) 2018-2019 Intel Corporation */ + + #ifndef __PECI_HWMON_H + #define __PECI_HWMON_H +@@ -29,11 +29,8 @@ struct temp_data { + */ + static inline bool peci_temp_need_update(struct temp_data *temp) + { +- if (temp->valid && +- time_before(jiffies, temp->last_updated + UPDATE_INTERVAL)) +- return false; +- +- return true; ++ return !temp->valid || ++ time_after(jiffies, temp->last_updated + UPDATE_INTERVAL); + } + + /** +diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig +index 66b71a6122d6..28a83b354ea8 100644 +--- a/drivers/mfd/Kconfig ++++ b/drivers/mfd/Kconfig +@@ -596,7 +596,7 @@ config MFD_INTEL_MSIC + devices used in Intel Medfield platforms. + + config MFD_INTEL_PECI_CLIENT +- bool "Intel PECI client" ++ tristate "Intel PECI client" + depends on (PECI || COMPILE_TEST) + select MFD_CORE + help +@@ -609,6 +609,9 @@ config MFD_INTEL_PECI_CLIENT + Additional drivers must be enabled in order to use the functionality + of the device. + ++ This driver can also be built as a module. If so, the module ++ will be called intel-peci-client. ++ + config MFD_IPAQ_MICRO + bool "Atmel Micro ASIC (iPAQ h3100/h3600/h3700) Support" + depends on SA1100_H3100 || SA1100_H3600 +diff --git a/drivers/mfd/intel-peci-client.c b/drivers/mfd/intel-peci-client.c +index d53e4f1078ac..d62442438512 100644 +--- a/drivers/mfd/intel-peci-client.c ++++ b/drivers/mfd/intel-peci-client.c +@@ -1,12 +1,12 @@ + // SPDX-License-Identifier: GPL-2.0 +-// Copyright (c) 2018 Intel Corporation ++// Copyright (c) 2018-2019 Intel Corporation + + #include <linux/bitfield.h> + #include <linux/mfd/core.h> + #include <linux/mfd/intel-peci-client.h> + #include <linux/module.h> +-#include <linux/peci.h> + #include <linux/of_device.h> ++#include <linux/peci.h> + + #define CPU_ID_MODEL_MASK GENMASK(7, 4) + #define CPU_ID_FAMILY_MASK GENMASK(11, 8) +@@ -18,12 +18,6 @@ + #define LOWER_BYTE_MASK GENMASK(7, 0) + #define UPPER_BYTE_MASK GENMASK(16, 8) + +-enum cpu_gens { +- CPU_GEN_HSX = 0, /* Haswell Xeon */ +- CPU_GEN_BRX, /* Broadwell Xeon */ +- CPU_GEN_SKX, /* Skylake Xeon */ +-}; +- + static struct mfd_cell peci_functions[] = { + { .name = "peci-cputemp", }, + { .name = "peci-dimmtemp", }, +@@ -31,19 +25,19 @@ static struct mfd_cell peci_functions[] = { + }; + + static const struct cpu_gen_info cpu_gen_info_table[] = { +- [CPU_GEN_HSX] = { ++ { /* Haswell Xeon */ + .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] = { ++ { /* Broadwell Xeon */ + .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] = { ++ { /* Skylake Xeon */ + .family = 6, /* Family code */ + .model = INTEL_FAM6_SKYLAKE_X, + .core_max = CORE_MAX_ON_SKX, +@@ -53,16 +47,17 @@ static const struct cpu_gen_info cpu_gen_info_table[] = { + + static int peci_client_get_cpu_gen_info(struct peci_client_manager *priv) + { ++ struct device *dev = &priv->client->dev; + u32 cpu_id; + u16 family; + u8 model; +- int rc; ++ int ret; + int i; + +- rc = peci_get_cpu_id(priv->client->adapter, priv->client->addr, +- &cpu_id); +- if (rc) +- return rc; ++ ret = peci_get_cpu_id(priv->client->adapter, priv->client->addr, ++ &cpu_id); ++ if (ret) ++ return ret; + + family = FIELD_PREP(LOWER_BYTE_MASK, + FIELD_GET(CPU_ID_FAMILY_MASK, cpu_id)) | +@@ -83,11 +78,11 @@ static int peci_client_get_cpu_gen_info(struct peci_client_manager *priv) + } + + if (!priv->gen_info) { +- dev_err(priv->dev, "Can't support this CPU: 0x%x\n", cpu_id); +- rc = -ENODEV; ++ dev_err(dev, "Can't support this CPU: 0x%x\n", cpu_id); ++ ret = -ENODEV; + } + +- return rc; ++ return ret; + } + + static int peci_client_probe(struct peci_client *client) +@@ -103,31 +98,29 @@ static int peci_client_probe(struct peci_client *client) + + dev_set_drvdata(dev, priv); + priv->client = client; +- priv->dev = dev; + cpu_no = client->addr - PECI_BASE_ADDR; + + ret = peci_client_get_cpu_gen_info(priv); + if (ret) + return ret; + +- ret = devm_mfd_add_devices(priv->dev, cpu_no, peci_functions, ++ ret = devm_mfd_add_devices(dev, cpu_no, peci_functions, + ARRAY_SIZE(peci_functions), NULL, 0, NULL); + if (ret < 0) { +- dev_err(priv->dev, "Failed to register child devices: %d\n", +- ret); ++ dev_err(dev, "Failed to register child devices: %d\n", ret); + return ret; + } + + return 0; + } + +-#ifdef CONFIG_OF ++#if IS_ENABLED(CONFIG_OF) + static const struct of_device_id peci_client_of_table[] = { + { .compatible = "intel,peci-client" }, + { } + }; + MODULE_DEVICE_TABLE(of, peci_client_of_table); +-#endif ++#endif /* CONFIG_OF */ + + static const struct peci_device_id peci_client_ids[] = { + { .name = "peci-client" }, +diff --git a/drivers/peci/Kconfig b/drivers/peci/Kconfig +index 9e9845ebcff4..9752feee2454 100644 +--- a/drivers/peci/Kconfig ++++ b/drivers/peci/Kconfig +@@ -2,10 +2,12 @@ + # Platform Environment Control Interface (PECI) subsystem configuration + # + ++menu "PECI support" ++ + config PECI +- bool "PECI support" +- select RT_MUTEXES ++ tristate "PECI support" + select CRC8 ++ default n + help + The Platform Environment Control Interface (PECI) is a one-wire bus + interface that provides a communication channel from Intel processors +@@ -14,26 +16,23 @@ config PECI + If you want PECI support, you should say Y here and also to the + specific driver for your bus adapter(s) below. + +-if PECI +- +-# +-# PECI hardware bus configuration +-# ++ This support is also available as a module. If so, the module ++ will be called peci-core. + +-menu "PECI Hardware Bus support" ++if PECI + +-config PECI_ASPEED +- tristate "ASPEED PECI support" +- select REGMAP_MMIO +- depends on OF +- depends on ARCH_ASPEED || COMPILE_TEST ++config PECI_CHARDEV ++ tristate "PECI device interface" + help +- Say Y here if you want support for the Platform Environment Control +- Interface (PECI) bus adapter driver on the ASPEED SoCs. ++ Say Y here to use peci-* device files, usually found in the /dev ++ directory on your system. They make it possible to have user-space ++ programs use the PECI bus. + +- This support is also available as a module. If so, the module +- will be called peci-aspeed. ++ This support is also available as a module. If so, the module ++ will be called peci-dev. + +-endmenu ++source "drivers/peci/busses/Kconfig" + + endif # PECI ++ ++endmenu +diff --git a/drivers/peci/Makefile b/drivers/peci/Makefile +index 886285e69765..da8b0a33fa42 100644 +--- a/drivers/peci/Makefile ++++ b/drivers/peci/Makefile +@@ -1,9 +1,11 @@ ++# SPDX-License-Identifier: GPL-2.0 + # +-# Makefile for the PECI core and bus drivers. ++# Makefile for the PECI core drivers. + # + + # Core functionality + obj-$(CONFIG_PECI) += peci-core.o ++obj-$(CONFIG_PECI_CHARDEV) += peci-dev.o + + # Hardware specific bus drivers +-obj-$(CONFIG_PECI_ASPEED) += peci-aspeed.o ++obj-y += busses/ +diff --git a/drivers/peci/busses/Kconfig b/drivers/peci/busses/Kconfig +new file mode 100644 +index 000000000000..a20d470b4250 +--- /dev/null ++++ b/drivers/peci/busses/Kconfig +@@ -0,0 +1,19 @@ ++# ++# PECI hardware bus configuration ++# ++ ++menu "PECI Hardware Bus support" ++ ++config PECI_ASPEED ++ tristate "ASPEED PECI support" ++ depends on ARCH_ASPEED || COMPILE_TEST ++ depends on OF ++ depends on PECI ++ help ++ Say Y here if you want support for the Platform Environment Control ++ Interface (PECI) bus adapter driver on the ASPEED SoCs. ++ ++ This support is also available as a module. If so, the module ++ will be called peci-aspeed. ++ ++endmenu +diff --git a/drivers/peci/busses/Makefile b/drivers/peci/busses/Makefile +new file mode 100644 +index 000000000000..69e31dfaca19 +--- /dev/null ++++ b/drivers/peci/busses/Makefile +@@ -0,0 +1,6 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# ++# Makefile for the PECI hardware bus drivers. ++# ++ ++obj-$(CONFIG_PECI_ASPEED) += peci-aspeed.o +diff --git a/drivers/peci/busses/peci-aspeed.c b/drivers/peci/busses/peci-aspeed.c +new file mode 100644 +index 000000000000..8a0dd40730cc +--- /dev/null ++++ b/drivers/peci/busses/peci-aspeed.c +@@ -0,0 +1,494 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (C) 2012-2017 ASPEED Technology Inc. ++// Copyright (c) 2018-2019 Intel Corporation ++ ++#include <linux/bitfield.h> ++#include <linux/clk.h> ++#include <linux/delay.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/jiffies.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/peci.h> ++#include <linux/platform_device.h> ++#include <linux/reset.h> ++ ++/* ASPEED PECI Registers */ ++/* Control Register */ ++#define ASPEED_PECI_CTRL 0x00 ++#define ASPEED_PECI_CTRL_SAMPLING_MASK GENMASK(19, 16) ++#define ASPEED_PECI_CTRL_READ_MODE_MASK GENMASK(13, 12) ++#define ASPEED_PECI_CTRL_READ_MODE_COUNT BIT(12) ++#define ASPEED_PECI_CTRL_READ_MODE_DBG BIT(13) ++#define ASPEED_PECI_CTRL_CLK_SOURCE_MASK BIT(11) ++#define ASPEED_PECI_CTRL_CLK_DIV_MASK GENMASK(10, 8) ++#define ASPEED_PECI_CTRL_INVERT_OUT BIT(7) ++#define ASPEED_PECI_CTRL_INVERT_IN BIT(6) ++#define ASPEED_PECI_CTRL_BUS_CONTENT_EN BIT(5) ++#define ASPEED_PECI_CTRL_PECI_EN BIT(4) ++#define ASPEED_PECI_CTRL_PECI_CLK_EN BIT(0) ++ ++/* Timing Negotiation Register */ ++#define ASPEED_PECI_TIMING_NEGOTIATION 0x04 ++#define ASPEED_PECI_TIMING_MESSAGE_MASK GENMASK(15, 8) ++#define ASPEED_PECI_TIMING_ADDRESS_MASK GENMASK(7, 0) ++ ++/* Command Register */ ++#define ASPEED_PECI_CMD 0x08 ++#define ASPEED_PECI_CMD_PIN_MON BIT(31) ++#define ASPEED_PECI_CMD_STS_MASK GENMASK(27, 24) ++#define ASPEED_PECI_CMD_IDLE_MASK (ASPEED_PECI_CMD_STS_MASK | \ ++ ASPEED_PECI_CMD_PIN_MON) ++#define ASPEED_PECI_CMD_FIRE BIT(0) ++ ++/* Read/Write Length Register */ ++#define ASPEED_PECI_RW_LENGTH 0x0c ++#define ASPEED_PECI_AW_FCS_EN BIT(31) ++#define ASPEED_PECI_READ_LEN_MASK GENMASK(23, 16) ++#define ASPEED_PECI_WRITE_LEN_MASK GENMASK(15, 8) ++#define ASPEED_PECI_TAGET_ADDR_MASK GENMASK(7, 0) ++ ++/* Expected FCS Data Register */ ++#define ASPEED_PECI_EXP_FCS 0x10 ++#define ASPEED_PECI_EXP_READ_FCS_MASK GENMASK(23, 16) ++#define ASPEED_PECI_EXP_AW_FCS_AUTO_MASK GENMASK(15, 8) ++#define ASPEED_PECI_EXP_WRITE_FCS_MASK GENMASK(7, 0) ++ ++/* Captured FCS Data Register */ ++#define ASPEED_PECI_CAP_FCS 0x14 ++#define ASPEED_PECI_CAP_READ_FCS_MASK GENMASK(23, 16) ++#define ASPEED_PECI_CAP_WRITE_FCS_MASK GENMASK(7, 0) ++ ++/* Interrupt Register */ ++#define ASPEED_PECI_INT_CTRL 0x18 ++#define ASPEED_PECI_TIMING_NEGO_SEL_MASK GENMASK(31, 30) ++#define ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO 0 ++#define ASPEED_PECI_2ND_BIT_OF_ADDR_NEGO 1 ++#define ASPEED_PECI_MESSAGE_NEGO 2 ++#define ASPEED_PECI_INT_MASK GENMASK(4, 0) ++#define ASPEED_PECI_INT_BUS_TIMEOUT BIT(4) ++#define ASPEED_PECI_INT_BUS_CONNECT BIT(3) ++#define ASPEED_PECI_INT_W_FCS_BAD BIT(2) ++#define ASPEED_PECI_INT_W_FCS_ABORT BIT(1) ++#define ASPEED_PECI_INT_CMD_DONE BIT(0) ++ ++/* Interrupt Status Register */ ++#define ASPEED_PECI_INT_STS 0x1c ++#define ASPEED_PECI_INT_TIMING_RESULT_MASK GENMASK(29, 16) ++ /* bits[4..0]: Same bit fields in the 'Interrupt Register' */ ++ ++/* Rx/Tx Data Buffer Registers */ ++#define ASPEED_PECI_W_DATA0 0x20 ++#define ASPEED_PECI_W_DATA1 0x24 ++#define ASPEED_PECI_W_DATA2 0x28 ++#define ASPEED_PECI_W_DATA3 0x2c ++#define ASPEED_PECI_R_DATA0 0x30 ++#define ASPEED_PECI_R_DATA1 0x34 ++#define ASPEED_PECI_R_DATA2 0x38 ++#define ASPEED_PECI_R_DATA3 0x3c ++#define ASPEED_PECI_W_DATA4 0x40 ++#define ASPEED_PECI_W_DATA5 0x44 ++#define ASPEED_PECI_W_DATA6 0x48 ++#define ASPEED_PECI_W_DATA7 0x4c ++#define ASPEED_PECI_R_DATA4 0x50 ++#define ASPEED_PECI_R_DATA5 0x54 ++#define ASPEED_PECI_R_DATA6 0x58 ++#define ASPEED_PECI_R_DATA7 0x5c ++#define ASPEED_PECI_DATA_BUF_SIZE_MAX 32 ++ ++/* Timing Negotiation */ ++#define ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT 8 ++#define ASPEED_PECI_RD_SAMPLING_POINT_MAX 15 ++#define ASPEED_PECI_CLK_DIV_DEFAULT 0 ++#define ASPEED_PECI_CLK_DIV_MAX 7 ++#define ASPEED_PECI_MSG_TIMING_DEFAULT 1 ++#define ASPEED_PECI_MSG_TIMING_MAX 255 ++#define ASPEED_PECI_ADDR_TIMING_DEFAULT 1 ++#define ASPEED_PECI_ADDR_TIMING_MAX 255 ++ ++/* Timeout */ ++#define ASPEED_PECI_IDLE_CHECK_TIMEOUT_USEC 50000 ++#define ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC 10000 ++#define ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT 1000 ++#define ASPEED_PECI_CMD_TIMEOUT_MS_MAX 60000 ++ ++struct aspeed_peci { ++ struct peci_adapter *adapter; ++ struct device *dev; ++ void __iomem *base; ++ struct clk *clk; ++ struct reset_control *rst; ++ int irq; ++ spinlock_t lock; /* to sync completion status handling */ ++ struct completion xfer_complete; ++ u32 status; ++ u32 cmd_timeout_ms; ++}; ++ ++static int aspeed_peci_check_idle(struct aspeed_peci *priv) ++{ ++ ulong timeout = jiffies + usecs_to_jiffies(ASPEED_PECI_IDLE_CHECK_TIMEOUT_USEC); ++ u32 cmd_sts; ++ ++ for (;;) { ++ cmd_sts = readl(priv->base + ASPEED_PECI_CMD); ++ if (!(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK)) ++ break; ++ if (time_after(jiffies, timeout)) { ++ cmd_sts = readl(priv->base + ASPEED_PECI_CMD); ++ break; ++ } ++ usleep_range((ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC >> 2) + 1, ++ ASPEED_PECI_IDLE_CHECK_INTERVAL_USEC); ++ } ++ ++ return !(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK) ? 0 : -ETIMEDOUT; ++} ++ ++static int aspeed_peci_xfer(struct peci_adapter *adapter, ++ struct peci_xfer_msg *msg) ++{ ++ struct aspeed_peci *priv = peci_get_adapdata(adapter); ++ long err, timeout = msecs_to_jiffies(priv->cmd_timeout_ms); ++ u32 peci_head, peci_state, rx_data = 0; ++ ulong flags; ++ int i, ret; ++ uint reg; ++ ++ if (msg->tx_len > ASPEED_PECI_DATA_BUF_SIZE_MAX || ++ msg->rx_len > ASPEED_PECI_DATA_BUF_SIZE_MAX) ++ return -EINVAL; ++ ++ /* Check command sts and bus idle state */ ++ ret = aspeed_peci_check_idle(priv); ++ if (ret) ++ return ret; /* -ETIMEDOUT */ ++ ++ spin_lock_irqsave(&priv->lock, flags); ++ reinit_completion(&priv->xfer_complete); ++ ++ peci_head = FIELD_PREP(ASPEED_PECI_TAGET_ADDR_MASK, msg->addr) | ++ FIELD_PREP(ASPEED_PECI_WRITE_LEN_MASK, msg->tx_len) | ++ FIELD_PREP(ASPEED_PECI_READ_LEN_MASK, msg->rx_len); ++ ++ writel(peci_head, priv->base + ASPEED_PECI_RW_LENGTH); ++ ++ for (i = 0; i < msg->tx_len; i += 4) { ++ reg = i < 16 ? ASPEED_PECI_W_DATA0 + i % 16 : ++ ASPEED_PECI_W_DATA4 + i % 16; ++ writel(le32_to_cpup((__le32 *)&msg->tx_buf[i]), ++ priv->base + reg); ++ } ++ ++ dev_dbg(priv->dev, "HEAD : 0x%08x\n", peci_head); ++ print_hex_dump_debug("TX : ", DUMP_PREFIX_NONE, 16, 1, ++ msg->tx_buf, msg->tx_len, true); ++ ++ priv->status = 0; ++ writel(ASPEED_PECI_CMD_FIRE, priv->base + ASPEED_PECI_CMD); ++ spin_unlock_irqrestore(&priv->lock, flags); ++ ++ err = wait_for_completion_interruptible_timeout(&priv->xfer_complete, ++ timeout); ++ ++ spin_lock_irqsave(&priv->lock, flags); ++ dev_dbg(priv->dev, "INT_STS : 0x%08x\n", priv->status); ++ peci_state = readl(priv->base + ASPEED_PECI_CMD); ++ dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n", ++ FIELD_GET(ASPEED_PECI_CMD_STS_MASK, peci_state)); ++ ++ writel(0, priv->base + ASPEED_PECI_CMD); ++ ++ if (err <= 0 || priv->status != ASPEED_PECI_INT_CMD_DONE) { ++ if (err < 0) { /* -ERESTARTSYS */ ++ ret = (int)err; ++ goto err_irqrestore; ++ } else if (err == 0) { ++ dev_dbg(priv->dev, "Timeout waiting for a response!\n"); ++ ret = -ETIMEDOUT; ++ goto err_irqrestore; ++ } ++ ++ dev_dbg(priv->dev, "No valid response!\n"); ++ ret = -EIO; ++ goto err_irqrestore; ++ } ++ ++ /* ++ * Note that rx_len and rx_buf size can be an odd number. ++ * Byte handling is more efficient. ++ */ ++ for (i = 0; i < msg->rx_len; i++) { ++ u8 byte_offset = i % 4; ++ ++ if (byte_offset == 0) { ++ reg = i < 16 ? ASPEED_PECI_R_DATA0 + i % 16 : ++ ASPEED_PECI_R_DATA4 + i % 16; ++ rx_data = readl(priv->base + reg); ++ } ++ ++ msg->rx_buf[i] = (u8)(rx_data >> (byte_offset << 3)); ++ } ++ ++ print_hex_dump_debug("RX : ", DUMP_PREFIX_NONE, 16, 1, ++ msg->rx_buf, msg->rx_len, true); ++ ++ peci_state = readl(priv->base + ASPEED_PECI_CMD); ++ dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n", ++ FIELD_GET(ASPEED_PECI_CMD_STS_MASK, peci_state)); ++ dev_dbg(priv->dev, "------------------------\n"); ++ ++err_irqrestore: ++ spin_unlock_irqrestore(&priv->lock, flags); ++ return ret; ++} ++ ++static irqreturn_t aspeed_peci_irq_handler(int irq, void *arg) ++{ ++ struct aspeed_peci *priv = arg; ++ u32 status; ++ ++ spin_lock(&priv->lock); ++ status = readl(priv->base + ASPEED_PECI_INT_STS); ++ writel(status, priv->base + ASPEED_PECI_INT_STS); ++ priv->status |= (status & ASPEED_PECI_INT_MASK); ++ ++ /* ++ * In most cases, interrupt bits will be set one by one but also note ++ * that multiple interrupt bits could be set at the same time. ++ */ ++ if (status & ASPEED_PECI_INT_BUS_TIMEOUT) { ++ dev_dbg(priv->dev, "ASPEED_PECI_INT_BUS_TIMEOUT\n"); ++ } ++ ++ if (status & ASPEED_PECI_INT_BUS_CONNECT) { ++ dev_dbg(priv->dev, "ASPEED_PECI_INT_BUS_CONNECT\n"); ++ } ++ ++ if (status & ASPEED_PECI_INT_W_FCS_BAD) { ++ dev_dbg(priv->dev, "ASPEED_PECI_INT_W_FCS_BAD\n"); ++ } ++ ++ if (status & ASPEED_PECI_INT_W_FCS_ABORT) { ++ dev_dbg(priv->dev, "ASPEED_PECI_INT_W_FCS_ABORT\n"); ++ } ++ ++ /* ++ * All commands should be ended up with a ASPEED_PECI_INT_CMD_DONE bit ++ * set even in an error case. ++ */ ++ if (status & ASPEED_PECI_INT_CMD_DONE) { ++ dev_dbg(priv->dev, "ASPEED_PECI_INT_CMD_DONE\n"); ++ complete(&priv->xfer_complete); ++ } ++ ++ spin_unlock(&priv->lock); ++ return IRQ_HANDLED; ++} ++ ++static int aspeed_peci_init_ctrl(struct aspeed_peci *priv) ++{ ++ u32 msg_timing, addr_timing, rd_sampling_point; ++ u32 clk_freq, clk_divisor, clk_div_val = 0; ++ int ret; ++ ++ priv->clk = devm_clk_get(priv->dev, NULL); ++ if (IS_ERR(priv->clk)) { ++ dev_err(priv->dev, "Failed to get clk source.\n"); ++ return PTR_ERR(priv->clk); ++ } ++ ++ ret = clk_prepare_enable(priv->clk); ++ if (ret) { ++ dev_err(priv->dev, "Failed to enable clock.\n"); ++ return ret; ++ } ++ ++ ret = device_property_read_u32(priv->dev, "clock-frequency", &clk_freq); ++ if (ret) { ++ dev_err(priv->dev, ++ "Could not read clock-frequency property.\n"); ++ clk_disable_unprepare(priv->clk); ++ return ret; ++ } ++ ++ clk_divisor = clk_get_rate(priv->clk) / clk_freq; ++ ++ while ((clk_divisor >> 1) && (clk_div_val < ASPEED_PECI_CLK_DIV_MAX)) ++ clk_div_val++; ++ ++ ret = device_property_read_u32(priv->dev, "msg-timing", &msg_timing); ++ if (ret || msg_timing > ASPEED_PECI_MSG_TIMING_MAX) { ++ if (!ret) ++ dev_warn(priv->dev, ++ "Invalid msg-timing : %u, Use default : %u\n", ++ msg_timing, ASPEED_PECI_MSG_TIMING_DEFAULT); ++ msg_timing = ASPEED_PECI_MSG_TIMING_DEFAULT; ++ } ++ ++ ret = device_property_read_u32(priv->dev, "addr-timing", &addr_timing); ++ if (ret || addr_timing > ASPEED_PECI_ADDR_TIMING_MAX) { ++ if (!ret) ++ dev_warn(priv->dev, ++ "Invalid addr-timing : %u, Use default : %u\n", ++ addr_timing, ASPEED_PECI_ADDR_TIMING_DEFAULT); ++ addr_timing = ASPEED_PECI_ADDR_TIMING_DEFAULT; ++ } ++ ++ ret = device_property_read_u32(priv->dev, "rd-sampling-point", ++ &rd_sampling_point); ++ if (ret || rd_sampling_point > ASPEED_PECI_RD_SAMPLING_POINT_MAX) { ++ if (!ret) ++ dev_warn(priv->dev, ++ "Invalid rd-sampling-point : %u. Use default : %u\n", ++ rd_sampling_point, ++ ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT); ++ rd_sampling_point = ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT; ++ } ++ ++ ret = device_property_read_u32(priv->dev, "cmd-timeout-ms", ++ &priv->cmd_timeout_ms); ++ if (ret || priv->cmd_timeout_ms > ASPEED_PECI_CMD_TIMEOUT_MS_MAX || ++ priv->cmd_timeout_ms == 0) { ++ if (!ret) ++ dev_warn(priv->dev, ++ "Invalid cmd-timeout-ms : %u. Use default : %u\n", ++ priv->cmd_timeout_ms, ++ ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT); ++ priv->cmd_timeout_ms = ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT; ++ } ++ ++ writel(FIELD_PREP(ASPEED_PECI_CTRL_CLK_DIV_MASK, ++ ASPEED_PECI_CLK_DIV_DEFAULT) | ++ ASPEED_PECI_CTRL_PECI_CLK_EN, priv->base + ASPEED_PECI_CTRL); ++ ++ /* ++ * Timing negotiation period setting. ++ * The unit of the programmed value is 4 times of PECI clock period. ++ */ ++ writel(FIELD_PREP(ASPEED_PECI_TIMING_MESSAGE_MASK, msg_timing) | ++ FIELD_PREP(ASPEED_PECI_TIMING_ADDRESS_MASK, addr_timing), ++ priv->base + ASPEED_PECI_TIMING_NEGOTIATION); ++ ++ /* Clear interrupts */ ++ writel(readl(priv->base + ASPEED_PECI_INT_STS) | ASPEED_PECI_INT_MASK, ++ priv->base + ASPEED_PECI_INT_STS); ++ ++ /* Set timing negotiation mode and enable interrupts */ ++ writel(FIELD_PREP(ASPEED_PECI_TIMING_NEGO_SEL_MASK, ++ ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO) | ++ ASPEED_PECI_INT_MASK, priv->base + ASPEED_PECI_INT_CTRL); ++ ++ /* Read sampling point and clock speed setting */ ++ writel(FIELD_PREP(ASPEED_PECI_CTRL_SAMPLING_MASK, rd_sampling_point) | ++ FIELD_PREP(ASPEED_PECI_CTRL_CLK_DIV_MASK, clk_div_val) | ++ ASPEED_PECI_CTRL_PECI_EN | ASPEED_PECI_CTRL_PECI_CLK_EN, ++ priv->base + ASPEED_PECI_CTRL); ++ ++ return 0; ++} ++ ++static int aspeed_peci_probe(struct platform_device *pdev) ++{ ++ struct peci_adapter *adapter; ++ struct aspeed_peci *priv; ++ struct resource *res; ++ int ret; ++ ++ adapter = peci_alloc_adapter(&pdev->dev, sizeof(*priv)); ++ if (!adapter) ++ return -ENOMEM; ++ ++ priv = peci_get_adapdata(adapter); ++ priv->adapter = adapter; ++ priv->dev = &pdev->dev; ++ dev_set_drvdata(&pdev->dev, priv); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ priv->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(priv->base)) { ++ ret = PTR_ERR(priv->base); ++ goto err_put_adapter_dev; ++ } ++ ++ priv->irq = platform_get_irq(pdev, 0); ++ if (!priv->irq) { ++ ret = -ENODEV; ++ goto err_put_adapter_dev; ++ } ++ ++ ret = devm_request_irq(&pdev->dev, priv->irq, aspeed_peci_irq_handler, ++ 0, "peci-aspeed-irq", priv); ++ if (ret) ++ goto err_put_adapter_dev; ++ ++ init_completion(&priv->xfer_complete); ++ spin_lock_init(&priv->lock); ++ ++ priv->adapter->owner = THIS_MODULE; ++ priv->adapter->dev.of_node = of_node_get(dev_of_node(priv->dev)); ++ strlcpy(priv->adapter->name, pdev->name, sizeof(priv->adapter->name)); ++ priv->adapter->xfer = aspeed_peci_xfer; ++ priv->adapter->use_dma = false; ++ ++ priv->rst = devm_reset_control_get(&pdev->dev, NULL); ++ if (IS_ERR(priv->rst)) { ++ dev_err(&pdev->dev, ++ "missing or invalid reset controller entry"); ++ ret = PTR_ERR(priv->rst); ++ goto err_put_adapter_dev; ++ } ++ reset_control_deassert(priv->rst); ++ ++ ret = aspeed_peci_init_ctrl(priv); ++ if (ret) ++ goto err_put_adapter_dev; ++ ++ ret = peci_add_adapter(priv->adapter); ++ if (ret) ++ goto err_put_adapter_dev; ++ ++ dev_info(&pdev->dev, "peci bus %d registered, irq %d\n", ++ priv->adapter->nr, priv->irq); ++ ++ return 0; ++ ++err_put_adapter_dev: ++ put_device(&adapter->dev); ++ return ret; ++} ++ ++static int aspeed_peci_remove(struct platform_device *pdev) ++{ ++ struct aspeed_peci *priv = dev_get_drvdata(&pdev->dev); ++ ++ clk_disable_unprepare(priv->clk); ++ reset_control_assert(priv->rst); ++ peci_del_adapter(priv->adapter); ++ of_node_put(priv->adapter->dev.of_node); ++ ++ return 0; ++} ++ ++static const struct of_device_id aspeed_peci_of_table[] = { ++ { .compatible = "aspeed,ast2400-peci", }, ++ { .compatible = "aspeed,ast2500-peci", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, aspeed_peci_of_table); ++ ++static struct platform_driver aspeed_peci_driver = { ++ .probe = aspeed_peci_probe, ++ .remove = aspeed_peci_remove, ++ .driver = { ++ .name = "peci-aspeed", ++ .of_match_table = of_match_ptr(aspeed_peci_of_table), ++ }, ++}; ++module_platform_driver(aspeed_peci_driver); ++ ++MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>"); ++MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>"); ++MODULE_DESCRIPTION("ASPEED PECI driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/peci/peci-aspeed.c b/drivers/peci/peci-aspeed.c +deleted file mode 100644 +index 51cb2563ceb6..000000000000 +--- a/drivers/peci/peci-aspeed.c ++++ /dev/null +@@ -1,505 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-// Copyright (C) 2012-2017 ASPEED Technology Inc. +-// Copyright (c) 2018 Intel Corporation +- +-#include <linux/bitfield.h> +-#include <linux/clk.h> +-#include <linux/interrupt.h> +-#include <linux/jiffies.h> +-#include <linux/module.h> +-#include <linux/of.h> +-#include <linux/peci.h> +-#include <linux/platform_device.h> +-#include <linux/regmap.h> +-#include <linux/reset.h> +- +-/* ASPEED PECI Registers */ +-#define ASPEED_PECI_CTRL 0x00 +-#define ASPEED_PECI_TIMING 0x04 +-#define ASPEED_PECI_CMD 0x08 +-#define ASPEED_PECI_CMD_CTRL 0x0c +-#define ASPEED_PECI_EXP_FCS 0x10 +-#define ASPEED_PECI_CAP_FCS 0x14 +-#define ASPEED_PECI_INT_CTRL 0x18 +-#define ASPEED_PECI_INT_STS 0x1c +-#define ASPEED_PECI_W_DATA0 0x20 +-#define ASPEED_PECI_W_DATA1 0x24 +-#define ASPEED_PECI_W_DATA2 0x28 +-#define ASPEED_PECI_W_DATA3 0x2c +-#define ASPEED_PECI_R_DATA0 0x30 +-#define ASPEED_PECI_R_DATA1 0x34 +-#define ASPEED_PECI_R_DATA2 0x38 +-#define ASPEED_PECI_R_DATA3 0x3c +-#define ASPEED_PECI_W_DATA4 0x40 +-#define ASPEED_PECI_W_DATA5 0x44 +-#define ASPEED_PECI_W_DATA6 0x48 +-#define ASPEED_PECI_W_DATA7 0x4c +-#define ASPEED_PECI_R_DATA4 0x50 +-#define ASPEED_PECI_R_DATA5 0x54 +-#define ASPEED_PECI_R_DATA6 0x58 +-#define ASPEED_PECI_R_DATA7 0x5c +- +-/* ASPEED_PECI_CTRL - 0x00 : Control Register */ +-#define PECI_CTRL_SAMPLING_MASK GENMASK(19, 16) +-#define PECI_CTRL_READ_MODE_MASK GENMASK(13, 12) +-#define PECI_CTRL_READ_MODE_COUNT BIT(12) +-#define PECI_CTRL_READ_MODE_DBG BIT(13) +-#define PECI_CTRL_CLK_SOURCE_MASK BIT(11) +-#define PECI_CTRL_CLK_DIV_MASK GENMASK(10, 8) +-#define PECI_CTRL_INVERT_OUT BIT(7) +-#define PECI_CTRL_INVERT_IN BIT(6) +-#define PECI_CTRL_BUS_CONTENT_EN BIT(5) +-#define PECI_CTRL_PECI_EN BIT(4) +-#define PECI_CTRL_PECI_CLK_EN BIT(0) +- +-/* ASPEED_PECI_TIMING - 0x04 : Timing Negotiation Register */ +-#define PECI_TIMING_MESSAGE_MASK GENMASK(15, 8) +-#define PECI_TIMING_ADDRESS_MASK GENMASK(7, 0) +- +-/* ASPEED_PECI_CMD - 0x08 : Command Register */ +-#define PECI_CMD_PIN_MON BIT(31) +-#define PECI_CMD_STS_MASK GENMASK(27, 24) +-#define PECI_CMD_IDLE_MASK (PECI_CMD_STS_MASK | PECI_CMD_PIN_MON) +-#define PECI_CMD_FIRE BIT(0) +- +-/* ASPEED_PECI_LEN - 0x0C : Read/Write Length Register */ +-#define PECI_AW_FCS_EN BIT(31) +-#define PECI_READ_LEN_MASK GENMASK(23, 16) +-#define PECI_WRITE_LEN_MASK GENMASK(15, 8) +-#define PECI_TAGET_ADDR_MASK GENMASK(7, 0) +- +-/* ASPEED_PECI_EXP_FCS - 0x10 : Expected FCS Data Register */ +-#define PECI_EXPECT_READ_FCS_MASK GENMASK(23, 16) +-#define PECI_EXPECT_AW_FCS_AUTO_MASK GENMASK(15, 8) +-#define PECI_EXPECT_WRITE_FCS_MASK GENMASK(7, 0) +- +-/* ASPEED_PECI_CAP_FCS - 0x14 : Captured FCS Data Register */ +-#define PECI_CAPTURE_READ_FCS_MASK GENMASK(23, 16) +-#define PECI_CAPTURE_WRITE_FCS_MASK GENMASK(7, 0) +- +-/* ASPEED_PECI_INT_CTRL/STS - 0x18/0x1c : Interrupt Register */ +-#define PECI_INT_TIMING_RESULT_MASK GENMASK(31, 30) +-#define PECI_INT_TIMEOUT BIT(4) +-#define PECI_INT_CONNECT BIT(3) +-#define PECI_INT_W_FCS_BAD BIT(2) +-#define PECI_INT_W_FCS_ABORT BIT(1) +-#define PECI_INT_CMD_DONE BIT(0) +- +-#define PECI_INT_MASK (PECI_INT_TIMEOUT | PECI_INT_CONNECT | \ +- PECI_INT_W_FCS_BAD | PECI_INT_W_FCS_ABORT | \ +- PECI_INT_CMD_DONE) +- +-#define PECI_IDLE_CHECK_TIMEOUT_USEC 50000 +-#define PECI_IDLE_CHECK_INTERVAL_USEC 10000 +- +-#define PECI_RD_SAMPLING_POINT_DEFAULT 8 +-#define PECI_RD_SAMPLING_POINT_MAX 15 +-#define PECI_CLK_DIV_DEFAULT 0 +-#define PECI_CLK_DIV_MAX 7 +-#define PECI_MSG_TIMING_DEFAULT 1 +-#define PECI_MSG_TIMING_MAX 255 +-#define PECI_ADDR_TIMING_DEFAULT 1 +-#define PECI_ADDR_TIMING_MAX 255 +-#define PECI_CMD_TIMEOUT_MS_DEFAULT 1000 +-#define PECI_CMD_TIMEOUT_MS_MAX 60000 +- +-struct aspeed_peci { +- struct peci_adapter *adapter; +- struct device *dev; +- struct regmap *regmap; +- struct clk *clk; +- struct reset_control *rst; +- int irq; +- spinlock_t lock; /* to sync completion status handling */ +- struct completion xfer_complete; +- u32 status; +- u32 cmd_timeout_ms; +-}; +- +-static int aspeed_peci_xfer_native(struct aspeed_peci *priv, +- struct peci_xfer_msg *msg) +-{ +- long err, timeout = msecs_to_jiffies(priv->cmd_timeout_ms); +- u32 peci_head, peci_state, rx_data, cmd_sts; +- unsigned long flags; +- int i, rc; +- uint reg; +- +- /* Check command sts and bus idle state */ +- rc = regmap_read_poll_timeout(priv->regmap, ASPEED_PECI_CMD, cmd_sts, +- !(cmd_sts & PECI_CMD_IDLE_MASK), +- PECI_IDLE_CHECK_INTERVAL_USEC, +- PECI_IDLE_CHECK_TIMEOUT_USEC); +- if (rc) +- return rc; /* -ETIMEDOUT */ +- +- spin_lock_irqsave(&priv->lock, flags); +- reinit_completion(&priv->xfer_complete); +- +- peci_head = FIELD_PREP(PECI_TAGET_ADDR_MASK, msg->addr) | +- FIELD_PREP(PECI_WRITE_LEN_MASK, msg->tx_len) | +- FIELD_PREP(PECI_READ_LEN_MASK, msg->rx_len); +- +- regmap_write(priv->regmap, ASPEED_PECI_CMD_CTRL, peci_head); +- +- for (i = 0; i < msg->tx_len; i += 4) { +- reg = i < 16 ? ASPEED_PECI_W_DATA0 + i % 16 : +- ASPEED_PECI_W_DATA4 + i % 16; +- regmap_write(priv->regmap, reg, +- le32_to_cpup((__le32 *)&msg->tx_buf[i])); +- } +- +- dev_dbg(priv->dev, "HEAD : 0x%08x\n", peci_head); +- print_hex_dump_debug("TX : ", DUMP_PREFIX_NONE, 16, 1, +- msg->tx_buf, msg->tx_len, true); +- +- priv->status = 0; +- regmap_write(priv->regmap, ASPEED_PECI_CMD, PECI_CMD_FIRE); +- spin_unlock_irqrestore(&priv->lock, flags); +- +- err = wait_for_completion_interruptible_timeout(&priv->xfer_complete, +- timeout); +- +- spin_lock_irqsave(&priv->lock, flags); +- dev_dbg(priv->dev, "INT_STS : 0x%08x\n", priv->status); +- regmap_read(priv->regmap, ASPEED_PECI_CMD, &peci_state); +- dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n", +- FIELD_GET(PECI_CMD_STS_MASK, peci_state)); +- +- regmap_write(priv->regmap, ASPEED_PECI_CMD, 0); +- +- if (err <= 0 || priv->status != PECI_INT_CMD_DONE) { +- if (err < 0) { /* -ERESTARTSYS */ +- rc = (int)err; +- goto err_irqrestore; +- } else if (err == 0) { +- dev_dbg(priv->dev, "Timeout waiting for a response!\n"); +- rc = -ETIMEDOUT; +- goto err_irqrestore; +- } +- +- dev_dbg(priv->dev, "No valid response!\n"); +- rc = -EIO; +- goto err_irqrestore; +- } +- +- /** +- * Note that rx_len and rx_buf size can be an odd number. +- * Byte handling is more efficient. +- */ +- for (i = 0; i < msg->rx_len; i++) { +- u8 byte_offset = i % 4; +- +- if (byte_offset == 0) { +- reg = i < 16 ? ASPEED_PECI_R_DATA0 + i % 16 : +- ASPEED_PECI_R_DATA4 + i % 16; +- regmap_read(priv->regmap, reg, &rx_data); +- } +- +- msg->rx_buf[i] = (u8)(rx_data >> (byte_offset << 3)); +- } +- +- print_hex_dump_debug("RX : ", DUMP_PREFIX_NONE, 16, 1, +- msg->rx_buf, msg->rx_len, true); +- +- regmap_read(priv->regmap, ASPEED_PECI_CMD, &peci_state); +- dev_dbg(priv->dev, "PECI_STATE : 0x%lx\n", +- FIELD_GET(PECI_CMD_STS_MASK, peci_state)); +- dev_dbg(priv->dev, "------------------------\n"); +- +-err_irqrestore: +- spin_unlock_irqrestore(&priv->lock, flags); +- return rc; +-} +- +-static irqreturn_t aspeed_peci_irq_handler(int irq, void *arg) +-{ +- struct aspeed_peci *priv = arg; +- u32 status_ack = 0; +- u32 status; +- +- spin_lock(&priv->lock); +- regmap_read(priv->regmap, ASPEED_PECI_INT_STS, &status); +- priv->status |= (status & PECI_INT_MASK); +- +- /** +- * In most cases, interrupt bits will be set one by one but also note +- * that multiple interrupt bits could be set at the same time. +- */ +- if (status & PECI_INT_TIMEOUT) { +- dev_dbg(priv->dev, "PECI_INT_TIMEOUT\n"); +- status_ack |= PECI_INT_TIMEOUT; +- } +- +- if (status & PECI_INT_CONNECT) { +- dev_dbg(priv->dev, "PECI_INT_CONNECT\n"); +- status_ack |= PECI_INT_CONNECT; +- } +- +- if (status & PECI_INT_W_FCS_BAD) { +- dev_dbg(priv->dev, "PECI_INT_W_FCS_BAD\n"); +- status_ack |= PECI_INT_W_FCS_BAD; +- } +- +- if (status & PECI_INT_W_FCS_ABORT) { +- dev_dbg(priv->dev, "PECI_INT_W_FCS_ABORT\n"); +- status_ack |= PECI_INT_W_FCS_ABORT; +- } +- +- /** +- * All commands should be ended up with a PECI_INT_CMD_DONE bit set +- * even in an error case. +- */ +- if (status & PECI_INT_CMD_DONE) { +- dev_dbg(priv->dev, "PECI_INT_CMD_DONE\n"); +- status_ack |= PECI_INT_CMD_DONE; +- complete(&priv->xfer_complete); +- } +- +- regmap_write(priv->regmap, ASPEED_PECI_INT_STS, status_ack); +- spin_unlock(&priv->lock); +- return IRQ_HANDLED; +-} +- +-static int aspeed_peci_init_ctrl(struct aspeed_peci *priv) +-{ +- u32 msg_timing, addr_timing, rd_sampling_point; +- u32 clk_freq, clk_divisor, clk_div_val = 0; +- int ret; +- +- priv->clk = devm_clk_get(priv->dev, NULL); +- if (IS_ERR(priv->clk)) { +- dev_err(priv->dev, "Failed to get clk source.\n"); +- return PTR_ERR(priv->clk); +- } +- +- ret = clk_prepare_enable(priv->clk); +- if (ret) { +- dev_err(priv->dev, "Failed to enable clock.\n"); +- return ret; +- } +- +- ret = of_property_read_u32(priv->dev->of_node, "clock-frequency", +- &clk_freq); +- if (ret) { +- dev_err(priv->dev, +- "Could not read clock-frequency property.\n"); +- clk_disable_unprepare(priv->clk); +- return ret; +- } +- +- clk_divisor = clk_get_rate(priv->clk) / clk_freq; +- +- while ((clk_divisor >> 1) && (clk_div_val < PECI_CLK_DIV_MAX)) +- clk_div_val++; +- +- ret = of_property_read_u32(priv->dev->of_node, "msg-timing", +- &msg_timing); +- if (ret || msg_timing > PECI_MSG_TIMING_MAX) { +- if (!ret) +- dev_warn(priv->dev, +- "Invalid msg-timing : %u, Use default : %u\n", +- msg_timing, PECI_MSG_TIMING_DEFAULT); +- msg_timing = PECI_MSG_TIMING_DEFAULT; +- } +- +- ret = of_property_read_u32(priv->dev->of_node, "addr-timing", +- &addr_timing); +- if (ret || addr_timing > PECI_ADDR_TIMING_MAX) { +- if (!ret) +- dev_warn(priv->dev, +- "Invalid addr-timing : %u, Use default : %u\n", +- addr_timing, PECI_ADDR_TIMING_DEFAULT); +- addr_timing = PECI_ADDR_TIMING_DEFAULT; +- } +- +- ret = of_property_read_u32(priv->dev->of_node, "rd-sampling-point", +- &rd_sampling_point); +- if (ret || rd_sampling_point > PECI_RD_SAMPLING_POINT_MAX) { +- if (!ret) +- dev_warn(priv->dev, +- "Invalid rd-sampling-point : %u. Use default : %u\n", +- rd_sampling_point, +- PECI_RD_SAMPLING_POINT_DEFAULT); +- rd_sampling_point = PECI_RD_SAMPLING_POINT_DEFAULT; +- } +- +- ret = of_property_read_u32(priv->dev->of_node, "cmd-timeout-ms", +- &priv->cmd_timeout_ms); +- if (ret || priv->cmd_timeout_ms > PECI_CMD_TIMEOUT_MS_MAX || +- priv->cmd_timeout_ms == 0) { +- if (!ret) +- dev_warn(priv->dev, +- "Invalid cmd-timeout-ms : %u. Use default : %u\n", +- priv->cmd_timeout_ms, +- PECI_CMD_TIMEOUT_MS_DEFAULT); +- priv->cmd_timeout_ms = PECI_CMD_TIMEOUT_MS_DEFAULT; +- } +- +- regmap_write(priv->regmap, ASPEED_PECI_CTRL, +- FIELD_PREP(PECI_CTRL_CLK_DIV_MASK, PECI_CLK_DIV_DEFAULT) | +- PECI_CTRL_PECI_CLK_EN); +- +- /** +- * Timing negotiation period setting. +- * The unit of the programmed value is 4 times of PECI clock period. +- */ +- regmap_write(priv->regmap, ASPEED_PECI_TIMING, +- FIELD_PREP(PECI_TIMING_MESSAGE_MASK, msg_timing) | +- FIELD_PREP(PECI_TIMING_ADDRESS_MASK, addr_timing)); +- +- /* Clear interrupts */ +- regmap_write(priv->regmap, ASPEED_PECI_INT_STS, PECI_INT_MASK); +- +- /* Enable interrupts */ +- regmap_write(priv->regmap, ASPEED_PECI_INT_CTRL, PECI_INT_MASK); +- +- /* Read sampling point and clock speed setting */ +- regmap_write(priv->regmap, ASPEED_PECI_CTRL, +- FIELD_PREP(PECI_CTRL_SAMPLING_MASK, rd_sampling_point) | +- FIELD_PREP(PECI_CTRL_CLK_DIV_MASK, clk_div_val) | +- PECI_CTRL_PECI_EN | PECI_CTRL_PECI_CLK_EN); +- +- return 0; +-} +- +-static const struct regmap_config aspeed_peci_regmap_config = { +- .reg_bits = 32, +- .val_bits = 32, +- .reg_stride = 4, +- .max_register = ASPEED_PECI_R_DATA7, +- .val_format_endian = REGMAP_ENDIAN_LITTLE, +- .fast_io = true, +-}; +- +-static int aspeed_peci_xfer(struct peci_adapter *adapter, +- struct peci_xfer_msg *msg) +-{ +- struct aspeed_peci *priv = peci_get_adapdata(adapter); +- +- return aspeed_peci_xfer_native(priv, msg); +-} +- +-static int aspeed_peci_probe(struct platform_device *pdev) +-{ +- struct peci_adapter *adapter; +- struct aspeed_peci *priv; +- struct resource *res; +- void __iomem *base; +- u32 cmd_sts; +- int ret; +- +- adapter = peci_alloc_adapter(&pdev->dev, sizeof(*priv)); +- if (!adapter) +- return -ENOMEM; +- +- priv = peci_get_adapdata(adapter); +- priv->adapter = adapter; +- priv->dev = &pdev->dev; +- dev_set_drvdata(&pdev->dev, priv); +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- base = devm_ioremap_resource(&pdev->dev, res); +- if (IS_ERR(base)) { +- ret = PTR_ERR(base); +- goto err_put_adapter_dev; +- } +- +- priv->regmap = devm_regmap_init_mmio(&pdev->dev, base, +- &aspeed_peci_regmap_config); +- if (IS_ERR(priv->regmap)) { +- ret = PTR_ERR(priv->regmap); +- goto err_put_adapter_dev; +- } +- +- /** +- * We check that the regmap works on this very first access, +- * but as this is an MMIO-backed regmap, subsequent regmap +- * access is not going to fail and we skip error checks from +- * this point. +- */ +- ret = regmap_read(priv->regmap, ASPEED_PECI_CMD, &cmd_sts); +- if (ret) { +- ret = -EIO; +- goto err_put_adapter_dev; +- } +- +- priv->irq = platform_get_irq(pdev, 0); +- if (!priv->irq) { +- ret = -ENODEV; +- goto err_put_adapter_dev; +- } +- +- ret = devm_request_irq(&pdev->dev, priv->irq, aspeed_peci_irq_handler, +- 0, "peci-aspeed-irq", priv); +- if (ret) +- goto err_put_adapter_dev; +- +- init_completion(&priv->xfer_complete); +- spin_lock_init(&priv->lock); +- +- priv->adapter->owner = THIS_MODULE; +- priv->adapter->dev.of_node = of_node_get(dev_of_node(priv->dev)); +- strlcpy(priv->adapter->name, pdev->name, sizeof(priv->adapter->name)); +- priv->adapter->xfer = aspeed_peci_xfer; +- +- priv->rst = devm_reset_control_get(&pdev->dev, NULL); +- if (IS_ERR(priv->rst)) { +- dev_err(&pdev->dev, +- "missing or invalid reset controller entry"); +- ret = PTR_ERR(priv->rst); +- goto err_put_adapter_dev; +- } +- reset_control_deassert(priv->rst); +- +- ret = aspeed_peci_init_ctrl(priv); +- if (ret) +- goto err_put_adapter_dev; +- +- ret = peci_add_adapter(priv->adapter); +- if (ret) +- goto err_put_adapter_dev; +- +- dev_info(&pdev->dev, "peci bus %d registered, irq %d\n", +- priv->adapter->nr, priv->irq); +- +- return 0; +- +-err_put_adapter_dev: +- put_device(&adapter->dev); +- return ret; +-} +- +-static int aspeed_peci_remove(struct platform_device *pdev) +-{ +- struct aspeed_peci *priv = dev_get_drvdata(&pdev->dev); +- +- clk_disable_unprepare(priv->clk); +- reset_control_assert(priv->rst); +- peci_del_adapter(priv->adapter); +- of_node_put(priv->adapter->dev.of_node); +- +- return 0; +-} +- +-static const struct of_device_id aspeed_peci_of_table[] = { +- { .compatible = "aspeed,ast2400-peci", }, +- { .compatible = "aspeed,ast2500-peci", }, +- { } +-}; +-MODULE_DEVICE_TABLE(of, aspeed_peci_of_table); +- +-static struct platform_driver aspeed_peci_driver = { +- .probe = aspeed_peci_probe, +- .remove = aspeed_peci_remove, +- .driver = { +- .name = "peci-aspeed", +- .of_match_table = of_match_ptr(aspeed_peci_of_table), +- }, +-}; +-module_platform_driver(aspeed_peci_driver); +- +-MODULE_AUTHOR("Ryan Chen <ryan_chen@aspeedtech.com>"); +-MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>"); +-MODULE_DESCRIPTION("ASPEED PECI driver"); +-MODULE_LICENSE("GPL v2"); +diff --git a/drivers/peci/peci-core.c b/drivers/peci/peci-core.c +index fac8c72dcda8..e2ef013e5002 100644 +--- a/drivers/peci/peci-core.c ++++ b/drivers/peci/peci-core.c +@@ -1,38 +1,31 @@ + // SPDX-License-Identifier: GPL-2.0 +-// Copyright (c) 2018 Intel Corporation ++// Copyright (c) 2018-2019 Intel Corporation + + #include <linux/bitfield.h> + #include <linux/crc8.h> + #include <linux/delay.h> +-#include <linux/fs.h> ++#include <linux/mm.h> + #include <linux/module.h> + #include <linux/of_device.h> + #include <linux/peci.h> + #include <linux/pm_domain.h> + #include <linux/pm_runtime.h> ++#include <linux/sched/task_stack.h> + #include <linux/slab.h> +-#include <linux/uaccess.h> + + /* Mask for getting minor revision number from DIB */ + #define REVISION_NUM_MASK GENMASK(15, 8) + +-/* CRC8 table for Assure Write Frame Check */ ++/* CRC8 table for Assured Write Frame Check */ + #define PECI_CRC8_POLYNOMIAL 0x07 + DECLARE_CRC8_TABLE(peci_crc8_table); + +-static struct device_type peci_adapter_type; +-static struct device_type peci_client_type; +- +-/* Max number of peci cdev */ +-#define PECI_CDEV_MAX 16 +- +-static dev_t peci_devt; + static bool is_registered; + + static DEFINE_MUTEX(core_lock); + static DEFINE_IDR(peci_adapter_idr); + +-static struct peci_adapter *peci_get_adapter(int nr) ++struct peci_adapter *peci_get_adapter(int nr) + { + struct peci_adapter *adapter; + +@@ -48,10 +41,12 @@ static struct peci_adapter *peci_get_adapter(int nr) + + out_unlock: + mutex_unlock(&core_lock); ++ + return adapter; + } ++EXPORT_SYMBOL_GPL(peci_get_adapter); + +-static void peci_put_adapter(struct peci_adapter *adapter) ++void peci_put_adapter(struct peci_adapter *adapter) + { + if (!adapter) + return; +@@ -59,6 +54,7 @@ static void peci_put_adapter(struct peci_adapter *adapter) + put_device(&adapter->dev); + module_put(adapter->owner); + } ++EXPORT_SYMBOL_GPL(peci_put_adapter); + + static ssize_t name_show(struct device *dev, + struct device_attribute *attr, +@@ -84,10 +80,11 @@ static struct attribute *peci_device_attrs[] = { + }; + ATTRIBUTE_GROUPS(peci_device); + +-static struct device_type peci_client_type = { ++struct device_type peci_client_type = { + .groups = peci_device_groups, + .release = peci_client_dev_release, + }; ++EXPORT_SYMBOL_GPL(peci_client_type); + + /** + * peci_verify_client - return parameter as peci_client, or NULL +@@ -103,19 +100,120 @@ struct peci_client *peci_verify_client(struct device *dev) + } + EXPORT_SYMBOL_GPL(peci_verify_client); + +-static u8 peci_aw_fcs(u8 *data, int len) ++/** ++ * peci_get_xfer_msg() - get a DMA safe peci_xfer_msg for the given tx and rx ++ * length ++ * @tx_len: the length of tx_buf. May be 0 if tx_buf isn't needed. ++ * @rx_len: the length of rx_buf. May be 0 if rx_buf isn't needed. ++ * ++ * Return: NULL if a DMA safe buffer was not obtained. ++ * Or a valid pointer to be used with DMA. After use, release it by ++ * calling peci_put_xfer_msg(). ++ * ++ * This function must only be called from process context! ++ */ ++struct peci_xfer_msg *peci_get_xfer_msg(u8 tx_len, u8 rx_len) ++{ ++ struct peci_xfer_msg *msg; ++ u8 *tx_buf, *rx_buf; ++ ++ if (tx_len) { ++ tx_buf = kzalloc(tx_len, GFP_KERNEL); ++ if (!tx_buf) ++ return NULL; ++ } else { ++ tx_buf = NULL; ++ } ++ ++ if (rx_len) { ++ rx_buf = kzalloc(rx_len, GFP_KERNEL); ++ if (!rx_buf) ++ goto err_free_tx_buf; ++ } else { ++ rx_buf = NULL; ++ } ++ ++ msg = kzalloc(sizeof(struct peci_xfer_msg), GFP_KERNEL); ++ if (!msg) ++ goto err_free_tx_rx_buf; ++ ++ msg->tx_len = tx_len; ++ msg->tx_buf = tx_buf; ++ msg->rx_len = rx_len; ++ msg->rx_buf = rx_buf; ++ ++ return msg; ++ ++err_free_tx_rx_buf: ++ kfree(rx_buf); ++err_free_tx_buf: ++ kfree(tx_buf); ++ ++ return NULL; ++} ++EXPORT_SYMBOL_GPL(peci_get_xfer_msg); ++ ++/** ++ * peci_put_xfer_msg - release a DMA safe peci_xfer_msg ++ * @msg: the message obtained from peci_get_xfer_msg(). May be NULL. ++ */ ++void peci_put_xfer_msg(struct peci_xfer_msg *msg) ++{ ++ if (!msg) ++ return; ++ ++ kfree(msg->rx_buf); ++ kfree(msg->tx_buf); ++ kfree(msg); ++} ++EXPORT_SYMBOL_GPL(peci_put_xfer_msg); ++ ++/* Calculate an Assured Write Frame Check Sequence byte */ ++static int peci_aw_fcs(struct peci_xfer_msg *msg, int len, u8 *aw_fcs) + { +- return crc8(peci_crc8_table, data, (size_t)len, 0); ++ u8 *tmp_buf; ++ ++ /* Allocate a temporary buffer to use a contiguous byte array */ ++ tmp_buf = kmalloc(len, GFP_KERNEL); ++ if (!tmp_buf) ++ return -ENOMEM; ++ ++ tmp_buf[0] = msg->addr; ++ tmp_buf[1] = msg->tx_len; ++ tmp_buf[2] = msg->rx_len; ++ memcpy(&tmp_buf[3], msg->tx_buf, len - 3); ++ ++ *aw_fcs = crc8(peci_crc8_table, tmp_buf, (size_t)len, 0); ++ ++ kfree(tmp_buf); ++ ++ return 0; + } + + static int __peci_xfer(struct peci_adapter *adapter, struct peci_xfer_msg *msg, + bool do_retry, bool has_aw_fcs) + { +- ktime_t start, end; +- s64 elapsed_ms; +- int rc = 0; ++ ulong timeout = jiffies; ++ u8 aw_fcs; ++ int ret; + +- /** ++ /* ++ * In case if adapter uses DMA, check at here whether tx and rx buffers ++ * are DMA capable or not. ++ */ ++ if (IS_ENABLED(CONFIG_HAS_DMA) && adapter->use_dma) { ++ if (is_vmalloc_addr(msg->tx_buf) || ++ is_vmalloc_addr(msg->rx_buf)) { ++ WARN_ONCE(1, "xfer msg is not dma capable\n"); ++ return -EAGAIN; ++ } else if (object_is_on_stack(msg->tx_buf) || ++ object_is_on_stack(msg->rx_buf)) { ++ WARN_ONCE(1, "xfer msg is on stack\n"); ++ return -EAGAIN; ++ } ++ } ++ ++ /* + * For some commands, the PECI originator may need to retry a command if + * the processor PECI client responds with a 0x8x completion code. In + * each instance, the processor PECI client may have started the +@@ -125,55 +223,56 @@ static int __peci_xfer(struct peci_adapter *adapter, struct peci_xfer_msg *msg, + */ + + if (do_retry) +- start = ktime_get(); ++ timeout += msecs_to_jiffies(PECI_DEV_RETRY_TIME_MS); + +- do { +- rc = adapter->xfer(adapter, msg); ++ for (;;) { ++ ret = adapter->xfer(adapter, msg); + +- if (!do_retry || rc) ++ if (!do_retry || ret) + break; + +- if (msg->rx_buf[0] == DEV_PECI_CC_SUCCESS) ++ if (msg->rx_buf[0] == PECI_DEV_CC_SUCCESS) + break; + + /* Retry is needed when completion code is 0x8x */ +- if ((msg->rx_buf[0] & DEV_PECI_CC_RETRY_CHECK_MASK) != +- DEV_PECI_CC_NEED_RETRY) { +- rc = -EIO; ++ if ((msg->rx_buf[0] & PECI_DEV_CC_RETRY_CHECK_MASK) != ++ PECI_DEV_CC_NEED_RETRY) { ++ ret = -EIO; + break; + } + + /* Set the retry bit to indicate a retry attempt */ +- msg->tx_buf[1] |= DEV_PECI_RETRY_BIT; ++ msg->tx_buf[1] |= PECI_DEV_RETRY_BIT; + + /* Recalculate the AW FCS if it has one */ +- if (has_aw_fcs) +- msg->tx_buf[msg->tx_len - 1] = 0x80 ^ +- peci_aw_fcs((u8 *)msg, +- 2 + msg->tx_len); ++ if (has_aw_fcs) { ++ ret = peci_aw_fcs(msg, 2 + msg->tx_len, &aw_fcs); ++ if (ret) ++ break; + +- /** ++ msg->tx_buf[msg->tx_len - 1] = 0x80 ^ aw_fcs; ++ } ++ ++ /* + * Retry for at least 250ms before returning an error. + * Retry interval guideline: + * No minimum < Retry Interval < No maximum + * (recommend 10ms) + */ +- end = ktime_get(); +- elapsed_ms = ktime_to_ms(ktime_sub(end, start)); +- if (elapsed_ms >= DEV_PECI_RETRY_TIME_MS) { ++ if (time_after(jiffies, timeout)) { + dev_dbg(&adapter->dev, "Timeout retrying xfer!\n"); +- rc = -ETIMEDOUT; ++ ret = -ETIMEDOUT; + break; + } + +- usleep_range((DEV_PECI_RETRY_INTERVAL_USEC >> 2) + 1, +- DEV_PECI_RETRY_INTERVAL_USEC); +- } while (true); ++ usleep_range((PECI_DEV_RETRY_INTERVAL_USEC >> 2) + 1, ++ PECI_DEV_RETRY_INTERVAL_USEC); ++ } + +- if (rc) +- dev_dbg(&adapter->dev, "xfer error, rc: %d\n", rc); ++ if (ret) ++ dev_dbg(&adapter->dev, "xfer error: %d\n", ret); + +- return rc; ++ return ret; + } + + static int peci_xfer(struct peci_adapter *adapter, struct peci_xfer_msg *msg) +@@ -190,34 +289,37 @@ static int peci_xfer_with_retries(struct peci_adapter *adapter, + + static int peci_scan_cmd_mask(struct peci_adapter *adapter) + { +- struct peci_xfer_msg msg; ++ struct peci_xfer_msg *msg; + u8 revision; +- int rc = 0; ++ int ret; + u64 dib; + + /* Update command mask just once */ + if (adapter->cmd_mask & BIT(PECI_CMD_XFER)) + return 0; + +- msg.addr = PECI_BASE_ADDR; +- msg.tx_len = GET_DIB_WR_LEN; +- msg.rx_len = GET_DIB_RD_LEN; +- msg.tx_buf[0] = GET_DIB_PECI_CMD; ++ msg = peci_get_xfer_msg(PECI_GET_DIB_WR_LEN, PECI_GET_DIB_RD_LEN); ++ if (!msg) ++ return -ENOMEM; ++ ++ msg->addr = PECI_BASE_ADDR; ++ msg->tx_buf[0] = PECI_GET_DIB_CMD; + +- rc = peci_xfer(adapter, &msg); +- if (rc) +- return rc; ++ ret = peci_xfer(adapter, msg); ++ if (ret) ++ return ret; + +- dib = le64_to_cpup((__le64 *)msg.rx_buf); ++ dib = le64_to_cpup((__le64 *)msg->rx_buf); + + /* Check special case for Get DIB command */ + if (dib == 0) { + dev_dbg(&adapter->dev, "DIB read as 0\n"); +- return -EIO; ++ ret = -EIO; ++ goto out; + } + + /** +- * Setting up the supporting commands based on minor revision number. ++ * Setting up the supporting commands based on revision number. + * See PECI Spec Table 3-1. + */ + revision = FIELD_GET(REVISION_NUM_MASK, dib); +@@ -243,10 +345,14 @@ static int peci_scan_cmd_mask(struct peci_adapter *adapter) + adapter->cmd_mask |= BIT(PECI_CMD_GET_DIB); + adapter->cmd_mask |= BIT(PECI_CMD_PING); + +- return rc; ++out: ++ peci_put_xfer_msg(msg); ++ ++ return ret; + } + +-static int peci_cmd_support(struct peci_adapter *adapter, enum peci_cmd cmd) ++static int peci_check_cmd_support(struct peci_adapter *adapter, ++ enum peci_cmd cmd) + { + if (!(adapter->cmd_mask & BIT(PECI_CMD_PING)) && + peci_scan_cmd_mask(adapter) < 0) { +@@ -262,70 +368,87 @@ static int peci_cmd_support(struct peci_adapter *adapter, enum peci_cmd cmd) + return 0; + } + +-static int peci_ioctl_xfer(struct peci_adapter *adapter, void *vmsg) ++static int peci_cmd_xfer(struct peci_adapter *adapter, void *vmsg) + { + struct peci_xfer_msg *msg = vmsg; + + return peci_xfer(adapter, msg); + } + +-static int peci_ioctl_ping(struct peci_adapter *adapter, void *vmsg) ++static int peci_cmd_ping(struct peci_adapter *adapter, void *vmsg) + { + struct peci_ping_msg *umsg = vmsg; +- struct peci_xfer_msg msg; ++ struct peci_xfer_msg *msg; ++ int ret; + +- msg.addr = umsg->addr; +- msg.tx_len = 0; +- msg.rx_len = 0; ++ msg = peci_get_xfer_msg(0, 0); ++ if (!msg) ++ return -ENOMEM; + +- return peci_xfer(adapter, &msg); ++ msg->addr = umsg->addr; ++ ++ ret = peci_xfer(adapter, msg); ++ ++ peci_put_xfer_msg(msg); ++ ++ return ret; + } + +-static int peci_ioctl_get_dib(struct peci_adapter *adapter, void *vmsg) ++static int peci_cmd_get_dib(struct peci_adapter *adapter, void *vmsg) + { + struct peci_get_dib_msg *umsg = vmsg; +- struct peci_xfer_msg msg; +- int rc; ++ struct peci_xfer_msg *msg; ++ int ret; + +- msg.addr = umsg->addr; +- msg.tx_len = GET_DIB_WR_LEN; +- msg.rx_len = GET_DIB_RD_LEN; +- msg.tx_buf[0] = GET_DIB_PECI_CMD; ++ msg = peci_get_xfer_msg(PECI_GET_DIB_WR_LEN, PECI_GET_DIB_RD_LEN); ++ if (!msg) ++ return -ENOMEM; + +- rc = peci_xfer(adapter, &msg); +- if (rc) +- return rc; ++ msg->addr = umsg->addr; ++ msg->tx_buf[0] = PECI_GET_DIB_CMD; + +- umsg->dib = le64_to_cpup((__le64 *)msg.rx_buf); ++ ret = peci_xfer(adapter, msg); ++ if (ret) ++ goto out; + +- return 0; ++ umsg->dib = le64_to_cpup((__le64 *)msg->rx_buf); ++ ++out: ++ peci_put_xfer_msg(msg); ++ ++ return ret; + } + +-static int peci_ioctl_get_temp(struct peci_adapter *adapter, void *vmsg) ++static int peci_cmd_get_temp(struct peci_adapter *adapter, void *vmsg) + { + struct peci_get_temp_msg *umsg = vmsg; +- struct peci_xfer_msg msg; +- int rc; ++ struct peci_xfer_msg *msg; ++ int ret; + +- msg.addr = umsg->addr; +- msg.tx_len = GET_TEMP_WR_LEN; +- msg.rx_len = GET_TEMP_RD_LEN; +- msg.tx_buf[0] = GET_TEMP_PECI_CMD; ++ msg = peci_get_xfer_msg(PECI_GET_TEMP_WR_LEN, PECI_GET_TEMP_RD_LEN); ++ if (!msg) ++ return -ENOMEM; + +- rc = peci_xfer(adapter, &msg); +- if (rc) +- return rc; ++ msg->addr = umsg->addr; ++ msg->tx_buf[0] = PECI_GET_TEMP_CMD; + +- umsg->temp_raw = le16_to_cpup((__le16 *)msg.rx_buf); ++ ret = peci_xfer(adapter, msg); ++ if (ret) ++ goto out; + +- return 0; ++ umsg->temp_raw = le16_to_cpup((__le16 *)msg->rx_buf); ++ ++out: ++ peci_put_xfer_msg(msg); ++ ++ return ret; + } + +-static int peci_ioctl_rd_pkg_cfg(struct peci_adapter *adapter, void *vmsg) ++static int peci_cmd_rd_pkg_cfg(struct peci_adapter *adapter, void *vmsg) + { + struct peci_rd_pkg_cfg_msg *umsg = vmsg; +- struct peci_xfer_msg msg; +- int rc = 0; ++ struct peci_xfer_msg *msg; ++ int ret; + + /* Per the PECI spec, the read length must be a byte, word, or dword */ + if (umsg->rx_len != 1 && umsg->rx_len != 2 && umsg->rx_len != 4) { +@@ -334,29 +457,34 @@ static int peci_ioctl_rd_pkg_cfg(struct peci_adapter *adapter, void *vmsg) + return -EINVAL; + } + +- msg.addr = umsg->addr; +- msg.tx_len = RDPKGCFG_WRITE_LEN; +- /* read lengths of 1 and 2 result in an error, so only use 4 for now */ +- msg.rx_len = RDPKGCFG_READ_LEN_BASE + umsg->rx_len; +- msg.tx_buf[0] = RDPKGCFG_PECI_CMD; +- msg.tx_buf[1] = 0; /* request byte for Host ID | Retry bit */ +- /* Host ID is 0 for PECI 3.0 */ +- msg.tx_buf[2] = umsg->index; /* RdPkgConfig index */ +- msg.tx_buf[3] = (u8)umsg->param; /* LSB - Config parameter */ +- msg.tx_buf[4] = (u8)(umsg->param >> 8); /* MSB - Config parameter */ ++ msg = peci_get_xfer_msg(PECI_RDPKGCFG_WRITE_LEN, ++ PECI_RDPKGCFG_READ_LEN_BASE + umsg->rx_len); ++ if (!msg) ++ return -ENOMEM; ++ ++ msg->addr = umsg->addr; ++ msg->tx_buf[0] = PECI_RDPKGCFG_CMD; ++ msg->tx_buf[1] = 0; /* request byte for Host ID | Retry bit */ ++ /* Host ID is 0 for PECI 3.0 */ ++ msg->tx_buf[2] = umsg->index; /* RdPkgConfig index */ ++ msg->tx_buf[3] = (u8)umsg->param; /* LSB - Config parameter */ ++ msg->tx_buf[4] = (u8)(umsg->param >> 8); /* MSB - Config parameter */ ++ ++ ret = peci_xfer_with_retries(adapter, msg, false); ++ if (!ret) ++ memcpy(umsg->pkg_config, &msg->rx_buf[1], umsg->rx_len); + +- rc = peci_xfer_with_retries(adapter, &msg, false); +- if (!rc) +- memcpy(umsg->pkg_config, &msg.rx_buf[1], umsg->rx_len); ++ peci_put_xfer_msg(msg); + +- return rc; ++ return ret; + } + +-static int peci_ioctl_wr_pkg_cfg(struct peci_adapter *adapter, void *vmsg) ++static int peci_cmd_wr_pkg_cfg(struct peci_adapter *adapter, void *vmsg) + { + struct peci_wr_pkg_cfg_msg *umsg = vmsg; +- struct peci_xfer_msg msg; +- int rc = 0, i; ++ struct peci_xfer_msg *msg; ++ int ret, i; ++ u8 aw_fcs; + + /* Per the PECI spec, the write length must be a dword */ + if (umsg->tx_len != 4) { +@@ -365,86 +493,113 @@ static int peci_ioctl_wr_pkg_cfg(struct peci_adapter *adapter, void *vmsg) + return -EINVAL; + } + +- msg.addr = umsg->addr; +- msg.tx_len = WRPKGCFG_WRITE_LEN_BASE + umsg->tx_len; +- /* read lengths of 1 and 2 result in an error, so only use 4 for now */ +- msg.rx_len = WRPKGCFG_READ_LEN; +- msg.tx_buf[0] = WRPKGCFG_PECI_CMD; +- msg.tx_buf[1] = 0; /* request byte for Host ID | Retry bit */ ++ msg = peci_get_xfer_msg(PECI_WRPKGCFG_WRITE_LEN_BASE + umsg->tx_len, ++ PECI_WRPKGCFG_READ_LEN); ++ if (!msg) ++ return -ENOMEM; ++ ++ msg->addr = umsg->addr; ++ msg->tx_buf[0] = PECI_WRPKGCFG_CMD; ++ msg->tx_buf[1] = 0; /* request byte for Host ID | Retry bit */ + /* Host ID is 0 for PECI 3.0 */ +- msg.tx_buf[2] = umsg->index; /* RdPkgConfig index */ +- msg.tx_buf[3] = (u8)umsg->param; /* LSB - Config parameter */ +- msg.tx_buf[4] = (u8)(umsg->param >> 8); /* MSB - Config parameter */ ++ msg->tx_buf[2] = umsg->index; /* RdPkgConfig index */ ++ msg->tx_buf[3] = (u8)umsg->param; /* LSB - Config parameter */ ++ msg->tx_buf[4] = (u8)(umsg->param >> 8); /* MSB - Config parameter */ + for (i = 0; i < umsg->tx_len; i++) +- msg.tx_buf[5 + i] = (u8)(umsg->value >> (i << 3)); ++ msg->tx_buf[5 + i] = (u8)(umsg->value >> (i << 3)); ++ ++ /* Add an Assured Write Frame Check Sequence byte */ ++ ret = peci_aw_fcs(msg, 8 + umsg->tx_len, &aw_fcs); ++ if (ret) ++ goto out; ++ ++ msg->tx_buf[5 + i] = 0x80 ^ aw_fcs; + +- /* Add an Assure Write Frame Check Sequence byte */ +- msg.tx_buf[5 + i] = 0x80 ^ +- peci_aw_fcs((u8 *)&msg, 8 + umsg->tx_len); ++ ret = peci_xfer_with_retries(adapter, msg, true); + +- rc = peci_xfer_with_retries(adapter, &msg, true); ++out: ++ peci_put_xfer_msg(msg); + +- return rc; ++ return ret; + } + +-static int peci_ioctl_rd_ia_msr(struct peci_adapter *adapter, void *vmsg) ++static int peci_cmd_rd_ia_msr(struct peci_adapter *adapter, void *vmsg) + { + struct peci_rd_ia_msr_msg *umsg = vmsg; +- struct peci_xfer_msg msg; +- int rc = 0; +- +- msg.addr = umsg->addr; +- msg.tx_len = RDIAMSR_WRITE_LEN; +- msg.rx_len = RDIAMSR_READ_LEN; +- msg.tx_buf[0] = RDIAMSR_PECI_CMD; +- msg.tx_buf[1] = 0; +- msg.tx_buf[2] = umsg->thread_id; +- msg.tx_buf[3] = (u8)umsg->address; +- msg.tx_buf[4] = (u8)(umsg->address >> 8); +- +- rc = peci_xfer_with_retries(adapter, &msg, false); +- if (!rc) +- memcpy(&umsg->value, &msg.rx_buf[1], sizeof(uint64_t)); +- +- return rc; ++ struct peci_xfer_msg *msg; ++ int ret; ++ ++ msg = peci_get_xfer_msg(PECI_RDIAMSR_WRITE_LEN, PECI_RDIAMSR_READ_LEN); ++ if (!msg) ++ return -ENOMEM; ++ ++ msg->addr = umsg->addr; ++ msg->tx_buf[0] = PECI_RDIAMSR_CMD; ++ msg->tx_buf[1] = 0; ++ msg->tx_buf[2] = umsg->thread_id; ++ msg->tx_buf[3] = (u8)umsg->address; ++ msg->tx_buf[4] = (u8)(umsg->address >> 8); ++ ++ ret = peci_xfer_with_retries(adapter, msg, false); ++ if (!ret) ++ memcpy(&umsg->value, &msg->rx_buf[1], sizeof(uint64_t)); ++ ++ peci_put_xfer_msg(msg); ++ ++ return ret; ++} ++ ++static int peci_cmd_wr_ia_msr(struct peci_adapter *adapter, void *vmsg) ++{ ++ return -ENOSYS; /* Not implemented yet */ + } + +-static int peci_ioctl_rd_pci_cfg(struct peci_adapter *adapter, void *vmsg) ++static int peci_cmd_rd_pci_cfg(struct peci_adapter *adapter, void *vmsg) + { + struct peci_rd_pci_cfg_msg *umsg = vmsg; +- struct peci_xfer_msg msg; ++ struct peci_xfer_msg *msg; + u32 address; +- int rc = 0; ++ int ret; ++ ++ msg = peci_get_xfer_msg(PECI_RDPCICFG_WRITE_LEN, ++ PECI_RDPCICFG_READ_LEN); ++ if (!msg) ++ return -ENOMEM; + + address = umsg->reg; /* [11:0] - Register */ + address |= (u32)umsg->function << 12; /* [14:12] - Function */ + address |= (u32)umsg->device << 15; /* [19:15] - Device */ + address |= (u32)umsg->bus << 20; /* [27:20] - Bus */ + /* [31:28] - Reserved */ +- msg.addr = umsg->addr; +- msg.tx_len = RDPCICFG_WRITE_LEN; +- msg.rx_len = RDPCICFG_READ_LEN; +- msg.tx_buf[0] = RDPCICFG_PECI_CMD; +- msg.tx_buf[1] = 0; /* request byte for Host ID | Retry bit */ ++ msg->addr = umsg->addr; ++ msg->tx_buf[0] = PECI_RDPCICFG_CMD; ++ msg->tx_buf[1] = 0; /* request byte for Host ID | Retry bit */ + /* Host ID is 0 for PECI 3.0 */ +- msg.tx_buf[2] = (u8)address; /* LSB - PCI Config Address */ +- msg.tx_buf[3] = (u8)(address >> 8); /* PCI Config Address */ +- msg.tx_buf[4] = (u8)(address >> 16); /* PCI Config Address */ +- msg.tx_buf[5] = (u8)(address >> 24); /* MSB - PCI Config Address */ ++ msg->tx_buf[2] = (u8)address; /* LSB - PCI Config Address */ ++ msg->tx_buf[3] = (u8)(address >> 8); /* PCI Config Address */ ++ msg->tx_buf[4] = (u8)(address >> 16); /* PCI Config Address */ ++ msg->tx_buf[5] = (u8)(address >> 24); /* MSB - PCI Config Address */ ++ ++ ret = peci_xfer_with_retries(adapter, msg, false); ++ if (!ret) ++ memcpy(umsg->pci_config, &msg->rx_buf[1], 4); + +- rc = peci_xfer_with_retries(adapter, &msg, false); +- if (!rc) +- memcpy(umsg->pci_config, &msg.rx_buf[1], 4); ++ peci_put_xfer_msg(msg); + +- return rc; ++ return ret; + } + +-static int peci_ioctl_rd_pci_cfg_local(struct peci_adapter *adapter, void *vmsg) ++static int peci_cmd_wr_pci_cfg(struct peci_adapter *adapter, void *vmsg) ++{ ++ return -ENOSYS; /* Not implemented yet */ ++} ++ ++static int peci_cmd_rd_pci_cfg_local(struct peci_adapter *adapter, void *vmsg) + { + struct peci_rd_pci_cfg_local_msg *umsg = vmsg; +- struct peci_xfer_msg msg; ++ struct peci_xfer_msg *msg; + u32 address; +- int rc = 0; ++ int ret; + + /* Per the PECI spec, the read length must be a byte, word, or dword */ + if (umsg->rx_len != 1 && umsg->rx_len != 2 && umsg->rx_len != 4) { +@@ -453,34 +608,41 @@ static int peci_ioctl_rd_pci_cfg_local(struct peci_adapter *adapter, void *vmsg) + return -EINVAL; + } + ++ msg = peci_get_xfer_msg(PECI_RDPCICFGLOCAL_WRITE_LEN, ++ PECI_RDPCICFGLOCAL_READ_LEN_BASE + ++ umsg->rx_len); ++ if (!msg) ++ return -ENOMEM; ++ + address = umsg->reg; /* [11:0] - Register */ + address |= (u32)umsg->function << 12; /* [14:12] - Function */ + address |= (u32)umsg->device << 15; /* [19:15] - Device */ + address |= (u32)umsg->bus << 20; /* [23:20] - Bus */ + +- msg.addr = umsg->addr; +- msg.tx_len = RDPCICFGLOCAL_WRITE_LEN; +- msg.rx_len = RDPCICFGLOCAL_READ_LEN_BASE + umsg->rx_len; +- msg.tx_buf[0] = RDPCICFGLOCAL_PECI_CMD; +- msg.tx_buf[1] = 0; /* request byte for Host ID | Retry bit */ +- /* Host ID is 0 for PECI 3.0 */ +- msg.tx_buf[2] = (u8)address; /* LSB - PCI Configuration Address */ +- msg.tx_buf[3] = (u8)(address >> 8); /* PCI Configuration Address */ +- msg.tx_buf[4] = (u8)(address >> 16); /* PCI Configuration Address */ ++ msg->addr = umsg->addr; ++ msg->tx_buf[0] = PECI_RDPCICFGLOCAL_CMD; ++ msg->tx_buf[1] = 0; /* request byte for Host ID | Retry bit */ ++ /* Host ID is 0 for PECI 3.0 */ ++ msg->tx_buf[2] = (u8)address; /* LSB - PCI Configuration Address */ ++ msg->tx_buf[3] = (u8)(address >> 8); /* PCI Configuration Address */ ++ msg->tx_buf[4] = (u8)(address >> 16); /* PCI Configuration Address */ ++ ++ ret = peci_xfer_with_retries(adapter, msg, false); ++ if (!ret) ++ memcpy(umsg->pci_config, &msg->rx_buf[1], umsg->rx_len); + +- rc = peci_xfer_with_retries(adapter, &msg, false); +- if (!rc) +- memcpy(umsg->pci_config, &msg.rx_buf[1], umsg->rx_len); ++ peci_put_xfer_msg(msg); + +- return rc; ++ return ret; + } + +-static int peci_ioctl_wr_pci_cfg_local(struct peci_adapter *adapter, void *vmsg) ++static int peci_cmd_wr_pci_cfg_local(struct peci_adapter *adapter, void *vmsg) + { + struct peci_wr_pci_cfg_local_msg *umsg = vmsg; +- struct peci_xfer_msg msg; +- int rc = 0, i; ++ struct peci_xfer_msg *msg; + u32 address; ++ int ret, i; ++ u8 aw_fcs; + + /* Per the PECI spec, the write length must be a byte, word, or dword */ + if (umsg->tx_len != 1 && umsg->tx_len != 2 && umsg->tx_len != 4) { +@@ -489,47 +651,56 @@ static int peci_ioctl_wr_pci_cfg_local(struct peci_adapter *adapter, void *vmsg) + return -EINVAL; + } + ++ msg = peci_get_xfer_msg(PECI_WRPCICFGLOCAL_WRITE_LEN_BASE + ++ umsg->tx_len, PECI_WRPCICFGLOCAL_READ_LEN); ++ if (!msg) ++ return -ENOMEM; ++ + address = umsg->reg; /* [11:0] - Register */ + address |= (u32)umsg->function << 12; /* [14:12] - Function */ + address |= (u32)umsg->device << 15; /* [19:15] - Device */ + address |= (u32)umsg->bus << 20; /* [23:20] - Bus */ + +- msg.addr = umsg->addr; +- msg.tx_len = WRPCICFGLOCAL_WRITE_LEN_BASE + umsg->tx_len; +- msg.rx_len = WRPCICFGLOCAL_READ_LEN; +- msg.tx_buf[0] = WRPCICFGLOCAL_PECI_CMD; +- msg.tx_buf[1] = 0; /* request byte for Host ID | Retry bit */ +- /* Host ID is 0 for PECI 3.0 */ +- msg.tx_buf[2] = (u8)address; /* LSB - PCI Configuration Address */ +- msg.tx_buf[3] = (u8)(address >> 8); /* PCI Configuration Address */ +- msg.tx_buf[4] = (u8)(address >> 16); /* PCI Configuration Address */ ++ msg->addr = umsg->addr; ++ msg->tx_buf[0] = PECI_WRPCICFGLOCAL_CMD; ++ msg->tx_buf[1] = 0; /* request byte for Host ID | Retry bit */ ++ /* Host ID is 0 for PECI 3.0 */ ++ msg->tx_buf[2] = (u8)address; /* LSB - PCI Configuration Address */ ++ msg->tx_buf[3] = (u8)(address >> 8); /* PCI Configuration Address */ ++ msg->tx_buf[4] = (u8)(address >> 16); /* PCI Configuration Address */ + for (i = 0; i < umsg->tx_len; i++) +- msg.tx_buf[5 + i] = (u8)(umsg->value >> (i << 3)); ++ msg->tx_buf[5 + i] = (u8)(umsg->value >> (i << 3)); ++ ++ /* Add an Assured Write Frame Check Sequence byte */ ++ ret = peci_aw_fcs(msg, 8 + umsg->tx_len, &aw_fcs); ++ if (ret) ++ goto out; ++ ++ msg->tx_buf[5 + i] = 0x80 ^ aw_fcs; + +- /* Add an Assure Write Frame Check Sequence byte */ +- msg.tx_buf[5 + i] = 0x80 ^ +- peci_aw_fcs((u8 *)&msg, 8 + umsg->tx_len); ++ ret = peci_xfer_with_retries(adapter, msg, true); + +- rc = peci_xfer_with_retries(adapter, &msg, true); ++out: ++ peci_put_xfer_msg(msg); + +- return rc; ++ return ret; + } + +-typedef int (*peci_ioctl_fn_type)(struct peci_adapter *, void *); +- +-static const peci_ioctl_fn_type peci_ioctl_fn[PECI_CMD_MAX] = { +- peci_ioctl_xfer, +- peci_ioctl_ping, +- peci_ioctl_get_dib, +- peci_ioctl_get_temp, +- peci_ioctl_rd_pkg_cfg, +- peci_ioctl_wr_pkg_cfg, +- peci_ioctl_rd_ia_msr, +- NULL, /* Reserved */ +- peci_ioctl_rd_pci_cfg, +- NULL, /* Reserved */ +- peci_ioctl_rd_pci_cfg_local, +- peci_ioctl_wr_pci_cfg_local, ++typedef int (*peci_cmd_fn_type)(struct peci_adapter *, void *); ++ ++static const peci_cmd_fn_type peci_cmd_fn[PECI_CMD_MAX] = { ++ peci_cmd_xfer, ++ peci_cmd_ping, ++ peci_cmd_get_dib, ++ peci_cmd_get_temp, ++ peci_cmd_rd_pkg_cfg, ++ peci_cmd_wr_pkg_cfg, ++ peci_cmd_rd_ia_msr, ++ peci_cmd_wr_ia_msr, ++ peci_cmd_rd_pci_cfg, ++ peci_cmd_wr_pci_cfg, ++ peci_cmd_rd_pci_cfg_local, ++ peci_cmd_wr_pci_cfg_local, + }; + + /** +@@ -545,109 +716,28 @@ static const peci_ioctl_fn_type peci_ioctl_fn[PECI_CMD_MAX] = { + */ + int peci_command(struct peci_adapter *adapter, enum peci_cmd cmd, void *vmsg) + { +- int rc = 0; ++ int ret; + + if (cmd >= PECI_CMD_MAX || cmd < PECI_CMD_XFER) +- return -EINVAL; ++ return -ENOTTY; + + dev_dbg(&adapter->dev, "%s, cmd=0x%02x\n", __func__, cmd); + +- if (!peci_ioctl_fn[cmd]) ++ if (!peci_cmd_fn[cmd]) + return -EINVAL; + +- rt_mutex_lock(&adapter->bus_lock); ++ mutex_lock(&adapter->bus_lock); + +- rc = peci_cmd_support(adapter, cmd); +- if (!rc) +- rc = peci_ioctl_fn[cmd](adapter, vmsg); ++ ret = peci_check_cmd_support(adapter, cmd); ++ if (!ret) ++ ret = peci_cmd_fn[cmd](adapter, vmsg); + +- rt_mutex_unlock(&adapter->bus_lock); ++ mutex_unlock(&adapter->bus_lock); + +- return rc; ++ return ret; + } + EXPORT_SYMBOL_GPL(peci_command); + +-static long peci_ioctl(struct file *file, unsigned int iocmd, unsigned long arg) +-{ +- struct peci_adapter *adapter = file->private_data; +- void __user *argp = (void __user *)arg; +- unsigned int msg_len; +- enum peci_cmd cmd; +- int rc = 0; +- u8 *msg; +- +- if (!capable(CAP_SYS_ADMIN)) +- return -EPERM; +- +- dev_dbg(&adapter->dev, "ioctl, cmd=0x%x, arg=0x%lx\n", iocmd, arg); +- +- switch (iocmd) { +- case PECI_IOC_XFER: +- case PECI_IOC_PING: +- case PECI_IOC_GET_DIB: +- case PECI_IOC_GET_TEMP: +- case PECI_IOC_RD_PKG_CFG: +- case PECI_IOC_WR_PKG_CFG: +- case PECI_IOC_RD_IA_MSR: +- case PECI_IOC_RD_PCI_CFG: +- case PECI_IOC_RD_PCI_CFG_LOCAL: +- case PECI_IOC_WR_PCI_CFG_LOCAL: +- cmd = _IOC_NR(iocmd); +- msg_len = _IOC_SIZE(iocmd); +- break; +- +- default: +- dev_dbg(&adapter->dev, "Invalid ioctl cmd : 0x%x\n", iocmd); +- return -ENOTTY; +- } +- +- if (!access_ok(VERIFY_WRITE, argp, msg_len)) +- return -EFAULT; +- +- msg = memdup_user(argp, msg_len); +- if (IS_ERR(msg)) +- return PTR_ERR(msg); +- +- rc = peci_command(adapter, cmd, msg); +- +- if (!rc && copy_to_user(argp, msg, msg_len)) +- rc = -EFAULT; +- +- kfree(msg); +- return (long)rc; +-} +- +-static int peci_open(struct inode *inode, struct file *file) +-{ +- unsigned int minor = iminor(inode); +- struct peci_adapter *adapter; +- +- adapter = peci_get_adapter(minor); +- if (!adapter) +- return -ENODEV; +- +- file->private_data = adapter; +- +- return 0; +-} +- +-static int peci_release(struct inode *inode, struct file *file) +-{ +- struct peci_adapter *adapter = file->private_data; +- +- peci_put_adapter(adapter); +- file->private_data = NULL; +- +- return 0; +-} +- +-static const struct file_operations peci_fops = { +- .owner = THIS_MODULE, +- .unlocked_ioctl = peci_ioctl, +- .open = peci_open, +- .release = peci_release, +-}; +- + static int peci_detect(struct peci_adapter *adapter, u8 addr) + { + struct peci_ping_msg msg; +@@ -666,9 +756,9 @@ peci_of_match_device(const struct of_device_id *matches, + return NULL; + + return of_match_device(matches, &client->dev); +-#else ++#else /* CONFIG_OF */ + return NULL; +-#endif ++#endif /* CONFIG_OF */ + } + + static const struct peci_device_id * +@@ -737,6 +827,7 @@ static int peci_device_probe(struct device *dev) + + err_detach_pm_domain: + dev_pm_domain_detach(&client->dev, true); ++ + return status; + } + +@@ -775,13 +866,14 @@ static void peci_device_shutdown(struct device *dev) + driver->shutdown(client); + } + +-static struct bus_type peci_bus_type = { ++struct bus_type peci_bus_type = { + .name = "peci", + .match = peci_device_match, + .probe = peci_device_probe, + .remove = peci_device_remove, + .shutdown = peci_device_shutdown, + }; ++EXPORT_SYMBOL_GPL(peci_bus_type); + + static int peci_check_addr_validity(u8 addr) + { +@@ -814,18 +906,18 @@ static int peci_check_client_busy(struct device *dev, void *client_new_p) + int peci_get_cpu_id(struct peci_adapter *adapter, u8 addr, u32 *cpu_id) + { + struct peci_rd_pkg_cfg_msg msg; +- int rc; ++ int ret; + + msg.addr = addr; +- msg.index = MBX_INDEX_CPU_ID; +- msg.param = PKG_ID_CPU_ID; ++ msg.index = PECI_MBX_INDEX_CPU_ID; ++ msg.param = PECI_PKG_ID_CPU_ID; + msg.rx_len = 4; + +- rc = peci_command(adapter, PECI_CMD_RD_PKG_CFG, &msg); +- if (!rc) ++ ret = peci_command(adapter, PECI_CMD_RD_PKG_CFG, &msg); ++ if (!ret) + *cpu_id = le32_to_cpup((__le32 *)msg.pkg_config); + +- return rc; ++ return ret; + } + EXPORT_SYMBOL_GPL(peci_get_cpu_id); + +@@ -833,7 +925,7 @@ static struct peci_client *peci_new_device(struct peci_adapter *adapter, + struct peci_board_info const *info) + { + struct peci_client *client; +- int rc; ++ int ret; + + /* Increase reference count for the adapter assigned */ + if (!peci_get_adapter(adapter->nr)) +@@ -847,46 +939,49 @@ static struct peci_client *peci_new_device(struct peci_adapter *adapter, + client->addr = info->addr; + strlcpy(client->name, info->type, sizeof(client->name)); + +- rc = peci_check_addr_validity(client->addr); +- if (rc) { ++ ret = peci_check_addr_validity(client->addr); ++ if (ret) { + dev_err(&adapter->dev, "Invalid PECI CPU address 0x%02hx\n", + client->addr); + goto err_free_client_silent; + } + + /* Check online status of client */ +- rc = peci_detect(adapter, client->addr); +- if (rc) ++ ret = peci_detect(adapter, client->addr); ++ if (ret) + goto err_free_client; + +- rc = device_for_each_child(&adapter->dev, client, +- peci_check_client_busy); +- if (rc) ++ ret = device_for_each_child(&adapter->dev, client, ++ peci_check_client_busy); ++ if (ret) + goto err_free_client; + + client->dev.parent = &client->adapter->dev; + client->dev.bus = &peci_bus_type; + client->dev.type = &peci_client_type; +- client->dev.of_node = info->of_node; ++ client->dev.of_node = of_node_get(info->of_node); + dev_set_name(&client->dev, "%d-%02x", adapter->nr, client->addr); + +- rc = device_register(&client->dev); +- if (rc) +- goto err_free_client; ++ ret = device_register(&client->dev); ++ if (ret) ++ goto err_put_of_node; + + dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n", + client->name, dev_name(&client->dev)); + + return client; + ++err_put_of_node: ++ of_node_put(info->of_node); + err_free_client: + dev_err(&adapter->dev, + "Failed to register peci client %s at 0x%02x (%d)\n", +- client->name, client->addr, rc); ++ client->name, client->addr, ret); + err_free_client_silent: + kfree(client); + err_put_adapter: + peci_put_adapter(adapter); ++ + return NULL; + } + +@@ -895,8 +990,10 @@ static void peci_unregister_device(struct peci_client *client) + if (!client) + return; + +- if (client->dev.of_node) ++ if (client->dev.of_node) { + of_node_clear_flag(client->dev.of_node, OF_POPULATED); ++ of_node_put(client->dev.of_node); ++ } + + device_unregister(&client->dev); + } +@@ -916,7 +1013,7 @@ static void peci_adapter_dev_release(struct device *dev) + + dev_dbg(dev, "%s: %s\n", __func__, adapter->name); + mutex_destroy(&adapter->userspace_clients_lock); +- rt_mutex_destroy(&adapter->bus_lock); ++ mutex_destroy(&adapter->bus_lock); + kfree(adapter); + } + +@@ -928,7 +1025,8 @@ static ssize_t peci_sysfs_new_device(struct device *dev, + struct peci_board_info info = {}; + struct peci_client *client; + char *blank, end; +- int rc; ++ short addr; ++ int ret; + + /* Parse device type */ + blank = strchr(buf, ' '); +@@ -943,16 +1041,17 @@ static ssize_t peci_sysfs_new_device(struct device *dev, + memcpy(info.type, buf, blank - buf); + + /* Parse remaining parameters, reject extra parameters */ +- rc = sscanf(++blank, "%hi%c", &info.addr, &end); +- if (rc < 1) { ++ ret = sscanf(++blank, "%hi%c", &addr, &end); ++ if (ret < 1) { + dev_err(dev, "%s: Can't parse client address\n", "new_device"); + return -EINVAL; + } +- if (rc > 1 && end != '\n') { ++ if (ret > 1 && end != '\n') { + dev_err(dev, "%s: Extra parameters\n", "new_device"); + return -EINVAL; + } + ++ info.addr = (u8)addr; + client = peci_new_device(adapter, &info); + if (!client) + return -EINVAL; +@@ -961,8 +1060,8 @@ static ssize_t peci_sysfs_new_device(struct device *dev, + mutex_lock(&adapter->userspace_clients_lock); + list_add_tail(&client->detected, &adapter->userspace_clients); + mutex_unlock(&adapter->userspace_clients_lock); +- dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device", +- info.type, info.addr); ++ dev_dbg(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device", ++ info.type, info.addr); + + return count; + } +@@ -975,9 +1074,9 @@ static ssize_t peci_sysfs_delete_device(struct device *dev, + struct peci_adapter *adapter = to_peci_adapter(dev); + struct peci_client *client, *next; + struct peci_board_info info = {}; +- struct peci_driver *driver; + char *blank, end; +- int rc; ++ short addr; ++ int ret; + + /* Parse device type */ + blank = strchr(buf, ' '); +@@ -992,41 +1091,41 @@ static ssize_t peci_sysfs_delete_device(struct device *dev, + memcpy(info.type, buf, blank - buf); + + /* Parse remaining parameters, reject extra parameters */ +- rc = sscanf(++blank, "%hi%c", &info.addr, &end); +- if (rc < 1) { ++ ret = sscanf(++blank, "%hi%c", &addr, &end); ++ if (ret < 1) { + dev_err(dev, "%s: Can't parse client address\n", + "delete_device"); + return -EINVAL; + } +- if (rc > 1 && end != '\n') { ++ if (ret > 1 && end != '\n') { + dev_err(dev, "%s: Extra parameters\n", "delete_device"); + return -EINVAL; + } + ++ info.addr = (u8)addr; ++ + /* Make sure the device was added through sysfs */ +- rc = -ENOENT; ++ ret = -ENOENT; + mutex_lock(&adapter->userspace_clients_lock); + list_for_each_entry_safe(client, next, &adapter->userspace_clients, + detected) { +- driver = to_peci_driver(client->dev.driver); +- + if (client->addr == info.addr && + !strncmp(client->name, info.type, PECI_NAME_SIZE)) { +- dev_info(dev, "%s: Deleting device %s at 0x%02hx\n", +- "delete_device", client->name, client->addr); ++ dev_dbg(dev, "%s: Deleting device %s at 0x%02hx\n", ++ "delete_device", client->name, client->addr); + list_del(&client->detected); + peci_unregister_device(client); +- rc = count; ++ ret = count; + break; + } + } + mutex_unlock(&adapter->userspace_clients_lock); + +- if (rc < 0) +- dev_err(dev, "%s: Can't find device in list\n", ++ if (ret < 0) ++ dev_dbg(dev, "%s: Can't find device in list\n", + "delete_device"); + +- return rc; ++ return ret; + } + static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, 0200, NULL, + peci_sysfs_delete_device); +@@ -1039,10 +1138,11 @@ static struct attribute *peci_adapter_attrs[] = { + }; + ATTRIBUTE_GROUPS(peci_adapter); + +-static struct device_type peci_adapter_type = { ++struct device_type peci_adapter_type = { + .groups = peci_adapter_groups, + .release = peci_adapter_dev_release, + }; ++EXPORT_SYMBOL_GPL(peci_adapter_type); + + /** + * peci_verify_adapter - return parameter as peci_adapter, or NULL +@@ -1063,32 +1163,26 @@ static struct peci_client *peci_of_register_device(struct peci_adapter *adapter, + struct device_node *node) + { + struct peci_board_info info = {}; +- struct peci_client *result; +- const __be32 *addr_be; +- int len; ++ struct peci_client *client; ++ u32 addr; ++ int ret; + + dev_dbg(&adapter->dev, "register %pOF\n", node); + +- if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) { +- dev_err(&adapter->dev, "modalias failure on %pOF\n", node); +- return ERR_PTR(-EINVAL); +- } +- +- addr_be = of_get_property(node, "reg", &len); +- if (!addr_be || len < sizeof(*addr_be)) { ++ ret = of_property_read_u32(node, "reg", &addr); ++ if (ret) { + dev_err(&adapter->dev, "invalid reg on %pOF\n", node); +- return ERR_PTR(-EINVAL); ++ return ERR_PTR(ret); + } + +- info.addr = be32_to_cpup(addr_be); +- info.of_node = of_node_get(node); ++ info.addr = addr; ++ info.of_node = node; + +- result = peci_new_device(adapter, &info); +- if (!result) +- result = ERR_PTR(-EINVAL); ++ client = peci_new_device(adapter, &info); ++ if (!client) ++ client = ERR_PTR(-EINVAL); + +- of_node_put(node); +- return result; ++ return client; + } + + static void peci_of_register_devices(struct peci_adapter *adapter) +@@ -1119,7 +1213,7 @@ static void peci_of_register_devices(struct peci_adapter *adapter) + + of_node_put(bus); + } +-#else ++#else /* CONFIG_OF */ + static void peci_of_register_devices(struct peci_adapter *adapter) { } + #endif /* CONFIG_OF */ + +@@ -1163,9 +1257,7 @@ static struct peci_adapter *peci_of_find_adapter(struct device_node *node) + return adapter; + } + +-static int peci_of_notify(struct notifier_block *nb, +- unsigned long action, +- void *arg) ++static int peci_of_notify(struct notifier_block *nb, ulong action, void *arg) + { + struct of_reconfig_data *rd = arg; + struct peci_adapter *adapter; +@@ -1216,7 +1308,7 @@ static int peci_of_notify(struct notifier_block *nb, + static struct notifier_block peci_of_notifier = { + .notifier_call = peci_of_notify, + }; +-#else ++#else /* CONFIG_OF_DYNAMIC */ + extern struct notifier_block peci_of_notifier; + #endif /* CONFIG_OF_DYNAMIC */ + +@@ -1240,7 +1332,7 @@ extern struct notifier_block peci_of_notifier; + * + * Return: the peci_adapter structure on success, else NULL. + */ +-struct peci_adapter *peci_alloc_adapter(struct device *dev, unsigned int size) ++struct peci_adapter *peci_alloc_adapter(struct device *dev, uint size) + { + struct peci_adapter *adapter; + +@@ -1263,7 +1355,7 @@ EXPORT_SYMBOL_GPL(peci_alloc_adapter); + + static int peci_register_adapter(struct peci_adapter *adapter) + { +- int rc = -EINVAL; ++ int ret = -EINVAL; + + /* Can't register until after driver model init */ + if (WARN_ON(!is_registered)) +@@ -1275,27 +1367,17 @@ static int peci_register_adapter(struct peci_adapter *adapter) + if (WARN(!adapter->xfer, "peci adapter has no xfer function\n")) + goto err_free_idr; + +- rt_mutex_init(&adapter->bus_lock); ++ mutex_init(&adapter->bus_lock); + mutex_init(&adapter->userspace_clients_lock); + INIT_LIST_HEAD(&adapter->userspace_clients); + + dev_set_name(&adapter->dev, "peci-%d", adapter->nr); + +- /* cdev */ +- cdev_init(&adapter->cdev, &peci_fops); +- adapter->cdev.owner = THIS_MODULE; +- adapter->dev.devt = MKDEV(MAJOR(peci_devt), adapter->nr); +- rc = cdev_add(&adapter->cdev, adapter->dev.devt, 1); +- if (rc) { +- pr_err("adapter '%s': can't add cdev (%d)\n", +- adapter->name, rc); +- goto err_free_idr; +- } +- rc = device_add(&adapter->dev); +- if (rc) { ++ ret = device_add(&adapter->dev); ++ if (ret) { + pr_err("adapter '%s': can't add device (%d)\n", +- adapter->name, rc); +- goto err_del_cdev; ++ adapter->name, ret); ++ goto err_free_idr; + } + + dev_dbg(&adapter->dev, "adapter [%s] registered\n", adapter->name); +@@ -1309,13 +1391,11 @@ static int peci_register_adapter(struct peci_adapter *adapter) + + return 0; + +-err_del_cdev: +- cdev_del(&adapter->cdev); + err_free_idr: + mutex_lock(&core_lock); + idr_remove(&peci_adapter_idr, adapter->nr); + mutex_unlock(&core_lock); +- return rc; ++ return ret; + } + + static int peci_add_numbered_adapter(struct peci_adapter *adapter) +@@ -1411,7 +1491,7 @@ void peci_del_adapter(struct peci_adapter *adapter) + } + mutex_unlock(&adapter->userspace_clients_lock); + +- /** ++ /* + * Detach any active clients. This can't fail, thus we do not + * check the returned value. + */ +@@ -1420,13 +1500,8 @@ void peci_del_adapter(struct peci_adapter *adapter) + /* device name is gone after device_unregister */ + dev_dbg(&adapter->dev, "adapter [%s] unregistered\n", adapter->name); + +- /* free cdev */ +- cdev_del(&adapter->cdev); +- + pm_runtime_disable(&adapter->dev); +- + nr = adapter->nr; +- + device_unregister(&adapter->dev); + + /* free bus id */ +@@ -1436,6 +1511,18 @@ void peci_del_adapter(struct peci_adapter *adapter) + } + EXPORT_SYMBOL_GPL(peci_del_adapter); + ++int peci_for_each_dev(void *data, int (*fn)(struct device *, void *)) ++{ ++ int ret; ++ ++ mutex_lock(&core_lock); ++ ret = bus_for_each_dev(&peci_bus_type, NULL, data, fn); ++ mutex_unlock(&core_lock); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(peci_for_each_dev); ++ + /** + * peci_register_driver - register a PECI driver + * @owner: owner module of the driver being registered +@@ -1446,7 +1533,7 @@ EXPORT_SYMBOL_GPL(peci_del_adapter); + */ + int peci_register_driver(struct module *owner, struct peci_driver *driver) + { +- int rc; ++ int ret; + + /* Can't register until after driver model init */ + if (WARN_ON(!is_registered)) +@@ -1456,13 +1543,13 @@ int peci_register_driver(struct module *owner, struct peci_driver *driver) + driver->driver.owner = owner; + driver->driver.bus = &peci_bus_type; + +- /** ++ /* + * When registration returns, the driver core + * will have called probe() for all matching-but-unbound devices. + */ +- rc = driver_register(&driver->driver); +- if (rc) +- return rc; ++ ret = driver_register(&driver->driver); ++ if (ret) ++ return ret; + + pr_debug("driver [%s] registered\n", driver->driver.name); + +@@ -1492,13 +1579,6 @@ static int __init peci_init(void) + return ret; + } + +- ret = alloc_chrdev_region(&peci_devt, 0, PECI_CDEV_MAX, "peci"); +- if (ret < 0) { +- pr_err("peci: Failed to allocate chr dev region!\n"); +- bus_unregister(&peci_bus_type); +- return ret; +- } +- + crc8_populate_msb(peci_crc8_table, PECI_CRC8_POLYNOMIAL); + + if (IS_ENABLED(CONFIG_OF_DYNAMIC)) +@@ -1514,11 +1594,10 @@ static void __exit peci_exit(void) + if (IS_ENABLED(CONFIG_OF_DYNAMIC)) + WARN_ON(of_reconfig_notifier_unregister(&peci_of_notifier)); + +- unregister_chrdev_region(peci_devt, PECI_CDEV_MAX); + bus_unregister(&peci_bus_type); + } + +-postcore_initcall(peci_init); ++subsys_initcall(peci_init); + module_exit(peci_exit); + + MODULE_AUTHOR("Jason M Biils <jason.m.bills@linux.intel.com>"); +diff --git a/drivers/peci/peci-dev.c b/drivers/peci/peci-dev.c +new file mode 100644 +index 000000000000..5de0683206bc +--- /dev/null ++++ b/drivers/peci/peci-dev.c +@@ -0,0 +1,340 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (c) 2018-2019 Intel Corporation ++ ++#include <linux/cdev.h> ++#include <linux/fs.h> ++#include <linux/list.h> ++#include <linux/module.h> ++#include <linux/notifier.h> ++#include <linux/peci.h> ++#include <linux/slab.h> ++#include <linux/uaccess.h> ++ ++/* ++ * A peci_dev represents an peci_adapter ... an PECI or SMBus master, not a ++ * slave (peci_client) with which messages will be exchanged. It's coupled ++ * with a character special file which is accessed by user mode drivers. ++ * ++ * The list of peci_dev structures is parallel to the peci_adapter lists ++ * maintained by the driver model, and is updated using bus notifications. ++ */ ++struct peci_dev { ++ struct list_head list; ++ struct peci_adapter *adapter; ++ struct device *dev; ++ struct cdev cdev; ++}; ++ ++#define PECI_MINORS MINORMASK ++ ++static dev_t peci_devt; ++static LIST_HEAD(peci_dev_list); ++static DEFINE_SPINLOCK(peci_dev_list_lock); ++ ++static struct peci_dev *peci_dev_get_by_minor(uint index) ++{ ++ struct peci_dev *peci_dev; ++ ++ spin_lock(&peci_dev_list_lock); ++ list_for_each_entry(peci_dev, &peci_dev_list, list) { ++ if (peci_dev->adapter->nr == index) ++ goto found; ++ } ++ peci_dev = NULL; ++found: ++ spin_unlock(&peci_dev_list_lock); ++ ++ return peci_dev; ++} ++ ++static struct peci_dev *peci_dev_alloc(struct peci_adapter *adapter) ++{ ++ struct peci_dev *peci_dev; ++ ++ if (adapter->nr >= PECI_MINORS) { ++ printk(KERN_ERR "peci-dev: Out of device minors (%d)\n", ++ adapter->nr); ++ return ERR_PTR(-ENODEV); ++ } ++ ++ peci_dev = kzalloc(sizeof(*peci_dev), GFP_KERNEL); ++ if (!peci_dev) ++ return ERR_PTR(-ENOMEM); ++ peci_dev->adapter = adapter; ++ ++ spin_lock(&peci_dev_list_lock); ++ list_add_tail(&peci_dev->list, &peci_dev_list); ++ spin_unlock(&peci_dev_list_lock); ++ ++ return peci_dev; ++} ++ ++static void peci_dev_put(struct peci_dev *peci_dev) ++{ ++ spin_lock(&peci_dev_list_lock); ++ list_del(&peci_dev->list); ++ spin_unlock(&peci_dev_list_lock); ++ kfree(peci_dev); ++} ++ ++static ssize_t name_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct peci_dev *peci_dev = peci_dev_get_by_minor(MINOR(dev->devt)); ++ ++ if (!peci_dev) ++ return -ENODEV; ++ ++ return sprintf(buf, "%s\n", peci_dev->adapter->name); ++} ++static DEVICE_ATTR_RO(name); ++ ++static struct attribute *peci_dev_attrs[] = { ++ &dev_attr_name.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(peci_dev); ++ ++static long peci_dev_ioctl(struct file *file, uint iocmd, ulong arg) ++{ ++ struct peci_dev *peci_dev = file->private_data; ++ struct peci_xfer_msg __user *uxmsg; ++ struct peci_xfer_msg *xmsg = NULL; ++ void __user *umsg; ++ enum peci_cmd cmd; ++ u8 *msg = NULL; ++ uint msg_len; ++ int ret; ++ ++ cmd = _IOC_NR(iocmd); ++ msg_len = _IOC_SIZE(iocmd); ++ ++ switch (cmd) { ++ case PECI_CMD_XFER: ++ if (msg_len != sizeof(struct peci_xfer_msg)) { ++ ret = -EFAULT; ++ break; ++ } ++ ++ uxmsg = (struct peci_xfer_msg __user *)arg; ++ xmsg = peci_get_xfer_msg(uxmsg->tx_len, uxmsg->rx_len); ++ if (IS_ERR(xmsg)) { ++ ret = PTR_ERR(xmsg); ++ break; ++ } ++ ++ if (uxmsg->tx_len && ++ copy_from_user(uxmsg->tx_buf, xmsg->tx_buf, ++ uxmsg->tx_len)) { ++ ret = -EFAULT; ++ break; ++ } ++ ++ ret = peci_command(peci_dev->adapter, cmd, xmsg); ++ if (!ret && uxmsg->rx_len && ++ copy_to_user(xmsg->rx_buf, uxmsg->rx_buf, ++ uxmsg->rx_len)) ++ ret = -EFAULT; ++ ++ break; ++ ++ default: ++ umsg = (void __user *)arg; ++ msg = memdup_user(umsg, msg_len); ++ if (IS_ERR(msg)) { ++ ret = PTR_ERR(msg); ++ break; ++ } ++ ++ ret = peci_command(peci_dev->adapter, cmd, msg); ++ if (!ret && copy_to_user(umsg, msg, msg_len)) ++ ret = -EFAULT; ++ ++ break; ++ } ++ ++ peci_put_xfer_msg(xmsg); ++ kfree(msg); ++ ++ return (long)ret; ++} ++ ++static int peci_dev_open(struct inode *inode, struct file *file) ++{ ++ struct peci_adapter *adapter; ++ struct peci_dev *peci_dev; ++ ++ peci_dev = peci_dev_get_by_minor(iminor(inode)); ++ if (!peci_dev) ++ return -ENODEV; ++ ++ adapter = peci_get_adapter(peci_dev->adapter->nr); ++ if (!adapter) ++ return -ENODEV; ++ ++ file->private_data = peci_dev; ++ ++ return 0; ++} ++ ++static int peci_dev_release(struct inode *inode, struct file *file) ++{ ++ struct peci_dev *peci_dev = file->private_data; ++ ++ peci_put_adapter(peci_dev->adapter); ++ file->private_data = NULL; ++ ++ return 0; ++} ++ ++static const struct file_operations peci_dev_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = peci_dev_ioctl, ++ .open = peci_dev_open, ++ .release = peci_dev_release, ++ .llseek = no_llseek, ++}; ++ ++static struct class *peci_dev_class; ++ ++static int peci_dev_attach_adapter(struct device *dev, void *dummy) ++{ ++ struct peci_adapter *adapter; ++ struct peci_dev *peci_dev; ++ dev_t devt; ++ int ret; ++ ++ if (dev->type != &peci_adapter_type) ++ return 0; ++ ++ adapter = to_peci_adapter(dev); ++ peci_dev = peci_dev_alloc(adapter); ++ if (IS_ERR(peci_dev)) ++ return PTR_ERR(peci_dev); ++ ++ cdev_init(&peci_dev->cdev, &peci_dev_fops); ++ peci_dev->cdev.owner = THIS_MODULE; ++ devt = MKDEV(MAJOR(peci_devt), adapter->nr); ++ ++ ret = cdev_add(&peci_dev->cdev, devt, 1); ++ if (ret) ++ goto err_put_dev; ++ ++ /* register this peci device with the driver core */ ++ peci_dev->dev = device_create(peci_dev_class, &adapter->dev, devt, NULL, ++ "peci-%d", adapter->nr); ++ if (IS_ERR(peci_dev->dev)) { ++ ret = PTR_ERR(peci_dev->dev); ++ goto err_del_cdev; ++ } ++ ++ pr_info("peci-dev: adapter [%s] registered as minor %d\n", ++ adapter->name, adapter->nr); ++ ++ return 0; ++ ++err_del_cdev: ++ cdev_del(&peci_dev->cdev); ++err_put_dev: ++ peci_dev_put(peci_dev); ++ ++ return ret; ++} ++ ++static int peci_dev_detach_adapter(struct device *dev, void *dummy) ++{ ++ struct peci_adapter *adapter; ++ struct peci_dev *peci_dev; ++ dev_t devt; ++ ++ if (dev->type != &peci_adapter_type) ++ return 0; ++ ++ adapter = to_peci_adapter(dev); ++ peci_dev = peci_dev_get_by_minor(adapter->nr); ++ if (!peci_dev) ++ return 0; ++ ++ cdev_del(&peci_dev->cdev); ++ devt = peci_dev->dev->devt; ++ peci_dev_put(peci_dev); ++ device_destroy(peci_dev_class, devt); ++ ++ pr_info("peci-dev: adapter [%s] unregistered\n", adapter->name); ++ ++ return 0; ++} ++ ++static int peci_dev_notifier_call(struct notifier_block *nb, ulong action, ++ void *data) ++{ ++ struct device *dev = data; ++ ++ switch (action) { ++ case BUS_NOTIFY_ADD_DEVICE: ++ return peci_dev_attach_adapter(dev, NULL); ++ case BUS_NOTIFY_DEL_DEVICE: ++ return peci_dev_detach_adapter(dev, NULL); ++ } ++ ++ return 0; ++} ++ ++static struct notifier_block peci_dev_notifier = { ++ .notifier_call = peci_dev_notifier_call, ++}; ++ ++static int __init peci_dev_init(void) ++{ ++ int ret; ++ ++ printk(KERN_INFO "peci /dev entries driver\n"); ++ ++ ret = alloc_chrdev_region(&peci_devt, 0, PECI_MINORS, "peci"); ++ if (ret < 0) { ++ pr_err("peci: Failed to allocate chr dev region!\n"); ++ bus_unregister(&peci_bus_type); ++ goto err; ++ } ++ ++ peci_dev_class = class_create(THIS_MODULE, "peci-dev"); ++ if (IS_ERR(peci_dev_class)) { ++ ret = PTR_ERR(peci_dev_class); ++ goto err_unreg_chrdev; ++ } ++ peci_dev_class->dev_groups = peci_dev_groups; ++ ++ /* Keep track of adapters which will be added or removed later */ ++ ret = bus_register_notifier(&peci_bus_type, &peci_dev_notifier); ++ if (ret) ++ goto err_destroy_class; ++ ++ /* Bind to already existing adapters right away */ ++ peci_for_each_dev(NULL, peci_dev_attach_adapter); ++ ++ return 0; ++ ++err_destroy_class: ++ class_destroy(peci_dev_class); ++err_unreg_chrdev: ++ unregister_chrdev_region(peci_devt, PECI_MINORS); ++err: ++ printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__); ++ ++ return ret; ++} ++ ++static void __exit peci_dev_exit(void) ++{ ++ bus_unregister_notifier(&peci_bus_type, &peci_dev_notifier); ++ peci_for_each_dev(NULL, peci_dev_detach_adapter); ++ class_destroy(peci_dev_class); ++ unregister_chrdev_region(peci_devt, PECI_MINORS); ++} ++ ++module_init(peci_dev_init); ++module_exit(peci_dev_exit); ++ ++MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>"); ++MODULE_DESCRIPTION("PECI /dev entries driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/include/linux/mfd/intel-peci-client.h b/include/linux/mfd/intel-peci-client.h +index 8f6d823a59cd..1f1b07a9aeab 100644 +--- a/include/linux/mfd/intel-peci-client.h ++++ b/include/linux/mfd/intel-peci-client.h +@@ -1,5 +1,5 @@ + /* SPDX-License-Identifier: GPL-2.0 */ +-/* Copyright (c) 2018 Intel Corporation */ ++/* Copyright (c) 2018-2019 Intel Corporation */ + + #ifndef __LINUX_MFD_INTEL_PECI_CLIENT_H + #define __LINUX_MFD_INTEL_PECI_CLIENT_H +@@ -9,7 +9,7 @@ + #if IS_ENABLED(CONFIG_X86) + #include <asm/intel-family.h> + #else +-/** ++/* + * Architectures other than x86 cannot include the header file so define these + * at here. These are needed for detecting type of client x86 CPUs behind a PECI + * connection. +@@ -58,7 +58,6 @@ struct cpu_gen_info { + /** + * struct peci_client_manager - PECI client manager information + * @client; pointer to the PECI client +- * @dev: pointer to the struct device + * @name: PECI client manager name + * @gen_info: CPU generation info of the detected CPU + * +@@ -67,7 +66,6 @@ struct cpu_gen_info { + */ + struct peci_client_manager { + struct peci_client *client; +- struct device *dev; + char name[PECI_NAME_SIZE]; + const struct cpu_gen_info *gen_info; + }; +diff --git a/include/linux/peci.h b/include/linux/peci.h +index d0e47d45d1d0..6fc424dc2a73 100644 +--- a/include/linux/peci.h ++++ b/include/linux/peci.h +@@ -1,19 +1,18 @@ + /* SPDX-License-Identifier: GPL-2.0 */ +-/* Copyright (c) 2018 Intel Corporation */ ++/* Copyright (c) 2018-2019 Intel Corporation */ + + #ifndef __LINUX_PECI_H + #define __LINUX_PECI_H + +-#include <linux/cdev.h> + #include <linux/device.h> ++#include <linux/mutex.h> + #include <linux/peci-ioctl.h> +-#include <linux/rtmutex.h> + + #define PECI_NAME_SIZE 32 + + struct peci_board_info { + char type[PECI_NAME_SIZE]; +- unsigned short addr; /* CPU client address */ ++ u8 addr; /* CPU client address */ + struct device_node *of_node; + }; + +@@ -22,29 +21,29 @@ struct peci_board_info { + * @owner: owner module of the PECI adpater + * @bus_lock: mutex for exclusion of multiple callers + * @dev: device interface to this driver +- * @cdev: character device object to create character device + * @nr: the bus number to map + * @name: name of the adapter + * @userspace_clients_lock: mutex for exclusion of clients handling + * @userspace_clients: list of registered clients + * @xfer: low-level transfer function pointer of the adapter + * @cmd_mask: mask for supportable PECI commands ++ * @use_dma: flag for indicating that adapter uses DMA + * + * Each PECI adapter can communicate with one or more PECI client children. + * These make a small bus, sharing a single wired PECI connection. + */ + struct peci_adapter { + struct module *owner; +- struct rt_mutex bus_lock; ++ struct mutex bus_lock; + struct device dev; +- struct cdev cdev; + int nr; + char name[PECI_NAME_SIZE]; + struct mutex userspace_clients_lock; /* clients list mutex */ + struct list_head userspace_clients; + int (*xfer)(struct peci_adapter *adapter, + struct peci_xfer_msg *msg); +- uint cmd_mask; ++ u32 cmd_mask; ++ bool use_dma; + }; + + static inline struct peci_adapter *to_peci_adapter(void *d) +@@ -87,8 +86,8 @@ static inline struct peci_client *to_peci_client(void *d) + } + + struct peci_device_id { +- char name[PECI_NAME_SIZE]; +- unsigned long driver_data; /* Data private to the driver */ ++ char name[PECI_NAME_SIZE]; ++ ulong driver_data; /* Data private to the driver */ + }; + + /** +@@ -129,13 +128,22 @@ static inline struct peci_driver *to_peci_driver(void *d) + /* use a define to avoid include chaining to get THIS_MODULE */ + #define peci_add_driver(driver) peci_register_driver(THIS_MODULE, driver) + ++extern struct bus_type peci_bus_type; ++extern struct device_type peci_adapter_type; ++extern struct device_type peci_client_type; ++ + int peci_register_driver(struct module *owner, struct peci_driver *drv); + void peci_del_driver(struct peci_driver *driver); + struct peci_client *peci_verify_client(struct device *dev); +-struct peci_adapter *peci_alloc_adapter(struct device *dev, unsigned int size); ++struct peci_adapter *peci_alloc_adapter(struct device *dev, uint size); ++struct peci_adapter *peci_get_adapter(int nr); ++void peci_put_adapter(struct peci_adapter *adapter); + int peci_add_adapter(struct peci_adapter *adapter); + void peci_del_adapter(struct peci_adapter *adapter); + struct peci_adapter *peci_verify_adapter(struct device *dev); ++int peci_for_each_dev(void *data, int (*fn)(struct device *, void *)); ++struct peci_xfer_msg *peci_get_xfer_msg(u8 tx_len, u8 rx_len); ++void peci_put_xfer_msg(struct peci_xfer_msg *msg); + int peci_command(struct peci_adapter *adpater, enum peci_cmd cmd, void *vmsg); + int peci_get_cpu_id(struct peci_adapter *adapter, u8 addr, u32 *cpu_id); + +diff --git a/include/uapi/linux/peci-ioctl.h b/include/uapi/linux/peci-ioctl.h +index a6dae71cbff5..8467b2fbee1f 100644 +--- a/include/uapi/linux/peci-ioctl.h ++++ b/include/uapi/linux/peci-ioctl.h +@@ -1,5 +1,5 @@ + /* SPDX-License-Identifier: GPL-2.0 */ +-/* Copyright (c) 2018 Intel Corporation */ ++/* Copyright (c) 2018-2019 Intel Corporation */ + + #ifndef __PECI_IOCTL_H + #define __PECI_IOCTL_H +@@ -7,136 +7,34 @@ + #include <linux/ioctl.h> + #include <linux/types.h> + +-/* Base Address of 48d */ +-#define PECI_BASE_ADDR 0x30 /* The PECI client's default address of 0x30 */ +-#define PECI_OFFSET_MAX 8 /* Max numver of CPU clients */ +- +-/* PCI Access */ +-#define MAX_PCI_READ_LEN 24 /* Number of bytes of the PCI Space read */ +- +-#define PCI_BUS0_CPU0 0x00 +-#define PCI_BUS0_CPU1 0x80 +-#define PCI_CPUBUSNO_BUS 0x00 +-#define PCI_CPUBUSNO_DEV 0x08 +-#define PCI_CPUBUSNO_FUNC 0x02 +-#define PCI_CPUBUSNO 0xcc +-#define PCI_CPUBUSNO_1 0xd0 +-#define PCI_CPUBUSNO_VALID 0xd4 +- +-/* Package Identifier Read Parameter Value */ +-#define PKG_ID_CPU_ID 0x0000 /* CPUID Info */ +-#define PKG_ID_PLATFORM_ID 0x0001 /* Platform ID */ +-#define PKG_ID_UNCORE_ID 0x0002 /* Uncore Device ID */ +-#define PKG_ID_MAX_THREAD_ID 0x0003 /* Max Thread ID */ +-#define PKG_ID_MICROCODE_REV 0x0004 /* CPU Microcode Update Revision */ +-#define PKG_ID_MACHINE_CHECK_STATUS 0x0005 /* Machine Check Status */ +- +-/* RdPkgConfig Index */ +-#define MBX_INDEX_CPU_ID 0 /* Package Identifier Read */ +-#define MBX_INDEX_VR_DEBUG 1 /* VR Debug */ +-#define MBX_INDEX_PKG_TEMP_READ 2 /* Package Temperature Read */ +-#define MBX_INDEX_ENERGY_COUNTER 3 /* Energy counter */ +-#define MBX_INDEX_ENERGY_STATUS 4 /* DDR Energy Status */ +-#define MBX_INDEX_WAKE_MODE_BIT 5 /* "Wake on PECI" Mode bit */ +-#define MBX_INDEX_EPI 6 /* Efficient Performance Indication */ +-#define MBX_INDEX_PKG_RAPL_PERF 8 /* Pkg RAPL Performance Status Read */ +-#define MBX_INDEX_PER_CORE_DTS_TEMP 9 /* Per Core DTS Temperature Read */ +-#define MBX_INDEX_DTS_MARGIN 10 /* DTS thermal margin */ +-#define MBX_INDEX_SKT_PWR_THRTL_DUR 11 /* Socket Power Throttled Duration */ +-#define MBX_INDEX_CFG_TDP_CONTROL 12 /* TDP Config Control */ +-#define MBX_INDEX_CFG_TDP_LEVELS 13 /* TDP Config Levels */ +-#define MBX_INDEX_DDR_DIMM_TEMP 14 /* DDR DIMM Temperature */ +-#define MBX_INDEX_CFG_ICCMAX 15 /* Configurable ICCMAX */ +-#define MBX_INDEX_TEMP_TARGET 16 /* Temperature Target Read */ +-#define MBX_INDEX_CURR_CFG_LIMIT 17 /* Current Config Limit */ +-#define MBX_INDEX_DIMM_TEMP_READ 20 /* Package Thermal Status Read */ +-#define MBX_INDEX_DRAM_IMC_TMP_READ 22 /* DRAM IMC Temperature Read */ +-#define MBX_INDEX_DDR_CH_THERM_STAT 23 /* DDR Channel Thermal Status */ +-#define MBX_INDEX_PKG_POWER_LIMIT1 26 /* Package Power Limit1 */ +-#define MBX_INDEX_PKG_POWER_LIMIT2 27 /* Package Power Limit2 */ +-#define MBX_INDEX_TDP 28 /* Thermal design power minimum */ +-#define MBX_INDEX_TDP_HIGH 29 /* Thermal design power maximum */ +-#define MBX_INDEX_TDP_UNITS 30 /* Units for power/energy registers */ +-#define MBX_INDEX_RUN_TIME 31 /* Accumulated Run Time */ +-#define MBX_INDEX_CONSTRAINED_TIME 32 /* Thermally Constrained Time Read */ +-#define MBX_INDEX_TURBO_RATIO 33 /* Turbo Activation Ratio */ +-#define MBX_INDEX_DDR_RAPL_PL1 34 /* DDR RAPL PL1 */ +-#define MBX_INDEX_DDR_PWR_INFO_HIGH 35 /* DRAM Power Info Read (high) */ +-#define MBX_INDEX_DDR_PWR_INFO_LOW 36 /* DRAM Power Info Read (low) */ +-#define MBX_INDEX_DDR_RAPL_PL2 37 /* DDR RAPL PL2 */ +-#define MBX_INDEX_DDR_RAPL_STATUS 38 /* DDR RAPL Performance Status */ +-#define MBX_INDEX_DDR_HOT_ABSOLUTE 43 /* DDR Hottest Dimm Absolute Temp */ +-#define MBX_INDEX_DDR_HOT_RELATIVE 44 /* DDR Hottest Dimm Relative Temp */ +-#define MBX_INDEX_DDR_THROTTLE_TIME 45 /* DDR Throttle Time */ +-#define MBX_INDEX_DDR_THERM_STATUS 46 /* DDR Thermal Status */ +-#define MBX_INDEX_TIME_AVG_TEMP 47 /* Package time-averaged temperature */ +-#define MBX_INDEX_TURBO_RATIO_LIMIT 49 /* Turbo Ratio Limit Read */ +-#define MBX_INDEX_HWP_AUTO_OOB 53 /* HWP Autonomous Out-of-band */ +-#define MBX_INDEX_DDR_WARM_BUDGET 55 /* DDR Warm Power Budget */ +-#define MBX_INDEX_DDR_HOT_BUDGET 56 /* DDR Hot Power Budget */ +-#define MBX_INDEX_PKG_PSYS_PWR_LIM3 57 /* Package/Psys Power Limit3 */ +-#define MBX_INDEX_PKG_PSYS_PWR_LIM1 58 /* Package/Psys Power Limit1 */ +-#define MBX_INDEX_PKG_PSYS_PWR_LIM2 59 /* Package/Psys Power Limit2 */ +-#define MBX_INDEX_PKG_PSYS_PWR_LIM4 60 /* Package/Psys Power Limit4 */ +-#define MBX_INDEX_PERF_LIMIT_REASON 65 /* Performance Limit Reasons */ +- +-/* WrPkgConfig Index */ +-#define MBX_INDEX_DIMM_AMBIENT 19 +-#define MBX_INDEX_DIMM_TEMP 24 ++/* The PECI client's default address of 0x30 */ ++#define PECI_BASE_ADDR 0x30 ++ ++/* Max number of CPU clients */ ++#define PECI_OFFSET_MAX 8 ++ ++/* PECI read/write data buffer size max */ ++#define PECI_BUFFER_SIZE 255 + + /* Device Specific Completion Code (CC) Definition */ +-#define DEV_PECI_CC_SUCCESS 0x40 +-#define DEV_PECI_CC_TIMEOUT 0x80 +-#define DEV_PECI_CC_OUT_OF_RESOURCE 0x81 +-#define DEV_PECI_CC_UNAVAIL_RESOURCE 0x82 +-#define DEV_PECI_CC_INVALID_REQ 0x90 ++#define PECI_DEV_CC_SUCCESS 0x40 ++#define PECI_DEV_CC_TIMEOUT 0x80 ++#define PECI_DEV_CC_OUT_OF_RESOURCE 0x81 ++#define PECI_DEV_CC_UNAVAIL_RESOURCE 0x82 ++#define PECI_DEV_CC_INVALID_REQ 0x90 + + /* Completion Code mask to check retry needs */ +-#define DEV_PECI_CC_RETRY_CHECK_MASK 0xf0 +-#define DEV_PECI_CC_NEED_RETRY 0x80 ++#define PECI_DEV_CC_RETRY_CHECK_MASK 0xf0 ++#define PECI_DEV_CC_NEED_RETRY 0x80 + + /* Skylake EDS says to retry for 250ms */ +-#define DEV_PECI_RETRY_TIME_MS 250 +-#define DEV_PECI_RETRY_INTERVAL_USEC 10000 +-#define DEV_PECI_RETRY_BIT 0x01 +- +-#define GET_TEMP_WR_LEN 1 +-#define GET_TEMP_RD_LEN 2 +-#define GET_TEMP_PECI_CMD 0x01 +- +-#define GET_DIB_WR_LEN 1 +-#define GET_DIB_RD_LEN 8 +-#define GET_DIB_PECI_CMD 0xf7 +- +-#define RDPKGCFG_WRITE_LEN 5 +-#define RDPKGCFG_READ_LEN_BASE 1 +-#define RDPKGCFG_PECI_CMD 0xa1 +- +-#define WRPKGCFG_WRITE_LEN_BASE 6 +-#define WRPKGCFG_READ_LEN 1 +-#define WRPKGCFG_PECI_CMD 0xa5 +- +-#define RDIAMSR_WRITE_LEN 5 +-#define RDIAMSR_READ_LEN 9 +-#define RDIAMSR_PECI_CMD 0xb1 +- +-#define WRIAMSR_PECI_CMD 0xb5 +- +-#define RDPCICFG_WRITE_LEN 6 +-#define RDPCICFG_READ_LEN 5 +-#define RDPCICFG_PECI_CMD 0x61 ++#define PECI_DEV_RETRY_TIME_MS 250 ++#define PECI_DEV_RETRY_INTERVAL_USEC 10000 ++#define PECI_DEV_RETRY_BIT 0x01 + +-#define WRPCICFG_PECI_CMD 0x65 ++#define PECI_WRIAMSR_CMD 0xb5 + +-#define RDPCICFGLOCAL_WRITE_LEN 5 +-#define RDPCICFGLOCAL_READ_LEN_BASE 1 +-#define RDPCICFGLOCAL_PECI_CMD 0xe1 +- +-#define WRPCICFGLOCAL_WRITE_LEN_BASE 6 +-#define WRPCICFGLOCAL_READ_LEN 1 +-#define WRPCICFGLOCAL_PECI_CMD 0xe5 +- +-#define PECI_BUFFER_SIZE 32 ++#define PECI_WRPCICFG_CMD 0x65 + + /** + * enum peci_cmd - PECI client commands +@@ -186,11 +84,12 @@ enum peci_cmd { + * raw PECI transfer + */ + struct peci_xfer_msg { +- __u8 addr; +- __u8 tx_len; +- __u8 rx_len; +- __u8 tx_buf[PECI_BUFFER_SIZE]; +- __u8 rx_buf[PECI_BUFFER_SIZE]; ++ __u8 addr; ++ __u8 tx_len; ++ __u8 rx_len; ++ __u8 padding; ++ __u8 *tx_buf; ++ __u8 *rx_buf; + } __attribute__((__packed__)); + + /** +@@ -202,7 +101,8 @@ struct peci_xfer_msg { + * powered-off, etc. + */ + struct peci_ping_msg { +- __u8 addr; ++ __u8 addr; ++ __u8 padding[3]; + } __attribute__((__packed__)); + + /** +@@ -216,8 +116,13 @@ struct peci_ping_msg { + * command. + */ + struct peci_get_dib_msg { +- __u8 addr; +- __u64 dib; ++#define PECI_GET_DIB_WR_LEN 1 ++#define PECI_GET_DIB_RD_LEN 8 ++#define PECI_GET_DIB_CMD 0xf7 ++ ++ __u8 addr; ++ __u8 padding[3]; ++ __u64 dib; + } __attribute__((__packed__)); + + /** +@@ -232,8 +137,14 @@ struct peci_get_dib_msg { + * below the maximum processor junction temperature. + */ + struct peci_get_temp_msg { +- __u8 addr; +- __s16 temp_raw; ++#define PECI_GET_TEMP_WR_LEN 1 ++#define PECI_GET_TEMP_RD_LEN 2 ++#define PECI_GET_TEMP_CMD 0x01 ++ ++ __u8 addr; ++ __u8 padding0[3]; ++ __s16 temp_raw; ++ __u8 padding1[2]; + } __attribute__((__packed__)); + + /** +@@ -251,11 +162,72 @@ struct peci_get_temp_msg { + * DIMM temperatures and so on. + */ + struct peci_rd_pkg_cfg_msg { +- __u8 addr; +- __u8 index; +- __u16 param; +- __u8 rx_len; +- __u8 pkg_config[4]; ++#define PECI_RDPKGCFG_WRITE_LEN 5 ++#define PECI_RDPKGCFG_READ_LEN_BASE 1 ++#define PECI_RDPKGCFG_CMD 0xa1 ++ ++ __u8 addr; ++ __u8 index; ++#define PECI_MBX_INDEX_CPU_ID 0 /* Package Identifier Read */ ++#define PECI_MBX_INDEX_VR_DEBUG 1 /* VR Debug */ ++#define PECI_MBX_INDEX_PKG_TEMP_READ 2 /* Package Temperature Read */ ++#define PECI_MBX_INDEX_ENERGY_COUNTER 3 /* Energy counter */ ++#define PECI_MBX_INDEX_ENERGY_STATUS 4 /* DDR Energy Status */ ++#define PECI_MBX_INDEX_WAKE_MODE_BIT 5 /* "Wake on PECI" Mode bit */ ++#define PECI_MBX_INDEX_EPI 6 /* Efficient Performance Indication */ ++#define PECI_MBX_INDEX_PKG_RAPL_PERF 8 /* Pkg RAPL Performance Status Read */ ++#define PECI_MBX_INDEX_PER_CORE_DTS_TEMP 9 /* Per Core DTS Temperature Read */ ++#define PECI_MBX_INDEX_DTS_MARGIN 10 /* DTS thermal margin */ ++#define PECI_MBX_INDEX_SKT_PWR_THRTL_DUR 11 /* Socket Power Throttled Duration */ ++#define PECI_MBX_INDEX_CFG_TDP_CONTROL 12 /* TDP Config Control */ ++#define PECI_MBX_INDEX_CFG_TDP_LEVELS 13 /* TDP Config Levels */ ++#define PECI_MBX_INDEX_DDR_DIMM_TEMP 14 /* DDR DIMM Temperature */ ++#define PECI_MBX_INDEX_CFG_ICCMAX 15 /* Configurable ICCMAX */ ++#define PECI_MBX_INDEX_TEMP_TARGET 16 /* Temperature Target Read */ ++#define PECI_MBX_INDEX_CURR_CFG_LIMIT 17 /* Current Config Limit */ ++#define PECI_MBX_INDEX_DIMM_TEMP_READ 20 /* Package Thermal Status Read */ ++#define PECI_MBX_INDEX_DRAM_IMC_TMP_READ 22 /* DRAM IMC Temperature Read */ ++#define PECI_MBX_INDEX_DDR_CH_THERM_STAT 23 /* DDR Channel Thermal Status */ ++#define PECI_MBX_INDEX_PKG_POWER_LIMIT1 26 /* Package Power Limit1 */ ++#define PECI_MBX_INDEX_PKG_POWER_LIMIT2 27 /* Package Power Limit2 */ ++#define PECI_MBX_INDEX_TDP 28 /* Thermal design power minimum */ ++#define PECI_MBX_INDEX_TDP_HIGH 29 /* Thermal design power maximum */ ++#define PECI_MBX_INDEX_TDP_UNITS 30 /* Units for power/energy registers */ ++#define PECI_MBX_INDEX_RUN_TIME 31 /* Accumulated Run Time */ ++#define PECI_MBX_INDEX_CONSTRAINED_TIME 32 /* Thermally Constrained Time Read */ ++#define PECI_MBX_INDEX_TURBO_RATIO 33 /* Turbo Activation Ratio */ ++#define PECI_MBX_INDEX_DDR_RAPL_PL1 34 /* DDR RAPL PL1 */ ++#define PECI_MBX_INDEX_DDR_PWR_INFO_HIGH 35 /* DRAM Power Info Read (high) */ ++#define PECI_MBX_INDEX_DDR_PWR_INFO_LOW 36 /* DRAM Power Info Read (low) */ ++#define PECI_MBX_INDEX_DDR_RAPL_PL2 37 /* DDR RAPL PL2 */ ++#define PECI_MBX_INDEX_DDR_RAPL_STATUS 38 /* DDR RAPL Performance Status */ ++#define PECI_MBX_INDEX_DDR_HOT_ABSOLUTE 43 /* DDR Hottest Dimm Absolute Temp */ ++#define PECI_MBX_INDEX_DDR_HOT_RELATIVE 44 /* DDR Hottest Dimm Relative Temp */ ++#define PECI_MBX_INDEX_DDR_THROTTLE_TIME 45 /* DDR Throttle Time */ ++#define PECI_MBX_INDEX_DDR_THERM_STATUS 46 /* DDR Thermal Status */ ++#define PECI_MBX_INDEX_TIME_AVG_TEMP 47 /* Package time-averaged temperature */ ++#define PECI_MBX_INDEX_TURBO_RATIO_LIMIT 49 /* Turbo Ratio Limit Read */ ++#define PECI_MBX_INDEX_HWP_AUTO_OOB 53 /* HWP Autonomous Out-of-band */ ++#define PECI_MBX_INDEX_DDR_WARM_BUDGET 55 /* DDR Warm Power Budget */ ++#define PECI_MBX_INDEX_DDR_HOT_BUDGET 56 /* DDR Hot Power Budget */ ++#define PECI_MBX_INDEX_PKG_PSYS_PWR_LIM3 57 /* Package/Psys Power Limit3 */ ++#define PECI_MBX_INDEX_PKG_PSYS_PWR_LIM1 58 /* Package/Psys Power Limit1 */ ++#define PECI_MBX_INDEX_PKG_PSYS_PWR_LIM2 59 /* Package/Psys Power Limit2 */ ++#define PECI_MBX_INDEX_PKG_PSYS_PWR_LIM4 60 /* Package/Psys Power Limit4 */ ++#define PECI_MBX_INDEX_PERF_LIMIT_REASON 65 /* Performance Limit Reasons */ ++ ++ __u16 param; ++/* When index is PECI_MBX_INDEX_CPU_ID */ ++#define PECI_PKG_ID_CPU_ID 0x0000 /* CPUID Info */ ++#define PECI_PKG_ID_PLATFORM_ID 0x0001 /* Platform ID */ ++#define PECI_PKG_ID_UNCORE_ID 0x0002 /* Uncore Device ID */ ++#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 */ ++ ++ __u8 rx_len; ++ __u8 padding[3]; ++ __u8 pkg_config[4]; + } __attribute__((__packed__)); + + /** +@@ -272,11 +244,19 @@ struct peci_rd_pkg_cfg_msg { + * may include power limiting, thermal averaging constant programming and so on. + */ + struct peci_wr_pkg_cfg_msg { +- __u8 addr; +- __u8 index; +- __u16 param; +- __u8 tx_len; +- __u32 value; ++#define PECI_WRPKGCFG_WRITE_LEN_BASE 6 ++#define PECI_WRPKGCFG_READ_LEN 1 ++#define PECI_WRPKGCFG_CMD 0xa5 ++ ++ __u8 addr; ++ __u8 index; ++#define PECI_MBX_INDEX_DIMM_AMBIENT 19 ++#define PECI_MBX_INDEX_DIMM_TEMP 24 ++ ++ __u16 param; ++ __u8 tx_len; ++ __u8 padding[3]; ++ __u32 value; + } __attribute__((__packed__)); + + /** +@@ -290,10 +270,34 @@ struct peci_wr_pkg_cfg_msg { + * (MSRs) defined in the processor's Intel Architecture (IA). + */ + struct peci_rd_ia_msr_msg { +- __u8 addr; +- __u8 thread_id; +- __u16 address; +- __u64 value; ++#define PECI_RDIAMSR_WRITE_LEN 5 ++#define PECI_RDIAMSR_READ_LEN 9 ++#define PECI_RDIAMSR_CMD 0xb1 ++ ++ __u8 addr; ++ __u8 thread_id; ++ __u16 address; ++ __u64 value; ++} __attribute__((__packed__)); ++ ++/** ++ * struct peci_wr_ia_msr_msg - WrIAMSR command ++ * @addr: address of the client ++ * @thread_id: ID of the specific logical processor ++ * @address: address of MSR to write to ++ * @tx_len: number of data to be written in bytes ++ * @value: data to be written ++ * ++ * The WrIAMSR() PECI command provides write access to Model Specific Registers ++ * (MSRs) defined in the processor's Intel Architecture (IA). ++ */ ++struct peci_wr_ia_msr_msg { ++ __u8 addr; ++ __u8 thread_id; ++ __u16 address; ++ __u8 tx_len; ++ __u8 padding[3]; ++ __u64 value; + } __attribute__((__packed__)); + + /** +@@ -310,12 +314,52 @@ struct peci_rd_ia_msr_msg { + * processor. + */ + struct peci_rd_pci_cfg_msg { +- __u8 addr; +- __u8 bus; +- __u8 device; +- __u8 function; +- __u16 reg; +- __u8 pci_config[4]; ++#define PECI_RDPCICFG_WRITE_LEN 6 ++#define PECI_RDPCICFG_READ_LEN 5 ++#define PECI_RDPCICFG_READ_LEN_MAX 24 ++#define PECI_RDPCICFG_CMD 0x61 ++ ++ __u8 addr; ++ __u8 bus; ++#define PECI_PCI_BUS0_CPU0 0x00 ++#define PECI_PCI_BUS0_CPU1 0x80 ++#define PECI_PCI_CPUBUSNO_BUS 0x00 ++#define PECI_PCI_CPUBUSNO_DEV 0x08 ++#define PECI_PCI_CPUBUSNO_FUNC 0x02 ++#define PECI_PCI_CPUBUSNO 0xcc ++#define PECI_PCI_CPUBUSNO_1 0xd0 ++#define PECI_PCI_CPUBUSNO_VALID 0xd4 ++ ++ __u8 device; ++ __u8 function; ++ __u16 reg; ++ __u8 padding[2]; ++ __u8 pci_config[4]; ++} __attribute__((__packed__)); ++ ++/** ++ * struct peci_wr_pci_cfg_msg - WrPCIConfig command ++ * @addr: address of the client ++ * @bus: PCI bus number ++ * @device: PCI device number ++ * @function: specific function to write to ++ * @reg: specific register to write to ++ * @tx_len: number of data to be written in bytes ++ * @pci_config: config data to be written ++ * ++ * The RdPCIConfig() command provides sideband write access to the PCI ++ * configuration space maintained in downstream devices external to the ++ * processor. ++ */ ++struct peci_wr_pci_cfg_msg { ++ __u8 addr; ++ __u8 bus; ++ __u8 device; ++ __u8 function; ++ __u16 reg; ++ __u8 tx_len; ++ __u8 padding; ++ __u8 pci_config[4]; + } __attribute__((__packed__)); + + /** +@@ -333,13 +377,18 @@ struct peci_rd_pci_cfg_msg { + * processor IIO and uncore registers within the PCI configuration space. + */ + struct peci_rd_pci_cfg_local_msg { +- __u8 addr; +- __u8 bus; +- __u8 device; +- __u8 function; +- __u16 reg; +- __u8 rx_len; +- __u8 pci_config[4]; ++#define PECI_RDPCICFGLOCAL_WRITE_LEN 5 ++#define PECI_RDPCICFGLOCAL_READ_LEN_BASE 1 ++#define PECI_RDPCICFGLOCAL_CMD 0xe1 ++ ++ __u8 addr; ++ __u8 bus; ++ __u8 device; ++ __u8 function; ++ __u16 reg; ++ __u8 rx_len; ++ __u8 padding[3]; ++ __u8 pci_config[4]; + } __attribute__((__packed__)); + + /** +@@ -357,13 +406,18 @@ struct peci_rd_pci_cfg_local_msg { + * access this space even before BIOS enumeration of the system buses. + */ + struct peci_wr_pci_cfg_local_msg { +- __u8 addr; +- __u8 bus; +- __u8 device; +- __u8 function; +- __u16 reg; +- __u8 tx_len; +- __u32 value; ++#define PECI_WRPCICFGLOCAL_WRITE_LEN_BASE 6 ++#define PECI_WRPCICFGLOCAL_READ_LEN 1 ++#define PECI_WRPCICFGLOCAL_CMD 0xe5 ++ ++ __u8 addr; ++ __u8 bus; ++ __u8 device; ++ __u8 function; ++ __u16 reg; ++ __u8 tx_len; ++ __u8 padding[3]; ++ __u32 value; + } __attribute__((__packed__)); + + #define PECI_IOC_BASE 0xb7 +@@ -389,9 +443,15 @@ struct peci_wr_pci_cfg_local_msg { + #define PECI_IOC_RD_IA_MSR \ + _IOWR(PECI_IOC_BASE, PECI_CMD_RD_IA_MSR, struct peci_rd_ia_msr_msg) + ++#define PECI_IOC_WR_IA_MSR \ ++ _IOWR(PECI_IOC_BASE, PECI_CMD_WR_IA_MSR, struct peci_wr_ia_msr_msg) ++ + #define PECI_IOC_RD_PCI_CFG \ + _IOWR(PECI_IOC_BASE, PECI_CMD_RD_PCI_CFG, struct peci_rd_pci_cfg_msg) + ++#define PECI_IOC_WR_PCI_CFG \ ++ _IOWR(PECI_IOC_BASE, PECI_CMD_WR_PCI_CFG, struct peci_wr_pci_cfg_msg) ++ + #define PECI_IOC_RD_PCI_CFG_LOCAL \ + _IOWR(PECI_IOC_BASE, PECI_CMD_RD_PCI_CFG_LOCAL, \ + struct peci_rd_pci_cfg_local_msg) +-- +2.7.4 + |