1/*-
2 * Copyright (c) 2004 Marcel Moolenaar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/bus.h>
33
34#include <machine/bus.h>
35#include <machine/vmparam.h>
36
37#include <dev/uart/uart.h>
38#include <dev/uart/uart_cpu.h>
39
40#define	UART_TAG_BR	0
41#define	UART_TAG_CH	1
42#define	UART_TAG_DB	2
43#define	UART_TAG_DT	3
44#define	UART_TAG_IO	4
45#define	UART_TAG_MM	5
46#define	UART_TAG_PA	6
47#define	UART_TAG_RS	7
48#define	UART_TAG_SB	8
49#define	UART_TAG_XO	9
50
51static struct uart_class *uart_classes[] = {
52	&uart_ns8250_class,
53	&uart_sab82532_class,
54	&uart_z8530_class,
55#if defined(__arm__)
56	&uart_s3c2410_class,
57#endif
58};
59static size_t uart_nclasses = sizeof(uart_classes) / sizeof(uart_classes[0]);
60
61static bus_addr_t
62uart_parse_addr(const char **p)
63{
64	return (strtoul(*p, (char**)(uintptr_t)p, 0));
65}
66
67static struct uart_class *
68uart_parse_class(struct uart_class *class, const char **p)
69{
70	struct uart_class *uc;
71	const char *nm;
72	size_t len;
73	u_int i;
74
75	for (i = 0; i < uart_nclasses; i++) {
76		uc = uart_classes[i];
77		nm = uart_getname(uc);
78		if (nm == NULL || *nm == '\0')
79			continue;
80		len = strlen(nm);
81		if (strncmp(nm, *p, len) == 0) {
82			*p += len;
83			return (uc);
84		}
85	}
86	return (class);
87}
88
89static long
90uart_parse_long(const char **p)
91{
92	return (strtol(*p, (char**)(uintptr_t)p, 0));
93}
94
95static int
96uart_parse_parity(const char **p)
97{
98	if (!strncmp(*p, "even", 4)) {
99		*p += 4;
100		return UART_PARITY_EVEN;
101	}
102	if (!strncmp(*p, "mark", 4)) {
103		*p += 4;
104		return UART_PARITY_MARK;
105	}
106	if (!strncmp(*p, "none", 4)) {
107		*p += 4;
108		return UART_PARITY_NONE;
109	}
110	if (!strncmp(*p, "odd", 3)) {
111		*p += 3;
112		return UART_PARITY_ODD;
113	}
114	if (!strncmp(*p, "space", 5)) {
115		*p += 5;
116		return UART_PARITY_SPACE;
117	}
118	return (-1);
119}
120
121static int
122uart_parse_tag(const char **p)
123{
124	int tag;
125
126	if ((*p)[0] == 'b' && (*p)[1] == 'r') {
127		tag = UART_TAG_BR;
128		goto out;
129	}
130	if ((*p)[0] == 'c' && (*p)[1] == 'h') {
131		tag = UART_TAG_CH;
132		goto out;
133	}
134	if ((*p)[0] == 'd' && (*p)[1] == 'b') {
135		tag = UART_TAG_DB;
136		goto out;
137	}
138	if ((*p)[0] == 'd' && (*p)[1] == 't') {
139		tag = UART_TAG_DT;
140		goto out;
141	}
142	if ((*p)[0] == 'i' && (*p)[1] == 'o') {
143		tag = UART_TAG_IO;
144		goto out;
145	}
146	if ((*p)[0] == 'm' && (*p)[1] == 'm') {
147		tag = UART_TAG_MM;
148		goto out;
149	}
150	if ((*p)[0] == 'p' && (*p)[1] == 'a') {
151		tag = UART_TAG_PA;
152		goto out;
153	}
154	if ((*p)[0] == 'r' && (*p)[1] == 's') {
155		tag = UART_TAG_RS;
156		goto out;
157	}
158	if ((*p)[0] == 's' && (*p)[1] == 'b') {
159		tag = UART_TAG_SB;
160		goto out;
161	}
162	if ((*p)[0] == 'x' && (*p)[1] == 'o') {
163		tag = UART_TAG_XO;
164		goto out;
165	}
166	return (-1);
167
168out:
169	*p += 2;
170	if ((*p)[0] != ':')
171		return (-1);
172	(*p)++;
173	return (tag);
174}
175
176/*
177 * Parse a device specification. The specification is a list of attributes
178 * separated by commas. Each attribute is a tag-value pair with the tag and
179 * value separated by a colon. Supported tags are:
180 *
181 *	br = Baudrate
182 *	ch = Channel
183 *	db = Data bits
184 *	dt = Device type
185 *	io = I/O port address
186 *	mm = Memory mapped I/O address
187 *	pa = Parity
188 *	rs = Register shift
189 *	sb = Stopbits
190 *	xo = Device clock (xtal oscillator)
191 *
192 * The io and mm tags are mutually exclusive.
193 */
194
195int
196uart_getenv(int devtype, struct uart_devinfo *di, struct uart_class *class)
197{
198	const char *spec;
199	bus_addr_t addr = ~0U;
200	int error;
201
202	/*
203	 * All uart_class references are weak. Make sure the default
204	 * device class has been compiled-in.
205	 */
206	if (class == NULL)
207		return (ENXIO);
208
209	/*
210	 * Check the environment variables "hw.uart.console" and
211	 * "hw.uart.dbgport". These variables, when present, specify
212	 * which UART port is to be used as serial console or debug
213	 * port (resp).
214	 */
215	if (devtype == UART_DEV_CONSOLE)
216		spec = getenv("hw.uart.console");
217	else if (devtype == UART_DEV_DBGPORT)
218		spec = getenv("hw.uart.dbgport");
219	else
220		spec = NULL;
221	if (spec == NULL)
222		return (ENXIO);
223
224	/* Set defaults. */
225	di->bas.chan = 0;
226	di->bas.regshft = 0;
227	di->bas.rclk = 0;
228	di->baudrate = 0;
229	di->databits = 8;
230	di->stopbits = 1;
231	di->parity = UART_PARITY_NONE;
232
233	/* Parse the attributes. */
234	while (1) {
235		switch (uart_parse_tag(&spec)) {
236		case UART_TAG_BR:
237			di->baudrate = uart_parse_long(&spec);
238			break;
239		case UART_TAG_CH:
240			di->bas.chan = uart_parse_long(&spec);
241			break;
242		case UART_TAG_DB:
243			di->databits = uart_parse_long(&spec);
244			break;
245		case UART_TAG_DT:
246			class = uart_parse_class(class, &spec);
247			break;
248		case UART_TAG_IO:
249			di->bas.bst = uart_bus_space_io;
250			addr = uart_parse_addr(&spec);
251			break;
252		case UART_TAG_MM:
253			di->bas.bst = uart_bus_space_mem;
254			addr = uart_parse_addr(&spec);
255			break;
256		case UART_TAG_PA:
257			di->parity = uart_parse_parity(&spec);
258			break;
259		case UART_TAG_RS:
260			di->bas.regshft = uart_parse_long(&spec);
261			break;
262		case UART_TAG_SB:
263			di->stopbits = uart_parse_long(&spec);
264			break;
265		case UART_TAG_XO:
266			di->bas.rclk = uart_parse_long(&spec);
267			break;
268		default:
269			return (EINVAL);
270		}
271		if (*spec == '\0')
272			break;
273		if (*spec != ',')
274			return (EINVAL);
275		spec++;
276	}
277
278	/*
279	 * If we still have an invalid address, the specification must be
280	 * missing an I/O port or memory address. We don't like that.
281	 */
282	if (addr == ~0U)
283		return (EINVAL);
284
285	/*
286	 * Accept only the well-known baudrates. Any invalid baudrate
287	 * is silently replaced with a 0-valued baudrate. The 0 baudrate
288	 * has special meaning. It means that we're not supposed to
289	 * program the baudrate and simply communicate with whatever
290	 * speed the hardware is currently programmed for.
291	 */
292	if (di->baudrate >= 19200) {
293		if (di->baudrate % 19200)
294			di->baudrate = 0;
295	} else if (di->baudrate >= 1200) {
296		if (di->baudrate % 1200)
297			di->baudrate = 0;
298	} else if (di->baudrate > 0) {
299		if (di->baudrate % 75)
300			di->baudrate = 0;
301	} else
302		di->baudrate = 0;
303
304	/* Set the ops and create a bus space handle. */
305	di->ops = uart_getops(class);
306	error = bus_space_map(di->bas.bst, addr, uart_getrange(class), 0,
307	    &di->bas.bsh);
308	return (error);
309}
310