uart_cpu_fdt.c revision 257183
1185377Ssam/*-
2185377Ssam * Copyright (c) 2009-2010 The FreeBSD Foundation
3185377Ssam * All rights reserved.
4185377Ssam *
5185377Ssam * This software was developed by Semihalf under sponsorship from
6185377Ssam * the FreeBSD Foundation.
7185377Ssam *
8185377Ssam * Redistribution and use in source and binary forms, with or without
9185377Ssam * modification, are permitted provided that the following conditions
10185377Ssam * are met:
11185377Ssam * 1. Redistributions of source code must retain the above copyright
12185377Ssam *    notice, this list of conditions and the following disclaimer.
13185377Ssam * 2. Redistributions in binary form must reproduce the above copyright
14185377Ssam *    notice, this list of conditions and the following disclaimer in the
15185377Ssam *    documentation and/or other materials provided with the distribution.
16185377Ssam *
17204644Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18185377Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19185377Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20185377Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21185377Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22185377Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23185377Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24185377Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25185377Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26185377Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27185377Ssam * SUCH DAMAGE.
28185377Ssam */
29185377Ssam
30185377Ssam#include <sys/cdefs.h>
31185377Ssam__FBSDID("$FreeBSD: head/sys/dev/uart/uart_cpu_fdt.c 257183 2013-10-26 18:25:55Z nwhitehorn $");
32185377Ssam
33185377Ssam#include <sys/param.h>
34185377Ssam#include <sys/bus.h>
35185377Ssam#include <sys/kernel.h>
36185377Ssam#include <sys/module.h>
37185377Ssam#include <sys/systm.h>
38185377Ssam
39185377Ssam#include <vm/vm.h>
40185377Ssam#include <vm/pmap.h>
41185377Ssam
42185377Ssam#include <machine/bus.h>
43185377Ssam#include <machine/fdt.h>
44185377Ssam
45185377Ssam#include <dev/fdt/fdt_common.h>
46185377Ssam#include <dev/ofw/ofw_bus.h>
47185377Ssam#include <dev/ofw/ofw_bus_subr.h>
48185377Ssam#include <dev/uart/uart.h>
49185377Ssam#include <dev/uart/uart_bus.h>
50185377Ssam#include <dev/uart/uart_cpu.h>
51185377Ssam
52185377Ssam/*
53185377Ssam * UART console routines.
54185377Ssam */
55185377Ssambus_space_tag_t uart_bus_space_io;
56185377Ssambus_space_tag_t uart_bus_space_mem;
57185377Ssam
58185377Ssamstatic int
59185377Ssamuart_fdt_get_clock(phandle_t node, pcell_t *cell)
60185377Ssam{
61185377Ssam	pcell_t clock;
62185377Ssam
63185377Ssam	if ((OF_getprop(node, "clock-frequency", &clock,
64185377Ssam	    sizeof(clock))) <= 0)
65185377Ssam		return (ENXIO);
66185377Ssam
67185377Ssam	if (clock == 0)
68185377Ssam		/* Try to retrieve parent 'bus-frequency' */
69185377Ssam		/* XXX this should go to simple-bus fixup or so */
70185377Ssam		if ((OF_getprop(OF_parent(node), "bus-frequency", &clock,
71185377Ssam		    sizeof(clock))) <= 0)
72185377Ssam			clock = 0;
73185377Ssam
74185377Ssam	*cell = fdt32_to_cpu(clock);
75185377Ssam	return (0);
76185377Ssam}
77185377Ssam
78185377Ssamstatic int
79185377Ssamuart_fdt_get_shift(phandle_t node, pcell_t *cell)
80185377Ssam{
81185377Ssam	pcell_t shift;
82185377Ssam
83185377Ssam	if ((OF_getprop(node, "reg-shift", &shift, sizeof(shift))) <= 0)
84185377Ssam		shift = 0;
85185377Ssam	*cell = fdt32_to_cpu(shift);
86185377Ssam	return (0);
87185377Ssam}
88185377Ssam
89185377Ssamint
90185377Ssamuart_cpu_eqres(struct uart_bas *b1, struct uart_bas *b2)
91185377Ssam{
92185377Ssam
93185377Ssam	if (b1->bst != b2->bst)
94185377Ssam		return (0);
95185377Ssam	if (pmap_kextract(b1->bsh) == 0)
96185377Ssam		return (0);
97185377Ssam	if (pmap_kextract(b2->bsh) == 0)
98185377Ssam		return (0);
99185377Ssam	return ((pmap_kextract(b1->bsh) == pmap_kextract(b2->bsh)) ? 1 : 0);
100185377Ssam}
101185377Ssam
102185377Ssamstatic int
103185377Ssamphandle_chosen_propdev(phandle_t chosen, const char *name, phandle_t *node)
104185377Ssam{
105185377Ssam	char buf[64];
106185377Ssam
107185377Ssam	if (OF_getprop(chosen, name, buf, sizeof(buf)) <= 0)
108185377Ssam		return (ENXIO);
109185377Ssam	if ((*node = OF_finddevice(buf)) == -1)
110185377Ssam		return (ENXIO);
111185377Ssam
112185377Ssam	return (0);
113185377Ssam}
114185377Ssam
115185377Ssamint
116185377Ssamuart_cpu_getdev(int devtype, struct uart_devinfo *di)
117185377Ssam{
118185377Ssam	const char *propnames[] = {"stdout-path", "linux,stdout-path", "stdout",
119185377Ssam	    "stdin-path", "stdin", NULL};
120185377Ssam	const char **name;
121185377Ssam	struct uart_class *class;
122185377Ssam	phandle_t node, chosen;
123185377Ssam	pcell_t shift, br, rclk;
124185377Ssam	u_long start, size, pbase, psize;
125185377Ssam	int err;
126185377Ssam
127185377Ssam	uart_bus_space_mem = fdtbus_bs_tag;
128185377Ssam	uart_bus_space_io = NULL;
129185377Ssam
130185377Ssam	/* Allow overriding the FDT using the environment. */
131185377Ssam	class = &uart_ns8250_class;
132185377Ssam	err = uart_getenv(devtype, di, class);
133185377Ssam	if (!err)
134185377Ssam		return (0);
135185377Ssam
136185377Ssam	if (devtype != UART_DEV_CONSOLE)
137185377Ssam		return (ENXIO);
138185377Ssam
139185377Ssam	/*
140185377Ssam	 * Retrieve /chosen/std{in,out}.
141185377Ssam	 */
142185377Ssam	if ((chosen = OF_finddevice("/chosen")) == -1)
143185377Ssam		return (ENXIO);
144185377Ssam	for (name = propnames; *name != NULL; name++) {
145185377Ssam		if (phandle_chosen_propdev(chosen, *name, &node) == 0)
146185377Ssam			break;
147185377Ssam	}
148185377Ssam	if (*name == NULL)
149185377Ssam		return (ENXIO);
150185377Ssam	/*
151185377Ssam	 * Retrieve serial attributes.
152185377Ssam	 */
153185377Ssam	uart_fdt_get_shift(node, &shift);
154185377Ssam
155185377Ssam	if (OF_getprop(node, "current-speed", &br, sizeof(br)) <= 0)
156185377Ssam		br = 0;
157185377Ssam	br = fdt32_to_cpu(br);
158185377Ssam
159185377Ssam	if ((err = uart_fdt_get_clock(node, &rclk)) != 0)
160185377Ssam		return (err);
161185377Ssam	/*
162185377Ssam	 * Finalize configuration.
163185377Ssam	 */
164185377Ssam	if (fdt_is_compatible(node, "fsl,imx-uart"))
165185377Ssam		class = &uart_imx_class;
166185377Ssam	else if (fdt_is_compatible(node, "quicc"))
167185377Ssam		class = &uart_quicc_class;
168185377Ssam	else if (fdt_is_compatible(node, "lpc"))
169185377Ssam		class = &uart_lpc_class;
170185377Ssam	else if (fdt_is_compatible(node, "arm,pl011"))
171185377Ssam		class = &uart_pl011_class;
172185377Ssam	else if (fdt_is_compatible(node, "exynos"))
173185377Ssam		class = &uart_s3c2410_class;
174185377Ssam	else if (fdt_is_compatible(node, "cadence,uart"))
175185377Ssam		class = &uart_cdnc_class;
176185377Ssam	else if (fdt_is_compatible(node, "ti,ns16550"))
177185377Ssam		class = &uart_ti8250_class;
178185377Ssam	else if (fdt_is_compatible(node, "ns16550"))
179185377Ssam		class = &uart_ns8250_class;
180185377Ssam
181185377Ssam	di->bas.chan = 0;
182185377Ssam	di->bas.regshft = (u_int)shift;
183185377Ssam	di->baudrate = br;
184185377Ssam	di->bas.rclk = (u_int)rclk;
185185377Ssam	di->ops = uart_getops(class);
186185377Ssam	di->databits = 8;
187185377Ssam	di->stopbits = 1;
188185377Ssam	di->parity = UART_PARITY_NONE;
189185377Ssam	di->bas.bst = uart_bus_space_mem;
190185377Ssam
191185377Ssam	err = fdt_regsize(node, &start, &size);
192185377Ssam	if (err)
193185377Ssam		return (ENXIO);
194185377Ssam	err = fdt_get_range(OF_parent(node), 0, &pbase, &psize);
195185377Ssam	if (err)
196185377Ssam		pbase = 0;
197185377Ssam
198185377Ssam	start += pbase;
199185377Ssam
200185377Ssam	return (bus_space_map(di->bas.bst, start, size, 0, &di->bas.bsh));
201185377Ssam}
202185377Ssam