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
23struct 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 */
29struct 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
43static 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
54static 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
67static 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
83static 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
113static 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
125static 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
144static 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
156static 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
163static 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
193static 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
207static 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
228static 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
247static 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
267static 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
284static 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
294static 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
335err_free_temp_channel_desc:
336 kfree(objp: hwmon->temp_channel_desc);
337err_free_hwmon:
338 kfree(objp: hwmon);
339 return ERR_PTR(error: err);
340}
341
342static 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
374int 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
403err_free_hwmon:
404 mlx5_hwmon_free(hwmon);
405 return err;
406}
407
408void 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

source code of linux/drivers/net/ethernet/mellanox/mlx5/core/hwmon.c