2015-07-31 12:53:24

by Chenhui Zhao

[permalink] [raw]
Subject: [PATCH 1/4] powerpc/85xx: support sleep feature on QorIQ SoCs with RCPM

In sleep mode, the clocks of e500 cores and unused IP blocks is
turned off. The IP blocks which are allowed to wake up the processor
are still running.

The sleep mode is equal to the Standby state in Linux. Use the
command to enter sleep mode:
echo standby > /sys/power/state

Signed-off-by: Chenhui Zhao <[email protected]>
---
Note: This patch set is based on CPU hotplug patches.

arch/powerpc/Kconfig | 3 +-
arch/powerpc/platforms/85xx/Kconfig | 5 +++
arch/powerpc/platforms/85xx/Makefile | 1 +
arch/powerpc/platforms/85xx/qoriq_pm.c | 59 ++++++++++++++++++++++++++++++++++
arch/powerpc/platforms/86xx/Kconfig | 1 +
5 files changed, 67 insertions(+), 2 deletions(-)
create mode 100644 arch/powerpc/platforms/85xx/qoriq_pm.c

diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index dd9e252..57bbbfb 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -233,7 +233,7 @@ config ARCH_HIBERNATION_POSSIBLE
config ARCH_SUSPEND_POSSIBLE
def_bool y
depends on ADB_PMU || PPC_EFIKA || PPC_LITE5200 || PPC_83xx || \
- (PPC_85xx && !PPC_E500MC) || PPC_86xx || PPC_PSERIES \
+ FSL_SOC_BOOKE || PPC_86xx || PPC_PSERIES \
|| 44x || 40x

config PPC_DCR_NATIVE
@@ -753,7 +753,6 @@ config FSL_PCI

config FSL_PMC
bool
- default y
depends on SUSPEND && (PPC_85xx || PPC_86xx)
help
Freescale MPC85xx/MPC86xx power management controller support
diff --git a/arch/powerpc/platforms/85xx/Kconfig b/arch/powerpc/platforms/85xx/Kconfig
index e626461..dff2ea6 100644
--- a/arch/powerpc/platforms/85xx/Kconfig
+++ b/arch/powerpc/platforms/85xx/Kconfig
@@ -9,6 +9,8 @@ menuconfig FSL_SOC_BOOKE
select SERIAL_8250_EXTENDED if SERIAL_8250
select SERIAL_8250_SHARE_IRQ if SERIAL_8250
select FSL_CORENET_RCPM if PPC_E500MC
+ select FSL_QORIQ_PM if SUSPEND && PPC_E500MC
+ select FSL_PMC if SUSPEND && !PPC_E500MC
default y

if FSL_SOC_BOOKE
@@ -289,3 +291,6 @@ endif # FSL_SOC_BOOKE

config TQM85xx
bool
+
+config FSL_QORIQ_PM
+ bool
diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile
index 7bc86da..fdae28b 100644
--- a/arch/powerpc/platforms/85xx/Makefile
+++ b/arch/powerpc/platforms/85xx/Makefile
@@ -3,6 +3,7 @@
#
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_FSL_PMC) += mpc85xx_pm_ops.o
+obj-$(CONFIG_FSL_QORIQ_PM) += qoriq_pm.o

obj-y += common.o

