135 lines
4.2 KiB
Diff
135 lines
4.2 KiB
Diff
From ad27287c79bea8c3bd76c3126303d038dd7c21a1 Mon Sep 17 00:00:00 2001
|
|
From: Samuel Holland <samuel@sholland.org>
|
|
Date: Sun, 12 Jan 2020 20:00:36 -0600
|
|
Subject: [PATCH 166/351] irqchip/sun6i-r: Add wakeup support
|
|
|
|
Maintain a mask of wake-enabled IRQs, and enable them in hardware
|
|
during the syscore phase of suspend. The restore the original mask
|
|
of enabled IRQs (just the NMI) during resume.
|
|
|
|
This serves two purposes. First, it lets power management firmware
|
|
running on the ARISC coprocessor know which wakeup sources Linux wants
|
|
to have enabled. That way, it can avoid turning them off when it shuts
|
|
down the remainder of the clock tree. Second, it preconfigures the
|
|
coprocessor's interrupt controller, so the firmware's wakeup logic
|
|
is as simple as waiting for an interrupt to arrive.
|
|
|
|
Signed-off-by: Samuel Holland <samuel@sholland.org>
|
|
---
|
|
drivers/irqchip/irq-sun6i-r.c | 51 +++++++++++++++++++++++++++++++++++
|
|
1 file changed, 51 insertions(+)
|
|
|
|
diff --git a/drivers/irqchip/irq-sun6i-r.c b/drivers/irqchip/irq-sun6i-r.c
|
|
index f8bfa5515f20..a5deea92057f 100644
|
|
--- a/drivers/irqchip/irq-sun6i-r.c
|
|
+++ b/drivers/irqchip/irq-sun6i-r.c
|
|
@@ -3,12 +3,14 @@
|
|
// Allwinner A31 and newer SoCs R_INTC driver
|
|
//
|
|
|
|
+#include <linux/atomic.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/irqchip.h>
|
|
#include <linux/irqdomain.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_irq.h>
|
|
+#include <linux/syscore_ops.h>
|
|
|
|
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
|
|
|
@@ -24,6 +26,9 @@
|
|
static void __iomem *base;
|
|
static irq_hw_number_t parent_offset;
|
|
static u32 parent_type;
|
|
+#ifdef CONFIG_PM_SLEEP
|
|
+static atomic_t wake_mask;
|
|
+#endif
|
|
|
|
static struct irq_chip sun6i_r_intc_edge;
|
|
static struct irq_chip sun6i_r_intc_level;
|
|
@@ -106,6 +111,20 @@ static int sun6i_r_intc_irq_set_type(struct irq_data *data, unsigned int type)
|
|
return irq_chip_set_type_parent(data, type);
|
|
}
|
|
|
|
+#ifdef CONFIG_PM_SLEEP
|
|
+static int sun6i_r_intc_irq_set_wake(struct irq_data *data, unsigned int on)
|
|
+{
|
|
+ if (on)
|
|
+ atomic_or(BIT(data->hwirq), &wake_mask);
|
|
+ else
|
|
+ atomic_andnot(BIT(data->hwirq), &wake_mask);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+#else
|
|
+#define sun6i_r_intc_irq_set_wake NULL
|
|
+#endif
|
|
+
|
|
static struct irq_chip sun6i_r_intc_edge = {
|
|
.name = "sun6i-r-intc",
|
|
.irq_ack = sun6i_r_intc_irq_ack,
|
|
@@ -116,6 +135,7 @@ static struct irq_chip sun6i_r_intc_edge = {
|
|
.irq_set_type = sun6i_r_intc_irq_set_type,
|
|
.irq_get_irqchip_state = irq_chip_get_parent_state,
|
|
.irq_set_irqchip_state = irq_chip_set_parent_state,
|
|
+ .irq_set_wake = sun6i_r_intc_irq_set_wake,
|
|
.irq_set_vcpu_affinity = irq_chip_set_vcpu_affinity_parent,
|
|
.flags = IRQCHIP_SET_TYPE_MASKED,
|
|
};
|
|
@@ -129,6 +149,7 @@ static struct irq_chip sun6i_r_intc_level = {
|
|
.irq_set_type = sun6i_r_intc_irq_set_type,
|
|
.irq_get_irqchip_state = irq_chip_get_parent_state,
|
|
.irq_set_irqchip_state = irq_chip_set_parent_state,
|
|
+ .irq_set_wake = sun6i_r_intc_irq_set_wake,
|
|
.irq_set_vcpu_affinity = irq_chip_set_vcpu_affinity_parent,
|
|
.flags = IRQCHIP_SET_TYPE_MASKED |
|
|
IRQCHIP_EOI_THREADED,
|
|
@@ -170,6 +191,34 @@ static const struct irq_domain_ops sun6i_r_intc_domain_ops = {
|
|
.free = irq_domain_free_irqs_common,
|
|
};
|
|
|
|
+#ifdef CONFIG_PM_SLEEP
|
|
+static int sun6i_r_intc_suspend(void)
|
|
+{
|
|
+ /* All wake IRQs are enabled during system sleep. */
|
|
+ writel_relaxed(atomic_read(&wake_mask), base + SUN6I_R_INTC_ENABLE);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void sun6i_r_intc_resume(void)
|
|
+{
|
|
+ /* Only the NMI is relevant during normal operation. */
|
|
+ writel_relaxed(NMI_HWIRQ_BIT, base + SUN6I_R_INTC_ENABLE);
|
|
+}
|
|
+
|
|
+static struct syscore_ops sun6i_r_intc_syscore_ops = {
|
|
+ .suspend = sun6i_r_intc_suspend,
|
|
+ .resume = sun6i_r_intc_resume,
|
|
+};
|
|
+
|
|
+static void sun6i_r_intc_syscore_init(void)
|
|
+{
|
|
+ register_syscore_ops(&sun6i_r_intc_syscore_ops);
|
|
+}
|
|
+#else
|
|
+static inline void sun6i_r_intc_syscore_init(void) {}
|
|
+#endif
|
|
+
|
|
static int __init sun6i_r_intc_init(struct device_node *node,
|
|
struct device_node *parent)
|
|
{
|
|
@@ -211,6 +260,8 @@ static int __init sun6i_r_intc_init(struct device_node *node,
|
|
writel_relaxed(NMI_HWIRQ_BIT, base + SUN6I_R_INTC_PENDING);
|
|
writel_relaxed(NMI_HWIRQ_BIT, base + SUN6I_R_INTC_ENABLE);
|
|
|
|
+ sun6i_r_intc_syscore_init();
|
|
+
|
|
return 0;
|
|
}
|
|
IRQCHIP_DECLARE(sun6i_r_intc, "allwinner,sun6i-a31-r-intc", sun6i_r_intc_init);
|
|
--
|
|
2.34.0
|
|
|