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(mdev, mtmp_in,  sizeof(mtmp_in),
50 				    mtmp_out, MLX5_ST_SZ_BYTES(mtmp_reg),
51 				    MLX5_REG_MTMP, 0, 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(mdev, mtmp_in,  sizeof(mtmp_in),
63 				    mtmp_out, sizeof(mtmp_out),
64 				    MLX5_REG_MTMP, 0, 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_in);
74 	if (err)
75 		return err;
76 
77 	MLX5_SET(mtmp_reg, mtmp_in, mte, 1);
78 	return mlx5_core_access_reg(mdev, mtmp_in,  sizeof(mtmp_in),
79 				    mtmp_out, sizeof(mtmp_out),
80 				    MLX5_REG_MTMP, 0, 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(hwmon->mdev, 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(hwmon->mdev,
122 				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(hwmon->mdev, 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(hwmon->temp_channel_desc[i].sensor_name,
180 				 sizeof(hwmon->temp_channel_desc[i].sensor_name), "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(mdev, &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(smap, 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(hwmon->mdev,
223 							 &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);
254 	if (err)
255 		return err;
256 
257 	err = mlx5_hwmon_query_mtmp(mdev, 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(mdev, mtcap_in,  sizeof(mtcap_in),
274 				   mtcap_out, sizeof(mtcap_out),
275 				   MLX5_REG_MTCAP, 0, 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(hwmon->temp_channel_config);
290 	kfree(hwmon->temp_channel_desc);
291 	kfree(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(sizeof(*mdev->hwmon), GFP_KERNEL);
302 	if (!hwmon)
303 		return ERR_PTR(-ENOMEM);
304 
305 	err = mlx5_hwmon_get_sensors_count(mdev, &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);
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(sensors_count, 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(sensors_count + 1, 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(hwmon->temp_channel_desc);
337 err_free_hwmon:
338 	kfree(hwmon);
339 	return ERR_PTR(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(hwmon->mdev, mtcap_in,  sizeof(mtcap_in),
350 				    mtcap_out, sizeof(mtcap_out),
351 				    MLX5_REG_MTCAP, 0, 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(hwmon->mdev,
363 						 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(hwmon))
385 		return PTR_ERR(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, "mlx5",
392 							   hwmon,
393 							   &hwmon->chip,
394 							   NULL);
395 	if (IS_ERR(hwmon->hwmon_dev)) {
396 		err = PTR_ERR(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(hwmon->hwmon_dev);
416 	mlx5_hwmon_free(hwmon);
417 	mdev->hwmon = NULL;
418 }
419