1 /******************************************************************************
3 * Name: hwcpu32.c - CPU support for IA32 (Throttling, Cx_states)
6 *****************************************************************************/
9 * Copyright (C) 2000 R. Byron Moore
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 #define _COMPONENT HARDWARE
31 MODULE_NAME ("Hwcpu32")
34 #define BIT_4 0x10 /* TBD: [investigate] is this correct? */
37 /****************************************************************************
39 * FUNCTION: Acpi_hw_enter_c1
41 * PARAMETERS: Pblk_address - Address of the processor control block
42 * Pm_timer_ticks - Number of PM timer ticks elapsed while asleep
44 * RETURN: Function status.
46 * DESCRIPTION: Set C1 state on IA32 processor (halt)
48 ****************************************************************************/
52 ACPI_IO_ADDRESS pblk_address
,
58 if (!pm_timer_ticks
) {
65 *pm_timer_ticks
= ACPI_UINT32_MAX
;
68 timer
= acpi_hw_pmt_ticks ();
81 timer
= acpi_hw_pmt_ticks () - timer
;
83 *pm_timer_ticks
= timer
;
90 /****************************************************************************
92 * FUNCTION: Acpi_hw_enter_c2
94 * PARAMETERS: Pblk_address - Address of the processor control block
95 * Pm_timer_ticks - Number of PM timer ticks elapsed while asleep
99 * DESCRIPTION: Set C2 state on IA32 processor
101 ****************************************************************************/
105 ACPI_IO_ADDRESS pblk_address
,
111 if (!pblk_address
|| !pm_timer_ticks
) {
112 return (AE_BAD_PARAMETER
);
116 * Disable interrupts before all C2/C3 transitions.
120 timer
= acpi_hw_pmt_ticks ();
125 * Read from the P_LVL2 (P_BLK+4) register to invoke a C2 transition.
127 acpi_os_in8 ((ACPI_IO_ADDRESS
) (pblk_address
+ 4));
132 * We have to do something useless after reading LVL2 because chipsets
133 * cannot guarantee that STPCLK# gets asserted in time to freeze execution.
135 acpi_hw_register_read (ACPI_MTX_DO_NOT_LOCK
, PM2_CONTROL
);
138 * Compute Time in C2:
139 * -------------------
141 timer
= acpi_hw_pmt_ticks () - timer
;
143 *pm_timer_ticks
= timer
;
146 * Re-enable interrupts after coming out of C2/C3.
154 /****************************************************************************
156 * FUNCTION: Acpi_hw_enter_c3
158 * PARAMETERS: Pblk_address - Address of the processor control block
159 * Pm_timer_ticks - Number of PM timer ticks elapsed while asleep
161 * RETURN: Status of function
163 * DESCRIPTION: Set C3 state on IA32 processor (UP only, cache coherency via
164 * disabling bus mastering)
166 ****************************************************************************/
170 ACPI_IO_ADDRESS pblk_address
,
174 u32 bus_master_status
= 0;
177 if (!pblk_address
|| !pm_timer_ticks
) {
178 return (AE_BAD_PARAMETER
);
182 * Check the BM_STS bit, if it is set, do not enter C3
183 * but clear the bit (with a write) and exit, telling
184 * the calling module that we spent zero time in C3.
185 * If bus mastering continues, this action should
186 * eventually cause a demotion to C2
188 if (1 == (bus_master_status
=
189 acpi_hw_register_bit_access (ACPI_READ
, ACPI_MTX_LOCK
, BM_STS
)))
192 * Clear the BM_STS bit by setting it.
194 acpi_hw_register_bit_access (ACPI_WRITE
, ACPI_MTX_LOCK
, BM_STS
, 1);
200 * Disable interrupts before all C2/C3 transitions.
205 * Disable Bus Mastering:
206 * ----------------------
207 * Set the PM2_CNT.ARB_DIS bit (bit #0), preserving all other bits.
209 acpi_hw_register_bit_access(ACPI_WRITE
, ACPI_MTX_LOCK
, ARB_DIS
, 1);
212 * Get the timer base before entering C state
214 timer
= acpi_hw_pmt_ticks ();
219 * Read from the P_LVL3 (P_BLK+5) register to invoke a C3 transition.
221 acpi_os_in8 ((ACPI_IO_ADDRESS
)(pblk_address
+ 5));
226 * We have to do something useless after reading LVL3 because chipsets
227 * cannot guarantee that STPCLK# gets asserted in time to freeze execution.
229 acpi_hw_register_read (ACPI_MTX_DO_NOT_LOCK
, PM2_CONTROL
);
231 * Immediately compute the time in the C state
233 timer
= acpi_hw_pmt_ticks() - timer
;
236 * Re-Enable Bus Mastering:
237 * ------------------------
238 * Clear the PM2_CNT.ARB_DIS bit (bit #0), preserving all other bits.
240 acpi_hw_register_bit_access(ACPI_WRITE
, ACPI_MTX_LOCK
, ARB_DIS
, 0);
242 /* TBD: [Unhandled]: Support 24-bit timers (this algorithm assumes 32-bit) */
244 *pm_timer_ticks
= timer
;
247 * Re-enable interrupts after coming out of C2/C3.
255 /****************************************************************************
257 * FUNCTION: Acpi_hw_enter_cx
259 * PARAMETERS: Processor_handle - handle of the processor
261 * RETURN: Status of function
263 * DESCRIPTION: Invoke the currently active processor Cx handler to put this
264 * processor to sleep.
266 ****************************************************************************/
270 ACPI_IO_ADDRESS pblk_address
,
274 if (!acpi_hw_cx_handlers
[acpi_hw_active_cx_state
]) {
278 return (acpi_hw_cx_handlers
[acpi_hw_active_cx_state
] (pblk_address
, pm_timer_ticks
));
282 /****************************************************************************
284 * FUNCTION: Acpi_hw_set_cx
286 * PARAMETERS: State - value (1-3) of the Cx state to 'make active'
288 * RETURN: Function status.
290 * DESCRIPTION: Sets the state to use during calls to Acpi_hw_enter_cx().
292 ****************************************************************************/
302 if ((cx_state
< 1) || (cx_state
> 3)) {
303 return (AE_BAD_PARAMETER
);
306 if (!acpi_hw_cx_handlers
[cx_state
]) {
313 * We only care when moving from one state to another...
315 if (acpi_hw_active_cx_state
== cx_state
) {
320 * Prepare to Use New State:
321 * -------------------------
322 * If the new Cx_state is C3, the BM_RLD bit must be set to allow
323 * the generation of a bus master requets to cause the processor
324 * in the C3 state to transition to the C0 state.
329 acpi_hw_register_bit_access (ACPI_WRITE
, ACPI_MTX_LOCK
, BM_RLD
, 1);
334 * Clean up from Old State:
335 * ------------------------
336 * If the old Cx_state was C3, the BM_RLD bit is reset. When the
337 * bit is reset, the generation of a bus master request does not
338 * effect any processor in the C3 state.
340 switch (acpi_hw_active_cx_state
)
343 acpi_hw_register_bit_access (ACPI_WRITE
, ACPI_MTX_LOCK
, BM_RLD
, 0);
351 acpi_hw_active_cx_state
= cx_state
;
357 /****************************************************************************
359 * FUNCTION: Acpi_hw_get_cx_info
361 * PARAMETERS: Cx_states - Information (latencies) on all Cx states
363 * RETURN: Status of function
365 * DESCRIPTION: This function is called both to initialize Cx handling
366 * and retrieve the current Cx information (latency values).
368 ****************************************************************************/
371 acpi_hw_get_cx_info (
374 u8 SMP_system
= FALSE
;
378 return(AE_BAD_PARAMETER
);
382 * TBD: [Unhandled] need to init SMP_system using info from the MAPIC
389 * C0 and C1 support is implied (but what about that PROC_C1 register
390 * in the FADT?!?!). Set C2/C3 to max. latency (not supported until
395 cx_states
[2] = MAX_CX_STATE_LATENCY
;
396 cx_states
[3] = MAX_CX_STATE_LATENCY
;
401 * We're only supporting C2 when the latency is <= 100 microseconds,
402 * and on SMP systems when P_LVL2_UP (which indicates C2 only on UP)
405 if (acpi_gbl_FADT
->plvl2_lat
<= 100) {
407 acpi_hw_cx_handlers
[2] = acpi_hw_enter_c2
;
408 cx_states
[2] = acpi_gbl_FADT
->plvl2_lat
;
411 else if (!acpi_gbl_FADT
->plvl2_up
) {
412 acpi_hw_cx_handlers
[2] = acpi_hw_enter_c2
;
413 cx_states
[2] = acpi_gbl_FADT
->plvl2_lat
;
420 * We're only supporting C3 on UP systems when the latency is
421 * <= 1000 microseconds and that include the ability to disable
422 * Bus Mastering while in C3 (ARB_DIS) but allows Bus Mastering
423 * requests to wake the system from C3 (BM_RLD). Note his method
424 * of maintaining cache coherency (disabling of bus mastering)
425 * cannot be used on SMP systems, and flushing caches (e.g. WBINVD)
426 * is simply too costly (at this time).
428 if (acpi_gbl_FADT
->plvl3_lat
<= 1000) {
429 if (!SMP_system
&& (acpi_gbl_FADT
->Xpm2_cnt_blk
.address
&&
430 acpi_gbl_FADT
->pm2_cnt_len
))
432 acpi_hw_cx_handlers
[3] = acpi_hw_enter_c3
;
433 cx_states
[3] = acpi_gbl_FADT
->plvl3_lat
;
441 /****************************************************************************
443 * FUNCTION: Acpi_hw_get_cx_handler
445 * PARAMETERS: State - the Cx state
446 * Handler - pointer to location for the returned handler
448 * RETURN: Status of function
450 * DESCRIPTION: This function is called to get an installed Cx state handler.
452 ****************************************************************************/
455 acpi_hw_get_cx_handler (
457 ACPI_C_STATE_HANDLER
*handler
)
460 if ((cx_state
== 0) || (cx_state
>= MAX_CX_STATES
) || !handler
) {
461 return(AE_BAD_PARAMETER
);
464 *handler
= acpi_hw_cx_handlers
[cx_state
];
470 /****************************************************************************
472 * FUNCTION: Acpi_hw_set_cx_handler
474 * PARAMETERS: Cx_state - the Cx state
475 * Handler - new Cx state handler
477 * RETURN: Status of function
479 * DESCRIPTION: This function is called to install a new Cx state handler.
481 ****************************************************************************/
484 acpi_hw_set_cx_handler (
486 ACPI_C_STATE_HANDLER handler
)
489 if ((cx_state
== 0) || (cx_state
>= MAX_CX_STATES
) || !handler
) {
490 return(AE_BAD_PARAMETER
);
493 acpi_hw_cx_handlers
[cx_state
] = handler
;
499 /**************************************************************************
501 * FUNCTION: Acpi_hw_local_pow
503 * PARAMETERS: x,y operands
507 * DESCRIPTION: Compute x ^ y
509 *************************************************************************/
517 NATIVE_UINT result
= 1;
520 for (i
= 0; i
< y
; i
++) {
528 /**************************************************************************
530 * FUNCTION: Acpi_hw_enable_throttling
532 * PARAMETERS: Pblk_address - Address of Pcnt (Processor Control)
537 * DESCRIPTION: Enable throttling by setting the THT_EN bit.
539 *************************************************************************/
542 acpi_hw_enable_throttling (
543 ACPI_IO_ADDRESS pblk_address
)
548 pblk_value
= acpi_os_in32 (pblk_address
);
549 pblk_value
= pblk_value
| BIT_4
;
550 acpi_os_out32 (pblk_address
, pblk_value
);
556 /**************************************************************************
558 * FUNCTION: Acpi_hw_disable_throttling
560 * PARAMETERS: Pblk_address - Address of Pcnt (Processor Control)
565 * DESCRIPTION:Disable throttling by clearing the THT_EN bit
567 *************************************************************************/
570 acpi_hw_disable_throttling (
571 ACPI_IO_ADDRESS pblk_address
)
576 pblk_value
= acpi_os_in32 (pblk_address
);
577 pblk_value
= pblk_value
& (~(u32
)BIT_4
);
578 acpi_os_out32 (pblk_address
, pblk_value
);
584 /**************************************************************************
586 * FUNCTION: Acpi_hw_get_duty_cycle
588 * PARAMETERS: Duty_offset Pcnt register duty cycle field offset
589 * Pblk_address Pcnt register address in chipset
590 * Num_throttle_states # of CPU throttle states this system
595 * DESCRIPTION: Get the duty cycle from the chipset
597 *************************************************************************/
600 acpi_hw_get_duty_cycle (
602 ACPI_IO_ADDRESS pblk_address
,
603 u32 num_throttle_states
)
607 u32 pcnt_mask_off_duty_field
;
611 * Use Num_throttle_states - 1 as mask [ex. 8 - 1 = 7 (Fh)]
612 * and then shift it into the right position
614 pcnt_mask_off_duty_field
= num_throttle_states
- 1;
617 * Read in the current value from the port
619 duty32_value
= acpi_os_in32 ((ACPI_IO_ADDRESS
) pblk_address
);
622 * Shift the the value to LSB
624 for (index
= 0; index
< (NATIVE_UINT
) duty_offset
; index
++) {
625 duty32_value
= duty32_value
>> 1;
629 * Get the duty field only
631 duty32_value
= duty32_value
& pcnt_mask_off_duty_field
;
633 return ((u32
) duty32_value
);
637 /**************************************************************************
639 * FUNCTION: Acpi_hw_program_duty_cycle
641 * PARAMETERS: Duty_offset Pcnt register duty cycle field offset
642 * Duty_cycle duty cycle to program into chipset
643 * Pblk_address Pcnt register address in chipset
644 * Num_throttle_states # of CPU throttle states this system
649 * DESCRIPTION: Program chipset with specified duty cycle by bit-shifting the
650 * duty cycle bits to the appropriate offset, reading the duty
651 * cycle register, OR-ing in the duty cycle, and writing it to
654 *************************************************************************/
657 acpi_hw_program_duty_cycle (
660 ACPI_IO_ADDRESS pblk_address
,
661 u32 num_throttle_states
)
665 u32 pcnt_mask_off_duty_field
;
670 * valid Duty_cycle passed
672 duty32_value
= duty_cycle
;
675 * use Num_throttle_states - 1 as mask [ex. 8 - 1 = 7 (Fh)]
676 * and then shift it into the right position
678 pcnt_mask_off_duty_field
= num_throttle_states
- 1;
683 for (index
= 0; index
< (NATIVE_UINT
) duty_offset
; index
++) {
684 pcnt_mask_off_duty_field
= pcnt_mask_off_duty_field
<< 1;
685 duty32_value
= duty32_value
<< 1;
689 * Read in the current value from the port
691 port_value
= acpi_os_in32 ((ACPI_IO_ADDRESS
) pblk_address
);
694 * Mask off the duty field so we don't OR in junk!
696 port_value
= port_value
& (~pcnt_mask_off_duty_field
);
699 * OR in the bits we want to write out to the port
701 port_value
= (port_value
| duty32_value
) & (~(u32
)BIT_4
);
704 * write it to the port
706 acpi_os_out32 ((ACPI_IO_ADDRESS
) pblk_address
, port_value
);