diff options
Diffstat (limited to 'drivers/sh/intc/userimask.c')
-rw-r--r-- | drivers/sh/intc/userimask.c | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/drivers/sh/intc/userimask.c b/drivers/sh/intc/userimask.c new file mode 100644 index 000000000000..e32304b66cf1 --- /dev/null +++ b/drivers/sh/intc/userimask.c | |||
@@ -0,0 +1,83 @@ | |||
1 | /* | ||
2 | * Support for hardware-assisted userspace interrupt masking. | ||
3 | * | ||
4 | * Copyright (C) 2010 Paul Mundt | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License. See the file "COPYING" in the main directory of this archive | ||
8 | * for more details. | ||
9 | */ | ||
10 | #define pr_fmt(fmt) "intc: " fmt | ||
11 | |||
12 | #include <linux/errno.h> | ||
13 | #include <linux/sysdev.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/io.h> | ||
16 | #include <asm/sizes.h> | ||
17 | #include "internals.h" | ||
18 | |||
19 | static void __iomem *uimask; | ||
20 | |||
21 | static ssize_t | ||
22 | show_intc_userimask(struct sysdev_class *cls, | ||
23 | struct sysdev_class_attribute *attr, char *buf) | ||
24 | { | ||
25 | return sprintf(buf, "%d\n", (__raw_readl(uimask) >> 4) & 0xf); | ||
26 | } | ||
27 | |||
28 | static ssize_t | ||
29 | store_intc_userimask(struct sysdev_class *cls, | ||
30 | struct sysdev_class_attribute *attr, | ||
31 | const char *buf, size_t count) | ||
32 | { | ||
33 | unsigned long level; | ||
34 | |||
35 | level = simple_strtoul(buf, NULL, 10); | ||
36 | |||
37 | /* | ||
38 | * Minimal acceptable IRQ levels are in the 2 - 16 range, but | ||
39 | * these are chomped so as to not interfere with normal IRQs. | ||
40 | * | ||
41 | * Level 1 is a special case on some CPUs in that it's not | ||
42 | * directly settable, but given that USERIMASK cuts off below a | ||
43 | * certain level, we don't care about this limitation here. | ||
44 | * Level 0 on the other hand equates to user masking disabled. | ||
45 | * | ||
46 | * We use the default priority level as a cut off so that only | ||
47 | * special case opt-in IRQs can be mangled. | ||
48 | */ | ||
49 | if (level >= intc_get_dfl_prio_level()) | ||
50 | return -EINVAL; | ||
51 | |||
52 | __raw_writel(0xa5 << 24 | level << 4, uimask); | ||
53 | |||
54 | return count; | ||
55 | } | ||
56 | |||
57 | static SYSDEV_CLASS_ATTR(userimask, S_IRUSR | S_IWUSR, | ||
58 | show_intc_userimask, store_intc_userimask); | ||
59 | |||
60 | |||
61 | static int __init userimask_sysdev_init(void) | ||
62 | { | ||
63 | if (unlikely(!uimask)) | ||
64 | return -ENXIO; | ||
65 | |||
66 | return sysdev_class_create_file(&intc_sysdev_class, &attr_userimask); | ||
67 | } | ||
68 | late_initcall(userimask_sysdev_init); | ||
69 | |||
70 | int register_intc_userimask(unsigned long addr) | ||
71 | { | ||
72 | if (unlikely(uimask)) | ||
73 | return -EBUSY; | ||
74 | |||
75 | uimask = ioremap_nocache(addr, SZ_4K); | ||
76 | if (unlikely(!uimask)) | ||
77 | return -ENOMEM; | ||
78 | |||
79 | pr_info("userimask support registered for levels 0 -> %d\n", | ||
80 | intc_get_dfl_prio_level() - 1); | ||
81 | |||
82 | return 0; | ||
83 | } | ||