1 | // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB |
2 | // Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved |
3 | |
4 | #include <linux/hwmon.h> |
5 | #include <linux/bitmap.h> |
6 | #include <linux/mlx5/device.h> |
7 | #include <linux/mlx5/mlx5_ifc.h> |
8 | #include <linux/mlx5/port.h> |
9 | #include "mlx5_core.h" |
10 | #include "hwmon.h" |
11 | |
12 | #define CHANNELS_TYPE_NUM 2 /* chip channel and temp channel */ |
13 | #define CHIP_CONFIG_NUM 1 |
14 | |
15 | /* module 0 is mapped to sensor_index 64 in MTMP register */ |
16 | #define to_mtmp_module_sensor_idx(idx) (64 + (idx)) |
17 | |
18 | /* All temperatures retrieved in units of 0.125C. hwmon framework expect |
19 | * it in units of millidegrees C. Hence multiply values by 125. |
20 | */ |
21 | #define mtmp_temp_to_mdeg(temp) ((temp) * 125) |
22 | |
23 | struct temp_channel_desc { |
24 | u32 sensor_index; |
25 | char sensor_name[32]; |
26 | }; |
27 | |
28 | /* chip_channel_config and channel_info arrays must be 0-terminated, hence + 1 */ |
29 | struct mlx5_hwmon { |
30 | struct mlx5_core_dev *mdev; |
31 | struct device *hwmon_dev; |
32 | struct hwmon_channel_info chip_info; |
33 | u32 chip_channel_config[CHIP_CONFIG_NUM + 1]; |
34 | struct hwmon_channel_info temp_info; |
35 | u32 *temp_channel_config; |
36 | const struct hwmon_channel_info *channel_info[CHANNELS_TYPE_NUM + 1]; |
37 | struct hwmon_chip_info chip; |
38 | struct temp_channel_desc *temp_channel_desc; |
39 | u32 asic_platform_scount; |
40 | u32 module_scount; |
41 | }; |
42 | |
43 | static int mlx5_hwmon_query_mtmp(struct mlx5_core_dev *mdev, u32 sensor_index, u32 *mtmp_out) |
44 | { |
45 | u32 mtmp_in[MLX5_ST_SZ_DW(mtmp_reg)] = {}; |
46 | |
47 | MLX5_SET(mtmp_reg, mtmp_in, sensor_index, sensor_index); |
48 | |
49 | return mlx5_core_access_reg(dev: mdev, data_in: mtmp_in, size_in: sizeof(mtmp_in), |
50 | data_out: mtmp_out, MLX5_ST_SZ_BYTES(mtmp_reg), |
51 | reg_num: MLX5_REG_MTMP, arg: 0, write: 0); |
52 | } |
53 | |
54 | static int mlx5_hwmon_reset_max_temp(struct mlx5_core_dev *mdev, int sensor_index) |
55 | { |
56 | u32 mtmp_out[MLX5_ST_SZ_DW(mtmp_reg)] = {}; |
57 | u32 mtmp_in[MLX5_ST_SZ_DW(mtmp_reg)] = {}; |
58 | |
59 | MLX5_SET(mtmp_reg, mtmp_in, sensor_index, sensor_index); |
60 | MLX5_SET(mtmp_reg, mtmp_in, mtr, 1); |
61 | |
62 | return mlx5_core_access_reg(dev: mdev, data_in: mtmp_in, size_in: sizeof(mtmp_in), |
63 | data_out: mtmp_out, size_out: sizeof(mtmp_out), |
64 | reg_num: MLX5_REG_MTMP, arg: 0, write: 0); |
65 | } |
66 | |
67 | static int mlx5_hwmon_enable_max_temp(struct mlx5_core_dev *mdev, int sensor_index) |
68 | { |
69 | u32 mtmp_out[MLX5_ST_SZ_DW(mtmp_reg)] = {}; |
70 | u32 mtmp_in[MLX5_ST_SZ_DW(mtmp_reg)] = {}; |
71 | int err; |
72 | |
73 | err = mlx5_hwmon_query_mtmp(mdev, sensor_index, mtmp_out: mtmp_in); |
74 | if (err) |
75 | return err; |
76 | |
77 | MLX5_SET(mtmp_reg, mtmp_in, mte, 1); |
78 | return mlx5_core_access_reg(dev: mdev, data_in: mtmp_in, size_in: sizeof(mtmp_in), |
79 | data_out: mtmp_out, size_out: sizeof(mtmp_out), |
80 | reg_num: MLX5_REG_MTMP, arg: 0, write: 1); |
81 | } |
82 | |
83 | static int mlx5_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, |
84 | int channel, long *val) |
85 | { |
86 | struct mlx5_hwmon *hwmon = dev_get_drvdata(dev); |
87 | u32 mtmp_out[MLX5_ST_SZ_DW(mtmp_reg)] = {}; |
88 | int err; |
89 | |
90 | if (type != hwmon_temp) |
91 | return -EOPNOTSUPP; |
92 | |
93 | err = mlx5_hwmon_query_mtmp(mdev: hwmon->mdev, sensor_index: hwmon->temp_channel_desc[channel].sensor_index, |
94 | mtmp_out); |
95 | if (err) |
96 | return err; |
97 | |
98 | switch (attr) { |
99 | case hwmon_temp_input: |
100 | *val = mtmp_temp_to_mdeg(MLX5_GET(mtmp_reg, mtmp_out, temperature)); |
101 | return 0; |
102 | case hwmon_temp_highest: |
103 | *val = mtmp_temp_to_mdeg(MLX5_GET(mtmp_reg, mtmp_out, max_temperature)); |
104 | return 0; |
105 | case hwmon_temp_crit: |
106 | *val = mtmp_temp_to_mdeg(MLX5_GET(mtmp_reg, mtmp_out, temp_threshold_hi)); |
107 | return 0; |
108 | default: |
109 | return -EOPNOTSUPP; |
110 | } |
111 | } |
112 | |
113 | static int mlx5_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, |
114 | int channel, long val) |
115 | { |
116 | struct mlx5_hwmon *hwmon = dev_get_drvdata(dev); |
117 | |
118 | if (type != hwmon_temp || attr != hwmon_temp_reset_history) |
119 | return -EOPNOTSUPP; |
120 | |
121 | return mlx5_hwmon_reset_max_temp(mdev: hwmon->mdev, |
122 | sensor_index: hwmon->temp_channel_desc[channel].sensor_index); |
123 | } |
124 | |
125 | static umode_t mlx5_hwmon_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, |
126 | int channel) |
127 | { |
128 | if (type != hwmon_temp) |
129 | return 0; |
130 | |
131 | switch (attr) { |
132 | case hwmon_temp_input: |
133 | case hwmon_temp_highest: |
134 | case hwmon_temp_crit: |
135 | case hwmon_temp_label: |
136 | return 0444; |
137 | case hwmon_temp_reset_history: |
138 | return 0200; |
139 | default: |
140 | return 0; |
141 | } |
142 | } |
143 | |
144 | static int mlx5_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, |
145 | int channel, const char **str) |
146 | { |
147 | struct mlx5_hwmon *hwmon = dev_get_drvdata(dev); |
148 | |
149 | if (type != hwmon_temp || attr != hwmon_temp_label) |
150 | return -EOPNOTSUPP; |
151 | |
152 | *str = (const char *)hwmon->temp_channel_desc[channel].sensor_name; |
153 | return 0; |
154 | } |
155 | |
156 | static const struct hwmon_ops mlx5_hwmon_ops = { |
157 | .read = mlx5_hwmon_read, |
158 | .read_string = mlx5_hwmon_read_string, |
159 | .is_visible = mlx5_hwmon_is_visible, |
160 | .write = mlx5_hwmon_write, |
161 | }; |
162 | |
163 | static int mlx5_hwmon_init_channels_names(struct mlx5_hwmon *hwmon) |
164 | { |
165 | u32 i; |
166 | |
167 | for (i = 0; i < hwmon->asic_platform_scount + hwmon->module_scount; i++) { |
168 | u32 mtmp_out[MLX5_ST_SZ_DW(mtmp_reg)] = {}; |
169 | char *sensor_name; |
170 | int err; |
171 | |
172 | err = mlx5_hwmon_query_mtmp(mdev: hwmon->mdev, sensor_index: hwmon->temp_channel_desc[i].sensor_index, |
173 | mtmp_out); |
174 | if (err) |
175 | return err; |
176 | |
177 | sensor_name = MLX5_ADDR_OF(mtmp_reg, mtmp_out, sensor_name_hi); |
178 | if (!*sensor_name) { |
179 | snprintf(buf: hwmon->temp_channel_desc[i].sensor_name, |
180 | size: sizeof(hwmon->temp_channel_desc[i].sensor_name), fmt: "sensor%u" , |
181 | hwmon->temp_channel_desc[i].sensor_index); |
182 | continue; |
183 | } |
184 | |
185 | memcpy(&hwmon->temp_channel_desc[i].sensor_name, sensor_name, |
186 | MLX5_FLD_SZ_BYTES(mtmp_reg, sensor_name_hi) + |
187 | MLX5_FLD_SZ_BYTES(mtmp_reg, sensor_name_lo)); |
188 | } |
189 | |
190 | return 0; |
191 | } |
192 | |
193 | static int mlx5_hwmon_get_module_sensor_index(struct mlx5_core_dev *mdev, u32 *module_index) |
194 | { |
195 | int module_num; |
196 | int err; |
197 | |
198 | err = mlx5_query_module_num(dev: mdev, module_num: &module_num); |
199 | if (err) |
200 | return err; |
201 | |
202 | *module_index = to_mtmp_module_sensor_idx(module_num); |
203 | |
204 | return 0; |
205 | } |
206 | |
207 | static int mlx5_hwmon_init_sensors_indexes(struct mlx5_hwmon *hwmon, u64 sensor_map) |
208 | { |
209 | DECLARE_BITMAP(smap, BITS_PER_TYPE(sensor_map)); |
210 | unsigned long bit_pos; |
211 | int err = 0; |
212 | int i = 0; |
213 | |
214 | bitmap_from_u64(dst: smap, mask: sensor_map); |
215 | |
216 | for_each_set_bit(bit_pos, smap, BITS_PER_TYPE(sensor_map)) { |
217 | hwmon->temp_channel_desc[i].sensor_index = bit_pos; |
218 | i++; |
219 | } |
220 | |
221 | if (hwmon->module_scount) |
222 | err = mlx5_hwmon_get_module_sensor_index(mdev: hwmon->mdev, |
223 | module_index: &hwmon->temp_channel_desc[i].sensor_index); |
224 | |
225 | return err; |
226 | } |
227 | |
228 | static void mlx5_hwmon_channel_info_init(struct mlx5_hwmon *hwmon) |
229 | { |
230 | int i; |
231 | |
232 | hwmon->channel_info[0] = &hwmon->chip_info; |
233 | hwmon->channel_info[1] = &hwmon->temp_info; |
234 | |
235 | hwmon->chip_channel_config[0] = HWMON_C_REGISTER_TZ; |
236 | hwmon->chip_info.config = (const u32 *)hwmon->chip_channel_config; |
237 | hwmon->chip_info.type = hwmon_chip; |
238 | |
239 | for (i = 0; i < hwmon->asic_platform_scount + hwmon->module_scount; i++) |
240 | hwmon->temp_channel_config[i] = HWMON_T_INPUT | HWMON_T_HIGHEST | HWMON_T_CRIT | |
241 | HWMON_T_RESET_HISTORY | HWMON_T_LABEL; |
242 | |
243 | hwmon->temp_info.config = (const u32 *)hwmon->temp_channel_config; |
244 | hwmon->temp_info.type = hwmon_temp; |
245 | } |
246 | |
247 | static int mlx5_hwmon_is_module_mon_cap(struct mlx5_core_dev *mdev, bool *mon_cap) |
248 | { |
249 | u32 mtmp_out[MLX5_ST_SZ_DW(mtmp_reg)]; |
250 | u32 module_index; |
251 | int err; |
252 | |
253 | err = mlx5_hwmon_get_module_sensor_index(mdev, module_index: &module_index); |
254 | if (err) |
255 | return err; |
256 | |
257 | err = mlx5_hwmon_query_mtmp(mdev, sensor_index: module_index, mtmp_out); |
258 | if (err) |
259 | return err; |
260 | |
261 | if (MLX5_GET(mtmp_reg, mtmp_out, temperature)) |
262 | *mon_cap = true; |
263 | |
264 | return 0; |
265 | } |
266 | |
267 | static int mlx5_hwmon_get_sensors_count(struct mlx5_core_dev *mdev, u32 *asic_platform_scount) |
268 | { |
269 | u32 mtcap_out[MLX5_ST_SZ_DW(mtcap_reg)] = {}; |
270 | u32 mtcap_in[MLX5_ST_SZ_DW(mtcap_reg)] = {}; |
271 | int err; |
272 | |
273 | err = mlx5_core_access_reg(dev: mdev, data_in: mtcap_in, size_in: sizeof(mtcap_in), |
274 | data_out: mtcap_out, size_out: sizeof(mtcap_out), |
275 | reg_num: MLX5_REG_MTCAP, arg: 0, write: 0); |
276 | if (err) |
277 | return err; |
278 | |
279 | *asic_platform_scount = MLX5_GET(mtcap_reg, mtcap_out, sensor_count); |
280 | |
281 | return 0; |
282 | } |
283 | |
284 | static void mlx5_hwmon_free(struct mlx5_hwmon *hwmon) |
285 | { |
286 | if (!hwmon) |
287 | return; |
288 | |
289 | kfree(objp: hwmon->temp_channel_config); |
290 | kfree(objp: hwmon->temp_channel_desc); |
291 | kfree(objp: hwmon); |
292 | } |
293 | |
294 | static struct mlx5_hwmon *mlx5_hwmon_alloc(struct mlx5_core_dev *mdev) |
295 | { |
296 | struct mlx5_hwmon *hwmon; |
297 | bool mon_cap = false; |
298 | u32 sensors_count; |
299 | int err; |
300 | |
301 | hwmon = kzalloc(size: sizeof(*mdev->hwmon), GFP_KERNEL); |
302 | if (!hwmon) |
303 | return ERR_PTR(error: -ENOMEM); |
304 | |
305 | err = mlx5_hwmon_get_sensors_count(mdev, asic_platform_scount: &hwmon->asic_platform_scount); |
306 | if (err) |
307 | goto err_free_hwmon; |
308 | |
309 | /* check if module sensor has thermal mon cap. if yes, allocate channel desc for it */ |
310 | err = mlx5_hwmon_is_module_mon_cap(mdev, mon_cap: &mon_cap); |
311 | if (err) |
312 | goto err_free_hwmon; |
313 | |
314 | hwmon->module_scount = mon_cap ? 1 : 0; |
315 | sensors_count = hwmon->asic_platform_scount + hwmon->module_scount; |
316 | hwmon->temp_channel_desc = kcalloc(n: sensors_count, size: sizeof(*hwmon->temp_channel_desc), |
317 | GFP_KERNEL); |
318 | if (!hwmon->temp_channel_desc) { |
319 | err = -ENOMEM; |
320 | goto err_free_hwmon; |
321 | } |
322 | |
323 | /* sensors configuration values array, must be 0-terminated hence, + 1 */ |
324 | hwmon->temp_channel_config = kcalloc(n: sensors_count + 1, size: sizeof(*hwmon->temp_channel_config), |
325 | GFP_KERNEL); |
326 | if (!hwmon->temp_channel_config) { |
327 | err = -ENOMEM; |
328 | goto err_free_temp_channel_desc; |
329 | } |
330 | |
331 | hwmon->mdev = mdev; |
332 | |
333 | return hwmon; |
334 | |
335 | err_free_temp_channel_desc: |
336 | kfree(objp: hwmon->temp_channel_desc); |
337 | err_free_hwmon: |
338 | kfree(objp: hwmon); |
339 | return ERR_PTR(error: err); |
340 | } |
341 | |
342 | static int mlx5_hwmon_dev_init(struct mlx5_hwmon *hwmon) |
343 | { |
344 | u32 mtcap_out[MLX5_ST_SZ_DW(mtcap_reg)] = {}; |
345 | u32 mtcap_in[MLX5_ST_SZ_DW(mtcap_reg)] = {}; |
346 | int err; |
347 | int i; |
348 | |
349 | err = mlx5_core_access_reg(dev: hwmon->mdev, data_in: mtcap_in, size_in: sizeof(mtcap_in), |
350 | data_out: mtcap_out, size_out: sizeof(mtcap_out), |
351 | reg_num: MLX5_REG_MTCAP, arg: 0, write: 0); |
352 | if (err) |
353 | return err; |
354 | |
355 | mlx5_hwmon_channel_info_init(hwmon); |
356 | mlx5_hwmon_init_sensors_indexes(hwmon, MLX5_GET64(mtcap_reg, mtcap_out, sensor_map)); |
357 | err = mlx5_hwmon_init_channels_names(hwmon); |
358 | if (err) |
359 | return err; |
360 | |
361 | for (i = 0; i < hwmon->asic_platform_scount + hwmon->module_scount; i++) { |
362 | err = mlx5_hwmon_enable_max_temp(mdev: hwmon->mdev, |
363 | sensor_index: hwmon->temp_channel_desc[i].sensor_index); |
364 | if (err) |
365 | return err; |
366 | } |
367 | |
368 | hwmon->chip.ops = &mlx5_hwmon_ops; |
369 | hwmon->chip.info = (const struct hwmon_channel_info **)hwmon->channel_info; |
370 | |
371 | return 0; |
372 | } |
373 | |
374 | int mlx5_hwmon_dev_register(struct mlx5_core_dev *mdev) |
375 | { |
376 | struct device *dev = mdev->device; |
377 | struct mlx5_hwmon *hwmon; |
378 | int err; |
379 | |
380 | if (!MLX5_CAP_MCAM_REG(mdev, mtmp)) |
381 | return 0; |
382 | |
383 | hwmon = mlx5_hwmon_alloc(mdev); |
384 | if (IS_ERR(ptr: hwmon)) |
385 | return PTR_ERR(ptr: hwmon); |
386 | |
387 | err = mlx5_hwmon_dev_init(hwmon); |
388 | if (err) |
389 | goto err_free_hwmon; |
390 | |
391 | hwmon->hwmon_dev = hwmon_device_register_with_info(dev, name: "mlx5" , |
392 | drvdata: hwmon, |
393 | info: &hwmon->chip, |
394 | NULL); |
395 | if (IS_ERR(ptr: hwmon->hwmon_dev)) { |
396 | err = PTR_ERR(ptr: hwmon->hwmon_dev); |
397 | goto err_free_hwmon; |
398 | } |
399 | |
400 | mdev->hwmon = hwmon; |
401 | return 0; |
402 | |
403 | err_free_hwmon: |
404 | mlx5_hwmon_free(hwmon); |
405 | return err; |
406 | } |
407 | |
408 | void mlx5_hwmon_dev_unregister(struct mlx5_core_dev *mdev) |
409 | { |
410 | struct mlx5_hwmon *hwmon = mdev->hwmon; |
411 | |
412 | if (!hwmon) |
413 | return; |
414 | |
415 | hwmon_device_unregister(dev: hwmon->hwmon_dev); |
416 | mlx5_hwmon_free(hwmon); |
417 | mdev->hwmon = NULL; |
418 | } |
419 | |