diff --git a/arch/powerpc/platforms/85xx/qoriq_pm.c b/arch/powerpc/platforms/85xx/qoriq_pm.c
new file mode 100644
index 0000000..27ec337
--- /dev/null
+++ b/arch/powerpc/platforms/85xx/qoriq_pm.c
@@ -0,0 +1,59 @@
+/*
+ * Support Power Management feature
+ *
+ * Copyright 2014-2015 Freescale Semiconductor Inc.
+ *
+ * Author: Chenhui Zhao <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/suspend.h>
+#include <linux/of_platform.h>
+
+#include <asm/fsl_pm.h>
+
+static int qoriq_suspend_enter(suspend_state_t state)
+{
+ int ret = 0;
+
+ switch (state) {
+ case PM_SUSPEND_STANDBY:
+ cur_cpu_spec->cpu_down_flush();
+ ret = qoriq_pm_ops->plat_enter_sleep();
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int qoriq_suspend_valid(suspend_state_t state)
+{
+ unsigned int pm_modes;
+
+ pm_modes = qoriq_pm_ops->get_pm_modes();
+
+ if ((state == PM_SUSPEND_STANDBY) && (pm_modes & FSL_PM_SLEEP))
+ return 1;
+
+ return 0;
+}
+
+static const struct platform_suspend_ops qoriq_suspend_ops = {
+ .valid = qoriq_suspend_valid,
+ .enter = qoriq_suspend_enter,
+};
+
+static int __init qoriq_suspend_init(void)
+{
+ suspend_set_ops(&qoriq_suspend_ops);
+
+ return 0;
+}
+arch_initcall(qoriq_suspend_init);
diff --git a/arch/powerpc/platforms/86xx/Kconfig b/arch/powerpc/platforms/86xx/Kconfig
index 1afd1e4..09638e0 100644
--- a/arch/powerpc/platforms/86xx/Kconfig
+++ b/arch/powerpc/platforms/86xx/Kconfig
@@ -5,6 +5,7 @@ menuconfig PPC_86xx
select FSL_SOC
select ALTIVEC
select ARCH_WANT_OPTIONAL_GPIOLIB
+ select FSL_PMC if SUSPEND
help
The Freescale E600 SoCs have 74xx cores.

--
1.9.1


2015-07-31 13:26:11

by Chenhui Zhao

[permalink] [raw]
Subject: [PATCH 2/4] powerpc: get the physical base address of DCSR

Add get_dcsrbase() to get the physical base address of DCSR.

Signed-off-by: Chenhui Zhao <[email protected]>
---
arch/powerpc/sysdev/fsl_soc.c | 31 +++++++++++++++++++++++++++++++
arch/powerpc/sysdev/fsl_soc.h | 1 +
2 files changed, 32 insertions(+)

diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c
index 99269c0..ffb7c1c 100644
--- a/arch/powerpc/sysdev/fsl_soc.c
+++ b/arch/powerpc/sysdev/fsl_soc.c
@@ -45,6 +45,37 @@ extern void init_fcc_ioports(struct fs_platform_info*);
extern void init_fec_ioports(struct fs_platform_info*);
extern void init_smc_ioports(struct fs_uart_platform_info*);
static phys_addr_t immrbase = -1;
+static phys_addr_t dcsrbase = -1;
+
+phys_addr_t get_dcsrbase(void)
+{
+ struct device_node *np;
+ const __be32 *prop;
+ int size;
+ u32 naddr;
+
+ if (dcsrbase != -1)
+ return dcsrbase;
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,dcsr");
+ if (!np)
+ return -1;
+
+ prop = of_get_property(np, "#address-cells", &size);
+ if (prop && size == 4)
+ naddr = be32_to_cpup(prop);
+ else
+ naddr = 2;
+
+ prop = of_get_property(np, "ranges", NULL);
+ if (prop)
+ dcsrbase = of_translate_address(np, prop + naddr);
+
+ of_node_put(np);
+
+ return dcsrbase;
+}
+EXPORT_SYMBOL(get_dcsrbase);

phys_addr_t get_immrbase(void)
{
diff --git a/arch/powerpc/sysdev/fsl_soc.h b/arch/powerpc/sysdev/fsl_soc.h
index 4c5a19e..5fdd3a5 100644
--- a/arch/powerpc/sysdev/fsl_soc.h
+++ b/arch/powerpc/sysdev/fsl_soc.h
@@ -6,6 +6,7 @@

struct spi_device;

+extern phys_addr_t get_dcsrbase(void);
extern phys_addr_t get_immrbase(void);
#if defined(CONFIG_CPM2) || defined(CONFIG_QUICC_ENGINE) || defined(CONFIG_8xx)
extern u32 get_brgfreq(void);
--
1.9.1

2015-07-31 13:08:48

by Chenhui Zhao

[permalink] [raw]
Subject: [PATCH 3/4] powerpc: pm: add EPU FSM configuration for deep sleep

In the last stage of deep sleep, software will trigger a Finite
State Machine (FSM) to control the hardware precedure, such as
board isolation, killing PLLs, removing power, and so on.

When the system is waked up by an interrupt, the FSM controls the
hardware to complete the early resume precedure.

This patch configure the EPU FSM preparing for deep sleep.

Signed-off-by: Chenhui Zhao <[email protected]>
---
arch/powerpc/platforms/85xx/Makefile | 2 +-
arch/powerpc/platforms/85xx/sleep_fsm.c | 256 ++++++++++++++++++++++++++++++++
arch/powerpc/platforms/85xx/sleep_fsm.h | 104 +++++++++++++
3 files changed, 361 insertions(+), 1 deletion(-)
create mode 100644 arch/powerpc/platforms/85xx/sleep_fsm.c
create mode 100644 arch/powerpc/platforms/85xx/sleep_fsm.h

diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile
index fdae28b..87fb847 100644
--- a/arch/powerpc/platforms/85xx/Makefile
+++ b/arch/powerpc/platforms/85xx/Makefile
@@ -3,7 +3,7 @@
#
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_FSL_PMC) += mpc85xx_pm_ops.o
-obj-$(CONFIG_FSL_QORIQ_PM) += qoriq_pm.o
+obj-$(CONFIG_FSL_QORIQ_PM) += qoriq_pm.o sleep_fsm.o

obj-y += common.o

diff --git a/arch/powerpc/platforms/85xx/sleep_fsm.c b/arch/powerpc/platforms/85xx/sleep_fsm.c
new file mode 100644
index 0000000..2e81d37
--- /dev/null
+++ b/arch/powerpc/platforms/85xx/sleep_fsm.c
@@ -0,0 +1,256 @@
+/*
+ * Freescale deep sleep FSM (finite-state machine) configuration
+ *
+ * Copyright 2015 Freescale Semiconductor Inc.
+ *
+ * Author: Hongbo Zhang <[email protected]>
+ * Chenhui Zhao <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/types.h>
+
+#include "sleep_fsm.h"
+/*
+ * These values are from chip's reference manual. For example,
+ * the values for T1040 can be found in "8.4.3.8 Programming
+ * supporting deep sleep mode" of Chapter 8 "Run Control and
+ * Power Management (RCPM)".
+ * The default value can be applied to T104x, LS1021.
+ */
+struct fsm_reg_vals epu_default_val[] = {
+ /* EPGCR (Event Processor Global Control Register) */
+ {EPGCR, 0},
+ /* EPECR (Event Processor Event Control Registers) */
+ {EPECR0 + EPECR_STRIDE * 0, 0},
+ {EPECR0 + EPECR_STRIDE * 1, 0},
+ {EPECR0 + EPECR_STRIDE * 2, 0xF0004004},
+ {EPECR0 + EPECR_STRIDE * 3, 0x80000084},
+ {EPECR0 + EPECR_STRIDE * 4, 0x20000084},
+ {EPECR0 + EPECR_STRIDE * 5, 0x08000004},
+ {EPECR0 + EPECR_STRIDE * 6, 0x80000084},
+ {EPECR0 + EPECR_STRIDE * 7, 0x80000084},
+ {EPECR0 + EPECR_STRIDE * 8, 0x60000084},
+ {EPECR0 + EPECR_STRIDE * 9, 0x08000084},
+ {EPECR0 + EPECR_STRIDE * 10, 0x42000084},
+ {EPECR0 + EPECR_STRIDE * 11, 0x90000084},
+ {EPECR0 + EPECR_STRIDE * 12, 0x80000084},
+ {EPECR0 + EPECR_STRIDE * 13, 0x08000084},
+ {EPECR0 + EPECR_STRIDE * 14, 0x02000084},
+ {EPECR0 + EPECR_STRIDE * 15, 0x00000004},
+ /*
+ * EPEVTCR (Event Processor EVT Pin Control Registers)
+ * SCU8 triger EVT2, and SCU11 triger EVT9
+ */
+ {EPEVTCR0 + EPEVTCR_STRIDE * 0, 0},
+ {EPEVTCR0 + EPEVTCR_STRIDE * 1, 0},
+ {EPEVTCR0 + EPEVTCR_STRIDE * 2, 0x80000001},
+ {EPEVTCR0 + EPEVTCR_STRIDE * 3, 0},
+ {EPEVTCR0 + EPEVTCR_STRIDE * 4, 0},
+ {EPEVTCR0 + EPEVTCR_STRIDE * 5, 0},
+ {EPEVTCR0 + EPEVTCR_STRIDE * 6, 0},
+ {EPEVTCR0 + EPEVTCR_STRIDE * 7, 0},
+ {EPEVTCR0 + EPEVTCR_STRIDE * 8, 0},
+ {EPEVTCR0 + EPEVTCR_STRIDE * 9, 0xB0000001},
+ /* EPCMPR (Event Processor Counter Compare Registers) */
+ {EPCMPR0 + EPCMPR_STRIDE * 0, 0},
+ {EPCMPR0 + EPCMPR_STRIDE * 1, 0},
+ {EPCMPR0 + EPCMPR_STRIDE * 2, 0x000000FF},
+ {EPCMPR0 + EPCMPR_STRIDE * 3, 0},
+ {EPCMPR0 + EPCMPR_STRIDE * 4, 0x000000FF},
+ {EPCMPR0 + EPCMPR_STRIDE * 5, 0x00000020},
+ {EPCMPR0 + EPCMPR_STRIDE * 6, 0},
+ {EPCMPR0 + EPCMPR_STRIDE * 7, 0},
+ {EPCMPR0 + EPCMPR_STRIDE * 8, 0x000000FF},
+ {EPCMPR0 + EPCMPR_STRIDE * 9, 0x000000FF},
+ {EPCMPR0 + EPCMPR_STRIDE * 10, 0x000000FF},
+ {EPCMPR0 + EPCMPR_STRIDE * 11, 0x000000FF},
+ {EPCMPR0 + EPCMPR_STRIDE * 12, 0x000000FF},
+ {EPCMPR0 + EPCMPR_STRIDE * 13, 0},
+ {EPCMPR0 + EPCMPR_STRIDE * 14, 0x000000FF},
+ {EPCMPR0 + EPCMPR_STRIDE * 15, 0x000000FF},
+ /* EPCCR (Event Processor Counter Control Registers) */
+ {EPCCR0 + EPCCR_STRIDE * 0, 0},
+ {EPCCR0 + EPCCR_STRIDE * 1, 0},
+ {EPCCR0 + EPCCR_STRIDE * 2, 0x92840000},
+ {EPCCR0 + EPCCR_STRIDE * 3, 0},
+ {EPCCR0 + EPCCR_STRIDE * 4, 0x92840000},
+ {EPCCR0 + EPCCR_STRIDE * 5, 0x92840000},
+ {EPCCR0 + EPCCR_STRIDE * 6, 0},
+ {EPCCR0 + EPCCR_STRIDE * 7, 0},
+ {EPCCR0 + EPCCR_STRIDE * 8, 0x92840000},
+ {EPCCR0 + EPCCR_STRIDE * 9, 0x92840000},
+ {EPCCR0 + EPCCR_STRIDE * 10, 0x92840000},
+ {EPCCR0 + EPCCR_STRIDE * 11, 0x92840000},
+ {EPCCR0 + EPCCR_STRIDE * 12, 0x92840000},
+ {EPCCR0 + EPCCR_STRIDE * 13, 0},
+ {EPCCR0 + EPCCR_STRIDE * 14, 0x92840000},
+ {EPCCR0 + EPCCR_STRIDE * 15, 0x92840000},
+ /* EPSMCR (Event Processor SCU Mux Control Registers) */
+ {EPSMCR0 + EPSMCR_STRIDE * 0, 0},
+ {EPSMCR0 + EPSMCR_STRIDE * 1, 0},
+ {EPSMCR0 + EPSMCR_STRIDE * 2, 0x6C700000},
+ {EPSMCR0 + EPSMCR_STRIDE * 3, 0x2F000000},
+ {EPSMCR0 + EPSMCR_STRIDE * 4, 0x002F0000},
+ {EPSMCR0 + EPSMCR_STRIDE * 5, 0x00002E00},
+ {EPSMCR0 + EPSMCR_STRIDE * 6, 0x7C000000},
+ {EPSMCR0 + EPSMCR_STRIDE * 7, 0x30000000},
+ {EPSMCR0 + EPSMCR_STRIDE * 8, 0x64300000},
+ {EPSMCR0 + EPSMCR_STRIDE * 9, 0x00003000},
+ {EPSMCR0 + EPSMCR_STRIDE * 10, 0x65000030},
+ {EPSMCR0 + EPSMCR_STRIDE * 11, 0x31740000},
+ {EPSMCR0 + EPSMCR_STRIDE * 12, 0x7F000000},
+ {EPSMCR0 + EPSMCR_STRIDE * 13, 0x00003100},
+ {EPSMCR0 + EPSMCR_STRIDE * 14, 0x00000031},
+ {EPSMCR0 + EPSMCR_STRIDE * 15, 0x76000000},
+ /* EPACR (Event Processor Action Control Registers) */
+ {EPACR0 + EPACR_STRIDE * 0, 0},
+ {EPACR0 + EPACR_STRIDE * 1, 0},
+ {EPACR0 + EPACR_STRIDE * 2, 0},
+ {EPACR0 + EPACR_STRIDE * 3, 0x00000080},
+ {EPACR0 + EPACR_STRIDE * 4, 0},
+ {EPACR0 + EPACR_STRIDE * 5, 0x00000040},
+ {EPACR0 + EPACR_STRIDE * 6, 0},
+ {EPACR0 + EPACR_STRIDE * 7, 0},
+ {EPACR0 + EPACR_STRIDE * 8, 0},
+ {EPACR0 + EPACR_STRIDE * 9, 0x0000001C},
+ {EPACR0 + EPACR_STRIDE * 10, 0x00000020},
+ {EPACR0 + EPACR_STRIDE * 11, 0},
+ {EPACR0 + EPACR_STRIDE * 12, 0x00000003},
+ {EPACR0 + EPACR_STRIDE * 13, 0x06000000},
+ {EPACR0 + EPACR_STRIDE * 14, 0x04000000},
+ {EPACR0 + EPACR_STRIDE * 15, 0x02000000},
+ /* EPIMCR (Event Processor Input Mux Control Registers) */
+ {EPIMCR0 + EPIMCR_STRIDE * 0, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 1, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 2, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 3, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 4, 0x44000000},
+ {EPIMCR0 + EPIMCR_STRIDE * 5, 0x40000000},
+ {EPIMCR0 + EPIMCR_STRIDE * 6, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 7, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 8, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 9, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 10, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 11, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 12, 0x44000000},
+ {EPIMCR0 + EPIMCR_STRIDE * 13, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 14, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 15, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 16, 0x6A000000},
+ {EPIMCR0 + EPIMCR_STRIDE * 17, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 18, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 19, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 20, 0x48000000},
+ {EPIMCR0 + EPIMCR_STRIDE * 21, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 22, 0x6C000000},
+ {EPIMCR0 + EPIMCR_STRIDE * 23, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 24, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 25, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 26, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 27, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 28, 0x76000000},
+ {EPIMCR0 + EPIMCR_STRIDE * 29, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 30, 0},
+ {EPIMCR0 + EPIMCR_STRIDE * 31, 0x76000000},
+ /* EPXTRIGCR (Event Processor Crosstrigger Control Register) */
+ {EPXTRIGCR, 0x0000FFDF},
+ /* end */
+ {FSM_END_FLAG, 0},
+};
+
+struct fsm_reg_vals npc_default_val[] = {
+ /* NPC triggered Memory-Mapped Access Registers */
+ {NCR, 0x80000000},
+ {MCCR1, 0},
+ {MCSR1, 0},
+ {MMAR1LO, 0},
+ {MMAR1HI, 0},
+ {MMDR1, 0},
+ {MCSR2, 0},
+ {MMAR2LO, 0},
+ {MMAR2HI, 0},
+ {MMDR2, 0},
+ {MCSR3, 0x80000000},
+ {MMAR3LO, 0x000E2130},
+ {MMAR3HI, 0x00030000},
+ {MMDR3, 0x00020000},
+ /* end */
+ {FSM_END_FLAG, 0},
+};
+
+/**
+ * fsl_fsm_setup - Configure EPU's FSM registers
+ * @base: the base address of registers
+ * @val: Pointer to address-value pairs for FSM registers
+ */
+void fsl_fsm_setup(void __iomem *base, struct fsm_reg_vals *val)
+{
+ struct fsm_reg_vals *data = val;
+
+ BUG_ON(!base || !data);
+ while (data->offset != FSM_END_FLAG) {
+ iowrite32be(data->value, base + data->offset);
+ data++;
+ }
+}
+
+void fsl_epu_setup_default(void __iomem *epu_base)
+{
+ fsl_fsm_setup(epu_base, epu_default_val);
+}
+
+void fsl_npc_setup_default(void __iomem *npc_base)
+{
+ fsl_fsm_setup(npc_base, npc_default_val);
+}
+
+void fsl_epu_clean_default(void __iomem *epu_base)
+{
+ u32 offset;
+
+ /* follow the exact sequence to clear the registers */
+ /* Clear EPACRn */
+ for (offset = EPACR0; offset <= EPACR15; offset += EPACR_STRIDE)
+ iowrite32be(0, epu_base + offset);
+
+ /* Clear EPEVTCRn */
+ for (offset = EPEVTCR0; offset <= EPEVTCR9; offset += EPEVTCR_STRIDE)
+ iowrite32be(0, epu_base + offset);
+
+ /* Clear EPGCR */
+ iowrite32be(0, epu_base + EPGCR);
+
+ /* Clear EPSMCRn */
+ for (offset = EPSMCR0; offset <= EPSMCR15; offset += EPSMCR_STRIDE)
+ iowrite32be(0, epu_base + offset);
+
+ /* Clear EPCCRn */
+ for (offset = EPCCR0; offset <= EPCCR31; offset += EPCCR_STRIDE)
+ iowrite32be(0, epu_base + offset);
+
+ /* Clear EPCMPRn */
+ for (offset = EPCMPR0; offset <= EPCMPR31; offset += EPCMPR_STRIDE)
+ iowrite32be(0, epu_base + offset);
+
+ /* Clear EPCTRn */
+ for (offset = EPCTR0; offset <= EPCTR31; offset += EPCTR_STRIDE)
+ iowrite32be(0, epu_base + offset);
+
+ /* Clear EPIMCRn */
+ for (offset = EPIMCR0; offset <= EPIMCR31; offset += EPIMCR_STRIDE)
+ iowrite32be(0, epu_base + offset);
+
+ /* Clear EPXTRIGCRn */
+ iowrite32be(0, epu_base + EPXTRIGCR);
+
+ /* Clear EPECRn */
+ for (offset = EPECR0; offset <= EPECR15; offset += EPECR_STRIDE)
+ iowrite32be(0, epu_base + offset);
+}
diff --git a/arch/powerpc/platforms/85xx/sleep_fsm.h b/arch/powerpc/platforms/85xx/sleep_fsm.h
new file mode 100644
index 0000000..2c60b40
--- /dev/null
+++ b/arch/powerpc/platforms/85xx/sleep_fsm.h
@@ -0,0 +1,104 @@
+/*
+ * Freescale deep sleep FSM (finite-state machine) configuration
+ *
+ * Copyright 2015 Freescale Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+#ifndef _FSL_SLEEP_FSM_H
+#define _FSL_SLEEP_FSM_H
+
+#define FSL_STRIDE_4B 4
+#define FSL_STRIDE_8B 8
+
+/* End flag */
+#define FSM_END_FLAG 0xFFFFFFFFUL
+
+/* Block offsets */
+#define RCPM_BLOCK_OFFSET 0x00022000
+#define EPU_BLOCK_OFFSET 0x00000000
+#define NPC_BLOCK_OFFSET 0x00001000
+
+/* EPGCR (Event Processor Global Control Register) */
+#define EPGCR 0x000
+
+/* EPEVTCR0-9 (Event Processor EVT Pin Control Registers) */
+#define EPEVTCR0 0x050
+#define EPEVTCR9 0x074
+#define EPEVTCR_STRIDE FSL_STRIDE_4B
+
+/* EPXTRIGCR (Event Processor Crosstrigger Control Register) */
+#define EPXTRIGCR 0x090
+
+/* EPIMCR0-31 (Event Processor Input Mux Control Registers) */
+#define EPIMCR0 0x100
+#define EPIMCR31 0x17C
+#define EPIMCR_STRIDE FSL_STRIDE_4B
+
+/* EPSMCR0-15 (Event Processor SCU Mux Control Registers) */
+#define EPSMCR0 0x200
+#define EPSMCR15 0x278
+#define EPSMCR_STRIDE FSL_STRIDE_8B
+
+/* EPECR0-15 (Event Processor Event Control Registers) */
+#define EPECR0 0x300
+#define EPECR15 0x33C
+#define EPECR_STRIDE FSL_STRIDE_4B
+
+/* EPACR0-15 (Event Processor Action Control Registers) */
+#define EPACR0 0x400
+#define EPACR15 0x43C
+#define EPACR_STRIDE FSL_STRIDE_4B
+
+/* EPCCRi0-15 (Event Processor Counter Control Registers) */
+#define EPCCR0 0x800
+#define EPCCR15 0x83C
+#define EPCCR31 0x87C
+#define EPCCR_STRIDE FSL_STRIDE_4B
+
+/* EPCMPR0-15 (Event Processor Counter Compare Registers) */
+#define EPCMPR0 0x900
+#define EPCMPR15 0x93C
+#define EPCMPR31 0x97C
+#define EPCMPR_STRIDE FSL_STRIDE_4B
+
+/* EPCTR0-31 (Event Processor Counter Register) */
+#define EPCTR0 0xA00
+#define EPCTR31 0xA7C
+#define EPCTR_STRIDE FSL_STRIDE_4B
+
+/* NPC triggered Memory-Mapped Access Registers */
+#define NCR 0x000
+#define MCCR1 0x0CC
+#define MCSR1 0x0D0
+#define MMAR1LO 0x0D4
+#define MMAR1HI 0x0D8
+#define MMDR1 0x0DC
+#define MCSR2 0x0E0
+#define MMAR2LO 0x0E4
+#define MMAR2HI 0x0E8
+#define MMDR2 0x0EC
+#define MCSR3 0x0F0
+#define MMAR3LO 0x0F4
+#define MMAR3HI 0x0F8
+#define MMDR3 0x0FC
+
+/* RCPM Core State Action Control Register 0 */
+#define CSTTACR0 0xB00
+
+/* RCPM Core Group 1 Configuration Register 0 */
+#define CG1CR0 0x31C
+
+struct fsm_reg_vals {
+ u32 offset;
+ u32 value;
+};
+
+void fsl_epu_setup_default(void __iomem *epu_base);
+void fsl_npc_setup_default(void __iomem *npc_base);
+void fsl_epu_clean_default(void __iomem *epu_base);
+
+#endif /* _FSL_SLEEP_FSM_H */
--
1.9.1

