1114b2e18SPierre-Alain TORET /*- 2114b2e18SPierre-Alain TORET * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3114b2e18SPierre-Alain TORET * 4114b2e18SPierre-Alain TORET * Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org> 5114b2e18SPierre-Alain TORET * Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org> 6114b2e18SPierre-Alain TORET * Copyright (c) 2009-2012 Jung-uk Kim <jkim@FreeBSD.org> 7114b2e18SPierre-Alain TORET * All rights reserved. 8114b2e18SPierre-Alain TORET * Copyright (c) 2017-2020 Conrad Meyer <cem@FreeBSD.org>. All rights reserved. 9114b2e18SPierre-Alain TORET * 10114b2e18SPierre-Alain TORET * Redistribution and use in source and binary forms, with or without 11114b2e18SPierre-Alain TORET * modification, are permitted provided that the following conditions 12114b2e18SPierre-Alain TORET * are met: 13114b2e18SPierre-Alain TORET * 1. Redistributions of source code must retain the above copyright 14114b2e18SPierre-Alain TORET * notice, this list of conditions and the following disclaimer. 15114b2e18SPierre-Alain TORET * 2. Redistributions in binary form must reproduce the above copyright 16114b2e18SPierre-Alain TORET * notice, this list of conditions and the following disclaimer in the 17114b2e18SPierre-Alain TORET * documentation and/or other materials provided with the distribution. 18114b2e18SPierre-Alain TORET * 19114b2e18SPierre-Alain TORET * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20114b2e18SPierre-Alain TORET * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21114b2e18SPierre-Alain TORET * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22114b2e18SPierre-Alain TORET * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 23114b2e18SPierre-Alain TORET * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24114b2e18SPierre-Alain TORET * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25114b2e18SPierre-Alain TORET * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26114b2e18SPierre-Alain TORET * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 27114b2e18SPierre-Alain TORET * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 28114b2e18SPierre-Alain TORET * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29114b2e18SPierre-Alain TORET * POSSIBILITY OF SUCH DAMAGE. 30*23be8282SSascha Wildner * 31*23be8282SSascha Wildner * $FreeBSD: head/sys/dev/amdtemp/amdtemp.c 361011 2020-05-13 18:07:37Z kevans $ 32114b2e18SPierre-Alain TORET */ 33114b2e18SPierre-Alain TORET 34114b2e18SPierre-Alain TORET /* 35114b2e18SPierre-Alain TORET * Driver for the AMD CPU on-die thermal sensors. 36114b2e18SPierre-Alain TORET * Initially based on the k8temp Linux driver. 37114b2e18SPierre-Alain TORET */ 38114b2e18SPierre-Alain TORET 39114b2e18SPierre-Alain TORET #include <sys/param.h> 40114b2e18SPierre-Alain TORET #include <sys/bus.h> 41114b2e18SPierre-Alain TORET #include <sys/conf.h> 42114b2e18SPierre-Alain TORET #include <sys/kernel.h> 43114b2e18SPierre-Alain TORET #include <sys/module.h> 44114b2e18SPierre-Alain TORET #include <sys/sysctl.h> 45114b2e18SPierre-Alain TORET #include <sys/systm.h> 46114b2e18SPierre-Alain TORET 47114b2e18SPierre-Alain TORET #include <machine/cpufunc.h> 48114b2e18SPierre-Alain TORET #include <machine/md_var.h> 49114b2e18SPierre-Alain TORET #include <machine/specialreg.h> 50114b2e18SPierre-Alain TORET 51114b2e18SPierre-Alain TORET #include <bus/pci/pcivar.h> 52114b2e18SPierre-Alain TORET #include <bus/pci/pci_cfgreg.h> 53114b2e18SPierre-Alain TORET 54114b2e18SPierre-Alain TORET #include <dev/powermng/amdsmn/amdsmn.h> 55114b2e18SPierre-Alain TORET 56114b2e18SPierre-Alain TORET typedef enum { 57114b2e18SPierre-Alain TORET CORE0_SENSOR0, 58114b2e18SPierre-Alain TORET CORE0_SENSOR1, 59114b2e18SPierre-Alain TORET CORE1_SENSOR0, 60114b2e18SPierre-Alain TORET CORE1_SENSOR1, 61114b2e18SPierre-Alain TORET CORE0, 62114b2e18SPierre-Alain TORET CORE1, 63114b2e18SPierre-Alain TORET CCD1, 64114b2e18SPierre-Alain TORET CCD_BASE = CCD1, 65114b2e18SPierre-Alain TORET CCD2, 66114b2e18SPierre-Alain TORET CCD3, 67114b2e18SPierre-Alain TORET CCD4, 68114b2e18SPierre-Alain TORET CCD5, 69114b2e18SPierre-Alain TORET CCD6, 70114b2e18SPierre-Alain TORET CCD7, 71114b2e18SPierre-Alain TORET CCD8, 72114b2e18SPierre-Alain TORET CCD_MAX = CCD8, 73114b2e18SPierre-Alain TORET NUM_CCDS = CCD_MAX - CCD_BASE + 1, 74114b2e18SPierre-Alain TORET } amdsensor_t; 75114b2e18SPierre-Alain TORET 76114b2e18SPierre-Alain TORET struct amdtemp_softc { 77114b2e18SPierre-Alain TORET int sc_ncores; 78114b2e18SPierre-Alain TORET int sc_ntemps; 79114b2e18SPierre-Alain TORET int sc_flags; 80114b2e18SPierre-Alain TORET #define AMDTEMP_FLAG_CS_SWAP 0x01 /* ThermSenseCoreSel is inverted. */ 81114b2e18SPierre-Alain TORET #define AMDTEMP_FLAG_CT_10BIT 0x02 /* CurTmp is 10-bit wide. */ 82114b2e18SPierre-Alain TORET #define AMDTEMP_FLAG_ALT_OFFSET 0x04 /* CurTmp starts at -28C. */ 83114b2e18SPierre-Alain TORET int32_t sc_offset; 84114b2e18SPierre-Alain TORET int32_t (*sc_gettemp)(device_t, amdsensor_t); 85114b2e18SPierre-Alain TORET struct sysctl_oid *sc_sysctl_cpu[MAXCPU]; 86114b2e18SPierre-Alain TORET struct intr_config_hook sc_ich; 87114b2e18SPierre-Alain TORET device_t sc_smn; 88114b2e18SPierre-Alain TORET }; 89114b2e18SPierre-Alain TORET 90114b2e18SPierre-Alain TORET /* 91114b2e18SPierre-Alain TORET * N.B. The numbers in macro names below are significant and represent CPU 92114b2e18SPierre-Alain TORET * family and model numbers. Do not make up fictitious family or model numbers 93114b2e18SPierre-Alain TORET * when adding support for new devices. 94114b2e18SPierre-Alain TORET */ 95114b2e18SPierre-Alain TORET #define VENDORID_AMD 0x1022 96114b2e18SPierre-Alain TORET #define DEVICEID_AMD_MISC0F 0x1103 97114b2e18SPierre-Alain TORET #define DEVICEID_AMD_MISC10 0x1203 98114b2e18SPierre-Alain TORET #define DEVICEID_AMD_MISC11 0x1303 99114b2e18SPierre-Alain TORET #define DEVICEID_AMD_MISC14 0x1703 100114b2e18SPierre-Alain TORET #define DEVICEID_AMD_MISC15 0x1603 101114b2e18SPierre-Alain TORET #define DEVICEID_AMD_MISC15_M10H 0x1403 102114b2e18SPierre-Alain TORET #define DEVICEID_AMD_MISC15_M30H 0x141d 103114b2e18SPierre-Alain TORET #define DEVICEID_AMD_MISC15_M60H_ROOT 0x1576 104114b2e18SPierre-Alain TORET #define DEVICEID_AMD_MISC16 0x1533 105114b2e18SPierre-Alain TORET #define DEVICEID_AMD_MISC16_M30H 0x1583 106114b2e18SPierre-Alain TORET #define DEVICEID_AMD_HOSTB17H_ROOT 0x1450 107114b2e18SPierre-Alain TORET #define DEVICEID_AMD_HOSTB17H_M10H_ROOT 0x15d0 108114b2e18SPierre-Alain TORET #define DEVICEID_AMD_HOSTB17H_M30H_ROOT 0x1480 /* Also M70h. */ 109114b2e18SPierre-Alain TORET 110114b2e18SPierre-Alain TORET static const struct amdtemp_product { 111114b2e18SPierre-Alain TORET uint16_t amdtemp_vendorid; 112114b2e18SPierre-Alain TORET uint16_t amdtemp_deviceid; 113114b2e18SPierre-Alain TORET /* 114114b2e18SPierre-Alain TORET * 0xFC register is only valid on the D18F3 PCI device; SMN temp 115114b2e18SPierre-Alain TORET * drivers do not attach to that device. 116114b2e18SPierre-Alain TORET */ 117114b2e18SPierre-Alain TORET bool amdtemp_has_cpuid; 118114b2e18SPierre-Alain TORET } amdtemp_products[] = { 119114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_MISC0F, true }, 120114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_MISC10, true }, 121114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_MISC11, true }, 122114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_MISC14, true }, 123114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_MISC15, true }, 124114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_MISC15_M10H, true }, 125114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_MISC15_M30H, true }, 126114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_MISC15_M60H_ROOT, false }, 127114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_MISC16, true }, 128114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_MISC16_M30H, true }, 129114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_HOSTB17H_ROOT, false }, 130114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_HOSTB17H_M10H_ROOT, false }, 131114b2e18SPierre-Alain TORET { VENDORID_AMD, DEVICEID_AMD_HOSTB17H_M30H_ROOT, false }, 132114b2e18SPierre-Alain TORET }; 133114b2e18SPierre-Alain TORET 134114b2e18SPierre-Alain TORET /* 135114b2e18SPierre-Alain TORET * Reported Temperature Control Register, family 0Fh-15h (some models), 16h. 136114b2e18SPierre-Alain TORET */ 137114b2e18SPierre-Alain TORET #define AMDTEMP_REPTMP_CTRL 0xa4 138114b2e18SPierre-Alain TORET 139114b2e18SPierre-Alain TORET #define AMDTEMP_REPTMP10H_CURTMP_MASK 0x7ff 140114b2e18SPierre-Alain TORET #define AMDTEMP_REPTMP10H_CURTMP_SHIFT 21 141114b2e18SPierre-Alain TORET #define AMDTEMP_REPTMP10H_TJSEL_MASK 0x3 142114b2e18SPierre-Alain TORET #define AMDTEMP_REPTMP10H_TJSEL_SHIFT 16 143114b2e18SPierre-Alain TORET 144114b2e18SPierre-Alain TORET /* 145114b2e18SPierre-Alain TORET * Reported Temperature, Family 15h, M60+ 146114b2e18SPierre-Alain TORET * 147114b2e18SPierre-Alain TORET * Same register bit definitions as other Family 15h CPUs, but access is 148114b2e18SPierre-Alain TORET * indirect via SMN, like Family 17h. 149114b2e18SPierre-Alain TORET */ 150114b2e18SPierre-Alain TORET #define AMDTEMP_15H_M60H_REPTMP_CTRL 0xd8200ca4 151114b2e18SPierre-Alain TORET 152114b2e18SPierre-Alain TORET /* 153114b2e18SPierre-Alain TORET * Reported Temperature, Family 17h 154114b2e18SPierre-Alain TORET * 155114b2e18SPierre-Alain TORET * According to AMD OSRR for 17H, section 4.2.1, bits 31-21 of this register 156114b2e18SPierre-Alain TORET * provide the current temp. bit 19, when clear, means the temp is reported in 157114b2e18SPierre-Alain TORET * a range 0.."225C" (probable typo for 255C), and when set changes the range 158114b2e18SPierre-Alain TORET * to -49..206C. 159114b2e18SPierre-Alain TORET */ 160114b2e18SPierre-Alain TORET #define AMDTEMP_17H_CUR_TMP 0x59800 161114b2e18SPierre-Alain TORET #define AMDTEMP_17H_CUR_TMP_RANGE_SEL (1u << 19) 162114b2e18SPierre-Alain TORET /* 163114b2e18SPierre-Alain TORET * The following register set was discovered experimentally by Ondrej Čerman 164114b2e18SPierre-Alain TORET * and collaborators, but is not (yet) documented in a PPR/OSRR (other than 165114b2e18SPierre-Alain TORET * the M70H PPR SMN memory map showing [0x59800, +0x314] as allocated to 166114b2e18SPierre-Alain TORET * SMU::THM). It seems plausible and the Linux sensor folks have adopted it. 167114b2e18SPierre-Alain TORET */ 168114b2e18SPierre-Alain TORET #define AMDTEMP_17H_CCD_TMP_BASE 0x59954 169114b2e18SPierre-Alain TORET #define AMDTEMP_17H_CCD_TMP_VALID (1u << 11) 170114b2e18SPierre-Alain TORET 171114b2e18SPierre-Alain TORET /* 172114b2e18SPierre-Alain TORET * AMD temperature range adjustment, in deciKelvins (i.e., 49.0 Celsius). 173114b2e18SPierre-Alain TORET */ 174114b2e18SPierre-Alain TORET #define AMDTEMP_CURTMP_RANGE_ADJUST 490 175114b2e18SPierre-Alain TORET 176114b2e18SPierre-Alain TORET /* 177114b2e18SPierre-Alain TORET * Thermaltrip Status Register (Family 0Fh only) 178114b2e18SPierre-Alain TORET */ 179114b2e18SPierre-Alain TORET #define AMDTEMP_THERMTP_STAT 0xe4 180114b2e18SPierre-Alain TORET #define AMDTEMP_TTSR_SELCORE 0x04 181114b2e18SPierre-Alain TORET #define AMDTEMP_TTSR_SELSENSOR 0x40 182114b2e18SPierre-Alain TORET 183114b2e18SPierre-Alain TORET /* 184114b2e18SPierre-Alain TORET * DRAM Configuration High Register 185114b2e18SPierre-Alain TORET */ 186114b2e18SPierre-Alain TORET #define AMDTEMP_DRAM_CONF_HIGH 0x94 /* Function 2 */ 187114b2e18SPierre-Alain TORET #define AMDTEMP_DRAM_MODE_DDR3 0x0100 188114b2e18SPierre-Alain TORET 189114b2e18SPierre-Alain TORET /* 190114b2e18SPierre-Alain TORET * CPU Family/Model Register 191114b2e18SPierre-Alain TORET */ 192114b2e18SPierre-Alain TORET #define AMDTEMP_CPUID 0xfc 193114b2e18SPierre-Alain TORET 194114b2e18SPierre-Alain TORET /* 195114b2e18SPierre-Alain TORET * Device methods. 196114b2e18SPierre-Alain TORET */ 197114b2e18SPierre-Alain TORET static void amdtemp_identify(driver_t *driver, device_t parent); 198114b2e18SPierre-Alain TORET static int amdtemp_probe(device_t dev); 199114b2e18SPierre-Alain TORET static int amdtemp_attach(device_t dev); 200114b2e18SPierre-Alain TORET static void amdtemp_intrhook(void *arg); 201114b2e18SPierre-Alain TORET static int amdtemp_detach(device_t dev); 202114b2e18SPierre-Alain TORET static int32_t amdtemp_gettemp0f(device_t dev, amdsensor_t sensor); 203114b2e18SPierre-Alain TORET static int32_t amdtemp_gettemp(device_t dev, amdsensor_t sensor); 204114b2e18SPierre-Alain TORET static int32_t amdtemp_gettemp15hm60h(device_t dev, amdsensor_t sensor); 205114b2e18SPierre-Alain TORET static int32_t amdtemp_gettemp17h(device_t dev, amdsensor_t sensor); 206114b2e18SPierre-Alain TORET static void amdtemp_probe_ccd_sensors17h(device_t dev, uint32_t model); 207114b2e18SPierre-Alain TORET static int amdtemp_sysctl(SYSCTL_HANDLER_ARGS); 208114b2e18SPierre-Alain TORET 209114b2e18SPierre-Alain TORET static device_method_t amdtemp_methods[] = { 210114b2e18SPierre-Alain TORET /* Device interface */ 211114b2e18SPierre-Alain TORET DEVMETHOD(device_identify, amdtemp_identify), 212114b2e18SPierre-Alain TORET DEVMETHOD(device_probe, amdtemp_probe), 213114b2e18SPierre-Alain TORET DEVMETHOD(device_attach, amdtemp_attach), 214114b2e18SPierre-Alain TORET DEVMETHOD(device_detach, amdtemp_detach), 215114b2e18SPierre-Alain TORET 216114b2e18SPierre-Alain TORET DEVMETHOD_END 217114b2e18SPierre-Alain TORET }; 218114b2e18SPierre-Alain TORET 219114b2e18SPierre-Alain TORET static driver_t amdtemp_driver = { 220114b2e18SPierre-Alain TORET "amdtemp", 221114b2e18SPierre-Alain TORET amdtemp_methods, 222114b2e18SPierre-Alain TORET sizeof(struct amdtemp_softc), 223114b2e18SPierre-Alain TORET }; 224114b2e18SPierre-Alain TORET 225114b2e18SPierre-Alain TORET static devclass_t amdtemp_devclass; 226114b2e18SPierre-Alain TORET DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL); 227114b2e18SPierre-Alain TORET MODULE_VERSION(amdtemp, 1); 228114b2e18SPierre-Alain TORET MODULE_DEPEND(amdtemp, amdsmn, 1, 1, 1); 229114b2e18SPierre-Alain TORET #if !defined(__DragonFly__) 230114b2e18SPierre-Alain TORET MODULE_PNP_INFO("U16:vendor;U16:device", pci, amdtemp, amdtemp_products, 231114b2e18SPierre-Alain TORET nitems(amdtemp_products)); 232114b2e18SPierre-Alain TORET #endif 233114b2e18SPierre-Alain TORET 234114b2e18SPierre-Alain TORET static bool 235114b2e18SPierre-Alain TORET amdtemp_match(device_t dev, const struct amdtemp_product **product_out) 236114b2e18SPierre-Alain TORET { 237114b2e18SPierre-Alain TORET int i; 238114b2e18SPierre-Alain TORET uint16_t vendor, devid; 239114b2e18SPierre-Alain TORET 240114b2e18SPierre-Alain TORET vendor = pci_get_vendor(dev); 241114b2e18SPierre-Alain TORET devid = pci_get_device(dev); 242114b2e18SPierre-Alain TORET 243114b2e18SPierre-Alain TORET for (i = 0; i < nitems(amdtemp_products); i++) { 244114b2e18SPierre-Alain TORET if (vendor == amdtemp_products[i].amdtemp_vendorid && 245114b2e18SPierre-Alain TORET devid == amdtemp_products[i].amdtemp_deviceid) { 246114b2e18SPierre-Alain TORET if (product_out != NULL) 247114b2e18SPierre-Alain TORET *product_out = &amdtemp_products[i]; 248114b2e18SPierre-Alain TORET return (true); 249114b2e18SPierre-Alain TORET } 250114b2e18SPierre-Alain TORET } 251114b2e18SPierre-Alain TORET return (false); 252114b2e18SPierre-Alain TORET } 253114b2e18SPierre-Alain TORET 254114b2e18SPierre-Alain TORET static void 255114b2e18SPierre-Alain TORET amdtemp_identify(driver_t *driver, device_t parent) 256114b2e18SPierre-Alain TORET { 257114b2e18SPierre-Alain TORET device_t child; 258114b2e18SPierre-Alain TORET 259114b2e18SPierre-Alain TORET /* Make sure we're not being doubly invoked. */ 260114b2e18SPierre-Alain TORET if (device_find_child(parent, "amdtemp", -1) != NULL) 261114b2e18SPierre-Alain TORET return; 262114b2e18SPierre-Alain TORET 263114b2e18SPierre-Alain TORET if (amdtemp_match(parent, NULL)) { 264114b2e18SPierre-Alain TORET child = device_add_child(parent, "amdtemp", -1); 265114b2e18SPierre-Alain TORET if (child == NULL) 266114b2e18SPierre-Alain TORET device_printf(parent, "add amdtemp child failed\n"); 267114b2e18SPierre-Alain TORET } 268114b2e18SPierre-Alain TORET } 269114b2e18SPierre-Alain TORET 270114b2e18SPierre-Alain TORET static int 271114b2e18SPierre-Alain TORET amdtemp_probe(device_t dev) 272114b2e18SPierre-Alain TORET { 273114b2e18SPierre-Alain TORET uint32_t family, model; 274114b2e18SPierre-Alain TORET 275114b2e18SPierre-Alain TORET if (resource_disabled("amdtemp", 0)) 276114b2e18SPierre-Alain TORET return (ENXIO); 277114b2e18SPierre-Alain TORET if (!amdtemp_match(device_get_parent(dev), NULL)) 278114b2e18SPierre-Alain TORET return (ENXIO); 279114b2e18SPierre-Alain TORET 280114b2e18SPierre-Alain TORET family = CPUID_TO_FAMILY(cpu_id); 281114b2e18SPierre-Alain TORET model = CPUID_TO_MODEL(cpu_id); 282114b2e18SPierre-Alain TORET 283114b2e18SPierre-Alain TORET switch (family) { 284114b2e18SPierre-Alain TORET case 0x0f: 285114b2e18SPierre-Alain TORET if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) || 286114b2e18SPierre-Alain TORET (model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1)) 287114b2e18SPierre-Alain TORET return (ENXIO); 288114b2e18SPierre-Alain TORET break; 289114b2e18SPierre-Alain TORET case 0x10: 290114b2e18SPierre-Alain TORET case 0x11: 291114b2e18SPierre-Alain TORET case 0x12: 292114b2e18SPierre-Alain TORET case 0x14: 293114b2e18SPierre-Alain TORET case 0x15: 294114b2e18SPierre-Alain TORET case 0x16: 295114b2e18SPierre-Alain TORET case 0x17: 296114b2e18SPierre-Alain TORET break; 297114b2e18SPierre-Alain TORET default: 298114b2e18SPierre-Alain TORET return (ENXIO); 299114b2e18SPierre-Alain TORET } 300114b2e18SPierre-Alain TORET device_set_desc(dev, "AMD CPU On-Die Thermal Sensors"); 301114b2e18SPierre-Alain TORET 302114b2e18SPierre-Alain TORET return (BUS_PROBE_GENERIC); 303114b2e18SPierre-Alain TORET } 304114b2e18SPierre-Alain TORET 305114b2e18SPierre-Alain TORET static int 306114b2e18SPierre-Alain TORET amdtemp_attach(device_t dev) 307114b2e18SPierre-Alain TORET { 308114b2e18SPierre-Alain TORET char tn[32]; 309114b2e18SPierre-Alain TORET u_int regs[4]; 310114b2e18SPierre-Alain TORET const struct amdtemp_product *product; 311114b2e18SPierre-Alain TORET struct amdtemp_softc *sc; 312114b2e18SPierre-Alain TORET struct sysctl_ctx_list *sysctlctx; 313114b2e18SPierre-Alain TORET struct sysctl_oid *sysctlnode; 314114b2e18SPierre-Alain TORET uint32_t cpuid, family, model; 315114b2e18SPierre-Alain TORET u_int bid; 316114b2e18SPierre-Alain TORET int erratum319, unit; 317114b2e18SPierre-Alain TORET bool needsmn; 318114b2e18SPierre-Alain TORET 319114b2e18SPierre-Alain TORET sc = device_get_softc(dev); 320114b2e18SPierre-Alain TORET erratum319 = 0; 321114b2e18SPierre-Alain TORET needsmn = false; 322114b2e18SPierre-Alain TORET 323114b2e18SPierre-Alain TORET if (!amdtemp_match(device_get_parent(dev), &product)) 324114b2e18SPierre-Alain TORET return (ENXIO); 325114b2e18SPierre-Alain TORET 326114b2e18SPierre-Alain TORET cpuid = cpu_id; 327114b2e18SPierre-Alain TORET family = CPUID_TO_FAMILY(cpuid); 328114b2e18SPierre-Alain TORET model = CPUID_TO_MODEL(cpuid); 329114b2e18SPierre-Alain TORET 330114b2e18SPierre-Alain TORET /* 331114b2e18SPierre-Alain TORET * This checks for the byzantine condition of running a heterogenous 332114b2e18SPierre-Alain TORET * revision multi-socket system where the attach thread is potentially 333114b2e18SPierre-Alain TORET * probing a remote socket's PCI device. 334114b2e18SPierre-Alain TORET * 335114b2e18SPierre-Alain TORET * Currently, such scenarios are unsupported on models using the SMN 336114b2e18SPierre-Alain TORET * (because on those models, amdtemp(4) attaches to a different PCI 337114b2e18SPierre-Alain TORET * device than the one that contains AMDTEMP_CPUID). 338114b2e18SPierre-Alain TORET * 339114b2e18SPierre-Alain TORET * The ancient 0x0F family of devices only supports this register from 340114b2e18SPierre-Alain TORET * models 40h+. 341114b2e18SPierre-Alain TORET */ 342114b2e18SPierre-Alain TORET if (product->amdtemp_has_cpuid && (family > 0x0f || 343114b2e18SPierre-Alain TORET (family == 0x0f && model >= 0x40))) { 344114b2e18SPierre-Alain TORET cpuid = pci_read_config(device_get_parent(dev), AMDTEMP_CPUID, 345114b2e18SPierre-Alain TORET 4); 346114b2e18SPierre-Alain TORET family = CPUID_TO_FAMILY(cpuid); 347114b2e18SPierre-Alain TORET model = CPUID_TO_MODEL(cpuid); 348114b2e18SPierre-Alain TORET } 349114b2e18SPierre-Alain TORET 350114b2e18SPierre-Alain TORET switch (family) { 351114b2e18SPierre-Alain TORET case 0x0f: 352114b2e18SPierre-Alain TORET /* 353114b2e18SPierre-Alain TORET * Thermaltrip Status Register 354114b2e18SPierre-Alain TORET * 355114b2e18SPierre-Alain TORET * - ThermSenseCoreSel 356114b2e18SPierre-Alain TORET * 357114b2e18SPierre-Alain TORET * Revision F & G: 0 - Core1, 1 - Core0 358114b2e18SPierre-Alain TORET * Other: 0 - Core0, 1 - Core1 359114b2e18SPierre-Alain TORET * 360114b2e18SPierre-Alain TORET * - CurTmp 361114b2e18SPierre-Alain TORET * 362114b2e18SPierre-Alain TORET * Revision G: bits 23-14 363114b2e18SPierre-Alain TORET * Other: bits 23-16 364114b2e18SPierre-Alain TORET * 365114b2e18SPierre-Alain TORET * XXX According to the BKDG, CurTmp, ThermSenseSel and 366114b2e18SPierre-Alain TORET * ThermSenseCoreSel bits were introduced in Revision F 367114b2e18SPierre-Alain TORET * but CurTmp seems working fine as early as Revision C. 368114b2e18SPierre-Alain TORET * However, it is not clear whether ThermSenseSel and/or 369114b2e18SPierre-Alain TORET * ThermSenseCoreSel work in undocumented cases as well. 370114b2e18SPierre-Alain TORET * In fact, the Linux driver suggests it may not work but 371114b2e18SPierre-Alain TORET * we just assume it does until we find otherwise. 372114b2e18SPierre-Alain TORET * 373114b2e18SPierre-Alain TORET * XXX According to Linux, CurTmp starts at -28C on 374114b2e18SPierre-Alain TORET * Socket AM2 Revision G processors, which is not 375114b2e18SPierre-Alain TORET * documented anywhere. 376114b2e18SPierre-Alain TORET */ 377114b2e18SPierre-Alain TORET if (model >= 0x40) 378114b2e18SPierre-Alain TORET sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP; 379114b2e18SPierre-Alain TORET if (model >= 0x60 && model != 0xc1) { 380114b2e18SPierre-Alain TORET do_cpuid(0x80000001, regs); 381114b2e18SPierre-Alain TORET bid = (regs[1] >> 9) & 0x1f; 382114b2e18SPierre-Alain TORET switch (model) { 383114b2e18SPierre-Alain TORET case 0x68: /* Socket S1g1 */ 384114b2e18SPierre-Alain TORET case 0x6c: 385114b2e18SPierre-Alain TORET case 0x7c: 386114b2e18SPierre-Alain TORET break; 387114b2e18SPierre-Alain TORET case 0x6b: /* Socket AM2 and ASB1 (2 cores) */ 388114b2e18SPierre-Alain TORET if (bid != 0x0b && bid != 0x0c) 389114b2e18SPierre-Alain TORET sc->sc_flags |= 390114b2e18SPierre-Alain TORET AMDTEMP_FLAG_ALT_OFFSET; 391114b2e18SPierre-Alain TORET break; 392114b2e18SPierre-Alain TORET case 0x6f: /* Socket AM2 and ASB1 (1 core) */ 393114b2e18SPierre-Alain TORET case 0x7f: 394114b2e18SPierre-Alain TORET if (bid != 0x07 && bid != 0x09 && 395114b2e18SPierre-Alain TORET bid != 0x0c) 396114b2e18SPierre-Alain TORET sc->sc_flags |= 397114b2e18SPierre-Alain TORET AMDTEMP_FLAG_ALT_OFFSET; 398114b2e18SPierre-Alain TORET break; 399114b2e18SPierre-Alain TORET default: 400114b2e18SPierre-Alain TORET sc->sc_flags |= AMDTEMP_FLAG_ALT_OFFSET; 401114b2e18SPierre-Alain TORET } 402114b2e18SPierre-Alain TORET sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT; 403114b2e18SPierre-Alain TORET } 404114b2e18SPierre-Alain TORET 405114b2e18SPierre-Alain TORET /* 406114b2e18SPierre-Alain TORET * There are two sensors per core. 407114b2e18SPierre-Alain TORET */ 408114b2e18SPierre-Alain TORET sc->sc_ntemps = 2; 409114b2e18SPierre-Alain TORET 410114b2e18SPierre-Alain TORET sc->sc_gettemp = amdtemp_gettemp0f; 411114b2e18SPierre-Alain TORET break; 412114b2e18SPierre-Alain TORET case 0x10: 413114b2e18SPierre-Alain TORET /* 414114b2e18SPierre-Alain TORET * Erratum 319 Inaccurate Temperature Measurement 415114b2e18SPierre-Alain TORET * 416114b2e18SPierre-Alain TORET * http://support.amd.com/us/Processor_TechDocs/41322.pdf 417114b2e18SPierre-Alain TORET */ 418114b2e18SPierre-Alain TORET do_cpuid(0x80000001, regs); 419114b2e18SPierre-Alain TORET switch ((regs[1] >> 28) & 0xf) { 420114b2e18SPierre-Alain TORET case 0: /* Socket F */ 421114b2e18SPierre-Alain TORET erratum319 = 1; 422114b2e18SPierre-Alain TORET break; 423114b2e18SPierre-Alain TORET case 1: /* Socket AM2+ or AM3 */ 424114b2e18SPierre-Alain TORET if ((pci_cfgregread(pci_get_bus(dev), 425114b2e18SPierre-Alain TORET pci_get_slot(dev), 2, AMDTEMP_DRAM_CONF_HIGH, 2) & 426114b2e18SPierre-Alain TORET AMDTEMP_DRAM_MODE_DDR3) != 0 || model > 0x04 || 427114b2e18SPierre-Alain TORET (model == 0x04 && (cpuid & CPUID_STEPPING) >= 3)) 428114b2e18SPierre-Alain TORET break; 429114b2e18SPierre-Alain TORET /* XXX 00100F42h (RB-C2) exists in both formats. */ 430114b2e18SPierre-Alain TORET erratum319 = 1; 431114b2e18SPierre-Alain TORET break; 432114b2e18SPierre-Alain TORET } 433114b2e18SPierre-Alain TORET /* FALLTHROUGH */ 434114b2e18SPierre-Alain TORET case 0x11: 435114b2e18SPierre-Alain TORET case 0x12: 436114b2e18SPierre-Alain TORET case 0x14: 437114b2e18SPierre-Alain TORET case 0x15: 438114b2e18SPierre-Alain TORET case 0x16: 439114b2e18SPierre-Alain TORET sc->sc_ntemps = 1; 440114b2e18SPierre-Alain TORET /* 441114b2e18SPierre-Alain TORET * Some later (60h+) models of family 15h use a similar SMN 442114b2e18SPierre-Alain TORET * network as family 17h. (However, the register index differs 443114b2e18SPierre-Alain TORET * from 17h and the decoding matches other 10h-15h models, 444114b2e18SPierre-Alain TORET * which differ from 17h.) 445114b2e18SPierre-Alain TORET */ 446114b2e18SPierre-Alain TORET if (family == 0x15 && model >= 0x60) { 447114b2e18SPierre-Alain TORET sc->sc_gettemp = amdtemp_gettemp15hm60h; 448114b2e18SPierre-Alain TORET needsmn = true; 449114b2e18SPierre-Alain TORET } else 450114b2e18SPierre-Alain TORET sc->sc_gettemp = amdtemp_gettemp; 451114b2e18SPierre-Alain TORET break; 452114b2e18SPierre-Alain TORET case 0x17: 453114b2e18SPierre-Alain TORET sc->sc_ntemps = 1; 454114b2e18SPierre-Alain TORET sc->sc_gettemp = amdtemp_gettemp17h; 455114b2e18SPierre-Alain TORET needsmn = true; 456114b2e18SPierre-Alain TORET break; 457114b2e18SPierre-Alain TORET default: 458114b2e18SPierre-Alain TORET device_printf(dev, "Bogus family 0x%x\n", family); 459114b2e18SPierre-Alain TORET return (ENXIO); 460114b2e18SPierre-Alain TORET } 461114b2e18SPierre-Alain TORET 462114b2e18SPierre-Alain TORET if (needsmn) { 463114b2e18SPierre-Alain TORET sc->sc_smn = device_find_child( 464114b2e18SPierre-Alain TORET device_get_parent(dev), "amdsmn", -1); 465114b2e18SPierre-Alain TORET if (sc->sc_smn == NULL) { 466114b2e18SPierre-Alain TORET if (bootverbose) 467114b2e18SPierre-Alain TORET device_printf(dev, "No SMN device found\n"); 468114b2e18SPierre-Alain TORET return (ENXIO); 469114b2e18SPierre-Alain TORET } 470114b2e18SPierre-Alain TORET } 471114b2e18SPierre-Alain TORET 472114b2e18SPierre-Alain TORET /* Find number of cores per package. */ 473114b2e18SPierre-Alain TORET sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ? 474114b2e18SPierre-Alain TORET (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1; 475114b2e18SPierre-Alain TORET if (sc->sc_ncores > MAXCPU) 476114b2e18SPierre-Alain TORET return (ENXIO); 477114b2e18SPierre-Alain TORET 478114b2e18SPierre-Alain TORET if (erratum319) 479114b2e18SPierre-Alain TORET device_printf(dev, 480114b2e18SPierre-Alain TORET "Erratum 319: temperature measurement may be inaccurate\n"); 481114b2e18SPierre-Alain TORET if (bootverbose) 482114b2e18SPierre-Alain TORET device_printf(dev, "Found %d cores and %d sensors.\n", 483114b2e18SPierre-Alain TORET sc->sc_ncores, 484114b2e18SPierre-Alain TORET sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1); 485114b2e18SPierre-Alain TORET 486114b2e18SPierre-Alain TORET /* 487114b2e18SPierre-Alain TORET * dev.amdtemp.N tree. 488114b2e18SPierre-Alain TORET */ 489114b2e18SPierre-Alain TORET unit = device_get_unit(dev); 490114b2e18SPierre-Alain TORET ksnprintf(tn, sizeof(tn), "dev.amdtemp.%d.sensor_offset", unit); 491114b2e18SPierre-Alain TORET TUNABLE_INT_FETCH(tn, &sc->sc_offset); 492114b2e18SPierre-Alain TORET 493114b2e18SPierre-Alain TORET sysctlctx = device_get_sysctl_ctx(dev); 494114b2e18SPierre-Alain TORET SYSCTL_ADD_INT(sysctlctx, 495114b2e18SPierre-Alain TORET SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 496114b2e18SPierre-Alain TORET "sensor_offset", CTLFLAG_RW, &sc->sc_offset, 0, 497114b2e18SPierre-Alain TORET "Temperature sensor offset"); 498114b2e18SPierre-Alain TORET sysctlnode = SYSCTL_ADD_NODE(sysctlctx, 499114b2e18SPierre-Alain TORET SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 500114b2e18SPierre-Alain TORET "core0", CTLFLAG_RD, 0, "Core 0"); 501114b2e18SPierre-Alain TORET 502114b2e18SPierre-Alain TORET SYSCTL_ADD_PROC(sysctlctx, 503114b2e18SPierre-Alain TORET SYSCTL_CHILDREN(sysctlnode), 504114b2e18SPierre-Alain TORET OID_AUTO, "sensor0", 505114b2e18SPierre-Alain TORET CTLTYPE_INT | CTLFLAG_RD, 506114b2e18SPierre-Alain TORET dev, CORE0_SENSOR0, amdtemp_sysctl, "IK", 507114b2e18SPierre-Alain TORET "Core 0 / Sensor 0 temperature"); 508114b2e18SPierre-Alain TORET 509114b2e18SPierre-Alain TORET if (family == 0x17) 510114b2e18SPierre-Alain TORET amdtemp_probe_ccd_sensors17h(dev, model); 511114b2e18SPierre-Alain TORET else if (sc->sc_ntemps > 1) { 512114b2e18SPierre-Alain TORET SYSCTL_ADD_PROC(sysctlctx, 513114b2e18SPierre-Alain TORET SYSCTL_CHILDREN(sysctlnode), 514114b2e18SPierre-Alain TORET OID_AUTO, "sensor1", 515114b2e18SPierre-Alain TORET CTLTYPE_INT | CTLFLAG_RD, 516114b2e18SPierre-Alain TORET dev, CORE0_SENSOR1, amdtemp_sysctl, "IK", 517114b2e18SPierre-Alain TORET "Core 0 / Sensor 1 temperature"); 518114b2e18SPierre-Alain TORET 519114b2e18SPierre-Alain TORET if (sc->sc_ncores > 1) { 520114b2e18SPierre-Alain TORET sysctlnode = SYSCTL_ADD_NODE(sysctlctx, 521114b2e18SPierre-Alain TORET SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 522114b2e18SPierre-Alain TORET OID_AUTO, "core1", CTLFLAG_RD, 523114b2e18SPierre-Alain TORET 0, "Core 1"); 524114b2e18SPierre-Alain TORET 525114b2e18SPierre-Alain TORET SYSCTL_ADD_PROC(sysctlctx, 526114b2e18SPierre-Alain TORET SYSCTL_CHILDREN(sysctlnode), 527114b2e18SPierre-Alain TORET OID_AUTO, "sensor0", 528114b2e18SPierre-Alain TORET CTLTYPE_INT | CTLFLAG_RD, 529114b2e18SPierre-Alain TORET dev, CORE1_SENSOR0, amdtemp_sysctl, "IK", 530114b2e18SPierre-Alain TORET "Core 1 / Sensor 0 temperature"); 531114b2e18SPierre-Alain TORET 532114b2e18SPierre-Alain TORET SYSCTL_ADD_PROC(sysctlctx, 533114b2e18SPierre-Alain TORET SYSCTL_CHILDREN(sysctlnode), 534114b2e18SPierre-Alain TORET OID_AUTO, "sensor1", 535114b2e18SPierre-Alain TORET CTLTYPE_INT | CTLFLAG_RD, 536114b2e18SPierre-Alain TORET dev, CORE1_SENSOR1, amdtemp_sysctl, "IK", 537114b2e18SPierre-Alain TORET "Core 1 / Sensor 1 temperature"); 538114b2e18SPierre-Alain TORET } 539114b2e18SPierre-Alain TORET } 540114b2e18SPierre-Alain TORET 541114b2e18SPierre-Alain TORET /* 542114b2e18SPierre-Alain TORET * Try to create dev.cpu sysctl entries and setup intrhook function. 543114b2e18SPierre-Alain TORET * This is needed because the cpu driver may be loaded late on boot, 544114b2e18SPierre-Alain TORET * after us. 545114b2e18SPierre-Alain TORET */ 546114b2e18SPierre-Alain TORET amdtemp_intrhook(dev); 547114b2e18SPierre-Alain TORET sc->sc_ich.ich_func = amdtemp_intrhook; 548114b2e18SPierre-Alain TORET sc->sc_ich.ich_arg = dev; 549114b2e18SPierre-Alain TORET if (config_intrhook_establish(&sc->sc_ich) != 0) { 550114b2e18SPierre-Alain TORET device_printf(dev, "config_intrhook_establish failed!\n"); 551114b2e18SPierre-Alain TORET return (ENXIO); 552114b2e18SPierre-Alain TORET } 553114b2e18SPierre-Alain TORET 554114b2e18SPierre-Alain TORET return (0); 555114b2e18SPierre-Alain TORET } 556114b2e18SPierre-Alain TORET 557114b2e18SPierre-Alain TORET void 558114b2e18SPierre-Alain TORET amdtemp_intrhook(void *arg) 559114b2e18SPierre-Alain TORET { 560114b2e18SPierre-Alain TORET struct amdtemp_softc *sc; 561114b2e18SPierre-Alain TORET struct sysctl_ctx_list *sysctlctx; 562114b2e18SPierre-Alain TORET device_t dev = (device_t)arg; 563114b2e18SPierre-Alain TORET device_t acpi, cpu, nexus; 564114b2e18SPierre-Alain TORET amdsensor_t sensor; 565114b2e18SPierre-Alain TORET int i; 566114b2e18SPierre-Alain TORET 567114b2e18SPierre-Alain TORET sc = device_get_softc(dev); 568114b2e18SPierre-Alain TORET 569114b2e18SPierre-Alain TORET /* 570114b2e18SPierre-Alain TORET * dev.cpu.N.temperature. 571114b2e18SPierre-Alain TORET */ 572114b2e18SPierre-Alain TORET nexus = device_find_child(root_bus, "nexus", 0); 573114b2e18SPierre-Alain TORET acpi = device_find_child(nexus, "acpi", 0); 574114b2e18SPierre-Alain TORET 575114b2e18SPierre-Alain TORET for (i = 0; i < sc->sc_ncores; i++) { 576114b2e18SPierre-Alain TORET if (sc->sc_sysctl_cpu[i] != NULL) 577114b2e18SPierre-Alain TORET continue; 578114b2e18SPierre-Alain TORET cpu = device_find_child(acpi, "cpu", 579114b2e18SPierre-Alain TORET device_get_unit(dev) * sc->sc_ncores + i); 580114b2e18SPierre-Alain TORET if (cpu != NULL) { 581114b2e18SPierre-Alain TORET sysctlctx = device_get_sysctl_ctx(cpu); 582114b2e18SPierre-Alain TORET 583114b2e18SPierre-Alain TORET sensor = sc->sc_ntemps > 1 ? 584114b2e18SPierre-Alain TORET (i == 0 ? CORE0 : CORE1) : CORE0_SENSOR0; 585114b2e18SPierre-Alain TORET sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx, 586114b2e18SPierre-Alain TORET SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), 587114b2e18SPierre-Alain TORET OID_AUTO, "temperature", 588114b2e18SPierre-Alain TORET CTLTYPE_INT | CTLFLAG_RD, 589114b2e18SPierre-Alain TORET dev, sensor, amdtemp_sysctl, "IK", 590114b2e18SPierre-Alain TORET "Current temparature"); 591114b2e18SPierre-Alain TORET } 592114b2e18SPierre-Alain TORET } 593114b2e18SPierre-Alain TORET if (sc->sc_ich.ich_arg != NULL) 594114b2e18SPierre-Alain TORET config_intrhook_disestablish(&sc->sc_ich); 595114b2e18SPierre-Alain TORET } 596114b2e18SPierre-Alain TORET 597114b2e18SPierre-Alain TORET int 598114b2e18SPierre-Alain TORET amdtemp_detach(device_t dev) 599114b2e18SPierre-Alain TORET { 600114b2e18SPierre-Alain TORET struct amdtemp_softc *sc = device_get_softc(dev); 601114b2e18SPierre-Alain TORET int i; 602114b2e18SPierre-Alain TORET 603114b2e18SPierre-Alain TORET for (i = 0; i < sc->sc_ncores; i++) 604114b2e18SPierre-Alain TORET if (sc->sc_sysctl_cpu[i] != NULL) 605114b2e18SPierre-Alain TORET sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0); 606114b2e18SPierre-Alain TORET 607114b2e18SPierre-Alain TORET /* NewBus removes the dev.amdtemp.N tree by itself. */ 608114b2e18SPierre-Alain TORET 609114b2e18SPierre-Alain TORET return (0); 610114b2e18SPierre-Alain TORET } 611114b2e18SPierre-Alain TORET 612114b2e18SPierre-Alain TORET static int 613114b2e18SPierre-Alain TORET amdtemp_sysctl(SYSCTL_HANDLER_ARGS) 614114b2e18SPierre-Alain TORET { 615114b2e18SPierre-Alain TORET device_t dev = (device_t)arg1; 616114b2e18SPierre-Alain TORET struct amdtemp_softc *sc = device_get_softc(dev); 617114b2e18SPierre-Alain TORET amdsensor_t sensor = (amdsensor_t)arg2; 618114b2e18SPierre-Alain TORET int32_t auxtemp[2], temp; 619114b2e18SPierre-Alain TORET int error; 620114b2e18SPierre-Alain TORET 621114b2e18SPierre-Alain TORET switch (sensor) { 622114b2e18SPierre-Alain TORET case CORE0: 623114b2e18SPierre-Alain TORET auxtemp[0] = sc->sc_gettemp(dev, CORE0_SENSOR0); 624114b2e18SPierre-Alain TORET auxtemp[1] = sc->sc_gettemp(dev, CORE0_SENSOR1); 625114b2e18SPierre-Alain TORET temp = imax(auxtemp[0], auxtemp[1]); 626114b2e18SPierre-Alain TORET break; 627114b2e18SPierre-Alain TORET case CORE1: 628114b2e18SPierre-Alain TORET auxtemp[0] = sc->sc_gettemp(dev, CORE1_SENSOR0); 629114b2e18SPierre-Alain TORET auxtemp[1] = sc->sc_gettemp(dev, CORE1_SENSOR1); 630114b2e18SPierre-Alain TORET temp = imax(auxtemp[0], auxtemp[1]); 631114b2e18SPierre-Alain TORET break; 632114b2e18SPierre-Alain TORET default: 633114b2e18SPierre-Alain TORET temp = sc->sc_gettemp(dev, sensor); 634114b2e18SPierre-Alain TORET break; 635114b2e18SPierre-Alain TORET } 636114b2e18SPierre-Alain TORET error = sysctl_handle_int(oidp, &temp, 0, req); 637114b2e18SPierre-Alain TORET 638114b2e18SPierre-Alain TORET return (error); 639114b2e18SPierre-Alain TORET } 640114b2e18SPierre-Alain TORET 641114b2e18SPierre-Alain TORET #define AMDTEMP_ZERO_C_TO_K 2731 642114b2e18SPierre-Alain TORET 643114b2e18SPierre-Alain TORET static int32_t 644114b2e18SPierre-Alain TORET amdtemp_gettemp0f(device_t dev, amdsensor_t sensor) 645114b2e18SPierre-Alain TORET { 646114b2e18SPierre-Alain TORET struct amdtemp_softc *sc = device_get_softc(dev); 647114b2e18SPierre-Alain TORET uint32_t mask, offset, temp; 648114b2e18SPierre-Alain TORET 649114b2e18SPierre-Alain TORET /* Set Sensor/Core selector. */ 650114b2e18SPierre-Alain TORET temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1); 651114b2e18SPierre-Alain TORET temp &= ~(AMDTEMP_TTSR_SELCORE | AMDTEMP_TTSR_SELSENSOR); 652114b2e18SPierre-Alain TORET switch (sensor) { 653114b2e18SPierre-Alain TORET case CORE0_SENSOR1: 654114b2e18SPierre-Alain TORET temp |= AMDTEMP_TTSR_SELSENSOR; 655114b2e18SPierre-Alain TORET /* FALLTHROUGH */ 656114b2e18SPierre-Alain TORET case CORE0_SENSOR0: 657114b2e18SPierre-Alain TORET case CORE0: 658114b2e18SPierre-Alain TORET if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0) 659114b2e18SPierre-Alain TORET temp |= AMDTEMP_TTSR_SELCORE; 660114b2e18SPierre-Alain TORET break; 661114b2e18SPierre-Alain TORET case CORE1_SENSOR1: 662114b2e18SPierre-Alain TORET temp |= AMDTEMP_TTSR_SELSENSOR; 663114b2e18SPierre-Alain TORET /* FALLTHROUGH */ 664114b2e18SPierre-Alain TORET case CORE1_SENSOR0: 665114b2e18SPierre-Alain TORET case CORE1: 666114b2e18SPierre-Alain TORET if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0) 667114b2e18SPierre-Alain TORET temp |= AMDTEMP_TTSR_SELCORE; 668114b2e18SPierre-Alain TORET break; 669114b2e18SPierre-Alain TORET default: 670*23be8282SSascha Wildner __assert_unreachable(); 671114b2e18SPierre-Alain TORET } 672114b2e18SPierre-Alain TORET pci_write_config(dev, AMDTEMP_THERMTP_STAT, temp, 1); 673114b2e18SPierre-Alain TORET 674114b2e18SPierre-Alain TORET mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc; 675114b2e18SPierre-Alain TORET offset = (sc->sc_flags & AMDTEMP_FLAG_ALT_OFFSET) != 0 ? 28 : 49; 676114b2e18SPierre-Alain TORET temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4); 677114b2e18SPierre-Alain TORET temp = ((temp >> 14) & mask) * 5 / 2; 678114b2e18SPierre-Alain TORET temp += AMDTEMP_ZERO_C_TO_K + (sc->sc_offset - offset) * 10; 679114b2e18SPierre-Alain TORET 680114b2e18SPierre-Alain TORET return (temp); 681114b2e18SPierre-Alain TORET } 682114b2e18SPierre-Alain TORET 683114b2e18SPierre-Alain TORET static uint32_t 684114b2e18SPierre-Alain TORET amdtemp_decode_fam10h_to_17h(int32_t sc_offset, uint32_t val, bool minus49) 685114b2e18SPierre-Alain TORET { 686114b2e18SPierre-Alain TORET uint32_t temp; 687114b2e18SPierre-Alain TORET 688114b2e18SPierre-Alain TORET /* Convert raw register subfield units (0.125C) to units of 0.1C. */ 689114b2e18SPierre-Alain TORET temp = (val & AMDTEMP_REPTMP10H_CURTMP_MASK) * 5 / 4; 690114b2e18SPierre-Alain TORET 691114b2e18SPierre-Alain TORET if (minus49) 692114b2e18SPierre-Alain TORET temp -= AMDTEMP_CURTMP_RANGE_ADJUST; 693114b2e18SPierre-Alain TORET 694114b2e18SPierre-Alain TORET temp += AMDTEMP_ZERO_C_TO_K + sc_offset * 10; 695114b2e18SPierre-Alain TORET return (temp); 696114b2e18SPierre-Alain TORET } 697114b2e18SPierre-Alain TORET 698114b2e18SPierre-Alain TORET static uint32_t 699114b2e18SPierre-Alain TORET amdtemp_decode_fam10h_to_16h(int32_t sc_offset, uint32_t val) 700114b2e18SPierre-Alain TORET { 701114b2e18SPierre-Alain TORET bool minus49; 702114b2e18SPierre-Alain TORET 703114b2e18SPierre-Alain TORET /* 704114b2e18SPierre-Alain TORET * On Family 15h and higher, if CurTmpTjSel is 11b, the range is 705114b2e18SPierre-Alain TORET * adjusted down by 49.0 degrees Celsius. (This adjustment is not 706114b2e18SPierre-Alain TORET * documented in BKDGs prior to family 15h model 00h.) 707114b2e18SPierre-Alain TORET */ 708114b2e18SPierre-Alain TORET minus49 = (CPUID_TO_FAMILY(cpu_id) >= 0x15 && 709114b2e18SPierre-Alain TORET ((val >> AMDTEMP_REPTMP10H_TJSEL_SHIFT) & 710114b2e18SPierre-Alain TORET AMDTEMP_REPTMP10H_TJSEL_MASK) == 0x3); 711114b2e18SPierre-Alain TORET 712114b2e18SPierre-Alain TORET return (amdtemp_decode_fam10h_to_17h(sc_offset, 713114b2e18SPierre-Alain TORET val >> AMDTEMP_REPTMP10H_CURTMP_SHIFT, minus49)); 714114b2e18SPierre-Alain TORET } 715114b2e18SPierre-Alain TORET 716114b2e18SPierre-Alain TORET static uint32_t 717114b2e18SPierre-Alain TORET amdtemp_decode_fam17h_tctl(int32_t sc_offset, uint32_t val) 718114b2e18SPierre-Alain TORET { 719114b2e18SPierre-Alain TORET bool minus49; 720114b2e18SPierre-Alain TORET 721114b2e18SPierre-Alain TORET minus49 = ((val & AMDTEMP_17H_CUR_TMP_RANGE_SEL) != 0); 722114b2e18SPierre-Alain TORET return (amdtemp_decode_fam10h_to_17h(sc_offset, 723114b2e18SPierre-Alain TORET val >> AMDTEMP_REPTMP10H_CURTMP_SHIFT, minus49)); 724114b2e18SPierre-Alain TORET } 725114b2e18SPierre-Alain TORET 726114b2e18SPierre-Alain TORET static int32_t 727114b2e18SPierre-Alain TORET amdtemp_gettemp(device_t dev, amdsensor_t sensor) 728114b2e18SPierre-Alain TORET { 729114b2e18SPierre-Alain TORET struct amdtemp_softc *sc = device_get_softc(dev); 730114b2e18SPierre-Alain TORET uint32_t temp; 731114b2e18SPierre-Alain TORET 732114b2e18SPierre-Alain TORET temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4); 733114b2e18SPierre-Alain TORET return (amdtemp_decode_fam10h_to_16h(sc->sc_offset, temp)); 734114b2e18SPierre-Alain TORET } 735114b2e18SPierre-Alain TORET 736114b2e18SPierre-Alain TORET static int32_t 737114b2e18SPierre-Alain TORET amdtemp_gettemp15hm60h(device_t dev, amdsensor_t sensor) 738114b2e18SPierre-Alain TORET { 739114b2e18SPierre-Alain TORET struct amdtemp_softc *sc = device_get_softc(dev); 740114b2e18SPierre-Alain TORET uint32_t val; 741114b2e18SPierre-Alain TORET int error; 742114b2e18SPierre-Alain TORET 743114b2e18SPierre-Alain TORET error = amdsmn_read(sc->sc_smn, AMDTEMP_15H_M60H_REPTMP_CTRL, &val); 744114b2e18SPierre-Alain TORET KASSERT(error == 0, ("amdsmn_read")); 745114b2e18SPierre-Alain TORET return (amdtemp_decode_fam10h_to_16h(sc->sc_offset, val)); 746114b2e18SPierre-Alain TORET } 747114b2e18SPierre-Alain TORET 748114b2e18SPierre-Alain TORET static int32_t 749114b2e18SPierre-Alain TORET amdtemp_gettemp17h(device_t dev, amdsensor_t sensor) 750114b2e18SPierre-Alain TORET { 751114b2e18SPierre-Alain TORET struct amdtemp_softc *sc = device_get_softc(dev); 752114b2e18SPierre-Alain TORET uint32_t val; 753114b2e18SPierre-Alain TORET int error; 754114b2e18SPierre-Alain TORET 755114b2e18SPierre-Alain TORET switch (sensor) { 756114b2e18SPierre-Alain TORET case CORE0_SENSOR0: 757114b2e18SPierre-Alain TORET /* Tctl */ 758114b2e18SPierre-Alain TORET error = amdsmn_read(sc->sc_smn, AMDTEMP_17H_CUR_TMP, &val); 759114b2e18SPierre-Alain TORET KASSERT(error == 0, ("amdsmn_read")); 760114b2e18SPierre-Alain TORET return (amdtemp_decode_fam17h_tctl(sc->sc_offset, val)); 761114b2e18SPierre-Alain TORET case CCD_BASE ... CCD_MAX: 762114b2e18SPierre-Alain TORET /* Tccd<N> */ 763114b2e18SPierre-Alain TORET error = amdsmn_read(sc->sc_smn, AMDTEMP_17H_CCD_TMP_BASE + 764114b2e18SPierre-Alain TORET (((int)sensor - CCD_BASE) * sizeof(val)), &val); 765114b2e18SPierre-Alain TORET KASSERT(error == 0, ("amdsmn_read2")); 766114b2e18SPierre-Alain TORET KASSERT((val & AMDTEMP_17H_CCD_TMP_VALID) != 0, 767114b2e18SPierre-Alain TORET ("sensor %d: not valid", (int)sensor)); 768114b2e18SPierre-Alain TORET return (amdtemp_decode_fam10h_to_17h(sc->sc_offset, val, true)); 769114b2e18SPierre-Alain TORET default: 770*23be8282SSascha Wildner __assert_unreachable(); 771114b2e18SPierre-Alain TORET } 772114b2e18SPierre-Alain TORET } 773114b2e18SPierre-Alain TORET 774114b2e18SPierre-Alain TORET static void 775114b2e18SPierre-Alain TORET amdtemp_probe_ccd_sensors17h(device_t dev, uint32_t model) 776114b2e18SPierre-Alain TORET { 777114b2e18SPierre-Alain TORET char sensor_name[16], sensor_descr[32]; 778114b2e18SPierre-Alain TORET struct amdtemp_softc *sc; 779114b2e18SPierre-Alain TORET uint32_t maxreg, i, val; 780114b2e18SPierre-Alain TORET int error; 781114b2e18SPierre-Alain TORET 782114b2e18SPierre-Alain TORET switch (model) { 783114b2e18SPierre-Alain TORET case 0x00 ... 0x1f: /* Zen1, Zen+ */ 784114b2e18SPierre-Alain TORET maxreg = 4; 785114b2e18SPierre-Alain TORET break; 786114b2e18SPierre-Alain TORET case 0x30 ... 0x3f: /* Zen2 TR/Epyc */ 787114b2e18SPierre-Alain TORET case 0x70 ... 0x7f: /* Zen2 Ryzen */ 788114b2e18SPierre-Alain TORET maxreg = 8; 789114b2e18SPierre-Alain TORET _Static_assert((int)NUM_CCDS >= 8, ""); 790114b2e18SPierre-Alain TORET break; 791114b2e18SPierre-Alain TORET default: 792114b2e18SPierre-Alain TORET device_printf(dev, 793114b2e18SPierre-Alain TORET "Unrecognized Family 17h Model: %02xh\n", model); 794114b2e18SPierre-Alain TORET return; 795114b2e18SPierre-Alain TORET } 796114b2e18SPierre-Alain TORET 797114b2e18SPierre-Alain TORET sc = device_get_softc(dev); 798114b2e18SPierre-Alain TORET for (i = 0; i < maxreg; i++) { 799114b2e18SPierre-Alain TORET error = amdsmn_read(sc->sc_smn, AMDTEMP_17H_CCD_TMP_BASE + 800114b2e18SPierre-Alain TORET (i * sizeof(val)), &val); 801114b2e18SPierre-Alain TORET if (error != 0) 802114b2e18SPierre-Alain TORET continue; 803114b2e18SPierre-Alain TORET if ((val & AMDTEMP_17H_CCD_TMP_VALID) == 0) 804114b2e18SPierre-Alain TORET continue; 805114b2e18SPierre-Alain TORET 806114b2e18SPierre-Alain TORET ksnprintf(sensor_name, sizeof(sensor_name), "ccd%u", i); 807114b2e18SPierre-Alain TORET ksnprintf(sensor_descr, sizeof(sensor_descr), 808114b2e18SPierre-Alain TORET "CCD %u temperature (Tccd%u)", i, i); 809114b2e18SPierre-Alain TORET 810114b2e18SPierre-Alain TORET SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 811114b2e18SPierre-Alain TORET SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 812114b2e18SPierre-Alain TORET sensor_name, CTLTYPE_INT | CTLFLAG_RD, 813114b2e18SPierre-Alain TORET dev, CCD_BASE + i, amdtemp_sysctl, "IK", sensor_descr); 814114b2e18SPierre-Alain TORET } 815114b2e18SPierre-Alain TORET } 816