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 13 struct nvme_hwmon_data { 14 struct nvme_ctrl *ctrl; 15 struct nvme_smart_log log; 16 struct mutex read_lock; 17 }; 18 19 static 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 40 static 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 60 static int nvme_hwmon_get_smart_log(struct nvme_hwmon_data *data) 61 { 62 int ret; 63 64 ret = nvme_get_log(data->ctrl, NVME_NSID_ALL, NVME_LOG_SMART, 0, 65 &data->log, sizeof(data->log), 0); 66 67 return ret <= 0 ? ret : -EIO; 68 } 69 70 static int nvme_hwmon_read(struct device *dev, enum hwmon_sensor_types type, 71 u32 attr, int channel, long *val) 72 { 73 struct nvme_hwmon_data *data = dev_get_drvdata(dev); 74 struct nvme_smart_log *log = &data->log; 75 int temp; 76 int err; 77 78 /* 79 * First handle attributes which don't require us to read 80 * the smart log. 81 */ 82 switch (attr) { 83 case hwmon_temp_max: 84 return nvme_get_temp_thresh(data->ctrl, channel, false, val); 85 case hwmon_temp_min: 86 return nvme_get_temp_thresh(data->ctrl, channel, true, val); 87 case hwmon_temp_crit: 88 *val = kelvin_to_millicelsius(data->ctrl->cctemp); 89 return 0; 90 default: 91 break; 92 } 93 94 mutex_lock(&data->read_lock); 95 err = nvme_hwmon_get_smart_log(data); 96 if (err) 97 goto unlock; 98 99 switch (attr) { 100 case hwmon_temp_input: 101 if (!channel) 102 temp = get_unaligned_le16(log->temperature); 103 else 104 temp = le16_to_cpu(log->temp_sensor[channel - 1]); 105 *val = kelvin_to_millicelsius(temp); 106 break; 107 case hwmon_temp_alarm: 108 *val = !!(log->critical_warning & NVME_SMART_CRIT_TEMPERATURE); 109 break; 110 default: 111 err = -EOPNOTSUPP; 112 break; 113 } 114 unlock: 115 mutex_unlock(&data->read_lock); 116 return err; 117 } 118 119 static int nvme_hwmon_write(struct device *dev, enum hwmon_sensor_types type, 120 u32 attr, int channel, long val) 121 { 122 struct nvme_hwmon_data *data = dev_get_drvdata(dev); 123 124 switch (attr) { 125 case hwmon_temp_max: 126 return nvme_set_temp_thresh(data->ctrl, channel, false, val); 127 case hwmon_temp_min: 128 return nvme_set_temp_thresh(data->ctrl, channel, true, val); 129 default: 130 break; 131 } 132 133 return -EOPNOTSUPP; 134 } 135 136 static const char * const nvme_hwmon_sensor_names[] = { 137 "Composite", 138 "Sensor 1", 139 "Sensor 2", 140 "Sensor 3", 141 "Sensor 4", 142 "Sensor 5", 143 "Sensor 6", 144 "Sensor 7", 145 "Sensor 8", 146 }; 147 148 static int nvme_hwmon_read_string(struct device *dev, 149 enum hwmon_sensor_types type, u32 attr, 150 int channel, const char **str) 151 { 152 *str = nvme_hwmon_sensor_names[channel]; 153 return 0; 154 } 155 156 static umode_t nvme_hwmon_is_visible(const void *_data, 157 enum hwmon_sensor_types type, 158 u32 attr, int channel) 159 { 160 const struct nvme_hwmon_data *data = _data; 161 162 switch (attr) { 163 case hwmon_temp_crit: 164 if (!channel && data->ctrl->cctemp) 165 return 0444; 166 break; 167 case hwmon_temp_max: 168 case hwmon_temp_min: 169 if ((!channel && data->ctrl->wctemp) || 170 (channel && data->log.temp_sensor[channel - 1])) { 171 if (data->ctrl->quirks & 172 NVME_QUIRK_NO_TEMP_THRESH_CHANGE) 173 return 0444; 174 return 0644; 175 } 176 break; 177 case hwmon_temp_alarm: 178 if (!channel) 179 return 0444; 180 break; 181 case hwmon_temp_input: 182 case hwmon_temp_label: 183 if (!channel || data->log.temp_sensor[channel - 1]) 184 return 0444; 185 break; 186 default: 187 break; 188 } 189 return 0; 190 } 191 192 static const struct hwmon_channel_info *nvme_hwmon_info[] = { 193 HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), 194 HWMON_CHANNEL_INFO(temp, 195 HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | 196 HWMON_T_CRIT | HWMON_T_LABEL | HWMON_T_ALARM, 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 HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | 210 HWMON_T_LABEL, 211 HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | 212 HWMON_T_LABEL), 213 NULL 214 }; 215 216 static const struct hwmon_ops nvme_hwmon_ops = { 217 .is_visible = nvme_hwmon_is_visible, 218 .read = nvme_hwmon_read, 219 .read_string = nvme_hwmon_read_string, 220 .write = nvme_hwmon_write, 221 }; 222 223 static const struct hwmon_chip_info nvme_hwmon_chip_info = { 224 .ops = &nvme_hwmon_ops, 225 .info = nvme_hwmon_info, 226 }; 227 228 void nvme_hwmon_init(struct nvme_ctrl *ctrl) 229 { 230 struct device *dev = ctrl->dev; 231 struct nvme_hwmon_data *data; 232 struct device *hwmon; 233 int err; 234 235 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 236 if (!data) 237 return; 238 239 data->ctrl = ctrl; 240 mutex_init(&data->read_lock); 241 242 err = nvme_hwmon_get_smart_log(data); 243 if (err) { 244 dev_warn(dev, "Failed to read smart log (error %d)\n", err); 245 devm_kfree(dev, data); 246 return; 247 } 248 249 hwmon = devm_hwmon_device_register_with_info(dev, "nvme", data, 250 &nvme_hwmon_chip_info, 251 NULL); 252 if (IS_ERR(hwmon)) { 253 dev_warn(dev, "Failed to instantiate hwmon device\n"); 254 devm_kfree(dev, data); 255 } 256 } 257