1 /* $NetBSD: amdzentemp.c,v 1.9 2019/06/16 09:12:51 mlelstv Exp $ */ 2 /* $OpenBSD: kate.c,v 1.2 2008/03/27 04:52:03 cnst Exp $ */ 3 4 /* 5 * Copyright (c) 2008 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Christoph Egger. 10 * 11 * NetBSD port by Ian Clark <mrrooster@gmail.com> 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 * POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 /* 36 * Copyright (c) 2008 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru> 37 * 38 * Permission to use, copy, modify, and distribute this software for any 39 * purpose with or without fee is hereby granted, provided that the above 40 * copyright notice and this permission notice appear in all copies. 41 * 42 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 43 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 44 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 45 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 46 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 47 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 48 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 49 */ 50 51 52 #include <sys/cdefs.h> 53 __KERNEL_RCSID(0, "$NetBSD: amdzentemp.c,v 1.9 2019/06/16 09:12:51 mlelstv Exp $ "); 54 55 #include <sys/param.h> 56 #include <sys/bus.h> 57 #include <sys/cpu.h> 58 #include <sys/systm.h> 59 #include <sys/device.h> 60 #include <sys/kmem.h> 61 #include <sys/module.h> 62 63 #include <machine/specialreg.h> 64 65 #include <dev/pci/pcireg.h> 66 #include <dev/pci/pcivar.h> 67 #include <dev/pci/pcidevs.h> 68 69 #include <dev/sysmon/sysmonvar.h> 70 71 #include "amdsmn.h" 72 73 /* Address to query for temp on family 17h */ 74 #define AMD_17H_CUR_TMP 0x59800 75 76 struct amdzentemp_softc { 77 pci_chipset_tag_t sc_pc; 78 pcitag_t sc_pcitag; 79 struct sysmon_envsys *sc_sme; 80 device_t sc_smn; 81 envsys_data_t *sc_sensor; 82 size_t sc_sensor_len; 83 size_t sc_numsensors; 84 int32_t sc_offset; 85 }; 86 87 88 static int amdzentemp_match(device_t, cfdata_t, void *); 89 static void amdzentemp_attach(device_t, device_t, void *); 90 static int amdzentemp_detach(device_t, int); 91 92 static void amdzentemp_family17_init(struct amdzentemp_softc *); 93 static void amdzentemp_family17_setup_sensors(struct amdzentemp_softc *, int); 94 static void amdzentemp_family17_refresh(struct sysmon_envsys *, envsys_data_t *); 95 96 CFATTACH_DECL_NEW(amdzentemp, sizeof(struct amdzentemp_softc), 97 amdzentemp_match, amdzentemp_attach, amdzentemp_detach, NULL); 98 99 static int 100 amdzentemp_match(device_t parent, cfdata_t match, void *aux) 101 { 102 struct pci_attach_args *pa __diagused = aux; 103 104 KASSERT(PCI_VENDOR(pa->pa_id) == PCI_VENDOR_AMD); 105 106 cfdata_t parent_cfdata = device_cfdata(parent); 107 108 /* Got AMD family 17h system management network */ 109 return parent_cfdata->cf_name && 110 memcmp(parent_cfdata->cf_name, "amdsmn", 6) == 0; 111 } 112 113 static void 114 amdzentemp_attach(device_t parent, device_t self, void *aux) 115 { 116 struct amdzentemp_softc *sc = device_private(self); 117 struct pci_attach_args *pa = aux; 118 int error; 119 size_t i; 120 121 aprint_naive("\n"); 122 aprint_normal(": AMD CPU Temperature Sensors (Family17h)"); 123 124 sc->sc_pc = pa->pa_pc; 125 sc->sc_pcitag = pa->pa_tag; 126 sc->sc_smn = parent; 127 128 amdzentemp_family17_init(sc); 129 130 aprint_normal("\n"); 131 132 sc->sc_sme = sysmon_envsys_create(); 133 sc->sc_sensor_len = sizeof(envsys_data_t) * sc->sc_numsensors; 134 sc->sc_sensor = kmem_zalloc(sc->sc_sensor_len, KM_SLEEP); 135 136 amdzentemp_family17_setup_sensors(sc, device_unit(self)); 137 138 /* 139 * Set properties in sensors. 140 */ 141 for (i = 0; i < sc->sc_numsensors; i++) { 142 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor[i])) 143 goto bad; 144 } 145 146 /* 147 * Register the sysmon_envsys device. 148 */ 149 sc->sc_sme->sme_name = device_xname(self); 150 sc->sc_sme->sme_cookie = sc; 151 152 sc->sc_sme->sme_refresh = amdzentemp_family17_refresh; 153 154 error = sysmon_envsys_register(sc->sc_sme); 155 if (error) { 156 aprint_error_dev(self, "unable to register with sysmon " 157 "(error=%d)\n", error); 158 goto bad; 159 } 160 161 (void)pmf_device_register(self, NULL, NULL); 162 163 return; 164 165 bad: 166 if (sc->sc_sme != NULL) { 167 sysmon_envsys_destroy(sc->sc_sme); 168 sc->sc_sme = NULL; 169 } 170 171 kmem_free(sc->sc_sensor, sc->sc_sensor_len); 172 sc->sc_sensor = NULL; 173 } 174 175 static int 176 amdzentemp_detach(device_t self, int flags) 177 { 178 struct amdzentemp_softc *sc = device_private(self); 179 180 pmf_device_deregister(self); 181 if (sc->sc_sme != NULL) 182 sysmon_envsys_unregister(sc->sc_sme); 183 184 if (sc->sc_sensor != NULL) 185 kmem_free(sc->sc_sensor, sc->sc_sensor_len); 186 187 return 0; 188 } 189 190 191 static void 192 amdzentemp_family17_init(struct amdzentemp_softc *sc) 193 { 194 195 sc->sc_numsensors = 1; 196 sc->sc_offset = 0; 197 198 if (strstr(cpu_brand_string, "AMD Ryzen 5 1600X") 199 || strstr(cpu_brand_string, "AMD Ryzen 7 1700X") 200 || strstr(cpu_brand_string, "AMD Ryzen 7 1800X")) 201 sc->sc_offset = -20000000; 202 else if (strstr(cpu_brand_string, "AMD Ryzen 7 2700X")) 203 sc->sc_offset = -10000000; 204 else if (strstr(cpu_brand_string, "AMD Ryzen Threadripper 19") 205 || strstr(cpu_brand_string, "AMD Ryzen Threadripper 29")) 206 sc->sc_offset = -27000000; 207 } 208 209 static void 210 amdzentemp_family17_setup_sensors(struct amdzentemp_softc *sc, int dv_unit) 211 { 212 sc->sc_sensor[0].units = ENVSYS_STEMP; 213 sc->sc_sensor[0].state = ENVSYS_SVALID; 214 sc->sc_sensor[0].flags = ENVSYS_FHAS_ENTROPY; 215 216 snprintf(sc->sc_sensor[0].desc, sizeof(sc->sc_sensor[0].desc), 217 "cpu%u temperature", dv_unit); 218 } 219 220 static void 221 amdzentemp_family17_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 222 { 223 struct amdzentemp_softc *sc = sme->sme_cookie; 224 uint32_t temp; 225 int error; 226 227 error = amdsmn_read(sc->sc_smn, AMD_17H_CUR_TMP, &temp); 228 if (error) { 229 edata->state = ENVSYS_SINVALID; 230 return; 231 } 232 edata->state = ENVSYS_SVALID; 233 /* From C to uK. */ 234 edata->value_cur = ((temp >> 21) * 125000) + 273150000; 235 /* adjust for possible offset of 49K */ 236 if (temp & 0x80000) 237 edata->value_cur -= 49000000; 238 edata->value_cur += sc->sc_offset; 239 } 240 241 MODULE(MODULE_CLASS_DRIVER, amdzentemp, "sysmon_envsys,amdsmn"); 242 243 #ifdef _MODULE 244 #include "ioconf.c" 245 #endif 246 247 static int 248 amdzentemp_modcmd(modcmd_t cmd, void *aux) 249 { 250 int error = 0; 251 252 switch (cmd) { 253 case MODULE_CMD_INIT: 254 #ifdef _MODULE 255 error = config_init_component(cfdriver_ioconf_amdzentemp, 256 cfattach_ioconf_amdzentemp, cfdata_ioconf_amdzentemp); 257 #endif 258 return error; 259 case MODULE_CMD_FINI: 260 #ifdef _MODULE 261 error = config_fini_component(cfdriver_ioconf_amdzentemp, 262 cfattach_ioconf_amdzentemp, cfdata_ioconf_amdzentemp); 263 #endif 264 return error; 265 default: 266 return ENOTTY; 267 } 268 } 269 270