1fc1f75e5SRui Paulo /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 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. 8c59b9a4fSConrad 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/param.h> 38fc1f75e5SRui Paulo #include <sys/bus.h> 39fc1f75e5SRui Paulo #include <sys/conf.h> 40fc1f75e5SRui Paulo #include <sys/kernel.h> 416c101ed7SAlexander Motin #include <sys/lock.h> 42a4165bbaSJung-uk Kim #include <sys/module.h> 436c101ed7SAlexander Motin #include <sys/mutex.h> 44fc1f75e5SRui Paulo #include <sys/sysctl.h> 45a4165bbaSJung-uk Kim #include <sys/systm.h> 46fc1f75e5SRui Paulo 47fdfa6079SJung-uk Kim #include <machine/cpufunc.h> 48fc1f75e5SRui Paulo #include <machine/md_var.h> 49a4165bbaSJung-uk Kim #include <machine/specialreg.h> 50fc1f75e5SRui Paulo 51fc1f75e5SRui Paulo #include <dev/pci/pcivar.h> 52074d80acSJung-uk Kim #include <x86/pci_cfgreg.h> 53fc1f75e5SRui Paulo 54a03d621bSConrad Meyer #include <dev/amdsmn/amdsmn.h> 55a03d621bSConrad Meyer 56fc1f75e5SRui Paulo typedef enum { 57074d80acSJung-uk Kim CORE0_SENSOR0, 58074d80acSJung-uk Kim CORE0_SENSOR1, 59074d80acSJung-uk Kim CORE1_SENSOR0, 60074d80acSJung-uk Kim CORE1_SENSOR1, 61fc1f75e5SRui Paulo CORE0, 62c59b9a4fSConrad Meyer CORE1, 63c59b9a4fSConrad Meyer CCD1, 64c59b9a4fSConrad Meyer CCD_BASE = CCD1, 65c59b9a4fSConrad Meyer CCD2, 66c59b9a4fSConrad Meyer CCD3, 67c59b9a4fSConrad Meyer CCD4, 68c59b9a4fSConrad Meyer CCD5, 69c59b9a4fSConrad Meyer CCD6, 70c59b9a4fSConrad Meyer CCD7, 71c59b9a4fSConrad Meyer CCD8, 7251c69c86SXin LI CCD9, 7351c69c86SXin LI CCD10, 7451c69c86SXin LI CCD11, 7551c69c86SXin LI CCD12, 7651c69c86SXin LI CCD_MAX = CCD12, 77c59b9a4fSConrad Meyer NUM_CCDS = CCD_MAX - CCD_BASE + 1, 78454e82d7SRui Paulo } amdsensor_t; 79454e82d7SRui Paulo 80454e82d7SRui Paulo struct amdtemp_softc { 81a4165bbaSJung-uk Kim int sc_ncores; 82454e82d7SRui Paulo int sc_ntemps; 83fdfa6079SJung-uk Kim int sc_flags; 84074d80acSJung-uk Kim #define AMDTEMP_FLAG_CS_SWAP 0x01 /* ThermSenseCoreSel is inverted. */ 85074d80acSJung-uk Kim #define AMDTEMP_FLAG_CT_10BIT 0x02 /* CurTmp is 10-bit wide. */ 86074d80acSJung-uk Kim #define AMDTEMP_FLAG_ALT_OFFSET 0x04 /* CurTmp starts at -28C. */ 87074d80acSJung-uk Kim int32_t sc_offset; 88323a94afSAkio Morita int32_t sc_temp_base; 89454e82d7SRui Paulo int32_t (*sc_gettemp)(device_t, amdsensor_t); 90a4165bbaSJung-uk Kim struct sysctl_oid *sc_sysctl_cpu[MAXCPU]; 91a4165bbaSJung-uk Kim struct intr_config_hook sc_ich; 92a03d621bSConrad Meyer device_t sc_smn; 936c101ed7SAlexander Motin struct mtx sc_lock; 94454e82d7SRui Paulo }; 95454e82d7SRui Paulo 96e49ec461SConrad Meyer /* 97e49ec461SConrad Meyer * N.B. The numbers in macro names below are significant and represent CPU 98e49ec461SConrad Meyer * family and model numbers. Do not make up fictitious family or model numbers 99e49ec461SConrad Meyer * when adding support for new devices. 100e49ec461SConrad Meyer */ 101454e82d7SRui Paulo #define VENDORID_AMD 0x1022 102454e82d7SRui Paulo #define DEVICEID_AMD_MISC0F 0x1103 103454e82d7SRui Paulo #define DEVICEID_AMD_MISC10 0x1203 104454e82d7SRui Paulo #define DEVICEID_AMD_MISC11 0x1303 105074d80acSJung-uk Kim #define DEVICEID_AMD_MISC14 0x1703 106074d80acSJung-uk Kim #define DEVICEID_AMD_MISC15 0x1603 107e49ec461SConrad Meyer #define DEVICEID_AMD_MISC15_M10H 0x1403 108e49ec461SConrad Meyer #define DEVICEID_AMD_MISC15_M30H 0x141d 109e49ec461SConrad Meyer #define DEVICEID_AMD_MISC15_M60H_ROOT 0x1576 1102b56f12bSChristian Brueffer #define DEVICEID_AMD_MISC16 0x1533 111df20515dSLuiz Otavio O Souza #define DEVICEID_AMD_MISC16_M30H 0x1583 1129d49c422SConrad Meyer #define DEVICEID_AMD_HOSTB17H_ROOT 0x1450 1139d49c422SConrad Meyer #define DEVICEID_AMD_HOSTB17H_M10H_ROOT 0x15d0 114ea6189d3SConrad Meyer #define DEVICEID_AMD_HOSTB17H_M30H_ROOT 0x1480 /* Also M70H, F19H M00H/M20H */ 1155b505170SConrad Meyer #define DEVICEID_AMD_HOSTB17H_M60H_ROOT 0x1630 11651c69c86SXin LI #define DEVICEID_AMD_HOSTB19H_M10H_ROOT 0x14a4 117323a94afSAkio Morita #define DEVICEID_AMD_HOSTB19H_M60H_ROOT 0x14d8 118*ef3f8aa0SOliver Fromme #define DEVICEID_AMD_HOSTB19H_M70H_ROOT 0x14e8 119454e82d7SRui Paulo 120e49ec461SConrad Meyer static const struct amdtemp_product { 121454e82d7SRui Paulo uint16_t amdtemp_vendorid; 122454e82d7SRui Paulo uint16_t amdtemp_deviceid; 123e49ec461SConrad Meyer /* 124e49ec461SConrad Meyer * 0xFC register is only valid on the D18F3 PCI device; SMN temp 125e49ec461SConrad Meyer * drivers do not attach to that device. 126e49ec461SConrad Meyer */ 127e49ec461SConrad Meyer bool amdtemp_has_cpuid; 128454e82d7SRui Paulo } amdtemp_products[] = { 129e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC0F, true }, 130e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC10, true }, 131e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC11, true }, 132e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC14, true }, 133e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC15, true }, 134e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC15_M10H, true }, 135e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC15_M30H, true }, 136e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC15_M60H_ROOT, false }, 137e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC16, true }, 138e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_MISC16_M30H, true }, 139e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_HOSTB17H_ROOT, false }, 140e49ec461SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_HOSTB17H_M10H_ROOT, false }, 14185dbddbeSConrad Meyer { VENDORID_AMD, DEVICEID_AMD_HOSTB17H_M30H_ROOT, false }, 1425b505170SConrad Meyer { VENDORID_AMD, DEVICEID_AMD_HOSTB17H_M60H_ROOT, false }, 14351c69c86SXin LI { VENDORID_AMD, DEVICEID_AMD_HOSTB19H_M10H_ROOT, false }, 144323a94afSAkio Morita { VENDORID_AMD, DEVICEID_AMD_HOSTB19H_M60H_ROOT, false }, 145*ef3f8aa0SOliver Fromme { VENDORID_AMD, DEVICEID_AMD_HOSTB19H_M70H_ROOT, false }, 146454e82d7SRui Paulo }; 147454e82d7SRui Paulo 148454e82d7SRui Paulo /* 149e49ec461SConrad Meyer * Reported Temperature Control Register, family 0Fh-15h (some models), 16h. 150454e82d7SRui Paulo */ 151a4165bbaSJung-uk Kim #define AMDTEMP_REPTMP_CTRL 0xa4 152454e82d7SRui Paulo 153e49ec461SConrad Meyer #define AMDTEMP_REPTMP10H_CURTMP_MASK 0x7ff 154e49ec461SConrad Meyer #define AMDTEMP_REPTMP10H_CURTMP_SHIFT 21 155e49ec461SConrad Meyer #define AMDTEMP_REPTMP10H_TJSEL_MASK 0x3 156e49ec461SConrad Meyer #define AMDTEMP_REPTMP10H_TJSEL_SHIFT 16 157e49ec461SConrad Meyer 158e49ec461SConrad Meyer /* 159e49ec461SConrad Meyer * Reported Temperature, Family 15h, M60+ 160e49ec461SConrad Meyer * 161e49ec461SConrad Meyer * Same register bit definitions as other Family 15h CPUs, but access is 162e49ec461SConrad Meyer * indirect via SMN, like Family 17h. 163e49ec461SConrad Meyer */ 164e49ec461SConrad Meyer #define AMDTEMP_15H_M60H_REPTMP_CTRL 0xd8200ca4 165e49ec461SConrad Meyer 166454e82d7SRui Paulo /* 167a03d621bSConrad Meyer * Reported Temperature, Family 17h 168fbd5d782SConrad Meyer * 169fbd5d782SConrad Meyer * According to AMD OSRR for 17H, section 4.2.1, bits 31-21 of this register 170fbd5d782SConrad Meyer * provide the current temp. bit 19, when clear, means the temp is reported in 171fbd5d782SConrad Meyer * a range 0.."225C" (probable typo for 255C), and when set changes the range 172fbd5d782SConrad Meyer * to -49..206C. 173a03d621bSConrad Meyer */ 174a03d621bSConrad Meyer #define AMDTEMP_17H_CUR_TMP 0x59800 175c59b9a4fSConrad Meyer #define AMDTEMP_17H_CUR_TMP_RANGE_SEL (1u << 19) 176c59b9a4fSConrad Meyer /* 177c1cbabe8SVal Packett * Bits 16-17, when set, mean that CUR_TMP is read-write. When it is, the 178c1cbabe8SVal Packett * 49 degree offset should apply as well. This was revealed in a Linux 179c1cbabe8SVal Packett * patch from an AMD employee. 180c1cbabe8SVal Packett */ 181c1cbabe8SVal Packett #define AMDTEMP_17H_CUR_TMP_TJ_SEL ((1u << 17) | (1u << 16)) 182c1cbabe8SVal Packett /* 183c59b9a4fSConrad Meyer * The following register set was discovered experimentally by Ondrej Čerman 184c59b9a4fSConrad Meyer * and collaborators, but is not (yet) documented in a PPR/OSRR (other than 185c59b9a4fSConrad Meyer * the M70H PPR SMN memory map showing [0x59800, +0x314] as allocated to 186c59b9a4fSConrad Meyer * SMU::THM). It seems plausible and the Linux sensor folks have adopted it. 187c59b9a4fSConrad Meyer */ 188c59b9a4fSConrad Meyer #define AMDTEMP_17H_CCD_TMP_BASE 0x59954 189c59b9a4fSConrad Meyer #define AMDTEMP_17H_CCD_TMP_VALID (1u << 11) 190e49ec461SConrad Meyer 19151c69c86SXin LI #define AMDTEMP_ZEN4_10H_CCD_TMP_BASE 0x59b00 192323a94afSAkio Morita #define AMDTEMP_ZEN4_CCD_TMP_BASE 0x59b08 193323a94afSAkio Morita 194e49ec461SConrad Meyer /* 195e49ec461SConrad Meyer * AMD temperature range adjustment, in deciKelvins (i.e., 49.0 Celsius). 196e49ec461SConrad Meyer */ 197e49ec461SConrad Meyer #define AMDTEMP_CURTMP_RANGE_ADJUST 490 198a03d621bSConrad Meyer 199a03d621bSConrad Meyer /* 200074d80acSJung-uk Kim * Thermaltrip Status Register (Family 0Fh only) 201454e82d7SRui Paulo */ 202a4165bbaSJung-uk Kim #define AMDTEMP_THERMTP_STAT 0xe4 203074d80acSJung-uk Kim #define AMDTEMP_TTSR_SELCORE 0x04 204074d80acSJung-uk Kim #define AMDTEMP_TTSR_SELSENSOR 0x40 205074d80acSJung-uk Kim 206074d80acSJung-uk Kim /* 207074d80acSJung-uk Kim * DRAM Configuration High Register 208074d80acSJung-uk Kim */ 209074d80acSJung-uk Kim #define AMDTEMP_DRAM_CONF_HIGH 0x94 /* Function 2 */ 210074d80acSJung-uk Kim #define AMDTEMP_DRAM_MODE_DDR3 0x0100 211454e82d7SRui Paulo 212a4165bbaSJung-uk Kim /* 213a4165bbaSJung-uk Kim * CPU Family/Model Register 214a4165bbaSJung-uk Kim */ 215a4165bbaSJung-uk Kim #define AMDTEMP_CPUID 0xfc 216fc1f75e5SRui Paulo 217fc1f75e5SRui Paulo /* 218fc1f75e5SRui Paulo * Device methods. 219fc1f75e5SRui Paulo */ 220454e82d7SRui Paulo static void amdtemp_identify(driver_t *driver, device_t parent); 221454e82d7SRui Paulo static int amdtemp_probe(device_t dev); 222454e82d7SRui Paulo static int amdtemp_attach(device_t dev); 223454e82d7SRui Paulo static void amdtemp_intrhook(void *arg); 224454e82d7SRui Paulo static int amdtemp_detach(device_t dev); 225454e82d7SRui Paulo static int32_t amdtemp_gettemp0f(device_t dev, amdsensor_t sensor); 226454e82d7SRui Paulo static int32_t amdtemp_gettemp(device_t dev, amdsensor_t sensor); 227e49ec461SConrad Meyer static int32_t amdtemp_gettemp15hm60h(device_t dev, amdsensor_t sensor); 228a03d621bSConrad Meyer static int32_t amdtemp_gettemp17h(device_t dev, amdsensor_t sensor); 229c59b9a4fSConrad Meyer static void amdtemp_probe_ccd_sensors17h(device_t dev, uint32_t model); 230ea6189d3SConrad Meyer static void amdtemp_probe_ccd_sensors19h(device_t dev, uint32_t model); 231454e82d7SRui Paulo static int amdtemp_sysctl(SYSCTL_HANDLER_ARGS); 232fc1f75e5SRui Paulo 233454e82d7SRui Paulo static device_method_t amdtemp_methods[] = { 234fc1f75e5SRui Paulo /* Device interface */ 235454e82d7SRui Paulo DEVMETHOD(device_identify, amdtemp_identify), 236454e82d7SRui Paulo DEVMETHOD(device_probe, amdtemp_probe), 237454e82d7SRui Paulo DEVMETHOD(device_attach, amdtemp_attach), 238454e82d7SRui Paulo DEVMETHOD(device_detach, amdtemp_detach), 239fc1f75e5SRui Paulo 24061bfd867SSofian Brabez DEVMETHOD_END 241fc1f75e5SRui Paulo }; 242fc1f75e5SRui Paulo 243454e82d7SRui Paulo static driver_t amdtemp_driver = { 244454e82d7SRui Paulo "amdtemp", 245454e82d7SRui Paulo amdtemp_methods, 246454e82d7SRui Paulo sizeof(struct amdtemp_softc), 247fc1f75e5SRui Paulo }; 248fc1f75e5SRui Paulo 24983a273efSJohn Baldwin DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, NULL, NULL); 250a03d621bSConrad Meyer MODULE_VERSION(amdtemp, 1); 251a03d621bSConrad Meyer MODULE_DEPEND(amdtemp, amdsmn, 1, 1, 1); 252a64bf59cSConrad Meyer MODULE_PNP_INFO("U16:vendor;U16:device", pci, amdtemp, amdtemp_products, 253329e817fSWarner Losh nitems(amdtemp_products)); 254fc1f75e5SRui Paulo 255e49ec461SConrad Meyer static bool 256e49ec461SConrad Meyer amdtemp_match(device_t dev, const struct amdtemp_product **product_out) 257fc1f75e5SRui Paulo { 258fc1f75e5SRui Paulo int i; 259fc1f75e5SRui Paulo uint16_t vendor, devid; 260fc1f75e5SRui Paulo 261fc1f75e5SRui Paulo vendor = pci_get_vendor(dev); 262fc1f75e5SRui Paulo devid = pci_get_device(dev); 263fc1f75e5SRui Paulo 264a64bf59cSConrad Meyer for (i = 0; i < nitems(amdtemp_products); i++) { 265454e82d7SRui Paulo if (vendor == amdtemp_products[i].amdtemp_vendorid && 266e49ec461SConrad Meyer devid == amdtemp_products[i].amdtemp_deviceid) { 267e49ec461SConrad Meyer if (product_out != NULL) 268e49ec461SConrad Meyer *product_out = &amdtemp_products[i]; 269e49ec461SConrad Meyer return (true); 270fc1f75e5SRui Paulo } 271e49ec461SConrad Meyer } 272e49ec461SConrad Meyer return (false); 273fc1f75e5SRui Paulo } 274fc1f75e5SRui Paulo 275fc1f75e5SRui Paulo static void 276454e82d7SRui Paulo amdtemp_identify(driver_t *driver, device_t parent) 277fc1f75e5SRui Paulo { 278fc1f75e5SRui Paulo device_t child; 279fc1f75e5SRui Paulo 280fc1f75e5SRui Paulo /* Make sure we're not being doubly invoked. */ 281454e82d7SRui Paulo if (device_find_child(parent, "amdtemp", -1) != NULL) 282fc1f75e5SRui Paulo return; 283fc1f75e5SRui Paulo 284e49ec461SConrad Meyer if (amdtemp_match(parent, NULL)) { 2855b56413dSWarner Losh child = device_add_child(parent, "amdtemp", DEVICE_UNIT_ANY); 286fc1f75e5SRui Paulo if (child == NULL) 287454e82d7SRui Paulo device_printf(parent, "add amdtemp child failed\n"); 288fc1f75e5SRui Paulo } 289fc1f75e5SRui Paulo } 290fc1f75e5SRui Paulo 291fc1f75e5SRui Paulo static int 292454e82d7SRui Paulo amdtemp_probe(device_t dev) 293fc1f75e5SRui Paulo { 294fdfa6079SJung-uk Kim uint32_t family, model; 295fc1f75e5SRui Paulo 296a8de37b0SEitan Adler if (resource_disabled("amdtemp", 0)) 297a8de37b0SEitan Adler return (ENXIO); 298e49ec461SConrad Meyer if (!amdtemp_match(device_get_parent(dev), NULL)) 29940f7bccbSConrad Meyer return (ENXIO); 300a8de37b0SEitan Adler 301fdfa6079SJung-uk Kim family = CPUID_TO_FAMILY(cpu_id); 302fdfa6079SJung-uk Kim model = CPUID_TO_MODEL(cpu_id); 303a4165bbaSJung-uk Kim 304a4165bbaSJung-uk Kim switch (family) { 305a4165bbaSJung-uk Kim case 0x0f: 306fdfa6079SJung-uk Kim if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) || 307fdfa6079SJung-uk Kim (model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1)) 308a4165bbaSJung-uk Kim return (ENXIO); 309a4165bbaSJung-uk Kim break; 310a4165bbaSJung-uk Kim case 0x10: 311a4165bbaSJung-uk Kim case 0x11: 312074d80acSJung-uk Kim case 0x12: 313074d80acSJung-uk Kim case 0x14: 314074d80acSJung-uk Kim case 0x15: 3152b56f12bSChristian Brueffer case 0x16: 316a03d621bSConrad Meyer case 0x17: 317ea6189d3SConrad Meyer case 0x19: 318a4165bbaSJung-uk Kim break; 319a4165bbaSJung-uk Kim default: 320fc1f75e5SRui Paulo return (ENXIO); 321fc1f75e5SRui Paulo } 322a4165bbaSJung-uk Kim device_set_desc(dev, "AMD CPU On-Die Thermal Sensors"); 323fc1f75e5SRui Paulo 324fc1f75e5SRui Paulo return (BUS_PROBE_GENERIC); 325fc1f75e5SRui Paulo } 326fc1f75e5SRui Paulo 327fc1f75e5SRui Paulo static int 328454e82d7SRui Paulo amdtemp_attach(device_t dev) 329fc1f75e5SRui Paulo { 330074d80acSJung-uk Kim char tn[32]; 331074d80acSJung-uk Kim u_int regs[4]; 332e49ec461SConrad Meyer const struct amdtemp_product *product; 333e49ec461SConrad Meyer struct amdtemp_softc *sc; 334fc1f75e5SRui Paulo struct sysctl_ctx_list *sysctlctx; 335fc1f75e5SRui Paulo struct sysctl_oid *sysctlnode; 336a4165bbaSJung-uk Kim uint32_t cpuid, family, model; 337074d80acSJung-uk Kim u_int bid; 338074d80acSJung-uk Kim int erratum319, unit; 339e49ec461SConrad Meyer bool needsmn; 340fc1f75e5SRui Paulo 341e49ec461SConrad Meyer sc = device_get_softc(dev); 342074d80acSJung-uk Kim erratum319 = 0; 343e49ec461SConrad Meyer needsmn = false; 344fdfa6079SJung-uk Kim 345e49ec461SConrad Meyer if (!amdtemp_match(device_get_parent(dev), &product)) 346e49ec461SConrad Meyer return (ENXIO); 347e49ec461SConrad Meyer 348074d80acSJung-uk Kim cpuid = cpu_id; 349074d80acSJung-uk Kim family = CPUID_TO_FAMILY(cpuid); 350074d80acSJung-uk Kim model = CPUID_TO_MODEL(cpuid); 351e49ec461SConrad Meyer 352e49ec461SConrad Meyer /* 353e49ec461SConrad Meyer * This checks for the byzantine condition of running a heterogenous 354e49ec461SConrad Meyer * revision multi-socket system where the attach thread is potentially 355e49ec461SConrad Meyer * probing a remote socket's PCI device. 356e49ec461SConrad Meyer * 357e49ec461SConrad Meyer * Currently, such scenarios are unsupported on models using the SMN 358e49ec461SConrad Meyer * (because on those models, amdtemp(4) attaches to a different PCI 359e49ec461SConrad Meyer * device than the one that contains AMDTEMP_CPUID). 360e49ec461SConrad Meyer * 361e49ec461SConrad Meyer * The ancient 0x0F family of devices only supports this register from 362e49ec461SConrad Meyer * models 40h+. 363e49ec461SConrad Meyer */ 364e49ec461SConrad Meyer if (product->amdtemp_has_cpuid && (family > 0x0f || 365e49ec461SConrad Meyer (family == 0x0f && model >= 0x40))) { 366e49ec461SConrad Meyer cpuid = pci_read_config(device_get_parent(dev), AMDTEMP_CPUID, 367e49ec461SConrad Meyer 4); 368a4165bbaSJung-uk Kim family = CPUID_TO_FAMILY(cpuid); 369a4165bbaSJung-uk Kim model = CPUID_TO_MODEL(cpuid); 370fdfa6079SJung-uk Kim } 371a4165bbaSJung-uk Kim 372a4165bbaSJung-uk Kim switch (family) { 373a4165bbaSJung-uk Kim case 0x0f: 374a4165bbaSJung-uk Kim /* 375fdfa6079SJung-uk Kim * Thermaltrip Status Register 376fdfa6079SJung-uk Kim * 377fdfa6079SJung-uk Kim * - ThermSenseCoreSel 378fdfa6079SJung-uk Kim * 379fdfa6079SJung-uk Kim * Revision F & G: 0 - Core1, 1 - Core0 380fdfa6079SJung-uk Kim * Other: 0 - Core0, 1 - Core1 381fdfa6079SJung-uk Kim * 382fdfa6079SJung-uk Kim * - CurTmp 383a4165bbaSJung-uk Kim * 384a4165bbaSJung-uk Kim * Revision G: bits 23-14 385fdfa6079SJung-uk Kim * Other: bits 23-16 386a4165bbaSJung-uk Kim * 387fdfa6079SJung-uk Kim * XXX According to the BKDG, CurTmp, ThermSenseSel and 388fdfa6079SJung-uk Kim * ThermSenseCoreSel bits were introduced in Revision F 389fdfa6079SJung-uk Kim * but CurTmp seems working fine as early as Revision C. 390fdfa6079SJung-uk Kim * However, it is not clear whether ThermSenseSel and/or 391fdfa6079SJung-uk Kim * ThermSenseCoreSel work in undocumented cases as well. 392fdfa6079SJung-uk Kim * In fact, the Linux driver suggests it may not work but 393fdfa6079SJung-uk Kim * we just assume it does until we find otherwise. 394074d80acSJung-uk Kim * 395074d80acSJung-uk Kim * XXX According to Linux, CurTmp starts at -28C on 396074d80acSJung-uk Kim * Socket AM2 Revision G processors, which is not 397074d80acSJung-uk Kim * documented anywhere. 398fc1f75e5SRui Paulo */ 399074d80acSJung-uk Kim if (model >= 0x40) 400fdfa6079SJung-uk Kim sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP; 401074d80acSJung-uk Kim if (model >= 0x60 && model != 0xc1) { 402074d80acSJung-uk Kim do_cpuid(0x80000001, regs); 403074d80acSJung-uk Kim bid = (regs[1] >> 9) & 0x1f; 404074d80acSJung-uk Kim switch (model) { 405074d80acSJung-uk Kim case 0x68: /* Socket S1g1 */ 406074d80acSJung-uk Kim case 0x6c: 407074d80acSJung-uk Kim case 0x7c: 408074d80acSJung-uk Kim break; 409074d80acSJung-uk Kim case 0x6b: /* Socket AM2 and ASB1 (2 cores) */ 410074d80acSJung-uk Kim if (bid != 0x0b && bid != 0x0c) 411074d80acSJung-uk Kim sc->sc_flags |= 412074d80acSJung-uk Kim AMDTEMP_FLAG_ALT_OFFSET; 413074d80acSJung-uk Kim break; 414074d80acSJung-uk Kim case 0x6f: /* Socket AM2 and ASB1 (1 core) */ 415074d80acSJung-uk Kim case 0x7f: 416074d80acSJung-uk Kim if (bid != 0x07 && bid != 0x09 && 417074d80acSJung-uk Kim bid != 0x0c) 418074d80acSJung-uk Kim sc->sc_flags |= 419074d80acSJung-uk Kim AMDTEMP_FLAG_ALT_OFFSET; 420074d80acSJung-uk Kim break; 421074d80acSJung-uk Kim default: 422074d80acSJung-uk Kim sc->sc_flags |= AMDTEMP_FLAG_ALT_OFFSET; 423074d80acSJung-uk Kim } 424fdfa6079SJung-uk Kim sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT; 425fdfa6079SJung-uk Kim } 426a4165bbaSJung-uk Kim 427a4165bbaSJung-uk Kim /* 428a4165bbaSJung-uk Kim * There are two sensors per core. 429a4165bbaSJung-uk Kim */ 430a4165bbaSJung-uk Kim sc->sc_ntemps = 2; 431a4165bbaSJung-uk Kim 432a4165bbaSJung-uk Kim sc->sc_gettemp = amdtemp_gettemp0f; 433a4165bbaSJung-uk Kim break; 434a4165bbaSJung-uk Kim case 0x10: 435074d80acSJung-uk Kim /* 436074d80acSJung-uk Kim * Erratum 319 Inaccurate Temperature Measurement 437074d80acSJung-uk Kim * 438074d80acSJung-uk Kim * http://support.amd.com/us/Processor_TechDocs/41322.pdf 439074d80acSJung-uk Kim */ 440074d80acSJung-uk Kim do_cpuid(0x80000001, regs); 441074d80acSJung-uk Kim switch ((regs[1] >> 28) & 0xf) { 442074d80acSJung-uk Kim case 0: /* Socket F */ 443074d80acSJung-uk Kim erratum319 = 1; 444074d80acSJung-uk Kim break; 445074d80acSJung-uk Kim case 1: /* Socket AM2+ or AM3 */ 4461587a9dbSJohn Baldwin if ((pci_cfgregread(pci_get_domain(dev), 4471587a9dbSJohn Baldwin pci_get_bus(dev), pci_get_slot(dev), 2, 4481587a9dbSJohn Baldwin AMDTEMP_DRAM_CONF_HIGH, 2) & 449074d80acSJung-uk Kim AMDTEMP_DRAM_MODE_DDR3) != 0 || model > 0x04 || 450074d80acSJung-uk Kim (model == 0x04 && (cpuid & CPUID_STEPPING) >= 3)) 451074d80acSJung-uk Kim break; 452074d80acSJung-uk Kim /* XXX 00100F42h (RB-C2) exists in both formats. */ 453074d80acSJung-uk Kim erratum319 = 1; 454074d80acSJung-uk Kim break; 455074d80acSJung-uk Kim } 456074d80acSJung-uk Kim /* FALLTHROUGH */ 457a4165bbaSJung-uk Kim case 0x11: 458074d80acSJung-uk Kim case 0x12: 459074d80acSJung-uk Kim case 0x14: 460074d80acSJung-uk Kim case 0x15: 4612b56f12bSChristian Brueffer case 0x16: 462a4165bbaSJung-uk Kim sc->sc_ntemps = 1; 463e49ec461SConrad Meyer /* 464e49ec461SConrad Meyer * Some later (60h+) models of family 15h use a similar SMN 465e49ec461SConrad Meyer * network as family 17h. (However, the register index differs 466e49ec461SConrad Meyer * from 17h and the decoding matches other 10h-15h models, 467e49ec461SConrad Meyer * which differ from 17h.) 468e49ec461SConrad Meyer */ 469e49ec461SConrad Meyer if (family == 0x15 && model >= 0x60) { 470e49ec461SConrad Meyer sc->sc_gettemp = amdtemp_gettemp15hm60h; 471e49ec461SConrad Meyer needsmn = true; 472e49ec461SConrad Meyer } else 473a4165bbaSJung-uk Kim sc->sc_gettemp = amdtemp_gettemp; 474a4165bbaSJung-uk Kim break; 475a03d621bSConrad Meyer case 0x17: 476ea6189d3SConrad Meyer case 0x19: 477a03d621bSConrad Meyer sc->sc_ntemps = 1; 478a03d621bSConrad Meyer sc->sc_gettemp = amdtemp_gettemp17h; 479e49ec461SConrad Meyer needsmn = true; 480e49ec461SConrad Meyer break; 481e49ec461SConrad Meyer default: 482e49ec461SConrad Meyer device_printf(dev, "Bogus family 0x%x\n", family); 483e49ec461SConrad Meyer return (ENXIO); 484e49ec461SConrad Meyer } 485e49ec461SConrad Meyer 486e49ec461SConrad Meyer if (needsmn) { 487a03d621bSConrad Meyer sc->sc_smn = device_find_child( 488a03d621bSConrad Meyer device_get_parent(dev), "amdsmn", -1); 489a03d621bSConrad Meyer if (sc->sc_smn == NULL) { 490a03d621bSConrad Meyer if (bootverbose) 491a03d621bSConrad Meyer device_printf(dev, "No SMN device found\n"); 492a03d621bSConrad Meyer return (ENXIO); 493a03d621bSConrad Meyer } 494fc1f75e5SRui Paulo } 495fc1f75e5SRui Paulo 496a4165bbaSJung-uk Kim /* Find number of cores per package. */ 497a4165bbaSJung-uk Kim sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ? 498a4165bbaSJung-uk Kim (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1; 499a4165bbaSJung-uk Kim if (sc->sc_ncores > MAXCPU) 500a4165bbaSJung-uk Kim return (ENXIO); 501a4165bbaSJung-uk Kim 5026c101ed7SAlexander Motin mtx_init(&sc->sc_lock, "amdtemp", NULL, MTX_DEF); 503074d80acSJung-uk Kim if (erratum319) 504074d80acSJung-uk Kim device_printf(dev, 505074d80acSJung-uk Kim "Erratum 319: temperature measurement may be inaccurate\n"); 506a4165bbaSJung-uk Kim if (bootverbose) 507a4165bbaSJung-uk Kim device_printf(dev, "Found %d cores and %d sensors.\n", 508a4165bbaSJung-uk Kim sc->sc_ncores, 509a4165bbaSJung-uk Kim sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1); 510454e82d7SRui Paulo 511fc1f75e5SRui Paulo /* 512454e82d7SRui Paulo * dev.amdtemp.N tree. 513fc1f75e5SRui Paulo */ 514074d80acSJung-uk Kim unit = device_get_unit(dev); 515074d80acSJung-uk Kim snprintf(tn, sizeof(tn), "dev.amdtemp.%d.sensor_offset", unit); 516074d80acSJung-uk Kim TUNABLE_INT_FETCH(tn, &sc->sc_offset); 517074d80acSJung-uk Kim 518fc1f75e5SRui Paulo sysctlctx = device_get_sysctl_ctx(dev); 519074d80acSJung-uk Kim SYSCTL_ADD_INT(sysctlctx, 520074d80acSJung-uk Kim SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 521074d80acSJung-uk Kim "sensor_offset", CTLFLAG_RW, &sc->sc_offset, 0, 522074d80acSJung-uk Kim "Temperature sensor offset"); 523fc1f75e5SRui Paulo sysctlnode = SYSCTL_ADD_NODE(sysctlctx, 524a4165bbaSJung-uk Kim SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 5257029da5cSPawel Biernacki "core0", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Core 0"); 526fc1f75e5SRui Paulo 527fc1f75e5SRui Paulo SYSCTL_ADD_PROC(sysctlctx, 528fc1f75e5SRui Paulo SYSCTL_CHILDREN(sysctlnode), 5297029da5cSPawel Biernacki OID_AUTO, "sensor0", 5306c101ed7SAlexander Motin CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 531074d80acSJung-uk Kim dev, CORE0_SENSOR0, amdtemp_sysctl, "IK", 532074d80acSJung-uk Kim "Core 0 / Sensor 0 temperature"); 533fc1f75e5SRui Paulo 534323a94afSAkio Morita sc->sc_temp_base = AMDTEMP_17H_CCD_TMP_BASE; 535323a94afSAkio Morita 536c59b9a4fSConrad Meyer if (family == 0x17) 537c59b9a4fSConrad Meyer amdtemp_probe_ccd_sensors17h(dev, model); 538ea6189d3SConrad Meyer else if (family == 0x19) 539ea6189d3SConrad Meyer amdtemp_probe_ccd_sensors19h(dev, model); 540c59b9a4fSConrad Meyer else if (sc->sc_ntemps > 1) { 541fc1f75e5SRui Paulo SYSCTL_ADD_PROC(sysctlctx, 542fc1f75e5SRui Paulo SYSCTL_CHILDREN(sysctlnode), 5437029da5cSPawel Biernacki OID_AUTO, "sensor1", 5446c101ed7SAlexander Motin CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 545074d80acSJung-uk Kim dev, CORE0_SENSOR1, amdtemp_sysctl, "IK", 546074d80acSJung-uk Kim "Core 0 / Sensor 1 temperature"); 547fc1f75e5SRui Paulo 548074d80acSJung-uk Kim if (sc->sc_ncores > 1) { 549fc1f75e5SRui Paulo sysctlnode = SYSCTL_ADD_NODE(sysctlctx, 550074d80acSJung-uk Kim SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 5517029da5cSPawel Biernacki OID_AUTO, "core1", CTLFLAG_RD | CTLFLAG_MPSAFE, 5527029da5cSPawel Biernacki 0, "Core 1"); 553fc1f75e5SRui Paulo 554fc1f75e5SRui Paulo SYSCTL_ADD_PROC(sysctlctx, 555fc1f75e5SRui Paulo SYSCTL_CHILDREN(sysctlnode), 5567029da5cSPawel Biernacki OID_AUTO, "sensor0", 5576c101ed7SAlexander Motin CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 558074d80acSJung-uk Kim dev, CORE1_SENSOR0, amdtemp_sysctl, "IK", 559074d80acSJung-uk Kim "Core 1 / Sensor 0 temperature"); 560fc1f75e5SRui Paulo 561fc1f75e5SRui Paulo SYSCTL_ADD_PROC(sysctlctx, 562fc1f75e5SRui Paulo SYSCTL_CHILDREN(sysctlnode), 5637029da5cSPawel Biernacki OID_AUTO, "sensor1", 5646c101ed7SAlexander Motin CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 565074d80acSJung-uk Kim dev, CORE1_SENSOR1, amdtemp_sysctl, "IK", 566074d80acSJung-uk Kim "Core 1 / Sensor 1 temperature"); 567074d80acSJung-uk Kim } 568a4165bbaSJung-uk Kim } 569a4165bbaSJung-uk Kim 570a4165bbaSJung-uk Kim /* 571a4165bbaSJung-uk Kim * Try to create dev.cpu sysctl entries and setup intrhook function. 572a4165bbaSJung-uk Kim * This is needed because the cpu driver may be loaded late on boot, 573a4165bbaSJung-uk Kim * after us. 574a4165bbaSJung-uk Kim */ 575a4165bbaSJung-uk Kim amdtemp_intrhook(dev); 576a4165bbaSJung-uk Kim sc->sc_ich.ich_func = amdtemp_intrhook; 577a4165bbaSJung-uk Kim sc->sc_ich.ich_arg = dev; 578a4165bbaSJung-uk Kim if (config_intrhook_establish(&sc->sc_ich) != 0) { 579a4165bbaSJung-uk Kim device_printf(dev, "config_intrhook_establish failed!\n"); 580a4165bbaSJung-uk Kim return (ENXIO); 581a4165bbaSJung-uk Kim } 582fc1f75e5SRui Paulo 583fc1f75e5SRui Paulo return (0); 584fc1f75e5SRui Paulo } 585fc1f75e5SRui Paulo 586fc1f75e5SRui Paulo void 587454e82d7SRui Paulo amdtemp_intrhook(void *arg) 588fc1f75e5SRui Paulo { 589454e82d7SRui Paulo struct amdtemp_softc *sc; 590fc1f75e5SRui Paulo struct sysctl_ctx_list *sysctlctx; 591a4165bbaSJung-uk Kim device_t dev = (device_t)arg; 592a4165bbaSJung-uk Kim device_t acpi, cpu, nexus; 593a4165bbaSJung-uk Kim amdsensor_t sensor; 594a4165bbaSJung-uk Kim int i; 595fc1f75e5SRui Paulo 596fc1f75e5SRui Paulo sc = device_get_softc(dev); 597fc1f75e5SRui Paulo 598fc1f75e5SRui Paulo /* 599fc1f75e5SRui Paulo * dev.cpu.N.temperature. 600fc1f75e5SRui Paulo */ 601fc1f75e5SRui Paulo nexus = device_find_child(root_bus, "nexus", 0); 602fc1f75e5SRui Paulo acpi = device_find_child(nexus, "acpi", 0); 603fc1f75e5SRui Paulo 604a4165bbaSJung-uk Kim for (i = 0; i < sc->sc_ncores; i++) { 605a4165bbaSJung-uk Kim if (sc->sc_sysctl_cpu[i] != NULL) 606a4165bbaSJung-uk Kim continue; 607fc1f75e5SRui Paulo cpu = device_find_child(acpi, "cpu", 608a4165bbaSJung-uk Kim device_get_unit(dev) * sc->sc_ncores + i); 609a4165bbaSJung-uk Kim if (cpu != NULL) { 610fc1f75e5SRui Paulo sysctlctx = device_get_sysctl_ctx(cpu); 611fc1f75e5SRui Paulo 612a4165bbaSJung-uk Kim sensor = sc->sc_ntemps > 1 ? 613074d80acSJung-uk Kim (i == 0 ? CORE0 : CORE1) : CORE0_SENSOR0; 614fc1f75e5SRui Paulo sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx, 615fc1f75e5SRui Paulo SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), 6167029da5cSPawel Biernacki OID_AUTO, "temperature", 6176c101ed7SAlexander Motin CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 618a4165bbaSJung-uk Kim dev, sensor, amdtemp_sysctl, "IK", 619a4165bbaSJung-uk Kim "Current temparature"); 620fc1f75e5SRui Paulo } 621fc1f75e5SRui Paulo } 622a4165bbaSJung-uk Kim if (sc->sc_ich.ich_arg != NULL) 623fc1f75e5SRui Paulo config_intrhook_disestablish(&sc->sc_ich); 624fc1f75e5SRui Paulo } 625fc1f75e5SRui Paulo 626fc1f75e5SRui Paulo int 627454e82d7SRui Paulo amdtemp_detach(device_t dev) 628fc1f75e5SRui Paulo { 629454e82d7SRui Paulo struct amdtemp_softc *sc = device_get_softc(dev); 630a4165bbaSJung-uk Kim int i; 631fc1f75e5SRui Paulo 632a4165bbaSJung-uk Kim for (i = 0; i < sc->sc_ncores; i++) 633a4165bbaSJung-uk Kim if (sc->sc_sysctl_cpu[i] != NULL) 634fc1f75e5SRui Paulo sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0); 635fc1f75e5SRui Paulo 636454e82d7SRui Paulo /* NewBus removes the dev.amdtemp.N tree by itself. */ 637fc1f75e5SRui Paulo 6386c101ed7SAlexander Motin mtx_destroy(&sc->sc_lock); 639fc1f75e5SRui Paulo return (0); 640fc1f75e5SRui Paulo } 641fc1f75e5SRui Paulo 642fc1f75e5SRui Paulo static int 643454e82d7SRui Paulo amdtemp_sysctl(SYSCTL_HANDLER_ARGS) 644fc1f75e5SRui Paulo { 645fc1f75e5SRui Paulo device_t dev = (device_t)arg1; 646454e82d7SRui Paulo struct amdtemp_softc *sc = device_get_softc(dev); 647a4165bbaSJung-uk Kim amdsensor_t sensor = (amdsensor_t)arg2; 648a4165bbaSJung-uk Kim int32_t auxtemp[2], temp; 649fc1f75e5SRui Paulo int error; 650fc1f75e5SRui Paulo 651a4165bbaSJung-uk Kim switch (sensor) { 652fc1f75e5SRui Paulo case CORE0: 653074d80acSJung-uk Kim auxtemp[0] = sc->sc_gettemp(dev, CORE0_SENSOR0); 654074d80acSJung-uk Kim auxtemp[1] = sc->sc_gettemp(dev, CORE0_SENSOR1); 655fc1f75e5SRui Paulo temp = imax(auxtemp[0], auxtemp[1]); 656fc1f75e5SRui Paulo break; 657fc1f75e5SRui Paulo case CORE1: 658074d80acSJung-uk Kim auxtemp[0] = sc->sc_gettemp(dev, CORE1_SENSOR0); 659074d80acSJung-uk Kim auxtemp[1] = sc->sc_gettemp(dev, CORE1_SENSOR1); 660fc1f75e5SRui Paulo temp = imax(auxtemp[0], auxtemp[1]); 661fc1f75e5SRui Paulo break; 662fc1f75e5SRui Paulo default: 663a4165bbaSJung-uk Kim temp = sc->sc_gettemp(dev, sensor); 664fc1f75e5SRui Paulo break; 665fc1f75e5SRui Paulo } 666fc1f75e5SRui Paulo error = sysctl_handle_int(oidp, &temp, 0, req); 667fc1f75e5SRui Paulo 668fc1f75e5SRui Paulo return (error); 669fc1f75e5SRui Paulo } 670fc1f75e5SRui Paulo 6719d6672e1SLuiz Otavio O Souza #define AMDTEMP_ZERO_C_TO_K 2731 672a4165bbaSJung-uk Kim 673fc1f75e5SRui Paulo static int32_t 674454e82d7SRui Paulo amdtemp_gettemp0f(device_t dev, amdsensor_t sensor) 675fc1f75e5SRui Paulo { 676a4165bbaSJung-uk Kim struct amdtemp_softc *sc = device_get_softc(dev); 677074d80acSJung-uk Kim uint32_t mask, offset, temp; 678fc1f75e5SRui Paulo 6796c101ed7SAlexander Motin mtx_lock(&sc->sc_lock); 6806c101ed7SAlexander Motin 681a4165bbaSJung-uk Kim /* Set Sensor/Core selector. */ 682074d80acSJung-uk Kim temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1); 683074d80acSJung-uk Kim temp &= ~(AMDTEMP_TTSR_SELCORE | AMDTEMP_TTSR_SELSENSOR); 684fc1f75e5SRui Paulo switch (sensor) { 685074d80acSJung-uk Kim case CORE0_SENSOR1: 686074d80acSJung-uk Kim temp |= AMDTEMP_TTSR_SELSENSOR; 6877ca2d97bSJung-uk Kim /* FALLTHROUGH */ 688074d80acSJung-uk Kim case CORE0_SENSOR0: 689a4165bbaSJung-uk Kim case CORE0: 690fdfa6079SJung-uk Kim if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0) 691074d80acSJung-uk Kim temp |= AMDTEMP_TTSR_SELCORE; 692fc1f75e5SRui Paulo break; 693074d80acSJung-uk Kim case CORE1_SENSOR1: 694074d80acSJung-uk Kim temp |= AMDTEMP_TTSR_SELSENSOR; 6957ca2d97bSJung-uk Kim /* FALLTHROUGH */ 696074d80acSJung-uk Kim case CORE1_SENSOR0: 697a4165bbaSJung-uk Kim case CORE1: 698fdfa6079SJung-uk Kim if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0) 699074d80acSJung-uk Kim temp |= AMDTEMP_TTSR_SELCORE; 700fc1f75e5SRui Paulo break; 701c59b9a4fSConrad Meyer default: 702c79cee71SKyle Evans __assert_unreachable(); 703fc1f75e5SRui Paulo } 704074d80acSJung-uk Kim pci_write_config(dev, AMDTEMP_THERMTP_STAT, temp, 1); 705a4165bbaSJung-uk Kim 706fdfa6079SJung-uk Kim mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc; 707074d80acSJung-uk Kim offset = (sc->sc_flags & AMDTEMP_FLAG_ALT_OFFSET) != 0 ? 28 : 49; 708074d80acSJung-uk Kim temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4); 709074d80acSJung-uk Kim temp = ((temp >> 14) & mask) * 5 / 2; 710074d80acSJung-uk Kim temp += AMDTEMP_ZERO_C_TO_K + (sc->sc_offset - offset) * 10; 711454e82d7SRui Paulo 7126c101ed7SAlexander Motin mtx_unlock(&sc->sc_lock); 713454e82d7SRui Paulo return (temp); 714454e82d7SRui Paulo } 715454e82d7SRui Paulo 716e49ec461SConrad Meyer static uint32_t 71702f70002SConrad Meyer amdtemp_decode_fam10h_to_17h(int32_t sc_offset, uint32_t val, bool minus49) 718e49ec461SConrad Meyer { 719e49ec461SConrad Meyer uint32_t temp; 720e49ec461SConrad Meyer 721e49ec461SConrad Meyer /* Convert raw register subfield units (0.125C) to units of 0.1C. */ 72202f70002SConrad Meyer temp = (val & AMDTEMP_REPTMP10H_CURTMP_MASK) * 5 / 4; 72302f70002SConrad Meyer 72402f70002SConrad Meyer if (minus49) 72502f70002SConrad Meyer temp -= AMDTEMP_CURTMP_RANGE_ADJUST; 72602f70002SConrad Meyer 72702f70002SConrad Meyer temp += AMDTEMP_ZERO_C_TO_K + sc_offset * 10; 72802f70002SConrad Meyer return (temp); 72902f70002SConrad Meyer } 73002f70002SConrad Meyer 73102f70002SConrad Meyer static uint32_t 73202f70002SConrad Meyer amdtemp_decode_fam10h_to_16h(int32_t sc_offset, uint32_t val) 73302f70002SConrad Meyer { 73402f70002SConrad Meyer bool minus49; 735e49ec461SConrad Meyer 736e49ec461SConrad Meyer /* 737e49ec461SConrad Meyer * On Family 15h and higher, if CurTmpTjSel is 11b, the range is 738e49ec461SConrad Meyer * adjusted down by 49.0 degrees Celsius. (This adjustment is not 739e49ec461SConrad Meyer * documented in BKDGs prior to family 15h model 00h.) 740e49ec461SConrad Meyer */ 74102f70002SConrad Meyer minus49 = (CPUID_TO_FAMILY(cpu_id) >= 0x15 && 742e49ec461SConrad Meyer ((val >> AMDTEMP_REPTMP10H_TJSEL_SHIFT) & 74302f70002SConrad Meyer AMDTEMP_REPTMP10H_TJSEL_MASK) == 0x3); 744e49ec461SConrad Meyer 74502f70002SConrad Meyer return (amdtemp_decode_fam10h_to_17h(sc_offset, 74602f70002SConrad Meyer val >> AMDTEMP_REPTMP10H_CURTMP_SHIFT, minus49)); 74702f70002SConrad Meyer } 74802f70002SConrad Meyer 74902f70002SConrad Meyer static uint32_t 75002f70002SConrad Meyer amdtemp_decode_fam17h_tctl(int32_t sc_offset, uint32_t val) 75102f70002SConrad Meyer { 75202f70002SConrad Meyer bool minus49; 75302f70002SConrad Meyer 754c1cbabe8SVal Packett minus49 = ((val & AMDTEMP_17H_CUR_TMP_RANGE_SEL) != 0) 755c1cbabe8SVal Packett || ((val & AMDTEMP_17H_CUR_TMP_TJ_SEL) == AMDTEMP_17H_CUR_TMP_TJ_SEL); 75602f70002SConrad Meyer return (amdtemp_decode_fam10h_to_17h(sc_offset, 75702f70002SConrad Meyer val >> AMDTEMP_REPTMP10H_CURTMP_SHIFT, minus49)); 758e49ec461SConrad Meyer } 759e49ec461SConrad Meyer 760454e82d7SRui Paulo static int32_t 761454e82d7SRui Paulo amdtemp_gettemp(device_t dev, amdsensor_t sensor) 762454e82d7SRui Paulo { 763074d80acSJung-uk Kim struct amdtemp_softc *sc = device_get_softc(dev); 764454e82d7SRui Paulo uint32_t temp; 765a4165bbaSJung-uk Kim 766a4165bbaSJung-uk Kim temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4); 767e49ec461SConrad Meyer return (amdtemp_decode_fam10h_to_16h(sc->sc_offset, temp)); 768e49ec461SConrad Meyer } 769fc1f75e5SRui Paulo 770e49ec461SConrad Meyer static int32_t 771e49ec461SConrad Meyer amdtemp_gettemp15hm60h(device_t dev, amdsensor_t sensor) 772e49ec461SConrad Meyer { 773e49ec461SConrad Meyer struct amdtemp_softc *sc = device_get_softc(dev); 774e49ec461SConrad Meyer uint32_t val; 775b9723c5bSMateusz Guzik int error __diagused; 776e49ec461SConrad Meyer 777e49ec461SConrad Meyer error = amdsmn_read(sc->sc_smn, AMDTEMP_15H_M60H_REPTMP_CTRL, &val); 778e49ec461SConrad Meyer KASSERT(error == 0, ("amdsmn_read")); 779e49ec461SConrad Meyer return (amdtemp_decode_fam10h_to_16h(sc->sc_offset, val)); 780fc1f75e5SRui Paulo } 781a03d621bSConrad Meyer 782a03d621bSConrad Meyer static int32_t 783a03d621bSConrad Meyer amdtemp_gettemp17h(device_t dev, amdsensor_t sensor) 784a03d621bSConrad Meyer { 785a03d621bSConrad Meyer struct amdtemp_softc *sc = device_get_softc(dev); 78602f70002SConrad Meyer uint32_t val; 787b9723c5bSMateusz Guzik int error __diagused; 788a03d621bSConrad Meyer 789c59b9a4fSConrad Meyer switch (sensor) { 790c59b9a4fSConrad Meyer case CORE0_SENSOR0: 791c59b9a4fSConrad Meyer /* Tctl */ 792fbd5d782SConrad Meyer error = amdsmn_read(sc->sc_smn, AMDTEMP_17H_CUR_TMP, &val); 793a03d621bSConrad Meyer KASSERT(error == 0, ("amdsmn_read")); 79402f70002SConrad Meyer return (amdtemp_decode_fam17h_tctl(sc->sc_offset, val)); 795c59b9a4fSConrad Meyer case CCD_BASE ... CCD_MAX: 796c59b9a4fSConrad Meyer /* Tccd<N> */ 797323a94afSAkio Morita error = amdsmn_read(sc->sc_smn, sc->sc_temp_base + 798c59b9a4fSConrad Meyer (((int)sensor - CCD_BASE) * sizeof(val)), &val); 799c59b9a4fSConrad Meyer KASSERT(error == 0, ("amdsmn_read2")); 800c59b9a4fSConrad Meyer KASSERT((val & AMDTEMP_17H_CCD_TMP_VALID) != 0, 801c59b9a4fSConrad Meyer ("sensor %d: not valid", (int)sensor)); 802c59b9a4fSConrad Meyer return (amdtemp_decode_fam10h_to_17h(sc->sc_offset, val, true)); 803c59b9a4fSConrad Meyer default: 804c79cee71SKyle Evans __assert_unreachable(); 805c59b9a4fSConrad Meyer } 806c59b9a4fSConrad Meyer } 807c59b9a4fSConrad Meyer 808c59b9a4fSConrad Meyer static void 809ea6189d3SConrad Meyer amdtemp_probe_ccd_sensors(device_t dev, uint32_t maxreg) 810c59b9a4fSConrad Meyer { 811c59b9a4fSConrad Meyer char sensor_name[16], sensor_descr[32]; 812c59b9a4fSConrad Meyer struct amdtemp_softc *sc; 813ea6189d3SConrad Meyer uint32_t i, val; 814c59b9a4fSConrad Meyer int error; 815c59b9a4fSConrad Meyer 816c59b9a4fSConrad Meyer sc = device_get_softc(dev); 817c59b9a4fSConrad Meyer for (i = 0; i < maxreg; i++) { 818323a94afSAkio Morita error = amdsmn_read(sc->sc_smn, sc->sc_temp_base + 819c59b9a4fSConrad Meyer (i * sizeof(val)), &val); 820c59b9a4fSConrad Meyer if (error != 0) 821c59b9a4fSConrad Meyer continue; 822c59b9a4fSConrad Meyer if ((val & AMDTEMP_17H_CCD_TMP_VALID) == 0) 823c59b9a4fSConrad Meyer continue; 824c59b9a4fSConrad Meyer 825c59b9a4fSConrad Meyer snprintf(sensor_name, sizeof(sensor_name), "ccd%u", i); 826c59b9a4fSConrad Meyer snprintf(sensor_descr, sizeof(sensor_descr), 827c59b9a4fSConrad Meyer "CCD %u temperature (Tccd%u)", i, i); 828c59b9a4fSConrad Meyer 829c59b9a4fSConrad Meyer SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 830c59b9a4fSConrad Meyer SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 831c59b9a4fSConrad Meyer sensor_name, CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, 832c59b9a4fSConrad Meyer dev, CCD_BASE + i, amdtemp_sysctl, "IK", sensor_descr); 833c59b9a4fSConrad Meyer } 834a03d621bSConrad Meyer } 835ea6189d3SConrad Meyer 836ea6189d3SConrad Meyer static void 837ea6189d3SConrad Meyer amdtemp_probe_ccd_sensors17h(device_t dev, uint32_t model) 838ea6189d3SConrad Meyer { 839ea6189d3SConrad Meyer uint32_t maxreg; 840ea6189d3SConrad Meyer 841ea6189d3SConrad Meyer switch (model) { 842b499ab87SConrad Meyer case 0x00 ... 0x2f: /* Zen1, Zen+ */ 843ea6189d3SConrad Meyer maxreg = 4; 844ea6189d3SConrad Meyer break; 845b499ab87SConrad Meyer case 0x30 ... 0x3f: /* Zen2 TR (Castle Peak)/EPYC (Rome) */ 846b499ab87SConrad Meyer case 0x60 ... 0x7f: /* Zen2 Ryzen (Renoir APU, Matisse) */ 847b499ab87SConrad Meyer case 0x90 ... 0x9f: /* Zen2 Ryzen (Van Gogh APU) */ 848ea6189d3SConrad Meyer maxreg = 8; 849ea6189d3SConrad Meyer _Static_assert((int)NUM_CCDS >= 8, ""); 850ea6189d3SConrad Meyer break; 851ea6189d3SConrad Meyer default: 852ea6189d3SConrad Meyer device_printf(dev, 853ea6189d3SConrad Meyer "Unrecognized Family 17h Model: %02xh\n", model); 854ea6189d3SConrad Meyer return; 855ea6189d3SConrad Meyer } 856ea6189d3SConrad Meyer 857ea6189d3SConrad Meyer amdtemp_probe_ccd_sensors(dev, maxreg); 858ea6189d3SConrad Meyer } 859ea6189d3SConrad Meyer 860ea6189d3SConrad Meyer static void 861ea6189d3SConrad Meyer amdtemp_probe_ccd_sensors19h(device_t dev, uint32_t model) 862ea6189d3SConrad Meyer { 863323a94afSAkio Morita struct amdtemp_softc *sc = device_get_softc(dev); 864ea6189d3SConrad Meyer uint32_t maxreg; 865ea6189d3SConrad Meyer 866ea6189d3SConrad Meyer switch (model) { 867ea6189d3SConrad Meyer case 0x00 ... 0x0f: /* Zen3 EPYC "Milan" */ 868ea6189d3SConrad Meyer case 0x20 ... 0x2f: /* Zen3 Ryzen "Vermeer" */ 869ea6189d3SConrad Meyer maxreg = 8; 870ea6189d3SConrad Meyer _Static_assert((int)NUM_CCDS >= 8, ""); 871ea6189d3SConrad Meyer break; 87251c69c86SXin LI case 0x10 ... 0x1f: 87351c69c86SXin LI sc->sc_temp_base = AMDTEMP_ZEN4_10H_CCD_TMP_BASE; 87451c69c86SXin LI maxreg = 12; 87551c69c86SXin LI _Static_assert((int)NUM_CCDS >= 12, ""); 87651c69c86SXin LI break; 877323a94afSAkio Morita case 0x60 ... 0x6f: /* Zen4 Ryzen "Raphael" */ 878*ef3f8aa0SOliver Fromme case 0x70 ... 0x7f: /* Zen4 Ryzen "Phoenix" */ 879323a94afSAkio Morita sc->sc_temp_base = AMDTEMP_ZEN4_CCD_TMP_BASE; 880323a94afSAkio Morita maxreg = 8; 881323a94afSAkio Morita _Static_assert((int)NUM_CCDS >= 8, ""); 882323a94afSAkio Morita break; 883ea6189d3SConrad Meyer default: 884ea6189d3SConrad Meyer device_printf(dev, 885ea6189d3SConrad Meyer "Unrecognized Family 19h Model: %02xh\n", model); 886ea6189d3SConrad Meyer return; 887ea6189d3SConrad Meyer } 888ea6189d3SConrad Meyer 889ea6189d3SConrad Meyer amdtemp_probe_ccd_sensors(dev, maxreg); 890ea6189d3SConrad Meyer } 891