Deleted Added
full compact
amdtemp.c (241856) amdtemp.c (241885)
1/*-
2 * Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org>
3 * Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org>
4 * Copyright (c) 2009-2012 Jung-uk Kim <jkim@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/*
30 * Driver for the AMD CPU on-die thermal sensors.
31 * Initially based on the k8temp Linux driver.
32 */
33
34#include <sys/cdefs.h>
1/*-
2 * Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org>
3 * Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org>
4 * Copyright (c) 2009-2012 Jung-uk Kim <jkim@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/*
30 * Driver for the AMD CPU on-die thermal sensors.
31 * Initially based on the k8temp Linux driver.
32 */
33
34#include <sys/cdefs.h>
35__FBSDID("$FreeBSD: head/sys/dev/amdtemp/amdtemp.c 241856 2012-10-22 03:41:14Z eadler $");
35__FBSDID("$FreeBSD: head/sys/dev/amdtemp/amdtemp.c 241885 2012-10-22 13:06:09Z eadler $");
36
37#include <sys/param.h>
38#include <sys/bus.h>
39#include <sys/conf.h>
40#include <sys/kernel.h>
41#include <sys/module.h>
42#include <sys/sysctl.h>
43#include <sys/systm.h>
44
45#include <machine/cpufunc.h>
46#include <machine/md_var.h>
47#include <machine/specialreg.h>
48
49#include <dev/pci/pcivar.h>
50#include <x86/pci_cfgreg.h>
51
52typedef enum {
53 CORE0_SENSOR0,
54 CORE0_SENSOR1,
55 CORE1_SENSOR0,
56 CORE1_SENSOR1,
57 CORE0,
58 CORE1
59} amdsensor_t;
60
61struct amdtemp_softc {
62 device_t sc_dev;
63 int sc_ncores;
64 int sc_ntemps;
65 int sc_flags;
66#define AMDTEMP_FLAG_CS_SWAP 0x01 /* ThermSenseCoreSel is inverted. */
67#define AMDTEMP_FLAG_CT_10BIT 0x02 /* CurTmp is 10-bit wide. */
68#define AMDTEMP_FLAG_ALT_OFFSET 0x04 /* CurTmp starts at -28C. */
69 int32_t sc_offset;
70 int32_t (*sc_gettemp)(device_t, amdsensor_t);
71 struct sysctl_oid *sc_sysctl_cpu[MAXCPU];
72 struct intr_config_hook sc_ich;
73};
74
75#define VENDORID_AMD 0x1022
76#define DEVICEID_AMD_MISC0F 0x1103
77#define DEVICEID_AMD_MISC10 0x1203
78#define DEVICEID_AMD_MISC11 0x1303
79#define DEVICEID_AMD_MISC14 0x1703
80#define DEVICEID_AMD_MISC15 0x1603
81
82static struct amdtemp_product {
83 uint16_t amdtemp_vendorid;
84 uint16_t amdtemp_deviceid;
85} amdtemp_products[] = {
86 { VENDORID_AMD, DEVICEID_AMD_MISC0F },
87 { VENDORID_AMD, DEVICEID_AMD_MISC10 },
88 { VENDORID_AMD, DEVICEID_AMD_MISC11 },
89 { VENDORID_AMD, DEVICEID_AMD_MISC14 },
90 { VENDORID_AMD, DEVICEID_AMD_MISC15 },
91 { 0, 0 }
92};
93
94/*
95 * Reported Temperature Control Register
96 */
97#define AMDTEMP_REPTMP_CTRL 0xa4
98
99/*
100 * Thermaltrip Status Register (Family 0Fh only)
101 */
102#define AMDTEMP_THERMTP_STAT 0xe4
103#define AMDTEMP_TTSR_SELCORE 0x04
104#define AMDTEMP_TTSR_SELSENSOR 0x40
105
106/*
107 * DRAM Configuration High Register
108 */
109#define AMDTEMP_DRAM_CONF_HIGH 0x94 /* Function 2 */
110#define AMDTEMP_DRAM_MODE_DDR3 0x0100
111
112/*
113 * CPU Family/Model Register
114 */
115#define AMDTEMP_CPUID 0xfc
116
117/*
118 * Device methods.
119 */
120static void amdtemp_identify(driver_t *driver, device_t parent);
121static int amdtemp_probe(device_t dev);
122static int amdtemp_attach(device_t dev);
123static void amdtemp_intrhook(void *arg);
124static int amdtemp_detach(device_t dev);
125static int amdtemp_match(device_t dev);
126static int32_t amdtemp_gettemp0f(device_t dev, amdsensor_t sensor);
127static int32_t amdtemp_gettemp(device_t dev, amdsensor_t sensor);
128static int amdtemp_sysctl(SYSCTL_HANDLER_ARGS);
129
130static device_method_t amdtemp_methods[] = {
131 /* Device interface */
132 DEVMETHOD(device_identify, amdtemp_identify),
133 DEVMETHOD(device_probe, amdtemp_probe),
134 DEVMETHOD(device_attach, amdtemp_attach),
135 DEVMETHOD(device_detach, amdtemp_detach),
136
137 {0, 0}
138};
139
140static driver_t amdtemp_driver = {
141 "amdtemp",
142 amdtemp_methods,
143 sizeof(struct amdtemp_softc),
144};
145
146static devclass_t amdtemp_devclass;
147DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL);
148
149static int
150amdtemp_match(device_t dev)
151{
152 int i;
153 uint16_t vendor, devid;
154
155 vendor = pci_get_vendor(dev);
156 devid = pci_get_device(dev);
157
158 for (i = 0; amdtemp_products[i].amdtemp_vendorid != 0; i++) {
159 if (vendor == amdtemp_products[i].amdtemp_vendorid &&
160 devid == amdtemp_products[i].amdtemp_deviceid)
161 return (1);
162 }
163
164 return (0);
165}
166
167static void
168amdtemp_identify(driver_t *driver, device_t parent)
169{
170 device_t child;
171
172 /* Make sure we're not being doubly invoked. */
173 if (device_find_child(parent, "amdtemp", -1) != NULL)
174 return;
175
176 if (amdtemp_match(parent)) {
177 child = device_add_child(parent, "amdtemp", -1);
178 if (child == NULL)
179 device_printf(parent, "add amdtemp child failed\n");
180 }
181}
182
183static int
184amdtemp_probe(device_t dev)
185{
186 uint32_t family, model;
187
36
37#include <sys/param.h>
38#include <sys/bus.h>
39#include <sys/conf.h>
40#include <sys/kernel.h>
41#include <sys/module.h>
42#include <sys/sysctl.h>
43#include <sys/systm.h>
44
45#include <machine/cpufunc.h>
46#include <machine/md_var.h>
47#include <machine/specialreg.h>
48
49#include <dev/pci/pcivar.h>
50#include <x86/pci_cfgreg.h>
51
52typedef enum {
53 CORE0_SENSOR0,
54 CORE0_SENSOR1,
55 CORE1_SENSOR0,
56 CORE1_SENSOR1,
57 CORE0,
58 CORE1
59} amdsensor_t;
60
61struct amdtemp_softc {
62 device_t sc_dev;
63 int sc_ncores;
64 int sc_ntemps;
65 int sc_flags;
66#define AMDTEMP_FLAG_CS_SWAP 0x01 /* ThermSenseCoreSel is inverted. */
67#define AMDTEMP_FLAG_CT_10BIT 0x02 /* CurTmp is 10-bit wide. */
68#define AMDTEMP_FLAG_ALT_OFFSET 0x04 /* CurTmp starts at -28C. */
69 int32_t sc_offset;
70 int32_t (*sc_gettemp)(device_t, amdsensor_t);
71 struct sysctl_oid *sc_sysctl_cpu[MAXCPU];
72 struct intr_config_hook sc_ich;
73};
74
75#define VENDORID_AMD 0x1022
76#define DEVICEID_AMD_MISC0F 0x1103
77#define DEVICEID_AMD_MISC10 0x1203
78#define DEVICEID_AMD_MISC11 0x1303
79#define DEVICEID_AMD_MISC14 0x1703
80#define DEVICEID_AMD_MISC15 0x1603
81
82static struct amdtemp_product {
83 uint16_t amdtemp_vendorid;
84 uint16_t amdtemp_deviceid;
85} amdtemp_products[] = {
86 { VENDORID_AMD, DEVICEID_AMD_MISC0F },
87 { VENDORID_AMD, DEVICEID_AMD_MISC10 },
88 { VENDORID_AMD, DEVICEID_AMD_MISC11 },
89 { VENDORID_AMD, DEVICEID_AMD_MISC14 },
90 { VENDORID_AMD, DEVICEID_AMD_MISC15 },
91 { 0, 0 }
92};
93
94/*
95 * Reported Temperature Control Register
96 */
97#define AMDTEMP_REPTMP_CTRL 0xa4
98
99/*
100 * Thermaltrip Status Register (Family 0Fh only)
101 */
102#define AMDTEMP_THERMTP_STAT 0xe4
103#define AMDTEMP_TTSR_SELCORE 0x04
104#define AMDTEMP_TTSR_SELSENSOR 0x40
105
106/*
107 * DRAM Configuration High Register
108 */
109#define AMDTEMP_DRAM_CONF_HIGH 0x94 /* Function 2 */
110#define AMDTEMP_DRAM_MODE_DDR3 0x0100
111
112/*
113 * CPU Family/Model Register
114 */
115#define AMDTEMP_CPUID 0xfc
116
117/*
118 * Device methods.
119 */
120static void amdtemp_identify(driver_t *driver, device_t parent);
121static int amdtemp_probe(device_t dev);
122static int amdtemp_attach(device_t dev);
123static void amdtemp_intrhook(void *arg);
124static int amdtemp_detach(device_t dev);
125static int amdtemp_match(device_t dev);
126static int32_t amdtemp_gettemp0f(device_t dev, amdsensor_t sensor);
127static int32_t amdtemp_gettemp(device_t dev, amdsensor_t sensor);
128static int amdtemp_sysctl(SYSCTL_HANDLER_ARGS);
129
130static device_method_t amdtemp_methods[] = {
131 /* Device interface */
132 DEVMETHOD(device_identify, amdtemp_identify),
133 DEVMETHOD(device_probe, amdtemp_probe),
134 DEVMETHOD(device_attach, amdtemp_attach),
135 DEVMETHOD(device_detach, amdtemp_detach),
136
137 {0, 0}
138};
139
140static driver_t amdtemp_driver = {
141 "amdtemp",
142 amdtemp_methods,
143 sizeof(struct amdtemp_softc),
144};
145
146static devclass_t amdtemp_devclass;
147DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL);
148
149static int
150amdtemp_match(device_t dev)
151{
152 int i;
153 uint16_t vendor, devid;
154
155 vendor = pci_get_vendor(dev);
156 devid = pci_get_device(dev);
157
158 for (i = 0; amdtemp_products[i].amdtemp_vendorid != 0; i++) {
159 if (vendor == amdtemp_products[i].amdtemp_vendorid &&
160 devid == amdtemp_products[i].amdtemp_deviceid)
161 return (1);
162 }
163
164 return (0);
165}
166
167static void
168amdtemp_identify(driver_t *driver, device_t parent)
169{
170 device_t child;
171
172 /* Make sure we're not being doubly invoked. */
173 if (device_find_child(parent, "amdtemp", -1) != NULL)
174 return;
175
176 if (amdtemp_match(parent)) {
177 child = device_add_child(parent, "amdtemp", -1);
178 if (child == NULL)
179 device_printf(parent, "add amdtemp child failed\n");
180 }
181}
182
183static int
184amdtemp_probe(device_t dev)
185{
186 uint32_t family, model;
187
188 if (resource_disabled("amdtemp", 0))
189 return (ENXIO);
190
188 family = CPUID_TO_FAMILY(cpu_id);
189 model = CPUID_TO_MODEL(cpu_id);
190
191 switch (family) {
192 case 0x0f:
193 if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) ||
194 (model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1))
195 return (ENXIO);
196 break;
197 case 0x10:
198 case 0x11:
199 case 0x12:
200 case 0x14:
201 case 0x15:
202 break;
203 default:
204 return (ENXIO);
205 }
206 device_set_desc(dev, "AMD CPU On-Die Thermal Sensors");
207
208 return (BUS_PROBE_GENERIC);
209}
210
211static int
212amdtemp_attach(device_t dev)
213{
214 char tn[32];
215 u_int regs[4];
216 struct amdtemp_softc *sc = device_get_softc(dev);
217 struct sysctl_ctx_list *sysctlctx;
218 struct sysctl_oid *sysctlnode;
219 uint32_t cpuid, family, model;
220 u_int bid;
221 int erratum319, unit;
222
223 erratum319 = 0;
224
225 /*
226 * CPUID Register is available from Revision F.
227 */
228 cpuid = cpu_id;
229 family = CPUID_TO_FAMILY(cpuid);
230 model = CPUID_TO_MODEL(cpuid);
231 if (family != 0x0f || model >= 0x40) {
232 cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4);
233 family = CPUID_TO_FAMILY(cpuid);
234 model = CPUID_TO_MODEL(cpuid);
235 }
236
237 switch (family) {
238 case 0x0f:
239 /*
240 * Thermaltrip Status Register
241 *
242 * - ThermSenseCoreSel
243 *
244 * Revision F & G: 0 - Core1, 1 - Core0
245 * Other: 0 - Core0, 1 - Core1
246 *
247 * - CurTmp
248 *
249 * Revision G: bits 23-14
250 * Other: bits 23-16
251 *
252 * XXX According to the BKDG, CurTmp, ThermSenseSel and
253 * ThermSenseCoreSel bits were introduced in Revision F
254 * but CurTmp seems working fine as early as Revision C.
255 * However, it is not clear whether ThermSenseSel and/or
256 * ThermSenseCoreSel work in undocumented cases as well.
257 * In fact, the Linux driver suggests it may not work but
258 * we just assume it does until we find otherwise.
259 *
260 * XXX According to Linux, CurTmp starts at -28C on
261 * Socket AM2 Revision G processors, which is not
262 * documented anywhere.
263 */
264 if (model >= 0x40)
265 sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP;
266 if (model >= 0x60 && model != 0xc1) {
267 do_cpuid(0x80000001, regs);
268 bid = (regs[1] >> 9) & 0x1f;
269 switch (model) {
270 case 0x68: /* Socket S1g1 */
271 case 0x6c:
272 case 0x7c:
273 break;
274 case 0x6b: /* Socket AM2 and ASB1 (2 cores) */
275 if (bid != 0x0b && bid != 0x0c)
276 sc->sc_flags |=
277 AMDTEMP_FLAG_ALT_OFFSET;
278 break;
279 case 0x6f: /* Socket AM2 and ASB1 (1 core) */
280 case 0x7f:
281 if (bid != 0x07 && bid != 0x09 &&
282 bid != 0x0c)
283 sc->sc_flags |=
284 AMDTEMP_FLAG_ALT_OFFSET;
285 break;
286 default:
287 sc->sc_flags |= AMDTEMP_FLAG_ALT_OFFSET;
288 }
289 sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT;
290 }
291
292 /*
293 * There are two sensors per core.
294 */
295 sc->sc_ntemps = 2;
296
297 sc->sc_gettemp = amdtemp_gettemp0f;
298 break;
299 case 0x10:
300 /*
301 * Erratum 319 Inaccurate Temperature Measurement
302 *
303 * http://support.amd.com/us/Processor_TechDocs/41322.pdf
304 */
305 do_cpuid(0x80000001, regs);
306 switch ((regs[1] >> 28) & 0xf) {
307 case 0: /* Socket F */
308 erratum319 = 1;
309 break;
310 case 1: /* Socket AM2+ or AM3 */
311 if ((pci_cfgregread(pci_get_bus(dev),
312 pci_get_slot(dev), 2, AMDTEMP_DRAM_CONF_HIGH, 2) &
313 AMDTEMP_DRAM_MODE_DDR3) != 0 || model > 0x04 ||
314 (model == 0x04 && (cpuid & CPUID_STEPPING) >= 3))
315 break;
316 /* XXX 00100F42h (RB-C2) exists in both formats. */
317 erratum319 = 1;
318 break;
319 }
320 /* FALLTHROUGH */
321 case 0x11:
322 case 0x12:
323 case 0x14:
324 case 0x15:
325 /*
326 * There is only one sensor per package.
327 */
328 sc->sc_ntemps = 1;
329
330 sc->sc_gettemp = amdtemp_gettemp;
331 break;
332 }
333
334 /* Find number of cores per package. */
335 sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ?
336 (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1;
337 if (sc->sc_ncores > MAXCPU)
338 return (ENXIO);
339
340 if (erratum319)
341 device_printf(dev,
342 "Erratum 319: temperature measurement may be inaccurate\n");
343 if (bootverbose)
344 device_printf(dev, "Found %d cores and %d sensors.\n",
345 sc->sc_ncores,
346 sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1);
347
348 /*
349 * dev.amdtemp.N tree.
350 */
351 unit = device_get_unit(dev);
352 snprintf(tn, sizeof(tn), "dev.amdtemp.%d.sensor_offset", unit);
353 TUNABLE_INT_FETCH(tn, &sc->sc_offset);
354
355 sysctlctx = device_get_sysctl_ctx(dev);
356 SYSCTL_ADD_INT(sysctlctx,
357 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
358 "sensor_offset", CTLFLAG_RW, &sc->sc_offset, 0,
359 "Temperature sensor offset");
360 sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
361 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
362 "core0", CTLFLAG_RD, 0, "Core 0");
363
364 SYSCTL_ADD_PROC(sysctlctx,
365 SYSCTL_CHILDREN(sysctlnode),
366 OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD,
367 dev, CORE0_SENSOR0, amdtemp_sysctl, "IK",
368 "Core 0 / Sensor 0 temperature");
369
370 if (sc->sc_ntemps > 1) {
371 SYSCTL_ADD_PROC(sysctlctx,
372 SYSCTL_CHILDREN(sysctlnode),
373 OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD,
374 dev, CORE0_SENSOR1, amdtemp_sysctl, "IK",
375 "Core 0 / Sensor 1 temperature");
376
377 if (sc->sc_ncores > 1) {
378 sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
379 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
380 OID_AUTO, "core1", CTLFLAG_RD, 0, "Core 1");
381
382 SYSCTL_ADD_PROC(sysctlctx,
383 SYSCTL_CHILDREN(sysctlnode),
384 OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD,
385 dev, CORE1_SENSOR0, amdtemp_sysctl, "IK",
386 "Core 1 / Sensor 0 temperature");
387
388 SYSCTL_ADD_PROC(sysctlctx,
389 SYSCTL_CHILDREN(sysctlnode),
390 OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD,
391 dev, CORE1_SENSOR1, amdtemp_sysctl, "IK",
392 "Core 1 / Sensor 1 temperature");
393 }
394 }
395
396 /*
397 * Try to create dev.cpu sysctl entries and setup intrhook function.
398 * This is needed because the cpu driver may be loaded late on boot,
399 * after us.
400 */
401 amdtemp_intrhook(dev);
402 sc->sc_ich.ich_func = amdtemp_intrhook;
403 sc->sc_ich.ich_arg = dev;
404 if (config_intrhook_establish(&sc->sc_ich) != 0) {
405 device_printf(dev, "config_intrhook_establish failed!\n");
406 return (ENXIO);
407 }
408
409 return (0);
410}
411
412void
413amdtemp_intrhook(void *arg)
414{
415 struct amdtemp_softc *sc;
416 struct sysctl_ctx_list *sysctlctx;
417 device_t dev = (device_t)arg;
418 device_t acpi, cpu, nexus;
419 amdsensor_t sensor;
420 int i;
421
422 sc = device_get_softc(dev);
423
424 /*
425 * dev.cpu.N.temperature.
426 */
427 nexus = device_find_child(root_bus, "nexus", 0);
428 acpi = device_find_child(nexus, "acpi", 0);
429
430 for (i = 0; i < sc->sc_ncores; i++) {
431 if (sc->sc_sysctl_cpu[i] != NULL)
432 continue;
433 cpu = device_find_child(acpi, "cpu",
434 device_get_unit(dev) * sc->sc_ncores + i);
435 if (cpu != NULL) {
436 sysctlctx = device_get_sysctl_ctx(cpu);
437
438 sensor = sc->sc_ntemps > 1 ?
439 (i == 0 ? CORE0 : CORE1) : CORE0_SENSOR0;
440 sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
441 SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)),
442 OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD,
443 dev, sensor, amdtemp_sysctl, "IK",
444 "Current temparature");
445 }
446 }
447 if (sc->sc_ich.ich_arg != NULL)
448 config_intrhook_disestablish(&sc->sc_ich);
449}
450
451int
452amdtemp_detach(device_t dev)
453{
454 struct amdtemp_softc *sc = device_get_softc(dev);
455 int i;
456
457 for (i = 0; i < sc->sc_ncores; i++)
458 if (sc->sc_sysctl_cpu[i] != NULL)
459 sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0);
460
461 /* NewBus removes the dev.amdtemp.N tree by itself. */
462
463 return (0);
464}
465
466static int
467amdtemp_sysctl(SYSCTL_HANDLER_ARGS)
468{
469 device_t dev = (device_t)arg1;
470 struct amdtemp_softc *sc = device_get_softc(dev);
471 amdsensor_t sensor = (amdsensor_t)arg2;
472 int32_t auxtemp[2], temp;
473 int error;
474
475 switch (sensor) {
476 case CORE0:
477 auxtemp[0] = sc->sc_gettemp(dev, CORE0_SENSOR0);
478 auxtemp[1] = sc->sc_gettemp(dev, CORE0_SENSOR1);
479 temp = imax(auxtemp[0], auxtemp[1]);
480 break;
481 case CORE1:
482 auxtemp[0] = sc->sc_gettemp(dev, CORE1_SENSOR0);
483 auxtemp[1] = sc->sc_gettemp(dev, CORE1_SENSOR1);
484 temp = imax(auxtemp[0], auxtemp[1]);
485 break;
486 default:
487 temp = sc->sc_gettemp(dev, sensor);
488 break;
489 }
490 error = sysctl_handle_int(oidp, &temp, 0, req);
491
492 return (error);
493}
494
495#define AMDTEMP_ZERO_C_TO_K 2732
496
497static int32_t
498amdtemp_gettemp0f(device_t dev, amdsensor_t sensor)
499{
500 struct amdtemp_softc *sc = device_get_softc(dev);
501 uint32_t mask, offset, temp;
502
503 /* Set Sensor/Core selector. */
504 temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1);
505 temp &= ~(AMDTEMP_TTSR_SELCORE | AMDTEMP_TTSR_SELSENSOR);
506 switch (sensor) {
507 case CORE0_SENSOR1:
508 temp |= AMDTEMP_TTSR_SELSENSOR;
509 /* FALLTHROUGH */
510 case CORE0_SENSOR0:
511 case CORE0:
512 if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0)
513 temp |= AMDTEMP_TTSR_SELCORE;
514 break;
515 case CORE1_SENSOR1:
516 temp |= AMDTEMP_TTSR_SELSENSOR;
517 /* FALLTHROUGH */
518 case CORE1_SENSOR0:
519 case CORE1:
520 if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0)
521 temp |= AMDTEMP_TTSR_SELCORE;
522 break;
523 }
524 pci_write_config(dev, AMDTEMP_THERMTP_STAT, temp, 1);
525
526 mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc;
527 offset = (sc->sc_flags & AMDTEMP_FLAG_ALT_OFFSET) != 0 ? 28 : 49;
528 temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
529 temp = ((temp >> 14) & mask) * 5 / 2;
530 temp += AMDTEMP_ZERO_C_TO_K + (sc->sc_offset - offset) * 10;
531
532 return (temp);
533}
534
535static int32_t
536amdtemp_gettemp(device_t dev, amdsensor_t sensor)
537{
538 struct amdtemp_softc *sc = device_get_softc(dev);
539 uint32_t temp;
540
541 temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4);
542 temp = ((temp >> 21) & 0x7ff) * 5 / 4;
543 temp += AMDTEMP_ZERO_C_TO_K + sc->sc_offset * 10;
544
545 return (temp);
546}
191 family = CPUID_TO_FAMILY(cpu_id);
192 model = CPUID_TO_MODEL(cpu_id);
193
194 switch (family) {
195 case 0x0f:
196 if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) ||
197 (model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1))
198 return (ENXIO);
199 break;
200 case 0x10:
201 case 0x11:
202 case 0x12:
203 case 0x14:
204 case 0x15:
205 break;
206 default:
207 return (ENXIO);
208 }
209 device_set_desc(dev, "AMD CPU On-Die Thermal Sensors");
210
211 return (BUS_PROBE_GENERIC);
212}
213
214static int
215amdtemp_attach(device_t dev)
216{
217 char tn[32];
218 u_int regs[4];
219 struct amdtemp_softc *sc = device_get_softc(dev);
220 struct sysctl_ctx_list *sysctlctx;
221 struct sysctl_oid *sysctlnode;
222 uint32_t cpuid, family, model;
223 u_int bid;
224 int erratum319, unit;
225
226 erratum319 = 0;
227
228 /*
229 * CPUID Register is available from Revision F.
230 */
231 cpuid = cpu_id;
232 family = CPUID_TO_FAMILY(cpuid);
233 model = CPUID_TO_MODEL(cpuid);
234 if (family != 0x0f || model >= 0x40) {
235 cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4);
236 family = CPUID_TO_FAMILY(cpuid);
237 model = CPUID_TO_MODEL(cpuid);
238 }
239
240 switch (family) {
241 case 0x0f:
242 /*
243 * Thermaltrip Status Register
244 *
245 * - ThermSenseCoreSel
246 *
247 * Revision F & G: 0 - Core1, 1 - Core0
248 * Other: 0 - Core0, 1 - Core1
249 *
250 * - CurTmp
251 *
252 * Revision G: bits 23-14
253 * Other: bits 23-16
254 *
255 * XXX According to the BKDG, CurTmp, ThermSenseSel and
256 * ThermSenseCoreSel bits were introduced in Revision F
257 * but CurTmp seems working fine as early as Revision C.
258 * However, it is not clear whether ThermSenseSel and/or
259 * ThermSenseCoreSel work in undocumented cases as well.
260 * In fact, the Linux driver suggests it may not work but
261 * we just assume it does until we find otherwise.
262 *
263 * XXX According to Linux, CurTmp starts at -28C on
264 * Socket AM2 Revision G processors, which is not
265 * documented anywhere.
266 */
267 if (model >= 0x40)
268 sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP;
269 if (model >= 0x60 && model != 0xc1) {
270 do_cpuid(0x80000001, regs);
271 bid = (regs[1] >> 9) & 0x1f;
272 switch (model) {
273 case 0x68: /* Socket S1g1 */
274 case 0x6c:
275 case 0x7c:
276 break;
277 case 0x6b: /* Socket AM2 and ASB1 (2 cores) */
278 if (bid != 0x0b && bid != 0x0c)
279 sc->sc_flags |=
280 AMDTEMP_FLAG_ALT_OFFSET;
281 break;
282 case 0x6f: /* Socket AM2 and ASB1 (1 core) */
283 case 0x7f:
284 if (bid != 0x07 && bid != 0x09 &&
285 bid != 0x0c)
286 sc->sc_flags |=
287 AMDTEMP_FLAG_ALT_OFFSET;
288 break;
289 default:
290 sc->sc_flags |= AMDTEMP_FLAG_ALT_OFFSET;
291 }
292 sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT;
293 }
294
295 /*
296 * There are two sensors per core.
297 */
298 sc->sc_ntemps = 2;
299
300 sc->sc_gettemp = amdtemp_gettemp0f;
301 break;
302 case 0x10:
303 /*
304 * Erratum 319 Inaccurate Temperature Measurement
305 *
306 * http://support.amd.com/us/Processor_TechDocs/41322.pdf
307 */
308 do_cpuid(0x80000001, regs);
309 switch ((regs[1] >> 28) & 0xf) {
310 case 0: /* Socket F */
311 erratum319 = 1;
312 break;
313 case 1: /* Socket AM2+ or AM3 */
314 if ((pci_cfgregread(pci_get_bus(dev),
315 pci_get_slot(dev), 2, AMDTEMP_DRAM_CONF_HIGH, 2) &
316 AMDTEMP_DRAM_MODE_DDR3) != 0 || model > 0x04 ||
317 (model == 0x04 && (cpuid & CPUID_STEPPING) >= 3))
318 break;
319 /* XXX 00100F42h (RB-C2) exists in both formats. */
320 erratum319 = 1;
321 break;
322 }
323 /* FALLTHROUGH */
324 case 0x11:
325 case 0x12:
326 case 0x14:
327 case 0x15:
328 /*
329 * There is only one sensor per package.
330 */
331 sc->sc_ntemps = 1;
332
333 sc->sc_gettemp = amdtemp_gettemp;
334 break;
335 }
336
337 /* Find number of cores per package. */
338 sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ?
339 (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1;
340 if (sc->sc_ncores > MAXCPU)
341 return (ENXIO);
342
343 if (erratum319)
344 device_printf(dev,
345 "Erratum 319: temperature measurement may be inaccurate\n");
346 if (bootverbose)
347 device_printf(dev, "Found %d cores and %d sensors.\n",
348 sc->sc_ncores,
349 sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1);
350
351 /*
352 * dev.amdtemp.N tree.
353 */
354 unit = device_get_unit(dev);
355 snprintf(tn, sizeof(tn), "dev.amdtemp.%d.sensor_offset", unit);
356 TUNABLE_INT_FETCH(tn, &sc->sc_offset);
357
358 sysctlctx = device_get_sysctl_ctx(dev);
359 SYSCTL_ADD_INT(sysctlctx,
360 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
361 "sensor_offset", CTLFLAG_RW, &sc->sc_offset, 0,
362 "Temperature sensor offset");
363 sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
364 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
365 "core0", CTLFLAG_RD, 0, "Core 0");
366
367 SYSCTL_ADD_PROC(sysctlctx,
368 SYSCTL_CHILDREN(sysctlnode),
369 OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD,
370 dev, CORE0_SENSOR0, amdtemp_sysctl, "IK",
371 "Core 0 / Sensor 0 temperature");
372
373 if (sc->sc_ntemps > 1) {
374 SYSCTL_ADD_PROC(sysctlctx,
375 SYSCTL_CHILDREN(sysctlnode),
376 OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD,
377 dev, CORE0_SENSOR1, amdtemp_sysctl, "IK",
378 "Core 0 / Sensor 1 temperature");
379
380 if (sc->sc_ncores > 1) {
381 sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
382 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
383 OID_AUTO, "core1", CTLFLAG_RD, 0, "Core 1");
384
385 SYSCTL_ADD_PROC(sysctlctx,
386 SYSCTL_CHILDREN(sysctlnode),
387 OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD,
388 dev, CORE1_SENSOR0, amdtemp_sysctl, "IK",
389 "Core 1 / Sensor 0 temperature");
390
391 SYSCTL_ADD_PROC(sysctlctx,
392 SYSCTL_CHILDREN(sysctlnode),
393 OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD,
394 dev, CORE1_SENSOR1, amdtemp_sysctl, "IK",
395 "Core 1 / Sensor 1 temperature");
396 }
397 }
398
399 /*
400 * Try to create dev.cpu sysctl entries and setup intrhook function.
401 * This is needed because the cpu driver may be loaded late on boot,
402 * after us.
403 */
404 amdtemp_intrhook(dev);
405 sc->sc_ich.ich_func = amdtemp_intrhook;
406 sc->sc_ich.ich_arg = dev;
407 if (config_intrhook_establish(&sc->sc_ich) != 0) {
408 device_printf(dev, "config_intrhook_establish failed!\n");
409 return (ENXIO);
410 }
411
412 return (0);
413}
414
415void
416amdtemp_intrhook(void *arg)
417{
418 struct amdtemp_softc *sc;
419 struct sysctl_ctx_list *sysctlctx;
420 device_t dev = (device_t)arg;
421 device_t acpi, cpu, nexus;
422 amdsensor_t sensor;
423 int i;
424
425 sc = device_get_softc(dev);
426
427 /*
428 * dev.cpu.N.temperature.
429 */
430 nexus = device_find_child(root_bus, "nexus", 0);
431 acpi = device_find_child(nexus, "acpi", 0);
432
433 for (i = 0; i < sc->sc_ncores; i++) {
434 if (sc->sc_sysctl_cpu[i] != NULL)
435 continue;
436 cpu = device_find_child(acpi, "cpu",
437 device_get_unit(dev) * sc->sc_ncores + i);
438 if (cpu != NULL) {
439 sysctlctx = device_get_sysctl_ctx(cpu);
440
441 sensor = sc->sc_ntemps > 1 ?
442 (i == 0 ? CORE0 : CORE1) : CORE0_SENSOR0;
443 sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
444 SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)),
445 OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD,
446 dev, sensor, amdtemp_sysctl, "IK",
447 "Current temparature");
448 }
449 }
450 if (sc->sc_ich.ich_arg != NULL)
451 config_intrhook_disestablish(&sc->sc_ich);
452}
453
454int
455amdtemp_detach(device_t dev)
456{
457 struct amdtemp_softc *sc = device_get_softc(dev);
458 int i;
459
460 for (i = 0; i < sc->sc_ncores; i++)
461 if (sc->sc_sysctl_cpu[i] != NULL)
462 sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0);
463
464 /* NewBus removes the dev.amdtemp.N tree by itself. */
465
466 return (0);
467}
468
469static int
470amdtemp_sysctl(SYSCTL_HANDLER_ARGS)
471{
472 device_t dev = (device_t)arg1;
473 struct amdtemp_softc *sc = device_get_softc(dev);
474 amdsensor_t sensor = (amdsensor_t)arg2;
475 int32_t auxtemp[2], temp;
476 int error;
477
478 switch (sensor) {
479 case CORE0:
480 auxtemp[0] = sc->sc_gettemp(dev, CORE0_SENSOR0);
481 auxtemp[1] = sc->sc_gettemp(dev, CORE0_SENSOR1);
482 temp = imax(auxtemp[0], auxtemp[1]);
483 break;
484 case CORE1:
485 auxtemp[0] = sc->sc_gettemp(dev, CORE1_SENSOR0);
486 auxtemp[1] = sc->sc_gettemp(dev, CORE1_SENSOR1);
487 temp = imax(auxtemp[0], auxtemp[1]);
488 break;
489 default:
490 temp = sc->sc_gettemp(dev, sensor);
491 break;
492 }
493 error = sysctl_handle_int(oidp, &temp, 0, req);
494
495 return (error);
496}
497
498#define AMDTEMP_ZERO_C_TO_K 2732
499
500static int32_t
501amdtemp_gettemp0f(device_t dev, amdsensor_t sensor)
502{
503 struct amdtemp_softc *sc = device_get_softc(dev);
504 uint32_t mask, offset, temp;
505
506 /* Set Sensor/Core selector. */
507 temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1);
508 temp &= ~(AMDTEMP_TTSR_SELCORE | AMDTEMP_TTSR_SELSENSOR);
509 switch (sensor) {
510 case CORE0_SENSOR1:
511 temp |= AMDTEMP_TTSR_SELSENSOR;
512 /* FALLTHROUGH */
513 case CORE0_SENSOR0:
514 case CORE0:
515 if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0)
516 temp |= AMDTEMP_TTSR_SELCORE;
517 break;
518 case CORE1_SENSOR1:
519 temp |= AMDTEMP_TTSR_SELSENSOR;
520 /* FALLTHROUGH */
521 case CORE1_SENSOR0:
522 case CORE1:
523 if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0)
524 temp |= AMDTEMP_TTSR_SELCORE;
525 break;
526 }
527 pci_write_config(dev, AMDTEMP_THERMTP_STAT, temp, 1);
528
529 mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc;
530 offset = (sc->sc_flags & AMDTEMP_FLAG_ALT_OFFSET) != 0 ? 28 : 49;
531 temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
532 temp = ((temp >> 14) & mask) * 5 / 2;
533 temp += AMDTEMP_ZERO_C_TO_K + (sc->sc_offset - offset) * 10;
534
535 return (temp);
536}
537
538static int32_t
539amdtemp_gettemp(device_t dev, amdsensor_t sensor)
540{
541 struct amdtemp_softc *sc = device_get_softc(dev);
542 uint32_t temp;
543
544 temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4);
545 temp = ((temp >> 21) & 0x7ff) * 5 / 4;
546 temp += AMDTEMP_ZERO_C_TO_K + sc->sc_offset * 10;
547
548 return (temp);
549}