summaryrefslogtreecommitdiff
path: root/arch/arc/plat-eznps/smp.c
blob: 56a4c8522f111cc5221e2ead6b6574100cd3f685 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/*
 * 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);
}

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,
};