#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/sensors.h>
#include <machine/md_var.h>
#include <machine/specialreg.h>
#include <bus/pci/pcivar.h>
#include "pcidevs.h"
#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
#define KM_GET_CURTMP(r) (((r) >> 21) & 0x7ff)
#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;
}