1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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/param.h> 30 #include <sys/systm.h> 31 #include <sys/bus.h> 32 33 #include <machine/bus.h> 34 #include <machine/vmparam.h> 35 36 #include <dev/uart/uart.h> 37 #include <dev/uart/uart_cpu.h> 38 39 #define UART_TAG_BR 0 40 #define UART_TAG_CH 1 41 #define UART_TAG_DB 2 42 #define UART_TAG_DT 3 43 #define UART_TAG_IO 4 44 #define UART_TAG_MM 5 45 #define UART_TAG_PA 6 46 #define UART_TAG_RS 7 47 #define UART_TAG_SB 8 48 #define UART_TAG_XO 9 49 #define UART_TAG_BD 10 50 #define UART_TAG_RW 11 51 52 static bus_addr_t 53 uart_parse_addr(const char **p) 54 { 55 return (strtoul(*p, (char**)(uintptr_t)p, 0)); 56 } 57 58 static struct uart_class * 59 uart_parse_class(struct uart_class *class, const char **p) 60 { 61 struct uart_class **puc, *uc; 62 const char *nm; 63 size_t len; 64 65 SET_FOREACH(puc, uart_class_set) { 66 uc = *puc; 67 nm = uart_getname(uc); 68 if (nm == NULL || *nm == '\0') 69 continue; 70 len = strlen(nm); 71 if (strncmp(nm, *p, len) == 0) { 72 *p += len; 73 return (uc); 74 } 75 } 76 return (class); 77 } 78 79 static long 80 uart_parse_long(const char **p) 81 { 82 return (strtol(*p, (char**)(uintptr_t)p, 0)); 83 } 84 85 static int 86 uart_parse_parity(const char **p) 87 { 88 if (!strncmp(*p, "even", 4)) { 89 *p += 4; 90 return UART_PARITY_EVEN; 91 } 92 if (!strncmp(*p, "mark", 4)) { 93 *p += 4; 94 return UART_PARITY_MARK; 95 } 96 if (!strncmp(*p, "none", 4)) { 97 *p += 4; 98 return UART_PARITY_NONE; 99 } 100 if (!strncmp(*p, "odd", 3)) { 101 *p += 3; 102 return UART_PARITY_ODD; 103 } 104 if (!strncmp(*p, "space", 5)) { 105 *p += 5; 106 return UART_PARITY_SPACE; 107 } 108 return (-1); 109 } 110 111 static int 112 uart_parse_tag(const char **p) 113 { 114 int tag; 115 116 if ((*p)[0] == 'b' && (*p)[1] == 'd') { 117 tag = UART_TAG_BD; 118 goto out; 119 } 120 if ((*p)[0] == 'b' && (*p)[1] == 'r') { 121 tag = UART_TAG_BR; 122 goto out; 123 } 124 if ((*p)[0] == 'c' && (*p)[1] == 'h') { 125 tag = UART_TAG_CH; 126 goto out; 127 } 128 if ((*p)[0] == 'd' && (*p)[1] == 'b') { 129 tag = UART_TAG_DB; 130 goto out; 131 } 132 if ((*p)[0] == 'd' && (*p)[1] == 't') { 133 tag = UART_TAG_DT; 134 goto out; 135 } 136 if ((*p)[0] == 'i' && (*p)[1] == 'o') { 137 tag = UART_TAG_IO; 138 goto out; 139 } 140 if ((*p)[0] == 'm' && (*p)[1] == 'm') { 141 tag = UART_TAG_MM; 142 goto out; 143 } 144 if ((*p)[0] == 'p' && (*p)[1] == 'a') { 145 tag = UART_TAG_PA; 146 goto out; 147 } 148 if ((*p)[0] == 'r' && (*p)[1] == 's') { 149 tag = UART_TAG_RS; 150 goto out; 151 } 152 if ((*p)[0] == 'r' && (*p)[1] == 'w') { 153 tag = UART_TAG_RW; 154 goto out; 155 } 156 if ((*p)[0] == 's' && (*p)[1] == 'b') { 157 tag = UART_TAG_SB; 158 goto out; 159 } 160 if ((*p)[0] == 'x' && (*p)[1] == 'o') { 161 tag = UART_TAG_XO; 162 goto out; 163 } 164 return (-1); 165 166 out: 167 *p += 2; 168 if ((*p)[0] != ':' && (*p)[0] != '=') 169 return (-1); 170 (*p)++; 171 return (tag); 172 } 173 174 /* 175 * Parse a device specification. The specification is a list of attributes 176 * separated by commas. Each attribute is a tag-value pair with the tag and 177 * value separated by a colon. Supported tags are: 178 * 179 * bd = Busy Detect 180 * br = Baudrate 181 * ch = Channel 182 * db = Data bits 183 * dt = Device type 184 * io = I/O port address 185 * mm = Memory mapped I/O address 186 * pa = Parity 187 * rs = Register shift 188 * rw = Register width 189 * sb = Stopbits 190 * xo = Device clock (xtal oscillator) 191 * 192 * The io and mm tags are mutually exclusive. 193 */ 194 195 int 196 uart_getenv(int devtype, struct uart_devinfo *di, struct uart_class *class) 197 { 198 const char *spec; 199 char *cp; 200 bus_addr_t addr = ~0U; 201 int error; 202 203 /* 204 * Check the environment variables "hw.uart.console" and 205 * "hw.uart.dbgport". These variables, when present, specify 206 * which UART port is to be used as serial console or debug 207 * port (resp). 208 */ 209 switch (devtype) { 210 case UART_DEV_CONSOLE: 211 cp = kern_getenv("hw.uart.console"); 212 break; 213 case UART_DEV_DBGPORT: 214 cp = kern_getenv("hw.uart.dbgport"); 215 break; 216 default: 217 cp = NULL; 218 break; 219 } 220 221 if (cp == NULL) 222 return (ENXIO); 223 224 /* Set defaults. */ 225 di->bas.chan = 0; 226 di->bas.regshft = 0; 227 di->bas.regiowidth = 1; 228 di->bas.rclk = 0; 229 di->baudrate = 0; 230 di->databits = 8; 231 di->stopbits = 1; 232 di->parity = UART_PARITY_NONE; 233 234 /* Parse the attributes. */ 235 spec = cp; 236 for (;;) { 237 switch (uart_parse_tag(&spec)) { 238 case UART_TAG_BD: 239 di->bas.busy_detect = uart_parse_long(&spec); 240 break; 241 case UART_TAG_BR: 242 di->baudrate = uart_parse_long(&spec); 243 break; 244 case UART_TAG_CH: 245 di->bas.chan = uart_parse_long(&spec); 246 break; 247 case UART_TAG_DB: 248 di->databits = uart_parse_long(&spec); 249 break; 250 case UART_TAG_DT: 251 class = uart_parse_class(class, &spec); 252 break; 253 case UART_TAG_IO: 254 di->bas.bst = uart_bus_space_io; 255 addr = uart_parse_addr(&spec); 256 break; 257 case UART_TAG_MM: 258 di->bas.bst = uart_bus_space_mem; 259 addr = uart_parse_addr(&spec); 260 break; 261 case UART_TAG_PA: 262 di->parity = uart_parse_parity(&spec); 263 break; 264 case UART_TAG_RS: 265 di->bas.regshft = uart_parse_long(&spec); 266 break; 267 case UART_TAG_RW: 268 di->bas.regiowidth = uart_parse_long(&spec); 269 break; 270 case UART_TAG_SB: 271 di->stopbits = uart_parse_long(&spec); 272 break; 273 case UART_TAG_XO: 274 di->bas.rclk = uart_parse_long(&spec); 275 if (di->bas.rclk == 0) 276 di->bas.rclk_guess = 1; 277 break; 278 default: 279 goto inval; 280 } 281 if (*spec == '\0') 282 break; 283 if (*spec != ',') 284 goto inval; 285 spec++; 286 } 287 288 /* 289 * If we still have an invalid address, the specification must be 290 * missing an I/O port or memory address. We don't like that. 291 */ 292 if (addr == ~0U) 293 goto inval; 294 freeenv(cp); 295 296 /* 297 * The default uart_class reference is weak. Make sure the default 298 * device class has been compiled-in or we've set one with dt=. 299 */ 300 if (class == NULL) 301 return (ENXIO); 302 303 /* 304 * Accept only the well-known baudrates. Any invalid baudrate 305 * is silently replaced with a 0-valued baudrate. The 0 baudrate 306 * has special meaning. It means that we're not supposed to 307 * program the baudrate and simply communicate with whatever 308 * speed the hardware is currently programmed for. 309 */ 310 if (di->baudrate >= 19200) { 311 if (di->baudrate % 19200) 312 di->baudrate = 0; 313 } else if (di->baudrate >= 1200) { 314 if (di->baudrate % 1200) 315 di->baudrate = 0; 316 } else if (di->baudrate > 0) { 317 if (di->baudrate % 75) 318 di->baudrate = 0; 319 } else 320 di->baudrate = 0; 321 322 /* Set the ops and create a bus space handle. */ 323 di->ops = uart_getops(class); 324 error = bus_space_map(di->bas.bst, addr, uart_getrange(class), 0, 325 &di->bas.bsh); 326 return (error); 327 inval: 328 printf("warning: bad uart specification: %s\n", cp); 329 freeenv(cp); 330 return (EINVAL); 331 } 332