嵌入式系統(tǒng)磚家_寄存器讀寫抽象之regmap

目錄:

一、regmap子系統(tǒng)的引入

二、regmap子系統(tǒng)的內部實現


一、regmap子系統(tǒng)的引入

沒有regmap子系統(tǒng)之前

在內核代碼里,有成千上萬的以I2C / SPI為通訊接口的設備驅動。

以I2C設備為例

各種I2C接口的設備驅動都需要通過I2C子系統(tǒng)的API(i2c_transfer())來進行讀寫寄存器的操作。在對應的設備驅動中,I2C讀寫寄存器的操作通常會被封裝成2個靜態(tài)函數。

xxx_i2c_read_reg()/xxx_i2c_write_reg():

在沒有regmap抽象層之前,內核里充斥著大量類似的代碼,這些代碼都是多余的,使用I2C總線來讀寫寄存器的操作是有共性的,應該被抽象出來,形成一份統(tǒng)一的代碼。驅動開發(fā)人員應使用該抽象層的API來讀寫I2C設備的寄存器,然后把更多的精力放在驅動的邏輯設計上。

同樣的,沒有regmap子系統(tǒng)時,spi設備的寄存器讀寫操作也是散落在各個spi設備驅動。

i2c/spi drvier、i2c/spi device、i2c/spi subsystem之間的關系如下:

有了regmap子系統(tǒng)后

1. 同樣以I2C設備為例,先定義struct regmap_config:

struct regmap_config里包含了讀寫芯片寄存器所需所有信息,例如寄存器數據位寬、地址位寬等。

2. 將struct regmap_config注冊給regmap子系統(tǒng):

得到一個struct regmap對象,有了這個對象,就可以調用regmap子系統(tǒng)提供的用于讀寫寄存器的API了。

3. 使用regmap API讀寫寄存器:

int regmap_read(struct regmap *map, unsigned int reg,

? ? ? ? ? ? ? ? unsigned int *val);

int regmap_write(struct regmap *map, unsigned int reg,

? ? ? ? ? ? ? ? unsigned int val);

int regmap_update_bits(struct regmap *map, unsigned int reg,

? ? ? ? ? ? ? ? unsigned int mask, unsigned int val);

讀寫寄存器的操作已經抽象到regmap子系統(tǒng)里了,完整的API位于include/linux/regmap.h。

有了regmap后,i2c/spi drvier、i2c/spi device、i2c/spi subsystem之間的關系如下:

我在Linux-4.14內核里搜索了一下,目前大約有491個地方使用了regmap子系統(tǒng),將來可能會更多。


二、regmap子系統(tǒng)的內部實現

regmap的拓撲結構

在Linux-4.14內核中,regmap分為3層:

源碼文件:

regmap的緩存功能

在regmap子系統(tǒng)里,可以選擇是否使用緩存功能:

regmap內支持3 種緩存類型:數組(flat)、LZO 壓縮和紅黑樹(rbtree)

1) 數組是最簡單的緩存類型,當設備寄存器很少時,可以用這種類型來緩存寄存器值。

2) LZO(Lempel–Ziv–Oberhumer) 是 Linux 中經常用到的一種壓縮算法,Linux 編譯后就會用這個算法來壓縮。這個算法有 3 個特性:壓縮快,解壓不需要額外內存,壓縮比可以自動調節(jié)。在這里,你可以理解為一個數組緩存,套了一層壓縮,來節(jié)約內存。當設備寄存器數量中等時,可以考慮這種緩存類型。

3) 紅黑樹特性就是索引快,所以當設備寄存器數量比較大,或者對寄存器操作延時要求低時,就可以用這種緩存類型。

相關結構體:

struct regmap_config

struct regmap_config里包含了讀寫芯片寄存器所需的所有信息,它是regmap API里最核心的結構體。使用regmap子系統(tǒng)的第一步就是填充該結構體。這個結構體看著龐大,但是大多數情況下只要初始化幾個成員變量就足夠了,最簡單和常見的情況類似這樣:

struct regmap_config重要成員變量的含義如下:

struct regmap_bus

該結構體用于描述一種硬件總線的寄存器讀寫操作(a hardware bus for the register map infrastructure)。

regmap_bus并不是面向用戶的API,也就是說使用regmap子系統(tǒng)并不要求一定要了解該結構體,但是理解該結構體有助于我們了解regmap是如何抽象寄存器讀寫操作的。無論是i2c還是spi,在調用devm_regmap_init_[i2c|spi|...]將struct regmap_config注冊給regmap子系統(tǒng)后,在子系統(tǒng)內部都會根據remap_config里的配置信息找到對應的struct regmap_bus。

比如:

static struct regmap_bus regmap_i2c

[...]

static struct regmap_bus regmap_i2c_smbus_i2c_block

static struct regmap_bus regmap_spi

static struct regmap_bus regmap_spmi_base

[...]

有了適配芯片的 regmap_config 和 regmap_bus,regmap子系統(tǒng)就有了讀寫該芯片寄存器的能力,然后就會返回一個struct regmap供設備驅動來使用了,struct regmap類似一個大管家,包含了所有信息,負責統(tǒng)籌一切。

struct regmap_bus重要成員變量的含義如下:

定義一個regmap_bus結構體時不需要初始化其所有成員變量,例如某些 i2c 芯片的寄存器是16bit(2Byte)的,其使用的regmap_bus如下:

常用的regmap API

regmap提供出來的讀寫寄存器的API非常多,最常用的3個API如下:

可以猜測上述API會利用struct regmap_config + struct regmap_bus完成寄存器的讀寫操作。

簡單看下regmap_read()的實現:

regmap_read

? ? struct regmap *map

? ? map->reg_read(context, reg, val);

? ? ? ? _regmap_bus_reg_read

? ? ? ? ? ? map->bus->reg_read


總結

regmap 是在 Linux 3.1 加入進來的特性,其最初的目的是減少i2c/spi等設備驅動里的重復邏輯,提供一種通用的接口來操作芯片內寄存器,隨著版本的更迭,regmap 支持的bus越來越多,并且除了能做到統(tǒng)一的 寄存器I/O 接口,還可以在驅動和硬件 IC 之間做一層緩存,從而能減少底層 I/O 的操作次數。給我的感覺是:內核越來越強大了,但是學習的難度也越來越大,前路漫漫啊,求老司機帶路...

你和我各有一個蘋果,如果我們交換蘋果的話,我們還是只有一個蘋果。但當你和我各有一個想法,我們交換想法的話,我們就都有兩個想法了。如果你也對嵌入式系統(tǒng)開發(fā)有興趣,并且想和更多人互相交流學習的話,請關注我的公眾號:ESexpert,一起來學習吧,歡迎各種收藏/轉發(fā)/批評。

?

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容