diff options
| author | Tony Lindgren <tony@atomide.com> | 2014-02-28 16:35:02 -0500 |
|---|---|---|
| committer | Tony Lindgren <tony@atomide.com> | 2014-02-28 16:35:02 -0500 |
| commit | 9594274201ac3c67d5c569aae63792e58bcd33e6 (patch) | |
| tree | e5b4eead06199bbd608142906fe571c293ac50ab /drivers | |
| parent | 38dbfb59d1175ef458d006556061adeaa8751b72 (diff) | |
| parent | 5c61e61966076406a1ab238f5b8588a0caa26c16 (diff) | |
Merge branch 'crossbar_3.14_rc1' of git://github.com/Sricharanti/sricharan into omap-for-v3.15/crossbar
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/irqchip/Kconfig | 8 | ||||
| -rw-r--r-- | drivers/irqchip/Makefile | 1 | ||||
| -rw-r--r-- | drivers/irqchip/irq-crossbar.c | 208 | ||||
| -rw-r--r-- | drivers/irqchip/irq-gic.c | 82 |
4 files changed, 289 insertions, 10 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 61ffdca96e25..111068782da4 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig | |||
| @@ -69,3 +69,11 @@ config VERSATILE_FPGA_IRQ_NR | |||
| 69 | config XTENSA_MX | 69 | config XTENSA_MX |
| 70 | bool | 70 | bool |
| 71 | select IRQ_DOMAIN | 71 | select IRQ_DOMAIN |
| 72 | |||
| 73 | config IRQ_CROSSBAR | ||
| 74 | bool | ||
| 75 | help | ||
| 76 | Support for a CROSSBAR ip that preceeds the main interrupt controller. | ||
| 77 | The primary irqchip invokes the crossbar's callback which inturn allocates | ||
| 78 | a free irq and configures the IP. Thus the peripheral interrupts are | ||
| 79 | routed to one of the free irqchip interrupt lines. | ||
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 86b484cb3ec2..3e776cb8dd46 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile | |||
| @@ -25,3 +25,4 @@ obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o | |||
| 25 | obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o | 25 | obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o |
| 26 | obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o | 26 | obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o |
| 27 | obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o | 27 | obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o |
| 28 | obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o | ||
diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c new file mode 100644 index 000000000000..fc817d28d1fe --- /dev/null +++ b/drivers/irqchip/irq-crossbar.c | |||
| @@ -0,0 +1,208 @@ | |||
| 1 | /* | ||
| 2 | * drivers/irqchip/irq-crossbar.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com | ||
| 5 | * Author: Sricharan R <r.sricharan@ti.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | * | ||
| 11 | */ | ||
| 12 | #include <linux/err.h> | ||
| 13 | #include <linux/io.h> | ||
| 14 | #include <linux/of_address.h> | ||
| 15 | #include <linux/of_irq.h> | ||
| 16 | #include <linux/slab.h> | ||
| 17 | #include <linux/irqchip/arm-gic.h> | ||
| 18 | |||
| 19 | #define IRQ_FREE -1 | ||
| 20 | #define GIC_IRQ_START 32 | ||
| 21 | |||
| 22 | /* | ||
| 23 | * @int_max: maximum number of supported interrupts | ||
| 24 | * @irq_map: array of interrupts to crossbar number mapping | ||
| 25 | * @crossbar_base: crossbar base address | ||
| 26 | * @register_offsets: offsets for each irq number | ||
| 27 | */ | ||
| 28 | struct crossbar_device { | ||
| 29 | uint int_max; | ||
| 30 | uint *irq_map; | ||
| 31 | void __iomem *crossbar_base; | ||
| 32 | int *register_offsets; | ||
| 33 | void (*write) (int, int); | ||
| 34 | }; | ||
| 35 | |||
| 36 | static struct crossbar_device *cb; | ||
| 37 | |||
| 38 | static inline void crossbar_writel(int irq_no, int cb_no) | ||
| 39 | { | ||
| 40 | writel(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); | ||
| 41 | } | ||
| 42 | |||
| 43 | static inline void crossbar_writew(int irq_no, int cb_no) | ||
| 44 | { | ||
| 45 | writew(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); | ||
| 46 | } | ||
| 47 | |||
| 48 | static inline void crossbar_writeb(int irq_no, int cb_no) | ||
| 49 | { | ||
| 50 | writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); | ||
| 51 | } | ||
| 52 | |||
| 53 | static inline int allocate_free_irq(int cb_no) | ||
| 54 | { | ||
| 55 | int i; | ||
| 56 | |||
| 57 | for (i = 0; i < cb->int_max; i++) { | ||
| 58 | if (cb->irq_map[i] == IRQ_FREE) { | ||
| 59 | cb->irq_map[i] = cb_no; | ||
| 60 | return i; | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | return -ENODEV; | ||
| 65 | } | ||
| 66 | |||
| 67 | static int crossbar_domain_map(struct irq_domain *d, unsigned int irq, | ||
| 68 | irq_hw_number_t hw) | ||
| 69 | { | ||
| 70 | cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]); | ||
| 71 | return 0; | ||
| 72 | } | ||
| 73 | |||
| 74 | static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq) | ||
| 75 | { | ||
| 76 | irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq; | ||
| 77 | |||
| 78 | if (hw > GIC_IRQ_START) | ||
| 79 | cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE; | ||
| 80 | } | ||
| 81 | |||
| 82 | static int crossbar_domain_xlate(struct irq_domain *d, | ||
| 83 | struct device_node *controller, | ||
| 84 | const u32 *intspec, unsigned int intsize, | ||
| 85 | unsigned long *out_hwirq, | ||
| 86 | unsigned int *out_type) | ||
| 87 | { | ||
| 88 | unsigned long ret; | ||
| 89 | |||
| 90 | ret = allocate_free_irq(intspec[1]); | ||
| 91 | |||
| 92 | if (IS_ERR_VALUE(ret)) | ||
| 93 | return ret; | ||
| 94 | |||
| 95 | *out_hwirq = ret + GIC_IRQ_START; | ||
| 96 | return 0; | ||
| 97 | } | ||
| 98 | |||
| 99 | const struct irq_domain_ops routable_irq_domain_ops = { | ||
| 100 | .map = crossbar_domain_map, | ||
| 101 | .unmap = crossbar_domain_unmap, | ||
| 102 | .xlate = crossbar_domain_xlate | ||
| 103 | }; | ||
| 104 | |||
| 105 | static int __init crossbar_of_init(struct device_node *node) | ||
| 106 | { | ||
| 107 | int i, size, max, reserved = 0, entry; | ||
| 108 | const __be32 *irqsr; | ||
| 109 | |||
| 110 | cb = kzalloc(sizeof(struct cb_device *), GFP_KERNEL); | ||
| 111 | |||
| 112 | if (!cb) | ||
| 113 | return -ENOMEM; | ||
| 114 | |||
| 115 | cb->crossbar_base = of_iomap(node, 0); | ||
| 116 | if (!cb->crossbar_base) | ||
| 117 | goto err1; | ||
| 118 | |||
| 119 | of_property_read_u32(node, "ti,max-irqs", &max); | ||
| 120 | cb->irq_map = kzalloc(max * sizeof(int), GFP_KERNEL); | ||
| 121 | if (!cb->irq_map) | ||
| 122 | goto err2; | ||
| 123 | |||
| 124 | cb->int_max = max; | ||
| 125 | |||
| 126 | for (i = 0; i < max; i++) | ||
| 127 | cb->irq_map[i] = IRQ_FREE; | ||
| 128 | |||
| 129 | /* Get and mark reserved irqs */ | ||
| 130 | irqsr = of_get_property(node, "ti,irqs-reserved", &size); | ||
| 131 | if (irqsr) { | ||
| 132 | size /= sizeof(__be32); | ||
| 133 | |||
| 134 | for (i = 0; i < size; i++) { | ||
| 135 | of_property_read_u32_index(node, | ||
| 136 | "ti,irqs-reserved", | ||
| 137 | i, &entry); | ||
| 138 | if (entry > max) { | ||
| 139 | pr_err("Invalid reserved entry\n"); | ||
| 140 | goto err3; | ||
| 141 | } | ||
| 142 | cb->irq_map[entry] = 0; | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | cb->register_offsets = kzalloc(max * sizeof(int), GFP_KERNEL); | ||
| 147 | if (!cb->register_offsets) | ||
| 148 | goto err3; | ||
| 149 | |||
| 150 | of_property_read_u32(node, "ti,reg-size", &size); | ||
| 151 | |||
| 152 | switch (size) { | ||
| 153 | case 1: | ||
| 154 | cb->write = crossbar_writeb; | ||
| 155 | break; | ||
| 156 | case 2: | ||
| 157 | cb->write = crossbar_writew; | ||
| 158 | break; | ||
| 159 | case 4: | ||
| 160 | cb->write = crossbar_writel; | ||
| 161 | break; | ||
| 162 | default: | ||
| 163 | pr_err("Invalid reg-size property\n"); | ||
| 164 | goto err4; | ||
| 165 | break; | ||
| 166 | } | ||
| 167 | |||
| 168 | /* | ||
| 169 | * Register offsets are not linear because of the | ||
| 170 | * reserved irqs. so find and store the offsets once. | ||
| 171 | */ | ||
| 172 | for (i = 0; i < max; i++) { | ||
| 173 | if (!cb->irq_map[i]) | ||
| 174 | continue; | ||
| 175 | |||
| 176 | cb->register_offsets[i] = reserved; | ||
| 177 | reserved += size; | ||
| 178 | } | ||
| 179 | |||
| 180 | register_routable_domain_ops(&routable_irq_domain_ops); | ||
| 181 | return 0; | ||
| 182 | |||
| 183 | err4: | ||
| 184 | kfree(cb->register_offsets); | ||
| 185 | err3: | ||
| 186 | kfree(cb->irq_map); | ||
| 187 | err2: | ||
| 188 | iounmap(cb->crossbar_base); | ||
| 189 | err1: | ||
| 190 | kfree(cb); | ||
| 191 | return -ENOMEM; | ||
| 192 | } | ||
| 193 | |||
| 194 | static const struct of_device_id crossbar_match[] __initconst = { | ||
| 195 | { .compatible = "ti,irq-crossbar" }, | ||
| 196 | {} | ||
| 197 | }; | ||
| 198 | |||
| 199 | int __init irqcrossbar_init(void) | ||
| 200 | { | ||
| 201 | struct device_node *np; | ||
| 202 | np = of_find_matching_node(NULL, crossbar_match); | ||
| 203 | if (!np) | ||
| 204 | return -ENODEV; | ||
| 205 | |||
| 206 | crossbar_of_init(np); | ||
| 207 | return 0; | ||
| 208 | } | ||
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 341c6016812d..07a7050841ec 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c | |||
| @@ -824,16 +824,25 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, | |||
| 824 | irq_set_chip_and_handler(irq, &gic_chip, | 824 | irq_set_chip_and_handler(irq, &gic_chip, |
| 825 | handle_fasteoi_irq); | 825 | handle_fasteoi_irq); |
| 826 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); | 826 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); |
| 827 | |||
| 828 | gic_routable_irq_domain_ops->map(d, irq, hw); | ||
| 827 | } | 829 | } |
| 828 | irq_set_chip_data(irq, d->host_data); | 830 | irq_set_chip_data(irq, d->host_data); |
| 829 | return 0; | 831 | return 0; |
| 830 | } | 832 | } |
| 831 | 833 | ||
| 834 | static void gic_irq_domain_unmap(struct irq_domain *d, unsigned int irq) | ||
| 835 | { | ||
| 836 | gic_routable_irq_domain_ops->unmap(d, irq); | ||
| 837 | } | ||
| 838 | |||
| 832 | static int gic_irq_domain_xlate(struct irq_domain *d, | 839 | static int gic_irq_domain_xlate(struct irq_domain *d, |
| 833 | struct device_node *controller, | 840 | struct device_node *controller, |
| 834 | const u32 *intspec, unsigned int intsize, | 841 | const u32 *intspec, unsigned int intsize, |
| 835 | unsigned long *out_hwirq, unsigned int *out_type) | 842 | unsigned long *out_hwirq, unsigned int *out_type) |
| 836 | { | 843 | { |
| 844 | unsigned long ret = 0; | ||
| 845 | |||
| 837 | if (d->of_node != controller) | 846 | if (d->of_node != controller) |
| 838 | return -EINVAL; | 847 | return -EINVAL; |
| 839 | if (intsize < 3) | 848 | if (intsize < 3) |
| @@ -843,11 +852,20 @@ static int gic_irq_domain_xlate(struct irq_domain *d, | |||
| 843 | *out_hwirq = intspec[1] + 16; | 852 | *out_hwirq = intspec[1] + 16; |
| 844 | 853 | ||
| 845 | /* For SPIs, we need to add 16 more to get the GIC irq ID number */ | 854 | /* For SPIs, we need to add 16 more to get the GIC irq ID number */ |
| 846 | if (!intspec[0]) | 855 | if (!intspec[0]) { |
| 847 | *out_hwirq += 16; | 856 | ret = gic_routable_irq_domain_ops->xlate(d, controller, |
| 857 | intspec, | ||
| 858 | intsize, | ||
| 859 | out_hwirq, | ||
| 860 | out_type); | ||
| 861 | |||
| 862 | if (IS_ERR_VALUE(ret)) | ||
| 863 | return ret; | ||
| 864 | } | ||
| 848 | 865 | ||
| 849 | *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; | 866 | *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; |
| 850 | return 0; | 867 | |
| 868 | return ret; | ||
| 851 | } | 869 | } |
| 852 | 870 | ||
| 853 | #ifdef CONFIG_SMP | 871 | #ifdef CONFIG_SMP |
| @@ -871,9 +889,41 @@ static struct notifier_block gic_cpu_notifier = { | |||
| 871 | 889 | ||
| 872 | const struct irq_domain_ops gic_irq_domain_ops = { | 890 | const struct irq_domain_ops gic_irq_domain_ops = { |
| 873 | .map = gic_irq_domain_map, | 891 | .map = gic_irq_domain_map, |
| 892 | .unmap = gic_irq_domain_unmap, | ||
| 874 | .xlate = gic_irq_domain_xlate, | 893 | .xlate = gic_irq_domain_xlate, |
| 875 | }; | 894 | }; |
| 876 | 895 | ||
| 896 | /* Default functions for routable irq domain */ | ||
| 897 | static int gic_routable_irq_domain_map(struct irq_domain *d, unsigned int irq, | ||
| 898 | irq_hw_number_t hw) | ||
| 899 | { | ||
| 900 | return 0; | ||
| 901 | } | ||
| 902 | |||
| 903 | static void gic_routable_irq_domain_unmap(struct irq_domain *d, | ||
| 904 | unsigned int irq) | ||
| 905 | { | ||
| 906 | } | ||
| 907 | |||
| 908 | static int gic_routable_irq_domain_xlate(struct irq_domain *d, | ||
| 909 | struct device_node *controller, | ||
| 910 | const u32 *intspec, unsigned int intsize, | ||
| 911 | unsigned long *out_hwirq, | ||
| 912 | unsigned int *out_type) | ||
| 913 | { | ||
| 914 | *out_hwirq += 16; | ||
| 915 | return 0; | ||
| 916 | } | ||
| 917 | |||
| 918 | const struct irq_domain_ops gic_default_routable_irq_domain_ops = { | ||
| 919 | .map = gic_routable_irq_domain_map, | ||
| 920 | .unmap = gic_routable_irq_domain_unmap, | ||
| 921 | .xlate = gic_routable_irq_domain_xlate, | ||
| 922 | }; | ||
| 923 | |||
| 924 | const struct irq_domain_ops *gic_routable_irq_domain_ops = | ||
| 925 | &gic_default_routable_irq_domain_ops; | ||
| 926 | |||
| 877 | void __init gic_init_bases(unsigned int gic_nr, int irq_start, | 927 | void __init gic_init_bases(unsigned int gic_nr, int irq_start, |
| 878 | void __iomem *dist_base, void __iomem *cpu_base, | 928 | void __iomem *dist_base, void __iomem *cpu_base, |
| 879 | u32 percpu_offset, struct device_node *node) | 929 | u32 percpu_offset, struct device_node *node) |
| @@ -881,6 +931,7 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, | |||
| 881 | irq_hw_number_t hwirq_base; | 931 | irq_hw_number_t hwirq_base; |
| 882 | struct gic_chip_data *gic; | 932 | struct gic_chip_data *gic; |
| 883 | int gic_irqs, irq_base, i; | 933 | int gic_irqs, irq_base, i; |
| 934 | int nr_routable_irqs; | ||
| 884 | 935 | ||
| 885 | BUG_ON(gic_nr >= MAX_GIC_NR); | 936 | BUG_ON(gic_nr >= MAX_GIC_NR); |
| 886 | 937 | ||
| @@ -946,14 +997,25 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, | |||
| 946 | gic->gic_irqs = gic_irqs; | 997 | gic->gic_irqs = gic_irqs; |
| 947 | 998 | ||
| 948 | gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */ | 999 | gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */ |
| 949 | irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, numa_node_id()); | 1000 | |
| 950 | if (IS_ERR_VALUE(irq_base)) { | 1001 | if (of_property_read_u32(node, "arm,routable-irqs", |
| 951 | WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n", | 1002 | &nr_routable_irqs)) { |
| 952 | irq_start); | 1003 | irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, |
| 953 | irq_base = irq_start; | 1004 | numa_node_id()); |
| 1005 | if (IS_ERR_VALUE(irq_base)) { | ||
| 1006 | WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n", | ||
| 1007 | irq_start); | ||
| 1008 | irq_base = irq_start; | ||
| 1009 | } | ||
| 1010 | |||
| 1011 | gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base, | ||
| 1012 | hwirq_base, &gic_irq_domain_ops, gic); | ||
| 1013 | } else { | ||
| 1014 | gic->domain = irq_domain_add_linear(node, nr_routable_irqs, | ||
| 1015 | &gic_irq_domain_ops, | ||
| 1016 | gic); | ||
| 954 | } | 1017 | } |
| 955 | gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base, | 1018 | |
| 956 | hwirq_base, &gic_irq_domain_ops, gic); | ||
| 957 | if (WARN_ON(!gic->domain)) | 1019 | if (WARN_ON(!gic->domain)) |
| 958 | return; | 1020 | return; |
| 959 | 1021 | ||
