blob: f9f0948ae095f433e35ecfc0f98741ab1a850742 [file] [log] [blame]
Caleb Connolly55759a32023-12-05 13:46:47 +00001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Qualcomm generic pmic gpio driver
4 *
5 * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
6 * (C) Copyright 2023 Linaro Ltd.
7 */
8
9#include <button.h>
10#include <dt-bindings/input/linux-event-codes.h>
11#include <dm.h>
12#include <dm/device-internal.h>
13#include <dm/lists.h>
14#include <log.h>
15#include <power/pmic.h>
16#include <spmi/spmi.h>
17#include <linux/bitops.h>
Caleb Connollyd176ad72024-11-13 05:51:03 +010018#include <time.h>
Caleb Connolly55759a32023-12-05 13:46:47 +000019
20#define REG_TYPE 0x4
21#define REG_SUBTYPE 0x5
22
Neil Armstrong36890802024-04-10 17:59:44 +020023struct qcom_pmic_btn_data {
24 char *compatible;
25 unsigned int status_bit;
26 int code;
27 char *label;
28};
29
Caleb Connolly55759a32023-12-05 13:46:47 +000030struct qcom_pmic_btn_priv {
31 u32 base;
32 u32 status_bit;
33 int code;
34 struct udevice *pmic;
Caleb Connollyd176ad72024-11-13 05:51:03 +010035 ulong last_release_time;
Caleb Connolly55759a32023-12-05 13:46:47 +000036};
37
38#define PON_INT_RT_STS 0x10
Neil Armstrong36890802024-04-10 17:59:44 +020039#define PON_KPDPWR_N_SET 0
40#define PON_RESIN_N_SET 1
Neil Armstrong7231ce32024-04-10 17:59:45 +020041#define PON_GEN3_RESIN_N_SET 6
42#define PON_GEN3_KPDPWR_N_SET 7
Caleb Connolly55759a32023-12-05 13:46:47 +000043
44static enum button_state_t qcom_pwrkey_get_state(struct udevice *dev)
45{
46 struct qcom_pmic_btn_priv *priv = dev_get_priv(dev);
Caleb Connollyd176ad72024-11-13 05:51:03 +010047 bool pressed;
48 int reg;
Caleb Connolly55759a32023-12-05 13:46:47 +000049
Caleb Connollyd176ad72024-11-13 05:51:03 +010050 if (get_timer_us(0) - priv->last_release_time < 25000)
51 return BUTTON_OFF;
Caleb Connolly55759a32023-12-05 13:46:47 +000052
Caleb Connollyd176ad72024-11-13 05:51:03 +010053 reg = pmic_reg_read(priv->pmic, priv->base + PON_INT_RT_STS);
Caleb Connolly55759a32023-12-05 13:46:47 +000054 if (reg < 0)
55 return 0;
56
Caleb Connollyd176ad72024-11-13 05:51:03 +010057 pressed = !!(reg & BIT(priv->status_bit));
58 if (!pressed)
59 priv->last_release_time = get_timer_us(0);
60
61 return pressed;
Caleb Connolly55759a32023-12-05 13:46:47 +000062}
63
64static int qcom_pwrkey_get_code(struct udevice *dev)
65{
66 struct qcom_pmic_btn_priv *priv = dev_get_priv(dev);
67
68 return priv->code;
69}
70
Neil Armstrong36890802024-04-10 17:59:44 +020071static const struct qcom_pmic_btn_data qcom_pmic_btn_data_table[] = {
72 {
73 .compatible = "qcom,pm8941-pwrkey",
74 .status_bit = PON_KPDPWR_N_SET,
75 .code = KEY_ENTER,
76 .label = "pwrkey",
77 },
78 {
79 .compatible = "qcom,pm8941-resin",
80 .status_bit = PON_RESIN_N_SET,
81 .code = KEY_DOWN,
82 .label = "vol_down",
83 },
Neil Armstrong7231ce32024-04-10 17:59:45 +020084 {
85 .compatible = "qcom,pmk8350-pwrkey",
86 .status_bit = PON_GEN3_KPDPWR_N_SET,
87 .code = KEY_ENTER,
88 .label = "pwrkey",
89 },
90 {
91 .compatible = "qcom,pmk8350-resin",
92 .status_bit = PON_GEN3_RESIN_N_SET,
93 .code = KEY_DOWN,
94 .label = "vol_down",
95 },
Neil Armstrong36890802024-04-10 17:59:44 +020096};
97
98static const struct qcom_pmic_btn_data *button_qcom_pmic_match(ofnode node)
99{
100 int i;
101
102 for (i = 0; i < ARRAY_SIZE(qcom_pmic_btn_data_table); ++i) {
103 if (ofnode_device_is_compatible(node,
104 qcom_pmic_btn_data_table[i].compatible))
105 return &qcom_pmic_btn_data_table[i];
106 }
107
108 return NULL;
109}
110
Caleb Connolly55759a32023-12-05 13:46:47 +0000111static int qcom_pwrkey_probe(struct udevice *dev)
112{
113 struct button_uc_plat *uc_plat = dev_get_uclass_plat(dev);
114 struct qcom_pmic_btn_priv *priv = dev_get_priv(dev);
Neil Armstrong36890802024-04-10 17:59:44 +0200115 const struct qcom_pmic_btn_data *btn_data;
Caleb Connolly55759a32023-12-05 13:46:47 +0000116 ofnode node = dev_ofnode(dev);
117 int ret;
118 u64 base;
119
120 /* Ignore the top-level pon node */
121 if (!uc_plat->label)
122 return 0;
123
Neil Armstrong36890802024-04-10 17:59:44 +0200124 /* Get the data for the node compatible */
125 btn_data = button_qcom_pmic_match(node);
126 if (!btn_data)
127 return -EINVAL;
128
129 priv->status_bit = btn_data->status_bit;
130 priv->code = btn_data->code;
131
Caleb Connolly55759a32023-12-05 13:46:47 +0000132 /* the pwrkey and resin nodes are children of the "pon" node, get the
133 * PMIC device to use in pmic_reg_* calls.
134 */
135 priv->pmic = dev->parent->parent;
136
137 /* Get the address of the parent pon node */
138 base = dev_read_addr(dev->parent);
139 if (base == FDT_ADDR_T_NONE) {
140 printf("%s: Can't find address\n", dev->name);
141 return -EINVAL;
142 }
143
144 priv->base = base;
145
146 /* Do a sanity check */
147 ret = pmic_reg_read(priv->pmic, priv->base + REG_TYPE);
148 if (ret != 0x1 && ret != 0xb) {
149 printf("%s: unexpected PMIC function type %d\n", dev->name, ret);
150 return -ENXIO;
151 }
152
153 ret = pmic_reg_read(priv->pmic, priv->base + REG_SUBTYPE);
Dan Carpenter7f1daec2024-01-31 10:09:15 +0300154 if (ret < 0 || (ret & 0x7) == 0) {
Neil Armstrong36890802024-04-10 17:59:44 +0200155 printf("%s: unexpected PMIC function subtype %d\n", dev->name, ret);
Caleb Connolly55759a32023-12-05 13:46:47 +0000156 return -ENXIO;
157 }
158
Caleb Connolly55759a32023-12-05 13:46:47 +0000159 return 0;
160}
161
162static int button_qcom_pmic_bind(struct udevice *parent)
163{
164 struct udevice *dev;
165 ofnode node;
166 int ret;
167
168 dev_for_each_subnode(node, parent) {
Neil Armstrong36890802024-04-10 17:59:44 +0200169 const struct qcom_pmic_btn_data *btn_data;
Caleb Connolly55759a32023-12-05 13:46:47 +0000170 struct button_uc_plat *uc_plat;
171 const char *label;
172
173 if (!ofnode_is_enabled(node))
174 continue;
175
Neil Armstrong36890802024-04-10 17:59:44 +0200176 /* Get the data for the node compatible */
177 btn_data = button_qcom_pmic_match(node);
178 if (!btn_data) {
179 debug("Unknown button node '%s'\n", ofnode_get_name(node));
180 continue;
181 }
182
Caleb Connolly55759a32023-12-05 13:46:47 +0000183 ret = device_bind_driver_to_node(parent, "qcom_pwrkey",
184 ofnode_get_name(node),
185 node, &dev);
186 if (ret) {
187 printf("Failed to bind %s! %d\n", label, ret);
188 return ret;
189 }
190 uc_plat = dev_get_uclass_plat(dev);
Neil Armstrong36890802024-04-10 17:59:44 +0200191 uc_plat->label = btn_data->label;
Caleb Connolly55759a32023-12-05 13:46:47 +0000192 }
193
194 return 0;
195}
196
197static const struct button_ops button_qcom_pmic_ops = {
198 .get_state = qcom_pwrkey_get_state,
199 .get_code = qcom_pwrkey_get_code,
200};
201
202static const struct udevice_id qcom_pwrkey_ids[] = {
203 { .compatible = "qcom,pm8916-pon" },
204 { .compatible = "qcom,pm8941-pon" },
205 { .compatible = "qcom,pm8998-pon" },
Neil Armstrong7231ce32024-04-10 17:59:45 +0200206 { .compatible = "qcom,pmk8350-pon" },
Caleb Connolly55759a32023-12-05 13:46:47 +0000207 { }
208};
209
210U_BOOT_DRIVER(qcom_pwrkey) = {
211 .name = "qcom_pwrkey",
212 .id = UCLASS_BUTTON,
213 .of_match = qcom_pwrkey_ids,
214 .bind = button_qcom_pmic_bind,
215 .probe = qcom_pwrkey_probe,
216 .ops = &button_qcom_pmic_ops,
217 .priv_auto = sizeof(struct qcom_pmic_btn_priv),
218};