]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/qcom_tlmm/qcom_tlmm_ipq4018_hw.c
unbound: Vendor import 1.19.1
[FreeBSD/FreeBSD.git] / sys / dev / qcom_tlmm / qcom_tlmm_ipq4018_hw.c
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 }