diff options
-rw-r--r-- | sys/dev/amdtemp/amdtemp.c | 351 |
1 files changed, 226 insertions, 125 deletions
diff --git a/sys/dev/amdtemp/amdtemp.c b/sys/dev/amdtemp/amdtemp.c index fdf0875..ababa89 100644 --- a/sys/dev/amdtemp/amdtemp.c +++ b/sys/dev/amdtemp/amdtemp.c @@ -1,6 +1,7 @@ /*- * Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org> * Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org> + * Copyright (c) 2009 Jung-uk Kim <jkim@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,8 +27,8 @@ */ /* - * Driver for the AMD K8/K10/K11 thermal sensors. Initially based on the - * k8temp Linux driver. + * Driver for the AMD CPU on-die thermal sensors for Family 0Fh/10h/11h procs. + * Initially based on the k8temp Linux driver. */ #include <sys/cdefs.h> @@ -35,18 +36,15 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/bus.h> -#include <sys/systm.h> -#include <sys/types.h> -#include <sys/module.h> #include <sys/conf.h> #include <sys/kernel.h> +#include <sys/module.h> #include <sys/sysctl.h> +#include <sys/systm.h> -#include <machine/specialreg.h> -#include <machine/cpufunc.h> #include <machine/md_var.h> +#include <machine/specialreg.h> -#include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> typedef enum { @@ -60,18 +58,19 @@ typedef enum { struct amdtemp_softc { device_t sc_dev; - int sc_temps[4]; + uint32_t sc_mask; + int sc_ncores; int sc_ntemps; - struct sysctl_oid *sc_oid; - struct sysctl_oid *sc_sysctl_cpu[2]; + int sc_swap; + int32_t (*sc_gettemp)(device_t, amdsensor_t); + struct sysctl_oid *sc_sysctl_cpu[MAXCPU]; struct intr_config_hook sc_ich; - int32_t (*sc_gettemp)(device_t, amdsensor_t); }; -#define VENDORID_AMD 0x1022 -#define DEVICEID_AMD_MISC0F 0x1103 -#define DEVICEID_AMD_MISC10 0x1203 -#define DEVICEID_AMD_MISC11 0x1303 +#define VENDORID_AMD 0x1022 +#define DEVICEID_AMD_MISC0F 0x1103 +#define DEVICEID_AMD_MISC10 0x1203 +#define DEVICEID_AMD_MISC11 0x1303 static struct amdtemp_product { uint16_t amdtemp_vendorid; @@ -84,22 +83,21 @@ static struct amdtemp_product { }; /* - * Register control (K8 family) + * Reported Temperature Control Register (Family 10h/11h only) */ -#define AMDTEMP_REG0F 0xe4 -#define AMDTEMP_REG_SELSENSOR 0x40 -#define AMDTEMP_REG_SELCORE 0x04 +#define AMDTEMP_REPTMP_CTRL 0xa4 /* - * Register control (K10 & K11) family + * Thermaltrip Status Register */ -#define AMDTEMP_REG 0xa4 - -#define TZ_ZEROC 2732 +#define AMDTEMP_THERMTP_STAT 0xe4 +#define AMDTEMP_TTSR_SELCORE 0x04 /* Family 0Fh only */ +#define AMDTEMP_TTSR_SELSENSOR 0x40 /* Family 0Fh only */ - /* -49 C is the mininum temperature */ -#define AMDTEMP_OFFSET0F (TZ_ZEROC-490) -#define AMDTEMP_OFFSET (TZ_ZEROC) +/* + * CPU Family/Model Register + */ +#define AMDTEMP_CPUID 0xfc /* * Device methods. @@ -138,8 +136,8 @@ amdtemp_match(device_t dev) { int i; uint16_t vendor, devid; - - vendor = pci_get_vendor(dev); + + vendor = pci_get_vendor(dev); devid = pci_get_device(dev); for (i = 0; amdtemp_products[i].amdtemp_vendorid != 0; i++) { @@ -159,32 +157,47 @@ amdtemp_identify(driver_t *driver, device_t parent) /* Make sure we're not being doubly invoked. */ if (device_find_child(parent, "amdtemp", -1) != NULL) return; - + if (amdtemp_match(parent)) { child = device_add_child(parent, "amdtemp", -1); if (child == NULL) device_printf(parent, "add amdtemp child failed\n"); } - } static int amdtemp_probe(device_t dev) { - uint32_t regs[4]; - + uint32_t cpuid, family, model, temp; + if (resource_disabled("amdtemp", 0)) return (ENXIO); - do_cpuid(1, regs); - switch (regs[0]) { - case 0xf40: - case 0xf50: - case 0xf51: + cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4); + family = CPUID_TO_FAMILY(cpuid); + model = CPUID_TO_MODEL(cpuid); + + switch (family) { + case 0x0f: + if ((model == 0x04 && (cpuid & CPUID_STEPPING) == 0) || + (model == 0x05 && (cpuid & CPUID_STEPPING) <= 1)) + return (ENXIO); + break; + case 0x10: + case 0x11: + /* + * DiodeOffset must be non-zero if thermal diode is supported. + */ + temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4); + temp = (temp >> 8) & 0x7f; + if (temp == 0) + return (ENXIO); + break; + default: return (ENXIO); } - device_set_desc(dev, "AMD K8 Thermal Sensors"); - + device_set_desc(dev, "AMD CPU On-Die Thermal Sensors"); + return (BUS_PROBE_GENERIC); } @@ -194,63 +207,119 @@ amdtemp_attach(device_t dev) struct amdtemp_softc *sc = device_get_softc(dev); struct sysctl_ctx_list *sysctlctx; struct sysctl_oid *sysctlnode; + uint32_t cpuid, family, model; + + cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4); + family = CPUID_TO_FAMILY(cpuid); + model = CPUID_TO_MODEL(cpuid); + + switch (family) { + case 0x0f: + /* + * Thermaltrip Status Register - CurTmp + * + * Revision G: bits 23-14 + * Earlier: bits 23-16 + */ + if (model >= 0x60 && model != 0xc1) + sc->sc_mask = 0x3ff << 14; + else + sc->sc_mask = 0xff << 16; + + /* + * Thermaltrip Status Register - ThermSenseCoreSel + * + * Revision F: 0 - Core1, 1 - Core0 + * Earlier: 0 - Core0, 1 - Core1 + */ + sc->sc_swap = (model >= 0x40); + + /* + * There are two sensors per core. + */ + sc->sc_ntemps = 2; - - /* - * Setup intrhook function to create dev.cpu sysctl entries. This is - * needed because the cpu driver may be loaded late on boot, after - * us. - */ - sc->sc_ich.ich_func = amdtemp_intrhook; - sc->sc_ich.ich_arg = dev; - if (config_intrhook_establish(&sc->sc_ich) != 0) { - device_printf(dev, "config_intrhook_establish " - "failed!\n"); - return (ENXIO); - } - - if (pci_get_device(dev) == DEVICEID_AMD_MISC0F) sc->sc_gettemp = amdtemp_gettemp0f; - else { + break; + case 0x10: + case 0x11: + /* + * Reported Temperature Control Register - Curtmp + */ + sc->sc_mask = 0x3ff << 21; + + /* + * There is only one sensor per package. + */ + sc->sc_ntemps = 1; + sc->sc_gettemp = amdtemp_gettemp; - return (0); + break; } + /* Find number of cores per package. */ + sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ? + (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1; + if (sc->sc_ncores > MAXCPU) + return (ENXIO); + + if (bootverbose) + device_printf(dev, "Found %d cores and %d sensors.\n", + sc->sc_ncores, + sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1); + /* * dev.amdtemp.N tree. */ sysctlctx = device_get_sysctl_ctx(dev); sysctlnode = SYSCTL_ADD_NODE(sysctlctx, - SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor0", - CTLFLAG_RD, 0, "Sensor 0"); - + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "sensor0", CTLFLAG_RD, 0, "Sensor 0"); + SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD, dev, SENSOR0_CORE0, amdtemp_sysctl, "IK", "Sensor 0 / Core 0 temperature"); - - SYSCTL_ADD_PROC(sysctlctx, - SYSCTL_CHILDREN(sysctlnode), - OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD, - dev, SENSOR0_CORE1, amdtemp_sysctl, "IK", - "Sensor 0 / Core 1 temperature"); - - sysctlnode = SYSCTL_ADD_NODE(sysctlctx, - SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor1", - CTLFLAG_RD, 0, "Sensor 1"); - - SYSCTL_ADD_PROC(sysctlctx, - SYSCTL_CHILDREN(sysctlnode), - OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD, - dev, SENSOR1_CORE0, amdtemp_sysctl, "IK", - "Sensor 1 / Core 0 temperature"); - - SYSCTL_ADD_PROC(sysctlctx, - SYSCTL_CHILDREN(sysctlnode), - OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD, - dev, SENSOR1_CORE1, amdtemp_sysctl, "IK", - "Sensor 1 / Core 1 temperature"); + + if (sc->sc_ntemps > 1) { + if (sc->sc_ncores > 1) + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sysctlnode), + OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD, + dev, SENSOR0_CORE1, amdtemp_sysctl, "IK", + "Sensor 0 / Core 1 temperature"); + + sysctlnode = SYSCTL_ADD_NODE(sysctlctx, + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "sensor1", CTLFLAG_RD, 0, "Sensor 1"); + + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sysctlnode), + OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD, + dev, SENSOR1_CORE0, amdtemp_sysctl, "IK", + "Sensor 1 / Core 0 temperature"); + + if (sc->sc_ncores > 1) + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sysctlnode), + OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD, + dev, SENSOR1_CORE1, amdtemp_sysctl, "IK", + "Sensor 1 / Core 1 temperature"); + } + + /* + * Try to create dev.cpu sysctl entries and setup intrhook function. + * This is needed because the cpu driver may be loaded late on boot, + * after us. + */ + amdtemp_intrhook(dev); + sc->sc_ich.ich_func = amdtemp_intrhook; + sc->sc_ich.ich_arg = dev; + if (config_intrhook_establish(&sc->sc_ich) != 0) { + device_printf(dev, "config_intrhook_establish failed!\n"); + return (ENXIO); + } return (0); } @@ -258,61 +327,67 @@ amdtemp_attach(device_t dev) void amdtemp_intrhook(void *arg) { - int i; - device_t nexus, acpi, cpu; - device_t dev = (device_t) arg; struct amdtemp_softc *sc; struct sysctl_ctx_list *sysctlctx; + device_t dev = (device_t)arg; + device_t acpi, cpu, nexus; + amdsensor_t sensor; + int i; sc = device_get_softc(dev); - + /* * dev.cpu.N.temperature. */ nexus = device_find_child(root_bus, "nexus", 0); acpi = device_find_child(nexus, "acpi", 0); - for (i = 0; i < 2; i++) { + for (i = 0; i < sc->sc_ncores; i++) { + if (sc->sc_sysctl_cpu[i] != NULL) + continue; cpu = device_find_child(acpi, "cpu", - device_get_unit(dev) * 2 + i); - if (cpu) { + device_get_unit(dev) * sc->sc_ncores + i); + if (cpu != NULL) { sysctlctx = device_get_sysctl_ctx(cpu); + sensor = sc->sc_ntemps > 1 ? + (i == 0 ? CORE0 : CORE1) : SENSOR0_CORE0; sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx, SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD, - dev, CORE0, amdtemp_sysctl, "IK", - "Max of sensor 0 / 1"); + dev, sensor, amdtemp_sysctl, "IK", + "Current temparature"); } } - config_intrhook_disestablish(&sc->sc_ich); + if (sc->sc_ich.ich_arg != NULL) + config_intrhook_disestablish(&sc->sc_ich); } int amdtemp_detach(device_t dev) { - int i; struct amdtemp_softc *sc = device_get_softc(dev); - - for (i = 0; i < 2; i++) { - if (sc->sc_sysctl_cpu[i]) + int i; + + for (i = 0; i < sc->sc_ncores; i++) + if (sc->sc_sysctl_cpu[i] != NULL) sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0); - } /* NewBus removes the dev.amdtemp.N tree by itself. */ - + return (0); } static int amdtemp_sysctl(SYSCTL_HANDLER_ARGS) { - device_t dev = (device_t) arg1; + device_t dev = (device_t)arg1; struct amdtemp_softc *sc = device_get_softc(dev); + amdsensor_t sensor = (amdsensor_t)arg2; + int32_t auxtemp[2], temp; int error; - int32_t temp, auxtemp[2]; - switch (arg2) { + switch (sensor) { case CORE0: auxtemp[0] = sc->sc_gettemp(dev, SENSOR0_CORE0); auxtemp[1] = sc->sc_gettemp(dev, SENSOR1_CORE0); @@ -324,54 +399,80 @@ amdtemp_sysctl(SYSCTL_HANDLER_ARGS) temp = imax(auxtemp[0], auxtemp[1]); break; default: - temp = sc->sc_gettemp(dev, arg2); + temp = sc->sc_gettemp(dev, sensor); break; } error = sysctl_handle_int(oidp, &temp, 0, req); - + return (error); } +#define AMDTEMP_ZERO_C_TO_K 2732 + static int32_t amdtemp_gettemp0f(device_t dev, amdsensor_t sensor) { - uint8_t cfg; + struct amdtemp_softc *sc = device_get_softc(dev); uint32_t temp; - - cfg = pci_read_config(dev, AMDTEMP_REG0F, 1); + int32_t diode_offset, offset; + uint8_t cfg, sel; + + /* Set Sensor/Core selector. */ + sel = 0; switch (sensor) { - case SENSOR0_CORE0: - cfg &= ~(AMDTEMP_REG_SELSENSOR | AMDTEMP_REG_SELCORE); - break; - case SENSOR0_CORE1: - cfg &= ~AMDTEMP_REG_SELSENSOR; - cfg |= AMDTEMP_REG_SELCORE; - break; case SENSOR1_CORE0: - cfg &= ~AMDTEMP_REG_SELCORE; - cfg |= AMDTEMP_REG_SELSENSOR; + sel |= AMDTEMP_TTSR_SELSENSOR; + /* FALLTROUGH */ + case SENSOR0_CORE0: + case CORE0: + if (sc->sc_swap) + sel |= AMDTEMP_TTSR_SELCORE; break; case SENSOR1_CORE1: - cfg |= (AMDTEMP_REG_SELSENSOR | AMDTEMP_REG_SELCORE); - break; - default: - cfg = 0; + sel |= AMDTEMP_TTSR_SELSENSOR; + /* FALLTROUGH */ + case SENSOR0_CORE1: + case CORE1: + if (!sc->sc_swap) + sel |= AMDTEMP_TTSR_SELCORE; break; } - pci_write_config(dev, AMDTEMP_REG0F, cfg, 1); - temp = pci_read_config(dev, AMDTEMP_REG0F, 4); - temp = ((temp >> 16) & 0xff) * 10 + AMDTEMP_OFFSET0F; - + cfg = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1); + cfg &= ~(AMDTEMP_TTSR_SELSENSOR | AMDTEMP_TTSR_SELCORE); + pci_write_config(dev, AMDTEMP_THERMTP_STAT, cfg | sel, 1); + + /* CurTmp starts from -49C. */ + offset = AMDTEMP_ZERO_C_TO_K - 490; + + /* Adjust offset if DiodeOffset is set and valid. */ + temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4); + diode_offset = (temp >> 8) & 0x3f; + if (diode_offset != 0) + offset += (diode_offset - 11) * 10; + + temp = ((temp & sc->sc_mask) >> 14) * 5 / 2 + offset; + return (temp); } static int32_t amdtemp_gettemp(device_t dev, amdsensor_t sensor) { + struct amdtemp_softc *sc = device_get_softc(dev); uint32_t temp; + int32_t diode_offset, offset; + + /* CurTmp starts from 0C. */ + offset = AMDTEMP_ZERO_C_TO_K; + + /* Adjust offset if DiodeOffset is set and valid. */ + temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4); + diode_offset = (temp >> 8) & 0x7f; + if (diode_offset > 0 && diode_offset < 0x40) + offset += (diode_offset - 11) * 10; - temp = pci_read_config(dev, AMDTEMP_REG, 4); - temp = ((temp >> 21) & 0x3ff) * 10 / 8 + AMDTEMP_OFFSET; + temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4); + temp = ((temp & sc->sc_mask) >> 21) * 5 / 4 + offset; return (temp); } |