uart_subr.c revision 153363
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: head/sys/dev/uart/uart_subr.c 153363 2005-12-12 21:00:58Z imp $");
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 bus_addr_t
52uart_parse_addr(__const char **p)
53{
54	return (strtoul(*p, (char**)(uintptr_t)p, 0));
55}
56
57static long
58uart_parse_long(__const char **p)
59{
60	return (strtol(*p, (char**)(uintptr_t)p, 0));
61}
62
63static int
64uart_parse_parity(__const char **p)
65{
66	if (!strncmp(*p, "even", 4)) {
67		*p += 4;
68		return UART_PARITY_EVEN;
69	}
70	if (!strncmp(*p, "mark", 4)) {
71		*p += 4;
72		return UART_PARITY_MARK;
73	}
74	if (!strncmp(*p, "none", 4)) {
75		*p += 4;
76		return UART_PARITY_NONE;
77	}
78	if (!strncmp(*p, "odd", 3)) {
79		*p += 3;
80		return UART_PARITY_ODD;
81	}
82	if (!strncmp(*p, "space", 5)) {
83		*p += 5;
84		return UART_PARITY_SPACE;
85	}
86	return (-1);
87}
88
89static int
90uart_parse_tag(__const char **p)
91{
92	int tag;
93
94	if ((*p)[0] == 'b' && (*p)[1] == 'r') {
95		tag = UART_TAG_BR;
96		goto out;
97	}
98	if ((*p)[0] == 'c' && (*p)[1] == 'h') {
99		tag = UART_TAG_CH;
100		goto out;
101	}
102	if ((*p)[0] == 'd' && (*p)[1] == 'b') {
103		tag = UART_TAG_DB;
104		goto out;
105	}
106	if ((*p)[0] == 'd' && (*p)[1] == 't') {
107		tag = UART_TAG_DT;
108		goto out;
109	}
110	if ((*p)[0] == 'i' && (*p)[1] == 'o') {
111		tag = UART_TAG_IO;
112		goto out;
113	}
114	if ((*p)[0] == 'm' && (*p)[1] == 'm') {
115		tag = UART_TAG_MM;
116		goto out;
117	}
118	if ((*p)[0] == 'p' && (*p)[1] == 'a') {
119		tag = UART_TAG_PA;
120		goto out;
121	}
122	if ((*p)[0] == 'r' && (*p)[1] == 's') {
123		tag = UART_TAG_RS;
124		goto out;
125	}
126	if ((*p)[0] == 's' && (*p)[1] == 'b') {
127		tag = UART_TAG_SB;
128		goto out;
129	}
130	if ((*p)[0] == 'x' && (*p)[1] == 'o') {
131		tag = UART_TAG_XO;
132		goto out;
133	}
134	return (-1);
135
136out:
137	*p += 2;
138	if ((*p)[0] != ':')
139		return (-1);
140	(*p)++;
141	return (tag);
142}
143
144/*
145 * Parse a device specification. The specification is a list of attributes
146 * seperated by commas. Each attribute is a tag-value pair with the tag and
147 * value seperated by a colon. Supported tags are:
148 *
149 *	br = Baudrate
150 *	ch = Channel
151 *	db = Data bits
152 *	dt = Device type
153 *	io = I/O port address
154 *	mm = Memory mapped I/O address
155 *	pa = Parity
156 *	rs = Register shift
157 *	sb = Stopbits
158 *	xo = Device clock (xtal oscillator)
159 *
160 * The io and mm tags are mutually exclusive.
161 */
162
163int
164uart_getenv(int devtype, struct uart_devinfo *di)
165{
166	__const char *spec;
167	bus_addr_t addr = ~0U;
168
169	/*
170	 * Check the environment variables "hw.uart.console" and
171	 * "hw.uart.dbgport". These variables, when present, specify
172	 * which UART port is to be used as serial console or debug
173	 * port (resp).
174	 */
175	if (devtype == UART_DEV_CONSOLE)
176		spec = getenv("hw.uart.console");
177	else if (devtype == UART_DEV_DBGPORT)
178		spec = getenv("hw.uart.dbgport");
179	else
180		spec = NULL;
181	if (spec == NULL)
182		return (ENXIO);
183
184	/* Set defaults. */
185	di->bas.chan = 0;
186	di->bas.regshft = 0;
187	di->bas.rclk = 0;
188	di->baudrate = 0;
189	di->databits = 8;
190	di->stopbits = 1;
191	di->parity = UART_PARITY_NONE;
192
193	/* Parse the attributes. */
194	while (1) {
195		switch (uart_parse_tag(&spec)) {
196		case UART_TAG_BR:
197			di->baudrate = uart_parse_long(&spec);
198			break;
199		case UART_TAG_CH:
200			di->bas.chan = uart_parse_long(&spec);
201			break;
202		case UART_TAG_DB:
203			di->databits = uart_parse_long(&spec);
204			break;
205		case UART_TAG_DT:
206			return (EINVAL);	/* XXX not yet implemented. */
207			break;
208		case UART_TAG_IO:
209			di->bas.bst = uart_bus_space_io;
210			addr = uart_parse_addr(&spec);
211			break;
212		case UART_TAG_MM:
213			di->bas.bst = uart_bus_space_mem;
214			addr = uart_parse_addr(&spec);
215			break;
216		case UART_TAG_PA:
217			di->parity = uart_parse_parity(&spec);
218			break;
219		case UART_TAG_RS:
220			di->bas.regshft = uart_parse_long(&spec);
221			break;
222		case UART_TAG_SB:
223			di->stopbits = uart_parse_long(&spec);
224			break;
225		case UART_TAG_XO:
226			di->bas.rclk = uart_parse_long(&spec);
227			break;
228		default:
229			return (EINVAL);
230		}
231		if (*spec == '\0')
232			break;
233		if (*spec != ',')
234			return (EINVAL);
235		spec++;
236	}
237
238	/*
239	 * If we still have an invalid address, the specification must be
240	 * missing an I/O port or memory address. We don't like that.
241	 */
242	if (addr == ~0U)
243		return (EINVAL);
244
245	/*
246	 * Accept only the well-known baudrates. Any invalid baudrate
247	 * is silently replaced with a 0-valued baudrate. The 0 baudrate
248	 * has special meaning. It means that we're not supposed to
249	 * program the baudrate and simply communicate with whatever
250	 * speed the hardware is currently programmed for.
251	 */
252	if (di->baudrate >= 19200) {
253		if (di->baudrate % 19200)
254			di->baudrate = 0;
255	} else if (di->baudrate >= 1200) {
256		if (di->baudrate % 1200)
257			di->baudrate = 0;
258	} else if (di->baudrate > 0) {
259		if (di->baudrate % 75)
260			di->baudrate = 0;
261	} else
262		di->baudrate = 0;
263
264	/* XXX the size of the mapping depends on the UART class. */
265	if (bus_space_map(di->bas.bst, addr, 8, 0, &di->bas.bsh) != 0)
266		return (EINVAL);
267	return (0);
268}
269