est.c revision 142203
1142140Snjl/*- 2142140Snjl * Copyright (c) 2004 Colin Percival 3142140Snjl * Copyright (c) 2005 Nate Lawson 4142140Snjl * All rights reserved. 5142140Snjl * 6142140Snjl * Redistribution and use in source and binary forms, with or without 7142140Snjl * modification, are permitted providing that the following conditions 8142140Snjl * are met: 9142140Snjl * 1. Redistributions of source code must retain the above copyright 10142140Snjl * notice, this list of conditions and the following disclaimer. 11142140Snjl * 2. Redistributions in binary form must reproduce the above copyright 12142140Snjl * notice, this list of conditions and the following disclaimer in the 13142140Snjl * documentation and/or other materials provided with the distribution. 14142140Snjl * 15142140Snjl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR``AS IS'' AND ANY EXPRESS OR 16142140Snjl * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17142140Snjl * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18142140Snjl * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 19142140Snjl * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20142140Snjl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21142140Snjl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22142140Snjl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23142140Snjl * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 24142140Snjl * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25142140Snjl * POSSIBILITY OF SUCH DAMAGE. 26142140Snjl */ 27142140Snjl 28142140Snjl#include <sys/cdefs.h> 29142140Snjl__FBSDID("$FreeBSD: head/sys/i386/cpufreq/est.c 142203 2005-02-22 06:31:45Z njl $"); 30142140Snjl 31142140Snjl#include <sys/param.h> 32142140Snjl#include <sys/bus.h> 33142140Snjl#include <sys/cpu.h> 34142140Snjl#include <sys/kernel.h> 35142140Snjl#include <sys/module.h> 36142140Snjl#include <sys/smp.h> 37142140Snjl#include <sys/systm.h> 38142140Snjl 39142140Snjl#include "cpufreq_if.h" 40142140Snjl#include <machine/md_var.h> 41142140Snjl 42142140Snjl/* Status/control registers (from the IA-32 System Programming Guide). */ 43142140Snjl#define MSR_PERF_STATUS 0x198 44142140Snjl#define MSR_PERF_CTL 0x199 45142140Snjl 46142140Snjl/* Register and bit for enabling SpeedStep. */ 47142140Snjl#define MSR_MISC_ENABLE 0x1a0 48142140Snjl#define MSR_SS_ENABLE (1<<16) 49142140Snjl 50142140Snjl/* Frequency and MSR control values. */ 51142140Snjltypedef struct { 52142140Snjl uint16_t freq; 53142140Snjl uint16_t volts; 54142140Snjl uint16_t id16; 55142140Snjl} freq_info; 56142140Snjl 57142140Snjl/* Identifying characteristics of a processor and supported frequencies. */ 58142140Snjltypedef struct { 59142140Snjl const char *vendor; 60142140Snjl uint32_t id32; 61142140Snjl uint32_t bus_clk; 62142140Snjl const freq_info *freqtab; 63142140Snjl} cpu_info; 64142140Snjl 65142140Snjlstruct est_softc { 66142140Snjl device_t dev; 67142140Snjl const freq_info *freq_list; 68142140Snjl}; 69142140Snjl 70142140Snjl/* Convert MHz and mV into IDs for passing to the MSR. */ 71142140Snjl#define ID16(MHz, mV, bus_clk) \ 72142140Snjl (((MHz / bus_clk) << 8) | ((mV ? mV - 700 : 0) >> 4)) 73142140Snjl#define ID32(MHz_hi, mV_hi, MHz_lo, mV_lo, bus_clk) \ 74142140Snjl ((ID16(MHz_lo, mV_lo, bus_clk) << 16) | (ID16(MHz_hi, mV_hi, bus_clk))) 75142140Snjl 76142140Snjl/* Format for storing IDs in our table. */ 77142140Snjl#define FREQ_INFO(MHz, mV, bus_clk) \ 78142140Snjl { MHz, mV, ID16(MHz, mV, bus_clk) } 79142140Snjl#define INTEL(tab, zhi, vhi, zlo, vlo, bus_clk) \ 80142140Snjl { GenuineIntel, ID32(zhi, vhi, zlo, vlo, bus_clk), bus_clk, tab } 81142140Snjl 82142140Snjlconst char GenuineIntel[] = "GenuineIntel"; 83142140Snjl 84142140Snjl/* Default bus clock value for Centrino processors. */ 85142140Snjl#define INTEL_BUS_CLK 100 86142140Snjl 87142140Snjl/* XXX Update this if new CPUs have more settings. */ 88142140Snjl#define EST_MAX_SETTINGS 10 89142140SnjlCTASSERT(EST_MAX_SETTINGS <= MAX_SETTINGS); 90142140Snjl 91142140Snjl/* Estimate in microseconds of latency for performing a transition. */ 92142140Snjl#define EST_TRANS_LAT 10 93142140Snjl 94142140Snjl/* 95142140Snjl * Frequency (MHz) and voltage (mV) settings. Data from the 96142140Snjl * Intel Pentium M Processor Datasheet (Order Number 252612), Table 5. 97142140Snjl * 98142140Snjl * XXX New Dothan processors have multiple VID# with different 99142140Snjl * settings for each VID#. Since we can't uniquely identify this info 100142140Snjl * without undisclosed methods from Intel, we can't support newer 101142140Snjl * processors with this table method. If ACPI Px states are supported, 102142140Snjl * we can get info from them. 103142140Snjl */ 104142140Snjlconst freq_info PM17_130[] = { 105142140Snjl /* 130nm 1.70GHz Pentium M */ 106142140Snjl FREQ_INFO(1700, 1484, INTEL_BUS_CLK), 107142140Snjl FREQ_INFO(1400, 1308, INTEL_BUS_CLK), 108142140Snjl FREQ_INFO(1200, 1228, INTEL_BUS_CLK), 109142140Snjl FREQ_INFO(1000, 1116, INTEL_BUS_CLK), 110142140Snjl FREQ_INFO( 800, 1004, INTEL_BUS_CLK), 111142140Snjl FREQ_INFO( 600, 956, INTEL_BUS_CLK), 112142140Snjl FREQ_INFO( 0, 0, 1), 113142140Snjl}; 114142140Snjlconst freq_info PM16_130[] = { 115142140Snjl /* 130nm 1.60GHz Pentium M */ 116142140Snjl FREQ_INFO(1600, 1484, INTEL_BUS_CLK), 117142140Snjl FREQ_INFO(1400, 1420, INTEL_BUS_CLK), 118142140Snjl FREQ_INFO(1200, 1276, INTEL_BUS_CLK), 119142140Snjl FREQ_INFO(1000, 1164, INTEL_BUS_CLK), 120142140Snjl FREQ_INFO( 800, 1036, INTEL_BUS_CLK), 121142140Snjl FREQ_INFO( 600, 956, INTEL_BUS_CLK), 122142140Snjl FREQ_INFO( 0, 0, 1), 123142140Snjl}; 124142140Snjlconst freq_info PM15_130[] = { 125142140Snjl /* 130nm 1.50GHz Pentium M */ 126142140Snjl FREQ_INFO(1500, 1484, INTEL_BUS_CLK), 127142140Snjl FREQ_INFO(1400, 1452, INTEL_BUS_CLK), 128142140Snjl FREQ_INFO(1200, 1356, INTEL_BUS_CLK), 129142140Snjl FREQ_INFO(1000, 1228, INTEL_BUS_CLK), 130142140Snjl FREQ_INFO( 800, 1116, INTEL_BUS_CLK), 131142140Snjl FREQ_INFO( 600, 956, INTEL_BUS_CLK), 132142140Snjl FREQ_INFO( 0, 0, 1), 133142140Snjl}; 134142140Snjlconst freq_info PM14_130[] = { 135142140Snjl /* 130nm 1.40GHz Pentium M */ 136142140Snjl FREQ_INFO(1400, 1484, INTEL_BUS_CLK), 137142140Snjl FREQ_INFO(1200, 1436, INTEL_BUS_CLK), 138142140Snjl FREQ_INFO(1000, 1308, INTEL_BUS_CLK), 139142140Snjl FREQ_INFO( 800, 1180, INTEL_BUS_CLK), 140142140Snjl FREQ_INFO( 600, 956, INTEL_BUS_CLK), 141142140Snjl FREQ_INFO( 0, 0, 1), 142142140Snjl}; 143142140Snjlconst freq_info PM13_130[] = { 144142140Snjl /* 130nm 1.30GHz Pentium M */ 145142140Snjl FREQ_INFO(1300, 1388, INTEL_BUS_CLK), 146142140Snjl FREQ_INFO(1200, 1356, INTEL_BUS_CLK), 147142140Snjl FREQ_INFO(1000, 1292, INTEL_BUS_CLK), 148142140Snjl FREQ_INFO( 800, 1260, INTEL_BUS_CLK), 149142140Snjl FREQ_INFO( 600, 956, INTEL_BUS_CLK), 150142140Snjl FREQ_INFO( 0, 0, 1), 151142140Snjl}; 152142140Snjlconst freq_info PM13_LV_130[] = { 153142140Snjl /* 130nm 1.30GHz Low Voltage Pentium M */ 154142140Snjl FREQ_INFO(1300, 1180, INTEL_BUS_CLK), 155142140Snjl FREQ_INFO(1200, 1164, INTEL_BUS_CLK), 156142140Snjl FREQ_INFO(1100, 1100, INTEL_BUS_CLK), 157142140Snjl FREQ_INFO(1000, 1020, INTEL_BUS_CLK), 158142140Snjl FREQ_INFO( 900, 1004, INTEL_BUS_CLK), 159142140Snjl FREQ_INFO( 800, 988, INTEL_BUS_CLK), 160142140Snjl FREQ_INFO( 600, 956, INTEL_BUS_CLK), 161142140Snjl FREQ_INFO( 0, 0, 1), 162142140Snjl}; 163142140Snjlconst freq_info PM12_LV_130[] = { 164142140Snjl /* 130 nm 1.20GHz Low Voltage Pentium M */ 165142140Snjl FREQ_INFO(1200, 1180, INTEL_BUS_CLK), 166142140Snjl FREQ_INFO(1100, 1164, INTEL_BUS_CLK), 167142140Snjl FREQ_INFO(1000, 1100, INTEL_BUS_CLK), 168142140Snjl FREQ_INFO( 900, 1020, INTEL_BUS_CLK), 169142140Snjl FREQ_INFO( 800, 1004, INTEL_BUS_CLK), 170142140Snjl FREQ_INFO( 600, 956, INTEL_BUS_CLK), 171142140Snjl FREQ_INFO( 0, 0, 1), 172142140Snjl}; 173142140Snjlconst freq_info PM11_LV_130[] = { 174142140Snjl /* 130 nm 1.10GHz Low Voltage Pentium M */ 175142140Snjl FREQ_INFO(1100, 1180, INTEL_BUS_CLK), 176142140Snjl FREQ_INFO(1000, 1164, INTEL_BUS_CLK), 177142140Snjl FREQ_INFO( 900, 1100, INTEL_BUS_CLK), 178142140Snjl FREQ_INFO( 800, 1020, INTEL_BUS_CLK), 179142140Snjl FREQ_INFO( 600, 956, INTEL_BUS_CLK), 180142140Snjl FREQ_INFO( 0, 0, 1), 181142140Snjl}; 182142140Snjlconst freq_info PM11_ULV_130[] = { 183142140Snjl /* 130 nm 1.10GHz Ultra Low Voltage Pentium M */ 184142140Snjl FREQ_INFO(1100, 1004, INTEL_BUS_CLK), 185142140Snjl FREQ_INFO(1000, 988, INTEL_BUS_CLK), 186142140Snjl FREQ_INFO( 900, 972, INTEL_BUS_CLK), 187142140Snjl FREQ_INFO( 800, 956, INTEL_BUS_CLK), 188142140Snjl FREQ_INFO( 600, 844, INTEL_BUS_CLK), 189142140Snjl FREQ_INFO( 0, 0, 1), 190142140Snjl}; 191142140Snjlconst freq_info PM10_ULV_130[] = { 192142140Snjl /* 130 nm 1.00GHz Ultra Low Voltage Pentium M */ 193142140Snjl FREQ_INFO(1000, 1004, INTEL_BUS_CLK), 194142140Snjl FREQ_INFO( 900, 988, INTEL_BUS_CLK), 195142140Snjl FREQ_INFO( 800, 972, INTEL_BUS_CLK), 196142140Snjl FREQ_INFO( 600, 844, INTEL_BUS_CLK), 197142140Snjl FREQ_INFO( 0, 0, 1), 198142140Snjl}; 199142140Snjl 200142140Snjl/* 201142140Snjl * Data from "Intel Pentium M Processor on 90nm Process with 202142140Snjl * 2-MB L2 Cache Datasheet", Order Number 302189, Table 5. 203142140Snjl */ 204142140Snjlconst freq_info PM_765A_90[] = { 205142140Snjl /* 90 nm 2.10GHz Pentium M, VID #A */ 206142140Snjl FREQ_INFO(2100, 1340, INTEL_BUS_CLK), 207142140Snjl FREQ_INFO(1800, 1276, INTEL_BUS_CLK), 208142140Snjl FREQ_INFO(1600, 1228, INTEL_BUS_CLK), 209142140Snjl FREQ_INFO(1400, 1180, INTEL_BUS_CLK), 210142140Snjl FREQ_INFO(1200, 1132, INTEL_BUS_CLK), 211142140Snjl FREQ_INFO(1000, 1084, INTEL_BUS_CLK), 212142140Snjl FREQ_INFO( 800, 1036, INTEL_BUS_CLK), 213142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 214142140Snjl FREQ_INFO( 0, 0, 1), 215142140Snjl}; 216142140Snjlconst freq_info PM_765B_90[] = { 217142140Snjl /* 90 nm 2.10GHz Pentium M, VID #B */ 218142140Snjl FREQ_INFO(2100, 1324, INTEL_BUS_CLK), 219142140Snjl FREQ_INFO(1800, 1260, INTEL_BUS_CLK), 220142140Snjl FREQ_INFO(1600, 1212, INTEL_BUS_CLK), 221142140Snjl FREQ_INFO(1400, 1180, INTEL_BUS_CLK), 222142140Snjl FREQ_INFO(1200, 1132, INTEL_BUS_CLK), 223142140Snjl FREQ_INFO(1000, 1084, INTEL_BUS_CLK), 224142140Snjl FREQ_INFO( 800, 1036, INTEL_BUS_CLK), 225142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 226142140Snjl FREQ_INFO( 0, 0, 1), 227142140Snjl}; 228142140Snjlconst freq_info PM_765C_90[] = { 229142140Snjl /* 90 nm 2.10GHz Pentium M, VID #C */ 230142140Snjl FREQ_INFO(2100, 1308, INTEL_BUS_CLK), 231142140Snjl FREQ_INFO(1800, 1244, INTEL_BUS_CLK), 232142140Snjl FREQ_INFO(1600, 1212, INTEL_BUS_CLK), 233142140Snjl FREQ_INFO(1400, 1164, INTEL_BUS_CLK), 234142140Snjl FREQ_INFO(1200, 1116, INTEL_BUS_CLK), 235142140Snjl FREQ_INFO(1000, 1084, INTEL_BUS_CLK), 236142140Snjl FREQ_INFO( 800, 1036, INTEL_BUS_CLK), 237142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 238142140Snjl FREQ_INFO( 0, 0, 1), 239142140Snjl}; 240142140Snjlconst freq_info PM_765E_90[] = { 241142140Snjl /* 90 nm 2.10GHz Pentium M, VID #E */ 242142140Snjl FREQ_INFO(2100, 1356, INTEL_BUS_CLK), 243142140Snjl FREQ_INFO(1800, 1292, INTEL_BUS_CLK), 244142140Snjl FREQ_INFO(1600, 1244, INTEL_BUS_CLK), 245142140Snjl FREQ_INFO(1400, 1196, INTEL_BUS_CLK), 246142140Snjl FREQ_INFO(1200, 1148, INTEL_BUS_CLK), 247142140Snjl FREQ_INFO(1000, 1100, INTEL_BUS_CLK), 248142140Snjl FREQ_INFO( 800, 1052, INTEL_BUS_CLK), 249142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 250142140Snjl FREQ_INFO( 0, 0, 1), 251142140Snjl}; 252142140Snjlconst freq_info PM_755A_90[] = { 253142140Snjl /* 90 nm 2.00GHz Pentium M, VID #A */ 254142140Snjl FREQ_INFO(2000, 1340, INTEL_BUS_CLK), 255142140Snjl FREQ_INFO(1800, 1292, INTEL_BUS_CLK), 256142140Snjl FREQ_INFO(1600, 1244, INTEL_BUS_CLK), 257142140Snjl FREQ_INFO(1400, 1196, INTEL_BUS_CLK), 258142140Snjl FREQ_INFO(1200, 1148, INTEL_BUS_CLK), 259142140Snjl FREQ_INFO(1000, 1100, INTEL_BUS_CLK), 260142140Snjl FREQ_INFO( 800, 1052, INTEL_BUS_CLK), 261142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 262142140Snjl FREQ_INFO( 0, 0, 1), 263142140Snjl}; 264142140Snjlconst freq_info PM_755B_90[] = { 265142140Snjl /* 90 nm 2.00GHz Pentium M, VID #B */ 266142140Snjl FREQ_INFO(2000, 1324, INTEL_BUS_CLK), 267142140Snjl FREQ_INFO(1800, 1276, INTEL_BUS_CLK), 268142140Snjl FREQ_INFO(1600, 1228, INTEL_BUS_CLK), 269142140Snjl FREQ_INFO(1400, 1180, INTEL_BUS_CLK), 270142140Snjl FREQ_INFO(1200, 1132, INTEL_BUS_CLK), 271142140Snjl FREQ_INFO(1000, 1084, INTEL_BUS_CLK), 272142140Snjl FREQ_INFO( 800, 1036, INTEL_BUS_CLK), 273142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 274142140Snjl FREQ_INFO( 0, 0, 1), 275142140Snjl}; 276142140Snjlconst freq_info PM_755C_90[] = { 277142140Snjl /* 90 nm 2.00GHz Pentium M, VID #C */ 278142140Snjl FREQ_INFO(2000, 1308, INTEL_BUS_CLK), 279142140Snjl FREQ_INFO(1800, 1276, INTEL_BUS_CLK), 280142140Snjl FREQ_INFO(1600, 1228, INTEL_BUS_CLK), 281142140Snjl FREQ_INFO(1400, 1180, INTEL_BUS_CLK), 282142140Snjl FREQ_INFO(1200, 1132, INTEL_BUS_CLK), 283142140Snjl FREQ_INFO(1000, 1084, INTEL_BUS_CLK), 284142140Snjl FREQ_INFO( 800, 1036, INTEL_BUS_CLK), 285142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 286142140Snjl FREQ_INFO( 0, 0, 1), 287142140Snjl}; 288142140Snjlconst freq_info PM_755D_90[] = { 289142140Snjl /* 90 nm 2.00GHz Pentium M, VID #D */ 290142140Snjl FREQ_INFO(2000, 1276, INTEL_BUS_CLK), 291142140Snjl FREQ_INFO(1800, 1244, INTEL_BUS_CLK), 292142140Snjl FREQ_INFO(1600, 1196, INTEL_BUS_CLK), 293142140Snjl FREQ_INFO(1400, 1164, INTEL_BUS_CLK), 294142140Snjl FREQ_INFO(1200, 1116, INTEL_BUS_CLK), 295142140Snjl FREQ_INFO(1000, 1084, INTEL_BUS_CLK), 296142140Snjl FREQ_INFO( 800, 1036, INTEL_BUS_CLK), 297142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 298142140Snjl FREQ_INFO( 0, 0, 1), 299142140Snjl}; 300142140Snjlconst freq_info PM_745A_90[] = { 301142140Snjl /* 90 nm 1.80GHz Pentium M, VID #A */ 302142140Snjl FREQ_INFO(1800, 1340, INTEL_BUS_CLK), 303142140Snjl FREQ_INFO(1600, 1292, INTEL_BUS_CLK), 304142140Snjl FREQ_INFO(1400, 1228, INTEL_BUS_CLK), 305142140Snjl FREQ_INFO(1200, 1164, INTEL_BUS_CLK), 306142140Snjl FREQ_INFO(1000, 1116, INTEL_BUS_CLK), 307142140Snjl FREQ_INFO( 800, 1052, INTEL_BUS_CLK), 308142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 309142140Snjl FREQ_INFO( 0, 0, 1), 310142140Snjl}; 311142140Snjlconst freq_info PM_745B_90[] = { 312142140Snjl /* 90 nm 1.80GHz Pentium M, VID #B */ 313142140Snjl FREQ_INFO(1800, 1324, INTEL_BUS_CLK), 314142140Snjl FREQ_INFO(1600, 1276, INTEL_BUS_CLK), 315142140Snjl FREQ_INFO(1400, 1212, INTEL_BUS_CLK), 316142140Snjl FREQ_INFO(1200, 1164, INTEL_BUS_CLK), 317142140Snjl FREQ_INFO(1000, 1116, INTEL_BUS_CLK), 318142140Snjl FREQ_INFO( 800, 1052, INTEL_BUS_CLK), 319142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 320142140Snjl FREQ_INFO( 0, 0, 1), 321142140Snjl}; 322142140Snjlconst freq_info PM_745C_90[] = { 323142140Snjl /* 90 nm 1.80GHz Pentium M, VID #C */ 324142140Snjl FREQ_INFO(1800, 1308, INTEL_BUS_CLK), 325142140Snjl FREQ_INFO(1600, 1260, INTEL_BUS_CLK), 326142140Snjl FREQ_INFO(1400, 1212, INTEL_BUS_CLK), 327142140Snjl FREQ_INFO(1200, 1148, INTEL_BUS_CLK), 328142140Snjl FREQ_INFO(1000, 1100, INTEL_BUS_CLK), 329142140Snjl FREQ_INFO( 800, 1052, INTEL_BUS_CLK), 330142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 331142140Snjl FREQ_INFO( 0, 0, 1), 332142140Snjl}; 333142140Snjlconst freq_info PM_745D_90[] = { 334142140Snjl /* 90 nm 1.80GHz Pentium M, VID #D */ 335142140Snjl FREQ_INFO(1800, 1276, INTEL_BUS_CLK), 336142140Snjl FREQ_INFO(1600, 1228, INTEL_BUS_CLK), 337142140Snjl FREQ_INFO(1400, 1180, INTEL_BUS_CLK), 338142140Snjl FREQ_INFO(1200, 1132, INTEL_BUS_CLK), 339142140Snjl FREQ_INFO(1000, 1084, INTEL_BUS_CLK), 340142140Snjl FREQ_INFO( 800, 1036, INTEL_BUS_CLK), 341142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 342142140Snjl FREQ_INFO( 0, 0, 1), 343142140Snjl}; 344142140Snjlconst freq_info PM_735A_90[] = { 345142140Snjl /* 90 nm 1.70GHz Pentium M, VID #A */ 346142140Snjl FREQ_INFO(1700, 1340, INTEL_BUS_CLK), 347142140Snjl FREQ_INFO(1400, 1244, INTEL_BUS_CLK), 348142140Snjl FREQ_INFO(1200, 1180, INTEL_BUS_CLK), 349142140Snjl FREQ_INFO(1000, 1116, INTEL_BUS_CLK), 350142140Snjl FREQ_INFO( 800, 1052, INTEL_BUS_CLK), 351142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 352142140Snjl FREQ_INFO( 0, 0, 1), 353142140Snjl}; 354142140Snjlconst freq_info PM_735B_90[] = { 355142140Snjl /* 90 nm 1.70GHz Pentium M, VID #B */ 356142140Snjl FREQ_INFO(1700, 1324, INTEL_BUS_CLK), 357142140Snjl FREQ_INFO(1400, 1244, INTEL_BUS_CLK), 358142140Snjl FREQ_INFO(1200, 1180, INTEL_BUS_CLK), 359142140Snjl FREQ_INFO(1000, 1116, INTEL_BUS_CLK), 360142140Snjl FREQ_INFO( 800, 1052, INTEL_BUS_CLK), 361142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 362142140Snjl FREQ_INFO( 0, 0, 1), 363142140Snjl}; 364142140Snjlconst freq_info PM_735C_90[] = { 365142140Snjl /* 90 nm 1.70GHz Pentium M, VID #C */ 366142140Snjl FREQ_INFO(1700, 1308, INTEL_BUS_CLK), 367142140Snjl FREQ_INFO(1400, 1228, INTEL_BUS_CLK), 368142140Snjl FREQ_INFO(1200, 1164, INTEL_BUS_CLK), 369142140Snjl FREQ_INFO(1000, 1116, INTEL_BUS_CLK), 370142140Snjl FREQ_INFO( 800, 1052, INTEL_BUS_CLK), 371142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 372142140Snjl FREQ_INFO( 0, 0, 1), 373142140Snjl}; 374142140Snjlconst freq_info PM_735D_90[] = { 375142140Snjl /* 90 nm 1.70GHz Pentium M, VID #D */ 376142140Snjl FREQ_INFO(1700, 1276, INTEL_BUS_CLK), 377142140Snjl FREQ_INFO(1400, 1212, INTEL_BUS_CLK), 378142140Snjl FREQ_INFO(1200, 1148, INTEL_BUS_CLK), 379142140Snjl FREQ_INFO(1000, 1100, INTEL_BUS_CLK), 380142140Snjl FREQ_INFO( 800, 1052, INTEL_BUS_CLK), 381142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 382142140Snjl FREQ_INFO( 0, 0, 1), 383142140Snjl}; 384142140Snjlconst freq_info PM_725A_90[] = { 385142140Snjl /* 90 nm 1.60GHz Pentium M, VID #A */ 386142140Snjl FREQ_INFO(1600, 1340, INTEL_BUS_CLK), 387142140Snjl FREQ_INFO(1400, 1276, INTEL_BUS_CLK), 388142140Snjl FREQ_INFO(1200, 1212, INTEL_BUS_CLK), 389142140Snjl FREQ_INFO(1000, 1132, INTEL_BUS_CLK), 390142140Snjl FREQ_INFO( 800, 1068, INTEL_BUS_CLK), 391142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 392142140Snjl FREQ_INFO( 0, 0, 1), 393142140Snjl}; 394142140Snjlconst freq_info PM_725B_90[] = { 395142140Snjl /* 90 nm 1.60GHz Pentium M, VID #B */ 396142140Snjl FREQ_INFO(1600, 1324, INTEL_BUS_CLK), 397142140Snjl FREQ_INFO(1400, 1260, INTEL_BUS_CLK), 398142140Snjl FREQ_INFO(1200, 1196, INTEL_BUS_CLK), 399142140Snjl FREQ_INFO(1000, 1132, INTEL_BUS_CLK), 400142140Snjl FREQ_INFO( 800, 1068, INTEL_BUS_CLK), 401142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 402142140Snjl FREQ_INFO( 0, 0, 1), 403142140Snjl}; 404142140Snjlconst freq_info PM_725C_90[] = { 405142140Snjl /* 90 nm 1.60GHz Pentium M, VID #C */ 406142140Snjl FREQ_INFO(1600, 1308, INTEL_BUS_CLK), 407142140Snjl FREQ_INFO(1400, 1244, INTEL_BUS_CLK), 408142140Snjl FREQ_INFO(1200, 1180, INTEL_BUS_CLK), 409142140Snjl FREQ_INFO(1000, 1116, INTEL_BUS_CLK), 410142140Snjl FREQ_INFO( 800, 1052, INTEL_BUS_CLK), 411142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 412142140Snjl FREQ_INFO( 0, 0, 1), 413142140Snjl}; 414142140Snjlconst freq_info PM_725D_90[] = { 415142140Snjl /* 90 nm 1.60GHz Pentium M, VID #D */ 416142140Snjl FREQ_INFO(1600, 1276, INTEL_BUS_CLK), 417142140Snjl FREQ_INFO(1400, 1228, INTEL_BUS_CLK), 418142140Snjl FREQ_INFO(1200, 1164, INTEL_BUS_CLK), 419142140Snjl FREQ_INFO(1000, 1116, INTEL_BUS_CLK), 420142140Snjl FREQ_INFO( 800, 1052, INTEL_BUS_CLK), 421142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 422142140Snjl FREQ_INFO( 0, 0, 1), 423142140Snjl}; 424142140Snjlconst freq_info PM_715A_90[] = { 425142140Snjl /* 90 nm 1.50GHz Pentium M, VID #A */ 426142140Snjl FREQ_INFO(1500, 1340, INTEL_BUS_CLK), 427142140Snjl FREQ_INFO(1200, 1228, INTEL_BUS_CLK), 428142140Snjl FREQ_INFO(1000, 1148, INTEL_BUS_CLK), 429142140Snjl FREQ_INFO( 800, 1068, INTEL_BUS_CLK), 430142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 431142140Snjl FREQ_INFO( 0, 0, 1), 432142140Snjl}; 433142140Snjlconst freq_info PM_715B_90[] = { 434142140Snjl /* 90 nm 1.50GHz Pentium M, VID #B */ 435142140Snjl FREQ_INFO(1500, 1324, INTEL_BUS_CLK), 436142140Snjl FREQ_INFO(1200, 1212, INTEL_BUS_CLK), 437142140Snjl FREQ_INFO(1000, 1148, INTEL_BUS_CLK), 438142140Snjl FREQ_INFO( 800, 1068, INTEL_BUS_CLK), 439142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 440142140Snjl FREQ_INFO( 0, 0, 1), 441142140Snjl}; 442142140Snjlconst freq_info PM_715C_90[] = { 443142140Snjl /* 90 nm 1.50GHz Pentium M, VID #C */ 444142140Snjl FREQ_INFO(1500, 1308, INTEL_BUS_CLK), 445142140Snjl FREQ_INFO(1200, 1212, INTEL_BUS_CLK), 446142140Snjl FREQ_INFO(1000, 1132, INTEL_BUS_CLK), 447142140Snjl FREQ_INFO( 800, 1068, INTEL_BUS_CLK), 448142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 449142140Snjl FREQ_INFO( 0, 0, 1), 450142140Snjl}; 451142140Snjlconst freq_info PM_715D_90[] = { 452142140Snjl /* 90 nm 1.50GHz Pentium M, VID #D */ 453142140Snjl FREQ_INFO(1500, 1276, INTEL_BUS_CLK), 454142140Snjl FREQ_INFO(1200, 1180, INTEL_BUS_CLK), 455142140Snjl FREQ_INFO(1000, 1116, INTEL_BUS_CLK), 456142140Snjl FREQ_INFO( 800, 1052, INTEL_BUS_CLK), 457142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 458142140Snjl FREQ_INFO( 0, 0, 1), 459142140Snjl}; 460142140Snjlconst freq_info PM_738_90[] = { 461142140Snjl /* 90 nm 1.40GHz Low Voltage Pentium M */ 462142140Snjl FREQ_INFO(1400, 1116, INTEL_BUS_CLK), 463142140Snjl FREQ_INFO(1300, 1116, INTEL_BUS_CLK), 464142140Snjl FREQ_INFO(1200, 1100, INTEL_BUS_CLK), 465142140Snjl FREQ_INFO(1100, 1068, INTEL_BUS_CLK), 466142140Snjl FREQ_INFO(1000, 1052, INTEL_BUS_CLK), 467142140Snjl FREQ_INFO( 900, 1036, INTEL_BUS_CLK), 468142140Snjl FREQ_INFO( 800, 1020, INTEL_BUS_CLK), 469142140Snjl FREQ_INFO( 600, 988, INTEL_BUS_CLK), 470142140Snjl FREQ_INFO( 0, 0, 1), 471142140Snjl}; 472142140Snjlconst freq_info PM_733_90[] = { 473142140Snjl /* 90 nm 1.10GHz Ultra Low Voltage Pentium M */ 474142140Snjl FREQ_INFO(1100, 940, INTEL_BUS_CLK), 475142140Snjl FREQ_INFO(1000, 924, INTEL_BUS_CLK), 476142140Snjl FREQ_INFO( 900, 892, INTEL_BUS_CLK), 477142140Snjl FREQ_INFO( 800, 876, INTEL_BUS_CLK), 478142140Snjl FREQ_INFO( 600, 812, INTEL_BUS_CLK), 479142140Snjl FREQ_INFO( 0, 0, 1), 480142140Snjl}; 481142140Snjlconst freq_info PM_723_90[] = { 482142140Snjl /* 90 nm 1.00GHz Ultra Low Voltage Pentium M */ 483142140Snjl FREQ_INFO(1000, 940, INTEL_BUS_CLK), 484142140Snjl FREQ_INFO( 900, 908, INTEL_BUS_CLK), 485142140Snjl FREQ_INFO( 800, 876, INTEL_BUS_CLK), 486142140Snjl FREQ_INFO( 600, 812, INTEL_BUS_CLK), 487142140Snjl FREQ_INFO( 0, 0, 1), 488142140Snjl}; 489142140Snjl 490142140Snjlconst cpu_info ESTprocs[] = { 491142140Snjl INTEL(PM17_130, 1700, 1484, 600, 956, INTEL_BUS_CLK), 492142140Snjl INTEL(PM16_130, 1600, 1484, 600, 956, INTEL_BUS_CLK), 493142140Snjl INTEL(PM15_130, 1500, 1484, 600, 956, INTEL_BUS_CLK), 494142140Snjl INTEL(PM14_130, 1400, 1484, 600, 956, INTEL_BUS_CLK), 495142140Snjl INTEL(PM13_130, 1300, 1388, 600, 956, INTEL_BUS_CLK), 496142140Snjl INTEL(PM13_LV_130, 1300, 1180, 600, 956, INTEL_BUS_CLK), 497142140Snjl INTEL(PM12_LV_130, 1200, 1180, 600, 956, INTEL_BUS_CLK), 498142140Snjl INTEL(PM11_LV_130, 1100, 1180, 600, 956, INTEL_BUS_CLK), 499142140Snjl INTEL(PM11_ULV_130, 1100, 1004, 600, 844, INTEL_BUS_CLK), 500142140Snjl INTEL(PM10_ULV_130, 1000, 1004, 600, 844, INTEL_BUS_CLK), 501142140Snjl INTEL(PM_765A_90, 2100, 1340, 600, 988, INTEL_BUS_CLK), 502142140Snjl INTEL(PM_765B_90, 2100, 1324, 600, 988, INTEL_BUS_CLK), 503142140Snjl INTEL(PM_765C_90, 2100, 1308, 600, 988, INTEL_BUS_CLK), 504142140Snjl INTEL(PM_765E_90, 2100, 1356, 600, 988, INTEL_BUS_CLK), 505142140Snjl INTEL(PM_755A_90, 2000, 1340, 600, 988, INTEL_BUS_CLK), 506142140Snjl INTEL(PM_755B_90, 2000, 1324, 600, 988, INTEL_BUS_CLK), 507142140Snjl INTEL(PM_755C_90, 2000, 1308, 600, 988, INTEL_BUS_CLK), 508142140Snjl INTEL(PM_755D_90, 2000, 1276, 600, 988, INTEL_BUS_CLK), 509142140Snjl INTEL(PM_745A_90, 1800, 1340, 600, 988, INTEL_BUS_CLK), 510142140Snjl INTEL(PM_745B_90, 1800, 1324, 600, 988, INTEL_BUS_CLK), 511142140Snjl INTEL(PM_745C_90, 1800, 1308, 600, 988, INTEL_BUS_CLK), 512142140Snjl INTEL(PM_745D_90, 1800, 1276, 600, 988, INTEL_BUS_CLK), 513142140Snjl INTEL(PM_735A_90, 1700, 1340, 600, 988, INTEL_BUS_CLK), 514142140Snjl INTEL(PM_735B_90, 1700, 1324, 600, 988, INTEL_BUS_CLK), 515142140Snjl INTEL(PM_735C_90, 1700, 1308, 600, 988, INTEL_BUS_CLK), 516142140Snjl INTEL(PM_735D_90, 1700, 1276, 600, 988, INTEL_BUS_CLK), 517142140Snjl INTEL(PM_725A_90, 1600, 1340, 600, 988, INTEL_BUS_CLK), 518142140Snjl INTEL(PM_725B_90, 1600, 1324, 600, 988, INTEL_BUS_CLK), 519142140Snjl INTEL(PM_725C_90, 1600, 1308, 600, 988, INTEL_BUS_CLK), 520142140Snjl INTEL(PM_725D_90, 1600, 1276, 600, 988, INTEL_BUS_CLK), 521142140Snjl INTEL(PM_715A_90, 1500, 1340, 600, 988, INTEL_BUS_CLK), 522142140Snjl INTEL(PM_715B_90, 1500, 1324, 600, 988, INTEL_BUS_CLK), 523142140Snjl INTEL(PM_715C_90, 1500, 1308, 600, 988, INTEL_BUS_CLK), 524142140Snjl INTEL(PM_715D_90, 1500, 1276, 600, 988, INTEL_BUS_CLK), 525142140Snjl INTEL(PM_738_90, 1400, 1116, 600, 988, INTEL_BUS_CLK), 526142140Snjl INTEL(PM_733_90, 1100, 940, 600, 812, INTEL_BUS_CLK), 527142140Snjl INTEL(PM_723_90, 1000, 940, 600, 812, INTEL_BUS_CLK), 528142140Snjl { NULL, 0, 0, NULL }, 529142140Snjl}; 530142140Snjl 531142140Snjlstatic void est_identify(driver_t *driver, device_t parent); 532142140Snjlstatic int est_probe(device_t parent); 533142140Snjlstatic int est_attach(device_t parent); 534142140Snjlstatic int est_detach(device_t parent); 535142140Snjlstatic int est_find_cpu(const char *vendor, uint64_t msr, uint32_t bus_clk, 536142140Snjl const freq_info **freqs); 537142140Snjlstatic const freq_info *est_get_current(const freq_info *freq_list); 538142140Snjlstatic int est_settings(device_t dev, struct cf_setting *sets, int *count); 539142140Snjlstatic int est_set(device_t dev, const struct cf_setting *set); 540142140Snjlstatic int est_get(device_t dev, struct cf_setting *set); 541142140Snjlstatic int est_type(device_t dev, int *type); 542142140Snjl 543142140Snjlstatic device_method_t est_methods[] = { 544142140Snjl /* Device interface */ 545142140Snjl DEVMETHOD(device_identify, est_identify), 546142140Snjl DEVMETHOD(device_probe, est_probe), 547142140Snjl DEVMETHOD(device_attach, est_attach), 548142140Snjl DEVMETHOD(device_detach, est_detach), 549142140Snjl 550142140Snjl /* cpufreq interface */ 551142140Snjl DEVMETHOD(cpufreq_drv_set, est_set), 552142140Snjl DEVMETHOD(cpufreq_drv_get, est_get), 553142140Snjl DEVMETHOD(cpufreq_drv_type, est_type), 554142140Snjl DEVMETHOD(cpufreq_drv_settings, est_settings), 555142140Snjl {0, 0} 556142140Snjl}; 557142140Snjl 558142140Snjlstatic driver_t est_driver = { 559142140Snjl "est", 560142140Snjl est_methods, 561142140Snjl sizeof(struct est_softc), 562142140Snjl}; 563142140Snjl 564142140Snjlstatic devclass_t est_devclass; 565142140SnjlDRIVER_MODULE(est, cpu, est_driver, est_devclass, 0, 0); 566142140Snjl 567142140Snjlstatic void 568142140Snjlest_identify(driver_t *driver, device_t parent) 569142140Snjl{ 570142140Snjl u_int p[4]; 571142140Snjl 572142140Snjl /* Make sure we're not being doubly invoked. */ 573142140Snjl if (device_find_child(parent, "est", -1) != NULL) 574142140Snjl return; 575142140Snjl 576142140Snjl /* Check that CPUID is supported and the vendor is Intel.*/ 577142140Snjl if (cpu_high == 0 || strcmp(cpu_vendor, GenuineIntel) != 0) 578142140Snjl return; 579142140Snjl 580142140Snjl /* Read capability bits and check if the CPU supports EST. */ 581142140Snjl do_cpuid(1, p); 582142140Snjl if ((p[2] & 0x80) == 0) 583142140Snjl return; 584142140Snjl 585142140Snjl if (BUS_ADD_CHILD(parent, 0, "est", -1) == NULL) 586142140Snjl device_printf(parent, "add est child failed\n"); 587142140Snjl} 588142140Snjl 589142140Snjlstatic int 590142140Snjlest_probe(device_t dev) 591142140Snjl{ 592142140Snjl const freq_info *f; 593142140Snjl device_t perf_dev; 594142140Snjl uint64_t msr; 595142140Snjl int error, type; 596142203Snjl 597142203Snjl if (resource_disabled("est", 0)) 598142203Snjl return (ENXIO); 599142140Snjl 600142140Snjl /* 601142140Snjl * If the ACPI perf driver has attached and is not just offering 602142140Snjl * info, let it manage things. 603142140Snjl */ 604142140Snjl perf_dev = device_find_child(device_get_parent(dev), "acpi_perf", -1); 605142140Snjl if (perf_dev && device_is_attached(perf_dev)) { 606142140Snjl error = CPUFREQ_DRV_TYPE(perf_dev, &type); 607142140Snjl if (error == 0 && (type & CPUFREQ_FLAG_INFO_ONLY) == 0) 608142140Snjl return (ENXIO); 609142140Snjl } 610142140Snjl 611142140Snjl /* Attempt to enable SpeedStep if not currently enabled. */ 612142140Snjl msr = rdmsr(MSR_MISC_ENABLE); 613142140Snjl if ((msr & MSR_SS_ENABLE) == 0) { 614142140Snjl wrmsr(MSR_MISC_ENABLE, msr | MSR_SS_ENABLE); 615142140Snjl 616142140Snjl /* Check if the enable failed. */ 617142140Snjl msr = rdmsr(MSR_MISC_ENABLE); 618142140Snjl if ((msr & MSR_SS_ENABLE) == 0) { 619142140Snjl device_printf(dev, "failed to enable SpeedStep\n"); 620142140Snjl return (ENXIO); 621142140Snjl } 622142140Snjl } 623142140Snjl 624142140Snjl /* Identify the exact CPU model */ 625142140Snjl msr = rdmsr(MSR_PERF_STATUS); 626142140Snjl if (est_find_cpu(cpu_vendor, msr, INTEL_BUS_CLK, &f) != 0) { 627142140Snjl printf( 628142140Snjl "CPU claims to support Enhanced Speedstep, but is not recognized.\n" 629142140Snjl "Please update driver or contact the maintainer.\n" 630142140Snjl "cpu_vendor = %s msr = %0jx, bus_clk = %x\n", 631142140Snjl cpu_vendor, msr, INTEL_BUS_CLK); 632142140Snjl return (ENXIO); 633142140Snjl } 634142140Snjl 635142140Snjl device_set_desc(dev, "Enhanced SpeedStep Frequency Control"); 636142140Snjl return (0); 637142140Snjl} 638142140Snjl 639142140Snjlstatic int 640142140Snjlest_attach(device_t dev) 641142140Snjl{ 642142140Snjl struct est_softc *sc; 643142140Snjl uint64_t msr; 644142140Snjl 645142140Snjl sc = device_get_softc(dev); 646142140Snjl sc->dev = dev; 647142140Snjl msr = rdmsr(MSR_PERF_STATUS); 648142140Snjl est_find_cpu(cpu_vendor, msr, INTEL_BUS_CLK, &sc->freq_list); 649142140Snjl cpufreq_register(dev); 650142140Snjl 651142140Snjl return (0); 652142140Snjl} 653142140Snjl 654142140Snjlstatic int 655142140Snjlest_detach(device_t dev) 656142140Snjl{ 657142140Snjl return (ENXIO); 658142140Snjl} 659142140Snjl 660142140Snjlstatic int 661142140Snjlest_find_cpu(const char *vendor, uint64_t msr, uint32_t bus_clk, 662142140Snjl const freq_info **freqs) 663142140Snjl{ 664142140Snjl const cpu_info *p; 665142140Snjl uint32_t id; 666142140Snjl 667142140Snjl /* Find a table which matches (vendor, id, bus_clk). */ 668142140Snjl id = msr >> 32; 669142140Snjl for (p = ESTprocs; p->id32 != 0; p++) { 670142140Snjl if (strcmp(p->vendor, vendor) == 0 && p->id32 == id && 671142140Snjl p->bus_clk == bus_clk) 672142140Snjl break; 673142140Snjl } 674142140Snjl if (p->id32 == 0) 675142140Snjl return (EOPNOTSUPP); 676142140Snjl 677142140Snjl /* Make sure the current setpoint is valid. */ 678142140Snjl if (est_get_current(p->freqtab) == NULL) 679142140Snjl return (EOPNOTSUPP); 680142140Snjl 681142140Snjl *freqs = p->freqtab; 682142140Snjl return (0); 683142140Snjl} 684142140Snjl 685142140Snjlstatic const freq_info * 686142140Snjlest_get_current(const freq_info *freq_list) 687142140Snjl{ 688142140Snjl const freq_info *f; 689142140Snjl int i; 690142140Snjl uint16_t id16; 691142140Snjl 692142140Snjl /* 693142140Snjl * Try a few times to get a valid value. Sometimes, if the CPU 694142140Snjl * is in the middle of an asynchronous transition (i.e., P4TCC), 695142140Snjl * we get a temporary invalid result. 696142140Snjl */ 697142140Snjl for (i = 0; i < 5; i++) { 698142140Snjl id16 = rdmsr(MSR_PERF_STATUS) & 0xffff; 699142140Snjl for (f = freq_list; f->id16 != 0; f++) { 700142140Snjl if (f->id16 == id16) 701142140Snjl return (f); 702142140Snjl } 703142140Snjl DELAY(100); 704142140Snjl } 705142140Snjl return (NULL); 706142140Snjl} 707142140Snjl 708142140Snjlstatic int 709142140Snjlest_settings(device_t dev, struct cf_setting *sets, int *count) 710142140Snjl{ 711142140Snjl struct est_softc *sc; 712142140Snjl const freq_info *f; 713142140Snjl int i; 714142140Snjl 715142140Snjl sc = device_get_softc(dev); 716142140Snjl if (*count < EST_MAX_SETTINGS) 717142140Snjl return (E2BIG); 718142140Snjl 719142140Snjl i = 0; 720142140Snjl for (f = sc->freq_list; f->freq != 0; f++) { 721142140Snjl sets[i].freq = f->freq; 722142140Snjl sets[i].volts = f->volts; 723142140Snjl sets[i].power = CPUFREQ_VAL_UNKNOWN; 724142140Snjl sets[i].lat = EST_TRANS_LAT; 725142140Snjl sets[i].dev = dev; 726142140Snjl i++; 727142140Snjl } 728142140Snjl *count = i + 1; 729142140Snjl 730142140Snjl return (0); 731142140Snjl} 732142140Snjl 733142140Snjlstatic int 734142140Snjlest_set(device_t dev, const struct cf_setting *set) 735142140Snjl{ 736142140Snjl struct est_softc *sc; 737142140Snjl const freq_info *f; 738142140Snjl uint64_t msr; 739142140Snjl 740142140Snjl /* Find the setting matching the requested one. */ 741142140Snjl sc = device_get_softc(dev); 742142140Snjl for (f = sc->freq_list; f->freq != 0; f++) { 743142140Snjl if (f->freq == set->freq) 744142140Snjl break; 745142140Snjl } 746142140Snjl if (f->freq == 0) 747142140Snjl return (EINVAL); 748142140Snjl 749142140Snjl /* Read the current register, mask out the old, set the new id. */ 750142140Snjl msr = rdmsr(MSR_PERF_CTL); 751142140Snjl msr = (msr & ~0xffff) | f->id16; 752142140Snjl wrmsr(MSR_PERF_CTL, msr); 753142140Snjl 754142140Snjl /* Wait a short while for the new setting. Is this necessary? */ 755142140Snjl DELAY(EST_TRANS_LAT); 756142140Snjl 757142140Snjl return (0); 758142140Snjl} 759142140Snjl 760142140Snjlstatic int 761142140Snjlest_get(device_t dev, struct cf_setting *set) 762142140Snjl{ 763142140Snjl struct est_softc *sc; 764142140Snjl const freq_info *f; 765142140Snjl 766142140Snjl sc = device_get_softc(dev); 767142140Snjl f = est_get_current(sc->freq_list); 768142140Snjl if (f == NULL) 769142140Snjl return (ENXIO); 770142140Snjl 771142140Snjl set->freq = f->freq; 772142140Snjl set->volts = f->volts; 773142140Snjl set->power = CPUFREQ_VAL_UNKNOWN; 774142140Snjl set->lat = EST_TRANS_LAT; 775142140Snjl set->dev = dev; 776142140Snjl return (0); 777142140Snjl} 778142140Snjl 779142140Snjlstatic int 780142140Snjlest_type(device_t dev, int *type) 781142140Snjl{ 782142140Snjl 783142140Snjl if (type == NULL) 784142140Snjl return (EINVAL); 785142140Snjl 786142140Snjl *type = CPUFREQ_TYPE_ABSOLUTE; 787142140Snjl return (0); 788142140Snjl} 789