diff options
author | Randall Spangler <rspangler@chromium.org> | 2012-01-13 15:53:29 -0800 |
---|---|---|
committer | Randall Spangler <rspangler@chromium.org> | 2012-01-17 12:19:47 -0800 |
commit | 002bc4278bcfe26ae9c3b6fb5cdd3ddc2d07403e (patch) | |
tree | bcffd121555801ba37cbaa476648fbb2a8ae635b /common/x86_power.c | |
parent | ff3ebed7a8970e11f4169390469e4bb1d67543cc (diff) | |
download | chrome-ec-002bc4278bcfe26ae9c3b6fb5cdd3ddc2d07403e.tar.gz |
Add x86 power state machine
For bringup, this powers on the x86 unconditionally.
Signed-off-by: Randall Spangler <rspangler@chromium.org>
BUG=chrome-os-partner:7528
TEST=none
Change-Id: Ib23e56d38ab42f8d8a4dbd1ba9dce12f0c3eeec9
Diffstat (limited to 'common/x86_power.c')
-rw-r--r-- | common/x86_power.c | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/common/x86_power.c b/common/x86_power.c new file mode 100644 index 0000000000..7618b69a44 --- /dev/null +++ b/common/x86_power.c @@ -0,0 +1,285 @@ +/* Copyright (c) 2012 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. + */ + +/* X86 chipset power control module for Chrome EC */ + +#include "board.h" +#include "clock.h" +#include "console.h" +#include "gpio.h" +#include "task.h" +#include "timer.h" +#include "uart.h" +#include "util.h" +#include "x86_power.h" + +/* Default timeout in us; if we've been waiting this long for an input + * transition, just jump to the next state. */ +#define DEFAULT_TIMEOUT 1000000 + +enum x86_state { + /* Stable states */ + X86_G3 = 0, /* Initial state */ + X86_S5, /* System is off */ + X86_S0, /* System is on */ + + /* Transitions */ + X86_G3S5, /* G3 -> S5 (at system init time) */ + X86_S5S0, /* S5 -> S0 */ + + /* TODO: S3 state, S0S5, S0S3, S3S0 */ +}; + +static const char * const state_names[] = { + "G3", + "S5", + "S0", + "G3->S5", + "S5->S0", +}; + +/* Input state flags */ +#define IN_PGOOD_5VALW 0x0001 +#define IN_PGOOD_1_5V_DDR 0x0002 +#define IN_PGOOD_1_5V_PCH 0x0004 +#define IN_PGOOD_1_8VS 0x0008 +#define IN_PGOOD_VCCP 0x0010 +#define IN_PGOOD_VCCSA 0x0020 +#define IN_PGOOD_CPU_CORE 0x0040 +#define IN_PGOOD_VGFX_CORE 0x0080 +#define IN_PCH_SLP_S3n_DEASSERTED 0x0100 +#define IN_PCH_SLP_S4n_DEASSERTED 0x0200 +#define IN_PCH_SLP_S5n_DEASSERTED 0x0400 +#define IN_PCH_SLP_An_DEASSERTED 0x0800 +/* All always-on supplies */ +#define IN_PGOOD_ALWAYS_ON (IN_PGOOD_5VALW) +/* All non-core power rails */ +#define IN_PGOOD_ALL_NONCORE (IN_PGOOD_1_5V_DDR | IN_PGOOD_1_5V_PCH | \ + IN_PGOOD_1_8VS | IN_PGOOD_VCCP | IN_PGOOD_VCCSA) +/* All core power rails */ +#define IN_PGOOD_ALL_CORE (IN_PGOOD_CPU_CORE | IN_PGOOD_VGFX_CORE) +/* All PM_SLP signals from PCH deasserted */ +#define IN_ALL_PM_SLP_DEASSERTED (IN_PCH_SLP_S3n_DEASSERTED | \ + IN_PCH_SLP_S4n_DEASSERTED | \ + IN_PCH_SLP_S5n_DEASSERTED | \ + IN_PCH_SLP_An_DEASSERTED) + + +static enum x86_state state; /* Current state */ +static uint32_t in_signals; /* Current input signal states (IN_PGOOD_*) */ +static uint32_t in_want; /* Input signal state we're waiting for */ + + +/* Update input signal state */ +static void update_in_signals(void) +{ + uint32_t inew = 0; + + if (gpio_get_level(GPIO_PGOOD_5VALW)) + inew |= IN_PGOOD_5VALW; + + if (gpio_get_level(GPIO_PGOOD_1_5V_DDR)) + inew |= IN_PGOOD_1_5V_DDR; + if (gpio_get_level(GPIO_PGOOD_1_5V_PCH)) + inew |= IN_PGOOD_1_5V_PCH; + if (gpio_get_level(GPIO_PGOOD_1_8VS)) + inew |= IN_PGOOD_1_8VS; + if (gpio_get_level(GPIO_PGOOD_VCCP)) + inew |= IN_PGOOD_VCCP; + if (gpio_get_level(GPIO_PGOOD_VCCSA)) + inew |= IN_PGOOD_VCCSA; + + if (gpio_get_level(GPIO_PGOOD_CPU_CORE)) + inew |= IN_PGOOD_CPU_CORE; + if (gpio_get_level(GPIO_PGOOD_VGFX_CORE)) + inew |= IN_PGOOD_VGFX_CORE; + + if (gpio_get_level(GPIO_PCH_SLP_An)) + inew |= IN_PCH_SLP_An_DEASSERTED; + if (gpio_get_level(GPIO_PCH_SLP_S3n)) + inew |= IN_PCH_SLP_S3n_DEASSERTED; + if (gpio_get_level(GPIO_PCH_SLP_S4n)) + inew |= IN_PCH_SLP_S4n_DEASSERTED; + if (gpio_get_level(GPIO_PCH_SLP_S5n)) + inew |= IN_PCH_SLP_S5n_DEASSERTED; + + in_signals = inew; +} + + +/* Wait for all the inputs in <want> to be present. Returns EC_ERROR_TIMEOUT + * if timeout before reaching the desired state. */ +static int wait_in_signals(uint32_t want) +{ + in_want = want; + + while ((in_signals & in_want) != in_want) { + if (task_wait_msg(DEFAULT_TIMEOUT) == (1 << TASK_ID_TIMER)) { + uart_printf("[x86 power timeout on input; " + "wanted 0x%04x, got 0x%04x]\n", + in_want, in_signals & in_want); + return EC_ERROR_TIMEOUT; + } + /* TODO: should really shrink the remaining timeout if we woke + * up but didn't have all the signals we wanted. Also need to + * handle aborts if we're no longer in the same state we were + * when we started waiting. */ + } + return EC_SUCCESS; +} + + +/*****************************************************************************/ +/* Interrupts */ + +void x86_power_interrupt(enum gpio_signal signal) +{ + /* Signals we handle specially */ + switch (signal) { + case GPIO_PCH_BKLTEN: + /* Copy backlight enable signal from PCH to BKLTEN */ + gpio_set_level(GPIO_ENABLE_BACKLIGHT, + gpio_get_level(GPIO_PCH_BKLTEN)); + return; + + case GPIO_PCH_SUSWARNn: + /* Copy SUSWARN# signal from PCH to SUSACK# */ + gpio_set_level(GPIO_PCH_SUSACKn, + gpio_get_level(GPIO_PCH_SUSWARNn)); + return; + + default: + /* All other signals we shadow and compare with our desired + * signal state. */ + update_in_signals(); + + /* Wake task if we want at least one signal, and all all the + * inputs we want are present */ + if (in_want && (in_signals & in_want) == in_want) + task_send_msg(TASK_ID_X86POWER, TASK_ID_X86POWER, 0); + } +} + +/*****************************************************************************/ +/* Initialization */ + +int x86_power_init(void) +{ + state = X86_G3; + + /* Update input state */ + update_in_signals(); + in_want = 0; + + /* Enable interrupts for our GPIOs */ + gpio_enable_interrupt(GPIO_PCH_BKLTEN); + gpio_enable_interrupt(GPIO_PCH_SLP_An); + gpio_enable_interrupt(GPIO_PCH_SLP_ME_CSW_DEVn); + gpio_enable_interrupt(GPIO_PCH_SLP_S3n); + gpio_enable_interrupt(GPIO_PCH_SLP_S4n); + gpio_enable_interrupt(GPIO_PCH_SLP_S5n); + gpio_enable_interrupt(GPIO_PCH_SLP_SUSn); + gpio_enable_interrupt(GPIO_PCH_SUSWARNn); + gpio_enable_interrupt(GPIO_PGOOD_1_5V_DDR); + gpio_enable_interrupt(GPIO_PGOOD_1_5V_PCH); + gpio_enable_interrupt(GPIO_PGOOD_1_8VS); + gpio_enable_interrupt(GPIO_PGOOD_5VALW); + gpio_enable_interrupt(GPIO_PGOOD_CPU_CORE); + gpio_enable_interrupt(GPIO_PGOOD_VCCP); + gpio_enable_interrupt(GPIO_PGOOD_VCCSA); + gpio_enable_interrupt(GPIO_PGOOD_VGFX_CORE); + + return EC_SUCCESS; +} + +/*****************************************************************************/ +/* Task function */ + +void x86_power_task(void) +{ + x86_power_init(); + + while (1) { + uart_printf("[x86 power state %d = %s]\n", state, + state_names[state]); + + switch (state) { + + case X86_G3: + /* Move to S5 state on boot */ + state = X86_G3S5; + break; + + case X86_S5: + /* For bringup, power on one second after boot */ + /* TODO: remove post-bringup */ + usleep(1000000); + state = X86_S5S0; + break; + + case X86_G3S5: + /* Wait for the always-on rails to be good */ + wait_in_signals(IN_PGOOD_ALWAYS_ON); + + /* Wait 10ms after +5VALW good */ + usleep(10000); + + /* Assert DPWROK, deassert RSMRST# */ + gpio_set_level(GPIO_PCH_DPWROK, 1); + gpio_set_level(GPIO_PCH_RSMRSTn, 1); + + /* Wait 5ms for SUSCLK to stabilize */ + usleep(5000); + + state = X86_S5; + break; + + case X86_S5S0: + /* TODO: this should be in response to a power button + * event, not causing one. For initial bringup, + * simulate the event. */ + /* Assert power button */ + gpio_set_level(GPIO_PCH_PWRBTNn, 0); + /* Wait 16ms after asserting PWRBTN# */ + usleep(16000); + /* Release power button */ + gpio_set_level(GPIO_PCH_PWRBTNn, 1); + + /* Wait for PM_SLP_S3n to be asserted */ + wait_in_signals(IN_ALL_PM_SLP_DEASSERTED); + + /* Turn on power rails */ + gpio_set_level(GPIO_ENABLE_VS, 1); + gpio_set_level(GPIO_SHUNT_1_5V_DDR, 0); + gpio_set_level(GPIO_ENABLE_1_5V_DDR, 1); + + /* Wait for non-core power rails good */ + wait_in_signals(IN_PGOOD_ALL_NONCORE); + + /* Enable +CPU_CORE and +VGFX_CORE */ + gpio_set_level(GPIO_ENABLE_VCORE, 1); + + /* Wait for all supplies good */ + wait_in_signals(IN_PGOOD_ALL_NONCORE | + IN_PGOOD_ALL_CORE); + + /* Wait 99ms after all voltages good */ + usleep(99000); + + /* Set PCH_PWROK */ + gpio_set_level(GPIO_PCH_PWROK, 1); + + state = X86_S0; + break; + + case X86_S0: + /* Steady state; wait for a message */ + task_wait_msg(-1); + } + } +} + + + |