143105Sdfr/*- 243105Sdfr * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp> 343105Sdfr * All rights reserved. 443105Sdfr * 543105Sdfr * Redistribution and use in source and binary forms, with or without 643105Sdfr * modification, are permitted provided that the following conditions 743105Sdfr * are met: 843105Sdfr * 1. Redistributions of source code must retain the above copyright 943105Sdfr * notice, this list of conditions and the following disclaimer as 1043105Sdfr * the first lines of this file unmodified. 1143105Sdfr * 2. Redistributions in binary form must reproduce the above copyright 1243105Sdfr * notice, this list of conditions and the following disclaimer in the 1343105Sdfr * documentation and/or other materials provided with the distribution. 1443105Sdfr * 1543105Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 1643105Sdfr * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1743105Sdfr * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1843105Sdfr * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 1943105Sdfr * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2043105Sdfr * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2143105Sdfr * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2243105Sdfr * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2343105Sdfr * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2443105Sdfr * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2543105Sdfr */ 2643105Sdfr 27116181Sobrien#include <sys/cdefs.h> 28116181Sobrien__FBSDID("$FreeBSD: releng/10.2/sys/dev/atkbdc/atkbdc_isa.c 238164 2012-07-06 12:13:28Z jhb $"); 29116181Sobrien 3043105Sdfr#include "opt_kbd.h" 3143105Sdfr 3243105Sdfr#include <sys/param.h> 3343105Sdfr#include <sys/systm.h> 3443105Sdfr#include <sys/kernel.h> 35129880Sphk#include <sys/module.h> 3643105Sdfr#include <sys/bus.h> 3743105Sdfr#include <sys/malloc.h> 3847400Sdfr#include <machine/resource.h> 3947400Sdfr#include <sys/rman.h> 40159541Simp#include <machine/bus.h> 4143105Sdfr 42147271Smarius#include <dev/atkbdc/atkbdc_subr.h> 43147271Smarius#include <dev/atkbdc/atkbdcreg.h> 4443105Sdfr 4543105Sdfr#include <isa/isareg.h> 4643105Sdfr#include <isa/isavar.h> 4743105Sdfr 48147271Smariusstatic int atkbdc_isa_probe(device_t dev); 49147271Smariusstatic int atkbdc_isa_attach(device_t dev); 50212413Savgstatic device_t atkbdc_isa_add_child(device_t bus, u_int order, const char *name, 51188160Simp int unit); 52216492Sjhbstatic struct resource *atkbdc_isa_alloc_resource(device_t dev, device_t child, 53216492Sjhb int type, int *rid, u_long start, u_long end, 54216492Sjhb u_long count, u_int flags); 55216492Sjhbstatic int atkbdc_isa_release_resource(device_t dev, device_t child, 56216492Sjhb int type, int rid, struct resource *r); 5743105Sdfr 58147271Smariusstatic device_method_t atkbdc_isa_methods[] = { 59147271Smarius DEVMETHOD(device_probe, atkbdc_isa_probe), 60147271Smarius DEVMETHOD(device_attach, atkbdc_isa_attach), 6147296Syokota DEVMETHOD(device_suspend, bus_generic_suspend), 6247296Syokota DEVMETHOD(device_resume, bus_generic_resume), 6343105Sdfr 64147271Smarius DEVMETHOD(bus_add_child, atkbdc_isa_add_child), 6543105Sdfr DEVMETHOD(bus_print_child, atkbdc_print_child), 6643105Sdfr DEVMETHOD(bus_read_ivar, atkbdc_read_ivar), 6743105Sdfr DEVMETHOD(bus_write_ivar, atkbdc_write_ivar), 6883147Syokota DEVMETHOD(bus_get_resource_list,atkbdc_get_resource_list), 69216492Sjhb DEVMETHOD(bus_alloc_resource, atkbdc_isa_alloc_resource), 70216492Sjhb DEVMETHOD(bus_release_resource, atkbdc_isa_release_resource), 7143105Sdfr DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 7243105Sdfr DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 7383147Syokota DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), 7483147Syokota DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), 7583147Syokota DEVMETHOD(bus_delete_resource, bus_generic_rl_delete_resource), 7643105Sdfr DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 7743105Sdfr DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 7843105Sdfr 7943105Sdfr { 0, 0 } 8043105Sdfr}; 8143105Sdfr 82147271Smariusstatic driver_t atkbdc_isa_driver = { 8343105Sdfr ATKBDC_DRIVER_NAME, 84147271Smarius atkbdc_isa_methods, 8543105Sdfr sizeof(atkbdc_softc_t *), 8643105Sdfr}; 8743105Sdfr 8858271Syokotastatic struct isa_pnp_id atkbdc_ids[] = { 8958271Syokota { 0x0303d041, "Keyboard controller (i8042)" }, /* PNP0303 */ 90238164Sjhb { 0x0b03d041, "Keyboard controller (i8042)" }, /* PNP030B */ 91233619Sjkim { 0x2003d041, "Keyboard controller (i8042)" }, /* PNP0320 */ 9258271Syokota { 0 } 9358271Syokota}; 9458271Syokota 9543105Sdfrstatic int 96147271Smariusatkbdc_isa_probe(device_t dev) 9743105Sdfr{ 9858271Syokota struct resource *port0; 9958271Syokota struct resource *port1; 10083147Syokota u_long start; 10183147Syokota u_long count; 10258271Syokota int error; 10358271Syokota int rid; 104207354Ssobomax#if defined(__i386__) || defined(__amd64__) 105158041Ssobomax bus_space_tag_t tag; 106158041Ssobomax bus_space_handle_t ioh1; 107158041Ssobomax volatile int i; 108158041Ssobomax register_t flags; 109158041Ssobomax#endif 11043105Sdfr 11158271Syokota /* check PnP IDs */ 11258271Syokota if (ISA_PNP_PROBE(device_get_parent(dev), dev, atkbdc_ids) == ENXIO) 11358271Syokota return ENXIO; 11447618Sdfr 11558271Syokota device_set_desc(dev, "Keyboard controller (i8042)"); 11658271Syokota 11783147Syokota /* 11883147Syokota * Adjust I/O port resources. 11983147Syokota * The AT keyboard controller uses two ports (a command/data port 12083147Syokota * 0x60 and a status port 0x64), which may be given to us in 12183147Syokota * one resource (0x60 through 0x64) or as two separate resources 122160091Sjkim * (0x60 and 0x64). Some brain-damaged ACPI BIOS has reversed 123160091Sjkim * command/data port and status port. Furthermore, /boot/device.hints 124160091Sjkim * may contain just one port, 0x60. We shall adjust resource settings 125160091Sjkim * so that these two ports are available as two separate resources 126160091Sjkim * in correct order. 12783147Syokota */ 12883147Syokota device_quiet(dev); 12947400Sdfr rid = 0; 13083147Syokota if (bus_get_resource(dev, SYS_RES_IOPORT, rid, &start, &count) != 0) 13183147Syokota return ENXIO; 132160091Sjkim if (start == IO_KBD + KBD_STATUS_PORT) { 133160091Sjkim start = IO_KBD; 134160091Sjkim count++; 135160091Sjkim } 136160091Sjkim if (count > 1) /* adjust the count and/or start port */ 13783147Syokota bus_set_resource(dev, SYS_RES_IOPORT, rid, start, 1); 138127135Snjl port0 = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); 13958271Syokota if (port0 == NULL) 14047400Sdfr return ENXIO; 14183147Syokota rid = 1; 14283147Syokota if (bus_get_resource(dev, SYS_RES_IOPORT, rid, NULL, NULL) != 0) 14358271Syokota bus_set_resource(dev, SYS_RES_IOPORT, 1, 14483147Syokota start + KBD_STATUS_PORT, 1); 145127135Snjl port1 = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); 14658271Syokota if (port1 == NULL) { 14758271Syokota bus_release_resource(dev, SYS_RES_IOPORT, 0, port0); 14858271Syokota return ENXIO; 14958271Syokota } 150158041Ssobomax 151207354Ssobomax#if defined(__i386__) || defined(__amd64__) 152158041Ssobomax /* 153158041Ssobomax * Check if we really have AT keyboard controller. Poll status 154158041Ssobomax * register until we get "all clear" indication. If no such 155158041Ssobomax * indication comes, it probably means that there is no AT 156158041Ssobomax * keyboard controller present. Give up in such case. Check relies 157158041Ssobomax * on the fact that reading from non-existing in/out port returns 158158041Ssobomax * 0xff on i386. May or may not be true on other platforms. 159158041Ssobomax */ 160158041Ssobomax tag = rman_get_bustag(port0); 161158041Ssobomax ioh1 = rman_get_bushandle(port1); 162158041Ssobomax flags = intr_disable(); 163158041Ssobomax for (i = 0; i != 65535; i++) { 164158041Ssobomax if ((bus_space_read_1(tag, ioh1, 0) & 0x2) == 0) 165158041Ssobomax break; 166158041Ssobomax } 167158041Ssobomax intr_restore(flags); 168158041Ssobomax if (i == 65535) { 169158041Ssobomax bus_release_resource(dev, SYS_RES_IOPORT, 0, port0); 170158041Ssobomax bus_release_resource(dev, SYS_RES_IOPORT, 1, port1); 171207354Ssobomax if (bootverbose) 172207354Ssobomax device_printf(dev, "AT keyboard controller not found\n"); 173158041Ssobomax return ENXIO; 174158041Ssobomax } 175158041Ssobomax#endif 176158041Ssobomax 17783147Syokota device_verbose(dev); 17858271Syokota 17958271Syokota error = atkbdc_probe_unit(device_get_unit(dev), port0, port1); 18058271Syokota 18158271Syokota bus_release_resource(dev, SYS_RES_IOPORT, 0, port0); 18258271Syokota bus_release_resource(dev, SYS_RES_IOPORT, 1, port1); 18358271Syokota 18443105Sdfr return error; 18543105Sdfr} 18643105Sdfr 18743105Sdfrstatic int 188147271Smariusatkbdc_isa_attach(device_t dev) 18943105Sdfr{ 19043105Sdfr atkbdc_softc_t *sc; 19183147Syokota int unit; 19247296Syokota int error; 19348341Syokota int rid; 19443105Sdfr 19547296Syokota unit = device_get_unit(dev); 19643105Sdfr sc = *(atkbdc_softc_t **)device_get_softc(dev); 19747296Syokota if (sc == NULL) { 19847296Syokota /* 19947296Syokota * We have to maintain two copies of the kbdc_softc struct, 20047296Syokota * as the low-level console needs to have access to the 201147271Smarius * keyboard controller before kbdc is probed and attached. 20247296Syokota * kbdc_soft[] contains the default entry for that purpose. 20347296Syokota * See atkbdc.c. XXX 20447296Syokota */ 20547296Syokota sc = atkbdc_get_softc(unit); 20647296Syokota if (sc == NULL) 20747296Syokota return ENOMEM; 20847296Syokota } 20943105Sdfr 21048341Syokota rid = 0; 211207354Ssobomax sc->retry = 5000; 212127135Snjl sc->port0 = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 213127135Snjl RF_ACTIVE); 21458271Syokota if (sc->port0 == NULL) 21548341Syokota return ENXIO; 21658271Syokota rid = 1; 217127135Snjl sc->port1 = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 218127135Snjl RF_ACTIVE); 21958271Syokota if (sc->port1 == NULL) { 22058271Syokota bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port0); 22158271Syokota return ENXIO; 22258271Syokota } 22358271Syokota 224216492Sjhb /* 225216492Sjhb * If the device is not created by the PnP BIOS or ACPI, then 226216492Sjhb * the hint for the IRQ is on the child atkbd device, not the 227216492Sjhb * keyboard controller, so this can fail. 228216492Sjhb */ 229216492Sjhb rid = 0; 230216492Sjhb sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); 231216492Sjhb 23258271Syokota error = atkbdc_attach_unit(unit, sc, sc->port0, sc->port1); 23358271Syokota if (error) { 23458271Syokota bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port0); 23558271Syokota bus_release_resource(dev, SYS_RES_IOPORT, 1, sc->port1); 236216492Sjhb if (sc->irq != NULL) 237216492Sjhb bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); 23847296Syokota return error; 23958271Syokota } 24047296Syokota *(atkbdc_softc_t **)device_get_softc(dev) = sc; 24147296Syokota 242216492Sjhb bus_generic_probe(dev); 24383147Syokota bus_generic_attach(dev); 24443105Sdfr 24583147Syokota return 0; 24683147Syokota} 24783147Syokota 24883147Syokotastatic device_t 249212413Savgatkbdc_isa_add_child(device_t bus, u_int order, const char *name, int unit) 25083147Syokota{ 25183147Syokota atkbdc_device_t *ivar; 252216492Sjhb atkbdc_softc_t *sc; 25383147Syokota device_t child; 25483147Syokota int t; 25583147Syokota 256216492Sjhb sc = *(atkbdc_softc_t **)device_get_softc(bus); 25783147Syokota ivar = malloc(sizeof(struct atkbdc_device), M_ATKBDDEV, 25883147Syokota M_NOWAIT | M_ZERO); 25983147Syokota if (!ivar) 26083147Syokota return NULL; 26183147Syokota 26283931Syokota child = device_add_child_ordered(bus, order, name, unit); 26383147Syokota if (child == NULL) { 26483147Syokota free(ivar, M_ATKBDDEV); 26583147Syokota return child; 26683147Syokota } 26783147Syokota 26883147Syokota resource_list_init(&ivar->resources); 26983147Syokota ivar->rid = order; 27083147Syokota 27143105Sdfr /* 272216492Sjhb * If the device is not created by the PnP BIOS or ACPI, refer 273216492Sjhb * to device hints for IRQ. We always populate the resource 274216492Sjhb * list entry so we can use a standard bus_get_resource() 275216492Sjhb * method. 27643105Sdfr */ 277216614Sjhb if (order == KBDC_RID_KBD) { 278216614Sjhb if (sc->irq == NULL) { 279216614Sjhb if (resource_int_value(name, unit, "irq", &t) != 0) 280216614Sjhb t = -1; 281216614Sjhb } else 282216614Sjhb t = rman_get_start(sc->irq); 283216614Sjhb if (t > 0) 284216614Sjhb resource_list_add(&ivar->resources, SYS_RES_IRQ, 285216614Sjhb ivar->rid, t, t, 1); 286216614Sjhb } 28743105Sdfr 288117167Sjhb if (resource_disabled(name, unit)) 28983147Syokota device_disable(child); 29043105Sdfr 29183147Syokota device_set_ivars(child, ivar); 29283147Syokota 29383147Syokota return child; 29443105Sdfr} 29543105Sdfr 296216492Sjhbstruct resource * 297216492Sjhbatkbdc_isa_alloc_resource(device_t dev, device_t child, int type, int *rid, 298216492Sjhb u_long start, u_long end, u_long count, u_int flags) 299216492Sjhb{ 300216492Sjhb atkbdc_softc_t *sc; 301216492Sjhb 302216492Sjhb sc = *(atkbdc_softc_t **)device_get_softc(dev); 303216492Sjhb if (type == SYS_RES_IRQ && *rid == KBDC_RID_KBD && sc->irq != NULL) 304216492Sjhb return (sc->irq); 305216492Sjhb return (bus_generic_rl_alloc_resource(dev, child, type, rid, start, 306216492Sjhb end, count, flags)); 307216492Sjhb} 308216492Sjhb 309216492Sjhbstatic int 310216492Sjhbatkbdc_isa_release_resource(device_t dev, device_t child, int type, int rid, 311216492Sjhb struct resource *r) 312216492Sjhb{ 313216492Sjhb atkbdc_softc_t *sc; 314216492Sjhb 315216492Sjhb sc = *(atkbdc_softc_t **)device_get_softc(dev); 316216492Sjhb if (type == SYS_RES_IRQ && rid == KBDC_RID_KBD && r == sc->irq) 317216492Sjhb return (0); 318216492Sjhb return (bus_generic_rl_release_resource(dev, child, type, rid, r)); 319216492Sjhb} 320216492Sjhb 321147271SmariusDRIVER_MODULE(atkbdc, isa, atkbdc_isa_driver, atkbdc_devclass, 0, 0); 322147271SmariusDRIVER_MODULE(atkbdc, acpi, atkbdc_isa_driver, atkbdc_devclass, 0, 0); 323