diff options
author | Paul Mundt <lethal@linux-sh.org> | 2010-04-13 01:43:03 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2010-04-13 01:43:03 -0400 |
commit | 43b8774dc409ea5d9369b978e2e7bc79289f0522 (patch) | |
tree | 13aa346ff8f30786e8ce3ccfdd8341d182ce4c87 | |
parent | 12129fea50edcd696a9556523b058d6c445f21d8 (diff) |
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>
-rw-r--r-- | arch/sh/Kconfig | 2 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh4a/setup-sh7786.c | 3 | ||||
-rw-r--r-- | drivers/sh/Kconfig | 13 | ||||
-rw-r--r-- | drivers/sh/intc.c | 69 | ||||
-rw-r--r-- | include/linux/sh_intc.h | 9 |
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 | ||
735 | source "drivers/sh/Kconfig" | ||
736 | |||
735 | endmenu | 737 | endmenu |
736 | 738 | ||
737 | menu "Boot options" | 739 | menu "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 | ||
911 | void __init plat_irq_setup(void) | 913 | void __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 | ||
926 | void __init plat_irq_setup_pins(int mode) | 929 | void __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 @@ | |||
1 | config 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 | ||
97 | static unsigned int intc_prio_level[NR_IRQS]; /* for now */ | 98 | static unsigned int intc_prio_level[NR_IRQS]; /* for now */ |
99 | static unsigned int default_prio_level = 2; /* 2 - 16 */ | ||
98 | static unsigned long ack_handle[NR_IRQS]; | 100 | static unsigned long ack_handle[NR_IRQS]; |
99 | 101 | ||
100 | static inline struct intc_desc_int *get_intc_desc(unsigned int irq) | 102 | 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 | /* 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 | ||
1043 | static void __iomem *uimask; | ||
1044 | |||
1045 | int 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 | |||
1060 | static ssize_t | ||
1061 | show_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 | |||
1067 | static ssize_t | ||
1068 | store_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 | |||
1096 | static SYSDEV_CLASS_ATTR(userimask, S_IRUSR | S_IWUSR, | ||
1097 | show_intc_userimask, store_intc_userimask); | ||
1098 | #endif | ||
1099 | |||
1040 | static ssize_t | 1100 | static ssize_t |
1041 | show_intc_name(struct sys_device *dev, struct sysdev_attribute *attr, char *buf) | 1101 | show_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 = { \ | |||
99 | int __init register_intc_controller(struct intc_desc *desc); | 99 | int __init register_intc_controller(struct intc_desc *desc); |
100 | int intc_set_priority(unsigned int irq, unsigned int prio); | 100 | int intc_set_priority(unsigned int irq, unsigned int prio); |
101 | 101 | ||
102 | #ifdef CONFIG_INTC_USERIMASK | ||
103 | int register_intc_userimask(unsigned long addr); | ||
104 | #else | ||
105 | static inline int register_intc_userimask(unsigned long addr) | ||
106 | { | ||
107 | return 0; | ||
108 | } | ||
109 | #endif | ||
110 | |||
102 | int reserve_irq_vector(unsigned int irq); | 111 | int reserve_irq_vector(unsigned int irq); |
103 | void reserve_irq_legacy(void); | 112 | void reserve_irq_legacy(void); |
104 | 113 | ||