Index: sys/arm/conf/A10 =================================================================== --- sys/arm/conf/A10 +++ sys/arm/conf/A10 @@ -61,7 +61,7 @@ # Console and misc device uart -device uart_ns8250 +device uart_snps device pty device snp device md Index: sys/arm/conf/A20 =================================================================== --- sys/arm/conf/A20 +++ sys/arm/conf/A20 @@ -71,7 +71,7 @@ # Console and misc device uart -device uart_ns8250 +device uart_snps device pty device snp device md Index: sys/arm/conf/ARMADA38X =================================================================== --- sys/arm/conf/ARMADA38X +++ sys/arm/conf/ARMADA38X @@ -48,7 +48,7 @@ # Serial ports device uart -device uart_ns8250 +device uart_snps # Network device ether Index: sys/arm/conf/RK3188 =================================================================== --- sys/arm/conf/RK3188 +++ sys/arm/conf/RK3188 @@ -59,7 +59,7 @@ # Console and misc device uart -device uart_ns8250 +device uart_snps device pty device snp device md Index: sys/arm/mv/files.mv =================================================================== --- sys/arm/mv/files.mv +++ sys/arm/mv/files.mv @@ -26,6 +26,7 @@ dev/nand/nfc_mv.c optional nand dev/mvs/mvs_soc.c optional mvs dev/uart/uart_dev_ns8250.c optional uart +dev/uart/uart_dev_snps.c optional uart dev/usb/controller/ehci_mv.c optional ehci dev/usb/controller/xhci_mv.c optional xhci Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -2555,11 +2555,12 @@ dev/uart/uart_bus_scc.c optional uart scc dev/uart/uart_core.c optional uart dev/uart/uart_dbg.c optional uart gdb -dev/uart/uart_dev_ns8250.c optional uart uart_ns8250 +dev/uart/uart_dev_ns8250.c optional uart uart_ns8250 | uart uart_snps dev/uart/uart_dev_pl011.c optional uart pl011 dev/uart/uart_dev_quicc.c optional uart quicc dev/uart/uart_dev_sab82532.c optional uart uart_sab82532 dev/uart/uart_dev_sab82532.c optional uart scc +dev/uart/uart_dev_snps.c optional uart uart_snps dev/uart/uart_dev_z8530.c optional uart uart_z8530 dev/uart/uart_dev_z8530.c optional uart scc dev/uart/uart_if.m optional uart Index: sys/dev/uart/uart_dev_ns8250.c =================================================================== --- sys/dev/uart/uart_dev_ns8250.c +++ sys/dev/uart/uart_dev_ns8250.c @@ -398,7 +398,6 @@ static struct ofw_compat_data compat_data[] = { {"ns16550", (uintptr_t)&uart_ns8250_class}, {"ns16550a", (uintptr_t)&uart_ns8250_class}, - {"snps,dw-apb-uart", (uintptr_t)&uart_ns8250_class}, {NULL, (uintptr_t)NULL}, }; UART_FDT_CLASS_AND_DEVICE(compat_data); @@ -451,19 +450,9 @@ pcell_t cell; #endif - ns8250->busy_detect = 0; - #ifdef FDT - /* - * Check whether uart requires to read USR reg when IIR_BUSY and - * has broken txfifo. - */ - ns8250->busy_detect = ofw_bus_is_compatible(sc->sc_dev, "snps,dw-apb-uart"); + /* Check whether uart has a broken txfifo. */ node = ofw_bus_get_node(sc->sc_dev); - /* XXX: This is kept for a short time for compatibility with older device trees */ - if ((OF_getencprop(node, "busy-detect", &cell, sizeof(cell))) > 0 - && cell != 0) - ns8250->busy_detect = 1; if ((OF_getencprop(node, "broken-txfifo", &cell, sizeof(cell))) > 0) broken_txfifo = cell ? 1 : 0; #endif Index: sys/dev/uart/uart_dev_snps.c =================================================================== --- /dev/null +++ sys/dev/uart/uart_dev_snps.c @@ -0,0 +1,284 @@ +/*- + * Copyright (c) 2016 Jared McNeill + * 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 ``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 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$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef EXT_RESOURCES +#include +#include +#endif + +#include "uart_if.h" + +struct snps_softc { + struct ns8250_softc ns8250; + +#ifdef EXT_RESOURCES + clk_t baudclk; + clk_t apb_pclk; + hwreset_t reset; +#endif +}; + +static int +snps_uart_attach(struct uart_softc *uart_sc) +{ + struct snps_softc *sc; + + sc = (struct snps_softc *)uart_sc; + + /* UART requires to read USR reg when IIR_BUSY */ + sc->ns8250.busy_detect = 1; + + return (ns8250_bus_attach(uart_sc)); +} + +static kobj_method_t snps_methods[] = { + KOBJMETHOD(uart_probe, ns8250_bus_probe), + KOBJMETHOD(uart_attach, snps_uart_attach), + KOBJMETHOD(uart_detach, ns8250_bus_detach), + KOBJMETHOD(uart_flush, ns8250_bus_flush), + KOBJMETHOD(uart_getsig, ns8250_bus_getsig), + KOBJMETHOD(uart_ioctl, ns8250_bus_ioctl), + KOBJMETHOD(uart_ipend, ns8250_bus_ipend), + KOBJMETHOD(uart_param, ns8250_bus_param), + KOBJMETHOD(uart_receive, ns8250_bus_receive), + KOBJMETHOD(uart_setsig, ns8250_bus_setsig), + KOBJMETHOD(uart_transmit, ns8250_bus_transmit), + KOBJMETHOD(uart_grab, ns8250_bus_grab), + KOBJMETHOD(uart_ungrab, ns8250_bus_ungrab), + KOBJMETHOD_END +}; + +struct uart_class uart_snps_class = { + "snps", + snps_methods, + sizeof(struct snps_softc), + .uc_ops = &uart_ns8250_ops, + .uc_range = 8, + .uc_rclk = 0, +}; + +static struct ofw_compat_data compat_data[] = { + { "snps,dw-apb-uart", (uintptr_t)&uart_snps_class }, + { NULL, (uintptr_t)NULL } +}; +UART_FDT_CLASS(compat_data); + +#ifdef EXT_RESOURCES +static int +snps_get_clocks(device_t dev, clk_t *baudclk, clk_t *apb_pclk) +{ + struct snps_softc *sc; + + sc = device_get_softc(dev); + *baudclk = NULL; + *apb_pclk = NULL; + + /* Baud clock is either named "baudclk", or there is a single + * unnamed clock. + */ + if (clk_get_by_ofw_name(dev, "baudclk", baudclk) != 0 && + clk_get_by_ofw_index(dev, 0, baudclk) != 0) + return (ENOENT); + + /* APB peripheral clock is optional */ + (void)clk_get_by_ofw_name(dev, "apb_pclk", apb_pclk); + + return (0); +} +#endif + +static int +snps_probe(device_t dev) +{ + struct snps_softc *sc; + struct uart_class *uart_class; + phandle_t node; + uint32_t shift, clock; + uint64_t freq; + int error; +#ifdef EXT_RESOURCES + clk_t baudclk, apb_pclk; + hwreset_t reset; +#endif + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + uart_class = (struct uart_class *)ofw_bus_search_compatible(dev, + compat_data)->ocd_data; + if (uart_class == NULL) + return (ENXIO); + + freq = 0; + sc = device_get_softc(dev); + sc->ns8250.base.sc_class = uart_class; + + node = ofw_bus_get_node(dev); + if (OF_getencprop(node, "reg-shift", &shift, sizeof(shift)) <= 0) + shift = 0; + if (OF_getencprop(node, "clock-frequency", &clock, sizeof(clock)) <= 0) + clock = 0; + +#ifdef EXT_RESOURCES + if (hwreset_get_by_ofw_idx(dev, 0, &reset) == 0) { + error = hwreset_deassert(reset); + if (error != 0) { + device_printf(dev, "cannot de-assert reset\n"); + return (error); + } + } + + if (snps_get_clocks(dev, &baudclk, &apb_pclk) == 0) { + error = clk_enable(baudclk); + if (error != 0) { + device_printf(dev, "cannot enable baud clock\n"); + return (error); + } + if (apb_pclk != NULL) { + error = clk_enable(apb_pclk); + if (error != 0) { + device_printf(dev, + "cannot enable peripheral clock\n"); + return (error); + } + } + + if (clock == 0) { + error = clk_get_freq(baudclk, &freq); + if (error != 0) { + device_printf(dev, "cannot get frequency\n"); + return (error); + } + clock = (uint32_t)freq; + } + } +#endif + + if (bootverbose && clock == 0) + device_printf(dev, "could not determine frequency\n"); + + error = uart_bus_probe(dev, (int)shift, (int)clock, 0, 0); + if (error != 0) + return (error); + +#ifdef EXT_RESOURCES + /* XXX uart_bus_probe has changed the softc, so refresh it */ + sc = device_get_softc(dev); + + /* Store clock and reset handles for detach */ + sc->baudclk = baudclk; + sc->apb_pclk = apb_pclk; + sc->reset = reset; +#endif + + return (0); +} + +static int +snps_detach(device_t dev) +{ +#ifdef EXT_RESOURCES + struct snps_softc *sc; + clk_t baudclk, apb_pclk; + hwreset_t reset; +#endif + int error; + +#ifdef EXT_RESOURCES + sc = device_get_softc(dev); + baudclk = sc->baudclk; + apb_pclk = sc->apb_pclk; + reset = sc->reset; +#endif + + error = uart_bus_detach(dev); + if (error != 0) + return (error); + +#ifdef EXT_RESOURCES + if (reset != NULL) { + error = hwreset_assert(reset); + if (error != 0) { + device_printf(dev, "cannot assert reset\n"); + return (error); + } + hwreset_release(reset); + } + if (apb_pclk != NULL) { + error = clk_release(apb_pclk); + if (error != 0) { + device_printf(dev, "cannot release peripheral clock\n"); + return (error); + } + } + if (baudclk != NULL) { + error = clk_release(baudclk); + if (error != 0) { + device_printf(dev, "cannot release baud clock\n"); + return (error); + } + } +#endif + + return (0); +} + +static device_method_t snps_bus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, snps_probe), + DEVMETHOD(device_attach, uart_bus_attach), + DEVMETHOD(device_detach, snps_detach), + DEVMETHOD_END +}; + +static driver_t snps_uart_driver = { + uart_driver_name, + snps_bus_methods, + sizeof(struct snps_softc) +}; + +DRIVER_MODULE(uart_snps, simplebus, snps_uart_driver, uart_devclass, 0, 0); +DRIVER_MODULE(uart_snps, ofwbus, snps_uart_driver, uart_devclass, 0, 0);