最近發現一個詭異問題,就是進入OPP Turbo (720MHz) 就會當機.
如果使用 OPP 120 (600MHz) 就沒有問題 .
找了好久還是不知道是發生啥事情. 最後想說是不是進入OPP Turbo 就會忽然耗電量過大或是其他沒注意到的地方.
就更改一下 Code , OPP Turbo mode 時候不要給足頻率 719MHz / 1.26V .讓CPU 跑接近OPP Turbo 卻不是Turbo 所需要的頻率/電壓 , 看看會如何 !!
結果是 Pass , Why ??????
後來和 FAE 討論結果 , 依照他們之前的經驗 , 建議更換一下 PLL 的 M /N 值 !!
應為可能那組 M/N 值剛好會造成 PLL 被干擾部穩定 !!
好吧 , 修正一下 Code , 讓 N 由 24 開始 , 24 OSC 先除成為 1 MHz 在開始倍頻!!
這樣就OK 了 , 跑了一個晚上都沒問題 !!
Code Patch 如下:
diff -Nura linux-3.2.0-psp04.06.00.08.sdk-orignal/arch/arm/mach-omap2/clock33xx_data.c linux-3.2.0-psp04.06.00.08.sdk/arch/arm/mach-omap2/clock33xx_data.c
--- linux-3.2.0-psp04.06.00.08.sdk-orignal/arch/arm/mach-omap2/clock33xx_data.c 2013-05-15 12:18:26.848742174 +0800
+++ linux-3.2.0-psp04.06.00.08.sdk/arch/arm/mach-omap2/clock33xx_data.c 2013-05-15 16:57:16.922444075 +0800
@@ -280,7 +280,7 @@
.idlest_mask = AM33XX_ST_DPLL_CLK_MASK,
.max_multiplier = AM33XX_MAX_DPLL_MULT,
.max_divider = AM33XX_MAX_DPLL_DIV,
- .min_divider = 1,
+ .min_divider = 24,
};
AM335X porting.
2013年5月16日 星期四
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
猜想說是否 , 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 還真難找....!!
初步 判別 , 應該卡在 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);
打完收工.......
好吧 , 加上 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 上市...哇咧......... !!
卡機 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 聲去表現一些工程模式的動作 ) !!
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 聲去表現一些工程模式的動作 ) !!
訂閱:
文章 (Atom)