xref: /openbmc/linux/drivers/nvme/host/hwmon.c (revision 76ce0265)
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