From edeea958f026102ce28c8b760f7a96b9ffd7f65a Mon Sep 17 00:00:00 2001 From: Jae Hyun Yoo 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 Signed-off-by: Jason M. Bills --- Documentation/hwmon/peci-cputemp | 34 +- drivers/hwmon/Kconfig | 4 +- drivers/hwmon/peci-cputemp.c | 171 +++--- drivers/hwmon/peci-dimmtemp.c | 184 +++++-- drivers/hwmon/peci-hwmon.h | 9 +- drivers/mfd/Kconfig | 5 +- drivers/mfd/intel-peci-client.c | 51 +- drivers/peci/Kconfig | 46 +- drivers/peci/Makefile | 7 +- drivers/peci/busses/Kconfig | 32 ++ drivers/peci/busses/Makefile | 7 + drivers/peci/busses/peci-aspeed.c | 492 +++++++++++++++++ drivers/peci/busses/peci-npcm.c | 410 +++++++++++++++ drivers/peci/peci-aspeed.c | 505 ------------------ drivers/peci/peci-core.c | 959 +++++++++++++++++++--------------- drivers/peci/peci-dev.c | 346 ++++++++++++ drivers/peci/peci-npcm.c | 410 --------------- include/linux/mfd/intel-peci-client.h | 31 +- include/linux/peci.h | 30 +- include/uapi/linux/peci-ioctl.h | 416 +++++++++------ 20 files changed, 2446 insertions(+), 1703 deletions(-) create mode 100644 drivers/peci/busses/Kconfig create mode 100644 drivers/peci/busses/Makefile create mode 100644 drivers/peci/busses/peci-aspeed.c create mode 100644 drivers/peci/busses/peci-npcm.c delete mode 100644 drivers/peci/peci-aspeed.c create mode 100644 drivers/peci/peci-dev.c delete mode 100644 drivers/peci/peci-npcm.c diff --git a/Documentation/hwmon/peci-cputemp b/Documentation/hwmon/peci-cputemp index 821a925..a3a3e46 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 c0623fa..7399c3c 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1333,7 +1333,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 @@ -1347,7 +1347,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 11880c8..d0d68e8 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 #include @@ -9,18 +9,13 @@ #include #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) -/* The RESOLVED_CORES register in PCU of a client CPU */ -#define REG_RESOLVED_CORES_BUS 1 -#define REG_RESOLVED_CORES_DEVICE 30 -#define REG_RESOLVED_CORES_FUNCTION 3 -#define REG_RESOLVED_CORES_OFFSET 0xB4 - struct temp_group { struct temp_data die; + struct temp_data dts; struct temp_data tcontrol; struct temp_data tthrottle; struct temp_data tjmax; @@ -43,6 +38,7 @@ struct peci_cputemp { enum cputemp_channels { channel_die, + channel_dts, channel_tcontrol, channel_tthrottle, channel_tjmax, @@ -54,6 +50,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 +70,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 +93,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 +125,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 +145,64 @@ static int get_die_temp(struct peci_cputemp *priv) return 0; } +static int get_dts(struct peci_cputemp *priv) +{ + s32 dts_margin; + u8 pkg_cfg[4]; + int ret; + + if (!peci_temp_need_update(&priv->temp.dts)) + return 0; + + ret = peci_client_read_package_config(priv->mgr, + PECI_MBX_INDEX_DTS_MARGIN, 0, + pkg_cfg); + + if (ret) + return ret; + + dts_margin = (pkg_cfg[1] << 8) | pkg_cfg[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 +233,7 @@ static int cputemp_read_string(struct device *dev, return -EOPNOTSUPP; *str = cputemp_label[channel]; + return 0; } @@ -200,26 +242,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 +280,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 +298,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 +311,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,40 +329,43 @@ 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; - msg.bus = REG_RESOLVED_CORES_BUS; - msg.device = REG_RESOLVED_CORES_DEVICE; - msg.function = REG_RESOLVED_CORES_FUNCTION; - msg.reg = REG_RESOLVED_CORES_OFFSET; + msg.bus = 1; + msg.device = 30; + msg.function = 3; + msg.reg = 0xb4; 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 (msg.cc != PECI_DEV_CC_SUCCESS) + ret = -EAGAIN; + 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 +378,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 +398,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; @@ -385,7 +438,7 @@ MODULE_DEVICE_TABLE(platform, peci_cputemp_ids); static struct platform_driver peci_cputemp_driver = { .probe = peci_cputemp_probe, .id_table = peci_cputemp_ids, - .driver = { .name = "peci-cputemp", }, + .driver = { .name = KBUILD_MODNAME, }, }; module_platform_driver(peci_cputemp_driver); diff --git a/drivers/hwmon/peci-dimmtemp.c b/drivers/hwmon/peci-dimmtemp.c index 86a45a9..a404b6e 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 #include @@ -21,6 +21,8 @@ struct peci_dimmtemp { struct workqueue_struct *work_queue; struct delayed_work work_handler; struct temp_data temp[DIMM_NUMS_MAX]; + long temp_max[DIMM_NUMS_MAX]; + long temp_crit[DIMM_NUMS_MAX]; u32 dimm_mask; int retry_count; u32 temp_config[DIMM_NUMS_MAX + 1]; @@ -44,20 +46,106 @@ 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; + struct peci_rd_pci_cfg_local_msg rp_msg; 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; + switch (priv->gen_info->model) { + case INTEL_FAM6_SKYLAKE_X: + rp_msg.addr = priv->mgr->client->addr; + rp_msg.bus = 2; + /* + * Device 10, Function 2: IMC 0 channel 0 -> rank 0 + * Device 10, Function 6: IMC 0 channel 1 -> rank 1 + * Device 11, Function 2: IMC 0 channel 2 -> rank 2 + * Device 12, Function 2: IMC 1 channel 0 -> rank 3 + * Device 12, Function 6: IMC 1 channel 1 -> rank 4 + * Device 13, Function 2: IMC 1 channel 2 -> rank 5 + */ + rp_msg.device = 10 + chan_rank / 3 * 2 + + (chan_rank % 3 == 2 ? 1 : 0); + rp_msg.function = chan_rank % 3 == 1 ? 6 : 2; + rp_msg.reg = 0x120 + dimm_order * 4; + rp_msg.rx_len = 4; + + ret = peci_command(priv->mgr->client->adapter, + PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg); + if (rp_msg.cc != PECI_DEV_CC_SUCCESS) + ret = -EAGAIN; + if (ret) + return ret; + + priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000; + priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000; + break; + case INTEL_FAM6_SKYLAKE_XD: + rp_msg.addr = priv->mgr->client->addr; + rp_msg.bus = 2; + /* + * Device 10, Function 2: IMC 0 channel 0 -> rank 0 + * Device 10, Function 6: IMC 0 channel 1 -> rank 1 + * Device 12, Function 2: IMC 1 channel 0 -> rank 2 + * Device 12, Function 6: IMC 1 channel 1 -> rank 3 + */ + rp_msg.device = 10 + chan_rank / 2 * 2; + rp_msg.function = chan_rank % 2 ? 6 : 2; + rp_msg.reg = 0x120 + dimm_order * 4; + rp_msg.rx_len = 4; + + ret = peci_command(priv->mgr->client->adapter, + PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg); + if (rp_msg.cc != PECI_DEV_CC_SUCCESS) + ret = -EAGAIN; + if (ret) + return ret; + + priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000; + priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000; + break; + case INTEL_FAM6_HASWELL_X: + case INTEL_FAM6_BROADWELL_X: + rp_msg.addr = priv->mgr->client->addr; + rp_msg.bus = 1; + /* + * Device 20, Function 0: IMC 0 channel 0 -> rank 0 + * Device 20, Function 1: IMC 0 channel 1 -> rank 1 + * Device 21, Function 0: IMC 0 channel 2 -> rank 2 + * Device 21, Function 1: IMC 0 channel 3 -> rank 3 + * Device 23, Function 0: IMC 1 channel 0 -> rank 4 + * Device 23, Function 1: IMC 1 channel 1 -> rank 5 + * Device 24, Function 0: IMC 1 channel 2 -> rank 6 + * Device 24, Function 1: IMC 1 channel 3 -> rank 7 + */ + rp_msg.device = 20 + chan_rank / 2 + chan_rank / 4; + rp_msg.function = chan_rank % 2; + rp_msg.reg = 0x120 + dimm_order * 4; + rp_msg.rx_len = 4; + + ret = peci_command(priv->mgr->client->adapter, + PECI_CMD_RD_PCI_CFG_LOCAL, &rp_msg); + if (rp_msg.cc != PECI_DEV_CC_SUCCESS) + ret = -EAGAIN; + if (ret) + return ret; + + priv->temp_max[dimm_no] = rp_msg.pci_config[1] * 1000; + priv->temp_crit[dimm_no] = rp_msg.pci_config[2] * 1000; + break; + default: + return -EOPNOTSUPP; + } + peci_temp_mark_updated(&priv->temp[dimm_no]); return 0; @@ -77,6 +165,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,17 +173,28 @@ 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; - - if (attr != hwmon_temp_input) - return -EOPNOTSUPP; - - rc = get_dimm_temp(priv, channel); - if (rc) - return rc; + int ret; + + ret = get_dimm_temp(priv, channel); + if (ret) + return ret; + + switch (attr) { + case hwmon_temp_input: + *val = priv->temp[channel].value; + break; + case hwmon_temp_max: + *val = priv->temp_max[channel]; + break; + case hwmon_temp_crit: + *val = priv->temp_crit[channel]; + break; + default: + ret = -EOPNOTSUPP; + break; + } - *val = priv->temp[channel].value; - return 0; + return ret; } static umode_t dimmtemp_is_visible(const void *data, @@ -120,16 +220,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 +243,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 +265,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 * @@ -177,7 +278,8 @@ static int create_dimm_temp_info(struct peci_dimmtemp *priv) if (priv->dimm_mask & BIT(i)) while (i >= config_idx) priv->temp_config[config_idx++] = - HWMON_T_LABEL | HWMON_T_INPUT; + HWMON_T_LABEL | HWMON_T_INPUT | + HWMON_T_MAX | HWMON_T_CRIT; priv->chip.ops = &dimmtemp_ops; priv->chip.info = priv->info; @@ -192,12 +294,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 +307,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 +319,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 +344,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 +354,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) @@ -275,7 +377,7 @@ static struct platform_driver peci_dimmtemp_driver = { .probe = peci_dimmtemp_probe, .remove = peci_dimmtemp_remove, .id_table = peci_dimmtemp_ids, - .driver = { .name = "peci-dimmtemp", }, + .driver = { .name = KBUILD_MODNAME, }, }; module_platform_driver(peci_dimmtemp_driver); diff --git a/drivers/hwmon/peci-hwmon.h b/drivers/hwmon/peci-hwmon.h index 6ca1855..ce6b470 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 5d89546..46f52a3 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -630,7 +630,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 @@ -643,6 +643,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 d53e4f1..18bf0af 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 #include #include #include -#include #include +#include #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,38 +25,45 @@ 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, .chan_rank_max = CHAN_RANK_MAX_ON_SKX, .dimm_idx_max = DIMM_IDX_MAX_ON_SKX }, + { /* Skylake Xeon D */ + .family = 6, /* Family code */ + .model = INTEL_FAM6_SKYLAKE_XD, + .core_max = CORE_MAX_ON_SKXD, + .chan_rank_max = CHAN_RANK_MAX_ON_SKXD, + .dimm_idx_max = DIMM_IDX_MAX_ON_SKXD }, }; 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 +84,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 +104,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" }, @@ -139,7 +138,7 @@ static struct peci_driver peci_client_driver = { .probe = peci_client_probe, .id_table = peci_client_ids, .driver = { - .name = "peci-client", + .name = KBUILD_MODNAME, .of_match_table = of_match_ptr(peci_client_of_table), }, }; diff --git a/drivers/peci/Kconfig b/drivers/peci/Kconfig index 7293108..9752fee 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,37 +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 -# - -menu "PECI Hardware Bus support" - -config PECI_ASPEED - tristate "ASPEED PECI support" - select REGMAP_MMIO - depends on OF - depends on ARCH_ASPEED || COMPILE_TEST - 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-core. - This support is also available as a module. If so, the module - will be called peci-aspeed. +if PECI -config PECI_NPCM - tristate "Nuvoton NPCM PECI support" - select REGMAP_MMIO - depends on OF - depends on ARCH_NPCM || 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 Nuvoton NPCM 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-npcm. -endmenu + will be called peci-dev. + +source "drivers/peci/busses/Kconfig" endif # PECI + +endmenu diff --git a/drivers/peci/Makefile b/drivers/peci/Makefile index 3326da5..da8b0a3 100644 --- a/drivers/peci/Makefile +++ b/drivers/peci/Makefile @@ -1,10 +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-$(CONFIG_PECI_NPCM) += peci-npcm.o +obj-y += busses/ diff --git a/drivers/peci/busses/Kconfig b/drivers/peci/busses/Kconfig new file mode 100644 index 0000000..bfacafb --- /dev/null +++ b/drivers/peci/busses/Kconfig @@ -0,0 +1,32 @@ +# +# 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. + +config PECI_NPCM + tristate "Nuvoton NPCM PECI support" + select REGMAP_MMIO + depends on OF + depends on ARCH_NPCM || COMPILE_TEST + depends on PECI + help + Say Y here if you want support for the Platform Environment Control + Interface (PECI) bus adapter driver on the Nuvoton NPCM SoCs. + + This support is also available as a module. If so, the module + will be called peci-npcm. + +endmenu diff --git a/drivers/peci/busses/Makefile b/drivers/peci/busses/Makefile new file mode 100644 index 0000000..aa8ce3a --- /dev/null +++ b/drivers/peci/busses/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the PECI hardware bus drivers. +# + +obj-$(CONFIG_PECI_ASPEED) += peci-aspeed.o +obj-$(CONFIG_PECI_NPCM) += peci-npcm.o diff --git a/drivers/peci/busses/peci-aspeed.c b/drivers/peci/busses/peci-aspeed.c new file mode 100644 index 0000000..851b71e3 --- /dev/null +++ b/drivers/peci/busses/peci-aspeed.c @@ -0,0 +1,492 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2012-2017 ASPEED Technology Inc. +// Copyright (c) 2018-2019 Intel Corporation + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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; + 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); + + priv->base = devm_platform_ioremap_resource(pdev, 0); + 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\n"); + 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 = KBUILD_MODNAME, + .of_match_table = of_match_ptr(aspeed_peci_of_table), + }, +}; +module_platform_driver(aspeed_peci_driver); + +MODULE_AUTHOR("Ryan Chen "); +MODULE_AUTHOR("Jae Hyun Yoo "); +MODULE_DESCRIPTION("ASPEED PECI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/peci/busses/peci-npcm.c b/drivers/peci/busses/peci-npcm.c new file mode 100644 index 0000000..f632365 --- /dev/null +++ b/drivers/peci/busses/peci-npcm.c @@ -0,0 +1,410 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Nuvoton Technology corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* NPCM7xx GCR module */ +#define NPCM7XX_INTCR3_OFFSET 0x9C +#define NPCM7XX_INTCR3_PECIVSEL BIT(19) + +/* NPCM PECI Registers */ +#define NPCM_PECI_CTL_STS 0x00 +#define NPCM_PECI_RD_LENGTH 0x04 +#define NPCM_PECI_ADDR 0x08 +#define NPCM_PECI_CMD 0x0C +#define NPCM_PECI_CTL2 0x10 +#define NPCM_PECI_WR_LENGTH 0x1C +#define NPCM_PECI_PDDR 0x2C +#define NPCM_PECI_DAT_INOUT(n) (0x100 + ((n) * 4)) + +#define NPCM_PECI_MAX_REG 0x200 + +/* NPCM_PECI_CTL_STS - 0x00 : Control Register */ +#define NPCM_PECI_CTRL_DONE_INT_EN BIT(6) +#define NPCM_PECI_CTRL_ABRT_ERR BIT(4) +#define NPCM_PECI_CTRL_CRC_ERR BIT(3) +#define NPCM_PECI_CTRL_DONE BIT(1) +#define NPCM_PECI_CTRL_START_BUSY BIT(0) + +/* NPCM_PECI_RD_LENGTH - 0x04 : Command Register */ +#define NPCM_PECI_RD_LEN_MASK GENMASK(6, 0) + +/* NPCM_PECI_CMD - 0x10 : Command Register */ +#define NPCM_PECI_CTL2_MASK GENMASK(7, 6) + +/* NPCM_PECI_WR_LENGTH - 0x1C : Command Register */ +#define NPCM_PECI_WR_LEN_MASK GENMASK(6, 0) + +/* NPCM_PECI_PDDR - 0x2C : Command Register */ +#define NPCM_PECI_PDDR_MASK GENMASK(4, 0) + +#define NPCM_PECI_INT_MASK (NPCM_PECI_CTRL_ABRT_ERR | \ + NPCM_PECI_CTRL_CRC_ERR | \ + NPCM_PECI_CTRL_DONE) + +#define NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC 50000 +#define NPCM_PECI_IDLE_CHECK_INTERVAL_USEC 10000 +#define NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT 1000 +#define NPCM_PECI_CMD_TIMEOUT_MS_MAX 60000 +#define NPCM_PECI_HOST_NEG_BIT_RATE_MAX 31 +#define NPCM_PECI_HOST_NEG_BIT_RATE_MIN 7 +#define NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT 15 +#define NPCM_PECI_PULL_DOWN_DEFAULT 0 +#define NPCM_PECI_PULL_DOWN_MAX 2 + +struct npcm_peci { + u32 cmd_timeout_ms; + u32 host_bit_rate; + struct completion xfer_complete; + struct regmap *gcr_regmap; + struct peci_adapter *adapter; + struct regmap *regmap; + u32 status; + spinlock_t lock; /* to sync completion status handling */ + struct device *dev; + struct clk *clk; + int irq; +}; + +static int npcm_peci_xfer_native(struct npcm_peci *priv, + struct peci_xfer_msg *msg) +{ + long err, timeout = msecs_to_jiffies(priv->cmd_timeout_ms); + unsigned long flags; + unsigned int msg_rd; + u32 cmd_sts; + int i, rc; + + /* Check command sts and bus idle state */ + rc = regmap_read_poll_timeout(priv->regmap, NPCM_PECI_CTL_STS, cmd_sts, + !(cmd_sts & NPCM_PECI_CTRL_START_BUSY), + NPCM_PECI_IDLE_CHECK_INTERVAL_USEC, + NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC); + if (rc) + return rc; /* -ETIMEDOUT */ + + spin_lock_irqsave(&priv->lock, flags); + reinit_completion(&priv->xfer_complete); + + regmap_write(priv->regmap, NPCM_PECI_ADDR, msg->addr); + regmap_write(priv->regmap, NPCM_PECI_RD_LENGTH, + NPCM_PECI_WR_LEN_MASK & msg->rx_len); + regmap_write(priv->regmap, NPCM_PECI_WR_LENGTH, + NPCM_PECI_WR_LEN_MASK & msg->tx_len); + + if (msg->tx_len) { + regmap_write(priv->regmap, NPCM_PECI_CMD, msg->tx_buf[0]); + + for (i = 0; i < (msg->tx_len - 1); i++) + regmap_write(priv->regmap, NPCM_PECI_DAT_INOUT(i), + msg->tx_buf[i + 1]); + } + + priv->status = 0; + regmap_update_bits(priv->regmap, NPCM_PECI_CTL_STS, + NPCM_PECI_CTRL_START_BUSY, + NPCM_PECI_CTRL_START_BUSY); + + spin_unlock_irqrestore(&priv->lock, flags); + + err = wait_for_completion_interruptible_timeout(&priv->xfer_complete, + timeout); + + spin_lock_irqsave(&priv->lock, flags); + + regmap_write(priv->regmap, NPCM_PECI_CMD, 0); + + if (err <= 0 || priv->status != NPCM_PECI_CTRL_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; + } + + for (i = 0; i < msg->rx_len; i++) { + regmap_read(priv->regmap, NPCM_PECI_DAT_INOUT(i), &msg_rd); + msg->rx_buf[i] = (u8)msg_rd; + } + +err_irqrestore: + spin_unlock_irqrestore(&priv->lock, flags); + return rc; +} + +static irqreturn_t npcm_peci_irq_handler(int irq, void *arg) +{ + struct npcm_peci *priv = arg; + u32 status_ack = 0; + u32 status; + + spin_lock(&priv->lock); + regmap_read(priv->regmap, NPCM_PECI_CTL_STS, &status); + priv->status |= (status & NPCM_PECI_INT_MASK); + + if (status & NPCM_PECI_CTRL_CRC_ERR) { + dev_dbg(priv->dev, "PECI_INT_W_FCS_BAD\n"); + status_ack |= NPCM_PECI_CTRL_CRC_ERR; + } + + if (status & NPCM_PECI_CTRL_ABRT_ERR) { + dev_dbg(priv->dev, "NPCM_PECI_CTRL_ABRT_ERR\n"); + status_ack |= NPCM_PECI_CTRL_ABRT_ERR; + } + + /* + * All commands should be ended up with a NPCM_PECI_CTRL_DONE + * bit set even in an error case. + */ + if (status & NPCM_PECI_CTRL_DONE) { + dev_dbg(priv->dev, "NPCM_PECI_CTRL_DONE\n"); + status_ack |= NPCM_PECI_CTRL_DONE; + complete(&priv->xfer_complete); + } + + regmap_write_bits(priv->regmap, NPCM_PECI_CTL_STS, + NPCM_PECI_INT_MASK, status_ack); + + spin_unlock(&priv->lock); + return IRQ_HANDLED; +} + +static int npcm_peci_init_ctrl(struct npcm_peci *priv) +{ + u32 cmd_sts, host_neg_bit_rate = 0, pull_down = 0; + int ret; + bool volt; + + 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, "cmd-timeout-ms", + &priv->cmd_timeout_ms); + if (ret || priv->cmd_timeout_ms > NPCM_PECI_CMD_TIMEOUT_MS_MAX || + priv->cmd_timeout_ms == 0) { + if (ret) + dev_warn(priv->dev, + "cmd-timeout-ms not found, use default : %u\n", + NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT); + else + dev_warn(priv->dev, + "Invalid cmd-timeout-ms : %u. Use default : %u\n", + priv->cmd_timeout_ms, + NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT); + + priv->cmd_timeout_ms = NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT; + } + + if (of_device_is_compatible(priv->dev->of_node, + "nuvoton,npcm750-peci")) { + priv->gcr_regmap = syscon_regmap_lookup_by_compatible + ("nuvoton,npcm750-gcr"); + if (!IS_ERR(priv->gcr_regmap)) { + volt = of_property_read_bool(priv->dev->of_node, + "high-volt-range"); + if (volt) + regmap_update_bits(priv->gcr_regmap, + NPCM7XX_INTCR3_OFFSET, + NPCM7XX_INTCR3_PECIVSEL, + NPCM7XX_INTCR3_PECIVSEL); + else + regmap_update_bits(priv->gcr_regmap, + NPCM7XX_INTCR3_OFFSET, + NPCM7XX_INTCR3_PECIVSEL, 0); + } + } + + ret = of_property_read_u32(priv->dev->of_node, "pull-down", + &pull_down); + if (ret || pull_down > NPCM_PECI_PULL_DOWN_MAX) { + if (ret) + dev_warn(priv->dev, + "pull-down not found, use default : %u\n", + NPCM_PECI_PULL_DOWN_DEFAULT); + else + dev_warn(priv->dev, + "Invalid pull-down : %u. Use default : %u\n", + pull_down, + NPCM_PECI_PULL_DOWN_DEFAULT); + pull_down = NPCM_PECI_PULL_DOWN_DEFAULT; + } + + regmap_update_bits(priv->regmap, NPCM_PECI_CTL2, NPCM_PECI_CTL2_MASK, + pull_down << 6); + + ret = of_property_read_u32(priv->dev->of_node, "host-neg-bit-rate", + &host_neg_bit_rate); + if (ret || host_neg_bit_rate > NPCM_PECI_HOST_NEG_BIT_RATE_MAX || + host_neg_bit_rate < NPCM_PECI_HOST_NEG_BIT_RATE_MIN) { + if (ret) + dev_warn(priv->dev, + "host-neg-bit-rate not found, use default : %u\n", + NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT); + else + dev_warn(priv->dev, + "Invalid host-neg-bit-rate : %u. Use default : %u\n", + host_neg_bit_rate, + NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT); + host_neg_bit_rate = NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT; + } + + regmap_update_bits(priv->regmap, NPCM_PECI_PDDR, NPCM_PECI_PDDR_MASK, + host_neg_bit_rate); + + priv->host_bit_rate = clk_get_rate(priv->clk) / + (4 * (host_neg_bit_rate + 1)); + + ret = regmap_read_poll_timeout(priv->regmap, NPCM_PECI_CTL_STS, cmd_sts, + !(cmd_sts & NPCM_PECI_CTRL_START_BUSY), + NPCM_PECI_IDLE_CHECK_INTERVAL_USEC, + NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC); + if (ret) + return ret; /* -ETIMEDOUT */ + + /* PECI interrupt enable */ + regmap_update_bits(priv->regmap, NPCM_PECI_CTL_STS, + NPCM_PECI_CTRL_DONE_INT_EN, + NPCM_PECI_CTRL_DONE_INT_EN); + + return 0; +} + +static const struct regmap_config npcm_peci_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = NPCM_PECI_MAX_REG, + .fast_io = true, +}; + +static int npcm_peci_xfer(struct peci_adapter *adapter, + struct peci_xfer_msg *msg) +{ + struct npcm_peci *priv = peci_get_adapdata(adapter); + + return npcm_peci_xfer_native(priv, msg); +} + +static int npcm_peci_probe(struct platform_device *pdev) +{ + struct peci_adapter *adapter; + struct npcm_peci *priv; + struct resource *res; + void __iomem *base; + 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, + &npcm_peci_regmap_config); + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + 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, npcm_peci_irq_handler, + 0, "peci-npcm-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 = npcm_peci_xfer; + + ret = npcm_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, host negotiation bit rate %dHz", + priv->adapter->nr, priv->host_bit_rate); + + return 0; + +err_put_adapter_dev: + put_device(&adapter->dev); + return ret; +} + +static int npcm_peci_remove(struct platform_device *pdev) +{ + struct npcm_peci *priv = dev_get_drvdata(&pdev->dev); + + clk_disable_unprepare(priv->clk); + peci_del_adapter(priv->adapter); + of_node_put(priv->adapter->dev.of_node); + + return 0; +} + +static const struct of_device_id npcm_peci_of_table[] = { + { .compatible = "nuvoton,npcm750-peci", }, + { } +}; +MODULE_DEVICE_TABLE(of, npcm_peci_of_table); + +static struct platform_driver npcm_peci_driver = { + .probe = npcm_peci_probe, + .remove = npcm_peci_remove, + .driver = { + .name = "peci-npcm", + .of_match_table = of_match_ptr(npcm_peci_of_table), + }, +}; +module_platform_driver(npcm_peci_driver); + +MODULE_AUTHOR("Tomer Maimon "); +MODULE_DESCRIPTION("NPCM Platform Environment Control Interface (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 51cb256..0000000 --- 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* 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 "); -MODULE_AUTHOR("Jae Hyun Yoo "); -MODULE_DESCRIPTION("ASPEED PECI driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/peci/peci-core.c b/drivers/peci/peci-core.c index 6f24146..2a6be04 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 #include #include -#include +#include #include #include #include #include #include +#include #include -#include /* 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,51 @@ 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) - break; - - if (msg->rx_buf[0] == DEV_PECI_CC_SUCCESS) + if (!do_retry || ret || !msg->rx_buf) 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) 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 +284,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 +340,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 +363,130 @@ 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; + u8 aw_fcs; + int ret; + + if (!msg->tx_len) { + ret = peci_xfer(adapter, msg); + } else { + switch (msg->tx_buf[0]) { + case PECI_RDPKGCFG_CMD: + case PECI_RDIAMSR_CMD: + case PECI_RDPCICFG_CMD: + case PECI_RDPCICFGLOCAL_CMD: + ret = peci_xfer_with_retries(adapter, msg, false); + break; + case PECI_WRPKGCFG_CMD: + case PECI_WRIAMSR_CMD: + case PECI_WRPCICFG_CMD: + case PECI_WRPCICFGLOCAL_CMD: + /* Check if the AW FCS byte is already provided */ + ret = peci_aw_fcs(msg, 2 + msg->tx_len, &aw_fcs); + if (ret) + break; + + if (msg->tx_buf[msg->tx_len - 1] != (0x80 ^ aw_fcs)) { + /* Add an Assured Write Frame Check Sequence byte */ + /* Increment the tx_len to include the new byte */ + msg->tx_len++; + ret = peci_aw_fcs(msg, 2 + msg->tx_len, + &aw_fcs); + if (ret) + break; + + msg->tx_buf[msg->tx_len - 1] = 0x80 ^ aw_fcs; + } + + ret = peci_xfer_with_retries(adapter, msg, true); + break; + case PECI_GET_DIB_CMD: + case PECI_GET_TEMP_CMD: + default: + ret = peci_xfer(adapter, msg); + break; + } + } - return peci_xfer(adapter, msg); + return ret; } -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 = peci_get_xfer_msg(0, 0); + if (!msg) + return -ENOMEM; - msg.addr = umsg->addr; - msg.tx_len = 0; - msg.rx_len = 0; + msg->addr = umsg->addr; - return peci_xfer(adapter, &msg); + 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 +495,35 @@ 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); + umsg->cc = msg->rx_buf[0]; + 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 +532,116 @@ 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: + umsg->cc = msg->rx_buf[0]; + 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)); + + umsg->cc = msg->rx_buf[0]; + peci_put_xfer_msg(msg); + + return ret; } -static int peci_ioctl_rd_pci_cfg(struct peci_adapter *adapter, void *vmsg) +static int peci_cmd_wr_ia_msr(struct peci_adapter *adapter, void *vmsg) +{ + return -ENOSYS; /* Not implemented yet */ +} + +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); + + umsg->cc = msg->rx_buf[0]; + peci_put_xfer_msg(msg); - rc = peci_xfer_with_retries(adapter, &msg, false); - if (!rc) - memcpy(umsg->pci_config, &msg.rx_buf[1], 4); + return ret; +} - return rc; +static int peci_cmd_wr_pci_cfg(struct peci_adapter *adapter, void *vmsg) +{ + return -ENOSYS; /* Not implemented yet */ } -static int peci_ioctl_rd_pci_cfg_local(struct peci_adapter *adapter, void *vmsg) +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 +650,42 @@ 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); + umsg->cc = msg->rx_buf[0]; + 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 +694,57 @@ 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; - /* Add an Assure Write Frame Check Sequence byte */ - msg.tx_buf[5 + i] = 0x80 ^ - peci_aw_fcs((u8 *)&msg, 8 + umsg->tx_len); + msg->tx_buf[5 + i] = 0x80 ^ aw_fcs; - rc = peci_xfer_with_retries(adapter, &msg, true); + ret = peci_xfer_with_retries(adapter, msg, true); - return rc; +out: + umsg->cc = msg->rx_buf[0]; + peci_put_xfer_msg(msg); + + 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 +760,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(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 +800,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 +871,7 @@ static int peci_device_probe(struct device *dev) err_detach_pm_domain: dev_pm_domain_detach(&client->dev, true); + return status; } @@ -775,13 +910,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 +950,22 @@ 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) - *cpu_id = le32_to_cpup((__le32 *)msg.pkg_config); + ret = peci_command(adapter, PECI_CMD_RD_PKG_CFG, &msg); + if (msg.cc != PECI_DEV_CC_SUCCESS) + ret = -EAGAIN; + if (ret) + return ret; + + *cpu_id = le32_to_cpup((__le32 *)msg.pkg_config); - return rc; + return 0; } EXPORT_SYMBOL_GPL(peci_get_cpu_id); @@ -833,7 +973,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 +987,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 +1038,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 +1061,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 +1073,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 +1089,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 +1108,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 +1122,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 +1139,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 +1186,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 +1211,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 +1261,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 +1305,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 +1356,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 +1380,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 +1403,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 +1415,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 +1439,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) @@ -1354,12 +1482,10 @@ int peci_add_adapter(struct peci_adapter *adapter) struct device *dev = &adapter->dev; int id; - if (dev->of_node) { - id = of_alias_get_id(dev->of_node, "peci"); - if (id >= 0) { - adapter->nr = id; - return peci_add_numbered_adapter(adapter); - } + id = of_alias_get_id(dev->of_node, "peci"); + if (id >= 0) { + adapter->nr = id; + return peci_add_numbered_adapter(adapter); } mutex_lock(&core_lock); @@ -1411,7 +1537,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 +1546,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 +1557,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 +1579,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 +1589,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 +1625,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 +1640,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 "); diff --git a/drivers/peci/peci-dev.c b/drivers/peci/peci-dev.c new file mode 100644 index 0000000..ac9cba0 --- /dev/null +++ b/drivers/peci/peci-dev.c @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018-2019 Intel Corporation + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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; + void __user *umsg = (void __user *)arg; + struct peci_xfer_msg *xmsg = NULL; + struct peci_xfer_msg uxmsg; + 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; + } + + if (copy_from_user(&uxmsg, umsg, msg_len)) { + ret = -EFAULT; + break; + } + + 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(xmsg->tx_buf, uxmsg.tx_buf, uxmsg.tx_len)) { + ret = -EFAULT; + break; + } + + xmsg->addr = uxmsg.addr; + xmsg->tx_len = uxmsg.tx_len; + xmsg->rx_len = uxmsg.rx_len; + + ret = peci_command(peci_dev->adapter, cmd, xmsg); + if (!ret && xmsg->rx_len && + copy_to_user(uxmsg.rx_buf, xmsg->rx_buf, xmsg->rx_len)) + ret = -EFAULT; + + break; + + default: + 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 || ret == -ETIMEDOUT) && + 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 "); +MODULE_DESCRIPTION("PECI /dev entries driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/peci/peci-npcm.c b/drivers/peci/peci-npcm.c deleted file mode 100644 index f632365..0000000 --- a/drivers/peci/peci-npcm.c +++ /dev/null @@ -1,410 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// Copyright (c) 2019 Nuvoton Technology corporation. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* NPCM7xx GCR module */ -#define NPCM7XX_INTCR3_OFFSET 0x9C -#define NPCM7XX_INTCR3_PECIVSEL BIT(19) - -/* NPCM PECI Registers */ -#define NPCM_PECI_CTL_STS 0x00 -#define NPCM_PECI_RD_LENGTH 0x04 -#define NPCM_PECI_ADDR 0x08 -#define NPCM_PECI_CMD 0x0C -#define NPCM_PECI_CTL2 0x10 -#define NPCM_PECI_WR_LENGTH 0x1C -#define NPCM_PECI_PDDR 0x2C -#define NPCM_PECI_DAT_INOUT(n) (0x100 + ((n) * 4)) - -#define NPCM_PECI_MAX_REG 0x200 - -/* NPCM_PECI_CTL_STS - 0x00 : Control Register */ -#define NPCM_PECI_CTRL_DONE_INT_EN BIT(6) -#define NPCM_PECI_CTRL_ABRT_ERR BIT(4) -#define NPCM_PECI_CTRL_CRC_ERR BIT(3) -#define NPCM_PECI_CTRL_DONE BIT(1) -#define NPCM_PECI_CTRL_START_BUSY BIT(0) - -/* NPCM_PECI_RD_LENGTH - 0x04 : Command Register */ -#define NPCM_PECI_RD_LEN_MASK GENMASK(6, 0) - -/* NPCM_PECI_CMD - 0x10 : Command Register */ -#define NPCM_PECI_CTL2_MASK GENMASK(7, 6) - -/* NPCM_PECI_WR_LENGTH - 0x1C : Command Register */ -#define NPCM_PECI_WR_LEN_MASK GENMASK(6, 0) - -/* NPCM_PECI_PDDR - 0x2C : Command Register */ -#define NPCM_PECI_PDDR_MASK GENMASK(4, 0) - -#define NPCM_PECI_INT_MASK (NPCM_PECI_CTRL_ABRT_ERR | \ - NPCM_PECI_CTRL_CRC_ERR | \ - NPCM_PECI_CTRL_DONE) - -#define NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC 50000 -#define NPCM_PECI_IDLE_CHECK_INTERVAL_USEC 10000 -#define NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT 1000 -#define NPCM_PECI_CMD_TIMEOUT_MS_MAX 60000 -#define NPCM_PECI_HOST_NEG_BIT_RATE_MAX 31 -#define NPCM_PECI_HOST_NEG_BIT_RATE_MIN 7 -#define NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT 15 -#define NPCM_PECI_PULL_DOWN_DEFAULT 0 -#define NPCM_PECI_PULL_DOWN_MAX 2 - -struct npcm_peci { - u32 cmd_timeout_ms; - u32 host_bit_rate; - struct completion xfer_complete; - struct regmap *gcr_regmap; - struct peci_adapter *adapter; - struct regmap *regmap; - u32 status; - spinlock_t lock; /* to sync completion status handling */ - struct device *dev; - struct clk *clk; - int irq; -}; - -static int npcm_peci_xfer_native(struct npcm_peci *priv, - struct peci_xfer_msg *msg) -{ - long err, timeout = msecs_to_jiffies(priv->cmd_timeout_ms); - unsigned long flags; - unsigned int msg_rd; - u32 cmd_sts; - int i, rc; - - /* Check command sts and bus idle state */ - rc = regmap_read_poll_timeout(priv->regmap, NPCM_PECI_CTL_STS, cmd_sts, - !(cmd_sts & NPCM_PECI_CTRL_START_BUSY), - NPCM_PECI_IDLE_CHECK_INTERVAL_USEC, - NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC); - if (rc) - return rc; /* -ETIMEDOUT */ - - spin_lock_irqsave(&priv->lock, flags); - reinit_completion(&priv->xfer_complete); - - regmap_write(priv->regmap, NPCM_PECI_ADDR, msg->addr); - regmap_write(priv->regmap, NPCM_PECI_RD_LENGTH, - NPCM_PECI_WR_LEN_MASK & msg->rx_len); - regmap_write(priv->regmap, NPCM_PECI_WR_LENGTH, - NPCM_PECI_WR_LEN_MASK & msg->tx_len); - - if (msg->tx_len) { - regmap_write(priv->regmap, NPCM_PECI_CMD, msg->tx_buf[0]); - - for (i = 0; i < (msg->tx_len - 1); i++) - regmap_write(priv->regmap, NPCM_PECI_DAT_INOUT(i), - msg->tx_buf[i + 1]); - } - - priv->status = 0; - regmap_update_bits(priv->regmap, NPCM_PECI_CTL_STS, - NPCM_PECI_CTRL_START_BUSY, - NPCM_PECI_CTRL_START_BUSY); - - spin_unlock_irqrestore(&priv->lock, flags); - - err = wait_for_completion_interruptible_timeout(&priv->xfer_complete, - timeout); - - spin_lock_irqsave(&priv->lock, flags); - - regmap_write(priv->regmap, NPCM_PECI_CMD, 0); - - if (err <= 0 || priv->status != NPCM_PECI_CTRL_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; - } - - for (i = 0; i < msg->rx_len; i++) { - regmap_read(priv->regmap, NPCM_PECI_DAT_INOUT(i), &msg_rd); - msg->rx_buf[i] = (u8)msg_rd; - } - -err_irqrestore: - spin_unlock_irqrestore(&priv->lock, flags); - return rc; -} - -static irqreturn_t npcm_peci_irq_handler(int irq, void *arg) -{ - struct npcm_peci *priv = arg; - u32 status_ack = 0; - u32 status; - - spin_lock(&priv->lock); - regmap_read(priv->regmap, NPCM_PECI_CTL_STS, &status); - priv->status |= (status & NPCM_PECI_INT_MASK); - - if (status & NPCM_PECI_CTRL_CRC_ERR) { - dev_dbg(priv->dev, "PECI_INT_W_FCS_BAD\n"); - status_ack |= NPCM_PECI_CTRL_CRC_ERR; - } - - if (status & NPCM_PECI_CTRL_ABRT_ERR) { - dev_dbg(priv->dev, "NPCM_PECI_CTRL_ABRT_ERR\n"); - status_ack |= NPCM_PECI_CTRL_ABRT_ERR; - } - - /* - * All commands should be ended up with a NPCM_PECI_CTRL_DONE - * bit set even in an error case. - */ - if (status & NPCM_PECI_CTRL_DONE) { - dev_dbg(priv->dev, "NPCM_PECI_CTRL_DONE\n"); - status_ack |= NPCM_PECI_CTRL_DONE; - complete(&priv->xfer_complete); - } - - regmap_write_bits(priv->regmap, NPCM_PECI_CTL_STS, - NPCM_PECI_INT_MASK, status_ack); - - spin_unlock(&priv->lock); - return IRQ_HANDLED; -} - -static int npcm_peci_init_ctrl(struct npcm_peci *priv) -{ - u32 cmd_sts, host_neg_bit_rate = 0, pull_down = 0; - int ret; - bool volt; - - 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, "cmd-timeout-ms", - &priv->cmd_timeout_ms); - if (ret || priv->cmd_timeout_ms > NPCM_PECI_CMD_TIMEOUT_MS_MAX || - priv->cmd_timeout_ms == 0) { - if (ret) - dev_warn(priv->dev, - "cmd-timeout-ms not found, use default : %u\n", - NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT); - else - dev_warn(priv->dev, - "Invalid cmd-timeout-ms : %u. Use default : %u\n", - priv->cmd_timeout_ms, - NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT); - - priv->cmd_timeout_ms = NPCM_PECI_CMD_TIMEOUT_MS_DEFAULT; - } - - if (of_device_is_compatible(priv->dev->of_node, - "nuvoton,npcm750-peci")) { - priv->gcr_regmap = syscon_regmap_lookup_by_compatible - ("nuvoton,npcm750-gcr"); - if (!IS_ERR(priv->gcr_regmap)) { - volt = of_property_read_bool(priv->dev->of_node, - "high-volt-range"); - if (volt) - regmap_update_bits(priv->gcr_regmap, - NPCM7XX_INTCR3_OFFSET, - NPCM7XX_INTCR3_PECIVSEL, - NPCM7XX_INTCR3_PECIVSEL); - else - regmap_update_bits(priv->gcr_regmap, - NPCM7XX_INTCR3_OFFSET, - NPCM7XX_INTCR3_PECIVSEL, 0); - } - } - - ret = of_property_read_u32(priv->dev->of_node, "pull-down", - &pull_down); - if (ret || pull_down > NPCM_PECI_PULL_DOWN_MAX) { - if (ret) - dev_warn(priv->dev, - "pull-down not found, use default : %u\n", - NPCM_PECI_PULL_DOWN_DEFAULT); - else - dev_warn(priv->dev, - "Invalid pull-down : %u. Use default : %u\n", - pull_down, - NPCM_PECI_PULL_DOWN_DEFAULT); - pull_down = NPCM_PECI_PULL_DOWN_DEFAULT; - } - - regmap_update_bits(priv->regmap, NPCM_PECI_CTL2, NPCM_PECI_CTL2_MASK, - pull_down << 6); - - ret = of_property_read_u32(priv->dev->of_node, "host-neg-bit-rate", - &host_neg_bit_rate); - if (ret || host_neg_bit_rate > NPCM_PECI_HOST_NEG_BIT_RATE_MAX || - host_neg_bit_rate < NPCM_PECI_HOST_NEG_BIT_RATE_MIN) { - if (ret) - dev_warn(priv->dev, - "host-neg-bit-rate not found, use default : %u\n", - NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT); - else - dev_warn(priv->dev, - "Invalid host-neg-bit-rate : %u. Use default : %u\n", - host_neg_bit_rate, - NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT); - host_neg_bit_rate = NPCM_PECI_HOST_NEG_BIT_RATE_DEFAULT; - } - - regmap_update_bits(priv->regmap, NPCM_PECI_PDDR, NPCM_PECI_PDDR_MASK, - host_neg_bit_rate); - - priv->host_bit_rate = clk_get_rate(priv->clk) / - (4 * (host_neg_bit_rate + 1)); - - ret = regmap_read_poll_timeout(priv->regmap, NPCM_PECI_CTL_STS, cmd_sts, - !(cmd_sts & NPCM_PECI_CTRL_START_BUSY), - NPCM_PECI_IDLE_CHECK_INTERVAL_USEC, - NPCM_PECI_IDLE_CHECK_TIMEOUT_USEC); - if (ret) - return ret; /* -ETIMEDOUT */ - - /* PECI interrupt enable */ - regmap_update_bits(priv->regmap, NPCM_PECI_CTL_STS, - NPCM_PECI_CTRL_DONE_INT_EN, - NPCM_PECI_CTRL_DONE_INT_EN); - - return 0; -} - -static const struct regmap_config npcm_peci_regmap_config = { - .reg_bits = 8, - .val_bits = 8, - .max_register = NPCM_PECI_MAX_REG, - .fast_io = true, -}; - -static int npcm_peci_xfer(struct peci_adapter *adapter, - struct peci_xfer_msg *msg) -{ - struct npcm_peci *priv = peci_get_adapdata(adapter); - - return npcm_peci_xfer_native(priv, msg); -} - -static int npcm_peci_probe(struct platform_device *pdev) -{ - struct peci_adapter *adapter; - struct npcm_peci *priv; - struct resource *res; - void __iomem *base; - 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, - &npcm_peci_regmap_config); - if (IS_ERR(priv->regmap)) { - ret = PTR_ERR(priv->regmap); - 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, npcm_peci_irq_handler, - 0, "peci-npcm-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 = npcm_peci_xfer; - - ret = npcm_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, host negotiation bit rate %dHz", - priv->adapter->nr, priv->host_bit_rate); - - return 0; - -err_put_adapter_dev: - put_device(&adapter->dev); - return ret; -} - -static int npcm_peci_remove(struct platform_device *pdev) -{ - struct npcm_peci *priv = dev_get_drvdata(&pdev->dev); - - clk_disable_unprepare(priv->clk); - peci_del_adapter(priv->adapter); - of_node_put(priv->adapter->dev.of_node); - - return 0; -} - -static const struct of_device_id npcm_peci_of_table[] = { - { .compatible = "nuvoton,npcm750-peci", }, - { } -}; -MODULE_DEVICE_TABLE(of, npcm_peci_of_table); - -static struct platform_driver npcm_peci_driver = { - .probe = npcm_peci_probe, - .remove = npcm_peci_remove, - .driver = { - .name = "peci-npcm", - .of_match_table = of_match_ptr(npcm_peci_of_table), - }, -}; -module_platform_driver(npcm_peci_driver); - -MODULE_AUTHOR("Tomer Maimon "); -MODULE_DESCRIPTION("NPCM Platform Environment Control Interface (PECI) driver"); -MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/intel-peci-client.h b/include/linux/mfd/intel-peci-client.h index 8f6d823..9854303 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,14 +9,15 @@ #if IS_ENABLED(CONFIG_X86) #include #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. */ -#define INTEL_FAM6_HASWELL_X 0x3F -#define INTEL_FAM6_BROADWELL_X 0x4F -#define INTEL_FAM6_SKYLAKE_X 0x55 +#define INTEL_FAM6_HASWELL_X 0x3F +#define INTEL_FAM6_BROADWELL_X 0x4F +#define INTEL_FAM6_SKYLAKE_X 0x55 +#define INTEL_FAM6_SKYLAKE_XD 0x56 #endif #define CORE_MAX_ON_HSX 18 /* Max number of cores on Haswell */ @@ -31,6 +32,10 @@ #define CHAN_RANK_MAX_ON_SKX 6 /* Max number of channel ranks on Skylake */ #define DIMM_IDX_MAX_ON_SKX 2 /* Max DIMM index per channel on Skylake */ +#define CORE_MAX_ON_SKXD 16 /* Max number of cores on Skylake D */ +#define CHAN_RANK_MAX_ON_SKXD 2 /* Max number of channel ranks on Skylake D */ +#define DIMM_IDX_MAX_ON_SKXD 2 /* Max DIMM index per channel on Skylake D */ + #define CORE_NUMS_MAX CORE_MAX_ON_SKX #define CHAN_RANK_MAX CHAN_RANK_MAX_ON_HSX #define DIMM_IDX_MAX DIMM_IDX_MAX_ON_HSX @@ -58,7 +63,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 +71,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; }; @@ -93,18 +96,22 @@ peci_client_read_package_config(struct peci_client_manager *priv, u8 index, u16 param, u8 *data) { struct peci_rd_pkg_cfg_msg msg; - int rc; + int ret; msg.addr = priv->client->addr; msg.index = index; msg.param = param; msg.rx_len = 4; - rc = peci_command(priv->client->adapter, PECI_CMD_RD_PKG_CFG, &msg); - if (!rc) - memcpy(data, msg.pkg_config, 4); + ret = peci_command(priv->client->adapter, PECI_CMD_RD_PKG_CFG, &msg); + if (msg.cc != PECI_DEV_CC_SUCCESS) + ret = -EAGAIN; + if (ret) + return ret; + + memcpy(data, msg.pkg_config, 4); - return rc; + return 0; } #endif /* __LINUX_MFD_INTEL_PECI_CLIENT_H */ diff --git a/include/linux/peci.h b/include/linux/peci.h index d0e47d4..6fc424d 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 #include +#include #include -#include #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 a6dae71..253fb42 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,35 @@ #include #include -/* 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_NEED_RETRY 0x80 +#define PECI_DEV_CC_OUT_OF_RESOURCE 0x81 +#define PECI_DEV_CC_UNAVAIL_RESOURCE 0x82 +#define PECI_DEV_CC_INVALID_REQ 0x90 +#define PECI_DEV_CC_MCA_ERROR 0x91 +#define PECI_DEV_CC_CATASTROPHIC_MCA_ERROR 0x93 +#define PECI_DEV_CC_FATAL_MCA_DETECTED 0x94 +#define PECI_DEV_CC_PARITY_ERROR_ON_GPSB_OR_PMSB 0x98 +#define PECI_DEV_CC_PARITY_ERROR_ON_GPSB_OR_PMSB_IERR 0x9B +#define PECI_DEV_CC_PARITY_ERROR_ON_GPSB_OR_PMSB_MCA 0x9C /* 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 /* 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 WRPCICFG_PECI_CMD 0x65 - -#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_DEV_RETRY_TIME_MS 250 +#define PECI_DEV_RETRY_INTERVAL_USEC 10000 +#define PECI_DEV_RETRY_BIT 0x01 /** * enum peci_cmd - PECI client commands @@ -186,11 +85,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 +102,8 @@ struct peci_xfer_msg { * powered-off, etc. */ struct peci_ping_msg { - __u8 addr; + __u8 addr; + __u8 padding[3]; } __attribute__((__packed__)); /** @@ -216,8 +117,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 +138,13 @@ 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 padding; + __s16 temp_raw; } __attribute__((__packed__)); /** @@ -242,6 +153,7 @@ struct peci_get_temp_msg { * @index: encoding index for the requested service * @param: specific data being requested * @rx_len: number of data to be read in bytes + * @cc: completion code * @pkg_config: package config data to be read * * The RdPkgConfig() command provides read access to the Package Configuration @@ -251,11 +163,73 @@ 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 cc; + __u8 padding[2]; + __u8 pkg_config[4]; } __attribute__((__packed__)); /** @@ -264,6 +238,7 @@ struct peci_rd_pkg_cfg_msg { * @index: encoding index for the requested service * @param: specific data being requested * @tx_len: number of data to be written in bytes + * @cc: completion code * @value: package config data to be written * * The WrPkgConfig() command provides write access to the Package Configuration @@ -272,11 +247,20 @@ 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 cc; + __u8 padding[2]; + __u32 value; } __attribute__((__packed__)); /** @@ -284,16 +268,47 @@ struct peci_wr_pkg_cfg_msg { * @addr: address of the client * @thread_id: ID of the specific logical processor * @address: address of MSR to read from + * @cc: completion code * @value: data to be read * * The RdIAMSR() PECI command provides read access to Model Specific Registers * (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; + __u8 cc; + __u8 padding[3]; + __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 + * @cc: completion code + * @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 { +#define PECI_WRIAMSR_CMD 0xb5 + + __u8 addr; + __u8 thread_id; + __u16 address; + __u8 tx_len; + __u8 cc; + __u8 padding[2]; + __u64 value; } __attribute__((__packed__)); /** @@ -303,6 +318,7 @@ struct peci_rd_ia_msr_msg { * @device: PCI device number * @function: specific function to read from * @reg: specific register to read from + * @cc: completion code * @pci_config: config data to be read * * The RdPCIConfig() command provides sideband read access to the PCI @@ -310,12 +326,56 @@ 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 cc; + __u8 padding[1]; + __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 + * @cc: completion code + * @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 { +#define PECI_WRPCICFG_CMD 0x65 + + __u8 addr; + __u8 bus; + __u8 device; + __u8 function; + __u16 reg; + __u8 tx_len; + __u8 cc; + __u8 pci_config[4]; } __attribute__((__packed__)); /** @@ -326,6 +386,7 @@ struct peci_rd_pci_cfg_msg { * @function: specific function to read from * @reg: specific register to read from * @rx_len: number of data to be read in bytes + * @cc: completion code * @pci_config: config data to be read * * The RdPCIConfigLocal() command provides sideband read access to the PCI @@ -333,13 +394,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 cc; + __u8 pci_config[4]; } __attribute__((__packed__)); /** @@ -350,6 +416,7 @@ struct peci_rd_pci_cfg_local_msg { * @function: specific function to read from * @reg: specific register to read from * @tx_len: number of data to be written in bytes + * @cc: completion code * @value: config data to be written * * The WrPCIConfigLocal() command provides sideband write access to the PCI @@ -357,13 +424,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 cc; + __u32 value; } __attribute__((__packed__)); #define PECI_IOC_BASE 0xb7 @@ -389,9 +461,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