aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Zyngier <marc.zyngier@arm.com>2015-03-11 11:44:52 -0400
committerJason Cooper <jason@lakedaemon.net>2015-03-18 13:36:32 -0400
commit8b283c025443e4c3a3323c92777f422fa504caa5 (patch)
tree20ee5fdd7d45838e7a6527a897a581b42474668f
parentc517d838eb7d07bbe9507871fab3931deccff539 (diff)
ARM: exynos4/5: convert pmu wakeup to stacked domains
Exynos has been (ab)using the gic_arch_extn to provide wakeup from suspend, and it makes a lot of sense to convert this code to use stacked domains instead. This patch does just this, updating the DT files to actually reflect what the HW provides. BIG FAT WARNING: because the DTs were so far lying by not exposing the fact that the PMU block is actually the first interrupt controller in the chain for RTC, kernels with this patch applied wont have any suspend-resume facility when booted with old DTs, and old kernels with updated DTs may not even boot. Also, I strongly suspect that there is more than two wake-up interrupts on these platforms, but I leave it to the maintainers to fix their mess. Signed-off-by: Marc Zyngier <marc.zyngier@arm.com> Link: https://lkml.kernel.org/r/1426088693-15724-2-git-send-email-marc.zyngier@arm.com [ jac: squash in maz's fixup from https://lkml.kernel.org/r/5506989D.9050703@arm.com ] Signed-off-by: Jason Cooper <jason@lakedaemon.net>
-rw-r--r--arch/arm/boot/dts/exynos3250.dtsi4
-rw-r--r--arch/arm/boot/dts/exynos4.dtsi4
-rw-r--r--arch/arm/boot/dts/exynos5250.dtsi4
-rw-r--r--arch/arm/boot/dts/exynos5420.dtsi4
-rw-r--r--arch/arm/mach-exynos/exynos.c15
-rw-r--r--arch/arm/mach-exynos/suspend.c135
6 files changed, 144 insertions, 22 deletions
diff --git a/arch/arm/boot/dts/exynos3250.dtsi b/arch/arm/boot/dts/exynos3250.dtsi
index 277b48b0b6f9..580b21095eab 100644
--- a/arch/arm/boot/dts/exynos3250.dtsi
+++ b/arch/arm/boot/dts/exynos3250.dtsi
@@ -130,6 +130,9 @@
130 pmu_system_controller: system-controller@10020000 { 130 pmu_system_controller: system-controller@10020000 {
131 compatible = "samsung,exynos3250-pmu", "syscon"; 131 compatible = "samsung,exynos3250-pmu", "syscon";
132 reg = <0x10020000 0x4000>; 132 reg = <0x10020000 0x4000>;
133 interrupt-controller;
134 #interrupt-cells = <3>;
135 interrupt-parent = <&gic>;
133 }; 136 };
134 137
135 mipi_phy: video-phy@10020710 { 138 mipi_phy: video-phy@10020710 {
@@ -184,6 +187,7 @@
184 compatible = "samsung,exynos3250-rtc"; 187 compatible = "samsung,exynos3250-rtc";
185 reg = <0x10070000 0x100>; 188 reg = <0x10070000 0x100>;
186 interrupts = <0 73 0>, <0 74 0>; 189 interrupts = <0 73 0>, <0 74 0>;
190 interrupt-parent = <&pmu_system_controller>;
187 status = "disabled"; 191 status = "disabled";
188 }; 192 };
189 193
diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi
index 76173cacd450..1d21f0272390 100644
--- a/arch/arm/boot/dts/exynos4.dtsi
+++ b/arch/arm/boot/dts/exynos4.dtsi
@@ -152,6 +152,9 @@
152 pmu_system_controller: system-controller@10020000 { 152 pmu_system_controller: system-controller@10020000 {
153 compatible = "samsung,exynos4210-pmu", "syscon"; 153 compatible = "samsung,exynos4210-pmu", "syscon";
154 reg = <0x10020000 0x4000>; 154 reg = <0x10020000 0x4000>;
155 interrupt-controller;
156 #interrupt-cells = <3>;
157 interrupt-parent = <&gic>;
155 }; 158 };
156 159
157 dsi_0: dsi@11C80000 { 160 dsi_0: dsi@11C80000 {
@@ -264,6 +267,7 @@
264 rtc@10070000 { 267 rtc@10070000 {
265 compatible = "samsung,s3c6410-rtc"; 268 compatible = "samsung,s3c6410-rtc";
266 reg = <0x10070000 0x100>; 269 reg = <0x10070000 0x100>;
270 interrupt-parent = <&pmu_system_controller>;
267 interrupts = <0 44 0>, <0 45 0>; 271 interrupts = <0 44 0>, <0 45 0>;
268 clocks = <&clock CLK_RTC>; 272 clocks = <&clock CLK_RTC>;
269 clock-names = "rtc"; 273 clock-names = "rtc";
diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi
index 9bb1b0b738f5..72fa2d196629 100644
--- a/arch/arm/boot/dts/exynos5250.dtsi
+++ b/arch/arm/boot/dts/exynos5250.dtsi
@@ -196,6 +196,9 @@
196 clock-names = "clkout16"; 196 clock-names = "clkout16";
197 clocks = <&clock CLK_FIN_PLL>; 197 clocks = <&clock CLK_FIN_PLL>;
198 #clock-cells = <1>; 198 #clock-cells = <1>;
199 interrupt-controller;
200 #interrupt-cells = <3>;
201 interrupt-parent = <&gic>;
199 }; 202 };
200 203
201 sysreg_system_controller: syscon@10050000 { 204 sysreg_system_controller: syscon@10050000 {
@@ -232,6 +235,7 @@
232 rtc: rtc@101E0000 { 235 rtc: rtc@101E0000 {
233 clocks = <&clock CLK_RTC>; 236 clocks = <&clock CLK_RTC>;
234 clock-names = "rtc"; 237 clock-names = "rtc";
238 interrupt-parent = <&pmu_system_controller>;
235 status = "disabled"; 239 status = "disabled";
236 }; 240 };
237 241
diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi
index 9dc2e9773b30..d11a6ab4ecd2 100644
--- a/arch/arm/boot/dts/exynos5420.dtsi
+++ b/arch/arm/boot/dts/exynos5420.dtsi
@@ -327,6 +327,7 @@
327 rtc: rtc@101E0000 { 327 rtc: rtc@101E0000 {
328 clocks = <&clock CLK_RTC>; 328 clocks = <&clock CLK_RTC>;
329 clock-names = "rtc"; 329 clock-names = "rtc";
330 interrupt-parent = <&pmu_system_controller>;
330 status = "disabled"; 331 status = "disabled";
331 }; 332 };
332 333
@@ -769,6 +770,9 @@
769 clock-names = "clkout16"; 770 clock-names = "clkout16";
770 clocks = <&clock CLK_FIN_PLL>; 771 clocks = <&clock CLK_FIN_PLL>;
771 #clock-cells = <1>; 772 #clock-cells = <1>;
773 interrupt-controller;
774 #interrupt-cells = <3>;
775 interrupt-parent = <&gic>;
772 }; 776 };
773 777
774 sysreg_system_controller: syscon@10050000 { 778 sysreg_system_controller: syscon@10050000 {
diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c
index 9e9dfdfad9d7..f44c2e05c82e 100644
--- a/arch/arm/mach-exynos/exynos.c
+++ b/arch/arm/mach-exynos/exynos.c
@@ -166,16 +166,14 @@ static void __init exynos_init_io(void)
166 exynos_map_io(); 166 exynos_map_io();
167} 167}
168 168
169/*
170 * Apparently, these SoCs are not able to wake-up from suspend using
171 * the PMU. Too bad. Should they suddenly become capable of such a
172 * feat, the matches below should be moved to suspend.c.
173 */
169static const struct of_device_id exynos_dt_pmu_match[] = { 174static const struct of_device_id exynos_dt_pmu_match[] = {
170 { .compatible = "samsung,exynos3250-pmu" },
171 { .compatible = "samsung,exynos4210-pmu" },
172 { .compatible = "samsung,exynos4212-pmu" },
173 { .compatible = "samsung,exynos4412-pmu" },
174 { .compatible = "samsung,exynos4415-pmu" },
175 { .compatible = "samsung,exynos5250-pmu" },
176 { .compatible = "samsung,exynos5260-pmu" }, 175 { .compatible = "samsung,exynos5260-pmu" },
177 { .compatible = "samsung,exynos5410-pmu" }, 176 { .compatible = "samsung,exynos5410-pmu" },
178 { .compatible = "samsung,exynos5420-pmu" },
179 { /*sentinel*/ }, 177 { /*sentinel*/ },
180}; 178};
181 179
@@ -186,9 +184,6 @@ static void exynos_map_pmu(void)
186 np = of_find_matching_node(NULL, exynos_dt_pmu_match); 184 np = of_find_matching_node(NULL, exynos_dt_pmu_match);
187 if (np) 185 if (np)
188 pmu_base_addr = of_iomap(np, 0); 186 pmu_base_addr = of_iomap(np, 0);
189
190 if (!pmu_base_addr)
191 panic("failed to find exynos pmu register\n");
192} 187}
193 188
194static void __init exynos_init_irq(void) 189static void __init exynos_init_irq(void)
diff --git a/arch/arm/mach-exynos/suspend.c b/arch/arm/mach-exynos/suspend.c
index 52e2b1a2fddb..7b09e7631245 100644
--- a/arch/arm/mach-exynos/suspend.c
+++ b/arch/arm/mach-exynos/suspend.c
@@ -18,7 +18,9 @@
18#include <linux/syscore_ops.h> 18#include <linux/syscore_ops.h>
19#include <linux/cpu_pm.h> 19#include <linux/cpu_pm.h>
20#include <linux/io.h> 20#include <linux/io.h>
21#include <linux/irqchip/arm-gic.h> 21#include <linux/irq.h>
22#include <linux/irqdomain.h>
23#include <linux/of_address.h>
22#include <linux/err.h> 24#include <linux/err.h>
23#include <linux/regulator/machine.h> 25#include <linux/regulator/machine.h>
24 26
@@ -43,8 +45,8 @@
43#define EXYNOS5420_CPU_STATE 0x28 45#define EXYNOS5420_CPU_STATE 0x28
44 46
45/** 47/**
46 * struct exynos_wkup_irq - Exynos GIC to PMU IRQ mapping 48 * struct exynos_wkup_irq - PMU IRQ to mask mapping
47 * @hwirq: Hardware IRQ signal of the GIC 49 * @hwirq: Hardware IRQ signal of the PMU
48 * @mask: Mask in PMU wake-up mask register 50 * @mask: Mask in PMU wake-up mask register
49 */ 51 */
50struct exynos_wkup_irq { 52struct exynos_wkup_irq {
@@ -93,14 +95,14 @@ static const struct exynos_wkup_irq exynos3250_wkup_irq[] = {
93}; 95};
94 96
95static const struct exynos_wkup_irq exynos4_wkup_irq[] = { 97static const struct exynos_wkup_irq exynos4_wkup_irq[] = {
96 { 76, BIT(1) }, /* RTC alarm */ 98 { 44, BIT(1) }, /* RTC alarm */
97 { 77, BIT(2) }, /* RTC tick */ 99 { 45, BIT(2) }, /* RTC tick */
98 { /* sentinel */ }, 100 { /* sentinel */ },
99}; 101};
100 102
101static const struct exynos_wkup_irq exynos5250_wkup_irq[] = { 103static const struct exynos_wkup_irq exynos5250_wkup_irq[] = {
102 { 75, BIT(1) }, /* RTC alarm */ 104 { 43, BIT(1) }, /* RTC alarm */
103 { 76, BIT(2) }, /* RTC tick */ 105 { 44, BIT(2) }, /* RTC tick */
104 { /* sentinel */ }, 106 { /* sentinel */ },
105}; 107};
106 108
@@ -167,6 +169,113 @@ static int exynos_irq_set_wake(struct irq_data *data, unsigned int state)
167 return -ENOENT; 169 return -ENOENT;
168} 170}
169 171
172static struct irq_chip exynos_pmu_chip = {
173 .name = "PMU",
174 .irq_eoi = irq_chip_eoi_parent,
175 .irq_mask = irq_chip_mask_parent,
176 .irq_unmask = irq_chip_unmask_parent,
177 .irq_retrigger = irq_chip_retrigger_hierarchy,
178 .irq_set_wake = exynos_irq_set_wake,
179#ifdef CONFIG_SMP
180 .irq_set_affinity = irq_chip_set_affinity_parent,
181#endif
182};
183
184static int exynos_pmu_domain_xlate(struct irq_domain *domain,
185 struct device_node *controller,
186 const u32 *intspec,
187 unsigned int intsize,
188 unsigned long *out_hwirq,
189 unsigned int *out_type)
190{
191 if (domain->of_node != controller)
192 return -EINVAL; /* Shouldn't happen, really... */
193 if (intsize != 3)
194 return -EINVAL; /* Not GIC compliant */
195 if (intspec[0] != 0)
196 return -EINVAL; /* No PPI should point to this domain */
197
198 *out_hwirq = intspec[1];
199 *out_type = intspec[2];
200 return 0;
201}
202
203static int exynos_pmu_domain_alloc(struct irq_domain *domain,
204 unsigned int virq,
205 unsigned int nr_irqs, void *data)
206{
207 struct of_phandle_args *args = data;
208 struct of_phandle_args parent_args;
209 irq_hw_number_t hwirq;
210 int i;
211
212 if (args->args_count != 3)
213 return -EINVAL; /* Not GIC compliant */
214 if (args->args[0] != 0)
215 return -EINVAL; /* No PPI should point to this domain */
216
217 hwirq = args->args[1];
218
219 for (i = 0; i < nr_irqs; i++)
220 irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
221 &exynos_pmu_chip, NULL);
222
223 parent_args = *args;
224 parent_args.np = domain->parent->of_node;
225 return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent_args);
226}
227
228static struct irq_domain_ops exynos_pmu_domain_ops = {
229 .xlate = exynos_pmu_domain_xlate,
230 .alloc = exynos_pmu_domain_alloc,
231 .free = irq_domain_free_irqs_common,
232};
233
234static int __init exynos_pmu_irq_init(struct device_node *node,
235 struct device_node *parent)
236{
237 struct irq_domain *parent_domain, *domain;
238
239 if (!parent) {
240 pr_err("%s: no parent, giving up\n", node->full_name);
241 return -ENODEV;
242 }
243
244 parent_domain = irq_find_host(parent);
245 if (!parent_domain) {
246 pr_err("%s: unable to obtain parent domain\n", node->full_name);
247 return -ENXIO;
248 }
249
250 pmu_base_addr = of_iomap(node, 0);
251
252 if (!pmu_base_addr) {
253 pr_err("%s: failed to find exynos pmu register\n",
254 node->full_name);
255 return -ENOMEM;
256 }
257
258 domain = irq_domain_add_hierarchy(parent_domain, 0, 0,
259 node, &exynos_pmu_domain_ops,
260 NULL);
261 if (!domain) {
262 iounmap(pmu_base_addr);
263 return -ENOMEM;
264 }
265
266 return 0;
267}
268
269#define EXYNOS_PMU_IRQ(symbol, name) OF_DECLARE_2(irqchip, symbol, name, exynos_pmu_irq_init)
270
271EXYNOS_PMU_IRQ(exynos3250_pmu_irq, "samsung,exynos3250-pmu");
272EXYNOS_PMU_IRQ(exynos4210_pmu_irq, "samsung,exynos4210-pmu");
273EXYNOS_PMU_IRQ(exynos4212_pmu_irq, "samsung,exynos4212-pmu");
274EXYNOS_PMU_IRQ(exynos4412_pmu_irq, "samsung,exynos4412-pmu");
275EXYNOS_PMU_IRQ(exynos4415_pmu_irq, "samsung,exynos4415-pmu");
276EXYNOS_PMU_IRQ(exynos5250_pmu_irq, "samsung,exynos5250-pmu");
277EXYNOS_PMU_IRQ(exynos5420_pmu_irq, "samsung,exynos5420-pmu");
278
170static int exynos_cpu_do_idle(void) 279static int exynos_cpu_do_idle(void)
171{ 280{
172 /* issue the standby signal into the pm unit. */ 281 /* issue the standby signal into the pm unit. */
@@ -615,17 +724,19 @@ static struct syscore_ops exynos_pm_syscore_ops;
615void __init exynos_pm_init(void) 724void __init exynos_pm_init(void)
616{ 725{
617 const struct of_device_id *match; 726 const struct of_device_id *match;
727 struct device_node *np;
618 u32 tmp; 728 u32 tmp;
619 729
620 of_find_matching_node_and_match(NULL, exynos_pmu_of_device_ids, &match); 730 np = of_find_matching_node_and_match(NULL, exynos_pmu_of_device_ids, &match);
621 if (!match) { 731 if (!np) {
622 pr_err("Failed to find PMU node\n"); 732 pr_err("Failed to find PMU node\n");
623 return; 733 return;
624 } 734 }
625 pm_data = (struct exynos_pm_data *) match->data;
626 735
627 /* Platform-specific GIC callback */ 736 if (WARN_ON(!of_find_property(np, "interrupt-controller", NULL)))
628 gic_arch_extn.irq_set_wake = exynos_irq_set_wake; 737 pr_warn("Outdated DT detected, suspend/resume will NOT work\n");
738
739 pm_data = (struct exynos_pm_data *) match->data;
629 740
630 /* All wakeup disable */ 741 /* All wakeup disable */
631 tmp = pmu_raw_readl(S5P_WAKEUP_MASK); 742 tmp = pmu_raw_readl(S5P_WAKEUP_MASK);