tegra132: psci: add cpu_on/off support

The CPU on/off functions are the method for the Kernel to support CPU
hot-plug function in PSCI. To support this, we still need flow controller
support to capture the WFI from the CPU and inform PMC to power gate the
CPU core. On the other path, we turn on the CPU by toggling the PMC and
use flow controller to let go when the power is steady.

BUG=chrome-os-partner:32136
BRANCH=None
TEST=built the kernel with PSCI enabled,
     check both of the CPUs are coming up,
     test the CPU hot-plug is working on Ryu

Change-Id: If2c529b6719c5747d5aea95fb5049b2d7353ff17
Signed-off-by: Patrick Georgi <pgeorgi@chromium.org>
Original-Commit-Id: 0f078e89daad1c4d8b342a395f36b3e922af66f5
Original-Change-Id: Ie49940adb2966dcc9967d2fcc9b1e0dcd6d98743
Original-Signed-off-by: Joseph Lo <josephl@nvidia.com>
Original-Reviewed-on: https://chromium-review.googlesource.com/231267
Original-Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: http://review.coreboot.org/9542
Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net>
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
This commit is contained in:
Joseph Lo 2014-11-21 08:54:15 +08:00 committed by Patrick Georgi
parent 14b444b83b
commit e28fd3626f
4 changed files with 197 additions and 2 deletions

View File

@ -97,6 +97,8 @@ ramstage-$(CONFIG_ARCH_USE_SECURE_MONITOR) += secmon.c
secmon-$(CONFIG_ARCH_USE_SECURE_MONITOR) += 32bit_reset.S secmon-$(CONFIG_ARCH_USE_SECURE_MONITOR) += 32bit_reset.S
secmon-$(CONFIG_ARCH_USE_SECURE_MONITOR) += cpu.c secmon-$(CONFIG_ARCH_USE_SECURE_MONITOR) += cpu.c
secmon-$(CONFIG_ARCH_USE_SECURE_MONITOR) += cpu_lib.S secmon-$(CONFIG_ARCH_USE_SECURE_MONITOR) += cpu_lib.S
secmon-$(CONFIG_ARCH_USE_SECURE_MONITOR) += flow_ctrl.c
secmon-$(CONFIG_ARCH_USE_SECURE_MONITOR) += power.c
secmon-$(CONFIG_ARCH_USE_SECURE_MONITOR) += psci.c secmon-$(CONFIG_ARCH_USE_SECURE_MONITOR) += psci.c
secmon-$(CONFIG_ARCH_USE_SECURE_MONITOR) += uart.c secmon-$(CONFIG_ARCH_USE_SECURE_MONITOR) += uart.c

View File

@ -0,0 +1,96 @@
/*
* This file is part of the coreboot project.
*
* Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <arch/io.h>
#include <soc/addressmap.h>
#include <soc/flow_ctrl.h>
#define FLOW_CTRL_HALT_CPU0_EVENTS 0x0
#define FLOW_CTRL_WAITEVENT (2 << 29)
#define FLOW_CTRL_WAIT_FOR_INTERRUPT (4 << 29)
#define FLOW_CTRL_HALT_LIC_IRQ (1 << 11)
#define FLOW_CTRL_HALT_LIC_FIQ (1 << 10)
#define FLOW_CTRL_CPU0_CSR 0x8
#define FLOW_CTRL_CSR_INTR_FLAG (1 << 15)
#define FLOW_CTRL_CSR_EVENT_FLAG (1 << 14)
#define FLOW_CTRL_CSR_WFI_CPU0 (1 << 8)
#define FLOW_CTRL_CSR_WFI_BITMAP (0xF << 8)
#define FLOW_CTRL_CSR_WFE_BITMAP (0xF << 4)
#define FLOW_CTRL_CSR_ENABLE (1 << 0)
#define FLOW_CTRL_HALT_CPU1_EVENTS 0x14
#define FLOW_CTRL_CPU1_CSR 0x18
#define HALT_REG_CORE0 (\
FLOW_CTRL_WAIT_FOR_INTERRUPT | \
FLOW_CTRL_HALT_LIC_IRQ | \
FLOW_CTRL_HALT_LIC_FIQ)
#define HALT_REG_CORE1 FLOW_CTRL_WAITEVENT
static void *tegra_flowctrl_base = (void*)TEGRA_FLOW_BASE;
static const uint8_t flowctrl_offset_halt_cpu[] = {
FLOW_CTRL_HALT_CPU0_EVENTS,
FLOW_CTRL_HALT_CPU1_EVENTS
};
static const uint8_t flowctrl_offset_cpu_csr[] = {
FLOW_CTRL_CPU0_CSR,
FLOW_CTRL_CPU1_CSR
};
static uint32_t flowctrl_read_cpu_csr(int cpu)
{
return read32(tegra_flowctrl_base + flowctrl_offset_cpu_csr[cpu]);
}
static void flowctrl_write_cpu_csr(int cpu, uint32_t val)
{
write32(val, tegra_flowctrl_base + flowctrl_offset_cpu_csr[cpu]);
val = readl(tegra_flowctrl_base + flowctrl_offset_cpu_csr[cpu]);
}
void flowctrl_write_cpu_halt(int cpu, uint32_t val)
{
writel(val, tegra_flowctrl_base + flowctrl_offset_halt_cpu[cpu]);
val = readl(tegra_flowctrl_base + flowctrl_offset_halt_cpu[cpu]);
}
static void flowctrl_prepare_cpu_off(int cpu)
{
uint32_t reg;
reg = flowctrl_read_cpu_csr(cpu);
reg &= ~FLOW_CTRL_CSR_WFE_BITMAP; /* clear wfe bitmap */
reg &= ~FLOW_CTRL_CSR_WFI_BITMAP; /* clear wfi bitmap */
reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr flag */
reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event flag */
reg |= FLOW_CTRL_CSR_WFI_CPU0 << cpu; /* power gating on wfi */
reg |= FLOW_CTRL_CSR_ENABLE; /* enable power gating */
flowctrl_write_cpu_csr(cpu, reg);
}
void flowctrl_cpu_off(int cpu)
{
uint32_t reg;
reg = cpu ? HALT_REG_CORE1 : HALT_REG_CORE0;
flowctrl_prepare_cpu_off(cpu);
flowctrl_write_cpu_halt(cpu, reg);
}

