MTK6573电源管理(PM)小结

来源:互联网 发布:aix测试端口 编辑:程序博客网 时间:2024/04/28 23:05

继续MTK平台的研究

开始研究电源管理,因为PM永远是嵌入式的核心技术,所以静下来走一遍流程。

MTK启动的过程:

硬件板载的启动入口为

static __init int board_init(void)
{
    mt6573_power_management_init();
    mt6573_board_init();
    return 0;
}


其中mt6573_board_init();的作用如下:

                mt6573_board_init() is used for chip-dependent code.
     *          It is suggested to put driver code in this function to do:
     *          1). Capability structure of platform devices.
     *          2). Define platform devices with their resources.
     *          3). Register MT65XX platform devices.

即注册各种devices 如:&pmem_multimedia_device,&mt6573_device_uart[i],&AudDrv_device,&mt6573_nand_dev,&kpd_pdev等等各种设备。

其中mt6573_power_management_init();初始化各种电源管理。下面是这个函数的源码:

void mt6573_power_management_init(void)
{
    /* Check Chip Version */
    gChipVer = DRV_Reg32(APHW_VER);
    printk("[%s]: gChipVer = 0x%x\r\n",__FUNCTION__, gChipVer);
    /* Load DVFS, DCM Setting from Spare*/
    mt6573_load_spare_settings();
    /* Clock Gating init, gated un-necessary power*/
    mt6573_CG_init();
    /* Set specific chip setting*/
    mt6573_chip_dep_init();         
    /* Power mamagement log init*/
    mt6573_log_init();
    /* DCM init*/
    mt6573_dcm_init();
    /* Thermal protect Init*/
    hwThermalProtectInit();
    /* Sleep Controller init*/
    slp_mod_init();
}

首先看mt6573_load_spare_settings();

void mt6573_load_spare_settings(void)
{
    u16 spar0;
    spar0 = 0;
    if (spar0 & SPARE_SECRET_KEY)
    {
        if(spar0 & SPARE_E1_PATCH)
            gChipVer = CHIP_VER_E2;

        if(spar0 & SPARE_DVFS_EN)
            bCanEnDVFS = TRUE;
        else
            bCanEnDVFS = FALSE;

        if(spar0 & SPARE_VAPROC_ADJUST_EN)
            bBUCK_ADJUST = TRUE;
        else
            bBUCK_ADJUST = FALSE;

        if(spar0 & SPARE_DVFS_LOG)
            bEnDVFSLog = TRUE;
        else
            bEnDVFSLog = FALSE;
    }
}
从代码中看是加载备用设备,但spar0 = 0所以后面的代码应该不会执行了。这是我个人的观点,希望有提出意见的。抛开这个问题可以看出主要是读标志位来给設的变量赋TURE or FALSE.

再看:mt6573_CG_init();

void mt6573_CG_init(void)
{
    UINT32 u4Val;
    struct cust_mt65xx_led *cust_led_list = get_cust_led_list();          //设置各种Led背光

    set_clock_listen(TRUE);
    DRV_SetReg32(APMCU_CG_CLR0, 0xffffffff);
    ... ...

    后面设置一些设备模块的时钟
}

mt6573_chip_dep_init();  设置芯片寄存器

。。。

重点看看sleep 控制器的初始化。

void slp_mod_init(void)
{
    slp_pmu_init();

    ost_mod_init();

    suspend_set_ops(&slp_suspend_ops);

    proc_create_data("slp_md_sta", 0444, NULL, &slp_md_sta_fops, NULL);
}

先看slp_pmu_init();

static void slp_pmu_init(void)
{
    u16 con1;

#ifdef VCORE_1_1_V_IN_SLEEP
    /* Vcore = 1.1V in sleep mode */
    con1 = (slp_read16(VCORE_CON1) & 0xfe0f) | (28 << 4);
    slp_write16(VCORE_CON1, con1);
#else
    /* Vcore = 0.9V in sleep mode */
    con1 = (slp_read16(VCORE_CON1) & 0xfe0f) | (20 << 4);
    slp_write16(VCORE_CON1, con1);
#endif

    /* Vaproc = 0.9V in sleep mode */
    con1 = (slp_read16(VAPROC_CON1) & 0xfe0f) | (20 << 4);
    slp_write16(VAPROC_CON1, con1);

    /* clear CCI_SRCLKEN to enable HW sleep-mode control */
    con1 = slp_read16(VA28_CON1) & ~(1U << 8);
    slp_write16(VA28_CON1, con1);

    slp_write_sync();
}
从代码上看,当睡眠有两种电压模式,一种是1.1V,还有一种是0.9V。依据芯片具体用哪种电压模式,然后写入寄存器。

再看:suspend_set_ops(&slp_suspend_ops);

void suspend_set_ops(struct platform_suspend_ops *ops)
{
    mutex_lock(&pm_mutex);
    suspend_ops = ops;
    mutex_unlock(&pm_mutex);
}

所以就是给slp_suspend_ops赋值就可以拉:

