diff options
author | Liviu Dudau <Liviu.Dudau@arm.com> | 2015-01-20 11:52:59 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2015-01-26 05:38:23 -0500 |
commit | fb7e7deb7fc348ae131268d30e391c8184285de6 (patch) | |
tree | a036b13c9e0bcaeebbe3ce6691042d9f9840d9b6 | |
parent | d7eb4f2ecccd71f701bc8873bcf07c2d3b0375f6 (diff) |
irqchip: gic: Allow interrupt level to be set for PPIs
During a recent cleanup of the arm64 DTs it has become clear that
the handling of PPIs in xxxx_set_type() is incorrect. The ARM TRMs
for GICv2 and later allow for "implementation defined" support for
setting the edge or level type of the PPI interrupts and don't restrict
the activation level of the signal. Current ARM implementations
do restrict the PPI level type to IRQ_TYPE_LEVEL_LOW, but licensees
of the IP can decide to shoot themselves in the foot at any time.
Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
Acked-by: Marc Zyngier <Marc.Zyngier@arm.com>
Cc: LAKML <linux-arm-kernel@lists.infradead.org>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Ian Campbell <ijc+devicetree@hellion.org.uk>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Haojian Zhuang <haojian.zhuang@linaro.org>
Link: http://lkml.kernel.org/r/1421772779-25764-1-git-send-email-Liviu.Dudau@arm.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | Documentation/devicetree/bindings/arm/gic.txt | 8 | ||||
-rw-r--r-- | drivers/irqchip/irq-gic-common.c | 18 | ||||
-rw-r--r-- | drivers/irqchip/irq-gic-common.h | 2 | ||||
-rw-r--r-- | drivers/irqchip/irq-gic-v3.c | 8 | ||||
-rw-r--r-- | drivers/irqchip/irq-gic.c | 9 | ||||
-rw-r--r-- | drivers/irqchip/irq-hip04.c | 9 |
6 files changed, 35 insertions, 19 deletions
diff --git a/Documentation/devicetree/bindings/arm/gic.txt b/Documentation/devicetree/bindings/arm/gic.txt index 8112d0c3675a..c97484b73e72 100644 --- a/Documentation/devicetree/bindings/arm/gic.txt +++ b/Documentation/devicetree/bindings/arm/gic.txt | |||
@@ -32,12 +32,16 @@ Main node required properties: | |||
32 | The 3rd cell is the flags, encoded as follows: | 32 | The 3rd cell is the flags, encoded as follows: |
33 | bits[3:0] trigger type and level flags. | 33 | bits[3:0] trigger type and level flags. |
34 | 1 = low-to-high edge triggered | 34 | 1 = low-to-high edge triggered |
35 | 2 = high-to-low edge triggered | 35 | 2 = high-to-low edge triggered (invalid for SPIs) |
36 | 4 = active high level-sensitive | 36 | 4 = active high level-sensitive |
37 | 8 = active low level-sensitive | 37 | 8 = active low level-sensitive (invalid for SPIs). |
38 | bits[15:8] PPI interrupt cpu mask. Each bit corresponds to each of | 38 | bits[15:8] PPI interrupt cpu mask. Each bit corresponds to each of |
39 | the 8 possible cpus attached to the GIC. A bit set to '1' indicated | 39 | the 8 possible cpus attached to the GIC. A bit set to '1' indicated |
40 | the interrupt is wired to that CPU. Only valid for PPI interrupts. | 40 | the interrupt is wired to that CPU. Only valid for PPI interrupts. |
41 | Also note that the configurability of PPI interrupts is IMPLEMENTATION | ||
42 | DEFINED and as such not guaranteed to be present (most SoC available | ||
43 | in 2014 seem to ignore the setting of this flag and use the hardware | ||
44 | default value). | ||
41 | 45 | ||
42 | - reg : Specifies base physical address(s) and size of the GIC registers. The | 46 | - reg : Specifies base physical address(s) and size of the GIC registers. The |
43 | first region is the GIC distributor register base and size. The 2nd region is | 47 | first region is the GIC distributor register base and size. The 2nd region is |
diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c index 61541ff24397..ad96ebb0c7ab 100644 --- a/drivers/irqchip/irq-gic-common.c +++ b/drivers/irqchip/irq-gic-common.c | |||
@@ -21,7 +21,7 @@ | |||
21 | 21 | ||
22 | #include "irq-gic-common.h" | 22 | #include "irq-gic-common.h" |
23 | 23 | ||
24 | void gic_configure_irq(unsigned int irq, unsigned int type, | 24 | int gic_configure_irq(unsigned int irq, unsigned int type, |
25 | void __iomem *base, void (*sync_access)(void)) | 25 | void __iomem *base, void (*sync_access)(void)) |
26 | { | 26 | { |
27 | u32 enablemask = 1 << (irq % 32); | 27 | u32 enablemask = 1 << (irq % 32); |
@@ -29,16 +29,17 @@ void gic_configure_irq(unsigned int irq, unsigned int type, | |||
29 | u32 confmask = 0x2 << ((irq % 16) * 2); | 29 | u32 confmask = 0x2 << ((irq % 16) * 2); |
30 | u32 confoff = (irq / 16) * 4; | 30 | u32 confoff = (irq / 16) * 4; |
31 | bool enabled = false; | 31 | bool enabled = false; |
32 | u32 val; | 32 | u32 val, oldval; |
33 | int ret = 0; | ||
33 | 34 | ||
34 | /* | 35 | /* |
35 | * Read current configuration register, and insert the config | 36 | * Read current configuration register, and insert the config |
36 | * for "irq", depending on "type". | 37 | * for "irq", depending on "type". |
37 | */ | 38 | */ |
38 | val = readl_relaxed(base + GIC_DIST_CONFIG + confoff); | 39 | val = oldval = readl_relaxed(base + GIC_DIST_CONFIG + confoff); |
39 | if (type == IRQ_TYPE_LEVEL_HIGH) | 40 | if (type & IRQ_TYPE_LEVEL_MASK) |
40 | val &= ~confmask; | 41 | val &= ~confmask; |
41 | else if (type == IRQ_TYPE_EDGE_RISING) | 42 | else if (type & IRQ_TYPE_EDGE_BOTH) |
42 | val |= confmask; | 43 | val |= confmask; |
43 | 44 | ||
44 | /* | 45 | /* |
@@ -54,15 +55,20 @@ void gic_configure_irq(unsigned int irq, unsigned int type, | |||
54 | 55 | ||
55 | /* | 56 | /* |
56 | * Write back the new configuration, and possibly re-enable | 57 | * Write back the new configuration, and possibly re-enable |
57 | * the interrupt. | 58 | * the interrupt. If we tried to write a new configuration and failed, |
59 | * return an error. | ||
58 | */ | 60 | */ |
59 | writel_relaxed(val, base + GIC_DIST_CONFIG + confoff); | 61 | writel_relaxed(val, base + GIC_DIST_CONFIG + confoff); |
62 | if (readl_relaxed(base + GIC_DIST_CONFIG + confoff) != val && val != oldval) | ||
63 | ret = -EINVAL; | ||
60 | 64 | ||
61 | if (enabled) | 65 | if (enabled) |
62 | writel_relaxed(enablemask, base + GIC_DIST_ENABLE_SET + enableoff); | 66 | writel_relaxed(enablemask, base + GIC_DIST_ENABLE_SET + enableoff); |
63 | 67 | ||
64 | if (sync_access) | 68 | if (sync_access) |
65 | sync_access(); | 69 | sync_access(); |
70 | |||
71 | return ret; | ||
66 | } | 72 | } |
67 | 73 | ||
68 | void __init gic_dist_config(void __iomem *base, int gic_irqs, | 74 | void __init gic_dist_config(void __iomem *base, int gic_irqs, |
diff --git a/drivers/irqchip/irq-gic-common.h b/drivers/irqchip/irq-gic-common.h index b41f02481c3a..35a9884778bd 100644 --- a/drivers/irqchip/irq-gic-common.h +++ b/drivers/irqchip/irq-gic-common.h | |||
@@ -20,7 +20,7 @@ | |||
20 | #include <linux/of.h> | 20 | #include <linux/of.h> |
21 | #include <linux/irqdomain.h> | 21 | #include <linux/irqdomain.h> |
22 | 22 | ||
23 | void gic_configure_irq(unsigned int irq, unsigned int type, | 23 | int gic_configure_irq(unsigned int irq, unsigned int type, |
24 | void __iomem *base, void (*sync_access)(void)); | 24 | void __iomem *base, void (*sync_access)(void)); |
25 | void gic_dist_config(void __iomem *base, int gic_irqs, | 25 | void gic_dist_config(void __iomem *base, int gic_irqs, |
26 | void (*sync_access)(void)); | 26 | void (*sync_access)(void)); |
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 1a146ccee701..6e508038f31b 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c | |||
@@ -238,7 +238,9 @@ static int gic_set_type(struct irq_data *d, unsigned int type) | |||
238 | if (irq < 16) | 238 | if (irq < 16) |
239 | return -EINVAL; | 239 | return -EINVAL; |
240 | 240 | ||
241 | if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING) | 241 | /* SPIs have restrictions on the supported types */ |
242 | if (irq >= 32 && type != IRQ_TYPE_LEVEL_HIGH && | ||
243 | type != IRQ_TYPE_EDGE_RISING) | ||
242 | return -EINVAL; | 244 | return -EINVAL; |
243 | 245 | ||
244 | if (gic_irq_in_rdist(d)) { | 246 | if (gic_irq_in_rdist(d)) { |
@@ -249,9 +251,7 @@ static int gic_set_type(struct irq_data *d, unsigned int type) | |||
249 | rwp_wait = gic_dist_wait_for_rwp; | 251 | rwp_wait = gic_dist_wait_for_rwp; |
250 | } | 252 | } |
251 | 253 | ||
252 | gic_configure_irq(irq, type, base, rwp_wait); | 254 | return gic_configure_irq(irq, type, base, rwp_wait); |
253 | |||
254 | return 0; | ||
255 | } | 255 | } |
256 | 256 | ||
257 | static u64 gic_mpidr_to_affinity(u64 mpidr) | 257 | static u64 gic_mpidr_to_affinity(u64 mpidr) |
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index d617ee5a3d8a..4634cf7d0ec3 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c | |||
@@ -188,12 +188,15 @@ static int gic_set_type(struct irq_data *d, unsigned int type) | |||
188 | { | 188 | { |
189 | void __iomem *base = gic_dist_base(d); | 189 | void __iomem *base = gic_dist_base(d); |
190 | unsigned int gicirq = gic_irq(d); | 190 | unsigned int gicirq = gic_irq(d); |
191 | int ret; | ||
191 | 192 | ||
192 | /* Interrupt configuration for SGIs can't be changed */ | 193 | /* Interrupt configuration for SGIs can't be changed */ |
193 | if (gicirq < 16) | 194 | if (gicirq < 16) |
194 | return -EINVAL; | 195 | return -EINVAL; |
195 | 196 | ||
196 | if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING) | 197 | /* SPIs have restrictions on the supported types */ |
198 | if (gicirq >= 32 && type != IRQ_TYPE_LEVEL_HIGH && | ||
199 | type != IRQ_TYPE_EDGE_RISING) | ||
197 | return -EINVAL; | 200 | return -EINVAL; |
198 | 201 | ||
199 | raw_spin_lock(&irq_controller_lock); | 202 | raw_spin_lock(&irq_controller_lock); |
@@ -201,11 +204,11 @@ static int gic_set_type(struct irq_data *d, unsigned int type) | |||
201 | if (gic_arch_extn.irq_set_type) | 204 | if (gic_arch_extn.irq_set_type) |
202 | gic_arch_extn.irq_set_type(d, type); | 205 | gic_arch_extn.irq_set_type(d, type); |
203 | 206 | ||
204 | gic_configure_irq(gicirq, type, base, NULL); | 207 | ret = gic_configure_irq(gicirq, type, base, NULL); |
205 | 208 | ||
206 | raw_spin_unlock(&irq_controller_lock); | 209 | raw_spin_unlock(&irq_controller_lock); |
207 | 210 | ||
208 | return 0; | 211 | return ret; |
209 | } | 212 | } |
210 | 213 | ||
211 | static int gic_retrigger(struct irq_data *d) | 214 | static int gic_retrigger(struct irq_data *d) |
diff --git a/drivers/irqchip/irq-hip04.c b/drivers/irqchip/irq-hip04.c index 6bc2deb73d53..7d6ffb5de84f 100644 --- a/drivers/irqchip/irq-hip04.c +++ b/drivers/irqchip/irq-hip04.c | |||
@@ -120,21 +120,24 @@ static int hip04_irq_set_type(struct irq_data *d, unsigned int type) | |||
120 | { | 120 | { |
121 | void __iomem *base = hip04_dist_base(d); | 121 | void __iomem *base = hip04_dist_base(d); |
122 | unsigned int irq = hip04_irq(d); | 122 | unsigned int irq = hip04_irq(d); |
123 | int ret; | ||
123 | 124 | ||
124 | /* Interrupt configuration for SGIs can't be changed */ | 125 | /* Interrupt configuration for SGIs can't be changed */ |
125 | if (irq < 16) | 126 | if (irq < 16) |
126 | return -EINVAL; | 127 | return -EINVAL; |
127 | 128 | ||
128 | if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING) | 129 | /* SPIs have restrictions on the supported types */ |
130 | if (irq >= 32 && type != IRQ_TYPE_LEVEL_HIGH && | ||
131 | type != IRQ_TYPE_EDGE_RISING) | ||
129 | return -EINVAL; | 132 | return -EINVAL; |
130 | 133 | ||
131 | raw_spin_lock(&irq_controller_lock); | 134 | raw_spin_lock(&irq_controller_lock); |
132 | 135 | ||
133 | gic_configure_irq(irq, type, base, NULL); | 136 | ret = gic_configure_irq(irq, type, base, NULL); |
134 | 137 | ||
135 | raw_spin_unlock(&irq_controller_lock); | 138 | raw_spin_unlock(&irq_controller_lock); |
136 | 139 | ||
137 | return 0; | 140 | return ret; |
138 | } | 141 | } |
139 | 142 | ||
140 | #ifdef CONFIG_SMP | 143 | #ifdef CONFIG_SMP |