xref: /netbsd-src/sys/arch/x86/pci/amdzentemp.c (revision 18f9d2550ca2d7fad952c41e271bee43e82c7640)
1*18f9d255Smsaitoh /*      $NetBSD: amdzentemp.c,v 1.22 2024/10/17 14:16:48 msaitoh Exp $ */
23a74451dSchristos /*      $OpenBSD: kate.c,v 1.2 2008/03/27 04:52:03 cnst Exp $   */
33a74451dSchristos 
43a74451dSchristos /*
5f90792d2Ssimonb  * Copyright (c) 2008, 2020 The NetBSD Foundation, Inc.
6f90792d2Ssimonb  * All rights reserved.
7f90792d2Ssimonb  *
8f90792d2Ssimonb  * Copyright (c) 2019 Conrad Meyer <cem@FreeBSD.org>
93a74451dSchristos  * All rights reserved.
103a74451dSchristos  *
113a74451dSchristos  * This code is derived from software contributed to The NetBSD Foundation
123a74451dSchristos  * by Christoph Egger.
133a74451dSchristos  *
143a74451dSchristos  * NetBSD port by Ian Clark <mrrooster@gmail.com>
153a74451dSchristos  *
163a74451dSchristos  * Redistribution and use in source and binary forms, with or without
173a74451dSchristos  * modification, are permitted provided that the following conditions
183a74451dSchristos  * are met:
193a74451dSchristos  * 1. Redistributions of source code must retain the above copyright
203a74451dSchristos  *    notice, this list of conditions and the following disclaimer.
213a74451dSchristos  * 2. Redistributions in binary form must reproduce the above copyright
223a74451dSchristos  *    notice, this list of conditions and the following disclaimer in the
233a74451dSchristos  *    documentation and/or other materials provided with the distribution.
243a74451dSchristos  *
253a74451dSchristos  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
263a74451dSchristos  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
273a74451dSchristos  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
283a74451dSchristos  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
293a74451dSchristos  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
303a74451dSchristos  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
313a74451dSchristos  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
323a74451dSchristos  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
333a74451dSchristos  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
343a74451dSchristos  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
353a74451dSchristos  * POSSIBILITY OF SUCH DAMAGE.
363a74451dSchristos  */
373a74451dSchristos 
383a74451dSchristos /*
393a74451dSchristos  * Copyright (c) 2008 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
403a74451dSchristos  *
413a74451dSchristos  * Permission to use, copy, modify, and distribute this software for any
423a74451dSchristos  * purpose with or without fee is hereby granted, provided that the above
433a74451dSchristos  * copyright notice and this permission notice appear in all copies.
443a74451dSchristos  *
453a74451dSchristos  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
463a74451dSchristos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
473a74451dSchristos  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
483a74451dSchristos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
493a74451dSchristos  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
503a74451dSchristos  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
513a74451dSchristos  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
523a74451dSchristos  */
533a74451dSchristos 
543a74451dSchristos 
553a74451dSchristos #include <sys/cdefs.h>
56*18f9d255Smsaitoh __KERNEL_RCSID(0, "$NetBSD: amdzentemp.c,v 1.22 2024/10/17 14:16:48 msaitoh Exp $ ");
573a74451dSchristos 
583a74451dSchristos #include <sys/param.h>
593a74451dSchristos #include <sys/bus.h>
603a74451dSchristos #include <sys/cpu.h>
613a74451dSchristos #include <sys/systm.h>
623a74451dSchristos #include <sys/device.h>
633a74451dSchristos #include <sys/kmem.h>
643a74451dSchristos #include <sys/module.h>
653a74451dSchristos 
663a74451dSchristos #include <machine/specialreg.h>
673a74451dSchristos 
683a74451dSchristos #include <dev/pci/pcireg.h>
693a74451dSchristos #include <dev/pci/pcivar.h>
703a74451dSchristos #include <dev/pci/pcidevs.h>
713a74451dSchristos 
723a74451dSchristos #include <dev/sysmon/sysmonvar.h>
733a74451dSchristos 
743a74451dSchristos #include "amdsmn.h"
753a74451dSchristos 
76f90792d2Ssimonb #define	AMD_CURTMP_RANGE_ADJUST	49000000	/* in microKelvins (ie, 49C) */
77f90792d2Ssimonb #define	F10_TEMP_CURTMP		__BITS(31,21)	/* XXX same as amdtemp.c */
78b067962fSnonaka #define	F10_TEMP_CURTMP_MASK	0x7ff
79f90792d2Ssimonb #define	F15M60_CURTMP_TJSEL	__BITS(17,16)
80f90792d2Ssimonb 
81f90792d2Ssimonb /*
82f90792d2Ssimonb  * Reported Temperature, Family 15h, M60+
83f90792d2Ssimonb  *
84f90792d2Ssimonb  * Same register bit definitions as other Family 15h CPUs, but access is
85f90792d2Ssimonb  * indirect via SMN, like Family 17h.
86f90792d2Ssimonb  */
87f90792d2Ssimonb #define	AMD_15H_M60H_REPTMP_CTRL	0xd8200ca4
88f90792d2Ssimonb 
89f90792d2Ssimonb /*
90f90792d2Ssimonb  * Reported Temperature, Family 17h
91f90792d2Ssimonb  *
92f90792d2Ssimonb  * According to AMD OSRR for 17H, section 4.2.1, bits 31-21 of this register
93f90792d2Ssimonb  * provide the current temp.  bit 19, when clear, means the temp is reported in
94f90792d2Ssimonb  * a range 0.."225C" (probable typo for 255C), and when set changes the range
95f90792d2Ssimonb  * to -49..206C.
96f90792d2Ssimonb  */
973a74451dSchristos #define	AMD_17H_CUR_TMP			0x59800
98c9f90033Smsaitoh #define	AMD_17H_CUR_TMP_RANGE_SEL	__BIT(19)
99b067962fSnonaka #define	AMD_17H_CCD_TMP_VALID		__BIT(11)
100b067962fSnonaka 
1013a74451dSchristos struct amdzentemp_softc {
102b067962fSnonaka 	device_t sc_dev;
1033a74451dSchristos 	struct sysmon_envsys *sc_sme;
1043a74451dSchristos 	device_t sc_smn;
1053a74451dSchristos 	envsys_data_t *sc_sensor;
1063a74451dSchristos 	size_t sc_sensor_len;
1073a74451dSchristos 	size_t sc_numsensors;
10886bc92a4Smlelstv 	int32_t sc_offset;
109c9f90033Smsaitoh 	int32_t sc_ccd_offset;
1103a74451dSchristos };
1113a74451dSchristos 
112b067962fSnonaka enum {
113b067962fSnonaka 	NOSENSOR = 0,
114b067962fSnonaka 	CORE0_SENSOR0,
115b067962fSnonaka 	CCD_BASE,
116b067962fSnonaka 	CCD0 = CCD_BASE,
117b067962fSnonaka 	CCD1,
118b067962fSnonaka 	CCD2,
119b067962fSnonaka 	CCD3,
120b067962fSnonaka 	CCD4,
121b067962fSnonaka 	CCD5,
122b067962fSnonaka 	CCD6,
123b067962fSnonaka 	CCD7,
12497018bdaSmsaitoh 	CCD8,
12597018bdaSmsaitoh 	CCD9,
12697018bdaSmsaitoh 	CCD10,
12797018bdaSmsaitoh 	CCD11,
128b067962fSnonaka 	CCD_MAX,
129b067962fSnonaka 	NUM_CCDS = CCD_MAX - CCD_BASE
130b067962fSnonaka };
131b067962fSnonaka 
1323a74451dSchristos 
1333a74451dSchristos static int  amdzentemp_match(device_t, cfdata_t, void *);
1343a74451dSchristos static void amdzentemp_attach(device_t, device_t, void *);
1353a74451dSchristos static int  amdzentemp_detach(device_t, int);
1363a74451dSchristos 
137b067962fSnonaka static void amdzentemp_init(struct amdzentemp_softc *, int, int);
138b067962fSnonaka static void amdzentemp_setup_sensors(struct amdzentemp_softc *);
139f90792d2Ssimonb static void amdzentemp_family15_refresh(struct sysmon_envsys *, envsys_data_t *);
1403a74451dSchristos static void amdzentemp_family17_refresh(struct sysmon_envsys *, envsys_data_t *);
141b067962fSnonaka static int  amdzentemp_probe_ccd_sensors(struct amdzentemp_softc *, int, int);
142b067962fSnonaka static void amdzentemp_setup_ccd_sensors(struct amdzentemp_softc *);
1433a74451dSchristos 
1443a74451dSchristos CFATTACH_DECL_NEW(amdzentemp, sizeof(struct amdzentemp_softc),
1453a74451dSchristos     amdzentemp_match, amdzentemp_attach, amdzentemp_detach, NULL);
1463a74451dSchristos 
1473a74451dSchristos static int
1483a74451dSchristos amdzentemp_match(device_t parent, cfdata_t match, void *aux)
1493a74451dSchristos {
150c0bd5644Spgoyette 	struct pci_attach_args *pa __diagused = aux;
151a74d9d21Spgoyette 
1523a74451dSchristos 	KASSERT(PCI_VENDOR(pa->pa_id) == PCI_VENDOR_AMD);
1533a74451dSchristos 
1543a74451dSchristos 	cfdata_t parent_cfdata = device_cfdata(parent);
1553a74451dSchristos 
1563a74451dSchristos 	/* Got AMD family 17h system management network */
1573a74451dSchristos 	return parent_cfdata->cf_name &&
1583a74451dSchristos 	    memcmp(parent_cfdata->cf_name, "amdsmn", 6) == 0;
1593a74451dSchristos }
1603a74451dSchristos 
1613a74451dSchristos static void
1623a74451dSchristos amdzentemp_attach(device_t parent, device_t self, void *aux)
1633a74451dSchristos {
1643a74451dSchristos 	struct amdzentemp_softc *sc = device_private(self);
165f90792d2Ssimonb 	struct cpu_info *ci = curcpu();
166b067962fSnonaka 	int family, model;
1673a74451dSchristos 	int error;
1683a74451dSchristos 	size_t i;
1693a74451dSchristos 
170b067962fSnonaka 	sc->sc_dev = self;
171b067962fSnonaka 
172f90792d2Ssimonb 	family = CPUID_TO_FAMILY(ci->ci_signature);
173b067962fSnonaka 	model = CPUID_TO_MODEL(ci->ci_signature);
1743a74451dSchristos 	aprint_naive("\n");
175b067962fSnonaka 	aprint_normal(": AMD CPU Temperature Sensors (Family%xh)", family);
1763a74451dSchristos 
1773a74451dSchristos 	sc->sc_smn = parent;
1783a74451dSchristos 
179b067962fSnonaka 	amdzentemp_init(sc, family, model);
1803a74451dSchristos 
1813a74451dSchristos 	aprint_normal("\n");
1823a74451dSchristos 
1833a74451dSchristos 	sc->sc_sme = sysmon_envsys_create();
1843a74451dSchristos 	sc->sc_sensor_len = sizeof(envsys_data_t) * sc->sc_numsensors;
1853a74451dSchristos 	sc->sc_sensor = kmem_zalloc(sc->sc_sensor_len, KM_SLEEP);
1863a74451dSchristos 
187b067962fSnonaka 	amdzentemp_setup_sensors(sc);
1883a74451dSchristos 
1893a74451dSchristos 	/*
1903a74451dSchristos 	 * Set properties in sensors.
1913a74451dSchristos 	 */
1923a74451dSchristos 	for (i = 0; i < sc->sc_numsensors; i++) {
193b067962fSnonaka 		if (sc->sc_sensor[i].private == NOSENSOR)
194b067962fSnonaka 			continue;
1953a74451dSchristos 		if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[i]))
1963a74451dSchristos 			goto bad;
1973a74451dSchristos 	}
1983a74451dSchristos 
1993a74451dSchristos 	/*
2003a74451dSchristos 	 * Register the sysmon_envsys device.
2013a74451dSchristos 	 */
2023a74451dSchristos 	sc->sc_sme->sme_name = device_xname(self);
2033a74451dSchristos 	sc->sc_sme->sme_cookie = sc;
2043a74451dSchristos 
205f90792d2Ssimonb 	switch (family) {
206f90792d2Ssimonb 	case 0x15:
207f90792d2Ssimonb 		sc->sc_sme->sme_refresh = amdzentemp_family15_refresh;
208f90792d2Ssimonb 		break;
209f90792d2Ssimonb 	case 0x17:
210b8ea29baSnonaka 	case 0x19:
2117d75762aSmsaitoh 	case 0x1a:
2123a74451dSchristos 		sc->sc_sme->sme_refresh = amdzentemp_family17_refresh;
213f90792d2Ssimonb 		break;
214f90792d2Ssimonb 	default:
215f90792d2Ssimonb 		/* XXX panic */
216f90792d2Ssimonb 		break;
217f90792d2Ssimonb 	}
2183a74451dSchristos 
2193a74451dSchristos 	error = sysmon_envsys_register(sc->sc_sme);
2203a74451dSchristos 	if (error) {
2213a74451dSchristos 		aprint_error_dev(self, "unable to register with sysmon "
2223a74451dSchristos 		    "(error=%d)\n", error);
2233a74451dSchristos 		goto bad;
2243a74451dSchristos 	}
2253a74451dSchristos 
2263a74451dSchristos 	(void)pmf_device_register(self, NULL, NULL);
2273a74451dSchristos 
2283a74451dSchristos 	return;
2293a74451dSchristos 
2303a74451dSchristos bad:
2313a74451dSchristos 	if (sc->sc_sme != NULL) {
2323a74451dSchristos 		sysmon_envsys_destroy(sc->sc_sme);
2333a74451dSchristos 		sc->sc_sme = NULL;
2343a74451dSchristos 	}
2353a74451dSchristos 
2363a74451dSchristos 	kmem_free(sc->sc_sensor, sc->sc_sensor_len);
2373a74451dSchristos 	sc->sc_sensor = NULL;
2383a74451dSchristos }
2393a74451dSchristos 
2403a74451dSchristos static int
2413a74451dSchristos amdzentemp_detach(device_t self, int flags)
2423a74451dSchristos {
2433a74451dSchristos 	struct amdzentemp_softc *sc = device_private(self);
2443a74451dSchristos 
2453a74451dSchristos 	pmf_device_deregister(self);
2463a74451dSchristos 	if (sc->sc_sme != NULL)
2473a74451dSchristos 		sysmon_envsys_unregister(sc->sc_sme);
2483a74451dSchristos 
2493a74451dSchristos 	if (sc->sc_sensor != NULL)
2503a74451dSchristos 		kmem_free(sc->sc_sensor, sc->sc_sensor_len);
2513a74451dSchristos 
2523a74451dSchristos 	return 0;
2533a74451dSchristos }
2543a74451dSchristos 
2553a74451dSchristos 
2563a74451dSchristos static void
257b067962fSnonaka amdzentemp_init(struct amdzentemp_softc *sc, int family, int model)
2583a74451dSchristos {
25986bc92a4Smlelstv 
260b067962fSnonaka 	sc->sc_numsensors = 1 + amdzentemp_probe_ccd_sensors(sc, family, model);
26186bc92a4Smlelstv 	sc->sc_offset = 0;
26286bc92a4Smlelstv 
26386bc92a4Smlelstv 	if (strstr(cpu_brand_string, "AMD Ryzen 5 1600X")
26486bc92a4Smlelstv 	    || strstr(cpu_brand_string, "AMD Ryzen 7 1700X")
26586bc92a4Smlelstv 	    || strstr(cpu_brand_string, "AMD Ryzen 7 1800X"))
26686bc92a4Smlelstv 		sc->sc_offset = -20000000;
26786bc92a4Smlelstv 	else if (strstr(cpu_brand_string, "AMD Ryzen 7 2700X"))
26886bc92a4Smlelstv 		sc->sc_offset = -10000000;
26986bc92a4Smlelstv 	else if (strstr(cpu_brand_string, "AMD Ryzen Threadripper 19")
27086bc92a4Smlelstv 	    || strstr(cpu_brand_string, "AMD Ryzen Threadripper 29"))
27186bc92a4Smlelstv 		sc->sc_offset = -27000000;
2723a74451dSchristos }
2733a74451dSchristos 
2743a74451dSchristos static void
275b067962fSnonaka amdzentemp_setup_sensors(struct amdzentemp_softc *sc)
2763a74451dSchristos {
2773a74451dSchristos 	sc->sc_sensor[0].units = ENVSYS_STEMP;
2783a74451dSchristos 	sc->sc_sensor[0].state = ENVSYS_SVALID;
2793a74451dSchristos 	sc->sc_sensor[0].flags = ENVSYS_FHAS_ENTROPY;
280b067962fSnonaka 	sc->sc_sensor[0].private = CORE0_SENSOR0;
2813a74451dSchristos 
2823a74451dSchristos 	snprintf(sc->sc_sensor[0].desc, sizeof(sc->sc_sensor[0].desc),
283b067962fSnonaka 	    "cpu%u temperature", device_unit(sc->sc_dev));
284b067962fSnonaka 
285b067962fSnonaka 	if (sc->sc_numsensors > 1)
286b067962fSnonaka 		amdzentemp_setup_ccd_sensors(sc);
2873a74451dSchristos }
2883a74451dSchristos 
2893a74451dSchristos static void
290f90792d2Ssimonb amdzentemp_family15_refresh(struct sysmon_envsys *sme,
291f90792d2Ssimonb     envsys_data_t *edata)
292f90792d2Ssimonb {
293f90792d2Ssimonb 	struct amdzentemp_softc *sc = sme->sme_cookie;
294f90792d2Ssimonb 	uint32_t val, temp;
295f90792d2Ssimonb 	int error;
296f90792d2Ssimonb 
297f90792d2Ssimonb 	error = amdsmn_read(sc->sc_smn, AMD_15H_M60H_REPTMP_CTRL, &val);
298f90792d2Ssimonb 	if (error) {
299f90792d2Ssimonb 		edata->state = ENVSYS_SINVALID;
300f90792d2Ssimonb 		return;
301f90792d2Ssimonb 	}
302f90792d2Ssimonb 
303f90792d2Ssimonb 	/* from amdtemp.c:amdtemp_family10_refresh() */
304f90792d2Ssimonb 	temp = __SHIFTOUT(val, F10_TEMP_CURTMP);
305f90792d2Ssimonb 
306f90792d2Ssimonb 	/* From Celsius to micro-Kelvin. */
307f90792d2Ssimonb 	edata->value_cur = (temp * 125000) + 273150000;
308f90792d2Ssimonb 
309f90792d2Ssimonb 	/*
310f90792d2Ssimonb 	 * On Family 15h and higher, if CurTmpTjSel is 11b, the range is
311f90792d2Ssimonb 	 * adjusted down by 49.0 degrees Celsius.  (This adjustment is not
312f90792d2Ssimonb 	 * documented in BKDGs prior to family 15h model 00h.)
313f90792d2Ssimonb 	 *
314f90792d2Ssimonb 	 * XXX should be in amdtemp.c:amdtemp_family10_refresh() for f15
315f90792d2Ssimonb 	 *     as well??
316f90792d2Ssimonb 	 */
317f90792d2Ssimonb 	if (__SHIFTOUT(val, F15M60_CURTMP_TJSEL) == 0x3)
318f90792d2Ssimonb 		edata->value_cur -= AMD_CURTMP_RANGE_ADJUST;
319f90792d2Ssimonb 
320f90792d2Ssimonb 	edata->state = ENVSYS_SVALID;
321f90792d2Ssimonb }
322f90792d2Ssimonb 
323f90792d2Ssimonb static void
3243a74451dSchristos amdzentemp_family17_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
3253a74451dSchristos {
3263a74451dSchristos 	struct amdzentemp_softc *sc = sme->sme_cookie;
3273a74451dSchristos 	uint32_t temp;
328b067962fSnonaka 	bool minus49;
329b067962fSnonaka 	int i, error;
3303a74451dSchristos 
331b067962fSnonaka 	switch (edata->private) {
332b067962fSnonaka 	case CORE0_SENSOR0:
333b067962fSnonaka 		/* Tctl */
3343a74451dSchristos 		error = amdsmn_read(sc->sc_smn, AMD_17H_CUR_TMP, &temp);
3353a74451dSchristos 		if (error) {
3363a74451dSchristos 			edata->state = ENVSYS_SINVALID;
3373a74451dSchristos 			return;
3383a74451dSchristos 		}
339c9f90033Smsaitoh 		minus49 = (temp & AMD_17H_CUR_TMP_RANGE_SEL) ?
340c9f90033Smsaitoh 		    true : false;
341b067962fSnonaka 		temp = __SHIFTOUT(temp, F10_TEMP_CURTMP);
342b067962fSnonaka 		break;
343b067962fSnonaka 	case CCD_BASE ... (CCD_MAX - 1):
344b067962fSnonaka 		/* Tccd */
345b067962fSnonaka 		i = edata->private - CCD_BASE;
346b067962fSnonaka 		error = amdsmn_read(sc->sc_smn,
347c9f90033Smsaitoh 		    AMD_17H_CUR_TMP + sc->sc_ccd_offset + (i * sizeof(temp)),
348c9f90033Smsaitoh 		    &temp);
349b067962fSnonaka 		if (error || !ISSET(temp, AMD_17H_CCD_TMP_VALID)) {
350b067962fSnonaka 			edata->state = ENVSYS_SINVALID;
351b067962fSnonaka 			return;
352b067962fSnonaka 		}
353b067962fSnonaka 		minus49 = true;
354b067962fSnonaka 		temp &= F10_TEMP_CURTMP_MASK;
355b067962fSnonaka 		break;
356b067962fSnonaka 	default:
357b067962fSnonaka 		edata->state = ENVSYS_SINVALID;
358b067962fSnonaka 		return;
359b067962fSnonaka 	}
3603a74451dSchristos 	edata->state = ENVSYS_SVALID;
3613a74451dSchristos 	/* From C to uK. */
362b067962fSnonaka 	edata->value_cur = (temp * 125000) + 273150000;
3631f4e4510Spara 	/* adjust for possible offset of 49K */
364b067962fSnonaka 	if (minus49)
365f90792d2Ssimonb 		edata->value_cur -= AMD_CURTMP_RANGE_ADJUST;
36686bc92a4Smlelstv 	edata->value_cur += sc->sc_offset;
3673a74451dSchristos }
3683a74451dSchristos 
369b067962fSnonaka static int
370b067962fSnonaka amdzentemp_probe_ccd_sensors17h(struct amdzentemp_softc *sc, int model)
371b067962fSnonaka {
372b067962fSnonaka 	int maxreg;
373b067962fSnonaka 
374b067962fSnonaka 	switch (model) {
375b067962fSnonaka 	case 0x00 ... 0x2f: /* Zen1, Zen+ */
376b39e984dSmsaitoh 		sc->sc_ccd_offset = 0x154;
377b067962fSnonaka 		maxreg = 4;
378b067962fSnonaka 		break;
379b067962fSnonaka 	case 0x30 ... 0x3f: /* Zen2 TR (Castle Peak)/EPYC (Rome) */
380b067962fSnonaka 	case 0x60 ... 0x7f: /* Zen2 Ryzen (Renoir APU, Matisse) */
381b067962fSnonaka 	case 0x90 ... 0x9f: /* Zen2 Ryzen (Van Gogh APU) */
382b39e984dSmsaitoh 		sc->sc_ccd_offset = 0x154;
383b39e984dSmsaitoh 		maxreg = 8;
384b39e984dSmsaitoh 		break;
385b39e984dSmsaitoh 	case 0xa0 ... 0xaf: /* Zen2 Ryzen (Mendocino APU) */
386b39e984dSmsaitoh 		sc->sc_ccd_offset = 0x300;
387b067962fSnonaka 		maxreg = 8;
388b067962fSnonaka 		break;
389b067962fSnonaka 	default:
390b067962fSnonaka 		aprint_error_dev(sc->sc_dev,
391b067962fSnonaka 		    "Unrecognized Family 17h Model: %02xh\n", model);
392b067962fSnonaka 		return 0;
393b067962fSnonaka 	}
394b067962fSnonaka 
395b067962fSnonaka 	return maxreg;
396b067962fSnonaka }
397b067962fSnonaka 
398b067962fSnonaka static int
399b067962fSnonaka amdzentemp_probe_ccd_sensors19h(struct amdzentemp_softc *sc, int model)
400b067962fSnonaka {
401b067962fSnonaka 	int maxreg;
402b067962fSnonaka 
403b067962fSnonaka 	switch (model) {
404b067962fSnonaka 	case 0x00 ... 0x0f: /* Zen3 EPYC "Milan" */
405b067962fSnonaka 	case 0x20 ... 0x2f: /* Zen3 Ryzen "Vermeer" */
406d137d649Smrg 	case 0x50 ... 0x5f: /* Zen3 Ryzen "Cezanne" */
407c9f90033Smsaitoh 		sc->sc_ccd_offset = 0x154;
408b067962fSnonaka 		maxreg = 8;
409b067962fSnonaka 		break;
41008e27227Smsaitoh 	case 0x60 ... 0x6f: /* Zen4 Ryzen "Raphael" */
411f2d8fd50Smsaitoh 	case 0x70 ... 0x7f: /* Zen4 Ryzen "Phoenix" */
412c9f90033Smsaitoh 		sc->sc_ccd_offset = 0x308;
41308e27227Smsaitoh 		maxreg = 8;
41408e27227Smsaitoh 		break;
41597018bdaSmsaitoh 	case 0x40 ... 0x4f: /* Zen3+ "Rembrandt" */
41697018bdaSmsaitoh 		sc->sc_ccd_offset = 0x300;
41797018bdaSmsaitoh 		maxreg = 8;
41897018bdaSmsaitoh 		break;
41997018bdaSmsaitoh 	case 0x10 ... 0x1f: /* Zen4 "Genoa" */
420*18f9d255Smsaitoh 	case 0xa0 ... 0xaf: /* Zen4 "Siena" */
42197018bdaSmsaitoh 		sc->sc_ccd_offset = 0x300;
42297018bdaSmsaitoh 		maxreg = 12;
42397018bdaSmsaitoh 		break;
424b067962fSnonaka 	default:
425b067962fSnonaka 		aprint_error_dev(sc->sc_dev,
426b067962fSnonaka 		    "Unrecognized Family 19h Model: %02xh\n", model);
427b067962fSnonaka 		return 0;
428b067962fSnonaka 	}
429b067962fSnonaka 
430b067962fSnonaka 	return maxreg;
431b067962fSnonaka }
432b067962fSnonaka 
433b067962fSnonaka static int
4347d75762aSmsaitoh amdzentemp_probe_ccd_sensors1ah(struct amdzentemp_softc *sc, int model)
4357d75762aSmsaitoh {
4367d75762aSmsaitoh 	int maxreg;
4377d75762aSmsaitoh 
4387d75762aSmsaitoh 	switch (model) {
439*18f9d255Smsaitoh 	case 0x00 ... 0x0f: /* Zen5 "Turin Classic" */
440*18f9d255Smsaitoh 		sc->sc_ccd_offset = 0x300;
441*18f9d255Smsaitoh 		maxreg = 16;
442*18f9d255Smsaitoh 		break;
443*18f9d255Smsaitoh 	case 0x10 ... 0x1f: /* Zen5 "Turin Dense" */
444*18f9d255Smsaitoh 		sc->sc_ccd_offset = 0x300;
445*18f9d255Smsaitoh 		maxreg = 12;
446*18f9d255Smsaitoh 		break;
447*18f9d255Smsaitoh 	case 0x20 ... 0x2f: /* Zen5 "Strix Point" */
448*18f9d255Smsaitoh 		sc->sc_ccd_offset = 0x300;
449*18f9d255Smsaitoh 		maxreg = 8;
450*18f9d255Smsaitoh 		break;
451*18f9d255Smsaitoh 	case 0x40 ... 0x4f: /* Zen5 "Granite Ridge "*/
4527d75762aSmsaitoh 		sc->sc_ccd_offset = 0x300;
4537d75762aSmsaitoh 		maxreg = 8;
4547d75762aSmsaitoh 		break;
4557d75762aSmsaitoh 	default:
4567d75762aSmsaitoh 		aprint_error_dev(sc->sc_dev,
4577d75762aSmsaitoh 		    "Unrecognized Family 19h Model: %02xh\n", model);
4587d75762aSmsaitoh 		return 0;
4597d75762aSmsaitoh 	}
4607d75762aSmsaitoh 
4617d75762aSmsaitoh 	return maxreg;
4627d75762aSmsaitoh }
4637d75762aSmsaitoh 
4647d75762aSmsaitoh static int
465b067962fSnonaka amdzentemp_probe_ccd_sensors(struct amdzentemp_softc *sc, int family, int model)
466b067962fSnonaka {
467b067962fSnonaka 	int nccd;
468b067962fSnonaka 
469b067962fSnonaka 	switch (family) {
470b067962fSnonaka 	case 0x17:
471b067962fSnonaka 		nccd = amdzentemp_probe_ccd_sensors17h(sc, model);
472b067962fSnonaka 		break;
473b067962fSnonaka 	case 0x19:
474b067962fSnonaka 		nccd = amdzentemp_probe_ccd_sensors19h(sc, model);
475b067962fSnonaka 		break;
4767d75762aSmsaitoh 	case 0x1a:
4777d75762aSmsaitoh 		nccd = amdzentemp_probe_ccd_sensors1ah(sc, model);
4787d75762aSmsaitoh 		break;
479b067962fSnonaka 	default:
480b067962fSnonaka 		return 0;
481b067962fSnonaka 	}
482b067962fSnonaka 
483b067962fSnonaka 	return nccd;
484b067962fSnonaka }
485b067962fSnonaka 
486b067962fSnonaka static void
487b067962fSnonaka amdzentemp_setup_ccd_sensors(struct amdzentemp_softc *sc)
488b067962fSnonaka {
489b067962fSnonaka 	envsys_data_t *edata;
490c9464754Snonaka 	size_t i;
491b067962fSnonaka 	uint32_t temp;
492c9464754Snonaka 	int error;
493b067962fSnonaka 
494b067962fSnonaka 	for (i = 0; i < sc->sc_numsensors - 1; i++) {
495b067962fSnonaka 		error = amdsmn_read(sc->sc_smn,
496c9f90033Smsaitoh 		    AMD_17H_CUR_TMP + sc->sc_ccd_offset + (i * sizeof(temp)),
497c9f90033Smsaitoh 		    &temp);
498b067962fSnonaka 		if (error || !ISSET(temp, AMD_17H_CCD_TMP_VALID))
499b067962fSnonaka 			continue;
500b067962fSnonaka 
501b067962fSnonaka 		edata = &sc->sc_sensor[1 + i];
502b067962fSnonaka 		edata->units = ENVSYS_STEMP;
503b067962fSnonaka 		edata->state = ENVSYS_SVALID;
504b067962fSnonaka 		edata->flags = ENVSYS_FHAS_ENTROPY;
505b067962fSnonaka 		edata->private = CCD_BASE + i;
506b067962fSnonaka 		snprintf(edata->desc, sizeof(edata->desc),
507c9464754Snonaka 		    "cpu%u ccd%zu temperature", device_unit(sc->sc_dev), i);
508b067962fSnonaka 	}
509b067962fSnonaka }
510b067962fSnonaka 
511a63689b9Spgoyette MODULE(MODULE_CLASS_DRIVER, amdzentemp, "sysmon_envsys,amdsmn");
5123a74451dSchristos 
5133a74451dSchristos #ifdef _MODULE
5143a74451dSchristos #include "ioconf.c"
5153a74451dSchristos #endif
5163a74451dSchristos 
5173a74451dSchristos static int
5183a74451dSchristos amdzentemp_modcmd(modcmd_t cmd, void *aux)
5193a74451dSchristos {
5203a74451dSchristos 	int error = 0;
5213a74451dSchristos 
5223a74451dSchristos 	switch (cmd) {
5233a74451dSchristos 	case MODULE_CMD_INIT:
5243a74451dSchristos #ifdef _MODULE
5253a74451dSchristos 		error = config_init_component(cfdriver_ioconf_amdzentemp,
5263a74451dSchristos 		    cfattach_ioconf_amdzentemp, cfdata_ioconf_amdzentemp);
5273a74451dSchristos #endif
5283a74451dSchristos 		return error;
5293a74451dSchristos 	case MODULE_CMD_FINI:
5303a74451dSchristos #ifdef _MODULE
5313a74451dSchristos 		error = config_fini_component(cfdriver_ioconf_amdzentemp,
5323a74451dSchristos 		    cfattach_ioconf_amdzentemp, cfdata_ioconf_amdzentemp);
5333a74451dSchristos #endif
5343a74451dSchristos 		return error;
5353a74451dSchristos 	default:
5363a74451dSchristos 		return ENOTTY;
5373a74451dSchristos 	}
5383a74451dSchristos }
539