mirror of
https://github.com/cyring/CoreFreq.git
synced 2025-07-23 20:20:40 +02:00
4939 lines
117 KiB
C
4939 lines
117 KiB
C
/*
|
|
* CoreFreq
|
|
* Copyright (C) 2015-2017 CYRIL INGENIERIE
|
|
* Licenses: GPL2
|
|
*/
|
|
|
|
#include <linux/version.h>
|
|
#include <linux/module.h>
|
|
#include <linux/cpu.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/device.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/utsname.h>
|
|
#include <linux/cpuidle.h>
|
|
#include <linux/cpufreq.h>
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
|
#include <linux/sched/signal.h>
|
|
#endif
|
|
#include <asm/msr.h>
|
|
#include <asm/nmi.h>
|
|
|
|
#include "bitasm.h"
|
|
#include "amdmsr.h"
|
|
#include "intelmsr.h"
|
|
#include "coretypes.h"
|
|
#include "corefreq-api.h"
|
|
#include "corefreqk.h"
|
|
|
|
MODULE_AUTHOR ("CYRIL INGENIERIE <labs[at]cyring[dot]fr>");
|
|
MODULE_DESCRIPTION ("CoreFreq Processor Driver");
|
|
MODULE_SUPPORTED_DEVICE ("Intel Core Core2 Atom Xeon i3 i5 i7, AMD Family 0Fh");
|
|
MODULE_LICENSE ("GPL");
|
|
MODULE_VERSION (COREFREQ_VERSION);
|
|
|
|
typedef struct {
|
|
FEATURES Features;
|
|
unsigned int SMT_Count;
|
|
signed int rc;
|
|
} ARG;
|
|
|
|
static struct {
|
|
signed int Major;
|
|
struct cdev *kcdev;
|
|
dev_t nmdev, mkdev;
|
|
struct class *clsdev;
|
|
} CoreFreqK;
|
|
|
|
static signed int ArchID = -1;
|
|
module_param(ArchID, int, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
MODULE_PARM_DESC(ArchID, "Force an architecture (ID)");
|
|
|
|
static signed int AutoClock = 1;
|
|
module_param(AutoClock, int, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
MODULE_PARM_DESC(AutoClock, "Auto estimate the clock frequency");
|
|
|
|
static unsigned int SleepInterval = 0;
|
|
module_param(SleepInterval, uint, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
MODULE_PARM_DESC(SleepInterval, "Timer interval (ms)");
|
|
|
|
static unsigned int TickInterval = 0;
|
|
module_param(TickInterval, uint, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
MODULE_PARM_DESC(TickInterval, "System requested interval (ms)");
|
|
|
|
static signed int Experimental = 0;
|
|
module_param(Experimental, int, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
MODULE_PARM_DESC(Experimental, "Enable features under development");
|
|
|
|
static unsigned short NMI_Disable = 0;
|
|
module_param(NMI_Disable, ushort, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
MODULE_PARM_DESC(NMI_Disable, "Disable the NMI handler");
|
|
|
|
static signed short PkgCStateLimit = -1;
|
|
module_param(PkgCStateLimit, short, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
MODULE_PARM_DESC(PkgCStateLimit, "Package C-State Limit");
|
|
|
|
static signed short IOMWAIT_Enable = -1;
|
|
module_param(IOMWAIT_Enable, short, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
MODULE_PARM_DESC(IOMWAIT_Enable, "I/O MWAIT Redirection Enable");
|
|
|
|
static signed short CStateIORedir = -1;
|
|
module_param(CStateIORedir, short, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
MODULE_PARM_DESC(CStateIORedir, "Power Mgmt IO Redirection C-State");
|
|
|
|
static signed short SpeedStep_Enable = -1;
|
|
module_param(SpeedStep_Enable, short, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
MODULE_PARM_DESC(SpeedStep_Enable, "Enable SpeedStep");
|
|
|
|
static signed short C1E_Enable = -1;
|
|
module_param(C1E_Enable, short, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
MODULE_PARM_DESC(C1E_Enable, "Enable SpeedStep C1E");
|
|
|
|
static signed short TurboBoost_Enable = -1;
|
|
module_param(TurboBoost_Enable, short, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
MODULE_PARM_DESC(TurboBoost_Enable, "Enable Turbo Boost");
|
|
|
|
static signed short C3A_Enable = -1;
|
|
module_param(C3A_Enable, short, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
MODULE_PARM_DESC(C3A_Enable, "Enable C3 Auto Demotion");
|
|
|
|
static signed short C1A_Enable = -1;
|
|
module_param(C1A_Enable, short, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
MODULE_PARM_DESC(C1A_Enable, "Enable C3 Auto Demotion");
|
|
|
|
static signed short C3U_Enable = -1;
|
|
module_param(C3U_Enable, short, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
MODULE_PARM_DESC(C3U_Enable, "Enable C3 UnDemotion");
|
|
|
|
static signed short C1U_Enable = -1;
|
|
module_param(C1U_Enable, short, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
MODULE_PARM_DESC(C1U_Enable, "Enable C1 UnDemotion");
|
|
|
|
static signed short ODCM_Enable = -1;
|
|
module_param(ODCM_Enable, short, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
MODULE_PARM_DESC(ODCM_Enable, "Enable On-Demand Clock Modulation");
|
|
|
|
static signed short ODCM_DutyCycle = -1;
|
|
module_param(ODCM_DutyCycle, short, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
MODULE_PARM_DESC(ODCM_DutyCycle, "ODCM DutyCycle [0-7] | [0-14]");
|
|
|
|
static signed short PowerMGMT_Unlock = -1;
|
|
module_param(PowerMGMT_Unlock, short, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
MODULE_PARM_DESC(PowerMGMT_Unlock, "Unlock Power Management");
|
|
|
|
static signed short PowerPolicy = -1;
|
|
module_param(PowerPolicy, short, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
MODULE_PARM_DESC(PowerPolicy, "Power Policy Preference [0-15]");
|
|
|
|
static signed int PState_FID = -1;
|
|
module_param(PState_FID, int, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
MODULE_PARM_DESC(PState_FID, "P-State Frequency Id");
|
|
|
|
static signed int PState_VID = -1;
|
|
module_param(PState_VID, int, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
|
MODULE_PARM_DESC(PState_VID, "P-State Voltage Id");
|
|
|
|
static PROC *Proc = NULL;
|
|
static KPUBLIC *KPublic = NULL;
|
|
static KPRIVATE *KPrivate = NULL;
|
|
static ktime_t RearmTheTimer;
|
|
|
|
|
|
unsigned int Intel_Brand(char *pBrand)
|
|
{
|
|
char idString[64] = {0x20};
|
|
unsigned long ix = 0, jx = 0, px = 0;
|
|
unsigned int frequency = 0, multiplier = 0;
|
|
BRAND Brand;
|
|
|
|
for (ix = 0; ix < 3; ix++) {
|
|
asm volatile
|
|
(
|
|
"movq %4, %%rax \n\t"
|
|
"xorq %%rbx, %%rbx \n\t"
|
|
"xorq %%rcx, %%rcx \n\t"
|
|
"xorq %%rdx, %%rdx \n\t"
|
|
"cpuid \n\t"
|
|
"mov %%eax, %0 \n\t"
|
|
"mov %%ebx, %1 \n\t"
|
|
"mov %%ecx, %2 \n\t"
|
|
"mov %%edx, %3"
|
|
: "=r" (Brand.AX),
|
|
"=r" (Brand.BX),
|
|
"=r" (Brand.CX),
|
|
"=r" (Brand.DX)
|
|
: "r" (0x80000002 + ix)
|
|
: "%rax", "%rbx", "%rcx", "%rdx"
|
|
);
|
|
for (jx = 0; jx < 4; jx++, px++) {
|
|
idString[px ] = Brand.AX.Chr[jx];
|
|
idString[px + 4] = Brand.BX.Chr[jx];
|
|
idString[px + 8] = Brand.CX.Chr[jx];
|
|
idString[px + 12] = Brand.DX.Chr[jx];
|
|
}
|
|
px += 12;
|
|
}
|
|
for (ix = 0; ix < 46; ix++)
|
|
if ((idString[ix+1] == 'H') && (idString[ix+2] == 'z')) {
|
|
switch (idString[ix]) {
|
|
case 'M':
|
|
multiplier = 1;
|
|
break;
|
|
case 'G':
|
|
multiplier = 1000;
|
|
break;
|
|
case 'T':
|
|
multiplier = 1000000;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
if (multiplier > 0) {
|
|
if (idString[ix-3] == '.') {
|
|
frequency = (int) (idString[ix-4] - '0') * multiplier;
|
|
frequency += (int) (idString[ix-2] - '0') * (multiplier / 10);
|
|
frequency += (int) (idString[ix-1] - '0') * (multiplier / 100);
|
|
} else {
|
|
frequency = (int) (idString[ix-4] - '0') * 1000;
|
|
frequency += (int) (idString[ix-3] - '0') * 100;
|
|
frequency += (int) (idString[ix-2] - '0') * 10;
|
|
frequency += (int) (idString[ix-1] - '0');
|
|
frequency *= frequency;
|
|
}
|
|
}
|
|
for (ix = jx = 0; jx < 48; jx++)
|
|
if (!(idString[jx] == 0x20 && idString[jx+1] == 0x20))
|
|
pBrand[ix++] = idString[jx];
|
|
|
|
return(frequency);
|
|
}
|
|
|
|
void AMD_Brand(char *pBrand)
|
|
{
|
|
char idString[64] = {0x20};
|
|
unsigned long ix = 0, jx = 0, px = 0;
|
|
BRAND Brand;
|
|
|
|
for (ix = 0; ix < 3; ix++) {
|
|
asm volatile
|
|
(
|
|
"movq %4, %%rax \n\t"
|
|
"xorq %%rbx, %%rbx \n\t"
|
|
"xorq %%rcx, %%rcx \n\t"
|
|
"xorq %%rdx, %%rdx \n\t"
|
|
"cpuid \n\t"
|
|
"mov %%eax, %0 \n\t"
|
|
"mov %%ebx, %1 \n\t"
|
|
"mov %%ecx, %2 \n\t"
|
|
"mov %%edx, %3"
|
|
: "=r" (Brand.AX),
|
|
"=r" (Brand.BX),
|
|
"=r" (Brand.CX),
|
|
"=r" (Brand.DX)
|
|
: "r" (0x80000002 + ix)
|
|
: "%rax", "%rbx", "%rcx", "%rdx"
|
|
);
|
|
for (jx = 0; jx < 4; jx++, px++) {
|
|
idString[px ] = Brand.AX.Chr[jx];
|
|
idString[px + 4] = Brand.BX.Chr[jx];
|
|
idString[px + 8] = Brand.CX.Chr[jx];
|
|
idString[px + 12] = Brand.DX.Chr[jx];
|
|
}
|
|
px += 12;
|
|
}
|
|
for (ix = jx = 0; jx < 48; jx++)
|
|
if (!(idString[jx] == 0x20 && idString[jx+1] == 0x20))
|
|
pBrand[ix++] = idString[jx];
|
|
}
|
|
|
|
// Retreive the Processor(BSP) features through calls to the CPUID instruction.
|
|
void Query_Features(void *pArg)
|
|
{
|
|
ARG *Arg = (ARG *) pArg;
|
|
|
|
unsigned int eax = 0x0, ebx = 0x0, ecx = 0x0, edx = 0x0; // DWORD Only!
|
|
|
|
// Must have x86 CPUID 0x0, 0x1, and Intel CPUID 0x4
|
|
asm volatile
|
|
(
|
|
"xorq %%rax, %%rax \n\t"
|
|
"xorq %%rbx, %%rbx \n\t"
|
|
"xorq %%rcx, %%rcx \n\t"
|
|
"xorq %%rdx, %%rdx \n\t"
|
|
"cpuid \n\t"
|
|
"mov %%eax, %0 \n\t"
|
|
"mov %%ebx, %1 \n\t"
|
|
"mov %%ecx, %2 \n\t"
|
|
"mov %%edx, %3"
|
|
: "=r" (Arg->Features.Info.LargestStdFunc),
|
|
"=r" (ebx),
|
|
"=r" (ecx),
|
|
"=r" (edx)
|
|
:
|
|
: "%rax", "%rbx", "%rcx", "%rdx"
|
|
);
|
|
Arg->Features.Info.Vendor.ID[ 0] = ebx;
|
|
Arg->Features.Info.Vendor.ID[ 1] = (ebx >> 8);
|
|
Arg->Features.Info.Vendor.ID[ 2] = (ebx >> 16);
|
|
Arg->Features.Info.Vendor.ID[ 3] = (ebx >> 24);
|
|
Arg->Features.Info.Vendor.ID[ 4] = edx;
|
|
Arg->Features.Info.Vendor.ID[ 5] = (edx >> 8);
|
|
Arg->Features.Info.Vendor.ID[ 6] = (edx >> 16);
|
|
Arg->Features.Info.Vendor.ID[ 7] = (edx >> 24);
|
|
Arg->Features.Info.Vendor.ID[ 8] = ecx;
|
|
Arg->Features.Info.Vendor.ID[ 9] = (ecx >> 8);
|
|
Arg->Features.Info.Vendor.ID[10] = (ecx >> 16);
|
|
Arg->Features.Info.Vendor.ID[11] = (ecx >> 24);
|
|
Arg->Features.Info.Vendor.ID[12] = '\0';
|
|
|
|
if (!strncmp(Arg->Features.Info.Vendor.ID, VENDOR_INTEL, 12))
|
|
Arg->Features.Info.Vendor.CRC = CRC_INTEL;
|
|
else if (!strncmp(Arg->Features.Info.Vendor.ID, VENDOR_AMD, 12))
|
|
Arg->Features.Info.Vendor.CRC = CRC_AMD;
|
|
else {
|
|
Arg->rc = -ENXIO;
|
|
return;
|
|
}
|
|
|
|
asm volatile
|
|
(
|
|
"movq $0x1, %%rax \n\t"
|
|
"xorq %%rbx, %%rbx \n\t"
|
|
"xorq %%rcx, %%rcx \n\t"
|
|
"xorq %%rdx, %%rdx \n\t"
|
|
"cpuid \n\t"
|
|
"mov %%eax, %0 \n\t"
|
|
"mov %%ebx, %1 \n\t"
|
|
"mov %%ecx, %2 \n\t"
|
|
"mov %%edx, %3"
|
|
: "=r" (Arg->Features.Std.EAX),
|
|
"=r" (Arg->Features.Std.EBX),
|
|
"=r" (Arg->Features.Std.ECX),
|
|
"=r" (Arg->Features.Std.EDX)
|
|
:
|
|
: "%rax", "%rbx", "%rcx", "%rdx"
|
|
);
|
|
if (Arg->Features.Info.LargestStdFunc >= 0x5) {
|
|
asm volatile
|
|
(
|
|
"movq $0x5, %%rax \n\t"
|
|
"xorq %%rbx, %%rbx \n\t"
|
|
"xorq %%rcx, %%rcx \n\t"
|
|
"xorq %%rdx, %%rdx \n\t"
|
|
"cpuid \n\t"
|
|
"mov %%eax, %0 \n\t"
|
|
"mov %%ebx, %1 \n\t"
|
|
"mov %%ecx, %2 \n\t"
|
|
"mov %%edx, %3"
|
|
: "=r" (Arg->Features.MWait.EAX),
|
|
"=r" (Arg->Features.MWait.EBX),
|
|
"=r" (Arg->Features.MWait.ECX),
|
|
"=r" (Arg->Features.MWait.EDX)
|
|
:
|
|
: "%rax", "%rbx", "%rcx", "%rdx"
|
|
);
|
|
}
|
|
if (Arg->Features.Info.LargestStdFunc >= 0x6) {
|
|
asm volatile
|
|
(
|
|
"movq $0x6, %%rax \n\t"
|
|
"xorq %%rbx, %%rbx \n\t"
|
|
"xorq %%rcx, %%rcx \n\t"
|
|
"xorq %%rdx, %%rdx \n\t"
|
|
"cpuid \n\t"
|
|
"mov %%eax, %0 \n\t"
|
|
"mov %%ebx, %1 \n\t"
|
|
"mov %%ecx, %2 \n\t"
|
|
"mov %%edx, %3"
|
|
: "=r" (Arg->Features.Power.EAX),
|
|
"=r" (Arg->Features.Power.EBX),
|
|
"=r" (Arg->Features.Power.ECX),
|
|
"=r" (Arg->Features.Power.EDX)
|
|
:
|
|
: "%rax", "%rbx", "%rcx", "%rdx"
|
|
);
|
|
}
|
|
if (Arg->Features.Info.LargestStdFunc >= 0x7) {
|
|
asm volatile
|
|
(
|
|
"movq $0x7, %%rax \n\t"
|
|
"xorq %%rbx, %%rbx \n\t"
|
|
"xorq %%rcx, %%rcx \n\t"
|
|
"xorq %%rdx, %%rdx \n\t"
|
|
"cpuid \n\t"
|
|
"mov %%eax, %0 \n\t"
|
|
"mov %%ebx, %1 \n\t"
|
|
"mov %%ecx, %2 \n\t"
|
|
"mov %%edx, %3"
|
|
: "=r" (Arg->Features.ExtFeature.EAX),
|
|
"=r" (Arg->Features.ExtFeature.EBX),
|
|
"=r" (Arg->Features.ExtFeature.ECX),
|
|
"=r" (Arg->Features.ExtFeature.EDX)
|
|
:
|
|
: "%rax", "%rbx", "%rcx", "%rdx"
|
|
);
|
|
}
|
|
// Must have 0x80000000, 0x80000001, 0x80000002, 0x80000003, 0x80000004
|
|
asm volatile
|
|
(
|
|
"movq $0x80000000, %%rax \n\t"
|
|
"xorq %%rbx, %%rbx \n\t"
|
|
"xorq %%rcx, %%rcx \n\t"
|
|
"xorq %%rdx, %%rdx \n\t"
|
|
"cpuid \n\t"
|
|
"mov %%eax, %0 \n\t"
|
|
"mov %%ebx, %1 \n\t"
|
|
"mov %%ecx, %2 \n\t"
|
|
"mov %%edx, %3"
|
|
: "=r" (Arg->Features.Info.LargestExtFunc),
|
|
"=r" (ebx),
|
|
"=r" (ecx),
|
|
"=r" (edx)
|
|
:
|
|
: "%rax", "%rbx", "%rcx", "%rdx"
|
|
);
|
|
asm volatile
|
|
(
|
|
"movq $0x80000001, %%rax \n\t"
|
|
"xorq %%rbx, %%rbx \n\t"
|
|
"xorq %%rcx, %%rcx \n\t"
|
|
"xorq %%rdx, %%rdx \n\t"
|
|
"cpuid \n\t"
|
|
"mov %%eax, %0 \n\t"
|
|
"mov %%ebx, %1 \n\t"
|
|
"mov %%ecx, %2 \n\t"
|
|
"mov %%edx, %3"
|
|
: "=r" (eax),
|
|
"=r" (ebx),
|
|
"=r" (Arg->Features.ExtInfo.ECX),
|
|
"=r" (Arg->Features.ExtInfo.EDX)
|
|
:
|
|
: "%rax", "%rbx", "%rcx", "%rdx"
|
|
);
|
|
if (Arg->Features.Info.LargestExtFunc >= 0x80000007) {
|
|
asm volatile
|
|
(
|
|
"movq $0x80000007, %%rax \n\t"
|
|
"xorq %%rbx, %%rbx \n\t"
|
|
"xorq %%rcx, %%rcx \n\t"
|
|
"xorq %%rdx, %%rdx \n\t"
|
|
"cpuid \n\t"
|
|
"mov %%eax, %0 \n\t"
|
|
"mov %%ebx, %1 \n\t"
|
|
"mov %%ecx, %2 \n\t"
|
|
"mov %%edx, %3"
|
|
: "=r" (Arg->Features.AdvPower.EAX),
|
|
"=r" (Arg->Features.AdvPower.EBX),
|
|
"=r" (Arg->Features.AdvPower.ECX),
|
|
"=r" (Arg->Features.AdvPower.EDX)
|
|
:
|
|
: "%rax", "%rbx", "%rcx", "%rdx"
|
|
);
|
|
}
|
|
|
|
// Reset the performance features bits (present is zero)
|
|
Arg->Features.PerfMon.EBX.CoreCycles = 1;
|
|
Arg->Features.PerfMon.EBX.InstrRetired = 1;
|
|
Arg->Features.PerfMon.EBX.RefCycles = 1;
|
|
Arg->Features.PerfMon.EBX.LLC_Ref = 1;
|
|
Arg->Features.PerfMon.EBX.LLC_Misses = 1;
|
|
Arg->Features.PerfMon.EBX.BranchRetired = 1;
|
|
Arg->Features.PerfMon.EBX.BranchMispred = 1;
|
|
|
|
// Per Vendor features
|
|
if (Arg->Features.Info.Vendor.CRC == CRC_INTEL) {
|
|
asm volatile
|
|
(
|
|
"movq $0x4, %%rax \n\t"
|
|
"xorq %%rbx, %%rbx \n\t"
|
|
"xorq %%rcx, %%rcx \n\t"
|
|
"xorq %%rdx, %%rdx \n\t"
|
|
"cpuid \n\t"
|
|
"mov %%eax, %0 \n\t"
|
|
"mov %%ebx, %1 \n\t"
|
|
"mov %%ecx, %2 \n\t"
|
|
"mov %%edx, %3"
|
|
: "=r" (eax),
|
|
"=r" (ebx),
|
|
"=r" (ecx),
|
|
"=r" (edx)
|
|
:
|
|
: "%rax", "%rbx", "%rcx", "%rdx"
|
|
);
|
|
Arg->SMT_Count = (eax >> 26) & 0x3f;
|
|
Arg->SMT_Count++;
|
|
|
|
if (Arg->Features.Info.LargestStdFunc >= 0xa) {
|
|
asm volatile
|
|
(
|
|
"movq $0xa, %%rax \n\t"
|
|
"xorq %%rbx, %%rbx \n\t"
|
|
"xorq %%rcx, %%rcx \n\t"
|
|
"xorq %%rdx, %%rdx \n\t"
|
|
"cpuid \n\t"
|
|
"mov %%eax, %0 \n\t"
|
|
"mov %%ebx, %1 \n\t"
|
|
"mov %%ecx, %2 \n\t"
|
|
"mov %%edx, %3"
|
|
: "=r" (Arg->Features.PerfMon.EAX),
|
|
"=r" (Arg->Features.PerfMon.EBX),
|
|
"=r" (Arg->Features.PerfMon.ECX),
|
|
"=r" (Arg->Features.PerfMon.EDX)
|
|
:
|
|
: "%rax", "%rbx", "%rcx", "%rdx"
|
|
);
|
|
}
|
|
Arg->Features.FactoryFreq = Intel_Brand(Arg->Features.Info.Brand);
|
|
|
|
} else if (Arg->Features.Info.Vendor.CRC == CRC_AMD) {
|
|
|
|
if (Arg->Features.Std.EDX.HTT)
|
|
Arg->SMT_Count = Arg->Features.Std.EBX.MaxThread;
|
|
else {
|
|
if (Arg->Features.Info.LargestExtFunc >= 0x80000008) {
|
|
asm volatile
|
|
(
|
|
"movq $0x80000008, %%rax \n\t"
|
|
"xorq %%rbx, %%rbx \n\t"
|
|
"xorq %%rcx, %%rcx \n\t"
|
|
"xorq %%rdx, %%rdx \n\t"
|
|
"cpuid \n\t"
|
|
"mov %%eax, %0 \n\t"
|
|
"mov %%ebx, %1 \n\t"
|
|
"mov %%ecx, %2 \n\t"
|
|
"mov %%edx, %3"
|
|
: "=r" (eax),
|
|
"=r" (ebx),
|
|
"=r" (ecx),
|
|
"=r" (edx)
|
|
:
|
|
: "%rax", "%rbx", "%rcx", "%rdx"
|
|
);
|
|
Arg->SMT_Count = (ecx & 0xf) + 1;
|
|
}
|
|
}
|
|
AMD_Brand(Arg->Features.Info.Brand);
|
|
}
|
|
}
|
|
|
|
|
|
typedef struct { // V[0] stores previous TSC
|
|
unsigned long long V[2]; // V[1] stores current TSC
|
|
} TSC_STRUCT;
|
|
|
|
#define OCCURRENCES 4
|
|
// OCCURRENCES x 2 (TSC values) needs a 64-byte cache line size.
|
|
#define STRUCT_SIZE (OCCURRENCES * sizeof(TSC_STRUCT))
|
|
|
|
void Compute_Clock(void *arg)
|
|
{
|
|
CLOCK *clock = (CLOCK *) arg;
|
|
unsigned int ratio = clock->Q;
|
|
struct kmem_cache *hardwareCache = NULL;
|
|
/*
|
|
TSC[0] stores the overhead
|
|
TSC[1] stores the estimation
|
|
*/
|
|
TSC_STRUCT *TSC[2] = {NULL, NULL};
|
|
unsigned long long D[2][OCCURRENCES];
|
|
unsigned int loop = 0, what = 0, best[2] = {0, 0}, top[2] = {0, 0};
|
|
|
|
void ComputeWithSerializedTSC(void)
|
|
{
|
|
// No preemption, no interrupt.
|
|
unsigned long flags;
|
|
preempt_disable();
|
|
raw_local_irq_save(flags);
|
|
|
|
// Warm-up & Overhead
|
|
for (loop = 0; loop < OCCURRENCES; loop++) {
|
|
RDTSCP64(TSC[0][loop].V[0]);
|
|
|
|
udelay(0);
|
|
|
|
RDTSCP64(TSC[0][loop].V[1]);
|
|
}
|
|
|
|
// Estimation
|
|
for (loop=0; loop < OCCURRENCES; loop++) {
|
|
RDTSCP64(TSC[1][loop].V[0]);
|
|
|
|
udelay(1000);
|
|
|
|
RDTSCP64(TSC[1][loop].V[1]);
|
|
}
|
|
// Restore preemption and interrupt.
|
|
raw_local_irq_restore(flags);
|
|
preempt_enable();
|
|
}
|
|
|
|
void ComputeWithUnSerializedTSC(void)
|
|
{
|
|
// No preemption, no interrupt.
|
|
unsigned long flags;
|
|
preempt_disable();
|
|
raw_local_irq_save(flags);
|
|
|
|
// Warm-up & Overhead
|
|
for (loop=0; loop < OCCURRENCES; loop++) {
|
|
RDTSC64(TSC[0][loop].V[0]);
|
|
|
|
udelay(0);
|
|
|
|
RDTSC64(TSC[0][loop].V[1]);
|
|
}
|
|
// Estimation
|
|
for (loop = 0; loop < OCCURRENCES; loop++) {
|
|
RDTSC64(TSC[1][loop].V[0]);
|
|
|
|
udelay(1000);
|
|
|
|
RDTSC64(TSC[1][loop].V[1]);
|
|
}
|
|
// Restore preemption and interrupt.
|
|
raw_local_irq_restore(flags);
|
|
preempt_enable();
|
|
}
|
|
|
|
// Allocate Cache aligned resources.
|
|
hardwareCache = kmem_cache_create("CoreFreqCache",
|
|
STRUCT_SIZE, 0,
|
|
SLAB_HWCACHE_ALIGN, NULL);
|
|
if (hardwareCache != NULL) {
|
|
TSC[0] = kmem_cache_alloc(hardwareCache, GFP_KERNEL);
|
|
if (TSC[0] != NULL) {
|
|
TSC[1] = kmem_cache_alloc(hardwareCache, GFP_KERNEL);
|
|
if (TSC[1] != NULL) {
|
|
|
|
// Is the TSC invariant or a serialized read instruction is available ?
|
|
if ( (Proc->Features.AdvPower.EDX.Inv_TSC == 1)
|
|
|| (Proc->Features.ExtInfo.EDX.RDTSCP == 1))
|
|
ComputeWithSerializedTSC();
|
|
else
|
|
ComputeWithUnSerializedTSC();
|
|
|
|
// Select the best clock.
|
|
memset(D, 0, 2 * OCCURRENCES);
|
|
for (loop = 0; loop < OCCURRENCES; loop++)
|
|
for (what = 0; what < 2; what++) {
|
|
D[what][loop] = TSC[what][loop].V[1]
|
|
- TSC[what][loop].V[0];
|
|
}
|
|
for (loop = 0; loop < OCCURRENCES; loop++) {
|
|
unsigned int inner = 0, count[2] = {0, 0};
|
|
for (inner = loop; inner < OCCURRENCES; inner++) {
|
|
for (what = 0; what < 2; what++) {
|
|
if (D[what][loop] == D[what][inner])
|
|
count[what]++;
|
|
}
|
|
}
|
|
for (what = 0; what < 2; what++) {
|
|
if ((count[what] > top[what])
|
|
|| ((count[what] == top[what])
|
|
&& (D[what][loop] < D[what][best[what]]))) {
|
|
|
|
top[what] = count[what];
|
|
best[what] = loop;
|
|
|
|
}
|
|
}
|
|
}
|
|
// Substract the overhead.
|
|
D[1][best[1]] -= D[0][best[0]];
|
|
D[1][best[1]] *= 1000;
|
|
// Compute Divisor and Remainder.
|
|
clock->Q = D[1][best[1]] / (1000000L * ratio);
|
|
clock->R = D[1][best[1]] % (1000000L * ratio);
|
|
// Compute full Hertz.
|
|
clock->Hz = D[1][best[1]] / ratio;
|
|
clock->Hz += D[1][best[1]] % ratio;
|
|
// Release resources.
|
|
kmem_cache_free(hardwareCache, TSC[1]);
|
|
}
|
|
kmem_cache_free(hardwareCache, TSC[0]);
|
|
}
|
|
kmem_cache_destroy(hardwareCache);
|
|
}
|
|
}
|
|
|
|
CLOCK Base_Clock(unsigned int cpu, unsigned int ratio)
|
|
{
|
|
CLOCK clock = {.Q = ratio, .R = 0, .Hz = 0};
|
|
|
|
smp_call_function_single(cpu,
|
|
Compute_Clock,
|
|
&clock,
|
|
1); // Synchronous call.
|
|
return(clock);
|
|
}
|
|
|
|
void ClockToHz(CLOCK *clock)
|
|
{
|
|
clock->Hz = clock->Q * 1000000L;
|
|
clock->Hz += clock->R * PRECISION;
|
|
}
|
|
|
|
// [Genuine Intel]
|
|
CLOCK Clock_GenuineIntel(unsigned int ratio)
|
|
{
|
|
CLOCK clock = {.Q = 100, .R = 0, .Hz = 100000000L};
|
|
|
|
if (Proc->Features.FactoryFreq > 0) {
|
|
clock.Hz = (Proc->Features.FactoryFreq * 1000000L) / ratio;
|
|
clock.Q = Proc->Features.FactoryFreq / ratio;
|
|
clock.R = (Proc->Features.FactoryFreq % ratio) * PRECISION;
|
|
}
|
|
return(clock);
|
|
};
|
|
|
|
// [Authentic AMD]
|
|
CLOCK Clock_AuthenticAMD(unsigned int ratio)
|
|
{
|
|
CLOCK clock = {.Q = 100, .R = 0, .Hz = 100000000L}; // AMD assumption
|
|
return(clock);
|
|
};
|
|
|
|
// [Core]
|
|
CLOCK Clock_Core(unsigned int ratio)
|
|
{
|
|
CLOCK clock = {.Q = 100, .R = 0};
|
|
FSB_FREQ FSB={.value = 0};
|
|
|
|
RDMSR(FSB, MSR_FSB_FREQ);
|
|
switch(FSB.Bus_Speed) {
|
|
case 0b101: {
|
|
clock.Q = 100;
|
|
clock.R = 0;
|
|
};
|
|
break;
|
|
case 0b001: {
|
|
clock.Q = 133;
|
|
clock.R = 3333;
|
|
}
|
|
break;
|
|
case 0b011: {
|
|
clock.Q = 166;
|
|
clock.R = 6666;
|
|
}
|
|
break;
|
|
}
|
|
ClockToHz(&clock);
|
|
clock.R *= ratio;
|
|
return(clock);
|
|
};
|
|
|
|
// [Core2]
|
|
CLOCK Clock_Core2(unsigned int ratio)
|
|
{
|
|
CLOCK clock = {.Q = 100, .R = 0};
|
|
FSB_FREQ FSB={.value = 0};
|
|
|
|
RDMSR(FSB, MSR_FSB_FREQ);
|
|
switch(FSB.Bus_Speed) {
|
|
case 0b101: {
|
|
clock.Q = 100;
|
|
clock.R = 0;
|
|
}
|
|
break;
|
|
case 0b001: {
|
|
clock.Q = 133;
|
|
clock.R = 3333;
|
|
}
|
|
break;
|
|
case 0b011: {
|
|
clock.Q = 166;
|
|
clock.R = 6666;
|
|
}
|
|
break;
|
|
case 0b010: {
|
|
clock.Q = 200;
|
|
clock.R = 0;
|
|
}
|
|
break;
|
|
case 0b000: {
|
|
clock.Q = 266;
|
|
clock.R = 6666;
|
|
}
|
|
break;
|
|
case 0b100: {
|
|
clock.Q = 333;
|
|
clock.R = 3333;
|
|
}
|
|
break;
|
|
case 0b110: {
|
|
clock.Q = 400;
|
|
clock.R = 0;
|
|
}
|
|
break;
|
|
}
|
|
ClockToHz(&clock);
|
|
clock.R *= ratio;
|
|
return(clock);
|
|
};
|
|
|
|
// [Atom]
|
|
CLOCK Clock_Atom(unsigned int ratio)
|
|
{
|
|
CLOCK clock = {.Q = 83, .R = 0};
|
|
FSB_FREQ FSB = {.value = 0};
|
|
|
|
RDMSR(FSB, MSR_FSB_FREQ);
|
|
switch(FSB.Bus_Speed) {
|
|
case 0b111: {
|
|
clock.Q = 83;
|
|
clock.R = 2000;
|
|
}
|
|
break;
|
|
case 0b101: {
|
|
clock.Q = 99;
|
|
clock.R = 8400;
|
|
}
|
|
break;
|
|
case 0b001: {
|
|
clock.Q = 133;
|
|
clock.R = 2000;
|
|
}
|
|
break;
|
|
case 0b011: {
|
|
clock.Q = 166;
|
|
clock.R = 4000;
|
|
}
|
|
break;
|
|
default: {
|
|
clock.Q = 83;
|
|
clock.R = 2000;
|
|
}
|
|
break;
|
|
}
|
|
ClockToHz(&clock);
|
|
clock.R *= ratio;
|
|
return(clock);
|
|
};
|
|
|
|
// [Airmont]
|
|
CLOCK Clock_Airmont(unsigned int ratio)
|
|
{
|
|
CLOCK clock = {.Q = 87, .R = 5};
|
|
FSB_FREQ FSB = {.value = 0};
|
|
|
|
RDMSR(FSB, MSR_FSB_FREQ);
|
|
switch(FSB.Bus_Speed) {
|
|
case 0b000: {
|
|
clock.Q = 83;
|
|
clock.R = 3333;
|
|
}
|
|
break;
|
|
case 0b001: {
|
|
clock.Q = 100;
|
|
clock.R = 0000;
|
|
}
|
|
break;
|
|
case 0b010: {
|
|
clock.Q = 133;
|
|
clock.R = 3333;
|
|
}
|
|
break;
|
|
case 0b011: {
|
|
clock.Q = 116;
|
|
clock.R = 6666;
|
|
}
|
|
break;
|
|
case 0b100: {
|
|
clock.Q = 80;
|
|
clock.R = 0000;
|
|
}
|
|
break;
|
|
case 0b101: {
|
|
clock.Q = 93;
|
|
clock.R = 3333;
|
|
}
|
|
break;
|
|
case 0b110: {
|
|
clock.Q = 90;
|
|
clock.R = 0000;
|
|
}
|
|
break;
|
|
case 0b111: {
|
|
clock.Q = 88;
|
|
clock.R = 9000;
|
|
}
|
|
break;
|
|
}
|
|
ClockToHz(&clock);
|
|
clock.R *= ratio;
|
|
return(clock);
|
|
};
|
|
|
|
// [Silvermont]
|
|
CLOCK Clock_Silvermont(unsigned int ratio)
|
|
{
|
|
CLOCK clock = {.Q = 83, .R = 3};
|
|
FSB_FREQ FSB = {.value = 0};
|
|
|
|
RDMSR(FSB, MSR_FSB_FREQ);
|
|
switch(FSB.Bus_Speed)
|
|
{
|
|
case 0b100: {
|
|
clock.Q = 80;
|
|
clock.R = 0;
|
|
}
|
|
break;
|
|
case 0b000: {
|
|
clock.Q = 83;
|
|
clock.R = 3000;
|
|
}
|
|
break;
|
|
case 0b001: {
|
|
clock.Q = 100;
|
|
clock.R = 0;
|
|
}
|
|
break;
|
|
case 0b010: {
|
|
clock.Q = 133;
|
|
clock.R = 3333;
|
|
}
|
|
break;
|
|
case 0b011: {
|
|
clock.Q = 116;
|
|
clock.R = 7000;
|
|
}
|
|
break;
|
|
}
|
|
ClockToHz(&clock);
|
|
clock.R *= ratio;
|
|
return(clock);
|
|
};
|
|
|
|
// [Nehalem]
|
|
CLOCK Clock_Nehalem(unsigned int ratio)
|
|
{
|
|
CLOCK clock = {.Q = 133, .R = 3333};
|
|
ClockToHz(&clock);
|
|
clock.R *= ratio;
|
|
return(clock);
|
|
};
|
|
|
|
// [Westmere]
|
|
CLOCK Clock_Westmere(unsigned int ratio)
|
|
{
|
|
CLOCK clock = {.Q = 133, .R = 3333};
|
|
ClockToHz(&clock);
|
|
clock.R *= ratio;
|
|
return(clock);
|
|
};
|
|
|
|
// [SandyBridge]
|
|
CLOCK Clock_SandyBridge(unsigned int ratio)
|
|
{
|
|
CLOCK clock = {.Q = 100, .R = 0};
|
|
ClockToHz(&clock);
|
|
clock.R *= ratio;
|
|
return(clock);
|
|
};
|
|
|
|
// [IvyBridge]
|
|
CLOCK Clock_IvyBridge(unsigned int ratio)
|
|
{
|
|
CLOCK clock = {.Q = 100, .R = 0};
|
|
ClockToHz(&clock);
|
|
clock.R *= ratio;
|
|
return(clock);
|
|
};
|
|
|
|
// [Haswell]
|
|
CLOCK Clock_Haswell(unsigned int ratio)
|
|
{
|
|
CLOCK clock = {.Q = 100, .R = 0};
|
|
ClockToHz(&clock);
|
|
clock.R *= ratio;
|
|
return(clock);
|
|
};
|
|
|
|
// [Skylake]
|
|
CLOCK Clock_Skylake(unsigned int ratio)
|
|
{
|
|
CLOCK clock = {.Q = 100, .R = 0};
|
|
|
|
if (Proc->Features.Info.LargestStdFunc >= 0x16) {
|
|
unsigned int eax = 0x0, ebx = 0x0, edx = 0x0, fsb = 0;
|
|
asm volatile
|
|
(
|
|
"movq $0x16, %%rax \n\t"
|
|
"xorq %%rbx, %%rbx \n\t"
|
|
"xorq %%rcx, %%rcx \n\t"
|
|
"xorq %%rdx, %%rdx \n\t"
|
|
"cpuid \n\t"
|
|
"mov %%eax, %0 \n\t"
|
|
"mov %%ebx, %1 \n\t"
|
|
"mov %%ecx, %2 \n\t"
|
|
"mov %%edx, %3"
|
|
: "=r" (eax),
|
|
"=r" (ebx),
|
|
"=r" (fsb),
|
|
"=r" (edx)
|
|
:
|
|
: "%rax", "%rbx", "%rcx", "%rdx"
|
|
);
|
|
if (fsb > 0)
|
|
clock.Q = fsb;
|
|
else
|
|
clock.Q = 100;
|
|
}
|
|
ClockToHz(&clock);
|
|
clock.R *= ratio;
|
|
return(clock);
|
|
};
|
|
|
|
void Define_CPUID(CORE *Core, const CPUID_STRUCT CpuIDforVendor[])
|
|
{ // Per vendor, define a CPUID dump table to query.
|
|
int i;
|
|
for (i = 0; i < CPUID_MAX_FUNC; i++) {
|
|
Core->CpuID[i].func = CpuIDforVendor[i].func;
|
|
Core->CpuID[i].sub = CpuIDforVendor[i].sub;
|
|
}
|
|
}
|
|
|
|
void Cache_Topology(CORE *Core)
|
|
{
|
|
unsigned long level = 0x0;
|
|
if (Proc->Features.Info.Vendor.CRC == CRC_INTEL) {
|
|
for (level = 0; level < CACHE_MAX_LEVEL; level++) {
|
|
asm volatile
|
|
(
|
|
"movq $0x4, %%rax \n\t"
|
|
"xorq %%rbx, %%rbx \n\t"
|
|
"movq %4, %%rcx \n\t"
|
|
"xorq %%rdx, %%rdx \n\t"
|
|
"cpuid \n\t"
|
|
"mov %%eax, %0 \n\t"
|
|
"mov %%ebx, %1 \n\t"
|
|
"mov %%ecx, %2 \n\t"
|
|
"mov %%edx, %3"
|
|
: "=r" (Core->T.Cache[level].AX),
|
|
"=r" (Core->T.Cache[level].BX),
|
|
"=r" (Core->T.Cache[level].Set),
|
|
"=r" (Core->T.Cache[level].DX)
|
|
: "r" (level)
|
|
: "%rax", "%rbx", "%rcx", "%rdx"
|
|
);
|
|
if (!Core->T.Cache[level].Type)
|
|
break;
|
|
}
|
|
}
|
|
else if (Proc->Features.Info.Vendor.CRC == CRC_AMD) {
|
|
struct CACHE_INFO CacheInfo; // Employ the Intel algorithm.
|
|
|
|
if (Proc->Features.Info.LargestExtFunc >= 0x80000005) {
|
|
Core->T.Cache[0].Level = 1;
|
|
Core->T.Cache[0].Type = 2; // Inst.
|
|
Core->T.Cache[1].Level = 1;
|
|
Core->T.Cache[1].Type = 1; // Data
|
|
|
|
// Fn8000_0005 L1 Data and Inst. caches
|
|
asm volatile
|
|
(
|
|
"movq $0x80000005, %%rax \n\t"
|
|
"xorq %%rbx, %%rbx \n\t"
|
|
"xorq %%rcx, %%rcx \n\t"
|
|
"xorq %%rdx, %%rdx \n\t"
|
|
"cpuid \n\t"
|
|
"mov %%eax, %0 \n\t"
|
|
"mov %%ebx, %1 \n\t"
|
|
"mov %%ecx, %2 \n\t"
|
|
"mov %%edx, %3"
|
|
: "=r" (CacheInfo.AX),
|
|
"=r" (CacheInfo.BX),
|
|
"=r" (CacheInfo.CX),
|
|
"=r" (CacheInfo.DX)
|
|
:
|
|
: "%rax", "%rbx", "%rcx", "%rdx"
|
|
);
|
|
// L1 Inst.
|
|
Core->T.Cache[0].Way = CacheInfo.CPUID_0x80000005_L1I.Assoc;
|
|
Core->T.Cache[0].Size = CacheInfo.CPUID_0x80000005_L1I.Size;
|
|
// L1 Data
|
|
Core->T.Cache[1].Way = CacheInfo.CPUID_0x80000005_L1D.Assoc;
|
|
Core->T.Cache[1].Size = CacheInfo.CPUID_0x80000005_L1D.Size;
|
|
}
|
|
if (Proc->Features.Info.LargestExtFunc >= 0x80000006) {
|
|
Core->T.Cache[2].Level = 2;
|
|
Core->T.Cache[2].Type = 3; // Unified!
|
|
Core->T.Cache[3].Level = 3;
|
|
Core->T.Cache[3].Type = 3;
|
|
|
|
// Fn8000_0006 L2 and L3 caches
|
|
asm volatile
|
|
(
|
|
"movq $0x80000006, %%rax \n\t"
|
|
"xorq %%rbx, %%rbx \n\t"
|
|
"xorq %%rcx, %%rcx \n\t"
|
|
"xorq %%rdx, %%rdx \n\t"
|
|
"cpuid \n\t"
|
|
"mov %%eax, %0 \n\t"
|
|
"mov %%ebx, %1 \n\t"
|
|
"mov %%ecx, %2 \n\t"
|
|
"mov %%edx, %3"
|
|
: "=r" (CacheInfo.AX),
|
|
"=r" (CacheInfo.BX),
|
|
"=r" (CacheInfo.CX),
|
|
"=r" (CacheInfo.DX)
|
|
:
|
|
: "%rax", "%rbx", "%rcx", "%rdx"
|
|
);
|
|
// L2
|
|
Core->T.Cache[2].Way = CacheInfo.CPUID_0x80000006_L2.Assoc;
|
|
Core->T.Cache[2].Size = CacheInfo.CPUID_0x80000006_L2.Size;
|
|
// L3
|
|
Core->T.Cache[3].Way = CacheInfo.CPUID_0x80000006_L3.Assoc;
|
|
Core->T.Cache[3].Size = CacheInfo.CPUID_0x80000006_L3.Size;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Enumerate the Processor's Cores and Threads topology.
|
|
void Map_Topology(void *arg)
|
|
{
|
|
if (arg != NULL) {
|
|
unsigned int eax = 0x0, ecx = 0x0, edx = 0x0;
|
|
CORE *Core = (CORE *) arg;
|
|
FEATURES features;
|
|
|
|
RDMSR(Core->T.Base, MSR_IA32_APICBASE);
|
|
|
|
asm volatile
|
|
(
|
|
"movq $0x1, %%rax \n\t"
|
|
"xorq %%rbx, %%rbx \n\t"
|
|
"xorq %%rcx, %%rcx \n\t"
|
|
"xorq %%rdx, %%rdx \n\t"
|
|
"cpuid \n\t"
|
|
"mov %%eax, %0 \n\t"
|
|
"mov %%ebx, %1 \n\t"
|
|
"mov %%ecx, %2 \n\t"
|
|
"mov %%edx, %3"
|
|
: "=r" (eax),
|
|
"=r" (features.Std.EBX),
|
|
"=r" (ecx),
|
|
"=r" (edx)
|
|
:
|
|
: "%rax", "%rbx", "%rcx", "%rdx"
|
|
);
|
|
|
|
Core->T.CoreID = Core->T.ApicID = features.Std.EBX.Apic_ID;
|
|
|
|
Cache_Topology(Core);
|
|
}
|
|
}
|
|
|
|
void Map_Extended_Topology(void *arg)
|
|
{
|
|
if (arg != NULL) {
|
|
CORE *Core = (CORE *) arg;
|
|
|
|
long InputLevel = 0;
|
|
int NoMoreLevels = 0,
|
|
SMT_Mask_Width = 0, SMT_Select_Mask = 0,
|
|
CorePlus_Mask_Width = 0, CoreOnly_Select_Mask = 0;
|
|
|
|
CPUID_TOPOLOGY_LEAF ExtTopology = {
|
|
.AX.Register = 0,
|
|
.BX.Register = 0,
|
|
.CX.Register = 0,
|
|
.DX.Register = 0
|
|
};
|
|
|
|
RDMSR(Core->T.Base, MSR_IA32_APICBASE);
|
|
|
|
do {
|
|
asm volatile
|
|
(
|
|
"movq $0xb, %%rax \n\t"
|
|
"xorq %%rbx, %%rbx \n\t"
|
|
"movq %4, %%rcx \n\t"
|
|
"xorq %%rdx, %%rdx \n\t"
|
|
"cpuid \n\t"
|
|
"mov %%eax, %0 \n\t"
|
|
"mov %%ebx, %1 \n\t"
|
|
"mov %%ecx, %2 \n\t"
|
|
"mov %%edx, %3"
|
|
: "=r" (ExtTopology.AX),
|
|
"=r" (ExtTopology.BX),
|
|
"=r" (ExtTopology.CX),
|
|
"=r" (ExtTopology.DX)
|
|
: "r" (InputLevel)
|
|
: "%rax", "%rbx", "%rcx", "%rdx"
|
|
);
|
|
// Exit from the loop if the BX register equals 0 or
|
|
// if the requested level exceeds the level of a Core.
|
|
if ( !ExtTopology.BX.Register
|
|
|| (InputLevel > LEVEL_CORE))
|
|
NoMoreLevels = 1;
|
|
else {
|
|
switch (ExtTopology.CX.Type) {
|
|
case LEVEL_THREAD: {
|
|
SMT_Mask_Width = ExtTopology.AX.SHRbits;
|
|
|
|
SMT_Select_Mask = ~((-1) << SMT_Mask_Width);
|
|
|
|
Core->T.ThreadID = ExtTopology.DX.x2ApicID
|
|
& SMT_Select_Mask;
|
|
}
|
|
break;
|
|
case LEVEL_CORE: {
|
|
CorePlus_Mask_Width = ExtTopology.AX.SHRbits;
|
|
|
|
CoreOnly_Select_Mask =
|
|
(~((-1) << CorePlus_Mask_Width))
|
|
^ SMT_Select_Mask;
|
|
|
|
Core->T.CoreID = (ExtTopology.DX.x2ApicID
|
|
& CoreOnly_Select_Mask) >> SMT_Mask_Width;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
InputLevel++;
|
|
} while (!NoMoreLevels);
|
|
|
|
Core->T.ApicID = ExtTopology.DX.x2ApicID;
|
|
|
|
Cache_Topology(Core);
|
|
}
|
|
}
|
|
|
|
int Core_Topology(unsigned int cpu)
|
|
{
|
|
int rc = smp_call_function_single(cpu,
|
|
(Proc->Features.Info.LargestStdFunc >= 0xb) ?
|
|
Map_Extended_Topology : Map_Topology,
|
|
KPublic->Core[cpu],
|
|
1); // Synchronous call.
|
|
|
|
if ( !rc
|
|
&& !Proc->Features.HTT_Enable
|
|
&& (KPublic->Core[cpu]->T.ThreadID > 0))
|
|
Proc->Features.HTT_Enable = 1;
|
|
|
|
return(rc);
|
|
}
|
|
|
|
unsigned int Proc_Topology(void)
|
|
{
|
|
unsigned int cpu, CountEnabledCPU = 0;
|
|
|
|
for (cpu = 0; cpu < Proc->CPU.Count; cpu++) {
|
|
unsigned long long OS_State=!cpu_online(cpu)||!cpu_active(cpu);
|
|
|
|
KPublic->Core[cpu]->T.Base.value = 0;
|
|
KPublic->Core[cpu]->T.ApicID = -1;
|
|
KPublic->Core[cpu]->T.CoreID = -1;
|
|
KPublic->Core[cpu]->T.ThreadID = -1;
|
|
|
|
// CPU state based on the OS
|
|
if (!OS_State) {
|
|
BITCLR(LOCKLESS, KPublic->Core[cpu]->OffLine, OS);
|
|
if (!Core_Topology(cpu)) {
|
|
// CPU state based on the hardware
|
|
if (KPublic->Core[cpu]->T.ApicID >= 0) {
|
|
BITCLR(LOCKLESS,KPublic->Core[cpu]->OffLine,HW);
|
|
CountEnabledCPU++;
|
|
}
|
|
else
|
|
BITSET(LOCKLESS,KPublic->Core[cpu]->OffLine,HW);
|
|
}
|
|
} else
|
|
BITSET(LOCKLESS, KPublic->Core[cpu]->OffLine, OS);
|
|
}
|
|
return(CountEnabledCPU);
|
|
}
|
|
|
|
void HyperThreading_Technology(void)
|
|
{
|
|
unsigned int CountEnabledCPU = Proc_Topology();
|
|
|
|
if (Proc->Features.Std.EDX.HTT)
|
|
Proc->CPU.OnLine = CountEnabledCPU;
|
|
else
|
|
Proc->CPU.OnLine = Proc->CPU.Count;
|
|
}
|
|
|
|
int Intel_MaxBusRatio(PLATFORM_ID *PfID)
|
|
{
|
|
struct SIGNATURE whiteList[] = {
|
|
_Core_Conroe, /* 06_0F */
|
|
_Core_Yorkfield, /* 06_17 */
|
|
_Atom_Bonnell, /* 06_1C */
|
|
_Atom_Silvermont, /* 06_26 */
|
|
_Atom_Lincroft, /* 06_27 */
|
|
_Atom_Clovertrail, /* 06_35 */
|
|
_Atom_Saltwell, /* 06_36 */
|
|
_Silvermont_637, /* 06_37 */
|
|
};
|
|
int id, ids = sizeof(whiteList) / sizeof(whiteList[0]);
|
|
for (id = 0; id < ids; id++) {
|
|
if((whiteList[id].ExtFamily == Proc->Features.Std.EAX.ExtFamily)
|
|
&& (whiteList[id].Family == Proc->Features.Std.EAX.Family)
|
|
&& (whiteList[id].ExtModel == Proc->Features.Std.EAX.ExtModel)
|
|
&& (whiteList[id].Model == Proc->Features.Std.EAX.Model)) {
|
|
|
|
RDMSR((*PfID), MSR_IA32_PLATFORM_ID);
|
|
return(0);
|
|
}
|
|
}
|
|
return(-1);
|
|
}
|
|
|
|
void Intel_Platform_Info(void)
|
|
{
|
|
PLATFORM_ID PfID = {.value = 0};
|
|
PLATFORM_INFO PfInfo = {.value = 0};
|
|
PERF_STATUS PerfStatus = {.value = 0};
|
|
unsigned int ratio0 = 10, ratio1 = 10, ratio2 = 10; // Arbitrary
|
|
|
|
RDMSR(PfInfo, MSR_PLATFORM_INFO);
|
|
if (PfInfo.value != 0) {
|
|
ratio0 = KMIN(PfInfo.MinimumRatio, PfInfo.MaxNonTurboRatio);
|
|
ratio1 = KMAX(PfInfo.MinimumRatio, PfInfo.MaxNonTurboRatio);
|
|
ratio2 = ratio1;
|
|
}
|
|
|
|
RDMSR(PerfStatus, MSR_IA32_PERF_STATUS);
|
|
if (PerfStatus.value != 0) { // §18.18.3.4
|
|
if (PerfStatus.CORE.XE_Enable) {
|
|
ratio2 = PerfStatus.CORE.MaxBusRatio;
|
|
} else {
|
|
if (Intel_MaxBusRatio(&PfID) == 0) {
|
|
if (PfID.value != 0)
|
|
ratio2 = PfID.MaxBusRatio;
|
|
}
|
|
}
|
|
} else {
|
|
if (Intel_MaxBusRatio(&PfID) == 0) {
|
|
if (PfID.value != 0)
|
|
ratio2 = PfID.MaxBusRatio;
|
|
}
|
|
}
|
|
Proc->Boost[0] = ratio0;
|
|
Proc->Boost[1] = KMIN(ratio1, ratio2);
|
|
Proc->Boost[LAST_BOOST] = KMAX(ratio1, ratio2);
|
|
|
|
Proc->Features.SpecTurboRatio = 1;
|
|
}
|
|
|
|
void Intel_Platform_Turbo(void)
|
|
{
|
|
PLATFORM_INFO Platform = {.value = 0};
|
|
RDMSR(Platform, MSR_PLATFORM_INFO);
|
|
|
|
Proc->Features.Ratio_Unlock = Platform.Ratio_Limited;
|
|
Proc->Features.TDP_Unlock = Platform.TDP_Limited;
|
|
|
|
Proc->Boost[0] = Platform.MinimumRatio;
|
|
Proc->Boost[1] = Platform.MaxNonTurboRatio;
|
|
|
|
Proc->Features.SpecTurboRatio = 0;
|
|
}
|
|
|
|
void Intel_Turbo_Config8C(void)
|
|
{
|
|
TURBO_RATIO_CONFIG0 TurboCfg0 = {.value = 0};
|
|
RDMSR(TurboCfg0, MSR_TURBO_RATIO_LIMIT);
|
|
|
|
Proc->Boost[MAX_BOOST - 8] = TurboCfg0.MaxRatio_8C;
|
|
Proc->Boost[MAX_BOOST - 7] = TurboCfg0.MaxRatio_7C;
|
|
Proc->Boost[MAX_BOOST - 6] = TurboCfg0.MaxRatio_6C;
|
|
Proc->Boost[MAX_BOOST - 5] = TurboCfg0.MaxRatio_5C;
|
|
Proc->Boost[MAX_BOOST - 4] = TurboCfg0.MaxRatio_4C;
|
|
Proc->Boost[MAX_BOOST - 3] = TurboCfg0.MaxRatio_3C;
|
|
Proc->Boost[MAX_BOOST - 2] = TurboCfg0.MaxRatio_2C;
|
|
Proc->Boost[MAX_BOOST - 1] = TurboCfg0.MaxRatio_1C;
|
|
|
|
Proc->Features.SpecTurboRatio += 8;
|
|
}
|
|
|
|
void Intel_Turbo_Config15C(void)
|
|
{
|
|
TURBO_RATIO_CONFIG1 TurboCfg1 = {.value = 0};
|
|
RDMSR(TurboCfg1, MSR_TURBO_RATIO_LIMIT1);
|
|
|
|
Proc->Boost[MAX_BOOST - 15] = TurboCfg1.IVB_EP.MaxRatio_15C;
|
|
Proc->Boost[MAX_BOOST - 14] = TurboCfg1.IVB_EP.MaxRatio_14C;
|
|
Proc->Boost[MAX_BOOST - 13] = TurboCfg1.IVB_EP.MaxRatio_13C;
|
|
Proc->Boost[MAX_BOOST - 12] = TurboCfg1.IVB_EP.MaxRatio_12C;
|
|
Proc->Boost[MAX_BOOST - 11] = TurboCfg1.IVB_EP.MaxRatio_11C;
|
|
Proc->Boost[MAX_BOOST - 10] = TurboCfg1.IVB_EP.MaxRatio_10C;
|
|
Proc->Boost[MAX_BOOST - 9] = TurboCfg1.IVB_EP.MaxRatio_9C;
|
|
|
|
Proc->Features.SpecTurboRatio += 7;
|
|
}
|
|
|
|
void Intel_Turbo_Config16C(void)
|
|
{
|
|
TURBO_RATIO_CONFIG1 TurboCfg1 = {.value = 0};
|
|
RDMSR(TurboCfg1, MSR_TURBO_RATIO_LIMIT1);
|
|
|
|
Proc->Boost[MAX_BOOST - 16] = TurboCfg1.HSW_EP.MaxRatio_16C;
|
|
Proc->Boost[MAX_BOOST - 15] = TurboCfg1.HSW_EP.MaxRatio_15C;
|
|
Proc->Boost[MAX_BOOST - 14] = TurboCfg1.HSW_EP.MaxRatio_14C;
|
|
Proc->Boost[MAX_BOOST - 13] = TurboCfg1.HSW_EP.MaxRatio_13C;
|
|
Proc->Boost[MAX_BOOST - 12] = TurboCfg1.HSW_EP.MaxRatio_12C;
|
|
Proc->Boost[MAX_BOOST - 11] = TurboCfg1.HSW_EP.MaxRatio_11C;
|
|
Proc->Boost[MAX_BOOST - 10] = TurboCfg1.HSW_EP.MaxRatio_10C;
|
|
Proc->Boost[MAX_BOOST - 9] = TurboCfg1.HSW_EP.MaxRatio_9C;
|
|
|
|
Proc->Features.SpecTurboRatio += 8;
|
|
}
|
|
|
|
void Intel_Turbo_Config18C(void)
|
|
{
|
|
TURBO_RATIO_CONFIG2 TurboCfg2 = {.value = 0};
|
|
RDMSR(TurboCfg2, MSR_TURBO_RATIO_LIMIT2);
|
|
|
|
Proc->Boost[MAX_BOOST - 18] = TurboCfg2.MaxRatio_18C;
|
|
Proc->Boost[MAX_BOOST - 17] = TurboCfg2.MaxRatio_17C;
|
|
|
|
Proc->Features.SpecTurboRatio += 2;
|
|
}
|
|
|
|
void Nehalem_Platform_Info(void)
|
|
{
|
|
Intel_Platform_Turbo();
|
|
Intel_Turbo_Config8C();
|
|
}
|
|
|
|
void IvyBridge_EP_Platform_Info(void)
|
|
{
|
|
Intel_Platform_Turbo();
|
|
Intel_Turbo_Config8C();
|
|
Intel_Turbo_Config15C();
|
|
}
|
|
|
|
void Haswell_EP_Platform_Info(void)
|
|
{
|
|
Intel_Platform_Turbo();
|
|
Intel_Turbo_Config8C();
|
|
Intel_Turbo_Config16C();
|
|
Intel_Turbo_Config18C();
|
|
}
|
|
|
|
void Skylake_X_Platform_Info(void)
|
|
{
|
|
TURBO_RATIO_CONFIG1 TurboCfg1 = {.value = 0};
|
|
RDMSR(TurboCfg1, MSR_TURBO_RATIO_LIMIT1);
|
|
|
|
Intel_Platform_Turbo();
|
|
Intel_Turbo_Config8C();
|
|
|
|
Proc->Boost[MAX_BOOST - 16] = TurboCfg1.SKL_X.NUMCORE_7;
|
|
Proc->Boost[MAX_BOOST - 15] = TurboCfg1.SKL_X.NUMCORE_6;
|
|
Proc->Boost[MAX_BOOST - 14] = TurboCfg1.SKL_X.NUMCORE_5;
|
|
Proc->Boost[MAX_BOOST - 13] = TurboCfg1.SKL_X.NUMCORE_4;
|
|
Proc->Boost[MAX_BOOST - 12] = TurboCfg1.SKL_X.NUMCORE_3;
|
|
Proc->Boost[MAX_BOOST - 11] = TurboCfg1.SKL_X.NUMCORE_2;
|
|
Proc->Boost[MAX_BOOST - 10] = TurboCfg1.SKL_X.NUMCORE_1;
|
|
Proc->Boost[MAX_BOOST - 9] = TurboCfg1.SKL_X.NUMCORE_0;
|
|
|
|
Proc->Features.SpecTurboRatio += 8;
|
|
}
|
|
|
|
|
|
typedef kernel_ulong_t (*PCI_CALLBACK)(struct pci_dev *);
|
|
|
|
typedef void (*ROUTER)(void __iomem *mchmap);
|
|
|
|
PCI_CALLBACK Router( struct pci_dev *dev, unsigned int offset,
|
|
unsigned long long wsize, ROUTER route)
|
|
{
|
|
void __iomem *mchmap;
|
|
union {
|
|
unsigned long long addr;
|
|
struct {
|
|
unsigned int low;
|
|
unsigned int high;
|
|
};
|
|
} mchbar;
|
|
unsigned long long wmask = BITCPL(wsize);
|
|
|
|
Proc->Uncore.ChipID = dev->device;
|
|
|
|
pci_read_config_dword(dev, offset , &mchbar.low);
|
|
pci_read_config_dword(dev, offset + 4, &mchbar.high);
|
|
|
|
mchbar.addr &= wmask;
|
|
|
|
mchmap = ioremap(mchbar.addr, wsize);
|
|
if (mchmap != NULL) {
|
|
route(mchmap);
|
|
|
|
iounmap(mchmap);
|
|
|
|
return(0);
|
|
} else
|
|
return((PCI_CALLBACK) -ENOMEM);
|
|
}
|
|
|
|
void Query_P965(void __iomem *mchmap)
|
|
{
|
|
unsigned short cha;
|
|
|
|
Proc->Uncore.CtrlCount = 1;
|
|
|
|
Proc->Uncore.Bus.ClkCfg.value = readl(mchmap + 0xc00);
|
|
|
|
Proc->Uncore.MC[0].P965.CKE0.value = readl(mchmap + 0x260);
|
|
Proc->Uncore.MC[0].P965.CKE1.value = readl(mchmap + 0x660);
|
|
|
|
Proc->Uncore.MC[0].ChannelCount =
|
|
(Proc->Uncore.MC[0].P965.CKE0.RankPop0 != 0)
|
|
+ (Proc->Uncore.MC[0].P965.CKE1.RankPop0 != 0);
|
|
|
|
Proc->Uncore.MC[0].SlotCount =
|
|
(Proc->Uncore.MC[0].P965.CKE0.SingleDimmPop ? 1 : 2)
|
|
+ (Proc->Uncore.MC[0].P965.CKE1.SingleDimmPop ? 1 : 2);
|
|
|
|
for (cha = 0; cha < Proc->Uncore.MC[0].ChannelCount; cha++) {
|
|
Proc->Uncore.MC[0].Channel[cha].P965.DRT0.value =
|
|
readl(mchmap + 0x29c + 0x400 * cha);
|
|
|
|
Proc->Uncore.MC[0].Channel[cha].P965.DRT1.value =
|
|
readw(mchmap + 0x250 + 0x400 * cha);
|
|
|
|
Proc->Uncore.MC[0].Channel[cha].P965.DRT2.value =
|
|
readl(mchmap + 0x252 + 0x400 * cha);
|
|
|
|
Proc->Uncore.MC[0].Channel[cha].P965.DRT3.value =
|
|
readw(mchmap + 0x256 + 0x400 * cha);
|
|
|
|
Proc->Uncore.MC[0].Channel[cha].P965.DRT4.value =
|
|
readl(mchmap + 0x258 + 0x400 * cha);
|
|
}
|
|
}
|
|
|
|
void Query_G965(void __iomem *mchmap)
|
|
{ // Source: Mobile Intel 965 Express Chipset Family
|
|
unsigned short cha, slot;
|
|
|
|
Proc->Uncore.CtrlCount = 1;
|
|
|
|
Proc->Uncore.Bus.ClkCfg.value = readl(mchmap + 0xc00);
|
|
|
|
Proc->Uncore.MC[0].G965.DRB0.value = readl(mchmap + 0x1200);
|
|
Proc->Uncore.MC[0].G965.DRB1.value = readl(mchmap + 0x1300);
|
|
|
|
Proc->Uncore.MC[0].ChannelCount =
|
|
(Proc->Uncore.MC[0].G965.DRB0.Rank1Addr != 0)
|
|
+ (Proc->Uncore.MC[0].G965.DRB1.Rank1Addr != 0);
|
|
|
|
Proc->Uncore.MC[0].SlotCount=Proc->Uncore.MC[0].ChannelCount > 1 ? 1:2;
|
|
|
|
for (cha = 0; cha < Proc->Uncore.MC[0].ChannelCount; cha++) {
|
|
Proc->Uncore.MC[0].Channel[cha].G965.DRT0.value =
|
|
readl(mchmap + 0x1210 + 0x100 * cha);
|
|
|
|
Proc->Uncore.MC[0].Channel[cha].G965.DRT1.value =
|
|
readl(mchmap + 0x1214 + 0x100 * cha);
|
|
|
|
Proc->Uncore.MC[0].Channel[cha].G965.DRT2.value =
|
|
readl(mchmap + 0x1218 + 0x100 * cha);
|
|
|
|
Proc->Uncore.MC[0].Channel[cha].G965.DRT3.value =
|
|
readl(mchmap + 0x121c + 0x100 * cha);
|
|
|
|
for (slot = 0; slot < Proc->Uncore.MC[0].SlotCount; slot++) {
|
|
Proc->Uncore.MC[0].Channel[cha].DIMM[slot].DRA.value =
|
|
readl(mchmap + 0x1208 + 0x100 * cha);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Query_P35(void __iomem *mchmap)
|
|
{ // Source: Intel® 3 Series Express Chipset Family
|
|
unsigned short cha;
|
|
|
|
Proc->Uncore.CtrlCount = 1;
|
|
|
|
Proc->Uncore.Bus.ClkCfg.value = readl(mchmap + 0xc00);
|
|
|
|
Proc->Uncore.MC[0].P35.CKE0.value = readl(mchmap + 0x260);
|
|
Proc->Uncore.MC[0].P35.CKE1.value = readl(mchmap + 0x660);
|
|
|
|
Proc->Uncore.MC[0].ChannelCount =
|
|
(Proc->Uncore.MC[0].P35.CKE0.RankPop0 != 0)
|
|
+ (Proc->Uncore.MC[0].P35.CKE1.RankPop0 != 0);
|
|
|
|
Proc->Uncore.MC[0].SlotCount =
|
|
(Proc->Uncore.MC[0].P35.CKE0.SingleDimmPop ? 1 : 2)
|
|
+ (Proc->Uncore.MC[0].P35.CKE1.SingleDimmPop ? 1 : 2);
|
|
|
|
for (cha = 0; cha < Proc->Uncore.MC[0].ChannelCount; cha++) {
|
|
Proc->Uncore.MC[0].Channel[cha].P35.DRT0.value =
|
|
readw(mchmap + 0x265 + 0x400 * cha);
|
|
|
|
Proc->Uncore.MC[0].Channel[cha].P35.DRT1.value =
|
|
readw(mchmap + 0x250 + 0x400 * cha);
|
|
|
|
Proc->Uncore.MC[0].Channel[cha].P35.DRT2.value =
|
|
readl(mchmap + 0x252 + 0x400 * cha);
|
|
|
|
Proc->Uncore.MC[0].Channel[cha].P35.DRT3.value =
|
|
readl(mchmap + 0x256 + 0x400 * cha);
|
|
|
|
Proc->Uncore.MC[0].Channel[cha].P35.DRT4.value =
|
|
readl(mchmap + 0x258 + 0x400 * cha);
|
|
|
|
Proc->Uncore.MC[0].Channel[cha].P35.DRT5.value =
|
|
readw(mchmap + 0x25d + 0x400 * cha);
|
|
}
|
|
}
|
|
|
|
kernel_ulong_t Query_NHM_Timing(unsigned int did,
|
|
unsigned short mc,
|
|
unsigned short cha)
|
|
{ // Source: Micron Technical Note DDR3 Power-Up, Initialization, & Reset
|
|
struct pci_dev *dev = pci_get_device(PCI_VENDOR_ID_INTEL, did, NULL);
|
|
if (dev != NULL) {
|
|
pci_read_config_dword(dev, 0x70,
|
|
&Proc->Uncore.MC[mc].Channel[cha].NHM.MR0_1.value);
|
|
|
|
pci_read_config_dword(dev, 0x74,
|
|
&Proc->Uncore.MC[mc].Channel[cha].NHM.MR2_3.value);
|
|
|
|
pci_read_config_dword(dev ,0x80,
|
|
&Proc->Uncore.MC[mc].Channel[cha].NHM.Rank_A.value);
|
|
|
|
pci_read_config_dword(dev ,0x84,
|
|
&Proc->Uncore.MC[mc].Channel[cha].NHM.Rank_B.value);
|
|
|
|
pci_read_config_dword(dev ,0x88,
|
|
&Proc->Uncore.MC[mc].Channel[cha].NHM.Bank.value);
|
|
|
|
pci_read_config_dword(dev ,0x8c,
|
|
&Proc->Uncore.MC[mc].Channel[cha].NHM.Refresh.value);
|
|
|
|
pci_read_config_dword(dev, 0xb8,
|
|
&Proc->Uncore.MC[mc].Channel[cha].NHM.Params.value);
|
|
|
|
pci_dev_put(dev);
|
|
return(0);
|
|
} else
|
|
return(-ENODEV);
|
|
}
|
|
|
|
kernel_ulong_t Query_NHM_DIMM( unsigned int did,
|
|
unsigned short mc,
|
|
unsigned short cha)
|
|
{
|
|
struct pci_dev *dev = pci_get_device(PCI_VENDOR_ID_INTEL, did, NULL);
|
|
if (dev != NULL) {
|
|
unsigned short slot;
|
|
|
|
for (slot = 0; slot < Proc->Uncore.MC[mc].SlotCount; slot++) {
|
|
pci_read_config_dword(dev, 0x48 + 4 * slot,
|
|
&Proc->Uncore.MC[mc].Channel[cha].DIMM[slot].DOD.value);
|
|
}
|
|
pci_dev_put(dev);
|
|
return(0);
|
|
} else
|
|
return(-ENODEV);
|
|
}
|
|
|
|
void Query_NHM_MaxDIMMs(struct pci_dev *dev, unsigned short mc)
|
|
{
|
|
pci_read_config_dword( dev, 0x64,
|
|
&Proc->Uncore.MC[mc].MaxDIMMs.NHM.DOD.value);
|
|
|
|
switch (Proc->Uncore.MC[mc].MaxDIMMs.NHM.DOD.MAXNUMDIMMS) {
|
|
case 0b00:
|
|
Proc->Uncore.MC[mc].SlotCount = 1;
|
|
break;
|
|
case 0b01:
|
|
Proc->Uncore.MC[mc].SlotCount = 2;
|
|
break;
|
|
case 0b10:
|
|
Proc->Uncore.MC[mc].SlotCount = 3;
|
|
break;
|
|
default:
|
|
Proc->Uncore.MC[mc].SlotCount = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
kernel_ulong_t Query_Bloomfield_IMC(struct pci_dev *dev, unsigned short mc)
|
|
{
|
|
kernel_ulong_t rc = 0;
|
|
unsigned int did[2][3] = {
|
|
{
|
|
PCI_DEVICE_ID_INTEL_I7_MC_CH0_CTRL,
|
|
PCI_DEVICE_ID_INTEL_I7_MC_CH1_CTRL,
|
|
PCI_DEVICE_ID_INTEL_I7_MC_CH2_CTRL
|
|
},
|
|
{
|
|
PCI_DEVICE_ID_INTEL_I7_MC_CH0_ADDR,
|
|
PCI_DEVICE_ID_INTEL_I7_MC_CH1_ADDR,
|
|
PCI_DEVICE_ID_INTEL_I7_MC_CH2_ADDR
|
|
}
|
|
};
|
|
unsigned short cha;
|
|
|
|
Query_NHM_MaxDIMMs(dev, mc);
|
|
|
|
pci_read_config_dword(dev,0x48, &Proc->Uncore.MC[mc].NHM.CONTROL.value);
|
|
pci_read_config_dword(dev,0x4c, &Proc->Uncore.MC[mc].NHM.STATUS.value);
|
|
|
|
Proc->Uncore.MC[mc].ChannelCount =
|
|
(Proc->Uncore.MC[mc].NHM.CONTROL.CHANNEL0_ACTIVE != 0)
|
|
+ (Proc->Uncore.MC[mc].NHM.CONTROL.CHANNEL1_ACTIVE != 0)
|
|
+ (Proc->Uncore.MC[mc].NHM.CONTROL.CHANNEL2_ACTIVE != 0);
|
|
|
|
for (cha = 0; (cha < Proc->Uncore.MC[mc].ChannelCount) && !rc; cha++) {
|
|
rc = Query_NHM_Timing(did[0][cha], mc, cha)
|
|
& Query_NHM_DIMM(did[1][cha], mc, cha);
|
|
}
|
|
return(rc);
|
|
}
|
|
|
|
kernel_ulong_t Query_Lynnfield_IMC(struct pci_dev *dev, unsigned short mc)
|
|
{
|
|
kernel_ulong_t rc = 0;
|
|
unsigned int did[2][2] = {
|
|
{
|
|
PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_CTRL,
|
|
PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_CTRL
|
|
},
|
|
{
|
|
PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH0_ADDR,
|
|
PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_CH1_ADDR
|
|
}
|
|
};
|
|
unsigned short cha;
|
|
|
|
Query_NHM_MaxDIMMs(dev, mc);
|
|
|
|
pci_read_config_dword(dev,0x48, &Proc->Uncore.MC[mc].NHM.CONTROL.value);
|
|
pci_read_config_dword(dev,0x4c, &Proc->Uncore.MC[mc].NHM.STATUS.value);
|
|
|
|
Proc->Uncore.MC[mc].ChannelCount =
|
|
(Proc->Uncore.MC[mc].NHM.CONTROL.CHANNEL0_ACTIVE != 0)
|
|
+ (Proc->Uncore.MC[mc].NHM.CONTROL.CHANNEL1_ACTIVE != 0);
|
|
|
|
for (cha = 0; (cha < Proc->Uncore.MC[mc].ChannelCount) && !rc; cha++) {
|
|
rc = Query_NHM_Timing(did[0][cha], mc, cha)
|
|
& Query_NHM_DIMM(did[1][cha], mc, cha);
|
|
}
|
|
return(rc);
|
|
}
|
|
|
|
void Query_C200(void __iomem *mchmap)
|
|
{ // Source: Intel® Xeon Processor E3-1200 Family
|
|
unsigned short cha;
|
|
|
|
Proc->Uncore.CtrlCount = 1;
|
|
|
|
/* ToDo */
|
|
Proc->Uncore.Bus.ClkCfg.value = readl(mchmap + 0xc00);
|
|
|
|
Proc->Uncore.MC[0].C200.MAD0.value = readl(mchmap + 0x5004);
|
|
Proc->Uncore.MC[0].C200.MAD1.value = readl(mchmap + 0x5008);
|
|
|
|
Proc->Uncore.MC[0].ChannelCount =
|
|
((Proc->Uncore.MC[0].C200.MAD0.Dimm_A_Size != 0)
|
|
|| (Proc->Uncore.MC[0].C200.MAD0.Dimm_B_Size != 0)) /*0 or 1*/
|
|
+ ((Proc->Uncore.MC[0].C200.MAD1.Dimm_A_Size != 0)
|
|
|| (Proc->Uncore.MC[0].C200.MAD1.Dimm_B_Size != 0)); /*0 or 1*/
|
|
|
|
for (cha = 0; cha < Proc->Uncore.MC[0].ChannelCount; cha++) {
|
|
Proc->Uncore.MC[0].Channel[cha].C200.DBP.value =
|
|
readl(mchmap + 0x4000 + 0x400 * cha);
|
|
|
|
Proc->Uncore.MC[0].Channel[cha].C200.RAP.value =
|
|
readl(mchmap + 0x4004 + 0x400 * cha);
|
|
|
|
Proc->Uncore.MC[0].Channel[cha].C200.RFTP.value =
|
|
readl(mchmap + 0x4298 + 0x400 * cha);
|
|
}
|
|
}
|
|
|
|
void Query_C220(void __iomem *mchmap)
|
|
{ // Source: Desktop 4th Generation Intel® Core™ Processor Family
|
|
unsigned short cha;
|
|
|
|
Proc->Uncore.CtrlCount = 1;
|
|
|
|
/* ToDo */
|
|
Proc->Uncore.Bus.ClkCfg.value = readl(mchmap + 0xc00);
|
|
|
|
Proc->Uncore.MC[0].C200.MAD0.value = readl(mchmap + 0x5004);
|
|
Proc->Uncore.MC[0].C200.MAD1.value = readl(mchmap + 0x5008);
|
|
|
|
Proc->Uncore.MC[0].ChannelCount =
|
|
((Proc->Uncore.MC[0].C200.MAD0.Dimm_A_Size != 0)
|
|
|| (Proc->Uncore.MC[0].C200.MAD0.Dimm_B_Size != 0))
|
|
+ ((Proc->Uncore.MC[0].C200.MAD1.Dimm_A_Size != 0)
|
|
|| (Proc->Uncore.MC[0].C200.MAD1.Dimm_B_Size != 0));
|
|
|
|
for (cha = 0; cha < Proc->Uncore.MC[0].ChannelCount; cha++) {
|
|
Proc->Uncore.MC[0].Channel[cha].C220.Timing.value =
|
|
readl(mchmap + 0x4c04 + 0x400 * cha);
|
|
|
|
Proc->Uncore.MC[0].Channel[cha].C220.Rank.value =
|
|
readl(mchmap + 0x4c14 + 0x400 * cha);
|
|
|
|
Proc->Uncore.MC[0].Channel[cha].C220.Refresh.value =
|
|
readl(mchmap + 0x4e98 + 0x400 * cha);
|
|
}
|
|
}
|
|
|
|
PCI_CALLBACK P965(struct pci_dev *dev)
|
|
{
|
|
return(Router(dev, 0x48, 0x4000, Query_P965));
|
|
}
|
|
|
|
PCI_CALLBACK G965(struct pci_dev *dev)
|
|
{
|
|
return(Router(dev, 0x48, 0x4000, Query_G965));
|
|
}
|
|
|
|
PCI_CALLBACK P35(struct pci_dev *dev)
|
|
{
|
|
return(Router(dev, 0x48, 0x4000, Query_P35));
|
|
}
|
|
|
|
PCI_CALLBACK Bloomfield_IMC(struct pci_dev *dev)
|
|
{
|
|
kernel_ulong_t rc = 0;
|
|
unsigned short mc;
|
|
|
|
Proc->Uncore.ChipID = dev->device;
|
|
|
|
Proc->Uncore.CtrlCount = 1;
|
|
for (mc = 0; (mc < Proc->Uncore.CtrlCount) && !rc; mc++)
|
|
rc = Query_Bloomfield_IMC(dev, mc);
|
|
|
|
return((PCI_CALLBACK) rc);
|
|
}
|
|
|
|
PCI_CALLBACK Lynnfield_IMC(struct pci_dev *dev)
|
|
{
|
|
kernel_ulong_t rc = 0;
|
|
unsigned short mc;
|
|
|
|
Proc->Uncore.ChipID = dev->device;
|
|
|
|
Proc->Uncore.CtrlCount = 1;
|
|
for (mc = 0; (mc < Proc->Uncore.CtrlCount) && !rc; mc++)
|
|
rc = Query_Lynnfield_IMC(dev, mc);
|
|
|
|
return((PCI_CALLBACK) rc);
|
|
}
|
|
|
|
PCI_CALLBACK NHM_IMC_TR(struct pci_dev *dev)
|
|
{
|
|
pci_read_config_dword(dev, 0x50, &Proc->Uncore.Bus.DimmClock.value);
|
|
|
|
return(0);
|
|
}
|
|
|
|
PCI_CALLBACK X58_QPI(struct pci_dev *dev)
|
|
{
|
|
pci_read_config_dword(dev, 0xd0, &Proc->Uncore.Bus.QuickPath.value);
|
|
|
|
return(0);
|
|
}
|
|
|
|
PCI_CALLBACK C200(struct pci_dev *dev)
|
|
{
|
|
return(Router(dev, 0x48, 0x8000, Query_C200));
|
|
}
|
|
|
|
PCI_CALLBACK C220(struct pci_dev *dev)
|
|
{
|
|
return(Router(dev, 0x48, 0x8000, Query_C220));
|
|
}
|
|
|
|
PCI_CALLBACK AMD_0F_MCH(struct pci_dev *dev)
|
|
{ // Source: BKDG for AMD NPT Family 0Fh Processors
|
|
unsigned short cha, slot, chip;
|
|
|
|
Proc->Uncore.ChipID = dev->device;
|
|
// Specs defined
|
|
Proc->Uncore.CtrlCount = 1;
|
|
// DRAM Configuration low register
|
|
pci_read_config_dword(dev, 0x90,
|
|
&Proc->Uncore.MC[0].AMD0F.DCRL.value);
|
|
// DRAM Configuration high register
|
|
pci_read_config_dword(dev, 0x94,
|
|
&Proc->Uncore.MC[0].AMD0F.DCRH.value);
|
|
// 1 channel if 64 bits / 2 channels if 128 bits width
|
|
Proc->Uncore.MC[0].ChannelCount = Proc->Uncore.MC[0].AMD0F.DCRL.Width128
|
|
+ 1;
|
|
// DIMM Geometry
|
|
for (chip = 0; chip < 8; chip++) {
|
|
cha = chip >> 2;
|
|
slot = chip % 4;
|
|
pci_read_config_dword(dev, 0x40 + 4 * chip,
|
|
&Proc->Uncore.MC[0].Channel[cha].DIMM[slot].MBA.value);
|
|
|
|
Proc->Uncore.MC[0].SlotCount +=
|
|
Proc->Uncore.MC[0].Channel[cha].DIMM[slot].MBA.CSEnable;
|
|
}
|
|
// DIMM Size
|
|
pci_read_config_dword( dev, 0x80,
|
|
&Proc->Uncore.MC[0].MaxDIMMs.AMD0F.CS.value);
|
|
// DRAM Timings
|
|
pci_read_config_dword(dev, 0x88,
|
|
&Proc->Uncore.MC[0].Channel[0].AMD0F.DTRL.value);
|
|
// Assume same timings for both channels
|
|
Proc->Uncore.MC[0].Channel[1].AMD0F.DTRL.value =
|
|
Proc->Uncore.MC[0].Channel[0].AMD0F.DTRL.value;
|
|
|
|
return(0);
|
|
}
|
|
|
|
PCI_CALLBACK AMD_0F_HTT(struct pci_dev *dev)
|
|
{
|
|
unsigned int link;
|
|
|
|
pci_read_config_dword(dev, 0x64, &Proc->Uncore.Bus.UnitID.value);
|
|
|
|
for (link = 0; link < 3; link++) {
|
|
pci_read_config_dword(dev, 0x88 + 0x20 * link,
|
|
&Proc->Uncore.Bus.LDTi_Freq[link].value);
|
|
};
|
|
|
|
return(0);
|
|
}
|
|
|
|
static int CoreFreqK_ProbePCI( struct pci_dev *dev,
|
|
const struct pci_device_id *id)
|
|
{
|
|
int rc = -ENODEV;
|
|
|
|
if (!pci_enable_device(dev)) {
|
|
PCI_CALLBACK Callback = (PCI_CALLBACK) id->driver_data;
|
|
rc =(int) Callback(dev);
|
|
}
|
|
|
|
return(rc);
|
|
}
|
|
|
|
static void CoreFreqK_RemovePCI(struct pci_dev *dev)
|
|
{
|
|
pci_disable_device(dev);
|
|
}
|
|
|
|
static struct pci_device_id CoreFreqK_pci_ids[] = {
|
|
{ // i946 - Lakeport
|
|
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82946GZ_HB),
|
|
.driver_data = (kernel_ulong_t) P965
|
|
},
|
|
{ // Q963/Q965 - Broadwater
|
|
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82965Q_HB),
|
|
.driver_data = (kernel_ulong_t) P965
|
|
},
|
|
{ // P965/G965 - Broadwater
|
|
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82965G_HB),
|
|
.driver_data = (kernel_ulong_t) P965
|
|
},
|
|
{ // GM965 - Crestline
|
|
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82965GM_HB),
|
|
.driver_data = (kernel_ulong_t) G965
|
|
},
|
|
{ // GME965
|
|
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82965GME_HB),
|
|
.driver_data = (kernel_ulong_t) G965
|
|
},
|
|
{ // GM45 - Cantiga
|
|
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_GM45_HB),
|
|
.driver_data = (kernel_ulong_t) G965
|
|
},
|
|
{ // Q35 - Bearlake
|
|
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_Q35_HB),
|
|
.driver_data = (kernel_ulong_t) P35
|
|
},
|
|
{ // P35/G33 - Bearlake
|
|
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_G33_HB),
|
|
.driver_data = (kernel_ulong_t) P35
|
|
},
|
|
{ // Q33 - Bearlake
|
|
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_Q33_HB),
|
|
.driver_data = (kernel_ulong_t) P35
|
|
},
|
|
{ // X38/X48 - Bearlake
|
|
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X38_HB),
|
|
.driver_data = (kernel_ulong_t) P35
|
|
},
|
|
{ // 3200/3210 - Unknown
|
|
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_3200_HB),
|
|
.driver_data = (kernel_ulong_t) P35
|
|
},
|
|
{ // Q45/Q43 - Unknown
|
|
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_Q45_HB),
|
|
.driver_data = (kernel_ulong_t) P35
|
|
},
|
|
{ // P45/G45 - Eaglelake
|
|
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_G45_HB),
|
|
.driver_data = (kernel_ulong_t) P35
|
|
},
|
|
{ // G41 - Eaglelake
|
|
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_G41_HB),
|
|
.driver_data = (kernel_ulong_t) P35
|
|
},
|
|
// 1st Generation
|
|
{ // Bloomfield IMC
|
|
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_MCR),
|
|
.driver_data = (kernel_ulong_t) Bloomfield_IMC
|
|
},
|
|
{ // Bloomfield IMC Test Registers
|
|
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7_MC_TEST),
|
|
.driver_data = (kernel_ulong_t) NHM_IMC_TR
|
|
},
|
|
{ // Nehalem Control Status and RAS Registers
|
|
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_CTRL),
|
|
.driver_data = (kernel_ulong_t) X58_QPI
|
|
},
|
|
{ // Lynnfield IMC
|
|
PCI_DEVICE(PCI_VENDOR_ID_INTEL,PCI_DEVICE_ID_INTEL_LYNNFIELD_MCR),
|
|
.driver_data = (kernel_ulong_t) Lynnfield_IMC
|
|
},
|
|
{ // Lynnfield IMC Test Registers
|
|
PCI_DEVICE(PCI_VENDOR_ID_INTEL,PCI_DEVICE_ID_INTEL_LYNNFIELD_MC_TEST),
|
|
.driver_data = (kernel_ulong_t) NHM_IMC_TR
|
|
},
|
|
// 2nd Generation
|
|
// Sandy Bridge ix-2xxx, Xeon E3-E5: IMC_HA=0x3ca0 / IMC_TA=0x3ca8 /
|
|
// TA0=0x3caa, TA1=0x3cab / TA2=0x3cac / TA3=0x3cad / TA4=0x3cae
|
|
{
|
|
PCI_DEVICE(PCI_VENDOR_ID_INTEL,PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_HA0),
|
|
.driver_data = (kernel_ulong_t) C200
|
|
},
|
|
// 3rd Generation
|
|
// Ivy Bridge ix-3xxx, Xeon E7/E5 v2: IMC_HA=0x0ea0 / IMC_TA=0x0ea8
|
|
// TA0=0x0eaa / TA1=0x0eab / TA2=0x0eac / TA3=0x0ead
|
|
{
|
|
PCI_DEVICE(PCI_VENDOR_ID_INTEL,PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0),
|
|
.driver_data = (kernel_ulong_t) C200
|
|
},
|
|
// 4th Generation
|
|
// Haswell ix-4xxx, Xeon E7/E5 v3: IMC_HA0=0x2fa0 / IMC_HA0_TA=0x2fa8
|
|
// TAD0=0x2faa / TAD1=0x2fab / TAD2=0x2fac / TAD3=0x2fad
|
|
{
|
|
PCI_DEVICE(PCI_VENDOR_ID_INTEL,PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0),
|
|
.driver_data = (kernel_ulong_t) C220
|
|
},
|
|
// AMD Family 0Fh
|
|
{
|
|
PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB_MEMCTL),
|
|
.driver_data = (kernel_ulong_t) AMD_0F_MCH
|
|
},
|
|
{
|
|
PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB),
|
|
.driver_data = (kernel_ulong_t) AMD_0F_HTT
|
|
},
|
|
{0, }
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(pci, CoreFreqK_pci_ids);
|
|
|
|
static struct pci_driver CoreFreqK_pci_driver = {
|
|
.name = "corefreqk",
|
|
.id_table = CoreFreqK_pci_ids,
|
|
.probe = CoreFreqK_ProbePCI,
|
|
.remove = CoreFreqK_RemovePCI
|
|
};
|
|
|
|
|
|
void Query_GenuineIntel(void)
|
|
{
|
|
Intel_Platform_Info();
|
|
HyperThreading_Technology();
|
|
}
|
|
|
|
void Query_AuthenticAMD(void)
|
|
{
|
|
if (Proc->Features.AdvPower.EDX.FID == 1) {
|
|
// Processor supports FID changes.
|
|
FIDVID_STATUS FidVidStatus = {.value = 0};
|
|
|
|
RDMSR(FidVidStatus, MSR_K7_FID_VID_STATUS);
|
|
|
|
Proc->Boost[0] = VCO[FidVidStatus.StartFID].MCF;
|
|
Proc->Boost[1] = 8 + FidVidStatus.MaxFID;
|
|
|
|
if (FidVidStatus.StartFID < 0b1000) {
|
|
unsigned int t;
|
|
for (t = 0; t < 5; t++)
|
|
Proc->Boost[MAX_BOOST-5+t] = VCO[FidVidStatus.StartFID].PCF[t];
|
|
}
|
|
else
|
|
Proc->Boost[LAST_BOOST] = 8 + FidVidStatus.MaxFID;
|
|
} else {
|
|
HWCR HwCfgRegister = {.value = 0};
|
|
|
|
RDMSR(HwCfgRegister, MSR_K7_HWCR);
|
|
|
|
Proc->Boost[0] = 8 + HwCfgRegister.Family_0Fh.StartFID;
|
|
Proc->Boost[1] = Proc->Boost[0];
|
|
Proc->Boost[LAST_BOOST] = Proc->Boost[0];
|
|
}
|
|
Proc->Features.FactoryFreq = Proc->Boost[1] * 1000; // MHz
|
|
|
|
HyperThreading_Technology();
|
|
}
|
|
/* Todo: AMD Hardware Families > 0Fh
|
|
|
|
if ( Proc->Features.AdvPower.DX.HwPstate == 1)
|
|
CoreCOF = 100 * (MSRC001_00[6B:64][CpuFid] + 10h)
|
|
/ (2^MSRC001_00[6B:64][CpuDid])
|
|
*/
|
|
|
|
void Query_Core2(void)
|
|
{
|
|
Intel_Platform_Info();
|
|
HyperThreading_Technology();
|
|
}
|
|
|
|
void Query_Nehalem(void)
|
|
{
|
|
Nehalem_Platform_Info();
|
|
HyperThreading_Technology();
|
|
}
|
|
|
|
void Query_IvyBridge_EP(void)
|
|
{
|
|
IvyBridge_EP_Platform_Info();
|
|
HyperThreading_Technology();
|
|
}
|
|
|
|
void Query_Haswell_EP(void)
|
|
{
|
|
Haswell_EP_Platform_Info();
|
|
HyperThreading_Technology();
|
|
}
|
|
|
|
void Query_Skylake_X(void)
|
|
{
|
|
Skylake_X_Platform_Info();
|
|
HyperThreading_Technology();
|
|
}
|
|
|
|
void Dump_CPUID(CORE *Core)
|
|
{
|
|
unsigned int i;
|
|
|
|
asm volatile
|
|
(
|
|
"xorq %%rax, %%rax \n\t"
|
|
"xorq %%rbx, %%rbx \n\t"
|
|
"xorq %%rcx, %%rcx \n\t"
|
|
"xorq %%rdx, %%rdx \n\t"
|
|
"cpuid \n\t"
|
|
"mov %%eax, %0 \n\t"
|
|
"mov %%ebx, %1 \n\t"
|
|
"mov %%ecx, %2 \n\t"
|
|
"mov %%edx, %3"
|
|
: "=r" (Core->Query.StdFunc.LargestStdFunc),
|
|
"=r" (Core->Query.StdFunc.BX),
|
|
"=r" (Core->Query.StdFunc.CX),
|
|
"=r" (Core->Query.StdFunc.DX)
|
|
:
|
|
: "%rax", "%rbx", "%rcx", "%rdx"
|
|
);
|
|
asm volatile
|
|
(
|
|
"movq $0x80000000, %%rax \n\t"
|
|
"xorq %%rbx, %%rbx \n\t"
|
|
"xorq %%rcx, %%rcx \n\t"
|
|
"xorq %%rdx, %%rdx \n\t"
|
|
"cpuid \n\t"
|
|
"mov %%eax, %0 \n\t"
|
|
"mov %%ebx, %1 \n\t"
|
|
"mov %%ecx, %2 \n\t"
|
|
"mov %%edx, %3"
|
|
: "=r" (Core->Query.ExtFunc.LargestExtFunc),
|
|
"=r" (Core->Query.ExtFunc.EBX),
|
|
"=r" (Core->Query.ExtFunc.ECX),
|
|
"=r" (Core->Query.ExtFunc.EDX)
|
|
:
|
|
: "%rax", "%rbx", "%rcx", "%rdx"
|
|
);
|
|
for (i = 0; (i < CPUID_MAX_FUNC) && (Core->CpuID[i].func != 0x0); i++) {
|
|
asm volatile
|
|
(
|
|
"xorq %%rax, %%rax \n\t"
|
|
"xorq %%rbx, %%rbx \n\t"
|
|
"xorq %%rcx, %%rcx \n\t"
|
|
"xorq %%rdx, %%rdx \n\t"
|
|
"mov %4, %%eax \n\t"
|
|
"mov %5, %%ecx \n\t"
|
|
"cpuid \n\t"
|
|
"mov %%eax, %0 \n\t"
|
|
"mov %%ebx, %1 \n\t"
|
|
"mov %%ecx, %2 \n\t"
|
|
"mov %%edx, %3"
|
|
: "=r" (Core->CpuID[i].reg[0]),
|
|
"=r" (Core->CpuID[i].reg[1]),
|
|
"=r" (Core->CpuID[i].reg[2]),
|
|
"=r" (Core->CpuID[i].reg[3])
|
|
: "r" (Core->CpuID[i].func),
|
|
"r" (Core->CpuID[i].sub)
|
|
: "%rax", "%rbx", "%rcx", "%rdx"
|
|
);
|
|
}
|
|
}
|
|
|
|
void SpeedStep_Technology(CORE *Core, unsigned int cpu)
|
|
{
|
|
if (Core->T.Base.BSP) {
|
|
if (Proc->Features.Std.ECX.EIST == 1) {
|
|
MISC_PROC_FEATURES MiscFeatures = {.value = 0};
|
|
RDMSR(MiscFeatures, MSR_IA32_MISC_ENABLE);
|
|
|
|
switch (SpeedStep_Enable) {
|
|
case COREFREQ_TOGGLE_OFF:
|
|
case COREFREQ_TOGGLE_ON:
|
|
MiscFeatures.EIST = SpeedStep_Enable;
|
|
WRMSR(MiscFeatures, MSR_IA32_MISC_ENABLE);
|
|
RDMSR(MiscFeatures, MSR_IA32_MISC_ENABLE);
|
|
break;
|
|
}
|
|
Core->Query.EIST = MiscFeatures.EIST;
|
|
} else {
|
|
Core->Query.EIST = 0;
|
|
}
|
|
BITSET(LOCKLESS, Proc->SpeedStep_Mask, cpu); // Per Package
|
|
} else
|
|
BITCLR(LOCKLESS, Proc->SpeedStep_Mask, cpu);
|
|
}
|
|
|
|
void TurboBoost_Technology(CORE *Core, unsigned int cpu)
|
|
{
|
|
MISC_PROC_FEATURES MiscFeatures = {.value = 0};
|
|
RDMSR(MiscFeatures, MSR_IA32_MISC_ENABLE);
|
|
|
|
if (MiscFeatures.Turbo_IDA == 0) {
|
|
PERF_CONTROL PerfControl = {.value = 0};
|
|
RDMSR(PerfControl, MSR_IA32_PERF_CTL);
|
|
|
|
switch (TurboBoost_Enable) {
|
|
case COREFREQ_TOGGLE_OFF:
|
|
case COREFREQ_TOGGLE_ON:
|
|
PerfControl.Turbo_IDA = !TurboBoost_Enable;
|
|
WRMSR(PerfControl, MSR_IA32_PERF_CTL);
|
|
RDMSR(PerfControl, MSR_IA32_PERF_CTL);
|
|
break;
|
|
}
|
|
Core->Query.Turbo = !PerfControl.Turbo_IDA;
|
|
} else {
|
|
Core->Query.Turbo = 0;
|
|
}
|
|
BITSET(LOCKLESS, Proc->TurboBoost_Mask, cpu); // Per Thread
|
|
}
|
|
|
|
void DynamicAcceleration(CORE *Core, unsigned int cpu)
|
|
{
|
|
if (Proc->Features.Power.EAX.TurboIDA) {
|
|
TurboBoost_Technology(Core, cpu);
|
|
} else {
|
|
Core->Query.Turbo = 0;
|
|
|
|
BITSET(LOCKLESS, Proc->TurboBoost_Mask, cpu); // Unique
|
|
}
|
|
}
|
|
|
|
void Query_Intel_C1E(CORE *Core, unsigned int cpu)
|
|
{
|
|
if (Core->T.Base.BSP) {
|
|
POWER_CONTROL PowerCtrl = {.value = 0};
|
|
RDMSR(PowerCtrl, MSR_IA32_POWER_CTL);
|
|
|
|
switch (C1E_Enable) {
|
|
case COREFREQ_TOGGLE_OFF:
|
|
case COREFREQ_TOGGLE_ON:
|
|
PowerCtrl.C1E = C1E_Enable;
|
|
WRMSR(PowerCtrl, MSR_IA32_POWER_CTL);
|
|
RDMSR(PowerCtrl, MSR_IA32_POWER_CTL);
|
|
break;
|
|
}
|
|
Core->Query.C1E = PowerCtrl.C1E;
|
|
|
|
BITSET(LOCKLESS, Proc->C1E_Mask, cpu); // Per Package
|
|
} else {
|
|
Core->Query.C1E = 0;
|
|
|
|
BITCLR(LOCKLESS, Proc->C1E_Mask, cpu);
|
|
}
|
|
}
|
|
|
|
void Query_AMD_C1E(CORE *Core, unsigned int cpu)
|
|
{
|
|
INT_PENDING_MSG IntPendingMsg = {.value = 0};
|
|
|
|
RDMSR(IntPendingMsg, MSR_K8_INT_PENDING_MSG);
|
|
|
|
Core->Query.C1E = IntPendingMsg.C1eOnCmpHalt
|
|
& !IntPendingMsg.SmiOnCmpHalt;
|
|
|
|
BITSET(LOCKLESS, Proc->C1E_Mask, cpu); // Per Core
|
|
}
|
|
|
|
void ThermalMonitor_Set(CORE *Core)
|
|
{
|
|
TJMAX TjMax = {.value = 0};
|
|
MISC_PROC_FEATURES MiscFeatures = {.value = 0};
|
|
THERM2_CONTROL Therm2Control = {.value = 0};
|
|
|
|
//Silvermont + Xeon[06_57] + Nehalem + Sandy Bridge & superior arch.
|
|
RDMSR(TjMax, MSR_IA32_TEMPERATURE_TARGET);
|
|
|
|
Core->PowerThermal.Target = TjMax.Target;
|
|
if (Core->PowerThermal.Target == 0)
|
|
Core->PowerThermal.Target = 100; // ToDo: TjMax database.
|
|
|
|
RDMSR(MiscFeatures, MSR_IA32_MISC_ENABLE);
|
|
|
|
Core->PowerThermal.TCC_Enable = MiscFeatures.TCC;
|
|
Core->PowerThermal.TM2_Enable = MiscFeatures.TM2_Enable;
|
|
|
|
RDMSR(Therm2Control, MSR_THERM2_CTL); // All Intel families.
|
|
|
|
Core->PowerThermal.TM2_Enable = Therm2Control.TM_SELECT;
|
|
}
|
|
|
|
void PowerThermal(CORE *Core, unsigned int cpu)
|
|
{
|
|
CLOCK_MODULATION ClockModulation = {.value = 0};
|
|
|
|
if (Proc->Features.Info.LargestStdFunc >= 0x6) {
|
|
struct THERMAL_POWER_LEAF Power = {{0}};
|
|
|
|
asm volatile
|
|
(
|
|
"movq $0x6, %%rax \n\t"
|
|
"xorq %%rbx, %%rbx \n\t"
|
|
"xorq %%rcx, %%rcx \n\t"
|
|
"xorq %%rdx, %%rdx \n\t"
|
|
"cpuid \n\t"
|
|
"mov %%eax, %0 \n\t"
|
|
"mov %%ebx, %1 \n\t"
|
|
"mov %%ecx, %2 \n\t"
|
|
"mov %%edx, %3"
|
|
: "=r" (Power.EAX),
|
|
"=r" (Power.EBX),
|
|
"=r" (Power.ECX),
|
|
"=r" (Power.EDX)
|
|
:
|
|
: "%rax", "%rbx", "%rcx", "%rdx"
|
|
);
|
|
|
|
if (Power.ECX.SETBH == 1) {
|
|
RDMSR(Core->PowerThermal.PerfEnergyBias, MSR_IA32_ENERGY_PERF_BIAS);
|
|
RDMSR(Core->PowerThermal.PwrManagement, MSR_MISC_PWR_MGMT);
|
|
|
|
if (Experimental == 1) {
|
|
switch (PowerMGMT_Unlock) {
|
|
case COREFREQ_TOGGLE_OFF:
|
|
case COREFREQ_TOGGLE_ON:
|
|
Core->PowerThermal.PwrManagement.Perf_BIAS_Enable=PowerMGMT_Unlock;
|
|
WRMSR(Core->PowerThermal.PwrManagement, MSR_MISC_PWR_MGMT);
|
|
RDMSR(Core->PowerThermal.PwrManagement, MSR_MISC_PWR_MGMT);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Core->PowerThermal.PwrManagement.Perf_BIAS_Enable
|
|
&& (PowerPolicy >= 0) && (PowerPolicy <= 15))
|
|
{
|
|
Core->PowerThermal.PerfEnergyBias.PowerPolicy = PowerPolicy;
|
|
WRMSR(Core->PowerThermal.PerfEnergyBias, MSR_IA32_ENERGY_PERF_BIAS);
|
|
RDMSR(Core->PowerThermal.PerfEnergyBias, MSR_IA32_ENERGY_PERF_BIAS);
|
|
}
|
|
}
|
|
|
|
if (Proc->Features.Std.EDX.ACPI == 1) {
|
|
int ToggleFeature = 0;
|
|
|
|
RDMSR(ClockModulation, MSR_IA32_THERM_CONTROL);
|
|
ClockModulation.ECMD = Power.EAX.ECMD;
|
|
|
|
switch (ODCM_Enable) {
|
|
case COREFREQ_TOGGLE_OFF:
|
|
case COREFREQ_TOGGLE_ON:
|
|
ClockModulation.ODCM_Enable = ODCM_Enable;
|
|
ToggleFeature = 1;
|
|
break;
|
|
}
|
|
if ((ODCM_DutyCycle >= 0)
|
|
&& (ODCM_DutyCycle <= (7 << ClockModulation.ECMD)))
|
|
{
|
|
ClockModulation.DutyCycle = ODCM_DutyCycle << !ClockModulation.ECMD;
|
|
ToggleFeature = 1;
|
|
}
|
|
if (ToggleFeature == 1) {
|
|
WRMSR(ClockModulation, MSR_IA32_THERM_CONTROL);
|
|
RDMSR(ClockModulation, MSR_IA32_THERM_CONTROL);
|
|
}
|
|
Core->PowerThermal.ClockModulation = ClockModulation;
|
|
}
|
|
}
|
|
BITSET(LOCKLESS, Proc->ODCM_Mask, cpu);
|
|
BITSET(LOCKLESS, Proc->PowerMgmt_Mask, cpu);
|
|
}
|
|
|
|
void CStatesConfiguration(int encoding, CORE *Core, unsigned int cpu)
|
|
{
|
|
CSTATE_CONFIG CStateConfig = {.value = 0};
|
|
CSTATE_IO_MWAIT CState_IO_MWAIT = {.value = 0};
|
|
int ToggleFeature = 0;
|
|
|
|
RDMSR(CStateConfig, MSR_PKG_CST_CONFIG_CONTROL);
|
|
|
|
switch (C3A_Enable) {
|
|
case COREFREQ_TOGGLE_OFF:
|
|
case COREFREQ_TOGGLE_ON:
|
|
CStateConfig.C3autoDemotion = C3A_Enable;
|
|
ToggleFeature = 1;
|
|
break;
|
|
}
|
|
switch (C1A_Enable) {
|
|
case COREFREQ_TOGGLE_OFF:
|
|
case COREFREQ_TOGGLE_ON:
|
|
CStateConfig.C1autoDemotion = C1A_Enable;
|
|
ToggleFeature = 1;
|
|
break;
|
|
}
|
|
if (encoding == 0x062A) {
|
|
switch (C3U_Enable) {
|
|
case COREFREQ_TOGGLE_OFF:
|
|
case COREFREQ_TOGGLE_ON:
|
|
CStateConfig.C3undemotion = C3U_Enable;
|
|
ToggleFeature = 1;
|
|
break;
|
|
}
|
|
switch (C1U_Enable) {
|
|
case COREFREQ_TOGGLE_OFF:
|
|
case COREFREQ_TOGGLE_ON:
|
|
CStateConfig.C1undemotion = C1U_Enable;
|
|
ToggleFeature = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (ToggleFeature == 1) {
|
|
WRMSR(CStateConfig, MSR_PKG_CST_CONFIG_CONTROL);
|
|
RDMSR(CStateConfig, MSR_PKG_CST_CONFIG_CONTROL);
|
|
}
|
|
if (CStateConfig.CFG_Lock == 0) {
|
|
ToggleFeature = 0;
|
|
if (encoding == 0x061A) { // NHM encoding compatibility
|
|
switch (IOMWAIT_Enable) {
|
|
case COREFREQ_TOGGLE_OFF:
|
|
case COREFREQ_TOGGLE_ON:
|
|
CStateConfig.IO_MWAIT_Redir = IOMWAIT_Enable;
|
|
ToggleFeature = 1;
|
|
break;
|
|
}
|
|
switch (PkgCStateLimit) {
|
|
case 7:
|
|
CStateConfig.Pkg_CStateLimit = 0b100;
|
|
ToggleFeature = 1;
|
|
break;
|
|
case 6:
|
|
CStateConfig.Pkg_CStateLimit = 0b011;
|
|
ToggleFeature = 1;
|
|
break;
|
|
case 3: // Cannot be used to limit package C-State to C3
|
|
CStateConfig.Pkg_CStateLimit = 0b010;
|
|
ToggleFeature = 1;
|
|
break;
|
|
case 1:
|
|
CStateConfig.Pkg_CStateLimit = 0b001;
|
|
ToggleFeature = 1;
|
|
break;
|
|
case 0:
|
|
CStateConfig.Pkg_CStateLimit = 0b000;
|
|
ToggleFeature = 1;
|
|
break;
|
|
}
|
|
} else if (encoding == 0x062A) { // SNB encoding compatibility
|
|
switch (PkgCStateLimit) {
|
|
case 7:
|
|
CStateConfig.Pkg_CStateLimit = 0b100;
|
|
ToggleFeature = 1;
|
|
break;
|
|
case 6:
|
|
CStateConfig.Pkg_CStateLimit = 0b011;
|
|
ToggleFeature = 1;
|
|
break;
|
|
case 3:
|
|
CStateConfig.Pkg_CStateLimit = 0b010;
|
|
ToggleFeature = 1;
|
|
break;
|
|
case 2:
|
|
CStateConfig.Pkg_CStateLimit = 0b001;
|
|
ToggleFeature = 1;
|
|
break;
|
|
case 1:
|
|
case 0:
|
|
CStateConfig.Pkg_CStateLimit = 0b000;
|
|
ToggleFeature = 1;
|
|
break;
|
|
}
|
|
} else if (encoding == 0x0645) {//HSW_ULT encoding compatibility
|
|
switch (PkgCStateLimit) {
|
|
case 10:
|
|
CStateConfig.Pkg_CStateLimit = 0b1000;
|
|
ToggleFeature = 1;
|
|
break;
|
|
case 9:
|
|
CStateConfig.Pkg_CStateLimit = 0b0111;
|
|
ToggleFeature = 1;
|
|
break;
|
|
case 8:
|
|
CStateConfig.Pkg_CStateLimit = 0b0110;
|
|
ToggleFeature = 1;
|
|
break;
|
|
case 7:
|
|
CStateConfig.Pkg_CStateLimit = 0b0100;
|
|
ToggleFeature = 1;
|
|
break;
|
|
case 6:
|
|
CStateConfig.Pkg_CStateLimit = 0b0011;
|
|
ToggleFeature = 1;
|
|
break;
|
|
case 3:
|
|
CStateConfig.Pkg_CStateLimit = 0b0010;
|
|
ToggleFeature = 1;
|
|
break;
|
|
case 2:
|
|
CStateConfig.Pkg_CStateLimit = 0b0001;
|
|
ToggleFeature = 1;
|
|
break;
|
|
case 1:
|
|
case 0:
|
|
CStateConfig.Pkg_CStateLimit = 0b0000;
|
|
ToggleFeature = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (ToggleFeature == 1) {
|
|
WRMSR(CStateConfig, MSR_PKG_CST_CONFIG_CONTROL);
|
|
RDMSR(CStateConfig, MSR_PKG_CST_CONFIG_CONTROL);
|
|
}
|
|
}
|
|
Core->Query.C3A = CStateConfig.C3autoDemotion;
|
|
Core->Query.C1A = CStateConfig.C1autoDemotion;
|
|
|
|
if (encoding == 0x062A) {
|
|
Core->Query.C3U = CStateConfig.C3undemotion;
|
|
Core->Query.C1U = CStateConfig.C1undemotion;
|
|
}
|
|
Core->Query.CfgLock = CStateConfig.CFG_Lock;
|
|
Core->Query.IORedir = CStateConfig.IO_MWAIT_Redir;
|
|
|
|
if (encoding == 0x061A) {
|
|
switch (CStateConfig.Pkg_CStateLimit & 0x7) {
|
|
case 0b100:
|
|
Core->Query.CStateLimit = 7;
|
|
break;
|
|
case 0b011:
|
|
Core->Query.CStateLimit = 6;
|
|
break;
|
|
case 0b010:
|
|
Core->Query.CStateLimit = 3;
|
|
break;
|
|
case 0b001:
|
|
Core->Query.CStateLimit = 1;
|
|
break;
|
|
case 0b000:
|
|
default:
|
|
Core->Query.CStateLimit = 0;
|
|
break;
|
|
}
|
|
} else if (encoding == 0x062A) {
|
|
switch (CStateConfig.Pkg_CStateLimit & 0x7) {
|
|
case 0b101:
|
|
case 0b100:
|
|
Core->Query.CStateLimit = 7;
|
|
break;
|
|
case 0b011:
|
|
Core->Query.CStateLimit = 6;
|
|
break;
|
|
case 0b010:
|
|
Core->Query.CStateLimit = 3;
|
|
break;
|
|
case 0b001:
|
|
Core->Query.CStateLimit = 2;
|
|
break;
|
|
case 0b000:
|
|
default:
|
|
Core->Query.CStateLimit = 0;
|
|
break;
|
|
}
|
|
} else if (encoding == 0x0645) {
|
|
switch (CStateConfig.Pkg_CStateLimit) {
|
|
case 0b1000:
|
|
Core->Query.CStateLimit = 10;
|
|
break;
|
|
case 0b0111:
|
|
Core->Query.CStateLimit = 9;
|
|
break;
|
|
case 0b0110:
|
|
Core->Query.CStateLimit = 8;
|
|
break;
|
|
case 0b0101:
|
|
case 0b0100:
|
|
Core->Query.CStateLimit = 7;
|
|
break;
|
|
case 0b0011:
|
|
Core->Query.CStateLimit = 6;
|
|
break;
|
|
case 0b0010:
|
|
Core->Query.CStateLimit = 3;
|
|
break;
|
|
case 0b0001:
|
|
Core->Query.CStateLimit = 2;
|
|
break;
|
|
case 0b0000:
|
|
default:
|
|
Core->Query.CStateLimit = 0;
|
|
break;
|
|
}
|
|
}
|
|
BITSET(LOCKLESS, Proc->C3A_Mask, cpu);
|
|
BITSET(LOCKLESS, Proc->C1A_Mask, cpu);
|
|
BITSET(LOCKLESS, Proc->C3U_Mask, cpu);
|
|
BITSET(LOCKLESS, Proc->C1U_Mask, cpu);
|
|
|
|
RDMSR(CState_IO_MWAIT, MSR_PMG_IO_CAPTURE_BASE);
|
|
|
|
if (CStateConfig.IO_MWAIT_Redir) {
|
|
switch (CStateIORedir) {
|
|
case 7:
|
|
CState_IO_MWAIT.CStateRange = 0b010;
|
|
WRMSR(CState_IO_MWAIT, MSR_PMG_IO_CAPTURE_BASE);
|
|
break;
|
|
case 6:
|
|
CState_IO_MWAIT.CStateRange = 0b001;
|
|
WRMSR(CState_IO_MWAIT, MSR_PMG_IO_CAPTURE_BASE);
|
|
break;
|
|
case 3:
|
|
CState_IO_MWAIT.CStateRange = 0b000;
|
|
WRMSR(CState_IO_MWAIT, MSR_PMG_IO_CAPTURE_BASE);
|
|
break;
|
|
}
|
|
if (CStateIORedir != -1) {
|
|
RDMSR(CState_IO_MWAIT, MSR_PMG_IO_CAPTURE_BASE);
|
|
}
|
|
}
|
|
|
|
switch (CState_IO_MWAIT.CStateRange) {
|
|
case 0b010:
|
|
Core->Query.CStateInclude = 7;
|
|
break;
|
|
case 0b001:
|
|
Core->Query.CStateInclude = 6;
|
|
break;
|
|
case 0b000:
|
|
Core->Query.CStateInclude = 3;
|
|
break;
|
|
default:
|
|
Core->Query.CStateInclude = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void PerCore_AMD_PStates(CORE *Core)
|
|
{
|
|
if (Experimental == 1) {
|
|
FIDVID_STATUS FidVidStatus = {.value = 0};
|
|
FIDVID_CONTROL FidVidControl = {.value = 0};
|
|
int NewFID = -1, NewVID = -1, loop = 100;
|
|
|
|
RDMSR(FidVidStatus, MSR_K7_FID_VID_STATUS);
|
|
|
|
NewFID = ((PState_FID >= FidVidStatus.StartFID)
|
|
&& (PState_FID <= FidVidStatus.MaxFID)) ?
|
|
PState_FID : FidVidStatus.CurrFID,
|
|
|
|
NewVID = ((PState_VID <= FidVidStatus.StartVID)
|
|
&& (PState_VID >= FidVidStatus.MaxVID)) ?
|
|
PState_VID : FidVidStatus.CurrVID;
|
|
do {
|
|
if (FidVidStatus.FidVidPending == 0) {
|
|
RDMSR(FidVidControl, MSR_K7_FID_VID_CTL);
|
|
|
|
FidVidControl.InitFidVid = 1;
|
|
FidVidControl.StpGntTOCnt = 400;
|
|
FidVidControl.NewFID = NewFID;
|
|
FidVidControl.NewVID = NewVID;
|
|
|
|
WRMSR(FidVidControl, MSR_K7_FID_VID_CTL);
|
|
loop = 0;
|
|
} else
|
|
RDMSR(FidVidStatus, MSR_K7_FID_VID_STATUS);
|
|
|
|
if (loop == 0) {
|
|
break;
|
|
} else
|
|
loop-- ;
|
|
} while (FidVidStatus.FidVidPending == 1) ;
|
|
}
|
|
}
|
|
|
|
void Microcode(CORE *Core)
|
|
{
|
|
MICROCODE_ID Microcode = {.value = 0};
|
|
RDMSR(Microcode, MSR_IA32_UCODE_REV);
|
|
Core->Query.Microcode = Microcode.Signature;
|
|
}
|
|
|
|
void PerCore_Intel_Query(CORE *Core, unsigned int cpu)
|
|
{
|
|
Microcode(Core);
|
|
|
|
Dump_CPUID(Core);
|
|
|
|
BITSET(LOCKLESS, Proc->SpeedStep_Mask, cpu);
|
|
BITSET(LOCKLESS, Proc->TurboBoost_Mask, cpu);
|
|
BITSET(LOCKLESS, Proc->C1E_Mask, cpu);
|
|
BITSET(LOCKLESS, Proc->C3A_Mask, cpu);
|
|
BITSET(LOCKLESS, Proc->C1A_Mask, cpu);
|
|
BITSET(LOCKLESS, Proc->C3U_Mask, cpu);
|
|
BITSET(LOCKLESS, Proc->C1U_Mask, cpu);
|
|
|
|
PowerThermal(Core, cpu);
|
|
|
|
ThermalMonitor_Set(Core);
|
|
}
|
|
|
|
void PerCore_AMD_Query(CORE *Core, unsigned int cpu)
|
|
{
|
|
Dump_CPUID(Core);
|
|
|
|
Query_AMD_C1E(Core, cpu);
|
|
|
|
BITSET(LOCKLESS, Proc->ODCM_Mask, cpu);
|
|
BITSET(LOCKLESS, Proc->PowerMgmt_Mask, cpu);
|
|
BITSET(LOCKLESS, Proc->SpeedStep_Mask, cpu);
|
|
BITSET(LOCKLESS, Proc->TurboBoost_Mask, cpu);
|
|
BITSET(LOCKLESS, Proc->C3A_Mask, cpu);
|
|
BITSET(LOCKLESS, Proc->C1A_Mask, cpu);
|
|
BITSET(LOCKLESS, Proc->C3U_Mask, cpu);
|
|
BITSET(LOCKLESS, Proc->C1U_Mask, cpu);
|
|
|
|
PerCore_AMD_PStates(Core);
|
|
}
|
|
|
|
void PerCore_Core2_Query(CORE *Core, unsigned int cpu)
|
|
{
|
|
Microcode(Core);
|
|
|
|
Dump_CPUID(Core);
|
|
|
|
SpeedStep_Technology(Core, cpu);
|
|
DynamicAcceleration(Core, cpu); // Unique
|
|
|
|
BITSET(LOCKLESS, Proc->C1E_Mask, cpu);
|
|
BITSET(LOCKLESS, Proc->C3A_Mask, cpu);
|
|
BITSET(LOCKLESS, Proc->C1A_Mask, cpu);
|
|
BITSET(LOCKLESS, Proc->C3U_Mask, cpu);
|
|
BITSET(LOCKLESS, Proc->C1U_Mask, cpu);
|
|
|
|
PowerThermal(Core, cpu); // Shared|Unique
|
|
|
|
ThermalMonitor_Set(Core);
|
|
}
|
|
|
|
void PerCore_Nehalem_Query(CORE *Core, unsigned int cpu)
|
|
{
|
|
Microcode(Core);
|
|
|
|
Dump_CPUID(Core);
|
|
|
|
SpeedStep_Technology(Core, cpu);
|
|
TurboBoost_Technology(Core, cpu);
|
|
Query_Intel_C1E(Core, cpu);
|
|
|
|
if (Core->T.ThreadID == 0) { // Per Core
|
|
CStatesConfiguration(0x061A, Core, cpu);
|
|
}
|
|
PowerThermal(Core, cpu);
|
|
|
|
ThermalMonitor_Set(Core);
|
|
}
|
|
|
|
void PerCore_SandyBridge_Query(CORE *Core, unsigned int cpu)
|
|
{
|
|
Microcode(Core);
|
|
|
|
Dump_CPUID(Core);
|
|
|
|
SpeedStep_Technology(Core, cpu);
|
|
TurboBoost_Technology(Core, cpu);
|
|
Query_Intel_C1E(Core, cpu);
|
|
|
|
if (Core->T.ThreadID == 0) { // Per Core
|
|
CStatesConfiguration(0x062A, Core, cpu);
|
|
}
|
|
PowerThermal(Core, cpu);
|
|
|
|
ThermalMonitor_Set(Core);
|
|
}
|
|
|
|
void PerCore_Haswell_ULT_Query(CORE *Core, unsigned int cpu)
|
|
{
|
|
Microcode(Core);
|
|
|
|
Dump_CPUID(Core);
|
|
|
|
SpeedStep_Technology(Core, cpu);
|
|
TurboBoost_Technology(Core, cpu);
|
|
Query_Intel_C1E(Core, cpu);
|
|
|
|
if (Core->T.ThreadID == 0) { // Per Core
|
|
CStatesConfiguration(0x0645, Core, cpu);
|
|
}
|
|
PowerThermal(Core, cpu);
|
|
|
|
ThermalMonitor_Set(Core);
|
|
}
|
|
|
|
void Sys_DumpTask(SYSGATE *SysGate)
|
|
{ // Source: /include/linux/sched.h
|
|
struct task_struct *process, *thread;
|
|
int cnt = 0;
|
|
|
|
rcu_read_lock();
|
|
for_each_process_thread(process, thread) {
|
|
task_lock(thread);
|
|
|
|
SysGate->taskList[cnt].runtime = thread->se.sum_exec_runtime;
|
|
SysGate->taskList[cnt].usertime = thread->utime;
|
|
SysGate->taskList[cnt].systime = thread->stime;
|
|
SysGate->taskList[cnt].state = thread->state;
|
|
SysGate->taskList[cnt].wake_cpu = thread->wake_cpu;
|
|
SysGate->taskList[cnt].pid = thread->pid;
|
|
SysGate->taskList[cnt].tgid = thread->tgid;
|
|
SysGate->taskList[cnt].ppid = thread->parent->pid;
|
|
memcpy(SysGate->taskList[cnt].comm, thread->comm,TASK_COMM_LEN);
|
|
|
|
task_unlock(thread);
|
|
cnt++;
|
|
}
|
|
rcu_read_unlock();
|
|
SysGate->taskCount = cnt;
|
|
}
|
|
|
|
void Sys_MemInfo(SYSGATE *SysGate)
|
|
{ // Source: /include/uapi/linux/sysinfo.h
|
|
struct sysinfo info;
|
|
si_meminfo(&info);
|
|
|
|
SysGate->memInfo.totalram = info.totalram << (PAGE_SHIFT - 10);
|
|
SysGate->memInfo.sharedram = info.sharedram << (PAGE_SHIFT - 10);
|
|
SysGate->memInfo.freeram = info.freeram << (PAGE_SHIFT - 10);
|
|
SysGate->memInfo.bufferram = info.bufferram << (PAGE_SHIFT - 10);
|
|
SysGate->memInfo.totalhigh = info.totalhigh << (PAGE_SHIFT - 10);
|
|
SysGate->memInfo.freehigh = info.freehigh << (PAGE_SHIFT - 10);
|
|
}
|
|
|
|
#define Sys_Tick(Pkg) \
|
|
({ \
|
|
if (Pkg->SysGate != NULL) { \
|
|
Pkg->tickStep--; \
|
|
if (!Pkg->tickStep) { \
|
|
Pkg->tickStep = Pkg->tickReset; \
|
|
\
|
|
Sys_DumpTask(Pkg->SysGate); \
|
|
Sys_MemInfo(Pkg->SysGate); \
|
|
} \
|
|
} \
|
|
})
|
|
|
|
void InitTimer(void *Cycle_Function)
|
|
{
|
|
unsigned int cpu=smp_processor_id();
|
|
|
|
if (BITVAL(KPrivate->Join[cpu]->TSM, CREATED) == 0) {
|
|
hrtimer_init( &KPrivate->Join[cpu]->Timer,
|
|
CLOCK_MONOTONIC,
|
|
HRTIMER_MODE_REL_PINNED);
|
|
|
|
KPrivate->Join[cpu]->Timer.function = Cycle_Function;
|
|
BITSET(LOCKLESS, KPrivate->Join[cpu]->TSM, CREATED);
|
|
}
|
|
}
|
|
|
|
void Controller_Init(void)
|
|
{
|
|
CLOCK clock = {.Q = 0, .R = 0, .Hz = 0};
|
|
unsigned int cpu = Proc->CPU.Count;
|
|
|
|
if (Arch[Proc->ArchID].Query != NULL)
|
|
Arch[Proc->ArchID].Query();
|
|
|
|
do { // from last AP to BSP
|
|
cpu--;
|
|
|
|
if (!BITVAL(KPublic->Core[cpu]->OffLine, OS)) {
|
|
unsigned int ratio = Proc->Boost[1];
|
|
|
|
if ((AutoClock != 0) && (ratio != 0)) {
|
|
clock = Base_Clock(cpu, ratio);
|
|
} // else ENOMEM
|
|
if ((clock.Hz == 0) && (ratio != 0)) {
|
|
if (Arch[Proc->ArchID].Clock != NULL)
|
|
clock = Arch[Proc->ArchID].Clock(ratio);
|
|
}
|
|
if (clock.Hz == 0) {
|
|
clock.Q = 100;
|
|
clock.R = 0;
|
|
clock.Hz = 100000000L;
|
|
}
|
|
KPublic->Core[cpu]->Clock = clock;
|
|
|
|
if (Arch[Proc->ArchID].Timer != NULL)
|
|
Arch[Proc->ArchID].Timer(cpu);
|
|
}
|
|
} while (cpu != 0) ;
|
|
}
|
|
|
|
void Controller_Start(int wait)
|
|
{
|
|
if (Arch[Proc->ArchID].Start != NULL) {
|
|
unsigned int cpu;
|
|
for (cpu = 0; cpu < Proc->CPU.Count; cpu++)
|
|
if ((BITVAL(KPrivate->Join[cpu]->TSM, CREATED) == 1)
|
|
&& (BITVAL(KPrivate->Join[cpu]->TSM, STARTED) == 0))
|
|
smp_call_function_single(cpu,
|
|
Arch[Proc->ArchID].Start,
|
|
NULL, wait);
|
|
}
|
|
}
|
|
|
|
void Controller_Stop(int wait)
|
|
{
|
|
if (Arch[Proc->ArchID].Stop != NULL) {
|
|
unsigned int cpu;
|
|
for (cpu = 0; cpu < Proc->CPU.Count; cpu++)
|
|
if ((BITVAL(KPrivate->Join[cpu]->TSM, CREATED) == 1)
|
|
&& (BITVAL(KPrivate->Join[cpu]->TSM, STARTED) == 1))
|
|
smp_call_function_single(cpu,
|
|
Arch[Proc->ArchID].Stop,
|
|
NULL, wait);
|
|
}
|
|
}
|
|
|
|
void Controller_Exit(void)
|
|
{
|
|
unsigned int cpu;
|
|
|
|
if (Arch[Proc->ArchID].Exit != NULL)
|
|
Arch[Proc->ArchID].Exit();
|
|
|
|
for (cpu = 0; cpu < Proc->CPU.Count; cpu++)
|
|
BITCLR(LOCKLESS, KPrivate->Join[cpu]->TSM, CREATED);
|
|
}
|
|
|
|
void Core_Counters_Set(CORE *Core)
|
|
{
|
|
if (Proc->Features.PerfMon.EAX.Version >= 2) {
|
|
CORE_GLOBAL_PERF_CONTROL Core_GlobalPerfControl = {.value = 0};
|
|
CORE_FIXED_PERF_CONTROL Core_FixedPerfControl = {.value = 0};
|
|
CORE_GLOBAL_PERF_STATUS Core_PerfOverflow = {.value = 0};
|
|
CORE_GLOBAL_PERF_OVF_CTRL Core_PerfOvfControl = {.value = 0};
|
|
|
|
RDMSR(Core_GlobalPerfControl, MSR_CORE_PERF_GLOBAL_CTRL);
|
|
Core->SaveArea.Core_GlobalPerfControl = Core_GlobalPerfControl;
|
|
Core_GlobalPerfControl.EN_FIXED_CTR0 = 1;
|
|
Core_GlobalPerfControl.EN_FIXED_CTR1 = 1;
|
|
Core_GlobalPerfControl.EN_FIXED_CTR2 = 1;
|
|
WRMSR(Core_GlobalPerfControl, MSR_CORE_PERF_GLOBAL_CTRL);
|
|
|
|
RDMSR(Core_FixedPerfControl, MSR_CORE_PERF_FIXED_CTR_CTRL);
|
|
Core->SaveArea.Core_FixedPerfControl = Core_FixedPerfControl;
|
|
Core_FixedPerfControl.EN0_OS = 1;
|
|
Core_FixedPerfControl.EN1_OS = 1;
|
|
Core_FixedPerfControl.EN2_OS = 1;
|
|
Core_FixedPerfControl.EN0_Usr = 1;
|
|
Core_FixedPerfControl.EN1_Usr = 1;
|
|
Core_FixedPerfControl.EN2_Usr = 1;
|
|
|
|
if (Proc->Features.PerfMon.EAX.Version >= 3) {
|
|
if (!Proc->Features.HTT_Enable) {
|
|
Core_FixedPerfControl.AnyThread_EN0 = 1;
|
|
Core_FixedPerfControl.AnyThread_EN1 = 1;
|
|
Core_FixedPerfControl.AnyThread_EN2 = 1;
|
|
} else {
|
|
// Per Thread
|
|
Core_FixedPerfControl.AnyThread_EN0 = 0;
|
|
Core_FixedPerfControl.AnyThread_EN1 = 0;
|
|
Core_FixedPerfControl.AnyThread_EN2 = 0;
|
|
}
|
|
}
|
|
WRMSR(Core_FixedPerfControl, MSR_CORE_PERF_FIXED_CTR_CTRL);
|
|
|
|
RDMSR(Core_PerfOverflow, MSR_CORE_PERF_GLOBAL_STATUS);
|
|
if (Core_PerfOverflow.Overflow_CTR0)
|
|
Core_PerfOvfControl.Clear_Ovf_CTR0 = 1;
|
|
if (Core_PerfOverflow.Overflow_CTR1)
|
|
Core_PerfOvfControl.Clear_Ovf_CTR1 = 1;
|
|
if (Core_PerfOverflow.Overflow_CTR2)
|
|
Core_PerfOvfControl.Clear_Ovf_CTR2 = 1;
|
|
if (Core_PerfOverflow.Overflow_CTR0
|
|
| Core_PerfOverflow.Overflow_CTR1
|
|
| Core_PerfOverflow.Overflow_CTR2)
|
|
WRMSR(Core_PerfOvfControl, MSR_CORE_PERF_GLOBAL_OVF_CTRL);
|
|
}
|
|
}
|
|
|
|
#define Uncore_Counters_Set(PMU, Core) \
|
|
({ \
|
|
if ((Proc->Features.PerfMon.EAX.Version >= 3) && (Core->T.Base.BSP))\
|
|
{ \
|
|
UNCORE_GLOBAL_PERF_CONTROL Uncore_GlobalPerfControl; \
|
|
UNCORE_FIXED_PERF_CONTROL Uncore_FixedPerfControl; \
|
|
UNCORE_GLOBAL_PERF_STATUS Uncore_PerfOverflow = {.value = 0}; \
|
|
UNCORE_GLOBAL_PERF_OVF_CTRL Uncore_PerfOvfControl = {.value = 0};\
|
|
\
|
|
RDMSR(Uncore_GlobalPerfControl, MSR_##PMU##_UNCORE_PERF_GLOBAL_CTRL);\
|
|
Proc->SaveArea.Uncore_GlobalPerfControl = Uncore_GlobalPerfControl;\
|
|
Uncore_GlobalPerfControl.PMU.EN_FIXED_CTR0 = 1; \
|
|
WRMSR(Uncore_GlobalPerfControl, MSR_##PMU##_UNCORE_PERF_GLOBAL_CTRL);\
|
|
\
|
|
RDMSR(Uncore_FixedPerfControl, MSR_UNCORE_PERF_FIXED_CTR_CTRL); \
|
|
Proc->SaveArea.Uncore_FixedPerfControl = Uncore_FixedPerfControl;\
|
|
Uncore_FixedPerfControl.PMU.EN_PMC0 = 1; \
|
|
WRMSR(Uncore_FixedPerfControl, MSR_UNCORE_PERF_FIXED_CTR_CTRL); \
|
|
\
|
|
RDMSR(Uncore_PerfOverflow, MSR_##PMU##_UNCORE_PERF_GLOBAL_STATUS);\
|
|
if (Uncore_PerfOverflow.PMU.Overflow_CTR0) { \
|
|
Uncore_PerfOvfControl.Clear_Ovf_CTR0 = 1; \
|
|
WRMSR(Uncore_PerfOvfControl, MSR_UNCORE_PERF_GLOBAL_OVF_CTRL);\
|
|
} \
|
|
} \
|
|
})
|
|
|
|
void Core_Counters_Clear(CORE *Core)
|
|
{
|
|
if (Proc->Features.PerfMon.EAX.Version >= 2) {
|
|
WRMSR(Core->SaveArea.Core_FixedPerfControl,
|
|
MSR_CORE_PERF_FIXED_CTR_CTRL);
|
|
WRMSR(Core->SaveArea.Core_GlobalPerfControl,
|
|
MSR_CORE_PERF_GLOBAL_CTRL);
|
|
}
|
|
}
|
|
|
|
#define Uncore_Counters_Clear(PMU, Core) \
|
|
({ \
|
|
if ((Proc->Features.PerfMon.EAX.Version >= 3) && (Core->T.Base.BSP))\
|
|
{ \
|
|
WRMSR(Proc->SaveArea.Uncore_FixedPerfControl, \
|
|
MSR_UNCORE_PERF_FIXED_CTR_CTRL);\
|
|
WRMSR(Proc->SaveArea.Uncore_GlobalPerfControl, \
|
|
MSR_##PMU##_UNCORE_PERF_GLOBAL_CTRL); \
|
|
} \
|
|
})
|
|
|
|
#define Counters_Genuine(Core, T) \
|
|
({ \
|
|
RDTSC_COUNTERx2(Core->Counter[T].TSC, \
|
|
MSR_IA32_APERF, Core->Counter[T].C0.UCC, \
|
|
MSR_IA32_MPERF, Core->Counter[T].C0.URC); \
|
|
/* Derive C1 */ \
|
|
Core->Counter[T].C1 = \
|
|
(Core->Counter[T].TSC > Core->Counter[T].C0.URC) ? \
|
|
Core->Counter[T].TSC - Core->Counter[T].C0.URC \
|
|
: 0; \
|
|
})
|
|
|
|
#define Counters_Core2(Core, T) \
|
|
({ \
|
|
if (!Proc->Features.AdvPower.EDX.Inv_TSC) \
|
|
{ \
|
|
RDTSC_COUNTERx3(Core->Counter[T].TSC, \
|
|
MSR_CORE_PERF_FIXED_CTR1,Core->Counter[T].C0.UCC,\
|
|
MSR_CORE_PERF_FIXED_CTR2,Core->Counter[T].C0.URC,\
|
|
MSR_CORE_PERF_FIXED_CTR0,Core->Counter[T].INST);\
|
|
} \
|
|
else \
|
|
{ \
|
|
RDTSCP_COUNTERx3(Core->Counter[T].TSC, \
|
|
MSR_CORE_PERF_FIXED_CTR1,Core->Counter[T].C0.UCC,\
|
|
MSR_CORE_PERF_FIXED_CTR2,Core->Counter[T].C0.URC,\
|
|
MSR_CORE_PERF_FIXED_CTR0,Core->Counter[T].INST);\
|
|
} \
|
|
/* Derive C1 */ \
|
|
Core->Counter[T].C1 = \
|
|
(Core->Counter[T].TSC > Core->Counter[T].C0.URC) ? \
|
|
Core->Counter[T].TSC - Core->Counter[T].C0.URC \
|
|
: 0; \
|
|
})
|
|
|
|
#define SMT_Counters_Nehalem(Core, T) \
|
|
({ \
|
|
register unsigned long long Cx = 0; \
|
|
\
|
|
RDTSCP_COUNTERx5(Core->Counter[T].TSC, \
|
|
MSR_CORE_PERF_FIXED_CTR1,Core->Counter[T].C0.UCC,\
|
|
MSR_CORE_PERF_FIXED_CTR2,Core->Counter[T].C0.URC,\
|
|
MSR_CORE_C3_RESIDENCY,Core->Counter[T].C3, \
|
|
MSR_CORE_C6_RESIDENCY,Core->Counter[T].C6, \
|
|
MSR_CORE_PERF_FIXED_CTR0,Core->Counter[T].INST);\
|
|
/* Derive C1 */ \
|
|
Cx = Core->Counter[T].C6 \
|
|
+ Core->Counter[T].C3 \
|
|
+ Core->Counter[T].C0.URC; \
|
|
\
|
|
Core->Counter[T].C1 = \
|
|
(Core->Counter[T].TSC > Cx) ? \
|
|
Core->Counter[T].TSC - Cx \
|
|
: 0; \
|
|
})
|
|
|
|
#define SMT_Counters_SandyBridge(Core, T) \
|
|
({ \
|
|
register unsigned long long Cx = 0; \
|
|
\
|
|
RDTSCP_COUNTERx6(Core->Counter[T].TSC, \
|
|
MSR_CORE_PERF_FIXED_CTR1,Core->Counter[T].C0.UCC,\
|
|
MSR_CORE_PERF_FIXED_CTR2,Core->Counter[T].C0.URC,\
|
|
MSR_CORE_C3_RESIDENCY,Core->Counter[T].C3, \
|
|
MSR_CORE_C6_RESIDENCY,Core->Counter[T].C6, \
|
|
MSR_CORE_C7_RESIDENCY,Core->Counter[T].C7, \
|
|
MSR_CORE_PERF_FIXED_CTR0,Core->Counter[T].INST);\
|
|
/* Derive C1 */ \
|
|
Cx = Core->Counter[T].C7 \
|
|
+ Core->Counter[T].C6 \
|
|
+ Core->Counter[T].C3 \
|
|
+ Core->Counter[T].C0.URC; \
|
|
\
|
|
Core->Counter[T].C1 = \
|
|
(Core->Counter[T].TSC > Cx) ? \
|
|
Core->Counter[T].TSC - Cx \
|
|
: 0; \
|
|
})
|
|
|
|
#define Delta_TSC(Core) \
|
|
({ \
|
|
Core->Delta.TSC = Core->Counter[1].TSC \
|
|
- Core->Counter[0].TSC; \
|
|
})
|
|
|
|
#define Delta_C0(Core) \
|
|
({ /* Absolute Delta of Unhalted (Core & Ref) C0 Counter. */ \
|
|
Core->Delta.C0.UCC = \
|
|
(Core->Counter[0].C0.UCC > \
|
|
Core->Counter[1].C0.UCC) ? \
|
|
Core->Counter[0].C0.UCC \
|
|
- Core->Counter[1].C0.UCC \
|
|
: Core->Counter[1].C0.UCC \
|
|
- Core->Counter[0].C0.UCC; \
|
|
\
|
|
Core->Delta.C0.URC = Core->Counter[1].C0.URC \
|
|
- Core->Counter[0].C0.URC; \
|
|
})
|
|
|
|
#define Delta_C1(Core) \
|
|
({ \
|
|
Core->Delta.C1 = \
|
|
(Core->Counter[0].C1 > \
|
|
Core->Counter[1].C1) ? \
|
|
Core->Counter[0].C1 \
|
|
- Core->Counter[1].C1 \
|
|
: Core->Counter[1].C1 \
|
|
- Core->Counter[0].C1; \
|
|
})
|
|
|
|
#define Delta_C3(Core) \
|
|
({ \
|
|
Core->Delta.C3 = Core->Counter[1].C3 \
|
|
- Core->Counter[0].C3; \
|
|
})
|
|
|
|
#define Delta_C6(Core) \
|
|
({ \
|
|
Core->Delta.C6 = Core->Counter[1].C6 \
|
|
- Core->Counter[0].C6; \
|
|
})
|
|
|
|
#define Delta_C7(Core) \
|
|
({ \
|
|
Core->Delta.C7 = Core->Counter[1].C7 \
|
|
- Core->Counter[0].C7; \
|
|
})
|
|
|
|
#define Delta_INST(Core) \
|
|
({ /* Delta of Instructions Retired */ \
|
|
Core->Delta.INST = Core->Counter[1].INST \
|
|
- Core->Counter[0].INST; \
|
|
})
|
|
|
|
#define PKG_Counters_Nehalem(Core, T) \
|
|
({ \
|
|
RDTSCP_COUNTERx4(Proc->Counter[T].PTSC, \
|
|
MSR_PKG_C3_RESIDENCY, Proc->Counter[T].PC03, \
|
|
MSR_PKG_C6_RESIDENCY, Proc->Counter[T].PC06, \
|
|
MSR_PKG_C7_RESIDENCY, Proc->Counter[T].PC07, \
|
|
MSR_UNCORE_PERF_FIXED_CTR0, Proc->Counter[T].Uncore.FC0);\
|
|
})
|
|
|
|
#define PKG_Counters_SandyBridge(Core, T) \
|
|
({ \
|
|
RDTSCP_COUNTERx4(Proc->Counter[T].PTSC, \
|
|
MSR_PKG_C2_RESIDENCY, Proc->Counter[T].PC02, \
|
|
MSR_PKG_C3_RESIDENCY, Proc->Counter[T].PC03, \
|
|
MSR_PKG_C6_RESIDENCY, Proc->Counter[T].PC06, \
|
|
MSR_PKG_C7_RESIDENCY, Proc->Counter[T].PC07); \
|
|
})
|
|
|
|
#define PKG_Counters_Haswell_ULT(Core, T) \
|
|
({ \
|
|
RDTSCP_COUNTERx7(Proc->Counter[T].PTSC, \
|
|
MSR_PKG_C2_RESIDENCY, Proc->Counter[T].PC02, \
|
|
MSR_PKG_C3_RESIDENCY, Proc->Counter[T].PC03, \
|
|
MSR_PKG_C6_RESIDENCY, Proc->Counter[T].PC06, \
|
|
MSR_PKG_C7_RESIDENCY, Proc->Counter[T].PC07, \
|
|
MSR_PKG_C8_RESIDENCY, Proc->Counter[T].PC08, \
|
|
MSR_PKG_C9_RESIDENCY, Proc->Counter[T].PC09, \
|
|
MSR_PKG_C10_RESIDENCY,Proc->Counter[T].PC10); \
|
|
})
|
|
|
|
#define Delta_PTSC(Pkg) \
|
|
({ \
|
|
Pkg->Delta.PTSC = Pkg->Counter[1].PTSC \
|
|
- Pkg->Counter[0].PTSC; \
|
|
})
|
|
|
|
#define Delta_PC02(Pkg) \
|
|
({ \
|
|
Pkg->Delta.PC02 = Pkg->Counter[1].PC02 \
|
|
- Pkg->Counter[0].PC02; \
|
|
})
|
|
|
|
#define Delta_PC03(Pkg) \
|
|
({ \
|
|
Pkg->Delta.PC03 = Pkg->Counter[1].PC03 \
|
|
- Pkg->Counter[0].PC03; \
|
|
})
|
|
|
|
#define Delta_PC06(Pkg) \
|
|
({ \
|
|
Pkg->Delta.PC06 = Pkg->Counter[1].PC06 \
|
|
- Pkg->Counter[0].PC06; \
|
|
})
|
|
|
|
#define Delta_PC07(Pkg) \
|
|
({ \
|
|
Pkg->Delta.PC07 = Pkg->Counter[1].PC07 \
|
|
- Pkg->Counter[0].PC07; \
|
|
})
|
|
|
|
#define Delta_PC08(Pkg) \
|
|
({ \
|
|
Pkg->Delta.PC08 = Pkg->Counter[1].PC08 \
|
|
- Pkg->Counter[0].PC08; \
|
|
})
|
|
|
|
#define Delta_PC09(Pkg) \
|
|
({ \
|
|
Pkg->Delta.PC09 = Pkg->Counter[1].PC09 \
|
|
- Pkg->Counter[0].PC09; \
|
|
})
|
|
|
|
#define Delta_PC10(Pkg) \
|
|
({ \
|
|
Pkg->Delta.PC10 = Pkg->Counter[1].PC10 \
|
|
- Pkg->Counter[0].PC10; \
|
|
})
|
|
|
|
#define Delta_UNCORE_FC0(Pkg) \
|
|
({ \
|
|
Pkg->Delta.Uncore.FC0 = Pkg->Counter[1].Uncore.FC0 \
|
|
- Pkg->Counter[0].Uncore.FC0; \
|
|
})
|
|
|
|
#define Save_TSC(Core) \
|
|
({ /* Save Time Stamp Counter. */ \
|
|
Core->Counter[0].TSC = Core->Counter[1].TSC; \
|
|
})
|
|
|
|
#define Save_C0(Core) \
|
|
({ /* Save the Unhalted Core & Reference Counter */ \
|
|
Core->Counter[0].C0.UCC = Core->Counter[1].C0.UCC; \
|
|
Core->Counter[0].C0.URC = Core->Counter[1].C0.URC; \
|
|
})
|
|
|
|
#define Save_C1(Core) \
|
|
({ \
|
|
Core->Counter[0].C1 = Core->Counter[1].C1; \
|
|
})
|
|
|
|
#define Save_C3(Core) \
|
|
({ \
|
|
Core->Counter[0].C3 = Core->Counter[1].C3; \
|
|
})
|
|
|
|
#define Save_C6(Core) \
|
|
({ \
|
|
Core->Counter[0].C6 = Core->Counter[1].C6; \
|
|
})
|
|
|
|
#define Save_C7(Core) \
|
|
({ \
|
|
Core->Counter[0].C7 = Core->Counter[1].C7; \
|
|
})
|
|
|
|
#define Save_INST(Core) \
|
|
({ /* Save the Instructions counter. */ \
|
|
Core->Counter[0].INST = Core->Counter[1].INST; \
|
|
})
|
|
|
|
#define Save_PTSC(Pkg) \
|
|
({ \
|
|
Pkg->Counter[0].PTSC = Pkg->Counter[1].PTSC; \
|
|
})
|
|
|
|
#define Save_PC02(Pkg) \
|
|
({ \
|
|
Pkg->Counter[0].PC02 = Pkg->Counter[1].PC02; \
|
|
})
|
|
|
|
#define Save_PC03(Pkg) \
|
|
({ \
|
|
Pkg->Counter[0].PC03 = Pkg->Counter[1].PC03; \
|
|
})
|
|
|
|
#define Save_PC06(Pkg) \
|
|
({ \
|
|
Pkg->Counter[0].PC06 = Pkg->Counter[1].PC06; \
|
|
})
|
|
|
|
#define Save_PC07(Pkg) \
|
|
({ \
|
|
Pkg->Counter[0].PC07 = Pkg->Counter[1].PC07; \
|
|
})
|
|
|
|
#define Save_PC08(Pkg) \
|
|
({ \
|
|
Pkg->Counter[0].PC08 = Pkg->Counter[1].PC08; \
|
|
})
|
|
|
|
#define Save_PC09(Pkg) \
|
|
({ \
|
|
Pkg->Counter[0].PC09 = Pkg->Counter[1].PC09; \
|
|
})
|
|
|
|
#define Save_PC10(Pkg) \
|
|
({ \
|
|
Pkg->Counter[0].PC10 = Pkg->Counter[1].PC10; \
|
|
})
|
|
|
|
#define Save_UNCORE_FC0(Pkg) \
|
|
({ \
|
|
Pkg->Counter[0].Uncore.FC0 = Pkg->Counter[1].Uncore.FC0; \
|
|
})
|
|
|
|
void Core_Intel_Temp(CORE *Core)
|
|
{
|
|
THERM_STATUS ThermStatus = {.value = 0};
|
|
RDMSR(ThermStatus, MSR_IA32_THERM_STATUS); // All Intel families.
|
|
|
|
Core->PowerThermal.Sensor = ThermStatus.DTS;
|
|
Core->PowerThermal.Trip = ThermStatus.StatusBit | ThermStatus.StatusLog;
|
|
}
|
|
|
|
void Core_AMD_Temp(CORE *Core)
|
|
{
|
|
if (Proc->Features.AdvPower.EDX.TTP == 1) {
|
|
THERMTRIP_STATUS ThermTrip;
|
|
|
|
RDPCI(ThermTrip, PCI_CONFIG_ADDRESS(0, 24, 3, 0xe4));
|
|
|
|
// Select Core to read sensor from:
|
|
ThermTrip.SensorCoreSelect = Core->Bind;
|
|
|
|
WRPCI(ThermTrip, PCI_CONFIG_ADDRESS(0, 24, 3, 0xe4));
|
|
RDPCI(ThermTrip, PCI_CONFIG_ADDRESS(0, 24, 3, 0xe4));
|
|
|
|
// Formula is " CurTmp - (TjOffset * 2) - 49 "
|
|
Core->PowerThermal.Target = ThermTrip.TjOffset;
|
|
Core->PowerThermal.Sensor = ThermTrip.CurrentTemp;
|
|
|
|
Core->PowerThermal.Trip = ThermTrip.SensorTrip;
|
|
}
|
|
}
|
|
|
|
static enum hrtimer_restart Cycle_GenuineIntel(struct hrtimer *pTimer)
|
|
{
|
|
unsigned int cpu = smp_processor_id();
|
|
CORE *Core = (CORE *) KPublic->Core[cpu];
|
|
|
|
if (BITVAL(KPrivate->Join[cpu]->TSM, MUSTFWD) == 1) {
|
|
hrtimer_forward(pTimer,
|
|
hrtimer_cb_get_time(pTimer),
|
|
RearmTheTimer);
|
|
|
|
Counters_Genuine(Core, 1);
|
|
|
|
if (Core->T.Base.BSP) {
|
|
Sys_Tick(Proc);
|
|
}
|
|
|
|
Core_Intel_Temp(Core);
|
|
|
|
Delta_C0(Core);
|
|
|
|
Delta_TSC(Core);
|
|
|
|
Delta_C1(Core);
|
|
|
|
Save_TSC(Core);
|
|
|
|
Save_C0(Core);
|
|
|
|
Save_C1(Core);
|
|
|
|
BITSET(LOCKLESS, Core->Sync.V, 63);
|
|
|
|
return(HRTIMER_RESTART);
|
|
} else
|
|
return(HRTIMER_NORESTART);
|
|
}
|
|
|
|
static enum hrtimer_restart Cycle_AuthenticAMD(struct hrtimer *pTimer)
|
|
{
|
|
unsigned int cpu = smp_processor_id();
|
|
CORE *Core = (CORE *) KPublic->Core[cpu];
|
|
|
|
if (BITVAL(KPrivate->Join[cpu]->TSM, MUSTFWD) == 1) {
|
|
hrtimer_forward(pTimer,
|
|
hrtimer_cb_get_time(pTimer),
|
|
RearmTheTimer);
|
|
|
|
Counters_Genuine(Core, 1);
|
|
|
|
if (Core->T.Base.BSP) {
|
|
Sys_Tick(Proc);
|
|
}
|
|
|
|
Delta_C0(Core);
|
|
|
|
Delta_TSC(Core);
|
|
|
|
Delta_C1(Core);
|
|
|
|
Save_TSC(Core);
|
|
|
|
Save_C0(Core);
|
|
|
|
Save_C1(Core);
|
|
|
|
BITSET(LOCKLESS, Core->Sync.V, 63);
|
|
|
|
return(HRTIMER_RESTART);
|
|
} else
|
|
return(HRTIMER_NORESTART);
|
|
}
|
|
|
|
void InitTimer_GenuineIntel(unsigned int cpu)
|
|
{
|
|
smp_call_function_single(cpu, InitTimer, Cycle_GenuineIntel, 1);
|
|
}
|
|
|
|
void Start_GenuineIntel(void *arg)
|
|
{
|
|
unsigned int cpu = smp_processor_id();
|
|
CORE *Core = (CORE *) KPublic->Core[cpu];
|
|
|
|
PerCore_Intel_Query(Core, cpu);
|
|
|
|
Counters_Genuine(Core, 0);
|
|
|
|
BITSET(LOCKLESS, KPrivate->Join[cpu]->TSM, MUSTFWD);
|
|
|
|
hrtimer_start( &KPrivate->Join[cpu]->Timer,
|
|
RearmTheTimer,
|
|
HRTIMER_MODE_REL_PINNED);
|
|
|
|
BITSET(LOCKLESS, KPrivate->Join[cpu]->TSM, STARTED);
|
|
}
|
|
|
|
void Stop_GenuineIntel(void *arg)
|
|
{
|
|
unsigned int cpu = smp_processor_id();
|
|
|
|
BITCLR(LOCKLESS, KPrivate->Join[cpu]->TSM, MUSTFWD);
|
|
|
|
hrtimer_cancel(&KPrivate->Join[cpu]->Timer);
|
|
|
|
BITCLR(LOCKLESS, KPrivate->Join[cpu]->TSM, STARTED);
|
|
}
|
|
|
|
/*
|
|
Note: hardware Family_12h
|
|
|
|
static enum hrtimer_restart Cycle_AMD_Family_12h(struct hrtimer *pTimer)
|
|
{
|
|
unsigned int cpu = smp_processor_id();
|
|
CORE *Core = (CORE *) KPublic->Core[cpu];
|
|
|
|
if (BITVAL(KPrivate->Join[cpu]->TSM, MUSTFWD) == 1) {
|
|
hrtimer_forward(pTimer,
|
|
hrtimer_cb_get_time(pTimer),
|
|
RearmTheTimer);
|
|
|
|
// Core Performance Boost instructions
|
|
// [ Here ]
|
|
|
|
// Derive C1
|
|
Core->Counter[1].C1 =
|
|
(Core->Counter[1].TSC > Core->Counter[1].C0.URC) ?
|
|
Core->Counter[1].TSC - Core->Counter[1].C0.URC
|
|
: 0;
|
|
|
|
Delta_C0(Core);
|
|
|
|
Delta_TSC(Core);
|
|
|
|
Delta_C1(Core);
|
|
|
|
Save_TSC(Core);
|
|
|
|
Save_C0(Core);
|
|
|
|
Save_C1(Core);
|
|
|
|
Core_AMD_Temp(Core);
|
|
|
|
BITSET(LOCKLESS, Core->Sync.V, 63);
|
|
|
|
return(HRTIMER_RESTART);
|
|
} else
|
|
return(HRTIMER_NORESTART);
|
|
}
|
|
*/
|
|
|
|
static enum hrtimer_restart Cycle_AMD_Family_0Fh(struct hrtimer *pTimer)
|
|
{
|
|
unsigned int cpu = smp_processor_id();
|
|
CORE *Core = (CORE *) KPublic->Core[cpu];
|
|
|
|
if (BITVAL(KPrivate->Join[cpu]->TSM, MUSTFWD) == 1) {
|
|
FIDVID_STATUS FidVidStatus = {.value = 0};
|
|
|
|
hrtimer_forward(pTimer,
|
|
hrtimer_cb_get_time(pTimer),
|
|
RearmTheTimer);
|
|
|
|
RDMSR(FidVidStatus, MSR_K7_FID_VID_STATUS);
|
|
|
|
Core->Counter[1].VID = FidVidStatus.CurrVID;
|
|
|
|
// P-States
|
|
Core->Counter[1].C0.UCC = Core->Counter[0].C0.UCC
|
|
+ (8 + FidVidStatus.CurrFID)
|
|
* Core->Clock.Hz;
|
|
|
|
Core->Counter[1].C0.URC = Core->Counter[1].C0.UCC;
|
|
|
|
Core->Counter[1].TSC = Core->Counter[0].TSC
|
|
+ (Proc->Boost[1] * Core->Clock.Hz);
|
|
|
|
/* Derive C1 */
|
|
Core->Counter[1].C1 =
|
|
(Core->Counter[1].TSC > Core->Counter[1].C0.URC) ?
|
|
Core->Counter[1].TSC - Core->Counter[1].C0.URC
|
|
: 0;
|
|
|
|
if (Core->T.Base.BSP) {
|
|
Sys_Tick(Proc);
|
|
}
|
|
|
|
Core_AMD_Temp(Core);
|
|
|
|
Delta_C0(Core);
|
|
|
|
Delta_TSC(Core);
|
|
|
|
Delta_C1(Core);
|
|
|
|
Save_TSC(Core);
|
|
|
|
Save_C0(Core);
|
|
|
|
Save_C1(Core);
|
|
|
|
BITSET(LOCKLESS, Core->Sync.V, 63);
|
|
|
|
return(HRTIMER_RESTART);
|
|
} else
|
|
return(HRTIMER_NORESTART);
|
|
}
|
|
|
|
void InitTimer_AuthenticAMD(unsigned int cpu)
|
|
{
|
|
/*
|
|
Note: hardware Family_12h
|
|
|
|
if (Proc->Features.AdvPower.DX.CPB == 1)
|
|
// Core Performance Boost [Here].
|
|
smp_call_function_single(cpu, InitTimer, Cycle_AMD_Family_12h, 1);
|
|
else
|
|
*/
|
|
if (Proc->Features.Power.ECX.EffFreq == 1) // MPERF & APERF ?
|
|
smp_call_function_single(cpu, InitTimer, Cycle_AuthenticAMD, 1);
|
|
else {
|
|
Proc->thermalFormula = THERMAL_FORMULA_AMD_0F;
|
|
Proc->voltageFormula = VOLTAGE_FORMULA_AMD_0F;
|
|
|
|
smp_call_function_single(cpu, InitTimer, Cycle_AMD_Family_0Fh, 1);
|
|
}
|
|
}
|
|
|
|
void Start_AuthenticAMD(void *arg)
|
|
{
|
|
unsigned int cpu = smp_processor_id();
|
|
CORE *Core=(CORE *) KPublic->Core[cpu];
|
|
|
|
PerCore_AMD_Query(Core, cpu);
|
|
|
|
BITSET(LOCKLESS, KPrivate->Join[cpu]->TSM, MUSTFWD);
|
|
|
|
hrtimer_start( &KPrivate->Join[cpu]->Timer,
|
|
RearmTheTimer,
|
|
HRTIMER_MODE_REL_PINNED);
|
|
|
|
BITSET(LOCKLESS, KPrivate->Join[cpu]->TSM, STARTED);
|
|
}
|
|
|
|
void Stop_AuthenticAMD(void *arg)
|
|
{
|
|
unsigned int cpu = smp_processor_id();
|
|
|
|
BITCLR(LOCKLESS, KPrivate->Join[cpu]->TSM, MUSTFWD);
|
|
|
|
hrtimer_cancel(&KPrivate->Join[cpu]->Timer);
|
|
|
|
BITCLR(LOCKLESS, KPrivate->Join[cpu]->TSM, STARTED);
|
|
}
|
|
|
|
static enum hrtimer_restart Cycle_Core2(struct hrtimer *pTimer)
|
|
{
|
|
PERF_STATUS PerfStatus = {.value = 0};
|
|
unsigned int cpu = smp_processor_id();
|
|
CORE *Core=(CORE *) KPublic->Core[cpu];
|
|
|
|
if (BITVAL(KPrivate->Join[cpu]->TSM, MUSTFWD) == 1) {
|
|
hrtimer_forward(pTimer,
|
|
hrtimer_cb_get_time(pTimer),
|
|
RearmTheTimer);
|
|
|
|
Counters_Core2(Core, 1);
|
|
|
|
if (Core->T.Base.BSP) {
|
|
RDMSR(PerfStatus, MSR_IA32_PERF_STATUS);
|
|
Core->Counter[1].VID = PerfStatus.CORE.CurrVID;
|
|
|
|
Sys_Tick(Proc);
|
|
}
|
|
|
|
Core_Intel_Temp(Core);
|
|
|
|
Delta_INST(Core);
|
|
|
|
Delta_C0(Core);
|
|
|
|
Delta_TSC(Core);
|
|
|
|
Delta_C1(Core);
|
|
|
|
Save_INST(Core);
|
|
|
|
Save_TSC(Core);
|
|
|
|
Save_C0(Core);
|
|
|
|
Save_C1(Core);
|
|
|
|
BITSET(LOCKLESS, Core->Sync.V, 63);
|
|
|
|
return(HRTIMER_RESTART);
|
|
} else
|
|
return(HRTIMER_NORESTART);
|
|
}
|
|
|
|
void InitTimer_Core2(unsigned int cpu)
|
|
{
|
|
smp_call_function_single(cpu, InitTimer, Cycle_Core2, 1);
|
|
}
|
|
|
|
void Start_Core2(void *arg)
|
|
{
|
|
unsigned int cpu = smp_processor_id();
|
|
CORE *Core = (CORE *) KPublic->Core[cpu];
|
|
|
|
PerCore_Core2_Query(Core, cpu);
|
|
|
|
Core_Counters_Set(Core);
|
|
Counters_Core2(Core, 0);
|
|
|
|
BITSET(LOCKLESS, KPrivate->Join[cpu]->TSM, MUSTFWD);
|
|
|
|
hrtimer_start( &KPrivate->Join[cpu]->Timer,
|
|
RearmTheTimer,
|
|
HRTIMER_MODE_REL_PINNED);
|
|
|
|
BITSET(LOCKLESS, KPrivate->Join[cpu]->TSM, STARTED);
|
|
}
|
|
|
|
void Stop_Core2(void *arg)
|
|
{
|
|
unsigned int cpu = smp_processor_id();
|
|
CORE *Core = (CORE *) KPublic->Core[cpu];
|
|
|
|
BITCLR(LOCKLESS, KPrivate->Join[cpu]->TSM, MUSTFWD);
|
|
|
|
hrtimer_cancel(&KPrivate->Join[cpu]->Timer);
|
|
|
|
Core_Counters_Clear(Core);
|
|
|
|
BITCLR(LOCKLESS, KPrivate->Join[cpu]->TSM, STARTED);
|
|
}
|
|
|
|
static enum hrtimer_restart Cycle_Nehalem(struct hrtimer *pTimer)
|
|
{
|
|
unsigned int cpu=smp_processor_id();
|
|
CORE *Core=(CORE *) KPublic->Core[cpu];
|
|
|
|
if (BITVAL(KPrivate->Join[cpu]->TSM, MUSTFWD) == 1) {
|
|
hrtimer_forward(pTimer,
|
|
hrtimer_cb_get_time(pTimer),
|
|
RearmTheTimer);
|
|
|
|
SMT_Counters_Nehalem(Core, 1);
|
|
|
|
if (Core->T.Base.BSP) {
|
|
PKG_Counters_Nehalem(Core, 1);
|
|
|
|
Delta_PC03(Proc);
|
|
|
|
Delta_PC06(Proc);
|
|
|
|
Delta_PC07(Proc);
|
|
|
|
Delta_PTSC(Proc);
|
|
|
|
Delta_UNCORE_FC0(Proc);
|
|
|
|
Save_PC03(Proc);
|
|
|
|
Save_PC06(Proc);
|
|
|
|
Save_PC07(Proc);
|
|
|
|
Save_PTSC(Proc);
|
|
|
|
Save_UNCORE_FC0(Proc);
|
|
|
|
Sys_Tick(Proc);
|
|
}
|
|
|
|
Core_Intel_Temp(Core);
|
|
|
|
RDCOUNTER(Core->Interrupt.SMI, MSR_SMI_COUNT);
|
|
|
|
Delta_INST(Core);
|
|
|
|
Delta_C0(Core);
|
|
|
|
Delta_C3(Core);
|
|
|
|
Delta_C6(Core);
|
|
|
|
Delta_TSC(Core);
|
|
|
|
Delta_C1(Core);
|
|
|
|
Save_INST(Core);
|
|
|
|
Save_TSC(Core);
|
|
|
|
Save_C0(Core);
|
|
|
|
Save_C3(Core);
|
|
|
|
Save_C6(Core);
|
|
|
|
Save_C1(Core);
|
|
|
|
BITSET(LOCKLESS, Core->Sync.V, 63);
|
|
|
|
return(HRTIMER_RESTART);
|
|
} else
|
|
return(HRTIMER_NORESTART);
|
|
}
|
|
|
|
void InitTimer_Nehalem(unsigned int cpu)
|
|
{
|
|
smp_call_function_single(cpu, InitTimer, Cycle_Nehalem, 1);
|
|
}
|
|
|
|
void Start_Nehalem(void *arg)
|
|
{
|
|
unsigned int cpu = smp_processor_id();
|
|
CORE *Core = (CORE *) KPublic->Core[cpu];
|
|
|
|
PerCore_Nehalem_Query(Core, cpu);
|
|
|
|
Core_Counters_Set(Core);
|
|
Uncore_Counters_Set(NHM, Core);
|
|
SMT_Counters_Nehalem(Core, 0);
|
|
|
|
if (Core->T.Base.BSP) {
|
|
PKG_Counters_Nehalem(Core, 0);
|
|
}
|
|
|
|
RDCOUNTER(Core->Interrupt.SMI, MSR_SMI_COUNT);
|
|
|
|
BITSET(LOCKLESS, KPrivate->Join[cpu]->TSM, MUSTFWD);
|
|
|
|
hrtimer_start( &KPrivate->Join[cpu]->Timer,
|
|
RearmTheTimer,
|
|
HRTIMER_MODE_REL_PINNED);
|
|
|
|
BITSET(LOCKLESS, KPrivate->Join[cpu]->TSM, STARTED);
|
|
}
|
|
|
|
void Stop_Nehalem(void *arg)
|
|
{
|
|
unsigned int cpu = smp_processor_id();
|
|
CORE *Core = (CORE *) KPublic->Core[cpu];
|
|
|
|
BITCLR(LOCKLESS, KPrivate->Join[cpu]->TSM, MUSTFWD);
|
|
|
|
hrtimer_cancel(&KPrivate->Join[cpu]->Timer);
|
|
|
|
Core_Counters_Clear(Core);
|
|
Uncore_Counters_Clear(NHM, Core);
|
|
|
|
BITCLR(LOCKLESS, KPrivate->Join[cpu]->TSM, STARTED);
|
|
}
|
|
|
|
|
|
static enum hrtimer_restart Cycle_SandyBridge(struct hrtimer *pTimer)
|
|
{
|
|
PERF_STATUS PerfStatus = {.value = 0};
|
|
unsigned int cpu = smp_processor_id();
|
|
CORE *Core = (CORE *) KPublic->Core[cpu];
|
|
|
|
if (BITVAL(KPrivate->Join[cpu]->TSM, MUSTFWD) == 1) {
|
|
hrtimer_forward(pTimer,
|
|
hrtimer_cb_get_time(pTimer),
|
|
RearmTheTimer);
|
|
|
|
SMT_Counters_SandyBridge(Core, 1);
|
|
|
|
if (Core->T.Base.BSP) {
|
|
PKG_Counters_SandyBridge(Core, 1);
|
|
|
|
RDMSR(PerfStatus, MSR_IA32_PERF_STATUS);
|
|
Core->Counter[1].VID = PerfStatus.SNB.CurrVID;
|
|
|
|
Delta_PC02(Proc);
|
|
|
|
Delta_PC03(Proc);
|
|
|
|
Delta_PC06(Proc);
|
|
|
|
Delta_PC07(Proc);
|
|
|
|
Delta_PTSC(Proc);
|
|
|
|
Delta_UNCORE_FC0(Proc);
|
|
|
|
Save_PC02(Proc);
|
|
|
|
Save_PC03(Proc);
|
|
|
|
Save_PC06(Proc);
|
|
|
|
Save_PC07(Proc);
|
|
|
|
Save_PTSC(Proc);
|
|
|
|
Save_UNCORE_FC0(Proc);
|
|
|
|
Sys_Tick(Proc);
|
|
}
|
|
|
|
Core_Intel_Temp(Core);
|
|
|
|
RDCOUNTER(Core->Interrupt.SMI, MSR_SMI_COUNT);
|
|
|
|
Delta_INST(Core);
|
|
|
|
Delta_C0(Core);
|
|
|
|
Delta_C3(Core);
|
|
|
|
Delta_C6(Core);
|
|
|
|
Delta_C7(Core);
|
|
|
|
Delta_TSC(Core);
|
|
|
|
Delta_C1(Core);
|
|
|
|
Save_INST(Core);
|
|
|
|
Save_TSC(Core);
|
|
|
|
Save_C0(Core);
|
|
|
|
Save_C3(Core);
|
|
|
|
Save_C6(Core);
|
|
|
|
Save_C7(Core);
|
|
|
|
Save_C1(Core);
|
|
|
|
BITSET(LOCKLESS, Core->Sync.V, 63);
|
|
|
|
return(HRTIMER_RESTART);
|
|
} else
|
|
return(HRTIMER_NORESTART);
|
|
}
|
|
|
|
void InitTimer_SandyBridge(unsigned int cpu)
|
|
{
|
|
smp_call_function_single(cpu, InitTimer, Cycle_SandyBridge, 1);
|
|
}
|
|
|
|
void Start_SandyBridge(void *arg)
|
|
{
|
|
unsigned int cpu = smp_processor_id();
|
|
CORE *Core = (CORE *) KPublic->Core[cpu];
|
|
|
|
PerCore_SandyBridge_Query(Core, cpu);
|
|
|
|
Core_Counters_Set(Core);
|
|
SMT_Counters_SandyBridge(Core, 0);
|
|
|
|
if (Core->T.Base.BSP) {
|
|
PKG_Counters_SandyBridge(Core, 0);
|
|
}
|
|
|
|
RDCOUNTER(Core->Interrupt.SMI, MSR_SMI_COUNT);
|
|
|
|
BITSET(LOCKLESS, KPrivate->Join[cpu]->TSM, MUSTFWD);
|
|
|
|
hrtimer_start( &KPrivate->Join[cpu]->Timer,
|
|
RearmTheTimer,
|
|
HRTIMER_MODE_REL_PINNED);
|
|
|
|
BITSET(LOCKLESS, KPrivate->Join[cpu]->TSM, STARTED);
|
|
}
|
|
|
|
void Stop_SandyBridge(void *arg)
|
|
{
|
|
unsigned int cpu = smp_processor_id();
|
|
CORE *Core = (CORE *) KPublic->Core[cpu];
|
|
|
|
BITCLR(LOCKLESS, KPrivate->Join[cpu]->TSM, MUSTFWD);
|
|
|
|
hrtimer_cancel(&KPrivate->Join[cpu]->Timer);
|
|
|
|
Core_Counters_Clear(Core);
|
|
|
|
BITCLR(LOCKLESS, KPrivate->Join[cpu]->TSM, STARTED);
|
|
}
|
|
|
|
|
|
static enum hrtimer_restart Cycle_Haswell_ULT(struct hrtimer *pTimer)
|
|
{
|
|
PERF_STATUS PerfStatus = {.value = 0};
|
|
unsigned int cpu = smp_processor_id();
|
|
CORE *Core = (CORE *) KPublic->Core[cpu];
|
|
|
|
if (BITVAL(KPrivate->Join[cpu]->TSM, MUSTFWD) == 1) {
|
|
hrtimer_forward(pTimer,
|
|
hrtimer_cb_get_time(pTimer),
|
|
RearmTheTimer);
|
|
|
|
SMT_Counters_SandyBridge(Core, 1);
|
|
|
|
if (Core->T.Base.BSP) {
|
|
PKG_Counters_Haswell_ULT(Core, 1);
|
|
|
|
RDMSR(PerfStatus, MSR_IA32_PERF_STATUS);
|
|
Core->Counter[1].VID = PerfStatus.SNB.CurrVID;
|
|
|
|
Delta_PC02(Proc);
|
|
|
|
Delta_PC03(Proc);
|
|
|
|
Delta_PC06(Proc);
|
|
|
|
Delta_PC07(Proc);
|
|
|
|
Delta_PC08(Proc);
|
|
|
|
Delta_PC09(Proc);
|
|
|
|
Delta_PC10(Proc);
|
|
|
|
Delta_PTSC(Proc);
|
|
|
|
Delta_UNCORE_FC0(Proc);
|
|
|
|
Save_PC02(Proc);
|
|
|
|
Save_PC03(Proc);
|
|
|
|
Save_PC06(Proc);
|
|
|
|
Save_PC07(Proc);
|
|
|
|
Save_PC08(Proc);
|
|
|
|
Save_PC09(Proc);
|
|
|
|
Save_PC10(Proc);
|
|
|
|
Save_PTSC(Proc);
|
|
|
|
Save_UNCORE_FC0(Proc);
|
|
|
|
Sys_Tick(Proc);
|
|
}
|
|
|
|
Core_Intel_Temp(Core);
|
|
|
|
RDCOUNTER(Core->Interrupt.SMI, MSR_SMI_COUNT);
|
|
|
|
Delta_INST(Core);
|
|
|
|
Delta_C0(Core);
|
|
|
|
Delta_C3(Core);
|
|
|
|
Delta_C6(Core);
|
|
|
|
Delta_C7(Core);
|
|
|
|
Delta_TSC(Core);
|
|
|
|
Delta_C1(Core);
|
|
|
|
Save_INST(Core);
|
|
|
|
Save_TSC(Core);
|
|
|
|
Save_C0(Core);
|
|
|
|
Save_C3(Core);
|
|
|
|
Save_C6(Core);
|
|
|
|
Save_C7(Core);
|
|
|
|
Save_C1(Core);
|
|
|
|
BITSET(LOCKLESS, Core->Sync.V, 63);
|
|
|
|
return(HRTIMER_RESTART);
|
|
} else
|
|
return(HRTIMER_NORESTART);
|
|
}
|
|
|
|
void InitTimer_Haswell_ULT(unsigned int cpu)
|
|
{
|
|
smp_call_function_single(cpu, InitTimer, Cycle_Haswell_ULT, 1);
|
|
}
|
|
|
|
void Start_Haswell_ULT(void *arg)
|
|
{
|
|
unsigned int cpu = smp_processor_id();
|
|
CORE *Core = (CORE *) KPublic->Core[cpu];
|
|
|
|
PerCore_Haswell_ULT_Query(Core, cpu);
|
|
|
|
Core_Counters_Set(Core);
|
|
SMT_Counters_SandyBridge(Core, 0);
|
|
|
|
if (Core->T.Base.BSP) {
|
|
PKG_Counters_Haswell_ULT(Core, 0);
|
|
}
|
|
|
|
RDCOUNTER(Core->Interrupt.SMI, MSR_SMI_COUNT);
|
|
|
|
BITSET(LOCKLESS, KPrivate->Join[cpu]->TSM, MUSTFWD);
|
|
|
|
hrtimer_start( &KPrivate->Join[cpu]->Timer,
|
|
RearmTheTimer,
|
|
HRTIMER_MODE_REL_PINNED);
|
|
|
|
BITSET(LOCKLESS, KPrivate->Join[cpu]->TSM, STARTED);
|
|
}
|
|
|
|
|
|
long Sys_IdleDriver_Query(SYSGATE *SysGate)
|
|
{
|
|
if (SysGate != NULL) {
|
|
struct cpuidle_driver *idleDriver;
|
|
struct cpufreq_policy freqPolicy;
|
|
|
|
if ((idleDriver = cpuidle_get_driver()) != NULL) {
|
|
int i;
|
|
|
|
strncpy(SysGate->IdleDriver.Name,
|
|
idleDriver->name,
|
|
CPUIDLE_NAME_LEN - 1);
|
|
|
|
if (idleDriver->state_count < CPUIDLE_STATE_MAX)
|
|
SysGate->IdleDriver.stateCount=idleDriver->state_count;
|
|
else // No overflow check.
|
|
SysGate->IdleDriver.stateCount=CPUIDLE_STATE_MAX;
|
|
|
|
for (i = 0; i < SysGate->IdleDriver.stateCount; i++) {
|
|
strncpy(SysGate->IdleDriver.State[i].Name,
|
|
idleDriver->states[i].name,
|
|
CPUIDLE_NAME_LEN - 1);
|
|
|
|
SysGate->IdleDriver.State[i].exitLatency =
|
|
idleDriver->states[i].exit_latency;
|
|
SysGate->IdleDriver.State[i].powerUsage =
|
|
idleDriver->states[i].power_usage;
|
|
SysGate->IdleDriver.State[i].targetResidency =
|
|
idleDriver->states[i].target_residency;
|
|
}
|
|
}
|
|
else
|
|
memset(&SysGate->IdleDriver, 0, sizeof(IDLEDRIVER));
|
|
|
|
memset(&freqPolicy, 0, sizeof(freqPolicy));
|
|
if (cpufreq_get_policy(&freqPolicy, smp_processor_id()) == 0) {
|
|
struct cpufreq_governor *pGovernor = freqPolicy.governor;
|
|
if (pGovernor != NULL)
|
|
strncpy(SysGate->IdleDriver.Governor,
|
|
pGovernor->name,
|
|
CPUIDLE_NAME_LEN - 1);
|
|
}
|
|
return(0);
|
|
}
|
|
else
|
|
return(-1);
|
|
}
|
|
|
|
long Sys_Kernel(SYSGATE *SysGate)
|
|
{ /* Sources: /include/generated/uapi/linux/version.h
|
|
/include/uapi/linux/utsname.h */
|
|
if (SysGate != NULL) {
|
|
SysGate->kernelVersionNumber = LINUX_VERSION_CODE;
|
|
memcpy(SysGate->sysname, utsname()->sysname, MAX_UTS_LEN);
|
|
memcpy(SysGate->release, utsname()->release, MAX_UTS_LEN);
|
|
memcpy(SysGate->version, utsname()->version, MAX_UTS_LEN);
|
|
memcpy(SysGate->machine, utsname()->machine, MAX_UTS_LEN);
|
|
|
|
return(0);
|
|
}
|
|
else
|
|
return(-1);
|
|
}
|
|
|
|
long SysGate_OnDemand(void)
|
|
{
|
|
long rc = -1;
|
|
if (Proc->SysGate == NULL) {
|
|
unsigned long pageSize = ROUND_TO_PAGES(sizeof(SYSGATE));
|
|
// Alloc on demand
|
|
if ((Proc->SysGate = kmalloc(pageSize, GFP_KERNEL)) != NULL) {
|
|
memset(Proc->SysGate, 0, pageSize);
|
|
rc = 0;
|
|
}
|
|
}
|
|
else // Already allocated
|
|
rc = 1;
|
|
|
|
return(rc);
|
|
}
|
|
|
|
static long CoreFreqK_ioctl( struct file *filp,
|
|
unsigned int cmd,
|
|
unsigned long arg)
|
|
{
|
|
long rc = -EPERM;
|
|
|
|
switch (cmd) {
|
|
case COREFREQ_IOCTL_SYSUPDT:
|
|
if (Proc->SysGate != NULL) {
|
|
Sys_DumpTask(Proc->SysGate);
|
|
Sys_MemInfo(Proc->SysGate);
|
|
rc = 0;
|
|
}
|
|
break;
|
|
case COREFREQ_IOCTL_SYSONCE:
|
|
rc = Sys_IdleDriver_Query(Proc->SysGate)
|
|
& Sys_Kernel(Proc->SysGate);
|
|
break;
|
|
case COREFREQ_IOCTL_MACHINE:
|
|
switch (arg) {
|
|
case COREFREQ_TOGGLE_OFF:
|
|
Controller_Stop(1);
|
|
rc = 0;
|
|
break;
|
|
case COREFREQ_TOGGLE_ON:
|
|
Controller_Start(1);
|
|
rc = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case COREFREQ_IOCTL_EIST:
|
|
switch (arg) {
|
|
case COREFREQ_TOGGLE_OFF:
|
|
case COREFREQ_TOGGLE_ON:
|
|
SpeedStep_Enable = arg;
|
|
Controller_Stop(1);
|
|
Controller_Start(1);
|
|
SpeedStep_Enable = -1;
|
|
rc = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case COREFREQ_IOCTL_C1E:
|
|
switch (arg) {
|
|
case COREFREQ_TOGGLE_OFF:
|
|
case COREFREQ_TOGGLE_ON:
|
|
C1E_Enable = arg;
|
|
Controller_Stop(1);
|
|
Controller_Start(1);
|
|
C1E_Enable = -1;
|
|
rc = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case COREFREQ_IOCTL_TURBO:
|
|
switch (arg) {
|
|
case COREFREQ_TOGGLE_OFF:
|
|
case COREFREQ_TOGGLE_ON:
|
|
TurboBoost_Enable = arg;
|
|
Controller_Stop(1);
|
|
Controller_Start(1);
|
|
TurboBoost_Enable = -1;
|
|
rc = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case COREFREQ_IOCTL_C1A:
|
|
switch (arg) {
|
|
case COREFREQ_TOGGLE_OFF:
|
|
case COREFREQ_TOGGLE_ON:
|
|
C1A_Enable = arg;
|
|
Controller_Stop(1);
|
|
Controller_Start(1);
|
|
C1A_Enable = -1;
|
|
rc = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case COREFREQ_IOCTL_C3A:
|
|
switch (arg) {
|
|
case COREFREQ_TOGGLE_OFF:
|
|
case COREFREQ_TOGGLE_ON:
|
|
C3A_Enable = arg;
|
|
Controller_Stop(1);
|
|
Controller_Start(1);
|
|
C3A_Enable = -1;
|
|
rc = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case COREFREQ_IOCTL_C1U:
|
|
switch (arg) {
|
|
case COREFREQ_TOGGLE_OFF:
|
|
case COREFREQ_TOGGLE_ON:
|
|
C1U_Enable = arg;
|
|
Controller_Stop(1);
|
|
Controller_Start(1);
|
|
C1U_Enable = -1;
|
|
rc = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case COREFREQ_IOCTL_C3U:
|
|
switch (arg) {
|
|
case COREFREQ_TOGGLE_OFF:
|
|
case COREFREQ_TOGGLE_ON:
|
|
C3U_Enable = arg;
|
|
Controller_Stop(1);
|
|
Controller_Start(1);
|
|
C3U_Enable = -1;
|
|
rc = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case COREFREQ_IOCTL_PKGCST:
|
|
PkgCStateLimit = arg;
|
|
Controller_Stop(1);
|
|
Controller_Start(1);
|
|
PkgCStateLimit = -1;
|
|
rc = 0;
|
|
break;
|
|
case COREFREQ_IOCTL_IOMWAIT:
|
|
switch (arg) {
|
|
case COREFREQ_TOGGLE_OFF:
|
|
case COREFREQ_TOGGLE_ON:
|
|
IOMWAIT_Enable = arg;
|
|
Controller_Stop(1);
|
|
Controller_Start(1);
|
|
IOMWAIT_Enable = -1;
|
|
rc = 0;
|
|
break;
|
|
}
|
|
break;
|
|
case COREFREQ_IOCTL_IORCST:
|
|
CStateIORedir = arg;
|
|
Controller_Stop(1);
|
|
Controller_Start(1);
|
|
CStateIORedir = -1;
|
|
rc = 0;
|
|
break;
|
|
case COREFREQ_IOCTL_ODCM:
|
|
ODCM_Enable = arg;
|
|
Controller_Stop(1);
|
|
Controller_Start(1);
|
|
ODCM_Enable = -1;
|
|
rc = 0;
|
|
break;
|
|
case COREFREQ_IOCTL_ODCM_DC:
|
|
ODCM_DutyCycle = arg;
|
|
Controller_Stop(1);
|
|
Controller_Start(1);
|
|
ODCM_DutyCycle = -1;
|
|
rc = 0;
|
|
break;
|
|
default:
|
|
rc = -EINVAL;
|
|
}
|
|
return(rc);
|
|
}
|
|
|
|
static int CoreFreqK_mmap(struct file *pfile, struct vm_area_struct *vma)
|
|
{
|
|
if (vma->vm_pgoff == 0) {
|
|
if (Proc != NULL) {
|
|
if (remap_pfn_range(vma,
|
|
vma->vm_start,
|
|
virt_to_phys((void *) Proc) >> PAGE_SHIFT,
|
|
vma->vm_end - vma->vm_start,
|
|
vma->vm_page_prot) < 0)
|
|
return(-EIO);
|
|
}
|
|
else
|
|
return(-EIO);
|
|
} else if (vma->vm_pgoff == 1) {
|
|
if (Proc != NULL) {
|
|
switch (SysGate_OnDemand()) {
|
|
case -1:
|
|
return(-EIO);
|
|
case 1:
|
|
// Fallthrough
|
|
case 0:
|
|
if (remap_pfn_range(vma,
|
|
vma->vm_start,
|
|
virt_to_phys((void *)Proc->SysGate)>>PAGE_SHIFT,
|
|
vma->vm_end - vma->vm_start,
|
|
vma->vm_page_prot) < 0)
|
|
return(-EIO);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
return(-EIO);
|
|
} else if (vma->vm_pgoff >= 10) {
|
|
unsigned int cpu = vma->vm_pgoff - 10;
|
|
|
|
if (Proc != NULL) {
|
|
if ((cpu >= 0) && (cpu < Proc->CPU.Count)) {
|
|
if (KPublic->Core[cpu] != NULL) {
|
|
if (remap_pfn_range(vma,
|
|
vma->vm_start,
|
|
virt_to_phys((void *) KPublic->Core[cpu]) >> PAGE_SHIFT,
|
|
vma->vm_end - vma->vm_start,
|
|
vma->vm_page_prot) < 0)
|
|
return(-EIO);
|
|
}
|
|
else
|
|
return(-EIO);
|
|
}
|
|
else
|
|
return(-EIO);
|
|
}
|
|
else
|
|
return(-EIO);
|
|
}
|
|
else
|
|
return(-EIO);
|
|
return(0);
|
|
}
|
|
|
|
static DEFINE_MUTEX(CoreFreqK_mutex); // Only one driver instance.
|
|
|
|
static int CoreFreqK_open(struct inode *inode, struct file *pfile)
|
|
{
|
|
if (!mutex_trylock(&CoreFreqK_mutex))
|
|
return(-EBUSY);
|
|
else
|
|
return(0);
|
|
}
|
|
|
|
static int CoreFreqK_release(struct inode *inode, struct file *pfile)
|
|
{
|
|
mutex_unlock(&CoreFreqK_mutex);
|
|
return(0);
|
|
}
|
|
|
|
static struct file_operations CoreFreqK_fops = {
|
|
.open = CoreFreqK_open,
|
|
.release = CoreFreqK_release,
|
|
.mmap = CoreFreqK_mmap,
|
|
.unlocked_ioctl = CoreFreqK_ioctl,
|
|
.owner = THIS_MODULE,
|
|
};
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
static int CoreFreqK_suspend(struct device *dev)
|
|
{
|
|
Controller_Stop(1);
|
|
|
|
printk(KERN_NOTICE "CoreFreq: Suspend\n");
|
|
|
|
return(0);
|
|
}
|
|
|
|
static int CoreFreqK_resume(struct device *dev)
|
|
{
|
|
Controller_Start(0);
|
|
|
|
printk(KERN_NOTICE "CoreFreq: Resume\n");
|
|
|
|
return(0);
|
|
}
|
|
|
|
static SIMPLE_DEV_PM_OPS(CoreFreqK_pm_ops, CoreFreqK_suspend, CoreFreqK_resume);
|
|
#define COREFREQ_PM_OPS (&CoreFreqK_pm_ops)
|
|
#else
|
|
#define COREFREQ_PM_OPS NULL
|
|
#endif
|
|
|
|
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
|
static int CoreFreqK_hotplug_cpu_online(unsigned int cpu)
|
|
{
|
|
if ((cpu >= 0) && (cpu < Proc->CPU.Count)) {
|
|
if ((KPublic->Core[cpu]->T.ApicID == -1)
|
|
&& !BITVAL(KPublic->Core[cpu]->OffLine, HW)) {
|
|
if (!Core_Topology(cpu)) {
|
|
if (KPublic->Core[cpu]->T.ApicID >= 0)
|
|
BITCLR(LOCKLESS,KPublic->Core[cpu]->OffLine,HW);
|
|
else
|
|
BITSET(LOCKLESS,KPublic->Core[cpu]->OffLine,HW);
|
|
}
|
|
memcpy(&KPublic->Core[cpu]->Clock,
|
|
&KPublic->Core[0]->Clock,
|
|
sizeof(CLOCK) );
|
|
}
|
|
if (Arch[Proc->ArchID].Timer != NULL) {
|
|
Arch[Proc->ArchID].Timer(cpu);
|
|
}
|
|
if ((BITVAL(KPrivate->Join[cpu]->TSM, STARTED) == 0)
|
|
&& (Arch[Proc->ArchID].Start != NULL)) {
|
|
smp_call_function_single(cpu,
|
|
Arch[Proc->ArchID].Start,
|
|
NULL, 0);
|
|
}
|
|
Proc->CPU.OnLine++;
|
|
BITCLR(LOCKLESS, KPublic->Core[cpu]->OffLine, OS);
|
|
|
|
return(0);
|
|
} else
|
|
return(-EINVAL);
|
|
}
|
|
|
|
static int CoreFreqK_hotplug_cpu_offline(unsigned int cpu)
|
|
{
|
|
if ((cpu >= 0) && (cpu < Proc->CPU.Count)) {
|
|
if ((BITVAL(KPrivate->Join[cpu]->TSM, CREATED) == 1)
|
|
&& (BITVAL(KPrivate->Join[cpu]->TSM, STARTED) == 1)
|
|
&& (Arch[Proc->ArchID].Stop != NULL)) {
|
|
smp_call_function_single(cpu,
|
|
Arch[Proc->ArchID].Stop,
|
|
NULL, 1);
|
|
}
|
|
Proc->CPU.OnLine--;
|
|
BITSET(LOCKLESS, KPublic->Core[cpu]->OffLine, OS);
|
|
|
|
return(0);
|
|
} else
|
|
return(-EINVAL);
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
|
|
static int CoreFreqK_hotplug( struct notifier_block *nfb,
|
|
unsigned long action,
|
|
void *hcpu)
|
|
{
|
|
unsigned int cpu = (unsigned long) hcpu, rc = 0;
|
|
|
|
switch (action) {
|
|
case CPU_ONLINE:
|
|
case CPU_DOWN_FAILED:
|
|
//- case CPU_ONLINE_FROZEN:
|
|
rc = CoreFreqK_hotplug_cpu_online(cpu);
|
|
break;
|
|
case CPU_DOWN_PREPARE:
|
|
//- case CPU_DOWN_PREPARE_FROZEN:
|
|
rc = CoreFreqK_hotplug_cpu_offline(cpu);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return(NOTIFY_OK);
|
|
}
|
|
|
|
static struct notifier_block CoreFreqK_notifier_block=
|
|
{
|
|
.notifier_call = CoreFreqK_hotplug,
|
|
};
|
|
#endif
|
|
#endif
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
|
|
static int CoreFreqK_NMI_handler(unsigned int type, struct pt_regs *pRegs)
|
|
{
|
|
unsigned int cpu = smp_processor_id();
|
|
|
|
switch (type) {
|
|
case NMI_LOCAL:
|
|
KPublic->Core[cpu]->Interrupt.NMI.LOCAL++;
|
|
break;
|
|
case NMI_UNKNOWN:
|
|
KPublic->Core[cpu]->Interrupt.NMI.UNKNOWN++;
|
|
break;
|
|
case NMI_SERR:
|
|
KPublic->Core[cpu]->Interrupt.NMI.PCISERR++;
|
|
break;
|
|
case NMI_IO_CHECK:
|
|
KPublic->Core[cpu]->Interrupt.NMI.IOCHECK++;
|
|
break;
|
|
}
|
|
return(NMI_DONE);
|
|
}
|
|
#endif
|
|
|
|
static int __init CoreFreqK_init(void)
|
|
{
|
|
int rc = 0;
|
|
ARG Arg = {.SMT_Count = 0, .rc = 0};
|
|
|
|
// Query features on the presumed BSP processor.
|
|
memset(&Arg.Features, 0, sizeof(FEATURES));
|
|
if ((rc = smp_call_function_single(0, Query_Features, &Arg, 1)) == 0)
|
|
rc = Arg.rc;
|
|
if (rc == 0) {
|
|
unsigned int OS_Count = num_present_cpus();
|
|
// Rely on operating system's cpu counting.
|
|
if (Arg.SMT_Count != OS_Count)
|
|
Arg.SMT_Count = OS_Count;
|
|
} else
|
|
rc = -ENXIO;
|
|
if (rc == 0)
|
|
{
|
|
CoreFreqK.kcdev = cdev_alloc();
|
|
CoreFreqK.kcdev->ops = &CoreFreqK_fops;
|
|
CoreFreqK.kcdev->owner = THIS_MODULE;
|
|
|
|
if (alloc_chrdev_region(&CoreFreqK.nmdev, 0, 1, DRV_FILENAME) >= 0)
|
|
{
|
|
CoreFreqK.Major = MAJOR(CoreFreqK.nmdev);
|
|
CoreFreqK.mkdev = MKDEV(CoreFreqK.Major, 0);
|
|
|
|
if (cdev_add(CoreFreqK.kcdev, CoreFreqK.mkdev, 1) >= 0)
|
|
{
|
|
struct device *tmpDev;
|
|
|
|
CoreFreqK.clsdev = class_create(THIS_MODULE, DRV_DEVNAME);
|
|
CoreFreqK.clsdev->pm = COREFREQ_PM_OPS;
|
|
|
|
if ((tmpDev=device_create(CoreFreqK.clsdev, NULL,
|
|
CoreFreqK.mkdev, NULL,
|
|
DRV_DEVNAME)) != NULL)
|
|
{
|
|
unsigned int cpu = 0;
|
|
unsigned long publicSize = 0,privateSize = 0,packageSize = 0;
|
|
|
|
publicSize=sizeof(KPUBLIC) + sizeof(CORE *) * Arg.SMT_Count;
|
|
|
|
privateSize=sizeof(KPRIVATE) + sizeof(JOIN *) * Arg.SMT_Count;
|
|
|
|
if (((KPublic = kmalloc(publicSize, GFP_KERNEL)) != NULL)
|
|
&& ((KPrivate = kmalloc(privateSize, GFP_KERNEL)) != NULL))
|
|
{
|
|
memset(KPublic, 0, publicSize);
|
|
memset(KPrivate, 0, privateSize);
|
|
|
|
packageSize = ROUND_TO_PAGES(sizeof(PROC));
|
|
if ((Proc = kmalloc(packageSize, GFP_KERNEL)) != NULL)
|
|
{
|
|
memset(Proc, 0, packageSize);
|
|
Proc->CPU.Count = Arg.SMT_Count;
|
|
|
|
if ( (SleepInterval >= LOOP_MIN_MS)
|
|
&& (SleepInterval <= LOOP_MAX_MS))
|
|
Proc->SleepInterval = SleepInterval;
|
|
else
|
|
Proc->SleepInterval = LOOP_DEF_MS;
|
|
|
|
// Compute the tick steps.
|
|
Proc->tickReset =
|
|
( (TickInterval >= Proc->SleepInterval)
|
|
&& (TickInterval <= LOOP_MAX_MS) ) ?
|
|
TickInterval:KMAX(TICK_DEF_MS,
|
|
Proc->SleepInterval);
|
|
Proc->tickReset /= Proc->SleepInterval;
|
|
Proc->tickStep = Proc->tickReset;
|
|
|
|
Proc->Registration.Experimental = Experimental;
|
|
|
|
memcpy(&Proc->Features, &Arg.Features,
|
|
sizeof(FEATURES));
|
|
|
|
Arch[0].Architecture=Proc->Features.Info.Vendor.ID;
|
|
|
|
RearmTheTimer =
|
|
ktime_set(0, Proc->SleepInterval * 1000000LU);
|
|
|
|
publicSize = ROUND_TO_PAGES(sizeof(CORE));
|
|
privateSize = ROUND_TO_PAGES(sizeof(JOIN));
|
|
|
|
if (((KPublic->Cache=kmem_cache_create(
|
|
"corefreqk-pub",
|
|
publicSize, 0,
|
|
SLAB_HWCACHE_ALIGN, NULL)) != NULL)
|
|
&& ((KPrivate->Cache=kmem_cache_create(
|
|
"corefreqk-priv",
|
|
privateSize, 0,
|
|
SLAB_HWCACHE_ALIGN, NULL)) != NULL))
|
|
{
|
|
int allocPerCPU = 1;
|
|
// Allocation per CPU
|
|
for (cpu = 0; cpu < Proc->CPU.Count; cpu++) {
|
|
void *kcache = NULL;
|
|
kcache=kmem_cache_alloc(KPublic->Cache,
|
|
GFP_KERNEL);
|
|
if (kcache != NULL) {
|
|
memset(kcache, 0, publicSize);
|
|
KPublic->Core[cpu] = kcache;
|
|
} else {
|
|
allocPerCPU = 0;
|
|
break;
|
|
}
|
|
kcache=kmem_cache_alloc(KPrivate->Cache,
|
|
GFP_KERNEL);
|
|
if (kcache != NULL) {
|
|
memset(kcache, 0, privateSize);
|
|
KPrivate->Join[cpu] = kcache;
|
|
} else {
|
|
allocPerCPU = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (allocPerCPU)
|
|
{
|
|
for (cpu = 0; cpu < Proc->CPU.Count; cpu++) {
|
|
BITCLR( LOCKLESS,
|
|
KPublic->Core[cpu]->Sync.V, 63);
|
|
|
|
KPublic->Core[cpu]->Bind = cpu;
|
|
|
|
Define_CPUID(KPublic->Core[cpu],
|
|
CpuIDforVendor);
|
|
}
|
|
|
|
switch (Proc->Features.Info.Vendor.CRC) {
|
|
case CRC_INTEL: {
|
|
Arch[0].Query = Query_GenuineIntel;
|
|
Arch[0].Start = Start_GenuineIntel;
|
|
Arch[0].Stop = Stop_GenuineIntel;
|
|
Arch[0].Timer = InitTimer_GenuineIntel;
|
|
Arch[0].Clock = Clock_GenuineIntel;
|
|
|
|
Arch[0].thermalFormula =
|
|
THERMAL_FORMULA_INTEL;
|
|
|
|
Arch[0].voltageFormula =
|
|
VOLTAGE_FORMULA_INTEL;
|
|
}
|
|
break;
|
|
case CRC_AMD: {
|
|
Arch[0].Query = Query_AuthenticAMD;
|
|
Arch[0].Start = Start_AuthenticAMD;
|
|
Arch[0].Stop = Stop_AuthenticAMD;
|
|
Arch[0].Timer = InitTimer_AuthenticAMD;
|
|
Arch[0].Clock = Clock_AuthenticAMD;
|
|
|
|
Arch[0].thermalFormula =
|
|
THERMAL_FORMULA_AMD;
|
|
|
|
Arch[0].voltageFormula =
|
|
VOLTAGE_FORMULA_AMD;
|
|
}
|
|
break;
|
|
}
|
|
if ( (ArchID != -1)
|
|
&& (ArchID >= 0)
|
|
&& (ArchID < ARCHITECTURES) ) {
|
|
Proc->ArchID = ArchID;
|
|
} else {
|
|
for ( Proc->ArchID = ARCHITECTURES - 1;
|
|
Proc->ArchID > 0;
|
|
Proc->ArchID--) {
|
|
// Search for an architecture signature.
|
|
if ((Arch[Proc->ArchID].Signature.ExtFamily
|
|
== Proc->Features.Std.EAX.ExtFamily)
|
|
&& (Arch[Proc->ArchID].Signature.Family
|
|
== Proc->Features.Std.EAX.Family)
|
|
&& (Arch[Proc->ArchID].Signature.ExtModel
|
|
== Proc->Features.Std.EAX.ExtModel)
|
|
&& (Arch[Proc->ArchID].Signature.Model
|
|
== Proc->Features.Std.EAX.Model)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Proc->thermalFormula =
|
|
Arch[Proc->ArchID].thermalFormula;
|
|
|
|
Proc->voltageFormula =
|
|
Arch[Proc->ArchID].voltageFormula;
|
|
|
|
strncpy(Proc->Architecture,
|
|
Arch[Proc->ArchID].Architecture, 32);
|
|
|
|
Controller_Init();
|
|
|
|
printk(KERN_INFO "CoreFreq:" \
|
|
" Processor [%1X%1X_%1X%1X]" \
|
|
" Architecture [%s] CPU [%u/%u]\n",
|
|
Proc->Features.Std.EAX.ExtFamily,
|
|
Proc->Features.Std.EAX.Family,
|
|
Proc->Features.Std.EAX.ExtModel,
|
|
Proc->Features.Std.EAX.Model,
|
|
Arch[Proc->ArchID].Architecture,
|
|
Proc->CPU.OnLine,
|
|
Proc->CPU.Count);
|
|
|
|
Controller_Start(0);
|
|
|
|
if (Proc->Registration.Experimental) {
|
|
Proc->Registration.pci =
|
|
pci_register_driver(&CoreFreqK_pci_driver);
|
|
}
|
|
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
|
|
// Always returns zero (kernel/notifier.c)
|
|
Proc->Registration.hotplug =
|
|
register_hotcpu_notifier(&CoreFreqK_notifier_block);
|
|
#else // Continue with or without cpu hot-plugging.
|
|
Proc->Registration.hotplug =
|
|
cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
|
|
"corefreqk/cpu:online",
|
|
CoreFreqK_hotplug_cpu_online,
|
|
CoreFreqK_hotplug_cpu_offline);
|
|
#endif
|
|
#endif
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
|
|
if (!NMI_Disable) {
|
|
Proc->Registration.nmi =
|
|
!( register_nmi_handler(NMI_LOCAL,
|
|
CoreFreqK_NMI_handler,
|
|
0,
|
|
"corefreqk")
|
|
| register_nmi_handler(NMI_UNKNOWN,
|
|
CoreFreqK_NMI_handler,
|
|
0,
|
|
"corefreqk")
|
|
| register_nmi_handler(NMI_SERR,
|
|
CoreFreqK_NMI_handler,
|
|
0,
|
|
"corefreqk")
|
|
| register_nmi_handler(NMI_IO_CHECK,
|
|
CoreFreqK_NMI_handler,
|
|
0,
|
|
"corefreqk"));
|
|
}
|
|
#endif
|
|
} else {
|
|
if (KPublic->Cache != NULL) {
|
|
for(cpu = 0;cpu < Proc->CPU.Count;cpu++)
|
|
{
|
|
if (KPublic->Core[cpu] != NULL)
|
|
kmem_cache_free(KPublic->Cache,
|
|
KPublic->Core[cpu]);
|
|
}
|
|
kmem_cache_destroy(KPublic->Cache);
|
|
}
|
|
if (KPrivate->Cache != NULL) {
|
|
for(cpu = 0;cpu < Proc->CPU.Count;cpu++)
|
|
{
|
|
if (KPrivate->Join[cpu] != NULL)
|
|
kmem_cache_free(KPrivate->Cache,
|
|
KPrivate->Join[cpu]);
|
|
}
|
|
kmem_cache_destroy(KPrivate->Cache);
|
|
}
|
|
kfree(Proc);
|
|
kfree(KPublic);
|
|
kfree(KPrivate);
|
|
|
|
device_destroy(CoreFreqK.clsdev,
|
|
CoreFreqK.mkdev);
|
|
class_destroy(CoreFreqK.clsdev);
|
|
cdev_del(CoreFreqK.kcdev);
|
|
unregister_chrdev_region(CoreFreqK.mkdev, 1);
|
|
|
|
rc = -ENOMEM;
|
|
}
|
|
} else {
|
|
if (KPublic->Cache != NULL)
|
|
kmem_cache_destroy(KPublic->Cache);
|
|
if (KPrivate->Cache != NULL)
|
|
kmem_cache_destroy(KPrivate->Cache);
|
|
|
|
kfree(Proc);
|
|
kfree(KPublic);
|
|
kfree(KPrivate);
|
|
|
|
device_destroy(CoreFreqK.clsdev,
|
|
CoreFreqK.mkdev);
|
|
class_destroy(CoreFreqK.clsdev);
|
|
cdev_del(CoreFreqK.kcdev);
|
|
unregister_chrdev_region(CoreFreqK.mkdev, 1);
|
|
|
|
rc = -ENOMEM;
|
|
}
|
|
} else {
|
|
kfree(KPublic);
|
|
kfree(KPrivate);
|
|
|
|
device_destroy(CoreFreqK.clsdev,
|
|
CoreFreqK.mkdev);
|
|
class_destroy(CoreFreqK.clsdev);
|
|
cdev_del(CoreFreqK.kcdev);
|
|
unregister_chrdev_region(CoreFreqK.mkdev, 1);
|
|
|
|
rc = -ENOMEM;
|
|
}
|
|
} else {
|
|
if (KPublic != NULL)
|
|
kfree(KPublic);
|
|
if (KPrivate != NULL)
|
|
kfree(KPrivate);
|
|
|
|
device_destroy(CoreFreqK.clsdev, CoreFreqK.mkdev);
|
|
class_destroy(CoreFreqK.clsdev);
|
|
cdev_del(CoreFreqK.kcdev);
|
|
unregister_chrdev_region(CoreFreqK.mkdev, 1);
|
|
|
|
rc = -ENOMEM;
|
|
}
|
|
} else {
|
|
class_destroy(CoreFreqK.clsdev);
|
|
cdev_del(CoreFreqK.kcdev);
|
|
unregister_chrdev_region(CoreFreqK.mkdev, 1);
|
|
|
|
rc = -EBUSY;
|
|
}
|
|
} else {
|
|
cdev_del(CoreFreqK.kcdev);
|
|
unregister_chrdev_region(CoreFreqK.mkdev, 1);
|
|
|
|
rc = -EBUSY;
|
|
}
|
|
} else {
|
|
cdev_del(CoreFreqK.kcdev);
|
|
|
|
rc = -EBUSY;
|
|
}
|
|
}
|
|
return(rc);
|
|
}
|
|
|
|
static void __exit CoreFreqK_cleanup(void)
|
|
{
|
|
if (Proc != NULL) {
|
|
unsigned int cpu = 0;
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 2, 0)
|
|
if (Proc->Registration.nmi) {
|
|
unregister_nmi_handler(NMI_LOCAL, "corefreqk");
|
|
unregister_nmi_handler(NMI_UNKNOWN, "corefreqk");
|
|
unregister_nmi_handler(NMI_SERR, "corefreqk");
|
|
unregister_nmi_handler(NMI_IO_CHECK, "corefreqk");
|
|
}
|
|
#endif
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
|
|
unregister_hotcpu_notifier(&CoreFreqK_notifier_block);
|
|
#else
|
|
if (!(Proc->Registration.hotplug < 0))
|
|
cpuhp_remove_state_nocalls(Proc->Registration.hotplug);
|
|
#endif
|
|
#endif
|
|
if (Proc->Registration.Experimental) {
|
|
if (!Proc->Registration.pci)
|
|
pci_unregister_driver(&CoreFreqK_pci_driver);
|
|
}
|
|
Controller_Stop(1);
|
|
Controller_Exit();
|
|
|
|
if (Proc->SysGate != NULL)
|
|
kfree(Proc->SysGate);
|
|
|
|
for (cpu = 0;(KPublic->Cache != NULL) && (cpu < Proc->CPU.Count); cpu++)
|
|
{
|
|
if (KPublic->Core[cpu] != NULL)
|
|
kmem_cache_free(KPublic->Cache, KPublic->Core[cpu]);
|
|
if (KPrivate->Join[cpu] != NULL)
|
|
kmem_cache_free(KPrivate->Cache, KPrivate->Join[cpu]);
|
|
}
|
|
if (KPublic->Cache != NULL)
|
|
kmem_cache_destroy(KPublic->Cache);
|
|
if (KPrivate->Cache != NULL)
|
|
kmem_cache_destroy(KPrivate->Cache);
|
|
|
|
if (KPublic != NULL)
|
|
kfree(KPublic);
|
|
if (KPrivate != NULL)
|
|
kfree(KPrivate);
|
|
|
|
device_destroy(CoreFreqK.clsdev, CoreFreqK.mkdev);
|
|
class_destroy(CoreFreqK.clsdev);
|
|
cdev_del(CoreFreqK.kcdev);
|
|
unregister_chrdev_region(CoreFreqK.mkdev, 1);
|
|
|
|
printk(KERN_NOTICE "CoreFreq: Unload\n");
|
|
|
|
kfree(Proc);
|
|
}
|
|
}
|
|
|
|
module_init(CoreFreqK_init);
|
|
module_exit(CoreFreqK_cleanup);
|