blob: f6da958097c4c02927c5d5b8b3b3ef02501eac3e [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
Caleb Connolly55759a32023-12-05 13:46:47 +000039
40static enum button_state_t qcom_pwrkey_get_state(struct udevice *dev)
41{
42 struct qcom_pmic_btn_priv *priv = dev_get_priv(dev);
43
44 int reg = pmic_reg_read(priv->pmic, priv->base + PON_INT_RT_STS);
45
46 if (reg < 0)
47 return 0;
48
49 return (reg & BIT(priv->status_bit)) != 0;
50}
51
52static int qcom_pwrkey_get_code(struct udevice *dev)
53{
54 struct qcom_pmic_btn_priv *priv = dev_get_priv(dev);
55
56 return priv->code;
57}
58
Neil Armstrong36890802024-04-10 17:59:44 +020059static const struct qcom_pmic_btn_data qcom_pmic_btn_data_table[] = {
60 {
61 .compatible = "qcom,pm8941-pwrkey",
62 .status_bit = PON_KPDPWR_N_SET,
63 .code = KEY_ENTER,
64 .label = "pwrkey",
65 },
66 {
67 .compatible = "qcom,pm8941-resin",
68 .status_bit = PON_RESIN_N_SET,
69 .code = KEY_DOWN,
70 .label = "vol_down",
71 },
72};
73
74static const struct qcom_pmic_btn_data *button_qcom_pmic_match(ofnode node)
75{
76 int i;
77
78 for (i = 0; i < ARRAY_SIZE(qcom_pmic_btn_data_table); ++i) {
79 if (ofnode_device_is_compatible(node,
80 qcom_pmic_btn_data_table[i].compatible))
81 return &qcom_pmic_btn_data_table[i];
82 }
83
84 return NULL;
85}
86
Caleb Connolly55759a32023-12-05 13:46:47 +000087static int qcom_pwrkey_probe(struct udevice *dev)
88{
89 struct button_uc_plat *uc_plat = dev_get_uclass_plat(dev);
90 struct qcom_pmic_btn_priv *priv = dev_get_priv(dev);
Neil Armstrong36890802024-04-10 17:59:44 +020091 const struct qcom_pmic_btn_data *btn_data;
Caleb Connolly55759a32023-12-05 13:46:47 +000092 ofnode node = dev_ofnode(dev);
93 int ret;
94 u64 base;
95
96 /* Ignore the top-level pon node */
97 if (!uc_plat->label)
98 return 0;
99
Neil Armstrong36890802024-04-10 17:59:44 +0200100 /* Get the data for the node compatible */
101 btn_data = button_qcom_pmic_match(node);
102 if (!btn_data)
103 return -EINVAL;
104
105 priv->status_bit = btn_data->status_bit;
106 priv->code = btn_data->code;
107
Caleb Connolly55759a32023-12-05 13:46:47 +0000108 /* the pwrkey and resin nodes are children of the "pon" node, get the
109 * PMIC device to use in pmic_reg_* calls.
110 */
111 priv->pmic = dev->parent->parent;
112
113 /* Get the address of the parent pon node */
114 base = dev_read_addr(dev->parent);
115 if (base == FDT_ADDR_T_NONE) {
116 printf("%s: Can't find address\n", dev->name);
117 return -EINVAL;
118 }
119
120 priv->base = base;
121
122 /* Do a sanity check */
123 ret = pmic_reg_read(priv->pmic, priv->base + REG_TYPE);
124 if (ret != 0x1 && ret != 0xb) {
125 printf("%s: unexpected PMIC function type %d\n", dev->name, ret);
126 return -ENXIO;
127 }
128
129 ret = pmic_reg_read(priv->pmic, priv->base + REG_SUBTYPE);
Dan Carpenter7f1daec2024-01-31 10:09:15 +0300130 if (ret < 0 || (ret & 0x7) == 0) {
Neil Armstrong36890802024-04-10 17:59:44 +0200131 printf("%s: unexpected PMIC function subtype %d\n", dev->name, ret);
Caleb Connolly55759a32023-12-05 13:46:47 +0000132 return -ENXIO;
133 }
134
Caleb Connolly55759a32023-12-05 13:46:47 +0000135 return 0;
136}
137
138static int button_qcom_pmic_bind(struct udevice *parent)
139{
140 struct udevice *dev;
141 ofnode node;
142 int ret;
143
144 dev_for_each_subnode(node, parent) {
Neil Armstrong36890802024-04-10 17:59:44 +0200145 const struct qcom_pmic_btn_data *btn_data;
Caleb Connolly55759a32023-12-05 13:46:47 +0000146 struct button_uc_plat *uc_plat;
147 const char *label;
148
149 if (!ofnode_is_enabled(node))
150 continue;
151
Neil Armstrong36890802024-04-10 17:59:44 +0200152 /* Get the data for the node compatible */
153 btn_data = button_qcom_pmic_match(node);
154 if (!btn_data) {
155 debug("Unknown button node '%s'\n", ofnode_get_name(node));
156 continue;
157 }
158
Caleb Connolly55759a32023-12-05 13:46:47 +0000159 ret = device_bind_driver_to_node(parent, "qcom_pwrkey",
160 ofnode_get_name(node),
161 node, &dev);
162 if (ret) {
163 printf("Failed to bind %s! %d\n", label, ret);
164 return ret;
165 }
166 uc_plat = dev_get_uclass_plat(dev);
Neil Armstrong36890802024-04-10 17:59:44 +0200167 uc_plat->label = btn_data->label;
Caleb Connolly55759a32023-12-05 13:46:47 +0000168 }
169
170 return 0;
171}
172
173static const struct button_ops button_qcom_pmic_ops = {
174 .get_state = qcom_pwrkey_get_state,
175 .get_code = qcom_pwrkey_get_code,
176};
177
178static const struct udevice_id qcom_pwrkey_ids[] = {
179 { .compatible = "qcom,pm8916-pon" },
180 { .compatible = "qcom,pm8941-pon" },
181 { .compatible = "qcom,pm8998-pon" },
182 { }
183};
184
185U_BOOT_DRIVER(qcom_pwrkey) = {
186 .name = "qcom_pwrkey",
187 .id = UCLASS_BUTTON,
188 .of_match = qcom_pwrkey_ids,
189 .bind = button_qcom_pmic_bind,
190 .probe = qcom_pwrkey_probe,
191 .ops = &button_qcom_pmic_ops,
192 .priv_auto = sizeof(struct qcom_pmic_btn_priv),
193};