244 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * int340x_thermal_zone.c4 * 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 = thermal_zone_device_priv(zone);18 unsigned long long tmp;19 acpi_status status;20 21 status = acpi_evaluate_integer(d->adev->handle, "_TMP", NULL, &tmp);22 if (ACPI_FAILURE(status))23 return -EIO;24 25 if (d->lpat_table) {26 int conv_temp;27 28 conv_temp = acpi_lpat_raw_to_temp(d->lpat_table, (int)tmp);29 if (conv_temp < 0)30 return conv_temp;31 32 *temp = conv_temp * 10;33 } else {34 /* _TMP returns the temperature in tenths of degrees Kelvin */35 *temp = deci_kelvin_to_millicelsius(tmp);36 }37 38 return 0;39}40 41static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone,42 const struct thermal_trip *trip, int temp)43{44 struct int34x_thermal_zone *d = thermal_zone_device_priv(zone);45 unsigned int trip_index = THERMAL_TRIP_PRIV_TO_INT(trip->priv);46 char name[] = {'P', 'A', 'T', '0' + trip_index, '\0'};47 acpi_status status;48 49 if (trip_index > 9)50 return -EINVAL;51 52 status = acpi_execute_simple_method(d->adev->handle, name,53 millicelsius_to_deci_kelvin(temp));54 if (ACPI_FAILURE(status))55 return -EIO;56 57 return 0;58}59 60static void int340x_thermal_critical(struct thermal_zone_device *zone)61{62 dev_dbg(thermal_zone_device(zone), "%s: critical temperature reached\n",63 thermal_zone_device_type(zone));64}65 66static int int340x_thermal_read_trips(struct acpi_device *zone_adev,67 struct thermal_trip *zone_trips,68 int trip_cnt)69{70 int i, ret;71 72 ret = thermal_acpi_critical_trip_temp(zone_adev,73 &zone_trips[trip_cnt].temperature);74 if (!ret) {75 zone_trips[trip_cnt].type = THERMAL_TRIP_CRITICAL;76 trip_cnt++;77 }78 79 ret = thermal_acpi_hot_trip_temp(zone_adev,80 &zone_trips[trip_cnt].temperature);81 if (!ret) {82 zone_trips[trip_cnt].type = THERMAL_TRIP_HOT;83 trip_cnt++;84 }85 86 ret = thermal_acpi_passive_trip_temp(zone_adev,87 &zone_trips[trip_cnt].temperature);88 if (!ret) {89 zone_trips[trip_cnt].type = THERMAL_TRIP_PASSIVE;90 trip_cnt++;91 }92 93 for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {94 ret = thermal_acpi_active_trip_temp(zone_adev, i,95 &zone_trips[trip_cnt].temperature);96 if (ret)97 break;98 99 zone_trips[trip_cnt].type = THERMAL_TRIP_ACTIVE;100 zone_trips[trip_cnt].priv = THERMAL_INT_TO_TRIP_PRIV(i);101 trip_cnt++;102 }103 104 return trip_cnt;105}106 107static struct thermal_zone_params int340x_thermal_params = {108 .governor_name = "user_space",109 .no_hwmon = true,110};111 112struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,113 int (*get_temp) (struct thermal_zone_device *, int *))114{115 const struct thermal_zone_device_ops zone_ops = {116 .set_trip_temp = int340x_thermal_set_trip_temp,117 .critical = int340x_thermal_critical,118 .get_temp = get_temp ? get_temp : int340x_thermal_get_zone_temp,119 };120 struct int34x_thermal_zone *int34x_zone;121 struct thermal_trip *zone_trips;122 unsigned long long trip_cnt = 0;123 unsigned long long hyst;124 acpi_status status;125 int i, ret;126 127 int34x_zone = kzalloc(sizeof(*int34x_zone), GFP_KERNEL);128 if (!int34x_zone)129 return ERR_PTR(-ENOMEM);130 131 int34x_zone->adev = adev;132 133 status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt);134 if (ACPI_SUCCESS(status))135 int34x_zone->aux_trip_nr = trip_cnt;136 137 zone_trips = kzalloc(sizeof(*zone_trips) * (trip_cnt + INT340X_THERMAL_MAX_TRIP_COUNT),138 GFP_KERNEL);139 if (!zone_trips) {140 ret = -ENOMEM;141 goto err_trips_alloc;142 }143 144 for (i = 0; i < trip_cnt; i++) {145 zone_trips[i].type = THERMAL_TRIP_PASSIVE;146 zone_trips[i].temperature = THERMAL_TEMP_INVALID;147 zone_trips[i].flags |= THERMAL_TRIP_FLAG_RW_TEMP;148 zone_trips[i].priv = THERMAL_INT_TO_TRIP_PRIV(i);149 }150 151 trip_cnt = int340x_thermal_read_trips(adev, zone_trips, trip_cnt);152 153 status = acpi_evaluate_integer(adev->handle, "GTSH", NULL, &hyst);154 if (ACPI_SUCCESS(status))155 hyst *= 100;156 else157 hyst = 0;158 159 for (i = 0; i < trip_cnt; ++i)160 zone_trips[i].hysteresis = hyst;161 162 int34x_zone->lpat_table = acpi_lpat_get_conversion_table(adev->handle);163 164 int34x_zone->zone = thermal_zone_device_register_with_trips(165 acpi_device_bid(adev),166 zone_trips, trip_cnt,167 int34x_zone,168 &zone_ops,169 &int340x_thermal_params,170 0, 0);171 kfree(zone_trips);172 173 if (IS_ERR(int34x_zone->zone)) {174 ret = PTR_ERR(int34x_zone->zone);175 goto err_thermal_zone;176 }177 ret = thermal_zone_device_enable(int34x_zone->zone);178 if (ret)179 goto err_enable;180 181 return int34x_zone;182 183err_enable:184 thermal_zone_device_unregister(int34x_zone->zone);185err_thermal_zone:186 acpi_lpat_free_conversion_table(int34x_zone->lpat_table);187err_trips_alloc:188 kfree(int34x_zone);189 return ERR_PTR(ret);190}191EXPORT_SYMBOL_GPL(int340x_thermal_zone_add);192 193void int340x_thermal_zone_remove(struct int34x_thermal_zone *int34x_zone)194{195 thermal_zone_device_unregister(int34x_zone->zone);196 acpi_lpat_free_conversion_table(int34x_zone->lpat_table);197 kfree(int34x_zone);198}199EXPORT_SYMBOL_GPL(int340x_thermal_zone_remove);200 201static int int340x_update_one_trip(struct thermal_trip *trip, void *arg)202{203 struct int34x_thermal_zone *int34x_zone = arg;204 struct acpi_device *zone_adev = int34x_zone->adev;205 int temp, err;206 207 switch (trip->type) {208 case THERMAL_TRIP_CRITICAL:209 err = thermal_acpi_critical_trip_temp(zone_adev, &temp);210 break;211 case THERMAL_TRIP_HOT:212 err = thermal_acpi_hot_trip_temp(zone_adev, &temp);213 break;214 case THERMAL_TRIP_PASSIVE:215 err = thermal_acpi_passive_trip_temp(zone_adev, &temp);216 break;217 case THERMAL_TRIP_ACTIVE:218 err = thermal_acpi_active_trip_temp(zone_adev,219 THERMAL_TRIP_PRIV_TO_INT(trip->priv),220 &temp);221 break;222 default:223 err = -ENODEV;224 }225 if (err)226 temp = THERMAL_TEMP_INVALID;227 228 thermal_zone_set_trip_temp(int34x_zone->zone, trip, temp);229 230 return 0;231}232 233void int340x_thermal_update_trips(struct int34x_thermal_zone *int34x_zone)234{235 thermal_zone_for_each_trip(int34x_zone->zone, int340x_update_one_trip,236 int34x_zone);237}238EXPORT_SYMBOL_GPL(int340x_thermal_update_trips);239 240MODULE_AUTHOR("Aaron Lu <aaron.lu@intel.com>");241MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");242MODULE_DESCRIPTION("Intel INT340x common thermal zone handler");243MODULE_LICENSE("GPL v2");244