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 | |
28 | static 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 | |
33 | static 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 | |
40 | static 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 | |
127 | static 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 | |
156 | static 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 | |
162 | static 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 | |
171 | static 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 */ |
177 | void 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 | |