diff options
author | Ben Lok <ben.lok@mediatek.com> | 2015-01-27 16:00:02 +0800 |
---|---|---|
committer | ChromeOS Commit Bot <chromeos-commit-bot@chromium.org> | 2015-02-10 08:46:06 +0000 |
commit | a9ae00b10129feca28713a7b88978ff6cfe7c2a6 (patch) | |
tree | daa689352b6693e9ae110f37b6510e540993dc53 | |
parent | 9ed4434ad8770ca3edd6bccd456738019c9e08b0 (diff) | |
download | chrome-ec-a9ae00b10129feca28713a7b88978ff6cfe7c2a6.tar.gz |
llama: add llama board support
This is to add llama board support:
- new files in board/llama folder, including battery.c and led.c
- new file power/mediatek.c, which is mostly based on power/tegra.c
- modified flash_ec for llama board
- disable tests for llama board.
BRANCH=none
BUG=none
TEST=make BOARD=llama
Change-Id: Ie1ae068c1a402f08e1449668b1be8f31105bb804
Signed-off-by: Ben Lok <ben.lok@mediatek.com>
Reviewed-on: https://chromium-review.googlesource.com/243510
Reviewed-by: Rong Chang <rongchang@chromium.org>
Tested-by: lok.ben ben.mtk <ben.lok.mtk@gmail.com>
Commit-Queue: lok.ben ben.mtk <ben.lok.mtk@gmail.com>
l--------- | board/llama/Makefile | 1 | ||||
-rw-r--r-- | board/llama/battery.c | 22 | ||||
-rw-r--r-- | board/llama/board.c | 88 | ||||
-rw-r--r-- | board/llama/board.h | 101 | ||||
-rw-r--r-- | board/llama/build.mk | 13 | ||||
-rw-r--r-- | board/llama/ec.tasklist | 25 | ||||
-rw-r--r-- | board/llama/gpio.inc | 69 | ||||
-rw-r--r-- | board/llama/led.c | 138 | ||||
-rw-r--r-- | power/build.mk | 1 | ||||
-rw-r--r-- | power/mediatek.c | 637 | ||||
-rw-r--r-- | test/build.mk | 3 | ||||
-rwxr-xr-x | util/flash_ec | 1 |
12 files changed, 1099 insertions, 0 deletions
diff --git a/board/llama/Makefile b/board/llama/Makefile new file mode 120000 index 0000000000..94aaae2c4d --- /dev/null +++ b/board/llama/Makefile @@ -0,0 +1 @@ +../../Makefile
\ No newline at end of file diff --git a/board/llama/battery.c b/board/llama/battery.c new file mode 100644 index 0000000000..a7d46e33c3 --- /dev/null +++ b/board/llama/battery.c @@ -0,0 +1,22 @@ +#include "battery.h" + +static const struct battery_info info = { + .voltage_max = 8700, + .voltage_normal = 7600, + .voltage_min = 6000, + + /* Pre-charge values. */ + .precharge_current = 256, /* mA */ + + .start_charging_min_c = 0, + .start_charging_max_c = 45, + .charging_min_c = 0, + .charging_max_c = 45, + .discharging_min_c = -10, + .discharging_max_c = 60, +}; + +const struct battery_info *battery_get_info(void) +{ + return &info; +} diff --git a/board/llama/board.c b/board/llama/board.c new file mode 100644 index 0000000000..7bacd2628d --- /dev/null +++ b/board/llama/board.c @@ -0,0 +1,88 @@ +/* Copyright 2015 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. + */ +/* llama board configuration */ + +#include "battery.h" +#include "charger.h" +#include "chipset.h" +#include "common.h" +#include "console.h" +#include "extpower.h" +#include "gpio.h" +#include "hooks.h" +#include "i2c.h" +#include "keyboard_raw.h" +#include "lid_switch.h" +#include "power.h" +#include "power_button.h" +#include "pwm.h" +#include "pwm_chip.h" +#include "registers.h" +#include "spi.h" +#include "task.h" +#include "util.h" + +#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) +#define CPRINTF(format, args...) cprintf(CC_CHIPSET, format, ## args) + +#define GPIO_KB_INPUT (GPIO_INPUT | GPIO_PULL_UP | GPIO_INT_BOTH) +#define GPIO_KB_OUTPUT GPIO_ODR_HIGH + +#include "gpio_list.h" + +/* power signal list. Must match order of enum power_signal. */ +const struct power_signal_info power_signal_list[] = { + {GPIO_SOC_POWER_GOOD_L, 0, "POWER_GOOD#"}, /* Active low */ + {GPIO_SUSPEND_L, 0, "SUSPEND#_ASSERTED"}, /* Active low */ +}; + +BUILD_ASSERT(ARRAY_SIZE(power_signal_list) == POWER_SIGNAL_COUNT); + +#ifdef CONFIG_PWM +/* 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); +#endif + +#ifdef CONFIG_I2C +/* I2C ports */ +const struct i2c_port_t i2c_ports[] = { + {"master", I2C_PORT_MASTER, 100, GPIO_MASTER_I2C_SCL, + GPIO_MASTER_I2C_SDA}, +#ifdef CONFIG_HOSTCMD_I2C_SLAVE_ADDR + {"slave", I2C_PORT_SLAVE, 100, GPIO_SLAVE_I2C_SCL, GPIO_SLAVE_I2C_SDA} +#endif +}; + +const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports); +#endif + +/** + * Discharge battery when on AC power for factory test. + */ +int board_discharge_on_ac(int enable) +{ + return charger_discharge_on_ac(enable); +} + +void board_config_pre_init(void) +{ + /* enable SYSCFG clock */ + STM32_RCC_APB2ENR |= 1 << 0; + + /* Remap USART DMA to match the USART driver */ + /* + * the DMA mapping is : + * Chan 2 : TIM1_CH1 + * Chan 3 : SPI1_TX + * Chan 4 : USART1_TX + * Chan 5 : USART1_RX + */ + STM32_SYSCFG_CFGR1 |= (1 << 9) | (1 << 10); /* Remap USART1 RX/TX DMA */ +} diff --git a/board/llama/board.h b/board/llama/board.h new file mode 100644 index 0000000000..fc2d37ec29 --- /dev/null +++ b/board/llama/board.h @@ -0,0 +1,101 @@ +/* Copyright 2015 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. + */ + +/* llama board configuration */ + +#ifndef __BOARD_H +#define __BOARD_H + +#define CONFIG_CHIPSET_MEDIATEK +/* Add for AC adaptor, charger, battery */ +#undef CONFIG_BATTERY_CUT_OFF +#define CONFIG_BATTERY_SMART +#define CONFIG_BOARD_PRE_INIT +#define CONFIG_CHARGER +#define CONFIG_CHARGER_BQ24715 +#define CONFIG_CHARGER_DISCHARGE_ON_AC +#define CONFIG_CHARGER_V2 +#define CONFIG_EXTPOWER_GPIO +#define CONFIG_FORCE_CONSOLE_RESUME +#define CONFIG_HOST_COMMAND_STATUS +#define CONFIG_I2C +#define CONFIG_KEYBOARD_COL2_INVERTED +#define CONFIG_KEYBOARD_PROTOCOL_MKBP +#define CONFIG_LED_COMMON +#define CONFIG_LOW_POWER_IDLE +#define CONFIG_FORCE_CONSOLE_RESUME +#define CONFIG_POWER_BUTTON +#define CONFIG_POWER_COMMON +/* #define CONFIG_PWM */ +#define CONFIG_SPI +#define CONFIG_STM_HWTIMER32 +#define CONFIG_VBOOT_HASH +#undef CONFIG_WATCHDOG_HELP +#define CONFIG_LID_SWITCH +#define CONFIG_SWITCH +#define CONFIG_BOARD_VERSION +#undef CONFIG_UART_CONSOLE +#define CONFIG_UART_CONSOLE 1 + +/* Hibernate is not supported on STM32F0.*/ +#undef CONFIG_HIBERNATE +/* #define CONFIG_HIBERNATE_WAKEUP_PINS STM32_PWR_CSR_EWUP1 */ + +/* + * Allow dangerous commands all the time, since we don't have a write protect + * switch. + */ +#define CONFIG_SYSTEM_UNLOCKED + +#define CONFIG_PMIC_FW_LONG_PRESS_TIMER +/* Optional features */ +/* #define CONFIG_HW_CRC */ +#define CONFIG_CMD_HOSTCMD + +#ifndef __ASSEMBLER__ + +/* 48 MHz SYSCLK clock frequency */ +#define CPU_CLOCK 48000000 + +/* Keyboard output port list */ +#define KB_OUT_PORT_LIST GPIO_A, GPIO_B, GPIO_C + +/* Single I2C port, where the EC is the master. */ +#define I2C_PORT_MASTER 0 +#define I2C_PORT_BATTERY I2C_PORT_MASTER +#define I2C_PORT_CHARGER I2C_PORT_MASTER + +/* Timer selection */ +#define TIM_CLOCK32 2 +#define TIM_WATCHDOG 4 + +#include "gpio_signal.h" + +enum power_signal { + MTK_POWER_GOOD = 0, + MTK_SUSPEND_ASSERTED, + /* Number of power signals */ + POWER_SIGNAL_COUNT +}; + +enum pwm_channel { + PWM_CH_POWER_LED = 0, + /* Number of PWM channels */ + PWM_CH_COUNT +}; + +/* Charger module */ +/* Charge sense resistor */ +#define CONFIG_CHARGER_SENSE_RESISTOR 10 /* mOhm */ +/* Input sensor resistor */ +#define CONFIG_CHARGER_SENSE_RESISTOR_AC 10 /* mOhm */ +#define CONFIG_CHARGER_INPUT_CURRENT 2150 /* mA */ + +/* Discharge battery when on AC power for factory test. */ +int board_discharge_on_ac(int enable); + +#endif /* !__ASSEMBLER__ */ + +#endif /* __BOARD_H */ diff --git a/board/llama/build.mk b/board/llama/build.mk new file mode 100644 index 0000000000..1247589da1 --- /dev/null +++ b/board/llama/build.mk @@ -0,0 +1,13 @@ +#-*- makefile -*- +#Copyright 2015 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 STM32F071RB +CHIP := stm32 +CHIP_FAMILY := stm32f0 +CHIP_VARIANT:= stm32f07x + +board-y = board.o battery.o led.o diff --git a/board/llama/ec.tasklist b/board/llama/ec.tasklist new file mode 100644 index 0000000000..b85777b97f --- /dev/null +++ b/board/llama/ec.tasklist @@ -0,0 +1,25 @@ +/* Copyright 2015 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, LARGER_TASK_STACK_SIZE) \ + TASK_ALWAYS(CHARGER, charger_task, NULL, LARGER_TASK_STACK_SIZE) \ + TASK_NOTEST(CHIPSET, chipset_task, NULL, LARGER_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/board/llama/gpio.inc b/board/llama/gpio.inc new file mode 100644 index 0000000000..795c870143 --- /dev/null +++ b/board/llama/gpio.inc @@ -0,0 +1,69 @@ +/* -*- mode:c -*- + * + * Copyright 2015 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. + */ + +/* Inputs with interrupt handlers are first for efficiency */ +GPIO(AC_PRESENT, A, 0, GPIO_INT_BOTH, extpower_interrupt) /* AC power present: PA 0 */ +GPIO(POWER_BUTTON_L, B, 5, GPIO_INT_BOTH | GPIO_PULL_UP, power_button_interrupt) /* Keyboard power button: PB 5 */ +GPIO(SOC_POWER_GOOD_L, A, 3, GPIO_INT_BOTH, power_signal_interrupt) +GPIO(LID_OPEN, C, 13, GPIO_INT_BOTH, lid_interrupt) /* LID switch detection */ +GPIO(SUSPEND_L, C, 7, GPIO_INT_BOTH, power_signal_interrupt) /* AP suspend/resume state */ +GPIO(5V_GOOD_L, A, 1, GPIO_INT_BOTH, NULL) +GPIO(AP_RESET_H, B, 3, GPIO_OUT_LOW, NULL) +GPIO(BOARD_VERSION1, C, 6, GPIO_INPUT|GPIO_PULL_UP, NULL) /* Board version stuffing resistor 1 */ + +/* SPI Chip Select */ +GPIO(SPI1_NSS, A, 4, GPIO_INT_BOTH | GPIO_PULL_UP, spi_event) + +/* Keyboard inputs */ +GPIO(KB_IN00, C, 8, GPIO_KB_INPUT, keyboard_raw_gpio_interrupt) +GPIO(KB_IN01, C, 9, GPIO_KB_INPUT, keyboard_raw_gpio_interrupt) +GPIO(KB_IN02, C, 10, GPIO_KB_INPUT, keyboard_raw_gpio_interrupt) +GPIO(KB_IN03, C, 11, GPIO_KB_INPUT, keyboard_raw_gpio_interrupt) +GPIO(KB_IN04, C, 12, GPIO_KB_INPUT, keyboard_raw_gpio_interrupt) +GPIO(KB_IN05, C, 14, GPIO_KB_INPUT, keyboard_raw_gpio_interrupt) +GPIO(KB_IN06, C, 15, GPIO_KB_INPUT, keyboard_raw_gpio_interrupt) +GPIO(KB_IN07, D, 2, GPIO_KB_INPUT, keyboard_raw_gpio_interrupt) + + +/* Outputs */ +GPIO(BAT_LED0, B, 11, GPIO_OUT_LOW, NULL) /* LED_GREEN */ +GPIO(BAT_LED1, A, 11, GPIO_OUT_LOW, NULL) /* LED_ORANGE */ +GPIO(EC_INT, B, 9, GPIO_OUT_HIGH, NULL) +GPIO(ENTERING_RW, F, 0, GPIO_OUT_LOW, NULL) +GPIO(KB_OUT00, B, 0, GPIO_KB_OUTPUT, NULL) +GPIO(KB_OUT01, B, 8, GPIO_KB_OUTPUT, NULL) +GPIO(KB_OUT02, B, 12, GPIO_OUT_LOW, NULL) /* KSO2 is inverted */ +GPIO(KB_OUT03, B, 13, GPIO_KB_OUTPUT, NULL) +GPIO(KB_OUT04, B, 14, GPIO_KB_OUTPUT, NULL) +GPIO(KB_OUT05, B, 15, GPIO_KB_OUTPUT, NULL) +GPIO(KB_OUT06, C, 0, GPIO_KB_OUTPUT, NULL) +GPIO(KB_OUT07, C, 1, GPIO_KB_OUTPUT, NULL) +GPIO(KB_OUT08, C, 2, GPIO_KB_OUTPUT, NULL) +GPIO(KB_OUT09, B, 1, GPIO_KB_OUTPUT, NULL) +GPIO(KB_OUT10, C, 5, GPIO_KB_OUTPUT, NULL) +GPIO(KB_OUT11, C, 4, GPIO_KB_OUTPUT, NULL) +GPIO(KB_OUT12, A, 14, GPIO_KB_OUTPUT, NULL) +GPIO(SYSTEM_POWER_H, B, 10, GPIO_OUT_LOW, NULL) +GPIO(PMIC_PWRON_H, A, 12, GPIO_OUT_LOW, NULL) +GPIO(PMIC_WARM_RESET_H, C, 3, GPIO_ODR_LOW, NULL) + +/* + * I2C pins should be configured as inputs until I2C module is + * initialized. This will avoid driving the lines unintentionally. + */ +GPIO(MASTER_I2C_SCL, B, 6, GPIO_INPUT, NULL) +GPIO(MASTER_I2C_SDA, B, 7, GPIO_INPUT, NULL) + +/* Unimplemented signals which we need to emulate for now */ +/* UNIMPLEMENTED(ENTERING_RW) */ +UNIMPLEMENTED(WP_L) +UNIMPLEMENTED(BOARD_VERSION2) +UNIMPLEMENTED(BOARD_VERSION3) + +ALTERNATE(A, 0x0600, 1, MODULE_UART, 0) /* USART1: PA9/PA10 */ +ALTERNATE(B, 0x00c0, 1, MODULE_I2C, 0) /* I2C MASTER:PB6/7 */ +ALTERNATE(A, 0x00f0, 0, MODULE_SPI, 0) /* SPI SLAVE:PA4/5/6/7 */ diff --git a/board/llama/led.c b/board/llama/led.c new file mode 100644 index 0000000000..fd86cab223 --- /dev/null +++ b/board/llama/led.c @@ -0,0 +1,138 @@ +/* Copyright 2015 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 LED and Power LED control for LLAMA Board. + */ + +#include "battery.h" +#include "charge_state.h" +#include "chipset.h" +#include "gpio.h" +#include "hooks.h" +#include "led_common.h" +#include "util.h" + +const enum ec_led_id supported_led_ids[] = { + EC_LED_ID_BATTERY_LED +}; + +const int supported_led_ids_count = ARRAY_SIZE(supported_led_ids); + +enum led_color { + LED_GREEN = 0, + LED_ORANGE, + LED_COLOR_COUNT /* Number of colors, not a color itself */ +}; + +static int bat_led_set(enum led_color color, int on) +{ + switch (color) { + case LED_GREEN: + gpio_set_level(GPIO_BAT_LED0, on ? 0 : 1); + break; + case LED_ORANGE: + gpio_set_level(GPIO_BAT_LED1, on ? 0 : 1); + break; + default: + return EC_ERROR_UNKNOWN; + } + return EC_SUCCESS; +} + +void led_get_brightness_range(enum ec_led_id led_id, uint8_t *brightness_range) +{ + /* Ignoring led_id as both leds support the same colors */ + brightness_range[EC_LED_COLOR_GREEN] = 1; + brightness_range[EC_LED_COLOR_YELLOW] = 1; +} + +int led_set_brightness(enum ec_led_id led_id, const uint8_t *brightness) +{ + if (EC_LED_ID_BATTERY_LED == led_id) { + if (brightness[EC_LED_COLOR_GREEN] != 0) { + bat_led_set(LED_GREEN, 1); + bat_led_set(LED_ORANGE, 0); + } else if (brightness[EC_LED_COLOR_YELLOW] != 0) { + bat_led_set(LED_GREEN, 1); + bat_led_set(LED_ORANGE, 1); + } else { + bat_led_set(LED_GREEN, 0); + bat_led_set(LED_ORANGE, 0); + } + return EC_SUCCESS; + } else { + return EC_ERROR_UNKNOWN; + } +} + +static void llama_led_set_power(void) +{ + static int power_second; + + power_second++; + + /* PWR LED behavior: + * Power on: Green + * Suspend: Green in breeze mode ( 1 sec on/ 3 sec off) + * Power off: OFF + */ + if (chipset_in_state(CHIPSET_STATE_ANY_OFF)) + bat_led_set(LED_GREEN, 0); + else if (chipset_in_state(CHIPSET_STATE_ON)) + bat_led_set(LED_GREEN, 1); + else if (chipset_in_state(CHIPSET_STATE_SUSPEND)) + bat_led_set(LED_GREEN, (power_second & 3) ? 0 : 1); +} + +static void llama_led_set_battery(void) +{ + static int battery_second; + + battery_second++; + + /* BAT LED behavior: + * Fully charged / idle: Off + * Under charging: Orange + * Battery low (10%): Orange in breeze mode (1 sec on, 3 sec off) + * Battery critical low (less than 3%) or abnormal battery + * situation: Orange in blinking mode (1 sec on, 1 sec off) + * Using battery or not connected to AC power: OFF + */ + switch (charge_get_state()) { + case PWR_STATE_CHARGE: + bat_led_set(LED_ORANGE, 1); + break; + case PWR_STATE_CHARGE_NEAR_FULL: + bat_led_set(LED_ORANGE, 1); + break; + case PWR_STATE_DISCHARGE: + if (charge_get_percent() < 3) + bat_led_set(LED_ORANGE, (battery_second & 1) ? 0 : 1); + else if (charge_get_percent() < 10) + bat_led_set(LED_ORANGE, (battery_second & 3) ? 0 : 1); + else + bat_led_set(LED_ORANGE, 0); + break; + case PWR_STATE_ERROR: + bat_led_set(LED_ORANGE, (battery_second & 1) ? 0 : 1); + break; + case PWR_STATE_IDLE: /* External power connected in IDLE. */ + bat_led_set(LED_ORANGE, 0); + break; + default: + /* Other states don't alter LED behavior */ + break; + } +} + +/** * Called by hook task every 1 sec */ +static void led_second(void) +{ + if (led_auto_control_is_enabled(EC_LED_ID_POWER_LED)) + llama_led_set_power(); + if (led_auto_control_is_enabled(EC_LED_ID_BATTERY_LED)) + llama_led_set_battery(); +} + +DECLARE_HOOK(HOOK_SECOND, led_second, HOOK_PRIO_DEFAULT); diff --git a/power/build.mk b/power/build.mk index 8d2d10ff8c..212ff2cf7e 100644 --- a/power/build.mk +++ b/power/build.mk @@ -14,4 +14,5 @@ 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_CHIPSET_MEDIATEK)+=mediatek.o power-$(CONFIG_POWER_COMMON)+=common.o diff --git a/power/mediatek.c b/power/mediatek.c new file mode 100644 index 0000000000..8515c27310 --- /dev/null +++ b/power/mediatek.c @@ -0,0 +1,637 @@ +/* Copyright 2015 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. + */ + +/* + * MEDIATEK 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 11s powers off the AP + * - Pressing and releasing pwron within that 11s is ignored + * - If POWER_GOOD is dropped by the AP, then we power the AP off + * - If SUSPEND_L goes low, enter suspend mode. + * + */ + +#include "battery.h" +#include "chipset.h" /* ./common/chipset.c implements chipset functions too */ +#include "common.h" +#include "gpio.h" +#include "hooks.h" +#include "keyboard_scan.h" +#include "lid_switch.h" +#include "power.h" +#include "power_button.h" +#include "power_led.h" +#include "system.h" +#include "task.h" +#include "test_util.h" + +#define CPRINTS(format, args...) cprints(CC_CHIPSET, format, ## args) + +#define INT_BOTH_PULL_UP (GPIO_INPUT | GPIO_PULL_UP | GPIO_INT_BOTH) + +/* masks for power signals */ +#define IN_POWER_GOOD POWER_SIGNAL_MASK(MTK_POWER_GOOD) +#define IN_SUSPEND POWER_SIGNAL_MASK(MTK_SUSPEND_ASSERTED) + +/* Long power key press to force shutdown */ +#define DELAY_FORCE_SHUTDOWN (11000 * MSEC) /* 11 seconds */ + +/* + * The minimum time to assert the PMIC PWRON pin is 20ms. + * Give it longer to ensure the PMIC doesn't lose it. + */ +#define PMIC_PWRON_DEBOUNCE_TIME (60 * MSEC) + +/* + * The time to bootup the PMIC from power-off to power-on. + */ +#define PMIC_PWRON_PRESS_TIME (3000 * MSEC) + +/* + * The minimum time to assert the PMIC THERM pin is 32us. However, + * it needs to be extended to about 50ms to let the 5V rail + * dissipate fully. + */ +#define PMIC_THERM_HOLD_TIME (50 * MSEC) + +/* + * 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 POWER_GOOD == 0). + */ +#define DELAY_SHUTDOWN_ON_POWER_HOLD (11000 * MSEC) /* 11 seconds */ + +/* + * The hold time for pulling down the PMIC_WARM_RESET_L pin so that + * the AP can entery the recovery mode (flash SPI flash from USB). + */ +#define PMIC_WARM_RESET_L_HOLD_TIME (4 * MSEC) + +/* + * The first time the PMIC sees power (AC or battery) it needs 200ms (+/-12% + * oscillator tolerance) for the RTC startup. In addition there is a startup + * time of approx. 0.5msec until V2_5 regulator starts up. */ +#define PMIC_RTC_STARTUP (225 * MSEC) + +/* TODO(crosbug.com/p/25047): move to HOOK_POWER_BUTTON_CHANGE */ +/* 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; + +/** + * Return values for check_for_power_off_event(). + */ +enum power_off_event_t { + POWER_OFF_CANCEL, + POWER_OFF_BY_POWER_BUTTON_PRESSED, + POWER_OFF_BY_LONG_PRESS, + POWER_OFF_BY_POWER_GOOD_LOST, + POWER_OFF_BY_POWER_REQ, + + POWER_OFF_EVENT_COUNT, +}; + +/** + * Return values for check_for_power_on_event(). + */ +enum power_on_event_t { + POWER_ON_CANCEL, + POWER_ON_BY_IN_POWER_GOOD, + POWER_ON_BY_AUTO_POWER_ON, + POWER_ON_BY_LID_OPEN, + POWER_ON_BY_POWER_BUTTON_PRESSED, + POWER_ON_BY_POWER_REQ_NONE, + + POWER_ON_EVENT_COUNT, +}; + +/* Forward declaration */ +static void chipset_turn_off_power_rails(void); + +/** + * Set the AP RESET signal. + * + * This function is for backward-compatible. + * + * AP_RESET_H (PB3) is stuffed before rev <= 2.0 and connected to PMIC RESET. + * After rev >= 2.2, this is removed. This should not effected the new board. + * + * @param asserted Assert (=1) or deassert (=0) the signal. This is the + * logical level of the pin, not the physical level. + */ +static void set_ap_reset(int asserted) +{ + /* Signal is active-high */ + CPRINTS("set_ap_reset(%d)", asserted); + gpio_set_level(GPIO_AP_RESET_H, asserted); +} + +/** + * Set the PMIC PWRON signal. + * + * Note that asserting requires holding for PMIC_PWRON_DEBOUNCE_TIME. + * + * @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_pwron(int asserted) +{ + /* Signal is active-high */ + CPRINTS("set_pmic_pwron(%d)", asserted); + gpio_set_level(GPIO_PMIC_PWRON_H, asserted); +} + +/** + * Set the PMIC WARM RESET signal. + * + * @param asserted Resetting (=0) or idle (=1) + */ +static void set_pmic_warm_reset(int asserted) +{ + /* Signal is active-high */ + /* @param asserted: Resetting (=0) or idle (=1) */ + gpio_set_level(GPIO_PMIC_WARM_RESET_H, asserted); +} + +/** + * 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 POWER_GOOD. + * + * @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_button_is_pressed()) { + pressed = POWER_OFF_BY_POWER_BUTTON_PRESSED; + } else if (power_request == POWER_REQ_OFF) { + power_request = POWER_REQ_NONE; + /* return non-zero for shudown down */ + return POWER_OFF_BY_POWER_REQ; + } + +#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, KB_SCAN_DISABLE_POWER_BUTTON); +#endif + + now = get_time(); + if (pressed) { +#ifndef CONFIG_PMIC_FW_LONG_PRESS_TIMER + /* + * Only assert PMIC_PWRON if PMIC supports long-press + * power off. + */ + CPRINTS("PMIC long-press power off\n"); + set_pmic_pwron(1); + usleep(PMIC_PWRON_DEBOUNCE_TIME); +#endif + + if (!power_button_was_pressed) { + power_off_deadline.val = now.val + DELAY_FORCE_SHUTDOWN; + CPRINTS("power waiting for long press %u", + power_off_deadline.le.lo); +#ifdef CONFIG_PMIC_FW_LONG_PRESS_TIMER + /* Ensure we will wake up to check the power key */ + timer_arm(power_off_deadline, TASK_ID_CHIPSET); +#endif + } else if (timestamp_expired(power_off_deadline, &now)) { + power_off_deadline.val = 0; + CPRINTS("power off after long press now=%u, %u", + now.le.lo, power_off_deadline.le.lo); + return POWER_OFF_BY_LONG_PRESS; + } + } else if (power_button_was_pressed) { + CPRINTS("power off cancel"); + set_pmic_pwron(0); +#ifdef CONFIG_PMIC_FW_LONG_PRESS_TIMER + timer_cancel(TASK_ID_CHIPSET); +#endif + } + + power_button_was_pressed = pressed; + + /* POWER_GOOD released by AP : shutdown immediately */ + if (!power_has_signals(IN_POWER_GOOD)) + return POWER_OFF_BY_POWER_GOOD_LOST; + + return POWER_OFF_CANCEL; +} + +static void llama_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, llama_lid_event, HOOK_PRIO_DEFAULT); + +enum power_state power_chipset_init(void) +{ + int init_power_state; + uint32_t reset_flags = system_get_reset_flags(); + + /* + * Force the AP shutdown unless we are doing SYSJUMP. Otherwise, + * the AP could stay in strange state. + */ + if (!(reset_flags & RESET_FLAG_SYSJUMP)) { + CPRINTS("not sysjump; forcing AP shutdown"); + chipset_turn_off_power_rails(); + + /* + * The warm reset triggers AP into the recovery mode ( + * flash SPI from USB). + */ + chipset_reset(0); + + init_power_state = POWER_G3; + } else { + /* In the SYSJUMP case, we check if the AP is on */ + if (power_get_signals() & IN_POWER_GOOD) { + CPRINTS("SOC ON\n"); + init_power_state = POWER_S0; + disable_sleep(SLEEP_MASK_AP_RUN); + } else { + CPRINTS("SOC OFF\n"); + init_power_state = POWER_G3; + enable_sleep(SLEEP_MASK_AP_RUN); + } + } + + /* Leave power off only if requested by reset flags */ + if (!(reset_flags & RESET_FLAG_AP_OFF) && + !(reset_flags & RESET_FLAG_SYSJUMP)) { + CPRINTS("reset_flag 0x%x", reset_flags); + auto_power_on = 1; + } + + /* + * Some batteries use clock stretching feature, which requires + * more time to be stable. See http://crosbug.com/p/28289 + */ + battery_wait_for_stable(); + + return init_power_state; +} + +/*****************************************************************************/ +/* Chipset interface */ + +static void chipset_turn_off_power_rails(void) +{ + /* Release the power on pin, if it was asserted */ + set_pmic_pwron(0); + /* Close the pmic power source immediately */ + /* set_pmic_source(0); */ + + usleep(PMIC_THERM_HOLD_TIME); + + /* Keep AP and PMIC in reset the whole time */ + set_pmic_warm_reset(1); + + /* Hold the reset pin so that the AP stays in off mode (rev <= 2.0) */ + set_ap_reset(1); +} + +void chipset_force_shutdown(void) +{ + chipset_turn_off_power_rails(); + + /* clean-up internal variable */ + power_request = POWER_REQ_NONE; +} + +/*****************************************************************************/ + +/** + * 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) +{ + int ap_off_flag; + + ap_off_flag = system_get_reset_flags() & RESET_FLAG_AP_OFF; + system_clear_reset_flags(RESET_FLAG_AP_OFF); + /* check if system is already ON */ + if (power_get_signals() & IN_POWER_GOOD) { + if (ap_off_flag) { + CPRINTS("system is on, but RESET_FLAG_AP_OFF is on"); + return POWER_ON_CANCEL; + } else { + CPRINTS("system is on, thus clear " "auto_power_on"); + /* no need to arrange another power on */ + auto_power_on = 0; + return POWER_ON_BY_IN_POWER_GOOD; + } + } else { + CPRINTS("POWER_GOOD is not asserted"); + } + + /* power on requested at EC startup for recovery */ + if (auto_power_on) { + auto_power_on = 0; + return POWER_ON_BY_AUTO_POWER_ON; + } + + /* Check lid open */ + if (lid_opened) { + lid_opened = 0; + return POWER_ON_BY_LID_OPEN; + } + + /* check for power button press */ + if (power_button_is_pressed()) + return POWER_ON_BY_POWER_BUTTON_PRESSED; + + if (power_request == POWER_REQ_ON) { + power_request = POWER_REQ_NONE; + return POWER_ON_BY_POWER_REQ_NONE; + } + + return POWER_OFF_CANCEL; +} + +/** + * Power on the AP + */ +static void power_on(void) +{ + uint64_t t; + + CPRINTS("power_on AP"); + + /* Set pull-up and enable interrupt */ + gpio_set_flags(power_signal_list[MTK_SUSPEND_ASSERTED].gpio, + GPIO_INPUT | GPIO_PULL_UP | GPIO_INT_BOTH); + + /* Make sure we de-assert and AP_RESET_L pin. */ + set_ap_reset(0); + + /* + * Before we push PMIC power button, wait for the PMI RTC ready, which + * takes PMIC_RTC_STARTUP from the AC/battery is plugged in. + */ + t = get_time().val; + if (t < PMIC_RTC_STARTUP) { + uint32_t wait = PMIC_RTC_STARTUP - t; + + CPRINTS("wait for %dms for PMIC RTC start-up", wait / MSEC); + usleep(wait); + } + + /* + * When power_on() is called, we are at S5S3. Initialize components + * to ready state before AP is up. + */ + hook_notify(HOOK_CHIPSET_PRE_INIT); + + /* Push the power button */ + set_pmic_pwron(1); + usleep(PMIC_PWRON_PRESS_TIME); + + /* Wait till the AP has SPI ready */ + /* usleep(PMIC_SPI_READY_TIME); */ + + /* enable interrupt */ + gpio_set_flags(GPIO_SUSPEND_L, INT_BOTH_PULL_UP); + gpio_set_flags(GPIO_EC_INT, GPIO_OUTPUT | GPIO_OUT_HIGH); + + disable_sleep(SLEEP_MASK_AP_RUN); +#ifdef HAS_TASK_POWERLED + powerled_set_state(POWERLED_STATE_ON); +#endif + /* Call hooks now that AP is running */ + hook_notify(HOOK_CHIPSET_STARTUP); + + CPRINTS("AP running ..."); +} + +/** + * Wait for the power button to be released + * + * @param timeout_us Timeout in microseconds, or -1 to wait forever + * @return EC_SUCCESS if ok, or + * EC_ERROR_TIMEOUT if power button failed to release + */ +static int wait_for_power_button_release(unsigned int timeout_us) +{ + timestamp_t deadline; + timestamp_t now = get_time(); + + deadline.val = now.val + timeout_us; + + while (power_button_is_pressed()) { + now = get_time(); + if (timeout_us < 0) { + task_wait_event(-1); + } else if (timestamp_expired(deadline, &now) || + (task_wait_event(deadline.val - now.val) == + TASK_EVENT_TIMER)) { + CPRINTS("power button not released in time"); + return EC_ERROR_TIMEOUT; + } + } + + CPRINTS("power button released"); + power_button_was_pressed = 0; + return EC_SUCCESS; +} + +/** + * 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_turn_off_power_rails(); + + /* Change SUSPEND_L pin to high-Z to reduce power draw. */ + gpio_set_flags(power_signal_list[MTK_SUSPEND_ASSERTED].gpio, + GPIO_INPUT); + + lid_opened = 0; + enable_sleep(SLEEP_MASK_AP_RUN); +#ifdef HAS_TASK_POWERLED + powerled_set_state(POWERLED_STATE_OFF); +#endif + CPRINTS("power shutdown complete"); +} + +void chipset_reset(int is_cold) +{ + if (is_cold) { + CPRINTS("EC triggered cold reboot"); + power_off(); + /* After POWER_GOOD is dropped off, + * the system will be on again + */ + power_request = POWER_REQ_ON; + } else { + CPRINTS("EC triggered warm reboot"); + CPRINTS("assert GPIO_PMIC_WARM_RESET_H for %d ms", + PMIC_WARM_RESET_L_HOLD_TIME / MSEC); + set_pmic_warm_reset(1); + usleep(PMIC_WARM_RESET_L_HOLD_TIME); + set_pmic_warm_reset(0); + } +} + +enum power_state power_handle_state(enum power_state state) +{ + int value; + static int boot_from_g3; + + switch (state) { + case POWER_G3: + boot_from_g3 = check_for_power_on_event(); + if (boot_from_g3) + return POWER_G3S5; + break; + + case POWER_G3S5: + return POWER_S5; + + case POWER_S5: + if (boot_from_g3) { + value = boot_from_g3; + boot_from_g3 = 0; + } else { + value = check_for_power_on_event(); + } + + if (value) { + CPRINTS("power on %d", value); + return POWER_S5S3; + } + return state; + + case POWER_S5S3: + power_on(); + if (power_wait_signals(IN_POWER_GOOD) == EC_SUCCESS) { + CPRINTS("POWER_GOOD seen"); + if (wait_for_power_button_release + (DELAY_SHUTDOWN_ON_POWER_HOLD) == EC_SUCCESS) { + set_pmic_pwron(0); + return POWER_S3; + } else { + CPRINTS("long-press button, shutdown"); + power_off(); + /* + * Since the AP may be up already, return S0S3 + * state to go through the suspend hook. + */ + return POWER_S0S3; + } + } else { + CPRINTS("POWER_GOOD not seen in time"); + } + set_pmic_pwron(0); + return POWER_S5; + + case POWER_S3: + if (!(power_get_signals() & IN_POWER_GOOD)) + return POWER_S3S5; + else if (!(power_get_signals() & IN_SUSPEND)) + return POWER_S3S0; + return state; + + case POWER_S3S0: +#ifdef HAS_TASK_POWERLED + powerled_set_state(POWERLED_STATE_ON); +#endif + hook_notify(HOOK_CHIPSET_RESUME); + return POWER_S0; + + case POWER_S0: + value = check_for_power_off_event(); + if (value) { + CPRINTS("power off %d", value); + power_off(); + return POWER_S0S3; + } else if (power_get_signals() & IN_SUSPEND) { + return POWER_S0S3; + } + return state; + + case POWER_S0S3: +#ifdef HAS_TASK_POWERLED + if (lid_is_open()) + powerled_set_state(POWERLED_STATE_SUSPEND); + else + powerled_set_state(POWERLED_STATE_OFF); +#endif + /* Call hooks here since we don't know it prior to AP suspend */ + hook_notify(HOOK_CHIPSET_SUSPEND); + return POWER_S3; + + case POWER_S3S5: + wait_for_power_button_release(-1); + return POWER_S5; + + case POWER_S5G3: + return POWER_G3; + } + + return state; +} + +static void powerbtn_mtk_changed(void) +{ + task_wake(TASK_ID_CHIPSET); +} + +DECLARE_HOOK(HOOK_POWER_BUTTON_CHANGE, powerbtn_mtk_changed, HOOK_PRIO_DEFAULT); diff --git a/test/build.mk b/test/build.mk index a6d539ae2e..787e99a320 100644 --- a/test/build.mk +++ b/test/build.mk @@ -22,6 +22,9 @@ test-list-$(BOARD_SAMUS)= test-list-$(BOARD_RYU)= test-list-$(BOARD_RYU_P1)= +# llama has issues when building tests +test-list-$(BOARD_LLAMA)= + # For some tests, we are running out of RAM for Samus PD. Disable them for # now. test-list-$(BOARD_SAMUS_PD)= diff --git a/util/flash_ec b/util/flash_ec index 3ad4bf514d..a3ba191e12 100755 --- a/util/flash_ec +++ b/util/flash_ec @@ -66,6 +66,7 @@ BOARDS_STM32=( fruitpie jerry kitty + llama mighty minimuffin nyan |