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