diff mbox series

[v2,7/9] input: pf1550: add onkey support

Message ID 7d80afedf1ad9e98c9739163751bcb2785009e74.1747409892.git.samuel.kayode@savoirfairelinux.com
State New
Headers show
Series add support for pf1550 PMIC MFD-based drivers | expand

Commit Message

Samuel Kayode May 16, 2025, 6:57 p.m. UTC
Add support for the onkey of the pf1550 PMIC.

Signed-off-by: Samuel Kayode <samuel.kayode@savoirfairelinux.com>
---
 drivers/input/keyboard/Kconfig        |   8 ++
 drivers/input/keyboard/Makefile       |   1 +
 drivers/input/keyboard/pf1550_onkey.c | 200 ++++++++++++++++++++++++++
 3 files changed, 209 insertions(+)
 create mode 100644 drivers/input/keyboard/pf1550_onkey.c
diff mbox series

Patch

diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 721ab69e84ac..b01decc03ab9 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -444,6 +444,14 @@  config KEYBOARD_SNVS_PWRKEY
 	  To compile this driver as a module, choose M here; the
 	  module will be called snvs_pwrkey.
 
+config KEYBOARD_PF1550_ONKEY
+	tristate "PF1550 OnKey Driver"
+	depends on MFD_PF1550
+	depends on OF
+	help
+	  This is onkey driver for PF1550 pmic, onkey can trigger release
+	  and 1s(push hold), 2s, 3s, 4s, 8s interrupt for long press detect
+
 config KEYBOARD_IMX
 	tristate "IMX keypad support"
 	depends on ARCH_MXC || COMPILE_TEST
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 1e0721c30709..8a6feae3ddb1 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -59,6 +59,7 @@  obj-$(CONFIG_KEYBOARD_QT2160)		+= qt2160.o
 obj-$(CONFIG_KEYBOARD_SAMSUNG)		+= samsung-keypad.o
 obj-$(CONFIG_KEYBOARD_SH_KEYSC)		+= sh_keysc.o
 obj-$(CONFIG_KEYBOARD_SNVS_PWRKEY)	+= snvs_pwrkey.o
+obj-$(CONFIG_KEYBOARD_PF1550_ONKEY)	+= pf1550_onkey.o
 obj-$(CONFIG_KEYBOARD_SPEAR)		+= spear-keyboard.o
 obj-$(CONFIG_KEYBOARD_STMPE)		+= stmpe-keypad.o
 obj-$(CONFIG_KEYBOARD_STOWAWAY)		+= stowaway.o
