uart_subr.c revision 139749
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 139749 2005-01-06 01:43:34Z 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->ops = uart_ns8250_ops;
186	di->bas.chan = 0;
187	di->bas.regshft = 0;
188	di->bas.rclk = 0;
189	di->baudrate = 0;
190	di->databits = 8;
191	di->stopbits = 1;
192	di->parity = UART_PARITY_NONE;
193
194	/* Parse the attributes. */
195	while (1) {
196		switch (uart_parse_tag(&spec)) {
197		case UART_TAG_BR:
198			di->baudrate = uart_parse_long(&spec);
199			break;
200		case UART_TAG_CH:
201			di->bas.chan = uart_parse_long(&spec);
202			break;
203		case UART_TAG_DB:
204			di->databits = uart_parse_long(&spec);
205			break;
206		case UART_TAG_DT:
207			return (EINVAL);	/* XXX not yet implemented. */
208			break;
209		case UART_TAG_IO:
210			di->bas.bst = uart_bus_space_io;
211			addr = uart_parse_addr(&spec);
212			break;
213		case UART_TAG_MM:
214			di->bas.bst = uart_bus_space_mem;
215			addr = uart_parse_addr(&spec);
216			break;
217		case UART_TAG_PA:
218			di->parity = uart_parse_parity(&spec);
219			break;
220		case UART_TAG_RS:
221			di->bas.regshft = uart_parse_long(&spec);
222			break;
223		case UART_TAG_SB:
224			di->stopbits = uart_parse_long(&spec);
225			break;
226		case UART_TAG_XO:
227			di->bas.rclk = uart_parse_long(&spec);
228			break;
229		default:
230			return (EINVAL);
231		}
232		if (*spec == '\0')
233			break;
234		if (*spec != ',')
235			return (EINVAL);
236		spec++;
237	}
238
239	/*
240	 * If we still have an invalid address, the specification must be
241	 * missing an I/O port or memory address. We don't like that.
242	 */
243	if (addr == ~0U)
244		return (EINVAL);
245
246	/*
247	 * Accept only the well-known baudrates. Any invalid baudrate
248	 * is silently replaced with a 0-valued baudrate. The 0 baudrate
249	 * has special meaning. It means that we're not supposed to
250	 * program the baudrate and simply communicate with whatever
251	 * speed the hardware is currently programmed for.
252	 */
253	if (di->baudrate >= 19200) {
254		if (di->baudrate % 19200)
255			di->baudrate = 0;
256	} else if (di->baudrate >= 1200) {
257		if (di->baudrate % 1200)
258			di->baudrate = 0;
259	} else if (di->baudrate > 0) {
260		if (di->baudrate % 75)
261			di->baudrate = 0;
262	} else
263		di->baudrate = 0;
264
265	/* XXX the size of the mapping depends on the UART class. */
266	if (bus_space_map(di->bas.bst, addr, 8, 0, &di->bas.bsh) != 0)
267		return (EINVAL);
268	return (0);
269}
270