atkbdc_isa.c revision 188160
10Sstevel@tonic-gate/*-
20Sstevel@tonic-gate * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
30Sstevel@tonic-gate * All rights reserved.
40Sstevel@tonic-gate *
52660Sjacobs * Redistribution and use in source and binary forms, with or without
62660Sjacobs * modification, are permitted provided that the following conditions
70Sstevel@tonic-gate * are met:
80Sstevel@tonic-gate * 1. Redistributions of source code must retain the above copyright
90Sstevel@tonic-gate *    notice, this list of conditions and the following disclaimer as
100Sstevel@tonic-gate *    the first lines of this file unmodified.
110Sstevel@tonic-gate * 2. Redistributions in binary form must reproduce the above copyright
120Sstevel@tonic-gate *    notice, this list of conditions and the following disclaimer in the
130Sstevel@tonic-gate *    documentation and/or other materials provided with the distribution.
140Sstevel@tonic-gate *
150Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
160Sstevel@tonic-gate * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
170Sstevel@tonic-gate * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
180Sstevel@tonic-gate * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
190Sstevel@tonic-gate * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
200Sstevel@tonic-gate * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
212660Sjacobs * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
222660Sjacobs * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
232660Sjacobs * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
242660Sjacobs * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
252660Sjacobs */
262660Sjacobs
270Sstevel@tonic-gate#include <sys/cdefs.h>
280Sstevel@tonic-gate__FBSDID("$FreeBSD: head/sys/dev/atkbdc/atkbdc_isa.c 188160 2009-02-05 18:38:39Z imp $");
290Sstevel@tonic-gate
300Sstevel@tonic-gate#include "opt_kbd.h"
310Sstevel@tonic-gate
320Sstevel@tonic-gate#include <sys/param.h>
330Sstevel@tonic-gate#include <sys/systm.h>
340Sstevel@tonic-gate#include <sys/kernel.h>
350Sstevel@tonic-gate#include <sys/module.h>
360Sstevel@tonic-gate#include <sys/bus.h>
370Sstevel@tonic-gate#include <sys/malloc.h>
380Sstevel@tonic-gate#include <machine/resource.h>
390Sstevel@tonic-gate#include <sys/rman.h>
400Sstevel@tonic-gate#include <machine/bus.h>
410Sstevel@tonic-gate
420Sstevel@tonic-gate#include <dev/atkbdc/atkbdc_subr.h>
430Sstevel@tonic-gate#include <dev/atkbdc/atkbdcreg.h>
440Sstevel@tonic-gate
450Sstevel@tonic-gate#include <isa/isareg.h>
460Sstevel@tonic-gate#include <isa/isavar.h>
470Sstevel@tonic-gate
480Sstevel@tonic-gatestatic int	atkbdc_isa_probe(device_t dev);
490Sstevel@tonic-gatestatic int	atkbdc_isa_attach(device_t dev);
500Sstevel@tonic-gatestatic device_t	atkbdc_isa_add_child(device_t bus, int order, const char *name,
510Sstevel@tonic-gate		    int unit);
520Sstevel@tonic-gate
530Sstevel@tonic-gatestatic device_method_t atkbdc_isa_methods[] = {
540Sstevel@tonic-gate	DEVMETHOD(device_probe,		atkbdc_isa_probe),
550Sstevel@tonic-gate	DEVMETHOD(device_attach,	atkbdc_isa_attach),
560Sstevel@tonic-gate	DEVMETHOD(device_suspend,	bus_generic_suspend),
570Sstevel@tonic-gate	DEVMETHOD(device_resume,	bus_generic_resume),
580Sstevel@tonic-gate
590Sstevel@tonic-gate	DEVMETHOD(bus_add_child,	atkbdc_isa_add_child),
600Sstevel@tonic-gate	DEVMETHOD(bus_print_child,	atkbdc_print_child),
610Sstevel@tonic-gate	DEVMETHOD(bus_read_ivar,	atkbdc_read_ivar),
620Sstevel@tonic-gate	DEVMETHOD(bus_write_ivar,	atkbdc_write_ivar),
630Sstevel@tonic-gate	DEVMETHOD(bus_get_resource_list,atkbdc_get_resource_list),
640Sstevel@tonic-gate	DEVMETHOD(bus_alloc_resource,	bus_generic_rl_alloc_resource),
650Sstevel@tonic-gate	DEVMETHOD(bus_release_resource,	bus_generic_rl_release_resource),
660Sstevel@tonic-gate	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
670Sstevel@tonic-gate	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
680Sstevel@tonic-gate	DEVMETHOD(bus_get_resource,	bus_generic_rl_get_resource),
690Sstevel@tonic-gate	DEVMETHOD(bus_set_resource,	bus_generic_rl_set_resource),
700Sstevel@tonic-gate	DEVMETHOD(bus_delete_resource,	bus_generic_rl_delete_resource),
710Sstevel@tonic-gate	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
720Sstevel@tonic-gate	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
730Sstevel@tonic-gate
740Sstevel@tonic-gate	{ 0, 0 }
752660Sjacobs};
760Sstevel@tonic-gate
770Sstevel@tonic-gatestatic driver_t atkbdc_isa_driver = {
780Sstevel@tonic-gate	ATKBDC_DRIVER_NAME,
790Sstevel@tonic-gate	atkbdc_isa_methods,
800Sstevel@tonic-gate	sizeof(atkbdc_softc_t *),
810Sstevel@tonic-gate};
820Sstevel@tonic-gate
830Sstevel@tonic-gatestatic struct isa_pnp_id atkbdc_ids[] = {
840Sstevel@tonic-gate	{ 0x0303d041, "Keyboard controller (i8042)" },	/* PNP0303 */
850Sstevel@tonic-gate	{ 0 }
860Sstevel@tonic-gate};
870Sstevel@tonic-gate
880Sstevel@tonic-gatestatic int
890Sstevel@tonic-gateatkbdc_isa_probe(device_t dev)
900Sstevel@tonic-gate{
910Sstevel@tonic-gate	struct resource	*port0;
920Sstevel@tonic-gate	struct resource	*port1;
930Sstevel@tonic-gate	u_long		start;
940Sstevel@tonic-gate	u_long		count;
950Sstevel@tonic-gate	int		error;
960Sstevel@tonic-gate	int		rid;
970Sstevel@tonic-gate#if defined(__i386__)
980Sstevel@tonic-gate	bus_space_tag_t	tag;
990Sstevel@tonic-gate	bus_space_handle_t ioh1;
1000Sstevel@tonic-gate	volatile int	i;
1010Sstevel@tonic-gate	register_t	flags;
1020Sstevel@tonic-gate#endif
1030Sstevel@tonic-gate
1040Sstevel@tonic-gate	/* check PnP IDs */
1050Sstevel@tonic-gate	if (ISA_PNP_PROBE(device_get_parent(dev), dev, atkbdc_ids) == ENXIO)
1060Sstevel@tonic-gate		return ENXIO;
1070Sstevel@tonic-gate
1080Sstevel@tonic-gate	device_set_desc(dev, "Keyboard controller (i8042)");
1090Sstevel@tonic-gate
1100Sstevel@tonic-gate	/*
1110Sstevel@tonic-gate	 * Adjust I/O port resources.
1120Sstevel@tonic-gate	 * The AT keyboard controller uses two ports (a command/data port
1130Sstevel@tonic-gate	 * 0x60 and a status port 0x64), which may be given to us in
1140Sstevel@tonic-gate	 * one resource (0x60 through 0x64) or as two separate resources
1150Sstevel@tonic-gate	 * (0x60 and 0x64). Some brain-damaged ACPI BIOS has reversed
1160Sstevel@tonic-gate	 * command/data port and status port. Furthermore, /boot/device.hints
1170Sstevel@tonic-gate	 * may contain just one port, 0x60. We shall adjust resource settings
1180Sstevel@tonic-gate	 * so that these two ports are available as two separate resources
1190Sstevel@tonic-gate	 * in correct order.
1200Sstevel@tonic-gate	 */
1210Sstevel@tonic-gate	device_quiet(dev);
1220Sstevel@tonic-gate	rid = 0;
1230Sstevel@tonic-gate	if (bus_get_resource(dev, SYS_RES_IOPORT, rid, &start, &count) != 0)
1240Sstevel@tonic-gate		return ENXIO;
1250Sstevel@tonic-gate	if (start == IO_KBD + KBD_STATUS_PORT) {
1260Sstevel@tonic-gate		start = IO_KBD;
1270Sstevel@tonic-gate		count++;
1280Sstevel@tonic-gate	}
1290Sstevel@tonic-gate	if (count > 1)	/* adjust the count and/or start port */
1300Sstevel@tonic-gate		bus_set_resource(dev, SYS_RES_IOPORT, rid, start, 1);
1310Sstevel@tonic-gate	port0 = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE);
1320Sstevel@tonic-gate	if (port0 == NULL)
1330Sstevel@tonic-gate		return ENXIO;
1340Sstevel@tonic-gate	rid = 1;
1350Sstevel@tonic-gate	if (bus_get_resource(dev, SYS_RES_IOPORT, rid, NULL, NULL) != 0)
1360Sstevel@tonic-gate		bus_set_resource(dev, SYS_RES_IOPORT, 1,
1370Sstevel@tonic-gate				 start + KBD_STATUS_PORT, 1);
1380Sstevel@tonic-gate	port1 = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE);
1392660Sjacobs	if (port1 == NULL) {
1400Sstevel@tonic-gate		bus_release_resource(dev, SYS_RES_IOPORT, 0, port0);
1410Sstevel@tonic-gate		return ENXIO;
1420Sstevel@tonic-gate	}
1430Sstevel@tonic-gate
1440Sstevel@tonic-gate#if defined(__i386__)
1450Sstevel@tonic-gate	/*
1460Sstevel@tonic-gate	 * Check if we really have AT keyboard controller. Poll status
1470Sstevel@tonic-gate	 * register until we get "all clear" indication. If no such
1480Sstevel@tonic-gate	 * indication comes, it probably means that there is no AT
1490Sstevel@tonic-gate	 * keyboard controller present. Give up in such case. Check relies
1500Sstevel@tonic-gate	 * on the fact that reading from non-existing in/out port returns
1510Sstevel@tonic-gate	 * 0xff on i386. May or may not be true on other platforms.
1520Sstevel@tonic-gate	 */
1530Sstevel@tonic-gate	tag = rman_get_bustag(port0);
1540Sstevel@tonic-gate	ioh1 = rman_get_bushandle(port1);
1550Sstevel@tonic-gate	flags = intr_disable();
1560Sstevel@tonic-gate	for (i = 0; i != 65535; i++) {
1570Sstevel@tonic-gate		if ((bus_space_read_1(tag, ioh1, 0) & 0x2) == 0)
1580Sstevel@tonic-gate			break;
1590Sstevel@tonic-gate	}
1600Sstevel@tonic-gate	intr_restore(flags);
1610Sstevel@tonic-gate	if (i == 65535) {
1620Sstevel@tonic-gate		bus_release_resource(dev, SYS_RES_IOPORT, 0, port0);
1630Sstevel@tonic-gate		bus_release_resource(dev, SYS_RES_IOPORT, 1, port1);
1640Sstevel@tonic-gate		return ENXIO;
1650Sstevel@tonic-gate	}
1660Sstevel@tonic-gate#endif
1670Sstevel@tonic-gate
1680Sstevel@tonic-gate	device_verbose(dev);
1690Sstevel@tonic-gate
1700Sstevel@tonic-gate	error = atkbdc_probe_unit(device_get_unit(dev), port0, port1);
1710Sstevel@tonic-gate	if (error == 0)
1720Sstevel@tonic-gate		bus_generic_probe(dev);
1730Sstevel@tonic-gate
1740Sstevel@tonic-gate	bus_release_resource(dev, SYS_RES_IOPORT, 0, port0);
1750Sstevel@tonic-gate	bus_release_resource(dev, SYS_RES_IOPORT, 1, port1);
1760Sstevel@tonic-gate
1770Sstevel@tonic-gate	return error;
1780Sstevel@tonic-gate}
1790Sstevel@tonic-gate
1800Sstevel@tonic-gatestatic int
1810Sstevel@tonic-gateatkbdc_isa_attach(device_t dev)
1820Sstevel@tonic-gate{
1830Sstevel@tonic-gate	atkbdc_softc_t	*sc;
1840Sstevel@tonic-gate	int		unit;
1850Sstevel@tonic-gate	int		error;
1860Sstevel@tonic-gate	int		rid;
1870Sstevel@tonic-gate
1880Sstevel@tonic-gate	unit = device_get_unit(dev);
1890Sstevel@tonic-gate	sc = *(atkbdc_softc_t **)device_get_softc(dev);
1900Sstevel@tonic-gate	if (sc == NULL) {
1910Sstevel@tonic-gate		/*
1920Sstevel@tonic-gate		 * We have to maintain two copies of the kbdc_softc struct,
1930Sstevel@tonic-gate		 * as the low-level console needs to have access to the
1940Sstevel@tonic-gate		 * keyboard controller before kbdc is probed and attached.
1950Sstevel@tonic-gate		 * kbdc_soft[] contains the default entry for that purpose.
1960Sstevel@tonic-gate		 * See atkbdc.c. XXX
1970Sstevel@tonic-gate		 */
1980Sstevel@tonic-gate		sc = atkbdc_get_softc(unit);
1990Sstevel@tonic-gate		if (sc == NULL)
2000Sstevel@tonic-gate			return ENOMEM;
2010Sstevel@tonic-gate	}
2020Sstevel@tonic-gate
2030Sstevel@tonic-gate	rid = 0;
2040Sstevel@tonic-gate	sc->port0 = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
205					   RF_ACTIVE);
206	if (sc->port0 == NULL)
207		return ENXIO;
208	rid = 1;
209	sc->port1 = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
210					   RF_ACTIVE);
211	if (sc->port1 == NULL) {
212		bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port0);
213		return ENXIO;
214	}
215
216	error = atkbdc_attach_unit(unit, sc, sc->port0, sc->port1);
217	if (error) {
218		bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port0);
219		bus_release_resource(dev, SYS_RES_IOPORT, 1, sc->port1);
220		return error;
221	}
222	*(atkbdc_softc_t **)device_get_softc(dev) = sc;
223
224	bus_generic_attach(dev);
225
226	return 0;
227}
228
229static device_t
230atkbdc_isa_add_child(device_t bus, int order, const char *name, int unit)
231{
232	atkbdc_device_t	*ivar;
233	device_t	child;
234	int		t;
235
236	ivar = malloc(sizeof(struct atkbdc_device), M_ATKBDDEV,
237		M_NOWAIT | M_ZERO);
238	if (!ivar)
239		return NULL;
240
241	child = device_add_child_ordered(bus, order, name, unit);
242	if (child == NULL) {
243		free(ivar, M_ATKBDDEV);
244		return child;
245	}
246
247	resource_list_init(&ivar->resources);
248	ivar->rid = order;
249
250	/*
251	 * If the device is not created by the PnP BIOS or ACPI,
252	 * refer to device hints for IRQ.
253	 */
254	if (ISA_PNP_PROBE(device_get_parent(bus), bus, atkbdc_ids) != 0) {
255		if (resource_int_value(name, unit, "irq", &t) != 0)
256			t = -1;
257	} else {
258		t = bus_get_resource_start(bus, SYS_RES_IRQ, ivar->rid);
259	}
260	if (t > 0)
261		resource_list_add(&ivar->resources, SYS_RES_IRQ, ivar->rid,
262				  t, t, 1);
263
264	if (resource_disabled(name, unit))
265		device_disable(child);
266
267	device_set_ivars(child, ivar);
268
269	return child;
270}
271
272DRIVER_MODULE(atkbdc, isa, atkbdc_isa_driver, atkbdc_devclass, 0, 0);
273DRIVER_MODULE(atkbdc, acpi, atkbdc_isa_driver, atkbdc_devclass, 0, 0);
274