OMAP335X-內核BSP之hwmod

MPU平臺:OAMP3352

內核版本:3.2.0

     聲明:我講解的範疇是從內核解壓以後經過彙編代碼執行最後跳到第一個C代碼這個點開始講解,一直講到文件系統被正確的掛載起來,用戶可以正常登入!至於之前的解壓縮內核、彙編啓動代碼我會以後另開文章講解。

     目標:本文想闡述清楚OAMP335X這個平臺的BSP部分的hwmod。

start_kernel  -->rest_init() -->kernel_init()  --> do_basic_setup()  -->do_initcalls()

TI的芯片很多,面對的應用領域也很廣,而且更新換代的速度也相對要快點。這樣的話就帶來一個問題,芯片之間的代碼可重用性就顯得很重要,若果你一直有開發三星MPU 的話,你可能就深有感觸(S3C6410的代碼裏面的一些數據結構竟然會在S3C2410的頭文件裏面,這樣的情形在TI裏面也是屢見不鮮)。所以今天說的這個hwmod的這個東西就是基於這種環境下的一個產物。

TI在omap_hwmod.c文件裏面有對這個hwmod的介紹,我翻譯了一下如下所示:

快速的來查看OMAP SoC的一個全局概貌的方法是,可以把SOC看成是通過互相連接在一起的一些IP模塊的大集合。而這個IP模塊包括如ARM處理器的設備、音頻串行接口、UART等等。這些設備中的一些,比如像DSP,是由TI自己創建,其他的一些設備是由其他大廠家的製造商來提供的(如SGX)。在TI的架構中,TI把芯片上的片內設備稱爲“OMAP 模塊”。這些IP模塊是跨越幾個相同OMAP版本,無需頻繁修訂。 

這些OMAP模塊是由不同的互連器連接在一起。大多數模塊之間的地址和數據流是通過基於OCP(一種片內模塊資源通信協議)的互連(如L3和L4總線),

OMAP hwmod提供一種統一的方式來描述芯片硬件模塊並將其融入到芯片的其餘部分。此描述可以自動從TI生成硬件數據庫(mach-omap2/omap_hwmod_33xx_data.c這個文件有詳細的解釋)。 OMAP hwmod提供了一個標準,一致的API如復位、使能、睡眠以及禁止這些硬件模塊功能。Hwmod也提供了一種方法用於其他核心代碼,如Linux設備代碼或OMAP電源管理或地址空間映射代碼或查詢硬件數據庫。

說說怎麼使用這個 hwmod:

驅動不應該直接調用硬件模塊功能函數(我也這麼認爲的,但是太多的封裝就受不了,你看android做了TMD多少封裝啊,難怪用過IOS以後用google的就 你懂的),這個應該由omap_device的代碼來幹這個事情,或是在一些極其特殊的情況下由板級代碼來執行。omap_device的代碼功能會使用omap_hwmod來建立一個platform_device結構體,而這種調用方式特體現了hwmode 數據和linux設備驅動模型之間的通信方式。很多的驅動只可以調用pm_runtime*()來調用omap_hwmod功能函數。

下面看看OMAP hwmod 的代碼在整個內核分層結構中所處的一個位置是是怎麼樣的:

*            +-------------------------------+

 *            |      Device driver code     |

 *            |      (e.g., drivers/)         |

 *            +-------------------------------+

 *            |      Linux driver model     |

 *            |     (platform_device /       |

 *            |  platform_driver data/code)   |

 *            +-------------------------------+

 *            | OMAP core-driver integration  |

 *            |(arch/arm/mach-omap2/devices.c)|

 *            +-------------------------------+

 *            |      omap_device code         |

 *            | (../plat-omap/omap_device.c)     |

 *            +-------------------------------+

 *   ---->     |    omap_hwmod code/data       |    <-----

 *            | (../mach-omap2/omap_hwmod*)   |

 *            +-------------------------------+

 *            | OMAP clock/PRCM/register fns   |

 *            | (__raw_{read,write}l, clk*)        |

 *            +-------------------------------+

 *

這個層次我認爲應該是從下往上的一個邏輯順序!應該是這樣的  ........

