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