12025cf9eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
23e8c4d31SAmit Kucheria /*
33e8c4d31SAmit Kucheria * int340x_thermal_zone.c
43e8c4d31SAmit Kucheria * Copyright (c) 2015, Intel Corporation.
53e8c4d31SAmit Kucheria */
63e8c4d31SAmit Kucheria #include <linux/kernel.h>
73e8c4d31SAmit Kucheria #include <linux/module.h>
83e8c4d31SAmit Kucheria #include <linux/init.h>
93e8c4d31SAmit Kucheria #include <linux/acpi.h>
103e8c4d31SAmit Kucheria #include <linux/thermal.h>
11006a6021SAkinobu Mita #include <linux/units.h>
123e8c4d31SAmit Kucheria #include "int340x_thermal_zone.h"
133e8c4d31SAmit Kucheria
int340x_thermal_get_zone_temp(struct thermal_zone_device * zone,int * temp)143e8c4d31SAmit Kucheria static int int340x_thermal_get_zone_temp(struct thermal_zone_device *zone,
153e8c4d31SAmit Kucheria int *temp)
163e8c4d31SAmit Kucheria {
17*5f68d078SDaniel Lezcano struct int34x_thermal_zone *d = thermal_zone_device_priv(zone);
183e8c4d31SAmit Kucheria unsigned long long tmp;
193e8c4d31SAmit Kucheria acpi_status status;
203e8c4d31SAmit Kucheria
213e8c4d31SAmit Kucheria status = acpi_evaluate_integer(d->adev->handle, "_TMP", NULL, &tmp);
223e8c4d31SAmit Kucheria if (ACPI_FAILURE(status))
233e8c4d31SAmit Kucheria return -EIO;
243e8c4d31SAmit Kucheria
253e8c4d31SAmit Kucheria if (d->lpat_table) {
263e8c4d31SAmit Kucheria int conv_temp;
273e8c4d31SAmit Kucheria
283e8c4d31SAmit Kucheria conv_temp = acpi_lpat_raw_to_temp(d->lpat_table, (int)tmp);
293e8c4d31SAmit Kucheria if (conv_temp < 0)
303e8c4d31SAmit Kucheria return conv_temp;
313e8c4d31SAmit Kucheria
32d0009d14SRafael J. Wysocki *temp = conv_temp * 10;
33be014c78SRafael J. Wysocki } else {
343e8c4d31SAmit Kucheria /* _TMP returns the temperature in tenths of degrees Kelvin */
35006a6021SAkinobu Mita *temp = deci_kelvin_to_millicelsius(tmp);
36be014c78SRafael J. Wysocki }
373e8c4d31SAmit Kucheria
383e8c4d31SAmit Kucheria return 0;
393e8c4d31SAmit Kucheria }
403e8c4d31SAmit Kucheria
int340x_thermal_set_trip_temp(struct thermal_zone_device * zone,int trip,int temp)413e8c4d31SAmit Kucheria static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone,
423e8c4d31SAmit Kucheria int trip, int temp)
433e8c4d31SAmit Kucheria {
44*5f68d078SDaniel Lezcano struct int34x_thermal_zone *d = thermal_zone_device_priv(zone);
451bcebcabSRafael J. Wysocki char name[] = {'P', 'A', 'T', '0' + trip, '\0'};
463e8c4d31SAmit Kucheria acpi_status status;
473e8c4d31SAmit Kucheria
481bcebcabSRafael J. Wysocki if (trip > 9)
491bcebcabSRafael J. Wysocki return -EINVAL;
501bcebcabSRafael J. Wysocki
513e8c4d31SAmit Kucheria status = acpi_execute_simple_method(d->adev->handle, name,
52006a6021SAkinobu Mita millicelsius_to_deci_kelvin(temp));
533e8c4d31SAmit Kucheria if (ACPI_FAILURE(status))
543e8c4d31SAmit Kucheria return -EIO;
553e8c4d31SAmit Kucheria
563e8c4d31SAmit Kucheria return 0;
573e8c4d31SAmit Kucheria }
583e8c4d31SAmit Kucheria
int340x_thermal_critical(struct thermal_zone_device * zone)59dd47366aSKai-Heng Feng static void int340x_thermal_critical(struct thermal_zone_device *zone)
60dd47366aSKai-Heng Feng {
61dd47366aSKai-Heng Feng dev_dbg(&zone->device, "%s: critical temperature reached\n", zone->type);
62dd47366aSKai-Heng Feng }
63dd47366aSKai-Heng Feng
643e8c4d31SAmit Kucheria static struct thermal_zone_device_ops int340x_thermal_zone_ops = {
653e8c4d31SAmit Kucheria .get_temp = int340x_thermal_get_zone_temp,
663e8c4d31SAmit Kucheria .set_trip_temp = int340x_thermal_set_trip_temp,
67dd47366aSKai-Heng Feng .critical = int340x_thermal_critical,
683e8c4d31SAmit Kucheria };
693e8c4d31SAmit Kucheria
int340x_thermal_read_trips(struct acpi_device * zone_adev,struct thermal_trip * zone_trips,int trip_cnt)70f4118dbeSRafael J. Wysocki static int int340x_thermal_read_trips(struct acpi_device *zone_adev,
71f4118dbeSRafael J. Wysocki struct thermal_trip *zone_trips,
72f4118dbeSRafael J. Wysocki int trip_cnt)
733e8c4d31SAmit Kucheria {
74f4118dbeSRafael J. Wysocki int i, ret;
753e8c4d31SAmit Kucheria
76dd3b3d16SRafael J. Wysocki ret = thermal_acpi_critical_trip_temp(zone_adev,
77dd3b3d16SRafael J. Wysocki &zone_trips[trip_cnt].temperature);
78dd3b3d16SRafael J. Wysocki if (!ret) {
79dd3b3d16SRafael J. Wysocki zone_trips[trip_cnt].type = THERMAL_TRIP_CRITICAL;
80f4118dbeSRafael J. Wysocki trip_cnt++;
81dd3b3d16SRafael J. Wysocki }
823e8c4d31SAmit Kucheria
83dd3b3d16SRafael J. Wysocki ret = thermal_acpi_hot_trip_temp(zone_adev,
84dd3b3d16SRafael J. Wysocki &zone_trips[trip_cnt].temperature);
85dd3b3d16SRafael J. Wysocki if (!ret) {
86dd3b3d16SRafael J. Wysocki zone_trips[trip_cnt].type = THERMAL_TRIP_HOT;
87f4118dbeSRafael J. Wysocki trip_cnt++;
88dd3b3d16SRafael J. Wysocki }
893e8c4d31SAmit Kucheria
90dd3b3d16SRafael J. Wysocki ret = thermal_acpi_passive_trip_temp(zone_adev,
91dd3b3d16SRafael J. Wysocki &zone_trips[trip_cnt].temperature);
92dd3b3d16SRafael J. Wysocki if (!ret) {
93dd3b3d16SRafael J. Wysocki zone_trips[trip_cnt].type = THERMAL_TRIP_PASSIVE;
94f4118dbeSRafael J. Wysocki trip_cnt++;
95dd3b3d16SRafael J. Wysocki }
963e8c4d31SAmit Kucheria
973e8c4d31SAmit Kucheria for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
98dd3b3d16SRafael J. Wysocki ret = thermal_acpi_active_trip_temp(zone_adev, i,
99dd3b3d16SRafael J. Wysocki &zone_trips[trip_cnt].temperature);
100f4118dbeSRafael J. Wysocki if (ret)
1013e8c4d31SAmit Kucheria break;
1023e8c4d31SAmit Kucheria
103dd3b3d16SRafael J. Wysocki zone_trips[trip_cnt].type = THERMAL_TRIP_ACTIVE;
104f4118dbeSRafael J. Wysocki trip_cnt++;
1053e8c4d31SAmit Kucheria }
1063e8c4d31SAmit Kucheria
1073e8c4d31SAmit Kucheria return trip_cnt;
1083e8c4d31SAmit Kucheria }
1093e8c4d31SAmit Kucheria
1103e8c4d31SAmit Kucheria static struct thermal_zone_params int340x_thermal_params = {
1113e8c4d31SAmit Kucheria .governor_name = "user_space",
1123e8c4d31SAmit Kucheria .no_hwmon = true,
1133e8c4d31SAmit Kucheria };
1143e8c4d31SAmit Kucheria
int340x_thermal_zone_add(struct acpi_device * adev,int (* get_temp)(struct thermal_zone_device *,int *))1153e8c4d31SAmit Kucheria struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
1160d2d586aSDaniel Lezcano int (*get_temp) (struct thermal_zone_device *, int *))
1173e8c4d31SAmit Kucheria {
11867c69458SRafael J. Wysocki struct int34x_thermal_zone *int34x_zone;
119f4118dbeSRafael J. Wysocki struct thermal_trip *zone_trips;
120f4118dbeSRafael J. Wysocki unsigned long long trip_cnt = 0;
121f4118dbeSRafael J. Wysocki unsigned long long hyst;
1223e8c4d31SAmit Kucheria int trip_mask = 0;
123f4118dbeSRafael J. Wysocki acpi_status status;
124f4118dbeSRafael J. Wysocki int i, ret;
1253e8c4d31SAmit Kucheria
12667c69458SRafael J. Wysocki int34x_zone = kzalloc(sizeof(*int34x_zone), GFP_KERNEL);
12767c69458SRafael J. Wysocki if (!int34x_zone)
1283e8c4d31SAmit Kucheria return ERR_PTR(-ENOMEM);
1293e8c4d31SAmit Kucheria
13067c69458SRafael J. Wysocki int34x_zone->adev = adev;
1310d2d586aSDaniel Lezcano
13267c69458SRafael J. Wysocki int34x_zone->ops = kmemdup(&int340x_thermal_zone_ops,
1330d2d586aSDaniel Lezcano sizeof(int340x_thermal_zone_ops), GFP_KERNEL);
13467c69458SRafael J. Wysocki if (!int34x_zone->ops) {
1350d2d586aSDaniel Lezcano ret = -ENOMEM;
1360d2d586aSDaniel Lezcano goto err_ops_alloc;
1370d2d586aSDaniel Lezcano }
1380d2d586aSDaniel Lezcano
1390d2d586aSDaniel Lezcano if (get_temp)
14067c69458SRafael J. Wysocki int34x_zone->ops->get_temp = get_temp;
1413e8c4d31SAmit Kucheria
1423e8c4d31SAmit Kucheria status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt);
143be014c78SRafael J. Wysocki if (ACPI_SUCCESS(status)) {
14467c69458SRafael J. Wysocki int34x_zone->aux_trip_nr = trip_cnt;
145f4118dbeSRafael J. Wysocki trip_mask = BIT(trip_cnt) - 1;
1463e8c4d31SAmit Kucheria }
1473e8c4d31SAmit Kucheria
148f4118dbeSRafael J. Wysocki zone_trips = kzalloc(sizeof(*zone_trips) * (trip_cnt + INT340X_THERMAL_MAX_TRIP_COUNT),
149f4118dbeSRafael J. Wysocki GFP_KERNEL);
150f4118dbeSRafael J. Wysocki if (!zone_trips) {
151f4118dbeSRafael J. Wysocki ret = -ENOMEM;
152f4118dbeSRafael J. Wysocki goto err_trips_alloc;
153f4118dbeSRafael J. Wysocki }
154f4118dbeSRafael J. Wysocki
155f4118dbeSRafael J. Wysocki for (i = 0; i < trip_cnt; i++) {
156f4118dbeSRafael J. Wysocki zone_trips[i].type = THERMAL_TRIP_PASSIVE;
157f4118dbeSRafael J. Wysocki zone_trips[i].temperature = THERMAL_TEMP_INVALID;
158f4118dbeSRafael J. Wysocki }
159f4118dbeSRafael J. Wysocki
160f4118dbeSRafael J. Wysocki trip_cnt = int340x_thermal_read_trips(adev, zone_trips, trip_cnt);
161f4118dbeSRafael J. Wysocki
162f4118dbeSRafael J. Wysocki status = acpi_evaluate_integer(adev->handle, "GTSH", NULL, &hyst);
163f4118dbeSRafael J. Wysocki if (ACPI_SUCCESS(status))
164f4118dbeSRafael J. Wysocki hyst *= 100;
165f4118dbeSRafael J. Wysocki else
166f4118dbeSRafael J. Wysocki hyst = 0;
167f4118dbeSRafael J. Wysocki
168f4118dbeSRafael J. Wysocki for (i = 0; i < trip_cnt; ++i)
169f4118dbeSRafael J. Wysocki zone_trips[i].hysteresis = hyst;
170f4118dbeSRafael J. Wysocki
17167c69458SRafael J. Wysocki int34x_zone->trips = zone_trips;
1723e8c4d31SAmit Kucheria
17367c69458SRafael J. Wysocki int34x_zone->lpat_table = acpi_lpat_get_conversion_table(adev->handle);
1743e8c4d31SAmit Kucheria
17567c69458SRafael J. Wysocki int34x_zone->zone = thermal_zone_device_register_with_trips(
1763e8c4d31SAmit Kucheria acpi_device_bid(adev),
177f4118dbeSRafael J. Wysocki zone_trips, trip_cnt,
17867c69458SRafael J. Wysocki trip_mask, int34x_zone,
17967c69458SRafael J. Wysocki int34x_zone->ops,
1803e8c4d31SAmit Kucheria &int340x_thermal_params,
1813e8c4d31SAmit Kucheria 0, 0);
18267c69458SRafael J. Wysocki if (IS_ERR(int34x_zone->zone)) {
18367c69458SRafael J. Wysocki ret = PTR_ERR(int34x_zone->zone);
1843e8c4d31SAmit Kucheria goto err_thermal_zone;
1853e8c4d31SAmit Kucheria }
18667c69458SRafael J. Wysocki ret = thermal_zone_device_enable(int34x_zone->zone);
187bbcf90c0SAndrzej Pietrasiewicz if (ret)
188bbcf90c0SAndrzej Pietrasiewicz goto err_enable;
1893e8c4d31SAmit Kucheria
19067c69458SRafael J. Wysocki return int34x_zone;
1913e8c4d31SAmit Kucheria
192bbcf90c0SAndrzej Pietrasiewicz err_enable:
19367c69458SRafael J. Wysocki thermal_zone_device_unregister(int34x_zone->zone);
1943e8c4d31SAmit Kucheria err_thermal_zone:
19567c69458SRafael J. Wysocki kfree(int34x_zone->trips);
19667c69458SRafael J. Wysocki acpi_lpat_free_conversion_table(int34x_zone->lpat_table);
197f4118dbeSRafael J. Wysocki err_trips_alloc:
19867c69458SRafael J. Wysocki kfree(int34x_zone->ops);
1990d2d586aSDaniel Lezcano err_ops_alloc:
20067c69458SRafael J. Wysocki kfree(int34x_zone);
2013e8c4d31SAmit Kucheria return ERR_PTR(ret);
2023e8c4d31SAmit Kucheria }
2033e8c4d31SAmit Kucheria EXPORT_SYMBOL_GPL(int340x_thermal_zone_add);
2043e8c4d31SAmit Kucheria
int340x_thermal_zone_remove(struct int34x_thermal_zone * int34x_zone)20567c69458SRafael J. Wysocki void int340x_thermal_zone_remove(struct int34x_thermal_zone *int34x_zone)
2063e8c4d31SAmit Kucheria {
20767c69458SRafael J. Wysocki thermal_zone_device_unregister(int34x_zone->zone);
20867c69458SRafael J. Wysocki acpi_lpat_free_conversion_table(int34x_zone->lpat_table);
20967c69458SRafael J. Wysocki kfree(int34x_zone->trips);
21067c69458SRafael J. Wysocki kfree(int34x_zone->ops);
21167c69458SRafael J. Wysocki kfree(int34x_zone);
2123e8c4d31SAmit Kucheria }
2133e8c4d31SAmit Kucheria EXPORT_SYMBOL_GPL(int340x_thermal_zone_remove);
2143e8c4d31SAmit Kucheria
int340x_thermal_update_trips(struct int34x_thermal_zone * int34x_zone)215b1bf9dbfSRafael J. Wysocki void int340x_thermal_update_trips(struct int34x_thermal_zone *int34x_zone)
216b1bf9dbfSRafael J. Wysocki {
217f4118dbeSRafael J. Wysocki struct acpi_device *zone_adev = int34x_zone->adev;
218f4118dbeSRafael J. Wysocki struct thermal_trip *zone_trips = int34x_zone->trips;
219f4118dbeSRafael J. Wysocki int trip_cnt = int34x_zone->zone->num_trips;
220f4118dbeSRafael J. Wysocki int act_trip_nr = 0;
221f4118dbeSRafael J. Wysocki int i;
222b1bf9dbfSRafael J. Wysocki
2239e9b7e18SRafael J. Wysocki mutex_lock(&int34x_zone->zone->lock);
224b1bf9dbfSRafael J. Wysocki
225f4118dbeSRafael J. Wysocki for (i = int34x_zone->aux_trip_nr; i < trip_cnt; i++) {
226dd3b3d16SRafael J. Wysocki int temp, err;
227b1bf9dbfSRafael J. Wysocki
228f4118dbeSRafael J. Wysocki switch (zone_trips[i].type) {
229f4118dbeSRafael J. Wysocki case THERMAL_TRIP_CRITICAL:
230dd3b3d16SRafael J. Wysocki err = thermal_acpi_critical_trip_temp(zone_adev, &temp);
231b1bf9dbfSRafael J. Wysocki break;
232f4118dbeSRafael J. Wysocki case THERMAL_TRIP_HOT:
233dd3b3d16SRafael J. Wysocki err = thermal_acpi_hot_trip_temp(zone_adev, &temp);
234f4118dbeSRafael J. Wysocki break;
235f4118dbeSRafael J. Wysocki case THERMAL_TRIP_PASSIVE:
236dd3b3d16SRafael J. Wysocki err = thermal_acpi_passive_trip_temp(zone_adev, &temp);
237f4118dbeSRafael J. Wysocki break;
238f4118dbeSRafael J. Wysocki case THERMAL_TRIP_ACTIVE:
239dd3b3d16SRafael J. Wysocki err = thermal_acpi_active_trip_temp(zone_adev, act_trip_nr++,
240dd3b3d16SRafael J. Wysocki &temp);
241f4118dbeSRafael J. Wysocki break;
242f4118dbeSRafael J. Wysocki default:
243f4118dbeSRafael J. Wysocki err = -ENODEV;
244f4118dbeSRafael J. Wysocki }
245f4118dbeSRafael J. Wysocki if (err) {
246f4118dbeSRafael J. Wysocki zone_trips[i].temperature = THERMAL_TEMP_INVALID;
247f4118dbeSRafael J. Wysocki continue;
248f4118dbeSRafael J. Wysocki }
249b1bf9dbfSRafael J. Wysocki
250dd3b3d16SRafael J. Wysocki zone_trips[i].temperature = temp;
251b1bf9dbfSRafael J. Wysocki }
252b1bf9dbfSRafael J. Wysocki
2539e9b7e18SRafael J. Wysocki mutex_unlock(&int34x_zone->zone->lock);
254b1bf9dbfSRafael J. Wysocki }
255b1bf9dbfSRafael J. Wysocki EXPORT_SYMBOL_GPL(int340x_thermal_update_trips);
256b1bf9dbfSRafael J. Wysocki
2573e8c4d31SAmit Kucheria MODULE_AUTHOR("Aaron Lu <aaron.lu@intel.com>");
2583e8c4d31SAmit Kucheria MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
2593e8c4d31SAmit Kucheria MODULE_DESCRIPTION("Intel INT340x common thermal zone handler");
2603e8c4d31SAmit Kucheria MODULE_LICENSE("GPL v2");
261