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/uart/uart_bus_fdt.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) 2009-2010 The FreeBSD Foundation
    5  *
    6  * This software was developed by Semihalf under sponsorship from
    7  * the FreeBSD Foundation.
    8  *
    9  * Redistribution and use in source and binary forms, with or without
   10  * modification, are permitted provided that the following conditions
   11  * are met:
   12  * 1. Redistributions of source code must retain the above copyright
   13  *    notice, this list of conditions and the following disclaimer.
   14  * 2. Redistributions in binary form must reproduce the above copyright
   15  *    notice, this list of conditions and the following disclaimer in the
   16  *    documentation and/or other materials provided with the distribution.
   17  *
   18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   28  * SUCH DAMAGE.
   29  */
   30 
   31 #include <sys/cdefs.h>
   32 __FBSDID("$FreeBSD$");
   33 
   34 #include "opt_platform.h"
   35 
   36 #include <sys/param.h>
   37 #include <sys/systm.h>
   38 #include <sys/bus.h>
   39 #include <sys/kernel.h>
   40 #include <sys/module.h>
   41 
   42 #include <machine/bus.h>
   43 
   44 #include <dev/fdt/fdt_common.h>
   45 #include <dev/ofw/ofw_bus.h>
   46 #include <dev/ofw/ofw_bus_subr.h>
   47 #include <dev/uart/uart.h>
   48 #include <dev/uart/uart_bus.h>
   49 #include <dev/uart/uart_cpu.h>
   50 #include <dev/uart/uart_cpu_fdt.h>
   51 
   52 static int uart_fdt_probe(device_t);
   53 
   54 static device_method_t uart_fdt_methods[] = {
   55         /* Device interface */
   56         DEVMETHOD(device_probe,         uart_fdt_probe),
   57         DEVMETHOD(device_attach,        uart_bus_attach),
   58         DEVMETHOD(device_detach,        uart_bus_detach),
   59         { 0, 0 }
   60 };
   61 
   62 static driver_t uart_fdt_driver = {
   63         uart_driver_name,
   64         uart_fdt_methods,
   65         sizeof(struct uart_softc),
   66 };
   67 
   68 int
   69 uart_fdt_get_clock(phandle_t node, pcell_t *cell)
   70 {
   71 
   72         /* clock-frequency is a FreeBSD-only extension. */
   73         if ((OF_getencprop(node, "clock-frequency", cell,
   74             sizeof(*cell))) <= 0) {
   75                 /* Try to retrieve parent 'bus-frequency' */
   76                 /* XXX this should go to simple-bus fixup or so */
   77                 if ((OF_getencprop(OF_parent(node), "bus-frequency", cell,
   78                     sizeof(*cell))) <= 0)
   79                         *cell = 0;
   80         }
   81 
   82         return (0);
   83 }
   84 
   85 int
   86 uart_fdt_get_shift(phandle_t node, pcell_t *cell)
   87 {
   88 
   89         if ((OF_getencprop(node, "reg-shift", cell, sizeof(*cell))) <= 0)
   90                 return (-1);
   91         return (0);
   92 }
   93 
   94 int
   95 uart_fdt_get_io_width(phandle_t node, pcell_t *cell)
   96 {
   97 
   98         if ((OF_getencprop(node, "reg-io-width", cell, sizeof(*cell))) <= 0)
   99                 return (-1);
  100         return (0);
  101 }
  102 
  103 static uintptr_t
  104 uart_fdt_find_device(device_t dev)
  105 {
  106         struct ofw_compat_data **cd;
  107         const struct ofw_compat_data *ocd;
  108 
  109         SET_FOREACH(cd, uart_fdt_class_and_device_set) {
  110                 ocd = ofw_bus_search_compatible(dev, *cd);
  111                 if (ocd->ocd_data != 0)
  112                         return (ocd->ocd_data);
  113         }
  114         return (0);
  115 }
  116 
  117 static int
  118 phandle_chosen_propdev(phandle_t chosen, const char *name, phandle_t *node)
  119 {
  120         char buf[64];
  121         char *sep;
  122 
  123         if (OF_getprop(chosen, name, buf, sizeof(buf)) <= 0)
  124                 return (ENXIO);
  125         /*
  126          * stdout-path may have a ':' to separate the device from the
  127          * connection settings. Split the string so we just pass the former
  128          * to OF_finddevice.
  129          */
  130         sep = strchr(buf, ':');
  131         if (sep != NULL)
  132                 *sep = '\0';
  133         if ((*node = OF_finddevice(buf)) == -1)
  134                 return (ENXIO);
  135 
  136         return (0);
  137 }
  138 
  139 static const struct ofw_compat_data *
  140 uart_fdt_find_compatible(phandle_t node, const struct ofw_compat_data *cd)
  141 {
  142         const struct ofw_compat_data *ocd;
  143 
  144         for (ocd = cd; ocd->ocd_str != NULL; ocd++) {
  145                 if (ofw_bus_node_is_compatible(node, ocd->ocd_str))
  146                         return (ocd);
  147         }
  148         return (NULL);
  149 }
  150 
  151 static uintptr_t
  152 uart_fdt_find_by_node(phandle_t node, int class_list)
  153 {
  154         struct ofw_compat_data **cd;
  155         const struct ofw_compat_data *ocd;
  156 
  157         if (class_list) {
  158                 SET_FOREACH(cd, uart_fdt_class_set) {
  159                         ocd = uart_fdt_find_compatible(node, *cd);
  160                         if ((ocd != NULL) && (ocd->ocd_data != 0))
  161                                 return (ocd->ocd_data);
  162                 }
  163         } else {
  164                 SET_FOREACH(cd, uart_fdt_class_and_device_set) {
  165                         ocd = uart_fdt_find_compatible(node, *cd);
  166                         if ((ocd != NULL) && (ocd->ocd_data != 0))
  167                                 return (ocd->ocd_data);
  168                 }
  169         }
  170 
  171         return (0);
  172 }
  173 
  174 int
  175 uart_cpu_fdt_probe(struct uart_class **classp, bus_space_tag_t *bst,
  176     bus_space_handle_t *bsh, int *baud, u_int *rclk, u_int *shiftp,
  177     u_int *iowidthp, const int devtype)
  178 {
  179         const char *propnames[] = {"stdout-path", "linux,stdout-path", "stdout",
  180             "stdin-path", "stdin", NULL};
  181         const char *propnames_dbgport[] = {"freebsd,debug-path", NULL};
  182         const char **name;
  183         struct uart_class *class;
  184         phandle_t node, chosen;
  185         pcell_t br, clk, shift, iowidth;
  186         char *cp = NULL;
  187         int err;
  188 
  189         /* Has the user forced a specific device node? */
  190         switch (devtype) {
  191         case UART_DEV_DBGPORT:
  192                 cp = kern_getenv("hw.fdt.dbgport");
  193                 name = propnames_dbgport;
  194                 break;
  195         case UART_DEV_CONSOLE:
  196                 cp = kern_getenv("hw.fdt.console");
  197                 name = propnames;
  198                 break;
  199         default:
  200                 return (ENXIO);
  201         }
  202 
  203         if (cp == NULL) {
  204                 /*
  205                  * Retrieve a node from /chosen.
  206                  */
  207                 node = -1;
  208                 if ((chosen = OF_finddevice("/chosen")) != -1) {
  209                         for (; *name != NULL; name++) {
  210                                 if (phandle_chosen_propdev(chosen, *name,
  211                                     &node) == 0)
  212                                         break;
  213                         }
  214                 }
  215                 if (chosen == -1 || *name == NULL)
  216                         node = OF_finddevice("serial0"); /* Last ditch */
  217         } else {
  218                 node = OF_finddevice(cp);
  219         }
  220 
  221         if (node == -1)
  222                 return (ENXIO);
  223 
  224         /*
  225          * Check old style of UART definition first. Unfortunately, the common
  226          * FDT processing is not possible if we have clock, power domains and
  227          * pinmux stuff.
  228          */
  229         class = (struct uart_class *)uart_fdt_find_by_node(node, 0);
  230         if (class != NULL) {
  231                 if ((err = uart_fdt_get_clock(node, &clk)) != 0)
  232                         return (err);
  233         } else {
  234                 /* Check class only linker set */
  235                 class =
  236                     (struct uart_class *)uart_fdt_find_by_node(node, 1);
  237                 if (class == NULL)
  238                         return (ENXIO);
  239                 clk = 0;
  240         }
  241 
  242         /*
  243          * Retrieve serial attributes.
  244          */
  245         if (uart_fdt_get_shift(node, &shift) != 0)
  246                 shift = uart_getregshift(class);
  247 
  248         if (uart_fdt_get_io_width(node, &iowidth) != 0)
  249                 iowidth = uart_getregiowidth(class);
  250 
  251         if (OF_getencprop(node, "current-speed", &br, sizeof(br)) <= 0)
  252                 br = 0;
  253 
  254         err = OF_decode_addr(node, 0, bst, bsh, NULL);
  255         if (err != 0)
  256                 return (err);
  257 
  258         *classp = class;
  259         *baud = br;
  260         *rclk = clk;
  261         *shiftp = shift;
  262         *iowidthp = iowidth;
  263 
  264         return (0);
  265 }
  266 
  267 static int
  268 uart_fdt_probe(device_t dev)
  269 {
  270         struct uart_softc *sc;
  271         phandle_t node;
  272         pcell_t clock, shift, iowidth;
  273         int err;
  274 
  275         sc = device_get_softc(dev);
  276 
  277         if (!ofw_bus_status_okay(dev))
  278                 return (ENXIO);
  279 
  280         sc->sc_class = (struct uart_class *)uart_fdt_find_device(dev);
  281         if (sc->sc_class == NULL)
  282                 return (ENXIO);
  283 
  284         node = ofw_bus_get_node(dev);
  285 
  286         if ((err = uart_fdt_get_clock(node, &clock)) != 0)
  287                 return (err);
  288         if (uart_fdt_get_shift(node, &shift) != 0)
  289                 shift = uart_getregshift(sc->sc_class);
  290         if (uart_fdt_get_io_width(node, &iowidth) != 0)
  291                 iowidth = uart_getregiowidth(sc->sc_class);
  292 
  293         return (uart_bus_probe(dev, (int)shift, (int)iowidth, (int)clock, 0, 0, 0));
  294 }
  295 
  296 DRIVER_MODULE(uart, simplebus, uart_fdt_driver, 0, 0);
  297 DRIVER_MODULE(uart, ofwbus, uart_fdt_driver, 0, 0);

Cache object: 4a5316461a0e2ea44817b6ee316dac37


[ 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.