1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2021 Adrian Chadd <adrian@FreeBSD.org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice unmodified, this list of conditions, and the following
11 * disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35
36 #include <sys/kernel.h>
37 #include <sys/module.h>
38 #include <sys/rman.h>
39 #include <sys/lock.h>
40 #include <sys/malloc.h>
41 #include <sys/mutex.h>
42 #include <sys/gpio.h>
43
44 #include <machine/bus.h>
45 #include <machine/resource.h>
46 #include <dev/gpio/gpiobusvar.h>
47
48 #include <dev/fdt/fdt_common.h>
49 #include <dev/ofw/ofw_bus.h>
50 #include <dev/ofw/ofw_bus_subr.h>
51
52 #include <dev/fdt/fdt_pinctrl.h>
53
54 #include "qcom_tlmm_var.h"
55 #include "qcom_tlmm_pin.h"
56
57 #include "qcom_tlmm_ipq4018_reg.h"
58 #include "qcom_tlmm_ipq4018_hw.h"
59
60 #include "gpio_if.h"
61
62 static struct gpio_pin *
63 qcom_tlmm_pin_lookup(struct qcom_tlmm_softc *sc, int pin)
64 {
65 if (pin >= sc->gpio_npins)
66 return (NULL);
67
68 return &sc->gpio_pins[pin];
69 }
70
71 static void
72 qcom_tlmm_pin_configure(struct qcom_tlmm_softc *sc,
73 struct gpio_pin *pin, unsigned int flags)
74 {
75
76 GPIO_LOCK_ASSERT(sc);
77
78 /*
79 * Manage input/output
80 */
81 if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) {
82 pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT);
83 if (flags & GPIO_PIN_OUTPUT) {
84 /*
85 * XXX TODO: read GPIO_PIN_PRESET_LOW /
86 * GPIO_PIN_PRESET_HIGH and if we're a GPIO
87 * function pin here, set the output
88 * pin value before we flip on oe_output.
89 */
90 pin->gp_flags |= GPIO_PIN_OUTPUT;
91 qcom_tlmm_ipq4018_hw_pin_set_oe_output(sc,
92 pin->gp_pin);
93 } else {
94 pin->gp_flags |= GPIO_PIN_INPUT;
95 qcom_tlmm_ipq4018_hw_pin_set_oe_input(sc,
96 pin->gp_pin);
97 }
98 }
99
100 /*
101 * Set pull-up / pull-down configuration
102 */
103 if (flags & GPIO_PIN_PULLUP) {
104 pin->gp_flags |= GPIO_PIN_PULLUP;
105 qcom_tlmm_ipq4018_hw_pin_set_pupd_config(sc, pin->gp_pin,
106 QCOM_TLMM_PIN_PUPD_CONFIG_PULL_UP);
107 } else if (flags & GPIO_PIN_PULLDOWN) {
108 pin->gp_flags |= GPIO_PIN_PULLDOWN;
109 qcom_tlmm_ipq4018_hw_pin_set_pupd_config(sc, pin->gp_pin,
110 QCOM_TLMM_PIN_PUPD_CONFIG_PULL_DOWN);
111 } else if ((flags & (GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN)) ==
112 (GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN)) {
113 pin->gp_flags |= GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN;
114 qcom_tlmm_ipq4018_hw_pin_set_pupd_config(sc, pin->gp_pin,
115 QCOM_TLMM_PIN_PUPD_CONFIG_BUS_HOLD);
116 } else {
117 pin->gp_flags &= ~(GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN);
118 qcom_tlmm_ipq4018_hw_pin_set_pupd_config(sc, pin->gp_pin,
119 QCOM_TLMM_PIN_PUPD_CONFIG_DISABLE);
120 }
121 }
122
123 device_t
124 qcom_tlmm_get_bus(device_t dev)
125 {
126 struct qcom_tlmm_softc *sc;
127
128 sc = device_get_softc(dev);
129
130 return (sc->busdev);
131 }
132
133 int
134 qcom_tlmm_pin_max(device_t dev, int *maxpin)
135 {
136 struct qcom_tlmm_softc *sc = device_get_softc(dev);
137
138 *maxpin = sc->gpio_npins - 1;
139 return (0);
140 }
141
142 int
143 qcom_tlmm_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
144 {
145 struct qcom_tlmm_softc *sc = device_get_softc(dev);
146 struct gpio_pin *p;
147
148 p = qcom_tlmm_pin_lookup(sc, pin);
149 if (p == NULL)
150 return (EINVAL);
151
152 GPIO_LOCK(sc);
153 *caps = p->gp_caps;
154 GPIO_UNLOCK(sc);
155
156 return (0);
157 }
158
159 int
160 qcom_tlmm_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
161 {
162 struct qcom_tlmm_softc *sc = device_get_softc(dev);
163 uint32_t ret = 0, val;
164 bool is_output;
165 qcom_tlmm_pin_pupd_config_t pupd_config;
166
167 if (pin >= sc->gpio_npins)
168 return (EINVAL);
169
170 *flags = 0;
171
172 GPIO_LOCK(sc);
173
174 /* Lookup function - see what it is, whether we're a GPIO line */
175 ret = qcom_tlmm_ipq4018_hw_pin_get_function(sc, pin, &val);
176 if (ret != 0)
177 goto done;
178
179 /* Lookup input/output state */
180 ret = qcom_tlmm_ipq4018_hw_pin_get_oe_state(sc, pin, &is_output);
181 if (ret != 0)
182 goto done;
183 if (is_output)
184 *flags |= GPIO_PIN_OUTPUT;
185 else
186 *flags |= GPIO_PIN_INPUT;
187
188 /* Lookup pull-up / pull-down state */
189 ret = qcom_tlmm_ipq4018_hw_pin_get_pupd_config(sc, pin,
190 &pupd_config);
191 if (ret != 0)
192 goto done;
193
194 switch (pupd_config) {
195 case QCOM_TLMM_PIN_PUPD_CONFIG_DISABLE:
196 break;
197 case QCOM_TLMM_PIN_PUPD_CONFIG_PULL_DOWN:
198 *flags |= GPIO_PIN_PULLDOWN;
199 break;
200 case QCOM_TLMM_PIN_PUPD_CONFIG_PULL_UP:
201 *flags |= GPIO_PIN_PULLUP;
202 break;
203 case QCOM_TLMM_PIN_PUPD_CONFIG_BUS_HOLD:
204 *flags |= (GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN);
205 break;
206 }
207
208 done:
209 GPIO_UNLOCK(sc);
210 return (ret);
211 }
212
213 int
214 qcom_tlmm_pin_getname(device_t dev, uint32_t pin, char *name)
215 {
216 struct qcom_tlmm_softc *sc = device_get_softc(dev);
217 struct gpio_pin *p;
218
219 p = qcom_tlmm_pin_lookup(sc, pin);
220 if (p == NULL)
221 return (EINVAL);
222
223 GPIO_LOCK(sc);
224 memcpy(name, p->gp_name, GPIOMAXNAME);
225 GPIO_UNLOCK(sc);
226
227 return (0);
228 }
229
230 int
231 qcom_tlmm_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
232 {
233 struct qcom_tlmm_softc *sc = device_get_softc(dev);
234 struct gpio_pin *p;
235
236 p = qcom_tlmm_pin_lookup(sc, pin);
237 if (p == NULL)
238 return (EINVAL);
239
240 GPIO_LOCK(sc);
241 qcom_tlmm_pin_configure(sc, p, flags);
242 GPIO_UNLOCK(sc);
243
244 return (0);
245 }
246
247 int
248 qcom_tlmm_pin_set(device_t dev, uint32_t pin, unsigned int value)
249 {
250 struct qcom_tlmm_softc *sc = device_get_softc(dev);
251 int ret;
252
253 if (pin >= sc->gpio_npins)
254 return (EINVAL);
255
256 GPIO_LOCK(sc);
257 ret = qcom_tlmm_ipq4018_hw_pin_set_output_value(sc, pin, value);
258 GPIO_UNLOCK(sc);
259
260 return (ret);
261 }
262
263 int
264 qcom_tlmm_pin_get(device_t dev, uint32_t pin, unsigned int *val)
265 {
266 struct qcom_tlmm_softc *sc = device_get_softc(dev);
267 int ret;
268
269 if (pin >= sc->gpio_npins)
270 return (EINVAL);
271
272 GPIO_LOCK(sc);
273 ret = qcom_tlmm_ipq4018_hw_pin_get_input_value(sc, pin, val);
274 GPIO_UNLOCK(sc);
275
276 return (ret);
277 }
278
279 int
280 qcom_tlmm_pin_toggle(device_t dev, uint32_t pin)
281 {
282 struct qcom_tlmm_softc *sc = device_get_softc(dev);
283 int ret;
284
285 if (pin >= sc->gpio_npins)
286 return (EINVAL);
287
288 GPIO_LOCK(sc);
289 ret = qcom_tlmm_ipq4018_hw_pin_toggle_output_value(sc, pin);
290 GPIO_UNLOCK(sc);
291
292 return (ret);
293 }
294
295 int
296 qcom_tlmm_filter(void *arg)
297 {
298
299 /* TODO: something useful */
300 return (FILTER_STRAY);
301 }
302
303 void
304 qcom_tlmm_intr(void *arg)
305 {
306 struct qcom_tlmm_softc *sc = arg;
307 GPIO_LOCK(sc);
308 /* TODO: something useful */
309 GPIO_UNLOCK(sc);
310 }
311
312 /*
313 * ofw bus interface
314 */
315 phandle_t
316 qcom_tlmm_pin_get_node(device_t dev, device_t bus)
317 {
318
319 /* We only have one child, the GPIO bus, which needs our own node. */
320 return (ofw_bus_get_node(dev));
321 }
322
Cache object: a8deeb69409273708319eb3eb7f1a27a
|