Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'omap-for-v3.15/crossbar-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap into next/drivers

Merge OMAP crossbar support from Tony Lindgren:

Add support for GIC crossbar that routes interrupts on newer omaps.

Looks like people wanted these merged via the omap tree as it's
the only user for the GIC crossbar.

* tag 'omap-for-v3.15/crossbar-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap:
ARM: DRA: Enable Crossbar IP support for DRA7XX
ARM: OMAP4+: Correct Wakeup-gen code to use physical irq number
DRIVERS: IRQCHIP: CROSSBAR: Add support for Crossbar IP
DRIVERS: IRQCHIP: IRQ-GIC: Add support for routable irqs

Signed-off-by: Olof Johansson <olof@lixom.net>

+346 -13
+6
Documentation/devicetree/bindings/arm/gic.txt
··· 50 50 regions, used when the GIC doesn't have banked registers. The offset is 51 51 cpu-offset * cpu-nr. 52 52 53 + - arm,routable-irqs : Total number of gic irq inputs which are not directly 54 + connected from the peripherals, but are routed dynamically 55 + by a crossbar/multiplexer preceding the GIC. The GIC irq 56 + input line is assigned dynamically when the corresponding 57 + peripheral's crossbar line is mapped. 53 58 Example: 54 59 55 60 intc: interrupt-controller@fff11000 { ··· 62 57 #interrupt-cells = <3>; 63 58 #address-cells = <1>; 64 59 interrupt-controller; 60 + arm,routable-irqs = <160>; 65 61 reg = <0xfff11000 0x1000>, 66 62 <0xfff10100 0x100>; 67 63 };
+27
Documentation/devicetree/bindings/arm/omap/crossbar.txt
··· 1 + Some socs have a large number of interrupts requests to service 2 + the needs of its many peripherals and subsystems. All of the 3 + interrupt lines from the subsystems are not needed at the same 4 + time, so they have to be muxed to the irq-controller appropriately. 5 + In such places a interrupt controllers are preceded by an CROSSBAR 6 + that provides flexibility in muxing the device requests to the controller 7 + inputs. 8 + 9 + Required properties: 10 + - compatible : Should be "ti,irq-crossbar" 11 + - reg: Base address and the size of the crossbar registers. 12 + - ti,max-irqs: Total number of irqs available at the interrupt controller. 13 + - ti,reg-size: Size of a individual register in bytes. Every individual 14 + register is assumed to be of same size. Valid sizes are 1, 2, 4. 15 + - ti,irqs-reserved: List of the reserved irq lines that are not muxed using 16 + crossbar. These interrupt lines are reserved in the soc, 17 + so crossbar bar driver should not consider them as free 18 + lines. 19 + 20 + Examples: 21 + crossbar_mpu: @4a020000 { 22 + compatible = "ti,irq-crossbar"; 23 + reg = <0x4a002a48 0x130>; 24 + ti,max-irqs = <160>; 25 + ti,reg-size = <2>; 26 + ti,irqs-reserved = <0 1 2 3 5 6 131 132 139 140>; 27 + };
+1
arch/arm/mach-omap2/Kconfig
··· 85 85 select CPU_V7 86 86 select HAVE_SMP 87 87 select HAVE_ARM_ARCH_TIMER 88 + select IRQ_CROSSBAR 88 89 89 90 config ARCH_OMAP2PLUS 90 91 bool
+2 -2
arch/arm/mach-omap2/omap-wakeupgen.c
··· 138 138 unsigned long flags; 139 139 140 140 raw_spin_lock_irqsave(&wakeupgen_lock, flags); 141 - _wakeupgen_clear(d->irq, irq_target_cpu[d->irq]); 141 + _wakeupgen_clear(d->hwirq, irq_target_cpu[d->hwirq]); 142 142 raw_spin_unlock_irqrestore(&wakeupgen_lock, flags); 143 143 } 144 144 ··· 150 150 unsigned long flags; 151 151 152 152 raw_spin_lock_irqsave(&wakeupgen_lock, flags); 153 - _wakeupgen_set(d->irq, irq_target_cpu[d->irq]); 153 + _wakeupgen_set(d->hwirq, irq_target_cpu[d->hwirq]); 154 154 raw_spin_unlock_irqrestore(&wakeupgen_lock, flags); 155 155 } 156 156
+4
arch/arm/mach-omap2/omap4-common.c
··· 22 22 #include <linux/of_platform.h> 23 23 #include <linux/export.h> 24 24 #include <linux/irqchip/arm-gic.h> 25 + #include <linux/irqchip/irq-crossbar.h> 25 26 #include <linux/of_address.h> 26 27 #include <linux/reboot.h> 27 28 ··· 289 288 290 289 skip_errata_init: 291 290 omap_wakeupgen_init(); 291 + #ifdef CONFIG_IRQ_CROSSBAR 292 + irqcrossbar_init(); 293 + #endif 292 294 irqchip_init(); 293 295 }
+8
drivers/irqchip/Kconfig
··· 69 69 config XTENSA_MX 70 70 bool 71 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.
+1
drivers/irqchip/Makefile
··· 26 26 obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o 27 27 obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o 28 28 obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o 29 + obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o
+208
drivers/irqchip/irq-crossbar.c
··· 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 + }
+72 -10
drivers/irqchip/irq-gic.c
··· 824 824 irq_set_chip_and_handler(irq, &gic_chip, 825 825 handle_fasteoi_irq); 826 826 set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); 827 + 828 + gic_routable_irq_domain_ops->map(d, irq, hw); 827 829 } 828 830 irq_set_chip_data(irq, d->host_data); 829 831 return 0; 832 + } 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); 830 837 } 831 838 832 839 static int gic_irq_domain_xlate(struct irq_domain *d, ··· 841 834 const u32 *intspec, unsigned int intsize, 842 835 unsigned long *out_hwirq, unsigned int *out_type) 843 836 { 837 + unsigned long ret = 0; 838 + 844 839 if (d->of_node != controller) 845 840 return -EINVAL; 846 841 if (intsize < 3) ··· 852 843 *out_hwirq = intspec[1] + 16; 853 844 854 845 /* For SPIs, we need to add 16 more to get the GIC irq ID number */ 855 - if (!intspec[0]) 856 - *out_hwirq += 16; 846 + if (!intspec[0]) { 847 + ret = gic_routable_irq_domain_ops->xlate(d, controller, 848 + intspec, 849 + intsize, 850 + out_hwirq, 851 + out_type); 852 + 853 + if (IS_ERR_VALUE(ret)) 854 + return ret; 855 + } 857 856 858 857 *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; 859 - return 0; 858 + 859 + return ret; 860 860 } 861 861 862 862 #ifdef CONFIG_SMP ··· 889 871 890 872 const struct irq_domain_ops gic_irq_domain_ops = { 891 873 .map = gic_irq_domain_map, 874 + .unmap = gic_irq_domain_unmap, 892 875 .xlate = gic_irq_domain_xlate, 893 876 }; 877 + 878 + /* Default functions for routable irq domain */ 879 + static int gic_routable_irq_domain_map(struct irq_domain *d, unsigned int irq, 880 + irq_hw_number_t hw) 881 + { 882 + return 0; 883 + } 884 + 885 + static void gic_routable_irq_domain_unmap(struct irq_domain *d, 886 + unsigned int irq) 887 + { 888 + } 889 + 890 + static int gic_routable_irq_domain_xlate(struct irq_domain *d, 891 + struct device_node *controller, 892 + const u32 *intspec, unsigned int intsize, 893 + unsigned long *out_hwirq, 894 + unsigned int *out_type) 895 + { 896 + *out_hwirq += 16; 897 + return 0; 898 + } 899 + 900 + const struct irq_domain_ops gic_default_routable_irq_domain_ops = { 901 + .map = gic_routable_irq_domain_map, 902 + .unmap = gic_routable_irq_domain_unmap, 903 + .xlate = gic_routable_irq_domain_xlate, 904 + }; 905 + 906 + const struct irq_domain_ops *gic_routable_irq_domain_ops = 907 + &gic_default_routable_irq_domain_ops; 894 908 895 909 void __init gic_init_bases(unsigned int gic_nr, int irq_start, 896 910 void __iomem *dist_base, void __iomem *cpu_base, ··· 931 881 irq_hw_number_t hwirq_base; 932 882 struct gic_chip_data *gic; 933 883 int gic_irqs, irq_base, i; 884 + int nr_routable_irqs; 934 885 935 886 BUG_ON(gic_nr >= MAX_GIC_NR); 936 887 ··· 997 946 gic->gic_irqs = gic_irqs; 998 947 999 948 gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */ 1000 - irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, numa_node_id()); 1001 - if (IS_ERR_VALUE(irq_base)) { 1002 - WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n", 1003 - irq_start); 1004 - irq_base = irq_start; 949 + 950 + if (of_property_read_u32(node, "arm,routable-irqs", 951 + &nr_routable_irqs)) { 952 + irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, 953 + numa_node_id()); 954 + if (IS_ERR_VALUE(irq_base)) { 955 + WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n", 956 + irq_start); 957 + irq_base = irq_start; 958 + } 959 + 960 + gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base, 961 + hwirq_base, &gic_irq_domain_ops, gic); 962 + } else { 963 + gic->domain = irq_domain_add_linear(node, nr_routable_irqs, 964 + &gic_irq_domain_ops, 965 + gic); 1005 966 } 1006 - gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base, 1007 - hwirq_base, &gic_irq_domain_ops, gic); 967 + 1008 968 if (WARN_ON(!gic->domain)) 1009 969 return; 1010 970
+6 -1
include/linux/irqchip/arm-gic.h
··· 93 93 void gic_migrate_target(unsigned int new_cpu_id); 94 94 unsigned long gic_get_sgir_physaddr(void); 95 95 96 + extern const struct irq_domain_ops *gic_routable_irq_domain_ops; 97 + static inline void __init register_routable_domain_ops 98 + (const struct irq_domain_ops *ops) 99 + { 100 + gic_routable_irq_domain_ops = ops; 101 + } 96 102 #endif /* __ASSEMBLY */ 97 - 98 103 #endif
+11
include/linux/irqchip/irq-crossbar.h
··· 1 + /* 2 + * drivers/irqchip/irq-crossbar.h 3 + * 4 + * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 as 8 + * published by the Free Software Foundation. 9 + * 10 + */ 11 + int irqcrossbar_init(void);