/* * Copyright (c) 2020 Takanori Watanabe * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PCHTHERM_REG_TEMP 0 #define PCHTHERM_REG_TSC 4 #define PCHTHERM_REG_TSS 6 #define PCHTHERM_REG_TSEL 8 #define PCHTHERM_REG_TSREL 0xa #define PCHTHERM_REG_TSMIC 0xc #define PCHTHERM_REG_CTT 0x10 #define PCHTHERM_REG_TAHV 0x14 #define PCHTHERM_REG_TALV 0x18 #define PCHTHERM_REG_TSPM 0x1c #define PCHTHERM_REG_TL 0x40 #define PCHTHERM_REG_TL2 0x50 #define PCHTHERM_REG_PHL 0x60 #define PCHTHERM_REG_PHLC 0x62 #define PCHTHERM_REG_TAS 0x80 #define PCHTHERM_REG_TSPIEN 0x82 #define PCHTHERM_REG_TSGPEN 0x84 #define PCHTHERM_REG_TCFD 0xf0 #define PCHTHERM_GEN_LOCKDOWN 0x80 #define PCHTHERM_GEN_ENABLE 1 #define PCHTHERM_TEMP_FACTOR 5 #define PCHTHERM_TEMP_ZERO 2231 #define PCHTHERM_TEMP_MASK 0x1ff #define PCHTHERM_TL_T0 0 #define PCHTHERM_TL_T1 10 #define PCHTHERM_TL_T2 20 #define PCHTHERM_TEMP_TO_IK(val) (((val) & PCHTHERM_TEMP_MASK) * \ PCHTHERM_TEMP_FACTOR + \ PCHTHERM_TEMP_ZERO) struct pchtherm_softc { int tbarrid; struct resource *tbar; int enable; int ctten; int pmtemp; unsigned int pmtime; }; static const struct pci_device_table pchtherm_devices[] = { { PCI_DEV(0x8086, 0x9c24), PCI_DESCR("Haswell Thermal Subsystem")}, { PCI_DEV(0x8086, 0x8c24), PCI_DESCR("Haswell Thermal Subsystem")}, { PCI_DEV(0x8086, 0x9ca4), PCI_DESCR("Wildcat Point Thermal Subsystem")}, { PCI_DEV(0x8086, 0x9d31), PCI_DESCR("Skylake PCH Thermal Subsystem")}, { PCI_DEV(0x8086, 0xa131), PCI_DESCR("Skylake PCH 100 Thermal Subsystem")}, }; static int pchtherm_probe(device_t dev) { const struct pci_device_table *tbl; tbl = PCI_MATCH(dev, pchtherm_devices); if (tbl == NULL) return (ENXIO); device_set_desc(dev, tbl->descr); return (BUS_PROBE_DEFAULT); } static int pchtherm_tltemp_sysctl(SYSCTL_HANDLER_ARGS) { struct pchtherm_softc *sc = oidp->oid_arg1; int regshift = oidp->oid_arg2; int temp; temp = bus_read_4(sc->tbar, PCHTHERM_REG_TL); temp >>= regshift; temp = PCHTHERM_TEMP_TO_IK(temp); return sysctl_handle_int(oidp, &temp, 0, req); } static int pchtherm_temp_sysctl(SYSCTL_HANDLER_ARGS) { struct pchtherm_softc *sc = oidp->oid_arg1; int regoff = oidp->oid_arg2; int temp; temp = bus_read_2(sc->tbar, regoff); temp = PCHTHERM_TEMP_TO_IK(temp); return sysctl_handle_int(oidp, &temp, 0, req); } #define FLAG_PRINT(dev, str, val) device_printf \ (dev, str " %s %sable\n", \ ((val) & 0x80)? "Locked" : "", \ ((val) & 0x1)? "En" : "Dis") static int pchtherm_attach(device_t dev) { struct pchtherm_softc *sc = device_get_softc(dev); unsigned int val; int flag; int temp; sc->tbarrid = PCIR_BAR(0); sc->tbar = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->tbarrid, RF_ACTIVE|RF_SHAREABLE); if (sc->tbar == NULL) { return (ENOMEM); } sc->enable = bus_read_1(sc->tbar, PCHTHERM_REG_TSEL); if (!(sc->enable & PCHTHERM_GEN_ENABLE )) { if (sc->enable & PCHTHERM_GEN_LOCKDOWN) { device_printf(dev, "Sensor not available\n"); return 0; } else { device_printf(dev, "Enabling Sensor\n"); bus_write_1(sc->tbar, PCHTHERM_REG_TSEL, PCHTHERM_GEN_ENABLE); sc->enable = bus_read_1(sc->tbar, PCHTHERM_REG_TSEL); if (!(sc->enable & PCHTHERM_REG_TSEL)){ device_printf(dev, "Sensor enable failed\n"); return 0; } } } sc->ctten = bus_read_1(sc->tbar, PCHTHERM_REG_TSC); if (bootverbose) { FLAG_PRINT(dev, "Catastrophic Power Down", sc->ctten); } val = bus_read_1(sc->tbar, PCHTHERM_REG_TSREL); if (bootverbose) { FLAG_PRINT(dev, "SMBus report", val); } val = bus_read_1(sc->tbar, PCHTHERM_REG_TSC); if (bootverbose) { FLAG_PRINT(dev, "SMI on alert", val); } val = bus_read_2(sc->tbar, PCHTHERM_REG_TSPM); flag = val >> 13; if (bootverbose) { device_printf(dev, "TSPM: %b\n", flag, "\20\3Lock\2DTSS0EN\1DSSOC0"); device_printf(dev, "MAX Thermal Sensor Shutdown Time %ds\n", 1<<((val>>9)&7)); } temp = val & PCHTHERM_TEMP_MASK; sc->pmtemp = PCHTHERM_TEMP_TO_IK(temp); sc->pmtime = 1<<((val>>9)&7); SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "pmtemp", CTLTYPE_INT|CTLFLAG_RD, sc, PCHTHERM_REG_TSPM, pchtherm_temp_sysctl, "IK", "Thermal sensor idle temperature"); SYSCTL_ADD_U32(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "pmtime", CTLFLAG_RD, &sc->pmtime, 0,"Thermal sensor idle duration"); val = bus_read_4(sc->tbar, PCHTHERM_REG_TL); flag = val>>29; if (bootverbose) { device_printf(dev, "Throttling %b\n", flag, "\20\3Lock\2TT13EN\1TTEN"); } SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "t0temp", CTLTYPE_INT |CTLFLAG_RD, sc, PCHTHERM_TL_T0, pchtherm_tltemp_sysctl, "IK", "T0 temperature"); SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "t1temp", CTLTYPE_INT |CTLFLAG_RD, sc, PCHTHERM_TL_T1, pchtherm_tltemp_sysctl, "IK", "T1 temperature"); SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "t2temp", CTLTYPE_INT |CTLFLAG_RD, sc, PCHTHERM_TL_T2, pchtherm_tltemp_sysctl, "IK", "T2 temperature"); val = bus_read_2(sc->tbar, PCHTHERM_REG_TL2); if (bootverbose) { flag = val >>14; device_printf(dev, "TL2 flag %b\n", flag, "\20\1PMCTEN\2Lock"); } /* If PHL is disable and lockdown, don't export it.*/ val = bus_read_2(sc->tbar, PCHTHERM_REG_PHLC); val <<= 16; val |= bus_read_2(sc->tbar, PCHTHERM_REG_PHL); if ((val & 0x10000) != 0x10000) { SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "pch_hot_level", CTLTYPE_INT |CTLFLAG_RD, sc, PCHTHERM_REG_PHL, pchtherm_temp_sysctl, "IK", "PCH Hot level Temperature"); } SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "temperature", CTLTYPE_INT |CTLFLAG_RD, sc, PCHTHERM_REG_TEMP, pchtherm_temp_sysctl, "IK", "Current temperature"); /* * If sensor enable bit is locked down, there is no way to change * alart values effectively. */ if (!(sc->enable & PCHTHERM_GEN_LOCKDOWN) || bus_read_2(sc->tbar, PCHTHERM_REG_TAHV) != 0) { SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "tahv", CTLTYPE_INT |CTLFLAG_RD, sc, PCHTHERM_REG_TAHV, pchtherm_temp_sysctl, "IK", "Alart High temperature"); } if (!(sc->enable & PCHTHERM_GEN_LOCKDOWN) || bus_read_2(sc->tbar, PCHTHERM_REG_TALV) != 0) { SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "talv", CTLTYPE_INT |CTLFLAG_RD, sc, PCHTHERM_REG_TALV, pchtherm_temp_sysctl, "IK", "Alart Low temperature"); } if (!(sc->ctten& PCHTHERM_GEN_LOCKDOWN) || sc->ctten& PCHTHERM_GEN_ENABLE) { SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "ctt", CTLTYPE_INT |CTLFLAG_RD, sc, PCHTHERM_REG_CTT, pchtherm_temp_sysctl, "IK", "Catastrophic Trip Point"); } return 0; } static int pchtherm_detach(device_t dev) { struct pchtherm_softc *sc = device_get_softc(dev); bus_release_resource(dev, SYS_RES_MEMORY, sc->tbarrid, sc->tbar); return 0; } static device_method_t pchtherm_methods[] = { DEVMETHOD(device_probe, pchtherm_probe), DEVMETHOD(device_attach, pchtherm_attach), DEVMETHOD(device_detach, pchtherm_detach), DEVMETHOD_END }; static driver_t pchtherm_driver = { "pchtherm", pchtherm_methods, sizeof(struct pchtherm_softc) }; static devclass_t pchtherm_devclass; DRIVER_MODULE(pchtherm, pci, pchtherm_driver, pchtherm_devclass, 0, 0); PCI_PNP_INFO(pchtherm_devices);