code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
 *
 * Vineetg: Aug 2009
 *  -"C" version of lowest level context switch asm macro called by schedular
 *   gcc doesn't generate the dward CFI info for hand written asm, hence can't
 *   backtrace out of it (e.g. tasks sleeping in kernel).
 *   So we cheat a bit by writing almost similar code in inline-asm.
 *  -This is a hacky way of doing things, but there is no other simple way.
 *   I don't want/intend to extend unwinding code to understand raw asm
 */

#include <asm/asm-offsets.h>
#include <linux/sched.h>
#include <linux/sched/debug.h>

#define KSP_WORD_OFF 	((TASK_THREAD + THREAD_KSP) / 4)

struct task_struct *__sched
__switch_to(struct task_struct *prev_task, struct task_struct *next_task)
{
	unsigned int tmp;
	unsigned int prev = (unsigned int)prev_task;
	unsigned int next = (unsigned int)next_task;

	__asm__ __volatile__(
		/* FP/BLINK save generated by gcc (standard function prologue */
		"st.a    r13, [sp, -4]   \n\t"
		"st.a    r14, [sp, -4]   \n\t"
		"st.a    r15, [sp, -4]   \n\t"
		"st.a    r16, [sp, -4]   \n\t"
		"st.a    r17, [sp, -4]   \n\t"
		"st.a    r18, [sp, -4]   \n\t"
		"st.a    r19, [sp, -4]   \n\t"
		"st.a    r20, [sp, -4]   \n\t"
		"st.a    r21, [sp, -4]   \n\t"
		"st.a    r22, [sp, -4]   \n\t"
		"st.a    r23, [sp, -4]   \n\t"
		"st.a    r24, [sp, -4]   \n\t"
#ifndef CONFIG_ARC_CURR_IN_REG
		"st.a    r25, [sp, -4]   \n\t"
#else
		"sub     sp, sp, 4      \n\t"	/* usual r25 placeholder */
#endif

		/* set ksp of outgoing task in tsk->thread.ksp */
#if KSP_WORD_OFF <= 255
		"st.as   sp, [%3, %1]    \n\t"
#else
		/*
		 * Workaround for NR_CPUS=4k
		 * %1 is bigger than 255 (S9 offset for st.as)
		 */
		"add2    r24, %3, %1     \n\t"
		"st      sp, [r24]       \n\t"
#endif

		/*
		 * setup _current_task with incoming tsk.
		 * optionally, set r25 to that as well
		 * For SMP extra work to get to &_current_task[cpu]
		 * (open coded SET_CURR_TASK_ON_CPU)
		 */
#ifndef CONFIG_SMP
		"st  %2, [@_current_task]	\n\t"
#else
		"lr   r24, [identity]		\n\t"
		"lsr  r24, r24, 8		\n\t"
		"bmsk r24, r24, 7		\n\t"
		"add2 r24, @_current_task, r24	\n\t"
		"st   %2,  [r24]		\n\t"
#endif
#ifdef CONFIG_ARC_CURR_IN_REG
		"mov r25, %2   \n\t"
#endif

		/* get ksp of incoming task from tsk->thread.ksp */
		"ld.as  sp, [%2, %1]   \n\t"

		/* start loading it's CALLEE reg file */

#ifndef CONFIG_ARC_CURR_IN_REG
		"ld.ab   r25, [sp, 4]   \n\t"
#else
		"add    sp, sp, 4       \n\t"
#endif
		"ld.ab   r24, [sp, 4]   \n\t"
		"ld.ab   r23, [sp, 4]   \n\t"
		"ld.ab   r22, [sp, 4]   \n\t"
		"ld.ab   r21, [sp, 4]   \n\t"
		"ld.ab   r20, [sp, 4]   \n\t"
		"ld.ab   r19, [sp, 4]   \n\t"
		"ld.ab   r18, [sp, 4]   \n\t"
		"ld.ab   r17, [sp, 4]   \n\t"
		"ld.ab   r16, [sp, 4]   \n\t"
		"ld.ab   r15, [sp, 4]   \n\t"
		"ld.ab   r14, [sp, 4]   \n\t"
		"ld.ab   r13, [sp, 4]   \n\t"

		/* last (ret value) = prev : although for ARC it mov r0, r0 */
		"mov     %0, %3        \n\t"

		/* FP/BLINK restore generated by gcc (standard func epilogue */

		: "=r"(tmp)
		: "n"(KSP_WORD_OFF), "r"(next), "r"(prev)
		: "blink"
	);

	return (struct task_struct *)tmp;
}


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
 *
 * Vineetg: Aug 2009
 *  -Moved core context switch macro out of entry.S into this file.
 *  -This is the more "natural" hand written assembler
 */

#include <linux/linkage.h>
#include <asm/entry.h>       /* For the SAVE_* macros */
#include <asm/asm-offsets.h>

#define KSP_WORD_OFF 	((TASK_THREAD + THREAD_KSP) / 4)

;################### Low Level Context Switch ##########################

	.section .sched.text,"ax",@progbits
	.align 4
	.global __switch_to
	.type   __switch_to, @function
__switch_to:
	CFI_STARTPROC

	/* Save regs on kernel mode stack of task */
	st.a    blink, [sp, -4]
	st.a    fp, [sp, -4]
	SAVE_CALLEE_SAVED_KERNEL

	/* Save the now KSP in task->thread.ksp */
#if KSP_WORD_OFF  <= 255
	st.as  sp, [r0, KSP_WORD_OFF]
#else
	/* Workaround for NR_CPUS=4k as ST.as can only take s9 offset */
	add2	r24, r0, KSP_WORD_OFF
	st	sp, [r24]
#endif
	/*
	* Return last task in r0 (return reg)
	* On ARC, Return reg = First Arg reg = r0.
	* Since we already have last task in r0,
	* don't need to do anything special to return it
	*/

	/*
	 * switch to new task, contained in r1
	 * Temp reg r3 is required to get the ptr to store val
	 */
	SET_CURR_TASK_ON_CPU  r1, r3

	/* reload SP with kernel mode stack pointer in task->thread.ksp */
	ld.as  sp, [r1, (TASK_THREAD + THREAD_KSP)/4]

	/* restore the registers */
	RESTORE_CALLEE_SAVED_KERNEL
	ld.ab   fp, [sp, 4]
	ld.ab   blink, [sp, 4]
	j       [blink]

END_CFI(__switch_to)

repo_name: angery/linux


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2012 Synopsys, Inc. (www.synopsys.com)
 *
 * Based on reduced version of METAG
 */

#include <linux/init.h>
#include <linux/reboot.h>
#include <linux/memblock.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <asm/mach_desc.h>

#ifdef CONFIG_SERIAL_EARLYCON

static unsigned int __initdata arc_base_baud;

unsigned int __init arc_early_base_baud(void)
{
	return arc_base_baud/16;
}

static void __init arc_set_early_base_baud(unsigned long dt_root)
{
	if (of_flat_dt_is_compatible(dt_root, "abilis,arc-tb10x"))
		arc_base_baud = 166666666;	/* Fixed 166.6MHz clk (TB10x) */
	else if (of_flat_dt_is_compatible(dt_root, "snps,arc-sdp") ||
		 of_flat_dt_is_compatible(dt_root, "snps,hsdk"))
		arc_base_baud = 33333333;	/* Fixed 33MHz clk (AXS10x & HSDK) */
	else
		arc_base_baud = 50000000;	/* Fixed default 50MHz */
}
#else
#define arc_set_early_base_baud(dt_root)
#endif

static const void * __init arch_get_next_mach(const char *const **match)
{
	static const struct machine_desc *mdesc = __arch_info_begin;
	const struct machine_desc *m = mdesc;

	if (m >= __arch_info_end)
		return NULL;

	mdesc++;
	*match = m->dt_compat;
	return m;
}

/**
 * setup_machine_fdt - Machine setup when an dtb was passed to the kernel
 * @dt:		virtual address pointer to dt blob
 *
 * If a dtb was passed to the kernel, then use it to choose the correct
 * machine_desc and to setup the system.
 */
const struct machine_desc * __init setup_machine_fdt(void *dt)
{
	const struct machine_desc *mdesc;
	unsigned long dt_root;

	if (!early_init_dt_scan(dt))
		return NULL;

	mdesc = of_flat_dt_match_machine(NULL, arch_get_next_mach);
	if (!mdesc)
		machine_halt();

	dt_root = of_get_flat_dt_root();
	arc_set_early_base_baud(dt_root);

	return mdesc;
}


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * several functions that help interpret ARC instructions
 * used for unaligned accesses, kprobes and kgdb
 *
 * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
 */

#include <linux/types.h>
#include <linux/kprobes.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <asm/disasm.h>

#if defined(CONFIG_KGDB) || defined(CONFIG_ARC_EMUL_UNALIGNED) || \
	defined(CONFIG_KPROBES)

/* disasm_instr: Analyses instruction at addr, stores
 * findings in *state
 */
