1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  *  Copyright(C) 2025 Svyatoslav Ryhel <clamor95@gmail.com>
4  */
5 
6 #include <stdlib.h>
7 #include <dm.h>
8 #include <input.h>
9 #include <keyboard.h>
10 #include <power/pmic.h>
11 #include <power/cpcap.h>
12 #include <linux/delay.h>
13 #include <linux/err.h>
14 #include <linux/input.h>
15 
16 static const unsigned int cpcpap_to_reg[] = {
17 	CPCAP_REG_INT1,
18 	CPCAP_REG_INT2,
19 	CPCAP_REG_INT3,
20 	CPCAP_REG_INT4,
21 };
22 
23 /**
24  * struct cpcap_pwrbutton_priv
25  *
26  * @bank: id of interrupt bank co-responding to an IRQ register
27  * @id: id of interrupt pin co-responding to the bit in IRQ register
28  * @keycode: linux key code
29  * @old_state: holder of last button state
30  * @skip: holder of keycode skip state. This is required since both pressing
31  *        and releasing generate same event and cause key send duplication.
32  */
33 struct cpcap_pwrbutton_priv {
34 	u32 bank;
35 	u32 id;
36 
37 	u32 keycode;
38 
39 	bool old_state;
40 	bool skip;
41 };
42 
cpcap_pwrbutton_read_keys(struct input_config * input)43 static int cpcap_pwrbutton_read_keys(struct input_config *input)
44 {
45 	struct udevice *dev = input->dev;
46 	struct cpcap_pwrbutton_priv *priv = dev_get_priv(dev);
47 	u32 value, state_changed;
48 	bool state;
49 
50 	value = pmic_reg_read(dev->parent, cpcpap_to_reg[priv->bank]) &
51 			      BIT(priv->id);
52 
53 	/* Interrupt bit is cleared by writing it to interrupt reg */
54 	pmic_reg_write(dev->parent, cpcpap_to_reg[priv->bank], BIT(priv->id));
55 
56 	state = value >> priv->id;
57 	state_changed = state != priv->old_state;
58 
59 	if (state_changed && !priv->skip) {
60 		priv->old_state = state;
61 		input_add_keycode(input, priv->keycode, state);
62 	}
63 
64 	if (state)
65 		priv->skip = !priv->skip;
66 
67 	return 0;
68 }
69 
cpcap_pwrbutton_of_to_plat(struct udevice * dev)70 static int cpcap_pwrbutton_of_to_plat(struct udevice *dev)
71 {
72 	struct cpcap_pwrbutton_priv *priv = dev_get_priv(dev);
73 	ofnode irq_parent;
74 	u32 irq_desc;
75 	int ret;
76 
77 	/* Check interrupt parent, driver supports only CPCAP as parent */
78 	irq_parent = ofnode_parse_phandle(dev_ofnode(dev), "interrupt-parent", 0);
79 	if (!ofnode_device_is_compatible(irq_parent, "motorola,cpcap"))
80 		return -EINVAL;
81 
82 	ret = dev_read_u32(dev, "interrupts", &irq_desc);
83 	if (ret)
84 		return ret;
85 
86 	/* IRQ registers are 16 bit wide */
87 	priv->bank = irq_desc / 16;
88 	priv->id = irq_desc % 16;
89 
90 	ret = dev_read_u32(dev, "linux,code", &priv->keycode);
91 	if (ret)
92 		return ret;
93 
94 	priv->old_state = false;
95 	priv->skip = false;
96 	return 0;
97 }
98 
cpcap_pwrbutton_probe(struct udevice * dev)99 static int cpcap_pwrbutton_probe(struct udevice *dev)
100 {
101 	struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev);
102 	struct stdio_dev *sdev = &uc_priv->sdev;
103 	struct input_config *input = &uc_priv->input;
104 	int ret;
105 
106 	input_init(input, false);
107 	input_add_tables(input, false);
108 
109 	/* Register the device */
110 	input->dev = dev;
111 	input->read_keys = cpcap_pwrbutton_read_keys;
112 	strcpy(sdev->name, "cpcap-pwrbutton");
113 	ret = input_stdio_register(sdev);
114 	if (ret) {
115 		log_debug("%s: input_stdio_register() failed\n", __func__);
116 		return ret;
117 	}
118 
119 	return 0;
120 }
121 
122 static const struct udevice_id cpcap_pwrbutton_ids[] = {
123 	{ .compatible = "motorola,cpcap-pwrbutton" },
124 	{ }
125 };
126 
127 U_BOOT_DRIVER(cpcap_pwrbutton) = {
128 	.name		= "cpcap_pwrbutton",
129 	.id		= UCLASS_KEYBOARD,
130 	.of_match	= cpcap_pwrbutton_ids,
131 	.of_to_plat	= cpcap_pwrbutton_of_to_plat,
132 	.probe		= cpcap_pwrbutton_probe,
133 	.priv_auto	= sizeof(struct cpcap_pwrbutton_priv),
134 };
135