aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/sh/Kconfig2
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7786.c3
-rw-r--r--drivers/sh/Kconfig13
-rw-r--r--drivers/sh/intc.c69
-rw-r--r--include/linux/sh_intc.h9
5 files changed, 94 insertions, 2 deletions
diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig
index 8d90564c2bcf..ba86bfba95ac 100644
--- a/arch/sh/Kconfig
+++ b/arch/sh/Kconfig
@@ -732,6 +732,8 @@ config GUSA_RB
732 LLSC, this should be more efficient than the other alternative of 732 LLSC, this should be more efficient than the other alternative of
733 disabling interrupts around the atomic sequence. 733 disabling interrupts around the atomic sequence.
734 734
735source "drivers/sh/Kconfig"
736
735endmenu 737endmenu
736 738
737menu "Boot options" 739menu "Boot options"
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c
index 61e549190873..235edf8065df 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c
@@ -21,6 +21,7 @@
21#include <linux/mm.h> 21#include <linux/mm.h>
22#include <linux/dma-mapping.h> 22#include <linux/dma-mapping.h>
23#include <linux/sh_timer.h> 23#include <linux/sh_timer.h>
24#include <linux/sh_intc.h>
24#include <cpu/dma-register.h> 25#include <cpu/dma-register.h>
25#include <asm/mmzone.h> 26#include <asm/mmzone.h>
26#include <asm/dmaengine.h> 27#include <asm/dmaengine.h>
@@ -907,6 +908,7 @@ static DECLARE_INTC_DESC(intc_desc_irl4567, "sh7786-irl4567", vectors_irl4567,
907#define INTC_INTMSK2 INTMSK2 908#define INTC_INTMSK2 INTMSK2
908#define INTC_INTMSKCLR1 CnINTMSKCLR1 909#define INTC_INTMSKCLR1 CnINTMSKCLR1
909#define INTC_INTMSKCLR2 INTMSKCLR2 910#define INTC_INTMSKCLR2 INTMSKCLR2
911#define INTC_USERIMASK 0xfe411000
910 912
911void __init plat_irq_setup(void) 913void __init plat_irq_setup(void)
912{ 914{
@@ -921,6 +923,7 @@ void __init plat_irq_setup(void)
921 __raw_writel(__raw_readl(INTC_ICR0) & ~0x00c00000, INTC_ICR0); 923 __raw_writel(__raw_readl(INTC_ICR0) & ~0x00c00000, INTC_ICR0);
922 924
923 register_intc_controller(&intc_desc); 925 register_intc_controller(&intc_desc);
926 register_intc_userimask(INTC_USERIMASK);
924} 927}
925 928
926void __init plat_irq_setup_pins(int mode) 929void __init plat_irq_setup_pins(int mode)
diff --git a/drivers/sh/Kconfig b/drivers/sh/Kconfig
new file mode 100644
index 000000000000..22c3cdaf22fe
--- /dev/null
+++ b/drivers/sh/Kconfig
@@ -0,0 +1,13 @@
1config INTC_USERIMASK
2 bool "Userspace interrupt masking support"
3 depends on ARCH_SHMOBILE || (SUPERH && CPU_SH4A)
4 help
5 This enables support for hardware-assisted userspace hardirq
6 masking.
7
8 SH-4A and newer interrupt blocks all support a special shadowed
9 page with all non-masking registers obscured when mapped in to
10 userspace. This is primarily for use by userspace device
11 drivers that are using special priority levels.
12
13 If in doubt, say N.
diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c
index 65e15828faaa..77d10acf1884 100644
--- a/drivers/sh/intc.c
+++ b/drivers/sh/intc.c
@@ -27,6 +27,7 @@
27#include <linux/topology.h> 27#include <linux/topology.h>
28#include <linux/bitmap.h> 28#include <linux/bitmap.h>
29#include <linux/cpumask.h> 29#include <linux/cpumask.h>
30#include <asm/sizes.h>
30 31
31#define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \ 32#define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \
32 ((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \ 33 ((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \
@@ -94,7 +95,8 @@ static DEFINE_SPINLOCK(vector_lock);
94#define SMP_NR(d, x) 1 95#define SMP_NR(d, x) 1
95#endif 96#endif
96 97
97static unsigned int intc_prio_level[NR_IRQS]; /* for now */ 98static unsigned int intc_prio_level[NR_IRQS]; /* for now */
99static unsigned int default_prio_level = 2; /* 2 - 16 */
98static unsigned long ack_handle[NR_IRQS]; 100static unsigned long ack_handle[NR_IRQS];
99 101
100static inline struct intc_desc_int *get_intc_desc(unsigned int irq) 102static 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 /* set priority level 789 /* set priority level
788 * - this needs to be at least 2 for 5-bit priorities on 7780 790 * - this needs to be at least 2 for 5-bit priorities on 7780
789 */ 791 */
790 intc_prio_level[irq] = 2; 792 intc_prio_level[irq] = default_prio_level;
791 793
792 /* enable secondary masking method if present */ 794 /* enable secondary masking method if present */
793 if (data[!primary]) 795 if (data[!primary])
@@ -1037,6 +1039,64 @@ err0:
1037 return -ENOMEM; 1039 return -ENOMEM;
1038} 1040}
1039 1041
1042#ifdef CONFIG_INTC_USERIMASK
1043static void __iomem *uimask;
1044
1045int register_intc_userimask(unsigned long addr)
1046{
1047 if (unlikely(uimask))
1048 return -EBUSY;
1049
1050 uimask = ioremap_nocache(addr, SZ_4K);
1051 if (unlikely(!uimask))
1052 return -ENOMEM;
1053
1054 pr_info("intc: userimask support registered for levels 0 -> %d\n",
1055 default_prio_level - 1);
1056
1057 return 0;
1058}
1059
1060static ssize_t
1061show_intc_userimask(struct sysdev_class *cls,
1062 struct sysdev_class_attribute *attr, char *buf)
1063{
1064 return sprintf(buf, "%d\n", (__raw_readl(uimask) >> 4) & 0xf);
1065}
1066
1067static ssize_t
1068store_intc_userimask(struct sysdev_class *cls,
1069 struct sysdev_class_attribute *attr,
1070 const char *buf, size_t count)
1071{
1072 unsigned long level;
1073
1074 level = simple_strtoul(buf, NULL, 10);
1075
1076 /*
1077 * Minimal acceptable IRQ levels are in the 2 - 16 range, but
1078 * these are chomped so as to not interfere with normal IRQs.
1079 *
1080 * Level 1 is a special case on some CPUs in that it's not
1081 * directly settable, but given that USERIMASK cuts off below a
1082 * certain level, we don't care about this limitation here.
1083 * Level 0 on the other hand equates to user masking disabled.
1084 *
1085 * We use default_prio_level as a cut off so that only special
1086 * case opt-in IRQs can be mangled.
1087 */
1088 if (level >= default_prio_level)
1089 return -EINVAL;
1090
1091 __raw_writel(0xa5 << 24 | level << 4, uimask);
1092
1093 return count;
1094}
1095
1096static SYSDEV_CLASS_ATTR(userimask, S_IRUSR | S_IWUSR,
1097 show_intc_userimask, store_intc_userimask);
1098#endif
1099
1040static ssize_t 1100static ssize_t
1041show_intc_name(struct sys_device *dev, struct sysdev_attribute *attr, char *buf) 1101show_intc_name(struct sys_device *dev, struct sysdev_attribute *attr, char *buf)
1042{ 1102{
@@ -1108,6 +1168,11 @@ static int __init register_intc_sysdevs(void)
1108 int id = 0; 1168 int id = 0;
1109 1169
1110 error = sysdev_class_register(&intc_sysdev_class); 1170 error = sysdev_class_register(&intc_sysdev_class);
1171#ifdef CONFIG_INTC_USERIMASK
1172 if (!error && uimask)
1173 error = sysdev_class_create_file(&intc_sysdev_class,
1174 &attr_userimask);
1175#endif
1111 if (!error) { 1176 if (!error) {
1112 list_for_each_entry(d, &intc_list, list) { 1177 list_for_each_entry(d, &intc_list, list) {
1113 d->sysdev.id = id; 1178 d->sysdev.id = id;
diff --git a/include/linux/sh_intc.h b/include/linux/sh_intc.h
index 01d8168c5a1b..f0e8cca199c7 100644
--- a/include/linux/sh_intc.h
+++ b/include/linux/sh_intc.h
@@ -99,6 +99,15 @@ struct intc_desc symbol __initdata = { \
99int __init register_intc_controller(struct intc_desc *desc); 99int __init register_intc_controller(struct intc_desc *desc);
100int intc_set_priority(unsigned int irq, unsigned int prio); 100int intc_set_priority(unsigned int irq, unsigned int prio);
101 101
102#ifdef CONFIG_INTC_USERIMASK
103int register_intc_userimask(unsigned long addr);
104#else
105static inline int register_intc_userimask(unsigned long addr)
106{
107 return 0;
108}
109#endif
110
102int reserve_irq_vector(unsigned int irq); 111int reserve_irq_vector(unsigned int irq);
103void reserve_irq_legacy(void); 112void reserve_irq_legacy(void);
104 113