void __kprobes disasm_instr(unsigned long addr, struct disasm_state *state,
	int userspace, struct pt_regs *regs, struct callee_regs *cregs)
{
	int fieldA = 0;
	int fieldC = 0, fieldCisReg = 0;
	uint16_t word1 = 0, word0 = 0;
	int subopcode, is_linked, op_format;
	uint16_t *ins_ptr;
	uint16_t ins_buf[4];
	int bytes_not_copied = 0;

	memset(state, 0, sizeof(struct disasm_state));

	/* This fetches the upper part of the 32 bit instruction
	 * in both the cases of Little Endian or Big Endian configurations. */
	if (userspace) {
		bytes_not_copied = copy_from_user(ins_buf,
						(const void __user *) addr, 8);
		if (bytes_not_copied > 6)
			goto fault;
		ins_ptr = ins_buf;
	} else {
		ins_ptr = (uint16_t *) addr;
	}

	word1 = *((uint16_t *)addr);

	state->major_opcode = (word1 >> 11) & 0x1F;

	/* Check if the instruction is 32 bit or 16 bit instruction */
	if (state->major_opcode < 0x0B) {
		if (bytes_not_copied > 4)
			goto fault;
		state->instr_len = 4;
		word0 = *((uint16_t *)(addr+2));
		state->words[0] = (word1 << 16) | word0;
	} else {
		state->instr_len = 2;
		state->words[0] = word1;
	}

	/* Read the second word in case of limm */
	word1 = *((uint16_t *)(addr + state->instr_len));
	word0 = *((uint16_t *)(addr + state->instr_len + 2));
	state->words[1] = (word1 << 16) | word0;

	switch (state->major_opcode) {
	case op_Bcc:
		state->is_branch = 1;

		/* unconditional branch s25, conditional branch s21 */
		fieldA = (IS_BIT(state->words[0], 16)) ?
			FIELD_s25(state->words[0]) :
			FIELD_s21(state->words[0]);

		state->delay_slot = IS_BIT(state->words[0], 5);
		state->target = fieldA + (addr & ~0x3);
		state->flow = direct_jump;
		break;

	case op_BLcc:
		if (IS_BIT(state->words[0], 16)) {
			/* Branch and Link*/
			/* unconditional branch s25, conditional branch s21 */
			fieldA = (IS_BIT(state->words[0], 17)) ?
				(FIELD_s25(state->words[0]) & ~0x3) :
				FIELD_s21(state->words[0]);

			state->flow = direct_call;
		} else {
			/*Branch On Compare */
			fieldA = FIELD_s9(state->words[0]) & ~0x3;
			state->flow = direct_jump;
		}

		state->delay_slot = IS_BIT(state->words[0], 5);
		state->target = fieldA + (addr & ~0x3);
		state->is_branch = 1;
		break;

	case op_LD:  /* LD<zz> a,[b,s9] */
		state->write = 0;
		state->di = BITS(state->words[0], 11, 11);
		if (state->di)
			break;
		state->x = BITS(state->words[0], 6, 6);
		state->zz = BITS(state->words[0], 7, 8);
		state->aa = BITS(state->words[0], 9, 10);
		state->wb_reg = FIELD_B(state->words[0]);
		if (state->wb_reg == REG_LIMM) {
			state->instr_len += 4;
			state->aa = 0;
			state->src1 = state->words[1];
		} else {
			state->src1 = get_reg(state->wb_reg, regs, cregs);
		}
		state->src2 = FIELD_s9(state->words[0]);
		state->dest = FIELD_A(state->words[0]);
		state->pref = (state->dest == REG_LIMM);
		break;

	case op_ST:
		state->write = 1;
		state->di = BITS(state->words[0], 5, 5);
		if (state->di)
			break;
		state->aa = BITS(state->words[0], 3, 4);
		state->zz = BITS(state->words[0], 1, 2);
		state->src1 = FIELD_C(state->words[0]);
		if (state->src1 == REG_LIMM) {
			state->instr_len += 4;
			state->src1 = state->words[1];
		} else {
			state->src1 = get_reg(state->src1, regs, cregs);
		}
		state->wb_reg = FIELD_B(state->words[0]);
		if (state->wb_reg == REG_LIMM) {
			state->aa = 0;
			state->instr_len += 4;
			state->src2 = state->words[1];
		} else {
			state->src2 = get_reg(state->wb_reg, regs, cregs);
		}
		state->src3 = FIELD_s9(state->words[0]);
		break;

	case op_MAJOR_4:
		subopcode = MINOR_OPCODE(state->words[0]);
		switch (subopcode) {
		case 32:	/* Jcc */
		case 33:	/* Jcc.D */
		case 34:	/* JLcc */
		case 35:	/* JLcc.D */
			is_linked = 0;

			if (subopcode == 33 || subopcode == 35)
				state->delay_slot = 1;

			if (subopcode == 34 || subopcode == 35)
				is_linked = 1;

			fieldCisReg = 0;
			op_format = BITS(state->words[0], 22, 23);
			if (op_format == 0 || ((op_format == 3) &&
				(!IS_BIT(state->words[0], 5)))) {
				fieldC = FIELD_C(state->words[0]);

				if (fieldC == REG_LIMM) {
					fieldC = state->words[1];
					state->instr_len += 4;
				} else {
					fieldCisReg = 1;
				}
			} else if (op_format == 1 || ((op_format == 3)
				&& (IS_BIT(state->words[0], 5)))) {
				fieldC = FIELD_C(state->words[0]);
			} else  {
				/* op_format == 2 */
				fieldC = FIELD_s12(state->words[0]);
			}

			if (!fieldCisReg) {
				state->target = fieldC;
				state->flow = is_linked ?
					direct_call : direct_jump;
			} else {
				state->target = get_reg(fieldC, regs, cregs);
				state->flow = is_linked ?
					indirect_call : indirect_jump;
			}
			state->is_branch = 1;
			break;

		case 40:	/* LPcc */
			if (BITS(state->words[0], 22, 23) == 3) {
				/* Conditional LPcc u7 */
				fieldC = FIELD_C(state->words[0]);

				fieldC = fieldC << 1;
				fieldC += (addr & ~0x03);
				state->is_branch = 1;
				state->flow = direct_jump;
				state->target = fieldC;
			}
			/* For Unconditional lp, next pc is the fall through
			 * which is updated */
			break;

		case 48 ... 55:	/* LD a,[b,c] */
			state->di = BITS(state->words[0], 15, 15);
			if (state->di)
				break;
			state->x = BITS(state->words[0], 16, 16);
			state->zz = BITS(state->words[0], 17, 18);
			state->aa = BITS(state->words[0], 22, 23);
			state->wb_reg = FIELD_B(state->words[0]);
			if (state->wb_reg == REG_LIMM) {
				state->instr_len += 4;
				state->src1 = state->words[1];
			} else {
				state->src1 = get_reg(state->wb_reg, regs,
						cregs);
			}
			state->src2 = FIELD_C(state->words[0]);
			if (state->src2 == REG_LIMM) {
				state->instr_len += 4;
				state->src2 = state->words[1];
			} else {
				state->src2 = get_reg(state->src2, regs,
					cregs);
			}
			state->dest = FIELD_A(state->words[0]);
			if (state->dest == REG_LIMM)
				state->pref = 1;
			break;

		case 10:	/* MOV */
			/* still need to check for limm to extract instr len */
			/* MOV is special case because it only takes 2 args */
			switch (BITS(state->words[0], 22, 23)) {
			case 0: /* OP a,b,c */
				if (FIELD_C(state->words[0]) == REG_LIMM)
					state->instr_len += 4;
				break;
			case 1: /* OP a,b,u6 */
				break;
			case 2: /* OP b,b,s12 */
				break;
			case 3: /* OP.cc b,b,c/u6 */
				if ((!IS_BIT(state->words[0], 5)) &&
				    (FIELD_C(state->words[0]) == REG_LIMM))
					state->instr_len += 4;
				break;
			}
			break;

		default:
			/* Not a Load, Jump or Loop instruction */
			/* still need to check for limm to extract instr len */
			switch (BITS(state->words[0], 22, 23)) {
			case 0: /* OP a,b,c */
				if ((FIELD_B(state->words[0]) == REG_LIMM) ||
				    (FIELD_C(state->words[0]) == REG_LIMM))
					state->instr_len += 4;
				break;
			case 1: /* OP a,b,u6 */
				break;
			case 2: /* OP b,b,s12 */
				break;
			case 3: /* OP.cc b,b,c/u6 */
				if ((!IS_BIT(state->words[0], 5)) &&
				   ((FIELD_B(state->words[0]) == REG_LIMM) ||
				    (FIELD_C(state->words[0]) == REG_LIMM)))
					state->instr_len += 4;
				break;
			}
			break;
		}
		break;

	/* 16 Bit Instructions */
	case op_LD_ADD: /* LD_S|LDB_S|LDW_S a,[b,c] */
		state->zz = BITS(state->words[0], 3, 4);
		state->src1 = get_reg(FIELD_S_B(state->words[0]), regs, cregs);
		state->src2 = get_reg(FIELD_S_C(state->words[0]), regs, cregs);
		state->dest = FIELD_S_A(state->words[0]);
		break;

	case op_ADD_MOV_CMP:
		/* check for limm, ignore mov_s h,b (== mov_s 0,b) */
		if ((BITS(state->words[0], 3, 4) < 3) &&
		    (FIELD_S_H(state->words[0]) == REG_LIMM))
			state->instr_len += 4;
		break;

	case op_S:
		subopcode = BITS(state->words[0], 5, 7);
		switch (subopcode) {
		case 0:	/* j_s */
		case 1:	/* j_s.d */
		case 2:	/* jl_s */
		case 3:	/* jl_s.d */
			state->target = get_reg(FIELD_S_B(state->words[0]),
						regs, cregs);
			state->delay_slot = subopcode & 1;
			state->flow = (subopcode >= 2) ?
				direct_call : indirect_jump;
			break;
		case 7:
			switch (BITS(state->words[0], 8, 10)) {
			case 4:	/* jeq_s [blink] */
			case 5:	/* jne_s [blink] */
			case 6:	/* j_s [blink] */
			case 7:	/* j_s.d [blink] */
				state->delay_slot = (subopcode == 7);
				state->flow = indirect_jump;
				state->target = get_reg(31, regs, cregs);
			default:
				break;
			}
		default:
			break;
		}
		break;

	case op_LD_S:	/* LD_S c, [b, u7] */
		state->src1 = get_reg(FIELD_S_B(state->words[0]), regs, cregs);
		state->src2 = FIELD_S_u7(state->words[0]);
		state->dest = FIELD_S_C(state->words[0]);
		break;

	case op_LDB_S:
	case op_STB_S:
		/* no further handling required as byte accesses should not
		 * cause an unaligned access exception */
		state->zz = 1;
		break;

	case op_LDWX_S:	/* LDWX_S c, [b, u6] */
		state->x = 1;
		fallthrough;

	case op_LDW_S:	/* LDW_S c, [b, u6] */
		state->zz = 2;
		state->src1 = get_reg(FIELD_S_B(state->words[0]), regs, cregs);
		state->src2 = FIELD_S_u6(state->words[0]);
		state->dest = FIELD_S_C(state->words[0]);
		break;

	case op_ST_S:	/* ST_S c, [b, u7] */
		state->write = 1;
		state->src1 = get_reg(FIELD_S_C(state->words[0]), regs, cregs);
		state->src2 = get_reg(FIELD_S_B(state->words[0]), regs, cregs);
		state->src3 = FIELD_S_u7(state->words[0]);
		break;

	case op_STW_S:	/* STW_S c,[b,u6] */
		state->write = 1;
		state->zz = 2;
		state->src1 = get_reg(FIELD_S_C(state->words[0]), regs, cregs);
		state->src2 = get_reg(FIELD_S_B(state->words[0]), regs, cregs);
		state->src3 = FIELD_S_u6(state->words[0]);
		break;

	case op_SP:	/* LD_S|LDB_S b,[sp,u7], ST_S|STB_S b,[sp,u7] */
		/* note: we are ignoring possibility of:
		 * ADD_S, SUB_S, PUSH_S, POP_S as these should not
		 * cause unaligned exception anyway */
		state->write = BITS(state->words[0], 6, 6);
		state->zz = BITS(state->words[0], 5, 5);
		if (state->zz)
			break;	/* byte accesses should not come here */
		if (!state->write) {
			state->src1 = get_reg(28, regs, cregs);
			state->src2 = FIELD_S_u7(state->words[0]);
			state->dest = FIELD_S_B(state->words[0]);
		} else {
			state->src1 = get_reg(FIELD_S_B(state->words[0]), regs,
					cregs);
			state->src2 = get_reg(28, regs, cregs);
			state->src3 = FIELD_S_u7(state->words[0]);
		}
		break;

	case op_GP:	/* LD_S|LDB_S|LDW_S r0,[gp,s11/s9/s10] */
		/* note: ADD_S r0, gp, s11 is ignored */
		state->zz = BITS(state->words[0], 9, 10);
		state->src1 = get_reg(26, regs, cregs);
		state->src2 = state->zz ? FIELD_S_s10(state->words[0]) :
			FIELD_S_s11(state->words[0]);
		state->dest = 0;
		break;

	case op_Pcl:	/* LD_S b,[pcl,u10] */
		state->src1 = regs->ret & ~3;
		state->src2 = FIELD_S_u10(state->words[0]);
		state->dest = FIELD_S_B(state->words[0]);
		break;

	case op_BR_S:
		state->target = FIELD_S_s8(state->words[0]) + (addr & ~0x03);
		state->flow = direct_jump;
		state->is_branch = 1;
		break;

	case op_B_S:
		fieldA = (BITS(state->words[0], 9, 10) == 3) ?
			FIELD_S_s7(state->words[0]) :
			FIELD_S_s10(state->words[0]);
		state->target = fieldA + (addr & ~0x03);
		state->flow = direct_jump;
		state->is_branch = 1;
		break;

	case op_BL_S:
		state->target = FIELD_S_s13(state->words[0]) + (addr & ~0x03);
		state->flow = direct_call;
		state->is_branch = 1;
		break;

	default:
		break;
	}

	if (bytes_not_copied <= (8 - state->instr_len))
		return;

fault:	state->fault = 1;
}

long __kprobes get_reg(int reg, struct pt_regs *regs,
		       struct callee_regs *cregs)
{
	long *p;

#if defined(CONFIG_ISA_ARCOMPACT)
	if (reg <= 12) {
		p = &regs->r0;
		return p[-reg];
	}
#else /* CONFIG_ISA_ARCV2 */
	if (reg <= 11) {
		p = &regs->r0;
		return p[reg];
	}

	if (reg == 12)
		return regs->r12;
	if (reg == 30)
		return regs->r30;
#ifdef CONFIG_ARC_HAS_ACCL_REGS
	if (reg == 58)
		return regs->r58;
	if (reg == 59)
		return regs->r59;
#endif
#endif
	if (cregs && (reg <= 25)) {
		p = &cregs->r13;
		return p[13 - reg];
	}

	if (reg == 26)
		return regs->r26;
	if (reg == 27)
		return regs->fp;
	if (reg == 28)
		return regs->sp;
	if (reg == 31)
		return regs->blink;

	return 0;
}

void __kprobes set_reg(int reg, long val, struct pt_regs *regs,
		struct callee_regs *cregs)
{
	long *p;

#if defined(CONFIG_ISA_ARCOMPACT)
	switch (reg) {
	case 0 ... 12:
		p = &regs->r0;
		p[-reg] = val;
		break;
	case 13 ... 25:
		if (cregs) {
			p = &cregs->r13;
			p[13 - reg] = val;
		}
		break;
	case 26:
		regs->r26 = val;
		break;
	case 27:
		regs->fp = val;
		break;
	case 28:
		regs->sp = val;
		break;
	case 31:
		regs->blink = val;
		break;
	default:
		break;
	}
#else /* CONFIG_ISA_ARCV2 */
	switch (reg) {
	case 0 ... 11:
		p = &regs->r0;
		p[reg] = val;
		break;
	case 12:
		regs->r12 = val;
		break;
	case 13 ... 25:
		if (cregs) {
			p = &cregs->r13;
			p[13 - reg] = val;
		}
		break;
	case 26:
		regs->r26 = val;
		break;
	case 27:
		regs->fp = val;
		break;
	case 28:
		regs->sp = val;
		break;
	case 30:
		regs->r30 = val;
		break;
	case 31:
		regs->blink = val;
		break;
#ifdef CONFIG_ARC_HAS_ACCL_REGS
	case 58:
		regs->r58 = val;
		break;
	case 59:
		regs->r59 = val;
		break;
#endif
	default:
		break;
	}
#endif
}

/*
 * Disassembles the insn at @pc and sets @next_pc to next PC (which could be
 * @pc +2/4/6 (ARCompact ISA allows free intermixing of 16/32 bit insns).
 *
 * If @pc is a branch
 *	-@tgt_if_br is set to branch target.
 *	-If branch has delay slot, @next_pc updated with actual next PC.
 */
int __kprobes disasm_next_pc(unsigned long pc, struct pt_regs *regs,
			     struct callee_regs *cregs,
			     unsigned long *next_pc, unsigned long *tgt_if_br)
{
	struct disasm_state instr;

	disasm_instr(pc, &instr, 0, regs, cregs);

	*next_pc = pc + instr.instr_len;

	/* Instruction with possible two targets branch, jump and loop */
	if (instr.is_branch)
		*tgt_if_br = instr.target;

	/* For the instructions with delay slots, the fall through is the
	 * instruction following the instruction in delay slot.
	 */
	 if (instr.delay_slot) {
		struct disasm_state instr_d;

		disasm_instr(*next_pc, &instr_d, 0, regs, cregs);

		*next_pc += instr_d.instr_len;
	 }

	 /* Zero Overhead Loop - end of the loop */
	if (!(regs->status32 & STATUS32_L) && (*next_pc == regs->lp_end)
		&& (regs->lp_count > 1)) {
		*next_pc = regs->lp_start;
	}

	return instr.is_branch;
}

#endif /* CONFIG_KGDB || CONFIG_ARC_EMUL_UNALIGNED || CONFIG_KPROBES */


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * ARCv2 ISA based core Low Level Intr/Traps/Exceptions(non-TLB) Handling
 *
 * Copyright (C) 2013 Synopsys, Inc. (www.synopsys.com)
 */

#include <linux/linkage.h>   /* ARC_{EXTRY,EXIT} */
#include <asm/entry.h>       /* SAVE_ALL_{INT1,INT2,TRAP...} */
#include <asm/errno.h>
#include <asm/arcregs.h>
#include <asm/irqflags.h>
#include <asm/mmu.h>

; A maximum number of supported interrupts in the core interrupt controller.
; This number is not equal to the maximum interrupt number (256) because
; first 16 lines are reserved for exceptions and are not configurable.
#define NR_CPU_IRQS	240

	.cpu HS

#define VECTOR	.word

;############################ Vector Table #################################

	.section .vector,"a",@progbits
	.align 4

# Initial 16 slots are Exception Vectors
VECTOR	res_service		; Reset Vector
VECTOR	mem_service		; Mem exception
VECTOR	instr_service		; Instrn Error
VECTOR	EV_MachineCheck		; Fatal Machine check
VECTOR	EV_TLBMissI		; Intruction TLB miss
VECTOR	EV_TLBMissD		; Data TLB miss
VECTOR	EV_TLBProtV		; Protection Violation
VECTOR	EV_PrivilegeV		; Privilege Violation
VECTOR	EV_SWI			; Software Breakpoint
VECTOR	EV_Trap			; Trap exception
VECTOR	EV_Extension		; Extn Instruction Exception
VECTOR	EV_DivZero		; Divide by Zero
VECTOR	EV_DCError		; Data Cache Error
VECTOR	EV_Misaligned		; Misaligned Data Access
VECTOR	reserved		; Reserved slots
VECTOR	reserved		; Reserved slots

# Begin Interrupt Vectors
VECTOR	handle_interrupt	; (16) Timer0
VECTOR	handle_interrupt	; unused (Timer1)
VECTOR	handle_interrupt	; unused (WDT)
VECTOR	handle_interrupt	; (19) Inter core Interrupt (IPI)
VECTOR	handle_interrupt	; (20) perf Interrupt
VECTOR	handle_interrupt	; (21) Software Triggered Intr (Self IPI)
VECTOR	handle_interrupt	; unused
VECTOR	handle_interrupt	; (23) unused
# End of fixed IRQs

.rept NR_CPU_IRQS - 8
	VECTOR	handle_interrupt
.endr

	.section .text, "ax",@progbits

reserved:
	flag 1		; Unexpected event, halt

;##################### Interrupt Handling ##############################

ENTRY(handle_interrupt)

	INTERRUPT_PROLOGUE

	# irq control APIs local_irq_save/restore/disable/enable fiddle with
	# global interrupt enable bits in STATUS32 (.IE for 1 prio, .E[] for 2 prio)
	# However a taken interrupt doesn't clear these bits. Thus irqs_disabled()
	# query in hard ISR path would return false (since .IE is set) which would
	# trips genirq interrupt handling asserts.
	#
	# So do a "soft" disable of interrutps here.
	#
	# Note this disable is only for consistent book-keeping as further interrupts
	# will be disabled anyways even w/o this. Hardware tracks active interrupts
	# seperately in AUX_IRQ_ACT.active and will not take new interrupts
	# unless this one returns (or higher prio becomes pending in 2-prio scheme)

	IRQ_DISABLE

	; icause is banked: one per priority level
	; so a higher prio interrupt taken here won't clobber prev prio icause
	lr  r0, [ICAUSE]
	mov   blink, ret_from_exception

	b.d  arch_do_IRQ
	mov r1, sp

END(handle_interrupt)

;################### Non TLB Exception Handling #############################

ENTRY(EV_SWI)
	; TODO: implement this
	EXCEPTION_PROLOGUE
	b   ret_from_exception
