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