1184610Salfred/*- 2184610Salfred * Copyright (c) 2004 Marcel Moolenaar 3184610Salfred * All rights reserved. 4184610Salfred * 5184610Salfred * Redistribution and use in source and binary forms, with or without 6184610Salfred * modification, are permitted provided that the following conditions 7184610Salfred * are met: 8184610Salfred * 9184610Salfred * 1. Redistributions of source code must retain the above copyright 10184610Salfred * notice, this list of conditions and the following disclaimer. 11184610Salfred * 2. Redistributions in binary form must reproduce the above copyright 12184610Salfred * notice, this list of conditions and the following disclaimer in the 13184610Salfred * documentation and/or other materials provided with the distribution. 14184610Salfred * 15184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16184610Salfred * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17184610Salfred * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18184610Salfred * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19184610Salfred * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20184610Salfred * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21184610Salfred * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22184610Salfred * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23184610Salfred * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24184610Salfred * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25184610Salfred */ 26184610Salfred 27184610Salfred#include <sys/cdefs.h> 28184610Salfred__FBSDID("$FreeBSD: releng/10.2/sys/dev/uart/uart_subr.c 283327 2015-05-23 20:54:25Z ian $"); 29184610Salfred 30184610Salfred#include <sys/param.h> 31184610Salfred#include <sys/systm.h> 32184610Salfred#include <sys/bus.h> 33184610Salfred 34184610Salfred#include <machine/bus.h> 35184610Salfred#include <machine/vmparam.h> 36184610Salfred 37184610Salfred#include <dev/uart/uart.h> 38184610Salfred#include <dev/uart/uart_cpu.h> 39184610Salfred 40184610Salfred#define UART_TAG_BR 0 41184610Salfred#define UART_TAG_CH 1 42184610Salfred#define UART_TAG_DB 2 43184610Salfred#define UART_TAG_DT 3 44184610Salfred#define UART_TAG_IO 4 45184610Salfred#define UART_TAG_MM 5 46184610Salfred#define UART_TAG_PA 6 47184610Salfred#define UART_TAG_RS 7 48184610Salfred#define UART_TAG_SB 8 49184610Salfred#define UART_TAG_XO 9 50184610Salfred 51184610Salfredstatic struct uart_class *uart_classes[] = { 52184610Salfred &uart_ns8250_class, 53184610Salfred &uart_sab82532_class, 54184610Salfred &uart_z8530_class, 55184610Salfred#if defined(__arm__) 56184610Salfred &uart_s3c2410_class, 57184610Salfred#endif 58184610Salfred}; 59184610Salfredstatic size_t uart_nclasses = sizeof(uart_classes) / sizeof(uart_classes[0]); 60184610Salfred 61184610Salfredstatic bus_addr_t 62184610Salfreduart_parse_addr(const char **p) 63184610Salfred{ 64184610Salfred return (strtoul(*p, (char**)(uintptr_t)p, 0)); 65184610Salfred} 66184610Salfred 67184610Salfredstatic struct uart_class * 68194677Sthompsauart_parse_class(struct uart_class *class, const char **p) 69194677Sthompsa{ 70194677Sthompsa struct uart_class *uc; 71194677Sthompsa const char *nm; 72194677Sthompsa size_t len; 73194677Sthompsa u_int i; 74194677Sthompsa 75194677Sthompsa for (i = 0; i < uart_nclasses; i++) { 76194677Sthompsa uc = uart_classes[i]; 77194677Sthompsa nm = uart_getname(uc); 78194677Sthompsa if (nm == NULL || *nm == '\0') 79194677Sthompsa continue; 80194677Sthompsa len = strlen(nm); 81194677Sthompsa if (strncmp(nm, *p, len) == 0) { 82194677Sthompsa *p += len; 83194677Sthompsa return (uc); 84194677Sthompsa } 85194677Sthompsa } 86194677Sthompsa return (class); 87194677Sthompsa} 88194677Sthompsa 89194677Sthompsastatic long 90194677Sthompsauart_parse_long(const char **p) 91188746Sthompsa{ 92184610Salfred return (strtol(*p, (char**)(uintptr_t)p, 0)); 93184610Salfred} 94194677Sthompsa 95188942Sthompsastatic int 96184610Salfreduart_parse_parity(const char **p) 97188942Sthompsa{ 98188942Sthompsa if (!strncmp(*p, "even", 4)) { 99184610Salfred *p += 4; 100207077Sthompsa return UART_PARITY_EVEN; 101184610Salfred } 102184610Salfred if (!strncmp(*p, "mark", 4)) { 103192502Sthompsa *p += 4; 104192502Sthompsa return UART_PARITY_MARK; 105184610Salfred } 106184610Salfred if (!strncmp(*p, "none", 4)) { 107184610Salfred *p += 4; 108184610Salfred return UART_PARITY_NONE; 109184610Salfred } 110184610Salfred if (!strncmp(*p, "odd", 3)) { 111184610Salfred *p += 3; 112192984Sthompsa return UART_PARITY_ODD; 113184610Salfred } 114184610Salfred if (!strncmp(*p, "space", 5)) { 115184610Salfred *p += 5; 116184610Salfred return UART_PARITY_SPACE; 117184610Salfred } 118184610Salfred return (-1); 119184610Salfred} 120184610Salfred 121184610Salfredstatic int 122184610Salfreduart_parse_tag(const char **p) 123188412Sthompsa{ 124188412Sthompsa int tag; 125188412Sthompsa 126188412Sthompsa if ((*p)[0] == 'b' && (*p)[1] == 'r') { 127193045Sthompsa tag = UART_TAG_BR; 128193045Sthompsa goto out; 129193045Sthompsa } 130184610Salfred if ((*p)[0] == 'c' && (*p)[1] == 'h') { 131193045Sthompsa tag = UART_TAG_CH; 132193045Sthompsa goto out; 133193045Sthompsa } 134193045Sthompsa if ((*p)[0] == 'd' && (*p)[1] == 'b') { 135193045Sthompsa tag = UART_TAG_DB; 136193045Sthompsa goto out; 137193045Sthompsa } 138184610Salfred if ((*p)[0] == 'd' && (*p)[1] == 't') { 139188412Sthompsa tag = UART_TAG_DT; 140188412Sthompsa goto out; 141188412Sthompsa } 142188412Sthompsa if ((*p)[0] == 'i' && (*p)[1] == 'o') { 143188412Sthompsa tag = UART_TAG_IO; 144188412Sthompsa goto out; 145188412Sthompsa } 146184610Salfred if ((*p)[0] == 'm' && (*p)[1] == 'm') { 147188412Sthompsa tag = UART_TAG_MM; 148188412Sthompsa goto out; 149188412Sthompsa } 150184610Salfred if ((*p)[0] == 'p' && (*p)[1] == 'a') { 151192984Sthompsa tag = UART_TAG_PA; 152184610Salfred goto out; 153187259Sthompsa } 154184610Salfred if ((*p)[0] == 'r' && (*p)[1] == 's') { 155184610Salfred tag = UART_TAG_RS; 156184610Salfred goto out; 157190734Sthompsa } 158190734Sthompsa if ((*p)[0] == 's' && (*p)[1] == 'b') { 159190734Sthompsa tag = UART_TAG_SB; 160190734Sthompsa goto out; 161184610Salfred } 162184610Salfred if ((*p)[0] == 'x' && (*p)[1] == 'o') { 163187259Sthompsa tag = UART_TAG_XO; 164184610Salfred goto out; 165184610Salfred } 166184610Salfred return (-1); 167190734Sthompsa 168190734Sthompsaout: 169190734Sthompsa *p += 2; 170190734Sthompsa if ((*p)[0] != ':') 171184610Salfred return (-1); 172184610Salfred (*p)++; 173187259Sthompsa return (tag); 174184610Salfred} 175184610Salfred 176184610Salfred/* 177190734Sthompsa * Parse a device specification. The specification is a list of attributes 178190734Sthompsa * separated by commas. Each attribute is a tag-value pair with the tag and 179190734Sthompsa * value separated by a colon. Supported tags are: 180184610Salfred * 181184610Salfred * br = Baudrate 182184610Salfred * ch = Channel 183184610Salfred * db = Data bits 184184610Salfred * dt = Device type 185184610Salfred * io = I/O port address 186184610Salfred * mm = Memory mapped I/O address 187184610Salfred * pa = Parity 188184610Salfred * rs = Register shift 189184610Salfred * sb = Stopbits 190184610Salfred * xo = Device clock (xtal oscillator) 191184610Salfred * 192184610Salfred * The io and mm tags are mutually exclusive. 193184610Salfred */ 194188412Sthompsa 195188412Sthompsaint 196188412Sthompsauart_getenv(int devtype, struct uart_devinfo *di, struct uart_class *class) 197184610Salfred{ 198184610Salfred const char *spec; 199184610Salfred bus_addr_t addr = ~0U; 200184610Salfred int error; 201184610Salfred 202184610Salfred /* 203184610Salfred * All uart_class references are weak. Make sure the default 204184610Salfred * device class has been compiled-in. 205184610Salfred */ 206184610Salfred if (class == NULL) 207184610Salfred return (ENXIO); 208184610Salfred 209189275Sthompsa /* 210184610Salfred * Check the environment variables "hw.uart.console" and 211188942Sthompsa * "hw.uart.dbgport". These variables, when present, specify 212188942Sthompsa * which UART port is to be used as serial console or debug 213184610Salfred * port (resp). 214184610Salfred */ 215184610Salfred if (devtype == UART_DEV_CONSOLE) 216192984Sthompsa spec = getenv("hw.uart.console"); 217188412Sthompsa else if (devtype == UART_DEV_DBGPORT) 218188412Sthompsa spec = getenv("hw.uart.dbgport"); 219188412Sthompsa else 220188412Sthompsa spec = NULL; 221188412Sthompsa if (spec == NULL) 222188412Sthompsa return (ENXIO); 223188412Sthompsa 224188412Sthompsa /* Set defaults. */ 225188412Sthompsa di->bas.chan = 0; 226188412Sthompsa di->bas.regshft = 0; 227184610Salfred di->bas.rclk = 0; 228188412Sthompsa di->baudrate = 0; 229188412Sthompsa di->databits = 8; 230184610Salfred di->stopbits = 1; 231188412Sthompsa di->parity = UART_PARITY_NONE; 232188412Sthompsa 233184610Salfred /* Parse the attributes. */ 234188412Sthompsa while (1) { 235188412Sthompsa switch (uart_parse_tag(&spec)) { 236184610Salfred case UART_TAG_BR: 237192984Sthompsa di->baudrate = uart_parse_long(&spec); 238184610Salfred break; 239184610Salfred case UART_TAG_CH: 240184610Salfred di->bas.chan = uart_parse_long(&spec); 241184610Salfred break; 242184610Salfred case UART_TAG_DB: 243184610Salfred di->databits = uart_parse_long(&spec); 244184610Salfred break; 245194228Sthompsa case UART_TAG_DT: 246184610Salfred class = uart_parse_class(class, &spec); 247184610Salfred break; 248188412Sthompsa case UART_TAG_IO: 249188412Sthompsa di->bas.bst = uart_bus_space_io; 250184610Salfred addr = uart_parse_addr(&spec); 251192984Sthompsa break; 252184610Salfred case UART_TAG_MM: 253184610Salfred di->bas.bst = uart_bus_space_mem; 254184610Salfred addr = uart_parse_addr(&spec); 255184610Salfred break; 256184610Salfred case UART_TAG_PA: 257184610Salfred di->parity = uart_parse_parity(&spec); 258184610Salfred break; 259194228Sthompsa case UART_TAG_RS: 260184610Salfred di->bas.regshft = uart_parse_long(&spec); 261184610Salfred break; 262184610Salfred case UART_TAG_SB: 263188412Sthompsa di->stopbits = uart_parse_long(&spec); 264184610Salfred break; 265184610Salfred case UART_TAG_XO: 266184610Salfred di->bas.rclk = uart_parse_long(&spec); 267188412Sthompsa break; 268184610Salfred default: 269184610Salfred return (EINVAL); 270184610Salfred } 271184610Salfred if (*spec == '\0') 272188412Sthompsa break; 273184610Salfred if (*spec != ',') 274184610Salfred return (EINVAL); 275184610Salfred spec++; 276188412Sthompsa } 277184610Salfred 278184610Salfred /* 279184610Salfred * If we still have an invalid address, the specification must be 280188412Sthompsa * missing an I/O port or memory address. We don't like that. 281188412Sthompsa */ 282184610Salfred if (addr == ~0U) 283188412Sthompsa return (EINVAL); 284184610Salfred 285184610Salfred /* 286188412Sthompsa * Accept only the well-known baudrates. Any invalid baudrate 287188412Sthompsa * is silently replaced with a 0-valued baudrate. The 0 baudrate 288184610Salfred * has special meaning. It means that we're not supposed to 289184610Salfred * program the baudrate and simply communicate with whatever 290184610Salfred * speed the hardware is currently programmed for. 291184610Salfred */ 292188412Sthompsa if (di->baudrate >= 19200) { 293184610Salfred if (di->baudrate % 19200) 294184610Salfred di->baudrate = 0; 295188412Sthompsa } else if (di->baudrate >= 1200) { 296188412Sthompsa if (di->baudrate % 1200) 297184610Salfred di->baudrate = 0; 298184610Salfred } else if (di->baudrate > 0) { 299184610Salfred if (di->baudrate % 75) 300184610Salfred di->baudrate = 0; 301188412Sthompsa } else 302184610Salfred di->baudrate = 0; 303184610Salfred 304184610Salfred /* Set the ops and create a bus space handle. */ 305188412Sthompsa di->ops = uart_getops(class); 306184610Salfred error = bus_space_map(di->bas.bst, addr, uart_getrange(class), 0, 307184610Salfred &di->bas.bsh); 308184610Salfred return (error); 309184610Salfred} 310188412Sthompsa