On Tue, Feb 25, 2014 at 04:33:46PM +0100, Daniel Lezcano wrote: > On 01/15/2014 02:55 PM, Paul Burton wrote: > >This patch introduces a cpuidle driver implementation for the MIPS > >Coherent Processing System (ie. Coherence Manager, Cluster Power > >Controller). It allows for use of the following idle states: > > > > - Coherent wait. This is the usual MIPS wait instruction. > > > > - Non-coherent wait. In this state a core will disable coherency with > > the rest of the system before running the wait instruction. This > > eliminates coherence interventions which would typically be used to > > keep cores coherent. > > > >These two states lay the groundwork for deeper states to be implemented > >later, since all deeper states require the core to become non-coherent. > > > >Signed-off-by: Paul Burton <paul.burton@xxxxxxxxxx> > >Cc: Rafael J. Wysocki <rjw@xxxxxxxxxxxxx> > >Cc: Daniel Lezcano <daniel.lezcano@xxxxxxxxxx> > >Cc: linux-pm@xxxxxxxxxxxxxxx > >--- > > drivers/cpuidle/Kconfig | 5 + > > drivers/cpuidle/Kconfig.mips | 14 + > > drivers/cpuidle/Makefile | 3 + > > drivers/cpuidle/cpuidle-mips-cps.c | 545 +++++++++++++++++++++++++++++++++++++ > > 4 files changed, 567 insertions(+) > > create mode 100644 drivers/cpuidle/Kconfig.mips > > create mode 100644 drivers/cpuidle/cpuidle-mips-cps.c > > > >diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig > >index b3fb81d..11ff281 100644 > >--- a/drivers/cpuidle/Kconfig > >+++ b/drivers/cpuidle/Kconfig > >@@ -35,6 +35,11 @@ depends on ARM > > source "drivers/cpuidle/Kconfig.arm" > > endmenu > > > >+menu "MIPS CPU Idle Drivers" > >+depends on MIPS > >+source "drivers/cpuidle/Kconfig.mips" > >+endmenu > >+ > > endif > > > > config ARCH_NEEDS_CPU_IDLE_COUPLED > >diff --git a/drivers/cpuidle/Kconfig.mips b/drivers/cpuidle/Kconfig.mips > >new file mode 100644 > >index 0000000..dc96691 > >--- /dev/null > >+++ b/drivers/cpuidle/Kconfig.mips > >@@ -0,0 +1,14 @@ > >+# > >+# MIPS CPU Idle drivers > >+# > >+ > >+config MIPS_CPS_CPUIDLE > >+ bool "Support for MIPS Coherent Processing Systems" > >+ depends on SYS_SUPPORTS_MIPS_CPS && CPU_MIPSR2 && !MIPS_MT_SMTC > >+ select ARCH_NEEDS_CPU_IDLE_COUPLED if MIPS_MT > >+ select MIPS_CM > >+ help > >+ Select this option to enable CPU idle driver for systems based > >+ around the MIPS Coherent Processing System architecture - that > >+ is, those with a Coherence Manager & optionally a Cluster > >+ Power Controller. > >diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile > >index 527be28..693cd95 100644 > >--- a/drivers/cpuidle/Makefile > >+++ b/drivers/cpuidle/Makefile > >@@ -13,3 +13,6 @@ obj-$(CONFIG_ARM_KIRKWOOD_CPUIDLE) += cpuidle-kirkwood.o > > obj-$(CONFIG_ARM_ZYNQ_CPUIDLE) += cpuidle-zynq.o > > obj-$(CONFIG_ARM_U8500_CPUIDLE) += cpuidle-ux500.o > > obj-$(CONFIG_ARM_AT91_CPUIDLE) += cpuidle-at91.o > >+ > >+# MIPS SoC drivers > >+obj-$(CONFIG_MIPS_CPS_CPUIDLE) += cpuidle-mips-cps.o > >diff --git a/drivers/cpuidle/cpuidle-mips-cps.c b/drivers/cpuidle/cpuidle-mips-cps.c > >new file mode 100644 > >index 0000000..a78bfb4 > >--- /dev/null > >+++ b/drivers/cpuidle/cpuidle-mips-cps.c > >@@ -0,0 +1,545 @@ > >+/* > >+ * Copyright (C) 2013 Imagination Technologies > >+ * Author: Paul Burton <paul.burton@xxxxxxxxxx> > >+ * > >+ * This program is free software; you can redistribute it and/or modify it > >+ * under the terms of the GNU General Public License as published by the > >+ * Free Software Foundation; either version 2 of the License, or (at your > >+ * option) any later version. > >+ */ > >+ > >+#include <linux/cpuidle.h> > >+#include <linux/init.h> > >+#include <linux/kconfig.h> > >+#include <linux/slab.h> > >+ > >+#include <asm/cacheflush.h> > >+#include <asm/cacheops.h> > >+#include <asm/idle.h> > >+#include <asm/mips-cm.h> > >+#include <asm/mipsmtregs.h> > >+#include <asm/uasm.h> > > The convention is to not include headers from arch. These headers shouldn't > appear in this driver. > Without accessing those headers I can't really implement anything useful - entering these idle states by their nature involves architecture specifics. Would you rather the bulk of the driver is implemented under arch/mips & the code in drivers/cpuidle simply calls elsewhere to do the real work? > >+/* > >+ * The CM & CPC can only handle coherence & power control on a per-core basis, > >+ * thus in an MT system the VPEs within each core are coupled and can only > >+ * enter or exit states requiring CM or CPC assistance in unison. > >+ */ > >+#ifdef CONFIG_MIPS_MT > >+# define coupled_coherence cpu_has_mipsmt > >+#else > >+# define coupled_coherence 0 > >+#endif > >+ > >+/* > >+ * cps_nc_entry_fn - type of a generated non-coherent state entry function > >+ * @vpe_mask: a bitmap of online coupled VPEs, excluding this one > >+ * @online: the count of online coupled VPEs (weight of vpe_mask + 1) > >+ * > >+ * 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, 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. > >+ */ > >+typedef unsigned (*cps_nc_entry_fn)(unsigned vpe_mask, unsigned online); > >+ > >+/* > >+ * The entry point of the generated non-coherent wait entry/exit function. > >+ * Actually per-core rather than per-CPU. > >+ */ > >+static DEFINE_PER_CPU_READ_MOSTLY(cps_nc_entry_fn, ncwait_asm_enter); > >+ > >+/* > >+ * 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, nc_ready_count); > >+ > >+/* A somewhat arbitrary number of labels & relocs for uasm */ > >+static struct uasm_label labels[32] __initdata; > >+static struct uasm_reloc relocs[32] __initdata; > >+ > >+/* CPU dependant sync types */ > >+static unsigned stype_intervention; > >+static unsigned stype_memory; > >+static unsigned stype_ordering; > >+ > >+enum mips_reg { > >+ zero, at, v0, v1, a0, a1, a2, a3, > >+ t0, t1, t2, t3, t4, t5, t6, t7, > >+ s0, s1, s2, s3, s4, s5, s6, s7, > >+ t8, t9, k0, k1, gp, sp, fp, ra, > >+}; > >+ > >+static int cps_ncwait_enter(struct cpuidle_device *dev, > >+ struct cpuidle_driver *drv, int index) > >+{ > >+ unsigned core = cpu_data[dev->cpu].core; > >+ unsigned online, first_cpu, num_left; > >+ cpumask_var_t coupled_mask, vpe_mask; > >+ > >+ if (!alloc_cpumask_var(&coupled_mask, GFP_KERNEL)) > >+ return -ENOMEM; > >+ > >+ if (!alloc_cpumask_var(&vpe_mask, GFP_KERNEL)) { > >+ free_cpumask_var(coupled_mask); > >+ return -ENOMEM; > >+ } > > You can't do that in this function where the local irqs are disabled. IMO, > if you set CONFIG_CPUMASK_OFFSTACK and CONFIG_DEBUG_ATOMIC_SLEEP, you should > see a kernel warning. Right you are, it should either use GFP_ATOMIC or just not handle the off-stack case (which isn't currently used). > > >+ /* Calculate which coupled CPUs (VPEs) are online */ > >+#ifdef CONFIG_MIPS_MT > >+ cpumask_and(coupled_mask, cpu_online_mask, &dev->coupled_cpus); > >+ first_cpu = cpumask_first(coupled_mask); > >+ online = cpumask_weight(coupled_mask); > >+ cpumask_clear_cpu(dev->cpu, coupled_mask); > >+ cpumask_shift_right(vpe_mask, coupled_mask, > >+ cpumask_first(&dev->coupled_cpus)); > > What is the purpose of this computation ? If you read through the code generated in cps_gen_entry_code, the vpe_mask is used to indicate the VPEs within the current core which are both online & not the VPE currently running the code. > > >+#else > >+ cpumask_clear(coupled_mask); > >+ cpumask_clear(vpe_mask); > >+ first_cpu = dev->cpu; > >+ online = 1; > >+#endif > > first_cpu is not used. Right you are. It's used in further work-in-progress patches but I'll remove it from this one. > > >+ /* > >+ * Run the generated entry code. Note that we assume the number of VPEs > >+ * within this core does not exceed the width in bits of a long. Since > >+ * MVPConf0.PVPE is 4 bits wide this seems like a safe assumption. > >+ */ > >+ num_left = per_cpu(ncwait_asm_enter, core)(vpe_mask->bits[0], online); > >+ > >+ /* > >+ * 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, > > Why is it needed ? Can't the other cpus stay idle ? Not with the current cpuidle code. Please see the end of cpuidle_enter_state_coupled in drivers/cpuidle/coupled.c. The code waits for all coupled CPUs to exit the idle state before any of them proceed. Whilst I suppose it would be possible to modify cpuidle to not require that, it would leave you with inaccurate residence statistics mentioned below. > > > * 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 && (num_left == online)) > >+ arch_send_call_function_ipi_mask(coupled_mask); > > Except there is no choice due to hardware limitations, I don't think this is > valid. By nature when one CPU (VPE) within a core leaves a non-coherent state the rest do too, because as I mentioned coherence is a property of the core not of individual VPEs. It would be possible to leave the other VPEs idle if we didn't differentiate between the coherent & non-coherent wait states, but again not with cpuidle as it is today (due to the waiting for all CPUs at the end of cpuidle_enter_state_coupled). > > >+ free_cpumask_var(vpe_mask); > >+ free_cpumask_var(coupled_mask); > >+ return index; > >+} > >+ > >+static struct cpuidle_driver cps_driver = { > >+ .name = "cpc_cpuidle", > >+ .owner = THIS_MODULE, > >+ .states = { > >+ MIPS_CPUIDLE_WAIT_STATE, > >+ { > >+ .enter = cps_ncwait_enter, > >+ .exit_latency = 200, > >+ .target_residency = 450, > >+ .flags = CPUIDLE_FLAG_TIME_VALID, > >+ .name = "nc-wait", > >+ .desc = "non-coherent MIPS wait", > >+ }, > >+ }, > >+ .state_count = 2, > >+ .safe_state_index = 0, > >+}; > > > > >+static void __init cps_gen_cache_routine(u32 **pp, struct uasm_label **pl, > >+ struct uasm_reloc **pr, > >+ const struct cache_desc *cache, > >+ unsigned op, int lbl) > >+{ > >+ unsigned cache_size = cache->ways << cache->waybit; > >+ unsigned i; > >+ const unsigned unroll_lines = 32; > >+ > >+ /* If the cache isn't present this function has it easy */ > >+ if (cache->flags & MIPS_CACHE_NOT_PRESENT) > >+ return; > >+ > >+ /* Load base address */ > >+ UASM_i_LA(pp, t0, (long)CKSEG0); > >+ > >+ /* Calculate end address */ > >+ if (cache_size < 0x8000) > >+ uasm_i_addiu(pp, t1, t0, cache_size); > >+ else > >+ UASM_i_LA(pp, t1, (long)(CKSEG0 + cache_size)); > >+ > >+ /* Start of cache op loop */ > >+ uasm_build_label(pl, *pp, lbl); > >+ > >+ /* Generate the cache ops */ > >+ for (i = 0; i < unroll_lines; i++) > >+ uasm_i_cache(pp, op, i * cache->linesz, t0); > >+ > >+ /* Update the base address */ > >+ uasm_i_addiu(pp, t0, t0, unroll_lines * cache->linesz); > >+ > >+ /* Loop if we haven't reached the end address yet */ > >+ uasm_il_bne(pp, pr, t0, t1, lbl); > >+ uasm_i_nop(pp); > >+} > >+ > >+static void * __init cps_gen_entry_code(struct cpuidle_device *dev) > >+{ > >+ unsigned core = cpu_data[dev->cpu].core; > >+ struct uasm_label *l = labels; > >+ struct uasm_reloc *r = relocs; > >+ u32 *buf, *p; > >+ const unsigned r_vpemask = a0; > >+ const unsigned r_online = a1; > >+ const unsigned r_pcount = t6; > >+ const unsigned r_pcohctl = t7; > >+ const unsigned max_instrs = 256; > >+ enum { > >+ lbl_incready = 1, > >+ lbl_lastvpe, > >+ lbl_vpehalt_loop, > >+ lbl_vpehalt_poll, > >+ lbl_vpehalt_next, > >+ lbl_disable_coherence, > >+ lbl_invicache, > >+ lbl_flushdcache, > >+ lbl_vpeactivate_loop, > >+ lbl_vpeactivate_next, > >+ lbl_wait, > >+ lbl_decready, > >+ }; > >+ > >+ /* Allocate a buffer to hold the generated code */ > >+ p = buf = kcalloc(max_instrs, sizeof(u32), GFP_KERNEL); > >+ if (!buf) > >+ return NULL; > >+ > >+ /* Clear labels & relocs ready for (re)use */ > >+ memset(labels, 0, sizeof(labels)); > >+ memset(relocs, 0, sizeof(relocs)); > >+ > >+ /* > >+ * Load address of the CM GCR_CL_COHERENCE register. This is done early > >+ * because it's 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()); > >+ > >+ if (coupled_coherence) { > >+ /* Load address of nc_ready_count */ > >+ UASM_i_LA(&p, r_pcount, (long)&per_cpu(nc_ready_count, core)); > >+ > >+ /* Increment nc_ready_count */ > >+ uasm_build_label(&l, p, lbl_incready); > >+ uasm_i_sync(&p, stype_ordering); > >+ uasm_i_ll(&p, t1, 0, r_pcount); > >+ uasm_i_addiu(&p, t2, t1, 1); > >+ uasm_i_sc(&p, t2, 0, r_pcount); > >+ uasm_il_beqz(&p, &r, t2, lbl_incready); > >+ uasm_i_addiu(&p, t1, t1, 1); > >+ > >+ /* > >+ * If this is the last VPE to become ready for non-coherence > >+ * then it should branch below. > >+ */ > >+ uasm_il_beq(&p, &r, t1, r_online, lbl_lastvpe); > >+ uasm_i_nop(&p); > >+ > >+ /* > >+ * Otherwise this is not the last VPE to become ready for > >+ * non-coherence. It needs to wait until coherence has been > >+ * disabled before executing a wait instruction, otherwise it > >+ * may return from wait quickly & re-enable coherence causing > >+ * a race with the VPE disabling coherence. It can't simply > >+ * poll the CPC sequencer for a non-coherent state as that > >+ * would race with any other VPE which may spot the > >+ * non-coherent state, run wait, return quickly & re-enable > >+ * coherence before this VPE ever saw the non-coherent state. > >+ * Instead this VPE will halt its TC such that it ceases to > >+ * execute for the moment. > >+ */ > >+ uasm_i_addiu(&p, t0, zero, TCHALT_H); > >+ uasm_i_mtc0(&p, t0, 2, 4); /* TCHalt */ > >+ > >+ /* instruction_hazard(), to ensure the TC halts */ > >+ UASM_i_LA(&p, t0, (long)p + 12); > >+ uasm_i_jr_hb(&p, t0); > >+ uasm_i_nop(&p); > >+ > >+ /* > >+ * The VPE which disables coherence will then clear the halt > >+ * bit for this VPE's TC once coherence has been disabled and > >+ * it can safely proceed to execute the wait instruction. > >+ */ > >+ uasm_il_b(&p, &r, lbl_wait); > >+ uasm_i_nop(&p); > >+ > >+ /* > >+ * The last VPE to increment nc_ready_count will continue from > >+ * here and must spin until all other VPEs within the core have > >+ * been halted, at which point it can be sure that it is safe > >+ * to disable coherence. > >+ * > >+ * t0: number of VPEs left to handle > >+ * t1: (shifted) mask of online VPEs > >+ * t2: current VPE index > >+ */ > >+ uasm_build_label(&l, p, lbl_lastvpe); > >+ uasm_i_addiu(&p, t0, r_online, -1); > >+ uasm_il_beqz(&p, &r, t0, lbl_disable_coherence); > >+ uasm_i_move(&p, t1, r_vpemask); > >+ uasm_i_move(&p, t2, zero); > >+ > >+ /* > >+ * Now loop through all VPEs within the core checking whether > >+ * they are online & not this VPE, which can be determined by > >+ * checking the vpe_mask argument. If a VPE is offline or is > >+ * this VPE, skip it. > >+ */ > >+ uasm_build_label(&l, p, lbl_vpehalt_loop); > >+ uasm_i_andi(&p, t3, t1, 1); > >+ uasm_il_beqz(&p, &r, t3, lbl_vpehalt_next); > >+ > >+ /* settc(vpe) */ > >+ uasm_i_mfc0(&p, t3, 1, 1); /* VPEControl */ > >+ uasm_i_ins(&p, t3, t2, 0, 8); > >+ uasm_i_mtc0(&p, t3, 1, 1); /* VPEControl */ > >+ uasm_i_ehb(&p); > >+ > >+ /* > >+ * It's very likely that the VPE has already halted itself > >+ * by now, but there's theoretically a chance that it may not > >+ * have. Wait until the VPE's TC is halted. > >+ */ > >+ uasm_build_label(&l, p, lbl_vpehalt_poll); > >+ uasm_i_mftc0(&p, t3, 2, 4); /* TCHalt */ > >+ uasm_il_beqz(&p, &r, t3, lbl_vpehalt_poll); > >+ uasm_i_nop(&p); > >+ > >+ /* Decrement the count of VPEs to be handled */ > >+ uasm_i_addiu(&p, t0, t0, -1); > >+ > >+ /* Proceed to the next VPE, if there is one */ > >+ uasm_build_label(&l, p, lbl_vpehalt_next); > >+ uasm_i_srl(&p, t1, t1, 1); > >+ uasm_il_bnez(&p, &r, t0, lbl_vpehalt_loop); > >+ uasm_i_addiu(&p, t2, t2, 1); > >+ } > >+ > >+ /* > >+ * 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); > >+ > >+ /* Completion barrier */ > >+ uasm_i_sync(&p, stype_memory); > >+ > >+ /* Invalidate the L1 icache */ > >+ cps_gen_cache_routine(&p, &l, &r, &cpu_data[dev->cpu].icache, > >+ Index_Invalidate_I, lbl_invicache); > >+ > >+ /* Writeback & invalidate the L1 dcache */ > >+ cps_gen_cache_routine(&p, &l, &r, &cpu_data[dev->cpu].dcache, > >+ Index_Writeback_Inv_D, lbl_flushdcache); > >+ > >+ /* > >+ * Disable all but self interventions. The load from COHCTL is defined > >+ * by the interAptiv & proAptiv SUMs as ensuring that the operation > >+ * resulting from the preceeding store is complete. > >+ */ > >+ uasm_i_addiu(&p, t0, zero, 1 << cpu_data[dev->cpu].core); > >+ uasm_i_sw(&p, t0, 0, r_pcohctl); > >+ uasm_i_lw(&p, t0, 0, r_pcohctl); > >+ > >+ /* Sync to ensure previous interventions are complete */ > >+ uasm_i_sync(&p, stype_intervention); > >+ > >+ /* Disable coherence */ > >+ uasm_i_sw(&p, zero, 0, r_pcohctl); > >+ uasm_i_lw(&p, t0, 0, r_pcohctl); > >+ > >+ if (coupled_coherence) { > >+ /* > >+ * Now that coherence is disabled it is safe for all VPEs to > >+ * proceed with executing their wait instruction, so this VPE > >+ * will go ahead and clear the halt bit of the TCs associated > >+ * with all other online VPEs within the core. Start by > >+ * initialising variables used throughout the loop, and > >+ * skipping the loop entirely if there are no VPEs to handle. > >+ * > >+ * t0: number of VPEs left to handle > >+ * t1: (shifted) mask of online VPEs > >+ * t2: current VPE index > >+ */ > >+ uasm_i_addiu(&p, t0, r_online, -1); > >+ uasm_il_beqz(&p, &r, t0, lbl_wait); > >+ uasm_i_move(&p, t1, r_vpemask); > >+ uasm_i_move(&p, t2, zero); > >+ > >+ /* > >+ * Now loop through all VPEs within the core checking whether > >+ * they are online & not this VPE, which can be determined by > >+ * checking the vpe_mask argument. If a VPE is offline or is > >+ * this VPE, skip it. > >+ */ > >+ uasm_build_label(&l, p, lbl_vpeactivate_loop); > >+ uasm_i_andi(&p, t3, t1, 1); > >+ uasm_il_beqz(&p, &r, t3, lbl_vpeactivate_next); > >+ > >+ /* settc(vpe) */ > >+ uasm_i_mfc0(&p, t3, 1, 1); /* VPEControl */ > >+ uasm_i_ins(&p, t3, t2, 0, 8); > >+ uasm_i_mtc0(&p, t3, 1, 1); /* VPEControl */ > >+ uasm_i_ehb(&p); > >+ > >+ /* Clear TCHalt */ > >+ uasm_i_mttc0(&p, zero, 2, 4); /* TCHalt */ > >+ > >+ /* Decrement the count of VPEs to be handled */ > >+ uasm_i_addiu(&p, t0, t0, -1); > >+ > >+ /* Proceed to the next VPE, if there is one */ > >+ uasm_build_label(&l, p, lbl_vpeactivate_next); > >+ uasm_i_srl(&p, t1, t1, 1); > >+ uasm_il_bnez(&p, &r, t0, lbl_vpeactivate_loop); > >+ uasm_i_addiu(&p, t2, t2, 1); > >+ } > >+ > >+ /* Now perform our wait */ > >+ uasm_build_label(&l, p, lbl_wait); > >+ uasm_i_wait(&p, 0); > >+ > >+ /* > >+ * Re-enable coherence. Note that 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, t0, zero, CM_GCR_Cx_COHERENCE_COHDOMAINEN_MSK); > >+ uasm_i_sw(&p, t0, 0, r_pcohctl); > >+ uasm_i_lw(&p, t0, 0, r_pcohctl); > >+ > >+ /* Ordering barrier */ > >+ uasm_i_sync(&p, stype_ordering); > >+ > >+ if (coupled_coherence) { > >+ /* Decrement nc_ready_count */ > >+ uasm_build_label(&l, p, lbl_decready); > >+ uasm_i_sync(&p, stype_ordering); > >+ uasm_i_ll(&p, t1, 0, r_pcount); > >+ uasm_i_addiu(&p, t2, t1, -1); > >+ uasm_i_sc(&p, t2, 0, r_pcount); > >+ uasm_il_beqz(&p, &r, t2, lbl_decready); > >+ uasm_i_move(&p, v0, t1); > >+ } > >+ > >+ /* The core is coherent, time to return to C code */ > >+ uasm_i_jr(&p, ra); > >+ uasm_i_nop(&p); > >+ > >+ /* 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)); > >+ > >+ /* Patch branch offsets */ > >+ uasm_resolve_relocs(relocs, labels); > >+ > >+ /* Flush the icache */ > >+ local_flush_icache_range((unsigned long)buf, (unsigned long)p); > >+ > >+ return buf; > >+} > > The two functions above should go to somewhere in arch/mips. Well I guess they can, but they only make any sense in the context of cpuidle which is why I implemented them here. > > >+ > >+static void __init cps_cpuidle_unregister(void) > >+{ > >+ int cpu; > >+ struct cpuidle_device *device; > >+ cps_nc_entry_fn *fn; > >+ > >+ for_each_possible_cpu(cpu) { > >+ device = &per_cpu(cpuidle_dev, cpu); > >+ cpuidle_unregister_device(device); > >+ > >+ /* Free entry code */ > >+ fn = &per_cpu(ncwait_asm_enter, cpu_data[cpu].core); > >+ kfree(*fn); > >+ *fn = NULL; > >+ } > >+ > >+ cpuidle_unregister_driver(&cps_driver); > >+} > >+ > >+static int __init cps_cpuidle_init(void) > >+{ > >+ int err, cpu, core, i; > >+ struct cpuidle_device *device; > >+ void *core_entry; > >+ > >+ /* > >+ * If interrupts were enabled whilst running the wait instruction then > >+ * the VPE may end up processing interrupts whilst non-coherent. > >+ */ > >+ if (cpu_wait != r4k_wait_irqoff) { > >+ pr_warn("cpuidle-cps requires that masked interrupts restart the CPU pipeline following a wait\n"); > >+ return -ENODEV; > >+ } > >+ > >+ /* Detect appropriate sync types for the system */ > >+ switch (current_cpu_data.cputype) { > >+ case CPU_INTERAPTIV: > >+ case CPU_PROAPTIV: > >+ stype_intervention = 0x2; > >+ stype_memory = 0x3; > >+ stype_ordering = 0x10; > >+ break; > >+ > >+ default: > >+ pr_warn("cpuidle-cps using heavyweight sync 0\n"); > >+ } > >+ > >+ /* > >+ * Set the coupled flag on the appropriate states if this system > >+ * requires it. > >+ */ > >+ if (coupled_coherence) > >+ for (i = 1; i < cps_driver.state_count; i++) > >+ cps_driver.states[i].flags |= CPUIDLE_FLAG_COUPLED; > > I am not sure CPUIDLE_FLAG_COUPLED is the solution for this driver. IIUC, > with the IPI above and the wakeup sync with the couple states, this driver > is waking up everybody instead of sleeping as much as possible. > > I would recommend to have a look at a different approach, like the one used > for cpuidle-ux500.c. Ok it looks like that just counts & performs work only on the last online CPU to run the code. As before that could work but only by disregarding any differentiation between coherent & non-coherent wait states at levels above the driver. Paul > > >+ > >+ err = cpuidle_register_driver(&cps_driver); > >+ if (err) { > >+ pr_err("Failed to register CPS cpuidle driver\n"); > >+ return err; > >+ } > >+ > >+ for_each_possible_cpu(cpu) { > >+ core = cpu_data[cpu].core; > >+ device = &per_cpu(cpuidle_dev, cpu); > >+ device->cpu = cpu; > >+#ifdef CONFIG_MIPS_MT > >+ cpumask_copy(&device->coupled_cpus, &cpu_sibling_map[cpu]); > >+#endif > >+ if (!per_cpu(ncwait_asm_enter, core)) { > >+ core_entry = cps_gen_entry_code(device); > >+ if (!core_entry) { > >+ pr_err("Failed to generate core %u entry\n", > >+ core); > >+ err = -ENOMEM; > >+ goto err_out; > >+ } > >+ per_cpu(ncwait_asm_enter, core) = core_entry; > >+ } > >+ > >+ err = cpuidle_register_device(device); > >+ if (err) { > >+ pr_err("Failed to register CPU%d cpuidle device\n", > >+ cpu); > >+ goto err_out; > >+ } > >+ } > >+ > >+ return 0; > >+err_out: > >+ cps_cpuidle_unregister(); > >+ return err; > >+} > >+device_initcall(cps_cpuidle_init); > > > > > -- > <http://www.linaro.org/> Linaro.org │ Open source software for ARM SoCs > > Follow Linaro: <http://www.facebook.com/pages/Linaro> Facebook | > <http://twitter.com/#!/linaroorg> Twitter | > <http://www.linaro.org/linaro-blog/> Blog >