1 /*-
2  * (MPSAFE)
3  *
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  * $DragonFly: src/sys/dev/misc/atkbdc_layer/atkbdc_isa.c,v 1.10 2007/10/23 03:04:49 y0netan1 Exp $
30  */
31 
32 #include "opt_kbd.h"
33 
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/bus.h>
38 #include <sys/malloc.h>
39 #include <sys/rman.h>
40 #include <sys/thread.h>
41 
42 #include <dev/misc/kbd/atkbdcreg.h>
43 
44 #include <bus/isa/isareg.h>
45 #include <bus/isa/isavar.h>
46 
47 #if 0
48 #define lwkt_gettoken(x)
49 #define lwkt_reltoken(x)
50 #endif
51 
52 MALLOC_DEFINE(M_ATKBDDEV, "atkbddev", "AT Keyboard device");
53 
54 /* children */
55 typedef struct atkbdc_device {
56 	int flags;	/* configuration flags */
57 	int irq;	/* ISA IRQ mask */
58 	u_int32_t vendorid;
59 	u_int32_t serial;
60 	u_int32_t logicalid;
61 	u_int32_t compatid;
62 } atkbdc_device_t;
63 
64 /* kbdc */
65 devclass_t atkbdc_devclass;
66 
67 static int	atkbdc_probe(device_t dev);
68 static int	atkbdc_attach(device_t dev);
69 static int	atkbdc_print_child(device_t bus, device_t dev);
70 static int	atkbdc_read_ivar(device_t bus, device_t dev, int index,
71 				 u_long *val);
72 static int	atkbdc_write_ivar(device_t bus, device_t dev, int index,
73 				  u_long val);
74 
75 static device_method_t atkbdc_methods[] = {
76 	DEVMETHOD(device_probe,		atkbdc_probe),
77 	DEVMETHOD(device_attach,	atkbdc_attach),
78 	DEVMETHOD(device_suspend,	bus_generic_suspend),
79 	DEVMETHOD(device_resume,	bus_generic_resume),
80 
81 	DEVMETHOD(bus_print_child,	atkbdc_print_child),
82 	DEVMETHOD(bus_read_ivar,	atkbdc_read_ivar),
83 	DEVMETHOD(bus_write_ivar,	atkbdc_write_ivar),
84 	DEVMETHOD(bus_alloc_resource,	bus_generic_alloc_resource),
85 	DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
86 	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
87 	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
88 	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
89 	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
90 
91 	{ 0, 0 }
92 };
93 
94 static driver_t atkbdc_driver = {
95 	ATKBDC_DRIVER_NAME,
96 	atkbdc_methods,
97 	sizeof(atkbdc_softc_t *),
98 };
99 
100 static struct isa_pnp_id atkbdc_ids[] = {
101 	{ 0x0303d041, "Keyboard controller (i8042)" },	/* PNP0303 */
102 	{ 0 }
103 };
104 
105 static int
106 atkbdc_probe(device_t dev)
107 {
108 	struct resource	*port0;
109 	struct resource	*port1;
110 	int		error;
111 	int		rid;
112 #if defined(__i386__)
113 	bus_space_tag_t	tag;
114 	bus_space_handle_t ioh1;
115 	volatile int	i;
116 #endif
117 
118 	/* check PnP IDs */
119 	if (ISA_PNP_PROBE(device_get_parent(dev), dev, atkbdc_ids) == ENXIO)
120 		return ENXIO;
121 
122 	device_set_desc(dev, "Keyboard controller (i8042)");
123 
124 	rid = 0;
125 	port0 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1,
126 				   RF_ACTIVE);
127 	if (port0 == NULL)
128 		return ENXIO;
129 	/* XXX */
130 	if (bus_get_resource_start(dev, SYS_RES_IOPORT, 1) <= 0) {
131 		bus_set_resource(dev, SYS_RES_IOPORT, 1,
132 				 rman_get_start(port0) + KBD_STATUS_PORT, 1);
133 	}
134 	rid = 1;
135 	port1 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1,
136 				   RF_ACTIVE);
137 	if (port1 == NULL) {
138 		bus_release_resource(dev, SYS_RES_IOPORT, 0, port0);
139 		return ENXIO;
140 	}
141 
142 #if defined(__i386__)
143 	/*
144 	 * Check if we really have AT keyboard controller. Poll status
145 	 * register until we get "all clear" indication. If no such
146 	 * indication comes, it probably means that there is no AT
147 	 * keyboard controller present. Give up in such case. Check relies
148 	 * on the fact that reading from non-existing in/out port returns
149 	 * 0xff on i386. May or may not be true on other platforms.
150 	 */
151 	tag = rman_get_bustag(port0);
152 	ioh1 = rman_get_bushandle(port1);
153 	for (i = 65536; i != 0; --i) {
154 		if ((bus_space_read_1(tag, ioh1, 0) & 0x2) == 0)
155 			break;
156 		DELAY(16);
157 	}
158 	if (i == 0) {
159 		bus_release_resource(dev, SYS_RES_IOPORT, 0, port0);
160 		bus_release_resource(dev, SYS_RES_IOPORT, 1, port1);
161 		return ENXIO;
162 	}
163 #endif
164 
165 	error = atkbdc_probe_unit(device_get_unit(dev), port0, port1);
166 
167 	bus_release_resource(dev, SYS_RES_IOPORT, 0, port0);
168 	bus_release_resource(dev, SYS_RES_IOPORT, 1, port1);
169 
170 	return error;
171 }
172 
173 static void
174 atkbdc_add_device(device_t dev, const char *name, int unit)
175 {
176 	atkbdc_device_t	*kdev;
177 	device_t	child;
178 	int		t;
179 
180 	if (resource_int_value(name, unit, "disabled", &t) == 0 && t != 0)
181 		return;
182 
183 	kdev = kmalloc(sizeof(struct atkbdc_device), M_ATKBDDEV,
184 			M_WAITOK | M_ZERO);
185 
186 	if (resource_int_value(name, unit, "irq", &t) == 0)
187 		kdev->irq = t;
188 	else
189 		kdev->irq = -1;
190 
191 	if (resource_int_value(name, unit, "flags", &t) == 0)
192 		kdev->flags = t;
193 	else
194 		kdev->flags = 0;
195 
196 	child = device_add_child(dev, name, unit);
197 	device_set_ivars(child, kdev);
198 }
199 
200 static int
201 atkbdc_attach(device_t dev)
202 {
203 	atkbdc_softc_t	*sc;
204 	int		unit;
205 	int		error;
206 	int		rid;
207 	int		i;
208 
209 	lwkt_gettoken(&tty_token);
210 	unit = device_get_unit(dev);
211 	sc = *(atkbdc_softc_t **)device_get_softc(dev);
212 	if (sc == NULL) {
213 		/*
214 		 * We have to maintain two copies of the kbdc_softc struct,
215 		 * as the low-level console needs to have access to the
216 		 * keyboard controller before kbdc is probed and attached.
217 		 * kbdc_soft[] contains the default entry for that purpose.
218 		 * See atkbdc.c. XXX
219 		 */
220 		sc = atkbdc_get_softc(unit);
221 		if (sc == NULL) {
222 			lwkt_reltoken(&tty_token);
223 			return ENOMEM;
224 		}
225 	}
226 
227 	rid = 0;
228 	sc->port0 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1,
229 				       RF_ACTIVE);
230 	if (sc->port0 == NULL) {
231 		lwkt_reltoken(&tty_token);
232 		return ENXIO;
233 	}
234 	rid = 1;
235 	sc->port1 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1,
236 				       RF_ACTIVE);
237 	if (sc->port1 == NULL) {
238 		bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port0);
239 		lwkt_reltoken(&tty_token);
240 		return ENXIO;
241 	}
242 
243 	error = atkbdc_attach_unit(unit, sc, sc->port0, sc->port1);
244 	if (error) {
245 		bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port0);
246 		bus_release_resource(dev, SYS_RES_IOPORT, 1, sc->port1);
247 		lwkt_reltoken(&tty_token);
248 		return error;
249 	}
250 	*(atkbdc_softc_t **)device_get_softc(dev) = sc;
251 
252 	/*
253 	 * Add all devices configured to be attached to atkbdc0.
254 	 */
255 	for (i = resource_query_string(-1, "at", device_get_nameunit(dev));
256 	     i != -1;
257 	     i = resource_query_string(i, "at", device_get_nameunit(dev))) {
258 		atkbdc_add_device(dev, resource_query_name(i),
259 				  resource_query_unit(i));
260 	}
261 
262 	/*
263 	 * and atkbdc?
264 	 */
265 	for (i = resource_query_string(-1, "at", device_get_name(dev));
266 	     i != -1;
267 	     i = resource_query_string(i, "at", device_get_name(dev))) {
268 		atkbdc_add_device(dev, resource_query_name(i),
269 				  resource_query_unit(i));
270 	}
271 
272 	bus_generic_attach(dev);
273 
274 	lwkt_reltoken(&tty_token);
275 	return 0;
276 }
277 
278 static int
279 atkbdc_print_child(device_t bus, device_t dev)
280 {
281 	atkbdc_device_t *kbdcdev;
282 	int retval = 0;
283 
284 	kbdcdev = (atkbdc_device_t *)device_get_ivars(dev);
285 
286 	retval += bus_print_child_header(bus, dev);
287 	if (kbdcdev->flags != 0)
288 		retval += kprintf(" flags 0x%x", kbdcdev->flags);
289 	if (kbdcdev->irq != -1)
290 		retval += kprintf(" irq %d", kbdcdev->irq);
291 	retval += bus_print_child_footer(bus, dev);
292 
293 	return (retval);
294 }
295 
296 static int
297 atkbdc_read_ivar(device_t bus, device_t dev, int index, u_long *val)
298 {
299 	atkbdc_device_t *ivar;
300 
301 	lwkt_gettoken(&tty_token);
302 	ivar = (atkbdc_device_t *)device_get_ivars(dev);
303 	switch (index) {
304 	case KBDC_IVAR_IRQ:
305 		*val = (u_long)ivar->irq;
306 		break;
307 	case KBDC_IVAR_FLAGS:
308 		*val = (u_long)ivar->flags;
309 		break;
310 	case KBDC_IVAR_VENDORID:
311 		*val = (u_long)ivar->vendorid;
312 		break;
313 	case KBDC_IVAR_SERIAL:
314 		*val = (u_long)ivar->serial;
315 		break;
316 	case KBDC_IVAR_LOGICALID:
317 		*val = (u_long)ivar->logicalid;
318 		break;
319 	case KBDC_IVAR_COMPATID:
320 		*val = (u_long)ivar->compatid;
321 		break;
322 	default:
323 		lwkt_reltoken(&tty_token);
324 		return ENOENT;
325 	}
326 
327 	lwkt_reltoken(&tty_token);
328 	return 0;
329 }
330 
331 static int
332 atkbdc_write_ivar(device_t bus, device_t dev, int index, u_long val)
333 {
334 	atkbdc_device_t *ivar;
335 
336 	lwkt_gettoken(&tty_token);
337 	ivar = (atkbdc_device_t *)device_get_ivars(dev);
338 	switch (index) {
339 	case KBDC_IVAR_IRQ:
340 		ivar->irq = (int)val;
341 		break;
342 	case KBDC_IVAR_FLAGS:
343 		ivar->flags = (int)val;
344 		break;
345 	case KBDC_IVAR_VENDORID:
346 		ivar->vendorid = (u_int32_t)val;
347 		break;
348 	case KBDC_IVAR_SERIAL:
349 		ivar->serial = (u_int32_t)val;
350 		break;
351 	case KBDC_IVAR_LOGICALID:
352 		ivar->logicalid = (u_int32_t)val;
353 		break;
354 	case KBDC_IVAR_COMPATID:
355 		ivar->compatid = (u_int32_t)val;
356 		break;
357 	default:
358 		lwkt_reltoken(&tty_token);
359 		return ENOENT;
360 	}
361 
362 	lwkt_reltoken(&tty_token);
363 	return 0;
364 }
365 
366 DRIVER_MODULE(atkbdc, isa, atkbdc_driver, atkbdc_devclass, 0, 0);
367 DRIVER_MODULE(atkbdc, acpi, atkbdc_driver, atkbdc_devclass, 0, 0);
368