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