/* Copyright 2013 The Chromium OS Authors. All rights reserved. * 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 "chipset.h" #include "common.h" #include "console.h" #include "dptf.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) #define PROCHOT_IN_DEBOUNCE_US (100 * MSEC) /*****************************************************************************/ /* This enforces the virtual OR of all throttling sources. */ K_MUTEX_DEFINE(throttle_mutex); static uint32_t throttle_request[NUM_THROTTLE_TYPES]; static int debounced_prochot_in; static enum gpio_signal gpio_prochot_in = GPIO_COUNT; 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); } static void prochot_input_deferred(void) { int prochot_in; /* * Shouldn't be possible, but better to protect against buffer * overflow */ ASSERT(signal_is_gpio(gpio_prochot_in)); prochot_in = gpio_get_level(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)) return; debounced_prochot_in = prochot_in; if (debounced_prochot_in) { CPRINTS("External PROCHOT assertion detected"); #ifdef CONFIG_FANS dptf_set_fan_duty_target(100); #endif } else { CPRINTS("External PROCHOT condition cleared"); #ifdef CONFIG_FANS /* Revert to automatic control of the fan */ dptf_set_fan_duty_target(-1); #endif } } DECLARE_DEFERRED(prochot_input_deferred); void throttle_ap_prochot_input_interrupt(enum gpio_signal signal) { /* * Save the PROCHOT signal that generated the interrupt so we don't * rely on a specific pin name. */ if (gpio_prochot_in == GPIO_COUNT) gpio_prochot_in = 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); } /*****************************************************************************/ /* Console commands */ #ifdef CONFIG_CMD_APTHROTTLE static int command_apthrottle(int argc, 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