2015-07-31 12:53:36

by Chenhui Zhao

[permalink] [raw]
Subject: [PATCH 4/4] powerpc: pm: support deep sleep feature on T104x

T104x has deep sleep feature, which can switch off most parts of
the SoC when it is in deep sleep mode. This way, it becomes more
energy-efficient.

The DDR controller will also be powered off in deep sleep. Therefore,
the last stage (the latter part of fsl_dp_enter_low) will run without DDR
access. This piece of code and related TLBs are prefetched in advance.

Due to the different initialization code between 32-bit and 64-bit, they
have separate resume entry and precedure.

The feature supports 32-bit and 64-bit kernel mode.

Signed-off-by: Chenhui Zhao <[email protected]>
---
arch/powerpc/include/asm/fsl_pm.h | 14 +
arch/powerpc/kernel/fsl_booke_entry_mapping.S | 10 +
arch/powerpc/kernel/head_64.S | 2 +-
arch/powerpc/platforms/85xx/Makefile | 1 +
arch/powerpc/platforms/85xx/deepsleep.c | 322 +++++++++++++++
arch/powerpc/platforms/85xx/qoriq_pm.c | 81 +++-
arch/powerpc/platforms/85xx/t104x_deepsleep.S | 570 ++++++++++++++++++++++++++
7 files changed, 997 insertions(+), 3 deletions(-)
create mode 100644 arch/powerpc/platforms/85xx/deepsleep.c
create mode 100644 arch/powerpc/platforms/85xx/t104x_deepsleep.S

diff --git a/arch/powerpc/include/asm/fsl_pm.h b/arch/powerpc/include/asm/fsl_pm.h
index 4b09f09..b44f484 100644
--- a/arch/powerpc/include/asm/fsl_pm.h
+++ b/arch/powerpc/include/asm/fsl_pm.h
@@ -12,6 +12,7 @@
#define __PPC_FSL_PM_H
#ifdef __KERNEL__

+#ifndef __ASSEMBLY__
#define E500_PM_PH10 1
#define E500_PM_PH15 2
#define E500_PM_PH20 3
@@ -44,5 +45,18 @@ struct fsl_pm_ops {
};

extern const struct fsl_pm_ops *qoriq_pm_ops;
+
+extern int fsl_dp_iomap(void);
+extern void fsl_dp_iounmap(void);
+
+extern int fsl_enter_epu_deepsleep(void);
+extern void fsl_dp_enter_low(void __iomem *ccsr_base, void __iomem *dcsr_base,
+ void __iomem *pld_base, int pld_flag);
+extern void fsl_booke_deep_sleep_resume(void);
+#endif /* __ASSEMBLY__ */
+
+#define T1040QDS_TETRA_FLAG 1
+#define T104xRDB_CPLD_FLAG 2
+
#endif /* __KERNEL__ */
#endif /* __PPC_FSL_PM_H */
diff --git a/arch/powerpc/kernel/fsl_booke_entry_mapping.S b/arch/powerpc/kernel/fsl_booke_entry_mapping.S
index f22e7e4..32ec426f 100644
--- a/arch/powerpc/kernel/fsl_booke_entry_mapping.S
+++ b/arch/powerpc/kernel/fsl_booke_entry_mapping.S
@@ -170,6 +170,10 @@ skpinv: addi r6,r6,1 /* Increment */
lis r6,MAS2_VAL(PAGE_OFFSET, BOOK3E_PAGESZ_64M, M_IF_SMP)@h
ori r6,r6,MAS2_VAL(PAGE_OFFSET, BOOK3E_PAGESZ_64M, M_IF_SMP)@l
mtspr SPRN_MAS2,r6
+#ifdef ENTRY_DEEPSLEEP_SETUP
+ LOAD_REG_IMMEDIATE(r8, MEMORY_START)
+ ori r8,r8,(MAS3_SX|MAS3_SW|MAS3_SR)
+#endif
mtspr SPRN_MAS3,r8
tlbwe

@@ -212,12 +216,18 @@ next_tlb_setup:
#error You need to specify the mapping or not use this at all.
#endif

+#ifdef ENTRY_DEEPSLEEP_SETUP
+ LOAD_REG_ADDR(r6, 2f)
+ mfmsr r7
+ rlwinm r7,r7,0,~(MSR_IS|MSR_DS)
+#else
lis r7,MSR_KERNEL@h
ori r7,r7,MSR_KERNEL@l
bl 1f /* Find our address */
1: mflr r9
rlwimi r6,r9,0,20,31
addi r6,r6,(2f - 1b)
+#endif
mtspr SPRN_SRR0,r6
mtspr SPRN_SRR1,r7
rfi /* start execution out of TLB1[0] entry */
diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S
index d48125d..b9eb02a 100644
--- a/arch/powerpc/kernel/head_64.S
+++ b/arch/powerpc/kernel/head_64.S
@@ -696,7 +696,7 @@ _GLOBAL(start_secondary_resume)
/*
* This subroutine clobbers r11 and r12
*/
-enable_64b_mode:
+_GLOBAL(enable_64b_mode)
mfmsr r11 /* grab the current MSR */
#ifdef CONFIG_PPC_BOOK3E
oris r11,r11,0x8000 /* CM bit set, we'll set ICM later */
diff --git a/arch/powerpc/platforms/85xx/Makefile b/arch/powerpc/platforms/85xx/Makefile
index 87fb847..a73d563 100644
--- a/arch/powerpc/platforms/85xx/Makefile
+++ b/arch/powerpc/platforms/85xx/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_FSL_PMC) += mpc85xx_pm_ops.o
obj-$(CONFIG_FSL_QORIQ_PM) += qoriq_pm.o sleep_fsm.o
+obj-$(CONFIG_FSL_QORIQ_PM) += deepsleep.o t104x_deepsleep.o

obj-y += common.o

