|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * Copyright 2023 Linaro Limited | 
|  | * Copyright 2023 Intel Corporation | 
|  | * | 
|  | * Library routines for retrieving trip point temperature values from the | 
|  | * platform firmware via ACPI. | 
|  | */ | 
|  | #include <linux/acpi.h> | 
|  | #include <linux/units.h> | 
|  | #include <linux/thermal.h> | 
|  | #include "internal.h" | 
|  |  | 
|  | /* | 
|  | * Minimum temperature for full military grade is 218°K (-55°C) and | 
|  | * max temperature is 448°K (175°C). We can consider those values as | 
|  | * the boundaries for the [trips] temperature returned by the | 
|  | * firmware. Any values out of these boundaries may be considered | 
|  | * bogus and we can assume the firmware has no data to provide. | 
|  | */ | 
|  | #define TEMP_MIN_DECIK	2180ULL | 
|  | #define TEMP_MAX_DECIK	4480ULL | 
|  |  | 
|  | static int acpi_trip_temp(struct acpi_device *adev, char *obj_name, | 
|  | int *ret_temp) | 
|  | { | 
|  | unsigned long long temp; | 
|  | acpi_status status; | 
|  |  | 
|  | status = acpi_evaluate_integer(adev->handle, obj_name, NULL, &temp); | 
|  | if (ACPI_FAILURE(status)) { | 
|  | acpi_handle_debug(adev->handle, "%s evaluation failed\n", obj_name); | 
|  | return -ENODATA; | 
|  | } | 
|  |  | 
|  | if (temp >= TEMP_MIN_DECIK && temp <= TEMP_MAX_DECIK) { | 
|  | *ret_temp = temp; | 
|  | } else { | 
|  | acpi_handle_debug(adev->handle, "%s result %llu out of range\n", | 
|  | obj_name, temp); | 
|  | *ret_temp = THERMAL_TEMP_INVALID; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int acpi_active_trip_temp(struct acpi_device *adev, int id, int *ret_temp) | 
|  | { | 
|  | char obj_name[] = {'_', 'A', 'C', '0' + id, '\0'}; | 
|  |  | 
|  | if (id < 0 || id > 9) | 
|  | return -EINVAL; | 
|  |  | 
|  | return acpi_trip_temp(adev, obj_name, ret_temp); | 
|  | } | 
|  | EXPORT_SYMBOL_NS_GPL(acpi_active_trip_temp, ACPI_THERMAL); | 
|  |  | 
|  | int acpi_passive_trip_temp(struct acpi_device *adev, int *ret_temp) | 
|  | { | 
|  | return acpi_trip_temp(adev, "_PSV", ret_temp); | 
|  | } | 
|  | EXPORT_SYMBOL_NS_GPL(acpi_passive_trip_temp, ACPI_THERMAL); | 
|  |  | 
|  | int acpi_hot_trip_temp(struct acpi_device *adev, int *ret_temp) | 
|  | { | 
|  | return acpi_trip_temp(adev, "_HOT", ret_temp); | 
|  | } | 
|  | EXPORT_SYMBOL_NS_GPL(acpi_hot_trip_temp, ACPI_THERMAL); | 
|  |  | 
|  | int acpi_critical_trip_temp(struct acpi_device *adev, int *ret_temp) | 
|  | { | 
|  | return acpi_trip_temp(adev, "_CRT", ret_temp); | 
|  | } | 
|  | EXPORT_SYMBOL_NS_GPL(acpi_critical_trip_temp, ACPI_THERMAL); | 
|  |  | 
|  | static int thermal_temp(int error, int temp_decik, int *ret_temp) | 
|  | { | 
|  | if (error) | 
|  | return error; | 
|  |  | 
|  | if (temp_decik == THERMAL_TEMP_INVALID) | 
|  | *ret_temp = THERMAL_TEMP_INVALID; | 
|  | else | 
|  | *ret_temp = deci_kelvin_to_millicelsius(temp_decik); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * thermal_acpi_active_trip_temp - Retrieve active trip point temperature | 
|  | * @adev: Target thermal zone ACPI device object. | 
|  | * @id: Active cooling level (0 - 9). | 
|  | * @ret_temp: Address to store the retrieved temperature value on success. | 
|  | * | 
|  | * Evaluate the _ACx object for the thermal zone represented by @adev to obtain | 
|  | * the temperature of the active cooling trip point corresponding to the active | 
|  | * cooling level given by @id. | 
|  | * | 
|  | * Return 0 on success or a negative error value on failure. | 
|  | */ | 
|  | int thermal_acpi_active_trip_temp(struct acpi_device *adev, int id, int *ret_temp) | 
|  | { | 
|  | int temp_decik = 0; | 
|  | int ret = acpi_active_trip_temp(adev, id, &temp_decik); | 
|  |  | 
|  | return thermal_temp(ret, temp_decik, ret_temp); | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(thermal_acpi_active_trip_temp); | 
|  |  | 
|  | /** | 
|  | * thermal_acpi_passive_trip_temp - Retrieve passive trip point temperature | 
|  | * @adev: Target thermal zone ACPI device object. | 
|  | * @ret_temp: Address to store the retrieved temperature value on success. | 
|  | * | 
|  | * Evaluate the _PSV object for the thermal zone represented by @adev to obtain | 
|  | * the temperature of the passive cooling trip point. | 
|  | * | 
|  | * Return 0 on success or -ENODATA on failure. | 
|  | */ | 
|  | int thermal_acpi_passive_trip_temp(struct acpi_device *adev, int *ret_temp) | 
|  | { | 
|  | int temp_decik = 0; | 
|  | int ret = acpi_passive_trip_temp(adev, &temp_decik); | 
|  |  | 
|  | return thermal_temp(ret, temp_decik, ret_temp); | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(thermal_acpi_passive_trip_temp); | 
|  |  | 
|  | /** | 
|  | * thermal_acpi_hot_trip_temp - Retrieve hot trip point temperature | 
|  | * @adev: Target thermal zone ACPI device object. | 
|  | * @ret_temp: Address to store the retrieved temperature value on success. | 
|  | * | 
|  | * Evaluate the _HOT object for the thermal zone represented by @adev to obtain | 
|  | * the temperature of the trip point at which the system is expected to be put | 
|  | * into the S4 sleep state. | 
|  | * | 
|  | * Return 0 on success or -ENODATA on failure. | 
|  | */ | 
|  | int thermal_acpi_hot_trip_temp(struct acpi_device *adev, int *ret_temp) | 
|  | { | 
|  | int temp_decik = 0; | 
|  | int ret = acpi_hot_trip_temp(adev, &temp_decik); | 
|  |  | 
|  | return thermal_temp(ret, temp_decik, ret_temp); | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(thermal_acpi_hot_trip_temp); | 
|  |  | 
|  | /** | 
|  | * thermal_acpi_critical_trip_temp - Retrieve critical trip point temperature | 
|  | * @adev: Target thermal zone ACPI device object. | 
|  | * @ret_temp: Address to store the retrieved temperature value on success. | 
|  | * | 
|  | * Evaluate the _CRT object for the thermal zone represented by @adev to obtain | 
|  | * the temperature of the critical cooling trip point. | 
|  | * | 
|  | * Return 0 on success or -ENODATA on failure. | 
|  | */ | 
|  | int thermal_acpi_critical_trip_temp(struct acpi_device *adev, int *ret_temp) | 
|  | { | 
|  | int temp_decik = 0; | 
|  | int ret = acpi_critical_trip_temp(adev, &temp_decik); | 
|  |  | 
|  | return thermal_temp(ret, temp_decik, ret_temp); | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(thermal_acpi_critical_trip_temp); |