From 93d7bcbc67658fd1fb2ec084666283eb4b5dc728 Mon Sep 17 00:00:00 2001 From: Tim Wawrzynczak Date: Fri, 29 May 2020 15:44:25 -0600 Subject: ec/google: Add function ec_fill_dptf_helpers() ec_fill_dptf_helpers() is used to generate all of the "helper" methods that DPTF requires. A system with a Chrome EC is typically in charge of fan PWM control as well as battery charging, so if DPTF needs to manipulate those, then it requires Methods provided by the EC. BUG=b:143539650 TEST=compiles Change-Id: Ib30072d1d0748b31bcab240a0fd0e2f12d34aaa4 Signed-off-by: Tim Wawrzynczak Reviewed-on: https://review.coreboot.org/c/coreboot/+/41894 Reviewed-by: Nick Vaccaro Reviewed-by: Duncan Laurie Tested-by: build bot (Jenkins) --- src/ec/google/chromeec/Makefile.inc | 2 + src/ec/google/chromeec/ec_acpi.c | 22 +++ src/ec/google/chromeec/ec_dptf_helpers.c | 292 +++++++++++++++++++++++++++++++ src/ec/google/common/dptf.h | 11 ++ 4 files changed, 327 insertions(+) create mode 100644 src/ec/google/chromeec/ec_dptf_helpers.c create mode 100644 src/ec/google/common/dptf.h 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 #include #include +#include #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 +#include +#include + +/* + * 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 + +/* Called by google_chromeec_fill_ssdt_generator */ +void ec_fill_dptf_helpers(const struct device *dev); + +#endif /* EC_GOOGLE_COMMON_DPTF_H */ -- cgit v1.2.3