summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYen Lin <yelin@nvidia.com>2013-09-03 12:04:55 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2013-10-17 22:52:12 +0000
commit140404ffdfb54faa432128435e87aacd9a5761a6 (patch)
tree1c5f958d30632c3c1b5443d4117df64eea886bef
parent99472d5cb577681b6b711e001f5a50e6abc2e356 (diff)
downloadchrome-ec-140404ffdfb54faa432128435e87aacd9a5761a6.tar.gz
ec: add nyan board
This is to add nyan board support: - new files in board/nyan folder, including battery.c - new common/chipset_tegra.c, which is mostly based on chipset_gaia.c - new include/tegra_power.h - modified build.mk and flash_ec for nyan BUG=none BRANCH=nyan TEST=tested on Venice 2 board Change-Id: I36895f34f2f4d144a9440aff358c8274797ebbd6 Signed-off-by: Yen Lin <yelin@nvidia.com> Reviewed-on: https://chromium-review.googlesource.com/168078 Reviewed-by: Vincent Palatin <vpalatin@chromium.org> Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r--board/nyan/battery.c131
-rw-r--r--board/nyan/board.c104
-rw-r--r--board/nyan/board.h129
-rw-r--r--board/nyan/build.mk13
-rw-r--r--board/nyan/ec.tasklist25
-rw-r--r--common/build.mk1
-rw-r--r--common/chipset_tegra.c602
-rw-r--r--include/config.h2
-rw-r--r--include/tegra_power.h28
-rwxr-xr-xutil/flash_ec2
10 files changed, 1036 insertions, 1 deletions
diff --git a/board/nyan/battery.c b/board/nyan/battery.c
new file mode 100644
index 0000000000..df29c7fd9b
--- /dev/null
+++ b/board/nyan/battery.c
@@ -0,0 +1,131 @@
+/* Copyright (c) 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.
+ *
+ * Battery pack vendor provided charging profile
+ */
+
+#include "battery.h"
+
+/*
+ * Design capacity
+ * Battery capacity = 8200 mAh
+ * 1C = 8200 mA
+ */
+#define DESIGN_CAPACITY 8200
+
+enum {
+ TEMP_RANGE_10,
+ TEMP_RANGE_23,
+ TEMP_RANGE_35,
+ TEMP_RANGE_45,
+ TEMP_RANGE_50,
+ TEMP_RANGE_MAX
+};
+
+enum {
+ VOLT_RANGE_7200,
+ VOLT_RANGE_8000,
+ VOLT_RANGE_8400,
+ VOLT_RANGE_MAX
+};
+
+/*
+ * Vendor provided charging method
+ * temp : < 7.2V, 7.2V ~ 8.0V, 8.0V ~ 8.4V
+ * - 0 ~ 10 : 0.8A 1.6A 0.8A
+ * - 10 ~ 23 : 1.6A 4.0A 1.6A
+ * - 23 ~ 35 : 4.0A 4.0A 4.0A
+ * - 35 ~ 45 : 1.6A 4.0A 1.6A
+ * - 45 ~ 50 : 0.8A 1.6A 0.8A
+ */
+static const int const current_limit[TEMP_RANGE_MAX][VOLT_RANGE_MAX] = {
+ { 800, 1600, 800},
+ {1600, 4000, 1600},
+ {4000, 4000, 4000},
+ {1600, 4000, 1600},
+ { 800, 1600, 800},
+};
+
+const struct battery_temperature_ranges bat_temp_ranges = {
+ /*
+ * Operational temperature range
+ * 0 <= T_charge <= 50 deg C
+ * -20 <= T_discharge <= 60 deg C
+ */
+ .start_charging_min_c = 0,
+ .start_charging_max_c = 50,
+ .charging_min_c = 0,
+ .charging_max_c = 50,
+ .discharging_min_c = -20,
+ .discharging_max_c = 60,
+};
+
+static const struct battery_info info = {
+ /*
+ * Design voltage
+ * max = 8.4V
+ * normal = 7.4V
+ * min = 6.0V
+ */
+ .voltage_max = 8400,
+ .voltage_normal = 7400,
+ .voltage_min = 6000,
+
+ /* Pre-charge current: I <= 0.01C */
+ .precharge_current = 64, /* mA */
+};
+
+static inline void limit_value(int *val, int limit)
+{
+ if (*val > limit)
+ *val = limit;
+}
+
+const struct battery_info *battery_get_info(void)
+{
+ return &info;
+}
+
+void battery_vendor_params(struct batt_params *batt)
+{
+ int *desired_current = &batt->desired_current;
+ int temp_range, volt_range;
+ int bat_temp_c = DECI_KELVIN_TO_CELSIUS(batt->temperature);
+
+ /* Limit charging voltage */
+ if (batt->desired_voltage > info.voltage_max)
+ batt->desired_voltage = info.voltage_max;
+
+ /* Don't charge if outside of allowable temperature range */
+ if (bat_temp_c >= bat_temp_ranges.charging_max_c ||
+ bat_temp_c < bat_temp_ranges.charging_min_c) {
+ batt->desired_voltage = 0;
+ batt->desired_current = 0;
+ return;
+ }
+
+ if (bat_temp_c <= 10)
+ temp_range = TEMP_RANGE_10;
+ else if (bat_temp_c <= 23)
+ temp_range = TEMP_RANGE_23;
+ else if (bat_temp_c <= 35)
+ temp_range = TEMP_RANGE_35;
+ else if (bat_temp_c <= 45)
+ temp_range = TEMP_RANGE_45;
+ else
+ temp_range = TEMP_RANGE_50;
+
+ if (batt->voltage < 7200)
+ volt_range = VOLT_RANGE_7200;
+ else if (batt->voltage < 8000)
+ volt_range = VOLT_RANGE_8000;
+ else
+ volt_range = VOLT_RANGE_8400;
+
+ limit_value(desired_current, current_limit[temp_range][volt_range]);
+
+ /* If battery wants current, give it at least the precharge current */
+ if (*desired_current > 0 && *desired_current < info.precharge_current)
+ *desired_current = info.precharge_current;
+}
diff --git a/board/nyan/board.c b/board/nyan/board.c
new file mode 100644
index 0000000000..ff052d5644
--- /dev/null
+++ b/board/nyan/board.c
@@ -0,0 +1,104 @@
+/* Copyright (c) 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.
+ */
+/* Nyan board-specific configuration */
+
+#include "battery.h"
+#include "common.h"
+#include "extpower.h"
+#include "tegra_power.h"
+#include "gpio.h"
+#include "i2c.h"
+#include "keyboard_raw.h"
+#include "lid_switch.h"
+#include "pmu_tpschrome.h"
+#include "pwm.h"
+#include "pwm_data.h"
+#include "registers.h"
+#include "spi.h"
+#include "task.h"
+#include "util.h"
+#include "timer.h"
+
+#define GPIO_KB_INPUT (GPIO_INPUT | GPIO_PULL_UP | GPIO_INT_BOTH)
+#define GPIO_KB_OUTPUT GPIO_ODR_HIGH
+
+/* GPIO signal list. Must match order from enum gpio_signal. */
+const struct gpio_info gpio_list[] = {
+ /* Inputs with interrupt handlers are first for efficiency */
+ {"KB_PWR_ON_L", GPIO_B, (1<<5), GPIO_INT_BOTH, tegra_power_event},
+ {"XPSHOLD", GPIO_A, (1<<3), GPIO_INT_BOTH, tegra_power_event},
+ {"CHARGER_INT", GPIO_C, (1<<6), GPIO_INT_RISING, NULL},
+ {"LID_OPEN", GPIO_C, (1<<13), GPIO_INT_BOTH, lid_interrupt},
+ {"SUSPEND_L", GPIO_C, (1<<7), GPIO_KB_INPUT, tegra_suspend_event},
+ {"SPI1_NSS", GPIO_A, (1<<4), GPIO_INT_BOTH | GPIO_PULL_UP,
+ spi_event},
+ {"AC_PRESENT", GPIO_A, (1<<0), GPIO_INT_BOTH, extpower_interrupt},
+ {"KB_IN00", GPIO_C, (1<<8), GPIO_KB_INPUT,
+ keyboard_raw_gpio_interrupt},
+ {"KB_IN01", GPIO_C, (1<<9), GPIO_KB_INPUT,
+ keyboard_raw_gpio_interrupt},
+ {"KB_IN02", GPIO_C, (1<<10), GPIO_KB_INPUT,
+ keyboard_raw_gpio_interrupt},
+ {"KB_IN03", GPIO_C, (1<<11), GPIO_KB_INPUT,
+ keyboard_raw_gpio_interrupt},
+ {"KB_IN04", GPIO_C, (1<<12), GPIO_KB_INPUT,
+ keyboard_raw_gpio_interrupt},
+ {"KB_IN05", GPIO_C, (1<<14), GPIO_KB_INPUT,
+ keyboard_raw_gpio_interrupt},
+ {"KB_IN06", GPIO_C, (1<<15), GPIO_KB_INPUT,
+ keyboard_raw_gpio_interrupt},
+ {"KB_IN07", GPIO_D, (1<<2), GPIO_KB_INPUT,
+ keyboard_raw_gpio_interrupt},
+ /* Other inputs */
+ {"WP_L", GPIO_B, (1<<4), GPIO_INPUT, NULL},
+ /* Outputs */
+ {"AP_RESET_L", GPIO_B, (1<<3), GPIO_OUT_LOW, NULL},
+ {"CHARGER_EN", GPIO_B, (1<<2), GPIO_OUT_LOW, NULL},
+ {"EC_INT", GPIO_B, (1<<9), GPIO_ODR_HIGH, NULL},
+ {"ENTERING_RW", GPIO_H, (1<<0), GPIO_OUT_LOW, NULL},
+ {"I2C1_SCL", GPIO_B, (1<<6), GPIO_ODR_HIGH, NULL},
+ {"I2C1_SDA", GPIO_B, (1<<7), GPIO_ODR_HIGH, NULL},
+ {"I2C2_SCL", GPIO_B, (1<<10), GPIO_ODR_HIGH, NULL},
+ {"I2C2_SDA", GPIO_B, (1<<11), GPIO_ODR_HIGH, NULL},
+ {"LED_POWER_L", GPIO_A, (1<<2), GPIO_OUT_HIGH, NULL},
+ {"PMIC_PWRON_L", GPIO_A, (1<<12), GPIO_OUT_HIGH, NULL},
+ {"PMIC_RESET", GPIO_A, (1<<15), GPIO_OUT_LOW, NULL},
+ {"KB_OUT00", GPIO_B, (1<<0), GPIO_KB_OUTPUT, NULL},
+ {"KB_OUT01", GPIO_B, (1<<8), GPIO_KB_OUTPUT, NULL},
+ {"KB_OUT02", GPIO_B, (1<<12), GPIO_KB_OUTPUT, NULL},
+ {"KB_OUT03", GPIO_B, (1<<13), GPIO_KB_OUTPUT, NULL},
+ {"KB_OUT04", GPIO_B, (1<<14), GPIO_KB_OUTPUT, NULL},
+ {"KB_OUT05", GPIO_B, (1<<15), GPIO_KB_OUTPUT, NULL},
+ {"KB_OUT06", GPIO_C, (1<<0), GPIO_KB_OUTPUT, NULL},
+ {"KB_OUT07", GPIO_C, (1<<1), GPIO_KB_OUTPUT, NULL},
+ {"KB_OUT08", GPIO_C, (1<<2), GPIO_KB_OUTPUT, NULL},
+ {"KB_OUT09", GPIO_B, (1<<1), GPIO_KB_OUTPUT, NULL},
+ {"KB_OUT10", GPIO_C, (1<<5), GPIO_KB_OUTPUT, NULL},
+ {"KB_OUT11", GPIO_C, (1<<4), GPIO_KB_OUTPUT, NULL},
+ {"KB_OUT12", GPIO_A, (1<<13), GPIO_KB_OUTPUT, NULL},
+};
+BUILD_ASSERT(ARRAY_SIZE(gpio_list) == GPIO_COUNT);
+
+/* Pins with alternate functions */
+const struct gpio_alt_func gpio_alt_funcs[] = {
+ {GPIO_A, 0x0004, GPIO_ALT_TIM2, MODULE_POWER_LED},
+ {GPIO_A, 0x00f0, GPIO_ALT_SPI, MODULE_SPI},
+ {GPIO_A, 0x0600, GPIO_ALT_USART, MODULE_UART},
+ {GPIO_B, 0x0cc0, GPIO_ALT_I2C, MODULE_I2C},
+};
+const int gpio_alt_funcs_count = ARRAY_SIZE(gpio_alt_funcs);
+
+/* I2C ports */
+const struct i2c_port_t i2c_ports[] = {
+ {"host", I2C_PORT_HOST, 100},
+};
+const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports);
+
+/* PWM channels. Must be in the exactly same order as in enum pwm_channel. */
+const struct pwm_t pwm_channels[] = {
+ {STM32_TIM(2), STM32_TIM_CH(3),
+ PWM_CONFIG_ACTIVE_LOW, GPIO_LED_POWER_L},
+};
+BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT);
diff --git a/board/nyan/board.h b/board/nyan/board.h
new file mode 100644
index 0000000000..6ea63a06dd
--- /dev/null
+++ b/board/nyan/board.h
@@ -0,0 +1,129 @@
+/* Copyright (c) 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.
+ */
+
+/* Nyan board configuration */
+
+#ifndef __BOARD_H
+#define __BOARD_H
+
+/* Optional features */
+#define CONFIG_BATTERY_SMART
+#define CONFIG_CHARGER
+#define CONFIG_CHARGER_BQ24725
+#define CONFIG_CHIPSET_TEGRA
+#define CONFIG_EXTPOWER_GPIO
+#define CONFIG_HOST_COMMAND_STATUS
+#define CONFIG_I2C
+#define CONFIG_KEYBOARD_PROTOCOL_MKBP
+#define CONFIG_SPI
+#define CONFIG_PWM
+
+#ifndef __ASSEMBLER__
+
+/* Module IDs */
+/* TODO(rspangler): use this in place of enum console_channel as well */
+enum module_id {
+ MODULE_I2C,
+ MODULE_POWER_LED,
+ MODULE_SPI,
+ MODULE_UART,
+ MODULE_CHIPSET,
+};
+
+/* By default, enable all console messages except keyboard */
+#define CC_DEFAULT (CC_ALL & ~CC_MASK(CC_KEYSCAN))
+
+/* Keyboard output port list */
+#define KB_OUT_PORT_LIST GPIO_A, GPIO_B, GPIO_C
+
+/*
+ * Charging.
+ *
+ * "HOST" means the port where the EC is the master, which has the battery,
+ * charger and PMU.
+ *
+ * "SLAVE" means the port where the EC is the slave, which has the AP (host
+ * processor).
+ *
+ * TODO: In this context, "host" is badly overloaded and confusing.
+ */
+#define I2C_PORT_HOST 0
+#define I2C_PORT_BATTERY I2C_PORT_HOST
+#define I2C_PORT_CHARGER I2C_PORT_HOST
+#define I2C_PORT_SLAVE 1
+#define I2C_PORTS_USED 1
+
+/* Timer selection */
+#define TIM_CLOCK_MSB 3
+#define TIM_CLOCK_LSB 9
+#define TIM_POWER_LED 2
+#define TIM_WATCHDOG 4
+
+/* GPIO signal list */
+enum gpio_signal {
+ /* Inputs with interrupt handlers are first for efficiency */
+ GPIO_KB_PWR_ON_L = 0,
+ GPIO_SOC1V8_XPSHOLD,
+ GPIO_CHARGER_INT,
+ GPIO_LID_OPEN,
+ GPIO_SUSPEND_L,
+ GPIO_SPI1_NSS,
+ GPIO_AC_PRESENT,
+ /* Keyboard inputs */
+ GPIO_KB_IN00,
+ GPIO_KB_IN01,
+ GPIO_KB_IN02,
+ GPIO_KB_IN03,
+ GPIO_KB_IN04,
+ GPIO_KB_IN05,
+ GPIO_KB_IN06,
+ GPIO_KB_IN07,
+ /* Other inputs */
+ GPIO_WP_L,
+ /* Outputs */
+ GPIO_AP_RESET_L,
+ GPIO_CHARGER_EN,
+ GPIO_EC_INT,
+ GPIO_ENTERING_RW,
+ GPIO_I2C1_SCL,
+ GPIO_I2C1_SDA,
+ GPIO_I2C2_SCL,
+ GPIO_I2C2_SDA,
+ GPIO_LED_POWER_L,
+ GPIO_PMIC_PWRON_L,
+ GPIO_PMIC_RESET,
+ GPIO_KB_OUT00,
+ GPIO_KB_OUT01,
+ GPIO_KB_OUT02,
+ GPIO_KB_OUT03,
+ GPIO_KB_OUT04,
+ GPIO_KB_OUT05,
+ GPIO_KB_OUT06,
+ GPIO_KB_OUT07,
+ GPIO_KB_OUT08,
+ GPIO_KB_OUT09,
+ GPIO_KB_OUT10,
+ GPIO_KB_OUT11,
+ GPIO_KB_OUT12,
+ /* Number of GPIOs; not an actual GPIO */
+ GPIO_COUNT
+};
+
+
+enum pwm_channel {
+ PWM_CH_POWER_LED = 0,
+ /* Number of PWM channels */
+ PWM_CH_COUNT
+};
+
+/* Charger module */
+#define CONFIG_CHARGER_SENSE_RESISTOR 10 /* Charge sense resistor, mOhm */
+#define CONFIG_CHARGER_SENSE_RESISTOR_AC 20 /* Input sensor resistor, mOhm */
+#define CONFIG_CHARGER_INPUT_CURRENT 4032 /* mA, based on Link HW design */
+#define CONFIG_CHARGER_CURRENT_LIMIT 3000 /* PL102 inductor 3.0A(3.8A) */
+
+#endif /* !__ASSEMBLER__ */
+
+#endif /* __BOARD_H */
diff --git a/board/nyan/build.mk b/board/nyan/build.mk
new file mode 100644
index 0000000000..17c032ab34
--- /dev/null
+++ b/board/nyan/build.mk
@@ -0,0 +1,13 @@
+# -*- makefile -*-
+# Copyright (c) 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.
+#
+# Board specific files build
+
+# the IC is STmicro STM32L151R8T6
+CHIP:=stm32
+CHIP_FAMILY:=stm32l
+CHIP_VARIANT:=stm32l15x
+
+board-y=board.o battery.o
diff --git a/board/nyan/ec.tasklist b/board/nyan/ec.tasklist
new file mode 100644
index 0000000000..448879d105
--- /dev/null
+++ b/board/nyan/ec.tasklist
@@ -0,0 +1,25 @@
+/* Copyright (c) 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.
+ */
+
+/**
+ * List of enabled tasks in the priority order
+ *
+ * The first one has the lowest priority.
+ *
+ * For each task, use the macro TASK(n, r, d, s) 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_NOTEST(VBOOTHASH, vboot_hash_task, NULL, TASK_STACK_SIZE) \
+ TASK_NOTEST(POWERLED, power_led_task, NULL, 256) \
+ TASK_ALWAYS(CHARGER, charger_task, NULL, TASK_STACK_SIZE) \
+ TASK_NOTEST(CHIPSET, chipset_task, NULL, TASK_STACK_SIZE) \
+ TASK_ALWAYS(HOSTCMD, host_command_task, NULL, TASK_STACK_SIZE) \
+ TASK_ALWAYS(CONSOLE, console_task, NULL, TASK_STACK_SIZE) \
+ TASK_NOTEST(KEYSCAN, keyboard_scan_task, NULL, TASK_STACK_SIZE)
diff --git a/common/build.mk b/common/build.mk
index c3e2fcf453..02d841e9ee 100644
--- a/common/build.mk
+++ b/common/build.mk
@@ -25,6 +25,7 @@ common-$(CONFIG_CHARGER_BQ24738)+=charger_bq24738.o
common-$(CONFIG_CHARGER_TPS65090)+=pmu_tps65090_charger.o
common-$(CONFIG_CHIPSET_BAYTRAIL)+=chipset_baytrail.o
common-$(CONFIG_CHIPSET_GAIA)+=chipset_gaia.o
+common-$(CONFIG_CHIPSET_TEGRA)+=chipset_tegra.o
common-$(CONFIG_CHIPSET_HASWELL)+=chipset_haswell.o
common-$(CONFIG_CHIPSET_IVYBRIDGE)+=chipset_ivybridge.o
common-$(CONFIG_CHIPSET_X86)+=chipset_x86_common.o
diff --git a/common/chipset_tegra.c b/common/chipset_tegra.c
new file mode 100644
index 0000000000..7032f969cb
--- /dev/null
+++ b/common/chipset_tegra.c
@@ -0,0 +1,602 @@
+/* Copyright (c) 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.
+ */
+
+/*
+ * TEGRA SoC power sequencing module for Chrome EC
+ *
+ * This implements the following features:
+ *
+ * - Cold reset powers on the AP
+ *
+ * When powered off:
+ * - Press pwron turns on the AP
+ * - Hold pwron turns on the AP, and then 9s later turns it off and leaves
+ * it off until pwron is released and pressed again
+ *
+ * When powered on:
+ * - The PMIC PWRON signal is released <= 1 second after the power button is
+ * released
+ * - Holding pwron for 9s powers off the AP
+ * - Pressing and releasing pwron within that 9s is ignored
+ * - If XPSHOLD is dropped by the AP, then we power the AP off
+ */
+
+#include "clock.h"
+#include "chipset.h" /* This module implements chipset functions too */
+#include "common.h"
+#include "console.h"
+#include "tegra_power.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "lid_switch.h"
+#include "keyboard_scan.h"
+#include "power_led.h"
+#include "pmu_tpschrome.h"
+#include "system.h"
+#include "task.h"
+#include "timer.h"
+#include "util.h"
+
+/* Console output macros */
+#define CPUTS(outstr) cputs(CC_CHIPSET, outstr)
+#define CPRINTF(format, args...) cprintf(CC_CHIPSET, format, ## args)
+
+/* Long power key press to force shutdown */
+#define DELAY_FORCE_SHUTDOWN (9 * SECOND)
+
+/*
+ * If the power key is pressed to turn on, then held for this long, we
+ * power off.
+ *
+ * Normal case: User releases power button and chipset_task() goes
+ * into the inner loop, waiting for next event to occur (power button
+ * press or XPSHOLD == 0).
+ */
+#define DELAY_SHUTDOWN_ON_POWER_HOLD (9 * SECOND)
+
+/* Maximum delay after power button press before we deassert GPIO_PMIC_PWRON */
+#define DELAY_RELEASE_PWRON SECOND /* 1s */
+
+/* debounce time to prevent accidental power-on after keyboard power off */
+#define KB_PWR_ON_DEBOUNCE 250 /* 250us */
+
+/*
+ * nyan's GPIO_SOC1V8_XPSHOLD will go low for ~20ms after initial high.
+ * XPSHOLD_DEBOUNCE is used to wait this long, then check the signal again.
+ */
+#define XPSHOLD_DEBOUNCE (30 * 1000) /* 30 ms */
+
+/* Application processor power state */
+static int ap_on;
+static int ap_suspended;
+
+/* simulated event state */
+static int force_signal = -1;
+static int force_value;
+
+/* 1 if the power button was pressed last time we checked */
+static char power_button_was_pressed;
+
+/* 1 if lid-open event has been detected */
+static char lid_opened;
+
+/* time where we will power off, if power button still held down */
+static timestamp_t power_off_deadline;
+
+/* force AP power on (used for recovery keypress) */
+static int auto_power_on;
+
+enum power_request_t {
+ POWER_REQ_NONE,
+ POWER_REQ_OFF,
+ POWER_REQ_ON,
+
+ POWER_REQ_COUNT,
+};
+
+static enum power_request_t power_request;
+
+/**
+ * Wait for GPIO "signal" to reach level "value".
+ * Returns EC_ERROR_TIMEOUT if timeout before reaching the desired state.
+ *
+ * @param signal Signal to watch
+ * @param value Value to watch for
+ * @param timeout Timeout in microseconds from now, or -1 to wait forever
+ * @return 0 if signal did change to required value, EC_ERROR_TIMEOUT if we
+ * timed out first.
+ */
+static int wait_in_signal(enum gpio_signal signal, int value, int timeout)
+{
+ timestamp_t deadline;
+ timestamp_t now = get_time();
+
+ deadline.val = now.val + timeout;
+
+ while (((force_signal != signal) || (force_value != value)) &&
+ gpio_get_level(signal) != value) {
+ now = get_time();
+ if (timeout < 0) {
+ task_wait_event(-1);
+ } else if (timestamp_expired(deadline, &now) ||
+ (task_wait_event(deadline.val - now.val) ==
+ TASK_EVENT_TIMER)) {
+ CPRINTF("[%T power timeout waiting for GPIO %d/%s]\n",
+ signal, gpio_get_name(signal));
+ return EC_ERROR_TIMEOUT;
+ }
+ }
+
+ return EC_SUCCESS;
+}
+
+/**
+ * Set the PMIC PWROK signal.
+ *
+ * @param asserted Assert (=1) or deassert (=0) the signal. This is the
+ * logical level of the pin, not the physical level.
+ */
+static void set_pmic_pwrok(int asserted)
+{
+ /* Signal is active-low */
+ gpio_set_level(GPIO_PMIC_PWRON_L, asserted ? 0 : 1);
+}
+
+/**
+ * Check for some event triggering the shutdown.
+ *
+ * It can be either a long power button press or a shutdown triggered from the
+ * AP and detected by reading XPSHOLD.
+ *
+ * @return non-zero if a shutdown should happen, 0 if not
+ */
+static int check_for_power_off_event(void)
+{
+ timestamp_t now;
+ int pressed = 0;
+
+ /*
+ * Check for power button press.
+ *
+ * If power_request is POWER_REQ_OFF, simulate the requst as power
+ * button is pressed. This will casue GPIO_PMIC_PWRON_L to be driven
+ * low (by set_pmic_pwrok(1)) until PMIC automatically turns off power.
+ * (PMIC turns off power if GPIO_PMIC_PWRON_L is low for >=9 seconds.)
+ */
+ if (gpio_get_level(GPIO_KB_PWR_ON_L) == 0) {
+ udelay(KB_PWR_ON_DEBOUNCE);
+ if (gpio_get_level(GPIO_KB_PWR_ON_L) == 0)
+ pressed = 1;
+ } else if (power_request == POWER_REQ_OFF) {
+ pressed = 1;
+ power_request = POWER_REQ_NONE;
+ }
+
+#ifdef HAS_TASK_KEYSCAN
+ /* Dis/Enable keyboard scanning when the power button state changes */
+ if (!pressed || pressed != power_button_was_pressed)
+ keyboard_scan_enable(!pressed);
+#endif
+
+ now = get_time();
+ if (pressed) {
+ set_pmic_pwrok(1);
+
+ if (!power_button_was_pressed) {
+ power_off_deadline.val = now.val + DELAY_FORCE_SHUTDOWN;
+ CPRINTF("[%T power waiting for long press %u]\n",
+ power_off_deadline.le.lo);
+ } else if (timestamp_expired(power_off_deadline, &now)) {
+ power_off_deadline.val = 0;
+ CPRINTF("[%T power off after long press now=%u, %u]\n",
+ now.le.lo, power_off_deadline.le.lo);
+ return 2;
+ }
+ } else if (power_button_was_pressed) {
+ CPRINTF("[%T power off cancel]\n");
+ set_pmic_pwrok(0);
+ }
+
+ power_button_was_pressed = pressed;
+
+ /* XPSHOLD released by AP : shutdown immediately */
+ if (gpio_get_level(GPIO_SOC1V8_XPSHOLD) == 0)
+ return 3;
+
+ return 0;
+}
+
+/**
+ * Deferred handling for suspend events
+ *
+ * The suspend event needs to be able to call the suspend and resume hooks.
+ * This cannot be done from interrupt level, since the handlers from those
+ * hooks may need to use mutexes or other functionality not present at
+ * interrupt level. Use a deferred function instead.
+ *
+ * Deferred functions are called from the hook task and not the chipset task,
+ * so that's a slight deviation from the spec in hooks.h, but a minor one.
+ */
+static void tegra_suspend_deferred(void)
+{
+ int new_ap_suspended;
+
+ if (!ap_on) /* power on/off : not a real suspend / resume */
+ return;
+
+ new_ap_suspended = !gpio_get_level(GPIO_SUSPEND_L);
+
+ /* We never want to call two suspend or two resumes in a row */
+ if (ap_suspended == new_ap_suspended)
+ return;
+
+ ap_suspended = new_ap_suspended;
+
+ if (ap_suspended) {
+ if (lid_is_open())
+ powerled_set_state(POWERLED_STATE_SUSPEND);
+ else
+ powerled_set_state(POWERLED_STATE_OFF);
+ /* Call hooks here since we don't know it prior to AP suspend */
+ hook_notify(HOOK_CHIPSET_SUSPEND);
+ } else {
+ powerled_set_state(POWERLED_STATE_ON);
+ hook_notify(HOOK_CHIPSET_RESUME);
+ }
+}
+DECLARE_DEFERRED(tegra_suspend_deferred);
+
+void tegra_suspend_event(enum gpio_signal signal)
+{
+ hook_call_deferred(tegra_suspend_deferred, 0);
+}
+
+void tegra_power_event(enum gpio_signal signal)
+{
+ /* Wake up the task */
+ task_wake(TASK_ID_CHIPSET);
+}
+
+static void tegra_lid_event(void)
+{
+ /* Power task only cares about lid-open events */
+ if (!lid_is_open())
+ return;
+
+ lid_opened = 1;
+ task_wake(TASK_ID_CHIPSET);
+}
+DECLARE_HOOK(HOOK_LID_CHANGE, tegra_lid_event, HOOK_PRIO_DEFAULT);
+
+static int tegra_power_init(void)
+{
+ /* Enable interrupts for our GPIOs */
+ gpio_enable_interrupt(GPIO_KB_PWR_ON_L);
+ gpio_enable_interrupt(GPIO_SOC1V8_XPSHOLD);
+ gpio_enable_interrupt(GPIO_SUSPEND_L);
+
+ /* Leave power off only if requested by reset flags */
+ if (!(system_get_reset_flags() & RESET_FLAG_AP_OFF)) {
+ CPRINTF("[%T auto_power_on is set due to reset_flag 0x%x]\n",
+ system_get_reset_flags());
+ auto_power_on = 1;
+ }
+
+ return EC_SUCCESS;
+}
+
+/*****************************************************************************/
+/* Chipset interface */
+
+int chipset_in_state(int state_mask)
+{
+ /* If AP is off, match any off state for now */
+ if ((state_mask & CHIPSET_STATE_ANY_OFF) && !ap_on)
+ return 1;
+
+ /* If AP is on, match on state */
+ if ((state_mask & CHIPSET_STATE_ON) && ap_on && !ap_suspended)
+ return 1;
+
+ /* if AP is suspended, match on state */
+ if ((state_mask & CHIPSET_STATE_SUSPEND) && ap_on && ap_suspended)
+ return 1;
+
+ /* In any other case, we don't have a match */
+ return 0;
+}
+
+void chipset_exit_hard_off(void)
+{
+ /* TODO: implement, if/when we take the AP down to a hard-off state */
+}
+
+void chipset_reset(int is_cold)
+{
+ /* TODO: implement cold reset. For now, all resets are warm resets. */
+ CPRINTF("[%T EC triggered warm reboot]\n");
+
+ /*
+ * This is a hack to do an AP warm reboot while still preserving RAM
+ * contents. This is useful for looking at kernel log message contents
+ * from previous boot in cases where the AP/OS is hard hung.
+ */
+ power_request = POWER_REQ_ON;
+ task_wake(TASK_ID_CHIPSET);
+}
+
+void chipset_force_shutdown(void)
+{
+ set_pmic_pwrok(0);
+}
+
+/*****************************************************************************/
+
+/**
+ * Check if there has been a power-on event
+ *
+ * This checks all power-on event signals and returns non-zero if any have been
+ * triggered (with debounce taken into account).
+ *
+ * @return non-zero if there has been a power-on event, 0 if not.
+ */
+static int check_for_power_on_event(void)
+{
+ /* check if system is already ON */
+ if (gpio_get_level(GPIO_SOC1V8_XPSHOLD)) {
+ CPRINTF("[%T system is on, thus clear auto_power_on]\n");
+ auto_power_on = 0; /* no need to arrange another power on */
+ return 1;
+ }
+
+ /* power on requested at EC startup for recovery */
+ if (auto_power_on) {
+ auto_power_on = 0;
+ return 2;
+ }
+
+ /* Check lid open */
+ if (lid_opened) {
+ lid_opened = 0;
+ return 3;
+ }
+
+ /* check for power button press */
+ if (gpio_get_level(GPIO_KB_PWR_ON_L) == 0) {
+ udelay(KB_PWR_ON_DEBOUNCE);
+ if (gpio_get_level(GPIO_KB_PWR_ON_L) == 0)
+ return 4;
+ }
+
+ if (power_request == POWER_REQ_ON) {
+ power_request = POWER_REQ_NONE;
+ return 5;
+ }
+
+ return 0;
+}
+
+/**
+ * Power on the AP
+ *
+ * @return 0 if ok, -1 on error (PP1800_LDO2 failed to come on)
+ */
+static int power_on(void)
+{
+ gpio_set_level(GPIO_AP_RESET_L, 1);
+ set_pmic_pwrok(1);
+
+ if (gpio_get_level(GPIO_SOC1V8_XPSHOLD) == 0)
+ /* Initialize non-AP components */
+ hook_notify(HOOK_CHIPSET_PRE_INIT);
+
+ ap_on = 1;
+ disable_sleep(SLEEP_MASK_AP_RUN);
+ powerled_set_state(POWERLED_STATE_ON);
+
+ /* Call hooks now that AP is running */
+ hook_notify(HOOK_CHIPSET_STARTUP);
+
+ CPRINTF("[%T AP running ...]\n");
+ return 0;
+}
+
+/**
+ * Wait for the power button to be released
+ *
+ * @return 0 if ok, -1 if power button failed to release
+ */
+static int wait_for_power_button_release(unsigned int timeout_us)
+{
+ /* wait for Power button release */
+ wait_in_signal(GPIO_KB_PWR_ON_L, 1, timeout_us);
+
+ udelay(KB_PWR_ON_DEBOUNCE);
+ if (gpio_get_level(GPIO_KB_PWR_ON_L) == 0) {
+ CPRINTF("[%T power button not released in time]\n");
+ return -1;
+ }
+ CPRINTF("[%T power button released]\n");
+ return 0;
+}
+
+/**
+ * Wait for the XPSHOLD signal from the AP to be asserted within timeout_us
+ * and if asserted clear the PMIC_PWRON signal
+ *
+ * @return 0 if ok, -1 if power button failed to release
+ */
+static int react_to_xpshold(unsigned int timeout_us)
+{
+ /* wait for Power button release */
+ wait_in_signal(GPIO_SOC1V8_XPSHOLD, 1, timeout_us);
+
+#ifdef BOARD_nyan
+ /*
+ * nyan's GPIO_SOC1V8_XPSHOLD will go low for about 20ms after initial
+ * high. Wait XPSHOLD_DEBOUNCE time, then check the signal again.
+ */
+ udelay(XPSHOLD_DEBOUNCE);
+#endif
+
+ if (gpio_get_level(GPIO_SOC1V8_XPSHOLD) == 0) {
+ CPRINTF("[%T XPSHOLD not seen in time]\n");
+ return -1;
+ }
+
+ CPRINTF("[%T XPSHOLD seen]\n");
+ return 0;
+}
+
+/**
+ * Power off the AP
+ */
+static void power_off(void)
+{
+ /* Call hooks before we drop power rails */
+ hook_notify(HOOK_CHIPSET_SHUTDOWN);
+ /* switch off all rails */
+ chipset_force_shutdown();
+ ap_on = 0;
+ ap_suspended = 0;
+ lid_opened = 0;
+ enable_sleep(SLEEP_MASK_AP_RUN);
+ powerled_set_state(POWERLED_STATE_OFF);
+ CPRINTF("[%T power shutdown complete]\n");
+}
+
+/*
+ * Calculates the delay in microseconds to the next time we have to check
+ * for a power event,
+ *
+ *@return delay to next check, or -1 if no future check is needed
+ */
+static int next_pwr_event(void)
+{
+ if (!power_off_deadline.val)
+ return -1;
+
+ return power_off_deadline.val - get_time().val;
+}
+
+/*****************************************************************************/
+static int wait_for_power_on(void)
+{
+ int value;
+ while (1) {
+ value = check_for_power_on_event();
+ if (!value) {
+ task_wait_event(-1);
+ continue;
+ }
+
+#ifdef HAS_TASK_CHARGER
+ /*
+ * If the system is already on (value == 1), the kernel
+ * would handle low power condition and we should not
+ * shutdown the system from EC.
+ */
+ if (value != 1 && charge_keep_power_off()) {
+ CPRINTF("[%T power on ignored due to low battery]\n");
+ continue;
+ }
+#endif
+
+ CPRINTF("[%T power on %d]\n", value);
+ return value;
+ }
+}
+
+void chipset_task(void)
+{
+ int value;
+
+ tegra_power_init();
+ ap_on = 0;
+
+ while (1) {
+ /* Wait until we need to power on, then power on */
+ wait_for_power_on();
+
+ if (!power_on()) {
+ int continue_power = 0;
+
+ if (!react_to_xpshold(DELAY_RELEASE_PWRON)) {
+ /* AP looks good */
+ if (!wait_for_power_button_release(
+ DELAY_SHUTDOWN_ON_POWER_HOLD))
+ continue_power = 1;
+ }
+ set_pmic_pwrok(0);
+ if (continue_power) {
+ power_button_was_pressed = 0;
+ while (!(value = check_for_power_off_event()))
+ task_wait_event(next_pwr_event());
+ CPRINTF("[%T power ending loop %d]\n", value);
+ }
+ }
+ power_off();
+ wait_for_power_button_release(-1);
+ }
+}
+
+/*****************************************************************************/
+/* Console debug command */
+
+static const char *power_req_name[POWER_REQ_COUNT] = {
+ "none",
+ "off",
+ "on",
+};
+
+/* Power states that we can report */
+enum power_state_t {
+ PSTATE_UNKNOWN,
+ PSTATE_OFF,
+ PSTATE_SUSPEND,
+ PSTATE_ON,
+
+ PSTATE_COUNT,
+};
+
+static const char * const state_name[] = {
+ "unknown",
+ "off",
+ "suspend",
+ "on",
+};
+
+static int command_power(int argc, char **argv)
+{
+ int v;
+
+ if (argc < 2) {
+ enum power_state_t state;
+
+ state = PSTATE_UNKNOWN;
+ if (chipset_in_state(CHIPSET_STATE_ANY_OFF))
+ state = PSTATE_OFF;
+ if (chipset_in_state(CHIPSET_STATE_SUSPEND))
+ state = PSTATE_SUSPEND;
+ if (chipset_in_state(CHIPSET_STATE_ON))
+ state = PSTATE_ON;
+ ccprintf("%s\n", state_name[state]);
+
+ return EC_SUCCESS;
+ }
+
+ if (!parse_bool(argv[1], &v))
+ return EC_ERROR_PARAM1;
+
+ power_request = v ? POWER_REQ_ON : POWER_REQ_OFF;
+ ccprintf("Requesting power %s\n", power_req_name[power_request]);
+ task_wake(TASK_ID_CHIPSET);
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(power, command_power,
+ "on/off",
+ "Turn AP power on/off",
+ NULL);
diff --git a/include/config.h b/include/config.h
index 2610a35284..7092d3da86 100644
--- a/include/config.h
+++ b/include/config.h
@@ -182,6 +182,7 @@
#undef CONFIG_CHIPSET_GAIA /* Gaia and Ares (ARM) */
#undef CONFIG_CHIPSET_HASWELL /* Intel Haswell (x86) */
#undef CONFIG_CHIPSET_IVYBRIDGE /* Intel Ivy Bridge (x86) */
+#undef CONFIG_CHIPSET_TEGRA /* Tegra */
/* Compile common x86 chipset infrastructure. Required for x86 chips. */
#undef CONFIG_CHIPSET_X86
@@ -776,6 +777,7 @@
#undef CONFIG_CHIPSET_HASWELL
#undef CONFIG_CHIPSET_IVYBRIDGE
#undef CONFIG_CHIPSET_X86
+#undef CONFIG_CHIPSET_TEGRA
#endif
#ifndef HAS_TASK_KEYPROTO
diff --git a/include/tegra_power.h b/include/tegra_power.h
new file mode 100644
index 0000000000..9b02a9feaf
--- /dev/null
+++ b/include/tegra_power.h
@@ -0,0 +1,28 @@
+/* Copyright (c) 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.
+ */
+
+/* Tegra power module for Chrome EC */
+
+#ifndef __CROS_EC_TEGRA_POWER_H
+#define __CROS_EC_TEGRA_POWER_H
+
+#include "gpio.h"
+
+#ifdef CONFIG_CHIPSET_TEGRA
+
+/**
+ * Interrupt handlers for Tegra chipset GPIOs.
+ */
+void tegra_power_event(enum gpio_signal signal);
+void tegra_suspend_event(enum gpio_signal signal);
+
+#else
+
+#define tegra_power_event NULL
+#define tegra_suspend_event NULL
+
+#endif
+
+#endif /* __CROS_EC_TEGRA_POWER_H */
diff --git a/util/flash_ec b/util/flash_ec
index 147b6006df..8d53ea6667 100755
--- a/util/flash_ec
+++ b/util/flash_ec
@@ -222,7 +222,7 @@ fi
save="$(servo_save)"
case "${BOARD}" in
- daisy | kirby | pit | puppy | snow | spring | discovery ) flash_stm32 ;;
+ daisy | kirby | pit | puppy | snow | spring | discovery | nyan ) flash_stm32 ;;
bolt | samus | falco | peppy | rambi | slippy ) flash_lm4 ;;
link ) flash_link ;;
*) die "board ${BOARD} not supported" ;;