cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

hwmon.c (6158B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * NVM Express hardware monitoring support
      4 * Copyright (c) 2019, Guenter Roeck
      5 */
      6
      7#include <linux/hwmon.h>
      8#include <linux/units.h>
      9#include <asm/unaligned.h>
     10
     11#include "nvme.h"
     12
     13struct nvme_hwmon_data {
     14	struct nvme_ctrl *ctrl;
     15	struct nvme_smart_log log;
     16	struct mutex read_lock;
     17};
     18
     19static int nvme_get_temp_thresh(struct nvme_ctrl *ctrl, int sensor, bool under,
     20				long *temp)
     21{
     22	unsigned int threshold = sensor << NVME_TEMP_THRESH_SELECT_SHIFT;
     23	u32 status;
     24	int ret;
     25
     26	if (under)
     27		threshold |= NVME_TEMP_THRESH_TYPE_UNDER;
     28
     29	ret = nvme_get_features(ctrl, NVME_FEAT_TEMP_THRESH, threshold, NULL, 0,
     30				&status);
     31	if (ret > 0)
     32		return -EIO;
     33	if (ret < 0)
     34		return ret;
     35	*temp = kelvin_to_millicelsius(status & NVME_TEMP_THRESH_MASK);
     36
     37	return 0;
     38}
     39
     40static int nvme_set_temp_thresh(struct nvme_ctrl *ctrl, int sensor, bool under,
     41				long temp)
     42{
     43	unsigned int threshold = sensor << NVME_TEMP_THRESH_SELECT_SHIFT;
     44	int ret;
     45
     46	temp = millicelsius_to_kelvin(temp);
     47	threshold |= clamp_val(temp, 0, NVME_TEMP_THRESH_MASK);
     48
     49	if (under)
     50		threshold |= NVME_TEMP_THRESH_TYPE_UNDER;
     51
     52	ret = nvme_set_features(ctrl, NVME_FEAT_TEMP_THRESH, threshold, NULL, 0,
     53				NULL);
     54	if (ret > 0)
     55		return -EIO;
     56
     57	return ret;
     58}
     59
     60static int nvme_hwmon_get_smart_log(struct nvme_hwmon_data *data)
     61{
     62	return nvme_get_log(data->ctrl, NVME_NSID_ALL, NVME_LOG_SMART, 0,
     63			   NVME_CSI_NVM, &data->log, sizeof(data->log), 0);
     64}
     65
     66static int nvme_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
     67			   u32 attr, int channel, long *val)
     68{
     69	struct nvme_hwmon_data *data = dev_get_drvdata(dev);
     70	struct nvme_smart_log *log = &data->log;
     71	int temp;
     72	int err;
     73
     74	/*
     75	 * First handle attributes which don't require us to read
     76	 * the smart log.
     77	 */
     78	switch (attr) {
     79	case hwmon_temp_max:
     80		return nvme_get_temp_thresh(data->ctrl, channel, false, val);
     81	case hwmon_temp_min:
     82		return nvme_get_temp_thresh(data->ctrl, channel, true, val);
     83	case hwmon_temp_crit:
     84		*val = kelvin_to_millicelsius(data->ctrl->cctemp);
     85		return 0;
     86	default:
     87		break;
     88	}
     89
     90	mutex_lock(&data->read_lock);
     91	err = nvme_hwmon_get_smart_log(data);
     92	if (err)
     93		goto unlock;
     94
     95	switch (attr) {
     96	case hwmon_temp_input:
     97		if (!channel)
     98			temp = get_unaligned_le16(log->temperature);
     99		else
    100			temp = le16_to_cpu(log->temp_sensor[channel - 1]);
    101		*val = kelvin_to_millicelsius(temp);
    102		break;
    103	case hwmon_temp_alarm:
    104		*val = !!(log->critical_warning & NVME_SMART_CRIT_TEMPERATURE);
    105		break;
    106	default:
    107		err = -EOPNOTSUPP;
    108		break;
    109	}
    110unlock:
    111	mutex_unlock(&data->read_lock);
    112	return err;
    113}
    114
    115static int nvme_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
    116			    u32 attr, int channel, long val)
    117{
    118	struct nvme_hwmon_data *data = dev_get_drvdata(dev);
    119
    120	switch (attr) {
    121	case hwmon_temp_max:
    122		return nvme_set_temp_thresh(data->ctrl, channel, false, val);
    123	case hwmon_temp_min:
    124		return nvme_set_temp_thresh(data->ctrl, channel, true, val);
    125	default:
    126		break;
    127	}
    128
    129	return -EOPNOTSUPP;
    130}
    131
    132static const char * const nvme_hwmon_sensor_names[] = {
    133	"Composite",
    134	"Sensor 1",
    135	"Sensor 2",
    136	"Sensor 3",
    137	"Sensor 4",
    138	"Sensor 5",
    139	"Sensor 6",
    140	"Sensor 7",
    141	"Sensor 8",
    142};
    143
    144static int nvme_hwmon_read_string(struct device *dev,
    145				  enum hwmon_sensor_types type, u32 attr,
    146				  int channel, const char **str)
    147{
    148	*str = nvme_hwmon_sensor_names[channel];
    149	return 0;
    150}
    151
    152static umode_t nvme_hwmon_is_visible(const void *_data,
    153				     enum hwmon_sensor_types type,
    154				     u32 attr, int channel)
    155{
    156	const struct nvme_hwmon_data *data = _data;
    157
    158	switch (attr) {
    159	case hwmon_temp_crit:
    160		if (!channel && data->ctrl->cctemp)
    161			return 0444;
    162		break;
    163	case hwmon_temp_max:
    164	case hwmon_temp_min:
    165		if ((!channel && data->ctrl->wctemp) ||
    166		    (channel && data->log.temp_sensor[channel - 1])) {
    167			if (data->ctrl->quirks &
    168			    NVME_QUIRK_NO_TEMP_THRESH_CHANGE)
    169				return 0444;
    170			return 0644;
    171		}
    172		break;
    173	case hwmon_temp_alarm:
    174		if (!channel)
    175			return 0444;
    176		break;
    177	case hwmon_temp_input:
    178	case hwmon_temp_label:
    179		if (!channel || data->log.temp_sensor[channel - 1])
    180			return 0444;
    181		break;
    182	default:
    183		break;
    184	}
    185	return 0;
    186}
    187
    188static const struct hwmon_channel_info *nvme_hwmon_info[] = {
    189	HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
    190	HWMON_CHANNEL_INFO(temp,
    191			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN |
    192				HWMON_T_CRIT | HWMON_T_LABEL | HWMON_T_ALARM,
    193			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN |
    194				HWMON_T_LABEL,
    195			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN |
    196				HWMON_T_LABEL,
    197			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN |
    198				HWMON_T_LABEL,
    199			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN |
    200				HWMON_T_LABEL,
    201			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN |
    202				HWMON_T_LABEL,
    203			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN |
    204				HWMON_T_LABEL,
    205			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN |
    206				HWMON_T_LABEL,
    207			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN |
    208				HWMON_T_LABEL),
    209	NULL
    210};
    211
    212static const struct hwmon_ops nvme_hwmon_ops = {
    213	.is_visible	= nvme_hwmon_is_visible,
    214	.read		= nvme_hwmon_read,
    215	.read_string	= nvme_hwmon_read_string,
    216	.write		= nvme_hwmon_write,
    217};
    218
    219static const struct hwmon_chip_info nvme_hwmon_chip_info = {
    220	.ops	= &nvme_hwmon_ops,
    221	.info	= nvme_hwmon_info,
    222};
    223
    224int nvme_hwmon_init(struct nvme_ctrl *ctrl)
    225{
    226	struct device *dev = ctrl->device;
    227	struct nvme_hwmon_data *data;
    228	struct device *hwmon;
    229	int err;
    230
    231	data = kzalloc(sizeof(*data), GFP_KERNEL);
    232	if (!data)
    233		return 0;
    234
    235	data->ctrl = ctrl;
    236	mutex_init(&data->read_lock);
    237
    238	err = nvme_hwmon_get_smart_log(data);
    239	if (err) {
    240		dev_warn(dev, "Failed to read smart log (error %d)\n", err);
    241		kfree(data);
    242		return err;
    243	}
    244
    245	hwmon = hwmon_device_register_with_info(dev, "nvme",
    246						data, &nvme_hwmon_chip_info,
    247						NULL);
    248	if (IS_ERR(hwmon)) {
    249		dev_warn(dev, "Failed to instantiate hwmon device\n");
    250		kfree(data);
    251		return PTR_ERR(hwmon);
    252	}
    253	ctrl->hwmon_device = hwmon;
    254	return 0;
    255}
    256
    257void nvme_hwmon_exit(struct nvme_ctrl *ctrl)
    258{
    259	if (ctrl->hwmon_device) {
    260		struct nvme_hwmon_data *data =
    261			dev_get_drvdata(ctrl->hwmon_device);
    262
    263		hwmon_device_unregister(ctrl->hwmon_device);
    264		ctrl->hwmon_device = NULL;
    265		kfree(data);
    266	}
    267}