Index: sys/x86/cpufreq/est.c =================================================================== --- sys/x86/cpufreq/est.c (revision 222863) +++ sys/x86/cpufreq/est.c (working copy) @@ -48,7 +48,16 @@ __FBSDID("$FreeBSD$"); #include #include "acpi_if.h" -/* Status/control registers (from the IA-32 System Programming Guide). */ +/* FSB register (Pentium M and NetBurst only) */ +#define MSR_EBC_FREQUENCY_ID 0x2c + +/* FSB register (Core only) */ +#define MSR_FSB_FREQ 0xcd + +/* Platform info (Nehalem and Sandy Bridge only) */ +#define MSR_PLATFORM_INFO 0xce + +/* Status/control registers */ #define MSR_PERF_STATUS 0x198 #define MSR_PERF_CTL 0x199 @@ -78,6 +87,17 @@ struct est_softc { freq_info *freq_list; }; +enum est_arch { + UNKNOWN, + PM, + NETBURST, + CORE, + CORE2, + NEHALEM, + WESTMERE, + SANDY_BRIDGE, +}; + /* Convert MHz and mV into IDs for passing to the MSR. */ #define ID16(MHz, mV, bus_clk) \ (((MHz / bus_clk) << 8) | ((mV ? mV - 700 : 0) >> 4)) @@ -94,7 +114,7 @@ struct est_softc { #define CENTAUR(tab, zhi, vhi, zlo, vlo, bus_clk) \ { CPU_VENDOR_CENTAUR, ID32(zhi, vhi, zlo, vlo, bus_clk), tab } -static int msr_info_enabled = 0; +static int msr_info_enabled = 1; TUNABLE_INT("hw.est.msr_info", &msr_info_enabled); static int strict = -1; TUNABLE_INT("hw.est.strict", &strict); @@ -1059,6 +1079,41 @@ est_detach(device_t dev) return (0); } +static enum est_arch +est_get_arch(void) +{ + + switch (CPUID_TO_FAMILY(cpu_id)) { + case 0xf: + return (NETBURST); + case 0x6: + switch (CPUID_TO_MODEL(cpu_id)) { + case 0x09: /* Banias */ + case 0x0d: /* Dothan */ + return (PM); + case 0x0e: /* Core */ + return (CORE); + case 0x0f: /* Core 2 */ + case 0x17: /* Penryn (Wolfdale/Yorkfield/Harpertown) */ + case 0x1c: /* Atom (Bonnell) */ + case 0x1d: /* Penryn (Dunnington) */ + return (CORE2); + case 0x1a: /* Bloomfield/Gainestown */ + case 0x1e: /* Clarksfield/Lynnfield/Jasper Forest */ + case 0x1f: /* Havendale/Auburndale; cancelled? */ + case 0x2e: /* Beckton */ + return (NEHALEM); + case 0x25: /* Arrandale/Clarkdale */ + case 0x2c: /* Gulftown */ + return (WESTMERE); + case 0x2a: /* Core i5 & i7 */ + case 0x2d: /* Xeon */ + return (SANDY_BRIDGE); + } + } + return (UNKNOWN); +} + /* * Probe for supported CPU settings. First, check our static table of * settings. If no match, try using the ones offered by acpi_perf @@ -1084,7 +1139,8 @@ est_get_info(device_t dev) if (error) { printf( "est: CPU supports Enhanced Speedstep, but is not recognized.\n" - "est: cpu_vendor %s, msr %0jx\n", cpu_vendor, msr); + "est: CPU vendor %s, id 0x%x\n" + "est: MSR 0x%x = 0x%016jx\n", cpu_vendor, cpu_id, MSR_PERF_STATUS, msr); return (ENXIO); } @@ -1185,86 +1241,197 @@ est_table_info(device_t dev, uint64_t msr, freq_in return (0); } -static int -bus_speed_ok(int bus) +static __inline int +est_msr_bus_clock(enum est_arch arch) { + static int bus_clk[] = { + 8, /* 267 MHz (FSB 1067) */ + 4, /* 133 MHz (FSB 533) */ + 6, /* 200 MHz (FSB 800) */ + 5, /* 167 MHz (FSB 667) */ + 10, /* 333 MHz (FSB 1333) */ + 3, /* 100 MHz (FSB 400) */ + 12, /* 400 MHz (FSB 1600) */ + 0 /* Undefined */ + }; + int id; - switch (bus) { - case 100: - case 133: - case 333: - return (1); + id = 7; + switch (arch) { + case PM: + if ((rdmsr(MSR_EBL_CR_POWERON) & 0x40000) == 0) + id = 5; /* 100 MHz */ + break; + case NETBURST: + id = (rdmsr(MSR_EBC_FREQUENCY_ID) >> 16) & 0x7; + switch (id) { + case 0: + switch (CPUID_TO_MODEL(cpu_id)) { + case 0x2: + id = 5; /* 100 MHz */ + break; + case 0x3: + case 0x4: + id = 0; /* 267 MHz */ + break; + default: + id = 7; /* Undefined */ + } + break; + case 4: + if (CPUID_TO_MODEL(cpu_id) != 0x6) + id = 7; /* Undefined */ + break; + } + break; + case CORE: + case CORE2: + id = rdmsr(MSR_FSB_FREQ) & 0x7; + break; + case NEHALEM: + case WESTMERE: + id = 1; /* 133 MHz (non-turbo) */ + break; + case SANDY_BRIDGE: + id = 5; /* 100 MHz (non-turbo) */ + break; default: - return (0); + break; } + return (bus_clk[id]); } -/* - * Flesh out a simple rate table containing the high and low frequencies - * based on the current clock speed and the upper 32 bits of the MSR. - */ +static __inline int +est_msr_freq(int bus_clk, uint16_t id16) +{ + int mul; + + mul = (id16 >> 8) & 0x1f; + if ((id16 & 0x8000) == 0) + mul *= 2; + if ((id16 & 0x4000) != 0) + mul++; + return ((bus_clk * mul * 50 + 1) / 3); +} + +static __inline int +est_msr_volt(enum est_arch arch, uint16_t id16) +{ + int vid; + + vid = id16 & 0xff; + switch (arch) { + case PM: + case NETBURST: + return ((vid * 16) + 700); + case CORE: + return ((vid * 25 + 1426) / 2); + case CORE2: + return ((vid * 25 + 1651) / 2); + default: + return (CPUFREQ_VAL_UNKNOWN); + } +} + +static __inline int +est_msr_turbo(void) +{ + u_int regs[4]; + + if (cpu_high < 6) + return (0); + do_cpuid(6, regs); + if ((regs[0] & 0x2) == 0) + return (0); + if (((rdmsr(MSR_MISC_ENABLE) >> 32) & 0x40) == 0) + return (0); + return (1); +} + static int est_msr_info(device_t dev, uint64_t msr, freq_info **freqs) { struct est_softc *sc; freq_info *fp; - int bus, freq, volts; - uint16_t id; + enum est_arch arch; + int bus_clk, count, i, turbo; + uint16_t cur, max, min, step; if (!msr_info_enabled) return (EOPNOTSUPP); - /* Figure out the bus clock. */ - freq = atomic_load_acq_64(&tsc_freq) / 1000000; - id = msr >> 32; - bus = freq / (id >> 8); - device_printf(dev, "Guessed bus clock (high) of %d MHz\n", bus); - if (!bus_speed_ok(bus)) { - /* We may be running on the low frequency. */ - id = msr >> 48; - bus = freq / (id >> 8); - device_printf(dev, "Guessed bus clock (low) of %d MHz\n", bus); - if (!bus_speed_ok(bus)) + arch = est_get_arch(); + bus_clk = est_msr_bus_clock(arch); + if (bus_clk == 0) + return (EOPNOTSUPP); + if (arch < NEHALEM) { + cur = msr & 0xffff; + if (arch >= CORE && (msr & 0x8000000) == 0) { + msr = rdmsr(MSR_IA32_PLATFORM_ID); + if (bootverbose) + device_printf(dev, "MSR 0x%x = 0x%016jx\n", + MSR_IA32_PLATFORM_ID, msr); + max = msr & 0xffff; + msr = rdmsr(MSR_PLATFORM_INFO); + if (bootverbose) + device_printf(dev, "MSR 0x%x = 0x%016jx\n", + MSR_PLATFORM_INFO, msr); + min = (msr & 0xff00) | ((msr >> 48) & 0xff); + } else { + min = (msr >> 48) & 0xffff; + max = (msr >> 32) & 0xffff; + } + if (est_msr_freq(bus_clk, cur) <= est_msr_freq(bus_clk, min)) + min = cur; + if (est_msr_freq(bus_clk, cur) >= est_msr_freq(bus_clk, max)) + max = cur; + if (est_msr_freq(bus_clk, min) >= est_msr_freq(bus_clk, max)) return (EOPNOTSUPP); - /* Calculate high frequency. */ - id = msr >> 32; - freq = ((id >> 8) & 0xff) * bus; - } + /* + * Fill out a new freq table containing just the high and + * low freqs. + */ + fp = malloc(sizeof(freq_info) * 3, M_DEVBUF, M_WAITOK | M_ZERO); - /* Fill out a new freq table containing just the high and low freqs. */ - sc = device_get_softc(dev); - fp = malloc(sizeof(freq_info) * 3, M_DEVBUF, M_WAITOK | M_ZERO); + /* First, the high frequency. */ + fp[0].freq = est_msr_freq(bus_clk, max); + fp[0].volts = est_msr_volt(arch, max); + fp[0].id16 = max; + fp[0].power = CPUFREQ_VAL_UNKNOWN; - /* First, the high frequency. */ - volts = id & 0xff; - if (volts != 0) { - volts <<= 4; - volts += 700; + /* Second, the low frequency. */ + fp[1].freq = est_msr_freq(bus_clk, min); + fp[1].volts = est_msr_volt(arch, min); + fp[1].id16 = min; + fp[1].power = CPUFREQ_VAL_UNKNOWN; + } else { + msr = rdmsr(MSR_PLATFORM_INFO); + min = (msr >> 40) & 0xff; + max = (msr >> 8) & 0xff; + if (min >= max) + return (EOPNOTSUPP); + turbo = est_msr_turbo(); + count = imin(max - min + turbo + 1, MAX_SETTINGS); + fp = malloc((count + 1) * sizeof(freq_info), M_DEVBUF, + M_WAITOK | M_ZERO); + step = (max - min) / (count - turbo - 1); + if (turbo) { + fp[0].freq = est_msr_freq(bus_clk, max << 8) + 1; + fp[0].volts = CPUFREQ_VAL_UNKNOWN; + fp[0].id16 = arch > WESTMERE ? (max + 1) << 8 : max + 1; + fp[0].power = CPUFREQ_VAL_UNKNOWN; + } + for (i = turbo, cur = max; i < count; i++, cur -= step) { + fp[i].freq = est_msr_freq(bus_clk, cur << 8); + fp[i].volts = CPUFREQ_VAL_UNKNOWN; + fp[i].id16 = arch > WESTMERE ? (cur + 1) << 8 : cur + 1; + fp[i].power = CPUFREQ_VAL_UNKNOWN; + } } - fp[0].freq = freq; - fp[0].volts = volts; - fp[0].id16 = id; - fp[0].power = CPUFREQ_VAL_UNKNOWN; - device_printf(dev, "Guessed high setting of %d MHz @ %d Mv\n", freq, - volts); - /* Second, the low frequency. */ - id = msr >> 48; - freq = ((id >> 8) & 0xff) * bus; - volts = id & 0xff; - if (volts != 0) { - volts <<= 4; - volts += 700; - } - fp[1].freq = freq; - fp[1].volts = volts; - fp[1].id16 = id; - fp[1].power = CPUFREQ_VAL_UNKNOWN; - device_printf(dev, "Guessed low setting of %d MHz @ %d Mv\n", freq, - volts); - /* Table is already terminated due to M_ZERO. */ + sc = device_get_softc(dev); sc->msr_settings = TRUE; *freqs = fp; return (0); @@ -1273,6 +1440,7 @@ est_msr_info(device_t dev, uint64_t msr, freq_info static void est_get_id16(uint16_t *id16_p) { + *id16_p = rdmsr(MSR_PERF_STATUS) & 0xffff; } @@ -1291,7 +1459,7 @@ est_set_id16(device_t dev, uint16_t id16, int need /* Wait a short while for the new setting. XXX Is this necessary? */ DELAY(EST_TRANS_LAT); - if (need_check) { + if (need_check) { est_get_id16(&new_id16); if (new_id16 != id16) { if (bootverbose)