NVMEM 子系統¶
Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
本文件解釋了 NVMEM 框架以及提供的 API,以及如何使用它。
1. 簡介¶
NVMEM 是非易失性儲存器層的縮寫。 它用於從非易失性儲存器(如 eeprom、efuse 等)檢索 SOC 或裝置特定資料的配置。
在這個框架存在之前,像 eeprom 這樣的 NVMEM 驅動程式儲存在 drivers/misc 中,它們都必須複製幾乎相同的程式碼來註冊 sysfs 檔案,允許核心使用者訪問他們正在驅動的裝置的內容等等。
就其他核心使用者而言,這也是一個問題,因為使用的解決方案几乎因驅動程式而異,存在相當大的抽象洩漏。
該框架旨在解決這些問題。 它還為消費裝置引入了 DT 表示,以便從 NVMEM 獲取他們需要的資料(MAC 地址、SoC/修訂 ID、部件號等)。
NVMEM 提供者¶
NVMEM 提供者是指實現初始化、讀取和寫入非易失性儲存器的方法的實體。
2. 註冊/登出 NVMEM 提供者¶
NVMEM 提供者可以透過向 nvmem_register() 提供相關的 nvmem 配置來向 NVMEM 核心註冊,成功後核心將返回一個有效的 nvmem_device 指標。
nvmem_unregister() 用於登出先前註冊的提供者。
例如,一個簡單的 nvram 案例
static int brcm_nvram_probe(struct platform_device *pdev)
{
struct nvmem_config config = {
.name = "brcm-nvram",
.reg_read = brcm_nvram_read,
};
...
config.dev = &pdev->dev;
config.priv = priv;
config.size = resource_size(res);
devm_nvmem_register(&config);
}
裝置驅動程式可以使用 nvmem_cell_info 結構定義和註冊 nvmem 單元
static const struct nvmem_cell_info foo_nvmem_cell = {
{
.name = "macaddr",
.offset = 0x7f00,
.bytes = ETH_ALEN,
}
};
int nvmem_add_one_cell(nvmem, &foo_nvmem_cell);
此外,還可以建立 nvmem 單元查詢條目,並像下面的示例中那樣從機器程式碼向 nvmem 框架註冊它們
static struct nvmem_cell_lookup foo_nvmem_lookup = {
.nvmem_name = "i2c-eeprom",
.cell_name = "macaddr",
.dev_id = "foo_mac.0",
.con_id = "mac-address",
};
nvmem_add_cell_lookups(&foo_nvmem_lookup, 1);
NVMEM 消費者¶
NVMEM 消費者是利用 NVMEM 提供者來讀取和寫入 NVMEM 的實體。
3. 基於 NVMEM 單元的消費者 API¶
NVMEM 單元是 NVMEM 中的資料條目/欄位。 NVMEM 框架提供 3 個 API 來讀取/寫入 NVMEM 單元
struct nvmem_cell *nvmem_cell_get(struct device *dev, const char *name);
struct nvmem_cell *devm_nvmem_cell_get(struct device *dev, const char *name);
void nvmem_cell_put(struct nvmem_cell *cell);
void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell);
void *nvmem_cell_read(struct nvmem_cell *cell, ssize_t *len);
int nvmem_cell_write(struct nvmem_cell *cell, void *buf, ssize_t len);
*nvmem_cell_get() apis 將獲取給定 id 的 nvmem 單元的引用,然後 nvmem_cell_read/write() 可以讀取或寫入該單元。 一旦完成單元的使用,消費者應呼叫 *nvmem_cell_put() 以釋放單元的所有分配記憶體。
4. 基於直接 NVMEM 裝置的消費者 API¶
在某些情況下,需要直接讀取/寫入 NVMEM。 為了方便這些消費者,NVMEM 框架提供以下 api
struct nvmem_device *nvmem_device_get(struct device *dev, const char *name);
struct nvmem_device *devm_nvmem_device_get(struct device *dev,
const char *name);
struct nvmem_device *nvmem_device_find(void *data,
int (*match)(struct device *dev, const void *data));
void nvmem_device_put(struct nvmem_device *nvmem);
int nvmem_device_read(struct nvmem_device *nvmem, unsigned int offset,
size_t bytes, void *buf);
int nvmem_device_write(struct nvmem_device *nvmem, unsigned int offset,
size_t bytes, void *buf);
int nvmem_device_cell_read(struct nvmem_device *nvmem,
struct nvmem_cell_info *info, void *buf);
int nvmem_device_cell_write(struct nvmem_device *nvmem,
struct nvmem_cell_info *info, void *buf);
在消費者可以直接讀取/寫入 NVMEM 之前,它應該從 *nvmem_device_get() api 之一獲取 nvmem_controller。
這些 api 和基於單元的 api 之間的區別在於,這些 api 始終將 nvmem_device 作為引數。
5. 釋放對 NVMEM 的引用¶
當消費者不再需要 NVMEM 時,它必須釋放它使用上面提到的 API 獲得的對 NVMEM 的引用。 NVMEM 框架提供 2 個 API 來釋放對 NVMEM 的引用
void nvmem_cell_put(struct nvmem_cell *cell);
void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell);
void nvmem_device_put(struct nvmem_device *nvmem);
void devm_nvmem_device_put(struct device *dev, struct nvmem_device *nvmem);
這兩個 API 都用於釋放對 NVMEM 的引用,並且 devm_nvmem_cell_put 和 devm_nvmem_device_put 會銷燬與此 NVMEM 關聯的 devres。
使用者空間¶
6. 使用者空間二進位制介面¶
使用者空間可以讀取/寫入位於以下位置的原始 NVMEM 檔案
/sys/bus/nvmem/devices/*/nvmem
示例
hexdump /sys/bus/nvmem/devices/qfprom0/nvmem
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
00000a0 db10 2240 0000 e000 0c00 0c00 0000 0c00
0000000 0000 0000 0000 0000 0000 0000 0000 0000
...
*
0001000
7. 裝置樹繫結¶
請參閱 Documentation/devicetree/bindings/nvmem/nvmem.txt
8. NVMEM 佈局¶
NVMEM 佈局是另一種建立單元的機制。 透過裝置樹繫結,可以使用偏移量和長度來指定簡單的單元。 有時,單元沒有靜態偏移量,但內容仍然定義明確,例如 tag-length-values。 在這種情況下,必須首先解析 NVMEM 裝置內容,然後相應地新增單元。 佈局允許您讀取 NVMEM 裝置的內容並允許您動態新增單元。
佈局的另一個用例是單元的後處理。 透過佈局,可以將自定義後處理鉤子與單元相關聯。 甚至可以將此鉤子新增到不是由佈局本身建立的單元。
9. 內部核心 API¶
-
int nvmem_add_one_cell(struct nvmem_device *nvmem, const struct nvmem_cell_info *info)¶
將一個單元資訊新增到 nvmem 裝置
引數
struct nvmem_device *nvmem要新增單元的 nvmem 裝置。
const struct nvmem_cell_info *info要新增到裝置的 nvmem 單元資訊
返回值
0 或失敗時的負錯誤程式碼。
-
int nvmem_register_notifier(struct notifier_block *nb)¶
為 nvmem 事件註冊一個通知程式塊。
引數
struct notifier_block *nbnvmem 事件發生時要呼叫的通知程式塊。
返回值
成功時為 0,失敗時為負錯誤號。
-
int nvmem_unregister_notifier(struct notifier_block *nb)¶
為 nvmem 事件登出一個通知程式塊。
引數
struct notifier_block *nb要登出的通知程式塊。
返回值
成功時為 0,失敗時為負錯誤號。
-
struct nvmem_device *nvmem_register(const struct nvmem_config *config)¶
為給定的 nvmem_config 註冊一個 nvmem 裝置。 還在 /sys/bus/nvmem/devices/dev-name/nvmem 中建立一個二進位制條目
引數
const struct nvmem_config *config建立 nvmem 裝置時使用的 nvmem 裝置配置。
返回值
如果出錯,將為 ERR_PTR(),成功時將為指向 nvmem_device 的有效指標。
-
void nvmem_unregister(struct nvmem_device *nvmem)¶
登出先前註冊的 nvmem 裝置
引數
struct nvmem_device *nvmem指向先前註冊的 nvmem 裝置的指標。
-
struct nvmem_device *devm_nvmem_register(struct device *dev, const struct nvmem_config *config)¶
為給定的 nvmem_config 註冊一個託管的 nvmem 裝置。 還在 /sys/bus/nvmem/devices/dev-name/nvmem 中建立一個二進位制條目
引數
struct device *dev使用 nvmem 裝置的裝置。
const struct nvmem_config *config建立 nvmem 裝置時使用的 nvmem 裝置配置。
返回值
如果出錯,將為 ERR_PTR(),成功時將為指向 nvmem_device 的有效指標。
-
struct nvmem_device *of_nvmem_device_get(struct device_node *np, const char *id)¶
從給定的 id 獲取 nvmem 裝置
引數
struct device_node *np使用 nvmem 裝置的裝置樹節點。
const char *id來自 nvmem-names 屬性的 nvmem 名稱。
返回值
如果出錯,將為 ERR_PTR(),成功時將為指向 struct nvmem_device 的有效指標。
-
struct nvmem_device *nvmem_device_get(struct device *dev, const char *dev_name)¶
從給定的 id 獲取 nvmem 裝置
引數
struct device *dev使用 nvmem 裝置的裝置。
const char *dev_name請求的 nvmem 裝置的名稱。
返回值
如果出錯,將為 ERR_PTR(),成功時將為指向 struct nvmem_device 的有效指標。
-
struct nvmem_device *nvmem_device_find(void *data, int (*match)(struct device *dev, const void *data))¶
查詢具有匹配函式的 nvmem 裝置
引數
void *data要傳遞給匹配函式的資料
int (*match)(struct device *dev, const void *data)用於檢查裝置的回撥函式
返回值
如果出錯,將為 ERR_PTR(),成功時將為指向 struct nvmem_device 的有效指標。
引數
struct device *dev使用 nvmem 裝置的裝置。
struct nvmem_device *nvmem指向由
devm_nvmem_cell_get()分配的 nvmem 裝置的指標,需要釋放它。
-
void nvmem_device_put(struct nvmem_device *nvmem)¶
放置已獲取的 nvmem 裝置
引數
struct nvmem_device *nvmem指向需要釋放的 nvmem 裝置的指標。
-
struct nvmem_device *devm_nvmem_device_get(struct device *dev, const char *id)¶
根據給定的 ID 從裝置獲取 nvmem 裝置。
引數
struct device *dev請求 nvmem 裝置的裝置。
const char *id請求的 nvmem 裝置的名稱 ID。
返回值
出錯時返回 ERR_PTR(),成功時返回指向 struct nvmem_device 的有效指標。 nvmem_device 將在裝置釋放後自動釋放。
-
struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id)¶
從給定的裝置節點和單元 ID 獲取 nvmem 單元。
引數
struct device_node *np使用 nvmem 單元的裝置樹節點。
const char *id來自 nvmem-cell-names 屬性的 nvmem 單元名稱,如果為空,則表示索引 0 處的單元(沒有附屬 nvmem-cell-names 屬性的單個單元)。
返回值
出錯時返回 ERR_PTR(),成功時返回指向 struct nvmem_cell 的有效指標。 nvmem_cell 將由 nvmem_cell_put() 釋放。
引數
struct device *dev請求 nvmem 單元的裝置。
const char *id要獲取的 nvmem 單元名稱(這與 DT 系統的 nvmem-cell-names 屬性中的名稱相對應,與非 DT 系統的查詢條目的 con_id 相對應)。
返回值
出錯時返回 ERR_PTR(),成功時返回指向 struct nvmem_cell 的有效指標。 nvmem_cell 將由 nvmem_cell_put() 釋放。
-
struct nvmem_cell *devm_nvmem_cell_get(struct device *dev, const char *id)¶
根據給定的 ID 從裝置獲取 nvmem 單元。
引數
struct device *dev請求 nvmem 單元的裝置。
const char *id要獲取的 nvmem 單元名稱 ID。
返回值
出錯時返回 ERR_PTR(),成功時返回指向 struct nvmem_cell 的有效指標。 nvmem_cell 將在裝置釋放後自動釋放。
-
void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell)¶
釋放先前從 devm_nvmem_cell_get 分配的 nvmem 單元。
引數
struct device *dev請求 nvmem 單元的裝置。
struct nvmem_cell *cell先前由
devm_nvmem_cell_get()分配的 nvmem 單元。
-
void nvmem_cell_put(struct nvmem_cell *cell)¶
釋放先前分配的 nvmem 單元。
引數
struct nvmem_cell *cell先前由
nvmem_cell_get()分配的 nvmem 單元。
-
void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len)¶
讀取給定的 nvmem 單元。
引數
struct nvmem_cell *cell要讀取的 nvmem 單元。
size_t *len指向單元長度的指標,成功讀取時將被填充;可以為 NULL。
返回值
-
int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len)¶
寫入給定的 nvmem 單元。
引數
struct nvmem_cell *cell要寫入的 nvmem 單元。
void *buf要寫入的緩衝區。
size_t len要寫入 nvmem 單元的緩衝區長度。
返回值
寫入的位元組長度,失敗時為負值。
引數
struct device *dev請求 nvmem 單元的裝置。
const char *cell_id要讀取的 nvmem 單元的名稱。
u8 *val指向輸出值的指標。
返回值
成功時返回 0,失敗時返回負 errno。
引數
struct device *dev請求 nvmem 單元的裝置。
const char *cell_id要讀取的 nvmem 單元的名稱。
u16 *val指向輸出值的指標。
返回值
成功時返回 0,失敗時返回負 errno。
引數
struct device *dev請求 nvmem 單元的裝置。
const char *cell_id要讀取的 nvmem 單元的名稱。
u32 *val指向輸出值的指標。
返回值
成功時返回 0,失敗時返回負 errno。
引數
struct device *dev請求 nvmem 單元的裝置。
const char *cell_id要讀取的 nvmem 單元的名稱。
u64 *val指向輸出值的指標。
返回值
成功時返回 0,失敗時返回負 errno。
-
int nvmem_cell_read_variable_le_u32(struct device *dev, const char *cell_id, u32 *val)¶
將最多 32 位的資料讀取為小端數字。
引數
struct device *dev請求 nvmem 單元的裝置。
const char *cell_id要讀取的 nvmem 單元的名稱。
u32 *val指向輸出值的指標。
返回值
成功時返回 0,失敗時返回負 errno。
-
int nvmem_cell_read_variable_le_u64(struct device *dev, const char *cell_id, u64 *val)¶
將最多 64 位的資料讀取為小端數字。
引數
struct device *dev請求 nvmem 單元的裝置。
const char *cell_id要讀取的 nvmem 單元的名稱。
u64 *val指向輸出值的指標。
返回值
成功時返回 0,失敗時返回負 errno。
-
ssize_t nvmem_device_cell_read(struct nvmem_device *nvmem, struct nvmem_cell_info *info, void *buf)¶
讀取給定的 nvmem 裝置和單元。
引數
struct nvmem_device *nvmem要從中讀取的 nvmem 裝置。
struct nvmem_cell_info *info要讀取的 nvmem 單元資訊。
void *buf緩衝區指標,成功讀取時將被填充。
返回值
成功時返回讀取的位元組長度,出錯時返回負錯誤程式碼。
-
int nvmem_device_cell_write(struct nvmem_device *nvmem, struct nvmem_cell_info *info, void *buf)¶
將單元寫入給定的 nvmem 裝置。
引數
struct nvmem_device *nvmem要寫入的 nvmem 裝置。
struct nvmem_cell_info *info要寫入的 nvmem 單元資訊。
void *buf要寫入單元的緩衝區。
返回值
寫入的位元組長度,失敗時返回負錯誤程式碼。
-
int nvmem_device_read(struct nvmem_device *nvmem, unsigned int offset, size_t bytes, void *buf)¶
從給定的 nvmem 裝置讀取。
引數
struct nvmem_device *nvmem要從中讀取的 nvmem 裝置。
unsigned int offsetnvmem 裝置中的偏移量。
size_t bytes要讀取的位元組數。
void *buf緩衝區指標,成功讀取時將被填充。
返回值
成功時返回讀取的位元組長度,出錯時返回負錯誤程式碼。
-
int nvmem_device_write(struct nvmem_device *nvmem, unsigned int offset, size_t bytes, void *buf)¶
將單元寫入給定的 nvmem 裝置。
引數
struct nvmem_device *nvmem要寫入的 nvmem 裝置。
unsigned int offsetnvmem 裝置中的偏移量。
size_t bytes要寫入的位元組數。
void *buf要寫入的緩衝區。
返回值
寫入的位元組長度,失敗時返回負錯誤程式碼。
-
void nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries)¶
註冊單元查詢條目列表。
引數
struct nvmem_cell_lookup *entries單元查詢條目陣列。
size_t nentries陣列中單元查詢條目的數量。
-
void nvmem_del_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries)¶
刪除先前新增的單元查詢條目列表。
引數
struct nvmem_cell_lookup *entries單元查詢條目陣列。
size_t nentries陣列中單元查詢條目的數量。
-
const char *nvmem_dev_name(struct nvmem_device *nvmem)¶
獲取給定 nvmem 裝置的名稱。
引數
struct nvmem_device *nvmemnvmem 裝置。
返回值
nvmem 裝置的名稱。
-
size_t nvmem_dev_size(struct nvmem_device *nvmem)¶
獲取給定 nvmem 裝置的大小。
引數
struct nvmem_device *nvmemnvmem 裝置。
返回值
nvmem 裝置的大小。