END(EV_SWI)

ENTRY(EV_DivZero)
	; TODO: implement this
	EXCEPTION_PROLOGUE
	b   ret_from_exception
END(EV_DivZero)

ENTRY(EV_DCError)
	; TODO: implement this
	EXCEPTION_PROLOGUE
	b   ret_from_exception
END(EV_DCError)

; ---------------------------------------------
; Memory Error Exception Handler
;   - Unlike ARCompact, handles Bus errors for both User/Kernel mode,
;     Instruction fetch or Data access, under a single Exception Vector
; ---------------------------------------------

ENTRY(mem_service)

	EXCEPTION_PROLOGUE

	lr  r0, [efa]
	mov r1, sp

	FAKE_RET_FROM_EXCPN

	bl  do_memory_error
	b   ret_from_exception
END(mem_service)

ENTRY(EV_Misaligned)

	EXCEPTION_PROLOGUE

	lr  r0, [efa]	; Faulting Data address
	mov r1, sp

	FAKE_RET_FROM_EXCPN

	SAVE_CALLEE_SAVED_USER
	mov r2, sp              ; callee_regs

	bl  do_misaligned_access

	; TBD: optimize - do this only if a callee reg was involved
	; either a dst of emulated LD/ST or src with address-writeback
	RESTORE_CALLEE_SAVED_USER

	b   ret_from_exception
END(EV_Misaligned)

; ---------------------------------------------
; Protection Violation Exception Handler
; ---------------------------------------------

ENTRY(EV_TLBProtV)

	EXCEPTION_PROLOGUE

	lr  r0, [efa]	; Faulting Data address
	mov r1, sp	; pt_regs

	FAKE_RET_FROM_EXCPN

	mov blink, ret_from_exception
	b   do_page_fault

END(EV_TLBProtV)

; From Linux standpoint Slow Path I/D TLB Miss is same a ProtV as they
; need to call do_page_fault().
; ECR in pt_regs provides whether access was R/W/X

.global        call_do_page_fault
.set call_do_page_fault, EV_TLBProtV

;############# Common Handlers for ARCompact and ARCv2 ##############

#include "entry.S"

;############# Return from Intr/Excp/Trap (ARCv2 ISA Specifics) ##############
;
; Restore the saved sys context (common exit-path for EXCPN/IRQ/Trap)
; IRQ shd definitely not happen between now and rtie
; All 2 entry points to here already disable interrupts

.Lrestore_regs:
restore_regs:

	# Interrpts are actually disabled from this point on, but will get
	# reenabled after we return from interrupt/exception.
	# But irq tracer needs to be told now...
	TRACE_ASM_IRQ_ENABLE

	ld	r0, [sp, PT_status32]	; U/K mode at time of entry
	lr	r10, [AUX_IRQ_ACT]

	bmsk	r11, r10, 15		; extract AUX_IRQ_ACT.active
	breq	r11, 0, .Lexcept_ret	; No intr active, ret from Exception

;####### Return from Intr #######

.Lisr_ret:

debug_marker_l1:
	; bbit1.nt r0, STATUS_DE_BIT, .Lintr_ret_to_delay_slot
	btst	r0, STATUS_DE_BIT		; Z flag set if bit clear
	bnz	.Lintr_ret_to_delay_slot	; branch if STATUS_DE_BIT set

	; Handle special case #1: (Entry via Exception, Return via IRQ)
	;
	; Exception in U mode, preempted in kernel, Intr taken (K mode), orig
	; task now returning to U mode (riding the Intr)
	; AUX_IRQ_ACTIVE won't have U bit set (since intr in K mode), hence SP
	; won't be switched to correct U mode value (from AUX_SP)
	; So force AUX_IRQ_ACT.U for such a case

	btst	r0, STATUS_U_BIT		; Z flag set if K (Z clear for U)
	bset.nz	r11, r11, AUX_IRQ_ACT_BIT_U	; NZ means U
	sr	r11, [AUX_IRQ_ACT]

	INTERRUPT_EPILOGUE
	rtie

;####### Return from Exception / pure kernel mode #######

.Lexcept_ret:	; Expects r0 has PT_status32

debug_marker_syscall:
	EXCEPTION_EPILOGUE
	rtie

;####### Return from Intr to insn in delay slot #######

; Handle special case #2: (Entry via Exception in Delay Slot, Return via IRQ)
;
; Intr returning to a Delay Slot (DS) insn
; (since IRQ NOT allowed in DS in ARCv2, this can only happen if orig
; entry was via Exception in DS which got preempted in kernel).
;
; IRQ RTIE won't reliably restore DE bit and/or BTA, needs workaround
;
; Solution is to drop out of interrupt context into pure kernel mode
; and return from pure kernel mode which does right things for delay slot

.Lintr_ret_to_delay_slot:
debug_marker_ds:

	ld	r2, [@intr_to_DE_cnt]
	add	r2, r2, 1
	st	r2, [@intr_to_DE_cnt]

	; drop out of interrupt context (clear AUX_IRQ_ACT.active)
	bmskn	r11, r10, 15
	sr	r11, [AUX_IRQ_ACT]
	b	.Lexcept_ret

END(ret_from_exception)


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Low Level Interrupts/Traps/Exceptions(non-TLB) Handling for ARCompact ISA
 *
 * Copyright (C) 2014-15 Synopsys, Inc. (www.synopsys.com)
 * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
 *
 * vineetg: May 2011
 *  -Userspace unaligned access emulation
 *
 * vineetg: Feb 2011 (ptrace low level code fixes)
 *  -traced syscall return code (r0) was not saved into pt_regs for restoring
 *   into user reg-file when traded task rets to user space.
 *  -syscalls needing arch-wrappers (mainly for passing sp as pt_regs)
 *   were not invoking post-syscall trace hook (jumping directly into
 *   ret_from_system_call)
 *
 * vineetg: Nov 2010:
 *  -Vector table jumps (@8 bytes) converted into branches (@4 bytes)
 *  -To maintain the slot size of 8 bytes/vector, added nop, which is
 *   not executed at runtime.
 *
 * vineetg: Nov 2009 (Everything needed for TIF_RESTORE_SIGMASK)
 *  -do_signal()invoked upon TIF_RESTORE_SIGMASK as well
 *  -Wrappers for sys_{,rt_}sigsuspend() no longer needed as they don't
 *   need ptregs anymore
 *
 * Vineetg: Oct 2009
 *  -In a rare scenario, Process gets a Priv-V exception and gets scheduled
 *   out. Since we don't do FAKE RTIE for Priv-V, CPU exception state remains
 *   active (AE bit enabled).  This causes a double fault for a subseq valid
 *   exception. Thus FAKE RTIE needed in low level Priv-Violation handler.
 *   Instr Error could also cause similar scenario, so same there as well.
 *
 * Vineetg: March 2009 (Supporting 2 levels of Interrupts)
 *
 * Vineetg: Aug 28th 2008: Bug #94984
 *  -Zero Overhead Loop Context shd be cleared when entering IRQ/EXcp/Trap
 *   Normally CPU does this automatically, however when doing FAKE rtie,
 *   we need to explicitly do this. The problem in macros
 *   FAKE_RET_FROM_EXCPN and FAKE_RET_FROM_EXCPN_LOCK_IRQ was that this bit
 *   was being "CLEARED" rather then "SET". Since it is Loop INHIBIT Bit,
 *   setting it and not clearing it clears ZOL context
 *
 * Vineetg: May 16th, 2008
 *  - r25 now contains the Current Task when in kernel
 *
 * Vineetg: Dec 22, 2007
 *    Minor Surgery of Low Level ISR to make it SMP safe
 *    - MMU_SCRATCH0 Reg used for freeing up r9 in Level 1 ISR
 *    - _current_task is made an array of NR_CPUS
 *    - Access of _current_task wrapped inside a macro so that if hardware
 *       team agrees for a dedicated reg, no other code is touched
 *
 * Amit Bhor, Rahul Trivedi, Kanika Nema, Sameer Dhavale : Codito Tech 2004
 */

#include <linux/errno.h>
#include <linux/linkage.h>	/* {ENTRY,EXIT} */
#include <asm/entry.h>
#include <asm/irqflags.h>

	.cpu A7

;############################ Vector Table #################################

.macro VECTOR  lbl
#if 1   /* Just in case, build breaks */
	j   \lbl
#else
	b   \lbl
	nop
#endif
.endm

	.section .vector, "ax",@progbits
	.align 4

/* Each entry in the vector table must occupy 2 words. Since it is a jump
 * across sections (.vector to .text) we are guaranteed that 'j somewhere'
 * will use the 'j limm' form of the instruction as long as somewhere is in
 * a section other than .vector.
 */

; ********* Critical System Events **********************
VECTOR   res_service             ; 0x0, Reset Vector	(0x0)
VECTOR   mem_service             ; 0x8, Mem exception   (0x1)
VECTOR   instr_service           ; 0x10, Instrn Error   (0x2)

; ******************** Device ISRs **********************
#ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS
VECTOR   handle_interrupt_level2
#else
VECTOR   handle_interrupt_level1
#endif

.rept   28
VECTOR   handle_interrupt_level1 ; Other devices
.endr

/* FOR ARC600: timer = 0x3, uart = 0x8, emac = 0x10 */

; ******************** Exceptions **********************
VECTOR   EV_MachineCheck         ; 0x100, Fatal Machine check   (0x20)
VECTOR   EV_TLBMissI             ; 0x108, Instruction TLB miss  (0x21)
VECTOR   EV_TLBMissD             ; 0x110, Data TLB miss         (0x22)
VECTOR   EV_TLBProtV             ; 0x118, Protection Violation  (0x23)
				 ;         or Misaligned Access
VECTOR   EV_PrivilegeV           ; 0x120, Privilege Violation   (0x24)
VECTOR   EV_Trap                 ; 0x128, Trap exception        (0x25)
VECTOR   EV_Extension            ; 0x130, Extn Instruction Excp (0x26)

.rept   24
VECTOR   reserved                ; Reserved Exceptions
.endr

;##################### Scratch Mem for IRQ stack switching #############

ARCFP_DATA int1_saved_reg
	.align 32
	.type   int1_saved_reg, @object
	.size   int1_saved_reg, 4
int1_saved_reg:
	.zero 4

/* Each Interrupt level needs its own scratch */
ARCFP_DATA int2_saved_reg
	.type   int2_saved_reg, @object
	.size   int2_saved_reg, 4
int2_saved_reg:
	.zero 4

; ---------------------------------------------
	.section .text, "ax",@progbits

reserved:
	flag 1		; Unexpected event, halt

;##################### Interrupt Handling ##############################

#ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS
; ---------------------------------------------
;  Level 2 ISR: Can interrupt a Level 1 ISR
; ---------------------------------------------
ENTRY(handle_interrupt_level2)

	INTERRUPT_PROLOGUE 2

	;------------------------------------------------------
	; if L2 IRQ interrupted a L1 ISR, disable preemption
	;
	; This is to avoid a potential L1-L2-L1 scenario
	;  -L1 IRQ taken
	;  -L2 interrupts L1 (before L1 ISR could run)
	;  -preemption off IRQ, user task in syscall picked to run
	;  -RTIE to userspace
	;	Returns from L2 context fine
	;	But both L1 and L2 re-enabled, so another L1 can be taken
	;	while prev L1 is still unserviced
	;
	;------------------------------------------------------

	; L2 interrupting L1 implies both L2 and L1 active
	; However both A2 and A1 are NOT set in STATUS32, thus
	; need to check STATUS32_L2 to determine if L1 was active

	ld r9, [sp, PT_status32]        ; get statu32_l2 (saved in pt_regs)
	bbit0 r9, STATUS_A1_BIT, 1f     ; L1 not active when L2 IRQ, so normal

	; bump thread_info->preempt_count (Disable preemption)
	GET_CURR_THR_INFO_FROM_SP   r10
	ld      r9, [r10, THREAD_INFO_PREEMPT_COUNT]
	add     r9, r9, 1
	st      r9, [r10, THREAD_INFO_PREEMPT_COUNT]

1:
	;------------------------------------------------------
	; setup params for Linux common ISR and invoke it
	;------------------------------------------------------
	lr  r0, [icause2]
	and r0, r0, 0x1f

	bl.d  @arch_do_IRQ
	mov r1, sp

	mov r8,0x2
	sr r8, [AUX_IRQ_LV12]       ; clear bit in Sticky Status Reg

	b   ret_from_exception

END(handle_interrupt_level2)

#endif

; ---------------------------------------------
; User Mode Memory Bus Error Interrupt Handler
; (Kernel mode memory errors handled via separate exception vectors)
; ---------------------------------------------
ENTRY(mem_service)

	INTERRUPT_PROLOGUE 2

	mov r0, ilink2
	mov r1, sp

	; User process needs to be killed with SIGBUS, but first need to get
	; out of the L2 interrupt context (drop to pure kernel mode) and jump
	; off to "C" code where SIGBUS in enqueued
	lr  r3, [status32]
	bclr r3, r3, STATUS_A2_BIT
	or  r3, r3, (STATUS_E1_MASK|STATUS_E2_MASK)
	sr  r3, [status32_l2]
	mov ilink2, 1f
	rtie
1:
	bl  do_memory_error
	b   ret_from_exception
END(mem_service)

; ---------------------------------------------
;  Level 1 ISR
; ---------------------------------------------
ENTRY(handle_interrupt_level1)

	INTERRUPT_PROLOGUE 1

	lr  r0, [icause1]
	and r0, r0, 0x1f

#ifdef CONFIG_TRACE_IRQFLAGS
	; icause1 needs to be read early, before calling tracing, which
	; can clobber scratch regs, hence use of stack to stash it
	push r0
	TRACE_ASM_IRQ_DISABLE
	pop  r0
#endif

	bl.d  @arch_do_IRQ
	mov r1, sp

	mov r8,0x1
	sr r8, [AUX_IRQ_LV12]       ; clear bit in Sticky Status Reg

	b   ret_from_exception
END(handle_interrupt_level1)

;################### Non TLB Exception Handling #############################

; ---------------------------------------------
; Protection Violation Exception Handler
; ---------------------------------------------

ENTRY(EV_TLBProtV)

	EXCEPTION_PROLOGUE

	mov r2, r10	; ECR set into r10 already
	lr  r0, [efa]	; Faulting Data address (not part of pt_regs saved above)

	; Exception auto-disables further Intr/exceptions.
	; Re-enable them by pretending to return from exception
	; (so rest of handler executes in pure K mode)

	FAKE_RET_FROM_EXCPN

	mov   r1, sp	; Handle to pt_regs

	;------ (5) Type of Protection Violation? ----------
	;
	; ProtV Hardware Exception is triggered for Access Faults of 2 types
	;   -Access Violation	: 00_23_(00|01|02|03)_00
	;			         x  r  w  r+w
	;   -Unaligned Access	: 00_23_04_00
	;
	bbit1 r2, ECR_C_BIT_PROTV_MISALIG_DATA, 4f

	;========= (6a) Access Violation Processing ========
	bl  do_page_fault
	b   ret_from_exception

	;========== (6b) Non aligned access ============
4:

	SAVE_CALLEE_SAVED_USER
	mov r2, sp              ; callee_regs

	bl  do_misaligned_access

	; TBD: optimize - do this only if a callee reg was involved
	; either a dst of emulated LD/ST or src with address-writeback
	RESTORE_CALLEE_SAVED_USER

	b   ret_from_exception

