amdtemp.c revision 196403
167754Smsmith/*-
267754Smsmith * Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org>
367754Smsmith * Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org>
467754Smsmith * All rights reserved.
567754Smsmith *
667754Smsmith * Redistribution and use in source and binary forms, with or without
7316303Sjkim * modification, are permitted provided that the following conditions
8316303Sjkim * are met:
9316303Sjkim * 1. Redistributions of source code must retain the above copyright
10316303Sjkim *    notice, this list of conditions and the following disclaimer.
11316303Sjkim * 2. Redistributions in binary form must reproduce the above copyright
1270243Smsmith *    notice, this list of conditions and the following disclaimer in the
1367754Smsmith *    documentation and/or other materials provided with the distribution.
14316303Sjkim *
15316303Sjkim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16316303Sjkim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17316303Sjkim * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18316303Sjkim * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
19316303Sjkim * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20316303Sjkim * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21316303Sjkim * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22316303Sjkim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
23316303Sjkim * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
24316303Sjkim * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25316303Sjkim * POSSIBILITY OF SUCH DAMAGE.
26316303Sjkim */
27316303Sjkim
28316303Sjkim/*
29316303Sjkim * Driver for the AMD K8/K10/K11 thermal sensors. Initially based on the
30316303Sjkim * k8temp Linux driver.
31316303Sjkim */
32316303Sjkim
33316303Sjkim#include <sys/cdefs.h>
34316303Sjkim__FBSDID("$FreeBSD: head/sys/dev/amdtemp/amdtemp.c 196403 2009-08-20 19:17:53Z jhb $");
35316303Sjkim
36316303Sjkim#include <sys/param.h>
37316303Sjkim#include <sys/bus.h>
38316303Sjkim#include <sys/systm.h>
39316303Sjkim#include <sys/types.h>
40316303Sjkim#include <sys/module.h>
41316303Sjkim#include <sys/conf.h>
42316303Sjkim#include <sys/kernel.h>
43316303Sjkim#include <sys/sysctl.h>
44316303Sjkim
45316303Sjkim#include <machine/specialreg.h>
46316303Sjkim#include <machine/cpufunc.h>
47316303Sjkim#include <machine/md_var.h>
48316303Sjkim
49316303Sjkim#include <dev/pci/pcireg.h>
50316303Sjkim#include <dev/pci/pcivar.h>
51316303Sjkim
52316303Sjkimtypedef enum {
53316303Sjkim	SENSOR0_CORE0,
54316303Sjkim	SENSOR0_CORE1,
55316303Sjkim	SENSOR1_CORE0,
56316303Sjkim	SENSOR1_CORE1,
57316303Sjkim	CORE0,
58316303Sjkim	CORE1
59316303Sjkim} amdsensor_t;
60316303Sjkim
61316303Sjkimstruct amdtemp_softc {
62316303Sjkim	device_t	sc_dev;
63316303Sjkim	int		sc_temps[4];
64316303Sjkim	int		sc_ntemps;
65316303Sjkim	struct sysctl_oid *sc_oid;
66316303Sjkim	struct sysctl_oid *sc_sysctl_cpu[2];
67316303Sjkim	struct intr_config_hook sc_ich;
68316303Sjkim	int32_t (*sc_gettemp)(device_t, amdsensor_t);
69316303Sjkim};
70316303Sjkim
71316303Sjkim#define VENDORID_AMD		0x1022
72316303Sjkim#define DEVICEID_AMD_MISC0F	0x1103
73316303Sjkim#define DEVICEID_AMD_MISC10	0x1203
74316303Sjkim#define DEVICEID_AMD_MISC11	0x1303
75316303Sjkim
76316303Sjkimstatic struct amdtemp_product {
77316303Sjkim	uint16_t	amdtemp_vendorid;
78316303Sjkim	uint16_t	amdtemp_deviceid;
79316303Sjkim} amdtemp_products[] = {
80316303Sjkim	{ VENDORID_AMD,	DEVICEID_AMD_MISC0F },
81316303Sjkim	{ VENDORID_AMD,	DEVICEID_AMD_MISC10 },
82316303Sjkim	{ VENDORID_AMD,	DEVICEID_AMD_MISC11 },
83316303Sjkim	{ 0, 0 }
84316303Sjkim};
85316303Sjkim
86316303Sjkim/*
87316303Sjkim * Register control (K8 family)
88316303Sjkim */
89316303Sjkim#define	AMDTEMP_REG0F		0xe4
90316303Sjkim#define	AMDTEMP_REG_SELSENSOR	0x40
91316303Sjkim#define	AMDTEMP_REG_SELCORE	0x04
92316303Sjkim
93316303Sjkim/*
94316303Sjkim * Register control (K10 & K11) family
95316303Sjkim */
96316303Sjkim#define	AMDTEMP_REG		0xa4
97316303Sjkim
98316303Sjkim#define	TZ_ZEROC		2732
99316303Sjkim
100316303Sjkim					/* -49 C is the mininum temperature */
101316303Sjkim#define	AMDTEMP_OFFSET0F	(TZ_ZEROC-490)
102316303Sjkim#define	AMDTEMP_OFFSET		(TZ_ZEROC)
103316303Sjkim
104316303Sjkim/*
105316303Sjkim * Device methods.
106316303Sjkim */
107316303Sjkimstatic void 	amdtemp_identify(driver_t *driver, device_t parent);
108316303Sjkimstatic int	amdtemp_probe(device_t dev);
109316303Sjkimstatic int	amdtemp_attach(device_t dev);
110316303Sjkimstatic void	amdtemp_intrhook(void *arg);
111316303Sjkimstatic int	amdtemp_detach(device_t dev);
112316303Sjkimstatic int 	amdtemp_match(device_t dev);
113316303Sjkimstatic int32_t	amdtemp_gettemp0f(device_t dev, amdsensor_t sensor);
114316303Sjkimstatic int32_t	amdtemp_gettemp(device_t dev, amdsensor_t sensor);
115316303Sjkimstatic int	amdtemp_sysctl(SYSCTL_HANDLER_ARGS);
116316303Sjkim
117316303Sjkimstatic device_method_t amdtemp_methods[] = {
118316303Sjkim	/* Device interface */
119217365Sjkim	DEVMETHOD(device_identify,	amdtemp_identify),
120217365Sjkim	DEVMETHOD(device_probe,		amdtemp_probe),
121217365Sjkim	DEVMETHOD(device_attach,	amdtemp_attach),
122217365Sjkim	DEVMETHOD(device_detach,	amdtemp_detach),
123217365Sjkim
124217365Sjkim	{0, 0}
125217365Sjkim};
126217365Sjkim
127217365Sjkimstatic driver_t amdtemp_driver = {
128217365Sjkim	"amdtemp",
129217365Sjkim	amdtemp_methods,
130217365Sjkim	sizeof(struct amdtemp_softc),
131217365Sjkim};
132217365Sjkim
13367754Smsmithstatic devclass_t amdtemp_devclass;
134316303SjkimDRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL);
135316303Sjkim
136316303Sjkimstatic int
137316303Sjkimamdtemp_match(device_t dev)
138316303Sjkim{
139316303Sjkim	int i;
140316303Sjkim	uint16_t vendor, devid;
141316303Sjkim
142316303Sjkim        vendor = pci_get_vendor(dev);
143316303Sjkim	devid = pci_get_device(dev);
144316303Sjkim
145316303Sjkim	for (i = 0; amdtemp_products[i].amdtemp_vendorid != 0; i++) {
146316303Sjkim		if (vendor == amdtemp_products[i].amdtemp_vendorid &&
147217365Sjkim		    devid == amdtemp_products[i].amdtemp_deviceid)
148217365Sjkim			return (1);
14967754Smsmith	}
150316303Sjkim
15167754Smsmith	return (0);
15267754Smsmith}
15367754Smsmith
15467754Smsmithstatic void
15567754Smsmithamdtemp_identify(driver_t *driver, device_t parent)
156193267Sjkim{
157193267Sjkim	device_t child;
158193267Sjkim
15967754Smsmith	/* Make sure we're not being doubly invoked. */
16067754Smsmith	if (device_find_child(parent, "amdtemp", -1) != NULL)
16182367Smsmith		return;
16267754Smsmith
16377424Smsmith	if (amdtemp_match(parent)) {
16477424Smsmith		child = device_add_child(parent, "amdtemp", -1);
16577424Smsmith		if (child == NULL)
16677424Smsmith			device_printf(parent, "add amdtemp child failed\n");
16777424Smsmith	}
16877424Smsmith
16977424Smsmith}
17077424Smsmith
17177424Smsmithstatic int
172102550Siwasakiamdtemp_probe(device_t dev)
17378986Smsmith{
174102550Siwasaki	uint32_t regs[4];
17567754Smsmith
17691116Smsmith	if (resource_disabled("amdtemp", 0))
17767754Smsmith		return (ENXIO);
17891116Smsmith
17991116Smsmith	do_cpuid(1, regs);
180193267Sjkim	switch (regs[0]) {
181193267Sjkim	case 0xf40:
182209746Sjkim	case 0xf50:
183233250Sjkim	case 0xf51:
18467754Smsmith		return (ENXIO);
185209746Sjkim	}
186102550Siwasaki	device_set_desc(dev, "AMD K8 Thermal Sensors");
187102550Siwasaki
18891116Smsmith	return (BUS_PROBE_GENERIC);
18977424Smsmith}
19091116Smsmith
19191116Smsmithstatic int
192114237Snjlamdtemp_attach(device_t dev)
19382367Smsmith{
194193267Sjkim	struct amdtemp_softc *sc = device_get_softc(dev);
19582367Smsmith	struct sysctl_ctx_list *sysctlctx;
196193267Sjkim	struct sysctl_oid *sysctlnode;
197193267Sjkim
198193267Sjkim
199200553Sjkim	/*
200285797Sjkim	 * Setup intrhook function to create dev.cpu sysctl entries. This is
201285797Sjkim	 * needed because the cpu driver may be loaded late on boot, after
20282367Smsmith	 * us.
20383174Smsmith	 */
20482367Smsmith	sc->sc_ich.ich_func = amdtemp_intrhook;
205114237Snjl	sc->sc_ich.ich_arg = dev;
20683174Smsmith	if (config_intrhook_establish(&sc->sc_ich) != 0) {
20783174Smsmith		device_printf(dev, "config_intrhook_establish "
20883174Smsmith		    "failed!\n");
20983174Smsmith		return (ENXIO);
21083174Smsmith	}
21183174Smsmith
21283174Smsmith	if (pci_get_device(dev) == DEVICEID_AMD_MISC0F)
21383174Smsmith		sc->sc_gettemp = amdtemp_gettemp0f;
21483174Smsmith	else {
21583174Smsmith		sc->sc_gettemp = amdtemp_gettemp;
21683174Smsmith		return (0);
21783174Smsmith	}
21883174Smsmith
219114237Snjl	/*
22083174Smsmith	 * dev.amdtemp.N tree.
22183174Smsmith	 */
22283174Smsmith	sysctlctx = device_get_sysctl_ctx(dev);
22382367Smsmith	sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
22483174Smsmith	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor0",
225102550Siwasaki	    CTLFLAG_RD, 0, "Sensor 0");
226102550Siwasaki
22783174Smsmith	SYSCTL_ADD_PROC(sysctlctx,
22882367Smsmith	    SYSCTL_CHILDREN(sysctlnode),
22983174Smsmith	    OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD,
23082367Smsmith	    dev, SENSOR0_CORE0, amdtemp_sysctl, "IK",
23183174Smsmith	    "Sensor 0 / Core 0 temperature");
23283174Smsmith
23383174Smsmith	SYSCTL_ADD_PROC(sysctlctx,
23483174Smsmith	    SYSCTL_CHILDREN(sysctlnode),
23583174Smsmith	    OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD,
23683174Smsmith	    dev, SENSOR0_CORE1, amdtemp_sysctl, "IK",
23799679Siwasaki	    "Sensor 0 / Core 1 temperature");
23899679Siwasaki
23999679Siwasaki	sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
24099679Siwasaki	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor1",
24199679Siwasaki	    CTLFLAG_RD, 0, "Sensor 1");
24299679Siwasaki
24399679Siwasaki	SYSCTL_ADD_PROC(sysctlctx,
24499679Siwasaki	    SYSCTL_CHILDREN(sysctlnode),
24599679Siwasaki	    OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD,
24682367Smsmith	    dev, SENSOR1_CORE0, amdtemp_sysctl, "IK",
24782367Smsmith	    "Sensor 1 / Core 0 temperature");
24882367Smsmith
249151937Sjkim	SYSCTL_ADD_PROC(sysctlctx,
25082367Smsmith	    SYSCTL_CHILDREN(sysctlnode),
251193267Sjkim	    OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD,
252193267Sjkim	    dev, SENSOR1_CORE1, amdtemp_sysctl, "IK",
253193267Sjkim	    "Sensor 1 / Core 1 temperature");
254193267Sjkim
255193267Sjkim	return (0);
256193267Sjkim}
257114237Snjl
25882367Smsmithvoid
259114237Snjlamdtemp_intrhook(void *arg)
260200553Sjkim{
261285797Sjkim	int i;
26282367Smsmith	device_t nexus, acpi, cpu;
26367754Smsmith	device_t dev = (device_t) arg;
26467754Smsmith	struct amdtemp_softc *sc;
26567754Smsmith	struct sysctl_ctx_list *sysctlctx;
266114237Snjl
26782367Smsmith	sc = device_get_softc(dev);
26882367Smsmith
26982367Smsmith	/*
27082367Smsmith	 * dev.cpu.N.temperature.
27182367Smsmith	 */
27282367Smsmith	nexus = device_find_child(root_bus, "nexus", 0);
27382367Smsmith	acpi = device_find_child(nexus, "acpi", 0);
27482367Smsmith
27582367Smsmith	for (i = 0; i < 2; i++) {
27682367Smsmith		cpu = device_find_child(acpi, "cpu",
277102550Siwasaki		    device_get_unit(dev) * 2 + i);
27882367Smsmith		if (cpu) {
27982367Smsmith			sysctlctx = device_get_sysctl_ctx(cpu);
28082367Smsmith
28182367Smsmith			sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
28282367Smsmith			    SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)),
28382367Smsmith			    OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD,
28482367Smsmith			    dev, CORE0, amdtemp_sysctl, "IK",
28582367Smsmith			    "Max of sensor 0 / 1");
28682367Smsmith		}
287193267Sjkim	}
28867754Smsmith	config_intrhook_disestablish(&sc->sc_ich);
28999679Siwasaki}
29067754Smsmith
29167754Smsmithint
29267754Smsmithamdtemp_detach(device_t dev)
293200553Sjkim{
294200553Sjkim	int i;
295114237Snjl	struct amdtemp_softc *sc = device_get_softc(dev);
29667754Smsmith
29767754Smsmith	for (i = 0; i < 2; i++) {
298285797Sjkim		if (sc->sc_sysctl_cpu[i])
299285797Sjkim			sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0);
300285797Sjkim	}
301285797Sjkim
302285797Sjkim	/* NewBus removes the dev.amdtemp.N tree by itself. */
303285797Sjkim
304285797Sjkim	return (0);
305285797Sjkim}
306285797Sjkim
307285797Sjkimstatic int
308285797Sjkimamdtemp_sysctl(SYSCTL_HANDLER_ARGS)
309285797Sjkim{
310285797Sjkim	device_t dev = (device_t) arg1;
311285797Sjkim	struct amdtemp_softc *sc = device_get_softc(dev);
312285797Sjkim	int error;
313193267Sjkim	int32_t temp, auxtemp[2];
314193267Sjkim
315218590Sjkim	switch (arg2) {
316218590Sjkim	case CORE0:
317218590Sjkim		auxtemp[0] = sc->sc_gettemp(dev, SENSOR0_CORE0);
318218590Sjkim		auxtemp[1] = sc->sc_gettemp(dev, SENSOR1_CORE0);
319193267Sjkim		temp = imax(auxtemp[0], auxtemp[1]);
320193267Sjkim		break;
321193267Sjkim	case CORE1:
322218590Sjkim		auxtemp[0] = sc->sc_gettemp(dev, SENSOR0_CORE1);
323218590Sjkim		auxtemp[1] = sc->sc_gettemp(dev, SENSOR1_CORE1);
324218590Sjkim		temp = imax(auxtemp[0], auxtemp[1]);
325218590Sjkim		break;
326193267Sjkim	default:
327218590Sjkim		temp = sc->sc_gettemp(dev, arg2);
328193267Sjkim		break;
329193267Sjkim	}
330193267Sjkim	error = sysctl_handle_int(oidp, &temp, 0, req);
331193267Sjkim
332193267Sjkim	return (error);
333193267Sjkim}
334193267Sjkim
335193267Sjkimstatic int32_t
336193267Sjkimamdtemp_gettemp0f(device_t dev, amdsensor_t sensor)
337193267Sjkim{
338193267Sjkim	uint8_t cfg;
339193267Sjkim	uint32_t temp;
340193267Sjkim
341193267Sjkim	cfg = pci_read_config(dev, AMDTEMP_REG0F, 1);
342193267Sjkim	switch (sensor) {
343193267Sjkim	case SENSOR0_CORE0:
344193267Sjkim		cfg &= ~(AMDTEMP_REG_SELSENSOR | AMDTEMP_REG_SELCORE);
345238381Sjkim		break;
346238381Sjkim	case SENSOR0_CORE1:
347204773Sjkim		cfg &= ~AMDTEMP_REG_SELSENSOR;
348193267Sjkim		cfg |= AMDTEMP_REG_SELCORE;
349193267Sjkim		break;
350193267Sjkim	case SENSOR1_CORE0:
351193267Sjkim		cfg &= ~AMDTEMP_REG_SELCORE;
352193267Sjkim		cfg |= AMDTEMP_REG_SELSENSOR;
353193267Sjkim		break;
354193267Sjkim	case SENSOR1_CORE1:
355193267Sjkim		cfg |= (AMDTEMP_REG_SELSENSOR | AMDTEMP_REG_SELCORE);
356193267Sjkim		break;
357238381Sjkim	default:
358238381Sjkim		cfg = 0;
359204773Sjkim		break;
360193267Sjkim	}
361193267Sjkim	pci_write_config(dev, AMDTEMP_REG0F, cfg, 1);
362193267Sjkim	temp = pci_read_config(dev, AMDTEMP_REG0F, 4);
363193267Sjkim	temp = ((temp >> 16) & 0xff) * 10 + AMDTEMP_OFFSET0F;
364193267Sjkim
365193267Sjkim	return (temp);
366193267Sjkim}
367193267Sjkim
368193267Sjkimstatic int32_t
369193267Sjkimamdtemp_gettemp(device_t dev, amdsensor_t sensor)
370193267Sjkim{
371193267Sjkim	uint32_t temp;
372193267Sjkim
373193267Sjkim	temp = pci_read_config(dev, AMDTEMP_REG, 4);
374193267Sjkim	temp = ((temp >> 21) & 0x3ff) * 10 / 8 + AMDTEMP_OFFSET;
375193267Sjkim
376193267Sjkim	return (temp);
377193267Sjkim}
378298714Sjkim