| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * SolidRun DPU driver for control plane |
| * |
| * Copyright (C) 2022-2023 SolidRun |
| * |
| * Author: Alvaro Karsz <alvaro.karsz@solid-run.com> |
| * |
| */ |
| #include <linux/hwmon.h> |
| |
| #include "snet_vdpa.h" |
| |
| /* Monitor offsets */ |
| #define SNET_MON_TMP0_IN_OFF 0x00 |
| #define SNET_MON_TMP0_MAX_OFF 0x08 |
| #define SNET_MON_TMP0_CRIT_OFF 0x10 |
| #define SNET_MON_TMP1_IN_OFF 0x18 |
| #define SNET_MON_TMP1_CRIT_OFF 0x20 |
| #define SNET_MON_CURR_IN_OFF 0x28 |
| #define SNET_MON_CURR_MAX_OFF 0x30 |
| #define SNET_MON_CURR_CRIT_OFF 0x38 |
| #define SNET_MON_PWR_IN_OFF 0x40 |
| #define SNET_MON_VOLT_IN_OFF 0x48 |
| #define SNET_MON_VOLT_CRIT_OFF 0x50 |
| #define SNET_MON_VOLT_LCRIT_OFF 0x58 |
| |
| static void snet_hwmon_read_reg(struct psnet *psnet, u32 reg, long *out) |
| { |
| *out = psnet_read64(psnet, psnet->cfg.hwmon_off + reg); |
| } |
| |
| static umode_t snet_howmon_is_visible(const void *data, |
| enum hwmon_sensor_types type, |
| u32 attr, int channel) |
| { |
| return 0444; |
| } |
| |
| static int snet_howmon_read(struct device *dev, enum hwmon_sensor_types type, |
| u32 attr, int channel, long *val) |
| { |
| struct psnet *psnet = dev_get_drvdata(dev); |
| int ret = 0; |
| |
| switch (type) { |
| case hwmon_in: |
| switch (attr) { |
| case hwmon_in_lcrit: |
| snet_hwmon_read_reg(psnet, SNET_MON_VOLT_LCRIT_OFF, val); |
| break; |
| case hwmon_in_crit: |
| snet_hwmon_read_reg(psnet, SNET_MON_VOLT_CRIT_OFF, val); |
| break; |
| case hwmon_in_input: |
| snet_hwmon_read_reg(psnet, SNET_MON_VOLT_IN_OFF, val); |
| break; |
| default: |
| ret = -EOPNOTSUPP; |
| break; |
| } |
| break; |
| |
| case hwmon_power: |
| switch (attr) { |
| case hwmon_power_input: |
| snet_hwmon_read_reg(psnet, SNET_MON_PWR_IN_OFF, val); |
| break; |
| |
| default: |
| ret = -EOPNOTSUPP; |
| break; |
| } |
| break; |
| |
| case hwmon_curr: |
| switch (attr) { |
| case hwmon_curr_input: |
| snet_hwmon_read_reg(psnet, SNET_MON_CURR_IN_OFF, val); |
| break; |
| case hwmon_curr_max: |
| snet_hwmon_read_reg(psnet, SNET_MON_CURR_MAX_OFF, val); |
| break; |
| case hwmon_curr_crit: |
| snet_hwmon_read_reg(psnet, SNET_MON_CURR_CRIT_OFF, val); |
| break; |
| default: |
| ret = -EOPNOTSUPP; |
| break; |
| } |
| break; |
| |
| case hwmon_temp: |
| switch (attr) { |
| case hwmon_temp_input: |
| if (channel == 0) |
| snet_hwmon_read_reg(psnet, SNET_MON_TMP0_IN_OFF, val); |
| else |
| snet_hwmon_read_reg(psnet, SNET_MON_TMP1_IN_OFF, val); |
| break; |
| case hwmon_temp_max: |
| if (channel == 0) |
| snet_hwmon_read_reg(psnet, SNET_MON_TMP0_MAX_OFF, val); |
| else |
| ret = -EOPNOTSUPP; |
| break; |
| case hwmon_temp_crit: |
| if (channel == 0) |
| snet_hwmon_read_reg(psnet, SNET_MON_TMP0_CRIT_OFF, val); |
| else |
| snet_hwmon_read_reg(psnet, SNET_MON_TMP1_CRIT_OFF, val); |
| break; |
| |
| default: |
| ret = -EOPNOTSUPP; |
| break; |
| } |
| break; |
| |
| default: |
| ret = -EOPNOTSUPP; |
| break; |
| } |
| return ret; |
| } |
| |
| static int snet_hwmon_read_string(struct device *dev, |
| enum hwmon_sensor_types type, u32 attr, |
| int channel, const char **str) |
| { |
| int ret = 0; |
| |
| switch (type) { |
| case hwmon_in: |
| *str = "main_vin"; |
| break; |
| case hwmon_power: |
| *str = "soc_pin"; |
| break; |
| case hwmon_curr: |
| *str = "soc_iin"; |
| break; |
| case hwmon_temp: |
| if (channel == 0) |
| *str = "power_stage_temp"; |
| else |
| *str = "ic_junction_temp"; |
| break; |
| default: |
| ret = -EOPNOTSUPP; |
| break; |
| } |
| return ret; |
| } |
| |
| static const struct hwmon_ops snet_hwmon_ops = { |
| .is_visible = snet_howmon_is_visible, |
| .read = snet_howmon_read, |
| .read_string = snet_hwmon_read_string |
| }; |
| |
| static const struct hwmon_channel_info * const snet_hwmon_info[] = { |
| HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_LABEL, |
| HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_LABEL), |
| HWMON_CHANNEL_INFO(power, HWMON_P_INPUT | HWMON_P_LABEL), |
| HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_CRIT | HWMON_C_LABEL), |
| HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_CRIT | HWMON_I_LCRIT | HWMON_I_LABEL), |
| NULL |
| }; |
| |
| static const struct hwmon_chip_info snet_hwmono_info = { |
| .ops = &snet_hwmon_ops, |
| .info = snet_hwmon_info, |
| }; |
| |
| /* Create an HW monitor device */ |
| void psnet_create_hwmon(struct pci_dev *pdev) |
| { |
| struct device *hwmon; |
| struct psnet *psnet = pci_get_drvdata(pdev); |
| |
| snprintf(psnet->hwmon_name, SNET_NAME_SIZE, "snet_%s", pci_name(pdev)); |
| hwmon = devm_hwmon_device_register_with_info(&pdev->dev, psnet->hwmon_name, psnet, |
| &snet_hwmono_info, NULL); |
| /* The monitor is not mandatory, Just alert user in case of an error */ |
| if (IS_ERR(hwmon)) |
| SNET_WARN(pdev, "Failed to create SNET hwmon, error %ld\n", PTR_ERR(hwmon)); |
| } |