blob: 31272f7cedca4a801f6a0cbd93822b2819810605 [file] [log] [blame]
/* Copyright 2013 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* Common chipset throttling code for Chrome EC */
#include "builtin/assert.h"
#include "chipset.h"
#include "common.h"
#include "console.h"
#include "dptf.h"
#include "gpio.h"
#include "hooks.h"
#include "host_command.h"
#include "task.h"
#include "throttle_ap.h"
#include "timer.h"
#include "util.h"
/* Console output macros */
#define CPUTS(outstr) cputs(CC_THERMAL, outstr)
#define CPRINTS(format, args...) cprints(CC_THERMAL, format, ##args)
/*
* When C10 deasserts, PROCHOT may also change state when the corresponding
* power rail is turned back on. Recheck PROCHOT directly from the C10 exit
* using a shorter debounce than the PROCHOT interrupt.
*/
#define C10_IN_DEBOUNCE_US (10 * MSEC)
/*****************************************************************************/
/* This enforces the virtual OR of all throttling sources. */
static K_MUTEX_DEFINE(throttle_mutex);
static uint32_t throttle_request[NUM_THROTTLE_TYPES];
static int debounced_prochot_in;
static const struct prochot_cfg *prochot_cfg;
void throttle_ap(enum throttle_level level, enum throttle_type type,
enum throttle_sources source)
{
uint32_t tmpval, bitmask;
mutex_lock(&throttle_mutex);
bitmask = BIT(source);
switch (level) {
case THROTTLE_ON:
throttle_request[type] |= bitmask;
break;
case THROTTLE_OFF:
throttle_request[type] &= ~bitmask;
break;
}
tmpval = throttle_request[type]; /* save for printing */
switch (type) {
case THROTTLE_SOFT:
#ifdef HAS_TASK_HOSTCMD
host_throttle_cpu(tmpval);
#endif
break;
case THROTTLE_HARD:
#ifdef CONFIG_CHIPSET_CAN_THROTTLE
chipset_throttle_cpu(tmpval);
#endif
break;
case NUM_THROTTLE_TYPES:
/* Make the compiler shut up. Don't use 'default', because
* we still want to catch any new types.
*/
break;
}
mutex_unlock(&throttle_mutex);
/* print outside the mutex */
CPRINTS("set AP throttling type %d to %s (0x%08x)", type,
tmpval ? "on" : "off", tmpval);
}
void throttle_ap_config_prochot(const struct prochot_cfg *cfg)
{
prochot_cfg = cfg;
if (IS_ENABLED(CONFIG_THROTTLE_AP_SINGLE_PIN)) {
gpio_set_flags(prochot_cfg->gpio_prochot_in, GPIO_INPUT);
}
}
__maybe_unused static bool prochot_is_gated_by_c10(int prochot_in)
{
#ifdef CONFIG_CPU_PROCHOT_GATE_ON_C10
int c10_in = gpio_get_level(prochot_cfg->gpio_c10_in);
if (!prochot_cfg->c10_active_high)
c10_in = !c10_in;
if (c10_in && prochot_in) {
return true;
}
#endif
return false;
}
static void prochot_input_deferred(void)
{
int prochot_in;
/*
* Validate board called throttle_ap_config_prochot().
*/
ASSERT(prochot_cfg);
prochot_in = gpio_get_level(prochot_cfg->gpio_prochot_in);
if (IS_ENABLED(CONFIG_CPU_PROCHOT_ACTIVE_LOW))
prochot_in = !prochot_in;
if (prochot_in == debounced_prochot_in)
return;
/*
* b/173180788 Confirmed from Intel internal that SLP_S3# asserts low
* about 10us before PROCHOT# asserts low, which means that
* the CPU is already in reset and therefore the PROCHOT#
* asserting low is normal behavior and not a concern
* for PROCHOT# event. Ignore all PROCHOT changes while the AP is off
*/
if (chipset_in_state(CHIPSET_STATE_ANY_OFF | CHIPSET_STATE_ANY_SUSPEND))
return;
/*
* b/185810479 When the AP enters C10, the PROCHOT signal may not be
* valid. Refer to the CONFIG_CPU_PROCHOT_GATE_ON_C10 documentation
* for details.
*/
if (prochot_is_gated_by_c10(prochot_in))
return;
debounced_prochot_in = prochot_in;
if (debounced_prochot_in) {
CPRINTS("External PROCHOT assertion detected");
#if defined(CONFIG_FANS) && !defined(CONFIG_THROTTLE_AP_NO_FAN)
dptf_set_fan_duty_target(100);
#endif
} else {
CPRINTS("External PROCHOT condition cleared");
#if defined(CONFIG_FANS) && !defined(CONFIG_THROTTLE_AP_NO_FAN)
/* Revert to automatic control of the fan */
dptf_set_fan_duty_target(-1);
#endif
}
if (prochot_cfg->callback)
prochot_cfg->callback(debounced_prochot_in,
prochot_cfg->callback_data);
}
DECLARE_DEFERRED(prochot_input_deferred);
void throttle_ap_prochot_input_interrupt(enum gpio_signal signal)
{
/*
* Trigger deferred notification of PROCHOT change so we can ignore
* any pulses that are too short.
*/
hook_call_deferred(&prochot_input_deferred_data,
PROCHOT_IN_DEBOUNCE_US);
}
#ifdef CONFIG_CPU_PROCHOT_GATE_ON_C10
void throttle_ap_c10_input_interrupt(enum gpio_signal signal)
{
/*
* This interrupt is configured to fire only when the AP exits C10
* and de-asserts the C10 signal. Recheck the PROCHOT signal in case
* another PROCHOT source is active when the AP exits C10.
*/
hook_call_deferred(&prochot_input_deferred_data, C10_IN_DEBOUNCE_US);
}
#endif
/*****************************************************************************/
/* Console commands */
#ifdef CONFIG_CMD_APTHROTTLE
static int command_apthrottle(int argc, const char **argv)
{
int i;
uint32_t tmpval;
for (i = 0; i < NUM_THROTTLE_TYPES; i++) {
mutex_lock(&throttle_mutex);
tmpval = throttle_request[i];
mutex_unlock(&throttle_mutex);
ccprintf("AP throttling type %d is %s (0x%08x)\n", i,
tmpval ? "on" : "off", tmpval);
}
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(apthrottle, command_apthrottle, NULL,
"Display the AP throttling state");
#endif
OSZAR »