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(mdev, mtmp_in,  sizeof(mtmp_in),
50				    mtmp_out, MLX5_ST_SZ_BYTES(mtmp_reg),
51				    MLX5_REG_MTMP, 0, 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(mdev, mtmp_in,  sizeof(mtmp_in),
63				    mtmp_out, sizeof(mtmp_out),
64				    MLX5_REG_MTMP, 0, 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_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
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(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
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(hwmon->mdev,
122				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(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
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(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
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(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
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);
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
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(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
284static 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
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(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
335err_free_temp_channel_desc:
336	kfree(hwmon->temp_channel_desc);
337err_free_hwmon:
338	kfree(hwmon);
339	return ERR_PTR(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(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
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(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
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(hwmon->hwmon_dev);
416	mlx5_hwmon_free(hwmon);
417	mdev->hwmon = NULL;
418}
419