Commit 7778a575 authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt Committed by David Gibson
Browse files

ppc: Add P7/P8 Power Management instructions



This adds the ISA 2.06 and later power management instructions
(doze, nap, sleep and rvwinkle) and associated wakeup cause testing
in LPCR

Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
[clg: fixed checkpatch.pl errors ]
Signed-off-by: default avatarCédric Le Goater <clg@kaod.org>
Reviewed-by: default avatarDavid Gibson <david@gibson.dropbear.id.au>
Signed-off-by: default avatarDavid Gibson <david@gibson.dropbear.id.au>
parent b9971cc5
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -125,6 +125,15 @@ enum powerpc_excp_t {
    POWERPC_EXCP_POWER8,
};

/*****************************************************************************/
/* PM instructions */
typedef enum {
    PPC_PM_DOZE,
    PPC_PM_NAP,
    PPC_PM_SLEEP,
    PPC_PM_RVWINKLE,
} powerpc_pm_insn_t;

/*****************************************************************************/
/* Input pins model                                                          */
typedef enum powerpc_input_t powerpc_input_t;
+16 −1
Original line number Diff line number Diff line
@@ -383,6 +383,14 @@ struct ppc_slb_t {
#define LPCR_LPES1        (1ull << (63 - 61))
#define LPCR_AIL_SHIFT    (63 - 40)      /* Alternate interrupt location */
#define LPCR_AIL          (3ull << LPCR_AIL_SHIFT)
#define LPCR_P7_PECE0     (1ull << (63 - 49))
#define LPCR_P7_PECE1     (1ull << (63 - 50))
#define LPCR_P7_PECE2     (1ull << (63 - 51))
#define LPCR_P8_PECE0     (1ull << (63 - 47))
#define LPCR_P8_PECE1     (1ull << (63 - 48))
#define LPCR_P8_PECE2     (1ull << (63 - 49))
#define LPCR_P8_PECE3     (1ull << (63 - 50))
#define LPCR_P8_PECE4     (1ull << (63 - 51))

#define msr_sf   ((env->msr >> MSR_SF)   & 1)
#define msr_isf  ((env->msr >> MSR_ISF)  & 1)
@@ -1059,6 +1067,11 @@ struct CPUPPCState {
     * instructions and SPRs are diallowed if MSR:HV is 0
     */
    bool has_hv_mode;
    /* On P7/P8, set when in PM state, we need to handle resume
     * in a special way (such as routing some resume causes to
     * 0x100), so flag this here.
     */
    bool in_pm_state;
#endif

    /* Those resources are used only during code translation */
@@ -2068,6 +2081,8 @@ enum {
    PPC2_FP_CVT_S64    = 0x0000000000010000ULL,
    /* Transactional Memory (ISA 2.07, Book II)                              */
    PPC2_TM            = 0x0000000000020000ULL,
    /* Server PM instructgions (ISA 2.06, Book III)                          */
    PPC2_PM_ISA206     = 0x0000000000040000ULL,

#define PPC_TCG_INSNS2 (PPC2_BOOKE206 | PPC2_VSX | PPC2_PRCNTL | PPC2_DBRX | \
                        PPC2_ISA205 | PPC2_VSX207 | PPC2_PERM_ISA206 | \
@@ -2075,7 +2090,7 @@ enum {
                        PPC2_FP_CVT_ISA206 | PPC2_FP_TST_ISA206 | \
                        PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | \
                        PPC2_ALTIVEC_207 | PPC2_ISA207S | PPC2_DFP | \
                        PPC2_FP_CVT_S64 | PPC2_TM)
                        PPC2_FP_CVT_S64 | PPC2_TM | PPC2_PM_ISA206)
};

/*****************************************************************************/
+59 −0
Original line number Diff line number Diff line
@@ -101,6 +101,44 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
    asrr0 = -1;
    asrr1 = -1;

    /* check for special resume at 0x100 from doze/nap/sleep/winkle on P7/P8 */
    if (env->in_pm_state) {
        env->in_pm_state = false;

        /* Pretend to be returning from doze always as we don't lose state */
        msr |= (0x1ull << (63 - 47));

        /* Non-machine check are routed to 0x100 with a wakeup cause
         * encoded in SRR1
         */
        if (excp != POWERPC_EXCP_MCHECK) {
            switch (excp) {
            case POWERPC_EXCP_RESET:
                msr |= 0x4ull << (63 - 45);
                break;
            case POWERPC_EXCP_EXTERNAL:
                msr |= 0x8ull << (63 - 45);
                break;
            case POWERPC_EXCP_DECR:
                msr |= 0x6ull << (63 - 45);
                break;
            case POWERPC_EXCP_SDOOR:
                msr |= 0x5ull << (63 - 45);
                break;
            case POWERPC_EXCP_SDOOR_HV:
                msr |= 0x3ull << (63 - 45);
                break;
            case POWERPC_EXCP_HV_MAINT:
                msr |= 0xaull << (63 - 45);
                break;
            default:
                cpu_abort(cs, "Unsupported exception %d in Power Save mode\n",
                          excp);
            }
            excp = POWERPC_EXCP_RESET;
        }
    }

    /* Exception targetting modifiers
     *
     * LPES0 is supported on POWER7/8
@@ -897,6 +935,27 @@ void helper_store_msr(CPUPPCState *env, target_ulong val)
    }
}

#if defined(TARGET_PPC64)
void helper_pminsn(CPUPPCState *env, powerpc_pm_insn_t insn)
{
    CPUState *cs;

    cs = CPU(ppc_env_get_cpu(env));
    cs->halted = 1;
    env->in_pm_state = true;

    /* Technically, nap doesn't set EE, but if we don't set it
     * then ppc_hw_interrupt() won't deliver. We could add some
     * other tests there based on LPCR but it's simpler to just
     * whack EE in. It will be cleared by the 0x100 at wakeup
     * anyway. It will still be observable by the guest in SRR1
     * but this doesn't seem to be a problem.
     */
    env->msr |= (1ull << MSR_EE);
    helper_raise_exception(env, EXCP_HLT);
}
#endif /* defined(TARGET_PPC64) */

