xref: /freebsd/sys/dev/superio/superio.c (revision 036d2e814bf0f5d88ffb4b24c159320894541757)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2019 Andriy Gapon
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
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 AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD$
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/bus.h>
36 #include <sys/conf.h>
37 #include <sys/kernel.h>
38 #include <sys/lock.h>
39 #include <sys/mutex.h>
40 #include <sys/malloc.h>
41 #include <sys/module.h>
42 #include <sys/proc.h>
43 #include <sys/rman.h>
44 #include <sys/time.h>
45 
46 #include <machine/bus.h>
47 #include <machine/resource.h>
48 #include <machine/stdarg.h>
49 
50 #include <isa/isavar.h>
51 
52 #include <dev/superio/superio.h>
53 
54 #include "isa_if.h"
55 
56 
57 typedef void (*sio_conf_enter_f)(struct resource*, uint16_t);
58 typedef void (*sio_conf_exit_f)(struct resource*, uint16_t);
59 
60 struct sio_conf_methods {
61 	sio_conf_enter_f	enter;
62 	sio_conf_exit_f		exit;
63 	superio_vendor_t	vendor;
64 };
65 
66 struct sio_device {
67 	uint8_t			ldn;
68 	superio_dev_type_t	type;
69 };
70 
71 struct superio_devinfo {
72 	STAILQ_ENTRY(superio_devinfo) link;
73 	struct resource_list	resources;
74 	device_t		dev;
75 	uint8_t			ldn;
76 	superio_dev_type_t	type;
77 	uint16_t		iobase;
78 	uint16_t		iobase2;
79 	uint8_t			irq;
80 	uint8_t			dma;
81 };
82 
83 struct siosc {
84 	struct mtx			conf_lock;
85 	STAILQ_HEAD(, superio_devinfo)	devlist;
86 	struct resource*		io_res;
87 	int				io_rid;
88 	uint16_t			io_port;
89 	const struct sio_conf_methods	*methods;
90 	const struct sio_device		*known_devices;
91 	superio_vendor_t		vendor;
92 	uint16_t			devid;
93 	uint8_t				revid;
94 	uint8_t				current_ldn;
95 	uint8_t				ldn_reg;
96 	uint8_t				enable_reg;
97 };
98 
99 #define NUMPORTS	2
100 
101 static uint8_t
102 sio_read(struct resource* res, uint8_t reg)
103 {
104 	bus_write_1(res, 0, reg);
105 	return (bus_read_1(res, 1));
106 }
107 
108 /* Read a word from two one-byte registers, big endian. */
109 static uint16_t
110 sio_readw(struct resource* res, uint8_t reg)
111 {
112 	uint16_t v;
113 
114 	v = sio_read(res, reg);
115 	v <<= 8;
116 	v |= sio_read(res, reg + 1);
117 	return (v);
118 }
119 
120 static void
121 sio_write(struct resource* res, uint8_t reg, uint8_t val)
122 {
123 	bus_write_1(res, 0, reg);
124 	bus_write_1(res, 1, val);
125 }
126 
127 static void
128 sio_ldn_select(struct siosc *sc, uint8_t ldn)
129 {
130 	mtx_assert(&sc->conf_lock, MA_OWNED);
131 	if (ldn == sc->current_ldn)
132 		return;
133 	sio_write(sc->io_res, sc->ldn_reg, ldn);
134 	sc->current_ldn = ldn;
135 }
136 
137 static uint8_t
138 sio_ldn_read(struct siosc *sc, uint8_t ldn, uint8_t reg)
139 {
140 	mtx_assert(&sc->conf_lock, MA_OWNED);
141 	if (reg >= sc->enable_reg) {
142 		sio_ldn_select(sc, ldn);
143 		KASSERT(sc->current_ldn == ldn, ("sio_ldn_select failed"));
144 	}
145 	return (sio_read(sc->io_res, reg));
146 }
147 
148 static uint16_t
149 sio_ldn_readw(struct siosc *sc, uint8_t ldn, uint8_t reg)
150 {
151 	mtx_assert(&sc->conf_lock, MA_OWNED);
152 	if (reg >= sc->enable_reg) {
153 		sio_ldn_select(sc, ldn);
154 		KASSERT(sc->current_ldn == ldn, ("sio_ldn_select failed"));
155 	}
156 	return (sio_readw(sc->io_res, reg));
157 }
158 
159 static void
160 sio_ldn_write(struct siosc *sc, uint8_t ldn, uint8_t reg, uint8_t val)
161 {
162 	mtx_assert(&sc->conf_lock, MA_OWNED);
163 	if (reg <= sc->ldn_reg) {
164 		printf("ignored attempt to write special register 0x%x\n", reg);
165 		return;
166 	}
167 	sio_ldn_select(sc, ldn);
168 	KASSERT(sc->current_ldn == ldn, ("sio_ldn_select failed"));
169 	sio_write(sc->io_res, reg, val);
170 }
171 
172 static void
173 sio_conf_enter(struct siosc *sc)
174 {
175 	mtx_lock(&sc->conf_lock);
176 	sc->methods->enter(sc->io_res, sc->io_port);
177 }
178 
179 static void
180 sio_conf_exit(struct siosc *sc)
181 {
182 	sc->methods->exit(sc->io_res, sc->io_port);
183 	mtx_unlock(&sc->conf_lock);
184 }
185 
186 static void
187 ite_conf_enter(struct resource* res, uint16_t port)
188 {
189 	bus_write_1(res, 0, 0x87);
190 	bus_write_1(res, 0, 0x01);
191 	bus_write_1(res, 0, 0x55);
192 	bus_write_1(res, 0, port == 0x2e ? 0x55 : 0xaa);
193 }
194 
195 static void
196 ite_conf_exit(struct resource* res, uint16_t port)
197 {
198 	sio_write(res, 0x02, 0x02);
199 }
200 
201 static const struct sio_conf_methods ite_conf_methods = {
202 	.enter = ite_conf_enter,
203 	.exit = ite_conf_exit,
204 	.vendor = SUPERIO_VENDOR_ITE
205 };
206 
207 static void
208 nvt_conf_enter(struct resource* res, uint16_t port)
209 {
210 	bus_write_1(res, 0, 0x87);
211 	bus_write_1(res, 0, 0x87);
212 }
213 
214 static void
215 nvt_conf_exit(struct resource* res, uint16_t port)
216 {
217 	bus_write_1(res, 0, 0xaa);
218 }
219 
220 static const struct sio_conf_methods nvt_conf_methods = {
221 	.enter = nvt_conf_enter,
222 	.exit = nvt_conf_exit,
223 	.vendor = SUPERIO_VENDOR_NUVOTON
224 };
225 
226 static const struct sio_conf_methods * const methods_table[] = {
227 	&ite_conf_methods,
228 	&nvt_conf_methods,
229 	NULL
230 };
231 
232 static const uint16_t ports_table[] = {
233 	0x2e, 0x4e, 0
234 };
235 
236 const struct sio_device ite_devices[] = {
237 	{ .ldn = 4, .type = SUPERIO_DEV_HWM },
238 	{ .ldn = 7, .type = SUPERIO_DEV_WDT },
239 	{ .type = SUPERIO_DEV_NONE },
240 };
241 
242 const struct sio_device nvt_devices[] = {
243 	{ .ldn = 8, .type = SUPERIO_DEV_WDT },
244 	{ .type = SUPERIO_DEV_NONE },
245 };
246 
247 const struct sio_device nct5104_devices[] = {
248 	{ .ldn = 7, .type = SUPERIO_DEV_GPIO },
249 	{ .ldn = 8, .type = SUPERIO_DEV_WDT },
250 	{ .ldn = 15, .type = SUPERIO_DEV_GPIO },
251 	{ .type = SUPERIO_DEV_NONE },
252 };
253 
254 static const struct {
255 	superio_vendor_t	vendor;
256 	uint16_t		devid;
257 	uint16_t		mask;
258 	const char		*descr;
259 	const struct sio_device	*devices;
260 } superio_table[] = {
261 	{
262 		.vendor = SUPERIO_VENDOR_ITE, .devid = 0x8712,
263 		.devices = ite_devices,
264 	},
265 	{
266 		.vendor = SUPERIO_VENDOR_ITE, .devid = 0x8716,
267 		.devices = ite_devices,
268 	},
269 	{
270 		.vendor = SUPERIO_VENDOR_ITE, .devid = 0x8718,
271 		.devices = ite_devices,
272 	},
273 	{
274 		.vendor = SUPERIO_VENDOR_ITE, .devid = 0x8720,
275 		.devices = ite_devices,
276 	},
277 	{
278 		.vendor = SUPERIO_VENDOR_ITE, .devid = 0x8721,
279 		.devices = ite_devices,
280 	},
281 	{
282 		.vendor = SUPERIO_VENDOR_ITE, .devid = 0x8726,
283 		.devices = ite_devices,
284 	},
285 	{
286 		.vendor = SUPERIO_VENDOR_ITE, .devid = 0x8728,
287 		.devices = ite_devices,
288 	},
289 	{
290 		.vendor = SUPERIO_VENDOR_ITE, .devid = 0x8771,
291 		.devices = ite_devices,
292 	},
293 	{
294 		.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0x1061, .mask = 0x00,
295 		.descr	= "Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. A)",
296 		.devices = nct5104_devices,
297 	},
298 	{
299 		.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0x5200, .mask = 0xff,
300 		.descr = "Winbond 83627HF/F/HG/G",
301 		.devices = nvt_devices,
302 	},
303 	{
304 		.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0x5900, .mask = 0xff,
305 		.descr = "Winbond 83627S",
306 		.devices = nvt_devices,
307 	},
308 	{
309 		.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0x6000, .mask = 0xff,
310 		.descr = "Winbond 83697HF",
311 		.devices = nvt_devices,
312 	},
313 	{
314 		.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0x6800, .mask = 0xff,
315 		.descr = "Winbond 83697UG",
316 		.devices = nvt_devices,
317 	},
318 	{
319 		.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0x7000, .mask = 0xff,
320 		.descr = "Winbond 83637HF",
321 		.devices = nvt_devices,
322 	},
323 	{
324 		.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0x8200, .mask = 0xff,
325 		.descr = "Winbond 83627THF",
326 		.devices = nvt_devices,
327 	},
328 	{
329 		.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0x8500, .mask = 0xff,
330 		.descr = "Winbond 83687THF",
331 		.devices = nvt_devices,
332 	},
333 	{
334 		.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0x8800, .mask = 0xff,
335 		.descr = "Winbond 83627EHF",
336 		.devices = nvt_devices,
337 	},
338 	{
339 		.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xa000, .mask = 0xff,
340 		.descr = "Winbond 83627DHG",
341 		.devices = nvt_devices,
342 	},
343 	{
344 		.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xa200, .mask = 0xff,
345 		.descr = "Winbond 83627UHG",
346 		.devices = nvt_devices,
347 	},
348 	{
349 		.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xa500, .mask = 0xff,
350 		.descr = "Winbond 83667HG",
351 		.devices = nvt_devices,
352 	},
353 	{
354 		.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xb000, .mask = 0xff,
355 		.descr = "Winbond 83627DHG-P",
356 		.devices = nvt_devices,
357 	},
358 	{
359 		.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xb300, .mask = 0xff,
360 		.descr = "Winbond 83667HG-B",
361 		.devices = nvt_devices,
362 	},
363 	{
364 		.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xb400, .mask = 0xff,
365 		.descr = "Nuvoton NCT6775",
366 		.devices = nvt_devices,
367 	},
368 	{
369 		.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xc300, .mask = 0xff,
370 		.descr = "Nuvoton NCT6776",
371 		.devices = nvt_devices,
372 	},
373 	{
374 		.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xc400, .mask = 0xff,
375 		.descr = "Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)",
376 		.devices = nct5104_devices,
377 	},
378 	{
379 		.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xc500, .mask = 0xff,
380 		.descr = "Nuvoton NCT6779",
381 		.devices = nvt_devices,
382 	},
383 	{
384 		.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xc800, .mask = 0xff,
385 		.descr = "Nuvoton NCT6791",
386 		.devices = nvt_devices,
387 	},
388 	{
389 		.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xc900, .mask = 0xff,
390 		.descr = "Nuvoton NCT6792",
391 		.devices = nvt_devices,
392 	},
393 	{
394 		.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xd100, .mask = 0xff,
395 		.descr = "Nuvoton NCT6793",
396 		.devices = nvt_devices,
397 	},
398 	{
399 		.vendor = SUPERIO_VENDOR_NUVOTON, .devid = 0xd300, .mask = 0xff,
400 		.descr = "Nuvoton NCT6795",
401 		.devices = nvt_devices,
402 	},
403 	{ 0, 0 }
404 };
405 
406 static const char *
407 devtype_to_str(superio_dev_type_t type)
408 {
409 	switch (type) {
410 	case SUPERIO_DEV_NONE:
411 		return ("none");
412 	case SUPERIO_DEV_HWM:
413 		return ("HWM");
414 	case SUPERIO_DEV_WDT:
415 		return ("WDT");
416 	case SUPERIO_DEV_GPIO:
417 		return ("GPIO");
418 	case SUPERIO_DEV_MAX:
419 		return ("invalid");
420 	}
421 	return ("invalid");
422 }
423 
424 static int
425 superio_detect(device_t dev, bool claim, struct siosc *sc)
426 {
427 	struct resource *res;
428 	rman_res_t port;
429 	rman_res_t count;
430 	uint16_t devid;
431 	uint8_t revid;
432 	int error;
433 	int rid;
434 	int i, m;
435 
436 	error = bus_get_resource(dev, SYS_RES_IOPORT, 0, &port, &count);
437 	if (error != 0)
438 		return (error);
439 	if (port > UINT16_MAX || count < NUMPORTS) {
440 		device_printf(dev, "unexpected I/O range size\n");
441 		return (ENXIO);
442 	}
443 
444 	/*
445 	 * Make a temporary resource reservation for hardware probing.
446 	 * If we can't get the resources we need then
447 	 * we need to abort.  Possibly this indicates
448 	 * the resources were used by another device
449 	 * in which case the probe would have failed anyhow.
450 	 */
451 	rid = 0;
452 	res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE);
453 	if (res == NULL) {
454 		if (claim)
455 			device_printf(dev, "failed to allocate I/O resource\n");
456 		return (ENXIO);
457 	}
458 
459 	for (m = 0; methods_table[m] != NULL; m++) {
460 		methods_table[m]->enter(res, port);
461 		if (methods_table[m]->vendor == SUPERIO_VENDOR_ITE) {
462 			devid = sio_readw(res, 0x20);
463 			revid = sio_read(res, 0x22);
464 		} else if (methods_table[m]->vendor == SUPERIO_VENDOR_NUVOTON) {
465 			devid = sio_read(res, 0x20);
466 			revid = sio_read(res, 0x21);
467 			devid = (devid << 8) | revid;
468 		} else {
469 			continue;
470 		}
471 		methods_table[m]->exit(res, port);
472 		for (i = 0; superio_table[i].vendor != 0; i++) {
473 			uint16_t mask;
474 
475 			mask = superio_table[i].mask;
476 			if (superio_table[i].vendor !=
477 			    methods_table[m]->vendor)
478 				continue;
479 			if ((superio_table[i].devid & ~mask) != (devid & ~mask))
480 				continue;
481 			break;
482 		}
483 
484 		/* Found a matching SuperIO entry. */
485 		if (superio_table[i].vendor != 0)
486 			break;
487 	}
488 
489 	if (methods_table[m] == NULL)
490 		error = ENXIO;
491 	else
492 		error = 0;
493 	if (!claim || error != 0) {
494 		bus_release_resource(dev, SYS_RES_IOPORT, rid, res);
495 		return (error);
496 	}
497 
498 	sc->methods = methods_table[m];
499 	sc->vendor = sc->methods->vendor;
500 	sc->known_devices = superio_table[i].devices;
501 	sc->io_res = res;
502 	sc->io_rid = rid;
503 	sc->io_port = port;
504 	sc->devid = devid;
505 	sc->revid = revid;
506 
507 	KASSERT(sc->vendor == SUPERIO_VENDOR_ITE ||
508 	    sc->vendor == SUPERIO_VENDOR_NUVOTON,
509 	    ("Only ITE and Nuvoton SuperIO-s are supported"));
510 	sc->ldn_reg = 0x07;
511 	sc->enable_reg = 0x30;
512 	sc->current_ldn = 0xff;	/* no device should have this */
513 
514 	if (superio_table[i].descr != NULL) {
515 		device_set_desc(dev, superio_table[i].descr);
516 	} else if (sc->vendor == SUPERIO_VENDOR_ITE) {
517 		char descr[64];
518 
519 		snprintf(descr, sizeof(descr),
520 		    "ITE IT%4x SuperIO (revision 0x%02x)",
521 		    sc->devid, sc->revid);
522 		device_set_desc_copy(dev, descr);
523 	}
524 	return (0);
525 }
526 
527 static void
528 superio_identify(driver_t *driver, device_t parent)
529 {
530 	device_t	child;
531 	int i;
532 
533 	/*
534 	 * Don't create child devices if any already exist.
535 	 * Those could be created via isa hints or if this
536 	 * driver is loaded, unloaded and then loaded again.
537 	 */
538 	if (device_find_child(parent, "superio", -1)) {
539 		if (bootverbose)
540 			printf("superio: device(s) already created\n");
541 		return;
542 	}
543 
544 	/*
545 	 * Create a child for each candidate port.
546 	 * It would be nice if we could somehow clean up those
547 	 * that this driver fails to probe.
548 	 */
549 	for (i = 0; ports_table[i] != 0; i++) {
550 		child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE,
551 		    "superio", -1);
552 		if (child == NULL) {
553 			device_printf(parent, "failed to add superio child\n");
554 			continue;
555 		}
556 		bus_set_resource(child, SYS_RES_IOPORT,	0, ports_table[i], 2);
557 		if (superio_detect(child, false, NULL) != 0)
558 			device_delete_child(parent, child);
559 	}
560 }
561 
562 static int
563 superio_probe(device_t dev)
564 {
565 	struct siosc *sc;
566 	int error;
567 
568 	/* Make sure we do not claim some ISA PNP device. */
569 	if (isa_get_logicalid(dev) != 0)
570 		return (ENXIO);
571 
572 	/*
573 	 * XXX We can populate the softc now only because we return
574 	 * BUS_PROBE_SPECIFIC
575 	 */
576 	sc = device_get_softc(dev);
577 	error = superio_detect(dev, true, sc);
578 	if (error != 0)
579 		return (error);
580 	return (BUS_PROBE_SPECIFIC);
581 }
582 
583 static void
584 superio_add_known_child(device_t dev, superio_dev_type_t type, uint8_t ldn)
585 {
586 	struct siosc *sc = device_get_softc(dev);
587 	struct superio_devinfo *dinfo;
588 	device_t child;
589 
590 	child = BUS_ADD_CHILD(dev, 0, NULL, -1);
591 	if (child == NULL) {
592 		device_printf(dev, "failed to add child for ldn %d, type %s\n",
593 		    ldn, devtype_to_str(type));
594 		return;
595 	}
596 	dinfo = device_get_ivars(child);
597 	dinfo->ldn = ldn;
598 	dinfo->type = type;
599 	sio_conf_enter(sc);
600 	dinfo->iobase = sio_ldn_readw(sc, ldn, 0x60);
601 	dinfo->iobase2 = sio_ldn_readw(sc, ldn, 0x62);
602 	dinfo->irq = sio_ldn_readw(sc, ldn, 0x70);
603 	dinfo->dma = sio_ldn_readw(sc, ldn, 0x74);
604 	sio_conf_exit(sc);
605 	STAILQ_INSERT_TAIL(&sc->devlist, dinfo, link);
606 }
607 
608 static int
609 superio_attach(device_t dev)
610 {
611 	struct siosc *sc = device_get_softc(dev);
612 	int i;
613 
614 	mtx_init(&sc->conf_lock, device_get_nameunit(dev), "superio", MTX_DEF);
615 	STAILQ_INIT(&sc->devlist);
616 
617 	for (i = 0; sc->known_devices[i].type != SUPERIO_DEV_NONE; i++) {
618 		superio_add_known_child(dev, sc->known_devices[i].type,
619 		    sc->known_devices[i].ldn);
620 	}
621 
622 	bus_generic_probe(dev);
623 	bus_generic_attach(dev);
624 	return (0);
625 }
626 
627 static int
628 superio_detach(device_t dev)
629 {
630 	struct siosc *sc = device_get_softc(dev);
631 	int error;
632 
633 	error = bus_generic_detach(dev);
634 	if (error != 0)
635 		return (error);
636 	device_delete_children(dev);
637 	bus_release_resource(dev, SYS_RES_IOPORT, sc->io_rid, sc->io_res);
638 	mtx_destroy(&sc->conf_lock);
639 	return (0);
640 }
641 
642 static device_t
643 superio_add_child(device_t dev, u_int order, const char *name, int unit)
644 {
645 	struct superio_devinfo *dinfo;
646 	device_t child;
647 
648 	child = device_add_child_ordered(dev, order, name, unit);
649 	if (child == NULL)
650 		return (NULL);
651 	dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_NOWAIT | M_ZERO);
652 	if (dinfo == NULL) {
653 		device_delete_child(dev, child);
654 		return (NULL);
655 	}
656 	dinfo->ldn = 0xff;
657 	dinfo->type = SUPERIO_DEV_NONE;
658 	dinfo->dev = child;
659 	resource_list_init(&dinfo->resources);
660 	device_set_ivars(child, dinfo);
661 	return (child);
662 }
663 
664 static int
665 superio_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
666 {
667 	struct superio_devinfo *dinfo;
668 
669 	dinfo = device_get_ivars(child);
670 	switch (which) {
671 	case SUPERIO_IVAR_LDN:
672 		*result = dinfo->ldn;
673 		break;
674 	case SUPERIO_IVAR_TYPE:
675 		*result = dinfo->type;
676 		break;
677 	case SUPERIO_IVAR_IOBASE:
678 		*result = dinfo->iobase;
679 		break;
680 	case SUPERIO_IVAR_IOBASE2:
681 		*result = dinfo->iobase2;
682 		break;
683 	case SUPERIO_IVAR_IRQ:
684 		*result = dinfo->irq;
685 		break;
686 	case SUPERIO_IVAR_DMA:
687 		*result = dinfo->dma;
688 		break;
689 	default:
690 		return (ENOENT);
691 	}
692 	return (0);
693 }
694 
695 static int
696 superio_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
697 {
698 
699 	switch (which) {
700 	case SUPERIO_IVAR_LDN:
701 	case SUPERIO_IVAR_TYPE:
702 	case SUPERIO_IVAR_IOBASE:
703 	case SUPERIO_IVAR_IOBASE2:
704 	case SUPERIO_IVAR_IRQ:
705 	case SUPERIO_IVAR_DMA:
706 		return (EINVAL);
707 	default:
708 		return (ENOENT);
709 	}
710 }
711 
712 static struct resource_list *
713 superio_get_resource_list(device_t dev, device_t child)
714 {
715 	struct superio_devinfo *dinfo = device_get_ivars(child);
716 
717 	return (&dinfo->resources);
718 }
719 
720 static int
721 superio_printf(struct superio_devinfo *dinfo, const char *fmt, ...)
722 {
723 	va_list ap;
724 	int retval;
725 
726 	retval = printf("superio:%s@ldn%0x2x: ",
727 	    devtype_to_str(dinfo->type), dinfo->ldn);
728 	va_start(ap, fmt);
729 	retval += vprintf(fmt, ap);
730 	va_end(ap);
731 	return (retval);
732 }
733 
734 static void
735 superio_child_detached(device_t dev, device_t child)
736 {
737 	struct superio_devinfo *dinfo;
738 	struct resource_list *rl;
739 
740 	dinfo = device_get_ivars(child);
741 	rl = &dinfo->resources;
742 
743 	if (resource_list_release_active(rl, dev, child, SYS_RES_IRQ) != 0)
744 		superio_printf(dinfo, "Device leaked IRQ resources\n");
745 	if (resource_list_release_active(rl, dev, child, SYS_RES_MEMORY) != 0)
746 		superio_printf(dinfo, "Device leaked memory resources\n");
747 	if (resource_list_release_active(rl, dev, child, SYS_RES_IOPORT) != 0)
748 		superio_printf(dinfo, "Device leaked I/O resources\n");
749 }
750 
751 static int
752 superio_child_location_str(device_t parent, device_t child, char *buf,
753     size_t buflen)
754 {
755 	uint8_t ldn;
756 
757 	ldn = superio_get_ldn(child);
758 	snprintf(buf, buflen, "ldn=0x%02x", ldn);
759 	return (0);
760 }
761 
762 static int
763 superio_child_pnp_str(device_t parent, device_t child, char *buf,
764     size_t buflen)
765 {
766 	superio_dev_type_t type;
767 
768 	type = superio_get_type(child);
769 	snprintf(buf, buflen, "type=%s", devtype_to_str(type));
770 	return (0);
771 }
772 
773 static int
774 superio_print_child(device_t parent, device_t child)
775 {
776 	superio_dev_type_t type;
777 	uint8_t ldn;
778 	int retval;
779 
780 	ldn = superio_get_ldn(child);
781 	type = superio_get_type(child);
782 
783 	retval = bus_print_child_header(parent, child);
784 	retval += printf(" at %s ldn 0x%02x", devtype_to_str(type), ldn);
785 	retval += bus_print_child_footer(parent, child);
786 
787 	return (retval);
788 }
789 
790 superio_vendor_t
791 superio_vendor(device_t dev)
792 {
793 	device_t sio_dev = device_get_parent(dev);
794 	struct siosc *sc = device_get_softc(sio_dev);
795 
796 	return (sc->vendor);
797 }
798 
799 uint16_t
800 superio_devid(device_t dev)
801 {
802 	device_t sio_dev = device_get_parent(dev);
803 	struct siosc *sc = device_get_softc(sio_dev);
804 
805 	return (sc->devid);
806 }
807 
808 uint8_t
809 superio_revid(device_t dev)
810 {
811 	device_t sio_dev = device_get_parent(dev);
812 	struct siosc *sc = device_get_softc(sio_dev);
813 
814 	return (sc->revid);
815 }
816 
817 uint8_t
818 superio_read(device_t dev, uint8_t reg)
819 {
820 	device_t sio_dev = device_get_parent(dev);
821 	struct siosc *sc = device_get_softc(sio_dev);
822 	struct superio_devinfo *dinfo = device_get_ivars(dev);
823 	uint8_t v;
824 
825 	sio_conf_enter(sc);
826 	v = sio_ldn_read(sc, dinfo->ldn, reg);
827 	sio_conf_exit(sc);
828 	return (v);
829 }
830 
831 void
832 superio_write(device_t dev, uint8_t reg, uint8_t val)
833 {
834 	device_t sio_dev = device_get_parent(dev);
835 	struct siosc *sc = device_get_softc(sio_dev);
836 	struct superio_devinfo *dinfo = device_get_ivars(dev);
837 
838 	sio_conf_enter(sc);
839 	sio_ldn_write(sc, dinfo->ldn, reg, val);
840 	sio_conf_exit(sc);
841 }
842 
843 bool
844 superio_dev_enabled(device_t dev, uint8_t mask)
845 {
846 	device_t sio_dev = device_get_parent(dev);
847 	struct siosc *sc = device_get_softc(sio_dev);
848 	struct superio_devinfo *dinfo = device_get_ivars(dev);
849 	uint8_t v;
850 
851 	/* GPIO device is always active in ITE chips. */
852 	if (sc->vendor == SUPERIO_VENDOR_ITE && dinfo->ldn == 7)
853 		return (true);
854 
855 	v = superio_read(dev, sc->enable_reg);
856 	return ((v & mask) != 0);
857 }
858 
859 void
860 superio_dev_enable(device_t dev, uint8_t mask)
861 {
862 	device_t sio_dev = device_get_parent(dev);
863 	struct siosc *sc = device_get_softc(sio_dev);
864 	struct superio_devinfo *dinfo = device_get_ivars(dev);
865 	uint8_t v;
866 
867 	/* GPIO device is always active in ITE chips. */
868 	if (sc->vendor == SUPERIO_VENDOR_ITE && dinfo->ldn == 7)
869 		return;
870 
871 	sio_conf_enter(sc);
872 	v = sio_ldn_read(sc, dinfo->ldn, sc->enable_reg);
873 	v |= mask;
874 	sio_ldn_write(sc, dinfo->ldn, sc->enable_reg, v);
875 	sio_conf_exit(sc);
876 }
877 
878 void
879 superio_dev_disable(device_t dev, uint8_t mask)
880 {
881 	device_t sio_dev = device_get_parent(dev);
882 	struct siosc *sc = device_get_softc(sio_dev);
883 	struct superio_devinfo *dinfo = device_get_ivars(dev);
884 	uint8_t v;
885 
886 	/* GPIO device is always active in ITE chips. */
887 	if (sc->vendor == SUPERIO_VENDOR_ITE && dinfo->ldn == 7)
888 		return;
889 
890 	sio_conf_enter(sc);
891 	v = sio_ldn_read(sc, dinfo->ldn, sc->enable_reg);
892 	v &= ~mask;
893 	sio_ldn_write(sc, dinfo->ldn, sc->enable_reg, v);
894 	sio_conf_exit(sc);
895 }
896 
897 device_t
898 superio_find_dev(device_t superio, superio_dev_type_t type, int ldn)
899 {
900 	struct siosc *sc = device_get_softc(superio);
901 	struct superio_devinfo *dinfo;
902 
903 	if (ldn < -1 || ldn > UINT8_MAX)
904 		return (NULL);		/* ERANGE */
905 	if (type == SUPERIO_DEV_NONE && ldn == -1)
906 		return (NULL);		/* EINVAL */
907 
908 	STAILQ_FOREACH(dinfo, &sc->devlist, link) {
909 		if (ldn != -1 && dinfo->ldn != ldn)
910 			continue;
911 		if (type != SUPERIO_DEV_NONE && dinfo->type != type)
912 			continue;
913 		return (dinfo->dev);
914 	}
915 	return (NULL);
916 }
917 
918 static devclass_t superio_devclass;
919 
920 static device_method_t superio_methods[] = {
921 	DEVMETHOD(device_identify,	superio_identify),
922 	DEVMETHOD(device_probe,		superio_probe),
923 	DEVMETHOD(device_attach,	superio_attach),
924 	DEVMETHOD(device_detach,	superio_detach),
925 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
926 	DEVMETHOD(device_suspend,	bus_generic_suspend),
927 	DEVMETHOD(device_resume,	bus_generic_resume),
928 
929 	DEVMETHOD(bus_add_child,	superio_add_child),
930 	DEVMETHOD(bus_child_detached,	superio_child_detached),
931 	DEVMETHOD(bus_child_location_str, superio_child_location_str),
932 	DEVMETHOD(bus_child_pnpinfo_str, superio_child_pnp_str),
933 	DEVMETHOD(bus_print_child,	superio_print_child),
934 	DEVMETHOD(bus_read_ivar,	superio_read_ivar),
935 	DEVMETHOD(bus_write_ivar,	superio_write_ivar),
936 	DEVMETHOD(bus_get_resource_list, superio_get_resource_list),
937 	DEVMETHOD(bus_alloc_resource,	bus_generic_rl_alloc_resource),
938 	DEVMETHOD(bus_release_resource,	bus_generic_rl_release_resource),
939 	DEVMETHOD(bus_set_resource,	bus_generic_rl_set_resource),
940 	DEVMETHOD(bus_get_resource,	bus_generic_rl_get_resource),
941 	DEVMETHOD(bus_delete_resource,	bus_generic_rl_delete_resource),
942 	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
943 	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
944 	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
945 	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
946 
947 	DEVMETHOD_END
948 };
949 
950 static driver_t superio_driver = {
951 	"superio",
952 	superio_methods,
953 	sizeof(struct siosc)
954 };
955 
956 DRIVER_MODULE(superio, isa, superio_driver, superio_devclass, 0, 0);
957 MODULE_VERSION(superio, 1);
958