dptf: Add support for Passive Policies

This patch adds support for emitting the Thermal Relationship Table, as
well as _PSV Methods, which together form the basis for DPTF Passive
Policies.

BUG=b:143539650
TEST=compiles

Change-Id: I82e1c9022999b0a2a733aa6cd9c98a850e6f5408
Signed-off-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/41886
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Duncan Laurie <dlaurie@chromium.org>
diff --git a/src/acpi/acpigen_dptf.c b/src/acpi/acpigen_dptf.c
index 74e9191..abd99c1 100644
--- a/src/acpi/acpigen_dptf.c
+++ b/src/acpi/acpigen_dptf.c
@@ -9,6 +9,7 @@
 /* Defaults */
 enum {
 	ART_REVISION			= 0,
+	DEFAULT_PRIORITY		= 100,
 	DEFAULT_WEIGHT			= 100,
 	DPTF_MAX_ART_THRESHOLDS		= 10,
 };
@@ -19,6 +20,12 @@
 	return deg_c * 10 + 2732;
 }
 
+/* Converts ms to 1/10th second for ACPI */
+static int to_acpi_time(int ms)
+{
+	return ms / 100;
+}
+
 /* Writes out a 0-argument non-Serialized Method that returns an Integer */
 static void write_simple_return_method(const char *name, int value)
 {
@@ -27,6 +34,13 @@
 	acpigen_pop_len(); /* Method */
 }
 
+/* Writes out 'count' ZEROs in a row */
+static void write_zeros(int count)
+{
+	for (; count; --count)
+		acpigen_write_integer(0);
+}
+
 /* Return the assigned namestring of any participant */
 static const char *namestring_of(enum dptf_participant participant)
 {
@@ -164,3 +178,87 @@
 	write_active_relationship_table(policies, max_count);
 	write_active_cooling_methods(policies, max_count);
 }
+
+/*
+ * This writes out the Thermal Relationship Table, which describes the thermal relationships
+ * between participants in a thermal zone. This information is used to passively cool (i.e.,
+ * throttle) the Source (source of heat), in order to indirectly cool the Target (temperature
+ * sensor).
+ */
+static void write_thermal_relationship_table(const struct dptf_passive_policy *policies,
+					     int max_count)
+{
+	char *pkg_count;
+	int i;
+
+	/* Nothing to do */
+	if (!max_count || policies[0].source == DPTF_NONE)
+		return;
+
+	acpigen_write_scope(TOPLEVEL_DPTF_SCOPE);
+
+	/*
+	 * A _TRT Revision (TRTR) of 1 means that the 'Priority' field is an arbitrary priority
+	 * value to be used for this specific relationship. The priority value determines the
+	 * order in which various sources are used in a passive thermal action for a given
+	 * target.
+	 */
+	acpigen_write_name_integer("TRTR", 1);
+
+	/* Thermal Relationship Table */
+	acpigen_write_method("_TRT", 0);
+
+	/* Return this package */
+	acpigen_emit_byte(RETURN_OP);
+	pkg_count = acpigen_write_package(0);
+
+	for (i = 0; i < max_count; ++i) {
+		/* Stop writing the table once an entry is empty */
+		if (policies[i].source == DPTF_NONE)
+			break;
+
+		/* Keep track of outer package item count */
+		(*pkg_count)++;
+
+		acpigen_write_package(8);
+
+		/* Source, Target, Priority, Sampling Period */
+		acpigen_emit_namestring(namestring_of(policies[i].source));
+		acpigen_emit_namestring(namestring_of(policies[i].target));
+		acpigen_write_integer(DEFAULT_IF_0(policies[i].priority, DEFAULT_PRIORITY));
+		acpigen_write_integer(to_acpi_time(policies[i].period));
+
+		/* Reserved */
+		write_zeros(4);
+
+		acpigen_pop_len(); /* Package */
+	}
+
+	acpigen_pop_len(); /* Package */
+	acpigen_pop_len(); /* Method */
+	acpigen_pop_len(); /* Scope */
+}
+
+/*
+ * When a temperature sensor measures above its the temperature returned in its _PSV Method,
+ * DPTF will begin throttling Sources in order to indirectly cool the sensor.
+ */
+static void write_all_PSV(const struct dptf_passive_policy *policies, int max_count)
+{
+	int i;
+
+	for (i = 0; i < max_count; ++i) {
+		if (policies[i].source == DPTF_NONE)
+			break;
+
+		dptf_write_scope(policies[i].target);
+		write_simple_return_method("_PSV", to_acpi_temp(policies[i].temp));
+		acpigen_pop_len(); /* Scope */
+	}
+}
+
+void dptf_write_passive_policies(const struct dptf_passive_policy *policies, int max_count)
+{
+	write_thermal_relationship_table(policies, max_count);
+	write_all_PSV(policies, max_count);
+}
diff --git a/src/drivers/intel/dptf/chip.h b/src/drivers/intel/dptf/chip.h
index 730d23e..0cd2ad2 100644
--- a/src/drivers/intel/dptf/chip.h
+++ b/src/drivers/intel/dptf/chip.h
@@ -8,6 +8,7 @@
 struct drivers_intel_dptf_config {
 	struct {
 		struct dptf_active_policy active[DPTF_MAX_ACTIVE_POLICIES];
+		struct dptf_passive_policy passive[DPTF_MAX_PASSIVE_POLICIES];
 	} policies;
 };
 
