Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,24 @@
# SPDX-License-Identifier: GPL-2.0-only
config ARCH_ROCKCHIP
bool "Rockchip RK2928 and RK3xxx SOCs"
depends on ARCH_MULTI_V7
select PINCTRL
select PINCTRL_ROCKCHIP
select ARCH_HAS_RESET_CONTROLLER
select ARM_AMBA
select ARM_GIC
select CACHE_L2X0
select GPIOLIB
select HAVE_ARM_ARCH_TIMER
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if SMP
select DW_APB_TIMER_OF
select REGULATOR
select ROCKCHIP_TIMER
select ARM_GLOBAL_TIMER
select CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
select ZONE_DMA if ARM_LPAE
select PM
help
Support for Rockchip's Cortex-A9 Single-to-Quad-Core-SoCs
containing the RK2928, RK30xx and RK31xx series.
@@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
CFLAGS_platsmp.o := -march=armv7-a
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip.o
obj-$(CONFIG_PM_SLEEP) += pm.o sleep.o
obj-$(CONFIG_SMP) += headsmp.o platsmp.o
+10
View File
@@ -0,0 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) 2013 MundoReader S.L.
* Author: Heiko Stuebner <heiko@sntech.de>
*/
extern char rockchip_secondary_trampoline;
extern char rockchip_secondary_trampoline_end;
extern unsigned long rockchip_boot_fn;
@@ -0,0 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) 2013 MundoReader S.L.
* Author: Heiko Stuebner <heiko@sntech.de>
*/
#include <linux/linkage.h>
#include <linux/init.h>
ENTRY(rockchip_secondary_trampoline)
ldr pc, 1f
ENDPROC(rockchip_secondary_trampoline)
.globl rockchip_boot_fn
rockchip_boot_fn:
1: .space 4
ENTRY(rockchip_secondary_trampoline_end)
@@ -0,0 +1,381 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2013 MundoReader S.L.
* Author: Heiko Stuebner <heiko@sntech.de>
*/
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/smp.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <linux/reset.h>
#include <linux/cpu.h>
#include <asm/cacheflush.h>
#include <asm/cp15.h>
#include <asm/smp_scu.h>
#include <asm/smp_plat.h>
#include <asm/mach/map.h>
#include "core.h"
static void __iomem *scu_base_addr;
static void __iomem *sram_base_addr;
static int ncores;
#define PMU_PWRDN_CON 0x08
#define PMU_PWRDN_ST 0x0c
#define PMU_PWRDN_SCU 4
static struct regmap *pmu;
static int has_pmu = true;
static struct reset_control *cpu_rstc[4];
static int pmu_power_domain_is_on(int pd)
{
u32 val;
int ret;
ret = regmap_read(pmu, PMU_PWRDN_ST, &val);
if (ret < 0)
return ret;
return !(val & BIT(pd));
}
static struct reset_control *rockchip_get_core_reset(int cpu)
{
struct device *dev = get_cpu_device(cpu);
struct device_node *np;
/* The cpu device is only available after the initial core bringup */
if (dev)
np = dev->of_node;
else
np = of_get_cpu_node(cpu, NULL);
return of_reset_control_get_exclusive(np, NULL);
}
static int pmu_set_power_domain(int pd, bool on)
{
u32 val = (on) ? 0 : BIT(pd);
struct reset_control *rstc;
int ret;
rstc = pd < ARRAY_SIZE(cpu_rstc) ? cpu_rstc[pd] : ERR_PTR(-EINVAL);
if (IS_ERR(rstc) && read_cpuid_part() != ARM_CPU_PART_CORTEX_A9) {
pr_err("%s: could not get reset control for core %d\n",
__func__, pd);
return PTR_ERR(rstc);
}
/*
* We need to soft reset the cpu when we turn off the cpu power domain,
* or else the active processors might be stalled when the individual
* processor is powered down.
*/
if (!IS_ERR(rstc) && !on)
reset_control_assert(rstc);
if (has_pmu) {
ret = regmap_update_bits(pmu, PMU_PWRDN_CON, BIT(pd), val);
if (ret < 0) {
pr_err("%s: could not update power domain\n",
__func__);
return ret;
}
ret = -1;
while (ret != on) {
ret = pmu_power_domain_is_on(pd);
if (ret < 0) {
pr_err("%s: could not read power domain state\n",
__func__);
return ret;
}
}
}
if (!IS_ERR(rstc) && on)
reset_control_deassert(rstc);
return 0;
}
/*
* Handling of CPU cores
*/
static int rockchip_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
int ret;
if (!sram_base_addr || (has_pmu && !pmu)) {
pr_err("%s: sram or pmu missing for cpu boot\n", __func__);
return -ENXIO;
}
if (cpu >= ncores) {
pr_err("%s: cpu %d outside maximum number of cpus %d\n",
__func__, cpu, ncores);
return -ENXIO;
}
/* start the core */
ret = pmu_set_power_domain(0 + cpu, true);
if (ret < 0)
return ret;
if (read_cpuid_part() != ARM_CPU_PART_CORTEX_A9) {
/*
* We communicate with the bootrom to active the cpus other
* than cpu0, after a blob of initialize code, they will
* stay at wfe state, once they are activated, they will check
* the mailbox:
* sram_base_addr + 4: 0xdeadbeaf
* sram_base_addr + 8: start address for pc
* The cpu0 need to wait the other cpus other than cpu0 entering
* the wfe state.The wait time is affected by many aspects.
* (e.g: cpu frequency, bootrom frequency, sram frequency, ...)
*/
mdelay(1); /* ensure the cpus other than cpu0 to startup */
writel(__pa_symbol(secondary_startup), sram_base_addr + 8);
writel(0xDEADBEAF, sram_base_addr + 4);
dsb_sev();
}
return 0;
}
/**
* rockchip_smp_prepare_sram - populate necessary sram block
* Starting cores execute the code residing at the start of the on-chip sram
* after power-on. Therefore make sure, this sram region is reserved and
* big enough. After this check, copy the trampoline code that directs the
* core to the real startup code in ram into the sram-region.
* @node: mmio-sram device node
*/
static int __init rockchip_smp_prepare_sram(struct device_node *node)
{
unsigned int trampoline_sz = &rockchip_secondary_trampoline_end -
&rockchip_secondary_trampoline;
struct resource res;
unsigned int rsize;
int ret;
ret = of_address_to_resource(node, 0, &res);
if (ret < 0) {
pr_err("%s: could not get address for node %pOF\n",
__func__, node);
return ret;
}
rsize = resource_size(&res);
if (rsize < trampoline_sz) {
pr_err("%s: reserved block with size 0x%x is too small for trampoline size 0x%x\n",
__func__, rsize, trampoline_sz);
return -EINVAL;
}
/* set the boot function for the sram code */
rockchip_boot_fn = __pa_symbol(secondary_startup);
/* copy the trampoline to sram, that runs during startup of the core */
memcpy_toio(sram_base_addr, &rockchip_secondary_trampoline, trampoline_sz);
flush_cache_all();
outer_clean_range(0, trampoline_sz);
dsb_sev();
return 0;
}
static const struct regmap_config rockchip_pmu_regmap_config = {
.name = "rockchip-pmu",
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};
static int __init rockchip_smp_prepare_pmu(void)
{
struct device_node *node;
void __iomem *pmu_base;
/*
* This function is only called via smp_ops->smp_prepare_cpu().
* That only happens if a "/cpus" device tree node exists
* and has an "enable-method" property that selects the SMP
* operations defined herein.
*/
node = of_find_node_by_path("/cpus");
pmu = syscon_regmap_lookup_by_phandle(node, "rockchip,pmu");
of_node_put(node);
if (!IS_ERR(pmu))
return 0;
pmu = syscon_regmap_lookup_by_compatible("rockchip,rk3066-pmu");
if (!IS_ERR(pmu))
return 0;
/* fallback, create our own regmap for the pmu area */
pmu = NULL;
node = of_find_compatible_node(NULL, NULL, "rockchip,rk3066-pmu");
if (!node) {
pr_err("%s: could not find pmu dt node\n", __func__);
return -ENODEV;
}
pmu_base = of_iomap(node, 0);
of_node_put(node);
if (!pmu_base) {
pr_err("%s: could not map pmu registers\n", __func__);
return -ENOMEM;
}
pmu = regmap_init_mmio(NULL, pmu_base, &rockchip_pmu_regmap_config);
if (IS_ERR(pmu)) {
int ret = PTR_ERR(pmu);
iounmap(pmu_base);
pmu = NULL;
pr_err("%s: regmap init failed\n", __func__);
return ret;
}
return 0;
}
static void __init rockchip_smp_prepare_cpus(unsigned int max_cpus)
{
struct device_node *node;
unsigned int i;
node = of_find_compatible_node(NULL, NULL, "rockchip,rk3066-smp-sram");
if (!node) {
pr_err("%s: could not find sram dt node\n", __func__);
return;
}
sram_base_addr = of_iomap(node, 0);
if (!sram_base_addr) {
pr_err("%s: could not map sram registers\n", __func__);
of_node_put(node);
return;
}
if (has_pmu && rockchip_smp_prepare_pmu()) {
of_node_put(node);
return;
}
if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
/* enable the SCU power domain */
pmu_set_power_domain(PMU_PWRDN_SCU, true);
of_node_put(node);
node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
if (!node) {
pr_err("%s: missing scu\n", __func__);
return;
}
scu_base_addr = of_iomap(node, 0);
if (!scu_base_addr) {
pr_err("%s: could not map scu registers\n", __func__);
of_node_put(node);
return;
}
/*
* While the number of cpus is gathered from dt, also get the
* number of cores from the scu to verify this value when
* booting the cores.
*/
ncores = scu_get_core_count(scu_base_addr);
pr_err("%s: ncores %d\n", __func__, ncores);
scu_enable(scu_base_addr);
} else {
unsigned int l2ctlr;
asm ("mrc p15, 1, %0, c9, c0, 2\n" : "=r" (l2ctlr));
ncores = ((l2ctlr >> 24) & 0x3) + 1;
}
/* Collect cpu core reset control for each core */
for (i = 0; i < ncores; i++)
cpu_rstc[i] = rockchip_get_core_reset(i);
/* Make sure that all cores except the first are really off */
for (i = 1; i < ncores; i++)
pmu_set_power_domain(0 + i, false);
if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
if (rockchip_smp_prepare_sram(node)) {
of_node_put(node);
return;
}
}
of_node_put(node);
}
static void __init rk3036_smp_prepare_cpus(unsigned int max_cpus)
{
has_pmu = false;
rockchip_smp_prepare_cpus(max_cpus);
}
#ifdef CONFIG_HOTPLUG_CPU
static int rockchip_cpu_kill(unsigned int cpu)
{
/*
* We need a delay here to ensure that the dying CPU can finish
* executing v7_coherency_exit() and reach the WFI/WFE state
* prior to having the power domain disabled.
*/
mdelay(1);
pmu_set_power_domain(0 + cpu, false);
return 1;
}
static void rockchip_cpu_die(unsigned int cpu)
{
v7_exit_coherency_flush(louis);
while (1)
cpu_do_idle();
}
#endif
static const struct smp_operations rk3036_smp_ops __initconst = {
.smp_prepare_cpus = rk3036_smp_prepare_cpus,
.smp_boot_secondary = rockchip_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_kill = rockchip_cpu_kill,
.cpu_die = rockchip_cpu_die,
#endif
};
static const struct smp_operations rockchip_smp_ops __initconst = {
.smp_prepare_cpus = rockchip_smp_prepare_cpus,
.smp_boot_secondary = rockchip_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_kill = rockchip_cpu_kill,
.cpu_die = rockchip_cpu_die,
#endif
};
CPU_METHOD_OF_DECLARE(rk3036_smp, "rockchip,rk3036-smp", &rk3036_smp_ops);
CPU_METHOD_OF_DECLARE(rk3066_smp, "rockchip,rk3066-smp", &rockchip_smp_ops);
+331
View File
@@ -0,0 +1,331 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
* Author: Tony Xie <tony.xie@rock-chips.com>
*/
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/regmap.h>
#include <linux/suspend.h>
#include <linux/mfd/syscon.h>
#include <linux/regulator/machine.h>
#include <asm/cacheflush.h>
#include <asm/tlbflush.h>
#include <asm/suspend.h>
#include "pm.h"
/* These enum are option of low power mode */
enum {
ROCKCHIP_ARM_OFF_LOGIC_NORMAL = 0,
ROCKCHIP_ARM_OFF_LOGIC_DEEP = 1,
};
struct rockchip_pm_data {
const struct platform_suspend_ops *ops;
int (*init)(struct device_node *np);
};
static void __iomem *rk3288_bootram_base;
static phys_addr_t rk3288_bootram_phy;
static struct regmap *pmu_regmap;
static struct regmap *sgrf_regmap;
static struct regmap *grf_regmap;
static u32 rk3288_pmu_pwr_mode_con;
static u32 rk3288_sgrf_soc_con0;
static u32 rk3288_sgrf_cpu_con0;
static inline u32 rk3288_l2_config(void)
{
u32 l2ctlr;
asm("mrc p15, 1, %0, c9, c0, 2" : "=r" (l2ctlr));
return l2ctlr;
}
static void __init rk3288_config_bootdata(void)
{
rkpm_bootdata_cpusp = rk3288_bootram_phy + (SZ_4K - 8);
rkpm_bootdata_cpu_code = __pa_symbol(cpu_resume);
rkpm_bootdata_l2ctlr_f = 1;
rkpm_bootdata_l2ctlr = rk3288_l2_config();
}
#define GRF_UOC0_CON0 0x320
#define GRF_UOC1_CON0 0x334
#define GRF_UOC2_CON0 0x348
#define GRF_SIDDQ BIT(13)
static bool rk3288_slp_disable_osc(void)
{
static const u32 reg_offset[] = { GRF_UOC0_CON0, GRF_UOC1_CON0,
GRF_UOC2_CON0 };
u32 reg, i;
/*
* if any usb phy is still on(GRF_SIDDQ==0), that means we need the
* function of usb wakeup, so do not switch to 32khz, since the usb phy
* clk does not connect to 32khz osc
*/
for (i = 0; i < ARRAY_SIZE(reg_offset); i++) {
regmap_read(grf_regmap, reg_offset[i], &reg);
if (!(reg & GRF_SIDDQ))
return false;
}
return true;
}
static void rk3288_slp_mode_set(int level)
{
u32 mode_set, mode_set1;
bool osc_disable = rk3288_slp_disable_osc();
regmap_read(sgrf_regmap, RK3288_SGRF_CPU_CON0, &rk3288_sgrf_cpu_con0);
regmap_read(sgrf_regmap, RK3288_SGRF_SOC_CON0, &rk3288_sgrf_soc_con0);
regmap_read(pmu_regmap, RK3288_PMU_PWRMODE_CON,
&rk3288_pmu_pwr_mode_con);
/*
* SGRF_FAST_BOOT_EN - system to boot from FAST_BOOT_ADDR
* PCLK_WDT_GATE - disable WDT during suspend.
*/
regmap_write(sgrf_regmap, RK3288_SGRF_SOC_CON0,
SGRF_PCLK_WDT_GATE | SGRF_FAST_BOOT_EN
| SGRF_PCLK_WDT_GATE_WRITE | SGRF_FAST_BOOT_EN_WRITE);
/*
* The dapswjdp can not auto reset before resume, that cause it may
* access some illegal address during resume. Let's disable it before
* suspend, and the MASKROM will enable it back.
*/
regmap_write(sgrf_regmap, RK3288_SGRF_CPU_CON0, SGRF_DAPDEVICEEN_WRITE);
/* booting address of resuming system is from this register value */
regmap_write(sgrf_regmap, RK3288_SGRF_FAST_BOOT_ADDR,
rk3288_bootram_phy);
mode_set = BIT(PMU_GLOBAL_INT_DISABLE) | BIT(PMU_L2FLUSH_EN) |
BIT(PMU_SREF0_ENTER_EN) | BIT(PMU_SREF1_ENTER_EN) |
BIT(PMU_DDR0_GATING_EN) | BIT(PMU_DDR1_GATING_EN) |
BIT(PMU_PWR_MODE_EN) | BIT(PMU_CHIP_PD_EN) |
BIT(PMU_SCU_EN);
mode_set1 = BIT(PMU_CLR_CORE) | BIT(PMU_CLR_CPUP);
if (level == ROCKCHIP_ARM_OFF_LOGIC_DEEP) {
/* arm off, logic deep sleep */
mode_set |= BIT(PMU_BUS_PD_EN) | BIT(PMU_PMU_USE_LF) |
BIT(PMU_DDR1IO_RET_EN) | BIT(PMU_DDR0IO_RET_EN) |
BIT(PMU_ALIVE_USE_LF) | BIT(PMU_PLL_PD_EN);
if (osc_disable)
mode_set |= BIT(PMU_OSC_24M_DIS);
mode_set1 |= BIT(PMU_CLR_ALIVE) | BIT(PMU_CLR_BUS) |
BIT(PMU_CLR_PERI) | BIT(PMU_CLR_DMA);
regmap_write(pmu_regmap, RK3288_PMU_WAKEUP_CFG1,
PMU_ARMINT_WAKEUP_EN);
/*
* In deep suspend we use PMU_PMU_USE_LF to let the rk3288
* switch its main clock supply to the alternative 32kHz
* source. Therefore set 30ms on a 32kHz clock for pmic
* stabilization. Similar 30ms on 24MHz for the other
* mode below.
*/
regmap_write(pmu_regmap, RK3288_PMU_STABL_CNT, 32 * 30);
/* only wait for stabilization, if we turned the osc off */
regmap_write(pmu_regmap, RK3288_PMU_OSC_CNT,
osc_disable ? 32 * 30 : 0);
} else {
/*
* arm off, logic normal
* if pmu_clk_core_src_gate_en is not set,
* wakeup will be error
*/
mode_set |= BIT(PMU_CLK_CORE_SRC_GATE_EN);
regmap_write(pmu_regmap, RK3288_PMU_WAKEUP_CFG1,
PMU_ARMINT_WAKEUP_EN | PMU_GPIOINT_WAKEUP_EN);
/* 30ms on a 24MHz clock for pmic stabilization */
regmap_write(pmu_regmap, RK3288_PMU_STABL_CNT, 24000 * 30);
/* oscillator is still running, so no need to wait */
regmap_write(pmu_regmap, RK3288_PMU_OSC_CNT, 0);
}
regmap_write(pmu_regmap, RK3288_PMU_PWRMODE_CON, mode_set);
regmap_write(pmu_regmap, RK3288_PMU_PWRMODE_CON1, mode_set1);
}
static void rk3288_slp_mode_set_resume(void)
{
regmap_write(sgrf_regmap, RK3288_SGRF_CPU_CON0,
rk3288_sgrf_cpu_con0 | SGRF_DAPDEVICEEN_WRITE);
regmap_write(pmu_regmap, RK3288_PMU_PWRMODE_CON,
rk3288_pmu_pwr_mode_con);
regmap_write(sgrf_regmap, RK3288_SGRF_SOC_CON0,
rk3288_sgrf_soc_con0 | SGRF_PCLK_WDT_GATE_WRITE
| SGRF_FAST_BOOT_EN_WRITE);
}
static int rockchip_lpmode_enter(unsigned long arg)
{
flush_cache_all();
cpu_do_idle();
pr_err("%s: Failed to suspend\n", __func__);
return 1;
}
static int rk3288_suspend_enter(suspend_state_t state)
{
local_fiq_disable();
rk3288_slp_mode_set(ROCKCHIP_ARM_OFF_LOGIC_NORMAL);
cpu_suspend(0, rockchip_lpmode_enter);
rk3288_slp_mode_set_resume();
local_fiq_enable();
return 0;
}
static int rk3288_suspend_prepare(void)
{
return regulator_suspend_prepare(PM_SUSPEND_MEM);
}
static void rk3288_suspend_finish(void)
{
if (regulator_suspend_finish())
pr_err("%s: Suspend finish failed\n", __func__);
}
static int __init rk3288_suspend_init(struct device_node *np)
{
struct device_node *sram_np;
struct resource res;
int ret;
pmu_regmap = syscon_node_to_regmap(np);
if (IS_ERR(pmu_regmap)) {
pr_err("%s: could not find pmu regmap\n", __func__);
return PTR_ERR(pmu_regmap);
}
sgrf_regmap = syscon_regmap_lookup_by_compatible(
"rockchip,rk3288-sgrf");
if (IS_ERR(sgrf_regmap)) {
pr_err("%s: could not find sgrf regmap\n", __func__);
return PTR_ERR(sgrf_regmap);
}
grf_regmap = syscon_regmap_lookup_by_compatible(
"rockchip,rk3288-grf");
if (IS_ERR(grf_regmap)) {
pr_err("%s: could not find grf regmap\n", __func__);
return PTR_ERR(grf_regmap);
}
sram_np = of_find_compatible_node(NULL, NULL,
"rockchip,rk3288-pmu-sram");
if (!sram_np) {
pr_err("%s: could not find bootram dt node\n", __func__);
return -ENODEV;
}
rk3288_bootram_base = of_iomap(sram_np, 0);
if (!rk3288_bootram_base) {
pr_err("%s: could not map bootram base\n", __func__);
of_node_put(sram_np);
return -ENOMEM;
}
ret = of_address_to_resource(sram_np, 0, &res);
if (ret) {
pr_err("%s: could not get bootram phy addr\n", __func__);
of_node_put(sram_np);
return ret;
}
rk3288_bootram_phy = res.start;
of_node_put(sram_np);
rk3288_config_bootdata();
/* copy resume code and data to bootsram */
memcpy(rk3288_bootram_base, rockchip_slp_cpu_resume,
rk3288_bootram_sz);
return 0;
}
static const struct platform_suspend_ops rk3288_suspend_ops = {
.enter = rk3288_suspend_enter,
.valid = suspend_valid_only_mem,
.prepare = rk3288_suspend_prepare,
.finish = rk3288_suspend_finish,
};
static const struct rockchip_pm_data rk3288_pm_data __initconst = {
.ops = &rk3288_suspend_ops,
.init = rk3288_suspend_init,
};
static const struct of_device_id rockchip_pmu_of_device_ids[] __initconst = {
{
.compatible = "rockchip,rk3288-pmu",
.data = &rk3288_pm_data,
},
{ /* sentinel */ },
};
void __init rockchip_suspend_init(void)
{
const struct rockchip_pm_data *pm_data;
const struct of_device_id *match;
struct device_node *np;
int ret;
np = of_find_matching_node_and_match(NULL, rockchip_pmu_of_device_ids,
&match);
if (!match) {
pr_err("Failed to find PMU node\n");
goto out_put;
}
pm_data = (struct rockchip_pm_data *) match->data;
if (pm_data->init) {
ret = pm_data->init(np);
if (ret) {
pr_err("%s: matches init error %d\n", __func__, ret);
goto out_put;
}
}
suspend_set_ops(pm_data->ops);
out_put:
of_node_put(np);
}
+97
View File
@@ -0,0 +1,97 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
* Author: Tony Xie <tony.xie@rock-chips.com>
*/
#ifndef __MACH_ROCKCHIP_PM_H
#define __MACH_ROCKCHIP_PM_H
extern unsigned long rkpm_bootdata_cpusp;
extern unsigned long rkpm_bootdata_cpu_code;
extern unsigned long rkpm_bootdata_l2ctlr_f;
extern unsigned long rkpm_bootdata_l2ctlr;
extern unsigned long rkpm_bootdata_ddr_code;
extern unsigned long rkpm_bootdata_ddr_data;
extern unsigned long rk3288_bootram_sz;
void rockchip_slp_cpu_resume(void);
#ifdef CONFIG_PM_SLEEP
void __init rockchip_suspend_init(void);
#else
static inline void rockchip_suspend_init(void)
{
}
#endif
/****** following is rk3288 defined **********/
#define RK3288_PMU_WAKEUP_CFG0 0x00
#define RK3288_PMU_WAKEUP_CFG1 0x04
#define RK3288_PMU_PWRMODE_CON 0x18
#define RK3288_PMU_OSC_CNT 0x20
#define RK3288_PMU_PLL_CNT 0x24
#define RK3288_PMU_STABL_CNT 0x28
#define RK3288_PMU_DDR0IO_PWRON_CNT 0x2c
#define RK3288_PMU_DDR1IO_PWRON_CNT 0x30
#define RK3288_PMU_CORE_PWRDWN_CNT 0x34
#define RK3288_PMU_CORE_PWRUP_CNT 0x38
#define RK3288_PMU_GPU_PWRDWN_CNT 0x3c
#define RK3288_PMU_GPU_PWRUP_CNT 0x40
#define RK3288_PMU_WAKEUP_RST_CLR_CNT 0x44
#define RK3288_PMU_PWRMODE_CON1 0x90
#define RK3288_SGRF_SOC_CON0 (0x0000)
#define RK3288_SGRF_FAST_BOOT_ADDR (0x0120)
#define SGRF_PCLK_WDT_GATE BIT(6)
#define SGRF_PCLK_WDT_GATE_WRITE BIT(22)
#define SGRF_FAST_BOOT_EN BIT(8)
#define SGRF_FAST_BOOT_EN_WRITE BIT(24)
#define RK3288_SGRF_CPU_CON0 (0x40)
#define SGRF_DAPDEVICEEN BIT(0)
#define SGRF_DAPDEVICEEN_WRITE BIT(16)
/* PMU_WAKEUP_CFG1 bits */
#define PMU_ARMINT_WAKEUP_EN BIT(0)
#define PMU_GPIOINT_WAKEUP_EN BIT(3)
enum rk3288_pwr_mode_con {
PMU_PWR_MODE_EN = 0,
PMU_CLK_CORE_SRC_GATE_EN,
PMU_GLOBAL_INT_DISABLE,
PMU_L2FLUSH_EN,
PMU_BUS_PD_EN,
PMU_A12_0_PD_EN,
PMU_SCU_EN,
PMU_PLL_PD_EN,
PMU_CHIP_PD_EN, /* POWER OFF PIN ENABLE */
PMU_PWROFF_COMB,
PMU_ALIVE_USE_LF,
PMU_PMU_USE_LF,
PMU_OSC_24M_DIS,
PMU_INPUT_CLAMP_EN,
PMU_WAKEUP_RESET_EN,
PMU_SREF0_ENTER_EN,
PMU_SREF1_ENTER_EN,
PMU_DDR0IO_RET_EN,
PMU_DDR1IO_RET_EN,
PMU_DDR0_GATING_EN,
PMU_DDR1_GATING_EN,
PMU_DDR0IO_RET_DE_REQ,
PMU_DDR1IO_RET_DE_REQ
};
enum rk3288_pwr_mode_con1 {
PMU_CLR_BUS = 0,
PMU_CLR_CORE,
PMU_CLR_CPUP,
PMU_CLR_ALIVE,
PMU_CLR_DMA,
PMU_CLR_PERI,
PMU_CLR_GPU,
PMU_CLR_VIDEO,
PMU_CLR_HEVC,
PMU_CLR_VIO,
};
#endif /* __MACH_ROCKCHIP_PM_H */
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Device Tree support for Rockchip SoCs
*
* Copyright (c) 2013 MundoReader S.L.
* Author: Heiko Stuebner <heiko@sntech.de>
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_clk.h>
#include <linux/clocksource.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include "core.h"
#include "pm.h"
#define RK3288_TIMER6_7_PHYS 0xff810000
static void __init rockchip_timer_init(void)
{
if (of_machine_is_compatible("rockchip,rk3288")) {
void __iomem *reg_base;
/*
* Most/all uboot versions for rk3288 don't enable timer7
* which is needed for the architected timer to work.
* So make sure it is running during early boot.
*/
reg_base = ioremap(RK3288_TIMER6_7_PHYS, SZ_16K);
if (reg_base) {
writel(0, reg_base + 0x30);
writel(0xffffffff, reg_base + 0x20);
writel(0xffffffff, reg_base + 0x24);
writel(1, reg_base + 0x30);
dsb();
iounmap(reg_base);
} else {
pr_err("rockchip: could not map timer7 registers\n");
}
}
of_clk_init(NULL);
timer_probe();
}
static void __init rockchip_dt_init(void)
{
rockchip_suspend_init();
}
static const char * const rockchip_board_dt_compat[] = {
"rockchip,rk2928",
"rockchip,rk3066a",
"rockchip,rk3066b",
"rockchip,rk3188",
"rockchip,rk3228",
"rockchip,rk3288",
"rockchip,rv1108",
NULL,
};
DT_MACHINE_START(ROCKCHIP_DT, "Rockchip (Device Tree)")
.l2c_aux_val = 0,
.l2c_aux_mask = ~0,
.init_time = rockchip_timer_init,
.dt_compat = rockchip_board_dt_compat,
.init_machine = rockchip_dt_init,
MACHINE_END
@@ -0,0 +1,64 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
* Author: Tony Xie <tony.xie@rock-chips.com>
*/
#include <linux/linkage.h>
#include <asm/assembler.h>
#include <asm/page.h>
.data
/*
* this code will be copied from
* ddr to sram for system resumeing.
* so it is ".data section".
*/
.align 2
ENTRY(rockchip_slp_cpu_resume)
setmode PSR_I_BIT | PSR_F_BIT | SVC_MODE, r1 @ set svc, irqs off
mrc p15, 0, r1, c0, c0, 5
and r1, r1, #0xf
cmp r1, #0
/* olny cpu0 can continue to run, the others is halt here */
beq cpu0run
secondary_loop:
wfe
b secondary_loop
cpu0run:
ldr r3, rkpm_bootdata_l2ctlr_f
cmp r3, #0
beq sp_set
ldr r3, rkpm_bootdata_l2ctlr
mcr p15, 1, r3, c9, c0, 2
sp_set:
ldr sp, rkpm_bootdata_cpusp
ldr r1, rkpm_bootdata_cpu_code
bx r1
ENDPROC(rockchip_slp_cpu_resume)
/* Parameters filled in by the kernel */
/* Flag for whether to restore L2CTLR on resume */
.global rkpm_bootdata_l2ctlr_f
rkpm_bootdata_l2ctlr_f:
.long 0
/* Saved L2CTLR to restore on resume */
.global rkpm_bootdata_l2ctlr
rkpm_bootdata_l2ctlr:
.long 0
/* CPU resume SP addr */
.globl rkpm_bootdata_cpusp
rkpm_bootdata_cpusp:
.long 0
/* CPU resume function (physical address) */
.globl rkpm_bootdata_cpu_code
rkpm_bootdata_cpu_code:
.long 0
ENTRY(rk3288_bootram_sz)
.word . - rockchip_slp_cpu_resume