Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1222 lines
37 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. legacy.c
  5. Abstract:
  6. This module implements code that works on Cyrix processors with LongHaul power management support.
  7. Author:
  8. Tom Brown (t-tbrown) 11-Jun-2001
  9. Environment:
  10. Kernel mode
  11. Revision History:
  12. --*/
  13. #include <ntddk.h>
  14. #include "..\lib\processor.h"
  15. #include "legacy.h"
  16. #include "viac3.h"
  17. extern PFDO_DATA DeviceExtensions[MAX_SUPPORTED_PROCESSORS];
  18. extern UCHAR DevExtIndex;
  19. extern GLOBALS Globals;
  20. // set in IdentifyCPUVersion according to data in
  21. // "Samuel 2, Ezra, and C5M LongHaul Programmer's Guide (Version 3.0.0 Gamma)"
  22. ULONG LongHaulFlags; // Encoding of the LongHaul revision set by IdentifyCPUVersion. Use it with the following macros
  23. ULONG FsbFreq; // (MHz) Front Side Bus frequency
  24. #define POWER_V 1200 // Power is measured in mW, but this is a hack value used for states that have an unknown mV
  25. #define ABSOLUTE_MIN_FREQ 300
  26. #define DEFAULT_LATENCY_US 500000 // Set default latency to 500ms in microseconds, because transitions suck (take a long time, occupy whole proc)
  27. ULONG NextTransitionThrottle; // (%) Next state (as percentage of total speed) that a worker thread will transition to, or INVALID_THROTTLE if no worker thread is queued
  28. #define RESV (-1) // A reserved value in the tables
  29. #define BR_MULT 2 // Used to avoid putting floats in the BR tables
  30. // divide by 2 is optimised to a shift by the compiler
  31. #define LH_SOFTBR_MIN 1 // index of lowest value in LH_SOFTBR
  32. const ULONG LH_SOFTBR [32] = { // Indexed by bits written to softBR(14,19:16) to the BR that the bits represent
  33. 20, 6, 8, 18, // Actual bus ratio = LH_SOFTBR[i] / BR_MULT
  34. 19, 7, 9, 11,
  35. 12, 14, 16, 10,
  36. 13, 15, 17, 24,
  37. RESV, 22, 24, RESV,
  38. 21, 23, 25, 27,
  39. 28, 30, 32, 26,
  40. 29, 31, RESV, RESV
  41. };
  42. const ULONG LH_SOFTBR_SORT [32] = { // Indexes of LH_SOFTBR, sorted decreasing by value
  43. 26, 29, 25, 28, // BRs are put into the pss in decreasing order
  44. 24, 23, 27, 22,
  45. 18, 21, 17, 20, // LH_SOFTBR[15] is never used because there are two entries for 12.0X(24)
  46. 0, 4, 3, 14,
  47. 10, 13, 9, 12,
  48. 8, 7, 11, 6,
  49. 2, 5, 1, RESV,
  50. RESV, RESV, RESV, RESV
  51. };
  52. #define LH_SOFTBR_SIZE (sizeof(LH_SOFTBR) / sizeof(ULONG))
  53. ULONG const* LhSoftVid; // Set to LH_SOFTVID_VRM85 or LH_SOFTVID_MOBILE
  54. ULONG const* LhSoftVidSort; // Set to LH_SOFTVID_VRM85_SORT or LH_SOFTVID_MOBILE_SORT
  55. ULONG LhSoftVidSize; // Set to LH_SOFTVID_VRM85_SIZE or LH_SOFTVID_MOBILE_SIZE
  56. const ULONG LH_SOFTVID_VRM85 [32] = { // mVolts with a VRM 8.5
  57. // VRM 8.5, bit15=0
  58. 1250, 1200, 1150, 1100,
  59. 1050, 1800, 1750, 1700,
  60. 1650, 1600, 1550, 1500,
  61. 1450, 1400, 1350, 1300,
  62. 1275, 1225, 1175, 1125,
  63. 1075, 1825, 1775, 1725,
  64. 1675, 1625, 1575, 1525,
  65. 1475, 1425, 1375, 1325
  66. };
  67. const ULONG LH_SOFTVID_VRM85_SORT [32] = { // Indexes of LH_SOFTVID_VRM85, sorted decreasing by value
  68. 21, 5, 22, 6, // voltage levels are used in decreasing order in the pss
  69. 23, 7, 24, 8,
  70. 25, 9, 26, 10,
  71. 27, 11, 28, 12,
  72. 29, 13, 30, 14,
  73. 31, 15, 16, 0,
  74. 17, 1, 18, 2,
  75. 19, 3, 20, 4
  76. };
  77. #define LH_SOFTVID_VRM85_SIZE (sizeof(LH_SOFTVID_VRM85) / sizeof(ULONG))
  78. const ULONG LH_SOFTVID_MOBILE [32] = { // mVolts with a Mobile VRM
  79. // Mobile VRM, bit15=1
  80. 2000, 1950, 1900, 1850,
  81. 1800, 1750, 1700, 1650,
  82. 1600, 1550, 1500, 1450,
  83. 1400, 1350, 1300, RESV,
  84. 1275, 1250, 1225, 1200,
  85. 1175, 1150, 1125, 1100,
  86. 1075, 1050, 1025, 1000,
  87. 975, 950, 925, RESV
  88. };
  89. const ULONG LH_SOFTVID_MOBILE_SORT [32] = { // Indexes of LH_SOFTVID_MOBILE, sorted decreasing by value
  90. 0, 1, 2, 3, // voltage levels are used in decreasing order in the pss
  91. 4, 5, 6, 7,
  92. 8, 9, 10, 11,
  93. 12, 13, 14, 16,
  94. 17, 18, 19, 20,
  95. 21, 22, 23, 24,
  96. 25, 26, 27, 28,
  97. 29, 30, RESV, RESV
  98. };
  99. #define LH_SOFTVID_MOBILE_SIZE (sizeof(LH_SOFTVID_MOBILE) / sizeof(ULONG))
  100. // Indexed by bits MSR[43,35:32] to the BR*BR_MULT they represent
  101. const ULONG LH_MAX_BR [32] = {
  102. // bit 43=0
  103. 10, 6, 8, 20, // DIVIDE these values by BR_MULT to get actual BR!
  104. 11, 7, 9, 19,
  105. 18, 14, 16, 12,
  106. 24, 15, 17, 13,
  107. // bit 43=1
  108. RESV, 22, 24, RESV,
  109. 27, 23, 25, 21,
  110. 26, 30, 32, 28,
  111. RESV, 31, RESV, 29
  112. };
  113. #define LH_MAX_BR_SIZE (sizeof(LH_MAX_BR) / sizeof(ULONG))
  114. // Indexed by bits MSR[59,51:48] to the BR*BR_MULT they represent
  115. const ULONG LH_MIN_BR [32] = {
  116. // bit 59=0
  117. 10, 6, 8, 20, // DIVIDE these values by BR_MULT to get actual BR!
  118. 11, 7, 9, 19,
  119. 18, 14, 16, 12,
  120. 24, 15, 17, 13,
  121. // bit 59=1
  122. RESV, RESV, RESV, RESV,
  123. RESV, RESV, RESV, RESV,
  124. RESV, RESV, RESV, RESV,
  125. RESV, RESV, RESV, RESV
  126. };
  127. #define LH_MIN_BR_SIZE (sizeof(LH_MIN_BR) / sizeof(ULONG))
  128. const ULONG LH_FSB [4] = { // (MHz) Frequency of FSB
  129. 133,
  130. 100,
  131. RESV,
  132. 66
  133. };
  134. VOID
  135. InitializeCPU()
  136. /*++
  137. Routine Description:
  138. Optionally initializes the softVID value in the MSR to MaximumVID.
  139. Arguments:
  140. None
  141. Return Value:
  142. None
  143. --*/
  144. {
  145. LONGHAUL_MSR longhaul_msr;
  146. // Need to initilize the MSR since it boots with an undetermined value.
  147. // The transition routine needs the value to be correct since it may need
  148. // to avoid taking voltage steps larger than MAX_V_STEP mV.
  149. if( SUPPORTS_SOFTVID && SET_MAXV_AT_STARTUP ) {
  150. longhaul_msr.AsQWord = ReadMSR(MSR_LONGHAUL_ADDR);
  151. ExSetTimerResolution (10032,TRUE);
  152. TransitionNow( RESV, FALSE, longhaul_msr.MaximumVID, TRUE );
  153. ExSetTimerResolution (0, FALSE);
  154. }
  155. }
  156. VOID
  157. IdentifyLongHaulParameters()
  158. /*++
  159. Routine Description:
  160. Read LondHaulMSR RevisionID. Make sure this software supports the revision
  161. and set the flags.
  162. Sets LhSoftVid, LhSoftVidSort, LhSoftVidSize to the correct constant.
  163. Arguments:
  164. None
  165. Return Value:
  166. None
  167. --*/
  168. {
  169. LONGHAUL_MSR longhaul_msr;
  170. ULONG freq;
  171. ACPI_PSS_DESCRIPTOR pss;
  172. PSS_CONTROL control;
  173. longhaul_msr.AsQWord = ReadMSR(MSR_LONGHAUL_ADDR);
  174. if( longhaul_msr.RevisionID == 0 ) {
  175. LongHaulFlags |= SUPPORTS_MSR_FLAG | SUPPORTS_SOFTBR_FLAG;
  176. } else if( longhaul_msr.RevisionID == 1 ) {
  177. LongHaulFlags |= SUPPORTS_MSR_FLAG | SUPPORTS_SOFTBR_FLAG | SUPPORTS_SOFTVID_FLAG | NEEDS_VOLTAGE_STEPPING_FLAG | SET_MAXV_AT_STARTUP_FLAG;
  178. } else if( longhaul_msr.RevisionID == 2 ) {
  179. LongHaulFlags |= SUPPORTS_MSR_FLAG | SUPPORTS_SOFTBR_FLAG | SUPPORTS_SOFTVID_FLAG | NEEDS_VOLTAGE_STEPPING_FLAG | SET_MAXV_AT_STARTUP_FLAG;
  180. } else {
  181. DebugPrint((WARN, "Unknown LongHaul revision %u.\n", longhaul_msr.RevisionID));
  182. return;
  183. }
  184. if( SUPPORTS_SOFTVID ) {
  185. if( longhaul_msr.VRMRev == VRM85 ) {
  186. LhSoftVid = LH_SOFTVID_VRM85;
  187. LhSoftVidSort = LH_SOFTVID_VRM85_SORT;
  188. LhSoftVidSize = LH_SOFTVID_VRM85_SIZE;
  189. } else {
  190. // Mobile VRM
  191. LhSoftVid = LH_SOFTVID_MOBILE;
  192. LhSoftVidSort = LH_SOFTVID_MOBILE_SORT;
  193. LhSoftVidSize = LH_SOFTVID_MOBILE_SIZE;
  194. }
  195. }
  196. }
  197. VOID
  198. IdentifyCPUVersion()
  199. /*++
  200. Routine Description:
  201. Set the LongHaul flags according to what is known about the hardware. Expects
  202. flags to be cleared when called.
  203. Arguments:
  204. None
  205. Return Value:
  206. None
  207. --*/
  208. {
  209. ULONG eax, ebx, ecx, edx;
  210. CPUID_FUNC1 cpuid_result;
  211. // Check CPUID Function 0 returns vendor string EBX:EDX:ECX = "CentaurHauls"
  212. CPUID(0x0, &eax, &ebx, &ecx, &edx);
  213. if ( ebx != CPUID_FUNC0_EBX || edx != CPUID_FUNC0_EDX || ecx != CPUID_FUNC0_ECX ) {
  214. DebugPrint((WARN, "Invalid result from CPUID Function 0.\n"));
  215. return;
  216. }
  217. // Get Family/Model/Stepping from CPUID Function 1
  218. CPUID(0x1, &cpuid_result.AsDWord, &ebx, &ecx, &edx);
  219. if (cpuid_result.Family == 6 ) {
  220. if ( cpuid_result.Model == 6 ) {
  221. DebugPrint((TRACE, "Found VIA C3 Samuel 1 (C5A).\n" ));
  222. return;
  223. }
  224. if ( cpuid_result.Model == 7 ) {
  225. if( cpuid_result.Stepping==0) {
  226. DebugPrint((TRACE, "Found VIA C3 Samuel 2 (C5B), stepping 0.\n" ));
  227. return;
  228. } else if ( cpuid_result.Stepping <= 7 ) {
  229. DebugPrint((TRACE, "Found VIA C3 Samuel 2 (C5B), stepping 1-7.\n" ));
  230. IdentifyLongHaulParameters();
  231. return;
  232. } else {
  233. DebugPrint((TRACE, "Found VIA C3 Ezra (C5C).\n" ));
  234. IdentifyLongHaulParameters();
  235. return;
  236. }
  237. }
  238. if ( cpuid_result.Model == 8 ) {
  239. DebugPrint((TRACE, "Found VIA C3 Ezra-T (C5M).\n" ));
  240. IdentifyLongHaulParameters();
  241. return;
  242. }
  243. }
  244. DebugPrint((WARN, "Unknown CPU family/model/stepping %u/%u/%u is not supported.\n",
  245. cpuid_result.Family,
  246. cpuid_result.Model,
  247. cpuid_result.Stepping
  248. ));
  249. return;
  250. }
  251. #ifdef DBG
  252. VOID
  253. DebugShowPossibleLongHaulMSR()
  254. /*++
  255. Routine Description:
  256. Debug spew the contents of the LongHaul MSR.
  257. Arguments:
  258. None
  259. Return Value:
  260. None
  261. --*/
  262. {
  263. LONGHAUL_MSR longhaul_msr;
  264. DebugAssert( SUPPORTS_MSR );
  265. longhaul_msr.AsQWord = ReadMSR(MSR_LONGHAUL_ADDR);
  266. DebugPrint((TRACE," RevisionID: %i\n",longhaul_msr.RevisionID));
  267. DebugPrint((TRACE," RevisionKey: %i\n", longhaul_msr.RevisionKey));
  268. DebugPrint((TRACE," EnableSoftBusRatio: %i\n", longhaul_msr.EnableSoftBusRatio));
  269. DebugPrint((TRACE," EnableSoftVID: %i\n", longhaul_msr.EnableSoftVID));
  270. DebugPrint((TRACE," EnableSoftBSEL: %i\n", longhaul_msr.EnableSoftBSEL));
  271. DebugPrint((TRACE," Reserved1: %i\n", longhaul_msr.Reserved1));
  272. DebugPrint((TRACE," Reserved2: %i\n", longhaul_msr.Reserved2));
  273. DebugPrint((TRACE," Reserved3: %i\n", longhaul_msr.Reserved3));
  274. DebugPrint((TRACE," VRMRev: %i\n", longhaul_msr.VRMRev));
  275. DebugPrint((TRACE," SoftBusRatio: %i\n", longhaul_msr.SoftBusRatio4<<4 | longhaul_msr.SoftBusRatio0));
  276. DebugPrint((TRACE," SoftVID: %i\n", longhaul_msr.SoftVID));
  277. DebugPrint((TRACE," Reserved4: %i\n", longhaul_msr.Reserved4));
  278. DebugPrint((TRACE," SoftBSEL: %i\n", longhaul_msr.SoftBSEL));
  279. DebugPrint((TRACE," Reserved5: %i\n", longhaul_msr.Reserved5));
  280. DebugPrint((TRACE," MaxMHzBR: %i\n", longhaul_msr.MaxMHzBR4<<4 | longhaul_msr.MaxMHzBR0));
  281. DebugPrint((TRACE," MaximumVID: %i\n", longhaul_msr.MaximumVID));
  282. DebugPrint((TRACE," MaxMHzFSB: %i\n", longhaul_msr.MaxMHzFSB));
  283. DebugPrint((TRACE," Reserved6: %i\n", longhaul_msr.Reserved6));
  284. DebugPrint((TRACE," MinMHzBR: %i\n", longhaul_msr.MinMHzBR4<<4 | longhaul_msr.MinMHzBR0));
  285. DebugPrint((TRACE," MinimumVID: %i\n", longhaul_msr.MinimumVID));
  286. DebugPrint((TRACE," MinMHzFSB: %i\n", longhaul_msr.MinMHzFSB));
  287. DebugPrint((TRACE," Reserved7: %i\n", longhaul_msr.Reserved7));
  288. }
  289. VOID
  290. DebugShowCurrent()
  291. /*++
  292. Routine Description:
  293. Show some information about the current state of the CPU.
  294. Arguments:
  295. None
  296. Return Value:
  297. None
  298. --*/
  299. {
  300. ULONG freq;
  301. DebugPrint((TRACE,"Reading current CPU power state\n"));
  302. CalculateCpuFrequency( &freq );
  303. DebugPrint((TRACE," running at %iMhz\n", freq));
  304. if( SUPPORTS_MSR ) {
  305. DebugShowPossibleLongHaulMSR();
  306. } else {
  307. DebugPrint((ERROR," Unknown CPU.\n"));
  308. }
  309. }
  310. #endif // DBG
  311. VOID
  312. TransitionRoutine (
  313. IN PDEVICE_OBJECT DeviceObject,
  314. IN PVOID Context
  315. )
  316. /*++
  317. Routine Description:
  318. Run by a system worker thread as a work item.
  319. Arguments:
  320. DeviceObject - Device object
  321. Context - Pointer to ACPI_PSS_DESCRIPTOR for the state to transition to
  322. Return Value:
  323. None
  324. --*/
  325. {
  326. ULONG Throttle;
  327. ULONG OldThrottle;
  328. ULONG newState;
  329. PFDO_DATA DeviceExtension;
  330. DebugEnter();
  331. DeviceExtension = (PFDO_DATA) DeviceObject->DeviceExtension;
  332. do {
  333. // Get the next state
  334. Throttle = NextTransitionThrottle;
  335. DebugAssert( INVALID_PERF_STATE > 100 ); // Check
  336. // Run through the performance states looking for one
  337. // that matches this throttling level.
  338. for (newState = 0; newState < DeviceExtension->PerfStates->Count; newState++) {
  339. if (DeviceExtension->PerfStates->State[newState].PercentFrequency <= Throttle) {
  340. DebugPrint((TRACE, "TransitionRoutine found match. PerfState = %u, Freq %u%%\n",
  341. newState,
  342. DeviceExtension->PerfStates->State[newState].PercentFrequency));
  343. break;
  344. }
  345. }
  346. if( newState >= DeviceExtension->PerfStates->Count ) {
  347. DebugAssert( !"Invalid throttle or perf state not found" );
  348. return;
  349. }
  350. // Convert index in PerfStates to index in PssPackage
  351. newState = newState + DeviceExtension->PpcResult;
  352. TransitionToState( &(DeviceExtension->PssPackage->State[newState]) );
  353. // If NextTransitionThrottle is still Throttle then set
  354. // NextTransitionThrottle to INVALID_PERF_STATE and exit.
  355. // If NextTransitionThrottle is a different value from the top
  356. // of this do...while then loop to perform transition to that throttle.
  357. OldThrottle = InterlockedCompareExchange(
  358. &NextTransitionThrottle, // pointer to data that will be read, compared, and optionly changed
  359. INVALID_PERF_STATE, // data that will be written if NextTransitionThrottle=Throttle
  360. Throttle ); // Throttle we just transitioned to. Check if NextTransitionThrottle still has this value
  361. DebugAssert( OldThrottle!=INVALID_PERF_STATE ); // OldThrottle could only be INVALID_PERF_STATE here if another thread in TransitionRoutine had cleared it, QueueTransition is designied to prevent that happening.
  362. } while( OldThrottle!=Throttle );
  363. IoFreeWorkItem((PIO_WORKITEM)Context);
  364. DebugExit();
  365. }
  366. NTSTATUS
  367. QueueTransition(
  368. IN PFDO_DATA DeviceExtension,
  369. IN ULONG NewState
  370. )
  371. /*++
  372. Routine Description:
  373. Setup a transition to be executed by a system worker thread and return.
  374. Needed because the transition code must be run at IRQL < DISPATCH_LEVEL.
  375. Creates a new queued work item if NextTransitionThrottle was INVALID_PERF_STATE.
  376. Arguments:
  377. DeviceExtension - Device object
  378. NewState - Number of the state to transition to in the PerfStates table
  379. Return Value:
  380. NTSTATUS - success if transition was successfully completed
  381. --*/
  382. {
  383. PIO_WORKITEM pWorkItem;
  384. ULONG old_next_transition;
  385. DebugAssert( NewState < DeviceExtension->PerfStates->Count );
  386. old_next_transition = InterlockedExchange(
  387. &NextTransitionThrottle,
  388. DeviceExtension->PerfStates->State[NewState].PercentFrequency );
  389. if ( old_next_transition == INVALID_THROTTLE ) {
  390. DebugPrint((TRACE,"No worker thread item was in the queue. Making new work item to transition to %i%%.\n",
  391. DeviceExtension->PerfStates->State[NewState].PercentFrequency));
  392. pWorkItem = IoAllocateWorkItem( DeviceExtension->Self );
  393. IoQueueWorkItem(
  394. pWorkItem,
  395. TransitionRoutine,
  396. DelayedWorkQueue,
  397. pWorkItem
  398. );
  399. } else {
  400. DebugPrint((TRACE,"Worker thread item was going to transition to %i%%. Now going to %i%%.\n",
  401. old_next_transition, DeviceExtension->PerfStates->State[NewState].PercentFrequency));
  402. }
  403. return STATUS_SUCCESS;
  404. }
  405. ULONG
  406. CalcNextVoltageStep(
  407. IN ULONG VidFinal,
  408. IN ULONG VidCurrent
  409. )
  410. /*++
  411. Routine Description:
  412. Calculate the next safe vid. Part of a hack that only changes voltage in
  413. MAX_V_STEP (mV) steps.
  414. Arguments:
  415. vidFinal - Final goal vid(index into LhSoftVid)
  416. vidCurrent - Current vid
  417. Return Value:
  418. Next vid which the CPU can change to.
  419. --*/
  420. {
  421. ULONG pos; // position in the LhSoftVidSort table
  422. if( (!NEEDS_VOLTAGE_STEPPING) || VidFinal==VidCurrent || LhSoftVid[VidFinal]==LhSoftVid[VidCurrent]) {
  423. return VidFinal;
  424. }
  425. // Search for vidCurrent in LhSoftVidSort
  426. pos = 0;
  427. while( pos<LhSoftVidSize && LhSoftVidSort[pos]!=VidCurrent ) {
  428. ++pos;
  429. }
  430. DebugAssert( LhSoftVidSort[pos]==VidCurrent ); // Must find the vidCurrent in the LhSoftVidSort
  431. if( LhSoftVid[VidFinal] > LhSoftVid[VidCurrent] ) {
  432. // need to move pos backwards in LhSoftVidSort
  433. DebugAssert( pos != 0 ); // can't be first in table unless it is highest
  434. do {
  435. --pos;
  436. if( pos == 0 ) {
  437. break;
  438. }
  439. } while( LhSoftVidSort[pos] != VidFinal
  440. && LhSoftVid[LhSoftVidSort[pos]]-LhSoftVid[VidCurrent] < MAX_V_STEP );
  441. DebugAssert( LhSoftVid[LhSoftVidSort[pos]]-LhSoftVid[VidCurrent] <= MAX_V_STEP );
  442. } else{
  443. // Need to move pos forwards in LhSoftVidSort
  444. DebugAssert( LhSoftVid[VidFinal] < LhSoftVid[VidCurrent] );
  445. DebugAssert( pos+1 < LhSoftVidSize );
  446. do {
  447. ++pos;
  448. } while( pos+1<LhSoftVidSize && LhSoftVidSort[pos] != VidFinal
  449. && LhSoftVid[VidCurrent]-LhSoftVid[LhSoftVidSort[pos]] < MAX_V_STEP );
  450. DebugAssert( LhSoftVid[VidCurrent]-LhSoftVid[LhSoftVidSort[pos]] <= MAX_V_STEP );
  451. }
  452. return LhSoftVidSort[pos];
  453. }
  454. NTSTATUS
  455. TransitionNow(
  456. IN ULONG Fid,
  457. IN BOOLEAN EnableFid,
  458. IN ULONG Vid,
  459. IN BOOLEAN EnableVid
  460. )
  461. /*++
  462. Routine Description:
  463. Set the clock to 1ms BEFORE calling TransitionNow.
  464. Perform the state transition now and return success or not.
  465. Arguments:
  466. fid - fid(BR) to transition to
  467. enableFid - should enable fid(softBR) transition
  468. vid - vid to transition to
  469. enableVid - should enable vid(softVID) transition
  470. Return Value:
  471. NTSTATUS - success if transition was successfully completed
  472. --*/
  473. {
  474. LONGHAUL_MSR msr;
  475. KIRQL irql;
  476. DebugAssert( SUPPORTS_MSR );
  477. msr.AsQWord = ReadMSR(MSR_LONGHAUL_ADDR);
  478. msr.RevisionKey = msr.RevisionID; // Need to copy to RevisionKey for each write
  479. if( EnableFid ) {
  480. msr.EnableSoftBusRatio = 1;
  481. msr.SoftBusRatio0 = Fid & 0x0F; // Write the 4 LS bits of the BR
  482. msr.SoftBusRatio4 = Fid >> 4; // Write the MS bit of the BR
  483. DebugPrint((TRACE,"changing to fid=%i(%i/%i)\n",
  484. Fid, LH_SOFTBR[Fid], BR_MULT ));
  485. }
  486. if( EnableVid ) {
  487. DebugAssert( SUPPORTS_SOFTVID );
  488. msr.EnableSoftVID = 1;
  489. msr.SoftVID = Vid;
  490. DebugPrint((TRACE,"changing to vid=%i(%imV)\n",
  491. Vid, LhSoftVid[Vid] ));
  492. }
  493. // Raise the IRQL to mask off all interrupts except the timer
  494. KeRaiseIrql(CLOCK1_LEVEL-1, &irql);
  495. DebugPrint((TRACE,"Raised IRQL from %i to %i\n", irql, KeGetCurrentIrql()));
  496. WriteMSR(MSR_LONGHAUL_ADDR, msr.AsQWord);
  497. // Halt twice to guarantee that second halt lasts a full 1 ms before the timer interrupt
  498. // Possible optimization!! use a timer to see if the first halt last long enough to meet
  499. // the hardware specs
  500. DebugPrint((TRACE,"Calling halt\n"));
  501. _asm {
  502. hlt
  503. hlt
  504. }
  505. // Clean up as needed
  506. msr.AsQWord = ReadMSR(MSR_LONGHAUL_ADDR);
  507. msr.RevisionKey = msr.RevisionID; // Need to copy to RevisionKey for each write
  508. msr.EnableSoftBusRatio = 0;
  509. msr.EnableSoftVID = 0;
  510. WriteMSR(MSR_LONGHAUL_ADDR, msr.AsQWord);
  511. // Bring the IRQL back down to its previous value
  512. KeLowerIrql(irql);
  513. DebugPrint((TRACE,"Restored IRQL. Now %i\n", KeGetCurrentIrql()));
  514. }
  515. NTSTATUS
  516. TransitionToState(
  517. IN PACPI_PSS_DESCRIPTOR Pss
  518. )
  519. /*++
  520. Routine Description:
  521. Perform the state transition now and return success or not.
  522. Arguments:
  523. Pss - pointer to the ACPI_PSS_DESCRIPTOR of the state to transition to
  524. Return Value:
  525. NTSTATUS - success if transition was successfully completed
  526. --*/
  527. {
  528. ULONG set_timer_result;
  529. PSS_CONTROL pssControl;
  530. ULONG vidNext;
  531. LONGHAUL_MSR msr_longhaul;
  532. DebugEnter();
  533. // Copy state control bits
  534. pssControl.AsDWord = Pss->Control;
  535. DebugAssert( KeGetCurrentIrql() < DISPATCH_LEVEL ); // Callers of ExSetTimerResolution must be running at IRQL < DISPATCH_LEVEL
  536. do {
  537. // Change the timer resolution to the smallest safe amount, 1ms
  538. // Since we are at a low irql another thread may have changed it so
  539. // set it each time we call TransitionNow
  540. set_timer_result = ExSetTimerResolution (10032,TRUE); // Set timer to 1ms in 100ns units, fudged to what ExSetTimerResolution normally returns
  541. DebugPrint((TRACE,"Set the timer, returned value %lu\n", set_timer_result));
  542. // Read the current state of the msr
  543. msr_longhaul.AsQWord = ReadMSR(MSR_LONGHAUL_ADDR);
  544. DebugPrint((TRACE,"current vid=%i(%imV) fid=%i(%i)\n",
  545. msr_longhaul.SoftVID, LhSoftVid[msr_longhaul.SoftVID],
  546. (msr_longhaul.SoftBusRatio4<<4) + msr_longhaul.SoftBusRatio0, LH_SOFTBR[msr_longhaul.SoftBusRatio4<<4 | msr_longhaul.SoftBusRatio0] ));
  547. if( pssControl.EnableVid ) {
  548. vidNext = CalcNextVoltageStep(pssControl.Vid,msr_longhaul.SoftVID);
  549. if( LhSoftVid[vidNext] >= LhSoftVid[pssControl.Vid]
  550. && pssControl.EnableFid ) {
  551. // Can set the freq and voltage
  552. DebugPrint((TRACE," vidNext=%i(%imV) pssControl.Vid=%i(%imV)\n",
  553. vidNext, LhSoftVid[vidNext],
  554. pssControl.Vid, LhSoftVid[pssControl.Vid] ));
  555. TransitionNow( pssControl.Fid, TRUE, vidNext, TRUE );
  556. } else {
  557. // only set the voltage
  558. TransitionNow( RESV, FALSE, vidNext, TRUE );
  559. }
  560. } else {
  561. DebugAssert( pssControl.EnableFid ); // every state should have EnableVid or EnableFid
  562. // No voltage transition
  563. TransitionNow( pssControl.Fid, TRUE, RESV, FALSE );
  564. }
  565. }while( pssControl.EnableVid && pssControl.Vid != vidNext );
  566. // Reset the timer to its default value
  567. set_timer_result = ExSetTimerResolution (0, FALSE);
  568. DebugPrint((TRACE,"Reset the timer, returned value %lu\n", set_timer_result));
  569. DebugExit();
  570. return STATUS_SUCCESS;
  571. }
  572. VOID
  573. MeasureFSBFreq()
  574. /*++
  575. Routine Description:
  576. Measure the frequency the front side bus is running by setting the bus
  577. ratio to a known safe value and timing the core frequency.
  578. Value is put in global FsbFreq.
  579. Arguments:
  580. None
  581. Return Value:
  582. None
  583. --*/
  584. {
  585. PSS_CONTROL control;
  586. ACPI_PSS_DESCRIPTOR pss;
  587. ULONG freq;
  588. // Set the BR to a known, safe, constant
  589. control.EnableFid = 1;
  590. control.Fid = LH_SOFTBR_MIN;
  591. control.EnableVid = 0;
  592. control.Vid = 0;
  593. pss.Control = control.AsDWord;
  594. TransitionToState(&pss);
  595. // Time the fsb
  596. CalculateCpuFrequency( &freq );
  597. FsbFreq = freq * BR_MULT / LH_SOFTBR[LH_SOFTBR_MIN];
  598. DebugPrint((TRACE, "Set BR to %i(%i.%i), measured CPU at %iMHz, meaning FSB at %iMHz\n",
  599. LH_SOFTBR_MIN,
  600. LH_SOFTBR[LH_SOFTBR_MIN]/BR_MULT, (100/BR_MULT) * (LH_SOFTBR[LH_SOFTBR_MIN]%BR_MULT),
  601. freq,
  602. FsbFreq));
  603. }
  604. ULONG
  605. CalcMaxFreq(
  606. IN ULONG V,
  607. IN ULONG Vmin,
  608. IN ULONG Fmin,
  609. IN ULONG Vmax,
  610. IN ULONG Fmax
  611. )
  612. /*++
  613. Routine Description:
  614. Calculate the maximum operating frequency allowed at a particular voltage
  615. given parameters from the LongHaul MSR.
  616. Picks the max frequency that is below a line formed between two points on
  617. a voltage vs. frequency graph.
  618. The two points are (Vmin,Fmin) and (Vmax,Fmax)
  619. Arguments:
  620. V - The voltage at which you want to know the max freq
  621. Vmin - Min allowed voltage
  622. Fmin - Max allowed frequency
  623. Vmax - Max allowed voltage
  624. Fmax - Min allowed frequency
  625. Return Value:
  626. The maximum operating frequency at a specific voltage.
  627. --*/
  628. {
  629. return ((Fmax-Fmin)*(V-Vmin) + Fmin*(Vmax-Vmin)) / (Vmax-Vmin);
  630. }
  631. NTSTATUS
  632. FindAcpiPerformanceStatesLongHaul(
  633. OUT PACPI_PSS_PACKAGE* Pss
  634. )
  635. /*++
  636. Routine Description:
  637. Build a new pss containing all the available states on this CPU.
  638. Only call if supports_longhaul_msr and supports_softBR.
  639. Function will either succeed and return STATUS_SUCCESS or fail and not
  640. touch pPss.
  641. Arguments:
  642. Pss - Reference to a pointer to a pss. The pointer must point to NULL when this function is called.
  643. Return Value:
  644. NT status code
  645. --*/
  646. {
  647. LONGHAUL_MSR longhaul_msr;
  648. PACPI_PSS_PACKAGE tmpPss;
  649. ULONG iPssSize;
  650. ULONG vidStart; // index in LhSoftVidSort of the greatest possible vid
  651. ULONG vidEnd; // index+1 in LhSoftVidSort of the smallest possible vid
  652. ULONG fidStart; // index in LH_SOFTBR_SORT of the greatest possible fid
  653. ULONG fidEnd; // index+1 in LH_SOFTBR_SORT of the smallest possible fid
  654. ULONG vid;
  655. ULONG fid;
  656. ULONG freq; // temp var used for frequencies
  657. ULONG numPStates;
  658. ULONG pssState;
  659. DebugAssert( Pss );
  660. DebugAssert( *Pss == NULL );
  661. DebugAssert( SUPPORTS_MSR && SUPPORTS_SOFTBR ); // Must support the LongHaul MSR and softBR
  662. MeasureFSBFreq();
  663. longhaul_msr.AsQWord= ReadMSR(MSR_LONGHAUL_ADDR);
  664. if( SUPPORTS_SOFTVID ) {
  665. // Search for the greatest vid
  666. vidStart = 0;
  667. while( vidStart < LhSoftVidSize
  668. && LhSoftVidSort[vidStart] != longhaul_msr.MaximumVID ) {
  669. ++vidStart;
  670. }
  671. DebugAssert( vidStart < LhSoftVidSize );
  672. // search for the smallest vid, which should come after vidStart
  673. // set vidEnd to one more than that
  674. vidEnd = vidStart;
  675. while( vidEnd < LhSoftVidSize
  676. && LhSoftVidSort[vidEnd] != longhaul_msr.MinimumVID ) {
  677. ++vidEnd;
  678. }
  679. DebugAssert( vidEnd < LhSoftVidSize );
  680. ++vidEnd;
  681. // Find the max freq at min volts in LH_SOFTBR_SORT
  682. freq = LH_MIN_BR[longhaul_msr.MinMHzBR4<<4 | longhaul_msr.MinMHzBR0] * LH_FSB[longhaul_msr.MinMHzFSB]; // freq is MHz*BR_MULT
  683. fidStart = 0;
  684. while( fidStart < LH_SOFTBR_SIZE
  685. && LH_SOFTBR_SORT[fidStart] != RESV
  686. && LH_SOFTBR[LH_SOFTBR_SORT[fidStart]] > (freq / FsbFreq) ) {
  687. ++fidStart;
  688. }
  689. if( fidStart == LH_SOFTBR_SIZE || LH_SOFTBR_SORT[fidStart] == RESV) {
  690. DebugAssert( fidStart > 0 );
  691. --fidStart;
  692. }
  693. DebugPrint((TRACE, " vidStart: %u(%u,%u) vidEnd-1: %u(%u,%u)\n",
  694. vidStart, LhSoftVidSort[vidStart], LhSoftVid[LhSoftVidSort[vidStart]],
  695. vidEnd-1, LhSoftVidSort[vidEnd-1], LhSoftVid[LhSoftVidSort[vidEnd-1]] ));
  696. } else {
  697. // no vids
  698. vidStart = vidEnd = 0;
  699. // Find top fid, the max freq
  700. freq = LH_MAX_BR[longhaul_msr.MaxMHzBR4<<4 | longhaul_msr.MaxMHzBR0] * LH_FSB[longhaul_msr.MaxMHzFSB];
  701. fidStart = 0;
  702. while( fidStart < LH_SOFTBR_SIZE
  703. && LH_SOFTBR_SORT[fidStart] != RESV
  704. && LH_SOFTBR[LH_SOFTBR_SORT[fidStart]] > freq/FsbFreq ) {
  705. ++fidStart;
  706. }
  707. if( fidStart == LH_SOFTBR_SIZE || LH_SOFTBR_SORT[fidStart] == RESV) {
  708. DebugAssert( fidStart > 0 );
  709. --fidStart;
  710. }
  711. }
  712. fidEnd = fidStart;
  713. // Set fidEnd to index+1 in the LH_SOFTBR_SORT table of the lowest possible
  714. // BR at the lowest possible voltage
  715. while( fidEnd < LH_SOFTBR_SIZE
  716. && LH_SOFTBR_SORT[fidEnd] != RESV
  717. && LH_SOFTBR[LH_SOFTBR_SORT[fidEnd]] >= ABSOLUTE_MIN_FREQ/FsbFreq*BR_MULT) {
  718. ++fidEnd;
  719. }
  720. DebugAssert( fidStart <= fidEnd ); // Last fid to go in PSS must be slower than first fid, or they are the same for no freq only states
  721. DebugAssert( LH_SOFTBR_SORT[fidEnd-1]!=RESV || fidStart==fidEnd);
  722. DebugAssert( LH_SOFTBR[LH_SOFTBR_SORT[fidEnd-1]]>=(ABSOLUTE_MIN_FREQ/FsbFreq*BR_MULT) || fidStart==fidEnd);
  723. DebugPrint((TRACE, " fidStart: %u(%u,%u) fidEnd-1: %u(%u,%u)\n",
  724. fidStart, LH_SOFTBR_SORT[fidStart], LH_SOFTBR[LH_SOFTBR_SORT[fidStart]],
  725. fidEnd-1, LH_SOFTBR_SORT[fidEnd-1], LH_SOFTBR[LH_SOFTBR_SORT[fidEnd-1]] ));
  726. numPStates = fidEnd - fidStart;
  727. if( vidStart != vidEnd ) {
  728. // Have some vid states, add them without double counting the state that is both a vid and fid
  729. numPStates += vidEnd - vidStart - 1;
  730. }
  731. iPssSize = (sizeof(ACPI_PSS_DESCRIPTOR) * (numPStates - 1) ) +
  732. sizeof(ACPI_PSS_PACKAGE);
  733. tmpPss = ExAllocatePoolWithTag(NonPagedPool,
  734. iPssSize,
  735. PROCESSOR_POOL_TAG);
  736. if( ! tmpPss ) {
  737. return STATUS_INSUFFICIENT_RESOURCES;
  738. }
  739. RtlZeroMemory(tmpPss, iPssSize);
  740. tmpPss->NumPStates = (UCHAR) numPStates;
  741. pssState = 0;
  742. if( SUPPORTS_SOFTVID ) {
  743. // Add the voltage states
  744. DebugAssert( vidStart != vidEnd ); // must have some states
  745. for( vid = vidStart; vid+1 < vidEnd; ++vid ) { // Last vid state is top fid state
  746. PSS_CONTROL pssControl = {0};
  747. PSS_STATUS pssStatus = {0};
  748. freq = CalcMaxFreq(
  749. LhSoftVid[LhSoftVidSort[vid]],
  750. LhSoftVid[longhaul_msr.MinimumVID],
  751. LH_MIN_BR[longhaul_msr.MinMHzBR4<<4 | longhaul_msr.MinMHzBR0] * LH_FSB[longhaul_msr.MinMHzFSB],
  752. LhSoftVid[longhaul_msr.MaximumVID],
  753. LH_MAX_BR[longhaul_msr.MaxMHzBR4<<4 | longhaul_msr.MaxMHzBR0] * LH_FSB[longhaul_msr.MaxMHzFSB]
  754. ); // freq is in MHz * BR_MULT
  755. DebugPrint((TRACE, "Max freq at %i volts: %i\n",
  756. LhSoftVid[LhSoftVidSort[vid]],
  757. freq/BR_MULT));
  758. fid = 0;
  759. while( fid < LH_SOFTBR_SIZE
  760. && LH_SOFTBR_SORT[fid] != RESV
  761. && LH_SOFTBR[LH_SOFTBR_SORT[fid]] > freq/FsbFreq ) {
  762. ++fid;
  763. }
  764. DebugAssert( fid < LH_SOFTBR_SIZE );
  765. DebugAssert( LH_SOFTBR[LH_SOFTBR_SORT[fid]] != RESV );
  766. pssStatus.Fid = pssControl.Fid = LH_SOFTBR_SORT[fid];
  767. pssControl.EnableFid = 1;
  768. pssStatus.Vid = pssControl.Vid = LhSoftVidSort[vid];
  769. pssControl.EnableVid = 1;
  770. tmpPss->State[pssState].Control = pssControl.AsDWord;
  771. tmpPss->State[pssState].Status = pssStatus.AsDWord;
  772. tmpPss->State[pssState].CoreFrequency =
  773. FsbFreq * LH_SOFTBR[LH_SOFTBR_SORT[fid]] / BR_MULT;
  774. tmpPss->State[pssState].Power = LhSoftVid[LhSoftVidSort[vid]];
  775. tmpPss->State[pssState].Latency = DEFAULT_LATENCY_US;
  776. DebugPrint((TRACE, "Adding state[%i]: Control:%x Status:%x CoreFreq:%i Power:%i V:%i F:%i/%i\n",
  777. pssState, tmpPss->State[pssState].Control, tmpPss->State[pssState].Status,
  778. tmpPss->State[pssState].CoreFrequency, tmpPss->State[pssState].Power,
  779. LhSoftVid[LhSoftVidSort[vid]], LH_SOFTBR[LH_SOFTBR_SORT[fid]], BR_MULT));
  780. ++pssState;
  781. }
  782. DebugAssert(vid == vidEnd-1); // all fid states are at low volts
  783. for( fid = fidStart; fid < fidEnd; ++fid ) {
  784. PSS_CONTROL pssControl = {0};
  785. PSS_STATUS pssStatus = {0};
  786. pssStatus.Fid = pssControl.Fid = LH_SOFTBR_SORT[fid];
  787. pssControl.EnableFid = 1;
  788. pssStatus.Vid = pssControl.Vid = LhSoftVidSort[vidEnd-1];
  789. pssControl.EnableVid = 1;
  790. tmpPss->State[pssState].Control = pssControl.AsDWord;
  791. tmpPss->State[pssState].Status = pssStatus.AsDWord;
  792. tmpPss->State[pssState].CoreFrequency =
  793. FsbFreq * LH_SOFTBR[LH_SOFTBR_SORT[fid]] / BR_MULT;
  794. tmpPss->State[pssState].Power = LhSoftVid[LhSoftVidSort[vid]];
  795. tmpPss->State[pssState].Latency = DEFAULT_LATENCY_US;
  796. DebugPrint((TRACE, "Adding state[%i]: Control:%x Status:%x CoreFreq:%i Power:%i V:%i F:%i/%i\n",
  797. pssState, tmpPss->State[pssState].Control, tmpPss->State[pssState].Status,
  798. tmpPss->State[pssState].CoreFrequency, tmpPss->State[pssState].Power,
  799. LhSoftVid[LhSoftVidSort[vid]], LH_SOFTBR[LH_SOFTBR_SORT[fid]], BR_MULT));
  800. ++pssState;
  801. }
  802. } else {
  803. for( fid = fidStart; fid < fidEnd; ++fid ) {
  804. PSS_CONTROL pssControl = {0};
  805. PSS_STATUS pssStatus = {0};
  806. pssStatus.Fid = pssControl.Fid = LH_SOFTBR_SORT[fid];
  807. pssControl.EnableFid = 1;
  808. pssControl.EnableVid = 0;
  809. tmpPss->State[pssState].Control = pssControl.AsDWord;
  810. tmpPss->State[pssState].Status = pssStatus.AsDWord;
  811. tmpPss->State[pssState].CoreFrequency =
  812. FsbFreq * LH_SOFTBR[LH_SOFTBR_SORT[fid]] / BR_MULT;
  813. tmpPss->State[pssState].Power = POWER_V;
  814. tmpPss->State[pssState].Latency = DEFAULT_LATENCY_US;
  815. DebugPrint((TRACE, "Adding state[%i]: Control:%x Status:%x CoreFreq:%i Power:%i F:%i/%i\n",
  816. pssState, tmpPss->State[pssState].Control, tmpPss->State[pssState].Status,
  817. tmpPss->State[pssState].CoreFrequency, tmpPss->State[pssState].Power,
  818. LH_SOFTBR[LH_SOFTBR_SORT[fid]], BR_MULT));
  819. ++pssState;
  820. }
  821. }
  822. *Pss = tmpPss;
  823. return STATUS_SUCCESS;
  824. }
  825. NTSTATUS
  826. InitializeNonAcpiPerformanceStates(
  827. IN PFDO_DATA DeviceExtension
  828. )
  829. /*++
  830. Routine Description:
  831. Create a PSS table for the legacy CPU states.
  832. Arguments:
  833. DeviceExtension - pointer to the device extension
  834. Return Value:
  835. NTSTATUS - NT status code
  836. --*/
  837. {
  838. NTSTATUS status = STATUS_SUCCESS;
  839. DebugEnter();
  840. PAGED_CODE();
  841. if ( !(SUPPORTS_MSR && SUPPORTS_SOFTBR) ) {
  842. DebugPrint((ERROR,"CPU not supported.\n"));
  843. status = STATUS_NOT_SUPPORTED;
  844. goto InitializeNonAcpiPerformanceStatesExit;
  845. }
  846. DeviceExtension->CurrentPerfState = INVALID_PERF_STATE;
  847. DeviceExtension->LegacyInterface = TRUE;
  848. // Set up _PCT
  849. DeviceExtension->PctPackage.Control.AddressSpaceID = AcpiGenericSpaceFixedFunction;
  850. DeviceExtension->PctPackage.Status.AddressSpaceID = AcpiGenericSpaceFixedFunction;
  851. status = FindAcpiPerformanceStatesLongHaul(&(DeviceExtension->PssPackage));
  852. if (!NT_SUCCESS(status)) {
  853. goto InitializeNonAcpiPerformanceStatesExit;
  854. }
  855. // Need to merge this new data with our perfstates
  856. status = MergePerformanceStates(DeviceExtension);
  857. InitializeNonAcpiPerformanceStatesExit:
  858. if (!NT_SUCCESS(status)) {
  859. if (DeviceExtension->PssPackage) {
  860. // Need to undo what has been done
  861. ExFreePool(DeviceExtension->PssPackage);
  862. DeviceExtension->PssPackage = NULL;
  863. }
  864. DeviceExtension->LegacyInterface = FALSE;
  865. }
  866. DebugExitStatus(status);
  867. return status;
  868. }
  869. NTSTATUS
  870. AcpiLegacyPerfStateTransition(
  871. IN PFDO_DATA DeviceExtension,
  872. IN ULONG State
  873. )
  874. /*++
  875. Routine Description:
  876. Perform a transition using the FFH on LongHaul viac3 chips
  877. Arguments:
  878. DeviceExtension - pointer to the device extension
  879. State - Target State
  880. Return Value:
  881. NT Status
  882. --*/
  883. {
  884. return Acpi2PerfStateTransition(DeviceExtension, State + DeviceExtension->PpcResult);
  885. }
  886. NTSTATUS
  887. GetLegacyMaxProcFrequency(
  888. OUT PULONG CpuSpeed
  889. )
  890. /*++
  891. Routine Description:
  892. Don't use
  893. Arguments:
  894. CpuSpeed - pointer to a ULONG
  895. Return Value:
  896. NTSTATUS - Always returns STATUS_NOT_FOUND
  897. --*/
  898. {
  899. TRAP();
  900. return STATUS_NOT_FOUND;
  901. }