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
c335ad48
Commit
c335ad48
authored
Feb 26, 2020
by
Olivier Deprez
Committed by
TrustedFirmware Code Review
Feb 26, 2020
Browse files
Merge "allwinner: Implement PSCI system suspend using SCPI" into integration
parents
fbe228b1
e382c88e
Changes
3
Show whitespace changes
Inline
Side-by-side
plat/allwinner/common/allwinner-common.mk
View file @
c335ad48
...
...
@@ -21,6 +21,7 @@ PLAT_BL_COMMON_SOURCES := drivers/ti/uart/${ARCH}/16550_console.S \
BL31_SOURCES
+=
drivers/allwinner/axp/common.c
\
drivers/allwinner/sunxi_msgbox.c
\
drivers/arm/css/scpi/css_scpi.c
\
drivers/arm/gic/common/gic_common.c
\
drivers/arm/gic/v2/gicv2_helpers.c
\
drivers/arm/gic/v2/gicv2_main.c
\
...
...
plat/allwinner/common/include/platform_def.h
View file @
c335ad48
...
...
@@ -40,6 +40,9 @@
#define MAX_MMAP_REGIONS (3 + PLATFORM_MMAP_REGIONS)
#define MAX_XLAT_TABLES 1
#define PLAT_CSS_SCP_COM_SHARED_MEM_BASE \
(SUNXI_SRAM_A2_BASE + SUNXI_SRAM_A2_SIZE - 0x200)
#define PLAT_MAX_PWR_LVL_STATES U(2)
#define PLAT_MAX_RET_STATE U(1)
#define PLAT_MAX_OFF_STATE U(2)
...
...
plat/allwinner/common/sunxi_pm.c
View file @
c335ad48
/*
* Copyright (c) 2017-20
19
, ARM Limited and Contributors. All rights reserved.
* Copyright (c) 2017-20
20
, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
...
...
@@ -10,6 +10,7 @@
#include <arch_helpers.h>
#include <common/debug.h>
#include <drivers/arm/css/css_scpi.h>
#include <drivers/arm/gicv2.h>
#include <drivers/delay_timer.h>
#include <lib/mmio.h>
...
...
@@ -17,6 +18,7 @@
#include <plat/common/platform.h>
#include <sunxi_cpucfg.h>
#include <sunxi_def.h>
#include <sunxi_mmap.h>
#include <sunxi_private.h>
...
...
@@ -24,25 +26,88 @@
#define SUNXI_WDOG0_CFG_REG (SUNXI_R_WDOG_BASE + 0x0014)
#define SUNXI_WDOG0_MODE_REG (SUNXI_R_WDOG_BASE + 0x0018)
#define mpidr_is_valid(mpidr) ( \
MPIDR_AFFLVL3_VAL(mpidr) == 0 && \
MPIDR_AFFLVL2_VAL(mpidr) == 0 && \
MPIDR_AFFLVL1_VAL(mpidr) < PLATFORM_CLUSTER_COUNT && \
MPIDR_AFFLVL0_VAL(mpidr) < PLATFORM_MAX_CPUS_PER_CLUSTER)
#define CPU_PWR_LVL MPIDR_AFFLVL0
#define CLUSTER_PWR_LVL MPIDR_AFFLVL1
#define SYSTEM_PWR_LVL MPIDR_AFFLVL2
#define CPU_PWR_STATE(state) \
((state)->pwr_domain_state[CPU_PWR_LVL])
#define CLUSTER_PWR_STATE(state) \
((state)->pwr_domain_state[CLUSTER_PWR_LVL])
#define SYSTEM_PWR_STATE(state) \
((state)->pwr_domain_state[SYSTEM_PWR_LVL])
#define mpidr_is_valid(mpidr) (plat_core_pos_by_mpidr(mpidr) >= 0)
/*
* The addresses for the SCP exception vectors are defined in the or1k
* architecture specification.
*/
#define OR1K_VEC_FIRST 0x01
#define OR1K_VEC_LAST 0x0e
#define OR1K_VEC_ADDR(n) (0x100 * (n))
/*
* This magic value is the little-endian representation of the or1k
* instruction "l.mfspr r2, r0, 0x12", which is guaranteed to be the
* first instruction in the SCP firmware.
*/
#define SCP_FIRMWARE_MAGIC 0xb4400012
static
bool
scpi_available
;
static
inline
scpi_power_state_t
scpi_map_state
(
plat_local_state_t
psci_state
)
{
if
(
is_local_state_run
(
psci_state
))
return
scpi_power_on
;
if
(
is_local_state_retn
(
psci_state
))
return
scpi_power_retention
;
return
scpi_power_off
;
}
static
void
sunxi_cpu_standby
(
plat_local_state_t
cpu_state
)
{
u_register_t
scr
=
read_scr_el3
();
assert
(
is_local_state_retn
(
cpu_state
));
write_scr_el3
(
scr
|
SCR_IRQ_BIT
);
wfi
();
write_scr_el3
(
scr
);
}
static
int
sunxi_pwr_domain_on
(
u_register_t
mpidr
)
{
if
(
mpidr_is_valid
(
mpidr
)
==
0
)
return
PSCI_E_INTERN_FAIL
;
if
(
scpi_available
)
{
scpi_set_css_power_state
(
mpidr
,
scpi_power_on
,
scpi_power_on
,
scpi_power_on
);
}
else
{
sunxi_cpu_on
(
mpidr
);
}
return
PSCI_E_SUCCESS
;
}
static
void
sunxi_pwr_domain_off
(
const
psci_power_state_t
*
target_state
)
{
plat_local_state_t
cpu_pwr_state
=
CPU_PWR_STATE
(
target_state
);
plat_local_state_t
cluster_pwr_state
=
CLUSTER_PWR_STATE
(
target_state
);
plat_local_state_t
system_pwr_state
=
SYSTEM_PWR_STATE
(
target_state
);
if
(
is_local_state_off
(
cpu_pwr_state
))
gicv2_cpuif_disable
();
if
(
scpi_available
)
{
scpi_set_css_power_state
(
read_mpidr
(),
scpi_map_state
(
cpu_pwr_state
),
scpi_map_state
(
cluster_pwr_state
),
scpi_map_state
(
system_pwr_state
));
}
}
static
void
__dead2
sunxi_pwr_down_wfi
(
const
psci_power_state_t
*
target_state
)
...
...
@@ -55,12 +120,26 @@ static void __dead2 sunxi_pwr_down_wfi(const psci_power_state_t *target_state)
static
void
sunxi_pwr_domain_on_finish
(
const
psci_power_state_t
*
target_state
)
{
if
(
is_local_state_off
(
SYSTEM_PWR_STATE
(
target_state
)))
gicv2_distif_init
();
if
(
is_local_state_off
(
CPU_PWR_STATE
(
target_state
)))
{
gicv2_pcpu_distif_init
();
gicv2_cpuif_enable
();
}
}
static
void
__dead2
sunxi_system_off
(
void
)
{
gicv2_cpuif_disable
();
if
(
scpi_available
)
{
/* Send the power down request to the SCP */
uint32_t
ret
=
scpi_sys_power_state
(
scpi_system_shutdown
);
if
(
ret
!=
SCP_OK
)
ERROR
(
"PSCI: SCPI %s failed: %d
\n
"
,
"shutdown"
,
ret
);
}
/* Turn off all secondary CPUs */
sunxi_disable_secondary_cpus
(
read_mpidr
());
...
...
@@ -74,6 +153,16 @@ static void __dead2 sunxi_system_off(void)
static
void
__dead2
sunxi_system_reset
(
void
)
{
gicv2_cpuif_disable
();
if
(
scpi_available
)
{
/* Send the system reset request to the SCP */
uint32_t
ret
=
scpi_sys_power_state
(
scpi_system_reboot
);
if
(
ret
!=
SCP_OK
)
ERROR
(
"PSCI: SCPI %s failed: %d
\n
"
,
"reboot"
,
ret
);
}
/* Reset the whole system when the watchdog times out */
mmio_write_32
(
SUNXI_WDOG0_CFG_REG
,
1
);
/* Enable the watchdog with the shortest timeout (0.5 seconds) */
...
...
@@ -86,6 +175,40 @@ static void __dead2 sunxi_system_reset(void)
panic
();
}
static
int
sunxi_validate_power_state
(
unsigned
int
power_state
,
psci_power_state_t
*
req_state
)
{
unsigned
int
power_level
=
psci_get_pstate_pwrlvl
(
power_state
);
unsigned
int
type
=
psci_get_pstate_type
(
power_state
);
assert
(
req_state
!=
NULL
);
if
(
power_level
>
PLAT_MAX_PWR_LVL
)
return
PSCI_E_INVALID_PARAMS
;
if
(
type
==
PSTATE_TYPE_STANDBY
)
{
/* Only one retention power state is supported. */
if
(
psci_get_pstate_id
(
power_state
)
>
0
)
return
PSCI_E_INVALID_PARAMS
;
/* The SoC cannot be suspended without losing state */
if
(
power_level
==
SYSTEM_PWR_LVL
)
return
PSCI_E_INVALID_PARAMS
;
for
(
unsigned
int
i
=
0
;
i
<=
power_level
;
++
i
)
req_state
->
pwr_domain_state
[
i
]
=
PLAT_MAX_RET_STATE
;
}
else
{
/* Only one off power state is supported. */
if
(
psci_get_pstate_id
(
power_state
)
>
0
)
return
PSCI_E_INVALID_PARAMS
;
for
(
unsigned
int
i
=
0
;
i
<=
power_level
;
++
i
)
req_state
->
pwr_domain_state
[
i
]
=
PLAT_MAX_OFF_STATE
;
}
/* Higher power domain levels should all remain running */
for
(
unsigned
int
i
=
power_level
+
1
;
i
<=
PLAT_MAX_PWR_LVL
;
++
i
)
req_state
->
pwr_domain_state
[
i
]
=
PSCI_LOCAL_STATE_RUN
;
return
PSCI_E_SUCCESS
;
}
static
int
sunxi_validate_ns_entrypoint
(
uintptr_t
ns_entrypoint
)
{
/* The non-secure entry point must be in DRAM */
...
...
@@ -95,13 +218,45 @@ static int sunxi_validate_ns_entrypoint(uintptr_t ns_entrypoint)
return
PSCI_E_INVALID_ADDRESS
;
}
static
void
sunxi_get_sys_suspend_power_state
(
psci_power_state_t
*
req_state
)
{
assert
(
req_state
);
for
(
unsigned
int
i
=
0
;
i
<=
PLAT_MAX_PWR_LVL
;
++
i
)
req_state
->
pwr_domain_state
[
i
]
=
PLAT_MAX_OFF_STATE
;
}
static
int
sunxi_get_node_hw_state
(
u_register_t
mpidr
,
unsigned
int
power_level
)
{
unsigned
int
cluster_state
,
cpu_state
;
unsigned
int
cpu
=
MPIDR_AFFLVL0_VAL
(
mpidr
);
/* SoC power level (always on if PSCI works). */
if
(
power_level
==
SYSTEM_PWR_LVL
)
return
HW_ON
;
if
(
scpi_get_css_power_state
(
mpidr
,
&
cpu_state
,
&
cluster_state
))
return
PSCI_E_NOT_SUPPORTED
;
/* Cluster power level (full power state available). */
if
(
power_level
==
CLUSTER_PWR_LVL
)
{
if
(
cluster_state
==
scpi_power_on
)
return
HW_ON
;
if
(
cluster_state
==
scpi_power_retention
)
return
HW_STANDBY
;
return
HW_OFF
;
}
/* CPU power level (one bit boolean for on or off). */
return
((
cpu_state
&
BIT
(
cpu
))
!=
0
)
?
HW_ON
:
HW_OFF
;
}
static
plat_psci_ops_t
sunxi_psci_ops
=
{
.
cpu_standby
=
sunxi_cpu_standby
,
.
pwr_domain_on
=
sunxi_pwr_domain_on
,
.
pwr_domain_off
=
sunxi_pwr_domain_off
,
.
pwr_domain_pwr_down_wfi
=
sunxi_pwr_down_wfi
,
.
pwr_domain_on_finish
=
sunxi_pwr_domain_on_finish
,
.
system_off
=
sunxi_system_off
,
.
system_reset
=
sunxi_system_reset
,
.
validate_power_state
=
sunxi_validate_power_state
,
.
validate_ns_entrypoint
=
sunxi_validate_ns_entrypoint
,
};
...
...
@@ -110,13 +265,44 @@ int plat_setup_psci_ops(uintptr_t sec_entrypoint,
{
assert
(
psci_ops
);
for
(
int
cpu
=
0
;
cpu
<
PLATFORM_CORE_COUNT
;
cpu
+=
1
)
{
/* Program all CPU entry points. */
for
(
unsigned
int
cpu
=
0
;
cpu
<
PLATFORM_CORE_COUNT
;
++
cpu
)
{
mmio_write_32
(
SUNXI_CPUCFG_RVBAR_LO_REG
(
cpu
),
sec_entrypoint
&
0xffffffff
);
mmio_write_32
(
SUNXI_CPUCFG_RVBAR_HI_REG
(
cpu
),
sec_entrypoint
>>
32
);
}
/* Check for a valid SCP firmware, and boot the SCP if found. */
if
(
mmio_read_32
(
SUNXI_SCP_BASE
)
==
SCP_FIRMWARE_MAGIC
)
{
/* Program SCP exception vectors to the firmware entrypoint. */
for
(
unsigned
int
i
=
OR1K_VEC_FIRST
;
i
<=
OR1K_VEC_LAST
;
++
i
)
{
uint32_t
vector
=
SUNXI_SRAM_A2_BASE
+
OR1K_VEC_ADDR
(
i
);
uint32_t
offset
=
SUNXI_SCP_BASE
-
vector
;
mmio_write_32
(
vector
,
offset
>>
2
);
clean_dcache_range
(
vector
,
sizeof
(
uint32_t
));
}
/* Take the SCP out of reset. */
mmio_setbits_32
(
SUNXI_R_CPUCFG_BASE
,
BIT
(
0
));
/* Wait for the SCP firmware to boot. */
if
(
scpi_wait_ready
()
==
0
)
scpi_available
=
true
;
}
NOTICE
(
"PSCI: System suspend is %s
\n
"
,
scpi_available
?
"available via SCPI"
:
"unavailable"
);
if
(
scpi_available
)
{
/* Suspend is only available via SCPI. */
sunxi_psci_ops
.
pwr_domain_suspend
=
sunxi_pwr_domain_off
;
sunxi_psci_ops
.
pwr_domain_suspend_finish
=
sunxi_pwr_domain_on_finish
;
sunxi_psci_ops
.
get_sys_suspend_power_state
=
sunxi_get_sys_suspend_power_state
;
sunxi_psci_ops
.
get_node_hw_state
=
sunxi_get_node_hw_state
;
}
else
{
/* This is only needed when SCPI is unavailable. */
sunxi_psci_ops
.
pwr_domain_pwr_down_wfi
=
sunxi_pwr_down_wfi
;
}
*
psci_ops
=
&
sunxi_psci_ops
;
return
0
;
...
...
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