root/arch/arc/include/asm/irqflags-arcv2.h
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (C) 2014-15 Synopsys, Inc. (www.synopsys.com)
 */

#ifndef __ASM_IRQFLAGS_ARCV2_H
#define __ASM_IRQFLAGS_ARCV2_H

#include <asm/arcregs.h>

/* status32 Bits */
#define STATUS_AD_BIT   19   /* Disable Align chk: core supports non-aligned */
#define STATUS_IE_BIT   31

#define STATUS_AD_MASK          (1<<STATUS_AD_BIT)
#define STATUS_IE_MASK          (1<<STATUS_IE_BIT)

/* status32 Bits as encoded/expected by CLRI/SETI */
#define CLRI_STATUS_IE_BIT      4

#define CLRI_STATUS_E_MASK      0xF
#define CLRI_STATUS_IE_MASK     (1 << CLRI_STATUS_IE_BIT)

#define AUX_USER_SP             0x00D
#define AUX_IRQ_CTRL            0x00E
#define AUX_IRQ_ACT             0x043   /* Active Intr across all levels */
#define AUX_IRQ_LVL_PEND        0x200   /* Pending Intr across all levels */
#define AUX_IRQ_HINT            0x201   /* For generating Soft Interrupts */
#define AUX_IRQ_PRIORITY        0x206
#define ICAUSE                  0x40a
#define AUX_IRQ_SELECT          0x40b
#define AUX_IRQ_ENABLE          0x40c

/* Was Intr taken in User Mode */
#define AUX_IRQ_ACT_BIT_U       31

/*
 * Hardware supports 16 priorities (0 highest, 15 lowest)
 * Linux by default runs at 1, priority 0 reserved for NMI style interrupts
 */
#define ARCV2_IRQ_DEF_PRIO      1

/* seed value for status register */
#ifdef CONFIG_ARC_USE_UNALIGNED_MEM_ACCESS
#define __AD_ENB        STATUS_AD_MASK
#else
#define __AD_ENB        0
#endif

#define ISA_INIT_STATUS_BITS    (STATUS_IE_MASK | __AD_ENB | \
                                        (ARCV2_IRQ_DEF_PRIO << 1))

#ifndef __ASSEMBLER__

/*
 * Save IRQ state and disable IRQs
 */
static inline long arch_local_irq_save(void)
{
        unsigned long flags;

        __asm__ __volatile__("  clri %0 \n" : "=r" (flags) : : "memory");

        return flags;
}

/*
 * restore saved IRQ state
 */
static inline void arch_local_irq_restore(unsigned long flags)
{
        __asm__ __volatile__("  seti %0 \n" : : "r" (flags) : "memory");
}

/*
 * Unconditionally Enable IRQs
 */
static inline void arch_local_irq_enable(void)
{
        unsigned int irqact = read_aux_reg(AUX_IRQ_ACT);

        if (irqact & 0xffff)
                write_aux_reg(AUX_IRQ_ACT, irqact & ~0xffff);

        __asm__ __volatile__("  seti    \n" : : : "memory");
}

/*
 * Unconditionally Disable IRQs
 */
static inline void arch_local_irq_disable(void)
{
        __asm__ __volatile__("  clri    \n" : : : "memory");
}

/*
 * save IRQ state
 */
static inline long arch_local_save_flags(void)
{
        unsigned long temp;

        __asm__ __volatile__(
        "       lr  %0, [status32]      \n"
        : "=&r"(temp)
        :
        : "memory");

        /* To be compatible with irq_save()/irq_restore()
         * encode the irq bits as expected by CLRI/SETI
         * (this was needed to make CONFIG_TRACE_IRQFLAGS work)
         */
        temp = (1 << 5) |
                ((!!(temp & STATUS_IE_MASK)) << CLRI_STATUS_IE_BIT) |
                ((temp >> 1) & CLRI_STATUS_E_MASK);
        return temp;
}

/*
 * Query IRQ state
 */
static inline int arch_irqs_disabled_flags(unsigned long flags)
{
        return !(flags & CLRI_STATUS_IE_MASK);
}

static inline int arch_irqs_disabled(void)
{
        return arch_irqs_disabled_flags(arch_local_save_flags());
}

static inline void arc_softirq_trigger(int irq)
{
        write_aux_reg(AUX_IRQ_HINT, irq);
}

static inline void arc_softirq_clear(int irq)
{
        write_aux_reg(AUX_IRQ_HINT, 0);
}

#else

#ifdef CONFIG_TRACE_IRQFLAGS

.macro TRACE_ASM_IRQ_DISABLE
        bl      trace_hardirqs_off
.endm

.macro TRACE_ASM_IRQ_ENABLE
        bl      trace_hardirqs_on
.endm

#else

.macro TRACE_ASM_IRQ_DISABLE
.endm

.macro TRACE_ASM_IRQ_ENABLE
.endm

#endif
.macro IRQ_DISABLE  scratch
        clri
        TRACE_ASM_IRQ_DISABLE
.endm

.macro IRQ_ENABLE  scratch
        TRACE_ASM_IRQ_ENABLE
        seti
.endm

#endif  /* __ASSEMBLER__ */

#endif