diff options
| -rw-r--r-- | Documentation/devicetree/bindings/arm/gic.txt | 6 | ||||
| -rw-r--r-- | drivers/irqchip/irq-gic.c | 82 | ||||
| -rw-r--r-- | include/linux/irqchip/arm-gic.h | 7 |
3 files changed, 84 insertions, 11 deletions
diff --git a/Documentation/devicetree/bindings/arm/gic.txt b/Documentation/devicetree/bindings/arm/gic.txt index bae0d87a38b2..5573c08d3180 100644 --- a/Documentation/devicetree/bindings/arm/gic.txt +++ b/Documentation/devicetree/bindings/arm/gic.txt | |||
| @@ -50,6 +50,11 @@ Optional | |||
| 50 | regions, used when the GIC doesn't have banked registers. The offset is | 50 | regions, used when the GIC doesn't have banked registers. The offset is |
| 51 | cpu-offset * cpu-nr. | 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 | Example: | 58 | Example: |
| 54 | 59 | ||
| 55 | intc: interrupt-controller@fff11000 { | 60 | intc: interrupt-controller@fff11000 { |
| @@ -57,6 +62,7 @@ Example: | |||
| 57 | #interrupt-cells = <3>; | 62 | #interrupt-cells = <3>; |
| 58 | #address-cells = <1>; | 63 | #address-cells = <1>; |
| 59 | interrupt-controller; | 64 | interrupt-controller; |
| 65 | arm,routable-irqs = <160>; | ||
| 60 | reg = <0xfff11000 0x1000>, | 66 | reg = <0xfff11000 0x1000>, |
| 61 | <0xfff10100 0x100>; | 67 | <0xfff10100 0x100>; |
| 62 | }; | 68 | }; |
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 | ||
diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h index 0ceb389dba6c..7ed92d0560d5 100644 --- a/include/linux/irqchip/arm-gic.h +++ b/include/linux/irqchip/arm-gic.h | |||
| @@ -93,6 +93,11 @@ int gic_get_cpu_id(unsigned int cpu); | |||
| 93 | void gic_migrate_target(unsigned int new_cpu_id); | 93 | void gic_migrate_target(unsigned int new_cpu_id); |
| 94 | unsigned long gic_get_sgir_physaddr(void); | 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 | #endif /* __ASSEMBLY */ | 102 | #endif /* __ASSEMBLY */ |
| 97 | |||
| 98 | #endif | 103 | #endif |
