1fc1f75e5SRui Paulo /*- 2718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3718cf2ccSPedro F. Giffuni * 4454e82d7SRui Paulo * Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org> 5454e82d7SRui Paulo * Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org> 6074d80acSJung-uk Kim * Copyright (c) 2009-2012 Jung-uk Kim <jkim@FreeBSD.org> 7fc1f75e5SRui Paulo * All rights reserved. 8*c59b9a4fSConrad Meyer * Copyright (c) 2017-2020 Conrad Meyer <cem@FreeBSD.org>. All rights reserved. 9fc1f75e5SRui Paulo * 10fc1f75e5SRui Paulo * Redistribution and use in source and binary forms, with or without 11fc1f75e5SRui Paulo * modification, are permitted provided that the following conditions 12fc1f75e5SRui Paulo * are met: 13fc1f75e5SRui Paulo * 1. Redistributions of source code must retain the above copyright 14fc1f75e5SRui Paulo * notice, this list of conditions and the following disclaimer. 15fc1f75e5SRui Paulo * 2. Redistributions in binary form must reproduce the above copyright 16fc1f75e5SRui Paulo * notice, this list of conditions and the following disclaimer in the 17fc1f75e5SRui Paulo * documentation and/or other materials provided with the distribution. 18fc1f75e5SRui Paulo * 19fc1f75e5SRui Paulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20fc1f75e5SRui Paulo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21fc1f75e5SRui Paulo * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22fc1f75e5SRui Paulo * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 23fc1f75e5SRui Paulo * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24fc1f75e5SRui Paulo * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25fc1f75e5SRui Paulo * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26fc1f75e5SRui Paulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 27fc1f75e5SRui Paulo * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 28fc1f75e5SRui Paulo * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29fc1f75e5SRui Paulo * POSSIBILITY OF SUCH DAMAGE. 30fc1f75e5SRui Paulo */ 31fc1f75e5SRui Paulo 32fc1f75e5SRui Paulo /* 33074d80acSJung-uk Kim * Driver for the AMD CPU on-die thermal sensors. 34a4165bbaSJung-uk Kim * Initially based on the k8temp Linux driver. 35fc1f75e5SRui Paulo */ 36fc1f75e5SRui Paulo 37fc1f75e5SRui Paulo #include <sys/cdefs.h> 38fc1f75e5SRui Paulo __FBSDID("$FreeBSD$"); 39fc1f75e5SRui Paulo 40fc1f75e5SRui Paulo #include <sys/param.h> 41fc1f75e5SRui Paulo #include <sys/bus.h> 42fc1f75e5SRui Paulo #include <sys/conf.h> 43fc1f75e5SRui Paulo #include <sys/kernel.h> 44a4165bbaSJung-uk Kim #include <sys/module.h> 45fc1f75e5SRui Paulo #include <sys/sysctl.h> 46a4165bbaSJung-uk Kim #include <sys/systm.h> 47fc1f75e5SRui Paulo 48fdfa6079SJung-uk Kim #include <machine/cpufunc.h> 49fc1f75e5SRui Paulo #include <machine/md_var.h> 50a4165bbaSJung-uk Kim #include <machine/specialreg.h> 51fc1f75e5SRui Paulo 52fc1f75e5SRui Paulo #include <dev/pci/pcivar.h> 53074d80acSJung-uk Kim #include <x86/pci_cfgreg.h> 54fc1f75e5SRui Paulo 55a03d621bSConrad Meyer #include <dev/amdsmn/amdsmn.h> 56a03d621bSConrad Meyer 57fc1f75e5SRui Paulo typedef enum { 58074d80acSJung-uk Kim CORE0_SENSOR0, 59074d80acSJung-uk Kim CORE0_SENSOR1, 60074d80acSJung-uk Kim CORE1_SENSOR0, 61074d80acSJung-uk Kim CORE1_SENSOR1, 62fc1f75e5SRui Paulo CORE0, 63*c59b9a4fSConrad Meyer CORE1, 64*c59b9a4fSConrad Meyer CCD1, 65*c59b9a4fSConrad Meyer CCD_BASE = CCD1, 66*c59b9a4fSConrad Meyer CCD2, 67*c59b9a4fSConrad Meyer CCD3, 68*c59b9a4fSConrad Meyer CCD4, 69*c59b9a4fSConrad Meyer CCD5, 70*c59b9a4fSConrad Meyer CCD6, 71*c59b9a4fSConrad Meyer CCD7, 72*c59b9a4fSConrad Meyer CCD8, 73*c59b9a4fSConrad Meyer CCD_MAX = CCD8, 74*c59b9a4fSConrad Meyer NUM_CCDS = CCD_MAX - CCD_BASE + 1, 75454e82d7SRui Paulo } amdsensor_t; 76454e82d7SRui Paulo 77454e82d7SRui Paulo struct amdtemp_softc { 78a4165bbaSJung-uk Kim int sc_ncores; 79454e82d7SRui Paulo int sc_ntemps; 80fdfa6079SJung-uk Kim int sc_flags; 81074d80acSJung-uk Kim #define AMDTEMP_FLAG_CS_SWAP 0x01 /* ThermSenseCoreSel is inverted. */ 82074d80acSJung-uk Kim #define AMDTEMP_FLAG_CT_10BIT 0x02 /* CurTmp is 10-bit wide. */ 83074d80acSJung-uk Kim #define AMDTEMP_FLAG_ALT_OFFSET 0x04 /* CurTmp starts at -28C. */ 84074d80acSJung-uk Kim int32_t sc_offset; 85454e82d7SRui Paulo int32_t (*sc_gettemp)(device_t, amdsensor_t); 86a4165bbaSJung-uk Kim struct sysctl_oid *sc_sysctl_cpu[MAXCPU]; 87a4165bbaSJung-uk Kim struct intr_config_hook sc_ich; 88a03d621bSConrad Meyer device_t sc_smn; 89454e82d7SRui Paulo }; 90454e82d7SRui Paulo 91e49ec461SConrad Meyer /* 92e49ec461SConrad Meyer * N.B. The numbers in macro names below are significant and represent CPU 93e49ec461SConrad Meyer * family and model numbers. Do not make up fictitious family or model numbers 94e49ec461SConrad Meyer * when adding support for new devices. 95e49ec461SConrad Meyer */ 96454e82d7SRui Paulo #define VENDORID_AMD 0x1022 97454e82d7SRui Paulo #define DEVICEID_AMD_MISC0F 0x1103 98454e82d7SRui Paulo #define DEVICEID_AMD_MISC10 0x1203 99454e82d7SRui Paulo #define DEVICEID_AMD_MISC11 0x1303 100074d80acSJung-uk Kim #define DEVICEID_AMD_MISC14 0x1703 101074d80acSJung-uk Kim #define DEVICEID_AMD_MISC15 0x1603 102e49ec461SConrad Meyer #define DEVICEID_AMD_MISC15_M10H 0x1403 103e49ec461SConrad Meyer #define DEVICEID_AMD_MISC15_M30H 0x141d 104e49ec461SConrad Meyer #define DEVICEID_AMD_MISC15_M60H_ROOT 0x1576 1052b56f12bSChristian Brueffer #define DEVICEID_AMD_MISC16 0x1533 106df20515dSLuiz Otavio O Souza #define DEVICEID_AMD_MISC16_M30H 0x1583 1079d49c422SConrad Meyer #define DEVICEID_AMD_HOSTB17H_ROOT 0x1450 1089d49c422SConrad Meyer #define DEVICEID_AMD_HOSTB17H_M10H_ROOT 0x15d0 109*c59b9a4fSConrad Meyer #define DEVICEID_AMD_HOSTB17H_M30H_ROOT 0x1480 /* Also M70h. */ 110454e82d7SRui Paulo 111e49ec461SConrad Meyer static const struct amdtemp_product { 112454e82d7SRui Paulo uint16_t amdtemp_vendorid; 113454e82d7SRui Paulo uint16_t amdtemp_deviceid; 114e49ec461SConrad Meyer /* 115e49ec461SConrad Meyer * 0xFC register is only valid on the D18F3 PCI device; SMN temp 116e49ec461SConrad Meyer * drivers do not attach to that device. 117e49ec461SConrad Meyer */ 118e49ec461SConrad Meyer bool amdtemp_has_cpuid; 119454e82d7SRui Paulo } amdtemp_products[] = { 120e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC0F, true }, 121e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC10, true }, 122e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC11, true }, 123e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC14, true }, 124e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC15, true }, 125e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC15_M10H, true }, 126e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC15_M30H, true }, 127e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC15_M60H_ROOT, false }, 128e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC16, true }, 129e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC16_M30H, true }, 130e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_HOSTB17H_ROOT, false }, 131e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_HOSTB17H_M10H_ROOT, false }, 13285dbddbeSConrad Meyer { VENDORID_AMD, DEVICEID_AMD_HOSTB17H_M30H_ROOT, false }, 133454e82d7SRui Paulo }; 134454e82d7SRui Paulo 135454e82d7SRui Paulo /* 136e49ec461SConrad Meyer * Reported Temperature Control Register, family 0Fh-15h (some models), 16h. 137454e82d7SRui Paulo */ 138a4165bbaSJung-uk Kim #define AMDTEMP_REPTMP_CTRL 0xa4 139454e82d7SRui Paulo 140e49ec461SConrad Meyer #define AMDTEMP_REPTMP10H_CURTMP_MASK 0x7ff 141e49ec461SConrad Meyer #define AMDTEMP_REPTMP10H_CURTMP_SHIFT 21 142e49ec461SConrad Meyer #define AMDTEMP_REPTMP10H_TJSEL_MASK 0x3 143e49ec461SConrad Meyer #define AMDTEMP_REPTMP10H_TJSEL_SHIFT 16 144e49ec461SConrad Meyer 145e49ec461SConrad Meyer /* 146e49ec461SConrad Meyer * Reported Temperature, Family 15h, M60+ 147e49ec461SConrad Meyer * 148e49ec461SConrad Meyer * Same register bit definitions as other Family 15h CPUs, but access is 149e49ec461SConrad Meyer * indirect via SMN, like Family 17h. 150e49ec461SConrad Meyer */ 151e49ec461SConrad Meyer #define AMDTEMP_15H_M60H_REPTMP_CTRL 0xd8200ca4 152e49ec461SConrad Meyer 153454e82d7SRui Paulo /* 154a03d621bSConrad Meyer * Reported Temperature, Family 17h 155fbd5d782SConrad Meyer * 156fbd5d782SConrad Meyer * According to AMD OSRR for 17H, section 4.2.1, bits 31-21 of this register 157fbd5d782SConrad Meyer * provide the current temp. bit 19, when clear, means the temp is reported in 158fbd5d782SConrad Meyer * a range 0.."225C" (probable typo for 255C), and when set changes the range 159fbd5d782SConrad Meyer * to -49..206C. 160a03d621bSConrad Meyer */ 161a03d621bSConrad Meyer #define AMDTEMP_17H_CUR_TMP 0x59800 162*c59b9a4fSConrad Meyer #define AMDTEMP_17H_CUR_TMP_RANGE_SEL (1u << 19) 163*c59b9a4fSConrad Meyer /* 164*c59b9a4fSConrad Meyer * The following register set was discovered experimentally by Ondrej Čerman 165*c59b9a4fSConrad Meyer * and collaborators, but is not (yet) documented in a PPR/OSRR (other than 166*c59b9a4fSConrad Meyer * the M70H PPR SMN memory map showing [0x59800, +0x314] as allocated to 167*c59b9a4fSConrad Meyer * SMU::THM). It seems plausible and the Linux sensor folks have adopted it. 168*c59b9a4fSConrad Meyer */ 169*c59b9a4fSConrad Meyer #define AMDTEMP_17H_CCD_TMP_BASE 0x59954 170*c59b9a4fSConrad Meyer #define AMDTEMP_17H_CCD_TMP_VALID (1u << 11) 171e49ec461SConrad Meyer 172e49ec461SConrad Meyer /* 173e49ec461SConrad Meyer * AMD temperature range adjustment, in deciKelvins (i.e., 49.0 Celsius). 174e49ec461SConrad Meyer */ 175e49ec461SConrad Meyer #define AMDTEMP_CURTMP_RANGE_ADJUST 490 176a03d621bSConrad Meyer 177a03d621bSConrad Meyer /* 178074d80acSJung-uk Kim * Thermaltrip Status Register (Family 0Fh only) 179454e82d7SRui Paulo */ 180a4165bbaSJung-uk Kim #define AMDTEMP_THERMTP_STAT 0xe4 181074d80acSJung-uk Kim #define AMDTEMP_TTSR_SELCORE 0x04 182074d80acSJung-uk Kim #define AMDTEMP_TTSR_SELSENSOR 0x40 183074d80acSJung-uk Kim 184074d80acSJung-uk Kim /* 185074d80acSJung-uk Kim * DRAM Configuration High Register 186074d80acSJung-uk Kim */ 187074d80acSJung-uk Kim #define AMDTEMP_DRAM_CONF_HIGH 0x94 /* Function 2 */ 188074d80acSJung-uk Kim #define AMDTEMP_DRAM_MODE_DDR3 0x0100 189454e82d7SRui Paulo 190a4165bbaSJung-uk Kim /* 191a4165bbaSJung-uk Kim * CPU Family/Model Register 192a4165bbaSJung-uk Kim */ 193a4165bbaSJung-uk Kim #define AMDTEMP_CPUID 0xfc 194fc1f75e5SRui Paulo 195fc1f75e5SRui Paulo /* 196fc1f75e5SRui Paulo * Device methods. 197fc1f75e5SRui Paulo */ 198454e82d7SRui Paulo static void amdtemp_identify(driver_t *driver, device_t parent); 199454e82d7SRui Paulo static int amdtemp_probe(device_t dev); 200454e82d7SRui Paulo static int amdtemp_attach(device_t dev); 201454e82d7SRui Paulo static void amdtemp_intrhook(void *arg); 202454e82d7SRui Paulo static int amdtemp_detach(device_t dev); 203454e82d7SRui Paulo static int32_t amdtemp_gettemp0f(device_t dev, amdsensor_t sensor); 204454e82d7SRui Paulo static int32_t amdtemp_gettemp(device_t dev, amdsensor_t sensor); 205e49ec461SConrad Meyer static int32_t amdtemp_gettemp15hm60h(device_t dev, amdsensor_t sensor); 206a03d621bSConrad Meyer static int32_t amdtemp_gettemp17h(device_t dev, amdsensor_t sensor); 207*c59b9a4fSConrad Meyer static void amdtemp_probe_ccd_sensors17h(device_t dev, uint32_t model); 208454e82d7SRui Paulo static int amdtemp_sysctl(SYSCTL_HANDLER_ARGS); 209fc1f75e5SRui Paulo 210454e82d7SRui Paulo static device_method_t amdtemp_methods[] = { 211fc1f75e5SRui Paulo /* Device interface */ 212454e82d7SRui Paulo DEVMETHOD(device_identify, amdtemp_identify), 213454e82d7SRui Paulo DEVMETHOD(device_probe, amdtemp_probe), 214454e82d7SRui Paulo DEVMETHOD(device_attach, amdtemp_attach), 215454e82d7SRui Paulo DEVMETHOD(device_detach, amdtemp_detach), 216fc1f75e5SRui Paulo 21761bfd867SSofian Brabez DEVMETHOD_END 218fc1f75e5SRui Paulo }; 219fc1f75e5SRui Paulo 220454e82d7SRui Paulo static driver_t amdtemp_driver = { 221454e82d7SRui Paulo "amdtemp", 222454e82d7SRui Paulo amdtemp_methods, 223454e82d7SRui Paulo sizeof(struct amdtemp_softc), 224fc1f75e5SRui Paulo }; 225fc1f75e5SRui Paulo 226454e82d7SRui Paulo static devclass_t amdtemp_devclass; 227454e82d7SRui Paulo DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL); 228a03d621bSConrad Meyer MODULE_VERSION(amdtemp, 1); 229a03d621bSConrad Meyer MODULE_DEPEND(amdtemp, amdsmn, 1, 1, 1); 230a64bf59cSConrad Meyer MODULE_PNP_INFO("U16:vendor;U16:device", pci, amdtemp, amdtemp_products, 231329e817fSWarner Losh nitems(amdtemp_products)); 232fc1f75e5SRui Paulo 233e49ec461SConrad Meyer static bool 234e49ec461SConrad Meyer amdtemp_match(device_t dev, const struct amdtemp_product **product_out) 235fc1f75e5SRui Paulo { 236fc1f75e5SRui Paulo int i; 237fc1f75e5SRui Paulo uint16_t vendor, devid; 238fc1f75e5SRui Paulo 239fc1f75e5SRui Paulo vendor = pci_get_vendor(dev); 240fc1f75e5SRui Paulo devid = pci_get_device(dev); 241fc1f75e5SRui Paulo 242a64bf59cSConrad Meyer for (i = 0; i < nitems(amdtemp_products); i++) { 243454e82d7SRui Paulo if (vendor == amdtemp_products[i].amdtemp_vendorid && 244e49ec461SConrad Meyer devid == amdtemp_products[i].amdtemp_deviceid) { 245e49ec461SConrad Meyer if (product_out != NULL) 246e49ec461SConrad Meyer *product_out = &amdtemp_products[i]; 247e49ec461SConrad Meyer return (true); 248fc1f75e5SRui Paulo } 249e49ec461SConrad Meyer } 250e49ec461SConrad Meyer return (false); 251fc1f75e5SRui Paulo } 252fc1f75e5SRui Paulo 253fc1f75e5SRui Paulo static void 254454e82d7SRui Paulo amdtemp_identify(driver_t *driver, device_t parent) 255fc1f75e5SRui Paulo { 256fc1f75e5SRui Paulo device_t child; 257fc1f75e5SRui Paulo 258fc1f75e5SRui Paulo /* Make sure we're not being doubly invoked. */ 259454e82d7SRui Paulo if (device_find_child(parent, "amdtemp", -1) != NULL) 260fc1f75e5SRui Paulo return; 261fc1f75e5SRui Paulo 262e49ec461SConrad Meyer if (amdtemp_match(parent, NULL)) { 263454e82d7SRui Paulo child = device_add_child(parent, "amdtemp", -1); 264fc1f75e5SRui Paulo if (child == NULL) 265454e82d7SRui Paulo device_printf(parent, "add amdtemp child failed\n"); 266fc1f75e5SRui Paulo } 267fc1f75e5SRui Paulo } 268fc1f75e5SRui Paulo 269fc1f75e5SRui Paulo static int 270454e82d7SRui Paulo amdtemp_probe(device_t dev) 271fc1f75e5SRui Paulo { 272fdfa6079SJung-uk Kim uint32_t family, model; 273fc1f75e5SRui Paulo 274a8de37b0SEitan Adler if (resource_disabled("amdtemp", 0)) 275a8de37b0SEitan Adler return (ENXIO); 276e49ec461SConrad Meyer if (!amdtemp_match(device_get_parent(dev), NULL)) 27740f7bccbSConrad Meyer return (ENXIO); 278a8de37b0SEitan Adler 279fdfa6079SJung-uk Kim family = CPUID_TO_FAMILY(cpu_id); 280fdfa6079SJung-uk Kim model = CPUID_TO_MODEL(cpu_id); 281a4165bbaSJung-uk Kim 282a4165bbaSJung-uk Kim switch (family) { 283a4165bbaSJung-uk Kim case 0x0f: 284fdfa6079SJung-uk Kim if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) || 285fdfa6079SJung-uk Kim (model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1)) 286a4165bbaSJung-uk Kim return (ENXIO); 287a4165bbaSJung-uk Kim break; 288a4165bbaSJung-uk Kim case 0x10: 289a4165bbaSJung-uk Kim case 0x11: 290074d80acSJung-uk Kim case 0x12: 291074d80acSJung-uk Kim case 0x14: 292074d80acSJung-uk Kim case 0x15: 2932b56f12bSChristian Brueffer case 0x16: 294a03d621bSConrad Meyer case 0x17: 295a4165bbaSJung-uk Kim break; 296a4165bbaSJung-uk Kim default: 297fc1f75e5SRui Paulo return (ENXIO); 298fc1f75e5SRui Paulo } 299a4165bbaSJung-uk Kim device_set_desc(dev, "AMD CPU On-Die Thermal Sensors"); 300fc1f75e5SRui Paulo 301fc1f75e5SRui Paulo return (BUS_PROBE_GENERIC); 302fc1f75e5SRui Paulo } 303fc1f75e5SRui Paulo 304fc1f75e5SRui Paulo static int 305454e82d7SRui Paulo amdtemp_attach(device_t dev) 306fc1f75e5SRui Paulo { 307074d80acSJung-uk Kim char tn[32]; 308074d80acSJung-uk Kim u_int regs[4]; 309e49ec461SConrad Meyer const struct amdtemp_product *product; 310e49ec461SConrad Meyer struct amdtemp_softc *sc; 311fc1f75e5SRui Paulo struct sysctl_ctx_list *sysctlctx; 312fc1f75e5SRui Paulo struct sysctl_oid *sysctlnode; 313a4165bbaSJung-uk Kim uint32_t cpuid, family, model; 314074d80acSJung-uk Kim u_int bid; 315074d80acSJung-uk Kim int erratum319, unit; 316e49ec461SConrad Meyer bool needsmn; 317fc1f75e5SRui Paulo 318e49ec461SConrad Meyer sc = device_get_softc(dev); 319074d80acSJung-uk Kim erratum319 = 0; 320e49ec461SConrad Meyer needsmn = false; 321fdfa6079SJung-uk Kim 322e49ec461SConrad Meyer if (!amdtemp_match(device_get_parent(dev), &product)) 323e49ec461SConrad Meyer return (ENXIO); 324e49ec461SConrad Meyer 325074d80acSJung-uk Kim cpuid = cpu_id; 326074d80acSJung-uk Kim family = CPUID_TO_FAMILY(cpuid); 327074d80acSJung-uk Kim model = CPUID_TO_MODEL(cpuid); 328e49ec461SConrad Meyer 329e49ec461SConrad Meyer /* 330e49ec461SConrad Meyer * This checks for the byzantine condition of running a heterogenous 331e49ec461SConrad Meyer * revision multi-socket system where the attach thread is potentially 332e49ec461SConrad Meyer * probing a remote socket's PCI device. 333e49ec461SConrad Meyer * 334e49ec461SConrad Meyer * Currently, such scenarios are unsupported on models using the SMN 335e49ec461SConrad Meyer * (because on those models, amdtemp(4) attaches to a different PCI 336e49ec461SConrad Meyer * device than the one that contains AMDTEMP_CPUID). 337e49ec461SConrad Meyer * 338e49ec461SConrad Meyer * The ancient 0x0F family of devices only supports this register from 339e49ec461SConrad Meyer * models 40h+. 340e49ec461SConrad Meyer */ 341e49ec461SConrad Meyer if (product->amdtemp_has_cpuid && (family > 0x0f || 342e49ec461SConrad Meyer (family == 0x0f && model >= 0x40))) { 343e49ec461SConrad Meyer cpuid = pci_read_config(device_get_parent(dev), AMDTEMP_CPUID, 344e49ec461SConrad Meyer 4); 345a4165bbaSJung-uk Kim family = CPUID_TO_FAMILY(cpuid); 346a4165bbaSJung-uk Kim model = CPUID_TO_MODEL(cpuid); 347fdfa6079SJung-uk Kim } 348a4165bbaSJung-uk Kim 349a4165bbaSJung-uk Kim switch (family) { 350a4165bbaSJung-uk Kim case 0x0f: 351a4165bbaSJung-uk Kim /* 352fdfa6079SJung-uk Kim * Thermaltrip Status Register 353fdfa6079SJung-uk Kim * 354fdfa6079SJung-uk Kim * - ThermSenseCoreSel 355fdfa6079SJung-uk Kim * 356fdfa6079SJung-uk Kim * Revision F & G: 0 - Core1, 1 - Core0 357fdfa6079SJung-uk Kim * Other: 0 - Core0, 1 - Core1 358fdfa6079SJung-uk Kim * 359fdfa6079SJung-uk Kim * - CurTmp 360a4165bbaSJung-uk Kim * 361a4165bbaSJung-uk Kim * Revision G: bits 23-14 362fdfa6079SJung-uk Kim * Other: bits 23-16 363a4165bbaSJung-uk Kim * 364fdfa6079SJung-uk Kim * XXX According to the BKDG, CurTmp, ThermSenseSel and 365fdfa6079SJung-uk Kim * ThermSenseCoreSel bits were introduced in Revision F 366fdfa6079SJung-uk Kim * but CurTmp seems working fine as early as Revision C. 367fdfa6079SJung-uk Kim * However, it is not clear whether ThermSenseSel and/or 368fdfa6079SJung-uk Kim * ThermSenseCoreSel work in undocumented cases as well. 369fdfa6079SJung-uk Kim * In fact, the Linux driver suggests it may not work but 370fdfa6079SJung-uk Kim * we just assume it does until we find otherwise. 371074d80acSJung-uk Kim * 372074d80acSJung-uk Kim * XXX According to Linux, CurTmp starts at -28C on 373074d80acSJung-uk Kim * Socket AM2 Revision G processors, which is not 374074d80acSJung-uk Kim * documented anywhere. 375fc1f75e5SRui Paulo */ 376074d80acSJung-uk Kim if (model >= 0x40) 377fdfa6079SJung-uk Kim sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP; 378074d80acSJung-uk Kim if (model >= 0x60 && model != 0xc1) { 379074d80acSJung-uk Kim do_cpuid(0x80000001, regs); 380074d80acSJung-uk Kim bid = (regs[1] >> 9) & 0x1f; 381074d80acSJung-uk Kim switch (model) { 382074d80acSJung-uk Kim case 0x68: /* Socket S1g1 */ 383074d80acSJung-uk Kim case 0x6c: 384074d80acSJung-uk Kim case 0x7c: 385074d80acSJung-uk Kim break; 386074d80acSJung-uk Kim case 0x6b: /* Socket AM2 and ASB1 (2 cores) */ 387074d80acSJung-uk Kim if (bid != 0x0b && bid != 0x0c) 388074d80acSJung-uk Kim sc->sc_flags |= 389074d80acSJung-uk Kim AMDTEMP_FLAG_ALT_OFFSET; 390074d80acSJung-uk Kim break; 391074d80acSJung-uk Kim case 0x6f: /* Socket AM2 and ASB1 (1 core) */ 392074d80acSJung-uk Kim case 0x7f: 393074d80acSJung-uk Kim if (bid != 0x07 && bid != 0x09 && 394074d80acSJung-uk Kim bid != 0x0c) 395074d80acSJung-uk Kim sc->sc_flags |= 396074d80acSJung-uk Kim AMDTEMP_FLAG_ALT_OFFSET; 397074d80acSJung-uk Kim break; 398074d80acSJung-uk Kim default: 399074d80acSJung-uk Kim sc->sc_flags |= AMDTEMP_FLAG_ALT_OFFSET; 400074d80acSJung-uk Kim } 401fdfa6079SJung-uk Kim sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT; 402fdfa6079SJung-uk Kim } 403a4165bbaSJung-uk Kim 404a4165bbaSJung-uk Kim /* 405a4165bbaSJung-uk Kim * There are two sensors per core. 406a4165bbaSJung-uk Kim */ 407a4165bbaSJung-uk Kim sc->sc_ntemps = 2; 408a4165bbaSJung-uk Kim 409a4165bbaSJung-uk Kim sc->sc_gettemp = amdtemp_gettemp0f; 410a4165bbaSJung-uk Kim break; 411a4165bbaSJung-uk Kim case 0x10: 412074d80acSJung-uk Kim /* 413074d80acSJung-uk Kim * Erratum 319 Inaccurate Temperature Measurement 414074d80acSJung-uk Kim * 415074d80acSJung-uk Kim * http://support.amd.com/us/Processor_TechDocs/41322.pdf 416074d80acSJung-uk Kim */ 417074d80acSJung-uk Kim do_cpuid(0x80000001, regs); 418074d80acSJung-uk Kim switch ((regs[1] >> 28) & 0xf) { 419074d80acSJung-uk Kim case 0: /* Socket F */ 420074d80acSJung-uk Kim erratum319 = 1; 421074d80acSJung-uk Kim break; 422074d80acSJung-uk Kim case 1: /* Socket AM2+ or AM3 */ 423074d80acSJung-uk Kim if ((pci_cfgregread(pci_get_bus(dev), 424074d80acSJung-uk Kim pci_get_slot(dev), 2, AMDTEMP_DRAM_CONF_HIGH, 2) & 425074d80acSJung-uk Kim AMDTEMP_DRAM_MODE_DDR3) != 0 || model > 0x04 || 426074d80acSJung-uk Kim (model == 0x04 && (cpuid & CPUID_STEPPING) >= 3)) 427074d80acSJung-uk Kim break; 428074d80acSJung-uk Kim /* XXX 00100F42h (RB-C2) exists in both formats. */ 429074d80acSJung-uk Kim erratum319 = 1; 430074d80acSJung-uk Kim break; 431074d80acSJung-uk Kim } 432074d80acSJung-uk Kim /* FALLTHROUGH */ 433a4165bbaSJung-uk Kim case 0x11: 434074d80acSJung-uk Kim case 0x12: 435074d80acSJung-uk Kim case 0x14: 436074d80acSJung-uk Kim case 0x15: 4372b56f12bSChristian Brueffer case 0x16: 438a4165bbaSJung-uk Kim sc->sc_ntemps = 1; 439e49ec461SConrad Meyer /* 440e49ec461SConrad Meyer * Some later (60h+) models of family 15h use a similar SMN 441e49ec461SConrad Meyer * network as family 17h. (However, the register index differs 442e49ec461SConrad Meyer * from 17h and the decoding matches other 10h-15h models, 443e49ec461SConrad Meyer * which differ from 17h.) 444e49ec461SConrad Meyer */ 445e49ec461SConrad Meyer if (family == 0x15 && model >= 0x60) { 446e49ec461SConrad Meyer sc->sc_gettemp = amdtemp_gettemp15hm60h; 447e49ec461SConrad Meyer needsmn = true; 448e49ec461SConrad Meyer } else 449a4165bbaSJung-uk Kim sc->sc_gettemp = amdtemp_gettemp; 450a4165bbaSJung-uk Kim break; 451a03d621bSConrad Meyer case 0x17: 452a03d621bSConrad Meyer sc->sc_ntemps = 1; 453a03d621bSConrad Meyer sc->sc_gettemp = amdtemp_gettemp17h; 454e49ec461SConrad Meyer needsmn = true; 455e49ec461SConrad Meyer break; 456e49ec461SConrad Meyer default: 457e49ec461SConrad Meyer device_printf(dev, "Bogus family 0x%x\n", family); 458e49ec461SConrad Meyer return (ENXIO); 459e49ec461SConrad Meyer } 460e49ec461SConrad Meyer 461e49ec461SConrad Meyer if (needsmn) { 462a03d621bSConrad Meyer sc->sc_smn = device_find_child( 463a03d621bSConrad Meyer device_get_parent(dev), "amdsmn", -1); 464a03d621bSConrad Meyer if (sc->sc_smn == NULL) { 465a03d621bSConrad Meyer if (bootverbose) 466a03d621bSConrad Meyer device_printf(dev, "No SMN device found\n"); 467a03d621bSConrad Meyer return (ENXIO); 468a03d621bSConrad Meyer } 469fc1f75e5SRui Paulo } 470fc1f75e5SRui Paulo 471a4165bbaSJung-uk Kim /* Find number of cores per package. */ 472a4165bbaSJung-uk Kim sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ? 473a4165bbaSJung-uk Kim (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1; 474a4165bbaSJung-uk Kim if (sc->sc_ncores > MAXCPU) 475a4165bbaSJung-uk Kim return (ENXIO); 476a4165bbaSJung-uk Kim 477074d80acSJung-uk Kim if (erratum319) 478074d80acSJung-uk Kim device_printf(dev, 479074d80acSJung-uk Kim "Erratum 319: temperature measurement may be inaccurate\n"); 480a4165bbaSJung-uk Kim if (bootverbose) 481a4165bbaSJung-uk Kim device_printf(dev, "Found %d cores and %d sensors.\n", 482a4165bbaSJung-uk Kim sc->sc_ncores, 483a4165bbaSJung-uk Kim sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1); 484454e82d7SRui Paulo 485fc1f75e5SRui Paulo /* 486454e82d7SRui Paulo * dev.amdtemp.N tree. 487fc1f75e5SRui Paulo */ 488074d80acSJung-uk Kim unit = device_get_unit(dev); 489074d80acSJung-uk Kim snprintf(tn, sizeof(tn), "dev.amdtemp.%d.sensor_offset", unit); 490074d80acSJung-uk Kim TUNABLE_INT_FETCH(tn, &sc->sc_offset); 491074d80acSJung-uk Kim 492fc1f75e5SRui Paulo sysctlctx = device_get_sysctl_ctx(dev); 493074d80acSJung-uk Kim SYSCTL_ADD_INT(sysctlctx, 494074d80acSJung-uk Kim SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 495074d80acSJung-uk Kim "sensor_offset", CTLFLAG_RW, &sc->sc_offset, 0, 496074d80acSJung-uk Kim "Temperature sensor offset"); 497fc1f75e5SRui Paulo sysctlnode = SYSCTL_ADD_NODE(sysctlctx, 498a4165bbaSJung-uk Kim SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 499074d80acSJung-uk Kim "core0", CTLFLAG_RD, 0, "Core 0"); 500fc1f75e5SRui Paulo 501fc1f75e5SRui Paulo SYSCTL_ADD_PROC(sysctlctx, 502fc1f75e5SRui Paulo SYSCTL_CHILDREN(sysctlnode), 503074d80acSJung-uk Kim OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD, 504074d80acSJung-uk Kim dev, CORE0_SENSOR0, amdtemp_sysctl, "IK", 505074d80acSJung-uk Kim "Core 0 / Sensor 0 temperature"); 506fc1f75e5SRui Paulo 507*c59b9a4fSConrad Meyer if (family == 0x17) 508*c59b9a4fSConrad Meyer amdtemp_probe_ccd_sensors17h(dev, model); 509*c59b9a4fSConrad Meyer else if (sc->sc_ntemps > 1) { 510fc1f75e5SRui Paulo SYSCTL_ADD_PROC(sysctlctx, 511fc1f75e5SRui Paulo SYSCTL_CHILDREN(sysctlnode), 512074d80acSJung-uk Kim OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD, 513074d80acSJung-uk Kim dev, CORE0_SENSOR1, amdtemp_sysctl, "IK", 514074d80acSJung-uk Kim "Core 0 / Sensor 1 temperature"); 515fc1f75e5SRui Paulo 516074d80acSJung-uk Kim if (sc->sc_ncores > 1) { 517fc1f75e5SRui Paulo sysctlnode = SYSCTL_ADD_NODE(sysctlctx, 518074d80acSJung-uk Kim SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 519074d80acSJung-uk Kim OID_AUTO, "core1", CTLFLAG_RD, 0, "Core 1"); 520fc1f75e5SRui Paulo 521fc1f75e5SRui Paulo SYSCTL_ADD_PROC(sysctlctx, 522fc1f75e5SRui Paulo SYSCTL_CHILDREN(sysctlnode), 523074d80acSJung-uk Kim OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD, 524074d80acSJung-uk Kim dev, CORE1_SENSOR0, amdtemp_sysctl, "IK", 525074d80acSJung-uk Kim "Core 1 / Sensor 0 temperature"); 526fc1f75e5SRui Paulo 527fc1f75e5SRui Paulo SYSCTL_ADD_PROC(sysctlctx, 528fc1f75e5SRui Paulo SYSCTL_CHILDREN(sysctlnode), 529074d80acSJung-uk Kim OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD, 530074d80acSJung-uk Kim dev, CORE1_SENSOR1, amdtemp_sysctl, "IK", 531074d80acSJung-uk Kim "Core 1 / Sensor 1 temperature"); 532074d80acSJung-uk Kim } 533a4165bbaSJung-uk Kim } 534a4165bbaSJung-uk Kim 535a4165bbaSJung-uk Kim /* 536a4165bbaSJung-uk Kim * Try to create dev.cpu sysctl entries and setup intrhook function. 537a4165bbaSJung-uk Kim * This is needed because the cpu driver may be loaded late on boot, 538a4165bbaSJung-uk Kim * after us. 539a4165bbaSJung-uk Kim */ 540a4165bbaSJung-uk Kim amdtemp_intrhook(dev); 541a4165bbaSJung-uk Kim sc->sc_ich.ich_func = amdtemp_intrhook; 542a4165bbaSJung-uk Kim sc->sc_ich.ich_arg = dev; 543a4165bbaSJung-uk Kim if (config_intrhook_establish(&sc->sc_ich) != 0) { 544a4165bbaSJung-uk Kim device_printf(dev, "config_intrhook_establish failed!\n"); 545a4165bbaSJung-uk Kim return (ENXIO); 546a4165bbaSJung-uk Kim } 547fc1f75e5SRui Paulo 548fc1f75e5SRui Paulo return (0); 549fc1f75e5SRui Paulo } 550fc1f75e5SRui Paulo 551fc1f75e5SRui Paulo void 552454e82d7SRui Paulo amdtemp_intrhook(void *arg) 553fc1f75e5SRui Paulo { 554454e82d7SRui Paulo struct amdtemp_softc *sc; 555fc1f75e5SRui Paulo struct sysctl_ctx_list *sysctlctx; 556a4165bbaSJung-uk Kim device_t dev = (device_t)arg; 557a4165bbaSJung-uk Kim device_t acpi, cpu, nexus; 558a4165bbaSJung-uk Kim amdsensor_t sensor; 559a4165bbaSJung-uk Kim int i; 560fc1f75e5SRui Paulo 561fc1f75e5SRui Paulo sc = device_get_softc(dev); 562fc1f75e5SRui Paulo 563fc1f75e5SRui Paulo /* 564fc1f75e5SRui Paulo * dev.cpu.N.temperature. 565fc1f75e5SRui Paulo */ 566fc1f75e5SRui Paulo nexus = device_find_child(root_bus, "nexus", 0); 567fc1f75e5SRui Paulo acpi = device_find_child(nexus, "acpi", 0); 568fc1f75e5SRui Paulo 569a4165bbaSJung-uk Kim for (i = 0; i < sc->sc_ncores; i++) { 570a4165bbaSJung-uk Kim if (sc->sc_sysctl_cpu[i] != NULL) 571a4165bbaSJung-uk Kim continue; 572fc1f75e5SRui Paulo cpu = device_find_child(acpi, "cpu", 573a4165bbaSJung-uk Kim device_get_unit(dev) * sc->sc_ncores + i); 574a4165bbaSJung-uk Kim if (cpu != NULL) { 575fc1f75e5SRui Paulo sysctlctx = device_get_sysctl_ctx(cpu); 576fc1f75e5SRui Paulo 577a4165bbaSJung-uk Kim sensor = sc->sc_ntemps > 1 ? 578074d80acSJung-uk Kim (i == 0 ? CORE0 : CORE1) : CORE0_SENSOR0; 579fc1f75e5SRui Paulo sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx, 580fc1f75e5SRui Paulo SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), 581fc1f75e5SRui Paulo OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD, 582a4165bbaSJung-uk Kim dev, sensor, amdtemp_sysctl, "IK", 583a4165bbaSJung-uk Kim "Current temparature"); 584fc1f75e5SRui Paulo } 585fc1f75e5SRui Paulo } 586a4165bbaSJung-uk Kim if (sc->sc_ich.ich_arg != NULL) 587fc1f75e5SRui Paulo config_intrhook_disestablish(&sc->sc_ich); 588fc1f75e5SRui Paulo } 589fc1f75e5SRui Paulo 590fc1f75e5SRui Paulo int 591454e82d7SRui Paulo amdtemp_detach(device_t dev) 592fc1f75e5SRui Paulo { 593454e82d7SRui Paulo struct amdtemp_softc *sc = device_get_softc(dev); 594a4165bbaSJung-uk Kim int i; 595fc1f75e5SRui Paulo 596a4165bbaSJung-uk Kim for (i = 0; i < sc->sc_ncores; i++) 597a4165bbaSJung-uk Kim if (sc->sc_sysctl_cpu[i] != NULL) 598fc1f75e5SRui Paulo sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0); 599fc1f75e5SRui Paulo 600454e82d7SRui Paulo /* NewBus removes the dev.amdtemp.N tree by itself. */ 601fc1f75e5SRui Paulo 602fc1f75e5SRui Paulo return (0); 603fc1f75e5SRui Paulo } 604fc1f75e5SRui Paulo 605fc1f75e5SRui Paulo static int 606454e82d7SRui Paulo amdtemp_sysctl(SYSCTL_HANDLER_ARGS) 607fc1f75e5SRui Paulo { 608fc1f75e5SRui Paulo device_t dev = (device_t)arg1; 609454e82d7SRui Paulo struct amdtemp_softc *sc = device_get_softc(dev); 610a4165bbaSJung-uk Kim amdsensor_t sensor = (amdsensor_t)arg2; 611a4165bbaSJung-uk Kim int32_t auxtemp[2], temp; 612fc1f75e5SRui Paulo int error; 613fc1f75e5SRui Paulo 614a4165bbaSJung-uk Kim switch (sensor) { 615fc1f75e5SRui Paulo case CORE0: 616074d80acSJung-uk Kim auxtemp[0] = sc->sc_gettemp(dev, CORE0_SENSOR0); 617074d80acSJung-uk Kim auxtemp[1] = sc->sc_gettemp(dev, CORE0_SENSOR1); 618fc1f75e5SRui Paulo temp = imax(auxtemp[0], auxtemp[1]); 619fc1f75e5SRui Paulo break; 620fc1f75e5SRui Paulo case CORE1: 621074d80acSJung-uk Kim auxtemp[0] = sc->sc_gettemp(dev, CORE1_SENSOR0); 622074d80acSJung-uk Kim auxtemp[1] = sc->sc_gettemp(dev, CORE1_SENSOR1); 623fc1f75e5SRui Paulo temp = imax(auxtemp[0], auxtemp[1]); 624fc1f75e5SRui Paulo break; 625fc1f75e5SRui Paulo default: 626a4165bbaSJung-uk Kim temp = sc->sc_gettemp(dev, sensor); 627fc1f75e5SRui Paulo break; 628fc1f75e5SRui Paulo } 629fc1f75e5SRui Paulo error = sysctl_handle_int(oidp, &temp, 0, req); 630fc1f75e5SRui Paulo 631fc1f75e5SRui Paulo return (error); 632fc1f75e5SRui Paulo } 633fc1f75e5SRui Paulo 6349d6672e1SLuiz Otavio O Souza #define AMDTEMP_ZERO_C_TO_K 2731 635a4165bbaSJung-uk Kim 636fc1f75e5SRui Paulo static int32_t 637454e82d7SRui Paulo amdtemp_gettemp0f(device_t dev, amdsensor_t sensor) 638fc1f75e5SRui Paulo { 639a4165bbaSJung-uk Kim struct amdtemp_softc *sc = device_get_softc(dev); 640074d80acSJung-uk Kim uint32_t mask, offset, temp; 641fc1f75e5SRui Paulo 642a4165bbaSJung-uk Kim /* Set Sensor/Core selector. */ 643074d80acSJung-uk Kim temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1); 644074d80acSJung-uk Kim temp &= ~(AMDTEMP_TTSR_SELCORE | AMDTEMP_TTSR_SELSENSOR); 645fc1f75e5SRui Paulo switch (sensor) { 646074d80acSJung-uk Kim case CORE0_SENSOR1: 647074d80acSJung-uk Kim temp |= AMDTEMP_TTSR_SELSENSOR; 6487ca2d97bSJung-uk Kim /* FALLTHROUGH */ 649074d80acSJung-uk Kim case CORE0_SENSOR0: 650a4165bbaSJung-uk Kim case CORE0: 651fdfa6079SJung-uk Kim if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0) 652074d80acSJung-uk Kim temp |= AMDTEMP_TTSR_SELCORE; 653fc1f75e5SRui Paulo break; 654074d80acSJung-uk Kim case CORE1_SENSOR1: 655074d80acSJung-uk Kim temp |= AMDTEMP_TTSR_SELSENSOR; 6567ca2d97bSJung-uk Kim /* FALLTHROUGH */ 657074d80acSJung-uk Kim case CORE1_SENSOR0: 658a4165bbaSJung-uk Kim case CORE1: 659fdfa6079SJung-uk Kim if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0) 660074d80acSJung-uk Kim temp |= AMDTEMP_TTSR_SELCORE; 661fc1f75e5SRui Paulo break; 662*c59b9a4fSConrad Meyer default: 663*c59b9a4fSConrad Meyer __unreachable(); 664fc1f75e5SRui Paulo } 665074d80acSJung-uk Kim pci_write_config(dev, AMDTEMP_THERMTP_STAT, temp, 1); 666a4165bbaSJung-uk Kim 667fdfa6079SJung-uk Kim mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc; 668074d80acSJung-uk Kim offset = (sc->sc_flags & AMDTEMP_FLAG_ALT_OFFSET) != 0 ? 28 : 49; 669074d80acSJung-uk Kim temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4); 670074d80acSJung-uk Kim temp = ((temp >> 14) & mask) * 5 / 2; 671074d80acSJung-uk Kim temp += AMDTEMP_ZERO_C_TO_K + (sc->sc_offset - offset) * 10; 672454e82d7SRui Paulo 673454e82d7SRui Paulo return (temp); 674454e82d7SRui Paulo } 675454e82d7SRui Paulo 676e49ec461SConrad Meyer static uint32_t 67702f70002SConrad Meyer amdtemp_decode_fam10h_to_17h(int32_t sc_offset, uint32_t val, bool minus49) 678e49ec461SConrad Meyer { 679e49ec461SConrad Meyer uint32_t temp; 680e49ec461SConrad Meyer 681e49ec461SConrad Meyer /* Convert raw register subfield units (0.125C) to units of 0.1C. */ 68202f70002SConrad Meyer temp = (val & AMDTEMP_REPTMP10H_CURTMP_MASK) * 5 / 4; 68302f70002SConrad Meyer 68402f70002SConrad Meyer if (minus49) 68502f70002SConrad Meyer temp -= AMDTEMP_CURTMP_RANGE_ADJUST; 68602f70002SConrad Meyer 68702f70002SConrad Meyer temp += AMDTEMP_ZERO_C_TO_K + sc_offset * 10; 68802f70002SConrad Meyer return (temp); 68902f70002SConrad Meyer } 69002f70002SConrad Meyer 69102f70002SConrad Meyer static uint32_t 69202f70002SConrad Meyer amdtemp_decode_fam10h_to_16h(int32_t sc_offset, uint32_t val) 69302f70002SConrad Meyer { 69402f70002SConrad Meyer bool minus49; 695e49ec461SConrad Meyer 696e49ec461SConrad Meyer /* 697e49ec461SConrad Meyer * On Family 15h and higher, if CurTmpTjSel is 11b, the range is 698e49ec461SConrad Meyer * adjusted down by 49.0 degrees Celsius. (This adjustment is not 699e49ec461SConrad Meyer * documented in BKDGs prior to family 15h model 00h.) 700e49ec461SConrad Meyer */ 70102f70002SConrad Meyer minus49 = (CPUID_TO_FAMILY(cpu_id) >= 0x15 && 702e49ec461SConrad Meyer ((val >> AMDTEMP_REPTMP10H_TJSEL_SHIFT) & 70302f70002SConrad Meyer AMDTEMP_REPTMP10H_TJSEL_MASK) == 0x3); 704e49ec461SConrad Meyer 70502f70002SConrad Meyer return (amdtemp_decode_fam10h_to_17h(sc_offset, 70602f70002SConrad Meyer val >> AMDTEMP_REPTMP10H_CURTMP_SHIFT, minus49)); 70702f70002SConrad Meyer } 70802f70002SConrad Meyer 70902f70002SConrad Meyer static uint32_t 71002f70002SConrad Meyer amdtemp_decode_fam17h_tctl(int32_t sc_offset, uint32_t val) 71102f70002SConrad Meyer { 71202f70002SConrad Meyer bool minus49; 71302f70002SConrad Meyer 71402f70002SConrad Meyer minus49 = ((val & AMDTEMP_17H_CUR_TMP_RANGE_SEL) != 0); 71502f70002SConrad Meyer return (amdtemp_decode_fam10h_to_17h(sc_offset, 71602f70002SConrad Meyer val >> AMDTEMP_REPTMP10H_CURTMP_SHIFT, minus49)); 717e49ec461SConrad Meyer } 718e49ec461SConrad Meyer 719454e82d7SRui Paulo static int32_t 720454e82d7SRui Paulo amdtemp_gettemp(device_t dev, amdsensor_t sensor) 721454e82d7SRui Paulo { 722074d80acSJung-uk Kim struct amdtemp_softc *sc = device_get_softc(dev); 723454e82d7SRui Paulo uint32_t temp; 724a4165bbaSJung-uk Kim 725a4165bbaSJung-uk Kim temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4); 726e49ec461SConrad Meyer return (amdtemp_decode_fam10h_to_16h(sc->sc_offset, temp)); 727e49ec461SConrad Meyer } 728fc1f75e5SRui Paulo 729e49ec461SConrad Meyer static int32_t 730e49ec461SConrad Meyer amdtemp_gettemp15hm60h(device_t dev, amdsensor_t sensor) 731e49ec461SConrad Meyer { 732e49ec461SConrad Meyer struct amdtemp_softc *sc = device_get_softc(dev); 733e49ec461SConrad Meyer uint32_t val; 734e49ec461SConrad Meyer int error; 735e49ec461SConrad Meyer 736e49ec461SConrad Meyer error = amdsmn_read(sc->sc_smn, AMDTEMP_15H_M60H_REPTMP_CTRL, &val); 737e49ec461SConrad Meyer KASSERT(error == 0, ("amdsmn_read")); 738e49ec461SConrad Meyer return (amdtemp_decode_fam10h_to_16h(sc->sc_offset, val)); 739fc1f75e5SRui Paulo } 740a03d621bSConrad Meyer 741a03d621bSConrad Meyer static int32_t 742a03d621bSConrad Meyer amdtemp_gettemp17h(device_t dev, amdsensor_t sensor) 743a03d621bSConrad Meyer { 744a03d621bSConrad Meyer struct amdtemp_softc *sc = device_get_softc(dev); 74502f70002SConrad Meyer uint32_t val; 746a03d621bSConrad Meyer int error; 747a03d621bSConrad Meyer 748*c59b9a4fSConrad Meyer switch (sensor) { 749*c59b9a4fSConrad Meyer case CORE0_SENSOR0: 750*c59b9a4fSConrad Meyer /* Tctl */ 751fbd5d782SConrad Meyer error = amdsmn_read(sc->sc_smn, AMDTEMP_17H_CUR_TMP, &val); 752a03d621bSConrad Meyer KASSERT(error == 0, ("amdsmn_read")); 75302f70002SConrad Meyer return (amdtemp_decode_fam17h_tctl(sc->sc_offset, val)); 754*c59b9a4fSConrad Meyer case CCD_BASE ... CCD_MAX: 755*c59b9a4fSConrad Meyer /* Tccd<N> */ 756*c59b9a4fSConrad Meyer error = amdsmn_read(sc->sc_smn, AMDTEMP_17H_CCD_TMP_BASE + 757*c59b9a4fSConrad Meyer (((int)sensor - CCD_BASE) * sizeof(val)), &val); 758*c59b9a4fSConrad Meyer KASSERT(error == 0, ("amdsmn_read2")); 759*c59b9a4fSConrad Meyer KASSERT((val & AMDTEMP_17H_CCD_TMP_VALID) != 0, 760*c59b9a4fSConrad Meyer ("sensor %d: not valid", (int)sensor)); 761*c59b9a4fSConrad Meyer return (amdtemp_decode_fam10h_to_17h(sc->sc_offset, val, true)); 762*c59b9a4fSConrad Meyer default: 763*c59b9a4fSConrad Meyer #if 0 764*c59b9a4fSConrad Meyer KASSERT(false, ("%s: invalid sensor %d", __func__, 765*c59b9a4fSConrad Meyer (int)sensor)); 766*c59b9a4fSConrad Meyer return (-1); 767*c59b9a4fSConrad Meyer #endif 768*c59b9a4fSConrad Meyer __unreachable(); 769*c59b9a4fSConrad Meyer } 770*c59b9a4fSConrad Meyer } 771*c59b9a4fSConrad Meyer 772*c59b9a4fSConrad Meyer static void 773*c59b9a4fSConrad Meyer amdtemp_probe_ccd_sensors17h(device_t dev, uint32_t model) 774*c59b9a4fSConrad Meyer { 775*c59b9a4fSConrad Meyer char sensor_name[16], sensor_descr[32]; 776*c59b9a4fSConrad Meyer struct amdtemp_softc *sc; 777*c59b9a4fSConrad Meyer uint32_t maxreg, i, val; 778*c59b9a4fSConrad Meyer int error; 779*c59b9a4fSConrad Meyer 780*c59b9a4fSConrad Meyer switch (model) { 781*c59b9a4fSConrad Meyer case 0x00 ... 0x1f: /* Zen1, Zen+ */ 782*c59b9a4fSConrad Meyer maxreg = 4; 783*c59b9a4fSConrad Meyer break; 784*c59b9a4fSConrad Meyer case 0x30 ... 0x3f: /* Zen2 TR/Epyc */ 785*c59b9a4fSConrad Meyer case 0x70 ... 0x7f: /* Zen2 Ryzen */ 786*c59b9a4fSConrad Meyer maxreg = 8; 787*c59b9a4fSConrad Meyer _Static_assert((int)NUM_CCDS >= 8, ""); 788*c59b9a4fSConrad Meyer break; 789*c59b9a4fSConrad Meyer default: 790*c59b9a4fSConrad Meyer device_printf(dev, 791*c59b9a4fSConrad Meyer "Unrecognized Family 17h Model: %02xh\n", model); 792*c59b9a4fSConrad Meyer return; 793*c59b9a4fSConrad Meyer } 794*c59b9a4fSConrad Meyer 795*c59b9a4fSConrad Meyer sc = device_get_softc(dev); 796*c59b9a4fSConrad Meyer for (i = 0; i < maxreg; i++) { 797*c59b9a4fSConrad Meyer error = amdsmn_read(sc->sc_smn, AMDTEMP_17H_CCD_TMP_BASE + 798*c59b9a4fSConrad Meyer (i * sizeof(val)), &val); 799*c59b9a4fSConrad Meyer if (error != 0) 800*c59b9a4fSConrad Meyer continue; 801*c59b9a4fSConrad Meyer if ((val & AMDTEMP_17H_CCD_TMP_VALID) == 0) 802*c59b9a4fSConrad Meyer continue; 803*c59b9a4fSConrad Meyer 804*c59b9a4fSConrad Meyer snprintf(sensor_name, sizeof(sensor_name), "ccd%u", i); 805*c59b9a4fSConrad Meyer snprintf(sensor_descr, sizeof(sensor_descr), 806*c59b9a4fSConrad Meyer "CCD %u temperature (Tccd%u)", i, i); 807*c59b9a4fSConrad Meyer 808*c59b9a4fSConrad Meyer SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 809*c59b9a4fSConrad Meyer SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 810*c59b9a4fSConrad Meyer sensor_name, CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 811*c59b9a4fSConrad Meyer dev, CCD_BASE + i, amdtemp_sysctl, "IK", sensor_descr); 812*c59b9a4fSConrad Meyer } 813a03d621bSConrad Meyer } 814