最近剛忙完 u-boot 的 logo driver , 還有解決 mmc remove 的時候整個 kernel 會
卡機 800 mS 的 bug , 有時間來繼續 這個 suspend mode 的 blog 了!!
PS. 這兩個 , 我後續會補上 !!
接上一篇 , 上一篇說道 PMIC 在 RTC-Mode 下會有一個詭異的 bug , 就是 在 PMIC Active mode 中 press power key 會一直送 INT 訊號 .
這個 INT 訊號 又不能 disable , PMIC wakeup 要用 !! 所以最後乾脆在 sleep 得時候打開 , resume 回來後 mask 掉 !! 不過之前 (sleep 狀態下) 有觸動 PWD_ON , 所以還是需要清除 .
真正 下 Sleep command 給 PMIC 的是 ASM 部份的 code , 所以在 tps65910 driver 中的 suspend 和 resume 可以處理 PWD_ON 的 mask 問題 , 片段 code 如下:
static int tps65910_i2c_suspend(struct i2c_client *i2c ,pm_message_t mesg )
{
struct tps65910 *priv_tps65910;
unsigned char buff;
priv_tps65910 = i2c_get_clientdata(i2c);
priv_tps65910->suspend_state = mesg.state;
if (priv_tps65910->suspend_state == PM_SUSPEND_RTCONLY)
{
//---- unmask PWRON INT event of tps65910.
tps65910_i2c_read(priv_tps65910, TPS65910_INT_MSK, 1 ,&buff);
buff &= ~INT_MSK_PWRON_IT_MSK_MASK;
tps65910_i2c_write(priv_tps65910, TPS65910_INT_MSK, 1 ,&buff);
//---- Save some context before into rtc-mode sleep .
tps65910_i2c_read(priv_tps65910, TPS65910_SLEEP_SET_LDO_OFF, 1 ,&priv_tps65910->sleep_ldo_off);
tps65910_i2c_read(priv_tps65910, TPS65910_SLEEP_KEEP_RES_ON, 1 ,&priv_tps65910->sleep_res_on);
tps65910_i2c_read(priv_tps65910, TPS65910_SLEEP_SET_RES_OFF, 1 ,&priv_tps65910->sleep_res_off);
tps65910_i2c_read(priv_tps65910, TPS65910_DEVCTRL, 1 ,&priv_tps65910->sleep_devctrl);
}
return 0 ;
}
static int tps65910_i2c_resume(struct i2c_client *i2c )
{
struct tps65910 *priv_tps65910;
unsigned char buff;
priv_tps65910 = i2c_get_clientdata(i2c);
if (priv_tps65910->suspend_state == PM_SUSPEND_RTCONLY)
{
//---- restore tps65910 context.
tps65910_i2c_write(priv_tps65910, TPS65910_DEVCTRL, 1 ,&priv_tps65910->sleep_devctrl);
tps65910_i2c_write(priv_tps65910, TPS65910_SLEEP_SET_LDO_OFF, 1 ,&priv_tps65910->sleep_ldo_off);
tps65910_i2c_write(priv_tps65910, TPS65910_SLEEP_KEEP_RES_ON, 1 ,&priv_tps65910->sleep_res_on);
tps65910_i2c_write(priv_tps65910, TPS65910_SLEEP_SET_RES_OFF, 1 ,&priv_tps65910->sleep_res_off);
//---- mask PWRON INT event of tps65910.
tps65910_i2c_read(priv_tps65910, TPS65910_INT_MSK, 1 ,&buff);
buff |= INT_MSK_PWRON_IT_MSK_MASK;
tps65910_i2c_write(priv_tps65910, TPS65910_INT_MSK, 1 ,&buff);
}
priv_tps65910->suspend_state = 0;
return 0 ;
}
由 code 可以看出 ,我還是保留原來 mem mode 的 sleep. suspend 時將 ASM 有動到的 暫存器全部備份 , 並且打開 PWD_ON 的 mask. resume 時候 , 會將 PWD_ON mask 掉.
先說明 suspend & resume function , code 如下 , 因為suspend 過程中 , 可能 user 一直 press power key , 所以會導致一堆 IRQ 訊號 , 因此 suspend 時候先關閉 IRQ , resuem 後在打開 IRQ .
static int tps65910_rtc_suspend(struct platform_device *pdev, pm_message_t msg)
{
struct device *dev = &pdev->dev;
struct pmic_data *pmic = dev_get_platdata(dev);
struct tps65910 *priv_tps65910;
priv_tps65910 = (struct tps65910 *)pmic->pmic;
priv_tps65910->rtc_suspend_state = msg.state;
mask_rtc_irq_bit(pmic , RTC_INTERRUPTS_REG_IT_TIMER);
if (priv_tps65910->rtc_suspend_state == PM_SUSPEND_RTCONLY)
{
disable_irq(priv_tps65910->irq_num);
}
else if (device_may_wakeup(dev))
enable_irq_wake(priv_tps65910->irq_num);
return 0;
}
static int tps65910_rtc_resume(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct pmic_data *pmic = dev_get_platdata(dev);
struct tps65910 *priv_tps65910;
priv_tps65910 = (struct tps65910 *)pmic->pmic;
tps65910_set_rtc_irq_bit(pmic , priv_tps65910->rtc_irq_bits);
if (priv_tps65910->rtc_suspend_state == PM_SUSPEND_RTCONLY)
{
enable_irq(priv_tps65910->irq_num);
}
else if (device_may_wakeup(dev))
disable_irq_wake(priv_tps65910->irq_num);
priv_tps65910->rtc_suspend_state = 0;
return 0;
}
接著 , 就是詭異的 INT 清除方式了 !!
PMIC 我們 project 只想要看 ALARM , 所以不是 ALARM 的IRQ 全部不處理.
我試過很多方式 ,都會需要很久的時間才能 clean IRQ Flag , 只有我目前這樣的方式最快 清除, 就是先設定 DEV_ON flag , 然後 清除 status flag , 接著在還原 DEV_ON flag , 就可以很快的清除 !!
我問過 FAE , 竟然回答我 多清除幾次就可 (xxxx 我也知道 , 花很久時間 kernel 會卡卡的) , 我會這樣做 是因為 , 我想 PWD_ON 的 IRQ 可能要確認已經收到並且 確認 PMIC 已經 power on , 所以我才會 設定 DEV_ON Flag .......
目前還沒有出現狀況 , 就先當這樣可以 working 吧 !! @@
static irqreturn_t tps65910_rtc_interrupt(int irq, void *data)
{
struct rtc_device *rtc = (struct rtc_device *)data;
struct device *dev = rtc->dev.parent;
struct pmic_data *pmic = dev_get_platdata(dev);
unsigned long events = 0;
int ret = IRQ_NONE;
int res;
u8 rd_reg,dev_reg,sts_reg;
res = tps65910_rtc_read_u8(pmic, &sts_reg,TPS65910_INT_STS);
if (res)
goto out;
if (!(sts_reg & INT_STS_RTC_ALARM_IT_MASK ))
{
tps65910_rtc_read_u8(pmic,&dev_reg,TPS65910_DEVCTRL);
dev_reg |= DEVCTRL_DEV_ON_MASK ;
tps65910_rtc_write_u8(pmic,dev_reg,TPS65910_DEVCTRL);
tps65910_rtc_read_u8(pmic,&rd_reg,TPS65910_INT_STS);
tps65910_rtc_write_u8(pmic,rd_reg,TPS65910_INT_STS);
dev_reg &= ~DEVCTRL_DEV_ON_MASK;
tps65910_rtc_write_u8(pmic,dev_reg,TPS65910_DEVCTRL);
return IRQ_HANDLED;
}
//==== starte handle Alarm irq.
RTC-only mode大概就這三項比較詭異 , 目前都用 "閃" 的方式 避開 , 等有時間在慢慢研究吧 !!
PS. 基本上不可能有時間 , 後面還有一堆bug , system library 要修改 @@ !!
預定 2013/03 上市...哇咧......... !!
沒有留言:
張貼留言