summaryrefslogtreecommitdiffstats
path: root/arch/arc/plat-eznps/smp.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arc/plat-eznps/smp.c')
-rw-r--r--arch/arc/plat-eznps/smp.c155
1 files changed, 155 insertions, 0 deletions
diff --git a/arch/arc/plat-eznps/smp.c b/arch/arc/plat-eznps/smp.c
new file mode 100644
index 000000000000..5e901f86e4bd
--- /dev/null
+++ b/arch/arc/plat-eznps/smp.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright(c) 2015 EZchip Technologies.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ */
+
+#include <linux/smp.h>
+#include <linux/of_fdt.h>
+#include <linux/io.h>
+#include <linux/irqdomain.h>
+#include <asm/irq.h>
+#include <plat/ctop.h>
+#include <plat/smp.h>
+#include <plat/mtm.h>
+
+#define NPS_DEFAULT_MSID 0x34
+#define NPS_MTM_CPU_CFG 0x90
+
+static char smp_cpuinfo_buf[128] = {"Extn [EZNPS-SMP]\t: On\n"};
+
+/* Get cpu map from device tree */
+static int __init eznps_get_map(const char *name, struct cpumask *cpumask)
+{
+ unsigned long dt_root = of_get_flat_dt_root();
+ const char *buf;
+
+ buf = of_get_flat_dt_prop(dt_root, name, NULL);
+ if (!buf)
+ return 1;
+
+ cpulist_parse(buf, cpumask);
+
+ return 0;
+}
+
+/* Update board cpu maps */
+static void __init eznps_init_cpumasks(void)
+{
+ struct cpumask cpumask;
+
+ if (eznps_get_map("present-cpus", &cpumask)) {
+ pr_err("Failed to get present-cpus from dtb");
+ return;
+ }
+ init_cpu_present(&cpumask);
+
+ if (eznps_get_map("possible-cpus", &cpumask)) {
+ pr_err("Failed to get possible-cpus from dtb");
+ return;
+ }
+ init_cpu_possible(&cpumask);
+}
+
+static void eznps_init_core(unsigned int cpu)
+{
+ u32 sync_value;
+ struct nps_host_reg_aux_hw_comply hw_comply;
+ struct nps_host_reg_aux_lpc lpc;
+
+ if (NPS_CPU_TO_THREAD_NUM(cpu) != 0)
+ return;
+
+ hw_comply.value = read_aux_reg(CTOP_AUX_HW_COMPLY);
+ hw_comply.me = 1;
+ hw_comply.le = 1;
+ hw_comply.te = 1;
+ write_aux_reg(CTOP_AUX_HW_COMPLY, hw_comply.value);
+
+ /* Enable MMU clock */
+ lpc.mep = 1;
+ write_aux_reg(CTOP_AUX_LPC, lpc.value);
+
+ /* Boot CPU only */
+ if (!cpu) {
+ /* Write to general purpose register in CRG */
+ sync_value = ioread32be(REG_GEN_PURP_0);
+ sync_value |= NPS_CRG_SYNC_BIT;
+ iowrite32be(sync_value, REG_GEN_PURP_0);
+ }
+}
+
+/*
+ * Master kick starting another CPU
+ */
+static void __init eznps_smp_wakeup_cpu(int cpu, unsigned long pc)
+{
+ struct nps_host_reg_mtm_cpu_cfg cpu_cfg;
+
+ if (mtm_enable_thread(cpu) == 0)
+ return;
+
+ /* set PC, dmsid, and start CPU */
+ cpu_cfg.value = (u32)res_service;
+ cpu_cfg.dmsid = NPS_DEFAULT_MSID;
+ cpu_cfg.cs = 1;
+ iowrite32be(cpu_cfg.value, nps_mtm_reg_addr(cpu, NPS_MTM_CPU_CFG));
+}
+
+static void eznps_ipi_send(int cpu)
+{
+ struct global_id gid;
+ struct {
+ union {
+ struct {
+ u32 num:8, cluster:8, core:8, thread:8;
+ };
+ u32 value;
+ };
+ } ipi;
+
+ gid.value = cpu;
+ ipi.thread = get_thread(gid);
+ ipi.core = gid.core;
+ ipi.cluster = nps_cluster_logic_to_phys(gid.cluster);
+ ipi.num = NPS_IPI_IRQ;
+
+ __asm__ __volatile__(
+ " mov r3, %0\n"
+ " .word %1\n"
+ :
+ : "r"(ipi.value), "i"(CTOP_INST_ASRI_0_R3)
+ : "r3");
+}
+
+static void eznps_init_per_cpu(int cpu)
+{
+ smp_ipi_irq_setup(cpu, NPS_IPI_IRQ);
+
+ eznps_init_core(cpu);
+ mtm_enable_core(cpu);
+}
+
+static void eznps_ipi_clear(int irq)
+{
+ write_aux_reg(CTOP_AUX_IACK, 1 << irq);
+}
+
+struct plat_smp_ops plat_smp_ops = {
+ .info = smp_cpuinfo_buf,
+ .init_early_smp = eznps_init_cpumasks,
+ .cpu_kick = eznps_smp_wakeup_cpu,
+ .ipi_send = eznps_ipi_send,
+ .init_per_cpu = eznps_init_per_cpu,
+ .ipi_clear = eznps_ipi_clear,
+};