diff --git a/drivers/input/keyboard/pf1550_onkey.c b/drivers/input/keyboard/pf1550_onkey.c
new file mode 100644
index 000000000000..b0601acf893d
--- /dev/null
+++ b/drivers/input/keyboard/pf1550_onkey.c
@@ -0,0 +1,200 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the PF1550 ON_KEY
+ * Copyright (C) 2016 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/pf1550.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+struct onkey_drv_data {
+	struct device *dev;
+	struct pf1550_dev *pf1550;
+	int irq;
+	int keycode;
+	int wakeup;
+	struct input_dev *input;
+};
+
+static struct pf1550_irq_info pf1550_onkey_irqs[] = {
+	{ PF1550_ONKEY_IRQ_PUSHI,	"release" },
+	{ PF1550_ONKEY_IRQ_1SI,		"1S" },
+	{ PF1550_ONKEY_IRQ_2SI,		"2S" },
+	{ PF1550_ONKEY_IRQ_3SI,		"3S" },
+	{ PF1550_ONKEY_IRQ_4SI,		"4S" },
+	{ PF1550_ONKEY_IRQ_8SI,		"8S" },
+};
+
+static irqreturn_t pf1550_onkey_irq_handler(int irq, void *data)
+{
+	struct onkey_drv_data *onkey = data;
+	int i, state, irq_type = -1;
+
+	onkey->irq = irq;
+
+	for (i = 0; i < ARRAY_SIZE(pf1550_onkey_irqs); i++)
+		if (onkey->irq == pf1550_onkey_irqs[i].virq)
+			irq_type = pf1550_onkey_irqs[i].irq;
+	switch (irq_type) {
+	case PF1550_ONKEY_IRQ_PUSHI:
+		state = 0;
+		break;
+	case PF1550_ONKEY_IRQ_1SI:
+	case PF1550_ONKEY_IRQ_2SI:
+	case PF1550_ONKEY_IRQ_3SI:
+	case PF1550_ONKEY_IRQ_4SI:
+	case PF1550_ONKEY_IRQ_8SI:
+		state = 1;
+		break;
+	default:
+		dev_err(onkey->dev, "onkey interrupt: irq %d occurred\n",
+			irq_type);
+		return IRQ_HANDLED;
+	}
+
+	input_event(onkey->input, EV_KEY, onkey->keycode, state);
+	input_sync(onkey->input);
+
+	return IRQ_HANDLED;
+}
+
+static int pf1550_onkey_probe(struct platform_device *pdev)
+{
+	struct onkey_drv_data *onkey;
+	struct input_dev *input = NULL;
+	struct device_node *np = pdev->dev.of_node;
+	struct pf1550_dev *pf1550 = dev_get_drvdata(pdev->dev.parent);
+	int i, error;
+
+	if (!np)
+		return -ENODEV;
+
+	onkey = devm_kzalloc(&pdev->dev, sizeof(*onkey), GFP_KERNEL);
+	if (!onkey)
+		return -ENOMEM;
+
+	if (of_property_read_u32(np, "linux,keycodes", &onkey->keycode)) {
+		onkey->keycode = KEY_POWER;
+		dev_warn(&pdev->dev, "KEY_POWER without setting in dts\n");
+	}
+
+	onkey->wakeup = of_property_read_bool(np, "wakeup-source");
+
+	input = devm_input_allocate_device(&pdev->dev);
+	if (!input) {
+		dev_err(&pdev->dev, "failed to allocate the input device\n");
+		return -ENOMEM;
+	}
+
+	input->name = pdev->name;
+	input->phys = "pf1550-onkey/input0";
+	input->id.bustype = BUS_HOST;
+
+	input_set_capability(input, EV_KEY, onkey->keycode);
+
+	for (i = 0; i < ARRAY_SIZE(pf1550_onkey_irqs); i++) {
+		struct pf1550_irq_info *onkey_irq =
+						&pf1550_onkey_irqs[i];
+		unsigned int virq = 0;
+
+		virq = regmap_irq_get_virq(pf1550->irq_data_onkey,
+					   onkey_irq->irq);
+		if (!virq)
+			return -EINVAL;
+
+		onkey_irq->virq = virq;
+
+		error = devm_request_threaded_irq(&pdev->dev, virq, NULL,
+						  pf1550_onkey_irq_handler,
+					IRQF_NO_SUSPEND,
+					onkey_irq->name, onkey);
+		if (error) {
+			dev_err(&pdev->dev,
+				"failed: irq request (IRQ: %d, error :%d)\n",
+				onkey_irq->irq, error);
+			return error;
+		}
+	}
+
+	error = input_register_device(input);
+	if (error < 0) {
+		dev_err(&pdev->dev, "failed to register input device\n");
+		input_free_device(input);
+		return error;
+	}
+
+	onkey->input = input;
+	onkey->pf1550 = pf1550;
+	platform_set_drvdata(pdev, onkey);
+
+	device_init_wakeup(&pdev->dev, onkey->wakeup);
+
+	return 0;
+}
+
+static int pf1550_onkey_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct onkey_drv_data *onkey = platform_get_drvdata(pdev);
+
+	if (!device_may_wakeup(&pdev->dev))
+		regmap_write(onkey->pf1550->regmap,
+			     PF1550_PMIC_REG_ONKEY_INT_MASK0,
+			     ONKEY_IRQ_PUSHI | ONKEY_IRQ_1SI | ONKEY_IRQ_2SI |
+			     ONKEY_IRQ_3SI | ONKEY_IRQ_4SI | ONKEY_IRQ_8SI);
+	else
+		enable_irq_wake(onkey->pf1550->irq);
+
+	return 0;
+}
+
+static int pf1550_onkey_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct onkey_drv_data *onkey = platform_get_drvdata(pdev);
+
+	if (!device_may_wakeup(&pdev->dev))
+		regmap_write(onkey->pf1550->regmap,
+			     PF1550_PMIC_REG_ONKEY_INT_MASK0,
+			     ~(ONKEY_IRQ_PUSHI | ONKEY_IRQ_1SI | ONKEY_IRQ_2SI |
+			     ONKEY_IRQ_3SI | ONKEY_IRQ_4SI | ONKEY_IRQ_8SI));
+	else
+		disable_irq_wake(onkey->pf1550->irq);
+
+	return 0;
+}
+
+static const struct of_device_id pf1550_onkey_ids[] = {
+	{ .compatible = "fsl,pf1550-onkey" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, pf1550_onkey_ids);
+
+static SIMPLE_DEV_PM_OPS(pf1550_onkey_pm_ops, pf1550_onkey_suspend,
+				pf1550_onkey_resume);
+
+static struct platform_driver pf1550_onkey_driver = {
+	.driver = {
+		.name = "pf1550-onkey",
+		.pm     = &pf1550_onkey_pm_ops,
+		.of_match_table = pf1550_onkey_ids,
+	},
+	.probe = pf1550_onkey_probe,
+};
+module_platform_driver(pf1550_onkey_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor");
+MODULE_DESCRIPTION("PF1550 onkey Driver");
+MODULE_LICENSE("GPL v2");