amdtemp.c revision 178949
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 27/* 28 * Driver for the AMD K8 thermal sensors. Based on a Linux driver by the 29 * same name. 30 */ 31 32#include <sys/cdefs.h> 33__FBSDID("$FreeBSD: head/sys/dev/k8temp/k8temp.c 178949 2008-05-11 23:14:07Z rpaulo $"); 34 35#include <sys/param.h> 36#include <sys/bus.h> 37#include <sys/systm.h> 38#include <sys/types.h> 39#include <sys/module.h> 40#include <sys/conf.h> 41#include <sys/kernel.h> 42#include <sys/sysctl.h> 43 44#include <machine/specialreg.h> 45#include <machine/cpufunc.h> 46#include <machine/md_var.h> 47 48#include <dev/pci/pcireg.h> 49#include <dev/pci/pcivar.h> 50 51struct k8temp_softc { 52 device_t sc_dev; 53 int sc_temps[4]; 54 int sc_ntemps; 55 struct sysctl_oid *sc_oid; 56 struct sysctl_oid *sc_sysctl_cpu[2]; 57}; 58 59#define VENDORID_AMD 0x1022 60#define DEVICEID_AMD_MISC 0x1103 61 62static struct k8temp_product { 63 uint16_t k8temp_vendorid; 64 uint16_t k8temp_deviceid; 65} k8temp_products[] = { 66 { VENDORID_AMD, DEVICEID_AMD_MISC }, 67 { 0, 0 } 68}; 69 70/* 71 * Register control 72 */ 73#define K8TEMP_REG 0xe4 74#define K8TEMP_REG_SELSENSOR 0x40 75#define K8TEMP_REG_SELCORE 0x04 76 77#define K8TEMP_MINTEMP 49 /* -49 C is the mininum temperature */ 78 79typedef enum { 80 SENSOR0_CORE0, 81 SENSOR0_CORE1, 82 SENSOR1_CORE0, 83 SENSOR1_CORE1, 84 CORE0, 85 CORE1 86} k8sensor_t; 87 88/* 89 * Device methods. 90 */ 91static void k8temp_identify(driver_t *driver, device_t parent); 92static int k8temp_probe(device_t dev); 93static int k8temp_attach(device_t dev); 94static int k8temp_detach(device_t dev); 95static int k8temp_match(device_t dev); 96static int32_t k8temp_gettemp(device_t dev, k8sensor_t sensor); 97static int k8temp_sysctl(SYSCTL_HANDLER_ARGS); 98 99static device_method_t k8temp_methods[] = { 100 /* Device interface */ 101 DEVMETHOD(device_identify, k8temp_identify), 102 DEVMETHOD(device_probe, k8temp_probe), 103 DEVMETHOD(device_attach, k8temp_attach), 104 DEVMETHOD(device_detach, k8temp_detach), 105 106 {0, 0} 107}; 108 109static driver_t k8temp_driver = { 110 "k8temp", 111 k8temp_methods, 112 sizeof(struct k8temp_softc), 113}; 114 115static devclass_t k8temp_devclass; 116DRIVER_MODULE(k8temp, hostb, k8temp_driver, k8temp_devclass, NULL, NULL); 117 118static int 119k8temp_match(device_t dev) 120{ 121 int i; 122 uint16_t vendor, devid; 123 124 vendor = pci_get_vendor(dev); 125 devid = pci_get_device(dev); 126 127 for (i = 0; k8temp_products[i].k8temp_vendorid != 0; i++) { 128 if (vendor == k8temp_products[i].k8temp_vendorid && 129 devid == k8temp_products[i].k8temp_deviceid) 130 return (1); 131 } 132 133 return (0); 134} 135 136static void 137k8temp_identify(driver_t *driver, device_t parent) 138{ 139 device_t child; 140 141 /* Make sure we're not being doubly invoked. */ 142 if (device_find_child(parent, "k8temp", -1) != NULL) 143 return; 144 145 if (k8temp_match(parent)) { 146 child = device_add_child(parent, "k8temp", -1); 147 if (child == NULL) 148 device_printf(parent, "add k8temp child failed\n"); 149 } 150 151} 152 153static int 154k8temp_probe(device_t dev) 155{ 156 uint32_t regs[4]; 157 158 if (resource_disabled("k8temp", 0)) 159 return (ENXIO); 160 161 do_cpuid(1, regs); 162 switch (regs[0]) { 163 case 0xf40: 164 case 0xf50: 165 case 0xf51: 166 return (ENXIO); 167 } 168 device_set_desc(dev, "AMD K8 Thermal Sensors"); 169 170 return (BUS_PROBE_GENERIC); 171} 172 173static int 174k8temp_attach(device_t dev) 175{ 176 device_t nexus, acpi, cpu; 177 struct k8temp_softc *sc = device_get_softc(dev); 178 int i; 179 struct sysctl_ctx_list *sysctlctx; 180 struct sysctl_oid *sysctlnode; 181 182 /* 183 * dev.cpu.N.temperature. 184 */ 185 nexus = device_find_child(root_bus, "nexus", 0); 186 acpi = device_find_child(nexus, "acpi", 0); 187 188 for (i = 0; i < 2; i++) { 189 cpu = device_find_child(acpi, "cpu", 190 device_get_unit(dev) * 2 + i); 191 if (cpu) { 192 sysctlctx = device_get_sysctl_ctx(cpu); 193 194 sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx, 195 SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), 196 OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD, 197 dev, CORE0, k8temp_sysctl, "I", 198 "Max of sensor 0 / 1"); 199 } 200 } 201 202 /* 203 * dev.k8temp.N tree. 204 */ 205 sysctlctx = device_get_sysctl_ctx(dev); 206 sysctlnode = SYSCTL_ADD_NODE(sysctlctx, 207 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor0", 208 CTLFLAG_RD, 0, "Sensor 0"); 209 210 SYSCTL_ADD_PROC(sysctlctx, 211 SYSCTL_CHILDREN(sysctlnode), 212 OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD, 213 dev, SENSOR0_CORE0, k8temp_sysctl, "I", 214 "Sensor 0 / Core 0 temperature"); 215 216 SYSCTL_ADD_PROC(sysctlctx, 217 SYSCTL_CHILDREN(sysctlnode), 218 OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD, 219 dev, SENSOR0_CORE1, k8temp_sysctl, "I", 220 "Sensor 0 / Core 1 temperature"); 221 222 sysctlnode = SYSCTL_ADD_NODE(sysctlctx, 223 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "sensor1", 224 CTLFLAG_RD, 0, "Sensor 1"); 225 226 SYSCTL_ADD_PROC(sysctlctx, 227 SYSCTL_CHILDREN(sysctlnode), 228 OID_AUTO, "core0", CTLTYPE_INT | CTLFLAG_RD, 229 dev, SENSOR0_CORE0, k8temp_sysctl, "I", 230 "Sensor 1 / Core 0 temperature"); 231 232 SYSCTL_ADD_PROC(sysctlctx, 233 SYSCTL_CHILDREN(sysctlnode), 234 OID_AUTO, "core1", CTLTYPE_INT | CTLFLAG_RD, 235 dev, SENSOR0_CORE0, k8temp_sysctl, "I", 236 "Sensor 1 / Core 1 temperature"); 237 238 return (0); 239} 240 241int 242k8temp_detach(device_t dev) 243{ 244 int i; 245 struct k8temp_softc *sc = device_get_softc(dev); 246 247 for (i = 0; i < 2; i++) { 248 if (sc->sc_sysctl_cpu[i]) 249 sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0); 250 } 251 252 /* NewBus removes the dev.k8temp.N tree by itself. */ 253 254 return (0); 255} 256 257static int 258k8temp_sysctl(SYSCTL_HANDLER_ARGS) 259{ 260 device_t dev = (device_t) arg1; 261 int error; 262 int32_t temp, auxtemp[2]; 263 264 switch (arg2) { 265 case CORE0: 266 auxtemp[0] = k8temp_gettemp(dev, SENSOR0_CORE0); 267 auxtemp[1] = k8temp_gettemp(dev, SENSOR1_CORE0); 268 temp = MAX(auxtemp[0], auxtemp[1]); 269 break; 270 case CORE1: 271 auxtemp[0] = k8temp_gettemp(dev, SENSOR0_CORE1); 272 auxtemp[1] = k8temp_gettemp(dev, SENSOR1_CORE1); 273 temp = MAX(auxtemp[0], auxtemp[1]); 274 break; 275 default: 276 temp = k8temp_gettemp(dev, arg2); 277 break; 278 } 279 error = sysctl_handle_int(oidp, &temp, 0, req); 280 281 return (error); 282} 283 284static int32_t 285k8temp_gettemp(device_t dev, k8sensor_t sensor) 286{ 287 uint8_t cfg; 288 uint32_t temp; 289 290 cfg = pci_read_config(dev, K8TEMP_REG, 1); 291 switch (sensor) { 292 case SENSOR0_CORE0: 293 cfg &= ~(K8TEMP_REG_SELSENSOR | K8TEMP_REG_SELCORE); 294 break; 295 case SENSOR0_CORE1: 296 cfg &= ~K8TEMP_REG_SELSENSOR; 297 cfg |= K8TEMP_REG_SELCORE; 298 break; 299 case SENSOR1_CORE0: 300 cfg &= ~K8TEMP_REG_SELCORE; 301 cfg |= K8TEMP_REG_SELSENSOR; 302 break; 303 case SENSOR1_CORE1: 304 cfg |= (K8TEMP_REG_SELSENSOR | K8TEMP_REG_SELCORE); 305 break; 306 default: 307 cfg = 0; 308 break; 309 } 310 pci_write_config(dev, K8TEMP_REG, cfg, 1); 311 temp = pci_read_config(dev, K8TEMP_REG, 4); 312 temp = ((temp >> 16) & 0xff) - K8TEMP_MINTEMP; 313 314 return (temp); 315} 316