Commit 0603839b authored by Huacai Chen's avatar Huacai Chen

LoongArch: Add exception/interrupt handling

Add the exception and interrupt handling machanism for basic LoongArch
support.
Reviewed-by: default avatarWANG Xuerui <git@xen0n.name>
Reviewed-by: default avatarJiaxun Yang <jiaxun.yang@flygoat.com>
Signed-off-by: default avatarHuacai Chen <chenhuacai@loongson.cn>
parent 628c3bb4
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_BRANCH_H
#define _ASM_BRANCH_H
#include <asm/ptrace.h>
static inline unsigned long exception_era(struct pt_regs *regs)
{
return regs->csr_era;
}
static inline int compute_return_era(struct pt_regs *regs)
{
regs->csr_era += 4;
return 0;
}
#endif /* _ASM_BRANCH_H */
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ASM_BUG_H
#define __ASM_BUG_H
#include <linux/compiler.h>
#ifdef CONFIG_BUG
#include <asm/break.h>
static inline void __noreturn BUG(void)
{
__asm__ __volatile__("break %0" : : "i" (BRK_BUG));
unreachable();
}
#define HAVE_ARCH_BUG
#endif
#include <asm-generic/bug.h>
#endif /* __ASM_BUG_H */
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef ARCH_LOONGARCH_ENTRY_COMMON_H
#define ARCH_LOONGARCH_ENTRY_COMMON_H
#include <linux/sched.h>
#include <linux/processor.h>
static inline bool on_thread_stack(void)
{
return !(((unsigned long)(current->stack) ^ current_stack_pointer) & ~(THREAD_SIZE - 1));
}
#endif
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_HARDIRQ_H
#define _ASM_HARDIRQ_H
#include <linux/cache.h>
#include <linux/threads.h>
#include <linux/irq.h>
extern void ack_bad_irq(unsigned int irq);
#define ack_bad_irq ack_bad_irq
#define NR_IPI 2
typedef struct {
unsigned int ipi_irqs[NR_IPI];
unsigned int __softirq_pending;
} ____cacheline_aligned irq_cpustat_t;
DECLARE_PER_CPU_ALIGNED(irq_cpustat_t, irq_stat);
#endif /* _ASM_HARDIRQ_H */
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef __ASM_HW_IRQ_H
#define __ASM_HW_IRQ_H
#include <linux/atomic.h>
extern atomic_t irq_err_count;
/*
* interrupt-retrigger: NOP for now. This may not be appropriate for all
* machines, we'll see ...
*/
#endif /* __ASM_HW_IRQ_H */
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_IRQ_H
#define _ASM_IRQ_H
#include <linux/irqdomain.h>
#include <linux/irqreturn.h>
#define IRQ_STACK_SIZE THREAD_SIZE
#define IRQ_STACK_START (IRQ_STACK_SIZE - 16)
DECLARE_PER_CPU(unsigned long, irq_stack);
/*
* The highest address on the IRQ stack contains a dummy frame which is
* structured as follows:
*
* top ------------
* | task sp | <- irq_stack[cpu] + IRQ_STACK_START
* ------------
* | | <- First frame of IRQ context
* ------------
*
* task sp holds a copy of the task stack pointer where the struct pt_regs
* from exception entry can be found.
*/
static inline bool on_irq_stack(int cpu, unsigned long sp)
{
unsigned long low = per_cpu(irq_stack, cpu);
unsigned long high = low + IRQ_STACK_SIZE;
return (low <= sp && sp <= high);
}
int get_ipi_irq(void);
int get_pmc_irq(void);
int get_timer_irq(void);
void spurious_interrupt(void);
#define NR_IRQS_LEGACY 16
#define arch_trigger_cpumask_backtrace arch_trigger_cpumask_backtrace
void arch_trigger_cpumask_backtrace(const struct cpumask *mask, bool exclude_self);
#define MAX_IO_PICS 2
#define NR_IRQS (64 + (256 * MAX_IO_PICS))
#define CORES_PER_EIO_NODE 4
#define LOONGSON_CPU_UART0_VEC 10 /* CPU UART0 */
#define LOONGSON_CPU_THSENS_VEC 14 /* CPU Thsens */
#define LOONGSON_CPU_HT0_VEC 16 /* CPU HT0 irq vector base number */
#define LOONGSON_CPU_HT1_VEC 24 /* CPU HT1 irq vector base number */
/* IRQ number definitions */
#define LOONGSON_LPC_IRQ_BASE 0
#define LOONGSON_LPC_LAST_IRQ (LOONGSON_LPC_IRQ_BASE + 15)
#define LOONGSON_CPU_IRQ_BASE 16
#define LOONGSON_CPU_LAST_IRQ (LOONGSON_CPU_IRQ_BASE + 14)
#define LOONGSON_PCH_IRQ_BASE 64
#define LOONGSON_PCH_ACPI_IRQ (LOONGSON_PCH_IRQ_BASE + 47)
#define LOONGSON_PCH_LAST_IRQ (LOONGSON_PCH_IRQ_BASE + 64 - 1)
#define LOONGSON_MSI_IRQ_BASE (LOONGSON_PCH_IRQ_BASE + 64)
#define LOONGSON_MSI_LAST_IRQ (LOONGSON_PCH_IRQ_BASE + 256 - 1)
#define GSI_MIN_LPC_IRQ LOONGSON_LPC_IRQ_BASE
#define GSI_MAX_LPC_IRQ (LOONGSON_LPC_IRQ_BASE + 16 - 1)
#define GSI_MIN_CPU_IRQ LOONGSON_CPU_IRQ_BASE
#define GSI_MAX_CPU_IRQ (LOONGSON_CPU_IRQ_BASE + 48 - 1)
#define GSI_MIN_PCH_IRQ LOONGSON_PCH_IRQ_BASE
#define GSI_MAX_PCH_IRQ (LOONGSON_PCH_IRQ_BASE + 256 - 1)
extern int find_pch_pic(u32 gsi);
extern int eiointc_get_node(int id);
static inline void eiointc_enable(void)
{
uint64_t misc;
misc = iocsr_read64(LOONGARCH_IOCSR_MISC_FUNC);
misc |= IOCSR_MISC_FUNC_EXT_IOI_EN;
iocsr_write64(misc, LOONGARCH_IOCSR_MISC_FUNC);
}
struct acpi_madt_lio_pic;
struct acpi_madt_eio_pic;
struct acpi_madt_ht_pic;
struct acpi_madt_bio_pic;
struct acpi_madt_msi_pic;
struct acpi_madt_lpc_pic;
struct irq_domain *loongarch_cpu_irq_init(void);
struct irq_domain *liointc_acpi_init(struct irq_domain *parent,
struct acpi_madt_lio_pic *acpi_liointc);
struct irq_domain *eiointc_acpi_init(struct irq_domain *parent,
struct acpi_madt_eio_pic *acpi_eiointc);
struct irq_domain *htvec_acpi_init(struct irq_domain *parent,
struct acpi_madt_ht_pic *acpi_htvec);
struct irq_domain *pch_lpc_acpi_init(struct irq_domain *parent,
struct acpi_madt_lpc_pic *acpi_pchlpc);
struct irq_domain *pch_msi_acpi_init(struct irq_domain *parent,
struct acpi_madt_msi_pic *acpi_pchmsi);
struct irq_domain *pch_pic_acpi_init(struct irq_domain *parent,
struct acpi_madt_bio_pic *acpi_pchpic);
extern struct acpi_madt_lio_pic *acpi_liointc;
extern struct acpi_madt_eio_pic *acpi_eiointc[MAX_IO_PICS];
extern struct acpi_madt_ht_pic *acpi_htintc;
extern struct acpi_madt_lpc_pic *acpi_pchlpc;
extern struct acpi_madt_msi_pic *acpi_pchmsi[MAX_IO_PICS];
extern struct acpi_madt_bio_pic *acpi_pchpic[MAX_IO_PICS];
extern struct irq_domain *cpu_domain;
extern struct irq_domain *liointc_domain;
extern struct irq_domain *pch_lpc_domain;
extern struct irq_domain *pch_msi_domain[MAX_IO_PICS];
extern struct irq_domain *pch_pic_domain[MAX_IO_PICS];
#include <asm-generic/irq.h>
#endif /* _ASM_IRQ_H */
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef __ASM_IRQ_REGS_H
#define __ASM_IRQ_REGS_H
#define ARCH_HAS_OWN_IRQ_REGS
#include <linux/thread_info.h>
static inline struct pt_regs *get_irq_regs(void)
{
return current_thread_info()->regs;
}
static inline struct pt_regs *set_irq_regs(struct pt_regs *new_regs)
{
struct pt_regs *old_regs;
old_regs = get_irq_regs();
current_thread_info()->regs = new_regs;
return old_regs;
}
#endif /* __ASM_IRQ_REGS_H */
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_IRQFLAGS_H
#define _ASM_IRQFLAGS_H
#ifndef __ASSEMBLY__
#include <linux/compiler.h>
#include <linux/stringify.h>
#include <asm/compiler.h>
#include <asm/loongarch.h>
static inline void arch_local_irq_enable(void)
{
u32 flags = CSR_CRMD_IE;
__asm__ __volatile__(
"csrxchg %[val], %[mask], %[reg]\n\t"
: [val] "+r" (flags)
: [mask] "r" (CSR_CRMD_IE), [reg] "i" (LOONGARCH_CSR_CRMD)
: "memory");
}
static inline void arch_local_irq_disable(void)
{
u32 flags = 0;
__asm__ __volatile__(
"csrxchg %[val], %[mask], %[reg]\n\t"
: [val] "+r" (flags)
: [mask] "r" (CSR_CRMD_IE), [reg] "i" (LOONGARCH_CSR_CRMD)
: "memory");
}
static inline unsigned long arch_local_irq_save(void)
{
u32 flags = 0;
__asm__ __volatile__(
"csrxchg %[val], %[mask], %[reg]\n\t"
: [val] "+r" (flags)
: [mask] "r" (CSR_CRMD_IE), [reg] "i" (LOONGARCH_CSR_CRMD)
: "memory");
return flags;
}
static inline void arch_local_irq_restore(unsigned long flags)
{
__asm__ __volatile__(
"csrxchg %[val], %[mask], %[reg]\n\t"
: [val] "+r" (flags)
: [mask] "r" (CSR_CRMD_IE), [reg] "i" (LOONGARCH_CSR_CRMD)
: "memory");
}
static inline unsigned long arch_local_save_flags(void)
{
u32 flags;
__asm__ __volatile__(
"csrrd %[val], %[reg]\n\t"
: [val] "=r" (flags)
: [reg] "i" (LOONGARCH_CSR_CRMD)
: "memory");
return flags;
}
static inline int arch_irqs_disabled_flags(unsigned long flags)
{
return !(flags & CSR_CRMD_IE);
}
static inline int arch_irqs_disabled(void)
{
return arch_irqs_disabled_flags(arch_local_save_flags());
}
#endif /* #ifndef __ASSEMBLY__ */
#endif /* _ASM_IRQFLAGS_H */
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_LOONGARCH_KDEBUG_H
#define _ASM_LOONGARCH_KDEBUG_H
#include <linux/notifier.h>
enum die_val {
DIE_OOPS = 1,
DIE_RI,
DIE_FP,
DIE_SIMD,
DIE_TRAP,
DIE_PAGE_FAULT,
DIE_BREAK,
DIE_SSTEPBP,
DIE_UPROBE,
DIE_UPROBE_XOL,
};
#endif /* _ASM_LOONGARCH_KDEBUG_H */
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_STACKFRAME_H
#define _ASM_STACKFRAME_H
#include <linux/threads.h>
#include <asm/asm.h>
#include <asm/asmmacro.h>
#include <asm/asm-offsets.h>
#include <asm/loongarch.h>
#include <asm/thread_info.h>
/* Make the addition of cfi info a little easier. */
.macro cfi_rel_offset reg offset=0 docfi=0
.if \docfi
.cfi_rel_offset \reg, \offset
.endif
.endm
.macro cfi_st reg offset=0 docfi=0
cfi_rel_offset \reg, \offset, \docfi
LONG_S \reg, sp, \offset
.endm
.macro cfi_restore reg offset=0 docfi=0
.if \docfi
.cfi_restore \reg
.endif
.endm
.macro cfi_ld reg offset=0 docfi=0
LONG_L \reg, sp, \offset
cfi_restore \reg \offset \docfi
.endm
.macro BACKUP_T0T1
csrwr t0, EXCEPTION_KS0
csrwr t1, EXCEPTION_KS1
.endm
.macro RELOAD_T0T1
csrrd t0, EXCEPTION_KS0
csrrd t1, EXCEPTION_KS1
.endm
.macro SAVE_TEMP docfi=0
RELOAD_T0T1
cfi_st t0, PT_R12, \docfi
cfi_st t1, PT_R13, \docfi
cfi_st t2, PT_R14, \docfi
cfi_st t3, PT_R15, \docfi
cfi_st t4, PT_R16, \docfi
cfi_st t5, PT_R17, \docfi
cfi_st t6, PT_R18, \docfi
cfi_st t7, PT_R19, \docfi
cfi_st t8, PT_R20, \docfi
.endm
.macro SAVE_STATIC docfi=0
cfi_st s0, PT_R23, \docfi
cfi_st s1, PT_R24, \docfi
cfi_st s2, PT_R25, \docfi
cfi_st s3, PT_R26, \docfi
cfi_st s4, PT_R27, \docfi
cfi_st s5, PT_R28, \docfi
cfi_st s6, PT_R29, \docfi
cfi_st s7, PT_R30, \docfi
cfi_st s8, PT_R31, \docfi
.endm
/*
* get_saved_sp returns the SP for the current CPU by looking in the
* kernelsp array for it. It stores the current sp in t0 and loads the
* new value in sp.
*/
.macro get_saved_sp docfi=0
la.abs t1, kernelsp
move t0, sp
.if \docfi
.cfi_register sp, t0
.endif
LONG_L sp, t1, 0
.endm
.macro set_saved_sp stackp temp temp2
la.abs \temp, kernelsp
LONG_S \stackp, \temp, 0
.endm
.macro SAVE_SOME docfi=0
csrrd t1, LOONGARCH_CSR_PRMD
andi t1, t1, 0x3 /* extract pplv bit */
move t0, sp
beqz t1, 8f
/* Called from user mode, new stack. */
get_saved_sp docfi=\docfi
8:
PTR_ADDI sp, sp, -PT_SIZE
.if \docfi
.cfi_def_cfa sp, 0
.endif
cfi_st t0, PT_R3, \docfi
cfi_rel_offset sp, PT_R3, \docfi
LONG_S zero, sp, PT_R0
csrrd t0, LOONGARCH_CSR_PRMD
LONG_S t0, sp, PT_PRMD
csrrd t0, LOONGARCH_CSR_CRMD
LONG_S t0, sp, PT_CRMD
csrrd t0, LOONGARCH_CSR_EUEN
LONG_S t0, sp, PT_EUEN
csrrd t0, LOONGARCH_CSR_ECFG
LONG_S t0, sp, PT_ECFG
csrrd t0, LOONGARCH_CSR_ESTAT
PTR_S t0, sp, PT_ESTAT
cfi_st ra, PT_R1, \docfi
cfi_st a0, PT_R4, \docfi
cfi_st a1, PT_R5, \docfi
cfi_st a2, PT_R6, \docfi
cfi_st a3, PT_R7, \docfi
cfi_st a4, PT_R8, \docfi
cfi_st a5, PT_R9, \docfi
cfi_st a6, PT_R10, \docfi
cfi_st a7, PT_R11, \docfi
csrrd ra, LOONGARCH_CSR_ERA
LONG_S ra, sp, PT_ERA
.if \docfi
.cfi_rel_offset ra, PT_ERA
.endif
cfi_st tp, PT_R2, \docfi
cfi_st fp, PT_R22, \docfi
/* Set thread_info if we're coming from user mode */
csrrd t0, LOONGARCH_CSR_PRMD
andi t0, t0, 0x3 /* extract pplv bit */
beqz t0, 9f
li.d tp, ~_THREAD_MASK
and tp, tp, sp
cfi_st u0, PT_R21, \docfi
csrrd u0, PERCPU_BASE_KS
9:
.endm
.macro SAVE_ALL docfi=0
SAVE_SOME \docfi
SAVE_TEMP \docfi
SAVE_STATIC \docfi
.endm
.macro RESTORE_TEMP docfi=0
cfi_ld t0, PT_R12, \docfi
cfi_ld t1, PT_R13, \docfi
cfi_ld t2, PT_R14, \docfi
cfi_ld t3, PT_R15, \docfi
cfi_ld t4, PT_R16, \docfi
cfi_ld t5, PT_R17, \docfi
cfi_ld t6, PT_R18, \docfi
cfi_ld t7, PT_R19, \docfi
cfi_ld t8, PT_R20, \docfi
.endm
.macro RESTORE_STATIC docfi=0
cfi_ld s0, PT_R23, \docfi
cfi_ld s1, PT_R24, \docfi
cfi_ld s2, PT_R25, \docfi
cfi_ld s3, PT_R26, \docfi
cfi_ld s4, PT_R27, \docfi
cfi_ld s5, PT_R28, \docfi
cfi_ld s6, PT_R29, \docfi
cfi_ld s7, PT_R30, \docfi
cfi_ld s8, PT_R31, \docfi
.endm
.macro RESTORE_SOME docfi=0
LONG_L a0, sp, PT_PRMD
andi a0, a0, 0x3 /* extract pplv bit */
beqz a0, 8f
cfi_ld u0, PT_R21, \docfi
8:
LONG_L a0, sp, PT_ERA
csrwr a0, LOONGARCH_CSR_ERA
LONG_L a0, sp, PT_PRMD
csrwr a0, LOONGARCH_CSR_PRMD
cfi_ld ra, PT_R1, \docfi
cfi_ld a0, PT_R4, \docfi
cfi_ld a1, PT_R5, \docfi
cfi_ld a2, PT_R6, \docfi
cfi_ld a3, PT_R7, \docfi
cfi_ld a4, PT_R8, \docfi
cfi_ld a5, PT_R9, \docfi
cfi_ld a6, PT_R10, \docfi
cfi_ld a7, PT_R11, \docfi
cfi_ld tp, PT_R2, \docfi
cfi_ld fp, PT_R22, \docfi
.endm
.macro RESTORE_SP_AND_RET docfi=0
cfi_ld sp, PT_R3, \docfi
ertn
.endm
.macro RESTORE_ALL_AND_RET docfi=0
RESTORE_STATIC \docfi
RESTORE_TEMP \docfi
RESTORE_SOME \docfi
RESTORE_SP_AND_RET \docfi
.endm
#endif /* _ASM_STACKFRAME_H */
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_STACKTRACE_H
#define _ASM_STACKTRACE_H
#include <asm/asm.h>
#include <asm/ptrace.h>
#include <asm/loongarch.h>
#include <linux/stringify.h>
#define STR_LONG_L __stringify(LONG_L)
#define STR_LONG_S __stringify(LONG_S)
#define STR_LONGSIZE __stringify(LONGSIZE)
#define STORE_ONE_REG(r) \
STR_LONG_S " $r" __stringify(r)", %1, "STR_LONGSIZE"*"__stringify(r)"\n\t"
#define CSRRD_ONE_REG(reg) \
__stringify(csrrd) " %0, "__stringify(reg)"\n\t"
static __always_inline void prepare_frametrace(struct pt_regs *regs)
{
__asm__ __volatile__(
/* Save $r1 */
STORE_ONE_REG(1)
/* Use $r1 to save PC */
"pcaddi $r1, 0\n\t"
STR_LONG_S " $r1, %0\n\t"
/* Restore $r1 */
STR_LONG_L " $r1, %1, "STR_LONGSIZE"\n\t"
STORE_ONE_REG(2)
STORE_ONE_REG(3)
STORE_ONE_REG(4)
STORE_ONE_REG(5)
STORE_ONE_REG(6)
STORE_ONE_REG(7)
STORE_ONE_REG(8)
STORE_ONE_REG(9)
STORE_ONE_REG(10)
STORE_ONE_REG(11)
STORE_ONE_REG(12)
STORE_ONE_REG(13)
STORE_ONE_REG(14)
STORE_ONE_REG(15)
STORE_ONE_REG(16)
STORE_ONE_REG(17)
STORE_ONE_REG(18)
STORE_ONE_REG(19)
STORE_ONE_REG(20)
STORE_ONE_REG(21)
STORE_ONE_REG(22)
STORE_ONE_REG(23)
STORE_ONE_REG(24)
STORE_ONE_REG(25)
STORE_ONE_REG(26)
STORE_ONE_REG(27)
STORE_ONE_REG(28)
STORE_ONE_REG(29)
STORE_ONE_REG(30)
STORE_ONE_REG(31)
: "=m" (regs->csr_era)
: "r" (regs->regs)
: "memory");
__asm__ __volatile__(CSRRD_ONE_REG(LOONGARCH_CSR_BADV) : "=r" (regs->csr_badvaddr));
__asm__ __volatile__(CSRRD_ONE_REG(LOONGARCH_CSR_CRMD) : "=r" (regs->csr_crmd));
__asm__ __volatile__(CSRRD_ONE_REG(LOONGARCH_CSR_PRMD) : "=r" (regs->csr_prmd));
__asm__ __volatile__(CSRRD_ONE_REG(LOONGARCH_CSR_EUEN) : "=r" (regs->csr_euen));
__asm__ __volatile__(CSRRD_ONE_REG(LOONGARCH_CSR_ECFG) : "=r" (regs->csr_ecfg));
__asm__ __volatile__(CSRRD_ONE_REG(LOONGARCH_CSR_ESTAT) : "=r" (regs->csr_estat));
}
#endif /* _ASM_STACKTRACE_H */
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef __UAPI_ASM_BREAK_H
#define __UAPI_ASM_BREAK_H
#define BRK_DEFAULT 0 /* Used as default */
#define BRK_BUG 1 /* Used by BUG() */
#define BRK_KDB 2 /* Used in KDB_ENTER() */
#define BRK_MATHEMU 3 /* Used by FPU emulator */
#define BRK_USERBP 4 /* User bp (used by debuggers) */
#define BRK_SSTEPBP 5 /* User bp (used by debuggers) */
#define BRK_OVERFLOW 6 /* Overflow check */
#define BRK_DIVZERO 7 /* Divide by zero check */
#define BRK_RANGE 8 /* Range error check */
#define BRK_MULOVFL 9 /* Multiply overflow */
#define BRK_KPROBE_BP 10 /* Kprobe break */
#define BRK_KPROBE_SSTEPBP 11 /* Kprobe single step break */
#define BRK_UPROBE_BP 12 /* See <asm/uprobes.h> */
#define BRK_UPROBE_XOLBP 13 /* See <asm/uprobes.h> */
#endif /* __UAPI_ASM_BREAK_H */
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/uaccess.h>
static inline int __get_inst(u32 *i, u32 *p, bool user)
{
return user ? get_user(*i, (u32 __user *)p) : get_kernel_nofault(*i, p);
}
static inline int __get_addr(unsigned long *a, unsigned long *p, bool user)
{
return user ? get_user(*a, (unsigned long __user *)p) : get_kernel_nofault(*a, p);
}
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*
* Derived from MIPS:
* Copyright (C) 1994 - 2000, 2001, 2003 Ralf Baechle
* Copyright (C) 1999, 2000 Silicon Graphics, Inc.
* Copyright (C) 2002, 2007 Maciej W. Rozycki
* Copyright (C) 2001, 2012 MIPS Technologies, Inc. All rights reserved.
*/
#include <asm/asm.h>
#include <asm/asmmacro.h>
#include <asm/loongarch.h>
#include <asm/regdef.h>
#include <asm/fpregdef.h>
#include <asm/stackframe.h>
#include <asm/thread_info.h>
.align 5
SYM_FUNC_START(__arch_cpu_idle)
/* start of rollback region */
LONG_L t0, tp, TI_FLAGS
nop
andi t0, t0, _TIF_NEED_RESCHED
bnez t0, 1f
nop
nop
nop
idle 0
/* end of rollback region */
1: jirl zero, ra, 0
SYM_FUNC_END(__arch_cpu_idle)
SYM_FUNC_START(handle_vint)
BACKUP_T0T1
SAVE_ALL
la.abs t1, __arch_cpu_idle
LONG_L t0, sp, PT_ERA
/* 32 byte rollback region */
ori t0, t0, 0x1f
xori t0, t0, 0x1f
bne t0, t1, 1f
LONG_S t0, sp, PT_ERA
1: move a0, sp
move a1, sp
la.abs t0, do_vint
jirl ra, t0, 0
RESTORE_ALL_AND_RET
SYM_FUNC_END(handle_vint)
SYM_FUNC_START(except_vec_cex)
b cache_parity_error
SYM_FUNC_END(except_vec_cex)
.macro build_prep_badv
csrrd t0, LOONGARCH_CSR_BADV
PTR_S t0, sp, PT_BVADDR
.endm
.macro build_prep_fcsr
movfcsr2gr a1, fcsr0
.endm
.macro build_prep_none
.endm
.macro BUILD_HANDLER exception handler prep
.align 5
SYM_FUNC_START(handle_\exception)
BACKUP_T0T1
SAVE_ALL
build_prep_\prep
move a0, sp
la.abs t0, do_\handler
jirl ra, t0, 0
RESTORE_ALL_AND_RET
SYM_FUNC_END(handle_\exception)
.endm
BUILD_HANDLER ade ade badv
BUILD_HANDLER ale ale badv
BUILD_HANDLER bp bp none
BUILD_HANDLER fpe fpe fcsr
BUILD_HANDLER fpu fpu none
BUILD_HANDLER lsx lsx none
BUILD_HANDLER lasx lasx none
BUILD_HANDLER lbt lbt none
BUILD_HANDLER ri ri none
BUILD_HANDLER watch watch none
BUILD_HANDLER reserved reserved none /* others */
SYM_FUNC_START(handle_sys)
la.abs t0, handle_syscall
jirl zero, t0, 0
SYM_FUNC_END(handle_sys)
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#include <linux/kernel.h>
#include <linux/acpi.h>
#include <linux/atomic.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irqchip.h>
#include <linux/kernel_stat.h>
#include <linux/proc_fs.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/kallsyms.h>
#include <linux/uaccess.h>
#include <asm/irq.h>
#include <asm/loongson.h>
#include <asm/setup.h>
DEFINE_PER_CPU(unsigned long, irq_stack);
struct irq_domain *cpu_domain;
struct irq_domain *liointc_domain;
struct irq_domain *pch_lpc_domain;
struct irq_domain *pch_msi_domain[MAX_IO_PICS];
struct irq_domain *pch_pic_domain[MAX_IO_PICS];
/*
* 'what should we do if we get a hw irq event on an illegal vector'.
* each architecture has to answer this themselves.
*/
void ack_bad_irq(unsigned int irq)
{
pr_warn("Unexpected IRQ # %d\n", irq);
}
atomic_t irq_err_count;
asmlinkage void spurious_interrupt(void)
{
atomic_inc(&irq_err_count);
}
int arch_show_interrupts(struct seq_file *p, int prec)
{
seq_printf(p, "%*s: %10u\n", prec, "ERR", atomic_read(&irq_err_count));
return 0;
}
void __init init_IRQ(void)
{
int i;
unsigned int order = get_order(IRQ_STACK_SIZE);
struct page *page;
clear_csr_ecfg(ECFG0_IM);
clear_csr_estat(ESTATF_IP);
irqchip_init();
for (i = 0; i < NR_IRQS; i++)
irq_set_noprobe(i);
for_each_possible_cpu(i) {
page = alloc_pages_node(cpu_to_node(i), GFP_KERNEL, order);
per_cpu(irq_stack, i) = (unsigned long)page_address(page);
pr_debug("CPU%d IRQ stack at 0x%lx - 0x%lx\n", i,
per_cpu(irq_stack, i), per_cpu(irq_stack, i) + IRQ_STACK_SIZE);
}
set_csr_ecfg(ECFGF_IP0 | ECFGF_IP1 | ECFGF_IP2 | ECFGF_IPI | ECFGF_PMC);
}
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment