All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/8] ARM: tegra: support LP1 suspend mode
@ 2013-07-26  9:15 ` Joseph Lo
  0 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-07-26  9:15 UTC (permalink / raw)
  To: Stephen Warren
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Joseph Lo

This series adds the support of LP1 suspend mode for Tegra.

Verified on Seaboard, Cardhu and Dalmore.
And tested with THUMB2_KERNEL as well.

P.S.
A known issue on Dalmore, the LP1 resume may take 10 ~ 15 seconds due to
the 792MHz BCT. It can't be reproduced on Seaboard or Cardhu. And the
root cause of this is about the default settings of EMC registers that
cause the DRAM can't leave self-refresh mode immediately. If you want
to test with quick LP1 resume on Dalmore, I can provide another HACK for
this.

Joseph Lo (8):
  ARM: tegra: add common resume handling code for LP1 resuming
  ARM: tegra: config the polarity of the request of sys clock
  clk: tegra114: add LP1 suspend/resume support
  ARM: tegra: add common LP1 suspend support
  ARM: tegra30: add LP1 suspend support
  ARM: tegra20: add LP1 suspend support
  ARM: tegra114: add LP1 suspend support
  ARM: dts: tegra: enable LP1 suspend mode

 arch/arm/boot/dts/tegra114-dalmore.dts     |   2 +-
 arch/arm/boot/dts/tegra20-colibri-512.dtsi |   2 +-
 arch/arm/boot/dts/tegra20-harmony.dts      |   2 +-
 arch/arm/boot/dts/tegra20-paz00.dts        |   2 +-
 arch/arm/boot/dts/tegra20-seaboard.dts     |   2 +-
 arch/arm/boot/dts/tegra20-tamonten.dtsi    |   2 +-
 arch/arm/boot/dts/tegra20-trimslice.dts    |   2 +-
 arch/arm/boot/dts/tegra20-ventana.dts      |   2 +-
 arch/arm/boot/dts/tegra20-whistler.dts     |   2 +-
 arch/arm/boot/dts/tegra30-beaver.dts       |   2 +-
 arch/arm/boot/dts/tegra30-cardhu.dtsi      |   2 +-
 arch/arm/mach-tegra/Makefile               |   3 +
 arch/arm/mach-tegra/iomap.h                |   6 +
 arch/arm/mach-tegra/pm-tegra20.c           |  37 ++
 arch/arm/mach-tegra/pm-tegra30.c           |  37 ++
 arch/arm/mach-tegra/pm.c                   | 121 +++++-
 arch/arm/mach-tegra/pm.h                   |  23 ++
 arch/arm/mach-tegra/pmc.c                  |  36 +-
 arch/arm/mach-tegra/pmc.h                  |   3 +
 arch/arm/mach-tegra/reset-handler.S        |  13 +
 arch/arm/mach-tegra/reset.c                |   2 +
 arch/arm/mach-tegra/reset.h                |   4 +
 arch/arm/mach-tegra/sleep-tegra20.S        | 300 ++++++++++++++
 arch/arm/mach-tegra/sleep-tegra30.S        | 615 +++++++++++++++++++++++++++++
 arch/arm/mach-tegra/sleep.S                |   8 +-
 arch/arm/mach-tegra/sleep.h                |  18 +
 drivers/clk/tegra/clk-tegra114.c           |  32 ++
 27 files changed, 1259 insertions(+), 21 deletions(-)
 create mode 100644 arch/arm/mach-tegra/pm-tegra20.c
 create mode 100644 arch/arm/mach-tegra/pm-tegra30.c

-- 
1.8.3.4

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 0/8] ARM: tegra: support LP1 suspend mode
@ 2013-07-26  9:15 ` Joseph Lo
  0 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-07-26  9:15 UTC (permalink / raw)
  To: linux-arm-kernel

This series adds the support of LP1 suspend mode for Tegra.

Verified on Seaboard, Cardhu and Dalmore.
And tested with THUMB2_KERNEL as well.

P.S.
A known issue on Dalmore, the LP1 resume may take 10 ~ 15 seconds due to
the 792MHz BCT. It can't be reproduced on Seaboard or Cardhu. And the
root cause of this is about the default settings of EMC registers that
cause the DRAM can't leave self-refresh mode immediately. If you want
to test with quick LP1 resume on Dalmore, I can provide another HACK for
this.

Joseph Lo (8):
  ARM: tegra: add common resume handling code for LP1 resuming
  ARM: tegra: config the polarity of the request of sys clock
  clk: tegra114: add LP1 suspend/resume support
  ARM: tegra: add common LP1 suspend support
  ARM: tegra30: add LP1 suspend support
  ARM: tegra20: add LP1 suspend support
  ARM: tegra114: add LP1 suspend support
  ARM: dts: tegra: enable LP1 suspend mode

 arch/arm/boot/dts/tegra114-dalmore.dts     |   2 +-
 arch/arm/boot/dts/tegra20-colibri-512.dtsi |   2 +-
 arch/arm/boot/dts/tegra20-harmony.dts      |   2 +-
 arch/arm/boot/dts/tegra20-paz00.dts        |   2 +-
 arch/arm/boot/dts/tegra20-seaboard.dts     |   2 +-
 arch/arm/boot/dts/tegra20-tamonten.dtsi    |   2 +-
 arch/arm/boot/dts/tegra20-trimslice.dts    |   2 +-
 arch/arm/boot/dts/tegra20-ventana.dts      |   2 +-
 arch/arm/boot/dts/tegra20-whistler.dts     |   2 +-
 arch/arm/boot/dts/tegra30-beaver.dts       |   2 +-
 arch/arm/boot/dts/tegra30-cardhu.dtsi      |   2 +-
 arch/arm/mach-tegra/Makefile               |   3 +
 arch/arm/mach-tegra/iomap.h                |   6 +
 arch/arm/mach-tegra/pm-tegra20.c           |  37 ++
 arch/arm/mach-tegra/pm-tegra30.c           |  37 ++
 arch/arm/mach-tegra/pm.c                   | 121 +++++-
 arch/arm/mach-tegra/pm.h                   |  23 ++
 arch/arm/mach-tegra/pmc.c                  |  36 +-
 arch/arm/mach-tegra/pmc.h                  |   3 +
 arch/arm/mach-tegra/reset-handler.S        |  13 +
 arch/arm/mach-tegra/reset.c                |   2 +
 arch/arm/mach-tegra/reset.h                |   4 +
 arch/arm/mach-tegra/sleep-tegra20.S        | 300 ++++++++++++++
 arch/arm/mach-tegra/sleep-tegra30.S        | 615 +++++++++++++++++++++++++++++
 arch/arm/mach-tegra/sleep.S                |   8 +-
 arch/arm/mach-tegra/sleep.h                |  18 +
 drivers/clk/tegra/clk-tegra114.c           |  32 ++
 27 files changed, 1259 insertions(+), 21 deletions(-)
 create mode 100644 arch/arm/mach-tegra/pm-tegra20.c
 create mode 100644 arch/arm/mach-tegra/pm-tegra30.c

-- 
1.8.3.4

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 1/8] ARM: tegra: add common resume handling code for LP1 resuming
  2013-07-26  9:15 ` Joseph Lo
@ 2013-07-26  9:15     ` Joseph Lo
  -1 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-07-26  9:15 UTC (permalink / raw)
  To: Stephen Warren
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Joseph Lo

The common LP1 resuming procedures of LP1 on Tegra was checking the LP1
mask first. The LP1 mask indicated that the Tegra device was in LP1 then
we need to resume the Tegra from the LP1 reset handler.

And the LP1 was putting the SDRAM to self-refresh mode, the SDRAM wasn't
accessible when resuming from LP1. We need to copy the LP1 reset handler
to IRAM before suspending. That's why you can see the address of LP1
reset handler was located in IRAM.

Signed-off-by: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
 arch/arm/mach-tegra/reset-handler.S | 13 +++++++++++++
 arch/arm/mach-tegra/reset.c         |  2 ++
 arch/arm/mach-tegra/sleep.h         |  2 ++
 3 files changed, 17 insertions(+)

diff --git a/arch/arm/mach-tegra/reset-handler.S b/arch/arm/mach-tegra/reset-handler.S
index 34614bd..492b10f 100644
--- a/arch/arm/mach-tegra/reset-handler.S
+++ b/arch/arm/mach-tegra/reset-handler.S
@@ -182,6 +182,19 @@ after_errata:
 1:
 #endif
 
+	/* Waking up from LP1? */
+	ldr	r8, [r12, #RESET_DATA(MASK_LP1)]
+	tst	r8, r11				@ if in_lp1
+	beq	__is_not_lp1
+	cmp	r10, #0
+	bne	__die				@ only CPU0 can be here
+	ldr	lr, [r12, #RESET_DATA(STARTUP_LP1)]
+ THUMB(	add	lr, lr, #1 )			@ switch to Thumb mode
+	cmp	lr, #0
+	bleq	__die				@ no LP1 startup handler
+	bx	lr
+__is_not_lp1:
+
 	/* Waking up from LP2? */
 	ldr	r9, [r12, #RESET_DATA(MASK_LP2)]
 	tst	r9, r11				@ if in_lp2
diff --git a/arch/arm/mach-tegra/reset.c b/arch/arm/mach-tegra/reset.c
index 1ac434e..fd0bbf8 100644
--- a/arch/arm/mach-tegra/reset.c
+++ b/arch/arm/mach-tegra/reset.c
@@ -81,6 +81,8 @@ void __init tegra_cpu_reset_handler_init(void)
 #endif
 
 #ifdef CONFIG_PM_SLEEP
+	__tegra_cpu_reset_handler_data[TEGRA_RESET_STARTUP_LP1] =
+		TEGRA_IRAM_CODE_AREA;
 	__tegra_cpu_reset_handler_data[TEGRA_RESET_STARTUP_LP2] =
 		virt_to_phys((void *)tegra_resume);
 #endif
diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h
index e907e40..e7f8b6f 100644
--- a/arch/arm/mach-tegra/sleep.h
+++ b/arch/arm/mach-tegra/sleep.h
@@ -29,6 +29,8 @@
 					+ IO_APB_VIRT)
 #define TEGRA_PMC_VIRT	(TEGRA_PMC_BASE - IO_APB_PHYS + IO_APB_VIRT)
 
+#define TEGRA_IRAM_CODE_AREA	(TEGRA_IRAM_BASE + SZ_4K)
+
 /* PMC_SCRATCH37-39 and 41 are used for tegra_pen_lock and idle */
 #define PMC_SCRATCH37	0x130
 #define PMC_SCRATCH38	0x134
-- 
1.8.3.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 1/8] ARM: tegra: add common resume handling code for LP1 resuming
@ 2013-07-26  9:15     ` Joseph Lo
  0 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-07-26  9:15 UTC (permalink / raw)
  To: linux-arm-kernel

The common LP1 resuming procedures of LP1 on Tegra was checking the LP1
mask first. The LP1 mask indicated that the Tegra device was in LP1 then
we need to resume the Tegra from the LP1 reset handler.

And the LP1 was putting the SDRAM to self-refresh mode, the SDRAM wasn't
accessible when resuming from LP1. We need to copy the LP1 reset handler
to IRAM before suspending. That's why you can see the address of LP1
reset handler was located in IRAM.

Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
 arch/arm/mach-tegra/reset-handler.S | 13 +++++++++++++
 arch/arm/mach-tegra/reset.c         |  2 ++
 arch/arm/mach-tegra/sleep.h         |  2 ++
 3 files changed, 17 insertions(+)

diff --git a/arch/arm/mach-tegra/reset-handler.S b/arch/arm/mach-tegra/reset-handler.S
index 34614bd..492b10f 100644
--- a/arch/arm/mach-tegra/reset-handler.S
+++ b/arch/arm/mach-tegra/reset-handler.S
@@ -182,6 +182,19 @@ after_errata:
 1:
 #endif
 
+	/* Waking up from LP1? */
+	ldr	r8, [r12, #RESET_DATA(MASK_LP1)]
+	tst	r8, r11				@ if in_lp1
+	beq	__is_not_lp1
+	cmp	r10, #0
+	bne	__die				@ only CPU0 can be here
+	ldr	lr, [r12, #RESET_DATA(STARTUP_LP1)]
+ THUMB(	add	lr, lr, #1 )			@ switch to Thumb mode
+	cmp	lr, #0
+	bleq	__die				@ no LP1 startup handler
+	bx	lr
+__is_not_lp1:
+
 	/* Waking up from LP2? */
 	ldr	r9, [r12, #RESET_DATA(MASK_LP2)]
 	tst	r9, r11				@ if in_lp2
diff --git a/arch/arm/mach-tegra/reset.c b/arch/arm/mach-tegra/reset.c
index 1ac434e..fd0bbf8 100644
--- a/arch/arm/mach-tegra/reset.c
+++ b/arch/arm/mach-tegra/reset.c
@@ -81,6 +81,8 @@ void __init tegra_cpu_reset_handler_init(void)
 #endif
 
 #ifdef CONFIG_PM_SLEEP
+	__tegra_cpu_reset_handler_data[TEGRA_RESET_STARTUP_LP1] =
+		TEGRA_IRAM_CODE_AREA;
 	__tegra_cpu_reset_handler_data[TEGRA_RESET_STARTUP_LP2] =
 		virt_to_phys((void *)tegra_resume);
 #endif
diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h
index e907e40..e7f8b6f 100644
--- a/arch/arm/mach-tegra/sleep.h
+++ b/arch/arm/mach-tegra/sleep.h
@@ -29,6 +29,8 @@
 					+ IO_APB_VIRT)
 #define TEGRA_PMC_VIRT	(TEGRA_PMC_BASE - IO_APB_PHYS + IO_APB_VIRT)
 
+#define TEGRA_IRAM_CODE_AREA	(TEGRA_IRAM_BASE + SZ_4K)
+
 /* PMC_SCRATCH37-39 and 41 are used for tegra_pen_lock and idle */
 #define PMC_SCRATCH37	0x130
 #define PMC_SCRATCH38	0x134
-- 
1.8.3.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 2/8] ARM: tegra: config the polarity of the request of sys clock
  2013-07-26  9:15 ` Joseph Lo
@ 2013-07-26  9:15     ` Joseph Lo
  -1 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-07-26  9:15 UTC (permalink / raw)
  To: Stephen Warren
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Joseph Lo

When suspending to LP1 mode, the SYSCLK will be clock gated. And different
board may have different polarity of the request of SYSCLK, this patch
configure the polarity from the DT for the board.

Signed-off-by: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
 arch/arm/mach-tegra/pmc.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c
index 8345fcd..018bc87 100644
--- a/arch/arm/mach-tegra/pmc.c
+++ b/arch/arm/mach-tegra/pmc.c
@@ -27,6 +27,8 @@
 #include "pmc.h"
 #include "sleep.h"
 
+#define TEGRA_POWER_SYSCLK_POLARITY	(1 << 10)  /* sys clk polarity */
+#define TEGRA_POWER_SYSCLK_OE		(1 << 11)  /* system clock enable */
 #define TEGRA_POWER_EFFECT_LP0		(1 << 14)  /* LP0 when CPU pwr gated */
 #define TEGRA_POWER_CPU_PWRREQ_POLARITY	(1 << 15)  /* CPU pwr req polarity */
 #define TEGRA_POWER_CPU_PWRREQ_OE	(1 << 16)  /* CPU pwr req enable */
@@ -238,6 +240,20 @@ void tegra_pmc_suspend_init(void)
 	reg = tegra_pmc_readl(PMC_CTRL);
 	reg |= TEGRA_POWER_CPU_PWRREQ_OE;
 	tegra_pmc_writel(reg, PMC_CTRL);
+
+	reg = tegra_pmc_readl(PMC_CTRL);
+
+	if (!pmc_pm_data.sysclkreq_high)
+		reg |= TEGRA_POWER_SYSCLK_POLARITY;
+	else
+		reg &= ~TEGRA_POWER_SYSCLK_POLARITY;
+
+	/* configure the output inverts while the request is tristated */
+	tegra_pmc_writel(reg, PMC_CTRL);
+
+	/* now enable the request */
+	reg |= TEGRA_POWER_SYSCLK_OE;
+	tegra_pmc_writel(reg, PMC_CTRL);
 }
 #endif
 
-- 
1.8.3.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 2/8] ARM: tegra: config the polarity of the request of sys clock
@ 2013-07-26  9:15     ` Joseph Lo
  0 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-07-26  9:15 UTC (permalink / raw)
  To: linux-arm-kernel

When suspending to LP1 mode, the SYSCLK will be clock gated. And different
board may have different polarity of the request of SYSCLK, this patch
configure the polarity from the DT for the board.

Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
 arch/arm/mach-tegra/pmc.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c
index 8345fcd..018bc87 100644
--- a/arch/arm/mach-tegra/pmc.c
+++ b/arch/arm/mach-tegra/pmc.c
@@ -27,6 +27,8 @@
 #include "pmc.h"
 #include "sleep.h"
 
+#define TEGRA_POWER_SYSCLK_POLARITY	(1 << 10)  /* sys clk polarity */
+#define TEGRA_POWER_SYSCLK_OE		(1 << 11)  /* system clock enable */
 #define TEGRA_POWER_EFFECT_LP0		(1 << 14)  /* LP0 when CPU pwr gated */
 #define TEGRA_POWER_CPU_PWRREQ_POLARITY	(1 << 15)  /* CPU pwr req polarity */
 #define TEGRA_POWER_CPU_PWRREQ_OE	(1 << 16)  /* CPU pwr req enable */
@@ -238,6 +240,20 @@ void tegra_pmc_suspend_init(void)
 	reg = tegra_pmc_readl(PMC_CTRL);
 	reg |= TEGRA_POWER_CPU_PWRREQ_OE;
 	tegra_pmc_writel(reg, PMC_CTRL);
+
+	reg = tegra_pmc_readl(PMC_CTRL);
+
+	if (!pmc_pm_data.sysclkreq_high)
+		reg |= TEGRA_POWER_SYSCLK_POLARITY;
+	else
+		reg &= ~TEGRA_POWER_SYSCLK_POLARITY;
+
+	/* configure the output inverts while the request is tristated */
+	tegra_pmc_writel(reg, PMC_CTRL);
+
+	/* now enable the request */
+	reg |= TEGRA_POWER_SYSCLK_OE;
+	tegra_pmc_writel(reg, PMC_CTRL);
 }
 #endif
 
-- 
1.8.3.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
  2013-07-26  9:15 ` Joseph Lo
@ 2013-07-26  9:15     ` Joseph Lo
  -1 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-07-26  9:15 UTC (permalink / raw)
  To: Stephen Warren
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Joseph Lo,
	Mike Turquette

When the system suspends to LP1, the clock of the CPU would be switched to
CLK_M (12MHz Oscillator) during suspend/resume flow. The clock driver
needs to restore the clock of CPU after LP1 resume.

Cc: Mike Turquette <mturquette-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Signed-off-by: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
 drivers/clk/tegra/clk-tegra114.c | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c
index f74ed19..b0e745a 100644
--- a/drivers/clk/tegra/clk-tegra114.c
+++ b/drivers/clk/tegra/clk-tegra114.c
@@ -22,6 +22,7 @@
 #include <linux/of_address.h>
 #include <linux/delay.h>
 #include <linux/export.h>
+#include <linux/syscore_ops.h>
 #include <linux/clk/tegra.h>
 
 #include "clk.h"
@@ -2332,6 +2333,33 @@ void tegra114_clock_deassert_dfll_dvco_reset(void)
 }
 EXPORT_SYMBOL(tegra114_clock_deassert_dfll_dvco_reset);
 
+#ifdef CONFIG_PM_SLEEP
+static u32 clk_rst_suspend[2];
+
+static int tegra114_clk_suspend(void)
+{
+	u32 *ctx = clk_rst_suspend;
+
+	*ctx++ = readl_relaxed(clk_base + CCLKG_BURST_POLICY);
+	*ctx++ = readl_relaxed(clk_base + CCLKG_BURST_POLICY + 4);
+
+	return 0;
+}
+
+static void tegra114_clk_resume(void)
+{
+	u32 *ctx = clk_rst_suspend;
+
+	writel_relaxed(*ctx++, clk_base + CCLKG_BURST_POLICY);
+	writel_relaxed(*ctx++, clk_base + CCLKG_BURST_POLICY + 4);
+}
+
+static struct syscore_ops tegra114_clk_syscore_ops = {
+	.suspend = tegra114_clk_suspend,
+	.resume = tegra114_clk_resume,
+};
+#endif
+
 static void __init tegra114_clock_init(struct device_node *np)
 {
 	struct device_node *node;
@@ -2384,5 +2412,9 @@ static void __init tegra114_clock_init(struct device_node *np)
 	tegra_clk_apply_init_table = tegra114_clock_apply_init_table;
 
 	tegra_cpu_car_ops = &tegra114_cpu_car_ops;
+
+#ifdef CONFIG_PM_SLEEP
+	register_syscore_ops(&tegra114_clk_syscore_ops);
+#endif
 }
 CLK_OF_DECLARE(tegra114, "nvidia,tegra114-car", tegra114_clock_init);
-- 
1.8.3.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
@ 2013-07-26  9:15     ` Joseph Lo
  0 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-07-26  9:15 UTC (permalink / raw)
  To: linux-arm-kernel

When the system suspends to LP1, the clock of the CPU would be switched to
CLK_M (12MHz Oscillator) during suspend/resume flow. The clock driver
needs to restore the clock of CPU after LP1 resume.

Cc: Mike Turquette <mturquette@linaro.org>
Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
 drivers/clk/tegra/clk-tegra114.c | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c
index f74ed19..b0e745a 100644
--- a/drivers/clk/tegra/clk-tegra114.c
+++ b/drivers/clk/tegra/clk-tegra114.c
@@ -22,6 +22,7 @@
 #include <linux/of_address.h>
 #include <linux/delay.h>
 #include <linux/export.h>
+#include <linux/syscore_ops.h>
 #include <linux/clk/tegra.h>
 
 #include "clk.h"
@@ -2332,6 +2333,33 @@ void tegra114_clock_deassert_dfll_dvco_reset(void)
 }
 EXPORT_SYMBOL(tegra114_clock_deassert_dfll_dvco_reset);
 
+#ifdef CONFIG_PM_SLEEP
+static u32 clk_rst_suspend[2];
+
+static int tegra114_clk_suspend(void)
+{
+	u32 *ctx = clk_rst_suspend;
+
+	*ctx++ = readl_relaxed(clk_base + CCLKG_BURST_POLICY);
+	*ctx++ = readl_relaxed(clk_base + CCLKG_BURST_POLICY + 4);
+
+	return 0;
+}
+
+static void tegra114_clk_resume(void)
+{
+	u32 *ctx = clk_rst_suspend;
+
+	writel_relaxed(*ctx++, clk_base + CCLKG_BURST_POLICY);
+	writel_relaxed(*ctx++, clk_base + CCLKG_BURST_POLICY + 4);
+}
+
+static struct syscore_ops tegra114_clk_syscore_ops = {
+	.suspend = tegra114_clk_suspend,
+	.resume = tegra114_clk_resume,
+};
+#endif
+
 static void __init tegra114_clock_init(struct device_node *np)
 {
 	struct device_node *node;
@@ -2384,5 +2412,9 @@ static void __init tegra114_clock_init(struct device_node *np)
 	tegra_clk_apply_init_table = tegra114_clock_apply_init_table;
 
 	tegra_cpu_car_ops = &tegra114_cpu_car_ops;
+
+#ifdef CONFIG_PM_SLEEP
+	register_syscore_ops(&tegra114_clk_syscore_ops);
+#endif
 }
 CLK_OF_DECLARE(tegra114, "nvidia,tegra114-car", tegra114_clock_init);
-- 
1.8.3.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 4/8] ARM: tegra: add common LP1 suspend support
  2013-07-26  9:15 ` Joseph Lo
@ 2013-07-26  9:15     ` Joseph Lo
  -1 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-07-26  9:15 UTC (permalink / raw)
  To: Stephen Warren
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Joseph Lo

The LP1 suspending mode on Tegra means CPU rail off, devices and PLLs are
clock gated and SDRAM in self-refresh mode. That means the low level LP1
suspending and resuming code couldn't be run on DRAM and the CPU must
switch to the always on clock domain (a.k.a. CLK_M 12MHz oscillator). And
the system clock (SCLK) would be switched to CLK_S, a 32KHz oscillator.
The LP1 low level handling code need to be moved to IRAM area first. And
marking the LP1 mask for indicating the Tegra device is in LP1. The CPU
power timer needs to be re-calculated based on 32KHz that was originally
based on PCLK.

When resuming from LP1, the LP1 reset handler will resume PLLs and then
put DRAM to normal mode. Then jumping to the "tegra_resume" that will
restore full context before back to kernel. The "tegra_resume" handler
was expected to be found in PMC_SCRATCH41 register.

This is common LP1 procedures for Tegra, so we do these jobs mainly in
this patch:
* moving LP1 low level handling code to IRAM
* marking LP1 mask
* copying the physical address of "tegra_resume" to PMC_SCRATCH41
* re-calculate the CPU power timer based on 32KHz

Signed-off-by: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
 arch/arm/mach-tegra/pm.c    | 97 ++++++++++++++++++++++++++++++++++++++++++---
 arch/arm/mach-tegra/pm.h    |  7 ++++
 arch/arm/mach-tegra/pmc.c   | 20 +++++++++-
 arch/arm/mach-tegra/pmc.h   |  3 ++
 arch/arm/mach-tegra/reset.h |  4 ++
 5 files changed, 125 insertions(+), 6 deletions(-)

diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index 5792872..45c9516 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -37,12 +37,18 @@
 #include "reset.h"
 #include "flowctrl.h"
 #include "fuse.h"
+#include "pm.h"
 #include "pmc.h"
 #include "sleep.h"
 
 #ifdef CONFIG_PM_SLEEP
 static DEFINE_SPINLOCK(tegra_lp2_lock);
+static void __iomem *iram_code = IO_ADDRESS(TEGRA_IRAM_CODE_AREA);
+static u32 iram_save_size;
+static void *iram_save_addr;
+struct tegra_lp1_iram *tegra_lp1_iram;
 void (*tegra_tear_down_cpu)(void);
+void (*tegra_sleep_core_finish)(unsigned long v2p);
 
 static void tegra_tear_down_cpu_init(void)
 {
@@ -174,14 +180,75 @@ enum tegra_suspend_mode tegra_pm_validate_suspend_mode(
 				enum tegra_suspend_mode mode)
 {
 	/*
-	 * The Tegra devices only support suspending to LP2 currently.
+	 * The Tegra devices only support suspending to LP1 currently.
 	 */
-	if (mode > TEGRA_SUSPEND_LP2)
-		return TEGRA_SUSPEND_LP2;
+	if (mode > TEGRA_SUSPEND_LP1)
+		return TEGRA_SUSPEND_LP1;
 
 	return mode;
 }
 
+static int tegra_sleep_core(unsigned long v2p)
+{
+	setup_mm_for_reboot();
+	tegra_sleep_core_finish(v2p);
+
+	/* should never here */
+	BUG();
+
+	return 0;
+}
+
+/*
+ * tegra_lp1_iram_hook
+ *
+ * Hooking the address of LP1 reset vector and SDRAM self-refresh code in
+ * SDRAM. These codes not be copied to IRAM in this fuction. We need to
+ * copy these code to IRAM before LP0/LP1 suspend and restore the content
+ * of IRAM after resume.
+ */
+static bool tegra_lp1_iram_hook(void)
+{
+	if (!tegra_lp1_iram)
+		return false;
+
+	iram_save_size = tegra_lp1_iram->end_addr - tegra_lp1_iram->start_addr;
+	iram_save_addr = kmalloc(iram_save_size, GFP_KERNEL);
+	if (!iram_save_addr)
+		return false;
+
+	return true;
+}
+
+static bool tegra_sleep_core_init(void)
+{
+	if (!tegra_sleep_core_finish)
+		return false;
+
+	return true;
+}
+
+static void tegra_suspend_enter_lp1(void)
+{
+	tegra_pmc_suspend();
+
+	/* copy the reset vector & SDRAM shutdown code into IRAM */
+	memcpy(iram_save_addr, iram_code, iram_save_size);
+	memcpy(iram_code, tegra_lp1_iram->start_addr, iram_save_size);
+
+	*((u32 *)tegra_cpu_lp1_mask) = 1;
+}
+
+static void tegra_suspend_exit_lp1(void)
+{
+	tegra_pmc_resume();
+
+	/* restore IRAM */
+	memcpy(iram_code, iram_save_addr, iram_save_size);
+
+	*(u32 *)tegra_cpu_lp1_mask = 0;
+}
+
 static const char *lp_state[TEGRA_MAX_SUSPEND_MODE] = {
 	[TEGRA_SUSPEND_NONE] = "none",
 	[TEGRA_SUSPEND_LP2] = "LP2",
@@ -205,6 +272,9 @@ static int tegra_suspend_enter(suspend_state_t state)
 
 	suspend_cpu_complex();
 	switch (mode) {
+	case TEGRA_SUSPEND_LP1:
+		tegra_suspend_enter_lp1();
+		break;
 	case TEGRA_SUSPEND_LP2:
 		tegra_set_cpu_in_lp2();
 		break;
@@ -212,9 +282,15 @@ static int tegra_suspend_enter(suspend_state_t state)
 		break;
 	}
 
-	cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu);
+	if (mode == TEGRA_SUSPEND_LP2)
+		cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu);
+	else
+		cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_core);
 
 	switch (mode) {
+	case TEGRA_SUSPEND_LP1:
+		tegra_suspend_exit_lp1();
+		break;
 	case TEGRA_SUSPEND_LP2:
 		tegra_clear_cpu_in_lp2();
 		break;
@@ -235,12 +311,23 @@ static const struct platform_suspend_ops tegra_suspend_ops = {
 
 void __init tegra_init_suspend(void)
 {
-	if (tegra_pmc_get_suspend_mode() == TEGRA_SUSPEND_NONE)
+	enum tegra_suspend_mode mode = tegra_pmc_get_suspend_mode();
+
+	if (mode == TEGRA_SUSPEND_NONE)
 		return;
 
 	tegra_tear_down_cpu_init();
 	tegra_pmc_suspend_init();
 
+	if (mode >= TEGRA_SUSPEND_LP1) {
+		if (!tegra_lp1_iram_hook() || !tegra_sleep_core_init()) {
+			pr_err("%s: unable to allocate memory for SDRAM"
+			       "self-refresh -- LP0/LP1 unavailable\n",
+			       __func__);
+			tegra_pmc_set_suspend_mode(TEGRA_SUSPEND_LP2);
+		}
+	}
+
 	suspend_set_ops(&tegra_suspend_ops);
 }
 #endif
diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h
index 94c4b9d..3cb9309 100644
--- a/arch/arm/mach-tegra/pm.h
+++ b/arch/arm/mach-tegra/pm.h
@@ -23,6 +23,13 @@
 
 #include "pmc.h"
 
+struct tegra_lp1_iram {
+	void	*start_addr;
+	void	*end_addr;
+};
+extern struct tegra_lp1_iram *tegra_lp1_iram;
+extern void (*tegra_sleep_core_finish)(unsigned long v2p);
+
 extern unsigned long l2x0_saved_regs_addr;
 
 void save_cpu_arch_register(void);
diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c
index 018bc87..dff239f 100644
--- a/arch/arm/mach-tegra/pmc.c
+++ b/arch/arm/mach-tegra/pmc.c
@@ -196,10 +196,28 @@ enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void)
 	return pmc_pm_data.suspend_mode;
 }
 
+void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode)
+{
+	if (mode < TEGRA_SUSPEND_NONE || mode >= TEGRA_MAX_SUSPEND_MODE)
+		return;
+
+	pmc_pm_data.suspend_mode = mode;
+}
+
+void tegra_pmc_suspend(void)
+{
+	tegra_pmc_writel(virt_to_phys(tegra_resume), PMC_SCRATCH41);
+}
+
+void tegra_pmc_resume(void)
+{
+	tegra_pmc_writel(0x0, PMC_SCRATCH41);
+}
+
 void tegra_pmc_pm_set(enum tegra_suspend_mode mode)
 {
 	u32 reg, csr_reg;
-	unsigned long rate = 0;
+	unsigned long rate = 32768;
 
 	reg = tegra_pmc_readl(PMC_CTRL);
 	reg |= TEGRA_POWER_CPU_PWRREQ_OE;
diff --git a/arch/arm/mach-tegra/pmc.h b/arch/arm/mach-tegra/pmc.h
index e1c2df2..549f8c7 100644
--- a/arch/arm/mach-tegra/pmc.h
+++ b/arch/arm/mach-tegra/pmc.h
@@ -28,6 +28,9 @@ enum tegra_suspend_mode {
 
 #ifdef CONFIG_PM_SLEEP
 enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void);
+void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode);
+void tegra_pmc_suspend(void);
+void tegra_pmc_resume(void);
 void tegra_pmc_pm_set(enum tegra_suspend_mode mode);
 void tegra_pmc_suspend_init(void);
 #endif
diff --git a/arch/arm/mach-tegra/reset.h b/arch/arm/mach-tegra/reset.h
index c90d8e9..76a9343 100644
--- a/arch/arm/mach-tegra/reset.h
+++ b/arch/arm/mach-tegra/reset.h
@@ -39,6 +39,10 @@ void __tegra_cpu_reset_handler_end(void);
 void tegra_secondary_startup(void);
 
 #ifdef CONFIG_PM_SLEEP
+#define tegra_cpu_lp1_mask \
+	(IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \
+	((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP1] - \
+	 (u32)__tegra_cpu_reset_handler_start)))
 #define tegra_cpu_lp2_mask \
 	(IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \
 	((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP2] - \
-- 
1.8.3.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 4/8] ARM: tegra: add common LP1 suspend support
@ 2013-07-26  9:15     ` Joseph Lo
  0 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-07-26  9:15 UTC (permalink / raw)
  To: linux-arm-kernel

The LP1 suspending mode on Tegra means CPU rail off, devices and PLLs are
clock gated and SDRAM in self-refresh mode. That means the low level LP1
suspending and resuming code couldn't be run on DRAM and the CPU must
switch to the always on clock domain (a.k.a. CLK_M 12MHz oscillator). And
the system clock (SCLK) would be switched to CLK_S, a 32KHz oscillator.
The LP1 low level handling code need to be moved to IRAM area first. And
marking the LP1 mask for indicating the Tegra device is in LP1. The CPU
power timer needs to be re-calculated based on 32KHz that was originally
based on PCLK.

When resuming from LP1, the LP1 reset handler will resume PLLs and then
put DRAM to normal mode. Then jumping to the "tegra_resume" that will
restore full context before back to kernel. The "tegra_resume" handler
was expected to be found in PMC_SCRATCH41 register.

This is common LP1 procedures for Tegra, so we do these jobs mainly in
this patch:
* moving LP1 low level handling code to IRAM
* marking LP1 mask
* copying the physical address of "tegra_resume" to PMC_SCRATCH41
* re-calculate the CPU power timer based on 32KHz

Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
 arch/arm/mach-tegra/pm.c    | 97 ++++++++++++++++++++++++++++++++++++++++++---
 arch/arm/mach-tegra/pm.h    |  7 ++++
 arch/arm/mach-tegra/pmc.c   | 20 +++++++++-
 arch/arm/mach-tegra/pmc.h   |  3 ++
 arch/arm/mach-tegra/reset.h |  4 ++
 5 files changed, 125 insertions(+), 6 deletions(-)

diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index 5792872..45c9516 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -37,12 +37,18 @@
 #include "reset.h"
 #include "flowctrl.h"
 #include "fuse.h"
+#include "pm.h"
 #include "pmc.h"
 #include "sleep.h"
 
 #ifdef CONFIG_PM_SLEEP
 static DEFINE_SPINLOCK(tegra_lp2_lock);
+static void __iomem *iram_code = IO_ADDRESS(TEGRA_IRAM_CODE_AREA);
+static u32 iram_save_size;
+static void *iram_save_addr;
+struct tegra_lp1_iram *tegra_lp1_iram;
 void (*tegra_tear_down_cpu)(void);
+void (*tegra_sleep_core_finish)(unsigned long v2p);
 
 static void tegra_tear_down_cpu_init(void)
 {
@@ -174,14 +180,75 @@ enum tegra_suspend_mode tegra_pm_validate_suspend_mode(
 				enum tegra_suspend_mode mode)
 {
 	/*
-	 * The Tegra devices only support suspending to LP2 currently.
+	 * The Tegra devices only support suspending to LP1 currently.
 	 */
-	if (mode > TEGRA_SUSPEND_LP2)
-		return TEGRA_SUSPEND_LP2;
+	if (mode > TEGRA_SUSPEND_LP1)
+		return TEGRA_SUSPEND_LP1;
 
 	return mode;
 }
 
+static int tegra_sleep_core(unsigned long v2p)
+{
+	setup_mm_for_reboot();
+	tegra_sleep_core_finish(v2p);
+
+	/* should never here */
+	BUG();
+
+	return 0;
+}
+
+/*
+ * tegra_lp1_iram_hook
+ *
+ * Hooking the address of LP1 reset vector and SDRAM self-refresh code in
+ * SDRAM. These codes not be copied to IRAM in this fuction. We need to
+ * copy these code to IRAM before LP0/LP1 suspend and restore the content
+ * of IRAM after resume.
+ */
+static bool tegra_lp1_iram_hook(void)
+{
+	if (!tegra_lp1_iram)
+		return false;
+
+	iram_save_size = tegra_lp1_iram->end_addr - tegra_lp1_iram->start_addr;
+	iram_save_addr = kmalloc(iram_save_size, GFP_KERNEL);
+	if (!iram_save_addr)
+		return false;
+
+	return true;
+}
+
+static bool tegra_sleep_core_init(void)
+{
+	if (!tegra_sleep_core_finish)
+		return false;
+
+	return true;
+}
+
+static void tegra_suspend_enter_lp1(void)
+{
+	tegra_pmc_suspend();
+
+	/* copy the reset vector & SDRAM shutdown code into IRAM */
+	memcpy(iram_save_addr, iram_code, iram_save_size);
+	memcpy(iram_code, tegra_lp1_iram->start_addr, iram_save_size);
+
+	*((u32 *)tegra_cpu_lp1_mask) = 1;
+}
+
+static void tegra_suspend_exit_lp1(void)
+{
+	tegra_pmc_resume();
+
+	/* restore IRAM */
+	memcpy(iram_code, iram_save_addr, iram_save_size);
+
+	*(u32 *)tegra_cpu_lp1_mask = 0;
+}
+
 static const char *lp_state[TEGRA_MAX_SUSPEND_MODE] = {
 	[TEGRA_SUSPEND_NONE] = "none",
 	[TEGRA_SUSPEND_LP2] = "LP2",
@@ -205,6 +272,9 @@ static int tegra_suspend_enter(suspend_state_t state)
 
 	suspend_cpu_complex();
 	switch (mode) {
+	case TEGRA_SUSPEND_LP1:
+		tegra_suspend_enter_lp1();
+		break;
 	case TEGRA_SUSPEND_LP2:
 		tegra_set_cpu_in_lp2();
 		break;
@@ -212,9 +282,15 @@ static int tegra_suspend_enter(suspend_state_t state)
 		break;
 	}
 
-	cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu);
+	if (mode == TEGRA_SUSPEND_LP2)
+		cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu);
+	else
+		cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_core);
 
 	switch (mode) {
+	case TEGRA_SUSPEND_LP1:
+		tegra_suspend_exit_lp1();
+		break;
 	case TEGRA_SUSPEND_LP2:
 		tegra_clear_cpu_in_lp2();
 		break;
@@ -235,12 +311,23 @@ static const struct platform_suspend_ops tegra_suspend_ops = {
 
 void __init tegra_init_suspend(void)
 {
-	if (tegra_pmc_get_suspend_mode() == TEGRA_SUSPEND_NONE)
+	enum tegra_suspend_mode mode = tegra_pmc_get_suspend_mode();
+
+	if (mode == TEGRA_SUSPEND_NONE)
 		return;
 
 	tegra_tear_down_cpu_init();
 	tegra_pmc_suspend_init();
 
+	if (mode >= TEGRA_SUSPEND_LP1) {
+		if (!tegra_lp1_iram_hook() || !tegra_sleep_core_init()) {
+			pr_err("%s: unable to allocate memory for SDRAM"
+			       "self-refresh -- LP0/LP1 unavailable\n",
+			       __func__);
+			tegra_pmc_set_suspend_mode(TEGRA_SUSPEND_LP2);
+		}
+	}
+
 	suspend_set_ops(&tegra_suspend_ops);
 }
 #endif
diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h
index 94c4b9d..3cb9309 100644
--- a/arch/arm/mach-tegra/pm.h
+++ b/arch/arm/mach-tegra/pm.h
@@ -23,6 +23,13 @@
 
 #include "pmc.h"
 
+struct tegra_lp1_iram {
+	void	*start_addr;
+	void	*end_addr;
+};
+extern struct tegra_lp1_iram *tegra_lp1_iram;
+extern void (*tegra_sleep_core_finish)(unsigned long v2p);
+
 extern unsigned long l2x0_saved_regs_addr;
 
 void save_cpu_arch_register(void);
diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c
index 018bc87..dff239f 100644
--- a/arch/arm/mach-tegra/pmc.c
+++ b/arch/arm/mach-tegra/pmc.c
@@ -196,10 +196,28 @@ enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void)
 	return pmc_pm_data.suspend_mode;
 }
 
+void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode)
+{
+	if (mode < TEGRA_SUSPEND_NONE || mode >= TEGRA_MAX_SUSPEND_MODE)
+		return;
+
+	pmc_pm_data.suspend_mode = mode;
+}
+
+void tegra_pmc_suspend(void)
+{
+	tegra_pmc_writel(virt_to_phys(tegra_resume), PMC_SCRATCH41);
+}
+
+void tegra_pmc_resume(void)
+{
+	tegra_pmc_writel(0x0, PMC_SCRATCH41);
+}
+
 void tegra_pmc_pm_set(enum tegra_suspend_mode mode)
 {
 	u32 reg, csr_reg;
-	unsigned long rate = 0;
+	unsigned long rate = 32768;
 
 	reg = tegra_pmc_readl(PMC_CTRL);
 	reg |= TEGRA_POWER_CPU_PWRREQ_OE;
diff --git a/arch/arm/mach-tegra/pmc.h b/arch/arm/mach-tegra/pmc.h
index e1c2df2..549f8c7 100644
--- a/arch/arm/mach-tegra/pmc.h
+++ b/arch/arm/mach-tegra/pmc.h
@@ -28,6 +28,9 @@ enum tegra_suspend_mode {
 
 #ifdef CONFIG_PM_SLEEP
 enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void);
+void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode);
+void tegra_pmc_suspend(void);
+void tegra_pmc_resume(void);
 void tegra_pmc_pm_set(enum tegra_suspend_mode mode);
 void tegra_pmc_suspend_init(void);
 #endif
diff --git a/arch/arm/mach-tegra/reset.h b/arch/arm/mach-tegra/reset.h
index c90d8e9..76a9343 100644
--- a/arch/arm/mach-tegra/reset.h
+++ b/arch/arm/mach-tegra/reset.h
@@ -39,6 +39,10 @@ void __tegra_cpu_reset_handler_end(void);
 void tegra_secondary_startup(void);
 
 #ifdef CONFIG_PM_SLEEP
+#define tegra_cpu_lp1_mask \
+	(IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \
+	((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP1] - \
+	 (u32)__tegra_cpu_reset_handler_start)))
 #define tegra_cpu_lp2_mask \
 	(IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \
 	((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP2] - \
-- 
1.8.3.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 5/8] ARM: tegra30: add LP1 suspend support
  2013-07-26  9:15 ` Joseph Lo
@ 2013-07-26  9:15     ` Joseph Lo
  -1 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-07-26  9:15 UTC (permalink / raw)
  To: Stephen Warren
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Joseph Lo

The LP1 suspend mode will power off the CPU, clock gated the PLLs and put
SDRAM to self-refresh mode. Any interrupt can wake up device from LP1. The
sequence when LP1 suspending:

* tunning off L1 data cache and the MMU
* storing some EMC registers, DPD (deep power down) status, clk source of
  mselect and SCLK burst policy
* putting SDRAM into self-refresh
* switching CPU to CLK_M (12MHz OSC)
* tunning off PLLM, PLLP, PLLA, PLLC and PLLX
* switching SCLK to CLK_S (32KHz OSC)
* shutting off the CPU rail

The sequence of LP1 resuming:

* re-enabling PLLM, PLLP, PLLA, PLLC and PLLX
* restoring the clk source of mselect and SCLK burst policy
* setting up CCLK burst policy to PLLX
* restoring DPD status and some EMC registers
* resuming SDRAM to normal mode
* jumping to the "tegra_resume" from PMC_SCRATCH41

Due to the SDRAM will be put into self-refresh mode, the low level
procedures of LP1 suspending and resuming should be copied to
TEGRA_IRAM_CODE_AREA (TEGRA_IRAM_BASE + SZ_4K) when suspending. Before
restoring the CPU context when resuming, the SDRAM needs to be switched
back to normal mode. And the PLLs need to be re-enabled, SCLK burst policy
be restored, CCLK burst policy be set in PLLX. Then jumping to
"tegra_resume" that was expected to be stored in PMC_SCRATCH41 to restore
CPU context and back to kernel.

Based on the work by: Scott Williams <scwilliams-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

Signed-off-by: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
 arch/arm/mach-tegra/Makefile        |   1 +
 arch/arm/mach-tegra/pm-tegra30.c    |  37 +++
 arch/arm/mach-tegra/pm.c            |  16 ++
 arch/arm/mach-tegra/pm.h            |   8 +
 arch/arm/mach-tegra/sleep-tegra30.S | 491 ++++++++++++++++++++++++++++++++++++
 arch/arm/mach-tegra/sleep.S         |   8 +-
 arch/arm/mach-tegra/sleep.h         |  16 ++
 7 files changed, 573 insertions(+), 4 deletions(-)
 create mode 100644 arch/arm/mach-tegra/pm-tegra30.c

diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 98b184e..d341980 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= cpuidle-tegra20.o
 endif
 obj-$(CONFIG_ARCH_TEGRA_3x_SOC)		+= tegra30_speedo.o
 obj-$(CONFIG_ARCH_TEGRA_3x_SOC)		+= sleep-tegra30.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC)		+= pm-tegra30.o
 ifeq ($(CONFIG_CPU_IDLE),y)
 obj-$(CONFIG_ARCH_TEGRA_3x_SOC)		+= cpuidle-tegra30.o
 endif
diff --git a/arch/arm/mach-tegra/pm-tegra30.c b/arch/arm/mach-tegra/pm-tegra30.c
new file mode 100644
index 0000000..6786955
--- /dev/null
+++ b/arch/arm/mach-tegra/pm-tegra30.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+
+#include "pm.h"
+
+#ifdef CONFIG_PM_SLEEP
+static struct tegra_lp1_iram tegra30_lp1_iram;
+extern u32 tegra30_iram_start, tegra30_iram_end;
+extern void tegra30_sleep_core_finish(unsigned long);
+
+void tegra30_lp1_iram_hook(void)
+{
+	tegra30_lp1_iram.start_addr = &tegra30_iram_start;
+	tegra30_lp1_iram.end_addr = &tegra30_iram_end;
+
+	tegra_lp1_iram = &tegra30_lp1_iram;
+}
+
+void tegra30_sleep_core_init(void)
+{
+	tegra_sleep_core_finish = tegra30_sleep_core_finish;
+}
+#endif
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index 45c9516..77dbb22 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -209,6 +209,14 @@ static int tegra_sleep_core(unsigned long v2p)
  */
 static bool tegra_lp1_iram_hook(void)
 {
+	switch (tegra_chip_id) {
+	case TEGRA30:
+		tegra30_lp1_iram_hook();
+		break;
+	default:
+		break;
+	}
+
 	if (!tegra_lp1_iram)
 		return false;
 
@@ -222,6 +230,14 @@ static bool tegra_lp1_iram_hook(void)
 
 static bool tegra_sleep_core_init(void)
 {
+	switch (tegra_chip_id) {
+	case TEGRA30:
+		tegra30_sleep_core_init();
+		break;
+	default:
+		break;
+	}
+
 	if (!tegra_sleep_core_finish)
 		return false;
 
diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h
index 3cb9309..32d2898 100644
--- a/arch/arm/mach-tegra/pm.h
+++ b/arch/arm/mach-tegra/pm.h
@@ -30,6 +30,14 @@ struct tegra_lp1_iram {
 extern struct tegra_lp1_iram *tegra_lp1_iram;
 extern void (*tegra_sleep_core_finish)(unsigned long v2p);
 
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+void tegra30_lp1_iram_hook(void);
+void tegra30_sleep_core_init(void);
+#else
+static inline void tegra30_lp1_iram_hook(void) {}
+static inline void void tegra30_sleep_core_init(void) {}
+#endif
+
 extern unsigned long l2x0_saved_regs_addr;
 
 void save_cpu_arch_register(void);
diff --git a/arch/arm/mach-tegra/sleep-tegra30.S b/arch/arm/mach-tegra/sleep-tegra30.S
index ecad4ea..5d2522a 100644
--- a/arch/arm/mach-tegra/sleep-tegra30.S
+++ b/arch/arm/mach-tegra/sleep-tegra30.S
@@ -18,13 +18,96 @@
 
 #include <asm/assembler.h>
 #include <asm/asm-offsets.h>
+#include <asm/cache.h>
 
 #include "fuse.h"
 #include "sleep.h"
 #include "flowctrl.h"
 
+#define EMC_CFG				0xc
+#define EMC_ADR_CFG			0x10
+#define EMC_TIMING_CONTROL		0x28
+#define EMC_REFRESH			0x70
+#define EMC_NOP				0xdc
+#define EMC_SELF_REF			0xe0
+#define EMC_MRW				0xe8
+#define EMC_FBIO_CFG5			0x104
+#define EMC_AUTO_CAL_CONFIG		0x2a4
+#define EMC_AUTO_CAL_INTERVAL		0x2a8
+#define EMC_AUTO_CAL_STATUS		0x2ac
+#define EMC_REQ_CTRL			0x2b0
+#define EMC_CFG_DIG_DLL			0x2bc
+#define EMC_EMC_STATUS			0x2b4
+#define EMC_ZCAL_INTERVAL		0x2e0
+#define EMC_ZQ_CAL			0x2ec
+#define EMC_XM2VTTGENPADCTRL		0x310
+#define EMC_XM2VTTGENPADCTRL2		0x314
+
+#define PMC_CTRL			0x0
+#define PMC_CTRL_SIDE_EFFECT_LP0 (1 << 14) /* enter LP0 when CPU pwr gated */
+
+#define PMC_PLLP_WB0_OVERRIDE		0xf8
+#define PMC_IO_DPD_REQ			0x1b8
+#define PMC_IO_DPD_STATUS		0x1bc
+
+#define CLK_RESET_CCLK_BURST		0x20
+#define CLK_RESET_CCLK_DIVIDER		0x24
+#define CLK_RESET_SCLK_BURST		0x28
+#define CLK_RESET_SCLK_DIVIDER		0x2c
+
+#define CLK_RESET_PLLC_BASE		0x80
+#define CLK_RESET_PLLC_MISC		0x8c
+#define CLK_RESET_PLLM_BASE		0x90
+#define CLK_RESET_PLLM_MISC		0x9c
+#define CLK_RESET_PLLP_BASE		0xa0
+#define CLK_RESET_PLLP_MISC		0xac
+#define CLK_RESET_PLLA_BASE		0xb0
+#define CLK_RESET_PLLA_MISC		0xbc
+#define CLK_RESET_PLLX_BASE		0xe0
+#define CLK_RESET_PLLX_MISC		0xe4
+
+#define CLK_RESET_CLK_SOURCE_MSELECT	0x3b4
+
+#define MSELECT_CLKM			(0x3 << 30)
+
+#define LOCK_DELAY 50 /* safety delay after lock is detected */
+
 #define TEGRA30_POWER_HOTPLUG_SHUTDOWN	(1 << 27) /* Hotplug shutdown */
 
+.macro emc_device_mask, rd, base
+	ldr	\rd, [\base, #EMC_ADR_CFG]
+	tst	\rd, #0x1
+	moveq	\rd, #(0x1 << 8)		@ just 1 device
+	movne	\rd, #(0x3 << 8)		@ 2 devices
+.endm
+
+.macro emc_timing_update, rd, base
+	mov	\rd, #1
+	str	\rd, [\base, #EMC_TIMING_CONTROL]
+1001:
+	ldr	\rd, [\base, #EMC_EMC_STATUS]
+	tst	\rd, #(0x1<<23)	@ wait EMC_STATUS_TIMING_UPDATE_STALLED is clear
+	bne	1001b
+.endm
+
+.macro pll_enable, rd, r_car_base, pll_base, pll_misc
+	ldr	\rd, [\r_car_base, #\pll_base]
+	tst	\rd, #(1 << 30)
+	orreq	\rd, \rd, #(1 << 30)
+	streq	\rd, [\r_car_base, #\pll_base]
+	/* Enable lock detector */
+	ldr	\rd, [\r_car_base, #\pll_misc]
+	orr	\rd, \rd, #(1 << 18)
+	str	\rd, [\r_car_base, #\pll_misc]
+.endm
+
+.macro pll_locked, rd, r_car_base, pll_base
+1:
+	ldr	\rd, [\r_car_base, #\pll_base]
+	tst	\rd, #(1 << 27)
+	beq	1b
+.endm
+
 #if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP)
 /*
  * tegra30_hotplug_shutdown(void)
@@ -129,6 +212,41 @@ ENDPROC(tegra30_cpu_shutdown)
 
 #ifdef CONFIG_PM_SLEEP
 /*
+ * tegra30_sleep_core_finish(unsigned long v2p)
+ *
+ * Enters suspend in LP0 or LP1 by turning off the MMU and jumping to
+ * tegra30_tear_down_core in IRAM
+ */
+ENTRY(tegra30_sleep_core_finish)
+	/* Flush, disable the L1 data cache and exit SMP */
+	bl	tegra_disable_clean_inv_dcache
+
+	/*
+	 * Preload all the address literals that are needed for the
+	 * CPU power-gating process, to avoid loading from SDRAM which
+	 * are not supported once SDRAM is put into self-refresh.
+	 * LP0 / LP1 use physical address, since the MMU needs to be
+	 * disalbed before putting SDRAM into self-refresh to avoid
+	 * memory access due to page table walks.
+	 */
+	mov32	r4, TEGRA_PMC_BASE
+	mov32	r5, TEGRA_CLK_RESET_BASE
+	mov32	r6, TEGRA_FLOW_CTRL_BASE
+	mov32	r7, TEGRA_TMRUS_BASE
+
+	mov32	r3, tegra_shut_off_mmu
+	add	r3, r3, r0
+
+	mov32	r0, tegra30_tear_down_core
+	mov32	r1, tegra30_iram_start
+	sub	r0, r0, r1
+	mov32	r1, TEGRA_IRAM_CODE_AREA
+	add	r0, r0, r1
+
+	mov	pc, r3
+ENDPROC(tegra30_sleep_core_finish)
+
+/*
  * tegra30_sleep_cpu_secondary_finish(unsigned long v2p)
  *
  * Enters LP2 on secondary CPU by exiting coherency and powergating the CPU.
@@ -158,6 +276,278 @@ ENTRY(tegra30_tear_down_cpu)
 	b	tegra30_enter_sleep
 ENDPROC(tegra30_tear_down_cpu)
 
+/* START OF ROUTINES COPIED TO IRAM */
+	.align L1_CACHE_SHIFT
+	.globl tegra30_iram_start
+tegra30_iram_start:
+
+/*
+ * tegra30_lp1_reset
+ *
+ * reset vector for LP1 restore; copied into IRAM during suspend.
+ * Brings the system back up to a safe staring point (SDRAM out of
+ * self-refresh, PLLC, PLLM and PLLP reenabled, CPU running on PLLX,
+ * system clock running on the same PLL that it suspended at), and
+ * jumps to tegra_resume to restore virtual addressing.
+ * The physical address of tegra_resume expected to be stored in
+ * PMC_SCRATCH41.
+ *
+ * NOTE: THIS *MUST* BE RELOCATED TO TEGRA_IRAM_CODE_AREA AND MUST BE FIRST.
+ */
+ENTRY(tegra30_lp1_reset)
+	/*
+	 * The CPU and system bus are running at 32KHz and executing from
+	 * IRAM when this code is executed; immediately switch to CLKM and
+	 * enable PLLP, PLLM, PLLC, PLLA and PLLX.
+	 */
+	mov32	r0, TEGRA_CLK_RESET_BASE
+
+	mov	r1, #(1 << 28)
+	str	r1, [r0, #CLK_RESET_SCLK_BURST]
+	str	r1, [r0, #CLK_RESET_CCLK_BURST]
+	mov	r1, #0
+	str	r1, [r0, #CLK_RESET_CCLK_DIVIDER]
+	str	r1, [r0, #CLK_RESET_SCLK_DIVIDER]
+
+	/* enable PLLM via PMC */
+	mov32	r2, TEGRA_PMC_BASE
+	ldr	r1, [r2, #PMC_PLLP_WB0_OVERRIDE]
+	orr	r1, r1, #(1 << 12)
+	str	r1, [r2, #PMC_PLLP_WB0_OVERRIDE]
+
+	pll_enable r1, r0, CLK_RESET_PLLM_BASE, CLK_RESET_PLLM_MISC
+	pll_enable r1, r0, CLK_RESET_PLLP_BASE, CLK_RESET_PLLP_MISC
+	pll_enable r1, r0, CLK_RESET_PLLA_BASE, CLK_RESET_PLLA_MISC
+	pll_enable r1, r0, CLK_RESET_PLLC_BASE, CLK_RESET_PLLC_MISC
+	pll_enable r1, r0, CLK_RESET_PLLX_BASE, CLK_RESET_PLLX_MISC
+
+	pll_locked r1, r0, CLK_RESET_PLLM_BASE
+	pll_locked r1, r0, CLK_RESET_PLLP_BASE
+	pll_locked r1, r0, CLK_RESET_PLLA_BASE
+	pll_locked r1, r0, CLK_RESET_PLLC_BASE
+	pll_locked r1, r0, CLK_RESET_PLLX_BASE
+
+	mov32	r7, TEGRA_TMRUS_BASE
+	ldr	r1, [r7]
+	add	r1, r1, #LOCK_DELAY
+	wait_until r1, r7, r3
+
+	adr	r5, tegra30_sdram_pad_save
+
+	ldr	r4, [r5, #0x18]		@ restore CLK_SOURCE_MSELECT
+	str	r4, [r0, #CLK_RESET_CLK_SOURCE_MSELECT]
+
+	ldr	r4, [r5, #0x1C]		@ restore SCLK_BURST
+	str	r4, [r0, #CLK_RESET_SCLK_BURST]
+
+	mov32	r4, ((1 << 28) | (0x8))	@ burst policy is PLLX
+	str	r4, [r0, #CLK_RESET_CCLK_BURST]
+
+	/* Restore pad power state to normal */
+	ldr	r1, [r5, #0x14]		@ PMC_IO_DPD_STATUS
+	mvn	r1, r1
+	bic	r1, r1, #(1 << 31)
+	orr	r1, r1, #(1 << 30)
+	str	r1, [r2, #PMC_IO_DPD_REQ]	@ DPD_OFF
+
+	mov32	r0, TEGRA_EMC_BASE	@ r0 reserved for emc base
+
+	ldr	r1, [r5, #0xC]		@ restore EMC_XM2VTTGENPADCTRL
+	str	r1, [r0, #EMC_XM2VTTGENPADCTRL]
+	ldr	r1, [r5, #0x10]		@ restore EMC_XM2VTTGENPADCTRL2
+	str	r1, [r0, #EMC_XM2VTTGENPADCTRL2]
+	ldr	r1, [r5, #0x8]		@ restore EMC_AUTO_CAL_INTERVAL
+	str	r1, [r0, #EMC_AUTO_CAL_INTERVAL]
+
+	/* Relock DLL */
+	ldr	r1, [r0, #EMC_CFG_DIG_DLL]
+	orr	r1, r1, #(1 << 30)	@ set DLL_RESET
+	str	r1, [r0, #EMC_CFG_DIG_DLL]
+
+	emc_timing_update r1, r0
+
+	ldr	r1, [r0, #EMC_AUTO_CAL_CONFIG]
+	orr	r1, r1, #(1 << 31)	@ set AUTO_CAL_ACTIVE
+	str	r1, [r0, #EMC_AUTO_CAL_CONFIG]
+
+emc_wait_auto_cal_onetime:
+	ldr	r1, [r0, #EMC_AUTO_CAL_STATUS]
+	tst	r1, #(1 << 31)		@ wait until AUTO_CAL_ACTIVE is cleared
+	bne	emc_wait_auto_cal_onetime
+
+	ldr	r1, [r0, #EMC_CFG]
+	bic	r1, r1, #(1 << 31)	@ disable DRAM_CLK_STOP_PD
+	str	r1, [r0, #EMC_CFG]
+
+	mov	r1, #0
+	str	r1, [r0, #EMC_SELF_REF]	@ take DRAM out of self refresh
+	mov	r1, #1
+	str	r1, [r0, #EMC_NOP]
+	str	r1, [r0, #EMC_NOP]
+	str	r1, [r0, #EMC_REFRESH]
+
+	emc_device_mask r1, r0
+
+exit_selfrefresh_loop:
+	ldr	r2, [r0, #EMC_EMC_STATUS]
+	ands	r2, r2, r1
+	bne	exit_selfrefresh_loop
+
+	lsr	r1, r1, #8		@ devSel, bit0:dev0, bit1:dev1
+
+	mov32	r7, TEGRA_TMRUS_BASE
+	ldr	r2, [r0, #EMC_FBIO_CFG5]
+
+	and	r2, r2,	#3		@ check DRAM_TYPE
+	cmp	r2, #2
+	beq	emc_lpddr2
+
+	/* Issue a ZQ_CAL for dev0 - DDR3 */
+	mov32	r2, 0x80000011		@ DEV_SELECTION=2, LENGTH=LONG, CMD=1
+	str	r2, [r0, #EMC_ZQ_CAL]
+	ldr	r2, [r7]
+	add	r2, r2, #10
+	wait_until r2, r7, r3
+
+	tst	r1, #2
+	beq	zcal_done
+
+	/* Issue a ZQ_CAL for dev1 - DDR3 */
+	mov32	r2, 0x40000011		@ DEV_SELECTION=1, LENGTH=LONG, CMD=1
+	str	r2, [r0, #EMC_ZQ_CAL]
+	ldr	r2, [r7]
+	add	r2, r2, #10
+	wait_until r2, r7, r3
+	b	zcal_done
+
+emc_lpddr2:
+	/* Issue a ZQ_CAL for dev0 - LPDDR2 */
+	mov32	r2, 0x800A00AB		@ DEV_SELECTION=2, MA=10, OP=0xAB
+	str	r2, [r0, #EMC_MRW]
+	ldr	r2, [r7]
+	add	r2, r2, #1
+	wait_until r2, r7, r3
+
+	tst	r1, #2
+	beq	zcal_done
+
+	/* Issue a ZQ_CAL for dev0 - LPDDR2 */
+	mov32	r2, 0x400A00AB		@ DEV_SELECTION=1, MA=10, OP=0xAB
+	str	r2, [r0, #EMC_MRW]
+	ldr	r2, [r7]
+	add	r2, r2, #1
+	wait_until r2, r7, r3
+
+zcal_done:
+	mov	r1, #0			@ unstall all transactions
+	str	r1, [r0, #EMC_REQ_CTRL]
+	ldr	r1, [r5, #0x4]		@ restore EMC_ZCAL_INTERVAL
+	str	r1, [r0, #EMC_ZCAL_INTERVAL]
+	ldr	r1, [r5, #0x0]		@ restore EMC_CFG
+	str	r1, [r0, #EMC_CFG]
+
+	mov32	r0, TEGRA_PMC_BASE
+	ldr	r0, [r0, #PMC_SCRATCH41]
+	mov	pc, r0			@ jump to tegra_resume
+ENDPROC(tegra30_lp1_reset)
+
+	.align	L1_CACHE_SHIFT
+	.type	tegra30_sdram_pad_save, %object
+tegra30_sdram_pad_save:
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+
+tegra30_sdram_pad_address:
+	.word	TEGRA_EMC_BASE + EMC_CFG				@0x0
+	.word	TEGRA_EMC_BASE + EMC_ZCAL_INTERVAL			@0x4
+	.word	TEGRA_EMC_BASE + EMC_AUTO_CAL_INTERVAL			@0x8
+	.word	TEGRA_EMC_BASE + EMC_XM2VTTGENPADCTRL			@0xc
+	.word	TEGRA_EMC_BASE + EMC_XM2VTTGENPADCTRL2			@0x10
+	.word	TEGRA_PMC_BASE + PMC_IO_DPD_STATUS			@0x14
+	.word	TEGRA_CLK_RESET_BASE + CLK_RESET_CLK_SOURCE_MSELECT	@0x18
+	.word	TEGRA_CLK_RESET_BASE + CLK_RESET_SCLK_BURST		@0x1c
+
+tegra30_sdram_pad_size:
+	.word	tegra30_sdram_pad_address - tegra30_sdram_pad_save
+
+/*
+ * tegra30_tear_down_core
+ *
+ * copied into and executed from IRAM
+ * puts memory in self-refresh for LP0 and LP1
+ */
+tegra30_tear_down_core:
+	bl	tegra30_sdram_self_refresh
+	bl	tegra30_switch_cpu_to_clk32k
+	b	tegra30_enter_sleep
+
+/*
+ * tegra30_switch_cpu_to_clk32k
+ *
+ * In LP0 and LP1 all PLLs will be turned off. Switching the CPU and System CLK
+ * to the 32KHz clock.
+ * r4 = TEGRA_PMC_BASE
+ * r5 = TEGRA_CLK_RESET_BASE
+ * r6 = TEGRA_FLOW_CTRL_BASE
+ * r7 = TEGRA_TMRUS_BASE
+ */
+tegra30_switch_cpu_to_clk32k:
+	/*
+	 * start by jumping to CLKM to safely disable PLLs, then jump to
+	 * CLKS.
+	 */
+	mov	r0, #(1 << 28)
+	str	r0, [r5, #CLK_RESET_SCLK_BURST]
+	/* 2uS delay delay between changing SCLK and CCLK */
+	wait_for_us r1, r7, r9
+	add	r1, r1, #2
+	wait_until r1, r7, r9
+	str	r0, [r5, #CLK_RESET_CCLK_BURST]
+	mov	r0, #0
+	str	r0, [r5, #CLK_RESET_CCLK_DIVIDER]
+	str	r0, [r5, #CLK_RESET_SCLK_DIVIDER]
+
+	/* switch the clock source of mselect to be CLK_M */
+	ldr	r0, [r5, #CLK_RESET_CLK_SOURCE_MSELECT]
+	orr	r0, r0, #MSELECT_CLKM
+	str	r0, [r5, #CLK_RESET_CLK_SOURCE_MSELECT]
+
+	/* 2uS delay delay between changing SCLK and disabling PLLs */
+	wait_for_us r1, r7, r9
+	add	r1, r1, #2
+	wait_until r1, r7, r9
+
+	/* disable PLLM via PMC in LP1 */
+	ldr	r0, [r4, #PMC_PLLP_WB0_OVERRIDE]
+	bic	r0, r0, #(1 << 12)
+	str	r0, [r4, #PMC_PLLP_WB0_OVERRIDE]
+
+	/* disable PLLP, PLLA, PLLC and PLLX */
+	ldr	r0, [r5, #CLK_RESET_PLLP_BASE]
+	bic	r0, r0, #(1 << 30)
+	str	r0, [r5, #CLK_RESET_PLLP_BASE]
+	ldr	r0, [r5, #CLK_RESET_PLLA_BASE]
+	bic	r0, r0, #(1 << 30)
+	str	r0, [r5, #CLK_RESET_PLLA_BASE]
+	ldr	r0, [r5, #CLK_RESET_PLLC_BASE]
+	bic	r0, r0, #(1 << 30)
+	str	r0, [r5, #CLK_RESET_PLLC_BASE]
+	ldr	r0, [r5, #CLK_RESET_PLLX_BASE]
+	bic	r0, r0, #(1 << 30)
+	str	r0, [r5, #CLK_RESET_PLLX_BASE]
+
+	/* switch to CLKS */
+	mov	r0, #0	/* brust policy = 32KHz */
+	str	r0, [r5, #CLK_RESET_SCLK_BURST]
+
+	mov	pc, lr
+
 /*
  * tegra30_enter_sleep
  *
@@ -194,4 +584,105 @@ halted:
 	/* !!!FIXME!!! Implement halt failure handler */
 	b	halted
 
+/*
+ * tegra30_sdram_self_refresh
+ *
+ * called with MMU off and caches disabled
+ * must be executed from IRAM
+ * r4 = TEGRA_PMC_BASE
+ * r5 = TEGRA_CLK_RESET_BASE
+ * r6 = TEGRA_FLOW_CTRL_BASE
+ * r7 = TEGRA_TMRUS_BASE
+ */
+tegra30_sdram_self_refresh:
+
+	adr	r2, tegra30_sdram_pad_address
+	adr	r8, tegra30_sdram_pad_save
+	mov	r9, #0
+
+	ldr	r3, tegra30_sdram_pad_size
+padsave:
+	ldr	r0, [r2, r9]		@ r0 is the addr in the pad_address
+
+	ldr	r1, [r0]
+	str	r1, [r8, r9]		@ save the content of the addr
+
+	add	r9, r9, #4
+	cmp	r3, r9
+	bne	padsave
+padsave_done:
+
+	dsb
+
+	mov32	r0, TEGRA_EMC_BASE	@ r0 reserved for emc base addr
+
+	mov	r1, #0
+	str	r1, [r0, #EMC_ZCAL_INTERVAL]
+	str	r1, [r0, #EMC_AUTO_CAL_INTERVAL]
+	ldr	r1, [r0, #EMC_CFG]
+	bic	r1, r1, #(1 << 28)
+	str	r1, [r0, #EMC_CFG]	@ disable DYN_SELF_REF
+
+	emc_timing_update r1, r0
+
+	ldr	r1, [r7]
+	add	r1, r1, #5
+	wait_until r1, r7, r2
+
+emc_wait_auto_cal:
+	ldr	r1, [r0, #EMC_AUTO_CAL_STATUS]
+	tst	r1, #(1 << 31)		@ wait until AUTO_CAL_ACTIVE is cleared
+	bne	emc_wait_auto_cal
+
+	mov	r1, #3
+	str	r1, [r0, #EMC_REQ_CTRL]	@ stall incoming DRAM requests
+
+emcidle:
+	ldr	r1, [r0, #EMC_EMC_STATUS]
+	tst	r1, #4
+	beq	emcidle
+
+	mov	r1, #1
+	str	r1, [r0, #EMC_SELF_REF]
+
+	emc_device_mask r1, r0
+
+emcself:
+	ldr	r2, [r0, #EMC_EMC_STATUS]
+	and	r2, r2, r1
+	cmp	r2, r1
+	bne	emcself			@ loop until DDR in self-refresh
+
+	/* Put VTTGEN in the lowest power mode */
+	ldr	r1, [r0, #EMC_XM2VTTGENPADCTRL]
+	mov32	r2, 0xF8F8FFFF	@ clear XM2VTTGEN_DRVUP and XM2VTTGEN_DRVDN
+	and	r1, r1, r2
+	str	r1, [r0, #EMC_XM2VTTGENPADCTRL]
+	ldr	r1, [r0, #EMC_XM2VTTGENPADCTRL2]
+	orr	r1, r1, #7		@ set E_NO_VTTGEN
+	str	r1, [r0, #EMC_XM2VTTGENPADCTRL2]
+
+	emc_timing_update r1, r0
+
+	ldr	r1, [r4, #PMC_CTRL]
+	tst	r1, #PMC_CTRL_SIDE_EFFECT_LP0
+	bne	pmc_io_dpd_skip
+	/*
+	 * Put DDR_DATA, DISC_ADDR_CMD, DDR_ADDR_CMD, POP_ADDR_CMD, POP_CLK
+	 * and COMP in the lowest power mode when LP1.
+	 */
+	mov32	r1, 0x8EC00000
+	str	r1, [r4, #PMC_IO_DPD_REQ]
+pmc_io_dpd_skip:
+
+	dsb
+
+	mov	pc, lr
+
+	.ltorg
+/* dummy symbol for end of IRAM */
+	.align L1_CACHE_SHIFT
+	.global tegra30_iram_end
+tegra30_iram_end:
+	b	.
 #endif
diff --git a/arch/arm/mach-tegra/sleep.S b/arch/arm/mach-tegra/sleep.S
index 8388113..8d06213 100644
--- a/arch/arm/mach-tegra/sleep.S
+++ b/arch/arm/mach-tegra/sleep.S
@@ -134,10 +134,10 @@ ENTRY(tegra_shut_off_mmu)
 #ifdef CONFIG_CACHE_L2X0
 	/* Disable L2 cache */
 	check_cpu_part_num 0xc09, r9, r10
-	movweq	r4, #:lower16:(TEGRA_ARM_PERIF_BASE + 0x3000)
-	movteq	r4, #:upper16:(TEGRA_ARM_PERIF_BASE + 0x3000)
-	moveq	r5, #0
-	streq	r5, [r4, #L2X0_CTRL]
+	movweq	r2, #:lower16:(TEGRA_ARM_PERIF_BASE + 0x3000)
+	movteq	r2, #:upper16:(TEGRA_ARM_PERIF_BASE + 0x3000)
+	moveq	r3, #0
+	streq	r3, [r2, #L2X0_CTRL]
 #endif
 	mov	pc, r0
 ENDPROC(tegra_shut_off_mmu)
diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h
index e7f8b6f..cfce981 100644
--- a/arch/arm/mach-tegra/sleep.h
+++ b/arch/arm/mach-tegra/sleep.h
@@ -48,6 +48,22 @@
 #define TEGRA_FLUSH_CACHE_ALL	1
 
 #ifdef __ASSEMBLY__
+/* waits until the microsecond counter (base) ticks, for exact timing loops */
+.macro wait_for_us, rd, base, tmp
+	ldr	\rd, [\base]
+1001:	ldr	\tmp, [\base]
+	cmp	\rd, \tmp
+	beq	1001b
+.endm
+
+/* waits until the microsecond counter (base) is > rn */
+.macro wait_until, rn, base, tmp
+	add	\rn, \rn, #1
+1002:	ldr	\tmp, [\base]
+	cmp	\tmp, \rn
+	bmi	1002b
+.endm
+
 /* returns the offset of the flow controller halt register for a cpu */
 .macro cpu_to_halt_reg rd, rcpu
 	cmp	\rcpu, #0
-- 
1.8.3.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 5/8] ARM: tegra30: add LP1 suspend support
@ 2013-07-26  9:15     ` Joseph Lo
  0 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-07-26  9:15 UTC (permalink / raw)
  To: linux-arm-kernel

The LP1 suspend mode will power off the CPU, clock gated the PLLs and put
SDRAM to self-refresh mode. Any interrupt can wake up device from LP1. The
sequence when LP1 suspending:

* tunning off L1 data cache and the MMU
* storing some EMC registers, DPD (deep power down) status, clk source of
  mselect and SCLK burst policy
* putting SDRAM into self-refresh
* switching CPU to CLK_M (12MHz OSC)
* tunning off PLLM, PLLP, PLLA, PLLC and PLLX
* switching SCLK to CLK_S (32KHz OSC)
* shutting off the CPU rail

The sequence of LP1 resuming:

* re-enabling PLLM, PLLP, PLLA, PLLC and PLLX
* restoring the clk source of mselect and SCLK burst policy
* setting up CCLK burst policy to PLLX
* restoring DPD status and some EMC registers
* resuming SDRAM to normal mode
* jumping to the "tegra_resume" from PMC_SCRATCH41

Due to the SDRAM will be put into self-refresh mode, the low level
procedures of LP1 suspending and resuming should be copied to
TEGRA_IRAM_CODE_AREA (TEGRA_IRAM_BASE + SZ_4K) when suspending. Before
restoring the CPU context when resuming, the SDRAM needs to be switched
back to normal mode. And the PLLs need to be re-enabled, SCLK burst policy
be restored, CCLK burst policy be set in PLLX. Then jumping to
"tegra_resume" that was expected to be stored in PMC_SCRATCH41 to restore
CPU context and back to kernel.

Based on the work by: Scott Williams <scwilliams@nvidia.com>

Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
 arch/arm/mach-tegra/Makefile        |   1 +
 arch/arm/mach-tegra/pm-tegra30.c    |  37 +++
 arch/arm/mach-tegra/pm.c            |  16 ++
 arch/arm/mach-tegra/pm.h            |   8 +
 arch/arm/mach-tegra/sleep-tegra30.S | 491 ++++++++++++++++++++++++++++++++++++
 arch/arm/mach-tegra/sleep.S         |   8 +-
 arch/arm/mach-tegra/sleep.h         |  16 ++
 7 files changed, 573 insertions(+), 4 deletions(-)
 create mode 100644 arch/arm/mach-tegra/pm-tegra30.c

diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 98b184e..d341980 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= cpuidle-tegra20.o
 endif
 obj-$(CONFIG_ARCH_TEGRA_3x_SOC)		+= tegra30_speedo.o
 obj-$(CONFIG_ARCH_TEGRA_3x_SOC)		+= sleep-tegra30.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC)		+= pm-tegra30.o
 ifeq ($(CONFIG_CPU_IDLE),y)
 obj-$(CONFIG_ARCH_TEGRA_3x_SOC)		+= cpuidle-tegra30.o
 endif
diff --git a/arch/arm/mach-tegra/pm-tegra30.c b/arch/arm/mach-tegra/pm-tegra30.c
new file mode 100644
index 0000000..6786955
--- /dev/null
+++ b/arch/arm/mach-tegra/pm-tegra30.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+
+#include "pm.h"
+
+#ifdef CONFIG_PM_SLEEP
+static struct tegra_lp1_iram tegra30_lp1_iram;
+extern u32 tegra30_iram_start, tegra30_iram_end;
+extern void tegra30_sleep_core_finish(unsigned long);
+
+void tegra30_lp1_iram_hook(void)
+{
+	tegra30_lp1_iram.start_addr = &tegra30_iram_start;
+	tegra30_lp1_iram.end_addr = &tegra30_iram_end;
+
+	tegra_lp1_iram = &tegra30_lp1_iram;
+}
+
+void tegra30_sleep_core_init(void)
+{
+	tegra_sleep_core_finish = tegra30_sleep_core_finish;
+}
+#endif
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index 45c9516..77dbb22 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -209,6 +209,14 @@ static int tegra_sleep_core(unsigned long v2p)
  */
 static bool tegra_lp1_iram_hook(void)
 {
+	switch (tegra_chip_id) {
+	case TEGRA30:
+		tegra30_lp1_iram_hook();
+		break;
+	default:
+		break;
+	}
+
 	if (!tegra_lp1_iram)
 		return false;
 
@@ -222,6 +230,14 @@ static bool tegra_lp1_iram_hook(void)
 
 static bool tegra_sleep_core_init(void)
 {
+	switch (tegra_chip_id) {
+	case TEGRA30:
+		tegra30_sleep_core_init();
+		break;
+	default:
+		break;
+	}
+
 	if (!tegra_sleep_core_finish)
 		return false;
 
diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h
index 3cb9309..32d2898 100644
--- a/arch/arm/mach-tegra/pm.h
+++ b/arch/arm/mach-tegra/pm.h
@@ -30,6 +30,14 @@ struct tegra_lp1_iram {
 extern struct tegra_lp1_iram *tegra_lp1_iram;
 extern void (*tegra_sleep_core_finish)(unsigned long v2p);
 
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+void tegra30_lp1_iram_hook(void);
+void tegra30_sleep_core_init(void);
+#else
+static inline void tegra30_lp1_iram_hook(void) {}
+static inline void void tegra30_sleep_core_init(void) {}
+#endif
+
 extern unsigned long l2x0_saved_regs_addr;
 
 void save_cpu_arch_register(void);
diff --git a/arch/arm/mach-tegra/sleep-tegra30.S b/arch/arm/mach-tegra/sleep-tegra30.S
index ecad4ea..5d2522a 100644
--- a/arch/arm/mach-tegra/sleep-tegra30.S
+++ b/arch/arm/mach-tegra/sleep-tegra30.S
@@ -18,13 +18,96 @@
 
 #include <asm/assembler.h>
 #include <asm/asm-offsets.h>
+#include <asm/cache.h>
 
 #include "fuse.h"
 #include "sleep.h"
 #include "flowctrl.h"
 
+#define EMC_CFG				0xc
+#define EMC_ADR_CFG			0x10
+#define EMC_TIMING_CONTROL		0x28
+#define EMC_REFRESH			0x70
+#define EMC_NOP				0xdc
+#define EMC_SELF_REF			0xe0
+#define EMC_MRW				0xe8
+#define EMC_FBIO_CFG5			0x104
+#define EMC_AUTO_CAL_CONFIG		0x2a4
+#define EMC_AUTO_CAL_INTERVAL		0x2a8
+#define EMC_AUTO_CAL_STATUS		0x2ac
+#define EMC_REQ_CTRL			0x2b0
+#define EMC_CFG_DIG_DLL			0x2bc
+#define EMC_EMC_STATUS			0x2b4
+#define EMC_ZCAL_INTERVAL		0x2e0
+#define EMC_ZQ_CAL			0x2ec
+#define EMC_XM2VTTGENPADCTRL		0x310
+#define EMC_XM2VTTGENPADCTRL2		0x314
+
+#define PMC_CTRL			0x0
+#define PMC_CTRL_SIDE_EFFECT_LP0 (1 << 14) /* enter LP0 when CPU pwr gated */
+
+#define PMC_PLLP_WB0_OVERRIDE		0xf8
+#define PMC_IO_DPD_REQ			0x1b8
+#define PMC_IO_DPD_STATUS		0x1bc
+
+#define CLK_RESET_CCLK_BURST		0x20
+#define CLK_RESET_CCLK_DIVIDER		0x24
+#define CLK_RESET_SCLK_BURST		0x28
+#define CLK_RESET_SCLK_DIVIDER		0x2c
+
+#define CLK_RESET_PLLC_BASE		0x80
+#define CLK_RESET_PLLC_MISC		0x8c
+#define CLK_RESET_PLLM_BASE		0x90
+#define CLK_RESET_PLLM_MISC		0x9c
+#define CLK_RESET_PLLP_BASE		0xa0
+#define CLK_RESET_PLLP_MISC		0xac
+#define CLK_RESET_PLLA_BASE		0xb0
+#define CLK_RESET_PLLA_MISC		0xbc
+#define CLK_RESET_PLLX_BASE		0xe0
+#define CLK_RESET_PLLX_MISC		0xe4
+
+#define CLK_RESET_CLK_SOURCE_MSELECT	0x3b4
+
+#define MSELECT_CLKM			(0x3 << 30)
+
+#define LOCK_DELAY 50 /* safety delay after lock is detected */
+
 #define TEGRA30_POWER_HOTPLUG_SHUTDOWN	(1 << 27) /* Hotplug shutdown */
 
+.macro emc_device_mask, rd, base
+	ldr	\rd, [\base, #EMC_ADR_CFG]
+	tst	\rd, #0x1
+	moveq	\rd, #(0x1 << 8)		@ just 1 device
+	movne	\rd, #(0x3 << 8)		@ 2 devices
+.endm
+
+.macro emc_timing_update, rd, base
+	mov	\rd, #1
+	str	\rd, [\base, #EMC_TIMING_CONTROL]
+1001:
+	ldr	\rd, [\base, #EMC_EMC_STATUS]
+	tst	\rd, #(0x1<<23)	@ wait EMC_STATUS_TIMING_UPDATE_STALLED is clear
+	bne	1001b
+.endm
+
+.macro pll_enable, rd, r_car_base, pll_base, pll_misc
+	ldr	\rd, [\r_car_base, #\pll_base]
+	tst	\rd, #(1 << 30)
+	orreq	\rd, \rd, #(1 << 30)
+	streq	\rd, [\r_car_base, #\pll_base]
+	/* Enable lock detector */
+	ldr	\rd, [\r_car_base, #\pll_misc]
+	orr	\rd, \rd, #(1 << 18)
+	str	\rd, [\r_car_base, #\pll_misc]
+.endm
+
+.macro pll_locked, rd, r_car_base, pll_base
+1:
+	ldr	\rd, [\r_car_base, #\pll_base]
+	tst	\rd, #(1 << 27)
+	beq	1b
+.endm
+
 #if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP)
 /*
  * tegra30_hotplug_shutdown(void)
@@ -129,6 +212,41 @@ ENDPROC(tegra30_cpu_shutdown)
 
 #ifdef CONFIG_PM_SLEEP
 /*
+ * tegra30_sleep_core_finish(unsigned long v2p)
+ *
+ * Enters suspend in LP0 or LP1 by turning off the MMU and jumping to
+ * tegra30_tear_down_core in IRAM
+ */
+ENTRY(tegra30_sleep_core_finish)
+	/* Flush, disable the L1 data cache and exit SMP */
+	bl	tegra_disable_clean_inv_dcache
+
+	/*
+	 * Preload all the address literals that are needed for the
+	 * CPU power-gating process, to avoid loading from SDRAM which
+	 * are not supported once SDRAM is put into self-refresh.
+	 * LP0 / LP1 use physical address, since the MMU needs to be
+	 * disalbed before putting SDRAM into self-refresh to avoid
+	 * memory access due to page table walks.
+	 */
+	mov32	r4, TEGRA_PMC_BASE
+	mov32	r5, TEGRA_CLK_RESET_BASE
+	mov32	r6, TEGRA_FLOW_CTRL_BASE
+	mov32	r7, TEGRA_TMRUS_BASE
+
+	mov32	r3, tegra_shut_off_mmu
+	add	r3, r3, r0
+
+	mov32	r0, tegra30_tear_down_core
+	mov32	r1, tegra30_iram_start
+	sub	r0, r0, r1
+	mov32	r1, TEGRA_IRAM_CODE_AREA
+	add	r0, r0, r1
+
+	mov	pc, r3
+ENDPROC(tegra30_sleep_core_finish)
+
+/*
  * tegra30_sleep_cpu_secondary_finish(unsigned long v2p)
  *
  * Enters LP2 on secondary CPU by exiting coherency and powergating the CPU.
@@ -158,6 +276,278 @@ ENTRY(tegra30_tear_down_cpu)
 	b	tegra30_enter_sleep
 ENDPROC(tegra30_tear_down_cpu)
 
+/* START OF ROUTINES COPIED TO IRAM */
+	.align L1_CACHE_SHIFT
+	.globl tegra30_iram_start
+tegra30_iram_start:
+
+/*
+ * tegra30_lp1_reset
+ *
+ * reset vector for LP1 restore; copied into IRAM during suspend.
+ * Brings the system back up to a safe staring point (SDRAM out of
+ * self-refresh, PLLC, PLLM and PLLP reenabled, CPU running on PLLX,
+ * system clock running on the same PLL that it suspended at), and
+ * jumps to tegra_resume to restore virtual addressing.
+ * The physical address of tegra_resume expected to be stored in
+ * PMC_SCRATCH41.
+ *
+ * NOTE: THIS *MUST* BE RELOCATED TO TEGRA_IRAM_CODE_AREA AND MUST BE FIRST.
+ */
+ENTRY(tegra30_lp1_reset)
+	/*
+	 * The CPU and system bus are running@32KHz and executing from
+	 * IRAM when this code is executed; immediately switch to CLKM and
+	 * enable PLLP, PLLM, PLLC, PLLA and PLLX.
+	 */
+	mov32	r0, TEGRA_CLK_RESET_BASE
+
+	mov	r1, #(1 << 28)
+	str	r1, [r0, #CLK_RESET_SCLK_BURST]
+	str	r1, [r0, #CLK_RESET_CCLK_BURST]
+	mov	r1, #0
+	str	r1, [r0, #CLK_RESET_CCLK_DIVIDER]
+	str	r1, [r0, #CLK_RESET_SCLK_DIVIDER]
+
+	/* enable PLLM via PMC */
+	mov32	r2, TEGRA_PMC_BASE
+	ldr	r1, [r2, #PMC_PLLP_WB0_OVERRIDE]
+	orr	r1, r1, #(1 << 12)
+	str	r1, [r2, #PMC_PLLP_WB0_OVERRIDE]
+
+	pll_enable r1, r0, CLK_RESET_PLLM_BASE, CLK_RESET_PLLM_MISC
+	pll_enable r1, r0, CLK_RESET_PLLP_BASE, CLK_RESET_PLLP_MISC
+	pll_enable r1, r0, CLK_RESET_PLLA_BASE, CLK_RESET_PLLA_MISC
+	pll_enable r1, r0, CLK_RESET_PLLC_BASE, CLK_RESET_PLLC_MISC
+	pll_enable r1, r0, CLK_RESET_PLLX_BASE, CLK_RESET_PLLX_MISC
+
+	pll_locked r1, r0, CLK_RESET_PLLM_BASE
+	pll_locked r1, r0, CLK_RESET_PLLP_BASE
+	pll_locked r1, r0, CLK_RESET_PLLA_BASE
+	pll_locked r1, r0, CLK_RESET_PLLC_BASE
+	pll_locked r1, r0, CLK_RESET_PLLX_BASE
+
+	mov32	r7, TEGRA_TMRUS_BASE
+	ldr	r1, [r7]
+	add	r1, r1, #LOCK_DELAY
+	wait_until r1, r7, r3
+
+	adr	r5, tegra30_sdram_pad_save
+
+	ldr	r4, [r5, #0x18]		@ restore CLK_SOURCE_MSELECT
+	str	r4, [r0, #CLK_RESET_CLK_SOURCE_MSELECT]
+
+	ldr	r4, [r5, #0x1C]		@ restore SCLK_BURST
+	str	r4, [r0, #CLK_RESET_SCLK_BURST]
+
+	mov32	r4, ((1 << 28) | (0x8))	@ burst policy is PLLX
+	str	r4, [r0, #CLK_RESET_CCLK_BURST]
+
+	/* Restore pad power state to normal */
+	ldr	r1, [r5, #0x14]		@ PMC_IO_DPD_STATUS
+	mvn	r1, r1
+	bic	r1, r1, #(1 << 31)
+	orr	r1, r1, #(1 << 30)
+	str	r1, [r2, #PMC_IO_DPD_REQ]	@ DPD_OFF
+
+	mov32	r0, TEGRA_EMC_BASE	@ r0 reserved for emc base
+
+	ldr	r1, [r5, #0xC]		@ restore EMC_XM2VTTGENPADCTRL
+	str	r1, [r0, #EMC_XM2VTTGENPADCTRL]
+	ldr	r1, [r5, #0x10]		@ restore EMC_XM2VTTGENPADCTRL2
+	str	r1, [r0, #EMC_XM2VTTGENPADCTRL2]
+	ldr	r1, [r5, #0x8]		@ restore EMC_AUTO_CAL_INTERVAL
+	str	r1, [r0, #EMC_AUTO_CAL_INTERVAL]
+
+	/* Relock DLL */
+	ldr	r1, [r0, #EMC_CFG_DIG_DLL]
+	orr	r1, r1, #(1 << 30)	@ set DLL_RESET
+	str	r1, [r0, #EMC_CFG_DIG_DLL]
+
+	emc_timing_update r1, r0
+
+	ldr	r1, [r0, #EMC_AUTO_CAL_CONFIG]
+	orr	r1, r1, #(1 << 31)	@ set AUTO_CAL_ACTIVE
+	str	r1, [r0, #EMC_AUTO_CAL_CONFIG]
+
+emc_wait_auto_cal_onetime:
+	ldr	r1, [r0, #EMC_AUTO_CAL_STATUS]
+	tst	r1, #(1 << 31)		@ wait until AUTO_CAL_ACTIVE is cleared
+	bne	emc_wait_auto_cal_onetime
+
+	ldr	r1, [r0, #EMC_CFG]
+	bic	r1, r1, #(1 << 31)	@ disable DRAM_CLK_STOP_PD
+	str	r1, [r0, #EMC_CFG]
+
+	mov	r1, #0
+	str	r1, [r0, #EMC_SELF_REF]	@ take DRAM out of self refresh
+	mov	r1, #1
+	str	r1, [r0, #EMC_NOP]
+	str	r1, [r0, #EMC_NOP]
+	str	r1, [r0, #EMC_REFRESH]
+
+	emc_device_mask r1, r0
+
+exit_selfrefresh_loop:
+	ldr	r2, [r0, #EMC_EMC_STATUS]
+	ands	r2, r2, r1
+	bne	exit_selfrefresh_loop
+
+	lsr	r1, r1, #8		@ devSel, bit0:dev0, bit1:dev1
+
+	mov32	r7, TEGRA_TMRUS_BASE
+	ldr	r2, [r0, #EMC_FBIO_CFG5]
+
+	and	r2, r2,	#3		@ check DRAM_TYPE
+	cmp	r2, #2
+	beq	emc_lpddr2
+
+	/* Issue a ZQ_CAL for dev0 - DDR3 */
+	mov32	r2, 0x80000011		@ DEV_SELECTION=2, LENGTH=LONG, CMD=1
+	str	r2, [r0, #EMC_ZQ_CAL]
+	ldr	r2, [r7]
+	add	r2, r2, #10
+	wait_until r2, r7, r3
+
+	tst	r1, #2
+	beq	zcal_done
+
+	/* Issue a ZQ_CAL for dev1 - DDR3 */
+	mov32	r2, 0x40000011		@ DEV_SELECTION=1, LENGTH=LONG, CMD=1
+	str	r2, [r0, #EMC_ZQ_CAL]
+	ldr	r2, [r7]
+	add	r2, r2, #10
+	wait_until r2, r7, r3
+	b	zcal_done
+
+emc_lpddr2:
+	/* Issue a ZQ_CAL for dev0 - LPDDR2 */
+	mov32	r2, 0x800A00AB		@ DEV_SELECTION=2, MA=10, OP=0xAB
+	str	r2, [r0, #EMC_MRW]
+	ldr	r2, [r7]
+	add	r2, r2, #1
+	wait_until r2, r7, r3
+
+	tst	r1, #2
+	beq	zcal_done
+
+	/* Issue a ZQ_CAL for dev0 - LPDDR2 */
+	mov32	r2, 0x400A00AB		@ DEV_SELECTION=1, MA=10, OP=0xAB
+	str	r2, [r0, #EMC_MRW]
+	ldr	r2, [r7]
+	add	r2, r2, #1
+	wait_until r2, r7, r3
+
+zcal_done:
+	mov	r1, #0			@ unstall all transactions
+	str	r1, [r0, #EMC_REQ_CTRL]
+	ldr	r1, [r5, #0x4]		@ restore EMC_ZCAL_INTERVAL
+	str	r1, [r0, #EMC_ZCAL_INTERVAL]
+	ldr	r1, [r5, #0x0]		@ restore EMC_CFG
+	str	r1, [r0, #EMC_CFG]
+
+	mov32	r0, TEGRA_PMC_BASE
+	ldr	r0, [r0, #PMC_SCRATCH41]
+	mov	pc, r0			@ jump to tegra_resume
+ENDPROC(tegra30_lp1_reset)
+
+	.align	L1_CACHE_SHIFT
+	.type	tegra30_sdram_pad_save, %object
+tegra30_sdram_pad_save:
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+
+tegra30_sdram_pad_address:
+	.word	TEGRA_EMC_BASE + EMC_CFG				@0x0
+	.word	TEGRA_EMC_BASE + EMC_ZCAL_INTERVAL			@0x4
+	.word	TEGRA_EMC_BASE + EMC_AUTO_CAL_INTERVAL			@0x8
+	.word	TEGRA_EMC_BASE + EMC_XM2VTTGENPADCTRL			@0xc
+	.word	TEGRA_EMC_BASE + EMC_XM2VTTGENPADCTRL2			@0x10
+	.word	TEGRA_PMC_BASE + PMC_IO_DPD_STATUS			@0x14
+	.word	TEGRA_CLK_RESET_BASE + CLK_RESET_CLK_SOURCE_MSELECT	@0x18
+	.word	TEGRA_CLK_RESET_BASE + CLK_RESET_SCLK_BURST		@0x1c
+
+tegra30_sdram_pad_size:
+	.word	tegra30_sdram_pad_address - tegra30_sdram_pad_save
+
+/*
+ * tegra30_tear_down_core
+ *
+ * copied into and executed from IRAM
+ * puts memory in self-refresh for LP0 and LP1
+ */
+tegra30_tear_down_core:
+	bl	tegra30_sdram_self_refresh
+	bl	tegra30_switch_cpu_to_clk32k
+	b	tegra30_enter_sleep
+
+/*
+ * tegra30_switch_cpu_to_clk32k
+ *
+ * In LP0 and LP1 all PLLs will be turned off. Switching the CPU and System CLK
+ * to the 32KHz clock.
+ * r4 = TEGRA_PMC_BASE
+ * r5 = TEGRA_CLK_RESET_BASE
+ * r6 = TEGRA_FLOW_CTRL_BASE
+ * r7 = TEGRA_TMRUS_BASE
+ */
+tegra30_switch_cpu_to_clk32k:
+	/*
+	 * start by jumping to CLKM to safely disable PLLs, then jump to
+	 * CLKS.
+	 */
+	mov	r0, #(1 << 28)
+	str	r0, [r5, #CLK_RESET_SCLK_BURST]
+	/* 2uS delay delay between changing SCLK and CCLK */
+	wait_for_us r1, r7, r9
+	add	r1, r1, #2
+	wait_until r1, r7, r9
+	str	r0, [r5, #CLK_RESET_CCLK_BURST]
+	mov	r0, #0
+	str	r0, [r5, #CLK_RESET_CCLK_DIVIDER]
+	str	r0, [r5, #CLK_RESET_SCLK_DIVIDER]
+
+	/* switch the clock source of mselect to be CLK_M */
+	ldr	r0, [r5, #CLK_RESET_CLK_SOURCE_MSELECT]
+	orr	r0, r0, #MSELECT_CLKM
+	str	r0, [r5, #CLK_RESET_CLK_SOURCE_MSELECT]
+
+	/* 2uS delay delay between changing SCLK and disabling PLLs */
+	wait_for_us r1, r7, r9
+	add	r1, r1, #2
+	wait_until r1, r7, r9
+
+	/* disable PLLM via PMC in LP1 */
+	ldr	r0, [r4, #PMC_PLLP_WB0_OVERRIDE]
+	bic	r0, r0, #(1 << 12)
+	str	r0, [r4, #PMC_PLLP_WB0_OVERRIDE]
+
+	/* disable PLLP, PLLA, PLLC and PLLX */
+	ldr	r0, [r5, #CLK_RESET_PLLP_BASE]
+	bic	r0, r0, #(1 << 30)
+	str	r0, [r5, #CLK_RESET_PLLP_BASE]
+	ldr	r0, [r5, #CLK_RESET_PLLA_BASE]
+	bic	r0, r0, #(1 << 30)
+	str	r0, [r5, #CLK_RESET_PLLA_BASE]
+	ldr	r0, [r5, #CLK_RESET_PLLC_BASE]
+	bic	r0, r0, #(1 << 30)
+	str	r0, [r5, #CLK_RESET_PLLC_BASE]
+	ldr	r0, [r5, #CLK_RESET_PLLX_BASE]
+	bic	r0, r0, #(1 << 30)
+	str	r0, [r5, #CLK_RESET_PLLX_BASE]
+
+	/* switch to CLKS */
+	mov	r0, #0	/* brust policy = 32KHz */
+	str	r0, [r5, #CLK_RESET_SCLK_BURST]
+
+	mov	pc, lr
+
 /*
  * tegra30_enter_sleep
  *
@@ -194,4 +584,105 @@ halted:
 	/* !!!FIXME!!! Implement halt failure handler */
 	b	halted
 
+/*
+ * tegra30_sdram_self_refresh
+ *
+ * called with MMU off and caches disabled
+ * must be executed from IRAM
+ * r4 = TEGRA_PMC_BASE
+ * r5 = TEGRA_CLK_RESET_BASE
+ * r6 = TEGRA_FLOW_CTRL_BASE
+ * r7 = TEGRA_TMRUS_BASE
+ */
+tegra30_sdram_self_refresh:
+
+	adr	r2, tegra30_sdram_pad_address
+	adr	r8, tegra30_sdram_pad_save
+	mov	r9, #0
+
+	ldr	r3, tegra30_sdram_pad_size
+padsave:
+	ldr	r0, [r2, r9]		@ r0 is the addr in the pad_address
+
+	ldr	r1, [r0]
+	str	r1, [r8, r9]		@ save the content of the addr
+
+	add	r9, r9, #4
+	cmp	r3, r9
+	bne	padsave
+padsave_done:
+
+	dsb
+
+	mov32	r0, TEGRA_EMC_BASE	@ r0 reserved for emc base addr
+
+	mov	r1, #0
+	str	r1, [r0, #EMC_ZCAL_INTERVAL]
+	str	r1, [r0, #EMC_AUTO_CAL_INTERVAL]
+	ldr	r1, [r0, #EMC_CFG]
+	bic	r1, r1, #(1 << 28)
+	str	r1, [r0, #EMC_CFG]	@ disable DYN_SELF_REF
+
+	emc_timing_update r1, r0
+
+	ldr	r1, [r7]
+	add	r1, r1, #5
+	wait_until r1, r7, r2
+
+emc_wait_auto_cal:
+	ldr	r1, [r0, #EMC_AUTO_CAL_STATUS]
+	tst	r1, #(1 << 31)		@ wait until AUTO_CAL_ACTIVE is cleared
+	bne	emc_wait_auto_cal
+
+	mov	r1, #3
+	str	r1, [r0, #EMC_REQ_CTRL]	@ stall incoming DRAM requests
+
+emcidle:
+	ldr	r1, [r0, #EMC_EMC_STATUS]
+	tst	r1, #4
+	beq	emcidle
+
+	mov	r1, #1
+	str	r1, [r0, #EMC_SELF_REF]
+
+	emc_device_mask r1, r0
+
+emcself:
+	ldr	r2, [r0, #EMC_EMC_STATUS]
+	and	r2, r2, r1
+	cmp	r2, r1
+	bne	emcself			@ loop until DDR in self-refresh
+
+	/* Put VTTGEN in the lowest power mode */
+	ldr	r1, [r0, #EMC_XM2VTTGENPADCTRL]
+	mov32	r2, 0xF8F8FFFF	@ clear XM2VTTGEN_DRVUP and XM2VTTGEN_DRVDN
+	and	r1, r1, r2
+	str	r1, [r0, #EMC_XM2VTTGENPADCTRL]
+	ldr	r1, [r0, #EMC_XM2VTTGENPADCTRL2]
+	orr	r1, r1, #7		@ set E_NO_VTTGEN
+	str	r1, [r0, #EMC_XM2VTTGENPADCTRL2]
+
+	emc_timing_update r1, r0
+
+	ldr	r1, [r4, #PMC_CTRL]
+	tst	r1, #PMC_CTRL_SIDE_EFFECT_LP0
+	bne	pmc_io_dpd_skip
+	/*
+	 * Put DDR_DATA, DISC_ADDR_CMD, DDR_ADDR_CMD, POP_ADDR_CMD, POP_CLK
+	 * and COMP in the lowest power mode when LP1.
+	 */
+	mov32	r1, 0x8EC00000
+	str	r1, [r4, #PMC_IO_DPD_REQ]
+pmc_io_dpd_skip:
+
+	dsb
+
+	mov	pc, lr
+
+	.ltorg
+/* dummy symbol for end of IRAM */
+	.align L1_CACHE_SHIFT
+	.global tegra30_iram_end
+tegra30_iram_end:
+	b	.
 #endif
diff --git a/arch/arm/mach-tegra/sleep.S b/arch/arm/mach-tegra/sleep.S
index 8388113..8d06213 100644
--- a/arch/arm/mach-tegra/sleep.S
+++ b/arch/arm/mach-tegra/sleep.S
@@ -134,10 +134,10 @@ ENTRY(tegra_shut_off_mmu)
 #ifdef CONFIG_CACHE_L2X0
 	/* Disable L2 cache */
 	check_cpu_part_num 0xc09, r9, r10
-	movweq	r4, #:lower16:(TEGRA_ARM_PERIF_BASE + 0x3000)
-	movteq	r4, #:upper16:(TEGRA_ARM_PERIF_BASE + 0x3000)
-	moveq	r5, #0
-	streq	r5, [r4, #L2X0_CTRL]
+	movweq	r2, #:lower16:(TEGRA_ARM_PERIF_BASE + 0x3000)
+	movteq	r2, #:upper16:(TEGRA_ARM_PERIF_BASE + 0x3000)
+	moveq	r3, #0
+	streq	r3, [r2, #L2X0_CTRL]
 #endif
 	mov	pc, r0
 ENDPROC(tegra_shut_off_mmu)
diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h
index e7f8b6f..cfce981 100644
--- a/arch/arm/mach-tegra/sleep.h
+++ b/arch/arm/mach-tegra/sleep.h
@@ -48,6 +48,22 @@
 #define TEGRA_FLUSH_CACHE_ALL	1
 
 #ifdef __ASSEMBLY__
+/* waits until the microsecond counter (base) ticks, for exact timing loops */
+.macro wait_for_us, rd, base, tmp
+	ldr	\rd, [\base]
+1001:	ldr	\tmp, [\base]
+	cmp	\rd, \tmp
+	beq	1001b
+.endm
+
+/* waits until the microsecond counter (base) is > rn */
+.macro wait_until, rn, base, tmp
+	add	\rn, \rn, #1
+1002:	ldr	\tmp, [\base]
+	cmp	\tmp, \rn
+	bmi	1002b
+.endm
+
 /* returns the offset of the flow controller halt register for a cpu */
 .macro cpu_to_halt_reg rd, rcpu
 	cmp	\rcpu, #0
-- 
1.8.3.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 6/8] ARM: tegra20: add LP1 suspend support
  2013-07-26  9:15 ` Joseph Lo
@ 2013-07-26  9:15     ` Joseph Lo
  -1 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-07-26  9:15 UTC (permalink / raw)
  To: Stephen Warren
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Joseph Lo

The LP1 suspend mode will power off the CPU, clock gated the PLLs and put
SDRAM to self-refresh mode. Any interrupt can wake up device from LP1. The
sequence when LP1 suspending:

* tunning off L1 data cache and the MMU
* putting SDRAM into self-refresh
* storing some EMC registers and SCLK burst policy
* switching CPU to CLK_M (12MHz OSC)
* switching SCLK to CLK_S (32KHz OSC)
* tunning off PLLM, PLLP and PLLC
* shutting off the CPU rail

The sequence of LP1 resuming:

* re-enabling PLLM, PLLP, and PLLC
* restoring some EMC registers and SCLK burst policy
* setting up CCLK burst policy to PLLP
* resuming SDRAM to normal mode
* jumping to the "tegra_resume" from PMC_SCRATCH41

Due to the SDRAM will be put into self-refresh mode, the low level
procedures of LP1 suspending and resuming should be copied to
TEGRA_IRAM_CODE_AREA (TEGRA_IRAM_BASE + SZ_4K) when suspending. Before
restoring the CPU context when resuming, the SDRAM needs to be switched
back to normal mode. And the PLLs need to be re-enabled, SCLK burst policy
be restored, CCLK burst policy be set in PLLP. Then jumping to
"tegra_resume" that was expected to be stored in PMC_SCRATCH41 to restore
CPU context and back to kernel.

Based on the work by:
Colin Cross <ccross-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>
Gary King <gking-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

Signed-off-by: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
 arch/arm/mach-tegra/Makefile        |   1 +
 arch/arm/mach-tegra/pm-tegra20.c    |  37 +++++
 arch/arm/mach-tegra/pm.c            |   6 +
 arch/arm/mach-tegra/pm.h            |   8 +
 arch/arm/mach-tegra/sleep-tegra20.S | 300 ++++++++++++++++++++++++++++++++++++
 5 files changed, 352 insertions(+)
 create mode 100644 arch/arm/mach-tegra/pm-tegra20.c

diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index d341980..a3fe22d 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_CPU_IDLE)			+= cpuidle.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= tegra20_speedo.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= tegra2_emc.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= sleep-tegra20.o
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= pm-tegra20.o
 ifeq ($(CONFIG_CPU_IDLE),y)
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= cpuidle-tegra20.o
 endif
diff --git a/arch/arm/mach-tegra/pm-tegra20.c b/arch/arm/mach-tegra/pm-tegra20.c
new file mode 100644
index 0000000..47bc40b
--- /dev/null
+++ b/arch/arm/mach-tegra/pm-tegra20.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+
+#include "pm.h"
+
+#ifdef CONFIG_PM_SLEEP
+static struct tegra_lp1_iram tegra20_lp1_iram;
+extern u32 tegra20_iram_start, tegra20_iram_end;
+extern void tegra20_sleep_core_finish(unsigned long);
+
+void tegra20_lp1_iram_hook(void)
+{
+	tegra20_lp1_iram.start_addr = &tegra20_iram_start;
+	tegra20_lp1_iram.end_addr = &tegra20_iram_end;
+
+	tegra_lp1_iram = &tegra20_lp1_iram;
+}
+
+void tegra20_sleep_core_init(void)
+{
+	tegra_sleep_core_finish = tegra20_sleep_core_finish;
+}
+#endif
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index 77dbb22..5ae7ee5 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -210,6 +210,9 @@ static int tegra_sleep_core(unsigned long v2p)
 static bool tegra_lp1_iram_hook(void)
 {
 	switch (tegra_chip_id) {
+	case TEGRA20:
+		tegra20_lp1_iram_hook();
+		break;
 	case TEGRA30:
 		tegra30_lp1_iram_hook();
 		break;
@@ -231,6 +234,9 @@ static bool tegra_lp1_iram_hook(void)
 static bool tegra_sleep_core_init(void)
 {
 	switch (tegra_chip_id) {
+	case TEGRA20:
+		tegra20_sleep_core_init();
+		break;
 	case TEGRA30:
 		tegra30_sleep_core_init();
 		break;
diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h
index 32d2898..7aef651 100644
--- a/arch/arm/mach-tegra/pm.h
+++ b/arch/arm/mach-tegra/pm.h
@@ -30,6 +30,14 @@ struct tegra_lp1_iram {
 extern struct tegra_lp1_iram *tegra_lp1_iram;
 extern void (*tegra_sleep_core_finish)(unsigned long v2p);
 
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+void tegra20_lp1_iram_hook(void);
+void tegra20_sleep_core_init(void);
+#else
+static inline void tegra20_lp1_iram_hook(void) {}
+static inline void void tegra20_sleep_core_init(void) {}
+#endif
+
 #ifdef CONFIG_ARCH_TEGRA_3x_SOC
 void tegra30_lp1_iram_hook(void);
 void tegra30_sleep_core_init(void);
diff --git a/arch/arm/mach-tegra/sleep-tegra20.S b/arch/arm/mach-tegra/sleep-tegra20.S
index f87721d..8972a8e 100644
--- a/arch/arm/mach-tegra/sleep-tegra20.S
+++ b/arch/arm/mach-tegra/sleep-tegra20.S
@@ -23,10 +23,49 @@
 #include <asm/assembler.h>
 #include <asm/proc-fns.h>
 #include <asm/cp15.h>
+#include <asm/cache.h>
 
 #include "sleep.h"
 #include "flowctrl.h"
 
+#define EMC_CFG				0xc
+#define EMC_ADR_CFG			0x10
+#define EMC_REFRESH			0x70
+#define EMC_NOP				0xdc
+#define EMC_SELF_REF			0xe0
+#define EMC_REQ_CTRL			0x2b0
+#define EMC_EMC_STATUS			0x2b4
+
+#define CLK_RESET_CCLK_BURST		0x20
+#define CLK_RESET_CCLK_DIVIDER		0x24
+#define CLK_RESET_SCLK_BURST		0x28
+#define CLK_RESET_SCLK_DIVIDER		0x2c
+#define CLK_RESET_PLLC_BASE		0x80
+#define CLK_RESET_PLLM_BASE		0x90
+#define CLK_RESET_PLLP_BASE		0xa0
+
+#define APB_MISC_XM2CFGCPADCTRL		0x8c8
+#define APB_MISC_XM2CFGDPADCTRL		0x8cc
+#define APB_MISC_XM2CLKCFGPADCTRL	0x8d0
+#define APB_MISC_XM2COMPPADCTRL		0x8d4
+#define APB_MISC_XM2VTTGENPADCTRL	0x8d8
+#define APB_MISC_XM2CFGCPADCTRL2	0x8e4
+#define APB_MISC_XM2CFGDPADCTRL2	0x8e8
+
+.macro pll_enable, rd, r_car_base, pll_base
+	ldr	\rd, [\r_car_base, #\pll_base]
+	tst	\rd, #(1 << 30)
+	orreq	\rd, \rd, #(1 << 30)
+	streq	\rd, [\r_car_base, #\pll_base]
+.endm
+
+.macro emc_device_mask, rd, base
+	ldr	\rd, [\base, #EMC_ADR_CFG]
+	tst	\rd, #(0x3 << 24)
+	moveq	\rd, #(0x1 << 8)		@ just 1 device
+	movne	\rd, #(0x3 << 8)		@ 2 devices
+.endm
+
 #if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP)
 /*
  * tegra20_hotplug_shutdown(void)
@@ -181,6 +220,28 @@ ENTRY(tegra20_cpu_is_resettable_soon)
 ENDPROC(tegra20_cpu_is_resettable_soon)
 
 /*
+ * tegra20_sleep_core_finish(unsigned long v2p)
+ *
+ * Enters suspend in LP0 or LP1 by turning off the mmu and jumping to
+ * tegra20_tear_down_core in IRAM
+ */
+ENTRY(tegra20_sleep_core_finish)
+	/* Flush, disable the L1 data cache and exit SMP */
+	bl	tegra_disable_clean_inv_dcache
+
+	mov32	r3, tegra_shut_off_mmu
+	add	r3, r3, r0
+
+	mov32	r0, tegra20_tear_down_core
+	mov32	r1, tegra20_iram_start
+	sub	r0, r0, r1
+	mov32	r1, TEGRA_IRAM_CODE_AREA
+	add	r0, r0, r1
+
+	mov	pc, r3
+ENDPROC(tegra20_sleep_core_finish)
+
+/*
  * tegra20_sleep_cpu_secondary_finish(unsigned long v2p)
  *
  * Enters WFI on secondary CPU by exiting coherency.
@@ -251,6 +312,150 @@ ENTRY(tegra20_tear_down_cpu)
 	b	tegra20_enter_sleep
 ENDPROC(tegra20_tear_down_cpu)
 
+/* START OF ROUTINES COPIED TO IRAM */
+	.align L1_CACHE_SHIFT
+	.globl tegra20_iram_start
+tegra20_iram_start:
+
+/*
+ * tegra20_lp1_reset
+ *
+ * reset vector for LP1 restore; copied into IRAM during suspend.
+ * Brings the system back up to a safe staring point (SDRAM out of
+ * self-refresh, PLLC, PLLM and PLLP reenabled, CPU running on PLLP,
+ * system clock running on the same PLL that it suspended at), and
+ * jumps to tegra_resume to restore virtual addressing and PLLX.
+ * The physical address of tegra_resume expected to be stored in
+ * PMC_SCRATCH41.
+ *
+ * NOTE: THIS *MUST* BE RELOCATED TO TEGRA_IRAM_CODE_AREA AND MUST BE FIRST.
+ */
+ENTRY(tegra20_lp1_reset)
+	/*
+	 * The CPU and system bus are running at 32KHz and executing from
+	 * IRAM when this code is executed; immediately switch to CLKM and
+	 * enable PLLM, PLLP, PLLC.
+	 */
+	mov32	r0, TEGRA_CLK_RESET_BASE
+
+	mov	r1, #(1 << 28)
+	str	r1, [r0, #CLK_RESET_SCLK_BURST]
+	str	r1, [r0, #CLK_RESET_CCLK_BURST]
+	mov	r1, #0
+	str	r1, [r0, #CLK_RESET_CCLK_DIVIDER]
+	str	r1, [r0, #CLK_RESET_SCLK_DIVIDER]
+
+	pll_enable r1, r0, CLK_RESET_PLLM_BASE
+	pll_enable r1, r0, CLK_RESET_PLLP_BASE
+	pll_enable r1, r0, CLK_RESET_PLLC_BASE
+
+	adr	r2, tegra20_sdram_pad_address
+	adr	r4, tegra20_sdram_pad_save
+	mov	r5, #0
+
+	ldr	r6, tegra20_sdram_pad_size
+padload:
+	ldr	r7, [r2, r5]		@ r7 is the addr in the pad_address
+
+	ldr	r1, [r4, r5]
+	str	r1, [r7]		@ restore the value in pad_save
+
+	add	r5, r5, #4
+	cmp	r6, r5
+	bne	padload
+
+padload_done:
+	/* 255uS delay for PLL stabilization */
+	mov32	r7, TEGRA_TMRUS_BASE
+	wait_for_us r1, r7, r9
+	add	r1, r1, #0xfe
+	wait_until r1, r7, r9
+
+	adr	r4, tegra20_sclk_save
+	ldr	r4, [r4]
+	str	r4, [r0, #CLK_RESET_SCLK_BURST]
+	mov32	r4, ((1 << 28) | (4))	@ burst policy is PLLP
+	str	r4, [r0, #CLK_RESET_CCLK_BURST]
+
+	mov32	r0, TEGRA_EMC_BASE
+	ldr	r1, [r0, #EMC_CFG]
+	bic	r1, r1, #(1 << 31)	@ disable DRAM_CLK_STOP
+	str	r1, [r0, #EMC_CFG]
+
+	mov	r1, #0
+	str	r1, [r0, #EMC_SELF_REF]	@ take DRAM out of self refresh
+	mov	r1, #1
+	str	r1, [r0, #EMC_NOP]
+	str	r1, [r0, #EMC_NOP]
+	str	r1, [r0, #EMC_REFRESH]
+
+	emc_device_mask r1, r0
+
+exit_selfrefresh_loop:
+	ldr	r2, [r0, #EMC_EMC_STATUS]
+	ands	r2, r2, r1
+	bne	exit_selfrefresh_loop
+
+	mov	r1, #0			@ unstall all transactions
+	str	r1, [r0, #EMC_REQ_CTRL]
+
+	mov32	r0, TEGRA_PMC_BASE
+	ldr	r0, [r0, #PMC_SCRATCH41]
+	mov	pc, r0			@ jump to tegra_resume
+ENDPROC(tegra20_lp1_reset)
+
+/*
+ * tegra20_tear_down_core
+ *
+ * copied into and executed from IRAM
+ * puts memory in self-refresh for LP0 and LP1
+ */
+tegra20_tear_down_core:
+	bl	tegra20_sdram_self_refresh
+	bl	tegra20_switch_cpu_to_clk32k
+	b	tegra20_enter_sleep
+
+/*
+ * tegra20_switch_cpu_to_clk32k
+ *
+ * In LP0 and LP1 all PLLs will be turned off. Switch the CPU and system clock
+ * to the 32KHz clock.
+ */
+tegra20_switch_cpu_to_clk32k:
+	/*
+	 * start by jumping to CLKM to safely disable PLLs, then jump to
+	 * CLKS.
+	 */
+	mov	r0, #(1 << 28)
+	str	r0, [r5, #CLK_RESET_SCLK_BURST]
+	str	r0, [r5, #CLK_RESET_CCLK_BURST]
+	mov	r0, #0
+	str	r0, [r5, #CLK_RESET_CCLK_DIVIDER]
+	str	r0, [r5, #CLK_RESET_SCLK_DIVIDER]
+
+	/* 2uS delay delay between changing SCLK and disabling PLLs */
+	mov32	r7, TEGRA_TMRUS_BASE
+	wait_for_us r1, r7, r9
+	add	r1, r1, #2
+	wait_until r1, r7, r9
+
+	/* switch to CLKS */
+	mov	r0, #0	/* brust policy = 32KHz */
+	str	r0, [r5, #CLK_RESET_SCLK_BURST]
+
+	/* disable PLLM, PLLP and PLLC */
+	ldr	r0, [r5, #CLK_RESET_PLLM_BASE]
+	bic	r0, r0, #(1 << 30)
+	str	r0, [r5, #CLK_RESET_PLLM_BASE]
+	ldr	r0, [r5, #CLK_RESET_PLLP_BASE]
+	bic	r0, r0, #(1 << 30)
+	str	r0, [r5, #CLK_RESET_PLLP_BASE]
+	ldr	r0, [r5, #CLK_RESET_PLLC_BASE]
+	bic	r0, r0, #(1 << 30)
+	str	r0, [r5, #CLK_RESET_PLLC_BASE]
+
+	mov	pc, lr
+
 /*
  * tegra20_enter_sleep
  *
@@ -275,4 +480,99 @@ halted:
 	isb
 	b	halted
 
+/*
+ * tegra20_sdram_self_refresh
+ *
+ * called with MMU off and caches disabled
+ * puts sdram in self refresh
+ * must be executed from IRAM
+ */
+tegra20_sdram_self_refresh:
+	mov32	r1, TEGRA_EMC_BASE	@ r1 reserved for emc base addr
+
+	mov	r2, #3
+	str	r2, [r1, #EMC_REQ_CTRL]	@ stall incoming DRAM requests
+
+emcidle:
+	ldr	r2, [r1, #EMC_EMC_STATUS]
+	tst	r2, #4
+	beq	emcidle
+
+	mov	r2, #1
+	str	r2, [r1, #EMC_SELF_REF]
+
+	emc_device_mask r2, r1
+
+emcself:
+	ldr	r3, [r1, #EMC_EMC_STATUS]
+	and	r3, r3, r2
+	cmp	r3, r2
+	bne	emcself			@ loop until DDR in self-refresh
+
+	adr	r2, tegra20_sdram_pad_address
+	adr	r3, tegra20_sdram_pad_safe
+	adr	r4, tegra20_sdram_pad_save
+	mov	r5, #0
+
+	ldr	r6, tegra20_sdram_pad_size
+padsave:
+	ldr	r0, [r2, r5]		@ r0 is the addr in the pad_address
+
+	ldr	r1, [r0]
+	str	r1, [r4, r5]		@ save the content of the addr
+
+	ldr	r1, [r3, r5]
+	str	r1, [r0]		@ set the save val to the addr
+
+	add	r5, r5, #4
+	cmp	r6, r5
+	bne	padsave
+padsave_done:
+
+	mov32	r5, TEGRA_CLK_RESET_BASE
+	ldr	r0, [r5, #CLK_RESET_SCLK_BURST]
+	adr	r2, tegra20_sclk_save
+	str	r0, [r2]
+	dsb
+	mov	pc, lr
+
+tegra20_sdram_pad_address:
+	.word	TEGRA_APB_MISC_BASE + APB_MISC_XM2CFGCPADCTRL
+	.word	TEGRA_APB_MISC_BASE + APB_MISC_XM2CFGDPADCTRL
+	.word	TEGRA_APB_MISC_BASE + APB_MISC_XM2CLKCFGPADCTRL
+	.word	TEGRA_APB_MISC_BASE + APB_MISC_XM2COMPPADCTRL
+	.word	TEGRA_APB_MISC_BASE + APB_MISC_XM2VTTGENPADCTRL
+	.word	TEGRA_APB_MISC_BASE + APB_MISC_XM2CFGCPADCTRL2
+	.word	TEGRA_APB_MISC_BASE + APB_MISC_XM2CFGDPADCTRL2
+
+tegra20_sdram_pad_size:
+	.word	tegra20_sdram_pad_size - tegra20_sdram_pad_address
+
+tegra20_sdram_pad_safe:
+	.word	0x8
+	.word	0x8
+	.word	0x0
+	.word	0x8
+	.word	0x5500
+	.word	0x08080040
+	.word	0x0
+
+tegra20_sclk_save:
+	.word	0x0
+
+tegra20_sdram_pad_save:
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+
+	.ltorg
+/* dummy symbol for end of IRAM */
+	.align L1_CACHE_SHIFT
+	.globl tegra20_iram_end
+tegra20_iram_end:
+	b	.
 #endif
-- 
1.8.3.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 6/8] ARM: tegra20: add LP1 suspend support
@ 2013-07-26  9:15     ` Joseph Lo
  0 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-07-26  9:15 UTC (permalink / raw)
  To: linux-arm-kernel

The LP1 suspend mode will power off the CPU, clock gated the PLLs and put
SDRAM to self-refresh mode. Any interrupt can wake up device from LP1. The
sequence when LP1 suspending:

* tunning off L1 data cache and the MMU
* putting SDRAM into self-refresh
* storing some EMC registers and SCLK burst policy
* switching CPU to CLK_M (12MHz OSC)
* switching SCLK to CLK_S (32KHz OSC)
* tunning off PLLM, PLLP and PLLC
* shutting off the CPU rail

The sequence of LP1 resuming:

* re-enabling PLLM, PLLP, and PLLC
* restoring some EMC registers and SCLK burst policy
* setting up CCLK burst policy to PLLP
* resuming SDRAM to normal mode
* jumping to the "tegra_resume" from PMC_SCRATCH41

Due to the SDRAM will be put into self-refresh mode, the low level
procedures of LP1 suspending and resuming should be copied to
TEGRA_IRAM_CODE_AREA (TEGRA_IRAM_BASE + SZ_4K) when suspending. Before
restoring the CPU context when resuming, the SDRAM needs to be switched
back to normal mode. And the PLLs need to be re-enabled, SCLK burst policy
be restored, CCLK burst policy be set in PLLP. Then jumping to
"tegra_resume" that was expected to be stored in PMC_SCRATCH41 to restore
CPU context and back to kernel.

Based on the work by:
Colin Cross <ccross@android.com>
Gary King <gking@nvidia.com>

Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
 arch/arm/mach-tegra/Makefile        |   1 +
 arch/arm/mach-tegra/pm-tegra20.c    |  37 +++++
 arch/arm/mach-tegra/pm.c            |   6 +
 arch/arm/mach-tegra/pm.h            |   8 +
 arch/arm/mach-tegra/sleep-tegra20.S | 300 ++++++++++++++++++++++++++++++++++++
 5 files changed, 352 insertions(+)
 create mode 100644 arch/arm/mach-tegra/pm-tegra20.c

diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index d341980..a3fe22d 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_CPU_IDLE)			+= cpuidle.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= tegra20_speedo.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= tegra2_emc.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= sleep-tegra20.o
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= pm-tegra20.o
 ifeq ($(CONFIG_CPU_IDLE),y)
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= cpuidle-tegra20.o
 endif
diff --git a/arch/arm/mach-tegra/pm-tegra20.c b/arch/arm/mach-tegra/pm-tegra20.c
new file mode 100644
index 0000000..47bc40b
--- /dev/null
+++ b/arch/arm/mach-tegra/pm-tegra20.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+
+#include "pm.h"
+
+#ifdef CONFIG_PM_SLEEP
+static struct tegra_lp1_iram tegra20_lp1_iram;
+extern u32 tegra20_iram_start, tegra20_iram_end;
+extern void tegra20_sleep_core_finish(unsigned long);
+
+void tegra20_lp1_iram_hook(void)
+{
+	tegra20_lp1_iram.start_addr = &tegra20_iram_start;
+	tegra20_lp1_iram.end_addr = &tegra20_iram_end;
+
+	tegra_lp1_iram = &tegra20_lp1_iram;
+}
+
+void tegra20_sleep_core_init(void)
+{
+	tegra_sleep_core_finish = tegra20_sleep_core_finish;
+}
+#endif
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index 77dbb22..5ae7ee5 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -210,6 +210,9 @@ static int tegra_sleep_core(unsigned long v2p)
 static bool tegra_lp1_iram_hook(void)
 {
 	switch (tegra_chip_id) {
+	case TEGRA20:
+		tegra20_lp1_iram_hook();
+		break;
 	case TEGRA30:
 		tegra30_lp1_iram_hook();
 		break;
@@ -231,6 +234,9 @@ static bool tegra_lp1_iram_hook(void)
 static bool tegra_sleep_core_init(void)
 {
 	switch (tegra_chip_id) {
+	case TEGRA20:
+		tegra20_sleep_core_init();
+		break;
 	case TEGRA30:
 		tegra30_sleep_core_init();
 		break;
diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h
index 32d2898..7aef651 100644
--- a/arch/arm/mach-tegra/pm.h
+++ b/arch/arm/mach-tegra/pm.h
@@ -30,6 +30,14 @@ struct tegra_lp1_iram {
 extern struct tegra_lp1_iram *tegra_lp1_iram;
 extern void (*tegra_sleep_core_finish)(unsigned long v2p);
 
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+void tegra20_lp1_iram_hook(void);
+void tegra20_sleep_core_init(void);
+#else
+static inline void tegra20_lp1_iram_hook(void) {}
+static inline void void tegra20_sleep_core_init(void) {}
+#endif
+
 #ifdef CONFIG_ARCH_TEGRA_3x_SOC
 void tegra30_lp1_iram_hook(void);
 void tegra30_sleep_core_init(void);
diff --git a/arch/arm/mach-tegra/sleep-tegra20.S b/arch/arm/mach-tegra/sleep-tegra20.S
index f87721d..8972a8e 100644
--- a/arch/arm/mach-tegra/sleep-tegra20.S
+++ b/arch/arm/mach-tegra/sleep-tegra20.S
@@ -23,10 +23,49 @@
 #include <asm/assembler.h>
 #include <asm/proc-fns.h>
 #include <asm/cp15.h>
+#include <asm/cache.h>
 
 #include "sleep.h"
 #include "flowctrl.h"
 
+#define EMC_CFG				0xc
+#define EMC_ADR_CFG			0x10
+#define EMC_REFRESH			0x70
+#define EMC_NOP				0xdc
+#define EMC_SELF_REF			0xe0
+#define EMC_REQ_CTRL			0x2b0
+#define EMC_EMC_STATUS			0x2b4
+
+#define CLK_RESET_CCLK_BURST		0x20
+#define CLK_RESET_CCLK_DIVIDER		0x24
+#define CLK_RESET_SCLK_BURST		0x28
+#define CLK_RESET_SCLK_DIVIDER		0x2c
+#define CLK_RESET_PLLC_BASE		0x80
+#define CLK_RESET_PLLM_BASE		0x90
+#define CLK_RESET_PLLP_BASE		0xa0
+
+#define APB_MISC_XM2CFGCPADCTRL		0x8c8
+#define APB_MISC_XM2CFGDPADCTRL		0x8cc
+#define APB_MISC_XM2CLKCFGPADCTRL	0x8d0
+#define APB_MISC_XM2COMPPADCTRL		0x8d4
+#define APB_MISC_XM2VTTGENPADCTRL	0x8d8
+#define APB_MISC_XM2CFGCPADCTRL2	0x8e4
+#define APB_MISC_XM2CFGDPADCTRL2	0x8e8
+
+.macro pll_enable, rd, r_car_base, pll_base
+	ldr	\rd, [\r_car_base, #\pll_base]
+	tst	\rd, #(1 << 30)
+	orreq	\rd, \rd, #(1 << 30)
+	streq	\rd, [\r_car_base, #\pll_base]
+.endm
+
+.macro emc_device_mask, rd, base
+	ldr	\rd, [\base, #EMC_ADR_CFG]
+	tst	\rd, #(0x3 << 24)
+	moveq	\rd, #(0x1 << 8)		@ just 1 device
+	movne	\rd, #(0x3 << 8)		@ 2 devices
+.endm
+
 #if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP)
 /*
  * tegra20_hotplug_shutdown(void)
@@ -181,6 +220,28 @@ ENTRY(tegra20_cpu_is_resettable_soon)
 ENDPROC(tegra20_cpu_is_resettable_soon)
 
 /*
+ * tegra20_sleep_core_finish(unsigned long v2p)
+ *
+ * Enters suspend in LP0 or LP1 by turning off the mmu and jumping to
+ * tegra20_tear_down_core in IRAM
+ */
+ENTRY(tegra20_sleep_core_finish)
+	/* Flush, disable the L1 data cache and exit SMP */
+	bl	tegra_disable_clean_inv_dcache
+
+	mov32	r3, tegra_shut_off_mmu
+	add	r3, r3, r0
+
+	mov32	r0, tegra20_tear_down_core
+	mov32	r1, tegra20_iram_start
+	sub	r0, r0, r1
+	mov32	r1, TEGRA_IRAM_CODE_AREA
+	add	r0, r0, r1
+
+	mov	pc, r3
+ENDPROC(tegra20_sleep_core_finish)
+
+/*
  * tegra20_sleep_cpu_secondary_finish(unsigned long v2p)
  *
  * Enters WFI on secondary CPU by exiting coherency.
@@ -251,6 +312,150 @@ ENTRY(tegra20_tear_down_cpu)
 	b	tegra20_enter_sleep
 ENDPROC(tegra20_tear_down_cpu)
 
+/* START OF ROUTINES COPIED TO IRAM */
+	.align L1_CACHE_SHIFT
+	.globl tegra20_iram_start
+tegra20_iram_start:
+
+/*
+ * tegra20_lp1_reset
+ *
+ * reset vector for LP1 restore; copied into IRAM during suspend.
+ * Brings the system back up to a safe staring point (SDRAM out of
+ * self-refresh, PLLC, PLLM and PLLP reenabled, CPU running on PLLP,
+ * system clock running on the same PLL that it suspended at), and
+ * jumps to tegra_resume to restore virtual addressing and PLLX.
+ * The physical address of tegra_resume expected to be stored in
+ * PMC_SCRATCH41.
+ *
+ * NOTE: THIS *MUST* BE RELOCATED TO TEGRA_IRAM_CODE_AREA AND MUST BE FIRST.
+ */
+ENTRY(tegra20_lp1_reset)
+	/*
+	 * The CPU and system bus are running@32KHz and executing from
+	 * IRAM when this code is executed; immediately switch to CLKM and
+	 * enable PLLM, PLLP, PLLC.
+	 */
+	mov32	r0, TEGRA_CLK_RESET_BASE
+
+	mov	r1, #(1 << 28)
+	str	r1, [r0, #CLK_RESET_SCLK_BURST]
+	str	r1, [r0, #CLK_RESET_CCLK_BURST]
+	mov	r1, #0
+	str	r1, [r0, #CLK_RESET_CCLK_DIVIDER]
+	str	r1, [r0, #CLK_RESET_SCLK_DIVIDER]
+
+	pll_enable r1, r0, CLK_RESET_PLLM_BASE
+	pll_enable r1, r0, CLK_RESET_PLLP_BASE
+	pll_enable r1, r0, CLK_RESET_PLLC_BASE
+
+	adr	r2, tegra20_sdram_pad_address
+	adr	r4, tegra20_sdram_pad_save
+	mov	r5, #0
+
+	ldr	r6, tegra20_sdram_pad_size
+padload:
+	ldr	r7, [r2, r5]		@ r7 is the addr in the pad_address
+
+	ldr	r1, [r4, r5]
+	str	r1, [r7]		@ restore the value in pad_save
+
+	add	r5, r5, #4
+	cmp	r6, r5
+	bne	padload
+
+padload_done:
+	/* 255uS delay for PLL stabilization */
+	mov32	r7, TEGRA_TMRUS_BASE
+	wait_for_us r1, r7, r9
+	add	r1, r1, #0xfe
+	wait_until r1, r7, r9
+
+	adr	r4, tegra20_sclk_save
+	ldr	r4, [r4]
+	str	r4, [r0, #CLK_RESET_SCLK_BURST]
+	mov32	r4, ((1 << 28) | (4))	@ burst policy is PLLP
+	str	r4, [r0, #CLK_RESET_CCLK_BURST]
+
+	mov32	r0, TEGRA_EMC_BASE
+	ldr	r1, [r0, #EMC_CFG]
+	bic	r1, r1, #(1 << 31)	@ disable DRAM_CLK_STOP
+	str	r1, [r0, #EMC_CFG]
+
+	mov	r1, #0
+	str	r1, [r0, #EMC_SELF_REF]	@ take DRAM out of self refresh
+	mov	r1, #1
+	str	r1, [r0, #EMC_NOP]
+	str	r1, [r0, #EMC_NOP]
+	str	r1, [r0, #EMC_REFRESH]
+
+	emc_device_mask r1, r0
+
+exit_selfrefresh_loop:
+	ldr	r2, [r0, #EMC_EMC_STATUS]
+	ands	r2, r2, r1
+	bne	exit_selfrefresh_loop
+
+	mov	r1, #0			@ unstall all transactions
+	str	r1, [r0, #EMC_REQ_CTRL]
+
+	mov32	r0, TEGRA_PMC_BASE
+	ldr	r0, [r0, #PMC_SCRATCH41]
+	mov	pc, r0			@ jump to tegra_resume
+ENDPROC(tegra20_lp1_reset)
+
+/*
+ * tegra20_tear_down_core
+ *
+ * copied into and executed from IRAM
+ * puts memory in self-refresh for LP0 and LP1
+ */
+tegra20_tear_down_core:
+	bl	tegra20_sdram_self_refresh
+	bl	tegra20_switch_cpu_to_clk32k
+	b	tegra20_enter_sleep
+
+/*
+ * tegra20_switch_cpu_to_clk32k
+ *
+ * In LP0 and LP1 all PLLs will be turned off. Switch the CPU and system clock
+ * to the 32KHz clock.
+ */
+tegra20_switch_cpu_to_clk32k:
+	/*
+	 * start by jumping to CLKM to safely disable PLLs, then jump to
+	 * CLKS.
+	 */
+	mov	r0, #(1 << 28)
+	str	r0, [r5, #CLK_RESET_SCLK_BURST]
+	str	r0, [r5, #CLK_RESET_CCLK_BURST]
+	mov	r0, #0
+	str	r0, [r5, #CLK_RESET_CCLK_DIVIDER]
+	str	r0, [r5, #CLK_RESET_SCLK_DIVIDER]
+
+	/* 2uS delay delay between changing SCLK and disabling PLLs */
+	mov32	r7, TEGRA_TMRUS_BASE
+	wait_for_us r1, r7, r9
+	add	r1, r1, #2
+	wait_until r1, r7, r9
+
+	/* switch to CLKS */
+	mov	r0, #0	/* brust policy = 32KHz */
+	str	r0, [r5, #CLK_RESET_SCLK_BURST]
+
+	/* disable PLLM, PLLP and PLLC */
+	ldr	r0, [r5, #CLK_RESET_PLLM_BASE]
+	bic	r0, r0, #(1 << 30)
+	str	r0, [r5, #CLK_RESET_PLLM_BASE]
+	ldr	r0, [r5, #CLK_RESET_PLLP_BASE]
+	bic	r0, r0, #(1 << 30)
+	str	r0, [r5, #CLK_RESET_PLLP_BASE]
+	ldr	r0, [r5, #CLK_RESET_PLLC_BASE]
+	bic	r0, r0, #(1 << 30)
+	str	r0, [r5, #CLK_RESET_PLLC_BASE]
+
+	mov	pc, lr
+
 /*
  * tegra20_enter_sleep
  *
@@ -275,4 +480,99 @@ halted:
 	isb
 	b	halted
 
+/*
+ * tegra20_sdram_self_refresh
+ *
+ * called with MMU off and caches disabled
+ * puts sdram in self refresh
+ * must be executed from IRAM
+ */
+tegra20_sdram_self_refresh:
+	mov32	r1, TEGRA_EMC_BASE	@ r1 reserved for emc base addr
+
+	mov	r2, #3
+	str	r2, [r1, #EMC_REQ_CTRL]	@ stall incoming DRAM requests
+
+emcidle:
+	ldr	r2, [r1, #EMC_EMC_STATUS]
+	tst	r2, #4
+	beq	emcidle
+
+	mov	r2, #1
+	str	r2, [r1, #EMC_SELF_REF]
+
+	emc_device_mask r2, r1
+
+emcself:
+	ldr	r3, [r1, #EMC_EMC_STATUS]
+	and	r3, r3, r2
+	cmp	r3, r2
+	bne	emcself			@ loop until DDR in self-refresh
+
+	adr	r2, tegra20_sdram_pad_address
+	adr	r3, tegra20_sdram_pad_safe
+	adr	r4, tegra20_sdram_pad_save
+	mov	r5, #0
+
+	ldr	r6, tegra20_sdram_pad_size
+padsave:
+	ldr	r0, [r2, r5]		@ r0 is the addr in the pad_address
+
+	ldr	r1, [r0]
+	str	r1, [r4, r5]		@ save the content of the addr
+
+	ldr	r1, [r3, r5]
+	str	r1, [r0]		@ set the save val to the addr
+
+	add	r5, r5, #4
+	cmp	r6, r5
+	bne	padsave
+padsave_done:
+
+	mov32	r5, TEGRA_CLK_RESET_BASE
+	ldr	r0, [r5, #CLK_RESET_SCLK_BURST]
+	adr	r2, tegra20_sclk_save
+	str	r0, [r2]
+	dsb
+	mov	pc, lr
+
+tegra20_sdram_pad_address:
+	.word	TEGRA_APB_MISC_BASE + APB_MISC_XM2CFGCPADCTRL
+	.word	TEGRA_APB_MISC_BASE + APB_MISC_XM2CFGDPADCTRL
+	.word	TEGRA_APB_MISC_BASE + APB_MISC_XM2CLKCFGPADCTRL
+	.word	TEGRA_APB_MISC_BASE + APB_MISC_XM2COMPPADCTRL
+	.word	TEGRA_APB_MISC_BASE + APB_MISC_XM2VTTGENPADCTRL
+	.word	TEGRA_APB_MISC_BASE + APB_MISC_XM2CFGCPADCTRL2
+	.word	TEGRA_APB_MISC_BASE + APB_MISC_XM2CFGDPADCTRL2
+
+tegra20_sdram_pad_size:
+	.word	tegra20_sdram_pad_size - tegra20_sdram_pad_address
+
+tegra20_sdram_pad_safe:
+	.word	0x8
+	.word	0x8
+	.word	0x0
+	.word	0x8
+	.word	0x5500
+	.word	0x08080040
+	.word	0x0
+
+tegra20_sclk_save:
+	.word	0x0
+
+tegra20_sdram_pad_save:
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+
+	.ltorg
+/* dummy symbol for end of IRAM */
+	.align L1_CACHE_SHIFT
+	.globl tegra20_iram_end
+tegra20_iram_end:
+	b	.
 #endif
-- 
1.8.3.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 7/8] ARM: tegra114: add LP1 suspend support
  2013-07-26  9:15 ` Joseph Lo
@ 2013-07-26  9:15     ` Joseph Lo
  -1 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-07-26  9:15 UTC (permalink / raw)
  To: Stephen Warren
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Joseph Lo

The LP1 suspend mode will power off the CPU, clock gated the PLLs and put
SDRAM to self-refresh mode. Any interrupt can wake up device from LP1. The
sequence when LP1 suspending:

* tunning off L1 data cache and the MMU
* storing some EMC registers, DPD (deep power down) status, clk source of
  mselect and SCLK burst policy
* putting SDRAM into self-refresh
* switching CPU to CLK_M (12MHz OSC)
* tunning off PLLM, PLLP, PLLA, PLLC and PLLX
* switching SCLK to CLK_S (32KHz OSC)
* shutting off the CPU rail

The sequence of LP1 resuming:

* re-enabling PLLM, PLLP, PLLA, PLLC and PLLX
* restoring the clk source of mselect and SCLK burst policy
* restoring DPD status and some EMC registers
* resuming SDRAM to normal mode
* jumping to the "tegra_resume" from PMC_SCRATCH41

Due to the SDRAM will be put into self-refresh mode, the low level
procedures of LP1 suspending and resuming should be copied to
TEGRA_IRAM_CODE_AREA (TEGRA_IRAM_BASE + SZ_4K) when suspending. Before
restoring the CPU context when resuming, the SDRAM needs to be switched
back to normal mode. And the PLLs need to be re-enabled, SCLK burst policy
be restored. Then jumping to "tegra_resume" that was expected to be stored
in PMC_SCRATCH41 to restore CPU context and back to kernel.

Based on the work by: Bo Yan <byan-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

Signed-off-by: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
 arch/arm/mach-tegra/Makefile        |   1 +
 arch/arm/mach-tegra/iomap.h         |   6 ++
 arch/arm/mach-tegra/pm.c            |   2 +
 arch/arm/mach-tegra/pm.h            |   2 +-
 arch/arm/mach-tegra/sleep-tegra30.S | 150 ++++++++++++++++++++++++++++++++----
 5 files changed, 147 insertions(+), 14 deletions(-)

diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index a3fe22d..f4e7063 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_TEGRA_PCI)			+= pcie.o
 
 obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= tegra114_speedo.o
 obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= sleep-tegra30.o
+obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= pm-tegra30.o
 ifeq ($(CONFIG_CPU_IDLE),y)
 obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= cpuidle-tegra114.o
 endif
diff --git a/arch/arm/mach-tegra/iomap.h b/arch/arm/mach-tegra/iomap.h
index 399fbca..7f463d7 100644
--- a/arch/arm/mach-tegra/iomap.h
+++ b/arch/arm/mach-tegra/iomap.h
@@ -237,6 +237,12 @@
 #define TEGRA_KFUSE_BASE		0x7000FC00
 #define TEGRA_KFUSE_SIZE		SZ_1K
 
+#define TEGRA_EMC0_BASE			0x7001A000
+#define TEGRA_EMC0_SIZE			SZ_2K
+
+#define TEGRA_EMC1_BASE			0x7001A800
+#define TEGRA_EMC1_SIZE			SZ_2K
+
 #define TEGRA_CSITE_BASE		0x70040000
 #define TEGRA_CSITE_SIZE		SZ_256K
 
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index 5ae7ee5..36cadaa 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -214,6 +214,7 @@ static bool tegra_lp1_iram_hook(void)
 		tegra20_lp1_iram_hook();
 		break;
 	case TEGRA30:
+	case TEGRA114:
 		tegra30_lp1_iram_hook();
 		break;
 	default:
@@ -238,6 +239,7 @@ static bool tegra_sleep_core_init(void)
 		tegra20_sleep_core_init();
 		break;
 	case TEGRA30:
+	case TEGRA114:
 		tegra30_sleep_core_init();
 		break;
 	default:
diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h
index 7aef651..1ccc0df 100644
--- a/arch/arm/mach-tegra/pm.h
+++ b/arch/arm/mach-tegra/pm.h
@@ -38,7 +38,7 @@ static inline void tegra20_lp1_iram_hook(void) {}
 static inline void void tegra20_sleep_core_init(void) {}
 #endif
 
-#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC) || defined(CONFIG_ARCH_TEGRA_114_SOC)
 void tegra30_lp1_iram_hook(void);
 void tegra30_sleep_core_init(void);
 #else
diff --git a/arch/arm/mach-tegra/sleep-tegra30.S b/arch/arm/mach-tegra/sleep-tegra30.S
index 5d2522a..e2f25f3 100644
--- a/arch/arm/mach-tegra/sleep-tegra30.S
+++ b/arch/arm/mach-tegra/sleep-tegra30.S
@@ -65,6 +65,10 @@
 #define CLK_RESET_PLLA_MISC		0xbc
 #define CLK_RESET_PLLX_BASE		0xe0
 #define CLK_RESET_PLLX_MISC		0xe4
+#define CLK_RESET_PLLX_MISC3		0x518
+#define CLK_RESET_PLLX_MISC3_IDDQ	3
+#define CLK_RESET_PLLM_MISC_IDDQ	5
+#define CLK_RESET_PLLC_MISC_IDDQ	26
 
 #define CLK_RESET_CLK_SOURCE_MSELECT	0x3b4
 
@@ -96,9 +100,15 @@
 	orreq	\rd, \rd, #(1 << 30)
 	streq	\rd, [\r_car_base, #\pll_base]
 	/* Enable lock detector */
+	.if	\pll_misc
+	ldr	\rd, [\r_car_base, #\pll_misc]
+	bic	\rd, \rd, #(1 << 18)
+	str	\rd, [\r_car_base, #\pll_misc]
+	ldr	\rd, [\r_car_base, #\pll_misc]
 	ldr	\rd, [\r_car_base, #\pll_misc]
 	orr	\rd, \rd, #(1 << 18)
 	str	\rd, [\r_car_base, #\pll_misc]
+	.endif
 .endm
 
 .macro pll_locked, rd, r_car_base, pll_base
@@ -108,6 +118,18 @@
 	beq	1b
 .endm
 
+.macro pll_iddq_exit, rd, car, iddq, iddq_bit
+	ldr	\rd, [\car, #\iddq]
+	bic	\rd, \rd, #(1<<\iddq_bit)
+	str	\rd, [\car, #\iddq]
+.endm
+
+.macro pll_iddq_entry, rd, car, iddq, iddq_bit
+	ldr	\rd, [\car, #\iddq]
+	orr	\rd, \rd, #(1<<\iddq_bit)
+	str	\rd, [\car, #\iddq]
+.endm
+
 #if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP)
 /*
  * tegra30_hotplug_shutdown(void)
@@ -309,6 +331,32 @@ ENTRY(tegra30_lp1_reset)
 	str	r1, [r0, #CLK_RESET_CCLK_DIVIDER]
 	str	r1, [r0, #CLK_RESET_SCLK_DIVIDER]
 
+	tegra_get_soc_id TEGRA_APB_MISC_BASE, r10
+	cmp	r10, #TEGRA30
+	beq	_no_pll_iddq_exit
+
+	pll_iddq_exit r1, r0, CLK_RESET_PLLM_MISC, CLK_RESET_PLLM_MISC_IDDQ
+	pll_iddq_exit r1, r0, CLK_RESET_PLLC_MISC, CLK_RESET_PLLC_MISC_IDDQ
+	pll_iddq_exit r1, r0, CLK_RESET_PLLX_MISC3, CLK_RESET_PLLX_MISC3_IDDQ
+
+	mov32	r7, TEGRA_TMRUS_BASE
+	ldr	r1, [r7]
+	add	r1, r1, #2
+	wait_until r1, r7, r3
+
+	/* enable PLLM via PMC */
+	mov32	r2, TEGRA_PMC_BASE
+	ldr	r1, [r2, #PMC_PLLP_WB0_OVERRIDE]
+	orr	r1, r1, #(1 << 12)
+	str	r1, [r2, #PMC_PLLP_WB0_OVERRIDE]
+
+	pll_enable r1, r0, CLK_RESET_PLLM_BASE, 0
+	pll_enable r1, r0, CLK_RESET_PLLC_BASE, 0
+	pll_enable r1, r0, CLK_RESET_PLLX_BASE, 0
+
+	b	_pll_m_c_x_done
+
+_no_pll_iddq_exit:
 	/* enable PLLM via PMC */
 	mov32	r2, TEGRA_PMC_BASE
 	ldr	r1, [r2, #PMC_PLLP_WB0_OVERRIDE]
@@ -316,11 +364,13 @@ ENTRY(tegra30_lp1_reset)
 	str	r1, [r2, #PMC_PLLP_WB0_OVERRIDE]
 
 	pll_enable r1, r0, CLK_RESET_PLLM_BASE, CLK_RESET_PLLM_MISC
-	pll_enable r1, r0, CLK_RESET_PLLP_BASE, CLK_RESET_PLLP_MISC
-	pll_enable r1, r0, CLK_RESET_PLLA_BASE, CLK_RESET_PLLA_MISC
 	pll_enable r1, r0, CLK_RESET_PLLC_BASE, CLK_RESET_PLLC_MISC
 	pll_enable r1, r0, CLK_RESET_PLLX_BASE, CLK_RESET_PLLX_MISC
 
+_pll_m_c_x_done:
+	pll_enable r1, r0, CLK_RESET_PLLP_BASE, CLK_RESET_PLLP_MISC
+	pll_enable r1, r0, CLK_RESET_PLLA_BASE, CLK_RESET_PLLA_MISC
+
 	pll_locked r1, r0, CLK_RESET_PLLM_BASE
 	pll_locked r1, r0, CLK_RESET_PLLP_BASE
 	pll_locked r1, r0, CLK_RESET_PLLA_BASE
@@ -340,8 +390,10 @@ ENTRY(tegra30_lp1_reset)
 	ldr	r4, [r5, #0x1C]		@ restore SCLK_BURST
 	str	r4, [r0, #CLK_RESET_SCLK_BURST]
 
-	mov32	r4, ((1 << 28) | (0x8))	@ burst policy is PLLX
-	str	r4, [r0, #CLK_RESET_CCLK_BURST]
+	cmp	r10, #TEGRA30
+	movweq	r4, #:lower16:((1 << 28) | (0x8))	@ burst policy is PLLX
+	movteq	r4, #:upper16:((1 << 28) | (0x8))
+	streq	r4, [r0, #CLK_RESET_CCLK_BURST]
 
 	/* Restore pad power state to normal */
 	ldr	r1, [r5, #0x14]		@ PMC_IO_DPD_STATUS
@@ -350,8 +402,13 @@ ENTRY(tegra30_lp1_reset)
 	orr	r1, r1, #(1 << 30)
 	str	r1, [r2, #PMC_IO_DPD_REQ]	@ DPD_OFF
 
-	mov32	r0, TEGRA_EMC_BASE	@ r0 reserved for emc base
+	cmp	r10, #TEGRA30
+	movweq	r0, #:lower16:TEGRA_EMC_BASE	@ r0 reserved for emc base
+	movteq	r0, #:upper16:TEGRA_EMC_BASE
+	movwne	r0, #:lower16:TEGRA_EMC0_BASE
+	movtne	r0, #:upper16:TEGRA_EMC0_BASE
 
+exit_self_refresh:
 	ldr	r1, [r5, #0xC]		@ restore EMC_XM2VTTGENPADCTRL
 	str	r1, [r0, #EMC_XM2VTTGENPADCTRL]
 	ldr	r1, [r5, #0x10]		@ restore EMC_XM2VTTGENPADCTRL2
@@ -366,8 +423,14 @@ ENTRY(tegra30_lp1_reset)
 
 	emc_timing_update r1, r0
 
+	cmp	r10, #TEGRA114
+	movweq	r1, #:lower16:TEGRA_EMC1_BASE
+	movteq	r1, #:upper16:TEGRA_EMC1_BASE
+	cmpeq	r0, r1
+
 	ldr	r1, [r0, #EMC_AUTO_CAL_CONFIG]
 	orr	r1, r1, #(1 << 31)	@ set AUTO_CAL_ACTIVE
+	orreq	r1, r1, #(1 << 27)	@ set slave mode for channel 1
 	str	r1, [r0, #EMC_AUTO_CAL_CONFIG]
 
 emc_wait_auto_cal_onetime:
@@ -382,9 +445,10 @@ emc_wait_auto_cal_onetime:
 	mov	r1, #0
 	str	r1, [r0, #EMC_SELF_REF]	@ take DRAM out of self refresh
 	mov	r1, #1
-	str	r1, [r0, #EMC_NOP]
-	str	r1, [r0, #EMC_NOP]
-	str	r1, [r0, #EMC_REFRESH]
+	cmp	r10, #TEGRA30
+	streq	r1, [r0, #EMC_NOP]
+	streq	r1, [r0, #EMC_NOP]
+	streq	r1, [r0, #EMC_REFRESH]
 
 	emc_device_mask r1, r0
 
@@ -446,6 +510,16 @@ zcal_done:
 	ldr	r1, [r5, #0x0]		@ restore EMC_CFG
 	str	r1, [r0, #EMC_CFG]
 
+	/* Tegra114 had dual EMC channel, now config the other one */
+	cmp	r10, #TEGRA114
+	bne	__no_dual_emc_chanl
+	mov32	r1, TEGRA_EMC1_BASE
+	cmp	r0, r1
+	movne	r0, r1
+	addne	r5, r5, #0x20
+	bne	exit_self_refresh
+__no_dual_emc_chanl:
+
 	mov32	r0, TEGRA_PMC_BASE
 	ldr	r0, [r0, #PMC_SCRATCH41]
 	mov	pc, r0			@ jump to tegra_resume
@@ -462,6 +536,11 @@ tegra30_sdram_pad_save:
 	.word	0
 	.word	0
 	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
 
 tegra30_sdram_pad_address:
 	.word	TEGRA_EMC_BASE + EMC_CFG				@0x0
@@ -473,8 +552,26 @@ tegra30_sdram_pad_address:
 	.word	TEGRA_CLK_RESET_BASE + CLK_RESET_CLK_SOURCE_MSELECT	@0x18
 	.word	TEGRA_CLK_RESET_BASE + CLK_RESET_SCLK_BURST		@0x1c
 
+tegra114_sdram_pad_address:
+	.word	TEGRA_EMC0_BASE + EMC_CFG				@0x0
+	.word	TEGRA_EMC0_BASE + EMC_ZCAL_INTERVAL			@0x4
+	.word	TEGRA_EMC0_BASE + EMC_AUTO_CAL_INTERVAL			@0x8
+	.word	TEGRA_EMC0_BASE + EMC_XM2VTTGENPADCTRL			@0xc
+	.word	TEGRA_EMC0_BASE + EMC_XM2VTTGENPADCTRL2			@0x10
+	.word	TEGRA_PMC_BASE + PMC_IO_DPD_STATUS			@0x14
+	.word	TEGRA_CLK_RESET_BASE + CLK_RESET_CLK_SOURCE_MSELECT	@0x18
+	.word	TEGRA_CLK_RESET_BASE + CLK_RESET_SCLK_BURST		@0x1c
+	.word	TEGRA_EMC1_BASE + EMC_CFG				@0x20
+	.word	TEGRA_EMC1_BASE + EMC_ZCAL_INTERVAL			@0x24
+	.word	TEGRA_EMC1_BASE + EMC_AUTO_CAL_INTERVAL			@0x28
+	.word	TEGRA_EMC1_BASE + EMC_XM2VTTGENPADCTRL			@0x2c
+	.word	TEGRA_EMC1_BASE + EMC_XM2VTTGENPADCTRL2			@0x30
+
 tegra30_sdram_pad_size:
-	.word	tegra30_sdram_pad_address - tegra30_sdram_pad_save
+	.word	tegra114_sdram_pad_address - tegra30_sdram_pad_address
+
+tegra114_sdram_pad_size:
+	.word	tegra30_sdram_pad_size - tegra114_sdram_pad_address
 
 /*
  * tegra30_tear_down_core
@@ -496,6 +593,7 @@ tegra30_tear_down_core:
  * r5 = TEGRA_CLK_RESET_BASE
  * r6 = TEGRA_FLOW_CTRL_BASE
  * r7 = TEGRA_TMRUS_BASE
+ * r10= SoC ID
  */
 tegra30_switch_cpu_to_clk32k:
 	/*
@@ -542,6 +640,11 @@ tegra30_switch_cpu_to_clk32k:
 	bic	r0, r0, #(1 << 30)
 	str	r0, [r5, #CLK_RESET_PLLX_BASE]
 
+	cmp	r10, #TEGRA30
+	beq	_no_pll_in_iddq
+	pll_iddq_entry r1, r5, CLK_RESET_PLLX_MISC3, CLK_RESET_PLLX_MISC3_IDDQ
+_no_pll_in_iddq:
+
 	/* switch to CLKS */
 	mov	r0, #0	/* brust policy = 32KHz */
 	str	r0, [r5, #CLK_RESET_SCLK_BURST]
@@ -593,14 +696,19 @@ halted:
  * r5 = TEGRA_CLK_RESET_BASE
  * r6 = TEGRA_FLOW_CTRL_BASE
  * r7 = TEGRA_TMRUS_BASE
+ * r10= SoC ID
  */
 tegra30_sdram_self_refresh:
 
-	adr	r2, tegra30_sdram_pad_address
 	adr	r8, tegra30_sdram_pad_save
+	tegra_get_soc_id TEGRA_APB_MISC_BASE, r10
+	cmp	r10, #TEGRA30
+	adreq	r2, tegra30_sdram_pad_address
+	ldreq	r3, tegra30_sdram_pad_size
+	adrne	r2, tegra114_sdram_pad_address
+	ldrne	r3, tegra114_sdram_pad_size
 	mov	r9, #0
 
-	ldr	r3, tegra30_sdram_pad_size
 padsave:
 	ldr	r0, [r2, r9]		@ r0 is the addr in the pad_address
 
@@ -614,13 +722,18 @@ padsave_done:
 
 	dsb
 
-	mov32	r0, TEGRA_EMC_BASE	@ r0 reserved for emc base addr
+	cmp	r10, #TEGRA30
+	ldreq	r0, =TEGRA_EMC_BASE	@ r0 reserved for emc base addr
+	ldrne	r0, =TEGRA_EMC0_BASE
 
+enter_self_refresh:
+	cmp	r10, #TEGRA30
 	mov	r1, #0
 	str	r1, [r0, #EMC_ZCAL_INTERVAL]
 	str	r1, [r0, #EMC_AUTO_CAL_INTERVAL]
 	ldr	r1, [r0, #EMC_CFG]
 	bic	r1, r1, #(1 << 28)
+	bicne	r1, r1, #(1 << 29)
 	str	r1, [r0, #EMC_CFG]	@ disable DYN_SELF_REF
 
 	emc_timing_update r1, r0
@@ -659,11 +772,22 @@ emcself:
 	and	r1, r1, r2
 	str	r1, [r0, #EMC_XM2VTTGENPADCTRL]
 	ldr	r1, [r0, #EMC_XM2VTTGENPADCTRL2]
-	orr	r1, r1, #7		@ set E_NO_VTTGEN
+	cmp	r10, #TEGRA30
+	orreq	r1, r1, #7		@ set E_NO_VTTGEN
+	orrne	r1, r1, #0x3f
 	str	r1, [r0, #EMC_XM2VTTGENPADCTRL2]
 
 	emc_timing_update r1, r0
 
+	/* Tegra114 had dual EMC channel, now config the other one */
+	cmp	r10, #TEGRA114
+	bne	no_dual_emc_chanl
+	mov32	r1, TEGRA_EMC1_BASE
+	cmp	r0, r1
+	movne	r0, r1
+	bne	enter_self_refresh
+no_dual_emc_chanl:
+
 	ldr	r1, [r4, #PMC_CTRL]
 	tst	r1, #PMC_CTRL_SIDE_EFFECT_LP0
 	bne	pmc_io_dpd_skip
-- 
1.8.3.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 7/8] ARM: tegra114: add LP1 suspend support
@ 2013-07-26  9:15     ` Joseph Lo
  0 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-07-26  9:15 UTC (permalink / raw)
  To: linux-arm-kernel

The LP1 suspend mode will power off the CPU, clock gated the PLLs and put
SDRAM to self-refresh mode. Any interrupt can wake up device from LP1. The
sequence when LP1 suspending:

* tunning off L1 data cache and the MMU
* storing some EMC registers, DPD (deep power down) status, clk source of
  mselect and SCLK burst policy
* putting SDRAM into self-refresh
* switching CPU to CLK_M (12MHz OSC)
* tunning off PLLM, PLLP, PLLA, PLLC and PLLX
* switching SCLK to CLK_S (32KHz OSC)
* shutting off the CPU rail

The sequence of LP1 resuming:

* re-enabling PLLM, PLLP, PLLA, PLLC and PLLX
* restoring the clk source of mselect and SCLK burst policy
* restoring DPD status and some EMC registers
* resuming SDRAM to normal mode
* jumping to the "tegra_resume" from PMC_SCRATCH41

Due to the SDRAM will be put into self-refresh mode, the low level
procedures of LP1 suspending and resuming should be copied to
TEGRA_IRAM_CODE_AREA (TEGRA_IRAM_BASE + SZ_4K) when suspending. Before
restoring the CPU context when resuming, the SDRAM needs to be switched
back to normal mode. And the PLLs need to be re-enabled, SCLK burst policy
be restored. Then jumping to "tegra_resume" that was expected to be stored
in PMC_SCRATCH41 to restore CPU context and back to kernel.

Based on the work by: Bo Yan <byan@nvidia.com>

Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
 arch/arm/mach-tegra/Makefile        |   1 +
 arch/arm/mach-tegra/iomap.h         |   6 ++
 arch/arm/mach-tegra/pm.c            |   2 +
 arch/arm/mach-tegra/pm.h            |   2 +-
 arch/arm/mach-tegra/sleep-tegra30.S | 150 ++++++++++++++++++++++++++++++++----
 5 files changed, 147 insertions(+), 14 deletions(-)

diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index a3fe22d..f4e7063 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_TEGRA_PCI)			+= pcie.o
 
 obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= tegra114_speedo.o
 obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= sleep-tegra30.o
+obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= pm-tegra30.o
 ifeq ($(CONFIG_CPU_IDLE),y)
 obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= cpuidle-tegra114.o
 endif
diff --git a/arch/arm/mach-tegra/iomap.h b/arch/arm/mach-tegra/iomap.h
index 399fbca..7f463d7 100644
--- a/arch/arm/mach-tegra/iomap.h
+++ b/arch/arm/mach-tegra/iomap.h
@@ -237,6 +237,12 @@
 #define TEGRA_KFUSE_BASE		0x7000FC00
 #define TEGRA_KFUSE_SIZE		SZ_1K
 
+#define TEGRA_EMC0_BASE			0x7001A000
+#define TEGRA_EMC0_SIZE			SZ_2K
+
+#define TEGRA_EMC1_BASE			0x7001A800
+#define TEGRA_EMC1_SIZE			SZ_2K
+
 #define TEGRA_CSITE_BASE		0x70040000
 #define TEGRA_CSITE_SIZE		SZ_256K
 
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index 5ae7ee5..36cadaa 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -214,6 +214,7 @@ static bool tegra_lp1_iram_hook(void)
 		tegra20_lp1_iram_hook();
 		break;
 	case TEGRA30:
+	case TEGRA114:
 		tegra30_lp1_iram_hook();
 		break;
 	default:
@@ -238,6 +239,7 @@ static bool tegra_sleep_core_init(void)
 		tegra20_sleep_core_init();
 		break;
 	case TEGRA30:
+	case TEGRA114:
 		tegra30_sleep_core_init();
 		break;
 	default:
diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h
index 7aef651..1ccc0df 100644
--- a/arch/arm/mach-tegra/pm.h
+++ b/arch/arm/mach-tegra/pm.h
@@ -38,7 +38,7 @@ static inline void tegra20_lp1_iram_hook(void) {}
 static inline void void tegra20_sleep_core_init(void) {}
 #endif
 
-#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC) || defined(CONFIG_ARCH_TEGRA_114_SOC)
 void tegra30_lp1_iram_hook(void);
 void tegra30_sleep_core_init(void);
 #else
diff --git a/arch/arm/mach-tegra/sleep-tegra30.S b/arch/arm/mach-tegra/sleep-tegra30.S
index 5d2522a..e2f25f3 100644
--- a/arch/arm/mach-tegra/sleep-tegra30.S
+++ b/arch/arm/mach-tegra/sleep-tegra30.S
@@ -65,6 +65,10 @@
 #define CLK_RESET_PLLA_MISC		0xbc
 #define CLK_RESET_PLLX_BASE		0xe0
 #define CLK_RESET_PLLX_MISC		0xe4
+#define CLK_RESET_PLLX_MISC3		0x518
+#define CLK_RESET_PLLX_MISC3_IDDQ	3
+#define CLK_RESET_PLLM_MISC_IDDQ	5
+#define CLK_RESET_PLLC_MISC_IDDQ	26
 
 #define CLK_RESET_CLK_SOURCE_MSELECT	0x3b4
 
@@ -96,9 +100,15 @@
 	orreq	\rd, \rd, #(1 << 30)
 	streq	\rd, [\r_car_base, #\pll_base]
 	/* Enable lock detector */
+	.if	\pll_misc
+	ldr	\rd, [\r_car_base, #\pll_misc]
+	bic	\rd, \rd, #(1 << 18)
+	str	\rd, [\r_car_base, #\pll_misc]
+	ldr	\rd, [\r_car_base, #\pll_misc]
 	ldr	\rd, [\r_car_base, #\pll_misc]
 	orr	\rd, \rd, #(1 << 18)
 	str	\rd, [\r_car_base, #\pll_misc]
+	.endif
 .endm
 
 .macro pll_locked, rd, r_car_base, pll_base
@@ -108,6 +118,18 @@
 	beq	1b
 .endm
 
+.macro pll_iddq_exit, rd, car, iddq, iddq_bit
+	ldr	\rd, [\car, #\iddq]
+	bic	\rd, \rd, #(1<<\iddq_bit)
+	str	\rd, [\car, #\iddq]
+.endm
+
+.macro pll_iddq_entry, rd, car, iddq, iddq_bit
+	ldr	\rd, [\car, #\iddq]
+	orr	\rd, \rd, #(1<<\iddq_bit)
+	str	\rd, [\car, #\iddq]
+.endm
+
 #if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP)
 /*
  * tegra30_hotplug_shutdown(void)
@@ -309,6 +331,32 @@ ENTRY(tegra30_lp1_reset)
 	str	r1, [r0, #CLK_RESET_CCLK_DIVIDER]
 	str	r1, [r0, #CLK_RESET_SCLK_DIVIDER]
 
+	tegra_get_soc_id TEGRA_APB_MISC_BASE, r10
+	cmp	r10, #TEGRA30
+	beq	_no_pll_iddq_exit
+
+	pll_iddq_exit r1, r0, CLK_RESET_PLLM_MISC, CLK_RESET_PLLM_MISC_IDDQ
+	pll_iddq_exit r1, r0, CLK_RESET_PLLC_MISC, CLK_RESET_PLLC_MISC_IDDQ
+	pll_iddq_exit r1, r0, CLK_RESET_PLLX_MISC3, CLK_RESET_PLLX_MISC3_IDDQ
+
+	mov32	r7, TEGRA_TMRUS_BASE
+	ldr	r1, [r7]
+	add	r1, r1, #2
+	wait_until r1, r7, r3
+
+	/* enable PLLM via PMC */
+	mov32	r2, TEGRA_PMC_BASE
+	ldr	r1, [r2, #PMC_PLLP_WB0_OVERRIDE]
+	orr	r1, r1, #(1 << 12)
+	str	r1, [r2, #PMC_PLLP_WB0_OVERRIDE]
+
+	pll_enable r1, r0, CLK_RESET_PLLM_BASE, 0
+	pll_enable r1, r0, CLK_RESET_PLLC_BASE, 0
+	pll_enable r1, r0, CLK_RESET_PLLX_BASE, 0
+
+	b	_pll_m_c_x_done
+
+_no_pll_iddq_exit:
 	/* enable PLLM via PMC */
 	mov32	r2, TEGRA_PMC_BASE
 	ldr	r1, [r2, #PMC_PLLP_WB0_OVERRIDE]
@@ -316,11 +364,13 @@ ENTRY(tegra30_lp1_reset)
 	str	r1, [r2, #PMC_PLLP_WB0_OVERRIDE]
 
 	pll_enable r1, r0, CLK_RESET_PLLM_BASE, CLK_RESET_PLLM_MISC
-	pll_enable r1, r0, CLK_RESET_PLLP_BASE, CLK_RESET_PLLP_MISC
-	pll_enable r1, r0, CLK_RESET_PLLA_BASE, CLK_RESET_PLLA_MISC
 	pll_enable r1, r0, CLK_RESET_PLLC_BASE, CLK_RESET_PLLC_MISC
 	pll_enable r1, r0, CLK_RESET_PLLX_BASE, CLK_RESET_PLLX_MISC
 
+_pll_m_c_x_done:
+	pll_enable r1, r0, CLK_RESET_PLLP_BASE, CLK_RESET_PLLP_MISC
+	pll_enable r1, r0, CLK_RESET_PLLA_BASE, CLK_RESET_PLLA_MISC
+
 	pll_locked r1, r0, CLK_RESET_PLLM_BASE
 	pll_locked r1, r0, CLK_RESET_PLLP_BASE
 	pll_locked r1, r0, CLK_RESET_PLLA_BASE
@@ -340,8 +390,10 @@ ENTRY(tegra30_lp1_reset)
 	ldr	r4, [r5, #0x1C]		@ restore SCLK_BURST
 	str	r4, [r0, #CLK_RESET_SCLK_BURST]
 
-	mov32	r4, ((1 << 28) | (0x8))	@ burst policy is PLLX
-	str	r4, [r0, #CLK_RESET_CCLK_BURST]
+	cmp	r10, #TEGRA30
+	movweq	r4, #:lower16:((1 << 28) | (0x8))	@ burst policy is PLLX
+	movteq	r4, #:upper16:((1 << 28) | (0x8))
+	streq	r4, [r0, #CLK_RESET_CCLK_BURST]
 
 	/* Restore pad power state to normal */
 	ldr	r1, [r5, #0x14]		@ PMC_IO_DPD_STATUS
@@ -350,8 +402,13 @@ ENTRY(tegra30_lp1_reset)
 	orr	r1, r1, #(1 << 30)
 	str	r1, [r2, #PMC_IO_DPD_REQ]	@ DPD_OFF
 
-	mov32	r0, TEGRA_EMC_BASE	@ r0 reserved for emc base
+	cmp	r10, #TEGRA30
+	movweq	r0, #:lower16:TEGRA_EMC_BASE	@ r0 reserved for emc base
+	movteq	r0, #:upper16:TEGRA_EMC_BASE
+	movwne	r0, #:lower16:TEGRA_EMC0_BASE
+	movtne	r0, #:upper16:TEGRA_EMC0_BASE
 
+exit_self_refresh:
 	ldr	r1, [r5, #0xC]		@ restore EMC_XM2VTTGENPADCTRL
 	str	r1, [r0, #EMC_XM2VTTGENPADCTRL]
 	ldr	r1, [r5, #0x10]		@ restore EMC_XM2VTTGENPADCTRL2
@@ -366,8 +423,14 @@ ENTRY(tegra30_lp1_reset)
 
 	emc_timing_update r1, r0
 
+	cmp	r10, #TEGRA114
+	movweq	r1, #:lower16:TEGRA_EMC1_BASE
+	movteq	r1, #:upper16:TEGRA_EMC1_BASE
+	cmpeq	r0, r1
+
 	ldr	r1, [r0, #EMC_AUTO_CAL_CONFIG]
 	orr	r1, r1, #(1 << 31)	@ set AUTO_CAL_ACTIVE
+	orreq	r1, r1, #(1 << 27)	@ set slave mode for channel 1
 	str	r1, [r0, #EMC_AUTO_CAL_CONFIG]
 
 emc_wait_auto_cal_onetime:
@@ -382,9 +445,10 @@ emc_wait_auto_cal_onetime:
 	mov	r1, #0
 	str	r1, [r0, #EMC_SELF_REF]	@ take DRAM out of self refresh
 	mov	r1, #1
-	str	r1, [r0, #EMC_NOP]
-	str	r1, [r0, #EMC_NOP]
-	str	r1, [r0, #EMC_REFRESH]
+	cmp	r10, #TEGRA30
+	streq	r1, [r0, #EMC_NOP]
+	streq	r1, [r0, #EMC_NOP]
+	streq	r1, [r0, #EMC_REFRESH]
 
 	emc_device_mask r1, r0
 
@@ -446,6 +510,16 @@ zcal_done:
 	ldr	r1, [r5, #0x0]		@ restore EMC_CFG
 	str	r1, [r0, #EMC_CFG]
 
+	/* Tegra114 had dual EMC channel, now config the other one */
+	cmp	r10, #TEGRA114
+	bne	__no_dual_emc_chanl
+	mov32	r1, TEGRA_EMC1_BASE
+	cmp	r0, r1
+	movne	r0, r1
+	addne	r5, r5, #0x20
+	bne	exit_self_refresh
+__no_dual_emc_chanl:
+
 	mov32	r0, TEGRA_PMC_BASE
 	ldr	r0, [r0, #PMC_SCRATCH41]
 	mov	pc, r0			@ jump to tegra_resume
@@ -462,6 +536,11 @@ tegra30_sdram_pad_save:
 	.word	0
 	.word	0
 	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
+	.word	0
 
 tegra30_sdram_pad_address:
 	.word	TEGRA_EMC_BASE + EMC_CFG				@0x0
@@ -473,8 +552,26 @@ tegra30_sdram_pad_address:
 	.word	TEGRA_CLK_RESET_BASE + CLK_RESET_CLK_SOURCE_MSELECT	@0x18
 	.word	TEGRA_CLK_RESET_BASE + CLK_RESET_SCLK_BURST		@0x1c
 
+tegra114_sdram_pad_address:
+	.word	TEGRA_EMC0_BASE + EMC_CFG				@0x0
+	.word	TEGRA_EMC0_BASE + EMC_ZCAL_INTERVAL			@0x4
+	.word	TEGRA_EMC0_BASE + EMC_AUTO_CAL_INTERVAL			@0x8
+	.word	TEGRA_EMC0_BASE + EMC_XM2VTTGENPADCTRL			@0xc
+	.word	TEGRA_EMC0_BASE + EMC_XM2VTTGENPADCTRL2			@0x10
+	.word	TEGRA_PMC_BASE + PMC_IO_DPD_STATUS			@0x14
+	.word	TEGRA_CLK_RESET_BASE + CLK_RESET_CLK_SOURCE_MSELECT	@0x18
+	.word	TEGRA_CLK_RESET_BASE + CLK_RESET_SCLK_BURST		@0x1c
+	.word	TEGRA_EMC1_BASE + EMC_CFG				@0x20
+	.word	TEGRA_EMC1_BASE + EMC_ZCAL_INTERVAL			@0x24
+	.word	TEGRA_EMC1_BASE + EMC_AUTO_CAL_INTERVAL			@0x28
+	.word	TEGRA_EMC1_BASE + EMC_XM2VTTGENPADCTRL			@0x2c
+	.word	TEGRA_EMC1_BASE + EMC_XM2VTTGENPADCTRL2			@0x30
+
 tegra30_sdram_pad_size:
-	.word	tegra30_sdram_pad_address - tegra30_sdram_pad_save
+	.word	tegra114_sdram_pad_address - tegra30_sdram_pad_address
+
+tegra114_sdram_pad_size:
+	.word	tegra30_sdram_pad_size - tegra114_sdram_pad_address
 
 /*
  * tegra30_tear_down_core
@@ -496,6 +593,7 @@ tegra30_tear_down_core:
  * r5 = TEGRA_CLK_RESET_BASE
  * r6 = TEGRA_FLOW_CTRL_BASE
  * r7 = TEGRA_TMRUS_BASE
+ * r10= SoC ID
  */
 tegra30_switch_cpu_to_clk32k:
 	/*
@@ -542,6 +640,11 @@ tegra30_switch_cpu_to_clk32k:
 	bic	r0, r0, #(1 << 30)
 	str	r0, [r5, #CLK_RESET_PLLX_BASE]
 
+	cmp	r10, #TEGRA30
+	beq	_no_pll_in_iddq
+	pll_iddq_entry r1, r5, CLK_RESET_PLLX_MISC3, CLK_RESET_PLLX_MISC3_IDDQ
+_no_pll_in_iddq:
+
 	/* switch to CLKS */
 	mov	r0, #0	/* brust policy = 32KHz */
 	str	r0, [r5, #CLK_RESET_SCLK_BURST]
@@ -593,14 +696,19 @@ halted:
  * r5 = TEGRA_CLK_RESET_BASE
  * r6 = TEGRA_FLOW_CTRL_BASE
  * r7 = TEGRA_TMRUS_BASE
+ * r10= SoC ID
  */
 tegra30_sdram_self_refresh:
 
-	adr	r2, tegra30_sdram_pad_address
 	adr	r8, tegra30_sdram_pad_save
+	tegra_get_soc_id TEGRA_APB_MISC_BASE, r10
+	cmp	r10, #TEGRA30
+	adreq	r2, tegra30_sdram_pad_address
+	ldreq	r3, tegra30_sdram_pad_size
+	adrne	r2, tegra114_sdram_pad_address
+	ldrne	r3, tegra114_sdram_pad_size
 	mov	r9, #0
 
-	ldr	r3, tegra30_sdram_pad_size
 padsave:
 	ldr	r0, [r2, r9]		@ r0 is the addr in the pad_address
 
@@ -614,13 +722,18 @@ padsave_done:
 
 	dsb
 
-	mov32	r0, TEGRA_EMC_BASE	@ r0 reserved for emc base addr
+	cmp	r10, #TEGRA30
+	ldreq	r0, =TEGRA_EMC_BASE	@ r0 reserved for emc base addr
+	ldrne	r0, =TEGRA_EMC0_BASE
 
+enter_self_refresh:
+	cmp	r10, #TEGRA30
 	mov	r1, #0
 	str	r1, [r0, #EMC_ZCAL_INTERVAL]
 	str	r1, [r0, #EMC_AUTO_CAL_INTERVAL]
 	ldr	r1, [r0, #EMC_CFG]
 	bic	r1, r1, #(1 << 28)
+	bicne	r1, r1, #(1 << 29)
 	str	r1, [r0, #EMC_CFG]	@ disable DYN_SELF_REF
 
 	emc_timing_update r1, r0
@@ -659,11 +772,22 @@ emcself:
 	and	r1, r1, r2
 	str	r1, [r0, #EMC_XM2VTTGENPADCTRL]
 	ldr	r1, [r0, #EMC_XM2VTTGENPADCTRL2]
-	orr	r1, r1, #7		@ set E_NO_VTTGEN
+	cmp	r10, #TEGRA30
+	orreq	r1, r1, #7		@ set E_NO_VTTGEN
+	orrne	r1, r1, #0x3f
 	str	r1, [r0, #EMC_XM2VTTGENPADCTRL2]
 
 	emc_timing_update r1, r0
 
+	/* Tegra114 had dual EMC channel, now config the other one */
+	cmp	r10, #TEGRA114
+	bne	no_dual_emc_chanl
+	mov32	r1, TEGRA_EMC1_BASE
+	cmp	r0, r1
+	movne	r0, r1
+	bne	enter_self_refresh
+no_dual_emc_chanl:
+
 	ldr	r1, [r4, #PMC_CTRL]
 	tst	r1, #PMC_CTRL_SIDE_EFFECT_LP0
 	bne	pmc_io_dpd_skip
-- 
1.8.3.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 8/8] ARM: dts: tegra: enable LP1 suspend mode
  2013-07-26  9:15 ` Joseph Lo
@ 2013-07-26  9:15     ` Joseph Lo
  -1 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-07-26  9:15 UTC (permalink / raw)
  To: Stephen Warren
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Joseph Lo

Enabling the LP1 suspend mode for Tegra devices.

Signed-off-by: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
 arch/arm/boot/dts/tegra114-dalmore.dts     | 2 +-
 arch/arm/boot/dts/tegra20-colibri-512.dtsi | 2 +-
 arch/arm/boot/dts/tegra20-harmony.dts      | 2 +-
 arch/arm/boot/dts/tegra20-paz00.dts        | 2 +-
 arch/arm/boot/dts/tegra20-seaboard.dts     | 2 +-
 arch/arm/boot/dts/tegra20-tamonten.dtsi    | 2 +-
 arch/arm/boot/dts/tegra20-trimslice.dts    | 2 +-
 arch/arm/boot/dts/tegra20-ventana.dts      | 2 +-
 arch/arm/boot/dts/tegra20-whistler.dts     | 2 +-
 arch/arm/boot/dts/tegra30-beaver.dts       | 2 +-
 arch/arm/boot/dts/tegra30-cardhu.dtsi      | 2 +-
 11 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/arch/arm/boot/dts/tegra114-dalmore.dts b/arch/arm/boot/dts/tegra114-dalmore.dts
index aeda147..a2a1b5f 100644
--- a/arch/arm/boot/dts/tegra114-dalmore.dts
+++ b/arch/arm/boot/dts/tegra114-dalmore.dts
@@ -1024,7 +1024,7 @@
 
 	pmc {
 		nvidia,invert-interrupt;
-		nvidia,suspend-mode = <2>;
+		nvidia,suspend-mode = <1>;
 		nvidia,cpu-pwr-good-time = <500>;
 		nvidia,cpu-pwr-off-time = <300>;
 		nvidia,core-pwr-good-time = <641 3845>;
diff --git a/arch/arm/boot/dts/tegra20-colibri-512.dtsi b/arch/arm/boot/dts/tegra20-colibri-512.dtsi
index 2fcb3f2..06c5fa0 100644
--- a/arch/arm/boot/dts/tegra20-colibri-512.dtsi
+++ b/arch/arm/boot/dts/tegra20-colibri-512.dtsi
@@ -363,7 +363,7 @@
 	};
 
 	pmc {
-		nvidia,suspend-mode = <2>;
+		nvidia,suspend-mode = <1>;
 		nvidia,cpu-pwr-good-time = <5000>;
 		nvidia,cpu-pwr-off-time = <5000>;
 		nvidia,core-pwr-good-time = <3845 3845>;
diff --git a/arch/arm/boot/dts/tegra20-harmony.dts b/arch/arm/boot/dts/tegra20-harmony.dts
index d9f89cd..d44cf90 100644
--- a/arch/arm/boot/dts/tegra20-harmony.dts
+++ b/arch/arm/boot/dts/tegra20-harmony.dts
@@ -417,7 +417,7 @@
 
 	pmc {
 		nvidia,invert-interrupt;
-		nvidia,suspend-mode = <2>;
+		nvidia,suspend-mode = <1>;
 		nvidia,cpu-pwr-good-time = <5000>;
 		nvidia,cpu-pwr-off-time = <5000>;
 		nvidia,core-pwr-good-time = <3845 3845>;
diff --git a/arch/arm/boot/dts/tegra20-paz00.dts b/arch/arm/boot/dts/tegra20-paz00.dts
index cfd1276..8d71fc9 100644
--- a/arch/arm/boot/dts/tegra20-paz00.dts
+++ b/arch/arm/boot/dts/tegra20-paz00.dts
@@ -417,7 +417,7 @@
 
 	pmc {
 		nvidia,invert-interrupt;
-		nvidia,suspend-mode = <2>;
+		nvidia,suspend-mode = <1>;
 		nvidia,cpu-pwr-good-time = <2000>;
 		nvidia,cpu-pwr-off-time = <0>;
 		nvidia,core-pwr-good-time = <3845 3845>;
diff --git a/arch/arm/boot/dts/tegra20-seaboard.dts b/arch/arm/boot/dts/tegra20-seaboard.dts
index 365760b..152dfb7 100644
--- a/arch/arm/boot/dts/tegra20-seaboard.dts
+++ b/arch/arm/boot/dts/tegra20-seaboard.dts
@@ -518,7 +518,7 @@
 
 	pmc {
 		nvidia,invert-interrupt;
-		nvidia,suspend-mode = <2>;
+		nvidia,suspend-mode = <1>;
 		nvidia,cpu-pwr-good-time = <5000>;
 		nvidia,cpu-pwr-off-time = <5000>;
 		nvidia,core-pwr-good-time = <3845 3845>;
diff --git a/arch/arm/boot/dts/tegra20-tamonten.dtsi b/arch/arm/boot/dts/tegra20-tamonten.dtsi
index c54faae..f00febe 100644
--- a/arch/arm/boot/dts/tegra20-tamonten.dtsi
+++ b/arch/arm/boot/dts/tegra20-tamonten.dtsi
@@ -459,7 +459,7 @@
 
 	pmc {
 		nvidia,invert-interrupt;
-		nvidia,suspend-mode = <2>;
+		nvidia,suspend-mode = <1>;
 		nvidia,cpu-pwr-good-time = <5000>;
 		nvidia,cpu-pwr-off-time = <5000>;
 		nvidia,core-pwr-good-time = <3845 3845>;
diff --git a/arch/arm/boot/dts/tegra20-trimslice.dts b/arch/arm/boot/dts/tegra20-trimslice.dts
index ed4b901..3e4eefb 100644
--- a/arch/arm/boot/dts/tegra20-trimslice.dts
+++ b/arch/arm/boot/dts/tegra20-trimslice.dts
@@ -302,7 +302,7 @@
 	};
 
 	pmc {
-		nvidia,suspend-mode = <2>;
+		nvidia,suspend-mode = <1>;
 		nvidia,cpu-pwr-good-time = <5000>;
 		nvidia,cpu-pwr-off-time = <5000>;
 		nvidia,core-pwr-good-time = <3845 3845>;
diff --git a/arch/arm/boot/dts/tegra20-ventana.dts b/arch/arm/boot/dts/tegra20-ventana.dts
index 7f8c28d..aab872c 100644
--- a/arch/arm/boot/dts/tegra20-ventana.dts
+++ b/arch/arm/boot/dts/tegra20-ventana.dts
@@ -494,7 +494,7 @@
 
 	pmc {
 		nvidia,invert-interrupt;
-		nvidia,suspend-mode = <2>;
+		nvidia,suspend-mode = <1>;
 		nvidia,cpu-pwr-good-time = <2000>;
 		nvidia,cpu-pwr-off-time = <100>;
 		nvidia,core-pwr-good-time = <3845 3845>;
diff --git a/arch/arm/boot/dts/tegra20-whistler.dts b/arch/arm/boot/dts/tegra20-whistler.dts
index ab67c94..3899cae 100644
--- a/arch/arm/boot/dts/tegra20-whistler.dts
+++ b/arch/arm/boot/dts/tegra20-whistler.dts
@@ -497,7 +497,7 @@
 
 	pmc {
 		nvidia,invert-interrupt;
-		nvidia,suspend-mode = <2>;
+		nvidia,suspend-mode = <1>;
 		nvidia,cpu-pwr-good-time = <2000>;
 		nvidia,cpu-pwr-off-time = <1000>;
 		nvidia,core-pwr-good-time = <0 3845>;
diff --git a/arch/arm/boot/dts/tegra30-beaver.dts b/arch/arm/boot/dts/tegra30-beaver.dts
index 87c5f7b..7378927 100644
--- a/arch/arm/boot/dts/tegra30-beaver.dts
+++ b/arch/arm/boot/dts/tegra30-beaver.dts
@@ -262,7 +262,7 @@
 	pmc {
 		status = "okay";
 		nvidia,invert-interrupt;
-		nvidia,suspend-mode = <2>;
+		nvidia,suspend-mode = <1>;
 		nvidia,cpu-pwr-good-time = <2000>;
 		nvidia,cpu-pwr-off-time = <200>;
 		nvidia,core-pwr-good-time = <3845 3845>;
diff --git a/arch/arm/boot/dts/tegra30-cardhu.dtsi b/arch/arm/boot/dts/tegra30-cardhu.dtsi
index c011b67..d78c90c 100644
--- a/arch/arm/boot/dts/tegra30-cardhu.dtsi
+++ b/arch/arm/boot/dts/tegra30-cardhu.dtsi
@@ -314,7 +314,7 @@
 	pmc {
 		status = "okay";
 		nvidia,invert-interrupt;
-		nvidia,suspend-mode = <2>;
+		nvidia,suspend-mode = <1>;
 		nvidia,cpu-pwr-good-time = <2000>;
 		nvidia,cpu-pwr-off-time = <200>;
 		nvidia,core-pwr-good-time = <3845 3845>;
-- 
1.8.3.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 8/8] ARM: dts: tegra: enable LP1 suspend mode
@ 2013-07-26  9:15     ` Joseph Lo
  0 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-07-26  9:15 UTC (permalink / raw)
  To: linux-arm-kernel

Enabling the LP1 suspend mode for Tegra devices.

Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
 arch/arm/boot/dts/tegra114-dalmore.dts     | 2 +-
 arch/arm/boot/dts/tegra20-colibri-512.dtsi | 2 +-
 arch/arm/boot/dts/tegra20-harmony.dts      | 2 +-
 arch/arm/boot/dts/tegra20-paz00.dts        | 2 +-
 arch/arm/boot/dts/tegra20-seaboard.dts     | 2 +-
 arch/arm/boot/dts/tegra20-tamonten.dtsi    | 2 +-
 arch/arm/boot/dts/tegra20-trimslice.dts    | 2 +-
 arch/arm/boot/dts/tegra20-ventana.dts      | 2 +-
 arch/arm/boot/dts/tegra20-whistler.dts     | 2 +-
 arch/arm/boot/dts/tegra30-beaver.dts       | 2 +-
 arch/arm/boot/dts/tegra30-cardhu.dtsi      | 2 +-
 11 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/arch/arm/boot/dts/tegra114-dalmore.dts b/arch/arm/boot/dts/tegra114-dalmore.dts
index aeda147..a2a1b5f 100644
--- a/arch/arm/boot/dts/tegra114-dalmore.dts
+++ b/arch/arm/boot/dts/tegra114-dalmore.dts
@@ -1024,7 +1024,7 @@
 
 	pmc {
 		nvidia,invert-interrupt;
-		nvidia,suspend-mode = <2>;
+		nvidia,suspend-mode = <1>;
 		nvidia,cpu-pwr-good-time = <500>;
 		nvidia,cpu-pwr-off-time = <300>;
 		nvidia,core-pwr-good-time = <641 3845>;
diff --git a/arch/arm/boot/dts/tegra20-colibri-512.dtsi b/arch/arm/boot/dts/tegra20-colibri-512.dtsi
index 2fcb3f2..06c5fa0 100644
--- a/arch/arm/boot/dts/tegra20-colibri-512.dtsi
+++ b/arch/arm/boot/dts/tegra20-colibri-512.dtsi
@@ -363,7 +363,7 @@
 	};
 
 	pmc {
-		nvidia,suspend-mode = <2>;
+		nvidia,suspend-mode = <1>;
 		nvidia,cpu-pwr-good-time = <5000>;
 		nvidia,cpu-pwr-off-time = <5000>;
 		nvidia,core-pwr-good-time = <3845 3845>;
diff --git a/arch/arm/boot/dts/tegra20-harmony.dts b/arch/arm/boot/dts/tegra20-harmony.dts
index d9f89cd..d44cf90 100644
--- a/arch/arm/boot/dts/tegra20-harmony.dts
+++ b/arch/arm/boot/dts/tegra20-harmony.dts
@@ -417,7 +417,7 @@
 
 	pmc {
 		nvidia,invert-interrupt;
-		nvidia,suspend-mode = <2>;
+		nvidia,suspend-mode = <1>;
 		nvidia,cpu-pwr-good-time = <5000>;
 		nvidia,cpu-pwr-off-time = <5000>;
 		nvidia,core-pwr-good-time = <3845 3845>;
diff --git a/arch/arm/boot/dts/tegra20-paz00.dts b/arch/arm/boot/dts/tegra20-paz00.dts
index cfd1276..8d71fc9 100644
--- a/arch/arm/boot/dts/tegra20-paz00.dts
+++ b/arch/arm/boot/dts/tegra20-paz00.dts
@@ -417,7 +417,7 @@
 
 	pmc {
 		nvidia,invert-interrupt;
-		nvidia,suspend-mode = <2>;
+		nvidia,suspend-mode = <1>;
 		nvidia,cpu-pwr-good-time = <2000>;
 		nvidia,cpu-pwr-off-time = <0>;
 		nvidia,core-pwr-good-time = <3845 3845>;
diff --git a/arch/arm/boot/dts/tegra20-seaboard.dts b/arch/arm/boot/dts/tegra20-seaboard.dts
index 365760b..152dfb7 100644
--- a/arch/arm/boot/dts/tegra20-seaboard.dts
+++ b/arch/arm/boot/dts/tegra20-seaboard.dts
@@ -518,7 +518,7 @@
 
 	pmc {
 		nvidia,invert-interrupt;
-		nvidia,suspend-mode = <2>;
+		nvidia,suspend-mode = <1>;
 		nvidia,cpu-pwr-good-time = <5000>;
 		nvidia,cpu-pwr-off-time = <5000>;
 		nvidia,core-pwr-good-time = <3845 3845>;
diff --git a/arch/arm/boot/dts/tegra20-tamonten.dtsi b/arch/arm/boot/dts/tegra20-tamonten.dtsi
index c54faae..f00febe 100644
--- a/arch/arm/boot/dts/tegra20-tamonten.dtsi
+++ b/arch/arm/boot/dts/tegra20-tamonten.dtsi
@@ -459,7 +459,7 @@
 
 	pmc {
 		nvidia,invert-interrupt;
-		nvidia,suspend-mode = <2>;
+		nvidia,suspend-mode = <1>;
 		nvidia,cpu-pwr-good-time = <5000>;
 		nvidia,cpu-pwr-off-time = <5000>;
 		nvidia,core-pwr-good-time = <3845 3845>;
diff --git a/arch/arm/boot/dts/tegra20-trimslice.dts b/arch/arm/boot/dts/tegra20-trimslice.dts
index ed4b901..3e4eefb 100644
--- a/arch/arm/boot/dts/tegra20-trimslice.dts
+++ b/arch/arm/boot/dts/tegra20-trimslice.dts
@@ -302,7 +302,7 @@
 	};
 
 	pmc {
-		nvidia,suspend-mode = <2>;
+		nvidia,suspend-mode = <1>;
 		nvidia,cpu-pwr-good-time = <5000>;
 		nvidia,cpu-pwr-off-time = <5000>;
 		nvidia,core-pwr-good-time = <3845 3845>;
diff --git a/arch/arm/boot/dts/tegra20-ventana.dts b/arch/arm/boot/dts/tegra20-ventana.dts
index 7f8c28d..aab872c 100644
--- a/arch/arm/boot/dts/tegra20-ventana.dts
+++ b/arch/arm/boot/dts/tegra20-ventana.dts
@@ -494,7 +494,7 @@
 
 	pmc {
 		nvidia,invert-interrupt;
-		nvidia,suspend-mode = <2>;
+		nvidia,suspend-mode = <1>;
 		nvidia,cpu-pwr-good-time = <2000>;
 		nvidia,cpu-pwr-off-time = <100>;
 		nvidia,core-pwr-good-time = <3845 3845>;
diff --git a/arch/arm/boot/dts/tegra20-whistler.dts b/arch/arm/boot/dts/tegra20-whistler.dts
index ab67c94..3899cae 100644
--- a/arch/arm/boot/dts/tegra20-whistler.dts
+++ b/arch/arm/boot/dts/tegra20-whistler.dts
@@ -497,7 +497,7 @@
 
 	pmc {
 		nvidia,invert-interrupt;
-		nvidia,suspend-mode = <2>;
+		nvidia,suspend-mode = <1>;
 		nvidia,cpu-pwr-good-time = <2000>;
 		nvidia,cpu-pwr-off-time = <1000>;
 		nvidia,core-pwr-good-time = <0 3845>;
diff --git a/arch/arm/boot/dts/tegra30-beaver.dts b/arch/arm/boot/dts/tegra30-beaver.dts
index 87c5f7b..7378927 100644
--- a/arch/arm/boot/dts/tegra30-beaver.dts
+++ b/arch/arm/boot/dts/tegra30-beaver.dts
@@ -262,7 +262,7 @@
 	pmc {
 		status = "okay";
 		nvidia,invert-interrupt;
-		nvidia,suspend-mode = <2>;
+		nvidia,suspend-mode = <1>;
 		nvidia,cpu-pwr-good-time = <2000>;
 		nvidia,cpu-pwr-off-time = <200>;
 		nvidia,core-pwr-good-time = <3845 3845>;
diff --git a/arch/arm/boot/dts/tegra30-cardhu.dtsi b/arch/arm/boot/dts/tegra30-cardhu.dtsi
index c011b67..d78c90c 100644
--- a/arch/arm/boot/dts/tegra30-cardhu.dtsi
+++ b/arch/arm/boot/dts/tegra30-cardhu.dtsi
@@ -314,7 +314,7 @@
 	pmc {
 		status = "okay";
 		nvidia,invert-interrupt;
-		nvidia,suspend-mode = <2>;
+		nvidia,suspend-mode = <1>;
 		nvidia,cpu-pwr-good-time = <2000>;
 		nvidia,cpu-pwr-off-time = <200>;
 		nvidia,core-pwr-good-time = <3845 3845>;
-- 
1.8.3.4

^ permalink raw reply related	[flat|nested] 88+ messages in thread

* Re: [PATCH 0/8] ARM: tegra: support LP1 suspend mode
  2013-07-26  9:15 ` Joseph Lo
@ 2013-07-27 16:12     ` Marc Dietrich
  -1 siblings, 0 replies; 88+ messages in thread
From: Marc Dietrich @ 2013-07-27 16:12 UTC (permalink / raw)
  To: Joseph Lo
  Cc: Stephen Warren, linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Friday 26 July 2013 17:15:02 Joseph Lo wrote:
> This series adds the support of LP1 suspend mode for Tegra.
> 
> Verified on Seaboard, Cardhu and Dalmore.

tested on AC100 and worked fine, except

[   68.448007] PM: Syncing filesystems ... done.
[   71.370086] Freezing user space processes ... (elapsed 0.001 seconds) done.
[   71.370088] Freezing remaining freezable tasks ... (elapsed 0.001 seconds) 
done.
[   71.371253] Suspending console(s) (use no_console_suspend to debug)
[   71.638481] PM: suspend of devices complete after 266.604 msecs
[   71.638905] PM: late suspend of devices complete after 0.417 msecs
[   71.639477] PM: noirq suspend of devices complete after 0.568 msecs
[   71.639480] Disabling non-boot CPUs ...
[   71.640242] CPU1: shutdown
[   71.641216] Entering suspend state LP1
[   71.641267] Enabling non-boot CPUs ...
[   71.643125] CPU1: Booted secondary processor
[   71.643449] CPU1 is up
[   71.643786] PM: noirq resume of devices complete after 0.331 msecs
[   71.644300] PM: early resume of devices complete after 0.364 msecs
[   71.648038] ------------[ cut here ]------------
[   71.648061] WARNING: CPU: 0 PID: 1025 at kernel/irq/manage.c:529 
irq_set_irq_wake+0xb8/0xf0()
[   71.648064] Unbalanced IRQ 384 wake disable
[   71.648068] Modules linked in:
[   71.648075] CPU: 0 PID: 1025 Comm: bash Not tainted 3.11.0-rc2-
next-20130726-00008-gef01509-dirty #6
[   71.648107] [<c001551c>] (unwind_backtrace+0x0/0xf8) from [<c001154c>] 
(show_stack+0x10/0x14)
[   71.648128] [<c001154c>] (show_stack+0x10/0x14) from [<c0515288>] 
(dump_stack+0x80/0xc4)
[   71.648142] [<c0515288>] (dump_stack+0x80/0xc4) from [<c0025340>] 
(warn_slowpath_common+0x64/0x88)
[   71.648151] [<c0025340>] (warn_slowpath_common+0x64/0x88) from [<c00253f8>] 
(warn_slowpath_fmt+0x30/0x40)
[   71.648161] [<c00253f8>] (warn_slowpath_fmt+0x30/0x40) from [<c007ff8c>] 
(irq_set_irq_wake+0xb8/0xf0)
[   71.648178] [<c007ff8c>] (irq_set_irq_wake+0xb8/0xf0) from [<c0334d00>] 
(tps6586x_rtc_resume+0x30/0x38)
[   71.648192] [<c0334d00>] (tps6586x_rtc_resume+0x30/0x38) from [<c02951d0>] 
(platform_pm_resume+0x2c/0x4c)
[   71.648202] [<c02951d0>] (platform_pm_resume+0x2c/0x4c) from [<c0299d40>] 
(dpm_run_callback.isra.5+0x18/0x38)
[   71.648211] [<c0299d40>] (dpm_run_callback.isra.5+0x18/0x38) from 
[<c029a044>] (device_resume+0xb0/0x170)
[   71.648219] [<c029a044>] (device_resume+0xb0/0x170) from [<c029aeb4>] 
(dpm_resume+0xe8/0x20c)
[   71.648227] [<c029aeb4>] (dpm_resume+0xe8/0x20c) from [<c029b174>] 
(dpm_resume_end+0xc/0x18)
[   71.648245] [<c029b174>] (dpm_resume_end+0xc/0x18) from [<c005e480>] 
(suspend_devices_and_enter+0xdc/0x2f8)
[   71.648256] [<c005e480>] (suspend_devices_and_enter+0xdc/0x2f8) from 
[<c005e810>] (pm_suspend+0x174/0x1e8)
[   71.648265] [<c005e810>] (pm_suspend+0x174/0x1e8) from [<c005d710>] 
(state_store+0x6c/0xbc)
[   71.648285] [<c005d710>] (state_store+0x6c/0xbc) from [<c01eca78>] 
(kobj_attr_store+0x14/0x20)
[   71.648298] [<c01eca78>] (kobj_attr_store+0x14/0x20) from [<c011e970>] 
(sysfs_write_file+0x168/0x198)
[   71.648312] [<c011e970>] (sysfs_write_file+0x168/0x198) from [<c00cd060>] 
(vfs_write+0xb0/0x188)
[   71.648321] [<c00cd060>] (vfs_write+0xb0/0x188) from [<c00cd410>] 
(SyS_write+0x3c/0x70)
[   71.648330] [<c00cd410>] (SyS_write+0x3c/0x70) from [<c000e560>] 
(ret_fast_syscall+0x0/0x30)
[   71.648334] ---[ end trace e5ed8212ac4db5da ]---
[   72.063578] mmc1: power class selection to bus width 8 failed
[   72.297753] usb 2-1: reset high-speed USB device number 2 using tegra-ehci
[   72.567736] usb 3-1: reset high-speed USB device number 2 using tegra-ehci
[   73.022058] usb 3-1.3: reset high-speed USB device number 4 using tegra-
ehci
[   73.216064] usb 3-1.2: reset high-speed USB device number 3 using tegra-
ehci
[   73.360225] PM: resume of devices complete after 1715.937 msecs
[   73.361008] Restarting tasks ... done.

and sdcard being offline after resume (fixed by CONFIG_MMC_UNSAFE_RESUME=y). 
Anyway, thanks for your great work!

Tested-By: Marc Dietrich <marvin24-Mmb7MZpHnFY@public.gmane.org>

> And tested with THUMB2_KERNEL as well.
> 
> P.S.
> A known issue on Dalmore, the LP1 resume may take 10 ~ 15 seconds due to
> the 792MHz BCT. It can't be reproduced on Seaboard or Cardhu. And the
> root cause of this is about the default settings of EMC registers that
> cause the DRAM can't leave self-refresh mode immediately. If you want
> to test with quick LP1 resume on Dalmore, I can provide another HACK for
> this.
> 
> Joseph Lo (8):
>   ARM: tegra: add common resume handling code for LP1 resuming
>   ARM: tegra: config the polarity of the request of sys clock
>   clk: tegra114: add LP1 suspend/resume support
>   ARM: tegra: add common LP1 suspend support
>   ARM: tegra30: add LP1 suspend support
>   ARM: tegra20: add LP1 suspend support
>   ARM: tegra114: add LP1 suspend support
>   ARM: dts: tegra: enable LP1 suspend mode
> 
>  arch/arm/boot/dts/tegra114-dalmore.dts     |   2 +-
>  arch/arm/boot/dts/tegra20-colibri-512.dtsi |   2 +-
>  arch/arm/boot/dts/tegra20-harmony.dts      |   2 +-
>  arch/arm/boot/dts/tegra20-paz00.dts        |   2 +-
>  arch/arm/boot/dts/tegra20-seaboard.dts     |   2 +-
>  arch/arm/boot/dts/tegra20-tamonten.dtsi    |   2 +-
>  arch/arm/boot/dts/tegra20-trimslice.dts    |   2 +-
>  arch/arm/boot/dts/tegra20-ventana.dts      |   2 +-
>  arch/arm/boot/dts/tegra20-whistler.dts     |   2 +-
>  arch/arm/boot/dts/tegra30-beaver.dts       |   2 +-
>  arch/arm/boot/dts/tegra30-cardhu.dtsi      |   2 +-
>  arch/arm/mach-tegra/Makefile               |   3 +
>  arch/arm/mach-tegra/iomap.h                |   6 +
>  arch/arm/mach-tegra/pm-tegra20.c           |  37 ++
>  arch/arm/mach-tegra/pm-tegra30.c           |  37 ++
>  arch/arm/mach-tegra/pm.c                   | 121 +++++-
>  arch/arm/mach-tegra/pm.h                   |  23 ++
>  arch/arm/mach-tegra/pmc.c                  |  36 +-
>  arch/arm/mach-tegra/pmc.h                  |   3 +
>  arch/arm/mach-tegra/reset-handler.S        |  13 +
>  arch/arm/mach-tegra/reset.c                |   2 +
>  arch/arm/mach-tegra/reset.h                |   4 +
>  arch/arm/mach-tegra/sleep-tegra20.S        | 300 ++++++++++++++
>  arch/arm/mach-tegra/sleep-tegra30.S        | 615
> +++++++++++++++++++++++++++++ arch/arm/mach-tegra/sleep.S                | 
>  8 +-
>  arch/arm/mach-tegra/sleep.h                |  18 +
>  drivers/clk/tegra/clk-tegra114.c           |  32 ++
>  27 files changed, 1259 insertions(+), 21 deletions(-)
>  create mode 100644 arch/arm/mach-tegra/pm-tegra20.c
>  create mode 100644 arch/arm/mach-tegra/pm-tegra30.c

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 0/8] ARM: tegra: support LP1 suspend mode
@ 2013-07-27 16:12     ` Marc Dietrich
  0 siblings, 0 replies; 88+ messages in thread
From: Marc Dietrich @ 2013-07-27 16:12 UTC (permalink / raw)
  To: linux-arm-kernel

On Friday 26 July 2013 17:15:02 Joseph Lo wrote:
> This series adds the support of LP1 suspend mode for Tegra.
> 
> Verified on Seaboard, Cardhu and Dalmore.

tested on AC100 and worked fine, except

[   68.448007] PM: Syncing filesystems ... done.
[   71.370086] Freezing user space processes ... (elapsed 0.001 seconds) done.
[   71.370088] Freezing remaining freezable tasks ... (elapsed 0.001 seconds) 
done.
[   71.371253] Suspending console(s) (use no_console_suspend to debug)
[   71.638481] PM: suspend of devices complete after 266.604 msecs
[   71.638905] PM: late suspend of devices complete after 0.417 msecs
[   71.639477] PM: noirq suspend of devices complete after 0.568 msecs
[   71.639480] Disabling non-boot CPUs ...
[   71.640242] CPU1: shutdown
[   71.641216] Entering suspend state LP1
[   71.641267] Enabling non-boot CPUs ...
[   71.643125] CPU1: Booted secondary processor
[   71.643449] CPU1 is up
[   71.643786] PM: noirq resume of devices complete after 0.331 msecs
[   71.644300] PM: early resume of devices complete after 0.364 msecs
[   71.648038] ------------[ cut here ]------------
[   71.648061] WARNING: CPU: 0 PID: 1025 at kernel/irq/manage.c:529 
irq_set_irq_wake+0xb8/0xf0()
[   71.648064] Unbalanced IRQ 384 wake disable
[   71.648068] Modules linked in:
[   71.648075] CPU: 0 PID: 1025 Comm: bash Not tainted 3.11.0-rc2-
next-20130726-00008-gef01509-dirty #6
[   71.648107] [<c001551c>] (unwind_backtrace+0x0/0xf8) from [<c001154c>] 
(show_stack+0x10/0x14)
[   71.648128] [<c001154c>] (show_stack+0x10/0x14) from [<c0515288>] 
(dump_stack+0x80/0xc4)
[   71.648142] [<c0515288>] (dump_stack+0x80/0xc4) from [<c0025340>] 
(warn_slowpath_common+0x64/0x88)
[   71.648151] [<c0025340>] (warn_slowpath_common+0x64/0x88) from [<c00253f8>] 
(warn_slowpath_fmt+0x30/0x40)
[   71.648161] [<c00253f8>] (warn_slowpath_fmt+0x30/0x40) from [<c007ff8c>] 
(irq_set_irq_wake+0xb8/0xf0)
[   71.648178] [<c007ff8c>] (irq_set_irq_wake+0xb8/0xf0) from [<c0334d00>] 
(tps6586x_rtc_resume+0x30/0x38)
[   71.648192] [<c0334d00>] (tps6586x_rtc_resume+0x30/0x38) from [<c02951d0>] 
(platform_pm_resume+0x2c/0x4c)
[   71.648202] [<c02951d0>] (platform_pm_resume+0x2c/0x4c) from [<c0299d40>] 
(dpm_run_callback.isra.5+0x18/0x38)
[   71.648211] [<c0299d40>] (dpm_run_callback.isra.5+0x18/0x38) from 
[<c029a044>] (device_resume+0xb0/0x170)
[   71.648219] [<c029a044>] (device_resume+0xb0/0x170) from [<c029aeb4>] 
(dpm_resume+0xe8/0x20c)
[   71.648227] [<c029aeb4>] (dpm_resume+0xe8/0x20c) from [<c029b174>] 
(dpm_resume_end+0xc/0x18)
[   71.648245] [<c029b174>] (dpm_resume_end+0xc/0x18) from [<c005e480>] 
(suspend_devices_and_enter+0xdc/0x2f8)
[   71.648256] [<c005e480>] (suspend_devices_and_enter+0xdc/0x2f8) from 
[<c005e810>] (pm_suspend+0x174/0x1e8)
[   71.648265] [<c005e810>] (pm_suspend+0x174/0x1e8) from [<c005d710>] 
(state_store+0x6c/0xbc)
[   71.648285] [<c005d710>] (state_store+0x6c/0xbc) from [<c01eca78>] 
(kobj_attr_store+0x14/0x20)
[   71.648298] [<c01eca78>] (kobj_attr_store+0x14/0x20) from [<c011e970>] 
(sysfs_write_file+0x168/0x198)
[   71.648312] [<c011e970>] (sysfs_write_file+0x168/0x198) from [<c00cd060>] 
(vfs_write+0xb0/0x188)
[   71.648321] [<c00cd060>] (vfs_write+0xb0/0x188) from [<c00cd410>] 
(SyS_write+0x3c/0x70)
[   71.648330] [<c00cd410>] (SyS_write+0x3c/0x70) from [<c000e560>] 
(ret_fast_syscall+0x0/0x30)
[   71.648334] ---[ end trace e5ed8212ac4db5da ]---
[   72.063578] mmc1: power class selection to bus width 8 failed
[   72.297753] usb 2-1: reset high-speed USB device number 2 using tegra-ehci
[   72.567736] usb 3-1: reset high-speed USB device number 2 using tegra-ehci
[   73.022058] usb 3-1.3: reset high-speed USB device number 4 using tegra-
ehci
[   73.216064] usb 3-1.2: reset high-speed USB device number 3 using tegra-
ehci
[   73.360225] PM: resume of devices complete after 1715.937 msecs
[   73.361008] Restarting tasks ... done.

and sdcard being offline after resume (fixed by CONFIG_MMC_UNSAFE_RESUME=y). 
Anyway, thanks for your great work!

Tested-By: Marc Dietrich <marvin24@gmx.de>

> And tested with THUMB2_KERNEL as well.
> 
> P.S.
> A known issue on Dalmore, the LP1 resume may take 10 ~ 15 seconds due to
> the 792MHz BCT. It can't be reproduced on Seaboard or Cardhu. And the
> root cause of this is about the default settings of EMC registers that
> cause the DRAM can't leave self-refresh mode immediately. If you want
> to test with quick LP1 resume on Dalmore, I can provide another HACK for
> this.
> 
> Joseph Lo (8):
>   ARM: tegra: add common resume handling code for LP1 resuming
>   ARM: tegra: config the polarity of the request of sys clock
>   clk: tegra114: add LP1 suspend/resume support
>   ARM: tegra: add common LP1 suspend support
>   ARM: tegra30: add LP1 suspend support
>   ARM: tegra20: add LP1 suspend support
>   ARM: tegra114: add LP1 suspend support
>   ARM: dts: tegra: enable LP1 suspend mode
> 
>  arch/arm/boot/dts/tegra114-dalmore.dts     |   2 +-
>  arch/arm/boot/dts/tegra20-colibri-512.dtsi |   2 +-
>  arch/arm/boot/dts/tegra20-harmony.dts      |   2 +-
>  arch/arm/boot/dts/tegra20-paz00.dts        |   2 +-
>  arch/arm/boot/dts/tegra20-seaboard.dts     |   2 +-
>  arch/arm/boot/dts/tegra20-tamonten.dtsi    |   2 +-
>  arch/arm/boot/dts/tegra20-trimslice.dts    |   2 +-
>  arch/arm/boot/dts/tegra20-ventana.dts      |   2 +-
>  arch/arm/boot/dts/tegra20-whistler.dts     |   2 +-
>  arch/arm/boot/dts/tegra30-beaver.dts       |   2 +-
>  arch/arm/boot/dts/tegra30-cardhu.dtsi      |   2 +-
>  arch/arm/mach-tegra/Makefile               |   3 +
>  arch/arm/mach-tegra/iomap.h                |   6 +
>  arch/arm/mach-tegra/pm-tegra20.c           |  37 ++
>  arch/arm/mach-tegra/pm-tegra30.c           |  37 ++
>  arch/arm/mach-tegra/pm.c                   | 121 +++++-
>  arch/arm/mach-tegra/pm.h                   |  23 ++
>  arch/arm/mach-tegra/pmc.c                  |  36 +-
>  arch/arm/mach-tegra/pmc.h                  |   3 +
>  arch/arm/mach-tegra/reset-handler.S        |  13 +
>  arch/arm/mach-tegra/reset.c                |   2 +
>  arch/arm/mach-tegra/reset.h                |   4 +
>  arch/arm/mach-tegra/sleep-tegra20.S        | 300 ++++++++++++++
>  arch/arm/mach-tegra/sleep-tegra30.S        | 615
> +++++++++++++++++++++++++++++ arch/arm/mach-tegra/sleep.S                | 
>  8 +-
>  arch/arm/mach-tegra/sleep.h                |  18 +
>  drivers/clk/tegra/clk-tegra114.c           |  32 ++
>  27 files changed, 1259 insertions(+), 21 deletions(-)
>  create mode 100644 arch/arm/mach-tegra/pm-tegra20.c
>  create mode 100644 arch/arm/mach-tegra/pm-tegra30.c

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 0/8] ARM: tegra: support LP1 suspend mode
  2013-07-27 16:12     ` Marc Dietrich
@ 2013-07-27 16:20       ` Dmitry Osipenko
  -1 siblings, 0 replies; 88+ messages in thread
From: Dmitry Osipenko @ 2013-07-27 16:20 UTC (permalink / raw)
  To: Marc Dietrich
  Cc: Joseph Lo, Stephen Warren, linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

27.07.2013 20:12, Marc Dietrich пишет:
> On Friday 26 July 2013 17:15:02 Joseph Lo wrote:
>> This series adds the support of LP1 suspend mode for Tegra.
>>
>> Verified on Seaboard, Cardhu and Dalmore.
> 
> tested on AC100 and worked fine, except
> 
> [   68.448007] PM: Syncing filesystems ... done.
> [   71.370086] Freezing user space processes ... (elapsed 0.001 seconds) done.
> [   71.370088] Freezing remaining freezable tasks ... (elapsed 0.001 seconds) 
> done.
> [   71.371253] Suspending console(s) (use no_console_suspend to debug)
> [   71.638481] PM: suspend of devices complete after 266.604 msecs
> [   71.638905] PM: late suspend of devices complete after 0.417 msecs
> [   71.639477] PM: noirq suspend of devices complete after 0.568 msecs
> [   71.639480] Disabling non-boot CPUs ...
> [   71.640242] CPU1: shutdown
> [   71.641216] Entering suspend state LP1
> [   71.641267] Enabling non-boot CPUs ...
> [   71.643125] CPU1: Booted secondary processor
> [   71.643449] CPU1 is up
> [   71.643786] PM: noirq resume of devices complete after 0.331 msecs
> [   71.644300] PM: early resume of devices complete after 0.364 msecs
> [   71.648038] ------------[ cut here ]------------
> [   71.648061] WARNING: CPU: 0 PID: 1025 at kernel/irq/manage.c:529 
> irq_set_irq_wake+0xb8/0xf0()
> [   71.648064] Unbalanced IRQ 384 wake disable
> [   71.648068] Modules linked in:
> [   71.648075] CPU: 0 PID: 1025 Comm: bash Not tainted 3.11.0-rc2-
> next-20130726-00008-gef01509-dirty #6
> [   71.648107] [<c001551c>] (unwind_backtrace+0x0/0xf8) from [<c001154c>] 
> (show_stack+0x10/0x14)
> [   71.648128] [<c001154c>] (show_stack+0x10/0x14) from [<c0515288>] 
> (dump_stack+0x80/0xc4)
> [   71.648142] [<c0515288>] (dump_stack+0x80/0xc4) from [<c0025340>] 
> (warn_slowpath_common+0x64/0x88)
> [   71.648151] [<c0025340>] (warn_slowpath_common+0x64/0x88) from [<c00253f8>] 
> (warn_slowpath_fmt+0x30/0x40)
> [   71.648161] [<c00253f8>] (warn_slowpath_fmt+0x30/0x40) from [<c007ff8c>] 
> (irq_set_irq_wake+0xb8/0xf0)
> [   71.648178] [<c007ff8c>] (irq_set_irq_wake+0xb8/0xf0) from [<c0334d00>] 
> (tps6586x_rtc_resume+0x30/0x38)
> [   71.648192] [<c0334d00>] (tps6586x_rtc_resume+0x30/0x38) from [<c02951d0>] 
> (platform_pm_resume+0x2c/0x4c)
> [   71.648202] [<c02951d0>] (platform_pm_resume+0x2c/0x4c) from [<c0299d40>] 
> (dpm_run_callback.isra.5+0x18/0x38)
> [   71.648211] [<c0299d40>] (dpm_run_callback.isra.5+0x18/0x38) from 
> [<c029a044>] (device_resume+0xb0/0x170)
> [   71.648219] [<c029a044>] (device_resume+0xb0/0x170) from [<c029aeb4>] 
> (dpm_resume+0xe8/0x20c)
> [   71.648227] [<c029aeb4>] (dpm_resume+0xe8/0x20c) from [<c029b174>] 
> (dpm_resume_end+0xc/0x18)
> [   71.648245] [<c029b174>] (dpm_resume_end+0xc/0x18) from [<c005e480>] 
> (suspend_devices_and_enter+0xdc/0x2f8)
> [   71.648256] [<c005e480>] (suspend_devices_and_enter+0xdc/0x2f8) from 
> [<c005e810>] (pm_suspend+0x174/0x1e8)
> [   71.648265] [<c005e810>] (pm_suspend+0x174/0x1e8) from [<c005d710>] 
> (state_store+0x6c/0xbc)
> [   71.648285] [<c005d710>] (state_store+0x6c/0xbc) from [<c01eca78>] 
> (kobj_attr_store+0x14/0x20)
> [   71.648298] [<c01eca78>] (kobj_attr_store+0x14/0x20) from [<c011e970>] 
> (sysfs_write_file+0x168/0x198)
> [   71.648312] [<c011e970>] (sysfs_write_file+0x168/0x198) from [<c00cd060>] 
> (vfs_write+0xb0/0x188)
> [   71.648321] [<c00cd060>] (vfs_write+0xb0/0x188) from [<c00cd410>] 
> (SyS_write+0x3c/0x70)
> [   71.648330] [<c00cd410>] (SyS_write+0x3c/0x70) from [<c000e560>] 
> (ret_fast_syscall+0x0/0x30)
> [   71.648334] ---[ end trace e5ed8212ac4db5da ]---
> [   72.063578] mmc1: power class selection to bus width 8 failed
> [   72.297753] usb 2-1: reset high-speed USB device number 2 using tegra-ehci
> [   72.567736] usb 3-1: reset high-speed USB device number 2 using tegra-ehci
> [   73.022058] usb 3-1.3: reset high-speed USB device number 4 using tegra-
> ehci
> [   73.216064] usb 3-1.2: reset high-speed USB device number 3 using tegra-
> ehci
> [   73.360225] PM: resume of devices complete after 1715.937 msecs
> [   73.361008] Restarting tasks ... done.
> 
> and sdcard being offline after resume (fixed by CONFIG_MMC_UNSAFE_RESUME=y). 
> Anyway, thanks for your great work!
> 
> Tested-By: Marc Dietrich <marvin24-Mmb7MZpHnFY@public.gmane.org>
> 
>> And tested with THUMB2_KERNEL as well.
>>
>> P.S.
>> A known issue on Dalmore, the LP1 resume may take 10 ~ 15 seconds due to
>> the 792MHz BCT. It can't be reproduced on Seaboard or Cardhu. And the
>> root cause of this is about the default settings of EMC registers that
>> cause the DRAM can't leave self-refresh mode immediately. If you want
>> to test with quick LP1 resume on Dalmore, I can provide another HACK for
>> this.
>>
>> Joseph Lo (8):
>>   ARM: tegra: add common resume handling code for LP1 resuming
>>   ARM: tegra: config the polarity of the request of sys clock
>>   clk: tegra114: add LP1 suspend/resume support
>>   ARM: tegra: add common LP1 suspend support
>>   ARM: tegra30: add LP1 suspend support
>>   ARM: tegra20: add LP1 suspend support
>>   ARM: tegra114: add LP1 suspend support
>>   ARM: dts: tegra: enable LP1 suspend mode
>>
>>  arch/arm/boot/dts/tegra114-dalmore.dts     |   2 +-
>>  arch/arm/boot/dts/tegra20-colibri-512.dtsi |   2 +-
>>  arch/arm/boot/dts/tegra20-harmony.dts      |   2 +-
>>  arch/arm/boot/dts/tegra20-paz00.dts        |   2 +-
>>  arch/arm/boot/dts/tegra20-seaboard.dts     |   2 +-
>>  arch/arm/boot/dts/tegra20-tamonten.dtsi    |   2 +-
>>  arch/arm/boot/dts/tegra20-trimslice.dts    |   2 +-
>>  arch/arm/boot/dts/tegra20-ventana.dts      |   2 +-
>>  arch/arm/boot/dts/tegra20-whistler.dts     |   2 +-
>>  arch/arm/boot/dts/tegra30-beaver.dts       |   2 +-
>>  arch/arm/boot/dts/tegra30-cardhu.dtsi      |   2 +-
>>  arch/arm/mach-tegra/Makefile               |   3 +
>>  arch/arm/mach-tegra/iomap.h                |   6 +
>>  arch/arm/mach-tegra/pm-tegra20.c           |  37 ++
>>  arch/arm/mach-tegra/pm-tegra30.c           |  37 ++
>>  arch/arm/mach-tegra/pm.c                   | 121 +++++-
>>  arch/arm/mach-tegra/pm.h                   |  23 ++
>>  arch/arm/mach-tegra/pmc.c                  |  36 +-
>>  arch/arm/mach-tegra/pmc.h                  |   3 +
>>  arch/arm/mach-tegra/reset-handler.S        |  13 +
>>  arch/arm/mach-tegra/reset.c                |   2 +
>>  arch/arm/mach-tegra/reset.h                |   4 +
>>  arch/arm/mach-tegra/sleep-tegra20.S        | 300 ++++++++++++++
>>  arch/arm/mach-tegra/sleep-tegra30.S        | 615
>> +++++++++++++++++++++++++++++ arch/arm/mach-tegra/sleep.S                | 
>>  8 +-
>>  arch/arm/mach-tegra/sleep.h                |  18 +
>>  drivers/clk/tegra/clk-tegra114.c           |  32 ++
>>  27 files changed, 1259 insertions(+), 21 deletions(-)
>>  create mode 100644 arch/arm/mach-tegra/pm-tegra20.c
>>  create mode 100644 arch/arm/mach-tegra/pm-tegra30.c
> --
> To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
I had same warn on 3.8.
https://bitbucket.org/digetx/picasso-kernel/commits/a86dc95ab48dd77eaa0463844cdf88a5acf9d842

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 0/8] ARM: tegra: support LP1 suspend mode
@ 2013-07-27 16:20       ` Dmitry Osipenko
  0 siblings, 0 replies; 88+ messages in thread
From: Dmitry Osipenko @ 2013-07-27 16:20 UTC (permalink / raw)
  To: linux-arm-kernel

27.07.2013 20:12, Marc Dietrich ?????:
> On Friday 26 July 2013 17:15:02 Joseph Lo wrote:
>> This series adds the support of LP1 suspend mode for Tegra.
>>
>> Verified on Seaboard, Cardhu and Dalmore.
> 
> tested on AC100 and worked fine, except
> 
> [   68.448007] PM: Syncing filesystems ... done.
> [   71.370086] Freezing user space processes ... (elapsed 0.001 seconds) done.
> [   71.370088] Freezing remaining freezable tasks ... (elapsed 0.001 seconds) 
> done.
> [   71.371253] Suspending console(s) (use no_console_suspend to debug)
> [   71.638481] PM: suspend of devices complete after 266.604 msecs
> [   71.638905] PM: late suspend of devices complete after 0.417 msecs
> [   71.639477] PM: noirq suspend of devices complete after 0.568 msecs
> [   71.639480] Disabling non-boot CPUs ...
> [   71.640242] CPU1: shutdown
> [   71.641216] Entering suspend state LP1
> [   71.641267] Enabling non-boot CPUs ...
> [   71.643125] CPU1: Booted secondary processor
> [   71.643449] CPU1 is up
> [   71.643786] PM: noirq resume of devices complete after 0.331 msecs
> [   71.644300] PM: early resume of devices complete after 0.364 msecs
> [   71.648038] ------------[ cut here ]------------
> [   71.648061] WARNING: CPU: 0 PID: 1025 at kernel/irq/manage.c:529 
> irq_set_irq_wake+0xb8/0xf0()
> [   71.648064] Unbalanced IRQ 384 wake disable
> [   71.648068] Modules linked in:
> [   71.648075] CPU: 0 PID: 1025 Comm: bash Not tainted 3.11.0-rc2-
> next-20130726-00008-gef01509-dirty #6
> [   71.648107] [<c001551c>] (unwind_backtrace+0x0/0xf8) from [<c001154c>] 
> (show_stack+0x10/0x14)
> [   71.648128] [<c001154c>] (show_stack+0x10/0x14) from [<c0515288>] 
> (dump_stack+0x80/0xc4)
> [   71.648142] [<c0515288>] (dump_stack+0x80/0xc4) from [<c0025340>] 
> (warn_slowpath_common+0x64/0x88)
> [   71.648151] [<c0025340>] (warn_slowpath_common+0x64/0x88) from [<c00253f8>] 
> (warn_slowpath_fmt+0x30/0x40)
> [   71.648161] [<c00253f8>] (warn_slowpath_fmt+0x30/0x40) from [<c007ff8c>] 
> (irq_set_irq_wake+0xb8/0xf0)
> [   71.648178] [<c007ff8c>] (irq_set_irq_wake+0xb8/0xf0) from [<c0334d00>] 
> (tps6586x_rtc_resume+0x30/0x38)
> [   71.648192] [<c0334d00>] (tps6586x_rtc_resume+0x30/0x38) from [<c02951d0>] 
> (platform_pm_resume+0x2c/0x4c)
> [   71.648202] [<c02951d0>] (platform_pm_resume+0x2c/0x4c) from [<c0299d40>] 
> (dpm_run_callback.isra.5+0x18/0x38)
> [   71.648211] [<c0299d40>] (dpm_run_callback.isra.5+0x18/0x38) from 
> [<c029a044>] (device_resume+0xb0/0x170)
> [   71.648219] [<c029a044>] (device_resume+0xb0/0x170) from [<c029aeb4>] 
> (dpm_resume+0xe8/0x20c)
> [   71.648227] [<c029aeb4>] (dpm_resume+0xe8/0x20c) from [<c029b174>] 
> (dpm_resume_end+0xc/0x18)
> [   71.648245] [<c029b174>] (dpm_resume_end+0xc/0x18) from [<c005e480>] 
> (suspend_devices_and_enter+0xdc/0x2f8)
> [   71.648256] [<c005e480>] (suspend_devices_and_enter+0xdc/0x2f8) from 
> [<c005e810>] (pm_suspend+0x174/0x1e8)
> [   71.648265] [<c005e810>] (pm_suspend+0x174/0x1e8) from [<c005d710>] 
> (state_store+0x6c/0xbc)
> [   71.648285] [<c005d710>] (state_store+0x6c/0xbc) from [<c01eca78>] 
> (kobj_attr_store+0x14/0x20)
> [   71.648298] [<c01eca78>] (kobj_attr_store+0x14/0x20) from [<c011e970>] 
> (sysfs_write_file+0x168/0x198)
> [   71.648312] [<c011e970>] (sysfs_write_file+0x168/0x198) from [<c00cd060>] 
> (vfs_write+0xb0/0x188)
> [   71.648321] [<c00cd060>] (vfs_write+0xb0/0x188) from [<c00cd410>] 
> (SyS_write+0x3c/0x70)
> [   71.648330] [<c00cd410>] (SyS_write+0x3c/0x70) from [<c000e560>] 
> (ret_fast_syscall+0x0/0x30)
> [   71.648334] ---[ end trace e5ed8212ac4db5da ]---
> [   72.063578] mmc1: power class selection to bus width 8 failed
> [   72.297753] usb 2-1: reset high-speed USB device number 2 using tegra-ehci
> [   72.567736] usb 3-1: reset high-speed USB device number 2 using tegra-ehci
> [   73.022058] usb 3-1.3: reset high-speed USB device number 4 using tegra-
> ehci
> [   73.216064] usb 3-1.2: reset high-speed USB device number 3 using tegra-
> ehci
> [   73.360225] PM: resume of devices complete after 1715.937 msecs
> [   73.361008] Restarting tasks ... done.
> 
> and sdcard being offline after resume (fixed by CONFIG_MMC_UNSAFE_RESUME=y). 
> Anyway, thanks for your great work!
> 
> Tested-By: Marc Dietrich <marvin24@gmx.de>
> 
>> And tested with THUMB2_KERNEL as well.
>>
>> P.S.
>> A known issue on Dalmore, the LP1 resume may take 10 ~ 15 seconds due to
>> the 792MHz BCT. It can't be reproduced on Seaboard or Cardhu. And the
>> root cause of this is about the default settings of EMC registers that
>> cause the DRAM can't leave self-refresh mode immediately. If you want
>> to test with quick LP1 resume on Dalmore, I can provide another HACK for
>> this.
>>
>> Joseph Lo (8):
>>   ARM: tegra: add common resume handling code for LP1 resuming
>>   ARM: tegra: config the polarity of the request of sys clock
>>   clk: tegra114: add LP1 suspend/resume support
>>   ARM: tegra: add common LP1 suspend support
>>   ARM: tegra30: add LP1 suspend support
>>   ARM: tegra20: add LP1 suspend support
>>   ARM: tegra114: add LP1 suspend support
>>   ARM: dts: tegra: enable LP1 suspend mode
>>
>>  arch/arm/boot/dts/tegra114-dalmore.dts     |   2 +-
>>  arch/arm/boot/dts/tegra20-colibri-512.dtsi |   2 +-
>>  arch/arm/boot/dts/tegra20-harmony.dts      |   2 +-
>>  arch/arm/boot/dts/tegra20-paz00.dts        |   2 +-
>>  arch/arm/boot/dts/tegra20-seaboard.dts     |   2 +-
>>  arch/arm/boot/dts/tegra20-tamonten.dtsi    |   2 +-
>>  arch/arm/boot/dts/tegra20-trimslice.dts    |   2 +-
>>  arch/arm/boot/dts/tegra20-ventana.dts      |   2 +-
>>  arch/arm/boot/dts/tegra20-whistler.dts     |   2 +-
>>  arch/arm/boot/dts/tegra30-beaver.dts       |   2 +-
>>  arch/arm/boot/dts/tegra30-cardhu.dtsi      |   2 +-
>>  arch/arm/mach-tegra/Makefile               |   3 +
>>  arch/arm/mach-tegra/iomap.h                |   6 +
>>  arch/arm/mach-tegra/pm-tegra20.c           |  37 ++
>>  arch/arm/mach-tegra/pm-tegra30.c           |  37 ++
>>  arch/arm/mach-tegra/pm.c                   | 121 +++++-
>>  arch/arm/mach-tegra/pm.h                   |  23 ++
>>  arch/arm/mach-tegra/pmc.c                  |  36 +-
>>  arch/arm/mach-tegra/pmc.h                  |   3 +
>>  arch/arm/mach-tegra/reset-handler.S        |  13 +
>>  arch/arm/mach-tegra/reset.c                |   2 +
>>  arch/arm/mach-tegra/reset.h                |   4 +
>>  arch/arm/mach-tegra/sleep-tegra20.S        | 300 ++++++++++++++
>>  arch/arm/mach-tegra/sleep-tegra30.S        | 615
>> +++++++++++++++++++++++++++++ arch/arm/mach-tegra/sleep.S                | 
>>  8 +-
>>  arch/arm/mach-tegra/sleep.h                |  18 +
>>  drivers/clk/tegra/clk-tegra114.c           |  32 ++
>>  27 files changed, 1259 insertions(+), 21 deletions(-)
>>  create mode 100644 arch/arm/mach-tegra/pm-tegra20.c
>>  create mode 100644 arch/arm/mach-tegra/pm-tegra30.c
> --
> To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
I had same warn on 3.8.
https://bitbucket.org/digetx/picasso-kernel/commits/a86dc95ab48dd77eaa0463844cdf88a5acf9d842

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 0/8] ARM: tegra: support LP1 suspend mode
  2013-07-27 16:20       ` Dmitry Osipenko
@ 2013-07-27 18:09           ` Marc Dietrich
  -1 siblings, 0 replies; 88+ messages in thread
From: Marc Dietrich @ 2013-07-27 18:09 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Joseph Lo, Stephen Warren, linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Saturday 27 July 2013 20:20:38 Dmitry Osipenko wrote:
> 27.07.2013 20:12, Marc Dietrich пишет:
> > On Friday 26 July 2013 17:15:02 Joseph Lo wrote:
> >> This series adds the support of LP1 suspend mode for Tegra.
> >> 
> >> Verified on Seaboard, Cardhu and Dalmore.
> > 
> > tested on AC100 and worked fine, except
> > 
> > [   68.448007] PM: Syncing filesystems ... done.
> > [   71.370086] Freezing user space processes ... (elapsed 0.001 seconds)
> > done. [   71.370088] Freezing remaining freezable tasks ... (elapsed
> > 0.001 seconds) done.
> > [   71.371253] Suspending console(s) (use no_console_suspend to debug)
> > [   71.638481] PM: suspend of devices complete after 266.604 msecs
> > [   71.638905] PM: late suspend of devices complete after 0.417 msecs
> > [   71.639477] PM: noirq suspend of devices complete after 0.568 msecs
> > [   71.639480] Disabling non-boot CPUs ...
> > [   71.640242] CPU1: shutdown
> > [   71.641216] Entering suspend state LP1
> > [   71.641267] Enabling non-boot CPUs ...
> > [   71.643125] CPU1: Booted secondary processor
> > [   71.643449] CPU1 is up
> > [   71.643786] PM: noirq resume of devices complete after 0.331 msecs
> > [   71.644300] PM: early resume of devices complete after 0.364 msecs
> > [   71.648038] ------------[ cut here ]------------
> > [   71.648061] WARNING: CPU: 0 PID: 1025 at kernel/irq/manage.c:529
> > irq_set_irq_wake+0xb8/0xf0()
> > [   71.648064] Unbalanced IRQ 384 wake disable
> > [   71.648068] Modules linked in:
> > [   71.648075] CPU: 0 PID: 1025 Comm: bash Not tainted 3.11.0-rc2-
> > next-20130726-00008-gef01509-dirty #6
> > [   71.648107] [<c001551c>] (unwind_backtrace+0x0/0xf8) from [<c001154c>]
> > (show_stack+0x10/0x14)
> > [   71.648128] [<c001154c>] (show_stack+0x10/0x14) from [<c0515288>]
> > (dump_stack+0x80/0xc4)
> > [   71.648142] [<c0515288>] (dump_stack+0x80/0xc4) from [<c0025340>]
> > (warn_slowpath_common+0x64/0x88)
> > [   71.648151] [<c0025340>] (warn_slowpath_common+0x64/0x88) from
> > [<c00253f8>] (warn_slowpath_fmt+0x30/0x40)
> > [   71.648161] [<c00253f8>] (warn_slowpath_fmt+0x30/0x40) from
> > [<c007ff8c>]
> > (irq_set_irq_wake+0xb8/0xf0)
> > [   71.648178] [<c007ff8c>] (irq_set_irq_wake+0xb8/0xf0) from [<c0334d00>]
> > (tps6586x_rtc_resume+0x30/0x38)
> > [   71.648192] [<c0334d00>] (tps6586x_rtc_resume+0x30/0x38) from
> > [<c02951d0>] (platform_pm_resume+0x2c/0x4c)
> > [   71.648202] [<c02951d0>] (platform_pm_resume+0x2c/0x4c) from
> > [<c0299d40>] (dpm_run_callback.isra.5+0x18/0x38)
> > [   71.648211] [<c0299d40>] (dpm_run_callback.isra.5+0x18/0x38) from
> > [<c029a044>] (device_resume+0xb0/0x170)
> > [   71.648219] [<c029a044>] (device_resume+0xb0/0x170) from [<c029aeb4>]
> > (dpm_resume+0xe8/0x20c)
> > [   71.648227] [<c029aeb4>] (dpm_resume+0xe8/0x20c) from [<c029b174>]
> > (dpm_resume_end+0xc/0x18)
> > [   71.648245] [<c029b174>] (dpm_resume_end+0xc/0x18) from [<c005e480>]
> > (suspend_devices_and_enter+0xdc/0x2f8)
> > [   71.648256] [<c005e480>] (suspend_devices_and_enter+0xdc/0x2f8) from
> > [<c005e810>] (pm_suspend+0x174/0x1e8)
> > [   71.648265] [<c005e810>] (pm_suspend+0x174/0x1e8) from [<c005d710>]
> > (state_store+0x6c/0xbc)
> > [   71.648285] [<c005d710>] (state_store+0x6c/0xbc) from [<c01eca78>]
> > (kobj_attr_store+0x14/0x20)
> > [   71.648298] [<c01eca78>] (kobj_attr_store+0x14/0x20) from [<c011e970>]
> > (sysfs_write_file+0x168/0x198)
> > [   71.648312] [<c011e970>] (sysfs_write_file+0x168/0x198) from
> > [<c00cd060>] (vfs_write+0xb0/0x188)
> > [   71.648321] [<c00cd060>] (vfs_write+0xb0/0x188) from [<c00cd410>]
> > (SyS_write+0x3c/0x70)
> > [   71.648330] [<c00cd410>] (SyS_write+0x3c/0x70) from [<c000e560>]
> > (ret_fast_syscall+0x0/0x30)
> > [   71.648334] ---[ end trace e5ed8212ac4db5da ]---
> > [   72.063578] mmc1: power class selection to bus width 8 failed
> > [   72.297753] usb 2-1: reset high-speed USB device number 2 using
> > tegra-ehci [   72.567736] usb 3-1: reset high-speed USB device number 2
> > using tegra-ehci [   73.022058] usb 3-1.3: reset high-speed USB device
> > number 4 using tegra- ehci
> > [   73.216064] usb 3-1.2: reset high-speed USB device number 3 using
> > tegra-
> > ehci
> > [   73.360225] PM: resume of devices complete after 1715.937 msecs
> > [   73.361008] Restarting tasks ... done.
> > 
> > and sdcard being offline after resume (fixed by
> > CONFIG_MMC_UNSAFE_RESUME=y). Anyway, thanks for your great work!
> > 
> > Tested-By: Marc Dietrich <marvin24-Mmb7MZpHnFY@public.gmane.org>
> 
> I had same warn on 3.8.
> https://bitbucket.org/digetx/picasso-kernel/commits/a86dc95ab48dd77eaa046384
> 4cdf88a5acf9d842

Stephen Warren said on IRC that this is known and on the internal bug tracker 
already. I just posted it so others are not surprised. Well, I should have 
added that it's a know problem. Sorry for that.

Marc

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 0/8] ARM: tegra: support LP1 suspend mode
@ 2013-07-27 18:09           ` Marc Dietrich
  0 siblings, 0 replies; 88+ messages in thread
From: Marc Dietrich @ 2013-07-27 18:09 UTC (permalink / raw)
  To: linux-arm-kernel

On Saturday 27 July 2013 20:20:38 Dmitry Osipenko wrote:
> 27.07.2013 20:12, Marc Dietrich ?????:
> > On Friday 26 July 2013 17:15:02 Joseph Lo wrote:
> >> This series adds the support of LP1 suspend mode for Tegra.
> >> 
> >> Verified on Seaboard, Cardhu and Dalmore.
> > 
> > tested on AC100 and worked fine, except
> > 
> > [   68.448007] PM: Syncing filesystems ... done.
> > [   71.370086] Freezing user space processes ... (elapsed 0.001 seconds)
> > done. [   71.370088] Freezing remaining freezable tasks ... (elapsed
> > 0.001 seconds) done.
> > [   71.371253] Suspending console(s) (use no_console_suspend to debug)
> > [   71.638481] PM: suspend of devices complete after 266.604 msecs
> > [   71.638905] PM: late suspend of devices complete after 0.417 msecs
> > [   71.639477] PM: noirq suspend of devices complete after 0.568 msecs
> > [   71.639480] Disabling non-boot CPUs ...
> > [   71.640242] CPU1: shutdown
> > [   71.641216] Entering suspend state LP1
> > [   71.641267] Enabling non-boot CPUs ...
> > [   71.643125] CPU1: Booted secondary processor
> > [   71.643449] CPU1 is up
> > [   71.643786] PM: noirq resume of devices complete after 0.331 msecs
> > [   71.644300] PM: early resume of devices complete after 0.364 msecs
> > [   71.648038] ------------[ cut here ]------------
> > [   71.648061] WARNING: CPU: 0 PID: 1025 at kernel/irq/manage.c:529
> > irq_set_irq_wake+0xb8/0xf0()
> > [   71.648064] Unbalanced IRQ 384 wake disable
> > [   71.648068] Modules linked in:
> > [   71.648075] CPU: 0 PID: 1025 Comm: bash Not tainted 3.11.0-rc2-
> > next-20130726-00008-gef01509-dirty #6
> > [   71.648107] [<c001551c>] (unwind_backtrace+0x0/0xf8) from [<c001154c>]
> > (show_stack+0x10/0x14)
> > [   71.648128] [<c001154c>] (show_stack+0x10/0x14) from [<c0515288>]
> > (dump_stack+0x80/0xc4)
> > [   71.648142] [<c0515288>] (dump_stack+0x80/0xc4) from [<c0025340>]
> > (warn_slowpath_common+0x64/0x88)
> > [   71.648151] [<c0025340>] (warn_slowpath_common+0x64/0x88) from
> > [<c00253f8>] (warn_slowpath_fmt+0x30/0x40)
> > [   71.648161] [<c00253f8>] (warn_slowpath_fmt+0x30/0x40) from
> > [<c007ff8c>]
> > (irq_set_irq_wake+0xb8/0xf0)
> > [   71.648178] [<c007ff8c>] (irq_set_irq_wake+0xb8/0xf0) from [<c0334d00>]
> > (tps6586x_rtc_resume+0x30/0x38)
> > [   71.648192] [<c0334d00>] (tps6586x_rtc_resume+0x30/0x38) from
> > [<c02951d0>] (platform_pm_resume+0x2c/0x4c)
> > [   71.648202] [<c02951d0>] (platform_pm_resume+0x2c/0x4c) from
> > [<c0299d40>] (dpm_run_callback.isra.5+0x18/0x38)
> > [   71.648211] [<c0299d40>] (dpm_run_callback.isra.5+0x18/0x38) from
> > [<c029a044>] (device_resume+0xb0/0x170)
> > [   71.648219] [<c029a044>] (device_resume+0xb0/0x170) from [<c029aeb4>]
> > (dpm_resume+0xe8/0x20c)
> > [   71.648227] [<c029aeb4>] (dpm_resume+0xe8/0x20c) from [<c029b174>]
> > (dpm_resume_end+0xc/0x18)
> > [   71.648245] [<c029b174>] (dpm_resume_end+0xc/0x18) from [<c005e480>]
> > (suspend_devices_and_enter+0xdc/0x2f8)
> > [   71.648256] [<c005e480>] (suspend_devices_and_enter+0xdc/0x2f8) from
> > [<c005e810>] (pm_suspend+0x174/0x1e8)
> > [   71.648265] [<c005e810>] (pm_suspend+0x174/0x1e8) from [<c005d710>]
> > (state_store+0x6c/0xbc)
> > [   71.648285] [<c005d710>] (state_store+0x6c/0xbc) from [<c01eca78>]
> > (kobj_attr_store+0x14/0x20)
> > [   71.648298] [<c01eca78>] (kobj_attr_store+0x14/0x20) from [<c011e970>]
> > (sysfs_write_file+0x168/0x198)
> > [   71.648312] [<c011e970>] (sysfs_write_file+0x168/0x198) from
> > [<c00cd060>] (vfs_write+0xb0/0x188)
> > [   71.648321] [<c00cd060>] (vfs_write+0xb0/0x188) from [<c00cd410>]
> > (SyS_write+0x3c/0x70)
> > [   71.648330] [<c00cd410>] (SyS_write+0x3c/0x70) from [<c000e560>]
> > (ret_fast_syscall+0x0/0x30)
> > [   71.648334] ---[ end trace e5ed8212ac4db5da ]---
> > [   72.063578] mmc1: power class selection to bus width 8 failed
> > [   72.297753] usb 2-1: reset high-speed USB device number 2 using
> > tegra-ehci [   72.567736] usb 3-1: reset high-speed USB device number 2
> > using tegra-ehci [   73.022058] usb 3-1.3: reset high-speed USB device
> > number 4 using tegra- ehci
> > [   73.216064] usb 3-1.2: reset high-speed USB device number 3 using
> > tegra-
> > ehci
> > [   73.360225] PM: resume of devices complete after 1715.937 msecs
> > [   73.361008] Restarting tasks ... done.
> > 
> > and sdcard being offline after resume (fixed by
> > CONFIG_MMC_UNSAFE_RESUME=y). Anyway, thanks for your great work!
> > 
> > Tested-By: Marc Dietrich <marvin24@gmx.de>
> 
> I had same warn on 3.8.
> https://bitbucket.org/digetx/picasso-kernel/commits/a86dc95ab48dd77eaa046384
> 4cdf88a5acf9d842

Stephen Warren said on IRC that this is known and on the internal bug tracker 
already. I just posted it so others are not surprised. Well, I should have 
added that it's a know problem. Sorry for that.

Marc

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 0/8] ARM: tegra: support LP1 suspend mode
  2013-07-27 18:09           ` Marc Dietrich
@ 2013-07-27 18:26             ` Dmitry Osipenko
  -1 siblings, 0 replies; 88+ messages in thread
From: Dmitry Osipenko @ 2013-07-27 18:26 UTC (permalink / raw)
  To: Marc Dietrich
  Cc: Joseph Lo, Stephen Warren, linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

27.07.2013 22:09, Marc Dietrich пишет:
> On Saturday 27 July 2013 20:20:38 Dmitry Osipenko wrote:
>> 27.07.2013 20:12, Marc Dietrich пишет:
>>> On Friday 26 July 2013 17:15:02 Joseph Lo wrote:
>>>> This series adds the support of LP1 suspend mode for Tegra.
>>>>
>>>> Verified on Seaboard, Cardhu and Dalmore.
>>>
>>> tested on AC100 and worked fine, except
>>>
>>> [   68.448007] PM: Syncing filesystems ... done.
>>> [   71.370086] Freezing user space processes ... (elapsed 0.001 seconds)
>>> done. [   71.370088] Freezing remaining freezable tasks ... (elapsed
>>> 0.001 seconds) done.
>>> [   71.371253] Suspending console(s) (use no_console_suspend to debug)
>>> [   71.638481] PM: suspend of devices complete after 266.604 msecs
>>> [   71.638905] PM: late suspend of devices complete after 0.417 msecs
>>> [   71.639477] PM: noirq suspend of devices complete after 0.568 msecs
>>> [   71.639480] Disabling non-boot CPUs ...
>>> [   71.640242] CPU1: shutdown
>>> [   71.641216] Entering suspend state LP1
>>> [   71.641267] Enabling non-boot CPUs ...
>>> [   71.643125] CPU1: Booted secondary processor
>>> [   71.643449] CPU1 is up
>>> [   71.643786] PM: noirq resume of devices complete after 0.331 msecs
>>> [   71.644300] PM: early resume of devices complete after 0.364 msecs
>>> [   71.648038] ------------[ cut here ]------------
>>> [   71.648061] WARNING: CPU: 0 PID: 1025 at kernel/irq/manage.c:529
>>> irq_set_irq_wake+0xb8/0xf0()
>>> [   71.648064] Unbalanced IRQ 384 wake disable
>>> [   71.648068] Modules linked in:
>>> [   71.648075] CPU: 0 PID: 1025 Comm: bash Not tainted 3.11.0-rc2-
>>> next-20130726-00008-gef01509-dirty #6
>>> [   71.648107] [<c001551c>] (unwind_backtrace+0x0/0xf8) from [<c001154c>]
>>> (show_stack+0x10/0x14)
>>> [   71.648128] [<c001154c>] (show_stack+0x10/0x14) from [<c0515288>]
>>> (dump_stack+0x80/0xc4)
>>> [   71.648142] [<c0515288>] (dump_stack+0x80/0xc4) from [<c0025340>]
>>> (warn_slowpath_common+0x64/0x88)
>>> [   71.648151] [<c0025340>] (warn_slowpath_common+0x64/0x88) from
>>> [<c00253f8>] (warn_slowpath_fmt+0x30/0x40)
>>> [   71.648161] [<c00253f8>] (warn_slowpath_fmt+0x30/0x40) from
>>> [<c007ff8c>]
>>> (irq_set_irq_wake+0xb8/0xf0)
>>> [   71.648178] [<c007ff8c>] (irq_set_irq_wake+0xb8/0xf0) from [<c0334d00>]
>>> (tps6586x_rtc_resume+0x30/0x38)
>>> [   71.648192] [<c0334d00>] (tps6586x_rtc_resume+0x30/0x38) from
>>> [<c02951d0>] (platform_pm_resume+0x2c/0x4c)
>>> [   71.648202] [<c02951d0>] (platform_pm_resume+0x2c/0x4c) from
>>> [<c0299d40>] (dpm_run_callback.isra.5+0x18/0x38)
>>> [   71.648211] [<c0299d40>] (dpm_run_callback.isra.5+0x18/0x38) from
>>> [<c029a044>] (device_resume+0xb0/0x170)
>>> [   71.648219] [<c029a044>] (device_resume+0xb0/0x170) from [<c029aeb4>]
>>> (dpm_resume+0xe8/0x20c)
>>> [   71.648227] [<c029aeb4>] (dpm_resume+0xe8/0x20c) from [<c029b174>]
>>> (dpm_resume_end+0xc/0x18)
>>> [   71.648245] [<c029b174>] (dpm_resume_end+0xc/0x18) from [<c005e480>]
>>> (suspend_devices_and_enter+0xdc/0x2f8)
>>> [   71.648256] [<c005e480>] (suspend_devices_and_enter+0xdc/0x2f8) from
>>> [<c005e810>] (pm_suspend+0x174/0x1e8)
>>> [   71.648265] [<c005e810>] (pm_suspend+0x174/0x1e8) from [<c005d710>]
>>> (state_store+0x6c/0xbc)
>>> [   71.648285] [<c005d710>] (state_store+0x6c/0xbc) from [<c01eca78>]
>>> (kobj_attr_store+0x14/0x20)
>>> [   71.648298] [<c01eca78>] (kobj_attr_store+0x14/0x20) from [<c011e970>]
>>> (sysfs_write_file+0x168/0x198)
>>> [   71.648312] [<c011e970>] (sysfs_write_file+0x168/0x198) from
>>> [<c00cd060>] (vfs_write+0xb0/0x188)
>>> [   71.648321] [<c00cd060>] (vfs_write+0xb0/0x188) from [<c00cd410>]
>>> (SyS_write+0x3c/0x70)
>>> [   71.648330] [<c00cd410>] (SyS_write+0x3c/0x70) from [<c000e560>]
>>> (ret_fast_syscall+0x0/0x30)
>>> [   71.648334] ---[ end trace e5ed8212ac4db5da ]---
>>> [   72.063578] mmc1: power class selection to bus width 8 failed
>>> [   72.297753] usb 2-1: reset high-speed USB device number 2 using
>>> tegra-ehci [   72.567736] usb 3-1: reset high-speed USB device number 2
>>> using tegra-ehci [   73.022058] usb 3-1.3: reset high-speed USB device
>>> number 4 using tegra- ehci
>>> [   73.216064] usb 3-1.2: reset high-speed USB device number 3 using
>>> tegra-
>>> ehci
>>> [   73.360225] PM: resume of devices complete after 1715.937 msecs
>>> [   73.361008] Restarting tasks ... done.
>>>
>>> and sdcard being offline after resume (fixed by
>>> CONFIG_MMC_UNSAFE_RESUME=y). Anyway, thanks for your great work!
>>>
>>> Tested-By: Marc Dietrich <marvin24-Mmb7MZpHnFY@public.gmane.org>
>>
>> I had same warn on 3.8.
>> https://bitbucket.org/digetx/picasso-kernel/commits/a86dc95ab48dd77eaa046384
>> 4cdf88a5acf9d842
> 
> Stephen Warren said on IRC that this is known and on the internal bug tracker 
> already. I just posted it so others are not surprised. Well, I should have 
> added that it's a know problem. Sorry for that.
> 
> Marc
> 
> 
> 
> 
It might be a known problem only for those who faced with it. Since it's on
their internal irc, it wasn't a known issue.

It also would be interesting to know if suspend with thumb2 works correctly. On
3.8 I've ported lp0 suspend and it works fine with arm compiled kernel, thumb2
also works but only for about 5-10 hours (resuming on each 5-30 minutes, default
android behavior) and after I'm getting pmd corruption. I have no idea from
where that corruption comes from without ability for hw debugging.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 0/8] ARM: tegra: support LP1 suspend mode
@ 2013-07-27 18:26             ` Dmitry Osipenko
  0 siblings, 0 replies; 88+ messages in thread
From: Dmitry Osipenko @ 2013-07-27 18:26 UTC (permalink / raw)
  To: linux-arm-kernel

27.07.2013 22:09, Marc Dietrich ?????:
> On Saturday 27 July 2013 20:20:38 Dmitry Osipenko wrote:
>> 27.07.2013 20:12, Marc Dietrich ?????:
>>> On Friday 26 July 2013 17:15:02 Joseph Lo wrote:
>>>> This series adds the support of LP1 suspend mode for Tegra.
>>>>
>>>> Verified on Seaboard, Cardhu and Dalmore.
>>>
>>> tested on AC100 and worked fine, except
>>>
>>> [   68.448007] PM: Syncing filesystems ... done.
>>> [   71.370086] Freezing user space processes ... (elapsed 0.001 seconds)
>>> done. [   71.370088] Freezing remaining freezable tasks ... (elapsed
>>> 0.001 seconds) done.
>>> [   71.371253] Suspending console(s) (use no_console_suspend to debug)
>>> [   71.638481] PM: suspend of devices complete after 266.604 msecs
>>> [   71.638905] PM: late suspend of devices complete after 0.417 msecs
>>> [   71.639477] PM: noirq suspend of devices complete after 0.568 msecs
>>> [   71.639480] Disabling non-boot CPUs ...
>>> [   71.640242] CPU1: shutdown
>>> [   71.641216] Entering suspend state LP1
>>> [   71.641267] Enabling non-boot CPUs ...
>>> [   71.643125] CPU1: Booted secondary processor
>>> [   71.643449] CPU1 is up
>>> [   71.643786] PM: noirq resume of devices complete after 0.331 msecs
>>> [   71.644300] PM: early resume of devices complete after 0.364 msecs
>>> [   71.648038] ------------[ cut here ]------------
>>> [   71.648061] WARNING: CPU: 0 PID: 1025 at kernel/irq/manage.c:529
>>> irq_set_irq_wake+0xb8/0xf0()
>>> [   71.648064] Unbalanced IRQ 384 wake disable
>>> [   71.648068] Modules linked in:
>>> [   71.648075] CPU: 0 PID: 1025 Comm: bash Not tainted 3.11.0-rc2-
>>> next-20130726-00008-gef01509-dirty #6
>>> [   71.648107] [<c001551c>] (unwind_backtrace+0x0/0xf8) from [<c001154c>]
>>> (show_stack+0x10/0x14)
>>> [   71.648128] [<c001154c>] (show_stack+0x10/0x14) from [<c0515288>]
>>> (dump_stack+0x80/0xc4)
>>> [   71.648142] [<c0515288>] (dump_stack+0x80/0xc4) from [<c0025340>]
>>> (warn_slowpath_common+0x64/0x88)
>>> [   71.648151] [<c0025340>] (warn_slowpath_common+0x64/0x88) from
>>> [<c00253f8>] (warn_slowpath_fmt+0x30/0x40)
>>> [   71.648161] [<c00253f8>] (warn_slowpath_fmt+0x30/0x40) from
>>> [<c007ff8c>]
>>> (irq_set_irq_wake+0xb8/0xf0)
>>> [   71.648178] [<c007ff8c>] (irq_set_irq_wake+0xb8/0xf0) from [<c0334d00>]
>>> (tps6586x_rtc_resume+0x30/0x38)
>>> [   71.648192] [<c0334d00>] (tps6586x_rtc_resume+0x30/0x38) from
>>> [<c02951d0>] (platform_pm_resume+0x2c/0x4c)
>>> [   71.648202] [<c02951d0>] (platform_pm_resume+0x2c/0x4c) from
>>> [<c0299d40>] (dpm_run_callback.isra.5+0x18/0x38)
>>> [   71.648211] [<c0299d40>] (dpm_run_callback.isra.5+0x18/0x38) from
>>> [<c029a044>] (device_resume+0xb0/0x170)
>>> [   71.648219] [<c029a044>] (device_resume+0xb0/0x170) from [<c029aeb4>]
>>> (dpm_resume+0xe8/0x20c)
>>> [   71.648227] [<c029aeb4>] (dpm_resume+0xe8/0x20c) from [<c029b174>]
>>> (dpm_resume_end+0xc/0x18)
>>> [   71.648245] [<c029b174>] (dpm_resume_end+0xc/0x18) from [<c005e480>]
>>> (suspend_devices_and_enter+0xdc/0x2f8)
>>> [   71.648256] [<c005e480>] (suspend_devices_and_enter+0xdc/0x2f8) from
>>> [<c005e810>] (pm_suspend+0x174/0x1e8)
>>> [   71.648265] [<c005e810>] (pm_suspend+0x174/0x1e8) from [<c005d710>]
>>> (state_store+0x6c/0xbc)
>>> [   71.648285] [<c005d710>] (state_store+0x6c/0xbc) from [<c01eca78>]
>>> (kobj_attr_store+0x14/0x20)
>>> [   71.648298] [<c01eca78>] (kobj_attr_store+0x14/0x20) from [<c011e970>]
>>> (sysfs_write_file+0x168/0x198)
>>> [   71.648312] [<c011e970>] (sysfs_write_file+0x168/0x198) from
>>> [<c00cd060>] (vfs_write+0xb0/0x188)
>>> [   71.648321] [<c00cd060>] (vfs_write+0xb0/0x188) from [<c00cd410>]
>>> (SyS_write+0x3c/0x70)
>>> [   71.648330] [<c00cd410>] (SyS_write+0x3c/0x70) from [<c000e560>]
>>> (ret_fast_syscall+0x0/0x30)
>>> [   71.648334] ---[ end trace e5ed8212ac4db5da ]---
>>> [   72.063578] mmc1: power class selection to bus width 8 failed
>>> [   72.297753] usb 2-1: reset high-speed USB device number 2 using
>>> tegra-ehci [   72.567736] usb 3-1: reset high-speed USB device number 2
>>> using tegra-ehci [   73.022058] usb 3-1.3: reset high-speed USB device
>>> number 4 using tegra- ehci
>>> [   73.216064] usb 3-1.2: reset high-speed USB device number 3 using
>>> tegra-
>>> ehci
>>> [   73.360225] PM: resume of devices complete after 1715.937 msecs
>>> [   73.361008] Restarting tasks ... done.
>>>
>>> and sdcard being offline after resume (fixed by
>>> CONFIG_MMC_UNSAFE_RESUME=y). Anyway, thanks for your great work!
>>>
>>> Tested-By: Marc Dietrich <marvin24@gmx.de>
>>
>> I had same warn on 3.8.
>> https://bitbucket.org/digetx/picasso-kernel/commits/a86dc95ab48dd77eaa046384
>> 4cdf88a5acf9d842
> 
> Stephen Warren said on IRC that this is known and on the internal bug tracker 
> already. I just posted it so others are not surprised. Well, I should have 
> added that it's a know problem. Sorry for that.
> 
> Marc
> 
> 
> 
> 
It might be a known problem only for those who faced with it. Since it's on
their internal irc, it wasn't a known issue.

It also would be interesting to know if suspend with thumb2 works correctly. On
3.8 I've ported lp0 suspend and it works fine with arm compiled kernel, thumb2
also works but only for about 5-10 hours (resuming on each 5-30 minutes, default
android behavior) and after I'm getting pmd corruption. I have no idea from
where that corruption comes from without ability for hw debugging.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 0/8] ARM: tegra: support LP1 suspend mode
  2013-07-27 18:26             ` Dmitry Osipenko
@ 2013-07-27 18:29                 ` Dmitry Osipenko
  -1 siblings, 0 replies; 88+ messages in thread
From: Dmitry Osipenko @ 2013-07-27 18:29 UTC (permalink / raw)
  To: Marc Dietrich
  Cc: Joseph Lo, Stephen Warren, linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

27.07.2013 22:26, Dmitry Osipenko пишет:
> It might be a known problem only for those who faced with it. Since it's on
> their internal irc, it wasn't a known issue.
> 
> It also would be interesting to know if suspend with thumb2 works correctly. On
> 3.8 I've ported lp0 suspend and it works fine with arm compiled kernel, thumb2
> also works but only for about 5-10 hours (resuming on each 5-30 minutes, default
> android behavior) and after I'm getting pmd corruption. I have no idea from
> where that corruption comes from without ability for hw debugging.
s/irc/bug tracker/ :)

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 0/8] ARM: tegra: support LP1 suspend mode
@ 2013-07-27 18:29                 ` Dmitry Osipenko
  0 siblings, 0 replies; 88+ messages in thread
From: Dmitry Osipenko @ 2013-07-27 18:29 UTC (permalink / raw)
  To: linux-arm-kernel

27.07.2013 22:26, Dmitry Osipenko ?????:
> It might be a known problem only for those who faced with it. Since it's on
> their internal irc, it wasn't a known issue.
> 
> It also would be interesting to know if suspend with thumb2 works correctly. On
> 3.8 I've ported lp0 suspend and it works fine with arm compiled kernel, thumb2
> also works but only for about 5-10 hours (resuming on each 5-30 minutes, default
> android behavior) and after I'm getting pmd corruption. I have no idea from
> where that corruption comes from without ability for hw debugging.
s/irc/bug tracker/ :)

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 0/8] ARM: tegra: support LP1 suspend mode
  2013-07-27 18:26             ` Dmitry Osipenko
@ 2013-07-27 19:03                 ` Marc Dietrich
  -1 siblings, 0 replies; 88+ messages in thread
From: Marc Dietrich @ 2013-07-27 19:03 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Joseph Lo, Stephen Warren, linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Saturday 27 July 2013 22:26:06 Dmitry Osipenko wrote:
> 27.07.2013 22:09, Marc Dietrich пишет:
> > On Saturday 27 July 2013 20:20:38 Dmitry Osipenko wrote:
> >> 27.07.2013 20:12, Marc Dietrich пишет:
> >>> On Friday 26 July 2013 17:15:02 Joseph Lo wrote:
> >>>> This series adds the support of LP1 suspend mode for Tegra.
> >>>> 
> >>>> Verified on Seaboard, Cardhu and Dalmore.
> >>> 
> >>> tested on AC100 and worked fine, except
> >>> 
> >>> [   68.448007] PM: Syncing filesystems ... done.
> >>> [   71.370086] Freezing user space processes ... (elapsed 0.001 seconds)
> >>> done. [   71.370088] Freezing remaining freezable tasks ... (elapsed
> >>> 0.001 seconds) done.
> >>> [   71.371253] Suspending console(s) (use no_console_suspend to debug)
> >>> [   71.638481] PM: suspend of devices complete after 266.604 msecs
> >>> [   71.638905] PM: late suspend of devices complete after 0.417 msecs
> >>> [   71.639477] PM: noirq suspend of devices complete after 0.568 msecs
> >>> [   71.639480] Disabling non-boot CPUs ...
> >>> [   71.640242] CPU1: shutdown
> >>> [   71.641216] Entering suspend state LP1
> >>> [   71.641267] Enabling non-boot CPUs ...
> >>> [   71.643125] CPU1: Booted secondary processor
> >>> [   71.643449] CPU1 is up
> >>> [   71.643786] PM: noirq resume of devices complete after 0.331 msecs
> >>> [   71.644300] PM: early resume of devices complete after 0.364 msecs
> >>> [   71.648038] ------------[ cut here ]------------
> >>> [   71.648061] WARNING: CPU: 0 PID: 1025 at kernel/irq/manage.c:529
> >>> irq_set_irq_wake+0xb8/0xf0()
> >>> [   71.648064] Unbalanced IRQ 384 wake disable
> >>> [   71.648068] Modules linked in:
> >>> [   71.648075] CPU: 0 PID: 1025 Comm: bash Not tainted 3.11.0-rc2-
> >>> next-20130726-00008-gef01509-dirty #6

> > Stephen Warren said on IRC that this is known and on the internal bug
> > tracker already. I just posted it so others are not surprised. Well, I
> > should have added that it's a know problem. Sorry for that.
> 
> It might be a known problem only for those who faced with it. Since it's on
> their internal irc, it wasn't a known issue.
> 
> It also would be interesting to know if suspend with thumb2 works correctly.
> On 3.8 I've ported lp0 suspend and it works fine with arm compiled kernel,
> thumb2 also works but only for about 5-10 hours (resuming on each 5-30
> minutes, default android behavior) and after I'm getting pmd corruption. I
> have no idea from where that corruption comes from without ability for hw
> debugging.

I don't think anyone can comment on this because lp0 support for mainline is 
not published yet. On the other hand, if you have patches for which you want 
some review, feel free to post them here.

Marc

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 0/8] ARM: tegra: support LP1 suspend mode
@ 2013-07-27 19:03                 ` Marc Dietrich
  0 siblings, 0 replies; 88+ messages in thread
From: Marc Dietrich @ 2013-07-27 19:03 UTC (permalink / raw)
  To: linux-arm-kernel

On Saturday 27 July 2013 22:26:06 Dmitry Osipenko wrote:
> 27.07.2013 22:09, Marc Dietrich ?????:
> > On Saturday 27 July 2013 20:20:38 Dmitry Osipenko wrote:
> >> 27.07.2013 20:12, Marc Dietrich ?????:
> >>> On Friday 26 July 2013 17:15:02 Joseph Lo wrote:
> >>>> This series adds the support of LP1 suspend mode for Tegra.
> >>>> 
> >>>> Verified on Seaboard, Cardhu and Dalmore.
> >>> 
> >>> tested on AC100 and worked fine, except
> >>> 
> >>> [   68.448007] PM: Syncing filesystems ... done.
> >>> [   71.370086] Freezing user space processes ... (elapsed 0.001 seconds)
> >>> done. [   71.370088] Freezing remaining freezable tasks ... (elapsed
> >>> 0.001 seconds) done.
> >>> [   71.371253] Suspending console(s) (use no_console_suspend to debug)
> >>> [   71.638481] PM: suspend of devices complete after 266.604 msecs
> >>> [   71.638905] PM: late suspend of devices complete after 0.417 msecs
> >>> [   71.639477] PM: noirq suspend of devices complete after 0.568 msecs
> >>> [   71.639480] Disabling non-boot CPUs ...
> >>> [   71.640242] CPU1: shutdown
> >>> [   71.641216] Entering suspend state LP1
> >>> [   71.641267] Enabling non-boot CPUs ...
> >>> [   71.643125] CPU1: Booted secondary processor
> >>> [   71.643449] CPU1 is up
> >>> [   71.643786] PM: noirq resume of devices complete after 0.331 msecs
> >>> [   71.644300] PM: early resume of devices complete after 0.364 msecs
> >>> [   71.648038] ------------[ cut here ]------------
> >>> [   71.648061] WARNING: CPU: 0 PID: 1025 at kernel/irq/manage.c:529
> >>> irq_set_irq_wake+0xb8/0xf0()
> >>> [   71.648064] Unbalanced IRQ 384 wake disable
> >>> [   71.648068] Modules linked in:
> >>> [   71.648075] CPU: 0 PID: 1025 Comm: bash Not tainted 3.11.0-rc2-
> >>> next-20130726-00008-gef01509-dirty #6

> > Stephen Warren said on IRC that this is known and on the internal bug
> > tracker already. I just posted it so others are not surprised. Well, I
> > should have added that it's a know problem. Sorry for that.
> 
> It might be a known problem only for those who faced with it. Since it's on
> their internal irc, it wasn't a known issue.
> 
> It also would be interesting to know if suspend with thumb2 works correctly.
> On 3.8 I've ported lp0 suspend and it works fine with arm compiled kernel,
> thumb2 also works but only for about 5-10 hours (resuming on each 5-30
> minutes, default android behavior) and after I'm getting pmd corruption. I
> have no idea from where that corruption comes from without ability for hw
> debugging.

I don't think anyone can comment on this because lp0 support for mainline is 
not published yet. On the other hand, if you have patches for which you want 
some review, feel free to post them here.

Marc

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 0/8] ARM: tegra: support LP1 suspend mode
  2013-07-27 19:03                 ` Marc Dietrich
@ 2013-07-27 19:11                   ` Dmitry Osipenko
  -1 siblings, 0 replies; 88+ messages in thread
From: Dmitry Osipenko @ 2013-07-27 19:11 UTC (permalink / raw)
  To: Marc Dietrich
  Cc: Joseph Lo, Stephen Warren, linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

27.07.2013 23:03, Marc Dietrich пишет:
> On Saturday 27 July 2013 22:26:06 Dmitry Osipenko wrote:
>> 27.07.2013 22:09, Marc Dietrich пишет:
>>> On Saturday 27 July 2013 20:20:38 Dmitry Osipenko wrote:
>>>> 27.07.2013 20:12, Marc Dietrich пишет:
>>>>> On Friday 26 July 2013 17:15:02 Joseph Lo wrote:
>>>>>> This series adds the support of LP1 suspend mode for Tegra.
>>>>>>
>>>>>> Verified on Seaboard, Cardhu and Dalmore.
>>>>>
>>>>> tested on AC100 and worked fine, except
>>>>>
>>>>> [   68.448007] PM: Syncing filesystems ... done.
>>>>> [   71.370086] Freezing user space processes ... (elapsed 0.001 seconds)
>>>>> done. [   71.370088] Freezing remaining freezable tasks ... (elapsed
>>>>> 0.001 seconds) done.
>>>>> [   71.371253] Suspending console(s) (use no_console_suspend to debug)
>>>>> [   71.638481] PM: suspend of devices complete after 266.604 msecs
>>>>> [   71.638905] PM: late suspend of devices complete after 0.417 msecs
>>>>> [   71.639477] PM: noirq suspend of devices complete after 0.568 msecs
>>>>> [   71.639480] Disabling non-boot CPUs ...
>>>>> [   71.640242] CPU1: shutdown
>>>>> [   71.641216] Entering suspend state LP1
>>>>> [   71.641267] Enabling non-boot CPUs ...
>>>>> [   71.643125] CPU1: Booted secondary processor
>>>>> [   71.643449] CPU1 is up
>>>>> [   71.643786] PM: noirq resume of devices complete after 0.331 msecs
>>>>> [   71.644300] PM: early resume of devices complete after 0.364 msecs
>>>>> [   71.648038] ------------[ cut here ]------------
>>>>> [   71.648061] WARNING: CPU: 0 PID: 1025 at kernel/irq/manage.c:529
>>>>> irq_set_irq_wake+0xb8/0xf0()
>>>>> [   71.648064] Unbalanced IRQ 384 wake disable
>>>>> [   71.648068] Modules linked in:
>>>>> [   71.648075] CPU: 0 PID: 1025 Comm: bash Not tainted 3.11.0-rc2-
>>>>> next-20130726-00008-gef01509-dirty #6
> 
>>> Stephen Warren said on IRC that this is known and on the internal bug
>>> tracker already. I just posted it so others are not surprised. Well, I
>>> should have added that it's a know problem. Sorry for that.
>>
>> It might be a known problem only for those who faced with it. Since it's on
>> their internal irc, it wasn't a known issue.
>>
>> It also would be interesting to know if suspend with thumb2 works correctly.
>> On 3.8 I've ported lp0 suspend and it works fine with arm compiled kernel,
>> thumb2 also works but only for about 5-10 hours (resuming on each 5-30
>> minutes, default android behavior) and after I'm getting pmd corruption. I
>> have no idea from where that corruption comes from without ability for hw
>> debugging.
> 
> I don't think anyone can comment on this because lp0 support for mainline is 
> not published yet. On the other hand, if you have patches for which you want 
> some review, feel free to post them here.
> 
> Marc
> 
I'm not expecting comment on it, just for notice :) I'll try this patches in
some near future.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 0/8] ARM: tegra: support LP1 suspend mode
@ 2013-07-27 19:11                   ` Dmitry Osipenko
  0 siblings, 0 replies; 88+ messages in thread
From: Dmitry Osipenko @ 2013-07-27 19:11 UTC (permalink / raw)
  To: linux-arm-kernel

27.07.2013 23:03, Marc Dietrich ?????:
> On Saturday 27 July 2013 22:26:06 Dmitry Osipenko wrote:
>> 27.07.2013 22:09, Marc Dietrich ?????:
>>> On Saturday 27 July 2013 20:20:38 Dmitry Osipenko wrote:
>>>> 27.07.2013 20:12, Marc Dietrich ?????:
>>>>> On Friday 26 July 2013 17:15:02 Joseph Lo wrote:
>>>>>> This series adds the support of LP1 suspend mode for Tegra.
>>>>>>
>>>>>> Verified on Seaboard, Cardhu and Dalmore.
>>>>>
>>>>> tested on AC100 and worked fine, except
>>>>>
>>>>> [   68.448007] PM: Syncing filesystems ... done.
>>>>> [   71.370086] Freezing user space processes ... (elapsed 0.001 seconds)
>>>>> done. [   71.370088] Freezing remaining freezable tasks ... (elapsed
>>>>> 0.001 seconds) done.
>>>>> [   71.371253] Suspending console(s) (use no_console_suspend to debug)
>>>>> [   71.638481] PM: suspend of devices complete after 266.604 msecs
>>>>> [   71.638905] PM: late suspend of devices complete after 0.417 msecs
>>>>> [   71.639477] PM: noirq suspend of devices complete after 0.568 msecs
>>>>> [   71.639480] Disabling non-boot CPUs ...
>>>>> [   71.640242] CPU1: shutdown
>>>>> [   71.641216] Entering suspend state LP1
>>>>> [   71.641267] Enabling non-boot CPUs ...
>>>>> [   71.643125] CPU1: Booted secondary processor
>>>>> [   71.643449] CPU1 is up
>>>>> [   71.643786] PM: noirq resume of devices complete after 0.331 msecs
>>>>> [   71.644300] PM: early resume of devices complete after 0.364 msecs
>>>>> [   71.648038] ------------[ cut here ]------------
>>>>> [   71.648061] WARNING: CPU: 0 PID: 1025 at kernel/irq/manage.c:529
>>>>> irq_set_irq_wake+0xb8/0xf0()
>>>>> [   71.648064] Unbalanced IRQ 384 wake disable
>>>>> [   71.648068] Modules linked in:
>>>>> [   71.648075] CPU: 0 PID: 1025 Comm: bash Not tainted 3.11.0-rc2-
>>>>> next-20130726-00008-gef01509-dirty #6
> 
>>> Stephen Warren said on IRC that this is known and on the internal bug
>>> tracker already. I just posted it so others are not surprised. Well, I
>>> should have added that it's a know problem. Sorry for that.
>>
>> It might be a known problem only for those who faced with it. Since it's on
>> their internal irc, it wasn't a known issue.
>>
>> It also would be interesting to know if suspend with thumb2 works correctly.
>> On 3.8 I've ported lp0 suspend and it works fine with arm compiled kernel,
>> thumb2 also works but only for about 5-10 hours (resuming on each 5-30
>> minutes, default android behavior) and after I'm getting pmd corruption. I
>> have no idea from where that corruption comes from without ability for hw
>> debugging.
> 
> I don't think anyone can comment on this because lp0 support for mainline is 
> not published yet. On the other hand, if you have patches for which you want 
> some review, feel free to post them here.
> 
> Marc
> 
I'm not expecting comment on it, just for notice :) I'll try this patches in
some near future.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 1/8] ARM: tegra: add common resume handling code for LP1 resuming
  2013-07-26  9:15     ` Joseph Lo
@ 2013-07-29 22:38         ` Stephen Warren
  -1 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-07-29 22:38 UTC (permalink / raw)
  To: Joseph Lo
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On 07/26/2013 03:15 AM, Joseph Lo wrote:
> The common LP1 resuming procedures of LP1 on Tegra was checking the LP1
> mask first. The LP1 mask indicated that the Tegra device was in LP1 then
> we need to resume the Tegra from the LP1 reset handler.
> 
> And the LP1 was putting the SDRAM to self-refresh mode, the SDRAM wasn't
> accessible when resuming from LP1. We need to copy the LP1 reset handler
> to IRAM before suspending. That's why you can see the address of LP1
> reset handler was located in IRAM.

That's rather hard to understand. How about:

==========
Add support to the Tegra CPU reset vector to detect whether the CPU is
resuming from LP1 suspend state. If it is, branch to the LP1-specific
resume code.

When Tegra enters the LP1 suspend state, the SDRAM controller is placed
into a self-refresh state. For this reason, we must place the LP1 resume
code into IRAM, so that it is accessible before SDRAM access has been
re-enabled.
==========

Talking about *copying* the LP1 resume code to IRAM isn't appropriate
here, since nothing in this patch performs any copying of the resume code.

> diff --git a/arch/arm/mach-tegra/reset-handler.S b/arch/arm/mach-tegra/reset-handler.S

> @@ -182,6 +182,19 @@ after_errata:
>  1:
>  #endif
>  
> +	/* Waking up from LP1? */
> +	ldr	r8, [r12, #RESET_DATA(MASK_LP1)]
> +	tst	r8, r11				@ if in_lp1
> +	beq	__is_not_lp1
> +	cmp	r10, #0
> +	bne	__die				@ only CPU0 can be here
> +	ldr	lr, [r12, #RESET_DATA(STARTUP_LP1)]
> + THUMB(	add	lr, lr, #1 )			@ switch to Thumb mode
> +	cmp	lr, #0
> +	bleq	__die				@ no LP1 startup handler

On thumb, lr==#1 at this point. I think you want to move the THUMB()
instruction after the bleq.

> diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h

> +#define TEGRA_IRAM_CODE_AREA	(TEGRA_IRAM_BASE + SZ_4K)

The layout of IRAM should be described in irammap.h not sleep.h.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 1/8] ARM: tegra: add common resume handling code for LP1 resuming
@ 2013-07-29 22:38         ` Stephen Warren
  0 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-07-29 22:38 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/26/2013 03:15 AM, Joseph Lo wrote:
> The common LP1 resuming procedures of LP1 on Tegra was checking the LP1
> mask first. The LP1 mask indicated that the Tegra device was in LP1 then
> we need to resume the Tegra from the LP1 reset handler.
> 
> And the LP1 was putting the SDRAM to self-refresh mode, the SDRAM wasn't
> accessible when resuming from LP1. We need to copy the LP1 reset handler
> to IRAM before suspending. That's why you can see the address of LP1
> reset handler was located in IRAM.

That's rather hard to understand. How about:

==========
Add support to the Tegra CPU reset vector to detect whether the CPU is
resuming from LP1 suspend state. If it is, branch to the LP1-specific
resume code.

When Tegra enters the LP1 suspend state, the SDRAM controller is placed
into a self-refresh state. For this reason, we must place the LP1 resume
code into IRAM, so that it is accessible before SDRAM access has been
re-enabled.
==========

Talking about *copying* the LP1 resume code to IRAM isn't appropriate
here, since nothing in this patch performs any copying of the resume code.

> diff --git a/arch/arm/mach-tegra/reset-handler.S b/arch/arm/mach-tegra/reset-handler.S

> @@ -182,6 +182,19 @@ after_errata:
>  1:
>  #endif
>  
> +	/* Waking up from LP1? */
> +	ldr	r8, [r12, #RESET_DATA(MASK_LP1)]
> +	tst	r8, r11				@ if in_lp1
> +	beq	__is_not_lp1
> +	cmp	r10, #0
> +	bne	__die				@ only CPU0 can be here
> +	ldr	lr, [r12, #RESET_DATA(STARTUP_LP1)]
> + THUMB(	add	lr, lr, #1 )			@ switch to Thumb mode
> +	cmp	lr, #0
> +	bleq	__die				@ no LP1 startup handler

On thumb, lr==#1 at this point. I think you want to move the THUMB()
instruction after the bleq.

> diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h

> +#define TEGRA_IRAM_CODE_AREA	(TEGRA_IRAM_BASE + SZ_4K)

The layout of IRAM should be described in irammap.h not sleep.h.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 2/8] ARM: tegra: config the polarity of the request of sys clock
  2013-07-26  9:15     ` Joseph Lo
@ 2013-07-29 22:47         ` Stephen Warren
  -1 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-07-29 22:47 UTC (permalink / raw)
  To: Joseph Lo
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On 07/26/2013 03:15 AM, Joseph Lo wrote:
> When suspending to LP1 mode, the SYSCLK will be clock gated. And different
> board may have different polarity of the request of SYSCLK, this patch
> configure the polarity from the DT for the board.

> diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c

> @@ -238,6 +240,20 @@ void tegra_pmc_suspend_init(void)
>  	reg = tegra_pmc_readl(PMC_CTRL);
>  	reg |= TEGRA_POWER_CPU_PWRREQ_OE;
>  	tegra_pmc_writel(reg, PMC_CTRL);
> +
> +	reg = tegra_pmc_readl(PMC_CTRL);
> +
> +	if (!pmc_pm_data.sysclkreq_high)
> +		reg |= TEGRA_POWER_SYSCLK_POLARITY;
> +	else
> +		reg &= ~TEGRA_POWER_SYSCLK_POLARITY;
> +
> +	/* configure the output inverts while the request is tristated */
> +	tegra_pmc_writel(reg, PMC_CTRL);

I think s/inverts/polarity/ in that comment would make a lot more sense.

Must _OE be disabled for the code to work correctly? If so, should the
code explicitly clear _OE first, since who knows what state it was
originally in? Can't we just set the new polarity and enable _OE in a
single register write?

> +	/* now enable the request */
> +	reg |= TEGRA_POWER_SYSCLK_OE;
> +	tegra_pmc_writel(reg, PMC_CTRL);

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 2/8] ARM: tegra: config the polarity of the request of sys clock
@ 2013-07-29 22:47         ` Stephen Warren
  0 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-07-29 22:47 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/26/2013 03:15 AM, Joseph Lo wrote:
> When suspending to LP1 mode, the SYSCLK will be clock gated. And different
> board may have different polarity of the request of SYSCLK, this patch
> configure the polarity from the DT for the board.

> diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c

> @@ -238,6 +240,20 @@ void tegra_pmc_suspend_init(void)
>  	reg = tegra_pmc_readl(PMC_CTRL);
>  	reg |= TEGRA_POWER_CPU_PWRREQ_OE;
>  	tegra_pmc_writel(reg, PMC_CTRL);
> +
> +	reg = tegra_pmc_readl(PMC_CTRL);
> +
> +	if (!pmc_pm_data.sysclkreq_high)
> +		reg |= TEGRA_POWER_SYSCLK_POLARITY;
> +	else
> +		reg &= ~TEGRA_POWER_SYSCLK_POLARITY;
> +
> +	/* configure the output inverts while the request is tristated */
> +	tegra_pmc_writel(reg, PMC_CTRL);

I think s/inverts/polarity/ in that comment would make a lot more sense.

Must _OE be disabled for the code to work correctly? If so, should the
code explicitly clear _OE first, since who knows what state it was
originally in? Can't we just set the new polarity and enable _OE in a
single register write?

> +	/* now enable the request */
> +	reg |= TEGRA_POWER_SYSCLK_OE;
> +	tegra_pmc_writel(reg, PMC_CTRL);

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
  2013-07-26  9:15     ` Joseph Lo
@ 2013-07-29 22:51         ` Stephen Warren
  -1 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-07-29 22:51 UTC (permalink / raw)
  To: Joseph Lo
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Mike Turquette

On 07/26/2013 03:15 AM, Joseph Lo wrote:
> When the system suspends to LP1, the clock of the CPU would be switched to
> CLK_M (12MHz Oscillator) during suspend/resume flow. The clock driver
> needs to restore the clock of CPU after LP1 resume.

It's unclear to me how the code change implements "restore the clock of
the CPU". A register name of CCLKG_BURST_POLICY doesn't sound like it's
anything to do with enabled/disabling the CPU clock, nor configuring its
rate. What exactly does this register do, and hence what does this new
code actually restore?

Why don't Tegra20/30 need a similar change?

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
@ 2013-07-29 22:51         ` Stephen Warren
  0 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-07-29 22:51 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/26/2013 03:15 AM, Joseph Lo wrote:
> When the system suspends to LP1, the clock of the CPU would be switched to
> CLK_M (12MHz Oscillator) during suspend/resume flow. The clock driver
> needs to restore the clock of CPU after LP1 resume.

It's unclear to me how the code change implements "restore the clock of
the CPU". A register name of CCLKG_BURST_POLICY doesn't sound like it's
anything to do with enabled/disabling the CPU clock, nor configuring its
rate. What exactly does this register do, and hence what does this new
code actually restore?

Why don't Tegra20/30 need a similar change?

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 4/8] ARM: tegra: add common LP1 suspend support
  2013-07-26  9:15     ` Joseph Lo
@ 2013-07-29 23:13         ` Stephen Warren
  -1 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-07-29 23:13 UTC (permalink / raw)
  To: Joseph Lo
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On 07/26/2013 03:15 AM, Joseph Lo wrote:
> The LP1 suspending mode on Tegra means CPU rail off, devices and PLLs are
> clock gated and SDRAM in self-refresh mode. That means the low level LP1
> suspending and resuming code couldn't be run on DRAM and the CPU must
> switch to the always on clock domain (a.k.a. CLK_M 12MHz oscillator). And
> the system clock (SCLK) would be switched to CLK_S, a 32KHz oscillator.
> The LP1 low level handling code need to be moved to IRAM area first. And
> marking the LP1 mask for indicating the Tegra device is in LP1. The CPU
> power timer needs to be re-calculated based on 32KHz that was originally
> based on PCLK.

> diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c

>  #ifdef CONFIG_PM_SLEEP
>  static DEFINE_SPINLOCK(tegra_lp2_lock);
> +static void __iomem *iram_code = IO_ADDRESS(TEGRA_IRAM_CODE_AREA);
> +static u32 iram_save_size;
> +static void *iram_save_addr;
> +struct tegra_lp1_iram *tegra_lp1_iram;
>  void (*tegra_tear_down_cpu)(void);
> +void (*tegra_sleep_core_finish)(unsigned long v2p);

I'm not sure all of those are required to be global variables. For
example, iram_code is just a constant, so you could easily just use it
directly in code.

> @@ -174,14 +180,75 @@ enum tegra_suspend_mode tegra_pm_validate_suspend_mode(
>  				enum tegra_suspend_mode mode)
>  {
>  	/*
> -	 * The Tegra devices only support suspending to LP2 currently.
> +	 * The Tegra devices only support suspending to LP1 currently.

That now says that only LP1 is supported. That's not true; both LP1 and
LP2 are supported. How about s/LP1/LP1 or lower/ or s:LP1:LP1/LP2: ?

> +static bool tegra_sleep_core_init(void)
> +{
> +	if (!tegra_sleep_core_finish)
> +		return false;
> +
> +	return true;
> +}

That function seems a little pointless. Why not just check the value of
tegra_sleep_core_finish directly in tegra_init_suspend()?

> +static void tegra_suspend_enter_lp1(void)
> +{
> +	tegra_pmc_suspend();
> +
> +	/* copy the reset vector & SDRAM shutdown code into IRAM */
> +	memcpy(iram_save_addr, iram_code, iram_save_size);
> +	memcpy(iram_code, tegra_lp1_iram->start_addr, iram_save_size);
> +
> +	*((u32 *)tegra_cpu_lp1_mask) = 1;
> +}
> +
> +static void tegra_suspend_exit_lp1(void)
> +{
> +	tegra_pmc_resume();
> +
> +	/* restore IRAM */
> +	memcpy(iram_code, iram_save_addr, iram_save_size);
> +
> +	*(u32 *)tegra_cpu_lp1_mask = 0;
> +}

I'm not really sure I like that, but I suppose it's OK. It sure seems
like a performance limiter, but I suppose LP1 is so slow it doesn't
matter, due to the need to ramp power rails, PLLs, SDRAM controller, etc.

It'd be nice to simply reserve more IRAM for the kernel's use. Right
now, only 1K is reserved, and presumably the code running on the AVP
can't use the rest of that page anyway, or can it?

> @@ -212,9 +282,15 @@ static int tegra_suspend_enter(suspend_state_t state)
>  		break;
>  	}
>  
> -	cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu);
> +	if (mode == TEGRA_SUSPEND_LP2)
> +		cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu);
> +	else
> +		cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_core);

Nit: It might be slightly simpler to simply calculate the parameter to
cpu_suspend(), but then take the same code-path:

if (mode == TEGRA_SUSPEND_LP2)
	sleep_func = tegra_sleep_cpu;
else
	sleep_func = tegra_sleep_core;
cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, sleep_func);

That way, it's a little more obvious that the code always calls
cpu_suspend() in the same way, but simply passes a different function to
it to power things down at the end.

> diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c

>  void tegra_pmc_pm_set(enum tegra_suspend_mode mode)
>  {
>  	u32 reg, csr_reg;
> -	unsigned long rate = 0;
> +	unsigned long rate = 32768;

That's LP1-specific. You should add a "case TEGRA_SUSPEND_LP1:" to the
switch statement that calculates rate instead; after all, that's the
whole point of having that switch statement.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 4/8] ARM: tegra: add common LP1 suspend support
@ 2013-07-29 23:13         ` Stephen Warren
  0 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-07-29 23:13 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/26/2013 03:15 AM, Joseph Lo wrote:
> The LP1 suspending mode on Tegra means CPU rail off, devices and PLLs are
> clock gated and SDRAM in self-refresh mode. That means the low level LP1
> suspending and resuming code couldn't be run on DRAM and the CPU must
> switch to the always on clock domain (a.k.a. CLK_M 12MHz oscillator). And
> the system clock (SCLK) would be switched to CLK_S, a 32KHz oscillator.
> The LP1 low level handling code need to be moved to IRAM area first. And
> marking the LP1 mask for indicating the Tegra device is in LP1. The CPU
> power timer needs to be re-calculated based on 32KHz that was originally
> based on PCLK.

> diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c

>  #ifdef CONFIG_PM_SLEEP
>  static DEFINE_SPINLOCK(tegra_lp2_lock);
> +static void __iomem *iram_code = IO_ADDRESS(TEGRA_IRAM_CODE_AREA);
> +static u32 iram_save_size;
> +static void *iram_save_addr;
> +struct tegra_lp1_iram *tegra_lp1_iram;
>  void (*tegra_tear_down_cpu)(void);
> +void (*tegra_sleep_core_finish)(unsigned long v2p);

I'm not sure all of those are required to be global variables. For
example, iram_code is just a constant, so you could easily just use it
directly in code.

> @@ -174,14 +180,75 @@ enum tegra_suspend_mode tegra_pm_validate_suspend_mode(
>  				enum tegra_suspend_mode mode)
>  {
>  	/*
> -	 * The Tegra devices only support suspending to LP2 currently.
> +	 * The Tegra devices only support suspending to LP1 currently.

That now says that only LP1 is supported. That's not true; both LP1 and
LP2 are supported. How about s/LP1/LP1 or lower/ or s:LP1:LP1/LP2: ?

> +static bool tegra_sleep_core_init(void)
> +{
> +	if (!tegra_sleep_core_finish)
> +		return false;
> +
> +	return true;
> +}

That function seems a little pointless. Why not just check the value of
tegra_sleep_core_finish directly in tegra_init_suspend()?

> +static void tegra_suspend_enter_lp1(void)
> +{
> +	tegra_pmc_suspend();
> +
> +	/* copy the reset vector & SDRAM shutdown code into IRAM */
> +	memcpy(iram_save_addr, iram_code, iram_save_size);
> +	memcpy(iram_code, tegra_lp1_iram->start_addr, iram_save_size);
> +
> +	*((u32 *)tegra_cpu_lp1_mask) = 1;
> +}
> +
> +static void tegra_suspend_exit_lp1(void)
> +{
> +	tegra_pmc_resume();
> +
> +	/* restore IRAM */
> +	memcpy(iram_code, iram_save_addr, iram_save_size);
> +
> +	*(u32 *)tegra_cpu_lp1_mask = 0;
> +}

I'm not really sure I like that, but I suppose it's OK. It sure seems
like a performance limiter, but I suppose LP1 is so slow it doesn't
matter, due to the need to ramp power rails, PLLs, SDRAM controller, etc.

It'd be nice to simply reserve more IRAM for the kernel's use. Right
now, only 1K is reserved, and presumably the code running on the AVP
can't use the rest of that page anyway, or can it?

> @@ -212,9 +282,15 @@ static int tegra_suspend_enter(suspend_state_t state)
>  		break;
>  	}
>  
> -	cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu);
> +	if (mode == TEGRA_SUSPEND_LP2)
> +		cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu);
> +	else
> +		cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_core);

Nit: It might be slightly simpler to simply calculate the parameter to
cpu_suspend(), but then take the same code-path:

if (mode == TEGRA_SUSPEND_LP2)
	sleep_func = tegra_sleep_cpu;
else
	sleep_func = tegra_sleep_core;
cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, sleep_func);

That way, it's a little more obvious that the code always calls
cpu_suspend() in the same way, but simply passes a different function to
it to power things down at the end.

> diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c

>  void tegra_pmc_pm_set(enum tegra_suspend_mode mode)
>  {
>  	u32 reg, csr_reg;
> -	unsigned long rate = 0;
> +	unsigned long rate = 32768;

That's LP1-specific. You should add a "case TEGRA_SUSPEND_LP1:" to the
switch statement that calculates rate instead; after all, that's the
whole point of having that switch statement.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 5/8] ARM: tegra30: add LP1 suspend support
  2013-07-26  9:15     ` Joseph Lo
@ 2013-07-29 23:45         ` Stephen Warren
  -1 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-07-29 23:45 UTC (permalink / raw)
  To: Joseph Lo
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On 07/26/2013 03:15 AM, Joseph Lo wrote:
> The LP1 suspend mode will power off the CPU, clock gated the PLLs and put
> SDRAM to self-refresh mode. Any interrupt can wake up device from LP1. The
> sequence when LP1 suspending:

> diff --git a/arch/arm/mach-tegra/pm-tegra30.c b/arch/arm/mach-tegra/pm-tegra30.c

> +void tegra30_lp1_iram_hook(void)
> +{
> +	tegra30_lp1_iram.start_addr = &tegra30_iram_start;
> +	tegra30_lp1_iram.end_addr = &tegra30_iram_end;

If you need to fill in the values in that struct dynamically anyway, why
not make tegra_lp1_iram be a struct rather than a pointer, and write
directly to it?

That said, aren't tegra30_iram_start constants that the linker can work
out, so I think you can just initialize the structure at compile-time,
and save some code.

> +	tegra_lp1_iram = &tegra30_lp1_iram;
> +}
> +
> +void tegra30_sleep_core_init(void)
> +{
> +	tegra_sleep_core_finish = tegra30_sleep_core_finish;
> +}

Is there a need to have separate iram_hook()/sleep_core_init()
functions? Perhaps they can be combined into a single function for
simplicity.

> diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h

> +#ifdef CONFIG_ARCH_TEGRA_3x_SOC
> +void tegra30_lp1_iram_hook(void);
> +void tegra30_sleep_core_init(void);
> +#else
> +static inline void tegra30_lp1_iram_hook(void) {}
> +static inline void void tegra30_sleep_core_init(void) {}
> +#endif

It'd be nice to be consistent re: whether we define dummy static
inlines, or use IS_ENABLED() at the call-site. IIRC, there's lots of use
of IS_ENABLED() in the Tegra PM code now, so perhaps these patches
should use that instead?

> diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h

> +/* waits until the microsecond counter (base) ticks, for exact timing loops */
> +.macro wait_for_us, rd, base, tmp
> +	ldr	\rd, [\base]
> +1001:	ldr	\tmp, [\base]
> +	cmp	\rd, \tmp
> +	beq	1001b
> +.endm

Doesn't this wait any amount of time from 0..1uS, and hence it actually
/isn't/ very exact?

> +/* waits until the microsecond counter (base) is > rn */
> +.macro wait_until, rn, base, tmp
> +	add	\rn, \rn, #1
> +1002:	ldr	\tmp, [\base]
> +	cmp	\tmp, \rn
> +	bmi	1002b
> +.endm

Parameter "rn" could be renamed to make its purpose clear from the name.
How about target_us or wait_until_this_time? "wait_until" is also rather
a generic name; wait_until_us would be much better, which would require
renaming wait_until_us above, which I think might be better deleted, or
renamed to wait_until_next_us?

> diff --git a/arch/arm/mach-tegra/sleep-tegra30.S b/arch/arm/mach-tegra/sleep-tegra30.S

> +ENTRY(tegra30_sleep_core_finish)

> +	/*
> +	 * Preload all the address literals that are needed for the
> +	 * CPU power-gating process, to avoid loading from SDRAM which
> +	 * are not supported once SDRAM is put into self-refresh.
> +	 * LP0 / LP1 use physical address, since the MMU needs to be
> +	 * disalbed before putting SDRAM into self-refresh to avoid

s/disalbed/disabled/

> +/*
> + * tegra30_lp1_reset
> + *
> + * reset vector for LP1 restore; copied into IRAM during suspend.
> + * Brings the system back up to a safe staring point (SDRAM out of
> + * self-refresh, PLLC, PLLM and PLLP reenabled, CPU running on PLLX,
> + * system clock running on the same PLL that it suspended at), and
> + * jumps to tegra_resume to restore virtual addressing.
> + * The physical address of tegra_resume expected to be stored in
> + * PMC_SCRATCH41.
> + *
> + * NOTE: THIS *MUST* BE RELOCATED TO TEGRA_IRAM_CODE_AREA AND MUST BE FIRST.

What does "AND MUST BE FIRST" mean?

> +ENTRY(tegra30_lp1_reset)
> +	/*
> +	 * The CPU and system bus are running at 32KHz and executing from
> +	 * IRAM when this code is executed; immediately switch to CLKM and
> +	 * enable PLLP, PLLM, PLLC, PLLA and PLLX.
> +	 */
> +	mov32	r0, TEGRA_CLK_RESET_BASE
> +
> +	mov	r1, #(1 << 28)

Some #defines for the various magic values used in this code would be
useful.

> +tegra30_sdram_pad_save:
> +	.word	0
> +	.word	0
> +	.word	0
> +	.word	0
> +	.word	0
> +	.word	0
> +	.word	0
> +	.word	0

This might be simpler, and easier to validate it's the right length:

        .rept   8
        .long   0
        .endr

> +tegra30_sdram_pad_address:
> +	.word	TEGRA_EMC_BASE + EMC_CFG				@0x0
> +	.word	TEGRA_EMC_BASE + EMC_ZCAL_INTERVAL			@0x4
> +	.word	TEGRA_EMC_BASE + EMC_AUTO_CAL_INTERVAL			@0x8
> +	.word	TEGRA_EMC_BASE + EMC_XM2VTTGENPADCTRL			@0xc
> +	.word	TEGRA_EMC_BASE + EMC_XM2VTTGENPADCTRL2			@0x10
> +	.word	TEGRA_PMC_BASE + PMC_IO_DPD_STATUS			@0x14
> +	.word	TEGRA_CLK_RESET_BASE + CLK_RESET_CLK_SOURCE_MSELECT	@0x18
> +	.word	TEGRA_CLK_RESET_BASE + CLK_RESET_SCLK_BURST		@0x1c
> +
> +tegra30_sdram_pad_size:
> +	.word	tegra30_sdram_pad_address - tegra30_sdram_pad_save

Perhaps if you swapp the order of declaring tegra30_sdram_pad_save and
tegra30_sdram_pad_address, you can even do something like:

	.rept (tegra30_sdram_pad_addr_end - tegra30_sdram_pad_addr) / 4

?

> +tegra30_switch_cpu_to_clk32k:
> +	/*
> +	 * start by jumping to CLKM to safely disable PLLs, then jump to

jumping sounds like a CPU program counter operation.
s/jumping/switching/ ?

> +	/* 2uS delay delay between changing SCLK and CCLK */
> +	wait_for_us r1, r7, r9
> +	add	r1, r1, #2
> +	wait_until r1, r7, r9

Ah, I see how wait_for_us is used now. Perhaps rename it
wait_for_us_boundary or wait_for_us_tick? Alternatively, perhaps
wait_until can just incorporate that logic itself?

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 5/8] ARM: tegra30: add LP1 suspend support
@ 2013-07-29 23:45         ` Stephen Warren
  0 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-07-29 23:45 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/26/2013 03:15 AM, Joseph Lo wrote:
> The LP1 suspend mode will power off the CPU, clock gated the PLLs and put
> SDRAM to self-refresh mode. Any interrupt can wake up device from LP1. The
> sequence when LP1 suspending:

> diff --git a/arch/arm/mach-tegra/pm-tegra30.c b/arch/arm/mach-tegra/pm-tegra30.c

> +void tegra30_lp1_iram_hook(void)
> +{
> +	tegra30_lp1_iram.start_addr = &tegra30_iram_start;
> +	tegra30_lp1_iram.end_addr = &tegra30_iram_end;

If you need to fill in the values in that struct dynamically anyway, why
not make tegra_lp1_iram be a struct rather than a pointer, and write
directly to it?

That said, aren't tegra30_iram_start constants that the linker can work
out, so I think you can just initialize the structure at compile-time,
and save some code.

> +	tegra_lp1_iram = &tegra30_lp1_iram;
> +}
> +
> +void tegra30_sleep_core_init(void)
> +{
> +	tegra_sleep_core_finish = tegra30_sleep_core_finish;
> +}

Is there a need to have separate iram_hook()/sleep_core_init()
functions? Perhaps they can be combined into a single function for
simplicity.

> diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h

> +#ifdef CONFIG_ARCH_TEGRA_3x_SOC
> +void tegra30_lp1_iram_hook(void);
> +void tegra30_sleep_core_init(void);
> +#else
> +static inline void tegra30_lp1_iram_hook(void) {}
> +static inline void void tegra30_sleep_core_init(void) {}
> +#endif

It'd be nice to be consistent re: whether we define dummy static
inlines, or use IS_ENABLED() at the call-site. IIRC, there's lots of use
of IS_ENABLED() in the Tegra PM code now, so perhaps these patches
should use that instead?

> diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h

> +/* waits until the microsecond counter (base) ticks, for exact timing loops */
> +.macro wait_for_us, rd, base, tmp
> +	ldr	\rd, [\base]
> +1001:	ldr	\tmp, [\base]
> +	cmp	\rd, \tmp
> +	beq	1001b
> +.endm

Doesn't this wait any amount of time from 0..1uS, and hence it actually
/isn't/ very exact?

> +/* waits until the microsecond counter (base) is > rn */
> +.macro wait_until, rn, base, tmp
> +	add	\rn, \rn, #1
> +1002:	ldr	\tmp, [\base]
> +	cmp	\tmp, \rn
> +	bmi	1002b
> +.endm

Parameter "rn" could be renamed to make its purpose clear from the name.
How about target_us or wait_until_this_time? "wait_until" is also rather
a generic name; wait_until_us would be much better, which would require
renaming wait_until_us above, which I think might be better deleted, or
renamed to wait_until_next_us?

> diff --git a/arch/arm/mach-tegra/sleep-tegra30.S b/arch/arm/mach-tegra/sleep-tegra30.S

> +ENTRY(tegra30_sleep_core_finish)

> +	/*
> +	 * Preload all the address literals that are needed for the
> +	 * CPU power-gating process, to avoid loading from SDRAM which
> +	 * are not supported once SDRAM is put into self-refresh.
> +	 * LP0 / LP1 use physical address, since the MMU needs to be
> +	 * disalbed before putting SDRAM into self-refresh to avoid

s/disalbed/disabled/

> +/*
> + * tegra30_lp1_reset
> + *
> + * reset vector for LP1 restore; copied into IRAM during suspend.
> + * Brings the system back up to a safe staring point (SDRAM out of
> + * self-refresh, PLLC, PLLM and PLLP reenabled, CPU running on PLLX,
> + * system clock running on the same PLL that it suspended at), and
> + * jumps to tegra_resume to restore virtual addressing.
> + * The physical address of tegra_resume expected to be stored in
> + * PMC_SCRATCH41.
> + *
> + * NOTE: THIS *MUST* BE RELOCATED TO TEGRA_IRAM_CODE_AREA AND MUST BE FIRST.

What does "AND MUST BE FIRST" mean?

> +ENTRY(tegra30_lp1_reset)
> +	/*
> +	 * The CPU and system bus are running at 32KHz and executing from
> +	 * IRAM when this code is executed; immediately switch to CLKM and
> +	 * enable PLLP, PLLM, PLLC, PLLA and PLLX.
> +	 */
> +	mov32	r0, TEGRA_CLK_RESET_BASE
> +
> +	mov	r1, #(1 << 28)

Some #defines for the various magic values used in this code would be
useful.

> +tegra30_sdram_pad_save:
> +	.word	0
> +	.word	0
> +	.word	0
> +	.word	0
> +	.word	0
> +	.word	0
> +	.word	0
> +	.word	0

This might be simpler, and easier to validate it's the right length:

        .rept   8
        .long   0
        .endr

> +tegra30_sdram_pad_address:
> +	.word	TEGRA_EMC_BASE + EMC_CFG				@0x0
> +	.word	TEGRA_EMC_BASE + EMC_ZCAL_INTERVAL			@0x4
> +	.word	TEGRA_EMC_BASE + EMC_AUTO_CAL_INTERVAL			@0x8
> +	.word	TEGRA_EMC_BASE + EMC_XM2VTTGENPADCTRL			@0xc
> +	.word	TEGRA_EMC_BASE + EMC_XM2VTTGENPADCTRL2			@0x10
> +	.word	TEGRA_PMC_BASE + PMC_IO_DPD_STATUS			@0x14
> +	.word	TEGRA_CLK_RESET_BASE + CLK_RESET_CLK_SOURCE_MSELECT	@0x18
> +	.word	TEGRA_CLK_RESET_BASE + CLK_RESET_SCLK_BURST		@0x1c
> +
> +tegra30_sdram_pad_size:
> +	.word	tegra30_sdram_pad_address - tegra30_sdram_pad_save

Perhaps if you swapp the order of declaring tegra30_sdram_pad_save and
tegra30_sdram_pad_address, you can even do something like:

	.rept (tegra30_sdram_pad_addr_end - tegra30_sdram_pad_addr) / 4

?

> +tegra30_switch_cpu_to_clk32k:
> +	/*
> +	 * start by jumping to CLKM to safely disable PLLs, then jump to

jumping sounds like a CPU program counter operation.
s/jumping/switching/ ?

> +	/* 2uS delay delay between changing SCLK and CCLK */
> +	wait_for_us r1, r7, r9
> +	add	r1, r1, #2
> +	wait_until r1, r7, r9

Ah, I see how wait_for_us is used now. Perhaps rename it
wait_for_us_boundary or wait_for_us_tick? Alternatively, perhaps
wait_until can just incorporate that logic itself?

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 7/8] ARM: tegra114: add LP1 suspend support
  2013-07-26  9:15     ` Joseph Lo
@ 2013-07-29 23:53         ` Stephen Warren
  -1 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-07-29 23:53 UTC (permalink / raw)
  To: Joseph Lo
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On 07/26/2013 03:15 AM, Joseph Lo wrote:
> The LP1 suspend mode will power off the CPU, clock gated the PLLs and put
> SDRAM to self-refresh mode. Any interrupt can wake up device from LP1. The
> sequence when LP1 suspending:

> diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
> index 5ae7ee5..36cadaa 100644
> --- a/arch/arm/mach-tegra/pm.c
> +++ b/arch/arm/mach-tegra/pm.c
> @@ -214,6 +214,7 @@ static bool tegra_lp1_iram_hook(void)
>  		tegra20_lp1_iram_hook();
>  		break;
>  	case TEGRA30:
> +	case TEGRA114:
>  		tegra30_lp1_iram_hook();

That's in pm-tegra30.c, which is only compiled in if
CONFIG_ARCH_TEGRA_3x_SOC is enabled. Similarly, this patch adds a lot of
code to sleep-tegra30.S, which also isn't built/linked if
!CONFIG_ARCH_TEGRA_3x_SOC.

Does this series built with all 7 combinations of Tegra20/30/114 support
enabled?

> diff --git a/arch/arm/mach-tegra/sleep-tegra30.S b/arch/arm/mach-tegra/sleep-tegra30.S

> @@ -96,9 +100,15 @@
>  	orreq	\rd, \rd, #(1 << 30)
>  	streq	\rd, [\r_car_base, #\pll_base]
>  	/* Enable lock detector */
> +	.if	\pll_misc
> +	ldr	\rd, [\r_car_base, #\pll_misc]
> +	bic	\rd, \rd, #(1 << 18)
> +	str	\rd, [\r_car_base, #\pll_misc]
> +	ldr	\rd, [\r_car_base, #\pll_misc]
>  	ldr	\rd, [\r_car_base, #\pll_misc]
>  	orr	\rd, \rd, #(1 << 18)
>  	str	\rd, [\r_car_base, #\pll_misc]
> +	.endif

Hmm. Those last 3 lines that aren't touched by this patch already
touched the pll_misc register even if !pll_misc. Was that a bug in a
previous patch?

Is git bisect maintained for both compile and run-time across this whole
patch series?

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 7/8] ARM: tegra114: add LP1 suspend support
@ 2013-07-29 23:53         ` Stephen Warren
  0 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-07-29 23:53 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/26/2013 03:15 AM, Joseph Lo wrote:
> The LP1 suspend mode will power off the CPU, clock gated the PLLs and put
> SDRAM to self-refresh mode. Any interrupt can wake up device from LP1. The
> sequence when LP1 suspending:

> diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
> index 5ae7ee5..36cadaa 100644
> --- a/arch/arm/mach-tegra/pm.c
> +++ b/arch/arm/mach-tegra/pm.c
> @@ -214,6 +214,7 @@ static bool tegra_lp1_iram_hook(void)
>  		tegra20_lp1_iram_hook();
>  		break;
>  	case TEGRA30:
> +	case TEGRA114:
>  		tegra30_lp1_iram_hook();

That's in pm-tegra30.c, which is only compiled in if
CONFIG_ARCH_TEGRA_3x_SOC is enabled. Similarly, this patch adds a lot of
code to sleep-tegra30.S, which also isn't built/linked if
!CONFIG_ARCH_TEGRA_3x_SOC.

Does this series built with all 7 combinations of Tegra20/30/114 support
enabled?

> diff --git a/arch/arm/mach-tegra/sleep-tegra30.S b/arch/arm/mach-tegra/sleep-tegra30.S

> @@ -96,9 +100,15 @@
>  	orreq	\rd, \rd, #(1 << 30)
>  	streq	\rd, [\r_car_base, #\pll_base]
>  	/* Enable lock detector */
> +	.if	\pll_misc
> +	ldr	\rd, [\r_car_base, #\pll_misc]
> +	bic	\rd, \rd, #(1 << 18)
> +	str	\rd, [\r_car_base, #\pll_misc]
> +	ldr	\rd, [\r_car_base, #\pll_misc]
>  	ldr	\rd, [\r_car_base, #\pll_misc]
>  	orr	\rd, \rd, #(1 << 18)
>  	str	\rd, [\r_car_base, #\pll_misc]
> +	.endif

Hmm. Those last 3 lines that aren't touched by this patch already
touched the pll_misc register even if !pll_misc. Was that a bug in a
previous patch?

Is git bisect maintained for both compile and run-time across this whole
patch series?

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 0/8] ARM: tegra: support LP1 suspend mode
  2013-07-27 16:12     ` Marc Dietrich
@ 2013-07-30  9:49       ` Joseph Lo
  -1 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-07-30  9:49 UTC (permalink / raw)
  To: Marc Dietrich
  Cc: Stephen Warren, linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Sun, 2013-07-28 at 00:12 +0800, Marc Dietrich wrote:
> On Friday 26 July 2013 17:15:02 Joseph Lo wrote:
> > This series adds the support of LP1 suspend mode for Tegra.
> > 
> > Verified on Seaboard, Cardhu and Dalmore.
> 
> tested on AC100 and worked fine, except
> 
[unbalanced wake_enable/disable of rtc-6586x]
> 
> and sdcard being offline after resume (fixed by CONFIG_MMC_UNSAFE_RESUME=y). 
> Anyway, thanks for your great work!
> 
> Tested-By: Marc Dietrich <marvin24-Mmb7MZpHnFY@public.gmane.org>
> 

Marc,

Thanks for your testing.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 0/8] ARM: tegra: support LP1 suspend mode
@ 2013-07-30  9:49       ` Joseph Lo
  0 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-07-30  9:49 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, 2013-07-28 at 00:12 +0800, Marc Dietrich wrote:
> On Friday 26 July 2013 17:15:02 Joseph Lo wrote:
> > This series adds the support of LP1 suspend mode for Tegra.
> > 
> > Verified on Seaboard, Cardhu and Dalmore.
> 
> tested on AC100 and worked fine, except
> 
[unbalanced wake_enable/disable of rtc-6586x]
> 
> and sdcard being offline after resume (fixed by CONFIG_MMC_UNSAFE_RESUME=y). 
> Anyway, thanks for your great work!
> 
> Tested-By: Marc Dietrich <marvin24@gmx.de>
> 

Marc,

Thanks for your testing.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 2/8] ARM: tegra: config the polarity of the request of sys clock
  2013-07-29 22:47         ` Stephen Warren
@ 2013-08-02  7:48             ` Joseph Lo
  -1 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-02  7:48 UTC (permalink / raw)
  To: Stephen Warren
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Tue, 2013-07-30 at 06:47 +0800, Stephen Warren wrote:
> On 07/26/2013 03:15 AM, Joseph Lo wrote:
> > When suspending to LP1 mode, the SYSCLK will be clock gated. And different
> > board may have different polarity of the request of SYSCLK, this patch
> > configure the polarity from the DT for the board.
> 
> > diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c
> 
> > @@ -238,6 +240,20 @@ void tegra_pmc_suspend_init(void)
> >  	reg = tegra_pmc_readl(PMC_CTRL);
> >  	reg |= TEGRA_POWER_CPU_PWRREQ_OE;
> >  	tegra_pmc_writel(reg, PMC_CTRL);
> > +
> > +	reg = tegra_pmc_readl(PMC_CTRL);
> > +
> > +	if (!pmc_pm_data.sysclkreq_high)
> > +		reg |= TEGRA_POWER_SYSCLK_POLARITY;
> > +	else
> > +		reg &= ~TEGRA_POWER_SYSCLK_POLARITY;
> > +
> > +	/* configure the output inverts while the request is tristated */
> > +	tegra_pmc_writel(reg, PMC_CTRL);
> 
> I think s/inverts/polarity/ in that comment would make a lot more sense.
> 
Yes, thanks.

> Must _OE be disabled for the code to work correctly? If so, should the
> code explicitly clear _OE first, since who knows what state it was
> originally in? Can't we just set the new polarity and enable _OE in a
> single register write?
> 
The SYSCLK is super clock that was connected to COP subsystem. It can't
be disabled when system is running. The boot loader had initialized it
and brought it to kernel. We follow the HW description in DT of the
polarity of SCLK to re-configure and re-init again. Then the PMC can
clock gate it when system go into suspend state.

And it can't be set it up by a single register write. It's a HW control
sequence. (Actually lots of PMC code have similar sequence.)

> > +	/* now enable the request */
> > +	reg |= TEGRA_POWER_SYSCLK_OE;
> > +	tegra_pmc_writel(reg, PMC_CTRL);
> 

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 2/8] ARM: tegra: config the polarity of the request of sys clock
@ 2013-08-02  7:48             ` Joseph Lo
  0 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-02  7:48 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 2013-07-30 at 06:47 +0800, Stephen Warren wrote:
> On 07/26/2013 03:15 AM, Joseph Lo wrote:
> > When suspending to LP1 mode, the SYSCLK will be clock gated. And different
> > board may have different polarity of the request of SYSCLK, this patch
> > configure the polarity from the DT for the board.
> 
> > diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c
> 
> > @@ -238,6 +240,20 @@ void tegra_pmc_suspend_init(void)
> >  	reg = tegra_pmc_readl(PMC_CTRL);
> >  	reg |= TEGRA_POWER_CPU_PWRREQ_OE;
> >  	tegra_pmc_writel(reg, PMC_CTRL);
> > +
> > +	reg = tegra_pmc_readl(PMC_CTRL);
> > +
> > +	if (!pmc_pm_data.sysclkreq_high)
> > +		reg |= TEGRA_POWER_SYSCLK_POLARITY;
> > +	else
> > +		reg &= ~TEGRA_POWER_SYSCLK_POLARITY;
> > +
> > +	/* configure the output inverts while the request is tristated */
> > +	tegra_pmc_writel(reg, PMC_CTRL);
> 
> I think s/inverts/polarity/ in that comment would make a lot more sense.
> 
Yes, thanks.

> Must _OE be disabled for the code to work correctly? If so, should the
> code explicitly clear _OE first, since who knows what state it was
> originally in? Can't we just set the new polarity and enable _OE in a
> single register write?
> 
The SYSCLK is super clock that was connected to COP subsystem. It can't
be disabled when system is running. The boot loader had initialized it
and brought it to kernel. We follow the HW description in DT of the
polarity of SCLK to re-configure and re-init again. Then the PMC can
clock gate it when system go into suspend state.

And it can't be set it up by a single register write. It's a HW control
sequence. (Actually lots of PMC code have similar sequence.)

> > +	/* now enable the request */
> > +	reg |= TEGRA_POWER_SYSCLK_OE;
> > +	tegra_pmc_writel(reg, PMC_CTRL);
> 

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
  2013-07-29 22:51         ` Stephen Warren
@ 2013-08-02  8:09             ` Joseph Lo
  -1 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-02  8:09 UTC (permalink / raw)
  To: Stephen Warren
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Mike Turquette

On Tue, 2013-07-30 at 06:51 +0800, Stephen Warren wrote:
> On 07/26/2013 03:15 AM, Joseph Lo wrote:
> > When the system suspends to LP1, the clock of the CPU would be switched to
> > CLK_M (12MHz Oscillator) during suspend/resume flow. The clock driver
> > needs to restore the clock of CPU after LP1 resume.
> 
> It's unclear to me how the code change implements "restore the clock of
> the CPU". A register name of CCLKG_BURST_POLICY doesn't sound like it's
> anything to do with enabled/disabling the CPU clock, nor configuring its
> rate. What exactly does this register do, and hence what does this new
> code actually restore?
> 
When system suspend to LP1, most of the PLLs was clock gated. Because we
didn't cut off the core power, the settings of PLL still keep there. But
we switch the clock source of CPU to CLK_M before shut off PLLs by
CCLKG_BURSY_POLICY register. So we need to resume it back to original
clock source by CCLKG_BURST_POLICY register. Or it would be keep in low
rate (CLK_M) after resume.

> Why don't Tegra20/30 need a similar change?
For Tegra20/30, the same code had been implemented in the suspend/resume
function of tegra_cpu_car_ops. It restores the CPU clock ASAP when CPU
resume from a suspend state to get quick performance I believe.

For Tegra114, the resume performance is cool (although we can't see it
in upstream kernel now, it still need some other functions.). We can
implement all the clock related suspend/resume function in the clock
driver.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
@ 2013-08-02  8:09             ` Joseph Lo
  0 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-02  8:09 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 2013-07-30 at 06:51 +0800, Stephen Warren wrote:
> On 07/26/2013 03:15 AM, Joseph Lo wrote:
> > When the system suspends to LP1, the clock of the CPU would be switched to
> > CLK_M (12MHz Oscillator) during suspend/resume flow. The clock driver
> > needs to restore the clock of CPU after LP1 resume.
> 
> It's unclear to me how the code change implements "restore the clock of
> the CPU". A register name of CCLKG_BURST_POLICY doesn't sound like it's
> anything to do with enabled/disabling the CPU clock, nor configuring its
> rate. What exactly does this register do, and hence what does this new
> code actually restore?
> 
When system suspend to LP1, most of the PLLs was clock gated. Because we
didn't cut off the core power, the settings of PLL still keep there. But
we switch the clock source of CPU to CLK_M before shut off PLLs by
CCLKG_BURSY_POLICY register. So we need to resume it back to original
clock source by CCLKG_BURST_POLICY register. Or it would be keep in low
rate (CLK_M) after resume.

> Why don't Tegra20/30 need a similar change?
For Tegra20/30, the same code had been implemented in the suspend/resume
function of tegra_cpu_car_ops. It restores the CPU clock ASAP when CPU
resume from a suspend state to get quick performance I believe.

For Tegra114, the resume performance is cool (although we can't see it
in upstream kernel now, it still need some other functions.). We can
implement all the clock related suspend/resume function in the clock
driver.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 4/8] ARM: tegra: add common LP1 suspend support
  2013-07-29 23:13         ` Stephen Warren
@ 2013-08-02  9:27             ` Joseph Lo
  -1 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-02  9:27 UTC (permalink / raw)
  To: Stephen Warren
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Tue, 2013-07-30 at 07:13 +0800, Stephen Warren wrote:
> On 07/26/2013 03:15 AM, Joseph Lo wrote:
> > The LP1 suspending mode on Tegra means CPU rail off, devices and PLLs are
> > clock gated and SDRAM in self-refresh mode. That means the low level LP1
> > suspending and resuming code couldn't be run on DRAM and the CPU must
> > switch to the always on clock domain (a.k.a. CLK_M 12MHz oscillator). And
> > the system clock (SCLK) would be switched to CLK_S, a 32KHz oscillator.
> > The LP1 low level handling code need to be moved to IRAM area first. And
> > marking the LP1 mask for indicating the Tegra device is in LP1. The CPU
> > power timer needs to be re-calculated based on 32KHz that was originally
> > based on PCLK.
> 
> > diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
> 
> >  #ifdef CONFIG_PM_SLEEP
> >  static DEFINE_SPINLOCK(tegra_lp2_lock);
> > +static void __iomem *iram_code = IO_ADDRESS(TEGRA_IRAM_CODE_AREA);
> > +static u32 iram_save_size;
> > +static void *iram_save_addr;
> > +struct tegra_lp1_iram *tegra_lp1_iram;
> >  void (*tegra_tear_down_cpu)(void);
> > +void (*tegra_sleep_core_finish)(unsigned long v2p);
> 
> I'm not sure all of those are required to be global variables. For
> example, iram_code is just a constant, so you could easily just use it
> directly in code.
> 
All of them does not mean the same thing. The LP1 resume code was built
in kernel image and store in RAM.
The tegra_lp1_iram hooks the LP1 resume code for different chips. Before
LP1 suspend, the original stuffs that in the area of IRAM would be store
in the iram_save_addr (RAM). Then copy the LP1 resume code to iram_code
area (IRAM).

> > +static bool tegra_sleep_core_init(void)
> > +{
> > +	if (!tegra_sleep_core_finish)
> > +		return false;
> > +
> > +	return true;
> > +}
> 
> That function seems a little pointless. Why not just check the value of
> tegra_sleep_core_finish directly in tegra_init_suspend()?
> 
I will add some code that wraps to different SoC later. We add some
common code for LP1 support here, but it didn't support by the chip yet.
Does that OK or should I move it to the next patch?

> > +static void tegra_suspend_enter_lp1(void)
> > +{
> > +	tegra_pmc_suspend();
> > +
> > +	/* copy the reset vector & SDRAM shutdown code into IRAM */
> > +	memcpy(iram_save_addr, iram_code, iram_save_size);
> > +	memcpy(iram_code, tegra_lp1_iram->start_addr, iram_save_size);
> > +
> > +	*((u32 *)tegra_cpu_lp1_mask) = 1;
> > +}
> > +
> > +static void tegra_suspend_exit_lp1(void)
> > +{
> > +	tegra_pmc_resume();
> > +
> > +	/* restore IRAM */
> > +	memcpy(iram_code, iram_save_addr, iram_save_size);
> > +
> > +	*(u32 *)tegra_cpu_lp1_mask = 0;
> > +}
> 
> I'm not really sure I like that, but I suppose it's OK. It sure seems
> like a performance limiter, but I suppose LP1 is so slow it doesn't
> matter, due to the need to ramp power rails, PLLs, SDRAM controller, etc.
> 
That's why we only back up the code size that exactly same with the LP1
resume code of the SoC.
> It'd be nice to simply reserve more IRAM for the kernel's use. Right
> now, only 1K is reserved, and presumably the code running on the AVP
> can't use the rest of that page anyway, or can it?
The LP1 resume code still running on the CPU (The LP0 would resume from
AVP).
> 
> > @@ -212,9 +282,15 @@ static int tegra_suspend_enter(suspend_state_t state)
> >  		break;
> >  	}
> >  
> > -	cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu);
> > +	if (mode == TEGRA_SUSPEND_LP2)
> > +		cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu);
> > +	else
> > +		cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_core);
> 
> Nit: It might be slightly simpler to simply calculate the parameter to
> cpu_suspend(), but then take the same code-path:
> 
> if (mode == TEGRA_SUSPEND_LP2)
> 	sleep_func = tegra_sleep_cpu;
> else
> 	sleep_func = tegra_sleep_core;
> cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, sleep_func);
> 
> That way, it's a little more obvious that the code always calls
> cpu_suspend() in the same way, but simply passes a different function to
> it to power things down at the end.
OK. Will do.

> 
> > diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c
> 
> >  void tegra_pmc_pm_set(enum tegra_suspend_mode mode)
> >  {
> >  	u32 reg, csr_reg;
> > -	unsigned long rate = 0;
> > +	unsigned long rate = 32768;
> 
> That's LP1-specific. You should add a "case TEGRA_SUSPEND_LP1:" to the
> switch statement that calculates rate instead; after all, that's the
> whole point of having that switch statement.
Will fix.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 4/8] ARM: tegra: add common LP1 suspend support
@ 2013-08-02  9:27             ` Joseph Lo
  0 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-02  9:27 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 2013-07-30 at 07:13 +0800, Stephen Warren wrote:
> On 07/26/2013 03:15 AM, Joseph Lo wrote:
> > The LP1 suspending mode on Tegra means CPU rail off, devices and PLLs are
> > clock gated and SDRAM in self-refresh mode. That means the low level LP1
> > suspending and resuming code couldn't be run on DRAM and the CPU must
> > switch to the always on clock domain (a.k.a. CLK_M 12MHz oscillator). And
> > the system clock (SCLK) would be switched to CLK_S, a 32KHz oscillator.
> > The LP1 low level handling code need to be moved to IRAM area first. And
> > marking the LP1 mask for indicating the Tegra device is in LP1. The CPU
> > power timer needs to be re-calculated based on 32KHz that was originally
> > based on PCLK.
> 
> > diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
> 
> >  #ifdef CONFIG_PM_SLEEP
> >  static DEFINE_SPINLOCK(tegra_lp2_lock);
> > +static void __iomem *iram_code = IO_ADDRESS(TEGRA_IRAM_CODE_AREA);
> > +static u32 iram_save_size;
> > +static void *iram_save_addr;
> > +struct tegra_lp1_iram *tegra_lp1_iram;
> >  void (*tegra_tear_down_cpu)(void);
> > +void (*tegra_sleep_core_finish)(unsigned long v2p);
> 
> I'm not sure all of those are required to be global variables. For
> example, iram_code is just a constant, so you could easily just use it
> directly in code.
> 
All of them does not mean the same thing. The LP1 resume code was built
in kernel image and store in RAM.
The tegra_lp1_iram hooks the LP1 resume code for different chips. Before
LP1 suspend, the original stuffs that in the area of IRAM would be store
in the iram_save_addr (RAM). Then copy the LP1 resume code to iram_code
area (IRAM).

> > +static bool tegra_sleep_core_init(void)
> > +{
> > +	if (!tegra_sleep_core_finish)
> > +		return false;
> > +
> > +	return true;
> > +}
> 
> That function seems a little pointless. Why not just check the value of
> tegra_sleep_core_finish directly in tegra_init_suspend()?
> 
I will add some code that wraps to different SoC later. We add some
common code for LP1 support here, but it didn't support by the chip yet.
Does that OK or should I move it to the next patch?

> > +static void tegra_suspend_enter_lp1(void)
> > +{
> > +	tegra_pmc_suspend();
> > +
> > +	/* copy the reset vector & SDRAM shutdown code into IRAM */
> > +	memcpy(iram_save_addr, iram_code, iram_save_size);
> > +	memcpy(iram_code, tegra_lp1_iram->start_addr, iram_save_size);
> > +
> > +	*((u32 *)tegra_cpu_lp1_mask) = 1;
> > +}
> > +
> > +static void tegra_suspend_exit_lp1(void)
> > +{
> > +	tegra_pmc_resume();
> > +
> > +	/* restore IRAM */
> > +	memcpy(iram_code, iram_save_addr, iram_save_size);
> > +
> > +	*(u32 *)tegra_cpu_lp1_mask = 0;
> > +}
> 
> I'm not really sure I like that, but I suppose it's OK. It sure seems
> like a performance limiter, but I suppose LP1 is so slow it doesn't
> matter, due to the need to ramp power rails, PLLs, SDRAM controller, etc.
> 
That's why we only back up the code size that exactly same with the LP1
resume code of the SoC.
> It'd be nice to simply reserve more IRAM for the kernel's use. Right
> now, only 1K is reserved, and presumably the code running on the AVP
> can't use the rest of that page anyway, or can it?
The LP1 resume code still running on the CPU (The LP0 would resume from
AVP).
> 
> > @@ -212,9 +282,15 @@ static int tegra_suspend_enter(suspend_state_t state)
> >  		break;
> >  	}
> >  
> > -	cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu);
> > +	if (mode == TEGRA_SUSPEND_LP2)
> > +		cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu);
> > +	else
> > +		cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_core);
> 
> Nit: It might be slightly simpler to simply calculate the parameter to
> cpu_suspend(), but then take the same code-path:
> 
> if (mode == TEGRA_SUSPEND_LP2)
> 	sleep_func = tegra_sleep_cpu;
> else
> 	sleep_func = tegra_sleep_core;
> cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, sleep_func);
> 
> That way, it's a little more obvious that the code always calls
> cpu_suspend() in the same way, but simply passes a different function to
> it to power things down at the end.
OK. Will do.

> 
> > diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c
> 
> >  void tegra_pmc_pm_set(enum tegra_suspend_mode mode)
> >  {
> >  	u32 reg, csr_reg;
> > -	unsigned long rate = 0;
> > +	unsigned long rate = 32768;
> 
> That's LP1-specific. You should add a "case TEGRA_SUSPEND_LP1:" to the
> switch statement that calculates rate instead; after all, that's the
> whole point of having that switch statement.
Will fix.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 2/8] ARM: tegra: config the polarity of the request of sys clock
  2013-08-02  7:48             ` Joseph Lo
@ 2013-08-02 20:24                 ` Stephen Warren
  -1 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-08-02 20:24 UTC (permalink / raw)
  To: Joseph Lo
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On 08/02/2013 01:48 AM, Joseph Lo wrote:
> On Tue, 2013-07-30 at 06:47 +0800, Stephen Warren wrote:
>> On 07/26/2013 03:15 AM, Joseph Lo wrote:
>>> When suspending to LP1 mode, the SYSCLK will be clock gated. And different
>>> board may have different polarity of the request of SYSCLK, this patch
>>> configure the polarity from the DT for the board.
>>
>>> diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c
>>
>>> @@ -238,6 +240,20 @@ void tegra_pmc_suspend_init(void)
>>>  	reg = tegra_pmc_readl(PMC_CTRL);
>>>  	reg |= TEGRA_POWER_CPU_PWRREQ_OE;
>>>  	tegra_pmc_writel(reg, PMC_CTRL);
>>> +
>>> +	reg = tegra_pmc_readl(PMC_CTRL);
>>> +
>>> +	if (!pmc_pm_data.sysclkreq_high)
>>> +		reg |= TEGRA_POWER_SYSCLK_POLARITY;
>>> +	else
>>> +		reg &= ~TEGRA_POWER_SYSCLK_POLARITY;
>>> +
>>> +	/* configure the output inverts while the request is tristated */
>>> +	tegra_pmc_writel(reg, PMC_CTRL);
>>
>> I think s/inverts/polarity/ in that comment would make a lot more sense.
>>
> Yes, thanks.
> 
>> Must _OE be disabled for the code to work correctly? If so, should the
>> code explicitly clear _OE first, since who knows what state it was
>> originally in? Can't we just set the new polarity and enable _OE in a
>> single register write?
>>
> The SYSCLK is super clock that was connected to COP subsystem. It can't
> be disabled when system is running. The boot loader had initialized it
> and brought it to kernel. We follow the HW description in DT of the
> polarity of SCLK to re-configure and re-init again. Then the PMC can
> clock gate it when system go into suspend state.

So it sounds like the bootloader has already configured the clock to a
certain polarity. If so, why do we need to reconfigure it again?

Or, is this code not duplicating something the bootloader must have
done, but simply informing some HW in the PMC that's receiving SYSCLK
how the clock is configured elsewhere?

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 2/8] ARM: tegra: config the polarity of the request of sys clock
@ 2013-08-02 20:24                 ` Stephen Warren
  0 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-08-02 20:24 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/02/2013 01:48 AM, Joseph Lo wrote:
> On Tue, 2013-07-30 at 06:47 +0800, Stephen Warren wrote:
>> On 07/26/2013 03:15 AM, Joseph Lo wrote:
>>> When suspending to LP1 mode, the SYSCLK will be clock gated. And different
>>> board may have different polarity of the request of SYSCLK, this patch
>>> configure the polarity from the DT for the board.
>>
>>> diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c
>>
>>> @@ -238,6 +240,20 @@ void tegra_pmc_suspend_init(void)
>>>  	reg = tegra_pmc_readl(PMC_CTRL);
>>>  	reg |= TEGRA_POWER_CPU_PWRREQ_OE;
>>>  	tegra_pmc_writel(reg, PMC_CTRL);
>>> +
>>> +	reg = tegra_pmc_readl(PMC_CTRL);
>>> +
>>> +	if (!pmc_pm_data.sysclkreq_high)
>>> +		reg |= TEGRA_POWER_SYSCLK_POLARITY;
>>> +	else
>>> +		reg &= ~TEGRA_POWER_SYSCLK_POLARITY;
>>> +
>>> +	/* configure the output inverts while the request is tristated */
>>> +	tegra_pmc_writel(reg, PMC_CTRL);
>>
>> I think s/inverts/polarity/ in that comment would make a lot more sense.
>>
> Yes, thanks.
> 
>> Must _OE be disabled for the code to work correctly? If so, should the
>> code explicitly clear _OE first, since who knows what state it was
>> originally in? Can't we just set the new polarity and enable _OE in a
>> single register write?
>>
> The SYSCLK is super clock that was connected to COP subsystem. It can't
> be disabled when system is running. The boot loader had initialized it
> and brought it to kernel. We follow the HW description in DT of the
> polarity of SCLK to re-configure and re-init again. Then the PMC can
> clock gate it when system go into suspend state.

So it sounds like the bootloader has already configured the clock to a
certain polarity. If so, why do we need to reconfigure it again?

Or, is this code not duplicating something the bootloader must have
done, but simply informing some HW in the PMC that's receiving SYSCLK
how the clock is configured elsewhere?

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
  2013-08-02  8:09             ` Joseph Lo
@ 2013-08-02 20:32                 ` Stephen Warren
  -1 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-08-02 20:32 UTC (permalink / raw)
  To: Joseph Lo
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Mike Turquette

On 08/02/2013 02:09 AM, Joseph Lo wrote:
> On Tue, 2013-07-30 at 06:51 +0800, Stephen Warren wrote:
>> On 07/26/2013 03:15 AM, Joseph Lo wrote:
>>> When the system suspends to LP1, the clock of the CPU would be switched to
>>> CLK_M (12MHz Oscillator) during suspend/resume flow. The clock driver
>>> needs to restore the clock of CPU after LP1 resume.
>>
>> It's unclear to me how the code change implements "restore the clock of
>> the CPU". A register name of CCLKG_BURST_POLICY doesn't sound like it's
>> anything to do with enabled/disabling the CPU clock, nor configuring its
>> rate. What exactly does this register do, and hence what does this new
>> code actually restore?
>>
> When system suspend to LP1, most of the PLLs was clock gated. Because we
> didn't cut off the core power, the settings of PLL still keep there. But
> we switch the clock source of CPU to CLK_M before shut off PLLs by
> CCLKG_BURSY_POLICY register. So we need to resume it back to original
> clock source by CCLKG_BURST_POLICY register. Or it would be keep in low
> rate (CLK_M) after resume.

OK, I guess the register name was badly chosen by HW. I'd like to see
part of your description above in the patch description. How about
replacing the patch description with:

----------
When the system suspends to LP1, the CPU clock source is switched to
CLK_M (12MHz Oscillator) during suspend/resume flow[1]. The CPU clock
source is controlled by the CCLKG_BURST_POLICY register, and hence this
register must be restored during LP1 resume.
----------

[1] Question: where does this happen? This patch doesn't make that
change. I wonder why the suspend path can't save this register, rather
than implementing a separate suspend syscore op in the clock driver.
Does the HW auto-switch the value in the register itself?

>> Why don't Tegra20/30 need a similar change?
>
> For Tegra20/30, the same code had been implemented in the suspend/resume
> function of tegra_cpu_car_ops. It restores the CPU clock ASAP when CPU
> resume from a suspend state to get quick performance I believe.
> 
> For Tegra114, the resume performance is cool (although we can't see it
> in upstream kernel now, it still need some other functions.). We can
> implement all the clock related suspend/resume function in the clock
> driver.

OK, I do see something similar in tegra20/30_cpu_clock_suspend/resume.
Why can't this new code be part of the equivalent functions; does the
Tegra114 suspend/resume code in mach-tegra/ not call
tegra_cpu_car_ops.suspend/resume() in the same way it does on Tegra20/30?

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
@ 2013-08-02 20:32                 ` Stephen Warren
  0 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-08-02 20:32 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/02/2013 02:09 AM, Joseph Lo wrote:
> On Tue, 2013-07-30 at 06:51 +0800, Stephen Warren wrote:
>> On 07/26/2013 03:15 AM, Joseph Lo wrote:
>>> When the system suspends to LP1, the clock of the CPU would be switched to
>>> CLK_M (12MHz Oscillator) during suspend/resume flow. The clock driver
>>> needs to restore the clock of CPU after LP1 resume.
>>
>> It's unclear to me how the code change implements "restore the clock of
>> the CPU". A register name of CCLKG_BURST_POLICY doesn't sound like it's
>> anything to do with enabled/disabling the CPU clock, nor configuring its
>> rate. What exactly does this register do, and hence what does this new
>> code actually restore?
>>
> When system suspend to LP1, most of the PLLs was clock gated. Because we
> didn't cut off the core power, the settings of PLL still keep there. But
> we switch the clock source of CPU to CLK_M before shut off PLLs by
> CCLKG_BURSY_POLICY register. So we need to resume it back to original
> clock source by CCLKG_BURST_POLICY register. Or it would be keep in low
> rate (CLK_M) after resume.

OK, I guess the register name was badly chosen by HW. I'd like to see
part of your description above in the patch description. How about
replacing the patch description with:

----------
When the system suspends to LP1, the CPU clock source is switched to
CLK_M (12MHz Oscillator) during suspend/resume flow[1]. The CPU clock
source is controlled by the CCLKG_BURST_POLICY register, and hence this
register must be restored during LP1 resume.
----------

[1] Question: where does this happen? This patch doesn't make that
change. I wonder why the suspend path can't save this register, rather
than implementing a separate suspend syscore op in the clock driver.
Does the HW auto-switch the value in the register itself?

>> Why don't Tegra20/30 need a similar change?
>
> For Tegra20/30, the same code had been implemented in the suspend/resume
> function of tegra_cpu_car_ops. It restores the CPU clock ASAP when CPU
> resume from a suspend state to get quick performance I believe.
> 
> For Tegra114, the resume performance is cool (although we can't see it
> in upstream kernel now, it still need some other functions.). We can
> implement all the clock related suspend/resume function in the clock
> driver.

OK, I do see something similar in tegra20/30_cpu_clock_suspend/resume.
Why can't this new code be part of the equivalent functions; does the
Tegra114 suspend/resume code in mach-tegra/ not call
tegra_cpu_car_ops.suspend/resume() in the same way it does on Tegra20/30?

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 4/8] ARM: tegra: add common LP1 suspend support
  2013-08-02  9:27             ` Joseph Lo
@ 2013-08-02 20:40                 ` Stephen Warren
  -1 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-08-02 20:40 UTC (permalink / raw)
  To: Joseph Lo
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On 08/02/2013 03:27 AM, Joseph Lo wrote:
> On Tue, 2013-07-30 at 07:13 +0800, Stephen Warren wrote:
>> On 07/26/2013 03:15 AM, Joseph Lo wrote:
>>> The LP1 suspending mode on Tegra means CPU rail off, devices and PLLs are
>>> clock gated and SDRAM in self-refresh mode. That means the low level LP1
>>> suspending and resuming code couldn't be run on DRAM and the CPU must
>>> switch to the always on clock domain (a.k.a. CLK_M 12MHz oscillator). And
>>> the system clock (SCLK) would be switched to CLK_S, a 32KHz oscillator.
>>> The LP1 low level handling code need to be moved to IRAM area first. And
>>> marking the LP1 mask for indicating the Tegra device is in LP1. The CPU
>>> power timer needs to be re-calculated based on 32KHz that was originally
>>> based on PCLK.
>>
>>> diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
>>
>>>  #ifdef CONFIG_PM_SLEEP
>>>  static DEFINE_SPINLOCK(tegra_lp2_lock);
>>> +static void __iomem *iram_code = IO_ADDRESS(TEGRA_IRAM_CODE_AREA);
>>> +static u32 iram_save_size;
>>> +static void *iram_save_addr;
>>> +struct tegra_lp1_iram *tegra_lp1_iram;
>>>  void (*tegra_tear_down_cpu)(void);
>>> +void (*tegra_sleep_core_finish)(unsigned long v2p);
>>
>> I'm not sure all of those are required to be global variables. For
>> example, iram_code is just a constant, so you could easily just use it
>> directly in code.
>
> All of them does not mean the same thing. The LP1 resume code was built
> in kernel image and store in RAM.
> The tegra_lp1_iram hooks the LP1 resume code for different chips. Before
> LP1 suspend, the original stuffs that in the area of IRAM would be store
> in the iram_save_addr (RAM). Then copy the LP1 resume code to iram_code
> area (IRAM).

Sure, some of those variable may differ based on the SoC at runtime etc.

But at least the value of iram_code never changes, right?

>>> +static bool tegra_sleep_core_init(void)
>>> +{
>>> +	if (!tegra_sleep_core_finish)
>>> +		return false;
>>> +
>>> +	return true;
>>> +}
>>
>> That function seems a little pointless. Why not just check the value of
>> tegra_sleep_core_finish directly in tegra_init_suspend()?
>
> I will add some code that wraps to different SoC later. We add some
> common code for LP1 support here, but it didn't support by the chip yet.
> Does that OK or should I move it to the next patch?

OK, I see this is enhanced immediately in the next patch, so it's fine.

>>> +static void tegra_suspend_enter_lp1(void)
>>> +{
>>> +	tegra_pmc_suspend();
>>> +
>>> +	/* copy the reset vector & SDRAM shutdown code into IRAM */
>>> +	memcpy(iram_save_addr, iram_code, iram_save_size);
>>> +	memcpy(iram_code, tegra_lp1_iram->start_addr, iram_save_size);
>>> +
>>> +	*((u32 *)tegra_cpu_lp1_mask) = 1;
>>> +}
>>> +
>>> +static void tegra_suspend_exit_lp1(void)
>>> +{
>>> +	tegra_pmc_resume();
>>> +
>>> +	/* restore IRAM */
>>> +	memcpy(iram_code, iram_save_addr, iram_save_size);
>>> +
>>> +	*(u32 *)tegra_cpu_lp1_mask = 0;
>>> +}
>>
>> I'm not really sure I like that, but I suppose it's OK. It sure seems
>> like a performance limiter, but I suppose LP1 is so slow it doesn't
>> matter, due to the need to ramp power rails, PLLs, SDRAM controller, etc.
>
> That's why we only back up the code size that exactly same with the LP1
> resume code of the SoC.
>
>> It'd be nice to simply reserve more IRAM for the kernel's use. Right
>> now, only 1K is reserved, and presumably the code running on the AVP
>> can't use the rest of that page anyway, or can it?
>
> The LP1 resume code still running on the CPU (The LP0 would resume from
> AVP).

Sure. However, if the AVP never touched the IRAM region where the LP1
resume code is placed, we would only need to copy the LP1 resume code to
IRAM once at kernel boot time, rather than every time we enter/leave LP1.

I guess the idea is that once we have an AVP driver, we will force the
AVP to suspend first, save the IRAM that it might have been using, do
the system suspend/resume, then restore the IRAM. And that changing that
sequence so that the AVP never ever touched the LP1 IRAM area would
require AVP firmware changes that we can't make?

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 4/8] ARM: tegra: add common LP1 suspend support
@ 2013-08-02 20:40                 ` Stephen Warren
  0 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-08-02 20:40 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/02/2013 03:27 AM, Joseph Lo wrote:
> On Tue, 2013-07-30 at 07:13 +0800, Stephen Warren wrote:
>> On 07/26/2013 03:15 AM, Joseph Lo wrote:
>>> The LP1 suspending mode on Tegra means CPU rail off, devices and PLLs are
>>> clock gated and SDRAM in self-refresh mode. That means the low level LP1
>>> suspending and resuming code couldn't be run on DRAM and the CPU must
>>> switch to the always on clock domain (a.k.a. CLK_M 12MHz oscillator). And
>>> the system clock (SCLK) would be switched to CLK_S, a 32KHz oscillator.
>>> The LP1 low level handling code need to be moved to IRAM area first. And
>>> marking the LP1 mask for indicating the Tegra device is in LP1. The CPU
>>> power timer needs to be re-calculated based on 32KHz that was originally
>>> based on PCLK.
>>
>>> diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
>>
>>>  #ifdef CONFIG_PM_SLEEP
>>>  static DEFINE_SPINLOCK(tegra_lp2_lock);
>>> +static void __iomem *iram_code = IO_ADDRESS(TEGRA_IRAM_CODE_AREA);
>>> +static u32 iram_save_size;
>>> +static void *iram_save_addr;
>>> +struct tegra_lp1_iram *tegra_lp1_iram;
>>>  void (*tegra_tear_down_cpu)(void);
>>> +void (*tegra_sleep_core_finish)(unsigned long v2p);
>>
>> I'm not sure all of those are required to be global variables. For
>> example, iram_code is just a constant, so you could easily just use it
>> directly in code.
>
> All of them does not mean the same thing. The LP1 resume code was built
> in kernel image and store in RAM.
> The tegra_lp1_iram hooks the LP1 resume code for different chips. Before
> LP1 suspend, the original stuffs that in the area of IRAM would be store
> in the iram_save_addr (RAM). Then copy the LP1 resume code to iram_code
> area (IRAM).

Sure, some of those variable may differ based on the SoC at runtime etc.

But at least the value of iram_code never changes, right?

>>> +static bool tegra_sleep_core_init(void)
>>> +{
>>> +	if (!tegra_sleep_core_finish)
>>> +		return false;
>>> +
>>> +	return true;
>>> +}
>>
>> That function seems a little pointless. Why not just check the value of
>> tegra_sleep_core_finish directly in tegra_init_suspend()?
>
> I will add some code that wraps to different SoC later. We add some
> common code for LP1 support here, but it didn't support by the chip yet.
> Does that OK or should I move it to the next patch?

OK, I see this is enhanced immediately in the next patch, so it's fine.

>>> +static void tegra_suspend_enter_lp1(void)
>>> +{
>>> +	tegra_pmc_suspend();
>>> +
>>> +	/* copy the reset vector & SDRAM shutdown code into IRAM */
>>> +	memcpy(iram_save_addr, iram_code, iram_save_size);
>>> +	memcpy(iram_code, tegra_lp1_iram->start_addr, iram_save_size);
>>> +
>>> +	*((u32 *)tegra_cpu_lp1_mask) = 1;
>>> +}
>>> +
>>> +static void tegra_suspend_exit_lp1(void)
>>> +{
>>> +	tegra_pmc_resume();
>>> +
>>> +	/* restore IRAM */
>>> +	memcpy(iram_code, iram_save_addr, iram_save_size);
>>> +
>>> +	*(u32 *)tegra_cpu_lp1_mask = 0;
>>> +}
>>
>> I'm not really sure I like that, but I suppose it's OK. It sure seems
>> like a performance limiter, but I suppose LP1 is so slow it doesn't
>> matter, due to the need to ramp power rails, PLLs, SDRAM controller, etc.
>
> That's why we only back up the code size that exactly same with the LP1
> resume code of the SoC.
>
>> It'd be nice to simply reserve more IRAM for the kernel's use. Right
>> now, only 1K is reserved, and presumably the code running on the AVP
>> can't use the rest of that page anyway, or can it?
>
> The LP1 resume code still running on the CPU (The LP0 would resume from
> AVP).

Sure. However, if the AVP never touched the IRAM region where the LP1
resume code is placed, we would only need to copy the LP1 resume code to
IRAM once at kernel boot time, rather than every time we enter/leave LP1.

I guess the idea is that once we have an AVP driver, we will force the
AVP to suspend first, save the IRAM that it might have been using, do
the system suspend/resume, then restore the IRAM. And that changing that
sequence so that the AVP never ever touched the LP1 IRAM area would
require AVP firmware changes that we can't make?

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 5/8] ARM: tegra30: add LP1 suspend support
  2013-07-29 23:45         ` Stephen Warren
@ 2013-08-05  6:46             ` Joseph Lo
  -1 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-05  6:46 UTC (permalink / raw)
  To: Stephen Warren
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Tue, 2013-07-30 at 07:45 +0800, Stephen Warren wrote:
> On 07/26/2013 03:15 AM, Joseph Lo wrote:
> > The LP1 suspend mode will power off the CPU, clock gated the PLLs and put
> > SDRAM to self-refresh mode. Any interrupt can wake up device from LP1. The
> > sequence when LP1 suspending:
> 
> > diff --git a/arch/arm/mach-tegra/pm-tegra30.c b/arch/arm/mach-tegra/pm-tegra30.c
> 
> > +void tegra30_lp1_iram_hook(void)
> > +{
> > +	tegra30_lp1_iram.start_addr = &tegra30_iram_start;
> > +	tegra30_lp1_iram.end_addr = &tegra30_iram_end;
> 
> If you need to fill in the values in that struct dynamically anyway, why
> not make tegra_lp1_iram be a struct rather than a pointer, and write
> directly to it?
> 
OK. Will fix.

> > +/*
> > + * tegra30_lp1_reset
> > + *
> > + * reset vector for LP1 restore; copied into IRAM during suspend.
> > + * Brings the system back up to a safe staring point (SDRAM out of
> > + * self-refresh, PLLC, PLLM and PLLP reenabled, CPU running on PLLX,
> > + * system clock running on the same PLL that it suspended at), and
> > + * jumps to tegra_resume to restore virtual addressing.
> > + * The physical address of tegra_resume expected to be stored in
> > + * PMC_SCRATCH41.
> > + *
> > + * NOTE: THIS *MUST* BE RELOCATED TO TEGRA_IRAM_CODE_AREA AND MUST BE FIRST.
> 
> What does "AND MUST BE FIRST" mean?
> 
It means the LP1 reset vector needs to be copied to IRAM_CODE_AREA first
before suspend to LP1. Looks no need this. Will remove it.

> > +ENTRY(tegra30_lp1_reset)
> > +	/*
> > +	 * The CPU and system bus are running at 32KHz and executing from
> > +	 * IRAM when this code is executed; immediately switch to CLKM and
> > +	 * enable PLLP, PLLM, PLLC, PLLA and PLLX.
> > +	 */
> > +	mov32	r0, TEGRA_CLK_RESET_BASE
> > +
> > +	mov	r1, #(1 << 28)
> 
> Some #defines for the various magic values used in this code would be
> useful.
It may still cause some confuse if I add a #defines value for it.
Because it includes a clock policy and the clock source (the value maybe
just 0). I add some comments for these codes about what they are doing.
> 
> > +tegra30_sdram_pad_save:
> > +	.word	0
> > +	.word	0
> > +	.word	0
> > +	.word	0
> > +	.word	0
> > +	.word	0
> > +	.word	0
> > +	.word	0
> 
> This might be simpler, and easier to validate it's the right length:
> 
>         .rept   8
>         .long   0
>         .endr
> 
> > +tegra30_sdram_pad_address:
> > +	.word	TEGRA_EMC_BASE + EMC_CFG				@0x0
> > +	.word	TEGRA_EMC_BASE + EMC_ZCAL_INTERVAL			@0x4
> > +	.word	TEGRA_EMC_BASE + EMC_AUTO_CAL_INTERVAL			@0x8
> > +	.word	TEGRA_EMC_BASE + EMC_XM2VTTGENPADCTRL			@0xc
> > +	.word	TEGRA_EMC_BASE + EMC_XM2VTTGENPADCTRL2			@0x10
> > +	.word	TEGRA_PMC_BASE + PMC_IO_DPD_STATUS			@0x14
> > +	.word	TEGRA_CLK_RESET_BASE + CLK_RESET_CLK_SOURCE_MSELECT	@0x18
> > +	.word	TEGRA_CLK_RESET_BASE + CLK_RESET_SCLK_BURST		@0x1c
> > +
> > +tegra30_sdram_pad_size:
> > +	.word	tegra30_sdram_pad_address - tegra30_sdram_pad_save
> 
> Perhaps if you swapp the order of declaring tegra30_sdram_pad_save and
> tegra30_sdram_pad_address, you can even do something like:
> 
> 	.rept (tegra30_sdram_pad_addr_end - tegra30_sdram_pad_addr) / 4
> 
> ?
OK. Looks better. Will do this.

> 
> > +	/* 2uS delay delay between changing SCLK and CCLK */
> > +	wait_for_us r1, r7, r9
> > +	add	r1, r1, #2
> > +	wait_until r1, r7, r9
> 
> Ah, I see how wait_for_us is used now. Perhaps rename it
> wait_for_us_boundary or wait_for_us_tick? Alternatively, perhaps
> wait_until can just incorporate that logic itself?
Indeed. Will fix.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 5/8] ARM: tegra30: add LP1 suspend support
@ 2013-08-05  6:46             ` Joseph Lo
  0 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-05  6:46 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 2013-07-30 at 07:45 +0800, Stephen Warren wrote:
> On 07/26/2013 03:15 AM, Joseph Lo wrote:
> > The LP1 suspend mode will power off the CPU, clock gated the PLLs and put
> > SDRAM to self-refresh mode. Any interrupt can wake up device from LP1. The
> > sequence when LP1 suspending:
> 
> > diff --git a/arch/arm/mach-tegra/pm-tegra30.c b/arch/arm/mach-tegra/pm-tegra30.c
> 
> > +void tegra30_lp1_iram_hook(void)
> > +{
> > +	tegra30_lp1_iram.start_addr = &tegra30_iram_start;
> > +	tegra30_lp1_iram.end_addr = &tegra30_iram_end;
> 
> If you need to fill in the values in that struct dynamically anyway, why
> not make tegra_lp1_iram be a struct rather than a pointer, and write
> directly to it?
> 
OK. Will fix.

> > +/*
> > + * tegra30_lp1_reset
> > + *
> > + * reset vector for LP1 restore; copied into IRAM during suspend.
> > + * Brings the system back up to a safe staring point (SDRAM out of
> > + * self-refresh, PLLC, PLLM and PLLP reenabled, CPU running on PLLX,
> > + * system clock running on the same PLL that it suspended at), and
> > + * jumps to tegra_resume to restore virtual addressing.
> > + * The physical address of tegra_resume expected to be stored in
> > + * PMC_SCRATCH41.
> > + *
> > + * NOTE: THIS *MUST* BE RELOCATED TO TEGRA_IRAM_CODE_AREA AND MUST BE FIRST.
> 
> What does "AND MUST BE FIRST" mean?
> 
It means the LP1 reset vector needs to be copied to IRAM_CODE_AREA first
before suspend to LP1. Looks no need this. Will remove it.

> > +ENTRY(tegra30_lp1_reset)
> > +	/*
> > +	 * The CPU and system bus are running at 32KHz and executing from
> > +	 * IRAM when this code is executed; immediately switch to CLKM and
> > +	 * enable PLLP, PLLM, PLLC, PLLA and PLLX.
> > +	 */
> > +	mov32	r0, TEGRA_CLK_RESET_BASE
> > +
> > +	mov	r1, #(1 << 28)
> 
> Some #defines for the various magic values used in this code would be
> useful.
It may still cause some confuse if I add a #defines value for it.
Because it includes a clock policy and the clock source (the value maybe
just 0). I add some comments for these codes about what they are doing.
> 
> > +tegra30_sdram_pad_save:
> > +	.word	0
> > +	.word	0
> > +	.word	0
> > +	.word	0
> > +	.word	0
> > +	.word	0
> > +	.word	0
> > +	.word	0
> 
> This might be simpler, and easier to validate it's the right length:
> 
>         .rept   8
>         .long   0
>         .endr
> 
> > +tegra30_sdram_pad_address:
> > +	.word	TEGRA_EMC_BASE + EMC_CFG				@0x0
> > +	.word	TEGRA_EMC_BASE + EMC_ZCAL_INTERVAL			@0x4
> > +	.word	TEGRA_EMC_BASE + EMC_AUTO_CAL_INTERVAL			@0x8
> > +	.word	TEGRA_EMC_BASE + EMC_XM2VTTGENPADCTRL			@0xc
> > +	.word	TEGRA_EMC_BASE + EMC_XM2VTTGENPADCTRL2			@0x10
> > +	.word	TEGRA_PMC_BASE + PMC_IO_DPD_STATUS			@0x14
> > +	.word	TEGRA_CLK_RESET_BASE + CLK_RESET_CLK_SOURCE_MSELECT	@0x18
> > +	.word	TEGRA_CLK_RESET_BASE + CLK_RESET_SCLK_BURST		@0x1c
> > +
> > +tegra30_sdram_pad_size:
> > +	.word	tegra30_sdram_pad_address - tegra30_sdram_pad_save
> 
> Perhaps if you swapp the order of declaring tegra30_sdram_pad_save and
> tegra30_sdram_pad_address, you can even do something like:
> 
> 	.rept (tegra30_sdram_pad_addr_end - tegra30_sdram_pad_addr) / 4
> 
> ?
OK. Looks better. Will do this.

> 
> > +	/* 2uS delay delay between changing SCLK and CCLK */
> > +	wait_for_us r1, r7, r9
> > +	add	r1, r1, #2
> > +	wait_until r1, r7, r9
> 
> Ah, I see how wait_for_us is used now. Perhaps rename it
> wait_for_us_boundary or wait_for_us_tick? Alternatively, perhaps
> wait_until can just incorporate that logic itself?
Indeed. Will fix.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 7/8] ARM: tegra114: add LP1 suspend support
  2013-07-29 23:53         ` Stephen Warren
@ 2013-08-05  6:51             ` Joseph Lo
  -1 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-05  6:51 UTC (permalink / raw)
  To: Stephen Warren
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Tue, 2013-07-30 at 07:53 +0800, Stephen Warren wrote:
> On 07/26/2013 03:15 AM, Joseph Lo wrote:
> > The LP1 suspend mode will power off the CPU, clock gated the PLLs and put
> > SDRAM to self-refresh mode. Any interrupt can wake up device from LP1. The
> > sequence when LP1 suspending:
> 
> > diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
> > index 5ae7ee5..36cadaa 100644
> > --- a/arch/arm/mach-tegra/pm.c
> > +++ b/arch/arm/mach-tegra/pm.c
> > @@ -214,6 +214,7 @@ static bool tegra_lp1_iram_hook(void)
> >  		tegra20_lp1_iram_hook();
> >  		break;
> >  	case TEGRA30:
> > +	case TEGRA114:
> >  		tegra30_lp1_iram_hook();
> 
> That's in pm-tegra30.c, which is only compiled in if
> CONFIG_ARCH_TEGRA_3x_SOC is enabled. Similarly, this patch adds a lot of
> code to sleep-tegra30.S, which also isn't built/linked if
> !CONFIG_ARCH_TEGRA_3x_SOC.
> 
The "pm-tegra30.c" would be build when just CONFIG_ARCH_TEGRA_3x_SOC,
CONFIG_ARCH_TEGRA_114_SOC or both.

> Does this series built with all 7 combinations of Tegra20/30/114 support
> enabled?
Yes, I did some test about this. But not include all 7 combination.
> 
> > diff --git a/arch/arm/mach-tegra/sleep-tegra30.S b/arch/arm/mach-tegra/sleep-tegra30.S
> 
> > @@ -96,9 +100,15 @@
> >  	orreq	\rd, \rd, #(1 << 30)
> >  	streq	\rd, [\r_car_base, #\pll_base]
> >  	/* Enable lock detector */
> > +	.if	\pll_misc
> > +	ldr	\rd, [\r_car_base, #\pll_misc]
> > +	bic	\rd, \rd, #(1 << 18)
> > +	str	\rd, [\r_car_base, #\pll_misc]
> > +	ldr	\rd, [\r_car_base, #\pll_misc]
> >  	ldr	\rd, [\r_car_base, #\pll_misc]
> >  	orr	\rd, \rd, #(1 << 18)
> >  	str	\rd, [\r_car_base, #\pll_misc]
> > +	.endif
> 
> Hmm. Those last 3 lines that aren't touched by this patch already
> touched the pll_misc register even if !pll_misc. Was that a bug in a
> previous patch?
Oops. Yes, will fix in next version.
> 
> Is git bisect maintained for both compile and run-time across this whole
> patch series?
Yes, it's OK. Will do it again before V2 sent.
> --
> To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 7/8] ARM: tegra114: add LP1 suspend support
@ 2013-08-05  6:51             ` Joseph Lo
  0 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-05  6:51 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 2013-07-30 at 07:53 +0800, Stephen Warren wrote:
> On 07/26/2013 03:15 AM, Joseph Lo wrote:
> > The LP1 suspend mode will power off the CPU, clock gated the PLLs and put
> > SDRAM to self-refresh mode. Any interrupt can wake up device from LP1. The
> > sequence when LP1 suspending:
> 
> > diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
> > index 5ae7ee5..36cadaa 100644
> > --- a/arch/arm/mach-tegra/pm.c
> > +++ b/arch/arm/mach-tegra/pm.c
> > @@ -214,6 +214,7 @@ static bool tegra_lp1_iram_hook(void)
> >  		tegra20_lp1_iram_hook();
> >  		break;
> >  	case TEGRA30:
> > +	case TEGRA114:
> >  		tegra30_lp1_iram_hook();
> 
> That's in pm-tegra30.c, which is only compiled in if
> CONFIG_ARCH_TEGRA_3x_SOC is enabled. Similarly, this patch adds a lot of
> code to sleep-tegra30.S, which also isn't built/linked if
> !CONFIG_ARCH_TEGRA_3x_SOC.
> 
The "pm-tegra30.c" would be build when just CONFIG_ARCH_TEGRA_3x_SOC,
CONFIG_ARCH_TEGRA_114_SOC or both.

> Does this series built with all 7 combinations of Tegra20/30/114 support
> enabled?
Yes, I did some test about this. But not include all 7 combination.
> 
> > diff --git a/arch/arm/mach-tegra/sleep-tegra30.S b/arch/arm/mach-tegra/sleep-tegra30.S
> 
> > @@ -96,9 +100,15 @@
> >  	orreq	\rd, \rd, #(1 << 30)
> >  	streq	\rd, [\r_car_base, #\pll_base]
> >  	/* Enable lock detector */
> > +	.if	\pll_misc
> > +	ldr	\rd, [\r_car_base, #\pll_misc]
> > +	bic	\rd, \rd, #(1 << 18)
> > +	str	\rd, [\r_car_base, #\pll_misc]
> > +	ldr	\rd, [\r_car_base, #\pll_misc]
> >  	ldr	\rd, [\r_car_base, #\pll_misc]
> >  	orr	\rd, \rd, #(1 << 18)
> >  	str	\rd, [\r_car_base, #\pll_misc]
> > +	.endif
> 
> Hmm. Those last 3 lines that aren't touched by this patch already
> touched the pll_misc register even if !pll_misc. Was that a bug in a
> previous patch?
Oops. Yes, will fix in next version.
> 
> Is git bisect maintained for both compile and run-time across this whole
> patch series?
Yes, it's OK. Will do it again before V2 sent.
> --
> To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
  2013-08-02 20:32                 ` Stephen Warren
@ 2013-08-05  8:02                     ` Joseph Lo
  -1 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-05  8:02 UTC (permalink / raw)
  To: Stephen Warren
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Mike Turquette

On Sat, 2013-08-03 at 04:32 +0800, Stephen Warren wrote:
> On 08/02/2013 02:09 AM, Joseph Lo wrote:
> > On Tue, 2013-07-30 at 06:51 +0800, Stephen Warren wrote:
> >> On 07/26/2013 03:15 AM, Joseph Lo wrote:
> >>> When the system suspends to LP1, the clock of the CPU would be switched to
> >>> CLK_M (12MHz Oscillator) during suspend/resume flow. The clock driver
> >>> needs to restore the clock of CPU after LP1 resume.
> >>
> >> It's unclear to me how the code change implements "restore the clock of
> >> the CPU". A register name of CCLKG_BURST_POLICY doesn't sound like it's
> >> anything to do with enabled/disabling the CPU clock, nor configuring its
> >> rate. What exactly does this register do, and hence what does this new
> >> code actually restore?
> >>
> > When system suspend to LP1, most of the PLLs was clock gated. Because we
> > didn't cut off the core power, the settings of PLL still keep there. But
> > we switch the clock source of CPU to CLK_M before shut off PLLs by
> > CCLKG_BURSY_POLICY register. So we need to resume it back to original
> > clock source by CCLKG_BURST_POLICY register. Or it would be keep in low
> > rate (CLK_M) after resume.
> 
> OK, I guess the register name was badly chosen by HW. I'd like to see
> part of your description above in the patch description. How about
> replacing the patch description with:
> 
> ----------
> When the system suspends to LP1, the CPU clock source is switched to
> CLK_M (12MHz Oscillator) during suspend/resume flow[1]. The CPU clock
> source is controlled by the CCLKG_BURST_POLICY register, and hence this
> register must be restored during LP1 resume.
> ----------
> 
> [1] Question: where does this happen? This patch doesn't make that
> change. I wonder why the suspend path can't save this register, rather
> than implementing a separate suspend syscore op in the clock driver.
> Does the HW auto-switch the value in the register itself?
> 
If we switch CPU to CLK_M in clock driver, the system will become slowly
during the middle of suspending flow. We do this at the very end of the
LP1 suspending flow before the CPU disable all the PLL clocks.

> >> Why don't Tegra20/30 need a similar change?
> >
> > For Tegra20/30, the same code had been implemented in the suspend/resume
> > function of tegra_cpu_car_ops. It restores the CPU clock ASAP when CPU
> > resume from a suspend state to get quick performance I believe.
> > 
> > For Tegra114, the resume performance is cool (although we can't see it
> > in upstream kernel now, it still need some other functions.). We can
> > implement all the clock related suspend/resume function in the clock
> > driver.
> 
> OK, I do see something similar in tegra20/30_cpu_clock_suspend/resume.
> Why can't this new code be part of the equivalent functions; does the
> Tegra114 suspend/resume code in mach-tegra/ not call
> tegra_cpu_car_ops.suspend/resume() in the same way it does on Tegra20/30?
One of the main reasons is due to DFLL clock. The CPU clock of Tegra114
is going to switch to DFLL to a get higher clock rate. But it depends
some other HW (i.e. I2C), we can't resume it so early when the CPU just
resume like we did in tegra_cpu_car_ops.suspend/resume for Tegra20/30.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
@ 2013-08-05  8:02                     ` Joseph Lo
  0 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-05  8:02 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, 2013-08-03 at 04:32 +0800, Stephen Warren wrote:
> On 08/02/2013 02:09 AM, Joseph Lo wrote:
> > On Tue, 2013-07-30 at 06:51 +0800, Stephen Warren wrote:
> >> On 07/26/2013 03:15 AM, Joseph Lo wrote:
> >>> When the system suspends to LP1, the clock of the CPU would be switched to
> >>> CLK_M (12MHz Oscillator) during suspend/resume flow. The clock driver
> >>> needs to restore the clock of CPU after LP1 resume.
> >>
> >> It's unclear to me how the code change implements "restore the clock of
> >> the CPU". A register name of CCLKG_BURST_POLICY doesn't sound like it's
> >> anything to do with enabled/disabling the CPU clock, nor configuring its
> >> rate. What exactly does this register do, and hence what does this new
> >> code actually restore?
> >>
> > When system suspend to LP1, most of the PLLs was clock gated. Because we
> > didn't cut off the core power, the settings of PLL still keep there. But
> > we switch the clock source of CPU to CLK_M before shut off PLLs by
> > CCLKG_BURSY_POLICY register. So we need to resume it back to original
> > clock source by CCLKG_BURST_POLICY register. Or it would be keep in low
> > rate (CLK_M) after resume.
> 
> OK, I guess the register name was badly chosen by HW. I'd like to see
> part of your description above in the patch description. How about
> replacing the patch description with:
> 
> ----------
> When the system suspends to LP1, the CPU clock source is switched to
> CLK_M (12MHz Oscillator) during suspend/resume flow[1]. The CPU clock
> source is controlled by the CCLKG_BURST_POLICY register, and hence this
> register must be restored during LP1 resume.
> ----------
> 
> [1] Question: where does this happen? This patch doesn't make that
> change. I wonder why the suspend path can't save this register, rather
> than implementing a separate suspend syscore op in the clock driver.
> Does the HW auto-switch the value in the register itself?
> 
If we switch CPU to CLK_M in clock driver, the system will become slowly
during the middle of suspending flow. We do this at the very end of the
LP1 suspending flow before the CPU disable all the PLL clocks.

> >> Why don't Tegra20/30 need a similar change?
> >
> > For Tegra20/30, the same code had been implemented in the suspend/resume
> > function of tegra_cpu_car_ops. It restores the CPU clock ASAP when CPU
> > resume from a suspend state to get quick performance I believe.
> > 
> > For Tegra114, the resume performance is cool (although we can't see it
> > in upstream kernel now, it still need some other functions.). We can
> > implement all the clock related suspend/resume function in the clock
> > driver.
> 
> OK, I do see something similar in tegra20/30_cpu_clock_suspend/resume.
> Why can't this new code be part of the equivalent functions; does the
> Tegra114 suspend/resume code in mach-tegra/ not call
> tegra_cpu_car_ops.suspend/resume() in the same way it does on Tegra20/30?
One of the main reasons is due to DFLL clock. The CPU clock of Tegra114
is going to switch to DFLL to a get higher clock rate. But it depends
some other HW (i.e. I2C), we can't resume it so early when the CPU just
resume like we did in tegra_cpu_car_ops.suspend/resume for Tegra20/30.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 4/8] ARM: tegra: add common LP1 suspend support
  2013-08-02 20:40                 ` Stephen Warren
@ 2013-08-05  8:07                   ` Joseph Lo
  -1 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-05  8:07 UTC (permalink / raw)
  To: Stephen Warren; +Cc: linux-tegra, linux-arm-kernel

On Sat, 2013-08-03 at 04:40 +0800, Stephen Warren wrote:
> On 08/02/2013 03:27 AM, Joseph Lo wrote:
> > On Tue, 2013-07-30 at 07:13 +0800, Stephen Warren wrote:
> >> On 07/26/2013 03:15 AM, Joseph Lo wrote:
> >>> The LP1 suspending mode on Tegra means CPU rail off, devices and PLLs are
> >>> clock gated and SDRAM in self-refresh mode. That means the low level LP1
> >>> suspending and resuming code couldn't be run on DRAM and the CPU must
> >>> switch to the always on clock domain (a.k.a. CLK_M 12MHz oscillator). And
> >>> the system clock (SCLK) would be switched to CLK_S, a 32KHz oscillator.
> >>> The LP1 low level handling code need to be moved to IRAM area first. And
> >>> marking the LP1 mask for indicating the Tegra device is in LP1. The CPU
> >>> power timer needs to be re-calculated based on 32KHz that was originally
> >>> based on PCLK.
> >>
> >>> diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
> >>
> >>>  #ifdef CONFIG_PM_SLEEP
> >>>  static DEFINE_SPINLOCK(tegra_lp2_lock);
> >>> +static void __iomem *iram_code = IO_ADDRESS(TEGRA_IRAM_CODE_AREA);
> >>> +static u32 iram_save_size;
> >>> +static void *iram_save_addr;
> >>> +struct tegra_lp1_iram *tegra_lp1_iram;
> >>>  void (*tegra_tear_down_cpu)(void);
> >>> +void (*tegra_sleep_core_finish)(unsigned long v2p);
> >>
> >> I'm not sure all of those are required to be global variables. For
> >> example, iram_code is just a constant, so you could easily just use it
> >> directly in code.
> >
> > All of them does not mean the same thing. The LP1 resume code was built
> > in kernel image and store in RAM.
> > The tegra_lp1_iram hooks the LP1 resume code for different chips. Before
> > LP1 suspend, the original stuffs that in the area of IRAM would be store
> > in the iram_save_addr (RAM). Then copy the LP1 resume code to iram_code
> > area (IRAM).
> 
> Sure, some of those variable may differ based on the SoC at runtime etc.
> 
> But at least the value of iram_code never changes, right?
Yes.

> >>> +static void tegra_suspend_enter_lp1(void)
> >>> +{
> >>> +	tegra_pmc_suspend();
> >>> +
> >>> +	/* copy the reset vector & SDRAM shutdown code into IRAM */
> >>> +	memcpy(iram_save_addr, iram_code, iram_save_size);
> >>> +	memcpy(iram_code, tegra_lp1_iram->start_addr, iram_save_size);
> >>> +
> >>> +	*((u32 *)tegra_cpu_lp1_mask) = 1;
> >>> +}
> >>> +
> >>> +static void tegra_suspend_exit_lp1(void)
> >>> +{
> >>> +	tegra_pmc_resume();
> >>> +
> >>> +	/* restore IRAM */
> >>> +	memcpy(iram_code, iram_save_addr, iram_save_size);
> >>> +
> >>> +	*(u32 *)tegra_cpu_lp1_mask = 0;
> >>> +}
> >>
> >> I'm not really sure I like that, but I suppose it's OK. It sure seems
> >> like a performance limiter, but I suppose LP1 is so slow it doesn't
> >> matter, due to the need to ramp power rails, PLLs, SDRAM controller, etc.
> >
> > That's why we only back up the code size that exactly same with the LP1
> > resume code of the SoC.
> >
> >> It'd be nice to simply reserve more IRAM for the kernel's use. Right
> >> now, only 1K is reserved, and presumably the code running on the AVP
> >> can't use the rest of that page anyway, or can it?
> >
> > The LP1 resume code still running on the CPU (The LP0 would resume from
> > AVP).
> 
> Sure. However, if the AVP never touched the IRAM region where the LP1
> resume code is placed, we would only need to copy the LP1 resume code to
> IRAM once at kernel boot time, rather than every time we enter/leave LP1.
> 
> I guess the idea is that once we have an AVP driver, we will force the
> AVP to suspend first, save the IRAM that it might have been using, do
> the system suspend/resume, then restore the IRAM. And that changing that
> sequence so that the AVP never ever touched the LP1 IRAM area would
> require AVP firmware changes that we can't make?
Yes, exactly.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 4/8] ARM: tegra: add common LP1 suspend support
@ 2013-08-05  8:07                   ` Joseph Lo
  0 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-05  8:07 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, 2013-08-03 at 04:40 +0800, Stephen Warren wrote:
> On 08/02/2013 03:27 AM, Joseph Lo wrote:
> > On Tue, 2013-07-30 at 07:13 +0800, Stephen Warren wrote:
> >> On 07/26/2013 03:15 AM, Joseph Lo wrote:
> >>> The LP1 suspending mode on Tegra means CPU rail off, devices and PLLs are
> >>> clock gated and SDRAM in self-refresh mode. That means the low level LP1
> >>> suspending and resuming code couldn't be run on DRAM and the CPU must
> >>> switch to the always on clock domain (a.k.a. CLK_M 12MHz oscillator). And
> >>> the system clock (SCLK) would be switched to CLK_S, a 32KHz oscillator.
> >>> The LP1 low level handling code need to be moved to IRAM area first. And
> >>> marking the LP1 mask for indicating the Tegra device is in LP1. The CPU
> >>> power timer needs to be re-calculated based on 32KHz that was originally
> >>> based on PCLK.
> >>
> >>> diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
> >>
> >>>  #ifdef CONFIG_PM_SLEEP
> >>>  static DEFINE_SPINLOCK(tegra_lp2_lock);
> >>> +static void __iomem *iram_code = IO_ADDRESS(TEGRA_IRAM_CODE_AREA);
> >>> +static u32 iram_save_size;
> >>> +static void *iram_save_addr;
> >>> +struct tegra_lp1_iram *tegra_lp1_iram;
> >>>  void (*tegra_tear_down_cpu)(void);
> >>> +void (*tegra_sleep_core_finish)(unsigned long v2p);
> >>
> >> I'm not sure all of those are required to be global variables. For
> >> example, iram_code is just a constant, so you could easily just use it
> >> directly in code.
> >
> > All of them does not mean the same thing. The LP1 resume code was built
> > in kernel image and store in RAM.
> > The tegra_lp1_iram hooks the LP1 resume code for different chips. Before
> > LP1 suspend, the original stuffs that in the area of IRAM would be store
> > in the iram_save_addr (RAM). Then copy the LP1 resume code to iram_code
> > area (IRAM).
> 
> Sure, some of those variable may differ based on the SoC at runtime etc.
> 
> But at least the value of iram_code never changes, right?
Yes.

> >>> +static void tegra_suspend_enter_lp1(void)
> >>> +{
> >>> +	tegra_pmc_suspend();
> >>> +
> >>> +	/* copy the reset vector & SDRAM shutdown code into IRAM */
> >>> +	memcpy(iram_save_addr, iram_code, iram_save_size);
> >>> +	memcpy(iram_code, tegra_lp1_iram->start_addr, iram_save_size);
> >>> +
> >>> +	*((u32 *)tegra_cpu_lp1_mask) = 1;
> >>> +}
> >>> +
> >>> +static void tegra_suspend_exit_lp1(void)
> >>> +{
> >>> +	tegra_pmc_resume();
> >>> +
> >>> +	/* restore IRAM */
> >>> +	memcpy(iram_code, iram_save_addr, iram_save_size);
> >>> +
> >>> +	*(u32 *)tegra_cpu_lp1_mask = 0;
> >>> +}
> >>
> >> I'm not really sure I like that, but I suppose it's OK. It sure seems
> >> like a performance limiter, but I suppose LP1 is so slow it doesn't
> >> matter, due to the need to ramp power rails, PLLs, SDRAM controller, etc.
> >
> > That's why we only back up the code size that exactly same with the LP1
> > resume code of the SoC.
> >
> >> It'd be nice to simply reserve more IRAM for the kernel's use. Right
> >> now, only 1K is reserved, and presumably the code running on the AVP
> >> can't use the rest of that page anyway, or can it?
> >
> > The LP1 resume code still running on the CPU (The LP0 would resume from
> > AVP).
> 
> Sure. However, if the AVP never touched the IRAM region where the LP1
> resume code is placed, we would only need to copy the LP1 resume code to
> IRAM once at kernel boot time, rather than every time we enter/leave LP1.
> 
> I guess the idea is that once we have an AVP driver, we will force the
> AVP to suspend first, save the IRAM that it might have been using, do
> the system suspend/resume, then restore the IRAM. And that changing that
> sequence so that the AVP never ever touched the LP1 IRAM area would
> require AVP firmware changes that we can't make?
Yes, exactly.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 2/8] ARM: tegra: config the polarity of the request of sys clock
  2013-08-02 20:24                 ` Stephen Warren
@ 2013-08-05  8:42                     ` Joseph Lo
  -1 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-05  8:42 UTC (permalink / raw)
  To: Stephen Warren
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Sat, 2013-08-03 at 04:24 +0800, Stephen Warren wrote:
> On 08/02/2013 01:48 AM, Joseph Lo wrote:
> > On Tue, 2013-07-30 at 06:47 +0800, Stephen Warren wrote:
> >> On 07/26/2013 03:15 AM, Joseph Lo wrote:
> >>> When suspending to LP1 mode, the SYSCLK will be clock gated. And different
> >>> board may have different polarity of the request of SYSCLK, this patch
> >>> configure the polarity from the DT for the board.
> >>
> >>> diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c
> >>
> >>> @@ -238,6 +240,20 @@ void tegra_pmc_suspend_init(void)
> >>>  	reg = tegra_pmc_readl(PMC_CTRL);
> >>>  	reg |= TEGRA_POWER_CPU_PWRREQ_OE;
> >>>  	tegra_pmc_writel(reg, PMC_CTRL);
> >>> +
> >>> +	reg = tegra_pmc_readl(PMC_CTRL);
> >>> +
> >>> +	if (!pmc_pm_data.sysclkreq_high)
> >>> +		reg |= TEGRA_POWER_SYSCLK_POLARITY;
> >>> +	else
> >>> +		reg &= ~TEGRA_POWER_SYSCLK_POLARITY;
> >>> +
> >>> +	/* configure the output inverts while the request is tristated */
> >>> +	tegra_pmc_writel(reg, PMC_CTRL);
> >>
> >> I think s/inverts/polarity/ in that comment would make a lot more sense.
> >>
> > Yes, thanks.
> > 
> >> Must _OE be disabled for the code to work correctly? If so, should the
> >> code explicitly clear _OE first, since who knows what state it was
> >> originally in? Can't we just set the new polarity and enable _OE in a
> >> single register write?
> >>
> > The SYSCLK is super clock that was connected to COP subsystem. It can't
> > be disabled when system is running. The boot loader had initialized it
> > and brought it to kernel. We follow the HW description in DT of the
> > polarity of SCLK to re-configure and re-init again. Then the PMC can
> > clock gate it when system go into suspend state.
> 
> So it sounds like the bootloader has already configured the clock to a
> certain polarity. If so, why do we need to reconfigure it again?
> 
> Or, is this code not duplicating something the bootloader must have
> done, but simply informing some HW in the PMC that's receiving SYSCLK
> how the clock is configured elsewhere?

I can't always guarantee the bootloader does the stuffs that kernel
needs. For this reason, it's better to do what the kernel needs in the
kernel.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 2/8] ARM: tegra: config the polarity of the request of sys clock
@ 2013-08-05  8:42                     ` Joseph Lo
  0 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-05  8:42 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, 2013-08-03 at 04:24 +0800, Stephen Warren wrote:
> On 08/02/2013 01:48 AM, Joseph Lo wrote:
> > On Tue, 2013-07-30 at 06:47 +0800, Stephen Warren wrote:
> >> On 07/26/2013 03:15 AM, Joseph Lo wrote:
> >>> When suspending to LP1 mode, the SYSCLK will be clock gated. And different
> >>> board may have different polarity of the request of SYSCLK, this patch
> >>> configure the polarity from the DT for the board.
> >>
> >>> diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c
> >>
> >>> @@ -238,6 +240,20 @@ void tegra_pmc_suspend_init(void)
> >>>  	reg = tegra_pmc_readl(PMC_CTRL);
> >>>  	reg |= TEGRA_POWER_CPU_PWRREQ_OE;
> >>>  	tegra_pmc_writel(reg, PMC_CTRL);
> >>> +
> >>> +	reg = tegra_pmc_readl(PMC_CTRL);
> >>> +
> >>> +	if (!pmc_pm_data.sysclkreq_high)
> >>> +		reg |= TEGRA_POWER_SYSCLK_POLARITY;
> >>> +	else
> >>> +		reg &= ~TEGRA_POWER_SYSCLK_POLARITY;
> >>> +
> >>> +	/* configure the output inverts while the request is tristated */
> >>> +	tegra_pmc_writel(reg, PMC_CTRL);
> >>
> >> I think s/inverts/polarity/ in that comment would make a lot more sense.
> >>
> > Yes, thanks.
> > 
> >> Must _OE be disabled for the code to work correctly? If so, should the
> >> code explicitly clear _OE first, since who knows what state it was
> >> originally in? Can't we just set the new polarity and enable _OE in a
> >> single register write?
> >>
> > The SYSCLK is super clock that was connected to COP subsystem. It can't
> > be disabled when system is running. The boot loader had initialized it
> > and brought it to kernel. We follow the HW description in DT of the
> > polarity of SCLK to re-configure and re-init again. Then the PMC can
> > clock gate it when system go into suspend state.
> 
> So it sounds like the bootloader has already configured the clock to a
> certain polarity. If so, why do we need to reconfigure it again?
> 
> Or, is this code not duplicating something the bootloader must have
> done, but simply informing some HW in the PMC that's receiving SYSCLK
> how the clock is configured elsewhere?

I can't always guarantee the bootloader does the stuffs that kernel
needs. For this reason, it's better to do what the kernel needs in the
kernel.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
  2013-08-05  8:02                     ` Joseph Lo
@ 2013-08-05 17:00                         ` Stephen Warren
  -1 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-08-05 17:00 UTC (permalink / raw)
  To: Joseph Lo
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Mike Turquette

On 08/05/2013 02:02 AM, Joseph Lo wrote:
> On Sat, 2013-08-03 at 04:32 +0800, Stephen Warren wrote:
>> On 08/02/2013 02:09 AM, Joseph Lo wrote:
>>> On Tue, 2013-07-30 at 06:51 +0800, Stephen Warren wrote:
>>>> On 07/26/2013 03:15 AM, Joseph Lo wrote:
>>>>> When the system suspends to LP1, the clock of the CPU would be switched to
>>>>> CLK_M (12MHz Oscillator) during suspend/resume flow. The clock driver
>>>>> needs to restore the clock of CPU after LP1 resume.
>>>>
>>>> It's unclear to me how the code change implements "restore the clock of
>>>> the CPU". A register name of CCLKG_BURST_POLICY doesn't sound like it's
>>>> anything to do with enabled/disabling the CPU clock, nor configuring its
>>>> rate. What exactly does this register do, and hence what does this new
>>>> code actually restore?
>>>>
>>> When system suspend to LP1, most of the PLLs was clock gated. Because we
>>> didn't cut off the core power, the settings of PLL still keep there. But
>>> we switch the clock source of CPU to CLK_M before shut off PLLs by
>>> CCLKG_BURSY_POLICY register. So we need to resume it back to original
>>> clock source by CCLKG_BURST_POLICY register. Or it would be keep in low
>>> rate (CLK_M) after resume.
>>
>> OK, I guess the register name was badly chosen by HW. I'd like to see
>> part of your description above in the patch description. How about
>> replacing the patch description with:
>>
>> ----------
>> When the system suspends to LP1, the CPU clock source is switched to
>> CLK_M (12MHz Oscillator) during suspend/resume flow[1]. The CPU clock
>> source is controlled by the CCLKG_BURST_POLICY register, and hence this
>> register must be restored during LP1 resume.
>> ----------
>>
>> [1] Question: where does this happen? This patch doesn't make that
>> change. I wonder why the suspend path can't save this register, rather
>> than implementing a separate suspend syscore op in the clock driver.
>> Does the HW auto-switch the value in the register itself?
>
> If we switch CPU to CLK_M in clock driver, the system will become slowly
> during the middle of suspending flow. We do this at the very end of the
> LP1 suspending flow before the CPU disable all the PLL clocks.

I think you answered "why is the switch to CLK_M performed very late",
whereas I asked "where is the code that performs the switch to CLK_M".

>>>> Why don't Tegra20/30 need a similar change?
>>>
>>> For Tegra20/30, the same code had been implemented in the suspend/resume
>>> function of tegra_cpu_car_ops. It restores the CPU clock ASAP when CPU
>>> resume from a suspend state to get quick performance I believe.
>>>
>>> For Tegra114, the resume performance is cool (although we can't see it
>>> in upstream kernel now, it still need some other functions.). We can
>>> implement all the clock related suspend/resume function in the clock
>>> driver.
>>
>> OK, I do see something similar in tegra20/30_cpu_clock_suspend/resume.
>> Why can't this new code be part of the equivalent functions; does the
>> Tegra114 suspend/resume code in mach-tegra/ not call
>> tegra_cpu_car_ops.suspend/resume() in the same way it does on Tegra20/30?
>
> One of the main reasons is due to DFLL clock. The CPU clock of Tegra114
> is going to switch to DFLL to a get higher clock rate. But it depends
> some other HW (i.e. I2C), we can't resume it so early when the CPU just
> resume like we did in tegra_cpu_car_ops.suspend/resume for Tegra20/30.

Is there a guarantee that the syscore_ops are run after the call to
tegar_cpu_car_ops.resume() would be? Perhaps the mach-tegra/ code should
simply call tegra_cpu_car_ops.resume() later on Tegra114 than it does on
earlier chips, so it's run at a suitable time?

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
@ 2013-08-05 17:00                         ` Stephen Warren
  0 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-08-05 17:00 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/05/2013 02:02 AM, Joseph Lo wrote:
> On Sat, 2013-08-03 at 04:32 +0800, Stephen Warren wrote:
>> On 08/02/2013 02:09 AM, Joseph Lo wrote:
>>> On Tue, 2013-07-30 at 06:51 +0800, Stephen Warren wrote:
>>>> On 07/26/2013 03:15 AM, Joseph Lo wrote:
>>>>> When the system suspends to LP1, the clock of the CPU would be switched to
>>>>> CLK_M (12MHz Oscillator) during suspend/resume flow. The clock driver
>>>>> needs to restore the clock of CPU after LP1 resume.
>>>>
>>>> It's unclear to me how the code change implements "restore the clock of
>>>> the CPU". A register name of CCLKG_BURST_POLICY doesn't sound like it's
>>>> anything to do with enabled/disabling the CPU clock, nor configuring its
>>>> rate. What exactly does this register do, and hence what does this new
>>>> code actually restore?
>>>>
>>> When system suspend to LP1, most of the PLLs was clock gated. Because we
>>> didn't cut off the core power, the settings of PLL still keep there. But
>>> we switch the clock source of CPU to CLK_M before shut off PLLs by
>>> CCLKG_BURSY_POLICY register. So we need to resume it back to original
>>> clock source by CCLKG_BURST_POLICY register. Or it would be keep in low
>>> rate (CLK_M) after resume.
>>
>> OK, I guess the register name was badly chosen by HW. I'd like to see
>> part of your description above in the patch description. How about
>> replacing the patch description with:
>>
>> ----------
>> When the system suspends to LP1, the CPU clock source is switched to
>> CLK_M (12MHz Oscillator) during suspend/resume flow[1]. The CPU clock
>> source is controlled by the CCLKG_BURST_POLICY register, and hence this
>> register must be restored during LP1 resume.
>> ----------
>>
>> [1] Question: where does this happen? This patch doesn't make that
>> change. I wonder why the suspend path can't save this register, rather
>> than implementing a separate suspend syscore op in the clock driver.
>> Does the HW auto-switch the value in the register itself?
>
> If we switch CPU to CLK_M in clock driver, the system will become slowly
> during the middle of suspending flow. We do this at the very end of the
> LP1 suspending flow before the CPU disable all the PLL clocks.

I think you answered "why is the switch to CLK_M performed very late",
whereas I asked "where is the code that performs the switch to CLK_M".

>>>> Why don't Tegra20/30 need a similar change?
>>>
>>> For Tegra20/30, the same code had been implemented in the suspend/resume
>>> function of tegra_cpu_car_ops. It restores the CPU clock ASAP when CPU
>>> resume from a suspend state to get quick performance I believe.
>>>
>>> For Tegra114, the resume performance is cool (although we can't see it
>>> in upstream kernel now, it still need some other functions.). We can
>>> implement all the clock related suspend/resume function in the clock
>>> driver.
>>
>> OK, I do see something similar in tegra20/30_cpu_clock_suspend/resume.
>> Why can't this new code be part of the equivalent functions; does the
>> Tegra114 suspend/resume code in mach-tegra/ not call
>> tegra_cpu_car_ops.suspend/resume() in the same way it does on Tegra20/30?
>
> One of the main reasons is due to DFLL clock. The CPU clock of Tegra114
> is going to switch to DFLL to a get higher clock rate. But it depends
> some other HW (i.e. I2C), we can't resume it so early when the CPU just
> resume like we did in tegra_cpu_car_ops.suspend/resume for Tegra20/30.

Is there a guarantee that the syscore_ops are run after the call to
tegar_cpu_car_ops.resume() would be? Perhaps the mach-tegra/ code should
simply call tegra_cpu_car_ops.resume() later on Tegra114 than it does on
earlier chips, so it's run at a suitable time?

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
  2013-08-05 17:00                         ` Stephen Warren
@ 2013-08-05 17:39                             ` Stephen Warren
  -1 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-08-05 17:39 UTC (permalink / raw)
  To: Joseph Lo
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Mike Turquette

On 08/05/2013 11:00 AM, Stephen Warren wrote:
> On 08/05/2013 02:02 AM, Joseph Lo wrote:
>> On Sat, 2013-08-03 at 04:32 +0800, Stephen Warren wrote:
>>> On 08/02/2013 02:09 AM, Joseph Lo wrote:
>>>> On Tue, 2013-07-30 at 06:51 +0800, Stephen Warren wrote:
>>>>> On 07/26/2013 03:15 AM, Joseph Lo wrote:
>>>>>> When the system suspends to LP1, the clock of the CPU would be switched to
>>>>>> CLK_M (12MHz Oscillator) during suspend/resume flow. The clock driver
>>>>>> needs to restore the clock of CPU after LP1 resume.
>>>>>
>>>>> It's unclear to me how the code change implements "restore the clock of
>>>>> the CPU". A register name of CCLKG_BURST_POLICY doesn't sound like it's
>>>>> anything to do with enabled/disabling the CPU clock, nor configuring its
>>>>> rate. What exactly does this register do, and hence what does this new
>>>>> code actually restore?
>>>>>
>>>> When system suspend to LP1, most of the PLLs was clock gated. Because we
>>>> didn't cut off the core power, the settings of PLL still keep there. But
>>>> we switch the clock source of CPU to CLK_M before shut off PLLs by
>>>> CCLKG_BURSY_POLICY register. So we need to resume it back to original
>>>> clock source by CCLKG_BURST_POLICY register. Or it would be keep in low
>>>> rate (CLK_M) after resume.
>>>
>>> OK, I guess the register name was badly chosen by HW. I'd like to see
>>> part of your description above in the patch description. How about
>>> replacing the patch description with:
>>>
>>> ----------
>>> When the system suspends to LP1, the CPU clock source is switched to
>>> CLK_M (12MHz Oscillator) during suspend/resume flow[1]. The CPU clock
>>> source is controlled by the CCLKG_BURST_POLICY register, and hence this
>>> register must be restored during LP1 resume.
>>> ----------
>>>
>>> [1] Question: where does this happen? This patch doesn't make that
>>> change. I wonder why the suspend path can't save this register, rather
>>> than implementing a separate suspend syscore op in the clock driver.
>>> Does the HW auto-switch the value in the register itself?
>>
>> If we switch CPU to CLK_M in clock driver, the system will become slowly
>> during the middle of suspending flow. We do this at the very end of the
>> LP1 suspending flow before the CPU disable all the PLL clocks.
> 
> I think you answered "why is the switch to CLK_M performed very late",
> whereas I asked "where is the code that performs the switch to CLK_M".

To expand on this a bit more, I can't find any reference to register
CCLKG_BURST_POLICY in arch/arm/mach-tegra/ or drivers/clk/tegra/ except
for the definition of clock cclk_g, nor any reference to that clock in
those two directories. And that CCLKG_BURST_POLICY register is what this
patch saves/restores.

I do see function tegra30_switch_cpu_to_clk32k in patch 5/8 appears to
do something related to switching to clk_m and touches some other
burst-related registers, but not CCLKG_BURST_POLICY...

So, if this syscore_op is attempting to undo some change that happens to
CCLKG_BURST_POLICY during suspend, I still can't see what change is
happening to that register during suspemd, nor which piece of code
causes it.

If the issue is that the value in this register is simply lost during
LP1 because the power is turned off, then I wonder what sets up this
register during the original boot path?

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
@ 2013-08-05 17:39                             ` Stephen Warren
  0 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-08-05 17:39 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/05/2013 11:00 AM, Stephen Warren wrote:
> On 08/05/2013 02:02 AM, Joseph Lo wrote:
>> On Sat, 2013-08-03 at 04:32 +0800, Stephen Warren wrote:
>>> On 08/02/2013 02:09 AM, Joseph Lo wrote:
>>>> On Tue, 2013-07-30 at 06:51 +0800, Stephen Warren wrote:
>>>>> On 07/26/2013 03:15 AM, Joseph Lo wrote:
>>>>>> When the system suspends to LP1, the clock of the CPU would be switched to
>>>>>> CLK_M (12MHz Oscillator) during suspend/resume flow. The clock driver
>>>>>> needs to restore the clock of CPU after LP1 resume.
>>>>>
>>>>> It's unclear to me how the code change implements "restore the clock of
>>>>> the CPU". A register name of CCLKG_BURST_POLICY doesn't sound like it's
>>>>> anything to do with enabled/disabling the CPU clock, nor configuring its
>>>>> rate. What exactly does this register do, and hence what does this new
>>>>> code actually restore?
>>>>>
>>>> When system suspend to LP1, most of the PLLs was clock gated. Because we
>>>> didn't cut off the core power, the settings of PLL still keep there. But
>>>> we switch the clock source of CPU to CLK_M before shut off PLLs by
>>>> CCLKG_BURSY_POLICY register. So we need to resume it back to original
>>>> clock source by CCLKG_BURST_POLICY register. Or it would be keep in low
>>>> rate (CLK_M) after resume.
>>>
>>> OK, I guess the register name was badly chosen by HW. I'd like to see
>>> part of your description above in the patch description. How about
>>> replacing the patch description with:
>>>
>>> ----------
>>> When the system suspends to LP1, the CPU clock source is switched to
>>> CLK_M (12MHz Oscillator) during suspend/resume flow[1]. The CPU clock
>>> source is controlled by the CCLKG_BURST_POLICY register, and hence this
>>> register must be restored during LP1 resume.
>>> ----------
>>>
>>> [1] Question: where does this happen? This patch doesn't make that
>>> change. I wonder why the suspend path can't save this register, rather
>>> than implementing a separate suspend syscore op in the clock driver.
>>> Does the HW auto-switch the value in the register itself?
>>
>> If we switch CPU to CLK_M in clock driver, the system will become slowly
>> during the middle of suspending flow. We do this at the very end of the
>> LP1 suspending flow before the CPU disable all the PLL clocks.
> 
> I think you answered "why is the switch to CLK_M performed very late",
> whereas I asked "where is the code that performs the switch to CLK_M".

To expand on this a bit more, I can't find any reference to register
CCLKG_BURST_POLICY in arch/arm/mach-tegra/ or drivers/clk/tegra/ except
for the definition of clock cclk_g, nor any reference to that clock in
those two directories. And that CCLKG_BURST_POLICY register is what this
patch saves/restores.

I do see function tegra30_switch_cpu_to_clk32k in patch 5/8 appears to
do something related to switching to clk_m and touches some other
burst-related registers, but not CCLKG_BURST_POLICY...

So, if this syscore_op is attempting to undo some change that happens to
CCLKG_BURST_POLICY during suspend, I still can't see what change is
happening to that register during suspemd, nor which piece of code
causes it.

If the issue is that the value in this register is simply lost during
LP1 because the power is turned off, then I wonder what sets up this
register during the original boot path?

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
  2013-08-05 17:39                             ` Stephen Warren
@ 2013-08-06  9:10                                 ` Joseph Lo
  -1 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-06  9:10 UTC (permalink / raw)
  To: Stephen Warren
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Mike Turquette

On Tue, 2013-08-06 at 01:39 +0800, Stephen Warren wrote:
> On 08/05/2013 11:00 AM, Stephen Warren wrote:
> > On 08/05/2013 02:02 AM, Joseph Lo wrote:
> >> On Sat, 2013-08-03 at 04:32 +0800, Stephen Warren wrote:
> >>> On 08/02/2013 02:09 AM, Joseph Lo wrote:
> >>>> On Tue, 2013-07-30 at 06:51 +0800, Stephen Warren wrote:
> >>>>> On 07/26/2013 03:15 AM, Joseph Lo wrote:
> >>>>>> When the system suspends to LP1, the clock of the CPU would be switched to
> >>>>>> CLK_M (12MHz Oscillator) during suspend/resume flow. The clock driver
> >>>>>> needs to restore the clock of CPU after LP1 resume.
> >>>>>
> >>>>> It's unclear to me how the code change implements "restore the clock of
> >>>>> the CPU". A register name of CCLKG_BURST_POLICY doesn't sound like it's
> >>>>> anything to do with enabled/disabling the CPU clock, nor configuring its
> >>>>> rate. What exactly does this register do, and hence what does this new
> >>>>> code actually restore?
> >>>>>
> >>>> When system suspend to LP1, most of the PLLs was clock gated. Because we
> >>>> didn't cut off the core power, the settings of PLL still keep there. But
> >>>> we switch the clock source of CPU to CLK_M before shut off PLLs by
> >>>> CCLKG_BURSY_POLICY register. So we need to resume it back to original
> >>>> clock source by CCLKG_BURST_POLICY register. Or it would be keep in low
> >>>> rate (CLK_M) after resume.
> >>>
> >>> OK, I guess the register name was badly chosen by HW. I'd like to see
> >>> part of your description above in the patch description. How about
> >>> replacing the patch description with:
> >>>
> >>> ----------
> >>> When the system suspends to LP1, the CPU clock source is switched to
> >>> CLK_M (12MHz Oscillator) during suspend/resume flow[1]. The CPU clock
> >>> source is controlled by the CCLKG_BURST_POLICY register, and hence this
> >>> register must be restored during LP1 resume.
> >>> ----------
> >>>
> >>> [1] Question: where does this happen? This patch doesn't make that
> >>> change. I wonder why the suspend path can't save this register, rather
> >>> than implementing a separate suspend syscore op in the clock driver.
> >>> Does the HW auto-switch the value in the register itself?
> >>
> >> If we switch CPU to CLK_M in clock driver, the system will become slowly
> >> during the middle of suspending flow. We do this at the very end of the
> >> LP1 suspending flow before the CPU disable all the PLL clocks.
> > 
> > I think you answered "why is the switch to CLK_M performed very late",
> > whereas I asked "where is the code that performs the switch to CLK_M".
You can find to code in the function tegraXX_switch_cpu_to_clk32k of the
patch 5/8 and 6/8. It switches the SCLK and CCLK to CLKM before
disabling the PLLs.
> 
> To expand on this a bit more, I can't find any reference to register
> CCLKG_BURST_POLICY in arch/arm/mach-tegra/ or drivers/clk/tegra/ except
> for the definition of clock cclk_g, nor any reference to that clock in
> those two directories. And that CCLKG_BURST_POLICY register is what this
> patch saves/restores.
> 
> I do see function tegra30_switch_cpu_to_clk32k in patch 5/8 appears to
> do something related to switching to clk_m and touches some other
> burst-related registers, but not CCLKG_BURST_POLICY...
> 
OK. It's a little difference we do in clock driver side and low level
code side.

As you knew, we have two CPU clusters (CCLKG & CCLKLP) in Tegra chips.
And they have exactly the same clock source for Tegra20/30, but
Tegra114. From the low level side, it should be supported when the
system going to suspend with CPU_G or CPU_LP. So we switched the clock
source of CPU to CLK_M by CCLK_BURST_POLICY. This register can support
the running CPU switch to the clock source they want.

On the clock driver side, because the clock source of CCLKG and CCLKLP
is not exactly the same for the Tegra114. The CCLKG supports DFLL clock
but CCLKLP. We need to restore it separately to avoid the CPU restore to
the wrong clock source.

The CPU clock suspend/resume function in the tegra_cpu_car_ops of
Tegra20/30 is also using CCLK_BURST_POLICY register, because the
definition of the clock source of CCLKG and CCLKLP is the same. We can
simply the code for just using CCLK_BURST_POLICY.

I think the code should OK when CPU_G or CPU_LP running with it.

> So, if this syscore_op is attempting to undo some change that happens to
> CCLKG_BURST_POLICY during suspend, I still can't see what change is
> happening to that register during suspemd, nor which piece of code
> causes it.
> 
> If the issue is that the value in this register is simply lost during
> LP1 because the power is turned off, then I wonder what sets up this
> register during the original boot path?

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
@ 2013-08-06  9:10                                 ` Joseph Lo
  0 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-06  9:10 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 2013-08-06 at 01:39 +0800, Stephen Warren wrote:
> On 08/05/2013 11:00 AM, Stephen Warren wrote:
> > On 08/05/2013 02:02 AM, Joseph Lo wrote:
> >> On Sat, 2013-08-03 at 04:32 +0800, Stephen Warren wrote:
> >>> On 08/02/2013 02:09 AM, Joseph Lo wrote:
> >>>> On Tue, 2013-07-30 at 06:51 +0800, Stephen Warren wrote:
> >>>>> On 07/26/2013 03:15 AM, Joseph Lo wrote:
> >>>>>> When the system suspends to LP1, the clock of the CPU would be switched to
> >>>>>> CLK_M (12MHz Oscillator) during suspend/resume flow. The clock driver
> >>>>>> needs to restore the clock of CPU after LP1 resume.
> >>>>>
> >>>>> It's unclear to me how the code change implements "restore the clock of
> >>>>> the CPU". A register name of CCLKG_BURST_POLICY doesn't sound like it's
> >>>>> anything to do with enabled/disabling the CPU clock, nor configuring its
> >>>>> rate. What exactly does this register do, and hence what does this new
> >>>>> code actually restore?
> >>>>>
> >>>> When system suspend to LP1, most of the PLLs was clock gated. Because we
> >>>> didn't cut off the core power, the settings of PLL still keep there. But
> >>>> we switch the clock source of CPU to CLK_M before shut off PLLs by
> >>>> CCLKG_BURSY_POLICY register. So we need to resume it back to original
> >>>> clock source by CCLKG_BURST_POLICY register. Or it would be keep in low
> >>>> rate (CLK_M) after resume.
> >>>
> >>> OK, I guess the register name was badly chosen by HW. I'd like to see
> >>> part of your description above in the patch description. How about
> >>> replacing the patch description with:
> >>>
> >>> ----------
> >>> When the system suspends to LP1, the CPU clock source is switched to
> >>> CLK_M (12MHz Oscillator) during suspend/resume flow[1]. The CPU clock
> >>> source is controlled by the CCLKG_BURST_POLICY register, and hence this
> >>> register must be restored during LP1 resume.
> >>> ----------
> >>>
> >>> [1] Question: where does this happen? This patch doesn't make that
> >>> change. I wonder why the suspend path can't save this register, rather
> >>> than implementing a separate suspend syscore op in the clock driver.
> >>> Does the HW auto-switch the value in the register itself?
> >>
> >> If we switch CPU to CLK_M in clock driver, the system will become slowly
> >> during the middle of suspending flow. We do this at the very end of the
> >> LP1 suspending flow before the CPU disable all the PLL clocks.
> > 
> > I think you answered "why is the switch to CLK_M performed very late",
> > whereas I asked "where is the code that performs the switch to CLK_M".
You can find to code in the function tegraXX_switch_cpu_to_clk32k of the
patch 5/8 and 6/8. It switches the SCLK and CCLK to CLKM before
disabling the PLLs.
> 
> To expand on this a bit more, I can't find any reference to register
> CCLKG_BURST_POLICY in arch/arm/mach-tegra/ or drivers/clk/tegra/ except
> for the definition of clock cclk_g, nor any reference to that clock in
> those two directories. And that CCLKG_BURST_POLICY register is what this
> patch saves/restores.
> 
> I do see function tegra30_switch_cpu_to_clk32k in patch 5/8 appears to
> do something related to switching to clk_m and touches some other
> burst-related registers, but not CCLKG_BURST_POLICY...
> 
OK. It's a little difference we do in clock driver side and low level
code side.

As you knew, we have two CPU clusters (CCLKG & CCLKLP) in Tegra chips.
And they have exactly the same clock source for Tegra20/30, but
Tegra114. From the low level side, it should be supported when the
system going to suspend with CPU_G or CPU_LP. So we switched the clock
source of CPU to CLK_M by CCLK_BURST_POLICY. This register can support
the running CPU switch to the clock source they want.

On the clock driver side, because the clock source of CCLKG and CCLKLP
is not exactly the same for the Tegra114. The CCLKG supports DFLL clock
but CCLKLP. We need to restore it separately to avoid the CPU restore to
the wrong clock source.

The CPU clock suspend/resume function in the tegra_cpu_car_ops of
Tegra20/30 is also using CCLK_BURST_POLICY register, because the
definition of the clock source of CCLKG and CCLKLP is the same. We can
simply the code for just using CCLK_BURST_POLICY.

I think the code should OK when CPU_G or CPU_LP running with it.

> So, if this syscore_op is attempting to undo some change that happens to
> CCLKG_BURST_POLICY during suspend, I still can't see what change is
> happening to that register during suspemd, nor which piece of code
> causes it.
> 
> If the issue is that the value in this register is simply lost during
> LP1 because the power is turned off, then I wonder what sets up this
> register during the original boot path?

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
  2013-08-05 17:00                         ` Stephen Warren
@ 2013-08-06  9:19                             ` Joseph Lo
  -1 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-06  9:19 UTC (permalink / raw)
  To: Stephen Warren
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Mike Turquette

On Tue, 2013-08-06 at 01:00 +0800, Stephen Warren wrote:
> On 08/05/2013 02:02 AM, Joseph Lo wrote:
> > On Sat, 2013-08-03 at 04:32 +0800, Stephen Warren wrote:
> >> On 08/02/2013 02:09 AM, Joseph Lo wrote:
> >>> On Tue, 2013-07-30 at 06:51 +0800, Stephen Warren wrote:
> >>>> On 07/26/2013 03:15 AM, Joseph Lo wrote:
> >>>>> When the system suspends to LP1, the clock of the CPU would be switched to
> >>>>> CLK_M (12MHz Oscillator) during suspend/resume flow. The clock driver
> >>>>> needs to restore the clock of CPU after LP1 resume.
> >>>>
[snip]
> >>>> Why don't Tegra20/30 need a similar change?
> >>>
> >>> For Tegra20/30, the same code had been implemented in the suspend/resume
> >>> function of tegra_cpu_car_ops. It restores the CPU clock ASAP when CPU
> >>> resume from a suspend state to get quick performance I believe.
> >>>
> >>> For Tegra114, the resume performance is cool (although we can't see it
> >>> in upstream kernel now, it still need some other functions.). We can
> >>> implement all the clock related suspend/resume function in the clock
> >>> driver.
> >>
> >> OK, I do see something similar in tegra20/30_cpu_clock_suspend/resume.
> >> Why can't this new code be part of the equivalent functions; does the
> >> Tegra114 suspend/resume code in mach-tegra/ not call
> >> tegra_cpu_car_ops.suspend/resume() in the same way it does on Tegra20/30?
> >
> > One of the main reasons is due to DFLL clock. The CPU clock of Tegra114
> > is going to switch to DFLL to a get higher clock rate. But it depends
> > some other HW (i.e. I2C), we can't resume it so early when the CPU just
> > resume like we did in tegra_cpu_car_ops.suspend/resume for Tegra20/30.
> 
> Is there a guarantee that the syscore_ops are run after the call to
> tegar_cpu_car_ops.resume() would be? Perhaps the mach-tegra/ code should
> simply call tegra_cpu_car_ops.resume() later on Tegra114 than it does on
> earlier chips, so it's run at a suitable time?

Yes, the tegra_cpu_car_ops.suspend/resume() is hooked to
suspend_cpu_complex()/resuem_cpu_complex() that in the "pm.c" file. It
would be the final/first function be invoked when system suspend/resume.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
@ 2013-08-06  9:19                             ` Joseph Lo
  0 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-06  9:19 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 2013-08-06 at 01:00 +0800, Stephen Warren wrote:
> On 08/05/2013 02:02 AM, Joseph Lo wrote:
> > On Sat, 2013-08-03 at 04:32 +0800, Stephen Warren wrote:
> >> On 08/02/2013 02:09 AM, Joseph Lo wrote:
> >>> On Tue, 2013-07-30 at 06:51 +0800, Stephen Warren wrote:
> >>>> On 07/26/2013 03:15 AM, Joseph Lo wrote:
> >>>>> When the system suspends to LP1, the clock of the CPU would be switched to
> >>>>> CLK_M (12MHz Oscillator) during suspend/resume flow. The clock driver
> >>>>> needs to restore the clock of CPU after LP1 resume.
> >>>>
[snip]
> >>>> Why don't Tegra20/30 need a similar change?
> >>>
> >>> For Tegra20/30, the same code had been implemented in the suspend/resume
> >>> function of tegra_cpu_car_ops. It restores the CPU clock ASAP when CPU
> >>> resume from a suspend state to get quick performance I believe.
> >>>
> >>> For Tegra114, the resume performance is cool (although we can't see it
> >>> in upstream kernel now, it still need some other functions.). We can
> >>> implement all the clock related suspend/resume function in the clock
> >>> driver.
> >>
> >> OK, I do see something similar in tegra20/30_cpu_clock_suspend/resume.
> >> Why can't this new code be part of the equivalent functions; does the
> >> Tegra114 suspend/resume code in mach-tegra/ not call
> >> tegra_cpu_car_ops.suspend/resume() in the same way it does on Tegra20/30?
> >
> > One of the main reasons is due to DFLL clock. The CPU clock of Tegra114
> > is going to switch to DFLL to a get higher clock rate. But it depends
> > some other HW (i.e. I2C), we can't resume it so early when the CPU just
> > resume like we did in tegra_cpu_car_ops.suspend/resume for Tegra20/30.
> 
> Is there a guarantee that the syscore_ops are run after the call to
> tegar_cpu_car_ops.resume() would be? Perhaps the mach-tegra/ code should
> simply call tegra_cpu_car_ops.resume() later on Tegra114 than it does on
> earlier chips, so it's run at a suitable time?

Yes, the tegra_cpu_car_ops.suspend/resume() is hooked to
suspend_cpu_complex()/resuem_cpu_complex() that in the "pm.c" file. It
would be the final/first function be invoked when system suspend/resume.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
  2013-08-06  9:10                                 ` Joseph Lo
@ 2013-08-06 18:37                                     ` Stephen Warren
  -1 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-08-06 18:37 UTC (permalink / raw)
  To: Joseph Lo
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Mike Turquette

On 08/06/2013 03:10 AM, Joseph Lo wrote:
> On Tue, 2013-08-06 at 01:39 +0800, Stephen Warren wrote:
>> On 08/05/2013 11:00 AM, Stephen Warren wrote:
>>> On 08/05/2013 02:02 AM, Joseph Lo wrote:
>>>> On Sat, 2013-08-03 at 04:32 +0800, Stephen Warren wrote:
>>>>> On 08/02/2013 02:09 AM, Joseph Lo wrote:
>>>>>> On Tue, 2013-07-30 at 06:51 +0800, Stephen Warren wrote:
>>>>>>> On 07/26/2013 03:15 AM, Joseph Lo wrote:
>>>>>>>> When the system suspends to LP1, the clock of the CPU would be switched to
>>>>>>>> CLK_M (12MHz Oscillator) during suspend/resume flow. The clock driver
>>>>>>>> needs to restore the clock of CPU after LP1 resume.
>>>>>>>
>>>>>>> It's unclear to me how the code change implements "restore the clock of
>>>>>>> the CPU". A register name of CCLKG_BURST_POLICY doesn't sound like it's
>>>>>>> anything to do with enabled/disabling the CPU clock, nor configuring its
>>>>>>> rate. What exactly does this register do, and hence what does this new
>>>>>>> code actually restore?
>>>>>>>
>>>>>> When system suspend to LP1, most of the PLLs was clock gated. Because we
>>>>>> didn't cut off the core power, the settings of PLL still keep there. But
>>>>>> we switch the clock source of CPU to CLK_M before shut off PLLs by
>>>>>> CCLKG_BURSY_POLICY register. So we need to resume it back to original
>>>>>> clock source by CCLKG_BURST_POLICY register. Or it would be keep in low
>>>>>> rate (CLK_M) after resume.
>>>>>
>>>>> OK, I guess the register name was badly chosen by HW. I'd like to see
>>>>> part of your description above in the patch description. How about
>>>>> replacing the patch description with:
>>>>>
>>>>> ----------
>>>>> When the system suspends to LP1, the CPU clock source is switched to
>>>>> CLK_M (12MHz Oscillator) during suspend/resume flow[1]. The CPU clock
>>>>> source is controlled by the CCLKG_BURST_POLICY register, and hence this
>>>>> register must be restored during LP1 resume.
>>>>> ----------
>>>>>
>>>>> [1] Question: where does this happen? This patch doesn't make that
>>>>> change. I wonder why the suspend path can't save this register, rather
>>>>> than implementing a separate suspend syscore op in the clock driver.
>>>>> Does the HW auto-switch the value in the register itself?
>>>>
>>>> If we switch CPU to CLK_M in clock driver, the system will become slowly
>>>> during the middle of suspending flow. We do this at the very end of the
>>>> LP1 suspending flow before the CPU disable all the PLL clocks.
>>>
>>> I think you answered "why is the switch to CLK_M performed very late",
>>> whereas I asked "where is the code that performs the switch to CLK_M".
> You can find to code in the function tegraXX_switch_cpu_to_clk32k of the
> patch 5/8 and 6/8. It switches the SCLK and CCLK to CLKM before
> disabling the PLLs.
>>
>> To expand on this a bit more, I can't find any reference to register
>> CCLKG_BURST_POLICY in arch/arm/mach-tegra/ or drivers/clk/tegra/ except
>> for the definition of clock cclk_g, nor any reference to that clock in
>> those two directories. And that CCLKG_BURST_POLICY register is what this
>> patch saves/restores.
>>
>> I do see function tegra30_switch_cpu_to_clk32k in patch 5/8 appears to
>> do something related to switching to clk_m and touches some other
>> burst-related registers, but not CCLKG_BURST_POLICY...
>>
> OK. It's a little difference we do in clock driver side and low level
> code side.
> 
> As you knew, we have two CPU clusters (CCLKG & CCLKLP) in Tegra chips.
> And they have exactly the same clock source for Tegra20/30, but
> Tegra114. From the low level side, it should be supported when the
> system going to suspend with CPU_G or CPU_LP. So we switched the clock
> source of CPU to CLK_M by CCLK_BURST_POLICY. This register can support
> the running CPU switch to the clock source they want.
> 
> On the clock driver side, because the clock source of CCLKG and CCLKLP
> is not exactly the same for the Tegra114. The CCLKG supports DFLL clock
> but CCLKLP. We need to restore it separately to avoid the CPU restore to
> the wrong clock source.
> 
> The CPU clock suspend/resume function in the tegra_cpu_car_ops of
> Tegra20/30 is also using CCLK_BURST_POLICY register, because the
> definition of the clock source of CCLKG and CCLKLP is the same. We can
> simply the code for just using CCLK_BURST_POLICY.
> 
> I think the code should OK when CPU_G or CPU_LP running with it.

OK, so just to paraphrase what you've said, to make sure I understand it:

On Tegra114, there are 3 registers:

1) Controls just the clock source of the G cluster.
2) Controls just the clock source of the LP cluster.
3) Controls both at the same time, simulating a write to both (1) and (2)

In tegra30_switch_cpu_to_clk32k(), we use register (3) above to switch
to clk_m then clk_s. This is possible, because in both registers (1) and
(2), the values for clk_m and clk_s are identical.

In tegra30_lp1_reset(), we can't use the same technique (a write to
register 3) to restore the clock source for both G and LP clusters,
since the values we need to write to those registers to re-select their
clock source is different.

however, on Tegra30, there really is only one register, so we can use
that to both switch to clk_m/clk_s, /and/ to switch back.

That all explains the following part of patch 7/8, which disables the
clock register restore path except on Tegra30 where it will work:

> -	mov32	r4, ((1 << 28) | (0x8))	@ burst policy is PLLX
> -	str	r4, [r0, #CLK_RESET_CCLK_BURST]
> +	cmp	r10, #TEGRA30
> +	movweq	r4, #:lower16:((1 << 28) | (0x8))	@ burst policy is PLLX
> +	movteq	r4, #:upper16:((1 << 28) | (0x8))
> +	streq	r4, [r0, #CLK_RESET_CCLK_BURST]

However, none of this explains why the CPU clock restore logic on
Tegra114 needs to be a syscore_op, rather than simply having
tegra30_lp1_reset() execute some Tegra114-specific code.

Re-writing the part of the patch I quoted above in C, it looks like:

if (soc is tegra30)
    write to CLK_RESET_CCLK_BURST to select pllx

Instead of making that code do nothing on Tegra114, why can't that code be:

if (soc is tegra30)
    write to CLK_RESET_CCLK_BURST to re-select pllx
else
    write to G-cluster-specific register to restore saved value
    write to LP-cluster-specific register to restore saved value

or:

if (soc is tegra30)
    write to CLK_RESET_CCLK_BURST to re-select pllx
else
    write to G-cluster-specific register to re-select pllx/dfll/...
    write to LP-cluster-specific register to restore ...

Plenty of other registers are saved/restored by tegra30_lp1_reset(), so
surely you just need to have save two more registers in the suspend
path, and restore them as I showed above.

That way, we don't have to have some special-case syscore_op to do this;
all chips will restore the clock sources at the exact same place in the
code, and everything will be consistent.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
@ 2013-08-06 18:37                                     ` Stephen Warren
  0 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-08-06 18:37 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/06/2013 03:10 AM, Joseph Lo wrote:
> On Tue, 2013-08-06 at 01:39 +0800, Stephen Warren wrote:
>> On 08/05/2013 11:00 AM, Stephen Warren wrote:
>>> On 08/05/2013 02:02 AM, Joseph Lo wrote:
>>>> On Sat, 2013-08-03 at 04:32 +0800, Stephen Warren wrote:
>>>>> On 08/02/2013 02:09 AM, Joseph Lo wrote:
>>>>>> On Tue, 2013-07-30 at 06:51 +0800, Stephen Warren wrote:
>>>>>>> On 07/26/2013 03:15 AM, Joseph Lo wrote:
>>>>>>>> When the system suspends to LP1, the clock of the CPU would be switched to
>>>>>>>> CLK_M (12MHz Oscillator) during suspend/resume flow. The clock driver
>>>>>>>> needs to restore the clock of CPU after LP1 resume.
>>>>>>>
>>>>>>> It's unclear to me how the code change implements "restore the clock of
>>>>>>> the CPU". A register name of CCLKG_BURST_POLICY doesn't sound like it's
>>>>>>> anything to do with enabled/disabling the CPU clock, nor configuring its
>>>>>>> rate. What exactly does this register do, and hence what does this new
>>>>>>> code actually restore?
>>>>>>>
>>>>>> When system suspend to LP1, most of the PLLs was clock gated. Because we
>>>>>> didn't cut off the core power, the settings of PLL still keep there. But
>>>>>> we switch the clock source of CPU to CLK_M before shut off PLLs by
>>>>>> CCLKG_BURSY_POLICY register. So we need to resume it back to original
>>>>>> clock source by CCLKG_BURST_POLICY register. Or it would be keep in low
>>>>>> rate (CLK_M) after resume.
>>>>>
>>>>> OK, I guess the register name was badly chosen by HW. I'd like to see
>>>>> part of your description above in the patch description. How about
>>>>> replacing the patch description with:
>>>>>
>>>>> ----------
>>>>> When the system suspends to LP1, the CPU clock source is switched to
>>>>> CLK_M (12MHz Oscillator) during suspend/resume flow[1]. The CPU clock
>>>>> source is controlled by the CCLKG_BURST_POLICY register, and hence this
>>>>> register must be restored during LP1 resume.
>>>>> ----------
>>>>>
>>>>> [1] Question: where does this happen? This patch doesn't make that
>>>>> change. I wonder why the suspend path can't save this register, rather
>>>>> than implementing a separate suspend syscore op in the clock driver.
>>>>> Does the HW auto-switch the value in the register itself?
>>>>
>>>> If we switch CPU to CLK_M in clock driver, the system will become slowly
>>>> during the middle of suspending flow. We do this at the very end of the
>>>> LP1 suspending flow before the CPU disable all the PLL clocks.
>>>
>>> I think you answered "why is the switch to CLK_M performed very late",
>>> whereas I asked "where is the code that performs the switch to CLK_M".
> You can find to code in the function tegraXX_switch_cpu_to_clk32k of the
> patch 5/8 and 6/8. It switches the SCLK and CCLK to CLKM before
> disabling the PLLs.
>>
>> To expand on this a bit more, I can't find any reference to register
>> CCLKG_BURST_POLICY in arch/arm/mach-tegra/ or drivers/clk/tegra/ except
>> for the definition of clock cclk_g, nor any reference to that clock in
>> those two directories. And that CCLKG_BURST_POLICY register is what this
>> patch saves/restores.
>>
>> I do see function tegra30_switch_cpu_to_clk32k in patch 5/8 appears to
>> do something related to switching to clk_m and touches some other
>> burst-related registers, but not CCLKG_BURST_POLICY...
>>
> OK. It's a little difference we do in clock driver side and low level
> code side.
> 
> As you knew, we have two CPU clusters (CCLKG & CCLKLP) in Tegra chips.
> And they have exactly the same clock source for Tegra20/30, but
> Tegra114. From the low level side, it should be supported when the
> system going to suspend with CPU_G or CPU_LP. So we switched the clock
> source of CPU to CLK_M by CCLK_BURST_POLICY. This register can support
> the running CPU switch to the clock source they want.
> 
> On the clock driver side, because the clock source of CCLKG and CCLKLP
> is not exactly the same for the Tegra114. The CCLKG supports DFLL clock
> but CCLKLP. We need to restore it separately to avoid the CPU restore to
> the wrong clock source.
> 
> The CPU clock suspend/resume function in the tegra_cpu_car_ops of
> Tegra20/30 is also using CCLK_BURST_POLICY register, because the
> definition of the clock source of CCLKG and CCLKLP is the same. We can
> simply the code for just using CCLK_BURST_POLICY.
> 
> I think the code should OK when CPU_G or CPU_LP running with it.

OK, so just to paraphrase what you've said, to make sure I understand it:

On Tegra114, there are 3 registers:

1) Controls just the clock source of the G cluster.
2) Controls just the clock source of the LP cluster.
3) Controls both at the same time, simulating a write to both (1) and (2)

In tegra30_switch_cpu_to_clk32k(), we use register (3) above to switch
to clk_m then clk_s. This is possible, because in both registers (1) and
(2), the values for clk_m and clk_s are identical.

In tegra30_lp1_reset(), we can't use the same technique (a write to
register 3) to restore the clock source for both G and LP clusters,
since the values we need to write to those registers to re-select their
clock source is different.

however, on Tegra30, there really is only one register, so we can use
that to both switch to clk_m/clk_s, /and/ to switch back.

That all explains the following part of patch 7/8, which disables the
clock register restore path except on Tegra30 where it will work:

> -	mov32	r4, ((1 << 28) | (0x8))	@ burst policy is PLLX
> -	str	r4, [r0, #CLK_RESET_CCLK_BURST]
> +	cmp	r10, #TEGRA30
> +	movweq	r4, #:lower16:((1 << 28) | (0x8))	@ burst policy is PLLX
> +	movteq	r4, #:upper16:((1 << 28) | (0x8))
> +	streq	r4, [r0, #CLK_RESET_CCLK_BURST]

However, none of this explains why the CPU clock restore logic on
Tegra114 needs to be a syscore_op, rather than simply having
tegra30_lp1_reset() execute some Tegra114-specific code.

Re-writing the part of the patch I quoted above in C, it looks like:

if (soc is tegra30)
    write to CLK_RESET_CCLK_BURST to select pllx

Instead of making that code do nothing on Tegra114, why can't that code be:

if (soc is tegra30)
    write to CLK_RESET_CCLK_BURST to re-select pllx
else
    write to G-cluster-specific register to restore saved value
    write to LP-cluster-specific register to restore saved value

or:

if (soc is tegra30)
    write to CLK_RESET_CCLK_BURST to re-select pllx
else
    write to G-cluster-specific register to re-select pllx/dfll/...
    write to LP-cluster-specific register to restore ...

Plenty of other registers are saved/restored by tegra30_lp1_reset(), so
surely you just need to have save two more registers in the suspend
path, and restore them as I showed above.

That way, we don't have to have some special-case syscore_op to do this;
all chips will restore the clock sources at the exact same place in the
code, and everything will be consistent.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
  2013-08-06 18:37                                     ` Stephen Warren
@ 2013-08-07  9:12                                         ` Joseph Lo
  -1 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-07  9:12 UTC (permalink / raw)
  To: Stephen Warren
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Mike Turquette

On Wed, 2013-08-07 at 02:37 +0800, Stephen Warren wrote:
> On 08/06/2013 03:10 AM, Joseph Lo wrote:
> > On Tue, 2013-08-06 at 01:39 +0800, Stephen Warren wrote:
> >> On 08/05/2013 11:00 AM, Stephen Warren wrote:
> >>> On 08/05/2013 02:02 AM, Joseph Lo wrote:
> >>>> On Sat, 2013-08-03 at 04:32 +0800, Stephen Warren wrote:
> >>>>> On 08/02/2013 02:09 AM, Joseph Lo wrote:
> >>>>>> On Tue, 2013-07-30 at 06:51 +0800, Stephen Warren wrote:
> >>>>>>> On 07/26/2013 03:15 AM, Joseph Lo wrote:
[snip]
> > OK. It's a little difference we do in clock driver side and low level
> > code side.
> > 
> > As you knew, we have two CPU clusters (CCLKG & CCLKLP) in Tegra chips.
> > And they have exactly the same clock source for Tegra20/30, but
> > Tegra114. From the low level side, it should be supported when the
> > system going to suspend with CPU_G or CPU_LP. So we switched the clock
> > source of CPU to CLK_M by CCLK_BURST_POLICY. This register can support
> > the running CPU switch to the clock source they want.
> > 
> > On the clock driver side, because the clock source of CCLKG and CCLKLP
> > is not exactly the same for the Tegra114. The CCLKG supports DFLL clock
> > but CCLKLP. We need to restore it separately to avoid the CPU restore to
> > the wrong clock source.
> > 
> > The CPU clock suspend/resume function in the tegra_cpu_car_ops of
> > Tegra20/30 is also using CCLK_BURST_POLICY register, because the
> > definition of the clock source of CCLKG and CCLKLP is the same. We can
> > simply the code for just using CCLK_BURST_POLICY.
> > 
> > I think the code should OK when CPU_G or CPU_LP running with it.
> 
> OK, so just to paraphrase what you've said, to make sure I understand it:
> 
> On Tegra114, there are 3 registers:
It's on Tegra30 too.
> 
> 1) Controls just the clock source of the G cluster.
CCLKG_BURST_POLICY at offset 0x368.
> 2) Controls just the clock source of the LP cluster.
CCLKLP_BURST_POLICY at offset 0x370.
> 3) Controls both at the same time, simulating a write to both (1) and (2)
CCLK_BURST_POLICY at offset 0x20.
Yes, exactly. And I double confirm the item 3 again with a downstream
kernel that support cluster switch today. It's true.
> 
> In tegra30_switch_cpu_to_clk32k(), we use register (3) above to switch
> to clk_m then clk_s. This is possible, because in both registers (1) and
> (2), the values for clk_m and clk_s are identical.
Yes.
> 
> In tegra30_lp1_reset(), we can't use the same technique (a write to
> register 3) to restore the clock source for both G and LP clusters,
> since the values we need to write to those registers to re-select their
> clock source is different.
> 
> however, on Tegra30, there really is only one register, so we can use
> that to both switch to clk_m/clk_s, /and/ to switch back.
> 
> That all explains the following part of patch 7/8, which disables the
> clock register restore path except on Tegra30 where it will work:
> 
> > -	mov32	r4, ((1 << 28) | (0x8))	@ burst policy is PLLX
> > -	str	r4, [r0, #CLK_RESET_CCLK_BURST]
> > +	cmp	r10, #TEGRA30
> > +	movweq	r4, #:lower16:((1 << 28) | (0x8))	@ burst policy is PLLX
> > +	movteq	r4, #:upper16:((1 << 28) | (0x8))
> > +	streq	r4, [r0, #CLK_RESET_CCLK_BURST]
OK. The burst policy of CPU clock has 5 different states (STDBY, IDLE,
RUN, IRQ and FIQ). The SW only can set up the clock source of each
state. The switching to different state was controlled by HW.

So the Tegra30 code here was set up the clock source of IDLE state only
(the CPU runs in this state after resume) to make the performance
better. But It had no idea about the clock sources of the other states.
It still needs clock driver to take care of that.

The clock source id (0x8) means PLLX_OUT0 in Tegra30. In Tegra114, it
means PLLX_OUT0_LJ (low jitter). It's not available at this moment. I
can switch it to PLLX_OUT0 (0xe) for Tegra114 too. The code may like
below.

> -     mov32   r4, ((1 << 28) | (0x8)) @ burst policy is PLLX
> -     str     r4, [r0, #CLK_RESET_CCLK_BURST]
> +     cmp     r10, #TEGRA30
> +     movweq  r4, #:lower16:((1 << 28) | (0x8))
> +     movteq  r4, #:upper16:((1 << 28) | (0x8))
> +	movwne  r4, #:lower16:((1 << 28) | (0xe))
> +	movteq  r4, #:upper16:((1 << 28) | (0xe))
> +     str	r4, [r0, #CLK_RESET_CCLK_BURST]

I had tested this code before I sent this series. It didn't impact or
improve the resuming performance. So I removed them.
Do you want me to add them back?

> 
> However, none of this explains why the CPU clock restore logic on
> Tegra114 needs to be a syscore_op, rather than simply having
> tegra30_lp1_reset() execute some Tegra114-specific code.
> 
> Re-writing the part of the patch I quoted above in C, it looks like:
> 
> if (soc is tegra30)
>     write to CLK_RESET_CCLK_BURST to select pllx
> 
> Instead of making that code do nothing on Tegra114, why can't that code be:
> 
> if (soc is tegra30)
>     write to CLK_RESET_CCLK_BURST to re-select pllx
> else
>     write to G-cluster-specific register to restore saved value
>     write to LP-cluster-specific register to restore saved value
> 
> or:
> 
> if (soc is tegra30)
>     write to CLK_RESET_CCLK_BURST to re-select pllx
> else
>     write to G-cluster-specific register to re-select pllx/dfll/...
>     write to LP-cluster-specific register to restore ...
> 
Hope the explanation above is enough for you. The low level code had no
idea of clock source of each CPU state in BURST_POLICY register. It
needs to be done by clk driver itself.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
@ 2013-08-07  9:12                                         ` Joseph Lo
  0 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-07  9:12 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, 2013-08-07 at 02:37 +0800, Stephen Warren wrote:
> On 08/06/2013 03:10 AM, Joseph Lo wrote:
> > On Tue, 2013-08-06 at 01:39 +0800, Stephen Warren wrote:
> >> On 08/05/2013 11:00 AM, Stephen Warren wrote:
> >>> On 08/05/2013 02:02 AM, Joseph Lo wrote:
> >>>> On Sat, 2013-08-03 at 04:32 +0800, Stephen Warren wrote:
> >>>>> On 08/02/2013 02:09 AM, Joseph Lo wrote:
> >>>>>> On Tue, 2013-07-30 at 06:51 +0800, Stephen Warren wrote:
> >>>>>>> On 07/26/2013 03:15 AM, Joseph Lo wrote:
[snip]
> > OK. It's a little difference we do in clock driver side and low level
> > code side.
> > 
> > As you knew, we have two CPU clusters (CCLKG & CCLKLP) in Tegra chips.
> > And they have exactly the same clock source for Tegra20/30, but
> > Tegra114. From the low level side, it should be supported when the
> > system going to suspend with CPU_G or CPU_LP. So we switched the clock
> > source of CPU to CLK_M by CCLK_BURST_POLICY. This register can support
> > the running CPU switch to the clock source they want.
> > 
> > On the clock driver side, because the clock source of CCLKG and CCLKLP
> > is not exactly the same for the Tegra114. The CCLKG supports DFLL clock
> > but CCLKLP. We need to restore it separately to avoid the CPU restore to
> > the wrong clock source.
> > 
> > The CPU clock suspend/resume function in the tegra_cpu_car_ops of
> > Tegra20/30 is also using CCLK_BURST_POLICY register, because the
> > definition of the clock source of CCLKG and CCLKLP is the same. We can
> > simply the code for just using CCLK_BURST_POLICY.
> > 
> > I think the code should OK when CPU_G or CPU_LP running with it.
> 
> OK, so just to paraphrase what you've said, to make sure I understand it:
> 
> On Tegra114, there are 3 registers:
It's on Tegra30 too.
> 
> 1) Controls just the clock source of the G cluster.
CCLKG_BURST_POLICY at offset 0x368.
> 2) Controls just the clock source of the LP cluster.
CCLKLP_BURST_POLICY at offset 0x370.
> 3) Controls both at the same time, simulating a write to both (1) and (2)
CCLK_BURST_POLICY at offset 0x20.
Yes, exactly. And I double confirm the item 3 again with a downstream
kernel that support cluster switch today. It's true.
> 
> In tegra30_switch_cpu_to_clk32k(), we use register (3) above to switch
> to clk_m then clk_s. This is possible, because in both registers (1) and
> (2), the values for clk_m and clk_s are identical.
Yes.
> 
> In tegra30_lp1_reset(), we can't use the same technique (a write to
> register 3) to restore the clock source for both G and LP clusters,
> since the values we need to write to those registers to re-select their
> clock source is different.
> 
> however, on Tegra30, there really is only one register, so we can use
> that to both switch to clk_m/clk_s, /and/ to switch back.
> 
> That all explains the following part of patch 7/8, which disables the
> clock register restore path except on Tegra30 where it will work:
> 
> > -	mov32	r4, ((1 << 28) | (0x8))	@ burst policy is PLLX
> > -	str	r4, [r0, #CLK_RESET_CCLK_BURST]
> > +	cmp	r10, #TEGRA30
> > +	movweq	r4, #:lower16:((1 << 28) | (0x8))	@ burst policy is PLLX
> > +	movteq	r4, #:upper16:((1 << 28) | (0x8))
> > +	streq	r4, [r0, #CLK_RESET_CCLK_BURST]
OK. The burst policy of CPU clock has 5 different states (STDBY, IDLE,
RUN, IRQ and FIQ). The SW only can set up the clock source of each
state. The switching to different state was controlled by HW.

So the Tegra30 code here was set up the clock source of IDLE state only
(the CPU runs in this state after resume) to make the performance
better. But It had no idea about the clock sources of the other states.
It still needs clock driver to take care of that.

The clock source id (0x8) means PLLX_OUT0 in Tegra30. In Tegra114, it
means PLLX_OUT0_LJ (low jitter). It's not available at this moment. I
can switch it to PLLX_OUT0 (0xe) for Tegra114 too. The code may like
below.

> -     mov32   r4, ((1 << 28) | (0x8)) @ burst policy is PLLX
> -     str     r4, [r0, #CLK_RESET_CCLK_BURST]
> +     cmp     r10, #TEGRA30
> +     movweq  r4, #:lower16:((1 << 28) | (0x8))
> +     movteq  r4, #:upper16:((1 << 28) | (0x8))
> +	movwne  r4, #:lower16:((1 << 28) | (0xe))
> +	movteq  r4, #:upper16:((1 << 28) | (0xe))
> +     str	r4, [r0, #CLK_RESET_CCLK_BURST]

I had tested this code before I sent this series. It didn't impact or
improve the resuming performance. So I removed them.
Do you want me to add them back?

> 
> However, none of this explains why the CPU clock restore logic on
> Tegra114 needs to be a syscore_op, rather than simply having
> tegra30_lp1_reset() execute some Tegra114-specific code.
> 
> Re-writing the part of the patch I quoted above in C, it looks like:
> 
> if (soc is tegra30)
>     write to CLK_RESET_CCLK_BURST to select pllx
> 
> Instead of making that code do nothing on Tegra114, why can't that code be:
> 
> if (soc is tegra30)
>     write to CLK_RESET_CCLK_BURST to re-select pllx
> else
>     write to G-cluster-specific register to restore saved value
>     write to LP-cluster-specific register to restore saved value
> 
> or:
> 
> if (soc is tegra30)
>     write to CLK_RESET_CCLK_BURST to re-select pllx
> else
>     write to G-cluster-specific register to re-select pllx/dfll/...
>     write to LP-cluster-specific register to restore ...
> 
Hope the explanation above is enough for you. The low level code had no
idea of clock source of each CPU state in BURST_POLICY register. It
needs to be done by clk driver itself.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
  2013-08-07  9:12                                         ` Joseph Lo
@ 2013-08-07 16:46                                             ` Stephen Warren
  -1 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-08-07 16:46 UTC (permalink / raw)
  To: Joseph Lo
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Mike Turquette, Paul Walmsley

On 08/07/2013 03:12 AM, Joseph Lo wrote:
> On Wed, 2013-08-07 at 02:37 +0800, Stephen Warren wrote:
>> On 08/06/2013 03:10 AM, Joseph Lo wrote:
>>> On Tue, 2013-08-06 at 01:39 +0800, Stephen Warren wrote:
>>>> On 08/05/2013 11:00 AM, Stephen Warren wrote:
>>>>> On 08/05/2013 02:02 AM, Joseph Lo wrote:
>>>>>> On Sat, 2013-08-03 at 04:32 +0800, Stephen Warren wrote:
>>>>>>> On 08/02/2013 02:09 AM, Joseph Lo wrote:
>>>>>>>> On Tue, 2013-07-30 at 06:51 +0800, Stephen Warren wrote:
>>>>>>>>> On 07/26/2013 03:15 AM, Joseph Lo wrote:
> [snip]
>>> OK. It's a little difference we do in clock driver side and low level
>>> code side.
>>>
>>> As you knew, we have two CPU clusters (CCLKG & CCLKLP) in Tegra chips.
>>> And they have exactly the same clock source for Tegra20/30, but
>>> Tegra114. From the low level side, it should be supported when the
>>> system going to suspend with CPU_G or CPU_LP. So we switched the clock
>>> source of CPU to CLK_M by CCLK_BURST_POLICY. This register can support
>>> the running CPU switch to the clock source they want.
>>>
>>> On the clock driver side, because the clock source of CCLKG and CCLKLP
>>> is not exactly the same for the Tegra114. The CCLKG supports DFLL clock
>>> but CCLKLP. We need to restore it separately to avoid the CPU restore to
>>> the wrong clock source.
>>>
>>> The CPU clock suspend/resume function in the tegra_cpu_car_ops of
>>> Tegra20/30 is also using CCLK_BURST_POLICY register, because the
>>> definition of the clock source of CCLKG and CCLKLP is the same. We can
>>> simply the code for just using CCLK_BURST_POLICY.
>>>
>>> I think the code should OK when CPU_G or CPU_LP running with it.
>>
>> OK, so just to paraphrase what you've said, to make sure I understand it:
>>
>> On Tegra114, there are 3 registers:
> It's on Tegra30 too.
>>
>> 1) Controls just the clock source of the G cluster.
> CCLKG_BURST_POLICY at offset 0x368.
>> 2) Controls just the clock source of the LP cluster.
> CCLKLP_BURST_POLICY at offset 0x370.
>> 3) Controls both at the same time, simulating a write to both (1) and (2)
> CCLK_BURST_POLICY at offset 0x20.
> Yes, exactly. And I double confirm the item 3 again with a downstream
> kernel that support cluster switch today. It's true.
>>
>> In tegra30_switch_cpu_to_clk32k(), we use register (3) above to switch
>> to clk_m then clk_s. This is possible, because in both registers (1) and
>> (2), the values for clk_m and clk_s are identical.
> Yes.
>>
>> In tegra30_lp1_reset(), we can't use the same technique (a write to
>> register 3) to restore the clock source for both G and LP clusters,
>> since the values we need to write to those registers to re-select their
>> clock source is different.
>>
>> however, on Tegra30, there really is only one register, so we can use
>> that to both switch to clk_m/clk_s, /and/ to switch back.
>>
>> That all explains the following part of patch 7/8, which disables the
>> clock register restore path except on Tegra30 where it will work:
>>
>>> -	mov32	r4, ((1 << 28) | (0x8))	@ burst policy is PLLX
>>> -	str	r4, [r0, #CLK_RESET_CCLK_BURST]
>>> +	cmp	r10, #TEGRA30
>>> +	movweq	r4, #:lower16:((1 << 28) | (0x8))	@ burst policy is PLLX
>>> +	movteq	r4, #:upper16:((1 << 28) | (0x8))
>>> +	streq	r4, [r0, #CLK_RESET_CCLK_BURST]
>
> OK. The burst policy of CPU clock has 5 different states (STDBY, IDLE,
> RUN, IRQ and FIQ). The SW only can set up the clock source of each
> state. The switching to different state was controlled by HW.
> 
> So the Tegra30 code here was set up the clock source of IDLE state only
> (the CPU runs in this state after resume) to make the performance
> better. But It had no idea about the clock sources of the other states.
> It still needs clock driver to take care of that.
> 
> The clock source id (0x8) means PLLX_OUT0 in Tegra30. In Tegra114, it
> means PLLX_OUT0_LJ (low jitter). It's not available at this moment. I
> can switch it to PLLX_OUT0 (0xe) for Tegra114 too. The code may like
> below.
> 
>> -     mov32   r4, ((1 << 28) | (0x8)) @ burst policy is PLLX
>> -     str     r4, [r0, #CLK_RESET_CCLK_BURST]
>> +     cmp     r10, #TEGRA30
>> +     movweq  r4, #:lower16:((1 << 28) | (0x8))
>> +     movteq  r4, #:upper16:((1 << 28) | (0x8))
>> +	movwne  r4, #:lower16:((1 << 28) | (0xe))
>> +	movteq  r4, #:upper16:((1 << 28) | (0xe))

I assume that should be movtne.

>> +     str	r4, [r0, #CLK_RESET_CCLK_BURST]
> 
> I had tested this code before I sent this series. It didn't impact or
> improve the resuming performance. So I removed them.
> Do you want me to add them back?

I think adding that code back would make sense. It would mean the code
works fundamentally the same way for all SoCs.

BTW, you mentioned that LP1 resume takes 10-15 seconds on Dalmore. Are
you sure this isn't because the above code is missing, and hence the CPU
keeps running at 32KHz or the crystal speed, since nothing restores the
CPU clock source until much later, when the syscore op runs?

>> However, none of this explains why the CPU clock restore logic on
>> Tegra114 needs to be a syscore_op, rather than simply having
>> tegra30_lp1_reset() execute some Tegra114-specific code.
>>
>> Re-writing the part of the patch I quoted above in C, it looks like:
>>
>> if (soc is tegra30)
>>     write to CLK_RESET_CCLK_BURST to select pllx
>>
>> Instead of making that code do nothing on Tegra114, why can't that code be:
>>
>> if (soc is tegra30)
>>     write to CLK_RESET_CCLK_BURST to re-select pllx
>> else
>>     write to G-cluster-specific register to restore saved value
>>     write to LP-cluster-specific register to restore saved value
>>
>> or:
>>
>> if (soc is tegra30)
>>     write to CLK_RESET_CCLK_BURST to re-select pllx
>> else
>>     write to G-cluster-specific register to re-select pllx/dfll/...
>>     write to LP-cluster-specific register to restore ...
>>
> Hope the explanation above is enough for you. The low level code had no
> idea of clock source of each CPU state in BURST_POLICY register. It
> needs to be done by clk driver itself.

I still have absolutely no idea why Tegra30 and Tegra114 are different.

You mentioned something about this low-level code only manipulating the
IDLE state, and the clock driver needing to restore the other 4 states.
This raises yet more questions:

1) Do we not need to restore the other 4 states on Tegra30? If not, why
not? If we do, presumably Tegra30 (and Tegra20?) need to the syscore_op
this patch series adds to Tegra114 only? If we don't, then why does
Tegra114 have to restore them?

2) What triggers the HW to switch from IDLE to RUN state? In other
words, I think you're saying that the existing Tegra30 code:

	mov32	r4, ((1 << 28) | (0x8))	@ burst policy is PLLX
	str	r4, [r0, #CLK_RESET_CCLK_BURST]

doesn't change the clock rate right away. When does it change?

Related, when tegra30_switch_cpu_to_clk32k() sets the CPU clock source
for the IDLE state to clk_s, when does that switch actually take place?
That function appears to assume it happens immediately, since it
immediately disables the PLLs that might have been the previous CPU
clock source. Perhaps this is what the MSELECT register writes do?  I
guess not since there's only 1 MSELECT write in tegra_lp1_reset() and
two changes of CPU clock source.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
@ 2013-08-07 16:46                                             ` Stephen Warren
  0 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-08-07 16:46 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/07/2013 03:12 AM, Joseph Lo wrote:
> On Wed, 2013-08-07 at 02:37 +0800, Stephen Warren wrote:
>> On 08/06/2013 03:10 AM, Joseph Lo wrote:
>>> On Tue, 2013-08-06 at 01:39 +0800, Stephen Warren wrote:
>>>> On 08/05/2013 11:00 AM, Stephen Warren wrote:
>>>>> On 08/05/2013 02:02 AM, Joseph Lo wrote:
>>>>>> On Sat, 2013-08-03 at 04:32 +0800, Stephen Warren wrote:
>>>>>>> On 08/02/2013 02:09 AM, Joseph Lo wrote:
>>>>>>>> On Tue, 2013-07-30 at 06:51 +0800, Stephen Warren wrote:
>>>>>>>>> On 07/26/2013 03:15 AM, Joseph Lo wrote:
> [snip]
>>> OK. It's a little difference we do in clock driver side and low level
>>> code side.
>>>
>>> As you knew, we have two CPU clusters (CCLKG & CCLKLP) in Tegra chips.
>>> And they have exactly the same clock source for Tegra20/30, but
>>> Tegra114. From the low level side, it should be supported when the
>>> system going to suspend with CPU_G or CPU_LP. So we switched the clock
>>> source of CPU to CLK_M by CCLK_BURST_POLICY. This register can support
>>> the running CPU switch to the clock source they want.
>>>
>>> On the clock driver side, because the clock source of CCLKG and CCLKLP
>>> is not exactly the same for the Tegra114. The CCLKG supports DFLL clock
>>> but CCLKLP. We need to restore it separately to avoid the CPU restore to
>>> the wrong clock source.
>>>
>>> The CPU clock suspend/resume function in the tegra_cpu_car_ops of
>>> Tegra20/30 is also using CCLK_BURST_POLICY register, because the
>>> definition of the clock source of CCLKG and CCLKLP is the same. We can
>>> simply the code for just using CCLK_BURST_POLICY.
>>>
>>> I think the code should OK when CPU_G or CPU_LP running with it.
>>
>> OK, so just to paraphrase what you've said, to make sure I understand it:
>>
>> On Tegra114, there are 3 registers:
> It's on Tegra30 too.
>>
>> 1) Controls just the clock source of the G cluster.
> CCLKG_BURST_POLICY at offset 0x368.
>> 2) Controls just the clock source of the LP cluster.
> CCLKLP_BURST_POLICY at offset 0x370.
>> 3) Controls both at the same time, simulating a write to both (1) and (2)
> CCLK_BURST_POLICY at offset 0x20.
> Yes, exactly. And I double confirm the item 3 again with a downstream
> kernel that support cluster switch today. It's true.
>>
>> In tegra30_switch_cpu_to_clk32k(), we use register (3) above to switch
>> to clk_m then clk_s. This is possible, because in both registers (1) and
>> (2), the values for clk_m and clk_s are identical.
> Yes.
>>
>> In tegra30_lp1_reset(), we can't use the same technique (a write to
>> register 3) to restore the clock source for both G and LP clusters,
>> since the values we need to write to those registers to re-select their
>> clock source is different.
>>
>> however, on Tegra30, there really is only one register, so we can use
>> that to both switch to clk_m/clk_s, /and/ to switch back.
>>
>> That all explains the following part of patch 7/8, which disables the
>> clock register restore path except on Tegra30 where it will work:
>>
>>> -	mov32	r4, ((1 << 28) | (0x8))	@ burst policy is PLLX
>>> -	str	r4, [r0, #CLK_RESET_CCLK_BURST]
>>> +	cmp	r10, #TEGRA30
>>> +	movweq	r4, #:lower16:((1 << 28) | (0x8))	@ burst policy is PLLX
>>> +	movteq	r4, #:upper16:((1 << 28) | (0x8))
>>> +	streq	r4, [r0, #CLK_RESET_CCLK_BURST]
>
> OK. The burst policy of CPU clock has 5 different states (STDBY, IDLE,
> RUN, IRQ and FIQ). The SW only can set up the clock source of each
> state. The switching to different state was controlled by HW.
> 
> So the Tegra30 code here was set up the clock source of IDLE state only
> (the CPU runs in this state after resume) to make the performance
> better. But It had no idea about the clock sources of the other states.
> It still needs clock driver to take care of that.
> 
> The clock source id (0x8) means PLLX_OUT0 in Tegra30. In Tegra114, it
> means PLLX_OUT0_LJ (low jitter). It's not available at this moment. I
> can switch it to PLLX_OUT0 (0xe) for Tegra114 too. The code may like
> below.
> 
>> -     mov32   r4, ((1 << 28) | (0x8)) @ burst policy is PLLX
>> -     str     r4, [r0, #CLK_RESET_CCLK_BURST]
>> +     cmp     r10, #TEGRA30
>> +     movweq  r4, #:lower16:((1 << 28) | (0x8))
>> +     movteq  r4, #:upper16:((1 << 28) | (0x8))
>> +	movwne  r4, #:lower16:((1 << 28) | (0xe))
>> +	movteq  r4, #:upper16:((1 << 28) | (0xe))

I assume that should be movtne.

>> +     str	r4, [r0, #CLK_RESET_CCLK_BURST]
> 
> I had tested this code before I sent this series. It didn't impact or
> improve the resuming performance. So I removed them.
> Do you want me to add them back?

I think adding that code back would make sense. It would mean the code
works fundamentally the same way for all SoCs.

BTW, you mentioned that LP1 resume takes 10-15 seconds on Dalmore. Are
you sure this isn't because the above code is missing, and hence the CPU
keeps running at 32KHz or the crystal speed, since nothing restores the
CPU clock source until much later, when the syscore op runs?

>> However, none of this explains why the CPU clock restore logic on
>> Tegra114 needs to be a syscore_op, rather than simply having
>> tegra30_lp1_reset() execute some Tegra114-specific code.
>>
>> Re-writing the part of the patch I quoted above in C, it looks like:
>>
>> if (soc is tegra30)
>>     write to CLK_RESET_CCLK_BURST to select pllx
>>
>> Instead of making that code do nothing on Tegra114, why can't that code be:
>>
>> if (soc is tegra30)
>>     write to CLK_RESET_CCLK_BURST to re-select pllx
>> else
>>     write to G-cluster-specific register to restore saved value
>>     write to LP-cluster-specific register to restore saved value
>>
>> or:
>>
>> if (soc is tegra30)
>>     write to CLK_RESET_CCLK_BURST to re-select pllx
>> else
>>     write to G-cluster-specific register to re-select pllx/dfll/...
>>     write to LP-cluster-specific register to restore ...
>>
> Hope the explanation above is enough for you. The low level code had no
> idea of clock source of each CPU state in BURST_POLICY register. It
> needs to be done by clk driver itself.

I still have absolutely no idea why Tegra30 and Tegra114 are different.

You mentioned something about this low-level code only manipulating the
IDLE state, and the clock driver needing to restore the other 4 states.
This raises yet more questions:

1) Do we not need to restore the other 4 states on Tegra30? If not, why
not? If we do, presumably Tegra30 (and Tegra20?) need to the syscore_op
this patch series adds to Tegra114 only? If we don't, then why does
Tegra114 have to restore them?

2) What triggers the HW to switch from IDLE to RUN state? In other
words, I think you're saying that the existing Tegra30 code:

	mov32	r4, ((1 << 28) | (0x8))	@ burst policy is PLLX
	str	r4, [r0, #CLK_RESET_CCLK_BURST]

doesn't change the clock rate right away. When does it change?

Related, when tegra30_switch_cpu_to_clk32k() sets the CPU clock source
for the IDLE state to clk_s, when does that switch actually take place?
That function appears to assume it happens immediately, since it
immediately disables the PLLs that might have been the previous CPU
clock source. Perhaps this is what the MSELECT register writes do?  I
guess not since there's only 1 MSELECT write in tegra_lp1_reset() and
two changes of CPU clock source.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
  2013-08-07 16:46                                             ` Stephen Warren
@ 2013-08-08  2:23                                                 ` Joseph Lo
  -1 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-08  2:23 UTC (permalink / raw)
  To: Stephen Warren
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Mike Turquette, Paul Walmsley

On Thu, 2013-08-08 at 00:46 +0800, Stephen Warren wrote:
> On 08/07/2013 03:12 AM, Joseph Lo wrote:
> > On Wed, 2013-08-07 at 02:37 +0800, Stephen Warren wrote:
> >> On 08/06/2013 03:10 AM, Joseph Lo wrote:
> >>> On Tue, 2013-08-06 at 01:39 +0800, Stephen Warren wrote:
> >>>> On 08/05/2013 11:00 AM, Stephen Warren wrote:
> >>>>> On 08/05/2013 02:02 AM, Joseph Lo wrote:
> >>>>>> On Sat, 2013-08-03 at 04:32 +0800, Stephen Warren wrote:
> >>>>>>> On 08/02/2013 02:09 AM, Joseph Lo wrote:
> >>>>>>>> On Tue, 2013-07-30 at 06:51 +0800, Stephen Warren wrote:
> >>>>>>>>> On 07/26/2013 03:15 AM, Joseph Lo wrote:
> > [snip]
[snip]
> >> In tegra30_lp1_reset(), we can't use the same technique (a write to
> >> register 3) to restore the clock source for both G and LP clusters,
> >> since the values we need to write to those registers to re-select their
> >> clock source is different.
> >>
> >> however, on Tegra30, there really is only one register, so we can use
> >> that to both switch to clk_m/clk_s, /and/ to switch back.
> >>
> >> That all explains the following part of patch 7/8, which disables the
> >> clock register restore path except on Tegra30 where it will work:
> >>
> >>> -	mov32	r4, ((1 << 28) | (0x8))	@ burst policy is PLLX
> >>> -	str	r4, [r0, #CLK_RESET_CCLK_BURST]
> >>> +	cmp	r10, #TEGRA30
> >>> +	movweq	r4, #:lower16:((1 << 28) | (0x8))	@ burst policy is PLLX
> >>> +	movteq	r4, #:upper16:((1 << 28) | (0x8))
> >>> +	streq	r4, [r0, #CLK_RESET_CCLK_BURST]
> >
> > OK. The burst policy of CPU clock has 5 different states (STDBY, IDLE,
> > RUN, IRQ and FIQ). The SW only can set up the clock source of each
> > state. The switching to different state was controlled by HW.
> > 
> > So the Tegra30 code here was set up the clock source of IDLE state only
> > (the CPU runs in this state after resume) to make the performance
> > better. But It had no idea about the clock sources of the other states.
> > It still needs clock driver to take care of that.
> > 
> > The clock source id (0x8) means PLLX_OUT0 in Tegra30. In Tegra114, it
> > means PLLX_OUT0_LJ (low jitter). It's not available at this moment. I
> > can switch it to PLLX_OUT0 (0xe) for Tegra114 too. The code may like
> > below.
> > 
> >> -     mov32   r4, ((1 << 28) | (0x8)) @ burst policy is PLLX
> >> -     str     r4, [r0, #CLK_RESET_CCLK_BURST]
> >> +     cmp     r10, #TEGRA30
> >> +     movweq  r4, #:lower16:((1 << 28) | (0x8))
> >> +     movteq  r4, #:upper16:((1 << 28) | (0x8))
> >> +	movwne  r4, #:lower16:((1 << 28) | (0xe))
> >> +	movteq  r4, #:upper16:((1 << 28) | (0xe))
> 
> I assume that should be movtne.
Ah, yes.
> 
> >> +     str	r4, [r0, #CLK_RESET_CCLK_BURST]
> > 
> > I had tested this code before I sent this series. It didn't impact or
> > improve the resuming performance. So I removed them.
> > Do you want me to add them back?
> 
> I think adding that code back would make sense. It would mean the code
> works fundamentally the same way for all SoCs.
OK.
> 
> BTW, you mentioned that LP1 resume takes 10-15 seconds on Dalmore. Are
> you sure this isn't because the above code is missing, and hence the CPU
> keeps running at 32KHz or the crystal speed, since nothing restores the
> CPU clock source until much later, when the syscore op runs?
It's due to the long time that SDRAM leaves self-refresh. Looks
something that related to BCT not be flashed during the flashing
process. Please also check the internal bug 1314918.
> 
> >> However, none of this explains why the CPU clock restore logic on
> >> Tegra114 needs to be a syscore_op, rather than simply having
> >> tegra30_lp1_reset() execute some Tegra114-specific code.
> >>
> >> Re-writing the part of the patch I quoted above in C, it looks like:
> >>
> >> if (soc is tegra30)
> >>     write to CLK_RESET_CCLK_BURST to select pllx
> >>
> >> Instead of making that code do nothing on Tegra114, why can't that code be:
> >>
> >> if (soc is tegra30)
> >>     write to CLK_RESET_CCLK_BURST to re-select pllx
> >> else
> >>     write to G-cluster-specific register to restore saved value
> >>     write to LP-cluster-specific register to restore saved value
> >>
> >> or:
> >>
> >> if (soc is tegra30)
> >>     write to CLK_RESET_CCLK_BURST to re-select pllx
> >> else
> >>     write to G-cluster-specific register to re-select pllx/dfll/...
> >>     write to LP-cluster-specific register to restore ...
> >>
> > Hope the explanation above is enough for you. The low level code had no
> > idea of clock source of each CPU state in BURST_POLICY register. It
> > needs to be done by clk driver itself.
> 
> I still have absolutely no idea why Tegra30 and Tegra114 are different.
> 
> You mentioned something about this low-level code only manipulating the
> IDLE state, and the clock driver needing to restore the other 4 states.
> This raises yet more questions:
> 
> 1) Do we not need to restore the other 4 states on Tegra30? If not, why
> not? If we do, presumably Tegra30 (and Tegra20?) need to the syscore_op
> this patch series adds to Tegra114 only? If we don't, then why does
> Tegra114 have to restore them?
We need to restore all of them for all Tegra chips. For Tegra20/30, we
had done it in the tegra_cpu_car_ops.suspend/resume. For Tegra114, the
patch was here.

The other reason is:
1) The PLLX is the main CPU clock source in Tegra20/30. We can restore
it ASAP to get a better performance.
2) For Tegra114, the PLLX is the CPU clock source when CPU runs at low
rates. When CPU in high rate, it uses DFLL as clock source. So it
depends on what the clock source of the CPU when it goes into suspend.
And the DFLL has its own resume code, it needs to be restored before the
CPU uses it as clock source again. It makes the CPU clock restore
sequence like this.

> 
> 2) What triggers the HW to switch from IDLE to RUN state? 
I also want to know more detail about it. The TRM only said it decided
by HW and gave an example about when switching to IRQ or FIQ state.
> In other
> words, I think you're saying that the existing Tegra30 code:
> 
> 	mov32	r4, ((1 << 28) | (0x8))	@ burst policy is PLLX
> 	str	r4, [r0, #CLK_RESET_CCLK_BURST]
> 
> doesn't change the clock rate right away. When does it change?
The code before this is the PLLX re-enable code. Then it switches to
PLLX. The rate is still kept the same when it suspended. The next rate
change after resume would be happened in the CPUfreq driver.
> 
> Related, when tegra30_switch_cpu_to_clk32k() sets the CPU clock source
> for the IDLE state to clk_s, when does that switch actually take place?
After the PLLs was disabled, only SCLK switched to CLK_S, the CPU and
MSELECT still keep in CLK_M. Because the CPU would be powered off
immediately.
> That function appears to assume it happens immediately, since it
> immediately disables the PLLs that might have been the previous CPU
> clock source. Perhaps this is what the MSELECT register writes do?  
Yes, switching them to the Oscillator before disabling the PLLs.
> I
> guess not since there's only 1 MSELECT write in tegra_lp1_reset() and
> two changes of CPU clock source.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
@ 2013-08-08  2:23                                                 ` Joseph Lo
  0 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-08  2:23 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, 2013-08-08 at 00:46 +0800, Stephen Warren wrote:
> On 08/07/2013 03:12 AM, Joseph Lo wrote:
> > On Wed, 2013-08-07 at 02:37 +0800, Stephen Warren wrote:
> >> On 08/06/2013 03:10 AM, Joseph Lo wrote:
> >>> On Tue, 2013-08-06 at 01:39 +0800, Stephen Warren wrote:
> >>>> On 08/05/2013 11:00 AM, Stephen Warren wrote:
> >>>>> On 08/05/2013 02:02 AM, Joseph Lo wrote:
> >>>>>> On Sat, 2013-08-03 at 04:32 +0800, Stephen Warren wrote:
> >>>>>>> On 08/02/2013 02:09 AM, Joseph Lo wrote:
> >>>>>>>> On Tue, 2013-07-30 at 06:51 +0800, Stephen Warren wrote:
> >>>>>>>>> On 07/26/2013 03:15 AM, Joseph Lo wrote:
> > [snip]
[snip]
> >> In tegra30_lp1_reset(), we can't use the same technique (a write to
> >> register 3) to restore the clock source for both G and LP clusters,
> >> since the values we need to write to those registers to re-select their
> >> clock source is different.
> >>
> >> however, on Tegra30, there really is only one register, so we can use
> >> that to both switch to clk_m/clk_s, /and/ to switch back.
> >>
> >> That all explains the following part of patch 7/8, which disables the
> >> clock register restore path except on Tegra30 where it will work:
> >>
> >>> -	mov32	r4, ((1 << 28) | (0x8))	@ burst policy is PLLX
> >>> -	str	r4, [r0, #CLK_RESET_CCLK_BURST]
> >>> +	cmp	r10, #TEGRA30
> >>> +	movweq	r4, #:lower16:((1 << 28) | (0x8))	@ burst policy is PLLX
> >>> +	movteq	r4, #:upper16:((1 << 28) | (0x8))
> >>> +	streq	r4, [r0, #CLK_RESET_CCLK_BURST]
> >
> > OK. The burst policy of CPU clock has 5 different states (STDBY, IDLE,
> > RUN, IRQ and FIQ). The SW only can set up the clock source of each
> > state. The switching to different state was controlled by HW.
> > 
> > So the Tegra30 code here was set up the clock source of IDLE state only
> > (the CPU runs in this state after resume) to make the performance
> > better. But It had no idea about the clock sources of the other states.
> > It still needs clock driver to take care of that.
> > 
> > The clock source id (0x8) means PLLX_OUT0 in Tegra30. In Tegra114, it
> > means PLLX_OUT0_LJ (low jitter). It's not available at this moment. I
> > can switch it to PLLX_OUT0 (0xe) for Tegra114 too. The code may like
> > below.
> > 
> >> -     mov32   r4, ((1 << 28) | (0x8)) @ burst policy is PLLX
> >> -     str     r4, [r0, #CLK_RESET_CCLK_BURST]
> >> +     cmp     r10, #TEGRA30
> >> +     movweq  r4, #:lower16:((1 << 28) | (0x8))
> >> +     movteq  r4, #:upper16:((1 << 28) | (0x8))
> >> +	movwne  r4, #:lower16:((1 << 28) | (0xe))
> >> +	movteq  r4, #:upper16:((1 << 28) | (0xe))
> 
> I assume that should be movtne.
Ah, yes.
> 
> >> +     str	r4, [r0, #CLK_RESET_CCLK_BURST]
> > 
> > I had tested this code before I sent this series. It didn't impact or
> > improve the resuming performance. So I removed them.
> > Do you want me to add them back?
> 
> I think adding that code back would make sense. It would mean the code
> works fundamentally the same way for all SoCs.
OK.
> 
> BTW, you mentioned that LP1 resume takes 10-15 seconds on Dalmore. Are
> you sure this isn't because the above code is missing, and hence the CPU
> keeps running at 32KHz or the crystal speed, since nothing restores the
> CPU clock source until much later, when the syscore op runs?
It's due to the long time that SDRAM leaves self-refresh. Looks
something that related to BCT not be flashed during the flashing
process. Please also check the internal bug 1314918.
> 
> >> However, none of this explains why the CPU clock restore logic on
> >> Tegra114 needs to be a syscore_op, rather than simply having
> >> tegra30_lp1_reset() execute some Tegra114-specific code.
> >>
> >> Re-writing the part of the patch I quoted above in C, it looks like:
> >>
> >> if (soc is tegra30)
> >>     write to CLK_RESET_CCLK_BURST to select pllx
> >>
> >> Instead of making that code do nothing on Tegra114, why can't that code be:
> >>
> >> if (soc is tegra30)
> >>     write to CLK_RESET_CCLK_BURST to re-select pllx
> >> else
> >>     write to G-cluster-specific register to restore saved value
> >>     write to LP-cluster-specific register to restore saved value
> >>
> >> or:
> >>
> >> if (soc is tegra30)
> >>     write to CLK_RESET_CCLK_BURST to re-select pllx
> >> else
> >>     write to G-cluster-specific register to re-select pllx/dfll/...
> >>     write to LP-cluster-specific register to restore ...
> >>
> > Hope the explanation above is enough for you. The low level code had no
> > idea of clock source of each CPU state in BURST_POLICY register. It
> > needs to be done by clk driver itself.
> 
> I still have absolutely no idea why Tegra30 and Tegra114 are different.
> 
> You mentioned something about this low-level code only manipulating the
> IDLE state, and the clock driver needing to restore the other 4 states.
> This raises yet more questions:
> 
> 1) Do we not need to restore the other 4 states on Tegra30? If not, why
> not? If we do, presumably Tegra30 (and Tegra20?) need to the syscore_op
> this patch series adds to Tegra114 only? If we don't, then why does
> Tegra114 have to restore them?
We need to restore all of them for all Tegra chips. For Tegra20/30, we
had done it in the tegra_cpu_car_ops.suspend/resume. For Tegra114, the
patch was here.

The other reason is:
1) The PLLX is the main CPU clock source in Tegra20/30. We can restore
it ASAP to get a better performance.
2) For Tegra114, the PLLX is the CPU clock source when CPU runs at low
rates. When CPU in high rate, it uses DFLL as clock source. So it
depends on what the clock source of the CPU when it goes into suspend.
And the DFLL has its own resume code, it needs to be restored before the
CPU uses it as clock source again. It makes the CPU clock restore
sequence like this.

> 
> 2) What triggers the HW to switch from IDLE to RUN state? 
I also want to know more detail about it. The TRM only said it decided
by HW and gave an example about when switching to IRQ or FIQ state.
> In other
> words, I think you're saying that the existing Tegra30 code:
> 
> 	mov32	r4, ((1 << 28) | (0x8))	@ burst policy is PLLX
> 	str	r4, [r0, #CLK_RESET_CCLK_BURST]
> 
> doesn't change the clock rate right away. When does it change?
The code before this is the PLLX re-enable code. Then it switches to
PLLX. The rate is still kept the same when it suspended. The next rate
change after resume would be happened in the CPUfreq driver.
> 
> Related, when tegra30_switch_cpu_to_clk32k() sets the CPU clock source
> for the IDLE state to clk_s, when does that switch actually take place?
After the PLLs was disabled, only SCLK switched to CLK_S, the CPU and
MSELECT still keep in CLK_M. Because the CPU would be powered off
immediately.
> That function appears to assume it happens immediately, since it
> immediately disables the PLLs that might have been the previous CPU
> clock source. Perhaps this is what the MSELECT register writes do?  
Yes, switching them to the Oscillator before disabling the PLLs.
> I
> guess not since there's only 1 MSELECT write in tegra_lp1_reset() and
> two changes of CPU clock source.

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
  2013-08-08  2:23                                                 ` Joseph Lo
@ 2013-08-08 19:54                                                     ` Stephen Warren
  -1 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-08-08 19:54 UTC (permalink / raw)
  To: Joseph Lo
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Mike Turquette, Paul Walmsley

On 08/07/2013 08:23 PM, Joseph Lo wrote:
> On Thu, 2013-08-08 at 00:46 +0800, Stephen Warren wrote:
..
>> I still have absolutely no idea why Tegra30 and Tegra114 are different.
>>
>> You mentioned something about this low-level code only manipulating the
>> IDLE state, and the clock driver needing to restore the other 4 states.
>> This raises yet more questions:
>>
>> 1) Do we not need to restore the other 4 states on Tegra30? If not, why
>> not? If we do, presumably Tegra30 (and Tegra20?) need to the syscore_op
>> this patch series adds to Tegra114 only? If we don't, then why does
>> Tegra114 have to restore them?
>
> We need to restore all of them for all Tegra chips. For Tegra20/30, we
> had done it in the tegra_cpu_car_ops.suspend/resume. For Tegra114, the
> patch was here.
> 
> The other reason is:
> 1) The PLLX is the main CPU clock source in Tegra20/30. We can restore
> it ASAP to get a better performance.
> 2) For Tegra114, the PLLX is the CPU clock source when CPU runs at low
> rates. When CPU in high rate, it uses DFLL as clock source. So it
> depends on what the clock source of the CPU when it goes into suspend.
> And the DFLL has its own resume code, it needs to be restored before the
> CPU uses it as clock source again. It makes the CPU clock restore
> sequence like this.

So, we don't yet support the DFLL upstream. Presumably, the CPU is
always running off PLLX on Tegra114 upstream right now. As such, we can
hard-code that into the resume path just like we do on earlier chips,
i.e. using tegra_cpu_car_ops.resume().

Once we do get DFLL support, presumably the DFLL resume path can switch
the register from PLLX to DFLL, and we still won't need a custom
syscore_ops.

Will that work?

>> 2) What triggers the HW to switch from IDLE to RUN state? 
>
> I also want to know more detail about it. The TRM only said it decided
> by HW and gave an example about when switching to IRQ or FIQ state.
>
>> In other
>> words, I think you're saying that the existing Tegra30 code:
>>
>> 	mov32	r4, ((1 << 28) | (0x8))	@ burst policy is PLLX
>> 	str	r4, [r0, #CLK_RESET_CCLK_BURST]
>>
>> doesn't change the clock rate right away. When does it change?
>
> The code before this is the PLLX re-enable code. Then it switches to
> PLLX. The rate is still kept the same when it suspended. The next rate
> change after resume would be happened in the CPUfreq driver.

Sorry, when I wrote "doesn't change the clock rate right away", I really
meant "doesn't change the clock *source* right away".

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
@ 2013-08-08 19:54                                                     ` Stephen Warren
  0 siblings, 0 replies; 88+ messages in thread
From: Stephen Warren @ 2013-08-08 19:54 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/07/2013 08:23 PM, Joseph Lo wrote:
> On Thu, 2013-08-08 at 00:46 +0800, Stephen Warren wrote:
..
>> I still have absolutely no idea why Tegra30 and Tegra114 are different.
>>
>> You mentioned something about this low-level code only manipulating the
>> IDLE state, and the clock driver needing to restore the other 4 states.
>> This raises yet more questions:
>>
>> 1) Do we not need to restore the other 4 states on Tegra30? If not, why
>> not? If we do, presumably Tegra30 (and Tegra20?) need to the syscore_op
>> this patch series adds to Tegra114 only? If we don't, then why does
>> Tegra114 have to restore them?
>
> We need to restore all of them for all Tegra chips. For Tegra20/30, we
> had done it in the tegra_cpu_car_ops.suspend/resume. For Tegra114, the
> patch was here.
> 
> The other reason is:
> 1) The PLLX is the main CPU clock source in Tegra20/30. We can restore
> it ASAP to get a better performance.
> 2) For Tegra114, the PLLX is the CPU clock source when CPU runs at low
> rates. When CPU in high rate, it uses DFLL as clock source. So it
> depends on what the clock source of the CPU when it goes into suspend.
> And the DFLL has its own resume code, it needs to be restored before the
> CPU uses it as clock source again. It makes the CPU clock restore
> sequence like this.

So, we don't yet support the DFLL upstream. Presumably, the CPU is
always running off PLLX on Tegra114 upstream right now. As such, we can
hard-code that into the resume path just like we do on earlier chips,
i.e. using tegra_cpu_car_ops.resume().

Once we do get DFLL support, presumably the DFLL resume path can switch
the register from PLLX to DFLL, and we still won't need a custom
syscore_ops.

Will that work?

>> 2) What triggers the HW to switch from IDLE to RUN state? 
>
> I also want to know more detail about it. The TRM only said it decided
> by HW and gave an example about when switching to IRQ or FIQ state.
>
>> In other
>> words, I think you're saying that the existing Tegra30 code:
>>
>> 	mov32	r4, ((1 << 28) | (0x8))	@ burst policy is PLLX
>> 	str	r4, [r0, #CLK_RESET_CCLK_BURST]
>>
>> doesn't change the clock rate right away. When does it change?
>
> The code before this is the PLLX re-enable code. Then it switches to
> PLLX. The rate is still kept the same when it suspended. The next rate
> change after resume would be happened in the CPUfreq driver.

Sorry, when I wrote "doesn't change the clock rate right away", I really
meant "doesn't change the clock *source* right away".

^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
  2013-08-08 19:54                                                     ` Stephen Warren
@ 2013-08-09  9:23                                                         ` Joseph Lo
  -1 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-09  9:23 UTC (permalink / raw)
  To: Stephen Warren
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Mike Turquette, Paul Walmsley

On Fri, 2013-08-09 at 03:54 +0800, Stephen Warren wrote:
> On 08/07/2013 08:23 PM, Joseph Lo wrote:
> > On Thu, 2013-08-08 at 00:46 +0800, Stephen Warren wrote:
> ..
> >> I still have absolutely no idea why Tegra30 and Tegra114 are different.
> >>
> >> You mentioned something about this low-level code only manipulating the
> >> IDLE state, and the clock driver needing to restore the other 4 states.
> >> This raises yet more questions:
> >>
> >> 1) Do we not need to restore the other 4 states on Tegra30? If not, why
> >> not? If we do, presumably Tegra30 (and Tegra20?) need to the syscore_op
> >> this patch series adds to Tegra114 only? If we don't, then why does
> >> Tegra114 have to restore them?
> >
> > We need to restore all of them for all Tegra chips. For Tegra20/30, we
> > had done it in the tegra_cpu_car_ops.suspend/resume. For Tegra114, the
> > patch was here.
> > 
> > The other reason is:
> > 1) The PLLX is the main CPU clock source in Tegra20/30. We can restore
> > it ASAP to get a better performance.
> > 2) For Tegra114, the PLLX is the CPU clock source when CPU runs at low
> > rates. When CPU in high rate, it uses DFLL as clock source. So it
> > depends on what the clock source of the CPU when it goes into suspend.
> > And the DFLL has its own resume code, it needs to be restored before the
> > CPU uses it as clock source again. It makes the CPU clock restore
> > sequence like this.
> 
> So, we don't yet support the DFLL upstream. Presumably, the CPU is
> always running off PLLX on Tegra114 upstream right now. As such, we can
> hard-code that into the resume path just like we do on earlier chips,
> i.e. using tegra_cpu_car_ops.resume().
> 
> Once we do get DFLL support, presumably the DFLL resume path can switch
> the register from PLLX to DFLL, and we still won't need a custom
> syscore_ops.
> 
> Will that work?
Hmm. If we can implement DFLL resume code there, that may be work. But I
can't confirm right now. I can move them to tegra_cpu_car_ops for now.
We can re-visit this later when we get more function to support.

Thanks,
Joseph

^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support
@ 2013-08-09  9:23                                                         ` Joseph Lo
  0 siblings, 0 replies; 88+ messages in thread
From: Joseph Lo @ 2013-08-09  9:23 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, 2013-08-09 at 03:54 +0800, Stephen Warren wrote:
> On 08/07/2013 08:23 PM, Joseph Lo wrote:
> > On Thu, 2013-08-08 at 00:46 +0800, Stephen Warren wrote:
> ..
> >> I still have absolutely no idea why Tegra30 and Tegra114 are different.
> >>
> >> You mentioned something about this low-level code only manipulating the
> >> IDLE state, and the clock driver needing to restore the other 4 states.
> >> This raises yet more questions:
> >>
> >> 1) Do we not need to restore the other 4 states on Tegra30? If not, why
> >> not? If we do, presumably Tegra30 (and Tegra20?) need to the syscore_op
> >> this patch series adds to Tegra114 only? If we don't, then why does
> >> Tegra114 have to restore them?
> >
> > We need to restore all of them for all Tegra chips. For Tegra20/30, we
> > had done it in the tegra_cpu_car_ops.suspend/resume. For Tegra114, the
> > patch was here.
> > 
> > The other reason is:
> > 1) The PLLX is the main CPU clock source in Tegra20/30. We can restore
> > it ASAP to get a better performance.
> > 2) For Tegra114, the PLLX is the CPU clock source when CPU runs at low
> > rates. When CPU in high rate, it uses DFLL as clock source. So it
> > depends on what the clock source of the CPU when it goes into suspend.
> > And the DFLL has its own resume code, it needs to be restored before the
> > CPU uses it as clock source again. It makes the CPU clock restore
> > sequence like this.
> 
> So, we don't yet support the DFLL upstream. Presumably, the CPU is
> always running off PLLX on Tegra114 upstream right now. As such, we can
> hard-code that into the resume path just like we do on earlier chips,
> i.e. using tegra_cpu_car_ops.resume().
> 
> Once we do get DFLL support, presumably the DFLL resume path can switch
> the register from PLLX to DFLL, and we still won't need a custom
> syscore_ops.
> 
> Will that work?
Hmm. If we can implement DFLL resume code there, that may be work. But I
can't confirm right now. I can move them to tegra_cpu_car_ops for now.
We can re-visit this later when we get more function to support.

Thanks,
Joseph

^ permalink raw reply	[flat|nested] 88+ messages in thread

end of thread, other threads:[~2013-08-09  9:23 UTC | newest]

Thread overview: 88+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-07-26  9:15 [PATCH 0/8] ARM: tegra: support LP1 suspend mode Joseph Lo
2013-07-26  9:15 ` Joseph Lo
     [not found] ` <1374830110-30685-1-git-send-email-josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2013-07-26  9:15   ` [PATCH 1/8] ARM: tegra: add common resume handling code for LP1 resuming Joseph Lo
2013-07-26  9:15     ` Joseph Lo
     [not found]     ` <1374830110-30685-2-git-send-email-josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2013-07-29 22:38       ` Stephen Warren
2013-07-29 22:38         ` Stephen Warren
2013-07-26  9:15   ` [PATCH 2/8] ARM: tegra: config the polarity of the request of sys clock Joseph Lo
2013-07-26  9:15     ` Joseph Lo
     [not found]     ` <1374830110-30685-3-git-send-email-josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2013-07-29 22:47       ` Stephen Warren
2013-07-29 22:47         ` Stephen Warren
     [not found]         ` <51F6F109.8010102-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2013-08-02  7:48           ` Joseph Lo
2013-08-02  7:48             ` Joseph Lo
     [not found]             ` <1375429739.6761.31.camel-yx3yKKdKkHfc7b1ADBJPm0n48jw8i0AO@public.gmane.org>
2013-08-02 20:24               ` Stephen Warren
2013-08-02 20:24                 ` Stephen Warren
     [not found]                 ` <51FC1579.50100-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2013-08-05  8:42                   ` Joseph Lo
2013-08-05  8:42                     ` Joseph Lo
2013-07-26  9:15   ` [PATCH 3/8] clk: tegra114: add LP1 suspend/resume support Joseph Lo
2013-07-26  9:15     ` Joseph Lo
     [not found]     ` <1374830110-30685-4-git-send-email-josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2013-07-29 22:51       ` Stephen Warren
2013-07-29 22:51         ` Stephen Warren
     [not found]         ` <51F6F209.2090309-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2013-08-02  8:09           ` Joseph Lo
2013-08-02  8:09             ` Joseph Lo
     [not found]             ` <1375430985.6761.51.camel-yx3yKKdKkHfc7b1ADBJPm0n48jw8i0AO@public.gmane.org>
2013-08-02 20:32               ` Stephen Warren
2013-08-02 20:32                 ` Stephen Warren
     [not found]                 ` <51FC1751.9010401-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2013-08-05  8:02                   ` Joseph Lo
2013-08-05  8:02                     ` Joseph Lo
     [not found]                     ` <1375689749.1731.36.camel-yx3yKKdKkHfc7b1ADBJPm0n48jw8i0AO@public.gmane.org>
2013-08-05 17:00                       ` Stephen Warren
2013-08-05 17:00                         ` Stephen Warren
     [not found]                         ` <51FFDA20.6050403-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2013-08-05 17:39                           ` Stephen Warren
2013-08-05 17:39                             ` Stephen Warren
     [not found]                             ` <51FFE363.4080603-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2013-08-06  9:10                               ` Joseph Lo
2013-08-06  9:10                                 ` Joseph Lo
     [not found]                                 ` <1375780249.2261.63.camel-yx3yKKdKkHfc7b1ADBJPm0n48jw8i0AO@public.gmane.org>
2013-08-06 18:37                                   ` Stephen Warren
2013-08-06 18:37                                     ` Stephen Warren
     [not found]                                     ` <52014258.9070602-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2013-08-07  9:12                                       ` Joseph Lo
2013-08-07  9:12                                         ` Joseph Lo
     [not found]                                         ` <1375866749.8111.57.camel-yx3yKKdKkHfc7b1ADBJPm0n48jw8i0AO@public.gmane.org>
2013-08-07 16:46                                           ` Stephen Warren
2013-08-07 16:46                                             ` Stephen Warren
     [not found]                                             ` <520279FC.1000006-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2013-08-08  2:23                                               ` Joseph Lo
2013-08-08  2:23                                                 ` Joseph Lo
     [not found]                                                 ` <1375928591.1758.66.camel-yx3yKKdKkHfc7b1ADBJPm0n48jw8i0AO@public.gmane.org>
2013-08-08 19:54                                                   ` Stephen Warren
2013-08-08 19:54                                                     ` Stephen Warren
     [not found]                                                     ` <5203F766.9050100-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2013-08-09  9:23                                                       ` Joseph Lo
2013-08-09  9:23                                                         ` Joseph Lo
2013-08-06  9:19                           ` Joseph Lo
2013-08-06  9:19                             ` Joseph Lo
2013-07-26  9:15   ` [PATCH 4/8] ARM: tegra: add common LP1 suspend support Joseph Lo
2013-07-26  9:15     ` Joseph Lo
     [not found]     ` <1374830110-30685-5-git-send-email-josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2013-07-29 23:13       ` Stephen Warren
2013-07-29 23:13         ` Stephen Warren
     [not found]         ` <51F6F725.6000106-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2013-08-02  9:27           ` Joseph Lo
2013-08-02  9:27             ` Joseph Lo
     [not found]             ` <1375435620.6761.120.camel-yx3yKKdKkHfc7b1ADBJPm0n48jw8i0AO@public.gmane.org>
2013-08-02 20:40               ` Stephen Warren
2013-08-02 20:40                 ` Stephen Warren
2013-08-05  8:07                 ` Joseph Lo
2013-08-05  8:07                   ` Joseph Lo
2013-07-26  9:15   ` [PATCH 5/8] ARM: tegra30: add " Joseph Lo
2013-07-26  9:15     ` Joseph Lo
     [not found]     ` <1374830110-30685-6-git-send-email-josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2013-07-29 23:45       ` Stephen Warren
2013-07-29 23:45         ` Stephen Warren
     [not found]         ` <51F6FE89.4060402-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2013-08-05  6:46           ` Joseph Lo
2013-08-05  6:46             ` Joseph Lo
2013-07-26  9:15   ` [PATCH 6/8] ARM: tegra20: " Joseph Lo
2013-07-26  9:15     ` Joseph Lo
2013-07-26  9:15   ` [PATCH 7/8] ARM: tegra114: " Joseph Lo
2013-07-26  9:15     ` Joseph Lo
     [not found]     ` <1374830110-30685-8-git-send-email-josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2013-07-29 23:53       ` Stephen Warren
2013-07-29 23:53         ` Stephen Warren
     [not found]         ` <51F7007F.6090200-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2013-08-05  6:51           ` Joseph Lo
2013-08-05  6:51             ` Joseph Lo
2013-07-26  9:15   ` [PATCH 8/8] ARM: dts: tegra: enable LP1 suspend mode Joseph Lo
2013-07-26  9:15     ` Joseph Lo
2013-07-27 16:12   ` [PATCH 0/8] ARM: tegra: support " Marc Dietrich
2013-07-27 16:12     ` Marc Dietrich
2013-07-27 16:20     ` Dmitry Osipenko
2013-07-27 16:20       ` Dmitry Osipenko
     [not found]       ` <51F3F356.8080709-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2013-07-27 18:09         ` Marc Dietrich
2013-07-27 18:09           ` Marc Dietrich
2013-07-27 18:26           ` Dmitry Osipenko
2013-07-27 18:26             ` Dmitry Osipenko
     [not found]             ` <51F410BE.3000904-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2013-07-27 18:29               ` Dmitry Osipenko
2013-07-27 18:29                 ` Dmitry Osipenko
2013-07-27 19:03               ` Marc Dietrich
2013-07-27 19:03                 ` Marc Dietrich
2013-07-27 19:11                 ` Dmitry Osipenko
2013-07-27 19:11                   ` Dmitry Osipenko
2013-07-30  9:49     ` Joseph Lo
2013-07-30  9:49       ` Joseph Lo

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.