diff options
authorNicolas Pitre <nicolas.pitre@linaro.org>2012-11-28 20:41:01 -0500
committerNicolas Pitre <nicolas.pitre@linaro.org>2013-05-13 19:03:38 -0400
commit574629e3d0dea3114e27b2c0020e9096729ec211 (patch)
parent51d376320af6317f4acf5c779df68f40243ef03c (diff)
ARM: bL_switcher: wait until inbound is alive before performing a switch
In some cases, a significant delay may be observed between the moment a request for a CPU to come up is made and the moment it is ready to start executing kernel code. This is especially true when a whole cluster has to be powered up which may take in the order of miliseconds. It is therefore a good idea to let the outbound CPU continue to execute code in the mean time, and be notified when the inbound is ready before performing the actual switch. This is achieved by registering a completion block with the appropriate IPI callback, and programming the sending of an IPI by the early assembly code prior to entering the main kernel code. Once the IPI is delivered to the outbound CPU, the completion block is "completed" and the switcher thread is resumed. Signed-off-by: Nicolas Pitre <nico@linaro.org>
1 files changed, 23 insertions, 9 deletions
diff --git a/arch/arm/common/bL_switcher.c b/arch/arm/common/bL_switcher.c
index bead7d0e97c..ac832ad3ce3 100644
--- a/arch/arm/common/bL_switcher.c
+++ b/arch/arm/common/bL_switcher.c
@@ -141,10 +141,11 @@ static unsigned int bL_gic_id[MAX_CPUS_PER_CLUSTER][MAX_NR_CLUSTERS];
static int bL_switch_to(unsigned int new_cluster_id)
unsigned int mpidr, cpuid, clusterid, ob_cluster, ib_cluster, this_cpu;
+ struct completion inbound_alive;
struct tick_device *tdev;
enum clock_event_mode tdev_mode;
long volatile *handshake_ptr;
- int ret;
+ int ipi_nr, ret;
mpidr = read_mpidr();
cpuid = MPIDR_AFFINITY_LEVEL(mpidr, 0);
@@ -157,10 +158,18 @@ static int bL_switch_to(unsigned int new_cluster_id)
pr_debug("before switch: CPU %d in cluster %d\n", cpuid, clusterid);
+ this_cpu = smp_processor_id();
/* Close the gate for our entry vectors */
mcpm_set_entry_vector(cpuid, ob_cluster, NULL);
mcpm_set_entry_vector(cpuid, ib_cluster, NULL);
+ /* Install our "inbound alive" notifier. */
+ init_completion(&inbound_alive);
+ ipi_nr = register_ipi_completion(&inbound_alive, this_cpu);
+ ipi_nr |= ((1 << 16) << bL_gic_id[cpuid][ob_cluster]);
+ mcpm_set_early_poke(cpuid, ib_cluster, gic_get_sgir_physaddr(), ipi_nr);
* Let's wake up the inbound CPU now in case it requires some delay
* to come online, but leave it gated in our entry vector code.
@@ -172,23 +181,28 @@ static int bL_switch_to(unsigned int new_cluster_id)
+ * Raise a SGI on the inbound CPU to make sure it doesn't stall
+ * in a possible WFI, such as in bL_power_down().
+ */
+ gic_send_sgi(bL_gic_id[cpuid][ib_cluster], 0);
+ /*
+ * Wait for the inbound to come up. This allows for other
+ * tasks to be scheduled in the mean time.
+ */
+ wait_for_completion(&inbound_alive);
+ mcpm_set_early_poke(cpuid, ib_cluster, 0, 0);
+ /*
* From this point we are entering the switch critical zone
* and can't sleep/schedule anymore.
- this_cpu = smp_processor_id();
/* redirect GIC's SGIs to our counterpart */
- /*
- * Raise a SGI on the inbound CPU to make sure it doesn't stall
- * in a possible WFI, such as in mcpm_power_down().
- */
- arch_send_wakeup_ipi_mask(cpumask_of(this_cpu));
tdev = tick_get_device(this_cpu);
if (tdev && !cpumask_equal(tdev->evtdev->cpumask, cpumask_of(this_cpu)))
tdev = NULL;