linux PMBus總線及設備驅動分析


PMBus協議規范介紹

 

PMBus是一套對電源進行配置、控制和監控的通訊協議標准。其最新版本為1.3,該規范還在不斷演進中,比如新標准中新增的zone PMBus、AVSBus等特性。在其官網上有詳細的規范文檔,本節不嘗試翻譯規范文檔,重點記錄作者在了解PMBus過程中的疑問和解答。

PMBus與I2C、SMBus的區別?

PMBus在SMBus(System Management Bus)基礎上增加了一套電源配置、控制和監控規范。SMBus最初是為電池智能管理而開發的一套標准,其基於I2C協議,並針對I2C協議的弱健壯性做了如下改進:

  • 支持SMBALERT#中斷;
  • 支持錯包檢測(PEC);
  • 支持包超時;
  • 支持START/STOP保護;
  • 支持Host Notify Protocol協議;

PMBus監控哪些參數?告警分為幾級?不同告警級別有什么樣的應對措施?

 

PMBus支持電壓、電流、功率、溫度和風扇等參數的上下限監控,支持warning和fault 2級告警級別(如上圖所示)。

  • warning告警:表示監控參數異常,系統需引起關注,但可以繼續運行,系統無需任何響應措施;
  • fault告警:比warning告警級別高,系統會根據異常對設備的危害情況,進行設備控制電路重啟(restart)或輸出切斷(shutdown)等處理;

告警產生時如何上報給主機?

告警上報一般有如下幾種方式:

  • 主機輪詢PMBus設備;
  • PMBus設備通過SMBALERT#中斷通知主機;
  • Host Notify Protocol(PMBus設備臨時切換成總線主機(bus master),並發送一組特定協議通知系統主機)。

什么情況下告警會取消或清除?重啟是否會清除告警?

任何warning或fault告警一旦上報,只有通過如下幾種方式可以取消清除:

  • PMBus設備接收到CLEAR_FAULTS命令;
  • PMBus設備RESET引腳生效;
  • PMBus設備通過CONTROL引腳或OPERATION命令關閉並重新打開;
  • 斷電;
  • 如果異常一直存在,那么即使進行告警清除操作,告警會馬上重新上報。

linux PMBus驅動設計分析

PMBus設備驅動位於linux/drivers/hwmon/pmbus,文件組織划分為3個部分:

  • pmbus_core.c:PMBus通用設備驅動,總線規范實現,sysfs外部接口呈現;
  • pmbus.c:標准PMBus設備注冊;
  • [device].c:非標准PMBus設備注冊。

比較有意思的是PMBus的通用設備驅動框架設計部分,其設計方案主要要解決如下2個問題:

  1. 支持PMBus設備廠商的自定義功能集。PMBus規范定義一套功能集,其中有些是基本功能,有些是可選功能;
  2. 支持PMBus設備廠商的自定義寄存器。

pmbus驅動框架的數據模型如下,其核心對象為i2c_client,即i2c設備對象,i2c_client繼承於linux設備驅動模型device對象。pmbus設備信息通過設備驅動模型抽象接口driver_data訪問,由pmbus_data對象實現。pmbus_data對象又關聯如下2個主要對象:

  • pmbus_driver_info:PMBus設備支持的功能集描述及相關接口,由pmbus設備實現。
  • pmbus_sensor:PMBus設備支持的監控傳感器對象鏈表,由voltage/current/power/temp/fan實現。

pmbus設備功能集識別有2種實現方式:

  • 由pmbus_driver_info對象的identify接口完成,其工作原理是通過讀取功能寄存器,如果讀取成功,則說明設備支持此功能,否則不支持;
  • 直接靜態初始化pmbus_driver_info。

設備自定義寄存器通過虛擬寄存器(Virtual registers)統一到pmbus驅動框架中。pmbus通用設備驅動只看到標准寄存器和虛擬寄存器。虛擬寄存器到設備自定義寄存器的映射過程通過設備注冊的4個接口:read_byte_data/read_word_data/write_word_data/write_byte來完成。 

應用示例

1. 編寫pmbus設備的smbus總線設備驅動並注冊。如下i2c-10[1-3]為epld實現的4個i2c總線設備

/ # ls /sys/bus/i2c/devices/ -l
total 0
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 i2c-100 -> ../../../devices/i2c-100
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 i2c-101 -> ../../../devices/i2c-101
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 i2c-102 -> ../../../devices/i2c-102
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 i2c-103 -> ../../../devices/i2c-103

/ # cats '/sys/bus/i2c/devices/i2c-10*/name'
/sys/bus/i2c/devices/i2c-100/name: WORK_EPLD1
/sys/bus/i2c/devices/i2c-101/name: WORK_EPLD2.0
/sys/bus/i2c/devices/i2c-102/name: WORK_EPLD2.1
/sys/bus/i2c/devices/i2c-103/name: WORK_NSE
/ #

  

 

