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}