Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
adam.huang
Arm Trusted Firmware
Commits
27c67f4e
Commit
27c67f4e
authored
Aug 26, 2016
by
davidcunado-arm
Committed by
GitHub
Aug 26, 2016
Browse files
Merge pull request #691 from rockchip-linux/fixes-suspend/resume-bugs
Fixes suspend/resume bugs
parents
c2229abd
bdb2763d
Changes
4
Hide whitespace changes
Inline
Side-by-side
plat/rockchip/common/include/plat_private.h
View file @
27c67f4e
...
@@ -56,6 +56,7 @@ struct rockchip_pm_ops_cb {
...
@@ -56,6 +56,7 @@ struct rockchip_pm_ops_cb {
int
(
*
sys_pwr_dm_resume
)(
void
);
int
(
*
sys_pwr_dm_resume
)(
void
);
void
(
*
sys_gbl_soft_reset
)(
void
)
__dead2
;
void
(
*
sys_gbl_soft_reset
)(
void
)
__dead2
;
void
(
*
system_off
)(
void
)
__dead2
;
void
(
*
system_off
)(
void
)
__dead2
;
void
(
*
sys_pwr_down_wfi
)(
const
psci_power_state_t
*
state_info
)
__dead2
;
};
};
/******************************************************************************
/******************************************************************************
...
...
plat/rockchip/common/plat_pm.c
View file @
27c67f4e
...
@@ -311,6 +311,18 @@ static void __dead2 rockchip_system_poweroff(void)
...
@@ -311,6 +311,18 @@ static void __dead2 rockchip_system_poweroff(void)
rockchip_ops
->
system_off
();
rockchip_ops
->
system_off
();
}
}
static
void
__dead2
rockchip_pwr_domain_pwr_down_wfi
(
const
psci_power_state_t
*
target_state
)
{
if
((
RK_CORE_PWR_STATE
(
target_state
)
==
PLAT_MAX_OFF_STATE
)
&&
(
rockchip_ops
))
{
if
(
RK_SYSTEM_PWR_STATE
(
target_state
)
==
PLAT_MAX_OFF_STATE
&&
rockchip_ops
->
sys_pwr_down_wfi
)
rockchip_ops
->
sys_pwr_down_wfi
(
target_state
);
}
psci_power_down_wfi
();
}
/*******************************************************************************
/*******************************************************************************
* Export the platform handlers via plat_rockchip_psci_pm_ops. The rockchip
* Export the platform handlers via plat_rockchip_psci_pm_ops. The rockchip
* standard
* standard
...
@@ -323,6 +335,7 @@ const plat_psci_ops_t plat_rockchip_psci_pm_ops = {
...
@@ -323,6 +335,7 @@ const plat_psci_ops_t plat_rockchip_psci_pm_ops = {
.
pwr_domain_suspend
=
rockchip_pwr_domain_suspend
,
.
pwr_domain_suspend
=
rockchip_pwr_domain_suspend
,
.
pwr_domain_on_finish
=
rockchip_pwr_domain_on_finish
,
.
pwr_domain_on_finish
=
rockchip_pwr_domain_on_finish
,
.
pwr_domain_suspend_finish
=
rockchip_pwr_domain_suspend_finish
,
.
pwr_domain_suspend_finish
=
rockchip_pwr_domain_suspend_finish
,
.
pwr_domain_pwr_down_wfi
=
rockchip_pwr_domain_pwr_down_wfi
,
.
system_reset
=
rockchip_system_reset
,
.
system_reset
=
rockchip_system_reset
,
.
system_off
=
rockchip_system_poweroff
,
.
system_off
=
rockchip_system_poweroff
,
.
validate_power_state
=
rockchip_validate_power_state
,
.
validate_power_state
=
rockchip_validate_power_state
,
...
...
plat/rockchip/rk3399/drivers/pmu/pmu.c
View file @
27c67f4e
...
@@ -47,6 +47,7 @@
...
@@ -47,6 +47,7 @@
#include <pmu_com.h>
#include <pmu_com.h>
#include <pwm.h>
#include <pwm.h>
#include <soc.h>
#include <soc.h>
#include <bl31.h>
DEFINE_BAKERY_LOCK
(
rockchip_pd_lock
);
DEFINE_BAKERY_LOCK
(
rockchip_pd_lock
);
...
@@ -734,6 +735,90 @@ static int hlvl_pwr_domain_resume(uint32_t lvl, plat_local_state_t lvl_state)
...
@@ -734,6 +735,90 @@ static int hlvl_pwr_domain_resume(uint32_t lvl, plat_local_state_t lvl_state)
return
0
;
return
0
;
}
}
/**
* init_pmu_counts - Init timing counts in the PMU register area
*
* At various points when we power up or down parts of the system we need
* a delay to wait for power / clocks to become stable. The PMU has counters
* to help software do the delay properly. Basically, it works like this:
* - Software sets up counter values
* - When software turns on something in the PMU, the counter kicks off
* - The hardware sets a bit automatically when the counter has finished and
* software knows that the initialization is done.
*
* It's software's job to setup these counters. The hardware power on default
* for these settings is conservative, setting everything to 0x5dc0
* (750 ms in 32 kHz counts or 1 ms in 24 MHz counts).
*
* Note that some of these counters are only really used at suspend/resume
* time (for instance, that's the only time we turn off/on the oscillator) and
* others are used during normal runtime (like turning on/off a CPU or GPU) but
* it doesn't hurt to init everything at boot.
*
* Also note that these counters can run off the 32 kHz clock or the 24 MHz
* clock. While the 24 MHz clock can give us more precision, it's not always
* available (like when we turn the oscillator off at sleep time). The
* pmu_use_lf (lf: low freq) is available in power mode. Current understanding
* is that counts work like this:
* IF (pmu_use_lf == 0) || (power_mode_en == 0)
* use the 24M OSC for counts
* ELSE
* use the 32K OSC for counts
*
* Notes:
* - There is a separate bit for the PMU called PMU_24M_EN_CFG. At the moment
* we always keep that 0. This apparently choose between using the PLL as
* the source for the PMU vs. the 24M clock. If we ever set it to 1 we
* should consider how it affects these counts (if at all).
* - The power_mode_en is documented to auto-clear automatically when we leave
* "power mode". That's why most clocks are on 24M. Only timings used when
* in "power mode" are 32k.
* - In some cases the kernel may override these counts.
*
* The PMU_STABLE_CNT / PMU_OSC_CNT / PMU_PLLLOCK_CNT are important CNTs
* in power mode, we need to ensure that they are available.
*/
static
void
init_pmu_counts
(
void
)
{
/* COUNTS FOR INSIDE POWER MODE */
/*
* From limited testing, need PMU stable >= 2ms, but go overkill
* and choose 30 ms to match testing on past SoCs. Also let
* OSC have 30 ms for stabilization.
*/
mmio_write_32
(
PMU_BASE
+
PMU_STABLE_CNT
,
CYCL_32K_CNT_MS
(
30
));
mmio_write_32
(
PMU_BASE
+
PMU_OSC_CNT
,
CYCL_32K_CNT_MS
(
30
));
/* Unclear what these should be; try 3 ms */
mmio_write_32
(
PMU_BASE
+
PMU_WAKEUP_RST_CLR_CNT
,
CYCL_32K_CNT_MS
(
3
));
/* Unclear what this should be, but set the default explicitly */
mmio_write_32
(
PMU_BASE
+
PMU_TIMEOUT_CNT
,
0x5dc0
);
/* COUNTS FOR OUTSIDE POWER MODE */
/* Put something sorta conservative here until we know better */
mmio_write_32
(
PMU_BASE
+
PMU_PLLLOCK_CNT
,
CYCL_24M_CNT_MS
(
3
));
mmio_write_32
(
PMU_BASE
+
PMU_DDRIO_PWRON_CNT
,
CYCL_24M_CNT_MS
(
1
));
mmio_write_32
(
PMU_BASE
+
PMU_CENTER_PWRDN_CNT
,
CYCL_24M_CNT_MS
(
1
));
mmio_write_32
(
PMU_BASE
+
PMU_CENTER_PWRUP_CNT
,
CYCL_24M_CNT_MS
(
1
));
/*
* Set CPU/GPU to 1 us.
*
* NOTE: Even though ATF doesn't configure the GPU we'll still setup
* counts here. After all ATF controls all these other bits and also
* chooses which clock these counters use.
*/
mmio_write_32
(
PMU_BASE
+
PMU_SCU_L_PWRDN_CNT
,
CYCL_24M_CNT_US
(
1
));
mmio_write_32
(
PMU_BASE
+
PMU_SCU_L_PWRUP_CNT
,
CYCL_24M_CNT_US
(
1
));
mmio_write_32
(
PMU_BASE
+
PMU_SCU_B_PWRDN_CNT
,
CYCL_24M_CNT_US
(
1
));
mmio_write_32
(
PMU_BASE
+
PMU_SCU_B_PWRUP_CNT
,
CYCL_24M_CNT_US
(
1
));
mmio_write_32
(
PMU_BASE
+
PMU_GPU_PWRDN_CNT
,
CYCL_24M_CNT_US
(
1
));
mmio_write_32
(
PMU_BASE
+
PMU_GPU_PWRUP_CNT
,
CYCL_24M_CNT_US
(
1
));
}
static
void
sys_slp_config
(
void
)
static
void
sys_slp_config
(
void
)
{
{
uint32_t
slp_mode_cfg
=
0
;
uint32_t
slp_mode_cfg
=
0
;
...
@@ -772,57 +857,15 @@ static void sys_slp_config(void)
...
@@ -772,57 +857,15 @@ static void sys_slp_config(void)
BIT
(
PMU_OSC_DIS
)
|
BIT
(
PMU_OSC_DIS
)
|
BIT
(
PMU_PMU_USE_LF
);
BIT
(
PMU_PMU_USE_LF
);
mmio_setbits_32
(
PMU_BASE
+
PMU_WKUP_CFG4
,
BIT
(
PMU_CLUSTER_L_WKUP_EN
));
mmio_setbits_32
(
PMU_BASE
+
PMU_WKUP_CFG4
,
BIT
(
PMU_CLUSTER_B_WKUP_EN
));
mmio_setbits_32
(
PMU_BASE
+
PMU_WKUP_CFG4
,
BIT
(
PMU_GPIO_WKUP_EN
));
mmio_setbits_32
(
PMU_BASE
+
PMU_WKUP_CFG4
,
BIT
(
PMU_GPIO_WKUP_EN
));
mmio_write_32
(
PMU_BASE
+
PMU_PWRMODE_CON
,
slp_mode_cfg
);
mmio_write_32
(
PMU_BASE
+
PMU_PWRMODE_CON
,
slp_mode_cfg
);
/*
* About to switch PMU counters to 32K; switch all timings to 32K
* for simplicity even if we don't plan on using them.
*/
mmio_write_32
(
PMU_BASE
+
PMU_SCU_L_PWRDN_CNT
,
CYCL_32K_CNT_MS
(
3
));
mmio_write_32
(
PMU_BASE
+
PMU_SCU_L_PWRUP_CNT
,
CYCL_32K_CNT_MS
(
3
));
mmio_write_32
(
PMU_BASE
+
PMU_SCU_B_PWRDN_CNT
,
CYCL_32K_CNT_MS
(
3
));
mmio_write_32
(
PMU_BASE
+
PMU_SCU_B_PWRUP_CNT
,
CYCL_32K_CNT_MS
(
3
));
mmio_write_32
(
PMU_BASE
+
PMU_CENTER_PWRDN_CNT
,
CYCL_32K_CNT_MS
(
3
));
mmio_write_32
(
PMU_BASE
+
PMU_CENTER_PWRUP_CNT
,
CYCL_32K_CNT_MS
(
3
));
mmio_write_32
(
PMU_BASE
+
PMU_WAKEUP_RST_CLR_CNT
,
CYCL_32K_CNT_MS
(
3
));
mmio_write_32
(
PMU_BASE
+
PMU_OSC_CNT
,
CYCL_32K_CNT_MS
(
3
));
mmio_write_32
(
PMU_BASE
+
PMU_DDRIO_PWRON_CNT
,
CYCL_32K_CNT_MS
(
3
));
mmio_write_32
(
PMU_BASE
+
PMU_PLLLOCK_CNT
,
CYCL_32K_CNT_MS
(
3
));
mmio_write_32
(
PMU_BASE
+
PMU_PLLRST_CNT
,
CYCL_32K_CNT_MS
(
3
));
mmio_write_32
(
PMU_BASE
+
PMU_STABLE_CNT
,
CYCL_32K_CNT_MS
(
3
));
mmio_clrbits_32
(
PMU_BASE
+
PMU_SFT_CON
,
BIT
(
PMU_24M_EN_CFG
));
mmio_write_32
(
PMU_BASE
+
PMU_PLL_CON
,
PLL_PD_HW
);
mmio_write_32
(
PMU_BASE
+
PMU_PLL_CON
,
PLL_PD_HW
);
mmio_write_32
(
PMUGRF_BASE
+
PMUGRF_SOC_CON0
,
EXTERNAL_32K
);
mmio_write_32
(
PMUGRF_BASE
+
PMUGRF_SOC_CON0
,
EXTERNAL_32K
);
mmio_write_32
(
PMUGRF_BASE
,
IOMUX_CLK_32K
);
/* 32k iomux */
mmio_write_32
(
PMUGRF_BASE
,
IOMUX_CLK_32K
);
/* 32k iomux */
}
}
static
void
sys_slp_unconfig
(
void
)
{
/*
* About to switch PMU counters to 24M; switch all timings to 24M
* for simplicity even if we don't plan on using them.
*/
mmio_write_32
(
PMU_BASE
+
PMU_SCU_L_PWRDN_CNT
,
CYCL_24M_CNT_MS
(
3
));
mmio_write_32
(
PMU_BASE
+
PMU_SCU_L_PWRUP_CNT
,
CYCL_24M_CNT_MS
(
3
));
mmio_write_32
(
PMU_BASE
+
PMU_SCU_B_PWRDN_CNT
,
CYCL_24M_CNT_MS
(
3
));
mmio_write_32
(
PMU_BASE
+
PMU_SCU_B_PWRUP_CNT
,
CYCL_24M_CNT_MS
(
3
));
mmio_write_32
(
PMU_BASE
+
PMU_CENTER_PWRDN_CNT
,
CYCL_24M_CNT_MS
(
3
));
mmio_write_32
(
PMU_BASE
+
PMU_CENTER_PWRUP_CNT
,
CYCL_24M_CNT_MS
(
3
));
mmio_write_32
(
PMU_BASE
+
PMU_WAKEUP_RST_CLR_CNT
,
CYCL_24M_CNT_MS
(
3
));
mmio_write_32
(
PMU_BASE
+
PMU_OSC_CNT
,
CYCL_24M_CNT_MS
(
3
));
mmio_write_32
(
PMU_BASE
+
PMU_DDRIO_PWRON_CNT
,
CYCL_24M_CNT_MS
(
3
));
mmio_write_32
(
PMU_BASE
+
PMU_PLLLOCK_CNT
,
CYCL_24M_CNT_MS
(
3
));
mmio_write_32
(
PMU_BASE
+
PMU_PLLRST_CNT
,
CYCL_24M_CNT_MS
(
3
));
mmio_write_32
(
PMU_BASE
+
PMU_STABLE_CNT
,
CYCL_24M_CNT_MS
(
3
));
mmio_setbits_32
(
PMU_BASE
+
PMU_SFT_CON
,
BIT
(
PMU_24M_EN_CFG
));
}
static
void
set_hw_idle
(
uint32_t
hw_idle
)
static
void
set_hw_idle
(
uint32_t
hw_idle
)
{
{
mmio_setbits_32
(
PMU_BASE
+
PMU_BUS_CLR
,
hw_idle
);
mmio_setbits_32
(
PMU_BASE
+
PMU_BUS_CLR
,
hw_idle
);
...
@@ -879,6 +922,10 @@ static int sys_pwr_domain_suspend(void)
...
@@ -879,6 +922,10 @@ static int sys_pwr_domain_suspend(void)
}
}
mmio_setbits_32
(
PMU_BASE
+
PMU_PWRDN_CON
,
BIT
(
PMU_SCU_B_PWRDWN_EN
));
mmio_setbits_32
(
PMU_BASE
+
PMU_PWRDN_CON
,
BIT
(
PMU_SCU_B_PWRDWN_EN
));
/*
* Disabling PLLs/PWM/DVFS is approaching WFI which is
* the last steps in suspend.
*/
plls_suspend_prepare
();
plls_suspend_prepare
();
disable_dvfs_plls
();
disable_dvfs_plls
();
disable_pwms
();
disable_pwms
();
...
@@ -899,7 +946,16 @@ static int sys_pwr_domain_resume(void)
...
@@ -899,7 +946,16 @@ static int sys_pwr_domain_resume(void)
enable_dvfs_plls
();
enable_dvfs_plls
();
plls_resume_finish
();
plls_resume_finish
();
sys_slp_unconfig
();
/*
* The wakeup status is not cleared by itself, we need to clear it
* manually. Otherwise we will alway query some interrupt next time.
*
* NOTE: If the kernel needs to query this, we might want to stash it
* somewhere.
*/
mmio_write_32
(
PMU_BASE
+
PMU_WAKEUP_STATUS
,
0xffffffff
);
mmio_write_32
(
PMU_BASE
+
PMU_WKUP_CFG4
,
0x00
);
mmio_write_32
(
SGRF_BASE
+
SGRF_SOC_CON0_1
(
1
),
mmio_write_32
(
SGRF_BASE
+
SGRF_SOC_CON0_1
(
1
),
(
cpu_warm_boot_addr
>>
CPU_BOOT_ADDR_ALIGN
)
|
(
cpu_warm_boot_addr
>>
CPU_BOOT_ADDR_ALIGN
)
|
...
@@ -993,6 +1049,42 @@ void __dead2 soc_system_off(void)
...
@@ -993,6 +1049,42 @@ void __dead2 soc_system_off(void)
while
(
1
)
while
(
1
)
;
;
}
}
static
void
__dead2
sys_pwr_down_wfi
(
const
psci_power_state_t
*
target_state
)
{
uint32_t
wakeup_status
;
/*
* Check wakeup status and abort suspend early if we see a wakeup
* event.
*
* NOTE: technically I we're supposed to just execute a wfi here and
* we'll either execute a normal suspend/resume or the wfi will be
* treated as a no-op if a wake event was present and caused an abort
* of the suspend/resume. For some reason that's not happening and if
* we execute the wfi while a wake event is pending then the whole
* system wedges.
*
* Until the above is solved this extra check prevents system wedges in
* most cases but there is still a small race condition between checking
* PMU_WAKEUP_STATUS and executing wfi. If a wake event happens in
* there then we will die.
*/
wakeup_status
=
mmio_read_32
(
PMU_BASE
+
PMU_WAKEUP_STATUS
);
if
(
wakeup_status
)
{
WARN
(
"early wake, will not enter power mode.
\n
"
);
mmio_write_32
(
PMU_BASE
+
PMU_PWRMODE_CON
,
0
);
disable_mmu_icache_el3
();
bl31_warm_entrypoint
();
while
(
1
)
;
}
else
{
/* Enter WFI */
psci_power_down_wfi
();
}
}
static
struct
rockchip_pm_ops_cb
pm_ops
=
{
static
struct
rockchip_pm_ops_cb
pm_ops
=
{
.
cores_pwr_dm_on
=
cores_pwr_domain_on
,
.
cores_pwr_dm_on
=
cores_pwr_domain_on
,
...
@@ -1008,6 +1100,7 @@ static struct rockchip_pm_ops_cb pm_ops = {
...
@@ -1008,6 +1100,7 @@ static struct rockchip_pm_ops_cb pm_ops = {
.
sys_pwr_dm_resume
=
sys_pwr_domain_resume
,
.
sys_pwr_dm_resume
=
sys_pwr_domain_resume
,
.
sys_gbl_soft_reset
=
soc_soft_reset
,
.
sys_gbl_soft_reset
=
soc_soft_reset
,
.
system_off
=
soc_system_off
,
.
system_off
=
soc_system_off
,
.
sys_pwr_down_wfi
=
sys_pwr_down_wfi
,
};
};
void
plat_rockchip_pmu_init
(
void
)
void
plat_rockchip_pmu_init
(
void
)
...
@@ -1037,6 +1130,14 @@ void plat_rockchip_pmu_init(void)
...
@@ -1037,6 +1130,14 @@ void plat_rockchip_pmu_init(void)
CPU_BOOT_ADDR_WMASK
);
CPU_BOOT_ADDR_WMASK
);
mmio_write_32
(
PMU_BASE
+
PMU_NOC_AUTO_ENA
,
NOC_AUTO_ENABLE
);
mmio_write_32
(
PMU_BASE
+
PMU_NOC_AUTO_ENA
,
NOC_AUTO_ENABLE
);
/*
* Enable Schmitt trigger for better 32 kHz input signal, which is
* important for suspend/resume reliability among other things.
*/
mmio_write_32
(
PMUGRF_BASE
+
PMUGRF_GPIO0A_SMT
,
GPIO0A0_SMT_ENABLE
);
init_pmu_counts
();
nonboot_cpus_off
();
nonboot_cpus_off
();
INFO
(
"%s(%d): pd status %x
\n
"
,
__func__
,
__LINE__
,
INFO
(
"%s(%d): pd status %x
\n
"
,
__func__
,
__LINE__
,
...
...
plat/rockchip/rk3399/drivers/pmu/pmu.h
View file @
27c67f4e
...
@@ -819,6 +819,7 @@ enum pmu_core_pwr_st {
...
@@ -819,6 +819,7 @@ enum pmu_core_pwr_st {
#define AP_PWROFF 0x0a
#define AP_PWROFF 0x0a
#define GPIO0A0_SMT_ENABLE BITS_WITH_WMASK(1, 3, 0)
#define GPIO1A6_IOMUX BITS_WITH_WMASK(0, 3, 12)
#define GPIO1A6_IOMUX BITS_WITH_WMASK(0, 3, 12)
#define TSADC_INT_PIN 38
#define TSADC_INT_PIN 38
...
@@ -876,6 +877,7 @@ enum pmu_core_pwr_st {
...
@@ -876,6 +877,7 @@ enum pmu_core_pwr_st {
#define GRF_SOC_CON4 0x0e210
#define GRF_SOC_CON4 0x0e210
#define GRF_GPIO4C_IOMUX 0x0e028
#define GRF_GPIO4C_IOMUX 0x0e028
#define PMUGRF_GPIO0A_SMT 0x0120
#define PMUGRF_SOC_CON0 0x0180
#define PMUGRF_SOC_CON0 0x0180
#define CCI_FORCE_WAKEUP WMSK_BIT(8)
#define CCI_FORCE_WAKEUP WMSK_BIT(8)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment