xref: /openbmc/linux/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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