Changeset View
Changeset View
Standalone View
Standalone View
/usr/src/sys/dev/amdtemp/amdtemp.c
| /*- | /*- | ||||
| * Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org> | * Copyright (c) 2008, 2009 Rui Paulo <rpaulo@FreeBSD.org> | ||||
| * 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-2011 Jung-uk Kim <jkim@FreeBSD.org> | ||||
allanjude: this change looks like it is inadvertent | |||||
rozhuk.im-gmail.comAuthorUnsubmitted 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) 2013 - 2014 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. | |||||
| #include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
| __FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
| #define CTASSERT(x) | |||||
mmokhiUnsubmitted Done Inline ActionsWhere is it used? mmokhi: Where is it used?
I think maybe, maybe you can use `KASSERT`? | |||||
rozhuk.im-gmail.comAuthorUnsubmitted 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> | ||||
| typedef enum { | |||||
| CORE0_SENSOR0, | |||||
| CORE0_SENSOR1, | |||||
| CORE1_SENSOR0, | |||||
| CORE1_SENSOR1, | |||||
| CORE0, | |||||
| CORE1 | |||||
| } amdsensor_t; | |||||
| struct amdtemp_softc { | struct amdtemp_softc { | ||||
| device_t sc_dev; | device_t dev; | ||||
| int sc_ncores; | struct mtx lock; /* Read/write lock for some registers. */ | ||||
| int sc_ntemps; | uint32_t cpu_ncores; | ||||
| int sc_flags; | uint32_t flags; | ||||
| #define AMDTEMP_FLAG_CS_SWAP 0x01 /* ThermSenseCoreSel is inverted. */ | uint32_t tts_flags; /* Thermaltrip Status flags. */ | ||||
| #define AMDTEMP_FLAG_CT_10BIT 0x02 /* CurTmp is 10-bit wide. */ | int32_t tts_temp_offset[4]; | ||||
| #define AMDTEMP_FLAG_ALT_OFFSET 0x04 /* CurTmp starts at -28C. */ | int32_t rtc_temp_offset; | ||||
| int32_t sc_offset; | int32_t tsi_temp_offset[8]; | ||||
| int32_t (*sc_gettemp)(device_t, amdsensor_t); | struct sysctl_oid *sysctl_cpu[MAXCPU]; /* dev.cpu.X.temperature oids. */ | ||||
| struct sysctl_oid *sc_sysctl_cpu[MAXCPU]; | struct intr_config_hook sc_ich; | ||||
| 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 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 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 | |||||
| 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 } | |||||
| }; | |||||
| /* | #define AMDTEMP_LOCK(sc) mtx_lock(&(sc)->lock) | ||||
| * Reported Temperature Control Register | #define AMDTEMP_UNLOCK(sc) mtx_unlock(&(sc)->lock) | ||||
| */ | |||||
| #define AMDTEMP_REPTMP_CTRL 0xa4 | |||||
| /* | |||||
| * 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 | /* D18F3xE4 Thermtrip Status Register */ | ||||
| #define AMDTEMP_TTSR_SELCORE 0x04 | #define AMD_REG_THERMTRIP_STAT 0xe4 | ||||
| #define AMDTEMP_TTSR_SELSENSOR 0x40 | 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 */ | /* D18F3x64 Hardware Thermal Control (HTC) */ | ||||
| #define AMDTEMP_DRAM_MODE_DDR3 0x0100 | #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", "This field is the offset from CurTmp used to normalize to Tcontrol." }, | |||||
| { AMD_REG_THERMTRIP_STAT, 8, 6, (CTLFLAG_RD | CTLTYPE_UINT), "IU", amdtemp_sysctl_reg_bits, "DiodeOffset", "Thermal diode offset is used to correct the measurement made by an external temperature sensor." }, | |||||
| { AMD_REG_THERMTRIP_STAT, 5, 1, (CTLFLAG_RD | CTLTYPE_UINT), "IU", amdtemp_sysctl_reg_bits, "ThermtpEn", "The THERMTRIP state is supported by the processor." }, | |||||
| { AMD_REG_THERMTRIP_STAT, 1, 1, (CTLFLAG_RD | CTLTYPE_UINT), "IU", amdtemp_sysctl_reg_bits, "Thermtrip", "The processor has entered the THERMTRIP state." }, | |||||
| { 0, 0, 0, 0, NULL, NULL, NULL, NULL } | |||||
| }; | |||||
| /* 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", "HTC temperature limit" }, | |||||
| { AMD_REG_HTC_CTRL, 24, 4, (CTLFLAG_RW | CTLTYPE_INT), "IK", amdtemp_htc_temp_sysctl, "HtcHystLmt", "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", "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", "HtcPstateLimit, HtcHystLmt, HtcTmpLmt, and HtcEn are read-only." }, | |||||
| { AMD_REG_HTC_CTRL, 23, 1, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_sysctl_reg_bits, "HtcSlewSel", "HTC slew-controlled temperature select." }, | |||||
| { AMD_REG_HTC_CTRL, 28, 3, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_sysctl_reg_bits, "HtcPstateLimit", "HTC P-state limit select." }, | |||||
| { AMD_REG_HTC_CTRL, 4, 1, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_sysctl_reg_bits, "HtcAct", "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", "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", "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", "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", "Provides the current control temperature, Tctl, after the slew-rate controls have been applied." }, | |||||
| { AMD_REG_REPTMP_CTRL, 16, 2, (CTLFLAG_RW | CTLTYPE_INT), "IK", amdtemp_rtc_temp_sysctl, "CurTmpTjSel", "Specifies a value used to create Tctl." }, | |||||
| //{ AMD_REG_REPTMP_CTRL, 18, 1, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_sysctl_reg_bits, "CurTmpTjSlewSel", "" }, | |||||
| //{ AMD_REG_REPTMP_CTRL, 19, 1, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_sysctl_reg_bits, "CurTmpRangeSel", "" }, | |||||
| { AMD_REG_REPTMP_CTRL, 7, 1, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_sysctl_reg_bits, "TmpSlewDnEn", "Temperature slew downward enable." }, | |||||
| { AMD_REG_REPTMP_CTRL, 5, 2, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_sysctl_reg_bits, "TmpMaxDiffUp", "Specifies the maximum difference, (Tctlm - Tctl), when Tctl immediatly updates to Tctlm." }, | |||||
| { AMD_REG_REPTMP_CTRL, 8, 5, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_sysctl_reg_bits, "PerStepTimeDn", "Specifies the time that Tctlm must remain below Tctl before applying a 0.125 downward step." }, | |||||
| { AMD_REG_REPTMP_CTRL, 0, 5, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_sysctl_reg_bits, "PerStepTimeUp", "Specifies the time that Tctlm must remain above Tctl before applying a 0.125 upward step." }, | |||||
| { 0, 0, 0, 0, NULL, NULL, NULL, NULL } | |||||
| }; | |||||
| /* SB-TSI registers. */ | |||||
| static struct amdtemp_sysctl_reg amdtemp_tsi_regs[] = { | |||||
| { SB_TSI_REG_CPU_TEMP_LB, SB_TSI_REG_CPU_TEMP_HB, 0, (CTLFLAG_RD | CTLTYPE_INT), "IK", amdtemp_tsi_temp_reg_sysctl, "cpu_temperature", "CPU Temperature" }, | |||||
| { SB_TSI_REG_HIGH_TEMP_TLB, SB_TSI_REG_HIGH_TEMP_THB, 0, (CTLFLAG_RD | CTLTYPE_INT), "IK", amdtemp_tsi_temp_reg_sysctl, "high_temperature_threshold", "High Temperature Threshold" }, | |||||
| { SB_TSI_REG_LOW_TEMP_TLB, SB_TSI_REG_LOW_TEMP_THB, 0, (CTLFLAG_RD | CTLTYPE_INT), "IK", amdtemp_tsi_temp_reg_sysctl, "low_temperature_threshold", "Low Temperature Threshold" }, | |||||
| { SB_TSI_REG_CPU_TEMP_OFF_HB, 0, 0, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_tsi_reg_sysctl, "cpu_temperature_offset_hi", "CPU Temperature Offset High Byte" }, | |||||
| { SB_TSI_REG_CPU_TEMP_OFF_LB, 0, 0, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_tsi_reg_sysctl, "cpu_temperature_offset_lo", "CPU Temperature Offset Low Byte" }, | |||||
| { SB_TSI_REG_STATUS, 0, 0, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_tsi_reg_sysctl, "status", "SB-TSI Status" }, | |||||
| { SB_TSI_REG_CFG, 0, 0, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_tsi_reg_sysctl, "cfg3", "SB-TSI Configuration Register 0x03" }, | |||||
| { SB_TSI_REG_CFG2, 0, 0, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_tsi_reg_sysctl, "cfg9", "SB-TSI Configuration Register 0x09" }, | |||||
| { SB_TSI_REG_UPD_RATE, 0, 0, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_tsi_reg_sysctl, "upd_rate", "Update Rate" }, | |||||
| { SB_TSI_REG_TIMEOUT_CFG, 0, 0, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_tsi_reg_sysctl, "timeout_cfg", "Timeout Configuration" }, | |||||
| { SB_TSI_REG_ALERT_THRESHOLD, 0, 0, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_tsi_reg_sysctl, "alert_threshold", "Alert Threshold" }, | |||||
| { SB_TSI_REG_ALERT_CFG, 0, 0, (CTLFLAG_RW | CTLTYPE_UINT), "IU", amdtemp_tsi_reg_sysctl, "alert_cfg", "Alert Configuration" }, | |||||
| { SB_TSI_REG_MANUFACTURE_ID, 0, 0, (CTLFLAG_RD | CTLTYPE_UINT), "IU", amdtemp_tsi_reg_sysctl, "manufacture_id", "Manufacture ID" }, | |||||
| { SB_TSI_REG_REVISION, 0, 0, (CTLFLAG_RD | CTLTYPE_UINT), "IU", amdtemp_tsi_reg_sysctl, "revision", "SB-TSI Revision" }, | |||||
| { 0, 0, 0, 0, NULL, NULL, NULL, NULL } | |||||
| }; | |||||
| /* | /* | ||||
| * Device methods. | * 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 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. | |||||
| amdtemp_methods, | amdtemp_methods, | ||||
| sizeof(struct amdtemp_softc), | sizeof(struct amdtemp_softc), | ||||
| }; | }; | ||||
| 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); | |||||
| static int | static int | ||||
| amdtemp_match(device_t dev) | amdtemp_dev_check(device_t dev) { | ||||
| { | uint32_t cpuid; | ||||
| int i; | |||||
| uint16_t vendor, devid; | |||||
| vendor = pci_get_vendor(dev); | if (resource_disabled("amdtemp", 0)) | ||||
| devid = pci_get_device(dev); | return (ENXIO); | ||||
| /* | |||||
| for (i = 0; amdtemp_products[i].amdtemp_vendorid != 0; 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 (CPU_VENDOR_AMD != pci_get_vendor(dev) || | |||||
| PCIC_BRIDGE != pci_get_class(dev) || | |||||
| 3 != pci_get_function(dev)) | |||||
| return (ENXIO); | |||||
| /* Is processor have Temperature sensor / THERMTRIP / HTC ? */ | |||||
| if (0 == (amd_pminfo & (AMDPM_TS | AMDPM_TTP | AMDPM_TM))) | |||||
| return (ENXIO); | |||||
| /* Check minimum cpu family. */ | |||||
| cpuid = pci_read_config(dev, AMD_REG_CPUID, 4); | |||||
| if (CPUID_TO_FAMILY(cpuid) < 0x0f) | |||||
| return (ENXIO); | |||||
| return (0); | return (0); | ||||
| } | } | ||||
| static void | static void | ||||
| amdtemp_identify(driver_t *driver, device_t parent) | amdtemp_identify(driver_t *driver, device_t parent) { | ||||
| { | |||||
| 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) != NULL) | ||||
| return; | return; | ||||
| if (0 != 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"); | ||||
| } | |||||
| } | } | ||||
| static int | static int | ||||
| amdtemp_probe(device_t dev) | amdtemp_probe(device_t dev) { | ||||
| { | |||||
| uint32_t family, model; | |||||
| if (resource_disabled("amdtemp", 0)) | if (0 != amdtemp_dev_check(dev)) | ||||
| return (ENXIO); | 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"); | 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]; | |||||
| u_int regs[4]; | |||||
| struct amdtemp_softc *sc = device_get_softc(dev); | struct amdtemp_softc *sc = device_get_softc(dev); | ||||
| struct sysctl_ctx_list *sysctlctx; | uint32_t i, cpuid, model; | ||||
| struct sysctl_oid *sysctlnode; | union reg_amd_thermtrip_status_desc reg_tts; | ||||
| uint32_t cpuid, family, model; | union reg_amd_htc_desc reg_htc; | ||||
| u_int bid; | union reg_amd_sbi_ctrl_desc reg_sbi; | ||||
| int erratum319, unit; | struct sysctl_ctx_list *ctx; | ||||
| struct sysctl_oid_list *child, *list; | |||||
| struct sysctl_oid *node, *sub_node; | |||||
| char str[32]; | |||||
| int tsi_ok = 0, 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 = pci_read_config(dev, AMD_REG_CPUID, 4); | ||||
| * CPUID Register is available from Revision F. | |||||
| */ | |||||
| cpuid = cpu_id; | |||||
| family = CPUID_TO_FAMILY(cpuid); | |||||
| model = CPUID_TO_MODEL(cpuid); | model = CPUID_TO_MODEL(cpuid); | ||||
| if (family != 0x0f || model >= 0x40) { | switch (CPUID_TO_FAMILY(cpuid)) { | ||||
| cpuid = pci_read_config(dev, AMDTEMP_CPUID, 4); | |||||
| family = CPUID_TO_FAMILY(cpuid); | |||||
| model = CPUID_TO_MODEL(cpuid); | |||||
| } | |||||
| switch (family) { | |||||
| case 0x0f: | case 0x0f: | ||||
| /* | /* | ||||
| * Thermaltrip Status Register | * Thermaltrip Status Register | ||||
| 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 (0 == (amd_pminfo & AMDPM_TTP)) /* No TTP: THERMTRIP */ | |||||
| break; | |||||
| reg_tts.u32 = pci_read_config(dev, AMD_REG_THERMTRIP_STAT, 4); | |||||
| if (0 == reg_tts.bits.ThermtpEn) | |||||
| 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); | 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 && | ||||
| sc->sc_flags |= | bid != 0x0c) | ||||
| AMDTEMP_FLAG_ALT_OFFSET; | sc->tts_flags |= AMDTEMP_TTS_F_OFF28; | ||||
| 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; | ||||
| } | } | ||||
| 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; | break; | ||||
| case 0x10: | case 0x10: | ||||
| sc->flags |= AMDTEMP_F_RTC; | |||||
| reg_sbi.u32 = pci_read_config(dev, AMD_REG_SBI_CTRL, 4); | |||||
| if (0 == reg_sbi.bits.SbTsiDis) | |||||
| sc->flags |= AMDTEMP_F_TSI; | |||||
| /* | /* | ||||
| * 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); | do_cpuid(0x80000001, regs); | ||||
| Context not available. | |||||
| 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), | if ((pci_cfgregread(pci_get_bus(dev), pci_get_slot(dev), 2, | ||||
| pci_get_slot(dev), 2, AMDTEMP_DRAM_CONF_HIGH, 2) & | AMD_REG_DRAM_CONF_HIGH, 2) & AMD_REG_DRAM_MODE_DDR3) != 0 || | ||||
| AMDTEMP_DRAM_MODE_DDR3) != 0 || model > 0x04 || | 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 */ | break; | ||||
| case 0x11: | case 0x11: | ||||
| case 0x12: | case 0x12: | ||||
| case 0x14: | case 0x14: | ||||
| case 0x15: | case 0x15: | ||||
| case 0x16: | case 0x16: | ||||
| /* | default: | ||||
| * There is only one sensor per package. | sc->flags |= AMDTEMP_F_RTC; | ||||
| */ | reg_sbi.u32 = pci_read_config(dev, AMD_REG_SBI_CTRL, 4); | ||||
| sc->sc_ntemps = 1; | if (0 == reg_sbi.bits.SbRmiDis || /* = SbTsiDis */ | ||||
| 0 == reg_sbi.bits.SbTsiDis) | |||||
| sc->sc_gettemp = amdtemp_gettemp; | sc->flags |= AMDTEMP_F_TSI; | ||||
| /* XXX TODO: read TSI via SMBus. */ | |||||
| break; | break; | ||||
| } | } | ||||
| if (0 != (amd_pminfo & AMDPM_TM)) { /* Hardware Thermal Control (HTC) */ | |||||
| reg_htc.u32 = pci_read_config(dev, AMD_REG_HTC_CTRL, 4); | |||||
| if (0 != reg_htc.bits.HtcEn) | |||||
| sc->flags |= AMDTEMP_F_HTC; | |||||
| } | |||||
| /* Find number of cores per package. */ | ctx = device_get_sysctl_ctx(dev); | ||||
| sc->sc_ncores = (amd_feature2 & AMDID2_CMP) != 0 ? | child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); | ||||
| (cpu_procinfo2 & AMDID_CMP_CORES) + 1 : 1; | if (0 != (sc->flags & AMDTEMP_F_TTS)) { /* Thermaltrip Status */ | ||||
| if (sc->sc_ncores > MAXCPU) | node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "tts", | ||||
| return (ENXIO); | CTLFLAG_RD, NULL, "Thermaltrip Status"); | ||||
| list = SYSCTL_CHILDREN(node); | |||||
| amdtemp_sysctl_reg_add(sc, list, amdtemp_thermtrip_status_reg_bits); | |||||
| for (i = 0; i < sc->cpu_ncores && i < 2; i ++) { | |||||
| snprintf(str, sizeof(str), "core%i", i); | |||||
| sub_node = SYSCTL_ADD_NODE(ctx, list, OID_AUTO, str, | |||||
| CTLFLAG_RD, NULL, "CPU core sensors"); | |||||
| SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sub_node), OID_AUTO, | |||||
| "sensor0", (CTLTYPE_INT | CTLFLAG_RD), sc, | |||||
| MAKE_ARG2(AMD_REG_THERMTRIP_STAT, i, 0), | |||||
| amdtemp_tts_temp_reg_sysctl, "IK", "Sensor 0 temperature"); | |||||
| SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sub_node), OID_AUTO, | |||||
| "sensor1", (CTLTYPE_INT | CTLFLAG_RD), sc, | |||||
| MAKE_ARG2(AMD_REG_THERMTRIP_STAT, i, 1), | |||||
| amdtemp_tts_temp_reg_sysctl, "IK", "Sensor 1 temperature"); | |||||
| SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(sub_node), OID_AUTO, | |||||
| "sensor0_offset", CTLFLAG_RW, | |||||
| &sc->tts_temp_offset[((i << 1) | 0)], 0, | |||||
| "Temperature sensor offset"); | |||||
| SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(sub_node), OID_AUTO, | |||||
| "sensor1_offset", CTLFLAG_RW, | |||||
| &sc->tts_temp_offset[((i << 1) | 1)], 0, | |||||
| "Temperature sensor offset"); | |||||
| } | |||||
| } | |||||
| if (0 != (sc->flags & AMDTEMP_F_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); | |||||
| } | |||||
| if (0 != (sc->flags & AMDTEMP_F_RTC)) { /* Reported Temperature Control */ | |||||
| node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "rtc", | |||||
| CTLFLAG_RD, NULL, "Reported Temperature Control"); | |||||
| list = SYSCTL_CHILDREN(node); | |||||
| amdtemp_sysctl_reg_add(sc, list, amdtemp_reptmp_reg_bits); | |||||
| SYSCTL_ADD_INT(ctx, list, OID_AUTO, "sensor_offset", CTLFLAG_RW, | |||||
| &sc->rtc_temp_offset, 0, "Temperature sensor offset"); | |||||
| } | |||||
| if (0 != (sc->flags & AMDTEMP_F_TSI)) { /* Temperature Sensor Interface */ | |||||
| for (i = 0; i < 8; i ++) { | |||||
| if (0 == amdtemp_sbi_read(sc, i, SB_TSI_REG_REVISION)) | |||||
| continue; | |||||
| if (0 == tsi_ok) { /* 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 (0 == tsi_ok) /* 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 (0 != (amd_pminfo & AMDPM_TS)) | |||||
| device_printf(dev, "CPU have TS: Temperature sensor\n"); | |||||
| if (0 != (sc->flags & AMDTEMP_F_TTS)) | |||||
| device_printf(dev, "Found: Thermaltrip Status (TTS)\n"); | |||||
| if (0 != (sc->flags & AMDTEMP_F_RTC)) | |||||
| device_printf(dev, "Found: Reported Temperature Control (RTC)\n"); | |||||
| if (0 != (sc->flags & AMDTEMP_F_TSI)) | |||||
| 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) | if (erratum319) | ||||
| device_printf(dev, | device_printf(dev, | ||||
| "Erratum 319: temperature measurement may be inaccurate\n"); | "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 (NULL == sc->sysctl_cpu[0]) { | ||||
| 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) != 0) { | |||||
| amdtemp_detach(dev); | |||||
| device_printf(dev, "config_intrhook_establish failed!\n"); | |||||
| return (ENXIO); | |||||
| } | |||||
| } | |||||
| return (0); | |||||
| } | |||||
| sysctlctx = device_get_sysctl_ctx(dev); | int | ||||
| SYSCTL_ADD_INT(sysctlctx, | amdtemp_detach(device_t dev) { | ||||
| SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, | struct amdtemp_softc *sc = device_get_softc(dev); | ||||
| "sensor_offset", CTLFLAG_RW, &sc->sc_offset, 0, | uint32_t i; | ||||
| "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, | for (i = 0; i < sc->cpu_ncores; i++) | ||||
| SYSCTL_CHILDREN(sysctlnode), | if (sc->sysctl_cpu[i] != NULL) | ||||
| OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD, | sysctl_remove_oid(sc->sysctl_cpu[i], 1, 0); | ||||
| dev, CORE0_SENSOR0, amdtemp_sysctl, "IK", | /* NewBus removes the dev.amdtemp.N tree by itself. */ | ||||
| "Core 0 / Sensor 0 temperature"); | if (sc->sc_ich.ich_arg != NULL) { | ||||
| sc->sc_ich.ich_arg = NULL; | |||||
| config_intrhook_disestablish(&sc->sc_ich); | |||||
| } | |||||
| mtx_destroy(&sc->lock); | |||||
| if (sc->sc_ntemps > 1) { | return (0); | ||||
| 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) { | void | ||||
| sysctlnode = SYSCTL_ADD_NODE(sysctlctx, | amdtemp_intrhook(void *arg) { | ||||
| SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), | struct amdtemp_softc *sc = arg; | ||||
| OID_AUTO, "core1", CTLFLAG_RD, 0, "Core 1"); | device_t dev = sc->dev, acpi, cpu, nexus; | ||||
| int (*sysctl_handler)(SYSCTL_HANDLER_ARGS); | |||||
| intptr_t sysctl_arg2; | |||||
| uint32_t i, unit_base; | |||||
| SYSCTL_ADD_PROC(sysctlctx, | if (sc->sc_ich.ich_arg != NULL) { | ||||
| SYSCTL_CHILDREN(sysctlnode), | sc->sc_ich.ich_arg = NULL; | ||||
| OID_AUTO, "sensor0", CTLTYPE_INT | CTLFLAG_RD, | config_intrhook_disestablish(&sc->sc_ich); | ||||
| dev, CORE1_SENSOR0, amdtemp_sysctl, "IK", | } | ||||
| "Core 1 / Sensor 0 temperature"); | |||||
| SYSCTL_ADD_PROC(sysctlctx, | /* dev.cpu.N.temperature. */ | ||||
| SYSCTL_CHILDREN(sysctlnode), | nexus = device_find_child(root_bus, "nexus", 0); | ||||
| OID_AUTO, "sensor1", CTLTYPE_INT | CTLFLAG_RD, | acpi = device_find_child(nexus, "acpi", 0); | ||||
| dev, CORE1_SENSOR1, amdtemp_sysctl, "IK", | /* XXX: cpu_ncores not constant for different CPUs... */ | ||||
| "Core 1 / Sensor 1 temperature"); | unit_base = (device_get_unit(dev) * sc->cpu_ncores); | ||||
| for (i = 0; i < sc->cpu_ncores; i ++) { | |||||
| if (sc->sysctl_cpu[i] != NULL) | |||||
| continue; | |||||
| cpu = device_find_child(acpi, "cpu", (unit_base + i)); | |||||
| if (cpu == NULL) | |||||
| continue; | |||||
| sysctl_handler = NULL; | |||||
| if (0 != (sc->flags & AMDTEMP_F_TSI)) { | |||||
| /* Temperature Sensor Interface */ | |||||
| if (0 == amdtemp_sbi_read(sc, i, SB_TSI_REG_REVISION)) | |||||
| continue; | |||||
| sysctl_handler = amdtemp_tsi_temp_reg_sysctl; | |||||
| sysctl_arg2 = MAKE_ARG2(SB_TSI_REG_CPU_TEMP_LB, | |||||
| SB_TSI_REG_CPU_TEMP_HB, i); | |||||
| } else if (0 != (sc->flags & AMDTEMP_F_RTC)) { | |||||
| /* Reported Temperature Control */ | |||||
| sysctl_handler = amdtemp_rtc_temp_sysctl; | |||||
| sysctl_arg2 = MAKE_ARG2(AMD_REG_REPTMP_CTRL, 21, 11); | |||||
| } else if (0 != (sc->flags & AMDTEMP_F_TTS)) { | |||||
| /* Thermaltrip Status */ | |||||
| sysctl_handler = amdtemp_tts_temp_reg_sysctl; | |||||
| sysctl_arg2 = MAKE_ARG2(AMD_REG_THERMTRIP_STAT, i, 0xff); | |||||
| } | } | ||||
| if (NULL == sysctl_handler) | |||||
| continue; | |||||
| 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 temparature"); | |||||
| } | } | ||||
| } | |||||
| /* | |||||
| * Try to create dev.cpu sysctl entries and setup intrhook function. | /* Sysctl staff. */ | ||||
| * This is needed because the cpu driver may be loaded late on boot, | static void | ||||
| * after us. | amdtemp_sysctl_reg_add(struct amdtemp_softc *sc, struct sysctl_oid_list *child, | ||||
| */ | struct amdtemp_sysctl_reg *regs) { | ||||
| amdtemp_intrhook(dev); | struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev); | ||||
| sc->sc_ich.ich_func = amdtemp_intrhook; | uint32_t i; | ||||
| sc->sc_ich.ich_arg = dev; | |||||
| if (config_intrhook_establish(&sc->sc_ich) != 0) { | for (i = 0; NULL != regs[i].oid_handler; i ++) { | ||||
| device_printf(dev, "config_intrhook_establish failed!\n"); | SYSCTL_ADD_PROC(ctx, child, OID_AUTO, regs[i].name, | ||||
| return (ENXIO); | regs[i].flags, sc, | ||||
| MAKE_ARG2(regs[i].reg, regs[i].a1, regs[i].a2), | |||||
| regs[i].oid_handler, regs[i].fmt, regs[i].descr); | |||||
| } | } | ||||
| } | |||||
| static void | |||||
| amdtemp_sysctl_reg_add2(struct amdtemp_softc *sc, struct sysctl_oid_list *child, | |||||
| struct amdtemp_sysctl_reg *regs, uint32_t a2) { | |||||
| struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev); | |||||
| uint32_t i; | |||||
| for (i = 0; NULL != regs[i].oid_handler; i ++) { | |||||
| SYSCTL_ADD_PROC(ctx, child, OID_AUTO, regs[i].name, | |||||
| regs[i].flags, sc, | |||||
| MAKE_ARG2(regs[i].reg, regs[i].a1, a2), | |||||
| regs[i].oid_handler, regs[i].fmt, regs[i].descr); | |||||
| } | |||||
| } | |||||
| static int | |||||
| amdtemp_sysctl_reg_bits(SYSCTL_HANDLER_ARGS) { | |||||
| struct amdtemp_softc *sc = arg1; | |||||
| uint32_t i, reg_data, reg_num, bits_off, bits_len, bits_mask = 0; | |||||
| unsigned val; | |||||
| int error; | |||||
| reg_num = ARG2_GET_REG(arg2); | |||||
| bits_off = ARG2_GET_A1(arg2); | |||||
| bits_len = ARG2_GET_A2(arg2); | |||||
| reg_data = pci_read_config(sc->dev, reg_num, 4); | |||||
| for(i = 0; i < bits_len; i ++) | |||||
| bits_mask |= ((uint32_t)1 << i); | |||||
| val = ((reg_data >> bits_off) & bits_mask); | |||||
| error = sysctl_handle_int(oidp, &val, 0, req); | |||||
| if (0 != error || NULL == req->newptr || val == reg_data) | |||||
| return (error); | |||||
| reg_data &= ~(bits_mask << bits_off); // clear all bits at offset | |||||
| reg_data |= ((val & bits_mask) << bits_off); // set value bits | |||||
| pci_write_config(sc->dev, reg_num, reg_data, 4); | |||||
| return (0); | 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; | |||||
| sc = device_get_softc(dev); | /* 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; | ||||
| * dev.cpu.N.temperature. | if (0 == (sc->tts_flags & AMDTEMP_TTS_F_CS_SWAP)) | ||||
| */ | reg_tts.bits.ThermSenseCoreSel = ((0 != core) ? 1 : 0); | ||||
| nexus = device_find_child(root_bus, "nexus", 0); | else /* Swap. */ | ||||
| acpi = device_find_child(nexus, "acpi", 0); | reg_tts.bits.ThermSenseCoreSel = ((0 != core) ? 0 : 1); | ||||
| reg_tts.bits.ThermSenseSel = ((0 != sense) ? 1 : 0); | |||||
| for (i = 0; i < sc->sc_ncores; i++) { | AMDTEMP_LOCK(sc); | ||||
| if (sc->sc_sysctl_cpu[i] != NULL) | pci_write_config(sc->dev, reg, reg_tts.u32, 4); | ||||
| continue; | reg_tts.u32 = pci_read_config(sc->dev, reg, 4); | ||||
| cpu = device_find_child(acpi, "cpu", | AMDTEMP_UNLOCK(sc); | ||||
| device_get_unit(dev) * sc->sc_ncores + i); | |||||
| if (cpu != NULL) { | |||||
| sysctlctx = device_get_sysctl_ctx(cpu); | |||||
| sensor = sc->sc_ntemps > 1 ? | val = reg_tts.bits.CurTmp; | ||||
| (i == 0 ? CORE0 : CORE1) : CORE0_SENSOR0; | if (0 == (sc->tts_flags & AMDTEMP_TTS_F_CT_10BIT)) | ||||
| sc->sc_sysctl_cpu[i] = SYSCTL_ADD_PROC(sysctlctx, | val &= ~3; /* Clear first 2 bits. */ | ||||
| SYSCTL_CHILDREN(device_get_sysctl_tree(cpu)), | val = (AMDTEMP_ZERO_C_TO_K + ((val * 5) / 2) - | ||||
| OID_AUTO, "temperature", CTLTYPE_INT | CTLFLAG_RD, | ((0 != (sc->tts_flags & AMDTEMP_TTS_F_OFF28)) ? 280 : 490)); | ||||
| dev, sensor, amdtemp_sysctl, "IK", | val += (sc->tts_temp_offset[((reg_tts.bits.ThermSenseCoreSel << 1) | | ||||
| "Current temparature"); | reg_tts.bits.ThermSenseSel)] * 10); | ||||
| } | |||||
| return (val); | |||||
| } | |||||
| /* If 0xff == ARG2_GET_A2(arg2) then retun max temp for core. */ | |||||
| static int | |||||
| amdtemp_tts_temp_reg_sysctl(SYSCTL_HANDLER_ARGS) { | |||||
| struct amdtemp_softc *sc = arg1; | |||||
| uint32_t reg_num; | |||||
| unsigned val; | |||||
| int error; | |||||
| reg_num = ARG2_GET_REG(arg2); | |||||
| if (0xff == ARG2_GET_A2(arg2)) { | |||||
| val = imax(amdtemp_tts_get_temp(sc, reg_num, ARG2_GET_A1(arg2), 0), | |||||
| amdtemp_tts_get_temp(sc, reg_num, ARG2_GET_A1(arg2), 1)); | |||||
| } else { | |||||
| val = amdtemp_tts_get_temp(sc, reg_num, ARG2_GET_A1(arg2), | |||||
| ARG2_GET_A2(arg2)); | |||||
| } | } | ||||
| if (sc->sc_ich.ich_arg != NULL) | error = sysctl_handle_int(oidp, &val, 0, req); | ||||
| config_intrhook_disestablish(&sc->sc_ich); | if (0 != error || NULL == req->newptr) | ||||
| return (error); | |||||
| return (0); | |||||
| } | } | ||||
| int | |||||
| amdtemp_detach(device_t dev) | |||||
| { | |||||
| struct amdtemp_softc *sc = device_get_softc(dev); | |||||
| int i; | |||||
| for (i = 0; i < sc->sc_ncores; i++) | /* D18F3x64 Hardware Thermal Control (HTC) */ | ||||
| if (sc->sc_sysctl_cpu[i] != NULL) | static int | ||||
| sysctl_remove_oid(sc->sc_sysctl_cpu[i], 1, 0); | 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; | |||||
| /* NewBus removes the dev.amdtemp.N tree by itself. */ | 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 24: /* HtcHystLmt */ | |||||
| val = ((reg_htc.bits.HtcHystLmt * 10) / 2); | |||||
| break; | |||||
| } | |||||
| val += AMDTEMP_ZERO_C_TO_K; | |||||
| error = sysctl_handle_int(oidp, &val, 0, req); | |||||
| if (0 != error || NULL == req->newptr) | |||||
| return (error); | |||||
| //pci_write_config(sc->dev, reg_num, reg_htc.u32, 4); | |||||
| return (0); | return (0); | ||||
| } | } | ||||
| /* D18F3xA4 Reported Temperature Control Register */ | |||||
| static int | static int | ||||
| amdtemp_sysctl(SYSCTL_HANDLER_ARGS) | amdtemp_rtc_temp_sysctl(SYSCTL_HANDLER_ARGS) { | ||||
| { | struct amdtemp_softc *sc = arg1; | ||||
| device_t dev = (device_t)arg1; | union reg_amd_rep_tmp_ctrl_desc reg_rtc; | ||||
| struct amdtemp_softc *sc = device_get_softc(dev); | uint32_t reg_num, bits_off; | ||||
| 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: | bits_off = ARG2_GET_A1(arg2); | ||||
| auxtemp[0] = sc->sc_gettemp(dev, CORE0_SENSOR0); | |||||
| auxtemp[1] = sc->sc_gettemp(dev, CORE0_SENSOR1); | AMDTEMP_LOCK(sc); | ||||
| temp = imax(auxtemp[0], auxtemp[1]); | reg_rtc.u32 = pci_read_config(sc->dev, reg_num, 4); | ||||
| switch (bits_off) { | |||||
| case 16: /* CurTmpTjSel */ | |||||
| reg_rtc.bits.CurTmpTjSel = 3; | |||||
| break; | break; | ||||
| case CORE1: | case 21: /* CurTmp */ | ||||
| auxtemp[0] = sc->sc_gettemp(dev, CORE1_SENSOR0); | reg_rtc.bits.CurTmpTjSel = 0; | ||||
| auxtemp[1] = sc->sc_gettemp(dev, CORE1_SENSOR1); | |||||
| temp = imax(auxtemp[0], auxtemp[1]); | |||||
| break; | break; | ||||
| default: | |||||
| temp = sc->sc_gettemp(dev, sensor); | |||||
| break; | |||||
| } | } | ||||
| error = sysctl_handle_int(oidp, &temp, 0, req); | 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); | |||||
| return (error); | val = (AMDTEMP_ZERO_C_TO_K + ((reg_rtc.bits.CurTmp * 10) / 8)); | ||||
| if (16 == bits_off) /* CurTmpTjSel */ | |||||
| val -= 490; | |||||
| else | |||||
| val += (sc->rtc_temp_offset * 10); | |||||
| error = sysctl_handle_int(oidp, &val, 0, req); | |||||
| if (0 != error || NULL == req->newptr) | |||||
| return (error); | |||||
| //pci_write_config(sc->dev, reg_num, reg_rtc.u32, 4); | |||||
| return (0); | |||||
| } | } | ||||
| #define AMDTEMP_ZERO_C_TO_K 2731 | |||||
| static int32_t | /* Set SMBus-based sideband interface address: 0-7. */ | ||||
| amdtemp_gettemp0f(device_t dev, amdsensor_t sensor) | static void | ||||
| { | amdtemp_sbi_set_addr(struct amdtemp_softc *sc, uint32_t sbi_addr) { | ||||
| struct amdtemp_softc *sc = device_get_softc(dev); | union reg_amd_sbi_ctrl_desc reg_sbi; | ||||
| uint32_t mask, offset, temp; | |||||
| /* Set Sensor/Core selector. */ | sbi_addr &= AMD_REG_SBI_ADDR_MASK; | ||||
| temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 1); | reg_sbi.u32 = pci_read_config(sc->dev, AMD_REG_SBI_CTRL, 4); | ||||
| temp &= ~(AMDTEMP_TTSR_SELCORE | AMDTEMP_TTSR_SELSENSOR); | if (reg_sbi.bits.SbiAddr == sbi_addr) /* Is address allready set? */ | ||||
| switch (sensor) { | return; | ||||
| case CORE0_SENSOR1: | reg_sbi.bits.SbiAddr = sbi_addr; | ||||
| temp |= AMDTEMP_TTSR_SELSENSOR; | pci_write_config(sc->dev, AMD_REG_SBI_CTRL, reg_sbi.u32, 4); | ||||
| /* FALLTHROUGH */ | } | ||||
| case CORE0_SENSOR0: | |||||
| case CORE0: | static uint32_t | ||||
| if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) != 0) | amdtemp_sbi_read(struct amdtemp_softc *sc, uint32_t sbi_addr, uint32_t reg_addr) { | ||||
| temp |= AMDTEMP_TTSR_SELCORE; | uint32_t ret; | ||||
| break; | |||||
| case CORE1_SENSOR1: | AMDTEMP_LOCK(sc); | ||||
| temp |= AMDTEMP_TTSR_SELSENSOR; | amdtemp_sbi_set_addr(sc, sbi_addr); | ||||
| /* FALLTHROUGH */ | pci_write_config(sc->dev, AMD_REG_SBI_ADDR, reg_addr, 4); | ||||
| case CORE1_SENSOR0: | ret = pci_read_config(sc->dev, AMD_REG_SBI_DATA, 4); | ||||
| case CORE1: | AMDTEMP_UNLOCK(sc); | ||||
| if ((sc->sc_flags & AMDTEMP_FLAG_CS_SWAP) == 0) | |||||
| temp |= AMDTEMP_TTSR_SELCORE; | return (ret); | ||||
| break; | } | ||||
| 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 (0 != reg_sbi.bits.SbiRegWrDn) | |||||
| break; | |||||
| DELAY(100); | |||||
| } | } | ||||
| pci_write_config(dev, AMDTEMP_THERMTP_STAT, temp, 1); | AMDTEMP_UNLOCK(sc); | ||||
| mask = (sc->sc_flags & AMDTEMP_FLAG_CT_10BIT) != 0 ? 0x3ff : 0x3fc; | if (data32 == 0) { | ||||
| offset = (sc->sc_flags & AMDTEMP_FLAG_ALT_OFFSET) != 0 ? 28 : 49; | device_printf(sc->dev, "timeout waiting for SBI write.\n"); | ||||
| temp = pci_read_config(dev, AMDTEMP_THERMTP_STAT, 4); | return (1); | ||||
| temp = ((temp >> 14) & mask) * 5 / 2; | } | ||||
| temp += AMDTEMP_ZERO_C_TO_K + (sc->sc_offset - offset) * 10; | return (0); | ||||
| } | |||||
| return (temp); | static int | ||||
| amdtemp_tsi_reg_sysctl(SYSCTL_HANDLER_ARGS) { | |||||
| struct amdtemp_softc *sc = arg1; | |||||
| uint32_t reg_data, reg_addr, sbi_addr; | |||||
| unsigned val; | |||||
| int error; | |||||
| reg_addr = ARG2_GET_REG(arg2); | |||||
| sbi_addr = (ARG2_GET_A2(arg2) & AMD_REG_SBI_ADDR_MASK); | |||||
| reg_data = amdtemp_sbi_read(sc, sbi_addr, reg_addr); | |||||
| val = reg_data; | |||||
| error = sysctl_handle_int(oidp, &val, 0, req); | |||||
| if (0 != error || NULL == req->newptr || val == reg_data) | |||||
| return (error); | |||||
| return (amdtemp_sbi_write(sc, sbi_addr, reg_addr, val)); | |||||
| } | } | ||||
| static int32_t | static int | ||||
| amdtemp_gettemp(device_t dev, amdsensor_t sensor) | amdtemp_tsi_temp_reg_sysctl(SYSCTL_HANDLER_ARGS) { | ||||
| { | struct amdtemp_softc *sc = arg1; | ||||
| struct amdtemp_softc *sc = device_get_softc(dev); | uint32_t reg_data_lo, reg_data_hi, sbi_addr; | ||||
| uint32_t temp; | unsigned val; | ||||
| int error; | |||||
| temp = pci_read_config(dev, AMDTEMP_REPTMP_CTRL, 4); | sbi_addr = (ARG2_GET_A2(arg2) & AMD_REG_SBI_ADDR_MASK); | ||||
| temp = ((temp >> 21) & 0x7ff) * 5 / 4; | reg_data_lo = amdtemp_sbi_read(sc, sbi_addr, ARG2_GET_REG(arg2)); | ||||
| temp += AMDTEMP_ZERO_C_TO_K + sc->sc_offset * 10; | 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 (SB_TSI_REG_CPU_TEMP_LB == ARG2_GET_REG(arg2)) | |||||
| 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 */ | |||||
| return (temp); | error = sysctl_handle_int(oidp, &val, 0, req); | ||||
| if (0 != error || NULL == req->newptr) | |||||
| return (error); | |||||
| return (0); | |||||
| } | } | ||||
| Context not available. | |||||
this change looks like it is inadvertent