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