diff mbox

[U-Boot,v2,09/12] tegra124: Add PSCI support for Tegra124

Message ID 48840a4f6b07a748ff10c20ffa69f6732c967b35.1424091289.git.jan.kiszka@siemens.com
State Changes Requested
Delegated to: Tom Warren
Headers show

Commit Message

Jan Kiszka Feb. 16, 2015, 12:54 p.m. UTC
This is based on Thierry Reding's work and uses Ian Campell's
preparatory patches. It comes with full support for CPU_ON/OFF PSCI
services. The algorithm used in this version for turning CPUs on and
off was proposed by Thierry Reding in
http://thread.gmane.org/gmane.comp.boot-loaders.u-boot/210881. It
consists of first enabling CPU1..3 via the PMC, just to powergate them
again with the help of the Flow Controller. Once the Flow Controller is
in place, we can leave the PMC alone while processing CPU_ON and CPU_OFF
PSCI requests.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
---
 arch/arm/cpu/armv7/Makefile               |   1 +
 arch/arm/cpu/armv7/tegra-common/Makefile  |   1 +
 arch/arm/cpu/armv7/tegra-common/psci.S    | 101 ++++++++++++++++++++++++++++++
 arch/arm/cpu/armv7/tegra124/Makefile      |   7 +++
 arch/arm/cpu/armv7/tegra124/ap.c          |  44 +++++++++++++
 arch/arm/include/asm/arch-tegra124/flow.h |   5 ++
 6 files changed, 159 insertions(+)
 create mode 100644 arch/arm/cpu/armv7/tegra-common/psci.S
 create mode 100644 arch/arm/cpu/armv7/tegra124/Makefile
 create mode 100644 arch/arm/cpu/armv7/tegra124/ap.c

Comments

Stephen Warren Feb. 17, 2015, 9:03 p.m. UTC | #1
On 02/16/2015 05:54 AM, Jan Kiszka wrote:
> This is based on Thierry Reding's work and uses Ian Campell's
> preparatory patches. It comes with full support for CPU_ON/OFF PSCI
> services. The algorithm used in this version for turning CPUs on and
> off was proposed by Thierry Reding in
> http://thread.gmane.org/gmane.comp.boot-loaders.u-boot/210881. It
> consists of first enabling CPU1..3 via the PMC, just to powergate them
> again with the help of the Flow Controller. Once the Flow Controller is
> in place, we can leave the PMC alone while processing CPU_ON and CPU_OFF
> PSCI requests.

> diff --git a/arch/arm/cpu/armv7/tegra124/ap.c b/arch/arm/cpu/armv7/tegra124/ap.c

