diff options
Diffstat (limited to 'arch/tile/include/asm/irqflags.h')
| -rw-r--r-- | arch/tile/include/asm/irqflags.h | 266 | 
1 files changed, 266 insertions, 0 deletions
diff --git a/arch/tile/include/asm/irqflags.h b/arch/tile/include/asm/irqflags.h new file mode 100644 index 00000000000..45cf67c2f28 --- /dev/null +++ b/arch/tile/include/asm/irqflags.h @@ -0,0 +1,266 @@ +/* + * Copyright 2010 Tilera Corporation. All Rights Reserved. + * + *   This program is free software; you can redistribute it and/or + *   modify it under the terms of the GNU General Public License + *   as published by the Free Software Foundation, version 2. + * + *   This program is distributed in the hope that it will be useful, but + *   WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + *   NON INFRINGEMENT.  See the GNU General Public License for + *   more details. + */ + +#ifndef _ASM_TILE_IRQFLAGS_H +#define _ASM_TILE_IRQFLAGS_H + +#include <arch/interrupts.h> +#include <arch/chip.h> + +/* + * The set of interrupts we want to allow when interrupts are nominally + * disabled.  The remainder are effectively "NMI" interrupts from + * the point of view of the generic Linux code.  Note that synchronous + * interrupts (aka "non-queued") are not blocked by the mask in any case. + */ +#if CHIP_HAS_AUX_PERF_COUNTERS() +#define LINUX_MASKABLE_INTERRUPTS \ +	(~(INT_MASK(INT_PERF_COUNT) | INT_MASK(INT_AUX_PERF_COUNT))) +#else +#define LINUX_MASKABLE_INTERRUPTS \ +	(~(INT_MASK(INT_PERF_COUNT))) +#endif + +#ifndef __ASSEMBLY__ + +/* NOTE: we can't include <linux/percpu.h> due to #include dependencies. */ +#include <asm/percpu.h> +#include <arch/spr_def.h> + +/* Set and clear kernel interrupt masks. */ +#if CHIP_HAS_SPLIT_INTR_MASK() +#if INT_PERF_COUNT < 32 || INT_AUX_PERF_COUNT < 32 || INT_MEM_ERROR >= 32 +# error Fix assumptions about which word various interrupts are in +#endif +#define interrupt_mask_set(n) do { \ +	int __n = (n); \ +	int __mask = 1 << (__n & 0x1f); \ +	if (__n < 32) \ +		__insn_mtspr(SPR_INTERRUPT_MASK_SET_1_0, __mask); \ +	else \ +		__insn_mtspr(SPR_INTERRUPT_MASK_SET_1_1, __mask); \ +} while (0) +#define interrupt_mask_reset(n) do { \ +	int __n = (n); \ +	int __mask = 1 << (__n & 0x1f); \ +	if (__n < 32) \ +		__insn_mtspr(SPR_INTERRUPT_MASK_RESET_1_0, __mask); \ +	else \ +		__insn_mtspr(SPR_INTERRUPT_MASK_RESET_1_1, __mask); \ +} while (0) +#define interrupt_mask_check(n) ({ \ +	int __n = (n); \ +	(((__n < 32) ? \ +	 __insn_mfspr(SPR_INTERRUPT_MASK_1_0) : \ +	 __insn_mfspr(SPR_INTERRUPT_MASK_1_1)) \ +	  >> (__n & 0x1f)) & 1; \ +}) +#define interrupt_mask_set_mask(mask) do { \ +	unsigned long long __m = (mask); \ +	__insn_mtspr(SPR_INTERRUPT_MASK_SET_1_0, (unsigned long)(__m)); \ +	__insn_mtspr(SPR_INTERRUPT_MASK_SET_1_1, (unsigned long)(__m>>32)); \ +} while (0) +#define interrupt_mask_reset_mask(mask) do { \ +	unsigned long long __m = (mask); \ +	__insn_mtspr(SPR_INTERRUPT_MASK_RESET_1_0, (unsigned long)(__m)); \ +	__insn_mtspr(SPR_INTERRUPT_MASK_RESET_1_1, (unsigned long)(__m>>32)); \ +} while (0) +#else +#define interrupt_mask_set(n) \ +	__insn_mtspr(SPR_INTERRUPT_MASK_SET_1, (1UL << (n))) +#define interrupt_mask_reset(n) \ +	__insn_mtspr(SPR_INTERRUPT_MASK_RESET_1, (1UL << (n))) +#define interrupt_mask_check(n) \ +	((__insn_mfspr(SPR_INTERRUPT_MASK_1) >> (n)) & 1) +#define interrupt_mask_set_mask(mask) \ +	__insn_mtspr(SPR_INTERRUPT_MASK_SET_1, (mask)) +#define interrupt_mask_reset_mask(mask) \ +	__insn_mtspr(SPR_INTERRUPT_MASK_RESET_1, (mask)) +#endif + +/* + * The set of interrupts we want active if irqs are enabled. + * Note that in particular, the tile timer interrupt comes and goes + * from this set, since we have no other way to turn off the timer. + * Likewise, INTCTRL_1 is removed and re-added during device + * interrupts, as is the the hardwall UDN_FIREWALL interrupt. + * We use a low bit (MEM_ERROR) as our sentinel value and make sure it + * is always claimed as an "active interrupt" so we can query that bit + * to know our current state. + */ +DECLARE_PER_CPU(unsigned long long, interrupts_enabled_mask); +#define INITIAL_INTERRUPTS_ENABLED INT_MASK(INT_MEM_ERROR) + +/* Disable interrupts. */ +#define raw_local_irq_disable() \ +	interrupt_mask_set_mask(LINUX_MASKABLE_INTERRUPTS) + +/* Disable all interrupts, including NMIs. */ +#define raw_local_irq_disable_all() \ +	interrupt_mask_set_mask(-1UL) + +/* Re-enable all maskable interrupts. */ +#define raw_local_irq_enable() \ +	interrupt_mask_reset_mask(__get_cpu_var(interrupts_enabled_mask)) + +/* Disable or enable interrupts based on flag argument. */ +#define raw_local_irq_restore(disabled) do { \ +	if (disabled) \ +		raw_local_irq_disable(); \ +	else \ +		raw_local_irq_enable(); \ +} while (0) + +/* Return true if "flags" argument means interrupts are disabled. */ +#define raw_irqs_disabled_flags(flags) ((flags) != 0) + +/* Return true if interrupts are currently disabled. */ +#define raw_irqs_disabled() interrupt_mask_check(INT_MEM_ERROR) + +/* Save whether interrupts are currently disabled. */ +#define raw_local_save_flags(flags) ((flags) = raw_irqs_disabled()) + +/* Save whether interrupts are currently disabled, then disable them. */ +#define raw_local_irq_save(flags) \ +	do { raw_local_save_flags(flags); raw_local_irq_disable(); } while (0) + +/* Prevent the given interrupt from being enabled next time we enable irqs. */ +#define raw_local_irq_mask(interrupt) \ +	(__get_cpu_var(interrupts_enabled_mask) &= ~INT_MASK(interrupt)) + +/* Prevent the given interrupt from being enabled immediately. */ +#define raw_local_irq_mask_now(interrupt) do { \ +	raw_local_irq_mask(interrupt); \ +	interrupt_mask_set(interrupt); \ +} while (0) + +/* Allow the given interrupt to be enabled next time we enable irqs. */ +#define raw_local_irq_unmask(interrupt) \ +	(__get_cpu_var(interrupts_enabled_mask) |= INT_MASK(interrupt)) + +/* Allow the given interrupt to be enabled immediately, if !irqs_disabled. */ +#define raw_local_irq_unmask_now(interrupt) do { \ +	raw_local_irq_unmask(interrupt); \ +	if (!irqs_disabled()) \ +		interrupt_mask_reset(interrupt); \ +} while (0) + +#else /* __ASSEMBLY__ */ + +/* We provide a somewhat more restricted set for assembly. */ + +#ifdef __tilegx__ + +#if INT_MEM_ERROR != 0 +# error Fix IRQ_DISABLED() macro +#endif + +/* Return 0 or 1 to indicate whether interrupts are currently disabled. */ +#define IRQS_DISABLED(tmp)					\ +	mfspr   tmp, INTERRUPT_MASK_1;				\ +	andi    tmp, tmp, 1 + +/* Load up a pointer to &interrupts_enabled_mask. */ +#define GET_INTERRUPTS_ENABLED_MASK_PTR(reg)			\ +	moveli reg, hw2_last(interrupts_enabled_mask); \ +	shl16insli reg, reg, hw1(interrupts_enabled_mask); \ +	shl16insli reg, reg, hw0(interrupts_enabled_mask); \ +	add     reg, reg, tp + +/* Disable interrupts. */ +#define IRQ_DISABLE(tmp0, tmp1)					\ +	moveli  tmp0, hw2_last(LINUX_MASKABLE_INTERRUPTS);	\ +	shl16insli tmp0, tmp0, hw1(LINUX_MASKABLE_INTERRUPTS);	\ +	shl16insli tmp0, tmp0, hw0(LINUX_MASKABLE_INTERRUPTS);	\ +	mtspr   INTERRUPT_MASK_SET_1, tmp0 + +/* Disable ALL synchronous interrupts (used by NMI entry). */ +#define IRQ_DISABLE_ALL(tmp)					\ +	movei   tmp, -1;					\ +	mtspr   INTERRUPT_MASK_SET_1, tmp + +/* Enable interrupts. */ +#define IRQ_ENABLE(tmp0, tmp1)					\ +	GET_INTERRUPTS_ENABLED_MASK_PTR(tmp0);			\ +	ld      tmp0, tmp0;					\ +	mtspr   INTERRUPT_MASK_RESET_1, tmp0 + +#else /* !__tilegx__ */ + +/* + * Return 0 or 1 to indicate whether interrupts are currently disabled. + * Note that it's important that we use a bit from the "low" mask word, + * since when we are enabling, that is the word we write first, so if we + * are interrupted after only writing half of the mask, the interrupt + * handler will correctly observe that we have interrupts enabled, and + * will enable interrupts itself on return from the interrupt handler + * (making the original code's write of the "high" mask word idempotent). + */ +#define IRQS_DISABLED(tmp)					\ +	mfspr   tmp, INTERRUPT_MASK_1_0;			\ +	shri    tmp, tmp, INT_MEM_ERROR;			\ +	andi    tmp, tmp, 1 + +/* Load up a pointer to &interrupts_enabled_mask. */ +#define GET_INTERRUPTS_ENABLED_MASK_PTR(reg)			\ +	moveli  reg, lo16(interrupts_enabled_mask);	\ +	auli    reg, reg, ha16(interrupts_enabled_mask);\ +	add     reg, reg, tp + +/* Disable interrupts. */ +#define IRQ_DISABLE(tmp0, tmp1)					\ +	{							\ +	 movei  tmp0, -1;					\ +	 moveli tmp1, lo16(LINUX_MASKABLE_INTERRUPTS)		\ +	};							\ +	{							\ +	 mtspr  INTERRUPT_MASK_SET_1_0, tmp0;			\ +	 auli   tmp1, tmp1, ha16(LINUX_MASKABLE_INTERRUPTS)	\ +	};							\ +	mtspr   INTERRUPT_MASK_SET_1_1, tmp1 + +/* Disable ALL synchronous interrupts (used by NMI entry). */ +#define IRQ_DISABLE_ALL(tmp)					\ +	movei   tmp, -1;					\ +	mtspr   INTERRUPT_MASK_SET_1_0, tmp;			\ +	mtspr   INTERRUPT_MASK_SET_1_1, tmp + +/* Enable interrupts. */ +#define IRQ_ENABLE(tmp0, tmp1)					\ +	GET_INTERRUPTS_ENABLED_MASK_PTR(tmp0);			\ +	{							\ +	 lw     tmp0, tmp0;					\ +	 addi   tmp1, tmp0, 4					\ +	};							\ +	lw      tmp1, tmp1;					\ +	mtspr   INTERRUPT_MASK_RESET_1_0, tmp0;			\ +	mtspr   INTERRUPT_MASK_RESET_1_1, tmp1 +#endif + +/* + * Do the CPU's IRQ-state tracing from assembly code. We call a + * C function, but almost everywhere we do, we don't mind clobbering + * all the caller-saved registers. + */ +#ifdef CONFIG_TRACE_IRQFLAGS +# define TRACE_IRQS_ON  jal trace_hardirqs_on +# define TRACE_IRQS_OFF jal trace_hardirqs_off +#else +# define TRACE_IRQS_ON +# define TRACE_IRQS_OFF +#endif + +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_TILE_IRQFLAGS_H */  | 
