Files
OpenCellular/chip/npcx/keyboard_raw.c
Mulin Chao e7969f8245 npcx: keyboard: Add quasi-bidirectional buffers support on npcx7 ec.
This CL added the support for the quasi-bidirectional buffer which has
an open-drain output and a low-impedance pull-up resistance on KSO pins.
The low-impedance pull-up is active when ec changes the output data
buffers from 0 to 1, thereby reducing the low-to-high transition time.
Add CONFIG_KEYBOARD_KSO_HIGH_DRIVE to enable/disable this feature for
npcx7 series ec.

BRANCH=none
BUG=none
TEST=No build errors for all boards using npcx5 series.
     Build poppy board and upload FW to platform. No issues found.

Change-Id: I138f0e433394816e1e5c58b5053580f202c1ac48
Signed-off-by: Mulin Chao <mlchao@nuvoton.com>
Reviewed-on: https://chromium-review.googlesource.com/497189
Reviewed-by: Randall Spangler <rspangler@chromium.org>
2017-05-06 14:20:15 -07:00

220 lines
5.2 KiB
C

/* Copyright (c) 2014 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.
*/
/* Functions needed by keyboard scanner module for Chrome EC */
#include "common.h"
#include "keyboard_raw.h"
#include "keyboard_scan.h"
#include "clock.h"
#include "gpio.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.
*/
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 = KB_COL_MASK;
#ifdef 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 = 0;
#ifdef CONFIG_KEYBOARD_COL2_INVERTED
gpio_set_level(GPIO_KBD_KSO2, 1);
#endif
}
/* Drive one line for detection */
else {
#ifdef CONFIG_KEYBOARD_COL2_INVERTED
if (col == 2)
gpio_set_level(GPIO_KBD_KSO2, 1);
else
gpio_set_level(GPIO_KBD_KSO2, 0);
#endif
mask = ((~(1 << col_out)) & KB_COL_MASK);
}
/* 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;
}
/**
* 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.
*/
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, 4);
#ifdef CONFIG_KEYBOARD_FACTORY_TEST
/* Run keyboard factory testing, scan out KSO/KSI if any shorted. */
int keyboard_factory_test_scan(void)
{
int i, j;
uint16_t shorted = 0;
uint32_t port, id;
/* Disable keyboard scan while testing */
keyboard_scan_enable(0, KB_SCAN_DISABLE_LID_CLOSED);
/* Set all of KSO/KSI pins to internal pull-up and input */
for (i = 0; i < keyboard_factory_scan_pins_used; i++) {
if (keyboard_factory_scan_pins[i][0] < 0)
continue;
port = keyboard_factory_scan_pins[i][0];
id = keyboard_factory_scan_pins[i][1];
gpio_set_alternate_function(port, 1 << id, -1);
gpio_set_flags_by_mask(port, 1 << id,
GPIO_INPUT | GPIO_PULL_UP);
}
/*
* Set start pin to output low, then check other pins
* going to low level, it indicate the two pins are shorted.
*/
for (i = 0; i < keyboard_factory_scan_pins_used; i++) {
if (keyboard_factory_scan_pins[i][0] < 0)
continue;
port = keyboard_factory_scan_pins[i][0];
id = keyboard_factory_scan_pins[i][1];
gpio_set_flags_by_mask(port, 1 << id, GPIO_OUT_LOW);
for (j = 0; j < i; j++) {
if (keyboard_factory_scan_pins[j][0] < 0)
continue;
if ((NPCX_PDIN(keyboard_factory_scan_pins[j][0]) &
(1 << keyboard_factory_scan_pins[j][1])) == 0) {
shorted = i << 8 | j;
goto done;
}
}
gpio_set_flags_by_mask(port, 1 << id,
GPIO_INPUT | GPIO_PULL_UP);
}
done:
gpio_config_module(MODULE_KEYBOARD_SCAN, 1);
keyboard_scan_enable(1, KB_SCAN_DISABLE_LID_CLOSED);
return shorted;
}
#endif