2. 編寫pmbus設備驅動並注冊。如下為注冊方法,hwmon[0-9]為注冊的pmbus設備

 

echo tps53667 0x60 > /sys/bus/i2c/devices/i2c-100/new_device
echo tps53667 0x62 > /sys/bus/i2c/devices/i2c-100/new_device
echo tps53667 0x1060 > /sys/bus/i2c/devices/i2c-100/new_device
echo tps53667 0x1062 > /sys/bus/i2c/devices/i2c-100/new_device

echo tps53667 0x60 > /sys/bus/i2c/devices/i2c-101/new_device
echo tps53667 0x60 > /sys/bus/i2c/devices/i2c-102/new_device

echo tps53667 0x70 > /sys/bus/i2c/devices/i2c-103/new_device
echo tps53667 0x1071 > /sys/bus/i2c/devices/i2c-103/new_device
echo tps53667 0x2072 > /sys/bus/i2c/devices/i2c-103/new_device
echo tps53667 0x3073 > /sys/bus/i2c/devices/i2c-103/new_device

/ # ls /sys/class/hwmon/ -l
total 0
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 hwmon0 -> ../../devices/i2c-100/100-0060/hwmon/hwmon0
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 hwmon1 -> ../../devices/i2c-100/100-0062/hwmon/hwmon1
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 hwmon2 -> ../../devices/i2c-100/100-1060/hwmon/hwmon2
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 hwmon3 -> ../../devices/i2c-100/100-1062/hwmon/hwmon3
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 hwmon4 -> ../../devices/i2c-101/101-0060/hwmon/hwmon4
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 hwmon5 -> ../../devices/i2c-102/102-0060/hwmon/hwmon5
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 hwmon6 -> ../../devices/i2c-103/103-0070/hwmon/hwmon6
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 hwmon7 -> ../../devices/i2c-103/103-1071/hwmon/hwmon7
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 hwmon8 -> ../../devices/i2c-103/103-2072/hwmon/hwmon8
lrwxrwxrwx    1 root     root             0 Jan  1 00:00 hwmon9 -> ../../devices/i2c-103/103-3073/hwmon/hwmon9

  

3. 查看pmbus設備監控數據,所有字段解釋詳見kernel文檔

 

/sys/class/hwmon # cats 'hwmon2/device/*'
# 電流監控數據  單位:mA 毫安  /1000
hwmon2/device/curr1_crit:       255000
hwmon2/device/curr1_crit_alarm: 0
hwmon2/device/curr1_input:      2679
hwmon2/device/curr1_label:      iin
hwmon2/device/curr1_max:        25000
hwmon2/device/curr1_max_alarm:  0
hwmon2/device/curr2_crit:       122000
hwmon2/device/curr2_crit_alarm: 0
hwmon2/device/curr2_input:      22968
hwmon2/device/curr2_label:      iout1
hwmon2/device/curr2_max:        98000
hwmon2/device/curr2_max_alarm:  0
hwmon2/device/driver:   cat: read error: Is a directory
hwmon2/device/hwmon:    cat: read error: Is a directory
# 電壓監控數據  單位:mV 毫伏 1/1000
hwmon2/device/in1_crit: 17000
hwmon2/device/in1_crit_alarm:   0
hwmon2/device/in1_input:        11906
hwmon2/device/in1_label:        vin
hwmon2/device/in2_alarm:        0
hwmon2/device/in2_input:        631
hwmon2/device/in2_label:        vout1
hwmon2/device/modalias: i2c:tps53667
hwmon2/device/name:     tps53667
# 功率監控數據  單位:uW 微伏 1/1000000
hwmon2/device/power1_input:     31625000
hwmon2/device/power1_label:     pin
hwmon2/device/power2_input:     23343750
hwmon2/device/power2_label:     pout1
hwmon2/device/subsystem:        cat: read error: Is a directory
# 溫度監控數據  單位:m℃ 毫攝氏度 1/1000
hwmon2/device/temp1_crit:       125000
hwmon2/device/temp1_crit_alarm: 0
hwmon2/device/temp1_input:      35500
hwmon2/device/temp1_max:        95000
hwmon2/device/temp1_max_alarm:  0
hwmon2/device/uevent:   DRIVER=tps53667
MODALIAS=i2c:tps53667

  

4. 構造一個UV fault alarm,如下。可見異常恢復后,告警依然保持,不會清除;重啟也不是清除告警;手動清除后,告警清除。

 

