blob: c8ad39d33ca990ec61efa08599eae48150a27f11 [file] [log] [blame]
Svyatoslav Ryhel43cc7f52025-03-25 20:23:07 +02001// 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
16static 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 */
33struct cpcap_pwrbutton_priv {
34 u32 bank;
35 u32 id;
36
37 u32 keycode;
38
39 bool old_state;
40 bool skip;
41};
42
43static 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
70static 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
99static 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
122static const struct udevice_id cpcap_pwrbutton_ids[] = {
123 { .compatible = "motorola,cpcap-pwrbutton" },
124 { }
125};
126
127U_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};