static struct platform_suspend_ops slp_suspend_ops = {
    .valid        = slp_suspend_ops_valid,
    .begin        = slp_suspend_ops_begin,
    .prepare    = slp_suspend_ops_prepare,
    .enter        = slp_suspend_ops_enter,
    .finish        = slp_suspend_ops_finish,
    .end        = slp_suspend_ops_end,
};

即初始化这个数据结构里的成员函数

其中重要的函数是

static int slp_suspend_ops_enter(suspend_state_t state)
{
    /* legacy log */
    printk("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
    printk("_Chip_pm_enter @@@@@@@@@@@@@@@@@@@@@@\n");
    printk(" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");

    if (slp_dump_gpio)
        gpio_dump_regs();

    if (get_chip_eco_ver() == CHIP_E1) {
        /* disable DCM to workaround EMI auto-refresh issue */
        MT6573_DISABLE_HW_DCM_AP();
    } else {
        MT6573_ENABLE_HW_DCM_AP();
    }

    if (slp_dump_regs)
        slp_dump_pm_regs();

    rtc_disable_writeif();

    slp_wake_reason = ost_go_to_sleep();

    rtc_enable_writeif();

    MT6573_DISABLE_HW_DCM_AP();

    return 0;
}

wake_reason_t ost_go_to_sleep(void)
{
    int i;
    unsigned long flags;
    struct mtk_irq_mask mask;
    wake_reason_t wr;

    spin_lock_irqsave(&ost_lock, flags);
    for (i = 0; i < NUM_WAKE_SRC; i++) {
        if (ost_wake_src & (1U << i))
            ost_enable_wake_irq(ost_wake_irq[i], false);
    }

    mt6573_irq_mask_all(&mask);

    ost_enable_wake_irq(MT6573_APOST_IRQ_LINE, true);

    /* OST will periodically wake up */
    wr = ost_enter_pwake_pause_mode();

    mt6573_irq_mask_restore(&mask);
    spin_unlock_irqrestore(&ost_lock, flags);

    return wr;
}
static wake_reason_t __tcmfunc ost_enter_pwake_pause_mode(void)
{
    u16 isr;
    u32 ufn, wakesta;
    unsigned long vbat, cnt = 0;

    while (1) {
        ufn = ost_get_wake_period(cnt) * 1000000 / OST_FRM_VAL;
        ost_write32(OST_UFN, ufn);
        ost_write32(OST_AFN, 0);

        /* unmask wakeup sources */
        ost_write32(OST_EVENT_MASK, ~ost_wake_src);

        /* unmask Pause Interrupt, Pause Abort and UFN Timeout */
        ost_write32(OST_INT_MASK, 0x0003);

        ost_write16(OST_CON, OST_CON_UFN_DOWN | OST_CON_EN);
        ost_write32(OST_CMD, OST_CMD_KEY | OST_CMD_CON_WR | OST_CMD_AFN_WR |
                             OST_CMD_UFN_WR | OST_CMD_OST_WR);
        while (!(ost_read16(OST_STA) & OST_STA_CMD_CPL));

        ost_write32(OST_CMD, OST_CMD_KEY | OST_CMD_PAUSE_STR);
        while (!(ost_read16(OST_STA) & OST_STA_CMD_CPL));

        /* flush L1 and L2 store buffers */
        ost_write_sync();

        /* enter WFI mode */
        __asm__ __volatile__("mcr p15, 0, %0, c7, c0, 4" : : "r" (0));

        wakesta = ost_read32(OST_WAKEUP_STA);
        isr = ost_read16(OST_ISR);

        ost_write32(OST_INT_MASK, 0x001f);
        ost_write16(OST_ISR, 0x001f);    /* write 1 clear */
        ost_write_sync();

        if (isr == 0x0004) {    /* UFN Timeout */
            vbat = BAT_Get_Battery_Voltage();
            printk("vbat-%lu = %lu\n", ++cnt, vbat);
            if (vbat <= SYSTEM_OFF_VOLTAGE) {
                printk("low battery => wake up\n");
                return WR_LOW_BAT;
            }
        } else {
            ost_output_wake_reason(wakesta, isr);
            return WR_WAKE_SRC;
        }
    }

    return WR_NONE;
}
static void ost_output_wake_reason(u32 wakesta, u16 isr)
{
    char str[128] = { 0 };

    if (wakesta & WAKE_SRC_KP)
        strcat(str, "KP ");

    if (wakesta & WAKE_SRC_MSDC0)
        strcat(str, "MSDC0 ");

    if (wakesta & WAKE_SRC_EINT)
        strcat(str, "EINT ");

    if (wakesta & WAKE_SRC_RTC)
        strcat(str, "RTC ");

    if (wakesta & WAKE_SRC_CCIF_MD)
        strcat(str, "CCIF_MD ");

    printk("wake up by %s(0x%x)(0x%x)\n", str, wakesta, isr);
}

这个函数是判断机器是以何种方式唤醒的,比如:电源键,USB中断,modem电话,时钟等等。


函数 proc_create_data("slp_md_sta", 0444, NULL, &slp_md_sta_fops, NULL);

static struct file_operations slp_md_sta_fops = {
    .open        = slp_md_sta_open,
    .read        = seq_read,
    .llseek        = seq_lseek,
    .release    = single_release,
};