diff --git a/arch/powerpc/platforms/85xx/deepsleep.c b/arch/powerpc/platforms/85xx/deepsleep.c
new file mode 100644
index 0000000..5de904d
--- /dev/null
+++ b/arch/powerpc/platforms/85xx/deepsleep.c
@@ -0,0 +1,322 @@
+/*
+ * Support deep sleep feature for T104x
+ *
+ * Copyright 2015 Freescale Semiconductor Inc.
+ *
+ * Author: Chenhui Zhao <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <sysdev/fsl_soc.h>
+#include <asm/machdep.h>
+#include <asm/fsl_pm.h>
+
+#include "sleep_fsm.h"
+
+#define SIZE_1MB 0x100000
+#define SIZE_2MB 0x200000
+
+#define CPC_CPCHDBCR0 0x10f00
+#define CPC_CPCHDBCR0_SPEC_DIS 0x08000000
+
+#define CCSR_SCFG_DPSLPCR 0xfc000
+#define CCSR_SCFG_DPSLPCR_WDRR_EN 0x1
+#define CCSR_SCFG_SPARECR2 0xfc504
+#define CCSR_SCFG_SPARECR3 0xfc508
+
+#define CCSR_GPIO1_GPDIR 0x130000
+#define CCSR_GPIO1_GPODR 0x130004
+#define CCSR_GPIO1_GPDAT 0x130008
+#define CCSR_GPIO1_GPDIR_29 0x4
+
+#define CCSR_LAW_BASE 0xC00
+#define DCFG_BRR 0xE4 /* boot release register */
+#define LCC_BSTRH 0x20 /* Boot space translation register high */
+#define LCC_BSTRL 0x24 /* Boot space translation register low */
+#define LCC_BSTAR 0x28 /* Boot space translation attribute register */
+#define RCPM_PCTBENR 0x1A0 /* Physical Core Timebase Enable Register */
+#define RCPM_BASE 0xE2000
+#define DCFG_BASE 0xE0000
+
+/* 128 bytes buffer for restoring data broke by DDR training initialization */
+#define DDR_BUF_SIZE 128
+static u8 ddr_buff[DDR_BUF_SIZE] __aligned(64);
+
+static void *dcsr_base, *ccsr_base, *pld_base;
+static int pld_flag;
+
+/* save LAW registers */
+struct fsl_law {
+ u32 lawbarh; /* LAWn base address high */
+ u32 lawbarl; /* LAWn base address low */
+ u32 lawar; /* LAWn attributes */
+ u32 reserved;
+};
+
+struct fsl_law *saved_law;
+static u32 num_laws;
+
+/* for nonboot cpu */
+struct fsl_bstr {
+ u32 bstrh;
+ u32 bstrl;
+ u32 bstar;
+ u32 cpu_mask;
+};
+static struct fsl_bstr saved_bstr;
+
+int fsl_dp_iomap(void)
+{
+ struct device_node *np;
+ int ret = 0;
+ phys_addr_t ccsr_phy_addr, dcsr_phy_addr;
+
+ saved_law = NULL;
+ ccsr_base = NULL;
+ dcsr_base = NULL;
+ pld_base = NULL;
+
+ ccsr_phy_addr = get_immrbase();
+ if (ccsr_phy_addr == -1) {
+ pr_err("%s: Can't get the address of CCSR\n", __func__);
+ ret = -EINVAL;
+ goto ccsr_err;
+ }
+ ccsr_base = ioremap(ccsr_phy_addr, SIZE_2MB);
+ if (!ccsr_base) {
+ ret = -ENOMEM;
+ goto ccsr_err;
+ }
+
+ dcsr_phy_addr = get_dcsrbase();
+ if (dcsr_phy_addr == -1) {
+ pr_err("%s: Can't get the address of DCSR\n", __func__);
+ ret = -EINVAL;
+ goto dcsr_err;
+ }
+ dcsr_base = ioremap(dcsr_phy_addr, SIZE_1MB);
+ if (!dcsr_base) {
+ ret = -ENOMEM;
+ goto dcsr_err;
+ }
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,tetra-fpga");
+ if (np) {
+ pld_flag = T1040QDS_TETRA_FLAG;
+ } else {
+ np = of_find_compatible_node(NULL, NULL, "fsl,deepsleep-cpld");
+ if (np) {
+ pld_flag = T104xRDB_CPLD_FLAG;
+ } else {
+ pr_err("%s: Can't find the FPGA/CPLD node\n",
+ __func__);
+ ret = -EINVAL;
+ goto pld_err;
+ }
+ }
+ pld_base = of_iomap(np, 0);
+ of_node_put(np);
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,corenet-law");
+ if (!np) {
+ pr_err("%s: Can't find the node of \"law\"\n", __func__);
+ ret = -EINVAL;
+ goto alloc_err;
+ }
+ ret = of_property_read_u32(np, "fsl,num-laws", &num_laws);
+ if (ret) {
+ ret = -EINVAL;
+ goto alloc_err;
+ }
+
+ saved_law = kcalloc(num_laws, sizeof(struct fsl_law), GFP_KERNEL);
+ if (!saved_law) {
+ ret = -ENOMEM;
+ goto alloc_err;
+ }
+ of_node_put(np);
+
+ return 0;
+
+alloc_err:
+ iounmap(pld_base);
+ pld_base = NULL;
+pld_err:
+ iounmap(dcsr_base);
+ dcsr_base = NULL;
+dcsr_err:
+ iounmap(ccsr_base);
+ ccsr_base = NULL;
+ccsr_err:
+ return ret;
+}
+
+void fsl_dp_iounmap(void)
+{
+ if (dcsr_base) {
+ iounmap(dcsr_base);
+ dcsr_base = NULL;
+ }
+
+ if (ccsr_base) {
+ iounmap(ccsr_base);
+ ccsr_base = NULL;
+ }
+
+ if (pld_base) {
+ iounmap(pld_base);
+ pld_base = NULL;
+ }
+
+ kfree(saved_law);
+ saved_law = NULL;
+}
+
+static void fsl_dp_ddr_save(void *ccsr_base)
+{
+ u32 ddr_buff_addr;
+
+ /*
+ * DDR training initialization will break 128 bytes at the beginning
+ * of DDR, therefore, save them so that the bootloader will restore
+ * them. Assume that DDR is mapped to the address space started with
+ * CONFIG_PAGE_OFFSET.
+ */
+ memcpy(ddr_buff, (void *)CONFIG_PAGE_OFFSET, DDR_BUF_SIZE);
+
+ /* assume ddr_buff is in the physical address space of 4GB */
+ ddr_buff_addr = (u32)(__pa(ddr_buff) & 0xffffffff);
+
+ /*
+ * the bootloader will restore the first 128 bytes of DDR from
+ * the location indicated by the register SPARECR3
+ */
+ out_be32(ccsr_base + CCSR_SCFG_SPARECR3, ddr_buff_addr);
+}
+
+static void fsl_dp_mp_save(void *ccsr)
+{
+ struct fsl_bstr *dst = &saved_bstr;
+
+ dst->bstrh = in_be32(ccsr + LCC_BSTRH);
+ dst->bstrl = in_be32(ccsr + LCC_BSTRL);
+ dst->bstar = in_be32(ccsr + LCC_BSTAR);
+ dst->cpu_mask = in_be32(ccsr + DCFG_BASE + DCFG_BRR);
+}
+
+static void fsl_dp_mp_restore(void *ccsr)
+{
+ struct fsl_bstr *src = &saved_bstr;
+
+ out_be32(ccsr + LCC_BSTRH, src->bstrh);
+ out_be32(ccsr + LCC_BSTRL, src->bstrl);
+ out_be32(ccsr + LCC_BSTAR, src->bstar);
+
+ /* release the nonboot cpus */
+ out_be32(ccsr + DCFG_BASE + DCFG_BRR, src->cpu_mask);
+
+ /* enable the time base */
+ out_be32(ccsr + RCPM_BASE + RCPM_PCTBENR, src->cpu_mask);
+ /* read back to sync write */
+ in_be32(ccsr + RCPM_BASE + RCPM_PCTBENR);
+}
+
+static void fsl_dp_law_save(void *ccsr)
+{
+ int i;
+ struct fsl_law *dst = saved_law;
+ struct fsl_law *src = (void *)(ccsr + CCSR_LAW_BASE);
+
+ for (i = 0; i < num_laws; i++) {
+ dst->lawbarh = in_be32(&src->lawbarh);
+ dst->lawbarl = in_be32(&src->lawbarl);
+ dst->lawar = in_be32(&src->lawar);
+ dst++;
+ src++;
+ }
+}
+
+static void fsl_dp_law_restore(void *ccsr)
+{
+ int i;
+ struct fsl_law *src = saved_law;
+ struct fsl_law *dst = (void *)(ccsr + CCSR_LAW_BASE);
+
+ for (i = 0; i < num_laws - 1; i++) {
+ out_be32(&dst->lawar, 0);
+ out_be32(&dst->lawbarl, src->lawbarl);
+ out_be32(&dst->lawbarh, src->lawbarh);
+ out_be32(&dst->lawar, src->lawar);
+
+ /* Read back so that we sync the writes */
+ in_be32(&dst->lawar);
+ src++;
+ dst++;
+ }
+}
+
+static void fsl_dp_set_resume_pointer(void *ccsr_base)
+{
+ u32 resume_addr;
+
+ /* the bootloader will finally jump to this address to return kernel */
+#ifdef CONFIG_PPC32
+ resume_addr = (u32)(__pa(fsl_booke_deep_sleep_resume));
+#else
+ resume_addr = (u32)(__pa(*(u64 *)fsl_booke_deep_sleep_resume)
+ & 0xffffffff);
+#endif
+
+ /* use the register SPARECR2 to save the resume address */
+ out_be32(ccsr_base + CCSR_SCFG_SPARECR2, resume_addr);
+}
+
+int fsl_enter_epu_deepsleep(void)
+{
+ fsl_dp_ddr_save(ccsr_base);
+
+ fsl_dp_set_resume_pointer(ccsr_base);
+
+ fsl_dp_mp_save(ccsr_base);
+ fsl_dp_law_save(ccsr_base);
+ /* enable Warm Device Reset request. */
+ setbits32(ccsr_base + CCSR_SCFG_DPSLPCR, CCSR_SCFG_DPSLPCR_WDRR_EN);
+
+ /* set GPIO1_29 as an output pin (not open-drain), and output 0 */
+ clrbits32(ccsr_base + CCSR_GPIO1_GPDAT, CCSR_GPIO1_GPDIR_29);
+ clrbits32(ccsr_base + CCSR_GPIO1_GPODR, CCSR_GPIO1_GPDIR_29);
+ setbits32(ccsr_base + CCSR_GPIO1_GPDIR, CCSR_GPIO1_GPDIR_29);
+
+ /*
+ * Disable CPC speculation to avoid deep sleep hang, especially
+ * in secure boot mode. This bit will be cleared automatically
+ * when resuming from deep sleep.
+ */
+ setbits32(ccsr_base + CPC_CPCHDBCR0, CPC_CPCHDBCR0_SPEC_DIS);
+
+ fsl_epu_setup_default(dcsr_base + EPU_BLOCK_OFFSET);
+ fsl_npc_setup_default(dcsr_base + NPC_BLOCK_OFFSET);
+
+ out_be32(dcsr_base + RCPM_BLOCK_OFFSET + CSTTACR0, 0x00001001);
+ out_be32(dcsr_base + RCPM_BLOCK_OFFSET + CG1CR0, 0x00000001);
+
+ fsl_dp_enter_low(ccsr_base, dcsr_base, pld_base, pld_flag);
+
+ fsl_dp_law_restore(ccsr_base);
+ fsl_dp_mp_restore(ccsr_base);
+
+ /* disable Warm Device Reset request */
+ clrbits32(ccsr_base + CCSR_SCFG_DPSLPCR, CCSR_SCFG_DPSLPCR_WDRR_EN);
+
+ fsl_epu_clean_default(dcsr_base + EPU_BLOCK_OFFSET);
+
+ return 0;
+}
diff --git a/arch/powerpc/platforms/85xx/qoriq_pm.c b/arch/powerpc/platforms/85xx/qoriq_pm.c
index 27ec337..f65f6cf 100644
--- a/arch/powerpc/platforms/85xx/qoriq_pm.c
+++ b/arch/powerpc/platforms/85xx/qoriq_pm.c
@@ -17,17 +17,68 @@

#include <asm/fsl_pm.h>

+static suspend_state_t cur_pm_state;
+
+/**
+ * fsl_set_power_except - set which IP block is not powerdown when sleep or
+ * deep sleep, such as MAC, USB, etc.
+ *
+ * @dev: a pointer to the struct device of the device with wakeup capability
+ * @on: if 1, do not power down; if 0, power down.
+ */
+static void fsl_set_power_except(struct device *dev, int on)
+{
+ u32 value[2];
+ int ret;
+
+ ret = of_property_read_u32_array(dev->of_node, "sleep", value, 2);
+ if (ret)
+ goto out;
+
+ /* get value[1], it is a bit mask */
+ qoriq_pm_ops->set_ip_power(on, &value[1]);
+
+ return;
+out:
+ dev_dbg(dev, "Can not set as wakeup sources\n");
+}
+
+static void qoriq_set_wakeup_source(struct device *dev, void *enable)
+{
+ if (!device_may_wakeup(dev))
+ return;
+
+ fsl_set_power_except(dev, *((int *)enable));
+}
+
static int qoriq_suspend_enter(suspend_state_t state)
{
int ret = 0;
+ int cpu;

switch (state) {
case PM_SUSPEND_STANDBY:
+
cur_cpu_spec->cpu_down_flush();
+
ret = qoriq_pm_ops->plat_enter_sleep();
+
+ break;
+
+ case PM_SUSPEND_MEM:
+
+ cpu = smp_processor_id();
+ qoriq_pm_ops->irq_mask(cpu);
+
+ ret = fsl_enter_epu_deepsleep();
+
+ qoriq_pm_ops->irq_unmask(cpu);
+
break;
+
default:
ret = -EINVAL;
+
}

return ret;
@@ -38,22 +89,48 @@ static int qoriq_suspend_valid(suspend_state_t state)
unsigned int pm_modes;

pm_modes = qoriq_pm_ops->get_pm_modes();
+ if (state == PM_SUSPEND_STANDBY && (pm_modes & FSL_PM_SLEEP))
+ return 1;

- if ((state == PM_SUSPEND_STANDBY) && (pm_modes & FSL_PM_SLEEP))
+ if (state == PM_SUSPEND_MEM && (pm_modes & FSL_PM_DEEP_SLEEP))
return 1;

return 0;
}

+static int qoriq_suspend_begin(suspend_state_t state)
+{
+ const int enable = 1;
+
+ cur_pm_state = state;
+ dpm_for_each_dev((void *)&enable, qoriq_set_wakeup_source);
+
+ if (cur_pm_state == PM_SUSPEND_MEM)
+ return fsl_dp_iomap();
+
+ return 0;
+}
+
+static void qoriq_suspend_end(void)
+{
+ const int enable = 0;
+
+ dpm_for_each_dev((void *)&enable, qoriq_set_wakeup_source);
+
+ if (cur_pm_state == PM_SUSPEND_MEM)
+ fsl_dp_iounmap();
+}
+
static const struct platform_suspend_ops qoriq_suspend_ops = {
.valid = qoriq_suspend_valid,
.enter = qoriq_suspend_enter,
+ .begin = qoriq_suspend_begin,
+ .end = qoriq_suspend_end,
};

