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(dev: ctrl, fid: NVME_FEAT_TEMP_THRESH, dword11: threshold, NULL, buflen: 0, |
30 | result: &status); |
31 | if (ret > 0) |
32 | return -EIO; |
33 | if (ret < 0) |
34 | return ret; |
35 | *temp = kelvin_to_millicelsius(t: 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(t: 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(dev: ctrl, fid: NVME_FEAT_TEMP_THRESH, dword11: threshold, NULL, buflen: 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 | return nvme_get_log(ctrl: data->ctrl, NVME_NSID_ALL, log_page: NVME_LOG_SMART, lsp: 0, |
63 | csi: NVME_CSI_NVM, log: data->log, size: sizeof(*data->log), offset: 0); |
64 | } |
65 | |
66 | static 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(ctrl: data->ctrl, sensor: channel, under: false, temp: val); |
81 | case hwmon_temp_min: |
82 | return nvme_get_temp_thresh(ctrl: data->ctrl, sensor: channel, under: true, temp: val); |
83 | case hwmon_temp_crit: |
84 | *val = kelvin_to_millicelsius(t: 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(p: log->temperature); |
99 | else |
100 | temp = le16_to_cpu(log->temp_sensor[channel - 1]); |
101 | *val = kelvin_to_millicelsius(t: 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 | } |
110 | unlock: |
111 | mutex_unlock(lock: &data->read_lock); |
112 | return err; |
113 | } |
114 | |
115 | static 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(ctrl: data->ctrl, sensor: channel, under: false, temp: val); |
123 | case hwmon_temp_min: |
124 | return nvme_set_temp_thresh(ctrl: data->ctrl, sensor: channel, under: true, temp: val); |
125 | default: |
126 | break; |
127 | } |
128 | |
129 | return -EOPNOTSUPP; |
130 | } |
131 | |
132 | static 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 | |
144 | static 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 | |
152 | static 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 | !(data->ctrl->quirks & |
168 | NVME_QUIRK_NO_SECONDARY_TEMP_THRESH))) { |
169 | if (data->ctrl->quirks & |
170 | NVME_QUIRK_NO_TEMP_THRESH_CHANGE) |
171 | return 0444; |
172 | return 0644; |
173 | } |
174 | break; |
175 | case hwmon_temp_alarm: |
176 | if (!channel) |
177 | return 0444; |
178 | break; |
179 | case hwmon_temp_input: |
180 | case hwmon_temp_label: |
181 | if (!channel || data->log->temp_sensor[channel - 1]) |
182 | return 0444; |
183 | break; |
184 | default: |
185 | break; |
186 | } |
187 | return 0; |
188 | } |
189 | |
190 | static const struct hwmon_channel_info *const nvme_hwmon_info[] = { |
191 | HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), |
192 | HWMON_CHANNEL_INFO(temp, |
193 | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | |
194 | HWMON_T_CRIT | HWMON_T_LABEL | HWMON_T_ALARM, |
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 | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | |
210 | HWMON_T_LABEL), |
211 | NULL |
212 | }; |
213 | |
214 | static const struct hwmon_ops nvme_hwmon_ops = { |
215 | .is_visible = nvme_hwmon_is_visible, |
216 | .read = nvme_hwmon_read, |
217 | .read_string = nvme_hwmon_read_string, |
218 | .write = nvme_hwmon_write, |
219 | }; |
220 | |
221 | static const struct hwmon_chip_info nvme_hwmon_chip_info = { |
222 | .ops = &nvme_hwmon_ops, |
223 | .info = nvme_hwmon_info, |
224 | }; |
225 | |
226 | int nvme_hwmon_init(struct nvme_ctrl *ctrl) |
227 | { |
228 | struct device *dev = ctrl->device; |
229 | struct nvme_hwmon_data *data; |
230 | struct device *hwmon; |
231 | int err; |
232 | |
233 | data = kzalloc(size: sizeof(*data), GFP_KERNEL); |
234 | if (!data) |
235 | return -ENOMEM; |
236 | |
237 | data->log = kzalloc(size: sizeof(*data->log), GFP_KERNEL); |
238 | if (!data->log) { |
239 | err = -ENOMEM; |
240 | goto err_free_data; |
241 | } |
242 | |
243 | data->ctrl = ctrl; |
244 | mutex_init(&data->read_lock); |
245 | |
246 | err = nvme_hwmon_get_smart_log(data); |
247 | if (err) { |
248 | dev_warn(dev, "Failed to read smart log (error %d)\n" , err); |
249 | goto err_free_log; |
250 | } |
251 | |
252 | hwmon = hwmon_device_register_with_info(dev, name: "nvme" , |
253 | drvdata: data, info: &nvme_hwmon_chip_info, |
254 | NULL); |
255 | if (IS_ERR(ptr: hwmon)) { |
256 | dev_warn(dev, "Failed to instantiate hwmon device\n" ); |
257 | err = PTR_ERR(ptr: hwmon); |
258 | goto err_free_log; |
259 | } |
260 | ctrl->hwmon_device = hwmon; |
261 | return 0; |
262 | |
263 | err_free_log: |
264 | kfree(objp: data->log); |
265 | err_free_data: |
266 | kfree(objp: data); |
267 | return err; |
268 | } |
269 | |
270 | void nvme_hwmon_exit(struct nvme_ctrl *ctrl) |
271 | { |
272 | if (ctrl->hwmon_device) { |
273 | struct nvme_hwmon_data *data = |
274 | dev_get_drvdata(dev: ctrl->hwmon_device); |
275 | |
276 | hwmon_device_unregister(dev: ctrl->hwmon_device); |
277 | ctrl->hwmon_device = NULL; |
278 | kfree(objp: data->log); |
279 | kfree(objp: data); |
280 | } |
281 | } |
282 | |