分类: LINUX
2013-01-31 11:10:52
继续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();初始化各种电源管理。下面是这个函数的源码:
首先看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,
};
http://blog.csdn.net/Dwyane_zhang/article/details/7613626