patch-2.4.10 linux/drivers/acpi/ospm/processor/prpower.c

Next file: linux/drivers/acpi/ospm/system/sm.c
Previous file: linux/drivers/acpi/ospm/processor/prperf.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.9/linux/drivers/acpi/ospm/processor/prpower.c linux/drivers/acpi/ospm/processor/prpower.c
@@ -1,7 +1,7 @@
 /*****************************************************************************
  *
  * Module Name: prpower.c
- *   $Revision: 25 $
+ *   $Revision: 30 $
  *
  *****************************************************************************/
 
@@ -27,6 +27,7 @@
 /* TBD: Linux specific */
 #include <linux/sched.h>
 #include <linux/pm.h>
+#include <asm/io.h>
 
 #include <acpi.h>
 #include <bm.h>
@@ -40,10 +41,15 @@
  *                                  Globals
  ****************************************************************************/
 
-extern FADT_DESCRIPTOR_REV2	acpi_fadt;
+extern fadt_descriptor_rev2	acpi_fadt;
 static u32			last_idle_jiffies = 0;
 static PR_CONTEXT		*processor_list[NR_CPUS];
 static void			(*pr_pm_idle_save)(void) = NULL;
+static u8			bm_control = 0;
+
+
+/* Used for PIIX4 errata handling. */
+unsigned short			acpi_piix4_bmisx = 0;
 
 
 /****************************************************************************
@@ -67,7 +73,11 @@
 	PR_CONTEXT		*processor,
 	u32			next_state)
 {
+
+	PROC_NAME("pr_power_activate_state");
+
 	if (!processor) {
+		ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Invalid (NULL) context.\n"));
 		return;
 	}
 
@@ -124,6 +134,8 @@
 	u32			start_ticks, end_ticks, time_elapsed;
 	PR_CONTEXT		*processor = NULL;
 
+	PROC_NAME("pr_power_idle");
+
 	processor = processor_list[smp_processor_id()];
 
 	if (!processor || processor->power.active_state == PR_C0) {
@@ -133,21 +145,6 @@
 	next_state = processor->power.active_state;
 
 	/*
-	 * Log BM Activity:
-	 * ----------------
-	 * Read BM_STS and record its value for later use by C3 policy.
-	 * Note that we save the BM_STS values for the last 32 call to
-	 * this function (cycles).  Also note that we must clear BM_STS
-	 * if set (sticky).
-	 */
-	processor->power.bm_activity <<= 1;
-	if (acpi_hw_register_bit_access(ACPI_READ, ACPI_MTX_DO_NOT_LOCK, BM_STS)) {
-		processor->power.bm_activity |= 1;
-		acpi_hw_register_bit_access(ACPI_WRITE, ACPI_MTX_DO_NOT_LOCK,
-			BM_STS, 1);
-	}
-
-	/*
 	 * Check OS Idleness:
 	 * ------------------
 	 * If the OS has been busy (hasn't called the idle handler in a while)
@@ -165,6 +162,37 @@
 		}
 	}
 
+	disable();
+
+	/*
+	 * Log BM Activity:
+	 * ----------------
+	 * Read BM_STS and record its value for later use by C3 policy.
+	 * (Note that we save the BM_STS values for the last 32 cycles).
+	 */
+	if (bm_control) {
+		processor->power.bm_activity <<= 1;
+		if (acpi_hw_register_bit_access(ACPI_READ, ACPI_MTX_DO_NOT_LOCK, BM_STS)) {
+			processor->power.bm_activity |= 1;
+			acpi_hw_register_bit_access(ACPI_WRITE, ACPI_MTX_DO_NOT_LOCK,
+				BM_STS, 1);
+		}
+		else if (acpi_piix4_bmisx) {
+			/*
+			 * PIIX4 Errata:
+			 * -------------
+			 * This code is a workaround for errata #18 "C3 Power State/
+			 * BMIDE and Type-F DMA Livelock" from the July '01 PIIX4
+			 * specification update.  Note that BM_STS doesn't always
+			 * reflect the true state of bus mastering activity; forcing
+			 * us to manually check the BMIDEA bit of each IDE channel.
+			 */
+			if ((inb_p(acpi_piix4_bmisx + 0x02) & 0x01) ||
+				(inb_p(acpi_piix4_bmisx + 0x0A) & 0x01))
+				processor->power.bm_activity |= 1;
+		}
+	}
+
 	c_state = &(processor->power.state[processor->power.active_state]);
 
 	c_state->utilization++;
