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