linux/drivers/nvme/host/hwmon.c
<<
>>
Prefs
   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}
 268