Home | History | Annotate | Line # | Download | only in x86
      1 /* $NetBSD: viac7temp.c,v 1.11 2024/04/30 19:35:29 andvar Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2009 Jared D. McNeill <jmcneill (at) invisible.ca>
      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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: viac7temp.c,v 1.11 2024/04/30 19:35:29 andvar Exp $");
     31 
     32 #include <sys/param.h>
     33 #include <sys/device.h>
     34 #include <sys/kmem.h>
     35 #include <sys/module.h>
     36 #include <sys/xcall.h>
     37 
     38 #include <dev/sysmon/sysmonvar.h>
     39 
     40 #include <machine/cpuvar.h>
     41 #include <machine/cpufunc.h>
     42 #include <machine/cputypes.h>
     43 #include <machine/specialreg.h>
     44 
     45 #define	MSR_TEMP_NANO	0x1423	/* VIA Nano and Zhaoxin CPUs */
     46 #define	MSR_TEMP_C7	0x1169	/* VIA C7 CPUs */
     47 
     48 struct viac7temp_softc {
     49 	device_t		 sc_dev;
     50 	struct cpu_info		*sc_ci;
     51 	struct sysmon_envsys	*sc_sme;
     52 	envsys_data_t		 sc_sensor;
     53 	uint32_t		 sc_temp_msr;
     54 };
     55 
     56 static int	viac7temp_match(device_t, cfdata_t, void *);
     57 static void	viac7temp_attach(device_t, device_t, void *);
     58 static int	viac7temp_detach(device_t, int);
     59 static void	viac7temp_refresh(struct sysmon_envsys *, envsys_data_t *);
     60 static void	viac7temp_refresh_xcall(void *, void *);
     61 static uint32_t	viac7temp_msr_register(struct cpu_info *ci);
     62 
     63 CFATTACH_DECL_NEW(viac7temp, sizeof(struct viac7temp_softc),
     64     viac7temp_match, viac7temp_attach, viac7temp_detach, NULL);
     65 
     66 static int
     67 viac7temp_match(device_t parent, cfdata_t cf, void *aux)
     68 {
     69 	struct cpufeature_attach_args *cfaa = aux;
     70 	struct cpu_info *ci = cfaa->ci;
     71 	uint32_t temp_msr;
     72 	uint64_t val;
     73 
     74 	if (strcmp(cfaa->name, "temperature") != 0)
     75 		return 0;
     76 
     77 	if (cpu_vendor != CPUVENDOR_IDT)
     78 		return 0;
     79 
     80 	temp_msr = viac7temp_msr_register(ci);
     81 
     82 	if (!temp_msr || rdmsr_safe(temp_msr, &val) == EFAULT)
     83 		return 0;
     84 
     85 	return 1;
     86 }
     87 
     88 static void
     89 viac7temp_attach(device_t parent, device_t self, void *aux)
     90 {
     91 	struct viac7temp_softc *sc = device_private(self);
     92 	struct cpufeature_attach_args *cfaa = aux;
     93 	struct cpu_info *ci = cfaa->ci;
     94 
     95 	sc->sc_ci = ci;
     96 	sc->sc_dev = self;
     97 
     98 	sc->sc_sensor.units = ENVSYS_STEMP;
     99 	sc->sc_sensor.flags = ENVSYS_FMONLIMITS|ENVSYS_FHAS_ENTROPY;
    100 	sc->sc_sensor.state = ENVSYS_SINVALID;
    101 
    102 	sc->sc_temp_msr = viac7temp_msr_register(ci);
    103 
    104 	(void)strlcpy(sc->sc_sensor.desc, "temperature",
    105 	    sizeof(sc->sc_sensor.desc));
    106 
    107 	sc->sc_sme = sysmon_envsys_create();
    108 
    109 	if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor) != 0)
    110 		goto fail;
    111 
    112 	sc->sc_sme->sme_cookie = sc;
    113 	sc->sc_sme->sme_name = device_xname(self);
    114 	sc->sc_sme->sme_refresh = viac7temp_refresh;
    115 
    116 	if (sysmon_envsys_register(sc->sc_sme) != 0)
    117 		goto fail;
    118 
    119 	aprint_naive("\n");
    120 	aprint_normal(": VIA C7/Nano temperature sensor\n");
    121 
    122 	(void)pmf_device_register(self, NULL, NULL);
    123 
    124 	return;
    125 
    126 fail:
    127 	sysmon_envsys_destroy(sc->sc_sme);
    128 	sc->sc_sme = NULL;
    129 }
    130 
    131 static int
    132 viac7temp_detach(device_t self, int flags)
    133 {
    134 	struct viac7temp_softc *sc = device_private(self);
    135 
    136 	if (sc->sc_sme != NULL)
    137 		sysmon_envsys_unregister(sc->sc_sme);
    138 
    139 	pmf_device_deregister(self);
    140 
    141 	return 0;
    142 }
    143 
    144 static void
    145 viac7temp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
    146 {
    147 	struct viac7temp_softc *sc = sme->sme_cookie;
    148 	uint64_t xc;
    149 
    150 	xc = xc_unicast(0, viac7temp_refresh_xcall, sc, edata, sc->sc_ci);
    151 	xc_wait(xc);
    152 }
    153 
    154 static void
    155 viac7temp_refresh_xcall(void *arg0, void *arg1)
    156 {
    157 	struct viac7temp_softc *sc = arg0;
    158 	envsys_data_t *edata = arg1;
    159 	uint64_t msr;
    160 
    161 	if (rdmsr_safe(sc->sc_temp_msr, &msr) == EFAULT) {
    162 		edata->value_cur = 0;
    163 		edata->state = ENVSYS_SINVALID;
    164 		aprint_error_dev(sc->sc_dev, "Reading temperature failed\n");
    165 		return;
    166 	}
    167 
    168 	/* Lower 24-bits hold value in Celsius */
    169 	edata->value_cur = msr & 0xffffff;
    170 	edata->value_cur *= 1000000;
    171 	edata->value_cur += 273150000;
    172 	edata->state = ENVSYS_SVALID;
    173 }
    174 
    175 static uint32_t viac7temp_msr_register(struct cpu_info *ci)
    176 {
    177 	uint32_t family, model;
    178 	uint32_t reg;
    179 
    180 	reg = 0;
    181 	model = CPUID_TO_MODEL(ci->ci_signature);
    182 	family = CPUID_TO_FAMILY(ci->ci_signature);
    183 
    184 	if (family == 0x07 || (family == 0x06 && model >= 0x0f))
    185 		reg = MSR_TEMP_NANO;
    186 	else if (family == 0x06 && model > 0x09)
    187 		reg = MSR_TEMP_C7;
    188 
    189 	return reg;
    190 }
    191 
    192 MODULE(MODULE_CLASS_DRIVER, viac7temp, NULL);
    193 
    194 #ifdef _MODULE
    195 #include "ioconf.c"
    196 #endif
    197 
    198 static int
    199 viac7temp_modcmd(modcmd_t cmd, void *arg __unused)
    200 {
    201 	int error = 0;
    202 
    203 	switch (cmd) {
    204 	case MODULE_CMD_INIT:
    205 #ifdef _MODULE
    206 		error = config_init_component(cfdriver_ioconf_viac7temp,
    207 		    cfattach_ioconf_viac7temp, cfdata_ioconf_viac7temp);
    208 #endif
    209 		return error;
    210 	case MODULE_CMD_FINI:
    211 #ifdef _MODULE
    212 		error = config_fini_component(cfdriver_ioconf_viac7temp,
    213 		    cfattach_ioconf_viac7temp, cfdata_ioconf_viac7temp);
    214 #endif
    215 		return error;
    216 	default:
    217 		return ENOTTY;
    218 	}
    219 }
    220