297 lines
8.5 KiB
C
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)
|
|
;
|
|
}
|