設備的驅動不應該直接包含OMAP特殊平臺的的代碼或數據,而只應該包含去如何操作這個IP模塊的代碼,這也是設備驅動的職責所在。這樣的話IP模塊也可以在其他平臺上使用(比如達芬奇),做到一個平臺無關性。

OMAP的hwmod代碼在設備啓動過程中也可以進行復位或睡眠這個設備,這樣讓設備可以處於一個正確的運行狀態。

OMAP硬件模塊的狀態:

剛一開始時候硬件模塊是未知的狀態,一旦被註冊的話,就處於被註冊狀態,若果該模塊的時鐘被得到分配,就進入時鐘已初始化狀態,最後模塊設置完成了就出一個已經初始化狀態。

Hwmod代碼也包括:處理I/O映射代碼功能

                   總線的吞吐率和模塊延時的測試代碼

上面零零碎碎的囉嗦了一下這個hwmod的是個什麼東西,當時 “最好的文檔還是代碼本身” 這是我一直奉行的觀念!

第一步:L3和L4

AM3352把片內的資源之間的相互連接用一種2層分級的方式來互聯,這兩個分級成爲L3和L4;

L3:是一個基於Network-On-Chip(NoC)協議的高性能的互聯器,Network-On-Chip(NoC)使用一種內部基於包一樣的協議方式來發送和接受數據命令在每個模塊之間;當時呢這個基於NOC的互聯器的外部接口(發送或接受)都是遵循OCPIP2.2協議的,至於這個OCPIP2.2什麼個什麼協議的話,請君自己去查查吧。下面是一個L3的拓補圖結構(根據時鐘快慢分成L3F和L3S兩個區域)

其實我們可以看出時鐘快的是對應處理數據量大的部分。注意有些模塊既可以是主動發送者,也可以是接受者。每一個模塊的發生器和接收器都唯一的ID號;至於更詳細的的L3互聯器介紹去看芯片文檔。

L4:是一種非阻塞的外設互聯器,該互聯器用於訪問一些低帶寬和一些分散的物理模塊,其總線的架構和內存映射外設如下圖所示:

仔細看會發現這個L4是嵌入到L3S裏面的。其具體實現可以參考芯片手冊。

好了我們說了一下這個L3和L4的互聯器,但是這個和我文本的目標hwmod有什麼關係呢?我也覺得沒什麼關係,非要說有點的話我認爲是搞出這個hwmod的理由之一吧。也就是說hwmod爲了符合這個郵件模塊而搞出來的吧!

第二步:struct omap_hwmod

這個結構體TI的解釋是:

integration data for OMAP hardware "modules" (IP blocks)

struct omap_hwmod {

const char       *name; //硬件模塊的名稱

struct omap_hwmod_class *class;  //硬件模塊屬於哪個類(硬件都有類了)

struct omap_device       *od;   //硬件模塊對應的具體設備

struct omap_hwmod_mux_info *mux;  //硬件模塊的管教複用信息

struct omap_hwmod_irq_info *mpu_irqs; //硬件模塊的中斷信息

struct omap_hwmod_dma_info *sdma_reqs;//硬件模塊的DMA信息

struct omap_hwmod_rst_info *rst_lines;  //硬件模塊的設置信息

union {

struct omap_hwmod_omap2_prcm omap2;//硬件模塊特殊的PRCM數據

struct omap_hwmod_omap4_prcm omap4;//硬件模塊特殊的PRCM數據

} prcm;

const char *main_clk;       //OMAP時鐘的名字

struct clk *_clk;          //主時鐘

struct omap_hwmod_opt_clk *opt_clks; //其他設備的時鐘

char *clkdm_name;  //

struct clockdomain *clkdm;

char *vdd_name;//電壓的名稱

struct omap_hwmod_ocp_if **masters; /* connect to *_IA */ //IA是指發生器的代理(就是說模塊和互聯器之前不是直接相連的而是通過這個代理器來收發數據和命令,嗨TI搞的好複雜可見BSP和硬件的耦合性是多麼的緊密!)

struct omap_hwmod_ocp_if **slaves;  /* connect to *_TA *///TA是目標的代理

void *dev_attr;

u32 _sysc_cache;

void __iomem *_mpu_rt_va;  //模塊cache寄存器的起始地址

spinlock_t _lock;

struct list_head node;

u16 flags;

u8 _mpu_port_index;

u8 response_lat;

u8 rst_lines_cnt;

u8 opt_clks_cnt;

u8 masters_cnt;

u8 slaves_cnt;

u8 hwmods_cnt;

u8 _int_flags;

u8 _state;   //模塊的狀態(未知、已註冊、已初始化)

u8 _postsetup_state;

}

