Index: sys/dev/uart/uart.h =================================================================== --- sys/dev/uart/uart.h +++ sys/dev/uart/uart.h @@ -80,10 +80,6 @@ extern struct uart_class at91_usart_class __attribute__((weak)); extern struct uart_class uart_exynos4210_class __attribute__((weak)); -#ifdef FDT -struct ofw_compat_data; -extern const struct ofw_compat_data *uart_fdt_compat_data; -#endif #ifdef PC98 struct uart_class *uart_pc98_getdev(u_long port); Index: sys/dev/uart/uart_bus_fdt.c =================================================================== --- sys/dev/uart/uart_bus_fdt.c +++ sys/dev/uart/uart_bus_fdt.c @@ -45,6 +45,7 @@ #include #include #include +#include static int uart_fdt_probe(device_t); @@ -91,7 +92,7 @@ }; /* Export the compat_data table for use by the uart_cpu_fdt.c probe routine. */ -const struct ofw_compat_data *uart_fdt_compat_data = compat_data; +UART_FDT_CLASS_AND_DEVICE(compat_data); static int uart_fdt_get_clock(phandle_t node, pcell_t *cell) @@ -127,6 +128,20 @@ return (0); } +static uintptr_t +uart_fdt_find_device(device_t dev) +{ + struct ofw_compat_data **cd; + const struct ofw_compat_data *ocd; + + SET_FOREACH(cd, uart_fdt_class_and_device_set) { + ocd = ofw_bus_search_compatible(dev, *cd); + if (ocd->ocd_data != 0) + return (ocd->ocd_data); + } + return (0); +} + static int uart_fdt_probe(device_t dev) { @@ -134,19 +149,16 @@ phandle_t node; pcell_t clock, shift; int err; - const struct ofw_compat_data * cd; sc = device_get_softc(dev); if (!ofw_bus_status_okay(dev)) return (ENXIO); - cd = ofw_bus_search_compatible(dev, compat_data); - if (cd->ocd_data == (uintptr_t)NULL) + sc->sc_class = (struct uart_class *)uart_fdt_find_device(dev); + if (sc->sc_class == NULL) return (ENXIO); - sc->sc_class = (struct uart_class *)cd->ocd_data; - node = ofw_bus_get_node(dev); if ((err = uart_fdt_get_clock(node, &clock)) != 0) Index: sys/dev/uart/uart_cpu_fdt.h =================================================================== --- /dev/null +++ sys/dev/uart/uart_cpu_fdt.h @@ -0,0 +1,54 @@ +/*- + * Copyright 1992-2014 The FreeBSD Project. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_UART_CPU_FDT_H_ +#define _DEV_UART_CPU_FDT_H_ + +#include + +#include + +/* + * If your UART driver implements only uart_class and uses uart_cpu_fdt.c + * for device instationation, then use UART_FDT_CLASS_AND_DEVICE for its + * declaration + */ +SET_DECLARE(uart_fdt_class_and_device_set, struct ofw_compat_data ); +#define UART_FDT_CLASS_AND_DEVICE(data) \ + DATA_SET(uart_fdt_class_and_device_set, data) + +/* + * If your UART driver implements uart_class and custom device layer, + * then use UART_FDT_CLASS for its declaration + */ +SET_DECLARE(uart_fdt_class_set, struct ofw_compat_data ); +#define UART_FDT_CLASS(data) \ + DATA_SET(uart_fdt_class_set, data) + + +#endif /* _DEV_UART_CPU_FDT_H_ */ Index: sys/dev/uart/uart_cpu_fdt.c =================================================================== --- sys/dev/uart/uart_cpu_fdt.c +++ sys/dev/uart/uart_cpu_fdt.c @@ -50,6 +50,7 @@ #include #include #include +#include /* * UART console routines. @@ -115,13 +116,46 @@ return (0); } +static const struct ofw_compat_data * +uart_fdt_find_compatible(phandle_t node, const struct ofw_compat_data *cd) +{ + const struct ofw_compat_data *ocd; + + for (ocd = cd; ocd->ocd_str != NULL; ocd++) { + if (fdt_is_compatible(node, ocd->ocd_str)) + return (ocd); + } + return (NULL); +} + +static uintptr_t +uart_fdt_find_by_node(phandle_t node, int class_list) +{ + struct ofw_compat_data **cd; + const struct ofw_compat_data *ocd; + + if (class_list) { + SET_FOREACH(cd, uart_fdt_class_set) { + ocd = uart_fdt_find_compatible(node, *cd); + if ((ocd != NULL) && (ocd->ocd_data != 0)) + return (ocd->ocd_data); + } + } else { + SET_FOREACH(cd, uart_fdt_class_and_device_set) { + ocd = uart_fdt_find_compatible(node, *cd); + if ((ocd != NULL) && (ocd->ocd_data != 0)) + return (ocd->ocd_data); + } + } + return (0); +} + int uart_cpu_getdev(int devtype, struct uart_devinfo *di) { const char *propnames[] = {"stdout-path", "linux,stdout-path", "stdout", "stdin-path", "stdin", NULL}; const char **name; - const struct ofw_compat_data *cd; struct uart_class *class; phandle_t node, chosen; pcell_t shift, br, rclk; @@ -160,24 +194,32 @@ * Retrieve serial attributes. */ uart_fdt_get_shift(node, &shift); - if (OF_getprop(node, "current-speed", &br, sizeof(br)) <= 0) br = 0; - br = fdt32_to_cpu(br); + else + br = fdt32_to_cpu(br); - if ((err = uart_fdt_get_clock(node, &rclk)) != 0) - return (err); /* - * Finalize configuration. + * Check old style of UART definition first. Unfortunately, the common + * FDT processing is not possible if we have clock, power domains and + * pinmux stuff. */ - for (cd = uart_fdt_compat_data; cd->ocd_str != NULL; ++cd) { - if (fdt_is_compatible(node, cd->ocd_str)) - break; + class = (struct uart_class *)uart_fdt_find_by_node(node, 0); + if (class != NULL) { + if ((err = uart_fdt_get_clock(node, &rclk)) != 0) + return (err); + } else { + /* Check class only linker set */ + class = + (struct uart_class *)uart_fdt_find_by_node(node, 1); + if (class == NULL) + return (ENXIO); + rclk = 0; } - if (cd->ocd_str == NULL) - return (ENXIO); - class = (struct uart_class *)cd->ocd_data; + /* + * Finalize configuration. + */ di->bas.chan = 0; di->bas.regshft = (u_int)shift; di->baudrate = br;