diff --git a/src/drivers/intel/dptf/dptf.c b/src/drivers/intel/dptf/dptf.c
index 20f8d9b..74f4812 100644
--- a/src/drivers/intel/dptf/dptf.c
+++ b/src/drivers/intel/dptf/dptf.c
@@ -35,6 +35,12 @@
 		if (config->policies.active[i].target == participant)
 			return true;
 
+	/* Passive? */
+	for (i = 0; i < DPTF_MAX_PASSIVE_POLICIES; ++i)
+		if (config->policies.passive[i].source == participant ||
+		    config->policies.passive[i].target == participant)
+			return true;
+
 	/* Check fan as well (its use is implicit in the Active policy) */
 	if (participant == DPTF_FAN && config->policies.active[0].target != DPTF_NONE)
 		return true;
@@ -55,6 +61,9 @@
 	dptf_write_active_policies(config->policies.active,
 				   DPTF_MAX_ACTIVE_POLICIES);
 
+	dptf_write_passive_policies(config->policies.passive,
+				    DPTF_MAX_PASSIVE_POLICIES);
+
 	printk(BIOS_INFO, "\\_SB.DPTF: %s at %s\n", dev->chip_ops->name, dev_path(dev));
 }
 
diff --git a/src/include/acpi/acpigen_dptf.h b/src/include/acpi/acpigen_dptf.h
index a082b62..214578a 100644
--- a/src/include/acpi/acpigen_dptf.h
+++ b/src/include/acpi/acpigen_dptf.h
@@ -27,6 +27,7 @@
 	/* A device can only define _AC0 .. _AC9 i.e. between 0 and 10 Active Cooling Methods */
 	DPTF_MAX_ACX			= 10,
 	DPTF_MAX_ACTIVE_POLICIES	= (DPTF_PARTICIPANT_COUNT-1),
+	DPTF_MAX_PASSIVE_POLICIES	= (DPTF_PARTICIPANT_COUNT-1),
 };
 
 /* Active Policy */
@@ -44,6 +45,20 @@
 	} thresholds[DPTF_MAX_ACX];
 };
 
+/* Passive Policy */
+struct dptf_passive_policy {
+	/* The device that can be throttled */
+	enum dptf_participant source;
+	/* The device that controls the throttling */
+	enum dptf_participant target;
+	/* How often to check the temperature for required throttling (ms) */
+	uint16_t period;
+	/* The trip point for turning on throttling (degrees C) */
+	uint8_t temp;
+	/* Relative priority between Policies */
+	uint8_t priority;
+};
+
 /*
  * This function provides tables of temperature and corresponding fan or percent.  When the
  * temperature thresholds are met (_AC0 - _AC9), the fan is driven to corresponding percentage
@@ -51,6 +66,14 @@
  */
 void dptf_write_active_policies(const struct dptf_active_policy *policies, int max_count);
 
+/*
+ * This function uses the definition of the passive policies to write out _PSV Methods on all
+ * participants that define it.  It also writes out the Thermal Relationship Table
+ * (\_SB.DPTF._TRT), which describes various passive (i.e., throttling) policies that can be
+ * applies when temperature sensors reach the _PSV threshold.
+ */
+void dptf_write_passive_policies(const struct dptf_passive_policy *policies, int max_count);
+
 /* Helper method to open the scope for a given participant. */
 void dptf_write_scope(enum dptf_participant participant);