END(EV_TLBProtV)

; Wrapper for Linux page fault handler called from EV_TLBMiss*
; Very similar to ProtV handler case (6a) above, but avoids the extra checks
; for Misaligned access
;
ENTRY(call_do_page_fault)

	EXCEPTION_PROLOGUE
	lr  r0, [efa]	; Faulting Data address
	mov   r1, sp
	FAKE_RET_FROM_EXCPN

	mov blink, ret_from_exception
	b  do_page_fault

END(call_do_page_fault)

;############# Common Handlers for ARCompact and ARCv2 ##############

#include "entry.S"

;############# Return from Intr/Excp/Trap (ARC Specifics) ##############
;
; Restore the saved sys context (common exit-path for EXCPN/IRQ/Trap)
; IRQ shd definitely not happen between now and rtie
; All 2 entry points to here already disable interrupts

.Lrestore_regs:

	# Interrupts are actually disabled from this point on, but will get
	# reenabled after we return from interrupt/exception.
	# But irq tracer needs to be told now...
	TRACE_ASM_IRQ_ENABLE

	lr	r10, [status32]

	; Restore REG File. In case multiple Events outstanding,
	; use the same priority as rtie: EXCPN, L2 IRQ, L1 IRQ, None
	; Note that we use realtime STATUS32 (not pt_regs->status32) to
	; decide that.

	and.f	0, r10, (STATUS_A1_MASK|STATUS_A2_MASK)
	bz	.Lexcep_or_pure_K_ret

	; Returning from Interrupts (Level 1 or 2)

#ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS

	; Level 2 interrupt return Path - from hardware standpoint
	bbit0  r10, STATUS_A2_BIT, not_level2_interrupt

	;------------------------------------------------------------------
	; However the context returning might not have taken L2 intr itself
	; e.g. Task'A' user-code -> L2 intr -> schedule -> 'B' user-code ret
	; Special considerations needed for the context which took L2 intr

	ld   r9, [sp, PT_event]        ; Ensure this is L2 intr context
	brne r9, event_IRQ2, 149f

	;------------------------------------------------------------------
	; if L2 IRQ interrupted an L1 ISR,  we'd disabled preemption earlier
	; so that sched doesn't move to new task, causing L1 to be delayed
	; undeterministically. Now that we've achieved that, let's reset
	; things to what they were, before returning from L2 context
	;----------------------------------------------------------------

	ld r9, [sp, PT_status32]       ; get statu32_l2 (saved in pt_regs)
	bbit0 r9, STATUS_A1_BIT, 149f  ; L1 not active when L2 IRQ, so normal

	; decrement thread_info->preempt_count (re-enable preemption)
	GET_CURR_THR_INFO_FROM_SP   r10
	ld      r9, [r10, THREAD_INFO_PREEMPT_COUNT]

	; paranoid check, given A1 was active when A2 happened, preempt count
	; must not be 0 because we would have incremented it.
	; If this does happen we simply HALT as it means a BUG !!!
	cmp     r9, 0
	bnz     2f
	flag 1

2:
	sub     r9, r9, 1
	st      r9, [r10, THREAD_INFO_PREEMPT_COUNT]

149:
	INTERRUPT_EPILOGUE 2	; return from level 2 interrupt
debug_marker_l2:
	rtie

not_level2_interrupt:

#endif

	INTERRUPT_EPILOGUE 1	; return from level 1 interrupt
debug_marker_l1:
	rtie

.Lexcep_or_pure_K_ret:

	;this case is for syscalls or Exceptions or pure kernel mode

	EXCEPTION_EPILOGUE
debug_marker_syscall:
	rtie

