diff options
author | Divya Jyothi <divya.jyothi@intel.com> | 2014-10-27 09:48:38 -0700 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-11-06 09:26:21 +0000 |
commit | 75ced738383d4b3bdf4e95b31c193cb0366e69cf (patch) | |
tree | 304e1fef1e803c3fb0af2241b9fd60c1c4815f5e | |
parent | 41cde665166da1aced2ece17f7b503c78cdb5c8f (diff) | |
download | chrome-ec-75ced738383d4b3bdf4e95b31c193cb0366e69cf.tar.gz |
Strago: Initial Version of Strago Board added.
Modules that are enabled are listed below:
- Power Sequencing
- Keyboard Scan and Protocol
- LPC to support Keyboard
- Power Button Task
ec.spi.bin has to be generated manualy using
pack_ec.py
BUG=None
BRANCH=None
TEST=Tested on Stargo-Proto board
Change-Id: Ic5d504c3d6e9c7c5f3482fb7e9e37800b6274824
Signed-off-by: Divya Jyothi <divya.jyothi@intel.com>
Reviewed-on: https://chromium-review.googlesource.com/226303
Reviewed-by: Vic Yang <victoryang@chromium.org>
-rw-r--r-- | board/strago/board.c | 28 | ||||
-rw-r--r-- | board/strago/board.h | 57 | ||||
-rw-r--r-- | board/strago/build.mk | 12 | ||||
-rw-r--r-- | board/strago/ec.tasklist | 26 | ||||
-rw-r--r-- | board/strago/gpio.inc | 53 | ||||
-rw-r--r-- | chip/mec1322/keyboard_raw.c | 27 | ||||
-rw-r--r-- | chip/mec1322/lpc.c | 93 | ||||
-rw-r--r-- | chip/mec1322/registers.h | 1 | ||||
-rw-r--r-- | common/power_button_x86.c | 3 | ||||
-rw-r--r-- | include/config.h | 1 | ||||
-rw-r--r-- | power/braswell.c | 327 | ||||
-rw-r--r-- | power/build.mk | 1 |
12 files changed, 607 insertions, 22 deletions
diff --git a/board/strago/board.c b/board/strago/board.c new file mode 100644 index 0000000000..1aa9762515 --- /dev/null +++ b/board/strago/board.c @@ -0,0 +1,28 @@ +/* Copyright (c) 2014 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. + */ +/* Strago board-specific configuration */ + +#include "extpower.h" +#include "gpio.h" +#include "lid_switch.h" +#include "power.h" +#include "power_button.h" +#include "registers.h" +#include "util.h" + +#define GPIO_KB_INPUT (GPIO_INPUT | GPIO_PULL_UP) +#define GPIO_KB_OUTPUT (GPIO_ODR_HIGH) +#define GPIO_KB_OUTPUT_COL2 (GPIO_OUT_LOW) + +#include "gpio_list.h" + +/* power signal list. Must match order of enum power_signal. */ +const struct power_signal_info power_signal_list[] = { + {GPIO_ALL_SYS_PGOOD, 1, "ALL_SYS_PWRGD"}, + {GPIO_RSMRST_L_PGOOD, 1, "RSMRST_N_PWRGD"}, + {GPIO_PCH_SLP_S3_L, 1, "SLP_S3#_DEASSERTED"}, + {GPIO_PCH_SLP_S4_L, 1, "SLP_S4#_DEASSERTED"}, +}; +BUILD_ASSERT(ARRAY_SIZE(power_signal_list) == POWER_SIGNAL_COUNT); diff --git a/board/strago/board.h b/board/strago/board.h new file mode 100644 index 0000000000..fb28504850 --- /dev/null +++ b/board/strago/board.h @@ -0,0 +1,57 @@ +/* Copyright (c) 2014 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. + */ + +/* Strago board configuration */ + +#ifndef __BOARD_H +#define __BOARD_H + + +/* Optional features */ +#define CONFIG_SYSTEM_UNLOCKED /* Allow dangerous commands */ +#define CONFIG_WATCHDOG_HELP +#define CONFIG_CHIPSET_BRASWELL +#define CONFIG_KEYBOARD_PROTOCOL_8042 +#define CONFIG_KEYBOARD_IRQ_GPIO GPIO_KBD_IRQ_L +#define CONFIG_KEYBOARD_COL2_INVERTED +#define CONFIG_POWER_BUTTON +#define CONFIG_POWER_BUTTON_X86 +#define CONFIG_LID_SWITCH +#define CONFIG_POWER_COMMON +#define CONFIG_EXTPOWER_GPIO + +/* Modules we want to exclude */ +#undef CONFIG_EEPROM +#undef CONFIG_EOPTION +#undef CONFIG_PSTORE +#undef CONFIG_PECI +#undef CONFIG_SWITCH +#undef CONFIG_I2C +#undef CONFIG_PWM +#undef CONFIG_FANS +#undef CONFIG_ADC +#undef CONFIG_WAKE_PIN +#undef CONFIG_SPI +#undef CONFIG_SPI_PORT +#undef CONFIG_SPI_CS_GPIO + +#ifndef __ASSEMBLER__ + +#include "gpio_signal.h" + +/* power signal definitions */ +enum power_signal { + X86_ALL_SYS_PWRGD = 0, + X86_RSMRST_L_PWRGD, + X86_SLP_S3_DEASSERTED, + X86_SLP_S4_DEASSERTED, + + /* Number of X86 signals */ + POWER_SIGNAL_COUNT +}; + +#endif /* !__ASSEMBLER__ */ + +#endif /* __BOARD_H */ diff --git a/board/strago/build.mk b/board/strago/build.mk new file mode 100644 index 0000000000..91ce24af42 --- /dev/null +++ b/board/strago/build.mk @@ -0,0 +1,12 @@ +# -*- makefile -*- +# Copyright (c) 2014 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. +# +# Board specific files build +# + +# the IC is SMSC MEC1322 / external SPI is 4MB / external clock is crystal +CHIP:=mec1322 + +board-y=board.o diff --git a/board/strago/ec.tasklist b/board/strago/ec.tasklist new file mode 100644 index 0000000000..bee345cf29 --- /dev/null +++ b/board/strago/ec.tasklist @@ -0,0 +1,26 @@ +/* Copyright (c) 2014 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. + */ + +/** + * List of enabled tasks in the priority order + * + * The first one has the lowest priority. + * + * For each task, use the macro TASK_ALWAYS(n, r, d, s) for base tasks and + * TASK_NOTEST(n, r, d, s) for tasks that can be excluded in test binaries, + * where : + * 'n' in the name of the task + * 'r' in the main routine of the task + * 'd' in an opaque parameter passed to the routine at startup + * 's' is the stack size in bytes; must be a multiple of 8 + */ +#define CONFIG_TASK_LIST \ + TASK_ALWAYS(HOOKS, hook_task, NULL, TASK_STACK_SIZE) \ + TASK_ALWAYS(HOSTCMD, host_command_task, NULL, TASK_STACK_SIZE) \ + TASK_ALWAYS(POWERBTN, power_button_task, NULL, TASK_STACK_SIZE) \ + TASK_NOTEST(KEYPROTO, keyboard_protocol_task, NULL, TASK_STACK_SIZE) \ + TASK_NOTEST(KEYSCAN, keyboard_scan_task, NULL, TASK_STACK_SIZE) \ + TASK_NOTEST(CHIPSET, chipset_task, NULL, TASK_STACK_SIZE) \ + TASK_ALWAYS(CONSOLE, console_task, NULL, TASK_STACK_SIZE) diff --git a/board/strago/gpio.inc b/board/strago/gpio.inc new file mode 100644 index 0000000000..ab33ba9d8e --- /dev/null +++ b/board/strago/gpio.inc @@ -0,0 +1,53 @@ +/* -*- mode:c -*- + * + * Copyright (c) 2014 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. + */ + +GPIO(POWER_BUTTON_L, PORT(3), 5, GPIO_INT_BOTH_DSLEEP, power_button_interrupt) /* Power button */ +GPIO(LID_OPEN, PORT(2), 7, GPIO_INT_BOTH_DSLEEP, lid_interrupt) /* Lid switch */ +GPIO(PCH_PWRBTN_L, PORT(16), 0, GPIO_OUT_HIGH, NULL) /* Power button output to PCH */ +GPIO(STARTUP_LATCH_SET, PORT(4), 6, GPIO_OUT_HIGH, NULL) /* To enable power button detection */ + +GPIO(AC_PRESENT, PORT(3), 0, GPIO_INT_BOTH_DSLEEP, extpower_interrupt) /* BC_ACOK / EC_ACIN - to know if battery or AC connected */ +GPIO(RSMRST_L_PGOOD, PORT(6), 3, GPIO_INT_BOTH, power_signal_interrupt) /* RSMRST_N_PWRGD from power logic */ +GPIO(PCH_RSMRST_L, PORT(14), 3, GPIO_OUT_LOW, NULL) /* RSMRST_N to PCH */ +GPIO(PCH_SLP_S4_L, PORT(20), 0, GPIO_INT_BOTH_DSLEEP | GPIO_PULL_UP, power_signal_interrupt) /* SLP_S4# signal from PCH */ +GPIO(PCH_SLP_S3_L, PORT(20), 6, GPIO_INT_BOTH_DSLEEP | GPIO_PULL_UP, power_signal_interrupt) /* SLP_S3# signal from PCH */ +GPIO(ALL_SYS_PGOOD, PORT(13), 0, GPIO_INT_BOTH_DSLEEP, power_signal_interrupt) /* ALL_SYS_PWRGD from power logic */ +GPIO(PCH_SYS_PWROK, PORT(6), 5, GPIO_OUT_LOW, NULL) /* EC thinks everything is up and ready (DELAY_ALL_SYS_PWRGD) */ + +GPIO(USB3_PWR_EN, PORT(5), 7, GPIO_OUT_HIGH, NULL) /* Enable power for USB3 Port */ +GPIO(USB2_PWR_EN, PORT(3), 6, GPIO_OUT_HIGH, NULL) /* Enable power for USB2 Port */ +GPIO(USB_CTL1, PORT(10), 5, GPIO_OUT_HIGH, NULL) /* USB charging mode control */ +GPIO(USB_ILIM_SEL, PORT(1), 3, GPIO_OUT_HIGH, NULL) /* USB current control */ + +GPIO(KBD_IRQ_L, PORT(15), 2, GPIO_ODR_HIGH, NULL) /* Negative edge triggered irq. */ + +UNIMPLEMENTED(CPU_PROCHOT) +UNIMPLEMENTED(PCH_RCIN_L) + +GPIO(PCH_SMI_L, PORT(4), 4, GPIO_ODR_HIGH, NULL) /* SMI output */ +GPIO(PCH_WAKE_L, PORT(6), 6, GPIO_ODR_HIGH, NULL) /* PCH wake pin */ +GPIO(KBD_KSO2, PORT(10), 1, GPIO_KB_OUTPUT_COL2, NULL) /* Negative edge triggered irq. */ + +/* + * Signals which aren't implemented on MEC1322 eval board but we'll + * emulate anyway, to make it more convenient to debug other code. + */ +UNIMPLEMENTED(ENTERING_RW) /* EC entering RW code */ + +/* Alternate functions GPIO definition */ +ALTERNATE(PORT(16), 0x24, 1, MODULE_UART, 0) /* UART0 */ +ALTERNATE(PORT(0), 0x3f, 3, MODULE_KEYBOARD_SCAN, GPIO_KB_OUTPUT) +ALTERNATE(PORT(10), 0xdd, 3, MODULE_KEYBOARD_SCAN, GPIO_KB_OUTPUT) +ALTERNATE(PORT(3), 0x04, 3, MODULE_KEYBOARD_SCAN, GPIO_KB_INPUT) +ALTERNATE(PORT(4), 0x0d, 3, MODULE_KEYBOARD_SCAN, GPIO_KB_INPUT) +ALTERNATE(PORT(12), 0x60, 2, MODULE_KEYBOARD_SCAN, GPIO_KB_INPUT) +ALTERNATE(PORT(14), 0x14, 3, MODULE_KEYBOARD_SCAN, GPIO_KB_INPUT) +ALTERNATE(PORT(1), 0x10, 1, MODULE_LPC, 0) /* 14: CLKRUN# */ +ALTERNATE(PORT(11), 0x9e, 1, MODULE_LPC, 0) /* 111~114:LAD[0:3], 117:PCI_CLK */ +ALTERNATE(PORT(11), 0x40, 1, MODULE_LPC, GPIO_INT_BOTH) /* 116: LRESET# */ +ALTERNATE(PORT(12), 0x01, 1, MODULE_LPC, 0) /* 120: LFRAME# */ +ALTERNATE(PORT(5), 0x10, 1, MODULE_SPI, 0) diff --git a/chip/mec1322/keyboard_raw.c b/chip/mec1322/keyboard_raw.c index 155ff839f2..da1b626db8 100644 --- a/chip/mec1322/keyboard_raw.c +++ b/chip/mec1322/keyboard_raw.c @@ -31,12 +31,29 @@ void keyboard_raw_task_start(void) test_mockable void keyboard_raw_drive_column(int out) { - if (out == KEYBOARD_COLUMN_ALL) + if (out == KEYBOARD_COLUMN_ALL) { MEC1322_KS_KSO_SEL = 1 << 5; /* KSEN=0, KSALL=1 */ - else if (out == KEYBOARD_COLUMN_NONE) +#ifdef CONFIG_KEYBOARD_COL2_INVERTED + gpio_set_level(GPIO_KBD_KSO2, 1); +#endif + } else if (out == KEYBOARD_COLUMN_NONE) { MEC1322_KS_KSO_SEL = 1 << 6; /* KSEN=1 */ - else - MEC1322_KS_KSO_SEL = out + 4; /* KSO starts from KSO04 */ +#ifdef CONFIG_KEYBOARD_COL2_INVERTED + gpio_set_level(GPIO_KBD_KSO2, 0); +#endif + } else { +#ifdef CONFIG_KEYBOARD_COL2_INVERTED + if (out == 2) { + MEC1322_KS_KSO_SEL = 1 << 6; /* KSEN=1 */ + gpio_set_level(GPIO_KBD_KSO2, 1); + } else { + MEC1322_KS_KSO_SEL = out; + gpio_set_level(GPIO_KBD_KSO2, 0); + } +#else + MEC1322_KS_KSO_SEL = out; +#endif + } } test_mockable int keyboard_raw_read_rows(void) @@ -55,7 +72,7 @@ void keyboard_raw_enable_interrupt(int enable) } } -static void keyboard_raw_interrupt(void) +void keyboard_raw_interrupt(void) { /* Clear interrupt status bits */ MEC1322_KS_KSI_STATUS = 0xff; diff --git a/chip/mec1322/lpc.c b/chip/mec1322/lpc.c index 265b7598e9..07e75f2a09 100644 --- a/chip/mec1322/lpc.c +++ b/chip/mec1322/lpc.c @@ -18,6 +18,10 @@ #include "timer.h" #include "util.h" +/* Console output macros */ +#define CPUTS(outstr) cputs(CC_LPC, outstr) +#define CPRINTS(format, args...) cprints(CC_LPC, format, ## args) + static uint8_t mem_mapped[0x200] __attribute__((section(".bss.big_align"))); static uint32_t host_events; /* Currently pending SCI/SMI events */ @@ -32,6 +36,34 @@ static int init_done; static struct ec_lpc_host_args * const lpc_host_args = (struct ec_lpc_host_args *)mem_mapped; + +#ifdef CONFIG_KEYBOARD_IRQ_GPIO +static void keyboard_irq_assert(void) +{ + /* + * Enforce signal-high for long enough for the signal to be pulled high + * by the external pullup resistor. This ensures the host will see the + * following falling edge, regardless of the line state before this + * function call. + */ + gpio_set_level(CONFIG_KEYBOARD_IRQ_GPIO, 1); + udelay(4); + /* Generate a falling edge */ + gpio_set_level(CONFIG_KEYBOARD_IRQ_GPIO, 0); + udelay(4); + + /* Set signal high, now that we've generated the edge */ + gpio_set_level(CONFIG_KEYBOARD_IRQ_GPIO, 1); +} +#else +static void keyboard_irq_assert(void) +{ + /* + * TODO(crosbug.com/p/24107): Implement SER_IRQ + */ +} +#endif + /** * Generate SMI pulse to the host chipset via GPIO. * @@ -158,14 +190,14 @@ static void lpc_send_response_packet(struct host_packet *pkt) */ static void setup_lpc(void) { - uintptr_t ptr = (uintptr_t)mem_mapped; - - /* EMI module only takes alias memory address */ - if (ptr < 0x120000) - ptr = ptr - 0x118000 + 0x20000000; - gpio_config_module(MODULE_LPC, 1); + /* Set up interrupt on LRESET# deassert */ + MEC1322_INT_SOURCE(19) |= 1 << 1; + MEC1322_INT_ENABLE(19) |= 1 << 1; + MEC1322_INT_BLK_EN |= 1 << 19; + task_enable_irq(MEC1322_IRQ_GIRQ19); + /* Set up ACPI0 for 0x62/0x66 */ MEC1322_LPC_ACPI_EC0_BAR = 0x00628034; MEC1322_INT_ENABLE(15) |= 1 << 6; @@ -181,18 +213,15 @@ static void setup_lpc(void) /* Set up 8042 interface at 0x60/0x64 */ MEC1322_LPC_8042_BAR = 0x00608104; MEC1322_8042_ACT |= 1; - MEC1322_INT_ENABLE(15) |= 1 << 14; + MEC1322_INT_ENABLE(15) |= ((1 << 13) | (1 << 14)); MEC1322_INT_BLK_EN |= 1 << 15; task_enable_irq(MEC1322_IRQ_8042EM_IBF); + task_enable_irq(MEC1322_IRQ_8042EM_OBF); /* TODO(crosbug.com/p/24107): Route KIRQ to SER_IRQ1 */ /* Set up EMI module for memory mapped region and port 80 */ MEC1322_LPC_EMI_BAR = 0x0080800f; - MEC1322_EMI_MBA0 = ptr; - MEC1322_EMI_MRL0 = 0x200; - MEC1322_EMI_MWL0 = 0x100; - MEC1322_INT_ENABLE(15) |= 1 << 2; MEC1322_INT_BLK_EN |= 1 << 15; task_enable_irq(MEC1322_IRQ_EMI); @@ -225,6 +254,27 @@ static void lpc_init(void) */ DECLARE_HOOK(HOOK_INIT, lpc_init, HOOK_PRIO_INIT_LPC); +void girq19_interrupt(void) +{ + /* Check interrupt result for LRESET# trigger */ + if (MEC1322_INT_RESULT(19) & (1 << 1)) { + /* Initialize LPC module when LRESET# is deasserted */ + if (!lpc_get_pltrst_asserted()) { + setup_lpc(); + } else { + /* Store port 80 reset event */ + port_80_write(PORT_80_EVENT_RESET); + } + + CPRINTS("LPC RESET# %sasserted", + lpc_get_pltrst_asserted() ? "" : "de"); + + /* Clear interrupt source */ + MEC1322_INT_SOURCE(19) |= 1 << 1; + } +} +DECLARE_IRQ(MEC1322_IRQ_GIRQ19, girq19_interrupt, 1); + void emi_interrupt(void) { port_80_write(MEC1322_EMI_H2E_MBX); @@ -313,6 +363,12 @@ void kb_ibf_interrupt(void) task_wake(TASK_ID_KEYPROTO); } DECLARE_IRQ(MEC1322_IRQ_8042EM_IBF, kb_ibf_interrupt, 1); + +void kb_obf_interrupt(void) +{ + task_wake(TASK_ID_KEYPROTO); +} +DECLARE_IRQ(MEC1322_IRQ_8042EM_OBF, kb_obf_interrupt, 1); #endif int lpc_keyboard_has_char(void) @@ -328,21 +384,21 @@ int lpc_keyboard_input_pending(void) void lpc_keyboard_put_char(uint8_t chr, int send_irq) { MEC1322_8042_E2H = chr; - /* - * TODO(crosbug.com/p/24107): Implement SER_IRQ and handle - * send_irq. - */ + if (send_irq) + keyboard_irq_assert(); } void lpc_keyboard_clear_buffer(void) { volatile char dummy __attribute__((unused)); + dummy = MEC1322_8042_OBF_CLR; } void lpc_keyboard_resume_irq(void) { - /* TODO(crosbug.com/p/24107): Implement SER_IRQ */ + if (lpc_keyboard_has_char()) + keyboard_irq_assert(); } void lpc_set_host_event_state(uint32_t mask) @@ -393,6 +449,11 @@ uint32_t lpc_get_host_event_mask(enum lpc_host_event_type type) return event_mask[type]; } +int lpc_get_pltrst_asserted(void) +{ + return (MEC1322_LPC_BUS_MONITOR & (1<<1)) ? 1 : 0; +} + /* On boards without a host, this command is used to set up LPC */ static int lpc_command_init(int argc, char **argv) { diff --git a/chip/mec1322/registers.h b/chip/mec1322/registers.h index 2ed60c9db1..ca06cb25f5 100644 --- a/chip/mec1322/registers.h +++ b/chip/mec1322/registers.h @@ -150,6 +150,7 @@ static inline uintptr_t gpio_port_base(int port_id) #define MEC1322_LPC_MEM_BAR_CFG REG32(MEC1322_LPC_CFG_BASE + 0xa4) #define MEC1322_LPC_RT_BASE 0x400f3100 +#define MEC1322_LPC_BUS_MONITOR REG32(MEC1322_LPC_RT_BASE + 0x4) #define MEC1322_LPC_MEM_HOST_CFG REG32(MEC1322_LPC_RT_BASE + 0xfc) diff --git a/common/power_button_x86.c b/common/power_button_x86.c index 539bfce347..8b221b8e30 100644 --- a/common/power_button_x86.c +++ b/common/power_button_x86.c @@ -118,11 +118,12 @@ static void set_pwrbtn_to_pch(int high) * If the battery is discharging and low enough we'd shut down the * system, don't press the power button. */ +#ifdef CONFIG_CHARGER if (!high && charge_want_shutdown()) { CPRINTS("PB PCH pwrbtn ignored due to battery level"); high = 1; } - +#endif CPRINTS("PB PCH pwrbtn=%s", high ? "HIGH" : "LOW"); gpio_set_level(GPIO_PCH_PWRBTN_L, high); } diff --git a/include/config.h b/include/config.h index c7ebc9a1a8..92cd267121 100644 --- a/include/config.h +++ b/include/config.h @@ -282,6 +282,7 @@ #undef CONFIG_CHIPSET_IVYBRIDGE /* Intel Ivy Bridge (x86) */ #undef CONFIG_CHIPSET_ROCKCHIP /* Rockchip rk32xx */ #undef CONFIG_CHIPSET_TEGRA /* nVidia Tegra 5 */ +#undef CONFIG_CHIPSET_BRASWELL /* Intel Braswell (x86) */ /* Support chipset throttling */ #undef CONFIG_CHIPSET_CAN_THROTTLE diff --git a/power/braswell.c b/power/braswell.c new file mode 100644 index 0000000000..818c610f44 --- /dev/null +++ b/power/braswell.c @@ -0,0 +1,327 @@ +/* Copyright (c) 2014 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 braswell chipset power control module for Chrome EC */ + +#include "chipset.h" +#include "common.h" +#include "console.h" +#include "ec_commands.h" +#include "gpio.h" +#include "hooks.h" +#include "host_command.h" +#include "lid_switch.h" +#include "lpc.h" +#include "power.h" +#include "power_button.h" +#include "system.h" +#include "timer.h" +#include "usb_charge.h" +#include "util.h" +#include "wireless.h" +#include "registers.h" + +/* Console output macros */ +#define CPUTS(outstr) cputs(CC_CHIPSET, outstr) +#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) + +/* Input state flags */ +#define IN_RSMRST_L_PWRGD POWER_SIGNAL_MASK(X86_RSMRST_L_PWRGD) +#define IN_ALL_SYS_PWRGD POWER_SIGNAL_MASK(X86_ALL_SYS_PWRGD) +#define IN_SLP_S3_DEASSERTED POWER_SIGNAL_MASK(X86_SLP_S3_DEASSERTED) +#define IN_SLP_S4_DEASSERTED POWER_SIGNAL_MASK(X86_SLP_S4_DEASSERTED) + +/* All always-on supplies */ +#define IN_PGOOD_ALWAYS_ON (IN_RSMRST_L_PWRGD) +/* All non-core power rails */ +#define IN_PGOOD_ALL_NONCORE (IN_ALL_SYS_PWRGD) +/* All core power rails */ +#define IN_PGOOD_ALL_CORE (IN_ALL_SYS_PWRGD) +/* Rails required for S5 */ +#define IN_PGOOD_S5 (IN_PGOOD_ALWAYS_ON) +/* Rails required for S3 */ +#define IN_PGOOD_S3 (IN_PGOOD_ALWAYS_ON) +/* Rails required for S0 */ +#define IN_PGOOD_S0 (IN_PGOOD_ALWAYS_ON | IN_PGOOD_ALL_NONCORE) + +/* All PM_SLP signals from PCH deasserted */ +#define IN_ALL_PM_SLP_DEASSERTED (IN_SLP_S3_DEASSERTED | IN_SLP_S4_DEASSERTED) +/* All inputs in the right state for S0 */ +#define IN_ALL_S0 (IN_PGOOD_ALWAYS_ON | IN_PGOOD_ALL_NONCORE | \ + IN_PGOOD_ALL_CORE | IN_ALL_PM_SLP_DEASSERTED) + +static int throttle_cpu; /* Throttle CPU? */ +static int pause_in_s5 = 1; /* Pause in S5 when shutting down? */ + +void chipset_force_shutdown(void) +{ + CPRINTS("%s()", __func__); + + /* + * Force power off. This condition will reset once the state machine + * transitions to G3. + */ + gpio_set_level(GPIO_PCH_SYS_PWROK, 0); + gpio_set_level(GPIO_PCH_RSMRST_L, 0); +} + +void chipset_reset(int cold_reset) +{ + CPRINTS("%s(%d)", __func__, cold_reset); + if (cold_reset) { + /* + * Drop and restore PWROK. This causes the PCH to reboot, + * regardless of its after-G3 setting. This type of reboot + * causes the PCH to assert PLTRST#, SLP_S3#, and SLP_S5#, so + * we actually drop power to the rest of the system (hence, a + * "cold" reboot). + */ + + /* Ignore if PWROK is already low */ + if (gpio_get_level(GPIO_PCH_SYS_PWROK) == 0) + return; + + /* PWROK must deassert for at least 3 RTC clocks = 91 us */ + gpio_set_level(GPIO_PCH_SYS_PWROK, 0); + udelay(100); + gpio_set_level(GPIO_PCH_SYS_PWROK, 1); + + } else { + /* + * Send a reset pulse to the PCH. This just causes it to + * assert INIT# to the CPU without dropping power or asserting + * PLTRST# to reset the rest of the system. The PCH uses a 16 + * ms debounce time, so assert the signal for twice that. + */ + gpio_set_level(GPIO_PCH_RCIN_L, 0); + usleep(32 * MSEC); + gpio_set_level(GPIO_PCH_RCIN_L, 1); + } +} + +void chipset_throttle_cpu(int throttle) +{ + if (chipset_in_state(CHIPSET_STATE_ON)) + gpio_set_level(GPIO_CPU_PROCHOT, throttle); +} + +enum power_state power_chipset_init(void) +{ + /* + * If we're switching between images without rebooting, see if the x86 + * is already powered on; if so, leave it there instead of cycling + * through G3. + */ + if (system_jumped_to_this_image()) { + if ((power_get_signals() & IN_PGOOD_S0) == IN_PGOOD_S0) { + /* Disable idle task deep sleep when in S0. */ + disable_sleep(SLEEP_MASK_AP_RUN); + + CPRINTS("already in S0"); + return POWER_S0; + } else { + /* Force all signals to their G3 states */ + CPRINTS("forcing G3"); + gpio_set_level(GPIO_PCH_SYS_PWROK, 0); + gpio_set_level(GPIO_PCH_RSMRST_L, 0); + + /*wireless_set_state(WIRELESS_OFF);*/ + } + } + + return POWER_G3; +} + +enum power_state power_handle_state(enum power_state state) +{ + switch (state) { + case POWER_G3: + break; + + case POWER_G3S5: + if (power_wait_signals(IN_PGOOD_S5)) { + chipset_force_shutdown(); + return POWER_G3; + } + + /* Deassert RSMRST# */ + gpio_set_level(GPIO_PCH_RSMRST_L, 1); + return POWER_S5; + + case POWER_S5: + /* Check for SLP S4 */ + if (gpio_get_level(GPIO_PCH_SLP_S4_L) == 1) + return POWER_S5S3; /* Power up to next state */ + break; + + case POWER_S5S3: + + /* Call hooks now that rails are up */ + hook_notify(HOOK_CHIPSET_STARTUP); + + return POWER_S3; + + + case POWER_S3: + + /* Check for state transitions */ + if (!power_has_signals(IN_PGOOD_S3)) { + /* Required rail went away */ + chipset_force_shutdown(); + return POWER_S3S5; + } else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 1) { + /* Power up to next state */ + return POWER_S3S0; + } else if (gpio_get_level(GPIO_PCH_SLP_S4_L) == 0) { + /* Power down to next state */ + return POWER_S3S5; + } + break; + + case POWER_S3S0: + /* Enable wireless */ + + /*wireless_set_state(WIRELESS_ON);*/ + + if (power_wait_signals(IN_PGOOD_S0)) { + chipset_force_shutdown(); + + /*wireless_set_state(WIRELESS_OFF);*/ + return POWER_S3; + } + + /* Call hooks now that rails are up */ + hook_notify(HOOK_CHIPSET_RESUME); + + /* + * Disable idle task deep sleep. This means that the low + * power idle task will not go into deep sleep while in S0. + */ + disable_sleep(SLEEP_MASK_AP_RUN); + + /* + * Wait 15 ms after all voltages good. 100 ms is only needed + * for PCIe devices; mini-PCIe devices should need only 10 ms. + */ + msleep(15); + + /* + * Throttle CPU if necessary. This should only be asserted + * when +VCCP is powered (it is by now). + */ + gpio_set_level(GPIO_CPU_PROCHOT, throttle_cpu); + + /* Set SYS and CORE PWROK */ + gpio_set_level(GPIO_PCH_SYS_PWROK, 1); + + /* Wait 50 ms for platform reset to deassert */ + { + int i = 0; + CPRINTS("power wait for PLTRST# to deassert"); + while (lpc_get_pltrst_asserted()) { + usleep(MSEC); +#ifndef STRAGO_PO + i++; +#endif + if (i >= 50) { + CPRINTS("power timeout on PLTRST#"); + chipset_force_shutdown(); + + /*wireless_set_state(WIRELESS_OFF);*/ + return POWER_S3; + } + } + } + + return POWER_S0; + + + case POWER_S0: + if (!power_has_signals(IN_PGOOD_S0)) { + /* Required rail went away - Cold Reset? */ + chipset_force_shutdown(); + return POWER_S0S3; + } else if (gpio_get_level(GPIO_PCH_SLP_S3_L) == 0) { + /* Power down to next state */ + return POWER_S0S3; + } + + break; + case POWER_S0S3: + /* Call hooks before we remove power rails */ + hook_notify(HOOK_CHIPSET_SUSPEND); + + /* Clear SYS and CORE PWROK */ + gpio_set_level(GPIO_PCH_SYS_PWROK, 0); + + /* Wait 40ns */ + udelay(1); + + /* Suspend wireless */ + + /*wireless_set_state(WIRELESS_SUSPEND);*/ + + /* + * Enable idle task deep sleep. Allow the low power idle task + * to go into deep sleep in S3 or lower. + */ + enable_sleep(SLEEP_MASK_AP_RUN); + + /* + * Deassert prochot since CPU is off and we're about to drop + * +VCCP. + */ + gpio_set_level(GPIO_CPU_PROCHOT, 0); + + return POWER_S3; + + case POWER_S3S5: + /* Call hooks before we remove power rails */ + hook_notify(HOOK_CHIPSET_SHUTDOWN); + + /*wireless_set_state(WIRELESS_OFF);*/ + + /* Start shutting down */ + return pause_in_s5 ? POWER_S5 : POWER_S5G3; + + case POWER_S5G3: + /* Assert RSMRST# */ + gpio_set_level(GPIO_PCH_RSMRST_L, 0); + return POWER_G3; + } + + return state; +} + +static int host_command_gsv(struct host_cmd_handler_args *args) +{ + const struct ec_params_get_set_value *p = args->params; + struct ec_response_get_set_value *r = args->response; + + if (p->flags & EC_GSV_SET) + pause_in_s5 = p->value; + + r->value = pause_in_s5; + + args->response_size = sizeof(*r); + return EC_RES_SUCCESS; +} +DECLARE_HOST_COMMAND(EC_CMD_GSV_PAUSE_IN_S5, + host_command_gsv, + EC_VER_MASK(0)); + +static int console_command_gsv(int argc, char **argv) +{ + if (argc > 1 && !parse_bool(argv[1], &pause_in_s5)) + return EC_ERROR_INVAL; + + ccprintf("pause_in_s5 = %s\n", pause_in_s5 ? "on" : "off"); + + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(pause_in_s5, console_command_gsv, + "[on|off]", + "Should the AP pause in S5 during shutdown?", + NULL); diff --git a/power/build.mk b/power/build.mk index e97371d125..5e62330490 100644 --- a/power/build.mk +++ b/power/build.mk @@ -12,4 +12,5 @@ power-$(CONFIG_CHIPSET_HASWELL)+=haswell.o power-$(CONFIG_CHIPSET_IVYBRIDGE)+=ivybridge.o power-$(CONFIG_CHIPSET_ROCKCHIP)+=rockchip.o power-$(CONFIG_CHIPSET_TEGRA)+=tegra.o +power-$(CONFIG_CHIPSET_BRASWELL)+=braswell.o power-$(CONFIG_POWER_COMMON)+=common.o |