/*
 * Copyright (C) 2021-2022 Suzhou Tiancheng Software Inc. All Rights Reserved.
 *
 */
/*
 * bsp_start.c
 *
 * created: 2022-02-18
 *  author: Bian
 */

/*
 * 1.安装中断向量
 * 2.初始化 Tick
 * 3.初始化 Console
 */

#include <stdint.h>
#include <string.h>
#include <larchintrin.h>

#include "cpu.h"
#include "ls2k500.h"

#include "os.h"

extern void machine_error_entry(void);
extern void exception_common_entry(void);
extern void tlbrefill_error_entry(void);

extern void loongarch_init_isr_table(void);
extern void Clock_initialize(void);

extern void console_init(unsigned int baudrate);
extern int main(void);

//-------------------------------------------------------------------------------------------------
// PMON 配置的芯片频率参数
//-------------------------------------------------------------------------------------------------

unsigned int cpu_frequency;             // 芯片主频
unsigned int ddr_frequency;
unsigned int hda_frequency;
unsigned int net_frequency;
unsigned int gmac_frequency;
unsigned int sb_frequency;
unsigned int gpu_frequency;

unsigned int pix0_frequency;
unsigned int pix1_frequency;

unsigned int lsu_frequency;
unsigned int print_frequency;
unsigned int apb_frequency;             // APB 总线频率
unsigned int usb_frequency;
unsigned int sata_frequency;

/**
 * 晶振频率
 */
unsigned int osc_frequency = 100000000; // default

