File Index
Files and directories
Sign in
FILE

hwmon.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/mellanox/mlx5/core/hwmon.c C
  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
420const char *hwmon_get_sensor_name(struct mlx5_hwmon *hwmon, int channel)
421{
422	return hwmon->temp_channel_desc[channel].sensor_name;
423}
~/ Read-only file preview.