summaryrefslogtreecommitdiff
path: root/big-little/lib/ipi.c
diff options
context:
space:
mode:
Diffstat (limited to 'big-little/lib/ipi.c')
-rwxr-xr-xbig-little/lib/ipi.c135
1 files changed, 135 insertions, 0 deletions
diff --git a/big-little/lib/ipi.c b/big-little/lib/ipi.c
new file mode 100755
index 0000000..936cda8
--- /dev/null
+++ b/big-little/lib/ipi.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2012, ARM Limited. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with
+ * or without modification, are permitted provided that the
+ * following conditions are met:
+ *
+ * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of ARM nor the names of its
+ * contributors may be used to endorse or promote products
+ * derived from this software without specific prior written
+ * permission.
+ */
+
+#include "ipi.h"
+#include "misc.h"
+#include "virt_helpers.h"
+#include "bakery.h"
+#include "hyp_vmmap.h"
+#include "gic_registers.h"
+
+extern void gic_send_ipi(unsigned, unsigned);
+
+/*
+ * Set of flags used by the interrupt handling code
+ * to distinguish between IPIs sent by the big-little
+ * code and the payload software.
+ * TODO: Assumes only one cpu will send an IPI at a
+ * time rather than multiple cpus sending the same
+ * IPI to each other at the same time from within the
+ * HYP mode.
+ */
+static unsigned lock_ipi_check;
+static unsigned hyp_ipi_check[16];
+
+/*
+ * Returns the id of the first IPI that is not pending on
+ * our cpu interface or the first IPI that is pending but
+ * was not generated by us. Returns 16 if no such IPI is
+ * found
+ */
+static unsigned get_free_ipi(void)
+{
+ unsigned shift, cpu_if_bit, cpu_id = read_cpuid(), cluster_id =
+ read_clusterid();
+ int ctr;
+
+ cpu_if_bit = 1 << get_cpuif(cluster_id, cpu_id);
+
+ /* Find the register offset */
+ for (ctr = 3; ctr >= 0; ctr--)
+ /* Check whether IPI<shift> has already been generated by us */
+ for (shift = 0; shift < 4; shift++) {
+ if (read32
+ (GIC_ID_PHY_BASE + GICD_SPENDSGIR +
+ (ctr << 2)) & (cpu_if_bit << (shift << 3)))
+ continue;
+
+ return (ctr << 2) + shift;
+ }
+
+ return MAX_IPI;
+}
+
+/*
+ * Given a mask of destination cpus and what the IPI is supposed
+ * todo, select an unused IPI and send it.
+ */
+unsigned send_hyp_ipi(unsigned cpuif_mask, unsigned type)
+{
+ unsigned rc = TRUE;
+ unsigned ipi_no = 0;
+
+ /*
+ * First choose a non-pending IPI to avoid a clash with the OS.
+ */
+ ipi_no = get_free_ipi();
+ if (ipi_no == MAX_IPI) {
+ rc = FALSE;
+ return rc;
+ }
+
+ /*
+ * For this IPI set the mask in our global variable. We do it, payload software
+ * does not. But, first check whether any earlier IPIs have already been acked
+ */
+ while (TRUE) {
+ spin_lock(&lock_ipi_check);
+ if (hyp_ipi_check[ipi_no] & 0xff) {
+ spin_unlock(&lock_ipi_check);
+ } else {
+ hyp_ipi_check[ipi_no] = (type << 8) | cpuif_mask;
+ dsb();
+ spin_unlock(&lock_ipi_check);
+ break;
+ }
+ };
+
+ /* Send the IPI to the cpu_mask */
+ gic_send_ipi(cpuif_mask, ipi_no);
+
+ return rc;
+}
+
+/*
+ * Given a received IPI, check whether it is a HYP IPI and return
+ * the type if so else 0
+ */
+unsigned get_hyp_ipi(unsigned cpu_if, unsigned ipi_no)
+{
+ unsigned type = 0;
+
+ spin_lock(&lock_ipi_check);
+ /*
+ * If this IPI was sent by the big-little code then our cpu_if bit must have
+ * been set in the ipi_check flag. Reset the bit and indicate that its an
+ * internal IPI.
+ */
+ if (hyp_ipi_check[ipi_no] & (1 << cpu_if)) {
+ type = hyp_ipi_check[ipi_no] >> 8;
+ hyp_ipi_check[ipi_no] &= ~(1 << cpu_if);
+ dsb();
+ }
+ spin_unlock(&lock_ipi_check);
+
+ return type;
+}