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: stable/11/sys/dev/uart/uart_subr.c 356923 2020-01-20 18:54:19Z loos $");
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
50356923Sloos#define	UART_TAG_BD	10
51127215Smarcel
52168281Smarcelstatic struct uart_class *uart_classes[] = {
53168281Smarcel	&uart_ns8250_class,
54168281Smarcel	&uart_sab82532_class,
55168281Smarcel	&uart_z8530_class,
56249765Snyan#if defined(__arm__)
57252394Sray	&uart_s3c2410_class,
58249765Snyan#endif
59168281Smarcel};
60168281Smarcel
61127215Smarcelstatic bus_addr_t
62228468Seduart_parse_addr(const char **p)
63127215Smarcel{
64127215Smarcel	return (strtoul(*p, (char**)(uintptr_t)p, 0));
65127215Smarcel}
66127215Smarcel
67168281Smarcelstatic struct uart_class *
68228468Seduart_parse_class(struct uart_class *class, const char **p)
69168281Smarcel{
70168281Smarcel	struct uart_class *uc;
71168281Smarcel	const char *nm;
72168281Smarcel	size_t len;
73168281Smarcel	u_int i;
74168281Smarcel
75298411Spfg	for (i = 0; i < nitems(uart_classes); i++) {
76168281Smarcel		uc = uart_classes[i];
77168281Smarcel		nm = uart_getname(uc);
78168281Smarcel		if (nm == NULL || *nm == '\0')
79168281Smarcel			continue;
80168281Smarcel		len = strlen(nm);
81168281Smarcel		if (strncmp(nm, *p, len) == 0) {
82168281Smarcel			*p += len;
83168281Smarcel			return (uc);
84168281Smarcel		}
85168281Smarcel	}
86168281Smarcel	return (class);
87168281Smarcel}
88168281Smarcel
89127215Smarcelstatic long
90228468Seduart_parse_long(const char **p)
91127215Smarcel{
92127215Smarcel	return (strtol(*p, (char**)(uintptr_t)p, 0));
93127215Smarcel}
94127215Smarcel
95127215Smarcelstatic int
96228468Seduart_parse_parity(const char **p)
97127215Smarcel{
98127215Smarcel	if (!strncmp(*p, "even", 4)) {
99127215Smarcel		*p += 4;
100127215Smarcel		return UART_PARITY_EVEN;
101127215Smarcel	}
102127215Smarcel	if (!strncmp(*p, "mark", 4)) {
103127215Smarcel		*p += 4;
104127215Smarcel		return UART_PARITY_MARK;
105127215Smarcel	}
106127215Smarcel	if (!strncmp(*p, "none", 4)) {
107127215Smarcel		*p += 4;
108127215Smarcel		return UART_PARITY_NONE;
109127215Smarcel	}
110127215Smarcel	if (!strncmp(*p, "odd", 3)) {
111127215Smarcel		*p += 3;
112127215Smarcel		return UART_PARITY_ODD;
113127215Smarcel	}
114127215Smarcel	if (!strncmp(*p, "space", 5)) {
115127215Smarcel		*p += 5;
116127215Smarcel		return UART_PARITY_SPACE;
117127215Smarcel	}
118127215Smarcel	return (-1);
119127215Smarcel}
120127215Smarcel
121127215Smarcelstatic int
122228468Seduart_parse_tag(const char **p)
123127215Smarcel{
124127215Smarcel	int tag;
125127215Smarcel
126356923Sloos	if ((*p)[0] == 'b' && (*p)[1] == 'd') {
127356923Sloos		tag = UART_TAG_BD;
128356923Sloos		goto out;
129356923Sloos	}
130127215Smarcel	if ((*p)[0] == 'b' && (*p)[1] == 'r') {
131127215Smarcel		tag = UART_TAG_BR;
132127215Smarcel		goto out;
133127215Smarcel	}
134127215Smarcel	if ((*p)[0] == 'c' && (*p)[1] == 'h') {
135127215Smarcel		tag = UART_TAG_CH;
136127215Smarcel		goto out;
137127215Smarcel	}
138127215Smarcel	if ((*p)[0] == 'd' && (*p)[1] == 'b') {
139127215Smarcel		tag = UART_TAG_DB;
140127215Smarcel		goto out;
141127215Smarcel	}
142127215Smarcel	if ((*p)[0] == 'd' && (*p)[1] == 't') {
143127215Smarcel		tag = UART_TAG_DT;
144127215Smarcel		goto out;
145127215Smarcel	}
146127215Smarcel	if ((*p)[0] == 'i' && (*p)[1] == 'o') {
147127215Smarcel		tag = UART_TAG_IO;
148127215Smarcel		goto out;
149127215Smarcel	}
150127215Smarcel	if ((*p)[0] == 'm' && (*p)[1] == 'm') {
151127215Smarcel		tag = UART_TAG_MM;
152127215Smarcel		goto out;
153127215Smarcel	}
154127215Smarcel	if ((*p)[0] == 'p' && (*p)[1] == 'a') {
155127215Smarcel		tag = UART_TAG_PA;
156127215Smarcel		goto out;
157127215Smarcel	}
158127215Smarcel	if ((*p)[0] == 'r' && (*p)[1] == 's') {
159127215Smarcel		tag = UART_TAG_RS;
160127215Smarcel		goto out;
161127215Smarcel	}
162127215Smarcel	if ((*p)[0] == 's' && (*p)[1] == 'b') {
163127215Smarcel		tag = UART_TAG_SB;
164127215Smarcel		goto out;
165127215Smarcel	}
166127215Smarcel	if ((*p)[0] == 'x' && (*p)[1] == 'o') {
167127215Smarcel		tag = UART_TAG_XO;
168127215Smarcel		goto out;
169127215Smarcel	}
170127215Smarcel	return (-1);
171127215Smarcel
172127215Smarcelout:
173127215Smarcel	*p += 2;
174127215Smarcel	if ((*p)[0] != ':')
175127215Smarcel		return (-1);
176127215Smarcel	(*p)++;
177127215Smarcel	return (tag);
178127215Smarcel}
179127215Smarcel
180127215Smarcel/*
181127215Smarcel * Parse a device specification. The specification is a list of attributes
182215034Sbrucec * separated by commas. Each attribute is a tag-value pair with the tag and
183215034Sbrucec * value separated by a colon. Supported tags are:
184127215Smarcel *
185356923Sloos *	bd = Busy Detect
186127215Smarcel *	br = Baudrate
187127215Smarcel *	ch = Channel
188127215Smarcel *	db = Data bits
189127215Smarcel *	dt = Device type
190127215Smarcel *	io = I/O port address
191127215Smarcel *	mm = Memory mapped I/O address
192127215Smarcel *	pa = Parity
193127215Smarcel *	rs = Register shift
194127215Smarcel *	sb = Stopbits
195127215Smarcel *	xo = Device clock (xtal oscillator)
196127215Smarcel *
197127215Smarcel * The io and mm tags are mutually exclusive.
198127215Smarcel */
199127215Smarcel
200127215Smarcelint
201168281Smarceluart_getenv(int devtype, struct uart_devinfo *di, struct uart_class *class)
202127215Smarcel{
203273761Saraujo	const char *spec;
204273761Saraujo	char *cp;
205127215Smarcel	bus_addr_t addr = ~0U;
206168281Smarcel	int error;
207127215Smarcel
208127215Smarcel	/*
209168281Smarcel	 * All uart_class references are weak. Make sure the default
210168281Smarcel	 * device class has been compiled-in.
211168281Smarcel	 */
212168281Smarcel	if (class == NULL)
213168281Smarcel		return (ENXIO);
214168281Smarcel
215168281Smarcel	/*
216127215Smarcel	 * Check the environment variables "hw.uart.console" and
217127215Smarcel	 * "hw.uart.dbgport". These variables, when present, specify
218127215Smarcel	 * which UART port is to be used as serial console or debug
219127215Smarcel	 * port (resp).
220127215Smarcel	 */
221273761Saraujo	switch (devtype) {
222273761Saraujo	case UART_DEV_CONSOLE:
223273576Saraujo		cp = kern_getenv("hw.uart.console");
224273761Saraujo		break;
225273761Saraujo	case UART_DEV_DBGPORT:
226273576Saraujo		cp = kern_getenv("hw.uart.dbgport");
227273761Saraujo		break;
228273761Saraujo	default:
229273576Saraujo		cp = NULL;
230273761Saraujo		break;
231273761Saraujo	}
232273761Saraujo
233273576Saraujo	if (cp == NULL)
234127215Smarcel		return (ENXIO);
235127215Smarcel
236127215Smarcel	/* Set defaults. */
237127215Smarcel	di->bas.chan = 0;
238127215Smarcel	di->bas.regshft = 0;
239127215Smarcel	di->bas.rclk = 0;
240127215Smarcel	di->baudrate = 0;
241127215Smarcel	di->databits = 8;
242127215Smarcel	di->stopbits = 1;
243127215Smarcel	di->parity = UART_PARITY_NONE;
244127215Smarcel
245127215Smarcel	/* Parse the attributes. */
246273576Saraujo	spec = cp;
247273761Saraujo	for (;;) {
248127215Smarcel		switch (uart_parse_tag(&spec)) {
249356923Sloos		case UART_TAG_BD:
250356923Sloos			di->bas.busy_detect = uart_parse_long(&spec);
251356923Sloos			break;
252127215Smarcel		case UART_TAG_BR:
253127215Smarcel			di->baudrate = uart_parse_long(&spec);
254127215Smarcel			break;
255127215Smarcel		case UART_TAG_CH:
256127215Smarcel			di->bas.chan = uart_parse_long(&spec);
257127215Smarcel			break;
258127215Smarcel		case UART_TAG_DB:
259127215Smarcel			di->databits = uart_parse_long(&spec);
260127215Smarcel			break;
261127215Smarcel		case UART_TAG_DT:
262168281Smarcel			class = uart_parse_class(class, &spec);
263127215Smarcel			break;
264127215Smarcel		case UART_TAG_IO:
265127215Smarcel			di->bas.bst = uart_bus_space_io;
266127215Smarcel			addr = uart_parse_addr(&spec);
267127215Smarcel			break;
268127215Smarcel		case UART_TAG_MM:
269127215Smarcel			di->bas.bst = uart_bus_space_mem;
270127215Smarcel			addr = uart_parse_addr(&spec);
271127215Smarcel			break;
272127215Smarcel		case UART_TAG_PA:
273127215Smarcel			di->parity = uart_parse_parity(&spec);
274127215Smarcel			break;
275127215Smarcel		case UART_TAG_RS:
276127215Smarcel			di->bas.regshft = uart_parse_long(&spec);
277127215Smarcel			break;
278127215Smarcel		case UART_TAG_SB:
279127215Smarcel			di->stopbits = uart_parse_long(&spec);
280127215Smarcel			break;
281127215Smarcel		case UART_TAG_XO:
282127215Smarcel			di->bas.rclk = uart_parse_long(&spec);
283127215Smarcel			break;
284127215Smarcel		default:
285273761Saraujo			freeenv(cp);
286127215Smarcel			return (EINVAL);
287127215Smarcel		}
288127215Smarcel		if (*spec == '\0')
289127215Smarcel			break;
290273576Saraujo		if (*spec != ',') {
291273761Saraujo			freeenv(cp);
292127215Smarcel			return (EINVAL);
293273576Saraujo		}
294127215Smarcel		spec++;
295127215Smarcel	}
296273761Saraujo	freeenv(cp);
297127215Smarcel
298127215Smarcel	/*
299127215Smarcel	 * If we still have an invalid address, the specification must be
300127215Smarcel	 * missing an I/O port or memory address. We don't like that.
301127215Smarcel	 */
302127215Smarcel	if (addr == ~0U)
303127215Smarcel		return (EINVAL);
304137704Smarcel
305137704Smarcel	/*
306137704Smarcel	 * Accept only the well-known baudrates. Any invalid baudrate
307137704Smarcel	 * is silently replaced with a 0-valued baudrate. The 0 baudrate
308137704Smarcel	 * has special meaning. It means that we're not supposed to
309137704Smarcel	 * program the baudrate and simply communicate with whatever
310137704Smarcel	 * speed the hardware is currently programmed for.
311137704Smarcel	 */
312137704Smarcel	if (di->baudrate >= 19200) {
313137704Smarcel		if (di->baudrate % 19200)
314137704Smarcel			di->baudrate = 0;
315137704Smarcel	} else if (di->baudrate >= 1200) {
316137704Smarcel		if (di->baudrate % 1200)
317137704Smarcel			di->baudrate = 0;
318137704Smarcel	} else if (di->baudrate > 0) {
319137704Smarcel		if (di->baudrate % 75)
320137704Smarcel			di->baudrate = 0;
321137704Smarcel	} else
322137704Smarcel		di->baudrate = 0;
323137704Smarcel
324168281Smarcel	/* Set the ops and create a bus space handle. */
325168281Smarcel	di->ops = uart_getops(class);
326168281Smarcel	error = bus_space_map(di->bas.bst, addr, uart_getrange(class), 0,
327168281Smarcel	    &di->bas.bsh);
328168281Smarcel	return (error);
329127215Smarcel}
330