1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /* Copyright (c) 2015-2018 Mellanox Technologies. All rights reserved */ |
3 | |
4 | #include <linux/kernel.h> |
5 | #include <linux/types.h> |
6 | #include <linux/device.h> |
7 | #include <linux/sysfs.h> |
8 | #include <linux/hwmon.h> |
9 | #include <linux/err.h> |
10 | #include <linux/sfp.h> |
11 | |
12 | #include "core.h" |
13 | #include "core_env.h" |
14 | |
15 | #define MLXSW_HWMON_SENSORS_MAX_COUNT 64 |
16 | #define MLXSW_HWMON_MODULES_MAX_COUNT 64 |
17 | #define MLXSW_HWMON_GEARBOXES_MAX_COUNT 32 |
18 | |
19 | #define MLXSW_HWMON_ATTR_PER_SENSOR 3 |
20 | #define MLXSW_HWMON_ATTR_PER_MODULE 7 |
21 | #define MLXSW_HWMON_ATTR_PER_GEARBOX 4 |
22 | #define MLXSW_HWMON_DEV_NAME_LEN_MAX 16 |
23 | |
24 | #define MLXSW_HWMON_ATTR_COUNT (MLXSW_HWMON_SENSORS_MAX_COUNT * MLXSW_HWMON_ATTR_PER_SENSOR + \ |
25 | MLXSW_HWMON_MODULES_MAX_COUNT * MLXSW_HWMON_ATTR_PER_MODULE + \ |
26 | MLXSW_HWMON_GEARBOXES_MAX_COUNT * MLXSW_HWMON_ATTR_PER_GEARBOX + \ |
27 | MLXSW_MFCR_TACHOS_MAX + MLXSW_MFCR_PWMS_MAX) |
28 | |
29 | struct mlxsw_hwmon_attr { |
30 | struct device_attribute dev_attr; |
31 | struct mlxsw_hwmon_dev *mlxsw_hwmon_dev; |
32 | unsigned int type_index; |
33 | char name[32]; |
34 | }; |
35 | |
36 | static int mlxsw_hwmon_get_attr_index(int index, int count) |
37 | { |
38 | if (index >= count) |
39 | return index % count + MLXSW_REG_MTMP_GBOX_INDEX_MIN; |
40 | |
41 | return index; |
42 | } |
43 | |
44 | struct mlxsw_hwmon_dev { |
45 | char name[MLXSW_HWMON_DEV_NAME_LEN_MAX]; |
46 | struct mlxsw_hwmon *hwmon; |
47 | struct device *hwmon_dev; |
48 | struct attribute_group group; |
49 | const struct attribute_group *groups[2]; |
50 | struct attribute *attrs[MLXSW_HWMON_ATTR_COUNT + 1]; |
51 | struct mlxsw_hwmon_attr hwmon_attrs[MLXSW_HWMON_ATTR_COUNT]; |
52 | unsigned int attrs_count; |
53 | u8 sensor_count; |
54 | u8 module_sensor_max; |
55 | u8 slot_index; |
56 | bool active; |
57 | }; |
58 | |
59 | struct mlxsw_hwmon { |
60 | struct mlxsw_core *core; |
61 | const struct mlxsw_bus_info *bus_info; |
62 | struct mlxsw_hwmon_dev line_cards[]; |
63 | }; |
64 | |
65 | static ssize_t mlxsw_hwmon_temp_show(struct device *dev, |
66 | struct device_attribute *attr, |
67 | char *buf) |
68 | { |
69 | struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = |
70 | container_of(attr, struct mlxsw_hwmon_attr, dev_attr); |
71 | struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev; |
72 | struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon; |
73 | char mtmp_pl[MLXSW_REG_MTMP_LEN]; |
74 | int temp, index; |
75 | int err; |
76 | |
77 | index = mlxsw_hwmon_get_attr_index(index: mlxsw_hwmon_attr->type_index, |
78 | count: mlxsw_hwmon_dev->module_sensor_max); |
79 | mlxsw_reg_mtmp_pack(payload: mtmp_pl, slot_index: mlxsw_hwmon_dev->slot_index, sensor_index: index, max_temp_enable: false, |
80 | max_temp_reset: false); |
81 | err = mlxsw_reg_query(mlxsw_core: mlxsw_hwmon->core, MLXSW_REG(mtmp), payload: mtmp_pl); |
82 | if (err) { |
83 | dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query temp sensor\n" ); |
84 | return err; |
85 | } |
86 | mlxsw_reg_mtmp_unpack(payload: mtmp_pl, p_temp: &temp, NULL, NULL, NULL, NULL); |
87 | return sprintf(buf, fmt: "%d\n" , temp); |
88 | } |
89 | |
90 | static ssize_t mlxsw_hwmon_temp_max_show(struct device *dev, |
91 | struct device_attribute *attr, |
92 | char *buf) |
93 | { |
94 | struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = |
95 | container_of(attr, struct mlxsw_hwmon_attr, dev_attr); |
96 | struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev; |
97 | struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon; |
98 | char mtmp_pl[MLXSW_REG_MTMP_LEN]; |
99 | int temp_max, index; |
100 | int err; |
101 | |
102 | index = mlxsw_hwmon_get_attr_index(index: mlxsw_hwmon_attr->type_index, |
103 | count: mlxsw_hwmon_dev->module_sensor_max); |
104 | mlxsw_reg_mtmp_pack(payload: mtmp_pl, slot_index: mlxsw_hwmon_dev->slot_index, sensor_index: index, max_temp_enable: false, |
105 | max_temp_reset: false); |
106 | err = mlxsw_reg_query(mlxsw_core: mlxsw_hwmon->core, MLXSW_REG(mtmp), payload: mtmp_pl); |
107 | if (err) { |
108 | dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query temp sensor\n" ); |
109 | return err; |
110 | } |
111 | mlxsw_reg_mtmp_unpack(payload: mtmp_pl, NULL, p_max_temp: &temp_max, NULL, NULL, NULL); |
112 | return sprintf(buf, fmt: "%d\n" , temp_max); |
113 | } |
114 | |
115 | static ssize_t mlxsw_hwmon_temp_rst_store(struct device *dev, |
116 | struct device_attribute *attr, |
117 | const char *buf, size_t len) |
118 | { |
119 | struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = |
120 | container_of(attr, struct mlxsw_hwmon_attr, dev_attr); |
121 | struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev; |
122 | struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon; |
123 | char mtmp_pl[MLXSW_REG_MTMP_LEN]; |
124 | unsigned long val; |
125 | int index; |
126 | int err; |
127 | |
128 | err = kstrtoul(s: buf, base: 10, res: &val); |
129 | if (err) |
130 | return err; |
131 | if (val != 1) |
132 | return -EINVAL; |
133 | |
134 | index = mlxsw_hwmon_get_attr_index(index: mlxsw_hwmon_attr->type_index, |
135 | count: mlxsw_hwmon_dev->module_sensor_max); |
136 | |
137 | mlxsw_reg_mtmp_slot_index_set(buf: mtmp_pl, val: mlxsw_hwmon_dev->slot_index); |
138 | mlxsw_reg_mtmp_sensor_index_set(buf: mtmp_pl, val: index); |
139 | err = mlxsw_reg_query(mlxsw_core: mlxsw_hwmon->core, MLXSW_REG(mtmp), payload: mtmp_pl); |
140 | if (err) |
141 | return err; |
142 | mlxsw_reg_mtmp_mte_set(buf: mtmp_pl, val: true); |
143 | mlxsw_reg_mtmp_mtr_set(buf: mtmp_pl, val: true); |
144 | err = mlxsw_reg_write(mlxsw_core: mlxsw_hwmon->core, MLXSW_REG(mtmp), payload: mtmp_pl); |
145 | if (err) { |
146 | dev_err(mlxsw_hwmon->bus_info->dev, "Failed to reset temp sensor history\n" ); |
147 | return err; |
148 | } |
149 | return len; |
150 | } |
151 | |
152 | static ssize_t mlxsw_hwmon_fan_rpm_show(struct device *dev, |
153 | struct device_attribute *attr, |
154 | char *buf) |
155 | { |
156 | struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = |
157 | container_of(attr, struct mlxsw_hwmon_attr, dev_attr); |
158 | struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev; |
159 | struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon; |
160 | char mfsm_pl[MLXSW_REG_MFSM_LEN]; |
161 | int err; |
162 | |
163 | mlxsw_reg_mfsm_pack(payload: mfsm_pl, tacho: mlxsw_hwmon_attr->type_index); |
164 | err = mlxsw_reg_query(mlxsw_core: mlxsw_hwmon->core, MLXSW_REG(mfsm), payload: mfsm_pl); |
165 | if (err) { |
166 | dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query fan\n" ); |
167 | return err; |
168 | } |
169 | return sprintf(buf, fmt: "%u\n" , mlxsw_reg_mfsm_rpm_get(buf: mfsm_pl)); |
170 | } |
171 | |
172 | static ssize_t mlxsw_hwmon_fan_fault_show(struct device *dev, |
173 | struct device_attribute *attr, |
174 | char *buf) |
175 | { |
176 | struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = |
177 | container_of(attr, struct mlxsw_hwmon_attr, dev_attr); |
178 | struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev; |
179 | struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon; |
180 | char fore_pl[MLXSW_REG_FORE_LEN]; |
181 | bool fault; |
182 | int err; |
183 | |
184 | err = mlxsw_reg_query(mlxsw_core: mlxsw_hwmon->core, MLXSW_REG(fore), payload: fore_pl); |
185 | if (err) { |
186 | dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query fan\n" ); |
187 | return err; |
188 | } |
189 | mlxsw_reg_fore_unpack(payload: fore_pl, tacho: mlxsw_hwmon_attr->type_index, fault: &fault); |
190 | |
191 | return sprintf(buf, fmt: "%u\n" , fault); |
192 | } |
193 | |
194 | static ssize_t mlxsw_hwmon_pwm_show(struct device *dev, |
195 | struct device_attribute *attr, |
196 | char *buf) |
197 | { |
198 | struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = |
199 | container_of(attr, struct mlxsw_hwmon_attr, dev_attr); |
200 | struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev; |
201 | struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon; |
202 | char mfsc_pl[MLXSW_REG_MFSC_LEN]; |
203 | int err; |
204 | |
205 | mlxsw_reg_mfsc_pack(payload: mfsc_pl, pwm: mlxsw_hwmon_attr->type_index, pwm_duty_cycle: 0); |
206 | err = mlxsw_reg_query(mlxsw_core: mlxsw_hwmon->core, MLXSW_REG(mfsc), payload: mfsc_pl); |
207 | if (err) { |
208 | dev_err(mlxsw_hwmon->bus_info->dev, "Failed to query PWM\n" ); |
209 | return err; |
210 | } |
211 | return sprintf(buf, fmt: "%u\n" , |
212 | mlxsw_reg_mfsc_pwm_duty_cycle_get(buf: mfsc_pl)); |
213 | } |
214 | |
215 | static ssize_t mlxsw_hwmon_pwm_store(struct device *dev, |
216 | struct device_attribute *attr, |
217 | const char *buf, size_t len) |
218 | { |
219 | struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = |
220 | container_of(attr, struct mlxsw_hwmon_attr, dev_attr); |
221 | struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev; |
222 | struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon; |
223 | char mfsc_pl[MLXSW_REG_MFSC_LEN]; |
224 | unsigned long val; |
225 | int err; |
226 | |
227 | err = kstrtoul(s: buf, base: 10, res: &val); |
228 | if (err) |
229 | return err; |
230 | if (val > 255) |
231 | return -EINVAL; |
232 | |
233 | mlxsw_reg_mfsc_pack(payload: mfsc_pl, pwm: mlxsw_hwmon_attr->type_index, pwm_duty_cycle: val); |
234 | err = mlxsw_reg_write(mlxsw_core: mlxsw_hwmon->core, MLXSW_REG(mfsc), payload: mfsc_pl); |
235 | if (err) { |
236 | dev_err(mlxsw_hwmon->bus_info->dev, "Failed to write PWM\n" ); |
237 | return err; |
238 | } |
239 | return len; |
240 | } |
241 | |
242 | static int mlxsw_hwmon_module_temp_get(struct device *dev, |
243 | struct device_attribute *attr, |
244 | int *p_temp) |
245 | { |
246 | struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = |
247 | container_of(attr, struct mlxsw_hwmon_attr, dev_attr); |
248 | struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev; |
249 | struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon; |
250 | char mtmp_pl[MLXSW_REG_MTMP_LEN]; |
251 | u8 module; |
252 | int err; |
253 | |
254 | module = mlxsw_hwmon_attr->type_index - mlxsw_hwmon_dev->sensor_count; |
255 | mlxsw_reg_mtmp_pack(payload: mtmp_pl, slot_index: mlxsw_hwmon_dev->slot_index, |
256 | MLXSW_REG_MTMP_MODULE_INDEX_MIN + module, max_temp_enable: false, |
257 | max_temp_reset: false); |
258 | err = mlxsw_reg_query(mlxsw_core: mlxsw_hwmon->core, MLXSW_REG(mtmp), payload: mtmp_pl); |
259 | if (err) { |
260 | dev_err(dev, "Failed to query module temperature\n" ); |
261 | return err; |
262 | } |
263 | mlxsw_reg_mtmp_unpack(payload: mtmp_pl, p_temp, NULL, NULL, NULL, NULL); |
264 | |
265 | return 0; |
266 | } |
267 | |
268 | static ssize_t mlxsw_hwmon_module_temp_show(struct device *dev, |
269 | struct device_attribute *attr, |
270 | char *buf) |
271 | { |
272 | int err, temp; |
273 | |
274 | err = mlxsw_hwmon_module_temp_get(dev, attr, p_temp: &temp); |
275 | if (err) |
276 | return err; |
277 | |
278 | return sprintf(buf, fmt: "%d\n" , temp); |
279 | } |
280 | |
281 | static ssize_t mlxsw_hwmon_module_temp_fault_show(struct device *dev, |
282 | struct device_attribute *attr, |
283 | char *buf) |
284 | { |
285 | struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = |
286 | container_of(attr, struct mlxsw_hwmon_attr, dev_attr); |
287 | struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev; |
288 | struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon; |
289 | char mtbr_pl[MLXSW_REG_MTBR_LEN] = {0}; |
290 | u8 module, fault; |
291 | u16 temp; |
292 | int err; |
293 | |
294 | module = mlxsw_hwmon_attr->type_index - mlxsw_hwmon_dev->sensor_count; |
295 | mlxsw_reg_mtbr_pack(payload: mtbr_pl, slot_index: mlxsw_hwmon_dev->slot_index, |
296 | MLXSW_REG_MTBR_BASE_MODULE_INDEX + module); |
297 | err = mlxsw_reg_query(mlxsw_core: mlxsw_hwmon->core, MLXSW_REG(mtbr), payload: mtbr_pl); |
298 | if (err) { |
299 | dev_err(dev, "Failed to query module temperature sensor\n" ); |
300 | return err; |
301 | } |
302 | |
303 | mlxsw_reg_mtbr_temp_unpack(payload: mtbr_pl, rec_ind: 0, p_temp: &temp, NULL); |
304 | |
305 | /* Update status and temperature cache. */ |
306 | switch (temp) { |
307 | case MLXSW_REG_MTBR_BAD_SENS_INFO: |
308 | /* Untrusted cable is connected. Reading temperature from its |
309 | * sensor is faulty. |
310 | */ |
311 | fault = 1; |
312 | break; |
313 | case MLXSW_REG_MTBR_NO_CONN: |
314 | case MLXSW_REG_MTBR_NO_TEMP_SENS: |
315 | case MLXSW_REG_MTBR_INDEX_NA: |
316 | default: |
317 | fault = 0; |
318 | break; |
319 | } |
320 | |
321 | return sprintf(buf, fmt: "%u\n" , fault); |
322 | } |
323 | |
324 | static int mlxsw_hwmon_module_temp_critical_get(struct device *dev, |
325 | struct device_attribute *attr, |
326 | int *p_temp) |
327 | { |
328 | struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = |
329 | container_of(attr, struct mlxsw_hwmon_attr, dev_attr); |
330 | struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev; |
331 | struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon; |
332 | u8 module; |
333 | int err; |
334 | |
335 | module = mlxsw_hwmon_attr->type_index - mlxsw_hwmon_dev->sensor_count; |
336 | err = mlxsw_env_module_temp_thresholds_get(core: mlxsw_hwmon->core, |
337 | slot_index: mlxsw_hwmon_dev->slot_index, |
338 | module, off: SFP_TEMP_HIGH_WARN, |
339 | temp: p_temp); |
340 | if (err) { |
341 | dev_err(dev, "Failed to query module temperature thresholds\n" ); |
342 | return err; |
343 | } |
344 | |
345 | return 0; |
346 | } |
347 | |
348 | static ssize_t |
349 | mlxsw_hwmon_module_temp_critical_show(struct device *dev, |
350 | struct device_attribute *attr, char *buf) |
351 | { |
352 | int err, temp; |
353 | |
354 | err = mlxsw_hwmon_module_temp_critical_get(dev, attr, p_temp: &temp); |
355 | if (err) |
356 | return err; |
357 | |
358 | return sprintf(buf, fmt: "%u\n" , temp); |
359 | } |
360 | |
361 | static int mlxsw_hwmon_module_temp_emergency_get(struct device *dev, |
362 | struct device_attribute *attr, |
363 | int *p_temp) |
364 | { |
365 | struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = |
366 | container_of(attr, struct mlxsw_hwmon_attr, dev_attr); |
367 | struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev; |
368 | struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon; |
369 | u8 module; |
370 | int err; |
371 | |
372 | module = mlxsw_hwmon_attr->type_index - mlxsw_hwmon_dev->sensor_count; |
373 | err = mlxsw_env_module_temp_thresholds_get(core: mlxsw_hwmon->core, |
374 | slot_index: mlxsw_hwmon_dev->slot_index, |
375 | module, off: SFP_TEMP_HIGH_ALARM, |
376 | temp: p_temp); |
377 | if (err) { |
378 | dev_err(dev, "Failed to query module temperature thresholds\n" ); |
379 | return err; |
380 | } |
381 | |
382 | return 0; |
383 | } |
384 | |
385 | static ssize_t |
386 | mlxsw_hwmon_module_temp_emergency_show(struct device *dev, |
387 | struct device_attribute *attr, |
388 | char *buf) |
389 | { |
390 | int err, temp; |
391 | |
392 | err = mlxsw_hwmon_module_temp_emergency_get(dev, attr, p_temp: &temp); |
393 | if (err) |
394 | return err; |
395 | |
396 | return sprintf(buf, fmt: "%u\n" , temp); |
397 | } |
398 | |
399 | static ssize_t |
400 | mlxsw_hwmon_module_temp_label_show(struct device *dev, |
401 | struct device_attribute *attr, |
402 | char *buf) |
403 | { |
404 | struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = |
405 | container_of(attr, struct mlxsw_hwmon_attr, dev_attr); |
406 | |
407 | return sprintf(buf, fmt: "front panel %03u\n" , |
408 | mlxsw_hwmon_attr->type_index + 1 - |
409 | mlxsw_hwmon_attr->mlxsw_hwmon_dev->sensor_count); |
410 | } |
411 | |
412 | static ssize_t |
413 | mlxsw_hwmon_gbox_temp_label_show(struct device *dev, |
414 | struct device_attribute *attr, |
415 | char *buf) |
416 | { |
417 | struct mlxsw_hwmon_attr *mlxsw_hwmon_attr = |
418 | container_of(attr, struct mlxsw_hwmon_attr, dev_attr); |
419 | struct mlxsw_hwmon_dev *mlxsw_hwmon_dev = mlxsw_hwmon_attr->mlxsw_hwmon_dev; |
420 | int index = mlxsw_hwmon_attr->type_index - |
421 | mlxsw_hwmon_dev->module_sensor_max + 1; |
422 | |
423 | return sprintf(buf, fmt: "gearbox %03u\n" , index); |
424 | } |
425 | |
426 | static ssize_t mlxsw_hwmon_temp_critical_alarm_show(struct device *dev, |
427 | struct device_attribute *attr, |
428 | char *buf) |
429 | { |
430 | int err, temp, emergency_temp, critic_temp; |
431 | |
432 | err = mlxsw_hwmon_module_temp_get(dev, attr, p_temp: &temp); |
433 | if (err) |
434 | return err; |
435 | |
436 | if (temp <= 0) |
437 | return sprintf(buf, fmt: "%d\n" , false); |
438 | |
439 | err = mlxsw_hwmon_module_temp_emergency_get(dev, attr, p_temp: &emergency_temp); |
440 | if (err) |
441 | return err; |
442 | |
443 | if (temp >= emergency_temp) |
444 | return sprintf(buf, fmt: "%d\n" , false); |
445 | |
446 | err = mlxsw_hwmon_module_temp_critical_get(dev, attr, p_temp: &critic_temp); |
447 | if (err) |
448 | return err; |
449 | |
450 | return sprintf(buf, fmt: "%d\n" , temp >= critic_temp); |
451 | } |
452 | |
453 | static ssize_t mlxsw_hwmon_temp_emergency_alarm_show(struct device *dev, |
454 | struct device_attribute *attr, |
455 | char *buf) |
456 | { |
457 | int err, temp, emergency_temp; |
458 | |
459 | err = mlxsw_hwmon_module_temp_get(dev, attr, p_temp: &temp); |
460 | if (err) |
461 | return err; |
462 | |
463 | if (temp <= 0) |
464 | return sprintf(buf, fmt: "%d\n" , false); |
465 | |
466 | err = mlxsw_hwmon_module_temp_emergency_get(dev, attr, p_temp: &emergency_temp); |
467 | if (err) |
468 | return err; |
469 | |
470 | return sprintf(buf, fmt: "%d\n" , temp >= emergency_temp); |
471 | } |
472 | |
473 | enum mlxsw_hwmon_attr_type { |
474 | MLXSW_HWMON_ATTR_TYPE_TEMP, |
475 | MLXSW_HWMON_ATTR_TYPE_TEMP_MAX, |
476 | MLXSW_HWMON_ATTR_TYPE_TEMP_RST, |
477 | MLXSW_HWMON_ATTR_TYPE_FAN_RPM, |
478 | MLXSW_HWMON_ATTR_TYPE_FAN_FAULT, |
479 | MLXSW_HWMON_ATTR_TYPE_PWM, |
480 | MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE, |
481 | MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_FAULT, |
482 | MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_CRIT, |
483 | MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_EMERG, |
484 | MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_LABEL, |
485 | MLXSW_HWMON_ATTR_TYPE_TEMP_GBOX_LABEL, |
486 | MLXSW_HWMON_ATTR_TYPE_TEMP_CRIT_ALARM, |
487 | MLXSW_HWMON_ATTR_TYPE_TEMP_EMERGENCY_ALARM, |
488 | }; |
489 | |
490 | static void mlxsw_hwmon_attr_add(struct mlxsw_hwmon_dev *mlxsw_hwmon_dev, |
491 | enum mlxsw_hwmon_attr_type attr_type, |
492 | unsigned int type_index, unsigned int num) |
493 | { |
494 | struct mlxsw_hwmon_attr *mlxsw_hwmon_attr; |
495 | unsigned int attr_index; |
496 | |
497 | attr_index = mlxsw_hwmon_dev->attrs_count; |
498 | mlxsw_hwmon_attr = &mlxsw_hwmon_dev->hwmon_attrs[attr_index]; |
499 | |
500 | switch (attr_type) { |
501 | case MLXSW_HWMON_ATTR_TYPE_TEMP: |
502 | mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_temp_show; |
503 | mlxsw_hwmon_attr->dev_attr.attr.mode = 0444; |
504 | snprintf(buf: mlxsw_hwmon_attr->name, size: sizeof(mlxsw_hwmon_attr->name), |
505 | fmt: "temp%u_input" , num + 1); |
506 | break; |
507 | case MLXSW_HWMON_ATTR_TYPE_TEMP_MAX: |
508 | mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_temp_max_show; |
509 | mlxsw_hwmon_attr->dev_attr.attr.mode = 0444; |
510 | snprintf(buf: mlxsw_hwmon_attr->name, size: sizeof(mlxsw_hwmon_attr->name), |
511 | fmt: "temp%u_highest" , num + 1); |
512 | break; |
513 | case MLXSW_HWMON_ATTR_TYPE_TEMP_RST: |
514 | mlxsw_hwmon_attr->dev_attr.store = mlxsw_hwmon_temp_rst_store; |
515 | mlxsw_hwmon_attr->dev_attr.attr.mode = 0200; |
516 | snprintf(buf: mlxsw_hwmon_attr->name, size: sizeof(mlxsw_hwmon_attr->name), |
517 | fmt: "temp%u_reset_history" , num + 1); |
518 | break; |
519 | case MLXSW_HWMON_ATTR_TYPE_FAN_RPM: |
520 | mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_fan_rpm_show; |
521 | mlxsw_hwmon_attr->dev_attr.attr.mode = 0444; |
522 | snprintf(buf: mlxsw_hwmon_attr->name, size: sizeof(mlxsw_hwmon_attr->name), |
523 | fmt: "fan%u_input" , num + 1); |
524 | break; |
525 | case MLXSW_HWMON_ATTR_TYPE_FAN_FAULT: |
526 | mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_fan_fault_show; |
527 | mlxsw_hwmon_attr->dev_attr.attr.mode = 0444; |
528 | snprintf(buf: mlxsw_hwmon_attr->name, size: sizeof(mlxsw_hwmon_attr->name), |
529 | fmt: "fan%u_fault" , num + 1); |
530 | break; |
531 | case MLXSW_HWMON_ATTR_TYPE_PWM: |
532 | mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_pwm_show; |
533 | mlxsw_hwmon_attr->dev_attr.store = mlxsw_hwmon_pwm_store; |
534 | mlxsw_hwmon_attr->dev_attr.attr.mode = 0644; |
535 | snprintf(buf: mlxsw_hwmon_attr->name, size: sizeof(mlxsw_hwmon_attr->name), |
536 | fmt: "pwm%u" , num + 1); |
537 | break; |
538 | case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE: |
539 | mlxsw_hwmon_attr->dev_attr.show = mlxsw_hwmon_module_temp_show; |
540 | mlxsw_hwmon_attr->dev_attr.attr.mode = 0444; |
541 | snprintf(buf: mlxsw_hwmon_attr->name, size: sizeof(mlxsw_hwmon_attr->name), |
542 | fmt: "temp%u_input" , num + 1); |
543 | break; |
544 | case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_FAULT: |
545 | mlxsw_hwmon_attr->dev_attr.show = |
546 | mlxsw_hwmon_module_temp_fault_show; |
547 | mlxsw_hwmon_attr->dev_attr.attr.mode = 0444; |
548 | snprintf(buf: mlxsw_hwmon_attr->name, size: sizeof(mlxsw_hwmon_attr->name), |
549 | fmt: "temp%u_fault" , num + 1); |
550 | break; |
551 | case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_CRIT: |
552 | mlxsw_hwmon_attr->dev_attr.show = |
553 | mlxsw_hwmon_module_temp_critical_show; |
554 | mlxsw_hwmon_attr->dev_attr.attr.mode = 0444; |
555 | snprintf(buf: mlxsw_hwmon_attr->name, size: sizeof(mlxsw_hwmon_attr->name), |
556 | fmt: "temp%u_crit" , num + 1); |
557 | break; |
558 | case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_EMERG: |
559 | mlxsw_hwmon_attr->dev_attr.show = |
560 | mlxsw_hwmon_module_temp_emergency_show; |
561 | mlxsw_hwmon_attr->dev_attr.attr.mode = 0444; |
562 | snprintf(buf: mlxsw_hwmon_attr->name, size: sizeof(mlxsw_hwmon_attr->name), |
563 | fmt: "temp%u_emergency" , num + 1); |
564 | break; |
565 | case MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_LABEL: |
566 | mlxsw_hwmon_attr->dev_attr.show = |
567 | mlxsw_hwmon_module_temp_label_show; |
568 | mlxsw_hwmon_attr->dev_attr.attr.mode = 0444; |
569 | snprintf(buf: mlxsw_hwmon_attr->name, size: sizeof(mlxsw_hwmon_attr->name), |
570 | fmt: "temp%u_label" , num + 1); |
571 | break; |
572 | case MLXSW_HWMON_ATTR_TYPE_TEMP_GBOX_LABEL: |
573 | mlxsw_hwmon_attr->dev_attr.show = |
574 | mlxsw_hwmon_gbox_temp_label_show; |
575 | mlxsw_hwmon_attr->dev_attr.attr.mode = 0444; |
576 | snprintf(buf: mlxsw_hwmon_attr->name, size: sizeof(mlxsw_hwmon_attr->name), |
577 | fmt: "temp%u_label" , num + 1); |
578 | break; |
579 | case MLXSW_HWMON_ATTR_TYPE_TEMP_CRIT_ALARM: |
580 | mlxsw_hwmon_attr->dev_attr.show = |
581 | mlxsw_hwmon_temp_critical_alarm_show; |
582 | mlxsw_hwmon_attr->dev_attr.attr.mode = 0444; |
583 | snprintf(buf: mlxsw_hwmon_attr->name, size: sizeof(mlxsw_hwmon_attr->name), |
584 | fmt: "temp%u_crit_alarm" , num + 1); |
585 | break; |
586 | case MLXSW_HWMON_ATTR_TYPE_TEMP_EMERGENCY_ALARM: |
587 | mlxsw_hwmon_attr->dev_attr.show = |
588 | mlxsw_hwmon_temp_emergency_alarm_show; |
589 | mlxsw_hwmon_attr->dev_attr.attr.mode = 0444; |
590 | snprintf(buf: mlxsw_hwmon_attr->name, size: sizeof(mlxsw_hwmon_attr->name), |
591 | fmt: "temp%u_emergency_alarm" , num + 1); |
592 | break; |
593 | default: |
594 | WARN_ON(1); |
595 | } |
596 | |
597 | mlxsw_hwmon_attr->type_index = type_index; |
598 | mlxsw_hwmon_attr->mlxsw_hwmon_dev = mlxsw_hwmon_dev; |
599 | mlxsw_hwmon_attr->dev_attr.attr.name = mlxsw_hwmon_attr->name; |
600 | sysfs_attr_init(&mlxsw_hwmon_attr->dev_attr.attr); |
601 | |
602 | mlxsw_hwmon_dev->attrs[attr_index] = &mlxsw_hwmon_attr->dev_attr.attr; |
603 | mlxsw_hwmon_dev->attrs_count++; |
604 | } |
605 | |
606 | static int mlxsw_hwmon_temp_init(struct mlxsw_hwmon_dev *mlxsw_hwmon_dev) |
607 | { |
608 | struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon; |
609 | char mtcap_pl[MLXSW_REG_MTCAP_LEN] = {0}; |
610 | int i; |
611 | int err; |
612 | |
613 | err = mlxsw_reg_query(mlxsw_core: mlxsw_hwmon->core, MLXSW_REG(mtcap), payload: mtcap_pl); |
614 | if (err) { |
615 | dev_err(mlxsw_hwmon->bus_info->dev, "Failed to get number of temp sensors\n" ); |
616 | return err; |
617 | } |
618 | mlxsw_hwmon_dev->sensor_count = mlxsw_reg_mtcap_sensor_count_get(buf: mtcap_pl); |
619 | for (i = 0; i < mlxsw_hwmon_dev->sensor_count; i++) { |
620 | char mtmp_pl[MLXSW_REG_MTMP_LEN] = {0}; |
621 | |
622 | mlxsw_reg_mtmp_slot_index_set(buf: mtmp_pl, |
623 | val: mlxsw_hwmon_dev->slot_index); |
624 | mlxsw_reg_mtmp_sensor_index_set(buf: mtmp_pl, val: i); |
625 | err = mlxsw_reg_query(mlxsw_core: mlxsw_hwmon->core, MLXSW_REG(mtmp), |
626 | payload: mtmp_pl); |
627 | if (err) |
628 | return err; |
629 | mlxsw_reg_mtmp_mte_set(buf: mtmp_pl, val: true); |
630 | mlxsw_reg_mtmp_mtr_set(buf: mtmp_pl, val: true); |
631 | err = mlxsw_reg_write(mlxsw_core: mlxsw_hwmon->core, |
632 | MLXSW_REG(mtmp), payload: mtmp_pl); |
633 | if (err) { |
634 | dev_err(mlxsw_hwmon->bus_info->dev, "Failed to setup temp sensor number %d\n" , |
635 | i); |
636 | return err; |
637 | } |
638 | mlxsw_hwmon_attr_add(mlxsw_hwmon_dev, |
639 | attr_type: MLXSW_HWMON_ATTR_TYPE_TEMP, type_index: i, num: i); |
640 | mlxsw_hwmon_attr_add(mlxsw_hwmon_dev, |
641 | attr_type: MLXSW_HWMON_ATTR_TYPE_TEMP_MAX, type_index: i, num: i); |
642 | mlxsw_hwmon_attr_add(mlxsw_hwmon_dev, |
643 | attr_type: MLXSW_HWMON_ATTR_TYPE_TEMP_RST, type_index: i, num: i); |
644 | } |
645 | return 0; |
646 | } |
647 | |
648 | static int mlxsw_hwmon_fans_init(struct mlxsw_hwmon_dev *mlxsw_hwmon_dev) |
649 | { |
650 | struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon; |
651 | char mfcr_pl[MLXSW_REG_MFCR_LEN] = {0}; |
652 | enum mlxsw_reg_mfcr_pwm_frequency freq; |
653 | unsigned int type_index; |
654 | unsigned int num; |
655 | u16 tacho_active; |
656 | u8 pwm_active; |
657 | int err; |
658 | |
659 | err = mlxsw_reg_query(mlxsw_core: mlxsw_hwmon->core, MLXSW_REG(mfcr), payload: mfcr_pl); |
660 | if (err) { |
661 | dev_err(mlxsw_hwmon->bus_info->dev, "Failed to get to probe PWMs and Tachometers\n" ); |
662 | return err; |
663 | } |
664 | mlxsw_reg_mfcr_unpack(payload: mfcr_pl, p_pwm_frequency: &freq, p_tacho_active: &tacho_active, p_pwm_active: &pwm_active); |
665 | num = 0; |
666 | for (type_index = 0; type_index < MLXSW_MFCR_TACHOS_MAX; type_index++) { |
667 | if (tacho_active & BIT(type_index)) { |
668 | mlxsw_hwmon_attr_add(mlxsw_hwmon_dev, |
669 | attr_type: MLXSW_HWMON_ATTR_TYPE_FAN_RPM, |
670 | type_index, num); |
671 | mlxsw_hwmon_attr_add(mlxsw_hwmon_dev, |
672 | attr_type: MLXSW_HWMON_ATTR_TYPE_FAN_FAULT, |
673 | type_index, num: num++); |
674 | } |
675 | } |
676 | num = 0; |
677 | for (type_index = 0; type_index < MLXSW_MFCR_PWMS_MAX; type_index++) { |
678 | if (pwm_active & BIT(type_index)) |
679 | mlxsw_hwmon_attr_add(mlxsw_hwmon_dev, |
680 | attr_type: MLXSW_HWMON_ATTR_TYPE_PWM, |
681 | type_index, num: num++); |
682 | } |
683 | return 0; |
684 | } |
685 | |
686 | static int mlxsw_hwmon_module_init(struct mlxsw_hwmon_dev *mlxsw_hwmon_dev) |
687 | { |
688 | struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon; |
689 | char mgpir_pl[MLXSW_REG_MGPIR_LEN]; |
690 | u8 module_sensor_max; |
691 | int i, err; |
692 | |
693 | mlxsw_reg_mgpir_pack(payload: mgpir_pl, slot_index: mlxsw_hwmon_dev->slot_index); |
694 | err = mlxsw_reg_query(mlxsw_core: mlxsw_hwmon->core, MLXSW_REG(mgpir), payload: mgpir_pl); |
695 | if (err) |
696 | return err; |
697 | |
698 | mlxsw_reg_mgpir_unpack(payload: mgpir_pl, NULL, NULL, NULL, |
699 | num_of_modules: &module_sensor_max, NULL); |
700 | |
701 | /* Add extra attributes for module temperature. Sensor index is |
702 | * assigned to sensor_count value, while all indexed before |
703 | * sensor_count are already utilized by the sensors connected through |
704 | * mtmp register by mlxsw_hwmon_temp_init(). |
705 | */ |
706 | mlxsw_hwmon_dev->module_sensor_max = mlxsw_hwmon_dev->sensor_count + |
707 | module_sensor_max; |
708 | for (i = mlxsw_hwmon_dev->sensor_count; |
709 | i < mlxsw_hwmon_dev->module_sensor_max; i++) { |
710 | mlxsw_hwmon_attr_add(mlxsw_hwmon_dev, |
711 | attr_type: MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE, type_index: i, num: i); |
712 | mlxsw_hwmon_attr_add(mlxsw_hwmon_dev, |
713 | attr_type: MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_FAULT, |
714 | type_index: i, num: i); |
715 | mlxsw_hwmon_attr_add(mlxsw_hwmon_dev, |
716 | attr_type: MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_CRIT, type_index: i, |
717 | num: i); |
718 | mlxsw_hwmon_attr_add(mlxsw_hwmon_dev, |
719 | attr_type: MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_EMERG, |
720 | type_index: i, num: i); |
721 | mlxsw_hwmon_attr_add(mlxsw_hwmon_dev, |
722 | attr_type: MLXSW_HWMON_ATTR_TYPE_TEMP_MODULE_LABEL, |
723 | type_index: i, num: i); |
724 | mlxsw_hwmon_attr_add(mlxsw_hwmon_dev, |
725 | attr_type: MLXSW_HWMON_ATTR_TYPE_TEMP_CRIT_ALARM, |
726 | type_index: i, num: i); |
727 | mlxsw_hwmon_attr_add(mlxsw_hwmon_dev, |
728 | attr_type: MLXSW_HWMON_ATTR_TYPE_TEMP_EMERGENCY_ALARM, |
729 | type_index: i, num: i); |
730 | } |
731 | |
732 | return 0; |
733 | } |
734 | |
735 | static int mlxsw_hwmon_gearbox_init(struct mlxsw_hwmon_dev *mlxsw_hwmon_dev) |
736 | { |
737 | struct mlxsw_hwmon *mlxsw_hwmon = mlxsw_hwmon_dev->hwmon; |
738 | enum mlxsw_reg_mgpir_device_type device_type; |
739 | int index, max_index, sensor_index; |
740 | char mgpir_pl[MLXSW_REG_MGPIR_LEN]; |
741 | char mtmp_pl[MLXSW_REG_MTMP_LEN]; |
742 | u8 gbox_num; |
743 | int err; |
744 | |
745 | mlxsw_reg_mgpir_pack(payload: mgpir_pl, slot_index: mlxsw_hwmon_dev->slot_index); |
746 | err = mlxsw_reg_query(mlxsw_core: mlxsw_hwmon->core, MLXSW_REG(mgpir), payload: mgpir_pl); |
747 | if (err) |
748 | return err; |
749 | |
750 | mlxsw_reg_mgpir_unpack(payload: mgpir_pl, num_of_devices: &gbox_num, device_type: &device_type, NULL, NULL, |
751 | NULL); |
752 | if (device_type != MLXSW_REG_MGPIR_DEVICE_TYPE_GEARBOX_DIE || |
753 | !gbox_num) |
754 | return 0; |
755 | |
756 | index = mlxsw_hwmon_dev->module_sensor_max; |
757 | max_index = mlxsw_hwmon_dev->module_sensor_max + gbox_num; |
758 | while (index < max_index) { |
759 | sensor_index = index % mlxsw_hwmon_dev->module_sensor_max + |
760 | MLXSW_REG_MTMP_GBOX_INDEX_MIN; |
761 | mlxsw_reg_mtmp_pack(payload: mtmp_pl, slot_index: mlxsw_hwmon_dev->slot_index, |
762 | sensor_index, max_temp_enable: true, max_temp_reset: true); |
763 | err = mlxsw_reg_write(mlxsw_core: mlxsw_hwmon->core, |
764 | MLXSW_REG(mtmp), payload: mtmp_pl); |
765 | if (err) { |
766 | dev_err(mlxsw_hwmon->bus_info->dev, "Failed to setup temp sensor number %d\n" , |
767 | sensor_index); |
768 | return err; |
769 | } |
770 | mlxsw_hwmon_attr_add(mlxsw_hwmon_dev, |
771 | attr_type: MLXSW_HWMON_ATTR_TYPE_TEMP, type_index: index, num: index); |
772 | mlxsw_hwmon_attr_add(mlxsw_hwmon_dev, |
773 | attr_type: MLXSW_HWMON_ATTR_TYPE_TEMP_MAX, type_index: index, |
774 | num: index); |
775 | mlxsw_hwmon_attr_add(mlxsw_hwmon_dev, |
776 | attr_type: MLXSW_HWMON_ATTR_TYPE_TEMP_RST, type_index: index, |
777 | num: index); |
778 | mlxsw_hwmon_attr_add(mlxsw_hwmon_dev, |
779 | attr_type: MLXSW_HWMON_ATTR_TYPE_TEMP_GBOX_LABEL, |
780 | type_index: index, num: index); |
781 | index++; |
782 | } |
783 | |
784 | return 0; |
785 | } |
786 | |
787 | static void |
788 | mlxsw_hwmon_got_active(struct mlxsw_core *mlxsw_core, u8 slot_index, |
789 | void *priv) |
790 | { |
791 | struct mlxsw_hwmon *hwmon = priv; |
792 | struct mlxsw_hwmon_dev *linecard; |
793 | struct device *dev; |
794 | int err; |
795 | |
796 | dev = hwmon->bus_info->dev; |
797 | linecard = &hwmon->line_cards[slot_index]; |
798 | if (linecard->active) |
799 | return; |
800 | /* For the main board, module sensor indexes start from 1, sensor index |
801 | * 0 is used for the ASIC. Use the same numbering for line cards. |
802 | */ |
803 | linecard->sensor_count = 1; |
804 | linecard->slot_index = slot_index; |
805 | linecard->hwmon = hwmon; |
806 | err = mlxsw_hwmon_module_init(mlxsw_hwmon_dev: linecard); |
807 | if (err) { |
808 | dev_err(dev, "Failed to configure hwmon objects for line card modules in slot %d\n" , |
809 | slot_index); |
810 | return; |
811 | } |
812 | |
813 | err = mlxsw_hwmon_gearbox_init(mlxsw_hwmon_dev: linecard); |
814 | if (err) { |
815 | dev_err(dev, "Failed to configure hwmon objects for line card gearboxes in slot %d\n" , |
816 | slot_index); |
817 | return; |
818 | } |
819 | |
820 | linecard->groups[0] = &linecard->group; |
821 | linecard->group.attrs = linecard->attrs; |
822 | sprintf(buf: linecard->name, fmt: "%s#%02u" , "linecard" , slot_index); |
823 | linecard->hwmon_dev = |
824 | hwmon_device_register_with_groups(dev, name: linecard->name, |
825 | drvdata: linecard, groups: linecard->groups); |
826 | if (IS_ERR(ptr: linecard->hwmon_dev)) { |
827 | dev_err(dev, "Failed to register hwmon objects for line card in slot %d\n" , |
828 | slot_index); |
829 | return; |
830 | } |
831 | |
832 | linecard->active = true; |
833 | } |
834 | |
835 | static void |
836 | mlxsw_hwmon_got_inactive(struct mlxsw_core *mlxsw_core, u8 slot_index, |
837 | void *priv) |
838 | { |
839 | struct mlxsw_hwmon *hwmon = priv; |
840 | struct mlxsw_hwmon_dev *linecard; |
841 | |
842 | linecard = &hwmon->line_cards[slot_index]; |
843 | if (!linecard->active) |
844 | return; |
845 | linecard->active = false; |
846 | hwmon_device_unregister(dev: linecard->hwmon_dev); |
847 | /* Reset attributes counter */ |
848 | linecard->attrs_count = 0; |
849 | } |
850 | |
851 | static struct mlxsw_linecards_event_ops mlxsw_hwmon_event_ops = { |
852 | .got_active = mlxsw_hwmon_got_active, |
853 | .got_inactive = mlxsw_hwmon_got_inactive, |
854 | }; |
855 | |
856 | int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core, |
857 | const struct mlxsw_bus_info *mlxsw_bus_info, |
858 | struct mlxsw_hwmon **p_hwmon) |
859 | { |
860 | char mgpir_pl[MLXSW_REG_MGPIR_LEN]; |
861 | struct mlxsw_hwmon *mlxsw_hwmon; |
862 | struct device *hwmon_dev; |
863 | u8 num_of_slots; |
864 | int err; |
865 | |
866 | mlxsw_reg_mgpir_pack(payload: mgpir_pl, slot_index: 0); |
867 | err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), payload: mgpir_pl); |
868 | if (err) |
869 | return err; |
870 | |
871 | mlxsw_reg_mgpir_unpack(payload: mgpir_pl, NULL, NULL, NULL, NULL, |
872 | num_of_slots: &num_of_slots); |
873 | |
874 | mlxsw_hwmon = kzalloc(struct_size(mlxsw_hwmon, line_cards, |
875 | num_of_slots + 1), GFP_KERNEL); |
876 | if (!mlxsw_hwmon) |
877 | return -ENOMEM; |
878 | |
879 | mlxsw_hwmon->core = mlxsw_core; |
880 | mlxsw_hwmon->bus_info = mlxsw_bus_info; |
881 | mlxsw_hwmon->line_cards[0].hwmon = mlxsw_hwmon; |
882 | mlxsw_hwmon->line_cards[0].slot_index = 0; |
883 | |
884 | err = mlxsw_hwmon_temp_init(mlxsw_hwmon_dev: &mlxsw_hwmon->line_cards[0]); |
885 | if (err) |
886 | goto err_temp_init; |
887 | |
888 | err = mlxsw_hwmon_fans_init(mlxsw_hwmon_dev: &mlxsw_hwmon->line_cards[0]); |
889 | if (err) |
890 | goto err_fans_init; |
891 | |
892 | err = mlxsw_hwmon_module_init(mlxsw_hwmon_dev: &mlxsw_hwmon->line_cards[0]); |
893 | if (err) |
894 | goto err_temp_module_init; |
895 | |
896 | err = mlxsw_hwmon_gearbox_init(mlxsw_hwmon_dev: &mlxsw_hwmon->line_cards[0]); |
897 | if (err) |
898 | goto err_temp_gearbox_init; |
899 | |
900 | mlxsw_hwmon->line_cards[0].groups[0] = &mlxsw_hwmon->line_cards[0].group; |
901 | mlxsw_hwmon->line_cards[0].group.attrs = mlxsw_hwmon->line_cards[0].attrs; |
902 | |
903 | hwmon_dev = hwmon_device_register_with_groups(dev: mlxsw_bus_info->dev, |
904 | name: "mlxsw" , |
905 | drvdata: &mlxsw_hwmon->line_cards[0], |
906 | groups: mlxsw_hwmon->line_cards[0].groups); |
907 | if (IS_ERR(ptr: hwmon_dev)) { |
908 | err = PTR_ERR(ptr: hwmon_dev); |
909 | goto err_hwmon_register; |
910 | } |
911 | |
912 | err = mlxsw_linecards_event_ops_register(mlxsw_core: mlxsw_hwmon->core, |
913 | ops: &mlxsw_hwmon_event_ops, |
914 | priv: mlxsw_hwmon); |
915 | if (err) |
916 | goto err_linecards_event_ops_register; |
917 | |
918 | mlxsw_hwmon->line_cards[0].hwmon_dev = hwmon_dev; |
919 | mlxsw_hwmon->line_cards[0].active = true; |
920 | *p_hwmon = mlxsw_hwmon; |
921 | return 0; |
922 | |
923 | err_linecards_event_ops_register: |
924 | hwmon_device_unregister(dev: mlxsw_hwmon->line_cards[0].hwmon_dev); |
925 | err_hwmon_register: |
926 | err_temp_gearbox_init: |
927 | err_temp_module_init: |
928 | err_fans_init: |
929 | err_temp_init: |
930 | kfree(objp: mlxsw_hwmon); |
931 | return err; |
932 | } |
933 | |
934 | void mlxsw_hwmon_fini(struct mlxsw_hwmon *mlxsw_hwmon) |
935 | { |
936 | mlxsw_hwmon->line_cards[0].active = false; |
937 | mlxsw_linecards_event_ops_unregister(mlxsw_core: mlxsw_hwmon->core, |
938 | ops: &mlxsw_hwmon_event_ops, priv: mlxsw_hwmon); |
939 | hwmon_device_unregister(dev: mlxsw_hwmon->line_cards[0].hwmon_dev); |
940 | kfree(objp: mlxsw_hwmon); |
941 | } |
942 | |