xref: /freebsd/sys/dev/qcom_tlmm/qcom_tlmm_ipq4018_hw.c (revision 59c8e88e72633afbc47a4ace0d2170d00d51f7dc)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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