Index: sys/dev/amdtemp/amdtemp.c =================================================================== --- sys/dev/amdtemp/amdtemp.c +++ sys/dev/amdtemp/amdtemp.c @@ -1,7 +1,8 @@ /*- * Copyright (c) 2008, 2009 Rui Paulo * Copyright (c) 2009 Norikatsu Shigemura - * Copyright (c) 2009-2012 Jung-uk Kim + * Copyright (c) 2009 - 2012 Jung-uk Kim + * Copyright (c) 2013 - 2017 Rozhuk Ivan * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,107 +36,630 @@ __FBSDID("$FreeBSD$"); #include -#include -#include +#include #include #include +#include +#include +#include #include -#include +#include +#include -#include +#include +#include #include #include +#include +#include #include -#include +#include -typedef enum { - CORE0_SENSOR0, - CORE0_SENSOR1, - CORE1_SENSOR0, - CORE1_SENSOR1, - CORE0, - CORE1 -} amdsensor_t; -struct amdtemp_softc { - device_t sc_dev; - int sc_ncores; - int sc_ntemps; - int sc_flags; -#define AMDTEMP_FLAG_CS_SWAP 0x01 /* ThermSenseCoreSel is inverted. */ -#define AMDTEMP_FLAG_CT_10BIT 0x02 /* CurTmp is 10-bit wide. */ -#define AMDTEMP_FLAG_ALT_OFFSET 0x04 /* CurTmp starts at -28C. */ - int32_t sc_offset; - int32_t (*sc_gettemp)(device_t, amdsensor_t); - struct sysctl_oid *sc_sysctl_cpu[MAXCPU]; - struct intr_config_hook sc_ich; +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_HTC 2 /* Hardware Thermal Control (HTC). */ +#define AMDTEMP_F_RTC 4 /* Reported Temperature Control. */ +#define AMDTEMP_F_TSI 8 /* TSI via CPU registers. */ +#define AMDTEMP_F_SBTSI 16 /* TSI via SMBus. */ -#define VENDORID_AMD 0x1022 -#define DEVICEID_AMD_MISC0F 0x1103 -#define DEVICEID_AMD_MISC10 0x1203 -#define DEVICEID_AMD_MISC11 0x1303 -#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 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. */ -static struct amdtemp_product { - uint16_t amdtemp_vendorid; - uint16_t amdtemp_deviceid; -} amdtemp_products[] = { - { VENDORID_AMD, DEVICEID_AMD_MISC0F }, - { VENDORID_AMD, DEVICEID_AMD_MISC10 }, - { VENDORID_AMD, DEVICEID_AMD_MISC11 }, - { VENDORID_AMD, DEVICEID_AMD_MISC12 }, - { VENDORID_AMD, DEVICEID_AMD_MISC14 }, - { VENDORID_AMD, DEVICEID_AMD_MISC15 }, - { VENDORID_AMD, DEVICEID_AMD_MISC16 }, - { VENDORID_AMD, DEVICEID_AMD_MISC16_M30H }, - { VENDORID_AMD, DEVICEID_AMD_MISC17 }, - { 0, 0 } -}; -/* - * Reported Temperature Control Register - */ -#define AMDTEMP_REPTMP_CTRL 0xa4 +#define AMDTEMP_LOCK(__sc) mtx_lock(&(__sc)->lock) +#define AMDTEMP_UNLOCK(__sc) mtx_unlock(&(__sc)->lock) -/* - * Thermaltrip Status Register (Family 0Fh only) + +/* D18F3xFC CPUID Family/Model/Stepping */ +#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 */ -#define AMDTEMP_THERMTP_STAT 0xe4 -#define AMDTEMP_TTSR_SELCORE 0x04 -#define AMDTEMP_TTSR_SELSENSOR 0x40 +/* 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 + /* - * DRAM Configuration High Register + * The default value of the HTC temperature threshold (Tctl_max) is specified + * in the AMD Family 14h Processor Power and Thermal Datasheet. */ -#define AMDTEMP_DRAM_CONF_HIGH 0x94 /* Function 2 */ -#define AMDTEMP_DRAM_MODE_DDR3 0x0100 +/* D18F3x64 Hardware Thermal Control (HTC) */ +#define AMD_REG_HTC_CTRL 0x64 +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. */ + } __packed bits; +}; + +/* 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 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. */ + } __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; +}; /* - * CPU Family/Model Register + * 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 AMDTEMP_CPUID 0xfc +#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_htc_temp_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", + __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, + "DiodeOffset", + __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, + "ThermtpEn", + __DESCR("The THERMTRIP state is supported by the " + "processor.") + }, { + 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 struct amdtemp_sysctl_reg amdtemp_htc_reg_bits[] = +{ + { + AMD_REG_HTC_CTRL, + 16, + 7, + (CTLFLAG_RD | CTLTYPE_INT), + "IK", + amdtemp_htc_temp_sysctl, + "HtcTmpLmt", + __DESCR("HTC temperature limit") + }, { + AMD_REG_HTC_CTRL, + 24, + 4, + (CTLFLAG_RW | CTLTYPE_INT), + "IK", + amdtemp_htc_temp_sysctl, + "HtcHystLmt", + __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, + "HtcEn", + __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, + "HtcLock", + __DESCR("HtcPstateLimit, HtcHystLmt, HtcTmpLmt, and " + "HtcEn are read-only.") + }, { + AMD_REG_HTC_CTRL, + 23, + 1, + (CTLFLAG_RW | CTLTYPE_UINT), + "IU", + amdtemp_sysctl_reg_bits, + "HtcSlewSel", + __DESCR("HTC slew-controlled temperature select.") + }, { + AMD_REG_HTC_CTRL, + 28, + 3, + (CTLFLAG_RW | CTLTYPE_UINT), + "IU", + amdtemp_sysctl_reg_bits, + "HtcPstateLimit", + __DESCR("HTC P-state limit select.") + }, { + AMD_REG_HTC_CTRL, + 4, + 1, + (CTLFLAG_RW | CTLTYPE_UINT), + "IU", + amdtemp_sysctl_reg_bits, + "HtcAct", + __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, + "HtcActSts", + __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, + "PslApicHiEn", + __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, + "PslApicLoEn", + __DESCR("P-state limit lower value change APIC " + "interrupt enable.") + }, + { 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", + __DESCR("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", + __DESCR("Specifies a value used to create Tctl.") + }, + /*{ + AMD_REG_REPTMP_CTRL, + 18, + 1, + (CTLFLAG_RW | CTLTYPE_UINT), + "IU", + amdtemp_sysctl_reg_bits, + "CurTmpTjSlewSel", + __DESCR("") + }, { + AMD_REG_REPTMP_CTRL, + 19, + 1, + (CTLFLAG_RW | CTLTYPE_UINT), + "IU", + amdtemp_sysctl_reg_bits, + "CurTmpRangeSel", + __DESCR("") + },*/ + { + AMD_REG_REPTMP_CTRL, + 7, + 1, + (CTLFLAG_RW | CTLTYPE_UINT), + "IU", + amdtemp_sysctl_reg_bits, + "TmpSlewDnEn", + __DESCR("Temperature slew downward enable.") + }, { + AMD_REG_REPTMP_CTRL, + 5, + 2, + (CTLFLAG_RW | CTLTYPE_UINT), + "IU", + amdtemp_sysctl_reg_bits, + "TmpMaxDiffUp", + __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, + "PerStepTimeDn", + __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, + "PerStepTimeUp", + __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 } +}; + +/* 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", + __DESCR("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", + __DESCR("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", + __DESCR("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", + __DESCR("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", + __DESCR("CPU Temperature Offset Low Byte") + }, + + { + SB_TSI_REG_STATUS, + 0, + 0, + (CTLFLAG_RW | CTLTYPE_UINT), + "IU", + amdtemp_tsi_reg_sysctl, + "status", + __DESCR("SB-TSI Status") + }, { + SB_TSI_REG_CFG, + 0, + 0, + (CTLFLAG_RW | CTLTYPE_UINT), + "IU", + amdtemp_tsi_reg_sysctl, + "cfg3", + __DESCR("SB-TSI Configuration Register 0x03") + }, { + SB_TSI_REG_CFG2, + 0, + 0, + (CTLFLAG_RW | CTLTYPE_UINT), + "IU", + amdtemp_tsi_reg_sysctl, + "cfg9", + __DESCR("SB-TSI Configuration Register 0x09") + }, { + SB_TSI_REG_UPD_RATE, + 0, + 0, + (CTLFLAG_RW | CTLTYPE_UINT), + "IU", + amdtemp_tsi_reg_sysctl, + "upd_rate", + __DESCR("Update Rate") + }, { + SB_TSI_REG_TIMEOUT_CFG, + 0, + 0, + (CTLFLAG_RW | CTLTYPE_UINT), + "IU", + amdtemp_tsi_reg_sysctl, + "timeout_cfg", + __DESCR("Timeout Configuration") + }, { + SB_TSI_REG_ALERT_THRESHOLD, + 0, + 0, + (CTLFLAG_RW | CTLTYPE_UINT), + "IU", + amdtemp_tsi_reg_sysctl, + "alert_threshold", + __DESCR("Alert Threshold") + }, { + SB_TSI_REG_ALERT_CFG, + 0, + 0, + (CTLFLAG_RW | CTLTYPE_UINT), + "IU", + amdtemp_tsi_reg_sysctl, + "alert_cfg", + __DESCR("Alert Configuration") + }, { + SB_TSI_REG_MANUFACTURE_ID, + 0, + 0, + (CTLFLAG_RD | CTLTYPE_UINT), + "IU", + amdtemp_tsi_reg_sysctl, + "manufacture_id", + __DESCR("Manufacture ID") + }, { + SB_TSI_REG_REVISION, + 0, + 0, + (CTLFLAG_RD | CTLTYPE_UINT), + "IU", + amdtemp_tsi_reg_sysctl, + "revision", + __DESCR("SB-TSI Revision") + }, + + { 0, 0, 0, 0, NULL, NULL, NULL, NULL } +}; + + /* * 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_attach(device_t dev); +static int amdtemp_detach(device_t dev); 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 int amdtemp_sysctl(SYSCTL_HANDLER_ARGS); -static device_method_t amdtemp_methods[] = { + + +static device_method_t amdtemp_methods[] = +{ /* Device interface */ DEVMETHOD(device_identify, amdtemp_identify), DEVMETHOD(device_probe, amdtemp_probe), @@ -145,30 +669,42 @@ DEVMETHOD_END }; -static driver_t amdtemp_driver = { +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 int -amdtemp_match(device_t dev) +amdtemp_dev_check(device_t dev) { - int i; - uint16_t vendor, devid; + uint32_t cpuid; - vendor = pci_get_vendor(dev); - devid = pci_get_device(dev); + if (resource_disabled("amdtemp", 0)) + return (ENXIO); + /* + * Device 18h Function 3 Configuration Registers: + * vendor = AMD (0x1022) + * class = bridge (0x06000000) + * function = 3 + */ + if (pci_get_vendor(dev) != CPU_VENDOR_AMD || + pci_get_class(dev) != PCIC_BRIDGE || + pci_get_function(dev) != 3) + return (ENXIO); + /* Is 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_TO_FAMILY(cpuid) < 0x0f) + return (ENXIO); - for (i = 0; amdtemp_products[i].amdtemp_vendorid != 0; i++) { - if (vendor == amdtemp_products[i].amdtemp_vendorid && - devid == amdtemp_products[i].amdtemp_deviceid) - return (1); - } - return (0); } @@ -180,11 +716,11 @@ /* Make sure we're not being doubly invoked. */ if (device_find_child(parent, "amdtemp", -1) != NULL) return; - - if (amdtemp_match(parent)) { - child = device_add_child(parent, "amdtemp", -1); - if (child == NULL) - device_printf(parent, "add amdtemp child failed\n"); + if (amdtemp_dev_check(parent) != 0) + return; + child = device_add_child(parent, "amdtemp", -1); + if (child == NULL) { + device_printf(parent, "add amdtemp child failed.\n"); } } @@ -191,30 +727,9 @@ static int amdtemp_probe(device_t dev) { - uint32_t family, model; - if (resource_disabled("amdtemp", 0)) + if (amdtemp_dev_check(dev) != 0) 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: - break; - default: - return (ENXIO); - } device_set_desc(dev, "AMD CPU On-Die Thermal Sensors"); return (BUS_PROBE_GENERIC); @@ -223,30 +738,30 @@ static int amdtemp_attach(device_t dev) { - char tn[32]; - u_int regs[4]; struct amdtemp_softc *sc = device_get_softc(dev); - struct sysctl_ctx_list *sysctlctx; - struct sysctl_oid *sysctlnode; - uint32_t cpuid, family, model; - u_int bid; - int erratum319, unit; + uint32_t i, cpuid, model; + union reg_amd_thermtrip_status_desc reg_tts; + union reg_amd_htc_desc reg_htc; + union reg_amd_sbi_ctrl_desc reg_sbi; + struct sysctl_ctx_list *ctx; + struct sysctl_oid_list *child, *list; + struct sysctl_oid *node, *sub_node; + char str[32]; + int tsi_ok, erratum319 = 0; + u_int regs[4], bid; - erratum319 = 0; + 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 Register is available from Revision F. - */ - cpuid = cpu_id; - family = CPUID_TO_FAMILY(cpuid); + /* Detect supported therm interfaces. */ + cpuid = pci_read_config(dev, AMD_REG_CPUID, 4); model = CPUID_TO_MODEL(cpuid); - if (family != 0x0f || model >= 0x40) { - cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4); - family = CPUID_TO_FAMILY(cpuid); - model = CPUID_TO_MODEL(cpuid); - } - - switch (family) { + switch (CPUID_TO_FAMILY(cpuid)) { case 0x0f: /* * Thermaltrip Status Register @@ -272,12 +787,23 @@ * 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 >= 0x40) - sc->sc_flags |= AMDTEMP_FLAG_CS_SWAP; + 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) { + sc->tts_flags |= AMDTEMP_TTS_F_CS_SWAP; + } if (model >= 0x60 && model != 0xc1) { do_cpuid(0x80000001, regs); - bid = (regs[1] >> 9) & 0x1f; + bid = ((regs[1] >> 9) & 0x1f); switch (model) { case 0x68: /* Socket S1g1 */ case 0x6c: @@ -284,45 +810,45 @@ case 0x7c: break; case 0x6b: /* Socket AM2 and ASB1 (2 cores) */ - if (bid != 0x0b && bid != 0x0c) - sc->sc_flags |= - AMDTEMP_FLAG_ALT_OFFSET; + 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->sc_flags |= - AMDTEMP_FLAG_ALT_OFFSET; + if (bid != 0x07 && + bid != 0x09 && + bid != 0x0c) { + sc->tts_flags |= AMDTEMP_TTS_F_OFF28; + } break; 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; } - - /* - * There are two sensors per core. - */ - sc->sc_ntemps = 2; - - sc->sc_gettemp = amdtemp_gettemp0f; break; case 0x10: + sc->flags |= AMDTEMP_F_RTC; + reg_sbi.u32 = pci_read_config(dev, AMD_REG_SBI_CTRL, 4); + if (reg_sbi.bits.SbTsiDis == 0) { + 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) { + switch (((regs[1] >> 28) & 0x0f)) { 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, AMDTEMP_DRAM_CONF_HIGH, 2) & - AMDTEMP_DRAM_MODE_DDR3) != 0 || model > 0x04 || + 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. */ @@ -329,231 +855,538 @@ erratum319 = 1; break; } - /* FALLTHROUGH */ + break; case 0x11: case 0x12: case 0x14: case 0x15: case 0x16: - /* - * There is only one sensor per package. - */ - sc->sc_ntemps = 1; - - sc->sc_gettemp = amdtemp_gettemp; + default: + sc->flags |= AMDTEMP_F_RTC; + reg_sbi.u32 = pci_read_config(dev, AMD_REG_SBI_CTRL, 4); + if (reg_sbi.bits.SbRmiDis == 0 || /* = SbTsiDis */ + reg_sbi.bits.SbTsiDis == 0) { + sc->flags |= AMDTEMP_F_TSI; + } + /* XXX TODO: read TSI via SMBus. */ break; } + /* Hardware Thermal Control (HTC). */ + if ((amd_pminfo & AMDPM_TM) != 0) { + reg_htc.u32 = pci_read_config(dev, AMD_REG_HTC_CTRL, 4); + if (reg_htc.bits.HtcEn != 0) { + sc->flags |= AMDTEMP_F_HTC; + } + } - /* 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. - */ - unit = device_get_unit(dev); - snprintf(tn, sizeof(tn), "dev.amdtemp.%d.sensor_offset", unit); - TUNABLE_INT_FETCH(tn, &sc->sc_offset); - - sysctlctx = device_get_sysctl_ctx(dev); - 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, - SYSCTL_CHILDREN(sysctlnode), - OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD, - dev, CORE0_SENSOR0, amdtemp_sysctl, "IK", - "Core 0 / Sensor 0 temperature"); - - if (sc->sc_ntemps > 1) { - SYSCTL_ADD_PROC(sysctlctx, - SYSCTL_CHILDREN(sysctlnode), - 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"); + /* Init sysctl interface. */ + ctx = device_get_sysctl_ctx(dev); + child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); + if ((sc->flags & AMDTEMP_F_TTS) != 0) { /* 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 ((sc->flags & AMDTEMP_F_HTC) != 0) { /* 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); + } + if ((sc->flags & AMDTEMP_F_RTC) != 0) { /* 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_TSI) != 0) { /* Temperature Sensor Interface */ + tsi_ok = 0; + for (i = 0; i < 8; i ++) { + if (amdtemp_sbi_read(sc, i, SB_TSI_REG_REVISION) == 0) + continue; + if (tsi_ok == 0) { /* First time add node. */ + node = SYSCTL_ADD_NODE(ctx, child, + OID_AUTO, "tsi", CTLFLAG_RD, NULL, + "Temperature Sensor Interface"); + list = SYSCTL_CHILDREN(node); + tsi_ok ++; + } + 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 (tsi_ok == 0) { /* Unset flag if no TSI sensors found. */ + sc->flags &= ~AMDTEMP_F_TSI; + } + } + + 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]. + */ + if ((amd_pminfo & AMDPM_TS) != 0) { + device_printf(dev, "CPU have TS: Temperature " + "sensor.\n"); + } + if ((sc->flags & AMDTEMP_F_TTS) != 0) { + device_printf(dev, "Found: Thermaltrip Status " + "(TTS).\n"); + } + if ((sc->flags & AMDTEMP_F_RTC) != 0) { + device_printf(dev, "Found: Reported " + "Temperature Control (RTC).\n"); + } + if ((sc->flags & AMDTEMP_F_TSI) != 0) { + device_printf(dev, "Found: Temperature Sensor " + "Interface via CPU registers (TSI).\n"); + } + if ((amd_pminfo & AMDPM_TM) != 0) { + device_printf(dev, "Found: Hardware Thermal " + "Control (HTC).\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); - 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); + amdtemp_intrhook(sc); + if (sc->sysctl_cpu[0] == NULL) { + 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; - struct sysctl_ctx_list *sysctlctx; - device_t dev = (device_t)arg; - device_t acpi, cpu, nexus; - amdsensor_t sensor; - int i; + struct amdtemp_softc *sc = arg; + device_t dev = sc->dev, acpi, cpu, nexus; + int (*sysctl_handler)(SYSCTL_HANDLER_ARGS); + intptr_t sysctl_arg2; + uint32_t i, unit_base; - sc = device_get_softc(dev); + if (sc->sc_ich.ich_arg != NULL) { + 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); 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++) { - if (sc->sc_sysctl_cpu[i] != NULL) + for (i = 0; i < sc->cpu_ncores; i ++) { + if (sc->sysctl_cpu[i] != NULL) continue; - cpu = device_find_child(acpi, "cpu", - device_get_unit(dev) * sc->sc_ncores + i); - if (cpu != NULL) { - sysctlctx = device_get_sysctl_ctx(cpu); - - sensor = sc->sc_ntemps > 1 ? - (i == 0 ? CORE0 : CORE1) : CORE0_SENSOR0; - sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx, - SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), - OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD, - dev, sensor, amdtemp_sysctl, "IK", - "Current temparature"); + cpu = device_find_child(acpi, "cpu", (unit_base + i)); + if (cpu == NULL) + continue; + sysctl_handler = NULL; + if ((sc->flags & AMDTEMP_F_TSI) != 0) { + /* 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 ((sc->flags & AMDTEMP_F_RTC) != 0) { + /* Reported Temperature Control */ + sysctl_handler = amdtemp_rtc_temp_sysctl; + sysctl_arg2 = MAKE_ARG2(AMD_REG_REPTMP_CTRL, 21, 11); + } else if ((sc->flags & AMDTEMP_F_TTS) != 0) { + /* Thermaltrip Status */ + 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(struct amdtemp_softc *sc, + struct sysctl_oid_list *child, struct amdtemp_sysctl_reg *regs) { - struct amdtemp_softc *sc = device_get_softc(dev); - int i; + struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev); + uint32_t i; - for (i = 0; i < sc->sc_ncores; i++) - if (sc->sc_sysctl_cpu[i] != NULL) - sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0); + for (i = 0; regs[i].oid_handler != NULL; i ++) { + sysctl_add_oid(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); + } +} - /* NewBus removes the dev.amdtemp.N tree by itself. */ +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; regs[i].oid_handler != NULL; i ++) { + sysctl_add_oid(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 (error != 0 || 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. */ + 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 ((sc->tts_flags & AMDTEMP_TTS_F_CS_SWAP) == 0) { + reg_tts.bits.ThermSenseCoreSel = ((core != 0) ? 1 : 0); + } else { /* Swap. */ + reg_tts.bits.ThermSenseCoreSel = ((core != 0) ? 0 : 1); + } + reg_tts.bits.ThermSenseSel = ((sense != 0) ? 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 ((sc->tts_flags & AMDTEMP_TTS_F_CT_10BIT) == 0) { + val &= ~3; /* Clear first 2 bits. */ + } + val = (AMDTEMP_ZERO_C_TO_K + ((val * 5) / 2) - + (((sc->tts_flags & AMDTEMP_TTS_F_OFF28) != 0) ? 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_sysctl(SYSCTL_HANDLER_ARGS) +amdtemp_tts_temp_reg_sysctl(SYSCTL_HANDLER_ARGS) { - device_t dev = (device_t)arg1; - struct amdtemp_softc *sc = device_get_softc(dev); - amdsensor_t sensor = (amdsensor_t)arg2; - int32_t auxtemp[2], temp; + struct amdtemp_softc *sc = arg1; + uint32_t reg_num; + unsigned val; int error; - switch (sensor) { - case CORE0: - auxtemp[0] = sc->sc_gettemp(dev, CORE0_SENSOR0); - auxtemp[1] = sc->sc_gettemp(dev, CORE0_SENSOR1); - temp = imax(auxtemp[0], auxtemp[1]); + reg_num = ARG2_GET_REG(arg2); + if (ARG2_GET_A2(arg2) == 0xff) { + 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 (error != 0 || req->newptr == NULL) + return (error); + + return (0); +} + + +/* D18F3x64 Hardware Thermal Control (HTC) */ +static int +amdtemp_htc_temp_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct amdtemp_softc *sc = arg1; + union reg_amd_htc_desc reg_htc; + uint32_t reg_num, bits_off; + unsigned val; + int error; + + reg_num = ARG2_GET_REG(arg2); + bits_off = ARG2_GET_A1(arg2); + + reg_htc.u32 = pci_read_config(sc->dev, reg_num, 4); + switch (bits_off) { + case 16: /* HtcTmpLmt */ + val = (((reg_htc.bits.HtcTmpLmt * 10) / 2) + 520); break; - case CORE1: - auxtemp[0] = sc->sc_gettemp(dev, CORE1_SENSOR0); - auxtemp[1] = sc->sc_gettemp(dev, CORE1_SENSOR1); - temp = imax(auxtemp[0], auxtemp[1]); + case 24: /* HtcHystLmt */ + val = ((reg_htc.bits.HtcHystLmt * 10) / 2); break; - default: - temp = sc->sc_gettemp(dev, sensor); - break; } - error = sysctl_handle_int(oidp, &temp, 0, req); + val += AMDTEMP_ZERO_C_TO_K; + error = sysctl_handle_int(oidp, &val, 0, req); + if (error != 0 || req->newptr == NULL) + return (error); + /* pci_write_config(sc->dev, reg_num, reg_htc.u32, 4); */ - return (error); + return (0); } -#define AMDTEMP_ZERO_C_TO_K 2731 -static int32_t -amdtemp_gettemp0f(device_t dev, amdsensor_t sensor) +/* D18F3xA4 Reported Temperature Control Register */ +static int +amdtemp_rtc_temp_sysctl(SYSCTL_HANDLER_ARGS) { - struct amdtemp_softc *sc = device_get_softc(dev); - uint32_t mask, offset, temp; + struct amdtemp_softc *sc = arg1; + union reg_amd_rep_tmp_ctrl_desc reg_rtc; + uint32_t reg_num, bits_off; + unsigned val; + int error; - /* Set Sensor/Core selector. */ - temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1); - temp &= ~(AMDTEMP_TTSR_SELCORE | AMDTEMP_TTSR_SELSENSOR); - switch (sensor) { - case CORE0_SENSOR1: - temp |= AMDTEMP_TTSR_SELSENSOR; - /* FALLTHROUGH */ - case CORE0_SENSOR0: - case CORE0: - if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0) - temp |= AMDTEMP_TTSR_SELCORE; + reg_num = ARG2_GET_REG(arg2); + bits_off = ARG2_GET_A1(arg2); + + AMDTEMP_LOCK(sc); + reg_rtc.u32 = pci_read_config(sc->dev, reg_num, 4); + switch (bits_off) { + case 16: /* CurTmpTjSel */ + reg_rtc.bits.CurTmpTjSel = 3; break; - case CORE1_SENSOR1: - temp |= AMDTEMP_TTSR_SELSENSOR; - /* FALLTHROUGH */ - case CORE1_SENSOR0: - case CORE1: - if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0) - temp |= AMDTEMP_TTSR_SELCORE; + case 21: /* CurTmp */ + reg_rtc.bits.CurTmpTjSel = 0; break; } - pci_write_config(dev, AMDTEMP_THERMTP_STAT, temp, 1); + pci_write_config(sc->dev, reg_num, reg_rtc.u32, 4); + reg_rtc.u32 = pci_read_config(sc->dev, reg_num, 4); + if (bits_off == 16) { /* CurTmpTjSel: switch back to CurTmp. */ + reg_rtc.bits.CurTmpTjSel = 0; + pci_write_config(sc->dev, reg_num, reg_rtc.u32, 4); + } + AMDTEMP_UNLOCK(sc); - mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc; - 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; + val = (AMDTEMP_ZERO_C_TO_K + ((reg_rtc.bits.CurTmp * 10) / 8)); + if (bits_off == 16) { /* CurTmpTjSel */ + val -= 490; + } else { + val += (sc->rtc_temp_offset * 10); + } + error = sysctl_handle_int(oidp, &val, 0, req); + if (error != 0 || req->newptr == NULL) + return (error); + /* pci_write_config(sc->dev, reg_num, reg_rtc.u32, 4); */ - return (temp); + return (0); } -static int32_t -amdtemp_gettemp(device_t dev, amdsensor_t sensor) + +/* Set SMBus-based sideband interface address: 0-7. */ +static void +amdtemp_sbi_set_addr(struct amdtemp_softc *sc, uint32_t sbi_addr) { - struct amdtemp_softc *sc = device_get_softc(dev); - uint32_t temp; + union reg_amd_sbi_ctrl_desc reg_sbi; - temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4); - temp = ((temp >> 21) & 0x7ff) * 5 / 4; - temp += AMDTEMP_ZERO_C_TO_K + sc->sc_offset * 10; + sbi_addr &= AMD_REG_SBI_ADDR_MASK; + reg_sbi.u32 = pci_read_config(sc->dev, AMD_REG_SBI_CTRL, 4); + if (reg_sbi.bits.SbiAddr == sbi_addr) /* Is address allready set? */ + return; + reg_sbi.bits.SbiAddr = sbi_addr; + pci_write_config(sc->dev, AMD_REG_SBI_CTRL, reg_sbi.u32, 4); +} - return (temp); +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_sbi; + 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_sbi.u32 = pci_read_config(sc->dev, AMD_REG_SBI_CTRL, 4); + if (reg_sbi.bits.SbiRegWrDn != 0) + 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 (error != 0 || req->newptr == NULL || 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)); + /* Apply offset only to sensor. */ + if (ARG2_GET_REG(arg2) == SB_TSI_REG_CPU_TEMP_LB) { + 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 (error != 0 || req->newptr == NULL) + return (error); + + return (0); +}