summaryrefslogtreecommitdiffstats
path: root/drivers/irqchip
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2015-04-11 05:17:28 -0400
committerThomas Gleixner <tglx@linutronix.de>2015-04-11 05:17:28 -0400
commitb7dccbea6b079be01e07921264709f249009b8e8 (patch)
tree25997ac6fb2741fb310cf03ebbdb79014fbbd238 /drivers/irqchip
parent425b655ce479843abae07bf4dc7c496ca9538a5a (diff)
parenta01e7b3258bea93fbf1f028fab1c739d80c61823 (diff)
Merge tag 'irqchip-core-4.1-3' of git://git.infradead.org/users/jcooper/linux into irq/core
irqchip core change for v4.1 (round 3) from Jason Cooper Purge the gic_arch_extn hacks and abuse by using the new stacked domains NOTE: Due to the nature of these changes, patches crossing subsystems have been kept together in their own branches. - tegra - Handle the LIC properly - omap - Convert crossbar to stacked domains - kill arm,routable-irqs in GIC binding - exynos - Convert PMU wakeup to stacked domains - shmobile, ux500, zynq (irq_set_wake branch) - Switch from abusing gic_arch_extn to using gic_set_irqchip_flags
Diffstat (limited to 'drivers/irqchip')
-rw-r--r--drivers/irqchip/Makefile1
-rw-r--r--drivers/irqchip/irq-crossbar.c210
-rw-r--r--drivers/irqchip/irq-gic.c58
-rw-r--r--drivers/irqchip/irq-tegra.c377
4 files changed, 508 insertions, 138 deletions
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index f117092ae014..552a74027601 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_ARCH_HIP04) += irq-hip04.o
6obj-$(CONFIG_ARCH_MMP) += irq-mmp.o 6obj-$(CONFIG_ARCH_MMP) += irq-mmp.o
7obj-$(CONFIG_ARCH_MVEBU) += irq-armada-370-xp.o 7obj-$(CONFIG_ARCH_MVEBU) += irq-armada-370-xp.o
8obj-$(CONFIG_ARCH_MXS) += irq-mxs.o 8obj-$(CONFIG_ARCH_MXS) += irq-mxs.o
9obj-$(CONFIG_ARCH_TEGRA) += irq-tegra.o
9obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o 10obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o
10obj-$(CONFIG_DW_APB_ICTL) += irq-dw-apb-ictl.o 11obj-$(CONFIG_DW_APB_ICTL) += irq-dw-apb-ictl.o
11obj-$(CONFIG_METAG) += irq-metag-ext.o 12obj-$(CONFIG_METAG) += irq-metag-ext.o
diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c
index bbbaf5de65d2..692fe2bc8197 100644
--- a/drivers/irqchip/irq-crossbar.c
+++ b/drivers/irqchip/irq-crossbar.c
@@ -11,11 +11,12 @@
11 */ 11 */
12#include <linux/err.h> 12#include <linux/err.h>
13#include <linux/io.h> 13#include <linux/io.h>
14#include <linux/irqdomain.h>
14#include <linux/of_address.h> 15#include <linux/of_address.h>
15#include <linux/of_irq.h> 16#include <linux/of_irq.h>
16#include <linux/slab.h> 17#include <linux/slab.h>
17#include <linux/irqchip/arm-gic.h> 18
18#include <linux/irqchip/irq-crossbar.h> 19#include "irqchip.h"
19 20
20#define IRQ_FREE -1 21#define IRQ_FREE -1
21#define IRQ_RESERVED -2 22#define IRQ_RESERVED -2
@@ -24,6 +25,7 @@
24 25
25/** 26/**
26 * struct crossbar_device - crossbar device description 27 * struct crossbar_device - crossbar device description
28 * @lock: spinlock serializing access to @irq_map
27 * @int_max: maximum number of supported interrupts 29 * @int_max: maximum number of supported interrupts
28 * @safe_map: safe default value to initialize the crossbar 30 * @safe_map: safe default value to initialize the crossbar
29 * @max_crossbar_sources: Maximum number of crossbar sources 31 * @max_crossbar_sources: Maximum number of crossbar sources
@@ -33,6 +35,7 @@
33 * @write: register write function pointer 35 * @write: register write function pointer
34 */ 36 */
35struct crossbar_device { 37struct crossbar_device {
38 raw_spinlock_t lock;
36 uint int_max; 39 uint int_max;
37 uint safe_map; 40 uint safe_map;
38 uint max_crossbar_sources; 41 uint max_crossbar_sources;
@@ -44,72 +47,101 @@ struct crossbar_device {
44 47
45static struct crossbar_device *cb; 48static struct crossbar_device *cb;
46 49
47static inline void crossbar_writel(int irq_no, int cb_no) 50static void crossbar_writel(int irq_no, int cb_no)
48{ 51{
49 writel(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); 52 writel(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
50} 53}
51 54
52static inline void crossbar_writew(int irq_no, int cb_no) 55static void crossbar_writew(int irq_no, int cb_no)
53{ 56{
54 writew(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); 57 writew(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
55} 58}
56 59
57static inline void crossbar_writeb(int irq_no, int cb_no) 60static void crossbar_writeb(int irq_no, int cb_no)
58{ 61{
59 writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); 62 writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]);
60} 63}
61 64
62static inline int get_prev_map_irq(int cb_no) 65static struct irq_chip crossbar_chip = {
63{ 66 .name = "CBAR",
64 int i; 67 .irq_eoi = irq_chip_eoi_parent,
65 68 .irq_mask = irq_chip_mask_parent,
66 for (i = cb->int_max - 1; i >= 0; i--) 69 .irq_unmask = irq_chip_unmask_parent,
67 if (cb->irq_map[i] == cb_no) 70 .irq_retrigger = irq_chip_retrigger_hierarchy,
68 return i; 71 .irq_set_wake = irq_chip_set_wake_parent,
69 72#ifdef CONFIG_SMP
70 return -ENODEV; 73 .irq_set_affinity = irq_chip_set_affinity_parent,
71} 74#endif
75};
72 76
73static inline int allocate_free_irq(int cb_no) 77static int allocate_gic_irq(struct irq_domain *domain, unsigned virq,
78 irq_hw_number_t hwirq)
74{ 79{
80 struct of_phandle_args args;
75 int i; 81 int i;
82 int err;
76 83
84 raw_spin_lock(&cb->lock);
77 for (i = cb->int_max - 1; i >= 0; i--) { 85 for (i = cb->int_max - 1; i >= 0; i--) {
78 if (cb->irq_map[i] == IRQ_FREE) { 86 if (cb->irq_map[i] == IRQ_FREE) {
79 cb->irq_map[i] = cb_no; 87 cb->irq_map[i] = hwirq;
80 return i; 88 break;
81 } 89 }
82 } 90 }
91 raw_spin_unlock(&cb->lock);
83 92
84 return -ENODEV; 93 if (i < 0)
85} 94 return -ENODEV;
86 95
87static inline bool needs_crossbar_write(irq_hw_number_t hw) 96 args.np = domain->parent->of_node;
88{ 97 args.args_count = 3;
89 int cb_no; 98 args.args[0] = 0; /* SPI */
99 args.args[1] = i;
100 args.args[2] = IRQ_TYPE_LEVEL_HIGH;
90 101
91 if (hw > GIC_IRQ_START) { 102 err = irq_domain_alloc_irqs_parent(domain, virq, 1, &args);
92 cb_no = cb->irq_map[hw - GIC_IRQ_START]; 103 if (err)
93 if (cb_no != IRQ_RESERVED && cb_no != IRQ_SKIP) 104 cb->irq_map[i] = IRQ_FREE;
94 return true; 105 else
95 } 106 cb->write(i, hwirq);
96 107
97 return false; 108 return err;
98} 109}
99 110
100static int crossbar_domain_map(struct irq_domain *d, unsigned int irq, 111static int crossbar_domain_alloc(struct irq_domain *d, unsigned int virq,
101 irq_hw_number_t hw) 112 unsigned int nr_irqs, void *data)
102{ 113{
103 if (needs_crossbar_write(hw)) 114 struct of_phandle_args *args = data;
104 cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]); 115 irq_hw_number_t hwirq;
116 int i;
117
118 if (args->args_count != 3)
119 return -EINVAL; /* Not GIC compliant */
120 if (args->args[0] != 0)
121 return -EINVAL; /* No PPI should point to this domain */
122
123 hwirq = args->args[1];
124 if ((hwirq + nr_irqs) > cb->max_crossbar_sources)
125 return -EINVAL; /* Can't deal with this */
126
127 for (i = 0; i < nr_irqs; i++) {
128 int err = allocate_gic_irq(d, virq + i, hwirq + i);
129
130 if (err)
131 return err;
132
133 irq_domain_set_hwirq_and_chip(d, virq + i, hwirq + i,
134 &crossbar_chip, NULL);
135 }
105 136
106 return 0; 137 return 0;
107} 138}
108 139
109/** 140/**
110 * crossbar_domain_unmap - unmap a crossbar<->irq connection 141 * crossbar_domain_free - unmap/free a crossbar<->irq connection
111 * @d: domain of irq to unmap 142 * @domain: domain of irq to unmap
112 * @irq: virq number 143 * @virq: virq number
144 * @nr_irqs: number of irqs to free
113 * 145 *
114 * We do not maintain a use count of total number of map/unmap 146 * We do not maintain a use count of total number of map/unmap
115 * calls for a particular irq to find out if a irq can be really 147 * calls for a particular irq to find out if a irq can be really
@@ -117,14 +149,20 @@ static int crossbar_domain_map(struct irq_domain *d, unsigned int irq,
117 * after which irq is anyways unusable. So an explicit map has to be called 149 * after which irq is anyways unusable. So an explicit map has to be called
118 * after that. 150 * after that.
119 */ 151 */
120static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq) 152static void crossbar_domain_free(struct irq_domain *domain, unsigned int virq,
153 unsigned int nr_irqs)
121{ 154{
122 irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq; 155 int i;
123 156
124 if (needs_crossbar_write(hw)) { 157 raw_spin_lock(&cb->lock);
125 cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE; 158 for (i = 0; i < nr_irqs; i++) {
126 cb->write(hw - GIC_IRQ_START, cb->safe_map); 159 struct irq_data *d = irq_domain_get_irq_data(domain, virq + i);
160
161 irq_domain_reset_irq_data(d);
162 cb->irq_map[d->hwirq] = IRQ_FREE;
163 cb->write(d->hwirq, cb->safe_map);
127 } 164 }
165 raw_spin_unlock(&cb->lock);
128} 166}
129 167
130static int crossbar_domain_xlate(struct irq_domain *d, 168static int crossbar_domain_xlate(struct irq_domain *d,
@@ -133,44 +171,22 @@ static int crossbar_domain_xlate(struct irq_domain *d,
133 unsigned long *out_hwirq, 171 unsigned long *out_hwirq,
134 unsigned int *out_type) 172 unsigned int *out_type)
135{ 173{
136 int ret; 174 if (d->of_node != controller)
137 int req_num = intspec[1]; 175 return -EINVAL; /* Shouldn't happen, really... */
138 int direct_map_num; 176 if (intsize != 3)
139 177 return -EINVAL; /* Not GIC compliant */
140 if (req_num >= cb->max_crossbar_sources) { 178 if (intspec[0] != 0)
141 direct_map_num = req_num - cb->max_crossbar_sources; 179 return -EINVAL; /* No PPI should point to this domain */
142 if (direct_map_num < cb->int_max) { 180
143 ret = cb->irq_map[direct_map_num]; 181 *out_hwirq = intspec[1];
144 if (ret == IRQ_RESERVED || ret == IRQ_SKIP) { 182 *out_type = intspec[2];
145 /* We use the interrupt num as h/w irq num */
146 ret = direct_map_num;
147 goto found;
148 }
149 }
150
151 pr_err("%s: requested crossbar number %d > max %d\n",
152 __func__, req_num, cb->max_crossbar_sources);
153 return -EINVAL;
154 }
155
156 ret = get_prev_map_irq(req_num);
157 if (ret >= 0)
158 goto found;
159
160 ret = allocate_free_irq(req_num);
161
162 if (ret < 0)
163 return ret;
164
165found:
166 *out_hwirq = ret + GIC_IRQ_START;
167 return 0; 183 return 0;
168} 184}
169 185
170static const struct irq_domain_ops routable_irq_domain_ops = { 186static const struct irq_domain_ops crossbar_domain_ops = {
171 .map = crossbar_domain_map, 187 .alloc = crossbar_domain_alloc,
172 .unmap = crossbar_domain_unmap, 188 .free = crossbar_domain_free,
173 .xlate = crossbar_domain_xlate 189 .xlate = crossbar_domain_xlate,
174}; 190};
175 191
176static int __init crossbar_of_init(struct device_node *node) 192static int __init crossbar_of_init(struct device_node *node)
@@ -293,7 +309,8 @@ static int __init crossbar_of_init(struct device_node *node)
293 cb->write(i, cb->safe_map); 309 cb->write(i, cb->safe_map);
294 } 310 }
295 311
296 register_routable_domain_ops(&routable_irq_domain_ops); 312 raw_spin_lock_init(&cb->lock);
313
297 return 0; 314 return 0;
298 315
299err_reg_offset: 316err_reg_offset:
@@ -309,18 +326,37 @@ err_cb:
309 return ret; 326 return ret;
310} 327}
311 328
312static const struct of_device_id crossbar_match[] __initconst = { 329static int __init irqcrossbar_init(struct device_node *node,
313 { .compatible = "ti,irq-crossbar" }, 330 struct device_node *parent)
314 {}
315};
316
317int __init irqcrossbar_init(void)
318{ 331{
319 struct device_node *np; 332 struct irq_domain *parent_domain, *domain;
320 np = of_find_matching_node(NULL, crossbar_match); 333 int err;
321 if (!np) 334
335 if (!parent) {
336 pr_err("%s: no parent, giving up\n", node->full_name);
322 return -ENODEV; 337 return -ENODEV;
338 }
339
340 parent_domain = irq_find_host(parent);
341 if (!parent_domain) {
342 pr_err("%s: unable to obtain parent domain\n", node->full_name);
343 return -ENXIO;
344 }
345
346 err = crossbar_of_init(node);
347 if (err)
348 return err;
349
350 domain = irq_domain_add_hierarchy(parent_domain, 0,
351 cb->max_crossbar_sources,
352 node, &crossbar_domain_ops,
353 NULL);
354 if (!domain) {
355 pr_err("%s: failed to allocated domain\n", node->full_name);
356 return -ENOMEM;
357 }
323 358
324 crossbar_of_init(np);
325 return 0; 359 return 0;
326} 360}
361
362IRQCHIP_DECLARE(ti_irqcrossbar, "ti,irq-crossbar", irqcrossbar_init);
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index d6d6b74801d4..a6ce3476834e 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -863,15 +863,12 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
863 irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data, 863 irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data,
864 handle_fasteoi_irq, NULL, NULL); 864 handle_fasteoi_irq, NULL, NULL);
865 set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); 865 set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
866
867 gic_routable_irq_domain_ops->map(d, irq, hw);
868 } 866 }
869 return 0; 867 return 0;
870} 868}
871 869
872static void gic_irq_domain_unmap(struct irq_domain *d, unsigned int irq) 870static void gic_irq_domain_unmap(struct irq_domain *d, unsigned int irq)
873{ 871{
874 gic_routable_irq_domain_ops->unmap(d, irq);
875} 872}
876 873
877static int gic_irq_domain_xlate(struct irq_domain *d, 874static int gic_irq_domain_xlate(struct irq_domain *d,
@@ -890,16 +887,8 @@ static int gic_irq_domain_xlate(struct irq_domain *d,
890 *out_hwirq = intspec[1] + 16; 887 *out_hwirq = intspec[1] + 16;
891 888
892 /* For SPIs, we need to add 16 more to get the GIC irq ID number */ 889 /* For SPIs, we need to add 16 more to get the GIC irq ID number */
893 if (!intspec[0]) { 890 if (!intspec[0])
894 ret = gic_routable_irq_domain_ops->xlate(d, controller, 891 *out_hwirq += 16;
895 intspec,
896 intsize,
897 out_hwirq,
898 out_type);
899
900 if (IS_ERR_VALUE(ret))
901 return ret;
902 }
903 892
904 *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; 893 *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
905 894
@@ -956,37 +945,11 @@ static const struct irq_domain_ops gic_irq_domain_ops = {
956 .xlate = gic_irq_domain_xlate, 945 .xlate = gic_irq_domain_xlate,
957}; 946};
958 947
959/* Default functions for routable irq domain */ 948void gic_set_irqchip_flags(unsigned long flags)
960static int gic_routable_irq_domain_map(struct irq_domain *d, unsigned int irq,
961 irq_hw_number_t hw)
962{
963 return 0;
964}
965
966static void gic_routable_irq_domain_unmap(struct irq_domain *d,
967 unsigned int irq)
968{
969}
970
971static int gic_routable_irq_domain_xlate(struct irq_domain *d,
972 struct device_node *controller,
973 const u32 *intspec, unsigned int intsize,
974 unsigned long *out_hwirq,
975 unsigned int *out_type)
976{ 949{
977 *out_hwirq += 16; 950 gic_chip.flags |= flags;
978 return 0;
979} 951}
980 952
981static const struct irq_domain_ops gic_default_routable_irq_domain_ops = {
982 .map = gic_routable_irq_domain_map,
983 .unmap = gic_routable_irq_domain_unmap,
984 .xlate = gic_routable_irq_domain_xlate,
985};
986
987const struct irq_domain_ops *gic_routable_irq_domain_ops =
988 &gic_default_routable_irq_domain_ops;
989
990void __init gic_init_bases(unsigned int gic_nr, int irq_start, 953void __init gic_init_bases(unsigned int gic_nr, int irq_start,
991 void __iomem *dist_base, void __iomem *cpu_base, 954 void __iomem *dist_base, void __iomem *cpu_base,
992 u32 percpu_offset, struct device_node *node) 955 u32 percpu_offset, struct device_node *node)
@@ -994,7 +957,6 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
994 irq_hw_number_t hwirq_base; 957 irq_hw_number_t hwirq_base;
995 struct gic_chip_data *gic; 958 struct gic_chip_data *gic;
996 int gic_irqs, irq_base, i; 959 int gic_irqs, irq_base, i;
997 int nr_routable_irqs;
998 960
999 BUG_ON(gic_nr >= MAX_GIC_NR); 961 BUG_ON(gic_nr >= MAX_GIC_NR);
1000 962
@@ -1050,15 +1012,9 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start,
1050 gic->gic_irqs = gic_irqs; 1012 gic->gic_irqs = gic_irqs;
1051 1013
1052 if (node) { /* DT case */ 1014 if (node) { /* DT case */
1053 const struct irq_domain_ops *ops = &gic_irq_domain_hierarchy_ops; 1015 gic->domain = irq_domain_add_linear(node, gic_irqs,
1054 1016 &gic_irq_domain_hierarchy_ops,
1055 if (!of_property_read_u32(node, "arm,routable-irqs", 1017 gic);
1056 &nr_routable_irqs)) {
1057 ops = &gic_irq_domain_ops;
1058 gic_irqs = nr_routable_irqs;
1059 }
1060
1061 gic->domain = irq_domain_add_linear(node, gic_irqs, ops, gic);
1062 } else { /* Non-DT case */ 1018 } else { /* Non-DT case */
1063 /* 1019 /*
1064 * For primary GICs, skip over SGIs. 1020 * For primary GICs, skip over SGIs.
diff --git a/drivers/irqchip/irq-tegra.c b/drivers/irqchip/irq-tegra.c
new file mode 100644
index 000000000000..51c485d9a877
--- /dev/null
+++ b/drivers/irqchip/irq-tegra.c
@@ -0,0 +1,377 @@
1/*
2 * Driver code for Tegra's Legacy Interrupt Controller
3 *
4 * Author: Marc Zyngier <marc.zyngier@arm.com>
5 *
6 * Heavily based on the original arch/arm/mach-tegra/irq.c code:
7 * Copyright (C) 2011 Google, Inc.
8 *
9 * Author:
10 * Colin Cross <ccross@android.com>
11 *
12 * Copyright (C) 2010,2013, NVIDIA Corporation
13 *
14 * This software is licensed under the terms of the GNU General Public
15 * License version 2, as published by the Free Software Foundation, and
16 * may be copied, distributed, and modified under those terms.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 */
24
25#include <linux/io.h>
26#include <linux/irq.h>
27#include <linux/irqdomain.h>
28#include <linux/of_address.h>
29#include <linux/slab.h>
30#include <linux/syscore_ops.h>
31
32#include <dt-bindings/interrupt-controller/arm-gic.h>
33
34#include "irqchip.h"
35
36#define ICTLR_CPU_IEP_VFIQ 0x08
37#define ICTLR_CPU_IEP_FIR 0x14
38#define ICTLR_CPU_IEP_FIR_SET 0x18
39#define ICTLR_CPU_IEP_FIR_CLR 0x1c
40
41#define ICTLR_CPU_IER 0x20
42#define ICTLR_CPU_IER_SET 0x24
43#define ICTLR_CPU_IER_CLR 0x28
44#define ICTLR_CPU_IEP_CLASS 0x2C
45
46#define ICTLR_COP_IER 0x30
47#define ICTLR_COP_IER_SET 0x34
48#define ICTLR_COP_IER_CLR 0x38
49#define ICTLR_COP_IEP_CLASS 0x3c
50
51#define TEGRA_MAX_NUM_ICTLRS 6
52
53static unsigned int num_ictlrs;
54
55struct tegra_ictlr_soc {
56 unsigned int num_ictlrs;
57};
58
59static const struct tegra_ictlr_soc tegra20_ictlr_soc = {
60 .num_ictlrs = 4,
61};
62
63static const struct tegra_ictlr_soc tegra30_ictlr_soc = {
64 .num_ictlrs = 5,
65};
66
67static const struct tegra_ictlr_soc tegra210_ictlr_soc = {
68 .num_ictlrs = 6,
69};
70
71static const struct of_device_id ictlr_matches[] = {
72 { .compatible = "nvidia,tegra210-ictlr", .data = &tegra210_ictlr_soc },
73 { .compatible = "nvidia,tegra30-ictlr", .data = &tegra30_ictlr_soc },
74 { .compatible = "nvidia,tegra20-ictlr", .data = &tegra20_ictlr_soc },
75 { }
76};
77
78struct tegra_ictlr_info {
79 void __iomem *base[TEGRA_MAX_NUM_ICTLRS];
80#ifdef CONFIG_PM_SLEEP
81 u32 cop_ier[TEGRA_MAX_NUM_ICTLRS];
82 u32 cop_iep[TEGRA_MAX_NUM_ICTLRS];
83 u32 cpu_ier[TEGRA_MAX_NUM_ICTLRS];
84 u32 cpu_iep[TEGRA_MAX_NUM_ICTLRS];
85
86 u32 ictlr_wake_mask[TEGRA_MAX_NUM_ICTLRS];
87#endif
88};
89
90static struct tegra_ictlr_info *lic;
91
92static inline void tegra_ictlr_write_mask(struct irq_data *d, unsigned long reg)
93{
94 void __iomem *base = d->chip_data;
95 u32 mask;
96
97 mask = BIT(d->hwirq % 32);
98 writel_relaxed(mask, base + reg);
99}
100
101static void tegra_mask(struct irq_data *d)
102{
103 tegra_ictlr_write_mask(d, ICTLR_CPU_IER_CLR);
104 irq_chip_mask_parent(d);
105}
106
107static void tegra_unmask(struct irq_data *d)
108{
109 tegra_ictlr_write_mask(d, ICTLR_CPU_IER_SET);
110 irq_chip_unmask_parent(d);
111}
112
113static void tegra_eoi(struct irq_data *d)
114{
115 tegra_ictlr_write_mask(d, ICTLR_CPU_IEP_FIR_CLR);
116 irq_chip_eoi_parent(d);
117}
118
119static int tegra_retrigger(struct irq_data *d)
120{
121 tegra_ictlr_write_mask(d, ICTLR_CPU_IEP_FIR_SET);
122 return irq_chip_retrigger_hierarchy(d);
123}
124
125#ifdef CONFIG_PM_SLEEP
126static int tegra_set_wake(struct irq_data *d, unsigned int enable)
127{
128 u32 irq = d->hwirq;
129 u32 index, mask;
130
131 index = (irq / 32);
132 mask = BIT(irq % 32);
133 if (enable)
134 lic->ictlr_wake_mask[index] |= mask;
135 else
136 lic->ictlr_wake_mask[index] &= ~mask;
137
138 /*
139 * Do *not* call into the parent, as the GIC doesn't have any
140 * wake-up facility...
141 */
142 return 0;
143}
144
145static int tegra_ictlr_suspend(void)
146{
147 unsigned long flags;
148 unsigned int i;
149
150 local_irq_save(flags);
151 for (i = 0; i < num_ictlrs; i++) {
152 void __iomem *ictlr = lic->base[i];
153
154 /* Save interrupt state */
155 lic->cpu_ier[i] = readl_relaxed(ictlr + ICTLR_CPU_IER);
156 lic->cpu_iep[i] = readl_relaxed(ictlr + ICTLR_CPU_IEP_CLASS);
157 lic->cop_ier[i] = readl_relaxed(ictlr + ICTLR_COP_IER);
158 lic->cop_iep[i] = readl_relaxed(ictlr + ICTLR_COP_IEP_CLASS);
159
160 /* Disable COP interrupts */
161 writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR);
162
163 /* Disable CPU interrupts */
164 writel_relaxed(~0ul, ictlr + ICTLR_CPU_IER_CLR);
165
166 /* Enable the wakeup sources of ictlr */
167 writel_relaxed(lic->ictlr_wake_mask[i], ictlr + ICTLR_CPU_IER_SET);
168 }
169 local_irq_restore(flags);
170
171 return 0;
172}
173
174static void tegra_ictlr_resume(void)
175{
176 unsigned long flags;
177 unsigned int i;
178
179 local_irq_save(flags);
180 for (i = 0; i < num_ictlrs; i++) {
181 void __iomem *ictlr = lic->base[i];
182
183 writel_relaxed(lic->cpu_iep[i],
184 ictlr + ICTLR_CPU_IEP_CLASS);
185 writel_relaxed(~0ul, ictlr + ICTLR_CPU_IER_CLR);
186 writel_relaxed(lic->cpu_ier[i],
187 ictlr + ICTLR_CPU_IER_SET);
188 writel_relaxed(lic->cop_iep[i],
189 ictlr + ICTLR_COP_IEP_CLASS);
190 writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR);
191 writel_relaxed(lic->cop_ier[i],
192 ictlr + ICTLR_COP_IER_SET);
193 }
194 local_irq_restore(flags);
195}
196
197static struct syscore_ops tegra_ictlr_syscore_ops = {
198 .suspend = tegra_ictlr_suspend,
199 .resume = tegra_ictlr_resume,
200};
201
202static void tegra_ictlr_syscore_init(void)
203{
204 register_syscore_ops(&tegra_ictlr_syscore_ops);
205}
206#else
207#define tegra_set_wake NULL
208static inline void tegra_ictlr_syscore_init(void) {}
209#endif
210
211static struct irq_chip tegra_ictlr_chip = {
212 .name = "LIC",
213 .irq_eoi = tegra_eoi,
214 .irq_mask = tegra_mask,
215 .irq_unmask = tegra_unmask,
216 .irq_retrigger = tegra_retrigger,
217 .irq_set_wake = tegra_set_wake,
218 .flags = IRQCHIP_MASK_ON_SUSPEND,
219#ifdef CONFIG_SMP
220 .irq_set_affinity = irq_chip_set_affinity_parent,
221#endif
222};
223
224static int tegra_ictlr_domain_xlate(struct irq_domain *domain,
225 struct device_node *controller,
226 const u32 *intspec,
227 unsigned int intsize,
228 unsigned long *out_hwirq,
229 unsigned int *out_type)
230{
231 if (domain->of_node != controller)
232 return -EINVAL; /* Shouldn't happen, really... */
233 if (intsize != 3)
234 return -EINVAL; /* Not GIC compliant */
235 if (intspec[0] != GIC_SPI)
236 return -EINVAL; /* No PPI should point to this domain */
237
238 *out_hwirq = intspec[1];
239 *out_type = intspec[2];
240 return 0;
241}
242
243static int tegra_ictlr_domain_alloc(struct irq_domain *domain,
244 unsigned int virq,
245 unsigned int nr_irqs, void *data)
246{
247 struct of_phandle_args *args = data;
248 struct of_phandle_args parent_args;
249 struct tegra_ictlr_info *info = domain->host_data;
250 irq_hw_number_t hwirq;
251 unsigned int i;
252
253 if (args->args_count != 3)
254 return -EINVAL; /* Not GIC compliant */
255 if (args->args[0] != GIC_SPI)
256 return -EINVAL; /* No PPI should point to this domain */
257
258 hwirq = args->args[1];
259 if (hwirq >= (num_ictlrs * 32))
260 return -EINVAL;
261
262 for (i = 0; i < nr_irqs; i++) {
263 int ictlr = (hwirq + i) / 32;
264
265 irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
266 &tegra_ictlr_chip,
267 &info->base[ictlr]);
268 }
269
270 parent_args = *args;
271 parent_args.np = domain->parent->of_node;
272 return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent_args);
273}
274
275static void tegra_ictlr_domain_free(struct irq_domain *domain,
276 unsigned int virq,
277 unsigned int nr_irqs)
278{
279 unsigned int i;
280
281 for (i = 0; i < nr_irqs; i++) {
282 struct irq_data *d = irq_domain_get_irq_data(domain, virq + i);
283 irq_domain_reset_irq_data(d);
284 }
285}
286
287static const struct irq_domain_ops tegra_ictlr_domain_ops = {
288 .xlate = tegra_ictlr_domain_xlate,
289 .alloc = tegra_ictlr_domain_alloc,
290 .free = tegra_ictlr_domain_free,
291};
292
293static int __init tegra_ictlr_init(struct device_node *node,
294 struct device_node *parent)
295{
296 struct irq_domain *parent_domain, *domain;
297 const struct of_device_id *match;
298 const struct tegra_ictlr_soc *soc;
299 unsigned int i;
300 int err;
301
302 if (!parent) {
303 pr_err("%s: no parent, giving up\n", node->full_name);
304 return -ENODEV;
305 }
306
307 parent_domain = irq_find_host(parent);
308 if (!parent_domain) {
309 pr_err("%s: unable to obtain parent domain\n", node->full_name);
310 return -ENXIO;
311 }
312
313 match = of_match_node(ictlr_matches, node);
314 if (!match) /* Should never happen... */
315 return -ENODEV;
316
317 soc = match->data;
318
319 lic = kzalloc(sizeof(*lic), GFP_KERNEL);
320 if (!lic)
321 return -ENOMEM;
322
323 for (i = 0; i < TEGRA_MAX_NUM_ICTLRS; i++) {
324 void __iomem *base;
325
326 base = of_iomap(node, i);
327 if (!base)
328 break;
329
330 lic->base[i] = base;
331
332 /* Disable all interrupts */
333 writel_relaxed(~0UL, base + ICTLR_CPU_IER_CLR);
334 /* All interrupts target IRQ */
335 writel_relaxed(0, base + ICTLR_CPU_IEP_CLASS);
336
337 num_ictlrs++;
338 }
339
340 if (!num_ictlrs) {
341 pr_err("%s: no valid regions, giving up\n", node->full_name);
342 err = -ENOMEM;
343 goto out_free;
344 }
345
346 WARN(num_ictlrs != soc->num_ictlrs,
347 "%s: Found %u interrupt controllers in DT; expected %u.\n",
348 node->full_name, num_ictlrs, soc->num_ictlrs);
349
350
351 domain = irq_domain_add_hierarchy(parent_domain, 0, num_ictlrs * 32,
352 node, &tegra_ictlr_domain_ops,
353 lic);
354 if (!domain) {
355 pr_err("%s: failed to allocated domain\n", node->full_name);
356 err = -ENOMEM;
357 goto out_unmap;
358 }
359
360 tegra_ictlr_syscore_init();
361
362 pr_info("%s: %d interrupts forwarded to %s\n",
363 node->full_name, num_ictlrs * 32, parent->full_name);
364
365 return 0;
366
367out_unmap:
368 for (i = 0; i < num_ictlrs; i++)
369 iounmap(lic->base[i]);
370out_free:
371 kfree(lic);
372 return err;
373}
374
375IRQCHIP_DECLARE(tegra20_ictlr, "nvidia,tegra20-ictlr", tegra_ictlr_init);
376IRQCHIP_DECLARE(tegra30_ictlr, "nvidia,tegra30-ictlr", tegra_ictlr_init);
377IRQCHIP_DECLARE(tegra210_ictlr, "nvidia,tegra210-ictlr", tegra_ictlr_init);