18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
38c2ecf20Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
48c2ecf20Sopenharmony_ci * for more details.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2013 Broadcom Corporation.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * based on arch/mips/cavium-octeon/cpu.c
98c2ecf20Sopenharmony_ci * Copyright (C) 2009 Wind River Systems,
108c2ecf20Sopenharmony_ci *   written by Ralf Baechle <ralf@linux-mips.org>
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci#include <linux/capability.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/irqflags.h>
158c2ecf20Sopenharmony_ci#include <linux/notifier.h>
168c2ecf20Sopenharmony_ci#include <linux/prefetch.h>
178c2ecf20Sopenharmony_ci#include <linux/ptrace.h>
188c2ecf20Sopenharmony_ci#include <linux/sched.h>
198c2ecf20Sopenharmony_ci#include <linux/sched/task_stack.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <asm/cop2.h>
228c2ecf20Sopenharmony_ci#include <asm/current.h>
238c2ecf20Sopenharmony_ci#include <asm/mipsregs.h>
248c2ecf20Sopenharmony_ci#include <asm/page.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <asm/netlogic/mips-extns.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/*
298c2ecf20Sopenharmony_ci * 64 bit ops are done in inline assembly to support 32 bit
308c2ecf20Sopenharmony_ci * compilation
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_civoid nlm_cop2_save(struct nlm_cop2_state *r)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	asm volatile(
358c2ecf20Sopenharmony_ci		".set	push\n"
368c2ecf20Sopenharmony_ci		".set	noat\n"
378c2ecf20Sopenharmony_ci		"dmfc2	$1, $0, 0\n"
388c2ecf20Sopenharmony_ci		"sd	$1, 0(%1)\n"
398c2ecf20Sopenharmony_ci		"dmfc2	$1, $0, 1\n"
408c2ecf20Sopenharmony_ci		"sd	$1, 8(%1)\n"
418c2ecf20Sopenharmony_ci		"dmfc2	$1, $0, 2\n"
428c2ecf20Sopenharmony_ci		"sd	$1, 16(%1)\n"
438c2ecf20Sopenharmony_ci		"dmfc2	$1, $0, 3\n"
448c2ecf20Sopenharmony_ci		"sd	$1, 24(%1)\n"
458c2ecf20Sopenharmony_ci		"dmfc2	$1, $1, 0\n"
468c2ecf20Sopenharmony_ci		"sd	$1, 0(%2)\n"
478c2ecf20Sopenharmony_ci		"dmfc2	$1, $1, 1\n"
488c2ecf20Sopenharmony_ci		"sd	$1, 8(%2)\n"
498c2ecf20Sopenharmony_ci		"dmfc2	$1, $1, 2\n"
508c2ecf20Sopenharmony_ci		"sd	$1, 16(%2)\n"
518c2ecf20Sopenharmony_ci		"dmfc2	$1, $1, 3\n"
528c2ecf20Sopenharmony_ci		"sd	$1, 24(%2)\n"
538c2ecf20Sopenharmony_ci		".set	pop\n"
548c2ecf20Sopenharmony_ci		: "=m"(*r)
558c2ecf20Sopenharmony_ci		: "r"(r->tx), "r"(r->rx));
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	r->tx_msg_status = __read_32bit_c2_register($2, 0);
588c2ecf20Sopenharmony_ci	r->rx_msg_status = __read_32bit_c2_register($3, 0) & 0x0fffffff;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_civoid nlm_cop2_restore(struct nlm_cop2_state *r)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	u32 rstat;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	asm volatile(
668c2ecf20Sopenharmony_ci		".set	push\n"
678c2ecf20Sopenharmony_ci		".set	noat\n"
688c2ecf20Sopenharmony_ci		"ld	$1, 0(%1)\n"
698c2ecf20Sopenharmony_ci		"dmtc2	$1, $0, 0\n"
708c2ecf20Sopenharmony_ci		"ld	$1, 8(%1)\n"
718c2ecf20Sopenharmony_ci		"dmtc2	$1, $0, 1\n"
728c2ecf20Sopenharmony_ci		"ld	$1, 16(%1)\n"
738c2ecf20Sopenharmony_ci		"dmtc2	$1, $0, 2\n"
748c2ecf20Sopenharmony_ci		"ld	$1, 24(%1)\n"
758c2ecf20Sopenharmony_ci		"dmtc2	$1, $0, 3\n"
768c2ecf20Sopenharmony_ci		"ld	$1, 0(%2)\n"
778c2ecf20Sopenharmony_ci		"dmtc2	$1, $1, 0\n"
788c2ecf20Sopenharmony_ci		"ld	$1, 8(%2)\n"
798c2ecf20Sopenharmony_ci		"dmtc2	$1, $1, 1\n"
808c2ecf20Sopenharmony_ci		"ld	$1, 16(%2)\n"
818c2ecf20Sopenharmony_ci		"dmtc2	$1, $1, 2\n"
828c2ecf20Sopenharmony_ci		"ld	$1, 24(%2)\n"
838c2ecf20Sopenharmony_ci		"dmtc2	$1, $1, 3\n"
848c2ecf20Sopenharmony_ci		".set	pop\n"
858c2ecf20Sopenharmony_ci		: : "m"(*r), "r"(r->tx), "r"(r->rx));
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	__write_32bit_c2_register($2, 0, r->tx_msg_status);
888c2ecf20Sopenharmony_ci	rstat = __read_32bit_c2_register($3, 0) & 0xf0000000u;
898c2ecf20Sopenharmony_ci	__write_32bit_c2_register($3, 0, r->rx_msg_status | rstat);
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic int nlm_cu2_call(struct notifier_block *nfb, unsigned long action,
938c2ecf20Sopenharmony_ci	void *data)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	unsigned long flags;
968c2ecf20Sopenharmony_ci	unsigned int status;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	switch (action) {
998c2ecf20Sopenharmony_ci	case CU2_EXCEPTION:
1008c2ecf20Sopenharmony_ci		if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
1018c2ecf20Sopenharmony_ci			break;
1028c2ecf20Sopenharmony_ci		local_irq_save(flags);
1038c2ecf20Sopenharmony_ci		KSTK_STATUS(current) |= ST0_CU2;
1048c2ecf20Sopenharmony_ci		status = read_c0_status();
1058c2ecf20Sopenharmony_ci		write_c0_status(status | ST0_CU2);
1068c2ecf20Sopenharmony_ci		nlm_cop2_restore(&(current->thread.cp2));
1078c2ecf20Sopenharmony_ci		write_c0_status(status & ~ST0_CU2);
1088c2ecf20Sopenharmony_ci		local_irq_restore(flags);
1098c2ecf20Sopenharmony_ci		pr_info("COP2 access enabled for pid %d (%s)\n",
1108c2ecf20Sopenharmony_ci					current->pid, current->comm);
1118c2ecf20Sopenharmony_ci		return NOTIFY_BAD;	/* Don't call default notifier */
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	return NOTIFY_OK;		/* Let default notifier send signals */
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic int __init nlm_cu2_setup(void)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	return cu2_notifier(nlm_cu2_call, 0);
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ciearly_initcall(nlm_cu2_setup);
122