1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
|
// SPDX-License-Identifier: GPL-2.0+
/*
* Qualcomm generic pmic gpio driver
*
* (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
* (C) Copyright 2023 Linaro Ltd.
*/
#include <button.h>
#include <dt-bindings/input/linux-event-codes.h>
#include <dm.h>
#include <dm/device-internal.h>
#include <dm/lists.h>
#include <log.h>
#include <power/pmic.h>
#include <spmi/spmi.h>
#include <linux/bitops.h>
#define REG_TYPE 0x4
#define REG_SUBTYPE 0x5
struct qcom_pmic_btn_priv {
u32 base;
u32 status_bit;
int code;
struct udevice *pmic;
};
#define PON_INT_RT_STS 0x10
#define KPDPWR_ON_INT_BIT 0
#define RESIN_ON_INT_BIT 1
#define NODE_IS_PWRKEY(node) (!strncmp(ofnode_get_name(node), "pwrkey", strlen("pwrkey")))
#define NODE_IS_RESIN(node) (!strncmp(ofnode_get_name(node), "resin", strlen("resin")))
static enum button_state_t qcom_pwrkey_get_state(struct udevice *dev)
{
struct qcom_pmic_btn_priv *priv = dev_get_priv(dev);
int reg = pmic_reg_read(priv->pmic, priv->base + PON_INT_RT_STS);
if (reg < 0)
return 0;
return (reg & BIT(priv->status_bit)) != 0;
}
static int qcom_pwrkey_get_code(struct udevice *dev)
{
struct qcom_pmic_btn_priv *priv = dev_get_priv(dev);
return priv->code;
}
static int qcom_pwrkey_probe(struct udevice *dev)
{
struct button_uc_plat *uc_plat = dev_get_uclass_plat(dev);
struct qcom_pmic_btn_priv *priv = dev_get_priv(dev);
ofnode node = dev_ofnode(dev);
int ret;
u64 base;
/* Ignore the top-level pon node */
if (!uc_plat->label)
return 0;
/* the pwrkey and resin nodes are children of the "pon" node, get the
* PMIC device to use in pmic_reg_* calls.
*/
priv->pmic = dev->parent->parent;
/* Get the address of the parent pon node */
base = dev_read_addr(dev->parent);
if (base == FDT_ADDR_T_NONE) {
printf("%s: Can't find address\n", dev->name);
return -EINVAL;
}
priv->base = base;
/* Do a sanity check */
ret = pmic_reg_read(priv->pmic, priv->base + REG_TYPE);
if (ret != 0x1 && ret != 0xb) {
printf("%s: unexpected PMIC function type %d\n", dev->name, ret);
return -ENXIO;
}
ret = pmic_reg_read(priv->pmic, priv->base + REG_SUBTYPE);
if (ret < 0 || (ret & 0x7) == 0) {
printf("%s: unexpected PMCI function subtype %d\n", dev->name, ret);
return -ENXIO;
}
if (NODE_IS_PWRKEY(node)) {
priv->status_bit = 0;
priv->code = KEY_ENTER;
} else if (NODE_IS_RESIN(node)) {
priv->status_bit = 1;
priv->code = KEY_DOWN;
} else {
/* Should not get here! */
printf("Invalid pon node '%s' should be 'pwrkey' or 'resin'\n",
ofnode_get_name(node));
return -EINVAL;
}
return 0;
}
static int button_qcom_pmic_bind(struct udevice *parent)
{
struct udevice *dev;
ofnode node;
int ret;
dev_for_each_subnode(node, parent) {
struct button_uc_plat *uc_plat;
const char *label;
if (!ofnode_is_enabled(node))
continue;
ret = device_bind_driver_to_node(parent, "qcom_pwrkey",
ofnode_get_name(node),
node, &dev);
if (ret) {
printf("Failed to bind %s! %d\n", label, ret);
return ret;
}
uc_plat = dev_get_uclass_plat(dev);
if (NODE_IS_PWRKEY(node)) {
uc_plat->label = "pwrkey";
} else if (NODE_IS_RESIN(node)) {
uc_plat->label = "vol_down";
} else {
debug("Unknown button node '%s' should be 'pwrkey' or 'resin'\n",
ofnode_get_name(node));
device_unbind(dev);
}
}
return 0;
}
static const struct button_ops button_qcom_pmic_ops = {
.get_state = qcom_pwrkey_get_state,
.get_code = qcom_pwrkey_get_code,
};
static const struct udevice_id qcom_pwrkey_ids[] = {
{ .compatible = "qcom,pm8916-pon" },
{ .compatible = "qcom,pm8941-pon" },
{ .compatible = "qcom,pm8998-pon" },
{ }
};
U_BOOT_DRIVER(qcom_pwrkey) = {
.name = "qcom_pwrkey",
.id = UCLASS_BUTTON,
.of_match = qcom_pwrkey_ids,
.bind = button_qcom_pmic_bind,
.probe = qcom_pwrkey_probe,
.ops = &button_qcom_pmic_ops,
.priv_auto = sizeof(struct qcom_pmic_btn_priv),
};
|