linux/drivers/thermal/of-thermal.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 *  of-thermal.c - Generic Thermal Management device tree support.
   4 *
   5 *  Copyright (C) 2013 Texas Instruments
   6 *  Copyright (C) 2013 Eduardo Valentin <eduardo.valentin@ti.com>
   7 */
   8#include <linux/thermal.h>
   9#include <linux/slab.h>
  10#include <linux/types.h>
  11#include <linux/of_device.h>
  12#include <linux/of_platform.h>
  13#include <linux/err.h>
  14#include <linux/export.h>
  15#include <linux/string.h>
  16
  17#include "thermal_core.h"
  18
  19/***   Private data structures to represent thermal device tree data ***/
  20
  21/**
  22 * struct __thermal_bind_param - a match between trip and cooling device
  23 * @cooling_device: a pointer to identify the referred cooling device
  24 * @trip_id: the trip point index
  25 * @usage: the percentage (from 0 to 100) of cooling contribution
  26 * @min: minimum cooling state used at this trip point
  27 * @max: maximum cooling state used at this trip point
  28 */
  29
  30struct __thermal_bind_params {
  31        struct device_node *cooling_device;
  32        unsigned int trip_id;
  33        unsigned int usage;
  34        unsigned long min;
  35        unsigned long max;
  36};
  37
  38/**
  39 * struct __thermal_zone - internal representation of a thermal zone
  40 * @passive_delay: polling interval while passive cooling is activated
  41 * @polling_delay: zone polling interval
  42 * @slope: slope of the temperature adjustment curve
  43 * @offset: offset of the temperature adjustment curve
  44 * @ntrips: number of trip points
  45 * @trips: an array of trip points (0..ntrips - 1)
  46 * @num_tbps: number of thermal bind params
  47 * @tbps: an array of thermal bind params (0..num_tbps - 1)
  48 * @sensor_data: sensor private data used while reading temperature and trend
  49 * @ops: set of callbacks to handle the thermal zone based on DT
  50 */
  51
  52struct __thermal_zone {
  53        int passive_delay;
  54        int polling_delay;
  55        int slope;
  56        int offset;
  57
  58        /* trip data */
  59        int ntrips;
  60        struct thermal_trip *trips;
  61
  62        /* cooling binding data */
  63        int num_tbps;
  64        struct __thermal_bind_params *tbps;
  65
  66        /* sensor interface */
  67        void *sensor_data;
  68        const struct thermal_zone_of_device_ops *ops;
  69};
  70
  71/***   DT thermal zone device callbacks   ***/
  72
  73static int of_thermal_get_temp(struct thermal_zone_device *tz,
  74                               int *temp)
  75{
  76        struct __thermal_zone *data = tz->devdata;
  77
  78        if (!data->ops->get_temp)
  79                return -EINVAL;
  80
  81        return data->ops->get_temp(data->sensor_data, temp);
  82}
  83
  84static int of_thermal_set_trips(struct thermal_zone_device *tz,
  85                                int low, int high)
  86{
  87        struct __thermal_zone *data = tz->devdata;
  88
  89        if (!data->ops || !data->ops->set_trips)
  90                return -EINVAL;
  91
  92        return data->ops->set_trips(data->sensor_data, low, high);
  93}
  94
  95/**
  96 * of_thermal_get_ntrips - function to export number of available trip
  97 *                         points.
  98 * @tz: pointer to a thermal zone
  99 *
 100 * This function is a globally visible wrapper to get number of trip points
 101 * stored in the local struct __thermal_zone
 102 *
 103 * Return: number of available trip points, -ENODEV when data not available
 104 */
 105int of_thermal_get_ntrips(struct thermal_zone_device *tz)
 106{
 107        struct __thermal_zone *data = tz->devdata;
 108
 109        if (!data || IS_ERR(data))
 110                return -ENODEV;
 111
 112        return data->ntrips;
 113}
 114EXPORT_SYMBOL_GPL(of_thermal_get_ntrips);
 115
 116/**
 117 * of_thermal_is_trip_valid - function to check if trip point is valid
 118 *
 119 * @tz: pointer to a thermal zone
 120 * @trip:       trip point to evaluate
 121 *
 122 * This function is responsible for checking if passed trip point is valid
 123 *
 124 * Return: true if trip point is valid, false otherwise
 125 */
 126bool of_thermal_is_trip_valid(struct thermal_zone_device *tz, int trip)
 127{
 128        struct __thermal_zone *data = tz->devdata;
 129
 130        if (!data || trip >= data->ntrips || trip < 0)
 131                return false;
 132
 133        return true;
 134}
 135EXPORT_SYMBOL_GPL(of_thermal_is_trip_valid);
 136
 137/**
 138 * of_thermal_get_trip_points - function to get access to a globally exported
 139 *                              trip points
 140 *
 141 * @tz: pointer to a thermal zone
 142 *
 143 * This function provides a pointer to trip points table
 144 *
 145 * Return: pointer to trip points table, NULL otherwise
 146 */
 147const struct thermal_trip *
 148of_thermal_get_trip_points(struct thermal_zone_device *tz)
 149{
 150        struct __thermal_zone *data = tz->devdata;
 151
 152        if (!data)
 153                return NULL;
 154
 155        return data->trips;
 156}
 157EXPORT_SYMBOL_GPL(of_thermal_get_trip_points);
 158
 159/**
 160 * of_thermal_set_emul_temp - function to set emulated temperature
 161 *
 162 * @tz: pointer to a thermal zone
 163 * @temp:       temperature to set
 164 *
 165 * This function gives the ability to set emulated value of temperature,
 166 * which is handy for debugging
 167 *
 168 * Return: zero on success, error code otherwise
 169 */
 170static int of_thermal_set_emul_temp(struct thermal_zone_device *tz,
 171                                    int temp)
 172{
 173        struct __thermal_zone *data = tz->devdata;
 174
 175        return data->ops->set_emul_temp(data->sensor_data, temp);
 176}
 177
 178static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip,
 179                                enum thermal_trend *trend)
 180{
 181        struct __thermal_zone *data = tz->devdata;
 182
 183        if (!data->ops->get_trend)
 184                return -EINVAL;
 185
 186        return data->ops->get_trend(data->sensor_data, trip, trend);
 187}
 188
 189static int of_thermal_bind(struct thermal_zone_device *thermal,
 190                           struct thermal_cooling_device *cdev)
 191{
 192        struct __thermal_zone *data = thermal->devdata;
 193        int i;
 194
 195        if (!data || IS_ERR(data))
 196                return -ENODEV;
 197
 198        /* find where to bind */
 199        for (i = 0; i < data->num_tbps; i++) {
 200                struct __thermal_bind_params *tbp = data->tbps + i;
 201
 202                if (tbp->cooling_device == cdev->np) {
 203                        int ret;
 204
 205                        ret = thermal_zone_bind_cooling_device(thermal,
 206                                                tbp->trip_id, cdev,
 207                                                tbp->max,
 208                                                tbp->min,
 209                                                tbp->usage);
 210                        if (ret)
 211                                return ret;
 212                }
 213        }
 214
 215        return 0;
 216}
 217
 218static int of_thermal_unbind(struct thermal_zone_device *thermal,
 219                             struct thermal_cooling_device *cdev)
 220{
 221        struct __thermal_zone *data = thermal->devdata;
 222        int i;
 223
 224        if (!data || IS_ERR(data))
 225                return -ENODEV;
 226
 227        /* find where to unbind */
 228        for (i = 0; i < data->num_tbps; i++) {
 229                struct __thermal_bind_params *tbp = data->tbps + i;
 230
 231                if (tbp->cooling_device == cdev->np) {
 232                        int ret;
 233
 234                        ret = thermal_zone_unbind_cooling_device(thermal,
 235                                                tbp->trip_id, cdev);
 236                        if (ret)
 237                                return ret;
 238                }
 239        }
 240
 241        return 0;
 242}
 243
 244static int of_thermal_get_trip_type(struct thermal_zone_device *tz, int trip,
 245                                    enum thermal_trip_type *type)
 246{
 247        struct __thermal_zone *data = tz->devdata;
 248
 249        if (trip >= data->ntrips || trip < 0)
 250                return -EDOM;
 251
 252        *type = data->trips[trip].type;
 253
 254        return 0;
 255}
 256
 257static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip,
 258                                    int *temp)
 259{
 260        struct __thermal_zone *data = tz->devdata;
 261
 262        if (trip >= data->ntrips || trip < 0)
 263                return -EDOM;
 264
 265        *temp = data->trips[trip].temperature;
 266
 267        return 0;
 268}
 269
 270static int of_thermal_set_trip_temp(struct thermal_zone_device *tz, int trip,
 271                                    int temp)
 272{
 273        struct __thermal_zone *data = tz->devdata;
 274
 275        if (trip >= data->ntrips || trip < 0)
 276                return -EDOM;
 277
 278        if (data->ops->set_trip_temp) {
 279                int ret;
 280
 281                ret = data->ops->set_trip_temp(data->sensor_data, trip, temp);
 282                if (ret)
 283                        return ret;
 284        }
 285
 286        /* thermal framework should take care of data->mask & (1 << trip) */
 287        data->trips[trip].temperature = temp;
 288
 289        return 0;
 290}
 291
 292static int of_thermal_get_trip_hyst(struct thermal_zone_device *tz, int trip,
 293                                    int *hyst)
 294{
 295        struct __thermal_zone *data = tz->devdata;
 296
 297        if (trip >= data->ntrips || trip < 0)
 298                return -EDOM;
 299
 300        *hyst = data->trips[trip].hysteresis;
 301
 302        return 0;
 303}
 304
 305static int of_thermal_set_trip_hyst(struct thermal_zone_device *tz, int trip,
 306                                    int hyst)
 307{
 308        struct __thermal_zone *data = tz->devdata;
 309
 310        if (trip >= data->ntrips || trip < 0)
 311                return -EDOM;
 312
 313        /* thermal framework should take care of data->mask & (1 << trip) */
 314        data->trips[trip].hysteresis = hyst;
 315
 316        return 0;
 317}
 318
 319static int of_thermal_get_crit_temp(struct thermal_zone_device *tz,
 320                                    int *temp)
 321{
 322        struct __thermal_zone *data = tz->devdata;
 323        int i;
 324
 325        for (i = 0; i < data->ntrips; i++)
 326                if (data->trips[i].type == THERMAL_TRIP_CRITICAL) {
 327                        *temp = data->trips[i].temperature;
 328                        return 0;
 329                }
 330
 331        return -EINVAL;
 332}
 333
 334static struct thermal_zone_device_ops of_thermal_ops = {
 335        .get_trip_type = of_thermal_get_trip_type,
 336        .get_trip_temp = of_thermal_get_trip_temp,
 337        .set_trip_temp = of_thermal_set_trip_temp,
 338        .get_trip_hyst = of_thermal_get_trip_hyst,
 339        .set_trip_hyst = of_thermal_set_trip_hyst,
 340        .get_crit_temp = of_thermal_get_crit_temp,
 341
 342        .bind = of_thermal_bind,
 343        .unbind = of_thermal_unbind,
 344};
 345
 346/***   sensor API   ***/
 347
 348static struct thermal_zone_device *
 349thermal_zone_of_add_sensor(struct device_node *zone,
 350                           struct device_node *sensor, void *data,
 351                           const struct thermal_zone_of_device_ops *ops)
 352{
 353        struct thermal_zone_device *tzd;
 354        struct __thermal_zone *tz;
 355
 356        tzd = thermal_zone_get_zone_by_name(zone->name);
 357        if (IS_ERR(tzd))
 358                return ERR_PTR(-EPROBE_DEFER);
 359
 360        tz = tzd->devdata;
 361
 362        if (!ops)
 363                return ERR_PTR(-EINVAL);
 364
 365        mutex_lock(&tzd->lock);
 366        tz->ops = ops;
 367        tz->sensor_data = data;
 368
 369        tzd->ops->get_temp = of_thermal_get_temp;
 370        tzd->ops->get_trend = of_thermal_get_trend;
 371
 372        /*
 373         * The thermal zone core will calculate the window if they have set the
 374         * optional set_trips pointer.
 375         */
 376        if (ops->set_trips)
 377                tzd->ops->set_trips = of_thermal_set_trips;
 378
 379        if (ops->set_emul_temp)
 380                tzd->ops->set_emul_temp = of_thermal_set_emul_temp;
 381
 382        mutex_unlock(&tzd->lock);
 383
 384        return tzd;
 385}
 386
 387/**
 388 * thermal_zone_of_sensor_register - registers a sensor to a DT thermal zone
 389 * @dev: a valid struct device pointer of a sensor device. Must contain
 390 *       a valid .of_node, for the sensor node.
 391 * @sensor_id: a sensor identifier, in case the sensor IP has more
 392 *             than one sensors
 393 * @data: a private pointer (owned by the caller) that will be passed
 394 *        back, when a temperature reading is needed.
 395 * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
 396 *
 397 * This function will search the list of thermal zones described in device
 398 * tree and look for the zone that refer to the sensor device pointed by
 399 * @dev->of_node as temperature providers. For the zone pointing to the
 400 * sensor node, the sensor will be added to the DT thermal zone device.
 401 *
 402 * The thermal zone temperature is provided by the @get_temp function
 403 * pointer. When called, it will have the private pointer @data back.
 404 *
 405 * The thermal zone temperature trend is provided by the @get_trend function
 406 * pointer. When called, it will have the private pointer @data back.
 407 *
 408 * TODO:
 409 * 01 - This function must enqueue the new sensor instead of using
 410 * it as the only source of temperature values.
 411 *
 412 * 02 - There must be a way to match the sensor with all thermal zones
 413 * that refer to it.
 414 *
 415 * Return: On success returns a valid struct thermal_zone_device,
 416 * otherwise, it returns a corresponding ERR_PTR(). Caller must
 417 * check the return value with help of IS_ERR() helper.
 418 */
 419struct thermal_zone_device *
 420thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,
 421                                const struct thermal_zone_of_device_ops *ops)
 422{
 423        struct device_node *np, *child, *sensor_np;
 424        struct thermal_zone_device *tzd = ERR_PTR(-ENODEV);
 425
 426        np = of_find_node_by_name(NULL, "thermal-zones");
 427        if (!np)
 428                return ERR_PTR(-ENODEV);
 429
 430        if (!dev || !dev->of_node) {
 431                of_node_put(np);
 432                return ERR_PTR(-EINVAL);
 433        }
 434
 435        sensor_np = of_node_get(dev->of_node);
 436
 437        for_each_available_child_of_node(np, child) {
 438                struct of_phandle_args sensor_specs;
 439                int ret, id;
 440
 441                /* For now, thermal framework supports only 1 sensor per zone */
 442                ret = of_parse_phandle_with_args(child, "thermal-sensors",
 443                                                 "#thermal-sensor-cells",
 444                                                 0, &sensor_specs);
 445                if (ret)
 446                        continue;
 447
 448                if (sensor_specs.args_count >= 1) {
 449                        id = sensor_specs.args[0];
 450                        WARN(sensor_specs.args_count > 1,
 451                             "%s: too many cells in sensor specifier %d\n",
 452                             sensor_specs.np->name, sensor_specs.args_count);
 453                } else {
 454                        id = 0;
 455                }
 456
 457                if (sensor_specs.np == sensor_np && id == sensor_id) {
 458                        tzd = thermal_zone_of_add_sensor(child, sensor_np,
 459                                                         data, ops);
 460                        if (!IS_ERR(tzd))
 461                                thermal_zone_device_enable(tzd);
 462
 463                        of_node_put(sensor_specs.np);
 464                        of_node_put(child);
 465                        goto exit;
 466                }
 467                of_node_put(sensor_specs.np);
 468        }
 469exit:
 470        of_node_put(sensor_np);
 471        of_node_put(np);
 472
 473        return tzd;
 474}
 475EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_register);
 476
 477/**
 478 * thermal_zone_of_sensor_unregister - unregisters a sensor from a DT thermal zone
 479 * @dev: a valid struct device pointer of a sensor device. Must contain
 480 *       a valid .of_node, for the sensor node.
 481 * @tzd: a pointer to struct thermal_zone_device where the sensor is registered.
 482 *
 483 * This function removes the sensor callbacks and private data from the
 484 * thermal zone device registered with thermal_zone_of_sensor_register()
 485 * API. It will also silent the zone by remove the .get_temp() and .get_trend()
 486 * thermal zone device callbacks.
 487 *
 488 * TODO: When the support to several sensors per zone is added, this
 489 * function must search the sensor list based on @dev parameter.
 490 *
 491 */
 492void thermal_zone_of_sensor_unregister(struct device *dev,
 493                                       struct thermal_zone_device *tzd)
 494{
 495        struct __thermal_zone *tz;
 496
 497        if (!dev || !tzd || !tzd->devdata)
 498                return;
 499
 500        tz = tzd->devdata;
 501
 502        /* no __thermal_zone, nothing to be done */
 503        if (!tz)
 504                return;
 505
 506        mutex_lock(&tzd->lock);
 507        tzd->ops->get_temp = NULL;
 508        tzd->ops->get_trend = NULL;
 509        tzd->ops->set_emul_temp = NULL;
 510
 511        tz->ops = NULL;
 512        tz->sensor_data = NULL;
 513        mutex_unlock(&tzd->lock);
 514}
 515EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_unregister);
 516
 517static void devm_thermal_zone_of_sensor_release(struct device *dev, void *res)
 518{
 519        thermal_zone_of_sensor_unregister(dev,
 520                                          *(struct thermal_zone_device **)res);
 521}
 522
 523static int devm_thermal_zone_of_sensor_match(struct device *dev, void *res,
 524                                             void *data)
 525{
 526        struct thermal_zone_device **r = res;
 527
 528        if (WARN_ON(!r || !*r))
 529                return 0;
 530
 531        return *r == data;
 532}
 533
 534/**
 535 * devm_thermal_zone_of_sensor_register - Resource managed version of
 536 *                              thermal_zone_of_sensor_register()
 537 * @dev: a valid struct device pointer of a sensor device. Must contain
 538 *       a valid .of_node, for the sensor node.
 539 * @sensor_id: a sensor identifier, in case the sensor IP has more
 540 *             than one sensors
 541 * @data: a private pointer (owned by the caller) that will be passed
 542 *        back, when a temperature reading is needed.
 543 * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
 544 *
 545 * Refer thermal_zone_of_sensor_register() for more details.
 546 *
 547 * Return: On success returns a valid struct thermal_zone_device,
 548 * otherwise, it returns a corresponding ERR_PTR(). Caller must
 549 * check the return value with help of IS_ERR() helper.
 550 * Registered thermal_zone_device device will automatically be
 551 * released when device is unbounded.
 552 */
 553struct thermal_zone_device *devm_thermal_zone_of_sensor_register(
 554        struct device *dev, int sensor_id,
 555        void *data, const struct thermal_zone_of_device_ops *ops)
 556{
 557        struct thermal_zone_device **ptr, *tzd;
 558
 559        ptr = devres_alloc(devm_thermal_zone_of_sensor_release, sizeof(*ptr),
 560                           GFP_KERNEL);
 561        if (!ptr)
 562                return ERR_PTR(-ENOMEM);
 563
 564        tzd = thermal_zone_of_sensor_register(dev, sensor_id, data, ops);
 565        if (IS_ERR(tzd)) {
 566                devres_free(ptr);
 567                return tzd;
 568        }
 569
 570        *ptr = tzd;
 571        devres_add(dev, ptr);
 572
 573        return tzd;
 574}
 575EXPORT_SYMBOL_GPL(devm_thermal_zone_of_sensor_register);
 576
 577/**
 578 * devm_thermal_zone_of_sensor_unregister - Resource managed version of
 579 *                              thermal_zone_of_sensor_unregister().
 580 * @dev: Device for which which resource was allocated.
 581 * @tzd: a pointer to struct thermal_zone_device where the sensor is registered.
 582 *
 583 * This function removes the sensor callbacks and private data from the
 584 * thermal zone device registered with devm_thermal_zone_of_sensor_register()
 585 * API. It will also silent the zone by remove the .get_temp() and .get_trend()
 586 * thermal zone device callbacks.
 587 * Normally this function will not need to be called and the resource
 588 * management code will ensure that the resource is freed.
 589 */
 590void devm_thermal_zone_of_sensor_unregister(struct device *dev,
 591                                            struct thermal_zone_device *tzd)
 592{
 593        WARN_ON(devres_release(dev, devm_thermal_zone_of_sensor_release,
 594                               devm_thermal_zone_of_sensor_match, tzd));
 595}
 596EXPORT_SYMBOL_GPL(devm_thermal_zone_of_sensor_unregister);
 597
 598/***   functions parsing device tree nodes   ***/
 599
 600/**
 601 * thermal_of_populate_bind_params - parse and fill cooling map data
 602 * @np: DT node containing a cooling-map node
 603 * @__tbp: data structure to be filled with cooling map info
 604 * @trips: array of thermal zone trip points
 605 * @ntrips: number of trip points inside trips.
 606 *
 607 * This function parses a cooling-map type of node represented by
 608 * @np parameter and fills the read data into @__tbp data structure.
 609 * It needs the already parsed array of trip points of the thermal zone
 610 * in consideration.
 611 *
 612 * Return: 0 on success, proper error code otherwise
 613 */
 614static int thermal_of_populate_bind_params(struct device_node *np,
 615                                           struct __thermal_bind_params *__tbp,
 616                                           struct thermal_trip *trips,
 617                                           int ntrips)
 618{
 619        struct of_phandle_args cooling_spec;
 620        struct device_node *trip;
 621        int ret, i;
 622        u32 prop;
 623
 624        /* Default weight. Usage is optional */
 625        __tbp->usage = THERMAL_WEIGHT_DEFAULT;
 626        ret = of_property_read_u32(np, "contribution", &prop);
 627        if (ret == 0)
 628                __tbp->usage = prop;
 629
 630        trip = of_parse_phandle(np, "trip", 0);
 631        if (!trip) {
 632                pr_err("missing trip property\n");
 633                return -ENODEV;
 634        }
 635
 636        /* match using device_node */
 637        for (i = 0; i < ntrips; i++)
 638                if (trip == trips[i].np) {
 639                        __tbp->trip_id = i;
 640                        break;
 641                }
 642
 643        if (i == ntrips) {
 644                ret = -ENODEV;
 645                goto end;
 646        }
 647
 648        ret = of_parse_phandle_with_args(np, "cooling-device", "#cooling-cells",
 649                                         0, &cooling_spec);
 650        if (ret < 0) {
 651                pr_err("missing cooling_device property\n");
 652                goto end;
 653        }
 654        __tbp->cooling_device = cooling_spec.np;
 655        if (cooling_spec.args_count >= 2) { /* at least min and max */
 656                __tbp->min = cooling_spec.args[0];
 657                __tbp->max = cooling_spec.args[1];
 658        } else {
 659                pr_err("wrong reference to cooling device, missing limits\n");
 660        }
 661
 662end:
 663        of_node_put(trip);
 664
 665        return ret;
 666}
 667
 668/**
 669 * It maps 'enum thermal_trip_type' found in include/linux/thermal.h
 670 * into the device tree binding of 'trip', property type.
 671 */
 672static const char * const trip_types[] = {
 673        [THERMAL_TRIP_ACTIVE]   = "active",
 674        [THERMAL_TRIP_PASSIVE]  = "passive",
 675        [THERMAL_TRIP_HOT]      = "hot",
 676        [THERMAL_TRIP_CRITICAL] = "critical",
 677};
 678
 679/**
 680 * thermal_of_get_trip_type - Get phy mode for given device_node
 681 * @np: Pointer to the given device_node
 682 * @type: Pointer to resulting trip type
 683 *
 684 * The function gets trip type string from property 'type',
 685 * and store its index in trip_types table in @type,
 686 *
 687 * Return: 0 on success, or errno in error case.
 688 */
 689static int thermal_of_get_trip_type(struct device_node *np,
 690                                    enum thermal_trip_type *type)
 691{
 692        const char *t;
 693        int err, i;
 694
 695        err = of_property_read_string(np, "type", &t);
 696        if (err < 0)
 697                return err;
 698
 699        for (i = 0; i < ARRAY_SIZE(trip_types); i++)
 700                if (!strcasecmp(t, trip_types[i])) {
 701                        *type = i;
 702                        return 0;
 703                }
 704
 705        return -ENODEV;
 706}
 707
 708/**
 709 * thermal_of_populate_trip - parse and fill one trip point data
 710 * @np: DT node containing a trip point node
 711 * @trip: trip point data structure to be filled up
 712 *
 713 * This function parses a trip point type of node represented by
 714 * @np parameter and fills the read data into @trip data structure.
 715 *
 716 * Return: 0 on success, proper error code otherwise
 717 */
 718static int thermal_of_populate_trip(struct device_node *np,
 719                                    struct thermal_trip *trip)
 720{
 721        int prop;
 722        int ret;
 723
 724        ret = of_property_read_u32(np, "temperature", &prop);
 725        if (ret < 0) {
 726                pr_err("missing temperature property\n");
 727                return ret;
 728        }
 729        trip->temperature = prop;
 730
 731        ret = of_property_read_u32(np, "hysteresis", &prop);
 732        if (ret < 0) {
 733                pr_err("missing hysteresis property\n");
 734                return ret;
 735        }
 736        trip->hysteresis = prop;
 737
 738        ret = thermal_of_get_trip_type(np, &trip->type);
 739        if (ret < 0) {
 740                pr_err("wrong trip type property\n");
 741                return ret;
 742        }
 743
 744        /* Required for cooling map matching */
 745        trip->np = np;
 746        of_node_get(np);
 747
 748        return 0;
 749}
 750
 751/**
 752 * thermal_of_build_thermal_zone - parse and fill one thermal zone data
 753 * @np: DT node containing a thermal zone node
 754 *
 755 * This function parses a thermal zone type of node represented by
 756 * @np parameter and fills the read data into a __thermal_zone data structure
 757 * and return this pointer.
 758 *
 759 * TODO: Missing properties to parse: thermal-sensor-names
 760 *
 761 * Return: On success returns a valid struct __thermal_zone,
 762 * otherwise, it returns a corresponding ERR_PTR(). Caller must
 763 * check the return value with help of IS_ERR() helper.
 764 */
 765static struct __thermal_zone
 766__init *thermal_of_build_thermal_zone(struct device_node *np)
 767{
 768        struct device_node *child = NULL, *gchild;
 769        struct __thermal_zone *tz;
 770        int ret, i;
 771        u32 prop, coef[2];
 772
 773        if (!np) {
 774                pr_err("no thermal zone np\n");
 775                return ERR_PTR(-EINVAL);
 776        }
 777
 778        tz = kzalloc(sizeof(*tz), GFP_KERNEL);
 779        if (!tz)
 780                return ERR_PTR(-ENOMEM);
 781
 782        ret = of_property_read_u32(np, "polling-delay-passive", &prop);
 783        if (ret < 0) {
 784                pr_err("missing polling-delay-passive property\n");
 785                goto free_tz;
 786        }
 787        tz->passive_delay = prop;
 788
 789        ret = of_property_read_u32(np, "polling-delay", &prop);
 790        if (ret < 0) {
 791                pr_err("missing polling-delay property\n");
 792                goto free_tz;
 793        }
 794        tz->polling_delay = prop;
 795
 796        /*
 797         * REVIST: for now, the thermal framework supports only
 798         * one sensor per thermal zone. Thus, we are considering
 799         * only the first two values as slope and offset.
 800         */
 801        ret = of_property_read_u32_array(np, "coefficients", coef, 2);
 802        if (ret == 0) {
 803                tz->slope = coef[0];
 804                tz->offset = coef[1];
 805        } else {
 806                tz->slope = 1;
 807                tz->offset = 0;
 808        }
 809
 810        /* trips */
 811        child = of_get_child_by_name(np, "trips");
 812
 813        /* No trips provided */
 814        if (!child)
 815                goto finish;
 816
 817        tz->ntrips = of_get_child_count(child);
 818        if (tz->ntrips == 0) /* must have at least one child */
 819                goto finish;
 820
 821        tz->trips = kcalloc(tz->ntrips, sizeof(*tz->trips), GFP_KERNEL);
 822        if (!tz->trips) {
 823                ret = -ENOMEM;
 824                goto free_tz;
 825        }
 826
 827        i = 0;
 828        for_each_child_of_node(child, gchild) {
 829                ret = thermal_of_populate_trip(gchild, &tz->trips[i++]);
 830                if (ret)
 831                        goto free_trips;
 832        }
 833
 834        of_node_put(child);
 835
 836        /* cooling-maps */
 837        child = of_get_child_by_name(np, "cooling-maps");
 838
 839        /* cooling-maps not provided */
 840        if (!child)
 841                goto finish;
 842
 843        tz->num_tbps = of_get_child_count(child);
 844        if (tz->num_tbps == 0)
 845                goto finish;
 846
 847        tz->tbps = kcalloc(tz->num_tbps, sizeof(*tz->tbps), GFP_KERNEL);
 848        if (!tz->tbps) {
 849                ret = -ENOMEM;
 850                goto free_trips;
 851        }
 852
 853        i = 0;
 854        for_each_child_of_node(child, gchild) {
 855                ret = thermal_of_populate_bind_params(gchild, &tz->tbps[i++],
 856                                                      tz->trips, tz->ntrips);
 857                if (ret)
 858                        goto free_tbps;
 859        }
 860
 861finish:
 862        of_node_put(child);
 863
 864        return tz;
 865
 866free_tbps:
 867        for (i = i - 1; i >= 0; i--)
 868                of_node_put(tz->tbps[i].cooling_device);
 869        kfree(tz->tbps);
 870free_trips:
 871        for (i = 0; i < tz->ntrips; i++)
 872                of_node_put(tz->trips[i].np);
 873        kfree(tz->trips);
 874        of_node_put(gchild);
 875free_tz:
 876        kfree(tz);
 877        of_node_put(child);
 878
 879        return ERR_PTR(ret);
 880}
 881
 882static inline void of_thermal_free_zone(struct __thermal_zone *tz)
 883{
 884        int i;
 885
 886        for (i = 0; i < tz->num_tbps; i++)
 887                of_node_put(tz->tbps[i].cooling_device);
 888        kfree(tz->tbps);
 889        for (i = 0; i < tz->ntrips; i++)
 890                of_node_put(tz->trips[i].np);
 891        kfree(tz->trips);
 892        kfree(tz);
 893}
 894
 895/**
 896 * of_parse_thermal_zones - parse device tree thermal data
 897 *
 898 * Initialization function that can be called by machine initialization
 899 * code to parse thermal data and populate the thermal framework
 900 * with hardware thermal zones info. This function only parses thermal zones.
 901 * Cooling devices and sensor devices nodes are supposed to be parsed
 902 * by their respective drivers.
 903 *
 904 * Return: 0 on success, proper error code otherwise
 905 *
 906 */
 907int __init of_parse_thermal_zones(void)
 908{
 909        struct device_node *np, *child;
 910        struct __thermal_zone *tz;
 911        struct thermal_zone_device_ops *ops;
 912
 913        np = of_find_node_by_name(NULL, "thermal-zones");
 914        if (!np) {
 915                pr_debug("unable to find thermal zones\n");
 916                return 0; /* Run successfully on systems without thermal DT */
 917        }
 918
 919        for_each_available_child_of_node(np, child) {
 920                struct thermal_zone_device *zone;
 921                struct thermal_zone_params *tzp;
 922                int i, mask = 0;
 923                u32 prop;
 924
 925                tz = thermal_of_build_thermal_zone(child);
 926                if (IS_ERR(tz)) {
 927                        pr_err("failed to build thermal zone %s: %ld\n",
 928                               child->name,
 929                               PTR_ERR(tz));
 930                        continue;
 931                }
 932
 933                ops = kmemdup(&of_thermal_ops, sizeof(*ops), GFP_KERNEL);
 934                if (!ops)
 935                        goto exit_free;
 936
 937                tzp = kzalloc(sizeof(*tzp), GFP_KERNEL);
 938                if (!tzp) {
 939                        kfree(ops);
 940                        goto exit_free;
 941                }
 942
 943                /* No hwmon because there might be hwmon drivers registering */
 944                tzp->no_hwmon = true;
 945
 946                if (!of_property_read_u32(child, "sustainable-power", &prop))
 947                        tzp->sustainable_power = prop;
 948
 949                for (i = 0; i < tz->ntrips; i++)
 950                        mask |= 1 << i;
 951
 952                /* these two are left for temperature drivers to use */
 953                tzp->slope = tz->slope;
 954                tzp->offset = tz->offset;
 955
 956                zone = thermal_zone_device_register(child->name, tz->ntrips,
 957                                                    mask, tz,
 958                                                    ops, tzp,
 959                                                    tz->passive_delay,
 960                                                    tz->polling_delay);
 961                if (IS_ERR(zone)) {
 962                        pr_err("Failed to build %s zone %ld\n", child->name,
 963                               PTR_ERR(zone));
 964                        kfree(tzp);
 965                        kfree(ops);
 966                        of_thermal_free_zone(tz);
 967                        /* attempting to build remaining zones still */
 968                }
 969        }
 970        of_node_put(np);
 971
 972        return 0;
 973
 974exit_free:
 975        of_node_put(child);
 976        of_node_put(np);
 977        of_thermal_free_zone(tz);
 978
 979        /* no memory available, so free what we have built */
 980        of_thermal_destroy_zones();
 981
 982        return -ENOMEM;
 983}
 984
 985/**
 986 * of_thermal_destroy_zones - remove all zones parsed and allocated resources
 987 *
 988 * Finds all zones parsed and added to the thermal framework and remove them
 989 * from the system, together with their resources.
 990 *
 991 */
 992void of_thermal_destroy_zones(void)
 993{
 994        struct device_node *np, *child;
 995
 996        np = of_find_node_by_name(NULL, "thermal-zones");
 997        if (!np) {
 998                pr_debug("unable to find thermal zones\n");
 999                return;
1000        }
1001
1002        for_each_available_child_of_node(np, child) {
1003                struct thermal_zone_device *zone;
1004
1005                zone = thermal_zone_get_zone_by_name(child->name);
1006                if (IS_ERR(zone))
1007                        continue;
1008
1009                thermal_zone_device_unregister(zone);
1010                kfree(zone->tzp);
1011                kfree(zone->ops);
1012                of_thermal_free_zone(zone->devdata);
1013        }
1014        of_node_put(np);
1015}
1016