1208748Sraj/*- 2208748Sraj * Copyright (c) 2009-2010 The FreeBSD Foundation 3208748Sraj * All rights reserved. 4208748Sraj * 5208748Sraj * This software was developed by Semihalf under sponsorship from 6208748Sraj * the FreeBSD Foundation. 7208748Sraj * 8208748Sraj * Redistribution and use in source and binary forms, with or without 9208748Sraj * modification, are permitted provided that the following conditions 10208748Sraj * are met: 11208748Sraj * 1. Redistributions of source code must retain the above copyright 12208748Sraj * notice, this list of conditions and the following disclaimer. 13208748Sraj * 2. Redistributions in binary form must reproduce the above copyright 14208748Sraj * notice, this list of conditions and the following disclaimer in the 15208748Sraj * documentation and/or other materials provided with the distribution. 16208748Sraj * 17208748Sraj * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18208748Sraj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19208748Sraj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20208748Sraj * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21208748Sraj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22208748Sraj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23208748Sraj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24208748Sraj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25208748Sraj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26208748Sraj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27208748Sraj * SUCH DAMAGE. 28208748Sraj */ 29208748Sraj 30208748Sraj#include <sys/cdefs.h> 31208748Sraj__FBSDID("$FreeBSD$"); 32208748Sraj 33208748Sraj#include <sys/param.h> 34208748Sraj#include <sys/bus.h> 35208748Sraj#include <sys/kernel.h> 36208748Sraj#include <sys/module.h> 37208748Sraj 38208748Sraj#include <machine/bus.h> 39208748Sraj#include <machine/fdt.h> 40208748Sraj 41208748Sraj#include <dev/fdt/fdt_common.h> 42208748Sraj#include <dev/ofw/ofw_bus.h> 43208748Sraj#include <dev/ofw/ofw_bus_subr.h> 44208748Sraj#include <dev/uart/uart.h> 45208748Sraj#include <dev/uart/uart_bus.h> 46208748Sraj#include <dev/uart/uart_cpu.h> 47208748Sraj 48208748Srajstatic int uart_fdt_probe(device_t); 49208748Sraj 50208748Srajstatic device_method_t uart_fdt_methods[] = { 51208748Sraj /* Device interface */ 52208748Sraj DEVMETHOD(device_probe, uart_fdt_probe), 53208748Sraj DEVMETHOD(device_attach, uart_bus_attach), 54208748Sraj DEVMETHOD(device_detach, uart_bus_detach), 55208748Sraj { 0, 0 } 56208748Sraj}; 57208748Sraj 58208748Srajstatic driver_t uart_fdt_driver = { 59208748Sraj uart_driver_name, 60208748Sraj uart_fdt_methods, 61208748Sraj sizeof(struct uart_softc), 62208748Sraj}; 63208748Sraj 64208748Srajstatic int 65208748Srajuart_fdt_get_clock(phandle_t node, pcell_t *cell) 66208748Sraj{ 67208748Sraj pcell_t clock; 68208748Sraj 69208748Sraj if ((OF_getprop(node, "clock-frequency", &clock, 70208748Sraj sizeof(clock))) <= 0) 71208748Sraj return (ENXIO); 72208748Sraj 73208748Sraj if (clock == 0) 74208748Sraj /* Try to retrieve parent 'bus-frequency' */ 75208748Sraj /* XXX this should go to simple-bus fixup or so */ 76208748Sraj if ((OF_getprop(OF_parent(node), "bus-frequency", &clock, 77208748Sraj sizeof(clock))) <= 0) 78208748Sraj clock = 0; 79208748Sraj 80208748Sraj *cell = fdt32_to_cpu(clock); 81208748Sraj return (0); 82208748Sraj} 83208748Sraj 84208748Srajstatic int 85208748Srajuart_fdt_get_shift(phandle_t node, pcell_t *cell) 86208748Sraj{ 87208748Sraj pcell_t shift; 88208748Sraj 89208748Sraj if ((OF_getprop(node, "reg-shift", &shift, sizeof(shift))) <= 0) 90208748Sraj shift = 0; 91208748Sraj *cell = fdt32_to_cpu(shift); 92208748Sraj return (0); 93208748Sraj} 94208748Sraj 95208748Srajstatic int 96208748Srajuart_fdt_probe(device_t dev) 97208748Sraj{ 98208748Sraj struct uart_softc *sc; 99208748Sraj phandle_t node; 100208748Sraj pcell_t clock, shift; 101208748Sraj int err; 102208748Sraj 103208748Sraj if (!ofw_bus_is_compatible(dev, "ns16550")) 104208748Sraj return (ENXIO); 105208748Sraj 106208748Sraj sc = device_get_softc(dev); 107208748Sraj sc->sc_class = &uart_ns8250_class; 108208748Sraj 109208748Sraj node = ofw_bus_get_node(dev); 110208748Sraj 111208748Sraj if ((err = uart_fdt_get_clock(node, &clock)) != 0) 112208748Sraj return (err); 113208748Sraj uart_fdt_get_shift(node, &shift); 114208748Sraj 115208748Sraj return (uart_bus_probe(dev, (int)shift, (int)clock, 0, 0)); 116208748Sraj} 117208748Sraj 118208748SrajDRIVER_MODULE(uart, simplebus, uart_fdt_driver, uart_devclass, 0, 0); 119208748Sraj 120208748Sraj/* 121208748Sraj * UART console routines. 122208748Sraj */ 123208748Srajbus_space_tag_t uart_bus_space_io; 124208748Srajbus_space_tag_t uart_bus_space_mem; 125208748Sraj 126208748Srajint 127208748Srajuart_cpu_eqres(struct uart_bas *b1, struct uart_bas *b2) 128208748Sraj{ 129208748Sraj 130208748Sraj return ((b1->bsh == b2->bsh && b1->bst == b2->bst) ? 1 : 0); 131208748Sraj} 132208748Sraj 133208748Srajint 134208748Srajuart_cpu_getdev(int devtype, struct uart_devinfo *di) 135208748Sraj{ 136208748Sraj char buf[64]; 137208748Sraj struct uart_class *class; 138208748Sraj phandle_t node, chosen; 139208748Sraj pcell_t shift, br, rclk; 140208748Sraj u_long start, size; 141208748Sraj int err; 142208748Sraj 143217520Smarcel uart_bus_space_mem = fdtbus_bs_tag; 144217520Smarcel uart_bus_space_io = NULL; 145217520Smarcel 146217520Smarcel /* Allow overriding the FDT uning the environment. */ 147217520Smarcel class = &uart_ns8250_class; 148217520Smarcel err = uart_getenv(devtype, di, class); 149217520Smarcel if (!err) 150217520Smarcel return (0); 151217520Smarcel 152208748Sraj if (devtype != UART_DEV_CONSOLE) 153208748Sraj return (ENXIO); 154208748Sraj 155208748Sraj /* 156208748Sraj * Retrieve /chosen/std{in,out}. 157208748Sraj */ 158208748Sraj if ((chosen = OF_finddevice("/chosen")) == 0) 159208748Sraj return (ENXIO); 160208748Sraj if (OF_getprop(chosen, "stdin", buf, sizeof(buf)) <= 0) 161208748Sraj return (ENXIO); 162208748Sraj if ((node = OF_finddevice(buf)) == 0) 163208748Sraj return (ENXIO); 164208748Sraj if (OF_getprop(chosen, "stdout", buf, sizeof(buf)) <= 0) 165208748Sraj return (ENXIO); 166208748Sraj if (OF_finddevice(buf) != node) 167208748Sraj /* Only stdin == stdout is supported. */ 168208748Sraj return (ENXIO); 169208748Sraj /* 170208748Sraj * Retrieve serial attributes. 171208748Sraj */ 172208748Sraj uart_fdt_get_shift(node, &shift); 173208748Sraj 174208748Sraj if (OF_getprop(node, "current-speed", &br, sizeof(br)) <= 0) 175208748Sraj br = 0; 176208748Sraj br = fdt32_to_cpu(br); 177208748Sraj 178208748Sraj if ((err = uart_fdt_get_clock(node, &rclk)) != 0) 179208748Sraj return (err); 180208748Sraj /* 181208748Sraj * Finalize configuration. 182208748Sraj */ 183208748Sraj class = &uart_quicc_class; 184208748Sraj if (fdt_is_compatible(node, "ns16550")) 185208748Sraj class = &uart_ns8250_class; 186208748Sraj 187208748Sraj di->bas.chan = 0; 188208748Sraj di->bas.regshft = (u_int)shift; 189208748Sraj di->baudrate = 0; 190208748Sraj di->bas.rclk = (u_int)rclk; 191208748Sraj di->ops = uart_getops(class); 192208748Sraj di->databits = 8; 193208748Sraj di->stopbits = 1; 194208748Sraj di->parity = UART_PARITY_NONE; 195217520Smarcel di->bas.bst = uart_bus_space_mem; 196208748Sraj 197208748Sraj err = fdt_regsize(node, &start, &size); 198208748Sraj if (err) 199208748Sraj return (ENXIO); 200210247Sraj start += fdt_immr_va; 201208748Sraj 202217520Smarcel return (bus_space_map(di->bas.bst, start, size, 0, &di->bas.bsh)); 203208748Sraj} 204