123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347 |
- // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
- /* Copyright (c) 2015-2018 Mellanox Technologies. All rights reserved */
- #include <linux/kernel.h>
- #include <linux/types.h>
- #include <linux/device.h>
- #include <linux/sysfs.h>
- #include <linux/hwmon.h>
- #include <linux/err.h>
- #include "core.h"
- #define MLXSW_HWMON_TEMP_SENSOR_MAX_COUNT 127
- #define MLXSW_HWMON_ATTR_COUNT (MLXSW_HWMON_TEMP_SENSOR_MAX_COUNT * 4 + \
- MLXSW_MFCR_TACHOS_MAX + MLXSW_MFCR_PWMS_MAX)
- struct mlxsw_hwmon_attr {
- struct device_attribute dev_attr;
- struct mlxsw_hwmon *hwmon;
- unsigned int type_index;
- char name[32];
- };
- struct mlxsw_hwmon {
- struct mlxsw_core *core;
- const struct mlxsw_bus_info *bus_info;
- struct device *hwmon_dev;
- struct attribute_group group;
- const struct attribute_group *groups[2];
- struct attribute *attrs[MLXSW_HWMON_ATTR_COUNT + 1];
- struct mlxsw_hwmon_attr hwmon_attrs[MLXSW_HWMON_ATTR_COUNT];
- unsigned int attrs_count;
- };
- static ssize_t mlxsw_hwmon_temp_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
- {
- struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
- container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
- struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
- char mtmp_pl[MLXSW_REG_MTMP_LEN];
- unsigned int temp;
- int err;
- mlxsw_reg_mtmp_pack(mtmp_pl, mlwsw_hwmon_attr->type_index,
- false, false);
- err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);
- if (err) {
- dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query temp sensor\n");
- return err;
- }
- mlxsw_reg_mtmp_unpack(mtmp_pl, &temp, NULL, NULL);
- return sprintf(buf, "%u\n", temp);
- }
- static ssize_t mlxsw_hwmon_temp_max_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
- {
- struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
- container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
- struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
- char mtmp_pl[MLXSW_REG_MTMP_LEN];
- unsigned int temp_max;
- int err;
- mlxsw_reg_mtmp_pack(mtmp_pl, mlwsw_hwmon_attr->type_index,
- false, false);
- err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);
- if (err) {
- dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query temp sensor\n");
- return err;
- }
- mlxsw_reg_mtmp_unpack(mtmp_pl, NULL, &temp_max, NULL);
- return sprintf(buf, "%u\n", temp_max);
- }
- static ssize_t mlxsw_hwmon_temp_rst_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t len)
- {
- struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
- container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
- struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
- char mtmp_pl[MLXSW_REG_MTMP_LEN];
- unsigned long val;
- int err;
- err = kstrtoul(buf, 10, &val);
- if (err)
- return err;
- if (val != 1)
- return -EINVAL;
- mlxsw_reg_mtmp_pack(mtmp_pl, mlwsw_hwmon_attr->type_index, true, true);
- err = mlxsw_reg_write(mlxsw_hwmon->core, MLXSW_REG(mtmp), mtmp_pl);
- if (err) {
- dev_err(mlxsw_hwmon->bus_info->dev, "Failed to reset temp sensor history\n");
- return err;
- }
- return len;
- }
- static ssize_t mlxsw_hwmon_fan_rpm_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
- {
- struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
- container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
- struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
- char mfsm_pl[MLXSW_REG_MFSM_LEN];
- int err;
- mlxsw_reg_mfsm_pack(mfsm_pl, mlwsw_hwmon_attr->type_index);
- err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfsm), mfsm_pl);
- if (err) {
- dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query fan\n");
- return err;
- }
- return sprintf(buf, "%u\n", mlxsw_reg_mfsm_rpm_get(mfsm_pl));
- }
- static ssize_t mlxsw_hwmon_pwm_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
- {
- struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
- container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
- struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
- char mfsc_pl[MLXSW_REG_MFSC_LEN];
- int err;
- mlxsw_reg_mfsc_pack(mfsc_pl, mlwsw_hwmon_attr->type_index, 0);
- err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfsc), mfsc_pl);
- if (err) {
- dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query PWM\n");
- return err;
- }
- return sprintf(buf, "%u\n",
- mlxsw_reg_mfsc_pwm_duty_cycle_get(mfsc_pl));
- }
- static ssize_t mlxsw_hwmon_pwm_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t len)
- {
- struct mlxsw_hwmon_attr *mlwsw_hwmon_attr =
- container_of(attr, struct mlxsw_hwmon_attr, dev_attr);
- struct mlxsw_hwmon *mlxsw_hwmon = mlwsw_hwmon_attr->hwmon;
- char mfsc_pl[MLXSW_REG_MFSC_LEN];
- unsigned long val;
- int err;
- err = kstrtoul(buf, 10, &val);
- if (err)
- return err;
- if (val > 255)
- return -EINVAL;
- mlxsw_reg_mfsc_pack(mfsc_pl, mlwsw_hwmon_attr->type_index, val);
- err = mlxsw_reg_write(mlxsw_hwmon->core, MLXSW_REG(mfsc), mfsc_pl);
- if (err) {
- dev_err(mlxsw_hwmon->bus_info->dev, "Failed to write PWM\n");
- return err;
- }
- return len;
- }
- enum mlxsw_hwmon_attr_type {
- MLXSW_HWMON_ATTR_TYPE_TEMP,
- MLXSW_HWMON_ATTR_TYPE_TEMP_MAX,
- MLXSW_HWMON_ATTR_TYPE_TEMP_RST,
- MLXSW_HWMON_ATTR_TYPE_FAN_RPM,
- MLXSW_HWMON_ATTR_TYPE_PWM,
- };
- static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon *mlxsw_hwmon,
- enum mlxsw_hwmon_attr_type attr_type,
- unsigned int type_index, unsigned int num) {
- struct mlxsw_hwmon_attr *mlxsw_hwmon_attr;
- unsigned int attr_index;
- attr_index = mlxsw_hwmon->attrs_count;
- mlxsw_hwmon_attr = &mlxsw_hwmon->hwmon_attrs[attr_index];
- switch (attr_type) {
- case MLXSW_HWMON_ATTR_TYPE_TEMP:
- mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_temp_show;
- mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
- snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
- "temp%u_input", num + 1);
- break;
- case MLXSW_HWMON_ATTR_TYPE_TEMP_MAX:
- mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_temp_max_show;
- mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
- snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
- "temp%u_highest", num + 1);
- break;
- case MLXSW_HWMON_ATTR_TYPE_TEMP_RST:
- mlxsw_hwmon_attr->dev_attr.store = mlxsw_hwmon_temp_rst_store;
- mlxsw_hwmon_attr->dev_attr.attr.mode = 0200;
- snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
- "temp%u_reset_history", num + 1);
- break;
- case MLXSW_HWMON_ATTR_TYPE_FAN_RPM:
- mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_fan_rpm_show;
- mlxsw_hwmon_attr->dev_attr.attr.mode = 0444;
- snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
- "fan%u_input", num + 1);
- break;
- case MLXSW_HWMON_ATTR_TYPE_PWM:
- mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_pwm_show;
- mlxsw_hwmon_attr->dev_attr.store = mlxsw_hwmon_pwm_store;
- mlxsw_hwmon_attr->dev_attr.attr.mode = 0644;
- snprintf(mlxsw_hwmon_attr->name, sizeof(mlxsw_hwmon_attr->name),
- "pwm%u", num + 1);
- break;
- default:
- WARN_ON(1);
- }
- mlxsw_hwmon_attr->type_index = type_index;
- mlxsw_hwmon_attr->hwmon = mlxsw_hwmon;
- mlxsw_hwmon_attr->dev_attr.attr.name = mlxsw_hwmon_attr->name;
- sysfs_attr_init(&mlxsw_hwmon_attr->dev_attr.attr);
- mlxsw_hwmon->attrs[attr_index] = &mlxsw_hwmon_attr->dev_attr.attr;
- mlxsw_hwmon->attrs_count++;
- }
- static int mlxsw_hwmon_temp_init(struct mlxsw_hwmon *mlxsw_hwmon)
- {
- char mtcap_pl[MLXSW_REG_MTCAP_LEN] = {0};
- char mtmp_pl[MLXSW_REG_MTMP_LEN];
- u8 sensor_count;
- int i;
- int err;
- err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mtcap), mtcap_pl);
- if (err) {
- dev_err(mlxsw_hwmon->bus_info->dev, "Failed to get number of temp sensors\n");
- return err;
- }
- sensor_count = mlxsw_reg_mtcap_sensor_count_get(mtcap_pl);
- for (i = 0; i < sensor_count; i++) {
- mlxsw_reg_mtmp_pack(mtmp_pl, i, true, true);
- err = mlxsw_reg_write(mlxsw_hwmon->core,
- MLXSW_REG(mtmp), mtmp_pl);
- if (err) {
- dev_err(mlxsw_hwmon->bus_info->dev, "Failed to setup temp sensor number %d\n",
- i);
- return err;
- }
- mlxsw_hwmon_attr_add(mlxsw_hwmon,
- MLXSW_HWMON_ATTR_TYPE_TEMP, i, i);
- mlxsw_hwmon_attr_add(mlxsw_hwmon,
- MLXSW_HWMON_ATTR_TYPE_TEMP_MAX, i, i);
- mlxsw_hwmon_attr_add(mlxsw_hwmon,
- MLXSW_HWMON_ATTR_TYPE_TEMP_RST, i, i);
- }
- return 0;
- }
- static int mlxsw_hwmon_fans_init(struct mlxsw_hwmon *mlxsw_hwmon)
- {
- char mfcr_pl[MLXSW_REG_MFCR_LEN] = {0};
- enum mlxsw_reg_mfcr_pwm_frequency freq;
- unsigned int type_index;
- unsigned int num;
- u16 tacho_active;
- u8 pwm_active;
- int err;
- err = mlxsw_reg_query(mlxsw_hwmon->core, MLXSW_REG(mfcr), mfcr_pl);
- if (err) {
- dev_err(mlxsw_hwmon->bus_info->dev, "Failed to get to probe PWMs and Tachometers\n");
- return err;
- }
- mlxsw_reg_mfcr_unpack(mfcr_pl, &freq, &tacho_active, &pwm_active);
- num = 0;
- for (type_index = 0; type_index < MLXSW_MFCR_TACHOS_MAX; type_index++) {
- if (tacho_active & BIT(type_index))
- mlxsw_hwmon_attr_add(mlxsw_hwmon,
- MLXSW_HWMON_ATTR_TYPE_FAN_RPM,
- type_index, num++);
- }
- num = 0;
- for (type_index = 0; type_index < MLXSW_MFCR_PWMS_MAX; type_index++) {
- if (pwm_active & BIT(type_index))
- mlxsw_hwmon_attr_add(mlxsw_hwmon,
- MLXSW_HWMON_ATTR_TYPE_PWM,
- type_index, num++);
- }
- return 0;
- }
- int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
- const struct mlxsw_bus_info *mlxsw_bus_info,
- struct mlxsw_hwmon **p_hwmon)
- {
- struct mlxsw_hwmon *mlxsw_hwmon;
- struct device *hwmon_dev;
- int err;
- mlxsw_hwmon = kzalloc(sizeof(*mlxsw_hwmon), GFP_KERNEL);
- if (!mlxsw_hwmon)
- return -ENOMEM;
- mlxsw_hwmon->core = mlxsw_core;
- mlxsw_hwmon->bus_info = mlxsw_bus_info;
- err = mlxsw_hwmon_temp_init(mlxsw_hwmon);
- if (err)
- goto err_temp_init;
- err = mlxsw_hwmon_fans_init(mlxsw_hwmon);
- if (err)
- goto err_fans_init;
- mlxsw_hwmon->groups[0] = &mlxsw_hwmon->group;
- mlxsw_hwmon->group.attrs = mlxsw_hwmon->attrs;
- hwmon_dev = hwmon_device_register_with_groups(mlxsw_bus_info->dev,
- "mlxsw", mlxsw_hwmon,
- mlxsw_hwmon->groups);
- if (IS_ERR(hwmon_dev)) {
- err = PTR_ERR(hwmon_dev);
- goto err_hwmon_register;
- }
- mlxsw_hwmon->hwmon_dev = hwmon_dev;
- *p_hwmon = mlxsw_hwmon;
- return 0;
- err_hwmon_register:
- err_fans_init:
- err_temp_init:
- kfree(mlxsw_hwmon);
- return err;
- }
- void mlxsw_hwmon_fini(struct mlxsw_hwmon *mlxsw_hwmon)
- {
- hwmon_device_unregister(mlxsw_hwmon->hwmon_dev);
- kfree(mlxsw_hwmon);
- }
|