aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/sh
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2010-04-13 01:43:03 -0400
committerPaul Mundt <lethal@linux-sh.org>2010-04-13 01:43:03 -0400
commit43b8774dc409ea5d9369b978e2e7bc79289f0522 (patch)
tree13aa346ff8f30786e8ce3ccfdd8341d182ce4c87 /drivers/sh
parent12129fea50edcd696a9556523b058d6c445f21d8 (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/Kconfig13
-rw-r--r--drivers/sh/intc.c69
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 @@
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;