Date: Mon, 7 Oct 2013 05:24:41 +0400 From: rozhuk.im@gmail.com To: <hackers@freebsd.org> Subject: amdtemp need help with testing Message-ID: <52520d5f.c402cd0a.5f4e.ffffffa2@mx.google.com>
next in thread | raw e-mail | index | archive | help
[-- Attachment #1 --]
I updated amdtemp and now I need your help with testing.
Now the driver should support all AMD processors.
For a family of 15h and 16h, not all sensors are available - for my system
does not find drivers for ati SMBus, and other systems based on the AMD I
have not.
[-- Attachment #2 --]
/*-
* Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org>
* Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org>
* Copyright (c) 2009-2011 Jung-uk Kim <jkim@FreeBSD.org>
* Copyright (c) 2013 Rozhuk Ivan <rozhuk.im@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Driver for the AMD CPU on-die thermal sensors.
* Initially based on the k8temp Linux driver.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/resource.h>
#include <sys/rman.h>
#include <sys/sysctl.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <machine/md_var.h>
#include <machine/specialreg.h>
#include <machine/cputypes.h>
#include <dev/pci/pcivar.h>
#if defined(__FreeBSD_version) && (__FreeBSD_version < 999999)
/* XXX */
extern uint32_t pci_cfgregread(int, int, int, int, int);
#endif
struct amdtemp_softc {
device_t dev;
struct mtx lock; /* Read/write lock for some registers. */
uint32_t cpu_ncores;
uint32_t flags;
uint32_t tts_flags; /* Thermaltrip Status flags. */
int32_t tts_temp_offset[4];
int32_t rtc_temp_offset;
int32_t tsi_temp_offset[8];
struct sysctl_oid *sysctl_cpu[MAXCPU]; /* dev.cpu.X.temperature oids. */
struct intr_config_hook sc_ich;
};
#define AMDTEMP_F_TTS 1 /* Thermaltrip Status. */
#define AMDTEMP_F_RTC 2 /* Reported Temperature Control. */
#define AMDTEMP_F_TSI 4 /* TSI via CPU registers. */
#define AMDTEMP_F_SBTSI 8 /* TSI via SMBus. */
#define AMDTEMP_TTS_F_CS_SWAP 0x01 /* ThermSenseCoreSel is inverted. */
#define AMDTEMP_TTS_F_CT_10BIT 0x02 /* CurTmp is 10-bit wide. */
#define AMDTEMP_TTS_F_OFF28 0x04 /* CurTmp starts at -28C. */
#define AMDTEMP_LOCK(sc) mtx_lock(&(sc)->lock)
#define AMDTEMP_UNLOCK(sc) mtx_unlock(&(sc)->lock)
/* CPU Family/Model Register */
#define AMD_REG_CPUID 0xfc
/*
* Thermaltrip Status Register
* BIOS and Kernel Developer’s Guide for AMD NPT Family 0Fh Processors
* 32559 Rev. 3.16 November 2009
*/
/* D18F3xE4 Thermtrip Status Register */
#define AMD_REG_THERMTRIP_STAT 0xe4
union reg_amd_thermtrip_status_desc {
uint32_t u32;
struct reg_amd_thermtrip_status_bits {
uint32_t r0:1; /* 0 Reserved. */
uint32_t Thermtp:1; /* 1 ro The processor has entered the THERMTRIP state. */
uint32_t ThermSenseCoreSel:1; /* 2 rw */
uint32_t ThermtpSense0:1; /* 3 ro */
uint32_t ThermtpSense1:1; /* 4 ro */
uint32_t ThermtpEn:1; /* 5 ro The THERMTRIP state is supported by the processor. */
uint32_t ThermSenseSel:1; /* 6 rw */
uint32_t r1:1; /* 7 Reserved. */
uint32_t DiodeOffset:6; /* 13:8 ro Thermal diode offset is used to correct the measurement made by an external temperature sensor. */
uint32_t CurTmp:10; /* 23:14 ro This field returns the current value of the internal thermal sensor. */
uint32_t TjOffset:5; /* 28:24 ro This field is the offset from CurTmp used to normalize to Tcontrol. */
uint32_t r2:2; /* 30:29 Reserved. */
uint32_t SwThermtp:1; /* 31 rw */
} __packed bits;
};
/* DRAM Configuration High Register */
#define AMD_REG_DRAM_CONF_HIGH 0x94 /* Function 2 */
#define AMD_REG_DRAM_MODE_DDR3 0x0100
/* D18F3xA4 Reported Temperature Control Register */
#define AMD_REG_REPTMP_CTRL 0xa4
union reg_amd_rep_tmp_ctrl_desc {
uint32_t u32;
struct reg_amd_rep_tmp_ctrl_bits {
uint32_t PerStepTimeUp:5; /* 4:0 rw per 1/8th step time up. */
uint32_t TmpMaxDiffUp:2;/* 6:5 rw temperature maximum difference up. */
uint32_t TmpSlewDnEn:1; /* 7 rw temperature slew downward enable. */
uint32_t PerStepTimeDn:5;/* 12:8 rw per 1/8th step time down. */
uint32_t r0:3; /* 15:13 Reserved. */
uint32_t CurTmpTjSel:2; /* 17:16 rw Current temperature select. */
uint32_t r1:3; /* 20:18 Reserved. */
uint32_t CurTmp:11; /* 31:21 ro/rw current temperature. */
} __packed bits;
};
/* CurTmpTjSel valid family 10h, 15h, 16h processors. */
/* SB-TSI */
#define AMD_REG_SBI_CTRL 0x1e4 /* SBI Control */
union reg_amd_sbi_ctrl_desc {
uint32_t u32;
struct reg_amd_sbi_ctrl_bits {
uint32_t r0:1; /* 0 Reserved. */
uint32_t SbRmiDis:1; /* 1 ro SMBus-based sideband remote management interface disable. */
uint32_t r1:1; /* 2 Reserved. */
uint32_t SbTsiDis:1; /* 3 ro SMBus-based sideband temperature sensor interface disable. */
uint32_t SbiAddr:3; /* 6:4 rw SMBus-based sideband interface address. */
uint32_t r2:1; /* 7 Reserved. */
uint32_t LvtOffset:4; /* 11:8 rw local vector table offset. */
uint32_t r3:19; /* 30:12 Reserved. */
uint32_t SbiRegWrDn:1; /* 31 ro SBI register write complete. */
} __packed bits;
};
/*
* AMD Family 10h Processor BKDG: SbRmiDis (bit offset: 1) + SbTsiDis (bit offset: 3)
* AMD Family 11h Processor BKDG: SbTsiDis (bit offset: 1)
* AMD Family 12h Processor BKDG: SbTsiDis (bit offset: 1)
* BKDG for AMD Family 14h Models 00h-0Fh Processors: SbTsiDis (bit offset: 1)
* BKDG for AMD Family 15h Models 00h-0Fh Processors: SbRmiDis, no TSI
* BKDG for AMD Family 16h Models 00h-0Fh Processors: ??? 48751 Rev 3.00 - May 30, 2013
*/
#define AMD_REG_SBI_ADDR 0x1e8 /* SBI Address */
#define AMD_REG_SBI_ADDR_MASK 0x07
#define AMD_REG_SBI_DATA 0x1ec /* SBI Data */
#define AMD_SBI_WRITE_TIMEOUT 100 /* XXX should be increased? */
/* SB-TSI registers. */
#define SB_TSI_REG_CPU_TEMP_HB 0x01 /* CPU Temperature High Byte Register. */
#define SB_TSI_REG_STATUS 0x02 /* SB-TSI Status Register. */
#define SB_TSI_REG_CFG 0x03 /* SB-TSI Configuration Register. */
#define SB_TSI_REG_UPD_RATE 0x04 /* Update Rate Register. */
#define SB_TSI_REG_HIGH_TEMP_THB 0x07 /* High Temperature Threshold High Byte Register. */
#define SB_TSI_REG_LOW_TEMP_THB 0x08 /* Low Temperature Threshold High Byte Register.*/
#define SB_TSI_REG_CFG2 0x09 /* SB-TSI Configuration Register. */
#define SB_TSI_REG_CPU_TEMP_LB 0x10 /* CPU Temperature Low Byte Register. */
#define SB_TSI_REG_CPU_TEMP_OFF_HB 0x11 /* CPU Temperature Offset High Byte Register. */
#define SB_TSI_REG_CPU_TEMP_OFF_LB 0x12 /* CPU Temperature Offset Low Byte Register. */
#define SB_TSI_REG_HIGH_TEMP_TLB 0x13 /* High Temperature Threshold Low Byte Register. */
#define SB_TSI_REG_LOW_TEMP_TLB 0x14 /* Low Temperature Threshold Low Byte Register. */
#define SB_TSI_REG_TIMEOUT_CFG 0x22 /* Timeout Configuration Register. */
#define SB_TSI_REG_ALERT_THRESHOLD 0x32 /* Alert Threshold Register. */
#define SB_TSI_REG_ALERT_CFG 0xbf /* Alert Configuration Register. */
#define SB_TSI_REG_MANUFACTURE_ID 0xfe /* Manufacture ID Register. */
#define SB_TSI_REG_REVISION 0xff /* SB-TSI Revision Register. */
#define AMDTEMP_ZERO_C_TO_K 2732
#define ARG2_GET_REG(arg) ((arg) & 0xffff)
#define ARG2_GET_A1(arg) (((arg) >> 16) & 0xff)
#define ARG2_GET_A2(arg) (((arg) >> 24) & 0xff)
#define MAKE_ARG2(reg, a1, a2) \
(((reg) & 0xff) | (((a1) & 0xff) << 16) | (((a2) & 0xff) << 24))
struct amdtemp_sysctl_reg {
uint16_t reg;
uint8_t a1;
uint8_t a2;
uint32_t flags;
char *fmt;
int (*oid_handler)(SYSCTL_HANDLER_ARGS);
char *name;
char *descr;
};
static void amdtemp_sysctl_reg_add(struct amdtemp_softc *sc,
struct sysctl_oid_list *child, struct amdtemp_sysctl_reg *regs);
static void amdtemp_sysctl_reg_add2(struct amdtemp_softc *sc,
struct sysctl_oid_list *child, struct amdtemp_sysctl_reg *regs,
uint32_t a2);
static int amdtemp_sysctl_reg_bits(SYSCTL_HANDLER_ARGS);
static uint32_t amdtemp_tts_get_temp(struct amdtemp_softc *sc, uint32_t reg,
uint8_t core, uint8_t sense);
static int amdtemp_tts_temp_reg_sysctl(SYSCTL_HANDLER_ARGS);
static int amdtemp_rtc_temp_sysctl(SYSCTL_HANDLER_ARGS);
static void amdtemp_sbi_set_addr(struct amdtemp_softc *sc, uint32_t sbi_addr);
static uint32_t amdtemp_sbi_read(struct amdtemp_softc *sc, uint32_t sbi_addr,
uint32_t reg_addr);
static int amdtemp_sbi_write(struct amdtemp_softc *sc, uint32_t sbi_addr,
uint32_t reg_addr, uint8_t data);
static int amdtemp_tsi_reg_sysctl(SYSCTL_HANDLER_ARGS);
static int amdtemp_tsi_temp_reg_sysctl(SYSCTL_HANDLER_ARGS);
/* D18F3xE4 Thermtrip Status Register */
static struct amdtemp_sysctl_reg amdtemp_thermtrip_status_reg_bits[] = {
{ AMD_REG_THERMTRIP_STAT, 24, 5, (CTLFLAG_RD | CTLTYPE_UINT), "IU", amdtemp_sysctl_reg_bits, "TjOffset", "This field is the offset from CurTmp used to normalize to Tcontrol." },
{ AMD_REG_THERMTRIP_STAT, 8, 6, (CTLFLAG_RD | CTLTYPE_UINT), "IU", amdtemp_sysctl_reg_bits, "DiodeOffset", "Thermal diode offset is used to correct the measurement made by an external temperature sensor." },
{ AMD_REG_THERMTRIP_STAT, 5, 1, (CTLFLAG_RD | CTLTYPE_UINT), "IU", amdtemp_sysctl_reg_bits, "ThermtpEn", "The THERMTRIP state is supported by the processor." },
{ AMD_REG_THERMTRIP_STAT, 1, 1, (CTLFLAG_RD | CTLTYPE_UINT), "IU", amdtemp_sysctl_reg_bits, "Thermtrip", "The processor has entered the THERMTRIP state." },
{ 0, 0, 0, 0, NULL, NULL, NULL, NULL }
};
/* D18F3xA4 Reported Temperature Control Register */
static struct amdtemp_sysctl_reg amdtemp_reptmp_reg_bits[] = {
{ AMD_REG_REPTMP_CTRL, 21,11, (CTLFLAG_RD | CTLTYPE_INT), "IK", amdtemp_rtc_temp_sysctl, "CurTmp", "Provides the current control temperature, Tctl, after the slew-rate controls have been applied." },
{ AMD_REG_REPTMP_CTRL, 16, 2, (CTLFLAG_RW | CTLTYPE_INT), "IK", amdtemp_rtc_temp_sysctl, "CurTmpTjSel", "Specifies a value used to create Tctl." },
{ AMD_REG_REPTMP_CTRL, 7, 1, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_sysctl_reg_bits, "TmpSlewDnEn", "Temperature slew downward enable." },
{ AMD_REG_REPTMP_CTRL, 5, 2, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_sysctl_reg_bits, "TmpMaxDiffUp", "Specifies the maximum difference, (Tctlm - Tctl), when Tctl immediatly updates to Tctlm." },
{ AMD_REG_REPTMP_CTRL, 8, 5, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_sysctl_reg_bits, "PerStepTimeDn", "Specifies the time that Tctlm must remain below Tctl before applying a 0.125 downward step." },
{ AMD_REG_REPTMP_CTRL, 0, 5, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_sysctl_reg_bits, "PerStepTimeUp", "Specifies the time that Tctlm must remain above Tctl before applying a 0.125 upward step." },
{ 0, 0, 0, 0, NULL, NULL, NULL, NULL }
};
/* SB-TSI registers. */
static struct amdtemp_sysctl_reg amdtemp_tsi_regs[] = {
{ SB_TSI_REG_CPU_TEMP_LB, SB_TSI_REG_CPU_TEMP_HB, 0, (CTLFLAG_RD | CTLTYPE_INT), "IK", amdtemp_tsi_temp_reg_sysctl, "cpu_temperature", "CPU Temperature" },
{ SB_TSI_REG_HIGH_TEMP_TLB, SB_TSI_REG_HIGH_TEMP_THB, 0, (CTLFLAG_RD | CTLTYPE_INT), "IK", amdtemp_tsi_temp_reg_sysctl, "high_temperature_threshold", "High Temperature Threshold" },
{ SB_TSI_REG_LOW_TEMP_TLB, SB_TSI_REG_LOW_TEMP_THB, 0, (CTLFLAG_RD | CTLTYPE_INT), "IK", amdtemp_tsi_temp_reg_sysctl, "low_temperature_threshold", "Low Temperature Threshold" },
{ SB_TSI_REG_CPU_TEMP_OFF_HB, 0, 0, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_tsi_reg_sysctl, "cpu_temperature_offset_hi", "CPU Temperature Offset High Byte" },
{ SB_TSI_REG_CPU_TEMP_OFF_LB, 0, 0, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_tsi_reg_sysctl, "cpu_temperature_offset_lo", "CPU Temperature Offset Low Byte" },
{ SB_TSI_REG_STATUS, 0, 0, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_tsi_reg_sysctl, "status", "SB-TSI Status" },
{ SB_TSI_REG_CFG, 0, 0, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_tsi_reg_sysctl, "cfg3", "SB-TSI Configuration Register 0x03" },
{ SB_TSI_REG_CFG2, 0, 0, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_tsi_reg_sysctl, "cfg9", "SB-TSI Configuration Register 0x09" },
{ SB_TSI_REG_UPD_RATE, 0, 0, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_tsi_reg_sysctl, "upd_rate", "Update Rate" },
{ SB_TSI_REG_TIMEOUT_CFG, 0, 0, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_tsi_reg_sysctl, "timeout_cfg", "Timeout Configuration" },
{ SB_TSI_REG_ALERT_THRESHOLD, 0, 0, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_tsi_reg_sysctl, "alert_threshold", "Alert Threshold" },
{ SB_TSI_REG_ALERT_CFG, 0, 0, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_tsi_reg_sysctl, "alert_cfg", "Alert Configuration" },
{ SB_TSI_REG_MANUFACTURE_ID, 0, 0, (CTLFLAG_RD | CTLTYPE_UINT), "IU", amdtemp_tsi_reg_sysctl, "manufacture_id", "Manufacture ID" },
{ SB_TSI_REG_REVISION, 0, 0, (CTLFLAG_RD | CTLTYPE_UINT), "IU", amdtemp_tsi_reg_sysctl, "revision", "SB-TSI Revision" },
{ 0, 0, 0, 0, NULL, NULL, NULL, NULL }
};
/*
* Device methods.
*/
static void amdtemp_identify(driver_t *driver, device_t parent);
static int amdtemp_probe(device_t dev);
static int amdtemp_attach(device_t dev);
static int amdtemp_detach(device_t dev);
static void amdtemp_intrhook(void *arg);
static device_method_t amdtemp_methods[] = {
/* Device interface */
DEVMETHOD(device_identify, amdtemp_identify),
DEVMETHOD(device_probe, amdtemp_probe),
DEVMETHOD(device_attach, amdtemp_attach),
DEVMETHOD(device_detach, amdtemp_detach),
DEVMETHOD_END
};
static driver_t amdtemp_driver = {
"amdtemp",
amdtemp_methods,
sizeof(struct amdtemp_softc),
};
static devclass_t amdtemp_devclass;
DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL);
MODULE_VERSION(amdtemp, 1);
static void
amdtemp_identify(driver_t *driver, device_t parent) {
device_t child;
uint32_t cpuid, model;
/* Make sure we're not being doubly invoked. */
if (device_find_child(parent, "amdtemp", -1) != NULL)
return;
/* AMD processors only. */
if (CPU_VENDOR_AMD != pci_get_vendor(parent))
return;
/* Is processor supported? */
cpuid = pci_read_config(parent, AMD_REG_CPUID, 4);
switch (CPUID_TO_FAMILY(cpuid)) {
case 0x0f:
model = CPUID_TO_MODEL(cpuid);
if ((model == 0x04 && (cpuid & CPUID_STEPPING) == 0) ||
(model == 0x05 && (cpuid & CPUID_STEPPING) <= 1))
return;
break;
case 0x10:
case 0x11:
case 0x12:
case 0x14:
case 0x15:
case 0x16:
break;
default:
return;
}
/* Ok! */
child = device_add_child(parent, "amdtemp", -1);
if (child == NULL)
device_printf(parent, "add amdtemp child failed\n");
}
static int
amdtemp_probe(device_t dev) {
if (resource_disabled("amdtemp", 0))
return (ENXIO);
device_set_desc(dev, "AMD CPU On-Die Thermal Sensors");
return (BUS_PROBE_GENERIC);
}
static int
amdtemp_attach(device_t dev) {
struct amdtemp_softc *sc = device_get_softc(dev);
uint32_t i, cpuid, model;
union reg_amd_sbi_ctrl_desc reg_ctrl;
struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev);
struct sysctl_oid_list *child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), *list;
struct sysctl_oid *node, *sub_node;
char str[32];
int erratum319 = 0;
u_int regs[4], bid;
sc->dev = dev;
/* Find number of cores per package. */
sc->cpu_ncores = (((amd_feature2 & AMDID2_CMP) != 0) ?
((cpu_procinfo2 & AMDID_CMP_CORES) + 1) : 1);
if (sc->cpu_ncores > MAXCPU)
return (ENXIO);
mtx_init(&sc->lock, device_get_nameunit(dev), "amdtemp", MTX_DEF);
cpuid = pci_read_config(dev, AMD_REG_CPUID, 4);
model = CPUID_TO_MODEL(cpuid);
switch (CPUID_TO_FAMILY(cpuid)) {
case 0x0f:
/*
* Thermaltrip Status Register
*
* - ThermSenseCoreSel
*
* Revision F & G: 0 - Core1, 1 - Core0
* Other: 0 - Core0, 1 - Core1
*
* - CurTmp
*
* Revision G: bits 23-14
* Other: bits 23-16
*
* XXX According to the BKDG, CurTmp, ThermSenseSel and
* ThermSenseCoreSel bits were introduced in Revision F
* but CurTmp seems working fine as early as Revision C.
* However, it is not clear whether ThermSenseSel and/or
* ThermSenseCoreSel work in undocumented cases as well.
* In fact, the Linux driver suggests it may not work but
* we just assume it does until we find otherwise.
*
* XXX According to Linux, CurTmp starts at -28C on
* Socket AM2 Revision G processors, which is not
* documented anywhere.
* XXX check TjOffset and DiodeOffset for -49C / -28C
*/
if ((model == 0x04 && (cpuid & CPUID_STEPPING) == 0) ||
(model == 0x05 && (cpuid & CPUID_STEPPING) <= 1))
break; /* No ThermalTrip. */
sc->flags |= AMDTEMP_F_TTS;
if (model >= 0x40)
sc->tts_flags |= AMDTEMP_TTS_F_CS_SWAP;
if (model >= 0x60 && model != 0xc1) {
do_cpuid(0x80000001, regs);
bid = (regs[1] >> 9) & 0x1f;
switch (model) {
case 0x68: /* Socket S1g1 */
case 0x6c:
case 0x7c:
break;
case 0x6b: /* Socket AM2 and ASB1 (2 cores) */
if (bid != 0x0b &&
bid != 0x0c)
sc->tts_flags |= AMDTEMP_TTS_F_OFF28;
break;
case 0x6f: /* Socket AM2 and ASB1 (1 core) */
case 0x7f:
if (bid != 0x07 &&
bid != 0x09 &&
bid != 0x0c)
sc->tts_flags |= AMDTEMP_TTS_F_OFF28;
break;
default:
sc->tts_flags |= AMDTEMP_TTS_F_OFF28;
}
sc->tts_flags |= AMDTEMP_TTS_F_CT_10BIT;
}
break;
case 0x10:
sc->flags |= AMDTEMP_F_RTC;
reg_ctrl.u32 = pci_read_config(dev, AMD_REG_SBI_CTRL, 4);
if (0 == reg_ctrl.bits.SbTsiDis)
sc->flags |= AMDTEMP_F_TSI;
/*
* Erratum 319 Inaccurate Temperature Measurement
* http://support.amd.com/us/Processor_TechDocs/41322.pdf
*/
do_cpuid(0x80000001, regs);
switch ((regs[1] >> 28) & 0xf) {
case 0: /* Socket F */
erratum319 = 1;
break;
case 1: /* Socket AM2+ or AM3 */
if ((pci_cfgregread(pci_get_bus(dev), pci_get_slot(dev), 2,
AMD_REG_DRAM_CONF_HIGH, 2) & AMD_REG_DRAM_MODE_DDR3) != 0 ||
model > 0x04 ||
(model == 0x04 && (cpuid & CPUID_STEPPING) >= 3))
break;
/* XXX 00100F42h (RB-C2) exists in both formats. */
erratum319 = 1;
break;
}
break;
case 0x11:
case 0x12:
case 0x14:
sc->flags |= AMDTEMP_F_RTC;
reg_ctrl.u32 = pci_read_config(dev, AMD_REG_SBI_CTRL, 4);
if (0 == reg_ctrl.bits.SbRmiDis) /* = SbTsiDis */
sc->flags |= AMDTEMP_F_TSI;
break;
case 0x15:
case 0x16:
default:
sc->flags |= AMDTEMP_F_RTC;
/* XXX TODO: read TSI via SMBus. */
break;
}
if (0 != (sc->flags & AMDTEMP_F_TTS)) { /* Thermaltrip Status */
node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "tts",
CTLFLAG_RD, NULL, "Thermaltrip Status");
list = SYSCTL_CHILDREN(node);
amdtemp_sysctl_reg_add(sc, list, amdtemp_thermtrip_status_reg_bits);
for (i = 0; i < sc->cpu_ncores && i < 2; i ++) {
snprintf(str, sizeof(str), "core%i", i);
sub_node = SYSCTL_ADD_NODE(ctx, list, OID_AUTO, str,
CTLFLAG_RD, NULL, "CPU core sensors");
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sub_node), OID_AUTO,
"sensor0", (CTLTYPE_INT | CTLFLAG_RD), sc,
MAKE_ARG2(AMD_REG_THERMTRIP_STAT, i, 0),
amdtemp_tts_temp_reg_sysctl, "IK", "Sensor 0 temperature");
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sub_node), OID_AUTO,
"sensor1", (CTLTYPE_INT | CTLFLAG_RD), sc,
MAKE_ARG2(AMD_REG_THERMTRIP_STAT, i, 1),
amdtemp_tts_temp_reg_sysctl, "IK", "Sensor 1 temperature");
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(sub_node), OID_AUTO,
"sensor0_offset", CTLFLAG_RW,
&sc->tts_temp_offset[((i << 1) | 0)], 0,
"Temperature sensor offset");
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(sub_node), OID_AUTO,
"sensor1_offset", CTLFLAG_RW,
&sc->tts_temp_offset[((i << 1) | 1)], 0,
"Temperature sensor offset");
}
}
if (0 != (sc->flags & AMDTEMP_F_RTC)) { /* Reported Temperature Control */
node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "rtc",
CTLFLAG_RD, NULL, "Reported Temperature Control");
list = SYSCTL_CHILDREN(node);
amdtemp_sysctl_reg_add(sc, list, amdtemp_reptmp_reg_bits);
SYSCTL_ADD_INT(ctx, list, OID_AUTO, "sensor_offset", CTLFLAG_RW,
&sc->rtc_temp_offset, 0, "Temperature sensor offset");
}
if (0 != (sc->flags & AMDTEMP_F_TSI)) { /* Temperature Sensor Interface */
node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "tsi",
CTLFLAG_RD, NULL, "Temperature Sensor Interface");
list = SYSCTL_CHILDREN(node);
for (i = 0; i < 8; i ++) {
if (0 == amdtemp_sbi_read(sc, i, SB_TSI_REG_REVISION))
continue;
snprintf(str, sizeof(str), "sensor%i", i);
sub_node = SYSCTL_ADD_NODE(ctx, list, OID_AUTO, str,
CTLFLAG_RD, NULL, "TSI sensor");
amdtemp_sysctl_reg_add2(sc, SYSCTL_CHILDREN(sub_node),
amdtemp_tsi_regs, i);
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(sub_node), OID_AUTO,
"sensor_offset", CTLFLAG_RW, &sc->tsi_temp_offset[i], 0,
"Temperature sensor offset");
}
}
if (bootverbose) {
if (0 != (sc->flags & AMDTEMP_F_TTS))
device_printf(dev, "Found: Thermaltrip Status.\n");
if (0 != (sc->flags & AMDTEMP_F_RTC))
device_printf(dev, "Found: Reported Temperature Control.\n");
if (0 != (sc->flags & AMDTEMP_F_TSI))
device_printf(dev, "Found: Temperature Sensor Interface via CPU registers.\n");
}
if (erratum319)
device_printf(dev,
"Erratum 319: temperature measurement may be inaccurate\n");
/*
* Try to create dev.cpu sysctl entries and setup intrhook function.
* This is needed because the cpu driver may be loaded late on boot,
* after us.
*/
amdtemp_intrhook(dev);
if (NULL == sc->sysctl_cpu[i]) {
sc->sc_ich.ich_func = amdtemp_intrhook;
sc->sc_ich.ich_arg = sc;
if (config_intrhook_establish(&sc->sc_ich) != 0) {
amdtemp_detach(dev);
device_printf(dev, "config_intrhook_establish failed!\n");
return (ENXIO);
}
}
return (0);
}
int
amdtemp_detach(device_t dev) {
struct amdtemp_softc *sc = device_get_softc(dev);
uint32_t i;
for (i = 0; i < sc->cpu_ncores; i++)
if (sc->sysctl_cpu[i] != NULL)
sysctl_remove_oid(sc->sysctl_cpu[i], 1, 0);
/* NewBus removes the dev.amdtemp.N tree by itself. */
if (sc->sc_ich.ich_arg != NULL) {
sc->sc_ich.ich_arg = NULL;
config_intrhook_disestablish(&sc->sc_ich);
}
mtx_destroy(&sc->lock);
return (0);
}
void
amdtemp_intrhook(void *arg) {
struct amdtemp_softc *sc = arg;
struct sysctl_ctx_list *sysctlctx;
device_t dev = sc->dev, acpi, cpu, nexus;
int (*sysctl_handler)(SYSCTL_HANDLER_ARGS);
intptr_t sysctl_arg2;
uint32_t i, unit_base;
if (sc->sc_ich.ich_arg != NULL) {
sc->sc_ich.ich_arg = NULL;
config_intrhook_disestablish(&sc->sc_ich);
}
/* dev.cpu.N.temperature. */
nexus = device_find_child(root_bus, "nexus", 0);
acpi = device_find_child(nexus, "acpi", 0);
unit_base = (device_get_unit(dev) * sc->cpu_ncores); /* XXX: cpu_ncores not constant for different CPUs... */
for (i = 0; i < sc->cpu_ncores; i ++) {
if (sc->sysctl_cpu[i] != NULL)
continue;
cpu = device_find_child(acpi, "cpu", (unit_base + i));
if (cpu == NULL)
continue;
sysctl_handler = NULL;
if (0 != (sc->flags & AMDTEMP_F_TSI)) { /* Temperature Sensor Interface */
if (0 == amdtemp_sbi_read(sc, i, SB_TSI_REG_REVISION))
continue;
sysctl_handler = amdtemp_tsi_temp_reg_sysctl;
sysctl_arg2 = MAKE_ARG2(SB_TSI_REG_CPU_TEMP_LB,
SB_TSI_REG_CPU_TEMP_HB, i);
} else if (0 != (sc->flags & AMDTEMP_F_RTC)) { /* Reported Temperature Control */
sysctl_handler = amdtemp_rtc_temp_sysctl;
sysctl_arg2 = MAKE_ARG2(AMD_REG_REPTMP_CTRL, 21, 11);
} else if (0 != (sc->flags & AMDTEMP_F_TTS)) { /* Thermaltrip Status */
sysctl_handler = amdtemp_tts_temp_reg_sysctl;
sysctl_arg2 = MAKE_ARG2(AMD_REG_THERMTRIP_STAT, i, 0xff);
}
if (NULL == sysctl_handler)
continue;
sysctlctx = device_get_sysctl_ctx(cpu);
sc->sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx,
SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), OID_AUTO,
"temperature", (CTLTYPE_INT | CTLFLAG_RD), sc, sysctl_arg2,
sysctl_handler, "IK", "Current temparature");
}
}
/* Sysctl staff. */
static void
amdtemp_sysctl_reg_add(struct amdtemp_softc *sc, struct sysctl_oid_list *child,
struct amdtemp_sysctl_reg *regs) {
struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev);
uint32_t i;
for (i = 0; NULL != regs[i].oid_handler; i ++) {
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, regs[i].name,
regs[i].flags, sc,
MAKE_ARG2(regs[i].reg, regs[i].a1, regs[i].a2),
regs[i].oid_handler, regs[i].fmt, regs[i].descr);
}
}
static void
amdtemp_sysctl_reg_add2(struct amdtemp_softc *sc, struct sysctl_oid_list *child,
struct amdtemp_sysctl_reg *regs, uint32_t a2) {
struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev);
uint32_t i;
for (i = 0; NULL != regs[i].oid_handler; i ++) {
SYSCTL_ADD_PROC(ctx, child, OID_AUTO, regs[i].name,
regs[i].flags, sc,
MAKE_ARG2(regs[i].reg, regs[i].a1, a2),
regs[i].oid_handler, regs[i].fmt, regs[i].descr);
}
}
static int
amdtemp_sysctl_reg_bits(SYSCTL_HANDLER_ARGS) {
struct amdtemp_softc *sc = arg1;
uint32_t i, reg_data, reg_num, bits_off, bits_len, bits_mask = 0;
unsigned val;
int error;
reg_num = ARG2_GET_REG(arg2);
bits_off = ARG2_GET_A1(arg2);
bits_len = ARG2_GET_A2(arg2);
reg_data = pci_read_config(sc->dev, reg_num, 4);
for(i = 0; i < bits_len; i ++)
bits_mask |= ((uint32_t)1 << i);
val = ((reg_data >> bits_off) & bits_mask);
error = sysctl_handle_int(oidp, &val, 0, req);
if (0 != error || NULL == req->newptr || val == reg_data)
return (error);
reg_data &= ~(bits_mask << bits_off); // clear all bits at offset
reg_data |= ((val & bits_mask) << bits_off); // set value bits
pci_write_config(sc->dev, reg_num, reg_data, 4);
return (0);
}
/* Thermaltrip Status Register */
static uint32_t
amdtemp_tts_get_temp(struct amdtemp_softc *sc, uint32_t reg, uint8_t core,
uint8_t sense) {
union reg_amd_thermtrip_status_desc reg_tts;
uint32_t val;
reg_tts.u32 = 0;
if (0 == (sc->tts_flags & AMDTEMP_TTS_F_CS_SWAP))
reg_tts.bits.ThermSenseCoreSel = ((0 != core) ? 1 : 0);
else /* Swap. */
reg_tts.bits.ThermSenseCoreSel = ((0 != core) ? 0 : 1);
reg_tts.bits.ThermSenseSel = ((0 != sense) ? 1 : 0);
AMDTEMP_LOCK(sc);
pci_write_config(sc->dev, reg, reg_tts.u32, 4);
reg_tts.u32 = pci_read_config(sc->dev, reg, 4);
AMDTEMP_UNLOCK(sc);
val = reg_tts.bits.CurTmp;
if (0 == (sc->tts_flags & AMDTEMP_TTS_F_CT_10BIT))
val &= ~3; /* Clear first 2 bits. */
val = (AMDTEMP_ZERO_C_TO_K + ((val * 5) / 2) -
((0 != (sc->tts_flags & AMDTEMP_TTS_F_OFF28)) ? 280 : 490));
val += (sc->tts_temp_offset[((reg_tts.bits.ThermSenseCoreSel << 1) |
reg_tts.bits.ThermSenseSel)] * 10);
return (val);
}
/* If 0xff == ARG2_GET_A2(arg2) then retun max temp for core. */
static int
amdtemp_tts_temp_reg_sysctl(SYSCTL_HANDLER_ARGS) {
struct amdtemp_softc *sc = arg1;
uint32_t reg_num;
unsigned val;
int error;
reg_num = ARG2_GET_REG(arg2);
if (0xff == ARG2_GET_A2(arg2)) {
val = imax(amdtemp_tts_get_temp(sc, reg_num, ARG2_GET_A1(arg2), 0),
amdtemp_tts_get_temp(sc, reg_num, ARG2_GET_A1(arg2), 1));
} else {
val = amdtemp_tts_get_temp(sc, reg_num, ARG2_GET_A1(arg2),
ARG2_GET_A2(arg2));
}
error = sysctl_handle_int(oidp, &val, 0, req);
if (0 != error || NULL == req->newptr)
return (error);
return (0);
}
/* D18F3xA4 Reported Temperature Control Register */
static int
amdtemp_rtc_temp_sysctl(SYSCTL_HANDLER_ARGS) {
struct amdtemp_softc *sc = arg1;
union reg_amd_rep_tmp_ctrl_desc reg_ctrl;
uint32_t reg_num, bits_off;
unsigned val;
int error;
reg_num = ARG2_GET_REG(arg2);
bits_off = ARG2_GET_A1(arg2);
AMDTEMP_LOCK(sc);
reg_ctrl.u32 = pci_read_config(sc->dev, reg_num, 4);
switch (bits_off) {
case 16: /* CurTmpTjSel */
reg_ctrl.bits.CurTmpTjSel = 3;
break;
case 21: /* CurTmp */
reg_ctrl.bits.CurTmpTjSel = 0;
break;
}
pci_write_config(sc->dev, reg_num, reg_ctrl.u32, 4);
reg_ctrl.u32 = pci_read_config(sc->dev, reg_num, 4);
if (bits_off == 16) { /* CurTmpTjSel: switch back to CurTmp. */
reg_ctrl.bits.CurTmpTjSel = 0;
pci_write_config(sc->dev, reg_num, reg_ctrl.u32, 4);
}
AMDTEMP_UNLOCK(sc);
val = (AMDTEMP_ZERO_C_TO_K + ((reg_ctrl.bits.CurTmp * 5) / 4));
if (16 == bits_off) /* CurTmpTjSel */
val -= 490;
else
val += (sc->rtc_temp_offset * 10);
error = sysctl_handle_int(oidp, &val, 0, req);
if (0 != error || NULL == req->newptr)
return (error);
//device_printf(sc->dev, "amdtemp_rtc_temp_sysctl: reg_num = %i, bits_off = %i, val = %i, reg_ctrl.u32 = %i\n", reg_num, bits_off, val, reg_ctrl.u32);
//pci_write_config(sc->dev, reg_num, reg_ctrl.u32, 4);
return (0);
}
/* Set SMBus-based sideband interface address: 0-7. */
static void
amdtemp_sbi_set_addr(struct amdtemp_softc *sc, uint32_t sbi_addr) {
union reg_amd_sbi_ctrl_desc reg_ctrl;
sbi_addr &= AMD_REG_SBI_ADDR_MASK;
reg_ctrl.u32 = pci_read_config(sc->dev, AMD_REG_SBI_CTRL, 4);
if (reg_ctrl.bits.SbiAddr == sbi_addr) /* Is address allready set? */
return;
reg_ctrl.bits.SbiAddr = sbi_addr;
pci_write_config(sc->dev, AMD_REG_SBI_CTRL, reg_ctrl.u32, 4);
}
static uint32_t
amdtemp_sbi_read(struct amdtemp_softc *sc, uint32_t sbi_addr, uint32_t reg_addr) {
uint32_t ret;
AMDTEMP_LOCK(sc);
amdtemp_sbi_set_addr(sc, sbi_addr);
pci_write_config(sc->dev, AMD_REG_SBI_ADDR, reg_addr, 4);
ret = pci_read_config(sc->dev, AMD_REG_SBI_DATA, 4);
AMDTEMP_UNLOCK(sc);
return (ret);
}
static int
amdtemp_sbi_write(struct amdtemp_softc *sc, uint32_t sbi_addr, uint32_t reg_addr,
uint8_t data) {
union reg_amd_sbi_ctrl_desc reg_ctrl;
uint32_t data32 = data;
AMDTEMP_LOCK(sc);
amdtemp_sbi_set_addr(sc, sbi_addr);
pci_write_config(sc->dev, AMD_REG_SBI_ADDR, reg_addr, 4);
pci_write_config(sc->dev, AMD_REG_SBI_DATA, data32, 4);
/* Wait write. */
data32 = AMD_SBI_WRITE_TIMEOUT;
while (data32 --) {
reg_ctrl.u32 = pci_read_config(sc->dev, AMD_REG_SBI_CTRL, 4);
if (0 != reg_ctrl.bits.SbiRegWrDn)
break;
DELAY(100);
}
AMDTEMP_UNLOCK(sc);
if (data32 == 0) {
device_printf(sc->dev, "timeout waiting for SBI write.\n");
return (1);
}
return (0);
}
static int
amdtemp_tsi_reg_sysctl(SYSCTL_HANDLER_ARGS) {
struct amdtemp_softc *sc = arg1;
uint32_t reg_data, reg_addr, sbi_addr;
unsigned val;
int error;
reg_addr = ARG2_GET_REG(arg2);
sbi_addr = (ARG2_GET_A2(arg2) & AMD_REG_SBI_ADDR_MASK);
reg_data = amdtemp_sbi_read(sc, sbi_addr, reg_addr);
val = reg_data;
error = sysctl_handle_int(oidp, &val, 0, req);
if (0 != error || NULL == req->newptr || val == reg_data)
return (error);
return (amdtemp_sbi_write(sc, sbi_addr, reg_addr, val));
}
static int
amdtemp_tsi_temp_reg_sysctl(SYSCTL_HANDLER_ARGS) {
struct amdtemp_softc *sc = arg1;
uint32_t reg_data_lo, reg_data_hi, sbi_addr;
unsigned val;
int error;
sbi_addr = (ARG2_GET_A2(arg2) & AMD_REG_SBI_ADDR_MASK);
reg_data_lo = amdtemp_sbi_read(sc, sbi_addr, ARG2_GET_REG(arg2));
reg_data_hi = amdtemp_sbi_read(sc, sbi_addr, ARG2_GET_A1(arg2));
val = (AMDTEMP_ZERO_C_TO_K + (reg_data_hi * 10));
if (SB_TSI_REG_CPU_TEMP_LB == ARG2_GET_REG(arg2)) /* Apply offset only to sensor. */
val += (sc->tsi_temp_offset[sbi_addr] * 10);
if (reg_data_lo & 0x80)
val += 5; /* 0,5 C */
if (reg_data_lo & 0x40)
val += 3; /* 0,25 C */
if (reg_data_lo & 0x20)
val += 1; /* 0,125 C */
error = sysctl_handle_int(oidp, &val, 0, req);
if (0 != error || NULL == req->newptr)
return (error);
return (0);
}
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?52520d5f.c402cd0a.5f4e.ffffffa2>
