2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2021 Adrian Chadd <adrian@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice unmodified, this list of conditions, and the following
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.
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
30 * This is a pinmux/gpio controller for the IPQ4018/IPQ4019.
33 #include <sys/cdefs.h>
34 #include <sys/param.h>
35 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/module.h>
42 #include <sys/malloc.h>
43 #include <sys/mutex.h>
46 #include <machine/bus.h>
47 #include <machine/resource.h>
48 #include <dev/gpio/gpiobusvar.h>
50 #include <dev/fdt/fdt_common.h>
51 #include <dev/ofw/ofw_bus.h>
52 #include <dev/ofw/ofw_bus_subr.h>
54 #include <dev/fdt/fdt_pinctrl.h>
56 #include "qcom_tlmm_var.h"
58 #include "qcom_tlmm_ipq4018_reg.h"
59 #include "qcom_tlmm_ipq4018_hw.h"
64 * Set the pin function. This is a hardware and pin specific mapping.
66 * Returns 0 if OK, an errno if an error was encountered.
69 qcom_tlmm_ipq4018_hw_pin_set_function(struct qcom_tlmm_softc *sc,
70 int pin, int function)
76 if (pin >= sc->gpio_npins)
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);
92 * Get the pin function. This is a hardware and pin specific mapping.
94 * Returns 0 if OK, an errno if a nerror was encountered.
97 qcom_tlmm_ipq4018_hw_pin_get_function(struct qcom_tlmm_softc *sc,
98 int pin, int *function)
102 GPIO_LOCK_ASSERT(sc);
104 if (pin >= sc->gpio_npins)
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;
118 * Set the OE bit to be output. This assumes the port is configured
122 qcom_tlmm_ipq4018_hw_pin_set_oe_output(struct qcom_tlmm_softc *sc,
127 GPIO_LOCK_ASSERT(sc);
129 if (pin >= sc->gpio_npins)
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;
136 QCOM_TLMM_IPQ4018_REG_PIN(pin, QCOM_TLMM_IPQ4018_REG_PIN_CONTROL),
143 * Set the OE bit to be input. This assumes the port is configured
147 qcom_tlmm_ipq4018_hw_pin_set_oe_input(struct qcom_tlmm_softc *sc,
152 GPIO_LOCK_ASSERT(sc);
154 if (pin >= sc->gpio_npins)
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);
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.
171 qcom_tlmm_ipq4018_hw_pin_get_oe_state(struct qcom_tlmm_softc *sc,
172 int pin, bool *is_output)
176 GPIO_LOCK_ASSERT(sc);
178 if (pin >= sc->gpio_npins)
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);
190 * Set the given GPIO pin to the given value.
193 qcom_tlmm_ipq4018_hw_pin_set_output_value(struct qcom_tlmm_softc *sc,
194 uint32_t pin, unsigned int value)
198 GPIO_LOCK_ASSERT(sc);
200 if (pin >= sc->gpio_npins)
203 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
204 QCOM_TLMM_IPQ4018_REG_PIN_IO));
206 reg |= QCOM_TLMM_IPQ4018_REG_PIN_IO_OUTPUT_EN;
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);
216 * Get the input state of the current GPIO pin.
219 qcom_tlmm_ipq4018_hw_pin_get_output_value(struct qcom_tlmm_softc *sc,
220 uint32_t pin, unsigned int *val)
224 GPIO_LOCK_ASSERT(sc);
226 if (pin >= sc->gpio_npins)
229 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
230 QCOM_TLMM_IPQ4018_REG_PIN_IO));
232 *val = !! (reg & QCOM_TLMM_IPQ4018_REG_PIN_IO_INPUT_STATUS);
239 * Get the input state of the current GPIO pin.
242 qcom_tlmm_ipq4018_hw_pin_get_input_value(struct qcom_tlmm_softc *sc,
243 uint32_t pin, unsigned int *val)
247 GPIO_LOCK_ASSERT(sc);
249 if (pin >= sc->gpio_npins)
252 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
253 QCOM_TLMM_IPQ4018_REG_PIN_IO));
255 *val = !! (reg & QCOM_TLMM_IPQ4018_REG_PIN_IO_INPUT_STATUS);
261 * Toggle the current output pin value.
264 qcom_tlmm_ipq4018_hw_pin_toggle_output_value(
265 struct qcom_tlmm_softc *sc, uint32_t pin)
269 GPIO_LOCK_ASSERT(sc);
271 if (pin >= sc->gpio_npins)
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;
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);
287 * Configure the pull-up / pull-down top-level configuration.
289 * This doesn't configure the resistor values, just what's enabled/disabled.
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)
298 GPIO_LOCK_ASSERT(sc);
300 if (pin >= sc->gpio_npins)
303 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
304 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL));
306 reg &= ~(QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_MASK
307 << QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_SHIFT);
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;
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;
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;
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;
328 GPIO_WRITE(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
329 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL), reg);
335 * Fetch the current pull-up / pull-down configuration.
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)
344 GPIO_LOCK_ASSERT(sc);
346 if (pin >= sc->gpio_npins)
349 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
350 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL));
352 reg >>= QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_SHIFT;
353 reg &= QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_MASK;
356 case QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_DISABLE:
357 *pupd = QCOM_TLMM_PIN_PUPD_CONFIG_DISABLE;
359 case QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_PULLDOWN:
360 *pupd = QCOM_TLMM_PIN_PUPD_CONFIG_PULL_DOWN;
362 case QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_PUPD_PULLUP:
363 *pupd = QCOM_TLMM_PIN_PUPD_CONFIG_PULL_UP;
366 *pupd = QCOM_TLMM_PIN_PUPD_CONFIG_DISABLE;
374 * Set the drive strength in mA.
377 qcom_tlmm_ipq4018_hw_pin_set_drive_strength(
378 struct qcom_tlmm_softc *sc, uint32_t pin, uint8_t drv)
382 GPIO_LOCK_ASSERT(sc);
384 if (pin >= sc->gpio_npins)
387 /* Convert mA to hardware */
388 if (drv > 16 || drv < 2)
392 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
393 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL));
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;
400 GPIO_WRITE(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
401 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL), reg);
407 * Get the drive strength in mA.
410 qcom_tlmm_ipq4018_hw_pin_get_drive_strength(
411 struct qcom_tlmm_softc *sc, uint32_t pin, uint8_t *drv)
415 GPIO_LOCK_ASSERT(sc);
417 if (pin >= sc->gpio_npins)
420 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
421 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL));
423 *drv = (reg >> QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_DRIVE_STRENGTH_SHIFT)
424 & QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_DRIVE_STRENGTH_MASK;
426 *drv = (*drv + 1) * 2;
433 * Enable/disable whether this pin is passed through to a VM.
436 qcom_tlmm_ipq4018_hw_pin_set_vm(
437 struct qcom_tlmm_softc *sc, uint32_t pin, bool enable)
441 GPIO_LOCK_ASSERT(sc);
443 if (pin >= sc->gpio_npins)
446 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
447 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL));
449 reg &= ~QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_VM_ENABLE;
451 reg |= QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_VM_ENABLE;
453 GPIO_WRITE(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
454 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL), reg);
460 * Get the VM configuration bit.
463 qcom_tlmm_ipq4018_hw_pin_get_vm(
464 struct qcom_tlmm_softc *sc, uint32_t pin, bool *enable)
468 GPIO_LOCK_ASSERT(sc);
470 if (pin >= sc->gpio_npins)
473 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
474 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL));
476 *enable = !! (reg & QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_VM_ENABLE);
482 * Enable/disable open drain.
485 qcom_tlmm_ipq4018_hw_pin_set_open_drain(
486 struct qcom_tlmm_softc *sc, uint32_t pin, bool enable)
490 GPIO_LOCK_ASSERT(sc);
492 if (pin >= sc->gpio_npins)
495 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
496 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL));
498 reg &= ~QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_OD_ENABLE;
500 reg |= QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_OD_ENABLE;
502 GPIO_WRITE(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
503 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL), reg);
509 * Get the open drain configuration bit.
512 qcom_tlmm_ipq4018_hw_pin_get_open_drain(
513 struct qcom_tlmm_softc *sc, uint32_t pin, bool *enable)
517 GPIO_LOCK_ASSERT(sc);
519 if (pin >= sc->gpio_npins)
522 reg = GPIO_READ(sc, QCOM_TLMM_IPQ4018_REG_PIN(pin,
523 QCOM_TLMM_IPQ4018_REG_PIN_CONTROL));
525 *enable = !! (reg & QCOM_TLMM_IPQ4018_REG_PIN_CONTROL_OD_ENABLE);