atkbdc_subr.c revision 58271
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 *
2650477Speter * $FreeBSD: head/sys/dev/atkbdc/atkbdc_subr.c 58271 2000-03-19 03:25:13Z yokota $
2743105Sdfr */
2843105Sdfr
2943105Sdfr#include "opt_kbd.h"
3043105Sdfr
3143105Sdfr#include <sys/param.h>
3243105Sdfr#include <sys/systm.h>
3343105Sdfr#include <sys/kernel.h>
3443105Sdfr#include <sys/bus.h>
3543105Sdfr#include <sys/malloc.h>
3658271Syokota#include <machine/bus_pio.h>
3747400Sdfr#include <machine/bus.h>
3847400Sdfr#include <machine/resource.h>
3947400Sdfr#include <sys/rman.h>
4043105Sdfr
4143105Sdfr#include <dev/kbd/atkbdcreg.h>
4243105Sdfr
4343105Sdfr#include <isa/isareg.h>
4443105Sdfr#include <isa/isavar.h>
4543105Sdfr
4643105SdfrMALLOC_DEFINE(M_ATKBDDEV, "atkbddev", "AT Keyboard device");
4743105Sdfr
4843105Sdfr/* children */
4943105Sdfrtypedef struct atkbdc_device {
5043105Sdfr	int flags;	/* configuration flags */
5143105Sdfr	int port;	/* port number (same as the controller's) */
5243105Sdfr	int irq;	/* ISA IRQ mask */
5343105Sdfr} atkbdc_device_t;
5443105Sdfr
5543105Sdfr/* kbdc */
5643105Sdfrdevclass_t atkbdc_devclass;
5743105Sdfr
5843105Sdfrstatic int	atkbdc_probe(device_t dev);
5943105Sdfrstatic int	atkbdc_attach(device_t dev);
6049195Smdoddstatic int	atkbdc_print_child(device_t bus, device_t dev);
6143105Sdfrstatic int	atkbdc_read_ivar(device_t bus, device_t dev, int index,
6243105Sdfr				 u_long *val);
6343105Sdfrstatic int	atkbdc_write_ivar(device_t bus, device_t dev, int index,
6443105Sdfr				  u_long val);
6543105Sdfr
6643105Sdfrstatic device_method_t atkbdc_methods[] = {
6743105Sdfr	DEVMETHOD(device_probe,		atkbdc_probe),
6843105Sdfr	DEVMETHOD(device_attach,	atkbdc_attach),
6947296Syokota	DEVMETHOD(device_suspend,	bus_generic_suspend),
7047296Syokota	DEVMETHOD(device_resume,	bus_generic_resume),
7143105Sdfr
7243105Sdfr	DEVMETHOD(bus_print_child,	atkbdc_print_child),
7343105Sdfr	DEVMETHOD(bus_read_ivar,	atkbdc_read_ivar),
7443105Sdfr	DEVMETHOD(bus_write_ivar,	atkbdc_write_ivar),
7543105Sdfr	DEVMETHOD(bus_alloc_resource,	bus_generic_alloc_resource),
7643105Sdfr	DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
7743105Sdfr	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
7843105Sdfr	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
7943105Sdfr	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
8043105Sdfr	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
8143105Sdfr
8243105Sdfr	{ 0, 0 }
8343105Sdfr};
8443105Sdfr
8543105Sdfrstatic driver_t atkbdc_driver = {
8643105Sdfr	ATKBDC_DRIVER_NAME,
8743105Sdfr	atkbdc_methods,
8843105Sdfr	sizeof(atkbdc_softc_t *),
8943105Sdfr};
9043105Sdfr
9158271Syokotastatic struct isa_pnp_id atkbdc_ids[] = {
9258271Syokota	{ 0x0303d041, "Keyboard controller (i8042)" },	/* PNP0303 */
9358271Syokota	{ 0 }
9458271Syokota};
9558271Syokota
9643105Sdfrstatic int
9743105Sdfratkbdc_probe(device_t dev)
9843105Sdfr{
9958271Syokota	struct resource	*port0;
10058271Syokota	struct resource	*port1;
10158271Syokota	int		error;
10258271Syokota	int		rid;
10343105Sdfr
10458271Syokota	/* check PnP IDs */
10558271Syokota	if (ISA_PNP_PROBE(device_get_parent(dev), dev, atkbdc_ids) == ENXIO)
10658271Syokota		return ENXIO;
10747618Sdfr
10858271Syokota	device_set_desc(dev, "Keyboard controller (i8042)");
10958271Syokota
11047400Sdfr	rid = 0;
11158271Syokota	port0 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1,
11258271Syokota				   RF_ACTIVE);
11358271Syokota	if (port0 == NULL)
11447400Sdfr		return ENXIO;
11558271Syokota	/* XXX */
11658271Syokota	if (bus_get_resource_start(dev, SYS_RES_IOPORT, 1) <= 0) {
11758271Syokota		bus_set_resource(dev, SYS_RES_IOPORT, 1,
11858271Syokota				 rman_get_start(port0) + KBD_STATUS_PORT, 1);
11958271Syokota	}
12058271Syokota	rid = 1;
12158271Syokota	port1 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1,
12258271Syokota				   RF_ACTIVE);
12358271Syokota	if (port1 == NULL) {
12458271Syokota		bus_release_resource(dev, SYS_RES_IOPORT, 0, port0);
12558271Syokota		return ENXIO;
12658271Syokota	}
12758271Syokota
12858271Syokota	error = atkbdc_probe_unit(device_get_unit(dev), port0, port1);
12958271Syokota
13058271Syokota	bus_release_resource(dev, SYS_RES_IOPORT, 0, port0);
13158271Syokota	bus_release_resource(dev, SYS_RES_IOPORT, 1, port1);
13258271Syokota
13343105Sdfr	return error;
13443105Sdfr}
13543105Sdfr
13643105Sdfrstatic void
13743105Sdfratkbdc_add_device(device_t dev, const char *name, int unit)
13843105Sdfr{
13943105Sdfr	atkbdc_device_t	*kdev;
14043105Sdfr	device_t	child;
14143105Sdfr	int		t;
14243105Sdfr
14357481Syokota	if (resource_int_value(name, unit, "disabled", &t) == 0 && t != 0)
14457481Syokota		return;
14557481Syokota
14643105Sdfr	kdev = malloc(sizeof(struct atkbdc_device), M_ATKBDDEV, M_NOWAIT);
14743105Sdfr	if (!kdev)
14843105Sdfr		return;
14943105Sdfr	bzero(kdev, sizeof *kdev);
15043105Sdfr
15143105Sdfr	if (resource_int_value(name, unit, "irq", &t) == 0)
15243105Sdfr		kdev->irq = t;
15343105Sdfr	else
15443105Sdfr		kdev->irq = -1;
15543105Sdfr
15643105Sdfr	if (resource_int_value(name, unit, "flags", &t) == 0)
15743105Sdfr		kdev->flags = t;
15843105Sdfr	else
15943105Sdfr		kdev->flags = 0;
16043105Sdfr
16154073Smdodd	child = device_add_child(dev, name, unit);
16254073Smdodd	device_set_ivars(child, kdev);
16343105Sdfr}
16443105Sdfr
16543105Sdfrstatic int
16643105Sdfratkbdc_attach(device_t dev)
16743105Sdfr{
16843105Sdfr	atkbdc_softc_t	*sc;
16947296Syokota	int		unit;
17047296Syokota	int		error;
17148341Syokota	int		rid;
17243105Sdfr	int		i;
17343105Sdfr
17447296Syokota	unit = device_get_unit(dev);
17543105Sdfr	sc = *(atkbdc_softc_t **)device_get_softc(dev);
17647296Syokota	if (sc == NULL) {
17747296Syokota		/*
17847296Syokota		 * We have to maintain two copies of the kbdc_softc struct,
17947296Syokota		 * as the low-level console needs to have access to the
18047296Syokota		 * keyboard controller before kbdc is probed and attached.
18147296Syokota		 * kbdc_soft[] contains the default entry for that purpose.
18247296Syokota		 * See atkbdc.c. XXX
18347296Syokota		 */
18447296Syokota		sc = atkbdc_get_softc(unit);
18547296Syokota		if (sc == NULL)
18647296Syokota			return ENOMEM;
18747296Syokota	}
18843105Sdfr
18948341Syokota	rid = 0;
19058271Syokota	sc->port0 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1,
19158271Syokota				       RF_ACTIVE);
19258271Syokota	if (sc->port0 == NULL)
19348341Syokota		return ENXIO;
19458271Syokota	rid = 1;
19558271Syokota	sc->port1 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1,
19658271Syokota				       RF_ACTIVE);
19758271Syokota	if (sc->port1 == NULL) {
19858271Syokota		bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port0);
19958271Syokota		return ENXIO;
20058271Syokota	}
20158271Syokota
20258271Syokota	error = atkbdc_attach_unit(unit, sc, sc->port0, sc->port1);
20358271Syokota	if (error) {
20458271Syokota		bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port0);
20558271Syokota		bus_release_resource(dev, SYS_RES_IOPORT, 1, sc->port1);
20647296Syokota		return error;
20758271Syokota	}
20847296Syokota	*(atkbdc_softc_t **)device_get_softc(dev) = sc;
20947296Syokota
21043105Sdfr	/*
21143105Sdfr	 * Add all devices configured to be attached to atkbdc0.
21243105Sdfr	 */
21343105Sdfr	for (i = resource_query_string(-1, "at", "atkbdc0");
21443105Sdfr	     i != -1;
21543105Sdfr	     i = resource_query_string(i, "at", "atkbdc0")) {
21643105Sdfr		atkbdc_add_device(dev, resource_query_name(i),
21743105Sdfr				  resource_query_unit(i));
21843105Sdfr	}
21943105Sdfr
22043105Sdfr	/*
22143105Sdfr	 * and atkbdc?
22243105Sdfr	 */
22343105Sdfr	for (i = resource_query_string(-1, "at", "atkbdc");
22443105Sdfr	     i != -1;
22543105Sdfr	     i = resource_query_string(i, "at", "atkbdc")) {
22643105Sdfr		atkbdc_add_device(dev, resource_query_name(i),
22743105Sdfr				  resource_query_unit(i));
22843105Sdfr	}
22943105Sdfr
23043105Sdfr	bus_generic_attach(dev);
23143105Sdfr
23243105Sdfr	return 0;
23343105Sdfr}
23443105Sdfr
23549195Smdoddstatic int
23643105Sdfratkbdc_print_child(device_t bus, device_t dev)
23743105Sdfr{
23843105Sdfr	atkbdc_device_t *kbdcdev;
23949195Smdodd	int retval = 0;
24043105Sdfr
24143105Sdfr	kbdcdev = (atkbdc_device_t *)device_get_ivars(dev);
24243105Sdfr
24349195Smdodd	retval += bus_print_child_header(bus, dev);
24443105Sdfr	if (kbdcdev->flags != 0)
24549195Smdodd		retval += printf(" flags 0x%x", kbdcdev->flags);
24646729Speter	if (kbdcdev->irq != -1)
24749195Smdodd		retval += printf(" irq %d", kbdcdev->irq);
24849195Smdodd	retval += bus_print_child_footer(bus, dev);
24943105Sdfr
25049195Smdodd	return (retval);
25143105Sdfr}
25243105Sdfr
25343105Sdfrstatic int
25443105Sdfratkbdc_read_ivar(device_t bus, device_t dev, int index, u_long *val)
25543105Sdfr{
25643105Sdfr	atkbdc_device_t *ivar;
25743105Sdfr
25843105Sdfr	ivar = (atkbdc_device_t *)device_get_ivars(dev);
25943105Sdfr	switch (index) {
26043105Sdfr	case KBDC_IVAR_PORT:
26143105Sdfr		*val = (u_long)ivar->port;
26243105Sdfr		break;
26343105Sdfr	case KBDC_IVAR_IRQ:
26443105Sdfr		*val = (u_long)ivar->irq;
26543105Sdfr		break;
26643105Sdfr	case KBDC_IVAR_FLAGS:
26743105Sdfr		*val = (u_long)ivar->flags;
26843105Sdfr		break;
26943105Sdfr	default:
27043105Sdfr		return ENOENT;
27143105Sdfr	}
27243105Sdfr	return 0;
27343105Sdfr}
27443105Sdfr
27543105Sdfrstatic int
27643105Sdfratkbdc_write_ivar(device_t bus, device_t dev, int index, u_long val)
27743105Sdfr{
27843105Sdfr	atkbdc_device_t *ivar;
27943105Sdfr
28043105Sdfr	ivar = (atkbdc_device_t *)device_get_ivars(dev);
28143105Sdfr	switch (index) {
28243105Sdfr	case KBDC_IVAR_PORT:
28343105Sdfr		ivar->port = (int)val;
28443105Sdfr		break;
28543105Sdfr	case KBDC_IVAR_IRQ:
28643105Sdfr		ivar->irq = (int)val;
28743105Sdfr		break;
28843105Sdfr	case KBDC_IVAR_FLAGS:
28943105Sdfr		ivar->flags = (int)val;
29043105Sdfr		break;
29143105Sdfr	default:
29243105Sdfr		return ENOENT;
29343105Sdfr	}
29443105Sdfr	return 0;
29543105Sdfr}
29643105Sdfr
29743105SdfrDRIVER_MODULE(atkbdc, isa, atkbdc_driver, atkbdc_devclass, 0, 0);
298