diff --git a/boards/realtek/rts5912_evb/rts5912_evb.dts b/boards/realtek/rts5912_evb/rts5912_evb.dts index 1e22d6c4ace..7f40c19b219 100644 --- a/boards/realtek/rts5912_evb/rts5912_evb.dts +++ b/boards/realtek/rts5912_evb/rts5912_evb.dts @@ -31,6 +31,25 @@ pinctrl-names = "default"; }; +&kbd { + status = "okay"; + pinctrl-0 = <&kso0_gpio041 &kso1_gpio042 + &kso2_gpio043 &kso3_gpio044 + &kso4_gpio045 &kso5_gpio046 + &kso6_gpio047 &kso7_gpio048 + &kso8_gpio049 &kso9_gpio050 + &kso10_gpio051 &kso11_gpio055 + &kso12_gpio056 &kso13_gpio057 + &kso14_gpio058 &kso15_gpio059 + &ksi0_gpio064 &ksi1_gpio065 + &ksi2_gpio066 &ksi3_gpio067 + &ksi4_gpio068 &ksi5_gpio069 + &ksi6_gpio070 &ksi7_gpio071>; + pinctrl-names = "default"; + row-size = <8>; + col-size = <16>; +}; + &swj_port { status = "okay"; }; diff --git a/boards/realtek/rts5912_evb/rts5912_evb_defconfig b/boards/realtek/rts5912_evb/rts5912_evb_defconfig index 64454c374d5..d821f0279dd 100644 --- a/boards/realtek/rts5912_evb/rts5912_evb_defconfig +++ b/boards/realtek/rts5912_evb/rts5912_evb_defconfig @@ -17,3 +17,6 @@ CONFIG_UART_CONSOLE=y # Enable GPIO CONFIG_GPIO=y + +# Input Driver +CONFIG_INPUT=y diff --git a/drivers/input/CMakeLists.txt b/drivers/input/CMakeLists.txt index d2d162fcc53..e9eb73b409e 100644 --- a/drivers/input/CMakeLists.txt +++ b/drivers/input/CMakeLists.txt @@ -28,6 +28,7 @@ zephyr_library_sources_ifdef(CONFIG_INPUT_PAT912X input_pat912x.c) zephyr_library_sources_ifdef(CONFIG_INPUT_PAW32XX input_paw32xx.c) zephyr_library_sources_ifdef(CONFIG_INPUT_PINNACLE input_pinnacle.c) zephyr_library_sources_ifdef(CONFIG_INPUT_PMW3610 input_pmw3610.c) +zephyr_library_sources_ifdef(CONFIG_INPUT_REALTEK_RTS5912_KBD input_realtek_rts5912_kbd.c) zephyr_library_sources_ifdef(CONFIG_INPUT_SBUS input_sbus.c) zephyr_library_sources_ifdef(CONFIG_INPUT_STM32_TSC_KEYS input_tsc_keys.c) zephyr_library_sources_ifdef(CONFIG_INPUT_STMPE811 input_stmpe811.c) diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 47cbf4d00a8..9867b3dfea7 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -30,6 +30,7 @@ source "drivers/input/Kconfig.pat912x" source "drivers/input/Kconfig.paw32xx" source "drivers/input/Kconfig.pinnacle" source "drivers/input/Kconfig.pmw3610" +source "drivers/input/Kconfig.rts5912" source "drivers/input/Kconfig.sbus" source "drivers/input/Kconfig.sdl" source "drivers/input/Kconfig.stmpe811" diff --git a/drivers/input/Kconfig.rts5912 b/drivers/input/Kconfig.rts5912 new file mode 100644 index 00000000000..7bd631a2ecd --- /dev/null +++ b/drivers/input/Kconfig.rts5912 @@ -0,0 +1,10 @@ +# Copyright (c) 2025 Realtek Corporation. +# SPDX-License-Identifier: Apache-2.0 + +config INPUT_REALTEK_RTS5912_KBD + bool "Realtek RTS5912 keyboard scanning driver" + default y + depends on DT_HAS_REALTEK_RTS5912_KBD_ENABLED + select INPUT_KBD_MATRIX + help + This option enables the Realtek keyboard scan driver. diff --git a/drivers/input/input_realtek_rts5912_kbd.c b/drivers/input/input_realtek_rts5912_kbd.c new file mode 100644 index 00000000000..ba8314a2932 --- /dev/null +++ b/drivers/input/input_realtek_rts5912_kbd.c @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2025 Realtek Corporation. + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT realtek_rts5912_kbd + +#include +#include +#include +#include +#include +#include +#include +#include +#include "reg/reg_kbm.h" + +#include +LOG_MODULE_REGISTER(input_realtek_rts5912_kbd, CONFIG_INPUT_LOG_LEVEL); + +struct rts5912_kbd_config { + struct input_kbd_matrix_common_config common; + /* Keyboard scan controller base address */ + volatile struct kbm_regs *base; + /* Keyboard scan input (KSI) wake-up irq */ + uint32_t irq; + /* KSI/KSO keyboard scan alternate configuration */ + const struct pinctrl_dev_config *pcfg; + const struct device *clk_dev; + struct rts5912_sccon_subsys sccon_cfg; + /* For user ignore specific pin of kso*/ + uint32_t kso_ignore_mask; +}; + +struct rts5912_kbd_data { + struct input_kbd_matrix_common_data common; +}; + +INPUT_KBD_STRUCT_CHECK(struct rts5912_kbd_config, struct rts5912_kbd_data); + +static void rts5912_kbd_drive_column(const struct device *dev, int col) +{ + const struct rts5912_kbd_config *config = dev->config; + const struct input_kbd_matrix_common_config *common = &config->common; + volatile struct kbm_regs *inst = config->base; + const uint32_t kso_mask = BIT_MASK(common->col_size) & ~config->kso_ignore_mask; + uint32_t kso_val; + uint32_t key; + + /* Tri-state all outputs */ + if (col == INPUT_KBD_MATRIX_COLUMN_DRIVE_NONE) { + kso_val = kso_mask; + /* Assert all outputs */ + } else if (col == INPUT_KBD_MATRIX_COLUMN_DRIVE_ALL) { + kso_val = 0; + /* Assert a single output */ + } else { + kso_val = kso_mask ^ BIT(col); + } + + /* Set KSO output data */ + key = irq_lock(); + inst->scan_out = kso_val; + irq_unlock(key); +} + +static kbd_row_t rts5912_kbd_read_row(const struct device *dev) +{ + const struct rts5912_kbd_config *config = dev->config; + volatile struct kbm_regs *inst = config->base; + const struct input_kbd_matrix_common_config *common = &config->common; + const uint32_t ksi_mask = BIT_MASK(common->row_size); + + /* Bits are active-low, so toggle it (return 1 means key pressed) */ + return (inst->scan_in ^ ksi_mask); +} + +static void rts5912_intc_isr_clear(const struct device *dev) +{ + const struct rts5912_kbd_config *config = dev->config; + volatile struct kbm_regs *inst = config->base; + + inst->ctrl |= KBM_CTRL_KSIINTSTS_Msk; +} + +static void rts5912_kbd_isr(const struct device *dev) +{ + /* W/C interrupt status of KSI pins */ + rts5912_intc_isr_clear(dev); + input_kbd_matrix_poll_start(dev); +} + +static void rts5912_kbd_set_detect_mode(const struct device *dev, bool enable) +{ + const struct rts5912_kbd_config *config = dev->config; + + if (enable) { + /* W/C interrupt status of KSI pins */ + rts5912_intc_isr_clear(dev); + + irq_enable(config->irq); + } else { + irq_disable(config->irq); + } +} + +static int rts5912_kbd_init(const struct device *dev) +{ + const struct rts5912_kbd_config *config = dev->config; + const struct input_kbd_matrix_common_config *common = &config->common; + + volatile struct kbm_regs *inst = config->base; + + const uint32_t kso_mask = BIT_MASK(common->col_size) & ~config->kso_ignore_mask; + const uint32_t ksi_mask = BIT_MASK(common->row_size); + + uint32_t status; + + /* Disable wakeup and interrupt of KSI pins before configuring */ + rts5912_kbd_set_detect_mode(dev, false); + + /* + * Enable the internal pull-up and kbs mode of the KSI pins. + * Enable the internal pull-up and kbs mode of the KSO pins. + * Enable the open-drain mode of the KSO pins. + */ + status = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); + if (status < 0) { + LOG_ERR("Failed to configure KSI and KSO pins"); + return status; + } + + if (!device_is_ready(config->clk_dev)) { + LOG_ERR("clock kbd device not ready"); + return -ENODEV; + } + + status = clock_control_on(config->clk_dev, (clock_control_subsys_t)&config->sccon_cfg); + if (status != 0) { + LOG_ERR("kbd clock power on fail"); + return status; + } + + /* KSO pins output low */ + inst->scan_out = 0x00; + + /* Enable KSI 8 if RAW Size more than 8*/ + if (ksi_mask & BIT(8)) { + inst->ctrl |= KBM_CTRL_KSI8EN_Msk; + } + + /* Enable KSI 9 if RAW Size more than 9*/ + if (ksi_mask & BIT(9)) { + inst->ctrl |= KBM_CTRL_KSI9EN_Msk; + } + + /* Enable KSO 18 if COL Size more than 18*/ + if (kso_mask & BIT(18)) { + inst->ctrl |= KBM_CTRL_KSO18EN_Msk; + } + + /* Enable KSO 19 if COL Size more than 19*/ + if (kso_mask & BIT(19)) { + inst->ctrl |= KBM_CTRL_KSO19EN_Msk; + } + + /* Enable KSO OpenDrain Output Type */ + inst->ctrl |= KBM_CTRL_KSOTYPE_Msk; + + /* Enable Scan Interrupt*/ + inst->int_en |= ksi_mask; + + /* W/C interrupt status of KSI pins */ + rts5912_intc_isr_clear(dev); + + /* Interrupts are enabled in the thread function */ + IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), + rts5912_kbd_isr, DEVICE_DT_INST_GET(0), 0); + + return input_kbd_matrix_common_init(dev); +} + +PINCTRL_DT_INST_DEFINE(0); + +INPUT_KBD_MATRIX_DT_INST_DEFINE(0); + +static const struct input_kbd_matrix_api rts5912_kbd_api = { + .drive_column = rts5912_kbd_drive_column, + .read_row = rts5912_kbd_read_row, + .set_detect_mode = rts5912_kbd_set_detect_mode, +}; + +static const struct rts5912_kbd_config rts5912_kbd_cfg_0 = { + .common = INPUT_KBD_MATRIX_DT_INST_COMMON_CONFIG_INIT(0, &rts5912_kbd_api), + .base = (volatile struct kbm_regs *)DT_INST_REG_ADDR_BY_IDX(0, 0), + .irq = DT_INST_IRQN(0), + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0), + .clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(0)), + .sccon_cfg = { + .clk_grp = DT_CLOCKS_CELL(DT_NODELABEL(kbd), clk_grp), + .clk_idx = DT_CLOCKS_CELL(DT_NODELABEL(kbd), clk_idx), + }, + .kso_ignore_mask = DT_INST_PROP_OR(0, kso_ignore_mask, 0x00), +}; + +static struct rts5912_kbd_data rts5912_kbd_data_0; + +PM_DEVICE_DT_INST_DEFINE(0, input_kbd_matrix_pm_action); + +DEVICE_DT_INST_DEFINE(0, &rts5912_kbd_init, PM_DEVICE_DT_INST_GET(0), &rts5912_kbd_data_0, + &rts5912_kbd_cfg_0, POST_KERNEL, CONFIG_INPUT_INIT_PRIORITY, NULL); + +BUILD_ASSERT(!IS_ENABLED(CONFIG_PM_DEVICE_SYSTEM_MANAGED) || IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME), + "CONFIG_PM_DEVICE_RUNTIME must be enabled when using CONFIG_PM_DEVICE_SYSTEM_MANAGED"); + +BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1, + "only one realtek,rts5912-kbd compatible node can be supported"); +BUILD_ASSERT(IN_RANGE(DT_INST_PROP(0, row_size), 1, 9), "invalid row-size"); +BUILD_ASSERT(IN_RANGE(DT_INST_PROP(0, col_size), 1, 19), "invalid col-size"); diff --git a/dts/arm/realtek/ec/rts5912-pinctrl.dtsi b/dts/arm/realtek/ec/rts5912-pinctrl.dtsi index 3ac81a34862..ce08a65be52 100644 --- a/dts/arm/realtek/ec/rts5912-pinctrl.dtsi +++ b/dts/arm/realtek/ec/rts5912-pinctrl.dtsi @@ -266,105 +266,145 @@ pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ kso1_gpio042: kso1_gpio042 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ kso2_gpio043: kso2_gpio043 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ kso3_gpio044: kso3_gpio044 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ kso4_gpio045: kso4_gpio045 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ kso5_gpio046: kso5_gpio046 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ kso6_gpio047: kso6_gpio047 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ kso7_gpio048: kso7_gpio048 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ kso8_gpio049: kso8_gpio049 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ kso9_gpio050: kso9_gpio050 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ kso10_gpio051: kso10_gpio051 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ kso11_gpio055: kso11_gpio055 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ kso12_gpio056: kso12_gpio056 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ kso13_gpio057: kso13_gpio057 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ kso14_gpio058: kso14_gpio058 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ kso15_gpio059: kso15_gpio059 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ kso16_gpio060: kso16_gpio060 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ kso17_gpio061: kso17_gpio061 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ kso18_gpio092: kso18_gpio092 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ kso19_gpio093: kso19_gpio093 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /* KSO PINCTRL SETTING END */ /* KSI PINCTRL SETTING START */ @@ -372,53 +412,73 @@ pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ ksi1_gpio065: ksi1_gpio065 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ ksi2_gpio066: ksi2_gpio066 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ ksi3_gpio067: ksi3_gpio067 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ ksi4_gpio068: ksi4_gpio068 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ ksi5_gpio069: ksi5_gpio069 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ ksi6_gpio070: ksi6_gpio070 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ ksi7_gpio071: ksi7_gpio071 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ ksi8_gpio054: ksi8_gpio054 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /omit-if-no-ref/ ksi9_gpio098: ksi9_gpio098 { pinmux = ; input-enable; input-schmitt-enable; + bias-pull-up; + drive-open-drain; }; /* KSO PINCTRL SETTING END */ /* KSM PINCTRL SETTING END */ diff --git a/dts/arm/realtek/ec/rts5912.dtsi b/dts/arm/realtek/ec/rts5912.dtsi index afff42e6abd..28419a1f1d0 100644 --- a/dts/arm/realtek/ec/rts5912.dtsi +++ b/dts/arm/realtek/ec/rts5912.dtsi @@ -258,6 +258,15 @@ clk-divider = <33>; status = "disabled"; }; + + kbd: kbd@40010000 { + compatible = "realtek,rts5912-kbd"; + reg = <0x40010000 0x10>; + clocks = <&sccon RTS5912_SCCON_PERIPH_GRP0 PERIPH_GRP0_KBM_CLKPWR>; + interrupts = <210 0>; + interrupt-parent = <&nvic>; + status = "disabled"; + }; }; swj_port: swj-port { diff --git a/dts/bindings/input/realtek,rts5912-kbd.yaml b/dts/bindings/input/realtek,rts5912-kbd.yaml new file mode 100644 index 00000000000..8429ddfa851 --- /dev/null +++ b/dts/bindings/input/realtek,rts5912-kbd.yaml @@ -0,0 +1,31 @@ +# Copyright (c) 2025 Realtek Corporation. +# SPDX-License-Identifier: Apache-2.0 + +description: Realtek RTS5912 keyboard matrix controller + +compatible: "realtek,rts5912-kbd" + +include: [kbd-matrix-common.yaml, pinctrl-device.yaml] + +properties: + reg: + required: true + + interrupts: + required: true + + pinctrl-0: + required: true + + pinctrl-names: + required: true + + row-size: + required: true + + col-size: + required: true + + kso-ignore-mask: + type: int + default: 0 diff --git a/soc/realtek/ec/rts5912/reg/reg_kbm.h b/soc/realtek/ec/rts5912/reg/reg_kbm.h new file mode 100644 index 00000000000..1d27d5ecc4f --- /dev/null +++ b/soc/realtek/ec/rts5912/reg/reg_kbm.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2025 Realtek, SIBG-SD7 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_SOC_REALTEK_RTS5912_REG_KBM_H +#define ZEPHYR_SOC_REALTEK_RTS5912_REG_KBM_H + +/** + * @brief Keyboard Matrix Controller (KBM) + */ + +struct kbm_regs { + uint32_t scan_out; + uint32_t scan_in; + uint32_t int_en; + uint32_t ctrl; +}; + +#define KBM_CTRL_KSOTYPE_Msk BIT(0) +#define KBM_CTRL_KSI8EN_Msk BIT(1) +#define KBM_CTRL_KSI9EN_Msk BIT(2) +#define KBM_CTRL_KSO18EN_Msk BIT(16) +#define KBM_CTRL_KSO19EN_Msk BIT(17) +#define KBM_CTRL_KSIINTSTS_Msk BIT(18) + +#endif /* ZEPHYR_SOC_REALTEK_RTS5912_REG_KBM_H */