The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/qcom_tlmm/qcom_tlmm_pinmux.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    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 the shared pinmux code that the qualcomm SoCs use for their
   31  * specific way of configuring up pins.
   32  *
   33  * For now this does use the IPQ4018 TLMM related softc, but that
   34  * may change as I extend the driver to support multiple kinds of
   35  * qualcomm chipsets in the future.
   36  */
   37 
   38 #include <sys/cdefs.h>
   39 __FBSDID("$FreeBSD$");
   40 
   41 #include <sys/param.h>
   42 #include <sys/systm.h>
   43 #include <sys/bus.h>
   44 
   45 #include <sys/kernel.h>
   46 #include <sys/module.h>
   47 #include <sys/rman.h>
   48 #include <sys/lock.h>
   49 #include <sys/malloc.h>
   50 #include <sys/mutex.h>
   51 #include <sys/gpio.h>
   52 
   53 #include <machine/bus.h>
   54 #include <machine/resource.h>
   55 #include <dev/gpio/gpiobusvar.h>
   56 
   57 #include <dev/fdt/fdt_common.h>
   58 #include <dev/ofw/ofw_bus.h>
   59 #include <dev/ofw/ofw_bus_subr.h>
   60 
   61 #include <dev/fdt/fdt_pinctrl.h>
   62 
   63 #include "qcom_tlmm_var.h"
   64 #include "qcom_tlmm_debug.h"
   65 
   66 /*
   67  * For now we're hard-coded to doing IPQ4018 stuff here, but
   68  * it's not going to be very hard to flip it to being generic.
   69  */
   70 #include "qcom_tlmm_ipq4018_hw.h"
   71 
   72 #include "gpio_if.h"
   73 
   74 /* Parameters */
   75 static const struct qcom_tlmm_prop_name prop_names[] = {
   76         { "bias-disable", PIN_ID_BIAS_DISABLE, 0 },
   77         { "bias-high-impedance", PIN_ID_BIAS_HIGH_IMPEDANCE, 0 },
   78         { "bias-bus-hold", PIN_ID_BIAS_BUS_HOLD, 0 },
   79         { "bias-pull-up", PIN_ID_BIAS_PULL_UP, 0 },
   80         { "bias-pull-down", PIN_ID_BIAS_PULL_DOWN, 0 },
   81         { "bias-pull-pin-default", PIN_ID_BIAS_PULL_PIN_DEFAULT, 0 },
   82         { "drive-push-pull", PIN_ID_DRIVE_PUSH_PULL, 0 },
   83         { "drive-open-drain", PIN_ID_DRIVE_OPEN_DRAIN, 0 },
   84         { "drive-open-source", PIN_ID_DRIVE_OPEN_SOURCE, 0 },
   85         { "drive-strength", PIN_ID_DRIVE_STRENGTH, 1 },
   86         { "input-enable", PIN_ID_INPUT_ENABLE, 0 },
   87         { "input-disable", PIN_ID_INPUT_DISABLE, 0 },
   88         { "input-schmitt-enable", PIN_ID_INPUT_SCHMITT_ENABLE, 0 },
   89         { "input-schmitt-disable", PIN_ID_INPUT_SCHMITT_DISABLE, 0 },
   90         { "input-debounce", PIN_ID_INPUT_DEBOUNCE, 0 },
   91         { "power-source", PIN_ID_POWER_SOURCE, 0 },
   92         { "slew-rate", PIN_ID_SLEW_RATE, 0},
   93         { "low-power-enable", PIN_ID_LOW_POWER_MODE_ENABLE, 0 },
   94         { "low-power-disable", PIN_ID_LOW_POWER_MODE_DISABLE, 0 },
   95         { "output-low", PIN_ID_OUTPUT_LOW, 0, },
   96         { "output-high", PIN_ID_OUTPUT_HIGH, 0, },
   97         { "vm-enable", PIN_ID_VM_ENABLE, 0, },
   98         { "vm-disable", PIN_ID_VM_DISABLE, 0, },
   99 };
  100 
  101 static const struct qcom_tlmm_spec_pin *
  102 qcom_tlmm_pinctrl_search_spin(struct qcom_tlmm_softc *sc, char *pin_name)
  103 {
  104         int i;
  105 
  106         if (sc->spec_pins == NULL)
  107                 return (NULL);
  108 
  109         for (i = 0; sc->spec_pins[i].name != NULL; i++) {
  110                 if (strcmp(pin_name, sc->spec_pins[i].name) == 0)
  111                         return (&sc->spec_pins[i]);
  112         }
  113 
  114         return (NULL);
  115 }
  116 
  117 static int
  118 qcom_tlmm_pinctrl_config_spin(struct qcom_tlmm_softc *sc,
  119      char *pin_name, const struct qcom_tlmm_spec_pin *spin,
  120     struct qcom_tlmm_pinctrl_cfg *cfg)
  121 {
  122         /* XXX TODO */
  123         device_printf(sc->dev, "%s: TODO: called; pin_name=%s\n",
  124              __func__, pin_name);
  125         return (0);
  126 }
  127 
  128 static const struct qcom_tlmm_gpio_mux *
  129 qcom_tlmm_pinctrl_search_gmux(struct qcom_tlmm_softc *sc, char *pin_name)
  130 {
  131         int i;
  132 
  133         if (sc->gpio_muxes == NULL)
  134                 return (NULL);
  135 
  136         for (i = 0; sc->gpio_muxes[i].id >= 0; i++) {
  137                 if (strcmp(pin_name, sc->gpio_muxes[i].name) == 0)
  138                         return  (&sc->gpio_muxes[i]);
  139         }
  140 
  141         return (NULL);
  142 }
  143 
  144 static int
  145 qcom_tlmm_pinctrl_gmux_function(const struct qcom_tlmm_gpio_mux *gmux,
  146     char *fnc_name)
  147 {
  148         int i;
  149 
  150         for (i = 0; i < 16; i++) { /* XXX size */
  151                 if ((gmux->functions[i] != NULL) &&
  152                     (strcmp(fnc_name, gmux->functions[i]) == 0))
  153                         return  (i);
  154         }
  155 
  156         return (-1);
  157 }
  158 
  159 static int
  160 qcom_tlmm_pinctrl_read_node(struct qcom_tlmm_softc *sc,
  161      phandle_t node, struct qcom_tlmm_pinctrl_cfg *cfg, char **pins,
  162      int *lpins)
  163 {
  164         int rv, i;
  165 
  166         *lpins = OF_getprop_alloc(node, "pins", (void **)pins);
  167         if (*lpins <= 0)
  168                 return (ENOENT);
  169 
  170         /* Read function (mux) settings. */
  171         rv = OF_getprop_alloc(node, "function", (void **)&cfg->function);
  172         if (rv <= 0)
  173                 cfg->function = NULL;
  174 
  175         /*
  176          * Read the rest of the properties.
  177          *
  178          * Properties that are a flag are simply present with a value of 0.
  179          * Properties that have arguments have have_value set to 1, and
  180          * we will parse an argument out for it to use.
  181          *
  182          * Properties that were not found/parsed with have a value of -1
  183          * and thus we won't program them into the hardware.
  184          */
  185         for (i = 0; i < PROP_ID_MAX_ID; i++) {
  186                 rv = OF_getencprop(node, prop_names[i].name, &cfg->params[i],
  187                     sizeof(cfg->params[i]));
  188                 if (prop_names[i].have_value) {
  189                         if (rv == 0) {
  190                                 device_printf(sc->dev,
  191                                     "WARNING: Missing value for propety"
  192                                     " \"%s\"\n",
  193                                     prop_names[i].name);
  194                                 cfg->params[i] = 0;
  195                         }
  196                 } else {
  197                         /* No value, default to 0 */
  198                         cfg->params[i] = 0;
  199                 }
  200                 if (rv < 0)
  201                         cfg->params[i] = -1;
  202         }
  203         return (0);
  204 }
  205 
  206 static int
  207 qcom_tlmm_pinctrl_config_gmux(struct qcom_tlmm_softc *sc, char *pin_name,
  208     const struct qcom_tlmm_gpio_mux *gmux, struct qcom_tlmm_pinctrl_cfg *cfg)
  209 {
  210         int err = 0, i;
  211 
  212         QCOM_TLMM_DPRINTF(sc, QCOM_TLMM_DEBUG_PINMUX,
  213             "%s: called; pin=%s, function %s\n",
  214             __func__, pin_name, cfg->function);
  215 
  216         GPIO_LOCK(sc);
  217 
  218         /*
  219          * Lookup the function in the configuration table.  Configure it
  220          * if required.
  221          */
  222         if (cfg->function != NULL) {
  223                 uint32_t tmp;
  224 
  225                 tmp = qcom_tlmm_pinctrl_gmux_function(gmux, cfg->function);
  226                 if (tmp == -1) {
  227                         device_printf(sc->dev,
  228                             "%s: pin=%s, function=%s, unknown!\n",
  229                             __func__,
  230                             pin_name,
  231                             cfg->function);
  232                         err = EINVAL;
  233                         goto done;
  234                 }
  235 
  236                 /*
  237                  * Program in the given function to the given pin.
  238                  */
  239                 QCOM_TLMM_DPRINTF(sc, QCOM_TLMM_DEBUG_PINMUX,
  240                     "%s: pin id=%u, new function=%u\n",
  241                     __func__,
  242                     gmux->id,
  243                     tmp);
  244                 err = qcom_tlmm_ipq4018_hw_pin_set_function(sc, gmux->id,
  245                     tmp);
  246                 if (err != 0) {
  247                         device_printf(sc->dev,
  248                             "%s: pin=%d: failed to set function (%d)\n",
  249                             __func__, gmux->id, err);
  250                         goto done;
  251                 }
  252         }
  253 
  254         /*
  255          * Iterate the set of properties; call the relevant method
  256          * if we need to change it.
  257          */
  258         for (i = 0; i < PROP_ID_MAX_ID; i++) {
  259                 if (cfg->params[i] == -1)
  260                         continue;
  261                 QCOM_TLMM_DPRINTF(sc, QCOM_TLMM_DEBUG_PINMUX,
  262                     "%s: pin_id=%u, param=%d, val=%d\n",
  263                     __func__,
  264                     gmux->id,
  265                     i,
  266                     cfg->params[i]);
  267                 switch (i) {
  268                 case PIN_ID_BIAS_DISABLE:
  269                         err = qcom_tlmm_ipq4018_hw_pin_set_pupd_config(sc,
  270                             gmux->id, QCOM_TLMM_PIN_PUPD_CONFIG_DISABLE);
  271                         if (err != 0) {
  272                                 device_printf(sc->dev,
  273                                     "%s: pin=%d: failed to set pupd(DISABLE):"
  274                                     " %d\n",
  275                                     __func__, gmux->id, err);
  276                                 goto done;
  277                         }
  278                         break;
  279                 case PIN_ID_BIAS_PULL_DOWN:
  280                         err = qcom_tlmm_ipq4018_hw_pin_set_pupd_config(sc,
  281                             gmux->id, QCOM_TLMM_PIN_PUPD_CONFIG_PULL_DOWN);
  282                         if (err != 0) {
  283                                 device_printf(sc->dev,
  284                                     "%s: pin=%d: failed to set pupd(PD):"
  285                                     " %d\n",
  286                                     __func__, gmux->id, err);
  287                                 goto done;
  288                         }
  289                         break;
  290                 case PIN_ID_BIAS_BUS_HOLD:
  291                         err = qcom_tlmm_ipq4018_hw_pin_set_pupd_config(sc,
  292                             gmux->id, QCOM_TLMM_PIN_PUPD_CONFIG_BUS_HOLD);
  293                         if (err != 0) {
  294                                 device_printf(sc->dev,
  295                                     "%s: pin=%d: failed to set pupd(HOLD):"
  296                                     " %d\n",
  297                                     __func__, gmux->id, err);
  298                                 goto done;
  299                         }
  300                         break;
  301 
  302                 case PIN_ID_BIAS_PULL_UP:
  303                         err = qcom_tlmm_ipq4018_hw_pin_set_pupd_config(sc,
  304                             gmux->id, QCOM_TLMM_PIN_PUPD_CONFIG_PULL_UP);
  305                         if (err != 0) {
  306                                 device_printf(sc->dev,
  307                                     "%s: pin=%d: failed to set pupd(PU):"
  308                                     " %d\n",
  309                                     __func__, gmux->id, err);
  310                                 goto done;
  311                         }
  312                         break;
  313                 case PIN_ID_OUTPUT_LOW:
  314                         err = qcom_tlmm_ipq4018_hw_pin_set_oe_output(sc,
  315                             gmux->id);
  316                         if (err != 0) {
  317                                 device_printf(sc->dev,
  318                                     "%s: pin=%d: failed to set OE:"
  319                                     " %d\n",
  320                                     __func__, gmux->id, err);
  321                                 goto done;
  322                         }
  323                         err = qcom_tlmm_ipq4018_hw_pin_set_output_value(
  324                             sc, gmux->id, 0);
  325                         if (err != 0) {
  326                                 device_printf(sc->dev,
  327                                     "%s: pin=%d: failed to set output value:"
  328                                     " %d\n",
  329                                     __func__, gmux->id, err);
  330                                 goto done;
  331                         }
  332                         break;
  333                 case PIN_ID_OUTPUT_HIGH:
  334                         err = qcom_tlmm_ipq4018_hw_pin_set_oe_output(sc,
  335                             gmux->id);
  336                         if (err != 0) {
  337                                 device_printf(sc->dev,
  338                                     "%s: pin=%d: failed to set OE:"
  339                                     " %d\n",
  340                                     __func__, gmux->id, err);
  341                                 goto done;
  342                         }
  343                         err = qcom_tlmm_ipq4018_hw_pin_set_output_value(
  344                             sc, gmux->id, 1);
  345                         if (err != 0) {
  346                                 device_printf(sc->dev,
  347                                     "%s: pin=%d: failed to set output value:"
  348                                     " %d\n",
  349                                     __func__, gmux->id, err);
  350                                 goto done;
  351                         }
  352                         break;
  353                 case PIN_ID_DRIVE_STRENGTH:
  354                         err = qcom_tlmm_ipq4018_hw_pin_set_drive_strength(sc,
  355                             gmux->id, cfg->params[i]);
  356                         if (err != 0) {
  357                                 device_printf(sc->dev,
  358                                     "%s: pin=%d: failed to set drive"
  359                                     " strength %d (%d)\n",
  360                                     __func__, gmux->id,
  361                                     cfg->params[i], err);
  362                                 goto done;
  363                         }
  364                         break;
  365                         case PIN_ID_VM_ENABLE:
  366                         err = qcom_tlmm_ipq4018_hw_pin_set_vm(sc,
  367                             gmux->id, true);
  368                         if (err != 0) {
  369                                 device_printf(sc->dev,
  370                                     "%s: pin=%d: failed to set VM enable:"
  371                                     " %d\n",
  372                                     __func__, gmux->id, err);
  373                                 goto done;
  374                         }
  375                         break;
  376                 case PIN_ID_VM_DISABLE:
  377                         err = qcom_tlmm_ipq4018_hw_pin_set_vm(sc,
  378                             gmux->id, false);
  379                         if (err != 0) {
  380                                 device_printf(sc->dev,
  381                                     "%s: pin=%d: failed to set VM disable:"
  382                                     " %d\n",
  383                                     __func__, gmux->id, err);
  384                                 goto done;
  385                         }
  386                         break;
  387                 case PIN_ID_DRIVE_OPEN_DRAIN:
  388                         err = qcom_tlmm_ipq4018_hw_pin_set_open_drain(sc,
  389                             gmux->id, true);
  390                         if (err != 0) {
  391                                 device_printf(sc->dev,
  392                                     "%s: pin=%d: failed to set open drain"
  393                                     " (%d)\n",
  394                                     __func__, gmux->id, err);
  395                                 goto done;
  396                         }
  397                         break;
  398                 case PIN_ID_INPUT_ENABLE:
  399                         /* Configure pin as an input */
  400                         err = qcom_tlmm_ipq4018_hw_pin_set_oe_input(sc,
  401                             gmux->id);
  402                         if (err != 0) {
  403                                 device_printf(sc->dev,
  404                                     "%s: pin=%d: failed to set pin as input"
  405                                     " (%d)\n",
  406                                     __func__, gmux->id, err);
  407                                 goto done;
  408                         }
  409                         break;
  410                 case PIN_ID_INPUT_DISABLE:
  411                         /*
  412                          * the linux-msm GPIO driver treats this as an error;
  413                          * a pin should be configured as an output instead.
  414                          */
  415                         err = ENXIO;
  416                         goto done;
  417                         break;
  418                 case PIN_ID_BIAS_HIGH_IMPEDANCE:
  419                 case PIN_ID_INPUT_SCHMITT_ENABLE:
  420                 case PIN_ID_INPUT_SCHMITT_DISABLE:
  421                 case PIN_ID_INPUT_DEBOUNCE:
  422                 case PIN_ID_SLEW_RATE:
  423                 case PIN_ID_LOW_POWER_MODE_ENABLE:
  424                 case PIN_ID_LOW_POWER_MODE_DISABLE:
  425                 case PIN_ID_BIAS_PULL_PIN_DEFAULT:
  426                 case PIN_ID_DRIVE_PUSH_PULL:
  427                 case PIN_ID_DRIVE_OPEN_SOURCE:
  428                 case PIN_ID_POWER_SOURCE:
  429                 default:
  430                         device_printf(sc->dev,
  431                             "%s: ERROR: unknown/unsupported param: "
  432                             " pin_id=%u, param=%d, val=%d\n",
  433                             __func__,
  434                             gmux->id,
  435                             i,
  436                             cfg->params[i]);
  437                         err = ENXIO;
  438                         goto done;
  439 
  440                 }
  441         }
  442 done:
  443         GPIO_UNLOCK(sc);
  444         return (0);
  445 }
  446 
  447 
  448 static int
  449 qcom_tlmm_pinctrl_config_node(struct qcom_tlmm_softc *sc,
  450     char *pin_name, struct qcom_tlmm_pinctrl_cfg *cfg)
  451 {
  452         const struct qcom_tlmm_gpio_mux *gmux;
  453         const struct qcom_tlmm_spec_pin *spin;
  454         int rv;
  455 
  456         /* Handle GPIO pins */
  457         gmux = qcom_tlmm_pinctrl_search_gmux(sc, pin_name);
  458 
  459         if (gmux != NULL) {
  460                 rv = qcom_tlmm_pinctrl_config_gmux(sc, pin_name, gmux, cfg);
  461                 return (rv);
  462         }
  463         /* Handle special pin groups */
  464         spin = qcom_tlmm_pinctrl_search_spin(sc, pin_name);
  465         if (spin != NULL) {
  466                 rv = qcom_tlmm_pinctrl_config_spin(sc, pin_name, spin, cfg);
  467                 return (rv);
  468         }
  469         device_printf(sc->dev, "Unknown pin: %s\n", pin_name);
  470         return (ENXIO);
  471 }
  472 
  473 static int
  474 qcom_tlmm_pinctrl_process_node(struct qcom_tlmm_softc *sc,
  475      phandle_t node)
  476 {
  477         struct qcom_tlmm_pinctrl_cfg cfg;
  478         char *pins, *pname;
  479         int i, len, lpins, rv;
  480 
  481         /*
  482          * Read the configuration and list of pins for the given node to
  483          * configure.
  484          */
  485         rv = qcom_tlmm_pinctrl_read_node(sc, node, &cfg, &pins, &lpins);
  486         if (rv != 0)
  487                 return (rv);
  488 
  489         len = 0;
  490         pname = pins;
  491         do {
  492                 i = strlen(pname) + 1;
  493                 /*
  494                  * Configure the given node with the specific configuration.
  495                  */
  496                 rv = qcom_tlmm_pinctrl_config_node(sc, pname, &cfg);
  497                 if (rv != 0)
  498                         device_printf(sc->dev,
  499                             "Cannot configure pin: %s: %d\n", pname, rv);
  500 
  501                 len += i;
  502                 pname += i;
  503         } while (len < lpins);
  504 
  505         if (pins != NULL)
  506                 free(pins, M_OFWPROP);
  507         if (cfg.function != NULL)
  508                 free(cfg.function, M_OFWPROP);
  509 
  510         return (rv);
  511 }
  512 
  513 int
  514 qcom_tlmm_pinctrl_configure(device_t dev, phandle_t cfgxref)
  515 {
  516         struct qcom_tlmm_softc *sc;
  517         phandle_t node, cfgnode;
  518         int rv;
  519 
  520         sc = device_get_softc(dev);
  521         cfgnode = OF_node_from_xref(cfgxref);
  522 
  523         for (node = OF_child(cfgnode); node != 0; node = OF_peer(node)) {
  524                 if (!ofw_bus_node_status_okay(node))
  525                         continue;
  526                 rv = qcom_tlmm_pinctrl_process_node(sc, node);
  527                 if (rv != 0)
  528                  device_printf(dev, "Pin config failed: %d\n", rv);
  529         }
  530 
  531         return (0);
  532 }
  533 

Cache object: 3140530959d07a6bb0ac0d01c74f9ad8


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.