> +void ap_pm_init(void)
> +{
> +	struct flow_ctlr *flow = (struct flow_ctlr *)NV_PA_FLOW_BASE;
> +	struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
> +
> +	writel((u32)park_cpu, EXCEP_VECTOR_CPU_RESET_VECTOR);
> +
> +	tegra_powergate_power_on(TEGRA_POWERGATE_CPU1);
> +	tegra_powergate_power_on(TEGRA_POWERGATE_CPU2);
> +	tegra_powergate_power_on(TEGRA_POWERGATE_CPU3);
> +
> +	writel((2 << CSR_WAIT_WFI_SHIFT) | CSR_ENABLE, &flow->cpu1_csr);
> +	writel((4 << CSR_WAIT_WFI_SHIFT) | CSR_ENABLE, &flow->cpu2_csr);
> +	writel((8 << CSR_WAIT_WFI_SHIFT) | CSR_ENABLE, &flow->cpu3_csr);
> +
> +	writel(EVENT_MODE_STOP, &flow->halt_cpu1_events);
> +	writel(EVENT_MODE_STOP, &flow->halt_cpu2_events);
> +	writel(EVENT_MODE_STOP, &flow->halt_cpu3_events);

I would expect to set up halt_cpu*_events before powering on the CPUs, 
to make sure that they do the expected action on the very first WFI. So, 
shouldn't the order above be:

Write to halt_cpu*_events
Write to cpu*_csr
power_on

I didn't review the assembly code at all really; I assume Thierry will.
Thierry Reding Feb. 19, 2015, 8:57 a.m. UTC | #2
On Mon, Feb 16, 2015 at 01:54:46PM +0100, Jan Kiszka wrote:
> This is based on Thierry Reding's work and uses Ian Campell's
> preparatory patches. It comes with full support for CPU_ON/OFF PSCI
> services. The algorithm used in this version for turning CPUs on and
> off was proposed by Thierry Reding in
> http://thread.gmane.org/gmane.comp.boot-loaders.u-boot/210881. It

I can't take full credit for this algorithm, it was originally Peter De
Schrijver who proposed it.

> consists of first enabling CPU1..3 via the PMC, just to powergate them
> again with the help of the Flow Controller. Once the Flow Controller is
> in place, we can leave the PMC alone while processing CPU_ON and CPU_OFF
> PSCI requests.
> 
> Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
> ---
>  arch/arm/cpu/armv7/Makefile               |   1 +
>  arch/arm/cpu/armv7/tegra-common/Makefile  |   1 +
>  arch/arm/cpu/armv7/tegra-common/psci.S    | 101 ++++++++++++++++++++++++++++++
>  arch/arm/cpu/armv7/tegra124/Makefile      |   7 +++
>  arch/arm/cpu/armv7/tegra124/ap.c          |  44 +++++++++++++
>  arch/arm/include/asm/arch-tegra124/flow.h |   5 ++
>  6 files changed, 159 insertions(+)
>  create mode 100644 arch/arm/cpu/armv7/tegra-common/psci.S
>  create mode 100644 arch/arm/cpu/armv7/tegra124/Makefile
>  create mode 100644 arch/arm/cpu/armv7/tegra124/ap.c
> 
> diff --git a/arch/arm/cpu/armv7/Makefile b/arch/arm/cpu/armv7/Makefile
> index 409e6f5..616b6cc 100644
> --- a/arch/arm/cpu/armv7/Makefile
> +++ b/arch/arm/cpu/armv7/Makefile
> @@ -59,6 +59,7 @@ obj-$(CONFIG_SOCFPGA) += socfpga/
>  obj-$(if $(filter stv0991,$(SOC)),y) += stv0991/
>  obj-$(CONFIG_ARCH_SUNXI) += sunxi/
>  obj-$(CONFIG_TEGRA20) += tegra20/
> +obj-$(CONFIG_TEGRA124) += tegra124/
>  obj-$(CONFIG_U8500) += u8500/
>  obj-$(CONFIG_ARCH_UNIPHIER) += uniphier/
>  obj-$(CONFIG_VF610) += vf610/
> diff --git a/arch/arm/cpu/armv7/tegra-common/Makefile b/arch/arm/cpu/armv7/tegra-common/Makefile
> index 463c260..89355ca 100644
> --- a/arch/arm/cpu/armv7/tegra-common/Makefile
> +++ b/arch/arm/cpu/armv7/tegra-common/Makefile
> @@ -7,4 +7,5 @@
>  # SPDX-License-Identifier:	GPL-2.0+
>  #
>  
> +obj-$(CONFIG_ARMV7_PSCI) += psci.o
>  obj-$(CONFIG_CMD_ENTERRCM) += cmd_enterrcm.o
> diff --git a/arch/arm/cpu/armv7/tegra-common/psci.S b/arch/arm/cpu/armv7/tegra-common/psci.S
> new file mode 100644
> index 0000000..b7501fb
> --- /dev/null
> +++ b/arch/arm/cpu/armv7/tegra-common/psci.S
> @@ -0,0 +1,101 @@
> +/*
> + * Copyright (C) 2014, NVIDIA
> + * Copyright (C) 2015, Siemens AG
> + *
> + * Authors:
> + *  Thierry Reding <treding@nvidia.com>
> + *  Jan Kiszka <jan.kiszka@siemens.com>
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#include <linux/linkage.h>
> +#include <asm/psci.h>
> +
> +	.pushsection ._secure.text, "ax"
> +	.arch_extension sec
> +
> +#define TEGRA_SB_CSR_0			0x6000c200
> +#define NS_RST_VEC_WR_DIS		(1 << 1)
> +
> +#define TEGRA_RESET_EXCEPTION_VECTOR	0x6000f100
> +
> +#define TEGRA_FLOW_CTRL_BASE		0x60007000
> +#define FLOW_CTRL_CPU_CSR		0x08
> +#define CSR_ENABLE			(1 << 0)
> +#define CSR_IMMEDIATE_WAKE		(1 << 3)
> +#define CSR_WAIT_WFI_SHIFT		8
> +#define FLOW_CTRL_CPU1_CSR		0x18
> +
> +@ converts CPU ID into FLOW_CTRL_CPUn_CSR offset
> +.macro get_csr_reg cpu, ofs, tmp
> +	cmp	\cpu, #0		@ CPU0?
> +	lsl	\tmp, \cpu, #3	@ multiple by 8 (register offset CPU1-3)
> +	moveq	\ofs, #FLOW_CTRL_CPU_CSR
> +	addne	\ofs, \tmp, #FLOW_CTRL_CPU1_CSR - 8
> +.endm
> +
> +ENTRY(psci_arch_init)
> +	mov	r6, lr
> +
> +	mrc	p15, 0, r5, c1, c1, 0	@ Read SCR
> +	bic	r5, r5, #1		@ Secure mode
> +	mcr	p15, 0, r5, c1, c1, 0	@ Write SCR
> +	isb
> +
> +	@ lock reset vector
> +	ldr	r4, =TEGRA_SB_CSR_0
> +	ldr	r5, [r4]
> +	orr	r5, r5, #NS_RST_VEC_WR_DIS
> +	str	r5, [r4]
> +
> +	mrc	p15, 0, r4, c0, c0, 5	@ MPIDR
> +	and	r4, r4, #7		@ number of CPUs in cluster

The comment here is somewhat confusing. Should this perhaps be something
like "index of CPU in cluster"?

> +	bl	psci_get_cpu_stack_top
> +	mov	sp, r5
> +
> +	bx	r6
> +ENDPROC(psci_arch_init)
> +
> +ENTRY(psci_cpu_off)
> +	bl psci_cpu_off_common
> +
> +	mrc	p15, 0, r1, c0, c0, 5		@ MPIDR
> +	and	r1, r1, #7			@ number of CPUs in cluster

Same here.

> diff --git a/arch/arm/cpu/armv7/tegra124/ap.c b/arch/arm/cpu/armv7/tegra124/ap.c
[...]

I think this code should work on Tegra114 as well. I'll go try them out
and confirm that. If it works out it would be nice to share this across
the two generations. It might even work on Tegra30, too.

> @@ -0,0 +1,44 @@
> +/*
> + * (C) Copyright 2015, Siemens AG
> + * Author: Jan Kiszka <jan.kiszka@siemens.com>
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <asm/io.h>
> +#include <asm/arch/flow.h>
> +#include <asm/arch/powergate.h>
> +#include <asm/arch-tegra/ap.h>
> +#include <asm/arch-tegra/pmc.h>
> +
> +static void park_cpu(void)
> +{
> +	while (1)
> +		asm volatile("wfi");
> +}
> +
> +void ap_pm_init(void)
> +{
> +	struct flow_ctlr *flow = (struct flow_ctlr *)NV_PA_FLOW_BASE;
> +	struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
> +
> +	writel((u32)park_cpu, EXCEP_VECTOR_CPU_RESET_VECTOR);
> +
> +	tegra_powergate_power_on(TEGRA_POWERGATE_CPU1);
> +	tegra_powergate_power_on(TEGRA_POWERGATE_CPU2);
> +	tegra_powergate_power_on(TEGRA_POWERGATE_CPU3);
> +
> +	writel((2 << CSR_WAIT_WFI_SHIFT) | CSR_ENABLE, &flow->cpu1_csr);
> +	writel((4 << CSR_WAIT_WFI_SHIFT) | CSR_ENABLE, &flow->cpu2_csr);
> +	writel((8 << CSR_WAIT_WFI_SHIFT) | CSR_ENABLE, &flow->cpu3_csr);
> +
> +	writel(EVENT_MODE_STOP, &flow->halt_cpu1_events);
> +	writel(EVENT_MODE_STOP, &flow->halt_cpu2_events);
> +	writel(EVENT_MODE_STOP, &flow->halt_cpu3_events);
> +
> +	while (readl(&pmc->pmc_pwrgate_status) & ((1 << TEGRA_POWERGATE_CPU1) |
> +						  (1 << TEGRA_POWERGATE_CPU2) |
> +						  (1 << TEGRA_POWERGATE_CPU3)))
> +		/* wait */;

Perhaps the wait should be folded into tegra_powergate_power_on()? I'm
not sure it's allowed to queue changes for more than a single partition
at a time.

Thierry
Thierry Reding Feb. 19, 2015, 9:04 a.m. UTC | #3
One more thing...

On Mon, Feb 16, 2015 at 01:54:46PM +0100, Jan Kiszka wrote:
> diff --git a/arch/arm/cpu/armv7/tegra-common/psci.S b/arch/arm/cpu/armv7/tegra-common/psci.S
[...]
> +ENTRY(psci_arch_init)
> +	mov	r6, lr
> +
> +	mrc	p15, 0, r5, c1, c1, 0	@ Read SCR
> +	bic	r5, r5, #1		@ Secure mode
> +	mcr	p15, 0, r5, c1, c1, 0	@ Write SCR
> +	isb
> +
> +	@ lock reset vector
> +	ldr	r4, =TEGRA_SB_CSR_0
> +	ldr	r5, [r4]
> +	orr	r5, r5, #NS_RST_VEC_WR_DIS
> +	str	r5, [r4]

Perhaps extend the comment to mention that this locks the reset vector
for accesses from non-secure mode, otherwise it might be confusing that
psci_cpu_on actually writes the reset vector.

Thierry
diff mbox

Patch

diff --git a/arch/arm/cpu/armv7/Makefile b/arch/arm/cpu/armv7/Makefile
index 409e6f5..616b6cc 100644
--- a/arch/arm/cpu/armv7/Makefile
+++ b/arch/arm/cpu/armv7/Makefile
@@ -59,6 +59,7 @@  obj-$(CONFIG_SOCFPGA) += socfpga/
 obj-$(if $(filter stv0991,$(SOC)),y) += stv0991/
 obj-$(CONFIG_ARCH_SUNXI) += sunxi/
 obj-$(CONFIG_TEGRA20) += tegra20/
+obj-$(CONFIG_TEGRA124) += tegra124/
 obj-$(CONFIG_U8500) += u8500/
 obj-$(CONFIG_ARCH_UNIPHIER) += uniphier/
 obj-$(CONFIG_VF610) += vf610/
diff --git a/arch/arm/cpu/armv7/tegra-common/Makefile b/arch/arm/cpu/armv7/tegra-common/Makefile
index 463c260..89355ca 100644
--- a/arch/arm/cpu/armv7/tegra-common/Makefile
+++ b/arch/arm/cpu/armv7/tegra-common/Makefile
@@ -7,4 +7,5 @@ 
 # SPDX-License-Identifier:	GPL-2.0+
 #
 
+obj-$(CONFIG_ARMV7_PSCI) += psci.o
 obj-$(CONFIG_CMD_ENTERRCM) += cmd_enterrcm.o
diff --git a/arch/arm/cpu/armv7/tegra-common/psci.S b/arch/arm/cpu/armv7/tegra-common/psci.S
new file mode 100644
index 0000000..b7501fb
--- /dev/null
+++ b/arch/arm/cpu/armv7/tegra-common/psci.S
@@ -0,0 +1,101 @@ 
+/*
+ * Copyright (C) 2014, NVIDIA
+ * Copyright (C) 2015, Siemens AG
+ *
+ * Authors:
+ *  Thierry Reding <treding@nvidia.com>
+ *  Jan Kiszka <jan.kiszka@siemens.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <linux/linkage.h>
+#include <asm/psci.h>
+
+	.pushsection ._secure.text, "ax"
+	.arch_extension sec
+
+#define TEGRA_SB_CSR_0			0x6000c200
+#define NS_RST_VEC_WR_DIS		(1 << 1)
+
+#define TEGRA_RESET_EXCEPTION_VECTOR	0x6000f100
+
+#define TEGRA_FLOW_CTRL_BASE		0x60007000
+#define FLOW_CTRL_CPU_CSR		0x08
+#define CSR_ENABLE			(1 << 0)
+#define CSR_IMMEDIATE_WAKE		(1 << 3)
+#define CSR_WAIT_WFI_SHIFT		8
+#define FLOW_CTRL_CPU1_CSR		0x18
+
+@ converts CPU ID into FLOW_CTRL_CPUn_CSR offset
+.macro get_csr_reg cpu, ofs, tmp
+	cmp	\cpu, #0		@ CPU0?
+	lsl	\tmp, \cpu, #3	@ multiple by 8 (register offset CPU1-3)
+	moveq	\ofs, #FLOW_CTRL_CPU_CSR
+	addne	\ofs, \tmp, #FLOW_CTRL_CPU1_CSR - 8
+.endm
+
+ENTRY(psci_arch_init)
+	mov	r6, lr
+
+	mrc	p15, 0, r5, c1, c1, 0	@ Read SCR
+	bic	r5, r5, #1		@ Secure mode
+	mcr	p15, 0, r5, c1, c1, 0	@ Write SCR
+	isb
+
+	@ lock reset vector
+	ldr	r4, =TEGRA_SB_CSR_0
+	ldr	r5, [r4]
+	orr	r5, r5, #NS_RST_VEC_WR_DIS
+	str	r5, [r4]
+
+	mrc	p15, 0, r4, c0, c0, 5	@ MPIDR
+	and	r4, r4, #7		@ number of CPUs in cluster
+	bl	psci_get_cpu_stack_top
+	mov	sp, r5
+
+	bx	r6
+ENDPROC(psci_arch_init)
+
+ENTRY(psci_cpu_off)
+	bl psci_cpu_off_common
+
+	mrc	p15, 0, r1, c0, c0, 5		@ MPIDR
+	and	r1, r1, #7			@ number of CPUs in cluster
+
+	get_csr_reg r1, r2, r3
+
+	ldr	r6, =TEGRA_FLOW_CTRL_BASE
+	mov	r5, #(CSR_ENABLE)
+	add	r5, r1, lsl #CSR_WAIT_WFI_SHIFT
+	str	r5, [r6, r2]
+
+_loop:	wfi
+	b	_loop
+ENDPROC(psci_cpu_off)
+
+ENTRY(psci_cpu_on)
+	push	{lr}
+
+	mov	r4, r1
+	bl	psci_get_cpu_stack_top	@ get stack top of target CPU
+	str	r2, [r5]		@ store target PC at stack top
+	dsb
+
+	ldr	r6, =TEGRA_RESET_EXCEPTION_VECTOR
+	ldr	r5, =psci_cpu_entry
+	str	r5, [r6]
+
+	get_csr_reg r1, r2, r3
+
+	ldr	r6, =TEGRA_FLOW_CTRL_BASE
+	mov	r5, #(CSR_IMMEDIATE_WAKE | CSR_ENABLE)
+	str	r5, [r6, r2]
+
+	mov	r0, #ARM_PSCI_RET_SUCCESS	@ Return PSCI_RET_SUCCESS
+	pop	{pc}
+ENDPROC(psci_cpu_on)
+
+	.globl psci_text_end
+psci_text_end:
+	.popsection
diff --git a/arch/arm/cpu/armv7/tegra124/Makefile b/arch/arm/cpu/armv7/tegra124/Makefile
new file mode 100644
index 0000000..b907277
--- /dev/null
+++ b/arch/arm/cpu/armv7/tegra124/Makefile
@@ -0,0 +1,7 @@ 
+#
+# (C) Copyright 2015, Siemens AG
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+obj-$(CONFIG_ARMV7_PSCI) += ap.o
diff --git a/arch/arm/cpu/armv7/tegra124/ap.c b/arch/arm/cpu/armv7/tegra124/ap.c
new file mode 100644
index 0000000..eebc0ea
--- /dev/null
+++ b/arch/arm/cpu/armv7/tegra124/ap.c
@@ -0,0 +1,44 @@ 
+/*
+ * (C) Copyright 2015, Siemens AG
+ * Author: Jan Kiszka <jan.kiszka@siemens.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch/flow.h>
+#include <asm/arch/powergate.h>
+#include <asm/arch-tegra/ap.h>
+#include <asm/arch-tegra/pmc.h>
+
+static void park_cpu(void)
+{
+	while (1)
+		asm volatile("wfi");
+}
+
+void ap_pm_init(void)
+{
+	struct flow_ctlr *flow = (struct flow_ctlr *)NV_PA_FLOW_BASE;
+	struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
+
+	writel((u32)park_cpu, EXCEP_VECTOR_CPU_RESET_VECTOR);
+
+	tegra_powergate_power_on(TEGRA_POWERGATE_CPU1);
+	tegra_powergate_power_on(TEGRA_POWERGATE_CPU2);
+	tegra_powergate_power_on(TEGRA_POWERGATE_CPU3);
+
+	writel((2 << CSR_WAIT_WFI_SHIFT) | CSR_ENABLE, &flow->cpu1_csr);
+	writel((4 << CSR_WAIT_WFI_SHIFT) | CSR_ENABLE, &flow->cpu2_csr);
+	writel((8 << CSR_WAIT_WFI_SHIFT) | CSR_ENABLE, &flow->cpu3_csr);
+
+	writel(EVENT_MODE_STOP, &flow->halt_cpu1_events);
+	writel(EVENT_MODE_STOP, &flow->halt_cpu2_events);
+	writel(EVENT_MODE_STOP, &flow->halt_cpu3_events);
+
+	while (readl(&pmc->pmc_pwrgate_status) & ((1 << TEGRA_POWERGATE_CPU1) |
+						  (1 << TEGRA_POWERGATE_CPU2) |
+						  (1 << TEGRA_POWERGATE_CPU3)))
+		/* wait */;
+}
diff --git a/arch/arm/include/asm/arch-tegra124/flow.h b/arch/arm/include/asm/arch-tegra124/flow.h
index 0db1881..d5f24a0 100644
--- a/arch/arm/include/asm/arch-tegra124/flow.h
+++ b/arch/arm/include/asm/arch-tegra124/flow.h
@@ -37,4 +37,9 @@  struct flow_ctlr {
 /* FLOW_CTLR_CLUSTER_CONTROL_0 0x2c */
 #define ACTIVE_LP		(1 << 0)
 
+/* CPUn_CSR_0 */
+#define CSR_ENABLE		(1 << 0)
+#define CSR_IMMEDIATE_WAKE	(1 << 3)
+#define CSR_WAIT_WFI_SHIFT	8
+
 #endif	/*  _TEGRA124_FLOW_H_ */