Home | History | Annotate | Line # | Download | only in pci
amdzentemp.c revision 1.7.2.3
      1  1.7.2.3  martin /*      $NetBSD: amdzentemp.c,v 1.7.2.3 2022/10/11 18:22:44 martin Exp $ */
      2  1.7.2.2  martin /*      $OpenBSD: kate.c,v 1.2 2008/03/27 04:52:03 cnst Exp $   */
      3  1.7.2.2  martin 
      4  1.7.2.2  martin /*
      5  1.7.2.3  martin  * Copyright (c) 2008, 2020 The NetBSD Foundation, Inc.
      6  1.7.2.3  martin  * All rights reserved.
      7  1.7.2.3  martin  *
      8  1.7.2.3  martin  * Copyright (c) 2019 Conrad Meyer <cem (at) FreeBSD.org>
      9  1.7.2.2  martin  * All rights reserved.
     10  1.7.2.2  martin  *
     11  1.7.2.2  martin  * This code is derived from software contributed to The NetBSD Foundation
     12  1.7.2.2  martin  * by Christoph Egger.
     13  1.7.2.2  martin  *
     14  1.7.2.2  martin  * NetBSD port by Ian Clark <mrrooster (at) gmail.com>
     15  1.7.2.2  martin  *
     16  1.7.2.2  martin  * Redistribution and use in source and binary forms, with or without
     17  1.7.2.2  martin  * modification, are permitted provided that the following conditions
     18  1.7.2.2  martin  * are met:
     19  1.7.2.2  martin  * 1. Redistributions of source code must retain the above copyright
     20  1.7.2.2  martin  *    notice, this list of conditions and the following disclaimer.
     21  1.7.2.2  martin  * 2. Redistributions in binary form must reproduce the above copyright
     22  1.7.2.2  martin  *    notice, this list of conditions and the following disclaimer in the
     23  1.7.2.2  martin  *    documentation and/or other materials provided with the distribution.
     24  1.7.2.2  martin  *
     25  1.7.2.2  martin  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     26  1.7.2.2  martin  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     27  1.7.2.2  martin  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     28  1.7.2.2  martin  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     29  1.7.2.2  martin  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     30  1.7.2.2  martin  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     31  1.7.2.2  martin  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     32  1.7.2.2  martin  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     33  1.7.2.2  martin  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     34  1.7.2.2  martin  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     35  1.7.2.2  martin  * POSSIBILITY OF SUCH DAMAGE.
     36  1.7.2.2  martin  */
     37  1.7.2.2  martin 
     38  1.7.2.2  martin /*
     39  1.7.2.2  martin  * Copyright (c) 2008 Constantine A. Murenin <cnst+openbsd (at) bugmail.mojo.ru>
     40  1.7.2.2  martin  *
     41  1.7.2.2  martin  * Permission to use, copy, modify, and distribute this software for any
     42  1.7.2.2  martin  * purpose with or without fee is hereby granted, provided that the above
     43  1.7.2.2  martin  * copyright notice and this permission notice appear in all copies.
     44  1.7.2.2  martin  *
     45  1.7.2.2  martin  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     46  1.7.2.2  martin  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     47  1.7.2.2  martin  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     48  1.7.2.2  martin  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     49  1.7.2.2  martin  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     50  1.7.2.2  martin  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     51  1.7.2.2  martin  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     52  1.7.2.2  martin  */
     53  1.7.2.2  martin 
     54  1.7.2.2  martin 
     55  1.7.2.2  martin #include <sys/cdefs.h>
     56  1.7.2.3  martin __KERNEL_RCSID(0, "$NetBSD: amdzentemp.c,v 1.7.2.3 2022/10/11 18:22:44 martin Exp $ ");
     57  1.7.2.2  martin 
     58  1.7.2.2  martin #include <sys/param.h>
     59  1.7.2.2  martin #include <sys/bus.h>
     60  1.7.2.2  martin #include <sys/cpu.h>
     61  1.7.2.2  martin #include <sys/systm.h>
     62  1.7.2.2  martin #include <sys/device.h>
     63  1.7.2.2  martin #include <sys/kmem.h>
     64  1.7.2.2  martin #include <sys/module.h>
     65  1.7.2.2  martin 
     66  1.7.2.2  martin #include <machine/specialreg.h>
     67  1.7.2.2  martin 
     68  1.7.2.2  martin #include <dev/pci/pcireg.h>
     69  1.7.2.2  martin #include <dev/pci/pcivar.h>
     70  1.7.2.2  martin #include <dev/pci/pcidevs.h>
     71  1.7.2.2  martin 
     72  1.7.2.2  martin #include <dev/sysmon/sysmonvar.h>
     73  1.7.2.2  martin 
     74  1.7.2.2  martin #include "amdsmn.h"
     75  1.7.2.2  martin 
     76  1.7.2.3  martin #define	AMD_CURTMP_RANGE_ADJUST	49000000	/* in microKelvins (ie, 49C) */
     77  1.7.2.3  martin #define	AMD_CURTMP_RANGE_CHECK	__BIT(19)
     78  1.7.2.3  martin #define	F10_TEMP_CURTMP		__BITS(31,21)	/* XXX same as amdtemp.c */
     79  1.7.2.3  martin #define	F10_TEMP_CURTMP_MASK	0x7ff
     80  1.7.2.3  martin #define	F15M60_CURTMP_TJSEL	__BITS(17,16)
     81  1.7.2.3  martin 
     82  1.7.2.3  martin /*
     83  1.7.2.3  martin  * Reported Temperature, Family 15h, M60+
     84  1.7.2.3  martin  *
     85  1.7.2.3  martin  * Same register bit definitions as other Family 15h CPUs, but access is
     86  1.7.2.3  martin  * indirect via SMN, like Family 17h.
     87  1.7.2.3  martin  */
     88  1.7.2.3  martin #define	AMD_15H_M60H_REPTMP_CTRL	0xd8200ca4
     89  1.7.2.3  martin 
     90  1.7.2.3  martin /*
     91  1.7.2.3  martin  * Reported Temperature, Family 17h
     92  1.7.2.3  martin  *
     93  1.7.2.3  martin  * According to AMD OSRR for 17H, section 4.2.1, bits 31-21 of this register
     94  1.7.2.3  martin  * provide the current temp.  bit 19, when clear, means the temp is reported in
     95  1.7.2.3  martin  * a range 0.."225C" (probable typo for 255C), and when set changes the range
     96  1.7.2.3  martin  * to -49..206C.
     97  1.7.2.3  martin  */
     98  1.7.2.3  martin #define	AMD_17H_CUR_TMP			0x59800
     99  1.7.2.3  martin 
    100  1.7.2.3  martin /*
    101  1.7.2.3  martin  * The following register set was discovered experimentally by Ondrej erman
    102  1.7.2.3  martin  * and collaborators, but is not (yet) documented in a PPR/OSRR (other than
    103  1.7.2.3  martin  * the M70H PPR SMN memory map showing [0x59800, +0x314] as allocated to
    104  1.7.2.3  martin  * SMU::THM).  It seems plausible and the Linux sensor folks have adopted it.
    105  1.7.2.3  martin  */
    106  1.7.2.3  martin #define	AMD_17H_CCD_TMP_BASE		0x59954
    107  1.7.2.3  martin #define	AMD_17H_CCD_TMP_VALID		__BIT(11)
    108  1.7.2.2  martin 
    109  1.7.2.2  martin struct amdzentemp_softc {
    110  1.7.2.3  martin 	device_t sc_dev;
    111  1.7.2.2  martin 	struct sysmon_envsys *sc_sme;
    112  1.7.2.2  martin 	device_t sc_smn;
    113  1.7.2.2  martin 	envsys_data_t *sc_sensor;
    114  1.7.2.2  martin 	size_t sc_sensor_len;
    115  1.7.2.3  martin 	size_t sc_numsensors;
    116  1.7.2.3  martin 	int32_t sc_offset;
    117  1.7.2.3  martin 	uint32_t sc_ccd_tmp_base;
    118  1.7.2.3  martin };
    119  1.7.2.3  martin 
    120  1.7.2.3  martin enum {
    121  1.7.2.3  martin 	NOSENSOR = 0,
    122  1.7.2.3  martin 	CORE0_SENSOR0,
    123  1.7.2.3  martin 	CCD_BASE,
    124  1.7.2.3  martin 	CCD0 = CCD_BASE,
    125  1.7.2.3  martin 	CCD1,
    126  1.7.2.3  martin 	CCD2,
    127  1.7.2.3  martin 	CCD3,
    128  1.7.2.3  martin 	CCD4,
    129  1.7.2.3  martin 	CCD5,
    130  1.7.2.3  martin 	CCD6,
    131  1.7.2.3  martin 	CCD7,
    132  1.7.2.3  martin 	CCD_MAX,
    133  1.7.2.3  martin 	NUM_CCDS = CCD_MAX - CCD_BASE
    134  1.7.2.2  martin };
    135  1.7.2.2  martin 
    136  1.7.2.2  martin 
    137  1.7.2.2  martin static int  amdzentemp_match(device_t, cfdata_t, void *);
    138  1.7.2.2  martin static void amdzentemp_attach(device_t, device_t, void *);
    139  1.7.2.2  martin static int  amdzentemp_detach(device_t, int);
    140  1.7.2.2  martin 
    141  1.7.2.3  martin static void amdzentemp_init(struct amdzentemp_softc *, int, int);
    142  1.7.2.3  martin static void amdzentemp_setup_sensors(struct amdzentemp_softc *);
    143  1.7.2.3  martin static void amdzentemp_family15_refresh(struct sysmon_envsys *, envsys_data_t *);
    144  1.7.2.2  martin static void amdzentemp_family17_refresh(struct sysmon_envsys *, envsys_data_t *);
    145  1.7.2.3  martin static int  amdzentemp_probe_ccd_sensors(struct amdzentemp_softc *, int, int);
    146  1.7.2.3  martin static void amdzentemp_setup_ccd_sensors(struct amdzentemp_softc *);
    147  1.7.2.2  martin 
    148  1.7.2.2  martin CFATTACH_DECL_NEW(amdzentemp, sizeof(struct amdzentemp_softc),
    149  1.7.2.2  martin     amdzentemp_match, amdzentemp_attach, amdzentemp_detach, NULL);
    150  1.7.2.2  martin 
    151  1.7.2.2  martin static int
    152  1.7.2.2  martin amdzentemp_match(device_t parent, cfdata_t match, void *aux)
    153  1.7.2.2  martin {
    154  1.7.2.2  martin 	struct pci_attach_args *pa __diagused = aux;
    155  1.7.2.2  martin 
    156  1.7.2.2  martin 	KASSERT(PCI_VENDOR(pa->pa_id) == PCI_VENDOR_AMD);
    157  1.7.2.3  martin 
    158  1.7.2.2  martin 	cfdata_t parent_cfdata = device_cfdata(parent);
    159  1.7.2.3  martin 
    160  1.7.2.2  martin 	/* Got AMD family 17h system management network */
    161  1.7.2.2  martin 	return parent_cfdata->cf_name &&
    162  1.7.2.2  martin 	    memcmp(parent_cfdata->cf_name, "amdsmn", 6) == 0;
    163  1.7.2.2  martin }
    164  1.7.2.2  martin 
    165  1.7.2.2  martin static void
    166  1.7.2.2  martin amdzentemp_attach(device_t parent, device_t self, void *aux)
    167  1.7.2.2  martin {
    168  1.7.2.2  martin 	struct amdzentemp_softc *sc = device_private(self);
    169  1.7.2.3  martin 	struct cpu_info *ci = curcpu();
    170  1.7.2.3  martin 	int family, model;
    171  1.7.2.2  martin 	int error;
    172  1.7.2.2  martin 	size_t i;
    173  1.7.2.2  martin 
    174  1.7.2.3  martin 	sc->sc_dev = self;
    175  1.7.2.3  martin 
    176  1.7.2.3  martin 	family = CPUID_TO_FAMILY(ci->ci_signature);
    177  1.7.2.3  martin 	model = CPUID_TO_MODEL(ci->ci_signature);
    178  1.7.2.2  martin 	aprint_naive("\n");
    179  1.7.2.3  martin 	aprint_normal(": AMD CPU Temperature Sensors (Family%xh)", family);
    180  1.7.2.2  martin 
    181  1.7.2.2  martin 	sc->sc_smn = parent;
    182  1.7.2.3  martin 
    183  1.7.2.3  martin 	amdzentemp_init(sc, family, model);
    184  1.7.2.2  martin 
    185  1.7.2.2  martin 	aprint_normal("\n");
    186  1.7.2.2  martin 
    187  1.7.2.2  martin 	sc->sc_sme = sysmon_envsys_create();
    188  1.7.2.2  martin 	sc->sc_sensor_len = sizeof(envsys_data_t) * sc->sc_numsensors;
    189  1.7.2.2  martin 	sc->sc_sensor = kmem_zalloc(sc->sc_sensor_len, KM_SLEEP);
    190  1.7.2.2  martin 
    191  1.7.2.3  martin 	amdzentemp_setup_sensors(sc);
    192  1.7.2.2  martin 
    193  1.7.2.2  martin 	/*
    194  1.7.2.2  martin 	 * Set properties in sensors.
    195  1.7.2.2  martin 	 */
    196  1.7.2.2  martin 	for (i = 0; i < sc->sc_numsensors; i++) {
    197  1.7.2.3  martin 		if (sc->sc_sensor[i].private == NOSENSOR)
    198  1.7.2.3  martin 			continue;
    199  1.7.2.2  martin 		if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[i]))
    200  1.7.2.2  martin 			goto bad;
    201  1.7.2.2  martin 	}
    202  1.7.2.2  martin 
    203  1.7.2.2  martin 	/*
    204  1.7.2.2  martin 	 * Register the sysmon_envsys device.
    205  1.7.2.2  martin 	 */
    206  1.7.2.2  martin 	sc->sc_sme->sme_name = device_xname(self);
    207  1.7.2.2  martin 	sc->sc_sme->sme_cookie = sc;
    208  1.7.2.2  martin 
    209  1.7.2.3  martin 	switch (family) {
    210  1.7.2.3  martin 	case 0x15:
    211  1.7.2.3  martin 		sc->sc_sme->sme_refresh = amdzentemp_family15_refresh;
    212  1.7.2.3  martin 		break;
    213  1.7.2.3  martin 	case 0x17:
    214  1.7.2.3  martin 	case 0x19:
    215  1.7.2.3  martin 		sc->sc_sme->sme_refresh = amdzentemp_family17_refresh;
    216  1.7.2.3  martin 		break;
    217  1.7.2.3  martin 	default:
    218  1.7.2.3  martin 		/* XXX panic */
    219  1.7.2.3  martin 		break;
    220  1.7.2.3  martin 	}
    221  1.7.2.2  martin 
    222  1.7.2.2  martin 	error = sysmon_envsys_register(sc->sc_sme);
    223  1.7.2.2  martin 	if (error) {
    224  1.7.2.2  martin 		aprint_error_dev(self, "unable to register with sysmon "
    225  1.7.2.2  martin 		    "(error=%d)\n", error);
    226  1.7.2.2  martin 		goto bad;
    227  1.7.2.2  martin 	}
    228  1.7.2.2  martin 
    229  1.7.2.2  martin 	(void)pmf_device_register(self, NULL, NULL);
    230  1.7.2.2  martin 
    231  1.7.2.2  martin 	return;
    232  1.7.2.2  martin 
    233  1.7.2.2  martin bad:
    234  1.7.2.2  martin 	if (sc->sc_sme != NULL) {
    235  1.7.2.2  martin 		sysmon_envsys_destroy(sc->sc_sme);
    236  1.7.2.2  martin 		sc->sc_sme = NULL;
    237  1.7.2.2  martin 	}
    238  1.7.2.2  martin 
    239  1.7.2.2  martin 	kmem_free(sc->sc_sensor, sc->sc_sensor_len);
    240  1.7.2.2  martin 	sc->sc_sensor = NULL;
    241  1.7.2.2  martin }
    242  1.7.2.2  martin 
    243  1.7.2.2  martin static int
    244  1.7.2.2  martin amdzentemp_detach(device_t self, int flags)
    245  1.7.2.2  martin {
    246  1.7.2.2  martin 	struct amdzentemp_softc *sc = device_private(self);
    247  1.7.2.2  martin 
    248  1.7.2.2  martin 	pmf_device_deregister(self);
    249  1.7.2.2  martin 	if (sc->sc_sme != NULL)
    250  1.7.2.2  martin 		sysmon_envsys_unregister(sc->sc_sme);
    251  1.7.2.2  martin 
    252  1.7.2.2  martin 	if (sc->sc_sensor != NULL)
    253  1.7.2.2  martin 		kmem_free(sc->sc_sensor, sc->sc_sensor_len);
    254  1.7.2.2  martin 
    255  1.7.2.2  martin 	return 0;
    256  1.7.2.2  martin }
    257  1.7.2.2  martin 
    258  1.7.2.2  martin 
    259  1.7.2.2  martin static void
    260  1.7.2.3  martin amdzentemp_init(struct amdzentemp_softc *sc, int family, int model)
    261  1.7.2.2  martin {
    262  1.7.2.3  martin 
    263  1.7.2.3  martin 	sc->sc_numsensors = 1 + amdzentemp_probe_ccd_sensors(sc, family, model);
    264  1.7.2.3  martin 	sc->sc_offset = 0;
    265  1.7.2.3  martin 
    266  1.7.2.3  martin 	if (strstr(cpu_brand_string, "AMD Ryzen 5 1600X")
    267  1.7.2.3  martin 	    || strstr(cpu_brand_string, "AMD Ryzen 7 1700X")
    268  1.7.2.3  martin 	    || strstr(cpu_brand_string, "AMD Ryzen 7 1800X"))
    269  1.7.2.3  martin 		sc->sc_offset = -20000000;
    270  1.7.2.3  martin 	else if (strstr(cpu_brand_string, "AMD Ryzen 7 2700X"))
    271  1.7.2.3  martin 		sc->sc_offset = -10000000;
    272  1.7.2.3  martin 	else if (strstr(cpu_brand_string, "AMD Ryzen Threadripper 19")
    273  1.7.2.3  martin 	    || strstr(cpu_brand_string, "AMD Ryzen Threadripper 29"))
    274  1.7.2.3  martin 		sc->sc_offset = -27000000;
    275  1.7.2.2  martin }
    276  1.7.2.2  martin 
    277  1.7.2.2  martin static void
    278  1.7.2.3  martin amdzentemp_setup_sensors(struct amdzentemp_softc *sc)
    279  1.7.2.2  martin {
    280  1.7.2.2  martin 	sc->sc_sensor[0].units = ENVSYS_STEMP;
    281  1.7.2.2  martin 	sc->sc_sensor[0].state = ENVSYS_SVALID;
    282  1.7.2.2  martin 	sc->sc_sensor[0].flags = ENVSYS_FHAS_ENTROPY;
    283  1.7.2.3  martin 	sc->sc_sensor[0].private = CORE0_SENSOR0;
    284  1.7.2.2  martin 
    285  1.7.2.2  martin 	snprintf(sc->sc_sensor[0].desc, sizeof(sc->sc_sensor[0].desc),
    286  1.7.2.3  martin 	    "cpu%u temperature", device_unit(sc->sc_dev));
    287  1.7.2.3  martin 
    288  1.7.2.3  martin 	if (sc->sc_numsensors > 1)
    289  1.7.2.3  martin 		amdzentemp_setup_ccd_sensors(sc);
    290  1.7.2.2  martin }
    291  1.7.2.2  martin 
    292  1.7.2.2  martin static void
    293  1.7.2.3  martin amdzentemp_family15_refresh(struct sysmon_envsys *sme,
    294  1.7.2.3  martin     envsys_data_t *edata)
    295  1.7.2.2  martin {
    296  1.7.2.2  martin 	struct amdzentemp_softc *sc = sme->sme_cookie;
    297  1.7.2.3  martin 	uint32_t val, temp;
    298  1.7.2.2  martin 	int error;
    299  1.7.2.3  martin 
    300  1.7.2.3  martin 	error = amdsmn_read(sc->sc_smn, AMD_15H_M60H_REPTMP_CTRL, &val);
    301  1.7.2.2  martin 	if (error) {
    302  1.7.2.2  martin 		edata->state = ENVSYS_SINVALID;
    303  1.7.2.2  martin 		return;
    304  1.7.2.2  martin 	}
    305  1.7.2.3  martin 
    306  1.7.2.3  martin 	/* from amdtemp.c:amdtemp_family10_refresh() */
    307  1.7.2.3  martin 	temp = __SHIFTOUT(val, F10_TEMP_CURTMP);
    308  1.7.2.3  martin 
    309  1.7.2.3  martin 	/* From Celsius to micro-Kelvin. */
    310  1.7.2.3  martin 	edata->value_cur = (temp * 125000) + 273150000;
    311  1.7.2.3  martin 
    312  1.7.2.3  martin 	/*
    313  1.7.2.3  martin 	 * On Family 15h and higher, if CurTmpTjSel is 11b, the range is
    314  1.7.2.3  martin 	 * adjusted down by 49.0 degrees Celsius.  (This adjustment is not
    315  1.7.2.3  martin 	 * documented in BKDGs prior to family 15h model 00h.)
    316  1.7.2.3  martin 	 *
    317  1.7.2.3  martin 	 * XXX should be in amdtemp.c:amdtemp_family10_refresh() for f15
    318  1.7.2.3  martin 	 *     as well??
    319  1.7.2.3  martin 	 */
    320  1.7.2.3  martin 	if (__SHIFTOUT(val, F15M60_CURTMP_TJSEL) == 0x3)
    321  1.7.2.3  martin 		edata->value_cur -= AMD_CURTMP_RANGE_ADJUST;
    322  1.7.2.3  martin 
    323  1.7.2.2  martin 	edata->state = ENVSYS_SVALID;
    324  1.7.2.3  martin }
    325  1.7.2.3  martin 
    326  1.7.2.3  martin static void
    327  1.7.2.3  martin amdzentemp_family17_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
    328  1.7.2.3  martin {
    329  1.7.2.3  martin 	struct amdzentemp_softc *sc = sme->sme_cookie;
    330  1.7.2.3  martin 	uint32_t temp;
    331  1.7.2.3  martin 	bool minus49;
    332  1.7.2.3  martin 	int i, error;
    333  1.7.2.3  martin 
    334  1.7.2.3  martin 	switch (edata->private) {
    335  1.7.2.3  martin 	case CORE0_SENSOR0:
    336  1.7.2.3  martin 		/* Tctl */
    337  1.7.2.3  martin 		error = amdsmn_read(sc->sc_smn, AMD_17H_CUR_TMP, &temp);
    338  1.7.2.3  martin 		if (error) {
    339  1.7.2.3  martin 			edata->state = ENVSYS_SINVALID;
    340  1.7.2.3  martin 			return;
    341  1.7.2.3  martin 		}
    342  1.7.2.3  martin 		minus49 = (temp & AMD_CURTMP_RANGE_CHECK) ? true : false;
    343  1.7.2.3  martin 		temp = __SHIFTOUT(temp, F10_TEMP_CURTMP);
    344  1.7.2.3  martin 		break;
    345  1.7.2.3  martin 	case CCD_BASE ... (CCD_MAX - 1):
    346  1.7.2.3  martin 		/* Tccd */
    347  1.7.2.3  martin 		i = edata->private - CCD_BASE;
    348  1.7.2.3  martin 		error = amdsmn_read(sc->sc_smn,
    349  1.7.2.3  martin 		    sc->sc_ccd_tmp_base + (i * sizeof(temp)), &temp);
    350  1.7.2.3  martin 		if (error || !ISSET(temp, AMD_17H_CCD_TMP_VALID)) {
    351  1.7.2.3  martin 			edata->state = ENVSYS_SINVALID;
    352  1.7.2.3  martin 			return;
    353  1.7.2.3  martin 		}
    354  1.7.2.3  martin 		minus49 = true;
    355  1.7.2.3  martin 		temp &= F10_TEMP_CURTMP_MASK;
    356  1.7.2.3  martin 		break;
    357  1.7.2.3  martin 	default:
    358  1.7.2.3  martin 		edata->state = ENVSYS_SINVALID;
    359  1.7.2.3  martin 		return;
    360  1.7.2.3  martin 	}
    361  1.7.2.3  martin 	edata->state = ENVSYS_SVALID;
    362  1.7.2.3  martin 	/* From C to uK. */
    363  1.7.2.3  martin 	edata->value_cur = (temp * 125000) + 273150000;
    364  1.7.2.3  martin 	/* adjust for possible offset of 49K */
    365  1.7.2.3  martin 	if (minus49)
    366  1.7.2.3  martin 		edata->value_cur -= AMD_CURTMP_RANGE_ADJUST;
    367  1.7.2.3  martin 	edata->value_cur += sc->sc_offset;
    368  1.7.2.3  martin }
    369  1.7.2.3  martin 
    370  1.7.2.3  martin static int
    371  1.7.2.3  martin amdzentemp_probe_ccd_sensors17h(struct amdzentemp_softc *sc, int model)
    372  1.7.2.3  martin {
    373  1.7.2.3  martin 	int maxreg;
    374  1.7.2.3  martin 
    375  1.7.2.3  martin 	switch (model) {
    376  1.7.2.3  martin 	case 0x00 ... 0x2f: /* Zen1, Zen+ */
    377  1.7.2.3  martin 		maxreg = 4;
    378  1.7.2.3  martin 		break;
    379  1.7.2.3  martin 	case 0x30 ... 0x3f: /* Zen2 TR (Castle Peak)/EPYC (Rome) */
    380  1.7.2.3  martin 	case 0x60 ... 0x7f: /* Zen2 Ryzen (Renoir APU, Matisse) */
    381  1.7.2.3  martin 	case 0x90 ... 0x9f: /* Zen2 Ryzen (Van Gogh APU) */
    382  1.7.2.3  martin 		maxreg = 8;
    383  1.7.2.3  martin 		break;
    384  1.7.2.3  martin 	default:
    385  1.7.2.3  martin 		aprint_error_dev(sc->sc_dev,
    386  1.7.2.3  martin 		    "Unrecognized Family 17h Model: %02xh\n", model);
    387  1.7.2.3  martin 		return 0;
    388  1.7.2.3  martin 	}
    389  1.7.2.3  martin 
    390  1.7.2.3  martin 	return maxreg;
    391  1.7.2.3  martin }
    392  1.7.2.3  martin 
    393  1.7.2.3  martin static int
    394  1.7.2.3  martin amdzentemp_probe_ccd_sensors19h(struct amdzentemp_softc *sc, int model)
    395  1.7.2.3  martin {
    396  1.7.2.3  martin 	int maxreg;
    397  1.7.2.3  martin 
    398  1.7.2.3  martin 	switch (model) {
    399  1.7.2.3  martin 	case 0x00 ... 0x0f: /* Zen3 EPYC "Milan" */
    400  1.7.2.3  martin 	case 0x20 ... 0x2f: /* Zen3 Ryzen "Vermeer" */
    401  1.7.2.3  martin 		maxreg = 8;
    402  1.7.2.3  martin 		break;
    403  1.7.2.3  martin 	case 0x60 ... 0x6f: /* Zen4 Ryzen "Raphael" */
    404  1.7.2.3  martin 		sc->sc_ccd_tmp_base = 0x59b08;
    405  1.7.2.3  martin 		maxreg = 8;
    406  1.7.2.3  martin 		break;
    407  1.7.2.3  martin 	default:
    408  1.7.2.3  martin 		aprint_error_dev(sc->sc_dev,
    409  1.7.2.3  martin 		    "Unrecognized Family 19h Model: %02xh\n", model);
    410  1.7.2.3  martin 		return 0;
    411  1.7.2.3  martin 	}
    412  1.7.2.3  martin 
    413  1.7.2.3  martin 	return maxreg;
    414  1.7.2.3  martin }
    415  1.7.2.3  martin 
    416  1.7.2.3  martin static int
    417  1.7.2.3  martin amdzentemp_probe_ccd_sensors(struct amdzentemp_softc *sc, int family, int model)
    418  1.7.2.3  martin {
    419  1.7.2.3  martin 	int nccd;
    420  1.7.2.3  martin 
    421  1.7.2.3  martin 	/* Set default CCD temp sensor base address. */
    422  1.7.2.3  martin 	sc->sc_ccd_tmp_base = 0x59954;
    423  1.7.2.3  martin 
    424  1.7.2.3  martin 	switch (family) {
    425  1.7.2.3  martin 	case 0x17:
    426  1.7.2.3  martin 		nccd = amdzentemp_probe_ccd_sensors17h(sc, model);
    427  1.7.2.3  martin 		break;
    428  1.7.2.3  martin 	case 0x19:
    429  1.7.2.3  martin 		nccd = amdzentemp_probe_ccd_sensors19h(sc, model);
    430  1.7.2.3  martin 		break;
    431  1.7.2.3  martin 	default:
    432  1.7.2.3  martin 		return 0;
    433  1.7.2.3  martin 	}
    434  1.7.2.3  martin 
    435  1.7.2.3  martin 	return nccd;
    436  1.7.2.3  martin }
    437  1.7.2.3  martin 
    438  1.7.2.3  martin static void
    439  1.7.2.3  martin amdzentemp_setup_ccd_sensors(struct amdzentemp_softc *sc)
    440  1.7.2.3  martin {
    441  1.7.2.3  martin 	envsys_data_t *edata;
    442  1.7.2.3  martin 	size_t i;
    443  1.7.2.3  martin 	uint32_t temp;
    444  1.7.2.3  martin 	int error;
    445  1.7.2.3  martin 
    446  1.7.2.3  martin 	for (i = 0; i < sc->sc_numsensors - 1; i++) {
    447  1.7.2.3  martin 		error = amdsmn_read(sc->sc_smn,
    448  1.7.2.3  martin 		    sc->sc_ccd_tmp_base + (i * sizeof(temp)), &temp);
    449  1.7.2.3  martin 		if (error || !ISSET(temp, AMD_17H_CCD_TMP_VALID))
    450  1.7.2.3  martin 			continue;
    451  1.7.2.3  martin 
    452  1.7.2.3  martin 		edata = &sc->sc_sensor[1 + i];
    453  1.7.2.3  martin 		edata->units = ENVSYS_STEMP;
    454  1.7.2.3  martin 		edata->state = ENVSYS_SVALID;
    455  1.7.2.3  martin 		edata->flags = ENVSYS_FHAS_ENTROPY;
    456  1.7.2.3  martin 		edata->private = CCD_BASE + i;
    457  1.7.2.3  martin 		snprintf(edata->desc, sizeof(edata->desc),
    458  1.7.2.3  martin 		    "cpu%u ccd%zu temperature", device_unit(sc->sc_dev), i);
    459  1.7.2.3  martin 	}
    460  1.7.2.2  martin }
    461  1.7.2.2  martin 
    462  1.7.2.2  martin MODULE(MODULE_CLASS_DRIVER, amdzentemp, "sysmon_envsys,amdsmn");
    463  1.7.2.2  martin 
    464  1.7.2.2  martin #ifdef _MODULE
    465  1.7.2.2  martin #include "ioconf.c"
    466  1.7.2.2  martin #endif
    467  1.7.2.2  martin 
    468  1.7.2.2  martin static int
    469  1.7.2.2  martin amdzentemp_modcmd(modcmd_t cmd, void *aux)
    470  1.7.2.2  martin {
    471  1.7.2.2  martin 	int error = 0;
    472  1.7.2.2  martin 
    473  1.7.2.2  martin 	switch (cmd) {
    474  1.7.2.2  martin 	case MODULE_CMD_INIT:
    475  1.7.2.2  martin #ifdef _MODULE
    476  1.7.2.2  martin 		error = config_init_component(cfdriver_ioconf_amdzentemp,
    477  1.7.2.2  martin 		    cfattach_ioconf_amdzentemp, cfdata_ioconf_amdzentemp);
    478  1.7.2.2  martin #endif
    479  1.7.2.2  martin 		return error;
    480  1.7.2.2  martin 	case MODULE_CMD_FINI:
    481  1.7.2.2  martin #ifdef _MODULE
    482  1.7.2.2  martin 		error = config_fini_component(cfdriver_ioconf_amdzentemp,
    483  1.7.2.2  martin 		    cfattach_ioconf_amdzentemp, cfdata_ioconf_amdzentemp);
    484  1.7.2.2  martin #endif
    485  1.7.2.2  martin 		return error;
    486  1.7.2.2  martin 	default:
    487  1.7.2.2  martin 		return ENOTTY;
    488  1.7.2.2  martin 	}
    489  1.7.2.2  martin }
    490