amdtemp.c revision 178151
1178151Srpaulo/*-
2178151Srpaulo * Copyright (c) 2008 Rui Paulo <rpaulo@FreeBSD.org>
3178151Srpaulo * All rights reserved.
4178151Srpaulo *
5178151Srpaulo * Redistribution and use in source and binary forms, with or without
6178151Srpaulo * modification, are permitted provided that the following conditions
7178151Srpaulo * are met:
8178151Srpaulo * 1. Redistributions of source code must retain the above copyright
9178151Srpaulo *    notice, this list of conditions and the following disclaimer.
10178151Srpaulo * 2. Redistributions in binary form must reproduce the above copyright
11178151Srpaulo *    notice, this list of conditions and the following disclaimer in the
12178151Srpaulo *    documentation and/or other materials provided with the distribution.
13178151Srpaulo *
14178151Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15178151Srpaulo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16178151Srpaulo * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17178151Srpaulo * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
18178151Srpaulo * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19178151Srpaulo * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20178151Srpaulo * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21178151Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22178151Srpaulo * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
23178151Srpaulo * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24178151Srpaulo * POSSIBILITY OF SUCH DAMAGE.
25178151Srpaulo *
26178151Srpaulo * $P4: //depot/user/rpaulo/k8temp/k8temp.c#8 $
27178151Srpaulo */
28178151Srpaulo
29178151Srpaulo/*
30178151Srpaulo * Driver for the AMD K8 thermal sensors. Based on a Linux driver by the
31178151Srpaulo * same name.
32178151Srpaulo */
33178151Srpaulo
34178151Srpaulo#include <sys/cdefs.h>
35178151Srpaulo__FBSDID("$FreeBSD: head/sys/dev/k8temp/k8temp.c 178151 2008-04-12 14:04:08Z rpaulo $");
36178151Srpaulo
37178151Srpaulo#include <sys/param.h>
38178151Srpaulo#include <sys/bus.h>
39178151Srpaulo#include <sys/systm.h>
40178151Srpaulo#include <sys/types.h>
41178151Srpaulo#include <sys/module.h>
42178151Srpaulo#include <sys/conf.h>
43178151Srpaulo#include <sys/kernel.h>
44178151Srpaulo#include <sys/sysctl.h>
45178151Srpaulo
46178151Srpaulo#include <machine/specialreg.h>
47178151Srpaulo#include <machine/cpufunc.h>
48178151Srpaulo#include <machine/md_var.h>
49178151Srpaulo
50178151Srpaulo#include <dev/pci/pcireg.h>
51178151Srpaulo#include <dev/pci/pcivar.h>
52178151Srpaulo
53178151Srpaulostruct k8temp_softc {
54178151Srpaulo	device_t	sc_dev;
55178151Srpaulo	int		sc_temps[4];
56178151Srpaulo	int		sc_ntemps;
57178151Srpaulo	struct sysctl_oid *sc_oid;
58178151Srpaulo	struct sysctl_oid *sc_sysctl_cpu[2];
59178151Srpaulo};
60178151Srpaulo
61178151Srpaulo#define VENDORID_AMD		0x1022
62178151Srpaulo#define DEVICEID_AMD_MISC	0x1103
63178151Srpaulo
64178151Srpaulostatic struct k8temp_product {
65178151Srpaulo	uint16_t	k8temp_vendorid;
66178151Srpaulo	uint16_t	k8temp_deviceid;
67178151Srpaulo} k8temp_products[] = {
68178151Srpaulo	{ VENDORID_AMD,	DEVICEID_AMD_MISC },
69178151Srpaulo	{ 0, 0 }
70178151Srpaulo};
71178151Srpaulo
72178151Srpaulo/*
73178151Srpaulo * Register control
74178151Srpaulo */
75178151Srpaulo#define	K8TEMP_REG		0xe4
76178151Srpaulo#define	K8TEMP_REG_SELSENSOR	0x40
77178151Srpaulo#define	K8TEMP_REG_SELCORE	0x04
78178151Srpaulo
79178151Srpaulo#define K8TEMP_MINTEMP		49	/* -49 C is the mininum temperature */
80178151Srpaulo
81178151Srpaulotypedef enum {
82178151Srpaulo	SENSOR0_CORE0,
83178151Srpaulo	SENSOR0_CORE1,
84178151Srpaulo	SENSOR1_CORE0,
85178151Srpaulo	SENSOR1_CORE1,
86178151Srpaulo	CORE0,
87178151Srpaulo	CORE1
88178151Srpaulo} k8sensor_t;
89178151Srpaulo
90178151Srpaulo/*
91178151Srpaulo * Device methods.
92178151Srpaulo */
93178151Srpaulostatic void 	k8temp_identify(driver_t *driver, device_t parent);
94178151Srpaulostatic int	k8temp_probe(device_t dev);
95178151Srpaulostatic int	k8temp_attach(device_t dev);
96178151Srpaulostatic int	k8temp_detach(device_t dev);
97178151Srpaulostatic int 	k8temp_match(device_t dev);
98178151Srpaulostatic int32_t	k8temp_gettemp(device_t dev, k8sensor_t sensor);
99178151Srpaulostatic int	k8temp_sysctl(SYSCTL_HANDLER_ARGS);
100178151Srpaulo
101178151Srpaulostatic device_method_t k8temp_methods[] = {
102178151Srpaulo	/* Device interface */
103178151Srpaulo	DEVMETHOD(device_identify,	k8temp_identify),
104178151Srpaulo	DEVMETHOD(device_probe,		k8temp_probe),
105178151Srpaulo	DEVMETHOD(device_attach,	k8temp_attach),
106178151Srpaulo	DEVMETHOD(device_detach,	k8temp_detach),
107178151Srpaulo
108178151Srpaulo	{0, 0}
109178151Srpaulo};
110178151Srpaulo
111178151Srpaulostatic driver_t k8temp_driver = {
112178151Srpaulo	"k8temp",
113178151Srpaulo	k8temp_methods,
114178151Srpaulo	sizeof(struct k8temp_softc),
115178151Srpaulo};
116178151Srpaulo
117178151Srpaulostatic devclass_t k8temp_devclass;
118178151SrpauloDRIVER_MODULE(k8temp, hostb, k8temp_driver, k8temp_devclass, NULL, NULL);
119178151Srpaulo
120178151Srpaulostatic int
121178151Srpaulok8temp_match(device_t dev)
122178151Srpaulo{
123178151Srpaulo	int i;
124178151Srpaulo	uint16_t vendor, devid;
125178151Srpaulo
126178151Srpaulo        vendor = pci_get_vendor(dev);
127178151Srpaulo	devid = pci_get_device(dev);
128178151Srpaulo
129178151Srpaulo	for (i = 0; k8temp_products[i].k8temp_vendorid != 0; i++) {
130178151Srpaulo		if (vendor == k8temp_products[i].k8temp_vendorid &&
131178151Srpaulo		    devid == k8temp_products[i].k8temp_deviceid)
132178151Srpaulo			return (1);
133178151Srpaulo	}
134178151Srpaulo
135178151Srpaulo	return (0);
136178151Srpaulo}
137178151Srpaulo
138178151Srpaulostatic void
139178151Srpaulok8temp_identify(driver_t *driver, device_t parent)
140178151Srpaulo{
141178151Srpaulo	device_t child;
142178151Srpaulo
143178151Srpaulo	/* Make sure we're not being doubly invoked. */
144178151Srpaulo	if (device_find_child(parent, "k8temp", -1) != NULL)
145178151Srpaulo		return;
146178151Srpaulo
147178151Srpaulo	if (k8temp_match(parent)) {
148178151Srpaulo		child = device_add_child(parent, "k8temp", -1);
149178151Srpaulo		if (child == NULL)
150178151Srpaulo			device_printf(parent, "add k8temp child failed\n");
151178151Srpaulo	}
152178151Srpaulo
153178151Srpaulo}
154178151Srpaulo
155178151Srpaulostatic int
156178151Srpaulok8temp_probe(device_t dev)
157178151Srpaulo{
158178151Srpaulo	uint32_t regs[4];
159178151Srpaulo
160178151Srpaulo	if (resource_disabled("k8temp", 0))
161178151Srpaulo		return (ENXIO);
162178151Srpaulo
163178151Srpaulo	do_cpuid(1, regs);
164178151Srpaulo	switch (regs[0]) {
165178151Srpaulo	case 0xf40:
166178151Srpaulo	case 0xf50:
167178151Srpaulo	case 0xf51:
168178151Srpaulo		return (ENXIO);
169178151Srpaulo	}
170178151Srpaulo	device_set_desc(dev, "AMD K8 Thermal Sensors");
171178151Srpaulo
172178151Srpaulo	return (BUS_PROBE_GENERIC);
173178151Srpaulo}
174178151Srpaulo
175178151Srpaulostatic int
176178151Srpaulok8temp_attach(device_t dev)
177178151Srpaulo{
178178151Srpaulo	device_t nexus, acpi, cpu;
179178151Srpaulo	struct k8temp_softc *sc = device_get_softc(dev);
180178151Srpaulo	int i;
181178151Srpaulo	struct sysctl_ctx_list *sysctlctx;
182178151Srpaulo	struct sysctl_oid *sysctlnode;
183178151Srpaulo
184178151Srpaulo	/*
185178151Srpaulo	 * dev.cpu.N.temperature.
186178151Srpaulo	 */
187178151Srpaulo	nexus = device_find_child(root_bus, "nexus", 0);
188178151Srpaulo	acpi = device_find_child(nexus, "acpi", 0);
189178151Srpaulo
190178151Srpaulo	for (i = 0; i < 2; i++) {
191178151Srpaulo		cpu = device_find_child(acpi, "cpu",
192178151Srpaulo		    device_get_unit(dev) * 2 + i);
193178151Srpaulo		if (cpu) {
194178151Srpaulo			sysctlctx = device_get_sysctl_ctx(cpu);
195178151Srpaulo
196178151Srpaulo			sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
197178151Srpaulo			    SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)),
198178151Srpaulo			    OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD,
199178151Srpaulo			    dev, CORE0, k8temp_sysctl, "I",
200178151Srpaulo			    "Max of sensor 0 / 1");
201178151Srpaulo		}
202178151Srpaulo	}
203178151Srpaulo
204178151Srpaulo	/*
205178151Srpaulo	 * dev.k8temp.N tree.
206178151Srpaulo	 */
207178151Srpaulo	sysctlctx = device_get_sysctl_ctx(dev);
208178151Srpaulo	sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
209178151Srpaulo	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor0",
210178151Srpaulo	    CTLFLAG_RD, 0, "Sensor 0");
211178151Srpaulo
212178151Srpaulo	SYSCTL_ADD_PROC(sysctlctx,
213178151Srpaulo	    SYSCTL_CHILDREN(sysctlnode),
214178151Srpaulo	    OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD,
215178151Srpaulo	    dev, SENSOR0_CORE0, k8temp_sysctl, "I",
216178151Srpaulo	    "Sensor 0 / Core 0 temperature");
217178151Srpaulo
218178151Srpaulo	SYSCTL_ADD_PROC(sysctlctx,
219178151Srpaulo	    SYSCTL_CHILDREN(sysctlnode),
220178151Srpaulo	    OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD,
221178151Srpaulo	    dev, SENSOR0_CORE1, k8temp_sysctl, "I",
222178151Srpaulo	    "Sensor 0 / Core 1 temperature");
223178151Srpaulo
224178151Srpaulo	sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
225178151Srpaulo	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor1",
226178151Srpaulo	    CTLFLAG_RD, 0, "Sensor 1");
227178151Srpaulo
228178151Srpaulo	SYSCTL_ADD_PROC(sysctlctx,
229178151Srpaulo	    SYSCTL_CHILDREN(sysctlnode),
230178151Srpaulo	    OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD,
231178151Srpaulo	    dev, SENSOR0_CORE0, k8temp_sysctl, "I",
232178151Srpaulo	    "Sensor 1 / Core 0 temperature");
233178151Srpaulo
234178151Srpaulo	SYSCTL_ADD_PROC(sysctlctx,
235178151Srpaulo	    SYSCTL_CHILDREN(sysctlnode),
236178151Srpaulo	    OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD,
237178151Srpaulo	    dev, SENSOR0_CORE0, k8temp_sysctl, "I",
238178151Srpaulo	    "Sensor 1 / Core 1 temperature");
239178151Srpaulo
240178151Srpaulo	return (0);
241178151Srpaulo}
242178151Srpaulo
243178151Srpauloint
244178151Srpaulok8temp_detach(device_t dev)
245178151Srpaulo{
246178151Srpaulo	int i;
247178151Srpaulo	struct k8temp_softc *sc = device_get_softc(dev);
248178151Srpaulo
249178151Srpaulo	for (i = 0; i < 2; i++) {
250178151Srpaulo		if (sc->sc_sysctl_cpu[i])
251178151Srpaulo			sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0);
252178151Srpaulo	}
253178151Srpaulo
254178151Srpaulo	/* NewBus removes the dev.k8temp.N tree by itself. */
255178151Srpaulo
256178151Srpaulo	return (0);
257178151Srpaulo}
258178151Srpaulo
259178151Srpaulostatic int
260178151Srpaulok8temp_sysctl(SYSCTL_HANDLER_ARGS)
261178151Srpaulo{
262178151Srpaulo	device_t dev = (device_t) arg1;
263178151Srpaulo	int error;
264178151Srpaulo	int32_t temp, auxtemp[2];
265178151Srpaulo
266178151Srpaulo	switch (arg2) {
267178151Srpaulo	case CORE0:
268178151Srpaulo		auxtemp[0] = k8temp_gettemp(dev, SENSOR0_CORE0);
269178151Srpaulo		auxtemp[1] = k8temp_gettemp(dev, SENSOR1_CORE0);
270178151Srpaulo		temp = max(auxtemp[0], auxtemp[1]);
271178151Srpaulo		break;
272178151Srpaulo	case CORE1:
273178151Srpaulo		auxtemp[0] = k8temp_gettemp(dev, SENSOR0_CORE1);
274178151Srpaulo		auxtemp[1] = k8temp_gettemp(dev, SENSOR1_CORE1);
275178151Srpaulo		temp = max(auxtemp[0], auxtemp[1]);
276178151Srpaulo		break;
277178151Srpaulo	default:
278178151Srpaulo		temp = k8temp_gettemp(dev, arg2);
279178151Srpaulo		break;
280178151Srpaulo	}
281178151Srpaulo	error = sysctl_handle_int(oidp, &temp, 0, req);
282178151Srpaulo
283178151Srpaulo	return (error);
284178151Srpaulo}
285178151Srpaulo
286178151Srpaulostatic int32_t
287178151Srpaulok8temp_gettemp(device_t dev, k8sensor_t sensor)
288178151Srpaulo{
289178151Srpaulo	uint8_t cfg;
290178151Srpaulo	uint32_t temp;
291178151Srpaulo
292178151Srpaulo	cfg = pci_read_config(dev, K8TEMP_REG, 1);
293178151Srpaulo	switch (sensor) {
294178151Srpaulo	case SENSOR0_CORE0:
295178151Srpaulo		cfg &= ~(K8TEMP_REG_SELSENSOR | K8TEMP_REG_SELCORE);
296178151Srpaulo		break;
297178151Srpaulo	case SENSOR0_CORE1:
298178151Srpaulo		cfg &= ~K8TEMP_REG_SELSENSOR;
299178151Srpaulo		cfg |= K8TEMP_REG_SELCORE;
300178151Srpaulo		break;
301178151Srpaulo	case SENSOR1_CORE0:
302178151Srpaulo		cfg &= ~K8TEMP_REG_SELCORE;
303178151Srpaulo		cfg |= K8TEMP_REG_SELSENSOR;
304178151Srpaulo		break;
305178151Srpaulo	case SENSOR1_CORE1:
306178151Srpaulo		cfg |= (K8TEMP_REG_SELSENSOR | K8TEMP_REG_SELCORE);
307178151Srpaulo		break;
308178151Srpaulo	default:
309178151Srpaulo		cfg = 0;
310178151Srpaulo		break;
311178151Srpaulo	}
312178151Srpaulo	pci_write_config(dev, K8TEMP_REG, cfg, 1);
313178151Srpaulo	temp = pci_read_config(dev, K8TEMP_REG, 4);
314178151Srpaulo	temp = ((temp >> 16) & 0xff) - K8TEMP_MINTEMP;
315178151Srpaulo
316178151Srpaulo	return (temp);
317178151Srpaulo}
318