Add the pm device for rts5912 input driver Signed-off-by: Lin Yu-Cheng <lin_yu_cheng@realtek.com>
306 lines
8.1 KiB
C
306 lines
8.1 KiB
C
/*
|
|
* Copyright (c) 2025 Realtek Corporation.
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT realtek_rts5912_kbd
|
|
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/drivers/pinctrl.h>
|
|
#include <zephyr/drivers/clock_control.h>
|
|
#include <zephyr/drivers/clock_control/clock_control_rts5912.h>
|
|
#include <zephyr/irq.h>
|
|
#include <zephyr/input/input.h>
|
|
#include <zephyr/input/input_kbd_matrix.h>
|
|
#include <zephyr/sys/util_macro.h>
|
|
#include "reg/reg_kbm.h"
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(input_realtek_rts5912_kbd, CONFIG_INPUT_LOG_LEVEL);
|
|
|
|
struct rts5912_kbd_config {
|
|
struct input_kbd_matrix_common_config common;
|
|
volatile struct kbm_regs *base;
|
|
uint32_t irq;
|
|
const struct pinctrl_dev_config *pcfg;
|
|
const struct device *clk_dev;
|
|
struct rts5912_sccon_subsys sccon_cfg;
|
|
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;
|
|
|
|
if (col == INPUT_KBD_MATRIX_COLUMN_DRIVE_NONE) {
|
|
kso_val = kso_mask;
|
|
} else if (col == INPUT_KBD_MATRIX_COLUMN_DRIVE_ALL) {
|
|
kso_val = 0;
|
|
} else {
|
|
kso_val = kso_mask ^ BIT(col);
|
|
}
|
|
|
|
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);
|
|
|
|
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)
|
|
{
|
|
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) {
|
|
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);
|
|
|
|
int ret;
|
|
|
|
rts5912_kbd_set_detect_mode(dev, false);
|
|
|
|
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
|
|
if (ret < 0) {
|
|
LOG_ERR("Failed to configure KSI and KSO pins: %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (!device_is_ready(config->clk_dev)) {
|
|
LOG_ERR("clock kbd device not ready");
|
|
return -ENODEV;
|
|
}
|
|
|
|
ret = clock_control_on(config->clk_dev, (clock_control_subsys_t)&config->sccon_cfg);
|
|
if (ret != 0) {
|
|
LOG_ERR("kbd clock power on fail: %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
inst->scan_out = 0x00;
|
|
|
|
if (ksi_mask & BIT(8)) {
|
|
inst->ctrl |= KBM_CTRL_KSI8EN_Msk;
|
|
}
|
|
|
|
if (ksi_mask & BIT(9)) {
|
|
inst->ctrl |= KBM_CTRL_KSI9EN_Msk;
|
|
}
|
|
|
|
if (kso_mask & BIT(18)) {
|
|
inst->ctrl |= KBM_CTRL_KSO18EN_Msk;
|
|
}
|
|
|
|
if (kso_mask & BIT(19)) {
|
|
inst->ctrl |= KBM_CTRL_KSO19EN_Msk;
|
|
}
|
|
|
|
inst->ctrl |= KBM_CTRL_KSOTYPE_Msk;
|
|
|
|
inst->int_en |= ksi_mask;
|
|
|
|
rts5912_intc_isr_clear(dev);
|
|
|
|
NVIC_ClearPendingIRQ(DT_INST_IRQN(0));
|
|
|
|
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);
|
|
}
|
|
|
|
#if defined(CONFIG_PM_DEVICE)
|
|
static int input_kbd_matrix_pm_action_suspend(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;
|
|
int ret;
|
|
|
|
ret = clock_control_off(config->clk_dev, (clock_control_subsys_t)&config->sccon_cfg);
|
|
if (ret != 0) {
|
|
LOG_ERR("clock_control_off failed: %d", ret);
|
|
return ret;
|
|
}
|
|
inst->int_en = 0;
|
|
|
|
rts5912_intc_isr_clear(dev);
|
|
|
|
if (kso_mask & BIT(18)) {
|
|
inst->ctrl &= ~KBM_CTRL_KSO18EN_Msk;
|
|
}
|
|
|
|
if (kso_mask & BIT(19)) {
|
|
inst->ctrl &= ~KBM_CTRL_KSO19EN_Msk;
|
|
}
|
|
|
|
inst->scan_out = 0x00;
|
|
inst->ctrl &= ~KBM_CTRL_KSOTYPE_Msk;
|
|
|
|
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_SLEEP);
|
|
if (ret < 0) {
|
|
LOG_ERR("pinctrl_apply_state failed: %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int input_kbd_matrix_pm_action_resume(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);
|
|
int ret;
|
|
|
|
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
|
|
if (ret < 0) {
|
|
LOG_ERR("pinctrl_apply_state failed: %d", ret);
|
|
return ret;
|
|
}
|
|
inst->ctrl |= KBM_CTRL_KSOTYPE_Msk;
|
|
|
|
inst->scan_out = 0x00;
|
|
|
|
if (kso_mask & BIT(18)) {
|
|
inst->ctrl |= KBM_CTRL_KSO18EN_Msk;
|
|
}
|
|
|
|
if (kso_mask & BIT(19)) {
|
|
inst->ctrl |= KBM_CTRL_KSO19EN_Msk;
|
|
}
|
|
inst->int_en |= ksi_mask;
|
|
ret = clock_control_on(config->clk_dev, (clock_control_subsys_t)&config->sccon_cfg);
|
|
if (ret != 0) {
|
|
LOG_ERR("clock_control_on failed: %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int input_kbd_matrix_pm_action_rts5912(const struct device *dev,
|
|
enum pm_device_action action)
|
|
{
|
|
int ret;
|
|
|
|
switch (action) {
|
|
case PM_DEVICE_ACTION_RESUME:
|
|
ret = input_kbd_matrix_pm_action_resume(dev);
|
|
if (ret != 0) {
|
|
LOG_ERR("kbd rts5912 resume fail: %d", ret);
|
|
return ret;
|
|
}
|
|
ret = input_kbd_matrix_pm_action(dev, action);
|
|
if (ret != 0) {
|
|
LOG_ERR("kbd pm resume fail: %d", ret);
|
|
return ret;
|
|
}
|
|
break;
|
|
case PM_DEVICE_ACTION_SUSPEND:
|
|
ret = input_kbd_matrix_pm_action_suspend(dev);
|
|
if (ret != 0) {
|
|
LOG_ERR("kbd rts5912 suspend fail: %d", ret);
|
|
return ret;
|
|
}
|
|
ret = input_kbd_matrix_pm_action(dev, action);
|
|
if (ret != 0) {
|
|
LOG_ERR("kbd pm suspend fail: %d", ret);
|
|
return ret;
|
|
}
|
|
break;
|
|
default:
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
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_rts5912);
|
|
|
|
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");
|