aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/sh/intc.c
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/intc.c
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/intc.c')
-rw-r--r--drivers/sh/intc.c69
1 files changed, 67 insertions, 2 deletions
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;