static void fetch_frequency_setting(void)
{
    unsigned int clk_sel, r0, r1;
    unsigned int odiv, loopc, refc;

    unsigned long mcsr2 = __csrrd_d(LA_CSR_MCSR2);
    mcsr2 &= MCSR2_CCFREQ_MASK;
    osc_frequency = (unsigned int)mcsr2;

    clk_sel = READ_REG32(CHIP_SAMP0_BASE);
    clk_sel = (clk_sel >> SAMP0_CLK_SEL_SHIFT) & SAMP0_CLK_SEL_MASK;

    switch (clk_sel)
    {
        case 0:         // 硬件低频时钟配置模式
            cpu_frequency  = 500000000;
            ddr_frequency  = 480000000;
            net_frequency  = 320000000;
            gpu_frequency  = 200000000;
            hda_frequency  = 24000000;
            pix0_frequency = 100000000;
            pix1_frequency = 100000000;
            gmac_frequency = 0;
            apb_frequency  = 100000000;
            usb_frequency  = 100000000;
            sata_frequency = 100000000;
            break;

        case 1:         // 硬件高频时钟配置模式
            cpu_frequency  = 800000000;
            ddr_frequency  = 600000000;
            net_frequency  = 400000000;
            gpu_frequency  = 300000000;
            hda_frequency  = 24000000;
            pix0_frequency = 200000000;
            pix1_frequency = 200000000;
            gmac_frequency = 0;
            apb_frequency  = 150000000;
            usb_frequency  = 150000000;
            sata_frequency = 150000000;
            break;

        case 2:         // 软件配置模式
        {
            r0 = READ_REG32(PLL_NODE0_BASE);
            loopc = (r0 >> NODE0_DIV_LOOPC_SHIFT) & NODE0_DIV_LOOPC_MASK;
            refc  = (r0 >> NODE0_DIV_REFC_SHIFT)  & NODE0_DIV_REFC_MASK;
            
            if (r0 & NODE0_PLL_SEL)
            {
                odiv = (r0 >> NODE0_DIV_SHIFT) & NODE0_DIV_MASK;
                cpu_frequency = (unsigned long)osc_frequency * loopc / (odiv * refc);     // 500MHZ
            }

            r0 = READ_REG32(PLL_DDR0_BASE);
            r1 = READ_REG32(PLL_DDR1_BASE);
            loopc = (r0 >> DDR0_DIV_LOOPC_SHIFT) & DDR0_DIV_LOOPC_MASK;
            refc  = (r0 >> DDR0_DIV_REFC_SHIFT)  & DDR0_DIV_REFC_MASK;

            if (r0 & DDR0_PLL_SEL_DDR)
            {
                odiv = (r0 >> DDR0_DIV_SHIFT) & DDR0_DIV_MASK;
                ddr_frequency = (unsigned long)osc_frequency * loopc / (odiv * refc);     // 300MHZ
            }

            if (r0 & DDR0_PLL_SEL_HDA)
            {
                odiv = (r1 >> DDR1_DIV_HDA_SHIFT) & DDR1_DIV_HDA_MASK;
                hda_frequency = (unsigned long)osc_frequency * loopc / (odiv * refc);     // 23.077MHZ
            }

            if (r0 & DDR0_PLL_SEL_NETWORK)
            {
                odiv = r1 & DDR1_DIV_NETWORK_MASK;
                net_frequency = (unsigned long)osc_frequency * loopc / (odiv * refc);     // 300MHZ
            }
 
            r0 = READ_REG32(PLL_SOC0_BASE);
            r1 = READ_REG32(PLL_SOC1_BASE);
            loopc = (r0 >> SOC0_DIV_LOOPC_SHIFT) & SOC0_DIV_LOOPC_MASK;
            refc  = (r0 >> SOC0_DIV_REFC_SHIFT)  & SOC0_DIV_REFC_MASK;
                
            if (r0 & SOC0_PLL_SEL_GPU)
            {
                odiv = (r0 >> SOC0_DIV_GPU_SHIFT) & SOC0_DIV_GPU_MASK;
                gpu_frequency = (unsigned long)osc_frequency * loopc / (odiv * refc);     // 200MHZ
            }

            if (r0 & SOC0_PLL_SEL_GMAC)
            {
                odiv = (r1 >> SOC1_DIV_GMAC_SHIFT) & SOC1_DIV_GMAC_MASK;
                gmac_frequency = (unsigned long)osc_frequency * loopc / (odiv * refc);    // 125MHZ
            }

            if (r0 & SOC0_PLL_SEL_SB)
            {
                odiv = r1 & SOC1_DIV_SB_MASK;
                sb_frequency = (unsigned long)osc_frequency * loopc / (odiv * refc);      // 100MHZ
            }

            r0 = READ_REG32(PLL_PIX0_BASE);
            loopc = (r0 >> PIX0_DIV_LOOPC_SHIFT) & PIX0_DIV_LOOPC_MASK;
            refc  = (r0 >> PIX0_DIV_REFC_SHIFT)  & PIX0_DIV_REFC_MASK;
            
            if (r0 & PIX0_PLL_SEL)
            {
                odiv = (r0 >> PIX0_DIV_SHIFT) & PIX0_DIV_MASK;
                pix0_frequency = (unsigned long)osc_frequency * loopc / (odiv * refc);    // 108.93MHZ
            }

            r1 = READ_REG32(PLL_PIX1_BASE);
            loopc = (r1 >> PIX1_DIV_LOOPC_SHIFT) & PIX1_DIV_LOOPC_MASK;
            refc  = (r1 >> PIX1_DIV_REFC_SHIFT)  & PIX1_DIV_REFC_MASK;

            if (r1 & PIX1_PLL_SEL)
            {
                odiv = (r1 >> PIX1_DIV_SHIFT) & PIX1_DIV_MASK;
                pix1_frequency = (unsigned long)osc_frequency * loopc / (odiv * refc);    // 108.93MHZ
            }

            break;
        }

        case 3:         // 硬件bypass模式, PLL输出时钟全部使用外部输入系统时钟.
        default:
            /*
             * 根据硬件参数设置各个频率
             */
            gmac_frequency = 0;
            break;
    }

    if (gmac_frequency == 0)    // 硬件配置
    {
        return;
    }

    /*
     * 设备时钟分频配置寄存器, 分频计算公式为: fout=fin*(freqscale+1)/8.
     *
     * lsu_freqdiv分频系数除外
     */
    r0 = READ_REG32(PLL_FREQSCALE_BASE);

    /*
     * XXX 这三个输出频率细化?
     */
    sb_frequency    = (unsigned int)((unsigned long)sb_frequency * (((r0 >> FREQSCALE_SB_SHIFT) & FREQSCALE_SB_MASK) + 1) / 8);
    gpu_frequency   = (unsigned int)((unsigned long)gpu_frequency * (((r0 >> FREQSCALE_GPU_SHIFT) & FREQSCALE_GPU_MASK) + 1) / 8);
    cpu_frequency   = (unsigned int)((unsigned long)cpu_frequency * ((r0 & FREQSCALE_NODE_MASK) + 1) / 8);
    
    /*
     * 基准频率 net_frequency
     *
     * lsu   = 9.68MHZ
     * print = 300MHZ
     */
    lsu_frequency   = (unsigned int)((unsigned long)net_frequency / ((r0 >> FREQSCALE_LSU_SHIFT) & FREQSCALE_LSU_MASK));
    print_frequency = (unsigned int)((unsigned long)net_frequency * (((r0 >> FREQSCALE_PRINT_SHIFT) & FREQSCALE_PRINT_MASK) + 1) / 8);
    
    /*
     * 基准频率 sb_frequency
     *
     * apb  = 100MHZ
     * usb  = 100MHZ
     * sata = 100MHZ
     */
    apb_frequency   = (unsigned int)((unsigned long)sb_frequency * (((r0 >> FREQSCALE_APB_SHIFT) & FREQSCALE_APB_MASK) + 1) / 8);
    usb_frequency   = (unsigned int)((unsigned long)sb_frequency * (((r0 >> FREQSCALE_USB_SHIFT) & FREQSCALE_USB_MASK) + 1) / 8);
    sata_frequency  = (unsigned int)((unsigned long)sb_frequency * (((r0 >> FREQSCALE_SATA_SHIFT) & FREQSCALE_SATA_MASK) + 1) / 8);

    return;
}

