patch-2.4.0-test3 linux/drivers/acpi/hardware/hwcpu32.c
Next file: linux/drivers/acpi/hardware/hwgpe.c
Previous file: linux/drivers/acpi/hardware/hwacpi.c
Back to the patch index
Back to the overall index
- Lines: 718
- Date:
Wed Jul 5 11:23:12 2000
- Orig file:
v2.4.0-test2/linux/drivers/acpi/hardware/hwcpu32.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.4.0-test2/linux/drivers/acpi/hardware/hwcpu32.c linux/drivers/acpi/hardware/hwcpu32.c
@@ -0,0 +1,716 @@
+/******************************************************************************
+ *
+ * Name: hwcpu32.c - CPU support for IA32 (Throttling, Cx_states)
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000 R. Byron Moore
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "acpi.h"
+#include "namesp.h"
+#include "hardware.h"
+
+#define _COMPONENT HARDWARE
+ MODULE_NAME ("Hwcpu32");
+
+
+#define BIT_4 0x10 /* TBD: [investigate] is this correct? */
+
+
+/****************************************************************************
+ *
+ * FUNCTION: Acpi_hw_enter_c1
+ *
+ * PARAMETERS: Pblk_address - Address of the processor control block
+ * Pm_timer_ticks - Number of PM timer ticks elapsed while asleep
+ *
+ * RETURN: Function status.
+ *
+ * DESCRIPTION: Set C1 state on IA32 processor (halt)
+ *
+ ****************************************************************************/
+
+ACPI_STATUS
+acpi_hw_enter_c1(
+ ACPI_IO_ADDRESS pblk_address,
+ u32 *pm_timer_ticks)
+{
+ u32 timer = 0;
+
+
+ if (!pm_timer_ticks) {
+ /*
+ * Enter C1:
+ * ---------
+ */
+ enable();
+ halt();
+ *pm_timer_ticks = ACPI_UINT32_MAX;
+ }
+ else {
+ timer = acpi_hw_pmt_ticks ();
+
+ /*
+ * Enter C1:
+ * ---------
+ */
+ enable ();
+ halt ();
+
+ /*
+ * Compute Time in C1:
+ * -------------------
+ */
+ timer = acpi_hw_pmt_ticks () - timer;
+
+ *pm_timer_ticks = timer;
+ }
+
+ return AE_OK;
+}
+
+
+/****************************************************************************
+ *
+ * FUNCTION: Acpi_hw_enter_c2
+ *
+ * PARAMETERS: Pblk_address - Address of the processor control block
+ * Pm_timer_ticks - Number of PM timer ticks elapsed while asleep
+ *
+ * RETURN: <none>
+ *
+ * DESCRIPTION: Set C2 state on IA32 processor
+ *
+ ****************************************************************************/
+
+ACPI_STATUS
+acpi_hw_enter_c2(
+ ACPI_IO_ADDRESS pblk_address,
+ u32 *pm_timer_ticks)
+{
+ u32 timer = 0;
+
+
+ if (!pblk_address || !pm_timer_ticks) {
+ return AE_BAD_PARAMETER;
+ }
+
+ /*
+ * Disable interrupts before all C2/C3 transitions.
+ */
+ disable ();
+
+ timer = acpi_hw_pmt_ticks ();
+
+ /*
+ * Enter C2:
+ * ---------
+ * Read from the P_LVL2 (P_BLK+4) register to invoke a C2 transition.
+ */
+ acpi_os_in8 ((ACPI_IO_ADDRESS) (pblk_address + 4));
+
+ /*
+ * Perform Dummy Op:
+ * -----------------
+ * We have to do something useless after reading LVL2 because chipsets
+ * cannot guarantee that STPCLK# gets asserted in time to freeze execution.
+ */
+ acpi_os_in8 ((ACPI_IO_ADDRESS) acpi_gbl_FACP->pm2_cnt_blk);
+
+ /*
+ * Compute Time in C2:
+ * -------------------
+ */
+ timer = acpi_hw_pmt_ticks () - timer;
+
+ *pm_timer_ticks = timer;
+
+ /*
+ * Re-enable interrupts after coming out of C2/C3.
+ */
+ enable ();
+
+ return AE_OK;
+}
+
+
+/****************************************************************************
+ *
+ * FUNCTION: Acpi_hw_enter_c3
+ *
+ * PARAMETERS: Pblk_address - Address of the processor control block
+ * Pm_timer_ticks - Number of PM timer ticks elapsed while asleep
+ *
+ * RETURN: Status of function
+ *
+ * DESCRIPTION: Set C3 state on IA32 processor (UP only, cache coherency via
+ * disabling bus mastering)
+ *
+ ****************************************************************************/
+
+ACPI_STATUS
+acpi_hw_enter_c3(
+ ACPI_IO_ADDRESS pblk_address,
+ u32 *pm_timer_ticks)
+{
+ u32 timer = 0;
+ u8 pm2_cnt_blk = 0;
+ u32 bus_master_status = 0;
+
+
+ if (!pblk_address || !pm_timer_ticks) {
+ return AE_BAD_PARAMETER;
+ }
+
+ /*
+ * Check the BM_STS bit, if it is set, do not enter C3
+ * but clear the bit (with a write) and exit, telling
+ * the calling module that we spent zero time in C3.
+ * If bus mastering continues, this action should
+ * eventually cause a demotion to C2
+ */
+ if (1 == (bus_master_status =
+ acpi_hw_register_access (ACPI_READ, ACPI_MTX_LOCK, (s32)BM_STS)))
+ {
+ /*
+ * Clear the BM_STS bit by setting it.
+ */
+ acpi_hw_register_access (ACPI_WRITE, ACPI_MTX_LOCK, (s32)BM_STS, 1);
+ *pm_timer_ticks = 0;
+ return AE_OK;
+ }
+
+ /*
+ * Disable interrupts before all C2/C3 transitions.
+ */
+ disable();
+
+ /*
+ * Disable Bus Mastering:
+ * ----------------------
+ * Set the PM2_CNT.ARB_DIS bit (bit #0), preserving all other bits.
+ */
+ pm2_cnt_blk = acpi_os_in8 ((ACPI_IO_ADDRESS) acpi_gbl_FACP->pm2_cnt_blk);
+ pm2_cnt_blk |= 0x01;
+ acpi_os_out8 ((ACPI_IO_ADDRESS) acpi_gbl_FACP->pm2_cnt_blk, pm2_cnt_blk);
+
+ /*
+ * Get the timer base before entering C state
+ */
+ timer = acpi_hw_pmt_ticks ();
+
+ /*
+ * Enter C3:
+ * ---------
+ * Read from the P_LVL3 (P_BLK+5) register to invoke a C3 transition.
+ */
+ acpi_os_in8 ((ACPI_IO_ADDRESS)(pblk_address + 5));
+
+ /*
+ * Perform Dummy Op:
+ * -----------------
+ * We have to do something useless after reading LVL3 because chipsets
+ * cannot guarantee that STPCLK# gets asserted in time to freeze execution.
+ */
+ acpi_os_in8 ((ACPI_IO_ADDRESS) acpi_gbl_FACP->pm2_cnt_blk);
+
+ /*
+ * Immediately compute the time in the C state
+ */
+ timer = acpi_hw_pmt_ticks() - timer;
+
+ /*
+ * Re-Enable Bus Mastering:
+ * ------------------------
+ * Clear the PM2_CNT.ARB_DIS bit (bit #0), preserving all other bits.
+ */
+ pm2_cnt_blk = acpi_os_in8 ((ACPI_IO_ADDRESS) acpi_gbl_FACP->pm2_cnt_blk);
+ pm2_cnt_blk &= 0xFE;
+ acpi_os_out8 ((ACPI_IO_ADDRESS) acpi_gbl_FACP->pm2_cnt_blk, pm2_cnt_blk);
+
+ /* TBD: [Unhandled]: Support 24-bit timers (this algorithm assumes 32-bit) */
+
+ *pm_timer_ticks = timer;
+
+ /*
+ * Re-enable interrupts after coming out of C2/C3.
+ */
+ enable();
+
+ return AE_OK;
+}
+
+
+/****************************************************************************
+ *
+ * FUNCTION: Acpi_hw_enter_cx
+ *
+ * PARAMETERS: Processor_handle - handle of the processor
+ *
+ * RETURN: Status of function
+ *
+ * DESCRIPTION: Invoke the currently active processor Cx handler to put this
+ * processor to sleep.
+ *
+ ****************************************************************************/
+
+ACPI_STATUS
+acpi_hw_enter_cx (
+ ACPI_IO_ADDRESS pblk_address,
+ u32 *pm_timer_ticks)
+{
+
+ if (!acpi_hw_cx_handlers[acpi_hw_active_cx_state]) {
+ return AE_SUPPORT;
+ }
+
+ return (acpi_hw_cx_handlers[acpi_hw_active_cx_state] (pblk_address, pm_timer_ticks));
+}
+
+
+/****************************************************************************
+ *
+ * FUNCTION: Acpi_hw_set_cx
+ *
+ * PARAMETERS: State - value (1-3) of the Cx state to 'make active'
+ *
+ * RETURN: Function status.
+ *
+ * DESCRIPTION: Sets the state to use during calls to Acpi_hw_enter_cx().
+ *
+ ****************************************************************************/
+
+ACPI_STATUS
+acpi_hw_set_cx (
+ u32 cx_state)
+{
+ /*
+ * Supported State?
+ * ----------------
+ */
+ if ((cx_state < 1) || (cx_state > 3)) {
+ return AE_BAD_PARAMETER;
+ }
+
+ if (!acpi_hw_cx_handlers[cx_state]) {
+ return AE_SUPPORT;
+ }
+
+ /*
+ * New Cx State?
+ * -------------
+ * We only care when moving from one state to another...
+ */
+ if (acpi_hw_active_cx_state == cx_state) {
+ return AE_OK;
+ }
+
+ /*
+ * Prepare to Use New State:
+ * -------------------------
+ * If the new Cx_state is C3, the BM_RLD bit must be set to allow
+ * the generation of a bus master requets to cause the processor
+ * in the C3 state to transition to the C0 state.
+ */
+ switch (cx_state)
+ {
+ case 3:
+ acpi_hw_register_access (ACPI_WRITE, ACPI_MTX_LOCK, (s32)BM_RLD, 1);
+ break;
+ }
+
+ /*
+ * Clean up from Old State:
+ * ------------------------
+ * If the old Cx_state was C3, the BM_RLD bit is reset. When the
+ * bit is reset, the generation of a bus master request does not
+ * effect any processor in the C3 state.
+ */
+ switch (acpi_hw_active_cx_state)
+ {
+ case 3:
+ acpi_hw_register_access (ACPI_WRITE, ACPI_MTX_LOCK, (s32)BM_RLD, 0);
+ break;
+ }
+
+ /*
+ * Enable:
+ * -------
+ */
+ acpi_hw_active_cx_state = cx_state;
+
+ return AE_OK;
+}
+
+
+/****************************************************************************
+ *
+ * FUNCTION: Acpi_hw_get_cx_info
+ *
+ * PARAMETERS: Cx_states - Information (latencies) on all Cx states
+ *
+ * RETURN: Status of function
+ *
+ * DESCRIPTION: This function is called both to initialize Cx handling
+ * and retrieve the current Cx information (latency values).
+ *
+ ****************************************************************************/
+
+ACPI_STATUS
+acpi_hw_get_cx_info (
+ u32 cx_states[])
+{
+ u8 SMP_system = FALSE;
+
+
+ if (!cx_states) {
+ return(AE_BAD_PARAMETER);
+ }
+
+ /*
+ * TBD: [Unhandled] need to init SMP_system using info from the MAPIC
+ * table.
+ */
+
+ /*
+ * Set Defaults:
+ * -------------
+ * C0 and C1 support is implied (but what about that PROC_C1 register
+ * in the FADT?!?!). Set C2/C3 to max. latency (not supported until
+ * proven otherwise).
+ */
+ cx_states[0] = 0;
+ cx_states[1] = 0;
+ cx_states[2] = MAX_CX_STATE_LATENCY;
+ cx_states[3] = MAX_CX_STATE_LATENCY;
+
+ /*
+ * C2 Supported?
+ * -------------
+ * We're only supporting C2 when the latency is <= 100 microseconds,
+ * and on SMP systems when P_LVL2_UP (which indicates C2 only on UP)
+ * is not set.
+ */
+ if (acpi_gbl_FACP->plvl2_lat <= 100) {
+ if (!SMP_system) {
+ acpi_hw_cx_handlers[2] = acpi_hw_enter_c2;
+ cx_states[2] = acpi_gbl_FACP->plvl2_lat;
+ }
+
+ else if (!acpi_gbl_FACP->plvl2_up) {
+ acpi_hw_cx_handlers[2] = acpi_hw_enter_c2;
+ cx_states[2] = acpi_gbl_FACP->plvl2_lat;
+ }
+ }
+
+ /*
+ * C3 Supported?
+ * -------------
+ * We're only supporting C3 on UP systems when the latency is
+ * <= 1000 microseconds and that include the ability to disable
+ * Bus Mastering while in C3 (ARB_DIS) but allows Bus Mastering
+ * requests to wake the system from C3 (BM_RLD). Note his method
+ * of maintaining cache coherency (disabling of bus mastering)
+ * cannot be used on SMP systems, and flushing caches (e.g. WBINVD)
+ * is simply too costly (at this time).
+ */
+ if (acpi_gbl_FACP->plvl3_lat <= 1000) {
+ if (!SMP_system && (acpi_gbl_FACP->pm2_cnt_blk &&
+ acpi_gbl_FACP->pm2_cnt_len))
+ {
+ acpi_hw_cx_handlers[3] = acpi_hw_enter_c3;
+ cx_states[3] = acpi_gbl_FACP->plvl3_lat;
+ }
+ }
+
+ return(AE_OK);
+}
+
+
+/****************************************************************************
+ *
+ * FUNCTION: Acpi_hw_get_cx_handler
+ *
+ * PARAMETERS: State - the Cx state
+ * Handler - pointer to location for the returned handler
+ *
+ * RETURN: Status of function
+ *
+ * DESCRIPTION: This function is called to get an installed Cx state handler.
+ *
+ ****************************************************************************/
+
+ACPI_STATUS
+acpi_hw_get_cx_handler (
+ u32 cx_state,
+ ACPI_C_STATE_HANDLER *handler)
+{
+
+ if ((cx_state == 0) || (cx_state >= MAX_CX_STATES) || !handler) {
+ return(AE_BAD_PARAMETER);
+ }
+
+ *handler = acpi_hw_cx_handlers[cx_state];
+
+ return(AE_OK);
+}
+
+
+/****************************************************************************
+ *
+ * FUNCTION: Acpi_hw_set_cx_handler
+ *
+ * PARAMETERS: Cx_state - the Cx state
+ * Handler - new Cx state handler
+ *
+ * RETURN: Status of function
+ *
+ * DESCRIPTION: This function is called to install a new Cx state handler.
+ *
+ ****************************************************************************/
+
+ACPI_STATUS
+acpi_hw_set_cx_handler (
+ u32 cx_state,
+ ACPI_C_STATE_HANDLER handler)
+{
+
+ if ((cx_state == 0) || (cx_state >= MAX_CX_STATES) || !handler) {
+ return(AE_BAD_PARAMETER);
+ }
+
+ acpi_hw_cx_handlers[cx_state] = handler;
+
+ return(AE_OK);
+}
+
+
+/**************************************************************************
+ *
+ * FUNCTION: Acpi_hw_local_pow
+ *
+ * PARAMETERS: x,y operands
+ *
+ * RETURN: result
+ *
+ * DESCRIPTION: Compute x ^ y
+ *
+ *************************************************************************/
+
+NATIVE_UINT
+acpi_hw_local_pow (
+ NATIVE_UINT x,
+ NATIVE_UINT y)
+{
+ NATIVE_UINT i;
+ NATIVE_UINT result = 1;
+
+
+ for (i = 0; i < y; i++) {
+ result = result * x;
+ }
+
+ return result;
+}
+
+
+/**************************************************************************
+ *
+ * FUNCTION: Acpi_hw_enable_throttling
+ *
+ * PARAMETERS: Pblk_address - Address of Pcnt (Processor Control)
+ * register
+ *
+ * RETURN: none
+ *
+ * DESCRIPTION: Enable throttling by setting the THT_EN bit.
+ *
+ *************************************************************************/
+
+void
+acpi_hw_enable_throttling (
+ ACPI_IO_ADDRESS pblk_address)
+{
+ u32 pblk_value;
+
+
+ pblk_value = acpi_os_in32 (pblk_address);
+ pblk_value = pblk_value | BIT_4;
+ acpi_os_out32 (pblk_address, pblk_value);
+
+ return;
+}
+
+
+/**************************************************************************
+ *
+ * FUNCTION: Acpi_hw_disable_throttling
+ *
+ * PARAMETERS: Pblk_address - Address of Pcnt (Processor Control)
+ * register
+ *
+ * RETURN: none
+ *
+ * DESCRIPTION:Disable throttling by clearing the THT_EN bit
+ *
+ *************************************************************************/
+
+void
+acpi_hw_disable_throttling (
+ ACPI_IO_ADDRESS pblk_address)
+{
+ u32 pblk_value;
+
+
+ pblk_value = acpi_os_in32 (pblk_address);
+ pblk_value = pblk_value & (~(u32)BIT_4);
+ acpi_os_out32 (pblk_address, pblk_value);
+
+ return;
+}
+
+
+/**************************************************************************
+ *
+ * FUNCTION: Acpi_hw_get_duty_cycle
+ *
+ * PARAMETERS: Duty_offset Pcnt register duty cycle field offset
+ * Pblk_address Pcnt register address in chipset
+ * Num_throttle_states # of CPU throttle states this system
+ * supports
+ *
+ * RETURN: none
+ *
+ * DESCRIPTION: Get the duty cycle from the chipset
+ *
+ *************************************************************************/
+
+u32
+acpi_hw_get_duty_cycle (
+ u8 duty_offset,
+ ACPI_IO_ADDRESS pblk_address,
+ u32 num_throttle_states)
+{
+ NATIVE_UINT index;
+ u32 duty32_value;
+ u32 pcnt_mask_off_duty_field;
+
+
+ /*
+ * Use Num_throttle_states - 1 as mask [ex. 8 - 1 = 7 (Fh)]
+ * and then shift it into the right position
+ */
+ pcnt_mask_off_duty_field = num_throttle_states - 1;
+
+ /*
+ * Read in the current value from the port
+ */
+ duty32_value = acpi_os_in32 ((ACPI_IO_ADDRESS) pblk_address);
+
+ /*
+ * Shift the the value to LSB
+ */
+ for (index = 0; index < (NATIVE_UINT) duty_offset; index++) {
+ duty32_value = duty32_value >> 1;
+ }
+
+ /*
+ * Get the duty field only
+ */
+ duty32_value = duty32_value & pcnt_mask_off_duty_field;
+
+ return ((u32) duty32_value);
+}
+
+
+/**************************************************************************
+ *
+ * FUNCTION: Acpi_hw_program_duty_cycle
+ *
+ * PARAMETERS: Duty_offset Pcnt register duty cycle field offset
+ * Duty_cycle duty cycle to program into chipset
+ * Pblk_address Pcnt register address in chipset
+ * Num_throttle_states # of CPU throttle states this system
+ * supports
+ *
+ * RETURN: none
+ *
+ * DESCRIPTION: Program chipset with specified duty cycle by bit-shifting the
+ * duty cycle bits to the appropriate offset, reading the duty
+ * cycle register, OR-ing in the duty cycle, and writing it to
+ * the Pcnt register.
+ *
+ *************************************************************************/
+
+void
+acpi_hw_program_duty_cycle (
+ u8 duty_offset,
+ u32 duty_cycle,
+ ACPI_IO_ADDRESS pblk_address,
+ u32 num_throttle_states)
+{
+ NATIVE_UINT index;
+ u32 duty32_value;
+ u32 pcnt_mask_off_duty_field;
+ u32 port_value;
+
+
+ /*
+ * valid Duty_cycle passed
+ */
+ duty32_value = duty_cycle;
+
+ /*
+ * use Num_throttle_states - 1 as mask [ex. 8 - 1 = 7 (Fh)]
+ * and then shift it into the right position
+ */
+ pcnt_mask_off_duty_field = num_throttle_states - 1;
+
+ /*
+ * Shift the mask
+ */
+ for (index = 0; index < (NATIVE_UINT) duty_offset; index++) {
+ pcnt_mask_off_duty_field = pcnt_mask_off_duty_field << 1;
+ duty32_value = duty32_value << 1;
+ }
+
+ /*
+ * Read in the current value from the port
+ */
+ port_value = acpi_os_in32 ((ACPI_IO_ADDRESS) pblk_address);
+
+ /*
+ * Mask off the duty field so we don't OR in junk!
+ */
+ port_value = port_value & (~pcnt_mask_off_duty_field);
+
+ /*
+ * OR in the bits we want to write out to the port
+ */
+ port_value = (port_value | duty32_value) & (~(u32)BIT_4);
+
+ /*
+ * write it to the port
+ */
+ acpi_os_out32 ((ACPI_IO_ADDRESS) pblk_address, port_value);
+
+ return;
+}
+
+
\ No newline at end of file
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)