| Commit | Line | Data |
|---|---|---|
| 984263bc | 1 | /*- |
| 22ff886e AH |
2 | * (MPSAFE) |
| 3 | * | |
| 984263bc MD |
4 | * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp> |
| 5 | * All rights reserved. | |
| 6 | * | |
| 7 | * Redistribution and use in source and binary forms, with or without | |
| 8 | * modification, are permitted provided that the following conditions | |
| 9 | * are met: | |
| 10 | * 1. Redistributions of source code must retain the above copyright | |
| 11 | * notice, this list of conditions and the following disclaimer as | |
| 12 | * the first lines of this file unmodified. | |
| 13 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 14 | * notice, this list of conditions and the following disclaimer in the | |
| 15 | * documentation and/or other materials provided with the distribution. | |
| 16 | * | |
| 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR | |
| 18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
| 19 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
| 20 | * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
| 21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
| 22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
| 26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 27 | * | |
| 28 | * $FreeBSD: src/sys/isa/atkbdc_isa.c,v 1.14.2.1 2000/03/31 12:52:05 yokota Exp $ | |
| 29 | */ | |
| 30 | ||
| 31 | #include "opt_kbd.h" | |
| 32 | ||
| 33 | #include <sys/param.h> | |
| 34 | #include <sys/systm.h> | |
| 35 | #include <sys/kernel.h> | |
| 36 | #include <sys/bus.h> | |
| 37 | #include <sys/malloc.h> | |
| 984263bc | 38 | #include <sys/rman.h> |
| 22ff886e | 39 | #include <sys/thread.h> |
| 984263bc | 40 | |
| 1f2de5d4 | 41 | #include <dev/misc/kbd/atkbdcreg.h> |
| 984263bc | 42 | |
| 1f2de5d4 MD |
43 | #include <bus/isa/isareg.h> |
| 44 | #include <bus/isa/isavar.h> | |
| 984263bc | 45 | |
| 22ff886e AH |
46 | #if 0 |
| 47 | #define lwkt_gettoken(x) | |
| 48 | #define lwkt_reltoken(x) | |
| 49 | #endif | |
| 50 | ||
| 984263bc MD |
51 | MALLOC_DEFINE(M_ATKBDDEV, "atkbddev", "AT Keyboard device"); |
| 52 | ||
| 53 | /* children */ | |
| 54 | typedef struct atkbdc_device { | |
| 55 | int flags; /* configuration flags */ | |
| 56 | int irq; /* ISA IRQ mask */ | |
| 57 | u_int32_t vendorid; | |
| 58 | u_int32_t serial; | |
| 59 | u_int32_t logicalid; | |
| 60 | u_int32_t compatid; | |
| 61 | } atkbdc_device_t; | |
| 62 | ||
| 63 | /* kbdc */ | |
| 64 | devclass_t atkbdc_devclass; | |
| 65 | ||
| 66 | static int atkbdc_probe(device_t dev); | |
| 67 | static int atkbdc_attach(device_t dev); | |
| 68 | static int atkbdc_print_child(device_t bus, device_t dev); | |
| 69 | static int atkbdc_read_ivar(device_t bus, device_t dev, int index, | |
| 70 | u_long *val); | |
| 71 | static int atkbdc_write_ivar(device_t bus, device_t dev, int index, | |
| 72 | u_long val); | |
| 73 | ||
| 74 | static device_method_t atkbdc_methods[] = { | |
| 75 | DEVMETHOD(device_probe, atkbdc_probe), | |
| 76 | DEVMETHOD(device_attach, atkbdc_attach), | |
| 77 | DEVMETHOD(device_suspend, bus_generic_suspend), | |
| 78 | DEVMETHOD(device_resume, bus_generic_resume), | |
| 79 | ||
| 80 | DEVMETHOD(bus_print_child, atkbdc_print_child), | |
| 81 | DEVMETHOD(bus_read_ivar, atkbdc_read_ivar), | |
| 82 | DEVMETHOD(bus_write_ivar, atkbdc_write_ivar), | |
| 83 | DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), | |
| 84 | DEVMETHOD(bus_release_resource, bus_generic_release_resource), | |
| 85 | DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), | |
| 86 | DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), | |
| 87 | DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), | |
| 88 | DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), | |
| 89 | ||
| 90 | { 0, 0 } | |
| 91 | }; | |
| 92 | ||
| 93 | static driver_t atkbdc_driver = { | |
| 94 | ATKBDC_DRIVER_NAME, | |
| 95 | atkbdc_methods, | |
| 96 | sizeof(atkbdc_softc_t *), | |
| 97 | }; | |
| 98 | ||
| 99 | static struct isa_pnp_id atkbdc_ids[] = { | |
| 100 | { 0x0303d041, "Keyboard controller (i8042)" }, /* PNP0303 */ | |
| 101 | { 0 } | |
| 102 | }; | |
| 103 | ||
| 104 | static int | |
| 105 | atkbdc_probe(device_t dev) | |
| 106 | { | |
| 107 | struct resource *port0; | |
| 108 | struct resource *port1; | |
| 109 | int error; | |
| 110 | int rid; | |
| d435571a | 111 | #if defined(__i386__) || defined(__x86_64__) |
| 269ffd40 YT |
112 | bus_space_tag_t tag; |
| 113 | bus_space_handle_t ioh1; | |
| 114 | volatile int i; | |
| 115 | #endif | |
| 984263bc MD |
116 | |
| 117 | /* check PnP IDs */ | |
| 118 | if (ISA_PNP_PROBE(device_get_parent(dev), dev, atkbdc_ids) == ENXIO) | |
| 119 | return ENXIO; | |
| 120 | ||
| 121 | device_set_desc(dev, "Keyboard controller (i8042)"); | |
| 122 | ||
| 123 | rid = 0; | |
| 124 | port0 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, | |
| 125 | RF_ACTIVE); | |
| 126 | if (port0 == NULL) | |
| 127 | return ENXIO; | |
| 128 | /* XXX */ | |
| 129 | if (bus_get_resource_start(dev, SYS_RES_IOPORT, 1) <= 0) { | |
| 130 | bus_set_resource(dev, SYS_RES_IOPORT, 1, | |
| 131 | rman_get_start(port0) + KBD_STATUS_PORT, 1); | |
| 132 | } | |
| 133 | rid = 1; | |
| 134 | port1 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, | |
| 135 | RF_ACTIVE); | |
| 136 | if (port1 == NULL) { | |
| 137 | bus_release_resource(dev, SYS_RES_IOPORT, 0, port0); | |
| 138 | return ENXIO; | |
| 139 | } | |
| 140 | ||
| d435571a | 141 | #if defined(__i386__) || defined(__x86_64__) |
| 269ffd40 YT |
142 | /* |
| 143 | * Check if we really have AT keyboard controller. Poll status | |
| 144 | * register until we get "all clear" indication. If no such | |
| 145 | * indication comes, it probably means that there is no AT | |
| 146 | * keyboard controller present. Give up in such case. Check relies | |
| 147 | * on the fact that reading from non-existing in/out port returns | |
| 148 | * 0xff on i386. May or may not be true on other platforms. | |
| 149 | */ | |
| 150 | tag = rman_get_bustag(port0); | |
| 151 | ioh1 = rman_get_bushandle(port1); | |
| 152 | for (i = 65536; i != 0; --i) { | |
| 153 | if ((bus_space_read_1(tag, ioh1, 0) & 0x2) == 0) | |
| 154 | break; | |
| a200774b | 155 | DELAY(16); |
| 269ffd40 YT |
156 | } |
| 157 | if (i == 0) { | |
| 158 | bus_release_resource(dev, SYS_RES_IOPORT, 0, port0); | |
| 159 | bus_release_resource(dev, SYS_RES_IOPORT, 1, port1); | |
| 160 | return ENXIO; | |
| 161 | } | |
| 162 | #endif | |
| 163 | ||
| 984263bc MD |
164 | error = atkbdc_probe_unit(device_get_unit(dev), port0, port1); |
| 165 | ||
| 166 | bus_release_resource(dev, SYS_RES_IOPORT, 0, port0); | |
| 167 | bus_release_resource(dev, SYS_RES_IOPORT, 1, port1); | |
| 168 | ||
| 169 | return error; | |
| 170 | } | |
| 171 | ||
| 172 | static void | |
| 173 | atkbdc_add_device(device_t dev, const char *name, int unit) | |
| 174 | { | |
| 175 | atkbdc_device_t *kdev; | |
| 176 | device_t child; | |
| 177 | int t; | |
| 178 | ||
| 179 | if (resource_int_value(name, unit, "disabled", &t) == 0 && t != 0) | |
| 180 | return; | |
| 181 | ||
| efda3bd0 | 182 | kdev = kmalloc(sizeof(struct atkbdc_device), M_ATKBDDEV, |
| bf22d4c1 | 183 | M_WAITOK | M_ZERO); |
| 984263bc MD |
184 | |
| 185 | if (resource_int_value(name, unit, "irq", &t) == 0) | |
| 186 | kdev->irq = t; | |
| 187 | else | |
| 188 | kdev->irq = -1; | |
| 189 | ||
| 190 | if (resource_int_value(name, unit, "flags", &t) == 0) | |
| 191 | kdev->flags = t; | |
| 192 | else | |
| 193 | kdev->flags = 0; | |
| 194 | ||
| 195 | child = device_add_child(dev, name, unit); | |
| 196 | device_set_ivars(child, kdev); | |
| 197 | } | |
| 198 | ||
| 199 | static int | |
| 200 | atkbdc_attach(device_t dev) | |
| 201 | { | |
| 202 | atkbdc_softc_t *sc; | |
| 203 | int unit; | |
| 204 | int error; | |
| 205 | int rid; | |
| 206 | int i; | |
| 207 | ||
| 22ff886e | 208 | lwkt_gettoken(&tty_token); |
| 984263bc MD |
209 | unit = device_get_unit(dev); |
| 210 | sc = *(atkbdc_softc_t **)device_get_softc(dev); | |
| 211 | if (sc == NULL) { | |
| 212 | /* | |
| 213 | * We have to maintain two copies of the kbdc_softc struct, | |
| 214 | * as the low-level console needs to have access to the | |
| 215 | * keyboard controller before kbdc is probed and attached. | |
| 216 | * kbdc_soft[] contains the default entry for that purpose. | |
| 217 | * See atkbdc.c. XXX | |
| 218 | */ | |
| 219 | sc = atkbdc_get_softc(unit); | |
| 22ff886e AH |
220 | if (sc == NULL) { |
| 221 | lwkt_reltoken(&tty_token); | |
| 984263bc | 222 | return ENOMEM; |
| 22ff886e | 223 | } |
| 984263bc MD |
224 | } |
| 225 | ||
| 226 | rid = 0; | |
| 227 | sc->port0 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, | |
| 228 | RF_ACTIVE); | |
| 22ff886e AH |
229 | if (sc->port0 == NULL) { |
| 230 | lwkt_reltoken(&tty_token); | |
| 984263bc | 231 | return ENXIO; |
| 22ff886e | 232 | } |
| 984263bc MD |
233 | rid = 1; |
| 234 | sc->port1 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, | |
| 235 | RF_ACTIVE); | |
| 236 | if (sc->port1 == NULL) { | |
| 237 | bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port0); | |
| 22ff886e | 238 | lwkt_reltoken(&tty_token); |
| 984263bc MD |
239 | return ENXIO; |
| 240 | } | |
| 241 | ||
| 242 | error = atkbdc_attach_unit(unit, sc, sc->port0, sc->port1); | |
| 243 | if (error) { | |
| 244 | bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port0); | |
| 245 | bus_release_resource(dev, SYS_RES_IOPORT, 1, sc->port1); | |
| 22ff886e | 246 | lwkt_reltoken(&tty_token); |
| 984263bc MD |
247 | return error; |
| 248 | } | |
| 249 | *(atkbdc_softc_t **)device_get_softc(dev) = sc; | |
| 250 | ||
| 251 | /* | |
| 252 | * Add all devices configured to be attached to atkbdc0. | |
| 253 | */ | |
| 254 | for (i = resource_query_string(-1, "at", device_get_nameunit(dev)); | |
| 255 | i != -1; | |
| 256 | i = resource_query_string(i, "at", device_get_nameunit(dev))) { | |
| 257 | atkbdc_add_device(dev, resource_query_name(i), | |
| 258 | resource_query_unit(i)); | |
| 259 | } | |
| 260 | ||
| 261 | /* | |
| 262 | * and atkbdc? | |
| 263 | */ | |
| 264 | for (i = resource_query_string(-1, "at", device_get_name(dev)); | |
| 265 | i != -1; | |
| 266 | i = resource_query_string(i, "at", device_get_name(dev))) { | |
| 267 | atkbdc_add_device(dev, resource_query_name(i), | |
| 268 | resource_query_unit(i)); | |
| 269 | } | |
| 270 | ||
| 271 | bus_generic_attach(dev); | |
| 272 | ||
| 22ff886e | 273 | lwkt_reltoken(&tty_token); |
| 984263bc MD |
274 | return 0; |
| 275 | } | |
| 276 | ||
| 277 | static int | |
| 278 | atkbdc_print_child(device_t bus, device_t dev) | |
| 279 | { | |
| 280 | atkbdc_device_t *kbdcdev; | |
| 281 | int retval = 0; | |
| 282 | ||
| 283 | kbdcdev = (atkbdc_device_t *)device_get_ivars(dev); | |
| 284 | ||
| 285 | retval += bus_print_child_header(bus, dev); | |
| 286 | if (kbdcdev->flags != 0) | |
| e3869ec7 | 287 | retval += kprintf(" flags 0x%x", kbdcdev->flags); |
| 984263bc | 288 | if (kbdcdev->irq != -1) |
| e3869ec7 | 289 | retval += kprintf(" irq %d", kbdcdev->irq); |
| 984263bc MD |
290 | retval += bus_print_child_footer(bus, dev); |
| 291 | ||
| 292 | return (retval); | |
| 293 | } | |
| 294 | ||
| 295 | static int | |
| 296 | atkbdc_read_ivar(device_t bus, device_t dev, int index, u_long *val) | |
| 297 | { | |
| 298 | atkbdc_device_t *ivar; | |
| 299 | ||
| 22ff886e | 300 | lwkt_gettoken(&tty_token); |
| 984263bc MD |
301 | ivar = (atkbdc_device_t *)device_get_ivars(dev); |
| 302 | switch (index) { | |
| 303 | case KBDC_IVAR_IRQ: | |
| 304 | *val = (u_long)ivar->irq; | |
| 305 | break; | |
| 306 | case KBDC_IVAR_FLAGS: | |
| 307 | *val = (u_long)ivar->flags; | |
| 308 | break; | |
| 309 | case KBDC_IVAR_VENDORID: | |
| 310 | *val = (u_long)ivar->vendorid; | |
| 311 | break; | |
| 312 | case KBDC_IVAR_SERIAL: | |
| 313 | *val = (u_long)ivar->serial; | |
| 314 | break; | |
| 315 | case KBDC_IVAR_LOGICALID: | |
| 316 | *val = (u_long)ivar->logicalid; | |
| 317 | break; | |
| 318 | case KBDC_IVAR_COMPATID: | |
| 319 | *val = (u_long)ivar->compatid; | |
| 320 | break; | |
| 321 | default: | |
| 22ff886e | 322 | lwkt_reltoken(&tty_token); |
| 984263bc MD |
323 | return ENOENT; |
| 324 | } | |
| 22ff886e AH |
325 | |
| 326 | lwkt_reltoken(&tty_token); | |
| 984263bc MD |
327 | return 0; |
| 328 | } | |
| 329 | ||
| 330 | static int | |
| 331 | atkbdc_write_ivar(device_t bus, device_t dev, int index, u_long val) | |
| 332 | { | |
| 333 | atkbdc_device_t *ivar; | |
| 334 | ||
| 22ff886e | 335 | lwkt_gettoken(&tty_token); |
| 984263bc MD |
336 | ivar = (atkbdc_device_t *)device_get_ivars(dev); |
| 337 | switch (index) { | |
| 338 | case KBDC_IVAR_IRQ: | |
| 339 | ivar->irq = (int)val; | |
| 340 | break; | |
| 341 | case KBDC_IVAR_FLAGS: | |
| 342 | ivar->flags = (int)val; | |
| 343 | break; | |
| 344 | case KBDC_IVAR_VENDORID: | |
| 345 | ivar->vendorid = (u_int32_t)val; | |
| 346 | break; | |
| 347 | case KBDC_IVAR_SERIAL: | |
| 348 | ivar->serial = (u_int32_t)val; | |
| 349 | break; | |
| 350 | case KBDC_IVAR_LOGICALID: | |
| 351 | ivar->logicalid = (u_int32_t)val; | |
| 352 | break; | |
| 353 | case KBDC_IVAR_COMPATID: | |
| 354 | ivar->compatid = (u_int32_t)val; | |
| 355 | break; | |
| 356 | default: | |
| 22ff886e | 357 | lwkt_reltoken(&tty_token); |
| 984263bc MD |
358 | return ENOENT; |
| 359 | } | |
| 22ff886e AH |
360 | |
| 361 | lwkt_reltoken(&tty_token); | |
| 984263bc MD |
362 | return 0; |
| 363 | } | |
| 364 | ||
| aa2b9d05 SW |
365 | DRIVER_MODULE(atkbdc, isa, atkbdc_driver, atkbdc_devclass, NULL, NULL); |
| 366 | DRIVER_MODULE(atkbdc, acpi, atkbdc_driver, atkbdc_devclass, NULL, NULL); |