input: add support for CPCAP power button
CPCAP has a dedicated interrupt for power button. Implement this to have more input control over the devices. Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
This commit is contained in:
@@ -55,6 +55,12 @@ config BUTTON_KEYBOARD
|
||||
dt node to define button-event mapping.
|
||||
For example, an arrows and enter may be implemented to navigate boot menu.
|
||||
|
||||
config CPCAP_POWER_BUTTON
|
||||
bool "Enable power button of CPCAP PMIC support"
|
||||
depends on DM_KEYBOARD && DM_PMIC_CPCAP
|
||||
help
|
||||
Enable support for a dedicated power button of the CPCAP PMIC.
|
||||
|
||||
config CROS_EC_KEYB
|
||||
bool "Enable Chrome OS EC keyboard support"
|
||||
depends on INPUT
|
||||
|
@@ -7,6 +7,7 @@ obj-$(CONFIG_$(PHASE_)CROS_EC_KEYB) += cros_ec_keyb.o
|
||||
obj-$(CONFIG_$(PHASE_)OF_CONTROL) += key_matrix.o
|
||||
obj-$(CONFIG_$(PHASE_)DM_KEYBOARD) += input.o keyboard-uclass.o
|
||||
obj-$(CONFIG_BUTTON_KEYBOARD) += button_kbd.o
|
||||
obj-$(CONFIG_CPCAP_POWER_BUTTON) += cpcap_pwrbutton.o
|
||||
|
||||
ifndef CONFIG_XPL_BUILD
|
||||
|
||||
|
134
drivers/input/cpcap_pwrbutton.c
Normal file
134
drivers/input/cpcap_pwrbutton.c
Normal file
@@ -0,0 +1,134 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright(C) 2025 Svyatoslav Ryhel <clamor95@gmail.com>
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <dm.h>
|
||||
#include <input.h>
|
||||
#include <keyboard.h>
|
||||
#include <power/pmic.h>
|
||||
#include <power/cpcap.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
static const unsigned int cpcpap_to_reg[] = {
|
||||
CPCAP_REG_INT1,
|
||||
CPCAP_REG_INT2,
|
||||
CPCAP_REG_INT3,
|
||||
CPCAP_REG_INT4,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cpcap_pwrbutton_priv
|
||||
*
|
||||
* @bank: id of interrupt bank co-responding to an IRQ register
|
||||
* @id: id of interrupt pin co-responding to the bit in IRQ register
|
||||
* @keycode: linux key code
|
||||
* @old_state: holder of last button state
|
||||
* @skip: holder of keycode skip state. This is required since both pressing
|
||||
* and releasing generate same event and cause key send duplication.
|
||||
*/
|
||||
struct cpcap_pwrbutton_priv {
|
||||
u32 bank;
|
||||
u32 id;
|
||||
|
||||
u32 keycode;
|
||||
|
||||
bool old_state;
|
||||
bool skip;
|
||||
};
|
||||
|
||||
static int cpcap_pwrbutton_read_keys(struct input_config *input)
|
||||
{
|
||||
struct udevice *dev = input->dev;
|
||||
struct cpcap_pwrbutton_priv *priv = dev_get_priv(dev);
|
||||
u32 value, state_changed;
|
||||
bool state;
|
||||
|
||||
value = pmic_reg_read(dev->parent, cpcpap_to_reg[priv->bank]) &
|
||||
BIT(priv->id);
|
||||
|
||||
/* Interrupt bit is cleared by writing it to interrupt reg */
|
||||
pmic_reg_write(dev->parent, cpcpap_to_reg[priv->bank], BIT(priv->id));
|
||||
|
||||
state = value >> priv->id;
|
||||
state_changed = state != priv->old_state;
|
||||
|
||||
if (state_changed && !priv->skip) {
|
||||
priv->old_state = state;
|
||||
input_add_keycode(input, priv->keycode, state);
|
||||
}
|
||||
|
||||
if (state)
|
||||
priv->skip = !priv->skip;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpcap_pwrbutton_of_to_plat(struct udevice *dev)
|
||||
{
|
||||
struct cpcap_pwrbutton_priv *priv = dev_get_priv(dev);
|
||||
ofnode irq_parent;
|
||||
u32 irq_desc;
|
||||
int ret;
|
||||
|
||||
/* Check interrupt parent, driver supports only CPCAP as parent */
|
||||
irq_parent = ofnode_parse_phandle(dev_ofnode(dev), "interrupt-parent", 0);
|
||||
if (!ofnode_device_is_compatible(irq_parent, "motorola,cpcap"))
|
||||
return -EINVAL;
|
||||
|
||||
ret = dev_read_u32(dev, "interrupts", &irq_desc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* IRQ registers are 16 bit wide */
|
||||
priv->bank = irq_desc / 16;
|
||||
priv->id = irq_desc % 16;
|
||||
|
||||
ret = dev_read_u32(dev, "linux,code", &priv->keycode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->old_state = false;
|
||||
priv->skip = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpcap_pwrbutton_probe(struct udevice *dev)
|
||||
{
|
||||
struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
struct stdio_dev *sdev = &uc_priv->sdev;
|
||||
struct input_config *input = &uc_priv->input;
|
||||
int ret;
|
||||
|
||||
input_init(input, false);
|
||||
input_add_tables(input, false);
|
||||
|
||||
/* Register the device */
|
||||
input->dev = dev;
|
||||
input->read_keys = cpcap_pwrbutton_read_keys;
|
||||
strcpy(sdev->name, "cpcap-pwrbutton");
|
||||
ret = input_stdio_register(sdev);
|
||||
if (ret) {
|
||||
log_debug("%s: input_stdio_register() failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id cpcap_pwrbutton_ids[] = {
|
||||
{ .compatible = "motorola,cpcap-pwrbutton" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(cpcap_pwrbutton) = {
|
||||
.name = "cpcap_pwrbutton",
|
||||
.id = UCLASS_KEYBOARD,
|
||||
.of_match = cpcap_pwrbutton_ids,
|
||||
.of_to_plat = cpcap_pwrbutton_of_to_plat,
|
||||
.probe = cpcap_pwrbutton_probe,
|
||||
.priv_auto = sizeof(struct cpcap_pwrbutton_priv),
|
||||
};
|
Reference in New Issue
Block a user