aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2016-11-22 10:04:19 -0500
committerDavid S. Miller <davem@davemloft.net>2016-11-22 10:04:19 -0500
commit41f698b0f5fdaeff7dd76c3b218cf4da3c8ef70b (patch)
tree31ed2e98fb076db71e812bd915a731fa7643a39f
parent2903372695eb33568bc13a02b3e91da1fe6fe855 (diff)
parenta50c1e35650b929500bd89be61c89d95a267ce56 (diff)
Merge branch 'mlxsw-thermal-zone'
Jiri Pirko says: ==================== mlxsw: core: Implement thermal zone Implement thermal zone for mlxsw based HW. The first patch is just a register dependency for the second patch. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/Kconfig9
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/Makefile1
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core.c8
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core.h24
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core_thermal.c442
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/reg.h49
6 files changed, 533 insertions, 0 deletions
diff --git a/drivers/net/ethernet/mellanox/mlxsw/Kconfig b/drivers/net/ethernet/mellanox/mlxsw/Kconfig
index c9822e653b93..95ae4c0d3a18 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/Kconfig
+++ b/drivers/net/ethernet/mellanox/mlxsw/Kconfig
@@ -19,6 +19,15 @@ config MLXSW_CORE_HWMON
19 ---help--- 19 ---help---
20 Say Y here if you want to expose HWMON interface on mlxsw devices. 20 Say Y here if you want to expose HWMON interface on mlxsw devices.
21 21
22config MLXSW_CORE_THERMAL
23 bool "Thermal zone support for Mellanox Technologies Switch ASICs"
24 depends on MLXSW_CORE && THERMAL
25 depends on !(MLXSW_CORE=y && THERMAL=m)
26 default y
27 ---help---
28 Say Y here if you want to automatically control fans speed according
29 ambient temperature reported by ASIC.
30
22config MLXSW_PCI 31config MLXSW_PCI
23 tristate "PCI bus implementation for Mellanox Technologies Switch ASICs" 32 tristate "PCI bus implementation for Mellanox Technologies Switch ASICs"
24 depends on PCI && HAS_DMA && HAS_IOMEM && MLXSW_CORE 33 depends on PCI && HAS_DMA && HAS_IOMEM && MLXSW_CORE
diff --git a/drivers/net/ethernet/mellanox/mlxsw/Makefile b/drivers/net/ethernet/mellanox/mlxsw/Makefile
index 272294244ab1..fe8dadba15ab 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/Makefile
+++ b/drivers/net/ethernet/mellanox/mlxsw/Makefile
@@ -1,6 +1,7 @@
1obj-$(CONFIG_MLXSW_CORE) += mlxsw_core.o 1obj-$(CONFIG_MLXSW_CORE) += mlxsw_core.o
2mlxsw_core-objs := core.o 2mlxsw_core-objs := core.o
3mlxsw_core-$(CONFIG_MLXSW_CORE_HWMON) += core_hwmon.o 3mlxsw_core-$(CONFIG_MLXSW_CORE_HWMON) += core_hwmon.o
4mlxsw_core-$(CONFIG_MLXSW_CORE_THERMAL) += core_thermal.o
4obj-$(CONFIG_MLXSW_PCI) += mlxsw_pci.o 5obj-$(CONFIG_MLXSW_PCI) += mlxsw_pci.o
5mlxsw_pci-objs := pci.o 6mlxsw_pci-objs := pci.o
6obj-$(CONFIG_MLXSW_I2C) += mlxsw_i2c.o 7obj-$(CONFIG_MLXSW_I2C) += mlxsw_i2c.o
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c
index 763752f1745d..bcd7251385e3 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/core.c
@@ -131,6 +131,7 @@ struct mlxsw_core {
131 } lag; 131 } lag;
132 struct mlxsw_res res; 132 struct mlxsw_res res;
133 struct mlxsw_hwmon *hwmon; 133 struct mlxsw_hwmon *hwmon;
134 struct mlxsw_thermal *thermal;
134 struct mlxsw_core_port ports[MLXSW_PORT_MAX_PORTS]; 135 struct mlxsw_core_port ports[MLXSW_PORT_MAX_PORTS];
135 unsigned long driver_priv[0]; 136 unsigned long driver_priv[0];
136 /* driver_priv has to be always the last item */ 137 /* driver_priv has to be always the last item */
@@ -1162,6 +1163,11 @@ int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
1162 if (err) 1163 if (err)
1163 goto err_hwmon_init; 1164 goto err_hwmon_init;
1164 1165
1166 err = mlxsw_thermal_init(mlxsw_core, mlxsw_bus_info,
1167 &mlxsw_core->thermal);
1168 if (err)
1169 goto err_thermal_init;
1170
1165 if (mlxsw_driver->init) { 1171 if (mlxsw_driver->init) {
1166 err = mlxsw_driver->init(mlxsw_core, mlxsw_bus_info); 1172 err = mlxsw_driver->init(mlxsw_core, mlxsw_bus_info);
1167 if (err) 1173 if (err)
@@ -1178,6 +1184,7 @@ err_debugfs_init:
1178 if (mlxsw_core->driver->fini) 1184 if (mlxsw_core->driver->fini)
1179 mlxsw_core->driver->fini(mlxsw_core); 1185 mlxsw_core->driver->fini(mlxsw_core);
1180err_driver_init: 1186err_driver_init:
1187err_thermal_init:
1181err_hwmon_init: 1188err_hwmon_init:
1182 devlink_unregister(devlink); 1189 devlink_unregister(devlink);
1183err_devlink_register: 1190err_devlink_register:
@@ -1204,6 +1211,7 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core)
1204 mlxsw_core_debugfs_fini(mlxsw_core); 1211 mlxsw_core_debugfs_fini(mlxsw_core);
1205 if (mlxsw_core->driver->fini) 1212 if (mlxsw_core->driver->fini)
1206 mlxsw_core->driver->fini(mlxsw_core); 1213 mlxsw_core->driver->fini(mlxsw_core);
1214 mlxsw_thermal_fini(mlxsw_core->thermal);
1207 devlink_unregister(devlink); 1215 devlink_unregister(devlink);
1208 mlxsw_emad_fini(mlxsw_core); 1216 mlxsw_emad_fini(mlxsw_core);
1209 mlxsw_core->bus->fini(mlxsw_core->bus_priv); 1217 mlxsw_core->bus->fini(mlxsw_core->bus_priv);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h
index f7a4d83801eb..3de8955a26fd 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/core.h
@@ -321,4 +321,28 @@ static inline int mlxsw_hwmon_init(struct mlxsw_core *mlxsw_core,
321 321
322#endif 322#endif
323 323
324struct mlxsw_thermal;
325
326#ifdef CONFIG_MLXSW_CORE_THERMAL
327
328int mlxsw_thermal_init(struct mlxsw_core *mlxsw_core,
329 const struct mlxsw_bus_info *mlxsw_bus_info,
330 struct mlxsw_thermal **p_thermal);
331void mlxsw_thermal_fini(struct mlxsw_thermal *thermal);
332
333#else
334
335static inline int mlxsw_thermal_init(struct mlxsw_core *mlxsw_core,
336 const struct mlxsw_bus_info *mlxsw_bus_info,
337 struct mlxsw_thermal **p_thermal)
338{
339 return 0;
340}
341
342static inline void mlxsw_thermal_fini(struct mlxsw_thermal *thermal)
343{
344}
345
346#endif
347
324#endif 348#endif
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c b/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c
new file mode 100644
index 000000000000..d866c98c1a97
--- /dev/null
+++ b/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c
@@ -0,0 +1,442 @@
1/*
2 * drivers/net/ethernet/mellanox/mlxsw/core_thermal.c
3 * Copyright (c) 2016 Ivan Vecera <cera@cera.cz>
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the names of the copyright holders nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
16 *
17 * Alternatively, this software may be distributed under the terms of the
18 * GNU General Public License ("GPL") version 2 as published by the Free
19 * Software Foundation.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include <linux/kernel.h>
35#include <linux/types.h>
36#include <linux/device.h>
37#include <linux/sysfs.h>
38#include <linux/thermal.h>
39#include <linux/err.h>
40
41#include "core.h"
42
43#define MLXSW_THERMAL_POLL_INT 1000 /* ms */
44#define MLXSW_THERMAL_MAX_TEMP 110000 /* 110C */
45#define MLXSW_THERMAL_MAX_STATE 10
46#define MLXSW_THERMAL_MAX_DUTY 255
47
48struct mlxsw_thermal_trip {
49 int type;
50 int temp;
51 int min_state;
52 int max_state;
53};
54
55static const struct mlxsw_thermal_trip default_thermal_trips[] = {
56 { /* In range - 0-40% PWM */
57 .type = THERMAL_TRIP_ACTIVE,
58 .temp = 75000,
59 .min_state = 0,
60 .max_state = (4 * MLXSW_THERMAL_MAX_STATE) / 10,
61 },
62 { /* High - 40-100% PWM */
63 .type = THERMAL_TRIP_ACTIVE,
64 .temp = 80000,
65 .min_state = (4 * MLXSW_THERMAL_MAX_STATE) / 10,
66 .max_state = MLXSW_THERMAL_MAX_STATE,
67 },
68 {
69 /* Very high - 100% PWM */
70 .type = THERMAL_TRIP_ACTIVE,
71 .temp = 85000,
72 .min_state = MLXSW_THERMAL_MAX_STATE,
73 .max_state = MLXSW_THERMAL_MAX_STATE,
74 },
75 { /* Warning */
76 .type = THERMAL_TRIP_HOT,
77 .temp = 105000,
78 .min_state = MLXSW_THERMAL_MAX_STATE,
79 .max_state = MLXSW_THERMAL_MAX_STATE,
80 },
81 { /* Critical - soft poweroff */
82 .type = THERMAL_TRIP_CRITICAL,
83 .temp = MLXSW_THERMAL_MAX_TEMP,
84 .min_state = MLXSW_THERMAL_MAX_STATE,
85 .max_state = MLXSW_THERMAL_MAX_STATE,
86 }
87};
88
89#define MLXSW_THERMAL_NUM_TRIPS ARRAY_SIZE(default_thermal_trips)
90
91/* Make sure all trips are writable */
92#define MLXSW_THERMAL_TRIP_MASK (BIT(MLXSW_THERMAL_NUM_TRIPS) - 1)
93
94struct mlxsw_thermal {
95 struct mlxsw_core *core;
96 const struct mlxsw_bus_info *bus_info;
97 struct thermal_zone_device *tzdev;
98 struct thermal_cooling_device *cdevs[MLXSW_MFCR_PWMS_MAX];
99 struct mlxsw_thermal_trip trips[MLXSW_THERMAL_NUM_TRIPS];
100 enum thermal_device_mode mode;
101};
102
103static inline u8 mlxsw_state_to_duty(int state)
104{
105 return DIV_ROUND_CLOSEST(state * MLXSW_THERMAL_MAX_DUTY,
106 MLXSW_THERMAL_MAX_STATE);
107}
108
109static inline int mlxsw_duty_to_state(u8 duty)
110{
111 return DIV_ROUND_CLOSEST(duty * MLXSW_THERMAL_MAX_STATE,
112 MLXSW_THERMAL_MAX_DUTY);
113}
114
115static int mlxsw_get_cooling_device_idx(struct mlxsw_thermal *thermal,
116 struct thermal_cooling_device *cdev)
117{
118 int i;
119
120 for (i = 0; i < MLXSW_MFCR_PWMS_MAX; i++)
121 if (thermal->cdevs[i] == cdev)
122 return i;
123
124 return -ENODEV;
125}
126
127static int mlxsw_thermal_bind(struct thermal_zone_device *tzdev,
128 struct thermal_cooling_device *cdev)
129{
130 struct mlxsw_thermal *thermal = tzdev->devdata;
131 struct device *dev = thermal->bus_info->dev;
132 int i, err;
133
134 /* If the cooling device is one of ours bind it */
135 if (mlxsw_get_cooling_device_idx(thermal, cdev) < 0)
136 return 0;
137
138 for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++) {
139 const struct mlxsw_thermal_trip *trip = &thermal->trips[i];
140
141 err = thermal_zone_bind_cooling_device(tzdev, i, cdev,
142 trip->max_state,
143 trip->min_state,
144 THERMAL_WEIGHT_DEFAULT);
145 if (err < 0) {
146 dev_err(dev, "Failed to bind cooling device to trip %d\n", i);
147 return err;
148 }
149 }
150 return 0;
151}
152
153static int mlxsw_thermal_unbind(struct thermal_zone_device *tzdev,
154 struct thermal_cooling_device *cdev)
155{
156 struct mlxsw_thermal *thermal = tzdev->devdata;
157 struct device *dev = thermal->bus_info->dev;
158 int i;
159 int err;
160
161 /* If the cooling device is our one unbind it */
162 if (mlxsw_get_cooling_device_idx(thermal, cdev) < 0)
163 return 0;
164
165 for (i = 0; i < MLXSW_THERMAL_NUM_TRIPS; i++) {
166 err = thermal_zone_unbind_cooling_device(tzdev, i, cdev);
167 if (err < 0) {
168 dev_err(dev, "Failed to unbind cooling device\n");
169 return err;
170 }
171 }
172 return 0;
173}
174
175static int mlxsw_thermal_get_mode(struct thermal_zone_device *tzdev,
176 enum thermal_device_mode *mode)
177{
178 struct mlxsw_thermal *thermal = tzdev->devdata;
179
180 *mode = thermal->mode;
181
182 return 0;
183}
184
185static int mlxsw_thermal_set_mode(struct thermal_zone_device *tzdev,
186 enum thermal_device_mode mode)
187{
188 struct mlxsw_thermal *thermal = tzdev->devdata;
189
190 mutex_lock(&tzdev->lock);
191
192 if (mode == THERMAL_DEVICE_ENABLED)
193 tzdev->polling_delay = MLXSW_THERMAL_POLL_INT;
194 else
195 tzdev->polling_delay = 0;
196
197 mutex_unlock(&tzdev->lock);
198
199 thermal->mode = mode;
200 thermal_zone_device_update(tzdev, THERMAL_EVENT_UNSPECIFIED);
201
202 return 0;
203}
204
205static int mlxsw_thermal_get_temp(struct thermal_zone_device *tzdev,
206 int *p_temp)
207{
208 struct mlxsw_thermal *thermal = tzdev->devdata;
209 struct device *dev = thermal->bus_info->dev;
210 char mtmp_pl[MLXSW_REG_MTMP_LEN];
211 unsigned int temp;
212 int err;
213
214 mlxsw_reg_mtmp_pack(mtmp_pl, 0, false, false);
215
216 err = mlxsw_reg_query(thermal->core, MLXSW_REG(mtmp), mtmp_pl);
217 if (err) {
218 dev_err(dev, "Failed to query temp sensor\n");
219 return err;
220 }
221 mlxsw_reg_mtmp_unpack(mtmp_pl, &temp, NULL, NULL);
222
223 *p_temp = (int) temp;
224 return 0;
225}
226
227static int mlxsw_thermal_get_trip_type(struct thermal_zone_device *tzdev,
228 int trip,
229 enum thermal_trip_type *p_type)
230{
231 struct mlxsw_thermal *thermal = tzdev->devdata;
232
233 if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS)
234 return -EINVAL;
235
236 *p_type = thermal->trips[trip].type;
237 return 0;
238}
239
240static int mlxsw_thermal_get_trip_temp(struct thermal_zone_device *tzdev,
241 int trip, int *p_temp)
242{
243 struct mlxsw_thermal *thermal = tzdev->devdata;
244
245 if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS)
246 return -EINVAL;
247
248 *p_temp = thermal->trips[trip].temp;
249 return 0;
250}
251
252static int mlxsw_thermal_set_trip_temp(struct thermal_zone_device *tzdev,
253 int trip, int temp)
254{
255 struct mlxsw_thermal *thermal = tzdev->devdata;
256
257 if (trip < 0 || trip >= MLXSW_THERMAL_NUM_TRIPS ||
258 temp > MLXSW_THERMAL_MAX_TEMP)
259 return -EINVAL;
260
261 thermal->trips[trip].temp = temp;
262 return 0;
263}
264
265static struct thermal_zone_device_ops mlxsw_thermal_ops = {
266 .bind = mlxsw_thermal_bind,
267 .unbind = mlxsw_thermal_unbind,
268 .get_mode = mlxsw_thermal_get_mode,
269 .set_mode = mlxsw_thermal_set_mode,
270 .get_temp = mlxsw_thermal_get_temp,
271 .get_trip_type = mlxsw_thermal_get_trip_type,
272 .get_trip_temp = mlxsw_thermal_get_trip_temp,
273 .set_trip_temp = mlxsw_thermal_set_trip_temp,
274};
275
276static int mlxsw_thermal_get_max_state(struct thermal_cooling_device *cdev,
277 unsigned long *p_state)
278{
279 *p_state = MLXSW_THERMAL_MAX_STATE;
280 return 0;
281}
282
283static int mlxsw_thermal_get_cur_state(struct thermal_cooling_device *cdev,
284 unsigned long *p_state)
285
286{
287 struct mlxsw_thermal *thermal = cdev->devdata;
288 struct device *dev = thermal->bus_info->dev;
289 char mfsc_pl[MLXSW_REG_MFSC_LEN];
290 int err, idx;
291 u8 duty;
292
293 idx = mlxsw_get_cooling_device_idx(thermal, cdev);
294 if (idx < 0)
295 return idx;
296
297 mlxsw_reg_mfsc_pack(mfsc_pl, idx, 0);
298 err = mlxsw_reg_query(thermal->core, MLXSW_REG(mfsc), mfsc_pl);
299 if (err) {
300 dev_err(dev, "Failed to query PWM duty\n");
301 return err;
302 }
303
304 duty = mlxsw_reg_mfsc_pwm_duty_cycle_get(mfsc_pl);
305 *p_state = mlxsw_duty_to_state(duty);
306 return 0;
307}
308
309static int mlxsw_thermal_set_cur_state(struct thermal_cooling_device *cdev,
310 unsigned long state)
311
312{
313 struct mlxsw_thermal *thermal = cdev->devdata;
314 struct device *dev = thermal->bus_info->dev;
315 char mfsc_pl[MLXSW_REG_MFSC_LEN];
316 int err, idx;
317
318 idx = mlxsw_get_cooling_device_idx(thermal, cdev);
319 if (idx < 0)
320 return idx;
321
322 mlxsw_reg_mfsc_pack(mfsc_pl, idx, mlxsw_state_to_duty(state));
323 err = mlxsw_reg_write(thermal->core, MLXSW_REG(mfsc), mfsc_pl);
324 if (err) {
325 dev_err(dev, "Failed to write PWM duty\n");
326 return err;
327 }
328 return 0;
329}
330
331static const struct thermal_cooling_device_ops mlxsw_cooling_ops = {
332 .get_max_state = mlxsw_thermal_get_max_state,
333 .get_cur_state = mlxsw_thermal_get_cur_state,
334 .set_cur_state = mlxsw_thermal_set_cur_state,
335};
336
337int mlxsw_thermal_init(struct mlxsw_core *core,
338 const struct mlxsw_bus_info *bus_info,
339 struct mlxsw_thermal **p_thermal)
340{
341 char mfcr_pl[MLXSW_REG_MFCR_LEN] = { 0 };
342 enum mlxsw_reg_mfcr_pwm_frequency freq;
343 struct device *dev = bus_info->dev;
344 struct mlxsw_thermal *thermal;
345 u16 tacho_active;
346 u8 pwm_active;
347 int err, i;
348
349 thermal = devm_kzalloc(dev, sizeof(*thermal),
350 GFP_KERNEL);
351 if (!thermal)
352 return -ENOMEM;
353
354 thermal->core = core;
355 thermal->bus_info = bus_info;
356 memcpy(thermal->trips, default_thermal_trips, sizeof(thermal->trips));
357
358 err = mlxsw_reg_query(thermal->core, MLXSW_REG(mfcr), mfcr_pl);
359 if (err) {
360 dev_err(dev, "Failed to probe PWMs\n");
361 goto err_free_thermal;
362 }
363 mlxsw_reg_mfcr_unpack(mfcr_pl, &freq, &tacho_active, &pwm_active);
364
365 for (i = 0; i < MLXSW_MFCR_TACHOS_MAX; i++) {
366 if (tacho_active & BIT(i)) {
367 char mfsl_pl[MLXSW_REG_MFSL_LEN];
368
369 mlxsw_reg_mfsl_pack(mfsl_pl, i, 0, 0);
370
371 /* We need to query the register to preserve maximum */
372 err = mlxsw_reg_query(thermal->core, MLXSW_REG(mfsl),
373 mfsl_pl);
374 if (err)
375 goto err_free_thermal;
376
377 /* set the minimal RPMs to 0 */
378 mlxsw_reg_mfsl_tach_min_set(mfsl_pl, 0);
379 err = mlxsw_reg_write(thermal->core, MLXSW_REG(mfsl),
380 mfsl_pl);
381 if (err)
382 goto err_free_thermal;
383 }
384 }
385 for (i = 0; i < MLXSW_MFCR_PWMS_MAX; i++) {
386 if (pwm_active & BIT(i)) {
387 struct thermal_cooling_device *cdev;
388
389 cdev = thermal_cooling_device_register("Fan", thermal,
390 &mlxsw_cooling_ops);
391 if (IS_ERR(cdev)) {
392 err = PTR_ERR(cdev);
393 dev_err(dev, "Failed to register cooling device\n");
394 goto err_unreg_cdevs;
395 }
396 thermal->cdevs[i] = cdev;
397 }
398 }
399
400 thermal->tzdev = thermal_zone_device_register("mlxsw",
401 MLXSW_THERMAL_NUM_TRIPS,
402 MLXSW_THERMAL_TRIP_MASK,
403 thermal,
404 &mlxsw_thermal_ops,
405 NULL, 0,
406 MLXSW_THERMAL_POLL_INT);
407 if (IS_ERR(thermal->tzdev)) {
408 err = PTR_ERR(thermal->tzdev);
409 dev_err(dev, "Failed to register thermal zone\n");
410 goto err_unreg_cdevs;
411 }
412
413 thermal->mode = THERMAL_DEVICE_ENABLED;
414 *p_thermal = thermal;
415 return 0;
416err_unreg_cdevs:
417 for (i = 0; i < MLXSW_MFCR_PWMS_MAX; i++)
418 if (thermal->cdevs[i])
419 thermal_cooling_device_unregister(thermal->cdevs[i]);
420err_free_thermal:
421 devm_kfree(dev, thermal);
422 return err;
423}
424
425void mlxsw_thermal_fini(struct mlxsw_thermal *thermal)
426{
427 int i;
428
429 if (thermal->tzdev) {
430 thermal_zone_device_unregister(thermal->tzdev);
431 thermal->tzdev = NULL;
432 }
433
434 for (i = 0; i < MLXSW_MFCR_PWMS_MAX; i++) {
435 if (thermal->cdevs[i]) {
436 thermal_cooling_device_unregister(thermal->cdevs[i]);
437 thermal->cdevs[i] = NULL;
438 }
439 }
440
441 devm_kfree(thermal->bus_info->dev, thermal);
442}
diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h
index edad7cb62475..2618e9cf3aab 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/reg.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h
@@ -4518,6 +4518,54 @@ static inline void mlxsw_reg_mfsm_pack(char *payload, u8 tacho)
4518 mlxsw_reg_mfsm_tacho_set(payload, tacho); 4518 mlxsw_reg_mfsm_tacho_set(payload, tacho);
4519} 4519}
4520 4520
4521/* MFSL - Management Fan Speed Limit Register
4522 * ------------------------------------------
4523 * The Fan Speed Limit register is used to configure the fan speed
4524 * event / interrupt notification mechanism. Fan speed threshold are
4525 * defined for both under-speed and over-speed.
4526 */
4527#define MLXSW_REG_MFSL_ID 0x9004
4528#define MLXSW_REG_MFSL_LEN 0x0C
4529
4530MLXSW_REG_DEFINE(mfsl, MLXSW_REG_MFSL_ID, MLXSW_REG_MFSL_LEN);
4531
4532/* reg_mfsl_tacho
4533 * Fan tachometer index.
4534 * Access: Index
4535 */
4536MLXSW_ITEM32(reg, mfsl, tacho, 0x00, 24, 4);
4537
4538/* reg_mfsl_tach_min
4539 * Tachometer minimum value (minimum RPM).
4540 * Access: RW
4541 */
4542MLXSW_ITEM32(reg, mfsl, tach_min, 0x04, 0, 16);
4543
4544/* reg_mfsl_tach_max
4545 * Tachometer maximum value (maximum RPM).
4546 * Access: RW
4547 */
4548MLXSW_ITEM32(reg, mfsl, tach_max, 0x08, 0, 16);
4549
4550static inline void mlxsw_reg_mfsl_pack(char *payload, u8 tacho,
4551 u16 tach_min, u16 tach_max)
4552{
4553 MLXSW_REG_ZERO(mfsl, payload);
4554 mlxsw_reg_mfsl_tacho_set(payload, tacho);
4555 mlxsw_reg_mfsl_tach_min_set(payload, tach_min);
4556 mlxsw_reg_mfsl_tach_max_set(payload, tach_max);
4557}
4558
4559static inline void mlxsw_reg_mfsl_unpack(char *payload, u8 tacho,
4560 u16 *p_tach_min, u16 *p_tach_max)
4561{
4562 if (p_tach_min)
4563 *p_tach_min = mlxsw_reg_mfsl_tach_min_get(payload);
4564
4565 if (p_tach_max)
4566 *p_tach_max = mlxsw_reg_mfsl_tach_max_get(payload);
4567}
4568
4521/* MTCAP - Management Temperature Capabilities 4569/* MTCAP - Management Temperature Capabilities
4522 * ------------------------------------------- 4570 * -------------------------------------------
4523 * This register exposes the capabilities of the device and 4571 * This register exposes the capabilities of the device and
@@ -5228,6 +5276,7 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = {
5228 MLXSW_REG(mfcr), 5276 MLXSW_REG(mfcr),
5229 MLXSW_REG(mfsc), 5277 MLXSW_REG(mfsc),
5230 MLXSW_REG(mfsm), 5278 MLXSW_REG(mfsm),
5279 MLXSW_REG(mfsl),
5231 MLXSW_REG(mtcap), 5280 MLXSW_REG(mtcap),
5232 MLXSW_REG(mtmp), 5281 MLXSW_REG(mtmp),
5233 MLXSW_REG(mpat), 5282 MLXSW_REG(mpat),