1 /*-
2 * Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org>
3 * Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org>
4 * Copyright (c) 2009 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 for Family 0Fh/10h/11h procs.
31 * Initially based on the k8temp Linux driver.
32 */
33
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
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
51 typedef enum {
52 SENSOR0_CORE0,
53 SENSOR0_CORE1,
54 SENSOR1_CORE0,
55 SENSOR1_CORE1,
56 CORE0,
57 CORE1
58 } amdsensor_t;
59
60 struct amdtemp_softc {
61 device_t sc_dev;
62 int sc_ncores;
63 int sc_ntemps;
64 int sc_flags;
65 #define AMDTEMP_FLAG_DO_QUIRK 0x01 /* DiodeOffset may be incorrect. */
66 #define AMDTEMP_FLAG_DO_ZERO 0x02 /* DiodeOffset starts from 0C. */
67 #define AMDTEMP_FLAG_DO_SIGN 0x04 /* DiodeOffsetSignBit is present. */
68 #define AMDTEMP_FLAG_CS_SWAP 0x08 /* ThermSenseCoreSel is inverted. */
69 #define AMDTEMP_FLAG_CT_10BIT 0x10 /* CurTmp is 10-bit wide. */
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_MISC16 0x1533
80 #define DEVICEID_AMD_MISC17 0x141d
81
82 static 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_MISC16 },
90 { VENDORID_AMD, DEVICEID_AMD_MISC17 },
91 { 0, 0 }
92 };
93
94 /*
95 * Reported Temperature Control Register (Family 10h/11h only)
96 */
97 #define AMDTEMP_REPTMP_CTRL 0xa4
98
99 /*
100 * Thermaltrip Status Register
101 */
102 #define AMDTEMP_THERMTP_STAT 0xe4
103 #define AMDTEMP_TTSR_SELCORE 0x04 /* Family 0Fh only */
104 #define AMDTEMP_TTSR_SELSENSOR 0x40 /* Family 0Fh only */
105
106 /*
107 * CPU Family/Model Register
108 */
109 #define AMDTEMP_CPUID 0xfc
110
111 /*
112 * Device methods.
113 */
114 static void amdtemp_identify(driver_t *driver, device_t parent);
115 static int amdtemp_probe(device_t dev);
116 static int amdtemp_attach(device_t dev);
117 static void amdtemp_intrhook(void *arg);
118 static int amdtemp_detach(device_t dev);
119 static int amdtemp_match(device_t dev);
120 static int32_t amdtemp_gettemp0f(device_t dev, amdsensor_t sensor);
121 static int32_t amdtemp_gettemp(device_t dev, amdsensor_t sensor);
122 static int amdtemp_sysctl(SYSCTL_HANDLER_ARGS);
123
124 static device_method_t amdtemp_methods[] = {
125 /* Device interface */
126 DEVMETHOD(device_identify, amdtemp_identify),
127 DEVMETHOD(device_probe, amdtemp_probe),
128 DEVMETHOD(device_attach, amdtemp_attach),
129 DEVMETHOD(device_detach, amdtemp_detach),
130
131 {0, 0}
132 };
133
134 static driver_t amdtemp_driver = {
135 "amdtemp",
136 amdtemp_methods,
137 sizeof(struct amdtemp_softc),
138 };
139
140 static devclass_t amdtemp_devclass;
141 DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL);
142
143 static int
144 amdtemp_match(device_t dev)
145 {
146 int i;
147 uint16_t vendor, devid;
148
149 vendor = pci_get_vendor(dev);
150 devid = pci_get_device(dev);
151
152 for (i = 0; amdtemp_products[i].amdtemp_vendorid != 0; i++) {
153 if (vendor == amdtemp_products[i].amdtemp_vendorid &&
154 devid == amdtemp_products[i].amdtemp_deviceid)
155 return (1);
156 }
157
158 return (0);
159 }
160
161 static void
162 amdtemp_identify(driver_t *driver, device_t parent)
163 {
164 device_t child;
165
166 /* Make sure we're not being doubly invoked. */
167 if (device_find_child(parent, "amdtemp", -1) != NULL)
168 return;
169
170 if (amdtemp_match(parent)) {
171 child = device_add_child(parent, "amdtemp", -1);
172 if (child == NULL)
173 device_printf(parent, "add amdtemp child failed\n");
174 }
175 }
176
177 static int
178 amdtemp_probe(device_t dev)
179 {
180 uint32_t family, model;
181
182 if (resource_disabled("amdtemp", 0))
183 return (ENXIO);
184
185 family = CPUID_TO_FAMILY(cpu_id);
186 model = CPUID_TO_MODEL(cpu_id);
187
188 switch (family) {
189 case 0x0f:
190 if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) ||
191 (model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1))
192 return (ENXIO);
193 break;
194 case 0x10:
195 case 0x11:
196 case 0x16:
197 break;
198 default:
199 return (ENXIO);
200 }
201 device_set_desc(dev, "AMD CPU On-Die Thermal Sensors");
202
203 return (BUS_PROBE_GENERIC);
204 }
205
206 static int
207 amdtemp_attach(device_t dev)
208 {
209 struct amdtemp_softc *sc = device_get_softc(dev);
210 struct sysctl_ctx_list *sysctlctx;
211 struct sysctl_oid *sysctlnode;
212 uint32_t regs[4];
213 uint32_t cpuid, family, model;
214
215 /*
216 * Errata #154: Incorect Diode Offset
217 */
218 if (cpu_id == 0x20f32) {
219 do_cpuid(0x80000001, regs);
220 if ((regs[1] & 0xfff) == 0x2c)
221 sc->sc_flags |= AMDTEMP_FLAG_DO_QUIRK;
222 }
223
224 /*
225 * CPUID Register is available from Revision F.
226 */
227 family = CPUID_TO_FAMILY(cpu_id);
228 model = CPUID_TO_MODEL(cpu_id);
229 if (family != 0x0f || model >= 0x40) {
230 cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4);
231 family = CPUID_TO_FAMILY(cpuid);
232 model = CPUID_TO_MODEL(cpuid);
233 }
234
235 switch (family) {
236 case 0x0f:
237 /*
238 * Thermaltrip Status Register
239 *
240 * - DiodeOffsetSignBit
241 *
242 * Revision D & E: bit 24
243 * Other: N/A
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 if (model < 0x40) {
264 sc->sc_flags |= AMDTEMP_FLAG_DO_ZERO;
265 if (model >= 0x10)
266 sc->sc_flags |= AMDTEMP_FLAG_DO_SIGN;
267 } else {
268 sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP;
269 if (model >= 0x60 && model != 0xc1)
270 sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT;
271 }
272
273 /*
274 * There are two sensors per core.
275 */
276 sc->sc_ntemps = 2;
277
278 sc->sc_gettemp = amdtemp_gettemp0f;
279 break;
280 case 0x10:
281 case 0x11:
282 case 0x16:
283 /*
284 * There is only one sensor per package.
285 */
286 sc->sc_ntemps = 1;
287
288 sc->sc_gettemp = amdtemp_gettemp;
289 break;
290 }
291
292 /* Find number of cores per package. */
293 sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ?
294 (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1;
295 if (sc->sc_ncores > MAXCPU)
296 return (ENXIO);
297
298 if (bootverbose)
299 device_printf(dev, "Found %d cores and %d sensors.\n",
300 sc->sc_ncores,
301 sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1);
302
303 /*
304 * dev.amdtemp.N tree.
305 */
306 sysctlctx = device_get_sysctl_ctx(dev);
307 sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
308 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
309 "sensor0", CTLFLAG_RD, 0, "Sensor 0");
310
311 SYSCTL_ADD_PROC(sysctlctx,
312 SYSCTL_CHILDREN(sysctlnode),
313 OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD,
314 dev, SENSOR0_CORE0, amdtemp_sysctl, "IK",
315 "Sensor 0 / Core 0 temperature");
316
317 if (sc->sc_ntemps > 1) {
318 if (sc->sc_ncores > 1)
319 SYSCTL_ADD_PROC(sysctlctx,
320 SYSCTL_CHILDREN(sysctlnode),
321 OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD,
322 dev, SENSOR0_CORE1, amdtemp_sysctl, "IK",
323 "Sensor 0 / Core 1 temperature");
324
325 sysctlnode = SYSCTL_ADD_NODE(sysctlctx,
326 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
327 "sensor1", CTLFLAG_RD, 0, "Sensor 1");
328
329 SYSCTL_ADD_PROC(sysctlctx,
330 SYSCTL_CHILDREN(sysctlnode),
331 OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD,
332 dev, SENSOR1_CORE0, amdtemp_sysctl, "IK",
333 "Sensor 1 / Core 0 temperature");
334
335 if (sc->sc_ncores > 1)
336 SYSCTL_ADD_PROC(sysctlctx,
337 SYSCTL_CHILDREN(sysctlnode),
338 OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD,
339 dev, SENSOR1_CORE1, amdtemp_sysctl, "IK",
340 "Sensor 1 / Core 1 temperature");
341 }
342
343 /*
344 * Try to create dev.cpu sysctl entries and setup intrhook function.
345 * This is needed because the cpu driver may be loaded late on boot,
346 * after us.
347 */
348 amdtemp_intrhook(dev);
349 sc->sc_ich.ich_func = amdtemp_intrhook;
350 sc->sc_ich.ich_arg = dev;
351 if (config_intrhook_establish(&sc->sc_ich) != 0) {
352 device_printf(dev, "config_intrhook_establish failed!\n");
353 return (ENXIO);
354 }
355
356 return (0);
357 }
358
359 void
360 amdtemp_intrhook(void *arg)
361 {
362 struct amdtemp_softc *sc;
363 struct sysctl_ctx_list *sysctlctx;
364 device_t dev = (device_t)arg;
365 device_t acpi, cpu, nexus;
366 amdsensor_t sensor;
367 int i;
368
369 sc = device_get_softc(dev);
370
371 /*
372 * dev.cpu.N.temperature.
373 */
374 nexus = device_find_child(root_bus, "nexus", 0);
375 acpi = device_find_child(nexus, "acpi", 0);
376
377 for (i = 0; i < sc->sc_ncores; i++) {
378 if (sc->sc_sysctl_cpu[i] != NULL)
379 continue;
380 cpu = device_find_child(acpi, "cpu",
381 device_get_unit(dev) * sc->sc_ncores + i);
382 if (cpu != NULL) {
383 sysctlctx = device_get_sysctl_ctx(cpu);
384
385 sensor = sc->sc_ntemps > 1 ?
386 (i == 0 ? CORE0 : CORE1) : SENSOR0_CORE0;
387 sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
388 SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)),
389 OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD,
390 dev, sensor, amdtemp_sysctl, "IK",
391 "Current temparature");
392 }
393 }
394 if (sc->sc_ich.ich_arg != NULL)
395 config_intrhook_disestablish(&sc->sc_ich);
396 }
397
398 int
399 amdtemp_detach(device_t dev)
400 {
401 struct amdtemp_softc *sc = device_get_softc(dev);
402 int i;
403
404 for (i = 0; i < sc->sc_ncores; i++)
405 if (sc->sc_sysctl_cpu[i] != NULL)
406 sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0);
407
408 /* NewBus removes the dev.amdtemp.N tree by itself. */
409
410 return (0);
411 }
412
413 static int
414 amdtemp_sysctl(SYSCTL_HANDLER_ARGS)
415 {
416 device_t dev = (device_t)arg1;
417 struct amdtemp_softc *sc = device_get_softc(dev);
418 amdsensor_t sensor = (amdsensor_t)arg2;
419 int32_t auxtemp[2], temp;
420 int error;
421
422 switch (sensor) {
423 case CORE0:
424 auxtemp[0] = sc->sc_gettemp(dev, SENSOR0_CORE0);
425 auxtemp[1] = sc->sc_gettemp(dev, SENSOR1_CORE0);
426 temp = imax(auxtemp[0], auxtemp[1]);
427 break;
428 case CORE1:
429 auxtemp[0] = sc->sc_gettemp(dev, SENSOR0_CORE1);
430 auxtemp[1] = sc->sc_gettemp(dev, SENSOR1_CORE1);
431 temp = imax(auxtemp[0], auxtemp[1]);
432 break;
433 default:
434 temp = sc->sc_gettemp(dev, sensor);
435 break;
436 }
437 error = sysctl_handle_int(oidp, &temp, 0, req);
438
439 return (error);
440 }
441
442 #define AMDTEMP_ZERO_C_TO_K 2732
443
444 static int32_t
445 amdtemp_gettemp0f(device_t dev, amdsensor_t sensor)
446 {
447 struct amdtemp_softc *sc = device_get_softc(dev);
448 uint32_t mask, temp;
449 int32_t diode_offset, offset;
450 uint8_t cfg, sel;
451
452 /* Set Sensor/Core selector. */
453 sel = 0;
454 switch (sensor) {
455 case SENSOR1_CORE0:
456 sel |= AMDTEMP_TTSR_SELSENSOR;
457 /* FALLTHROUGH */
458 case SENSOR0_CORE0:
459 case CORE0:
460 if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0)
461 sel |= AMDTEMP_TTSR_SELCORE;
462 break;
463 case SENSOR1_CORE1:
464 sel |= AMDTEMP_TTSR_SELSENSOR;
465 /* FALLTHROUGH */
466 case SENSOR0_CORE1:
467 case CORE1:
468 if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0)
469 sel |= AMDTEMP_TTSR_SELCORE;
470 break;
471 }
472 cfg = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1);
473 cfg &= ~(AMDTEMP_TTSR_SELSENSOR | AMDTEMP_TTSR_SELCORE);
474 pci_write_config(dev, AMDTEMP_THERMTP_STAT, cfg | sel, 1);
475
476 /* CurTmp starts from -49C. */
477 offset = AMDTEMP_ZERO_C_TO_K - 490;
478
479 /* Adjust offset if DiodeOffset is set and valid. */
480 temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
481 diode_offset = (temp >> 8) & 0x3f;
482 if ((sc->sc_flags & AMDTEMP_FLAG_DO_ZERO) != 0) {
483 if ((sc->sc_flags & AMDTEMP_FLAG_DO_SIGN) != 0 &&
484 ((temp >> 24) & 0x1) != 0)
485 diode_offset *= -1;
486 if ((sc->sc_flags & AMDTEMP_FLAG_DO_QUIRK) != 0 &&
487 ((temp >> 25) & 0xf) <= 2)
488 diode_offset += 10;
489 offset += diode_offset * 10;
490 } else if (diode_offset != 0)
491 offset += (diode_offset - 11) * 10;
492
493 mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc;
494 temp = ((temp >> 14) & mask) * 5 / 2 + offset;
495
496 return (temp);
497 }
498
499 static int32_t
500 amdtemp_gettemp(device_t dev, amdsensor_t sensor)
501 {
502 uint32_t temp;
503 int32_t diode_offset, offset;
504
505 /* CurTmp starts from 0C. */
506 offset = AMDTEMP_ZERO_C_TO_K;
507
508 /* Adjust offset if DiodeOffset is set and valid. */
509 temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4);
510 diode_offset = (temp >> 8) & 0x7f;
511 if (diode_offset > 0 && diode_offset < 0x40)
512 offset += (diode_offset - 11) * 10;
513
514 temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4);
515 temp = ((temp >> 21) & 0x7ff) * 5 / 4 + offset;
516
517 return (temp);
518 }
Cache object: c93b4be436c8c2ce6f6d269423720e4a
|