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