brintos

brintos / linux-shallow public Read only

0
0
Text · 10.5 KiB · 353f81d Raw
419 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB2// Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved3 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 114 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 expect19 * 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