aboutsummaryrefslogtreecommitdiffstats
path: root/roms/opensbi/lib/sbi/sbi_ipi.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/opensbi/lib/sbi/sbi_ipi.c')
-rw-r--r--roms/opensbi/lib/sbi/sbi_ipi.c254
1 files changed, 254 insertions, 0 deletions
diff --git a/roms/opensbi/lib/sbi/sbi_ipi.c b/roms/opensbi/lib/sbi/sbi_ipi.c
new file mode 100644
index 000000000..43478328b
--- /dev/null
+++ b/roms/opensbi/lib/sbi/sbi_ipi.c
@@ -0,0 +1,254 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * Anup Patel <anup.patel@wdc.com>
+ * Nick Kossifidis <mick@ics.forth.gr>
+ */
+
+#include <sbi/riscv_asm.h>
+#include <sbi/riscv_atomic.h>
+#include <sbi/riscv_barrier.h>
+#include <sbi/sbi_bitops.h>
+#include <sbi/sbi_domain.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_hsm.h>
+#include <sbi/sbi_init.h>
+#include <sbi/sbi_ipi.h>
+#include <sbi/sbi_platform.h>
+
+struct sbi_ipi_data {
+ unsigned long ipi_type;
+};
+
+static unsigned long ipi_data_off;
+
+static const struct sbi_ipi_event_ops *ipi_ops_array[SBI_IPI_EVENT_MAX];
+
+static int sbi_ipi_send(struct sbi_scratch *scratch, u32 remote_hartid,
+ u32 event, void *data)
+{
+ int ret;
+ struct sbi_scratch *remote_scratch = NULL;
+ const struct sbi_platform *plat = sbi_platform_ptr(scratch);
+ struct sbi_ipi_data *ipi_data;
+ const struct sbi_ipi_event_ops *ipi_ops;
+
+ if ((SBI_IPI_EVENT_MAX <= event) ||
+ !ipi_ops_array[event])
+ return SBI_EINVAL;
+ ipi_ops = ipi_ops_array[event];
+
+ remote_scratch = sbi_hartid_to_scratch(remote_hartid);
+ if (!remote_scratch)
+ return SBI_EINVAL;
+
+ ipi_data = sbi_scratch_offset_ptr(remote_scratch, ipi_data_off);
+
+ if (ipi_ops->update) {
+ ret = ipi_ops->update(scratch, remote_scratch,
+ remote_hartid, data);
+ if (ret < 0)
+ return ret;
+ }
+
+ /*
+ * Set IPI type on remote hart's scratch area and
+ * trigger the interrupt
+ */
+ atomic_raw_set_bit(event, &ipi_data->ipi_type);
+ smp_wmb();
+ sbi_platform_ipi_send(plat, remote_hartid);
+
+ if (ipi_ops->sync)
+ ipi_ops->sync(scratch);
+
+ return 0;
+}
+
+/**
+ * As this this function only handlers scalar values of hart mask, it must be
+ * set to all online harts if the intention is to send IPIs to all the harts.
+ * If hmask is zero, no IPIs will be sent.
+ */
+int sbi_ipi_send_many(ulong hmask, ulong hbase, u32 event, void *data)
+{
+ int rc;
+ ulong i, m;
+ struct sbi_domain *dom = sbi_domain_thishart_ptr();
+ struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
+
+ if (hbase != -1UL) {
+ rc = sbi_hsm_hart_started_mask(dom, hbase, &m);
+ if (rc)
+ return rc;
+ m &= hmask;
+
+ /* Send IPIs */
+ for (i = hbase; m; i++, m >>= 1) {
+ if (m & 1UL)
+ sbi_ipi_send(scratch, i, event, data);
+ }
+ } else {
+ hbase = 0;
+ while (!sbi_hsm_hart_started_mask(dom, hbase, &m)) {
+ /* Send IPIs */
+ for (i = hbase; m; i++, m >>= 1) {
+ if (m & 1UL)
+ sbi_ipi_send(scratch, i, event, data);
+ }
+ hbase += BITS_PER_LONG;
+ }
+ }
+
+ return 0;
+}
+
+int sbi_ipi_event_create(const struct sbi_ipi_event_ops *ops)
+{
+ int i, ret = SBI_ENOSPC;
+
+ if (!ops || !ops->process)
+ return SBI_EINVAL;
+
+ for (i = 0; i < SBI_IPI_EVENT_MAX; i++) {
+ if (!ipi_ops_array[i]) {
+ ret = i;
+ ipi_ops_array[i] = ops;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+void sbi_ipi_event_destroy(u32 event)
+{
+ if (SBI_IPI_EVENT_MAX <= event)
+ return;
+
+ ipi_ops_array[event] = NULL;
+}
+
+static void sbi_ipi_process_smode(struct sbi_scratch *scratch)
+{
+ csr_set(CSR_MIP, MIP_SSIP);
+}
+
+static struct sbi_ipi_event_ops ipi_smode_ops = {
+ .name = "IPI_SMODE",
+ .process = sbi_ipi_process_smode,
+};
+
+static u32 ipi_smode_event = SBI_IPI_EVENT_MAX;
+
+int sbi_ipi_send_smode(ulong hmask, ulong hbase)
+{
+ return sbi_ipi_send_many(hmask, hbase, ipi_smode_event, NULL);
+}
+
+void sbi_ipi_clear_smode(void)
+{
+ csr_clear(CSR_MIP, MIP_SSIP);
+}
+
+static void sbi_ipi_process_halt(struct sbi_scratch *scratch)
+{
+ sbi_hsm_hart_stop(scratch, TRUE);
+}
+
+static struct sbi_ipi_event_ops ipi_halt_ops = {
+ .name = "IPI_HALT",
+ .process = sbi_ipi_process_halt,
+};
+
+static u32 ipi_halt_event = SBI_IPI_EVENT_MAX;
+
+int sbi_ipi_send_halt(ulong hmask, ulong hbase)
+{
+ return sbi_ipi_send_many(hmask, hbase, ipi_halt_event, NULL);
+}
+
+void sbi_ipi_process(void)
+{
+ unsigned long ipi_type;
+ unsigned int ipi_event;
+ const struct sbi_ipi_event_ops *ipi_ops;
+ struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
+ const struct sbi_platform *plat = sbi_platform_ptr(scratch);
+ struct sbi_ipi_data *ipi_data =
+ sbi_scratch_offset_ptr(scratch, ipi_data_off);
+
+ u32 hartid = current_hartid();
+ sbi_platform_ipi_clear(plat, hartid);
+
+ ipi_type = atomic_raw_xchg_ulong(&ipi_data->ipi_type, 0);
+ ipi_event = 0;
+ while (ipi_type) {
+ if (!(ipi_type & 1UL))
+ goto skip;
+
+ ipi_ops = ipi_ops_array[ipi_event];
+ if (ipi_ops && ipi_ops->process)
+ ipi_ops->process(scratch);
+
+skip:
+ ipi_type = ipi_type >> 1;
+ ipi_event++;
+ };
+}
+
+int sbi_ipi_init(struct sbi_scratch *scratch, bool cold_boot)
+{
+ int ret;
+ struct sbi_ipi_data *ipi_data;
+
+ if (cold_boot) {
+ ipi_data_off = sbi_scratch_alloc_offset(sizeof(*ipi_data),
+ "IPI_DATA");
+ if (!ipi_data_off)
+ return SBI_ENOMEM;
+ ret = sbi_ipi_event_create(&ipi_smode_ops);
+ if (ret < 0)
+ return ret;
+ ipi_smode_event = ret;
+ ret = sbi_ipi_event_create(&ipi_halt_ops);
+ if (ret < 0)
+ return ret;
+ ipi_halt_event = ret;
+ } else {
+ if (!ipi_data_off)
+ return SBI_ENOMEM;
+ if (SBI_IPI_EVENT_MAX <= ipi_smode_event ||
+ SBI_IPI_EVENT_MAX <= ipi_halt_event)
+ return SBI_ENOSPC;
+ }
+
+ ipi_data = sbi_scratch_offset_ptr(scratch, ipi_data_off);
+ ipi_data->ipi_type = 0x00;
+
+ /* Platform init */
+ ret = sbi_platform_ipi_init(sbi_platform_ptr(scratch), cold_boot);
+ if (ret)
+ return ret;
+
+ /* Enable software interrupts */
+ csr_set(CSR_MIE, MIP_MSIP);
+
+ return 0;
+}
+
+void sbi_ipi_exit(struct sbi_scratch *scratch)
+{
+ /* Disable software interrupts */
+ csr_clear(CSR_MIE, MIP_MSIP);
+
+ /* Process pending IPIs */
+ sbi_ipi_process();
+
+ /* Platform exit */
+ sbi_platform_ipi_exit(sbi_platform_ptr(scratch));
+}