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