目前我們換了 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年12月28日 星期五
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 聲去表現一些工程模式的動作 ) !!
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
過程很多細節的問題 , 都克服了 , 這篇文章紀錄一些目前我覺得重點和詭異的地方.
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 才是大問題 , 一切靠自己了 @@
我感覺是派幾個工程師 為我們需求寫的 , 並且確定在 下一版 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/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 .....
很不幸的 , 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
.......
}
聽說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 =>
}
整個結構很亂 , 不過按照下列的 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日 星期二
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格式問題 !!
先解釋ㄧ下 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 了 !!
這個
2012年3月24日 星期六
Project Starting .......
農曆過完年後開使進行AM335X CPU 的 Porting !
之前 Samsung Porting 過程都沒有記錄,所以這次我想記錄在Bolgger中 。
訂閱:
文章 (Atom)