2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2016-2022 Stormshield
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
28 #include <sys/param.h>
29 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/module.h>
34 #include <sys/sysctl.h>
35 #include <sys/watchdog.h>
37 #include <dev/superio/superio.h>
39 #include <machine/bus.h>
40 #include <machine/resource.h>
42 #define NCTHWM_FAN_MAX 5
44 #define NCTHWM_BANK_SELECT 0x4e
45 #define NCTHWM_VENDOR_ID 0x4f
47 #define NCTHWM_VERBOSE_PRINTF(dev, ...) \
49 if (__predict_false(bootverbose)) \
50 device_printf(dev, __VA_ARGS__); \
55 struct ncthwm_device *nctdevp;
56 struct resource *iores;
60 struct ncthwm_fan_info
63 uint8_t low_byte_offset;
64 uint8_t high_byte_offset;
67 struct ncthwm_device {
73 struct ncthwm_fan_info fan_info[NCTHWM_FAN_MAX];
74 } ncthwm_devices[] = {
77 .descr = "HWM on Nuvoton NCT6779D",
82 { .name = "SYSFAN", .low_byte_offset = 0xc1, .high_byte_offset = 0xc0 },
83 { .name = "CPUFAN", .low_byte_offset = 0xc3, .high_byte_offset = 0xc2 },
84 { .name = "AUXFAN0", .low_byte_offset = 0xc5, .high_byte_offset = 0xc4 },
85 { .name = "AUXFAN1", .low_byte_offset = 0xc7, .high_byte_offset = 0xc6 },
86 { .name = "AUXFAN2", .low_byte_offset = 0xc9, .high_byte_offset = 0xc8 },
90 .descr = "HWM on Nuvoton NCT6796D-E",
95 { .name = "SYSFAN", .low_byte_offset = 0xc1, .high_byte_offset = 0xc0 },
96 { .name = "CPUFAN", .low_byte_offset = 0xc3, .high_byte_offset = 0xc2 },
97 { .name = "AUXFAN0", .low_byte_offset = 0xc5, .high_byte_offset = 0xc4 },
98 { .name = "AUXFAN1", .low_byte_offset = 0xc7, .high_byte_offset = 0xc6 },
99 { .name = "AUXFAN2", .low_byte_offset = 0xc9, .high_byte_offset = 0xc8 },
104 static struct ncthwm_device *
105 ncthwm_lookup_device(device_t dev)
110 devid = superio_devid(dev);
111 for (i = 0; i < nitems(ncthwm_devices); i++) {
112 if (devid == ncthwm_devices[i].devid)
113 return (ncthwm_devices + i);
119 ncthwm_write(struct ncthwm_softc *sc, uint8_t reg, uint8_t val)
121 bus_write_1(sc->iores, 0, reg);
122 bus_write_1(sc->iores, 1, val);
126 ncthwm_read(struct ncthwm_softc *sc, uint8_t reg)
128 bus_write_1(sc->iores, 0, reg);
129 return (bus_read_1(sc->iores, 1));
133 ncthwm_query_fan_speed(SYSCTL_HANDLER_ARGS)
135 struct ncthwm_softc *sc;
136 struct ncthwm_fan_info *fan;
143 KASSERT(sc->nctdevp != NULL, ("Unreachable"));
145 if (sc->nctdevp->fan_count <= arg2)
147 fan = &sc->nctdevp->fan_info[arg2];
149 KASSERT(sc->iores != NULL, ("Unreachable"));
151 ncthwm_write(sc, NCTHWM_BANK_SELECT, sc->nctdevp->fan_bank);
152 val = ncthwm_read(sc, fan->high_byte_offset) << 8;
153 val |= ncthwm_read(sc, fan->low_byte_offset);
155 NCTHWM_VERBOSE_PRINTF(sc->dev, "%s: read %u from bank %u offset 0x%x-0x%x\n",
156 fan->name, val, sc->nctdevp->fan_bank, fan->high_byte_offset, fan->low_byte_offset);
158 return (sysctl_handle_16(oidp, &val, 0, req));
162 ncthwm_probe(device_t dev)
164 struct ncthwm_device *nctdevp;
167 ldn = superio_get_ldn(dev);
169 if (superio_vendor(dev) != SUPERIO_VENDOR_NUVOTON) {
170 NCTHWM_VERBOSE_PRINTF(dev, "ldn 0x%x not a Nuvoton device\n", ldn);
173 if (superio_get_type(dev) != SUPERIO_DEV_HWM) {
174 NCTHWM_VERBOSE_PRINTF(dev, "ldn 0x%x not a HWM device\n", ldn);
178 nctdevp = ncthwm_lookup_device(dev);
179 if (nctdevp == NULL) {
180 NCTHWM_VERBOSE_PRINTF(dev, "ldn 0x%x not supported\n", ldn);
183 device_set_desc(dev, nctdevp->descr);
184 return (BUS_PROBE_DEFAULT);
188 ncthwm_attach(device_t dev)
190 struct ncthwm_softc *sc;
194 sc = device_get_softc(dev);
197 sc->nctdevp = ncthwm_lookup_device(dev);
198 if (sc->nctdevp == NULL) {
199 device_printf(dev, "device not supported\n");
203 iobase = superio_get_iobase(dev) + sc->nctdevp->base_offset;
205 if (bus_set_resource(dev, SYS_RES_IOPORT, sc->iorid, iobase, 2) != 0) {
206 device_printf(dev, "failed to set I/O port resource at 0x%x\n", iobase);
209 sc->iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
210 &sc->iorid, RF_ACTIVE);
211 if (sc->iores == NULL) {
212 device_printf(dev, "can't map I/O space at 0x%x\n", iobase);
215 NCTHWM_VERBOSE_PRINTF(dev, "iobase 0x%x iores %p\n", iobase, sc->iores);
217 /* Register FAN sysctl */
218 for (i = 0; i < sc->nctdevp->fan_count; i++) {
219 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
220 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
221 sc->nctdevp->fan_info[i].name,
222 CTLTYPE_U16 | CTLFLAG_RD, sc, i,
223 ncthwm_query_fan_speed, "SU", "Fan speed in RPM");
230 ncthwm_detach(device_t dev)
232 struct ncthwm_softc *sc = device_get_softc(dev);
235 bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->iores);
240 static device_method_t ncthwm_methods[] = {
241 /* Methods from the device interface */
242 DEVMETHOD(device_probe, ncthwm_probe),
243 DEVMETHOD(device_attach, ncthwm_attach),
244 DEVMETHOD(device_detach, ncthwm_detach),
246 /* Terminate method list */
250 static driver_t ncthwm_driver = {
253 sizeof (struct ncthwm_softc)
256 DRIVER_MODULE(ncthwm, superio, ncthwm_driver, NULL, NULL);
257 MODULE_DEPEND(ncthwm, superio, 1, 1, 1);
258 MODULE_VERSION(ncthwm, 1);