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