uart_cpu_fdt.c revision 250840
1250840Smarcel/*-
2250840Smarcel * Copyright (c) 2009-2010 The FreeBSD Foundation
3250840Smarcel * All rights reserved.
4250840Smarcel *
5250840Smarcel * This software was developed by Semihalf under sponsorship from
6250840Smarcel * the FreeBSD Foundation.
7250840Smarcel *
8250840Smarcel * Redistribution and use in source and binary forms, with or without
9250840Smarcel * modification, are permitted provided that the following conditions
10250840Smarcel * are met:
11250840Smarcel * 1. Redistributions of source code must retain the above copyright
12250840Smarcel *    notice, this list of conditions and the following disclaimer.
13250840Smarcel * 2. Redistributions in binary form must reproduce the above copyright
14250840Smarcel *    notice, this list of conditions and the following disclaimer in the
15250840Smarcel *    documentation and/or other materials provided with the distribution.
16250840Smarcel *
17250840Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18250840Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19250840Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20250840Smarcel * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21250840Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22250840Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23250840Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24250840Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25250840Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26250840Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27250840Smarcel * SUCH DAMAGE.
28250840Smarcel */
29250840Smarcel
30250840Smarcel#include <sys/cdefs.h>
31250840Smarcel__FBSDID("$FreeBSD: head/sys/dev/uart/uart_cpu_fdt.c 250840 2013-05-21 03:05:49Z marcel $");
32250840Smarcel
33250840Smarcel#include <sys/param.h>
34250840Smarcel#include <sys/bus.h>
35250840Smarcel#include <sys/kernel.h>
36250840Smarcel#include <sys/module.h>
37250840Smarcel
38250840Smarcel#include <machine/bus.h>
39250840Smarcel#include <machine/fdt.h>
40250840Smarcel
41250840Smarcel#include <dev/fdt/fdt_common.h>
42250840Smarcel#include <dev/ofw/ofw_bus.h>
43250840Smarcel#include <dev/ofw/ofw_bus_subr.h>
44250840Smarcel#include <dev/uart/uart.h>
45250840Smarcel#include <dev/uart/uart_bus.h>
46250840Smarcel#include <dev/uart/uart_cpu.h>
47250840Smarcel
48250840Smarcel/*
49250840Smarcel * UART console routines.
50250840Smarcel */
51250840Smarcelbus_space_tag_t uart_bus_space_io;
52250840Smarcelbus_space_tag_t uart_bus_space_mem;
53250840Smarcel
54250840Smarcelstatic int
55250840Smarceluart_fdt_get_clock(phandle_t node, pcell_t *cell)
56250840Smarcel{
57250840Smarcel	pcell_t clock;
58250840Smarcel
59250840Smarcel	if ((OF_getprop(node, "clock-frequency", &clock,
60250840Smarcel	    sizeof(clock))) <= 0)
61250840Smarcel		return (ENXIO);
62250840Smarcel
63250840Smarcel	if (clock == 0)
64250840Smarcel		/* Try to retrieve parent 'bus-frequency' */
65250840Smarcel		/* XXX this should go to simple-bus fixup or so */
66250840Smarcel		if ((OF_getprop(OF_parent(node), "bus-frequency", &clock,
67250840Smarcel		    sizeof(clock))) <= 0)
68250840Smarcel			clock = 0;
69250840Smarcel
70250840Smarcel	*cell = fdt32_to_cpu(clock);
71250840Smarcel	return (0);
72250840Smarcel}
73250840Smarcel
74250840Smarcelstatic int
75250840Smarceluart_fdt_get_shift(phandle_t node, pcell_t *cell)
76250840Smarcel{
77250840Smarcel	pcell_t shift;
78250840Smarcel
79250840Smarcel	if ((OF_getprop(node, "reg-shift", &shift, sizeof(shift))) <= 0)
80250840Smarcel		shift = 0;
81250840Smarcel	*cell = fdt32_to_cpu(shift);
82250840Smarcel	return (0);
83250840Smarcel}
84250840Smarcel
85250840Smarcelint
86250840Smarceluart_cpu_eqres(struct uart_bas *b1, struct uart_bas *b2)
87250840Smarcel{
88250840Smarcel
89250840Smarcel	return ((b1->bsh == b2->bsh && b1->bst == b2->bst) ? 1 : 0);
90250840Smarcel}
91250840Smarcel
92250840Smarcelint
93250840Smarceluart_cpu_getdev(int devtype, struct uart_devinfo *di)
94250840Smarcel{
95250840Smarcel	char buf[64];
96250840Smarcel	struct uart_class *class;
97250840Smarcel	phandle_t node, chosen;
98250840Smarcel	pcell_t shift, br, rclk;
99250840Smarcel	u_long start, size, pbase, psize;
100250840Smarcel	int err;
101250840Smarcel
102250840Smarcel	uart_bus_space_mem = fdtbus_bs_tag;
103250840Smarcel	uart_bus_space_io = NULL;
104250840Smarcel
105250840Smarcel	/* Allow overriding the FDT uning the environment. */
106250840Smarcel	class = &uart_ns8250_class;
107250840Smarcel	err = uart_getenv(devtype, di, class);
108250840Smarcel	if (!err)
109250840Smarcel		return (0);
110250840Smarcel
111250840Smarcel	if (devtype != UART_DEV_CONSOLE)
112250840Smarcel		return (ENXIO);
113250840Smarcel
114250840Smarcel	/*
115250840Smarcel	 * Retrieve /chosen/std{in,out}.
116250840Smarcel	 */
117250840Smarcel	if ((chosen = OF_finddevice("/chosen")) == -1)
118250840Smarcel		return (ENXIO);
119250840Smarcel	if (OF_getprop(chosen, "stdin", buf, sizeof(buf)) <= 0)
120250840Smarcel		return (ENXIO);
121250840Smarcel	if ((node = OF_finddevice(buf)) == -1)
122250840Smarcel		return (ENXIO);
123250840Smarcel	if (OF_getprop(chosen, "stdout", buf, sizeof(buf)) <= 0)
124250840Smarcel		return (ENXIO);
125250840Smarcel	if (OF_finddevice(buf) != node)
126250840Smarcel		/* Only stdin == stdout is supported. */
127250840Smarcel		return (ENXIO);
128250840Smarcel	/*
129250840Smarcel	 * Retrieve serial attributes.
130250840Smarcel	 */
131250840Smarcel	uart_fdt_get_shift(node, &shift);
132250840Smarcel
133250840Smarcel	if (OF_getprop(node, "current-speed", &br, sizeof(br)) <= 0)
134250840Smarcel		br = 0;
135250840Smarcel	br = fdt32_to_cpu(br);
136250840Smarcel
137250840Smarcel	if ((err = uart_fdt_get_clock(node, &rclk)) != 0)
138250840Smarcel		return (err);
139250840Smarcel	/*
140250840Smarcel	 * Finalize configuration.
141250840Smarcel	 */
142250840Smarcel	if (fdt_is_compatible(node, "fsl,imx-uart"))
143250840Smarcel		class = &uart_imx_class;
144250840Smarcel	if (fdt_is_compatible(node, "quicc"))
145250840Smarcel		class = &uart_quicc_class;
146250840Smarcel	if (fdt_is_compatible(node, "lpc"))
147250840Smarcel		class = &uart_lpc_class;
148250840Smarcel	if (fdt_is_compatible(node, "ns16550"))
149250840Smarcel		class = &uart_ns8250_class;
150250840Smarcel	if (fdt_is_compatible(node, "arm,pl011"))
151250840Smarcel		class = &uart_pl011_class;
152250840Smarcel	if (fdt_is_compatible(node, "cadence,uart"))
153250840Smarcel		class = &uart_cdnc_class;
154250840Smarcel
155250840Smarcel	di->bas.chan = 0;
156250840Smarcel	di->bas.regshft = (u_int)shift;
157250840Smarcel	di->baudrate = br;
158250840Smarcel	di->bas.rclk = (u_int)rclk;
159250840Smarcel	di->ops = uart_getops(class);
160250840Smarcel	di->databits = 8;
161250840Smarcel	di->stopbits = 1;
162250840Smarcel	di->parity = UART_PARITY_NONE;
163250840Smarcel	di->bas.bst = uart_bus_space_mem;
164250840Smarcel
165250840Smarcel	err = fdt_regsize(node, &start, &size);
166250840Smarcel	if (err)
167250840Smarcel		return (ENXIO);
168250840Smarcel	err = fdt_get_range(OF_parent(node), 0, &pbase, &psize);
169250840Smarcel	if (err)
170250840Smarcel		pbase = 0;
171250840Smarcel
172250840Smarcel	start += pbase;
173250840Smarcel
174250840Smarcel	return (bus_space_map(di->bas.bst, start, size, 0, &di->bas.bsh));
175250840Smarcel}
176