static int __init qoriq_suspend_init(void)
{
suspend_set_ops(&qoriq_suspend_ops);
-
return 0;
}
arch_initcall(qoriq_suspend_init);
diff --git a/arch/powerpc/platforms/85xx/t104x_deepsleep.S b/arch/powerpc/platforms/85xx/t104x_deepsleep.S
new file mode 100644
index 0000000..773a9e4
--- /dev/null
+++ b/arch/powerpc/platforms/85xx/t104x_deepsleep.S
@@ -0,0 +1,570 @@
+/*
+ * Enter and resume from deep sleep state
+ *
+ * Copyright 2015 Freescale Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <asm/page.h>
+#include <asm/ppc_asm.h>
+#include <asm/reg.h>
+#include <asm/asm-offsets.h>
+#include <asm/fsl_pm.h>
+#include <asm/mmu.h>
+
+/*
+ * the number of bytes occupied by one register
+ * the value of 8 is compatible with both 32-bit and 64-bit registers
+ */
+#define STRIDE_SIZE 8
+
+/* GPR0 - GPR31 */
+#define BOOKE_GPR0_OFF 0x0000
+#define BOOKE_GPR_COUNT 32
+/* IVOR0 - IVOR42 */
+#define BOOKE_IVOR0_OFF (BOOKE_GPR0_OFF + BOOKE_GPR_COUNT * STRIDE_SIZE)
+#define BOOKE_IVOR_COUNT 43
+/* SPRG0 - SPRG9 */
+#define BOOKE_SPRG0_OFF (BOOKE_IVOR0_OFF + BOOKE_IVOR_COUNT * STRIDE_SIZE)
+#define BOOKE_SPRG_COUNT 10
+/* IVPR */
+#define BOOKE_IVPR_OFF (BOOKE_SPRG0_OFF + BOOKE_SPRG_COUNT * STRIDE_SIZE)
+
+#define BOOKE_LR_OFF (BOOKE_IVPR_OFF + STRIDE_SIZE)
+#define BOOKE_MSR_OFF (BOOKE_LR_OFF + STRIDE_SIZE)
+#define BOOKE_TBU_OFF (BOOKE_MSR_OFF + STRIDE_SIZE)
+#define BOOKE_TBL_OFF (BOOKE_TBU_OFF + STRIDE_SIZE)
+#define BOOKE_EPCR_OFF (BOOKE_TBL_OFF + STRIDE_SIZE)
+#define BOOKE_HID0_OFF (BOOKE_EPCR_OFF + STRIDE_SIZE)
+#define BOOKE_PIR_OFF (BOOKE_HID0_OFF + STRIDE_SIZE)
+#define BOOKE_PID0_OFF (BOOKE_PIR_OFF + STRIDE_SIZE)
+#define BOOKE_BUCSR_OFF (BOOKE_PID0_OFF + STRIDE_SIZE)
+
+#define BUFFER_SIZE (BOOKE_BUCSR_OFF + STRIDE_SIZE)
+
+#undef SAVE_GPR
+#define SAVE_GPR(gpr, offset) \
+ PPC_STL gpr, offset(r10)
+
+#define RESTORE_GPR(gpr, offset) \
+ PPC_LL gpr, offset(r10)
+
+#define SAVE_SPR(spr, offset) \
+ mfspr r0, spr ;\
+ PPC_STL r0, offset(r10)
+
+#define RESTORE_SPR(spr, offset) \
+ PPC_LL r0, offset(r10) ;\
+ mtspr spr, r0
+
+#define SAVE_ALL_GPR \
+ SAVE_GPR(r1, BOOKE_GPR0_OFF + STRIDE_SIZE * 1) ;\
+ SAVE_GPR(r2, BOOKE_GPR0_OFF + STRIDE_SIZE * 2) ;\
+ SAVE_GPR(r13, BOOKE_GPR0_OFF + STRIDE_SIZE * 13) ;\
+ SAVE_GPR(r14, BOOKE_GPR0_OFF + STRIDE_SIZE * 14) ;\
+ SAVE_GPR(r15, BOOKE_GPR0_OFF + STRIDE_SIZE * 15) ;\
+ SAVE_GPR(r16, BOOKE_GPR0_OFF + STRIDE_SIZE * 16) ;\
+ SAVE_GPR(r17, BOOKE_GPR0_OFF + STRIDE_SIZE * 17) ;\
+ SAVE_GPR(r18, BOOKE_GPR0_OFF + STRIDE_SIZE * 18) ;\
+ SAVE_GPR(r19, BOOKE_GPR0_OFF + STRIDE_SIZE * 19) ;\
+ SAVE_GPR(r20, BOOKE_GPR0_OFF + STRIDE_SIZE * 20) ;\
+ SAVE_GPR(r21, BOOKE_GPR0_OFF + STRIDE_SIZE * 21) ;\
+ SAVE_GPR(r22, BOOKE_GPR0_OFF + STRIDE_SIZE * 22) ;\
+ SAVE_GPR(r23, BOOKE_GPR0_OFF + STRIDE_SIZE * 23) ;\
+ SAVE_GPR(r24, BOOKE_GPR0_OFF + STRIDE_SIZE * 24) ;\
+ SAVE_GPR(r25, BOOKE_GPR0_OFF + STRIDE_SIZE * 25) ;\
+ SAVE_GPR(r26, BOOKE_GPR0_OFF + STRIDE_SIZE * 26) ;\
+ SAVE_GPR(r27, BOOKE_GPR0_OFF + STRIDE_SIZE * 27) ;\
+ SAVE_GPR(r28, BOOKE_GPR0_OFF + STRIDE_SIZE * 28) ;\
+ SAVE_GPR(r29, BOOKE_GPR0_OFF + STRIDE_SIZE * 29) ;\
+ SAVE_GPR(r30, BOOKE_GPR0_OFF + STRIDE_SIZE * 30) ;\
+ SAVE_GPR(r31, BOOKE_GPR0_OFF + STRIDE_SIZE * 31)
+
+#define RESTORE_ALL_GPR \
+ RESTORE_GPR(r1, BOOKE_GPR0_OFF + STRIDE_SIZE * 1) ;\
+ RESTORE_GPR(r2, BOOKE_GPR0_OFF + STRIDE_SIZE * 2) ;\
+ RESTORE_GPR(r13, BOOKE_GPR0_OFF + STRIDE_SIZE * 13) ;\
+ RESTORE_GPR(r14, BOOKE_GPR0_OFF + STRIDE_SIZE * 14) ;\
+ RESTORE_GPR(r15, BOOKE_GPR0_OFF + STRIDE_SIZE * 15) ;\
+ RESTORE_GPR(r16, BOOKE_GPR0_OFF + STRIDE_SIZE * 16) ;\
+ RESTORE_GPR(r17, BOOKE_GPR0_OFF + STRIDE_SIZE * 17) ;\
+ RESTORE_GPR(r18, BOOKE_GPR0_OFF + STRIDE_SIZE * 18) ;\
+ RESTORE_GPR(r19, BOOKE_GPR0_OFF + STRIDE_SIZE * 19) ;\
+ RESTORE_GPR(r20, BOOKE_GPR0_OFF + STRIDE_SIZE * 20) ;\
+ RESTORE_GPR(r21, BOOKE_GPR0_OFF + STRIDE_SIZE * 21) ;\
+ RESTORE_GPR(r22, BOOKE_GPR0_OFF + STRIDE_SIZE * 22) ;\
+ RESTORE_GPR(r23, BOOKE_GPR0_OFF + STRIDE_SIZE * 23) ;\
+ RESTORE_GPR(r24, BOOKE_GPR0_OFF + STRIDE_SIZE * 24) ;\
+ RESTORE_GPR(r25, BOOKE_GPR0_OFF + STRIDE_SIZE * 25) ;\
+ RESTORE_GPR(r26, BOOKE_GPR0_OFF + STRIDE_SIZE * 26) ;\
+ RESTORE_GPR(r27, BOOKE_GPR0_OFF + STRIDE_SIZE * 27) ;\
+ RESTORE_GPR(r28, BOOKE_GPR0_OFF + STRIDE_SIZE * 28) ;\
+ RESTORE_GPR(r29, BOOKE_GPR0_OFF + STRIDE_SIZE * 29) ;\
+ RESTORE_GPR(r30, BOOKE_GPR0_OFF + STRIDE_SIZE * 30) ;\
+ RESTORE_GPR(r31, BOOKE_GPR0_OFF + STRIDE_SIZE * 31)
+
+#define SAVE_ALL_SPRG \
+ SAVE_SPR(SPRN_SPRG0, BOOKE_SPRG0_OFF + STRIDE_SIZE * 0) ;\
+ SAVE_SPR(SPRN_SPRG1, BOOKE_SPRG0_OFF + STRIDE_SIZE * 1) ;\
+ SAVE_SPR(SPRN_SPRG2, BOOKE_SPRG0_OFF + STRIDE_SIZE * 2) ;\
+ SAVE_SPR(SPRN_SPRG3, BOOKE_SPRG0_OFF + STRIDE_SIZE * 3) ;\
+ SAVE_SPR(SPRN_SPRG4, BOOKE_SPRG0_OFF + STRIDE_SIZE * 4) ;\
+ SAVE_SPR(SPRN_SPRG5, BOOKE_SPRG0_OFF + STRIDE_SIZE * 5) ;\
+ SAVE_SPR(SPRN_SPRG6, BOOKE_SPRG0_OFF + STRIDE_SIZE * 6) ;\
+ SAVE_SPR(SPRN_SPRG7, BOOKE_SPRG0_OFF + STRIDE_SIZE * 7) ;\
+ SAVE_SPR(SPRN_SPRG8, BOOKE_SPRG0_OFF + STRIDE_SIZE * 8) ;\
+ SAVE_SPR(SPRN_SPRG9, BOOKE_SPRG0_OFF + STRIDE_SIZE * 9)
+
+#define RESTORE_ALL_SPRG \
+ RESTORE_SPR(SPRN_SPRG0, BOOKE_SPRG0_OFF + STRIDE_SIZE * 0) ;\
+ RESTORE_SPR(SPRN_SPRG1, BOOKE_SPRG0_OFF + STRIDE_SIZE * 1) ;\
+ RESTORE_SPR(SPRN_SPRG2, BOOKE_SPRG0_OFF + STRIDE_SIZE * 2) ;\
+ RESTORE_SPR(SPRN_SPRG3, BOOKE_SPRG0_OFF + STRIDE_SIZE * 3) ;\
+ RESTORE_SPR(SPRN_SPRG4, BOOKE_SPRG0_OFF + STRIDE_SIZE * 4) ;\
+ RESTORE_SPR(SPRN_SPRG5, BOOKE_SPRG0_OFF + STRIDE_SIZE * 5) ;\
+ RESTORE_SPR(SPRN_SPRG6, BOOKE_SPRG0_OFF + STRIDE_SIZE * 6) ;\
+ RESTORE_SPR(SPRN_SPRG7, BOOKE_SPRG0_OFF + STRIDE_SIZE * 7) ;\
+ RESTORE_SPR(SPRN_SPRG8, BOOKE_SPRG0_OFF + STRIDE_SIZE * 8) ;\
+ RESTORE_SPR(SPRN_SPRG9, BOOKE_SPRG0_OFF + STRIDE_SIZE * 9)
+
+#define SAVE_ALL_IVOR \
+ SAVE_SPR(SPRN_IVOR0, BOOKE_IVOR0_OFF + STRIDE_SIZE * 0) ;\
+ SAVE_SPR(SPRN_IVOR1, BOOKE_IVOR0_OFF + STRIDE_SIZE * 1) ;\
+ SAVE_SPR(SPRN_IVOR2, BOOKE_IVOR0_OFF + STRIDE_SIZE * 2) ;\
+ SAVE_SPR(SPRN_IVOR3, BOOKE_IVOR0_OFF + STRIDE_SIZE * 3) ;\
+ SAVE_SPR(SPRN_IVOR4, BOOKE_IVOR0_OFF + STRIDE_SIZE * 4) ;\
+ SAVE_SPR(SPRN_IVOR5, BOOKE_IVOR0_OFF + STRIDE_SIZE * 5) ;\
+ SAVE_SPR(SPRN_IVOR6, BOOKE_IVOR0_OFF + STRIDE_SIZE * 6) ;\
+ SAVE_SPR(SPRN_IVOR7, BOOKE_IVOR0_OFF + STRIDE_SIZE * 7) ;\
+ SAVE_SPR(SPRN_IVOR8, BOOKE_IVOR0_OFF + STRIDE_SIZE * 8) ;\
+ SAVE_SPR(SPRN_IVOR9, BOOKE_IVOR0_OFF + STRIDE_SIZE * 9) ;\
+ SAVE_SPR(SPRN_IVOR10, BOOKE_IVOR0_OFF + STRIDE_SIZE * 10) ;\
+ SAVE_SPR(SPRN_IVOR11, BOOKE_IVOR0_OFF + STRIDE_SIZE * 11) ;\
+ SAVE_SPR(SPRN_IVOR12, BOOKE_IVOR0_OFF + STRIDE_SIZE * 12) ;\
+ SAVE_SPR(SPRN_IVOR13, BOOKE_IVOR0_OFF + STRIDE_SIZE * 13) ;\
+ SAVE_SPR(SPRN_IVOR14, BOOKE_IVOR0_OFF + STRIDE_SIZE * 14) ;\
+ SAVE_SPR(SPRN_IVOR15, BOOKE_IVOR0_OFF + STRIDE_SIZE * 15) ;\
+ SAVE_SPR(SPRN_IVOR35, BOOKE_IVOR0_OFF + STRIDE_SIZE * 35) ;\
+ SAVE_SPR(SPRN_IVOR36, BOOKE_IVOR0_OFF + STRIDE_SIZE * 36) ;\
+ SAVE_SPR(SPRN_IVOR37, BOOKE_IVOR0_OFF + STRIDE_SIZE * 37) ;\
+ SAVE_SPR(SPRN_IVOR38, BOOKE_IVOR0_OFF + STRIDE_SIZE * 38) ;\
+ SAVE_SPR(SPRN_IVOR39, BOOKE_IVOR0_OFF + STRIDE_SIZE * 39) ;\
+ SAVE_SPR(SPRN_IVOR40, BOOKE_IVOR0_OFF + STRIDE_SIZE * 40) ;\
+ SAVE_SPR(SPRN_IVOR41, BOOKE_IVOR0_OFF + STRIDE_SIZE * 41)
+
+#define RESTORE_ALL_IVOR \
+ RESTORE_SPR(SPRN_IVOR0, BOOKE_IVOR0_OFF + STRIDE_SIZE * 0) ;\
+ RESTORE_SPR(SPRN_IVOR1, BOOKE_IVOR0_OFF + STRIDE_SIZE * 1) ;\
+ RESTORE_SPR(SPRN_IVOR2, BOOKE_IVOR0_OFF + STRIDE_SIZE * 2) ;\
+ RESTORE_SPR(SPRN_IVOR3, BOOKE_IVOR0_OFF + STRIDE_SIZE * 3) ;\
+ RESTORE_SPR(SPRN_IVOR4, BOOKE_IVOR0_OFF + STRIDE_SIZE * 4) ;\
+ RESTORE_SPR(SPRN_IVOR5, BOOKE_IVOR0_OFF + STRIDE_SIZE * 5) ;\
+ RESTORE_SPR(SPRN_IVOR6, BOOKE_IVOR0_OFF + STRIDE_SIZE * 6) ;\
+ RESTORE_SPR(SPRN_IVOR7, BOOKE_IVOR0_OFF + STRIDE_SIZE * 7) ;\
+ RESTORE_SPR(SPRN_IVOR8, BOOKE_IVOR0_OFF + STRIDE_SIZE * 8) ;\
+ RESTORE_SPR(SPRN_IVOR9, BOOKE_IVOR0_OFF + STRIDE_SIZE * 9) ;\
+ RESTORE_SPR(SPRN_IVOR10, BOOKE_IVOR0_OFF + STRIDE_SIZE * 10) ;\
+ RESTORE_SPR(SPRN_IVOR11, BOOKE_IVOR0_OFF + STRIDE_SIZE * 11) ;\
+ RESTORE_SPR(SPRN_IVOR12, BOOKE_IVOR0_OFF + STRIDE_SIZE * 12) ;\
+ RESTORE_SPR(SPRN_IVOR13, BOOKE_IVOR0_OFF + STRIDE_SIZE * 13) ;\
+ RESTORE_SPR(SPRN_IVOR14, BOOKE_IVOR0_OFF + STRIDE_SIZE * 14) ;\
+ RESTORE_SPR(SPRN_IVOR15, BOOKE_IVOR0_OFF + STRIDE_SIZE * 15) ;\
+ RESTORE_SPR(SPRN_IVOR35, BOOKE_IVOR0_OFF + STRIDE_SIZE * 35) ;\
+ RESTORE_SPR(SPRN_IVOR36, BOOKE_IVOR0_OFF + STRIDE_SIZE * 36) ;\
+ RESTORE_SPR(SPRN_IVOR37, BOOKE_IVOR0_OFF + STRIDE_SIZE * 37) ;\
+ RESTORE_SPR(SPRN_IVOR38, BOOKE_IVOR0_OFF + STRIDE_SIZE * 38) ;\
+ RESTORE_SPR(SPRN_IVOR39, BOOKE_IVOR0_OFF + STRIDE_SIZE * 39) ;\
+ RESTORE_SPR(SPRN_IVOR40, BOOKE_IVOR0_OFF + STRIDE_SIZE * 40) ;\
+ RESTORE_SPR(SPRN_IVOR41, BOOKE_IVOR0_OFF + STRIDE_SIZE * 41)
+
+/* reset time base to prevent from overflow */
+#define DELAY(count) \
+ li r3, count; \
+ li r4, 0; \
+ mtspr SPRN_TBWL, r4; \
+101: mfspr r4, SPRN_TBRL; \
+ cmpw r4, r3; \
+ blt 101b
+
+#define FSL_DIS_ALL_IRQ \
+ mfmsr r8; \
+ rlwinm r8, r8, 0, ~MSR_CE; \
+ rlwinm r8, r8, 0, ~MSR_ME; \
+ rlwinm r8, r8, 0, ~MSR_EE; \
+ rlwinm r8, r8, 0, ~MSR_DE; \
+ mtmsr r8; \
+ isync
+
+ .section .data
+ .align 6
+regs_buffer:
+ .space BUFFER_SIZE
+
+ .section .text
+/*
+ * Save CPU registers
+ * r3 : the base address of the buffer which stores the values of registers
+ */
+e5500_cpu_state_save:
+ /* store the base address to r10 */
+ mr r10, r3
+
+ SAVE_ALL_GPR
+ SAVE_ALL_SPRG
+ SAVE_ALL_IVOR
+
+ SAVE_SPR(SPRN_IVPR, BOOKE_IVPR_OFF)
+ SAVE_SPR(SPRN_PID0, BOOKE_PID0_OFF)
+ SAVE_SPR(SPRN_EPCR, BOOKE_EPCR_OFF)
+ SAVE_SPR(SPRN_HID0, BOOKE_HID0_OFF)
+ SAVE_SPR(SPRN_PIR, BOOKE_PIR_OFF)
+ SAVE_SPR(SPRN_BUCSR, BOOKE_BUCSR_OFF)
+1:
+ mfspr r5, SPRN_TBRU
+ mfspr r4, SPRN_TBRL
+ SAVE_GPR(r5, BOOKE_TBU_OFF)
+ SAVE_GPR(r4, BOOKE_TBL_OFF)
+ mfspr r3, SPRN_TBRU
+ cmpw r3, r5
+ bne 1b
+
+ blr
+
+/*
+ * Restore CPU registers
+ * r3 : the base address of the buffer which stores the values of registers
+ */
+e5500_cpu_state_restore:
+ /* store the base address to r10 */
+ mr r10, r3
+
+ RESTORE_ALL_GPR
+ RESTORE_ALL_SPRG
+ RESTORE_ALL_IVOR
+
+ RESTORE_SPR(SPRN_IVPR, BOOKE_IVPR_OFF)
+ RESTORE_SPR(SPRN_PID0, BOOKE_PID0_OFF)
+ RESTORE_SPR(SPRN_EPCR, BOOKE_EPCR_OFF)
+ RESTORE_SPR(SPRN_HID0, BOOKE_HID0_OFF)
+ RESTORE_SPR(SPRN_PIR, BOOKE_PIR_OFF)
+ RESTORE_SPR(SPRN_BUCSR, BOOKE_BUCSR_OFF)
+
+ li r0, 0
+ mtspr SPRN_TBWL, r0
+ RESTORE_SPR(SPRN_TBWU, BOOKE_TBU_OFF)
+ RESTORE_SPR(SPRN_TBWL, BOOKE_TBL_OFF)
+
+ blr
+
+#define CPC_CPCCSR0 0x0
+#define CPC_CPCCSR0_CPCFL 0x800
+
+/*
+ * Flush the CPC cache.
+ * r3 : the base address of CPC
+ */
+flush_cpc_cache:
+ lwz r6, CPC_CPCCSR0(r3)
+ ori r6, r6, CPC_CPCCSR0_CPCFL
+ stw r6, CPC_CPCCSR0(r3)
+ sync
+
+ /* Wait until completing the flush */
+1: lwz r6, CPC_CPCCSR0(r3)
+ andi. r6, r6, CPC_CPCCSR0_CPCFL
+ bne 1b
+
+ blr
+
+/*
+ * the last stage to enter deep sleep
+ *
+ */
+ .align 6
+_GLOBAL(fsl_dp_enter_low)
+deepsleep_start:
+ LOAD_REG_ADDR(r9, buf_tmp)
+ /* save the return address and MSR */
+ mflr r8
+ PPC_STL r8, 0(r9)
+ mfmsr r8
+ PPC_STL r8, 8(r9)
+ mfspr r8, SPRN_TCR
+ PPC_STL r8, 16(r9)
+ mfcr r8
+ PPC_STL r8, 24(r9)
+ li r8, 0
+ mtspr SPRN_TCR, r8
+
+ /* save the parameters */
+ PPC_STL r3, 32(r9)
+ PPC_STL r4, 40(r9)
+ PPC_STL r5, 48(r9)
+ PPC_STL r6, 56(r9)
+
+ LOAD_REG_ADDR(r3, regs_buffer)
+ bl e5500_cpu_state_save
+
+ /* restore the parameters */
+ LOAD_REG_ADDR(r9, buf_tmp)
+ PPC_LL r31, 32(r9)
+ PPC_LL r30, 40(r9)
+ PPC_LL r29, 48(r9)
+ PPC_LL r28, 56(r9)
+
+ /* flush caches inside CPU */
+ LOAD_REG_ADDR(r3, cur_cpu_spec)
+ PPC_LL r3, 0(r3)
+ PPC_LL r3, CPU_DOWN_FLUSH(r3)
+ PPC_LCMPI 0, r3, 0
+ beq 6f
+#ifdef CONFIG_PPC64
+ PPC_LL r3, 0(r3)
+#endif
+ mtctr r3
+ bctrl
+6:
+ /* Flush the CPC cache */
+#define CPC_OFFSET 0x10000
+ mr r3, r31
+ addis r3, r3, CPC_OFFSET@h
+ bl flush_cpc_cache
+
+ /* prefecth TLB */
+#define CCSR_GPIO1_GPDAT 0x130008
+#define CCSR_GPIO1_GPDAT_29 0x4
+ LOAD_REG_IMMEDIATE(r11, CCSR_GPIO1_GPDAT)
+ add r11, r31, r11
+ lwz r10, 0(r11)
+
+#define CCSR_RCPM_PCPH15SETR 0xe20b4
+#define CCSR_RCPM_PCPH15SETR_CORE0 0x1
+ LOAD_REG_IMMEDIATE(r12, CCSR_RCPM_PCPH15SETR)
+ add r12, r31, r12
+ lwz r10, 0(r12)
+
+#define CCSR_DDR_SDRAM_CFG_2 0x8114
+#define CCSR_DDR_SDRAM_CFG_2_FRC_SR 0x80000000
+ LOAD_REG_IMMEDIATE(r13, CCSR_DDR_SDRAM_CFG_2)
+ add r13, r31, r13
+ lwz r10, 0(r13)
+
+#define DCSR_EPU_EPGCR 0x000
+#define DCSR_EPU_EPGCR_GCE 0x80000000
+ li r14, DCSR_EPU_EPGCR
+ add r14, r30, r14
+ lwz r10, 0(r14)
+
+#define DCSR_EPU_EPECR15 0x33C
+#define DCSR_EPU_EPECR15_IC0 0x80000000
+ li r15, DCSR_EPU_EPECR15
+ add r15, r30, r15
+ lwz r10, 0(r15)
+
+#define CCSR_SCFG_QMIFRSTCR 0xfc40c
+#define CCSR_SCFG_QMIFRSTCR_QMIFRST 0x80000000
+ LOAD_REG_IMMEDIATE(r16, CCSR_SCFG_QMIFRSTCR)
+ add r16, r31, r16
+ lwz r10, 0(r16)
+
+/*
+ * There are two kind of register maps, one for T1040QDS and
+ * the other for T104xRDB.
+ */
+#define T104XRDB_CPLD_MISCCSR 0x17
+#define T104XRDB_CPLD_MISCCSR_SLEEPEN 0x40
+#define T1040QDS_QIXIS_PWR_CTL2 0x21
+#define T1040QDS_QIXIS_PWR_CTL2_PCTL 0x2
+ li r3, T1040QDS_QIXIS_PWR_CTL2
+ PPC_LCMPI 0, r28, T1040QDS_TETRA_FLAG
+ beq 20f
+ li r3, T104XRDB_CPLD_MISCCSR
+20: add r29, r29, r3
+ lbz r10, 0(r29)
+ sync
+
+ LOAD_REG_ADDR(r8, deepsleep_start)
+ LOAD_REG_ADDR(r9, deepsleep_end)
+
+ /* prefecth code to cache so that executing code after disable DDR */
+1: icbtls 2, 0, r8
+ addi r8, r8, 64
+ cmpw r8, r9
+ blt 1b
+ sync
+
+ FSL_DIS_ALL_IRQ
+
+ /*
+ * Place DDR controller in self refresh mode.
+ * From here on, can't access DDR any more.
+ */
+ lwz r10, 0(r13)
+ oris r10, r10, CCSR_DDR_SDRAM_CFG_2_FRC_SR@h
+ stw r10, 0(r13)
+ lwz r10, 0(r13)
+ sync
+
+ DELAY(500)
+
+ /*
+ * Enable deep sleep signals by write external CPLD/FPGA register.
+ * The bootloader will disable them when wakeup from deep sleep.
+ */
+ lbz r10, 0(r29)
+ li r3, T1040QDS_QIXIS_PWR_CTL2_PCTL
+ PPC_LCMPI 0, r28, T1040QDS_TETRA_FLAG
+ beq 22f
+ li r3, T104XRDB_CPLD_MISCCSR_SLEEPEN
+22: or r10, r10, r3
+ stb r10, 0(r29)
+ lbz r10, 0(r29)
+ sync
+
+ /*
+ * Set GPIO1_29 to lock the signal MCKE down during deep sleep.
+ * The bootloader will clear it when wakeup.
+ */
+ lwz r10, 0(r11)
+ ori r10, r10, CCSR_GPIO1_GPDAT_29
+ stw r10, 0(r11)
+ lwz r10, 0(r11)
+
+ DELAY(100)
+
+ /* Reset QMan system bus interface */
+ lwz r10, 0(r16)
+ oris r10, r10, CCSR_SCFG_QMIFRSTCR_QMIFRST@h
+ stw r10, 0(r16)
+ lwz r10, 0(r16)
+
+ /* Enable all EPU Counters */
+ li r10, 0
+ oris r10, r10, DCSR_EPU_EPGCR_GCE@h
+ stw r10, 0(r14)
+ lwz r10, 0(r14)
+
+ /* Enable SCU15 to trigger on RCPM Concentrator 0 */
+ lwz r10, 0(r15)
+ oris r10, r10, DCSR_EPU_EPECR15_IC0@h
+ stw r10, 0(r15)
+ lwz r10, 0(r15)
+
+ /* put Core0 in PH15 mode, trigger EPU FSM */
+ lwz r10, 0(r12)
+ ori r10, r10, CCSR_RCPM_PCPH15SETR_CORE0
+ stw r10, 0(r12)
+2:
+ b 2b
+
+ /*
+ * Leave some space to prevent prefeching instruction
+ * beyond deepsleep_end. The space also can be used as heap.
+ */
+buf_tmp:
+ .space 128
+ .align 6
+deepsleep_end:
+
+ .align 12
+#ifdef CONFIG_PPC32
+_GLOBAL(fsl_booke_deep_sleep_resume)
+ /* disable interrupts */
+ FSL_DIS_ALL_IRQ
+
+#define ENTRY_DEEPSLEEP_SETUP
+#define ENTRY_MAPPING_BOOT_SETUP
+#include <../../kernel/fsl_booke_entry_mapping.S>
+#undef ENTRY_DEEPSLEEP_SETUP
+#undef ENTRY_MAPPING_BOOT_SETUP
+
+ li r3, 0
+ mfspr r4, SPRN_PIR
+ bl call_setup_cpu
+
+ /* Load each CAM entry */
+ LOAD_REG_ADDR(r3, tlbcam_index)
+ lwz r3, 0(r3)
+ mtctr r3
+ li r9, 0
+3: mr r3, r9
+ bl loadcam_entry
+ addi r9, r9, 1
+ bdnz 3b
+
+ /* restore cpu registers */
+ LOAD_REG_ADDR(r3, regs_buffer)
+ bl e5500_cpu_state_restore
+
+ /* restore return address */
+ LOAD_REG_ADDR(r3, buf_tmp)
+ lwz r4, 16(r3)
+ mtspr SPRN_TCR, r4
+ lwz r4, 0(r3)
+ mtlr r4
+ lwz r4, 8(r3)
+ mtmsr r4
+ lwz r4, 24(r3)
+ mtcr r4
+
+ blr
+
+#else /* CONFIG_PPC32 */
+
+_GLOBAL(fsl_booke_deep_sleep_resume)
+ /* disable interrupts */
+ FSL_DIS_ALL_IRQ
+
+ /* switch to 64-bit mode */
+ bl .enable_64b_mode
+
+ /* set TOC pointer */
+ bl .relative_toc
+
+ /* setup initial TLBs, switch to kernel space ... */
+ bl .start_initialization_book3e
+
+ /* address space changed, set TOC pointer again */
+ bl .relative_toc
+
+ /* call a cpu state restore handler */
+ LOAD_REG_ADDR(r23, cur_cpu_spec)
+ ld r23,0(r23)
+ ld r23,CPU_SPEC_RESTORE(r23)
+ cmpdi 0,r23,0
+ beq 1f
+ ld r23,0(r23)
+ mtctr r23
+ bctrl
+1:
+ LOAD_REG_ADDR(r3, regs_buffer)
+ bl e5500_cpu_state_restore
+
+ /* Load each CAM entry */
+ LOAD_REG_ADDR(r3, tlbcam_index)
+ lwz r3, 0(r3)
+ mtctr r3
+ li r0, 0
+3: mr r3, r0
+ bl loadcam_entry
+ addi r0, r0, 1
+ bdnz 3b
+
+ /* restore return address */
+ LOAD_REG_ADDR(r3, buf_tmp)
+ ld r4, 16(r3)
+ mtspr SPRN_TCR, r4
+ ld r4, 0(r3)
+ mtlr r4
+ ld r4, 8(r3)
+ mtmsr r4
+ ld r4, 24(r3)
+ mtcr r4
+
+ blr
+
+#endif /* CONFIG_PPC32 */
--
1.9.1

