1 /*
2 * Copyright (c) 2020 Takanori Watanabe
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26 #include <sys/param.h>
27 #include <sys/systm.h>
28 #include <sys/kernel.h>
29 #include <sys/module.h>
30 #include <sys/errno.h>
31 #include <sys/lock.h>
32 #include <sys/mutex.h>
33 #include <sys/sysctl.h>
34 #include <sys/syslog.h>
35 #include <sys/bus.h>
36
37 #include <machine/bus.h>
38 #include <sys/rman.h>
39 #include <machine/resource.h>
40 #include <dev/pci/pcivar.h>
41 #include <dev/pci/pcireg.h>
42
43 #define PCHTHERM_REG_TEMP 0
44 #define PCHTHERM_REG_TSC 4
45 #define PCHTHERM_REG_TSS 6
46 #define PCHTHERM_REG_TSEL 8
47 #define PCHTHERM_REG_TSREL 0xa
48 #define PCHTHERM_REG_TSMIC 0xc
49 #define PCHTHERM_REG_CTT 0x10
50 #define PCHTHERM_REG_TAHV 0x14
51 #define PCHTHERM_REG_TALV 0x18
52 #define PCHTHERM_REG_TSPM 0x1c
53 #define PCHTHERM_REG_TL 0x40
54 #define PCHTHERM_REG_TL2 0x50
55 #define PCHTHERM_REG_PHL 0x60
56 #define PCHTHERM_REG_PHLC 0x62
57 #define PCHTHERM_REG_TAS 0x80
58 #define PCHTHERM_REG_TSPIEN 0x82
59 #define PCHTHERM_REG_TSGPEN 0x84
60 #define PCHTHERM_REG_TCFD 0xf0
61 #define PCHTHERM_GEN_LOCKDOWN 0x80
62 #define PCHTHERM_GEN_ENABLE 1
63 #define PCHTHERM_TEMP_FACTOR 5
64 #define PCHTHERM_TEMP_ZERO 2231
65 #define PCHTHERM_TEMP_MASK 0x1ff
66 #define PCHTHERM_TL_T0 0
67 #define PCHTHERM_TL_T1 10
68 #define PCHTHERM_TL_T2 20
69 #define PCHTHERM_TEMP_TO_IK(val) (((val) & PCHTHERM_TEMP_MASK) * \
70 PCHTHERM_TEMP_FACTOR + \
71 PCHTHERM_TEMP_ZERO)
72
73 struct pchtherm_softc
74 {
75 int tbarrid;
76 struct resource *tbar;
77 int enable;
78 int ctten;
79 int pmtemp;
80 unsigned int pmtime;
81 };
82
83 static const struct pci_device_table pchtherm_devices[] =
84 {
85 { PCI_DEV(0x8086, 0x9c24),
86 PCI_DESCR("Haswell Thermal Subsystem")},
87 { PCI_DEV(0x8086, 0x8c24),
88 PCI_DESCR("Haswell Thermal Subsystem")},
89 { PCI_DEV(0x8086, 0x9ca4),
90 PCI_DESCR("Wildcat Point Thermal Subsystem")},
91 { PCI_DEV(0x8086, 0x9d31),
92 PCI_DESCR("Skylake PCH Thermal Subsystem")},
93 { PCI_DEV(0x8086, 0xa131),
94 PCI_DESCR("Skylake PCH 100 Thermal Subsystem")},
95 { PCI_DEV(0x8086, 0x9df9),
96 PCI_DESCR("CannonLake-LP Thermal Subsystem")},
97 { PCI_DEV(0x8086, 0xa379),
98 PCI_DESCR("CannonLake-H Thermal Subsystem")},
99 { PCI_DEV(0x8086, 0x02f9),
100 PCI_DESCR("CometLake-LP Thermal Subsystem")},
101 { PCI_DEV(0x8086, 0x06f9),
102 PCI_DESCR("CometLake-H Thermal Subsystem")},
103 { PCI_DEV(0x8086, 0xa1b1),
104 PCI_DESCR("Lewisburg Thermal Subsystem")},
105 { PCI_DEV(0x8086, 0x8d24),
106 PCI_DESCR("Wellsburg Thermal Subsystem")},
107 };
108
pchtherm_probe(device_t dev)109 static int pchtherm_probe(device_t dev)
110 {
111 const struct pci_device_table *tbl;
112
113 tbl = PCI_MATCH(dev, pchtherm_devices);
114 if (tbl == NULL)
115 return (ENXIO);
116 device_set_desc(dev, tbl->descr);
117
118 return (BUS_PROBE_DEFAULT);
119
120 }
pchtherm_tltemp_sysctl(SYSCTL_HANDLER_ARGS)121 static int pchtherm_tltemp_sysctl(SYSCTL_HANDLER_ARGS)
122 {
123 struct pchtherm_softc *sc = oidp->oid_arg1;
124 int regshift = oidp->oid_arg2;
125 int temp;
126
127 temp = bus_read_4(sc->tbar, PCHTHERM_REG_TL);
128 temp >>= regshift;
129 temp = PCHTHERM_TEMP_TO_IK(temp);
130
131 return sysctl_handle_int(oidp, &temp, 0, req);
132 }
pchtherm_temp_sysctl(SYSCTL_HANDLER_ARGS)133 static int pchtherm_temp_sysctl(SYSCTL_HANDLER_ARGS)
134 {
135 struct pchtherm_softc *sc = oidp->oid_arg1;
136 int regoff = oidp->oid_arg2;
137 int temp;
138
139 temp = bus_read_2(sc->tbar, regoff);
140 temp = PCHTHERM_TEMP_TO_IK(temp);
141
142 return sysctl_handle_int(oidp, &temp, 0, req);
143 }
144
145 #define FLAG_PRINT(dev, str, val) device_printf \
146 (dev, str " %s %sable\n", \
147 ((val) & 0x80)? "Locked" : "", \
148 ((val) & 0x1)? "En" : "Dis")
149
pchtherm_attach(device_t dev)150 static int pchtherm_attach(device_t dev)
151 {
152 struct pchtherm_softc *sc = device_get_softc(dev);
153 unsigned int val;
154 int flag;
155 int temp;
156
157 sc->tbarrid = PCIR_BAR(0);
158 sc->tbar = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
159 &sc->tbarrid, RF_ACTIVE|RF_SHAREABLE);
160 if (sc->tbar == NULL) {
161 return (ENOMEM);
162 }
163 sc->enable = bus_read_1(sc->tbar, PCHTHERM_REG_TSEL);
164 if (!(sc->enable & PCHTHERM_GEN_ENABLE )) {
165 if (sc->enable & PCHTHERM_GEN_LOCKDOWN) {
166 device_printf(dev, "Sensor not available\n");
167 return 0;
168 } else {
169 device_printf(dev, "Enabling Sensor\n");
170 bus_write_1(sc->tbar, PCHTHERM_REG_TSEL,
171 PCHTHERM_GEN_ENABLE);
172 sc->enable = bus_read_1(sc->tbar, PCHTHERM_REG_TSEL);
173 if (!(sc->enable & PCHTHERM_GEN_ENABLE)) {
174 device_printf(dev, "Sensor enable failed\n");
175 return 0;
176 }
177 }
178 }
179
180 sc->ctten = bus_read_1(sc->tbar, PCHTHERM_REG_TSC);
181 if (bootverbose) {
182 FLAG_PRINT(dev, "Catastrophic Power Down", sc->ctten);
183 }
184 val = bus_read_1(sc->tbar, PCHTHERM_REG_TSREL);
185 if (bootverbose) {
186 FLAG_PRINT(dev, "SMBus report", val);
187 }
188 val = bus_read_1(sc->tbar, PCHTHERM_REG_TSMIC);
189 if (bootverbose) {
190 FLAG_PRINT(dev, "SMI on alert", val);
191 }
192 val = bus_read_2(sc->tbar, PCHTHERM_REG_TSPM);
193 flag = val >> 13;
194 if (bootverbose) {
195 device_printf(dev, "TSPM: %b\n",
196 flag, "\20\3Lock\2DTSS0EN\1DSSOC0");
197 device_printf(dev, "MAX Thermal Sensor Shutdown Time %ds\n",
198 1<<((val>>9)&7));
199 }
200
201 temp = val & PCHTHERM_TEMP_MASK;
202 sc->pmtemp = PCHTHERM_TEMP_TO_IK(temp);
203 sc->pmtime = 1<<((val>>9)&7);
204 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
205 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
206 OID_AUTO, "pmtemp", CTLTYPE_INT|CTLFLAG_RD,
207 sc, PCHTHERM_REG_TSPM, pchtherm_temp_sysctl,
208 "IK", "Thermal sensor idle temperature");
209 SYSCTL_ADD_U32(device_get_sysctl_ctx(dev),
210 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
211 OID_AUTO, "pmtime", CTLFLAG_RD,
212 &sc->pmtime, 0,"Thermal sensor idle duration");
213
214 val = bus_read_4(sc->tbar, PCHTHERM_REG_TL);
215 flag = val>>29;
216
217 if (bootverbose) {
218 device_printf(dev, "Throttling %b\n",
219 flag, "\20\3Lock\2TT13EN\1TTEN");
220 }
221
222 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
223 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
224 OID_AUTO, "t0temp", CTLTYPE_INT |CTLFLAG_RD,
225 sc, PCHTHERM_TL_T0, pchtherm_tltemp_sysctl,
226 "IK", "T0 temperature");
227
228 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
229 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
230 OID_AUTO, "t1temp", CTLTYPE_INT |CTLFLAG_RD,
231 sc, PCHTHERM_TL_T1, pchtherm_tltemp_sysctl,
232 "IK", "T1 temperature");
233
234 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
235 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
236 OID_AUTO, "t2temp", CTLTYPE_INT |CTLFLAG_RD,
237 sc, PCHTHERM_TL_T2, pchtherm_tltemp_sysctl,
238 "IK", "T2 temperature");
239
240 val = bus_read_2(sc->tbar, PCHTHERM_REG_TL2);
241 if (bootverbose) {
242 flag = val >>14;
243 device_printf(dev, "TL2 flag %b\n",
244 flag, "\20\1PMCTEN\2Lock");
245 }
246
247 /* If PHL is disable and lockdown, don't export it.*/
248 val = bus_read_2(sc->tbar, PCHTHERM_REG_PHLC);
249 val <<= 16;
250 val |= bus_read_2(sc->tbar, PCHTHERM_REG_PHL);
251 if ((val & 0x10000) != 0x10000) {
252 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
253 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
254 OID_AUTO, "pch_hot_level",
255 CTLTYPE_INT |CTLFLAG_RD,
256 sc, PCHTHERM_REG_PHL,
257 pchtherm_temp_sysctl, "IK",
258 "PCH Hot level Temperature");
259 }
260
261 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
262 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
263 OID_AUTO, "temperature", CTLTYPE_INT |CTLFLAG_RD,
264 sc, PCHTHERM_REG_TEMP,
265 pchtherm_temp_sysctl, "IK", "Current temperature");
266 /*
267 * If sensor enable bit is locked down, there is no way to change
268 * alart values effectively.
269 */
270 if (!(sc->enable & PCHTHERM_GEN_LOCKDOWN) ||
271 bus_read_2(sc->tbar, PCHTHERM_REG_TAHV) != 0) {
272 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
273 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
274 OID_AUTO, "tahv", CTLTYPE_INT |CTLFLAG_RD,
275 sc, PCHTHERM_REG_TAHV,
276 pchtherm_temp_sysctl, "IK",
277 "Alart High temperature");
278 }
279
280 if (!(sc->enable & PCHTHERM_GEN_LOCKDOWN) ||
281 bus_read_2(sc->tbar, PCHTHERM_REG_TALV) != 0) {
282 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
283 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
284 OID_AUTO, "talv", CTLTYPE_INT |CTLFLAG_RD,
285 sc, PCHTHERM_REG_TALV,
286 pchtherm_temp_sysctl, "IK",
287 "Alart Low temperature");
288 }
289 if (!(sc->ctten& PCHTHERM_GEN_LOCKDOWN) ||
290 sc->ctten& PCHTHERM_GEN_ENABLE) {
291 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
292 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
293 OID_AUTO, "ctt",
294 CTLTYPE_INT |CTLFLAG_RD,
295 sc, PCHTHERM_REG_CTT,
296 pchtherm_temp_sysctl, "IK",
297 "Catastrophic Trip Point");
298 }
299
300 return 0;
301 }
pchtherm_detach(device_t dev)302 static int pchtherm_detach(device_t dev)
303 {
304 struct pchtherm_softc *sc = device_get_softc(dev);
305 bus_release_resource(dev, SYS_RES_MEMORY, sc->tbarrid, sc->tbar);
306
307 return 0;
308 }
309
310 static device_method_t pchtherm_methods[] =
311 {
312 DEVMETHOD(device_probe, pchtherm_probe),
313 DEVMETHOD(device_attach, pchtherm_attach),
314 DEVMETHOD(device_detach, pchtherm_detach),
315
316 DEVMETHOD_END
317 };
318
319 static driver_t pchtherm_driver = {
320 "pchtherm",
321 pchtherm_methods,
322 sizeof(struct pchtherm_softc)
323 };
324
325 DRIVER_MODULE(pchtherm, pci, pchtherm_driver, 0, 0);
326 PCI_PNP_INFO(pchtherm_devices);
327