static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr)
{
    CPUState *cs = CPU(ppc_env_get_cpu(env));
+1 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ DEF_HELPER_1(rfci, void, env)
DEF_HELPER_1(rfdi, void, env)
DEF_HELPER_1(rfmci, void, env)
#if defined(TARGET_PPC64)
DEF_HELPER_2(pminsn, void, env, i32)
DEF_HELPER_1(rfid, void, env)
DEF_HELPER_1(hrfid, void, env)
#endif
+66 −0
Original line number Diff line number Diff line
@@ -3603,6 +3603,68 @@ static void gen_wait(DisasContext *ctx)
    gen_exception_err(ctx, EXCP_HLT, 1);
}

#if defined(TARGET_PPC64)
static void gen_doze(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
    GEN_PRIV;
#else
    TCGv_i32 t;

    CHK_HV;
    t = tcg_const_i32(PPC_PM_DOZE);
    gen_helper_pminsn(cpu_env, t);
    tcg_temp_free_i32(t);
    gen_stop_exception(ctx);
#endif /* defined(CONFIG_USER_ONLY) */
}

static void gen_nap(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
    GEN_PRIV;
#else
    TCGv_i32 t;

    CHK_HV;
    t = tcg_const_i32(PPC_PM_NAP);
    gen_helper_pminsn(cpu_env, t);
    tcg_temp_free_i32(t);
    gen_stop_exception(ctx);
#endif /* defined(CONFIG_USER_ONLY) */
}

static void gen_sleep(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
    GEN_PRIV;
#else
    TCGv_i32 t;

    CHK_HV;
    t = tcg_const_i32(PPC_PM_SLEEP);
    gen_helper_pminsn(cpu_env, t);
    tcg_temp_free_i32(t);
    gen_stop_exception(ctx);
#endif /* defined(CONFIG_USER_ONLY) */
}

static void gen_rvwinkle(DisasContext *ctx)
{
#if defined(CONFIG_USER_ONLY)
    GEN_PRIV;
#else
    TCGv_i32 t;

    CHK_HV;
    t = tcg_const_i32(PPC_PM_RVWINKLE);
    gen_helper_pminsn(cpu_env, t);
    tcg_temp_free_i32(t);
    gen_stop_exception(ctx);
#endif /* defined(CONFIG_USER_ONLY) */
}
#endif /* #if defined(TARGET_PPC64) */

/***                         Floating-point load                           ***/
#define GEN_LDF(name, ldop, opc, type)                                        \
static void glue(gen_, name)(DisasContext *ctx)                                       \
@@ -9911,6 +9973,10 @@ GEN_HANDLER(mcrf, 0x13, 0x00, 0xFF, 0x00000001, PPC_INTEGER),
GEN_HANDLER(rfi, 0x13, 0x12, 0x01, 0x03FF8001, PPC_FLOW),
#if defined(TARGET_PPC64)
GEN_HANDLER(rfid, 0x13, 0x12, 0x00, 0x03FF8001, PPC_64B),
GEN_HANDLER_E(doze, 0x13, 0x12, 0x0c, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
GEN_HANDLER_E(nap, 0x13, 0x12, 0x0d, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
GEN_HANDLER_E(sleep, 0x13, 0x12, 0x0e, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
GEN_HANDLER_E(rvwinkle, 0x13, 0x12, 0x0f, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
GEN_HANDLER(hrfid, 0x13, 0x12, 0x08, 0x03FF8001, PPC_64H),
#endif
GEN_HANDLER(sc, 0x11, 0xFF, 0xFF, 0x03FFF01D, PPC_FLOW),
Loading