diff options
-rw-r--r-- | board/nyan/battery.c | 131 | ||||
-rw-r--r-- | board/nyan/board.c | 104 | ||||
-rw-r--r-- | board/nyan/board.h | 129 | ||||
-rw-r--r-- | board/nyan/build.mk | 13 | ||||
-rw-r--r-- | board/nyan/ec.tasklist | 25 | ||||
-rw-r--r-- | common/build.mk | 1 | ||||
-rw-r--r-- | common/chipset_tegra.c | 602 | ||||
-rw-r--r-- | include/config.h | 2 | ||||
-rw-r--r-- | include/tegra_power.h | 28 | ||||
-rwxr-xr-x | util/flash_ec | 2 |
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" ;; |