uart_cpu_fdt.c revision 259357
150479Speter/*- 250186Sdwhite * Copyright (c) 2009-2010 The FreeBSD Foundation 391853Sluigi * All rights reserved. 491853Sluigi * 591853Sluigi * This software was developed by Semihalf under sponsorship from 691853Sluigi * the FreeBSD Foundation. 791853Sluigi * 891853Sluigi * Redistribution and use in source and binary forms, with or without 991853Sluigi * modification, are permitted provided that the following conditions 1091853Sluigi * are met: 1150186Sdwhite * 1. Redistributions of source code must retain the above copyright 1291853Sluigi * notice, this list of conditions and the following disclaimer. 1391853Sluigi * 2. Redistributions in binary form must reproduce the above copyright 1450186Sdwhite * notice, this list of conditions and the following disclaimer in the 1550186Sdwhite * documentation and/or other materials provided with the distribution. 1650186Sdwhite * 1750186Sdwhite * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1850186Sdwhite * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1950186Sdwhite * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2050186Sdwhite * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2150186Sdwhite * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2250186Sdwhite * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2350186Sdwhite * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2450186Sdwhite * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2550186Sdwhite * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2691853Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2791853Sluigi * SUCH DAMAGE. 2891853Sluigi */ 2991853Sluigi 3050186Sdwhite#include <sys/cdefs.h> 3191853Sluigi__FBSDID("$FreeBSD: stable/10/sys/dev/uart/uart_cpu_fdt.c 259357 2013-12-13 22:48:01Z ian $"); 3250186Sdwhite 3350186Sdwhite#include "opt_platform.h" 3450186Sdwhite 3550186Sdwhite#include <sys/param.h> 3650186Sdwhite#include <sys/bus.h> 3750186Sdwhite#include <sys/kernel.h> 3850186Sdwhite#include <sys/module.h> 3950186Sdwhite 4050186Sdwhite#include <machine/bus.h> 4150186Sdwhite#include <machine/fdt.h> 4250186Sdwhite 4350186Sdwhite#include <dev/fdt/fdt_common.h> 4450186Sdwhite#include <dev/ofw/ofw_bus.h> 4550186Sdwhite#include <dev/ofw/ofw_bus_subr.h> 4650186Sdwhite#include <dev/uart/uart.h> 4750186Sdwhite#include <dev/uart/uart_bus.h> 4850186Sdwhite#include <dev/uart/uart_cpu.h> 4950186Sdwhite 5050186Sdwhite/* 5150186Sdwhite * UART console routines. 5250186Sdwhite */ 5350186Sdwhitebus_space_tag_t uart_bus_space_io; 5450186Sdwhitebus_space_tag_t uart_bus_space_mem; 5550186Sdwhite 5650186Sdwhitestatic int 5750186Sdwhiteuart_fdt_get_clock(phandle_t node, pcell_t *cell) 5850186Sdwhite{ 5950186Sdwhite pcell_t clock; 6050186Sdwhite 6150186Sdwhite if ((OF_getprop(node, "clock-frequency", &clock, 6250186Sdwhite sizeof(clock))) <= 0) 6350186Sdwhite return (ENXIO); 6450186Sdwhite 6550186Sdwhite if (clock == 0) 6650186Sdwhite /* Try to retrieve parent 'bus-frequency' */ 6750186Sdwhite /* XXX this should go to simple-bus fixup or so */ 6891853Sluigi if ((OF_getprop(OF_parent(node), "bus-frequency", &clock, 6991853Sluigi sizeof(clock))) <= 0) 7050186Sdwhite clock = 0; 7191853Sluigi 7250186Sdwhite *cell = fdt32_to_cpu(clock); 7350186Sdwhite return (0); 7450186Sdwhite} 7550186Sdwhite 7650186Sdwhitestatic int 7750186Sdwhiteuart_fdt_get_shift(phandle_t node, pcell_t *cell) 7850186Sdwhite{ 7950186Sdwhite pcell_t shift; 8050186Sdwhite 8150186Sdwhite if ((OF_getprop(node, "reg-shift", &shift, sizeof(shift))) <= 0) 8250186Sdwhite shift = 0; 8350186Sdwhite *cell = fdt32_to_cpu(shift); 8450186Sdwhite return (0); 8550186Sdwhite} 8650186Sdwhite 8750186Sdwhiteint 8850186Sdwhiteuart_cpu_eqres(struct uart_bas *b1, struct uart_bas *b2) 8950186Sdwhite{ 9050186Sdwhite 9150186Sdwhite return ((b1->bsh == b2->bsh && b1->bst == b2->bst) ? 1 : 0); 9250186Sdwhite} 9350186Sdwhite 9450186Sdwhiteint 9550186Sdwhiteuart_cpu_getdev(int devtype, struct uart_devinfo *di) 9650186Sdwhite{ 9750186Sdwhite char buf[64]; 9850186Sdwhite const struct ofw_compat_data *cd; 9950186Sdwhite struct uart_class *class; 10050186Sdwhite phandle_t node, chosen; 10150186Sdwhite pcell_t shift, br, rclk; 10250186Sdwhite u_long start, size, pbase, psize; 10350186Sdwhite int err; 10450186Sdwhite 10550186Sdwhite uart_bus_space_mem = fdtbus_bs_tag; 10650186Sdwhite uart_bus_space_io = NULL; 10750186Sdwhite 10850186Sdwhite /* Allow overriding the FDT uning the environment. */ 10950186Sdwhite class = &uart_ns8250_class; 11050186Sdwhite err = uart_getenv(devtype, di, class); 11150186Sdwhite if (!err) 11250186Sdwhite return (0); 11350186Sdwhite 11450186Sdwhite if (devtype != UART_DEV_CONSOLE) 11550186Sdwhite return (ENXIO); 11650186Sdwhite 11750186Sdwhite /* 11850186Sdwhite * Retrieve /chosen/std{in,out}. 11950186Sdwhite */ 12050186Sdwhite if ((chosen = OF_finddevice("/chosen")) == -1) 12150186Sdwhite return (ENXIO); 12250186Sdwhite if (OF_getprop(chosen, "stdin", buf, sizeof(buf)) <= 0) 12350186Sdwhite return (ENXIO); 12450186Sdwhite if ((node = OF_finddevice(buf)) == -1) 12550186Sdwhite return (ENXIO); 12650186Sdwhite if (OF_getprop(chosen, "stdout", buf, sizeof(buf)) <= 0) 12791853Sluigi return (ENXIO); 12891853Sluigi if (OF_finddevice(buf) != node) 12950186Sdwhite /* Only stdin == stdout is supported. */ 13091853Sluigi return (ENXIO); 13191853Sluigi /* 13291853Sluigi * Retrieve serial attributes. 13391853Sluigi */ 13491853Sluigi uart_fdt_get_shift(node, &shift); 13591853Sluigi 13650186Sdwhite if (OF_getprop(node, "current-speed", &br, sizeof(br)) <= 0) 13791853Sluigi br = 0; 13891853Sluigi br = fdt32_to_cpu(br); 13991853Sluigi 14091853Sluigi if ((err = uart_fdt_get_clock(node, &rclk)) != 0) 14191853Sluigi return (err); 14291853Sluigi /* 143 * Finalize configuration. 144 */ 145 for (cd = uart_fdt_compat_data; cd->ocd_str != NULL; ++cd) { 146 if (fdt_is_compatible(node, cd->ocd_str)) 147 break; 148 } 149 if (cd->ocd_str == NULL) 150 return (ENXIO); 151 class = (struct uart_class *)cd->ocd_data; 152 153 di->bas.chan = 0; 154 di->bas.regshft = (u_int)shift; 155 di->baudrate = br; 156 di->bas.rclk = (u_int)rclk; 157 di->ops = uart_getops(class); 158 di->databits = 8; 159 di->stopbits = 1; 160 di->parity = UART_PARITY_NONE; 161 di->bas.bst = uart_bus_space_mem; 162 163 err = fdt_regsize(node, &start, &size); 164 if (err) 165 return (ENXIO); 166 err = fdt_get_range(OF_parent(node), 0, &pbase, &psize); 167 if (err) 168 pbase = 0; 169 170 start += pbase; 171 172 return (bus_space_map(di->bas.bst, start, size, 0, &di->bas.bsh)); 173} 174