Changeset View
Changeset View
Standalone View
Standalone View
/usr/src/sys/dev/amdtemp/amdtemp.c
Context not available. | |||||
* Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org> | * Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org> | ||||
allanjude: this change looks like it is inadvertent | |||||
Done Inline ActionsMy code is old. Then I start there was 2009-2011. rozhuk.im-gmail.com: My code is old. Then I start there was 2009-2011. | |||||
* Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org> | * Copyright (c) 2009 Norikatsu Shigemura <nork@FreeBSD.org> | ||||
* Copyright (c) 2009-2012 Jung-uk Kim <jkim@FreeBSD.org> | * Copyright (c) 2009-2012 Jung-uk Kim <jkim@FreeBSD.org> | ||||
* Copyright (c) 2013-2018 Rozhuk Ivan <rozhuk.im@gmail.com> | |||||
* All rights reserved. | * All rights reserved. | ||||
* | * | ||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
Context not available. | |||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
Done Inline ActionsWhere is it used? mmokhi: Where is it used?
I think maybe, maybe you can use `KASSERT`? | |||||
Done Inline ActionsThis is for disable sysctl static type checks. rozhuk.im-gmail.com: This is for disable sysctl static type checks. | |||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/bus.h> | #include <sys/systm.h> | ||||
#include <sys/conf.h> | |||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/bus.h> | |||||
#include <sys/resource.h> | |||||
#include <sys/rman.h> | |||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/systm.h> | #include <sys/lock.h> | ||||
#include <sys/mutex.h> | |||||
#include <machine/cpufunc.h> | #include <machine/bus.h> | ||||
#include <machine/resource.h> | |||||
#include <machine/md_var.h> | #include <machine/md_var.h> | ||||
#include <machine/specialreg.h> | #include <machine/specialreg.h> | ||||
#include <machine/cputypes.h> | |||||
#include <machine/pci_cfgreg.h> | |||||
#include <dev/pci/pcivar.h> | #include <dev/pci/pcivar.h> | ||||
#include <x86/pci_cfgreg.h> | #include <dev/pci/pcireg.h> | ||||
#include <dev/amdsmn/amdsmn.h> | |||||
typedef enum { | typedef struct pci_io_data_s { | ||||
CORE0_SENSOR0, | uint32_t bus; /* 0xffffffff = amdtemp bus. */ | ||||
CORE0_SENSOR1, | uint32_t slot; | ||||
CORE1_SENSOR0, | uint32_t func; | ||||
CORE1_SENSOR1, | uint32_t addr; /* Addr to set addr for read/write data. */ | ||||
CORE0, | uint32_t data; /* Addr for read/write data */ | ||||
CORE1 | uint32_t addr_offset; /* Register offset in this addr space. */ | ||||
} amdsensor_t; | } pci_io_t, *pci_io_p; | ||||
struct amdtemp_softc { | typedef struct amdtemp_softc_s { | ||||
int sc_ncores; | device_t dev; | ||||
int sc_ntemps; | struct mtx lock; /* Read/write lock for some registers. */ | ||||
int sc_flags; | uint32_t cpu_ncores; | ||||
#define AMDTEMP_FLAG_CS_SWAP 0x01 /* ThermSenseCoreSel is inverted. */ | uint32_t flags; | ||||
#define AMDTEMP_FLAG_CT_10BIT 0x02 /* CurTmp is 10-bit wide. */ | uint32_t tts_flags; /* Thermaltrip Status flags. */ | ||||
#define AMDTEMP_FLAG_ALT_OFFSET 0x04 /* CurTmp starts at -28C. */ | int32_t tts_temp_offset[4]; | ||||
int32_t sc_offset; | int32_t rtc_temp_offset; | ||||
int32_t (*sc_gettemp)(device_t, amdsensor_t); | pci_io_t pci_io; | ||||
struct sysctl_oid *sc_sysctl_cpu[MAXCPU]; | struct sysctl_oid *sysctl_cpu[MAXCPU]; /* dev.cpu.X.temperature oids. */ | ||||
struct intr_config_hook sc_ich; | struct intr_config_hook sc_ich; | ||||
device_t sc_smn; | } amdtemp_softc_t, *amdtemp_softc_p; | ||||
}; | #define AMDTEMP_F_RTC 1 /* Reported Temperature Control. */ | ||||
#define AMDTEMP_F_TTS 2 /* Thermaltrip Status. */ | |||||
#define AMDTEMP_F_HTC 4 /* Hardware Thermal Control (HTC). */ | |||||
#define VENDORID_AMD 0x1022 | #define AMDTEMP_TTS_F_CS_SWAP 0x01 /* ThermSenseCoreSel is inverted. */ | ||||
#define DEVICEID_AMD_MISC0F 0x1103 | #define AMDTEMP_TTS_F_CT_10BIT 0x02 /* CurTmp is 10-bit wide. */ | ||||
#define DEVICEID_AMD_MISC10 0x1203 | #define AMDTEMP_TTS_F_OFF28 0x04 /* CurTmp starts at -28C. */ | ||||
#define DEVICEID_AMD_MISC11 0x1303 | #define AMDTEMP_TTS_F_NO_SENS 0x08 /* No sensors. */ | ||||
#define DEVICEID_AMD_MISC12 0x1403 | |||||
#define DEVICEID_AMD_MISC14 0x1703 | |||||
#define DEVICEID_AMD_MISC15 0x1603 | |||||
#define DEVICEID_AMD_MISC16 0x1533 | |||||
#define DEVICEID_AMD_MISC16_M30H 0x1583 | |||||
#define DEVICEID_AMD_MISC17 0x141d | |||||
#define DEVICEID_AMD_HOSTB17H_ROOT 0x1450 | |||||
#define DEVICEID_AMD_HOSTB17H_DF_F3 0x1463 | |||||
#define DEVICEID_AMD_HOSTB17H_M10H_ROOT 0x15d0 | |||||
#define DEVICEID_AMD_HOSTB17H_M10H_DF_F3 0x15eb | |||||
static struct amdtemp_product { | |||||
uint16_t amdtemp_vendorid; | /* D18F3xFC CPUID Family/Model/Stepping */ | ||||
uint16_t amdtemp_deviceid; | #define AMD_REG_CPUID 0xfc | ||||
} amdtemp_products[] = { | /* DRAM Configuration High Register */ | ||||
{ VENDORID_AMD, DEVICEID_AMD_MISC0F }, | #define AMD_REG_DRAM_CONF_HIGH 0x94 /* Function 2 */ | ||||
{ VENDORID_AMD, DEVICEID_AMD_MISC10 }, | #define AMD_REG_DRAM_MODE_DDR3 0x0100 | ||||
{ VENDORID_AMD, DEVICEID_AMD_MISC11 }, | |||||
{ VENDORID_AMD, DEVICEID_AMD_MISC12 }, | /* D18F3xA4 Reported Temperature Control Register */ | ||||
{ VENDORID_AMD, DEVICEID_AMD_MISC14 }, | #define AMD_REG_REPTMP_CTRL 0xa4 | ||||
{ VENDORID_AMD, DEVICEID_AMD_MISC15 }, | union reg_amd_rep_tmp_ctrl_desc { | ||||
{ VENDORID_AMD, DEVICEID_AMD_MISC16 }, | uint32_t u32; | ||||
{ VENDORID_AMD, DEVICEID_AMD_MISC16_M30H }, | struct reg_amd_rep_tmp_ctrl_bits { | ||||
{ VENDORID_AMD, DEVICEID_AMD_MISC17 }, | uint32_t PerStepTimeUp:5; /* 4:0 rw per 1/8th step time up. */ | ||||
{ VENDORID_AMD, DEVICEID_AMD_HOSTB17H_ROOT }, | uint32_t TmpMaxDiffUp:2;/* 6:5 rw temperature maximum difference up. */ | ||||
{ VENDORID_AMD, DEVICEID_AMD_HOSTB17H_M10H_ROOT }, | 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 CurTmpTjSlewSel:1;/* 18 rw */ | |||||
uint32_t CurTmpRangeSel:1;/* 19 rw */ | |||||
uint32_t r1:1; /* 20 Reserved. */ | |||||
uint32_t CurTmp:11; /* 31:21 ro/rw current temperature. */ | |||||
} bits; | |||||
}; | }; | ||||
CTASSERT(sizeof(struct reg_amd_rep_tmp_ctrl_bits) == sizeof(uint32_t)); | |||||
/* CurTmpTjSel valid family 10h, 15h, 16h processors. */ | |||||
/* | /* | ||||
* Reported Temperature Control Register | * Thermaltrip Status Register | ||||
* BIOS and Kernel Developer’s Guide for AMD NPT Family 0Fh Processors | |||||
* 32559 Rev. 3.16 November 2009 | |||||
*/ | */ | ||||
#define AMDTEMP_REPTMP_CTRL 0xa4 | /* 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 */ | |||||
} bits; | |||||
}; | |||||
CTASSERT(sizeof(struct reg_amd_thermtrip_status_bits) == sizeof(uint32_t)); | |||||
/* | /* | ||||
* Reported Temperature, Family 17h | * The default value of the HTC temperature threshold (Tctl_max) is specified | ||||
* | * in the AMD Family 14h Processor Power and Thermal Datasheet. | ||||
* According to AMD OSRR for 17H, section 4.2.1, bits 31-21 of this register | |||||
* provide the current temp. bit 19, when clear, means the temp is reported in | |||||
* a range 0.."225C" (probable typo for 255C), and when set changes the range | |||||
* to -49..206C. | |||||
*/ | */ | ||||
#define AMDTEMP_17H_CUR_TMP 0x59800 | /* D18F3x64 Hardware Thermal Control (HTC) */ | ||||
#define AMDTEMP_17H_CUR_TMP_RANGE_SEL (1 << 19) | #define AMD_REG_HTC_CTRL 0x64 | ||||
#define AMDTEMP_17H_CUR_TMP_RANGE_OFF 490 | union reg_amd_htc_desc { | ||||
uint32_t u32; | |||||
struct reg_amd_htc_bits { | |||||
uint32_t HtcEn:1; /* 0 rw 1=HTC is enabled; the processor is capable of entering the HTC-active state. */ | |||||
uint32_t r0:3; /* 3:1 Reserved. */ | |||||
uint32_t HtcAct:1; /* 4 ro 1=The processor is currently in the HTC-active state. */ | |||||
uint32_t HtcActSts:1; /* 5 ro Read; set-by-hardware; write-1-to-clear. Reset: 0. This bit is set by hardware when the processor enters the HTC-active state. It is cleared by writing a 1 to it. */ | |||||
uint32_t PslApicHiEn:1; /* 6 rw P-state limit higher value change APIC interrupt enable. */ | |||||
uint32_t PslApicLoEn:1; /* 7 rw P-state limit lower value change APIC interrupt enable. */ | |||||
uint32_t r1:8; /* 15:8 Reserved. */ | |||||
uint32_t HtcTmpLmt:7; /* 22:16 rw HTC temperature limit. */ | |||||
uint32_t HtcSlewSel:1; /* 23 rw HTC slew-controlled temperature select. */ | |||||
uint32_t HtcHystLmt:4; /* 27:24 rw HTC hysteresis. The processor exits the HTC active state when the temperature selected by HtcSlewSel is less than the HTC temperature limit (HtcTmpLmt) minus the HTC hysteresis (HtcHystLmt). */ | |||||
uint32_t HtcPstateLimit:3; /* 30:28 rw HTC P-state limit select. */ | |||||
uint32_t HtcLock:1; /* 31 Read; write-1-only. 1=HtcPstateLimit, HtcHystLmt, HtcTmpLmt, and HtcEn are read-only. */ | |||||
} bits; | |||||
}; | |||||
CTASSERT(sizeof(struct reg_amd_htc_bits) == sizeof(uint32_t)); | |||||
/* | /* For F15h M60h. */ | ||||
* Thermaltrip Status Register (Family 0Fh only) | static pci_io_t pci_io_smu_f15 = { | ||||
*/ | 0xffffffff, | ||||
#define AMDTEMP_THERMTP_STAT 0xe4 | 0, | ||||
#define AMDTEMP_TTSR_SELCORE 0x04 | 0, | ||||
#define AMDTEMP_TTSR_SELSENSOR 0x40 | 0xb8, | ||||
0xbc, | |||||
0xd8200c00 | |||||
}; | |||||
/* | /* For F17h M00h. */ | ||||
* DRAM Configuration High Register | static pci_io_t pci_io_smn_f17 = { | ||||
*/ | 0xffffffff, | ||||
#define AMDTEMP_DRAM_CONF_HIGH 0x94 /* Function 2 */ | 0, | ||||
#define AMDTEMP_DRAM_MODE_DDR3 0x0100 | 0, | ||||
0x60, | |||||
0x64, | |||||
(0x59800 - AMD_REG_REPTMP_CTRL) | |||||
}; | |||||
/* | #define AMDTEMP_ZERO_C_TO_K 2732 | ||||
* CPU Family/Model Register | |||||
*/ | |||||
#define AMDTEMP_CPUID 0xfc | |||||
#if __FreeBSD_version < 1200018 /* Since revision 310051. */ | |||||
#define AMDTEMP_SYSCTL_ADD_PROC(ctx, parent, nbr, name, access, ptr, arg, handler, fmt, descr) \ | |||||
sysctl_add_oid(ctx, parent, nbr, name, (access), \ | |||||
(ptr), (arg), (handler), (fmt), (descr)) | |||||
#else | |||||
#define AMDTEMP_SYSCTL_ADD_PROC(ctx, parent, nbr, name, access, ptr, arg, handler, fmt, descr) \ | |||||
sysctl_add_oid(ctx, parent, nbr, name, (access), \ | |||||
(ptr), (arg), (handler), (fmt), (descr), NULL) | |||||
#endif | |||||
#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)) | |||||
#define AMDTEMP_LOCK(__sc) mtx_lock(&(__sc)->lock) | |||||
#define AMDTEMP_UNLOCK(__sc) mtx_unlock(&(__sc)->lock) | |||||
typedef struct amdtemp_sysctl_reg_s { | |||||
uint16_t reg; | |||||
uint8_t a1; | |||||
uint8_t a2; | |||||
uint32_t flags; | |||||
char *fmt; | |||||
int (*oid_handler)(SYSCTL_HANDLER_ARGS); | |||||
char *name; | |||||
char *descr; | |||||
} sysctl_reg_t, *sysctl_reg_p; | |||||
static void amdtemp_sysctl_reg_add(amdtemp_softc_p sc, | |||||
struct sysctl_oid_list *child, | |||||
sysctl_reg_p regs); | |||||
static int amdtemp_sysctl_reg_bits(SYSCTL_HANDLER_ARGS); | |||||
static uint32_t amdtemp_tts_get_temp(amdtemp_softc_p sc, | |||||
uint32_t reg, uint8_t core, uint8_t sense); | |||||
static int amdtemp_tts_temp_reg_sysctl(SYSCTL_HANDLER_ARGS); | |||||
static int amdtemp_htc_temp_sysctl(SYSCTL_HANDLER_ARGS); | |||||
static int amdtemp_rtc_temp_sysctl(SYSCTL_HANDLER_ARGS); | |||||
static uint32_t amdtemp_pci_read(amdtemp_softc_p sc, uint32_t addr); | |||||
static void amdtemp_pci_write(amdtemp_softc_p sc, uint32_t addr, | |||||
uint32_t data); | |||||
/* D18F3xE4 Thermtrip Status Register */ | |||||
static sysctl_reg_t amdtemp_thermtrip_status_reg_bits[] = { | |||||
{ | |||||
AMD_REG_THERMTRIP_STAT, | |||||
24, | |||||
5, | |||||
(CTLFLAG_RD | CTLTYPE_UINT), | |||||
"IU", | |||||
amdtemp_sysctl_reg_bits, | |||||
"tj_offset", | |||||
__DESCR("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, | |||||
"diode_offset", | |||||
__DESCR("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, | |||||
"enable", | |||||
__DESCR("The THERMTRIP state is supported by the " | |||||
"processor.") | |||||
}, { | |||||
AMD_REG_THERMTRIP_STAT, | |||||
3, | |||||
1, | |||||
(CTLFLAG_RD | CTLTYPE_UINT), | |||||
"IU", | |||||
amdtemp_sysctl_reg_bits, | |||||
"sense", | |||||
__DESCR("The processor temperature exceeded the " | |||||
"THERMTRIP value.") | |||||
}, { | |||||
AMD_REG_THERMTRIP_STAT, | |||||
1, | |||||
1, | |||||
(CTLFLAG_RD | CTLTYPE_UINT), | |||||
"IU", | |||||
amdtemp_sysctl_reg_bits, | |||||
"thermtrip", | |||||
__DESCR("The processor has entered the THERMTRIP state.") | |||||
}, | |||||
{ 0, 0, 0, 0, NULL, NULL, NULL, NULL } | |||||
}; | |||||
/* D18F3x64 Hardware Thermal Control (HTC) */ | |||||
static sysctl_reg_t amdtemp_htc_reg_bits[] = { | |||||
{ | |||||
AMD_REG_HTC_CTRL, | |||||
16, | |||||
7, | |||||
(CTLFLAG_RD | CTLTYPE_INT), | |||||
"IK", | |||||
amdtemp_htc_temp_sysctl, | |||||
"temperature_limit", | |||||
__DESCR("HTC temperature limit") | |||||
}, { | |||||
AMD_REG_HTC_CTRL, | |||||
24, | |||||
4, | |||||
(CTLFLAG_RW | CTLTYPE_INT), | |||||
"IK", | |||||
amdtemp_htc_temp_sysctl, | |||||
"hysteresis_limit", | |||||
__DESCR("HTC hysteresis. The processor exits the " | |||||
"HTC active state when the temperature selected by " | |||||
"HtcSlewSel is less than the HTC temperature limit " | |||||
"(HtcTmpLmt) minus the HTC hysteresis (HtcHystLmt).") | |||||
}, { | |||||
AMD_REG_HTC_CTRL, | |||||
0, | |||||
1, | |||||
(CTLFLAG_RW | CTLTYPE_UINT), | |||||
"IU", | |||||
amdtemp_sysctl_reg_bits, | |||||
"enabled", | |||||
__DESCR("HTC is enabled; the processor is capable of " | |||||
"entering the HTC-active state.") | |||||
}, { | |||||
AMD_REG_HTC_CTRL, | |||||
31, | |||||
1, | |||||
(CTLFLAG_RW | CTLTYPE_UINT), | |||||
"IU", | |||||
amdtemp_sysctl_reg_bits, | |||||
"lock", | |||||
__DESCR("HtcPstateLimit, HtcHystLmt, HtcTmpLmt, and " | |||||
"HtcEn are read-only.") | |||||
}, { | |||||
AMD_REG_HTC_CTRL, | |||||
23, | |||||
1, | |||||
(CTLFLAG_RW | CTLTYPE_UINT), | |||||
"IU", | |||||
amdtemp_sysctl_reg_bits, | |||||
"slew_select", | |||||
__DESCR("HTC slew-controlled temperature select.") | |||||
}, { | |||||
AMD_REG_HTC_CTRL, | |||||
28, | |||||
3, | |||||
(CTLFLAG_RW | CTLTYPE_UINT), | |||||
"IU", | |||||
amdtemp_sysctl_reg_bits, | |||||
"p-state_limit", | |||||
__DESCR("HTC P-state limit select.") | |||||
}, { | |||||
AMD_REG_HTC_CTRL, | |||||
4, | |||||
1, | |||||
(CTLFLAG_RW | CTLTYPE_UINT), | |||||
"IU", | |||||
amdtemp_sysctl_reg_bits, | |||||
"active", | |||||
__DESCR("The processor is currently in the HTC-active " | |||||
"state.") | |||||
}, { | |||||
AMD_REG_HTC_CTRL, | |||||
5, | |||||
1, | |||||
(CTLFLAG_RW | CTLTYPE_UINT), | |||||
"IU", | |||||
amdtemp_sysctl_reg_bits, | |||||
"active_sts", | |||||
__DESCR("set-by-hardware; write-1-to-clear. Reset: 0. " | |||||
"This bit is set by hardware when the processor enters " | |||||
"the HTC-active state. It is cleared by writing a 1 to " | |||||
"it.") | |||||
}, { | |||||
AMD_REG_HTC_CTRL, | |||||
6, | |||||
1, | |||||
(CTLFLAG_RW | CTLTYPE_UINT), | |||||
"IU", | |||||
amdtemp_sysctl_reg_bits, | |||||
"p-state_apic_higher_enable", | |||||
__DESCR("P-state limit higher value change APIC " | |||||
"interrupt enable.") | |||||
}, { | |||||
AMD_REG_HTC_CTRL, | |||||
7, | |||||
1, | |||||
(CTLFLAG_RW | CTLTYPE_UINT), | |||||
"IU", | |||||
amdtemp_sysctl_reg_bits, | |||||
"p-state_apic_lower_enable", | |||||
__DESCR("P-state limit lower value change APIC " | |||||
"interrupt enable.") | |||||
}, | |||||
{ 0, 0, 0, 0, NULL, NULL, NULL, NULL } | |||||
}; | |||||
/* D18F3xA4 Reported Temperature Control Register */ | |||||
static sysctl_reg_t amdtemp_reptmp_reg_bits[] = { | |||||
{ | |||||
AMD_REG_REPTMP_CTRL, | |||||
21, | |||||
11, | |||||
(CTLFLAG_RD | CTLTYPE_INT), | |||||
"IK", | |||||
amdtemp_rtc_temp_sysctl, | |||||
"current_temperature", | |||||
__DESCR("Provides the current control temperature, " | |||||
"Tctl, after the slew-rate controls have been applied.") | |||||
}, { | |||||
AMD_REG_REPTMP_CTRL, | |||||
16, | |||||
2, | |||||
(CTLFLAG_RW | CTLTYPE_UINT), | |||||
"IU", | |||||
amdtemp_sysctl_reg_bits, | |||||
"current_temperature_tj_sel", | |||||
__DESCR("Specifies a value used to create Tctl.") | |||||
}, { | |||||
AMD_REG_REPTMP_CTRL, | |||||
7, | |||||
1, | |||||
(CTLFLAG_RW | CTLTYPE_UINT), | |||||
"IU", | |||||
amdtemp_sysctl_reg_bits, | |||||
"temperature_slew_downward_enable", | |||||
__DESCR("Temperature slew downward enable.") | |||||
}, { | |||||
AMD_REG_REPTMP_CTRL, | |||||
5, | |||||
2, | |||||
(CTLFLAG_RW | CTLTYPE_UINT), | |||||
"IU", | |||||
amdtemp_sysctl_reg_bits, | |||||
"temperature_max_diff_up", | |||||
__DESCR("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, | |||||
"per_step_time_dn", | |||||
__DESCR("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, | |||||
"per_step_time_up", | |||||
__DESCR("Specifies the time that Tctlm must remain " | |||||
"above Tctl before applying a 0.125 upward step.") | |||||
}, | |||||
{ 0, 0, 0, 0, NULL, NULL, NULL, NULL } | |||||
}; | |||||
/* | /* | ||||
* Device methods. | * Device methods. | ||||
*/ | */ | ||||
static void amdtemp_identify(driver_t *driver, device_t parent); | static void amdtemp_identify(driver_t *driver, device_t parent); | ||||
static int amdtemp_probe(device_t dev); | static int amdtemp_probe(device_t dev); | ||||
static int amdtemp_attach(device_t dev); | static int amdtemp_attach(device_t dev); | ||||
static int amdtemp_detach(device_t dev); | |||||
static void amdtemp_intrhook(void *arg); | static void amdtemp_intrhook(void *arg); | ||||
static int amdtemp_detach(device_t dev); | |||||
static int amdtemp_match(device_t dev); | |||||
static int32_t amdtemp_gettemp0f(device_t dev, amdsensor_t sensor); | |||||
static int32_t amdtemp_gettemp(device_t dev, amdsensor_t sensor); | |||||
static int32_t amdtemp_gettemp17h(device_t dev, amdsensor_t sensor); | |||||
static int amdtemp_sysctl(SYSCTL_HANDLER_ARGS); | |||||
static device_method_t amdtemp_methods[] = { | static device_method_t amdtemp_methods[] = { | ||||
/* Device interface */ | /* Device interface */ | ||||
DEVMETHOD(device_identify, amdtemp_identify), | DEVMETHOD(device_identify, amdtemp_identify), | ||||
Context not available. | |||||
static driver_t amdtemp_driver = { | static driver_t amdtemp_driver = { | ||||
"amdtemp", | "amdtemp", | ||||
amdtemp_methods, | amdtemp_methods, | ||||
sizeof(struct amdtemp_softc), | sizeof(amdtemp_softc_t), | ||||
}; | }; | ||||
static devclass_t amdtemp_devclass; | static devclass_t amdtemp_devclass; | ||||
DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL); | DRIVER_MODULE(amdtemp, hostb, amdtemp_driver, amdtemp_devclass, NULL, NULL); | ||||
MODULE_VERSION(amdtemp, 1); | MODULE_VERSION(amdtemp, 1); | ||||
MODULE_DEPEND(amdtemp, amdsmn, 1, 1, 1); | |||||
MODULE_PNP_INFO("U16:vendor;U16:device", pci, amdtemp, amdtemp_products, | |||||
nitems(amdtemp_products)); | |||||
static int | static int | ||||
amdtemp_match(device_t dev) | amdtemp_dev_check(device_t dev) | ||||
{ | { | ||||
int i; | uint32_t cpuid; | ||||
uint16_t vendor, devid; | |||||
vendor = pci_get_vendor(dev); | if (resource_disabled("amdtemp", 0)) | ||||
devid = pci_get_device(dev); | return (ENXIO); | ||||
/* | |||||
for (i = 0; i < nitems(amdtemp_products); i++) { | * Device 18h Function 3 Configuration Registers: | ||||
if (vendor == amdtemp_products[i].amdtemp_vendorid && | * vendor = AMD (0x1022) | ||||
devid == amdtemp_products[i].amdtemp_deviceid) | * class = bridge (0x06000000) | ||||
return (1); | * function = 3 | ||||
*/ | |||||
if (pci_get_vendor(dev) != CPU_VENDOR_AMD || | |||||
pci_get_class(dev) != PCIC_BRIDGE || | |||||
pci_get_function(dev) != 3) | |||||
return (ENXIO); | |||||
/* Does processor have Temperature sensor / THERMTRIP / HTC ? */ | |||||
if ((amd_pminfo & (AMDPM_TS | AMDPM_TTP | AMDPM_TM)) == 0) | |||||
return (ENXIO); | |||||
/* Check minimum cpu family. */ | |||||
cpuid = pci_read_config(dev, AMD_REG_CPUID, 4); | |||||
if (cpuid != cpu_id) { /* XXX: Ryzen fix. */ | |||||
device_printf(dev, "cpu_id = %x, AMD_REG_CPUID = %x.\n", | |||||
cpu_id, cpuid); | |||||
cpuid = cpu_id; | |||||
} | } | ||||
if (CPUID_TO_FAMILY(cpuid) < 0x0f) | |||||
return (ENXIO); | |||||
return (0); | return (0); | ||||
} | } | ||||
Context not available. | |||||
device_t child; | device_t child; | ||||
/* Make sure we're not being doubly invoked. */ | /* Make sure we're not being doubly invoked. */ | ||||
if (device_find_child(parent, "amdtemp", -1) != NULL) | if (device_find_child(parent, "amdtemp", -1)) | ||||
return; | return; | ||||
if (amdtemp_dev_check(parent)) | |||||
if (amdtemp_match(parent)) { | return; | ||||
child = device_add_child(parent, "amdtemp", -1); | child = device_add_child(parent, "amdtemp", -1); | ||||
if (child == NULL) | if (child == NULL) { | ||||
device_printf(parent, "add amdtemp child failed\n"); | device_printf(parent, "add amdtemp child failed.\n"); | ||||
} | } | ||||
} | } | ||||
Context not available. | |||||
static int | static int | ||||
amdtemp_probe(device_t dev) | amdtemp_probe(device_t dev) | ||||
{ | { | ||||
uint32_t family, model; | |||||
if (resource_disabled("amdtemp", 0)) | if (amdtemp_dev_check(dev)) | ||||
return (ENXIO); | return (ENXIO); | ||||
if (!amdtemp_match(device_get_parent(dev))) | |||||
return (ENXIO); | |||||
family = CPUID_TO_FAMILY(cpu_id); | |||||
model = CPUID_TO_MODEL(cpu_id); | |||||
switch (family) { | |||||
case 0x0f: | |||||
if ((model == 0x04 && (cpu_id & CPUID_STEPPING) == 0) || | |||||
(model == 0x05 && (cpu_id & CPUID_STEPPING) <= 1)) | |||||
return (ENXIO); | |||||
break; | |||||
case 0x10: | |||||
case 0x11: | |||||
case 0x12: | |||||
case 0x14: | |||||
case 0x15: | |||||
case 0x16: | |||||
case 0x17: | |||||
break; | |||||
default: | |||||
return (ENXIO); | |||||
} | |||||
device_set_desc(dev, "AMD CPU On-Die Thermal Sensors"); | device_set_desc(dev, "AMD CPU On-Die Thermal Sensors"); | ||||
return (BUS_PROBE_GENERIC); | return (BUS_PROBE_GENERIC); | ||||
Context not available. | |||||
static int | static int | ||||
amdtemp_attach(device_t dev) | amdtemp_attach(device_t dev) | ||||
{ | { | ||||
char tn[32]; | amdtemp_softc_p sc = device_get_softc(dev); | ||||
u_int regs[4]; | uint32_t i, cpuid, model, family, regs[4], bid; | ||||
struct amdtemp_softc *sc = device_get_softc(dev); | union reg_amd_thermtrip_status_desc reg_tts; | ||||
struct sysctl_ctx_list *sysctlctx; | union reg_amd_htc_desc reg_htc; | ||||
struct sysctl_oid *sysctlnode; | struct sysctl_ctx_list *ctx; | ||||
uint32_t cpuid, family, model; | struct sysctl_oid_list *child, *list; | ||||
u_int bid; | struct sysctl_oid *node, *sub_node; | ||||
int erratum319, unit; | char str[32]; | ||||
int erratum319 = 0; | |||||
erratum319 = 0; | sc->dev = dev; | ||||
/* Find number of cores per package. */ | |||||
sc->cpu_ncores = ((amd_feature2 & AMDID2_CMP) ? | |||||
((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); | |||||
/* | /* Detect supported therm interfaces. */ | ||||
* CPUID Register is available from Revision F. | do_cpuid(0x80000001, regs); | ||||
*/ | cpuid = pci_read_config(dev, AMD_REG_CPUID, 4); | ||||
cpuid = cpu_id; | if (cpuid != cpu_id) /* XXX: Ryzen fix. */ | ||||
cpuid = cpu_id; | |||||
model = CPUID_TO_MODEL(cpuid); | |||||
family = CPUID_TO_FAMILY(cpuid); | family = CPUID_TO_FAMILY(cpuid); | ||||
model = CPUID_TO_MODEL(cpuid); | |||||
if ((family != 0x0f || model >= 0x40) && family != 0x17) { | /* PCI reading specific. */ | ||||
cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4); | switch (family) { | ||||
family = CPUID_TO_FAMILY(cpuid); | case 0x15: | ||||
model = CPUID_TO_MODEL(cpuid); | if (model >= 0x60 && model <= 0x6f) | ||||
sc->pci_io = pci_io_smu_f15; /* Read via SMU. */ | |||||
break; | |||||
case 0x17: /* Read via SMN. */ | |||||
sc->pci_io = pci_io_smn_f17; | |||||
break; | |||||
} | } | ||||
/* Temperature sensor / Reported Temperature Control. */ | |||||
if (family > 0x0f && (amd_pminfo & AMDPM_TS) != 0) { | |||||
sc->flags |= AMDTEMP_F_RTC; | |||||
} | |||||
/* ThermalTrip. */ | |||||
switch (family) { | switch (family) { | ||||
case 0x0f: | case 0x0f: | ||||
/* | /* | ||||
Context not available. | |||||
* XXX According to Linux, CurTmp starts at -28C on | * XXX According to Linux, CurTmp starts at -28C on | ||||
* Socket AM2 Revision G processors, which is not | * Socket AM2 Revision G processors, which is not | ||||
* documented anywhere. | * documented anywhere. | ||||
* XXX check TjOffset and DiodeOffset for -49C / -28C | |||||
*/ | */ | ||||
if ((amd_pminfo & AMDPM_TTP) == 0) /* No TTP: THERMTRIP */ | |||||
break; | |||||
reg_tts.u32 = pci_read_config(dev, AMD_REG_THERMTRIP_STAT, 4); | |||||
if (reg_tts.bits.ThermtpEn == 0) | |||||
break; | |||||
if ((model == 0x04 && (cpuid & CPUID_STEPPING) == 0) || | |||||
(model == 0x05 && (cpuid & CPUID_STEPPING) <= 1)) | |||||
break; /* No ThermalTrip. */ | |||||
sc->flags |= AMDTEMP_F_TTS; | |||||
if (model >= 0x40) | if (model >= 0x40) | ||||
sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP; | sc->tts_flags |= AMDTEMP_TTS_F_CS_SWAP; | ||||
if (model >= 0x60 && model != 0xc1) { | if (model >= 0x60 && model != 0xc1) { | ||||
do_cpuid(0x80000001, regs); | bid = ((regs[1] >> 9) & 0x1f); | ||||
bid = (regs[1] >> 9) & 0x1f; | |||||
switch (model) { | switch (model) { | ||||
case 0x68: /* Socket S1g1 */ | case 0x68: /* Socket S1g1 */ | ||||
case 0x6c: | case 0x6c: | ||||
Context not available. | |||||
case 0x7c: | case 0x7c: | ||||
break; | break; | ||||
case 0x6b: /* Socket AM2 and ASB1 (2 cores) */ | case 0x6b: /* Socket AM2 and ASB1 (2 cores) */ | ||||
if (bid != 0x0b && bid != 0x0c) | if (bid != 0x0b && bid != 0x0c) { | ||||
sc->sc_flags |= | sc->tts_flags |= AMDTEMP_TTS_F_OFF28; | ||||
AMDTEMP_FLAG_ALT_OFFSET; | } | ||||
break; | break; | ||||
case 0x6f: /* Socket AM2 and ASB1 (1 core) */ | case 0x6f: /* Socket AM2 and ASB1 (1 core) */ | ||||
case 0x7f: | case 0x7f: | ||||
if (bid != 0x07 && bid != 0x09 && | if (bid != 0x07 && bid != 0x09 && | ||||
bid != 0x0c) | bid != 0x0c) { | ||||
sc->sc_flags |= | sc->tts_flags |= AMDTEMP_TTS_F_OFF28; | ||||
AMDTEMP_FLAG_ALT_OFFSET; | } | ||||
break; | break; | ||||
default: | default: | ||||
sc->sc_flags |= AMDTEMP_FLAG_ALT_OFFSET; | sc->tts_flags |= AMDTEMP_TTS_F_OFF28; | ||||
break; | |||||
} | } | ||||
sc->sc_flags |= AMDTEMP_FLAG_CT_10BIT; | sc->tts_flags |= AMDTEMP_TTS_F_CT_10BIT; | ||||
} | } | ||||
break; | |||||
default: | |||||
if ((amd_pminfo & AMDPM_TTP) == 0) /* No TTP: THERMTRIP */ | |||||
break; | |||||
sc->flags |= AMDTEMP_F_TTS; | |||||
if (amdtemp_tts_get_temp(sc, AMD_REG_THERMTRIP_STAT, 0, 0) == 2242 && | |||||
amdtemp_tts_get_temp(sc, AMD_REG_THERMTRIP_STAT, 0, 1) == 2242) | |||||
sc->tts_flags |= AMDTEMP_TTS_F_NO_SENS; | |||||
break; | |||||
} | |||||
/* | /* Hardware Thermal Control (HTC). */ | ||||
* There are two sensors per core. | if (amd_pminfo & AMDPM_TM) { | ||||
reg_htc.u32 = amdtemp_pci_read(sc, AMD_REG_HTC_CTRL); | |||||
if (reg_htc.bits.HtcEn) | |||||
sc->flags |= AMDTEMP_F_HTC; | |||||
} | |||||
/* Init sysctl interface. */ | |||||
ctx = device_get_sysctl_ctx(dev); | |||||
child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); | |||||
if (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 (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 ++) { | |||||
if (sc->tts_flags & AMDTEMP_TTS_F_NO_SENS) | |||||
break; | |||||
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 (sc->flags & AMDTEMP_F_HTC) { /* Hardware Thermal Control (HTC) */ | |||||
node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "htc", | |||||
CTLFLAG_RD, NULL, "Hardware Thermal Control (HTC)"); | |||||
amdtemp_sysctl_reg_add(sc, SYSCTL_CHILDREN(node), | |||||
amdtemp_htc_reg_bits); | |||||
} | |||||
/* Verbose staff. */ | |||||
if (bootverbose) { | |||||
/* CPUID Fn8000_0007_EDX Advanced Power Management Information | |||||
* 0 TS: Temperature sensor. | |||||
* 3 TTP: THERMTRIP. Value: Fuse[ThermTripEn]. | |||||
* 4 TM: hardware thermal control (HTC). Value: ~Fuse[HtcDis]. | |||||
*/ | */ | ||||
sc->sc_ntemps = 2; | device_printf(dev, "amdtemp_attach: %d:%d:%d\n", | ||||
pci_get_bus(dev), pci_get_slot(dev), | |||||
sc->sc_gettemp = amdtemp_gettemp0f; | pci_get_function(dev)); | ||||
break; | if (amd_pminfo & AMDPM_TS) { | ||||
case 0x10: | device_printf(dev, "CPU have TS: Temperature " | ||||
"sensor.\n"); | |||||
} | |||||
if (sc->flags & AMDTEMP_F_RTC) { | |||||
device_printf(dev, "Found: Reported " | |||||
"Temperature Control (RTC).\n"); | |||||
} | |||||
if (sc->flags & AMDTEMP_F_TTS) { | |||||
device_printf(dev, "Found: Thermaltrip Status " | |||||
"(TTS).\n"); | |||||
} | |||||
if (amd_pminfo & AMDPM_TM) { | |||||
device_printf(dev, "Found: Hardware Thermal " | |||||
"Control (HTC), state: %sabled.\n", | |||||
((sc->flags & AMDTEMP_F_HTC) ? "en" : "dis")); | |||||
} | |||||
} | |||||
if (family == 0x10) { | |||||
/* | /* | ||||
* Erratum 319 Inaccurate Temperature Measurement | * Erratum 319 Inaccurate Temperature Measurement | ||||
* | |||||
* http://support.amd.com/us/Processor_TechDocs/41322.pdf | * http://support.amd.com/us/Processor_TechDocs/41322.pdf | ||||
*/ | */ | ||||
do_cpuid(0x80000001, regs); | switch (((regs[1] >> 28) & 0x0f)) { | ||||
switch ((regs[1] >> 28) & 0xf) { | |||||
case 0: /* Socket F */ | case 0: /* Socket F */ | ||||
erratum319 = 1; | erratum319 = 1; | ||||
break; | break; | ||||
case 1: /* Socket AM2+ or AM3 */ | case 1: /* Socket AM2+ or AM3 */ | ||||
if ((pci_cfgregread(pci_get_bus(dev), | bid = pci_cfgregread(pci_get_bus(dev), | ||||
pci_get_slot(dev), 2, AMDTEMP_DRAM_CONF_HIGH, 2) & | pci_get_slot(dev), 2, | ||||
AMDTEMP_DRAM_MODE_DDR3) != 0 || model > 0x04 || | AMD_REG_DRAM_CONF_HIGH, 2); | ||||
if ((bid & AMD_REG_DRAM_MODE_DDR3) || | |||||
model > 0x04 || | |||||
(model == 0x04 && (cpuid & CPUID_STEPPING) >= 3)) | (model == 0x04 && (cpuid & CPUID_STEPPING) >= 3)) | ||||
break; | break; | ||||
/* XXX 00100F42h (RB-C2) exists in both formats. */ | /* XXX 00100F42h (RB-C2) exists in both formats. */ | ||||
Context not available. | |||||
erratum319 = 1; | erratum319 = 1; | ||||
break; | break; | ||||
} | } | ||||
/* FALLTHROUGH */ | if (erratum319) { | ||||
case 0x11: | device_printf(dev, "Erratum 319: temperature " | ||||
case 0x12: | "measurement may be inaccurate.\n"); | ||||
case 0x14: | |||||
case 0x15: | |||||
case 0x16: | |||||
/* | |||||
* There is only one sensor per package. | |||||
*/ | |||||
sc->sc_ntemps = 1; | |||||
sc->sc_gettemp = amdtemp_gettemp; | |||||
break; | |||||
case 0x17: | |||||
sc->sc_ntemps = 1; | |||||
sc->sc_gettemp = amdtemp_gettemp17h; | |||||
sc->sc_smn = device_find_child( | |||||
device_get_parent(dev), "amdsmn", -1); | |||||
if (sc->sc_smn == NULL) { | |||||
if (bootverbose) | |||||
device_printf(dev, "No SMN device found\n"); | |||||
return (ENXIO); | |||||
} | } | ||||
break; | |||||
} | } | ||||
/* Find number of cores per package. */ | |||||
sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ? | |||||
(cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1; | |||||
if (sc->sc_ncores > MAXCPU) | |||||
return (ENXIO); | |||||
if (erratum319) | |||||
device_printf(dev, | |||||
"Erratum 319: temperature measurement may be inaccurate\n"); | |||||
if (bootverbose) | |||||
device_printf(dev, "Found %d cores and %d sensors.\n", | |||||
sc->sc_ncores, | |||||
sc->sc_ntemps > 1 ? sc->sc_ntemps * sc->sc_ncores : 1); | |||||
/* | /* | ||||
* dev.amdtemp.N tree. | * 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. | |||||
*/ | */ | ||||
unit = device_get_unit(dev); | amdtemp_intrhook(sc); | ||||
snprintf(tn, sizeof(tn), "dev.amdtemp.%d.sensor_offset", unit); | if (sc->sysctl_cpu[0] == NULL) { | ||||
TUNABLE_INT_FETCH(tn, &sc->sc_offset); | sc->sc_ich.ich_func = amdtemp_intrhook; | ||||
sc->sc_ich.ich_arg = sc; | |||||
if (config_intrhook_establish(&sc->sc_ich)) { | |||||
amdtemp_detach(dev); | |||||
device_printf(dev, "config_intrhook_establish " | |||||
"failed!\n"); | |||||
return (ENXIO); | |||||
} | |||||
} | |||||
sysctlctx = device_get_sysctl_ctx(dev); | return (0); | ||||
SYSCTL_ADD_INT(sysctlctx, | } | ||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, | |||||
"sensor_offset", CTLFLAG_RW, &sc->sc_offset, 0, | |||||
"Temperature sensor offset"); | |||||
sysctlnode = SYSCTL_ADD_NODE(sysctlctx, | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, | |||||
"core0", CTLFLAG_RD, 0, "Core 0"); | |||||
SYSCTL_ADD_PROC(sysctlctx, | int | ||||
SYSCTL_CHILDREN(sysctlnode), | amdtemp_detach(device_t dev) | ||||
OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD, | { | ||||
dev, CORE0_SENSOR0, amdtemp_sysctl, "IK", | amdtemp_softc_p sc = device_get_softc(dev); | ||||
"Core 0 / Sensor 0 temperature"); | uint32_t i; | ||||
if (sc->sc_ntemps > 1) { | for (i = 0; i < sc->cpu_ncores; i ++) { | ||||
SYSCTL_ADD_PROC(sysctlctx, | if (sc->sysctl_cpu[i]) { | ||||
SYSCTL_CHILDREN(sysctlnode), | sysctl_remove_oid(sc->sysctl_cpu[i], 1, 0); | ||||
OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD, | |||||
dev, CORE0_SENSOR1, amdtemp_sysctl, "IK", | |||||
"Core 0 / Sensor 1 temperature"); | |||||
if (sc->sc_ncores > 1) { | |||||
sysctlnode = SYSCTL_ADD_NODE(sysctlctx, | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | |||||
OID_AUTO, "core1", CTLFLAG_RD, 0, "Core 1"); | |||||
SYSCTL_ADD_PROC(sysctlctx, | |||||
SYSCTL_CHILDREN(sysctlnode), | |||||
OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD, | |||||
dev, CORE1_SENSOR0, amdtemp_sysctl, "IK", | |||||
"Core 1 / Sensor 0 temperature"); | |||||
SYSCTL_ADD_PROC(sysctlctx, | |||||
SYSCTL_CHILDREN(sysctlnode), | |||||
OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD, | |||||
dev, CORE1_SENSOR1, amdtemp_sysctl, "IK", | |||||
"Core 1 / Sensor 1 temperature"); | |||||
} | } | ||||
} | } | ||||
/* NewBus removes the dev.amdtemp.N tree by itself. */ | |||||
/* | if (sc->sc_ich.ich_arg) { | ||||
* Try to create dev.cpu sysctl entries and setup intrhook function. | sc->sc_ich.ich_arg = NULL; | ||||
* This is needed because the cpu driver may be loaded late on boot, | config_intrhook_disestablish(&sc->sc_ich); | ||||
* after us. | |||||
*/ | |||||
amdtemp_intrhook(dev); | |||||
sc->sc_ich.ich_func = amdtemp_intrhook; | |||||
sc->sc_ich.ich_arg = dev; | |||||
if (config_intrhook_establish(&sc->sc_ich) != 0) { | |||||
device_printf(dev, "config_intrhook_establish failed!\n"); | |||||
return (ENXIO); | |||||
} | } | ||||
mtx_destroy(&sc->lock); | |||||
return (0); | return (0); | ||||
} | } | ||||
Context not available. | |||||
void | void | ||||
amdtemp_intrhook(void *arg) | amdtemp_intrhook(void *arg) | ||||
{ | { | ||||
struct amdtemp_softc *sc; | amdtemp_softc_p sc = arg; | ||||
struct sysctl_ctx_list *sysctlctx; | device_t dev = sc->dev, acpi, cpu, nexus; | ||||
device_t dev = (device_t)arg; | int (*sysctl_handler)(SYSCTL_HANDLER_ARGS); | ||||
device_t acpi, cpu, nexus; | intptr_t sysctl_arg2; | ||||
amdsensor_t sensor; | uint32_t i, unit_base; | ||||
int i; | |||||
sc = device_get_softc(dev); | if (sc->sc_ich.ich_arg) { | ||||
sc->sc_ich.ich_arg = NULL; | |||||
config_intrhook_disestablish(&sc->sc_ich); | |||||
} | |||||
/* | /* dev.cpu.N.temperature. */ | ||||
* dev.cpu.N.temperature. | |||||
*/ | |||||
nexus = device_find_child(root_bus, "nexus", 0); | nexus = device_find_child(root_bus, "nexus", 0); | ||||
acpi = device_find_child(nexus, "acpi", 0); | acpi = device_find_child(nexus, "acpi", 0); | ||||
/* XXX: cpu_ncores not constant for different CPUs... */ | |||||
unit_base = (device_get_unit(dev) * sc->cpu_ncores); | |||||
for (i = 0; i < sc->sc_ncores; i++) { | for (i = 0; i < sc->cpu_ncores; i ++) { | ||||
if (sc->sc_sysctl_cpu[i] != NULL) | if (sc->sysctl_cpu[i]) | ||||
continue; | continue; | ||||
cpu = device_find_child(acpi, "cpu", | cpu = device_find_child(acpi, "cpu", (unit_base + i)); | ||||
device_get_unit(dev) * sc->sc_ncores + i); | if (cpu == NULL) | ||||
if (cpu != NULL) { | continue; | ||||
sysctlctx = device_get_sysctl_ctx(cpu); | sysctl_handler = NULL; | ||||
if (sc->flags & AMDTEMP_F_RTC) { | |||||
sensor = sc->sc_ntemps > 1 ? | /* Reported Temperature Control */ | ||||
(i == 0 ? CORE0 : CORE1) : CORE0_SENSOR0; | sysctl_handler = amdtemp_rtc_temp_sysctl; | ||||
sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx, | sysctl_arg2 = MAKE_ARG2(AMD_REG_REPTMP_CTRL, 21, 11); | ||||
SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), | } else if ((sc->flags & AMDTEMP_F_TTS) && | ||||
OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD, | (sc->tts_flags & AMDTEMP_TTS_F_NO_SENS) == 0) { | ||||
dev, sensor, amdtemp_sysctl, "IK", | /* Thermaltrip Status */ | ||||
"Current temparature"); | sysctl_handler = amdtemp_tts_temp_reg_sysctl; | ||||
sysctl_arg2 = MAKE_ARG2(AMD_REG_THERMTRIP_STAT, i, 0xff); | |||||
} | } | ||||
if (sysctl_handler == NULL) | |||||
continue; | |||||
sc->sysctl_cpu[i] = SYSCTL_ADD_PROC( | |||||
device_get_sysctl_ctx(cpu), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), | |||||
OID_AUTO, "temperature", (CTLTYPE_INT | CTLFLAG_RD), | |||||
sc, sysctl_arg2, sysctl_handler, "IK", | |||||
"Current temperature"); | |||||
} | } | ||||
if (sc->sc_ich.ich_arg != NULL) | |||||
config_intrhook_disestablish(&sc->sc_ich); | |||||
} | } | ||||
int | |||||
amdtemp_detach(device_t dev) | /* Sysctl staff. */ | ||||
static void | |||||
amdtemp_sysctl_reg_add(amdtemp_softc_p sc, | |||||
struct sysctl_oid_list *child, sysctl_reg_p regs) | |||||
{ | { | ||||
struct amdtemp_softc *sc = device_get_softc(dev); | struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev); | ||||
int i; | uint32_t i; | ||||
for (i = 0; i < sc->sc_ncores; i++) | for (i = 0; regs[i].oid_handler; i ++) { | ||||
if (sc->sc_sysctl_cpu[i] != NULL) | AMDTEMP_SYSCTL_ADD_PROC(ctx, child, OID_AUTO, | ||||
sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0); | 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); | |||||
} | |||||
} | |||||
/* NewBus removes the dev.amdtemp.N tree by itself. */ | static int | ||||
amdtemp_sysctl_reg_bits(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
amdtemp_softc_p 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 = amdtemp_pci_read(sc, reg_num); | |||||
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 (error || req->newptr == NULL || 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. */ | |||||
amdtemp_pci_write(sc, reg_num, reg_data); | |||||
return (0); | return (0); | ||||
} | } | ||||
/* Thermaltrip Status Register */ | |||||
static uint32_t | |||||
amdtemp_tts_get_temp(amdtemp_softc_p 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 ((sc->tts_flags & AMDTEMP_TTS_F_CS_SWAP) == 0) { | |||||
reg_tts.bits.ThermSenseCoreSel = (core ? 1 : 0); | |||||
} else { /* Swap. */ | |||||
reg_tts.bits.ThermSenseCoreSel = (core ? 0 : 1); | |||||
} | |||||
reg_tts.bits.ThermSenseSel = (sense ? 1 : 0); | |||||
/* XXX: probably lock required. */ | |||||
amdtemp_pci_write(sc, reg, reg_tts.u32); | |||||
reg_tts.u32 = amdtemp_pci_read(sc, reg); | |||||
val = reg_tts.bits.CurTmp; | |||||
if ((sc->tts_flags & AMDTEMP_TTS_F_CT_10BIT) == 0) { | |||||
val &= ~0x00000003; /* Clear first 2 bits. */ | |||||
} | |||||
val = (AMDTEMP_ZERO_C_TO_K + ((val * 5) / 2) - | |||||
((sc->tts_flags & AMDTEMP_TTS_F_OFF28) ? 280 : 490)); | |||||
val += (sc->tts_temp_offset[((2 * reg_tts.bits.ThermSenseCoreSel) + | |||||
reg_tts.bits.ThermSenseSel)] * 10); | |||||
return (val); | |||||
} | |||||
/* If 0xff == ARG2_GET_A2(arg2) then retun max temp for core. */ | |||||
static int | static int | ||||
amdtemp_sysctl(SYSCTL_HANDLER_ARGS) | amdtemp_tts_temp_reg_sysctl(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
device_t dev = (device_t)arg1; | amdtemp_softc_p sc = arg1; | ||||
struct amdtemp_softc *sc = device_get_softc(dev); | uint32_t reg_num; | ||||
amdsensor_t sensor = (amdsensor_t)arg2; | unsigned val; | ||||
int32_t auxtemp[2], temp; | |||||
int error; | int error; | ||||
switch (sensor) { | reg_num = ARG2_GET_REG(arg2); | ||||
case CORE0: | if (ARG2_GET_A2(arg2) == 0xff) { /* Core temp max. */ | ||||
auxtemp[0] = sc->sc_gettemp(dev, CORE0_SENSOR0); | val = imax(amdtemp_tts_get_temp(sc, reg_num, ARG2_GET_A1(arg2), 0), | ||||
auxtemp[1] = sc->sc_gettemp(dev, CORE0_SENSOR1); | amdtemp_tts_get_temp(sc, reg_num, ARG2_GET_A1(arg2), 1)); | ||||
temp = imax(auxtemp[0], auxtemp[1]); | } else { | ||||
break; | val = amdtemp_tts_get_temp(sc, reg_num, ARG2_GET_A1(arg2), | ||||
case CORE1: | ARG2_GET_A2(arg2)); | ||||
auxtemp[0] = sc->sc_gettemp(dev, CORE1_SENSOR0); | |||||
auxtemp[1] = sc->sc_gettemp(dev, CORE1_SENSOR1); | |||||
temp = imax(auxtemp[0], auxtemp[1]); | |||||
break; | |||||
default: | |||||
temp = sc->sc_gettemp(dev, sensor); | |||||
break; | |||||
} | } | ||||
error = sysctl_handle_int(oidp, &temp, 0, req); | error = sysctl_handle_int(oidp, &val, 0, req); | ||||
if (error || req->newptr == NULL) | |||||
return (error); | |||||
return (error); | return (0); | ||||
} | } | ||||
#define AMDTEMP_ZERO_C_TO_K 2731 | |||||
static int32_t | /* x64 Hardware Thermal Control (HTC) */ | ||||
amdtemp_gettemp0f(device_t dev, amdsensor_t sensor) | static int | ||||
amdtemp_htc_temp_sysctl(SYSCTL_HANDLER_ARGS) | |||||
{ | { | ||||
struct amdtemp_softc *sc = device_get_softc(dev); | amdtemp_softc_p sc = arg1; | ||||
uint32_t mask, offset, temp; | union reg_amd_htc_desc reg_htc; | ||||
uint32_t reg_num, bits_off; | |||||
unsigned val; | |||||
int error; | |||||
/* Set Sensor/Core selector. */ | reg_num = ARG2_GET_REG(arg2); | ||||
temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1); | bits_off = ARG2_GET_A1(arg2); | ||||
temp &= ~(AMDTEMP_TTSR_SELCORE | AMDTEMP_TTSR_SELSENSOR); | |||||
switch (sensor) { | reg_htc.u32 = amdtemp_pci_read(sc, reg_num); | ||||
case CORE0_SENSOR1: | switch (bits_off) { | ||||
temp |= AMDTEMP_TTSR_SELSENSOR; | case 16: /* HtcTmpLmt */ | ||||
/* FALLTHROUGH */ | val = (((reg_htc.bits.HtcTmpLmt * 10) / 2) + 520); | ||||
case CORE0_SENSOR0: | |||||
case CORE0: | |||||
if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0) | |||||
temp |= AMDTEMP_TTSR_SELCORE; | |||||
break; | break; | ||||
case CORE1_SENSOR1: | case 24: /* HtcHystLmt */ | ||||
temp |= AMDTEMP_TTSR_SELSENSOR; | val = ((reg_htc.bits.HtcHystLmt * 10) / 2); | ||||
/* FALLTHROUGH */ | |||||
case CORE1_SENSOR0: | |||||
case CORE1: | |||||
if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0) | |||||
temp |= AMDTEMP_TTSR_SELCORE; | |||||
break; | break; | ||||
} | } | ||||
pci_write_config(dev, AMDTEMP_THERMTP_STAT, temp, 1); | val += AMDTEMP_ZERO_C_TO_K; | ||||
error = sysctl_handle_int(oidp, &val, 0, req); | |||||
if (error || req->newptr == NULL) | |||||
return (error); | |||||
/* amdtemp_pci_write(sc, reg_num, reg_htc.u32); */ | |||||
mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc; | return (0); | ||||
offset = (sc->sc_flags & AMDTEMP_FLAG_ALT_OFFSET) != 0 ? 28 : 49; | } | ||||
temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4); | |||||
temp = ((temp >> 14) & mask) * 5 / 2; | |||||
temp += AMDTEMP_ZERO_C_TO_K + (sc->sc_offset - offset) * 10; | |||||
return (temp); | |||||
/* xA4 Reported Temperature Control Register */ | |||||
static int | |||||
amdtemp_rtc_temp_sysctl(SYSCTL_HANDLER_ARGS) | |||||
{ | |||||
amdtemp_softc_p sc = arg1; | |||||
union reg_amd_rep_tmp_ctrl_desc reg_rtc; | |||||
uint32_t reg_num, bits_off; | |||||
unsigned val; | |||||
int error; | |||||
reg_num = ARG2_GET_REG(arg2); | |||||
bits_off = ARG2_GET_A1(arg2); | |||||
reg_rtc.u32 = amdtemp_pci_read(sc, reg_num); | |||||
val = (AMDTEMP_ZERO_C_TO_K + ((reg_rtc.bits.CurTmp * 10) / 8)); | |||||
if (3 == reg_rtc.bits.CurTmpTjSel) { /* RangeUnajusted */ | |||||
val -= 490; | |||||
} | |||||
val += (sc->rtc_temp_offset * 10); | |||||
error = sysctl_handle_int(oidp, &val, 0, req); | |||||
if (error || req->newptr == NULL) | |||||
return (error); | |||||
/* amdtemp_pci_write(sc, reg_num, reg_rtc.u32); */ | |||||
return (0); | |||||
} | } | ||||
static int32_t | |||||
amdtemp_gettemp(device_t dev, amdsensor_t sensor) | static uint32_t | ||||
amdtemp_pci_read(amdtemp_softc_p sc, uint32_t addr) | |||||
{ | { | ||||
struct amdtemp_softc *sc = device_get_softc(dev); | uint32_t ret, bus; | ||||
uint32_t temp; | device_t dev; | ||||
temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4); | if (sc->pci_io.addr == 0) | ||||
temp = ((temp >> 21) & 0x7ff) * 5 / 4; | return (pci_read_config(sc->dev, addr, 4)); | ||||
temp += AMDTEMP_ZERO_C_TO_K + sc->sc_offset * 10; | |||||
return (temp); | bus = sc->pci_io.bus; | ||||
if (bus == 0xffffffff) | |||||
bus = pci_get_bus(sc->dev); | |||||
dev = pci_find_bsf(bus, sc->pci_io.slot, sc->pci_io.func); | |||||
if (dev == NULL) { | |||||
device_printf(sc->dev, "Couldn't find NB PCI device\n"); | |||||
return (0); | |||||
} | |||||
AMDTEMP_LOCK(sc); | |||||
pci_write_config(dev, sc->pci_io.addr, | |||||
(sc->pci_io.addr_offset + addr), 4); | |||||
ret = pci_read_config(dev, sc->pci_io.data, 4); | |||||
AMDTEMP_UNLOCK(sc); | |||||
return (ret); | |||||
} | } | ||||
static int32_t | static void | ||||
amdtemp_gettemp17h(device_t dev, amdsensor_t sensor) | amdtemp_pci_write(amdtemp_softc_p sc, uint32_t addr, uint32_t data) | ||||
{ | { | ||||
struct amdtemp_softc *sc = device_get_softc(dev); | uint32_t bus; | ||||
uint32_t temp, val; | device_t dev; | ||||
int error; | |||||
error = amdsmn_read(sc->sc_smn, AMDTEMP_17H_CUR_TMP, &val); | if (sc->pci_io.addr == 0) { | ||||
KASSERT(error == 0, ("amdsmn_read")); | pci_write_config(sc->dev, addr, data, 4); | ||||
return; | |||||
} | |||||
temp = ((val >> 21) & 0x7ff) * 5 / 4; | bus = sc->pci_io.bus; | ||||
if ((val & AMDTEMP_17H_CUR_TMP_RANGE_SEL) != 0) | if (bus == 0xffffffff) | ||||
temp -= AMDTEMP_17H_CUR_TMP_RANGE_OFF; | bus = pci_get_bus(sc->dev); | ||||
temp += AMDTEMP_ZERO_C_TO_K + sc->sc_offset * 10; | dev = pci_find_bsf(bus, sc->pci_io.slot, sc->pci_io.func); | ||||
if (dev == NULL) { | |||||
device_printf(sc->dev, "Couldn't find NB PCI device\n"); | |||||
return; | |||||
} | |||||
return (temp); | AMDTEMP_LOCK(sc); | ||||
pci_write_config(dev, sc->pci_io.addr, | |||||
(sc->pci_io.addr_offset + addr), 4); | |||||
pci_write_config(dev, sc->pci_io.data, data, 4); | |||||
AMDTEMP_UNLOCK(sc); | |||||
} | } | ||||
Context not available. |
this change looks like it is inadvertent