@@ -177,21 +205,17 @@
 	switch (processor->power.active_state) {
 
 	case PR_C1:
-		/* See how long we're asleep for */
-		acpi_get_timer(&start_ticks);
 		/* Invoke C1 */
 		enable(); halt();
-		/* Compute time elapsed */
-		acpi_get_timer(&end_ticks);
+		/* no C1 time measurement, so just enter some number of times */
+		time_elapsed = 0xFFFFFFFF;
 		break;
 
 	case PR_C2:
-		/* Interrupts must be disabled during C2 transitions */
-		disable();
 		/* See how long we're asleep for */
 		acpi_get_timer(&start_ticks);
 		/* Invoke C2 */
-		acpi_os_in8(processor->power.p_lvl2);
+		acpi_os_read_port(processor->power.p_lvl2, NULL, 8);
 		/* Dummy op - must do something useless after P_LVL2 read */
 		acpi_hw_register_bit_access(ACPI_READ, ACPI_MTX_DO_NOT_LOCK,
 			BM_STS);
@@ -199,18 +223,17 @@
 		acpi_get_timer(&end_ticks);
 		/* Re-enable interrupts */
 		enable();
+		acpi_get_timer_duration(start_ticks, end_ticks, &time_elapsed);
 		break;
 
 	case PR_C3:
-		/* Interrupts must be disabled during C3 transitions */
-		disable();
 		/* Disable bus master arbitration */
 		acpi_hw_register_bit_access(ACPI_WRITE, ACPI_MTX_DO_NOT_LOCK,
 			ARB_DIS, 1);
 		/* See how long we're asleep for */
 		acpi_get_timer(&start_ticks);
-		/* Invoke C2 */
-		acpi_os_in8(processor->power.p_lvl3);
+		/* Invoke C3 */
+		acpi_os_read_port(processor->power.p_lvl3, NULL, 8);
 		/* Dummy op - must do something useless after P_LVL3 read */
 		acpi_hw_register_bit_access(ACPI_READ, ACPI_MTX_DO_NOT_LOCK,
 			BM_STS);
@@ -221,25 +244,20 @@
 			ARB_DIS, 0);
 		/* Re-enable interrupts */
 		enable();
+		acpi_get_timer_duration(start_ticks, end_ticks, &time_elapsed);
 		break;
 
 	default:
