amdtemp.c revision 178988
1254225Speter/*-
2254225Speter * Copyright (c) 2008 Rui Paulo <rpaulo@FreeBSD.org>
3254225Speter * All rights reserved.
4254225Speter *
5254225Speter * Redistribution and use in source and binary forms, with or without
6254225Speter * modification, are permitted provided that the following conditions
7254225Speter * are met:
8254225Speter * 1. Redistributions of source code must retain the above copyright
9254225Speter *    notice, this list of conditions and the following disclaimer.
10254225Speter * 2. Redistributions in binary form must reproduce the above copyright
11254225Speter *    notice, this list of conditions and the following disclaimer in the
12254225Speter *    documentation and/or other materials provided with the distribution.
13254225Speter *
14254225Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15254225Speter * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16254225Speter * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17254225Speter * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
18254225Speter * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19254225Speter * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20254225Speter * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21254225Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22254225Speter * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
23254225Speter * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24254225Speter * POSSIBILITY OF SUCH DAMAGE.
25254225Speter */
26254225Speter
27254225Speter/*
28254225Speter * Driver for the AMD K8 thermal sensors. Based on a Linux driver by the
29254225Speter * same name.
30254225Speter */
31254225Speter
32254225Speter#include <sys/cdefs.h>
33254225Speter__FBSDID("$FreeBSD: head/sys/dev/k8temp/k8temp.c 178988 2008-05-14 09:57:21Z rpaulo $");
34254225Speter
35254225Speter#include <sys/param.h>
36254225Speter#include <sys/bus.h>
37254225Speter#include <sys/systm.h>
38254225Speter#include <sys/types.h>
39254225Speter#include <sys/module.h>
40254225Speter#include <sys/conf.h>
41254225Speter#include <sys/kernel.h>
42254225Speter#include <sys/sysctl.h>
43254225Speter
44254225Speter#include <machine/specialreg.h>
45254225Speter#include <machine/cpufunc.h>
46254225Speter#include <machine/md_var.h>
47254225Speter
48254225Speter#include <dev/pci/pcireg.h>
49254225Speter#include <dev/pci/pcivar.h>
50254225Speter
51254225Speterstruct k8temp_softc {
52254225Speter	device_t	sc_dev;
53254225Speter	int		sc_temps[4];
54254225Speter	int		sc_ntemps;
55254225Speter	struct sysctl_oid *sc_oid;
56254225Speter	struct sysctl_oid *sc_sysctl_cpu[2];
57254225Speter};
58254225Speter
59254225Speter#define VENDORID_AMD		0x1022
60254225Speter#define DEVICEID_AMD_MISC	0x1103
61254225Speter
62254225Speterstatic struct k8temp_product {
63254225Speter	uint16_t	k8temp_vendorid;
64254225Speter	uint16_t	k8temp_deviceid;
65254225Speter} k8temp_products[] = {
66254225Speter	{ VENDORID_AMD,	DEVICEID_AMD_MISC },
67254225Speter	{ 0, 0 }
68254225Speter};
69254225Speter
70254225Speter/*
71254225Speter * Register control
72254225Speter */
73254225Speter#define	K8TEMP_REG		0xe4
74254225Speter#define	K8TEMP_REG_SELSENSOR	0x40
75254225Speter#define	K8TEMP_REG_SELCORE	0x04
76254225Speter
77254225Speter#define K8TEMP_MINTEMP		49	/* -49 C is the mininum temperature */
78254225Speter
79254225Spetertypedef enum {
80254225Speter	SENSOR0_CORE0,
81254225Speter	SENSOR0_CORE1,
82254225Speter	SENSOR1_CORE0,
83254225Speter	SENSOR1_CORE1,
84254225Speter	CORE0,
85254225Speter	CORE1
86254225Speter} k8sensor_t;
87254225Speter
88254225Speter/*
89254225Speter * Device methods.
90254225Speter */
91254225Speterstatic void 	k8temp_identify(driver_t *driver, device_t parent);
92254225Speterstatic int	k8temp_probe(device_t dev);
93254225Speterstatic int	k8temp_attach(device_t dev);
94254225Speterstatic int	k8temp_detach(device_t dev);
95254225Speterstatic int 	k8temp_match(device_t dev);
96254225Speterstatic int32_t	k8temp_gettemp(device_t dev, k8sensor_t sensor);
97254225Speterstatic int	k8temp_sysctl(SYSCTL_HANDLER_ARGS);
98254225Speter
99254225Speterstatic device_method_t k8temp_methods[] = {
100254225Speter	/* Device interface */
101254225Speter	DEVMETHOD(device_identify,	k8temp_identify),
102254225Speter	DEVMETHOD(device_probe,		k8temp_probe),
103254225Speter	DEVMETHOD(device_attach,	k8temp_attach),
104254225Speter	DEVMETHOD(device_detach,	k8temp_detach),
105254225Speter
106254225Speter	{0, 0}
107254225Speter};
108254225Speter
109254225Speterstatic driver_t k8temp_driver = {
110254225Speter	"k8temp",
111254225Speter	k8temp_methods,
112254225Speter	sizeof(struct k8temp_softc),
113254225Speter};
114254225Speter
115254225Speterstatic devclass_t k8temp_devclass;
116254225SpeterDRIVER_MODULE(k8temp, hostb, k8temp_driver, k8temp_devclass, NULL, NULL);
117254225Speter
118254225Speterstatic int
119254225Speterk8temp_match(device_t dev)
120254225Speter{
121254225Speter	int i;
122254225Speter	uint16_t vendor, devid;
123254225Speter
124254225Speter        vendor = pci_get_vendor(dev);
125254225Speter	devid = pci_get_device(dev);
126254225Speter
127254225Speter	for (i = 0; k8temp_products[i].k8temp_vendorid != 0; i++) {
128254225Speter		if (vendor == k8temp_products[i].k8temp_vendorid &&
129254225Speter		    devid == k8temp_products[i].k8temp_deviceid)
130254225Speter			return (1);
131	}
132
133	return (0);
134}
135
136static void
137k8temp_identify(driver_t *driver, device_t parent)
138{
139	device_t child;
140
141	/* Make sure we're not being doubly invoked. */
142	if (device_find_child(parent, "k8temp", -1) != NULL)
143		return;
144
145	if (k8temp_match(parent)) {
146		child = device_add_child(parent, "k8temp", -1);
147		if (child == NULL)
148			device_printf(parent, "add k8temp child failed\n");
149	}
150
151}
152
153static int
154k8temp_probe(device_t dev)
155{
156	uint32_t regs[4];
157
158	if (resource_disabled("k8temp", 0))
159		return (ENXIO);
160
161	do_cpuid(1, regs);
162	switch (regs[0]) {
163	case 0xf40:
164	case 0xf50:
165	case 0xf51:
166		return (ENXIO);
167	}
168	device_set_desc(dev, "AMD K8 Thermal Sensors");
169
170	return (BUS_PROBE_GENERIC);
171}
172
173static int
174k8temp_attach(device_t dev)
175{
176	device_t nexus, acpi, cpu;
177	struct k8temp_softc *sc = device_get_softc(dev);
178	int i;
179	struct sysctl_ctx_list *sysctlctx;
180	struct sysctl_oid *sysctlnode;
181
182	/*
183	 * dev.cpu.N.temperature.
184	 */
185	nexus = device_find_child(root_bus, "nexus", 0);
186	acpi = device_find_child(nexus, "acpi", 0);
187
188	for (i = 0; i < 2; i++) {
189		cpu = device_find_child(acpi, "cpu",
190		    device_get_unit(dev) * 2 + i);
191		if (cpu) {
192			sysctlctx = device_get_sysctl_ctx(cpu);
193
194			sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
195			    SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)),
196			    OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD,
197			    dev, CORE0, k8temp_sysctl, "I",
198			    "Max of sensor 0 / 1");
199		}
200	}
201
202	/*
203	 * dev.k8temp.N tree.
204	 */
205	sysctlctx = device_get_sysctl_ctx(dev);
206	sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
207	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor0",
208	    CTLFLAG_RD, 0, "Sensor 0");
209
210	SYSCTL_ADD_PROC(sysctlctx,
211	    SYSCTL_CHILDREN(sysctlnode),
212	    OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD,
213	    dev, SENSOR0_CORE0, k8temp_sysctl, "I",
214	    "Sensor 0 / Core 0 temperature");
215
216	SYSCTL_ADD_PROC(sysctlctx,
217	    SYSCTL_CHILDREN(sysctlnode),
218	    OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD,
219	    dev, SENSOR0_CORE1, k8temp_sysctl, "I",
220	    "Sensor 0 / Core 1 temperature");
221
222	sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
223	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor1",
224	    CTLFLAG_RD, 0, "Sensor 1");
225
226	SYSCTL_ADD_PROC(sysctlctx,
227	    SYSCTL_CHILDREN(sysctlnode),
228	    OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD,
229	    dev, SENSOR0_CORE0, k8temp_sysctl, "I",
230	    "Sensor 1 / Core 0 temperature");
231
232	SYSCTL_ADD_PROC(sysctlctx,
233	    SYSCTL_CHILDREN(sysctlnode),
234	    OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD,
235	    dev, SENSOR0_CORE0, k8temp_sysctl, "I",
236	    "Sensor 1 / Core 1 temperature");
237
238	return (0);
239}
240
241int
242k8temp_detach(device_t dev)
243{
244	int i;
245	struct k8temp_softc *sc = device_get_softc(dev);
246
247	for (i = 0; i < 2; i++) {
248		if (sc->sc_sysctl_cpu[i])
249			sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0);
250	}
251
252	/* NewBus removes the dev.k8temp.N tree by itself. */
253
254	return (0);
255}
256
257static int
258k8temp_sysctl(SYSCTL_HANDLER_ARGS)
259{
260	device_t dev = (device_t) arg1;
261	int error;
262	int32_t temp, auxtemp[2];
263
264	switch (arg2) {
265	case CORE0:
266		auxtemp[0] = k8temp_gettemp(dev, SENSOR0_CORE0);
267		auxtemp[1] = k8temp_gettemp(dev, SENSOR1_CORE0);
268		temp = imax(auxtemp[0], auxtemp[1]);
269		break;
270	case CORE1:
271		auxtemp[0] = k8temp_gettemp(dev, SENSOR0_CORE1);
272		auxtemp[1] = k8temp_gettemp(dev, SENSOR1_CORE1);
273		temp = imax(auxtemp[0], auxtemp[1]);
274		break;
275	default:
276		temp = k8temp_gettemp(dev, arg2);
277		break;
278	}
279	error = sysctl_handle_int(oidp, &temp, 0, req);
280
281	return (error);
282}
283
284static int32_t
285k8temp_gettemp(device_t dev, k8sensor_t sensor)
286{
287	uint8_t cfg;
288	uint32_t temp;
289
290	cfg = pci_read_config(dev, K8TEMP_REG, 1);
291	switch (sensor) {
292	case SENSOR0_CORE0:
293		cfg &= ~(K8TEMP_REG_SELSENSOR | K8TEMP_REG_SELCORE);
294		break;
295	case SENSOR0_CORE1:
296		cfg &= ~K8TEMP_REG_SELSENSOR;
297		cfg |= K8TEMP_REG_SELCORE;
298		break;
299	case SENSOR1_CORE0:
300		cfg &= ~K8TEMP_REG_SELCORE;
301		cfg |= K8TEMP_REG_SELSENSOR;
302		break;
303	case SENSOR1_CORE1:
304		cfg |= (K8TEMP_REG_SELSENSOR | K8TEMP_REG_SELCORE);
305		break;
306	default:
307		cfg = 0;
308		break;
309	}
310	pci_write_config(dev, K8TEMP_REG, cfg, 1);
311	temp = pci_read_config(dev, K8TEMP_REG, 4);
312	temp = ((temp >> 16) & 0xff) - K8TEMP_MINTEMP;
313
314	return (temp);
315}
316