1fc1f75e5SRui Paulo /*- 2454e82d7SRui Paulo * Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org> 3454e82d7SRui Paulo * Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org> 4a4165bbaSJung-uk Kim * Copyright (c) 2009 Jung-uk Kim <jkim@FreeBSD.org> 5fc1f75e5SRui Paulo * All rights reserved. 6fc1f75e5SRui Paulo * 7fc1f75e5SRui Paulo * Redistribution and use in source and binary forms, with or without 8fc1f75e5SRui Paulo * modification, are permitted provided that the following conditions 9fc1f75e5SRui Paulo * are met: 10fc1f75e5SRui Paulo * 1. Redistributions of source code must retain the above copyright 11fc1f75e5SRui Paulo * notice, this list of conditions and the following disclaimer. 12fc1f75e5SRui Paulo * 2. Redistributions in binary form must reproduce the above copyright 13fc1f75e5SRui Paulo * notice, this list of conditions and the following disclaimer in the 14fc1f75e5SRui Paulo * documentation and/or other materials provided with the distribution. 15fc1f75e5SRui Paulo * 16fc1f75e5SRui Paulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17fc1f75e5SRui Paulo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18fc1f75e5SRui Paulo * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19fc1f75e5SRui Paulo * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 20fc1f75e5SRui Paulo * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21fc1f75e5SRui Paulo * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22fc1f75e5SRui Paulo * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23fc1f75e5SRui Paulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24fc1f75e5SRui Paulo * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25fc1f75e5SRui Paulo * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26fc1f75e5SRui Paulo * POSSIBILITY OF SUCH DAMAGE. 27fc1f75e5SRui Paulo */ 28fc1f75e5SRui Paulo 29fc1f75e5SRui Paulo /* 30a4165bbaSJung-uk Kim * Driver for the AMD CPU on-die thermal sensors for Family 0Fh/10h/11h procs. 31a4165bbaSJung-uk Kim * Initially based on the k8temp Linux driver. 32fc1f75e5SRui Paulo */ 33fc1f75e5SRui Paulo 34fc1f75e5SRui Paulo #include <sys/cdefs.h> 35fc1f75e5SRui Paulo __FBSDID("$FreeBSD$"); 36fc1f75e5SRui Paulo 37fc1f75e5SRui Paulo #include <sys/param.h> 38fc1f75e5SRui Paulo #include <sys/bus.h> 39fc1f75e5SRui Paulo #include <sys/conf.h> 40fc1f75e5SRui Paulo #include <sys/kernel.h> 41a4165bbaSJung-uk Kim #include <sys/module.h> 42fc1f75e5SRui Paulo #include <sys/sysctl.h> 43a4165bbaSJung-uk Kim #include <sys/systm.h> 44fc1f75e5SRui Paulo 45fdfa6079SJung-uk Kim #include <machine/cpufunc.h> 46fc1f75e5SRui Paulo #include <machine/md_var.h> 47a4165bbaSJung-uk Kim #include <machine/specialreg.h> 48fc1f75e5SRui Paulo 49fc1f75e5SRui Paulo #include <dev/pci/pcivar.h> 50fc1f75e5SRui Paulo 51fc1f75e5SRui Paulo typedef enum { 52fc1f75e5SRui Paulo SENSOR0_CORE0, 53fc1f75e5SRui Paulo SENSOR0_CORE1, 54fc1f75e5SRui Paulo SENSOR1_CORE0, 55fc1f75e5SRui Paulo SENSOR1_CORE1, 56fc1f75e5SRui Paulo CORE0, 57fc1f75e5SRui Paulo CORE1 58454e82d7SRui Paulo } amdsensor_t; 59454e82d7SRui Paulo 60454e82d7SRui Paulo struct amdtemp_softc { 61454e82d7SRui Paulo device_t sc_dev; 62a4165bbaSJung-uk Kim int sc_ncores; 63454e82d7SRui Paulo int sc_ntemps; 64fdfa6079SJung-uk Kim int sc_flags; 65fdfa6079SJung-uk Kim #define AMDTEMP_FLAG_DO_QUIRK 0x01 /* DiodeOffset may be incorrect. */ 66fdfa6079SJung-uk Kim #define AMDTEMP_FLAG_DO_ZERO 0x02 /* DiodeOffset starts from 0C. */ 67fdfa6079SJung-uk Kim #define AMDTEMP_FLAG_DO_SIGN 0x04 /* DiodeOffsetSignBit is present. */ 68fdfa6079SJung-uk Kim #define AMDTEMP_FLAG_CS_SWAP 0x08 /* ThermSenseCoreSel is inverted. */ 69fdfa6079SJung-uk Kim #define AMDTEMP_FLAG_CT_10BIT 0x10 /* CurTmp is 10-bit wide. */ 70454e82d7SRui Paulo int32_t (*sc_gettemp)(device_t, amdsensor_t); 71a4165bbaSJung-uk Kim struct sysctl_oid *sc_sysctl_cpu[MAXCPU]; 72a4165bbaSJung-uk Kim struct intr_config_hook sc_ich; 73454e82d7SRui Paulo }; 74454e82d7SRui Paulo 75454e82d7SRui Paulo #define VENDORID_AMD 0x1022 76454e82d7SRui Paulo #define DEVICEID_AMD_MISC0F 0x1103 77454e82d7SRui Paulo #define DEVICEID_AMD_MISC10 0x1203 78454e82d7SRui Paulo #define DEVICEID_AMD_MISC11 0x1303 79454e82d7SRui Paulo 80454e82d7SRui Paulo static struct amdtemp_product { 81454e82d7SRui Paulo uint16_t amdtemp_vendorid; 82454e82d7SRui Paulo uint16_t amdtemp_deviceid; 83454e82d7SRui Paulo } amdtemp_products[] = { 84454e82d7SRui Paulo { VENDORID_AMD, DEVICEID_AMD_MISC0F }, 85454e82d7SRui Paulo { VENDORID_AMD, DEVICEID_AMD_MISC10 }, 86454e82d7SRui Paulo { VENDORID_AMD, DEVICEID_AMD_MISC11 }, 87454e82d7SRui Paulo { 0, 0 } 88454e82d7SRui Paulo }; 89454e82d7SRui Paulo 90454e82d7SRui Paulo /* 91a4165bbaSJung-uk Kim * Reported Temperature Control Register (Family 10h/11h only) 92454e82d7SRui Paulo */ 93a4165bbaSJung-uk Kim #define AMDTEMP_REPTMP_CTRL 0xa4 94454e82d7SRui Paulo 95454e82d7SRui Paulo /* 96a4165bbaSJung-uk Kim * Thermaltrip Status Register 97454e82d7SRui Paulo */ 98a4165bbaSJung-uk Kim #define AMDTEMP_THERMTP_STAT 0xe4 99a4165bbaSJung-uk Kim #define AMDTEMP_TTSR_SELCORE 0x04 /* Family 0Fh only */ 100a4165bbaSJung-uk Kim #define AMDTEMP_TTSR_SELSENSOR 0x40 /* Family 0Fh only */ 101454e82d7SRui Paulo 102a4165bbaSJung-uk Kim /* 103a4165bbaSJung-uk Kim * CPU Family/Model Register 104a4165bbaSJung-uk Kim */ 105a4165bbaSJung-uk Kim #define AMDTEMP_CPUID 0xfc 106fc1f75e5SRui Paulo 107fc1f75e5SRui Paulo /* 108fc1f75e5SRui Paulo * Device methods. 109fc1f75e5SRui Paulo */ 110454e82d7SRui Paulo static void amdtemp_identify(driver_t *driver, device_t parent); 111454e82d7SRui Paulo static int amdtemp_probe(device_t dev); 112454e82d7SRui Paulo static int amdtemp_attach(device_t dev); 113454e82d7SRui Paulo static void amdtemp_intrhook(void *arg); 114454e82d7SRui Paulo static int amdtemp_detach(device_t dev); 115454e82d7SRui Paulo static int amdtemp_match(device_t dev); 116454e82d7SRui Paulo static int32_t amdtemp_gettemp0f(device_t dev, amdsensor_t sensor); 117454e82d7SRui Paulo static int32_t amdtemp_gettemp(device_t dev, amdsensor_t sensor); 118454e82d7SRui Paulo static int amdtemp_sysctl(SYSCTL_HANDLER_ARGS); 119fc1f75e5SRui Paulo 120454e82d7SRui Paulo static device_method_t amdtemp_methods[] = { 121fc1f75e5SRui Paulo /* Device interface */ 122454e82d7SRui Paulo DEVMETHOD(device_identify, amdtemp_identify), 123454e82d7SRui Paulo DEVMETHOD(device_probe, amdtemp_probe), 124454e82d7SRui Paulo DEVMETHOD(device_attach, amdtemp_attach), 125454e82d7SRui Paulo DEVMETHOD(device_detach, amdtemp_detach), 126fc1f75e5SRui Paulo 127fc1f75e5SRui Paulo {0, 0} 128fc1f75e5SRui Paulo }; 129fc1f75e5SRui Paulo 130454e82d7SRui Paulo static driver_t amdtemp_driver = { 131454e82d7SRui Paulo "amdtemp", 132454e82d7SRui Paulo amdtemp_methods, 133454e82d7SRui Paulo sizeof(struct amdtemp_softc), 134fc1f75e5SRui Paulo }; 135fc1f75e5SRui Paulo 136454e82d7SRui Paulo static devclass_t amdtemp_devclass; 137454e82d7SRui Paulo DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL); 138fc1f75e5SRui Paulo 139fc1f75e5SRui Paulo static int 140454e82d7SRui Paulo amdtemp_match(device_t dev) 141fc1f75e5SRui Paulo { 142fc1f75e5SRui Paulo int i; 143fc1f75e5SRui Paulo uint16_t vendor, devid; 144fc1f75e5SRui Paulo 145fc1f75e5SRui Paulo vendor = pci_get_vendor(dev); 146fc1f75e5SRui Paulo devid = pci_get_device(dev); 147fc1f75e5SRui Paulo 148454e82d7SRui Paulo for (i = 0; amdtemp_products[i].amdtemp_vendorid != 0; i++) { 149454e82d7SRui Paulo if (vendor == amdtemp_products[i].amdtemp_vendorid && 150454e82d7SRui Paulo devid == amdtemp_products[i].amdtemp_deviceid) 151fc1f75e5SRui Paulo return (1); 152fc1f75e5SRui Paulo } 153fc1f75e5SRui Paulo 154fc1f75e5SRui Paulo return (0); 155fc1f75e5SRui Paulo } 156fc1f75e5SRui Paulo 157fc1f75e5SRui Paulo static void 158454e82d7SRui Paulo amdtemp_identify(driver_t *driver, device_t parent) 159fc1f75e5SRui Paulo { 160fc1f75e5SRui Paulo device_t child; 161fc1f75e5SRui Paulo 162fc1f75e5SRui Paulo /* Make sure we're not being doubly invoked. */ 163454e82d7SRui Paulo if (device_find_child(parent, "amdtemp", -1) != NULL) 164fc1f75e5SRui Paulo return; 165fc1f75e5SRui Paulo 166454e82d7SRui Paulo if (amdtemp_match(parent)) { 167454e82d7SRui Paulo child = device_add_child(parent, "amdtemp", -1); 168fc1f75e5SRui Paulo if (child == NULL) 169454e82d7SRui Paulo device_printf(parent, "add amdtemp child failed\n"); 170fc1f75e5SRui Paulo } 171fc1f75e5SRui Paulo } 172fc1f75e5SRui Paulo 173fc1f75e5SRui Paulo static int 174454e82d7SRui Paulo amdtemp_probe(device_t dev) 175fc1f75e5SRui Paulo { 176fdfa6079SJung-uk Kim uint32_t family, model; 177fc1f75e5SRui Paulo 178454e82d7SRui Paulo if (resource_disabled("amdtemp", 0)) 179fc1f75e5SRui Paulo return (ENXIO); 180fc1f75e5SRui Paulo 181fdfa6079SJung-uk Kim family = CPUID_TO_FAMILY(cpu_id); 182fdfa6079SJung-uk Kim model = CPUID_TO_MODEL(cpu_id); 183a4165bbaSJung-uk Kim 184a4165bbaSJung-uk Kim switch (family) { 185a4165bbaSJung-uk Kim case 0x0f: 186fdfa6079SJung-uk Kim if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) || 187fdfa6079SJung-uk Kim (model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1)) 188a4165bbaSJung-uk Kim return (ENXIO); 189a4165bbaSJung-uk Kim break; 190a4165bbaSJung-uk Kim case 0x10: 191a4165bbaSJung-uk Kim case 0x11: 192a4165bbaSJung-uk Kim break; 193a4165bbaSJung-uk Kim default: 194fc1f75e5SRui Paulo return (ENXIO); 195fc1f75e5SRui Paulo } 196a4165bbaSJung-uk Kim device_set_desc(dev, "AMD CPU On-Die Thermal Sensors"); 197fc1f75e5SRui Paulo 198fc1f75e5SRui Paulo return (BUS_PROBE_GENERIC); 199fc1f75e5SRui Paulo } 200fc1f75e5SRui Paulo 201fc1f75e5SRui Paulo static int 202454e82d7SRui Paulo amdtemp_attach(device_t dev) 203fc1f75e5SRui Paulo { 204454e82d7SRui Paulo struct amdtemp_softc *sc = device_get_softc(dev); 205fc1f75e5SRui Paulo struct sysctl_ctx_list *sysctlctx; 206fc1f75e5SRui Paulo struct sysctl_oid *sysctlnode; 207fdfa6079SJung-uk Kim uint32_t regs[4]; 208a4165bbaSJung-uk Kim uint32_t cpuid, family, model; 209fc1f75e5SRui Paulo 210fdfa6079SJung-uk Kim /* 211fdfa6079SJung-uk Kim * Errata #154: Incorect Diode Offset 212fdfa6079SJung-uk Kim */ 213fdfa6079SJung-uk Kim if (cpu_id == 0x20f32) { 214fdfa6079SJung-uk Kim do_cpuid(0x80000001, regs); 215fdfa6079SJung-uk Kim if ((regs[1] & 0xfff) == 0x2c) 216fdfa6079SJung-uk Kim sc->sc_flags |= AMDTEMP_FLAG_DO_QUIRK; 217fdfa6079SJung-uk Kim } 218fdfa6079SJung-uk Kim 219fdfa6079SJung-uk Kim /* 220fdfa6079SJung-uk Kim * CPUID Register is available from Revision F. 221fdfa6079SJung-uk Kim */ 222fdfa6079SJung-uk Kim family = CPUID_TO_FAMILY(cpu_id); 223fdfa6079SJung-uk Kim model = CPUID_TO_MODEL(cpu_id); 224fdfa6079SJung-uk Kim if (family != 0x0f || model >= 0x40) { 225a4165bbaSJung-uk Kim cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4); 226a4165bbaSJung-uk Kim family = CPUID_TO_FAMILY(cpuid); 227a4165bbaSJung-uk Kim model = CPUID_TO_MODEL(cpuid); 228fdfa6079SJung-uk Kim } 229a4165bbaSJung-uk Kim 230a4165bbaSJung-uk Kim switch (family) { 231a4165bbaSJung-uk Kim case 0x0f: 232a4165bbaSJung-uk Kim /* 233fdfa6079SJung-uk Kim * Thermaltrip Status Register 234fdfa6079SJung-uk Kim * 235fdfa6079SJung-uk Kim * - DiodeOffsetSignBit 236fdfa6079SJung-uk Kim * 237fdfa6079SJung-uk Kim * Revision D & E: bit 24 238fdfa6079SJung-uk Kim * Other: N/A 239fdfa6079SJung-uk Kim * 240fdfa6079SJung-uk Kim * - ThermSenseCoreSel 241fdfa6079SJung-uk Kim * 242fdfa6079SJung-uk Kim * Revision F & G: 0 - Core1, 1 - Core0 243fdfa6079SJung-uk Kim * Other: 0 - Core0, 1 - Core1 244fdfa6079SJung-uk Kim * 245fdfa6079SJung-uk Kim * - CurTmp 246a4165bbaSJung-uk Kim * 247a4165bbaSJung-uk Kim * Revision G: bits 23-14 248fdfa6079SJung-uk Kim * Other: bits 23-16 249a4165bbaSJung-uk Kim * 250fdfa6079SJung-uk Kim * XXX According to the BKDG, CurTmp, ThermSenseSel and 251fdfa6079SJung-uk Kim * ThermSenseCoreSel bits were introduced in Revision F 252fdfa6079SJung-uk Kim * but CurTmp seems working fine as early as Revision C. 253fdfa6079SJung-uk Kim * However, it is not clear whether ThermSenseSel and/or 254fdfa6079SJung-uk Kim * ThermSenseCoreSel work in undocumented cases as well. 255fdfa6079SJung-uk Kim * In fact, the Linux driver suggests it may not work but 256fdfa6079SJung-uk Kim * we just assume it does until we find otherwise. 257fc1f75e5SRui Paulo */ 258fdfa6079SJung-uk Kim if (model < 0x40) { 259fdfa6079SJung-uk Kim sc->sc_flags |= AMDTEMP_FLAG_DO_ZERO; 260fdfa6079SJung-uk Kim if (model >= 0x10) 261fdfa6079SJung-uk Kim sc->sc_flags |= AMDTEMP_FLAG_DO_SIGN; 262fdfa6079SJung-uk Kim } else { 263fdfa6079SJung-uk Kim sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP; 264fdfa6079SJung-uk Kim if (model >= 0x60 && model != 0xc1) 265fdfa6079SJung-uk Kim sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT; 266fdfa6079SJung-uk Kim } 267a4165bbaSJung-uk Kim 268a4165bbaSJung-uk Kim /* 269a4165bbaSJung-uk Kim * There are two sensors per core. 270a4165bbaSJung-uk Kim */ 271a4165bbaSJung-uk Kim sc->sc_ntemps = 2; 272a4165bbaSJung-uk Kim 273a4165bbaSJung-uk Kim sc->sc_gettemp = amdtemp_gettemp0f; 274a4165bbaSJung-uk Kim break; 275a4165bbaSJung-uk Kim case 0x10: 276a4165bbaSJung-uk Kim case 0x11: 277a4165bbaSJung-uk Kim /* 278a4165bbaSJung-uk Kim * There is only one sensor per package. 279a4165bbaSJung-uk Kim */ 280a4165bbaSJung-uk Kim sc->sc_ntemps = 1; 281a4165bbaSJung-uk Kim 282a4165bbaSJung-uk Kim sc->sc_gettemp = amdtemp_gettemp; 283a4165bbaSJung-uk Kim break; 284fc1f75e5SRui Paulo } 285fc1f75e5SRui Paulo 286a4165bbaSJung-uk Kim /* Find number of cores per package. */ 287a4165bbaSJung-uk Kim sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ? 288a4165bbaSJung-uk Kim (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1; 289a4165bbaSJung-uk Kim if (sc->sc_ncores > MAXCPU) 290a4165bbaSJung-uk Kim return (ENXIO); 291a4165bbaSJung-uk Kim 292a4165bbaSJung-uk Kim if (bootverbose) 293a4165bbaSJung-uk Kim device_printf(dev, "Found %d cores and %d sensors.\n", 294a4165bbaSJung-uk Kim sc->sc_ncores, 295a4165bbaSJung-uk Kim sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1); 296454e82d7SRui Paulo 297fc1f75e5SRui Paulo /* 298454e82d7SRui Paulo * dev.amdtemp.N tree. 299fc1f75e5SRui Paulo */ 300fc1f75e5SRui Paulo sysctlctx = device_get_sysctl_ctx(dev); 301fc1f75e5SRui Paulo sysctlnode = SYSCTL_ADD_NODE(sysctlctx, 302a4165bbaSJung-uk Kim SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 303a4165bbaSJung-uk Kim "sensor0", CTLFLAG_RD, 0, "Sensor 0"); 304fc1f75e5SRui Paulo 305fc1f75e5SRui Paulo SYSCTL_ADD_PROC(sysctlctx, 306fc1f75e5SRui Paulo SYSCTL_CHILDREN(sysctlnode), 307fc1f75e5SRui Paulo OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD, 308454e82d7SRui Paulo dev, SENSOR0_CORE0, amdtemp_sysctl, "IK", 309fc1f75e5SRui Paulo "Sensor 0 / Core 0 temperature"); 310fc1f75e5SRui Paulo 311a4165bbaSJung-uk Kim if (sc->sc_ntemps > 1) { 312a4165bbaSJung-uk Kim if (sc->sc_ncores > 1) 313fc1f75e5SRui Paulo SYSCTL_ADD_PROC(sysctlctx, 314fc1f75e5SRui Paulo SYSCTL_CHILDREN(sysctlnode), 315fc1f75e5SRui Paulo OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD, 316454e82d7SRui Paulo dev, SENSOR0_CORE1, amdtemp_sysctl, "IK", 317fc1f75e5SRui Paulo "Sensor 0 / Core 1 temperature"); 318fc1f75e5SRui Paulo 319fc1f75e5SRui Paulo sysctlnode = SYSCTL_ADD_NODE(sysctlctx, 320a4165bbaSJung-uk Kim SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 321a4165bbaSJung-uk Kim "sensor1", CTLFLAG_RD, 0, "Sensor 1"); 322fc1f75e5SRui Paulo 323fc1f75e5SRui Paulo SYSCTL_ADD_PROC(sysctlctx, 324fc1f75e5SRui Paulo SYSCTL_CHILDREN(sysctlnode), 325fc1f75e5SRui Paulo OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD, 326454e82d7SRui Paulo dev, SENSOR1_CORE0, amdtemp_sysctl, "IK", 327fc1f75e5SRui Paulo "Sensor 1 / Core 0 temperature"); 328fc1f75e5SRui Paulo 329a4165bbaSJung-uk Kim if (sc->sc_ncores > 1) 330fc1f75e5SRui Paulo SYSCTL_ADD_PROC(sysctlctx, 331fc1f75e5SRui Paulo SYSCTL_CHILDREN(sysctlnode), 332fc1f75e5SRui Paulo OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD, 333454e82d7SRui Paulo dev, SENSOR1_CORE1, amdtemp_sysctl, "IK", 334fc1f75e5SRui Paulo "Sensor 1 / Core 1 temperature"); 335a4165bbaSJung-uk Kim } 336a4165bbaSJung-uk Kim 337a4165bbaSJung-uk Kim /* 338a4165bbaSJung-uk Kim * Try to create dev.cpu sysctl entries and setup intrhook function. 339a4165bbaSJung-uk Kim * This is needed because the cpu driver may be loaded late on boot, 340a4165bbaSJung-uk Kim * after us. 341a4165bbaSJung-uk Kim */ 342a4165bbaSJung-uk Kim amdtemp_intrhook(dev); 343a4165bbaSJung-uk Kim sc->sc_ich.ich_func = amdtemp_intrhook; 344a4165bbaSJung-uk Kim sc->sc_ich.ich_arg = dev; 345a4165bbaSJung-uk Kim if (config_intrhook_establish(&sc->sc_ich) != 0) { 346a4165bbaSJung-uk Kim device_printf(dev, "config_intrhook_establish failed!\n"); 347a4165bbaSJung-uk Kim return (ENXIO); 348a4165bbaSJung-uk Kim } 349fc1f75e5SRui Paulo 350fc1f75e5SRui Paulo return (0); 351fc1f75e5SRui Paulo } 352fc1f75e5SRui Paulo 353fc1f75e5SRui Paulo void 354454e82d7SRui Paulo amdtemp_intrhook(void *arg) 355fc1f75e5SRui Paulo { 356454e82d7SRui Paulo struct amdtemp_softc *sc; 357fc1f75e5SRui Paulo struct sysctl_ctx_list *sysctlctx; 358a4165bbaSJung-uk Kim device_t dev = (device_t)arg; 359a4165bbaSJung-uk Kim device_t acpi, cpu, nexus; 360a4165bbaSJung-uk Kim amdsensor_t sensor; 361a4165bbaSJung-uk Kim int i; 362fc1f75e5SRui Paulo 363fc1f75e5SRui Paulo sc = device_get_softc(dev); 364fc1f75e5SRui Paulo 365fc1f75e5SRui Paulo /* 366fc1f75e5SRui Paulo * dev.cpu.N.temperature. 367fc1f75e5SRui Paulo */ 368fc1f75e5SRui Paulo nexus = device_find_child(root_bus, "nexus", 0); 369fc1f75e5SRui Paulo acpi = device_find_child(nexus, "acpi", 0); 370fc1f75e5SRui Paulo 371a4165bbaSJung-uk Kim for (i = 0; i < sc->sc_ncores; i++) { 372a4165bbaSJung-uk Kim if (sc->sc_sysctl_cpu[i] != NULL) 373a4165bbaSJung-uk Kim continue; 374fc1f75e5SRui Paulo cpu = device_find_child(acpi, "cpu", 375a4165bbaSJung-uk Kim device_get_unit(dev) * sc->sc_ncores + i); 376a4165bbaSJung-uk Kim if (cpu != NULL) { 377fc1f75e5SRui Paulo sysctlctx = device_get_sysctl_ctx(cpu); 378fc1f75e5SRui Paulo 379a4165bbaSJung-uk Kim sensor = sc->sc_ntemps > 1 ? 380a4165bbaSJung-uk Kim (i == 0 ? CORE0 : CORE1) : SENSOR0_CORE0; 381fc1f75e5SRui Paulo sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx, 382fc1f75e5SRui Paulo SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), 383fc1f75e5SRui Paulo OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD, 384a4165bbaSJung-uk Kim dev, sensor, amdtemp_sysctl, "IK", 385a4165bbaSJung-uk Kim "Current temparature"); 386fc1f75e5SRui Paulo } 387fc1f75e5SRui Paulo } 388a4165bbaSJung-uk Kim if (sc->sc_ich.ich_arg != NULL) 389fc1f75e5SRui Paulo config_intrhook_disestablish(&sc->sc_ich); 390fc1f75e5SRui Paulo } 391fc1f75e5SRui Paulo 392fc1f75e5SRui Paulo int 393454e82d7SRui Paulo amdtemp_detach(device_t dev) 394fc1f75e5SRui Paulo { 395454e82d7SRui Paulo struct amdtemp_softc *sc = device_get_softc(dev); 396a4165bbaSJung-uk Kim int i; 397fc1f75e5SRui Paulo 398a4165bbaSJung-uk Kim for (i = 0; i < sc->sc_ncores; i++) 399a4165bbaSJung-uk Kim if (sc->sc_sysctl_cpu[i] != NULL) 400fc1f75e5SRui Paulo sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0); 401fc1f75e5SRui Paulo 402454e82d7SRui Paulo /* NewBus removes the dev.amdtemp.N tree by itself. */ 403fc1f75e5SRui Paulo 404fc1f75e5SRui Paulo return (0); 405fc1f75e5SRui Paulo } 406fc1f75e5SRui Paulo 407fc1f75e5SRui Paulo static int 408454e82d7SRui Paulo amdtemp_sysctl(SYSCTL_HANDLER_ARGS) 409fc1f75e5SRui Paulo { 410fc1f75e5SRui Paulo device_t dev = (device_t)arg1; 411454e82d7SRui Paulo struct amdtemp_softc *sc = device_get_softc(dev); 412a4165bbaSJung-uk Kim amdsensor_t sensor = (amdsensor_t)arg2; 413a4165bbaSJung-uk Kim int32_t auxtemp[2], temp; 414fc1f75e5SRui Paulo int error; 415fc1f75e5SRui Paulo 416a4165bbaSJung-uk Kim switch (sensor) { 417fc1f75e5SRui Paulo case CORE0: 418454e82d7SRui Paulo auxtemp[0] = sc->sc_gettemp(dev, SENSOR0_CORE0); 419454e82d7SRui Paulo auxtemp[1] = sc->sc_gettemp(dev, SENSOR1_CORE0); 420fc1f75e5SRui Paulo temp = imax(auxtemp[0], auxtemp[1]); 421fc1f75e5SRui Paulo break; 422fc1f75e5SRui Paulo case CORE1: 423454e82d7SRui Paulo auxtemp[0] = sc->sc_gettemp(dev, SENSOR0_CORE1); 424454e82d7SRui Paulo auxtemp[1] = sc->sc_gettemp(dev, SENSOR1_CORE1); 425fc1f75e5SRui Paulo temp = imax(auxtemp[0], auxtemp[1]); 426fc1f75e5SRui Paulo break; 427fc1f75e5SRui Paulo default: 428a4165bbaSJung-uk Kim temp = sc->sc_gettemp(dev, sensor); 429fc1f75e5SRui Paulo break; 430fc1f75e5SRui Paulo } 431fc1f75e5SRui Paulo error = sysctl_handle_int(oidp, &temp, 0, req); 432fc1f75e5SRui Paulo 433fc1f75e5SRui Paulo return (error); 434fc1f75e5SRui Paulo } 435fc1f75e5SRui Paulo 436a4165bbaSJung-uk Kim #define AMDTEMP_ZERO_C_TO_K 2732 437a4165bbaSJung-uk Kim 438fc1f75e5SRui Paulo static int32_t 439454e82d7SRui Paulo amdtemp_gettemp0f(device_t dev, amdsensor_t sensor) 440fc1f75e5SRui Paulo { 441a4165bbaSJung-uk Kim struct amdtemp_softc *sc = device_get_softc(dev); 442fdfa6079SJung-uk Kim uint32_t mask, temp; 443a4165bbaSJung-uk Kim int32_t diode_offset, offset; 444a4165bbaSJung-uk Kim uint8_t cfg, sel; 445fc1f75e5SRui Paulo 446a4165bbaSJung-uk Kim /* Set Sensor/Core selector. */ 447a4165bbaSJung-uk Kim sel = 0; 448fc1f75e5SRui Paulo switch (sensor) { 449fc1f75e5SRui Paulo case SENSOR1_CORE0: 450a4165bbaSJung-uk Kim sel |= AMDTEMP_TTSR_SELSENSOR; 4517ca2d97bSJung-uk Kim /* FALLTHROUGH */ 452a4165bbaSJung-uk Kim case SENSOR0_CORE0: 453a4165bbaSJung-uk Kim case CORE0: 454fdfa6079SJung-uk Kim if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0) 455a4165bbaSJung-uk Kim sel |= AMDTEMP_TTSR_SELCORE; 456fc1f75e5SRui Paulo break; 457fc1f75e5SRui Paulo case SENSOR1_CORE1: 458a4165bbaSJung-uk Kim sel |= AMDTEMP_TTSR_SELSENSOR; 4597ca2d97bSJung-uk Kim /* FALLTHROUGH */ 460a4165bbaSJung-uk Kim case SENSOR0_CORE1: 461a4165bbaSJung-uk Kim case CORE1: 462fdfa6079SJung-uk Kim if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0) 463a4165bbaSJung-uk Kim sel |= AMDTEMP_TTSR_SELCORE; 464fc1f75e5SRui Paulo break; 465fc1f75e5SRui Paulo } 466a4165bbaSJung-uk Kim cfg = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1); 467a4165bbaSJung-uk Kim cfg &= ~(AMDTEMP_TTSR_SELSENSOR | AMDTEMP_TTSR_SELCORE); 468a4165bbaSJung-uk Kim pci_write_config(dev, AMDTEMP_THERMTP_STAT, cfg | sel, 1); 469a4165bbaSJung-uk Kim 470a4165bbaSJung-uk Kim /* CurTmp starts from -49C. */ 471a4165bbaSJung-uk Kim offset = AMDTEMP_ZERO_C_TO_K - 490; 472a4165bbaSJung-uk Kim 473a4165bbaSJung-uk Kim /* Adjust offset if DiodeOffset is set and valid. */ 474a4165bbaSJung-uk Kim temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4); 475a4165bbaSJung-uk Kim diode_offset = (temp >> 8) & 0x3f; 476fdfa6079SJung-uk Kim if ((sc->sc_flags & AMDTEMP_FLAG_DO_ZERO) != 0) { 477fdfa6079SJung-uk Kim if ((sc->sc_flags & AMDTEMP_FLAG_DO_SIGN) != 0 && 478fdfa6079SJung-uk Kim ((temp >> 24) & 0x1) != 0) 479fdfa6079SJung-uk Kim diode_offset *= -1; 480fdfa6079SJung-uk Kim if ((sc->sc_flags & AMDTEMP_FLAG_DO_QUIRK) != 0 && 481fdfa6079SJung-uk Kim ((temp >> 25) & 0xf) <= 2) 482fdfa6079SJung-uk Kim diode_offset += 10; 483fdfa6079SJung-uk Kim offset += diode_offset * 10; 484fdfa6079SJung-uk Kim } else if (diode_offset != 0) 485a4165bbaSJung-uk Kim offset += (diode_offset - 11) * 10; 486a4165bbaSJung-uk Kim 487fdfa6079SJung-uk Kim mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc; 488fdfa6079SJung-uk Kim temp = ((temp >> 14) & mask) * 5 / 2 + offset; 489454e82d7SRui Paulo 490454e82d7SRui Paulo return (temp); 491454e82d7SRui Paulo } 492454e82d7SRui Paulo 493454e82d7SRui Paulo static int32_t 494454e82d7SRui Paulo amdtemp_gettemp(device_t dev, amdsensor_t sensor) 495454e82d7SRui Paulo { 496454e82d7SRui Paulo uint32_t temp; 497a4165bbaSJung-uk Kim int32_t diode_offset, offset; 498454e82d7SRui Paulo 499a4165bbaSJung-uk Kim /* CurTmp starts from 0C. */ 500a4165bbaSJung-uk Kim offset = AMDTEMP_ZERO_C_TO_K; 501a4165bbaSJung-uk Kim 502a4165bbaSJung-uk Kim /* Adjust offset if DiodeOffset is set and valid. */ 503a4165bbaSJung-uk Kim temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4); 504a4165bbaSJung-uk Kim diode_offset = (temp >> 8) & 0x7f; 505a4165bbaSJung-uk Kim if (diode_offset > 0 && diode_offset < 0x40) 506a4165bbaSJung-uk Kim offset += (diode_offset - 11) * 10; 507a4165bbaSJung-uk Kim 508a4165bbaSJung-uk Kim temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4); 509fdfa6079SJung-uk Kim temp = ((temp >> 21) & 0x7ff) * 5 / 4 + offset; 510fc1f75e5SRui Paulo 511fc1f75e5SRui Paulo return (temp); 512fc1f75e5SRui Paulo } 513