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