2012年11月19日 星期一

AM335X RTC-Only mode suspend ...... part #3

最近剛忙完 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 上市...哇咧......... !!


沒有留言:

張貼留言