xref: /netbsd-src/sys/arch/x86/pci/amdtemp.c (revision 2900250157ace9330b4534b5ef1da554c1403179)
1*29002501Sis /*      $NetBSD: amdtemp.c,v 1.23 2018/12/30 15:43:43 is Exp $ */
268519ac9Scegger /*      $OpenBSD: kate.c,v 1.2 2008/03/27 04:52:03 cnst Exp $   */
368519ac9Scegger 
468519ac9Scegger /*
568519ac9Scegger  * Copyright (c) 2008 The NetBSD Foundation, Inc.
668519ac9Scegger  * All rights reserved.
768519ac9Scegger  *
868519ac9Scegger  * This code is derived from software contributed to The NetBSD Foundation
968519ac9Scegger  * by Christoph Egger.
1068519ac9Scegger  *
1168519ac9Scegger  * Redistribution and use in source and binary forms, with or without
1268519ac9Scegger  * modification, are permitted provided that the following conditions
1368519ac9Scegger  * are met:
1468519ac9Scegger  * 1. Redistributions of source code must retain the above copyright
1568519ac9Scegger  *    notice, this list of conditions and the following disclaimer.
1668519ac9Scegger  * 2. Redistributions in binary form must reproduce the above copyright
1768519ac9Scegger  *    notice, this list of conditions and the following disclaimer in the
1868519ac9Scegger  *    documentation and/or other materials provided with the distribution.
1968519ac9Scegger  *
2068519ac9Scegger  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2168519ac9Scegger  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2268519ac9Scegger  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2368519ac9Scegger  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2468519ac9Scegger  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2568519ac9Scegger  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2668519ac9Scegger  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2768519ac9Scegger  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2868519ac9Scegger  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2968519ac9Scegger  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3068519ac9Scegger  * POSSIBILITY OF SUCH DAMAGE.
3168519ac9Scegger  */
3268519ac9Scegger 
3368519ac9Scegger /*
3468519ac9Scegger  * Copyright (c) 2008 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
3568519ac9Scegger  *
3668519ac9Scegger  * Permission to use, copy, modify, and distribute this software for any
3768519ac9Scegger  * purpose with or without fee is hereby granted, provided that the above
3868519ac9Scegger  * copyright notice and this permission notice appear in all copies.
3968519ac9Scegger  *
4068519ac9Scegger  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
4168519ac9Scegger  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
4268519ac9Scegger  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
4368519ac9Scegger  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
4468519ac9Scegger  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
4568519ac9Scegger  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
4668519ac9Scegger  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
4768519ac9Scegger  */
4868519ac9Scegger 
4968519ac9Scegger #include <sys/cdefs.h>
50*29002501Sis __KERNEL_RCSID(0, "$NetBSD: amdtemp.c,v 1.23 2018/12/30 15:43:43 is Exp $ ");
5168519ac9Scegger 
5268519ac9Scegger #include <sys/param.h>
53c6e5cf85Sjruoho #include <sys/bus.h>
54c6e5cf85Sjruoho #include <sys/cpu.h>
5568519ac9Scegger #include <sys/systm.h>
5668519ac9Scegger #include <sys/device.h>
5768519ac9Scegger #include <sys/kmem.h>
58c6e5cf85Sjruoho #include <sys/module.h>
5968519ac9Scegger 
6068519ac9Scegger #include <machine/specialreg.h>
6168519ac9Scegger 
6268519ac9Scegger #include <dev/pci/pcireg.h>
6368519ac9Scegger #include <dev/pci/pcivar.h>
6468519ac9Scegger #include <dev/pci/pcidevs.h>
6568519ac9Scegger 
66c6e5cf85Sjruoho #include <dev/sysmon/sysmonvar.h>
67c6e5cf85Sjruoho 
6868519ac9Scegger /*
6968519ac9Scegger  * AMD K8:
7068519ac9Scegger  * http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/32559.pdf
7118130206Scegger  * AMD K8 Errata: #141
7218130206Scegger  * http://support.amd.com/us/Processor_TechDocs/33610_PUB_Rev3%2042v3.pdf
7318130206Scegger  *
7468519ac9Scegger  * Family10h:
7568519ac9Scegger  * http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/31116.PDF
76f94b5172Scegger  * Family10h Errata: #319
77f94b5172Scegger  * http://support.amd.com/de/Processor_TechDocs/41322.pdf
7818130206Scegger  *
7918130206Scegger  * Family11h:
8018130206Scegger  * http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/41256.pdf
8168519ac9Scegger  */
8268519ac9Scegger 
832e5784d8Sjmcneill /* AMD Processors, Function 3 -- Miscellaneous Control
8468519ac9Scegger  */
8568519ac9Scegger 
8668519ac9Scegger /* Function 3 Registers */
8768519ac9Scegger #define THERMTRIP_STAT_R      0xe4
8868519ac9Scegger #define NORTHBRIDGE_CAP_R     0xe8
8968519ac9Scegger #define CPUID_FAMILY_MODEL_R  0xfc
9068519ac9Scegger 
9168519ac9Scegger /*
9268519ac9Scegger  * AMD NPT Family 0Fh Processors, Function 3 -- Miscellaneous Control
9368519ac9Scegger  */
9468519ac9Scegger 
9568519ac9Scegger /* Bits within Thermtrip Status Register */
9668519ac9Scegger #define K8_THERM_SENSE_SEL       (1 << 6)
9768519ac9Scegger #define K8_THERM_SENSE_CORE_SEL  (1 << 2)
9868519ac9Scegger 
9968519ac9Scegger /* Flip core and sensor selection bits */
10068519ac9Scegger #define K8_T_SEL_C0(v)           (v |= K8_THERM_SENSE_CORE_SEL)
10168519ac9Scegger #define K8_T_SEL_C1(v)           (v &= ~(K8_THERM_SENSE_CORE_SEL))
10268519ac9Scegger #define K8_T_SEL_S0(v)           (v &= ~(K8_THERM_SENSE_SEL))
10368519ac9Scegger #define K8_T_SEL_S1(v)           (v |= K8_THERM_SENSE_SEL)
10468519ac9Scegger 
10568519ac9Scegger /*
1062e5784d8Sjmcneill  * AMD Family 10h Processors, Function 3 -- Miscellaneous Control
10768519ac9Scegger  */
10868519ac9Scegger 
10968519ac9Scegger /* Function 3 Registers */
11068519ac9Scegger #define F10_TEMPERATURE_CTL_R	0xa4
111124c8cd2Smaxv #define 	F10_TEMP_CURTMP		__BITS(31,21)
11268519ac9Scegger 
11368519ac9Scegger /*
11468519ac9Scegger  * Revision Guide for AMD NPT Family 0Fh Processors,
11568519ac9Scegger  * Publication # 33610, Revision 3.30, February 2008
11668519ac9Scegger  */
11772f1a5aaScegger #define K8_SOCKET_F	1	/* Server */
11872f1a5aaScegger #define K8_SOCKET_AM2	2	/* Desktop */
11972f1a5aaScegger #define K8_SOCKET_S1	3	/* Laptop */
12072f1a5aaScegger 
12168519ac9Scegger static const struct {
12268519ac9Scegger 	const char rev[5];
12372f1a5aaScegger 	const struct {
12472f1a5aaScegger 		const pcireg_t cpuid;
12572f1a5aaScegger 		const uint8_t socket;
12672f1a5aaScegger 	} cpu[5];
12768519ac9Scegger } amdtemp_core[] = {
12872f1a5aaScegger 	{ "BH-F", { { 0x00040FB0, K8_SOCKET_AM2 },	/* F2 */
12972f1a5aaScegger 		  { 0x00040F80, K8_SOCKET_S1 },		/* F2 */
13072f1a5aaScegger 		  { 0, 0 }, { 0, 0 }, { 0, 0 } } },
13172f1a5aaScegger 	{ "DH-F", { { 0x00040FF0, K8_SOCKET_AM2 },	/* F2 */
13272f1a5aaScegger 		  { 0x00040FC0, K8_SOCKET_S1 },		/* F2 */
13372f1a5aaScegger 		  { 0x00050FF0, K8_SOCKET_AM2 },	/* F2, F3 */
13472f1a5aaScegger 		  { 0, 0 }, { 0, 0 } } },
13572f1a5aaScegger 	{ "JH-F", { { 0x00040F10, K8_SOCKET_F },	/* F2, F3 */
13672f1a5aaScegger 		  { 0x00040F30, K8_SOCKET_AM2 },	/* F2, F3 */
13772f1a5aaScegger 		  { 0x000C0F10, K8_SOCKET_F },		/* F3 */
13872f1a5aaScegger 		  { 0, 0 }, { 0, 0 } } },
13972f1a5aaScegger 	{ "BH-G", { { 0x00060FB0, K8_SOCKET_AM2 },	/* G1, G2 */
14072f1a5aaScegger 		  { 0x00060F80, K8_SOCKET_S1 },		/* G1, G2 */
14172f1a5aaScegger 		  { 0, 0 }, { 0, 0 }, { 0, 0 } } },
14272f1a5aaScegger 	{ "DH-G", { { 0x00060FF0, K8_SOCKET_AM2 },	/* G1, G2 */
14372f1a5aaScegger 		  { 0x00060FC0, K8_SOCKET_S1 },		/* G2 */
14472f1a5aaScegger 		  { 0x00070FF0, K8_SOCKET_AM2 },	/* G1, G2 */
14572f1a5aaScegger 		  { 0x00070FC0, K8_SOCKET_S1 },		/* G2 */
14672f1a5aaScegger 		  { 0, 0 } } }
14768519ac9Scegger };
14868519ac9Scegger 
14968519ac9Scegger struct amdtemp_softc {
15068519ac9Scegger 	pci_chipset_tag_t sc_pc;
15168519ac9Scegger 	pcitag_t sc_pcitag;
15268519ac9Scegger 
15368519ac9Scegger 	struct sysmon_envsys *sc_sme;
15468519ac9Scegger 	envsys_data_t *sc_sensor;
155c6e5cf85Sjruoho 	size_t sc_sensor_len;
15668519ac9Scegger 
15768519ac9Scegger 	char sc_rev;
15868519ac9Scegger 	int8_t sc_numsensors;
15968519ac9Scegger 	uint32_t sc_family;
16072f1a5aaScegger 	int32_t sc_adjustment;
16168519ac9Scegger };
16268519ac9Scegger 
16368519ac9Scegger static int  amdtemp_match(device_t, cfdata_t, void *);
16468519ac9Scegger static void amdtemp_attach(device_t, device_t, void *);
165c6e5cf85Sjruoho static int  amdtemp_detach(device_t, int);
16668519ac9Scegger 
16768519ac9Scegger static void amdtemp_k8_init(struct amdtemp_softc *, pcireg_t);
16868519ac9Scegger static void amdtemp_k8_setup_sensors(struct amdtemp_softc *, int);
16968519ac9Scegger static void amdtemp_k8_refresh(struct sysmon_envsys *, envsys_data_t *);
17068519ac9Scegger 
17168519ac9Scegger static void amdtemp_family10_init(struct amdtemp_softc *);
17268519ac9Scegger static void amdtemp_family10_setup_sensors(struct amdtemp_softc *, int);
17368519ac9Scegger static void amdtemp_family10_refresh(struct sysmon_envsys *, envsys_data_t *);
17468519ac9Scegger 
17568519ac9Scegger CFATTACH_DECL_NEW(amdtemp, sizeof(struct amdtemp_softc),
176c6e5cf85Sjruoho     amdtemp_match, amdtemp_attach, amdtemp_detach, NULL);
17768519ac9Scegger 
17868519ac9Scegger static int
amdtemp_match(device_t parent,cfdata_t match,void * aux)17968519ac9Scegger amdtemp_match(device_t parent, cfdata_t match, void *aux)
18068519ac9Scegger {
18168519ac9Scegger 	struct pci_attach_args *pa = aux;
18268519ac9Scegger 	pcireg_t cpu_signature;
18368519ac9Scegger 	uint32_t family;
18468519ac9Scegger 
185c42e0053Scegger 	KASSERT(PCI_VENDOR(pa->pa_id) == PCI_VENDOR_AMD);
18668519ac9Scegger 
1873bd1efebSjruoho 	cpu_signature = pci_conf_read(pa->pa_pc,
1883bd1efebSjruoho 	    pa->pa_tag, CPUID_FAMILY_MODEL_R);
18968519ac9Scegger 
190124c8cd2Smaxv 	/*
191124c8cd2Smaxv 	 * This CPUID northbridge register has been introduced in
192124c8cd2Smaxv 	 * Revision F.
193124c8cd2Smaxv 	 */
19468519ac9Scegger 	if (cpu_signature == 0x0)
19568519ac9Scegger 		return 0;
19668519ac9Scegger 
197b1a32cacSmsaitoh 	family = CPUID_TO_FAMILY(cpu_signature);
19868519ac9Scegger 
199f94b5172Scegger 	/* Errata #319: This has been fixed in Revision C2. */
200f94b5172Scegger 	if (family == 0x10) {
201b1a32cacSmsaitoh 		if (CPUID_TO_BASEMODEL(cpu_signature) < 4)
202f94b5172Scegger 			return 0;
203124c8cd2Smaxv 		if (CPUID_TO_BASEMODEL(cpu_signature) == 4 &&
204124c8cd2Smaxv 		    CPUID_TO_STEPPING(cpu_signature) < 2)
205f94b5172Scegger 			return 0;
206f94b5172Scegger 	}
207f94b5172Scegger 
208124c8cd2Smaxv 	/* Not yet supported CPUs. */
209867d63b1Sis 	if (family > 0x16)
21068519ac9Scegger 		return 0;
21168519ac9Scegger 
212c42e0053Scegger 	return 1;
21368519ac9Scegger }
21468519ac9Scegger 
21568519ac9Scegger static void
amdtemp_attach(device_t parent,device_t self,void * aux)21668519ac9Scegger amdtemp_attach(device_t parent, device_t self, void *aux)
21768519ac9Scegger {
21868519ac9Scegger 	struct amdtemp_softc *sc = device_private(self);
21968519ac9Scegger 	struct pci_attach_args *pa = aux;
22068519ac9Scegger 	pcireg_t cpu_signature;
22168519ac9Scegger 	int error;
22268519ac9Scegger 	uint8_t i;
22368519ac9Scegger 
22468519ac9Scegger 	aprint_naive("\n");
225960d9f28Scegger 	aprint_normal(": AMD CPU Temperature Sensors");
22668519ac9Scegger 
2273bd1efebSjruoho 	cpu_signature = pci_conf_read(pa->pa_pc,
2283bd1efebSjruoho 	    pa->pa_tag, CPUID_FAMILY_MODEL_R);
22968519ac9Scegger 
23068519ac9Scegger 	/* If we hit this, then match routine is wrong. */
23168519ac9Scegger 	KASSERT(cpu_signature != 0x0);
23268519ac9Scegger 
233b1a32cacSmsaitoh 	sc->sc_family = CPUID_TO_FAMILY(cpu_signature);
2343bd1efebSjruoho 
23568519ac9Scegger 	KASSERT(sc->sc_family >= 0xf);
23668519ac9Scegger 
2373bd1efebSjruoho 	sc->sc_sme = NULL;
2383bd1efebSjruoho 	sc->sc_sensor = NULL;
2393bd1efebSjruoho 
24068519ac9Scegger 	sc->sc_pc = pa->pa_pc;
24168519ac9Scegger 	sc->sc_pcitag = pa->pa_tag;
24272f1a5aaScegger 	sc->sc_adjustment = 0;
24368519ac9Scegger 
24468519ac9Scegger 	switch (sc->sc_family) {
24568519ac9Scegger 	case 0xf:  /* AMD K8 NPT */
24668519ac9Scegger 		amdtemp_k8_init(sc, cpu_signature);
24768519ac9Scegger 		break;
24868519ac9Scegger 
24968519ac9Scegger 	case 0x10: /* AMD Barcelona/Phenom */
25068519ac9Scegger 	case 0x11: /* AMD Griffin */
251f34c8606Snonaka 	case 0x12: /* AMD Lynx/Sabine (Llano) */
252f34c8606Snonaka 	case 0x14: /* AMD Brazos (Ontario/Zacate/Desna) */
253*29002501Sis 	case 0x15: /* AMD Bobcat */
254*29002501Sis 	case 0x16: /* AMD Puma/Jaguar */
25568519ac9Scegger 		amdtemp_family10_init(sc);
25668519ac9Scegger 		break;
25768519ac9Scegger 
25868519ac9Scegger 	default:
259960d9f28Scegger 		aprint_normal(", family 0x%x not supported\n",
260960d9f28Scegger 		    sc->sc_family);
26168519ac9Scegger 		return;
26268519ac9Scegger 	}
26368519ac9Scegger 
26468519ac9Scegger 	aprint_normal("\n");
26568519ac9Scegger 
26672f1a5aaScegger 	if (sc->sc_adjustment != 0)
26772f1a5aaScegger 		aprint_debug_dev(self, "Workaround enabled\n");
26872f1a5aaScegger 
26968519ac9Scegger 	sc->sc_sme = sysmon_envsys_create();
270c6e5cf85Sjruoho 	sc->sc_sensor_len = sizeof(envsys_data_t) * sc->sc_numsensors;
2713bd1efebSjruoho 	sc->sc_sensor = kmem_zalloc(sc->sc_sensor_len, KM_SLEEP);
2723bd1efebSjruoho 
27368519ac9Scegger 	switch (sc->sc_family) {
27468519ac9Scegger 	case 0xf:
27568519ac9Scegger 		amdtemp_k8_setup_sensors(sc, device_unit(self));
27668519ac9Scegger 		break;
27768519ac9Scegger 	case 0x10:
27868519ac9Scegger 	case 0x11:
279f34c8606Snonaka 	case 0x12:
2802e5784d8Sjmcneill 	case 0x14:
2815828a00eScegger 	case 0x15:
282867d63b1Sis 	case 0x16:
28368519ac9Scegger 		amdtemp_family10_setup_sensors(sc, device_unit(self));
28468519ac9Scegger 		break;
28568519ac9Scegger 	}
28668519ac9Scegger 
28768519ac9Scegger 	/*
28868519ac9Scegger 	 * Set properties in sensors.
28968519ac9Scegger 	 */
29068519ac9Scegger 	for (i = 0; i < sc->sc_numsensors; i++) {
291124c8cd2Smaxv 		if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[i]))
29268519ac9Scegger 			goto bad;
29368519ac9Scegger 	}
29468519ac9Scegger 
29568519ac9Scegger 	/*
29668519ac9Scegger 	 * Register the sysmon_envsys device.
29768519ac9Scegger 	 */
29868519ac9Scegger 	sc->sc_sme->sme_name = device_xname(self);
29968519ac9Scegger 	sc->sc_sme->sme_cookie = sc;
30068519ac9Scegger 
30168519ac9Scegger 	switch (sc->sc_family) {
30268519ac9Scegger 	case 0xf:
30368519ac9Scegger 		sc->sc_sme->sme_refresh = amdtemp_k8_refresh;
30468519ac9Scegger 		break;
30568519ac9Scegger 	case 0x10:
30668519ac9Scegger 	case 0x11:
307f34c8606Snonaka 	case 0x12:
3082e5784d8Sjmcneill 	case 0x14:
3095828a00eScegger 	case 0x15:
310867d63b1Sis 	case 0x16:
31168519ac9Scegger 		sc->sc_sme->sme_refresh = amdtemp_family10_refresh;
31268519ac9Scegger 		break;
31368519ac9Scegger 	}
31468519ac9Scegger 
31568519ac9Scegger 	error = sysmon_envsys_register(sc->sc_sme);
31668519ac9Scegger 	if (error) {
31768519ac9Scegger 		aprint_error_dev(self, "unable to register with sysmon "
31868519ac9Scegger 			"(error=%d)\n", error);
31968519ac9Scegger 		goto bad;
32068519ac9Scegger 	}
32168519ac9Scegger 
3223bd1efebSjruoho 	(void)pmf_device_register(self, NULL, NULL);
32368519ac9Scegger 
32468519ac9Scegger 	return;
32568519ac9Scegger 
32668519ac9Scegger bad:
3273bd1efebSjruoho 	if (sc->sc_sme != NULL) {
32868519ac9Scegger 		sysmon_envsys_destroy(sc->sc_sme);
329c6e5cf85Sjruoho 		sc->sc_sme = NULL;
330c6e5cf85Sjruoho 	}
331c6e5cf85Sjruoho 
3323bd1efebSjruoho 	if (sc->sc_sensor != NULL) {
3333bd1efebSjruoho 		kmem_free(sc->sc_sensor, sc->sc_sensor_len);
3343bd1efebSjruoho 		sc->sc_sensor = NULL;
3353bd1efebSjruoho 	}
3363bd1efebSjruoho }
3373bd1efebSjruoho 
338c6e5cf85Sjruoho static int
amdtemp_detach(device_t self,int flags)339c6e5cf85Sjruoho amdtemp_detach(device_t self, int flags)
340c6e5cf85Sjruoho {
341c6e5cf85Sjruoho 	struct amdtemp_softc *sc = device_private(self);
342c6e5cf85Sjruoho 
3435828a00eScegger 	pmf_device_deregister(self);
344c6e5cf85Sjruoho 	if (sc->sc_sme != NULL)
345c6e5cf85Sjruoho 		sysmon_envsys_unregister(sc->sc_sme);
346c6e5cf85Sjruoho 
347c6e5cf85Sjruoho 	if (sc->sc_sensor != NULL)
348c6e5cf85Sjruoho 		kmem_free(sc->sc_sensor, sc->sc_sensor_len);
349c6e5cf85Sjruoho 
350c6e5cf85Sjruoho 	return 0;
35168519ac9Scegger }
35268519ac9Scegger 
35368519ac9Scegger static void
amdtemp_k8_init(struct amdtemp_softc * sc,pcireg_t cpu_signature)35468519ac9Scegger amdtemp_k8_init(struct amdtemp_softc *sc, pcireg_t cpu_signature)
35568519ac9Scegger {
35668519ac9Scegger 	pcireg_t data;
35768519ac9Scegger 	uint32_t cmpcap;
35868519ac9Scegger 	uint8_t i, j;
35968519ac9Scegger 
36068519ac9Scegger 	aprint_normal(" (K8");
36168519ac9Scegger 
36268519ac9Scegger 	for (i = 0; i < __arraycount(amdtemp_core) && sc->sc_rev == '\0'; i++) {
36372f1a5aaScegger 		for (j = 0; amdtemp_core[i].cpu[j].cpuid != 0; j++) {
36468519ac9Scegger 			if ((cpu_signature & ~0xf)
36572f1a5aaScegger 			    != amdtemp_core[i].cpu[j].cpuid)
36672f1a5aaScegger 				continue;
36772f1a5aaScegger 
36868519ac9Scegger 			sc->sc_rev = amdtemp_core[i].rev[3];
36968519ac9Scegger 			aprint_normal(": core rev %.4s%.1x",
37068519ac9Scegger 			    amdtemp_core[i].rev,
371b1a32cacSmsaitoh 			    CPUID_TO_STEPPING(cpu_signature));
37272f1a5aaScegger 
37372f1a5aaScegger 			switch (amdtemp_core[i].cpu[j].socket) {
37472f1a5aaScegger 			case K8_SOCKET_AM2:
3759db96566Scegger 				if (sc->sc_rev == 'G')
37672f1a5aaScegger 					sc->sc_adjustment = 21000000;
37772f1a5aaScegger 				aprint_normal(", socket AM2");
37872f1a5aaScegger 				break;
37972f1a5aaScegger 			case K8_SOCKET_S1:
38072f1a5aaScegger 				aprint_normal(", socket S1");
38172f1a5aaScegger 				break;
38272f1a5aaScegger 			case K8_SOCKET_F:
38372f1a5aaScegger 				aprint_normal(", socket F");
38472f1a5aaScegger 				break;
38568519ac9Scegger 			}
38668519ac9Scegger 		}
38768519ac9Scegger 	}
38868519ac9Scegger 
38968519ac9Scegger 	if (sc->sc_rev == '\0') {
390124c8cd2Smaxv 		/*
391124c8cd2Smaxv 		 * CPUID Family Model Register was introduced in
392124c8cd2Smaxv 		 * Revision F
393124c8cd2Smaxv 		 */
39468519ac9Scegger 		sc->sc_rev = 'G';	/* newer than E, assume G */
39568519ac9Scegger 		aprint_normal(": cpuid 0x%x", cpu_signature);
39668519ac9Scegger 	}
39768519ac9Scegger 
39868519ac9Scegger 	aprint_normal(")");
39968519ac9Scegger 
40068519ac9Scegger 	data = pci_conf_read(sc->sc_pc, sc->sc_pcitag, NORTHBRIDGE_CAP_R);
40168519ac9Scegger 	cmpcap = (data >> 12) & 0x3;
40268519ac9Scegger 
40368519ac9Scegger 	sc->sc_numsensors = cmpcap ? 4 : 2;
40468519ac9Scegger }
40568519ac9Scegger 
40668519ac9Scegger static void
amdtemp_k8_setup_sensors(struct amdtemp_softc * sc,int dv_unit)40768519ac9Scegger amdtemp_k8_setup_sensors(struct amdtemp_softc *sc, int dv_unit)
40868519ac9Scegger {
40968519ac9Scegger 	uint8_t i;
41068519ac9Scegger 
411124c8cd2Smaxv 	/*
412124c8cd2Smaxv 	 * There are two sensors per CPU core. So we use the device unit as
413124c8cd2Smaxv 	 * socket counter to correctly enumerate the CPUs on multi-socket
414124c8cd2Smaxv 	 * machines.
41568519ac9Scegger 	 */
41668519ac9Scegger 	dv_unit *= (sc->sc_numsensors / 2);
41768519ac9Scegger 	for (i = 0; i < sc->sc_numsensors; i++) {
41868519ac9Scegger 		sc->sc_sensor[i].units = ENVSYS_STEMP;
41968519ac9Scegger 		sc->sc_sensor[i].state = ENVSYS_SVALID;
42098b25da6Spgoyette 		sc->sc_sensor[i].flags = ENVSYS_FHAS_ENTROPY;
42168519ac9Scegger 
42268519ac9Scegger 		snprintf(sc->sc_sensor[i].desc, sizeof(sc->sc_sensor[i].desc),
42368519ac9Scegger 		    "CPU%u Sensor%u", dv_unit + (i / 2), i % 2);
42468519ac9Scegger 	}
42568519ac9Scegger }
42668519ac9Scegger 
42768519ac9Scegger static void
amdtemp_k8_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)42868519ac9Scegger amdtemp_k8_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
42968519ac9Scegger {
43068519ac9Scegger 	struct amdtemp_softc *sc = sme->sme_cookie;
43168519ac9Scegger 	pcireg_t status, match, tmp;
43268519ac9Scegger 	uint32_t value;
43368519ac9Scegger 
43468519ac9Scegger 	status = pci_conf_read(sc->sc_pc, sc->sc_pcitag, THERMTRIP_STAT_R);
43568519ac9Scegger 
43668519ac9Scegger 	switch (edata->sensor) { /* sensor number */
43768519ac9Scegger 	case 0: /* Core 0 Sensor 0 */
43868519ac9Scegger 		K8_T_SEL_C0(status);
43968519ac9Scegger 		K8_T_SEL_S0(status);
44068519ac9Scegger 		break;
44168519ac9Scegger 	case 1: /* Core 0 Sensor 1 */
44268519ac9Scegger 		K8_T_SEL_C0(status);
44368519ac9Scegger 		K8_T_SEL_S1(status);
44468519ac9Scegger 		break;
44568519ac9Scegger 	case 2: /* Core 1 Sensor 0 */
44668519ac9Scegger 		K8_T_SEL_C1(status);
44768519ac9Scegger 		K8_T_SEL_S0(status);
44868519ac9Scegger 		break;
44968519ac9Scegger 	case 3: /* Core 1 Sensor 1 */
45068519ac9Scegger 		K8_T_SEL_C1(status);
45168519ac9Scegger 		K8_T_SEL_S1(status);
45268519ac9Scegger 		break;
45368519ac9Scegger 	}
45468519ac9Scegger 
45568519ac9Scegger 	match = status & (K8_THERM_SENSE_CORE_SEL | K8_THERM_SENSE_SEL);
45668519ac9Scegger 	pci_conf_write(sc->sc_pc, sc->sc_pcitag, THERMTRIP_STAT_R, status);
45768519ac9Scegger 	status = pci_conf_read(sc->sc_pc, sc->sc_pcitag, THERMTRIP_STAT_R);
45868519ac9Scegger 	tmp = status & (K8_THERM_SENSE_CORE_SEL | K8_THERM_SENSE_SEL);
45968519ac9Scegger 
46068519ac9Scegger 	value = 0x3ff & (status >> 14);
46168519ac9Scegger 	if (sc->sc_rev != 'G')
46268519ac9Scegger 		value &= ~0x3;
46368519ac9Scegger 
46468519ac9Scegger 	edata->state = ENVSYS_SINVALID;
46568519ac9Scegger 	if ((tmp == match) && ((value & ~0x3) != 0)) {
46668519ac9Scegger 		edata->state = ENVSYS_SVALID;
467124c8cd2Smaxv 		edata->value_cur = (value * 250000 - 49000000) + 273150000 +
468124c8cd2Smaxv 		    sc->sc_adjustment;
46968519ac9Scegger 	}
47068519ac9Scegger }
47168519ac9Scegger 
47268519ac9Scegger static void
amdtemp_family10_init(struct amdtemp_softc * sc)47368519ac9Scegger amdtemp_family10_init(struct amdtemp_softc *sc)
47468519ac9Scegger {
4752e5784d8Sjmcneill 	aprint_normal(" (Family%02xh)", sc->sc_family);
47668519ac9Scegger 
47768519ac9Scegger 	sc->sc_numsensors = 1;
47868519ac9Scegger }
47968519ac9Scegger 
48068519ac9Scegger static void
amdtemp_family10_setup_sensors(struct amdtemp_softc * sc,int dv_unit)48168519ac9Scegger amdtemp_family10_setup_sensors(struct amdtemp_softc *sc, int dv_unit)
48268519ac9Scegger {
48368519ac9Scegger 	/* sanity check for future enhancements */
48468519ac9Scegger 	KASSERT(sc->sc_numsensors == 1);
48568519ac9Scegger 
486124c8cd2Smaxv 	/*
487124c8cd2Smaxv 	 * There's one sensor per memory controller (= socket), so we use the
488124c8cd2Smaxv 	 * device unit as socket counter to correctly enumerate the CPUs.
48968519ac9Scegger 	 */
49068519ac9Scegger 	sc->sc_sensor[0].units = ENVSYS_STEMP;
49168519ac9Scegger 	sc->sc_sensor[0].state = ENVSYS_SVALID;
49298b25da6Spgoyette 	sc->sc_sensor[0].flags = ENVSYS_FHAS_ENTROPY;
49368519ac9Scegger 
49468519ac9Scegger 	snprintf(sc->sc_sensor[0].desc, sizeof(sc->sc_sensor[0].desc),
4952e5784d8Sjmcneill 	    "cpu%u temperature", dv_unit);
49668519ac9Scegger }
49768519ac9Scegger 
49868519ac9Scegger static void
amdtemp_family10_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)49968519ac9Scegger amdtemp_family10_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
50068519ac9Scegger {
50168519ac9Scegger 	struct amdtemp_softc *sc = sme->sme_cookie;
50268519ac9Scegger 	pcireg_t status;
50368519ac9Scegger 	uint32_t value;
50468519ac9Scegger 
505124c8cd2Smaxv 	status = pci_conf_read(sc->sc_pc, sc->sc_pcitag,
506124c8cd2Smaxv 	    F10_TEMPERATURE_CTL_R);
507124c8cd2Smaxv 	value = __SHIFTOUT(status, F10_TEMP_CURTMP);
50868519ac9Scegger 
509124c8cd2Smaxv 	/* From Celsius to micro-Kelvin. */
510124c8cd2Smaxv 	edata->value_cur = (value * 125000) + 273150000;
51168519ac9Scegger 	edata->state = ENVSYS_SVALID;
51268519ac9Scegger }
513c6e5cf85Sjruoho 
51476a2f91aSpgoyette MODULE(MODULE_CLASS_DRIVER, amdtemp, "sysmon_envsys");
515c6e5cf85Sjruoho 
516c6e5cf85Sjruoho #ifdef _MODULE
517c6e5cf85Sjruoho #include "ioconf.c"
518c6e5cf85Sjruoho #endif
519c6e5cf85Sjruoho 
520c6e5cf85Sjruoho static int
amdtemp_modcmd(modcmd_t cmd,void * aux)521c6e5cf85Sjruoho amdtemp_modcmd(modcmd_t cmd, void *aux)
522c6e5cf85Sjruoho {
523c6e5cf85Sjruoho 	int error = 0;
524c6e5cf85Sjruoho 
525c6e5cf85Sjruoho 	switch (cmd) {
526c6e5cf85Sjruoho 	case MODULE_CMD_INIT:
527c6e5cf85Sjruoho #ifdef _MODULE
528c6e5cf85Sjruoho 		error = config_init_component(cfdriver_ioconf_amdtemp,
529c6e5cf85Sjruoho 		    cfattach_ioconf_amdtemp, cfdata_ioconf_amdtemp);
530c6e5cf85Sjruoho #endif
531c6e5cf85Sjruoho 		return error;
532c6e5cf85Sjruoho 	case MODULE_CMD_FINI:
533c6e5cf85Sjruoho #ifdef _MODULE
534c6e5cf85Sjruoho 		error = config_fini_component(cfdriver_ioconf_amdtemp,
535c6e5cf85Sjruoho 		    cfattach_ioconf_amdtemp, cfdata_ioconf_amdtemp);
536c6e5cf85Sjruoho #endif
537c6e5cf85Sjruoho 		return error;
538c6e5cf85Sjruoho 	default:
539c6e5cf85Sjruoho 		return ENOTTY;
540c6e5cf85Sjruoho 	}
541c6e5cf85Sjruoho }
542