1400b6a7bSGuenter Roeck // SPDX-License-Identifier: GPL-2.0 2400b6a7bSGuenter Roeck /* 3400b6a7bSGuenter Roeck * NVM Express hardware monitoring support 4400b6a7bSGuenter Roeck * Copyright (c) 2019, Guenter Roeck 5400b6a7bSGuenter Roeck */ 6400b6a7bSGuenter Roeck 7400b6a7bSGuenter Roeck #include <linux/hwmon.h> 87724cd2bSAkinobu Mita #include <linux/units.h> 9400b6a7bSGuenter Roeck #include <asm/unaligned.h> 10400b6a7bSGuenter Roeck 11400b6a7bSGuenter Roeck #include "nvme.h" 12400b6a7bSGuenter Roeck 13400b6a7bSGuenter Roeck struct nvme_hwmon_data { 14400b6a7bSGuenter Roeck struct nvme_ctrl *ctrl; 15400b6a7bSGuenter Roeck struct nvme_smart_log log; 16400b6a7bSGuenter Roeck struct mutex read_lock; 17400b6a7bSGuenter Roeck }; 18400b6a7bSGuenter Roeck 1952deba0fSAkinobu Mita static int nvme_get_temp_thresh(struct nvme_ctrl *ctrl, int sensor, bool under, 2052deba0fSAkinobu Mita long *temp) 2152deba0fSAkinobu Mita { 2252deba0fSAkinobu Mita unsigned int threshold = sensor << NVME_TEMP_THRESH_SELECT_SHIFT; 2352deba0fSAkinobu Mita u32 status; 2452deba0fSAkinobu Mita int ret; 2552deba0fSAkinobu Mita 2652deba0fSAkinobu Mita if (under) 2752deba0fSAkinobu Mita threshold |= NVME_TEMP_THRESH_TYPE_UNDER; 2852deba0fSAkinobu Mita 2952deba0fSAkinobu Mita ret = nvme_get_features(ctrl, NVME_FEAT_TEMP_THRESH, threshold, NULL, 0, 3052deba0fSAkinobu Mita &status); 3152deba0fSAkinobu Mita if (ret > 0) 3252deba0fSAkinobu Mita return -EIO; 3352deba0fSAkinobu Mita if (ret < 0) 3452deba0fSAkinobu Mita return ret; 357724cd2bSAkinobu Mita *temp = kelvin_to_millicelsius(status & NVME_TEMP_THRESH_MASK); 3652deba0fSAkinobu Mita 3752deba0fSAkinobu Mita return 0; 3852deba0fSAkinobu Mita } 3952deba0fSAkinobu Mita 4052deba0fSAkinobu Mita static int nvme_set_temp_thresh(struct nvme_ctrl *ctrl, int sensor, bool under, 4152deba0fSAkinobu Mita long temp) 4252deba0fSAkinobu Mita { 4352deba0fSAkinobu Mita unsigned int threshold = sensor << NVME_TEMP_THRESH_SELECT_SHIFT; 4452deba0fSAkinobu Mita int ret; 4552deba0fSAkinobu Mita 467724cd2bSAkinobu Mita temp = millicelsius_to_kelvin(temp); 4752deba0fSAkinobu Mita threshold |= clamp_val(temp, 0, NVME_TEMP_THRESH_MASK); 4852deba0fSAkinobu Mita 4952deba0fSAkinobu Mita if (under) 5052deba0fSAkinobu Mita threshold |= NVME_TEMP_THRESH_TYPE_UNDER; 5152deba0fSAkinobu Mita 5252deba0fSAkinobu Mita ret = nvme_set_features(ctrl, NVME_FEAT_TEMP_THRESH, threshold, NULL, 0, 5352deba0fSAkinobu Mita NULL); 5452deba0fSAkinobu Mita if (ret > 0) 5552deba0fSAkinobu Mita return -EIO; 5652deba0fSAkinobu Mita 5752deba0fSAkinobu Mita return ret; 5852deba0fSAkinobu Mita } 5952deba0fSAkinobu Mita 60400b6a7bSGuenter Roeck static int nvme_hwmon_get_smart_log(struct nvme_hwmon_data *data) 61400b6a7bSGuenter Roeck { 6259e330f8SKeith Busch return nvme_get_log(data->ctrl, NVME_NSID_ALL, NVME_LOG_SMART, 0, 63be93e87eSKeith Busch NVME_CSI_NVM, &data->log, sizeof(data->log), 0); 64400b6a7bSGuenter Roeck } 65400b6a7bSGuenter Roeck 66400b6a7bSGuenter Roeck static int nvme_hwmon_read(struct device *dev, enum hwmon_sensor_types type, 67400b6a7bSGuenter Roeck u32 attr, int channel, long *val) 68400b6a7bSGuenter Roeck { 69400b6a7bSGuenter Roeck struct nvme_hwmon_data *data = dev_get_drvdata(dev); 70400b6a7bSGuenter Roeck struct nvme_smart_log *log = &data->log; 71400b6a7bSGuenter Roeck int temp; 72400b6a7bSGuenter Roeck int err; 73400b6a7bSGuenter Roeck 74400b6a7bSGuenter Roeck /* 75400b6a7bSGuenter Roeck * First handle attributes which don't require us to read 76400b6a7bSGuenter Roeck * the smart log. 77400b6a7bSGuenter Roeck */ 78400b6a7bSGuenter Roeck switch (attr) { 79400b6a7bSGuenter Roeck case hwmon_temp_max: 8052deba0fSAkinobu Mita return nvme_get_temp_thresh(data->ctrl, channel, false, val); 8152deba0fSAkinobu Mita case hwmon_temp_min: 8252deba0fSAkinobu Mita return nvme_get_temp_thresh(data->ctrl, channel, true, val); 83400b6a7bSGuenter Roeck case hwmon_temp_crit: 847724cd2bSAkinobu Mita *val = kelvin_to_millicelsius(data->ctrl->cctemp); 85400b6a7bSGuenter Roeck return 0; 86400b6a7bSGuenter Roeck default: 87400b6a7bSGuenter Roeck break; 88400b6a7bSGuenter Roeck } 89400b6a7bSGuenter Roeck 90400b6a7bSGuenter Roeck mutex_lock(&data->read_lock); 91400b6a7bSGuenter Roeck err = nvme_hwmon_get_smart_log(data); 92400b6a7bSGuenter Roeck if (err) 93400b6a7bSGuenter Roeck goto unlock; 94400b6a7bSGuenter Roeck 95400b6a7bSGuenter Roeck switch (attr) { 96400b6a7bSGuenter Roeck case hwmon_temp_input: 97400b6a7bSGuenter Roeck if (!channel) 98400b6a7bSGuenter Roeck temp = get_unaligned_le16(log->temperature); 99400b6a7bSGuenter Roeck else 100400b6a7bSGuenter Roeck temp = le16_to_cpu(log->temp_sensor[channel - 1]); 1017724cd2bSAkinobu Mita *val = kelvin_to_millicelsius(temp); 102400b6a7bSGuenter Roeck break; 103400b6a7bSGuenter Roeck case hwmon_temp_alarm: 104400b6a7bSGuenter Roeck *val = !!(log->critical_warning & NVME_SMART_CRIT_TEMPERATURE); 105400b6a7bSGuenter Roeck break; 106400b6a7bSGuenter Roeck default: 107400b6a7bSGuenter Roeck err = -EOPNOTSUPP; 108400b6a7bSGuenter Roeck break; 109400b6a7bSGuenter Roeck } 110400b6a7bSGuenter Roeck unlock: 111400b6a7bSGuenter Roeck mutex_unlock(&data->read_lock); 112400b6a7bSGuenter Roeck return err; 113400b6a7bSGuenter Roeck } 114400b6a7bSGuenter Roeck 11552deba0fSAkinobu Mita static int nvme_hwmon_write(struct device *dev, enum hwmon_sensor_types type, 11652deba0fSAkinobu Mita u32 attr, int channel, long val) 11752deba0fSAkinobu Mita { 11852deba0fSAkinobu Mita struct nvme_hwmon_data *data = dev_get_drvdata(dev); 11952deba0fSAkinobu Mita 12052deba0fSAkinobu Mita switch (attr) { 12152deba0fSAkinobu Mita case hwmon_temp_max: 12252deba0fSAkinobu Mita return nvme_set_temp_thresh(data->ctrl, channel, false, val); 12352deba0fSAkinobu Mita case hwmon_temp_min: 12452deba0fSAkinobu Mita return nvme_set_temp_thresh(data->ctrl, channel, true, val); 12552deba0fSAkinobu Mita default: 12652deba0fSAkinobu Mita break; 12752deba0fSAkinobu Mita } 12852deba0fSAkinobu Mita 12952deba0fSAkinobu Mita return -EOPNOTSUPP; 13052deba0fSAkinobu Mita } 13152deba0fSAkinobu Mita 132400b6a7bSGuenter Roeck static const char * const nvme_hwmon_sensor_names[] = { 133400b6a7bSGuenter Roeck "Composite", 134400b6a7bSGuenter Roeck "Sensor 1", 135400b6a7bSGuenter Roeck "Sensor 2", 136400b6a7bSGuenter Roeck "Sensor 3", 137400b6a7bSGuenter Roeck "Sensor 4", 138400b6a7bSGuenter Roeck "Sensor 5", 139400b6a7bSGuenter Roeck "Sensor 6", 140400b6a7bSGuenter Roeck "Sensor 7", 141400b6a7bSGuenter Roeck "Sensor 8", 142400b6a7bSGuenter Roeck }; 143400b6a7bSGuenter Roeck 144400b6a7bSGuenter Roeck static int nvme_hwmon_read_string(struct device *dev, 145400b6a7bSGuenter Roeck enum hwmon_sensor_types type, u32 attr, 146400b6a7bSGuenter Roeck int channel, const char **str) 147400b6a7bSGuenter Roeck { 148400b6a7bSGuenter Roeck *str = nvme_hwmon_sensor_names[channel]; 149400b6a7bSGuenter Roeck return 0; 150400b6a7bSGuenter Roeck } 151400b6a7bSGuenter Roeck 152400b6a7bSGuenter Roeck static umode_t nvme_hwmon_is_visible(const void *_data, 153400b6a7bSGuenter Roeck enum hwmon_sensor_types type, 154400b6a7bSGuenter Roeck u32 attr, int channel) 155400b6a7bSGuenter Roeck { 156400b6a7bSGuenter Roeck const struct nvme_hwmon_data *data = _data; 157400b6a7bSGuenter Roeck 158400b6a7bSGuenter Roeck switch (attr) { 159400b6a7bSGuenter Roeck case hwmon_temp_crit: 160400b6a7bSGuenter Roeck if (!channel && data->ctrl->cctemp) 161400b6a7bSGuenter Roeck return 0444; 162400b6a7bSGuenter Roeck break; 163400b6a7bSGuenter Roeck case hwmon_temp_max: 16452deba0fSAkinobu Mita case hwmon_temp_min: 16552deba0fSAkinobu Mita if ((!channel && data->ctrl->wctemp) || 1666c6aa2f2SAkinobu Mita (channel && data->log.temp_sensor[channel - 1])) { 1676c6aa2f2SAkinobu Mita if (data->ctrl->quirks & 1686c6aa2f2SAkinobu Mita NVME_QUIRK_NO_TEMP_THRESH_CHANGE) 1696c6aa2f2SAkinobu Mita return 0444; 17052deba0fSAkinobu Mita return 0644; 1716c6aa2f2SAkinobu Mita } 172400b6a7bSGuenter Roeck break; 173400b6a7bSGuenter Roeck case hwmon_temp_alarm: 174400b6a7bSGuenter Roeck if (!channel) 175400b6a7bSGuenter Roeck return 0444; 176400b6a7bSGuenter Roeck break; 177400b6a7bSGuenter Roeck case hwmon_temp_input: 178400b6a7bSGuenter Roeck case hwmon_temp_label: 179400b6a7bSGuenter Roeck if (!channel || data->log.temp_sensor[channel - 1]) 180400b6a7bSGuenter Roeck return 0444; 181400b6a7bSGuenter Roeck break; 182400b6a7bSGuenter Roeck default: 183400b6a7bSGuenter Roeck break; 184400b6a7bSGuenter Roeck } 185400b6a7bSGuenter Roeck return 0; 186400b6a7bSGuenter Roeck } 187400b6a7bSGuenter Roeck 188400b6a7bSGuenter Roeck static const struct hwmon_channel_info *nvme_hwmon_info[] = { 189400b6a7bSGuenter Roeck HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), 190400b6a7bSGuenter Roeck HWMON_CHANNEL_INFO(temp, 19152deba0fSAkinobu Mita HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | 19252deba0fSAkinobu Mita HWMON_T_CRIT | HWMON_T_LABEL | HWMON_T_ALARM, 19352deba0fSAkinobu Mita HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | 19452deba0fSAkinobu Mita HWMON_T_LABEL, 19552deba0fSAkinobu Mita HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | 19652deba0fSAkinobu Mita HWMON_T_LABEL, 19752deba0fSAkinobu Mita HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | 19852deba0fSAkinobu Mita HWMON_T_LABEL, 19952deba0fSAkinobu Mita HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | 20052deba0fSAkinobu Mita HWMON_T_LABEL, 20152deba0fSAkinobu Mita HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | 20252deba0fSAkinobu Mita HWMON_T_LABEL, 20352deba0fSAkinobu Mita HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | 20452deba0fSAkinobu Mita HWMON_T_LABEL, 20552deba0fSAkinobu Mita HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | 20652deba0fSAkinobu Mita HWMON_T_LABEL, 20752deba0fSAkinobu Mita HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | 20852deba0fSAkinobu Mita HWMON_T_LABEL), 209400b6a7bSGuenter Roeck NULL 210400b6a7bSGuenter Roeck }; 211400b6a7bSGuenter Roeck 212400b6a7bSGuenter Roeck static const struct hwmon_ops nvme_hwmon_ops = { 213400b6a7bSGuenter Roeck .is_visible = nvme_hwmon_is_visible, 214400b6a7bSGuenter Roeck .read = nvme_hwmon_read, 215400b6a7bSGuenter Roeck .read_string = nvme_hwmon_read_string, 21652deba0fSAkinobu Mita .write = nvme_hwmon_write, 217400b6a7bSGuenter Roeck }; 218400b6a7bSGuenter Roeck 219400b6a7bSGuenter Roeck static const struct hwmon_chip_info nvme_hwmon_chip_info = { 220400b6a7bSGuenter Roeck .ops = &nvme_hwmon_ops, 221400b6a7bSGuenter Roeck .info = nvme_hwmon_info, 222400b6a7bSGuenter Roeck }; 223400b6a7bSGuenter Roeck 22459e330f8SKeith Busch int nvme_hwmon_init(struct nvme_ctrl *ctrl) 225400b6a7bSGuenter Roeck { 226*ed7770f6SHannes Reinecke struct device *dev = ctrl->device; 227400b6a7bSGuenter Roeck struct nvme_hwmon_data *data; 228400b6a7bSGuenter Roeck struct device *hwmon; 229400b6a7bSGuenter Roeck int err; 230400b6a7bSGuenter Roeck 231*ed7770f6SHannes Reinecke data = kzalloc(sizeof(*data), GFP_KERNEL); 232400b6a7bSGuenter Roeck if (!data) 23359e330f8SKeith Busch return 0; 234400b6a7bSGuenter Roeck 235400b6a7bSGuenter Roeck data->ctrl = ctrl; 236400b6a7bSGuenter Roeck mutex_init(&data->read_lock); 237400b6a7bSGuenter Roeck 238400b6a7bSGuenter Roeck err = nvme_hwmon_get_smart_log(data); 239400b6a7bSGuenter Roeck if (err) { 240*ed7770f6SHannes Reinecke dev_warn(dev, "Failed to read smart log (error %d)\n", err); 241*ed7770f6SHannes Reinecke kfree(data); 24259e330f8SKeith Busch return err; 243400b6a7bSGuenter Roeck } 244400b6a7bSGuenter Roeck 245*ed7770f6SHannes Reinecke hwmon = hwmon_device_register_with_info(dev, "nvme", 246*ed7770f6SHannes Reinecke data, &nvme_hwmon_chip_info, 247400b6a7bSGuenter Roeck NULL); 248400b6a7bSGuenter Roeck if (IS_ERR(hwmon)) { 249400b6a7bSGuenter Roeck dev_warn(dev, "Failed to instantiate hwmon device\n"); 250*ed7770f6SHannes Reinecke kfree(data); 251*ed7770f6SHannes Reinecke } 252*ed7770f6SHannes Reinecke ctrl->hwmon_device = hwmon; 253*ed7770f6SHannes Reinecke return 0; 254400b6a7bSGuenter Roeck } 25559e330f8SKeith Busch 256*ed7770f6SHannes Reinecke void nvme_hwmon_exit(struct nvme_ctrl *ctrl) 257*ed7770f6SHannes Reinecke { 258*ed7770f6SHannes Reinecke if (ctrl->hwmon_device) { 259*ed7770f6SHannes Reinecke struct nvme_hwmon_data *data = 260*ed7770f6SHannes Reinecke dev_get_drvdata(ctrl->hwmon_device); 261*ed7770f6SHannes Reinecke 262*ed7770f6SHannes Reinecke hwmon_device_unregister(ctrl->hwmon_device); 263*ed7770f6SHannes Reinecke ctrl->hwmon_device = NULL; 264*ed7770f6SHannes Reinecke kfree(data); 265*ed7770f6SHannes Reinecke } 266400b6a7bSGuenter Roeck } 267