Skip to content

Commit

Permalink
ARM: exynos4/5: convert pmu wakeup to stacked domains
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Marc Zyngier authored and Jason Cooper committed Mar 18, 2015
1 parent c517d83 commit 8b283c0
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 22 deletions.
4 changes: 4 additions & 0 deletions arch/arm/boot/dts/exynos3250.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@
pmu_system_controller: system-controller@10020000 {
compatible = "samsung,exynos3250-pmu", "syscon";
reg = <0x10020000 0x4000>;
interrupt-controller;
#interrupt-cells = <3>;
interrupt-parent = <&gic>;
};

mipi_phy: video-phy@10020710 {
Expand Down Expand Up @@ -184,6 +187,7 @@
compatible = "samsung,exynos3250-rtc";
reg = <0x10070000 0x100>;
interrupts = <0 73 0>, <0 74 0>;
interrupt-parent = <&pmu_system_controller>;
status = "disabled";
};

Expand Down
4 changes: 4 additions & 0 deletions arch/arm/boot/dts/exynos4.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@
pmu_system_controller: system-controller@10020000 {
compatible = "samsung,exynos4210-pmu", "syscon";
reg = <0x10020000 0x4000>;
interrupt-controller;
#interrupt-cells = <3>;
interrupt-parent = <&gic>;
};

dsi_0: dsi@11C80000 {
Expand Down Expand Up @@ -264,6 +267,7 @@
rtc@10070000 {
compatible = "samsung,s3c6410-rtc";
reg = <0x10070000 0x100>;
interrupt-parent = <&pmu_system_controller>;
interrupts = <0 44 0>, <0 45 0>;
clocks = <&clock CLK_RTC>;
clock-names = "rtc";
Expand Down
4 changes: 4 additions & 0 deletions arch/arm/boot/dts/exynos5250.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@
clock-names = "clkout16";
clocks = <&clock CLK_FIN_PLL>;
#clock-cells = <1>;
interrupt-controller;
#interrupt-cells = <3>;
interrupt-parent = <&gic>;
};

sysreg_system_controller: syscon@10050000 {
Expand Down Expand Up @@ -232,6 +235,7 @@
rtc: rtc@101E0000 {
clocks = <&clock CLK_RTC>;
clock-names = "rtc";
interrupt-parent = <&pmu_system_controller>;
status = "disabled";
};

Expand Down
4 changes: 4 additions & 0 deletions arch/arm/boot/dts/exynos5420.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@
rtc: rtc@101E0000 {
clocks = <&clock CLK_RTC>;
clock-names = "rtc";
interrupt-parent = <&pmu_system_controller>;
status = "disabled";
};

Expand Down Expand Up @@ -769,6 +770,9 @@
clock-names = "clkout16";
clocks = <&clock CLK_FIN_PLL>;
#clock-cells = <1>;
interrupt-controller;
#interrupt-cells = <3>;
interrupt-parent = <&gic>;
};

sysreg_system_controller: syscon@10050000 {
Expand Down
15 changes: 5 additions & 10 deletions arch/arm/mach-exynos/exynos.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,16 +166,14 @@ static void __init exynos_init_io(void)
exynos_map_io();
}

/*
* Apparently, these SoCs are not able to wake-up from suspend using
* the PMU. Too bad. Should they suddenly become capable of such a
* feat, the matches below should be moved to suspend.c.
*/
static const struct of_device_id exynos_dt_pmu_match[] = {
{ .compatible = "samsung,exynos3250-pmu" },
{ .compatible = "samsung,exynos4210-pmu" },
{ .compatible = "samsung,exynos4212-pmu" },
{ .compatible = "samsung,exynos4412-pmu" },
{ .compatible = "samsung,exynos4415-pmu" },
{ .compatible = "samsung,exynos5250-pmu" },
{ .compatible = "samsung,exynos5260-pmu" },
{ .compatible = "samsung,exynos5410-pmu" },
{ .compatible = "samsung,exynos5420-pmu" },
{ /*sentinel*/ },
};

Expand All @@ -186,9 +184,6 @@ static void exynos_map_pmu(void)
np = of_find_matching_node(NULL, exynos_dt_pmu_match);
if (np)
pmu_base_addr = of_iomap(np, 0);

if (!pmu_base_addr)
panic("failed to find exynos pmu register\n");
}

static void __init exynos_init_irq(void)
Expand Down
135 changes: 123 additions & 12 deletions arch/arm/mach-exynos/suspend.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
#include <linux/syscore_ops.h>
#include <linux/cpu_pm.h>
#include <linux/io.h>
#include <linux/irqchip/arm-gic.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/of_address.h>
#include <linux/err.h>
#include <linux/regulator/machine.h>

Expand All @@ -43,8 +45,8 @@
#define EXYNOS5420_CPU_STATE 0x28

/**
* struct exynos_wkup_irq - Exynos GIC to PMU IRQ mapping
* @hwirq: Hardware IRQ signal of the GIC
* struct exynos_wkup_irq - PMU IRQ to mask mapping
* @hwirq: Hardware IRQ signal of the PMU
* @mask: Mask in PMU wake-up mask register
*/
struct exynos_wkup_irq {
Expand Down Expand Up @@ -93,14 +95,14 @@ static const struct exynos_wkup_irq exynos3250_wkup_irq[] = {
};

static const struct exynos_wkup_irq exynos4_wkup_irq[] = {
{ 76, BIT(1) }, /* RTC alarm */
{ 77, BIT(2) }, /* RTC tick */
{ 44, BIT(1) }, /* RTC alarm */
{ 45, BIT(2) }, /* RTC tick */
{ /* sentinel */ },
};

static const struct exynos_wkup_irq exynos5250_wkup_irq[] = {
{ 75, BIT(1) }, /* RTC alarm */
{ 76, BIT(2) }, /* RTC tick */
{ 43, BIT(1) }, /* RTC alarm */
{ 44, BIT(2) }, /* RTC tick */
{ /* sentinel */ },
};

Expand Down Expand Up @@ -167,6 +169,113 @@ static int exynos_irq_set_wake(struct irq_data *data, unsigned int state)
return -ENOENT;
}

