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