diff options
author | Javier Martinez Canillas <javier.martinez@collabora.co.uk> | 2015-06-12 01:43:15 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2015-06-16 05:34:41 -0400 |
commit | 6fd4899a54a522ccd6a24fea2318d3b515b95945 (patch) | |
tree | b42d7ddeaee568223b0b1f59f203db9b43474c62 | |
parent | 55963c9f20d03124eefb4c365e1ca1f485fc3974 (diff) |
irqchip: exynos-combiner: Save IRQ enable set on suspend
The Exynos interrupt combiner IP loses its state when the SoC enters
into a low power state during a Suspend-to-RAM. This means that if a
IRQ is used as a source, the interrupts for the devices are disabled
when the system is resumed from a sleep state so are not triggered.
Save the interrupt enable set register for each combiner group and
restore it after resume to make sure that the interrupts are enabled.
Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
Reviewed-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Kukjin Kim <kgene@kernel.org>
Cc: Tomasz Figa <tomasz.figa@gmail.com>
Cc: Doug Anderson <dianders@chromium.org>
Cc: linux-arm-kernel@lists.infradead.org
Cc: Peter Chubb <peter.chubb@nicta.com.au>
Cc: Shuah Khan <shuahkhan@gmail.com>
Cc: Chanho Park <parkch98@gmail.com>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Link: http://lkml.kernel.org/r/1434087795-13990-1-git-send-email-javier.martinez@collabora.co.uk
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | drivers/irqchip/exynos-combiner.c | 64 |
1 files changed, 59 insertions, 5 deletions
diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c index a57a3a1f339f..5c82e3bdafdf 100644 --- a/drivers/irqchip/exynos-combiner.c +++ b/drivers/irqchip/exynos-combiner.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <linux/init.h> | 13 | #include <linux/init.h> |
14 | #include <linux/io.h> | 14 | #include <linux/io.h> |
15 | #include <linux/slab.h> | 15 | #include <linux/slab.h> |
16 | #include <linux/syscore_ops.h> | ||
16 | #include <linux/irqdomain.h> | 17 | #include <linux/irqdomain.h> |
17 | #include <linux/irqchip/chained_irq.h> | 18 | #include <linux/irqchip/chained_irq.h> |
18 | #include <linux/interrupt.h> | 19 | #include <linux/interrupt.h> |
@@ -34,9 +35,14 @@ struct combiner_chip_data { | |||
34 | unsigned int irq_mask; | 35 | unsigned int irq_mask; |
35 | void __iomem *base; | 36 | void __iomem *base; |
36 | unsigned int parent_irq; | 37 | unsigned int parent_irq; |
38 | #ifdef CONFIG_PM | ||
39 | u32 pm_save; | ||
40 | #endif | ||
37 | }; | 41 | }; |
38 | 42 | ||
43 | static struct combiner_chip_data *combiner_data; | ||
39 | static struct irq_domain *combiner_irq_domain; | 44 | static struct irq_domain *combiner_irq_domain; |
45 | static unsigned int max_nr = 20; | ||
40 | 46 | ||
41 | static inline void __iomem *combiner_base(struct irq_data *data) | 47 | static inline void __iomem *combiner_base(struct irq_data *data) |
42 | { | 48 | { |
@@ -170,12 +176,10 @@ static const struct irq_domain_ops combiner_irq_domain_ops = { | |||
170 | }; | 176 | }; |
171 | 177 | ||
172 | static void __init combiner_init(void __iomem *combiner_base, | 178 | static void __init combiner_init(void __iomem *combiner_base, |
173 | struct device_node *np, | 179 | struct device_node *np) |
174 | unsigned int max_nr) | ||
175 | { | 180 | { |
176 | int i, irq; | 181 | int i, irq; |
177 | unsigned int nr_irq; | 182 | unsigned int nr_irq; |
178 | struct combiner_chip_data *combiner_data; | ||
179 | 183 | ||
180 | nr_irq = max_nr * IRQ_IN_COMBINER; | 184 | nr_irq = max_nr * IRQ_IN_COMBINER; |
181 | 185 | ||
@@ -201,11 +205,59 @@ static void __init combiner_init(void __iomem *combiner_base, | |||
201 | } | 205 | } |
202 | } | 206 | } |
203 | 207 | ||
208 | #ifdef CONFIG_PM | ||
209 | |||
210 | /** | ||
211 | * combiner_suspend - save interrupt combiner state before suspend | ||
212 | * | ||
213 | * Save the interrupt enable set register for all combiner groups since | ||
214 | * the state is lost when the system enters into a sleep state. | ||
215 | * | ||
216 | */ | ||
217 | static int combiner_suspend(void) | ||
218 | { | ||
219 | int i; | ||
220 | |||
221 | for (i = 0; i < max_nr; i++) | ||
222 | combiner_data[i].pm_save = | ||
223 | __raw_readl(combiner_data[i].base + COMBINER_ENABLE_SET); | ||
224 | |||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | /** | ||
229 | * combiner_resume - restore interrupt combiner state after resume | ||
230 | * | ||
231 | * Restore the interrupt enable set register for all combiner groups since | ||
232 | * the state is lost when the system enters into a sleep state on suspend. | ||
233 | * | ||
234 | */ | ||
235 | static void combiner_resume(void) | ||
236 | { | ||
237 | int i; | ||
238 | |||
239 | for (i = 0; i < max_nr; i++) { | ||
240 | __raw_writel(combiner_data[i].irq_mask, | ||
241 | combiner_data[i].base + COMBINER_ENABLE_CLEAR); | ||
242 | __raw_writel(combiner_data[i].pm_save, | ||
243 | combiner_data[i].base + COMBINER_ENABLE_SET); | ||
244 | } | ||
245 | } | ||
246 | |||
247 | #else | ||
248 | #define combiner_suspend NULL | ||
249 | #define combiner_resume NULL | ||
250 | #endif | ||
251 | |||
252 | static struct syscore_ops combiner_syscore_ops = { | ||
253 | .suspend = combiner_suspend, | ||
254 | .resume = combiner_resume, | ||
255 | }; | ||
256 | |||
204 | static int __init combiner_of_init(struct device_node *np, | 257 | static int __init combiner_of_init(struct device_node *np, |
205 | struct device_node *parent) | 258 | struct device_node *parent) |
206 | { | 259 | { |
207 | void __iomem *combiner_base; | 260 | void __iomem *combiner_base; |
208 | unsigned int max_nr = 20; | ||
209 | 261 | ||
210 | combiner_base = of_iomap(np, 0); | 262 | combiner_base = of_iomap(np, 0); |
211 | if (!combiner_base) { | 263 | if (!combiner_base) { |
@@ -219,7 +271,9 @@ static int __init combiner_of_init(struct device_node *np, | |||
219 | __func__, max_nr); | 271 | __func__, max_nr); |
220 | } | 272 | } |
221 | 273 | ||
222 | combiner_init(combiner_base, np, max_nr); | 274 | combiner_init(combiner_base, np); |
275 | |||
276 | register_syscore_ops(&combiner_syscore_ops); | ||
223 | 277 | ||
224 | return 0; | 278 | return 0; |
225 | } | 279 | } |