/* * cps_nc_entry_fn - type of a generated non-coherent state entry function * @online: the count of online coupled VPEs * @nc_ready_count: pointer to a non-coherent mapping of the core ready_count * * The code entering & exiting non-coherent states is generated at runtime * using uasm, in order to ensure that the compiler cannot insert a stray * memory access at an unfortunate time and to allow the generation of optimal * core-specific code particularly for cache routines. If coupled_coherence * is non-zero and this is the entry function for the CPS_PM_NC_WAIT state, * returns the number of VPEs that were in the wait state at the point this * VPE left it. Returns garbage if coupled_coherence is zero or this is not * the entry function for CPS_PM_NC_WAIT.
*/ typedefunsigned (*cps_nc_entry_fn)(unsigned online, u32 *nc_ready_count);
/* * The entry point of the generated non-coherent idle state entry/exit * functions. Actually per-core rather than per-CPU.
*/ static DEFINE_PER_CPU_READ_MOSTLY(cps_nc_entry_fn[CPS_PM_STATE_COUNT],
nc_asm_enter);
/* Bitmap indicating which states are supported by the system */ static DECLARE_BITMAP(state_support, CPS_PM_STATE_COUNT);
/* * Indicates the number of coupled VPEs ready to operate in a non-coherent * state. Actually per-core rather than per-CPU.
*/ static DEFINE_PER_CPU_ALIGNED(u32*, ready_count);
/* Indicates online CPUs coupled with the current CPU */ static DEFINE_PER_CPU_ALIGNED(cpumask_t, online_coupled);
/* Used to synchronize entry to deep idle states */ static DEFINE_PER_CPU_ALIGNED(atomic_t, pm_barrier);
/* Saved CPU state across the CPS_PM_POWER_GATED state */
DEFINE_PER_CPU_ALIGNED(struct mips_static_suspend_state, cps_cpu_state);
/* A somewhat arbitrary number of labels & relocs for uasm */ staticstruct uasm_label labels[32]; staticstruct uasm_reloc relocs[32];
staticvoid coupled_barrier(atomic_t *a, unsigned online)
{ /* * This function is effectively the same as * cpuidle_coupled_parallel_barrier, which can't be used here since * there's no cpuidle device.
*/
/* Setup the VPE to run mips_cps_pm_restore when started again */ if (IS_ENABLED(CONFIG_CPU_PM) && state == CPS_PM_POWER_GATED) { /* Power gating relies upon CPS SMP */ if (!mips_cps_smp_in_use()) return -EINVAL;
/* Indicate that this CPU might not be coherent */
cpumask_clear_cpu(cpu, &cpu_coherent_mask);
smp_mb__after_atomic();
/* Create a non-coherent mapping of the core ready_count */
core_ready_count = per_cpu(ready_count, cpu);
nc_addr = kmap_noncoherent(virt_to_page(core_ready_count),
(unsignedlong)core_ready_count);
nc_addr += ((unsignedlong)core_ready_count & ~PAGE_MASK);
nc_core_ready_count = nc_addr;
/* Ensure ready_count is zero-initialised before the assembly runs */
WRITE_ONCE(*nc_core_ready_count, 0);
barrier = &per_cpu(pm_barrier, cpumask_first(&cpu_sibling_map[cpu]));
coupled_barrier(barrier, online);
/* Run the generated entry code */
left = entry(online, nc_core_ready_count);
/* Remove the non-coherent mapping of ready_count */
kunmap_noncoherent();
/* Indicate that this CPU is definitely coherent */
cpumask_set_cpu(cpu, &cpu_coherent_mask);
/* * If this VPE is the first to leave the non-coherent wait state then * it needs to wake up any coupled VPEs still running their wait * instruction so that they return to cpuidle, which can then complete * coordination between the coupled VPEs & provide the governor with * a chance to reflect on the length of time the VPEs were in the * idle state.
*/ if (coupled_coherence && (state == CPS_PM_NC_WAIT) && (left == online))
arch_send_call_function_ipi_mask(coupled_mask);
/* * Determine whether this CPU requires an FSB flush, and if so which * performance counter/event reflect stalls due to a full FSB.
*/ switch (__get_cpu_type(cpu_info->cputype)) { case CPU_INTERAPTIV:
perf_counter = 1;
perf_event = 51; break;
case CPU_PROAPTIV: /* Newer proAptiv cores don't require this workaround */ if (revision >= PRID_REV_ENCODE_332(1, 1, 0)) return 0;
/* On older ones it's unavailable */ return -1;
default: /* Assume that the CPU does not need this workaround */ return 0;
}
/* * Ensure that the fill/store buffer (FSB) is not holding the results * of a prefetch, since if it is then the CPC sequencer may become * stuck in the D3 (ClrBus) state whilst entering a low power state.
*/
/* Base address for loads */
UASM_i_LA(pp, GPR_T0, (long)CKSEG0);
/* Start of clear loop */
uasm_build_label(pl, *pp, lbl);
/* Perform some loads to fill the FSB */ for (i = 0; i < num_loads; i++)
uasm_i_lw(pp, GPR_ZERO, i * line_size * line_stride, GPR_T0);
/* * Invalidate the new D-cache entries so that the cache will need * refilling (via the FSB) if the loop is executed again.
*/ for (i = 0; i < num_loads; i++) {
uasm_i_cache(pp, Hit_Invalidate_D,
i * line_size * line_stride, GPR_T0);
uasm_i_cache(pp, Hit_Writeback_Inv_SD,
i * line_size * line_stride, GPR_T0);
}
if (IS_ENABLED(CONFIG_CPU_PM) && state == CPS_PM_POWER_GATED) { /* Power gating relies upon CPS SMP */ if (!mips_cps_smp_in_use()) goto out_err;
/* * Save CPU state. Note the non-standard calling convention * with the return address placed in v0 to avoid clobbering * the ra register before it is saved.
*/
UASM_i_LA(&p, GPR_T0, (long)mips_cps_pm_save);
uasm_i_jalr(&p, GPR_V0, GPR_T0);
uasm_i_nop(&p);
}
/* * Load addresses of required CM & CPC registers. This is done early * because they're needed in both the enable & disable coherence steps * but in the coupled case the enable step will only run on one VPE.
*/
UASM_i_LA(&p, r_pcohctl, (long)addr_gcr_cl_coherence());
/* Barrier ensuring all CPUs see the updated r_nc_count value */
uasm_i_sync(&p, __SYNC_mb);
/* * If this is the last VPE to become ready for non-coherence * then it should branch below.
*/
uasm_il_beq(&p, &r, GPR_T1, r_online, lbl_disable_coherence);
uasm_i_nop(&p);
if (state < CPS_PM_POWER_GATED) { /* * Otherwise this is not the last VPE to become ready * for non-coherence. It needs to wait until coherence * has been disabled before proceeding, which it will do * by polling for the top bit of ready_count being set.
*/
uasm_i_addiu(&p, GPR_T1, GPR_ZERO, -1);
uasm_build_label(&l, p, lbl_poll_cont);
uasm_i_lw(&p, GPR_T0, 0, r_nc_count);
uasm_il_bltz(&p, &r, GPR_T0, lbl_secondary_cont);
uasm_i_ehb(&p); if (cpu_has_mipsmt)
uasm_i_yield(&p, GPR_ZERO, GPR_T1);
uasm_il_b(&p, &r, lbl_poll_cont);
uasm_i_nop(&p);
} else { /* * The core will lose power & this VPE will not continue * so it can simply halt here.
*/ if (cpu_has_mipsmt) { /* Halt the VPE via C0 tchalt register */
uasm_i_addiu(&p, GPR_T0, GPR_ZERO, TCHALT_H);
uasm_i_mtc0(&p, GPR_T0, 2, 4);
} elseif (cpu_has_vp) { /* Halt the VP via the CPC VP_STOP register */ unsignedint vpe_id;
/* * This is the point of no return - this VPE will now proceed to * disable coherence. At this point we *must* be sure that no other * VPE within the core will interfere with the L1 dcache.
*/
uasm_build_label(&l, p, lbl_disable_coherence);
if (mips_cm_revision() < CM_REV_CM3) { /* * Disable all but self interventions. The load from COHCTL is * defined by the interAptiv & proAptiv SUMs as ensuring that the * operation resulting from the preceding store is complete.
*/
uasm_i_addiu(&p, GPR_T0, GPR_ZERO, 1 << cpu_core(&cpu_data[cpu]));
uasm_i_sw(&p, GPR_T0, 0, r_pcohctl);
uasm_i_lw(&p, GPR_T0, 0, r_pcohctl);
/* Barrier to ensure write to coherence control is complete */
uasm_i_sync(&p, __SYNC_full);
uasm_i_ehb(&p);
}
if (state == CPS_PM_POWER_GATED) { /* If anything goes wrong just hang */
uasm_build_label(&l, p, lbl_hang);
uasm_il_b(&p, &r, lbl_hang);
uasm_i_nop(&p);
/* * There's no point generating more code, the core is * powered down & if powered back up will run from the * reset vector not from here.
*/ goto gen_done;
}
/* Barrier to ensure write to CPC command is complete */
uasm_i_sync(&p, __SYNC_full);
uasm_i_ehb(&p);
}
if (state == CPS_PM_NC_WAIT) { /* * At this point it is safe for all VPEs to proceed with * execution. This VPE will set the top bit of ready_count * to indicate to the other VPEs that they may continue.
*/ if (coupled_coherence)
cps_gen_set_top_bit(&p, &l, &r, r_nc_count,
lbl_set_cont);
/* * VPEs which did not disable coherence will continue * executing, after coherence has been disabled, from this * point.
*/
uasm_build_label(&l, p, lbl_secondary_cont);
/* Now perform our wait */
uasm_i_wait(&p, 0);
}
/* * Re-enable coherence. Note that for CPS_PM_NC_WAIT all coupled VPEs * will run this. The first will actually re-enable coherence & the * rest will just be performing a rather unusual nop.
*/
uasm_i_addiu(&p, GPR_T0, GPR_ZERO, mips_cm_revision() < CM_REV_CM3
? CM_GCR_Cx_COHERENCE_COHDOMAINEN
: CM3_GCR_Cx_COHERENCE_COHEN);
/* Barrier ensuring all CPUs see the updated r_nc_count value */
uasm_i_sync(&p, __SYNC_mb);
}
if (coupled_coherence && (state == CPS_PM_CLOCK_GATED)) { /* * At this point it is safe for all VPEs to proceed with * execution. This VPE will set the top bit of ready_count * to indicate to the other VPEs that they may continue.
*/
cps_gen_set_top_bit(&p, &l, &r, r_nc_count, lbl_set_cont);
/* * This core will be reliant upon another core sending a * power-up command to the CPC in order to resume operation. * Thus an arbitrary VPE can't trigger the core leaving the * idle state and the one that disables coherence might as well * be the one to re-enable it. The rest will continue from here * after that has been done.
*/
uasm_build_label(&l, p, lbl_secondary_cont);
/* Barrier ensuring all CPUs see the updated r_nc_count value */
uasm_i_sync(&p, __SYNC_mb);
}
/* The core is coherent, time to return to C code */
uasm_i_jr(&p, GPR_RA);
uasm_i_nop(&p);
gen_done: /* Ensure the code didn't exceed the resources allocated for it */
BUG_ON((p - buf) > max_instrs);
BUG_ON((l - labels) > ARRAY_SIZE(labels));
BUG_ON((r - relocs) > ARRAY_SIZE(relocs));
for (state = CPS_PM_NC_WAIT; state < CPS_PM_STATE_COUNT; state++) { if (per_cpu(nc_asm_enter, cpu)[state]) continue; if (!test_bit(state, state_support)) continue;
entry_fn = cps_gen_entry_code(cpu, state); if (!entry_fn) {
pr_err("Failed to generate core %u state %u entry\n",
core, state);
clear_bit(state, state_support);
}
switch (event) { case PM_SUSPEND_PREPARE:
stat = read_cpc_cl_stat_conf(); /* * If we're attempting to suspend the system and power down all * of the cores, the JTAG detect bit indicates that the CPC will * instead put the cores into clock-off state. In this state * a connected debugger can cause the CPU to attempt * interactions with the powered down system. At best this will * fail. At worst, it can hang the NoC, requiring a hard reset. * To avoid this, just block system suspend if a JTAG probe * is detected.
*/ if (stat & CPC_Cx_STAT_CONF_EJTAG_PROBE) {
pr_warn("JTAG probe is connected - abort suspend\n"); return NOTIFY_BAD;
} return NOTIFY_DONE; default: return NOTIFY_DONE;
}
}
staticint __init cps_pm_init(void)
{ /* A CM is required for all non-coherent states */ if (!mips_cm_present()) {
pr_warn("pm-cps: no CM, non-coherent states unavailable\n"); return 0;
}
/* * If interrupts were enabled whilst running a wait instruction on a * non-coherent core then the VPE may end up processing interrupts * whilst non-coherent. That would be bad.
*/ if (cpu_wait == r4k_wait_irqoff)
set_bit(CPS_PM_NC_WAIT, state_support); else
pr_warn("pm-cps: non-coherent wait unavailable\n");
/* Detect whether a CPC is present */ if (mips_cpc_present()) { /* Detect whether clock gating is implemented */ if (read_cpc_cl_stat_conf() & CPC_Cx_STAT_CONF_CLKGAT_IMPL)
set_bit(CPS_PM_CLOCK_GATED, state_support); else
pr_warn("pm-cps: CPC does not support clock gating\n");
/* Power gating is available with CPS SMP & any CPC */ if (mips_cps_smp_in_use())
set_bit(CPS_PM_POWER_GATED, state_support); else
pr_warn("pm-cps: CPS SMP not in use, power gating unavailable\n");
} else {
pr_warn("pm-cps: no CPC, clock & power gating unavailable\n");
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.