diff options
author | Paul Mundt <lethal@linux-sh.org> | 2010-09-23 07:09:38 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2010-10-04 12:15:47 -0400 |
commit | 44629f57accccbb8e6d443246fe6f51b42f7f781 (patch) | |
tree | b75bab4a39a2be40f58a9d6daac4a208035300fe | |
parent | e8184a47c9cc04380553114815356d1042a27788 (diff) |
sh: intc: Implement reverse mapping for IRQs to per-controller IDs.
This implements a scheme roughly analogous to the PowerPC virtual to
hardware IRQ mapping, which we use for IRQ to per-controller ID mapping.
This makes it possible for drivers to use the IDs directly for lookup
instead of hardcoding the vector.
The main motivation for this work is as a building block for dynamically
allocating virtual IRQs for demuxing INTC events sharing a single INTEVT
in addition to a common masking source.
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
-rw-r--r-- | drivers/sh/Kconfig | 9 | ||||
-rw-r--r-- | drivers/sh/intc.c | 91 | ||||
-rw-r--r-- | include/linux/sh_intc.h | 1 |
3 files changed, 100 insertions, 1 deletions
diff --git a/drivers/sh/Kconfig b/drivers/sh/Kconfig index 1dc8b7ae35fb..e01ae42774af 100644 --- a/drivers/sh/Kconfig +++ b/drivers/sh/Kconfig | |||
@@ -22,3 +22,12 @@ config INTC_BALANCING | |||
22 | vectors. | 22 | vectors. |
23 | 23 | ||
24 | If in doubt, say N. | 24 | If in doubt, say N. |
25 | |||
26 | config INTC_MAPPING_DEBUG | ||
27 | bool "Expose IRQ to per-controller id mapping via debugfs" | ||
28 | depends on DEBUG_FS | ||
29 | help | ||
30 | This will create a debugfs entry for showing the relationship | ||
31 | between system IRQs and the per-controller id tables. | ||
32 | |||
33 | If in doubt, say N. | ||
diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c index 4e01d65e5edb..a27dcb4254c7 100644 --- a/drivers/sh/intc.c +++ b/drivers/sh/intc.c | |||
@@ -30,6 +30,11 @@ | |||
30 | #include <linux/topology.h> | 30 | #include <linux/topology.h> |
31 | #include <linux/bitmap.h> | 31 | #include <linux/bitmap.h> |
32 | #include <linux/cpumask.h> | 32 | #include <linux/cpumask.h> |
33 | #include <linux/spinlock.h> | ||
34 | #include <linux/debugfs.h> | ||
35 | #include <linux/seq_file.h> | ||
36 | #include <linux/radix-tree.h> | ||
37 | #include <linux/mutex.h> | ||
33 | #include <asm/sizes.h> | 38 | #include <asm/sizes.h> |
34 | 39 | ||
35 | #define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \ | 40 | #define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \ |
@@ -54,9 +59,15 @@ struct intc_window { | |||
54 | unsigned long size; | 59 | unsigned long size; |
55 | }; | 60 | }; |
56 | 61 | ||
62 | struct intc_map_entry { | ||
63 | intc_enum enum_id; | ||
64 | struct intc_desc_int *desc; | ||
65 | }; | ||
66 | |||
57 | struct intc_desc_int { | 67 | struct intc_desc_int { |
58 | struct list_head list; | 68 | struct list_head list; |
59 | struct sys_device sysdev; | 69 | struct sys_device sysdev; |
70 | struct radix_tree_root tree; | ||
60 | pm_message_t state; | 71 | pm_message_t state; |
61 | unsigned long *reg; | 72 | unsigned long *reg; |
62 | #ifdef CONFIG_SMP | 73 | #ifdef CONFIG_SMP |
@@ -86,7 +97,9 @@ static LIST_HEAD(intc_list); | |||
86 | * unused irq_desc positions in the sparse array. | 97 | * unused irq_desc positions in the sparse array. |
87 | */ | 98 | */ |
88 | static DECLARE_BITMAP(intc_irq_map, NR_IRQS); | 99 | static DECLARE_BITMAP(intc_irq_map, NR_IRQS); |
100 | static struct intc_map_entry intc_irq_xlate[NR_IRQS]; | ||
89 | static DEFINE_SPINLOCK(vector_lock); | 101 | static DEFINE_SPINLOCK(vector_lock); |
102 | static DEFINE_MUTEX(irq_xlate_mutex); | ||
90 | 103 | ||
91 | #ifdef CONFIG_SMP | 104 | #ifdef CONFIG_SMP |
92 | #define IS_SMP(x) x.smp | 105 | #define IS_SMP(x) x.smp |
@@ -828,6 +841,26 @@ static unsigned int __init intc_sense_data(struct intc_desc *desc, | |||
828 | return 0; | 841 | return 0; |
829 | } | 842 | } |
830 | 843 | ||
844 | unsigned int intc_irq_lookup(const char *chipname, intc_enum enum_id) | ||
845 | { | ||
846 | struct intc_map_entry *ptr; | ||
847 | struct intc_desc_int *d; | ||
848 | unsigned int irq = 0; | ||
849 | |||
850 | list_for_each_entry(d, &intc_list, list) { | ||
851 | if (strcmp(d->chip.name, chipname) == 0) { | ||
852 | ptr = radix_tree_lookup(&d->tree, enum_id); | ||
853 | if (ptr) { | ||
854 | irq = ptr - intc_irq_xlate; | ||
855 | break; | ||
856 | } | ||
857 | } | ||
858 | } | ||
859 | |||
860 | return irq; | ||
861 | } | ||
862 | EXPORT_SYMBOL_GPL(intc_irq_lookup); | ||
863 | |||
831 | static void __init intc_register_irq(struct intc_desc *desc, | 864 | static void __init intc_register_irq(struct intc_desc *desc, |
832 | struct intc_desc_int *d, | 865 | struct intc_desc_int *d, |
833 | intc_enum enum_id, | 866 | intc_enum enum_id, |
@@ -837,10 +870,15 @@ static void __init intc_register_irq(struct intc_desc *desc, | |||
837 | unsigned int data[2], primary; | 870 | unsigned int data[2], primary; |
838 | 871 | ||
839 | /* | 872 | /* |
840 | * Register the IRQ position with the global IRQ map | 873 | * Register the IRQ position with the global IRQ map, then insert |
874 | * it in to the radix tree. | ||
841 | */ | 875 | */ |
842 | set_bit(irq, intc_irq_map); | 876 | set_bit(irq, intc_irq_map); |
843 | 877 | ||
878 | mutex_lock(&irq_xlate_mutex); | ||
879 | radix_tree_insert(&d->tree, enum_id, &intc_irq_xlate[irq]); | ||
880 | mutex_unlock(&irq_xlate_mutex); | ||
881 | |||
844 | /* | 882 | /* |
845 | * Prefer single interrupt source bitmap over other combinations: | 883 | * Prefer single interrupt source bitmap over other combinations: |
846 | * | 884 | * |
@@ -1082,6 +1120,9 @@ int __init register_intc_controller(struct intc_desc *desc) | |||
1082 | continue; | 1120 | continue; |
1083 | } | 1121 | } |
1084 | 1122 | ||
1123 | intc_irq_xlate[irq].enum_id = vect->enum_id; | ||
1124 | intc_irq_xlate[irq].desc = d; | ||
1125 | |||
1085 | intc_register_irq(desc, d, vect->enum_id, irq); | 1126 | intc_register_irq(desc, d, vect->enum_id, irq); |
1086 | 1127 | ||
1087 | for (k = i + 1; k < hw->nr_vectors; k++) { | 1128 | for (k = i + 1; k < hw->nr_vectors; k++) { |
@@ -1196,6 +1237,54 @@ static SYSDEV_CLASS_ATTR(userimask, S_IRUSR | S_IWUSR, | |||
1196 | show_intc_userimask, store_intc_userimask); | 1237 | show_intc_userimask, store_intc_userimask); |
1197 | #endif | 1238 | #endif |
1198 | 1239 | ||
1240 | #ifdef CONFIG_INTC_MAPPING_DEBUG | ||
1241 | static int intc_irq_xlate_debug(struct seq_file *m, void *priv) | ||
1242 | { | ||
1243 | int i; | ||
1244 | |||
1245 | seq_printf(m, "%-5s %-7s %-15s\n", "irq", "enum", "chip name"); | ||
1246 | |||
1247 | for (i = 1; i < nr_irqs; i++) { | ||
1248 | struct intc_desc_int *desc = intc_irq_xlate[i].desc; | ||
1249 | |||
1250 | if (!desc) | ||
1251 | continue; | ||
1252 | |||
1253 | seq_printf(m, "%5d ", i); | ||
1254 | seq_printf(m, "0x%05x ", intc_irq_xlate[i].enum_id); | ||
1255 | seq_printf(m, "%-15s\n", desc->chip.name); | ||
1256 | } | ||
1257 | |||
1258 | return 0; | ||
1259 | } | ||
1260 | |||
1261 | static int intc_irq_xlate_open(struct inode *inode, struct file *file) | ||
1262 | { | ||
1263 | return single_open(file, intc_irq_xlate_debug, inode->i_private); | ||
1264 | } | ||
1265 | |||
1266 | static const struct file_operations intc_irq_xlate_fops = { | ||
1267 | .open = intc_irq_xlate_open, | ||
1268 | .read = seq_read, | ||
1269 | .llseek = seq_lseek, | ||
1270 | .release = single_release, | ||
1271 | }; | ||
1272 | |||
1273 | static int __init intc_irq_xlate_init(void) | ||
1274 | { | ||
1275 | /* | ||
1276 | * XXX.. use arch_debugfs_dir here when all of the intc users are | ||
1277 | * converted. | ||
1278 | */ | ||
1279 | if (debugfs_create_file("intc_irq_xlate", S_IRUGO, NULL, NULL, | ||
1280 | &intc_irq_xlate_fops) == NULL) | ||
1281 | return -ENOMEM; | ||
1282 | |||
1283 | return 0; | ||
1284 | } | ||
1285 | fs_initcall(intc_irq_xlate_init); | ||
1286 | #endif | ||
1287 | |||
1199 | static ssize_t | 1288 | static ssize_t |
1200 | show_intc_name(struct sys_device *dev, struct sysdev_attribute *attr, char *buf) | 1289 | show_intc_name(struct sys_device *dev, struct sysdev_attribute *attr, char *buf) |
1201 | { | 1290 | { |
diff --git a/include/linux/sh_intc.h b/include/linux/sh_intc.h index bff2f286ca61..d40fd77fa75c 100644 --- a/include/linux/sh_intc.h +++ b/include/linux/sh_intc.h | |||
@@ -108,6 +108,7 @@ struct intc_desc symbol __initdata = { \ | |||
108 | int __init register_intc_controller(struct intc_desc *desc); | 108 | int __init register_intc_controller(struct intc_desc *desc); |
109 | void reserve_intc_vectors(struct intc_vect *vectors, unsigned int nr_vecs); | 109 | void reserve_intc_vectors(struct intc_vect *vectors, unsigned int nr_vecs); |
110 | int intc_set_priority(unsigned int irq, unsigned int prio); | 110 | int intc_set_priority(unsigned int irq, unsigned int prio); |
111 | unsigned int intc_irq_lookup(const char *chipname, intc_enum enum_id); | ||
111 | 112 | ||
112 | #ifdef CONFIG_INTC_USERIMASK | 113 | #ifdef CONFIG_INTC_USERIMASK |
113 | int register_intc_userimask(unsigned long addr); | 114 | int register_intc_userimask(unsigned long addr); |