@@ -76,6 +76,19 @@ config THERMAL_OF
Say 'Y' here if you need to build thermal infrastructure
based on device tree.
+config THERMAL_ACPI
+ bool
+ prompt "APIs to parse thermal data out of the ACPI tables"
+ depends on ACPI_THERMAL
+ default y
+ help
+ This options provides helpers to add the support to
+ read and parse thermal data definitions out of the
+ ACPI tables blob.
+
+ Say 'Y' here if you need to build thermal infrastructure
+ based on ACPI.
+
config THERMAL_WRITABLE_TRIPS
bool "Enable writable trip points"
help
@@ -13,6 +13,7 @@ thermal_sys-$(CONFIG_THERMAL_NETLINK) += thermal_netlink.o
# interface to/from other layers providing sensors
thermal_sys-$(CONFIG_THERMAL_HWMON) += thermal_hwmon.o
thermal_sys-$(CONFIG_THERMAL_OF) += thermal_of.o
+thermal_sys-$(CONFIG_THERMAL_ACPI) += thermal_acpi.o
# governors
thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += gov_fair_share.o
new file mode 100644
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2022 Linaro Limited
+ *
+ * Author: Daniel Lezcano <daniel.lezcano@linaro.org>
+ *
+ * ACPI thermal configuration
+ */
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/units.h>
+#include <uapi/linux/thermal.h>
+
+#include "thermal_core.h"
+
+int thermal_acpi_trip_gtsh(struct acpi_device *adev)
+{
+ unsigned long long hyst;
+ acpi_status status;
+
+ status = acpi_evaluate_integer(adev->handle, "GTSH", NULL, &hyst);
+ if (ACPI_FAILURE(status))
+ return 0;
+
+ return (int)(hyst * 100);
+}
+EXPORT_SYMBOL_GPL(thermal_acpi_trip_gtsh);
+
+int thermal_acpi_get_tzd(struct acpi_device *adev, struct acpi_handle_list *devices)
+{
+ acpi_status status;
+
+ /*
+ * _TZD (Thermal zone device): This optional object evaluates
+ * to a package of device names. Each name corresponds to a
+ * device in the ACPI namespace that is associated with the
+ * thermal zone. The temperature reported by the thermal zone
+ * is roughly correspondent to that of each of the devices.
+ */
+ status = acpi_evaluate_reference(adev->handle, "_TZD", NULL, devices);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(thermal_acpi_get_tzd);
+
+int thermal_acpi_get_temp(struct acpi_device *adev, int *temperature)
+{
+ unsigned long long temp;
+ acpi_status status;
+
+ /*
+ * _TMP (Temperature): This control method returns the thermal zone’s
+ * current operating temperature. The return value is the current
+ * temperature of the thermal zone in tenths of degrees Kelvin
+ */
+ status = acpi_evaluate_integer(adev->handle, "_TMP", NULL, &temp);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ *temperature = deci_kelvin_to_millicelsius(temp);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(thermal_acpi_get_temp);
+
+int thermal_acpi_trip_crit(struct acpi_device *adev, struct thermal_trip *trip)
+{
+ unsigned long long temp;
+ acpi_status status;
+
+ /*
+ * _CRT (Critical temperature): This object, when defined under a thermal
+ * zone, returns the critical temperature at which OSPM must shutdown
+ * the system. If this object it present under a device, the device’s
+ * driver evaluates this object to determine the device’s critical cooling
+ * temperature trip point. This value may then be used by the device’s
+ * driver to program an internal device temperature sensor trip point
+ */
+ status = acpi_evaluate_integer(adev->handle, "_CRT", NULL, &temp);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ trip->hysteresis = thermal_acpi_trip_gtsh(adev);
+ trip->temperature = deci_kelvin_to_millicelsius(temp);
+ trip->type = THERMAL_TRIP_CRITICAL;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(thermal_acpi_trip_crit);
+
+int thermal_acpi_trip_hot(struct acpi_device *adev, struct thermal_trip *trip)
+{
+ unsigned long long temp;
+ acpi_status status;
+
+ /*
+ * _HOT (Hot Temperature): This optional object, when defined under a
+ * thermal zone, returns the critical temperature at which OSPM may
+ * choose to transition the system into the S4 sleeping state. The
+ * platform vendor should define _HOT to be far enough below _CRT so as
+ * to allow OSPM enough time to transition the system into the S4
+ * sleeping state. While dependent on the amount of installed memory,
+ * on typical platforms OSPM implementations can transition the system
+ * into the S4 sleeping state in tens of seconds. If this object it
+ * present under a device, the device’s driver evaluates this object to
+ * determine the device’s hot cooling temperature trip point. This value
+ * may then be used by the device’s driver to program an internal device
+ * temperature sensor trip point.
+ */
+ status = acpi_evaluate_integer(adev->handle, "_HOT", NULL, &temp);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ trip->hysteresis = thermal_acpi_trip_gtsh(adev);
+ trip->temperature = deci_kelvin_to_millicelsius(temp);
+ trip->type = THERMAL_TRIP_HOT;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(thermal_acpi_trip_hot);
+
+int thermal_acpi_trip_psv_psl(struct acpi_device *adev, struct acpi_handle_list *devices)
+{
+ acpi_status status;
+
+ /*
+ * _PSL (Passive List): This object is defined under a thermal zone and
+ * evaluates to a list of processor objects to be used for passive cooling
+ */
+ status = acpi_evaluate_reference(adev->handle, "_PSL", NULL, devices);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(thermal_acpi_trip_psv_psl);
+
+int thermal_acpi_trip_psv_tsp(struct acpi_device *adev)
+{
+ acpi_status status;
+ unsigned long long tsp;
+
+ /*
+ * _TSP (Thermal Sampling Period): This object evaluates to a thermal
+ * sampling period (in tenths of seconds) used by OSPM to implement the
+ * Passive cooling equation. This value, along with _TC1 and _TC2, will
+ * enable OSPM to provide the proper hysteresis required by the system
+ * to accomplish an effective passive cooling policy.
+ */
+ status = acpi_evaluate_integer(adev->handle, "_TSP", NULL, &tsp);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ return (int)tsp;
+}
+EXPORT_SYMBOL_GPL(thermal_acpi_trip_psv_tsp);
+
+int thermal_acpi_trip_psv_tc1(struct acpi_device *adev)
+{
+ acpi_status status;
+ unsigned long long tc1;
+
+ /*
+ * _TC1 (Thermal Constant 1): This object evaluates to the constant _TC1
+ * for use in the Passive cooling formula
+ */
+ status = acpi_evaluate_integer(adev->handle, "_TC1", NULL, &tc1);
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
+
+ return (int)tc1;
+}
+EXPORT_SYMBOL_GPL(thermal_acpi_trip_psv_tc1);
+
+int thermal_acpi_trip_psv_tc2(struct acpi_device *adev)
+{
+ acpi_status status;
+ unsigned long long tc2;
+
+ /*
+ * _TC2 (Thermal Constant 1): This object evaluates to the constant _TC2
+ * for use in the Passive cooling formula
+ */
+ status = acpi_evaluate_integer(adev->handle, "_TC2", NULL, &tc2);
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
+
+ return (int)tc2;
+}
+EXPORT_SYMBOL_GPL(thermal_acpi_trip_psv_tc2);
+
+int thermal_acpi_trip_psv(struct acpi_device *adev, struct thermal_trip *trip)
+{
+ unsigned long long temp;
+ acpi_status status;
+
+ /*
+ * _PSV (Passive): This optional object, if present under a thermal zone,
+ * evaluates to the temperature at which OSPM must activate passive
+ * cooling policy
+ */
+ status = acpi_evaluate_integer(adev->handle, "_PSV", NULL, &temp);
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
+
+ /*
+ * The _PSL, _TSP, _TC1 and _TC2 are required if the _PSV object exists.
+ * We assume the caller will raise an error if it was able to get the _PSV
+ * but then fail to get the other objects.
+ */
+ trip->hysteresis = thermal_acpi_trip_gtsh(adev);
+ trip->temperature = deci_kelvin_to_millicelsius(temp);
+ trip->type = THERMAL_TRIP_PASSIVE;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(thermal_acpi_trip_psv);
+
+int thermal_acpi_trip_acl(struct acpi_device *adev,
+ struct acpi_handle_list *devices, int id)
+{
+ acpi_status status;
+ char name[5];
+
+ /*
+ * _ALx: This object is defined under a thermal zone and evaluates to a
+ * list of Active cooling devices to be turned on when the corresponding
+ * _ACx temperature threshold is exceeded. For example, these devices
+ * could be fans.
+ */
+ sprintf(name, "_AL%d", id);
+
+ status = acpi_evaluate_reference(adev->handle, name, NULL, devices);
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(thermal_acpi_trip_acl);
+
+int thermal_acpi_trip_act(struct acpi_device *adev,
+ struct thermal_trip *trip, int id)
+{
+ acpi_status status;
+ unsigned long long temp;
+ char name[5];
+
+ /*
+ * _ACx: This optional object, if present under a thermal zone, returns
+ * the temperature trip point at which OSPM must start or stop active
+ * cooling, where x is a value between 0 and 9 that designates multiple
+ * active cooling levels of the thermal zone. If the Active cooling
+ * device has one cooling level (that is, “on”) then that cooling level
+ * must be defined as _AC0. If the cooling device has two levels of
+ * capability, such as a high fan speed and a low fan speed, then they
+ * must be defined as _AC0 and _AC1 respectively. The smaller the value
+ * of x, the greater the cooling strength _ACx represents. In the above
+ * example, _AC0 represents the greater level of cooling (the faster fan
+ * speed) and _AC1 represents the lesser level of cooling (the slower
+ * fan speed). For every _ACx method, there must be a matching _ALx
+ * object or a corresponding entry in an _ART object’s active cooling
+ * relationship list.
+ */
+ sprintf(name, "_AC%d", id);
+
+ status = acpi_evaluate_integer(adev->handle, name, NULL, &temp);
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
+
+ trip->hysteresis = thermal_acpi_trip_gtsh(adev);
+ trip->temperature = deci_kelvin_to_millicelsius(temp);
+ trip->type = THERMAL_TRIP_ACTIVE;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(thermal_acpi_trip_act);
@@ -334,6 +334,22 @@ static inline void devm_thermal_of_zone_unregister(struct device *dev,
}
#endif
+#ifdef CONFIG_THERMAL_ACPI
+struct acpi_handle_list;
+int thermal_acpi_get_temp(struct acpi_device *adev, int *temperature);
+int thermal_acpi_trip_crit(struct acpi_device *adev, struct thermal_trip *trip);
+int thermal_acpi_trip_hot(struct acpi_device *adev, struct thermal_trip *trip);
+int thermal_acpi_trip_psv(struct acpi_device *adev, struct thermal_trip *trip);
+int thermal_acpi_trip_act(struct acpi_device *adev, struct thermal_trip *trip, int id);
+int thermal_acpi_trip_acl(struct acpi_device *adev, struct acpi_handle_list *devices, int id);
+int thermal_acpi_trip_psv_psl(struct acpi_device *adev, struct acpi_handle_list *devices);
+int thermal_acpi_trip_psv_tsp(struct acpi_device *adev);
+int thermal_acpi_trip_psv_tc1(struct acpi_device *adev);
+int thermal_acpi_trip_psv_tc2(struct acpi_device *adev);
+int thermal_acpi_trip_gtsh(struct acpi_device *adev);
+int thermal_acpi_get_tzd(struct acpi_device *adev, struct acpi_handle_list *devices);
+#endif
+
int __thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id,
struct thermal_trip *trip);
int thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id,