#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: est.c,v 1.33 2021/10/07 12:52:27 msaitoh Exp $");
#include <sys/param.h>
#include <sys/device.h>
#include <sys/kmem.h>
#include <sys/module.h>
#include <sys/sysctl.h>
#include <sys/xcall.h>
#include <x86/cpuvar.h>
#include <x86/cputypes.h>
#include <x86/cpu_msr.h>
#include <x86/est.h>
#include <x86/specialreg.h>
#define MSR2FREQINC(msr) (((int) (msr) >> 8) & 0xff)
#define MSR2VOLTINC(msr) ((int) (msr) & 0xff)
#define MSR2MHZ(msr, bus) ((MSR2FREQINC((msr)) * (bus) + 50) / 100)
#define MSR2MV(msr) (MSR2VOLTINC(msr) * 16 + 700)
#define ID16(MHz, mV, bus_clk) \
((((MHz * 100 + 50) / bus_clk) << 8) | ((mV ? mV - 700 : 0) >> 4))
#define ENTRY(ven, bus_clk, tab) \
{ CPUVENDOR_##ven, bus_clk == BUS133 ? 1 : 0, __arraycount(tab), tab }
#define BUS_CLK(fqp) ((fqp)->bus_clk ? BUS133 : BUS100)
struct fqlist {
int vendor;
unsigned bus_clk;
unsigned n;
const uint16_t *table;
};
#ifdef __i386__
enum { BUS100 = 10000, BUS133 = 13333, BUS166 = 16666, BUS200 = 20000 };
static const uint16_t pm130_900_ulv[] = {
ID16( 900, 1004, BUS100),
ID16( 800, 988, BUS100),
ID16( 600, 844, BUS100),
};
static const uint16_t pm130_1000_ulv[] = {
ID16(1000, 1004, BUS100),
ID16( 900, 988, BUS100),
ID16( 800, 972, BUS100),
ID16( 600, 844, BUS100),
};
static const uint16_t pm130_1100_ulv[] = {
ID16(1100, 1004, BUS100),
ID16(1000, 988, BUS100),
ID16( 900, 972, BUS100),
ID16( 800, 956, BUS100),
ID16( 600, 844, BUS100),
};
static const uint16_t pm130_1100_lv[] = {
ID16(1100, 1180, BUS100),
ID16(1000, 1164, BUS100),
ID16( 900, 1100, BUS100),
ID16( 800, 1020, BUS100),
ID16( 600, 956, BUS100),
};
static const uint16_t pm130_1200_lv[] = {
ID16(1200, 1180, BUS100),
ID16(1100, 1164, BUS100),
ID16(1000, 1100, BUS100),
ID16( 900, 1020, BUS100),
ID16( 800, 1004, BUS100),
ID16( 600, 956, BUS100),
};
static const uint16_t pm130_1300_lv[] = {
ID16(1300, 1180, BUS100),
ID16(1200, 1164, BUS100),
ID16(1100, 1100, BUS100),
ID16(1000, 1020, BUS100),
ID16( 900, 1004, BUS100),
ID16( 800, 988, BUS100),
ID16( 600, 956, BUS100),
};
static const uint16_t pm130_1300[] = {
ID16(1300, 1388, BUS100),
ID16(1200, 1356, BUS100),
ID16(1000, 1292, BUS100),
ID16( 800, 1260, BUS100),
ID16( 600, 956, BUS100),
};
static const uint16_t pm130_1400[] = {
ID16(1400, 1484, BUS100),
ID16(1200, 1436, BUS100),
ID16(1000, 1308, BUS100),
ID16( 800, 1180, BUS100),
ID16( 600, 956, BUS100),
};
static const uint16_t pm130_1500[] = {
ID16(1500, 1484, BUS100),
ID16(1400, 1452, BUS100),
ID16(1200, 1356, BUS100),
ID16(1000, 1228, BUS100),
ID16( 800, 1116, BUS100),
ID16( 600, 956, BUS100),
};
static const uint16_t pm130_1600[] = {
ID16(1600, 1484, BUS100),
ID16(1400, 1420, BUS100),
ID16(1200, 1276, BUS100),
ID16(1000, 1164, BUS100),
ID16( 800, 1036, BUS100),
ID16( 600, 956, BUS100),
};
static const uint16_t pm130_1700[] = {
ID16(1700, 1484, BUS100),
ID16(1400, 1308, BUS100),
ID16(1200, 1228, BUS100),
ID16(1000, 1116, BUS100),
ID16( 800, 1004, BUS100),
ID16( 600, 956, BUS100),
};
static const uint16_t pm90_n723[] = {
ID16(1000, 940, BUS100),
ID16( 900, 908, BUS100),
ID16( 800, 876, BUS100),
ID16( 600, 812, BUS100),
};
static const uint16_t pm90_n733g[] = {
ID16(1100, 956, BUS100),
ID16(1000, 940, BUS100),
ID16( 900, 908, BUS100),
ID16( 800, 876, BUS100),
ID16( 600, 812, BUS100),
};
static const uint16_t pm90_n733h[] = {
ID16(1100, 940, BUS100),
ID16(1000, 924, BUS100),
ID16( 900, 892, BUS100),
ID16( 800, 876, BUS100),
ID16( 600, 812, BUS100),
};
static const uint16_t pm90_n733i[] = {
ID16(1100, 924, BUS100),
ID16(1000, 908, BUS100),
ID16( 900, 892, BUS100),
ID16( 800, 860, BUS100),
ID16( 600, 812, BUS100),
};
static const uint16_t pm90_n733j[] = {
ID16(1100, 908, BUS100),
ID16(1000, 892, BUS100),
ID16( 900, 876, BUS100),
ID16( 800, 860, BUS100),
ID16( 600, 812, BUS100),
};
static const uint16_t pm90_n733k[] = {
ID16(1100, 892, BUS100),
ID16(1000, 876, BUS100),
ID16( 900, 860, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 812, BUS100),
};
static const uint16_t pm90_n733l[] = {
ID16(1100, 876, BUS100),
ID16(1000, 876, BUS100),
ID16( 900, 860, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 812, BUS100),
};
static const uint16_t pm90_n753g[] = {
ID16(1200, 956, BUS100),
ID16(1100, 940, BUS100),
ID16(1000, 908, BUS100),
ID16( 900, 892, BUS100),
ID16( 800, 860, BUS100),
ID16( 600, 812, BUS100),
};
static const uint16_t pm90_n753h[] = {
ID16(1200, 940, BUS100),
ID16(1100, 924, BUS100),
ID16(1000, 908, BUS100),
ID16( 900, 876, BUS100),
ID16( 800, 860, BUS100),
ID16( 600, 812, BUS100),
};
static const uint16_t pm90_n753i[] = {
ID16(1200, 924, BUS100),
ID16(1100, 908, BUS100),
ID16(1000, 892, BUS100),
ID16( 900, 876, BUS100),
ID16( 800, 860, BUS100),
ID16( 600, 812, BUS100),
};
static const uint16_t pm90_n753j[] = {
ID16(1200, 908, BUS100),
ID16(1100, 892, BUS100),
ID16(1000, 876, BUS100),
ID16( 900, 860, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 812, BUS100),
};
static const uint16_t pm90_n753k[] = {
ID16(1200, 892, BUS100),
ID16(1100, 892, BUS100),
ID16(1000, 876, BUS100),
ID16( 900, 860, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 812, BUS100),
};
static const uint16_t pm90_n753l[] = {
ID16(1200, 876, BUS100),
ID16(1100, 876, BUS100),
ID16(1000, 860, BUS100),
ID16( 900, 844, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 812, BUS100),
};
static const uint16_t pm90_n773g[] = {
ID16(1300, 956, BUS100),
ID16(1200, 940, BUS100),
ID16(1100, 924, BUS100),
ID16(1000, 908, BUS100),
ID16( 900, 876, BUS100),
ID16( 800, 860, BUS100),
ID16( 600, 812, BUS100),
};
static const uint16_t pm90_n773h[] = {
ID16(1300, 940, BUS100),
ID16(1200, 924, BUS100),
ID16(1100, 908, BUS100),
ID16(1000, 892, BUS100),
ID16( 900, 876, BUS100),
ID16( 800, 860, BUS100),
ID16( 600, 812, BUS100),
};
static const uint16_t pm90_n773i[] = {
ID16(1300, 924, BUS100),
ID16(1200, 908, BUS100),
ID16(1100, 892, BUS100),
ID16(1000, 876, BUS100),
ID16( 900, 860, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 812, BUS100),
};
static const uint16_t pm90_n773j[] = {
ID16(1300, 908, BUS100),
ID16(1200, 908, BUS100),
ID16(1100, 892, BUS100),
ID16(1000, 876, BUS100),
ID16( 900, 860, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 812, BUS100),
};
static const uint16_t pm90_n773k[] = {
ID16(1300, 892, BUS100),
ID16(1200, 892, BUS100),
ID16(1100, 876, BUS100),
ID16(1000, 860, BUS100),
ID16( 900, 860, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 812, BUS100),
};
static const uint16_t pm90_n773l[] = {
ID16(1300, 876, BUS100),
ID16(1200, 876, BUS100),
ID16(1100, 860, BUS100),
ID16(1000, 860, BUS100),
ID16( 900, 844, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 812, BUS100),
};
static const uint16_t pm90_n738[] = {
ID16(1400, 1116, BUS100),
ID16(1300, 1116, BUS100),
ID16(1200, 1100, BUS100),
ID16(1100, 1068, BUS100),
ID16(1000, 1052, BUS100),
ID16( 900, 1036, BUS100),
ID16( 800, 1020, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n758[] = {
ID16(1500, 1116, BUS100),
ID16(1400, 1116, BUS100),
ID16(1300, 1100, BUS100),
ID16(1200, 1084, BUS100),
ID16(1100, 1068, BUS100),
ID16(1000, 1052, BUS100),
ID16( 900, 1036, BUS100),
ID16( 800, 1020, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n778[] = {
ID16(1600, 1116, BUS100),
ID16(1500, 1116, BUS100),
ID16(1400, 1100, BUS100),
ID16(1300, 1184, BUS100),
ID16(1200, 1068, BUS100),
ID16(1100, 1052, BUS100),
ID16(1000, 1052, BUS100),
ID16( 900, 1036, BUS100),
ID16( 800, 1020, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n710[] = {
ID16(1400, 1340, BUS133),
ID16(1200, 1228, BUS133),
ID16(1000, 1148, BUS133),
ID16( 800, 1068, BUS133),
ID16( 600, 998, BUS133),
};
static const uint16_t pm90_n715a[] = {
ID16(1500, 1340, BUS100),
ID16(1200, 1228, BUS100),
ID16(1000, 1148, BUS100),
ID16( 800, 1068, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n715b[] = {
ID16(1500, 1324, BUS100),
ID16(1200, 1212, BUS100),
ID16(1000, 1148, BUS100),
ID16( 800, 1068, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n715c[] = {
ID16(1500, 1308, BUS100),
ID16(1200, 1212, BUS100),
ID16(1000, 1132, BUS100),
ID16( 800, 1068, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n715d[] = {
ID16(1500, 1276, BUS100),
ID16(1200, 1180, BUS100),
ID16(1000, 1116, BUS100),
ID16( 800, 1052, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n725a[] = {
ID16(1600, 1340, BUS100),
ID16(1400, 1276, BUS100),
ID16(1200, 1212, BUS100),
ID16(1000, 1132, BUS100),
ID16( 800, 1068, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n725b[] = {
ID16(1600, 1324, BUS100),
ID16(1400, 1260, BUS100),
ID16(1200, 1196, BUS100),
ID16(1000, 1132, BUS100),
ID16( 800, 1068, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n725c[] = {
ID16(1600, 1308, BUS100),
ID16(1400, 1244, BUS100),
ID16(1200, 1180, BUS100),
ID16(1000, 1116, BUS100),
ID16( 800, 1052, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n725d[] = {
ID16(1600, 1276, BUS100),
ID16(1400, 1228, BUS100),
ID16(1200, 1164, BUS100),
ID16(1000, 1116, BUS100),
ID16( 800, 1052, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n730[] = {
ID16(1600, 1308, BUS133),
ID16(1333, 1260, BUS133),
ID16(1200, 1212, BUS133),
ID16(1067, 1180, BUS133),
ID16( 800, 988, BUS133),
};
static const uint16_t pm90_n735a[] = {
ID16(1700, 1340, BUS100),
ID16(1400, 1244, BUS100),
ID16(1200, 1180, BUS100),
ID16(1000, 1116, BUS100),
ID16( 800, 1052, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n735b[] = {
ID16(1700, 1324, BUS100),
ID16(1400, 1244, BUS100),
ID16(1200, 1180, BUS100),
ID16(1000, 1116, BUS100),
ID16( 800, 1052, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n735c[] = {
ID16(1700, 1308, BUS100),
ID16(1400, 1228, BUS100),
ID16(1200, 1164, BUS100),
ID16(1000, 1116, BUS100),
ID16( 800, 1052, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n735d[] = {
ID16(1700, 1276, BUS100),
ID16(1400, 1212, BUS100),
ID16(1200, 1148, BUS100),
ID16(1000, 1100, BUS100),
ID16( 800, 1052, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n740[] = {
ID16(1733, 1356, BUS133),
ID16(1333, 1212, BUS133),
ID16(1067, 1100, BUS133),
ID16( 800, 988, BUS133),
};
static const uint16_t pm90_n745a[] = {
ID16(1800, 1340, BUS100),
ID16(1600, 1292, BUS100),
ID16(1400, 1228, BUS100),
ID16(1200, 1164, BUS100),
ID16(1000, 1116, BUS100),
ID16( 800, 1052, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n745b[] = {
ID16(1800, 1324, BUS100),
ID16(1600, 1276, BUS100),
ID16(1400, 1212, BUS100),
ID16(1200, 1164, BUS100),
ID16(1000, 1116, BUS100),
ID16( 800, 1052, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n745c[] = {
ID16(1800, 1308, BUS100),
ID16(1600, 1260, BUS100),
ID16(1400, 1212, BUS100),
ID16(1200, 1148, BUS100),
ID16(1000, 1100, BUS100),
ID16( 800, 1052, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n745d[] = {
ID16(1800, 1276, BUS100),
ID16(1600, 1228, BUS100),
ID16(1400, 1180, BUS100),
ID16(1200, 1132, BUS100),
ID16(1000, 1084, BUS100),
ID16( 800, 1036, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n750[] = {
ID16(1867, 1308, BUS133),
ID16(1600, 1228, BUS133),
ID16(1333, 1148, BUS133),
ID16(1067, 1068, BUS133),
ID16( 800, 988, BUS133),
};
static const uint16_t pm90_n755a[] = {
ID16(2000, 1340, BUS100),
ID16(1800, 1292, BUS100),
ID16(1600, 1244, BUS100),
ID16(1400, 1196, BUS100),
ID16(1200, 1148, BUS100),
ID16(1000, 1100, BUS100),
ID16( 800, 1052, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n755b[] = {
ID16(2000, 1324, BUS100),
ID16(1800, 1276, BUS100),
ID16(1600, 1228, BUS100),
ID16(1400, 1180, BUS100),
ID16(1200, 1132, BUS100),
ID16(1000, 1084, BUS100),
ID16( 800, 1036, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n755c[] = {
ID16(2000, 1308, BUS100),
ID16(1800, 1276, BUS100),
ID16(1600, 1228, BUS100),
ID16(1400, 1180, BUS100),
ID16(1200, 1132, BUS100),
ID16(1000, 1084, BUS100),
ID16( 800, 1036, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n755d[] = {
ID16(2000, 1276, BUS100),
ID16(1800, 1244, BUS100),
ID16(1600, 1196, BUS100),
ID16(1400, 1164, BUS100),
ID16(1200, 1116, BUS100),
ID16(1000, 1084, BUS100),
ID16( 800, 1036, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n760[] = {
ID16(2000, 1356, BUS133),
ID16(1600, 1244, BUS133),
ID16(1333, 1164, BUS133),
ID16(1067, 1084, BUS133),
ID16( 800, 988, BUS133),
};
static const uint16_t pm90_n765a[] = {
ID16(2100, 1340, BUS100),
ID16(1800, 1276, BUS100),
ID16(1600, 1228, BUS100),
ID16(1400, 1180, BUS100),
ID16(1200, 1132, BUS100),
ID16(1000, 1084, BUS100),
ID16( 800, 1036, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n765b[] = {
ID16(2100, 1324, BUS100),
ID16(1800, 1260, BUS100),
ID16(1600, 1212, BUS100),
ID16(1400, 1180, BUS100),
ID16(1200, 1132, BUS100),
ID16(1000, 1084, BUS100),
ID16( 800, 1036, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n765c[] = {
ID16(2100, 1308, BUS100),
ID16(1800, 1244, BUS100),
ID16(1600, 1212, BUS100),
ID16(1400, 1164, BUS100),
ID16(1200, 1116, BUS100),
ID16(1000, 1084, BUS100),
ID16( 800, 1036, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n765e[] = {
ID16(2100, 1356, BUS100),
ID16(1800, 1292, BUS100),
ID16(1600, 1244, BUS100),
ID16(1400, 1196, BUS100),
ID16(1200, 1148, BUS100),
ID16(1000, 1100, BUS100),
ID16( 800, 1052, BUS100),
ID16( 600, 988, BUS100),
};
static const uint16_t pm90_n770[] = {
ID16(2133, 1356, BUS133),
ID16(1867, 1292, BUS133),
ID16(1600, 1212, BUS133),
ID16(1333, 1148, BUS133),
ID16(1067, 1068, BUS133),
ID16( 800, 988, BUS133),
};
static const uint16_t pm90_n780[] = {
ID16(2267, 1388, BUS133),
ID16(1867, 1292, BUS133),
ID16(1600, 1212, BUS133),
ID16(1333, 1148, BUS133),
ID16(1067, 1068, BUS133),
ID16( 800, 988, BUS133),
};
static const uint16_t C7M_770_ULV[] = {
ID16(1000, 844, BUS100),
ID16( 800, 796, BUS100),
ID16( 600, 796, BUS100),
ID16( 400, 796, BUS100),
};
static const uint16_t C7M_779_ULV[] = {
ID16(1000, 796, BUS100),
ID16( 800, 796, BUS100),
ID16( 600, 796, BUS100),
ID16( 400, 796, BUS100),
};
static const uint16_t C7M_772_ULV[] = {
ID16(1200, 844, BUS100),
ID16(1000, 844, BUS100),
ID16( 800, 828, BUS100),
ID16( 600, 796, BUS100),
ID16( 400, 796, BUS100),
};
static const uint16_t C7M_775_ULV[] = {
ID16(1500, 956, BUS100),
ID16(1400, 940, BUS100),
ID16(1000, 860, BUS100),
ID16( 800, 828, BUS100),
ID16( 600, 796, BUS100),
ID16( 400, 796, BUS100),
};
static const uint16_t C7M_771[] = {
ID16(1200, 860, BUS100),
ID16(1000, 860, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 844, BUS100),
ID16( 400, 844, BUS100),
};
static const uint16_t C7M_754[] = {
ID16(1500, 1004, BUS100),
ID16(1400, 988, BUS100),
ID16(1000, 940, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 844, BUS100),
ID16( 400, 844, BUS100),
};
static const uint16_t C7M_764[] = {
ID16(1600, 1084, BUS100),
ID16(1400, 1052, BUS100),
ID16(1000, 1004, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 844, BUS100),
ID16( 400, 844, BUS100),
};
static const uint16_t C7M_784[] = {
ID16(1800, 1148, BUS100),
ID16(1600, 1100, BUS100),
ID16(1400, 1052, BUS100),
ID16(1000, 1004, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 844, BUS100),
ID16( 400, 844, BUS100),
};
static const uint16_t C7M_794[] = {
ID16(2000, 1148, BUS100),
ID16(1800, 1132, BUS100),
ID16(1600, 1100, BUS100),
ID16(1400, 1052, BUS100),
ID16(1000, 1004, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 844, BUS100),
ID16( 400, 844, BUS100),
};
static const uint16_t C7M_765[] = {
ID16(1600, 1084, BUS133),
ID16(1467, 1052, BUS133),
ID16(1200, 1004, BUS133),
ID16( 800, 844, BUS133),
ID16( 667, 844, BUS133),
ID16( 533, 844, BUS133),
};
static const uint16_t C7M_785[] = {
ID16(1867, 1148, BUS133),
ID16(1600, 1100, BUS133),
ID16(1467, 1052, BUS133),
ID16(1200, 1004, BUS133),
ID16( 800, 844, BUS133),
ID16( 667, 844, BUS133),
ID16( 533, 844, BUS133),
};
static const uint16_t C7M_795[] = {
ID16(2000, 1148, BUS133),
ID16(1867, 1132, BUS133),
ID16(1600, 1100, BUS133),
ID16(1467, 1052, BUS133),
ID16(1200, 1004, BUS133),
ID16( 800, 844, BUS133),
ID16( 667, 844, BUS133),
ID16( 533, 844, BUS133),
};
static const uint16_t eden90_1000[] = {
ID16(1000, 844, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 844, BUS100),
ID16( 400, 844, BUS100),
};
static const struct fqlist est_cpus[] = {
ENTRY(INTEL, BUS100, pm130_900_ulv),
ENTRY(INTEL, BUS100, pm130_1000_ulv),
ENTRY(INTEL, BUS100, pm130_1100_ulv),
ENTRY(INTEL, BUS100, pm130_1100_lv),
ENTRY(INTEL, BUS100, pm130_1200_lv),
ENTRY(INTEL, BUS100, pm130_1300_lv),
ENTRY(INTEL, BUS100, pm130_1300),
ENTRY(INTEL, BUS100, pm130_1400),
ENTRY(INTEL, BUS100, pm130_1500),
ENTRY(INTEL, BUS100, pm130_1600),
ENTRY(INTEL, BUS100, pm130_1700),
ENTRY(INTEL, BUS100, pm90_n723),
ENTRY(INTEL, BUS100, pm90_n733g),
ENTRY(INTEL, BUS100, pm90_n733h),
ENTRY(INTEL, BUS100, pm90_n733i),
ENTRY(INTEL, BUS100, pm90_n733j),
ENTRY(INTEL, BUS100, pm90_n733k),
ENTRY(INTEL, BUS100, pm90_n733l),
ENTRY(INTEL, BUS100, pm90_n753g),
ENTRY(INTEL, BUS100, pm90_n753h),
ENTRY(INTEL, BUS100, pm90_n753i),
ENTRY(INTEL, BUS100, pm90_n753j),
ENTRY(INTEL, BUS100, pm90_n753k),
ENTRY(INTEL, BUS100, pm90_n753l),
ENTRY(INTEL, BUS100, pm90_n773g),
ENTRY(INTEL, BUS100, pm90_n773h),
ENTRY(INTEL, BUS100, pm90_n773i),
ENTRY(INTEL, BUS100, pm90_n773j),
ENTRY(INTEL, BUS100, pm90_n773k),
ENTRY(INTEL, BUS100, pm90_n773l),
ENTRY(INTEL, BUS100, pm90_n738),
ENTRY(INTEL, BUS100, pm90_n758),
ENTRY(INTEL, BUS100, pm90_n778),
ENTRY(INTEL, BUS133, pm90_n710),
ENTRY(INTEL, BUS100, pm90_n715a),
ENTRY(INTEL, BUS100, pm90_n715b),
ENTRY(INTEL, BUS100, pm90_n715c),
ENTRY(INTEL, BUS100, pm90_n715d),
ENTRY(INTEL, BUS100, pm90_n725a),
ENTRY(INTEL, BUS100, pm90_n725b),
ENTRY(INTEL, BUS100, pm90_n725c),
ENTRY(INTEL, BUS100, pm90_n725d),
ENTRY(INTEL, BUS133, pm90_n730),
ENTRY(INTEL, BUS100, pm90_n735a),
ENTRY(INTEL, BUS100, pm90_n735b),
ENTRY(INTEL, BUS100, pm90_n735c),
ENTRY(INTEL, BUS100, pm90_n735d),
ENTRY(INTEL, BUS133, pm90_n740),
ENTRY(INTEL, BUS100, pm90_n745a),
ENTRY(INTEL, BUS100, pm90_n745b),
ENTRY(INTEL, BUS100, pm90_n745c),
ENTRY(INTEL, BUS100, pm90_n745d),
ENTRY(INTEL, BUS133, pm90_n750),
ENTRY(INTEL, BUS100, pm90_n755a),
ENTRY(INTEL, BUS100, pm90_n755b),
ENTRY(INTEL, BUS100, pm90_n755c),
ENTRY(INTEL, BUS100, pm90_n755d),
ENTRY(INTEL, BUS133, pm90_n760),
ENTRY(INTEL, BUS100, pm90_n765a),
ENTRY(INTEL, BUS100, pm90_n765b),
ENTRY(INTEL, BUS100, pm90_n765c),
ENTRY(INTEL, BUS100, pm90_n765e),
ENTRY(INTEL, BUS133, pm90_n770),
ENTRY(INTEL, BUS133, pm90_n780),
ENTRY(IDT, BUS100, C7M_770_ULV),
ENTRY(IDT, BUS100, C7M_779_ULV),
ENTRY(IDT, BUS100, C7M_772_ULV),
ENTRY(IDT, BUS100, C7M_771),
ENTRY(IDT, BUS100, C7M_775_ULV),
ENTRY(IDT, BUS100, C7M_754),
ENTRY(IDT, BUS100, C7M_764),
ENTRY(IDT, BUS133, C7M_765),
ENTRY(IDT, BUS100, C7M_784),
ENTRY(IDT, BUS133, C7M_785),
ENTRY(IDT, BUS100, C7M_794),
ENTRY(IDT, BUS133, C7M_795),
ENTRY(IDT, BUS100, eden90_1000)
};
#endif
static int est_match(device_t, cfdata_t, void *);
static void est_attach(device_t, device_t, void *);
static int est_detach(device_t, int);
static int est_bus_clock(struct cpu_info *);
static bool est_tables(device_t);
static void est_xcall(uint16_t);
static bool est_sysctl(device_t);
static int est_sysctl_helper(SYSCTLFN_PROTO);
struct est_softc {
device_t sc_dev;
struct cpu_info *sc_ci;
struct sysctllog *sc_log;
struct fqlist *sc_fqlist;
struct fqlist sc_fake_fqlist;
uint16_t *sc_fake_table;
char *sc_freqs;
size_t sc_freqs_len;
int sc_bus_clock;
int sc_node_target;
int sc_node_current;
};
CFATTACH_DECL_NEW(est, sizeof(struct est_softc),
est_match, est_attach, est_detach, NULL);
static int
est_match(device_t parent, cfdata_t cf, void *aux)
{
struct cpufeature_attach_args *cfaa = aux;
struct cpu_info *ci = cfaa->ci;
if (strcmp(cfaa->name, "frequency") != 0)
return 0;
if (cpu_vendor != CPUVENDOR_IDT &&
cpu_vendor != CPUVENDOR_INTEL)
return 0;
if ((ci->ci_feat_val[1] & CPUID2_EST) == 0)
return 0;
return (est_bus_clock(ci) != 0) ? 5 : 0;
}
static void
est_attach(device_t parent, device_t self, void *aux)
{
struct est_softc *sc = device_private(self);
struct cpufeature_attach_args *cfaa = aux;
struct cpu_info *ci = cfaa->ci;
sc->sc_ci = ci;
sc->sc_dev = self;
sc->sc_log = NULL;
sc->sc_freqs = NULL;
sc->sc_fqlist = NULL;
sc->sc_fake_table = NULL;
sc->sc_bus_clock = est_bus_clock(ci);
KASSERT(sc->sc_bus_clock != 0);
aprint_naive("\n");
aprint_normal(": Enhanced SpeedStep\n");
(void)pmf_device_register(self, NULL, NULL);
if (est_tables(self) != false)
est_sysctl(self);
}
static int
est_detach(device_t self, int flags)
{
struct est_softc *sc = device_private(self);
uint16_t n = sc->sc_fake_fqlist.n;
if (sc->sc_log != NULL)
sysctl_teardown(&sc->sc_log);
if (sc->sc_freqs != NULL)
kmem_free(sc->sc_freqs, sc->sc_freqs_len);
if (sc->sc_fake_table != NULL)
kmem_free(sc->sc_fake_table, n * sizeof(*sc->sc_fake_table));
pmf_device_deregister(self);
return 0;
}
static int
est_bus_clock(struct cpu_info *ci)
{
uint32_t family, model;
int bus_clock = 0;
family = CPUID_TO_BASEFAMILY(ci->ci_signature);
model = CPUID_TO_MODEL(ci->ci_signature);
switch (family) {
case 0x0f:
bus_clock = p4_get_bus_clock(ci);
break;
case 0x06:
if (cpu_vendor != CPUVENDOR_IDT)
bus_clock = p3_get_bus_clock(ci);
else {
switch (model) {
case 0x0a:
case 0x0d:
bus_clock = viac7_get_bus_clock(ci);
break;
default:
bus_clock = via_get_bus_clock(ci);
break;
}
}
}
return bus_clock;
}
static bool
est_tables(device_t self)
{
struct est_softc *sc = device_private(self);
#ifdef __i386__
const struct fqlist *fql;
#endif
uint64_t msr;
uint16_t cur, idhi, idlo;
size_t len;
int i, mv;
msr = rdmsr(MSR_PERF_STATUS);
idhi = (msr >> 32) & 0xffff;
idlo = (msr >> 48) & 0xffff;
cur = msr & 0xffff;
#ifdef __i386__
if (idhi == 0 || idlo == 0 || cur == 0 ||
((cur >> 8) & 0xff) < ((idlo >> 8) & 0xff) ||
((cur >> 8) & 0xff) > ((idhi >> 8) & 0xff)) {
aprint_debug_dev(self, "strange msr value 0x%"PRIx64"\n", msr);
return false;
}
#endif
#ifdef __amd64__
uint8_t crhi = (idhi >> 8) & 0xff;
uint8_t crlo = (idlo >> 8) & 0xff;
uint8_t crcur = (cur >> 8) & 0xff;
if (crlo == 0 || crhi == 0 || crcur == 0 || crhi == crlo ||
crlo > crhi || crcur < crlo || crcur > crhi) {
aprint_debug_dev(self, "strange msr value 0x%"PRIu64"\n", msr);
aprint_debug_dev(self, " crhi=%u, crlo=%u, crcur=%u\n",
crhi, crlo, crcur);
return false;
}
#endif
msr = rdmsr(MSR_PERF_STATUS);
mv = MSR2MV(msr);
#ifdef __i386__
sc->sc_fqlist = NULL;
for (i = 0; i < __arraycount(est_cpus); i++) {
fql = &est_cpus[i];
if (cpu_vendor == fql->vendor &&
sc->sc_bus_clock == BUS_CLK(fql) &&
idhi == fql->table[0] && idlo == fql->table[fql->n - 1]) {
sc->sc_fqlist = __UNCONST(fql);
break;
}
}
#endif
if (sc->sc_fqlist == NULL) {
int j, tablesize, freq, volt;
int minfreq, minvolt, maxfreq, maxvolt, freqinc, voltinc;
if (idhi == idlo) {
aprint_debug_dev(self, "idhi == idlo\n");
return false;
}
#ifdef EST_DEBUG
aprint_normal_dev(self, "bus_clock = %d\n", sc->sc_bus_clock);
aprint_normal_dev(self, "idlo = 0x%x\n", idlo);
aprint_normal_dev(self, "lo %4d mV, %4d MHz\n",
MSR2MV(idlo), MSR2MHZ(idlo, sc->sc_bus_clock));
aprint_normal_dev(self, "raw %4d , %4d \n",
(idlo & 0xff), ((idlo >> 8) & 0xff));
aprint_normal_dev(self, "idhi = 0x%x\n", idhi);
aprint_normal_dev(self, "hi %4d mV, %4d MHz\n",
MSR2MV(idhi), MSR2MHZ(idhi, sc->sc_bus_clock));
aprint_normal_dev(self, "raw %4d , %4d \n",
(idhi & 0xff), ((idhi >> 8) & 0xff));
aprint_normal_dev(self, "cur = 0x%x\n", cur);
#endif
minfreq = MSR2FREQINC(idlo);
maxfreq = MSR2FREQINC(idhi);
minvolt = MSR2VOLTINC(idlo);
maxvolt = MSR2VOLTINC(idhi);
freqinc = maxfreq - minfreq;
voltinc = maxvolt - minvolt;
if (freqinc == 0)
return false;
if (freqinc < voltinc || voltinc == 0) {
tablesize = maxfreq - minfreq + 1;
if (voltinc != 0)
voltinc = voltinc * 100 / freqinc - 1;
freqinc = 100;
} else {
tablesize = maxvolt - minvolt + 1;
freqinc = freqinc * 100 / voltinc - 1;
voltinc = 100;
}
sc->sc_fake_table = kmem_alloc(tablesize *
sizeof(uint16_t), KM_SLEEP);
sc->sc_fake_fqlist.n = tablesize;
freq = maxfreq * 100;
volt = maxvolt * 100;
for (j = 0; j < tablesize; j++) {
sc->sc_fake_table[j] = (((freq + 99) / 100) << 8) +
(volt + 99) / 100;
#ifdef EST_DEBUG
aprint_normal_dev(self, "fake entry %d: %4d mV, "
"%4d MHz, MSR*100 mV = %4d freq = %4d\n",
j, MSR2MV(sc->sc_fake_table[j]),
MSR2MHZ(sc->sc_fake_table[j], sc->sc_bus_clock),
volt, freq);
#endif
freq -= freqinc;
volt -= voltinc;
}
sc->sc_fake_fqlist.table = sc->sc_fake_table;
sc->sc_fake_fqlist.vendor = cpu_vendor;
sc->sc_fqlist = &sc->sc_fake_fqlist;
}
sc->sc_freqs_len = sc->sc_fqlist->n * (sizeof("9999 ") - 1) + 1;
sc->sc_freqs = kmem_zalloc(sc->sc_freqs_len, KM_SLEEP);
for (i = len = 0; i < sc->sc_fqlist->n; i++) {
if (len >= sc->sc_freqs_len)
break;
len += snprintf(sc->sc_freqs + len, sc->sc_freqs_len - len,
"%d%s", MSR2MHZ(sc->sc_fqlist->table[i], sc->sc_bus_clock),
i < sc->sc_fqlist->n - 1 ? " " : "");
}
aprint_debug_dev(self, "%d mV, %d (MHz): %s\n", mv,
MSR2MHZ(msr, sc->sc_bus_clock), sc->sc_freqs);
return true;
}
static void
est_xcall(uint16_t val)
{
struct msr_rw_info msr;
uint64_t xc;
msr.msr_read = true;
msr.msr_type = MSR_PERF_CTL;
msr.msr_mask = 0xffffULL;
msr.msr_value = val;
xc = xc_broadcast(0, x86_msr_xcall, &msr, NULL);
xc_wait(xc);
}
static bool
est_sysctl(device_t self)
{
struct est_softc *sc = device_private(self);
const struct sysctlnode *node, *cpunode, *freqnode;
int rv;
rv = sysctl_createv(&sc->sc_log, 0, NULL, &node,
CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
if (rv != 0)
goto fail;
rv = sysctl_createv(&sc->sc_log, 0, &node, &cpunode,
0, CTLTYPE_NODE, "cpu", NULL,
NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
if (rv != 0)
goto fail;
rv = sysctl_createv(&sc->sc_log, 0, &cpunode, &freqnode,
0, CTLTYPE_NODE, "frequency", NULL,
NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
if (rv != 0)
goto fail;
rv = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
CTLFLAG_READWRITE, CTLTYPE_INT, "target", NULL,
est_sysctl_helper, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL);
if (rv != 0)
goto fail;
sc->sc_node_target = node->sysctl_num;
rv = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
0, CTLTYPE_INT, "current", NULL,
est_sysctl_helper, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL);
if (rv != 0)
goto fail;
sc->sc_node_current = node->sysctl_num;
rv = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
0, CTLTYPE_STRING, "available", NULL,
NULL, 0, sc->sc_freqs, sc->sc_freqs_len,
CTL_CREATE, CTL_EOL);
if (rv != 0)
goto fail;
return true;
fail:
aprint_error_dev(self, "failed to initialize sysctl (err %d)\n", rv);
sysctl_teardown(&sc->sc_log);
sc->sc_log = NULL;
return false;
}
static int
est_sysctl_helper(SYSCTLFN_ARGS)
{
struct sysctlnode node;
struct est_softc *sc;
int fq, err, i, oldfq;
fq = oldfq = 0;
node = *rnode;
sc = node.sysctl_data;
if (sc->sc_fqlist == NULL)
return EOPNOTSUPP;
node.sysctl_data = &fq;
if (rnode->sysctl_num == sc->sc_node_target) {
fq = oldfq = MSR2MHZ(rdmsr(MSR_PERF_CTL), sc->sc_bus_clock);
} else if (rnode->sysctl_num == sc->sc_node_current) {
fq = MSR2MHZ(rdmsr(MSR_PERF_STATUS), sc->sc_bus_clock);
} else
return EOPNOTSUPP;
err = sysctl_lookup(SYSCTLFN_CALL(&node));
if (err != 0 || newp == NULL)
return err;
if (fq == oldfq || rnode->sysctl_num != sc->sc_node_target)
return 0;
for (i = sc->sc_fqlist->n - 1; i > 0; i--) {
if (MSR2MHZ(sc->sc_fqlist->table[i], sc->sc_bus_clock) >= fq)
break;
}
est_xcall(sc->sc_fqlist->table[i]);
return 0;
}
MODULE(MODULE_CLASS_DRIVER, est, NULL);
#ifdef _MODULE
#include "ioconf.c"
#endif
static int
est_modcmd(modcmd_t cmd, void *aux)
{
int error = 0;
switch (cmd) {
case MODULE_CMD_INIT:
#ifdef _MODULE
error = config_init_component(cfdriver_ioconf_est,
cfattach_ioconf_est, cfdata_ioconf_est);
#endif
return error;
case MODULE_CMD_FINI:
#ifdef _MODULE
error = config_fini_component(cfdriver_ioconf_est,
cfattach_ioconf_est, cfdata_ioconf_est);
#endif
return error;
default:
return ENOTTY;
}
}