1139749Simp/*- 2127215Smarcel * Copyright (c) 2003, 2004 Marcel Moolenaar 3155322Smarius * Copyright (c) 2004 - 2006 Marius Strobl <marius@FreeBSD.org> 4119815Smarcel * All rights reserved. 5119815Smarcel * 6119815Smarcel * Redistribution and use in source and binary forms, with or without 7119815Smarcel * modification, are permitted provided that the following conditions 8119815Smarcel * are met: 9119815Smarcel * 10119815Smarcel * 1. Redistributions of source code must retain the above copyright 11119815Smarcel * notice, this list of conditions and the following disclaimer. 12119815Smarcel * 2. Redistributions in binary form must reproduce the above copyright 13119815Smarcel * notice, this list of conditions and the following disclaimer in the 14119815Smarcel * documentation and/or other materials provided with the distribution. 15119815Smarcel * 16119815Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17119815Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18119815Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19119815Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20119815Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21119815Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22119815Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23119815Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24119815Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25119815Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26119815Smarcel */ 27119815Smarcel 28119815Smarcel#include <sys/cdefs.h> 29119815Smarcel__FBSDID("$FreeBSD$"); 30119815Smarcel 31119815Smarcel#include <sys/param.h> 32119815Smarcel#include <sys/systm.h> 33119815Smarcel 34119815Smarcel#include <machine/bus.h> 35119815Smarcel#include <machine/bus_private.h> 36119815Smarcel 37119815Smarcel#include <dev/ofw/openfirm.h> 38119815Smarcel#include <machine/ofw_machdep.h> 39119815Smarcel 40119815Smarcel#include <dev/uart/uart.h> 41119815Smarcel#include <dev/uart/uart_cpu.h> 42119815Smarcel 43127215Smarcelbus_space_tag_t uart_bus_space_io; 44127215Smarcelbus_space_tag_t uart_bus_space_mem; 45127215Smarcel 46119815Smarcelstatic struct bus_space_tag bst_store[3]; 47119815Smarcel 48138157Smarius/* 49155322Smarius * Determine which channel of a SCC a device referenced by a full device 50155322Smarius * path or as an alias is (in the latter case we try to look up the device 51155322Smarius * path via the /aliases node). 52155322Smarius * Only the device paths of devices which are used for TTYs really allow 53155322Smarius * to do this as they look like these (taken from /aliases nodes): 54138157Smarius * ttya: '/central/fhc/zs@0,902000:a' 55138157Smarius * ttyc: '/pci@1f,0/pci@1,1/ebus@1/se@14,400000:a' 56155322Smarius * Additionally, for device paths of SCCs which are connected to a RSC 57155322Smarius * (Remote System Control) device we can hardcode the appropriate channel. 58155322Smarius * Such device paths look like these: 59155322Smarius * rsc: '/pci@1f,4000/ebus@1/se@14,200000:ssp' 60155322Smarius * ttyc: '/pci@1f,4000/ebus@1/se@14,200000:ssp' 61138157Smarius */ 62119815Smarcelstatic int 63119815Smarceluart_cpu_channel(char *dev) 64119815Smarcel{ 65119815Smarcel char alias[64]; 66119815Smarcel phandle_t aliases; 67119815Smarcel int len; 68155322Smarius const char *p; 69119815Smarcel 70119815Smarcel strcpy(alias, dev); 71119815Smarcel if ((aliases = OF_finddevice("/aliases")) != -1) 72155322Smarius (void)OF_getprop(aliases, dev, alias, sizeof(alias)); 73119815Smarcel len = strlen(alias); 74229272Sed if ((p = strrchr(alias, ':')) == NULL) 75119815Smarcel return (0); 76155322Smarius p++; 77155322Smarius if (p - alias == len - 1 && (*p == 'a' || *p == 'b')) 78155322Smarius return (*p - 'a' + 1); 79155322Smarius if (strcmp(p, "ssp") == 0) 80155322Smarius return (1); 81155322Smarius return (0); 82119815Smarcel} 83119815Smarcel 84119815Smarcelint 85119866Smarceluart_cpu_eqres(struct uart_bas *b1, struct uart_bas *b2) 86119866Smarcel{ 87119866Smarcel 88119866Smarcel return ((b1->bsh == b2->bsh) ? 1 : 0); 89119866Smarcel} 90119866Smarcel 91127741Smarcel/* 92138157Smarius * Get the package handle of the UART that is selected as the console, if 93149111Smarius * the console is an UART of course. Note that we enforce that both input 94149111Smarius * and output are selected. 95127741Smarcel * Note that the currently active console (i.e. /chosen/stdout and 96127741Smarcel * /chosen/stdin) may not be the same as the device selected in the 97127741Smarcel * environment (ie /options/output-device and /options/input-device) because 98127825Smarcel * keyboard and screen were selected but the keyboard was unplugged or the 99127825Smarcel * user has changed the environment. In the latter case I would assume that 100127825Smarcel * the user expects that FreeBSD uses the new console setting. 101127825Smarcel * For weirder configurations, use ofw_console(4). 102127741Smarcel */ 103122466Sjakestatic phandle_t 104127741Smarceluart_cpu_getdev_console(phandle_t options, char *dev, size_t devsz) 105122466Sjake{ 106149111Smarius char buf[sizeof("serial")]; 107149111Smarius ihandle_t inst; 108149111Smarius phandle_t chosen, input, output; 109122466Sjake 110127741Smarcel if (OF_getprop(options, "input-device", dev, devsz) == -1) 111127741Smarcel return (-1); 112149111Smarius input = OF_finddevice(dev); 113149111Smarius if (OF_getprop(options, "output-device", dev, devsz) == -1) 114127825Smarcel return (-1); 115149111Smarius output = OF_finddevice(dev); 116149111Smarius if (input == -1 || output == -1 || 117149111Smarius OF_getproplen(input, "keyboard") >= 0) { 118127825Smarcel if ((chosen = OF_finddevice("/chosen")) == -1) 119127825Smarcel return (-1); 120149111Smarius if (OF_getprop(chosen, "stdin", &inst, sizeof(inst)) == -1) 121127825Smarcel return (-1); 122149111Smarius if ((input = OF_instance_to_package(inst)) == -1) 123127825Smarcel return (-1); 124149111Smarius if (OF_getprop(chosen, "stdout", &inst, sizeof(inst)) == -1) 125127825Smarcel return (-1); 126149111Smarius if ((output = OF_instance_to_package(inst)) == -1) 127127825Smarcel return (-1); 128127825Smarcel snprintf(dev, devsz, "ttya"); 129143468Smarius } 130149111Smarius if (input != output) 131149111Smarius return (-1); 132127741Smarcel if (OF_getprop(input, "device_type", buf, sizeof(buf)) == -1) 133127741Smarcel return (-1); 134127741Smarcel if (strcmp(buf, "serial") != 0) 135127741Smarcel return (-1); 136206451Smarius /* For a Serengeti console device point to the bootbus controller. */ 137206451Smarius if (OF_getprop(input, "name", buf, sizeof(buf)) > 0 && 138206451Smarius !strcmp(buf, "sgcn")) { 139206451Smarius if ((chosen = OF_finddevice("/chosen")) == -1) 140206451Smarius return (-1); 141206451Smarius if (OF_getprop(chosen, "iosram", &input, sizeof(input)) == -1) 142206451Smarius return (-1); 143206451Smarius } 144127741Smarcel return (input); 145127741Smarcel} 146127741Smarcel 147127741Smarcel/* 148138157Smarius * Get the package handle of the UART that's selected as the debug port. 149138157Smarius * Since there's no place for this in the OF, we use the kernel environment 150127741Smarcel * variable "hw.uart.dbgport". Note however that the variable is not a 151127741Smarcel * list of attributes. It's single device name or alias, as known by 152127741Smarcel * the OF. 153127741Smarcel */ 154127741Smarcelstatic phandle_t 155149111Smariusuart_cpu_getdev_dbgport(char *dev, size_t devsz) 156127741Smarcel{ 157149111Smarius char buf[sizeof("serial")]; 158127741Smarcel phandle_t input; 159127741Smarcel 160127741Smarcel if (!getenv_string("hw.uart.dbgport", dev, devsz)) 161127741Smarcel return (-1); 162127741Smarcel if ((input = OF_finddevice(dev)) == -1) 163127741Smarcel return (-1); 164127741Smarcel if (OF_getprop(input, "device_type", buf, sizeof(buf)) == -1) 165127741Smarcel return (-1); 166127741Smarcel if (strcmp(buf, "serial") != 0) 167127741Smarcel return (-1); 168127741Smarcel return (input); 169127741Smarcel} 170127741Smarcel 171138157Smarius/* 172146972Smarius * Get the package handle of the UART that is selected as the keyboard port, 173146972Smarius * if it's actually used to connect the keyboard according to the OF. I.e. 174146972Smarius * this will return the UART used to connect the keyboard regardless whether 175146972Smarius * it's stdin or not, however not in case the user or the OF gave preference 176146972Smarius * to e.g. a PS/2 keyboard by setting /aliases/keyboard accordingly. 177138157Smarius */ 178127741Smarcelstatic phandle_t 179146972Smariusuart_cpu_getdev_keyboard(char *dev, size_t devsz) 180127741Smarcel{ 181149111Smarius char buf[sizeof("serial")]; 182146972Smarius phandle_t input; 183127741Smarcel 184146972Smarius if ((input = OF_finddevice("keyboard")) == -1) 185146972Smarius return (-1); 186146972Smarius if (OF_getprop(input, "device_type", buf, sizeof(buf)) == -1) 187146972Smarius return (-1); 188146972Smarius if (strcmp(buf, "serial") != 0) 189146972Smarius return (-1); 190146972Smarius if (OF_getprop(input, "name", dev, devsz) == -1) 191146972Smarius return (-1); 192146972Smarius /* 193146972Smarius * So far this also matched PS/2 keyboard nodes so make sure it's 194146972Smarius * one of the SCCs/UARTs known to be used to connect keyboards. 195146972Smarius */ 196146972Smarius if (strcmp(dev, "su") && strcmp(dev, "su_pnp") && strcmp(dev, "zs")) 197146972Smarius return (-1); 198146972Smarius return (input); 199122466Sjake} 200122466Sjake 201119866Smarcelint 202119815Smarceluart_cpu_getdev(int devtype, struct uart_devinfo *di) 203119815Smarcel{ 204149111Smarius char buf[32], compat[32], dev[64]; 205168281Smarcel struct uart_class *class; 206127741Smarcel phandle_t input, options; 207119815Smarcel bus_addr_t addr; 208168281Smarcel int baud, bits, error, range, space, stop; 209119815Smarcel char flag, par; 210119815Smarcel 211127741Smarcel if ((options = OF_finddevice("/options")) == -1) 212127741Smarcel return (ENXIO); 213127741Smarcel switch (devtype) { 214127741Smarcel case UART_DEV_CONSOLE: 215127741Smarcel input = uart_cpu_getdev_console(options, dev, sizeof(dev)); 216127741Smarcel break; 217127741Smarcel case UART_DEV_DBGPORT: 218149111Smarius input = uart_cpu_getdev_dbgport(dev, sizeof(dev)); 219127741Smarcel break; 220127741Smarcel case UART_DEV_KEYBOARD: 221146972Smarius input = uart_cpu_getdev_keyboard(dev, sizeof(dev)); 222127741Smarcel break; 223127741Smarcel default: 224127741Smarcel input = -1; 225127741Smarcel break; 226127741Smarcel } 227127741Smarcel if (input == -1) 228127741Smarcel return (ENXIO); 229141753Smarius error = OF_decode_addr(input, 0, &space, &addr); 230119815Smarcel if (error) 231119815Smarcel return (error); 232119815Smarcel 233119815Smarcel /* Get the device class. */ 234119815Smarcel if (OF_getprop(input, "name", buf, sizeof(buf)) == -1) 235119815Smarcel return (ENXIO); 236120009Stmm if (OF_getprop(input, "compatible", compat, sizeof(compat)) == -1) 237120009Stmm compat[0] = '\0'; 238119815Smarcel di->bas.regshft = 0; 239119815Smarcel di->bas.rclk = 0; 240168281Smarcel class = NULL; 241221960Smarius if (!strcmp(buf, "se") || !strcmp(buf, "FJSV,se") || 242221960Smarius !strcmp(compat, "sab82532")) { 243168281Smarcel class = &uart_sab82532_class; 244138157Smarius /* SAB82532 are only known to be used for TTYs. */ 245138157Smarius if ((di->bas.chan = uart_cpu_channel(dev)) == 0) 246138157Smarius return (ENXIO); 247168281Smarcel addr += uart_getrange(class) * (di->bas.chan - 1); 248119815Smarcel } else if (!strcmp(buf, "zs")) { 249168281Smarcel class = &uart_z8530_class; 250138157Smarius if ((di->bas.chan = uart_cpu_channel(dev)) == 0) { 251138157Smarius /* 252138157Smarius * There's no way to determine from OF which 253138157Smarius * channel has the keyboard. Should always be 254138157Smarius * on channel 1 however. 255138157Smarius */ 256138157Smarius if (devtype == UART_DEV_KEYBOARD) 257138157Smarius di->bas.chan = 1; 258138157Smarius else 259138157Smarius return (ENXIO); 260138157Smarius } 261119815Smarcel di->bas.regshft = 1; 262168281Smarcel range = uart_getrange(class) << di->bas.regshft; 263168281Smarcel addr += range - range * (di->bas.chan - 1); 264155322Smarius } else if (!strcmp(buf, "lom-console") || !strcmp(buf, "su") || 265155322Smarius !strcmp(buf, "su_pnp") || !strcmp(compat, "rsc-console") || 266200926Smarius !strcmp(compat, "su") || !strcmp(compat, "su16550") || 267200926Smarius !strcmp(compat, "su16552")) { 268168281Smarcel class = &uart_ns8250_class; 269120452Smarcel di->bas.chan = 0; 270206451Smarius } else if (!strcmp(compat, "sgsbbc")) { 271206451Smarius class = &uart_sbbc_class; 272206451Smarius di->bas.chan = 0; 273168281Smarcel } 274168281Smarcel if (class == NULL) 275119815Smarcel return (ENXIO); 276119815Smarcel 277119815Smarcel /* Fill in the device info. */ 278168281Smarcel di->ops = uart_getops(class); 279119815Smarcel di->bas.bst = &bst_store[devtype]; 280119815Smarcel di->bas.bsh = sparc64_fake_bustag(space, addr, di->bas.bst); 281119815Smarcel 282119815Smarcel /* Get the line settings. */ 283120545Sjake if (devtype == UART_DEV_KEYBOARD) 284120545Sjake di->baudrate = 1200; 285155322Smarius else if (!strcmp(compat, "rsc-console")) 286155322Smarius di->baudrate = 115200; 287120545Sjake else 288120545Sjake di->baudrate = 9600; 289119815Smarcel di->databits = 8; 290119815Smarcel di->stopbits = 1; 291119815Smarcel di->parity = UART_PARITY_NONE; 292119815Smarcel snprintf(buf, sizeof(buf), "%s-mode", dev); 293155322Smarius if (OF_getprop(options, buf, buf, sizeof(buf)) == -1 && 294155322Smarius OF_getprop(input, "ssp-console-modes", buf, sizeof(buf)) == -1) 295119815Smarcel return (0); 296119815Smarcel if (sscanf(buf, "%d,%d,%c,%d,%c", &baud, &bits, &par, &stop, &flag) 297119815Smarcel != 5) 298119815Smarcel return (0); 299119815Smarcel di->baudrate = baud; 300119815Smarcel di->databits = bits; 301119815Smarcel di->stopbits = stop; 302119815Smarcel di->parity = (par == 'n') ? UART_PARITY_NONE : 303119815Smarcel (par == 'o') ? UART_PARITY_ODD : UART_PARITY_EVEN; 304119815Smarcel return (0); 305119815Smarcel} 306