diff options
author | Randall Spangler <rspangler@chromium.org> | 2012-01-10 14:09:02 -0800 |
---|---|---|
committer | Randall Spangler <rspangler@chromium.org> | 2012-01-10 14:14:59 -0800 |
commit | 39f86d2fad567661d64716d9e6e815802835ccaa (patch) | |
tree | dfc253556afea85e85155a4fc6da756603da8b6d | |
parent | 55ffdb465b7cdfb19380c681ae4542d4022a6aa2 (diff) | |
download | chrome-ec-39f86d2fad567661d64716d9e6e815802835ccaa.tar.gz |
Split power button code into its own file
Signed-off-by: Randall Spangler <rspangler@chromium.org>
BUG=chrome-os-partner:7499
TEST=press and release power button; should see debug messages
Change-Id: I8909ae4643afc98753edb690771618ad43135e3e
-rw-r--r-- | Makefile.toolchain | 3 | ||||
-rw-r--r-- | board/bds/ec.tasklist | 2 | ||||
-rw-r--r-- | board/link/ec.tasklist | 2 | ||||
-rw-r--r-- | chip/lm4/build.mk | 5 | ||||
-rw-r--r-- | chip/lm4/gpio.c | 234 | ||||
-rw-r--r-- | chip/lm4/power_button.c | 201 | ||||
-rw-r--r-- | common/main.c | 2 | ||||
-rw-r--r-- | include/gpio.h | 13 | ||||
-rw-r--r-- | include/power_button.h | 24 |
9 files changed, 272 insertions, 214 deletions
diff --git a/Makefile.toolchain b/Makefile.toolchain index a7a961c4f8..e3d59ea15e 100644 --- a/Makefile.toolchain +++ b/Makefile.toolchain @@ -24,7 +24,8 @@ CFLAGS_WARN=-Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \ -Wno-pointer-sign -fno-strict-overflow -fconserve-stack CFLAGS_DEBUG= -g CFLAGS_INCLUDE=$(foreach i,$(includes),-I$(i) ) -CFLAGS_DEFINE=-DOUTDIR=$(out) -DCHIP=$(CHIP) -DTASKFILE=$(PROJECT).tasklist +CFLAGS_DEFINE=-DOUTDIR=$(out) -DCHIP=$(CHIP) -DTASKFILE=$(PROJECT).tasklist \ + -DBOARD=$(BOARD) -DBOARD_$(BOARD) CPPFLAGS=$(CFLAGS_DEFINE) $(CFLAGS_INCLUDE) CFLAGS=$(CPPFLAGS) $(CFLAGS_CPU) $(CFLAGS_DEBUG) $(CFLAGS_WARN) BUILD_CFLAGS=$(CPPFLAGS) -O3 $(CFLAGS_DEBUG) $(CFLAGS_WARN) diff --git a/board/bds/ec.tasklist b/board/bds/ec.tasklist index 9a1f87398e..f036105fb3 100644 --- a/board/bds/ec.tasklist +++ b/board/bds/ec.tasklist @@ -16,7 +16,7 @@ #define CONFIG_TASK_LIST \ TASK(BLINK, UserLedBlink, NULL) \ TASK(KEYSCAN, keyboard_scan_task, NULL) \ - TASK(GPIOISR, gpio_task, NULL) \ + TASK(POWERBTN, power_button_task, NULL) \ TASK(CONSOLE, console_task, NULL) \ TASK(HOSTCMD, host_command_task, NULL) \ TASK(I8042CMD, i8042_command_task, NULL) diff --git a/board/link/ec.tasklist b/board/link/ec.tasklist index 9a1f87398e..f036105fb3 100644 --- a/board/link/ec.tasklist +++ b/board/link/ec.tasklist @@ -16,7 +16,7 @@ #define CONFIG_TASK_LIST \ TASK(BLINK, UserLedBlink, NULL) \ TASK(KEYSCAN, keyboard_scan_task, NULL) \ - TASK(GPIOISR, gpio_task, NULL) \ + TASK(POWERBTN, power_button_task, NULL) \ TASK(CONSOLE, console_task, NULL) \ TASK(HOSTCMD, host_command_task, NULL) \ TASK(I8042CMD, i8042_command_task, NULL) diff --git a/chip/lm4/build.mk b/chip/lm4/build.mk index 49356d2959..28f7c4da2f 100644 --- a/chip/lm4/build.mk +++ b/chip/lm4/build.mk @@ -1,3 +1,6 @@ +# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. # # LM4 chip specific files build # @@ -6,5 +9,5 @@ CFLAGS_CPU=-mcpu=cortex-m4 -mthumb -Os -mno-sched-prolog chip-objs=init.o panic.o switch.o task.o timer.o pwm.o i2c.o adc.o -chip-objs+=clock.o gpio.o system.o lpc.o uart.o x86_power.o +chip-objs+=clock.o gpio.o system.o lpc.o uart.o x86_power.o power_button.o chip-objs+=flash.o watchdog.o eeprom.o keyboard_scan.o temp_sensor.o diff --git a/chip/lm4/gpio.c b/chip/lm4/gpio.c index 9a6e8429bf..50216106e3 100644 --- a/chip/lm4/gpio.c +++ b/chip/lm4/gpio.c @@ -6,147 +6,29 @@ /* GPIO module for Chrome EC */ #include "gpio.h" +#include "power_button.h" #include "registers.h" #include "task.h" #include "timer.h" #include "uart.h" +#include "util.h" -enum debounce_isr_id -{ - DEBOUNCE_LID, - DEBOUNCE_PWRBTN, - DEBOUNCE_ISR_ID_MAX -}; -struct debounce_isr_t -{ - /* TODO: Add a carry bit to indicate timestamp overflow */ - timestamp_t tstamp; - int started; - void (*callback)(void); +struct gpio_info { + int port; /* Port (LM4_GPIO_*) */ + int mask; /* Bitmask on that port (0x01 - 0x80) */ + void (*irq_handler)(enum gpio_signal signal); }; -struct debounce_isr_t debounce_isr[DEBOUNCE_ISR_ID_MAX]; -enum power_button_state { - PWRBTN_STATE_STOPPED = 0, - PWRBTN_STATE_START = 1, - PWRBTN_STATE_T0 = 2, - PWRBTN_STATE_T1 = 3, - PWRBTN_STATE_T2 = 4, - PWRBTN_STATE_STOPPING = 5, +const struct gpio_info signal_info[EC_GPIO_COUNT] = { + {LM4_GPIO_A, 0x80, NULL}, /* DEBUG_LED */ + {LM4_GPIO_C, 0x20, power_button_interrupt}, /* POWER_BUTTON */ + {LM4_GPIO_C, 0x00, NULL}, /* POWER_BUTTON_OUT */ + {LM4_GPIO_D, 0x01, power_button_interrupt}, /* LID_SWITCH */ + {LM4_GPIO_D, 0x00, NULL}, /* LID_SWITCH_OUT */ }; -static enum power_button_state pwrbtn_state = PWRBTN_STATE_STOPPED; -/* The next timestamp to move onto next state if power button is still pressed. - */ -static timestamp_t pwrbtn_next_ts = {0}; - -#define PWRBTN_DELAY_T0 32000 // 32ms -#define PWRBTN_DELAY_T1 (4000000 - PWRBTN_DELAY_T0) // 4 secs - t0 -#define PWRBTN_DELAY_T2 4000000 // 4 secs - - -static void lid_switch_isr(void) -{ - /* TODO: Currently we pass through the LID_SW# pin to R_EC_LID_OUT# - directly. Modify this if we need to consider more conditions. - */ - uint32_t val = LM4_GPIO_DATA(LM4_GPIO_K, 0x20); - if (val) { - LM4_GPIO_DATA(LM4_GPIO_F, 0x1) = 0x1; - } - else { - LM4_GPIO_DATA(LM4_GPIO_F, 0x1) = 0x0; - } -} - - -/* Power button state machine. - * - * PWRBTN# --- ---- - * to EC |______________________| - * - * - * PWRBTN# --- --------- ---- - * to PCH |__| |___________| - * t0 t1 t2 - */ -static void set_pwrbtn_to_pch(int high) -{ -#if defined(EVT) - LM4_GPIO_DATA(LM4_GPIO_G, 0x80) = high ? 0x80 : 0; // PG7 - R_PBTN_OUT# -#else - uart_printf("[%d] set_pwrbtn_to_pch(%s)\n", get_time().le.lo, high ? "HIGH" : "LOW"); -#endif -} - -static void pwrbtn_sm_start(void) -{ - pwrbtn_state = PWRBTN_STATE_START; - pwrbtn_next_ts = get_time(); // execute action now! -} - -static void pwrbtn_sm_stop(void) -{ - pwrbtn_state = PWRBTN_STATE_STOPPING; - pwrbtn_next_ts = get_time(); // execute action now! -} - -static void pwrbtn_sm_handle(timestamp_t current) -{ - // Not the time to move onto next state. - if (pwrbtn_state == PWRBTN_STATE_STOPPED || - current.val < pwrbtn_next_ts.val) return; - - switch (pwrbtn_state) { - case PWRBTN_STATE_START: - pwrbtn_next_ts.val = current.val + PWRBTN_DELAY_T0; - pwrbtn_state = PWRBTN_STATE_T0; - set_pwrbtn_to_pch(0); - break; - case PWRBTN_STATE_T0: - pwrbtn_next_ts.val = current.val + PWRBTN_DELAY_T1; - pwrbtn_state = PWRBTN_STATE_T1; - set_pwrbtn_to_pch(1); - break; - case PWRBTN_STATE_T1: - pwrbtn_next_ts.val = current.val + PWRBTN_DELAY_T2; - pwrbtn_state = PWRBTN_STATE_T2; - set_pwrbtn_to_pch(0); - break; - case PWRBTN_STATE_T2: - /* T2 has passed */ - case PWRBTN_STATE_STOPPING: - set_pwrbtn_to_pch(1); - pwrbtn_state = PWRBTN_STATE_STOPPED; - break; - default: - break; - } -} - -static void power_button_isr(void) -{ -#if defined(EVT) - uint32_t val = LM4_GPIO_DATA(LM4_GPIO_K, 0x80); // PK7 -#else - uint32_t val = LM4_GPIO_DATA(LM4_GPIO_C, 0x20); // PC5 -#endif - if (!val) { - /* pressed */ - pwrbtn_sm_start(); - /* TODO: implement after chip/lm4/x86_power.c is completed. */ - // if system is in S5, power_on_system() - // elif system is in S3, resume_system() - // else S0 i8042_send_host(make_code); - } else { - /* released */ - pwrbtn_sm_stop(); - /* TODO: implement after chip/lm4/x86_power.c is completed. */ - // if system in S0, i8042_send_host(break_code); - } -} int gpio_pre_init(void) { @@ -163,6 +45,7 @@ int gpio_pre_init(void) LM4_GPIO_DEN(LM4_GPIO_A) |= 0x80; LM4_GPIO_DIR(LM4_GPIO_A) |= 0x80; +#ifdef BOARD_link /* Set up LID switch input (block K pin 5) */ LM4_GPIO_PCTL(LM4_GPIO_K) &= ~(0xf00000); LM4_GPIO_DIR(LM4_GPIO_K) &= ~(0x20); @@ -183,9 +66,10 @@ int gpio_pre_init(void) (LM4_GPIO_DATA(LM4_GPIO_K, 0x20) ? 1 : 0); LM4_GPIO_DIR(LM4_GPIO_F) |= 0x1; LM4_GPIO_DEN(LM4_GPIO_F) |= 0x1; +#endif /* Setup power button input and output pins */ -#if defined(EVT) +#ifdef BOARD_link /* input: PK7 */ LM4_GPIO_PCTL(LM4_GPIO_K) &= ~0xf0000000; LM4_GPIO_DIR(LM4_GPIO_K) &= ~0x80; @@ -212,76 +96,35 @@ int gpio_pre_init(void) } -int gpio_init(void) -{ - debounce_isr[DEBOUNCE_LID].started = 0; - debounce_isr[DEBOUNCE_LID].callback = lid_switch_isr; - debounce_isr[DEBOUNCE_PWRBTN].started = 0; - debounce_isr[DEBOUNCE_PWRBTN].callback = power_button_isr; - - return EC_SUCCESS; -} - - int gpio_get_level(enum gpio_signal signal) { - switch (signal) { - case EC_GPIO_DEBUG_LED: - return LM4_GPIO_DATA(LM4_GPIO_A, 0x80) & 0x80 ? 1 : 0; - default: - return 0; - } + return LM4_GPIO_DATA(signal_info[signal].port, + signal_info[signal].mask) ? 1 : 0; } int gpio_set_level(enum gpio_signal signal, int value) { - switch (signal) { - case EC_GPIO_DEBUG_LED: - LM4_GPIO_DATA(LM4_GPIO_A, 0x80) = (value ? 0x80 : 0); - return EC_SUCCESS; - default: - return EC_ERROR_UNKNOWN; - } + /* Ok to write 0xff becuase LM4_GPIO_DATA bit-masks only the bit + * we care about. */ + LM4_GPIO_DATA(signal_info[signal].port, + signal_info[signal].mask) = (value ? 0xff : 0); + return EC_SUCCESS; } + static void gpio_interrupt(int port, uint32_t mis) { - timestamp_t timelimit; - - /* Set 30 ms debounce timelimit */ - timelimit = get_time(); - timelimit.val += 30000; + int i = 0; + const struct gpio_info *g = signal_info; - /* Handle interrupts */ - if (port == LM4_GPIO_K && (mis & 0x20)) { - debounce_isr[DEBOUNCE_LID].tstamp = timelimit; - debounce_isr[DEBOUNCE_LID].started = 1; - } - - /* Handle power button */ -#if defined(EVT) - if (port == LM4_GPIO_K && (mis & 0x80)) { // PK7 -#else - if (port == LM4_GPIO_C && (mis & 0x20)) { // PC5 -#endif - debounce_isr[DEBOUNCE_PWRBTN].tstamp = timelimit; - debounce_isr[DEBOUNCE_PWRBTN].started = 1; + for (i = 0; i < EC_GPIO_COUNT; i++, g++) { + if (port == g->port && (mis & g->mask) && g->irq_handler) + g->irq_handler(i); } } -static void __gpio_k_interrupt(void) -{ - uint32_t mis = LM4_GPIO_MIS(LM4_GPIO_K); - - /* Clear the interrupt bits we received */ - LM4_GPIO_ICR(LM4_GPIO_K) = mis; - - gpio_interrupt(LM4_GPIO_K, mis); -} -DECLARE_IRQ(LM4_IRQ_GPIOK, __gpio_k_interrupt, 1); -#if !defined(EVT) static void __gpio_c_interrupt(void) { uint32_t mis = LM4_GPIO_MIS(LM4_GPIO_C); @@ -292,26 +135,9 @@ static void __gpio_c_interrupt(void) gpio_interrupt(LM4_GPIO_C, mis); } DECLARE_IRQ(LM4_IRQ_GPIOC, __gpio_c_interrupt, 1); -#endif - -int gpio_task(void) -{ - int i; - timestamp_t ts; - while (1) { - usleep(1000); - ts = get_time(); - for (i = 0; i < DEBOUNCE_ISR_ID_MAX; ++i) { - if (debounce_isr[i].started && - ts.val >= debounce_isr[i].tstamp.val) { - debounce_isr[i].started = 0; - debounce_isr[i].callback(); - } - } - - pwrbtn_sm_handle(ts); - } +int gpio_init(void) +{ return EC_SUCCESS; } diff --git a/chip/lm4/power_button.c b/chip/lm4/power_button.c new file mode 100644 index 0000000000..28519b6e5f --- /dev/null +++ b/chip/lm4/power_button.c @@ -0,0 +1,201 @@ +/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Power button and lid switch module for Chrome EC */ + +#include "gpio.h" +#include "power_button.h" +#include "task.h" +#include "timer.h" +#include "uart.h" + +enum debounce_isr_id { + DEBOUNCE_LID, + DEBOUNCE_PWRBTN, + DEBOUNCE_ISR_ID_MAX +}; + +struct debounce_isr_t { + /* TODO: Add a carry bit to indicate timestamp overflow */ + timestamp_t tstamp; + int started; + void (*callback)(void); +}; + +struct debounce_isr_t debounce_isr[DEBOUNCE_ISR_ID_MAX]; + +enum power_button_state { + PWRBTN_STATE_STOPPED = 0, + PWRBTN_STATE_START = 1, + PWRBTN_STATE_T0 = 2, + PWRBTN_STATE_T1 = 3, + PWRBTN_STATE_T2 = 4, + PWRBTN_STATE_STOPPING = 5, +}; +static enum power_button_state pwrbtn_state = PWRBTN_STATE_STOPPED; +/* The next timestamp to move onto next state if power button is still pressed. + */ +static timestamp_t pwrbtn_next_ts = {0}; +static int init_called; + +#define PWRBTN_DELAY_T0 32000 /* 32ms */ +#define PWRBTN_DELAY_T1 (4000000 - PWRBTN_DELAY_T0) /* 4 secs - t0 */ +#define PWRBTN_DELAY_T2 4000000 /* 4 secs */ + + +static void lid_switch_isr(void) +{ + /* TODO: Currently we pass through the LID_SW# pin to R_EC_LID_OUT# + * directly. Modify this if we need to consider more conditions. */ +#ifdef BOARD_bds + gpio_set_level(EC_GPIO_LID_SWITCH_OUT, + gpio_get_level(EC_GPIO_LID_SWITCH)); +#endif +} + + +/* Power button state machine. + * + * PWRBTN# --- ---- + * to EC |______________________| + * + * + * PWRBTN# --- --------- ---- + * to PCH |__| |___________| + * t0 t1 t2 + */ +static void set_pwrbtn_to_pch(int high) +{ +#ifdef BOARD_link + gpio_set_level(EC_GPIO_POWER_BUTTON_OUT, high); +#else + uart_printf("[%d] set_pwrbtn_to_pch(%s)\n", + get_time().le.lo, high ? "HIGH" : "LOW"); +#endif +} + + +static void pwrbtn_sm_start(void) +{ + pwrbtn_state = PWRBTN_STATE_START; + pwrbtn_next_ts = get_time(); /* execute action now! */ +} + + +static void pwrbtn_sm_stop(void) +{ + pwrbtn_state = PWRBTN_STATE_STOPPING; + pwrbtn_next_ts = get_time(); /* execute action now ! */ +} + + +static void pwrbtn_sm_handle(timestamp_t current) +{ + /* Not the time to move onto next state */ + if (pwrbtn_state == PWRBTN_STATE_STOPPED || + current.val < pwrbtn_next_ts.val) + return; + + switch (pwrbtn_state) { + case PWRBTN_STATE_START: + pwrbtn_next_ts.val = current.val + PWRBTN_DELAY_T0; + pwrbtn_state = PWRBTN_STATE_T0; + set_pwrbtn_to_pch(0); + break; + case PWRBTN_STATE_T0: + pwrbtn_next_ts.val = current.val + PWRBTN_DELAY_T1; + pwrbtn_state = PWRBTN_STATE_T1; + set_pwrbtn_to_pch(1); + break; + case PWRBTN_STATE_T1: + pwrbtn_next_ts.val = current.val + PWRBTN_DELAY_T2; + pwrbtn_state = PWRBTN_STATE_T2; + set_pwrbtn_to_pch(0); + break; + case PWRBTN_STATE_T2: + /* T2 has passed */ + case PWRBTN_STATE_STOPPING: + set_pwrbtn_to_pch(1); + pwrbtn_state = PWRBTN_STATE_STOPPED; + break; + default: + break; + } +} + + +static void power_button_isr(void) +{ + if (!gpio_get_level(EC_GPIO_POWER_BUTTON)) { + /* pressed */ + pwrbtn_sm_start(); + /* TODO: implement after chip/lm4/x86_power.c is completed. */ + /* if system is in S5, power_on_system() + * elif system is in S3, resume_system() + * else S0 i8042_send_host(make_code); */ + } else { + /* released */ + pwrbtn_sm_stop(); + /* TODO: implement after chip/lm4/x86_power.c is completed. */ + /* if system in S0, i8042_send_host(break_code); */ + } +} + + +void power_button_interrupt(enum gpio_signal signal) +{ + timestamp_t timelimit; + int d = (signal == EC_GPIO_LID_SWITCH ? DEBOUNCE_LID : DEBOUNCE_PWRBTN); + + /* TODO: (crosbug.com/p/7456) there's currently a race condition where + * we can get an interrupt before clock_init() has been called. In + * this case, get_time crashes(). Work around this by checking if our + * init has been called and ignoring interrupts if so. */ + if (!init_called) + return; + + /* Set 30 ms debounce timelimit */ + timelimit = get_time(); + timelimit.val += 30000; + + /* Handle lid switch and power button debounce */ + debounce_isr[d].tstamp = timelimit; + debounce_isr[d].started = 1; +} + + +int power_button_init(void) +{ + debounce_isr[DEBOUNCE_LID].started = 0; + debounce_isr[DEBOUNCE_LID].callback = lid_switch_isr; + debounce_isr[DEBOUNCE_PWRBTN].started = 0; + debounce_isr[DEBOUNCE_PWRBTN].callback = power_button_isr; + + init_called = 1; + + return EC_SUCCESS; +} + + +void power_button_task(void) +{ + int i; + timestamp_t ts; + + while (1) { + usleep(1000); + ts = get_time(); + for (i = 0; i < DEBOUNCE_ISR_ID_MAX; ++i) { + if (debounce_isr[i].started && + ts.val >= debounce_isr[i].tstamp.val) { + debounce_isr[i].started = 0; + debounce_isr[i].callback(); + } + } + + pwrbtn_sm_handle(ts); + } +} + diff --git a/common/main.c b/common/main.c index 47f3e0f0e9..c2c71fd67a 100644 --- a/common/main.c +++ b/common/main.c @@ -21,6 +21,7 @@ #include "lpc.h" #include "memory_commands.h" #include "port80.h" +#include "power_button.h" #include "powerdemo.h" #include "pwm.h" #include "pwm_commands.h" @@ -81,6 +82,7 @@ int main(void) i2c_init(); temp_sensor_init(); memory_commands_init(); + power_button_init(); keyboard_init(); adc_init(); diff --git a/include/gpio.h b/include/gpio.h index 5e4edf0593..54ea231dba 100644 --- a/include/gpio.h +++ b/include/gpio.h @@ -12,12 +12,13 @@ /* GPIO signal definitions. */ enum gpio_signal { - /* Firmware write protect */ - EC_GPIO_WRITE_PROTECT = 0, - /* Recovery switch */ - EC_GPIO_RECOVERY_SWITCH, - /* Debug LED */ - EC_GPIO_DEBUG_LED + EC_GPIO_DEBUG_LED = 0, /* Debug LED */ + EC_GPIO_POWER_BUTTON, /* Power button */ + EC_GPIO_POWER_BUTTON_OUT, /* Power button output to PCH */ + EC_GPIO_LID_SWITCH, /* Lid switch */ + EC_GPIO_LID_SWITCH_OUT, /* Lid switch output to PCH */ + /* Number of GPIOs; not an actual GPIO */ + EC_GPIO_COUNT }; diff --git a/include/power_button.h b/include/power_button.h new file mode 100644 index 0000000000..c6cf4f4e24 --- /dev/null +++ b/include/power_button.h @@ -0,0 +1,24 @@ +/* Copyright (c) 2011 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. + */ + +/* Power button module for Chrome EC */ + +#ifndef __CROS_EC_POWER_BUTTON_H +#define __CROS_EC_POWER_BUTTON_H + +#include "common.h" +#include "gpio.h" + +/* Initializes the module. */ +int power_button_init(void); + +/* Interrupt handler for the power button and lid switch. Passed the signal + * which triggered the interrupt. */ +void power_button_interrupt(enum gpio_signal signal); + +/* Power button task */ +void power_button_task(void); + +#endif /* __CROS_EC_POWER_BUTTON_H */ |