ITE drivers/kscan: add keyboard scan driver for it8xxx2_evb
Add keyboard scan driver for board it8xxx2_evb. Signed-off-by: Ruibin Chang <ruibin.chang@ite.com.tw>
This commit is contained in:
parent
28da82c0e1
commit
d16ae76d30
12 changed files with 729 additions and 106 deletions
|
@ -25,6 +25,7 @@
|
|||
zephyr,flash = &flash0;
|
||||
zephyr,flash-controller = &flashctrl;
|
||||
zephyr,code-partition = &slot0_partition;
|
||||
zephyr,keyboard-scan = &kscan0;
|
||||
};
|
||||
|
||||
pwmleds {
|
||||
|
@ -91,6 +92,9 @@
|
|||
prescaler-cx = <PWM_PRESCALER_C4>;
|
||||
pwm-output-frequency = <30000>;
|
||||
};
|
||||
&kscan0 {
|
||||
status = "okay";
|
||||
};
|
||||
&flash0 {
|
||||
partitions {
|
||||
compatible = "fixed-partitions";
|
||||
|
|
|
@ -17,6 +17,8 @@ CONFIG_UART_CONSOLE=y
|
|||
CONFIG_UART_NS16550=y
|
||||
CONFIG_WATCHDOG=y
|
||||
CONFIG_WDT_ITE_IT8XXX2=y
|
||||
CONFIG_KSCAN=y
|
||||
CONFIG_KSCAN_ITE_IT8XXX2=y
|
||||
|
||||
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=32768
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
zephyr_library()
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_KSCAN_FT5336 kscan_ft5336.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_KSCAN_ITE_IT8XXX2 kscan_ite_it8xxx2.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_KSCAN_XEC kscan_mchp_xec.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_KSCAN_SDL kscan_sdl.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_KSCAN_HT16K33 kscan_ht16k33.c)
|
||||
|
|
|
@ -11,6 +11,7 @@ menuconfig KSCAN
|
|||
if KSCAN
|
||||
|
||||
source "drivers/kscan/Kconfig.ft5336"
|
||||
source "drivers/kscan/Kconfig.it8xxx2"
|
||||
source "drivers/kscan/Kconfig.xec"
|
||||
source "drivers/kscan/Kconfig.sdl"
|
||||
source "drivers/kscan/Kconfig.ht16k33"
|
||||
|
|
46
drivers/kscan/Kconfig.it8xxx2
Normal file
46
drivers/kscan/Kconfig.it8xxx2
Normal file
|
@ -0,0 +1,46 @@
|
|||
# Copyright (c) 2021 ITE Corporation. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig KSCAN_ITE_IT8XXX2
|
||||
bool "ITE KSCAN driver"
|
||||
depends on SOC_IT8XXX2 && KSCAN
|
||||
select MULTITHREADING
|
||||
help
|
||||
This option enables the ITE keyboard scan driver.
|
||||
|
||||
if KSCAN_ITE_IT8XXX2
|
||||
|
||||
config KSCAN_ITE_IT8XXX2_COLUMN_SIZE
|
||||
int "KSCAN_ITE_IT8XXX2_COLUMN_SIZE"
|
||||
range 16 18
|
||||
default 16
|
||||
help
|
||||
Adjust the value to your keyboard columns. The maximum
|
||||
column size for the ITE family is 18.
|
||||
|
||||
config KSCAN_ITE_IT8XXX2_ROW_SIZE
|
||||
int "KSCAN_ITE_IT8XXX2_ROW_SIZE"
|
||||
default 8
|
||||
help
|
||||
Adjust the value to your keyboard rows. The maximum
|
||||
row size for the ITE family is 8.
|
||||
|
||||
config KSCAN_ITE_IT8XXX2_DEBOUNCE_DOWN
|
||||
int "KSCAN_ITE_IT8XXX2_DEBOUNCE_DOWN"
|
||||
default 9
|
||||
help
|
||||
Determines the time in msecs for debouncing a key press.
|
||||
|
||||
config KSCAN_ITE_IT8XXX2_DEBOUNCE_UP
|
||||
int "KSCAN_ITE_IT8XXX2_DEBOUNCE_UP"
|
||||
default 30
|
||||
help
|
||||
Determines the time in msecs for debouncing a key release.
|
||||
|
||||
config KSCAN_ITE_IT8XXX2_POLL_PERIOD
|
||||
int "KSCAN_ITE_IT8XXX2_POLL_PERIOD"
|
||||
default 3
|
||||
help
|
||||
Defines the poll period in msecs between matrix scans.
|
||||
|
||||
endif # KSCAN_ITE_IT8XXX2
|
572
drivers/kscan/kscan_ite_it8xxx2.c
Normal file
572
drivers/kscan/kscan_ite_it8xxx2.c
Normal file
|
@ -0,0 +1,572 @@
|
|||
/*
|
||||
* Copyright (c) 2021 ITE Corporation. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT ite_it8xxx2_kscan
|
||||
|
||||
#include <device.h>
|
||||
#include <drivers/gpio.h>
|
||||
#include <drivers/kscan.h>
|
||||
#include <drivers/pinmux.h>
|
||||
#include <errno.h>
|
||||
#include <kernel.h>
|
||||
#include <soc.h>
|
||||
#include <soc_dt.h>
|
||||
#include <sys/atomic.h>
|
||||
|
||||
#include <logging/log.h>
|
||||
#define LOG_LEVEL CONFIG_KSCAN_LOG_LEVEL
|
||||
LOG_MODULE_REGISTER(kscan_ite_it8xxx2);
|
||||
|
||||
#define KEYBOARD_COLUMN_DRIVE_ALL -2
|
||||
#define KEYBOARD_COLUMN_DRIVE_NONE -1
|
||||
/* Free run timer counts transform to micro-seconds (clock source is 32768Hz) */
|
||||
#define CLOCK_32K_HW_CYCLES_TO_US(X) \
|
||||
(uint32_t)((((uint64_t)(X) * 1000000U) / \
|
||||
sys_clock_hw_cycles_per_sec()))
|
||||
/* Milli-second transform to micro-second */
|
||||
#define MS_TO_US 1000U
|
||||
/* Number of tracked scan times */
|
||||
#define SCAN_OCURRENCES 30U
|
||||
/* Thread stack size */
|
||||
#define TASK_STACK_SIZE 1024
|
||||
|
||||
/* Device config */
|
||||
enum kscan_pin_func {
|
||||
KSO16 = 0,
|
||||
KSO17,
|
||||
};
|
||||
|
||||
struct kscan_alt_cfg {
|
||||
/* Pinmux control device structure */
|
||||
const struct device *pinctrls;
|
||||
/* GPIO pin */
|
||||
uint8_t pin;
|
||||
/* Alternate function */
|
||||
uint8_t alt_fun;
|
||||
};
|
||||
|
||||
struct kscan_it8xxx2_config {
|
||||
/* Keyboard scan controller base address */
|
||||
uintptr_t base;
|
||||
/* KSI[7:0] wake-up edge mode register */
|
||||
uintptr_t reg_wuemr3;
|
||||
/* KSI[7:0] wake-up edge sense register */
|
||||
uintptr_t reg_wuesr3;
|
||||
/* KSI[7:0] wake-up enable register */
|
||||
uintptr_t reg_wuenr3;
|
||||
/* Keyboard scan input (KSI) wake-up irq */
|
||||
int irq;
|
||||
/* GPIO control device structure */
|
||||
const struct device *gpio_dev;
|
||||
/* Keyboard scan alternate configuration list */
|
||||
const struct kscan_alt_cfg *alt_list;
|
||||
};
|
||||
|
||||
/* Device data */
|
||||
struct kscan_it8xxx2_data {
|
||||
/* Variables in usec units */
|
||||
uint32_t deb_time_press;
|
||||
uint32_t deb_time_rel;
|
||||
int32_t poll_timeout;
|
||||
uint32_t poll_period;
|
||||
uint8_t matrix_stable_state[CONFIG_KSCAN_ITE_IT8XXX2_COLUMN_SIZE];
|
||||
uint8_t matrix_unstable_state[CONFIG_KSCAN_ITE_IT8XXX2_COLUMN_SIZE];
|
||||
uint8_t matrix_previous_state[CONFIG_KSCAN_ITE_IT8XXX2_COLUMN_SIZE];
|
||||
/* Index in to the scan_clock_cycle to indicate start of debouncing */
|
||||
uint8_t scan_cycle_idx[CONFIG_KSCAN_ITE_IT8XXX2_COLUMN_SIZE]
|
||||
[CONFIG_KSCAN_ITE_IT8XXX2_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, TASK_STACK_SIZE);
|
||||
};
|
||||
|
||||
/* Driver convenience defines */
|
||||
#define DRV_CONFIG(dev) ((const struct kscan_it8xxx2_config *)(dev)->config)
|
||||
#define DRV_DATA(dev) ((struct kscan_it8xxx2_data *)(dev)->data)
|
||||
#define DRV_REG(dev) (struct kscan_it8xxx2_regs *)(DRV_CONFIG(dev)->base)
|
||||
|
||||
static void drive_keyboard_column(const struct device *dev, int col)
|
||||
{
|
||||
struct kscan_it8xxx2_regs *const inst = DRV_REG(dev);
|
||||
int mask;
|
||||
|
||||
/* Tri-state all outputs */
|
||||
if (col == KEYBOARD_COLUMN_DRIVE_NONE)
|
||||
mask = 0x3ffff;
|
||||
/* Assert all outputs */
|
||||
else if (col == KEYBOARD_COLUMN_DRIVE_ALL)
|
||||
mask = 0;
|
||||
/* Assert a single output */
|
||||
else
|
||||
mask = 0x3ffff ^ BIT(col);
|
||||
|
||||
/* Set KSO[17:0] output data */
|
||||
inst->KBS_KSOL = (uint8_t) (mask & 0xff);
|
||||
inst->KBS_KSOH1 = (uint8_t) ((mask >> 8) & 0xff);
|
||||
#if (CONFIG_KSCAN_ITE_IT8XXX2_COLUMN_SIZE > 16)
|
||||
inst->KBS_KSOH2 = (uint8_t) ((mask >> 16) & 0xff);
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint8_t read_keyboard_row(const struct device *dev)
|
||||
{
|
||||
struct kscan_it8xxx2_regs *const inst = DRV_REG(dev);
|
||||
|
||||
/* Bits are active-low, so toggle it (return 1 means key pressed) */
|
||||
return (inst->KBS_KSI ^ 0xff);
|
||||
}
|
||||
|
||||
static bool is_matrix_ghosting(const uint8_t *state)
|
||||
{
|
||||
/*
|
||||
* 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_KSCAN_ITE_IT8XXX2_COLUMN_SIZE; c++) {
|
||||
if (!state[c])
|
||||
continue;
|
||||
|
||||
for (int c_n = c + 1; c_n < CONFIG_KSCAN_ITE_IT8XXX2_COLUMN_SIZE; c_n++) {
|
||||
/*
|
||||
* 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 columns 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_n];
|
||||
|
||||
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)
|
||||
{
|
||||
uint8_t row;
|
||||
uint8_t key_event = 0U;
|
||||
|
||||
for (int col = 0; col < CONFIG_KSCAN_ITE_IT8XXX2_COLUMN_SIZE; col++) {
|
||||
/* Drive specific column low and others high */
|
||||
drive_keyboard_column(dev, col);
|
||||
/* Allow the matrix to stabilize before reading it */
|
||||
k_busy_wait(50U);
|
||||
row = read_keyboard_row(dev);
|
||||
new_state[col] = row;
|
||||
|
||||
key_event |= row;
|
||||
}
|
||||
|
||||
drive_keyboard_column(dev, KEYBOARD_COLUMN_DRIVE_NONE);
|
||||
|
||||
return key_event != 0U ? true : false;
|
||||
}
|
||||
|
||||
static void keyboard_raw_interrupt(const struct device *dev)
|
||||
{
|
||||
const struct kscan_it8xxx2_config *const config = DRV_CONFIG(dev);
|
||||
volatile uint8_t *reg_wuesr3 = (uint8_t *)config->reg_wuesr3;
|
||||
struct kscan_it8xxx2_data *data = DRV_DATA(dev);
|
||||
|
||||
/* W/C wakeup interrupt status of KSI[0:7] pins */
|
||||
*reg_wuesr3 = 0xFF;
|
||||
|
||||
/* W/C interrupt status of KSI[7:0] pins */
|
||||
ite_intc_isr_clear(config->irq);
|
||||
|
||||
/* Release poll lock semaphore */
|
||||
k_sem_give(&data->poll_lock);
|
||||
}
|
||||
|
||||
void keyboard_raw_enable_interrupt(const struct device *dev, int enable)
|
||||
{
|
||||
const struct kscan_it8xxx2_config *const config = DRV_CONFIG(dev);
|
||||
volatile uint8_t *reg_wuesr3 = (uint8_t *)config->reg_wuesr3;
|
||||
|
||||
if (enable) {
|
||||
/* W/C wakeup interrupt status of KSI[0:7] pins */
|
||||
*reg_wuesr3 = 0xFF;
|
||||
|
||||
/* W/C interrupt status of KSI[7:0] pins */
|
||||
ite_intc_isr_clear(config->irq);
|
||||
|
||||
irq_enable(config->irq);
|
||||
} else {
|
||||
irq_disable(config->irq);
|
||||
}
|
||||
}
|
||||
|
||||
static bool check_key_events(const struct device *dev)
|
||||
{
|
||||
struct kscan_it8xxx2_data *data = DRV_DATA(dev);
|
||||
uint8_t matrix_new_state[CONFIG_KSCAN_ITE_IT8XXX2_COLUMN_SIZE] = {0U};
|
||||
bool key_pressed = false;
|
||||
uint32_t cycles_now = k_cycle_get_32();
|
||||
uint8_t row_changed = 0U;
|
||||
uint8_t deb_col;
|
||||
|
||||
if (++data->scan_cycles_idx >= SCAN_OCURRENCES)
|
||||
data->scan_cycles_idx = 0U;
|
||||
|
||||
data->scan_clk_cycle[data->scan_cycles_idx] = cycles_now;
|
||||
|
||||
/* Scan the matrix */
|
||||
key_pressed = read_keyboard_matrix(dev, matrix_new_state);
|
||||
|
||||
/* Abort if ghosting is detected */
|
||||
if (is_matrix_ghosting(matrix_new_state)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* The intent of this loop is to gather information related to key
|
||||
* changes
|
||||
*/
|
||||
for (int c = 0; c < CONFIG_KSCAN_ITE_IT8XXX2_COLUMN_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_KSCAN_ITE_IT8XXX2_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[c][r] =
|
||||
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_KSCAN_ITE_IT8XXX2_COLUMN_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_KSCAN_ITE_IT8XXX2_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;
|
||||
|
||||
/* Convert the clock cycle differences to usec */
|
||||
uint32_t debt = CLOCK_32K_HW_CYCLES_TO_US(cycles_now -
|
||||
data->scan_clk_cycle[data->scan_cycle_idx[c][r]]);
|
||||
|
||||
/* Does the key requires more time to be debounced ? */
|
||||
if (debt < (row_bit ? data->deb_time_press :
|
||||
data->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 ((atomic_get(&data->enable_scan) == 1U) &&
|
||||
(data->callback != NULL)) {
|
||||
data->callback(dev, r, c,
|
||||
row_bit ? true : false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return key_pressed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determine if a timer is expired.
|
||||
*
|
||||
* @param start_cycles The starting time of HW cycle.
|
||||
* @param timeout Pointer to the period time.
|
||||
*
|
||||
* @retval true If timer is expired;
|
||||
* false If timer isn't expired.
|
||||
*/
|
||||
static bool poll_expired(uint32_t start_cycles, int32_t *timeout)
|
||||
{
|
||||
uint32_t now_cycles;
|
||||
uint32_t microsecs_spent;
|
||||
|
||||
now_cycles = k_cycle_get_32();
|
||||
microsecs_spent = CLOCK_32K_HW_CYCLES_TO_US(now_cycles - start_cycles);
|
||||
|
||||
/* Update the timeout value */
|
||||
*timeout -= microsecs_spent;
|
||||
|
||||
return !(*timeout >= 0);
|
||||
}
|
||||
|
||||
void polling_task(const struct device *dev, void *dummy2, void *dummy3)
|
||||
{
|
||||
struct kscan_it8xxx2_data *data = DRV_DATA(dev);
|
||||
int32_t local_poll_timeout = data->poll_timeout;
|
||||
uint32_t current_cycles;
|
||||
uint32_t cycles_delta;
|
||||
uint32_t wait_period;
|
||||
|
||||
ARG_UNUSED(dummy2);
|
||||
ARG_UNUSED(dummy3);
|
||||
|
||||
while (true) {
|
||||
/* Init all KSO output low */
|
||||
drive_keyboard_column(dev, KEYBOARD_COLUMN_DRIVE_ALL);
|
||||
/* Enable wakeup and interrupt of KSI pins */
|
||||
keyboard_raw_enable_interrupt(dev, 1);
|
||||
/* Wait poll lock semaphore */
|
||||
k_sem_take(&data->poll_lock, K_FOREVER);
|
||||
/* Disable wakeup and interrupt of KSI pins after fired */
|
||||
keyboard_raw_enable_interrupt(dev, 0);
|
||||
|
||||
uint32_t start_poll_cycles = k_cycle_get_32();
|
||||
|
||||
while (atomic_get(&data->enable_scan) == 1U) {
|
||||
uint32_t start_period_cycles = k_cycle_get_32();
|
||||
|
||||
if (check_key_events(dev)) {
|
||||
start_poll_cycles = k_cycle_get_32();
|
||||
} else if (poll_expired(start_poll_cycles,
|
||||
&local_poll_timeout)) {
|
||||
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_delta = current_cycles - start_period_cycles;
|
||||
wait_period = data->poll_period -
|
||||
CLOCK_32K_HW_CYCLES_TO_US(cycles_delta);
|
||||
|
||||
/* Override wait_period in case it's less than 1000 us */
|
||||
if (wait_period < MS_TO_US)
|
||||
wait_period = MS_TO_US;
|
||||
|
||||
/*
|
||||
* 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 int kscan_it8xxx2_init(const struct device *dev)
|
||||
{
|
||||
const struct kscan_it8xxx2_config *const config = DRV_CONFIG(dev);
|
||||
volatile uint8_t *reg_wuemr3 = (uint8_t *)config->reg_wuemr3;
|
||||
volatile uint8_t *reg_wuesr3 = (uint8_t *)config->reg_wuesr3;
|
||||
volatile uint8_t *reg_wuenr3 = (uint8_t *)config->reg_wuenr3;
|
||||
struct kscan_it8xxx2_data *data = DRV_DATA(dev);
|
||||
struct kscan_it8xxx2_regs *const inst = DRV_REG(dev);
|
||||
|
||||
/* Disable wakeup and interrupt of KSI pins before configuring */
|
||||
keyboard_raw_enable_interrupt(dev, 0);
|
||||
|
||||
/*
|
||||
* Bit[2] = 1: Enable the internal pull-up of KSO[15:0] pins
|
||||
* Bit[0] = 1: Enable the open-drain mode of KSO[17:0] pins
|
||||
*/
|
||||
inst->KBS_KSOCTRL = (IT8XXX2_KBS_KSOOD | IT8XXX2_KBS_KSOPU);
|
||||
|
||||
#if (CONFIG_KSCAN_ITE_IT8XXX2_COLUMN_SIZE > 16)
|
||||
/*
|
||||
* For KSO[16] and KSO[17]:
|
||||
* 1.GPOTRC:
|
||||
* Bit[x] = 1b: Enable the open-drain mode of KSO pin
|
||||
* 2.GPCRCx:
|
||||
* Bit[7:6] = 00b: Select alternate KSO function
|
||||
* Bit[2] = 1b: Enable the internal pull-up of KSO pin
|
||||
*
|
||||
* NOTE: Set input temporarily for gpio_pin_configure(), after that
|
||||
* pinmux_pin_set() set to alternate function immediately.
|
||||
*/
|
||||
gpio_pin_configure(config->gpio_dev,
|
||||
config->alt_list[KSO16].pin,
|
||||
(GPIO_OPEN_DRAIN | GPIO_INPUT | GPIO_PULL_UP));
|
||||
gpio_pin_configure(config->gpio_dev,
|
||||
config->alt_list[KSO17].pin,
|
||||
(GPIO_OPEN_DRAIN | GPIO_INPUT | GPIO_PULL_UP));
|
||||
pinmux_pin_set(config->alt_list[KSO16].pinctrls,
|
||||
config->alt_list[KSO16].pin,
|
||||
config->alt_list[KSO16].alt_fun);
|
||||
pinmux_pin_set(config->alt_list[KSO17].pinctrls,
|
||||
config->alt_list[KSO17].pin,
|
||||
config->alt_list[KSO17].alt_fun);
|
||||
#endif
|
||||
|
||||
/* Bit[2] = 1: Enable the internal pull-up of KSI[7:0] pins */
|
||||
inst->KBS_KSICTRL = IT8XXX2_KBS_KSIPU;
|
||||
|
||||
/* KSO[17:0] pins output low */
|
||||
inst->KBS_KSOL = 0x00;
|
||||
inst->KBS_KSOH1 = 0x00;
|
||||
#if (CONFIG_KSCAN_ITE_IT8XXX2_COLUMN_SIZE > 16)
|
||||
inst->KBS_KSOH2 = 0x00;
|
||||
#endif
|
||||
|
||||
/* Select wakeup interrupt falling-edge triggered of KSI[7:0] pins */
|
||||
*reg_wuemr3 = 0xFF;
|
||||
|
||||
/* W/C wakeup interrupt status of KSI[7:0] pins */
|
||||
*reg_wuesr3 = 0xFF;
|
||||
|
||||
/* W/C interrupt status of KSI[7:0] pins */
|
||||
ite_intc_isr_clear(config->irq);
|
||||
|
||||
/* Enable wakeup interrupt of KSI[7:0] pins */
|
||||
*reg_wuenr3 = 0xFF;
|
||||
|
||||
/* Kconfig.it8xxx2 time figures are transformed from msec to usec */
|
||||
data->deb_time_press =
|
||||
(uint32_t) (CONFIG_KSCAN_ITE_IT8XXX2_DEBOUNCE_DOWN * MS_TO_US);
|
||||
data->deb_time_rel =
|
||||
(uint32_t) (CONFIG_KSCAN_ITE_IT8XXX2_DEBOUNCE_UP * MS_TO_US);
|
||||
data->poll_period =
|
||||
(uint32_t) (CONFIG_KSCAN_ITE_IT8XXX2_POLL_PERIOD * MS_TO_US);
|
||||
data->poll_timeout = 100 * MS_TO_US;
|
||||
|
||||
/* Init null for callback function */
|
||||
data->callback = NULL;
|
||||
|
||||
/* Create poll lock semaphore */
|
||||
k_sem_init(&data->poll_lock, 0, 1);
|
||||
|
||||
/* Enable keyboard scan loop */
|
||||
atomic_set(&data->enable_scan, 1);
|
||||
|
||||
/* Create keyboard scan task */
|
||||
k_thread_create(&data->thread, data->thread_stack,
|
||||
TASK_STACK_SIZE,
|
||||
(void (*)(void *, void *, void *))polling_task,
|
||||
(void *)dev, NULL, NULL,
|
||||
K_PRIO_COOP(4), 0, K_NO_WAIT);
|
||||
|
||||
irq_connect_dynamic(DT_INST_IRQN(0), 0,
|
||||
(void (*)(const void *))keyboard_raw_interrupt,
|
||||
(const void *)dev, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kscan_it8xxx2_configure(const struct device *dev,
|
||||
kscan_callback_t callback)
|
||||
{
|
||||
struct kscan_it8xxx2_data *data = DRV_DATA(dev);
|
||||
|
||||
if (!callback) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Setup callback function */
|
||||
data->callback = callback;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kscan_it8xxx2_disable_callback(const struct device *dev)
|
||||
{
|
||||
struct kscan_it8xxx2_data *data = DRV_DATA(dev);
|
||||
|
||||
/* Disable keyboard scan loop */
|
||||
atomic_set(&data->enable_scan, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kscan_it8xxx2_enable_callback(const struct device *dev)
|
||||
{
|
||||
struct kscan_it8xxx2_data *data = DRV_DATA(dev);
|
||||
|
||||
/* Enable keyboard scan loop */
|
||||
atomic_set(&data->enable_scan, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct kscan_driver_api kscan_it8xxx2_driver_api = {
|
||||
.config = kscan_it8xxx2_configure,
|
||||
.disable_callback = kscan_it8xxx2_disable_callback,
|
||||
.enable_callback = kscan_it8xxx2_enable_callback,
|
||||
};
|
||||
|
||||
static const struct kscan_alt_cfg kscan_alt_0[DT_INST_NUM_PINCTRLS_BY_IDX(0, 0)] =
|
||||
IT8XXX2_DT_ALT_ITEMS_LIST(0);
|
||||
|
||||
static const struct kscan_it8xxx2_config kscan_it8xxx2_cfg_0 = {
|
||||
.base = DT_INST_REG_ADDR_BY_IDX(0, 0),
|
||||
.reg_wuemr3 = DT_INST_REG_ADDR_BY_IDX(0, 1),
|
||||
.reg_wuesr3 = DT_INST_REG_ADDR_BY_IDX(0, 2),
|
||||
.reg_wuenr3 = DT_INST_REG_ADDR_BY_IDX(0, 3),
|
||||
.irq = DT_INST_IRQN(0),
|
||||
.gpio_dev = DEVICE_DT_GET(DT_PHANDLE_BY_IDX(DT_DRV_INST(0), gpio_dev, 0)),
|
||||
.alt_list = kscan_alt_0,
|
||||
};
|
||||
|
||||
static struct kscan_it8xxx2_data kscan_it8xxx2_kbd_data;
|
||||
|
||||
DEVICE_DT_INST_DEFINE(0,
|
||||
&kscan_it8xxx2_init,
|
||||
NULL,
|
||||
&kscan_it8xxx2_kbd_data,
|
||||
&kscan_it8xxx2_cfg_0,
|
||||
POST_KERNEL,
|
||||
CONFIG_KSCAN_INIT_PRIORITY,
|
||||
&kscan_it8xxx2_driver_api);
|
25
dts/bindings/kscan/ite,it8xxx2-kscan.yaml
Normal file
25
dts/bindings/kscan/ite,it8xxx2-kscan.yaml
Normal file
|
@ -0,0 +1,25 @@
|
|||
# Copyright (c) 2021 ITE Corporation. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: ITE it8xxx2 keyboard matrix controller
|
||||
|
||||
compatible: "ite,it8xxx2-kscan"
|
||||
|
||||
include: kscan.yaml
|
||||
|
||||
properties:
|
||||
reg:
|
||||
required: true
|
||||
|
||||
interrupts:
|
||||
required: true
|
||||
|
||||
gpio-dev:
|
||||
type: phandle
|
||||
required: true
|
||||
description: configure GPIO controller
|
||||
|
||||
pinctrl-0:
|
||||
type: phandles
|
||||
required: true
|
||||
description: configure KSO pinmux controller
|
|
@ -113,5 +113,13 @@
|
|||
pinctrl_shi_cs: shi_cs {
|
||||
pinctrls = <&pinmuxm 5 IT8XXX2_PINMUX_FUNC_1>;
|
||||
};
|
||||
|
||||
/* Keyboard alternate function */
|
||||
pinctrl_kso16: kso16 {
|
||||
pinctrls = <&pinmuxc 3 IT8XXX2_PINMUX_FUNC_1>;
|
||||
};
|
||||
pinctrl_kso17: kso17 {
|
||||
pinctrls = <&pinmuxc 5 IT8XXX2_PINMUX_FUNC_1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -805,5 +805,20 @@
|
|||
reg = <0x00f02000 0x100>;
|
||||
label = "GCTRL";
|
||||
};
|
||||
|
||||
kscan0: kscan@f01d00 {
|
||||
compatible = "ite,it8xxx2-kscan";
|
||||
reg = <0x00f01d00 0x29
|
||||
0x00f01b02 0x01 /* WUEMR3 */
|
||||
0x00f01b06 0x01 /* WUESR3 */
|
||||
0x00f01b0a 0x01>; /* WUENR3 */
|
||||
label = "KSCAN";
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = <IT8XXX2_IRQ_WKINTC IRQ_TYPE_LEVEL_HIGH>;
|
||||
status = "disabled";
|
||||
gpio-dev = <&gpioc>;
|
||||
pinctrl-0 = <&pinctrl_kso16 /* GPC3 */
|
||||
&pinctrl_kso17>; /* GPC5 */
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#define IT8XXX2_IRQ_WU23 6
|
||||
/* Group 1 */
|
||||
#define IT8XXX2_IRQ_WU26 12
|
||||
#define IT8XXX2_IRQ_WKINTC 13
|
||||
#define IT8XXX2_IRQ_WU25 14
|
||||
/* Group 2 */
|
||||
#define IT8XXX2_IRQ_WU24 17
|
||||
|
|
|
@ -14,3 +14,11 @@ IT8XXX2_REG_OFFSET_CHECK(gctrl_it8xxx2_regs, GCTRL_SPCTRL4, 0x1c);
|
|||
IT8XXX2_REG_OFFSET_CHECK(gctrl_it8xxx2_regs, GCTRL_RSTC5, 0x21);
|
||||
IT8XXX2_REG_OFFSET_CHECK(gctrl_it8xxx2_regs, GCTRL_MCCR2, 0x44);
|
||||
IT8XXX2_REG_OFFSET_CHECK(gctrl_it8xxx2_regs, GCTRL_ECHIPID2, 0x86);
|
||||
|
||||
/* KSCAN register structure check */
|
||||
IT8XXX2_REG_SIZE_CHECK(kscan_it8xxx2_regs, 0x0F);
|
||||
IT8XXX2_REG_OFFSET_CHECK(kscan_it8xxx2_regs, KBS_KSOL, 0x00);
|
||||
IT8XXX2_REG_OFFSET_CHECK(kscan_it8xxx2_regs, KBS_KSOCTRL, 0x02);
|
||||
IT8XXX2_REG_OFFSET_CHECK(kscan_it8xxx2_regs, KBS_KSI, 0x04);
|
||||
IT8XXX2_REG_OFFSET_CHECK(kscan_it8xxx2_regs, KBS_KSIGDAT, 0x08);
|
||||
IT8XXX2_REG_OFFSET_CHECK(kscan_it8xxx2_regs, KBS_KSOLGOEN, 0x0e);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/*
|
||||
/*
|
||||
* Copyright (c) 2020 ITE Corporation. All Rights Reserved.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
@ -995,114 +995,54 @@ struct pwm_it8xxx2_regs {
|
|||
|
||||
/**
|
||||
*
|
||||
* (1Dxxh) Keyboard Matrix Scan control (KBS)
|
||||
* (1Dxxh) Keyboard Matrix Scan control (KSCAN)
|
||||
*
|
||||
*/
|
||||
#define KSOL ECREG(EC_REG_BASE_ADDR + 0x1D00)
|
||||
#define KSOH1 ECREG(EC_REG_BASE_ADDR + 0x1D01)
|
||||
#define KSOCTRL ECREG(EC_REG_BASE_ADDR + 0x1D02)
|
||||
#define KSOH2 ECREG(EC_REG_BASE_ADDR + 0x1D03)
|
||||
#define KSI ECREG(EC_REG_BASE_ADDR + 0x1D04)
|
||||
#define KSICTRL ECREG(EC_REG_BASE_ADDR + 0x1D05)
|
||||
#define KSIGCTRL ECREG(EC_REG_BASE_ADDR + 0x1D06)
|
||||
#define KSIGOEN ECREG(EC_REG_BASE_ADDR + 0x1D07)
|
||||
#define KSIGDAT ECREG(EC_REG_BASE_ADDR + 0x1D08)
|
||||
#define KSIGDMRR ECREG(EC_REG_BASE_ADDR + 0x1D09)
|
||||
#define KSOHGCTRL ECREG(EC_REG_BASE_ADDR + 0x1D0A)
|
||||
#define KSOHGOEN ECREG(EC_REG_BASE_ADDR + 0x1D0B)
|
||||
#define KSOHGDMRR ECREG(EC_REG_BASE_ADDR + 0x1D0C)
|
||||
#define KSOLGCTRL ECREG(EC_REG_BASE_ADDR + 0x1D0D)
|
||||
#define KSOLGOEN ECREG(EC_REG_BASE_ADDR + 0x1D0E)
|
||||
#define KSOLGDMRR ECREG(EC_REG_BASE_ADDR + 0x1D0F)
|
||||
#define KSO0LSDR ECREG(EC_REG_BASE_ADDR + 0x1D10)
|
||||
#define KSO1LSDR ECREG(EC_REG_BASE_ADDR + 0x1D11)
|
||||
#define KSO2LSDR ECREG(EC_REG_BASE_ADDR + 0x1D12)
|
||||
#define KSO3LSDR ECREG(EC_REG_BASE_ADDR + 0x1D13)
|
||||
#define KSO4LSDR ECREG(EC_REG_BASE_ADDR + 0x1D14)
|
||||
#define KSO5LSDR ECREG(EC_REG_BASE_ADDR + 0x1D15)
|
||||
#define KSO6LSDR ECREG(EC_REG_BASE_ADDR + 0x1D16)
|
||||
#define KSO7LSDR ECREG(EC_REG_BASE_ADDR + 0x1D17)
|
||||
#define KSO8LSDR ECREG(EC_REG_BASE_ADDR + 0x1D18)
|
||||
#define KSO9LSDR ECREG(EC_REG_BASE_ADDR + 0x1D19)
|
||||
#define KSO10LSDR ECREG(EC_REG_BASE_ADDR + 0x1D1A)
|
||||
#define KSO11LSDR ECREG(EC_REG_BASE_ADDR + 0x1D1B)
|
||||
#define KSO12LSDR ECREG(EC_REG_BASE_ADDR + 0x1D1C)
|
||||
#define KSO13LSDR ECREG(EC_REG_BASE_ADDR + 0x1D1D)
|
||||
#define KSO14LSDR ECREG(EC_REG_BASE_ADDR + 0x1D1E)
|
||||
#define KSO15LSDR ECREG(EC_REG_BASE_ADDR + 0x1D1F)
|
||||
#define KSO16LSDR ECREG(EC_REG_BASE_ADDR + 0x1D20)
|
||||
#define KSO17LSDR ECREG(EC_REG_BASE_ADDR + 0x1D21)
|
||||
#define SDC1R ECREG(EC_REG_BASE_ADDR + 0x1D22)
|
||||
#define SDEN BIT(7)
|
||||
#define INTSDVEN BIT(5)
|
||||
#ifndef __ASSEMBLER__
|
||||
struct kscan_it8xxx2_regs {
|
||||
/* 0x000: Keyboard Scan Out */
|
||||
volatile uint8_t KBS_KSOL;
|
||||
/* 0x001: Keyboard Scan Out */
|
||||
volatile uint8_t KBS_KSOH1;
|
||||
/* 0x002: Keyboard Scan Out Control */
|
||||
volatile uint8_t KBS_KSOCTRL;
|
||||
/* 0x003: Keyboard Scan Out */
|
||||
volatile uint8_t KBS_KSOH2;
|
||||
/* 0x004: Keyboard Scan In */
|
||||
volatile uint8_t KBS_KSI;
|
||||
/* 0x005: Keyboard Scan In Control */
|
||||
volatile uint8_t KBS_KSICTRL;
|
||||
/* 0x006: Keyboard Scan In [7:0] GPIO Control */
|
||||
volatile uint8_t KBS_KSIGCTRL;
|
||||
/* 0x007: Keyboard Scan In [7:0] GPIO Output Enable */
|
||||
volatile uint8_t KBS_KSIGOEN;
|
||||
/* 0x008: Keyboard Scan In [7:0] GPIO Data */
|
||||
volatile uint8_t KBS_KSIGDAT;
|
||||
/* 0x009: Keyboard Scan In [7:0] GPIO Data Mirror */
|
||||
volatile uint8_t KBS_KSIGDMRR;
|
||||
/* 0x00A: Keyboard Scan Out [15:8] GPIO Control */
|
||||
volatile uint8_t KBS_KSOHGCTRL;
|
||||
/* 0x00B: Keyboard Scan Out [15:8] GPIO Output Enable */
|
||||
volatile uint8_t KBS_KSOHGOEN;
|
||||
/* 0x00C: Keyboard Scan Out [15:8] GPIO Data Mirror */
|
||||
volatile uint8_t KBS_KSOHGDMRR;
|
||||
/* 0x00D: Keyboard Scan Out [7:0] GPIO Control */
|
||||
volatile uint8_t KBS_KSOLGCTRL;
|
||||
/* 0x00E: Keyboard Scan Out [7:0] GPIO Output Enable */
|
||||
volatile uint8_t KBS_KSOLGOEN;
|
||||
};
|
||||
#endif /* !__ASSEMBLER__ */
|
||||
|
||||
/* BIT2 ~ BIT0 Scan loop select */
|
||||
#define SLS_00_ROUND 0x00
|
||||
#define SLS_02_ROUND 0x01
|
||||
#define SLS_03_ROUND 0x02
|
||||
#define SLS_04_ROUND 0x03
|
||||
#define SLS_05_ROUND 0x04
|
||||
#define SLS_06_ROUND 0x05
|
||||
#define SLS_07_ROUND 0x06
|
||||
#define SLS_08_ROUND 0x07
|
||||
|
||||
#define SDC2R ECREG(EC_REG_BASE_ADDR + 0x1D23)
|
||||
#define KSOPCS1 BIT(7)
|
||||
#define KSOPCS0 BIT(6)
|
||||
|
||||
/* BIT3 ~ BIT0 Wait KSO high delay */
|
||||
#define WKSOHDLY_23US 0x00
|
||||
#define WKSOHDLY_31US 0x01
|
||||
#define WKSOHDLY_39US 0x02
|
||||
#define WKSOHDLY_47US 0x03
|
||||
#define WKSOHDLY_55US 0x04
|
||||
#define WKSOHDLY_63US 0x05
|
||||
#define WKSOHDLY_71US 0x06
|
||||
#define WKSOHDLY_79US 0x07
|
||||
#define WKSOHDLY_87US 0x08
|
||||
#define WKSOHDLY_95US 0x09
|
||||
|
||||
#define SDC3R ECREG(EC_REG_BASE_ADDR + 0x1D24)
|
||||
|
||||
/* BIT7 ~ BIT4 Wait KSO low delay */
|
||||
#define WKSOLDLY_11US (0x00 << 4)
|
||||
#define WKSOLDLY_13US (0x01 << 4)
|
||||
#define WKSOLDLY_15US (0x02 << 4)
|
||||
#define WKSOLDLY_17US (0x03 << 4)
|
||||
#define WKSOLDLY_19US (0x04 << 4)
|
||||
#define WKSOLDLY_21US (0x05 << 4)
|
||||
#define WKSOLDLY_23US (0x06 << 4)
|
||||
#define WKSOLDLY_25US (0x07 << 4)
|
||||
#define WKSOLDLY_27US (0x08 << 4)
|
||||
#define WKSOLDLY_29US (0x09 << 4)
|
||||
|
||||
/* BIT3 ~ BIT0 Spacing delay between rounds */
|
||||
#define SDLYBR_00MS 0x00
|
||||
#define SDLYBR_01MS 0x01
|
||||
#define SDLYBR_02MS 0x02
|
||||
#define SDLYBR_03MS 0x03
|
||||
#define SDLYBR_04MS 0x04
|
||||
#define SDLYBR_05MS 0x05
|
||||
#define SDLYBR_06MS 0x06
|
||||
#define SDLYBR_07MS 0x07
|
||||
#define SDLYBR_08MS 0x08
|
||||
#define SDLYBR_09MS 0x09
|
||||
#define SDLYBR_10MS 0x0A
|
||||
#define SDLYBR_11MS 0x0B
|
||||
#define SDLYBR_12MS 0x0C
|
||||
#define SDLYBR_13MS 0x0D
|
||||
#define SDLYBR_14MS 0x0E
|
||||
#define SDLYBR_15MS 0x0F
|
||||
|
||||
#define SDSR ECREG(EC_REG_BASE_ADDR + 0x1D25)
|
||||
#define SDV BIT(0)
|
||||
|
||||
/* Keyboard Scan Out Control Register */
|
||||
#define KSOPU BIT(2)
|
||||
#define KSOOD BIT(0)
|
||||
|
||||
/* Keyboard Scan In Control Register */
|
||||
#define KSIPU BIT(2)
|
||||
/* KBS register fields */
|
||||
/* 0x002: Keyboard Scan Out Control */
|
||||
#define IT8XXX2_KBS_KSOPU BIT(2)
|
||||
#define IT8XXX2_KBS_KSOOD BIT(0)
|
||||
/* 0x005: Keyboard Scan In Control */
|
||||
#define IT8XXX2_KBS_KSIPU BIT(2)
|
||||
/* 0x00D: Keyboard Scan Out [7:0] GPIO Control */
|
||||
#define IT8XXX2_KBS_KSO2GCTRL BIT(2)
|
||||
/* 0x00E: Keyboard Scan Out [7:0] GPIO Output Enable */
|
||||
#define IT8XXX2_KBS_KSO2GOEN BIT(2)
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue