/* Copyright 2014 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ /* Functions needed by keyboard scanner module for Chrome EC */ #include "clock.h" #include "common.h" #include "gpio.h" #include "keyboard_raw.h" #include "keyboard_scan.h" #include "registers.h" #include "task.h" /** * Initialize the raw keyboard interface. */ void keyboard_raw_init(void) { /* Enable clock for KBS peripheral */ clock_enable_peripheral(CGC_OFFSET_KBS, CGC_KBS_MASK, CGC_MODE_RUN | CGC_MODE_SLEEP); /* Ensure top-level interrupt is disabled */ keyboard_raw_enable_interrupt(0); /* * Select quasi-bidirectional buffers for KSO pins. It reduces the * low-to-high transition time. This feature only supports in npcx7. */ #ifdef CONFIG_KEYBOARD_KSO_HIGH_DRIVE SET_FIELD(NPCX_KBSCTL, NPCX_KBHDRV_FIELD, 0x01); #endif /* pull-up KBSIN 0-7 internally */ NPCX_KBSINPU = 0xFF; /* Disable automatic scan mode */ CLEAR_BIT(NPCX_KBSCTL, NPCX_KBSMODE); /* Disable automatic interrupt enable */ CLEAR_BIT(NPCX_KBSCTL, NPCX_KBSIEN); /* Disable increment enable */ CLEAR_BIT(NPCX_KBSCTL, NPCX_KBSINC); /* Set KBSOUT to zero to detect key-press */ NPCX_KBSOUT0 = 0x00; NPCX_KBSOUT1 = 0x00; gpio_config_module(MODULE_KEYBOARD_SCAN, 1); /* * Enable interrupts for the inputs. The top-level interrupt is still * masked off, so this won't trigger interrupts yet. */ /* Clear pending input sources used by scanner */ NPCX_WKPCL(MIWU_TABLE_WKKEY, MIWU_GROUP_WKKEY) = 0xFF; /* Enable Wake-up Button */ NPCX_WKEN(MIWU_TABLE_WKKEY, MIWU_GROUP_WKKEY) = 0xFF; /* Select high to low transition (falling edge) */ NPCX_WKEDG(MIWU_TABLE_WKKEY, MIWU_GROUP_WKKEY) = 0xFF; /* Enable interrupt of WK KBS */ keyboard_raw_enable_interrupt(1); } /** * Finish initialization after task scheduling has started. */ #if !defined(CONFIG_KEYBOARD_SCAN_ADC) void keyboard_raw_task_start(void) { /* Enable MIWU to trigger KBS interrupt */ task_enable_irq(NPCX_IRQ_KSI_WKINTC_1); } /** * Drive the specified column low. */ test_mockable void keyboard_raw_drive_column(int col) { /* * Nuvoton Keyboard Scan IP supports 18x8 Matrix * It also support automatic scan functionality */ uint32_t mask, col_out; /* Add support for CONFIG_KEYBOARD_KSO_BASE shifting */ col_out = col + CONFIG_KEYBOARD_KSO_BASE; /* Drive all lines to high */ if (col == KEYBOARD_COLUMN_NONE) { mask = ~0; #if defined(CONFIG_KEYBOARD_CUSTOMIZATION) board_keyboard_drive_col(col); #elif defined(CONFIG_KEYBOARD_COL2_INVERTED) gpio_set_level(GPIO_KBD_KSO2, 0); #endif } /* Set KBSOUT to zero to detect key-press */ else if (col == KEYBOARD_COLUMN_ALL) { mask = ~(BIT(keyboard_cols) - 1); #if defined(CONFIG_KEYBOARD_CUSTOMIZATION) board_keyboard_drive_col(col); #elif defined(CONFIG_KEYBOARD_COL2_INVERTED) gpio_set_level(GPIO_KBD_KSO2, 1); #endif } /* Drive one line for detection */ else { #if defined(CONFIG_KEYBOARD_CUSTOMIZATION) board_keyboard_drive_col(col); #elif defined(CONFIG_KEYBOARD_COL2_INVERTED) if (col == 2) gpio_set_level(GPIO_KBD_KSO2, 1); else gpio_set_level(GPIO_KBD_KSO2, 0); #endif mask = ~BIT(col_out); } /* Set KBSOUT */ NPCX_KBSOUT0 = (mask & 0xFFFF); NPCX_KBSOUT1 = ((mask >> 16) & 0x03); } /** * Read raw row state. * Bits are 1 if signal is present, 0 if not present. */ test_mockable int keyboard_raw_read_rows(void) { /* Bits are active-low, so invert returned levels */ return (~NPCX_KBSIN) & KB_ROW_MASK; } #ifndef NPCX_SELECT_KSI_TO_GPIO /** * Enable or disable keyboard interrupts. */ void keyboard_raw_enable_interrupt(int enable) { if (enable) task_enable_irq(NPCX_IRQ_KSI_WKINTC_1); else task_disable_irq(NPCX_IRQ_KSI_WKINTC_1); } /* * Interrupt handler for the entire GPIO bank of keyboard rows. */ static void keyboard_raw_interrupt(void) { /* Clear pending input sources used by scanner */ NPCX_WKPCL(MIWU_TABLE_WKKEY, MIWU_GROUP_WKKEY) = 0xFF; /* Wake the scan task */ task_wake(TASK_ID_KEYSCAN); } DECLARE_IRQ(NPCX_IRQ_KSI_WKINTC_1, keyboard_raw_interrupt, 5); #endif #else void keyboard_raw_task_start(void) { /* Enable interrupts for keyboard matrix inputs */ keyboard_raw_enable_interrupt(1); } static void set_kb_columns(int level) { gpio_set_level(GPIO_KSO_00, level & BIT(0)); gpio_set_level(GPIO_KSO_01, level & BIT(1)); gpio_set_level(GPIO_KSO_02, level & BIT(2)); gpio_set_level(GPIO_KSO_03, level & BIT(3)); gpio_set_level(GPIO_KSO_04, level & BIT(4)); gpio_set_level(GPIO_KSO_05, level & BIT(5)); gpio_set_level(GPIO_KSO_06, level & BIT(6)); gpio_set_level(GPIO_KSO_07, level & BIT(7)); gpio_set_level(GPIO_KSO_08, level & BIT(8)); gpio_set_level(GPIO_KSO_09, level & BIT(9)); gpio_set_level(GPIO_KSO_10, level & BIT(10)); gpio_set_level(GPIO_KSO_11, level & BIT(11)); gpio_set_level(GPIO_KSO_12, level & BIT(12)); gpio_set_level(GPIO_KSO_13, level & BIT(13)); gpio_set_level(GPIO_KSO_14, level & BIT(14)); } void keyboard_raw_drive_column(int col) { if (col == KEYBOARD_COLUMN_NONE) /* Drive all lines to low */ set_kb_columns(0); else if (col == KEYBOARD_COLUMN_ALL) /* Drive all lines to high to detect any key press */ set_kb_columns(0xFFFF); else set_kb_columns(BIT(col)); } void keyboard_raw_enable_interrupt(int enable) { if (enable) { gpio_enable_interrupt(GPIO_KSI_00); gpio_enable_interrupt(GPIO_KSI_01); gpio_enable_interrupt(GPIO_KSI_02); gpio_enable_interrupt(GPIO_KSI_03); gpio_enable_interrupt(GPIO_KSI_04); gpio_enable_interrupt(GPIO_KSI_05); gpio_enable_interrupt(GPIO_KSI_06); gpio_enable_interrupt(GPIO_KSI_07); gpio_enable_interrupt(GPIO_RFR_KEY_L); } else { gpio_disable_interrupt(GPIO_KSI_00); gpio_disable_interrupt(GPIO_KSI_01); gpio_disable_interrupt(GPIO_KSI_02); gpio_disable_interrupt(GPIO_KSI_03); gpio_disable_interrupt(GPIO_KSI_04); gpio_disable_interrupt(GPIO_KSI_05); gpio_disable_interrupt(GPIO_KSI_06); gpio_disable_interrupt(GPIO_KSI_07); gpio_disable_interrupt(GPIO_RFR_KEY_L); } } void keyboard_raw_gpio_interrupt(enum gpio_signal signal) { /* Wake the scan task */ task_wake(TASK_ID_KEYSCAN); } #endif int keyboard_raw_is_input_low(int port, int id) { return (NPCX_PDIN(port) & BIT(id)) == 0; }