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