END(ret_from_exception)


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Common Low Level Interrupts/Traps/Exceptions(non-TLB) Handling for ARC
 * (included from entry-<isa>.S
 *
 * Copyright (C) 2014-15 Synopsys, Inc. (www.synopsys.com)
 * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
 */

/*------------------------------------------------------------------
 *    Function                            ABI
 *------------------------------------------------------------------
 *
 *  Arguments                           r0 - r7
 *  Caller Saved Registers              r0 - r12
 *  Callee Saved Registers              r13- r25
 *  Global Pointer (gp)                 r26
 *  Frame Pointer (fp)                  r27
 *  Stack Pointer (sp)                  r28
 *  Branch link register (blink)        r31
 *------------------------------------------------------------------
 */

;################### Special Sys Call Wrappers ##########################

ENTRY(sys_clone_wrapper)
	SAVE_CALLEE_SAVED_USER
	bl  @sys_clone
	DISCARD_CALLEE_SAVED_USER

	GET_CURR_THR_INFO_FLAGS   r10
	and.f 0, r10, _TIF_SYSCALL_WORK
	bnz   tracesys_exit

	b .Lret_from_system_call
END(sys_clone_wrapper)

ENTRY(sys_clone3_wrapper)
	SAVE_CALLEE_SAVED_USER
	bl  @sys_clone3
	DISCARD_CALLEE_SAVED_USER

	GET_CURR_THR_INFO_FLAGS   r10
	and.f 0, r10, _TIF_SYSCALL_WORK
	bnz   tracesys_exit

	b .Lret_from_system_call
END(sys_clone3_wrapper)

ENTRY(ret_from_fork)
	; when the forked child comes here from the __switch_to function
	; r0 has the last task pointer.
	; put last task in scheduler queue
	jl   @schedule_tail

	ld   r9, [sp, PT_status32]
	brne r9, 0, 1f

	jl.d [r14]		; kernel thread entry point
	mov  r0, r13		; (see PF_KTHREAD block in copy_thread)

1:
	; Return to user space
	; 1. Any forked task (Reach here via BRne above)
	; 2. First ever init task (Reach here via return from JL above)
	;    This is the historic "kernel_execve" use-case, to return to init
	;    user mode, in a round about way since that is always done from
	;    a kernel thread which is executed via JL above but always returns
	;    out whenever kernel_execve (now inline do_fork()) is involved
	b    ret_from_exception
END(ret_from_fork)

;################### Non TLB Exception Handling #############################

; ---------------------------------------------
; Instruction Error Exception Handler
; ---------------------------------------------

ENTRY(instr_service)

	EXCEPTION_PROLOGUE

	lr  r0, [efa]
	mov r1, sp

	FAKE_RET_FROM_EXCPN

	bl  do_insterror_or_kprobe
	b   ret_from_exception
END(instr_service)

; ---------------------------------------------
; Machine Check Exception Handler
; ---------------------------------------------

ENTRY(EV_MachineCheck)

	EXCEPTION_PROLOGUE

	lr  r2, [ecr]
	lr  r0, [efa]
	mov r1, sp

	; MC excpetions disable MMU
	ARC_MMU_REENABLE r3

	lsr  	r3, r2, 8
	bmsk 	r3, r3, 7
	brne    r3, ECR_C_MCHK_DUP_TLB, 1f

	bl      do_tlb_overlap_fault
	b       ret_from_exception

1:
	; DEAD END: can't do much, display Regs and HALT
	SAVE_CALLEE_SAVED_USER

	GET_CURR_TASK_FIELD_PTR   TASK_THREAD, r10
	st  sp, [r10, THREAD_CALLEE_REG]

	j  do_machine_check_fault

END(EV_MachineCheck)

; ---------------------------------------------
; Privilege Violation Exception Handler
; ---------------------------------------------
ENTRY(EV_PrivilegeV)

	EXCEPTION_PROLOGUE

	lr  r0, [efa]
	mov r1, sp

	FAKE_RET_FROM_EXCPN

	bl  do_privilege_fault
	b   ret_from_exception
END(EV_PrivilegeV)

; ---------------------------------------------
; Extension Instruction Exception Handler
; ---------------------------------------------
ENTRY(EV_Extension)

	EXCEPTION_PROLOGUE

	lr  r0, [efa]
	mov r1, sp

	FAKE_RET_FROM_EXCPN

	bl  do_extension_fault
	b   ret_from_exception
END(EV_Extension)

;################ Trap Handling (Syscall, Breakpoint) ##################

; ---------------------------------------------
; syscall Tracing
; ---------------------------------------------
tracesys:
	; save EFA in case tracer wants the PC of traced task
	; using ERET won't work since next-PC has already committed
	GET_CURR_TASK_FIELD_PTR   TASK_THREAD, r11
	st  r12, [r11, THREAD_FAULT_ADDR]	; thread.fault_address

	; PRE Sys Call Ptrace hook
	mov r0, sp			; pt_regs needed
	bl  @syscall_trace_entry

	; Tracing code now returns the syscall num (orig or modif)
	mov r8, r0

	; Do the Sys Call as we normally would.
	; Validate the Sys Call number
	cmp     r8,  NR_syscalls - 1
	mov.hi  r0, -ENOSYS
	bhi     tracesys_exit

	; Restore the sys-call args. Mere invocation of the hook abv could have
	; clobbered them (since they are in scratch regs). The tracer could also
	; have deliberately changed the syscall args: r0-r7
	ld  r0, [sp, PT_r0]
	ld  r1, [sp, PT_r1]
	ld  r2, [sp, PT_r2]
	ld  r3, [sp, PT_r3]
	ld  r4, [sp, PT_r4]
	ld  r5, [sp, PT_r5]
	ld  r6, [sp, PT_r6]
	ld  r7, [sp, PT_r7]
	ld.as   r9, [sys_call_table, r8]
	jl      [r9]        ; Entry into Sys Call Handler

tracesys_exit:
	st  r0, [sp, PT_r0]     ; sys call return value in pt_regs

	;POST Sys Call Ptrace Hook
	mov r0, sp		; pt_regs needed
	bl  @syscall_trace_exit
	b   ret_from_exception ; NOT ret_from_system_call at is saves r0 which
	; we'd done before calling post hook above

; ---------------------------------------------
; Breakpoint TRAP
; ---------------------------------------------
trap_with_param:
	mov r0, r12	; EFA in case ptracer/gdb wants stop_pc
	mov r1, sp

	; Save callee regs in case gdb wants to have a look
	; SP will grow up by size of CALLEE Reg-File
	; NOTE: clobbers r12
	SAVE_CALLEE_SAVED_USER

	; save location of saved Callee Regs @ thread_struct->pc
	GET_CURR_TASK_FIELD_PTR   TASK_THREAD, r10
	st  sp, [r10, THREAD_CALLEE_REG]

	; Call the trap handler
	bl  do_non_swi_trap

	; unwind stack to discard Callee saved Regs
	DISCARD_CALLEE_SAVED_USER

	b   ret_from_exception

; ---------------------------------------------
; syscall TRAP
; ABI: (r0-r7) upto 8 args, (r8) syscall number
; ---------------------------------------------

ENTRY(EV_Trap)

	EXCEPTION_PROLOGUE

	lr  r12, [efa]

	FAKE_RET_FROM_EXCPN

	;============ TRAP 1   :breakpoints
	; Check ECR for trap with arg (PROLOGUE ensures r10 has ECR)
	bmsk.f 0, r10, 7
	bnz    trap_with_param

	;============ TRAP  (no param): syscall top level

	; If syscall tracing ongoing, invoke pre-post-hooks
	GET_CURR_THR_INFO_FLAGS   r10
	and.f 0, r10, _TIF_SYSCALL_WORK
	bnz   tracesys  ; this never comes back

	;============ Normal syscall case

	; syscall num shd not exceed the total system calls avail
	cmp     r8,  NR_syscalls - 1
	mov.hi  r0, -ENOSYS
	bhi     .Lret_from_system_call

	; Offset into the syscall_table and call handler
	ld.as   r9,[sys_call_table, r8]
	jl      [r9]        ; Entry into Sys Call Handler

.Lret_from_system_call:

	st  r0, [sp, PT_r0]     ; sys call return value in pt_regs

	; fall through to ret_from_exception
END(EV_Trap)

;############# Return from Intr/Excp/Trap (Linux Specifics) ##############
;
; If ret to user mode do we need to handle signals, schedule() et al.

ENTRY(ret_from_exception)

	; Pre-{IRQ,Trap,Exception} K/U mode from pt_regs->status32
	ld  r8, [sp, PT_status32]   ; returning to User/Kernel Mode

	bbit0  r8, STATUS_U_BIT, resume_kernel_mode

	; Before returning to User mode check-for-and-complete any pending work
	; such as rescheduling/signal-delivery etc.
resume_user_mode_begin:

	; Disable IRQs to ensures that chk for pending work itself is atomic
	; (and we don't end up missing a NEED_RESCHED/SIGPENDING due to an
	; interim IRQ).
	IRQ_DISABLE	r10

	; Fast Path return to user mode if no pending work
	GET_CURR_THR_INFO_FLAGS   r9
	and.f  0,  r9, _TIF_WORK_MASK
	bz     .Lrestore_regs

	; --- (Slow Path #1) task preemption ---
	bbit0  r9, TIF_NEED_RESCHED, .Lchk_pend_signals
	mov    blink, resume_user_mode_begin  ; tail-call to U mode ret chks
	j      @schedule 	; BTST+Bnz causes relo error in link

.Lchk_pend_signals:
	IRQ_ENABLE	r10

	; --- (Slow Path #2) pending signal  ---
	mov r0, sp	; pt_regs for arg to do_signal()/do_notify_resume()

	GET_CURR_THR_INFO_FLAGS   r9
	and.f  0,  r9, _TIF_SIGPENDING|_TIF_NOTIFY_SIGNAL
	bz .Lchk_notify_resume

	; Normal Trap/IRQ entry only saves Scratch (caller-saved) regs
	; in pt_reg since the "C" ABI (kernel code) will automatically
	; save/restore callee-saved regs.
	;
	; However, here we need to explicitly save callee regs because
	; (i)  If this signal causes coredump - full regfile needed
	; (ii) If signal is SIGTRAP/SIGSTOP, task is being traced thus
	;      tracer might call PEEKUSR(CALLEE reg)
	;
	; NOTE: SP will grow up by size of CALLEE Reg-File
	SAVE_CALLEE_SAVED_USER		; clobbers r12

	; save location of saved Callee Regs @ thread_struct->callee
	GET_CURR_TASK_FIELD_PTR   TASK_THREAD, r10
	st  sp, [r10, THREAD_CALLEE_REG]

	bl  @do_signal

	; Ideally we want to discard the Callee reg above, however if this was
	; a tracing signal, tracer could have done a POKEUSR(CALLEE reg)
	RESTORE_CALLEE_SAVED_USER

	b      resume_user_mode_begin	; loop back to start of U mode ret

	; --- (Slow Path #3) notify_resume ---
.Lchk_notify_resume:
	btst   r9, TIF_NOTIFY_RESUME
	blnz   @do_notify_resume
	b      resume_user_mode_begin	; unconditionally back to U mode ret chks
					; for single exit point from this block

resume_kernel_mode:

	; Disable Interrupts from this point on
	; CONFIG_PREEMPTION: This is a must for preempt_schedule_irq()
	; !CONFIG_PREEMPTION: To ensure restore_regs is intr safe
	IRQ_DISABLE	r9

#ifdef CONFIG_PREEMPTION

	; Can't preempt if preemption disabled
	GET_CURR_THR_INFO_FROM_SP   r10
	ld  r8, [r10, THREAD_INFO_PREEMPT_COUNT]
	brne  r8, 0, .Lrestore_regs

	; check if this task's NEED_RESCHED flag set
	ld  r9, [r10, THREAD_INFO_FLAGS]
	bbit0  r9, TIF_NEED_RESCHED, .Lrestore_regs

	; Invoke PREEMPTION
	jl      preempt_schedule_irq

	; preempt_schedule_irq() always returns with IRQ disabled
#endif

	b	.Lrestore_regs

##### DONT ADD CODE HERE - .Lrestore_regs actually follows in entry-<isa>.S


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * fpu.c - save/restore of Floating Point Unit Registers on task switch
 *
 * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
 */

#include <linux/sched.h>
#include <asm/fpu.h>

#ifdef CONFIG_ISA_ARCOMPACT

/*
 * To save/restore FPU regs, simplest scheme would use LR/SR insns.
 * However since SR serializes the pipeline, an alternate "hack" can be used
 * which uses the FPU Exchange insn (DEXCL) to r/w FPU regs.
 *
 * Store to 64bit dpfp1 reg from a pair of core regs:
 *   dexcl1 0, r1, r0  ; where r1:r0 is the 64 bit val
 *
 * Read from dpfp1 into pair of core regs (w/o clobbering dpfp1)
 *   mov_s    r3, 0
 *   daddh11  r1, r3, r3   ; get "hi" into r1 (dpfp1 unchanged)
 *   dexcl1   r0, r1, r3   ; get "low" into r0 (dpfp1 low clobbered)
 *   dexcl1    0, r1, r0   ; restore dpfp1 to orig value
 *
 * However we can tweak the read, so that read-out of outgoing task's FPU regs
 * and write of incoming task's regs happen in one shot. So all the work is
 * done before context switch
 */

void fpu_save_restore(struct task_struct *prev, struct task_struct *next)
{
	unsigned int *saveto = &prev->thread.fpu.aux_dpfp[0].l;
	unsigned int *readfrom = &next->thread.fpu.aux_dpfp[0].l;

	const unsigned int zero = 0;

	__asm__ __volatile__(
		"daddh11  %0, %2, %2\n"
		"dexcl1   %1, %3, %4\n"
		: "=&r" (*(saveto + 1)), /* early clobber must here */
		  "=&r" (*(saveto))
		: "r" (zero), "r" (*(readfrom + 1)), "r" (*(readfrom))
	);

	__asm__ __volatile__(
		"daddh22  %0, %2, %2\n"
		"dexcl2   %1, %3, %4\n"
		: "=&r"(*(saveto + 3)),	/* early clobber must here */
		  "=&r"(*(saveto + 2))
		: "r" (zero), "r" (*(readfrom + 3)), "r" (*(readfrom + 2))
	);
}

#else

void fpu_init_task(struct pt_regs *regs)
{
	const unsigned int fwe = 0x80000000;

	/* default rounding mode */
	write_aux_reg(ARC_REG_FPU_CTRL, 0x100);

	/* Initialize to zero: setting requires FWE be set */
	write_aux_reg(ARC_REG_FPU_STATUS, fwe);
}

void fpu_save_restore(struct task_struct *prev, struct task_struct *next)
{
	struct arc_fpu *save = &prev->thread.fpu;
	struct arc_fpu *restore = &next->thread.fpu;
	const unsigned int fwe = 0x80000000;

	save->ctrl = read_aux_reg(ARC_REG_FPU_CTRL);
	save->status = read_aux_reg(ARC_REG_FPU_STATUS);

	write_aux_reg(ARC_REG_FPU_CTRL, restore->ctrl);
	write_aux_reg(ARC_REG_FPU_STATUS, (fwe | restore->status));
}

#endif


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * ARC CPU startup Code
 *
 * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
 *
 * Vineetg: Dec 2007
 *  -Check if we are running on Simulator or on real hardware
 *      to skip certain things during boot on simulator
 */

#include <linux/linkage.h>
#include <asm/asm-offsets.h>
#include <asm/entry.h>
#include <asm/arcregs.h>
#include <asm/cache.h>
#include <asm/dsp-impl.h>
#include <asm/irqflags.h>

.macro CPU_EARLY_SETUP

	; Setting up Vectror Table (in case exception happens in early boot
	sr	@_int_vec_base_lds, [AUX_INTR_VEC_BASE]

	; Disable I-cache/D-cache if kernel so configured
	lr	r5, [ARC_REG_IC_BCR]
	breq    r5, 0, 1f		; I$ doesn't exist
	lr	r5, [ARC_REG_IC_CTRL]
#ifdef CONFIG_ARC_HAS_ICACHE
	bclr	r5, r5, 0		; 0 - Enable, 1 is Disable
#else
	bset	r5, r5, 0		; I$ exists, but is not used
#endif
	sr	r5, [ARC_REG_IC_CTRL]

1:
	lr	r5, [ARC_REG_DC_BCR]
	breq    r5, 0, 1f		; D$ doesn't exist
	lr	r5, [ARC_REG_DC_CTRL]
	bclr	r5, r5, 6		; Invalidate (discard w/o wback)
#ifdef CONFIG_ARC_HAS_DCACHE
	bclr	r5, r5, 0		; Enable (+Inv)
#else
	bset	r5, r5, 0		; Disable (+Inv)
#endif
	sr	r5, [ARC_REG_DC_CTRL]

1:

#ifdef CONFIG_ISA_ARCV2
	; Unaligned access is disabled at reset, so re-enable early as
	; gcc 7.3.1 (ARC GNU 2018.03) onwards generates unaligned access
	; by default
	lr	r5, [status32]
#ifdef CONFIG_ARC_USE_UNALIGNED_MEM_ACCESS
	bset	r5, r5, STATUS_AD_BIT
#else
	; Although disabled at reset, bootloader might have enabled it
	bclr	r5, r5, STATUS_AD_BIT
#endif
	kflag	r5

#ifdef CONFIG_ARC_LPB_DISABLE
	lr	r5, [ARC_REG_LPB_BUILD]
	breq    r5, 0, 1f		; LPB doesn't exist
	mov	r5, 1
	sr	r5, [ARC_REG_LPB_CTRL]
1:
#endif /* CONFIG_ARC_LPB_DISABLE */

	/* On HSDK, CCMs need to remapped super early */
#ifdef CONFIG_ARC_SOC_HSDK
	mov	r6, 0x60000000
	lr	r5, [ARC_REG_ICCM_BUILD]
	breq	r5, 0, 1f
	sr	r6, [ARC_REG_AUX_ICCM]
1:
	lr	r5, [ARC_REG_DCCM_BUILD]
	breq	r5, 0, 2f
	sr	r6, [ARC_REG_AUX_DCCM]
2:
#endif	/* CONFIG_ARC_SOC_HSDK */

#endif	/* CONFIG_ISA_ARCV2 */

	; Config DSP_CTRL properly, so kernel may use integer multiply,
	; multiply-accumulate, and divide operations
	DSP_EARLY_INIT
.endm

	.section .init.text, "ax",@progbits

;----------------------------------------------------------------
; Default Reset Handler (jumped into from Reset vector)
; - Don't clobber r0,r1,r2 as they might have u-boot provided args
; - Platforms can override this weak version if needed
;----------------------------------------------------------------
WEAK(res_service)
	j	stext
END(res_service)

;----------------------------------------------------------------
; Kernel Entry point
;----------------------------------------------------------------
ENTRY(stext)

	CPU_EARLY_SETUP

#ifdef CONFIG_SMP
	GET_CPU_ID  r5
	cmp	r5, 0
	mov.nz	r0, r5
	bz	.Lmaster_proceed

	; Non-Masters wait for Master to boot enough and bring them up
	; when they resume, tail-call to entry point
	mov	blink, @first_lines_of_secondary
	j	arc_platform_smp_wait_to_boot

.Lmaster_proceed:
#endif

	; Clear BSS before updating any globals
	; XXX: use ZOL here
	mov	r5, __bss_start
	sub	r6, __bss_stop, r5
	lsr.f	lp_count, r6, 2
	lpnz	1f
	st.ab   0, [r5, 4]
1:

	; Uboot - kernel ABI
	;    r0 = [0] No uboot interaction, [1] cmdline in r2, [2] DTB in r2
	;    r1 = magic number (always zero as of now)
	;    r2 = pointer to uboot provided cmdline or external DTB in mem
	; These are handled later in handle_uboot_args()
	st	r0, [@uboot_tag]
	st      r1, [@uboot_magic]
	st	r2, [@uboot_arg]

	; setup "current" tsk and optionally cache it in dedicated r25
	mov	r9, @init_task
	SET_CURR_TASK_ON_CPU  r9, r0	; r9 = tsk, r0 = scratch

	; setup stack (fp, sp)
	mov	fp, 0

	; tsk->thread_info is really a PAGE, whose bottom hoists stack
	GET_TSK_STACK_BASE r9, sp	; r9 = tsk, sp = stack base(output)

	j	start_kernel	; "C" entry point
END(stext)

#ifdef CONFIG_SMP
;----------------------------------------------------------------
;     First lines of code run by secondary before jumping to 'C'
;----------------------------------------------------------------
	.section .text, "ax",@progbits
ENTRY(first_lines_of_secondary)

	; setup per-cpu idle task as "current" on this CPU
	ld	r0, [@secondary_idle_tsk]
	SET_CURR_TASK_ON_CPU  r0, r1

	; setup stack (fp, sp)
	mov	fp, 0

	; set it's stack base to tsk->thread_info bottom
	GET_TSK_STACK_BASE r0, sp

	j	start_kernel_secondary
END(first_lines_of_secondary)
#endif


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2014 Synopsys, Inc. (www.synopsys.com)
 */

#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/irqdomain.h>
#include <linux/irqchip.h>
#include <asm/irq.h>

#define NR_EXCEPTIONS	16

struct bcr_irq_arcv2 {
#ifdef CONFIG_CPU_BIG_ENDIAN
	unsigned int pad:3, firq:1, prio:4, exts:8, irqs:8, ver:8;
#else
	unsigned int ver:8, irqs:8, exts:8, prio:4, firq:1, pad:3;
#endif
};

/*
 * Early Hardware specific Interrupt setup
 * -Called very early (start_kernel -> setup_arch -> setup_processor)
 * -Platform Independent (must for any ARC Core)
 * -Needed for each CPU (hence not foldable into init_IRQ)
 */
void arc_init_IRQ(void)
{
	unsigned int tmp, irq_prio, i;
	struct bcr_irq_arcv2 irq_bcr;

	struct aux_irq_ctrl {
#ifdef CONFIG_CPU_BIG_ENDIAN
		unsigned int res3:18, save_idx_regs:1, res2:1,
			     save_u_to_u:1, save_lp_regs:1, save_blink:1,
			     res:4, save_nr_gpr_pairs:5;
#else
		unsigned int save_nr_gpr_pairs:5, res:4,
			     save_blink:1, save_lp_regs:1, save_u_to_u:1,
			     res2:1, save_idx_regs:1, res3:18;
#endif
	} ictrl;

	*(unsigned int *)&ictrl = 0;

#ifndef CONFIG_ARC_IRQ_NO_AUTOSAVE
	ictrl.save_nr_gpr_pairs = 6;	/* r0 to r11 (r12 saved manually) */
	ictrl.save_blink = 1;
	ictrl.save_lp_regs = 1;		/* LP_COUNT, LP_START, LP_END */
	ictrl.save_u_to_u = 0;		/* user ctxt saved on kernel stack */
	ictrl.save_idx_regs = 1;	/* JLI, LDI, EI */
#endif

	WRITE_AUX(AUX_IRQ_CTRL, ictrl);

	/*
	 * ARCv2 core intc provides multiple interrupt priorities (upto 16).
	 * Typical builds though have only two levels (0-high, 1-low)
	 * Linux by default uses lower prio 1 for most irqs, reserving 0 for
	 * NMI style interrupts in future (say perf)
	 */

	READ_BCR(ARC_REG_IRQ_BCR, irq_bcr);

	irq_prio = irq_bcr.prio;	/* Encoded as N-1 for N levels */
	pr_info("archs-intc\t: %d priority levels (default %d)%s\n",
		irq_prio + 1, ARCV2_IRQ_DEF_PRIO,
		irq_bcr.firq ? " FIRQ (not used)":"");

	/*
	 * Set a default priority for all available interrupts to prevent
	 * switching of register banks if Fast IRQ and multiple register banks
	 * are supported by CPU.
	 * Also disable private-per-core IRQ lines so faulty external HW won't
	 * trigger interrupt that kernel is not ready to handle.
	 */
	for (i = NR_EXCEPTIONS; i < irq_bcr.irqs + NR_EXCEPTIONS; i++) {
		write_aux_reg(AUX_IRQ_SELECT, i);
		write_aux_reg(AUX_IRQ_PRIORITY, ARCV2_IRQ_DEF_PRIO);

		/*
		 * Only mask cpu private IRQs here.
		 * "common" interrupts are masked at IDU, otherwise it would
		 * need to be unmasked at each cpu, with IPIs
		 */
		if (i < FIRST_EXT_IRQ)
			write_aux_reg(AUX_IRQ_ENABLE, 0);
	}

	/* setup status32, don't enable intr yet as kernel doesn't want */
	tmp = read_aux_reg(ARC_REG_STATUS32);
	tmp |= ARCV2_IRQ_DEF_PRIO << 1;
	tmp &= ~STATUS_IE_MASK;
	asm volatile("kflag %0	\n"::"r"(tmp));
}

static void arcv2_irq_mask(struct irq_data *data)
{
	write_aux_reg(AUX_IRQ_SELECT, data->hwirq);
	write_aux_reg(AUX_IRQ_ENABLE, 0);
}

static void arcv2_irq_unmask(struct irq_data *data)
{
	write_aux_reg(AUX_IRQ_SELECT, data->hwirq);
	write_aux_reg(AUX_IRQ_ENABLE, 1);
}

void arcv2_irq_enable(struct irq_data *data)
{
	/* set default priority */
	write_aux_reg(AUX_IRQ_SELECT, data->hwirq);
	write_aux_reg(AUX_IRQ_PRIORITY, ARCV2_IRQ_DEF_PRIO);

	/*
	 * hw auto enables (linux unmask) all by default
	 * So no need to do IRQ_ENABLE here
	 * XXX: However OSCI LAN need it
	 */
	write_aux_reg(AUX_IRQ_ENABLE, 1);
}

static struct irq_chip arcv2_irq_chip = {
	.name           = "ARCv2 core Intc",
	.irq_mask	= arcv2_irq_mask,
	.irq_unmask	= arcv2_irq_unmask,
	.irq_enable	= arcv2_irq_enable
};

static int arcv2_irq_map(struct irq_domain *d, unsigned int irq,
			 irq_hw_number_t hw)
{
	/*
	 * core intc IRQs [16, 23]:
	 * Statically assigned always private-per-core (Timers, WDT, IPI, PCT)
	 */
	if (hw < FIRST_EXT_IRQ) {
		/*
		 * A subsequent request_percpu_irq() fails if percpu_devid is
		 * not set. That in turns sets NOAUTOEN, meaning each core needs
		 * to call enable_percpu_irq()
		 */
		irq_set_percpu_devid(irq);
		irq_set_chip_and_handler(irq, &arcv2_irq_chip, handle_percpu_irq);
	} else {
		irq_set_chip_and_handler(irq, &arcv2_irq_chip, handle_level_irq);
	}

	return 0;
}

static const struct irq_domain_ops arcv2_irq_ops = {
	.xlate = irq_domain_xlate_onecell,
	.map = arcv2_irq_map,
};

static int __init
init_onchip_IRQ(struct device_node *intc, struct device_node *parent)
{
	struct irq_domain *root_domain;
	struct bcr_irq_arcv2 irq_bcr;
	unsigned int nr_cpu_irqs;

	READ_BCR(ARC_REG_IRQ_BCR, irq_bcr);
	nr_cpu_irqs = irq_bcr.irqs + NR_EXCEPTIONS;

	if (parent)
		panic("DeviceTree incore intc not a root irq controller\n");

	root_domain = irq_domain_add_linear(intc, nr_cpu_irqs, &arcv2_irq_ops, NULL);
	if (!root_domain)
		panic("root irq domain not avail\n");

	/*
	 * Needed for primary domain lookup to succeed
	 * This is a primary irqchip, and can never have a parent
	 */
	irq_set_default_host(root_domain);

#ifdef CONFIG_SMP
	irq_create_mapping(root_domain, IPI_IRQ);
#endif
	irq_create_mapping(root_domain, SOFTIRQ_IRQ);

	return 0;
}

IRQCHIP_DECLARE(arc_intc, "snps,archs-intc", init_onchip_IRQ);


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2011-12 Synopsys, Inc. (www.synopsys.com)
 */

#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/irqdomain.h>
#include <linux/irqchip.h>
#include <asm/irq.h>

#define NR_CPU_IRQS	32	/* number of irq lines coming in */
#define TIMER0_IRQ	3	/* Fixed by ISA */

/*
 * Early Hardware specific Interrupt setup
 * -Platform independent, needed for each CPU (not foldable into init_IRQ)
 * -Called very early (start_kernel -> setup_arch -> setup_processor)
 *
 * what it does ?
 * -Optionally, setup the High priority Interrupts as Level 2 IRQs
 */
void arc_init_IRQ(void)
{
	unsigned int level_mask = 0, i;

       /* Is timer high priority Interrupt (Level2 in ARCompact jargon) */
	level_mask |= IS_ENABLED(CONFIG_ARC_COMPACT_IRQ_LEVELS) << TIMER0_IRQ;

	/*
	 * Write to register, even if no LV2 IRQs configured to reset it
	 * in case bootloader had mucked with it
	 */
	write_aux_reg(AUX_IRQ_LEV, level_mask);

	if (level_mask)
		pr_info("Level-2 interrupts bitset %x\n", level_mask);

	/*
	 * Disable all IRQ lines so faulty external hardware won't
	 * trigger interrupt that kernel is not ready to handle.
	 */
	for (i = TIMER0_IRQ; i < NR_CPU_IRQS; i++) {
		unsigned int ienb;

		ienb = read_aux_reg(AUX_IENABLE);
		ienb &= ~(1 << i);
		write_aux_reg(AUX_IENABLE, ienb);
	}
}

/*
 * ARC700 core includes a simple on-chip intc supporting
 * -per IRQ enable/disable
 * -2 levels of interrupts (high/low)
 * -all interrupts being level triggered
 *
 * To reduce platform code, we assume all IRQs directly hooked-up into intc.
 * Platforms with external intc, hence cascaded IRQs, are free to over-ride
 * below, per IRQ.
 */

static void arc_irq_mask(struct irq_data *data)
{
	unsigned int ienb;

	ienb = read_aux_reg(AUX_IENABLE);
	ienb &= ~(1 << data->hwirq);
	write_aux_reg(AUX_IENABLE, ienb);
}

static void arc_irq_unmask(struct irq_data *data)
{
	unsigned int ienb;

	ienb = read_aux_reg(AUX_IENABLE);
	ienb |= (1 << data->hwirq);
	write_aux_reg(AUX_IENABLE, ienb);
}

static struct irq_chip onchip_intc = {
	.name           = "ARC In-core Intc",
	.irq_mask	= arc_irq_mask,
	.irq_unmask	= arc_irq_unmask,
};

static int arc_intc_domain_map(struct irq_domain *d, unsigned int irq,
			       irq_hw_number_t hw)
{
	switch (hw) {
	case TIMER0_IRQ:
		irq_set_percpu_devid(irq);
		irq_set_chip_and_handler(irq, &onchip_intc, handle_percpu_irq);
		break;
	default:
		irq_set_chip_and_handler(irq, &onchip_intc, handle_level_irq);
	}
	return 0;
}

static const struct irq_domain_ops arc_intc_domain_ops = {
	.xlate = irq_domain_xlate_onecell,
	.map = arc_intc_domain_map,
};

static int __init
init_onchip_IRQ(struct device_node *intc, struct device_node *parent)
{
	struct irq_domain *root_domain;

	if (parent)
		panic("DeviceTree incore intc not a root irq controller\n");

	root_domain = irq_domain_add_linear(intc, NR_CPU_IRQS,
					    &arc_intc_domain_ops, NULL);
	if (!root_domain)
		panic("root irq domain not avail\n");

	/*
	 * Needed for primary domain lookup to succeed
	 * This is a primary irqchip, and can never have a parent
	 */
	irq_set_default_host(root_domain);

	return 0;
}

IRQCHIP_DECLARE(arc_intc, "snps,arc700-intc", init_onchip_IRQ);

/*
 * arch_local_irq_enable - Enable interrupts.
 *
 * 1. Explicitly called to re-enable interrupts
 * 2. Implicitly called from spin_unlock_irq, write_unlock_irq etc
 *    which maybe in hard ISR itself
 *
 * Semantics of this function change depending on where it is called from:
 *
 * -If called from hard-ISR, it must not invert interrupt priorities
 *  e.g. suppose TIMER is high priority (Level 2) IRQ
 *    Time hard-ISR, timer_interrupt( ) calls spin_unlock_irq several times.
 *    Here local_irq_enable( ) shd not re-enable lower priority interrupts
 * -If called from soft-ISR, it must re-enable all interrupts
 *    soft ISR are low priority jobs which can be very slow, thus all IRQs
 *    must be enabled while they run.
 *    Now hardware context wise we may still be in L2 ISR (not done rtie)
 *    still we must re-enable both L1 and L2 IRQs
 *  Another twist is prev scenario with flow being
 *     L1 ISR ==> interrupted by L2 ISR  ==> L2 soft ISR
 *     here we must not re-enable Ll as prev Ll Interrupt's h/w context will get
 *     over-written (this is deficiency in ARC700 Interrupt mechanism)
 */

#ifdef CONFIG_ARC_COMPACT_IRQ_LEVELS	/* Complex version for 2 IRQ levels */

void arch_local_irq_enable(void)
{
	unsigned long flags = arch_local_save_flags();

	if (flags & STATUS_A2_MASK)
		flags |= STATUS_E2_MASK;
	else if (flags & STATUS_A1_MASK)
		flags |= STATUS_E1_MASK;

	arch_local_irq_restore(flags);
}

EXPORT_SYMBOL(arch_local_irq_enable);
#endif


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2011-12 Synopsys, Inc. (www.synopsys.com)
 */

#include <linux/interrupt.h>
#include <linux/irqchip.h>
#include <asm/mach_desc.h>

#include <asm/irq_regs.h>
#include <asm/smp.h>

/*
 * Late Interrupt system init called from start_kernel for Boot CPU only
 *
 * Since slab must already be initialized, platforms can start doing any
 * needed request_irq( )s
 */
void __init init_IRQ(void)
{
	/*
	 * process the entire interrupt tree in one go
	 * Any external intc will be setup provided DT chains them
	 * properly
	 */
	irqchip_init();

#ifdef CONFIG_SMP
	/* a SMP H/w block could do IPI IRQ request here */
	if (plat_smp_ops.init_per_cpu)
		plat_smp_ops.init_per_cpu(smp_processor_id());
#endif

	if (machine_desc->init_per_cpu)
		machine_desc->init_per_cpu(smp_processor_id());
}

/*
 * "C" Entry point for any ARC ISR, called from low level vector handler
 * @irq is the vector number read from ICAUSE reg of on-chip intc
 */
void arch_do_IRQ(unsigned int hwirq, struct pt_regs *regs)
{
	struct pt_regs *old_regs;

	irq_enter();
	old_regs = set_irq_regs(regs);
	generic_handle_domain_irq(NULL, hwirq);
	set_irq_regs(old_regs);
	irq_exit();
}


code:
// SPDX-License-Identifier: GPL-2.0

#include <linux/kernel.h>
#include <linux/jump_label.h>

#include "asm/cacheflush.h"

#define JUMPLABEL_ERR	"ARC: jump_label: ERROR: "

/* Halt system on fatal error to make debug easier */
#define arc_jl_fatal(format...)						\
({									\
	pr_err(JUMPLABEL_ERR format);					\
	BUG();								\
})

static inline u32 arc_gen_nop(void)
{
	/* 1x 32bit NOP in middle endian */
	return 0x7000264a;
}

/*
 * Atomic update of patched instruction is only available if this
 * instruction doesn't cross L1 cache line boundary. You can read about
 * the way we achieve this in arc/include/asm/jump_label.h
 */
static inline void instruction_align_assert(void *addr, int len)
{
	unsigned long a = (unsigned long)addr;

	if ((a >> L1_CACHE_SHIFT) != ((a + len - 1) >> L1_CACHE_SHIFT))
		arc_jl_fatal("instruction (addr %px) cross L1 cache line border",
			     addr);
}

/*
 * ARCv2 'Branch unconditionally' instruction:
 * 00000ssssssssss1SSSSSSSSSSNRtttt
 * s S[n:0] lower bits signed immediate (number is bitfield size)
 * S S[m:n+1] upper bits signed immediate (number is bitfield size)
 * t S[24:21] upper bits signed immediate (branch unconditionally far)
 * N N <.d> delay slot mode
 * R R Reserved
 */
static inline u32 arc_gen_branch(jump_label_t pc, jump_label_t target)
{
	u32 instruction_l, instruction_r;
	u32 pcl = pc & GENMASK(31, 2);
	u32 u_offset = target - pcl;
	u32 s, S, t;

	/*
	 * Offset in 32-bit branch instruction must to fit into s25.
	 * Something is terribly broken if we get such huge offset within one
	 * function.
	 */
	if ((s32)u_offset < -16777216 || (s32)u_offset > 16777214)
		arc_jl_fatal("gen branch with offset (%d) not fit in s25",
			     (s32)u_offset);

	/*
	 * All instructions are aligned by 2 bytes so we should never get offset
	 * here which is not 2 bytes aligned.
	 */
	if (u_offset & 0x1)
		arc_jl_fatal("gen branch with offset (%d) unaligned to 2 bytes",
			     (s32)u_offset);

	s = (u_offset >> 1)  & GENMASK(9, 0);
	S = (u_offset >> 11) & GENMASK(9, 0);
	t = (u_offset >> 21) & GENMASK(3, 0);

	/* 00000ssssssssss1 */
	instruction_l = (s << 1) | 0x1;
	/* SSSSSSSSSSNRtttt */
	instruction_r = (S << 6) | t;

	return (instruction_r << 16) | (instruction_l & GENMASK(15, 0));
}

void arch_jump_label_transform(struct jump_entry *entry,
			       enum jump_label_type type)
{
	jump_label_t *instr_addr = (jump_label_t *)entry->code;
	u32 instr;

	instruction_align_assert(instr_addr, JUMP_LABEL_NOP_SIZE);

	if (type == JUMP_LABEL_JMP)
		instr = arc_gen_branch(entry->code, entry->target);
	else
		instr = arc_gen_nop();

	WRITE_ONCE(*instr_addr, instr);
	flush_icache_range(entry->code, entry->code + JUMP_LABEL_NOP_SIZE);
}

#ifdef CONFIG_ARC_DBG_JUMP_LABEL
#define SELFTEST_MSG	"ARC: instruction generation self-test: "

struct arc_gen_branch_testdata {
	jump_label_t pc;
	jump_label_t target_address;
	u32 expected_instr;
};

static __init int branch_gen_test(const struct arc_gen_branch_testdata *test)
{
	u32 instr_got;

	instr_got = arc_gen_branch(test->pc, test->target_address);
	if (instr_got == test->expected_instr)
		return 0;

	pr_err(SELFTEST_MSG "FAIL:\n arc_gen_branch(0x%08x, 0x%08x) != 0x%08x, got 0x%08x\n",
	       test->pc, test->target_address,
	       test->expected_instr, instr_got);

	return -EFAULT;
}

/*
 * Offset field in branch instruction is not continuous. Test all
 * available offset field and sign combinations. Test data is generated
 * from real working code.
 */
static const struct arc_gen_branch_testdata arcgenbr_test_data[] __initconst = {
	{0x90007548, 0x90007514, 0xffcf07cd}, /* tiny (-52) offs */
	{0x9000c9c0, 0x9000c782, 0xffcf05c3}, /* tiny (-574) offs */
	{0x9000cc1c, 0x9000c782, 0xffcf0367}, /* tiny (-1178) offs */
	{0x9009dce0, 0x9009d106, 0xff8f0427}, /* small (-3034) offs */
	{0x9000f5de, 0x90007d30, 0xfc0f0755}, /* big  (-30892) offs */
	{0x900a2444, 0x90035f64, 0xc9cf0321}, /* huge (-443616) offs */
	{0x90007514, 0x9000752c, 0x00000019}, /* tiny (+24) offs */
	{0x9001a578, 0x9001a77a, 0x00000203}, /* tiny (+514) offs */
	{0x90031ed8, 0x90032634, 0x0000075d}, /* tiny (+1884) offs */
	{0x9008c7f2, 0x9008d3f0, 0x00400401}, /* small (+3072) offs */
	{0x9000bb38, 0x9003b340, 0x17c00009}, /* big  (+194568) offs */
	{0x90008f44, 0x90578d80, 0xb7c2063d}  /* huge (+5701180) offs */
};

static __init int instr_gen_test(void)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(arcgenbr_test_data); i++)
		if (branch_gen_test(&arcgenbr_test_data[i]))
			return -EFAULT;

	pr_info(SELFTEST_MSG "OK\n");

	return 0;
}
early_initcall(instr_gen_test);

#endif /* CONFIG_ARC_DBG_JUMP_LABEL */


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * kgdb support for ARC
 *
 * Copyright (C) 2012 Synopsys, Inc. (www.synopsys.com)
 */

#include <linux/kgdb.h>
#include <linux/sched.h>
#include <linux/sched/task_stack.h>
#include <asm/disasm.h>
#include <asm/cacheflush.h>

static void to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs,
			struct callee_regs *cregs)
{
	int regno;

	for (regno = 0; regno <= 26; regno++)
		gdb_regs[_R0 + regno] = get_reg(regno, kernel_regs, cregs);

	for (regno = 27; regno < GDB_MAX_REGS; regno++)
		gdb_regs[regno] = 0;

	gdb_regs[_FP]		= kernel_regs->fp;
	gdb_regs[__SP]		= kernel_regs->sp;
	gdb_regs[_BLINK]	= kernel_regs->blink;
	gdb_regs[_RET]		= kernel_regs->ret;
	gdb_regs[_STATUS32]	= kernel_regs->status32;
	gdb_regs[_LP_COUNT]	= kernel_regs->lp_count;
	gdb_regs[_LP_END]	= kernel_regs->lp_end;
	gdb_regs[_LP_START]	= kernel_regs->lp_start;
	gdb_regs[_BTA]		= kernel_regs->bta;
	gdb_regs[_STOP_PC]	= kernel_regs->ret;
}

static void from_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs,
			struct callee_regs *cregs)
{
	int regno;

	for (regno = 0; regno <= 26; regno++)
		set_reg(regno, gdb_regs[regno + _R0], kernel_regs, cregs);

	kernel_regs->fp		= gdb_regs[_FP];
	kernel_regs->sp		= gdb_regs[__SP];
	kernel_regs->blink	= gdb_regs[_BLINK];
	kernel_regs->ret	= gdb_regs[_RET];
	kernel_regs->status32	= gdb_regs[_STATUS32];
	kernel_regs->lp_count	= gdb_regs[_LP_COUNT];
	kernel_regs->lp_end	= gdb_regs[_LP_END];
	kernel_regs->lp_start	= gdb_regs[_LP_START];
	kernel_regs->bta	= gdb_regs[_BTA];
}

void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs)
{
	to_gdb_regs(gdb_regs, kernel_regs, (struct callee_regs *)
		current->thread.callee_reg);
}

