156 lines
3.5 KiB
C
156 lines
3.5 KiB
C
/*
|
|
* 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,
|
|
};
|