18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2010, 2011, 2012, Lemote, Inc.
48c2ecf20Sopenharmony_ci * Author: Chen Huacai, chenhc@lemote.com
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <irq.h>
88c2ecf20Sopenharmony_ci#include <linux/init.h>
98c2ecf20Sopenharmony_ci#include <linux/cpu.h>
108c2ecf20Sopenharmony_ci#include <linux/sched.h>
118c2ecf20Sopenharmony_ci#include <linux/sched/hotplug.h>
128c2ecf20Sopenharmony_ci#include <linux/sched/task_stack.h>
138c2ecf20Sopenharmony_ci#include <linux/smp.h>
148c2ecf20Sopenharmony_ci#include <linux/cpufreq.h>
158c2ecf20Sopenharmony_ci#include <linux/kexec.h>
168c2ecf20Sopenharmony_ci#include <asm/processor.h>
178c2ecf20Sopenharmony_ci#include <asm/time.h>
188c2ecf20Sopenharmony_ci#include <asm/tlbflush.h>
198c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
208c2ecf20Sopenharmony_ci#include <loongson.h>
218c2ecf20Sopenharmony_ci#include <loongson_regs.h>
228c2ecf20Sopenharmony_ci#include <workarounds.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "smp.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ciDEFINE_PER_CPU(int, cpu_state);
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define LS_IPI_IRQ (MIPS_CPU_IRQ_BASE + 6)
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic void *ipi_set0_regs[16];
318c2ecf20Sopenharmony_cistatic void *ipi_clear0_regs[16];
328c2ecf20Sopenharmony_cistatic void *ipi_status0_regs[16];
338c2ecf20Sopenharmony_cistatic void *ipi_en0_regs[16];
348c2ecf20Sopenharmony_cistatic void *ipi_mailbox_buf[16];
358c2ecf20Sopenharmony_cistatic uint32_t core0_c0count[NR_CPUS];
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/* read a 32bit value from ipi register */
388c2ecf20Sopenharmony_ci#define loongson3_ipi_read32(addr) readl(addr)
398c2ecf20Sopenharmony_ci/* read a 64bit value from ipi register */
408c2ecf20Sopenharmony_ci#define loongson3_ipi_read64(addr) readq(addr)
418c2ecf20Sopenharmony_ci/* write a 32bit value to ipi register */
428c2ecf20Sopenharmony_ci#define loongson3_ipi_write32(action, addr)	\
438c2ecf20Sopenharmony_ci	do {					\
448c2ecf20Sopenharmony_ci		writel(action, addr);		\
458c2ecf20Sopenharmony_ci		__wbflush();			\
468c2ecf20Sopenharmony_ci	} while (0)
478c2ecf20Sopenharmony_ci/* write a 64bit value to ipi register */
488c2ecf20Sopenharmony_ci#define loongson3_ipi_write64(action, addr)	\
498c2ecf20Sopenharmony_ci	do {					\
508c2ecf20Sopenharmony_ci		writeq(action, addr);		\
518c2ecf20Sopenharmony_ci		__wbflush();			\
528c2ecf20Sopenharmony_ci	} while (0)
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ciu32 (*ipi_read_clear)(int cpu);
558c2ecf20Sopenharmony_civoid (*ipi_write_action)(int cpu, u32 action);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic u32 csr_ipi_read_clear(int cpu)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	u32 action;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	/* Load the ipi register to figure out what we're supposed to do */
628c2ecf20Sopenharmony_ci	action = csr_readl(LOONGSON_CSR_IPI_STATUS);
638c2ecf20Sopenharmony_ci	/* Clear the ipi register to clear the interrupt */
648c2ecf20Sopenharmony_ci	csr_writel(action, LOONGSON_CSR_IPI_CLEAR);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	return action;
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic void csr_ipi_write_action(int cpu, u32 action)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	unsigned int irq = 0;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	while ((irq = ffs(action))) {
748c2ecf20Sopenharmony_ci		uint32_t val = CSR_IPI_SEND_BLOCK;
758c2ecf20Sopenharmony_ci		val |= (irq - 1);
768c2ecf20Sopenharmony_ci		val |= (cpu << CSR_IPI_SEND_CPU_SHIFT);
778c2ecf20Sopenharmony_ci		csr_writel(val, LOONGSON_CSR_IPI_SEND);
788c2ecf20Sopenharmony_ci		action &= ~BIT(irq - 1);
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic u32 legacy_ipi_read_clear(int cpu)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	u32 action;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	/* Load the ipi register to figure out what we're supposed to do */
878c2ecf20Sopenharmony_ci	action = loongson3_ipi_read32(ipi_status0_regs[cpu_logical_map(cpu)]);
888c2ecf20Sopenharmony_ci	/* Clear the ipi register to clear the interrupt */
898c2ecf20Sopenharmony_ci	loongson3_ipi_write32(action, ipi_clear0_regs[cpu_logical_map(cpu)]);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	return action;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic void legacy_ipi_write_action(int cpu, u32 action)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	loongson3_ipi_write32((u32)action, ipi_set0_regs[cpu]);
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic void csr_ipi_probe(void)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	if (cpu_has_csr() && csr_readl(LOONGSON_CSR_FEATURES) & LOONGSON_CSRF_IPI) {
1028c2ecf20Sopenharmony_ci		ipi_read_clear = csr_ipi_read_clear;
1038c2ecf20Sopenharmony_ci		ipi_write_action = csr_ipi_write_action;
1048c2ecf20Sopenharmony_ci	} else {
1058c2ecf20Sopenharmony_ci		ipi_read_clear = legacy_ipi_read_clear;
1068c2ecf20Sopenharmony_ci		ipi_write_action = legacy_ipi_write_action;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic void ipi_set0_regs_init(void)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	ipi_set0_regs[0] = (void *)
1138c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + SET0);
1148c2ecf20Sopenharmony_ci	ipi_set0_regs[1] = (void *)
1158c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + SET0);
1168c2ecf20Sopenharmony_ci	ipi_set0_regs[2] = (void *)
1178c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + SET0);
1188c2ecf20Sopenharmony_ci	ipi_set0_regs[3] = (void *)
1198c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + SET0);
1208c2ecf20Sopenharmony_ci	ipi_set0_regs[4] = (void *)
1218c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + SET0);
1228c2ecf20Sopenharmony_ci	ipi_set0_regs[5] = (void *)
1238c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + SET0);
1248c2ecf20Sopenharmony_ci	ipi_set0_regs[6] = (void *)
1258c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + SET0);
1268c2ecf20Sopenharmony_ci	ipi_set0_regs[7] = (void *)
1278c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + SET0);
1288c2ecf20Sopenharmony_ci	ipi_set0_regs[8] = (void *)
1298c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + SET0);
1308c2ecf20Sopenharmony_ci	ipi_set0_regs[9] = (void *)
1318c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + SET0);
1328c2ecf20Sopenharmony_ci	ipi_set0_regs[10] = (void *)
1338c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + SET0);
1348c2ecf20Sopenharmony_ci	ipi_set0_regs[11] = (void *)
1358c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + SET0);
1368c2ecf20Sopenharmony_ci	ipi_set0_regs[12] = (void *)
1378c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + SET0);
1388c2ecf20Sopenharmony_ci	ipi_set0_regs[13] = (void *)
1398c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + SET0);
1408c2ecf20Sopenharmony_ci	ipi_set0_regs[14] = (void *)
1418c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + SET0);
1428c2ecf20Sopenharmony_ci	ipi_set0_regs[15] = (void *)
1438c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + SET0);
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic void ipi_clear0_regs_init(void)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	ipi_clear0_regs[0] = (void *)
1498c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + CLEAR0);
1508c2ecf20Sopenharmony_ci	ipi_clear0_regs[1] = (void *)
1518c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + CLEAR0);
1528c2ecf20Sopenharmony_ci	ipi_clear0_regs[2] = (void *)
1538c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + CLEAR0);
1548c2ecf20Sopenharmony_ci	ipi_clear0_regs[3] = (void *)
1558c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + CLEAR0);
1568c2ecf20Sopenharmony_ci	ipi_clear0_regs[4] = (void *)
1578c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + CLEAR0);
1588c2ecf20Sopenharmony_ci	ipi_clear0_regs[5] = (void *)
1598c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + CLEAR0);
1608c2ecf20Sopenharmony_ci	ipi_clear0_regs[6] = (void *)
1618c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + CLEAR0);
1628c2ecf20Sopenharmony_ci	ipi_clear0_regs[7] = (void *)
1638c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + CLEAR0);
1648c2ecf20Sopenharmony_ci	ipi_clear0_regs[8] = (void *)
1658c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + CLEAR0);
1668c2ecf20Sopenharmony_ci	ipi_clear0_regs[9] = (void *)
1678c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + CLEAR0);
1688c2ecf20Sopenharmony_ci	ipi_clear0_regs[10] = (void *)
1698c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + CLEAR0);
1708c2ecf20Sopenharmony_ci	ipi_clear0_regs[11] = (void *)
1718c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + CLEAR0);
1728c2ecf20Sopenharmony_ci	ipi_clear0_regs[12] = (void *)
1738c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + CLEAR0);
1748c2ecf20Sopenharmony_ci	ipi_clear0_regs[13] = (void *)
1758c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + CLEAR0);
1768c2ecf20Sopenharmony_ci	ipi_clear0_regs[14] = (void *)
1778c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + CLEAR0);
1788c2ecf20Sopenharmony_ci	ipi_clear0_regs[15] = (void *)
1798c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + CLEAR0);
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic void ipi_status0_regs_init(void)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	ipi_status0_regs[0] = (void *)
1858c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + STATUS0);
1868c2ecf20Sopenharmony_ci	ipi_status0_regs[1] = (void *)
1878c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + STATUS0);
1888c2ecf20Sopenharmony_ci	ipi_status0_regs[2] = (void *)
1898c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + STATUS0);
1908c2ecf20Sopenharmony_ci	ipi_status0_regs[3] = (void *)
1918c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + STATUS0);
1928c2ecf20Sopenharmony_ci	ipi_status0_regs[4] = (void *)
1938c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + STATUS0);
1948c2ecf20Sopenharmony_ci	ipi_status0_regs[5] = (void *)
1958c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + STATUS0);
1968c2ecf20Sopenharmony_ci	ipi_status0_regs[6] = (void *)
1978c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + STATUS0);
1988c2ecf20Sopenharmony_ci	ipi_status0_regs[7] = (void *)
1998c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + STATUS0);
2008c2ecf20Sopenharmony_ci	ipi_status0_regs[8] = (void *)
2018c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + STATUS0);
2028c2ecf20Sopenharmony_ci	ipi_status0_regs[9] = (void *)
2038c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + STATUS0);
2048c2ecf20Sopenharmony_ci	ipi_status0_regs[10] = (void *)
2058c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + STATUS0);
2068c2ecf20Sopenharmony_ci	ipi_status0_regs[11] = (void *)
2078c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + STATUS0);
2088c2ecf20Sopenharmony_ci	ipi_status0_regs[12] = (void *)
2098c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + STATUS0);
2108c2ecf20Sopenharmony_ci	ipi_status0_regs[13] = (void *)
2118c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + STATUS0);
2128c2ecf20Sopenharmony_ci	ipi_status0_regs[14] = (void *)
2138c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + STATUS0);
2148c2ecf20Sopenharmony_ci	ipi_status0_regs[15] = (void *)
2158c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + STATUS0);
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic void ipi_en0_regs_init(void)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	ipi_en0_regs[0] = (void *)
2218c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + EN0);
2228c2ecf20Sopenharmony_ci	ipi_en0_regs[1] = (void *)
2238c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + EN0);
2248c2ecf20Sopenharmony_ci	ipi_en0_regs[2] = (void *)
2258c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + EN0);
2268c2ecf20Sopenharmony_ci	ipi_en0_regs[3] = (void *)
2278c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + EN0);
2288c2ecf20Sopenharmony_ci	ipi_en0_regs[4] = (void *)
2298c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + EN0);
2308c2ecf20Sopenharmony_ci	ipi_en0_regs[5] = (void *)
2318c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + EN0);
2328c2ecf20Sopenharmony_ci	ipi_en0_regs[6] = (void *)
2338c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + EN0);
2348c2ecf20Sopenharmony_ci	ipi_en0_regs[7] = (void *)
2358c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + EN0);
2368c2ecf20Sopenharmony_ci	ipi_en0_regs[8] = (void *)
2378c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + EN0);
2388c2ecf20Sopenharmony_ci	ipi_en0_regs[9] = (void *)
2398c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + EN0);
2408c2ecf20Sopenharmony_ci	ipi_en0_regs[10] = (void *)
2418c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + EN0);
2428c2ecf20Sopenharmony_ci	ipi_en0_regs[11] = (void *)
2438c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + EN0);
2448c2ecf20Sopenharmony_ci	ipi_en0_regs[12] = (void *)
2458c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + EN0);
2468c2ecf20Sopenharmony_ci	ipi_en0_regs[13] = (void *)
2478c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + EN0);
2488c2ecf20Sopenharmony_ci	ipi_en0_regs[14] = (void *)
2498c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + EN0);
2508c2ecf20Sopenharmony_ci	ipi_en0_regs[15] = (void *)
2518c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + EN0);
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic void ipi_mailbox_buf_init(void)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	ipi_mailbox_buf[0] = (void *)
2578c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + BUF);
2588c2ecf20Sopenharmony_ci	ipi_mailbox_buf[1] = (void *)
2598c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + BUF);
2608c2ecf20Sopenharmony_ci	ipi_mailbox_buf[2] = (void *)
2618c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + BUF);
2628c2ecf20Sopenharmony_ci	ipi_mailbox_buf[3] = (void *)
2638c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + BUF);
2648c2ecf20Sopenharmony_ci	ipi_mailbox_buf[4] = (void *)
2658c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + BUF);
2668c2ecf20Sopenharmony_ci	ipi_mailbox_buf[5] = (void *)
2678c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + BUF);
2688c2ecf20Sopenharmony_ci	ipi_mailbox_buf[6] = (void *)
2698c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + BUF);
2708c2ecf20Sopenharmony_ci	ipi_mailbox_buf[7] = (void *)
2718c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + BUF);
2728c2ecf20Sopenharmony_ci	ipi_mailbox_buf[8] = (void *)
2738c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + BUF);
2748c2ecf20Sopenharmony_ci	ipi_mailbox_buf[9] = (void *)
2758c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + BUF);
2768c2ecf20Sopenharmony_ci	ipi_mailbox_buf[10] = (void *)
2778c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + BUF);
2788c2ecf20Sopenharmony_ci	ipi_mailbox_buf[11] = (void *)
2798c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + BUF);
2808c2ecf20Sopenharmony_ci	ipi_mailbox_buf[12] = (void *)
2818c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + BUF);
2828c2ecf20Sopenharmony_ci	ipi_mailbox_buf[13] = (void *)
2838c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + BUF);
2848c2ecf20Sopenharmony_ci	ipi_mailbox_buf[14] = (void *)
2858c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + BUF);
2868c2ecf20Sopenharmony_ci	ipi_mailbox_buf[15] = (void *)
2878c2ecf20Sopenharmony_ci		(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + BUF);
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci/*
2918c2ecf20Sopenharmony_ci * Simple enough, just poke the appropriate ipi register
2928c2ecf20Sopenharmony_ci */
2938c2ecf20Sopenharmony_cistatic void loongson3_send_ipi_single(int cpu, unsigned int action)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	ipi_write_action(cpu_logical_map(cpu), (u32)action);
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic void
2998c2ecf20Sopenharmony_ciloongson3_send_ipi_mask(const struct cpumask *mask, unsigned int action)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	unsigned int i;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	for_each_cpu(i, mask)
3048c2ecf20Sopenharmony_ci		ipi_write_action(cpu_logical_map(i), (u32)action);
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic irqreturn_t loongson3_ipi_interrupt(int irq, void *dev_id)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	int i, cpu = smp_processor_id();
3118c2ecf20Sopenharmony_ci	unsigned int action, c0count;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	action = ipi_read_clear(cpu);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	if (action & SMP_RESCHEDULE_YOURSELF)
3168c2ecf20Sopenharmony_ci		scheduler_ipi();
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	if (action & SMP_CALL_FUNCTION) {
3198c2ecf20Sopenharmony_ci		irq_enter();
3208c2ecf20Sopenharmony_ci		generic_smp_call_function_interrupt();
3218c2ecf20Sopenharmony_ci		irq_exit();
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	if (action & SMP_ASK_C0COUNT) {
3258c2ecf20Sopenharmony_ci		BUG_ON(cpu != 0);
3268c2ecf20Sopenharmony_ci		c0count = read_c0_count();
3278c2ecf20Sopenharmony_ci		c0count = c0count ? c0count : 1;
3288c2ecf20Sopenharmony_ci		for (i = 1; i < nr_cpu_ids; i++)
3298c2ecf20Sopenharmony_ci			core0_c0count[i] = c0count;
3308c2ecf20Sopenharmony_ci		__wbflush(); /* Let others see the result ASAP */
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci#define MAX_LOOPS 800
3378c2ecf20Sopenharmony_ci/*
3388c2ecf20Sopenharmony_ci * SMP init and finish on secondary CPUs
3398c2ecf20Sopenharmony_ci */
3408c2ecf20Sopenharmony_cistatic void loongson3_init_secondary(void)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	int i;
3438c2ecf20Sopenharmony_ci	uint32_t initcount;
3448c2ecf20Sopenharmony_ci	unsigned int cpu = smp_processor_id();
3458c2ecf20Sopenharmony_ci	unsigned int imask = STATUSF_IP7 | STATUSF_IP6 |
3468c2ecf20Sopenharmony_ci			     STATUSF_IP3 | STATUSF_IP2;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	/* Set interrupt mask, but don't enable */
3498c2ecf20Sopenharmony_ci	change_c0_status(ST0_IM, imask);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	for (i = 0; i < num_possible_cpus(); i++)
3528c2ecf20Sopenharmony_ci		loongson3_ipi_write32(0xffffffff, ipi_en0_regs[cpu_logical_map(i)]);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	per_cpu(cpu_state, cpu) = CPU_ONLINE;
3558c2ecf20Sopenharmony_ci	cpu_set_core(&cpu_data[cpu],
3568c2ecf20Sopenharmony_ci		     cpu_logical_map(cpu) % loongson_sysconf.cores_per_package);
3578c2ecf20Sopenharmony_ci	cpu_data[cpu].package =
3588c2ecf20Sopenharmony_ci		cpu_logical_map(cpu) / loongson_sysconf.cores_per_package;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	i = 0;
3618c2ecf20Sopenharmony_ci	core0_c0count[cpu] = 0;
3628c2ecf20Sopenharmony_ci	loongson3_send_ipi_single(0, SMP_ASK_C0COUNT);
3638c2ecf20Sopenharmony_ci	while (!core0_c0count[cpu]) {
3648c2ecf20Sopenharmony_ci		i++;
3658c2ecf20Sopenharmony_ci		cpu_relax();
3668c2ecf20Sopenharmony_ci	}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	if (i > MAX_LOOPS)
3698c2ecf20Sopenharmony_ci		i = MAX_LOOPS;
3708c2ecf20Sopenharmony_ci	if (cpu_data[cpu].package)
3718c2ecf20Sopenharmony_ci		initcount = core0_c0count[cpu] + i;
3728c2ecf20Sopenharmony_ci	else /* Local access is faster for loops */
3738c2ecf20Sopenharmony_ci		initcount = core0_c0count[cpu] + i/2;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	write_c0_count(initcount);
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_cistatic void loongson3_smp_finish(void)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	int cpu = smp_processor_id();
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	write_c0_compare(read_c0_count() + mips_hpt_frequency/HZ);
3838c2ecf20Sopenharmony_ci	local_irq_enable();
3848c2ecf20Sopenharmony_ci	loongson3_ipi_write64(0,
3858c2ecf20Sopenharmony_ci			ipi_mailbox_buf[cpu_logical_map(cpu)] + 0x0);
3868c2ecf20Sopenharmony_ci	pr_info("CPU#%d finished, CP0_ST=%x\n",
3878c2ecf20Sopenharmony_ci			smp_processor_id(), read_c0_status());
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cistatic void __init loongson3_smp_setup(void)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	int i = 0, num = 0; /* i: physical id, num: logical id */
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	init_cpu_possible(cpu_none_mask);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	/* For unified kernel, NR_CPUS is the maximum possible value,
3978c2ecf20Sopenharmony_ci	 * loongson_sysconf.nr_cpus is the really present value */
3988c2ecf20Sopenharmony_ci	while (i < loongson_sysconf.nr_cpus) {
3998c2ecf20Sopenharmony_ci		if (loongson_sysconf.reserved_cpus_mask & (1<<i)) {
4008c2ecf20Sopenharmony_ci			/* Reserved physical CPU cores */
4018c2ecf20Sopenharmony_ci			__cpu_number_map[i] = -1;
4028c2ecf20Sopenharmony_ci		} else {
4038c2ecf20Sopenharmony_ci			__cpu_number_map[i] = num;
4048c2ecf20Sopenharmony_ci			__cpu_logical_map[num] = i;
4058c2ecf20Sopenharmony_ci			set_cpu_possible(num, true);
4068c2ecf20Sopenharmony_ci			num++;
4078c2ecf20Sopenharmony_ci		}
4088c2ecf20Sopenharmony_ci		i++;
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci	pr_info("Detected %i available CPU(s)\n", num);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	while (num < loongson_sysconf.nr_cpus) {
4138c2ecf20Sopenharmony_ci		__cpu_logical_map[num] = -1;
4148c2ecf20Sopenharmony_ci		num++;
4158c2ecf20Sopenharmony_ci	}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	csr_ipi_probe();
4188c2ecf20Sopenharmony_ci	ipi_set0_regs_init();
4198c2ecf20Sopenharmony_ci	ipi_clear0_regs_init();
4208c2ecf20Sopenharmony_ci	ipi_status0_regs_init();
4218c2ecf20Sopenharmony_ci	ipi_en0_regs_init();
4228c2ecf20Sopenharmony_ci	ipi_mailbox_buf_init();
4238c2ecf20Sopenharmony_ci	cpu_set_core(&cpu_data[0],
4248c2ecf20Sopenharmony_ci		     cpu_logical_map(0) % loongson_sysconf.cores_per_package);
4258c2ecf20Sopenharmony_ci	cpu_data[0].package = cpu_logical_map(0) / loongson_sysconf.cores_per_package;
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_cistatic void __init loongson3_prepare_cpus(unsigned int max_cpus)
4298c2ecf20Sopenharmony_ci{
4308c2ecf20Sopenharmony_ci	if (request_irq(LS_IPI_IRQ, loongson3_ipi_interrupt,
4318c2ecf20Sopenharmony_ci			IRQF_PERCPU | IRQF_NO_SUSPEND, "SMP_IPI", NULL))
4328c2ecf20Sopenharmony_ci		pr_err("Failed to request IPI IRQ\n");
4338c2ecf20Sopenharmony_ci	init_cpu_present(cpu_possible_mask);
4348c2ecf20Sopenharmony_ci	per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE;
4358c2ecf20Sopenharmony_ci}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci/*
4388c2ecf20Sopenharmony_ci * Setup the PC, SP, and GP of a secondary processor and start it runing!
4398c2ecf20Sopenharmony_ci */
4408c2ecf20Sopenharmony_cistatic int loongson3_boot_secondary(int cpu, struct task_struct *idle)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	unsigned long startargs[4];
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	pr_info("Booting CPU#%d...\n", cpu);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	/* startargs[] are initial PC, SP and GP for secondary CPU */
4478c2ecf20Sopenharmony_ci	startargs[0] = (unsigned long)&smp_bootstrap;
4488c2ecf20Sopenharmony_ci	startargs[1] = (unsigned long)__KSTK_TOS(idle);
4498c2ecf20Sopenharmony_ci	startargs[2] = (unsigned long)task_thread_info(idle);
4508c2ecf20Sopenharmony_ci	startargs[3] = 0;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	pr_debug("CPU#%d, func_pc=%lx, sp=%lx, gp=%lx\n",
4538c2ecf20Sopenharmony_ci			cpu, startargs[0], startargs[1], startargs[2]);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	loongson3_ipi_write64(startargs[3],
4568c2ecf20Sopenharmony_ci			ipi_mailbox_buf[cpu_logical_map(cpu)] + 0x18);
4578c2ecf20Sopenharmony_ci	loongson3_ipi_write64(startargs[2],
4588c2ecf20Sopenharmony_ci			ipi_mailbox_buf[cpu_logical_map(cpu)] + 0x10);
4598c2ecf20Sopenharmony_ci	loongson3_ipi_write64(startargs[1],
4608c2ecf20Sopenharmony_ci			ipi_mailbox_buf[cpu_logical_map(cpu)] + 0x8);
4618c2ecf20Sopenharmony_ci	loongson3_ipi_write64(startargs[0],
4628c2ecf20Sopenharmony_ci			ipi_mailbox_buf[cpu_logical_map(cpu)] + 0x0);
4638c2ecf20Sopenharmony_ci	return 0;
4648c2ecf20Sopenharmony_ci}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic int loongson3_cpu_disable(void)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	unsigned long flags;
4718c2ecf20Sopenharmony_ci	unsigned int cpu = smp_processor_id();
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	if (cpu == 0)
4748c2ecf20Sopenharmony_ci		return -EBUSY;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	set_cpu_online(cpu, false);
4778c2ecf20Sopenharmony_ci	calculate_cpu_foreign_map();
4788c2ecf20Sopenharmony_ci	local_irq_save(flags);
4798c2ecf20Sopenharmony_ci	irq_cpu_offline();
4808c2ecf20Sopenharmony_ci	clear_c0_status(ST0_IM);
4818c2ecf20Sopenharmony_ci	local_irq_restore(flags);
4828c2ecf20Sopenharmony_ci	local_flush_tlb_all();
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	return 0;
4858c2ecf20Sopenharmony_ci}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_cistatic void loongson3_cpu_die(unsigned int cpu)
4898c2ecf20Sopenharmony_ci{
4908c2ecf20Sopenharmony_ci	while (per_cpu(cpu_state, cpu) != CPU_DEAD)
4918c2ecf20Sopenharmony_ci		cpu_relax();
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	mb();
4948c2ecf20Sopenharmony_ci}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci/* To shutdown a core in Loongson 3, the target core should go to CKSEG1 and
4978c2ecf20Sopenharmony_ci * flush all L1 entries at first. Then, another core (usually Core 0) can
4988c2ecf20Sopenharmony_ci * safely disable the clock of the target core. loongson3_play_dead() is
4998c2ecf20Sopenharmony_ci * called via CKSEG1 (uncached and unmmaped) */
5008c2ecf20Sopenharmony_cistatic void loongson3_type1_play_dead(int *state_addr)
5018c2ecf20Sopenharmony_ci{
5028c2ecf20Sopenharmony_ci	register int val;
5038c2ecf20Sopenharmony_ci	register long cpuid, core, node, count;
5048c2ecf20Sopenharmony_ci	register void *addr, *base, *initfunc;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	__asm__ __volatile__(
5078c2ecf20Sopenharmony_ci		"   .set push                     \n"
5088c2ecf20Sopenharmony_ci		"   .set noreorder                \n"
5098c2ecf20Sopenharmony_ci		"   li %[addr], 0x80000000        \n" /* KSEG0 */
5108c2ecf20Sopenharmony_ci		"1: cache 0, 0(%[addr])           \n" /* flush L1 ICache */
5118c2ecf20Sopenharmony_ci		"   cache 0, 1(%[addr])           \n"
5128c2ecf20Sopenharmony_ci		"   cache 0, 2(%[addr])           \n"
5138c2ecf20Sopenharmony_ci		"   cache 0, 3(%[addr])           \n"
5148c2ecf20Sopenharmony_ci		"   cache 1, 0(%[addr])           \n" /* flush L1 DCache */
5158c2ecf20Sopenharmony_ci		"   cache 1, 1(%[addr])           \n"
5168c2ecf20Sopenharmony_ci		"   cache 1, 2(%[addr])           \n"
5178c2ecf20Sopenharmony_ci		"   cache 1, 3(%[addr])           \n"
5188c2ecf20Sopenharmony_ci		"   addiu %[sets], %[sets], -1    \n"
5198c2ecf20Sopenharmony_ci		"   bnez  %[sets], 1b             \n"
5208c2ecf20Sopenharmony_ci		"   addiu %[addr], %[addr], 0x20  \n"
5218c2ecf20Sopenharmony_ci		"   li    %[val], 0x7             \n" /* *state_addr = CPU_DEAD; */
5228c2ecf20Sopenharmony_ci		"   sw    %[val], (%[state_addr]) \n"
5238c2ecf20Sopenharmony_ci		"   sync                          \n"
5248c2ecf20Sopenharmony_ci		"   cache 21, (%[state_addr])     \n" /* flush entry of *state_addr */
5258c2ecf20Sopenharmony_ci		"   .set pop                      \n"
5268c2ecf20Sopenharmony_ci		: [addr] "=&r" (addr), [val] "=&r" (val)
5278c2ecf20Sopenharmony_ci		: [state_addr] "r" (state_addr),
5288c2ecf20Sopenharmony_ci		  [sets] "r" (cpu_data[smp_processor_id()].dcache.sets));
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	__asm__ __volatile__(
5318c2ecf20Sopenharmony_ci		"   .set push                         \n"
5328c2ecf20Sopenharmony_ci		"   .set noreorder                    \n"
5338c2ecf20Sopenharmony_ci		"   .set mips64                       \n"
5348c2ecf20Sopenharmony_ci		"   mfc0  %[cpuid], $15, 1            \n"
5358c2ecf20Sopenharmony_ci		"   andi  %[cpuid], 0x3ff             \n"
5368c2ecf20Sopenharmony_ci		"   dli   %[base], 0x900000003ff01000 \n"
5378c2ecf20Sopenharmony_ci		"   andi  %[core], %[cpuid], 0x3      \n"
5388c2ecf20Sopenharmony_ci		"   sll   %[core], 8                  \n" /* get core id */
5398c2ecf20Sopenharmony_ci		"   or    %[base], %[base], %[core]   \n"
5408c2ecf20Sopenharmony_ci		"   andi  %[node], %[cpuid], 0xc      \n"
5418c2ecf20Sopenharmony_ci		"   dsll  %[node], 42                 \n" /* get node id */
5428c2ecf20Sopenharmony_ci		"   or    %[base], %[base], %[node]   \n"
5438c2ecf20Sopenharmony_ci		"1: li    %[count], 0x100             \n" /* wait for init loop */
5448c2ecf20Sopenharmony_ci		"2: bnez  %[count], 2b                \n" /* limit mailbox access */
5458c2ecf20Sopenharmony_ci		"   addiu %[count], -1                \n"
5468c2ecf20Sopenharmony_ci		"   ld    %[initfunc], 0x20(%[base])  \n" /* get PC via mailbox */
5478c2ecf20Sopenharmony_ci		"   beqz  %[initfunc], 1b             \n"
5488c2ecf20Sopenharmony_ci		"   nop                               \n"
5498c2ecf20Sopenharmony_ci		"   ld    $sp, 0x28(%[base])          \n" /* get SP via mailbox */
5508c2ecf20Sopenharmony_ci		"   ld    $gp, 0x30(%[base])          \n" /* get GP via mailbox */
5518c2ecf20Sopenharmony_ci		"   ld    $a1, 0x38(%[base])          \n"
5528c2ecf20Sopenharmony_ci		"   jr    %[initfunc]                 \n" /* jump to initial PC */
5538c2ecf20Sopenharmony_ci		"   nop                               \n"
5548c2ecf20Sopenharmony_ci		"   .set pop                          \n"
5558c2ecf20Sopenharmony_ci		: [core] "=&r" (core), [node] "=&r" (node),
5568c2ecf20Sopenharmony_ci		  [base] "=&r" (base), [cpuid] "=&r" (cpuid),
5578c2ecf20Sopenharmony_ci		  [count] "=&r" (count), [initfunc] "=&r" (initfunc)
5588c2ecf20Sopenharmony_ci		: /* No Input */
5598c2ecf20Sopenharmony_ci		: "a1");
5608c2ecf20Sopenharmony_ci}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_cistatic void loongson3_type2_play_dead(int *state_addr)
5638c2ecf20Sopenharmony_ci{
5648c2ecf20Sopenharmony_ci	register int val;
5658c2ecf20Sopenharmony_ci	register long cpuid, core, node, count;
5668c2ecf20Sopenharmony_ci	register void *addr, *base, *initfunc;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	__asm__ __volatile__(
5698c2ecf20Sopenharmony_ci		"   .set push                     \n"
5708c2ecf20Sopenharmony_ci		"   .set noreorder                \n"
5718c2ecf20Sopenharmony_ci		"   li %[addr], 0x80000000        \n" /* KSEG0 */
5728c2ecf20Sopenharmony_ci		"1: cache 0, 0(%[addr])           \n" /* flush L1 ICache */
5738c2ecf20Sopenharmony_ci		"   cache 0, 1(%[addr])           \n"
5748c2ecf20Sopenharmony_ci		"   cache 0, 2(%[addr])           \n"
5758c2ecf20Sopenharmony_ci		"   cache 0, 3(%[addr])           \n"
5768c2ecf20Sopenharmony_ci		"   cache 1, 0(%[addr])           \n" /* flush L1 DCache */
5778c2ecf20Sopenharmony_ci		"   cache 1, 1(%[addr])           \n"
5788c2ecf20Sopenharmony_ci		"   cache 1, 2(%[addr])           \n"
5798c2ecf20Sopenharmony_ci		"   cache 1, 3(%[addr])           \n"
5808c2ecf20Sopenharmony_ci		"   addiu %[sets], %[sets], -1    \n"
5818c2ecf20Sopenharmony_ci		"   bnez  %[sets], 1b             \n"
5828c2ecf20Sopenharmony_ci		"   addiu %[addr], %[addr], 0x20  \n"
5838c2ecf20Sopenharmony_ci		"   li    %[val], 0x7             \n" /* *state_addr = CPU_DEAD; */
5848c2ecf20Sopenharmony_ci		"   sw    %[val], (%[state_addr]) \n"
5858c2ecf20Sopenharmony_ci		"   sync                          \n"
5868c2ecf20Sopenharmony_ci		"   cache 21, (%[state_addr])     \n" /* flush entry of *state_addr */
5878c2ecf20Sopenharmony_ci		"   .set pop                      \n"
5888c2ecf20Sopenharmony_ci		: [addr] "=&r" (addr), [val] "=&r" (val)
5898c2ecf20Sopenharmony_ci		: [state_addr] "r" (state_addr),
5908c2ecf20Sopenharmony_ci		  [sets] "r" (cpu_data[smp_processor_id()].dcache.sets));
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	__asm__ __volatile__(
5938c2ecf20Sopenharmony_ci		"   .set push                         \n"
5948c2ecf20Sopenharmony_ci		"   .set noreorder                    \n"
5958c2ecf20Sopenharmony_ci		"   .set mips64                       \n"
5968c2ecf20Sopenharmony_ci		"   mfc0  %[cpuid], $15, 1            \n"
5978c2ecf20Sopenharmony_ci		"   andi  %[cpuid], 0x3ff             \n"
5988c2ecf20Sopenharmony_ci		"   dli   %[base], 0x900000003ff01000 \n"
5998c2ecf20Sopenharmony_ci		"   andi  %[core], %[cpuid], 0x3      \n"
6008c2ecf20Sopenharmony_ci		"   sll   %[core], 8                  \n" /* get core id */
6018c2ecf20Sopenharmony_ci		"   or    %[base], %[base], %[core]   \n"
6028c2ecf20Sopenharmony_ci		"   andi  %[node], %[cpuid], 0xc      \n"
6038c2ecf20Sopenharmony_ci		"   dsll  %[node], 42                 \n" /* get node id */
6048c2ecf20Sopenharmony_ci		"   or    %[base], %[base], %[node]   \n"
6058c2ecf20Sopenharmony_ci		"   dsrl  %[node], 30                 \n" /* 15:14 */
6068c2ecf20Sopenharmony_ci		"   or    %[base], %[base], %[node]   \n"
6078c2ecf20Sopenharmony_ci		"1: li    %[count], 0x100             \n" /* wait for init loop */
6088c2ecf20Sopenharmony_ci		"2: bnez  %[count], 2b                \n" /* limit mailbox access */
6098c2ecf20Sopenharmony_ci		"   addiu %[count], -1                \n"
6108c2ecf20Sopenharmony_ci		"   ld    %[initfunc], 0x20(%[base])  \n" /* get PC via mailbox */
6118c2ecf20Sopenharmony_ci		"   beqz  %[initfunc], 1b             \n"
6128c2ecf20Sopenharmony_ci		"   nop                               \n"
6138c2ecf20Sopenharmony_ci		"   ld    $sp, 0x28(%[base])          \n" /* get SP via mailbox */
6148c2ecf20Sopenharmony_ci		"   ld    $gp, 0x30(%[base])          \n" /* get GP via mailbox */
6158c2ecf20Sopenharmony_ci		"   ld    $a1, 0x38(%[base])          \n"
6168c2ecf20Sopenharmony_ci		"   jr    %[initfunc]                 \n" /* jump to initial PC */
6178c2ecf20Sopenharmony_ci		"   nop                               \n"
6188c2ecf20Sopenharmony_ci		"   .set pop                          \n"
6198c2ecf20Sopenharmony_ci		: [core] "=&r" (core), [node] "=&r" (node),
6208c2ecf20Sopenharmony_ci		  [base] "=&r" (base), [cpuid] "=&r" (cpuid),
6218c2ecf20Sopenharmony_ci		  [count] "=&r" (count), [initfunc] "=&r" (initfunc)
6228c2ecf20Sopenharmony_ci		: /* No Input */
6238c2ecf20Sopenharmony_ci		: "a1");
6248c2ecf20Sopenharmony_ci}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_cistatic void loongson3_type3_play_dead(int *state_addr)
6278c2ecf20Sopenharmony_ci{
6288c2ecf20Sopenharmony_ci	register int val;
6298c2ecf20Sopenharmony_ci	register long cpuid, core, node, count;
6308c2ecf20Sopenharmony_ci	register void *addr, *base, *initfunc;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	__asm__ __volatile__(
6338c2ecf20Sopenharmony_ci		"   .set push                     \n"
6348c2ecf20Sopenharmony_ci		"   .set noreorder                \n"
6358c2ecf20Sopenharmony_ci		"   li %[addr], 0x80000000        \n" /* KSEG0 */
6368c2ecf20Sopenharmony_ci		"1: cache 0, 0(%[addr])           \n" /* flush L1 ICache */
6378c2ecf20Sopenharmony_ci		"   cache 0, 1(%[addr])           \n"
6388c2ecf20Sopenharmony_ci		"   cache 0, 2(%[addr])           \n"
6398c2ecf20Sopenharmony_ci		"   cache 0, 3(%[addr])           \n"
6408c2ecf20Sopenharmony_ci		"   cache 1, 0(%[addr])           \n" /* flush L1 DCache */
6418c2ecf20Sopenharmony_ci		"   cache 1, 1(%[addr])           \n"
6428c2ecf20Sopenharmony_ci		"   cache 1, 2(%[addr])           \n"
6438c2ecf20Sopenharmony_ci		"   cache 1, 3(%[addr])           \n"
6448c2ecf20Sopenharmony_ci		"   addiu %[sets], %[sets], -1    \n"
6458c2ecf20Sopenharmony_ci		"   bnez  %[sets], 1b             \n"
6468c2ecf20Sopenharmony_ci		"   addiu %[addr], %[addr], 0x40  \n"
6478c2ecf20Sopenharmony_ci		"   li %[addr], 0x80000000        \n" /* KSEG0 */
6488c2ecf20Sopenharmony_ci		"2: cache 2, 0(%[addr])           \n" /* flush L1 VCache */
6498c2ecf20Sopenharmony_ci		"   cache 2, 1(%[addr])           \n"
6508c2ecf20Sopenharmony_ci		"   cache 2, 2(%[addr])           \n"
6518c2ecf20Sopenharmony_ci		"   cache 2, 3(%[addr])           \n"
6528c2ecf20Sopenharmony_ci		"   cache 2, 4(%[addr])           \n"
6538c2ecf20Sopenharmony_ci		"   cache 2, 5(%[addr])           \n"
6548c2ecf20Sopenharmony_ci		"   cache 2, 6(%[addr])           \n"
6558c2ecf20Sopenharmony_ci		"   cache 2, 7(%[addr])           \n"
6568c2ecf20Sopenharmony_ci		"   cache 2, 8(%[addr])           \n"
6578c2ecf20Sopenharmony_ci		"   cache 2, 9(%[addr])           \n"
6588c2ecf20Sopenharmony_ci		"   cache 2, 10(%[addr])          \n"
6598c2ecf20Sopenharmony_ci		"   cache 2, 11(%[addr])          \n"
6608c2ecf20Sopenharmony_ci		"   cache 2, 12(%[addr])          \n"
6618c2ecf20Sopenharmony_ci		"   cache 2, 13(%[addr])          \n"
6628c2ecf20Sopenharmony_ci		"   cache 2, 14(%[addr])          \n"
6638c2ecf20Sopenharmony_ci		"   cache 2, 15(%[addr])          \n"
6648c2ecf20Sopenharmony_ci		"   addiu %[vsets], %[vsets], -1  \n"
6658c2ecf20Sopenharmony_ci		"   bnez  %[vsets], 2b            \n"
6668c2ecf20Sopenharmony_ci		"   addiu %[addr], %[addr], 0x40  \n"
6678c2ecf20Sopenharmony_ci		"   li    %[val], 0x7             \n" /* *state_addr = CPU_DEAD; */
6688c2ecf20Sopenharmony_ci		"   sw    %[val], (%[state_addr]) \n"
6698c2ecf20Sopenharmony_ci		"   sync                          \n"
6708c2ecf20Sopenharmony_ci		"   cache 21, (%[state_addr])     \n" /* flush entry of *state_addr */
6718c2ecf20Sopenharmony_ci		"   .set pop                      \n"
6728c2ecf20Sopenharmony_ci		: [addr] "=&r" (addr), [val] "=&r" (val)
6738c2ecf20Sopenharmony_ci		: [state_addr] "r" (state_addr),
6748c2ecf20Sopenharmony_ci		  [sets] "r" (cpu_data[smp_processor_id()].dcache.sets),
6758c2ecf20Sopenharmony_ci		  [vsets] "r" (cpu_data[smp_processor_id()].vcache.sets));
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	__asm__ __volatile__(
6788c2ecf20Sopenharmony_ci		"   .set push                         \n"
6798c2ecf20Sopenharmony_ci		"   .set noreorder                    \n"
6808c2ecf20Sopenharmony_ci		"   .set mips64                       \n"
6818c2ecf20Sopenharmony_ci		"   mfc0  %[cpuid], $15, 1            \n"
6828c2ecf20Sopenharmony_ci		"   andi  %[cpuid], 0x3ff             \n"
6838c2ecf20Sopenharmony_ci		"   dli   %[base], 0x900000003ff01000 \n"
6848c2ecf20Sopenharmony_ci		"   andi  %[core], %[cpuid], 0x3      \n"
6858c2ecf20Sopenharmony_ci		"   sll   %[core], 8                  \n" /* get core id */
6868c2ecf20Sopenharmony_ci		"   or    %[base], %[base], %[core]   \n"
6878c2ecf20Sopenharmony_ci		"   andi  %[node], %[cpuid], 0xc      \n"
6888c2ecf20Sopenharmony_ci		"   dsll  %[node], 42                 \n" /* get node id */
6898c2ecf20Sopenharmony_ci		"   or    %[base], %[base], %[node]   \n"
6908c2ecf20Sopenharmony_ci		"1: li    %[count], 0x100             \n" /* wait for init loop */
6918c2ecf20Sopenharmony_ci		"2: bnez  %[count], 2b                \n" /* limit mailbox access */
6928c2ecf20Sopenharmony_ci		"   addiu %[count], -1                \n"
6938c2ecf20Sopenharmony_ci		"   ld    %[initfunc], 0x20(%[base])  \n" /* get PC via mailbox */
6948c2ecf20Sopenharmony_ci		"   beqz  %[initfunc], 1b             \n"
6958c2ecf20Sopenharmony_ci		"   nop                               \n"
6968c2ecf20Sopenharmony_ci		"   ld    $sp, 0x28(%[base])          \n" /* get SP via mailbox */
6978c2ecf20Sopenharmony_ci		"   ld    $gp, 0x30(%[base])          \n" /* get GP via mailbox */
6988c2ecf20Sopenharmony_ci		"   ld    $a1, 0x38(%[base])          \n"
6998c2ecf20Sopenharmony_ci		"   jr    %[initfunc]                 \n" /* jump to initial PC */
7008c2ecf20Sopenharmony_ci		"   nop                               \n"
7018c2ecf20Sopenharmony_ci		"   .set pop                          \n"
7028c2ecf20Sopenharmony_ci		: [core] "=&r" (core), [node] "=&r" (node),
7038c2ecf20Sopenharmony_ci		  [base] "=&r" (base), [cpuid] "=&r" (cpuid),
7048c2ecf20Sopenharmony_ci		  [count] "=&r" (count), [initfunc] "=&r" (initfunc)
7058c2ecf20Sopenharmony_ci		: /* No Input */
7068c2ecf20Sopenharmony_ci		: "a1");
7078c2ecf20Sopenharmony_ci}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_civoid play_dead(void)
7108c2ecf20Sopenharmony_ci{
7118c2ecf20Sopenharmony_ci	int prid_imp, prid_rev, *state_addr;
7128c2ecf20Sopenharmony_ci	unsigned int cpu = smp_processor_id();
7138c2ecf20Sopenharmony_ci	void (*play_dead_at_ckseg1)(int *);
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	idle_task_exit();
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	prid_imp = read_c0_prid() & PRID_IMP_MASK;
7188c2ecf20Sopenharmony_ci	prid_rev = read_c0_prid() & PRID_REV_MASK;
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	if (prid_imp == PRID_IMP_LOONGSON_64G) {
7218c2ecf20Sopenharmony_ci		play_dead_at_ckseg1 =
7228c2ecf20Sopenharmony_ci			(void *)CKSEG1ADDR((unsigned long)loongson3_type3_play_dead);
7238c2ecf20Sopenharmony_ci		goto out;
7248c2ecf20Sopenharmony_ci	}
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	switch (prid_rev) {
7278c2ecf20Sopenharmony_ci	case PRID_REV_LOONGSON3A_R1:
7288c2ecf20Sopenharmony_ci	default:
7298c2ecf20Sopenharmony_ci		play_dead_at_ckseg1 =
7308c2ecf20Sopenharmony_ci			(void *)CKSEG1ADDR((unsigned long)loongson3_type1_play_dead);
7318c2ecf20Sopenharmony_ci		break;
7328c2ecf20Sopenharmony_ci	case PRID_REV_LOONGSON3B_R1:
7338c2ecf20Sopenharmony_ci	case PRID_REV_LOONGSON3B_R2:
7348c2ecf20Sopenharmony_ci		play_dead_at_ckseg1 =
7358c2ecf20Sopenharmony_ci			(void *)CKSEG1ADDR((unsigned long)loongson3_type2_play_dead);
7368c2ecf20Sopenharmony_ci		break;
7378c2ecf20Sopenharmony_ci	case PRID_REV_LOONGSON3A_R2_0:
7388c2ecf20Sopenharmony_ci	case PRID_REV_LOONGSON3A_R2_1:
7398c2ecf20Sopenharmony_ci	case PRID_REV_LOONGSON3A_R3_0:
7408c2ecf20Sopenharmony_ci	case PRID_REV_LOONGSON3A_R3_1:
7418c2ecf20Sopenharmony_ci		play_dead_at_ckseg1 =
7428c2ecf20Sopenharmony_ci			(void *)CKSEG1ADDR((unsigned long)loongson3_type3_play_dead);
7438c2ecf20Sopenharmony_ci		break;
7448c2ecf20Sopenharmony_ci	}
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ciout:
7478c2ecf20Sopenharmony_ci	state_addr = &per_cpu(cpu_state, cpu);
7488c2ecf20Sopenharmony_ci	mb();
7498c2ecf20Sopenharmony_ci	play_dead_at_ckseg1(state_addr);
7508c2ecf20Sopenharmony_ci}
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_cistatic int loongson3_disable_clock(unsigned int cpu)
7538c2ecf20Sopenharmony_ci{
7548c2ecf20Sopenharmony_ci	uint64_t core_id = cpu_core(&cpu_data[cpu]);
7558c2ecf20Sopenharmony_ci	uint64_t package_id = cpu_data[cpu].package;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	if ((read_c0_prid() & PRID_REV_MASK) == PRID_REV_LOONGSON3A_R1) {
7588c2ecf20Sopenharmony_ci		LOONGSON_CHIPCFG(package_id) &= ~(1 << (12 + core_id));
7598c2ecf20Sopenharmony_ci	} else {
7608c2ecf20Sopenharmony_ci		if (!(loongson_sysconf.workarounds & WORKAROUND_CPUHOTPLUG))
7618c2ecf20Sopenharmony_ci			LOONGSON_FREQCTRL(package_id) &= ~(1 << (core_id * 4 + 3));
7628c2ecf20Sopenharmony_ci	}
7638c2ecf20Sopenharmony_ci	return 0;
7648c2ecf20Sopenharmony_ci}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_cistatic int loongson3_enable_clock(unsigned int cpu)
7678c2ecf20Sopenharmony_ci{
7688c2ecf20Sopenharmony_ci	uint64_t core_id = cpu_core(&cpu_data[cpu]);
7698c2ecf20Sopenharmony_ci	uint64_t package_id = cpu_data[cpu].package;
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	if ((read_c0_prid() & PRID_REV_MASK) == PRID_REV_LOONGSON3A_R1) {
7728c2ecf20Sopenharmony_ci		LOONGSON_CHIPCFG(package_id) |= 1 << (12 + core_id);
7738c2ecf20Sopenharmony_ci	} else {
7748c2ecf20Sopenharmony_ci		if (!(loongson_sysconf.workarounds & WORKAROUND_CPUHOTPLUG))
7758c2ecf20Sopenharmony_ci			LOONGSON_FREQCTRL(package_id) |= 1 << (core_id * 4 + 3);
7768c2ecf20Sopenharmony_ci	}
7778c2ecf20Sopenharmony_ci	return 0;
7788c2ecf20Sopenharmony_ci}
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_cistatic int register_loongson3_notifier(void)
7818c2ecf20Sopenharmony_ci{
7828c2ecf20Sopenharmony_ci	return cpuhp_setup_state_nocalls(CPUHP_MIPS_SOC_PREPARE,
7838c2ecf20Sopenharmony_ci					 "mips/loongson:prepare",
7848c2ecf20Sopenharmony_ci					 loongson3_enable_clock,
7858c2ecf20Sopenharmony_ci					 loongson3_disable_clock);
7868c2ecf20Sopenharmony_ci}
7878c2ecf20Sopenharmony_ciearly_initcall(register_loongson3_notifier);
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci#endif
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ciconst struct plat_smp_ops loongson3_smp_ops = {
7928c2ecf20Sopenharmony_ci	.send_ipi_single = loongson3_send_ipi_single,
7938c2ecf20Sopenharmony_ci	.send_ipi_mask = loongson3_send_ipi_mask,
7948c2ecf20Sopenharmony_ci	.init_secondary = loongson3_init_secondary,
7958c2ecf20Sopenharmony_ci	.smp_finish = loongson3_smp_finish,
7968c2ecf20Sopenharmony_ci	.boot_secondary = loongson3_boot_secondary,
7978c2ecf20Sopenharmony_ci	.smp_setup = loongson3_smp_setup,
7988c2ecf20Sopenharmony_ci	.prepare_cpus = loongson3_prepare_cpus,
7998c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
8008c2ecf20Sopenharmony_ci	.cpu_disable = loongson3_cpu_disable,
8018c2ecf20Sopenharmony_ci	.cpu_die = loongson3_cpu_die,
8028c2ecf20Sopenharmony_ci#endif
8038c2ecf20Sopenharmony_ci#ifdef CONFIG_KEXEC
8048c2ecf20Sopenharmony_ci	.kexec_nonboot_cpu = kexec_nonboot_cpu_jump,
8058c2ecf20Sopenharmony_ci#endif
8068c2ecf20Sopenharmony_ci};
807