+		ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Attempt to use unsupported power state C%d.\n", processor->power.active_state));
+		enable();
 		break;
 	}
 
 	/*
-	 * Compute the amount of time asleep (in the Cx state).
-	 *
-	 * TBD: Convert time_threshold to PM timer ticks initially to
-	 *      avoid having to do the math (acpi_get_timer_duration).
-	 */
-	acpi_get_timer_duration(start_ticks, end_ticks, &time_elapsed);
-
-	/*
 	 * Promotion?
 	 * ----------
 	 * Track the number of successful sleeps (time asleep is greater
-	 * than time_threshold) and promote when count_threashold is
+	 * than time_threshold) and promote when count_threshold is
 	 * reached.
 	 */
 	if ((c_state->promotion.target_state) && 	
@@ -256,7 +274,7 @@
 			 * by this state's promotion policy, prevents
 			 * promotions from occuring.
 			 */
-			if (!(processor->power.bm_activity &
+			if (bm_control && !(processor->power.bm_activity &
 				c_state->promotion.bm_threshold)) {
 				next_state = c_state->promotion.target_state;
 			}
@@ -287,8 +305,8 @@
 		 * state's promotion policy, causes an immediate demotion
 		 * to occur.
 		 */
-		if (processor->power.bm_activity &
-			c_state->demotion.bm_threshold) {
+		if (bm_control && (processor->power.bm_activity &
+			c_state->demotion.bm_threshold)) {
 			next_state = c_state->demotion.target_state;
 		}
 	}
@@ -335,12 +353,14 @@
  *
  ****************************************************************************/
 
-ACPI_STATUS
+acpi_status
 pr_power_set_default_policy (
 	PR_CONTEXT                 *processor)
 {
+	FUNCTION_TRACE("pr_power_set_default_policy");
+
 	if (!processor) {
-		return(AE_BAD_PARAMETER);
+		return_ACPI_STATUS(AE_BAD_PARAMETER);
 	}
 
 	/*
@@ -367,7 +387,7 @@
 	else {
 		processor->power.active_state =
 			processor->power.default_state = PR_C0;
-		return(AE_OK);
+		return_ACPI_STATUS(AE_OK);
 	}
 
 	/*
@@ -382,7 +402,7 @@
 		 * of transition).  Demote from C2 to C1 anytime we're
 		 * asleep in C2 for less than this time.
 		 */
-		processor->power.state[PR_C1].promotion.count_threshold = 1;
+		processor->power.state[PR_C1].promotion.count_threshold = 10;
 		processor->power.state[PR_C1].promotion.time_threshold =
 			2 * processor->power.state[PR_C2].latency;
 		processor->power.state[PR_C1].promotion.target_state = PR_C2;
@@ -423,7 +443,7 @@
 			PR_C2;
 	}
 
-	return(AE_OK);
+	return_ACPI_STATUS(AE_OK);
 }
 
 /*****************************************************************************
@@ -444,12 +464,14 @@
  *         by different CPUs results in lowest common denominator).
  */
 
-ACPI_STATUS
+acpi_status
 pr_power_add_device (
 	PR_CONTEXT                 *processor)
 {
+	FUNCTION_TRACE("pr_power_add_device");
+
 	if (!processor) {
-		return(AE_BAD_PARAMETER);
+		return_ACPI_STATUS(AE_BAD_PARAMETER);
 	}
 
 	/*
@@ -517,11 +539,8 @@
 #ifdef CONFIG_SMP
 	if (smp_num_cpus == 1) {
 #endif /*CONFIG_SMP*/
-		if ((acpi_fadt.plvl3_lat <= PR_MAX_C3_LATENCY) &&
-			(acpi_fadt.V1_pm2_cnt_blk && acpi_fadt.pm2_cnt_len)) {
-			/* TBD: Resolve issue with C3 and HDD corruption. */
-			processor->power.state[PR_C3].is_valid = FALSE;
-			/* processor->power.state[PR_C3].is_valid = TRUE;*/
+		if ((acpi_fadt.plvl3_lat <= PR_MAX_C3_LATENCY) && bm_control) {
+			processor->power.state[PR_C3].is_valid = TRUE;
 			processor->power.p_lvl3 = processor->pblk.address + 5;
 		}
 #ifdef CONFIG_SMP
@@ -546,7 +565,7 @@
 	 */
 	processor_list[processor->uid] = processor;
 
-	return(AE_OK);
+	return_ACPI_STATUS(AE_OK);
 }
 
 
@@ -562,21 +581,23 @@
  *
  ****************************************************************************/
 
-ACPI_STATUS
+acpi_status
 pr_power_remove_device (
 	PR_CONTEXT              *processor)
 {
-	ACPI_STATUS             status = AE_OK;
+	acpi_status             status = AE_OK;
+
+	FUNCTION_TRACE("pr_power_remove_device");
 
 	if (!processor) {
-		return(AE_BAD_PARAMETER);
+		return_ACPI_STATUS(AE_BAD_PARAMETER);
 	}
 
 	MEMSET(&(processor->power), 0, sizeof(PR_POWER));
 
 	processor_list[processor->uid] = NULL;
 
-	return(status);
+	return_ACPI_STATUS(status);
 }
 
 
@@ -592,16 +613,24 @@
  *
  ****************************************************************************/
 
-ACPI_STATUS
+acpi_status
 pr_power_initialize (void)
 {
 	u32			i = 0;
 
+	FUNCTION_TRACE("pr_power_initialize");
+
 	/* TBD: Linux-specific. */
 	for (i=0; i<NR_CPUS; i++) {
 		processor_list[i] = NULL;
 	}
 
+	ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Max CPUs[%d], this CPU[%d].\n", NR_CPUS, smp_processor_id()));
+
+	/* only use C3 if we can control busmastering */
+	if (acpi_fadt.V1_pm2_cnt_blk && acpi_fadt.pm2_cnt_len)
+		bm_control = 1;
+
 	/*
 	 * Install idle handler.
 	 *
@@ -610,7 +639,7 @@
 	pr_pm_idle_save = pm_idle;
 	pm_idle = pr_power_idle;
 
-	return(AE_OK);
+	return_ACPI_STATUS(AE_OK);
 }
 
 
@@ -626,9 +655,11 @@
  *
  ****************************************************************************/
 
-ACPI_STATUS
+acpi_status
 pr_power_terminate (void)
 {
+	FUNCTION_TRACE("pr_power_terminate");
+
 	/*
 	 * Remove idle handler.
 	 *
@@ -636,5 +667,5 @@
 	 */
 	pm_idle = pr_pm_idle_save;
 
-	return(AE_OK);
+	return_ACPI_STATUS(AE_OK);
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)