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