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->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