1139749Simp/*-
2127215Smarcel * Copyright (c) 2004 Marcel Moolenaar
3127215Smarcel * All rights reserved.
4127215Smarcel *
5127215Smarcel * Redistribution and use in source and binary forms, with or without
6127215Smarcel * modification, are permitted provided that the following conditions
7127215Smarcel * are met:
8127215Smarcel *
9127215Smarcel * 1. Redistributions of source code must retain the above copyright
10127215Smarcel *    notice, this list of conditions and the following disclaimer.
11127215Smarcel * 2. Redistributions in binary form must reproduce the above copyright
12127215Smarcel *    notice, this list of conditions and the following disclaimer in the
13127215Smarcel *    documentation and/or other materials provided with the distribution.
14127215Smarcel *
15127215Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16127215Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17127215Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18127215Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19127215Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20127215Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21127215Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22127215Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23127215Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24127215Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25127215Smarcel */
26127215Smarcel
27127215Smarcel#include <sys/cdefs.h>
28127215Smarcel__FBSDID("$FreeBSD$");
29127215Smarcel
30127215Smarcel#include <sys/param.h>
31127215Smarcel#include <sys/systm.h>
32127215Smarcel#include <sys/bus.h>
33127215Smarcel
34127215Smarcel#include <machine/bus.h>
35127215Smarcel#include <machine/vmparam.h>
36127215Smarcel
37127215Smarcel#include <dev/uart/uart.h>
38127215Smarcel#include <dev/uart/uart_cpu.h>
39127215Smarcel
40127215Smarcel#define	UART_TAG_BR	0
41127215Smarcel#define	UART_TAG_CH	1
42127215Smarcel#define	UART_TAG_DB	2
43127215Smarcel#define	UART_TAG_DT	3
44127215Smarcel#define	UART_TAG_IO	4
45127215Smarcel#define	UART_TAG_MM	5
46127215Smarcel#define	UART_TAG_PA	6
47127215Smarcel#define	UART_TAG_RS	7
48127215Smarcel#define	UART_TAG_SB	8
49127215Smarcel#define	UART_TAG_XO	9
50127215Smarcel
51168281Smarcelstatic struct uart_class *uart_classes[] = {
52168281Smarcel	&uart_ns8250_class,
53168281Smarcel	&uart_sab82532_class,
54168281Smarcel	&uart_z8530_class,
55249765Snyan#if defined(__arm__)
56239278Sgonzo	&uart_lpc_class,
57252394Sray	&uart_s3c2410_class,
58249765Snyan#endif
59168281Smarcel};
60168281Smarcelstatic size_t uart_nclasses = sizeof(uart_classes) / sizeof(uart_classes[0]);
61168281Smarcel
62127215Smarcelstatic bus_addr_t
63228468Seduart_parse_addr(const char **p)
64127215Smarcel{
65127215Smarcel	return (strtoul(*p, (char**)(uintptr_t)p, 0));
66127215Smarcel}
67127215Smarcel
68168281Smarcelstatic struct uart_class *
69228468Seduart_parse_class(struct uart_class *class, const char **p)
70168281Smarcel{
71168281Smarcel	struct uart_class *uc;
72168281Smarcel	const char *nm;
73168281Smarcel	size_t len;
74168281Smarcel	u_int i;
75168281Smarcel
76168281Smarcel	for (i = 0; i < uart_nclasses; i++) {
77168281Smarcel		uc = uart_classes[i];
78168281Smarcel		nm = uart_getname(uc);
79168281Smarcel		if (nm == NULL || *nm == '\0')
80168281Smarcel			continue;
81168281Smarcel		len = strlen(nm);
82168281Smarcel		if (strncmp(nm, *p, len) == 0) {
83168281Smarcel			*p += len;
84168281Smarcel			return (uc);
85168281Smarcel		}
86168281Smarcel	}
87168281Smarcel	return (class);
88168281Smarcel}
89168281Smarcel
90127215Smarcelstatic long
91228468Seduart_parse_long(const char **p)
92127215Smarcel{
93127215Smarcel	return (strtol(*p, (char**)(uintptr_t)p, 0));
94127215Smarcel}
95127215Smarcel
96127215Smarcelstatic int
97228468Seduart_parse_parity(const char **p)
98127215Smarcel{
99127215Smarcel	if (!strncmp(*p, "even", 4)) {
100127215Smarcel		*p += 4;
101127215Smarcel		return UART_PARITY_EVEN;
102127215Smarcel	}
103127215Smarcel	if (!strncmp(*p, "mark", 4)) {
104127215Smarcel		*p += 4;
105127215Smarcel		return UART_PARITY_MARK;
106127215Smarcel	}
107127215Smarcel	if (!strncmp(*p, "none", 4)) {
108127215Smarcel		*p += 4;
109127215Smarcel		return UART_PARITY_NONE;
110127215Smarcel	}
111127215Smarcel	if (!strncmp(*p, "odd", 3)) {
112127215Smarcel		*p += 3;
113127215Smarcel		return UART_PARITY_ODD;
114127215Smarcel	}
115127215Smarcel	if (!strncmp(*p, "space", 5)) {
116127215Smarcel		*p += 5;
117127215Smarcel		return UART_PARITY_SPACE;
118127215Smarcel	}
119127215Smarcel	return (-1);
120127215Smarcel}
121127215Smarcel
122127215Smarcelstatic int
123228468Seduart_parse_tag(const char **p)
124127215Smarcel{
125127215Smarcel	int tag;
126127215Smarcel
127127215Smarcel	if ((*p)[0] == 'b' && (*p)[1] == 'r') {
128127215Smarcel		tag = UART_TAG_BR;
129127215Smarcel		goto out;
130127215Smarcel	}
131127215Smarcel	if ((*p)[0] == 'c' && (*p)[1] == 'h') {
132127215Smarcel		tag = UART_TAG_CH;
133127215Smarcel		goto out;
134127215Smarcel	}
135127215Smarcel	if ((*p)[0] == 'd' && (*p)[1] == 'b') {
136127215Smarcel		tag = UART_TAG_DB;
137127215Smarcel		goto out;
138127215Smarcel	}
139127215Smarcel	if ((*p)[0] == 'd' && (*p)[1] == 't') {
140127215Smarcel		tag = UART_TAG_DT;
141127215Smarcel		goto out;
142127215Smarcel	}
143127215Smarcel	if ((*p)[0] == 'i' && (*p)[1] == 'o') {
144127215Smarcel		tag = UART_TAG_IO;
145127215Smarcel		goto out;
146127215Smarcel	}
147127215Smarcel	if ((*p)[0] == 'm' && (*p)[1] == 'm') {
148127215Smarcel		tag = UART_TAG_MM;
149127215Smarcel		goto out;
150127215Smarcel	}
151127215Smarcel	if ((*p)[0] == 'p' && (*p)[1] == 'a') {
152127215Smarcel		tag = UART_TAG_PA;
153127215Smarcel		goto out;
154127215Smarcel	}
155127215Smarcel	if ((*p)[0] == 'r' && (*p)[1] == 's') {
156127215Smarcel		tag = UART_TAG_RS;
157127215Smarcel		goto out;
158127215Smarcel	}
159127215Smarcel	if ((*p)[0] == 's' && (*p)[1] == 'b') {
160127215Smarcel		tag = UART_TAG_SB;
161127215Smarcel		goto out;
162127215Smarcel	}
163127215Smarcel	if ((*p)[0] == 'x' && (*p)[1] == 'o') {
164127215Smarcel		tag = UART_TAG_XO;
165127215Smarcel		goto out;
166127215Smarcel	}
167127215Smarcel	return (-1);
168127215Smarcel
169127215Smarcelout:
170127215Smarcel	*p += 2;
171127215Smarcel	if ((*p)[0] != ':')
172127215Smarcel		return (-1);
173127215Smarcel	(*p)++;
174127215Smarcel	return (tag);
175127215Smarcel}
176127215Smarcel
177127215Smarcel/*
178127215Smarcel * Parse a device specification. The specification is a list of attributes
179215034Sbrucec * separated by commas. Each attribute is a tag-value pair with the tag and
180215034Sbrucec * value separated by a colon. Supported tags are:
181127215Smarcel *
182127215Smarcel *	br = Baudrate
183127215Smarcel *	ch = Channel
184127215Smarcel *	db = Data bits
185127215Smarcel *	dt = Device type
186127215Smarcel *	io = I/O port address
187127215Smarcel *	mm = Memory mapped I/O address
188127215Smarcel *	pa = Parity
189127215Smarcel *	rs = Register shift
190127215Smarcel *	sb = Stopbits
191127215Smarcel *	xo = Device clock (xtal oscillator)
192127215Smarcel *
193127215Smarcel * The io and mm tags are mutually exclusive.
194127215Smarcel */
195127215Smarcel
196127215Smarcelint
197168281Smarceluart_getenv(int devtype, struct uart_devinfo *di, struct uart_class *class)
198127215Smarcel{
199228468Sed	const char *spec;
200127215Smarcel	bus_addr_t addr = ~0U;
201168281Smarcel	int error;
202127215Smarcel
203127215Smarcel	/*
204168281Smarcel	 * All uart_class references are weak. Make sure the default
205168281Smarcel	 * device class has been compiled-in.
206168281Smarcel	 */
207168281Smarcel	if (class == NULL)
208168281Smarcel		return (ENXIO);
209168281Smarcel
210168281Smarcel	/*
211127215Smarcel	 * Check the environment variables "hw.uart.console" and
212127215Smarcel	 * "hw.uart.dbgport". These variables, when present, specify
213127215Smarcel	 * which UART port is to be used as serial console or debug
214127215Smarcel	 * port (resp).
215127215Smarcel	 */
216127215Smarcel	if (devtype == UART_DEV_CONSOLE)
217127215Smarcel		spec = getenv("hw.uart.console");
218127215Smarcel	else if (devtype == UART_DEV_DBGPORT)
219127215Smarcel		spec = getenv("hw.uart.dbgport");
220127215Smarcel	else
221127226Smarcel		spec = NULL;
222127226Smarcel	if (spec == NULL)
223127215Smarcel		return (ENXIO);
224127215Smarcel
225127215Smarcel	/* Set defaults. */
226127215Smarcel	di->bas.chan = 0;
227127215Smarcel	di->bas.regshft = 0;
228127215Smarcel	di->bas.rclk = 0;
229127215Smarcel	di->baudrate = 0;
230127215Smarcel	di->databits = 8;
231127215Smarcel	di->stopbits = 1;
232127215Smarcel	di->parity = UART_PARITY_NONE;
233127215Smarcel
234127215Smarcel	/* Parse the attributes. */
235127215Smarcel	while (1) {
236127215Smarcel		switch (uart_parse_tag(&spec)) {
237127215Smarcel		case UART_TAG_BR:
238127215Smarcel			di->baudrate = uart_parse_long(&spec);
239127215Smarcel			break;
240127215Smarcel		case UART_TAG_CH:
241127215Smarcel			di->bas.chan = uart_parse_long(&spec);
242127215Smarcel			break;
243127215Smarcel		case UART_TAG_DB:
244127215Smarcel			di->databits = uart_parse_long(&spec);
245127215Smarcel			break;
246127215Smarcel		case UART_TAG_DT:
247168281Smarcel			class = uart_parse_class(class, &spec);
248127215Smarcel			break;
249127215Smarcel		case UART_TAG_IO:
250127215Smarcel			di->bas.bst = uart_bus_space_io;
251127215Smarcel			addr = uart_parse_addr(&spec);
252127215Smarcel			break;
253127215Smarcel		case UART_TAG_MM:
254127215Smarcel			di->bas.bst = uart_bus_space_mem;
255127215Smarcel			addr = uart_parse_addr(&spec);
256127215Smarcel			break;
257127215Smarcel		case UART_TAG_PA:
258127215Smarcel			di->parity = uart_parse_parity(&spec);
259127215Smarcel			break;
260127215Smarcel		case UART_TAG_RS:
261127215Smarcel			di->bas.regshft = uart_parse_long(&spec);
262127215Smarcel			break;
263127215Smarcel		case UART_TAG_SB:
264127215Smarcel			di->stopbits = uart_parse_long(&spec);
265127215Smarcel			break;
266127215Smarcel		case UART_TAG_XO:
267127215Smarcel			di->bas.rclk = uart_parse_long(&spec);
268127215Smarcel			break;
269127215Smarcel		default:
270127215Smarcel			return (EINVAL);
271127215Smarcel		}
272127215Smarcel		if (*spec == '\0')
273127215Smarcel			break;
274127215Smarcel		if (*spec != ',')
275127215Smarcel			return (EINVAL);
276127215Smarcel		spec++;
277127215Smarcel	}
278127215Smarcel
279127215Smarcel	/*
280127215Smarcel	 * If we still have an invalid address, the specification must be
281127215Smarcel	 * missing an I/O port or memory address. We don't like that.
282127215Smarcel	 */
283127215Smarcel	if (addr == ~0U)
284127215Smarcel		return (EINVAL);
285137704Smarcel
286137704Smarcel	/*
287137704Smarcel	 * Accept only the well-known baudrates. Any invalid baudrate
288137704Smarcel	 * is silently replaced with a 0-valued baudrate. The 0 baudrate
289137704Smarcel	 * has special meaning. It means that we're not supposed to
290137704Smarcel	 * program the baudrate and simply communicate with whatever
291137704Smarcel	 * speed the hardware is currently programmed for.
292137704Smarcel	 */
293137704Smarcel	if (di->baudrate >= 19200) {
294137704Smarcel		if (di->baudrate % 19200)
295137704Smarcel			di->baudrate = 0;
296137704Smarcel	} else if (di->baudrate >= 1200) {
297137704Smarcel		if (di->baudrate % 1200)
298137704Smarcel			di->baudrate = 0;
299137704Smarcel	} else if (di->baudrate > 0) {
300137704Smarcel		if (di->baudrate % 75)
301137704Smarcel			di->baudrate = 0;
302137704Smarcel	} else
303137704Smarcel		di->baudrate = 0;
304137704Smarcel
305168281Smarcel	/* Set the ops and create a bus space handle. */
306168281Smarcel	di->ops = uart_getops(class);
307168281Smarcel	error = bus_space_map(di->bas.bst, addr, uart_getrange(class), 0,
308168281Smarcel	    &di->bas.bsh);
309168281Smarcel	return (error);
310127215Smarcel}
311