From: Wang Dongsheng <[email protected]>

Each core's AltiVec unit may be placed into a power savings mode
by turning off power to the unit. Core hardware will automatically
power down the AltiVec unit after no AltiVec instructions have
executed in N cycles. The AltiVec power-control is triggered by hardware.

Signed-off-by: Wang Dongsheng <[email protected]>

diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
index 5d7d9c2..5c7a7ba 100644
--- a/arch/powerpc/include/asm/reg.h
+++ b/arch/powerpc/include/asm/reg.h
@@ -1053,6 +1053,8 @@
 #define PVR_8560       0x80200000
 #define PVR_VER_E500V1 0x8020
 #define PVR_VER_E500V2 0x8021
+#define PVR_VER_E6500  0x8040
+
 /*
  * For the 8xx processors, all of them report the same PVR family for
  * the PowerPC core. The various versions of these processors must be
diff --git a/arch/powerpc/include/asm/reg_booke.h 
b/arch/powerpc/include/asm/reg_booke.h
index b417de3..c047e08 100644
--- a/arch/powerpc/include/asm/reg_booke.h
+++ b/arch/powerpc/include/asm/reg_booke.h
@@ -170,6 +170,7 @@
 #define SPRN_L2CSR1    0x3FA   /* L2 Data Cache Control and Status Register 1 
*/
 #define SPRN_DCCR      0x3FA   /* Data Cache Cacheability Register */
 #define SPRN_ICCR      0x3FB   /* Instruction Cache Cacheability Register */
+#define SPRN_PWRMGTCR0 0x3FB   /* Power management control register 0 */
 #define SPRN_SVR       0x3FF   /* System Version Register */
 
 /*
@@ -216,6 +217,9 @@
 #define        CCR1_DPC        0x00000100 /* Disable L1 I-Cache/D-Cache parity 
checking */
 #define        CCR1_TCS        0x00000080 /* Timer Clock Select */
 
+/* Bit definitions for PWRMGTCR0. */
+#define PWRMGTCR0_ALTIVEC_IDLE (1 << 22) /* Altivec idle enable */
+
 /* Bit definitions for the MCSR. */
 #define MCSR_MCS       0x80000000 /* Machine Check Summary */
 #define MCSR_IB                0x40000000 /* Instruction PLB Error */
diff --git a/arch/powerpc/platforms/85xx/common.c 
b/arch/powerpc/platforms/85xx/common.c
index d0861a0..dbbbc24 100644
--- a/arch/powerpc/platforms/85xx/common.c
+++ b/arch/powerpc/platforms/85xx/common.c
@@ -7,10 +7,22 @@
  */
 #include <linux/of_platform.h>
 
+#include <asm/time.h>
+
 #include <sysdev/cpm2_pic.h>
 
 #include "mpc85xx.h"
 
+#define MAX_BIT                                64
+
+#define ALTIVEC_COUNT_OFFSET           16
+#define ALTIVEC_IDLE_COUNT_MASK                0x003f0000
+
+/*
+ * FIXME - We don't know the AltiVec application scenarios.
+ */
+#define ALTIVEC_IDLE_TIME      1000 /* 1ms */
+
 static struct of_device_id __initdata mpc85xx_common_ids[] = {
        { .type = "soc", },
        { .compatible = "soc", },
@@ -80,3 +92,63 @@ void __init mpc85xx_cpm2_pic_init(void)
        irq_set_chained_handler(irq, cpm2_cascade);
 }
 #endif
+
+static bool has_pw20_altivec_idle(void)
+{
+       u32 pvr;
+
+       pvr = mfspr(SPRN_PVR);
+
+       /* PW20 & AltiVec idle feature only exists for E6500 */
+       if (PVR_VER(pvr) != PVR_VER_E6500)
+               return false;
+
+       /* Fix erratum, e6500 rev1 does not support PW20 & AltiVec idle */
+       if (PVR_REV(pvr) < 0x20)
+               return false;
+
+       return true;
+}
+
+static unsigned int get_idle_ticks_bit(unsigned int us)
+{
+       unsigned int cycle;
+
+       /*
+        * The time control by TB turn over bit, so we need
+        * to be divided by 2.
+        */
+       cycle = (us / 2) * tb_ticks_per_usec;
+
+       return ilog2(cycle) + 1;
+}
+
+static void setup_altivec_idle(void *unused)
+{
+       u32 altivec_idle, bit;
+
+       if (!has_pw20_altivec_idle())
+               return;
+
+       /* Enable Altivec Idle */
+       altivec_idle = mfspr(SPRN_PWRMGTCR0);
+       altivec_idle |= PWRMGTCR0_ALTIVEC_IDLE;
+
+       /* Set Automatic AltiVec Idle Count */
+       /* clear count */
+       altivec_idle &= ~ALTIVEC_IDLE_COUNT_MASK;
+
+       /* set count */
+       bit = get_idle_ticks_bit(ALTIVEC_IDLE_TIME);
+       altivec_idle |= ((MAX_BIT - bit) << ALTIVEC_COUNT_OFFSET);
+
+       mtspr(SPRN_PWRMGTCR0, altivec_idle);
+}
+
+static int __init setup_idle_hw_governor(void)
+{
+       on_each_cpu(setup_altivec_idle, NULL, 1);
+
+       return 0;
+}
+late_initcall(setup_idle_hw_governor);
-- 
1.8.0


_______________________________________________
Linuxppc-dev mailing list
[email protected]
https://lists.ozlabs.org/listinfo/linuxppc-dev

Reply via email to