driver: kscan: npcx: add driver support for kscan
Add keyboard scan driver support. Signed-off-by: Mulin Chao <mlchao@nuvoton.com> Signed-off-by: Jun Lin <CHLin56@nuvoton.com>
This commit is contained in:
parent
14d3688dd4
commit
a55c9f8ce7
6 changed files with 632 additions and 0 deletions
|
|
@ -10,5 +10,6 @@ zephyr_library_sources_ifdef(CONFIG_KSCAN_SDL kscan_sdl.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_KSCAN_HT16K33 kscan_ht16k33.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_KSCAN_CST816S kscan_cst816s.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_KSCAN_CAP1203 kscan_cap1203.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_KSCAN_NPCX kscan_npcx.c)
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_USERSPACE kscan_handlers.c)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ source "drivers/kscan/Kconfig.sdl"
|
|||
source "drivers/kscan/Kconfig.ht16k33"
|
||||
source "drivers/kscan/Kconfig.cst816s"
|
||||
source "drivers/kscan/Kconfig.cap1203"
|
||||
source "drivers/kscan/Kconfig.npcx"
|
||||
|
||||
module = KSCAN
|
||||
module-str = kscan
|
||||
|
|
|
|||
43
drivers/kscan/Kconfig.npcx
Normal file
43
drivers/kscan/Kconfig.npcx
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# NPCX Keyboard Scan driver configuration options
|
||||
|
||||
# Copyright (c) 2022 Nuvoton Technology Corporation.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig KSCAN_NPCX
|
||||
bool "Nuvoton NPCX embedded controller (EC) Keyboard Scan (KSCAN) driver"
|
||||
default y
|
||||
depends on DT_HAS_NUVOTON_NPCX_KSCAN_ENABLED
|
||||
select MULTITHREADING
|
||||
help
|
||||
This option enables the KSCAN driver for NPCX family of
|
||||
processors.
|
||||
|
||||
if KSCAN_NPCX
|
||||
|
||||
config KSCAN_NPCX_POLL_PERIOD_MS
|
||||
int "Keyscan NPCX Poll Period"
|
||||
default 5
|
||||
help
|
||||
Defines the poll period in msecs between between matrix scans.
|
||||
|
||||
config KSCAN_NPCX_KSO_HIGH_DRIVE
|
||||
bool "Select quasi-bidirectional buffers for KSO pins"
|
||||
default y
|
||||
help
|
||||
Select quasi-bidirectional buffers for KSO pins to reduce the
|
||||
low-to-high transition time.
|
||||
|
||||
config KSCAN_NPCX_POLL_COL_OUTPUT_SETTLE_TIME_US
|
||||
int "keyboard matrix poll column output settle time"
|
||||
default 50
|
||||
help
|
||||
Delay (us) between setting column output and waiting for it
|
||||
to settle
|
||||
|
||||
config KSCAN_NPCX_THREAD_STACK_SIZE
|
||||
int "Stack size for the kscan thread"
|
||||
default 1024
|
||||
help
|
||||
Size of the stack used for the kscan thread.
|
||||
|
||||
endif # KSCAN_NPCX
|
||||
539
drivers/kscan/kscan_npcx.c
Normal file
539
drivers/kscan/kscan_npcx.c
Normal file
|
|
@ -0,0 +1,539 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Intel Corporation
|
||||
* Copyright (c) 2022 Nuvoton Technology Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT nuvoton_npcx_kscan
|
||||
|
||||
#include "soc_miwu.h"
|
||||
|
||||
#include <zephyr/drivers/clock_control.h>
|
||||
#include <zephyr/drivers/kscan.h>
|
||||
#include <zephyr/drivers/pinctrl.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include <soc.h>
|
||||
#define LOG_LEVEL CONFIG_KSCAN_LOG_LEVEL
|
||||
LOG_MODULE_REGISTER(kscan_npcx);
|
||||
|
||||
#define KEYBOARD_COLUMN_DRIVE_ALL -2
|
||||
#define KEYBOARD_COLUMN_DRIVE_NONE -1
|
||||
/* Number of tracked scan times */
|
||||
#define SCAN_OCURRENCES 30U
|
||||
|
||||
#define KSCAN_ROW_SIZE DT_INST_PROP(0, row_size)
|
||||
#define KSCAN_COL_SIZE DT_INST_PROP(0, col_size)
|
||||
|
||||
#define HAS_GHOSTING_ENABLED !DT_INST_PROP(0, no_ghostkey_check)
|
||||
|
||||
/* Driver config */
|
||||
struct kscan_npcx_config {
|
||||
/* keyboard scan controller base address */
|
||||
struct kbs_reg *base;
|
||||
/* clock configuration */
|
||||
struct npcx_clk_cfg clk_cfg;
|
||||
/* pinmux configuration */
|
||||
const struct pinctrl_dev_config *pcfg;
|
||||
/* Keyboard scan input (KSI) wake-up irq */
|
||||
int irq;
|
||||
/* Size of keyboard inputs-wui mapping array */
|
||||
int wui_size;
|
||||
uint8_t row_size;
|
||||
uint8_t col_size;
|
||||
uint32_t deb_time_press;
|
||||
uint32_t deb_time_rel;
|
||||
/* Mapping table between keyboard inputs and wui */
|
||||
struct npcx_wui wui_maps[];
|
||||
};
|
||||
|
||||
struct kscan_npcx_data {
|
||||
/* variables in usec units */
|
||||
int64_t poll_timeout;
|
||||
uint32_t poll_period;
|
||||
uint8_t matrix_stable_state[KSCAN_COL_SIZE];
|
||||
uint8_t matrix_unstable_state[KSCAN_COL_SIZE];
|
||||
uint8_t matrix_previous_state[KSCAN_COL_SIZE];
|
||||
uint8_t matrix_new_state[KSCAN_COL_SIZE];
|
||||
/* Index in to the scan_clock_cycle to indicate start of debouncing */
|
||||
uint8_t scan_cycle_idx[KSCAN_COL_SIZE * KSCAN_ROW_SIZE];
|
||||
struct miwu_dev_callback ksi_callback[KSCAN_ROW_SIZE];
|
||||
/* Track previous "elapsed clock cycles" per matrix scan. This
|
||||
* is used to calculate the debouncing time for every key
|
||||
*/
|
||||
uint8_t scan_clk_cycle[SCAN_OCURRENCES];
|
||||
struct k_sem poll_lock;
|
||||
uint8_t scan_cycles_idx;
|
||||
kscan_callback_t callback;
|
||||
struct k_thread thread;
|
||||
atomic_t enable_scan;
|
||||
|
||||
K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_KSCAN_NPCX_THREAD_STACK_SIZE);
|
||||
};
|
||||
|
||||
/* Keyboard Scan local functions */
|
||||
static void kscan_npcx_ksi_isr(const struct device *dev, struct npcx_wui *wui)
|
||||
{
|
||||
ARG_UNUSED(wui);
|
||||
struct kscan_npcx_data *const data = dev->data;
|
||||
|
||||
k_sem_give(&data->poll_lock);
|
||||
}
|
||||
|
||||
static int kscan_npcx_resume_detection(const struct device *dev, bool resume)
|
||||
{
|
||||
const struct kscan_npcx_config *const config = dev->config;
|
||||
|
||||
if (resume) {
|
||||
irq_enable(config->irq);
|
||||
} else {
|
||||
irq_disable(config->irq);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kscan_npcx_drive_column(const struct device *dev, int col)
|
||||
{
|
||||
const struct kscan_npcx_config *config = dev->config;
|
||||
struct kbs_reg *const inst = config->base;
|
||||
uint32_t mask;
|
||||
|
||||
if (col >= config->col_size) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (col == KEYBOARD_COLUMN_DRIVE_NONE) {
|
||||
/* Drive all lines to high. ie. Key detection is disabled. */
|
||||
mask = ~0;
|
||||
} else if (col == KEYBOARD_COLUMN_DRIVE_ALL) {
|
||||
/* Drive all lines to low for detection any key press */
|
||||
mask = ~(BIT(config->col_size) - 1);
|
||||
} else {
|
||||
/*
|
||||
* Drive one line to low for determining which key's state
|
||||
* changed.
|
||||
*/
|
||||
mask = ~BIT(col);
|
||||
}
|
||||
|
||||
LOG_DBG("Drive col mask:%x", mask);
|
||||
|
||||
/* Set KBSOUT */
|
||||
inst->KBSOUT0 = (mask & 0xFFFF);
|
||||
inst->KBSOUT1 = ((mask >> 16) & 0x03);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kscan_npcx_read_row(const struct device *dev)
|
||||
{
|
||||
const struct kscan_npcx_config *config = dev->config;
|
||||
struct kbs_reg *const inst = config->base;
|
||||
int val;
|
||||
|
||||
val = inst->KBSIN;
|
||||
|
||||
/* 1 means key pressed, otherwise means key released. */
|
||||
val = (~val & (BIT(config->row_size) - 1));
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static bool is_matrix_ghosting(const struct device *dev, const uint8_t *state)
|
||||
{
|
||||
const struct kscan_npcx_config *const config = dev->config;
|
||||
|
||||
/*
|
||||
* matrix keyboard designs are suceptible to ghosting.
|
||||
* An extra key appears to be pressed when 3 keys
|
||||
* belonging to the same block are pressed.
|
||||
* for example, in the following block
|
||||
*
|
||||
* . . w . q .
|
||||
* . . . . . .
|
||||
* . . . . . .
|
||||
* . . m . a .
|
||||
*
|
||||
* the key m would look as pressed if the user pressed keys
|
||||
* w, q and a simultaneously. A block can also be formed,
|
||||
* with not adjacent columns.
|
||||
*/
|
||||
for (int c = 0; c < config->col_size; c++) {
|
||||
if (!state[c]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int c_next = c + 1; c_next < config->col_size; c_next++) {
|
||||
/*
|
||||
* We AND the columns to detect a "block".
|
||||
* This is an indication of ghosting, due to current
|
||||
* flowing from a key which was never pressed. In our
|
||||
* case, current flowing is a bit set to 1 as we
|
||||
* flipped the bits when the matrix was scanned.
|
||||
* Now we OR the colums using z&(z-1) which is
|
||||
* non-zero only if z has more than one bit set.
|
||||
*/
|
||||
uint8_t common_row_bits = state[c] & state[c_next];
|
||||
|
||||
if (common_row_bits & (common_row_bits - 1)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool read_keyboard_matrix(const struct device *dev, uint8_t *new_state)
|
||||
{
|
||||
const struct kscan_npcx_config *const config = dev->config;
|
||||
int row;
|
||||
uint8_t key_event = 0U;
|
||||
|
||||
for (int col = 0; col < config->col_size; col++) {
|
||||
kscan_npcx_drive_column(dev, col);
|
||||
|
||||
/* Allow the matrix to stabilize before reading it */
|
||||
k_busy_wait(CONFIG_KSCAN_NPCX_POLL_COL_OUTPUT_SETTLE_TIME_US);
|
||||
|
||||
row = kscan_npcx_read_row(dev);
|
||||
new_state[col] = row & 0xFF;
|
||||
key_event |= row;
|
||||
}
|
||||
|
||||
kscan_npcx_drive_column(dev, KEYBOARD_COLUMN_DRIVE_NONE);
|
||||
|
||||
return key_event != 0U;
|
||||
}
|
||||
|
||||
static void update_matrix_state(const struct device *dev, uint8_t *matrix_new_state)
|
||||
{
|
||||
const struct kscan_npcx_config *const config = dev->config;
|
||||
struct kscan_npcx_data *const data = dev->data;
|
||||
uint32_t cycles_now = k_cycle_get_32();
|
||||
uint8_t row_changed = 0U;
|
||||
uint8_t deb_col;
|
||||
|
||||
data->scan_clk_cycle[data->scan_cycles_idx] = cycles_now;
|
||||
|
||||
/*
|
||||
* The intent of this loop is to gather information related to key
|
||||
* changes.
|
||||
*/
|
||||
for (int c = 0; c < config->col_size; c++) {
|
||||
/* Check if there was an update from the previous scan */
|
||||
row_changed = matrix_new_state[c] ^ data->matrix_previous_state[c];
|
||||
|
||||
if (!row_changed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int r = 0; r < config->row_size; r++) {
|
||||
uint8_t cyc_idx = c * config->row_size + r;
|
||||
|
||||
/*
|
||||
* Index all they keys that changed for each row
|
||||
* in order to debounce each key in terms of it
|
||||
*/
|
||||
if (row_changed & BIT(r)) {
|
||||
data->scan_cycle_idx[cyc_idx] = data->scan_cycles_idx;
|
||||
}
|
||||
}
|
||||
|
||||
data->matrix_unstable_state[c] |= row_changed;
|
||||
data->matrix_previous_state[c] = matrix_new_state[c];
|
||||
}
|
||||
|
||||
for (int c = 0; c < config->col_size; c++) {
|
||||
deb_col = data->matrix_unstable_state[c];
|
||||
|
||||
if (!deb_col) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Debouncing for each row key occurs here */
|
||||
for (int r = 0; r < config->row_size; r++) {
|
||||
uint8_t mask = BIT(r);
|
||||
uint8_t row_bit = matrix_new_state[c] & mask;
|
||||
|
||||
/* Continue if we already debounce a key */
|
||||
if (!(deb_col & mask)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t cyc_idx = c * config->row_size + r;
|
||||
/* Convert the clock cycle differences to usec */
|
||||
uint32_t debt = k_cyc_to_us_floor32(
|
||||
cycles_now - data->scan_clk_cycle[data->scan_cycle_idx[cyc_idx]]);
|
||||
|
||||
/* Does the key requires more time to be debounced? */
|
||||
if (debt < (row_bit ? config->deb_time_press : config->deb_time_rel)) {
|
||||
/* Need more time to debounce */
|
||||
continue;
|
||||
}
|
||||
|
||||
data->matrix_unstable_state[c] &= ~row_bit;
|
||||
|
||||
/* Check if there was a change in the stable state */
|
||||
if ((data->matrix_stable_state[c] & mask) == row_bit) {
|
||||
/* Key state did not change */
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* The current row has been debounced, therefore update
|
||||
* the stable state. Then, proceed to notify the
|
||||
* application about the keys pressed.
|
||||
*/
|
||||
data->matrix_stable_state[c] ^= mask;
|
||||
if (data->callback) {
|
||||
data->callback(dev, r, c, row_bit ? true : false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool check_key_events(const struct device *dev)
|
||||
{
|
||||
const struct kscan_npcx_config *const config = dev->config;
|
||||
struct kscan_npcx_data *const data = dev->data;
|
||||
uint8_t *matrix_new_state = data->matrix_new_state;
|
||||
bool key_pressed = false;
|
||||
|
||||
if (++data->scan_cycles_idx >= SCAN_OCURRENCES) {
|
||||
data->scan_cycles_idx = 0U;
|
||||
}
|
||||
|
||||
/* Scan the matrix */
|
||||
key_pressed = read_keyboard_matrix(dev, matrix_new_state);
|
||||
|
||||
for (int c = 0; c < config->col_size; c++) {
|
||||
LOG_DBG("U%x, P%x, N%x", data->matrix_unstable_state[c],
|
||||
data->matrix_previous_state[c], matrix_new_state[c]);
|
||||
}
|
||||
|
||||
/* Abort if ghosting is detected */
|
||||
if (HAS_GHOSTING_ENABLED && is_matrix_ghosting(dev, matrix_new_state)) {
|
||||
return key_pressed;
|
||||
}
|
||||
|
||||
update_matrix_state(dev, matrix_new_state);
|
||||
|
||||
return key_pressed;
|
||||
}
|
||||
|
||||
static void ksan_matrix_poll(const struct device *dev)
|
||||
{
|
||||
struct kscan_npcx_data *const data = dev->data;
|
||||
uint64_t poll_time_end = sys_clock_timeout_end_calc(K_USEC(data->poll_timeout));
|
||||
uint32_t current_cycles;
|
||||
uint32_t cycles_diff;
|
||||
uint32_t wait_period;
|
||||
|
||||
while (atomic_get(&data->enable_scan) == 1U) {
|
||||
uint32_t start_period_cycles = k_cycle_get_32();
|
||||
|
||||
if (check_key_events(dev)) {
|
||||
poll_time_end = sys_clock_timeout_end_calc(K_USEC(data->poll_timeout));
|
||||
} else if (start_period_cycles > poll_time_end) {
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Subtract the time invested from the sleep period in order
|
||||
* to compensate for the time invested in debouncing a key
|
||||
*/
|
||||
current_cycles = k_cycle_get_32();
|
||||
cycles_diff = current_cycles - start_period_cycles;
|
||||
wait_period = data->poll_period - k_cyc_to_us_floor32(cycles_diff);
|
||||
|
||||
/* Override wait_period in case it is less than 1 ms */
|
||||
if (wait_period < USEC_PER_MSEC) {
|
||||
wait_period = USEC_PER_MSEC;
|
||||
}
|
||||
|
||||
/*
|
||||
* wait period results in a larger number when current cycles
|
||||
* counter wrap. In this case, the whole poll period is used
|
||||
*/
|
||||
if (wait_period > data->poll_period) {
|
||||
LOG_DBG("wait_period : %u", wait_period);
|
||||
|
||||
wait_period = data->poll_period;
|
||||
}
|
||||
|
||||
/* Allow other threads to run while we sleep */
|
||||
k_usleep(wait_period);
|
||||
}
|
||||
}
|
||||
|
||||
static void kscan_matrix_polling_thread(const struct device *dev, void *dummy2, void *dummy3)
|
||||
{
|
||||
struct kscan_npcx_data *const data = dev->data;
|
||||
|
||||
ARG_UNUSED(dummy2);
|
||||
ARG_UNUSED(dummy3);
|
||||
|
||||
while (true) {
|
||||
/* Enable interrupt of KSI pins */
|
||||
kscan_npcx_resume_detection(dev, true);
|
||||
|
||||
kscan_npcx_drive_column(dev, KEYBOARD_COLUMN_DRIVE_ALL);
|
||||
k_sem_take(&data->poll_lock, K_FOREVER);
|
||||
LOG_DBG("Start KB scan!!");
|
||||
|
||||
/* Disable interrupt of KSI pins and start polling */
|
||||
kscan_npcx_resume_detection(dev, false);
|
||||
|
||||
ksan_matrix_poll(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void kscan_npcx_init_ksi_wui_callback(const struct device *dev,
|
||||
struct miwu_dev_callback *callback,
|
||||
const struct npcx_wui *wui,
|
||||
miwu_dev_callback_handler_t handler)
|
||||
{
|
||||
/* KSI signal which has no wake-up input source */
|
||||
if (wui->table == NPCX_MIWU_TABLE_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Install callback function */
|
||||
npcx_miwu_init_dev_callback(callback, wui, handler, dev);
|
||||
npcx_miwu_manage_dev_callback(callback, 1);
|
||||
|
||||
/* Configure MIWU setting and enable its interrupt */
|
||||
npcx_miwu_interrupt_configure(wui, NPCX_MIWU_MODE_EDGE, NPCX_MIWU_TRIG_BOTH);
|
||||
npcx_miwu_irq_enable(wui);
|
||||
}
|
||||
|
||||
static int kscan_npcx_init(const struct device *dev)
|
||||
{
|
||||
const struct device *clk_dev = DEVICE_DT_GET(NPCX_CLK_CTRL_NODE);
|
||||
const struct kscan_npcx_config *const config = dev->config;
|
||||
struct kscan_npcx_data *const data = dev->data;
|
||||
struct kbs_reg *const inst = config->base;
|
||||
int ret;
|
||||
|
||||
if (!device_is_ready(clk_dev)) {
|
||||
LOG_ERR("%s device not ready", clk_dev->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Turn on KBSCAN controller device clock */
|
||||
ret = clock_control_on(clk_dev, (clock_control_subsys_t *)&config->clk_cfg);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Turn on KBSCAN clock fail %d", ret);
|
||||
}
|
||||
|
||||
/* Pull-up KBSIN0-7 internally */
|
||||
inst->KBSINPU = 0xFF;
|
||||
|
||||
/*
|
||||
* Keyboard Scan Control Register
|
||||
*
|
||||
* [6:7] - KBHDRV KBSOUTn signals output buffers are open-drain.
|
||||
* [3] - KBSINC Auto-increment of Buffer Data register is disabled
|
||||
* [2] - KBSIEN Interrupt of Auto-Scan is disabled
|
||||
* [1] - KBSMODE Key detection mechanism is implemented by firmware
|
||||
* [0] - START Write 0 to this field is not affected
|
||||
*/
|
||||
inst->KBSCTL = 0x00;
|
||||
|
||||
/*
|
||||
* Select quasi-bidirectional buffers for KSO pins. It reduces the
|
||||
* low-to-high transition time. This feature only supports in npcx7.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_KSCAN_NPCX_KSO_HIGH_DRIVE)) {
|
||||
SET_FIELD(inst->KBSCTL, NPCX_KBSCTL_KBHDRV_FIELD, 0x01);
|
||||
}
|
||||
|
||||
/* Drive all column lines to low for detection any key press */
|
||||
kscan_npcx_drive_column(dev, KEYBOARD_COLUMN_DRIVE_NONE);
|
||||
|
||||
/* Configure wake-up input and callback for keyboard input signal */
|
||||
for (int i = 0; i < config->row_size; i++)
|
||||
kscan_npcx_init_ksi_wui_callback(dev, &data->ksi_callback[i], &config->wui_maps[i],
|
||||
kscan_npcx_ksi_isr);
|
||||
|
||||
/* Configure pin-mux for kscan device */
|
||||
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("kscan pinctrl setup failed (%d)", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Initialize semaphore usbed by kscan task and driver */
|
||||
k_sem_init(&data->poll_lock, 0, 1);
|
||||
|
||||
/* Time figures are transformed from msec to usec */
|
||||
data->poll_period = (uint32_t)(CONFIG_KSCAN_NPCX_POLL_PERIOD_MS * USEC_PER_MSEC);
|
||||
data->poll_timeout = 100 * USEC_PER_MSEC;
|
||||
|
||||
k_thread_create(&data->thread, data->thread_stack, CONFIG_KSCAN_NPCX_THREAD_STACK_SIZE,
|
||||
(void (*)(void *, void *, void *))kscan_matrix_polling_thread, (void *)dev,
|
||||
NULL, NULL, K_PRIO_COOP(4), 0, K_NO_WAIT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kscan_npcx_configure(const struct device *dev, kscan_callback_t callback)
|
||||
{
|
||||
struct kscan_npcx_data *const data = dev->data;
|
||||
|
||||
if (!callback) {
|
||||
return -EINVAL;
|
||||
}
|
||||
data->callback = callback;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kscan_npcx_enable_interface(const struct device *dev)
|
||||
{
|
||||
struct kscan_npcx_data *const data = dev->data;
|
||||
|
||||
atomic_set(&data->enable_scan, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kscan_npcx_disable_interface(const struct device *dev)
|
||||
{
|
||||
struct kscan_npcx_data *const data = dev->data;
|
||||
|
||||
atomic_set(&data->enable_scan, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static const struct kscan_driver_api kscan_npcx_driver_api = {
|
||||
.config = kscan_npcx_configure,
|
||||
.enable_callback = kscan_npcx_enable_interface,
|
||||
.disable_callback = kscan_npcx_disable_interface,
|
||||
};
|
||||
|
||||
PINCTRL_DT_INST_DEFINE(0);
|
||||
static const struct kscan_npcx_config kscan_cfg_0 = {
|
||||
.base = (struct kbs_reg *)DT_INST_REG_ADDR(0),
|
||||
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0),
|
||||
.clk_cfg = NPCX_DT_CLK_CFG_ITEM(0),
|
||||
.irq = DT_INST_IRQN(0),
|
||||
.wui_size = NPCX_DT_WUI_ITEMS_LEN(0),
|
||||
.wui_maps = NPCX_DT_WUI_ITEMS_LIST(0),
|
||||
.row_size = KSCAN_ROW_SIZE,
|
||||
.col_size = KSCAN_COL_SIZE,
|
||||
.deb_time_press = DT_INST_PROP(0, debounce_down_ms),
|
||||
.deb_time_rel = DT_INST_PROP(0, debounce_up_ms),
|
||||
};
|
||||
|
||||
static struct kscan_npcx_data kscan_data_0;
|
||||
|
||||
DEVICE_DT_INST_DEFINE(0, kscan_npcx_init, NULL, &kscan_data_0, &kscan_cfg_0, POST_KERNEL,
|
||||
CONFIG_KSCAN_INIT_PRIORITY, &kscan_npcx_driver_api);
|
||||
|
||||
BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1,
|
||||
"only one 'nuvoton_npcx_kscan' compatible node can be supported");
|
||||
|
|
@ -1590,4 +1590,46 @@ struct peci_reg {
|
|||
#define PECI_HIGH_SPEED_MIN_VAL 0x07
|
||||
|
||||
#define NPCX_PECI_RATE_EHSP 6
|
||||
|
||||
/* KBS (Keyboard Scan) device registers */
|
||||
struct kbs_reg {
|
||||
volatile uint8_t reserved1[4];
|
||||
/* 0x004: Keyboard Scan In */
|
||||
volatile uint8_t KBSIN;
|
||||
/* 0x005: Keyboard Scan In Pull-Up Enable */
|
||||
volatile uint8_t KBSINPU;
|
||||
/* 0x006: Keyboard Scan Out 0 */
|
||||
volatile uint16_t KBSOUT0;
|
||||
/* 0x008: Keyboard Scan Out 1 */
|
||||
volatile uint16_t KBSOUT1;
|
||||
/* 0x00A: Keyboard Scan Buffer Index */
|
||||
volatile uint8_t KBS_BUF_INDX;
|
||||
/* 0x00B: Keyboard Scan Buffer Data */
|
||||
volatile uint8_t KBS_BUF_DATA;
|
||||
/* 0x00C: Keyboard Scan Event */
|
||||
volatile uint8_t KBSEVT;
|
||||
/* 0x00D: Keyboard Scan Control */
|
||||
volatile uint8_t KBSCTL;
|
||||
/* 0x00E: Keyboard Scan Configuration Index */
|
||||
volatile uint8_t KBS_CFG_INDX;
|
||||
/* 0x00F: Keyboard Scan Configuration Data */
|
||||
volatile uint8_t KBS_CFG_DATA;
|
||||
};
|
||||
|
||||
/* KBS register fields */
|
||||
#define NPCX_KBSBUFINDX 0
|
||||
#define NPCX_KBSEVT_KBSDONE 0
|
||||
#define NPCX_KBSEVT_KBSERR 1
|
||||
#define NPCX_KBSCTL_START 0
|
||||
#define NPCX_KBSCTL_KBSMODE 1
|
||||
#define NPCX_KBSCTL_KBSIEN 2
|
||||
#define NPCX_KBSCTL_KBSINC 3
|
||||
#define NPCX_KBSCTL_KBHDRV_FIELD FIELD(6, 2)
|
||||
#define NPCX_KBSCFGINDX 0
|
||||
/* Index of 'Automatic Scan' configuration register */
|
||||
#define KBS_CFG_INDX_DLY1 0 /* Keyboard Scan Delay T1 Byte */
|
||||
#define KBS_CFG_INDX_DLY2 1 /* Keyboard Scan Delay T2 Byte */
|
||||
#define KBS_CFG_INDX_RTYTO 2 /* Keyboard Scan Retry Timeout */
|
||||
#define KBS_CFG_INDX_CNUM 3 /* Keyboard Scan Columns Number */
|
||||
#define KBS_CFG_INDX_CDIV 4 /* Keyboard Scan Clock Divisor */
|
||||
#endif /* _NUVOTON_NPCX_REG_DEF_H */
|
||||
|
|
|
|||
|
|
@ -178,3 +178,9 @@ NPCX_REG_SIZE_CHECK(peci_reg, 0x050);
|
|||
NPCX_REG_OFFSET_CHECK(peci_reg, PECI_ADDR, 0x002);
|
||||
NPCX_REG_OFFSET_CHECK(peci_reg, PECI_WR_LENGTH, 0x007);
|
||||
NPCX_REG_OFFSET_CHECK(peci_reg, PECI_WR_FCS, 0x00b);
|
||||
|
||||
/* KBS register structure check */
|
||||
NPCX_REG_SIZE_CHECK(kbs_reg, 0x010);
|
||||
NPCX_REG_OFFSET_CHECK(kbs_reg, KBSIN, 0x004);
|
||||
NPCX_REG_OFFSET_CHECK(kbs_reg, KBSOUT0, 0x006);
|
||||
NPCX_REG_OFFSET_CHECK(kbs_reg, KBS_BUF_INDX, 0x00a);
|
||||
|
|
|
|||
Loading…
Reference in a new issue