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 */