blob: ad7fed3ddaaab936aa312ac01632f7ab0f4a83e4 [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>
18
19#define REG_TYPE 0x4
20#define REG_SUBTYPE 0x5
21
Neil Armstrong36890802024-04-10 17:59:44 +020022struct qcom_pmic_btn_data {
23 char *compatible;
24 unsigned int status_bit;
25 int code;
26 char *label;
27};
28
Caleb Connolly55759a32023-12-05 13:46:47 +000029struct qcom_pmic_btn_priv {
30 u32 base;
31 u32 status_bit;
32 int code;
33 struct udevice *pmic;
34};
35
36#define PON_INT_RT_STS 0x10
Neil Armstrong36890802024-04-10 17:59:44 +020037#define PON_KPDPWR_N_SET 0
38#define PON_RESIN_N_SET 1
Neil Armstrong7231ce32024-04-10 17:59:45 +020039#define PON_GEN3_RESIN_N_SET 6
40#define PON_GEN3_KPDPWR_N_SET 7
Caleb Connolly55759a32023-12-05 13:46:47 +000041
42static enum button_state_t qcom_pwrkey_get_state(struct udevice *dev)
43{
44 struct qcom_pmic_btn_priv *priv = dev_get_priv(dev);
45
46 int reg = pmic_reg_read(priv->pmic, priv->base + PON_INT_RT_STS);
47
48 if (reg < 0)
49 return 0;
50
51 return (reg & BIT(priv->status_bit)) != 0;
52}
53
54static int qcom_pwrkey_get_code(struct udevice *dev)
55{
56 struct qcom_pmic_btn_priv *priv = dev_get_priv(dev);
57
58 return priv->code;
59}
60
Neil Armstrong36890802024-04-10 17:59:44 +020061static const struct qcom_pmic_btn_data qcom_pmic_btn_data_table[] = {
62 {
63 .compatible = "qcom,pm8941-pwrkey",
64 .status_bit = PON_KPDPWR_N_SET,
65 .code = KEY_ENTER,
66 .label = "pwrkey",
67 },
68 {
69 .compatible = "qcom,pm8941-resin",
70 .status_bit = PON_RESIN_N_SET,
71 .code = KEY_DOWN,
72 .label = "vol_down",
73 },
Neil Armstrong7231ce32024-04-10 17:59:45 +020074 {
75 .compatible = "qcom,pmk8350-pwrkey",
76 .status_bit = PON_GEN3_KPDPWR_N_SET,
77 .code = KEY_ENTER,
78 .label = "pwrkey",
79 },
80 {
81 .compatible = "qcom,pmk8350-resin",
82 .status_bit = PON_GEN3_RESIN_N_SET,
83 .code = KEY_DOWN,
84 .label = "vol_down",
85 },
Neil Armstrong36890802024-04-10 17:59:44 +020086};
87
88static const struct qcom_pmic_btn_data *button_qcom_pmic_match(ofnode node)
89{
90 int i;
91
92 for (i = 0; i < ARRAY_SIZE(qcom_pmic_btn_data_table); ++i) {
93 if (ofnode_device_is_compatible(node,
94 qcom_pmic_btn_data_table[i].compatible))
95 return &qcom_pmic_btn_data_table[i];
96 }
97
98 return NULL;
99}
100
Caleb Connolly55759a32023-12-05 13:46:47 +0000101static int qcom_pwrkey_probe(struct udevice *dev)
102{
103 struct button_uc_plat *uc_plat = dev_get_uclass_plat(dev);
104 struct qcom_pmic_btn_priv *priv = dev_get_priv(dev);
Neil Armstrong36890802024-04-10 17:59:44 +0200105 const struct qcom_pmic_btn_data *btn_data;
Caleb Connolly55759a32023-12-05 13:46:47 +0000106 ofnode node = dev_ofnode(dev);
107 int ret;
108 u64 base;
109
110 /* Ignore the top-level pon node */
111 if (!uc_plat->label)
112 return 0;
113
Neil Armstrong36890802024-04-10 17:59:44 +0200114 /* Get the data for the node compatible */
115 btn_data = button_qcom_pmic_match(node);
116 if (!btn_data)
117 return -EINVAL;
118
119 priv->status_bit = btn_data->status_bit;
120 priv->code = btn_data->code;
121
Caleb Connolly55759a32023-12-05 13:46:47 +0000122 /* the pwrkey and resin nodes are children of the "pon" node, get the
123 * PMIC device to use in pmic_reg_* calls.
124 */
125 priv->pmic = dev->parent->parent;
126
127 /* Get the address of the parent pon node */
128 base = dev_read_addr(dev->parent);
129 if (base == FDT_ADDR_T_NONE) {
130 printf("%s: Can't find address\n", dev->name);
131 return -EINVAL;
132 }
133
134 priv->base = base;
135
136 /* Do a sanity check */
137 ret = pmic_reg_read(priv->pmic, priv->base + REG_TYPE);
138 if (ret != 0x1 && ret != 0xb) {
139 printf("%s: unexpected PMIC function type %d\n", dev->name, ret);
140 return -ENXIO;
141 }
142
143 ret = pmic_reg_read(priv->pmic, priv->base + REG_SUBTYPE);
Dan Carpenter7f1daec2024-01-31 10:09:15 +0300144 if (ret < 0 || (ret & 0x7) == 0) {
Neil Armstrong36890802024-04-10 17:59:44 +0200145 printf("%s: unexpected PMIC function subtype %d\n", dev->name, ret);
Caleb Connolly55759a32023-12-05 13:46:47 +0000146 return -ENXIO;
147 }
148
Caleb Connolly55759a32023-12-05 13:46:47 +0000149 return 0;
150}
151
152static int button_qcom_pmic_bind(struct udevice *parent)
153{
154 struct udevice *dev;
155 ofnode node;
156 int ret;
157
158 dev_for_each_subnode(node, parent) {
Neil Armstrong36890802024-04-10 17:59:44 +0200159 const struct qcom_pmic_btn_data *btn_data;
Caleb Connolly55759a32023-12-05 13:46:47 +0000160 struct button_uc_plat *uc_plat;
161 const char *label;
162
163 if (!ofnode_is_enabled(node))
164 continue;
165
Neil Armstrong36890802024-04-10 17:59:44 +0200166 /* Get the data for the node compatible */
167 btn_data = button_qcom_pmic_match(node);
168 if (!btn_data) {
169 debug("Unknown button node '%s'\n", ofnode_get_name(node));
170 continue;
171 }
172
Caleb Connolly55759a32023-12-05 13:46:47 +0000173 ret = device_bind_driver_to_node(parent, "qcom_pwrkey",
174 ofnode_get_name(node),
175 node, &dev);
176 if (ret) {
177 printf("Failed to bind %s! %d\n", label, ret);
178 return ret;
179 }
180 uc_plat = dev_get_uclass_plat(dev);
Neil Armstrong36890802024-04-10 17:59:44 +0200181 uc_plat->label = btn_data->label;
Caleb Connolly55759a32023-12-05 13:46:47 +0000182 }
183
184 return 0;
185}
186
187static const struct button_ops button_qcom_pmic_ops = {
188 .get_state = qcom_pwrkey_get_state,
189 .get_code = qcom_pwrkey_get_code,
190};
191
192static const struct udevice_id qcom_pwrkey_ids[] = {
193 { .compatible = "qcom,pm8916-pon" },
194 { .compatible = "qcom,pm8941-pon" },
195 { .compatible = "qcom,pm8998-pon" },
Neil Armstrong7231ce32024-04-10 17:59:45 +0200196 { .compatible = "qcom,pmk8350-pon" },
Caleb Connolly55759a32023-12-05 13:46:47 +0000197 { }
198};
199
200U_BOOT_DRIVER(qcom_pwrkey) = {
201 .name = "qcom_pwrkey",
202 .id = UCLASS_BUTTON,
203 .of_match = qcom_pwrkey_ids,
204 .bind = button_qcom_pmic_bind,
205 .probe = qcom_pwrkey_probe,
206 .ops = &button_qcom_pmic_ops,
207 .priv_auto = sizeof(struct qcom_pmic_btn_priv),
208};