diff options
Diffstat (limited to 'big-little/lib/ipi.c')
-rwxr-xr-x | big-little/lib/ipi.c | 135 |
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; +} |