1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * SolidRun DPU driver for control plane
4 *
5 * Copyright (C) 2022-2023 SolidRun
6 *
7 * Author: Alvaro Karsz <alvaro.karsz@solid-run.com>
8 *
9 */
10#include <linux/hwmon.h>
11
12#include "snet_vdpa.h"
13
14/* Monitor offsets */
15#define SNET_MON_TMP0_IN_OFF 0x00
16#define SNET_MON_TMP0_MAX_OFF 0x08
17#define SNET_MON_TMP0_CRIT_OFF 0x10
18#define SNET_MON_TMP1_IN_OFF 0x18
19#define SNET_MON_TMP1_CRIT_OFF 0x20
20#define SNET_MON_CURR_IN_OFF 0x28
21#define SNET_MON_CURR_MAX_OFF 0x30
22#define SNET_MON_CURR_CRIT_OFF 0x38
23#define SNET_MON_PWR_IN_OFF 0x40
24#define SNET_MON_VOLT_IN_OFF 0x48
25#define SNET_MON_VOLT_CRIT_OFF 0x50
26#define SNET_MON_VOLT_LCRIT_OFF 0x58
27
28static void snet_hwmon_read_reg(struct psnet *psnet, u32 reg, long *out)
29{
30 *out = psnet_read64(psnet, off: psnet->cfg.hwmon_off + reg);
31}
32
33static umode_t snet_howmon_is_visible(const void *data,
34 enum hwmon_sensor_types type,
35 u32 attr, int channel)
36{
37 return 0444;
38}
39
40static int snet_howmon_read(struct device *dev, enum hwmon_sensor_types type,
41 u32 attr, int channel, long *val)
42{
43 struct psnet *psnet = dev_get_drvdata(dev);
44 int ret = 0;
45
46 switch (type) {
47 case hwmon_in:
48 switch (attr) {
49 case hwmon_in_lcrit:
50 snet_hwmon_read_reg(psnet, SNET_MON_VOLT_LCRIT_OFF, out: val);
51 break;
52 case hwmon_in_crit:
53 snet_hwmon_read_reg(psnet, SNET_MON_VOLT_CRIT_OFF, out: val);
54 break;
55 case hwmon_in_input:
56 snet_hwmon_read_reg(psnet, SNET_MON_VOLT_IN_OFF, out: val);
57 break;
58 default:
59 ret = -EOPNOTSUPP;
60 break;
61 }
62 break;
63
64 case hwmon_power:
65 switch (attr) {
66 case hwmon_power_input:
67 snet_hwmon_read_reg(psnet, SNET_MON_PWR_IN_OFF, out: val);
68 break;
69
70 default:
71 ret = -EOPNOTSUPP;
72 break;
73 }
74 break;
75
76 case hwmon_curr:
77 switch (attr) {
78 case hwmon_curr_input:
79 snet_hwmon_read_reg(psnet, SNET_MON_CURR_IN_OFF, out: val);
80 break;
81 case hwmon_curr_max:
82 snet_hwmon_read_reg(psnet, SNET_MON_CURR_MAX_OFF, out: val);
83 break;
84 case hwmon_curr_crit:
85 snet_hwmon_read_reg(psnet, SNET_MON_CURR_CRIT_OFF, out: val);
86 break;
87 default:
88 ret = -EOPNOTSUPP;
89 break;
90 }
91 break;
92
93 case hwmon_temp:
94 switch (attr) {
95 case hwmon_temp_input:
96 if (channel == 0)
97 snet_hwmon_read_reg(psnet, SNET_MON_TMP0_IN_OFF, out: val);
98 else
99 snet_hwmon_read_reg(psnet, SNET_MON_TMP1_IN_OFF, out: val);
100 break;
101 case hwmon_temp_max:
102 if (channel == 0)
103 snet_hwmon_read_reg(psnet, SNET_MON_TMP0_MAX_OFF, out: val);
104 else
105 ret = -EOPNOTSUPP;
106 break;
107 case hwmon_temp_crit:
108 if (channel == 0)
109 snet_hwmon_read_reg(psnet, SNET_MON_TMP0_CRIT_OFF, out: val);
110 else
111 snet_hwmon_read_reg(psnet, SNET_MON_TMP1_CRIT_OFF, out: val);
112 break;
113
114 default:
115 ret = -EOPNOTSUPP;
116 break;
117 }
118 break;
119
120 default:
121 ret = -EOPNOTSUPP;
122 break;
123 }
124 return ret;
125}
126
127static int snet_hwmon_read_string(struct device *dev,
128 enum hwmon_sensor_types type, u32 attr,
129 int channel, const char **str)
130{
131 int ret = 0;
132
133 switch (type) {
134 case hwmon_in:
135 *str = "main_vin";
136 break;
137 case hwmon_power:
138 *str = "soc_pin";
139 break;
140 case hwmon_curr:
141 *str = "soc_iin";
142 break;
143 case hwmon_temp:
144 if (channel == 0)
145 *str = "power_stage_temp";
146 else
147 *str = "ic_junction_temp";
148 break;
149 default:
150 ret = -EOPNOTSUPP;
151 break;
152 }
153 return ret;
154}
155
156static const struct hwmon_ops snet_hwmon_ops = {
157 .is_visible = snet_howmon_is_visible,
158 .read = snet_howmon_read,
159 .read_string = snet_hwmon_read_string
160};
161
162static const struct hwmon_channel_info * const snet_hwmon_info[] = {
163 HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_LABEL,
164 HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_LABEL),
165 HWMON_CHANNEL_INFO(power, HWMON_P_INPUT | HWMON_P_LABEL),
166 HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_CRIT | HWMON_C_LABEL),
167 HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_CRIT | HWMON_I_LCRIT | HWMON_I_LABEL),
168 NULL
169};
170
171static const struct hwmon_chip_info snet_hwmono_info = {
172 .ops = &snet_hwmon_ops,
173 .info = snet_hwmon_info,
174};
175
176/* Create an HW monitor device */
177void psnet_create_hwmon(struct pci_dev *pdev)
178{
179 struct device *hwmon;
180 struct psnet *psnet = pci_get_drvdata(pdev);
181
182 snprintf(buf: psnet->hwmon_name, SNET_NAME_SIZE, fmt: "snet_%s", pci_name(pdev));
183 hwmon = devm_hwmon_device_register_with_info(dev: &pdev->dev, name: psnet->hwmon_name, drvdata: psnet,
184 info: &snet_hwmono_info, NULL);
185 /* The monitor is not mandatory, Just alert user in case of an error */
186 if (IS_ERR(ptr: hwmon))
187 SNET_WARN(pdev, "Failed to create SNET hwmon, error %ld\n", PTR_ERR(hwmon));
188}
189

source code of linux/drivers/vdpa/solidrun/snet_hwmon.c