summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ec/google/chromeec/Makefile.inc2
-rw-r--r--src/ec/google/chromeec/ec_acpi.c22
-rw-r--r--src/ec/google/chromeec/ec_dptf_helpers.c292
-rw-r--r--src/ec/google/common/dptf.h11
4 files changed, 327 insertions, 0 deletions
diff --git a/src/ec/google/chromeec/Makefile.inc b/src/ec/google/chromeec/Makefile.inc
index 1f7e03d88e..a37d673b33 100644
--- a/src/ec/google/chromeec/Makefile.inc
+++ b/src/ec/google/chromeec/Makefile.inc
@@ -42,6 +42,8 @@ verstage-$(CONFIG_EC_GOOGLE_CHROMEEC_SWITCHES) += switches.c
romstage-$(CONFIG_EC_GOOGLE_CHROMEEC_SWITCHES) += switches.c
ramstage-$(CONFIG_EC_GOOGLE_CHROMEEC_SWITCHES) += switches.c
+ramstage-$(CONFIG_DRIVERS_INTEL_DPTF) += ec_dptf_helpers.c
+
CHROMEEC_SOURCE ?= $(top)/3rdparty/chromeec
# These are Chrome EC firmware images that a payload (such as depthcharge) can
diff --git a/src/ec/google/chromeec/ec_acpi.c b/src/ec/google/chromeec/ec_acpi.c
index dca52854c5..8a76805407 100644
--- a/src/ec/google/chromeec/ec_acpi.c
+++ b/src/ec/google/chromeec/ec_acpi.c
@@ -7,6 +7,7 @@
#include <acpi/acpigen_usb.h>
#include <console/console.h>
#include <drivers/usb/acpi/chip.h>
+#include <ec/google/common/dptf.h>
#include "chip.h"
#include "ec.h"
@@ -241,11 +242,32 @@ static void fill_ssdt_ps2_keyboard(const struct device *dev)
!!(keybd.capabilities & KEYBD_CAP_SCRNLOCK_KEY));
}
+static const char *ec_acpi_name(const struct device *dev)
+{
+ return "EC0";
+}
+
+static struct device_operations ec_ops = {
+ .acpi_name = ec_acpi_name,
+};
+
void google_chromeec_fill_ssdt_generator(const struct device *dev)
{
+ struct device_path path;
+ struct device *ec;
+
if (!dev->enabled)
return;
+ /* Set up a minimal EC0 device to pass to the DPTF helpers */
+ path.type = DEVICE_PATH_GENERIC;
+ path.generic.id = 0;
+ ec = alloc_find_dev(dev->bus, &path);
+ ec->ops = &ec_ops;
+
+ if (CONFIG(DRIVERS_INTEL_DPTF))
+ ec_fill_dptf_helpers(ec);
+
fill_ssdt_typec_device(dev);
fill_ssdt_ps2_keyboard(dev);
}
diff --git a/src/ec/google/chromeec/ec_dptf_helpers.c b/src/ec/google/chromeec/ec_dptf_helpers.c
new file mode 100644
index 0000000000..74049ac703
--- /dev/null
+++ b/src/ec/google/chromeec/ec_dptf_helpers.c
@@ -0,0 +1,292 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <acpi/acpigen.h>
+#include <acpi/acpigen_dptf.h>
+#include <ec/google/common/dptf.h>
+
+/*
+ * The Chrome EC is typically in charge of many system functions, including battery charging and
+ * fan PWM control. This places it in the middle of a DPTF implementation and therefore, many of
+ * the "helper" ACPI Methods themselves call EC Methods. Because of that, the responsibility for
+ * producing the corresponding AML lies here.
+ */
+
+/* DPTF Event types */
+enum {
+ TRIP_POINTS_CHANGED_EVENT = 0x81,
+ THERMAL_EVENT = 0x90,
+};
+
+/* EC constants */
+enum {
+ EC_FAN_DUTY_AUTO = 0xFF,
+};
+
+static void write_charger_PPPC(const struct device *ec)
+{
+ acpigen_write_method_serialized("PPPC", 0);
+
+ /*
+ * Convert size of PPSS table to index
+ *
+ * Store (SizeOf (PPSS), Local0)
+ * Decrement (Local0)
+ */
+ acpigen_write_store();
+ acpigen_emit_byte(SIZEOF_OP);
+ acpigen_emit_namestring("PPSS");
+ acpigen_emit_byte(LOCAL0_OP);
+ acpigen_emit_byte(DECREMENT_OP);
+ acpigen_emit_byte(LOCAL0_OP);
+
+ /*
+ * Check if charging is disabled (AC removed)
+ *
+ * If (\_SB.PCI0.LPCB.EC0.ACEX () = Zero) {
+ * Return (Local0)
+ * }
+ */
+ acpigen_write_if();
+ acpigen_emit_byte(LEQUAL_OP);
+ acpigen_emit_namestring(acpi_device_path_join(ec, "ACEX"));
+ acpigen_emit_byte(ZERO_OP);
+ acpigen_write_return_op(LOCAL0_OP);
+ acpigen_pop_len(); /* If */
+
+ /* Return highest power state (index 0) */
+ acpigen_write_return_op(ZERO_OP);
+
+ acpigen_pop_len(); /* Method */
+}
+
+static void write_charger_SPPC(const struct device *ec)
+{
+ /*
+ * SPPC - Set charger current limit
+ * Method(SPPC, 1) {
+ * Store (DeRefOf (Index (DeRefOf (Index
+ * (PPSS, ToInteger (Arg0))), 4)), Local0)
+ * \_SB.PCI0.LPCB.EC0.CHGS (Local0)
+ * }
+ */
+
+ acpigen_write_method_serialized("SPPC", 1);
+
+ /* Retrieve Control (index 4) for specified PPSS level */
+ acpigen_emit_byte(STORE_OP);
+ acpigen_emit_byte(DEREF_OP);
+ acpigen_emit_byte(INDEX_OP);
+ acpigen_emit_byte(DEREF_OP);
+ acpigen_emit_byte(INDEX_OP);
+ acpigen_emit_namestring("PPSS");
+ acpigen_write_to_integer(ARG0_OP, ZERO_OP);
+ acpigen_emit_byte(ZERO_OP); /* 3rd arg to Index */
+ acpigen_write_integer(4); /* Index */
+ acpigen_emit_byte(ZERO_OP); /* 3rd arg to Index */
+ acpigen_emit_byte(LOCAL0_OP);
+
+ /* Pass Control value to EC to limit charging */
+ acpigen_emit_namestring(acpi_device_path_join(ec, "CHGS"));
+ acpigen_emit_byte(LOCAL0_OP);
+ acpigen_pop_len(); /* Method */
+}
+
+static void write_fan_fst(const struct device *ec)
+{
+ /* TFST is a package that is used to store data from FAND */
+ acpigen_write_name("TFST");
+ acpigen_write_package(3);
+ acpigen_write_integer(0); /* Revision */
+ acpigen_write_integer(0); /* Control */
+ acpigen_write_integer(0); /* Speed */
+ acpigen_pop_len(); /* Package */
+
+ /* _FST */
+ acpigen_write_method_serialized("_FST", 0);
+ acpigen_write_store();
+ acpigen_emit_namestring(acpi_device_path_join(ec, "FAND"));
+ acpigen_emit_byte(INDEX_OP);
+ acpigen_emit_namestring("TFST");
+ acpigen_write_integer(1);
+ acpigen_emit_byte(ZERO_OP); /* 3rd arg to Index */
+ acpigen_emit_byte(RETURN_OP);
+ acpigen_emit_namestring("TFST");
+ acpigen_pop_len(); /* Method _FST */
+}
+
+static void write_fan_fsl(const struct device *ec)
+{
+ /* _FSL */
+ acpigen_write_method_serialized("_FSL", 1);
+ acpigen_write_store();
+ acpigen_emit_byte(ARG0_OP);
+ acpigen_emit_namestring(acpi_device_path_join(ec, "FAND"));
+ acpigen_pop_len(); /* Method _FSL */
+}
+
+/* Note: requires manual insertion of acpigen_pop_len() for the If */
+static void write_is_policy_enabled(unsigned int index, bool enabled)
+{
+ /*
+ * If (And (LEqual (Deref (Index (IDSP, index)), Arg0))
+ * (LEqual (Arg1, enabled)))
+ */
+ acpigen_write_if();
+ acpigen_emit_byte(LAND_OP);
+ acpigen_emit_byte(LEQUAL_OP);
+ acpigen_emit_byte(DEREF_OP);
+ acpigen_emit_byte(INDEX_OP);
+ acpigen_emit_namestring("IDSP");
+ acpigen_write_integer(index);
+ acpigen_emit_byte(ZERO_OP); /* 3rd arg of index - unused */
+ acpigen_emit_byte(ARG0_OP); /* end lequal */
+ acpigen_emit_byte(LEQUAL_OP);
+ acpigen_emit_byte(ARG1_OP);
+ acpigen_write_integer(enabled ? 1 : 0);
+}
+
+static void write_dptf_OSC(const struct device *ec)
+{
+ char name[16];
+ int i;
+
+ /*
+ * Arg0: Buffer containing UUID
+ * Arg1: "Integer containing Revision ID of buffer format", but Linux passes whether
+ * it is enabling (1) or disabling (0) the policy in Arg1.
+ * Arg2: Integer containing count of entries in Arg3
+ * Arg3: Buffer containing list of DWORD capabilities
+ * Return: Buffer containing list of DWORD capabilities
+ */
+ acpigen_write_method_serialized("_OSC", 4);
+
+ /*
+ * If the Passive Policy is enabled:
+ * 1) Disable temperature sensor trip points in the EC (replaces TINI)
+ * 2) Disable the charge limit in the EC (replaces TCHG.INIT)
+ */
+ write_is_policy_enabled(0, true);
+ for (i = 0; i < DPTF_MAX_TSR; ++i) {
+ snprintf(name, sizeof(name), "^TSR%1d.PATD", i);
+ acpigen_emit_namestring(name);
+ }
+
+ acpigen_emit_namestring(acpi_device_path_join(ec, "CHGD"));
+ acpigen_pop_len(); /* If */
+
+ /* If the Active Policy is disabled, disable DPTF fan control in the EC */
+ write_is_policy_enabled(2, false);
+ acpigen_write_store();
+ acpigen_write_integer(EC_FAN_DUTY_AUTO);
+ acpigen_emit_namestring(acpi_device_path_join(ec, "FAND"));
+ acpigen_pop_len(); /* If */
+
+ acpigen_write_return_op(ARG3_OP);
+ acpigen_pop_len(); /* Method _OSC */
+}
+
+static void write_dppm_methods(const struct device *ec)
+{
+ enum dptf_participant p;
+ char name[16];
+ int i;
+
+ acpigen_write_scope("\\_SB.DPTF");
+ write_dptf_OSC(ec);
+
+ /* TEVT */
+ if (CONFIG(EC_SUPPORTS_DPTF_TEVT)) {
+ acpigen_write_method("TEVT", 0);
+
+ /* Local0 = ToInteger(Arg0) */
+ acpigen_write_to_integer(ARG0_OP, LOCAL0_OP);
+ for (p = DPTF_TEMP_SENSOR_0, i = 0; p <= DPTF_TEMP_SENSOR_3; ++p, ++i) {
+ snprintf(name, sizeof(name), "^TSR%1d", i);
+ acpigen_write_if_lequal_op_int(LOCAL0_OP, i);
+ acpigen_notify(name, THERMAL_EVENT);
+ acpigen_pop_len(); /* If */
+ }
+
+ acpigen_pop_len(); /* Method */
+ }
+
+ /* TPET */
+ acpigen_write_method("TPET", 0);
+ for (p = DPTF_TEMP_SENSOR_0, i = 0; p <= DPTF_TEMP_SENSOR_3; ++p, ++i) {
+ snprintf(name, sizeof(name), "^TSR%1d", i);
+ acpigen_notify(name, TRIP_POINTS_CHANGED_EVENT);
+ }
+
+ acpigen_pop_len(); /* Method */
+ acpigen_pop_len(); /* Scope */
+}
+
+static void write_charger_methods(const struct device *ec)
+{
+ dptf_write_scope(DPTF_CHARGER);
+ write_charger_PPPC(ec);
+ write_charger_SPPC(ec);
+ acpigen_pop_len(); /* Scope */
+}
+
+static void write_fan_methods(const struct device *ec)
+{
+ dptf_write_scope(DPTF_FAN);
+ write_fan_fsl(ec);
+ write_fan_fst(ec);
+ acpigen_pop_len(); /* Scope */
+}
+
+static void write_thermal_methods(const struct device *ec, enum dptf_participant participant,
+ int tsr_index)
+{
+ dptf_write_scope(participant);
+
+ /* GTSH - Amount of hysteresis inherent in temperature reading */
+ acpigen_write_name_integer("GTSH", 2);
+
+ /* _TMP - read temperature from EC */
+ acpigen_write_method_serialized("_TMP", 0);
+ acpigen_emit_byte(RETURN_OP);
+ acpigen_emit_namestring(acpi_device_path_join(ec, "TSRD"));
+ acpigen_write_integer(tsr_index);
+ acpigen_pop_len(); /* Method _TMP */
+
+ /* PATC - Aux trip point count */
+ acpigen_write_name_integer("PATC", 2);
+
+ /* PAT0 - Set Aux trip point 0 */
+ acpigen_write_method_serialized("PAT0", 1);
+ acpigen_emit_namestring(acpi_device_path_join(ec, "PAT0"));
+ acpigen_write_integer(tsr_index);
+ acpigen_emit_byte(ARG0_OP);
+ acpigen_pop_len(); /* Method PAT0 */
+
+ /* PAT1 - Set Aux trip point 1 */
+ acpigen_write_method_serialized("PAT1", 1);
+ acpigen_emit_namestring(acpi_device_path_join(ec, "PAT1"));
+ acpigen_write_integer(tsr_index);
+ acpigen_emit_byte(ARG0_OP);
+ acpigen_pop_len(); /* Method PAT0 */
+
+ /* PATD - Disable Aux trip point */
+ acpigen_write_method_serialized("PATD", 0);
+ acpigen_emit_namestring(acpi_device_path_join(ec, "PATD"));
+ acpigen_write_integer(tsr_index);
+ acpigen_pop_len(); /* Method PAT0 */
+
+ acpigen_pop_len(); /* Scope */
+}
+
+void ec_fill_dptf_helpers(const struct device *ec)
+{
+ enum dptf_participant p;
+ int i;
+
+ write_dppm_methods(ec);
+ write_charger_methods(ec);
+ write_fan_methods(ec);
+
+ for (p = DPTF_TEMP_SENSOR_0, i = 0; p <= DPTF_TEMP_SENSOR_3; ++p, ++i)
+ write_thermal_methods(ec, p, i);
+}
diff --git a/src/ec/google/common/dptf.h b/src/ec/google/common/dptf.h
new file mode 100644
index 0000000000..a59ee0b6bb
--- /dev/null
+++ b/src/ec/google/common/dptf.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef EC_GOOGLE_COMMON_DPTF_H
+#define EC_GOOGLE_COMMON_DPTF_H
+
+#include <device/device.h>
+
+/* Called by google_chromeec_fill_ssdt_generator */
+void ec_fill_dptf_helpers(const struct device *dev);
+
+#endif /* EC_GOOGLE_COMMON_DPTF_H */