linux/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * int340x_thermal_zone.c
   4 * Copyright (c) 2015, Intel Corporation.
   5 */
   6#include <linux/kernel.h>
   7#include <linux/module.h>
   8#include <linux/init.h>
   9#include <linux/acpi.h>
  10#include <linux/thermal.h>
  11#include <linux/units.h>
  12#include "int340x_thermal_zone.h"
  13
  14static int int340x_thermal_get_zone_temp(struct thermal_zone_device *zone,
  15                                         int *temp)
  16{
  17        struct int34x_thermal_zone *d = zone->devdata;
  18        unsigned long long tmp;
  19        acpi_status status;
  20
  21        if (d->override_ops && d->override_ops->get_temp)
  22                return d->override_ops->get_temp(zone, temp);
  23
  24        status = acpi_evaluate_integer(d->adev->handle, "_TMP", NULL, &tmp);
  25        if (ACPI_FAILURE(status))
  26                return -EIO;
  27
  28        if (d->lpat_table) {
  29                int conv_temp;
  30
  31                conv_temp = acpi_lpat_raw_to_temp(d->lpat_table, (int)tmp);
  32                if (conv_temp < 0)
  33                        return conv_temp;
  34
  35                *temp = (unsigned long)conv_temp * 10;
  36        } else
  37                /* _TMP returns the temperature in tenths of degrees Kelvin */
  38                *temp = deci_kelvin_to_millicelsius(tmp);
  39
  40        return 0;
  41}
  42
  43static int int340x_thermal_get_trip_temp(struct thermal_zone_device *zone,
  44                                         int trip, int *temp)
  45{
  46        struct int34x_thermal_zone *d = zone->devdata;
  47        int i;
  48
  49        if (d->override_ops && d->override_ops->get_trip_temp)
  50                return d->override_ops->get_trip_temp(zone, trip, temp);
  51
  52        if (trip < d->aux_trip_nr)
  53                *temp = d->aux_trips[trip];
  54        else if (trip == d->crt_trip_id)
  55                *temp = d->crt_temp;
  56        else if (trip == d->psv_trip_id)
  57                *temp = d->psv_temp;
  58        else if (trip == d->hot_trip_id)
  59                *temp = d->hot_temp;
  60        else {
  61                for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
  62                        if (d->act_trips[i].valid &&
  63                            d->act_trips[i].id == trip) {
  64                                *temp = d->act_trips[i].temp;
  65                                break;
  66                        }
  67                }
  68                if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT)
  69                        return -EINVAL;
  70        }
  71
  72        return 0;
  73}
  74
  75static int int340x_thermal_get_trip_type(struct thermal_zone_device *zone,
  76                                         int trip,
  77                                         enum thermal_trip_type *type)
  78{
  79        struct int34x_thermal_zone *d = zone->devdata;
  80        int i;
  81
  82        if (d->override_ops && d->override_ops->get_trip_type)
  83                return d->override_ops->get_trip_type(zone, trip, type);
  84
  85        if (trip < d->aux_trip_nr)
  86                *type = THERMAL_TRIP_PASSIVE;
  87        else if (trip == d->crt_trip_id)
  88                *type = THERMAL_TRIP_CRITICAL;
  89        else if (trip == d->hot_trip_id)
  90                *type = THERMAL_TRIP_HOT;
  91        else if (trip == d->psv_trip_id)
  92                *type = THERMAL_TRIP_PASSIVE;
  93        else {
  94                for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
  95                        if (d->act_trips[i].valid &&
  96                            d->act_trips[i].id == trip) {
  97                                *type = THERMAL_TRIP_ACTIVE;
  98                                break;
  99                        }
 100                }
 101                if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT)
 102                        return -EINVAL;
 103        }
 104
 105        return 0;
 106}
 107
 108static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone,
 109                                      int trip, int temp)
 110{
 111        struct int34x_thermal_zone *d = zone->devdata;
 112        acpi_status status;
 113        char name[10];
 114
 115        if (d->override_ops && d->override_ops->set_trip_temp)
 116                return d->override_ops->set_trip_temp(zone, trip, temp);
 117
 118        snprintf(name, sizeof(name), "PAT%d", trip);
 119        status = acpi_execute_simple_method(d->adev->handle, name,
 120                        millicelsius_to_deci_kelvin(temp));
 121        if (ACPI_FAILURE(status))
 122                return -EIO;
 123
 124        d->aux_trips[trip] = temp;
 125
 126        return 0;
 127}
 128
 129
 130static int int340x_thermal_get_trip_hyst(struct thermal_zone_device *zone,
 131                int trip, int *temp)
 132{
 133        struct int34x_thermal_zone *d = zone->devdata;
 134        acpi_status status;
 135        unsigned long long hyst;
 136
 137        if (d->override_ops && d->override_ops->get_trip_hyst)
 138                return d->override_ops->get_trip_hyst(zone, trip, temp);
 139
 140        status = acpi_evaluate_integer(d->adev->handle, "GTSH", NULL, &hyst);
 141        if (ACPI_FAILURE(status))
 142                *temp = 0;
 143        else
 144                *temp = hyst * 100;
 145
 146        return 0;
 147}
 148
 149static struct thermal_zone_device_ops int340x_thermal_zone_ops = {
 150        .get_temp       = int340x_thermal_get_zone_temp,
 151        .get_trip_temp  = int340x_thermal_get_trip_temp,
 152        .get_trip_type  = int340x_thermal_get_trip_type,
 153        .set_trip_temp  = int340x_thermal_set_trip_temp,
 154        .get_trip_hyst =  int340x_thermal_get_trip_hyst,
 155};
 156
 157static int int340x_thermal_get_trip_config(acpi_handle handle, char *name,
 158                                      int *temp)
 159{
 160        unsigned long long r;
 161        acpi_status status;
 162
 163        status = acpi_evaluate_integer(handle, name, NULL, &r);
 164        if (ACPI_FAILURE(status))
 165                return -EIO;
 166
 167        *temp = deci_kelvin_to_millicelsius(r);
 168
 169        return 0;
 170}
 171
 172int int340x_thermal_read_trips(struct int34x_thermal_zone *int34x_zone)
 173{
 174        int trip_cnt = int34x_zone->aux_trip_nr;
 175        int i;
 176
 177        int34x_zone->crt_trip_id = -1;
 178        if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_CRT",
 179                                             &int34x_zone->crt_temp))
 180                int34x_zone->crt_trip_id = trip_cnt++;
 181
 182        int34x_zone->hot_trip_id = -1;
 183        if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_HOT",
 184                                             &int34x_zone->hot_temp))
 185                int34x_zone->hot_trip_id = trip_cnt++;
 186
 187        int34x_zone->psv_trip_id = -1;
 188        if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_PSV",
 189                                             &int34x_zone->psv_temp))
 190                int34x_zone->psv_trip_id = trip_cnt++;
 191
 192        for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
 193                char name[5] = { '_', 'A', 'C', '0' + i, '\0' };
 194
 195                if (int340x_thermal_get_trip_config(int34x_zone->adev->handle,
 196                                        name,
 197                                        &int34x_zone->act_trips[i].temp))
 198                        break;
 199
 200                int34x_zone->act_trips[i].id = trip_cnt++;
 201                int34x_zone->act_trips[i].valid = true;
 202        }
 203
 204        return trip_cnt;
 205}
 206EXPORT_SYMBOL_GPL(int340x_thermal_read_trips);
 207
 208static struct thermal_zone_params int340x_thermal_params = {
 209        .governor_name = "user_space",
 210        .no_hwmon = true,
 211};
 212
 213struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
 214                                struct thermal_zone_device_ops *override_ops)
 215{
 216        struct int34x_thermal_zone *int34x_thermal_zone;
 217        acpi_status status;
 218        unsigned long long trip_cnt;
 219        int trip_mask = 0;
 220        int ret;
 221
 222        int34x_thermal_zone = kzalloc(sizeof(*int34x_thermal_zone),
 223                                      GFP_KERNEL);
 224        if (!int34x_thermal_zone)
 225                return ERR_PTR(-ENOMEM);
 226
 227        int34x_thermal_zone->adev = adev;
 228        int34x_thermal_zone->override_ops = override_ops;
 229
 230        status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt);
 231        if (ACPI_FAILURE(status))
 232                trip_cnt = 0;
 233        else {
 234                int34x_thermal_zone->aux_trips =
 235                        kcalloc(trip_cnt,
 236                                sizeof(*int34x_thermal_zone->aux_trips),
 237                                GFP_KERNEL);
 238                if (!int34x_thermal_zone->aux_trips) {
 239                        ret = -ENOMEM;
 240                        goto err_trip_alloc;
 241                }
 242                trip_mask = BIT(trip_cnt) - 1;
 243                int34x_thermal_zone->aux_trip_nr = trip_cnt;
 244        }
 245
 246        trip_cnt = int340x_thermal_read_trips(int34x_thermal_zone);
 247
 248        int34x_thermal_zone->lpat_table = acpi_lpat_get_conversion_table(
 249                                                                adev->handle);
 250
 251        int34x_thermal_zone->zone = thermal_zone_device_register(
 252                                                acpi_device_bid(adev),
 253                                                trip_cnt,
 254                                                trip_mask, int34x_thermal_zone,
 255                                                &int340x_thermal_zone_ops,
 256                                                &int340x_thermal_params,
 257                                                0, 0);
 258        if (IS_ERR(int34x_thermal_zone->zone)) {
 259                ret = PTR_ERR(int34x_thermal_zone->zone);
 260                goto err_thermal_zone;
 261        }
 262        ret = thermal_zone_device_enable(int34x_thermal_zone->zone);
 263        if (ret)
 264                goto err_enable;
 265
 266        return int34x_thermal_zone;
 267
 268err_enable:
 269        thermal_zone_device_unregister(int34x_thermal_zone->zone);
 270err_thermal_zone:
 271        acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
 272        kfree(int34x_thermal_zone->aux_trips);
 273err_trip_alloc:
 274        kfree(int34x_thermal_zone);
 275        return ERR_PTR(ret);
 276}
 277EXPORT_SYMBOL_GPL(int340x_thermal_zone_add);
 278
 279void int340x_thermal_zone_remove(struct int34x_thermal_zone
 280                                 *int34x_thermal_zone)
 281{
 282        thermal_zone_device_unregister(int34x_thermal_zone->zone);
 283        acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
 284        kfree(int34x_thermal_zone->aux_trips);
 285        kfree(int34x_thermal_zone);
 286}
 287EXPORT_SYMBOL_GPL(int340x_thermal_zone_remove);
 288
 289MODULE_AUTHOR("Aaron Lu <aaron.lu@intel.com>");
 290MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
 291MODULE_DESCRIPTION("Intel INT340x common thermal zone handler");
 292MODULE_LICENSE("GPL v2");
 293