//-----------------------------------------------------------------------------
// LS2K500 bsp start
//-----------------------------------------------------------------------------

extern int bsp_start_hook1(void);
extern int bsp_start_hook2(void);

void bsp_start(void)
{
	OS_ERR   uErr;
    uint64_t eentry;

    loongarch_interrupt_disable();

    eentry = PHYS_TO_CACHED(__csrrd_d(LA_CSR_MERREBASE));
    memcpy((void *)PHYS_TO_UNCACHED(eentry), (void *)machine_error_entry, 32);

    eentry = __csrrd_d(LA_CSR_EBASE);
    memcpy((void *)PHYS_TO_UNCACHED(eentry), (void *)exception_common_entry, 32);

    eentry = PHYS_TO_CACHED(__csrrd_d(LA_CSR_TLBREBASE));
    memcpy((void *)PHYS_TO_UNCACHED(eentry), (void *)tlbrefill_error_entry, 32);

    loongarch_init_isr_table();         /* initialize isr table */

    fetch_frequency_setting();

    /**
     * Initialize uC/OS-III
     */
    OSInit(&uErr);

    /**
     * 初始化内存管理
     */
    #if 1
    {
    	extern unsigned char _end;
        extern int heap_add_region(void *addr, size_t size);
        extern void malloc_create_oslock(void);

        size_t heap_start = ((size_t)&_end + 0x10) & ~0x0FULL;
        size_t heap_end   = PHYS_TO_CACHED(get_memory_size() - 8);
        heap_end -= 4*1024*1024;					/* 保留后 4M 内存 */

        if (heap_start + 1*1024*1024 < heap_end)	/* 至少有 1M 空间 */
        {
            heap_add_region((void *)heap_start, heap_end - heap_start);
            malloc_create_oslock();
        }
        else
        {
            for ( ; ; );
        }
    }
    #endif

    bsp_start_hook1();					/* hook1: 实现文件系统初始化等 */

    console_init(115200);               /* initialize console */

    Clock_initialize();                 /* initialize ticker */

    loongarch_interrupt_enable();		/* Enable CPU Interrept */

    bsp_start_hook2();					/* hook2: 实现 EMMC, USB 初始化等 */

    //-------------------------------------------------------------------------
    // goto main function
    //-------------------------------------------------------------------------

    /*
     * 使用汇编跳转, 不再使用 main();
     */
    volatile register unsigned long mainAddr = (unsigned long)main;

    asm volatile( "move $r20, %0 ; " : "=r"(mainAddr) );
    asm volatile( "jirl $r1, $r20, 0 ;" );

    /*
     * NEVER go here!
     */
    while (1)
        ;
}

//-------------------------------------------------------------------------------------------------

/*
 * @@ END
 */


