1147271Smarius/*- 2147271Smarius * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp> 3147271Smarius * Copyright (c) 2005 Marius Strobl <marius@FreeBSD.org> 4147271Smarius * All rights reserved. 5147271Smarius * 6147271Smarius * Redistribution and use in source and binary forms, with or without 7147271Smarius * modification, are permitted provided that the following conditions 8147271Smarius * are met: 9147271Smarius * 1. Redistributions of source code must retain the above copyright 10147271Smarius * notice, this list of conditions and the following disclaimer as 11147271Smarius * the first lines of this file unmodified. 12147271Smarius * 2. Redistributions in binary form must reproduce the above copyright 13147271Smarius * notice, this list of conditions and the following disclaimer in the 14147271Smarius * documentation and/or other materials provided with the distribution. 15147271Smarius * 16147271Smarius * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 17147271Smarius * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18147271Smarius * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19147271Smarius * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20147271Smarius * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21147271Smarius * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22147271Smarius * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23147271Smarius * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24147271Smarius * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25147271Smarius * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26147271Smarius * 27147271Smarius * from: FreeBSD: src/sys/isa/atkbdc_isa.c,v 1.31 2005/05/29 04:42:28 nyan 28147271Smarius */ 29147271Smarius 30147271Smarius#include <sys/cdefs.h> 31147271Smarius__FBSDID("$FreeBSD: releng/11.0/sys/dev/atkbdc/atkbdc_ebus.c 300173 2016-05-18 23:39:31Z gonzo $"); 32147271Smarius 33147271Smarius#include "opt_kbd.h" 34147271Smarius 35147271Smarius#include <sys/param.h> 36147271Smarius#include <sys/systm.h> 37147271Smarius#include <sys/kernel.h> 38147271Smarius#include <sys/module.h> 39147271Smarius#include <sys/bus.h> 40147271Smarius#include <sys/kbio.h> 41147271Smarius#include <sys/malloc.h> 42147271Smarius 43147271Smarius#include <dev/ofw/ofw_bus.h> 44147271Smarius 45147271Smarius#include <machine/resource.h> 46147271Smarius#include <machine/ver.h> 47147271Smarius 48147271Smarius#include <sys/rman.h> 49147271Smarius 50147271Smarius#include <dev/kbd/kbdreg.h> 51147271Smarius#include <dev/atkbdc/atkbdreg.h> 52147271Smarius#include <dev/atkbdc/atkbdc_subr.h> 53147271Smarius#include <dev/atkbdc/atkbdcreg.h> 54147271Smarius#include <dev/atkbdc/psm.h> 55147271Smarius 56147271Smariusstatic device_probe_t atkbdc_ebus_probe; 57147271Smariusstatic device_attach_t atkbdc_ebus_attach; 58147271Smarius 59147271Smariusstatic device_method_t atkbdc_ebus_methods[] = { 60147271Smarius DEVMETHOD(device_probe, atkbdc_ebus_probe), 61147271Smarius DEVMETHOD(device_attach, atkbdc_ebus_attach), 62147271Smarius DEVMETHOD(device_suspend, bus_generic_suspend), 63147271Smarius DEVMETHOD(device_resume, bus_generic_resume), 64147271Smarius 65147271Smarius DEVMETHOD(bus_print_child, atkbdc_print_child), 66147271Smarius DEVMETHOD(bus_read_ivar, atkbdc_read_ivar), 67147271Smarius DEVMETHOD(bus_write_ivar, atkbdc_write_ivar), 68147271Smarius DEVMETHOD(bus_get_resource_list,atkbdc_get_resource_list), 69147271Smarius DEVMETHOD(bus_alloc_resource, bus_generic_rl_alloc_resource), 70147271Smarius DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource), 71147271Smarius DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 72147271Smarius DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 73147271Smarius DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), 74147271Smarius DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), 75147271Smarius DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource), 76147271Smarius DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 77147271Smarius DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 78147271Smarius 79147271Smarius { 0, 0 } 80147271Smarius}; 81147271Smarius 82147271Smariusstatic driver_t atkbdc_ebus_driver = { 83147271Smarius ATKBDC_DRIVER_NAME, 84147271Smarius atkbdc_ebus_methods, 85147271Smarius sizeof(atkbdc_softc_t *), 86147271Smarius}; 87147271Smarius 88147271SmariusDRIVER_MODULE(atkbdc, ebus, atkbdc_ebus_driver, atkbdc_devclass, 0, 0); 89147271Smarius 90147271Smariusstatic int 91147271Smariusatkbdc_ebus_probe(device_t dev) 92147271Smarius{ 93147271Smarius struct resource *port0, *port1; 94294883Sjhibbits rman_res_t count, start; 95147271Smarius int error, rid; 96147271Smarius 97147271Smarius if (strcmp(ofw_bus_get_name(dev), "8042") != 0) 98147271Smarius return (ENXIO); 99147271Smarius 100147271Smarius /* 101147271Smarius * On AXi and AXmp boards the NS16550 (used to connect keyboard/ 102147271Smarius * mouse) share their IRQ lines with the i8042. Any IRQ activity 103147271Smarius * (typically during attach) of the NS16550 used to connect the 104147271Smarius * keyboard when actually the PS/2 keyboard is selected in OFW 105147271Smarius * causes interaction with the OBP i8042 driver resulting in a 106147271Smarius * hang and vice versa. As RS232 keyboards and mice obviously 107147271Smarius * aren't meant to be used in parallel with PS/2 ones on these 108147271Smarius * boards don't attach to the i8042 in case the PS/2 keyboard 109147271Smarius * isn't selected in order to prevent such hangs. 110147271Smarius * Note that it's not sufficient here to rely on the '8042' node 111147271Smarius * only showing up when a PS/2 keyboard is actually connected as 112147271Smarius * the user still might have adjusted the 'keyboard' alias to 113147271Smarius * point to the RS232 keyboard. 114147271Smarius */ 115147271Smarius if ((!strcmp(sparc64_model, "SUNW,UltraAX-MP") || 116147271Smarius !strcmp(sparc64_model, "SUNW,UltraSPARC-IIi-Engine")) && 117147271Smarius OF_finddevice("keyboard") != ofw_bus_get_node(dev)) { 118147271Smarius device_disable(dev); 119147271Smarius return (ENXIO); 120147271Smarius } 121147271Smarius 122147271Smarius device_set_desc(dev, "Keyboard controller (i8042)"); 123147271Smarius 124147271Smarius /* 125147271Smarius * The '8042' node has two identical 8 addresses wide resources 126147271Smarius * which are apparently meant to be used one for the keyboard 127147271Smarius * half and the other one for the mouse half. To simplify matters 128147271Smarius * we use one for the command/data port resource and the other 129147271Smarius * one for the status port resource as the atkbdc(4) back-end 130147271Smarius * expects two struct resource rather than two bus space handles. 131147271Smarius */ 132147271Smarius rid = 0; 133147271Smarius if (bus_get_resource(dev, SYS_RES_MEMORY, rid, &start, &count) != 0) { 134147271Smarius device_printf(dev, 135147271Smarius "cannot determine command/data port resource\n"); 136147271Smarius return (ENXIO); 137147271Smarius } 138147271Smarius port0 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, start, start, 1, 139147271Smarius RF_ACTIVE); 140147271Smarius if (port0 == NULL) { 141147271Smarius device_printf(dev, 142147271Smarius "cannot allocate command/data port resource\n"); 143147271Smarius return (ENXIO); 144147271Smarius } 145147271Smarius 146147271Smarius rid = 1; 147147271Smarius if (bus_get_resource(dev, SYS_RES_MEMORY, rid, &start, &count) != 0) { 148147271Smarius device_printf(dev, "cannot determine status port resource\n"); 149147271Smarius error = ENXIO; 150147271Smarius goto fail_port0; 151147271Smarius } 152147271Smarius start += KBD_STATUS_PORT; 153147271Smarius port1 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, start, start, 1, 154147271Smarius RF_ACTIVE); 155147271Smarius if (port1 == NULL) { 156147271Smarius device_printf(dev, "cannot allocate status port resource\n"); 157147271Smarius error = ENXIO; 158147271Smarius goto fail_port0; 159147271Smarius } 160147271Smarius 161147271Smarius error = atkbdc_probe_unit(device_get_unit(dev), port0, port1); 162147271Smarius if (error != 0) 163147271Smarius device_printf(dev, "atkbdc_porbe_unit failed\n"); 164147271Smarius 165147271Smarius bus_release_resource(dev, SYS_RES_MEMORY, 1, port1); 166147271Smarius fail_port0: 167147271Smarius bus_release_resource(dev, SYS_RES_MEMORY, 0, port0); 168147271Smarius 169147271Smarius return (error); 170147271Smarius} 171147271Smarius 172147271Smariusstatic int 173147271Smariusatkbdc_ebus_attach(device_t dev) 174147271Smarius{ 175147271Smarius atkbdc_softc_t *sc; 176147271Smarius atkbdc_device_t *adi; 177147271Smarius device_t cdev; 178147271Smarius phandle_t child; 179294883Sjhibbits rman_res_t count, intr, start; 180147271Smarius int children, error, rid, unit; 181147271Smarius char *cname, *dname; 182147271Smarius 183147271Smarius unit = device_get_unit(dev); 184147271Smarius sc = *(atkbdc_softc_t **)device_get_softc(dev); 185147271Smarius if (sc == NULL) { 186147271Smarius /* 187147271Smarius * We have to maintain two copies of the kbdc_softc struct, 188147271Smarius * as the low-level console needs to have access to the 189147271Smarius * keyboard controller before kbdc is probed and attached. 190147271Smarius * kbdc_soft[] contains the default entry for that purpose. 191147271Smarius * See atkbdc.c. XXX 192147271Smarius */ 193147271Smarius sc = atkbdc_get_softc(unit); 194147271Smarius if (sc == NULL) 195147271Smarius return (ENOMEM); 196147271Smarius device_set_softc(dev, sc); 197147271Smarius } 198147271Smarius 199147271Smarius rid = 0; 200147271Smarius if (bus_get_resource(dev, SYS_RES_MEMORY, rid, &start, &count) != 0) { 201147271Smarius device_printf(dev, 202147271Smarius "cannot determine command/data port resource\n"); 203147271Smarius return (ENXIO); 204147271Smarius } 205207354Ssobomax sc->retry = 5000; 206147271Smarius sc->port0 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, start, start, 207147271Smarius 1, RF_ACTIVE); 208147271Smarius if (sc->port0 == NULL) { 209147271Smarius device_printf(dev, 210147271Smarius "cannot allocate command/data port resource\n"); 211147271Smarius return (ENXIO); 212147271Smarius } 213147271Smarius 214147271Smarius rid = 1; 215147271Smarius if (bus_get_resource(dev, SYS_RES_MEMORY, rid, &start, &count) != 0) { 216147271Smarius device_printf(dev, "cannot determine status port resource\n"); 217147271Smarius error = ENXIO; 218147271Smarius goto fail_port0; 219147271Smarius } 220147271Smarius start += KBD_STATUS_PORT; 221147271Smarius sc->port1 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, start, start, 222147271Smarius 1, RF_ACTIVE); 223147271Smarius if (sc->port1 == NULL) { 224147271Smarius device_printf(dev, "cannot allocate status port resource\n"); 225147271Smarius error = ENXIO; 226147271Smarius goto fail_port0; 227147271Smarius } 228147271Smarius 229147271Smarius error = atkbdc_attach_unit(unit, sc, sc->port0, sc->port1); 230147271Smarius if (error != 0) { 231147271Smarius device_printf(dev, "atkbdc_attach_unit failed\n"); 232147271Smarius goto fail_port1; 233147271Smarius } 234147271Smarius 235147271Smarius /* Attach children. */ 236147271Smarius children = 0; 237147271Smarius for (child = OF_child(ofw_bus_get_node(dev)); child != 0; 238147271Smarius child = OF_peer(child)) { 239147271Smarius if ((OF_getprop_alloc(child, "name", 1, (void **)&cname)) == -1) 240147271Smarius continue; 241147271Smarius if (children >= 2) { 242147271Smarius device_printf(dev, 243147271Smarius "<%s>: only two children per 8042 supported\n", 244147271Smarius cname); 245300173Sgonzo OF_prop_free(cname); 246147271Smarius continue; 247147271Smarius } 248147271Smarius adi = malloc(sizeof(struct atkbdc_device), M_ATKBDDEV, 249147271Smarius M_NOWAIT | M_ZERO); 250147271Smarius if (adi == NULL) { 251147271Smarius device_printf(dev, "<%s>: malloc failed\n", cname); 252300173Sgonzo OF_prop_free(cname); 253147271Smarius continue; 254147271Smarius } 255147271Smarius if (strcmp(cname, "kb_ps2") == 0) { 256147271Smarius adi->rid = KBDC_RID_KBD; 257147271Smarius dname = ATKBD_DRIVER_NAME; 258147271Smarius } else if (strcmp(cname, "kdmouse") == 0) { 259147271Smarius adi->rid = KBDC_RID_AUX; 260147271Smarius dname = PSM_DRIVER_NAME; 261147271Smarius } else { 262147271Smarius device_printf(dev, "<%s>: unknown device\n", cname); 263147271Smarius free(adi, M_ATKBDDEV); 264300173Sgonzo OF_prop_free(cname); 265147271Smarius continue; 266147271Smarius } 267147271Smarius intr = bus_get_resource_start(dev, SYS_RES_IRQ, adi->rid); 268147271Smarius if (intr == 0) { 269147271Smarius device_printf(dev, 270147271Smarius "<%s>: cannot determine interrupt resource\n", 271147271Smarius cname); 272147271Smarius free(adi, M_ATKBDDEV); 273300173Sgonzo OF_prop_free(cname); 274147271Smarius continue; 275147271Smarius } 276147271Smarius resource_list_init(&adi->resources); 277147271Smarius resource_list_add(&adi->resources, SYS_RES_IRQ, adi->rid, 278147271Smarius intr, intr, 1); 279147271Smarius if ((cdev = device_add_child(dev, dname, -1)) == NULL) { 280147271Smarius device_printf(dev, "<%s>: device_add_child failed\n", 281147271Smarius cname); 282147271Smarius resource_list_free(&adi->resources); 283147271Smarius free(adi, M_ATKBDDEV); 284300173Sgonzo OF_prop_free(cname); 285147271Smarius continue; 286147271Smarius } 287147271Smarius device_set_ivars(cdev, adi); 288147271Smarius children++; 289147271Smarius } 290147271Smarius 291147271Smarius error = bus_generic_attach(dev); 292147271Smarius if (error != 0) { 293147271Smarius device_printf(dev, "bus_generic_attach failed\n"); 294147271Smarius goto fail_port1; 295147271Smarius } 296147271Smarius 297147271Smarius return (0); 298147271Smarius 299147271Smarius fail_port1: 300147271Smarius bus_release_resource(dev, SYS_RES_MEMORY, 1, sc->port1); 301147271Smarius fail_port0: 302147271Smarius bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->port0); 303147271Smarius 304147271Smarius return (error); 305147271Smarius} 306