From: Mikael Pettersson <mikpe@csd.uu.se>

This patch is a cleanup and correction to perfctr's PPC32 low-level driver.
 This is a prerequisite for the next patch which enables performance
monitor interrupts.

Details from my RELEASE-NOTES entry:

- PPC32: Correct MMCR0 handling for FCECE/TRIGGER.  Read MMCR0 at suspend
  and then freeze the counters.  Move this code from read_counters() to
  suspend().  At resume, reload MMCR0 to unfreeze the counters.  Clean up
  the cstatus checks controlling this behaviour.

Signed-off-by: Mikael Pettersson <mikpe@csd.uu.se>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/drivers/perfctr/ppc.c |   37 ++++++++++++++++++++++++++-----------
 1 files changed, 26 insertions(+), 11 deletions(-)

diff -puN drivers/perfctr/ppc.c~perfctr-ppc32-mmcr0-handling-fixes drivers/perfctr/ppc.c
--- 25/drivers/perfctr/ppc.c~perfctr-ppc32-mmcr0-handling-fixes	Fri Nov 12 16:19:46 2004
+++ 25-akpm/drivers/perfctr/ppc.c	Fri Nov 12 16:19:46 2004
@@ -106,6 +106,18 @@ static inline int is_isuspend_cpu(const 
 static inline void clear_isuspend_cpu(struct perfctr_cpu_state *state) { }
 #endif
 
+/* The ppc driver internally uses cstatus & (1<<30) to record that
+   a context has an asynchronously changing MMCR0. */
+static inline unsigned int perfctr_cstatus_set_mmcr0_quirk(unsigned int cstatus)
+{
+	return cstatus | (1 << 30);
+}
+
+static inline int perfctr_cstatus_has_mmcr0_quirk(unsigned int cstatus)
+{
+	return cstatus & (1 << 30);
+}
+
 /****************************************************************
  *								*
  * Driver procedures.						*
@@ -230,7 +242,7 @@ static inline unsigned int read_pmc(unsi
 	}
 }
 
-static void ppc_read_counters(/*const*/ struct perfctr_cpu_state *state,
+static void ppc_read_counters(struct perfctr_cpu_state *state,
 			      struct perfctr_low_ctrs *ctrs)
 {
 	unsigned int cstatus, nrctrs, i;
@@ -243,12 +255,6 @@ static void ppc_read_counters(/*const*/ 
 		unsigned int pmc = state->pmc[i].map;
 		ctrs->pmc[i] = read_pmc(pmc);
 	}
-	/* handle MMCR0 changes due to FCECE or TRIGGER on 74xx */
-	if (state->cstatus & (1<<30)) {
-		unsigned int mmcr0 = mfspr(SPRN_MMCR0);
-		state->ppc_mmcr[0] = mmcr0;
-		get_cpu_cache()->ppc_mmcr[0] = mmcr0;
-	}
 }
 
 static unsigned int pmc_max_event(unsigned int pmc)
@@ -338,14 +344,15 @@ static int ppc_check_control(struct perf
 
 	/*
 	 * MMCR0[FC] and MMCR0[TRIGGER] may change on 74xx if FCECE or
-	 * TRIGGER is set. To avoid undoing those changes, we must read
-	 * MMCR0 back into state->ppc_mmcr[0] and the cache at suspends.
+	 * TRIGGER is set. At suspends we must read MMCR0 back into
+	 * the state and the cache and then freeze the counters, and
+	 * at resumes we must unfreeze the counters and reload MMCR0.
 	 */
 	switch (pm_type) {
 	case PM_7450:
 	case PM_7400:
 		if (state->ppc_mmcr[0] & (MMCR0_FCECE | MMCR0_TRIGGER))
-			state->cstatus |= (1<<30);
+			state->cstatus = perfctr_cstatus_set_mmcr0_quirk(state->cstatus);
 	default:
 		;
 	}
@@ -445,7 +452,7 @@ static void perfctr_cpu_write_control(co
 	return ppc_write_control(state);
 }
 
-static void perfctr_cpu_read_counters(/*const*/ struct perfctr_cpu_state *state,
+static void perfctr_cpu_read_counters(struct perfctr_cpu_state *state,
 				      struct perfctr_low_ctrs *ctrs)
 {
 	return ppc_read_counters(state, ctrs);
@@ -559,6 +566,12 @@ void perfctr_cpu_suspend(struct perfctr_
 	unsigned int i, cstatus, nractrs;
 	struct perfctr_low_ctrs now;
 
+	if (perfctr_cstatus_has_mmcr0_quirk(state->cstatus)) {
+		unsigned int mmcr0 = mfspr(SPRN_MMCR0);
+		mtspr(SPRN_MMCR0, mmcr0 | MMCR0_FC);
+		get_cpu_cache()->ppc_mmcr[0] = mmcr0 | MMCR0_FC;
+		state->ppc_mmcr[0] = mmcr0;
+	}
 	if (perfctr_cstatus_has_ictrs(state->cstatus))
 		perfctr_cpu_isuspend(state);
 	perfctr_cpu_read_counters(state, &now);
@@ -574,6 +587,8 @@ void perfctr_cpu_resume(struct perfctr_c
 {
 	if (perfctr_cstatus_has_ictrs(state->cstatus))
 	    perfctr_cpu_iresume(state);
+	if (perfctr_cstatus_has_mmcr0_quirk(state->cstatus))
+		get_cpu_cache()->k1.id = 0; /* force reload of MMCR0 */
 	perfctr_cpu_write_control(state);
 	//perfctr_cpu_read_counters(state, &state->start);
 	{
_