1 1.1 yamt /* $NetBSD: pchtemp.c,v 1.1 2026/02/20 07:54:26 yamt Exp $ */ 2 1.1 yamt 3 1.1 yamt /*- 4 1.1 yamt * Copyright (c)2026 YAMAMOTO Takashi, 5 1.1 yamt * All rights reserved. 6 1.1 yamt * 7 1.1 yamt * Redistribution and use in source and binary forms, with or without 8 1.1 yamt * modification, are permitted provided that the following conditions 9 1.1 yamt * are met: 10 1.1 yamt * 1. Redistributions of source code must retain the above copyright 11 1.1 yamt * notice, this list of conditions and the following disclaimer. 12 1.1 yamt * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 yamt * notice, this list of conditions and the following disclaimer in the 14 1.1 yamt * documentation and/or other materials provided with the distribution. 15 1.1 yamt * 16 1.1 yamt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 1.1 yamt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 1.1 yamt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 1.1 yamt * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 1.1 yamt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 1.1 yamt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 1.1 yamt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 1.1 yamt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 1.1 yamt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 1.1 yamt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 1.1 yamt * SUCH DAMAGE. 27 1.1 yamt */ 28 1.1 yamt 29 1.1 yamt #include <sys/cdefs.h> 30 1.1 yamt __KERNEL_RCSID(0, "$NetBSD: pchtemp.c,v 1.1 2026/02/20 07:54:26 yamt Exp $"); 31 1.1 yamt 32 1.1 yamt #include <sys/param.h> 33 1.1 yamt 34 1.1 yamt #include <sys/bus.h> 35 1.1 yamt #include <sys/module.h> 36 1.1 yamt 37 1.1 yamt #include <dev/pci/pcidevs.h> 38 1.1 yamt #include <dev/pci/pcireg.h> 39 1.1 yamt #include <dev/pci/pcivar.h> 40 1.1 yamt 41 1.1 yamt #include <dev/sysmon/sysmonvar.h> 42 1.1 yamt 43 1.1 yamt /* 44 1.1 yamt * references: 45 1.1 yamt * 46 1.1 yamt * 200-series-chipset-pch-datasheet-vol-2.pdf, downloaded from: 47 1.1 yamt * https://www.intel.com/content/www/us/en/content-details/335193/intel-200-series-chipset-family-platform-controller-hub-pch-datasheet-volume-2-of-2.html 48 1.1 yamt */ 49 1.1 yamt 50 1.1 yamt /* Thermal Reporting Configuration Registers */ 51 1.1 yamt #define PCHTEMP_CONF_TBAR 0x10 52 1.1 yamt #define PCHTEMP_CONF_TBARH 0x14 53 1.1 yamt 54 1.1 yamt /* Thermal Reporting Memory Mapped Registers */ 55 1.1 yamt #define PCHTEMP_TEMP 0x00 56 1.1 yamt #define PCHTEMP_TSEL 0x08 57 1.1 yamt 58 1.1 yamt /* PCHTEMP_TEMP */ 59 1.1 yamt #define PCHTEMP_TEMP_TSR_MASK 0x1ff 60 1.1 yamt 61 1.1 yamt /* PCHTEMP_TSEL */ 62 1.1 yamt #define PCHTEMP_TSEL_PLDB 0x80 63 1.1 yamt #define PCHTEMP_TSEL_ETS 0x01 64 1.1 yamt 65 1.1 yamt struct pchtemp_softc { 66 1.1 yamt device_t sc_dev; 67 1.1 yamt 68 1.1 yamt /* Thermal Reporting Memory Mapped Registers */ 69 1.1 yamt bus_space_tag_t sc_iot; 70 1.1 yamt bus_space_handle_t sc_ioh; 71 1.1 yamt bus_size_t sc_size; 72 1.1 yamt 73 1.1 yamt /* envsys stuff */ 74 1.1 yamt struct sysmon_envsys *sc_sme; 75 1.1 yamt envsys_data_t sc_sensor; 76 1.1 yamt }; 77 1.1 yamt 78 1.1 yamt static int32_t 79 1.1 yamt pchtemp_convert_to_envsys_temp(uint16_t tsr) 80 1.1 yamt { 81 1.1 yamt /* 82 1.1 yamt * the tsr value is in 0.5 degC resolution, offset -50C. 83 1.1 yamt * ie. degC = tsr / 2 - 50 84 1.1 yamt * 85 1.1 yamt * ENVSYS_STEMP is in microkelvin. (uK) 86 1.1 yamt * 87 1.1 yamt * uK = K * 1000000 88 1.1 yamt * = (degC + 273.15) * 1000000 89 1.1 yamt * = ((tsr / 2 - 50) + 273.15) * 1000000 90 1.1 yamt * = tsr * 500000 + 223150000 91 1.1 yamt * 92 1.1 yamt * this does never overflow int32_t as 0 <= tsr <= 0x1ff. 93 1.1 yamt */ 94 1.1 yamt return (int32_t)tsr * 500000 + 223150000; 95 1.1 yamt } 96 1.1 yamt 97 1.1 yamt static void 98 1.1 yamt pchtemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 99 1.1 yamt { 100 1.1 yamt struct pchtemp_softc *sc = sme->sme_cookie; 101 1.1 yamt uint16_t temp = bus_space_read_2(sc->sc_iot, sc->sc_ioh, PCHTEMP_TEMP); 102 1.1 yamt uint16_t tsr = temp & PCHTEMP_TEMP_TSR_MASK; 103 1.1 yamt 104 1.1 yamt sc->sc_sensor.value_cur = pchtemp_convert_to_envsys_temp(tsr); 105 1.1 yamt sc->sc_sensor.state = ENVSYS_SVALID; 106 1.1 yamt } 107 1.1 yamt 108 1.1 yamt static int 109 1.1 yamt pchtemp_match(device_t parent, cfdata_t match, void *aux) 110 1.1 yamt { 111 1.1 yamt const struct pci_attach_args *pa = aux; 112 1.1 yamt if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_INTEL) { 113 1.1 yamt return 0; 114 1.1 yamt } 115 1.1 yamt switch (PCI_PRODUCT(pa->pa_id)) { 116 1.1 yamt case PCI_PRODUCT_INTEL_8SERIES_THERM: 117 1.1 yamt case PCI_PRODUCT_INTEL_CORE4G_M_THERM: 118 1.1 yamt case PCI_PRODUCT_INTEL_CORE5G_M_THERM: 119 1.1 yamt case PCI_PRODUCT_INTEL_100SERIES_THERM: 120 1.1 yamt case PCI_PRODUCT_INTEL_100SERIES_LP_THERM: 121 1.1 yamt case PCI_PRODUCT_INTEL_2HS_THERM: 122 1.1 yamt case PCI_PRODUCT_INTEL_3HS_THERM: 123 1.1 yamt case PCI_PRODUCT_INTEL_3HS_U_THERM: 124 1.1 yamt case PCI_PRODUCT_INTEL_4HS_H_THERM: 125 1.1 yamt case PCI_PRODUCT_INTEL_CMTLK_THERM: 126 1.1 yamt case PCI_PRODUCT_INTEL_C610_THERM: 127 1.1 yamt case PCI_PRODUCT_INTEL_C620_THERM: 128 1.1 yamt return 1; 129 1.1 yamt } 130 1.1 yamt return 0; 131 1.1 yamt } 132 1.1 yamt 133 1.1 yamt static void 134 1.1 yamt pchtemp_envsys_attach(struct pchtemp_softc *sc) 135 1.1 yamt { 136 1.1 yamt const char *xname = device_xname(sc->sc_dev); 137 1.1 yamt struct sysmon_envsys *sme; 138 1.1 yamt envsys_data_t *sensor = &sc->sc_sensor; 139 1.1 yamt int error; 140 1.1 yamt 141 1.1 yamt sensor->units = ENVSYS_STEMP; 142 1.1 yamt sensor->state = ENVSYS_SINVALID; 143 1.1 yamt sensor->flags = 0; 144 1.1 yamt (void)snprintf(sensor->desc, sizeof(sensor->desc), "%s temperature", 145 1.1 yamt xname); 146 1.1 yamt 147 1.1 yamt sme = sysmon_envsys_create(); 148 1.1 yamt error = sysmon_envsys_sensor_attach(sme, sensor); 149 1.1 yamt if (error != 0) { 150 1.1 yamt aprint_error_dev(sc->sc_dev, 151 1.1 yamt "sysmon_envsys_sensor_attach failed (error %d)\n", error); 152 1.1 yamt goto fail; 153 1.1 yamt } 154 1.1 yamt sme->sme_cookie = sc; 155 1.1 yamt sme->sme_name = xname; 156 1.1 yamt sme->sme_refresh = pchtemp_refresh; 157 1.1 yamt error = sysmon_envsys_register(sme); 158 1.1 yamt if (error != 0) { 159 1.1 yamt aprint_error_dev(sc->sc_dev, 160 1.1 yamt "sysmon_envsys_register failed (error %d)\n", error); 161 1.1 yamt goto fail; 162 1.1 yamt } 163 1.1 yamt sc->sc_sme = sme; 164 1.1 yamt return; 165 1.1 yamt fail: 166 1.1 yamt sysmon_envsys_destroy(sme); 167 1.1 yamt } 168 1.1 yamt 169 1.1 yamt static void 170 1.1 yamt pchtemp_attach(device_t parent, device_t self, void *aux) 171 1.1 yamt { 172 1.1 yamt struct pchtemp_softc *sc = device_private(self); 173 1.1 yamt struct pci_attach_args *pa = aux; 174 1.1 yamt uint8_t tsel; 175 1.1 yamt 176 1.1 yamt KASSERT( 177 1.1 yamt sc->sc_sme == NULL && sc->sc_size == 0); /* zeroed by autoconf */ 178 1.1 yamt sc->sc_dev = self; 179 1.1 yamt 180 1.1 yamt aprint_naive("\n"); 181 1.1 yamt aprint_normal(": Intel PCH Temperature Sensor\n"); 182 1.1 yamt 183 1.1 yamt if (pci_mapreg_map(pa, PCHTEMP_CONF_TBAR, 184 1.1 yamt PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_64BIT, 0, &sc->sc_iot, 185 1.1 yamt &sc->sc_ioh, NULL, &sc->sc_size)) { 186 1.1 yamt aprint_error_dev(self, "can't map I/O space\n"); 187 1.1 yamt goto out; 188 1.1 yamt } 189 1.1 yamt 190 1.1 yamt /* try to enable the sensor if it isn't already enabled */ 191 1.1 yamt tsel = bus_space_read_1(sc->sc_iot, sc->sc_ioh, PCHTEMP_TSEL); 192 1.1 yamt if ((tsel & PCHTEMP_TSEL_ETS) == 0) { 193 1.1 yamt aprint_normal_dev(sc->sc_dev, "disabled by BIOS\n"); 194 1.1 yamt if ((tsel & PCHTEMP_TSEL_PLDB) != 0) { 195 1.1 yamt aprint_normal_dev(sc->sc_dev, 196 1.1 yamt "can't enable the sensor as it's locked\n"); 197 1.1 yamt goto out; 198 1.1 yamt } 199 1.1 yamt tsel |= PCHTEMP_TSEL_ETS; 200 1.1 yamt bus_space_write_1(sc->sc_iot, sc->sc_ioh, PCHTEMP_TSEL, tsel); 201 1.1 yamt aprint_normal_dev(sc->sc_dev, "enabled by the driver\n"); 202 1.1 yamt } 203 1.1 yamt 204 1.1 yamt pchtemp_envsys_attach(sc); 205 1.1 yamt out:; 206 1.1 yamt } 207 1.1 yamt 208 1.1 yamt static int 209 1.1 yamt pchtemp_detach(device_t self, int flags) 210 1.1 yamt { 211 1.1 yamt struct pchtemp_softc *sc = device_private(self); 212 1.1 yamt 213 1.1 yamt if (sc->sc_sme != NULL) { 214 1.1 yamt sysmon_envsys_unregister(sc->sc_sme); 215 1.1 yamt } 216 1.1 yamt if (sc->sc_size != 0) { 217 1.1 yamt bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size); 218 1.1 yamt } 219 1.1 yamt return 0; 220 1.1 yamt } 221 1.1 yamt 222 1.1 yamt CFATTACH_DECL_NEW(pchtemp, sizeof(struct pchtemp_softc), pchtemp_match, 223 1.1 yamt pchtemp_attach, pchtemp_detach, NULL); 224 1.1 yamt 225 1.1 yamt MODULE(MODULE_CLASS_DRIVER, pchtemp, "sysmon_envsys"); 226 1.1 yamt 227 1.1 yamt #ifdef _MODULE 228 1.1 yamt #include "ioconf.c" 229 1.1 yamt #endif 230 1.1 yamt 231 1.1 yamt static int 232 1.1 yamt pchtemp_modcmd(modcmd_t cmd, void *aux) 233 1.1 yamt { 234 1.1 yamt int error = 0; 235 1.1 yamt 236 1.1 yamt switch (cmd) { 237 1.1 yamt case MODULE_CMD_INIT: 238 1.1 yamt #ifdef _MODULE 239 1.1 yamt error = config_init_component(cfdriver_ioconf_pchtemp, 240 1.1 yamt cfattach_ioconf_pchtemp, cfdata_ioconf_pchtemp); 241 1.1 yamt #endif 242 1.1 yamt break; 243 1.1 yamt case MODULE_CMD_FINI: 244 1.1 yamt #ifdef _MODULE 245 1.1 yamt error = config_fini_component(cfdriver_ioconf_pchtemp, 246 1.1 yamt cfattach_ioconf_pchtemp, cfdata_ioconf_pchtemp); 247 1.1 yamt #endif 248 1.1 yamt break; 249 1.1 yamt default: 250 1.1 yamt error = ENOTTY; 251 1.1 yamt break; 252 1.1 yamt } 253 1.1 yamt return error; 254 1.1 yamt } 255