1*114b2e18SPierre-Alain TORET /*- 2*114b2e18SPierre-Alain TORET 3*114b2e18SPierre-Alain TORET * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 4*114b2e18SPierre-Alain TORET * 5*114b2e18SPierre-Alain TORET * Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org> 6*114b2e18SPierre-Alain TORET * Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org> 7*114b2e18SPierre-Alain TORET * Copyright (c) 2009-2012 Jung-uk Kim <jkim@FreeBSD.org> 8*114b2e18SPierre-Alain TORET * All rights reserved. 9*114b2e18SPierre-Alain TORET * Copyright (c) 2017-2020 Conrad Meyer <cem@FreeBSD.org>. All rights reserved. 10*114b2e18SPierre-Alain TORET * 11*114b2e18SPierre-Alain TORET * Redistribution and use in source and binary forms, with or without 12*114b2e18SPierre-Alain TORET * modification, are permitted provided that the following conditions 13*114b2e18SPierre-Alain TORET * are met: 14*114b2e18SPierre-Alain TORET * 1. Redistributions of source code must retain the above copyright 15*114b2e18SPierre-Alain TORET * notice, this list of conditions and the following disclaimer. 16*114b2e18SPierre-Alain TORET * 2. Redistributions in binary form must reproduce the above copyright 17*114b2e18SPierre-Alain TORET * notice, this list of conditions and the following disclaimer in the 18*114b2e18SPierre-Alain TORET * documentation and/or other materials provided with the distribution. 19*114b2e18SPierre-Alain TORET * 20*114b2e18SPierre-Alain TORET * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21*114b2e18SPierre-Alain TORET * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22*114b2e18SPierre-Alain TORET * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23*114b2e18SPierre-Alain TORET * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 24*114b2e18SPierre-Alain TORET * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25*114b2e18SPierre-Alain TORET * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26*114b2e18SPierre-Alain TORET * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27*114b2e18SPierre-Alain TORET * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28*114b2e18SPierre-Alain TORET * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 29*114b2e18SPierre-Alain TORET * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30*114b2e18SPierre-Alain TORET * POSSIBILITY OF SUCH DAMAGE. 31*114b2e18SPierre-Alain TORET */ 32*114b2e18SPierre-Alain TORET 33*114b2e18SPierre-Alain TORET /* 34*114b2e18SPierre-Alain TORET * Driver for the AMD CPU on-die thermal sensors. 35*114b2e18SPierre-Alain TORET * Initially based on the k8temp Linux driver. 36*114b2e18SPierre-Alain TORET */ 37*114b2e18SPierre-Alain TORET 38*114b2e18SPierre-Alain TORET //#include <stdio.h> 39*114b2e18SPierre-Alain TORET 40*114b2e18SPierre-Alain TORET #include <sys/cdefs.h> 41*114b2e18SPierre-Alain TORET //__FBSDID("$FreeBSD$"); 42*114b2e18SPierre-Alain TORET 43*114b2e18SPierre-Alain TORET 44*114b2e18SPierre-Alain TORET #include <sys/param.h> 45*114b2e18SPierre-Alain TORET #include <sys/bus.h> 46*114b2e18SPierre-Alain TORET #include <sys/conf.h> 47*114b2e18SPierre-Alain TORET #include <sys/kernel.h> 48*114b2e18SPierre-Alain TORET #include <sys/module.h> 49*114b2e18SPierre-Alain TORET #include <sys/sysctl.h> 50*114b2e18SPierre-Alain TORET #include <sys/systm.h> 51*114b2e18SPierre-Alain TORET 52*114b2e18SPierre-Alain TORET #include <machine/cpufunc.h> 53*114b2e18SPierre-Alain TORET #include <machine/md_var.h> 54*114b2e18SPierre-Alain TORET #include <machine/specialreg.h> 55*114b2e18SPierre-Alain TORET 56*114b2e18SPierre-Alain TORET #include <bus/pci/pcivar.h> 57*114b2e18SPierre-Alain TORET #include <bus/pci/pci_cfgreg.h> 58*114b2e18SPierre-Alain TORET 59*114b2e18SPierre-Alain TORET #include <dev/powermng/amdsmn/amdsmn.h> 60*114b2e18SPierre-Alain TORET 61*114b2e18SPierre-Alain TORET typedef enum { 62*114b2e18SPierre-Alain TORET CORE0_SENSOR0, 63*114b2e18SPierre-Alain TORET CORE0_SENSOR1, 64*114b2e18SPierre-Alain TORET CORE1_SENSOR0, 65*114b2e18SPierre-Alain TORET CORE1_SENSOR1, 66*114b2e18SPierre-Alain TORET CORE0, 67*114b2e18SPierre-Alain TORET CORE1, 68*114b2e18SPierre-Alain TORET CCD1, 69*114b2e18SPierre-Alain TORET CCD_BASE = CCD1, 70*114b2e18SPierre-Alain TORET CCD2, 71*114b2e18SPierre-Alain TORET CCD3, 72*114b2e18SPierre-Alain TORET CCD4, 73*114b2e18SPierre-Alain TORET CCD5, 74*114b2e18SPierre-Alain TORET CCD6, 75*114b2e18SPierre-Alain TORET CCD7, 76*114b2e18SPierre-Alain TORET CCD8, 77*114b2e18SPierre-Alain TORET CCD_MAX = CCD8, 78*114b2e18SPierre-Alain TORET NUM_CCDS = CCD_MAX - CCD_BASE + 1, 79*114b2e18SPierre-Alain TORET } amdsensor_t; 80*114b2e18SPierre-Alain TORET 81*114b2e18SPierre-Alain TORET struct amdtemp_softc { 82*114b2e18SPierre-Alain TORET int sc_ncores; 83*114b2e18SPierre-Alain TORET int sc_ntemps; 84*114b2e18SPierre-Alain TORET int sc_flags; 85*114b2e18SPierre-Alain TORET #define AMDTEMP_FLAG_CS_SWAP 0x01 /* ThermSenseCoreSel is inverted. */ 86*114b2e18SPierre-Alain TORET #define AMDTEMP_FLAG_CT_10BIT 0x02 /* CurTmp is 10-bit wide. */ 87*114b2e18SPierre-Alain TORET #define AMDTEMP_FLAG_ALT_OFFSET 0x04 /* CurTmp starts at -28C. */ 88*114b2e18SPierre-Alain TORET int32_t sc_offset; 89*114b2e18SPierre-Alain TORET int32_t (*sc_gettemp)(device_t, amdsensor_t); 90*114b2e18SPierre-Alain TORET struct sysctl_oid *sc_sysctl_cpu[MAXCPU]; 91*114b2e18SPierre-Alain TORET struct intr_config_hook sc_ich; 92*114b2e18SPierre-Alain TORET device_t sc_smn; 93*114b2e18SPierre-Alain TORET }; 94*114b2e18SPierre-Alain TORET 95*114b2e18SPierre-Alain TORET /* 96*114b2e18SPierre-Alain TORET * N.B. The numbers in macro names below are significant and represent CPU 97*114b2e18SPierre-Alain TORET * family and model numbers. Do not make up fictitious family or model numbers 98*114b2e18SPierre-Alain TORET * when adding support for new devices. 99*114b2e18SPierre-Alain TORET */ 100*114b2e18SPierre-Alain TORET #define VENDORID_AMD 0x1022 101*114b2e18SPierre-Alain TORET #define DEVICEID_AMD_MISC0F 0x1103 102*114b2e18SPierre-Alain TORET #define DEVICEID_AMD_MISC10 0x1203 103*114b2e18SPierre-Alain TORET #define DEVICEID_AMD_MISC11 0x1303 104*114b2e18SPierre-Alain TORET #define DEVICEID_AMD_MISC14 0x1703 105*114b2e18SPierre-Alain TORET #define DEVICEID_AMD_MISC15 0x1603 106*114b2e18SPierre-Alain TORET #define DEVICEID_AMD_MISC15_M10H 0x1403 107*114b2e18SPierre-Alain TORET #define DEVICEID_AMD_MISC15_M30H 0x141d 108*114b2e18SPierre-Alain TORET #define DEVICEID_AMD_MISC15_M60H_ROOT 0x1576 109*114b2e18SPierre-Alain TORET #define DEVICEID_AMD_MISC16 0x1533 110*114b2e18SPierre-Alain TORET #define DEVICEID_AMD_MISC16_M30H 0x1583 111*114b2e18SPierre-Alain TORET #define DEVICEID_AMD_HOSTB17H_ROOT 0x1450 112*114b2e18SPierre-Alain TORET #define DEVICEID_AMD_HOSTB17H_M10H_ROOT 0x15d0 113*114b2e18SPierre-Alain TORET #define DEVICEID_AMD_HOSTB17H_M30H_ROOT 0x1480 /* Also M70h. */ 114*114b2e18SPierre-Alain TORET 115*114b2e18SPierre-Alain TORET static const struct amdtemp_product { 116*114b2e18SPierre-Alain TORET uint16_t amdtemp_vendorid; 117*114b2e18SPierre-Alain TORET uint16_t amdtemp_deviceid; 118*114b2e18SPierre-Alain TORET /* 119*114b2e18SPierre-Alain TORET * 0xFC register is only valid on the D18F3 PCI device; SMN temp 120*114b2e18SPierre-Alain TORET * drivers do not attach to that device. 121*114b2e18SPierre-Alain TORET */ 122*114b2e18SPierre-Alain TORET bool amdtemp_has_cpuid; 123*114b2e18SPierre-Alain TORET } amdtemp_products[] = { 124*114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_MISC0F, true }, 125*114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_MISC10, true }, 126*114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_MISC11, true }, 127*114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_MISC14, true }, 128*114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_MISC15, true }, 129*114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_MISC15_M10H, true }, 130*114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_MISC15_M30H, true }, 131*114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_MISC15_M60H_ROOT, false }, 132*114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_MISC16, true }, 133*114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_MISC16_M30H, true }, 134*114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_HOSTB17H_ROOT, false }, 135*114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_HOSTB17H_M10H_ROOT, false }, 136*114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_HOSTB17H_M30H_ROOT, false }, 137*114b2e18SPierre-Alain TORET }; 138*114b2e18SPierre-Alain TORET 139*114b2e18SPierre-Alain TORET /* 140*114b2e18SPierre-Alain TORET * Reported Temperature Control Register, family 0Fh-15h (some models), 16h. 141*114b2e18SPierre-Alain TORET */ 142*114b2e18SPierre-Alain TORET #define AMDTEMP_REPTMP_CTRL 0xa4 143*114b2e18SPierre-Alain TORET 144*114b2e18SPierre-Alain TORET #define AMDTEMP_REPTMP10H_CURTMP_MASK 0x7ff 145*114b2e18SPierre-Alain TORET #define AMDTEMP_REPTMP10H_CURTMP_SHIFT 21 146*114b2e18SPierre-Alain TORET #define AMDTEMP_REPTMP10H_TJSEL_MASK 0x3 147*114b2e18SPierre-Alain TORET #define AMDTEMP_REPTMP10H_TJSEL_SHIFT 16 148*114b2e18SPierre-Alain TORET 149*114b2e18SPierre-Alain TORET /* 150*114b2e18SPierre-Alain TORET * Reported Temperature, Family 15h, M60+ 151*114b2e18SPierre-Alain TORET * 152*114b2e18SPierre-Alain TORET * Same register bit definitions as other Family 15h CPUs, but access is 153*114b2e18SPierre-Alain TORET * indirect via SMN, like Family 17h. 154*114b2e18SPierre-Alain TORET */ 155*114b2e18SPierre-Alain TORET #define AMDTEMP_15H_M60H_REPTMP_CTRL 0xd8200ca4 156*114b2e18SPierre-Alain TORET 157*114b2e18SPierre-Alain TORET /* 158*114b2e18SPierre-Alain TORET * Reported Temperature, Family 17h 159*114b2e18SPierre-Alain TORET * 160*114b2e18SPierre-Alain TORET * According to AMD OSRR for 17H, section 4.2.1, bits 31-21 of this register 161*114b2e18SPierre-Alain TORET * provide the current temp. bit 19, when clear, means the temp is reported in 162*114b2e18SPierre-Alain TORET * a range 0.."225C" (probable typo for 255C), and when set changes the range 163*114b2e18SPierre-Alain TORET * to -49..206C. 164*114b2e18SPierre-Alain TORET */ 165*114b2e18SPierre-Alain TORET #define AMDTEMP_17H_CUR_TMP 0x59800 166*114b2e18SPierre-Alain TORET #define AMDTEMP_17H_CUR_TMP_RANGE_SEL (1u << 19) 167*114b2e18SPierre-Alain TORET /* 168*114b2e18SPierre-Alain TORET * The following register set was discovered experimentally by Ondrej Čerman 169*114b2e18SPierre-Alain TORET * and collaborators, but is not (yet) documented in a PPR/OSRR (other than 170*114b2e18SPierre-Alain TORET * the M70H PPR SMN memory map showing [0x59800, +0x314] as allocated to 171*114b2e18SPierre-Alain TORET * SMU::THM). It seems plausible and the Linux sensor folks have adopted it. 172*114b2e18SPierre-Alain TORET */ 173*114b2e18SPierre-Alain TORET #define AMDTEMP_17H_CCD_TMP_BASE 0x59954 174*114b2e18SPierre-Alain TORET #define AMDTEMP_17H_CCD_TMP_VALID (1u << 11) 175*114b2e18SPierre-Alain TORET 176*114b2e18SPierre-Alain TORET /* 177*114b2e18SPierre-Alain TORET * AMD temperature range adjustment, in deciKelvins (i.e., 49.0 Celsius). 178*114b2e18SPierre-Alain TORET */ 179*114b2e18SPierre-Alain TORET #define AMDTEMP_CURTMP_RANGE_ADJUST 490 180*114b2e18SPierre-Alain TORET 181*114b2e18SPierre-Alain TORET /* 182*114b2e18SPierre-Alain TORET * Thermaltrip Status Register (Family 0Fh only) 183*114b2e18SPierre-Alain TORET */ 184*114b2e18SPierre-Alain TORET #define AMDTEMP_THERMTP_STAT 0xe4 185*114b2e18SPierre-Alain TORET #define AMDTEMP_TTSR_SELCORE 0x04 186*114b2e18SPierre-Alain TORET #define AMDTEMP_TTSR_SELSENSOR 0x40 187*114b2e18SPierre-Alain TORET 188*114b2e18SPierre-Alain TORET /* 189*114b2e18SPierre-Alain TORET * DRAM Configuration High Register 190*114b2e18SPierre-Alain TORET */ 191*114b2e18SPierre-Alain TORET #define AMDTEMP_DRAM_CONF_HIGH 0x94 /* Function 2 */ 192*114b2e18SPierre-Alain TORET #define AMDTEMP_DRAM_MODE_DDR3 0x0100 193*114b2e18SPierre-Alain TORET 194*114b2e18SPierre-Alain TORET /* 195*114b2e18SPierre-Alain TORET * CPU Family/Model Register 196*114b2e18SPierre-Alain TORET */ 197*114b2e18SPierre-Alain TORET #define AMDTEMP_CPUID 0xfc 198*114b2e18SPierre-Alain TORET 199*114b2e18SPierre-Alain TORET /* 200*114b2e18SPierre-Alain TORET * Device methods. 201*114b2e18SPierre-Alain TORET */ 202*114b2e18SPierre-Alain TORET static void amdtemp_identify(driver_t *driver, device_t parent); 203*114b2e18SPierre-Alain TORET static int amdtemp_probe(device_t dev); 204*114b2e18SPierre-Alain TORET static int amdtemp_attach(device_t dev); 205*114b2e18SPierre-Alain TORET static void amdtemp_intrhook(void *arg); 206*114b2e18SPierre-Alain TORET static int amdtemp_detach(device_t dev); 207*114b2e18SPierre-Alain TORET static int32_t amdtemp_gettemp0f(device_t dev, amdsensor_t sensor); 208*114b2e18SPierre-Alain TORET static int32_t amdtemp_gettemp(device_t dev, amdsensor_t sensor); 209*114b2e18SPierre-Alain TORET static int32_t amdtemp_gettemp15hm60h(device_t dev, amdsensor_t sensor); 210*114b2e18SPierre-Alain TORET static int32_t amdtemp_gettemp17h(device_t dev, amdsensor_t sensor); 211*114b2e18SPierre-Alain TORET static void amdtemp_probe_ccd_sensors17h(device_t dev, uint32_t model); 212*114b2e18SPierre-Alain TORET static int amdtemp_sysctl(SYSCTL_HANDLER_ARGS); 213*114b2e18SPierre-Alain TORET 214*114b2e18SPierre-Alain TORET static device_method_t amdtemp_methods[] = { 215*114b2e18SPierre-Alain TORET /* Device interface */ 216*114b2e18SPierre-Alain TORET DEVMETHOD(device_identify, amdtemp_identify), 217*114b2e18SPierre-Alain TORET DEVMETHOD(device_probe, amdtemp_probe), 218*114b2e18SPierre-Alain TORET DEVMETHOD(device_attach, amdtemp_attach), 219*114b2e18SPierre-Alain TORET DEVMETHOD(device_detach, amdtemp_detach), 220*114b2e18SPierre-Alain TORET 221*114b2e18SPierre-Alain TORET DEVMETHOD_END 222*114b2e18SPierre-Alain TORET }; 223*114b2e18SPierre-Alain TORET 224*114b2e18SPierre-Alain TORET static driver_t amdtemp_driver = { 225*114b2e18SPierre-Alain TORET "amdtemp", 226*114b2e18SPierre-Alain TORET amdtemp_methods, 227*114b2e18SPierre-Alain TORET sizeof(struct amdtemp_softc), 228*114b2e18SPierre-Alain TORET }; 229*114b2e18SPierre-Alain TORET 230*114b2e18SPierre-Alain TORET static devclass_t amdtemp_devclass; 231*114b2e18SPierre-Alain TORET DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL); 232*114b2e18SPierre-Alain TORET MODULE_VERSION(amdtemp, 1); 233*114b2e18SPierre-Alain TORET MODULE_DEPEND(amdtemp, amdsmn, 1, 1, 1); 234*114b2e18SPierre-Alain TORET #if !defined(__DragonFly__) 235*114b2e18SPierre-Alain TORET MODULE_PNP_INFO("U16:vendor;U16:device", pci, amdtemp, amdtemp_products, 236*114b2e18SPierre-Alain TORET nitems(amdtemp_products)); 237*114b2e18SPierre-Alain TORET #endif 238*114b2e18SPierre-Alain TORET 239*114b2e18SPierre-Alain TORET static bool 240*114b2e18SPierre-Alain TORET amdtemp_match(device_t dev, const struct amdtemp_product **product_out) 241*114b2e18SPierre-Alain TORET { 242*114b2e18SPierre-Alain TORET int i; 243*114b2e18SPierre-Alain TORET uint16_t vendor, devid; 244*114b2e18SPierre-Alain TORET 245*114b2e18SPierre-Alain TORET vendor = pci_get_vendor(dev); 246*114b2e18SPierre-Alain TORET devid = pci_get_device(dev); 247*114b2e18SPierre-Alain TORET 248*114b2e18SPierre-Alain TORET for (i = 0; i < nitems(amdtemp_products); i++) { 249*114b2e18SPierre-Alain TORET if (vendor == amdtemp_products[i].amdtemp_vendorid && 250*114b2e18SPierre-Alain TORET devid == amdtemp_products[i].amdtemp_deviceid) { 251*114b2e18SPierre-Alain TORET if (product_out != NULL) 252*114b2e18SPierre-Alain TORET *product_out = &amdtemp_products[i]; 253*114b2e18SPierre-Alain TORET return (true); 254*114b2e18SPierre-Alain TORET } 255*114b2e18SPierre-Alain TORET } 256*114b2e18SPierre-Alain TORET return (false); 257*114b2e18SPierre-Alain TORET } 258*114b2e18SPierre-Alain TORET 259*114b2e18SPierre-Alain TORET static void 260*114b2e18SPierre-Alain TORET amdtemp_identify(driver_t *driver, device_t parent) 261*114b2e18SPierre-Alain TORET { 262*114b2e18SPierre-Alain TORET device_t child; 263*114b2e18SPierre-Alain TORET 264*114b2e18SPierre-Alain TORET /* Make sure we're not being doubly invoked. */ 265*114b2e18SPierre-Alain TORET if (device_find_child(parent, "amdtemp", -1) != NULL) 266*114b2e18SPierre-Alain TORET return; 267*114b2e18SPierre-Alain TORET 268*114b2e18SPierre-Alain TORET if (amdtemp_match(parent, NULL)) { 269*114b2e18SPierre-Alain TORET child = device_add_child(parent, "amdtemp", -1); 270*114b2e18SPierre-Alain TORET if (child == NULL) 271*114b2e18SPierre-Alain TORET device_printf(parent, "add amdtemp child failed\n"); 272*114b2e18SPierre-Alain TORET } 273*114b2e18SPierre-Alain TORET } 274*114b2e18SPierre-Alain TORET 275*114b2e18SPierre-Alain TORET static int 276*114b2e18SPierre-Alain TORET amdtemp_probe(device_t dev) 277*114b2e18SPierre-Alain TORET { 278*114b2e18SPierre-Alain TORET uint32_t family, model; 279*114b2e18SPierre-Alain TORET 280*114b2e18SPierre-Alain TORET if (resource_disabled("amdtemp", 0)) 281*114b2e18SPierre-Alain TORET return (ENXIO); 282*114b2e18SPierre-Alain TORET if (!amdtemp_match(device_get_parent(dev), NULL)) 283*114b2e18SPierre-Alain TORET return (ENXIO); 284*114b2e18SPierre-Alain TORET 285*114b2e18SPierre-Alain TORET family = CPUID_TO_FAMILY(cpu_id); 286*114b2e18SPierre-Alain TORET model = CPUID_TO_MODEL(cpu_id); 287*114b2e18SPierre-Alain TORET 288*114b2e18SPierre-Alain TORET switch (family) { 289*114b2e18SPierre-Alain TORET case 0x0f: 290*114b2e18SPierre-Alain TORET if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) || 291*114b2e18SPierre-Alain TORET (model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1)) 292*114b2e18SPierre-Alain TORET return (ENXIO); 293*114b2e18SPierre-Alain TORET break; 294*114b2e18SPierre-Alain TORET case 0x10: 295*114b2e18SPierre-Alain TORET case 0x11: 296*114b2e18SPierre-Alain TORET case 0x12: 297*114b2e18SPierre-Alain TORET case 0x14: 298*114b2e18SPierre-Alain TORET case 0x15: 299*114b2e18SPierre-Alain TORET case 0x16: 300*114b2e18SPierre-Alain TORET case 0x17: 301*114b2e18SPierre-Alain TORET break; 302*114b2e18SPierre-Alain TORET default: 303*114b2e18SPierre-Alain TORET return (ENXIO); 304*114b2e18SPierre-Alain TORET } 305*114b2e18SPierre-Alain TORET device_set_desc(dev, "AMD CPU On-Die Thermal Sensors"); 306*114b2e18SPierre-Alain TORET 307*114b2e18SPierre-Alain TORET return (BUS_PROBE_GENERIC); 308*114b2e18SPierre-Alain TORET } 309*114b2e18SPierre-Alain TORET 310*114b2e18SPierre-Alain TORET static int 311*114b2e18SPierre-Alain TORET amdtemp_attach(device_t dev) 312*114b2e18SPierre-Alain TORET { 313*114b2e18SPierre-Alain TORET char tn[32]; 314*114b2e18SPierre-Alain TORET u_int regs[4]; 315*114b2e18SPierre-Alain TORET const struct amdtemp_product *product; 316*114b2e18SPierre-Alain TORET struct amdtemp_softc *sc; 317*114b2e18SPierre-Alain TORET struct sysctl_ctx_list *sysctlctx; 318*114b2e18SPierre-Alain TORET struct sysctl_oid *sysctlnode; 319*114b2e18SPierre-Alain TORET uint32_t cpuid, family, model; 320*114b2e18SPierre-Alain TORET u_int bid; 321*114b2e18SPierre-Alain TORET int erratum319, unit; 322*114b2e18SPierre-Alain TORET bool needsmn; 323*114b2e18SPierre-Alain TORET 324*114b2e18SPierre-Alain TORET sc = device_get_softc(dev); 325*114b2e18SPierre-Alain TORET erratum319 = 0; 326*114b2e18SPierre-Alain TORET needsmn = false; 327*114b2e18SPierre-Alain TORET 328*114b2e18SPierre-Alain TORET if (!amdtemp_match(device_get_parent(dev), &product)) 329*114b2e18SPierre-Alain TORET return (ENXIO); 330*114b2e18SPierre-Alain TORET 331*114b2e18SPierre-Alain TORET cpuid = cpu_id; 332*114b2e18SPierre-Alain TORET family = CPUID_TO_FAMILY(cpuid); 333*114b2e18SPierre-Alain TORET model = CPUID_TO_MODEL(cpuid); 334*114b2e18SPierre-Alain TORET 335*114b2e18SPierre-Alain TORET /* 336*114b2e18SPierre-Alain TORET * This checks for the byzantine condition of running a heterogenous 337*114b2e18SPierre-Alain TORET * revision multi-socket system where the attach thread is potentially 338*114b2e18SPierre-Alain TORET * probing a remote socket's PCI device. 339*114b2e18SPierre-Alain TORET * 340*114b2e18SPierre-Alain TORET * Currently, such scenarios are unsupported on models using the SMN 341*114b2e18SPierre-Alain TORET * (because on those models, amdtemp(4) attaches to a different PCI 342*114b2e18SPierre-Alain TORET * device than the one that contains AMDTEMP_CPUID). 343*114b2e18SPierre-Alain TORET * 344*114b2e18SPierre-Alain TORET * The ancient 0x0F family of devices only supports this register from 345*114b2e18SPierre-Alain TORET * models 40h+. 346*114b2e18SPierre-Alain TORET */ 347*114b2e18SPierre-Alain TORET if (product->amdtemp_has_cpuid && (family > 0x0f || 348*114b2e18SPierre-Alain TORET (family == 0x0f && model >= 0x40))) { 349*114b2e18SPierre-Alain TORET cpuid = pci_read_config(device_get_parent(dev), AMDTEMP_CPUID, 350*114b2e18SPierre-Alain TORET 4); 351*114b2e18SPierre-Alain TORET family = CPUID_TO_FAMILY(cpuid); 352*114b2e18SPierre-Alain TORET model = CPUID_TO_MODEL(cpuid); 353*114b2e18SPierre-Alain TORET } 354*114b2e18SPierre-Alain TORET 355*114b2e18SPierre-Alain TORET switch (family) { 356*114b2e18SPierre-Alain TORET case 0x0f: 357*114b2e18SPierre-Alain TORET /* 358*114b2e18SPierre-Alain TORET * Thermaltrip Status Register 359*114b2e18SPierre-Alain TORET * 360*114b2e18SPierre-Alain TORET * - ThermSenseCoreSel 361*114b2e18SPierre-Alain TORET * 362*114b2e18SPierre-Alain TORET * Revision F & G: 0 - Core1, 1 - Core0 363*114b2e18SPierre-Alain TORET * Other: 0 - Core0, 1 - Core1 364*114b2e18SPierre-Alain TORET * 365*114b2e18SPierre-Alain TORET * - CurTmp 366*114b2e18SPierre-Alain TORET * 367*114b2e18SPierre-Alain TORET * Revision G: bits 23-14 368*114b2e18SPierre-Alain TORET * Other: bits 23-16 369*114b2e18SPierre-Alain TORET * 370*114b2e18SPierre-Alain TORET * XXX According to the BKDG, CurTmp, ThermSenseSel and 371*114b2e18SPierre-Alain TORET * ThermSenseCoreSel bits were introduced in Revision F 372*114b2e18SPierre-Alain TORET * but CurTmp seems working fine as early as Revision C. 373*114b2e18SPierre-Alain TORET * However, it is not clear whether ThermSenseSel and/or 374*114b2e18SPierre-Alain TORET * ThermSenseCoreSel work in undocumented cases as well. 375*114b2e18SPierre-Alain TORET * In fact, the Linux driver suggests it may not work but 376*114b2e18SPierre-Alain TORET * we just assume it does until we find otherwise. 377*114b2e18SPierre-Alain TORET * 378*114b2e18SPierre-Alain TORET * XXX According to Linux, CurTmp starts at -28C on 379*114b2e18SPierre-Alain TORET * Socket AM2 Revision G processors, which is not 380*114b2e18SPierre-Alain TORET * documented anywhere. 381*114b2e18SPierre-Alain TORET */ 382*114b2e18SPierre-Alain TORET if (model >= 0x40) 383*114b2e18SPierre-Alain TORET sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP; 384*114b2e18SPierre-Alain TORET if (model >= 0x60 && model != 0xc1) { 385*114b2e18SPierre-Alain TORET do_cpuid(0x80000001, regs); 386*114b2e18SPierre-Alain TORET bid = (regs[1] >> 9) & 0x1f; 387*114b2e18SPierre-Alain TORET switch (model) { 388*114b2e18SPierre-Alain TORET case 0x68: /* Socket S1g1 */ 389*114b2e18SPierre-Alain TORET case 0x6c: 390*114b2e18SPierre-Alain TORET case 0x7c: 391*114b2e18SPierre-Alain TORET break; 392*114b2e18SPierre-Alain TORET case 0x6b: /* Socket AM2 and ASB1 (2 cores) */ 393*114b2e18SPierre-Alain TORET if (bid != 0x0b && bid != 0x0c) 394*114b2e18SPierre-Alain TORET sc->sc_flags |= 395*114b2e18SPierre-Alain TORET AMDTEMP_FLAG_ALT_OFFSET; 396*114b2e18SPierre-Alain TORET break; 397*114b2e18SPierre-Alain TORET case 0x6f: /* Socket AM2 and ASB1 (1 core) */ 398*114b2e18SPierre-Alain TORET case 0x7f: 399*114b2e18SPierre-Alain TORET if (bid != 0x07 && bid != 0x09 && 400*114b2e18SPierre-Alain TORET bid != 0x0c) 401*114b2e18SPierre-Alain TORET sc->sc_flags |= 402*114b2e18SPierre-Alain TORET AMDTEMP_FLAG_ALT_OFFSET; 403*114b2e18SPierre-Alain TORET break; 404*114b2e18SPierre-Alain TORET default: 405*114b2e18SPierre-Alain TORET sc->sc_flags |= AMDTEMP_FLAG_ALT_OFFSET; 406*114b2e18SPierre-Alain TORET } 407*114b2e18SPierre-Alain TORET sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT; 408*114b2e18SPierre-Alain TORET } 409*114b2e18SPierre-Alain TORET 410*114b2e18SPierre-Alain TORET /* 411*114b2e18SPierre-Alain TORET * There are two sensors per core. 412*114b2e18SPierre-Alain TORET */ 413*114b2e18SPierre-Alain TORET sc->sc_ntemps = 2; 414*114b2e18SPierre-Alain TORET 415*114b2e18SPierre-Alain TORET sc->sc_gettemp = amdtemp_gettemp0f; 416*114b2e18SPierre-Alain TORET break; 417*114b2e18SPierre-Alain TORET case 0x10: 418*114b2e18SPierre-Alain TORET /* 419*114b2e18SPierre-Alain TORET * Erratum 319 Inaccurate Temperature Measurement 420*114b2e18SPierre-Alain TORET * 421*114b2e18SPierre-Alain TORET * http://support.amd.com/us/Processor_TechDocs/41322.pdf 422*114b2e18SPierre-Alain TORET */ 423*114b2e18SPierre-Alain TORET do_cpuid(0x80000001, regs); 424*114b2e18SPierre-Alain TORET switch ((regs[1] >> 28) & 0xf) { 425*114b2e18SPierre-Alain TORET case 0: /* Socket F */ 426*114b2e18SPierre-Alain TORET erratum319 = 1; 427*114b2e18SPierre-Alain TORET break; 428*114b2e18SPierre-Alain TORET case 1: /* Socket AM2+ or AM3 */ 429*114b2e18SPierre-Alain TORET if ((pci_cfgregread(pci_get_bus(dev), 430*114b2e18SPierre-Alain TORET pci_get_slot(dev), 2, AMDTEMP_DRAM_CONF_HIGH, 2) & 431*114b2e18SPierre-Alain TORET AMDTEMP_DRAM_MODE_DDR3) != 0 || model > 0x04 || 432*114b2e18SPierre-Alain TORET (model == 0x04 && (cpuid & CPUID_STEPPING) >= 3)) 433*114b2e18SPierre-Alain TORET break; 434*114b2e18SPierre-Alain TORET /* XXX 00100F42h (RB-C2) exists in both formats. */ 435*114b2e18SPierre-Alain TORET erratum319 = 1; 436*114b2e18SPierre-Alain TORET break; 437*114b2e18SPierre-Alain TORET } 438*114b2e18SPierre-Alain TORET /* FALLTHROUGH */ 439*114b2e18SPierre-Alain TORET case 0x11: 440*114b2e18SPierre-Alain TORET case 0x12: 441*114b2e18SPierre-Alain TORET case 0x14: 442*114b2e18SPierre-Alain TORET case 0x15: 443*114b2e18SPierre-Alain TORET case 0x16: 444*114b2e18SPierre-Alain TORET sc->sc_ntemps = 1; 445*114b2e18SPierre-Alain TORET /* 446*114b2e18SPierre-Alain TORET * Some later (60h+) models of family 15h use a similar SMN 447*114b2e18SPierre-Alain TORET * network as family 17h. (However, the register index differs 448*114b2e18SPierre-Alain TORET * from 17h and the decoding matches other 10h-15h models, 449*114b2e18SPierre-Alain TORET * which differ from 17h.) 450*114b2e18SPierre-Alain TORET */ 451*114b2e18SPierre-Alain TORET if (family == 0x15 && model >= 0x60) { 452*114b2e18SPierre-Alain TORET sc->sc_gettemp = amdtemp_gettemp15hm60h; 453*114b2e18SPierre-Alain TORET needsmn = true; 454*114b2e18SPierre-Alain TORET } else 455*114b2e18SPierre-Alain TORET sc->sc_gettemp = amdtemp_gettemp; 456*114b2e18SPierre-Alain TORET break; 457*114b2e18SPierre-Alain TORET case 0x17: 458*114b2e18SPierre-Alain TORET sc->sc_ntemps = 1; 459*114b2e18SPierre-Alain TORET sc->sc_gettemp = amdtemp_gettemp17h; 460*114b2e18SPierre-Alain TORET needsmn = true; 461*114b2e18SPierre-Alain TORET break; 462*114b2e18SPierre-Alain TORET default: 463*114b2e18SPierre-Alain TORET device_printf(dev, "Bogus family 0x%x\n", family); 464*114b2e18SPierre-Alain TORET return (ENXIO); 465*114b2e18SPierre-Alain TORET } 466*114b2e18SPierre-Alain TORET 467*114b2e18SPierre-Alain TORET if (needsmn) { 468*114b2e18SPierre-Alain TORET sc->sc_smn = device_find_child( 469*114b2e18SPierre-Alain TORET device_get_parent(dev), "amdsmn", -1); 470*114b2e18SPierre-Alain TORET if (sc->sc_smn == NULL) { 471*114b2e18SPierre-Alain TORET if (bootverbose) 472*114b2e18SPierre-Alain TORET device_printf(dev, "No SMN device found\n"); 473*114b2e18SPierre-Alain TORET return (ENXIO); 474*114b2e18SPierre-Alain TORET } 475*114b2e18SPierre-Alain TORET } 476*114b2e18SPierre-Alain TORET 477*114b2e18SPierre-Alain TORET /* Find number of cores per package. */ 478*114b2e18SPierre-Alain TORET sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ? 479*114b2e18SPierre-Alain TORET (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1; 480*114b2e18SPierre-Alain TORET if (sc->sc_ncores > MAXCPU) 481*114b2e18SPierre-Alain TORET return (ENXIO); 482*114b2e18SPierre-Alain TORET 483*114b2e18SPierre-Alain TORET if (erratum319) 484*114b2e18SPierre-Alain TORET device_printf(dev, 485*114b2e18SPierre-Alain TORET "Erratum 319: temperature measurement may be inaccurate\n"); 486*114b2e18SPierre-Alain TORET if (bootverbose) 487*114b2e18SPierre-Alain TORET device_printf(dev, "Found %d cores and %d sensors.\n", 488*114b2e18SPierre-Alain TORET sc->sc_ncores, 489*114b2e18SPierre-Alain TORET sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1); 490*114b2e18SPierre-Alain TORET 491*114b2e18SPierre-Alain TORET /* 492*114b2e18SPierre-Alain TORET * dev.amdtemp.N tree. 493*114b2e18SPierre-Alain TORET */ 494*114b2e18SPierre-Alain TORET unit = device_get_unit(dev); 495*114b2e18SPierre-Alain TORET ksnprintf(tn, sizeof(tn), "dev.amdtemp.%d.sensor_offset", unit); 496*114b2e18SPierre-Alain TORET TUNABLE_INT_FETCH(tn, &sc->sc_offset); 497*114b2e18SPierre-Alain TORET 498*114b2e18SPierre-Alain TORET sysctlctx = device_get_sysctl_ctx(dev); 499*114b2e18SPierre-Alain TORET SYSCTL_ADD_INT(sysctlctx, 500*114b2e18SPierre-Alain TORET SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 501*114b2e18SPierre-Alain TORET "sensor_offset", CTLFLAG_RW, &sc->sc_offset, 0, 502*114b2e18SPierre-Alain TORET "Temperature sensor offset"); 503*114b2e18SPierre-Alain TORET sysctlnode = SYSCTL_ADD_NODE(sysctlctx, 504*114b2e18SPierre-Alain TORET SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 505*114b2e18SPierre-Alain TORET "core0", CTLFLAG_RD, 0, "Core 0"); 506*114b2e18SPierre-Alain TORET 507*114b2e18SPierre-Alain TORET SYSCTL_ADD_PROC(sysctlctx, 508*114b2e18SPierre-Alain TORET SYSCTL_CHILDREN(sysctlnode), 509*114b2e18SPierre-Alain TORET OID_AUTO, "sensor0", 510*114b2e18SPierre-Alain TORET CTLTYPE_INT | CTLFLAG_RD, 511*114b2e18SPierre-Alain TORET dev, CORE0_SENSOR0, amdtemp_sysctl, "IK", 512*114b2e18SPierre-Alain TORET "Core 0 / Sensor 0 temperature"); 513*114b2e18SPierre-Alain TORET 514*114b2e18SPierre-Alain TORET if (family == 0x17) 515*114b2e18SPierre-Alain TORET amdtemp_probe_ccd_sensors17h(dev, model); 516*114b2e18SPierre-Alain TORET else if (sc->sc_ntemps > 1) { 517*114b2e18SPierre-Alain TORET SYSCTL_ADD_PROC(sysctlctx, 518*114b2e18SPierre-Alain TORET SYSCTL_CHILDREN(sysctlnode), 519*114b2e18SPierre-Alain TORET OID_AUTO, "sensor1", 520*114b2e18SPierre-Alain TORET CTLTYPE_INT | CTLFLAG_RD, 521*114b2e18SPierre-Alain TORET dev, CORE0_SENSOR1, amdtemp_sysctl, "IK", 522*114b2e18SPierre-Alain TORET "Core 0 / Sensor 1 temperature"); 523*114b2e18SPierre-Alain TORET 524*114b2e18SPierre-Alain TORET if (sc->sc_ncores > 1) { 525*114b2e18SPierre-Alain TORET sysctlnode = SYSCTL_ADD_NODE(sysctlctx, 526*114b2e18SPierre-Alain TORET SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 527*114b2e18SPierre-Alain TORET OID_AUTO, "core1", CTLFLAG_RD, 528*114b2e18SPierre-Alain TORET 0, "Core 1"); 529*114b2e18SPierre-Alain TORET 530*114b2e18SPierre-Alain TORET SYSCTL_ADD_PROC(sysctlctx, 531*114b2e18SPierre-Alain TORET SYSCTL_CHILDREN(sysctlnode), 532*114b2e18SPierre-Alain TORET OID_AUTO, "sensor0", 533*114b2e18SPierre-Alain TORET CTLTYPE_INT | CTLFLAG_RD, 534*114b2e18SPierre-Alain TORET dev, CORE1_SENSOR0, amdtemp_sysctl, "IK", 535*114b2e18SPierre-Alain TORET "Core 1 / Sensor 0 temperature"); 536*114b2e18SPierre-Alain TORET 537*114b2e18SPierre-Alain TORET SYSCTL_ADD_PROC(sysctlctx, 538*114b2e18SPierre-Alain TORET SYSCTL_CHILDREN(sysctlnode), 539*114b2e18SPierre-Alain TORET OID_AUTO, "sensor1", 540*114b2e18SPierre-Alain TORET CTLTYPE_INT | CTLFLAG_RD, 541*114b2e18SPierre-Alain TORET dev, CORE1_SENSOR1, amdtemp_sysctl, "IK", 542*114b2e18SPierre-Alain TORET "Core 1 / Sensor 1 temperature"); 543*114b2e18SPierre-Alain TORET } 544*114b2e18SPierre-Alain TORET } 545*114b2e18SPierre-Alain TORET 546*114b2e18SPierre-Alain TORET /* 547*114b2e18SPierre-Alain TORET * Try to create dev.cpu sysctl entries and setup intrhook function. 548*114b2e18SPierre-Alain TORET * This is needed because the cpu driver may be loaded late on boot, 549*114b2e18SPierre-Alain TORET * after us. 550*114b2e18SPierre-Alain TORET */ 551*114b2e18SPierre-Alain TORET amdtemp_intrhook(dev); 552*114b2e18SPierre-Alain TORET sc->sc_ich.ich_func = amdtemp_intrhook; 553*114b2e18SPierre-Alain TORET sc->sc_ich.ich_arg = dev; 554*114b2e18SPierre-Alain TORET if (config_intrhook_establish(&sc->sc_ich) != 0) { 555*114b2e18SPierre-Alain TORET device_printf(dev, "config_intrhook_establish failed!\n"); 556*114b2e18SPierre-Alain TORET return (ENXIO); 557*114b2e18SPierre-Alain TORET } 558*114b2e18SPierre-Alain TORET 559*114b2e18SPierre-Alain TORET return (0); 560*114b2e18SPierre-Alain TORET } 561*114b2e18SPierre-Alain TORET 562*114b2e18SPierre-Alain TORET void 563*114b2e18SPierre-Alain TORET amdtemp_intrhook(void *arg) 564*114b2e18SPierre-Alain TORET { 565*114b2e18SPierre-Alain TORET struct amdtemp_softc *sc; 566*114b2e18SPierre-Alain TORET struct sysctl_ctx_list *sysctlctx; 567*114b2e18SPierre-Alain TORET device_t dev = (device_t)arg; 568*114b2e18SPierre-Alain TORET device_t acpi, cpu, nexus; 569*114b2e18SPierre-Alain TORET amdsensor_t sensor; 570*114b2e18SPierre-Alain TORET int i; 571*114b2e18SPierre-Alain TORET 572*114b2e18SPierre-Alain TORET sc = device_get_softc(dev); 573*114b2e18SPierre-Alain TORET 574*114b2e18SPierre-Alain TORET /* 575*114b2e18SPierre-Alain TORET * dev.cpu.N.temperature. 576*114b2e18SPierre-Alain TORET */ 577*114b2e18SPierre-Alain TORET nexus = device_find_child(root_bus, "nexus", 0); 578*114b2e18SPierre-Alain TORET acpi = device_find_child(nexus, "acpi", 0); 579*114b2e18SPierre-Alain TORET 580*114b2e18SPierre-Alain TORET for (i = 0; i < sc->sc_ncores; i++) { 581*114b2e18SPierre-Alain TORET if (sc->sc_sysctl_cpu[i] != NULL) 582*114b2e18SPierre-Alain TORET continue; 583*114b2e18SPierre-Alain TORET cpu = device_find_child(acpi, "cpu", 584*114b2e18SPierre-Alain TORET device_get_unit(dev) * sc->sc_ncores + i); 585*114b2e18SPierre-Alain TORET if (cpu != NULL) { 586*114b2e18SPierre-Alain TORET sysctlctx = device_get_sysctl_ctx(cpu); 587*114b2e18SPierre-Alain TORET 588*114b2e18SPierre-Alain TORET sensor = sc->sc_ntemps > 1 ? 589*114b2e18SPierre-Alain TORET (i == 0 ? CORE0 : CORE1) : CORE0_SENSOR0; 590*114b2e18SPierre-Alain TORET sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx, 591*114b2e18SPierre-Alain TORET SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), 592*114b2e18SPierre-Alain TORET OID_AUTO, "temperature", 593*114b2e18SPierre-Alain TORET CTLTYPE_INT | CTLFLAG_RD, 594*114b2e18SPierre-Alain TORET dev, sensor, amdtemp_sysctl, "IK", 595*114b2e18SPierre-Alain TORET "Current temparature"); 596*114b2e18SPierre-Alain TORET } 597*114b2e18SPierre-Alain TORET } 598*114b2e18SPierre-Alain TORET if (sc->sc_ich.ich_arg != NULL) 599*114b2e18SPierre-Alain TORET config_intrhook_disestablish(&sc->sc_ich); 600*114b2e18SPierre-Alain TORET } 601*114b2e18SPierre-Alain TORET 602*114b2e18SPierre-Alain TORET int 603*114b2e18SPierre-Alain TORET amdtemp_detach(device_t dev) 604*114b2e18SPierre-Alain TORET { 605*114b2e18SPierre-Alain TORET struct amdtemp_softc *sc = device_get_softc(dev); 606*114b2e18SPierre-Alain TORET int i; 607*114b2e18SPierre-Alain TORET 608*114b2e18SPierre-Alain TORET for (i = 0; i < sc->sc_ncores; i++) 609*114b2e18SPierre-Alain TORET if (sc->sc_sysctl_cpu[i] != NULL) 610*114b2e18SPierre-Alain TORET sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0); 611*114b2e18SPierre-Alain TORET 612*114b2e18SPierre-Alain TORET /* NewBus removes the dev.amdtemp.N tree by itself. */ 613*114b2e18SPierre-Alain TORET 614*114b2e18SPierre-Alain TORET return (0); 615*114b2e18SPierre-Alain TORET } 616*114b2e18SPierre-Alain TORET 617*114b2e18SPierre-Alain TORET static int 618*114b2e18SPierre-Alain TORET amdtemp_sysctl(SYSCTL_HANDLER_ARGS) 619*114b2e18SPierre-Alain TORET { 620*114b2e18SPierre-Alain TORET device_t dev = (device_t)arg1; 621*114b2e18SPierre-Alain TORET struct amdtemp_softc *sc = device_get_softc(dev); 622*114b2e18SPierre-Alain TORET amdsensor_t sensor = (amdsensor_t)arg2; 623*114b2e18SPierre-Alain TORET int32_t auxtemp[2], temp; 624*114b2e18SPierre-Alain TORET int error; 625*114b2e18SPierre-Alain TORET 626*114b2e18SPierre-Alain TORET switch (sensor) { 627*114b2e18SPierre-Alain TORET case CORE0: 628*114b2e18SPierre-Alain TORET auxtemp[0] = sc->sc_gettemp(dev, CORE0_SENSOR0); 629*114b2e18SPierre-Alain TORET auxtemp[1] = sc->sc_gettemp(dev, CORE0_SENSOR1); 630*114b2e18SPierre-Alain TORET temp = imax(auxtemp[0], auxtemp[1]); 631*114b2e18SPierre-Alain TORET break; 632*114b2e18SPierre-Alain TORET case CORE1: 633*114b2e18SPierre-Alain TORET auxtemp[0] = sc->sc_gettemp(dev, CORE1_SENSOR0); 634*114b2e18SPierre-Alain TORET auxtemp[1] = sc->sc_gettemp(dev, CORE1_SENSOR1); 635*114b2e18SPierre-Alain TORET temp = imax(auxtemp[0], auxtemp[1]); 636*114b2e18SPierre-Alain TORET break; 637*114b2e18SPierre-Alain TORET default: 638*114b2e18SPierre-Alain TORET temp = sc->sc_gettemp(dev, sensor); 639*114b2e18SPierre-Alain TORET break; 640*114b2e18SPierre-Alain TORET } 641*114b2e18SPierre-Alain TORET error = sysctl_handle_int(oidp, &temp, 0, req); 642*114b2e18SPierre-Alain TORET 643*114b2e18SPierre-Alain TORET return (error); 644*114b2e18SPierre-Alain TORET } 645*114b2e18SPierre-Alain TORET 646*114b2e18SPierre-Alain TORET #define AMDTEMP_ZERO_C_TO_K 2731 647*114b2e18SPierre-Alain TORET 648*114b2e18SPierre-Alain TORET static int32_t 649*114b2e18SPierre-Alain TORET amdtemp_gettemp0f(device_t dev, amdsensor_t sensor) 650*114b2e18SPierre-Alain TORET { 651*114b2e18SPierre-Alain TORET struct amdtemp_softc *sc = device_get_softc(dev); 652*114b2e18SPierre-Alain TORET uint32_t mask, offset, temp; 653*114b2e18SPierre-Alain TORET 654*114b2e18SPierre-Alain TORET /* Set Sensor/Core selector. */ 655*114b2e18SPierre-Alain TORET temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1); 656*114b2e18SPierre-Alain TORET temp &= ~(AMDTEMP_TTSR_SELCORE | AMDTEMP_TTSR_SELSENSOR); 657*114b2e18SPierre-Alain TORET switch (sensor) { 658*114b2e18SPierre-Alain TORET case CORE0_SENSOR1: 659*114b2e18SPierre-Alain TORET temp |= AMDTEMP_TTSR_SELSENSOR; 660*114b2e18SPierre-Alain TORET /* FALLTHROUGH */ 661*114b2e18SPierre-Alain TORET case CORE0_SENSOR0: 662*114b2e18SPierre-Alain TORET case CORE0: 663*114b2e18SPierre-Alain TORET if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0) 664*114b2e18SPierre-Alain TORET temp |= AMDTEMP_TTSR_SELCORE; 665*114b2e18SPierre-Alain TORET break; 666*114b2e18SPierre-Alain TORET case CORE1_SENSOR1: 667*114b2e18SPierre-Alain TORET temp |= AMDTEMP_TTSR_SELSENSOR; 668*114b2e18SPierre-Alain TORET /* FALLTHROUGH */ 669*114b2e18SPierre-Alain TORET case CORE1_SENSOR0: 670*114b2e18SPierre-Alain TORET case CORE1: 671*114b2e18SPierre-Alain TORET if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0) 672*114b2e18SPierre-Alain TORET temp |= AMDTEMP_TTSR_SELCORE; 673*114b2e18SPierre-Alain TORET break; 674*114b2e18SPierre-Alain TORET default: 675*114b2e18SPierre-Alain TORET __builtin_unreachable(); 676*114b2e18SPierre-Alain TORET } 677*114b2e18SPierre-Alain TORET pci_write_config(dev, AMDTEMP_THERMTP_STAT, temp, 1); 678*114b2e18SPierre-Alain TORET 679*114b2e18SPierre-Alain TORET mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc; 680*114b2e18SPierre-Alain TORET offset = (sc->sc_flags & AMDTEMP_FLAG_ALT_OFFSET) != 0 ? 28 : 49; 681*114b2e18SPierre-Alain TORET temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4); 682*114b2e18SPierre-Alain TORET temp = ((temp >> 14) & mask) * 5 / 2; 683*114b2e18SPierre-Alain TORET temp += AMDTEMP_ZERO_C_TO_K + (sc->sc_offset - offset) * 10; 684*114b2e18SPierre-Alain TORET 685*114b2e18SPierre-Alain TORET return (temp); 686*114b2e18SPierre-Alain TORET } 687*114b2e18SPierre-Alain TORET 688*114b2e18SPierre-Alain TORET static uint32_t 689*114b2e18SPierre-Alain TORET amdtemp_decode_fam10h_to_17h(int32_t sc_offset, uint32_t val, bool minus49) 690*114b2e18SPierre-Alain TORET { 691*114b2e18SPierre-Alain TORET uint32_t temp; 692*114b2e18SPierre-Alain TORET 693*114b2e18SPierre-Alain TORET /* Convert raw register subfield units (0.125C) to units of 0.1C. */ 694*114b2e18SPierre-Alain TORET temp = (val & AMDTEMP_REPTMP10H_CURTMP_MASK) * 5 / 4; 695*114b2e18SPierre-Alain TORET 696*114b2e18SPierre-Alain TORET if (minus49) 697*114b2e18SPierre-Alain TORET temp -= AMDTEMP_CURTMP_RANGE_ADJUST; 698*114b2e18SPierre-Alain TORET 699*114b2e18SPierre-Alain TORET temp += AMDTEMP_ZERO_C_TO_K + sc_offset * 10; 700*114b2e18SPierre-Alain TORET return (temp); 701*114b2e18SPierre-Alain TORET } 702*114b2e18SPierre-Alain TORET 703*114b2e18SPierre-Alain TORET static uint32_t 704*114b2e18SPierre-Alain TORET amdtemp_decode_fam10h_to_16h(int32_t sc_offset, uint32_t val) 705*114b2e18SPierre-Alain TORET { 706*114b2e18SPierre-Alain TORET bool minus49; 707*114b2e18SPierre-Alain TORET 708*114b2e18SPierre-Alain TORET /* 709*114b2e18SPierre-Alain TORET * On Family 15h and higher, if CurTmpTjSel is 11b, the range is 710*114b2e18SPierre-Alain TORET * adjusted down by 49.0 degrees Celsius. (This adjustment is not 711*114b2e18SPierre-Alain TORET * documented in BKDGs prior to family 15h model 00h.) 712*114b2e18SPierre-Alain TORET */ 713*114b2e18SPierre-Alain TORET minus49 = (CPUID_TO_FAMILY(cpu_id) >= 0x15 && 714*114b2e18SPierre-Alain TORET ((val >> AMDTEMP_REPTMP10H_TJSEL_SHIFT) & 715*114b2e18SPierre-Alain TORET AMDTEMP_REPTMP10H_TJSEL_MASK) == 0x3); 716*114b2e18SPierre-Alain TORET 717*114b2e18SPierre-Alain TORET return (amdtemp_decode_fam10h_to_17h(sc_offset, 718*114b2e18SPierre-Alain TORET val >> AMDTEMP_REPTMP10H_CURTMP_SHIFT, minus49)); 719*114b2e18SPierre-Alain TORET } 720*114b2e18SPierre-Alain TORET 721*114b2e18SPierre-Alain TORET static uint32_t 722*114b2e18SPierre-Alain TORET amdtemp_decode_fam17h_tctl(int32_t sc_offset, uint32_t val) 723*114b2e18SPierre-Alain TORET { 724*114b2e18SPierre-Alain TORET bool minus49; 725*114b2e18SPierre-Alain TORET 726*114b2e18SPierre-Alain TORET minus49 = ((val & AMDTEMP_17H_CUR_TMP_RANGE_SEL) != 0); 727*114b2e18SPierre-Alain TORET return (amdtemp_decode_fam10h_to_17h(sc_offset, 728*114b2e18SPierre-Alain TORET val >> AMDTEMP_REPTMP10H_CURTMP_SHIFT, minus49)); 729*114b2e18SPierre-Alain TORET } 730*114b2e18SPierre-Alain TORET 731*114b2e18SPierre-Alain TORET static int32_t 732*114b2e18SPierre-Alain TORET amdtemp_gettemp(device_t dev, amdsensor_t sensor) 733*114b2e18SPierre-Alain TORET { 734*114b2e18SPierre-Alain TORET struct amdtemp_softc *sc = device_get_softc(dev); 735*114b2e18SPierre-Alain TORET uint32_t temp; 736*114b2e18SPierre-Alain TORET 737*114b2e18SPierre-Alain TORET temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4); 738*114b2e18SPierre-Alain TORET return (amdtemp_decode_fam10h_to_16h(sc->sc_offset, temp)); 739*114b2e18SPierre-Alain TORET } 740*114b2e18SPierre-Alain TORET 741*114b2e18SPierre-Alain TORET static int32_t 742*114b2e18SPierre-Alain TORET amdtemp_gettemp15hm60h(device_t dev, amdsensor_t sensor) 743*114b2e18SPierre-Alain TORET { 744*114b2e18SPierre-Alain TORET struct amdtemp_softc *sc = device_get_softc(dev); 745*114b2e18SPierre-Alain TORET uint32_t val; 746*114b2e18SPierre-Alain TORET int error; 747*114b2e18SPierre-Alain TORET 748*114b2e18SPierre-Alain TORET error = amdsmn_read(sc->sc_smn, AMDTEMP_15H_M60H_REPTMP_CTRL, &val); 749*114b2e18SPierre-Alain TORET KASSERT(error == 0, ("amdsmn_read")); 750*114b2e18SPierre-Alain TORET return (amdtemp_decode_fam10h_to_16h(sc->sc_offset, val)); 751*114b2e18SPierre-Alain TORET } 752*114b2e18SPierre-Alain TORET 753*114b2e18SPierre-Alain TORET static int32_t 754*114b2e18SPierre-Alain TORET amdtemp_gettemp17h(device_t dev, amdsensor_t sensor) 755*114b2e18SPierre-Alain TORET { 756*114b2e18SPierre-Alain TORET struct amdtemp_softc *sc = device_get_softc(dev); 757*114b2e18SPierre-Alain TORET uint32_t val; 758*114b2e18SPierre-Alain TORET int error; 759*114b2e18SPierre-Alain TORET 760*114b2e18SPierre-Alain TORET switch (sensor) { 761*114b2e18SPierre-Alain TORET case CORE0_SENSOR0: 762*114b2e18SPierre-Alain TORET /* Tctl */ 763*114b2e18SPierre-Alain TORET error = amdsmn_read(sc->sc_smn, AMDTEMP_17H_CUR_TMP, &val); 764*114b2e18SPierre-Alain TORET KASSERT(error == 0, ("amdsmn_read")); 765*114b2e18SPierre-Alain TORET return (amdtemp_decode_fam17h_tctl(sc->sc_offset, val)); 766*114b2e18SPierre-Alain TORET case CCD_BASE ... CCD_MAX: 767*114b2e18SPierre-Alain TORET /* Tccd<N> */ 768*114b2e18SPierre-Alain TORET error = amdsmn_read(sc->sc_smn, AMDTEMP_17H_CCD_TMP_BASE + 769*114b2e18SPierre-Alain TORET (((int)sensor - CCD_BASE) * sizeof(val)), &val); 770*114b2e18SPierre-Alain TORET KASSERT(error == 0, ("amdsmn_read2")); 771*114b2e18SPierre-Alain TORET KASSERT((val & AMDTEMP_17H_CCD_TMP_VALID) != 0, 772*114b2e18SPierre-Alain TORET ("sensor %d: not valid", (int)sensor)); 773*114b2e18SPierre-Alain TORET return (amdtemp_decode_fam10h_to_17h(sc->sc_offset, val, true)); 774*114b2e18SPierre-Alain TORET default: 775*114b2e18SPierre-Alain TORET __builtin_unreachable(); 776*114b2e18SPierre-Alain TORET } 777*114b2e18SPierre-Alain TORET } 778*114b2e18SPierre-Alain TORET 779*114b2e18SPierre-Alain TORET static void 780*114b2e18SPierre-Alain TORET amdtemp_probe_ccd_sensors17h(device_t dev, uint32_t model) 781*114b2e18SPierre-Alain TORET { 782*114b2e18SPierre-Alain TORET char sensor_name[16], sensor_descr[32]; 783*114b2e18SPierre-Alain TORET struct amdtemp_softc *sc; 784*114b2e18SPierre-Alain TORET uint32_t maxreg, i, val; 785*114b2e18SPierre-Alain TORET int error; 786*114b2e18SPierre-Alain TORET 787*114b2e18SPierre-Alain TORET switch (model) { 788*114b2e18SPierre-Alain TORET case 0x00 ... 0x1f: /* Zen1, Zen+ */ 789*114b2e18SPierre-Alain TORET maxreg = 4; 790*114b2e18SPierre-Alain TORET break; 791*114b2e18SPierre-Alain TORET case 0x30 ... 0x3f: /* Zen2 TR/Epyc */ 792*114b2e18SPierre-Alain TORET case 0x70 ... 0x7f: /* Zen2 Ryzen */ 793*114b2e18SPierre-Alain TORET maxreg = 8; 794*114b2e18SPierre-Alain TORET _Static_assert((int)NUM_CCDS >= 8, ""); 795*114b2e18SPierre-Alain TORET break; 796*114b2e18SPierre-Alain TORET default: 797*114b2e18SPierre-Alain TORET device_printf(dev, 798*114b2e18SPierre-Alain TORET "Unrecognized Family 17h Model: %02xh\n", model); 799*114b2e18SPierre-Alain TORET return; 800*114b2e18SPierre-Alain TORET } 801*114b2e18SPierre-Alain TORET 802*114b2e18SPierre-Alain TORET sc = device_get_softc(dev); 803*114b2e18SPierre-Alain TORET for (i = 0; i < maxreg; i++) { 804*114b2e18SPierre-Alain TORET error = amdsmn_read(sc->sc_smn, AMDTEMP_17H_CCD_TMP_BASE + 805*114b2e18SPierre-Alain TORET (i * sizeof(val)), &val); 806*114b2e18SPierre-Alain TORET if (error != 0) 807*114b2e18SPierre-Alain TORET continue; 808*114b2e18SPierre-Alain TORET if ((val & AMDTEMP_17H_CCD_TMP_VALID) == 0) 809*114b2e18SPierre-Alain TORET continue; 810*114b2e18SPierre-Alain TORET 811*114b2e18SPierre-Alain TORET ksnprintf(sensor_name, sizeof(sensor_name), "ccd%u", i); 812*114b2e18SPierre-Alain TORET ksnprintf(sensor_descr, sizeof(sensor_descr), 813*114b2e18SPierre-Alain TORET "CCD %u temperature (Tccd%u)", i, i); 814*114b2e18SPierre-Alain TORET 815*114b2e18SPierre-Alain TORET SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 816*114b2e18SPierre-Alain TORET SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 817*114b2e18SPierre-Alain TORET sensor_name, CTLTYPE_INT | CTLFLAG_RD, 818*114b2e18SPierre-Alain TORET dev, CCD_BASE + i, amdtemp_sysctl, "IK", sensor_descr); 819*114b2e18SPierre-Alain TORET } 820*114b2e18SPierre-Alain TORET } 821