Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * int340x_thermal_zone.c
0004  * Copyright (c) 2015, Intel Corporation.
0005  */
0006 #include <linux/kernel.h>
0007 #include <linux/module.h>
0008 #include <linux/init.h>
0009 #include <linux/acpi.h>
0010 #include <linux/thermal.h>
0011 #include <linux/units.h>
0012 #include "int340x_thermal_zone.h"
0013 
0014 static int int340x_thermal_get_zone_temp(struct thermal_zone_device *zone,
0015                      int *temp)
0016 {
0017     struct int34x_thermal_zone *d = zone->devdata;
0018     unsigned long long tmp;
0019     acpi_status status;
0020 
0021     if (d->override_ops && d->override_ops->get_temp)
0022         return d->override_ops->get_temp(zone, temp);
0023 
0024     status = acpi_evaluate_integer(d->adev->handle, "_TMP", NULL, &tmp);
0025     if (ACPI_FAILURE(status))
0026         return -EIO;
0027 
0028     if (d->lpat_table) {
0029         int conv_temp;
0030 
0031         conv_temp = acpi_lpat_raw_to_temp(d->lpat_table, (int)tmp);
0032         if (conv_temp < 0)
0033             return conv_temp;
0034 
0035         *temp = (unsigned long)conv_temp * 10;
0036     } else
0037         /* _TMP returns the temperature in tenths of degrees Kelvin */
0038         *temp = deci_kelvin_to_millicelsius(tmp);
0039 
0040     return 0;
0041 }
0042 
0043 static int int340x_thermal_get_trip_temp(struct thermal_zone_device *zone,
0044                      int trip, int *temp)
0045 {
0046     struct int34x_thermal_zone *d = zone->devdata;
0047     int i;
0048 
0049     if (d->override_ops && d->override_ops->get_trip_temp)
0050         return d->override_ops->get_trip_temp(zone, trip, temp);
0051 
0052     if (trip < d->aux_trip_nr)
0053         *temp = d->aux_trips[trip];
0054     else if (trip == d->crt_trip_id)
0055         *temp = d->crt_temp;
0056     else if (trip == d->psv_trip_id)
0057         *temp = d->psv_temp;
0058     else if (trip == d->hot_trip_id)
0059         *temp = d->hot_temp;
0060     else {
0061         for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
0062             if (d->act_trips[i].valid &&
0063                 d->act_trips[i].id == trip) {
0064                 *temp = d->act_trips[i].temp;
0065                 break;
0066             }
0067         }
0068         if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT)
0069             return -EINVAL;
0070     }
0071 
0072     return 0;
0073 }
0074 
0075 static int int340x_thermal_get_trip_type(struct thermal_zone_device *zone,
0076                      int trip,
0077                      enum thermal_trip_type *type)
0078 {
0079     struct int34x_thermal_zone *d = zone->devdata;
0080     int i;
0081 
0082     if (d->override_ops && d->override_ops->get_trip_type)
0083         return d->override_ops->get_trip_type(zone, trip, type);
0084 
0085     if (trip < d->aux_trip_nr)
0086         *type = THERMAL_TRIP_PASSIVE;
0087     else if (trip == d->crt_trip_id)
0088         *type = THERMAL_TRIP_CRITICAL;
0089     else if (trip == d->hot_trip_id)
0090         *type = THERMAL_TRIP_HOT;
0091     else if (trip == d->psv_trip_id)
0092         *type = THERMAL_TRIP_PASSIVE;
0093     else {
0094         for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
0095             if (d->act_trips[i].valid &&
0096                 d->act_trips[i].id == trip) {
0097                 *type = THERMAL_TRIP_ACTIVE;
0098                 break;
0099             }
0100         }
0101         if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT)
0102             return -EINVAL;
0103     }
0104 
0105     return 0;
0106 }
0107 
0108 static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone,
0109                       int trip, int temp)
0110 {
0111     struct int34x_thermal_zone *d = zone->devdata;
0112     acpi_status status;
0113     char name[10];
0114 
0115     if (d->override_ops && d->override_ops->set_trip_temp)
0116         return d->override_ops->set_trip_temp(zone, trip, temp);
0117 
0118     snprintf(name, sizeof(name), "PAT%d", trip);
0119     status = acpi_execute_simple_method(d->adev->handle, name,
0120             millicelsius_to_deci_kelvin(temp));
0121     if (ACPI_FAILURE(status))
0122         return -EIO;
0123 
0124     d->aux_trips[trip] = temp;
0125 
0126     return 0;
0127 }
0128 
0129 
0130 static int int340x_thermal_get_trip_hyst(struct thermal_zone_device *zone,
0131         int trip, int *temp)
0132 {
0133     struct int34x_thermal_zone *d = zone->devdata;
0134     acpi_status status;
0135     unsigned long long hyst;
0136 
0137     if (d->override_ops && d->override_ops->get_trip_hyst)
0138         return d->override_ops->get_trip_hyst(zone, trip, temp);
0139 
0140     status = acpi_evaluate_integer(d->adev->handle, "GTSH", NULL, &hyst);
0141     if (ACPI_FAILURE(status))
0142         *temp = 0;
0143     else
0144         *temp = hyst * 100;
0145 
0146     return 0;
0147 }
0148 
0149 static void int340x_thermal_critical(struct thermal_zone_device *zone)
0150 {
0151     dev_dbg(&zone->device, "%s: critical temperature reached\n", zone->type);
0152 }
0153 
0154 static struct thermal_zone_device_ops int340x_thermal_zone_ops = {
0155     .get_temp       = int340x_thermal_get_zone_temp,
0156     .get_trip_temp  = int340x_thermal_get_trip_temp,
0157     .get_trip_type  = int340x_thermal_get_trip_type,
0158     .set_trip_temp  = int340x_thermal_set_trip_temp,
0159     .get_trip_hyst =  int340x_thermal_get_trip_hyst,
0160     .critical   = int340x_thermal_critical,
0161 };
0162 
0163 static int int340x_thermal_get_trip_config(acpi_handle handle, char *name,
0164                       int *temp)
0165 {
0166     unsigned long long r;
0167     acpi_status status;
0168 
0169     status = acpi_evaluate_integer(handle, name, NULL, &r);
0170     if (ACPI_FAILURE(status))
0171         return -EIO;
0172 
0173     *temp = deci_kelvin_to_millicelsius(r);
0174 
0175     return 0;
0176 }
0177 
0178 int int340x_thermal_read_trips(struct int34x_thermal_zone *int34x_zone)
0179 {
0180     int trip_cnt = int34x_zone->aux_trip_nr;
0181     int i;
0182 
0183     int34x_zone->crt_trip_id = -1;
0184     if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_CRT",
0185                          &int34x_zone->crt_temp))
0186         int34x_zone->crt_trip_id = trip_cnt++;
0187 
0188     int34x_zone->hot_trip_id = -1;
0189     if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_HOT",
0190                          &int34x_zone->hot_temp))
0191         int34x_zone->hot_trip_id = trip_cnt++;
0192 
0193     int34x_zone->psv_trip_id = -1;
0194     if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_PSV",
0195                          &int34x_zone->psv_temp))
0196         int34x_zone->psv_trip_id = trip_cnt++;
0197 
0198     for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
0199         char name[5] = { '_', 'A', 'C', '0' + i, '\0' };
0200 
0201         if (int340x_thermal_get_trip_config(int34x_zone->adev->handle,
0202                     name,
0203                     &int34x_zone->act_trips[i].temp))
0204             break;
0205 
0206         int34x_zone->act_trips[i].id = trip_cnt++;
0207         int34x_zone->act_trips[i].valid = true;
0208     }
0209 
0210     return trip_cnt;
0211 }
0212 EXPORT_SYMBOL_GPL(int340x_thermal_read_trips);
0213 
0214 static struct thermal_zone_params int340x_thermal_params = {
0215     .governor_name = "user_space",
0216     .no_hwmon = true,
0217 };
0218 
0219 struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
0220                 struct thermal_zone_device_ops *override_ops)
0221 {
0222     struct int34x_thermal_zone *int34x_thermal_zone;
0223     acpi_status status;
0224     unsigned long long trip_cnt;
0225     int trip_mask = 0;
0226     int ret;
0227 
0228     int34x_thermal_zone = kzalloc(sizeof(*int34x_thermal_zone),
0229                       GFP_KERNEL);
0230     if (!int34x_thermal_zone)
0231         return ERR_PTR(-ENOMEM);
0232 
0233     int34x_thermal_zone->adev = adev;
0234     int34x_thermal_zone->override_ops = override_ops;
0235 
0236     status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt);
0237     if (ACPI_FAILURE(status))
0238         trip_cnt = 0;
0239     else {
0240         int i;
0241 
0242         int34x_thermal_zone->aux_trips =
0243             kcalloc(trip_cnt,
0244                 sizeof(*int34x_thermal_zone->aux_trips),
0245                 GFP_KERNEL);
0246         if (!int34x_thermal_zone->aux_trips) {
0247             ret = -ENOMEM;
0248             goto err_trip_alloc;
0249         }
0250         trip_mask = BIT(trip_cnt) - 1;
0251         int34x_thermal_zone->aux_trip_nr = trip_cnt;
0252         for (i = 0; i < trip_cnt; ++i)
0253             int34x_thermal_zone->aux_trips[i] = THERMAL_TEMP_INVALID;
0254     }
0255 
0256     trip_cnt = int340x_thermal_read_trips(int34x_thermal_zone);
0257 
0258     int34x_thermal_zone->lpat_table = acpi_lpat_get_conversion_table(
0259                                 adev->handle);
0260 
0261     int34x_thermal_zone->zone = thermal_zone_device_register(
0262                         acpi_device_bid(adev),
0263                         trip_cnt,
0264                         trip_mask, int34x_thermal_zone,
0265                         &int340x_thermal_zone_ops,
0266                         &int340x_thermal_params,
0267                         0, 0);
0268     if (IS_ERR(int34x_thermal_zone->zone)) {
0269         ret = PTR_ERR(int34x_thermal_zone->zone);
0270         goto err_thermal_zone;
0271     }
0272     ret = thermal_zone_device_enable(int34x_thermal_zone->zone);
0273     if (ret)
0274         goto err_enable;
0275 
0276     return int34x_thermal_zone;
0277 
0278 err_enable:
0279     thermal_zone_device_unregister(int34x_thermal_zone->zone);
0280 err_thermal_zone:
0281     acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
0282     kfree(int34x_thermal_zone->aux_trips);
0283 err_trip_alloc:
0284     kfree(int34x_thermal_zone);
0285     return ERR_PTR(ret);
0286 }
0287 EXPORT_SYMBOL_GPL(int340x_thermal_zone_add);
0288 
0289 void int340x_thermal_zone_remove(struct int34x_thermal_zone
0290                  *int34x_thermal_zone)
0291 {
0292     thermal_zone_device_unregister(int34x_thermal_zone->zone);
0293     acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
0294     kfree(int34x_thermal_zone->aux_trips);
0295     kfree(int34x_thermal_zone);
0296 }
0297 EXPORT_SYMBOL_GPL(int340x_thermal_zone_remove);
0298 
0299 MODULE_AUTHOR("Aaron Lu <aaron.lu@intel.com>");
0300 MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
0301 MODULE_DESCRIPTION("Intel INT340x common thermal zone handler");
0302 MODULE_LICENSE("GPL v2");