162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci// Copyright (c) 2018-2021 Intel Corporation
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/auxiliary_bus.h>
562306a36Sopenharmony_ci#include <linux/bitfield.h>
662306a36Sopenharmony_ci#include <linux/bitops.h>
762306a36Sopenharmony_ci#include <linux/devm-helpers.h>
862306a36Sopenharmony_ci#include <linux/hwmon.h>
962306a36Sopenharmony_ci#include <linux/jiffies.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/peci.h>
1262306a36Sopenharmony_ci#include <linux/peci-cpu.h>
1362306a36Sopenharmony_ci#include <linux/units.h>
1462306a36Sopenharmony_ci#include <linux/workqueue.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "common.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define DIMM_MASK_CHECK_DELAY_JIFFIES	msecs_to_jiffies(5000)
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* Max number of channel ranks and DIMM index per channel */
2162306a36Sopenharmony_ci#define CHAN_RANK_MAX_ON_HSX	8
2262306a36Sopenharmony_ci#define DIMM_IDX_MAX_ON_HSX	3
2362306a36Sopenharmony_ci#define CHAN_RANK_MAX_ON_BDX	4
2462306a36Sopenharmony_ci#define DIMM_IDX_MAX_ON_BDX	3
2562306a36Sopenharmony_ci#define CHAN_RANK_MAX_ON_BDXD	2
2662306a36Sopenharmony_ci#define DIMM_IDX_MAX_ON_BDXD	2
2762306a36Sopenharmony_ci#define CHAN_RANK_MAX_ON_SKX	6
2862306a36Sopenharmony_ci#define DIMM_IDX_MAX_ON_SKX	2
2962306a36Sopenharmony_ci#define CHAN_RANK_MAX_ON_ICX	8
3062306a36Sopenharmony_ci#define DIMM_IDX_MAX_ON_ICX	2
3162306a36Sopenharmony_ci#define CHAN_RANK_MAX_ON_ICXD	4
3262306a36Sopenharmony_ci#define DIMM_IDX_MAX_ON_ICXD	2
3362306a36Sopenharmony_ci#define CHAN_RANK_MAX_ON_SPR	8
3462306a36Sopenharmony_ci#define DIMM_IDX_MAX_ON_SPR	2
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define CHAN_RANK_MAX		CHAN_RANK_MAX_ON_HSX
3762306a36Sopenharmony_ci#define DIMM_IDX_MAX		DIMM_IDX_MAX_ON_HSX
3862306a36Sopenharmony_ci#define DIMM_NUMS_MAX		(CHAN_RANK_MAX * DIMM_IDX_MAX)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define CPU_SEG_MASK		GENMASK(23, 16)
4162306a36Sopenharmony_ci#define GET_CPU_SEG(x)		(((x) & CPU_SEG_MASK) >> 16)
4262306a36Sopenharmony_ci#define CPU_BUS_MASK		GENMASK(7, 0)
4362306a36Sopenharmony_ci#define GET_CPU_BUS(x)		((x) & CPU_BUS_MASK)
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define DIMM_TEMP_MAX		GENMASK(15, 8)
4662306a36Sopenharmony_ci#define DIMM_TEMP_CRIT		GENMASK(23, 16)
4762306a36Sopenharmony_ci#define GET_TEMP_MAX(x)		(((x) & DIMM_TEMP_MAX) >> 8)
4862306a36Sopenharmony_ci#define GET_TEMP_CRIT(x)	(((x) & DIMM_TEMP_CRIT) >> 16)
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#define NO_DIMM_RETRY_COUNT_MAX	5
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistruct peci_dimmtemp;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistruct dimm_info {
5562306a36Sopenharmony_ci	int chan_rank_max;
5662306a36Sopenharmony_ci	int dimm_idx_max;
5762306a36Sopenharmony_ci	u8 min_peci_revision;
5862306a36Sopenharmony_ci	int (*read_thresholds)(struct peci_dimmtemp *priv, int dimm_order,
5962306a36Sopenharmony_ci			       int chan_rank, u32 *data);
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistruct peci_dimm_thresholds {
6362306a36Sopenharmony_ci	long temp_max;
6462306a36Sopenharmony_ci	long temp_crit;
6562306a36Sopenharmony_ci	struct peci_sensor_state state;
6662306a36Sopenharmony_ci};
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cienum peci_dimm_threshold_type {
6962306a36Sopenharmony_ci	temp_max_type,
7062306a36Sopenharmony_ci	temp_crit_type,
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistruct peci_dimmtemp {
7462306a36Sopenharmony_ci	struct peci_device *peci_dev;
7562306a36Sopenharmony_ci	struct device *dev;
7662306a36Sopenharmony_ci	const char *name;
7762306a36Sopenharmony_ci	const struct dimm_info *gen_info;
7862306a36Sopenharmony_ci	struct delayed_work detect_work;
7962306a36Sopenharmony_ci	struct {
8062306a36Sopenharmony_ci		struct peci_sensor_data temp;
8162306a36Sopenharmony_ci		struct peci_dimm_thresholds thresholds;
8262306a36Sopenharmony_ci	} dimm[DIMM_NUMS_MAX];
8362306a36Sopenharmony_ci	char **dimmtemp_label;
8462306a36Sopenharmony_ci	DECLARE_BITMAP(dimm_mask, DIMM_NUMS_MAX);
8562306a36Sopenharmony_ci	u8 no_dimm_retry_count;
8662306a36Sopenharmony_ci};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic u8 __dimm_temp(u32 reg, int dimm_order)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	return (reg >> (dimm_order * 8)) & 0xff;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no, long *val)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	int dimm_order = dimm_no % priv->gen_info->dimm_idx_max;
9662306a36Sopenharmony_ci	int chan_rank = dimm_no / priv->gen_info->dimm_idx_max;
9762306a36Sopenharmony_ci	int ret = 0;
9862306a36Sopenharmony_ci	u32 data;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	mutex_lock(&priv->dimm[dimm_no].temp.state.lock);
10162306a36Sopenharmony_ci	if (!peci_sensor_need_update(&priv->dimm[dimm_no].temp.state))
10262306a36Sopenharmony_ci		goto skip_update;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	ret = peci_pcs_read(priv->peci_dev, PECI_PCS_DDR_DIMM_TEMP, chan_rank, &data);
10562306a36Sopenharmony_ci	if (ret)
10662306a36Sopenharmony_ci		goto unlock;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	priv->dimm[dimm_no].temp.value = __dimm_temp(data, dimm_order) * MILLIDEGREE_PER_DEGREE;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	peci_sensor_mark_updated(&priv->dimm[dimm_no].temp.state);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ciskip_update:
11362306a36Sopenharmony_ci	*val = priv->dimm[dimm_no].temp.value;
11462306a36Sopenharmony_ciunlock:
11562306a36Sopenharmony_ci	mutex_unlock(&priv->dimm[dimm_no].temp.state.lock);
11662306a36Sopenharmony_ci	return ret;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic int update_thresholds(struct peci_dimmtemp *priv, int dimm_no)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	int dimm_order = dimm_no % priv->gen_info->dimm_idx_max;
12262306a36Sopenharmony_ci	int chan_rank = dimm_no / priv->gen_info->dimm_idx_max;
12362306a36Sopenharmony_ci	u32 data;
12462306a36Sopenharmony_ci	int ret;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	if (!peci_sensor_need_update(&priv->dimm[dimm_no].thresholds.state))
12762306a36Sopenharmony_ci		return 0;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	ret = priv->gen_info->read_thresholds(priv, dimm_order, chan_rank, &data);
13062306a36Sopenharmony_ci	if (ret == -ENODATA) /* Use default or previous value */
13162306a36Sopenharmony_ci		return 0;
13262306a36Sopenharmony_ci	if (ret)
13362306a36Sopenharmony_ci		return ret;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	priv->dimm[dimm_no].thresholds.temp_max = GET_TEMP_MAX(data) * MILLIDEGREE_PER_DEGREE;
13662306a36Sopenharmony_ci	priv->dimm[dimm_no].thresholds.temp_crit = GET_TEMP_CRIT(data) * MILLIDEGREE_PER_DEGREE;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	peci_sensor_mark_updated(&priv->dimm[dimm_no].thresholds.state);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	return 0;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic int get_dimm_thresholds(struct peci_dimmtemp *priv, enum peci_dimm_threshold_type type,
14462306a36Sopenharmony_ci			       int dimm_no, long *val)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	int ret;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	mutex_lock(&priv->dimm[dimm_no].thresholds.state.lock);
14962306a36Sopenharmony_ci	ret = update_thresholds(priv, dimm_no);
15062306a36Sopenharmony_ci	if (ret)
15162306a36Sopenharmony_ci		goto unlock;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	switch (type) {
15462306a36Sopenharmony_ci	case temp_max_type:
15562306a36Sopenharmony_ci		*val = priv->dimm[dimm_no].thresholds.temp_max;
15662306a36Sopenharmony_ci		break;
15762306a36Sopenharmony_ci	case temp_crit_type:
15862306a36Sopenharmony_ci		*val = priv->dimm[dimm_no].thresholds.temp_crit;
15962306a36Sopenharmony_ci		break;
16062306a36Sopenharmony_ci	default:
16162306a36Sopenharmony_ci		ret = -EOPNOTSUPP;
16262306a36Sopenharmony_ci		break;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ciunlock:
16562306a36Sopenharmony_ci	mutex_unlock(&priv->dimm[dimm_no].thresholds.state.lock);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	return ret;
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic int dimmtemp_read_string(struct device *dev,
17162306a36Sopenharmony_ci				enum hwmon_sensor_types type,
17262306a36Sopenharmony_ci				u32 attr, int channel, const char **str)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	struct peci_dimmtemp *priv = dev_get_drvdata(dev);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	if (attr != hwmon_temp_label)
17762306a36Sopenharmony_ci		return -EOPNOTSUPP;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	*str = (const char *)priv->dimmtemp_label[channel];
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	return 0;
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic int dimmtemp_read(struct device *dev, enum hwmon_sensor_types type,
18562306a36Sopenharmony_ci			 u32 attr, int channel, long *val)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	struct peci_dimmtemp *priv = dev_get_drvdata(dev);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	switch (attr) {
19062306a36Sopenharmony_ci	case hwmon_temp_input:
19162306a36Sopenharmony_ci		return get_dimm_temp(priv, channel, val);
19262306a36Sopenharmony_ci	case hwmon_temp_max:
19362306a36Sopenharmony_ci		return get_dimm_thresholds(priv, temp_max_type, channel, val);
19462306a36Sopenharmony_ci	case hwmon_temp_crit:
19562306a36Sopenharmony_ci		return get_dimm_thresholds(priv, temp_crit_type, channel, val);
19662306a36Sopenharmony_ci	default:
19762306a36Sopenharmony_ci		break;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	return -EOPNOTSUPP;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic umode_t dimmtemp_is_visible(const void *data, enum hwmon_sensor_types type,
20462306a36Sopenharmony_ci				   u32 attr, int channel)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	const struct peci_dimmtemp *priv = data;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (test_bit(channel, priv->dimm_mask))
20962306a36Sopenharmony_ci		return 0444;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	return 0;
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic const struct hwmon_ops peci_dimmtemp_ops = {
21562306a36Sopenharmony_ci	.is_visible = dimmtemp_is_visible,
21662306a36Sopenharmony_ci	.read_string = dimmtemp_read_string,
21762306a36Sopenharmony_ci	.read = dimmtemp_read,
21862306a36Sopenharmony_ci};
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic int check_populated_dimms(struct peci_dimmtemp *priv)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	int chan_rank_max = priv->gen_info->chan_rank_max;
22362306a36Sopenharmony_ci	int dimm_idx_max = priv->gen_info->dimm_idx_max;
22462306a36Sopenharmony_ci	DECLARE_BITMAP(dimm_mask, DIMM_NUMS_MAX);
22562306a36Sopenharmony_ci	DECLARE_BITMAP(chan_rank_empty, CHAN_RANK_MAX);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	int chan_rank, dimm_idx, ret, i;
22862306a36Sopenharmony_ci	u32 pcs;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	if (chan_rank_max * dimm_idx_max > DIMM_NUMS_MAX) {
23162306a36Sopenharmony_ci		WARN_ONCE(1, "Unsupported number of DIMMs - chan_rank_max: %d, dimm_idx_max: %d",
23262306a36Sopenharmony_ci			  chan_rank_max, dimm_idx_max);
23362306a36Sopenharmony_ci		return -EINVAL;
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	bitmap_zero(dimm_mask, DIMM_NUMS_MAX);
23762306a36Sopenharmony_ci	bitmap_zero(chan_rank_empty, CHAN_RANK_MAX);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	for (chan_rank = 0; chan_rank < chan_rank_max; chan_rank++) {
24062306a36Sopenharmony_ci		ret = peci_pcs_read(priv->peci_dev, PECI_PCS_DDR_DIMM_TEMP, chan_rank, &pcs);
24162306a36Sopenharmony_ci		if (ret) {
24262306a36Sopenharmony_ci			/*
24362306a36Sopenharmony_ci			 * Overall, we expect either success or -EINVAL in
24462306a36Sopenharmony_ci			 * order to determine whether DIMM is populated or not.
24562306a36Sopenharmony_ci			 * For anything else we fall back to deferring the
24662306a36Sopenharmony_ci			 * detection to be performed at a later point in time.
24762306a36Sopenharmony_ci			 */
24862306a36Sopenharmony_ci			if (ret == -EINVAL) {
24962306a36Sopenharmony_ci				bitmap_set(chan_rank_empty, chan_rank, 1);
25062306a36Sopenharmony_ci				continue;
25162306a36Sopenharmony_ci			}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci			return -EAGAIN;
25462306a36Sopenharmony_ci		}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci		for (dimm_idx = 0; dimm_idx < dimm_idx_max; dimm_idx++)
25762306a36Sopenharmony_ci			if (__dimm_temp(pcs, dimm_idx))
25862306a36Sopenharmony_ci				bitmap_set(dimm_mask, chan_rank * dimm_idx_max + dimm_idx, 1);
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	/*
26262306a36Sopenharmony_ci	 * If we got all -EINVALs, it means that the CPU doesn't have any
26362306a36Sopenharmony_ci	 * DIMMs. Unfortunately, it may also happen at the very start of
26462306a36Sopenharmony_ci	 * host platform boot. Retrying a couple of times lets us make sure
26562306a36Sopenharmony_ci	 * that the state is persistent.
26662306a36Sopenharmony_ci	 */
26762306a36Sopenharmony_ci	if (bitmap_full(chan_rank_empty, chan_rank_max)) {
26862306a36Sopenharmony_ci		if (priv->no_dimm_retry_count < NO_DIMM_RETRY_COUNT_MAX) {
26962306a36Sopenharmony_ci			priv->no_dimm_retry_count++;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci			return -EAGAIN;
27262306a36Sopenharmony_ci		}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci		return -ENODEV;
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	/*
27862306a36Sopenharmony_ci	 * It's possible that memory training is not done yet. In this case we
27962306a36Sopenharmony_ci	 * defer the detection to be performed at a later point in time.
28062306a36Sopenharmony_ci	 */
28162306a36Sopenharmony_ci	if (bitmap_empty(dimm_mask, DIMM_NUMS_MAX)) {
28262306a36Sopenharmony_ci		priv->no_dimm_retry_count = 0;
28362306a36Sopenharmony_ci		return -EAGAIN;
28462306a36Sopenharmony_ci	}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	for_each_set_bit(i, dimm_mask, DIMM_NUMS_MAX) {
28762306a36Sopenharmony_ci		dev_dbg(priv->dev, "Found DIMM%#x\n", i);
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	bitmap_copy(priv->dimm_mask, dimm_mask, DIMM_NUMS_MAX);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	return 0;
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic int create_dimm_temp_label(struct peci_dimmtemp *priv, int chan)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	int rank = chan / priv->gen_info->dimm_idx_max;
29862306a36Sopenharmony_ci	int idx = chan % priv->gen_info->dimm_idx_max;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	priv->dimmtemp_label[chan] = devm_kasprintf(priv->dev, GFP_KERNEL,
30162306a36Sopenharmony_ci						    "DIMM %c%d", 'A' + rank,
30262306a36Sopenharmony_ci						    idx + 1);
30362306a36Sopenharmony_ci	if (!priv->dimmtemp_label[chan])
30462306a36Sopenharmony_ci		return -ENOMEM;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	return 0;
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic const struct hwmon_channel_info * const peci_dimmtemp_temp_info[] = {
31062306a36Sopenharmony_ci	HWMON_CHANNEL_INFO(temp,
31162306a36Sopenharmony_ci			   [0 ... DIMM_NUMS_MAX - 1] = HWMON_T_LABEL |
31262306a36Sopenharmony_ci				HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT),
31362306a36Sopenharmony_ci	NULL
31462306a36Sopenharmony_ci};
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic const struct hwmon_chip_info peci_dimmtemp_chip_info = {
31762306a36Sopenharmony_ci	.ops = &peci_dimmtemp_ops,
31862306a36Sopenharmony_ci	.info = peci_dimmtemp_temp_info,
31962306a36Sopenharmony_ci};
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic int create_dimm_temp_info(struct peci_dimmtemp *priv)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	int ret, i, channels;
32462306a36Sopenharmony_ci	struct device *dev;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	/*
32762306a36Sopenharmony_ci	 * We expect to either find populated DIMMs and carry on with creating
32862306a36Sopenharmony_ci	 * sensors, or find out that there are no DIMMs populated.
32962306a36Sopenharmony_ci	 * All other states mean that the platform never reached the state that
33062306a36Sopenharmony_ci	 * allows to check DIMM state - causing us to retry later on.
33162306a36Sopenharmony_ci	 */
33262306a36Sopenharmony_ci	ret = check_populated_dimms(priv);
33362306a36Sopenharmony_ci	if (ret == -ENODEV) {
33462306a36Sopenharmony_ci		dev_dbg(priv->dev, "No DIMMs found\n");
33562306a36Sopenharmony_ci		return 0;
33662306a36Sopenharmony_ci	} else if (ret) {
33762306a36Sopenharmony_ci		schedule_delayed_work(&priv->detect_work, DIMM_MASK_CHECK_DELAY_JIFFIES);
33862306a36Sopenharmony_ci		dev_dbg(priv->dev, "Deferred populating DIMM temp info\n");
33962306a36Sopenharmony_ci		return ret;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	channels = priv->gen_info->chan_rank_max * priv->gen_info->dimm_idx_max;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	priv->dimmtemp_label = devm_kzalloc(priv->dev, channels * sizeof(char *), GFP_KERNEL);
34562306a36Sopenharmony_ci	if (!priv->dimmtemp_label)
34662306a36Sopenharmony_ci		return -ENOMEM;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	for_each_set_bit(i, priv->dimm_mask, DIMM_NUMS_MAX) {
34962306a36Sopenharmony_ci		ret = create_dimm_temp_label(priv, i);
35062306a36Sopenharmony_ci		if (ret)
35162306a36Sopenharmony_ci			return ret;
35262306a36Sopenharmony_ci		mutex_init(&priv->dimm[i].thresholds.state.lock);
35362306a36Sopenharmony_ci		mutex_init(&priv->dimm[i].temp.state.lock);
35462306a36Sopenharmony_ci	}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	dev = devm_hwmon_device_register_with_info(priv->dev, priv->name, priv,
35762306a36Sopenharmony_ci						   &peci_dimmtemp_chip_info, NULL);
35862306a36Sopenharmony_ci	if (IS_ERR(dev)) {
35962306a36Sopenharmony_ci		dev_err(priv->dev, "Failed to register hwmon device\n");
36062306a36Sopenharmony_ci		return PTR_ERR(dev);
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	dev_dbg(priv->dev, "%s: sensor '%s'\n", dev_name(dev), priv->name);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	return 0;
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic void create_dimm_temp_info_delayed(struct work_struct *work)
36962306a36Sopenharmony_ci{
37062306a36Sopenharmony_ci	struct peci_dimmtemp *priv = container_of(to_delayed_work(work),
37162306a36Sopenharmony_ci						  struct peci_dimmtemp,
37262306a36Sopenharmony_ci						  detect_work);
37362306a36Sopenharmony_ci	int ret;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	ret = create_dimm_temp_info(priv);
37662306a36Sopenharmony_ci	if (ret && ret != -EAGAIN)
37762306a36Sopenharmony_ci		dev_err(priv->dev, "Failed to populate DIMM temp info\n");
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic int peci_dimmtemp_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	struct device *dev = &adev->dev;
38362306a36Sopenharmony_ci	struct peci_device *peci_dev = to_peci_device(dev->parent);
38462306a36Sopenharmony_ci	struct peci_dimmtemp *priv;
38562306a36Sopenharmony_ci	int ret;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
38862306a36Sopenharmony_ci	if (!priv)
38962306a36Sopenharmony_ci		return -ENOMEM;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	priv->name = devm_kasprintf(dev, GFP_KERNEL, "peci_dimmtemp.cpu%d",
39262306a36Sopenharmony_ci				    peci_dev->info.socket_id);
39362306a36Sopenharmony_ci	if (!priv->name)
39462306a36Sopenharmony_ci		return -ENOMEM;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	priv->dev = dev;
39762306a36Sopenharmony_ci	priv->peci_dev = peci_dev;
39862306a36Sopenharmony_ci	priv->gen_info = (const struct dimm_info *)id->driver_data;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	/*
40162306a36Sopenharmony_ci	 * This is just a sanity check. Since we're using commands that are
40262306a36Sopenharmony_ci	 * guaranteed to be supported on a given platform, we should never see
40362306a36Sopenharmony_ci	 * revision lower than expected.
40462306a36Sopenharmony_ci	 */
40562306a36Sopenharmony_ci	if (peci_dev->info.peci_revision < priv->gen_info->min_peci_revision)
40662306a36Sopenharmony_ci		dev_warn(priv->dev,
40762306a36Sopenharmony_ci			 "Unexpected PECI revision %#x, some features may be unavailable\n",
40862306a36Sopenharmony_ci			 peci_dev->info.peci_revision);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	ret = devm_delayed_work_autocancel(priv->dev, &priv->detect_work,
41162306a36Sopenharmony_ci					   create_dimm_temp_info_delayed);
41262306a36Sopenharmony_ci	if (ret)
41362306a36Sopenharmony_ci		return ret;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	ret = create_dimm_temp_info(priv);
41662306a36Sopenharmony_ci	if (ret && ret != -EAGAIN) {
41762306a36Sopenharmony_ci		dev_err(dev, "Failed to populate DIMM temp info\n");
41862306a36Sopenharmony_ci		return ret;
41962306a36Sopenharmony_ci	}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	return 0;
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic int
42562306a36Sopenharmony_ciread_thresholds_hsx(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	u8 dev, func;
42862306a36Sopenharmony_ci	u16 reg;
42962306a36Sopenharmony_ci	int ret;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	/*
43262306a36Sopenharmony_ci	 * Device 20, Function 0: IMC 0 channel 0 -> rank 0
43362306a36Sopenharmony_ci	 * Device 20, Function 1: IMC 0 channel 1 -> rank 1
43462306a36Sopenharmony_ci	 * Device 21, Function 0: IMC 0 channel 2 -> rank 2
43562306a36Sopenharmony_ci	 * Device 21, Function 1: IMC 0 channel 3 -> rank 3
43662306a36Sopenharmony_ci	 * Device 23, Function 0: IMC 1 channel 0 -> rank 4
43762306a36Sopenharmony_ci	 * Device 23, Function 1: IMC 1 channel 1 -> rank 5
43862306a36Sopenharmony_ci	 * Device 24, Function 0: IMC 1 channel 2 -> rank 6
43962306a36Sopenharmony_ci	 * Device 24, Function 1: IMC 1 channel 3 -> rank 7
44062306a36Sopenharmony_ci	 */
44162306a36Sopenharmony_ci	dev = 20 + chan_rank / 2 + chan_rank / 4;
44262306a36Sopenharmony_ci	func = chan_rank % 2;
44362306a36Sopenharmony_ci	reg = 0x120 + dimm_order * 4;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	ret = peci_pci_local_read(priv->peci_dev, 1, dev, func, reg, data);
44662306a36Sopenharmony_ci	if (ret)
44762306a36Sopenharmony_ci		return ret;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	return 0;
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic int
45362306a36Sopenharmony_ciread_thresholds_bdxd(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci	u8 dev, func;
45662306a36Sopenharmony_ci	u16 reg;
45762306a36Sopenharmony_ci	int ret;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	/*
46062306a36Sopenharmony_ci	 * Device 10, Function 2: IMC 0 channel 0 -> rank 0
46162306a36Sopenharmony_ci	 * Device 10, Function 6: IMC 0 channel 1 -> rank 1
46262306a36Sopenharmony_ci	 * Device 12, Function 2: IMC 1 channel 0 -> rank 2
46362306a36Sopenharmony_ci	 * Device 12, Function 6: IMC 1 channel 1 -> rank 3
46462306a36Sopenharmony_ci	 */
46562306a36Sopenharmony_ci	dev = 10 + chan_rank / 2 * 2;
46662306a36Sopenharmony_ci	func = (chan_rank % 2) ? 6 : 2;
46762306a36Sopenharmony_ci	reg = 0x120 + dimm_order * 4;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	ret = peci_pci_local_read(priv->peci_dev, 2, dev, func, reg, data);
47062306a36Sopenharmony_ci	if (ret)
47162306a36Sopenharmony_ci		return ret;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	return 0;
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic int
47762306a36Sopenharmony_ciread_thresholds_skx(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	u8 dev, func;
48062306a36Sopenharmony_ci	u16 reg;
48162306a36Sopenharmony_ci	int ret;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	/*
48462306a36Sopenharmony_ci	 * Device 10, Function 2: IMC 0 channel 0 -> rank 0
48562306a36Sopenharmony_ci	 * Device 10, Function 6: IMC 0 channel 1 -> rank 1
48662306a36Sopenharmony_ci	 * Device 11, Function 2: IMC 0 channel 2 -> rank 2
48762306a36Sopenharmony_ci	 * Device 12, Function 2: IMC 1 channel 0 -> rank 3
48862306a36Sopenharmony_ci	 * Device 12, Function 6: IMC 1 channel 1 -> rank 4
48962306a36Sopenharmony_ci	 * Device 13, Function 2: IMC 1 channel 2 -> rank 5
49062306a36Sopenharmony_ci	 */
49162306a36Sopenharmony_ci	dev = 10 + chan_rank / 3 * 2 + (chan_rank % 3 == 2 ? 1 : 0);
49262306a36Sopenharmony_ci	func = chan_rank % 3 == 1 ? 6 : 2;
49362306a36Sopenharmony_ci	reg = 0x120 + dimm_order * 4;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	ret = peci_pci_local_read(priv->peci_dev, 2, dev, func, reg, data);
49662306a36Sopenharmony_ci	if (ret)
49762306a36Sopenharmony_ci		return ret;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	return 0;
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic int
50362306a36Sopenharmony_ciread_thresholds_icx(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci	u32 reg_val;
50662306a36Sopenharmony_ci	u64 offset;
50762306a36Sopenharmony_ci	int ret;
50862306a36Sopenharmony_ci	u8 dev;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	ret = peci_ep_pci_local_read(priv->peci_dev, 0, 13, 0, 2, 0xd4, &reg_val);
51162306a36Sopenharmony_ci	if (ret || !(reg_val & BIT(31)))
51262306a36Sopenharmony_ci		return -ENODATA; /* Use default or previous value */
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	ret = peci_ep_pci_local_read(priv->peci_dev, 0, 13, 0, 2, 0xd0, &reg_val);
51562306a36Sopenharmony_ci	if (ret)
51662306a36Sopenharmony_ci		return -ENODATA; /* Use default or previous value */
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	/*
51962306a36Sopenharmony_ci	 * Device 26, Offset 224e0: IMC 0 channel 0 -> rank 0
52062306a36Sopenharmony_ci	 * Device 26, Offset 264e0: IMC 0 channel 1 -> rank 1
52162306a36Sopenharmony_ci	 * Device 27, Offset 224e0: IMC 1 channel 0 -> rank 2
52262306a36Sopenharmony_ci	 * Device 27, Offset 264e0: IMC 1 channel 1 -> rank 3
52362306a36Sopenharmony_ci	 * Device 28, Offset 224e0: IMC 2 channel 0 -> rank 4
52462306a36Sopenharmony_ci	 * Device 28, Offset 264e0: IMC 2 channel 1 -> rank 5
52562306a36Sopenharmony_ci	 * Device 29, Offset 224e0: IMC 3 channel 0 -> rank 6
52662306a36Sopenharmony_ci	 * Device 29, Offset 264e0: IMC 3 channel 1 -> rank 7
52762306a36Sopenharmony_ci	 */
52862306a36Sopenharmony_ci	dev = 26 + chan_rank / 2;
52962306a36Sopenharmony_ci	offset = 0x224e0 + dimm_order * 4 + (chan_rank % 2) * 0x4000;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	ret = peci_mmio_read(priv->peci_dev, 0, GET_CPU_SEG(reg_val), GET_CPU_BUS(reg_val),
53262306a36Sopenharmony_ci			     dev, 0, offset, data);
53362306a36Sopenharmony_ci	if (ret)
53462306a36Sopenharmony_ci		return ret;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	return 0;
53762306a36Sopenharmony_ci}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_cistatic int
54062306a36Sopenharmony_ciread_thresholds_spr(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	u32 reg_val;
54362306a36Sopenharmony_ci	u64 offset;
54462306a36Sopenharmony_ci	int ret;
54562306a36Sopenharmony_ci	u8 dev;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	ret = peci_ep_pci_local_read(priv->peci_dev, 0, 30, 0, 2, 0xd4, &reg_val);
54862306a36Sopenharmony_ci	if (ret || !(reg_val & BIT(31)))
54962306a36Sopenharmony_ci		return -ENODATA; /* Use default or previous value */
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	ret = peci_ep_pci_local_read(priv->peci_dev, 0, 30, 0, 2, 0xd0, &reg_val);
55262306a36Sopenharmony_ci	if (ret)
55362306a36Sopenharmony_ci		return -ENODATA; /* Use default or previous value */
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	/*
55662306a36Sopenharmony_ci	 * Device 26, Offset 219a8: IMC 0 channel 0 -> rank 0
55762306a36Sopenharmony_ci	 * Device 26, Offset 299a8: IMC 0 channel 1 -> rank 1
55862306a36Sopenharmony_ci	 * Device 27, Offset 219a8: IMC 1 channel 0 -> rank 2
55962306a36Sopenharmony_ci	 * Device 27, Offset 299a8: IMC 1 channel 1 -> rank 3
56062306a36Sopenharmony_ci	 * Device 28, Offset 219a8: IMC 2 channel 0 -> rank 4
56162306a36Sopenharmony_ci	 * Device 28, Offset 299a8: IMC 2 channel 1 -> rank 5
56262306a36Sopenharmony_ci	 * Device 29, Offset 219a8: IMC 3 channel 0 -> rank 6
56362306a36Sopenharmony_ci	 * Device 29, Offset 299a8: IMC 3 channel 1 -> rank 7
56462306a36Sopenharmony_ci	 */
56562306a36Sopenharmony_ci	dev = 26 + chan_rank / 2;
56662306a36Sopenharmony_ci	offset = 0x219a8 + dimm_order * 4 + (chan_rank % 2) * 0x8000;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	ret = peci_mmio_read(priv->peci_dev, 0, GET_CPU_SEG(reg_val), GET_CPU_BUS(reg_val),
56962306a36Sopenharmony_ci			     dev, 0, offset, data);
57062306a36Sopenharmony_ci	if (ret)
57162306a36Sopenharmony_ci		return ret;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	return 0;
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_cistatic const struct dimm_info dimm_hsx = {
57762306a36Sopenharmony_ci	.chan_rank_max	= CHAN_RANK_MAX_ON_HSX,
57862306a36Sopenharmony_ci	.dimm_idx_max	= DIMM_IDX_MAX_ON_HSX,
57962306a36Sopenharmony_ci	.min_peci_revision = 0x33,
58062306a36Sopenharmony_ci	.read_thresholds = &read_thresholds_hsx,
58162306a36Sopenharmony_ci};
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_cistatic const struct dimm_info dimm_bdx = {
58462306a36Sopenharmony_ci	.chan_rank_max	= CHAN_RANK_MAX_ON_BDX,
58562306a36Sopenharmony_ci	.dimm_idx_max	= DIMM_IDX_MAX_ON_BDX,
58662306a36Sopenharmony_ci	.min_peci_revision = 0x33,
58762306a36Sopenharmony_ci	.read_thresholds = &read_thresholds_hsx,
58862306a36Sopenharmony_ci};
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_cistatic const struct dimm_info dimm_bdxd = {
59162306a36Sopenharmony_ci	.chan_rank_max	= CHAN_RANK_MAX_ON_BDXD,
59262306a36Sopenharmony_ci	.dimm_idx_max	= DIMM_IDX_MAX_ON_BDXD,
59362306a36Sopenharmony_ci	.min_peci_revision = 0x33,
59462306a36Sopenharmony_ci	.read_thresholds = &read_thresholds_bdxd,
59562306a36Sopenharmony_ci};
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_cistatic const struct dimm_info dimm_skx = {
59862306a36Sopenharmony_ci	.chan_rank_max	= CHAN_RANK_MAX_ON_SKX,
59962306a36Sopenharmony_ci	.dimm_idx_max	= DIMM_IDX_MAX_ON_SKX,
60062306a36Sopenharmony_ci	.min_peci_revision = 0x33,
60162306a36Sopenharmony_ci	.read_thresholds = &read_thresholds_skx,
60262306a36Sopenharmony_ci};
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_cistatic const struct dimm_info dimm_icx = {
60562306a36Sopenharmony_ci	.chan_rank_max	= CHAN_RANK_MAX_ON_ICX,
60662306a36Sopenharmony_ci	.dimm_idx_max	= DIMM_IDX_MAX_ON_ICX,
60762306a36Sopenharmony_ci	.min_peci_revision = 0x40,
60862306a36Sopenharmony_ci	.read_thresholds = &read_thresholds_icx,
60962306a36Sopenharmony_ci};
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_cistatic const struct dimm_info dimm_icxd = {
61262306a36Sopenharmony_ci	.chan_rank_max	= CHAN_RANK_MAX_ON_ICXD,
61362306a36Sopenharmony_ci	.dimm_idx_max	= DIMM_IDX_MAX_ON_ICXD,
61462306a36Sopenharmony_ci	.min_peci_revision = 0x40,
61562306a36Sopenharmony_ci	.read_thresholds = &read_thresholds_icx,
61662306a36Sopenharmony_ci};
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_cistatic const struct dimm_info dimm_spr = {
61962306a36Sopenharmony_ci	.chan_rank_max	= CHAN_RANK_MAX_ON_SPR,
62062306a36Sopenharmony_ci	.dimm_idx_max	= DIMM_IDX_MAX_ON_SPR,
62162306a36Sopenharmony_ci	.min_peci_revision = 0x40,
62262306a36Sopenharmony_ci	.read_thresholds = &read_thresholds_spr,
62362306a36Sopenharmony_ci};
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic const struct auxiliary_device_id peci_dimmtemp_ids[] = {
62662306a36Sopenharmony_ci	{
62762306a36Sopenharmony_ci		.name = "peci_cpu.dimmtemp.hsx",
62862306a36Sopenharmony_ci		.driver_data = (kernel_ulong_t)&dimm_hsx,
62962306a36Sopenharmony_ci	},
63062306a36Sopenharmony_ci	{
63162306a36Sopenharmony_ci		.name = "peci_cpu.dimmtemp.bdx",
63262306a36Sopenharmony_ci		.driver_data = (kernel_ulong_t)&dimm_bdx,
63362306a36Sopenharmony_ci	},
63462306a36Sopenharmony_ci	{
63562306a36Sopenharmony_ci		.name = "peci_cpu.dimmtemp.bdxd",
63662306a36Sopenharmony_ci		.driver_data = (kernel_ulong_t)&dimm_bdxd,
63762306a36Sopenharmony_ci	},
63862306a36Sopenharmony_ci	{
63962306a36Sopenharmony_ci		.name = "peci_cpu.dimmtemp.skx",
64062306a36Sopenharmony_ci		.driver_data = (kernel_ulong_t)&dimm_skx,
64162306a36Sopenharmony_ci	},
64262306a36Sopenharmony_ci	{
64362306a36Sopenharmony_ci		.name = "peci_cpu.dimmtemp.icx",
64462306a36Sopenharmony_ci		.driver_data = (kernel_ulong_t)&dimm_icx,
64562306a36Sopenharmony_ci	},
64662306a36Sopenharmony_ci	{
64762306a36Sopenharmony_ci		.name = "peci_cpu.dimmtemp.icxd",
64862306a36Sopenharmony_ci		.driver_data = (kernel_ulong_t)&dimm_icxd,
64962306a36Sopenharmony_ci	},
65062306a36Sopenharmony_ci	{
65162306a36Sopenharmony_ci		.name = "peci_cpu.dimmtemp.spr",
65262306a36Sopenharmony_ci		.driver_data = (kernel_ulong_t)&dimm_spr,
65362306a36Sopenharmony_ci	},
65462306a36Sopenharmony_ci	{ }
65562306a36Sopenharmony_ci};
65662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(auxiliary, peci_dimmtemp_ids);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_cistatic struct auxiliary_driver peci_dimmtemp_driver = {
65962306a36Sopenharmony_ci	.probe		= peci_dimmtemp_probe,
66062306a36Sopenharmony_ci	.id_table	= peci_dimmtemp_ids,
66162306a36Sopenharmony_ci};
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_cimodule_auxiliary_driver(peci_dimmtemp_driver);
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ciMODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
66662306a36Sopenharmony_ciMODULE_AUTHOR("Iwona Winiarska <iwona.winiarska@intel.com>");
66762306a36Sopenharmony_ciMODULE_DESCRIPTION("PECI dimmtemp driver");
66862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
66962306a36Sopenharmony_ciMODULE_IMPORT_NS(PECI_CPU);
670