sh: intc: userimask support.
This adds support for hardware-assisted userspace irq masking for special priority levels. Due to the SR.IMASK interactivity, only some platforms implement this in hardware (including but not limited to SH-4A interrupt controllers, and ARM-based SH-Mobile CPUs). Each CPU needs to wire this up on its own, for now only SH7786 is wired up as an example. Signed-off-by: Paul Mundt <lethal@linux-sh.org>
This commit is contained in:
parent
12129fea50
commit
43b8774dc4
@ -732,6 +732,8 @@ config GUSA_RB
|
||||
LLSC, this should be more efficient than the other alternative of
|
||||
disabling interrupts around the atomic sequence.
|
||||
|
||||
source "drivers/sh/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Boot options"
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <linux/mm.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/sh_timer.h>
|
||||
#include <linux/sh_intc.h>
|
||||
#include <cpu/dma-register.h>
|
||||
#include <asm/mmzone.h>
|
||||
#include <asm/dmaengine.h>
|
||||
@ -907,6 +908,7 @@ static DECLARE_INTC_DESC(intc_desc_irl4567, "sh7786-irl4567", vectors_irl4567,
|
||||
#define INTC_INTMSK2 INTMSK2
|
||||
#define INTC_INTMSKCLR1 CnINTMSKCLR1
|
||||
#define INTC_INTMSKCLR2 INTMSKCLR2
|
||||
#define INTC_USERIMASK 0xfe411000
|
||||
|
||||
void __init plat_irq_setup(void)
|
||||
{
|
||||
@ -921,6 +923,7 @@ void __init plat_irq_setup(void)
|
||||
__raw_writel(__raw_readl(INTC_ICR0) & ~0x00c00000, INTC_ICR0);
|
||||
|
||||
register_intc_controller(&intc_desc);
|
||||
register_intc_userimask(INTC_USERIMASK);
|
||||
}
|
||||
|
||||
void __init plat_irq_setup_pins(int mode)
|
||||
|
13
drivers/sh/Kconfig
Normal file
13
drivers/sh/Kconfig
Normal file
@ -0,0 +1,13 @@
|
||||
config INTC_USERIMASK
|
||||
bool "Userspace interrupt masking support"
|
||||
depends on ARCH_SHMOBILE || (SUPERH && CPU_SH4A)
|
||||
help
|
||||
This enables support for hardware-assisted userspace hardirq
|
||||
masking.
|
||||
|
||||
SH-4A and newer interrupt blocks all support a special shadowed
|
||||
page with all non-masking registers obscured when mapped in to
|
||||
userspace. This is primarily for use by userspace device
|
||||
drivers that are using special priority levels.
|
||||
|
||||
If in doubt, say N.
|
@ -27,6 +27,7 @@
|
||||
#include <linux/topology.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <asm/sizes.h>
|
||||
|
||||
#define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \
|
||||
((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \
|
||||
@ -94,7 +95,8 @@ static DEFINE_SPINLOCK(vector_lock);
|
||||
#define SMP_NR(d, x) 1
|
||||
#endif
|
||||
|
||||
static unsigned int intc_prio_level[NR_IRQS]; /* for now */
|
||||
static unsigned int intc_prio_level[NR_IRQS]; /* for now */
|
||||
static unsigned int default_prio_level = 2; /* 2 - 16 */
|
||||
static unsigned long ack_handle[NR_IRQS];
|
||||
|
||||
static inline struct intc_desc_int *get_intc_desc(unsigned int irq)
|
||||
@ -787,7 +789,7 @@ static void __init intc_register_irq(struct intc_desc *desc,
|
||||
/* set priority level
|
||||
* - this needs to be at least 2 for 5-bit priorities on 7780
|
||||
*/
|
||||
intc_prio_level[irq] = 2;
|
||||
intc_prio_level[irq] = default_prio_level;
|
||||
|
||||
/* enable secondary masking method if present */
|
||||
if (data[!primary])
|
||||
@ -1037,6 +1039,64 @@ err0:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_INTC_USERIMASK
|
||||
static void __iomem *uimask;
|
||||
|
||||
int register_intc_userimask(unsigned long addr)
|
||||
{
|
||||
if (unlikely(uimask))
|
||||
return -EBUSY;
|
||||
|
||||
uimask = ioremap_nocache(addr, SZ_4K);
|
||||
if (unlikely(!uimask))
|
||||
return -ENOMEM;
|
||||
|
||||
pr_info("intc: userimask support registered for levels 0 -> %d\n",
|
||||
default_prio_level - 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
show_intc_userimask(struct sysdev_class *cls,
|
||||
struct sysdev_class_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", (__raw_readl(uimask) >> 4) & 0xf);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
store_intc_userimask(struct sysdev_class *cls,
|
||||
struct sysdev_class_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
unsigned long level;
|
||||
|
||||
level = simple_strtoul(buf, NULL, 10);
|
||||
|
||||
/*
|
||||
* Minimal acceptable IRQ levels are in the 2 - 16 range, but
|
||||
* these are chomped so as to not interfere with normal IRQs.
|
||||
*
|
||||
* Level 1 is a special case on some CPUs in that it's not
|
||||
* directly settable, but given that USERIMASK cuts off below a
|
||||
* certain level, we don't care about this limitation here.
|
||||
* Level 0 on the other hand equates to user masking disabled.
|
||||
*
|
||||
* We use default_prio_level as a cut off so that only special
|
||||
* case opt-in IRQs can be mangled.
|
||||
*/
|
||||
if (level >= default_prio_level)
|
||||
return -EINVAL;
|
||||
|
||||
__raw_writel(0xa5 << 24 | level << 4, uimask);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static SYSDEV_CLASS_ATTR(userimask, S_IRUSR | S_IWUSR,
|
||||
show_intc_userimask, store_intc_userimask);
|
||||
#endif
|
||||
|
||||
static ssize_t
|
||||
show_intc_name(struct sys_device *dev, struct sysdev_attribute *attr, char *buf)
|
||||
{
|
||||
@ -1108,6 +1168,11 @@ static int __init register_intc_sysdevs(void)
|
||||
int id = 0;
|
||||
|
||||
error = sysdev_class_register(&intc_sysdev_class);
|
||||
#ifdef CONFIG_INTC_USERIMASK
|
||||
if (!error && uimask)
|
||||
error = sysdev_class_create_file(&intc_sysdev_class,
|
||||
&attr_userimask);
|
||||
#endif
|
||||
if (!error) {
|
||||
list_for_each_entry(d, &intc_list, list) {
|
||||
d->sysdev.id = id;
|
||||
|
@ -99,6 +99,15 @@ struct intc_desc symbol __initdata = { \
|
||||
int __init register_intc_controller(struct intc_desc *desc);
|
||||
int intc_set_priority(unsigned int irq, unsigned int prio);
|
||||
|
||||
#ifdef CONFIG_INTC_USERIMASK
|
||||
int register_intc_userimask(unsigned long addr);
|
||||
#else
|
||||
static inline int register_intc_userimask(unsigned long addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int reserve_irq_vector(unsigned int irq);
|
||||
void reserve_irq_legacy(void);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user