xref: /freebsd/sys/dev/uart/uart_subr.c (revision 7bd6fde3)
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 
51 static bus_addr_t
52 uart_parse_addr(__const char **p)
53 {
54 	return (strtoul(*p, (char**)(uintptr_t)p, 0));
55 }
56 
57 static long
58 uart_parse_long(__const char **p)
59 {
60 	return (strtol(*p, (char**)(uintptr_t)p, 0));
61 }
62 
63 static int
64 uart_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 
89 static int
90 uart_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 
136 out:
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 
163 int
164 uart_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