mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 12:21:18 +00:00
Implement LP0-Entry BPMP Firmware
This commit is contained in:
parent
f7598cb433
commit
5370c7b150
12 changed files with 510 additions and 0 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -52,3 +52,5 @@ Mkfile.old
|
||||||
dkms.conf
|
dkms.conf
|
||||||
|
|
||||||
.*/
|
.*/
|
||||||
|
|
||||||
|
exosphere/bpmpfw/out/*
|
66
exosphere/bpmpfw/Makefile
Normal file
66
exosphere/bpmpfw/Makefile
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2))
|
||||||
|
|
||||||
|
ifeq ($(strip $(DEVKITARM)),)
|
||||||
|
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||||
|
endif
|
||||||
|
|
||||||
|
include $(DEVKITARM)/base_tools
|
||||||
|
|
||||||
|
name := $(shell basename $(CURDIR))
|
||||||
|
|
||||||
|
dir_source := src
|
||||||
|
dir_build := build
|
||||||
|
dir_out := out
|
||||||
|
|
||||||
|
ASFLAGS :=
|
||||||
|
|
||||||
|
CFLAGS = \
|
||||||
|
-Iinclude \
|
||||||
|
-Iinclude/compat \
|
||||||
|
-march=armv8-a \
|
||||||
|
-mlittle-endian \
|
||||||
|
-fno-stack-protector \
|
||||||
|
-fno-common \
|
||||||
|
-fno-builtin \
|
||||||
|
-fno-inline \
|
||||||
|
-ffreestanding \
|
||||||
|
-std=gnu99 \
|
||||||
|
-Werror \
|
||||||
|
-Wall \
|
||||||
|
-Wno-error=unused-variable
|
||||||
|
|
||||||
|
LDFLAGS := -nostartfiles -Wl,--nmagic
|
||||||
|
|
||||||
|
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||||
|
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||||
|
$(call rwildcard, $(dir_source), *.s *.c)))
|
||||||
|
|
||||||
|
define bin2o
|
||||||
|
bin2s $< | $(AS) -o $(@)
|
||||||
|
endef
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: $(dir_out)/$(name).bin
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
@rm -rf $(dir_build)
|
||||||
|
@rm -rf $(dir_out)
|
||||||
|
|
||||||
|
$(dir_out)/$(name).bin: $(dir_build)/$(name).elf
|
||||||
|
@mkdir -p "$(@D)"
|
||||||
|
$(OBJCOPY) -S -O binary $< $@
|
||||||
|
|
||||||
|
$(dir_build)/$(name).elf: $(bundled) $(objects)
|
||||||
|
$(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^
|
||||||
|
|
||||||
|
$(dir_build)/%.bin.o: $(dir_build)/%.bin
|
||||||
|
@$(bin2o)
|
||||||
|
|
||||||
|
$(dir_build)/%.o: $(dir_source)/%.c
|
||||||
|
@mkdir -p "$(@D)"
|
||||||
|
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||||
|
|
||||||
|
$(dir_build)/%.o: $(dir_source)/%.s
|
||||||
|
@mkdir -p "$(@D)"
|
||||||
|
$(COMPILE.s) $(OUTPUT_OPTION) $<
|
21
exosphere/bpmpfw/linker.ld
Normal file
21
exosphere/bpmpfw/linker.ld
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
|
||||||
|
OUTPUT_ARCH(arm)
|
||||||
|
|
||||||
|
ENTRY(_start)
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
. = 0x40003000;
|
||||||
|
|
||||||
|
__start__ = ABSOLUTE(.);
|
||||||
|
|
||||||
|
.text : ALIGN(4) { *(.text.start) *(.text*); . = ALIGN(4); }
|
||||||
|
.rodata : ALIGN(4) { *(.rodata*); . = ALIGN(4); }
|
||||||
|
.bss : ALIGN(8) { __bss_start__ = .; *(.bss* COMMON); . = ALIGN(8); __bss_end__ = .; }
|
||||||
|
|
||||||
|
. = ALIGN(4);
|
||||||
|
|
||||||
|
__end__ = ABSOLUTE(.);
|
||||||
|
|
||||||
|
__stack_top__ = 0x40005000;
|
||||||
|
__stack_bottom__ = 0x40004000;
|
||||||
|
}
|
58
exosphere/bpmpfw/src/emc.c
Normal file
58
exosphere/bpmpfw/src/emc.c
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "lp0.h"
|
||||||
|
#include "emc.h"
|
||||||
|
#include "pmc.h"
|
||||||
|
#include "timer.h"
|
||||||
|
|
||||||
|
void emc_trigger_timing_update(void) {
|
||||||
|
EMC_TIMING_CONTROL_0 = 1;
|
||||||
|
while (EMC_EMC_STATUS_0 & 0x800000) {
|
||||||
|
/* Wait until TIMING_UPDATE_STALLED is unset. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Puts DRAM into self refresh mode. */
|
||||||
|
void emc_put_dram_in_self_refresh_mode(void) {
|
||||||
|
/* Verify CH1_ENABLE [PMC]. */
|
||||||
|
if (!(EMC_FBIO_CFG7_0 & 4)) {
|
||||||
|
reboot();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear config. */
|
||||||
|
EMC_CFG_0 = 0;
|
||||||
|
emc_trigger_timing_update();
|
||||||
|
timer_wait(5);
|
||||||
|
|
||||||
|
/* Set calibration intervals. */
|
||||||
|
EMC_ZCAL_INTERVAL_0 = 0;
|
||||||
|
EMC_AUTO_CAL_CONFIG_0 = 0x600; /* AUTO_CAL_MEASURE_STALL | AUTO_CAL_UPDATE_STALL */
|
||||||
|
|
||||||
|
/* If EMC0 mirror is set, clear digital DLL. */
|
||||||
|
if (EMC0_CFG_DIG_DLL_0 & 1) {
|
||||||
|
EMC_CFG_DIG_DLL_0 &= 0xFFFFFFFE;
|
||||||
|
emc_trigger_timing_update();
|
||||||
|
while (EMC0_CFG_DIG_DLL_0 & 1) { /* Wait for EMC0 to clear. */ }
|
||||||
|
while (EMC1_CFG_DIG_DLL_0 & 1) { /* Wait for EMC1 to clear. */ }
|
||||||
|
} else {
|
||||||
|
emc_trigger_timing_update();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stall all transactions to DRAM. */
|
||||||
|
EMC_REQ_CTRL_0 = 3; /* STALL_ALL_WRITES | STALL_ALL_READS. */
|
||||||
|
while (!(EMC0_EMC_STATUS_0 & 4)) { /* Wait for NO_OUTSTANDING_TRANSACTIONS for EMC0. */ }
|
||||||
|
while (!(EMC1_EMC_STATUS_0 & 4)) { /* Wait for NO_OUTSTANDING_TRANSACTIONS for EMC1. */ }
|
||||||
|
|
||||||
|
/* Enable Self-Refresh Mode. */
|
||||||
|
EMC_SELF_REF_0 |= 1;
|
||||||
|
|
||||||
|
|
||||||
|
/* Wait until we see the right devices in self refresh mode. */
|
||||||
|
uint32_t num_populated_devices = 1;
|
||||||
|
if (EMC_ADR_CFG_0) {
|
||||||
|
num_populated_devices = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (((EMC0_EMC_STATUS_0 >> 8) & 3) != num_populated_devices) { /* Wait for EMC0 DRAM_IN_SELF_REFRESH to be correct. */ }
|
||||||
|
while (((EMC1_EMC_STATUS_0 >> 8) & 3) != num_populated_devices) { /* Wait for EMC1 DRAM_IN_SELF_REFRESH to be correct. */ }
|
||||||
|
}
|
53
exosphere/bpmpfw/src/emc.h
Normal file
53
exosphere/bpmpfw/src/emc.h
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
#ifndef EXOSPHERE_BPMPFW_EMC_H
|
||||||
|
#define EXOSPHERE_BPMPFW_EMC_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define EMC_BASE (0x7001B000)
|
||||||
|
|
||||||
|
#define EMC0_BASE (0x7001E000)
|
||||||
|
#define EMC1_BASE (0x7001F000)
|
||||||
|
|
||||||
|
|
||||||
|
#define MAKE_EMC_REG(ofs) ((*((volatile uint32_t *)(EMC_BASE + ofs))))
|
||||||
|
|
||||||
|
#define MAKE_EMC0_REG(ofs) ((*((volatile uint32_t *)(EMC0_BASE + ofs))))
|
||||||
|
#define MAKE_EMC1_REG(ofs) ((*((volatile uint32_t *)(EMC1_BASE + ofs))))
|
||||||
|
|
||||||
|
#define EMC_CFG_0 MAKE_EMC_REG(0x00C)
|
||||||
|
|
||||||
|
#define EMC_ADR_CFG_0 MAKE_EMC_REG(0x10)
|
||||||
|
|
||||||
|
#define EMC_TIMING_CONTROL_0 MAKE_EMC_REG(0x028)
|
||||||
|
|
||||||
|
#define EMC_SELF_REF_0 MAKE_EMC_REG(0x0E0)
|
||||||
|
|
||||||
|
#define EMC_MRW_0 MAKE_EMC_REG(0x0E8)
|
||||||
|
|
||||||
|
#define EMC_FBIO_CFG5_0 MAKE_EMC_REG(0x104)
|
||||||
|
|
||||||
|
#define EMC_MRW3_0 MAKE_EMC_REG(0x138)
|
||||||
|
|
||||||
|
#define EMC_AUTO_CAL_CONFIG_0 MAKE_EMC_REG(0x2A4)
|
||||||
|
|
||||||
|
#define EMC_REQ_CTRL_0 MAKE_EMC_REG(0x2B0)
|
||||||
|
|
||||||
|
#define EMC_EMC_STATUS_0 MAKE_EMC_REG(0x2B4)
|
||||||
|
#define EMC0_EMC_STATUS_0 MAKE_EMC0_REG(0x2B4)
|
||||||
|
#define EMC1_EMC_STATUS_0 MAKE_EMC1_REG(0x2B4)
|
||||||
|
|
||||||
|
#define EMC_CFG_DIG_DLL_0 MAKE_EMC_REG(0x2BC)
|
||||||
|
#define EMC0_CFG_DIG_DLL_0 MAKE_EMC0_REG(0x2BC)
|
||||||
|
#define EMC1_CFG_DIG_DLL_0 MAKE_EMC1_REG(0x2BC)
|
||||||
|
|
||||||
|
#define EMC_ZCAL_INTERVAL_0 MAKE_EMC_REG(0x2E0)
|
||||||
|
|
||||||
|
#define EMC_PMC_SCRATCH3_0 MAKE_EMC_REG(0x448)
|
||||||
|
|
||||||
|
#define EMC_FBIO_CFG7_0 MAKE_EMC_REG(0x584)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void emc_put_dram_in_self_refresh_mode(void);
|
||||||
|
|
||||||
|
#endif
|
83
exosphere/bpmpfw/src/i2c.c
Normal file
83
exosphere/bpmpfw/src/i2c.c
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
#include "i2c.h"
|
||||||
|
#include "timer.h"
|
||||||
|
|
||||||
|
/* Load hardware config for I2C4. */
|
||||||
|
void i2c_load_config(void) {
|
||||||
|
/* Set MSTR_CONFIG_LOAD, TIMEOUT_CONFIG_LOAD, undocumented bit. */
|
||||||
|
I2C_I2C_CONFIG_LOAD_0 = 0x25;
|
||||||
|
|
||||||
|
/* Wait a bit for master config to be loaded. */
|
||||||
|
for (unsigned int i = 0; i < 20; i++) {
|
||||||
|
timer_wait(1);
|
||||||
|
if (!(I2C_I2C_CONFIG_LOAD_0 & 1)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize I2C4. */
|
||||||
|
void i2c_init(void) {
|
||||||
|
/* Setup divisor, and clear the bus. */
|
||||||
|
I2C_I2C_CLK_DIVISOR_REGISTER_0 = 0x50001;
|
||||||
|
I2C_I2C_BUS_CLEAR_CONFIG_0 = 0x90003;
|
||||||
|
|
||||||
|
/* Load hardware configuration. */
|
||||||
|
i2c_load_config();
|
||||||
|
|
||||||
|
/* Wait a while until BUS_CLEAR_DONE is set. */
|
||||||
|
for (unsigned int i = 0; i < 10; i++) {
|
||||||
|
timer_wait(20000);
|
||||||
|
if (I2C_INTERRUPT_STATUS_REGISTER_0 & 0x800) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read the BUS_CLEAR_STATUS. Result doesn't matter. */
|
||||||
|
uint32_t unused_clear_status = I2C_I2C_BUS_CLEAR_STATUS_0;
|
||||||
|
|
||||||
|
/* Read and set the Interrupt Status. */
|
||||||
|
uint32_t int_status = I2C_INTERRUPT_STATUS_REGISTER_0;
|
||||||
|
I2C_INTERRUPT_STATUS_REGISTER_0 = int_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Writes a value to an i2c device. */
|
||||||
|
int i2c_write(unsigned int device, uint32_t val, unsigned int num_bytes) {
|
||||||
|
if (num_bytes > 4) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set device for 7-bit mode. */
|
||||||
|
I2C_I2C_CMD_ADDR0_0 = device << 1;
|
||||||
|
|
||||||
|
/* Load in data to write. */
|
||||||
|
I2C_I2C_CMD_DATA1_0 = val;
|
||||||
|
|
||||||
|
/* Set config with LENGTH = num_bytes, NEW_MASTER_FSM, DEBOUNCE_CNT = 4T. */
|
||||||
|
I2C_I2C_CNFG_0 = ((num_bytes << 1) - 2) | 0x2800;
|
||||||
|
|
||||||
|
i2c_load_config();
|
||||||
|
|
||||||
|
/* Config |= SEND; */
|
||||||
|
I2C_I2C_CNFG_0 |= 0x200;
|
||||||
|
|
||||||
|
|
||||||
|
while (I2C_I2C_STATUS_0 & 0x100) {
|
||||||
|
/* Wait until not busy. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return CMD1_STAT == SL1_XFER_SUCCESSFUL. */
|
||||||
|
return (I2C_I2C_STATUS_0 & 7) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Writes a byte val to reg for given device. */
|
||||||
|
int i2c_send_byte_command(unsigned int device, unsigned char reg, unsigned char b) {
|
||||||
|
uint32_t val = (reg) | (b << 8);
|
||||||
|
/* Write 1 byte (reg) + 1 byte (value) */
|
||||||
|
return i2c_write(device, val, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Actually reset device 27. This might turn off the screen? */
|
||||||
|
int i2c_send_reset_cmd(void) {
|
||||||
|
/* Write 00 to Device 27 Reg 00. */
|
||||||
|
return i2c_send_byte_command(27, 0, 0);
|
||||||
|
}
|
34
exosphere/bpmpfw/src/i2c.h
Normal file
34
exosphere/bpmpfw/src/i2c.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef EXOSPHERE_BPMPFW_I2C_H
|
||||||
|
#define EXOSPHERE_BPMPFW_I2C_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* I2C_BASE = I2C4. */
|
||||||
|
#define I2C_BASE (0x7000D000)
|
||||||
|
|
||||||
|
#define MAKE_I2C_REG(ofs) ((*((volatile uint32_t *)(I2C_BASE + ofs))))
|
||||||
|
|
||||||
|
#define I2C_I2C_CNFG_0 MAKE_I2C_REG(0x000)
|
||||||
|
|
||||||
|
#define I2C_I2C_CMD_ADDR0_0 MAKE_I2C_REG(0x004)
|
||||||
|
|
||||||
|
#define I2C_I2C_CMD_DATA1_0 MAKE_I2C_REG(0x00C)
|
||||||
|
|
||||||
|
#define I2C_I2C_STATUS_0 MAKE_I2C_REG(0x01C)
|
||||||
|
|
||||||
|
#define I2C_INTERRUPT_STATUS_REGISTER_0 MAKE_I2C_REG(0x068)
|
||||||
|
|
||||||
|
#define I2C_I2C_CLK_DIVISOR_REGISTER_0 MAKE_I2C_REG(0x06C)
|
||||||
|
|
||||||
|
#define I2C_I2C_BUS_CLEAR_CONFIG_0 MAKE_I2C_REG(0x084)
|
||||||
|
|
||||||
|
#define I2C_I2C_BUS_CLEAR_STATUS_0 MAKE_I2C_REG(0x088)
|
||||||
|
|
||||||
|
|
||||||
|
#define I2C_I2C_CONFIG_LOAD_0 MAKE_I2C_REG(0x08C)
|
||||||
|
|
||||||
|
void i2c_init(void);
|
||||||
|
|
||||||
|
int i2c_send_reset_cmd(void);
|
||||||
|
|
||||||
|
#endif
|
105
exosphere/bpmpfw/src/lp0.c
Normal file
105
exosphere/bpmpfw/src/lp0.c
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "lp0.h"
|
||||||
|
#include "i2c.h"
|
||||||
|
#include "pmc.h"
|
||||||
|
#include "emc.h"
|
||||||
|
#include "timer.h"
|
||||||
|
|
||||||
|
#define CACHE_CTRL (*((volatile uint32_t *)0x50040000))
|
||||||
|
|
||||||
|
#define PRI_ICTLR_COP_IER_CLR_0 (*((volatile uint32_t *)0x60004038))
|
||||||
|
#define SEC_ICTLR_COP_IER_CLR_0 (*((volatile uint32_t *)0x60004138))
|
||||||
|
#define TRI_ICTLR_COP_IER_CLR_0 (*((volatile uint32_t *)0x60004238))
|
||||||
|
#define QUAD_ICTLR_COP_IER_CLR_0 (*((volatile uint32_t *)0x60004338))
|
||||||
|
#define PENTA_ICTLR_COP_IER_CLR_0 (*((volatile uint32_t *)0x60004438))
|
||||||
|
#define HEXA_ICTLR_COP_IER_CLR_0 (*((volatile uint32_t *)0x60004538))
|
||||||
|
|
||||||
|
void reboot(void) {
|
||||||
|
/* Write MAIN_RST */
|
||||||
|
APBDEV_PMC_CNTRL_0 = 0x10;
|
||||||
|
while (1) {
|
||||||
|
/* Wait for reboot. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void set_pmc_dpd_io_pads(void) {
|
||||||
|
/* Read val from EMC_PMC scratch, configure accordingly. */
|
||||||
|
uint32_t emc_pmc_val = EMC_PMC_SCRATCH3_0;
|
||||||
|
APBDEV_PMC_DDR_CNTRL_0 = emc_pmc_val & 0x7FFFF;
|
||||||
|
if (emc_pmc_val & 0x40000000) {
|
||||||
|
APBDEV_PMC_WEAK_BIAS_0 = 0x7FFF0000;
|
||||||
|
}
|
||||||
|
/* Request to put pads in Deep Power Down. */
|
||||||
|
APBDEV_PMC_IO_DPD3_REQ_0 = 0x8FFFFFFF;
|
||||||
|
while (APBDEV_PMC_IO_DPD3_STATUS_0 != 0xFFFFFFF) { /* Wait a while. */ }
|
||||||
|
spinlock_wait(32);
|
||||||
|
APBDEV_PMC_IO_DPD4_REQ_0 = 0x8FFFFFFF;
|
||||||
|
while (APBDEV_PMC_IO_DPD4_STATUS_0 != 0xFFF1FFF) { /* Wait a while. */ }
|
||||||
|
spinlock_wait(32);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lp0_entry_main(void) {
|
||||||
|
/* Disable the BPMP Cache. */
|
||||||
|
CACHE_CTRL |= 0xC00;
|
||||||
|
|
||||||
|
/* Wait until the CPU Rail is turned off. */
|
||||||
|
while (APBDEV_PMC_PWRGATE_STATUS_0 & 1) { /* Wait for TrustZone to finish. */ }
|
||||||
|
|
||||||
|
/* Clamp the CPU Rail. */
|
||||||
|
APBDEV_PMC_SET_SW_CLAMP_0 |= 0x1;
|
||||||
|
while (!(APBDEV_PMC_CLAMP_STATUS_0 & 1)) { /* Wait for CPU Rail to be clamped. */ }
|
||||||
|
|
||||||
|
/* Waste some time. */
|
||||||
|
spinlock_wait(10);
|
||||||
|
|
||||||
|
/* Reset device 27 over I2C, then wait a while. */
|
||||||
|
i2c_init();
|
||||||
|
i2c_send_reset_cmd();
|
||||||
|
timer_wait(700);
|
||||||
|
|
||||||
|
/* Clear Interrupt Enable for BPMP in all ICTLRs. */
|
||||||
|
PRI_ICTLR_COP_IER_CLR_0 = 0xFFFFFFFF;
|
||||||
|
SEC_ICTLR_COP_IER_CLR_0 = 0xFFFFFFFF;
|
||||||
|
TRI_ICTLR_COP_IER_CLR_0 = 0xFFFFFFFF;
|
||||||
|
QUAD_ICTLR_COP_IER_CLR_0 = 0xFFFFFFFF;
|
||||||
|
PENTA_ICTLR_COP_IER_CLR_0 = 0xFFFFFFFF;
|
||||||
|
HEXA_ICTLR_COP_IER_CLR_0 = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
/* Write EMC's DRAM op into PMC scratch. */
|
||||||
|
if ((EMC_FBIO_CFG5_0 & 3) != 1) {
|
||||||
|
/* If DRAM_TYPE != LPDDR4, something's gone wrong. Reboot. */
|
||||||
|
reboot();
|
||||||
|
}
|
||||||
|
/* Write MRW3_OP into scratch. */
|
||||||
|
APBDEV_PMC_SCRATCH18_0 = (APBDEV_PMC_SCRATCH18_0 & 0xFFFFFF3F) | (EMC_MRW3_0 & 0xC0);
|
||||||
|
uint32_t mrw3_op = ((EMC_MRW3_0 & 0xC0) << 8) | (EMC_MRW3_0 & 0xC0);
|
||||||
|
APBDEV_PMC_SCRATCH12_0 = (APBDEV_PMC_SCRATCH12_0 & 0xFFFF3F3F) | mrw3_op;
|
||||||
|
APBDEV_PMC_SCRATCH13_0 = (APBDEV_PMC_SCRATCH13_0 & 0xFFFF3F3F) | mrw3_op;
|
||||||
|
|
||||||
|
/* Ready DRAM for deep sleep. */
|
||||||
|
emc_put_dram_in_self_refresh_mode();
|
||||||
|
|
||||||
|
/* Setup LPDDR MRW based on device config. */
|
||||||
|
EMC_MRW_0 = 0x88110000;
|
||||||
|
if (EMC_ADR_CFG_0 & 1) {
|
||||||
|
EMC_MRW_0 = 0x48110000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Put IO pads in Deep Power Down. */
|
||||||
|
set_pmc_dpd_io_pads();
|
||||||
|
|
||||||
|
/* Enable pad sampling during deep sleep. */
|
||||||
|
APBDEV_PMC_DPD_SAMPLE_0 |= 1;
|
||||||
|
|
||||||
|
/* Waste some more time. */
|
||||||
|
spinlock_wait(0x128);
|
||||||
|
|
||||||
|
/* Enter deep sleep. */
|
||||||
|
APBDEV_PMC_DPD_ENABLE_0 |= 1;
|
||||||
|
|
||||||
|
while (1) { /* Wait until we're asleep. */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
8
exosphere/bpmpfw/src/lp0.h
Normal file
8
exosphere/bpmpfw/src/lp0.h
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef EXOSPHERE_BPMPFW_LP0_H
|
||||||
|
#define EXOSPHERE_BPMPFW_LP0_H
|
||||||
|
|
||||||
|
void lp0_entry_main(void);
|
||||||
|
|
||||||
|
void reboot(void);
|
||||||
|
|
||||||
|
#endif
|
37
exosphere/bpmpfw/src/pmc.h
Normal file
37
exosphere/bpmpfw/src/pmc.h
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef EXOSPHERE_BPMPFW_PMC_H
|
||||||
|
#define EXOSPHERE_BPMPFW_PMC_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define PMC_BASE (0x7000E400)
|
||||||
|
|
||||||
|
#define MAKE_PMC_REG(ofs) ((*((volatile uint32_t *)(PMC_BASE + ofs))))
|
||||||
|
|
||||||
|
#define APBDEV_PMC_CNTRL_0 MAKE_PMC_REG(0x000)
|
||||||
|
|
||||||
|
#define APBDEV_PMC_DPD_SAMPLE_0 MAKE_PMC_REG(0x020)
|
||||||
|
|
||||||
|
#define APBDEV_PMC_DPD_ENABLE_0 MAKE_PMC_REG(0x024)
|
||||||
|
|
||||||
|
#define APBDEV_PMC_CLAMP_STATUS_0 MAKE_PMC_REG(0x02C)
|
||||||
|
|
||||||
|
#define APBDEV_PMC_PWRGATE_STATUS_0 MAKE_PMC_REG(0x038)
|
||||||
|
|
||||||
|
#define APBDEV_PMC_SCRATCH12_0 MAKE_PMC_REG(0x080)
|
||||||
|
#define APBDEV_PMC_SCRATCH13_0 MAKE_PMC_REG(0x084)
|
||||||
|
#define APBDEV_PMC_SCRATCH18_0 MAKE_PMC_REG(0x098)
|
||||||
|
|
||||||
|
|
||||||
|
#define APBDEV_PMC_WEAK_BIAS_0 MAKE_PMC_REG(0x2C8)
|
||||||
|
|
||||||
|
#define APBDEV_PMC_IO_DPD3_REQ_0 MAKE_PMC_REG(0x45C)
|
||||||
|
#define APBDEV_PMC_IO_DPD3_STATUS_0 MAKE_PMC_REG(0x460)
|
||||||
|
|
||||||
|
#define APBDEV_PMC_IO_DPD4_REQ_0 MAKE_PMC_REG(0x464)
|
||||||
|
#define APBDEV_PMC_IO_DPD4_STATUS_0 MAKE_PMC_REG(0x468)
|
||||||
|
|
||||||
|
#define APBDEV_PMC_SET_SW_CLAMP_0 MAKE_PMC_REG(0x47C)
|
||||||
|
|
||||||
|
#define APBDEV_PMC_DDR_CNTRL_0 MAKE_PMC_REG(0x4E4)
|
||||||
|
|
||||||
|
#endif
|
28
exosphere/bpmpfw/src/start.s
Normal file
28
exosphere/bpmpfw/src/start.s
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
.section .text.start
|
||||||
|
.align 4
|
||||||
|
.global _start
|
||||||
|
_start:
|
||||||
|
b crt0
|
||||||
|
.global _reboot
|
||||||
|
b reboot
|
||||||
|
|
||||||
|
.global crt0
|
||||||
|
.type crt0, %function
|
||||||
|
crt0:
|
||||||
|
@ setup to call lp0_entry_main
|
||||||
|
msr cpsr_f, #0xC0
|
||||||
|
msr cpsr_cf, #0xD3
|
||||||
|
ldr sp, =__stack_top__
|
||||||
|
ldr lr, =reboot
|
||||||
|
bl lp0_entry_main
|
||||||
|
infloop:
|
||||||
|
b infloop
|
||||||
|
|
||||||
|
|
||||||
|
.global spinlock_wait
|
||||||
|
.type spinlock_wait, %function
|
||||||
|
spinlock_wait:
|
||||||
|
sub r0, r0, #1
|
||||||
|
cmp r0, #0
|
||||||
|
bgt spinlock_wait
|
||||||
|
bx lr
|
15
exosphere/bpmpfw/src/timer.h
Normal file
15
exosphere/bpmpfw/src/timer.h
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef EXOSPHERE_BPMPFW_TIMER_H
|
||||||
|
#define EXOSPHERE_BPMPFW_TIMER_H
|
||||||
|
|
||||||
|
#define TIMERUS_CNTR_1US_0 (*((volatile uint32_t *)(0x60005010)))
|
||||||
|
|
||||||
|
static inline void timer_wait(uint32_t microseconds) {
|
||||||
|
uint32_t old_time = TIMERUS_CNTR_1US_0;
|
||||||
|
while (TIMERUS_CNTR_1US_0 - old_time <= microseconds) {
|
||||||
|
/* Spin-lock. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void spinlock_wait(uint32_t count);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in a new issue