From d16ae76d301ce7cc96aac30f105d047d093433a7 Mon Sep 17 00:00:00 2001 From: Ruibin Chang Date: Tue, 10 Nov 2020 14:49:52 +0800 Subject: [PATCH] ITE drivers/kscan: add keyboard scan driver for it8xxx2_evb Add keyboard scan driver for board it8xxx2_evb. Signed-off-by: Ruibin Chang --- boards/riscv/it8xxx2_evb/it8xxx2_evb.dts | 4 + .../riscv/it8xxx2_evb/it8xxx2_evb_defconfig | 2 + drivers/kscan/CMakeLists.txt | 1 + drivers/kscan/Kconfig | 1 + drivers/kscan/Kconfig.it8xxx2 | 46 ++ drivers/kscan/kscan_ite_it8xxx2.c | 572 ++++++++++++++++++ dts/bindings/kscan/ite,it8xxx2-kscan.yaml | 25 + dts/riscv/it8xxx2-alts-map.dtsi | 8 + dts/riscv/it8xxx2.dtsi | 15 + .../interrupt-controller/ite-intc.h | 1 + soc/riscv/riscv-ite/common/check_regs.c | 8 + soc/riscv/riscv-ite/common/chip_chipregs.h | 152 ++--- 12 files changed, 729 insertions(+), 106 deletions(-) create mode 100644 drivers/kscan/Kconfig.it8xxx2 create mode 100644 drivers/kscan/kscan_ite_it8xxx2.c create mode 100644 dts/bindings/kscan/ite,it8xxx2-kscan.yaml diff --git a/boards/riscv/it8xxx2_evb/it8xxx2_evb.dts b/boards/riscv/it8xxx2_evb/it8xxx2_evb.dts index 9f615a7790b..fc3e7e31ce1 100644 --- a/boards/riscv/it8xxx2_evb/it8xxx2_evb.dts +++ b/boards/riscv/it8xxx2_evb/it8xxx2_evb.dts @@ -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-output-frequency = <30000>; }; +&kscan0 { + status = "okay"; +}; &flash0 { partitions { compatible = "fixed-partitions"; diff --git a/boards/riscv/it8xxx2_evb/it8xxx2_evb_defconfig b/boards/riscv/it8xxx2_evb/it8xxx2_evb_defconfig index 817c640d502..8519ad9b8a5 100644 --- a/boards/riscv/it8xxx2_evb/it8xxx2_evb_defconfig +++ b/boards/riscv/it8xxx2_evb/it8xxx2_evb_defconfig @@ -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 diff --git a/drivers/kscan/CMakeLists.txt b/drivers/kscan/CMakeLists.txt index 10571e991c5..72db4bb4938 100644 --- a/drivers/kscan/CMakeLists.txt +++ b/drivers/kscan/CMakeLists.txt @@ -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) diff --git a/drivers/kscan/Kconfig b/drivers/kscan/Kconfig index 04592219f1d..1a950d61d9e 100644 --- a/drivers/kscan/Kconfig +++ b/drivers/kscan/Kconfig @@ -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" diff --git a/drivers/kscan/Kconfig.it8xxx2 b/drivers/kscan/Kconfig.it8xxx2 new file mode 100644 index 00000000000..789c4379720 --- /dev/null +++ b/drivers/kscan/Kconfig.it8xxx2 @@ -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 diff --git a/drivers/kscan/kscan_ite_it8xxx2.c b/drivers/kscan/kscan_ite_it8xxx2.c new file mode 100644 index 00000000000..ae1acfd54b8 --- /dev/null +++ b/drivers/kscan/kscan_ite_it8xxx2.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#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); diff --git a/dts/bindings/kscan/ite,it8xxx2-kscan.yaml b/dts/bindings/kscan/ite,it8xxx2-kscan.yaml new file mode 100644 index 00000000000..7994301611c --- /dev/null +++ b/dts/bindings/kscan/ite,it8xxx2-kscan.yaml @@ -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 diff --git a/dts/riscv/it8xxx2-alts-map.dtsi b/dts/riscv/it8xxx2-alts-map.dtsi index 94e1ae17a0e..a96837599f9 100644 --- a/dts/riscv/it8xxx2-alts-map.dtsi +++ b/dts/riscv/it8xxx2-alts-map.dtsi @@ -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>; + }; }; }; diff --git a/dts/riscv/it8xxx2.dtsi b/dts/riscv/it8xxx2.dtsi index 61c35211fbc..aa34ca9d5a1 100644 --- a/dts/riscv/it8xxx2.dtsi +++ b/dts/riscv/it8xxx2.dtsi @@ -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 = ; + status = "disabled"; + gpio-dev = <&gpioc>; + pinctrl-0 = <&pinctrl_kso16 /* GPC3 */ + &pinctrl_kso17>; /* GPC5 */ + }; }; }; diff --git a/include/dt-bindings/interrupt-controller/ite-intc.h b/include/dt-bindings/interrupt-controller/ite-intc.h index 5055dc69431..af2aa47d2ae 100644 --- a/include/dt-bindings/interrupt-controller/ite-intc.h +++ b/include/dt-bindings/interrupt-controller/ite-intc.h @@ -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 diff --git a/soc/riscv/riscv-ite/common/check_regs.c b/soc/riscv/riscv-ite/common/check_regs.c index fc2500f02f7..dc27b4db610 100644 --- a/soc/riscv/riscv-ite/common/check_regs.c +++ b/soc/riscv/riscv-ite/common/check_regs.c @@ -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); diff --git a/soc/riscv/riscv-ite/common/chip_chipregs.h b/soc/riscv/riscv-ite/common/chip_chipregs.h index 4331c799933..f1d599e2540 100644 --- a/soc/riscv/riscv-ite/common/chip_chipregs.h +++ b/soc/riscv/riscv-ite/common/chip_chipregs.h @@ -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) /** *