summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/drivers/acpi/thermal_zone/Kconfig7
-rw-r--r--src/drivers/acpi/thermal_zone/Makefile.inc1
-rw-r--r--src/drivers/acpi/thermal_zone/chip.h63
-rw-r--r--src/drivers/acpi/thermal_zone/thermal_zone.c131
4 files changed, 202 insertions, 0 deletions
diff --git a/src/drivers/acpi/thermal_zone/Kconfig b/src/drivers/acpi/thermal_zone/Kconfig
new file mode 100644
index 0000000000..5deace4c50
--- /dev/null
+++ b/src/drivers/acpi/thermal_zone/Kconfig
@@ -0,0 +1,7 @@
+config DRIVERS_ACPI_THERMAL_ZONE
+ bool
+ default n
+ depends on HAVE_ACPI_TABLES
+ help
+ Adds a chip driver that generates ACPI ThermalZones. See the chapter
+ on Thermal Management in the ACPI specification.
diff --git a/src/drivers/acpi/thermal_zone/Makefile.inc b/src/drivers/acpi/thermal_zone/Makefile.inc
new file mode 100644
index 0000000000..0a15b91667
--- /dev/null
+++ b/src/drivers/acpi/thermal_zone/Makefile.inc
@@ -0,0 +1 @@
+ramstage-$(CONFIG_DRIVERS_ACPI_THERMAL_ZONE) += thermal_zone.c
diff --git a/src/drivers/acpi/thermal_zone/chip.h b/src/drivers/acpi/thermal_zone/chip.h
new file mode 100644
index 0000000000..5792e4c961
--- /dev/null
+++ b/src/drivers/acpi/thermal_zone/chip.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __DRIVERS_ACPI_THERMAL_ZONE_H__
+#define __DRIVERS_ACPI_THERMAL_ZONE_H__
+
+#include <types.h>
+
+/*
+ * All temperature units are in Celsius.
+ * All time units are in seconds.
+ */
+struct drivers_acpi_thermal_zone_config {
+ /* Description of the thermal zone */
+ const char *description;
+
+ /*
+ * Device that will provide the temperature reading
+ *
+ * This device must have an ACPI method named `TMP` that accepts the
+ * sensor ID as the first argument. It must then return an Integer containing the
+ * sensor's temperature in deci-Kelvin.
+ */
+ DEVTREE_CONST struct device *temperature_controller;
+
+ /* Used to identify the temperature sensor */
+ unsigned int sensor_id;
+
+ /* The polling period in seconds for this thermal zone. */
+ unsigned int polling_period;
+
+ /* The temperature (_CRT) at which the OS must shutdown the system. */
+ unsigned int critical_temperature;
+
+ /* The temperature (_HOT) at which the OS may choose to hibernate the system */
+ unsigned int hibernate_temperature;
+
+ struct acpi_thermal_zone_passive_config {
+ /*
+ * The temperature (_PSV) at which the OS must activate passive cooling (i.e.,
+ * throttle the CPUs).
+ */
+ unsigned int temperature;
+
+ /**
+ * DeltaP[%] = _TC1 * (Tn - Tn-1) + _TC2 * (Tn - Tt)
+ * Where:
+ * Tn = current temperature
+ * Tt = target temperature (_PSV)
+ *
+ * If any of these values are 0, then one of the following defaults will be
+ * used: TC1: 2, TC2: 5, TSP: 10
+ */
+ unsigned int time_constant_1;
+ unsigned int time_constant_2;
+ unsigned int time_sampling_period;
+
+ } passive_config;
+
+ /* Place the ThermalZone in the \_TZ scope */
+ bool use_acpi1_thermal_zone_scope;
+};
+
+#endif /* __DRIVERS_ACPI_THERMAL_ZONE_H__ */
diff --git a/src/drivers/acpi/thermal_zone/thermal_zone.c b/src/drivers/acpi/thermal_zone/thermal_zone.c
new file mode 100644
index 0000000000..1a52997607
--- /dev/null
+++ b/src/drivers/acpi/thermal_zone/thermal_zone.c
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <acpi/acpigen.h>
+#include <assert.h>
+#include <commonlib/bsd/helpers.h>
+#include <console/console.h>
+#include <device/device.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "chip.h"
+
+#define TZ_DEVICE_PATH "\\_TZ"
+/* These defaults should be good enough for most systems */
+#define DEFAULT_TC1 2
+#define DEFAULT_TC2 5
+#define DEFAULT_TSP 10
+
+#define CELSIUS_TO_DECI_KELVIN(temp_c) ((temp_c) * 10 + 2731)
+#define SECONDS_TO_DECI_SECONDS(s) ((s) * 10)
+
+static const char *thermal_zone_acpi_name(const struct device *dev)
+{
+ char *name;
+
+ if (dev->path.type != DEVICE_PATH_GENERIC)
+ return NULL;
+
+ name = malloc(ACPI_NAME_BUFFER_SIZE);
+ snprintf(name, ACPI_NAME_BUFFER_SIZE, "TM%02X", dev->path.generic.id);
+
+ return name;
+}
+
+static void thermal_zone_fill_ssdt(const struct device *dev)
+{
+ struct drivers_acpi_thermal_zone_config *config = config_of(dev);
+ const char *scope;
+ const char *name;
+
+ assert(dev->path.type == DEVICE_PATH_GENERIC);
+
+ if (config->use_acpi1_thermal_zone_scope)
+ scope = TZ_DEVICE_PATH;
+ else
+ scope = acpi_device_scope(dev);
+
+ name = acpi_device_name(dev);
+
+ assert(name);
+ assert(scope);
+
+ if (!config->temperature_controller) {
+ printk(BIOS_ERR, "%s: missing temperature_controller\n", dev_path(dev));
+ return;
+ }
+
+ printk(BIOS_INFO, "%s.%s: %s at %s\n", scope, name, dev->chip_ops->name, dev_path(dev));
+
+ acpigen_write_scope(scope);
+ acpigen_write_thermal_zone(name);
+
+ if (config->description)
+ acpigen_write_name_string("_STR", config->description);
+
+ if (config->polling_period)
+ acpigen_write_name_integer(
+ "_TZP", SECONDS_TO_DECI_SECONDS(config->polling_period));
+
+ if (config->critical_temperature)
+ acpigen_write_name_integer(
+ "_CRT", CELSIUS_TO_DECI_KELVIN(config->critical_temperature));
+
+ if (config->hibernate_temperature)
+ acpigen_write_name_integer(
+ "_HOT", CELSIUS_TO_DECI_KELVIN(config->hibernate_temperature));
+
+ if (config->passive_config.temperature) {
+ acpigen_write_name_integer(
+ "_PSV", CELSIUS_TO_DECI_KELVIN(config->passive_config.temperature));
+
+ /*
+ * The linux kernel currently has an artificial limit of 10 on the number of
+ * references that can be returned in a list. If we don't respect this limit,
+ * then the passive threshold won't work.
+ *
+ * See https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/third_party/kernel/v5.10/include/acpi/acpi_bus.h;l=19
+ */
+ acpigen_write_processor_package("_PSL", 0, MIN(10, dev_count_cpu()));
+
+ acpigen_write_name_integer("_TC1", config->passive_config.time_constant_1
+ ?: DEFAULT_TC1);
+ acpigen_write_name_integer("_TC2", config->passive_config.time_constant_2
+ ?: DEFAULT_TC2);
+ acpigen_write_name_integer(
+ "_TSP",
+ SECONDS_TO_DECI_SECONDS(config->passive_config.time_sampling_period
+ ?: DEFAULT_TSP));
+ }
+
+ /*
+ * Method (_TMP) {
+ * Return (<path>.TMP(<sensor_id>))
+ * }
+ */
+ acpigen_write_method_serialized("_TMP", 0);
+ acpigen_emit_byte(RETURN_OP);
+ acpigen_emit_namestring(acpi_device_path_join(config->temperature_controller, "TMP"));
+ acpigen_write_integer(config->sensor_id);
+ acpigen_write_method_end();
+
+ acpigen_write_thermal_zone_end();
+ acpigen_write_scope_end();
+}
+
+static struct device_operations thermal_zone_ops = {
+ .read_resources = noop_read_resources,
+ .set_resources = noop_set_resources,
+ .acpi_name = thermal_zone_acpi_name,
+ .acpi_fill_ssdt = thermal_zone_fill_ssdt,
+};
+
+static void thermal_zone_enable_dev(struct device *dev)
+{
+ dev->ops = &thermal_zone_ops;
+}
+
+struct chip_operations drivers_acpi_thermal_zone_ops = {
+ CHIP_NAME("ACPI Thermal Zone")
+ .enable_dev = thermal_zone_enable_dev,
+};