Commit 4b236b62 authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt Committed by David Gibson
Browse files

ppc: Initial HDEC support



The current behaviour isn't completely right, as for the DEC, we
don't properly re-arm when wrapping around, but I will fix this
in a separate patch.

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>
Signed-off-by: default avatarDavid Gibson <david@gibson.dropbear.id.au>
parent b378bb09
Loading
Loading
Loading
Loading
+12 −5
Original line number Diff line number Diff line
@@ -699,10 +699,19 @@ static inline void cpu_ppc_decr_lower(PowerPCCPU *cpu)

static inline void cpu_ppc_hdecr_excp(PowerPCCPU *cpu)
{
    CPUPPCState *env = &cpu->env;

    /* Raise it */
    LOG_TB("raise decrementer exception\n");
    LOG_TB("raise hv decrementer exception\n");

    /* The architecture specifies that we don't deliver HDEC
     * interrupts in a PM state. Not only they don't cause a
     * wakeup but they also get effectively discarded.
     */
    if (!env->in_pm_state) {
        ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 1);
    }
}

static inline void cpu_ppc_hdecr_lower(PowerPCCPU *cpu)
{
@@ -928,9 +937,7 @@ clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq)
    }
    /* Create new timer */
    tb_env->decr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_ppc_decr_cb, cpu);
    if (0) {
        /* XXX: find a suitable condition to enable the hypervisor decrementer
         */
    if (env->has_hv_mode) {
        tb_env->hdecr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_ppc_hdecr_cb,
                                                cpu);
    } else {
+12 −10
Original line number Diff line number Diff line
@@ -753,7 +753,6 @@ void ppc_cpu_do_interrupt(CPUState *cs)
static void ppc_hw_interrupt(CPUPPCState *env)
{
    PowerPCCPU *cpu = ppc_env_get_cpu(env);
    int hdice;
#if 0
    CPUState *cs = CPU(cpu);

@@ -781,15 +780,13 @@ static void ppc_hw_interrupt(CPUPPCState *env)
        return;
    }
#endif
    if (0) {
        /* XXX: find a suitable condition to enable the hypervisor mode */
        hdice = env->spr[SPR_LPCR] & 1;
    } else {
        hdice = 0;
    }
    if ((msr_ee != 0 || msr_hv == 0 || msr_pr != 0) && hdice != 0) {
    /* Hypervisor decrementer exception */
    if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) {
        /* LPCR will be clear when not supported so this will work */
        bool hdice = !!(env->spr[SPR_LPCR] & LPCR_HDICE);
        if ((msr_ee != 0 || msr_hv == 0) && hdice) {
            /* HDEC clears on delivery */
            env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR);
            powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_HDECR);
            return;
        }
@@ -941,6 +938,11 @@ void helper_pminsn(CPUPPCState *env, powerpc_pm_insn_t insn)
    cs->halted = 1;
    env->in_pm_state = true;

    /* The architecture specifies that HDEC interrupts are
     * discarded in PM states
     */
    env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR);

    /* 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
+2 −0
Original line number Diff line number Diff line
@@ -600,6 +600,8 @@ DEF_HELPER_2(store_601_rtcl, void, env, tl)
DEF_HELPER_2(store_601_rtcu, void, env, tl)
DEF_HELPER_1(load_decr, tl, env)
DEF_HELPER_2(store_decr, void, env, tl)
DEF_HELPER_1(load_hdecr, tl, env)
DEF_HELPER_2(store_hdecr, void, env, tl)
DEF_HELPER_2(store_hid0_601, void, env, tl)
DEF_HELPER_3(store_403_pbr, void, env, i32, tl)
DEF_HELPER_1(load_40x_pit, tl, env)
+10 −0
Original line number Diff line number Diff line
@@ -102,6 +102,16 @@ void helper_store_decr(CPUPPCState *env, target_ulong val)
    cpu_ppc_store_decr(env, val);
}

target_ulong helper_load_hdecr(CPUPPCState *env)
{
    return cpu_ppc_load_hdecr(env);
}

void helper_store_hdecr(CPUPPCState *env, target_ulong val)
{
    cpu_ppc_store_hdecr(env, val);
}

target_ulong helper_load_40x_pit(CPUPPCState *env)
{
    return load_40x_pit(env);
+30 −0
Original line number Diff line number Diff line
@@ -277,6 +277,32 @@ static void spr_read_purr (DisasContext *ctx, int gprn, int sprn)
{
    gen_helper_load_purr(cpu_gpr[gprn], cpu_env);
}

/* HDECR */
static void spr_read_hdecr(DisasContext *ctx, int gprn, int sprn)
{
    if (ctx->tb->cflags & CF_USE_ICOUNT) {
        gen_io_start();
    }
    gen_helper_load_hdecr(cpu_gpr[gprn], cpu_env);
    if (ctx->tb->cflags & CF_USE_ICOUNT) {
        gen_io_end();
        gen_stop_exception(ctx);
    }
}

static void spr_write_hdecr(DisasContext *ctx, int sprn, int gprn)
{
    if (ctx->tb->cflags & CF_USE_ICOUNT) {
        gen_io_start();
    }
    gen_helper_store_hdecr(cpu_env, cpu_gpr[gprn]);
    if (ctx->tb->cflags & CF_USE_ICOUNT) {
        gen_io_end();
        gen_stop_exception(ctx);
    }
}

#endif
#endif

@@ -7824,6 +7850,10 @@ static void gen_spr_power5p_lpar(CPUPPCState *env)
                     SPR_NOACCESS, SPR_NOACCESS,
                     &spr_read_generic, &spr_write_lpcr,
                     KVM_REG_PPC_LPCR, LPCR_LPES0 | LPCR_LPES1);
    spr_register_hv(env, SPR_HDEC, "HDEC",
                    SPR_NOACCESS, SPR_NOACCESS,
                    SPR_NOACCESS, SPR_NOACCESS,
                    &spr_read_hdecr, &spr_write_hdecr, 0);
#endif
}