/sys/devices/i2c-103/103-3073 # cats 'in*'
in1_crit:       17000
in1_crit_alarm: 0
in1_input:      11968
in1_label:      vin
in2_alarm:      0
in2_input:      0
in2_label:      vout1
/sys/devices/i2c-103/103-3073 # echo 10000 > in1_crit  # 將in voltage的UV fault閾值設成10V
/sys/devices/i2c-103/103-3073 # cats 'in*'
in1_crit:       10000
in1_crit_alarm: 1  # 告警觸發
in1_input:      11953
in1_label:      vin
in2_alarm:      0
in2_input:      0
in2_label:      vout1
/sys/devices/i2c-103/103-3073 # echo 17000 > in1_crit  # 將in voltage的UV fault閾值恢復成17V
/sys/devices/i2c-103/103-3073 # cats 'in*'
in1_crit:       17000
in1_crit_alarm: 1  # 異常后再恢復正常,告警依然保持
in1_input:      11968
in1_label:      vin
in2_alarm:      0
in2_input:      0
in2_label:      vout1
/sys/devices/i2c-103/103-3073 # reboot 
。。。(啟動過程省略)
/sys/devices/i2c-103/103-3073 # cats 'in*'
in1_crit:       17000
in1_crit_alarm: 1  # 重啟不會清除告警
in1_input:      11968
in1_label:      vin
in2_alarm:      0
in2_input:      0
in2_label:      vout1
/sys/devices/i2c-103/103-3073 # echo 0 > clear_fault   # 手動清除告警
/sys/devices/i2c-103/103-3073 # cats 'in*'
in1_crit:       17000
in1_crit_alarm: 0  # 告警清除
in1_input:      11968
in1_label:      vin
in2_alarm:      0
in2_input:      631
in2_label:      vout1

  

 

附主要數據結構:

struct pmbus_data {
    struct device *dev;
    struct device *hwmon_dev;

    u32 flags;        /* from platform data */

    int exponent;        /* linear mode: exponent for output voltages */

    const struct pmbus_driver_info *info;

    int max_attributes;
    int num_attributes;
    struct attribute_group group;

    struct pmbus_sensor *sensors;

    struct mutex update_lock;
    bool valid;
    unsigned long last_updated;    /* in jiffies */

    /*
     * A single status register covers multiple attributes,
     * so we keep them all together.
     */
    u8 status[PB_NUM_STATUS_REG];
    u8 status_register;

    u8 currpage;
};

struct pmbus_driver_info {
    int pages;        /* Total number of pages */
    enum pmbus_data_format format[PSC_NUM_CLASSES];
    /*
     * Support one set of coefficients for each sensor type
     * Used for chips providing data in direct mode.
     */
    int m[PSC_NUM_CLASSES];    /* mantissa for direct data format */
    int b[PSC_NUM_CLASSES];    /* offset */
    int R[PSC_NUM_CLASSES];    /* exponent */

    u32 func[PMBUS_PAGES];    /* Functionality, per page */
    /*
     * The following functions map manufacturing specific register values
     * to PMBus standard register values. Specify only if mapping is
     * necessary.
     * Functions return the register value (read) or zero (write) if
     * successful. A return value of -ENODATA indicates that there is no
     * manufacturer specific register, but that a standard PMBus register
     * may exist. Any other negative return value indicates that the
     * register does not exist, and that no attempt should be made to read
     * the standard register.
     */
    int (*read_byte_data)(struct i2c_client *client, int page, int reg);
    int (*read_word_data)(struct i2c_client *client, int page, int reg);
    int (*write_word_data)(struct i2c_client *client, int page, int reg,
                   u16 word);
    int (*write_byte)(struct i2c_client *client, int page, u8 value);
    /*
     * The identify function determines supported PMBus functionality.
     * This function is only necessary if a chip driver supports multiple
     * chips, and the chip functionality is not pre-determined.
     */
    int (*identify)(struct i2c_client *client,
            struct pmbus_driver_info *info);
};

struct pmbus_sensor {
    struct pmbus_sensor *next;
    char name[PMBUS_NAME_SIZE];    /* sysfs sensor name */
    struct device_attribute attribute;
    u8 page;        /* page number */
    u16 reg;        /* register */
    enum pmbus_sensor_classes class;    /* sensor class */
    bool update;        /* runtime sensor update needed */
    int data;        /* Sensor data.
                   Negative if there was a read error */
};

