diff options
author | David Hendricks <dhendrix@chromium.org> | 2012-02-14 18:41:52 -0800 |
---|---|---|
committer | David Hendricks <dhendrix@chromium.org> | 2012-02-17 20:30:07 -0800 |
commit | a3d621f1b2ae63518ebb1fb273ad0ef9fed6ab09 (patch) | |
tree | e935efa28b18080cd382b821dd3625292a9b8717 | |
parent | ad7a0d5ae67cd73fe11c92c1964496455490f2c9 (diff) | |
download | chrome-ec-a3d621f1b2ae63518ebb1fb273ad0ef9fed6ab09.tar.gz |
Add keyboard_scan for STM32
This loosely ports the LM4 keyboard_scan code to STM32
Notable differences:
- Keyboard GPIO layout is spread across multiple ports and is not
contiguous in many places. Because of this, bitmasks are mostly
generated on-the-fly instead of hard coded (IO is kept to a minimum)
- Longer timeout when scanning columns (100us versus 20us)
Also, some functions are stubbed out currently since they rely on
other bits being implemented:
- keyboard_state_changed()
- keyboard_has_char()
- keyboard_put_char()
BUG=none
TEST=Tested on STM32L-Discovery (monitoring keystrokes via UART)
Change-Id: I84985879589e70688b2b29b288ab17037f7668b2
-rw-r--r-- | board/discovery/board.c | 14 | ||||
-rw-r--r-- | board/discovery/board.h | 11 | ||||
-rw-r--r-- | board/discovery/ec.tasklist | 1 | ||||
-rw-r--r-- | chip/stm32l/build.mk | 1 | ||||
-rw-r--r-- | chip/stm32l/keyboard_scan.c | 465 |
5 files changed, 490 insertions, 2 deletions
diff --git a/board/discovery/board.c b/board/discovery/board.c index 0669bc1187..5a8f601cec 100644 --- a/board/discovery/board.c +++ b/board/discovery/board.c @@ -12,7 +12,15 @@ /* GPIO signal list. Must match order from enum gpio_signal. */ const struct gpio_info gpio_list[GPIO_COUNT] = { /* Inputs with interrupt handlers are first for efficiency */ - {"USER_BUTTON", GPIO_A, (1<<0), GPIO_INT_BOTH, NULL}, + {"USER_BUTTON", GPIO_A, (1<<0), GPIO_INT_BOTH, NULL}, + {"KB_COL00", GPIO_C, (1<<8), GPIO_INT_BOTH, matrix_interrupt}, + {"KB_COL01", GPIO_C, (1<<9), GPIO_INT_BOTH, matrix_interrupt}, + {"KB_COL02", GPIO_C, (1<<10), GPIO_INT_BOTH, matrix_interrupt}, + {"KB_COL03", GPIO_C, (1<<11), GPIO_INT_BOTH, matrix_interrupt}, + {"KB_COL04", GPIO_C, (1<<12), GPIO_INT_BOTH, matrix_interrupt}, + {"KB_COL05", GPIO_C, (1<<14), GPIO_INT_BOTH, matrix_interrupt}, + {"KB_COL06", GPIO_C, (1<<15), GPIO_INT_BOTH, matrix_interrupt}, + {"KB_COL07", GPIO_D, (1<<2), GPIO_INT_BOTH, matrix_interrupt}, /* Other inputs */ /* Outputs */ {"BLUE_LED", GPIO_B, (1<<6), GPIO_OUT_LOW, NULL}, @@ -21,10 +29,12 @@ const struct gpio_info gpio_list[GPIO_COUNT] = { void configure_board(void) { - /* Enable all GPIOs clocks + /* Enable all GPIOs clocks in normal run-mode, enable keyboard + * GPIO clocks in low-power (sleep) mode. * TODO: more fine-grained enabling for power saving */ STM32L_RCC_AHBENR |= 0x3f; + STM32L_RCC_AHBLPENR |= 0x0e; #if CONFIG_CONSOLE_UART == 1 /* Select Alternate function for USART1 on pins PA9/PA10 */ diff --git a/board/discovery/board.h b/board/discovery/board.h index 69cc237d62..12eeaf5246 100644 --- a/board/discovery/board.h +++ b/board/discovery/board.h @@ -23,6 +23,15 @@ enum gpio_signal { /* Inputs with interrupt handlers are first for efficiency */ GPIO_USER_BUTTON = 0, /* Blue user button */ + /* Keyboard inputs */ + KB_COL00, + KB_COL01, + KB_COL02, + KB_COL03, + KB_COL04, + KB_COL05, + KB_COL06, + KB_COL07, /* Other inputs */ /* Outputs */ GPIO_BLUE_LED, /* Blue debug LED */ @@ -34,4 +43,6 @@ enum gpio_signal { void configure_board(void); +void matrix_interrupt(enum gpio_signal signal); + #endif /* __BOARD_H */ diff --git a/board/discovery/ec.tasklist b/board/discovery/ec.tasklist index 0e5992b96b..98f6cb5bfd 100644 --- a/board/discovery/ec.tasklist +++ b/board/discovery/ec.tasklist @@ -15,4 +15,5 @@ */ #define CONFIG_TASK_LIST \ TASK(WATCHDOG, watchdog_task, NULL) \ + TASK(KEYSCAN, keyboard_scan_task, NULL) \ TASK(CONSOLE, console_task, NULL) diff --git a/chip/stm32l/build.mk b/chip/stm32l/build.mk index d8a0b28c07..884528b52d 100644 --- a/chip/stm32l/build.mk +++ b/chip/stm32l/build.mk @@ -11,3 +11,4 @@ CORE:=cortex-m chip-y=uart.o clock.o hwtimer.o system.o gpio.o chip-y+=jtag.o stubs.o chip-$(CONFIG_TASK_WATCHDOG)+=watchdog.o +chip-$(CONFIG_TASK_KEYSCAN)+=keyboard_scan.o diff --git a/chip/stm32l/keyboard_scan.c b/chip/stm32l/keyboard_scan.c new file mode 100644 index 0000000000..b9a6f1b927 --- /dev/null +++ b/chip/stm32l/keyboard_scan.c @@ -0,0 +1,465 @@ +/* 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. + */ + +/* Keyboard scanner module for Chrome EC */ + +#include "board.h" +#include "gpio.h" +#include "keyboard.h" +#include "keyboard_scan.h" +#include "registers.h" +#include "task.h" +#include "timer.h" +#include "uart.h" +#include "util.h" + +/* Notes: + * + * Daisy schematic calls the outputs rows and the inputs columns. The + * codebase uses the opposite convention. + * + * Outputs: Open-drain, pull-up, output '1' --> impedence state (Hi-Z) + * Inputs: Pull-up + * Daisy: + * + * Columns (outputs): + * KB_ROW00 = PB5 + * KB_ROW01 = PB8 + * KB_ROW02:5 = PB12:15 + * KB_ROW06:8 = PC0:2 + * KB_ROW09:12 = PC4:7 + * Rows (inputs): + * KB_COL00:04 = PC8:12 + * KB_COL05:06 = PC14:15 + * KB_COL07 = PD2 + * Other: + * + * + * Discovery: + * + * Columns (outputs): + * KB_ROW00 = PB5 + * KB_ROW01 = PB8 + * KB_ROW02:05 = PB12:15 + * KB_ROW06:08 = PC0:2 + * KB_ROW09:10 = PA1:2 + * KB_ROW11:12 = PC6:7 + * Rows (inputs): + * KB_COL00:04 = PC8:12 + * KB_COL05:06 = PC14:15 + * KB_COL07 = PD2 + * Other: + */ + +extern struct gpio_info gpio_list[]; + +/* used for select_column() */ +enum COL_INDEX { + COL_ASSERT_ALL = -2, + COL_TRI_STATE_ALL = -1, + /* 0 ~ 12 for the corresponding column */ +}; + +#define POLLING_MODE_TIMEOUT 1000000 /* 1 sec */ +#define SCAN_LOOP_DELAY 10000 /* 10 ms */ + +#define KB_COLS 13 + +/* 15:14, 12:8, 2 */ +#define IRQ_MASK 0xdf04 + +static uint8_t raw_state[KB_COLS]; + +/* Mask with 1 bits only for keys that actually exist */ +static const uint8_t *actual_key_mask; + +/* All actual key masks (todo: move to keyboard matrix definition) */ +/* TODO: (crosbug.com/p/7485) fill in real key mask with 0-bits for coords that + aren't keys */ +static const uint8_t actual_key_masks[4][KB_COLS] = { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0}, + {0}, + {0}, + }; + +struct kbc_gpio { + int num; /* logical row or column number */ + uint32_t port; + int pin; +}; + +const struct kbc_gpio kbc_outputs[] = { + /* Keep this in order of column number */ +#if defined(BOARD_daisy) + { 0, GPIO_B, 5 }, /* KB_ROW00: PB5 */ + { 1, GPIO_B, 8 }, /* KB_ROW01: PB8 */ + { 2, GPIO_B, 12 }, /* KB_ROW02: PB12 */ + { 3, GPIO_B, 13 }, /* KB_ROW03: PB13 */ + { 4, GPIO_B, 14 }, /* KB_ROW04: PB14 */ + { 5, GPIO_B, 15 }, /* KB_ROW05: PB15 */ + { 6, GPIO_C, 0 }, /* KB_ROW06: PC0 */ + { 7, GPIO_C, 1 }, /* KB_ROW07: PC1 */ + { 8, GPIO_C, 2 }, /* KB_ROW08: PC2 */ + { 9, GPIO_C, 4 }, /* KB_ROW09: PC4 */ + { 10, GPIO_C, 5 }, /* KB_ROW10: PC5 */ + { 11, GPIO_C, 6 }, /* KB_ROW11: PC6 */ + { 12, GPIO_C, 7 }, /* KB_ROW12: PC7 */ +#elif defined(BOARD_discovery) + { 0, GPIO_B, 5 }, /* KB_ROW00: PB5 */ + { 1, GPIO_B, 8 }, /* KB_ROW01: PB8 */ + { 2, GPIO_B, 12 }, /* KB_ROW02: PB12 */ + { 3, GPIO_B, 13 }, /* KB_ROW03: PB13 */ + { 4, GPIO_B, 14 }, /* KB_ROW04: PB14 */ + { 5, GPIO_B, 15 }, /* KB_ROW05: PB15 */ + { 6, GPIO_C, 0 }, /* KB_ROW06: PC0 */ + { 7, GPIO_C, 1 }, /* KB_ROW07: PC1 */ + { 8, GPIO_C, 2 }, /* KB_ROW08: PC2 */ + { 9, GPIO_A, 1 }, /* KB_ROW09: PA1 */ + { 10, GPIO_A, 2 }, /* KB_ROW10: PA2 */ + { 11, GPIO_C, 6 }, /* KB_ROW11: PC6 */ + { 12, GPIO_C, 7 }, /* KB_ROW12: PC7 */ +#elif defined(BOARD_adv) + { 0, GPIO_B, 5 }, /* KB_ROW00: PB5 */ + { 1, GPIO_B, 8 }, /* KB_ROW01: PB8 */ + { 2, GPIO_B, 12 }, /* KB_ROW02: PB12 */ + { 3, GPIO_B, 14 }, /* KB_ROW03: PB14 */ + { 4, GPIO_B, 15 }, /* KB_ROW04: PB15 */ + { 5, GPIO_C, 0 }, /* KB_ROW05: PC0 */ + { 6, GPIO_C, 2 }, /* KB_ROW06: PC2 */ + { 7, GPIO_C, 4 }, /* KB_ROW07: PC4 */ + { 8, GPIO_C, 5 }, /* KB_ROW08: PC5 */ + { 9, GPIO_C, 6 }, /* KB_ROW09: PC6 */ + { 10, GPIO_B, 13 }, /* KB_ROW10: PB13 */ + { 11, GPIO_C, 1 }, /* KB_ROW11: PC1 */ + { 12, GPIO_C, 7 }, /* KB_ROW12: PC7 */ +#else +#error "Need to define columns (outputs) for this board" +#endif +}; + +const struct kbc_gpio kbc_inputs[] = { +#if defined(BOARD_daisy) || defined(BOARD_discovery) || defined(BOARD_adv) + { 0, GPIO_C, 8 }, /* KB_COL00: PC8 */ + { 1, GPIO_C, 9 }, /* KB_COL01: PC9 */ + { 2, GPIO_C, 10 }, /* KB_COL02: PC10 */ + { 3, GPIO_C, 11 }, /* KB_COL03: PC11 */ + { 4, GPIO_C, 12 }, /* KB_COL04: PC12 */ + { 5, GPIO_C, 14 }, /* KB_COL05: PC14 */ + { 6, GPIO_C, 15 }, /* KB_COL06: PC15 */ + { 7, GPIO_D, 2 }, /* KB_COL07: PD2 */ +#else +#error "Need to define rows (inputs) for this board" +#endif +}; + +#if defined(BOARD_daisy) || defined(BOARD_adv) +static const uint32_t ports[] = { GPIO_B, GPIO_C, GPIO_D }; +#elif defined(BOARD_discovery) +static const uint32_t ports[] = { GPIO_A, GPIO_B, GPIO_C, GPIO_D }; +#else +#error "Need to specify GPIO ports used by keyboard" +#endif + +static void select_column(int col) +{ + int i; + int done = 0; + + for (i = 0; i < ARRAY_SIZE(ports); i++) { + uint32_t bsrr = 0; + int j; + + for (j = 0; j < ARRAY_SIZE(kbc_outputs); j++) { + if (kbc_outputs[j].port != ports[i]) + continue; + + if (col == COL_ASSERT_ALL) { + /* drive low (clear output data) */ + bsrr |= (1 << (kbc_outputs[j].pin)) << 16; + } else if (col == COL_TRI_STATE_ALL) { + /* put column in hi-Z state (set output data) */ + bsrr |= 1 << (kbc_outputs[j].pin); + } else { + /* drive specified column low, others => hi-Z */ + if (kbc_outputs[j].num == col) { + /* to avoid conflict, tri-state all + * columns first, then assert column */ + select_column(COL_TRI_STATE_ALL); + bsrr |= (1 << kbc_outputs[j].pin) << 16; + done = 1; + break; + } + } + } + + if (bsrr) + STM32L_GPIO_BSRR_OFF(ports[i]) = bsrr; + + if (done) + break; + } +} + +int keyboard_scan_init(void) +{ + int i, j; + uint32_t tmp32; + uint16_t tmp16; + + uart_printf("[kbscan %s()] initializing keyboard...\n", __func__); + + /* initialize outputs (pull-up, open-drain) + * TODO: this should be done via GPIO declaration in board.c */ + for (i = 0; i < ARRAY_SIZE(ports); i++) { + uint32_t mask32 = 0, mode = 0, pupd = 0; + uint16_t mask16 = 0, otype = 0; + + for (j = 0; j < ARRAY_SIZE(kbc_outputs); j++) { + if (kbc_outputs[j].port != ports[i]) + continue; + + mask32 |= 3 << (kbc_outputs[j].pin * 2); + mask16 |= 1 << kbc_outputs[j].pin; + + /* output mode */ + mode |= 1 << (kbc_outputs[j].pin * 2); + + /* pull-up */ + pupd |= 1 << (kbc_outputs[j].pin * 2); + + /* open-drain */ + otype |= 1 << kbc_outputs[j].pin; + } + + if (!mask32) /* nothing to do on this port */ + continue; + + tmp32 = STM32L_GPIO_MODER_OFF(ports[i]); + tmp32 = (tmp32 & ~mask32) | mode; + STM32L_GPIO_MODER_OFF(ports[i]) = tmp32; + + tmp32 = STM32L_GPIO_PUPDR_OFF(ports[i]); + tmp32 = (tmp32 & ~mask32) | pupd; + STM32L_GPIO_PUPDR_OFF(ports[i]) = tmp32; + + tmp16 = STM32L_GPIO_OTYPER_OFF(ports[i]); + tmp16 = (tmp16 & ~mask16) | otype; + STM32L_GPIO_OTYPER_OFF(ports[i]) = tmp16; + } + + /* Tri-state (put into Hi-Z) the outputs */ + select_column(COL_TRI_STATE_ALL); + + /* initialize inputs + * TODO: this should be done via GPIO declaration in board.c */ + for (i = 0; i < ARRAY_SIZE(ports); i++) { + uint32_t mask32 = 0, pupd = 0; + + for (j = 0; j < ARRAY_SIZE(kbc_inputs); j++) { + if (kbc_inputs[j].port != ports[i]) + continue; + + mask32 |= 3 << (kbc_inputs[j].pin * 2); + + /* pull-up */ + pupd |= 1 << (kbc_inputs[j].pin * 2); + } + + if (!mask32) + continue; /* nothing to do on this port */ + + STM32L_GPIO_MODER_OFF(ports[i]) &= ~mask32; + + tmp32 = STM32L_GPIO_PUPDR_OFF(ports[i]); + tmp32 = (tmp32 & ~mask32) | pupd; + STM32L_GPIO_PUPDR_OFF(ports[i]) = tmp32; + } + + /* Initialize raw state */ + for (i = 0; i < KB_COLS; i++) + raw_state[i] = 0; + + /* TODO: method to set which keyboard we have, so we set the actual + * key mask properly */ + actual_key_mask = actual_key_masks[0]; + + gpio_enable_interrupt(KB_COL00); + gpio_enable_interrupt(KB_COL01); + gpio_enable_interrupt(KB_COL02); + gpio_enable_interrupt(KB_COL03); + gpio_enable_interrupt(KB_COL04); + gpio_enable_interrupt(KB_COL05); + gpio_enable_interrupt(KB_COL06); + gpio_enable_interrupt(KB_COL07); + + return EC_SUCCESS; +} + + +void wait_for_interrupt(void) +{ + uint32_t pr_before, pr_after; + + /* Assert all outputs would trigger un-wanted interrupts. + * Clear them before enable interrupt. */ + pr_before = STM32L_EXTI_PR; + select_column(COL_ASSERT_ALL); + pr_after = STM32L_EXTI_PR; + STM32L_EXTI_PR |= ((pr_after & ~pr_before) & IRQ_MASK); + + STM32L_EXTI_IMR |= IRQ_MASK; /* 1: unmask interrupt */ +} + + +void enter_polling_mode(void) +{ + STM32L_EXTI_IMR &= ~IRQ_MASK; /* 0: mask interrupts */ + select_column(COL_TRI_STATE_ALL); +} + + +/* Returns 1 if any key is still pressed. 0 if no key is pressed. */ +static int check_keys_changed(void) +{ + int c; + uint8_t r; + int change = 0; + int num_press = 0; + + for (c = 0; c < KB_COLS; c++) { + uint16_t tmp; + + /* Select column, then wait a bit for it to settle */ + select_column(c); + udelay(100); + + r = 0; +#if defined(BOARD_daisy) || defined(BOARD_discovery) || defined(BOARD_adv) + tmp = STM32L_GPIO_IDR(C); + /* KB_COL00:04 = PC8:12 */ + if (tmp & (1 << 8)) + r |= 1 << 0; + if (tmp & (1 << 9)) + r |= 1 << 1; + if (tmp & (1 << 10)) + r |= 1 << 2; + if (tmp & (1 << 11)) + r |= 1 << 3; + if (tmp & (1 << 12)) + r |= 1 << 4; + /* KB_COL05:06 = PC14:15 */ + if (tmp & (1 << 14)) + r |= 1 << 5; + if (tmp & (1 << 15)) + r |= 1 << 6; + + tmp = STM32L_GPIO_IDR(D); + /* KB_COL07 = PD2 */ + if (tmp & (1 << 2)) + r |= 1 << 7; + + /* Invert it so 0=not pressed, 1=pressed */ + r ^= 0xff; +#else +#error "Key scanning unsupported on this board" +#endif + /* Mask off keys that don't exist so they never show + * as pressed */ + r &= actual_key_mask[c]; + +#ifdef OR_WITH_CURRENT_STATE_FOR_TESTING + /* KLUDGE - or current state in, so we can make sure + * all the lines are hooked up */ + r |= raw_state[c]; +#endif + + /* Check for changes */ + if (r != raw_state[c]) { + int i; + for (i = 0; i < 8; ++i) { + uint8_t prev = (raw_state[c] >> i) & 1; + uint8_t now = (r >> i) & 1; + if (prev != now) + /* TODO: implement this */ + ; //keyboard_state_changed(i, c, now); + } + raw_state[c] = r; + change = 1; + } + } + select_column(COL_TRI_STATE_ALL); + + /* Count number of key pressed */ + for (c = 0; c < KB_COLS; c++) { + if (raw_state[c]) + ++num_press; + } + + if (change) { + uart_printf("[%d keys pressed: ", num_press); + for (c = 0; c < KB_COLS; c++) { + if (raw_state[c]) + uart_printf(" %02x", raw_state[c]); + else + uart_puts(" --"); + } + uart_puts("]\n"); + } + + return num_press ? 1 : 0; +} + + +void keyboard_scan_task(void) +{ + int key_press_timer = 0; + + keyboard_scan_init(); + + while (1) { + wait_for_interrupt(); + task_wait_msg(-1); + + enter_polling_mode(); + /* Busy polling keyboard state. */ + while (1) { + /* sleep for debounce. */ + usleep(SCAN_LOOP_DELAY); + /* Check for keys down */ + if (check_keys_changed()) { + key_press_timer = 0; + } else { + if (++key_press_timer >= + (POLLING_MODE_TIMEOUT / SCAN_LOOP_DELAY)) { + key_press_timer = 0; + break; /* exit the while loop */ + } + } + } + /* TODO: (crosbug.com/p/7484) A race condition here. + * If a key state is changed here (before interrupt is + * enabled), it will be lost. + */ + } +} + + +void matrix_interrupt(enum gpio_signal signal) +{ + task_send_msg(TASK_ID_KEYSCAN, TASK_ID_KEYSCAN, 0); +} + +int keyboard_has_char() +{ + /* TODO: needs to be implemented */ + return 0; +} + +void keyboard_put_char(uint8_t chr, int send_irq) +{ + /* TODO: needs to be implemented */ +} |