blob: c44138f58758c23fbae5a95754b4e824a34a649d [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0-only */
#include <acpi/acpigen.h>
#include <acpi/acpigen_dptf.h>
#include <ec/google/common/dptf.h>
#include <drivers/intel/dptf/chip.h>
#include "chip.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,
};
/* Return the fan number as a string for the FAN participant */
static const char *fan_num_namestring_of(enum dptf_participant participant)
{
switch (participant) {
case DPTF_FAN:
return "FAN0";
case DPTF_FAN_2:
return "FAN1";
default:
return "";
}
}
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, int participant)
{
/* 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_write_store();
acpigen_emit_namestring(acpi_device_path_join(ec, fan_num_namestring_of(participant)));
acpigen_emit_byte(INDEX_OP);
acpigen_emit_namestring("TFST");
acpigen_write_integer(2);
acpigen_emit_byte(ZERO_OP);
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 */
}
/*
* Emit code to execute if the policy is enabled after this function is called, and also
* remember to manually add a acpigen_pop_len() afterwards!
*/
static void write_is_policy_enabled(bool enabled)
{
/*
* Local0 = SizeOf (IDSP)
* Local1 = 0
* Local2 = 0
*
* While (Local1 < Local0) {
* If (IDSP[Local1] == Arg0 && Arg1 == enabled) {
* Local2 = 1
* }
* Local1++
* }
*
* If (Local2 == 1) {
* ..........
*/
/* Local0 = SizeOf (IDSP) */
acpigen_write_store();
acpigen_emit_byte(SIZEOF_OP);
acpigen_emit_namestring("IDSP");
acpigen_emit_byte(LOCAL0_OP);
/* Local1 = 0 (index variable) */
acpigen_write_store();
acpigen_write_zero();
acpigen_emit_byte(LOCAL1_OP);
/* Local2 = 0 (out variable, 1=found, 0=not found) */
acpigen_write_store();
acpigen_write_zero();
acpigen_emit_byte(LOCAL2_OP);
/*
* While (Local1 < Local0) {
*/
acpigen_emit_byte(WHILE_OP);
acpigen_write_len_f();
acpigen_emit_byte(LLESS_OP);
acpigen_emit_byte(LOCAL1_OP);
acpigen_emit_byte(LOCAL0_OP);
/* If (IDSP[Local1] == Arg0 && Arg1 == 1) { */
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_emit_byte(LOCAL1_OP);
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);
/* { Local2 = 1 } */
acpigen_write_store();
acpigen_write_one();
acpigen_emit_byte(LOCAL2_OP);
acpigen_pop_len(); /* If */
/*
* Local1++
* } # End of While
*/
acpigen_emit_byte(INCREMENT_OP);
acpigen_emit_byte(LOCAL1_OP);
acpigen_pop_len(); /* While */
/*
* If (Local2 == 1)
*/
acpigen_write_if();
acpigen_emit_byte(LEQUAL_OP);
acpigen_emit_byte(LOCAL2_OP);
acpigen_write_one();
/* caller must insert acpigen_pop_len() ! */
}
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(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 (from write_is_policy_enabled) */
/* If the Active Policy is disabled, disable DPTF fan control in the EC */
write_is_policy_enabled(false);
acpigen_write_store();
acpigen_write_integer(EC_FAN_DUTY_AUTO);
acpigen_emit_namestring(acpi_device_path_join(ec, "FAND"));
acpigen_pop_len(); /* If (from write_is_policy_enabled) */
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", 1);
/* Local0 = ToInteger(Arg0) */
acpigen_write_to_integer(ARG0_OP, LOCAL0_OP);
for (p = DPTF_TEMP_SENSOR_0, i = 0; p <= DPTF_TEMP_SENSOR_4; ++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_4; ++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, int participant)
{
dptf_write_scope(participant);
write_fan_fsl(ec);
write_fan_fst(ec, participant);
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 (2 degrees, in units of
* 1/10th degree K)
*/
acpigen_write_name_integer("GTSH", 20);
/* _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, const struct device *fan_dev)
{
enum dptf_participant p;
int i;
struct ec_google_chromeec_config *config = fan_dev->chip_info;
write_dppm_methods(ec);
write_charger_methods(ec);
if (config->ec_multifan_support) {
for (p = DPTF_FAN; p <= DPTF_FAN_2; ++p)
write_fan_methods(ec, p);
} else
write_fan_methods(ec, DPTF_FAN);
for (p = DPTF_TEMP_SENSOR_0, i = 0; p <= DPTF_TEMP_SENSOR_4; ++p, ++i)
write_thermal_methods(ec, p, i);
}