第三步: omap_hwmod_33xx_data.c

該文件裏面定義了所有的片內資源(以hwmod形式);

int __init am33xx_hwmod_init(void)

{

return omap_hwmod_register(am33xx_hwmods); //註冊了所有的片內模塊資源並以鏈表形式鏈接在一起!omap_hwmod_list

}

第四步:omap_hwmod.c

這個文件使用於 OMAP2/3/4 平臺。

static int __init omap_hwmod_setup_all(void)

{

int r;

      if (!mpu_oh) {

pr_err("omap_hwmod: %s: MPU initiator hwmod %s not yet registered\n",

       __func__, MPU_INITIATOR_NAME);

return -EINVAL;

}

//爲每一個模塊找到自己的寄存器虛擬首地址

r = omap_hwmod_for_each(_populate_mpu_rt_base, NULL);

//爲每一個模塊初始化時鐘

r = omap_hwmod_for_each(_init_clocks, NULL);

WARN(IS_ERR_VALUE(r),

     "omap_hwmod: %s: _init_clocks failed\n", __func__);

//爲每一個模塊進行配置操作

omap_hwmod_for_each(_setup, NULL);

return 0;

}

core_initcall(omap_hwmod_setup_all);

同時這個文件還提供了:

/*對一個模塊進行分配相應的資源(中斷、寄存器地址、內存空間、DMA資源)*/

int omap_hwmod_fill_resources(struct omap_hwmod *oh, struct resource *res)

/*得到一個目標模塊寄存器的虛擬首地址*/

void __iomem *omap_hwmod_get_mpu_rt_va(struct omap_hwmod *oh)

/*讀或寫一個值到某個模塊的一個寄存器裏面*/

u32 omap_hwmod_read(struct omap_hwmod *oh, u16 reg_offs)

void omap_hwmod_write(u32 v, struct omap_hwmod *oh, u16 reg_offs)

/*通過名字找到一個對應的模塊*/

struct omap_hwmod *omap_hwmod_lookup(const char *name)

/*對一個模塊進行復位*/

/*對一個模塊進行使能*/

/*對一個模塊進行睡眠*/

/*對一個模塊進行停止*/

/*對一個模塊進行使能和禁止時鐘*/

/*對一個模塊:應許這個模塊喚醒張整個系統*/

還有很多的其他函數用於操作這個硬件模塊,君可以自己去查看;

總結:前前後後的講了這麼多的hwmod;

      至少明確了:爲什麼TI要高一套hwmod的軟件機制(硬件使用了這套L3和L4的硬件模塊互聯機制);

      至少明確了:hwmod在BSP啓動中充當一個什麼的角色(一個hwmod封裝了所有的當前屬於這個硬件模塊的所有資源,以及對硬件模塊的操作功能函數,注意這個是和平臺有關性的,所以omap_device.c(基於linux驅動模型的方式)對這些功能函數又進行一次平臺無關性的封裝,這個下一節講。)

     明確了:真正的BSP代碼是和硬件的耦合性很大的,這一部分的代碼纔是水平的體現,你不僅要對整個硬件體系架構很清楚而且還要對每個硬件資源的性能有使用經驗,而且還要把這些東西抽象出一套符合平臺型的一個軟件架構。最後這個架構還要很好的整合到linux那套底層BSP架構中。而且還要考慮到內核的一系列問題(性能、資源利用、調度、通信協議等等)。這些基本上都是芯片廠商的事情,等我們拿到一塊TI的評估板 這一切的一切 都已經做的很好了,連頂層的應用程序都在一個SDK的包裏面提供給用戶,所以我們工作中很多的是軟件方面事情(至少中國國內的整個大環境是這樣的)。

原文鏈接:https://blog.csdn.net/zhou13454069844/article/details/16963853
原文鏈接:https://blog.csdn.net/zhou13454069844/article/details/16963731

發佈了60 篇原創文章 · 獲贊 20 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章