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