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
318c2f97
Unverified
Commit
318c2f97
authored
Oct 31, 2018
by
Antonio Niño Díaz
Committed by
GitHub
Oct 31, 2018
Browse files
Merge pull request #1646 from Andre-ARM/allwinner/pmic-v2
Allwinner/pmic v2
parents
55dd52a3
7db0c960
Changes
20
Hide whitespace changes
Inline
Side-by-side
drivers/allwinner/sunxi_rsb.c
0 → 100644
View file @
318c2f97
/*
* Copyright (c) 2017-2018 ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <debug.h>
#include <delay_timer.h>
#include <errno.h>
#include <mmio.h>
#include <sunxi_mmap.h>
#define RSB_CTRL 0x00
#define RSB_CCR 0x04
#define RSB_INTE 0x08
#define RSB_STAT 0x0c
#define RSB_DADDR0 0x10
#define RSB_DLEN 0x18
#define RSB_DATA0 0x1c
#define RSB_LCR 0x24
#define RSB_PMCR 0x28
#define RSB_CMD 0x2c
#define RSB_SADDR 0x30
#define RSBCMD_SRTA 0xE8
#define RSBCMD_RD8 0x8B
#define RSBCMD_RD16 0x9C
#define RSBCMD_RD32 0xA6
#define RSBCMD_WR8 0x4E
#define RSBCMD_WR16 0x59
#define RSBCMD_WR32 0x63
#define MAX_TRIES 100000
static
int
rsb_wait_bit
(
const
char
*
desc
,
unsigned
int
offset
,
uint32_t
mask
)
{
uint32_t
reg
,
tries
=
MAX_TRIES
;
do
reg
=
mmio_read_32
(
SUNXI_R_RSB_BASE
+
offset
);
while
((
reg
&
mask
)
&&
--
tries
);
/* transaction in progress */
if
(
reg
&
mask
)
{
ERROR
(
"%s: timed out
\n
"
,
desc
);
return
-
ETIMEDOUT
;
}
return
0
;
}
static
int
rsb_wait_stat
(
const
char
*
desc
)
{
uint32_t
reg
;
int
ret
=
rsb_wait_bit
(
desc
,
RSB_CTRL
,
BIT
(
7
));
if
(
ret
)
return
ret
;
reg
=
mmio_read_32
(
SUNXI_R_RSB_BASE
+
RSB_STAT
);
if
(
reg
==
0x01
)
return
0
;
ERROR
(
"%s: 0x%x
\n
"
,
desc
,
reg
);
return
-
reg
;
}
/* Initialize the RSB controller. */
int
rsb_init_controller
(
void
)
{
mmio_write_32
(
SUNXI_R_RSB_BASE
+
RSB_CTRL
,
0x01
);
/* soft reset */
return
rsb_wait_bit
(
"RSB: reset controller"
,
RSB_CTRL
,
BIT
(
0
));
}
int
rsb_read
(
uint8_t
rt_addr
,
uint8_t
reg_addr
)
{
int
ret
;
mmio_write_32
(
SUNXI_R_RSB_BASE
+
RSB_CMD
,
RSBCMD_RD8
);
/* read a byte */
mmio_write_32
(
SUNXI_R_RSB_BASE
+
RSB_SADDR
,
rt_addr
<<
16
);
mmio_write_32
(
SUNXI_R_RSB_BASE
+
RSB_DADDR0
,
reg_addr
);
mmio_write_32
(
SUNXI_R_RSB_BASE
+
RSB_CTRL
,
0x80
);
/* start transaction */
ret
=
rsb_wait_stat
(
"RSB: read command"
);
if
(
ret
)
return
ret
;
return
mmio_read_32
(
SUNXI_R_RSB_BASE
+
RSB_DATA0
)
&
0xff
;
/* result */
}
int
rsb_write
(
uint8_t
rt_addr
,
uint8_t
reg_addr
,
uint8_t
value
)
{
mmio_write_32
(
SUNXI_R_RSB_BASE
+
RSB_CMD
,
RSBCMD_WR8
);
/* byte write */
mmio_write_32
(
SUNXI_R_RSB_BASE
+
RSB_SADDR
,
rt_addr
<<
16
);
mmio_write_32
(
SUNXI_R_RSB_BASE
+
RSB_DADDR0
,
reg_addr
);
mmio_write_32
(
SUNXI_R_RSB_BASE
+
RSB_DATA0
,
value
);
mmio_write_32
(
SUNXI_R_RSB_BASE
+
RSB_CTRL
,
0x80
);
/* start transaction */
return
rsb_wait_stat
(
"RSB: write command"
);
}
int
rsb_set_device_mode
(
uint32_t
device_mode
)
{
mmio_write_32
(
SUNXI_R_RSB_BASE
+
RSB_PMCR
,
(
device_mode
&
0x00ffffff
)
|
BIT
(
31
));
return
rsb_wait_bit
(
"RSB: set device to RSB"
,
RSB_PMCR
,
BIT
(
31
));
}
int
rsb_set_bus_speed
(
uint32_t
source_freq
,
uint32_t
bus_freq
)
{
uint32_t
reg
;
if
(
bus_freq
==
0
)
return
-
EINVAL
;
reg
=
source_freq
/
bus_freq
;
if
(
reg
<
2
)
return
-
EINVAL
;
reg
=
reg
/
2
-
1
;
reg
|=
(
1U
<<
8
);
/* one cycle of CD output delay */
mmio_write_32
(
SUNXI_R_RSB_BASE
+
RSB_CCR
,
reg
);
return
0
;
}
/* Initialize the RSB PMIC connection. */
int
rsb_assign_runtime_address
(
uint16_t
hw_addr
,
uint8_t
rt_addr
)
{
mmio_write_32
(
SUNXI_R_RSB_BASE
+
RSB_SADDR
,
hw_addr
|
(
rt_addr
<<
16
));
mmio_write_32
(
SUNXI_R_RSB_BASE
+
RSB_CMD
,
RSBCMD_SRTA
);
mmio_write_32
(
SUNXI_R_RSB_BASE
+
RSB_CTRL
,
0x80
);
return
rsb_wait_stat
(
"RSB: set run-time address"
);
}
include/drivers/allwinner/sunxi_rsb.h
0 → 100644
View file @
318c2f97
/*
* Copyright (c) 2017-2018 ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SUNXI_RSB_H
#define SUNXI_RSB_H
#include <stdint.h>
int
rsb_init_controller
(
void
);
int
rsb_set_bus_speed
(
uint32_t
source_freq
,
uint32_t
bus_freq
);
int
rsb_set_device_mode
(
uint32_t
device_mode
);
int
rsb_assign_runtime_address
(
uint16_t
hw_addr
,
uint8_t
rt_addr
);
int
rsb_read
(
uint8_t
rt_addr
,
uint8_t
reg_addr
);
int
rsb_write
(
uint8_t
rt_addr
,
uint8_t
reg_addr
,
uint8_t
value
);
#endif
plat/allwinner/common/allwinner-common.mk
0 → 100644
View file @
318c2f97
#
# Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
include
lib/xlat_tables_v2/xlat_tables.mk
AW_PLAT
:=
plat/allwinner
PLAT_INCLUDES
:=
-Iinclude
/plat/arm/common
\
-Iinclude
/plat/arm/common/aarch64
\
-I
${AW_PLAT}
/common/include
\
-I
${AW_PLAT}
/
${PLAT}
/include
include
lib/libfdt/libfdt.mk
PLAT_BL_COMMON_SOURCES
:=
drivers/console/
${ARCH}
/console.S
\
drivers/ti/uart/
${ARCH}
/16550_console.S
\
${XLAT_TABLES_LIB_SRCS}
\
${AW_PLAT}
/common/plat_helpers.S
\
${AW_PLAT}
/common/sunxi_common.c
BL31_SOURCES
+=
drivers/arm/gic/common/gic_common.c
\
drivers/arm/gic/v2/gicv2_helpers.c
\
drivers/arm/gic/v2/gicv2_main.c
\
drivers/delay_timer/delay_timer.c
\
drivers/delay_timer/generic_delay_timer.c
\
lib/cpus/
${ARCH}
/cortex_a53.S
\
plat/common/plat_gicv2.c
\
plat/common/plat_psci_common.c
\
${AW_PLAT}
/common/sunxi_bl31_setup.c
\
${AW_PLAT}
/common/sunxi_cpu_ops.c
\
${AW_PLAT}
/common/sunxi_pm.c
\
${AW_PLAT}
/
${PLAT}
/sunxi_power.c
\
${AW_PLAT}
/common/sunxi_security.c
\
${AW_PLAT}
/common/sunxi_topology.c
# The bootloader is guaranteed to only run on CPU 0 by the boot ROM.
COLD_BOOT_SINGLE_CPU
:=
1
# Enable workarounds for Cortex-A53 errata. Allwinner uses at least r0p4.
ERRATA_A53_835769
:=
1
ERRATA_A53_843419
:=
1
ERRATA_A53_855873
:=
1
MULTI_CONSOLE_API
:=
1
# The reset vector can be changed for each CPU.
PROGRAMMABLE_RESET_ADDRESS
:=
1
# Allow mapping read-only data as execute-never.
SEPARATE_CODE_AND_RODATA
:=
1
# BL31 gets loaded alongside BL33 (U-Boot) by U-Boot's SPL
RESET_TO_BL31
:=
1
# We are short on memory, so save 3.5KB by not having an extra coherent page.
USE_COHERENT_MEM
:=
0
plat/allwinner/common/arisc_off.S
0 → 100644
View file @
318c2f97
#
turn_off_core
.
S
#
#
Copyright
(
c
)
2018
,
Andre
Przywara
<
osp
@
andrep
.
de
>
#
SPDX
-
License
-
Identifier
:
BSD
-
3
-
Clause
#
#
OpenRISC
assembly
to
turn
off
an
ARM
core
on
an
Allwinner
SoC
from
#
the
arisc
management
controller
.
#
Generate
a
binary
representation
with
:
#
$
or1k
-
elf
-
as
-
c
-
o
turn_off_core
.
o
turn_off_core
.
S
#
$
or1k
-
elf
-
objcopy
-
O
binary
--
reverse
-
bytes
=
4
turn_off_core
.
o
\
#
turn_off_core
.
bin
#
The
encoded
instructions
go
into
an
array
defined
in
#
plat
/
allwinner
/
sun50i_
*/
include
/
core_off_arisc
.
h
,
to
be
handed
off
to
#
the
arisc
processor
.
#
#
This
routine
is
meant
to
be
called
directly
from
arisc
reset
(
put
the
#
start
address
in
the
reset
vector
),
to
be
actually
triggered
by
that
#
very
ARM
core
to
be
turned
off
.
#
It
expects
the
core
number
presented
as
a
mask
in
the
upper
half
of
#
r3
,
so
to
be
patched
in
the
lower
16
bits
of
the
first
instruction
,
#
overwriting
the
0
in
this
code
here
.
#
The
code
will
do
the
following
:
#
-
Read
the
C_CPU_STATUS
register
,
which
contains
the
status
of
the
WFI
#
lines
of
each
of
the
four
A53
cores
.
#
-
Loop
until
the
core
in
question
reaches
WFI
.
#
-
Using
that
mask
,
activate
the
core
output
clamps
by
setting
the
#
respective
core
bit
in
CPUX_PWROFF_GATING_REG
(
0x1f01500
)
.
#
Note
that
the
clamp
for
core
0
covers
more
than
just
the
core
,
activating
#
it
hangs
the
whole
system
.
So
we
skip
this
step
for
core
0
.
#
-
Using
the
negated
mask
,
assert
the
core
's reset line by clearing the
#
respective
bit
in
C_RST_CTRL
(
0x1f01c30
)
.
#
-
Finally
turn
off
the
core
's power switch by writing 0xff to the
#
respective
CPUx_PWR_SWITCH_REG
(
0x1f01540
ff
.
)
#
-
Assert
the
arisc
's own reset to end execution.
#
This
also
signals
other
arisc
users
that
the
chip
is
free
again
.
#
So
in
C
this
would
look
like
:
#
while
(!(
readl
(
0x1700030
)
&
(
1
U
<<
core_nr
)))
#
;
# if (core_nr != 0)
#
writel
(
readl
(0
x1f01500
)
|
(
1
U
<<
core_nr
),
0x1f01500
)
;
#
writel
(
readl
(0
x1f01c30
)
&
~
(
1
U
<<
core_nr
),
0x1f01c30
)
;
#
writel
(0
xff
,
0x1f01540
+
(
core_nr
*
4
))
;
#
(
using
A64
/
H5
addresses
)
.
text
_start
:
l.movhi
r3
,
0
#
FIXUP
!
with
core
mask
l.movhi
r0
,
0
#
clear
r0
l.movhi
r13
,
0x170
#
r13
:
CPU_CFG_BASE
=
0x01700000
wait_wfi
:
l.lwz
r5
,
0x30
(
r13
)
#
load
C_CPU_STATUS
l.and
r5
,
r5
,
r3
#
mask
requested
core
l.sfeq
r5
,
r0
#
is
it
not
yet
in
WFI
?
l.bf
wait_wfi
#
try
again
l.srli
r6
,
r3
,
16
#
move
mask
to
lower
16
bits
l.sfeqi
r6
,
1
#
core
0
is
special
l.bf
1
f
#
don
't touch the bit for core 0
l.movhi
r13
,
0x1f0
#
address
of
R_CPUCFG
(
delay
)
l.lwz
r5
,
0x1500
(
r13
)
#
core
output
clamps
l.or
r5
,
r5
,
r6
#
set
bit
to
...
l.sw
0x1500
(
r13
),
r5
#
...
activate
for
our
core
1
:
l.lwz
r5
,
0x1c30
(
r13
)
#
CPU
power
-
on
reset
l.xori
r6
,
r6
,
-
1
#
negate
core
mask
l.and
r5
,
r5
,
r6
#
clear
bit
to
...
l.sw
0x1c30
(
r13
),
r5
#
...
assert
for
our
core
l.ff1
r6
,
r3
#
get
core
number
from
high
mask
l.addi
r6
,
r6
,
-
17
#
convert
to
0
-
3
l.slli
r6
,
r6
,
2
#
r5
:
core
number
*
4
(
0
-
12
)
l.add
r6
,
r6
,
r13
#
add
to
base
address
l.ori
r5
,
r0
,
0xff
#
0xff
means
all
switches
off
l.sw
0x1540
(
r6
),
r5
#
core
power
switch
registers
reset
:
l.sw
0x1c00
(
r13
),
r0
#
pull
down
our
own
reset
line
l.j
reset
#
just
in
case
....
l.nop
0x0
#
(
delay
slot
)
#
same
as
above
,
but
with
the
MMIO
addresses
matching
the
H6
SoC
_start_h6
:
l.movhi
r3
,
0
#
FIXUP
!
with
core
mask
l.movhi
r0
,
0
#
clear
r0
l.movhi
r13
,
0x901
#
r13
:
CPU_CFG_BASE
=
0x09010000
1
:
l.lwz
r5
,
0x80
(
r13
)
#
load
C_CPU_STATUS
l.and
r5
,
r5
,
r3
#
mask
requested
core
l.sfeq
r5
,
r0
#
is
it
not
yet
in
WFI
?
l.bf
1
b
#
try
again
l.srli
r6
,
r3
,
16
#
move
mask
to
lower
16
bits
(
ds
)
l.sfeqi
r6
,
1
#
core
0
is
special
l.bf
1
f
#
don
't touch the bit for core 0
l.movhi
r13
,
0x700
#
address
of
R_CPUCFG
(
ds
)
l.lwz
r5
,
0x0444
(
r13
)
#
core
output
clamps
l.or
r5
,
r5
,
r6
#
set
bit
to
...
l.sw
0x0444
(
r13
),
r5
#
...
activate
for
our
core
1
:
l.lwz
r5
,
0x0440
(
r13
)
#
CPU
power
-
on
reset
l.xori
r6
,
r6
,
-
1
#
negate
core
mask
l.and
r5
,
r5
,
r6
#
clear
bit
to
...
l.sw
0x0440
(
r13
),
r5
#
...
assert
for
our
core
l.ff1
r6
,
r3
#
get
core
number
from
high
mask
l.addi
r6
,
r6
,
-
17
#
convert
to
0
-
3
l.slli
r6
,
r6
,
2
#
r5
:
core
number
*
4
(
0
-
12
)
l.add
r6
,
r6
,
r13
#
add
to
base
address
l.ori
r5
,
r0
,
0xff
#
0xff
means
all
switches
off
l.sw
0x0450
(
r6
),
r5
#
core
power
switch
registers
1
:
l.sw
0x0400
(
r13
),
r0
#
pull
down
our
own
reset
line
l.j
1
b
#
just
in
case
...
l.nop
0x0
#
(
delay
slot
)
plat/allwinner/common/include/platform_def.h
View file @
318c2f97
...
...
@@ -18,11 +18,17 @@
/* The traditional U-Boot load address is 160MB into DRAM, so at 0x4a000000 */
#define PLAT_SUNXI_NS_IMAGE_OFFSET (SUNXI_DRAM_BASE + (160U << 20))
/* How much memory to reserve as secure for BL32, if configured */
#define SUNXI_DRAM_SEC_SIZE (32U << 20)
/* How much DRAM to map (to map BL33, for fetching the DTB from U-Boot) */
#define SUNXI_DRAM_MAP_SIZE (64U << 20)
#define CACHE_WRITEBACK_SHIFT 6
#define CACHE_WRITEBACK_GRANULE (1 << CACHE_WRITEBACK_SHIFT)
#define MAX_MMAP_REGIONS (
4
+ PLATFORM_MMAP_REGIONS)
#define MAX_XLAT_TABLES
2
#define MAX_MMAP_REGIONS (
3
+ PLATFORM_MMAP_REGIONS)
#define MAX_XLAT_TABLES
1
#define PLAT_MAX_PWR_LVL_STATES U(2)
#define PLAT_MAX_RET_STATE U(1)
...
...
@@ -34,13 +40,13 @@
PLATFORM_CORE_COUNT)
#define PLAT_PHY_ADDR_SPACE_SIZE (1ULL << 32)
#define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL <<
3
2)
#define PLAT_VIRT_ADDR_SPACE_SIZE (1ULL << 2
8
)
#define PLATFORM_CLUSTER_COUNT 1
#define PLATFORM_CORE_COUNT (PLATFORM_CLUSTER_COUNT * \
PLATFORM_MAX_CPUS_PER_CLUSTER)
#define PLATFORM_MAX_CPUS_PER_CLUSTER 4
#define PLATFORM_MMAP_REGIONS
3
#define PLATFORM_MMAP_REGIONS
4
#define PLATFORM_STACK_SIZE (0x1000 / PLATFORM_CORE_COUNT)
#ifndef SPD_none
...
...
plat/allwinner/common/include/sunxi_def.h
View file @
318c2f97
...
...
@@ -14,4 +14,8 @@
#define SUNXI_UART0_BAUDRATE 115200
#define SUNXI_UART0_CLK_IN_HZ SUNXI_OSC24M_CLK_IN_HZ
#define SUNXI_SOC_A64 0x1689
#define SUNXI_SOC_H5 0x1718
#define SUNXI_SOC_H6 0x1728
#endif
/* __SUNXI_DEF_H__ */
plat/allwinner/common/sunxi_private.h
→
plat/allwinner/common/
include/
sunxi_private.h
View file @
318c2f97
...
...
@@ -4,19 +4,23 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef
__
SUNXI_PRIVATE_H
__
#define
__
SUNXI_PRIVATE_H
__
#ifndef SUNXI_PRIVATE_H
#define SUNXI_PRIVATE_H
void
sunxi_configure_mmu_el3
(
int
flags
);
void
sunxi_cpu_off
(
unsigned
int
cluster
,
unsigned
int
core
);
void
sunxi_cpu_on
(
unsigned
int
cluster
,
unsigned
int
core
);
void
sunxi_cpu_off
(
unsigned
int
cluster
,
unsigned
int
core
);
void
sunxi_disable_secondary_cpus
(
unsigned
int
primary_cpu
);
void
__dead2
sunxi_power_down
(
void
);
uint16_t
sunxi_read_soc_id
(
void
);
void
sunxi_pmic_setup
(
void
);
int
sunxi_pmic_setup
(
uint16_t
socid
,
const
void
*
fdt
);
void
sunxi_security_setup
(
void
);
void
__dead2
sunxi_power_down
(
void
);
uint16_t
sunxi_read_soc_id
(
void
);
void
sunxi_set_gpio_out
(
char
port
,
int
pin
,
bool
level_high
);
int
sunxi_init_platform_r_twi
(
uint16_t
socid
,
bool
use_rsb
);
void
sunxi_execute_arisc_code
(
uint32_t
*
code
,
size_t
size
,
int
patch_offset
,
uint16_t
param
);
#endif
/*
__
SUNXI_PRIVATE_H
__
*/
#endif
/* SUNXI_PRIVATE_H */
plat/allwinner/common/sunxi_bl31_setup.c
View file @
318c2f97
...
...
@@ -10,13 +10,14 @@
#include <debug.h>
#include <generic_delay_timer.h>
#include <gicv2.h>
#include <libfdt.h>
#include <platform.h>
#include <platform_def.h>
#include <sunxi_def.h>
#include <sunxi_mmap.h>
#include <sunxi_private.h>
#include <uart_16550.h>
#include "sunxi_private.h"
static
entry_point_info_t
bl32_image_ep_info
;
static
entry_point_info_t
bl33_image_ep_info
;
...
...
@@ -28,6 +29,47 @@ static const gicv2_driver_data_t sunxi_gic_data = {
.
gicc_base
=
SUNXI_GICC_BASE
,
};
/*
* Try to find a DTB loaded in memory by previous stages.
*
* At the moment we implement a heuristic to find the DTB attached to U-Boot:
* U-Boot appends its DTB to the end of the image. Assuming that BL33 is
* U-Boot, try to find the size of the U-Boot image to learn the DTB address.
* The generic ARMv8 U-Boot image contains the load address and its size
* as u64 variables at the beginning of the image. There might be padding
* or other headers before that data, so scan the first 2KB after the BL33
* entry point to find the load address, which should be followed by the
* size. Adding those together gives us the address of the DTB.
*/
static
void
*
sunxi_find_dtb
(
void
)
{
uint64_t
*
u_boot_base
;
int
i
;
u_boot_base
=
(
void
*
)(
SUNXI_DRAM_VIRT_BASE
+
SUNXI_DRAM_SEC_SIZE
);
for
(
i
=
0
;
i
<
2048
/
sizeof
(
uint64_t
);
i
++
)
{
uint32_t
*
dtb_base
;
if
(
u_boot_base
[
i
]
!=
PLAT_SUNXI_NS_IMAGE_OFFSET
)
continue
;
/* Does the suspected U-Boot size look anyhow reasonable? */
if
(
u_boot_base
[
i
+
1
]
>=
256
*
1024
*
1024
)
continue
;
/* end of the image: base address + size */
dtb_base
=
(
void
*
)((
char
*
)
u_boot_base
+
u_boot_base
[
i
+
1
]);
if
(
fdt_check_header
(
dtb_base
)
!=
0
)
continue
;
return
dtb_base
;
}
return
NULL
;
}
void
bl31_early_platform_setup2
(
u_register_t
arg0
,
u_register_t
arg1
,
u_register_t
arg2
,
u_register_t
arg3
)
{
...
...
@@ -66,15 +108,16 @@ void bl31_platform_setup(void)
{
const
char
*
soc_name
;
uint16_t
soc_id
=
sunxi_read_soc_id
();
void
*
fdt
;
switch
(
soc_id
)
{
case
0x1689
:
case
SUNXI_SOC_A64
:
soc_name
=
"A64/H64/R18"
;
break
;
case
0x1718
:
case
SUNXI_SOC_H5
:
soc_name
=
"H5"
;
break
;
case
0x1728
:
case
SUNXI_SOC_H6
:
soc_name
=
"H6"
;
break
;
default:
...
...
@@ -85,6 +128,18 @@ void bl31_platform_setup(void)
generic_delay_timer_init
();
fdt
=
sunxi_find_dtb
();
if
(
fdt
)
{
const
char
*
model
;
int
length
;
model
=
fdt_getprop
(
fdt
,
0
,
"model"
,
&
length
);
NOTICE
(
"BL31: Found U-Boot DTB at %p, model: %s
\n
"
,
fdt
,
model
?:
"unknown"
);
}
else
{
NOTICE
(
"BL31: No DTB found.
\n
"
);
}
/* Configure the interrupt controller */
gicv2_driver_init
(
&
sunxi_gic_data
);
gicv2_distif_init
();
...
...
@@ -93,7 +148,7 @@ void bl31_platform_setup(void)
sunxi_security_setup
();
sunxi_pmic_setup
();
sunxi_pmic_setup
(
soc_id
,
fdt
);
INFO
(
"BL31: Platform setup done
\n
"
);
}
...
...
plat/allwinner/common/sunxi_common.c
View file @
318c2f97
...
...
@@ -4,21 +4,28 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch_helpers.h>
#include <debug.h>
#include <errno.h>
#include <mmio.h>
#include <platform.h>
#include <platform_def.h>
#include <sunxi_def.h>
#include <sunxi_mmap.h>
#include <sunxi_private.h>
#include <xlat_tables_v2.h>
#include "sunxi_private.h"
static
mmap_region_t
sunxi_mmap
[
PLATFORM_MMAP_REGIONS
+
1
]
=
{
MAP_REGION_FLAT
(
SUNXI_SRAM_BASE
,
SUNXI_SRAM_SIZE
,
MT_MEMORY
|
MT_RW
|
MT_SECURE
),
MAP_REGION_FLAT
(
SUNXI_DEV_BASE
,
SUNXI_DEV_SIZE
,
MT_DEVICE
|
MT_RW
|
MT_SECURE
),
MAP_REGION_FLAT
(
SUNXI_DRAM_BASE
,
SUNXI_DRAM_SIZE
,
MT_MEMORY
|
MT_RW
|
MT_NS
),
MAP_REGION
(
SUNXI_DRAM_BASE
,
SUNXI_DRAM_VIRT_BASE
,
SUNXI_DRAM_SEC_SIZE
,
MT_MEMORY
|
MT_RW
|
MT_SECURE
),
MAP_REGION
(
PLAT_SUNXI_NS_IMAGE_OFFSET
,
SUNXI_DRAM_VIRT_BASE
+
SUNXI_DRAM_SEC_SIZE
,
SUNXI_DRAM_MAP_SIZE
,
MT_MEMORY
|
MT_RO
|
MT_NS
),
{},
};
...
...
@@ -47,9 +54,6 @@ void sunxi_configure_mmu_el3(int flags)
mmap_add_region
(
BL_RO_DATA_BASE
,
BL_RO_DATA_BASE
,
BL_RO_DATA_END
-
BL_RO_DATA_BASE
,
MT_RO_DATA
|
MT_SECURE
);
mmap_add_region
(
BL_COHERENT_RAM_BASE
,
BL_COHERENT_RAM_BASE
,
BL_COHERENT_RAM_END
-
BL_COHERENT_RAM_BASE
,
MT_DEVICE
|
MT_RW
|
MT_SECURE
);
mmap_add
(
sunxi_mmap
);
init_xlat_tables
();
...
...
@@ -71,3 +75,136 @@ uint16_t sunxi_read_soc_id(void)
return
reg
>>
16
;
}
/*
* Configure a given pin to the GPIO-OUT function and sets its level.
* The port is given as a capital letter, the pin is the number within
* this port group.
* So to set pin PC7 to high, use: sunxi_set_gpio_out('C', 7, true);
*/
void
sunxi_set_gpio_out
(
char
port
,
int
pin
,
bool
level_high
)
{
uintptr_t
port_base
;
if
(
port
<
'A'
||
port
>
'L'
)
return
;
if
(
port
==
'L'
)
port_base
=
SUNXI_R_PIO_BASE
;
else
port_base
=
SUNXI_PIO_BASE
+
(
port
-
'A'
)
*
0x24
;
/* Set the new level first before configuring the pin. */
if
(
level_high
)
mmio_setbits_32
(
port_base
+
0x10
,
BIT
(
pin
));
else
mmio_clrbits_32
(
port_base
+
0x10
,
BIT
(
pin
));
/* configure pin as GPIO out (4(3) bits per pin, 1: GPIO out */
mmio_clrsetbits_32
(
port_base
+
(
pin
/
8
)
*
4
,
0x7
<<
((
pin
%
8
)
*
4
),
0x1
<<
((
pin
%
8
)
*
4
));
}
int
sunxi_init_platform_r_twi
(
uint16_t
socid
,
bool
use_rsb
)
{
uint32_t
pin_func
=
0x77
;
uint32_t
device_bit
;
unsigned
int
reset_offset
=
0xb0
;
switch
(
socid
)
{
case
SUNXI_SOC_H5
:
if
(
use_rsb
)
return
-
ENODEV
;
pin_func
=
0x22
;
device_bit
=
BIT
(
6
);
break
;
case
SUNXI_SOC_H6
:
if
(
use_rsb
)
return
-
ENODEV
;
pin_func
=
0x33
;
device_bit
=
BIT
(
16
);
reset_offset
=
0x19c
;
break
;
case
SUNXI_SOC_A64
:
pin_func
=
use_rsb
?
0x22
:
0x33
;
device_bit
=
use_rsb
?
BIT
(
3
)
:
BIT
(
6
);
break
;
default:
INFO
(
"R_I2C/RSB on Allwinner 0x%x SoC not supported
\n
"
,
socid
);
return
-
ENODEV
;
}
/* un-gate R_PIO clock */
if
(
socid
!=
SUNXI_SOC_H6
)
mmio_setbits_32
(
SUNXI_R_PRCM_BASE
+
0x28
,
BIT
(
0
));
/* switch pins PL0 and PL1 to the desired function */
mmio_clrsetbits_32
(
SUNXI_R_PIO_BASE
+
0x00
,
0xffU
,
pin_func
);
/* level 2 drive strength */
mmio_clrsetbits_32
(
SUNXI_R_PIO_BASE
+
0x14
,
0x0fU
,
0xaU
);
/* set both pins to pull-up */
mmio_clrsetbits_32
(
SUNXI_R_PIO_BASE
+
0x1c
,
0x0fU
,
0x5U
);
/* assert, then de-assert reset of I2C/RSB controller */
mmio_clrbits_32
(
SUNXI_R_PRCM_BASE
+
reset_offset
,
device_bit
);
mmio_setbits_32
(
SUNXI_R_PRCM_BASE
+
reset_offset
,
device_bit
);
/* un-gate clock */
if
(
socid
!=
SUNXI_SOC_H6
)
mmio_setbits_32
(
SUNXI_R_PRCM_BASE
+
0x28
,
device_bit
);
else
mmio_setbits_32
(
SUNXI_R_PRCM_BASE
+
0x19c
,
device_bit
|
BIT
(
0
));
return
0
;
}
/* This lock synchronises access to the arisc management processor. */
DEFINE_BAKERY_LOCK
(
arisc_lock
);
/*
* Tell the "arisc" SCP core (an OpenRISC core) to execute some code.
* We don't have any service running there, so we place some OpenRISC code
* in SRAM, put the address of that into the reset vector and release the
* arisc reset line. The SCP will execute that code and pull the line up again.
*/
void
sunxi_execute_arisc_code
(
uint32_t
*
code
,
size_t
size
,
int
patch_offset
,
uint16_t
param
)
{
uintptr_t
arisc_reset_vec
=
SUNXI_SRAM_A2_BASE
-
0x4000
+
0x100
;
do
{
bakery_lock_get
(
&
arisc_lock
);
/* Wait until the arisc is in reset state. */
if
(
!
(
mmio_read_32
(
SUNXI_R_CPUCFG_BASE
)
&
BIT
(
0
)))
break
;
bakery_lock_release
(
&
arisc_lock
);
}
while
(
1
);
/* Patch up the code to feed in an input parameter. */
if
(
patch_offset
>=
0
&&
patch_offset
<=
(
size
-
4
))
code
[
patch_offset
]
=
(
code
[
patch_offset
]
&
~
0xffff
)
|
param
;
clean_dcache_range
((
uintptr_t
)
code
,
size
);
/*
* The OpenRISC unconditional branch has opcode 0, the branch offset
* is in the lower 26 bits, containing the distance to the target,
* in instruction granularity (32 bits).
*/
mmio_write_32
(
arisc_reset_vec
,
((
uintptr_t
)
code
-
arisc_reset_vec
)
/
4
);
clean_dcache_range
(
arisc_reset_vec
,
4
);
/* De-assert the arisc reset line to let it run. */
mmio_setbits_32
(
SUNXI_R_CPUCFG_BASE
,
BIT
(
0
));
/*
* We release the lock here, although the arisc is still busy.
* But as long as it runs, the reset line is high, so other users
* won't leave the loop above.
* Once it has finished, the code is supposed to clear the reset line,
* to signal this to other users.
*/
bakery_lock_release
(
&
arisc_lock
);
}
plat/allwinner/common/sunxi_cpu_ops.c
View file @
318c2f97
...
...
@@ -4,15 +4,19 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch_helpers.h>
#include <assert.h>
#include <core_off_arisc.h>
#include <debug.h>
#include <delay_timer.h>
#include <mmio.h>
#include <platform.h>
#include <platform_def.h>
#include <sunxi_mmap.h>
#include <sunxi_cpucfg.h>
#include <sunxi_mmap.h>
#include <sunxi_private.h>
#include <utils_def.h>
#include "sunxi_private.h"
static
void
sunxi_cpu_disable_power
(
unsigned
int
cluster
,
unsigned
int
core
)
{
if
(
mmio_read_32
(
SUNXI_CPU_POWER_CLAMP_REG
(
cluster
,
core
))
==
0xff
)
...
...
@@ -40,16 +44,37 @@ static void sunxi_cpu_enable_power(unsigned int cluster, unsigned int core)
void
sunxi_cpu_off
(
unsigned
int
cluster
,
unsigned
int
core
)
{
int
corenr
=
cluster
*
PLATFORM_MAX_CPUS_PER_CLUSTER
+
core
;
VERBOSE
(
"PSCI: Powering off cluster %d core %d
\n
"
,
cluster
,
core
);
/* Deassert DBGPWRDUP */
mmio_clrbits_32
(
SUNXI_CPUCFG_DBG_REG0
,
BIT
(
core
));
/* Activate the core output clamps */
mmio_setbits_32
(
SUNXI_POWEROFF_GATING_REG
(
cluster
),
BIT
(
core
));
/* Assert CPU power-on reset */
mmio_clrbits_32
(
SUNXI_POWERON_RST_REG
(
cluster
),
BIT
(
core
));
/* Remove power from the CPU */
sunxi_cpu_disable_power
(
cluster
,
core
);
/* We can't turn ourself off like this, but it works for other cores. */
if
(
plat_my_core_pos
()
!=
corenr
)
{
/* Activate the core output clamps, but not for core 0. */
if
(
corenr
!=
0
)
mmio_setbits_32
(
SUNXI_POWEROFF_GATING_REG
(
cluster
),
BIT
(
core
));
/* Assert CPU power-on reset */
mmio_clrbits_32
(
SUNXI_POWERON_RST_REG
(
cluster
),
BIT
(
core
));
/* Remove power from the CPU */
sunxi_cpu_disable_power
(
cluster
,
core
);
return
;
}
/* Simplifies assembly, all SoCs so far are single cluster anyway. */
assert
(
cluster
==
0
);
/*
* If we are supposed to turn ourself off, tell the arisc SCP
* to do that work for us. The code expects the core mask to be
* patched into the first instruction.
*/
sunxi_execute_arisc_code
(
arisc_core_off
,
sizeof
(
arisc_core_off
),
0
,
BIT_32
(
core
));
}
void
sunxi_cpu_on
(
unsigned
int
cluster
,
unsigned
int
core
)
...
...
plat/allwinner/common/sunxi_pm.c
View file @
318c2f97
...
...
@@ -13,15 +13,14 @@
#include <platform.h>
#include <platform_def.h>
#include <psci.h>
#include <sunxi_mmap.h>
#include <sunxi_cpucfg.h>
#include <sunxi_mmap.h>
#include <sunxi_private.h>
#define SUNXI_WDOG0_CTRL_REG (SUNXI_WDOG_BASE + 0x0010)
#define SUNXI_WDOG0_CFG_REG (SUNXI_WDOG_BASE + 0x0014)
#define SUNXI_WDOG0_MODE_REG (SUNXI_WDOG_BASE + 0x0018)
#include "sunxi_private.h"
#define mpidr_is_valid(mpidr) ( \
MPIDR_AFFLVL3_VAL(mpidr) == 0 && \
MPIDR_AFFLVL2_VAL(mpidr) == 0 && \
...
...
@@ -43,6 +42,16 @@ static void sunxi_pwr_domain_off(const psci_power_state_t *target_state)
gicv2_cpuif_disable
();
}
static
void
__dead2
sunxi_pwr_down_wfi
(
const
psci_power_state_t
*
target_state
)
{
u_register_t
mpidr
=
read_mpidr
();
sunxi_cpu_off
(
MPIDR_AFFLVL1_VAL
(
mpidr
),
MPIDR_AFFLVL0_VAL
(
mpidr
));
while
(
1
)
wfi
();
}
static
void
sunxi_pwr_domain_on_finish
(
const
psci_power_state_t
*
target_state
)
{
gicv2_pcpu_distif_init
();
...
...
@@ -83,6 +92,7 @@ static int sunxi_validate_ns_entrypoint(uintptr_t ns_entrypoint)
static
plat_psci_ops_t
sunxi_psci_ops
=
{
.
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
,
...
...
plat/allwinner/common/sunxi_security.c
View file @
318c2f97
...
...
@@ -7,6 +7,7 @@
#include <debug.h>
#include <mmio.h>
#include <sunxi_mmap.h>
#include <sunxi_private.h>
#ifdef SUNXI_SPC_BASE
#define SPC_DECPORT_STA_REG(p) (SUNXI_SPC_BASE + ((p) * 0x0c) + 0x4)
...
...
plat/allwinner/sun50i_a64/include/core_off_arisc.h
0 → 100644
View file @
318c2f97
/*
* Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
static
uint32_t
arisc_core_off
[]
=
{
0x18600000
,
/* l.movhi r3, <corenr> */
0x18000000
,
/* l.movhi r0, 0x0 */
0x19a00170
,
/* l.movhi r13, 0x170 */
0x84ad0030
,
/* l.lwz r5, 0x30(r13) */
0xe0a51803
,
/* l.and r5, r5, r3 */
0xe4050000
,
/* l.sfeq r5, r0 */
0x13fffffd
,
/* l.bf -12 */
0xb8c30050
,
/* l.srli r6, r3, 16 */
0xbc060001
,
/* l.sfeqi r6, 1 */
0x10000005
,
/* l.bf +20 */
0x19a001f0
,
/* l.movhi r13, 0x1f0 */
0x84ad1500
,
/* l.lwz r5, 0x1500(r13) */
0xe0a53004
,
/* l.or r5, r5, r6 */
0xd44d2d00
,
/* l.sw 0x1500(r13), r5 */
0x84ad1c30
,
/* l.lwz r5, 0x1c30(r13) */
0xacc6ffff
,
/* l.xori r6, r6, -1 */
0xe0a53003
,
/* l.and r5, r5, r6 */
0xd46d2c30
,
/* l.sw 0x1c30(r13), r5 */
0xe0c3000f
,
/* l.ff1 r6, r3 */
0x9cc6ffef
,
/* l.addi r6, r6, -17 */
0xb8c60002
,
/* l.slli r6, r6, 2 */
0xe0c66800
,
/* l.add r6, r6, r13 */
0xa8a000ff
,
/* l.ori r5, r0, 0xff */
0xd4462d40
,
/* l.sw 0x1540(r6), r5 */
0xd46d0400
,
/* l.sw 0x1c00(r13), r0 */
0x03ffffff
,
/* l.j -1 */
0x15000000
,
/* l.nop */
};
plat/allwinner/sun50i_a64/include/sunxi_mmap.h
View file @
318c2f97
...
...
@@ -21,7 +21,7 @@
#define SUNXI_DEV_BASE 0x01000000
#define SUNXI_DEV_SIZE 0x01000000
#define SUNXI_DRAM_BASE 0x40000000
#define SUNXI_DRAM_
SIZ
E
0x
8
0000000
#define SUNXI_DRAM_
VIRT_BAS
E 0x0
2
000000
/* Memory-mapped devices */
#define SUNXI_CPU_MBIST_BASE 0x01502000
...
...
plat/allwinner/sun50i_a64/platform.mk
View file @
318c2f97
...
...
@@ -4,51 +4,7 @@
# SPDX-License-Identifier: BSD-3-Clause
#
include
lib/xlat_tables_v2/xlat_tables.mk
# The differences between the platform are covered by the include files.
include
plat/allwinner/common/allwinner-common.mk
AW_PLAT
:=
plat/allwinner
PLAT_INCLUDES
:=
-Iinclude
/plat/arm/common/
\
-Iinclude
/plat/arm/common/aarch64
\
-I
${AW_PLAT}
/common/include
\
-I
${AW_PLAT}
/
${PLAT}
/include
PLAT_BL_COMMON_SOURCES
:=
drivers/console/
${ARCH}
/console.S
\
drivers/ti/uart/
${ARCH}
/16550_console.S
\
${XLAT_TABLES_LIB_SRCS}
\
${AW_PLAT}
/common/plat_helpers.S
\
${AW_PLAT}
/common/sunxi_common.c
BL31_SOURCES
+=
drivers/arm/gic/common/gic_common.c
\
drivers/arm/gic/v2/gicv2_helpers.c
\
drivers/arm/gic/v2/gicv2_main.c
\
drivers/delay_timer/delay_timer.c
\
drivers/delay_timer/generic_delay_timer.c
\
lib/cpus/
${ARCH}
/cortex_a53.S
\
plat/common/plat_gicv2.c
\
plat/common/plat_psci_common.c
\
${AW_PLAT}
/common/sunxi_bl31_setup.c
\
${AW_PLAT}
/common/sunxi_cpu_ops.c
\
${AW_PLAT}
/common/sunxi_pm.c
\
${AW_PLAT}
/sun50i_a64/sunxi_power.c
\
${AW_PLAT}
/common/sunxi_security.c
\
${AW_PLAT}
/common/sunxi_topology.c
# The bootloader is guaranteed to only run on CPU 0 by the boot ROM.
COLD_BOOT_SINGLE_CPU
:=
1
# Enable workarounds for Cortex-A53 errata. Allwinner uses at least r0p4.
ERRATA_A53_835769
:=
1
ERRATA_A53_843419
:=
1
ERRATA_A53_855873
:=
1
MULTI_CONSOLE_API
:=
1
# The reset vector can be changed for each CPU.
PROGRAMMABLE_RESET_ADDRESS
:=
1
# Allow mapping read-only data as execute-never.
SEPARATE_CODE_AND_RODATA
:=
1
# BL31 gets loaded alongside BL33 (U-Boot) by U-Boot's SPL
RESET_TO_BL31
:=
1
PLAT_BL_COMMON_SOURCES
+=
drivers/allwinner/sunxi_rsb.c
plat/allwinner/sun50i_a64/sunxi_power.c
View file @
318c2f97
...
...
@@ -5,20 +5,350 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <allwinner/sunxi_rsb.h>
#include <arch_helpers.h>
#include <debug.h>
#include <delay_timer.h>
#include <errno.h>
#include <libfdt.h>
#include <mmio.h>
#include <platform_def.h>
#include <sunxi_def.h>
#include <sunxi_mmap.h>
#include <sunxi_private.h>
int
sunxi_pmic_setup
(
void
)
static
enum
pmic_type
{
GENERIC_H5
,
GENERIC_A64
,
REF_DESIGN_H5
,
/* regulators controlled by GPIO pins on port L */
AXP803_RSB
,
/* PMIC connected via RSB on most A64 boards */
}
pmic
;
#define AXP803_HW_ADDR 0x3a3
#define AXP803_RT_ADDR 0x2d
/*
* On boards without a proper PMIC we struggle to turn off the system properly.
* Try to turn off as much off the system as we can, to reduce power
* consumption. This should be entered with only one core running and SMP
* disabled.
* This function only cares about peripherals.
*/
void
sunxi_turn_off_soc
(
uint16_t
socid
)
{
int
i
;
/** Turn off most peripherals, most importantly DRAM users. **/
/* Keep DRAM controller running for now. */
mmio_clrbits_32
(
SUNXI_CCU_BASE
+
0x2c0
,
~
BIT_32
(
14
));
mmio_clrbits_32
(
SUNXI_CCU_BASE
+
0x60
,
~
BIT_32
(
14
));
/* Contains msgbox (bit 21) and spinlock (bit 22) */
mmio_write_32
(
SUNXI_CCU_BASE
+
0x2c4
,
0
);
mmio_write_32
(
SUNXI_CCU_BASE
+
0x64
,
0
);
mmio_write_32
(
SUNXI_CCU_BASE
+
0x2c8
,
0
);
/* Keep PIO controller running for now. */
mmio_clrbits_32
(
SUNXI_CCU_BASE
+
0x68
,
~
(
BIT_32
(
5
)));
mmio_write_32
(
SUNXI_CCU_BASE
+
0x2d0
,
0
);
/* Contains UART0 (bit 16) */
mmio_write_32
(
SUNXI_CCU_BASE
+
0x2d8
,
0
);
mmio_write_32
(
SUNXI_CCU_BASE
+
0x6c
,
0
);
mmio_write_32
(
SUNXI_CCU_BASE
+
0x70
,
0
);
/** Turn off DRAM controller. **/
mmio_clrbits_32
(
SUNXI_CCU_BASE
+
0x2c0
,
BIT_32
(
14
));
mmio_clrbits_32
(
SUNXI_CCU_BASE
+
0x60
,
BIT_32
(
14
));
/** Migrate CPU and bus clocks away from the PLLs. **/
/* AHB1: use OSC24M/1, APB1 = AHB1 / 2 */
mmio_write_32
(
SUNXI_CCU_BASE
+
0x54
,
0x1000
);
/* APB2: use OSC24M */
mmio_write_32
(
SUNXI_CCU_BASE
+
0x58
,
0x1000000
);
/* AHB2: use AHB1 clock */
mmio_write_32
(
SUNXI_CCU_BASE
+
0x5c
,
0
);
/* CPU: use OSC24M */
mmio_write_32
(
SUNXI_CCU_BASE
+
0x50
,
0x10000
);
/** Turn off PLLs. **/
for
(
i
=
0
;
i
<
6
;
i
++
)
mmio_clrbits_32
(
SUNXI_CCU_BASE
+
i
*
8
,
BIT
(
31
));
switch
(
socid
)
{
case
SUNXI_SOC_H5
:
mmio_clrbits_32
(
SUNXI_CCU_BASE
+
0x44
,
BIT
(
31
));
break
;
case
SUNXI_SOC_A64
:
mmio_clrbits_32
(
SUNXI_CCU_BASE
+
0x2c
,
BIT
(
31
));
mmio_clrbits_32
(
SUNXI_CCU_BASE
+
0x4c
,
BIT
(
31
));
break
;
}
}
static
int
rsb_init
(
void
)
{
int
ret
;
ret
=
rsb_init_controller
();
if
(
ret
)
return
ret
;
/* Start with 400 KHz to issue the I2C->RSB switch command. */
ret
=
rsb_set_bus_speed
(
SUNXI_OSC24M_CLK_IN_HZ
,
400000
);
if
(
ret
)
return
ret
;
/*
* Initiate an I2C transaction to write 0x7c into register 0x3e,
* switching the PMIC to RSB mode.
*/
ret
=
rsb_set_device_mode
(
0x7c3e00
);
if
(
ret
)
return
ret
;
/* Now in RSB mode, switch to the recommended 3 MHz. */
ret
=
rsb_set_bus_speed
(
SUNXI_OSC24M_CLK_IN_HZ
,
3000000
);
if
(
ret
)
return
ret
;
/* Associate the 8-bit runtime address with the 12-bit bus address. */
return
rsb_assign_runtime_address
(
AXP803_HW_ADDR
,
AXP803_RT_ADDR
);
}
static
int
axp_write
(
uint8_t
reg
,
uint8_t
val
)
{
return
rsb_write
(
AXP803_RT_ADDR
,
reg
,
val
);
}
static
int
axp_setbits
(
uint8_t
reg
,
uint8_t
set_mask
)
{
uint8_t
regval
;
int
ret
;
ret
=
rsb_read
(
AXP803_RT_ADDR
,
reg
);
if
(
ret
<
0
)
return
ret
;
regval
=
ret
|
set_mask
;
return
rsb_write
(
AXP803_RT_ADDR
,
reg
,
regval
);
}
static
bool
should_enable_regulator
(
const
void
*
fdt
,
int
node
)
{
/* STUB */
NOTICE
(
"BL31: STUB PMIC setup code called
\n
"
);
if
(
fdt_getprop
(
fdt
,
node
,
"phandle"
,
NULL
)
!=
NULL
)
return
true
;
if
(
fdt_getprop
(
fdt
,
node
,
"regulator-always-on"
,
NULL
)
!=
NULL
)
return
true
;
return
false
;
}
/*
* Retrieve the voltage from a given regulator DTB node.
* Both the regulator-{min,max}-microvolt properties must be present and
* have the same value. Return that value in millivolts.
*/
static
int
fdt_get_regulator_millivolt
(
const
void
*
fdt
,
int
node
)
{
const
fdt32_t
*
prop
;
uint32_t
min_volt
;
prop
=
fdt_getprop
(
fdt
,
node
,
"regulator-min-microvolt"
,
NULL
);
if
(
prop
==
NULL
)
return
-
EINVAL
;
min_volt
=
fdt32_to_cpu
(
*
prop
);
prop
=
fdt_getprop
(
fdt
,
node
,
"regulator-max-microvolt"
,
NULL
);
if
(
prop
==
NULL
)
return
-
EINVAL
;
if
(
fdt32_to_cpu
(
*
prop
)
!=
min_volt
)
return
-
EINVAL
;
return
min_volt
/
1000
;
}
#define NO_SPLIT 0xff
struct
axp_regulator
{
char
*
dt_name
;
uint16_t
min_volt
;
uint16_t
max_volt
;
uint16_t
step
;
unsigned
char
split
;
unsigned
char
volt_reg
;
unsigned
char
switch_reg
;
unsigned
char
switch_bit
;
}
regulators
[]
=
{
{
"dcdc1"
,
1600
,
3400
,
100
,
NO_SPLIT
,
0x20
,
0xff
,
9
},
{
"dcdc5"
,
800
,
1840
,
10
,
32
,
0x24
,
0xff
,
9
},
{
"dldo1"
,
700
,
3300
,
100
,
NO_SPLIT
,
0x15
,
0x12
,
3
},
{
"dldo2"
,
700
,
4200
,
100
,
27
,
0x16
,
0x12
,
4
},
{
"dldo3"
,
700
,
3300
,
100
,
NO_SPLIT
,
0x17
,
0x12
,
5
},
{
"fldo1"
,
700
,
1450
,
50
,
NO_SPLIT
,
0x1c
,
0x13
,
2
},
{}
};
static
int
setup_regulator
(
const
void
*
fdt
,
int
node
,
const
struct
axp_regulator
*
reg
)
{
int
mvolt
;
uint8_t
regval
;
if
(
!
should_enable_regulator
(
fdt
,
node
))
return
-
ENOENT
;
mvolt
=
fdt_get_regulator_millivolt
(
fdt
,
node
);
if
(
mvolt
<
reg
->
min_volt
||
mvolt
>
reg
->
max_volt
)
return
-
EINVAL
;
regval
=
(
mvolt
/
reg
->
step
)
-
(
reg
->
min_volt
/
reg
->
step
);
if
(
regval
>
reg
->
split
)
regval
=
((
regval
-
reg
->
split
)
/
2
)
+
reg
->
split
;
axp_write
(
reg
->
volt_reg
,
regval
);
if
(
reg
->
switch_reg
<
0xff
)
axp_setbits
(
reg
->
switch_reg
,
BIT
(
reg
->
switch_bit
));
INFO
(
"PMIC: AXP803: %s voltage: %d.%03dV
\n
"
,
reg
->
dt_name
,
mvolt
/
1000
,
mvolt
%
1000
);
return
0
;
}
static
void
setup_axp803_rails
(
const
void
*
fdt
)
{
int
node
;
bool
dc1sw
=
false
;
/* locate the PMIC DT node, bail out if not found */
node
=
fdt_node_offset_by_compatible
(
fdt
,
-
1
,
"x-powers,axp803"
);
if
(
node
==
-
FDT_ERR_NOTFOUND
)
{
WARN
(
"BL31: PMIC: No AXP803 DT node, skipping initial setup.
\n
"
);
return
;
}
if
(
fdt_getprop
(
fdt
,
node
,
"x-powers,drive-vbus-en"
,
NULL
))
axp_setbits
(
0x8f
,
BIT
(
4
));
/* descend into the "regulators" subnode */
node
=
fdt_first_subnode
(
fdt
,
node
);
/* iterate over all regulators to find used ones */
for
(
node
=
fdt_first_subnode
(
fdt
,
node
);
node
!=
-
FDT_ERR_NOTFOUND
;
node
=
fdt_next_subnode
(
fdt
,
node
))
{
struct
axp_regulator
*
reg
;
const
char
*
name
;
int
length
;
/* We only care if it's always on or referenced. */
if
(
!
should_enable_regulator
(
fdt
,
node
))
continue
;
name
=
fdt_get_name
(
fdt
,
node
,
&
length
);
for
(
reg
=
regulators
;
reg
->
dt_name
;
reg
++
)
{
if
(
!
strncmp
(
name
,
reg
->
dt_name
,
length
))
{
setup_regulator
(
fdt
,
node
,
reg
);
break
;
}
}
if
(
!
strncmp
(
name
,
"dc1sw"
,
length
))
{
/* Delay DC1SW enablement to avoid overheating. */
dc1sw
=
true
;
continue
;
}
}
/*
* If DLDO2 is enabled after DC1SW, the PMIC overheats and shuts
* down. So always enable DC1SW as the very last regulator.
*/
if
(
dc1sw
)
{
INFO
(
"PMIC: AXP803: Enabling DC1SW
\n
"
);
axp_setbits
(
0x12
,
BIT
(
7
));
}
}
int
sunxi_pmic_setup
(
uint16_t
socid
,
const
void
*
fdt
)
{
int
ret
;
switch
(
socid
)
{
case
SUNXI_SOC_H5
:
pmic
=
REF_DESIGN_H5
;
NOTICE
(
"BL31: PMIC: Defaulting to PortL GPIO according to H5 reference design.
\n
"
);
break
;
case
SUNXI_SOC_A64
:
pmic
=
GENERIC_A64
;
ret
=
sunxi_init_platform_r_twi
(
socid
,
true
);
if
(
ret
)
return
ret
;
ret
=
rsb_init
();
if
(
ret
)
return
ret
;
pmic
=
AXP803_RSB
;
NOTICE
(
"BL31: PMIC: Detected AXP803 on RSB.
\n
"
);
if
(
fdt
)
setup_axp803_rails
(
fdt
);
break
;
default:
NOTICE
(
"BL31: PMIC: No support for Allwinner %x SoC.
\n
"
,
socid
);
return
-
ENODEV
;
}
return
0
;
}
void
__dead2
sunxi_power_down
(
void
)
{
ERROR
(
"PSCI: Full shutdown not implemented, halting
\n
"
);
switch
(
pmic
)
{
case
GENERIC_H5
:
/* Turn off as many peripherals and clocks as we can. */
sunxi_turn_off_soc
(
SUNXI_SOC_H5
);
/* Turn off the pin controller now. */
mmio_write_32
(
SUNXI_CCU_BASE
+
0x68
,
0
);
break
;
case
GENERIC_A64
:
/* Turn off as many peripherals and clocks as we can. */
sunxi_turn_off_soc
(
SUNXI_SOC_A64
);
/* Turn off the pin controller now. */
mmio_write_32
(
SUNXI_CCU_BASE
+
0x68
,
0
);
break
;
case
REF_DESIGN_H5
:
sunxi_turn_off_soc
(
SUNXI_SOC_H5
);
/*
* Switch PL pins to power off the board:
* - PL5 (VCC_IO) -> high
* - PL8 (PWR-STB = CPU power supply) -> low
* - PL9 (PWR-DRAM) ->low
* - PL10 (power LED) -> low
* Note: Clearing PL8 will reset the board, so keep it up.
*/
sunxi_set_gpio_out
(
'L'
,
5
,
1
);
sunxi_set_gpio_out
(
'L'
,
9
,
0
);
sunxi_set_gpio_out
(
'L'
,
10
,
0
);
/* Turn off pin controller now. */
mmio_write_32
(
SUNXI_CCU_BASE
+
0x68
,
0
);
break
;
case
AXP803_RSB
:
/* (Re-)init RSB in case the rich OS has disabled it. */
sunxi_init_platform_r_twi
(
SUNXI_SOC_A64
,
true
);
rsb_init
();
/* Set "power disable control" bit */
axp_setbits
(
0x32
,
BIT
(
7
));
break
;
default:
break
;
}
udelay
(
1000
);
ERROR
(
"PSCI: Cannot turn off system, halting.
\n
"
);
wfi
();
panic
();
}
plat/allwinner/sun50i_h6/include/core_off_arisc.h
0 → 100644
View file @
318c2f97
/*
* Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
static
uint32_t
arisc_core_off
[]
=
{
0x18600000
,
/* l.movhi r3, <corenr> */
0x18000000
,
/* l.movhi r0, 0x0 */
0x19a00901
,
/* l.movhi r13, 0x901 */
0x84ad0080
,
/* l.lwz r5, 0x80(r13) */
0xe0a51803
,
/* l.and r5, r5, r3 */
0xe4050000
,
/* l.sfeq r5, r0 */
0x13fffffd
,
/* l.bf -12 */
0xb8c30050
,
/* l.srli r6, r3, 16 */
0xbc060001
,
/* l.sfeqi r6, 1 */
0x10000005
,
/* l.bf +20 */
0x19a00700
,
/* l.movhi r13, 0x700 */
0x84ad0444
,
/* l.lwz r5, 0x0444(r13) */
0xe0a53004
,
/* l.or r5, r5, r6 */
0xd40d2c44
,
/* l.sw 0x0444(r13), r5 */
0x84ad0440
,
/* l.lwz r5, 0x0440(r13) */
0xacc6ffff
,
/* l.xori r6, r6, -1 */
0xe0a53003
,
/* l.and r5, r5, r6 */
0xd40d2c40
,
/* l.sw 0x0440(r13), r5 */
0xe0c3000f
,
/* l.ff1 r6, r3 */
0x9cc6ffef
,
/* l.addi r6, r6, -17 */
0xb8c60002
,
/* l.slli r6, r6, 2 */
0xe0c66800
,
/* l.add r6, r6, r13 */
0xa8a000ff
,
/* l.ori r5, r0, 0xff */
0xd4062c50
,
/* l.sw 0x0450(r6), r5 */
0xd40d0400
,
/* l.sw 0x0400(r13), r0 */
0x03ffffff
,
/* l.j -1 */
0x15000000
,
/* l.nop */
};
plat/allwinner/sun50i_h6/include/sunxi_mmap.h
View file @
318c2f97
...
...
@@ -11,7 +11,7 @@
#define SUNXI_ROM_BASE 0x00000000
#define SUNXI_ROM_SIZE 0x00010000
#define SUNXI_SRAM_BASE 0x00020000
#define SUNXI_SRAM_SIZE 0x000
9
8000
#define SUNXI_SRAM_SIZE 0x000
f
8000
#define SUNXI_SRAM_A1_BASE 0x00020000
#define SUNXI_SRAM_A1_SIZE 0x00008000
#define SUNXI_SRAM_A2_BASE 0x00104000
...
...
@@ -21,7 +21,7 @@
#define SUNXI_DEV_BASE 0x01000000
#define SUNXI_DEV_SIZE 0x09000000
#define SUNXI_DRAM_BASE 0x40000000
#define SUNXI_DRAM_
SIZ
E
0x
c
0000000
#define SUNXI_DRAM_
VIRT_BAS
E 0x0
a
000000
/* Memory-mapped devices */
#define SUNXI_SYSCON_BASE 0x03000000
...
...
plat/allwinner/sun50i_h6/platform.mk
View file @
318c2f97
...
...
@@ -4,53 +4,7 @@
# SPDX-License-Identifier: BSD-3-Clause
#
include
lib/xlat_tables_v2/xlat_tables.mk
# The differences between the platform are covered by the include files.
include
plat/allwinner/common/allwinner-common.mk
AW_PLAT
:=
plat/allwinner
AW_DRIVERS
:=
drivers/allwinner
PLAT_INCLUDES
:=
-Iinclude
/plat/arm/common
\
-Iinclude
/plat/arm/common/aarch64
\
-I
${AW_PLAT}
/common/include
\
-I
${AW_PLAT}
/
${PLAT}
/include
PLAT_BL_COMMON_SOURCES
:=
drivers/console/
${ARCH}
/console.S
\
drivers/mentor/i2c/mi2cv.c
\
drivers/ti/uart/
${ARCH}
/16550_console.S
\
${XLAT_TABLES_LIB_SRCS}
\
${AW_PLAT}
/common/plat_helpers.S
\
${AW_PLAT}
/common/sunxi_common.c
BL31_SOURCES
+=
drivers/arm/gic/common/gic_common.c
\
drivers/arm/gic/v2/gicv2_helpers.c
\
drivers/arm/gic/v2/gicv2_main.c
\
drivers/delay_timer/delay_timer.c
\
drivers/delay_timer/generic_delay_timer.c
\
lib/cpus/
${ARCH}
/cortex_a53.S
\
plat/common/plat_gicv2.c
\
plat/common/plat_psci_common.c
\
${AW_PLAT}
/common/sunxi_bl31_setup.c
\
${AW_PLAT}
/common/sunxi_cpu_ops.c
\
${AW_PLAT}
/common/sunxi_pm.c
\
${AW_PLAT}
/sun50i_h6/sunxi_power.c
\
${AW_PLAT}
/common/sunxi_security.c
\
${AW_PLAT}
/common/sunxi_topology.c
# The bootloader is guaranteed to only run on CPU 0 by the boot ROM.
COLD_BOOT_SINGLE_CPU
:=
1
# Enable workarounds for Cortex-A53 errata. Allwinner uses at least r0p4.
ERRATA_A53_835769
:=
1
ERRATA_A53_843419
:=
1
ERRATA_A53_855873
:=
1
MULTI_CONSOLE_API
:=
1
# The reset vector can be changed for each CPU.
PROGRAMMABLE_RESET_ADDRESS
:=
1
# Allow mapping read-only data as execute-never.
SEPARATE_CODE_AND_RODATA
:=
1
# BL31 gets loaded alongside BL33 (U-Boot) by U-Boot's SPL
RESET_TO_BL31
:=
1
PLAT_BL_COMMON_SOURCES
+=
drivers/mentor/i2c/mi2cv.c
plat/allwinner/sun50i_h6/sunxi_power.c
View file @
318c2f97
...
...
@@ -12,7 +12,9 @@
#include <mmio.h>
#include <mentor/mi2cv.h>
#include <string.h>
#include <sunxi_def.h>
#include <sunxi_mmap.h>
#include <sunxi_private.h>
#define AXP805_ADDR 0x36
#define AXP805_ID 0x03
...
...
@@ -24,36 +26,6 @@ enum pmic_type {
enum
pmic_type
pmic
;
static
int
sunxi_init_r_i2c
(
void
)
{
uint32_t
reg
;
/* switch pins PL0 and PL1 to I2C */
reg
=
mmio_read_32
(
SUNXI_R_PIO_BASE
+
0x00
);
mmio_write_32
(
SUNXI_R_PIO_BASE
+
0x00
,
(
reg
&
~
0xff
)
|
0x33
);
/* level 2 drive strength */
reg
=
mmio_read_32
(
SUNXI_R_PIO_BASE
+
0x14
);
mmio_write_32
(
SUNXI_R_PIO_BASE
+
0x14
,
(
reg
&
~
0x0f
)
|
0xa
);
/* set both ports to pull-up */
reg
=
mmio_read_32
(
SUNXI_R_PIO_BASE
+
0x1c
);
mmio_write_32
(
SUNXI_R_PIO_BASE
+
0x1c
,
(
reg
&
~
0x0f
)
|
0x5
);
/* assert & de-assert reset of R_I2C */
reg
=
mmio_read_32
(
SUNXI_R_PRCM_BASE
+
0x19c
);
mmio_write_32
(
SUNXI_R_PRCM_BASE
+
0x19c
,
reg
&
~
BIT
(
16
));
mmio_write_32
(
SUNXI_R_PRCM_BASE
+
0x19c
,
reg
|
BIT
(
16
));
/* un-gate R_I2C clock */
mmio_write_32
(
SUNXI_R_PRCM_BASE
+
0x19c
,
reg
|
BIT
(
16
)
|
BIT
(
0
));
/* call mi2cv driver */
i2c_init
((
void
*
)
SUNXI_R_I2C_BASE
);
return
0
;
}
int
axp_i2c_read
(
uint8_t
chip
,
uint8_t
reg
,
uint8_t
*
val
)
{
int
ret
;
...
...
@@ -96,11 +68,13 @@ static int axp805_probe(void)
return
0
;
}
int
sunxi_pmic_setup
(
void
)
int
sunxi_pmic_setup
(
uint16_t
socid
,
const
void
*
fdt
)
{
int
ret
;
sunxi_init_r_i2c
();
sunxi_init_platform_r_twi
(
SUNXI_SOC_H6
,
false
);
/* initialise mi2cv driver */
i2c_init
((
void
*
)
SUNXI_R_I2C_BASE
);
NOTICE
(
"PMIC: Probing AXP805
\n
"
);
pmic
=
AXP805
;
...
...
@@ -120,7 +94,10 @@ void __dead2 sunxi_power_down(void)
switch
(
pmic
)
{
case
AXP805
:
sunxi_init_r_i2c
();
/* Re-initialise after rich OS might have used it. */
sunxi_init_platform_r_twi
(
SUNXI_SOC_H6
,
false
);
/* initialise mi2cv driver */
i2c_init
((
void
*
)
SUNXI_R_I2C_BASE
);
axp_i2c_read
(
AXP805_ADDR
,
0x32
,
&
val
);
axp_i2c_write
(
AXP805_ADDR
,
0x32
,
val
|
0x80
);
break
;
...
...
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