/* $OpenBSD: km.c,v 1.8 2013/09/17 13:42:34 kettenis Exp $ */ /* * Copyright (c) 2008/2010 Constantine A. Murenin * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "pcidevs.h" /* * AMD Family 10h/11h/14h/15h Processors, Function 3 -- Miscellaneous Control */ /* Function 3 Registers */ #define KM_REP_TEMP_CONTR_R 0xa4 #define KM_THERMTRIP_STAT_R 0xe4 #define KM_NORTHBRIDGE_CAP_R 0xe8 #define KM_CPUID_FAMILY_MODEL_R 0xfc /* Operations on Reported Temperature Control Register */ #define KM_GET_CURTMP(r) (((r) >> 21) & 0x7ff) /* Operations on Thermtrip Status Register */ #define KM_GET_DIODEOFFSET(r) (((r) >> 8) & 0x7f) struct km_softc { device_t sc_dev; struct ksensor sc_sensor; struct ksensordev sc_sensordev; }; static void km_identify(driver_t *, device_t); static int km_probe(device_t); static int km_attach(device_t); static int km_detach(device_t); static void km_refresh(void *); static device_method_t km_methods[] = { DEVMETHOD(device_identify, km_identify), DEVMETHOD(device_probe, km_probe), DEVMETHOD(device_attach, km_attach), DEVMETHOD(device_detach, km_detach), { NULL, NULL } }; static driver_t km_driver = { "km", km_methods, sizeof(struct km_softc) }; static devclass_t km_devclass; DRIVER_MODULE(km, hostb, km_driver, km_devclass, NULL, NULL); static void km_identify(driver_t *driver, device_t parent) { if (km_probe(parent) == ENXIO) return; if (device_find_child(parent, driver->name, -1) != NULL) return; device_add_child(parent, driver->name, -1); } static int km_probe(device_t dev) { char *desc; if (pci_get_vendor(dev) != PCI_VENDOR_AMD) return ENXIO; switch (pci_get_device(dev)) { case PCI_PRODUCT_AMD_AMD64_F10_MISC: desc = "AMD Family 10h temperature sensor"; break; case PCI_PRODUCT_AMD_AMD64_F11_MISC: desc = "AMD Family 11h temperature sensor"; break; case PCI_PRODUCT_AMD_AMD64_F14_MISC: if (CPUID_TO_FAMILY(cpu_id) == 0x12) desc = "AMD Family 12h temperature sensor"; else desc = "AMD Family 14h temperature sensor"; break; case PCI_PRODUCT_AMD_AMD64_F15_0x_MISC: desc = "AMD Family 15/0xh temperature sensor"; break; case PCI_PRODUCT_AMD_AMD64_F15_1x_MISC: desc = "AMD Family 15/1xh temperature sensor"; break; case PCI_PRODUCT_AMD_AMD64_F15_3x_MISC: desc = "AMD Family 15/3xh temperature sensor"; break; case PCI_PRODUCT_AMD_AMD64_F16_MISC: desc = "AMD Family 16h temperature sensor"; break; default: return ENXIO; } if (device_get_desc(dev) == NULL) device_set_desc(dev, desc); return 0; } static int km_attach(device_t dev) { struct km_softc *sc; sc = device_get_softc(dev); sc->sc_dev = dev; strlcpy(sc->sc_sensordev.xname, device_get_nameunit(dev), sizeof(sc->sc_sensordev.xname)); sc->sc_sensor.type = SENSOR_TEMP; sensor_attach(&sc->sc_sensordev, &sc->sc_sensor); sensor_task_register(sc, km_refresh, 5); sensordev_install(&sc->sc_sensordev); return 0; } static int km_detach(device_t dev) { struct km_softc *sc = device_get_softc(dev); sensordev_deinstall(&sc->sc_sensordev); sensor_task_unregister(sc); return 0; } static void km_refresh(void *arg) { struct km_softc *sc = arg; struct ksensor *s = &sc->sc_sensor; uint32_t r; int c; r = pci_read_config(sc->sc_dev, KM_REP_TEMP_CONTR_R, 4); c = KM_GET_CURTMP(r); s->value = c * 125000 + 273150000; }