1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
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 /*
30 * This is a pinmux/gpio controller for the IPQ4018/IPQ4019.
31 */
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/bus.h>
36
37 #include <sys/kernel.h>
38 #include <sys/module.h>
39 #include <sys/rman.h>
40 #include <sys/lock.h>
41 #include <sys/malloc.h>
42 #include <sys/mutex.h>
43 #include <sys/gpio.h>
44
45 #include <machine/bus.h>
46 #include <machine/resource.h>
47 #include <dev/gpio/gpiobusvar.h>
48
49 #include <dev/fdt/fdt_common.h>
50 #include <dev/ofw/ofw_bus.h>
51 #include <dev/ofw/ofw_bus_subr.h>
52
53 #include <dev/fdt/fdt_pinctrl.h>
54
55 #include "qcom_tlmm_var.h"
56
57 #include "qcom_tlmm_ipq4018_reg.h"
58 #include "qcom_tlmm_ipq4018_hw.h"
59
60 #include "gpio_if.h"
61
62 /*
63 * Set the pin function. This is a hardware and pin specific mapping.
64 *
65 * Returns 0 if OK, an errno if an error was encountered.
66 */
67 int
qcom_tlmm_ipq4018_hw_pin_set_function(struct qcom_tlmm_softc * sc,int pin,int function)68 qcom_tlmm_ipq4018_hw_pin_set_function(struct qcom_tlmm_softc *sc,
69 int pin, int function)
70 {
71 uint32_t reg;
72
73 GPIO_LOCK_ASSERT(sc);
74
75 if (pin >= sc->gpio_npins)
76 return (EINVAL);
77
78 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
79 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL));
80 reg &= ~(QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_MUX_MASK
81 << QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_MUX_SHIFT);
82 reg |= (function & QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_MUX_MASK)
83 << QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_MUX_SHIFT;
84 GPIO_WRITE(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
85 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL), reg);
86
87 return (0);
88 }
89
90 /*
91 * Get the pin function. This is a hardware and pin specific mapping.
92 *
93 * Returns 0 if OK, an errno if a nerror was encountered.
94 */
95 int
qcom_tlmm_ipq4018_hw_pin_get_function(struct qcom_tlmm_softc * sc,int pin,int * function)96 qcom_tlmm_ipq4018_hw_pin_get_function(struct qcom_tlmm_softc *sc,
97 int pin, int *function)
98 {
99 uint32_t reg;
100
101 GPIO_LOCK_ASSERT(sc);
102
103 if (pin >= sc->gpio_npins)
104 return (EINVAL);
105
106
107 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
108 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL));
109 reg = reg >> QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_MUX_SHIFT;
110 reg &= QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_MUX_MASK;
111 *function = reg;
112
113 return (0);
114 }
115
116 /*
117 * Set the OE bit to be output. This assumes the port is configured
118 * as a GPIO port.
119 */
120 int
qcom_tlmm_ipq4018_hw_pin_set_oe_output(struct qcom_tlmm_softc * sc,int pin)121 qcom_tlmm_ipq4018_hw_pin_set_oe_output(struct qcom_tlmm_softc *sc,
122 int pin)
123 {
124 uint32_t reg;
125
126 GPIO_LOCK_ASSERT(sc);
127
128 if (pin >= sc->gpio_npins)
129 return (EINVAL);
130
131 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
132 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL));
133 reg |= QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_OE_ENABLE;
134 GPIO_WRITE(sc,
135 QCOM_TLMM_IPQ4018_REG_PIN(pin, QCOM_TLMM_IPQ4018_REG_PIN_CONTROL),
136 reg);
137
138 return (0);
139 }
140
141 /*
142 * Set the OE bit to be input. This assumes the port is configured
143 * as a GPIO port.
144 */
145 int
qcom_tlmm_ipq4018_hw_pin_set_oe_input(struct qcom_tlmm_softc * sc,int pin)146 qcom_tlmm_ipq4018_hw_pin_set_oe_input(struct qcom_tlmm_softc *sc,
147 int pin)
148 {
149 uint32_t reg;
150
151 GPIO_LOCK_ASSERT(sc);
152
153 if (pin >= sc->gpio_npins)
154 return (EINVAL);
155
156 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
157 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL));
158 reg &= ~QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_OE_ENABLE;
159 GPIO_WRITE(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
160 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL), reg);
161
162 return (0);
163 }
164
165 /*
166 * Get the GPIO pin direction. is_output is set to true if the pin
167 * is an output pin, false if it's set to an input pin.
168 */
169 int
qcom_tlmm_ipq4018_hw_pin_get_oe_state(struct qcom_tlmm_softc * sc,int pin,bool * is_output)170 qcom_tlmm_ipq4018_hw_pin_get_oe_state(struct qcom_tlmm_softc *sc,
171 int pin, bool *is_output)
172 {
173 uint32_t reg;
174
175 GPIO_LOCK_ASSERT(sc);
176
177 if (pin >= sc->gpio_npins)
178 return (EINVAL);
179
180 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
181 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL));
182 *is_output = !! (reg & QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_OE_ENABLE);
183
184 return (0);
185 }
186
187
188 /*
189 * Set the given GPIO pin to the given value.
190 */
191 int
qcom_tlmm_ipq4018_hw_pin_set_output_value(struct qcom_tlmm_softc * sc,uint32_t pin,unsigned int value)192 qcom_tlmm_ipq4018_hw_pin_set_output_value(struct qcom_tlmm_softc *sc,
193 uint32_t pin, unsigned int value)
194 {
195 uint32_t reg;
196
197 GPIO_LOCK_ASSERT(sc);
198
199 if (pin >= sc->gpio_npins)
200 return (EINVAL);
201
202 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
203 QCOM_TLMM_IPQ4018_REG_PIN_IO));
204 if (value)
205 reg |= QCOM_TLMM_IPQ4018_REG_PIN_IO_OUTPUT_EN;
206 else
207 reg &= ~QCOM_TLMM_IPQ4018_REG_PIN_IO_OUTPUT_EN;
208 GPIO_WRITE(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
209 QCOM_TLMM_IPQ4018_REG_PIN_IO), reg);
210
211 return (0);
212 }
213
214 /*
215 * Get the input state of the current GPIO pin.
216 */
217 int
qcom_tlmm_ipq4018_hw_pin_get_output_value(struct qcom_tlmm_softc * sc,uint32_t pin,unsigned int * val)218 qcom_tlmm_ipq4018_hw_pin_get_output_value(struct qcom_tlmm_softc *sc,
219 uint32_t pin, unsigned int *val)
220 {
221 uint32_t reg;
222
223 GPIO_LOCK_ASSERT(sc);
224
225 if (pin >= sc->gpio_npins)
226 return (EINVAL);
227
228 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
229 QCOM_TLMM_IPQ4018_REG_PIN_IO));
230
231 *val = !! (reg & QCOM_TLMM_IPQ4018_REG_PIN_IO_INPUT_STATUS);
232
233 return (0);
234 }
235
236
237 /*
238 * Get the input state of the current GPIO pin.
239 */
240 int
qcom_tlmm_ipq4018_hw_pin_get_input_value(struct qcom_tlmm_softc * sc,uint32_t pin,unsigned int * val)241 qcom_tlmm_ipq4018_hw_pin_get_input_value(struct qcom_tlmm_softc *sc,
242 uint32_t pin, unsigned int *val)
243 {
244 uint32_t reg;
245
246 GPIO_LOCK_ASSERT(sc);
247
248 if (pin >= sc->gpio_npins)
249 return (EINVAL);
250
251 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
252 QCOM_TLMM_IPQ4018_REG_PIN_IO));
253
254 *val = !! (reg & QCOM_TLMM_IPQ4018_REG_PIN_IO_INPUT_STATUS);
255
256 return (0);
257 }
258
259 /*
260 * Toggle the current output pin value.
261 */
262 int
qcom_tlmm_ipq4018_hw_pin_toggle_output_value(struct qcom_tlmm_softc * sc,uint32_t pin)263 qcom_tlmm_ipq4018_hw_pin_toggle_output_value(
264 struct qcom_tlmm_softc *sc, uint32_t pin)
265 {
266 uint32_t reg;
267
268 GPIO_LOCK_ASSERT(sc);
269
270 if (pin >= sc->gpio_npins)
271 return (EINVAL);
272
273 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
274 QCOM_TLMM_IPQ4018_REG_PIN_IO));
275 if ((reg & QCOM_TLMM_IPQ4018_REG_PIN_IO_OUTPUT_EN) == 0)
276 reg |= QCOM_TLMM_IPQ4018_REG_PIN_IO_OUTPUT_EN;
277 else
278 reg &= ~QCOM_TLMM_IPQ4018_REG_PIN_IO_OUTPUT_EN;
279 GPIO_WRITE(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
280 QCOM_TLMM_IPQ4018_REG_PIN_IO), reg);
281
282 return (0);
283 }
284
285 /*
286 * Configure the pull-up / pull-down top-level configuration.
287 *
288 * This doesn't configure the resistor values, just what's enabled/disabled.
289 */
290 int
qcom_tlmm_ipq4018_hw_pin_set_pupd_config(struct qcom_tlmm_softc * sc,uint32_t pin,qcom_tlmm_pin_pupd_config_t pupd)291 qcom_tlmm_ipq4018_hw_pin_set_pupd_config(
292 struct qcom_tlmm_softc *sc, uint32_t pin,
293 qcom_tlmm_pin_pupd_config_t pupd)
294 {
295 uint32_t reg;
296
297 GPIO_LOCK_ASSERT(sc);
298
299 if (pin >= sc->gpio_npins)
300 return (EINVAL);
301
302 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
303 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL));
304
305 reg &= ~(QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_MASK
306 << QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_SHIFT);
307
308 switch (pupd) {
309 case QCOM_TLMM_PIN_PUPD_CONFIG_DISABLE:
310 reg |= QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_DISABLE
311 << QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_SHIFT;
312 break;
313 case QCOM_TLMM_PIN_PUPD_CONFIG_PULL_DOWN:
314 reg |= QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_PULLDOWN
315 << QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_SHIFT;
316 break;
317 case QCOM_TLMM_PIN_PUPD_CONFIG_PULL_UP:
318 reg |= QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_PULLUP
319 << QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_SHIFT;
320 break;
321 case QCOM_TLMM_PIN_PUPD_CONFIG_BUS_HOLD:
322 reg |= QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_BUSHOLD
323 << QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_SHIFT;
324 break;
325 }
326
327 GPIO_WRITE(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
328 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL), reg);
329
330 return (0);
331 }
332
333 /*
334 * Fetch the current pull-up / pull-down configuration.
335 */
336 int
qcom_tlmm_ipq4018_hw_pin_get_pupd_config(struct qcom_tlmm_softc * sc,uint32_t pin,qcom_tlmm_pin_pupd_config_t * pupd)337 qcom_tlmm_ipq4018_hw_pin_get_pupd_config(
338 struct qcom_tlmm_softc *sc, uint32_t pin,
339 qcom_tlmm_pin_pupd_config_t *pupd)
340 {
341 uint32_t reg;
342
343 GPIO_LOCK_ASSERT(sc);
344
345 if (pin >= sc->gpio_npins)
346 return (EINVAL);
347
348 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
349 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL));
350
351 reg >>= QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_SHIFT;
352 reg &= QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_MASK;
353
354 switch (reg) {
355 case QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_DISABLE:
356 *pupd = QCOM_TLMM_PIN_PUPD_CONFIG_DISABLE;
357 break;
358 case QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_PULLDOWN:
359 *pupd = QCOM_TLMM_PIN_PUPD_CONFIG_PULL_DOWN;
360 break;
361 case QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_PULLUP:
362 *pupd = QCOM_TLMM_PIN_PUPD_CONFIG_PULL_UP;
363 break;
364 default:
365 *pupd = QCOM_TLMM_PIN_PUPD_CONFIG_DISABLE;
366 break;
367 }
368
369 return (0);
370 }
371
372 /*
373 * Set the drive strength in mA.
374 */
375 int
qcom_tlmm_ipq4018_hw_pin_set_drive_strength(struct qcom_tlmm_softc * sc,uint32_t pin,uint8_t drv)376 qcom_tlmm_ipq4018_hw_pin_set_drive_strength(
377 struct qcom_tlmm_softc *sc, uint32_t pin, uint8_t drv)
378 {
379 uint32_t reg;
380
381 GPIO_LOCK_ASSERT(sc);
382
383 if (pin >= sc->gpio_npins)
384 return (EINVAL);
385
386 /* Convert mA to hardware */
387 if (drv > 16 || drv < 2)
388 return (EINVAL);
389 drv = (drv / 2) - 1;
390
391 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
392 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL));
393
394 reg &= ~(QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_DRIVE_STRENGTH_SHIFT
395 << QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_DRIVE_STRENGTH_MASK);
396 reg |= (drv & QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_DRIVE_STRENGTH_MASK)
397 << QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_DRIVE_STRENGTH_SHIFT;
398
399 GPIO_WRITE(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
400 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL), reg);
401
402 return (0);
403 }
404
405 /*
406 * Get the drive strength in mA.
407 */
408 int
qcom_tlmm_ipq4018_hw_pin_get_drive_strength(struct qcom_tlmm_softc * sc,uint32_t pin,uint8_t * drv)409 qcom_tlmm_ipq4018_hw_pin_get_drive_strength(
410 struct qcom_tlmm_softc *sc, uint32_t pin, uint8_t *drv)
411 {
412 uint32_t reg;
413
414 GPIO_LOCK_ASSERT(sc);
415
416 if (pin >= sc->gpio_npins)
417 return (EINVAL);
418
419 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
420 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL));
421
422 *drv = (reg >> QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_DRIVE_STRENGTH_SHIFT)
423 & QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_DRIVE_STRENGTH_MASK;
424
425 *drv = (*drv + 1) * 2;
426
427 return (0);
428 }
429
430
431 /*
432 * Enable/disable whether this pin is passed through to a VM.
433 */
434 int
qcom_tlmm_ipq4018_hw_pin_set_vm(struct qcom_tlmm_softc * sc,uint32_t pin,bool enable)435 qcom_tlmm_ipq4018_hw_pin_set_vm(
436 struct qcom_tlmm_softc *sc, uint32_t pin, bool enable)
437 {
438 uint32_t reg;
439
440 GPIO_LOCK_ASSERT(sc);
441
442 if (pin >= sc->gpio_npins)
443 return (EINVAL);
444
445 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
446 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL));
447
448 reg &= ~QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_VM_ENABLE;
449 if (enable)
450 reg |= QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_VM_ENABLE;
451
452 GPIO_WRITE(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
453 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL), reg);
454
455 return (0);
456 }
457
458 /*
459 * Get the VM configuration bit.
460 */
461 int
qcom_tlmm_ipq4018_hw_pin_get_vm(struct qcom_tlmm_softc * sc,uint32_t pin,bool * enable)462 qcom_tlmm_ipq4018_hw_pin_get_vm(
463 struct qcom_tlmm_softc *sc, uint32_t pin, bool *enable)
464 {
465 uint32_t reg;
466
467 GPIO_LOCK_ASSERT(sc);
468
469 if (pin >= sc->gpio_npins)
470 return (EINVAL);
471
472 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
473 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL));
474
475 *enable = !! (reg & QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_VM_ENABLE);
476
477 return (0);
478 }
479
480 /*
481 * Enable/disable open drain.
482 */
483 int
qcom_tlmm_ipq4018_hw_pin_set_open_drain(struct qcom_tlmm_softc * sc,uint32_t pin,bool enable)484 qcom_tlmm_ipq4018_hw_pin_set_open_drain(
485 struct qcom_tlmm_softc *sc, uint32_t pin, bool enable)
486 {
487 uint32_t reg;
488
489 GPIO_LOCK_ASSERT(sc);
490
491 if (pin >= sc->gpio_npins)
492 return (EINVAL);
493
494 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
495 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL));
496
497 reg &= ~QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_OD_ENABLE;
498 if (enable)
499 reg |= QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_OD_ENABLE;
500
501 GPIO_WRITE(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
502 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL), reg);
503
504 return (0);
505 }
506
507 /*
508 * Get the open drain configuration bit.
509 */
510 int
qcom_tlmm_ipq4018_hw_pin_get_open_drain(struct qcom_tlmm_softc * sc,uint32_t pin,bool * enable)511 qcom_tlmm_ipq4018_hw_pin_get_open_drain(
512 struct qcom_tlmm_softc *sc, uint32_t pin, bool *enable)
513 {
514 uint32_t reg;
515
516 GPIO_LOCK_ASSERT(sc);
517
518 if (pin >= sc->gpio_npins)
519 return (EINVAL);
520
521 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
522 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL));
523
524 *enable = !! (reg & QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_OD_ENABLE);
525
526 return (0);
527 }
528