[PATCH 4/4] arm: ls1021a: set wakeup devices dynamically for sleep/deep sleep

From: Chenhui Zhao
Date: Fri Jan 30 2015 - 07:56:01 EST


If a device works as a wakeup source, it will keep working in the period of
sleep/deep sleep. This patch sets the wakeup devices according to the wakeup
attribute of device.

Signed-off-by: Chenhui Zhao <chenhui.zhao@xxxxxxxxxxxxx>
---
arch/arm/boot/dts/ls1021a.dtsi | 2 +
arch/arm/mach-imx/pm-ls1.c | 101 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 103 insertions(+)

diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi
index 0c51ce0..64534c0 100644
--- a/arch/arm/boot/dts/ls1021a.dtsi
+++ b/arch/arm/boot/dts/ls1021a.dtsi
@@ -136,6 +136,7 @@
sdhci,auto-cmd12;
big-endian;
bus-width = <4>;
+ sleep = <&rcpm 0x00000080 0x0>;
status = "disabled";
};

@@ -289,6 +290,7 @@
interrupts = <GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&sysclk>;
clock-names = "ipg";
+ sleep = <&rcpm 0x0 0x40000000>;
status = "disabled";
};

diff --git a/arch/arm/mach-imx/pm-ls1.c b/arch/arm/mach-imx/pm-ls1.c
index 4f9ca80..b11fcb2 100644
--- a/arch/arm/mach-imx/pm-ls1.c
+++ b/arch/arm/mach-imx/pm-ls1.c
@@ -35,6 +35,13 @@
#define CCSR_SCFG_DPSLPCR 0
#define CCSR_SCFG_DPSLPCR_VAL 0x1
#define CCSR_SCFG_PMCINTECR 0x160
+#define CCSR_SCFG_PMCINTECR_LPUART 0x40000000
+#define CCSR_SCFG_PMCINTECR_FTM 0x20000000
+#define CCSR_SCFG_PMCINTECR_GPIO 0x10000000
+#define CCSR_SCFG_PMCINTECR_IRQ0 0x08000000
+#define CCSR_SCFG_PMCINTECR_IRQ1 0x04000000
+#define CCSR_SCFG_PMCINTECR_ETSECRXG0 0x00800000
+#define CCSR_SCFG_PMCINTECR_ETSECRXG1 0x00400000
#define CCSR_SCFG_PMCINTLECR 0x164
#define CCSR_SCFG_PMCINTSR 0x168
#define CCSR_SCFG_SPARECR2 0x504
@@ -50,7 +57,11 @@
#define CCSR_RCPM_CLPCL10SETR 0x1c4
#define CCSR_RCPM_CLPCL10SETR_C0 0x1
#define CCSR_RCPM_IPPDEXPCR0 0x140
+#define CCSR_RCPM_IPPDEXPCR0_ETSEC 0x80000000
+#define CCSR_RCPM_IPPDEXPCR0_GPIO 0x00000040
#define CCSR_RCPM_IPPDEXPCR1 0x144
+#define CCSR_RCPM_IPPDEXPCR1_LPUART 0x40000000
+#define CCSR_RCPM_IPPDEXPCR1_FLEXTIMER 0x20000000

#define QIXIS_CTL_SYS 0x5
#define QIXIS_CTL_SYS_EVTSW_MASK 0x0c
@@ -64,6 +75,10 @@
/* use the last page of SRAM */
#define SRAM_CODE_BASE_PHY (OCRAM_BASE + OCRAM_SIZE - PAGE_SIZE)

+#define SLEEP_ARRAY_SIZE 3
+
+static u32 ippdexpcr0, ippdexpcr1;
+
struct ls1_pm_baseaddr {
void __iomem *rcpm;
void __iomem *epu;
@@ -242,6 +257,49 @@ static void ls1_board_resume(void)
iowrite8(tmp, ls1_pm_base.fpga + QIXIS_CTL_SYS);
}