2015-08-01 00:42:05

by Scott Wood

[permalink] [raw]
Subject: Re: [PATCH 3/4] powerpc: pm: add EPU FSM configuration for deep sleep

On Fri, 2015-07-31 at 20:53 +0800, Chenhui Zhao wrote:
> In the last stage of deep sleep, software will trigger a Finite
> State Machine (FSM) to control the hardware precedure, such as
> board isolation, killing PLLs, removing power, and so on.
>
> When the system is waked up by an interrupt, the FSM controls the
> hardware to complete the early resume precedure.
>
> This patch configure the EPU FSM preparing for deep sleep.
>
> Signed-off-by: Chenhui Zhao <[email protected]>
> ---
> arch/powerpc/platforms/85xx/Makefile | 2 +-
> arch/powerpc/platforms/85xx/sleep_fsm.c | 256
> ++++++++++++++++++++++++++++++++
> arch/powerpc/platforms/85xx/sleep_fsm.h | 104 +++++++++++++
> 3 files changed, 361 insertions(+), 1 deletion(-)
> create mode 100644 arch/powerpc/platforms/85xx/sleep_fsm.c
> create mode 100644 arch/powerpc/platforms/85xx/sleep_fsm.h

When I asked why this was in drivers/platform[1], you said it was to share
with LS1, and that the values used were the same -- so why did you move it to
arch/powerpc?

