PMBus 核心驅動程式和內部 API¶
簡介¶
[來自 pmbus.org] 電源管理匯流排 (PMBus) 是一種開放標準電源管理協議,具有完全定義的命令語言,有助於與電源轉換器和電源系統中的其他裝置進行通訊。 該協議透過行業標準 SMBus 序列介面實現,可對相容電源轉換產品進行程式設計、控制和即時監控。 這種靈活且高度通用的標準允許基於模擬和數字技術的裝置之間進行通訊,並提供真正的互操作性,從而降低設計複雜性並縮短電源系統設計人員的上市時間。 該開放式電源系統標準由領先的電源和半導體公司率先推出,由 PMBus 實施者論壇 (PMBus-IF) 維護和推廣,該論壇由 30 多個採用者組成,旨在為使用者提供支援並促進採用。
不幸的是,雖然 PMBus 命令是標準化的,但沒有強制性命令,製造商可以根據需要新增任意數量的非標準命令。 此外,如果執行不支援的命令,不同的 PMBu 裝置的行為也不同。 有些裝置返回錯誤,有些裝置返回 0xff 或 0xffff 並設定狀態錯誤標誌,有些裝置可能只是掛起。
儘管存在所有這些困難,但自核心版本 2.6.39 以來,通用 PMBus 裝置驅動程式仍然有用並受到支援。 但是,除了核心 PMBus 驅動程式之外,還需要支援裝置特定的擴充套件,因為人們根本不知道 PMBus 裝置開發人員接下來會想出什麼新的裝置特定功能。
為了使裝置特定的擴充套件儘可能具有可擴充套件性,並避免必須為新裝置重複修改核心 PMBus 驅動程式,PMBus 驅動程式被拆分為核心、通用和裝置特定程式碼。 核心程式碼(在 pmbus_core.c 中)提供通用功能。 通用程式碼(在 pmbus.c 中)為通用 PMBus 裝置提供支援。 裝置特定程式碼負責裝置特定的初始化,並在需要時將裝置特定功能對映到通用功能。 這在某種程度上與 PCI 程式碼類似,其中通用程式碼根據需要透過各種裝置的怪癖進行增強。
PMBus 裝置功能自動檢測¶
對於通用 PMBus 裝置,pmbus.c 中的程式碼嘗試自動檢測所有受支援的 PMBus 命令。 自動檢測受到一定的限制,因為需要考慮的變數太多。 例如,幾乎不可能自動檢測哪些 PMBus 命令已分頁,哪些命令已跨所有頁面複製(有關多頁 PMBus 裝置的詳細資訊,請參閱 PMBus 規範)。
因此,如果並非所有命令都可以自動檢測到,則提供裝置特定的驅動程式通常是有意義的。 此驅動程式中的資料結構可用於通知核心驅動程式有關各個晶片支援的功能。
某些命令始終會自動檢測到。 這適用於所有限制命令(lcrit、min、max 和 crit 屬性)以及關聯的警報屬性。 限制和警報屬性是自動檢測到的,因為可能存在的組合太多,無法提供手動配置介面。
PMBus 內部 API¶
核心和裝置特定 PMBus 程式碼之間的 API 在 drivers/hwmon/pmbus/pmbus.h 中定義。 除了內部 API 之外,pmbus.h 還定義了標準 PMBus 命令和虛擬 PMBus 命令。
標準 PMBus 命令¶
標準 PMBus 命令(命令值 0x00 到 0xff)在 PMBUs 規範中定義。
虛擬 PMBus 命令¶
提供虛擬 PMBus 命令是為了支援幾個晶片供應商已經實現的非標準功能,因此希望提供支援。
虛擬 PMBus 命令以命令值 0x100 開頭,因此可以很容易地與標準 PMBus 命令(不能有大於 0xff 的值)區分開來。 對虛擬 PMBus 命令的支援是裝置特定的,因此必須在裝置特定的程式碼中實現。
虛擬命令名為 PMBUS_VIRT_xxx,並以 PMBUS_VIRT_BASE 開頭。 所有虛擬命令都是字大小。
目前有兩種型別的虛擬命令。
READ 命令是隻讀的;寫入將被忽略或返回錯誤。
RESET 命令是讀/寫的。 讀取重置暫存器返回零(用於檢測),寫入任何值都會導致關聯的歷史記錄被重置。
虛擬命令必須在裝置特定的驅動程式程式碼中處理。 如果支援虛擬命令,晶片驅動程式程式碼將返回非負值,如果不支援虛擬命令,則返回負錯誤程式碼。 在這種情況下,晶片驅動程式可能會返回 -ENODATA 或任何其他 Linux 錯誤程式碼,但處理非 -ENODATA 的錯誤程式碼效率更高,因此首選。 無論哪種情況,如果晶片驅動程式在讀取或寫入虛擬暫存器時返回錯誤程式碼,則呼叫的 PMBus 核心程式碼將中止(換句話說,PMBus 核心程式碼永遠不會將虛擬命令傳送到晶片)。
PMBus 驅動程式資訊¶
PMBus 驅動程式資訊(在 struct pmbus_driver_info 中定義)是裝置特定驅動程式將資訊傳遞給核心 PMBus 驅動程式的主要手段。 具體來說,它提供以下資訊。
對於支援以直接資料格式儲存其資料的裝置,它提供用於將暫存器值轉換為標準化資料的係數。 此資料通常由晶片製造商在裝置資料表中提供。
可以將支援的晶片功能提供給核心驅動程式。 對於如果執行不支援的命令會產生不良反應的晶片,和/或為了加快裝置檢測和初始化速度,這可能是必要的。
提供幾個函式入口點,以支援覆蓋和/或增強通用命令執行。 此功能可用於將非標準 PMBus 命令對映到標準命令,或使用裝置特定資訊增強標準命令返回值。
PEC 支援¶
許多 PMBus 裝置支援 SMBus PEC(資料包錯誤檢查)。 如果 I2C 介面卡和 PMBus 晶片都支援,則預設情況下啟用它。 如果支援 PEC,則 PMBus 核心驅動程式會將名為“pec”的屬性新增到 I2C 裝置。 此屬性可用於控制與 PMBus 晶片通訊中的 PEC 支援。
API 函式¶
晶片驅動程式提供的函式¶
如果成功,所有函式都返回命令返回值(讀取)或零(寫入)。 返回值 -ENODATA 表示不存在製造商特定的命令,但可能存在標準 PMBus 命令。 任何其他負返回值表示該晶片不存在該命令,並且不應嘗試讀取或寫入標準命令。
如上所述,此規則的一個例外適用於虛擬命令,虛擬命令必須在驅動程式特定的程式碼中處理。 有關更多詳細資訊,請參閱上面的“虛擬 PMBus 命令”。
核心 PMBus 驅動程式程式碼中的命令執行如下
if (chip_access_function) {
status = chip_access_function();
if (status != -ENODATA)
return status;
}
if (command >= PMBUS_VIRT_BASE) /* For word commands/registers only */
return -EINVAL;
return generic_access();
晶片驅動程式可能會在 struct pmbus_driver_info 中提供指向以下函式的指標。 所有函式都是可選的。
int (*read_byte_data)(struct i2c_client *client, int page, int reg);
從頁面 <page>、暫存器 <reg> 讀取位元組。 <page> 可以為 -1,表示“當前頁面”。
int (*read_word_data)(struct i2c_client *client, int page, int phase,
int reg);
從頁面 <page>、階段 <phase>、暫存器 <reg> 讀取字。 如果晶片不支援多個階段,則可以忽略 phase 引數。 如果晶片支援多個階段,則 phase 值 0xff 表示所有階段。
int (*write_word_data)(struct i2c_client *client, int page, int reg,
u16 word);
將字寫入頁面 <page>、暫存器 <reg>。
int (*write_byte)(struct i2c_client *client, int page, u8 value);
將位元組寫入頁面 <page>、暫存器 <reg>。 <page> 可以為 -1,表示“當前頁面”。
int (*identify)(struct i2c_client *client, struct pmbus_driver_info *info);
確定支援的 PMBus 功能。 只有在晶片驅動程式支援多個晶片並且晶片功能不是預先確定的情況下,此函式才是必要的。 它目前僅由通用 pmbus 驅動程式 (pmbus.c) 使用。
核心驅動程式匯出的函式¶
預計晶片驅動程式使用以下函式來讀取或寫入 PMBus 暫存器。 晶片驅動程式也可以使用直接 I2C 命令。 如果使用直接 I2C 命令,則晶片驅動程式程式碼不得直接修改當前頁面,因為所選頁面快取在核心驅動程式中,並且核心驅動程式將假定它已被選中。 必須使用 pmbus_set_page() 選擇新頁面。
int pmbus_set_page(struct i2c_client *client, u8 page, u8 phase);
將 PMBus 頁面暫存器設定為 <page> 和 <phase> 以供後續命令使用。 如果晶片不支援多個階段,則忽略 phase 引數。 否則,phase 值 0xff 選擇所有階段。
int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 phase,
u8 reg);
從 <page>、<phase>、<reg> 讀取字資料。 類似於 i2c_smbus_read_word_data(),但首先選擇頁面和階段。 如果晶片不支援多個階段,則忽略 phase 引數。 否則,phase 值 0xff 選擇所有階段。
int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg,
u16 word);
將字資料寫入 <page>、<reg>。 類似於 i2c_smbus_write_word_data(),但首先選擇頁面。
int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg);
從 <page>、<reg> 讀取位元組資料。 類似於 i2c_smbus_read_byte_data(),但首先選擇頁面。 <page> 可以為 -1,表示“當前頁面”。
int pmbus_write_byte(struct i2c_client *client, int page, u8 value);
將位元組資料寫入 <page>、<reg>。 類似於 i2c_smbus_write_byte(),但首先選擇頁面。 <page> 可以為 -1,表示“當前頁面”。
void pmbus_clear_faults(struct i2c_client *client);
在所有晶片頁面上執行 PMBus“清除故障”命令。 如果定義了裝置特定的 write_byte 函式,則此函式會呼叫該函式。 因此,_不能_從該函式呼叫它。
bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg);
檢查位元組暫存器是否存在。 如果暫存器存在,則返回 true,否則返回 false。 如果定義了裝置特定的 write_byte 函式以獲取晶片狀態,則此函式會呼叫該函式。 因此,_不能_從該函式呼叫它。
bool pmbus_check_word_register(struct i2c_client *client, int page, int reg);
檢查字暫存器是否存在。 如果暫存器存在,則返回 true,否則返回 false。 如果定義了裝置特定的 write_byte 函式以獲取晶片狀態,則此函式會呼叫該函式。 因此,_不能_從該函式呼叫它。
int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info);
執行探測函式。 類似於其他驅動程式的標準探測函式,其中指向 struct pmbus_driver_info 的指標作為附加引數。 如果支援,則呼叫 identify 函式。 必須僅從裝置探測函式呼叫。
const struct pmbus_driver_info
*pmbus_get_driver_info(struct i2c_client *client);
返回指向傳遞給 pmbus_do_probe() 的 struct pmbus_driver_info 的指標。
PMBus 驅動程式平臺數據¶
PMBus 平臺數據在 include/linux/pmbus.h 中定義。 平臺數據當前提供一個帶有四個已用位的標誌欄位
#define PMBUS_SKIP_STATUS_CHECK BIT(0)
#define PMBUS_WRITE_PROTECTED BIT(1)
#define PMBUS_NO_CAPABILITY BIT(2)
#define PMBUS_READ_STATUS_AFTER_FAILED_CHECK BIT(3)
#define PMBUS_NO_WRITE_PROTECT BIT(4)
#define PMBUS_USE_COEFFICIENTS_CMD BIT(5)
#define PMBUS_OP_PROTECTED BIT(6)
#define PMBUS_VOUT_PROTECTED BIT(7)
struct pmbus_platform_data {
u32 flags; /* Device specific flags */
/* regulator support */
int num_regulators;
struct regulator_init_data *reg_init_data;
};
標誌¶
PMBUS_SKIP_STATUS_CHECK
在暫存器檢測期間,跳過檢查狀態暫存器是否存在通訊或命令錯誤。
某些 PMBus 晶片在嘗試讀取不受支援的暫存器時會響應有效資料。 對於此類晶片,嘗試確定晶片暫存器是否存在時,必須檢查狀態暫存器。 其他 PMBus 晶片不支援 STATUS_CML 暫存器,或者報告沒有明顯原因的通訊錯誤。 對於此類晶片,必須停用狀態暫存器檢查。
某些 i2c 控制器不支援單位元組命令(沒有資料的寫命令,i2c_smbus_write_byte())。 對於此類控制器,無法清除狀態暫存器,並且必須設定 PMBUS_SKIP_STATUS_CHECK 標誌。
PMBUS_WRITE_PROTECTED
如果晶片受到防寫並且防寫不是由標準 WRITE_PROTECT 命令確定的,則設定此標誌。
PMBUS_NO_CAPABILITY
某些 PMBus 晶片在讀取 CAPABILITY 暫存器時不會響應有效資料。 對於此類晶片,應設定此標誌,以便 PMBus 核心驅動程式不使用 CAPABILITY 來確定其行為。
PMBUS_READ_STATUS_AFTER_FAILED_CHECK
在每次暫存器檢查失敗後讀取 STATUS 暫存器。
某些 PMBus 晶片在嘗試讀取不受支援的暫存器時最終會進入未定義的狀態。 對於此類晶片,必須在暫存器檢查失敗後將晶片 pmbus 控制器重置為已知狀態。 這可以透過讀取已知暫存器來完成。 透過設定此標誌,驅動程式將在每次暫存器檢查失敗後嘗試讀取 STATUS 暫存器。 此讀取可能會失敗,但它會將晶片置於已知狀態。
PMBUS_NO_WRITE_PROTECT
某些 PMBus 晶片在讀取 WRITE_PROTECT 暫存器時會響應無效資料。 對於此類晶片,應設定此標誌,以便 PMBus 核心驅動程式不使用 WRITE_PROTECT 命令來確定其行為。
PMBUS_USE_COEFFICIENTS_CMD
設定此標誌後,PMBus 核心驅動程式將使用 COEFFICIENTS 暫存器來初始化直接模式格式的係數。
PMBUS_OP_PROTECTED
如果晶片 OPERATION 命令受到保護並且保護不是由標準 WRITE_PROTECT 命令確定的,則設定此標誌。
PMBUS_VOUT_PROTECTED
如果晶片 VOUT_COMMAND 命令受到保護並且保護不是由標準 WRITE_PROTECT 命令確定的,則設定此標誌。
模組引數¶
pmbus_core.wp: PMBus 防寫強制模式
PMBus 可能會提供各種防寫配置。 如果需要特定的防寫,可以使用“pmbus_core.wp”。 實際更改保護的能力也可能取決於晶片,因此實際的執行時防寫配置可能與請求的配置不同。 pmbus_core 當前支援以下值
0:刪除防寫。
1:停用所有寫入,但寫入 WRITE_PROTECT、OPERATION、PAGE、ON_OFF_CONFIG 和 VOUT_COMMAND 命令除外。
2:停用所有寫入,但寫入 WRITE_PROTECT、OPERATION 和 PAGE 命令除外。
3:停用所有寫入,但寫入 WRITE_PROTECT 命令除外。 請注意,保護應包括 PAGE 暫存器。 如果晶片嚴格遵循 PMBus 規範,這可能會給多頁晶片帶來問題,從而阻止晶片更改活動頁面。