diff options
-rw-r--r-- | src/acpi/Makefile.inc | 1 | ||||
-rw-r--r-- | src/acpi/acpigen_dptf.c | 166 | ||||
-rw-r--r-- | src/drivers/intel/dptf/chip.h | 5 | ||||
-rw-r--r-- | src/drivers/intel/dptf/dptf.c | 26 | ||||
-rw-r--r-- | src/include/acpi/acpigen_dptf.h | 63 |
5 files changed, 249 insertions, 12 deletions
diff --git a/src/acpi/Makefile.inc b/src/acpi/Makefile.inc index 29ddc08d96..f70b23ff5b 100644 --- a/src/acpi/Makefile.inc +++ b/src/acpi/Makefile.inc @@ -4,6 +4,7 @@ ifeq ($(CONFIG_HAVE_ACPI_TABLES),y) ramstage-y += acpi.c ramstage-y += acpigen.c +ramstage-y += acpigen_dptf.c ramstage-y += acpigen_dsm.c ramstage-y += acpigen_ps2_keybd.c ramstage-y += acpigen_usb.c diff --git a/src/acpi/acpigen_dptf.c b/src/acpi/acpigen_dptf.c new file mode 100644 index 0000000000..74e91910bb --- /dev/null +++ b/src/acpi/acpigen_dptf.c @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <acpi/acpigen.h> +#include <acpi/acpigen_dptf.h> + +/* Hardcoded paths */ +#define TOPLEVEL_DPTF_SCOPE "\\_SB.DPTF" + +/* Defaults */ +enum { + ART_REVISION = 0, + DEFAULT_WEIGHT = 100, + DPTF_MAX_ART_THRESHOLDS = 10, +}; + +/* Convert degrees C to 1/10 degree Kelvin for ACPI */ +static int to_acpi_temp(int deg_c) +{ + return deg_c * 10 + 2732; +} + +/* Writes out a 0-argument non-Serialized Method that returns an Integer */ +static void write_simple_return_method(const char *name, int value) +{ + acpigen_write_method(name, 0); + acpigen_write_return_integer(value); + acpigen_pop_len(); /* Method */ +} + +/* Return the assigned namestring of any participant */ +static const char *namestring_of(enum dptf_participant participant) +{ + switch (participant) { + case DPTF_CPU: + return "TCPU"; + case DPTF_CHARGER: + return "TCHG"; + case DPTF_FAN: + return "TFN1"; + case DPTF_TEMP_SENSOR_0: + return "TSR0"; + case DPTF_TEMP_SENSOR_1: + return "TSR1"; + case DPTF_TEMP_SENSOR_2: + return "TSR2"; + case DPTF_TEMP_SENSOR_3: + return "TSR3"; + default: + return ""; + } +} + +/* Helper to get Scope for participants underneath \_SB.DPTF */ +static const char *scope_of(enum dptf_participant participant) +{ + static char scope[16]; + + if (participant == DPTF_CPU) + snprintf(scope, sizeof(scope), "\\_SB.%s", namestring_of(participant)); + else + snprintf(scope, sizeof(scope), TOPLEVEL_DPTF_SCOPE ".%s", + namestring_of(participant)); + + return scope; +} + +/* Write out scope of a participant */ +void dptf_write_scope(enum dptf_participant participant) +{ + acpigen_write_scope(scope_of(participant)); +} + +/* + * This table describes active cooling relationships between the system's fan and the + * temperature sensors that it can have an effect on. As ever-increasing temperature thresholds + * are crossed (_AC9.._AC0, low to high), the corresponding fan percentages listed in this table + * are used to increase the speed of the fan in order to speed up cooling. + */ +static void write_active_relationship_table(const struct dptf_active_policy *policies, + int max_count) +{ + char *pkg_count; + int i, j; + + /* Nothing to do */ + if (!max_count || policies[0].target == DPTF_NONE) + return; + + acpigen_write_scope(TOPLEVEL_DPTF_SCOPE); + acpigen_write_method("_ART", 0); + + /* Return this package */ + acpigen_emit_byte(RETURN_OP); + + /* Keep track of items added to the package */ + pkg_count = acpigen_write_package(1); /* The '1' here is for the revision */ + acpigen_write_integer(ART_REVISION); + + for (i = 0; i < max_count; ++i) { + /* + * These have to be filled out from AC0 down to AC9, filling in only as many + * as are used. As soon as one isn't filled in, we're done. + */ + if (policies[i].target == DPTF_NONE) + break; + + (*pkg_count)++; + + /* Source, Target, Percent, Fan % for each of _AC0 ... _AC9 */ + acpigen_write_package(13); + acpigen_emit_namestring(namestring_of(DPTF_FAN)); + acpigen_emit_namestring(namestring_of(policies[i].target)); + acpigen_write_integer(DEFAULT_IF_0(policies[i].weight, DEFAULT_WEIGHT)); + + /* Write out fan %; corresponds with target's _ACx methods */ + for (j = 0; j < DPTF_MAX_ART_THRESHOLDS; ++j) + acpigen_write_integer(policies[i].thresholds[j].fan_pct); + + acpigen_pop_len(); /* inner Package */ + } + + acpigen_pop_len(); /* outer Package */ + acpigen_pop_len(); /* Method _ART */ + acpigen_pop_len(); /* Scope */ +} + +/* + * _AC9 through _AC0 represent temperature thresholds, in increasing order, defined from _AC0 + * down, that, when reached, DPTF will activate TFN1 in order to actively cool the temperature + * sensor(s). As increasing thresholds are reached, the fan is spun faster. + */ +static void write_active_cooling_methods(const struct dptf_active_policy *policies, + int max_count) +{ + char name[5]; + int i, j; + + /* Nothing to do */ + if (!max_count || policies[0].target == DPTF_NONE) + return; + + for (i = 0; i < max_count; ++i) { + if (policies[i].target == DPTF_NONE) + break; + + dptf_write_scope(policies[i].target); + + /* Write out as many of _AC0 through _AC9 that are applicable */ + for (j = 0; j < DPTF_MAX_ACX; ++j) { + if (!policies[i].thresholds[j].temp) + break; + + snprintf(name, sizeof(name), "_AC%1X", j); + write_simple_return_method(name, to_acpi_temp( + policies[i].thresholds[j].temp)); + } + + acpigen_pop_len(); /* Scope */ + } +} + +void dptf_write_active_policies(const struct dptf_active_policy *policies, int max_count) +{ + write_active_relationship_table(policies, max_count); + write_active_cooling_methods(policies, max_count); +} diff --git a/src/drivers/intel/dptf/chip.h b/src/drivers/intel/dptf/chip.h index 704b83e763..730d23ed6b 100644 --- a/src/drivers/intel/dptf/chip.h +++ b/src/drivers/intel/dptf/chip.h @@ -3,7 +3,12 @@ #ifndef _DRIVERS_INTEL_DPTF_CHIP_H_ #define _DRIVERS_INTEL_DPTF_CHIP_H_ +#include <acpi/acpigen_dptf.h> + struct drivers_intel_dptf_config { + struct { + struct dptf_active_policy active[DPTF_MAX_ACTIVE_POLICIES]; + } policies; }; #endif /* _DRIVERS_INTEL_DPTF_CHIP_H_ */ diff --git a/src/drivers/intel/dptf/dptf.c b/src/drivers/intel/dptf/dptf.c index 060864adc1..20f8d9b4ae 100644 --- a/src/drivers/intel/dptf/dptf.c +++ b/src/drivers/intel/dptf/dptf.c @@ -5,18 +5,6 @@ #include <device/device.h> #include "chip.h" -enum dptf_participant { - DPTF_NONE, - DPTF_CPU, - DPTF_CHARGER, - DPTF_FAN, - DPTF_TEMP_SENSOR_0, - DPTF_TEMP_SENSOR_1, - DPTF_TEMP_SENSOR_2, - DPTF_TEMP_SENSOR_3, - DPTF_PARTICIPANT_COUNT, -}; - /* Generic DPTF participants have a PTYP field to distinguish them */ enum dptf_generic_participant_type { DPTF_GENERIC_PARTICIPANT_TYPE_TSR = 0x3, @@ -40,6 +28,17 @@ enum dptf_generic_participant_type { static bool is_participant_used(const struct drivers_intel_dptf_config *config, enum dptf_participant participant) { + int i; + + /* Active? */ + for (i = 0; i < DPTF_MAX_ACTIVE_POLICIES; ++i) + if (config->policies.active[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; + return false; } @@ -53,6 +52,9 @@ static void dptf_fill_ssdt(const struct device *dev) { struct drivers_intel_dptf_config *config = config_of(dev); + dptf_write_active_policies(config->policies.active, + DPTF_MAX_ACTIVE_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 new file mode 100644 index 0000000000..a082b62fd9 --- /dev/null +++ b/src/include/acpi/acpigen_dptf.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef ACPI_ACPIGEN_DPTF_H +#define ACPI_ACPIGEN_DPTF_H + +#include <device/device.h> +#include <stdbool.h> + +/* A common idiom is to use a default value if none is provided (i.e., == 0) */ +#define DEFAULT_IF_0(thing, default_) ((thing) ? (thing) : (default_)) + +/* List of available participants (i.e., they can participate in policies) */ +enum dptf_participant { + DPTF_NONE, + DPTF_CPU, + DPTF_CHARGER, + DPTF_FAN, + DPTF_TEMP_SENSOR_0, + DPTF_TEMP_SENSOR_1, + DPTF_TEMP_SENSOR_2, + DPTF_TEMP_SENSOR_3, + DPTF_PARTICIPANT_COUNT, +}; + +/* DPTF compile-time constants */ +enum { + /* 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), +}; + +/* Active Policy */ +struct dptf_active_policy { + /* Device capable of being affected by the fan */ + enum dptf_participant target; + /* Source's contribution to the Target's cooling capability as a percentage */ + uint8_t weight; + /* When target reaches temperature 'temp', the source will turn on at 'fan_pct' % */ + struct { + /* (degrees C) */ + uint8_t temp; + /* 0 - 100 */ + uint8_t fan_pct; + } thresholds[DPTF_MAX_ACX]; +}; + +/* + * 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 + * of full speed. + */ +void dptf_write_active_policies(const struct dptf_active_policy *policies, int max_count); + +/* Helper method to open the scope for a given participant. */ +void dptf_write_scope(enum dptf_participant participant); + +/* + * Write out a _STA that will check the value of the DPTE field in GNVS, and return 0xF if DPTE + * is 1, otherwise it will return 0. + */ +void dptf_write_STA(void); + +#endif /* ACPI_ACPIGEN_DPTF_H */ |