[1] Note that other proposed patches create a drivers/soc/fsl instead of
drivers/platform/fsl... We need one of them, not both.

> +void fsl_fsm_setup(void __iomem *base, struct fsm_reg_vals *val)
> +{
> + struct fsm_reg_vals *data = val;
> +
> + BUG_ON(!base || !data);

This BUG_ON is useless. If one of those is NULL you'll get an oops anyway.

> diff --git a/arch/powerpc/platforms/85xx/sleep_fsm.h
> b/arch/powerpc/platforms/85xx/sleep_fsm.h
> new file mode 100644
> index 0000000..2c60b40
> --- /dev/null
> +++ b/arch/powerpc/platforms/85xx/sleep_fsm.h
> @@ -0,0 +1,104 @@
> +/*
> + * Freescale deep sleep FSM (finite-state machine) configuration
> + *
> + * Copyright 2015 Freescale Semiconductor Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or (at your
> + * option) any later version.
> + */
> +#ifndef _FSL_SLEEP_FSM_H
> +#define _FSL_SLEEP_FSM_H
> +
> +#define FSL_STRIDE_4B 4
> +#define FSL_STRIDE_8B 8

Why not just use 4/8 directly?

>
> +/* Block offsets */
> +#define RCPM_BLOCK_OFFSET 0x00022000
> +#define EPU_BLOCK_OFFSET 0x00000000
> +#define NPC_BLOCK_OFFSET 0x00001000

I thought you said OK to not putting these offsets in the kernel source...

-Scott

2015-08-01 02:49:37

by Scott Wood

[permalink] [raw]
Subject: Re: [PATCH 4/4] powerpc: pm: support deep sleep feature on T104x

On Fri, 2015-07-31 at 20:53 +0800, Chenhui Zhao wrote:
> diff --git a/arch/powerpc/kernel/fsl_booke_entry_mapping.S
> b/arch/powerpc/kernel/fsl_booke_entry_mapping.S
> index f22e7e4..32ec426f 100644
> --- a/arch/powerpc/kernel/fsl_booke_entry_mapping.S
> +++ b/arch/powerpc/kernel/fsl_booke_entry_mapping.S
> @@ -170,6 +170,10 @@ skpinv: addi r6,r6,1 /* Increment */
> lis r6,MAS2_VAL(PAGE_OFFSET, BOOK3E_PAGESZ_64M, M_IF_SMP)@h
> ori r6,r6,MAS2_VAL(PAGE_OFFSET, BOOK3E_PAGESZ_64M, M_IF_SMP)@l
> mtspr SPRN_MAS2,r6
> +#ifdef ENTRY_DEEPSLEEP_SETUP
> + LOAD_REG_IMMEDIATE(r8, MEMORY_START)
> + ori r8,r8,(MAS3_SX|MAS3_SW|MAS3_SR)
> +#endif
> mtspr SPRN_MAS3,r8
> tlbwe
>
> @@ -212,12 +216,18 @@ next_tlb_setup:
> #error You need to specify the mapping or not use this at all.
> #endif
>
> +#ifdef ENTRY_DEEPSLEEP_SETUP
> + LOAD_REG_ADDR(r6, 2f)
> + mfmsr r7
> + rlwinm r7,r7,0,~(MSR_IS|MSR_DS)
> +#else
> lis r7,MSR_KERNEL@h
> ori r7,r7,MSR_KERNEL@l
> bl 1f /* Find our address */
> 1: mflr r9
> rlwimi r6,r9,0,20,31
> addi r6,r6,(2f - 1b)
> +#endif

Could you explain what's going on here? What does the TLB look like before
and after?

> +int fsl_dp_iomap(void)

I don't think this needs to be global (see the comment where it gets called),
but if it must be, this name is too terse.

> +{
> + struct device_node *np;
> + int ret = 0;
> + phys_addr_t ccsr_phy_addr, dcsr_phy_addr;
> +
> + saved_law = NULL;
> + ccsr_base = NULL;
> + dcsr_base = NULL;
> + pld_base = NULL;
> +
> + ccsr_phy_addr = get_immrbase();
> + if (ccsr_phy_addr == -1) {
> + pr_err("%s: Can't get the address of CCSR\n", __func__);
> + ret = -EINVAL;
> + goto ccsr_err;
> + }
> + ccsr_base = ioremap(ccsr_phy_addr, SIZE_2MB);
> + if (!ccsr_base) {
> + ret = -ENOMEM;
> + goto ccsr_err;
> + }
> +
> + dcsr_phy_addr = get_dcsrbase();
> + if (dcsr_phy_addr == -1) {
> + pr_err("%s: Can't get the address of DCSR\n", __func__);
> + ret = -EINVAL;
> + goto dcsr_err;
> + }
> + dcsr_base = ioremap(dcsr_phy_addr, SIZE_1MB);
> + if (!dcsr_base) {
> + ret = -ENOMEM;
> + goto dcsr_err;
> + }

Please just map the device tree nodes you need.

> +
> + np = of_find_compatible_node(NULL, NULL, "fsl,tetra-fpga");
> + if (np) {
> + pld_flag = T1040QDS_TETRA_FLAG;
> + } else {
> + np = of_find_compatible_node(NULL, NULL, "fsl,deepsleep-cpld");

I've already rejected fsl,deepsleep-cpld multiple times when others tried to
add it to a device tree.

> +{
> + u32 ddr_buff_addr;
> +
> + /*
> + * DDR training initialization will break 128 bytes at the beginning
> + * of DDR, therefore, save them so that the bootloader will restore
> + * them. Assume that DDR is mapped to the address space started with
> + * CONFIG_PAGE_OFFSET.
> + */
> + memcpy(ddr_buff, (void *)CONFIG_PAGE_OFFSET, DDR_BUF_SIZE);

That assumption may not be true in all relocatable scenarios.

It'd be a lot simpler to just mark that first page as reserved.

> + /* assume ddr_buff is in the physical address space of 4GB */
> + ddr_buff_addr = (u32)(__pa(ddr_buff) & 0xffffffff);
> +
> + /*
> + * the bootloader will restore the first 128 bytes of DDR from
> + * the location indicated by the register SPARECR3
> + */
> + out_be32(ccsr_base + CCSR_SCFG_SPARECR3, ddr_buff_addr);

...yeah, please just mark it reserved.

> +}
> +
> +static void fsl_dp_mp_save(void *ccsr)
> +{
> + struct fsl_bstr *dst = &saved_bstr;
> +
> + dst->bstrh = in_be32(ccsr + LCC_BSTRH);
> + dst->bstrl = in_be32(ccsr + LCC_BSTRL);
> + dst->bstar = in_be32(ccsr + LCC_BSTAR);
> + dst->cpu_mask = in_be32(ccsr + DCFG_BASE + DCFG_BRR);
> +}

What is "mp"?

> +static void fsl_dp_law_save(void *ccsr)
> +{
> + int i;
> + struct fsl_law *dst = saved_law;
> + struct fsl_law *src = (void *)(ccsr + CCSR_LAW_BASE);
> +
> + for (i = 0; i < num_laws; i++) {
> + dst->lawbarh = in_be32(&src->lawbarh);
> + dst->lawbarl = in_be32(&src->lawbarl);
> + dst->lawar = in_be32(&src->lawar);
> + dst++;
> + src++;
> + }
> +}

Why wouldn't U-Boot restore these the same way on resume as they are now?

> +int fsl_enter_epu_deepsleep(void)
> +{
> + fsl_dp_ddr_save(ccsr_base);
> +
> + fsl_dp_set_resume_pointer(ccsr_base);
> +
> + fsl_dp_mp_save(ccsr_base);
> + fsl_dp_law_save(ccsr_base);
> + /* enable Warm Device Reset request. */
> + setbits32(ccsr_base + CCSR_SCFG_DPSLPCR, CCSR_SCFG_DPSLPCR_WDRR_EN);
> +
> + /* set GPIO1_29 as an output pin (not open-drain), and output 0 */
> + clrbits32(ccsr_base + CCSR_GPIO1_GPDAT, CCSR_GPIO1_GPDIR_29);
> + clrbits32(ccsr_base + CCSR_GPIO1_GPODR, CCSR_GPIO1_GPDIR_29);
> + setbits32(ccsr_base + CCSR_GPIO1_GPDIR, CCSR_GPIO1_GPDIR_29);
> +
> + /*
> + * Disable CPC speculation to avoid deep sleep hang, especially
> + * in secure boot mode. This bit will be cleared automatically
> + * when resuming from deep sleep.
> + */
> + setbits32(ccsr_base + CPC_CPCHDBCR0, CPC_CPCHDBCR0_SPEC_DIS);

Is there an erratum for this?

> + fsl_epu_setup_default(dcsr_base + EPU_BLOCK_OFFSET);
> + fsl_npc_setup_default(dcsr_base + NPC_BLOCK_OFFSET);
> +
> + out_be32(dcsr_base + RCPM_BLOCK_OFFSET + CSTTACR0, 0x00001001);
> + out_be32(dcsr_base + RCPM_BLOCK_OFFSET + CG1CR0, 0x00000001);

What is 0x1001 and 0x1 here?

> diff --git a/arch/powerpc/platforms/85xx/qoriq_pm.c
> b/arch/powerpc/platforms/85xx/qoriq_pm.c
> index 27ec337..f65f6cf 100644
> --- a/arch/powerpc/platforms/85xx/qoriq_pm.c
> +++ b/arch/powerpc/platforms/85xx/qoriq_pm.c
> @@ -17,17 +17,68 @@
>
> #include <asm/fsl_pm.h>
>
> +static suspend_state_t cur_pm_state;
> +
> +/**
> + * fsl_set_power_except - set which IP block is not powerdown when sleep or
> + * deep sleep, such as MAC, USB, etc.
> + *
> + * @dev: a pointer to the struct device of the device with wakeup
> capability
> + * @on: if 1, do not power down; if 0, power down.
> + */
> +static void fsl_set_power_except(struct device *dev, int on)
> +{
> + u32 value[2];
> + int ret;
> +
> + ret = of_property_read_u32_array(dev->of_node, "sleep", value, 2);
> + if (ret)
> + goto out;

This property is not defined in the binding for t1040, and was largely
abandoned. Nothing in the kernel currently uses it.

It might be better to do something with the clock API. Then again, it might
be an odd fit given the odd nature of this register. Then again, it's an
equally bad fit for the sleep property as currently documented (on the only
85xx the binding supports, it's used for DEVDISR). In any case, don't use
this without a binding update.

This function will also crash if you pass it a device that doesn't have an
of_node.

> switch (state) {
> case PM_SUSPEND_STANDBY:
> +
> cur_cpu_spec->cpu_down_flush();
> +
> ret = qoriq_pm_ops->plat_enter_sleep();
> +
> + break;

Why are you adding these blank lines, particularly right after the case line?

> + case PM_SUSPEND_MEM:
> +
> + cpu = smp_processor_id();
> + qoriq_pm_ops->irq_mask(cpu);

Should this be hard_smp_processor_id()?

> +static int qoriq_suspend_begin(suspend_state_t state)
> +{
> + const int enable = 1;
> +
> + cur_pm_state = state;
> + dpm_for_each_dev((void *)&enable, qoriq_set_wakeup_source);

Unnecessary cast.

> +
> + if (cur_pm_state == PM_SUSPEND_MEM)
> + return fsl_dp_iomap();
> +
> + return 0;
> +}
> +
> +static void qoriq_suspend_end(void)
> +{
> + const int enable = 0;
> +
> + dpm_for_each_dev((void *)&enable, qoriq_set_wakeup_source);
> +
> + if (cur_pm_state == PM_SUSPEND_MEM)
> + fsl_dp_iounmap();
> +}

Why are you mapping/unmapping on demand? Just map it once at bootup.

static int __init qoriq_suspend_init(void)
> {
> suspend_set_ops(&qoriq_suspend_ops);
> -
> return 0;
> }

Please don't make random whitespace changes, especially to code you're not
otherwise touching.


> arch_initcall(qoriq_suspend_init);
> diff --git a/arch/powerpc/platforms/85xx/t104x_deepsleep.S
> b/arch/powerpc/platforms/85xx/t104x_deepsleep.S
> new file mode 100644
> index 0000000..773a9e4
> --- /dev/null
> +++ b/arch/powerpc/platforms/85xx/t104x_deepsleep.S
> @@ -0,0 +1,570 @@
> +/*
> + * Enter and resume from deep sleep state
> + *
> + * Copyright 2015 Freescale Semiconductor Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or (at your
> + * option) any later version.
> + */
> +
> +#include <asm/page.h>
> +#include <asm/ppc_asm.h>
> +#include <asm/reg.h>
> +#include <asm/asm-offsets.h>
> +#include <asm/fsl_pm.h>
> +#include <asm/mmu.h>
> +
> +/*
> + * the number of bytes occupied by one register
> + * the value of 8 is compatible with both 32-bit and 64-bit registers
> + */
> +#define STRIDE_SIZE 8
> +
> +/* GPR0 - GPR31 */
> +#define BOOKE_GPR0_OFF 0x0000
> +#define BOOKE_GPR_COUNT 32
> +/* IVOR0 - IVOR42 */
> +#define BOOKE_IVOR0_OFF (BOOKE_GPR0_OFF + BOOKE_GPR_COUNT * STRIDE_SIZE)
> +#define BOOKE_IVOR_COUNT 43
> +/* SPRG0 - SPRG9 */
> +#define BOOKE_SPRG0_OFF (BOOKE_IVOR0_OFF + BOOKE_IVOR_COUNT *
> STRIDE_SIZE)
> +#define BOOKE_SPRG_COUNT 10
> +/* IVPR */
> +#define BOOKE_IVPR_OFF (BOOKE_SPRG0_OFF + BOOKE_SPRG_COUNT *
> STRIDE_SIZE)
> +
> +#define BOOKE_LR_OFF (BOOKE_IVPR_OFF + STRIDE_SIZE)
> +#define BOOKE_MSR_OFF (BOOKE_LR_OFF + STRIDE_SIZE)
> +#define BOOKE_TBU_OFF (BOOKE_MSR_OFF + STRIDE_SIZE)
> +#define BOOKE_TBL_OFF (BOOKE_TBU_OFF + STRIDE_SIZE)
> +#define BOOKE_EPCR_OFF (BOOKE_TBL_OFF + STRIDE_SIZE)
> +#define BOOKE_HID0_OFF (BOOKE_EPCR_OFF + STRIDE_SIZE)
> +#define BOOKE_PIR_OFF (BOOKE_HID0_OFF + STRIDE_SIZE)
> +#define BOOKE_PID0_OFF (BOOKE_PIR_OFF + STRIDE_SIZE)
> +#define BOOKE_BUCSR_OFF (BOOKE_PID0_OFF + STRIDE_SIZE)
> +
> +#define BUFFER_SIZE (BOOKE_BUCSR_OFF + STRIDE_SIZE)
> +
> +#undef SAVE_GPR
> +#define SAVE_GPR(gpr, offset) \
> + PPC_STL gpr, offset(r10)
> +
> +#define RESTORE_GPR(gpr, offset) \
> + PPC_LL gpr, offset(r10)
> +
> +#define SAVE_SPR(spr, offset) \
> + mfspr r0, spr ;\
> + PPC_STL r0, offset(r10)
> +
> +#define RESTORE_SPR(spr, offset) \
> + PPC_LL r0, offset(r10) ;\
> + mtspr spr, r0

No space before ;

> +/* reset time base to prevent from overflow */
> +/*
> + * There are two kind of register maps, one for T1040QDS and
> + * the other for T104xRDB.
> + */

And then what happens on a custom board? Is there any way this stuff can
happen earlier, from C code? In any case, please rework so that there's a
function pointer for board-specific logic.

-Scott

2015-08-03 12:01:38

by Chenhui Zhao

[permalink] [raw]
Subject: Re: [PATCH 3/4] powerpc: pm: add EPU FSM configuration for deep sleep



On Sat, Aug 1, 2015 at 8:41 AM, Scott Wood <[email protected]>
wrote:
> On Fri, 2015-07-31 at 20:53 +0800, Chenhui Zhao wrote:
>> In the last stage of deep sleep, software will trigger a Finite
>> State Machine (FSM) to control the hardware precedure, such as
>> board isolation, killing PLLs, removing power, and so on.
>>
>> When the system is waked up by an interrupt, the FSM controls the
>> hardware to complete the early resume precedure.
>>
>> This patch configure the EPU FSM preparing for deep sleep.
>>
>> Signed-off-by: Chenhui Zhao <[email protected]>
>> ---
>> arch/powerpc/platforms/85xx/Makefile | 2 +-
>> arch/powerpc/platforms/85xx/sleep_fsm.c | 256
>> ++++++++++++++++++++++++++++++++
>> arch/powerpc/platforms/85xx/sleep_fsm.h | 104 +++++++++++++
>> 3 files changed, 361 insertions(+), 1 deletion(-)
>> create mode 100644 arch/powerpc/platforms/85xx/sleep_fsm.c
>> create mode 100644 arch/powerpc/platforms/85xx/sleep_fsm.h
>
> When I asked why this was in drivers/platform[1], you said it was to
> share
> with LS1, and that the values used were the same -- so why did you
> move it to
> arch/powerpc?

There are some changes. LS1 will use PSCI (Power State Coordination
Interface) to implement deep sleep. So these code just used by PowerPC.

>
> [1] Note that other proposed patches create a drivers/soc/fsl instead
> of
> drivers/platform/fsl... We need one of them, not both.
>
>> +void fsl_fsm_setup(void __iomem *base, struct fsm_reg_vals *val)
>> +{
>> + struct fsm_reg_vals *data = val;
>> +
>> + BUG_ON(!base || !data);
>
> This BUG_ON is useless. If one of those is NULL you'll get an oops
> anyway.
>
>
>> diff --git a/arch/powerpc/platforms/85xx/sleep_fsm.h
>> b/arch/powerpc/platforms/85xx/sleep_fsm.h
>> new file mode 100644
>> index 0000000..2c60b40
>> --- /dev/null
>> +++ b/arch/powerpc/platforms/85xx/sleep_fsm.h
>> @@ -0,0 +1,104 @@
>> +/*
>> + * Freescale deep sleep FSM (finite-state machine) configuration
>> + *
>> + * Copyright 2015 Freescale Semiconductor Inc.
>> + *
>> + * This program is free software; you can redistribute it and/or
>> modify it
>> + * under the terms of the GNU General Public License as
>> published by the
>> + * Free Software Foundation; either version 2 of the License, or
>> (at your
>> + * option) any later version.
>> + */
>> +#ifndef _FSL_SLEEP_FSM_H
>> +#define _FSL_SLEEP_FSM_H
>> +
>> +#define FSL_STRIDE_4B 4
>> +#define FSL_STRIDE_8B 8
>
> Why not just use 4/8 directly?
>
>>
>> +/* Block offsets */
>> +#define RCPM_BLOCK_OFFSET 0x00022000
>> +#define EPU_BLOCK_OFFSET 0x00000000
>> +#define NPC_BLOCK_OFFSET 0x00001000
>
> I thought you said OK to not putting these offsets in the kernel
> source...
>
> -Scott

OK. Will change them.

-Chenhui

2015-08-03 21:23:03

by Scott Wood

[permalink] [raw]
Subject: Re: [PATCH 3/4] powerpc: pm: add EPU FSM configuration for deep sleep

On Mon, 2015-08-03 at 20:01 +0800, Chenhui Zhao wrote:
> On Sat, Aug 1, 2015 at 8:41 AM, Scott Wood <[email protected]>
> wrote:
> > On Fri, 2015-07-31 at 20:53 +0800, Chenhui Zhao wrote:
> > > In the last stage of deep sleep, software will trigger a Finite
> > > State Machine (FSM) to control the hardware precedure, such as
> > > board isolation, killing PLLs, removing power, and so on.
> > >
> > > When the system is waked up by an interrupt, the FSM controls the
> > > hardware to complete the early resume precedure.
> > >
> > > This patch configure the EPU FSM preparing for deep sleep.
> > >
> > > Signed-off-by: Chenhui Zhao <[email protected]>
> > > ---
> > > arch/powerpc/platforms/85xx/Makefile | 2 +-
> > > arch/powerpc/platforms/85xx/sleep_fsm.c | 256
> > > ++++++++++++++++++++++++++++++++
> > > arch/powerpc/platforms/85xx/sleep_fsm.h | 104 +++++++++++++
> > > 3 files changed, 361 insertions(+), 1 deletion(-)
> > > create mode 100644 arch/powerpc/platforms/85xx/sleep_fsm.c
> > > create mode 100644 arch/powerpc/platforms/85xx/sleep_fsm.h
> >
> > When I asked why this was in drivers/platform[1], you said it was to
> > share
> > with LS1, and that the values used were the same -- so why did you
> > move it to
> > arch/powerpc?
>
> There are some changes. LS1 will use PSCI (Power State Coordination
> Interface) to implement deep sleep. So these code just used by PowerPC.

Then remove the comment about the values being usable on ls1021.

-Scott