/* * 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 #include #include #include #include #include #include #include #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); } 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, };