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 /drivers/sh | |
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>
Diffstat (limited to 'drivers/sh')
-rw-r--r-- | drivers/sh/Kconfig | 13 | ||||
-rw-r--r-- | drivers/sh/intc.c | 69 |
2 files changed, 80 insertions, 2 deletions
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; |