+static void ls1_setup_pmc_int(void)
+{
+ u32 pmcintecr;
+
+ pmcintecr = 0;
+ if (ippdexpcr0 & CCSR_RCPM_IPPDEXPCR0_ETSEC)
+ pmcintecr |= CCSR_SCFG_PMCINTECR_ETSECRXG0 |
+ CCSR_SCFG_PMCINTECR_ETSECRXG1;
+
+ if (ippdexpcr0 & CCSR_RCPM_IPPDEXPCR0_GPIO)
+ pmcintecr |= CCSR_SCFG_PMCINTECR_GPIO;
+
+ if (ippdexpcr1 & CCSR_RCPM_IPPDEXPCR1_LPUART)
+ pmcintecr |= CCSR_SCFG_PMCINTECR_LPUART;
+
+ if (ippdexpcr1 & CCSR_RCPM_IPPDEXPCR1_FLEXTIMER)
+ pmcintecr |= CCSR_SCFG_PMCINTECR_FTM;
+
+ /* always set external IRQ pins as wakeup source */
+ pmcintecr |= CCSR_SCFG_PMCINTECR_IRQ0 | CCSR_SCFG_PMCINTECR_IRQ1;
+
+ /* enable wakeup interrupt during deep sleep */
+ iowrite32be(pmcintecr, ls1_pm_base.scfg + CCSR_SCFG_PMCINTECR);
+ iowrite32be(0, ls1_pm_base.scfg + CCSR_SCFG_PMCINTLECR);
+ /* clear PMC interrupt status */
+ iowrite32be(0xffffffff, ls1_pm_base.scfg + CCSR_SCFG_PMCINTSR);
+}
+
+static void ls1_clear_pmc_int(void)
+{
+ /* disable wakeup interrupt during deep sleep */
+ iowrite32be(0, ls1_pm_base.scfg + CCSR_SCFG_PMCINTECR);
+ /* clear PMC interrupt status */
+ iowrite32be(0xffffffff, ls1_pm_base.scfg + CCSR_SCFG_PMCINTSR);
+}
+
+/* set IP powerdown exception, make them work during sleep/deep sleep */
+static void ls1_set_powerdown(void)
+{
+ iowrite32be(ippdexpcr0, ls1_pm_base.rcpm + CCSR_RCPM_IPPDEXPCR0);
+ iowrite32be(ippdexpcr1, ls1_pm_base.rcpm + CCSR_RCPM_IPPDEXPCR1);
+}
+
static void ls1_enter_deepsleep(void)
{
/* save DDR data */
@@ -265,8 +323,12 @@ static void ls1_enter_deepsleep(void)
/* copy the last stage code to sram */
ls1_copy_sram_code();

+ ls1_setup_pmc_int();
+
cpu_suspend(SRAM_CODE_BASE_PHY, ls1_start_deepsleep);

+ ls1_clear_pmc_int();
+
/* disable Warm Device Reset */
ls1_clrsetbits_be32(ls1_pm_base.scfg + CCSR_SCFG_DPSLPCR,
CCSR_SCFG_DPSLPCR_VAL, 0);
@@ -274,10 +336,45 @@ static void ls1_enter_deepsleep(void)
ls1_board_resume();
}

+static void ls1_set_power_except(struct device *dev, int on)
+{
+ int ret;
+ u32 value[SLEEP_ARRAY_SIZE];
+
+ /*
+ * Get the values in the "sleep" property. There are three values.
+ * The first points to the RCPM node, the second is the value of
+ * the ippdexpcr0 register, and the third is the value of
+ * the ippdexpcr1 register.
+ */
+ ret = of_property_read_u32_array(dev->of_node, "sleep",
+ value, SLEEP_ARRAY_SIZE);
+ if (ret) {
+ dev_err(dev, "%s: Can not find the \"sleep\" property.\n",
+ __func__);
+ return;
+ }
+
+ ippdexpcr0 |= value[1];
+ ippdexpcr1 |= value[2];
+
+ pr_debug("%s: set %s as a wakeup source", __func__,
+ dev->of_node->full_name);
+}
+
+static void ls1_set_wakeup_device(struct device *dev, void *enable)
+{
+ /* set each device which can act as wakeup source */
+ if (device_may_wakeup(dev))
+ ls1_set_power_except(dev, *((int *)enable));
+}
+
static int ls1_suspend_enter(suspend_state_t state)
{
int ret = 0;

+ ls1_set_powerdown();
+
switch (state) {
case PM_SUSPEND_STANDBY:
flush_cache_louis();
@@ -316,6 +413,10 @@ static int ls1_suspend_begin(suspend_state_t state)

ls1_pm_state = state;

+ ippdexpcr0 = 0;
+ ippdexpcr1 = 0;
+ dpm_for_each_dev(NULL, ls1_set_wakeup_device);
+
if (ls1_pm_state == PM_SUSPEND_MEM)
ret = ls1_pm_iomap();

--
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/