]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/ncthwm/ncthwm.c
acpica: Create merge commit against vendor branch
[FreeBSD/FreeBSD.git] / sys / dev / ncthwm / ncthwm.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2016-2022 Stormshield
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/param.h>
29 #include <sys/systm.h>
30 #include <sys/bus.h>
31 #include <sys/kernel.h>
32 #include <sys/module.h>
33 #include <sys/rman.h>
34 #include <sys/sysctl.h>
35 #include <sys/watchdog.h>
36
37 #include <dev/superio/superio.h>
38
39 #include <machine/bus.h>
40 #include <machine/resource.h>
41
42 #define NCTHWM_FAN_MAX                 5
43
44 #define NCTHWM_BANK_SELECT 0x4e
45 #define NCTHWM_VENDOR_ID   0x4f
46
47 #define NCTHWM_VERBOSE_PRINTF(dev, ...)         \
48         do {                                        \
49                 if (__predict_false(bootverbose))       \
50                         device_printf(dev, __VA_ARGS__);    \
51         } while (0)
52
53 struct ncthwm_softc {
54         device_t              dev;
55         struct ncthwm_device *nctdevp;
56         struct resource      *iores;
57         int                   iorid;
58 };
59
60 struct ncthwm_fan_info
61 {
62         const char *name;
63         uint8_t     low_byte_offset;
64         uint8_t     high_byte_offset;
65 };
66
67 struct ncthwm_device {
68         uint16_t                 devid;
69         const char              *descr;
70         uint8_t                  base_offset;
71         uint8_t                  fan_bank;
72         uint8_t                  fan_count;
73         struct ncthwm_fan_info   fan_info[NCTHWM_FAN_MAX];
74 } ncthwm_devices[] = {
75         {
76                 .devid       = 0xc562,
77                 .descr       = "HWM on Nuvoton NCT6779D",
78                 .base_offset = 5,
79                 .fan_bank    = 4,
80                 .fan_count   = 5,
81                 .fan_info = {
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 },
87                 },
88         }, {
89                 .devid       = 0xd42a,
90                 .descr       = "HWM on Nuvoton NCT6796D-E",
91                 .base_offset = 5,
92                 .fan_bank    = 4,
93                 .fan_count   = 5,
94                 .fan_info = {
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 },
100                 },
101         }
102 };
103
104 static struct ncthwm_device *
105 ncthwm_lookup_device(device_t dev)
106 {
107         int      i;
108         uint16_t devid;
109
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);
114         }
115         return (NULL);
116 }
117
118 static void
119 ncthwm_write(struct ncthwm_softc *sc, uint8_t reg, uint8_t val)
120 {
121         bus_write_1(sc->iores, 0, reg);
122         bus_write_1(sc->iores, 1, val);
123 }
124
125 static uint8_t
126 ncthwm_read(struct ncthwm_softc *sc, uint8_t reg)
127 {
128         bus_write_1(sc->iores, 0, reg);
129         return (bus_read_1(sc->iores, 1));
130 }
131
132 static int
133 ncthwm_query_fan_speed(SYSCTL_HANDLER_ARGS)
134 {
135         struct ncthwm_softc    *sc;
136         struct ncthwm_fan_info *fan;
137         uint16_t                val;
138
139         sc = arg1;
140         if (sc == NULL)
141                 return (EINVAL);
142
143         KASSERT(sc->nctdevp != NULL, ("Unreachable"));
144
145         if (sc->nctdevp->fan_count <= arg2)
146                 return (EINVAL);
147         fan = &sc->nctdevp->fan_info[arg2];
148
149         KASSERT(sc->iores != NULL, ("Unreachable"));
150
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);
154
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);
157
158         return (sysctl_handle_16(oidp, &val, 0, req));
159 }
160
161 static int
162 ncthwm_probe(device_t dev)
163 {
164         struct ncthwm_device *nctdevp;
165         uint8_t               ldn;
166
167         ldn = superio_get_ldn(dev);
168
169         if (superio_vendor(dev) != SUPERIO_VENDOR_NUVOTON) {
170                 NCTHWM_VERBOSE_PRINTF(dev, "ldn 0x%x not a Nuvoton device\n", ldn);
171                 return (ENXIO);
172         }
173         if (superio_get_type(dev) != SUPERIO_DEV_HWM) {
174                 NCTHWM_VERBOSE_PRINTF(dev, "ldn 0x%x not a HWM device\n", ldn);
175                 return (ENXIO);
176         }
177
178         nctdevp = ncthwm_lookup_device(dev);
179         if (nctdevp == NULL) {
180                 NCTHWM_VERBOSE_PRINTF(dev, "ldn 0x%x not supported\n", ldn);
181                 return (ENXIO);
182         }
183         device_set_desc(dev, nctdevp->descr);
184         return (BUS_PROBE_DEFAULT);
185 }
186
187 static int
188 ncthwm_attach(device_t dev)
189 {
190         struct ncthwm_softc *sc;
191         int                  i;
192         uint16_t             iobase;
193
194         sc      = device_get_softc(dev);
195         sc->dev = dev;
196
197         sc->nctdevp = ncthwm_lookup_device(dev);
198         if (sc->nctdevp == NULL) {
199                 device_printf(dev, "device not supported\n");
200                 return (ENXIO);
201         }
202
203         iobase    = superio_get_iobase(dev) + sc->nctdevp->base_offset;
204         sc->iorid = 0;
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);
207                 return (ENXIO);
208         }
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);
213                 return (ENXIO);
214         }
215         NCTHWM_VERBOSE_PRINTF(dev, "iobase 0x%x iores %p\n", iobase, sc->iores);
216
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");
224         }
225
226         return (0);
227 }
228
229 static int
230 ncthwm_detach(device_t dev)
231 {
232         struct ncthwm_softc *sc = device_get_softc(dev);
233
234         if (sc->iores)
235                 bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->iores);
236
237         return (0);
238 }
239
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),
245
246         /* Terminate method list */
247         { 0, 0 }
248 };
249
250 static driver_t ncthwm_driver = {
251         "ncthwm",
252         ncthwm_methods,
253         sizeof (struct ncthwm_softc)
254 };
255
256 DRIVER_MODULE(ncthwm, superio, ncthwm_driver, NULL, NULL);
257 MODULE_DEPEND(ncthwm, superio, 1, 1, 1);
258 MODULE_VERSION(ncthwm, 1);