blob: 1774e7353d1ab8d9c7a6a3f8e2b84e3bb9a8a2d9 [file] [log] [blame]
/* 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;
}