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  */
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>
38 #include <sys/rman.h>
39 #include <sys/thread.h>
40 
41 #include <dev/misc/kbd/atkbdcreg.h>
42 
43 #include <bus/isa/isareg.h>
44 #include <bus/isa/isavar.h>
45 
46 #if 0
47 #define lwkt_gettoken(x)
48 #define lwkt_reltoken(x)
49 #endif
50 
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 	DEVMETHOD_END
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;
111 #if defined(__i386__) || defined(__x86_64__)
112 	bus_space_tag_t	tag;
113 	bus_space_handle_t ioh1;
114 	volatile int	i;
115 #endif
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, -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 
141 #if defined(__i386__) || defined(__x86_64__)
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;
155 		DELAY(16);
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 
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 
182 	kdev = kmalloc(sizeof(struct atkbdc_device), M_ATKBDDEV,
183 			M_WAITOK | M_ZERO);
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 
208 	lwkt_gettoken(&tty_token);
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);
220 		if (sc == NULL) {
221 			lwkt_reltoken(&tty_token);
222 			return ENOMEM;
223 		}
224 	}
225 
226 	rid = 0;
227 	sc->port0 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1,
228 				       RF_ACTIVE);
229 	if (sc->port0 == NULL) {
230 		lwkt_reltoken(&tty_token);
231 		return ENXIO;
232 	}
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);
238 		lwkt_reltoken(&tty_token);
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);
246 		lwkt_reltoken(&tty_token);
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 
273 	lwkt_reltoken(&tty_token);
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)
287 		retval += kprintf(" flags 0x%x", kbdcdev->flags);
288 	if (kbdcdev->irq != -1)
289 		retval += kprintf(" irq %d", kbdcdev->irq);
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 
300 	lwkt_gettoken(&tty_token);
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:
322 		lwkt_reltoken(&tty_token);
323 		return ENOENT;
324 	}
325 
326 	lwkt_reltoken(&tty_token);
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 
335 	lwkt_gettoken(&tty_token);
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:
357 		lwkt_reltoken(&tty_token);
358 		return ENOENT;
359 	}
360 
361 	lwkt_reltoken(&tty_token);
362 	return 0;
363 }
364 
365 DRIVER_MODULE(atkbdc, isa, atkbdc_driver, atkbdc_devclass, NULL, NULL);
366 DRIVER_MODULE(atkbdc, acpi, atkbdc_driver, atkbdc_devclass, NULL, NULL);
367