void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *kernel_regs)
{
	from_gdb_regs(gdb_regs, kernel_regs, (struct callee_regs *)
		current->thread.callee_reg);
}

void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs,
				 struct task_struct *task)
{
	if (task)
		to_gdb_regs(gdb_regs, task_pt_regs(task),
			(struct callee_regs *) task->thread.callee_reg);
}

struct single_step_data_t {
	uint16_t opcode[2];
	unsigned long address[2];
	int is_branch;
	int armed;
} single_step_data;

static void undo_single_step(struct pt_regs *regs)
{
	if (single_step_data.armed) {
		int i;

		for (i = 0; i < (single_step_data.is_branch ? 2 : 1); i++) {
			memcpy((void *) single_step_data.address[i],
				&single_step_data.opcode[i],
				BREAK_INSTR_SIZE);

			flush_icache_range(single_step_data.address[i],
				single_step_data.address[i] +
				BREAK_INSTR_SIZE);
		}
		single_step_data.armed = 0;
	}
}

static void place_trap(unsigned long address, void *save)
{
	memcpy(save, (void *) address, BREAK_INSTR_SIZE);
	memcpy((void *) address, &arch_kgdb_ops.gdb_bpt_instr,
		BREAK_INSTR_SIZE);
	flush_icache_range(address, address + BREAK_INSTR_SIZE);
}

