amdtemp.c revision 178151
1178151Srpaulo/*- 2178151Srpaulo * Copyright (c) 2008 Rui Paulo <rpaulo@FreeBSD.org> 3178151Srpaulo * All rights reserved. 4178151Srpaulo * 5178151Srpaulo * Redistribution and use in source and binary forms, with or without 6178151Srpaulo * modification, are permitted provided that the following conditions 7178151Srpaulo * are met: 8178151Srpaulo * 1. Redistributions of source code must retain the above copyright 9178151Srpaulo * notice, this list of conditions and the following disclaimer. 10178151Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 11178151Srpaulo * notice, this list of conditions and the following disclaimer in the 12178151Srpaulo * documentation and/or other materials provided with the distribution. 13178151Srpaulo * 14178151Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15178151Srpaulo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16178151Srpaulo * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17178151Srpaulo * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 18178151Srpaulo * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19178151Srpaulo * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20178151Srpaulo * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21178151Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 22178151Srpaulo * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 23178151Srpaulo * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24178151Srpaulo * POSSIBILITY OF SUCH DAMAGE. 25178151Srpaulo * 26178151Srpaulo * $P4: //depot/user/rpaulo/k8temp/k8temp.c#8 $ 27178151Srpaulo */ 28178151Srpaulo 29178151Srpaulo/* 30178151Srpaulo * Driver for the AMD K8 thermal sensors. Based on a Linux driver by the 31178151Srpaulo * same name. 32178151Srpaulo */ 33178151Srpaulo 34178151Srpaulo#include <sys/cdefs.h> 35178151Srpaulo__FBSDID("$FreeBSD: head/sys/dev/k8temp/k8temp.c 178151 2008-04-12 14:04:08Z rpaulo $"); 36178151Srpaulo 37178151Srpaulo#include <sys/param.h> 38178151Srpaulo#include <sys/bus.h> 39178151Srpaulo#include <sys/systm.h> 40178151Srpaulo#include <sys/types.h> 41178151Srpaulo#include <sys/module.h> 42178151Srpaulo#include <sys/conf.h> 43178151Srpaulo#include <sys/kernel.h> 44178151Srpaulo#include <sys/sysctl.h> 45178151Srpaulo 46178151Srpaulo#include <machine/specialreg.h> 47178151Srpaulo#include <machine/cpufunc.h> 48178151Srpaulo#include <machine/md_var.h> 49178151Srpaulo 50178151Srpaulo#include <dev/pci/pcireg.h> 51178151Srpaulo#include <dev/pci/pcivar.h> 52178151Srpaulo 53178151Srpaulostruct k8temp_softc { 54178151Srpaulo device_t sc_dev; 55178151Srpaulo int sc_temps[4]; 56178151Srpaulo int sc_ntemps; 57178151Srpaulo struct sysctl_oid *sc_oid; 58178151Srpaulo struct sysctl_oid *sc_sysctl_cpu[2]; 59178151Srpaulo}; 60178151Srpaulo 61178151Srpaulo#define VENDORID_AMD 0x1022 62178151Srpaulo#define DEVICEID_AMD_MISC 0x1103 63178151Srpaulo 64178151Srpaulostatic struct k8temp_product { 65178151Srpaulo uint16_t k8temp_vendorid; 66178151Srpaulo uint16_t k8temp_deviceid; 67178151Srpaulo} k8temp_products[] = { 68178151Srpaulo { VENDORID_AMD, DEVICEID_AMD_MISC }, 69178151Srpaulo { 0, 0 } 70178151Srpaulo}; 71178151Srpaulo 72178151Srpaulo/* 73178151Srpaulo * Register control 74178151Srpaulo */ 75178151Srpaulo#define K8TEMP_REG 0xe4 76178151Srpaulo#define K8TEMP_REG_SELSENSOR 0x40 77178151Srpaulo#define K8TEMP_REG_SELCORE 0x04 78178151Srpaulo 79178151Srpaulo#define K8TEMP_MINTEMP 49 /* -49 C is the mininum temperature */ 80178151Srpaulo 81178151Srpaulotypedef enum { 82178151Srpaulo SENSOR0_CORE0, 83178151Srpaulo SENSOR0_CORE1, 84178151Srpaulo SENSOR1_CORE0, 85178151Srpaulo SENSOR1_CORE1, 86178151Srpaulo CORE0, 87178151Srpaulo CORE1 88178151Srpaulo} k8sensor_t; 89178151Srpaulo 90178151Srpaulo/* 91178151Srpaulo * Device methods. 92178151Srpaulo */ 93178151Srpaulostatic void k8temp_identify(driver_t *driver, device_t parent); 94178151Srpaulostatic int k8temp_probe(device_t dev); 95178151Srpaulostatic int k8temp_attach(device_t dev); 96178151Srpaulostatic int k8temp_detach(device_t dev); 97178151Srpaulostatic int k8temp_match(device_t dev); 98178151Srpaulostatic int32_t k8temp_gettemp(device_t dev, k8sensor_t sensor); 99178151Srpaulostatic int k8temp_sysctl(SYSCTL_HANDLER_ARGS); 100178151Srpaulo 101178151Srpaulostatic device_method_t k8temp_methods[] = { 102178151Srpaulo /* Device interface */ 103178151Srpaulo DEVMETHOD(device_identify, k8temp_identify), 104178151Srpaulo DEVMETHOD(device_probe, k8temp_probe), 105178151Srpaulo DEVMETHOD(device_attach, k8temp_attach), 106178151Srpaulo DEVMETHOD(device_detach, k8temp_detach), 107178151Srpaulo 108178151Srpaulo {0, 0} 109178151Srpaulo}; 110178151Srpaulo 111178151Srpaulostatic driver_t k8temp_driver = { 112178151Srpaulo "k8temp", 113178151Srpaulo k8temp_methods, 114178151Srpaulo sizeof(struct k8temp_softc), 115178151Srpaulo}; 116178151Srpaulo 117178151Srpaulostatic devclass_t k8temp_devclass; 118178151SrpauloDRIVER_MODULE(k8temp, hostb, k8temp_driver, k8temp_devclass, NULL, NULL); 119178151Srpaulo 120178151Srpaulostatic int 121178151Srpaulok8temp_match(device_t dev) 122178151Srpaulo{ 123178151Srpaulo int i; 124178151Srpaulo uint16_t vendor, devid; 125178151Srpaulo 126178151Srpaulo vendor = pci_get_vendor(dev); 127178151Srpaulo devid = pci_get_device(dev); 128178151Srpaulo 129178151Srpaulo for (i = 0; k8temp_products[i].k8temp_vendorid != 0; i++) { 130178151Srpaulo if (vendor == k8temp_products[i].k8temp_vendorid && 131178151Srpaulo devid == k8temp_products[i].k8temp_deviceid) 132178151Srpaulo return (1); 133178151Srpaulo } 134178151Srpaulo 135178151Srpaulo return (0); 136178151Srpaulo} 137178151Srpaulo 138178151Srpaulostatic void 139178151Srpaulok8temp_identify(driver_t *driver, device_t parent) 140178151Srpaulo{ 141178151Srpaulo device_t child; 142178151Srpaulo 143178151Srpaulo /* Make sure we're not being doubly invoked. */ 144178151Srpaulo if (device_find_child(parent, "k8temp", -1) != NULL) 145178151Srpaulo return; 146178151Srpaulo 147178151Srpaulo if (k8temp_match(parent)) { 148178151Srpaulo child = device_add_child(parent, "k8temp", -1); 149178151Srpaulo if (child == NULL) 150178151Srpaulo device_printf(parent, "add k8temp child failed\n"); 151178151Srpaulo } 152178151Srpaulo 153178151Srpaulo} 154178151Srpaulo 155178151Srpaulostatic int 156178151Srpaulok8temp_probe(device_t dev) 157178151Srpaulo{ 158178151Srpaulo uint32_t regs[4]; 159178151Srpaulo 160178151Srpaulo if (resource_disabled("k8temp", 0)) 161178151Srpaulo return (ENXIO); 162178151Srpaulo 163178151Srpaulo do_cpuid(1, regs); 164178151Srpaulo switch (regs[0]) { 165178151Srpaulo case 0xf40: 166178151Srpaulo case 0xf50: 167178151Srpaulo case 0xf51: 168178151Srpaulo return (ENXIO); 169178151Srpaulo } 170178151Srpaulo device_set_desc(dev, "AMD K8 Thermal Sensors"); 171178151Srpaulo 172178151Srpaulo return (BUS_PROBE_GENERIC); 173178151Srpaulo} 174178151Srpaulo 175178151Srpaulostatic int 176178151Srpaulok8temp_attach(device_t dev) 177178151Srpaulo{ 178178151Srpaulo device_t nexus, acpi, cpu; 179178151Srpaulo struct k8temp_softc *sc = device_get_softc(dev); 180178151Srpaulo int i; 181178151Srpaulo struct sysctl_ctx_list *sysctlctx; 182178151Srpaulo struct sysctl_oid *sysctlnode; 183178151Srpaulo 184178151Srpaulo /* 185178151Srpaulo * dev.cpu.N.temperature. 186178151Srpaulo */ 187178151Srpaulo nexus = device_find_child(root_bus, "nexus", 0); 188178151Srpaulo acpi = device_find_child(nexus, "acpi", 0); 189178151Srpaulo 190178151Srpaulo for (i = 0; i < 2; i++) { 191178151Srpaulo cpu = device_find_child(acpi, "cpu", 192178151Srpaulo device_get_unit(dev) * 2 + i); 193178151Srpaulo if (cpu) { 194178151Srpaulo sysctlctx = device_get_sysctl_ctx(cpu); 195178151Srpaulo 196178151Srpaulo sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx, 197178151Srpaulo SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), 198178151Srpaulo OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD, 199178151Srpaulo dev, CORE0, k8temp_sysctl, "I", 200178151Srpaulo "Max of sensor 0 / 1"); 201178151Srpaulo } 202178151Srpaulo } 203178151Srpaulo 204178151Srpaulo /* 205178151Srpaulo * dev.k8temp.N tree. 206178151Srpaulo */ 207178151Srpaulo sysctlctx = device_get_sysctl_ctx(dev); 208178151Srpaulo sysctlnode = SYSCTL_ADD_NODE(sysctlctx, 209178151Srpaulo SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor0", 210178151Srpaulo CTLFLAG_RD, 0, "Sensor 0"); 211178151Srpaulo 212178151Srpaulo SYSCTL_ADD_PROC(sysctlctx, 213178151Srpaulo SYSCTL_CHILDREN(sysctlnode), 214178151Srpaulo OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD, 215178151Srpaulo dev, SENSOR0_CORE0, k8temp_sysctl, "I", 216178151Srpaulo "Sensor 0 / Core 0 temperature"); 217178151Srpaulo 218178151Srpaulo SYSCTL_ADD_PROC(sysctlctx, 219178151Srpaulo SYSCTL_CHILDREN(sysctlnode), 220178151Srpaulo OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD, 221178151Srpaulo dev, SENSOR0_CORE1, k8temp_sysctl, "I", 222178151Srpaulo "Sensor 0 / Core 1 temperature"); 223178151Srpaulo 224178151Srpaulo sysctlnode = SYSCTL_ADD_NODE(sysctlctx, 225178151Srpaulo SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor1", 226178151Srpaulo CTLFLAG_RD, 0, "Sensor 1"); 227178151Srpaulo 228178151Srpaulo SYSCTL_ADD_PROC(sysctlctx, 229178151Srpaulo SYSCTL_CHILDREN(sysctlnode), 230178151Srpaulo OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD, 231178151Srpaulo dev, SENSOR0_CORE0, k8temp_sysctl, "I", 232178151Srpaulo "Sensor 1 / Core 0 temperature"); 233178151Srpaulo 234178151Srpaulo SYSCTL_ADD_PROC(sysctlctx, 235178151Srpaulo SYSCTL_CHILDREN(sysctlnode), 236178151Srpaulo OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD, 237178151Srpaulo dev, SENSOR0_CORE0, k8temp_sysctl, "I", 238178151Srpaulo "Sensor 1 / Core 1 temperature"); 239178151Srpaulo 240178151Srpaulo return (0); 241178151Srpaulo} 242178151Srpaulo 243178151Srpauloint 244178151Srpaulok8temp_detach(device_t dev) 245178151Srpaulo{ 246178151Srpaulo int i; 247178151Srpaulo struct k8temp_softc *sc = device_get_softc(dev); 248178151Srpaulo 249178151Srpaulo for (i = 0; i < 2; i++) { 250178151Srpaulo if (sc->sc_sysctl_cpu[i]) 251178151Srpaulo sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0); 252178151Srpaulo } 253178151Srpaulo 254178151Srpaulo /* NewBus removes the dev.k8temp.N tree by itself. */ 255178151Srpaulo 256178151Srpaulo return (0); 257178151Srpaulo} 258178151Srpaulo 259178151Srpaulostatic int 260178151Srpaulok8temp_sysctl(SYSCTL_HANDLER_ARGS) 261178151Srpaulo{ 262178151Srpaulo device_t dev = (device_t) arg1; 263178151Srpaulo int error; 264178151Srpaulo int32_t temp, auxtemp[2]; 265178151Srpaulo 266178151Srpaulo switch (arg2) { 267178151Srpaulo case CORE0: 268178151Srpaulo auxtemp[0] = k8temp_gettemp(dev, SENSOR0_CORE0); 269178151Srpaulo auxtemp[1] = k8temp_gettemp(dev, SENSOR1_CORE0); 270178151Srpaulo temp = max(auxtemp[0], auxtemp[1]); 271178151Srpaulo break; 272178151Srpaulo case CORE1: 273178151Srpaulo auxtemp[0] = k8temp_gettemp(dev, SENSOR0_CORE1); 274178151Srpaulo auxtemp[1] = k8temp_gettemp(dev, SENSOR1_CORE1); 275178151Srpaulo temp = max(auxtemp[0], auxtemp[1]); 276178151Srpaulo break; 277178151Srpaulo default: 278178151Srpaulo temp = k8temp_gettemp(dev, arg2); 279178151Srpaulo break; 280178151Srpaulo } 281178151Srpaulo error = sysctl_handle_int(oidp, &temp, 0, req); 282178151Srpaulo 283178151Srpaulo return (error); 284178151Srpaulo} 285178151Srpaulo 286178151Srpaulostatic int32_t 287178151Srpaulok8temp_gettemp(device_t dev, k8sensor_t sensor) 288178151Srpaulo{ 289178151Srpaulo uint8_t cfg; 290178151Srpaulo uint32_t temp; 291178151Srpaulo 292178151Srpaulo cfg = pci_read_config(dev, K8TEMP_REG, 1); 293178151Srpaulo switch (sensor) { 294178151Srpaulo case SENSOR0_CORE0: 295178151Srpaulo cfg &= ~(K8TEMP_REG_SELSENSOR | K8TEMP_REG_SELCORE); 296178151Srpaulo break; 297178151Srpaulo case SENSOR0_CORE1: 298178151Srpaulo cfg &= ~K8TEMP_REG_SELSENSOR; 299178151Srpaulo cfg |= K8TEMP_REG_SELCORE; 300178151Srpaulo break; 301178151Srpaulo case SENSOR1_CORE0: 302178151Srpaulo cfg &= ~K8TEMP_REG_SELCORE; 303178151Srpaulo cfg |= K8TEMP_REG_SELSENSOR; 304178151Srpaulo break; 305178151Srpaulo case SENSOR1_CORE1: 306178151Srpaulo cfg |= (K8TEMP_REG_SELSENSOR | K8TEMP_REG_SELCORE); 307178151Srpaulo break; 308178151Srpaulo default: 309178151Srpaulo cfg = 0; 310178151Srpaulo break; 311178151Srpaulo } 312178151Srpaulo pci_write_config(dev, K8TEMP_REG, cfg, 1); 313178151Srpaulo temp = pci_read_config(dev, K8TEMP_REG, 4); 314178151Srpaulo temp = ((temp >> 16) & 0xff) - K8TEMP_MINTEMP; 315178151Srpaulo 316178151Srpaulo return (temp); 317178151Srpaulo} 318