Files
BPI-CM9-Linux/aml-comp/uboot/bl30/rtos_sdk/soc/riscv/n200_pso.c
T

297 lines
8.5 KiB
C

/*
* Copyright (c) 2021-2022 Amlogic, Inc. All rights reserved.
*
* SPDX-License-Identifier: MIT
*/
#include <stdio.h>
#include <string.h>
#include "FreeRTOS.h"
#include "task.h"
#include "common.h"
#include "interrupt_control_eclic.h"
#include "n200_timer.h"
#include "riscv_encoding.h"
#include "n200_pso.h"
#include "ao_domain_ress.h"
#include "watchdog.h"
#define PSO_PRINT(...) printf(__VA_ARGS__)
#define PSO_PRINT_DEBUG(...) //printf(__VA_ARGS__)
uint32_t *pso_save_area = (uint32_t *)PSO_SAVE_AREA_BASE;
static uint32_t *pmp_save = (uint32_t *)PSO_SAVE_PMP_BASE;
static uint32_t *eclic_int_save = (uint32_t *)PSO_SAVE_ECLIC_INT_BASE;
static uint8_t *eclic_mth_save = (uint8_t *)PSO_SAVE_ECLIC_MTH_BASE;
static uint32_t *aocpu_irq_sel_save = (uint32_t *)PSO_SAVE_AO_IRQ_SEL_BASE;
static inline void pso_save_area_init(void)
{
memset((void *)pso_save_area, 0, PSO_SAVE_AREA_SIZE);
pso_save_area[0] = (uint32_t)&pso_resume;
PSO_PRINT_DEBUG("pso_save_area[0] addr: 0x%p\n", pso_save_area);
PSO_PRINT_DEBUG("pso_save_area[0] data: 0x%x\n", pso_save_area[0]);
}
static inline void pmp_cfg_save(void)
{
pmp_save[0] = read_csr(pmpaddr0);
pmp_save[1] = read_csr(pmpaddr1);
pmp_save[2] = read_csr(pmpaddr2);
pmp_save[3] = read_csr(pmpaddr3);
pmp_save[4] = read_csr(pmpaddr4);
pmp_save[5] = read_csr(pmpaddr5);
pmp_save[6] = read_csr(pmpaddr6);
pmp_save[7] = read_csr(pmpaddr7);
pmp_save[8] = read_csr(pmpcfg0);
pmp_save[9] = read_csr(pmpcfg1);
}
static inline void pmp_cfg_restore(void)
{
write_csr(pmpaddr0, pmp_save[0]);
write_csr(pmpaddr1, pmp_save[1]);
write_csr(pmpaddr2, pmp_save[2]);
write_csr(pmpaddr3, pmp_save[3]);
write_csr(pmpaddr4, pmp_save[4]);
write_csr(pmpaddr5, pmp_save[5]);
write_csr(pmpaddr6, pmp_save[6]);
write_csr(pmpaddr7, pmp_save[7]);
write_csr(pmpcfg0, pmp_save[8]);
write_csr(pmpcfg1, pmp_save[9]);
PSO_PRINT_DEBUG("pmpaddr0: %lx\n", read_csr(pmpaddr0));
PSO_PRINT_DEBUG("pmpaddr1: %lx\n", read_csr(pmpaddr1));
PSO_PRINT_DEBUG("pmpaddr2: %lx\n", read_csr(pmpaddr2));
PSO_PRINT_DEBUG("pmpaddr3: %lx\n", read_csr(pmpaddr3));
PSO_PRINT_DEBUG("pmpaddr4: %lx\n", read_csr(pmpaddr4));
PSO_PRINT_DEBUG("pmpaddr5: %lx\n", read_csr(pmpaddr5));
PSO_PRINT_DEBUG("pmpaddr6: %lx\n", read_csr(pmpaddr6));
PSO_PRINT_DEBUG("pmpaddr7: %lx\n", read_csr(pmpaddr7));
PSO_PRINT_DEBUG("pmpcfg0 : %lx\n", read_csr(pmpcfg0));
PSO_PRINT_DEBUG("pmpcfg1 : %lx\n", read_csr(pmpcfg1));
}
static inline void interrupt_cfg_save(void)
{
uint32_t *ptr, i = 0;
uint32_t *base = (uint32_t *)(ECLIC_ADDR_BASE + ECLIC_INT_IP_OFFSET);
uint32_t *upper = (uint32_t *)(base + ECLIC_NUM_INTERRUPTS);
/* save all IP/IE/ATTR/CTRL bits for all interrupt sources */
for (ptr = base; ptr < upper; ptr++, i++)
eclic_int_save[i] = *ptr;
/* save eclic mth */
*eclic_mth_save = *(volatile uint8_t *)(ECLIC_ADDR_BASE + ECLIC_MTH_OFFSET);
/* save irq map AOCPU_IRQ_SELx */
for (i = 0; i < PSO_SAVE_AO_IRQ_SEL_SIZE_W; i++)
aocpu_irq_sel_save[i] = REG32(AOCPU_IRQ_SEL0 + i * 4);
}
static inline void interrupt_cfg_restore(void)
{
uint32_t *ptr, i = 0;
uint32_t *base = (uint32_t *)(ECLIC_ADDR_BASE + ECLIC_INT_IP_OFFSET);
uint32_t *upper = (uint32_t *)(base + ECLIC_NUM_INTERRUPTS);
eclic_set_nlbits(0);
/* restore all IP/IE/ATTR/CTRL bits for all interrupt sources */
for (ptr = base; ptr < upper; ptr++, i++)
*ptr = eclic_int_save[i];
/* restore eclic mth */
*(volatile uint8_t *)(ECLIC_ADDR_BASE + ECLIC_MTH_OFFSET) = *eclic_mth_save;
/* restore irq map AOCPU_IRQ_SELx */
for (i = 0; i < PSO_SAVE_AO_IRQ_SEL_SIZE_W; i++)
REG32(AOCPU_IRQ_SEL0 + i * 4) = aocpu_irq_sel_save[i];
}
void add_hw_pwrctrl_fsm_awake_irq(uint16_t sel_irq)
{
uint8_t reg = (uint8_t)(sel_irq / 32);
uint8_t os = (uint8_t)(sel_irq % 32);
/* add fsm awake irq */
REG32(AO_PWRCTRL_AOCPU_IRQ_MASK0 + reg * 4) |= (1 << os);
/* enable fsm awake irq latch mode */
REG32(AO_IRQCTRL_PWRUP_AOCPU_IRQ_MODE0 + reg * 4) |= (1 << os);
}
void remove_hw_pwrctrl_fsm_awake_irq(uint16_t sel_irq)
{
uint8_t reg = (uint8_t)(sel_irq / 32);
uint8_t os = (uint8_t)(sel_irq % 32);
if (((REG32(AO_IRQCTRL_PWRUP_AOCPU_IRQ_LATCH0 + reg * 4) & (1 << os)) >> os) == 1) {
PSO_PRINT("irq %d trigger\n", sel_irq);
/* clear fsm awake irq latch */
REG32(AO_IRQCTRL_PWRUP_AOCPU_IRQ_LATCH_CLR0 + reg * 4) |= (1 << os);
/* set eclic irq pending */
eclic_set_pending(GetIrqInner(sel_irq));
}
/* delete fsm awake irq */
REG32(AO_PWRCTRL_AOCPU_IRQ_MASK0 + reg * 4) &= ~(1 << os);
/* disable fsm awake irq latch mode */
REG32(AO_IRQCTRL_PWRUP_AOCPU_IRQ_MODE0 + reg * 4) &= ~(1 << os);
}
static inline void clear_ao_irqctrl_pwrup_latch(void)
{
REG32(AO_IRQCTRL_PWRUP_AOCPU_IRQ_LATCH_CLR0) = 0xffffffff;
REG32(AO_IRQCTRL_PWRUP_AOCPU_IRQ_LATCH_CLR1) = 0xffffffff;
REG32(AO_IRQCTRL_PWRUP_AOCPU_IRQ_LATCH_CLR2) = 0xffffffff;
REG32(AO_IRQCTRL_PWRUP_AOCPU_IRQ_LATCH_CLR3) = 0xffffffff;
}
#ifdef AOCPU_WFI_AUTO_PSO
static inline void wfi_mode_en(void)
{
/* set sleep value 1 */
write_csr(0x811, 1);
/* set wfi mode en to use auto fsm off */
REG32(AO_PWRCTRL_AOCPU_CORE_WFI_MODE_EN) = 1;
}
static inline void wfi_mode_dis(void)
{
/* set sleep value 1 */
write_csr(0x811, 0);
/* set wfi mode en to use auto fsm off */
REG32(AO_PWRCTRL_AOCPU_CORE_WFI_MODE_EN) = 0;
}
#endif
static inline void start_hw_pwrctrl_fsm_off(void)
{
#ifdef AOCPU_WFI_AUTO_PSO
wfi_mode_en();
#else
/* else set start off manually */
REG32(AO_PWRCTRL_AOCPU_FSM_START_OFF) = 1;
#endif
}
static inline void init_pwrctrl_fsm(void)
{
// disable mempd step power on
REG32(AO_PWRCTRL_AOCPU_AUTO_OFF_CTRL0) |= (0x0 << 21);
// need pwr_ack
REG32(AO_PWRCTRL_AOCPU_AUTO_OFF_CTRL0) |= (0x1 << 29);
// need on/off handshake, off
REG32(AO_PWRCTRL_AOCPU_AUTO_OFF_CTRL0) &= ~((1 << 19) | (1 << 17));
// need on/off pactive, on: don't need, off: need
REG32(AO_PWRCTRL_AOCPU_AUTO_OFF_CTRL0) &= ~((1 << 18) | (1 << 16));
// power switch
REG32(AO_PWRCTRL_AOCPU_AUTO_OFF_CTRL0) |= (1 << 11); // off
REG32(AO_PWRCTRL_AOCPU_AUTO_OFF_CTRL0) &= ~(1 << 10); // on
// isolation enable
REG32(AO_PWRCTRL_AOCPU_AUTO_OFF_CTRL0) |= (1 << 7); // off
REG32(AO_PWRCTRL_AOCPU_AUTO_OFF_CTRL0) &= ~(1 << 6); // on
// focrst enable
REG32(AO_PWRCTRL_AOCPU_AUTO_OFF_CTRL0) |= (1 << 3); // off
REG32(AO_PWRCTRL_AOCPU_AUTO_OFF_CTRL0) &= ~(1 << 2); // on
// delay
REG32(AO_PWRCTRL_AOCPU_AUTO_OFF_CTRL0) |= (1 << 24);
// 10us from start-off for non-shaking
REG32(AO_PWRCTRL_AOCPU_TIMER_TH_01) = (1 << 16) | (10 << 0);
REG32(AO_PWRCTRL_AOCPU_TIMER_TH_23) = (1 << 16) | (1 << 0);
REG32(AO_PWRCTRL_AOCPU_TIMER_TH_45) = (1 << 0);
// power on
// 2us after power ack
REG32(AO_PWRCTRL_AOCPU_TIMER_TH_45) |= (2 << 16);
// 10us after mem on
REG32(AO_PWRCTRL_AOCPU_TIMER_TH_67) |= (1 << 16) | (10 << 0);
// enable fsm
REG32(AO_PWRCTRL_AOCPU_AUTO_OFF_CTRL0) |= (1 << 31);
}
void pso_cut_off_power(unsigned long ra)
{
PSO_PRINT("pso: cut off power ra: 0x%lx\n", ra);
/* disable all irq */
for (int irq = 0; irq < ECLIC_NUM_INTERRUPTS; irq++)
eclic_disable_interrupt(irq);
init_pwrctrl_fsm();
/* register irqs to fsm for wake-up */
for (int irq = 0; irq < SOC_ECLIC_NUM_INTERRUPTS; irq++) {
if (eclic_interrupt_inner[irq] != ECLIC_INT_NUM_INIT)
add_hw_pwrctrl_fsm_awake_irq(eclic_interrupt_inner[irq]);
}
/* set start off */
start_hw_pwrctrl_fsm_off();
/* set oscin to 32K */
aoclk_to_32k();
#if defined(CONFIG_WATCHDOG)
vWatchdogUpdate(true);
#endif
asm volatile ("wfi");
/* Never return here */
while (1)
;
}
void pso_power_off(void)
{
PSO_PRINT_DEBUG("pso: power off\n");
/* clear latch */
clear_ao_irqctrl_pwrup_latch();
/* pso data save area init */
pso_save_area_init();
pmp_cfg_save();
interrupt_cfg_save();
pso_context_save();
}
void pso_resume(void)
{
#if defined(CONFIG_WATCHDOG)
vWatchdogUpdate(false);
#endif
aoclk_to_24m();
PSO_PRINT("Warm boot AO\n");
PSO_PRINT_DEBUG("pso: resume\n");
pso_save_area[0] = 0;
pmp_cfg_restore();
interrupt_cfg_restore();
PSO_PRINT_DEBUG("irq latch0: 0x%x\n", REG32(AO_IRQCTRL_PWRUP_AOCPU_IRQ_LATCH0));
PSO_PRINT_DEBUG("irq latch1: 0x%x\n", REG32(AO_IRQCTRL_PWRUP_AOCPU_IRQ_LATCH1));
PSO_PRINT_DEBUG("irq latch2: 0x%x\n", REG32(AO_IRQCTRL_PWRUP_AOCPU_IRQ_LATCH2));
PSO_PRINT_DEBUG("irq latch3: 0x%x\n", REG32(AO_IRQCTRL_PWRUP_AOCPU_IRQ_LATCH3));
/* unregister irqs to fsm */
for (int irq = 0; irq < SOC_ECLIC_NUM_INTERRUPTS; irq++) {
if (eclic_interrupt_inner[irq] != ECLIC_INT_NUM_INIT)
remove_hw_pwrctrl_fsm_awake_irq(eclic_interrupt_inner[irq]);
}
#ifdef AOCPU_WFI_AUTO_PSO
wfi_mode_dis();
#endif
pso_context_restore();
/* Never return here */
while (1)
;
}