Commit 43b8774d authored by Paul Mundt's avatar Paul Mundt

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: default avatarPaul Mundt <lethal@linux-sh.org>
parent 12129fea
...@@ -732,6 +732,8 @@ config GUSA_RB ...@@ -732,6 +732,8 @@ config GUSA_RB
LLSC, this should be more efficient than the other alternative of LLSC, this should be more efficient than the other alternative of
disabling interrupts around the atomic sequence. disabling interrupts around the atomic sequence.
source "drivers/sh/Kconfig"
endmenu endmenu
menu "Boot options" menu "Boot options"
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/sh_timer.h> #include <linux/sh_timer.h>
#include <linux/sh_intc.h>
#include <cpu/dma-register.h> #include <cpu/dma-register.h>
#include <asm/mmzone.h> #include <asm/mmzone.h>
#include <asm/dmaengine.h> #include <asm/dmaengine.h>
...@@ -907,6 +908,7 @@ static DECLARE_INTC_DESC(intc_desc_irl4567, "sh7786-irl4567", vectors_irl4567, ...@@ -907,6 +908,7 @@ static DECLARE_INTC_DESC(intc_desc_irl4567, "sh7786-irl4567", vectors_irl4567,
#define INTC_INTMSK2 INTMSK2 #define INTC_INTMSK2 INTMSK2
#define INTC_INTMSKCLR1 CnINTMSKCLR1 #define INTC_INTMSKCLR1 CnINTMSKCLR1
#define INTC_INTMSKCLR2 INTMSKCLR2 #define INTC_INTMSKCLR2 INTMSKCLR2
#define INTC_USERIMASK 0xfe411000
void __init plat_irq_setup(void) void __init plat_irq_setup(void)
{ {
...@@ -921,6 +923,7 @@ 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); __raw_writel(__raw_readl(INTC_ICR0) & ~0x00c00000, INTC_ICR0);
register_intc_controller(&intc_desc); register_intc_controller(&intc_desc);
register_intc_userimask(INTC_USERIMASK);
} }
void __init plat_irq_setup_pins(int mode) void __init plat_irq_setup_pins(int mode)
......
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 @@ ...@@ -27,6 +27,7 @@
#include <linux/topology.h> #include <linux/topology.h>
#include <linux/bitmap.h> #include <linux/bitmap.h>
#include <linux/cpumask.h> #include <linux/cpumask.h>
#include <asm/sizes.h>
#define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \ #define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \
((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \ ((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \
...@@ -94,7 +95,8 @@ static DEFINE_SPINLOCK(vector_lock); ...@@ -94,7 +95,8 @@ static DEFINE_SPINLOCK(vector_lock);
#define SMP_NR(d, x) 1 #define SMP_NR(d, x) 1
#endif #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 unsigned long ack_handle[NR_IRQS];
static inline struct intc_desc_int *get_intc_desc(unsigned int irq) 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, ...@@ -787,7 +789,7 @@ static void __init intc_register_irq(struct intc_desc *desc,
/* set priority level /* set priority level
* - this needs to be at least 2 for 5-bit priorities on 7780 * - 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 */ /* enable secondary masking method if present */
if (data[!primary]) if (data[!primary])
...@@ -1037,6 +1039,64 @@ int __init register_intc_controller(struct intc_desc *desc) ...@@ -1037,6 +1039,64 @@ int __init register_intc_controller(struct intc_desc *desc)
return -ENOMEM; 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 static ssize_t
show_intc_name(struct sys_device *dev, struct sysdev_attribute *attr, char *buf) show_intc_name(struct sys_device *dev, struct sysdev_attribute *attr, char *buf)
{ {
...@@ -1108,6 +1168,11 @@ static int __init register_intc_sysdevs(void) ...@@ -1108,6 +1168,11 @@ static int __init register_intc_sysdevs(void)
int id = 0; int id = 0;
error = sysdev_class_register(&intc_sysdev_class); 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) { if (!error) {
list_for_each_entry(d, &intc_list, list) { list_for_each_entry(d, &intc_list, list) {
d->sysdev.id = id; d->sysdev.id = id;
......
...@@ -99,6 +99,15 @@ struct intc_desc symbol __initdata = { \ ...@@ -99,6 +99,15 @@ struct intc_desc symbol __initdata = { \
int __init register_intc_controller(struct intc_desc *desc); int __init register_intc_controller(struct intc_desc *desc);
int intc_set_priority(unsigned int irq, unsigned int prio); 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); int reserve_irq_vector(unsigned int irq);
void reserve_irq_legacy(void); void reserve_irq_legacy(void);
......
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