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