]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/qcom_tlmm/qcom_tlmm_ipq4018_hw.c
unbound: Import upstream 0ee44ef3 when ENOBUFS is returned
[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/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 }