fb60aab245
In order to bring consistency in-tree, migrate all drivers to the new prefix <zephyr/...>. Note that the conversion has been scripted, refer to #45388 for more details. Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
593 lines
16 KiB
C
593 lines
16 KiB
C
/*
|
|
* Copyright (c) 2021 ITE Corporation. All Rights Reserved.
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT ite_it8xxx2_kscan
|
|
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/drivers/gpio.h>
|
|
#include <zephyr/drivers/interrupt_controller/wuc_ite_it8xxx2.h>
|
|
#include <zephyr/drivers/kscan.h>
|
|
#include <zephyr/drivers/pinctrl.h>
|
|
#include <zephyr/dt-bindings/interrupt-controller/it8xxx2-wuc.h>
|
|
#include <errno.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <soc.h>
|
|
#include <soc_dt.h>
|
|
#include <zephyr/sys/atomic.h>
|
|
|
|
#include <zephyr/logging/log.h>
|
|
#define LOG_LEVEL CONFIG_KSCAN_LOG_LEVEL
|
|
LOG_MODULE_REGISTER(kscan_ite_it8xxx2);
|
|
|
|
#define KEYBOARD_KSI_PIN_COUNT IT8XXX2_DT_INST_WUCCTRL_LEN(0)
|
|
#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_wuc_map_cfg {
|
|
/* WUC control device structure */
|
|
const struct device *wucs;
|
|
/* WUC pin mask */
|
|
uint8_t mask;
|
|
};
|
|
|
|
struct kscan_it8xxx2_config {
|
|
/* Keyboard scan controller base address */
|
|
struct kscan_it8xxx2_regs *base;
|
|
/* Keyboard scan input (KSI) wake-up irq */
|
|
int irq;
|
|
/* KSI[7:0] wake-up input source configuration list */
|
|
const struct kscan_wuc_map_cfg *wuc_map_list;
|
|
/* Keyboard scan alternate configuration */
|
|
const struct pinctrl_dev_config *pcfg;
|
|
/* KSO16 GPIO cells */
|
|
struct gpio_dt_spec kso16_gpios;
|
|
/* KSO17 GPIO cells */
|
|
struct gpio_dt_spec kso17_gpios;
|
|
};
|
|
|
|
/* 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;
|
|
/* KSI[7:0] wake-up interrupt status mask */
|
|
uint8_t ksi_pin_mask;
|
|
|
|
K_KERNEL_STACK_MEMBER(thread_stack, TASK_STACK_SIZE);
|
|
};
|
|
|
|
static void drive_keyboard_column(const struct device *dev, int col)
|
|
{
|
|
const struct kscan_it8xxx2_config *const config = dev->config;
|
|
struct kscan_it8xxx2_regs *const inst = config->base;
|
|
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)
|
|
{
|
|
const struct kscan_it8xxx2_config *const config = dev->config;
|
|
struct kscan_it8xxx2_regs *const inst = config->base;
|
|
|
|
/* 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 susceptible 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 = dev->config;
|
|
struct kscan_it8xxx2_data *data = dev->data;
|
|
|
|
/*
|
|
* W/C wakeup interrupt status of KSI[7:0] pins
|
|
*
|
|
* NOTE: We want to clear the status as soon as possible,
|
|
* so clear KSI[7:0] pins at a time.
|
|
*/
|
|
it8xxx2_wuc_clear_status(config->wuc_map_list[0].wucs,
|
|
data->ksi_pin_mask);
|
|
|
|
/* 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 = dev->config;
|
|
struct kscan_it8xxx2_data *data = dev->data;
|
|
|
|
if (enable) {
|
|
/*
|
|
* W/C wakeup interrupt status of KSI[7:0] pins
|
|
*
|
|
* NOTE: We want to clear the status as soon as possible,
|
|
* so clear KSI[7:0] pins at a time.
|
|
*/
|
|
it8xxx2_wuc_clear_status(config->wuc_map_list[0].wucs,
|
|
data->ksi_pin_mask);
|
|
|
|
/* 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 = dev->data;
|
|
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 = dev->data;
|
|
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 = dev->config;
|
|
struct kscan_it8xxx2_data *data = dev->data;
|
|
struct kscan_it8xxx2_regs *const inst = config->base;
|
|
|
|
/* 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)
|
|
int status;
|
|
|
|
/*
|
|
* 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_dt(&config->kso16_gpios, GPIO_INPUT);
|
|
gpio_pin_configure_dt(&config->kso17_gpios, GPIO_INPUT);
|
|
status = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to configure kscan pins");
|
|
return status;
|
|
}
|
|
|
|
#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
|
|
|
|
for (int i = 0; i < KEYBOARD_KSI_PIN_COUNT; i++) {
|
|
/* Select wakeup interrupt falling-edge triggered of KSI[7:0] pins */
|
|
it8xxx2_wuc_set_polarity(config->wuc_map_list[i].wucs,
|
|
config->wuc_map_list[i].mask,
|
|
WUC_TYPE_EDGE_FALLING);
|
|
/* W/C wakeup interrupt status of KSI[7:0] pins */
|
|
it8xxx2_wuc_clear_status(config->wuc_map_list[i].wucs,
|
|
config->wuc_map_list[i].mask);
|
|
/* Enable wakeup interrupt of KSI[7:0] pins */
|
|
it8xxx2_wuc_enable(config->wuc_map_list[i].wucs,
|
|
config->wuc_map_list[i].mask);
|
|
|
|
/*
|
|
* We want to clear KSI[7:0] pins status at a time when wakeup
|
|
* interrupt fire, so gather the KSI[7:0] pin mask value here.
|
|
*/
|
|
if (IS_ENABLED(CONFIG_LOG)) {
|
|
if (config->wuc_map_list[i].wucs != config->wuc_map_list[0].wucs) {
|
|
LOG_ERR("KSI%d pin isn't in the same wuc node!", i);
|
|
}
|
|
}
|
|
data->ksi_pin_mask |= config->wuc_map_list[i].mask;
|
|
}
|
|
|
|
/* W/C interrupt status of KSI[7:0] pins */
|
|
ite_intc_isr_clear(config->irq);
|
|
|
|
/* 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);
|
|
|
|
irq_connect_dynamic(DT_INST_IRQN(0), 0,
|
|
(void (*)(const void *))keyboard_raw_interrupt,
|
|
(const void *)dev, 0);
|
|
|
|
/* 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);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int kscan_it8xxx2_configure(const struct device *dev,
|
|
kscan_callback_t callback)
|
|
{
|
|
struct kscan_it8xxx2_data *data = dev->data;
|
|
|
|
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 = dev->data;
|
|
|
|
/* 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 = dev->data;
|
|
|
|
/* 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_wuc_map_cfg kscan_wuc_0[IT8XXX2_DT_INST_WUCCTRL_LEN(0)] =
|
|
IT8XXX2_DT_WUC_ITEMS_LIST(0);
|
|
|
|
PINCTRL_DT_INST_DEFINE(0);
|
|
|
|
static const struct kscan_it8xxx2_config kscan_it8xxx2_cfg_0 = {
|
|
.base = (struct kscan_it8xxx2_regs *)DT_INST_REG_ADDR_BY_IDX(0, 0),
|
|
.irq = DT_INST_IRQN(0),
|
|
.wuc_map_list = kscan_wuc_0,
|
|
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0),
|
|
.kso16_gpios = GPIO_DT_SPEC_INST_GET(0, kso16_gpios),
|
|
.kso17_gpios = GPIO_DT_SPEC_INST_GET(0, kso17_gpios),
|
|
};
|
|
|
|
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);
|