1/*-
2 * Copyright (c) 2016 Ruslan Bukin <br@bsdpad.com>
3 * All rights reserved.
4 *
5 * Portions of this software were developed by SRI International and the
6 * University of Cambridge Computer Laboratory under DARPA/AFRL contract
7 * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme.
8 *
9 * Portions of this software were developed by the University of Cambridge
10 * Computer Laboratory as part of the CTSRD Project, with support from the
11 * UK Higher Education Innovation Fund (HEIF).
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 *    notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 *    notice, this list of conditions and the following disclaimer in the
20 *    documentation and/or other materials provided with the distribution.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * $FreeBSD$
35 */
36
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD$");
39
40#include <sys/param.h>
41#include <sys/conf.h>
42#include <sys/kernel.h>
43#include <sys/smp.h>
44#include <sys/systm.h>
45
46#include <machine/cpufunc.h>
47#include <machine/hwfunc.h>
48#include <machine/md_var.h>
49#include <machine/smp.h>
50
51#define	VPECONF0_VPA	(1 << 0)
52#define	MVPCONTROL_VPC	(1 << 1)
53#define	MVPCONF0_PVPE_SHIFT	10
54#define	MVPCONF0_PVPE_MASK	(0xf << MVPCONF0_PVPE_SHIFT)
55#define	TCSTATUS_A	(1 << 13)
56
57unsigned malta_ap_boot = ~0;
58
59#define	C_SW0		(1 << 8)
60#define	C_SW1		(1 << 9)
61#define	C_IRQ0		(1 << 10)
62#define	C_IRQ1		(1 << 11)
63#define	C_IRQ2		(1 << 12)
64#define	C_IRQ3		(1 << 13)
65#define	C_IRQ4		(1 << 14)
66#define	C_IRQ5		(1 << 15)
67
68static inline void
69evpe(void)
70{
71	__asm __volatile(
72	"	.set push			\n"
73	"	.set noreorder			\n"
74	"	.set noat			\n"
75	"	.set mips32r2			\n"
76	"	.word	0x41600021	# evpe	\n"
77	"	ehb				\n"
78	"	.set pop			\n");
79}
80
81static inline void
82ehb(void)
83{
84	__asm __volatile(
85	"	.set mips32r2	\n"
86	"	ehb		\n"
87	"	.set mips0	\n");
88}
89
90#define	mttc0(rd, sel, val)						\
91({									\
92	__asm __volatile(						\
93	"	.set push					\n"	\
94	"	.set mips32r2					\n"	\
95	"	.set noat					\n"	\
96	"	move	$1, %0					\n"	\
97	"	.word 0x41810000 | (" #rd " << 11) | " #sel "	\n"	\
98	"	.set pop					\n"	\
99	:: "r" (val));							\
100})
101
102#define	mftc0(rt, sel)							\
103({									\
104	unsigned long __res;						\
105	__asm __volatile(						\
106	"	.set push					\n"	\
107	"	.set mips32r2					\n"	\
108	"	.set noat					\n"	\
109	"	.word 0x41000800 | (" #rt " << 16) | " #sel "	\n"	\
110	"	move	%0, $1					\n"	\
111	"	.set pop					\n"	\
112	: "=r" (__res));						\
113	 __res;								\
114})
115
116#define	write_c0_register32(reg, sel, val)				\
117({									\
118	__asm __volatile(						\
119	"	.set push					\n"	\
120	"	.set mips32					\n"	\
121	"	mtc0	%0, $%1, %2				\n"	\
122	"	.set pop					\n"	\
123	:: "r" (val), "i" (reg), "i" (sel));				\
124})
125
126#define	read_c0_register32(reg, sel)					\
127({									\
128	uint32_t __retval;						\
129	__asm __volatile(						\
130	"	.set push					\n"	\
131	"	.set mips32					\n"	\
132	"	mfc0	%0, $%1, %2				\n"	\
133	"	.set pop					\n"	\
134	: "=r" (__retval) : "i" (reg), "i" (sel));			\
135	__retval;							\
136})
137
138static void
139set_thread_context(int cpuid)
140{
141	uint32_t reg;
142
143	reg = read_c0_register32(1, 1);
144	reg &= ~(0xff);
145	reg |= cpuid;
146	write_c0_register32(1, 1, reg);
147
148	ehb();
149}
150
151void
152platform_ipi_send(int cpuid)
153{
154	uint32_t reg;
155
156	set_thread_context(cpuid);
157
158	/* Set cause */
159	reg = mftc0(13, 0);
160	reg |= (C_SW1);
161	mttc0(13, 0, reg);
162}
163
164void
165platform_ipi_clear(void)
166{
167	uint32_t reg;
168
169	reg = mips_rd_cause();
170	reg &= ~(C_SW1);
171	mips_wr_cause(reg);
172}
173
174int
175platform_ipi_hardintr_num(void)
176{
177
178	return (-1);
179}
180
181int
182platform_ipi_softintr_num(void)
183{
184
185	return (1);
186}
187
188void
189platform_init_ap(int cpuid)
190{
191	uint32_t clock_int_mask;
192	uint32_t ipi_intr_mask;
193
194	/*
195	 * Clear any pending IPIs.
196	 */
197	platform_ipi_clear();
198
199	/*
200	 * Unmask the clock and ipi interrupts.
201	 */
202	ipi_intr_mask = soft_int_mask(platform_ipi_softintr_num());
203	clock_int_mask = hard_int_mask(5);
204	set_intr_mask(ipi_intr_mask | clock_int_mask);
205
206	mips_wbflush();
207}
208
209void
210platform_cpu_mask(cpuset_t *mask)
211{
212	uint32_t i, ncpus, reg;
213
214	reg = mftc0(0, 2);
215	ncpus = ((reg & MVPCONF0_PVPE_MASK) >> MVPCONF0_PVPE_SHIFT) + 1;
216
217	CPU_ZERO(mask);
218	for (i = 0; i < ncpus; i++)
219		CPU_SET(i, mask);
220}
221
222struct cpu_group *
223platform_smp_topo(void)
224{
225
226	return (smp_topo_none());
227}
228
229int
230platform_start_ap(int cpuid)
231{
232	uint32_t reg;
233	int timeout;
234
235	/* Enter into configuration */
236	reg = read_c0_register32(0, 1);
237	reg |= (MVPCONTROL_VPC);
238	write_c0_register32(0, 1, reg);
239
240	set_thread_context(cpuid);
241
242	/*
243	 * Hint: how to set entry point.
244	 * reg = 0x80000000;
245	 * mttc0(2, 3, reg);
246	 */
247
248	/* Enable thread */
249	reg = mftc0(2, 1);
250	reg |= (TCSTATUS_A);
251	mttc0(2, 1, reg);
252
253	/* Unhalt CPU core */
254	mttc0(2, 4, 0);
255
256	/* Activate VPE */
257	reg = mftc0(1, 2);
258	reg |= (VPECONF0_VPA);
259	mttc0(1, 2, reg);
260
261	/* Out of configuration */
262	reg = read_c0_register32(0, 1);
263	reg &= ~(MVPCONTROL_VPC);
264	write_c0_register32(0, 1, reg);
265
266	evpe();
267
268	if (atomic_cmpset_32(&malta_ap_boot, ~0, cpuid) == 0)
269		return (-1);
270
271	printf("Waiting for cpu%d to start\n", cpuid);
272
273	timeout = 100;
274	do {
275		DELAY(1000);
276		if (atomic_cmpset_32(&malta_ap_boot, 0, ~0) != 0) {
277			printf("CPU %d started\n", cpuid);
278			return (0);
279		}
280	} while (timeout--);
281
282	printf("CPU %d failed to start\n", cpuid);
283
284	return (0);
285}
286