static void do_single_step(struct pt_regs *regs)
{
	single_step_data.is_branch = disasm_next_pc((unsigned long)
		regs->ret, regs, (struct callee_regs *)
		current->thread.callee_reg,
		&single_step_data.address[0],
		&single_step_data.address[1]);

	place_trap(single_step_data.address[0], &single_step_data.opcode[0]);

	if (single_step_data.is_branch) {
		place_trap(single_step_data.address[1],
			&single_step_data.opcode[1]);
	}

	single_step_data.armed++;
}

int kgdb_arch_handle_exception(int e_vector, int signo, int err_code,
			       char *remcomInBuffer, char *remcomOutBuffer,
			       struct pt_regs *regs)
{
	unsigned long addr;
	char *ptr;

	undo_single_step(regs);

	switch (remcomInBuffer[0]) {
	case 's':
	case 'c':
		ptr = &remcomInBuffer[1];
		if (kgdb_hex2long(&ptr, &addr))
			regs->ret = addr;
		fallthrough;

	case 'D':
	case 'k':
		atomic_set(&kgdb_cpu_doing_single_step, -1);

		if (remcomInBuffer[0] == 's') {
			do_single_step(regs);
			atomic_set(&kgdb_cpu_doing_single_step,
				   smp_processor_id());
		}

		return 0;
	}
	return -1;
}

int kgdb_arch_init(void)
{
	single_step_data.armed = 0;
	return 0;
}

void kgdb_trap(struct pt_regs *regs)
{
	/* trap_s 3 is used for breakpoints that overwrite existing
	 * instructions, while trap_s 4 is used for compiled breakpoints.
	 *
	 * with trap_s 3 breakpoints the original instruction needs to be
	 * restored and continuation needs to start at the location of the
	 * breakpoint.
	 *
	 * with trap_s 4 (compiled) breakpoints, continuation needs to
	 * start after the breakpoint.
	 */
	if (regs->ecr_param == 3)
		instruction_pointer(regs) -= BREAK_INSTR_SIZE;

	kgdb_handle_exception(1, SIGTRAP, 0, regs);
}

void kgdb_arch_exit(void)
{
}

void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip)
{
	instruction_pointer(regs) = ip;
}

void kgdb_call_nmi_hook(void *ignored)
{
	/* Default implementation passes get_irq_regs() but we don't */
	kgdb_nmicallback(raw_smp_processor_id(), NULL);
}

const struct kgdb_arch arch_kgdb_ops = {
	/* breakpoint instruction: TRAP_S 0x3 */
#ifdef CONFIG_CPU_BIG_ENDIAN
	.gdb_bpt_instr		= {0x78, 0x7e},
#else
	.gdb_bpt_instr		= {0x7e, 0x78},
#endif
};


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
 */

#include <linux/types.h>
#include <linux/kprobes.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kdebug.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <asm/cacheflush.h>
#include <asm/current.h>
#include <asm/disasm.h>

#define MIN_STACK_SIZE(addr)	min((unsigned long)MAX_STACK_SIZE, \
		(unsigned long)current_thread_info() + THREAD_SIZE - (addr))

DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);

int __kprobes arch_prepare_kprobe(struct kprobe *p)
{
	/* Attempt to probe at unaligned address */
	if ((unsigned long)p->addr & 0x01)
		return -EINVAL;

	/* Address should not be in exception handling code */

	p->ainsn.is_short = is_short_instr((unsigned long)p->addr);
	p->opcode = *p->addr;

	return 0;
}

void __kprobes arch_arm_kprobe(struct kprobe *p)
{
	*p->addr = UNIMP_S_INSTRUCTION;

	flush_icache_range((unsigned long)p->addr,
			   (unsigned long)p->addr + sizeof(kprobe_opcode_t));
}

void __kprobes arch_disarm_kprobe(struct kprobe *p)
{
	*p->addr = p->opcode;

	flush_icache_range((unsigned long)p->addr,
			   (unsigned long)p->addr + sizeof(kprobe_opcode_t));
}

void __kprobes arch_remove_kprobe(struct kprobe *p)
{
	arch_disarm_kprobe(p);

	/* Can we remove the kprobe in the middle of kprobe handling? */
	if (p->ainsn.t1_addr) {
		*(p->ainsn.t1_addr) = p->ainsn.t1_opcode;

		flush_icache_range((unsigned long)p->ainsn.t1_addr,
				   (unsigned long)p->ainsn.t1_addr +
				   sizeof(kprobe_opcode_t));

		p->ainsn.t1_addr = NULL;
	}

	if (p->ainsn.t2_addr) {
		*(p->ainsn.t2_addr) = p->ainsn.t2_opcode;

		flush_icache_range((unsigned long)p->ainsn.t2_addr,
				   (unsigned long)p->ainsn.t2_addr +
				   sizeof(kprobe_opcode_t));

		p->ainsn.t2_addr = NULL;
	}
}

static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)
{
	kcb->prev_kprobe.kp = kprobe_running();
	kcb->prev_kprobe.status = kcb->kprobe_status;
}

static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb)
{
	__this_cpu_write(current_kprobe, kcb->prev_kprobe.kp);
	kcb->kprobe_status = kcb->prev_kprobe.status;
}

static inline void __kprobes set_current_kprobe(struct kprobe *p)
{
	__this_cpu_write(current_kprobe, p);
}

static void __kprobes resume_execution(struct kprobe *p, unsigned long addr,
				       struct pt_regs *regs)
{
	/* Remove the trap instructions inserted for single step and
	 * restore the original instructions
	 */
	if (p->ainsn.t1_addr) {
		*(p->ainsn.t1_addr) = p->ainsn.t1_opcode;

		flush_icache_range((unsigned long)p->ainsn.t1_addr,
				   (unsigned long)p->ainsn.t1_addr +
				   sizeof(kprobe_opcode_t));

		p->ainsn.t1_addr = NULL;
	}

	if (p->ainsn.t2_addr) {
		*(p->ainsn.t2_addr) = p->ainsn.t2_opcode;

		flush_icache_range((unsigned long)p->ainsn.t2_addr,
				   (unsigned long)p->ainsn.t2_addr +
				   sizeof(kprobe_opcode_t));

		p->ainsn.t2_addr = NULL;
	}

	return;
}

static void __kprobes setup_singlestep(struct kprobe *p, struct pt_regs *regs)
{
	unsigned long next_pc;
	unsigned long tgt_if_br = 0;
	int is_branch;
	unsigned long bta;

	/* Copy the opcode back to the kprobe location and execute the
	 * instruction. Because of this we will not be able to get into the
	 * same kprobe until this kprobe is done
	 */
	*(p->addr) = p->opcode;

	flush_icache_range((unsigned long)p->addr,
			   (unsigned long)p->addr + sizeof(kprobe_opcode_t));

	/* Now we insert the trap at the next location after this instruction to
	 * single step. If it is a branch we insert the trap at possible branch
	 * targets
	 */

	bta = regs->bta;

	if (regs->status32 & 0x40) {
		/* We are in a delay slot with the branch taken */

		next_pc = bta & ~0x01;

		if (!p->ainsn.is_short) {
			if (bta & 0x01)
				regs->blink += 2;
			else {
				/* Branch not taken */
				next_pc += 2;

				/* next pc is taken from bta after executing the
				 * delay slot instruction
				 */
				regs->bta += 2;
			}
		}

		is_branch = 0;
	} else
		is_branch =
		    disasm_next_pc((unsigned long)p->addr, regs,
			(struct callee_regs *) current->thread.callee_reg,
			&next_pc, &tgt_if_br);

	p->ainsn.t1_addr = (kprobe_opcode_t *) next_pc;
	p->ainsn.t1_opcode = *(p->ainsn.t1_addr);
	*(p->ainsn.t1_addr) = TRAP_S_2_INSTRUCTION;

	flush_icache_range((unsigned long)p->ainsn.t1_addr,
			   (unsigned long)p->ainsn.t1_addr +
			   sizeof(kprobe_opcode_t));

	if (is_branch) {
		p->ainsn.t2_addr = (kprobe_opcode_t *) tgt_if_br;
		p->ainsn.t2_opcode = *(p->ainsn.t2_addr);
		*(p->ainsn.t2_addr) = TRAP_S_2_INSTRUCTION;

		flush_icache_range((unsigned long)p->ainsn.t2_addr,
				   (unsigned long)p->ainsn.t2_addr +
				   sizeof(kprobe_opcode_t));
	}
}

int __kprobes arc_kprobe_handler(unsigned long addr, struct pt_regs *regs)
{
	struct kprobe *p;
	struct kprobe_ctlblk *kcb;

	preempt_disable();

	kcb = get_kprobe_ctlblk();
	p = get_kprobe((unsigned long *)addr);

	if (p) {
		/*
		 * We have reentered the kprobe_handler, since another kprobe
		 * was hit while within the handler, we save the original
		 * kprobes and single step on the instruction of the new probe
		 * without calling any user handlers to avoid recursive
		 * kprobes.
		 */
		if (kprobe_running()) {
			save_previous_kprobe(kcb);
			set_current_kprobe(p);
			kprobes_inc_nmissed_count(p);
			setup_singlestep(p, regs);
			kcb->kprobe_status = KPROBE_REENTER;
			return 1;
		}

		set_current_kprobe(p);
		kcb->kprobe_status = KPROBE_HIT_ACTIVE;

		/* If we have no pre-handler or it returned 0, we continue with
		 * normal processing. If we have a pre-handler and it returned
		 * non-zero - which means user handler setup registers to exit
		 * to another instruction, we must skip the single stepping.
		 */
		if (!p->pre_handler || !p->pre_handler(p, regs)) {
			setup_singlestep(p, regs);
			kcb->kprobe_status = KPROBE_HIT_SS;
		} else {
			reset_current_kprobe();
			preempt_enable_no_resched();
		}

		return 1;
	}

	/* no_kprobe: */
	preempt_enable_no_resched();
	return 0;
}

static int __kprobes arc_post_kprobe_handler(unsigned long addr,
					 struct pt_regs *regs)
{
	struct kprobe *cur = kprobe_running();
	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();

	if (!cur)
		return 0;

	resume_execution(cur, addr, regs);

	/* Rearm the kprobe */
	arch_arm_kprobe(cur);

	/*
	 * When we return from trap instruction we go to the next instruction
	 * We restored the actual instruction in resume_exectuiont and we to
	 * return to the same address and execute it
	 */
	regs->ret = addr;

	if ((kcb->kprobe_status != KPROBE_REENTER) && cur->post_handler) {
		kcb->kprobe_status = KPROBE_HIT_SSDONE;
		cur->post_handler(cur, regs, 0);
	}

	if (kcb->kprobe_status == KPROBE_REENTER) {
		restore_previous_kprobe(kcb);
		goto out;
	}

	reset_current_kprobe();

out:
	preempt_enable_no_resched();
	return 1;
}

/*
 * Fault can be for the instruction being single stepped or for the
 * pre/post handlers in the module.
 * This is applicable for applications like user probes, where we have the
 * probe in user space and the handlers in the kernel
 */

int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned long trapnr)
{
	struct kprobe *cur = kprobe_running();
	struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();

	switch (kcb->kprobe_status) {
	case KPROBE_HIT_SS:
	case KPROBE_REENTER:
		/*
		 * We are here because the instruction being single stepped
		 * caused the fault. We reset the current kprobe and allow the
		 * exception handler as if it is regular exception. In our
		 * case it doesn't matter because the system will be halted
		 */
		resume_execution(cur, (unsigned long)cur->addr, regs);

		if (kcb->kprobe_status == KPROBE_REENTER)
			restore_previous_kprobe(kcb);
		else
			reset_current_kprobe();

		preempt_enable_no_resched();
		break;

	case KPROBE_HIT_ACTIVE:
	case KPROBE_HIT_SSDONE:
		/*
		 * We are here because the instructions in the pre/post handler
		 * caused the fault.
		 */

		/*
		 * In case the user-specified fault handler returned zero,
		 * try to fix up.
		 */
		if (fixup_exception(regs))
			return 1;

		/*
		 * fixup_exception() could not handle it,
		 * Let do_page_fault() fix it.
		 */
		break;

	default:
		break;
	}
	return 0;
}

int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
				       unsigned long val, void *data)
{
	struct die_args *args = data;
	unsigned long addr = args->err;
	int ret = NOTIFY_DONE;

	switch (val) {
	case DIE_IERR:
		if (arc_kprobe_handler(addr, args->regs))
			return NOTIFY_STOP;
		break;

	case DIE_TRAP:
		if (arc_post_kprobe_handler(addr, args->regs))
			return NOTIFY_STOP;
		break;

	default:
		break;
	}

	return ret;
}

static void __used kretprobe_trampoline_holder(void)
{
	__asm__ __volatile__(".global __kretprobe_trampoline\n"
			     "__kretprobe_trampoline:\n"
			     "nop\n");
}

void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
				      struct pt_regs *regs)
{

	ri->ret_addr = (kprobe_opcode_t *) regs->blink;
	ri->fp = NULL;

	/* Replace the return addr with trampoline addr */
	regs->blink = (unsigned long)&__kretprobe_trampoline;
}

static int __kprobes trampoline_probe_handler(struct kprobe *p,
					      struct pt_regs *regs)
{
	regs->ret = __kretprobe_trampoline_handler(regs, NULL);

