[PATCH v1 3/6] input: add support for CPCAP power button

Svyatoslav Ryhel clamor95 at gmail.com
Sun Mar 30 11:45:09 CEST 2025


CPCAP has a dedicated interrupt for power button. Implement this to have
more input control over the devices.

Signed-off-by: Svyatoslav Ryhel <clamor95 at gmail.com>
---
 drivers/input/Kconfig           |   6 ++
 drivers/input/Makefile          |   1 +
 drivers/input/cpcap_pwrbutton.c | 134 ++++++++++++++++++++++++++++++++
 3 files changed, 141 insertions(+)
 create mode 100644 drivers/input/cpcap_pwrbutton.c

diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index c2b365af11d..c09f0ae795e 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -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
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 8d4107b8848..1303fcdb0b7 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -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
 
diff --git a/drivers/input/cpcap_pwrbutton.c b/drivers/input/cpcap_pwrbutton.c
new file mode 100644
index 00000000000..c8ad39d33ca
--- /dev/null
+++ b/drivers/input/cpcap_pwrbutton.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *  Copyright(C) 2025 Svyatoslav Ryhel <clamor95 at 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),
+};
-- 
2.43.0



More information about the U-Boot mailing list