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