/*
 * Virtual registers.
 * Useful to support attributes which are not supported by standard PMBus
 * registers but exist as manufacturer specific registers on individual chips.
 * Must be mapped to real registers in device specific code.
 *
 * Semantics:
 * Virtual registers are all word size.
 * READ registers are read-only; writes are either ignored or return an error.
 * RESET registers are read/write. Reading reset registers returns zero
 * (used for detection), writing any value causes the associated history to be
 * reset.
 * Virtual registers have to be handled in device specific driver code. Chip
 * driver code returns non-negative register values if a virtual register is
 * supported, or a negative error code if not. The chip driver may return
 * -ENODATA or any other error code in this case, though an error code other
 * than -ENODATA is handled more efficiently and thus preferred. Either case,
 * the calling PMBus core code will abort if the chip driver returns an error
 * code when reading or writing virtual registers.
 */
#define PMBUS_VIRT_BASE            0x100
#define PMBUS_VIRT_READ_TEMP_AVG    (PMBUS_VIRT_BASE + 0)
#define PMBUS_VIRT_READ_TEMP_MIN    (PMBUS_VIRT_BASE + 1)
#define PMBUS_VIRT_READ_TEMP_MAX    (PMBUS_VIRT_BASE + 2)
#define PMBUS_VIRT_RESET_TEMP_HISTORY    (PMBUS_VIRT_BASE + 3)
#define PMBUS_VIRT_READ_VIN_AVG        (PMBUS_VIRT_BASE + 4)
#define PMBUS_VIRT_READ_VIN_MIN        (PMBUS_VIRT_BASE + 5)
#define PMBUS_VIRT_READ_VIN_MAX        (PMBUS_VIRT_BASE + 6)
#define PMBUS_VIRT_RESET_VIN_HISTORY    (PMBUS_VIRT_BASE + 7)
#define PMBUS_VIRT_READ_IIN_AVG        (PMBUS_VIRT_BASE + 8)
#define PMBUS_VIRT_READ_IIN_MIN        (PMBUS_VIRT_BASE + 9)
#define PMBUS_VIRT_READ_IIN_MAX        (PMBUS_VIRT_BASE + 10)
#define PMBUS_VIRT_RESET_IIN_HISTORY    (PMBUS_VIRT_BASE + 11)
#define PMBUS_VIRT_READ_PIN_AVG        (PMBUS_VIRT_BASE + 12)
#define PMBUS_VIRT_READ_PIN_MAX        (PMBUS_VIRT_BASE + 13)
#define PMBUS_VIRT_RESET_PIN_HISTORY    (PMBUS_VIRT_BASE + 14)
#define PMBUS_VIRT_READ_POUT_AVG    (PMBUS_VIRT_BASE + 15)
#define PMBUS_VIRT_READ_POUT_MAX    (PMBUS_VIRT_BASE + 16)
#define PMBUS_VIRT_RESET_POUT_HISTORY    (PMBUS_VIRT_BASE + 17)
#define PMBUS_VIRT_READ_VOUT_AVG    (PMBUS_VIRT_BASE + 18)
#define PMBUS_VIRT_READ_VOUT_MIN    (PMBUS_VIRT_BASE + 19)
#define PMBUS_VIRT_READ_VOUT_MAX    (PMBUS_VIRT_BASE + 20)
#define PMBUS_VIRT_RESET_VOUT_HISTORY    (PMBUS_VIRT_BASE + 21)
#define PMBUS_VIRT_READ_IOUT_AVG    (PMBUS_VIRT_BASE + 22)
#define PMBUS_VIRT_READ_IOUT_MIN    (PMBUS_VIRT_BASE + 23)
#define PMBUS_VIRT_READ_IOUT_MAX    (PMBUS_VIRT_BASE + 24)
#define PMBUS_VIRT_RESET_IOUT_HISTORY    (PMBUS_VIRT_BASE + 25)
#define PMBUS_VIRT_READ_TEMP2_AVG    (PMBUS_VIRT_BASE + 26)
#define PMBUS_VIRT_READ_TEMP2_MIN    (PMBUS_VIRT_BASE + 27)
#define PMBUS_VIRT_READ_TEMP2_MAX    (PMBUS_VIRT_BASE + 28)
#define PMBUS_VIRT_RESET_TEMP2_HISTORY    (PMBUS_VIRT_BASE + 29)

#define PMBUS_VIRT_READ_VMON        (PMBUS_VIRT_BASE + 30)
#define PMBUS_VIRT_VMON_UV_WARN_LIMIT    (PMBUS_VIRT_BASE + 31)
#define PMBUS_VIRT_VMON_OV_WARN_LIMIT    (PMBUS_VIRT_BASE + 32)
#define PMBUS_VIRT_VMON_UV_FAULT_LIMIT    (PMBUS_VIRT_BASE + 33)
#define PMBUS_VIRT_VMON_OV_FAULT_LIMIT    (PMBUS_VIRT_BASE + 34)
#define PMBUS_VIRT_STATUS_VMON        (PMBUS_VIRT_BASE + 35)

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM