2012年12月28日 星期五

Memory Testing utility.

目前我們換了 Hynix 200 mDDR , 跑起來還算正常 , 只是 RTC-only mode 進入 suspend 後 卻沒有辦法 resume , suspend 前我將 resume 的 phy address 內容 printk 出來和 warn reset 後將 mDDR 中 的對應值 printf 出來比對 , 發現相同 (前 5 個 word) .

猜想說是否 , mDDR 的 timing 有問題 , 所以找個 memory 的壓力測試程式來跑跑看吧 !!

尋找了一下 , GNU 有 memtester , 並且是 open source ... 就用這個吧 !!
http://pyropus.ca/software/memtester/

Download 後需要移植到ARM上 ,
其實很簡單 , 修改 conf-cc & conf-ld 兩個檔案內容即可.
將 cc 改成對應的 Cross-compiler 即可.

另外猜想 , mDDR 連續 Access 時候耗電比較大 , Idle 時候耗電比較小 ,
所以稍微修改一下在 每個測試項目結束的時候進行 usleep , 讓耗電減少 , 看看能不能
測出 mDDR 輕載和重載 過程導致 電源不穩定.

執行結果如下:

[root@jeff :/bin]# memtester 100
memtester version 4.3.0 (32-bit)
Copyright (C) 2001-2012 Charles Cazabon.
Licensed under the GNU General Public License version 2 (only).

pagesize is 4096
pagesizemask is 0xfffff000
want 100MB (104857600 bytes)
got  100MB (104857600 bytes), trying mlock ...locked.
Loop 1:
  Stuck Address       : ok        
  Random Value        : ok       Sleep 92mS
  Compare XOR         : ok       Sleep 187mS
  Compare SUB         : ok       Sleep 246mS
  Compare MUL         : ok       Sleep 20mS
  Compare DIV         : ok       Sleep 187mS
  Compare OR          : ok       Sleep 84mS
  Compare AND         : ok       Sleep 205mS
  Sequential Increment: ok       Sleep 232mS
  Solid Bits          : ok       Sleep 122mS
  Block Sequential    : ok       Sleep 146mS
  Checkerboard        : ok       Sleep 70mS
  Bit Spread          : ok       Sleep 163mS
  Bit Flip            : ok       Sleep 27mS
  Walking Ones        : ok       Sleep 133mS
  Walking Zeroes      : ok       Sleep 147mS
  8-bit Writes        : ok       Sleep 12mS
  16-bit Writes       : ok       Sleep 208mS

2012年11月22日 星期四

Internal SRAM Testing


呼 ,  最近剛剛修正一些 system 相關的 library so file , 和生產測試程式要用的 library .
最後發現 , 有一個項目是測試 CPU 內部的SRMA 讀寫動作 !!

哇咧 , AM335x ROM code booting 就會把 MLO 載入 SRAM 中執行 , 要是 SRAM 有問題, 那不就開不了機 ?? 後續哪需要測試這個 SRAM ?? 並且我們應用上也沒有使用這個 SRAM !! 真是三條線 .... 好吧依照需求寫 Driver 吧 !!

我想有兩種方式可以完成 ,
A. 使用 字元Driver  , 然後完成 mmap 的 function .讓 測試程式可以 mmap 出這段 SRAM , 然後進行 read/write testing.

B. 使用 Block Driver 將 SRAM 製作程一個 Block Driver . 說真的 Block Driver 我比較少碰到 , 所以不知道怎樣寫.... @@ . 不過我想應該有現成的... 找找看吧 !!

最後發現 MTD + MTD_PLATRAM 就可以將 某一段 ADDRESS map 到 mtbblock 上 來當作 Storage 存取用. 因為只是測試要用 , 所以我打算編譯程 ko module .

第一步驟 , 打開這兩個 driver , 修改後的 .config diff file.

-# CONFIG_MTD_RAM is not set
+CONFIG_MTD_RAM=m
 # CONFIG_MTD_ROM is not set
 # CONFIG_MTD_ABSENT is not set

@@ -671,7 +671,7 @@
 # CONFIG_MTD_COMPLEX_MAPPINGS is not set
 # CONFIG_MTD_PHYSMAP is not set
 # CONFIG_MTD_PHYSMAP_OF is not set
-# CONFIG_MTD_PLATRAM is not set
+CONFIG_MTD_PLATRAM=m


第二步驟 , 在 am335x_evm.c 中增加 SRAM platform device . 如下(diff list):

+//---- internel ram
+static struct platdata_mtd_ram am335x_sram_data =
+{
+    .bankwidth = 1,
+};
+
+static struct resource am335x_sram_resource =
+{
+    .start = AM33XX_SRAM_PA,
+    .end   = AM33XX_SRAM_PA + 64 * 1024 - 1,
+    .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device am335x_sram_mtd_device =
+{
+    .name = "mtd-ram",
+    .id = 0,
+    .dev =
+    {
+        .platform_data = &am335x_sram_data,
+    },
+    .num_resources = 1,
+    .resource = &am335x_sram_resource,
+};
+
+static void internal_ram(void)
+{
+int status = 0;
+
+    status = platform_device_register(&am335x_sram_mtd_device);
+    if (status)
+        pr_err("failed to register matrix am335x_sram_mtd_device device\n");
+
+}
 

要使用前 , 在 code 中掛載 Driver ko
    system("modprobe plat-ram");

測試完畢後 , 卸載 Driver ko

    system("rmmod plat_ram");
    system("rmmod map_ram");


因為我有完整的 build kernel module . 所以用 modprobe  就可以自動掛在相依的 ko driver .

卸載時 , 我手動將沒有用到的 一起卸載 , 所以多一行 卸載 map_ram.

這樣就完成了 !!


另外要注意一件事情 , 就是  Suspend 過程中有一段 code 會放到 SRAM 中執行 , 小心不要因為 測試弄毀原來中的 CODE , 或是 suspend 過程中. 記得在將 suspend code 放入 SRAM 中.

2012年11月20日 星期二

MMC0 card remove bug , kernel lag.

最近在測試 整個 kernel 功能的時候發現 , SD Card 移除的過程中 kernel 會"卡機" 約800mS , 這 800 mS 中 , 連 kernel driver 中的 add_timer() function 都停滯 !!
初步 判別 , 應該卡在 IRQ 中.

尋找了半天 ,發現 , Card remove 過程 , 會呼叫
int _mmc_detect_card_removed(struct mmc_host *host)
在這個 function 中 會呼叫 ret = host->bus_ops->alive(host);
alive() ; 在core/sd.c 中有註冊 , 內容是

static int mmc_sd_alive(struct mmc_host *host)
{
    return mmc_send_status(host->card, NULL);
}


好玩的事情來了 , Card 已經拔除 , 還送 status command 給 Host !!

mmc_send_status() 定義在 mmc_ops.c  中

int mmc_send_status(struct mmc_card *card, u32 *status)
{
    int err;
    struct mmc_command cmd = {0};

    BUG_ON(!card);
    BUG_ON(!card->host);

    cmd.opcode = MMC_SEND_STATUS;
    if (!mmc_host_is_spi(card->host))
        cmd.arg = card->rca << 16;
    cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;

    err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
    if (err)
        return err;

    /* NOTE: callers are required to understand the difference
     * between "native" and SPI format status words!
     */
    if (status)
        *status = cmd.resp[0];

    return 0;
}


送出 command 後要等  cmd done. cmd done 需要 mmc controller 的irq 中斷 .
AM335x BSP 的 mmc 中斷流程如下:

omap_hsmmc_irq()
   omap_hsmmc_do_irq()
      ## if have error.
          omap_hsmmc_reset_controller_fsm()

主要卡在 omap_hsmmc_reset_controller_fsm() 中.

首先 , 我不喜歡 IRQ 執行太多事情 , 所以先用 tasklet 分開 ,這時候要注意 , MMC 的 IRQ 會一直送 ,直到IRQ 事件處理完畢 , tasklet 只能排程一次 , 所以要先關閉   IRQ , 等 tasklit 完成後在打開.

修改後如下:

static void omap_hsmmc_irq_tasklet(unsigned long arg)
{
struct omap_hsmmc_host *host = (struct omap_hsmmc_host *) arg;

    omap_hsmmc_do_irq(host, host->irq_status,host->irq);

//---- reenable mmc irq signal.

    OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
    spin_unlock(&host->irq_lock);

}


static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
{
struct omap_hsmmc_host *host = dev_id;


#if defined(USING_TASKLET)

//---- disable mmc irq signal.
    spin_lock(&host->irq_lock);
    OMAP_HSMMC_WRITE(host->base, ISE, 0);

    host->irq_status = OMAP_HSMMC_READ(host->base, STAT);

    tasklet_schedule(&host->omap_irq_tasklet);

#else

int status;

    status = OMAP_HSMMC_READ(host->base, STAT);
    do {
        omap_hsmmc_do_irq(host, status, irq);
        /* Flush posted write */
        status = OMAP_HSMMC_READ(host->base, STAT);
    } while (status & INT_EN_MASK);


#endif

    return IRQ_HANDLED;
}


這樣後還是會 "卡機" , 看一下主要卡住的地方 omap_hsmmc_reset_controller_fsm()

unsigned long limit = (loops_per_jiffy  *
            msecs_to_jiffies(MMC_TIMEOUT_MS));


 ..............
...............

        while ((!(OMAP_HSMMC_READ(host->base, SYSCTL) & bit)) &&
                    (i++ < limit))
            cpu_relax();



首先 , cpu_relax() 其實應該是 一堆 NOP 指令(我之前沒有用過@@) , 主要是 loops_per_jiffy 這個參數 ,這個參數應該是每秒執行多少個 nop 指令 , 所以上面這樣的 code 感覺怪怪的 , 這個 cpu_relax() + OMAP_HSMMC_READ + 其他判別 的 code 會執行 數百萬次 ....當然會卡住 !!
都改成 tacklet 了 , 就想說換成一般 mdelay 的方式 , 這個 limit 只是 timeout 的變數, 沒有太大影響. 修改後如下:

static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host,
                           unsigned long bit)
{
unsigned long i = 0;

#if !defined(USING_TASKLET)
unsigned long limit = (loops_per_jiffy  *
            msecs_to_jiffies(MMC_TIMEOUT_MS));
#endif

    OMAP_HSMMC_WRITE(host->base, SYSCTL,
             OMAP_HSMMC_READ(host->base, SYSCTL) | bit);

    /*
     * OMAP4 ES2 and greater has an updated reset logic.
     * Monitor a 0->1 transition first
     */
    if (mmc_slot(host).features & HSMMC_HAS_UPDATED_RESET)
    {

#if defined(USING_TASKLET)
        while ((!(OMAP_HSMMC_READ(host->base, SYSCTL) & bit)) &&
                    (i++ < MMC_TIMEOUT_MS))
            mdelay(1);

#else
        while ((!(OMAP_HSMMC_READ(host->base, SYSCTL) & bit)) &&
                    (i++ < limit))
            cpu_relax();
#endif

......................
}

這樣.... 終於不 lag 了....... 呼 ...... BSP 有這樣的 bug 還真難找....!!





2012年11月19日 星期一

u-boot support logo image.

u-boot 起來(排除 u-boot console wait time)  到 kernel logo show 出來會有段蠻常的時間 , 如果沒有任何 logo , 使用者可能會認為...當機 !!

好吧 , 加上 frame buffer driver 到 u-boot 上吧 !! 不過要先確認 backlit dirver 可以  working . backlit 比較簡單 , 所以就不說明了 !!

開始 lcd driver 移植到 u-boot 吧 !!


第一步驟 , 要先啟動 LCDC 相關的 clock / power domain .

#define CM_PER_LCDC_CLKCTRL         (CM_PER + 0x18) /* LCDC  */
#define CM_PER_LCDC_CLKSTCTRL       (CM_PER + 0x148)/* LCDC */

第二步驟 , 設定 LCDC 的 PLL Clock .
void disp_pll_clocks(void)
{

u32 clkmode, clksel, div_m2;

    clkmode = readl(CM_CLKMODE_DPLL_DISP);
    clksel = readl(CM_CLKSEL_DPLL_DISP);
    div_m2 = readl(CM_DIV_M2_DPLL_DISP);

    /* Set the PLL to bypass Mode */
    clkmode = (clkmode & 0xfffffff8) | 0x00000004;
    writel(clkmode, CM_CLKMODE_DPLL_DISP);
    while ((readl(CM_IDLEST_DPLL_DISP) & 0x00000100) != 0x00000100);

    clksel = clksel & (~0x7ffff);
    clksel = clksel | ((DISPPLL_M << 0x8) | DISPPLL_N);
    writel(clksel, CM_CLKSEL_DPLL_DISP);

    div_m2 = div_m2 & 0xFFFFFFE0;
    div_m2 = div_m2 | DISPPLL_M2;
    writel(div_m2, CM_DIV_M2_DPLL_DISP);

    clkmode = (clkmode & 0xfffffff8) | 0x7;
    writel(clkmode, CM_CLKMODE_DPLL_DISP);
    while ((readl(CM_IDLEST_DPLL_DISP) & 0x00000001) != 0x1);


}


第三步驟, 設定 LCD 相關 GPIO 的 function.

static struct module_pin_mux lcdc_pin_mux[] =
{

    {OFFSET(gpmc_csn3),   (MODE(7) | PULLUDEN)},    /* GPIO2_0 ,LCD_PWREN */

    {OFFSET(lcd_data0),     MODE(0) | PULLUDEN},    /* Makesure lcd_data_0  */
    {OFFSET(lcd_data1),     MODE(0) | PULLUDEN},    /* Makesure lcd_data_1  */
    {OFFSET(lcd_data2),     MODE(0) | PULLUDEN},    /* Makesure lcd_data_2  */
    {OFFSET(lcd_data3),     MODE(0) | PULLUDEN},    /* Makesure lcd_data_3  */
    {OFFSET(lcd_data4),     MODE(0) | PULLUDEN},    /* Makesure lcd_data_4  */
    {OFFSET(lcd_data5),     MODE(0) | PULLUDEN},    /* Makesure lcd_data_5  */
    {OFFSET(lcd_data6),     MODE(0) | PULLUDEN},    /* Makesure lcd_data_6  */
    {OFFSET(lcd_data7),     MODE(0) | PULLUDEN},    /* Makesure lcd_data_7  */
    {OFFSET(lcd_data8),     MODE(0) | PULLUDEN},    /* Makesure lcd_data_8  */
    {OFFSET(lcd_data9),     MODE(0) | PULLUDEN},    /* Makesure lcd_data_9  */
    {OFFSET(lcd_data10),    MODE(0) | PULLUDEN},    /* Makesure lcd_data_10 */
    {OFFSET(lcd_data11),    MODE(0) | PULLUDEN},    /* Makesure lcd_data_11 */
    {OFFSET(lcd_data12),    MODE(0) | PULLUDEN},    /* Makesure lcd_data_12 */
    {OFFSET(lcd_data13),    MODE(0) | PULLUDEN},    /* Makesure lcd_data_13 */
    {OFFSET(lcd_data14),    MODE(0) | PULLUDEN},    /* Makesure lcd_data_14 */
    {OFFSET(lcd_data15),    MODE(0) | PULLUDEN},    /* Makesure lcd_data_15 */

    {OFFSET(gpmc_ad15),     MODE(1) | PULLUDEN},    /* lcd_data_16 */
    {OFFSET(gpmc_ad14),     MODE(1) | PULLUDEN},    /* lcd_data_17 */
    {OFFSET(gpmc_ad13),     MODE(1) | PULLUDEN},    /* lcd_data_18 */
    {OFFSET(gpmc_ad12),     MODE(1) | PULLUDEN},    /* lcd_data_19 */
    {OFFSET(gpmc_ad11),     MODE(1) | PULLUDEN},    /* lcd_data_20 */
    {OFFSET(gpmc_ad10),     MODE(1) | PULLUDEN},    /* lcd_data_21 */
    {OFFSET(gpmc_ad9),      MODE(1) | PULLUDEN},    /* lcd_data_22 */
    {OFFSET(gpmc_ad8),      MODE(1) | PULLUDEN},    /* lcd_data_23 */

    {OFFSET(lcd_pclk),          MODE(0) | PULLUDEN},    /* lcd_pclk */
    {OFFSET(lcd_vsync),         MODE(0) | PULLUDEN},    /* lcd_vsync */
    {OFFSET(lcd_hsync),         MODE(0) | PULLUDEN},    /* lcd_hsync */
    {OFFSET(lcd_ac_bias_en),    MODE(0) | PULLUDEN},    /* lcd_ac_bias */


    {-1},
};


第四步驟, 在 video 下增加 driver file .
Make file 中多這項目. am335x_lcdc.c 需要由 kernel 那邊移植過來. 去掉 kernel 相關的部份.

COBJS-$(CONFIG_VIDEO_AM335X) += am335x_lcdc.o

第五步驟 , 在 am335_evm.h 中增加下列選項
u-boot 本身就有支援 lcd 的 driver library 了 , 所以要打開 CONFIG_LCD ,
另外SYS_CONSOLE_IS_IN_ENV 要打開 , 不然 console 會跑到 LCD 上 ,並非 UART.


/* LCD Support */
#define CONFIG_LCD
#ifdef CONFIG_LCD

 #define CONFIG_VIDEO_AM335X
 #define CONFIG_SYS_CONSOLE_IS_IN_ENV   // prevent console switch to LCD.

//---- select LCD type .
 #define CONFIG_LCD_TM050RBH01

//---- setting logo file name
 #define CONFIG_LOGO_FILE_NAME   "inst_logo.raw"

//---- enable backlit support.
 #define CONFIG_CMD_BACKLIT


#endif


最後一個步驟 ,在 lcd.h 中增加 相關的 struct 欄位, 這個欄位最後和 kernel 的相同,
這樣移植過程比較順利..


#elif defined(CONFIG_VIDEO_AM335X)

enum panel_type
{
    QVGA = 0,
    WVGA,
};

enum panel_shade
{
    MONOCHROME = 0,
    COLOR_ACTIVE,
    COLOR_PASSIVE,
};

typedef struct vidinfo
{

    ushort  vl_col;     /* Number of columns (i.e. 160) */
    ushort  vl_row;     /* Number of rows (i.e. 100) */
    u_char  vl_bpix;    /* Bits per pixel, 0 = 1 */

//---- Frame buffer parameter.
    enum panel_type panel_type ;
    int max_bpp;
    int min_bpp;
    enum panel_shade panel_shade;

    int ac_bias;        /* AC Bias Pin Frequency */
    int ac_bias_intrpt;   /* AC Bias Pin Transitions per Interrupt */
    int dma_burst_sz; /* DMA burst size */
    int bpp;    /* Bits per pixel */
    int fdd;     /* FIFO DMA Request Delay */

    /* TFT Alternative Signal Mapping (Only for active) */
    unsigned char tft_alt_mode;
    /* 12 Bit Per Pixel (5-6-5) Mode (Only for passive) */
    unsigned char stn_565_mode;

    /* Mono 8-bit Mode: 1=D0-D7 or 0=D0-D3 */
    unsigned char mono_8bit_mode;
    unsigned char sync_edge;          /* Horizontal and Vertical Sync Edge: 0=rising 1=falling */
    unsigned char sync_ctrl;         /* Horizontal and Vertical Sync: Control: 0=ignore */
    unsigned char raster_order;   /* Raster Data Order Select: 1=Most-to-least 0=Least-to-most */
    int fifo_th;        /* DMA FIFO threshold */

//---- LCD Panel data
    unsigned short  width;
    unsigned short  height;
    int     hfp;        /* Horizontal front porch */
    int     hbp;        /* Horizontal back porch */
    int     hsw;        /* Horizontal Sync Pulse Width */
    int     vfp;        /* Vertical front porch */
    int     vbp;        /* Vertical back porch */
    int     vsw;        /* Vertical Sync Pulse Width */
    unsigned int    pxl_clk;    /* Pixel clock */
    unsigned char   invert_pxl_clk; /* Invert Pixel clock */
    unsigned char invert_line_clock;         /* Invert line clock */
    unsigned char invert_frm_clock;      /* Invert frame clock  */

//---- par parameter.
    unsigned long   pseudo_palette[32];
    unsigned char   *v_palette_base;
    unsigned int    palette_sz;
    void            *video_mem;

}vidinfo_t;


注意事項.
Driver 其實只要 implentment 下列這三個 function 就可以了.
其中要注意 setcolreg 的function , AM335x TFT 24/32 BPP 沒有 調色盤 , 不需要
設定..


void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue);
void lcd_ctrl_init(void *lcdbase);
void lcd_enable(void);

打完收工.......

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 上市...哇咧......... !!


2012年11月6日 星期二

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

接續上一篇 !!

TI 的 GP-EVM H/W 上使用了  CPLD + EEPROM , 讓 BSP 可以支援很多不同版本的 EVB .

我們的產品並不會像這樣使用 ( CPLD +EEPROM = 錢多 ) , 所以我將
board-am335xevm.c 大修改 , 將 CPLD 和 EEPROM 判別的部份全部移除 , 只留下我們要的  init function.

詭異的問題來了 ,我在  .init_machine   = am335x_ec810_init, 直接呼叫 setup_ec810_board(ec810_dev_cfg); 去 initial 所有有用到的 device . 這樣會導致 resume 當機 . (如上一篇問的 ) !!

換個方式 , 使用和 EVB 一樣的方式 , 掛 eeprom driver , 然後在 eeprom 的 .setup function 中去執行 setup_ec810_board(ec810_dev_cfg); 就可以安然的 resume ....
詭異了.....!!

時間關係 , 所以先用這樣的方式解決 , 等有時間在詳細去追這個詭異的問題 !!



static void am335x_evm_setup(struct memory_accessor *mem_acc, void *context)
{
    DEBUG_MSG(">>>>========<<<<");
    setup_ec810_board(ec810_dev_cfg);

}

static struct at24_platform_data am335x_baseboard_eeprom_info = {
    .byte_len       = (256*1024) / 8,
    .page_size      = 64,
    .flags          = AT24_FLAG_ADDR16,
    .setup          = am335x_evm_setup,
    .context        = (void *)NULL,
};





因為 RTC-Only mode , 所以 CPU 都沒 power 了 , 只能靠 PMIC 叫醒 CPU (reset ) .
我們產品的需求 要有 alarm wake up , 所以 RTC 也只能使用  PMIC 的 RTC 了 .

PMIC 有兩個途徑可以起來 , RTC Irq & Power key press.
一般 active mode (PMIC 供電 , CPU 運作) , 使用者可以透過power key 去 執行 suspend 的動作, 由 CPU 的 GPIO pin 去 handle 就可  !!

問題來了 tps65910 卻會發 INT 給 CPU , 並且清除不了這個 INT , 會讓我的 CPU 忙於處理這個 INT , 問 FAE , 竟然跟我說多清幾次就可.... 哇咧..... 使用者, 如果按著不放, 所其他 AP 都鈍鈍的...... @@ !!

偏偏 PMIC wakeup 需要有 INT 訊號 , 所以 PWD_ON 的 mask 也不能清除 . 一清除PMIC 又馬上進入 sleep mode .

Try 了很多情況 , 最後終於解決了 !! 解決方式 , 下一篇說明 !!

PS. 現在要去忙 u-boot 的 ehpwm & ecap driver 了 , 一個給 LCD backlit 用(u-boot logo ) , 另外一個給 Buzzer 用 (u-boot 下只有 logo , 不想顯示太多東西 , 所以只好用 beep 聲去表現一些工程模式的動作 ) !!







2012年10月29日 星期一

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

經過漫長的半個月的移植 , 終於可以動作了 , 並且可以正常的 sleep & resume .
過程很多細節的問題 , 都克服了 , 這篇文章紀錄一些目前我覺得重點和詭異的地方.


A. 首先 , 我將 TI 給我的 kernel source code , 移植到我們的 source code 中 !!
    雖然這個版本並非 official release , 有點不穩定 , 不過總比沒有東西好 , 其中的
    bug , 只好自己解決  , 或是 call FAE 一起解決了 .

    移植過程中 , 我為了舊有的 deep sleep mode 還存在 , 所以我自己增加一個
    echo "rtconly" > /sys/power/state 來區分 之前的 "mem" (Deep sleep mode ).

    移植完畢後 , 卻沒有辦法 sleep , 下 command 給PMIC , 要 PMIC 進入sleep mode ,
    卻失敗 ,  經過一段時間 delay 後就回 kernel 了.  (參考下列的 code , 不過是 組合語言 )

    原來 , PMIC 要進入 sleep mode , 須要有幾個condiction , 除了 DEV_SLP 外 ,
    還需要 SLEEP Pin 的配合 , 修改 H/W , 讓 Sleep 空接 , PMIC Power on default
    SLEEP pin 會 pull_low.
   
    進入 sleep mode 後發現 mDDR Power 被關閉 , 試著修改一些 KEEP_ON 暫存器 還是
    沒有用 , 經過 Study , 有可能 PMIC 直接 進入 off state , 並非 sleep state !!
    果然 , PWRHOLD Pin 需要輸入 high , 讓 PMIC 不要回到  off state !!
 
     搞定上面的問題後終於可以 sleep 了 , 並且 mDDR power 也持續供應 .

     問題來了 , wake up 卻........死當 !! 

     在 u-boot , kernel 中 將 kernel resume 中的 physical add 中的 前 4 byte printf 出來
     看看 , 是否 mDDR 有正確 keep data  !!
 
     內容值都一樣 , 表示 mDDR 有 keep data , 那.....哪邊死當呢 ????
     (下篇說明  , 這個詭異的問題 )



//--------------------------------
/* Put the PLLs in bypass mode */
//--------------------------------
put_pll_bypass:
    pll_bypass  core, virt_core_clk_mode, virt_core_idlest, core_val
    pll_bypass  ddr, virt_ddr_clk_mode, virt_ddr_idlest, ddr_val
    pll_bypass  disp, virt_disp_clk_mode, virt_disp_idlest, disp_val
    pll_bypass  per, virt_per_clk_mode, virt_per_idlest, per_val
    pll_bypass  mpu, virt_mpu_clk_mode, virt_mpu_idlest, mpu_val


    //---- check sleep mode .
    //---- PM_SUSPEND_MEM = 3 , deep sleep mode .
    //---- PM_SUSPEND_RTCONLY = 4 , rtc only mode.

    ldr r0, sleep_mode
    cmp r0, #3
    beq deepsleep

//--------------------------------
    /* RTC Only mode sleep. */

    ldr     r0, i2c0_clk_addr_virt
    mov     r1, #2
    str     r1, [r0]

    /* set clock registers */
    ldr     r0, i2c0_addr_virt

    mov     r1, #3
    strh    r1, [r0, #I2C_PSC]
    mov     r1, #54
    strh    r1, [r0, #I2C_SCLL]
    mov     r1, #54
    strh    r1, [r0, #I2C_SCLH]

    /* set own address */
    mov     r1, #1
    strh    r1, [r0, #I2C_OA]

    /* take out of reset */
    mov     r1, #0x8000
    strh    r1, [r0, #I2C_CON]


    /* only enable xrdy interrupt */
    ldrh    r1, [r0, #I2C_IRQENABLE_SET]
    strh    r1, [r0, #I2C_IRQENABLE_CLR]
    mov     r1, #0x1F
    strh    r1, [r0, #I2C_IRQENABLE_SET]

    /* disable auto idle. starterware does this */
    ldrh    r1, [r0, #I2C_SYSC]
    bic     r1, r1, #1
    strh    r1, [r0, #I2C_SYSC]


    /* write the sleep sequence */

    mov     r1, #0x2D
    mov     r2, #0x50   @ TPS65910_INT_STS
    mov     r3, #0xFF   @ clear all irq event .
    bl  am33xx_i2c0_write

    mov     r1, #0x2D
    mov     r2, #0x52   @ TPS65910_INT_STS2
    mov     r3, #0xFF   @ clear all irq event.
    bl  am33xx_i2c0_write

    mov     r1, #0x2D
    mov     r2, #0x43   @ TPS65910_SLEEP_SET_LDO_OFF
//    mov     r3, #0xFF   @ turn off  LDO in sleep mode.
    mov     r3, #0xDF   @ turn off  LDO in sleep mode.
    bl  am33xx_i2c0_write

    mov     r1, #0x2D
    mov     r2, #0x42   @ TPS65910_SLEEP_KEEP_RES_ON
    mov     r3, #0x00   @
    bl  am33xx_i2c0_write

    mov     r1, #0x2D
    mov     r2, #0x44   @ TPS65910_SLEEP_SET_RES_OFF
    mov     r3, #0x0E
    bl  am33xx_i2c0_write


    mov     r1, #0x2D
    mov     r2, #0x3F   @ TPS65910_DEVCTRL
//    mov     r3, #0x62
    mov     r3, #0x32
    bl  am33xx_i2c0_write


    /* Wait a bit, then abort */
    mov     r0, #0x100000

wait_i2c0:
    subs    r0, r0, #1
    bne     wait_i2c0
    b   abort

2012年10月11日 星期四

再度開啟AM335x 專案......

經過幾個月的等待  ,  TI 是出了 RTC-only mode 的 code , 不過....
我感覺是派幾個工程師 為我們需求寫的 , 並且確定在 下一版 official release 中不會有 rtc-mode 的 code.

所以表示 , 我們又是白老鼠 ?? 幫 TI 驗證這段 rtc-mode 的 code @@

真是有點無力.......

rtc-mode 主要在RTC block 內有 三個 register (RTC_SCRATCH0 ~ 3 )  是給使用者存東西的 , 在這 3 個 register 中放入 resume function address & 一些判別的資料 .

修改 u-boot , 在 reset 時候這些暫存器內容如果正確 , 直接回  DDR resume address function. 就可以叫醒 CPU 了 .

當然 suspend 的時候直接控制 PMIC , 將 CPU 電源都切除 (RTC block 除外) , 這樣就會省許多電力.

當 user 要 resume 的時候 , 就將 CPU 所有電源打開,  並且發 reset  給 CPU  , CPU 由 ROM Code 開始跑.....

u-boot 檢查到CPU RTC 內的那三個 register 正確 , 就直接回 DDR resume function, 整個 system 就可以 resume 了.


說的簡單 , 實際上 困難重重 ,首先 要對一些暫存器 , 設定 PMIC 的 sleep mode .
光儲存必要暫存器的值 .... 就翻天了.....
那麼多暫存器....那都能了解 , 並且  clock domain , power domain 還有 M3 這樣多的東西, 等我弄懂....往生了..... @@

TI 給的 code 又超亂 (臨時 加上去  , 並且感覺手動測試一下就丟出來了 , 反正不是 official release ) ,  我要 patch 半天.... 才搞懂改了那些 .

不過 , 看 patch file , 修改我的 code , 難免有錯誤.....
debug 才是大問題 , 一切靠自己了 @@








2012年6月19日 星期二

專案終止 ~~~~~

經過漫長的奮鬥 , 我們還是需要等 RTC only sleep mode 的 BSP .......
並且....可能要到 2012/09/xx 才會收到 !!!
我們還需要花時間從標準 BSP 移植到 我們的 source 中 ....又是一段漫長時間 !!

經過上級指示 .... 先放棄這個 Project , 更換 CPU ....... !!
我終於可以不用當 "白老鼠" 了 ~~~~~ , 也不用 衝 " 世界第一 " ...... !!

So.... 這個 Blog 也跟著這個專案一起終止 ....... !!!

2012年6月15日 星期五

tps65910 RTC Function....

我們可以進入 Deep sleep mode 後 , 希望可以由 Power key 和 RTC Alarm 來wakeup CPU .
很不幸的 , AM335x 的 BSP 中沒有辦法由 RTC Alarm wakeup CPU .
經過TI 證實... RTC Alarm wakeup CPU 有 Bug , 所以目前還不會 working.
Deep Sleep mode 並不是我們最終想要的 mode , 我們要的是 RTC only mode .
好像....RTC only mode 有同樣的 bug ......... !!
再次說明..... 我們是白老鼠...... @@ !!

經過討論 , 想利用 外部RTC 透過 GPIO 方式來叫醒 CPU .... !!
So.... 只好利用tps65910 的 RTC + INT 功能來實現了 !!
BSP 又少 TSP65910 RTC 的 Driver ......... @@ !!

好吧 , 接著來進行 TPS65910 RTC 的Driver .....

2012年6月14日 星期四

mDDR Suspend function Porting......

弄完CODEC 後,接下來要開始難度較高的Kernel Suspend 了。
聽說AM33x + LPDDR(mDDR) 我們是台灣第一家始用.......
2012/06 /07 :
這幾天稍微修正 BSP  後 ,順利可以suspend , 並且可以順利 Resume ..........
其中有些錯誤 ,
 A :Sleep33xx.S  中有ㄧ個暫存器 248是錯誤的, 應該是252。
B: At same file  , bl data_macro_cm 確沒有完整得 return , 所以 0xa4  沒有設定。
C: pm33xx.c  中的M3  ipc  resume  address  是固定的位置 , 應該用計算方式比較好。
 另外 , pm33x.h  內要填寫mDDR  的參數 , 在 Resume  時要填回。
這樣就可以 suspend resume 了。
假日在補上 和CODEC  一樣的 Function list 。



:./kernel/power/main.c
state_store()
{

    enter_state();

}

:./kernel/power/suspend.c
enter_state()
{
    valid_state();              ==> suspend_valid_only_mem();
    sys_sync();
    suspend_prepare();

    pm_restrict_gfp_mask();

    suspend_devices_and_enter();

// wakeup Process......

    pm_restore_gfp_mask();
    suspend_finish();
}

suspend_prepare()
{

    pm_prepare_console();

    pm_notifier_call_chain();

    usermodehelper_disable();

    suspend_freeze_processes();

    suspend_thaw_processes();

    usermodehelper_enable();


}


suspend_devices_and_enter()
{

    trace_machine_suspend();
    suspend_ops->begin()        ==> am33xx_pm_begin();

    suspend_console();

    suspend_test_start();

    dpm_suspend_start();

    suspend_test_finish();
    suspend_test();

    suspend_enter();        // System sleeping..... !

//  Resume process......

    suspend_test_start();
    dpm_resume_end();
    suspend_test_finish();
    resume_console();

    suspend_ops->end            ==>  am33xx_pm_end();

    trace_machine_suspend();
}

suspend_enter()
{

    suspend_ops->prepare        ==> am33xx_pm_prepare_late();

    dpm_suspend_noirq();

    suspend_ops->prepare_late   ==> xxx

    disable_nonboot_cpus();

    arch_suspend_disable_irqs();

    syscore_suspend();

    suspend_ops->enter          ==> am33xx_pm_enter(); --> am33xx_pm_suspend();


// Resume Process ....

    arch_suspend_enable_irqs();

    enable_nonboot_cpus();

    suspend_ops->wake           ==> xxx

    dpm_resume_noirq();

    suspend_ops->finish         ==> am33xx_pm_finish();



}



am33xx_do_sram_idle()
{

ENTRY(am33xx_do_wfi)

## EMIF config for low power mode .
## Disable EMIF.
## Weak pull down for DQ, DM.
## Disable VTP.
## IO to work in mDDR mode.         ==> not ste it
## Enable SRAM LDO ret mode.
## Put the PLLs in bypass mode.

## ==================================

## Relock the PLLs.
## Disable SRAM LDO ret mode.
## IO to work in DDR mode.
## Restore the pull for DQ, DM.
## Enable VTP.
## Enable EMIF.
## Disable EMIF self-refresh.

## ==================================

## Take the PLLs out of LP_BYPASS.
## Disable SRAM LDO ret mode.
## Restore the pull for DQ, DM
## Take out IO of mDDR mode
## Enable VTP
## Restore DDR Setting .

## Enable EMIF
## Disable EMIF self-refresh




.......


}



2012年5月23日 星期三

tlv320aic3110 codec driver 完成~~

花費了好久時間 , 終於完成 TLV320AIC3110 Codec Driver 了 !! 
整個結構很亂 , 不過按照下列的 Function 動作 , 把相對應的 Code  填寫完畢 , 
差不多就完成了 , 剩下的 DAPM 部分 , 我還不是很了解 , 我只將會用到的 Register 
用 amixer 的方式完成 , 其他進階的 Wadge 部份 , 我都先不去實作 , 等有需要再來
修補 !! 

 

 
soc_pcm_open()
{
    cpu_dai->driver->ops->startup()     => dabinci_mcaps_startup();
    platform->driver->ops->open()       => davinci_pcm_open();
    codec_dai->driver->ops->startup()   => aic3110_startup();
    rtd->dai_link->ops->startup()       =>
}

soc_pcm_hw_params()
{
    rtd->dai_link->ops->hw_params()     => evm_aic3110_hw_params()
                                            snd_soc_dai_set_fmt(codec_dai)          => aic3110_set_dai_fmt()
                                            snd_soc_dai_set_fmt(cpu_dai)            => davinci_mcasp_set_dai_fmt()
                                            snd_soc_dai_set_sysclk(codec_sysclk)    => aic3110_set_dai_sysclk()
                                       ???  snd_soc_dai_set_sysclk(cpu_sysclk)      =>

    codec_dai->driver->ops->hw_params() => aic3110_hw_params()
    cpu_dai->driver->ops->hw_params()   => davinci_mcasp_hw_params()
    platform->driver->ops->hw_params()  =>
}

soc_pcm_trigger()
{

    codec_dai->driver->ops->trigger()   => None.
    platform->driver->ops->trigger()    => davinci_pcm_trigger()
    cpu_dai->driver->ops->trigger()     => davinci_mcasp_trigger()
}


soc_pcm_prepare()
{
    rtd->dai_link->ops->prepare()       =>
    platform->driver->ops->prepare()    => davinci_pcm_prepare();
    codec_dai->driver->ops->prepare()   => None
    cpu_dai->driver->ops->prepare()     => None

}

soc_pcm_pointer()
{
    platform->driver->ops->pointer()    =>

    cpu_dai->driver->ops->delay()       =>
    codec_dai->driver->ops->delay       =>
    platform->driver->delay             =>
}

2012年4月26日 星期四

mDDR2 Problem .... Solved

之前,我們的mDDR 200 一直有問題沒有辦法Initial ,今天終於在FAE幫忙下解決了!!
有兩個Register 設定值不對,這兩個值在所有文件上還找不到說明,真是.......

2012年4月3日 星期二

tlv320aic3110 codec driver potting!!

解決USB問題後,下—個困難的是CODEC...
目前先找找看有沒有現有的driver 然後用修改的方式比較容易:-)

2012年4月2日 星期一

g_mass_storage driver for windows 7 !!

上星期 ㄊㄨ、 了一個星期,還是沒有辦法法解決 win 7 下 Luns=2 卻只連接ㄧ個Device 的問題!
今天總算被我解開了!!
先解釋ㄧ下 bug 狀況, 使用 BSP Build 出來的g_mass_storage.ko 和 modprobe g_mass_storage luns=2 remoable=1 stall=0 file=/dev/mmc/mmcblk1p1,/dev/mmc/mmcblk0p1
指令來掛載 kernel module !
結果,在Fedora Host 兩個都可以正確Access ! 但是,如果換成Windows 7 的時候,嘿嘿,就是只能看到 第一個Device (mmcblk1p1 ) !!
首先可以到 Win7 的 Mass Driver 中去觀看Driver的訊息 , 例如下圖 可以看到 USB/PID/VID & VER , 和 sub class 狀態 !! 確定 Device descriptor 都沒有傳輸錯誤 !!

重點是下列的圖 ,可發現 "裝置例項路徑" , "匯流排關係" , "上層" & "下層" 中最後一段都是亂碼 (紅色圈起部分 ) !!

發現 , Win 7 對於這樣亂碼的內容好像沒有辦法支援Luns=2 的狀況 , 所以更改 modprobe 指令為
modprobe g_mass_storage iSerialNumber=12345 luns=2 remoable=1 stall=0 file=/dev/mmc/mmcblk1p1,/dev/mmc/mmcblk0p1
得到結果如下 :

上圖 , 可以發現 VID/PID , Class type 都沒有更動 !!

上圖可以發現 , "裝置例項路徑" , "匯流排關係" & "下層" 資料中 , 都用標準的字元 "12345" 來取代 , 並且在最後用 "&1" or "&0" 來標示 device 0 & 1 !!
所以.....給Win 7 的序號需要標準的字元 , 不要含代一些其他字元 , 這樣就可以解決這個 bug 了 !!
這個 bog bug花費我... 一星期多的時間 , 一直懷疑 SCSI Command 傳輸有問題 , 在要放棄修正這個 bug 前...想到 , 會不會是 win 7 沒有正確收到 Luns=2 這個 參數 , 才找到原來是不起眼的Serial Number格式問題 !!




2012年3月24日 星期六

Project Starting .......

農曆過完年後開使進行AM335X  CPU 的 Porting !
之前 Samsung Porting 過程都沒有記錄,所以這次我想記錄在Bolgger中 。