static struct irq_chip exynos_pmu_chip = {
.name = "PMU",
.irq_eoi = irq_chip_eoi_parent,
.irq_mask = irq_chip_mask_parent,
.irq_unmask = irq_chip_unmask_parent,
.irq_retrigger = irq_chip_retrigger_hierarchy,
.irq_set_wake = exynos_irq_set_wake,
#ifdef CONFIG_SMP
.irq_set_affinity = irq_chip_set_affinity_parent,
#endif
};

static int exynos_pmu_domain_xlate(struct irq_domain *domain,
struct device_node *controller,
const u32 *intspec,
unsigned int intsize,
unsigned long *out_hwirq,
unsigned int *out_type)
{
if (domain->of_node != controller)
return -EINVAL; /* Shouldn't happen, really... */
if (intsize != 3)
return -EINVAL; /* Not GIC compliant */
if (intspec[0] != 0)
return -EINVAL; /* No PPI should point to this domain */

*out_hwirq = intspec[1];
*out_type = intspec[2];
return 0;
}

static int exynos_pmu_domain_alloc(struct irq_domain *domain,
unsigned int virq,
unsigned int nr_irqs, void *data)
{
struct of_phandle_args *args = data;
struct of_phandle_args parent_args;
irq_hw_number_t hwirq;
int i;

if (args->args_count != 3)
return -EINVAL; /* Not GIC compliant */
if (args->args[0] != 0)
return -EINVAL; /* No PPI should point to this domain */

hwirq = args->args[1];

for (i = 0; i < nr_irqs; i++)
irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
&exynos_pmu_chip, NULL);

parent_args = *args;
parent_args.np = domain->parent->of_node;
return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent_args);
}

static struct irq_domain_ops exynos_pmu_domain_ops = {
.xlate = exynos_pmu_domain_xlate,
.alloc = exynos_pmu_domain_alloc,
.free = irq_domain_free_irqs_common,
};

static int __init exynos_pmu_irq_init(struct device_node *node,
struct device_node *parent)
{
struct irq_domain *parent_domain, *domain;

if (!parent) {
pr_err("%s: no parent, giving up\n", node->full_name);
return -ENODEV;
}

parent_domain = irq_find_host(parent);
if (!parent_domain) {
pr_err("%s: unable to obtain parent domain\n", node->full_name);
return -ENXIO;
}

pmu_base_addr = of_iomap(node, 0);

if (!pmu_base_addr) {
pr_err("%s: failed to find exynos pmu register\n",
node->full_name);
return -ENOMEM;
}

domain = irq_domain_add_hierarchy(parent_domain, 0, 0,
node, &exynos_pmu_domain_ops,
NULL);
if (!domain) {
iounmap(pmu_base_addr);
return -ENOMEM;
}

return 0;
}

#define EXYNOS_PMU_IRQ(symbol, name) OF_DECLARE_2(irqchip, symbol, name, exynos_pmu_irq_init)

EXYNOS_PMU_IRQ(exynos3250_pmu_irq, "samsung,exynos3250-pmu");
EXYNOS_PMU_IRQ(exynos4210_pmu_irq, "samsung,exynos4210-pmu");
EXYNOS_PMU_IRQ(exynos4212_pmu_irq, "samsung,exynos4212-pmu");
EXYNOS_PMU_IRQ(exynos4412_pmu_irq, "samsung,exynos4412-pmu");
EXYNOS_PMU_IRQ(exynos4415_pmu_irq, "samsung,exynos4415-pmu");
EXYNOS_PMU_IRQ(exynos5250_pmu_irq, "samsung,exynos5250-pmu");
EXYNOS_PMU_IRQ(exynos5420_pmu_irq, "samsung,exynos5420-pmu");

static int exynos_cpu_do_idle(void)
{
/* issue the standby signal into the pm unit. */
Expand Down Expand Up @@ -615,17 +724,19 @@ static struct syscore_ops exynos_pm_syscore_ops;
void __init exynos_pm_init(void)
{
const struct of_device_id *match;
struct device_node *np;
u32 tmp;

of_find_matching_node_and_match(NULL, exynos_pmu_of_device_ids, &match);
if (!match) {
np = of_find_matching_node_and_match(NULL, exynos_pmu_of_device_ids, &match);
if (!np) {
pr_err("Failed to find PMU node\n");
return;
}
pm_data = (struct exynos_pm_data *) match->data;

/* Platform-specific GIC callback */
gic_arch_extn.irq_set_wake = exynos_irq_set_wake;
if (WARN_ON(!of_find_property(np, "interrupt-controller", NULL)))
pr_warn("Outdated DT detected, suspend/resume will NOT work\n");

pm_data = (struct exynos_pm_data *) match->data;

/* All wakeup disable */
tmp = pmu_raw_readl(S5P_WAKEUP_MASK);
Expand Down

0 comments on commit 8b283c0

Please sign in to comment.