linux/drivers/net/ethernet/mellanox/mlxsw/core_thermal.c
<<
>>
Prefs
   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}
 443