	/* By returning a non zero value, we are telling the kprobe handler
	 * that we don't want the post_handler to run
	 */
	return 1;
}

static struct kprobe trampoline_p = {
	.addr = (kprobe_opcode_t *) &__kretprobe_trampoline,
	.pre_handler = trampoline_probe_handler
};

int __init arch_init_kprobes(void)
{
	/* Registering the trampoline code for the kret probe */
	return register_kprobe(&trampoline_p);
}

int __kprobes arch_trampoline_kprobe(struct kprobe *p)
{
	if (p->addr == (kprobe_opcode_t *) &__kretprobe_trampoline)
		return 1;

	return 0;
}

void trap_is_kprobe(unsigned long address, struct pt_regs *regs)
{
	notify_die(DIE_TRAP, "kprobe_trap", regs, address, 0, SIGTRAP);
}


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * ARC ARConnect (MultiCore IP) support (formerly known as MCIP)
 *
 * Copyright (C) 2013 Synopsys, Inc. (www.synopsys.com)
 */

#include <linux/smp.h>
#include <linux/irq.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/spinlock.h>
#include <soc/arc/mcip.h>
#include <asm/irqflags-arcv2.h>
#include <asm/setup.h>

static DEFINE_RAW_SPINLOCK(mcip_lock);

#ifdef CONFIG_SMP

static char smp_cpuinfo_buf[128];

/*
 * Set mask to halt GFRC if any online core in SMP cluster is halted.
 * Only works for ARC HS v3.0+, on earlier versions has no effect.
 */
static void mcip_update_gfrc_halt_mask(int cpu)
{
	struct bcr_generic gfrc;
	unsigned long flags;
	u32 gfrc_halt_mask;

	READ_BCR(ARC_REG_GFRC_BUILD, gfrc);

	/*
	 * CMD_GFRC_SET_CORE and CMD_GFRC_READ_CORE commands were added in
	 * GFRC 0x3 version.
	 */
	if (gfrc.ver < 0x3)
		return;

	raw_spin_lock_irqsave(&mcip_lock, flags);

	__mcip_cmd(CMD_GFRC_READ_CORE, 0);
	gfrc_halt_mask = read_aux_reg(ARC_REG_MCIP_READBACK);
	gfrc_halt_mask |= BIT(cpu);
	__mcip_cmd_data(CMD_GFRC_SET_CORE, 0, gfrc_halt_mask);

	raw_spin_unlock_irqrestore(&mcip_lock, flags);
}

static void mcip_update_debug_halt_mask(int cpu)
{
	u32 mcip_mask = 0;
	unsigned long flags;

	raw_spin_lock_irqsave(&mcip_lock, flags);

	/*
	 * mcip_mask is same for CMD_DEBUG_SET_SELECT and CMD_DEBUG_SET_MASK
	 * commands. So read it once instead of reading both CMD_DEBUG_READ_MASK
	 * and CMD_DEBUG_READ_SELECT.
	 */
	__mcip_cmd(CMD_DEBUG_READ_SELECT, 0);
	mcip_mask = read_aux_reg(ARC_REG_MCIP_READBACK);

	mcip_mask |= BIT(cpu);

	__mcip_cmd_data(CMD_DEBUG_SET_SELECT, 0, mcip_mask);
	/*
	 * Parameter specified halt cause:
	 * STATUS32[H]/actionpoint/breakpoint/self-halt
	 * We choose all of them (0xF).
	 */
	__mcip_cmd_data(CMD_DEBUG_SET_MASK, 0xF, mcip_mask);

	raw_spin_unlock_irqrestore(&mcip_lock, flags);
}

static void mcip_setup_per_cpu(int cpu)
{
	struct mcip_bcr mp;

	READ_BCR(ARC_REG_MCIP_BCR, mp);

	smp_ipi_irq_setup(cpu, IPI_IRQ);
	smp_ipi_irq_setup(cpu, SOFTIRQ_IRQ);

	/* Update GFRC halt mask as new CPU came online */
	if (mp.gfrc)
		mcip_update_gfrc_halt_mask(cpu);

	/* Update MCIP debug mask as new CPU came online */
	if (mp.dbg)
		mcip_update_debug_halt_mask(cpu);
}

static void mcip_ipi_send(int cpu)
{
	unsigned long flags;
	int ipi_was_pending;

	/* ARConnect can only send IPI to others */
	if (unlikely(cpu == raw_smp_processor_id())) {
		arc_softirq_trigger(SOFTIRQ_IRQ);
		return;
	}

	raw_spin_lock_irqsave(&mcip_lock, flags);

	/*
	 * If receiver already has a pending interrupt, elide sending this one.
	 * Linux cross core calling works well with concurrent IPIs
	 * coalesced into one
	 * see arch/arc/kernel/smp.c: ipi_send_msg_one()
	 */
	__mcip_cmd(CMD_INTRPT_READ_STATUS, cpu);
	ipi_was_pending = read_aux_reg(ARC_REG_MCIP_READBACK);
	if (!ipi_was_pending)
		__mcip_cmd(CMD_INTRPT_GENERATE_IRQ, cpu);

	raw_spin_unlock_irqrestore(&mcip_lock, flags);
}

static void mcip_ipi_clear(int irq)
{
	unsigned int cpu, c;
	unsigned long flags;

	if (unlikely(irq == SOFTIRQ_IRQ)) {
		arc_softirq_clear(irq);
		return;
	}

	raw_spin_lock_irqsave(&mcip_lock, flags);

	/* Who sent the IPI */
	__mcip_cmd(CMD_INTRPT_CHECK_SOURCE, 0);

	cpu = read_aux_reg(ARC_REG_MCIP_READBACK);	/* 1,2,4,8... */

	/*
	 * In rare case, multiple concurrent IPIs sent to same target can
	 * possibly be coalesced by MCIP into 1 asserted IRQ, so @cpus can be
	 * "vectored" (multiple bits sets) as opposed to typical single bit
	 */
	do {
		c = __ffs(cpu);			/* 0,1,2,3 */
		__mcip_cmd(CMD_INTRPT_GENERATE_ACK, c);
		cpu &= ~(1U << c);
	} while (cpu);

	raw_spin_unlock_irqrestore(&mcip_lock, flags);
}

static void mcip_probe_n_setup(void)
{
	struct mcip_bcr mp;

	READ_BCR(ARC_REG_MCIP_BCR, mp);

	sprintf(smp_cpuinfo_buf,
		"Extn [SMP]\t: ARConnect (v%d): %d cores with %s%s%s%s\n",
		mp.ver, mp.num_cores,
		IS_AVAIL1(mp.ipi, "IPI "),
		IS_AVAIL1(mp.idu, "IDU "),
		IS_AVAIL1(mp.dbg, "DEBUG "),
		IS_AVAIL1(mp.gfrc, "GFRC"));

	cpuinfo_arc700[0].extn.gfrc = mp.gfrc;
}

struct plat_smp_ops plat_smp_ops = {
	.info		= smp_cpuinfo_buf,
	.init_early_smp	= mcip_probe_n_setup,
	.init_per_cpu	= mcip_setup_per_cpu,
	.ipi_send	= mcip_ipi_send,
	.ipi_clear	= mcip_ipi_clear,
};

#endif

/***************************************************************************
 * ARCv2 Interrupt Distribution Unit (IDU)
 *
 * Connects external "COMMON" IRQs to core intc, providing:
 *  -dynamic routing (IRQ affinity)
 *  -load balancing (Round Robin interrupt distribution)
 *  -1:N distribution
 *
 * It physically resides in the MCIP hw block
 */

#include <linux/irqchip.h>
#include <linux/of.h>
#include <linux/of_irq.h>

/*
 * Set the DEST for @cmn_irq to @cpu_mask (1 bit per core)
 */
static void idu_set_dest(unsigned int cmn_irq, unsigned int cpu_mask)
{
	__mcip_cmd_data(CMD_IDU_SET_DEST, cmn_irq, cpu_mask);
}

static void idu_set_mode(unsigned int cmn_irq, bool set_lvl, unsigned int lvl,
			 bool set_distr, unsigned int distr)
{
	union {
		unsigned int word;
		struct {
			unsigned int distr:2, pad:2, lvl:1, pad2:27;
		};
	} data;

	data.word = __mcip_cmd_read(CMD_IDU_READ_MODE, cmn_irq);
	if (set_distr)
		data.distr = distr;
	if (set_lvl)
		data.lvl = lvl;
	__mcip_cmd_data(CMD_IDU_SET_MODE, cmn_irq, data.word);
}

static void idu_irq_mask_raw(irq_hw_number_t hwirq)
{
	unsigned long flags;

	raw_spin_lock_irqsave(&mcip_lock, flags);
	__mcip_cmd_data(CMD_IDU_SET_MASK, hwirq, 1);
	raw_spin_unlock_irqrestore(&mcip_lock, flags);
}

static void idu_irq_mask(struct irq_data *data)
{
	idu_irq_mask_raw(data->hwirq);
}

static void idu_irq_unmask(struct irq_data *data)
{
	unsigned long flags;

	raw_spin_lock_irqsave(&mcip_lock, flags);
	__mcip_cmd_data(CMD_IDU_SET_MASK, data->hwirq, 0);
	raw_spin_unlock_irqrestore(&mcip_lock, flags);
}

static void idu_irq_ack(struct irq_data *data)
{
	unsigned long flags;

	raw_spin_lock_irqsave(&mcip_lock, flags);
	__mcip_cmd(CMD_IDU_ACK_CIRQ, data->hwirq);
	raw_spin_unlock_irqrestore(&mcip_lock, flags);
}

static void idu_irq_mask_ack(struct irq_data *data)
{
	unsigned long flags;

	raw_spin_lock_irqsave(&mcip_lock, flags);
	__mcip_cmd_data(CMD_IDU_SET_MASK, data->hwirq, 1);
	__mcip_cmd(CMD_IDU_ACK_CIRQ, data->hwirq);
	raw_spin_unlock_irqrestore(&mcip_lock, flags);
}

static int
idu_irq_set_affinity(struct irq_data *data, const struct cpumask *cpumask,
		     bool force)
{
	unsigned long flags;
	cpumask_t online;
	unsigned int destination_bits;
	unsigned int distribution_mode;

	/* errout if no online cpu per @cpumask */
	if (!cpumask_and(&online, cpumask, cpu_online_mask))
		return -EINVAL;

	raw_spin_lock_irqsave(&mcip_lock, flags);

	destination_bits = cpumask_bits(&online)[0];
	idu_set_dest(data->hwirq, destination_bits);

	if (ffs(destination_bits) == fls(destination_bits))
		distribution_mode = IDU_M_DISTRI_DEST;
	else
		distribution_mode = IDU_M_DISTRI_RR;

	idu_set_mode(data->hwirq, false, 0, true, distribution_mode);

	raw_spin_unlock_irqrestore(&mcip_lock, flags);

	return IRQ_SET_MASK_OK;
}

static int idu_irq_set_type(struct irq_data *data, u32 type)
{
	unsigned long flags;

	/*
	 * ARCv2 IDU HW does not support inverse polarity, so these are the
	 * only interrupt types supported.
	 */
	if (type & ~(IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH))
		return -EINVAL;

	raw_spin_lock_irqsave(&mcip_lock, flags);

	idu_set_mode(data->hwirq, true,
		     type & IRQ_TYPE_EDGE_RISING ? IDU_M_TRIG_EDGE :
						   IDU_M_TRIG_LEVEL,
		     false, 0);

	raw_spin_unlock_irqrestore(&mcip_lock, flags);

	return 0;
}

static void idu_irq_enable(struct irq_data *data)
{
	/*
	 * By default send all common interrupts to all available online CPUs.
	 * The affinity of common interrupts in IDU must be set manually since
	 * in some cases the kernel will not call irq_set_affinity() by itself:
	 *   1. When the kernel is not configured with support of SMP.
	 *   2. When the kernel is configured with support of SMP but upper
	 *      interrupt controllers does not support setting of the affinity
	 *      and cannot propagate it to IDU.
	 */
	idu_irq_set_affinity(data, cpu_online_mask, false);
	idu_irq_unmask(data);
}

static struct irq_chip idu_irq_chip = {
	.name			= "MCIP IDU Intc",
	.irq_mask		= idu_irq_mask,
	.irq_unmask		= idu_irq_unmask,
	.irq_ack		= idu_irq_ack,
	.irq_mask_ack		= idu_irq_mask_ack,
	.irq_enable		= idu_irq_enable,
	.irq_set_type		= idu_irq_set_type,
#ifdef CONFIG_SMP
	.irq_set_affinity       = idu_irq_set_affinity,
#endif

};

static void idu_cascade_isr(struct irq_desc *desc)
{
	struct irq_domain *idu_domain = irq_desc_get_handler_data(desc);
	struct irq_chip *core_chip = irq_desc_get_chip(desc);
	irq_hw_number_t core_hwirq = irqd_to_hwirq(irq_desc_get_irq_data(desc));
	irq_hw_number_t idu_hwirq = core_hwirq - FIRST_EXT_IRQ;

	chained_irq_enter(core_chip, desc);
	generic_handle_domain_irq(idu_domain, idu_hwirq);
	chained_irq_exit(core_chip, desc);
}

static int idu_irq_map(struct irq_domain *d, unsigned int virq, irq_hw_number_t hwirq)
{
	irq_set_chip_and_handler(virq, &idu_irq_chip, handle_level_irq);
	irq_set_status_flags(virq, IRQ_MOVE_PCNTXT);

	return 0;
}

static const struct irq_domain_ops idu_irq_ops = {
	.xlate	= irq_domain_xlate_onetwocell,
	.map	= idu_irq_map,
};

/*
 * [16, 23]: Statically assigned always private-per-core (Timers, WDT, IPI)
 * [24, 23+C]: If C > 0 then "C" common IRQs
 * [24+C, N]: Not statically assigned, private-per-core
 */

static int __init
idu_of_init(struct device_node *intc, struct device_node *parent)
{
	struct irq_domain *domain;
	int nr_irqs;
	int i, virq;
	struct mcip_bcr mp;
	struct mcip_idu_bcr idu_bcr;

	READ_BCR(ARC_REG_MCIP_BCR, mp);

	if (!mp.idu)
		panic("IDU not detected, but DeviceTree using it");

	READ_BCR(ARC_REG_MCIP_IDU_BCR, idu_bcr);
	nr_irqs = mcip_idu_bcr_to_nr_irqs(idu_bcr);

	pr_info("MCIP: IDU supports %u common irqs\n", nr_irqs);

	domain = irq_domain_add_linear(intc, nr_irqs, &idu_irq_ops, NULL);

	/* Parent interrupts (core-intc) are already mapped */

	for (i = 0; i < nr_irqs; i++) {
		/* Mask all common interrupts by default */
		idu_irq_mask_raw(i);

		/*
		 * Return parent uplink IRQs (towards core intc) 24,25,.....
		 * this step has been done before already
		 * however we need it to get the parent virq and set IDU handler
		 * as first level isr
		 */
		virq = irq_create_mapping(NULL, i + FIRST_EXT_IRQ);
		BUG_ON(!virq);
		irq_set_chained_handler_and_data(virq, idu_cascade_isr, domain);
	}

	__mcip_cmd(CMD_IDU_ENABLE, 0);

	return 0;
}
IRQCHIP_DECLARE(arcv2_idu_intc, "snps,archs-idu-intc", idu_of_init);