View File

@ -0,0 +1,26 @@
/*
* This file is part of the coreboot project.
*
* Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _TEGRA132_FLOW_CTRL_H_
#define _TEGRA132_FLOW_CTRL_H_
void flowctrl_cpu_off(int cpu);
void flowctrl_write_cpu_halt(int cpu, uint32_t val);
#endif

View File

@ -17,8 +17,16 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
*/ */
#include <arch/cpu.h>
#include <arch/io.h>
#include <arch/psci.h> #include <arch/psci.h>
#include <soc/addressmap.h>
#include <soc/clk_rst.h>
#include <soc/cpu.h> #include <soc/cpu.h>
#include <soc/flow_ctrl.h>
#include <soc/power.h>
#include <console/console.h>
static void *cpu_on_entry_point; static void *cpu_on_entry_point;
@ -57,14 +65,77 @@ static size_t children_at_level(int parent_level, uint64_t mpidr)
} }
} }
#define TEGRA132_PM_CORE_C7 0x3
static inline void tegra132_enter_sleep(unsigned long pmstate)
{
asm volatile(
" isb\n"
" msr actlr_el1, %0\n"
" wfi\n"
:
: "r" (pmstate));
}
static void prepare_cpu_on(int cpu)
{
uint32_t partid;
partid = cpu ? POWER_PARTID_CE1 : POWER_PARTID_CE0;
power_ungate_partition(partid);
flowctrl_write_cpu_halt(cpu, 0);
}
static int cmd_prepare(struct psci_cmd *cmd) static int cmd_prepare(struct psci_cmd *cmd)
{ {
return PSCI_RET_NOT_SUPPORTED; int ret;
switch (cmd->type) {
case PSCI_CMD_ON:
prepare_cpu_on(cmd->target->cpu_state.ci->id);
ret = PSCI_RET_SUCCESS;
break;
case PSCI_CMD_OFF:
if (cmd->state_id != -1) {
ret = PSCI_RET_INVALID_PARAMETERS;
break;
}
cmd->state_id = TEGRA132_PM_CORE_C7;
ret = PSCI_RET_SUCCESS;
break;
default:
ret = PSCI_RET_NOT_SUPPORTED;
break;
}
return ret;
} }
static int cmd_commit(struct psci_cmd *cmd) static int cmd_commit(struct psci_cmd *cmd)
{ {
return PSCI_RET_NOT_SUPPORTED; int ret;
struct cpu_info *ci;
ci = cmd->target->cpu_state.ci;
switch (cmd->type) {
case PSCI_CMD_ON:
/* Take CPU out of reset */
start_cpu_silent(ci->id, cpu_on_entry_point);
ret = PSCI_RET_SUCCESS;
break;
case PSCI_CMD_OFF:
flowctrl_cpu_off(ci->id);
tegra132_enter_sleep(cmd->state_id);
/* Never reach here */
ret = PSCI_RET_NOT_SUPPORTED;
printk(BIOS_ERR, "t132 CPU%d PSCI_CMD_OFF fail\n", ci->id);
break;
default:
ret = PSCI_RET_NOT_SUPPORTED;
break;
}
return ret;
} }
struct psci_soc_ops soc_psci_ops = { struct psci_soc_ops soc_psci_ops = {