amdtemp.c revision 178151
1/*- 2 * Copyright (c) 2008 Rui Paulo <rpaulo@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 22 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 23 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 * POSSIBILITY OF SUCH DAMAGE. 25 * 26 * $P4: //depot/user/rpaulo/k8temp/k8temp.c#8 $ 27 */ 28 29/* 30 * Driver for the AMD K8 thermal sensors. Based on a Linux driver by the 31 * same name. 32 */ 33 34#include <sys/cdefs.h> 35__FBSDID("$FreeBSD: head/sys/dev/k8temp/k8temp.c 178151 2008-04-12 14:04:08Z rpaulo $"); 36 37#include <sys/param.h> 38#include <sys/bus.h> 39#include <sys/systm.h> 40#include <sys/types.h> 41#include <sys/module.h> 42#include <sys/conf.h> 43#include <sys/kernel.h> 44#include <sys/sysctl.h> 45 46#include <machine/specialreg.h> 47#include <machine/cpufunc.h> 48#include <machine/md_var.h> 49 50#include <dev/pci/pcireg.h> 51#include <dev/pci/pcivar.h> 52 53struct k8temp_softc { 54 device_t sc_dev; 55 int sc_temps[4]; 56 int sc_ntemps; 57 struct sysctl_oid *sc_oid; 58 struct sysctl_oid *sc_sysctl_cpu[2]; 59}; 60 61#define VENDORID_AMD 0x1022 62#define DEVICEID_AMD_MISC 0x1103 63 64static struct k8temp_product { 65 uint16_t k8temp_vendorid; 66 uint16_t k8temp_deviceid; 67} k8temp_products[] = { 68 { VENDORID_AMD, DEVICEID_AMD_MISC }, 69 { 0, 0 } 70}; 71 72/* 73 * Register control 74 */ 75#define K8TEMP_REG 0xe4 76#define K8TEMP_REG_SELSENSOR 0x40 77#define K8TEMP_REG_SELCORE 0x04 78 79#define K8TEMP_MINTEMP 49 /* -49 C is the mininum temperature */ 80 81typedef enum { 82 SENSOR0_CORE0, 83 SENSOR0_CORE1, 84 SENSOR1_CORE0, 85 SENSOR1_CORE1, 86 CORE0, 87 CORE1 88} k8sensor_t; 89 90/* 91 * Device methods. 92 */ 93static void k8temp_identify(driver_t *driver, device_t parent); 94static int k8temp_probe(device_t dev); 95static int k8temp_attach(device_t dev); 96static int k8temp_detach(device_t dev); 97static int k8temp_match(device_t dev); 98static int32_t k8temp_gettemp(device_t dev, k8sensor_t sensor); 99static int k8temp_sysctl(SYSCTL_HANDLER_ARGS); 100 101static device_method_t k8temp_methods[] = { 102 /* Device interface */ 103 DEVMETHOD(device_identify, k8temp_identify), 104 DEVMETHOD(device_probe, k8temp_probe), 105 DEVMETHOD(device_attach, k8temp_attach), 106 DEVMETHOD(device_detach, k8temp_detach), 107 108 {0, 0} 109}; 110 111static driver_t k8temp_driver = { 112 "k8temp", 113 k8temp_methods, 114 sizeof(struct k8temp_softc), 115}; 116 117static devclass_t k8temp_devclass; 118DRIVER_MODULE(k8temp, hostb, k8temp_driver, k8temp_devclass, NULL, NULL); 119 120static int 121k8temp_match(device_t dev) 122{ 123 int i; 124 uint16_t vendor, devid; 125 126 vendor = pci_get_vendor(dev); 127 devid = pci_get_device(dev); 128 129 for (i = 0; k8temp_products[i].k8temp_vendorid != 0; i++) { 130 if (vendor == k8temp_products[i].k8temp_vendorid && 131 devid == k8temp_products[i].k8temp_deviceid) 132 return (1); 133 } 134 135 return (0); 136} 137 138static void 139k8temp_identify(driver_t *driver, device_t parent) 140{ 141 device_t child; 142 143 /* Make sure we're not being doubly invoked. */ 144 if (device_find_child(parent, "k8temp", -1) != NULL) 145 return; 146 147 if (k8temp_match(parent)) { 148 child = device_add_child(parent, "k8temp", -1); 149 if (child == NULL) 150 device_printf(parent, "add k8temp child failed\n"); 151 } 152 153} 154 155static int 156k8temp_probe(device_t dev) 157{ 158 uint32_t regs[4]; 159 160 if (resource_disabled("k8temp", 0)) 161 return (ENXIO); 162 163 do_cpuid(1, regs); 164 switch (regs[0]) { 165 case 0xf40: 166 case 0xf50: 167 case 0xf51: 168 return (ENXIO); 169 } 170 device_set_desc(dev, "AMD K8 Thermal Sensors"); 171 172 return (BUS_PROBE_GENERIC); 173} 174 175static int 176k8temp_attach(device_t dev) 177{ 178 device_t nexus, acpi, cpu; 179 struct k8temp_softc *sc = device_get_softc(dev); 180 int i; 181 struct sysctl_ctx_list *sysctlctx; 182 struct sysctl_oid *sysctlnode; 183 184 /* 185 * dev.cpu.N.temperature. 186 */ 187 nexus = device_find_child(root_bus, "nexus", 0); 188 acpi = device_find_child(nexus, "acpi", 0); 189 190 for (i = 0; i < 2; i++) { 191 cpu = device_find_child(acpi, "cpu", 192 device_get_unit(dev) * 2 + i); 193 if (cpu) { 194 sysctlctx = device_get_sysctl_ctx(cpu); 195 196 sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx, 197 SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), 198 OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD, 199 dev, CORE0, k8temp_sysctl, "I", 200 "Max of sensor 0 / 1"); 201 } 202 } 203 204 /* 205 * dev.k8temp.N tree. 206 */ 207 sysctlctx = device_get_sysctl_ctx(dev); 208 sysctlnode = SYSCTL_ADD_NODE(sysctlctx, 209 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor0", 210 CTLFLAG_RD, 0, "Sensor 0"); 211 212 SYSCTL_ADD_PROC(sysctlctx, 213 SYSCTL_CHILDREN(sysctlnode), 214 OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD, 215 dev, SENSOR0_CORE0, k8temp_sysctl, "I", 216 "Sensor 0 / Core 0 temperature"); 217 218 SYSCTL_ADD_PROC(sysctlctx, 219 SYSCTL_CHILDREN(sysctlnode), 220 OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD, 221 dev, SENSOR0_CORE1, k8temp_sysctl, "I", 222 "Sensor 0 / Core 1 temperature"); 223 224 sysctlnode = SYSCTL_ADD_NODE(sysctlctx, 225 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor1", 226 CTLFLAG_RD, 0, "Sensor 1"); 227 228 SYSCTL_ADD_PROC(sysctlctx, 229 SYSCTL_CHILDREN(sysctlnode), 230 OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD, 231 dev, SENSOR0_CORE0, k8temp_sysctl, "I", 232 "Sensor 1 / Core 0 temperature"); 233 234 SYSCTL_ADD_PROC(sysctlctx, 235 SYSCTL_CHILDREN(sysctlnode), 236 OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD, 237 dev, SENSOR0_CORE0, k8temp_sysctl, "I", 238 "Sensor 1 / Core 1 temperature"); 239 240 return (0); 241} 242 243int 244k8temp_detach(device_t dev) 245{ 246 int i; 247 struct k8temp_softc *sc = device_get_softc(dev); 248 249 for (i = 0; i < 2; i++) { 250 if (sc->sc_sysctl_cpu[i]) 251 sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0); 252 } 253 254 /* NewBus removes the dev.k8temp.N tree by itself. */ 255 256 return (0); 257} 258 259static int 260k8temp_sysctl(SYSCTL_HANDLER_ARGS) 261{ 262 device_t dev = (device_t) arg1; 263 int error; 264 int32_t temp, auxtemp[2]; 265 266 switch (arg2) { 267 case CORE0: 268 auxtemp[0] = k8temp_gettemp(dev, SENSOR0_CORE0); 269 auxtemp[1] = k8temp_gettemp(dev, SENSOR1_CORE0); 270 temp = max(auxtemp[0], auxtemp[1]); 271 break; 272 case CORE1: 273 auxtemp[0] = k8temp_gettemp(dev, SENSOR0_CORE1); 274 auxtemp[1] = k8temp_gettemp(dev, SENSOR1_CORE1); 275 temp = max(auxtemp[0], auxtemp[1]); 276 break; 277 default: 278 temp = k8temp_gettemp(dev, arg2); 279 break; 280 } 281 error = sysctl_handle_int(oidp, &temp, 0, req); 282 283 return (error); 284} 285 286static int32_t 287k8temp_gettemp(device_t dev, k8sensor_t sensor) 288{ 289 uint8_t cfg; 290 uint32_t temp; 291 292 cfg = pci_read_config(dev, K8TEMP_REG, 1); 293 switch (sensor) { 294 case SENSOR0_CORE0: 295 cfg &= ~(K8TEMP_REG_SELSENSOR | K8TEMP_REG_SELCORE); 296 break; 297 case SENSOR0_CORE1: 298 cfg &= ~K8TEMP_REG_SELSENSOR; 299 cfg |= K8TEMP_REG_SELCORE; 300 break; 301 case SENSOR1_CORE0: 302 cfg &= ~K8TEMP_REG_SELCORE; 303 cfg |= K8TEMP_REG_SELSENSOR; 304 break; 305 case SENSOR1_CORE1: 306 cfg |= (K8TEMP_REG_SELSENSOR | K8TEMP_REG_SELCORE); 307 break; 308 default: 309 cfg = 0; 310 break; 311 } 312 pci_write_config(dev, K8TEMP_REG, cfg, 1); 313 temp = pci_read_config(dev, K8TEMP_REG, 4); 314 temp = ((temp >> 16) & 0xff) - K8TEMP_MINTEMP; 315 316 return (temp); 317} 318