14abe6533SAdrian Chadd /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
34abe6533SAdrian Chadd *
44abe6533SAdrian Chadd * Copyright (c) 2021 Adrian Chadd <adrian@FreeBSD.org>
54abe6533SAdrian Chadd *
64abe6533SAdrian Chadd * Redistribution and use in source and binary forms, with or without
74abe6533SAdrian Chadd * modification, are permitted provided that the following conditions
84abe6533SAdrian Chadd * are met:
94abe6533SAdrian Chadd * 1. Redistributions of source code must retain the above copyright
104abe6533SAdrian Chadd * notice unmodified, this list of conditions, and the following
114abe6533SAdrian Chadd * disclaimer.
124abe6533SAdrian Chadd * 2. Redistributions in binary form must reproduce the above copyright
134abe6533SAdrian Chadd * notice, this list of conditions and the following disclaimer in the
144abe6533SAdrian Chadd * documentation and/or other materials provided with the distribution.
154abe6533SAdrian Chadd *
164abe6533SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
174abe6533SAdrian Chadd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
184abe6533SAdrian Chadd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
194abe6533SAdrian Chadd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
204abe6533SAdrian Chadd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
214abe6533SAdrian Chadd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
224abe6533SAdrian Chadd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
234abe6533SAdrian Chadd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
244abe6533SAdrian Chadd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
254abe6533SAdrian Chadd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
264abe6533SAdrian Chadd * SUCH DAMAGE.
274abe6533SAdrian Chadd */
284abe6533SAdrian Chadd
294abe6533SAdrian Chadd /*
304abe6533SAdrian Chadd * This is the shared pinmux code that the qualcomm SoCs use for their
314abe6533SAdrian Chadd * specific way of configuring up pins.
324abe6533SAdrian Chadd *
334abe6533SAdrian Chadd * For now this does use the IPQ4018 TLMM related softc, but that
344abe6533SAdrian Chadd * may change as I extend the driver to support multiple kinds of
354abe6533SAdrian Chadd * qualcomm chipsets in the future.
364abe6533SAdrian Chadd */
374abe6533SAdrian Chadd
384abe6533SAdrian Chadd #include <sys/param.h>
394abe6533SAdrian Chadd #include <sys/systm.h>
404abe6533SAdrian Chadd #include <sys/bus.h>
414abe6533SAdrian Chadd
424abe6533SAdrian Chadd #include <sys/kernel.h>
434abe6533SAdrian Chadd #include <sys/module.h>
444abe6533SAdrian Chadd #include <sys/rman.h>
454abe6533SAdrian Chadd #include <sys/lock.h>
464abe6533SAdrian Chadd #include <sys/malloc.h>
474abe6533SAdrian Chadd #include <sys/mutex.h>
484abe6533SAdrian Chadd #include <sys/gpio.h>
494abe6533SAdrian Chadd
504abe6533SAdrian Chadd #include <machine/bus.h>
514abe6533SAdrian Chadd #include <machine/resource.h>
524abe6533SAdrian Chadd #include <dev/gpio/gpiobusvar.h>
534abe6533SAdrian Chadd
544abe6533SAdrian Chadd #include <dev/fdt/fdt_common.h>
554abe6533SAdrian Chadd #include <dev/ofw/ofw_bus.h>
564abe6533SAdrian Chadd #include <dev/ofw/ofw_bus_subr.h>
574abe6533SAdrian Chadd
584abe6533SAdrian Chadd #include <dev/fdt/fdt_pinctrl.h>
594abe6533SAdrian Chadd
604abe6533SAdrian Chadd #include "qcom_tlmm_var.h"
614abe6533SAdrian Chadd #include "qcom_tlmm_debug.h"
624abe6533SAdrian Chadd
634abe6533SAdrian Chadd /*
644abe6533SAdrian Chadd * For now we're hard-coded to doing IPQ4018 stuff here, but
654abe6533SAdrian Chadd * it's not going to be very hard to flip it to being generic.
664abe6533SAdrian Chadd */
674abe6533SAdrian Chadd #include "qcom_tlmm_ipq4018_hw.h"
684abe6533SAdrian Chadd
694abe6533SAdrian Chadd #include "gpio_if.h"
704abe6533SAdrian Chadd
714abe6533SAdrian Chadd /* Parameters */
724abe6533SAdrian Chadd static const struct qcom_tlmm_prop_name prop_names[] = {
734abe6533SAdrian Chadd { "bias-disable", PIN_ID_BIAS_DISABLE, 0 },
744abe6533SAdrian Chadd { "bias-high-impedance", PIN_ID_BIAS_HIGH_IMPEDANCE, 0 },
754abe6533SAdrian Chadd { "bias-bus-hold", PIN_ID_BIAS_BUS_HOLD, 0 },
764abe6533SAdrian Chadd { "bias-pull-up", PIN_ID_BIAS_PULL_UP, 0 },
774abe6533SAdrian Chadd { "bias-pull-down", PIN_ID_BIAS_PULL_DOWN, 0 },
784abe6533SAdrian Chadd { "bias-pull-pin-default", PIN_ID_BIAS_PULL_PIN_DEFAULT, 0 },
794abe6533SAdrian Chadd { "drive-push-pull", PIN_ID_DRIVE_PUSH_PULL, 0 },
804abe6533SAdrian Chadd { "drive-open-drain", PIN_ID_DRIVE_OPEN_DRAIN, 0 },
814abe6533SAdrian Chadd { "drive-open-source", PIN_ID_DRIVE_OPEN_SOURCE, 0 },
824abe6533SAdrian Chadd { "drive-strength", PIN_ID_DRIVE_STRENGTH, 1 },
834abe6533SAdrian Chadd { "input-enable", PIN_ID_INPUT_ENABLE, 0 },
844abe6533SAdrian Chadd { "input-disable", PIN_ID_INPUT_DISABLE, 0 },
854abe6533SAdrian Chadd { "input-schmitt-enable", PIN_ID_INPUT_SCHMITT_ENABLE, 0 },
864abe6533SAdrian Chadd { "input-schmitt-disable", PIN_ID_INPUT_SCHMITT_DISABLE, 0 },
874abe6533SAdrian Chadd { "input-debounce", PIN_ID_INPUT_DEBOUNCE, 0 },
884abe6533SAdrian Chadd { "power-source", PIN_ID_POWER_SOURCE, 0 },
894abe6533SAdrian Chadd { "slew-rate", PIN_ID_SLEW_RATE, 0},
904abe6533SAdrian Chadd { "low-power-enable", PIN_ID_LOW_POWER_MODE_ENABLE, 0 },
914abe6533SAdrian Chadd { "low-power-disable", PIN_ID_LOW_POWER_MODE_DISABLE, 0 },
924abe6533SAdrian Chadd { "output-low", PIN_ID_OUTPUT_LOW, 0, },
934abe6533SAdrian Chadd { "output-high", PIN_ID_OUTPUT_HIGH, 0, },
944abe6533SAdrian Chadd { "vm-enable", PIN_ID_VM_ENABLE, 0, },
954abe6533SAdrian Chadd { "vm-disable", PIN_ID_VM_DISABLE, 0, },
964abe6533SAdrian Chadd };
974abe6533SAdrian Chadd
984abe6533SAdrian Chadd static const struct qcom_tlmm_spec_pin *
qcom_tlmm_pinctrl_search_spin(struct qcom_tlmm_softc * sc,char * pin_name)994abe6533SAdrian Chadd qcom_tlmm_pinctrl_search_spin(struct qcom_tlmm_softc *sc, char *pin_name)
1004abe6533SAdrian Chadd {
1014abe6533SAdrian Chadd int i;
1024abe6533SAdrian Chadd
1034abe6533SAdrian Chadd if (sc->spec_pins == NULL)
1044abe6533SAdrian Chadd return (NULL);
1054abe6533SAdrian Chadd
1064abe6533SAdrian Chadd for (i = 0; sc->spec_pins[i].name != NULL; i++) {
1074abe6533SAdrian Chadd if (strcmp(pin_name, sc->spec_pins[i].name) == 0)
1084abe6533SAdrian Chadd return (&sc->spec_pins[i]);
1094abe6533SAdrian Chadd }
1104abe6533SAdrian Chadd
1114abe6533SAdrian Chadd return (NULL);
1124abe6533SAdrian Chadd }
1134abe6533SAdrian Chadd
1144abe6533SAdrian Chadd static int
qcom_tlmm_pinctrl_config_spin(struct qcom_tlmm_softc * sc,char * pin_name,const struct qcom_tlmm_spec_pin * spin,struct qcom_tlmm_pinctrl_cfg * cfg)1154abe6533SAdrian Chadd qcom_tlmm_pinctrl_config_spin(struct qcom_tlmm_softc *sc,
1164abe6533SAdrian Chadd char *pin_name, const struct qcom_tlmm_spec_pin *spin,
1174abe6533SAdrian Chadd struct qcom_tlmm_pinctrl_cfg *cfg)
1184abe6533SAdrian Chadd {
1194abe6533SAdrian Chadd /* XXX TODO */
1204abe6533SAdrian Chadd device_printf(sc->dev, "%s: TODO: called; pin_name=%s\n",
1214abe6533SAdrian Chadd __func__, pin_name);
1224abe6533SAdrian Chadd return (0);
1234abe6533SAdrian Chadd }
1244abe6533SAdrian Chadd
1254abe6533SAdrian Chadd static const struct qcom_tlmm_gpio_mux *
qcom_tlmm_pinctrl_search_gmux(struct qcom_tlmm_softc * sc,char * pin_name)1264abe6533SAdrian Chadd qcom_tlmm_pinctrl_search_gmux(struct qcom_tlmm_softc *sc, char *pin_name)
1274abe6533SAdrian Chadd {
1284abe6533SAdrian Chadd int i;
1294abe6533SAdrian Chadd
1304abe6533SAdrian Chadd if (sc->gpio_muxes == NULL)
1314abe6533SAdrian Chadd return (NULL);
1324abe6533SAdrian Chadd
1334abe6533SAdrian Chadd for (i = 0; sc->gpio_muxes[i].id >= 0; i++) {
1344abe6533SAdrian Chadd if (strcmp(pin_name, sc->gpio_muxes[i].name) == 0)
1354abe6533SAdrian Chadd return (&sc->gpio_muxes[i]);
1364abe6533SAdrian Chadd }
1374abe6533SAdrian Chadd
1384abe6533SAdrian Chadd return (NULL);
1394abe6533SAdrian Chadd }
1404abe6533SAdrian Chadd
1414abe6533SAdrian Chadd static int
qcom_tlmm_pinctrl_gmux_function(const struct qcom_tlmm_gpio_mux * gmux,char * fnc_name)1424abe6533SAdrian Chadd qcom_tlmm_pinctrl_gmux_function(const struct qcom_tlmm_gpio_mux *gmux,
1434abe6533SAdrian Chadd char *fnc_name)
1444abe6533SAdrian Chadd {
1454abe6533SAdrian Chadd int i;
1464abe6533SAdrian Chadd
1474abe6533SAdrian Chadd for (i = 0; i < 16; i++) { /* XXX size */
1484abe6533SAdrian Chadd if ((gmux->functions[i] != NULL) &&
1494abe6533SAdrian Chadd (strcmp(fnc_name, gmux->functions[i]) == 0))
1504abe6533SAdrian Chadd return (i);
1514abe6533SAdrian Chadd }
1524abe6533SAdrian Chadd
1534abe6533SAdrian Chadd return (-1);
1544abe6533SAdrian Chadd }
1554abe6533SAdrian Chadd
1564abe6533SAdrian Chadd static int
qcom_tlmm_pinctrl_read_node(struct qcom_tlmm_softc * sc,phandle_t node,struct qcom_tlmm_pinctrl_cfg * cfg,char ** pins,int * lpins)1574abe6533SAdrian Chadd qcom_tlmm_pinctrl_read_node(struct qcom_tlmm_softc *sc,
1584abe6533SAdrian Chadd phandle_t node, struct qcom_tlmm_pinctrl_cfg *cfg, char **pins,
1594abe6533SAdrian Chadd int *lpins)
1604abe6533SAdrian Chadd {
1614abe6533SAdrian Chadd int rv, i;
1624abe6533SAdrian Chadd
1634abe6533SAdrian Chadd *lpins = OF_getprop_alloc(node, "pins", (void **)pins);
1644abe6533SAdrian Chadd if (*lpins <= 0)
1654abe6533SAdrian Chadd return (ENOENT);
1664abe6533SAdrian Chadd
1674abe6533SAdrian Chadd /* Read function (mux) settings. */
1684abe6533SAdrian Chadd rv = OF_getprop_alloc(node, "function", (void **)&cfg->function);
1694abe6533SAdrian Chadd if (rv <= 0)
1704abe6533SAdrian Chadd cfg->function = NULL;
1714abe6533SAdrian Chadd
1724abe6533SAdrian Chadd /*
1734abe6533SAdrian Chadd * Read the rest of the properties.
1744abe6533SAdrian Chadd *
1754abe6533SAdrian Chadd * Properties that are a flag are simply present with a value of 0.
1764abe6533SAdrian Chadd * Properties that have arguments have have_value set to 1, and
1774abe6533SAdrian Chadd * we will parse an argument out for it to use.
1784abe6533SAdrian Chadd *
1794abe6533SAdrian Chadd * Properties that were not found/parsed with have a value of -1
1804abe6533SAdrian Chadd * and thus we won't program them into the hardware.
1814abe6533SAdrian Chadd */
1824abe6533SAdrian Chadd for (i = 0; i < PROP_ID_MAX_ID; i++) {
1834abe6533SAdrian Chadd rv = OF_getencprop(node, prop_names[i].name, &cfg->params[i],
1844abe6533SAdrian Chadd sizeof(cfg->params[i]));
1854abe6533SAdrian Chadd if (prop_names[i].have_value) {
1864abe6533SAdrian Chadd if (rv == 0) {
1874abe6533SAdrian Chadd device_printf(sc->dev,
1884abe6533SAdrian Chadd "WARNING: Missing value for propety"
1894abe6533SAdrian Chadd " \"%s\"\n",
1904abe6533SAdrian Chadd prop_names[i].name);
1914abe6533SAdrian Chadd cfg->params[i] = 0;
1924abe6533SAdrian Chadd }
1934abe6533SAdrian Chadd } else {
1944abe6533SAdrian Chadd /* No value, default to 0 */
1954abe6533SAdrian Chadd cfg->params[i] = 0;
1964abe6533SAdrian Chadd }
1974abe6533SAdrian Chadd if (rv < 0)
1984abe6533SAdrian Chadd cfg->params[i] = -1;
1994abe6533SAdrian Chadd }
2004abe6533SAdrian Chadd return (0);
2014abe6533SAdrian Chadd }
2024abe6533SAdrian Chadd
2034abe6533SAdrian Chadd static int
qcom_tlmm_pinctrl_config_gmux(struct qcom_tlmm_softc * sc,char * pin_name,const struct qcom_tlmm_gpio_mux * gmux,struct qcom_tlmm_pinctrl_cfg * cfg)2044abe6533SAdrian Chadd qcom_tlmm_pinctrl_config_gmux(struct qcom_tlmm_softc *sc, char *pin_name,
2054abe6533SAdrian Chadd const struct qcom_tlmm_gpio_mux *gmux, struct qcom_tlmm_pinctrl_cfg *cfg)
2064abe6533SAdrian Chadd {
2074abe6533SAdrian Chadd int err = 0, i;
2084abe6533SAdrian Chadd
2094abe6533SAdrian Chadd QCOM_TLMM_DPRINTF(sc, QCOM_TLMM_DEBUG_PINMUX,
2104abe6533SAdrian Chadd "%s: called; pin=%s, function %s\n",
2114abe6533SAdrian Chadd __func__, pin_name, cfg->function);
2124abe6533SAdrian Chadd
2134abe6533SAdrian Chadd GPIO_LOCK(sc);
2144abe6533SAdrian Chadd
2154abe6533SAdrian Chadd /*
2164abe6533SAdrian Chadd * Lookup the function in the configuration table. Configure it
2174abe6533SAdrian Chadd * if required.
2184abe6533SAdrian Chadd */
2194abe6533SAdrian Chadd if (cfg->function != NULL) {
2204abe6533SAdrian Chadd uint32_t tmp;
2214abe6533SAdrian Chadd
2224abe6533SAdrian Chadd tmp = qcom_tlmm_pinctrl_gmux_function(gmux, cfg->function);
2234abe6533SAdrian Chadd if (tmp == -1) {
2244abe6533SAdrian Chadd device_printf(sc->dev,
2254abe6533SAdrian Chadd "%s: pin=%s, function=%s, unknown!\n",
2264abe6533SAdrian Chadd __func__,
2274abe6533SAdrian Chadd pin_name,
2284abe6533SAdrian Chadd cfg->function);
2294abe6533SAdrian Chadd err = EINVAL;
2304abe6533SAdrian Chadd goto done;
2314abe6533SAdrian Chadd }
2324abe6533SAdrian Chadd
2334abe6533SAdrian Chadd /*
2344abe6533SAdrian Chadd * Program in the given function to the given pin.
2354abe6533SAdrian Chadd */
2364abe6533SAdrian Chadd QCOM_TLMM_DPRINTF(sc, QCOM_TLMM_DEBUG_PINMUX,
2374abe6533SAdrian Chadd "%s: pin id=%u, new function=%u\n",
2384abe6533SAdrian Chadd __func__,
2394abe6533SAdrian Chadd gmux->id,
2404abe6533SAdrian Chadd tmp);
2414abe6533SAdrian Chadd err = qcom_tlmm_ipq4018_hw_pin_set_function(sc, gmux->id,
2424abe6533SAdrian Chadd tmp);
2434abe6533SAdrian Chadd if (err != 0) {
2444abe6533SAdrian Chadd device_printf(sc->dev,
2454abe6533SAdrian Chadd "%s: pin=%d: failed to set function (%d)\n",
2464abe6533SAdrian Chadd __func__, gmux->id, err);
2474abe6533SAdrian Chadd goto done;
2484abe6533SAdrian Chadd }
2494abe6533SAdrian Chadd }
2504abe6533SAdrian Chadd
2514abe6533SAdrian Chadd /*
2524abe6533SAdrian Chadd * Iterate the set of properties; call the relevant method
2534abe6533SAdrian Chadd * if we need to change it.
2544abe6533SAdrian Chadd */
2554abe6533SAdrian Chadd for (i = 0; i < PROP_ID_MAX_ID; i++) {
2564abe6533SAdrian Chadd if (cfg->params[i] == -1)
2574abe6533SAdrian Chadd continue;
2584abe6533SAdrian Chadd QCOM_TLMM_DPRINTF(sc, QCOM_TLMM_DEBUG_PINMUX,
2594abe6533SAdrian Chadd "%s: pin_id=%u, param=%d, val=%d\n",
2604abe6533SAdrian Chadd __func__,
2614abe6533SAdrian Chadd gmux->id,
2624abe6533SAdrian Chadd i,
2634abe6533SAdrian Chadd cfg->params[i]);
2644abe6533SAdrian Chadd switch (i) {
2654abe6533SAdrian Chadd case PIN_ID_BIAS_DISABLE:
2664abe6533SAdrian Chadd err = qcom_tlmm_ipq4018_hw_pin_set_pupd_config(sc,
2674abe6533SAdrian Chadd gmux->id, QCOM_TLMM_PIN_PUPD_CONFIG_DISABLE);
2684abe6533SAdrian Chadd if (err != 0) {
2694abe6533SAdrian Chadd device_printf(sc->dev,
2704abe6533SAdrian Chadd "%s: pin=%d: failed to set pupd(DISABLE):"
2714abe6533SAdrian Chadd " %d\n",
2724abe6533SAdrian Chadd __func__, gmux->id, err);
2734abe6533SAdrian Chadd goto done;
2744abe6533SAdrian Chadd }
2754abe6533SAdrian Chadd break;
2764abe6533SAdrian Chadd case PIN_ID_BIAS_PULL_DOWN:
2774abe6533SAdrian Chadd err = qcom_tlmm_ipq4018_hw_pin_set_pupd_config(sc,
2784abe6533SAdrian Chadd gmux->id, QCOM_TLMM_PIN_PUPD_CONFIG_PULL_DOWN);
2794abe6533SAdrian Chadd if (err != 0) {
2804abe6533SAdrian Chadd device_printf(sc->dev,
2814abe6533SAdrian Chadd "%s: pin=%d: failed to set pupd(PD):"
2824abe6533SAdrian Chadd " %d\n",
2834abe6533SAdrian Chadd __func__, gmux->id, err);
2844abe6533SAdrian Chadd goto done;
2854abe6533SAdrian Chadd }
2864abe6533SAdrian Chadd break;
2874abe6533SAdrian Chadd case PIN_ID_BIAS_BUS_HOLD:
2884abe6533SAdrian Chadd err = qcom_tlmm_ipq4018_hw_pin_set_pupd_config(sc,
2894abe6533SAdrian Chadd gmux->id, QCOM_TLMM_PIN_PUPD_CONFIG_BUS_HOLD);
2904abe6533SAdrian Chadd if (err != 0) {
2914abe6533SAdrian Chadd device_printf(sc->dev,
2924abe6533SAdrian Chadd "%s: pin=%d: failed to set pupd(HOLD):"
2934abe6533SAdrian Chadd " %d\n",
2944abe6533SAdrian Chadd __func__, gmux->id, err);
2954abe6533SAdrian Chadd goto done;
2964abe6533SAdrian Chadd }
2974abe6533SAdrian Chadd break;
2984abe6533SAdrian Chadd
2994abe6533SAdrian Chadd case PIN_ID_BIAS_PULL_UP:
3004abe6533SAdrian Chadd err = qcom_tlmm_ipq4018_hw_pin_set_pupd_config(sc,
3014abe6533SAdrian Chadd gmux->id, QCOM_TLMM_PIN_PUPD_CONFIG_PULL_UP);
3024abe6533SAdrian Chadd if (err != 0) {
3034abe6533SAdrian Chadd device_printf(sc->dev,
3044abe6533SAdrian Chadd "%s: pin=%d: failed to set pupd(PU):"
3054abe6533SAdrian Chadd " %d\n",
3064abe6533SAdrian Chadd __func__, gmux->id, err);
3074abe6533SAdrian Chadd goto done;
3084abe6533SAdrian Chadd }
3094abe6533SAdrian Chadd break;
3104abe6533SAdrian Chadd case PIN_ID_OUTPUT_LOW:
3114abe6533SAdrian Chadd err = qcom_tlmm_ipq4018_hw_pin_set_oe_output(sc,
3124abe6533SAdrian Chadd gmux->id);
3134abe6533SAdrian Chadd if (err != 0) {
3144abe6533SAdrian Chadd device_printf(sc->dev,
3154abe6533SAdrian Chadd "%s: pin=%d: failed to set OE:"
3164abe6533SAdrian Chadd " %d\n",
3174abe6533SAdrian Chadd __func__, gmux->id, err);
3184abe6533SAdrian Chadd goto done;
3194abe6533SAdrian Chadd }
3204abe6533SAdrian Chadd err = qcom_tlmm_ipq4018_hw_pin_set_output_value(
3214abe6533SAdrian Chadd sc, gmux->id, 0);
3224abe6533SAdrian Chadd if (err != 0) {
3234abe6533SAdrian Chadd device_printf(sc->dev,
3244abe6533SAdrian Chadd "%s: pin=%d: failed to set output value:"
3254abe6533SAdrian Chadd " %d\n",
3264abe6533SAdrian Chadd __func__, gmux->id, err);
3274abe6533SAdrian Chadd goto done;
3284abe6533SAdrian Chadd }
3294abe6533SAdrian Chadd break;
3304abe6533SAdrian Chadd case PIN_ID_OUTPUT_HIGH:
3314abe6533SAdrian Chadd err = qcom_tlmm_ipq4018_hw_pin_set_oe_output(sc,
3324abe6533SAdrian Chadd gmux->id);
3334abe6533SAdrian Chadd if (err != 0) {
3344abe6533SAdrian Chadd device_printf(sc->dev,
3354abe6533SAdrian Chadd "%s: pin=%d: failed to set OE:"
3364abe6533SAdrian Chadd " %d\n",
3374abe6533SAdrian Chadd __func__, gmux->id, err);
3384abe6533SAdrian Chadd goto done;
3394abe6533SAdrian Chadd }
3404abe6533SAdrian Chadd err = qcom_tlmm_ipq4018_hw_pin_set_output_value(
3414abe6533SAdrian Chadd sc, gmux->id, 1);
3424abe6533SAdrian Chadd if (err != 0) {
3434abe6533SAdrian Chadd device_printf(sc->dev,
3444abe6533SAdrian Chadd "%s: pin=%d: failed to set output value:"
3454abe6533SAdrian Chadd " %d\n",
3464abe6533SAdrian Chadd __func__, gmux->id, err);
3474abe6533SAdrian Chadd goto done;
3484abe6533SAdrian Chadd }
3494abe6533SAdrian Chadd break;
3504abe6533SAdrian Chadd case PIN_ID_DRIVE_STRENGTH:
3514abe6533SAdrian Chadd err = qcom_tlmm_ipq4018_hw_pin_set_drive_strength(sc,
3524abe6533SAdrian Chadd gmux->id, cfg->params[i]);
3534abe6533SAdrian Chadd if (err != 0) {
3544abe6533SAdrian Chadd device_printf(sc->dev,
3554abe6533SAdrian Chadd "%s: pin=%d: failed to set drive"
3564abe6533SAdrian Chadd " strength %d (%d)\n",
3574abe6533SAdrian Chadd __func__, gmux->id,
3584abe6533SAdrian Chadd cfg->params[i], err);
3594abe6533SAdrian Chadd goto done;
3604abe6533SAdrian Chadd }
3614abe6533SAdrian Chadd break;
3624abe6533SAdrian Chadd case PIN_ID_VM_ENABLE:
3634abe6533SAdrian Chadd err = qcom_tlmm_ipq4018_hw_pin_set_vm(sc,
3644abe6533SAdrian Chadd gmux->id, true);
3654abe6533SAdrian Chadd if (err != 0) {
3664abe6533SAdrian Chadd device_printf(sc->dev,
3674abe6533SAdrian Chadd "%s: pin=%d: failed to set VM enable:"
3684abe6533SAdrian Chadd " %d\n",
3694abe6533SAdrian Chadd __func__, gmux->id, err);
3704abe6533SAdrian Chadd goto done;
3714abe6533SAdrian Chadd }
3724abe6533SAdrian Chadd break;
3734abe6533SAdrian Chadd case PIN_ID_VM_DISABLE:
3744abe6533SAdrian Chadd err = qcom_tlmm_ipq4018_hw_pin_set_vm(sc,
3754abe6533SAdrian Chadd gmux->id, false);
3764abe6533SAdrian Chadd if (err != 0) {
3774abe6533SAdrian Chadd device_printf(sc->dev,
3784abe6533SAdrian Chadd "%s: pin=%d: failed to set VM disable:"
3794abe6533SAdrian Chadd " %d\n",
3804abe6533SAdrian Chadd __func__, gmux->id, err);
3814abe6533SAdrian Chadd goto done;
3824abe6533SAdrian Chadd }
3834abe6533SAdrian Chadd break;
3844abe6533SAdrian Chadd case PIN_ID_DRIVE_OPEN_DRAIN:
3854abe6533SAdrian Chadd err = qcom_tlmm_ipq4018_hw_pin_set_open_drain(sc,
3864abe6533SAdrian Chadd gmux->id, true);
3874abe6533SAdrian Chadd if (err != 0) {
3884abe6533SAdrian Chadd device_printf(sc->dev,
3894abe6533SAdrian Chadd "%s: pin=%d: failed to set open drain"
3904abe6533SAdrian Chadd " (%d)\n",
3914abe6533SAdrian Chadd __func__, gmux->id, err);
3924abe6533SAdrian Chadd goto done;
3934abe6533SAdrian Chadd }
3944abe6533SAdrian Chadd break;
3954abe6533SAdrian Chadd case PIN_ID_INPUT_ENABLE:
3964abe6533SAdrian Chadd /* Configure pin as an input */
3974abe6533SAdrian Chadd err = qcom_tlmm_ipq4018_hw_pin_set_oe_input(sc,
3984abe6533SAdrian Chadd gmux->id);
3994abe6533SAdrian Chadd if (err != 0) {
4004abe6533SAdrian Chadd device_printf(sc->dev,
4014abe6533SAdrian Chadd "%s: pin=%d: failed to set pin as input"
4024abe6533SAdrian Chadd " (%d)\n",
4034abe6533SAdrian Chadd __func__, gmux->id, err);
4044abe6533SAdrian Chadd goto done;
4054abe6533SAdrian Chadd }
4064abe6533SAdrian Chadd break;
4074abe6533SAdrian Chadd case PIN_ID_INPUT_DISABLE:
4084abe6533SAdrian Chadd /*
4094abe6533SAdrian Chadd * the linux-msm GPIO driver treats this as an error;
4104abe6533SAdrian Chadd * a pin should be configured as an output instead.
4114abe6533SAdrian Chadd */
4124abe6533SAdrian Chadd err = ENXIO;
4134abe6533SAdrian Chadd goto done;
4144abe6533SAdrian Chadd break;
4154abe6533SAdrian Chadd case PIN_ID_BIAS_HIGH_IMPEDANCE:
4164abe6533SAdrian Chadd case PIN_ID_INPUT_SCHMITT_ENABLE:
4174abe6533SAdrian Chadd case PIN_ID_INPUT_SCHMITT_DISABLE:
4184abe6533SAdrian Chadd case PIN_ID_INPUT_DEBOUNCE:
4194abe6533SAdrian Chadd case PIN_ID_SLEW_RATE:
4204abe6533SAdrian Chadd case PIN_ID_LOW_POWER_MODE_ENABLE:
4214abe6533SAdrian Chadd case PIN_ID_LOW_POWER_MODE_DISABLE:
4224abe6533SAdrian Chadd case PIN_ID_BIAS_PULL_PIN_DEFAULT:
4234abe6533SAdrian Chadd case PIN_ID_DRIVE_PUSH_PULL:
4244abe6533SAdrian Chadd case PIN_ID_DRIVE_OPEN_SOURCE:
4254abe6533SAdrian Chadd case PIN_ID_POWER_SOURCE:
4264abe6533SAdrian Chadd default:
4274abe6533SAdrian Chadd device_printf(sc->dev,
4284abe6533SAdrian Chadd "%s: ERROR: unknown/unsupported param: "
4294abe6533SAdrian Chadd " pin_id=%u, param=%d, val=%d\n",
4304abe6533SAdrian Chadd __func__,
4314abe6533SAdrian Chadd gmux->id,
4324abe6533SAdrian Chadd i,
4334abe6533SAdrian Chadd cfg->params[i]);
4344abe6533SAdrian Chadd err = ENXIO;
4354abe6533SAdrian Chadd goto done;
4364abe6533SAdrian Chadd
4374abe6533SAdrian Chadd }
4384abe6533SAdrian Chadd }
4394abe6533SAdrian Chadd done:
4404abe6533SAdrian Chadd GPIO_UNLOCK(sc);
4414abe6533SAdrian Chadd return (0);
4424abe6533SAdrian Chadd }
4434abe6533SAdrian Chadd
4444abe6533SAdrian Chadd
4454abe6533SAdrian Chadd static int
qcom_tlmm_pinctrl_config_node(struct qcom_tlmm_softc * sc,char * pin_name,struct qcom_tlmm_pinctrl_cfg * cfg)4464abe6533SAdrian Chadd qcom_tlmm_pinctrl_config_node(struct qcom_tlmm_softc *sc,
4474abe6533SAdrian Chadd char *pin_name, struct qcom_tlmm_pinctrl_cfg *cfg)
4484abe6533SAdrian Chadd {
4494abe6533SAdrian Chadd const struct qcom_tlmm_gpio_mux *gmux;
4504abe6533SAdrian Chadd const struct qcom_tlmm_spec_pin *spin;
4514abe6533SAdrian Chadd int rv;
4524abe6533SAdrian Chadd
4534abe6533SAdrian Chadd /* Handle GPIO pins */
4544abe6533SAdrian Chadd gmux = qcom_tlmm_pinctrl_search_gmux(sc, pin_name);
4554abe6533SAdrian Chadd
4564abe6533SAdrian Chadd if (gmux != NULL) {
4574abe6533SAdrian Chadd rv = qcom_tlmm_pinctrl_config_gmux(sc, pin_name, gmux, cfg);
4584abe6533SAdrian Chadd return (rv);
4594abe6533SAdrian Chadd }
4604abe6533SAdrian Chadd /* Handle special pin groups */
4614abe6533SAdrian Chadd spin = qcom_tlmm_pinctrl_search_spin(sc, pin_name);
4624abe6533SAdrian Chadd if (spin != NULL) {
4634abe6533SAdrian Chadd rv = qcom_tlmm_pinctrl_config_spin(sc, pin_name, spin, cfg);
4644abe6533SAdrian Chadd return (rv);
4654abe6533SAdrian Chadd }
4664abe6533SAdrian Chadd device_printf(sc->dev, "Unknown pin: %s\n", pin_name);
4674abe6533SAdrian Chadd return (ENXIO);
4684abe6533SAdrian Chadd }
4694abe6533SAdrian Chadd
4704abe6533SAdrian Chadd static int
qcom_tlmm_pinctrl_process_node(struct qcom_tlmm_softc * sc,phandle_t node)4714abe6533SAdrian Chadd qcom_tlmm_pinctrl_process_node(struct qcom_tlmm_softc *sc,
4724abe6533SAdrian Chadd phandle_t node)
4734abe6533SAdrian Chadd {
4744abe6533SAdrian Chadd struct qcom_tlmm_pinctrl_cfg cfg;
4754abe6533SAdrian Chadd char *pins, *pname;
4764abe6533SAdrian Chadd int i, len, lpins, rv;
4774abe6533SAdrian Chadd
4784abe6533SAdrian Chadd /*
4794abe6533SAdrian Chadd * Read the configuration and list of pins for the given node to
4804abe6533SAdrian Chadd * configure.
4814abe6533SAdrian Chadd */
4824abe6533SAdrian Chadd rv = qcom_tlmm_pinctrl_read_node(sc, node, &cfg, &pins, &lpins);
4834abe6533SAdrian Chadd if (rv != 0)
4844abe6533SAdrian Chadd return (rv);
4854abe6533SAdrian Chadd
4864abe6533SAdrian Chadd len = 0;
4874abe6533SAdrian Chadd pname = pins;
4884abe6533SAdrian Chadd do {
4894abe6533SAdrian Chadd i = strlen(pname) + 1;
4904abe6533SAdrian Chadd /*
4914abe6533SAdrian Chadd * Configure the given node with the specific configuration.
4924abe6533SAdrian Chadd */
4934abe6533SAdrian Chadd rv = qcom_tlmm_pinctrl_config_node(sc, pname, &cfg);
4944abe6533SAdrian Chadd if (rv != 0)
4954abe6533SAdrian Chadd device_printf(sc->dev,
4964abe6533SAdrian Chadd "Cannot configure pin: %s: %d\n", pname, rv);
4974abe6533SAdrian Chadd
4984abe6533SAdrian Chadd len += i;
4994abe6533SAdrian Chadd pname += i;
5004abe6533SAdrian Chadd } while (len < lpins);
5014abe6533SAdrian Chadd
5024abe6533SAdrian Chadd if (pins != NULL)
5034abe6533SAdrian Chadd free(pins, M_OFWPROP);
5044abe6533SAdrian Chadd if (cfg.function != NULL)
5054abe6533SAdrian Chadd free(cfg.function, M_OFWPROP);
5064abe6533SAdrian Chadd
5074abe6533SAdrian Chadd return (rv);
5084abe6533SAdrian Chadd }
5094abe6533SAdrian Chadd
5104abe6533SAdrian Chadd int
qcom_tlmm_pinctrl_configure(device_t dev,phandle_t cfgxref)5114abe6533SAdrian Chadd qcom_tlmm_pinctrl_configure(device_t dev, phandle_t cfgxref)
5124abe6533SAdrian Chadd {
5134abe6533SAdrian Chadd struct qcom_tlmm_softc *sc;
5144abe6533SAdrian Chadd phandle_t node, cfgnode;
5154abe6533SAdrian Chadd int rv;
5164abe6533SAdrian Chadd
5174abe6533SAdrian Chadd sc = device_get_softc(dev);
5184abe6533SAdrian Chadd cfgnode = OF_node_from_xref(cfgxref);
5194abe6533SAdrian Chadd
5204abe6533SAdrian Chadd for (node = OF_child(cfgnode); node != 0; node = OF_peer(node)) {
5214abe6533SAdrian Chadd if (!ofw_bus_node_status_okay(node))
5224abe6533SAdrian Chadd continue;
5234abe6533SAdrian Chadd rv = qcom_tlmm_pinctrl_process_node(sc, node);
5244abe6533SAdrian Chadd if (rv != 0)
5254abe6533SAdrian Chadd device_printf(dev, "Pin config failed: %d\n", rv);
5264abe6533SAdrian Chadd }
5274abe6533SAdrian Chadd
5284abe6533SAdrian Chadd return (0);
5294abe6533SAdrian Chadd }
5304abe6533SAdrian Chadd
531