| /* Copyright 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. |
| * |
| * Raw keyboard I/O layer for STM32 |
| * |
| * To make this code portable, we rely heavily on looping over the keyboard |
| * input and output entries in the board's gpio_list[]. Each set of inputs or |
| * outputs must be listed in consecutive, increasing order so that scan loops |
| * can iterate beginning at KB_IN00 or KB_OUT00 for however many GPIOs are |
| * utilized (KEYBOARD_ROWS or KEYBOARD_COLS_MAX). |
| */ |
| |
| #include "gpio.h" |
| #include "keyboard_config.h" |
| #include "keyboard_raw.h" |
| #include "keyboard_scan.h" |
| #include "registers.h" |
| #include "task.h" |
| #include "util.h" |
| |
| /* Mask of external interrupts on input lines */ |
| static unsigned int irq_mask; |
| |
| static const uint32_t kb_out_ports[] = { KB_OUT_PORT_LIST }; |
| |
| static void set_irq_mask(void) |
| { |
| int i; |
| |
| for (i = GPIO_KB_IN00; i < GPIO_KB_IN00 + KEYBOARD_ROWS; i++) |
| irq_mask |= gpio_list[i].mask; |
| } |
| |
| void keyboard_raw_init(void) |
| { |
| /* Determine EXTI_PR mask to use for the board */ |
| set_irq_mask(); |
| |
| /* Ensure interrupts are disabled in EXTI_PR */ |
| keyboard_raw_enable_interrupt(0); |
| } |
| |
| void keyboard_raw_task_start(void) |
| { |
| /* Enable interrupts for keyboard matrix inputs */ |
| gpio_enable_interrupt(GPIO_KB_IN00); |
| gpio_enable_interrupt(GPIO_KB_IN01); |
| gpio_enable_interrupt(GPIO_KB_IN02); |
| gpio_enable_interrupt(GPIO_KB_IN03); |
| gpio_enable_interrupt(GPIO_KB_IN04); |
| gpio_enable_interrupt(GPIO_KB_IN05); |
| gpio_enable_interrupt(GPIO_KB_IN06); |
| gpio_enable_interrupt(GPIO_KB_IN07); |
| } |
| |
| test_mockable void keyboard_raw_drive_column(int out) |
| { |
| int i, done = 0; |
| |
| for (i = 0; i < ARRAY_SIZE(kb_out_ports); i++) { |
| uint32_t bsrr = 0; |
| int j; |
| |
| for (j = GPIO_KB_OUT00; j <= GPIO_KB_OUT12; j++) { |
| if (gpio_list[j].port != kb_out_ports[i]) |
| continue; |
| |
| if (out == KEYBOARD_COLUMN_ALL) { |
| /* drive low (clear bit) */ |
| bsrr |= gpio_list[j].mask << 16; |
| } else if (out == KEYBOARD_COLUMN_NONE) { |
| /* put output in hi-Z state (set bit) */ |
| bsrr |= gpio_list[j].mask; |
| } else if (j - GPIO_KB_OUT00 == out) { |
| /* |
| * Drive specified output low, others => hi-Z. |
| * |
| * To avoid conflict, tri-state all outputs |
| * first, then assert specified output. |
| */ |
| keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE); |
| bsrr |= gpio_list[j].mask << 16; |
| done = 1; |
| break; |
| } |
| } |
| |
| #ifdef CONFIG_KEYBOARD_COL2_INVERTED |
| if (bsrr & (gpio_list[GPIO_KB_OUT02].mask << 16 | |
| gpio_list[GPIO_KB_OUT02].mask)) |
| bsrr ^= (gpio_list[GPIO_KB_OUT02].mask << 16 | |
| gpio_list[GPIO_KB_OUT02].mask); |
| #endif |
| |
| if (bsrr) |
| STM32_GPIO_BSRR(kb_out_ports[i]) = bsrr; |
| |
| if (done) |
| break; |
| } |
| } |
| |
| test_mockable int keyboard_raw_read_rows(void) |
| { |
| int i; |
| unsigned int port, prev_port = 0; |
| int state = 0; |
| uint16_t port_val = 0; |
| |
| for (i = 0; i < KEYBOARD_ROWS; i++) { |
| port = gpio_list[GPIO_KB_IN00 + i].port; |
| if (port != prev_port) { |
| port_val = STM32_GPIO_IDR(port); |
| prev_port = port; |
| } |
| |
| if (port_val & gpio_list[GPIO_KB_IN00 + i].mask) |
| state |= 1 << i; |
| } |
| |
| /* Invert it so 0=not pressed, 1=pressed */ |
| return state ^ 0xff; |
| } |
| |
| void keyboard_raw_enable_interrupt(int enable) |
| { |
| if (enable) { |
| /* |
| * Assert all outputs would trigger un-wanted interrupts. |
| * Clear them before enable interrupt. |
| */ |
| STM32_EXTI_PR |= irq_mask; |
| STM32_EXTI_IMR |= irq_mask; /* 1: unmask interrupt */ |
| } else { |
| STM32_EXTI_IMR &= ~irq_mask; /* 0: mask interrupts */ |
| } |
| } |
| |
| void keyboard_raw_gpio_interrupt(enum gpio_signal signal) |
| { |
| task_wake(TASK_ID_KEYSCAN); |
| } |