Linux kernel dpll 子系統

DPLL

PLL - 鎖相環是一種電子電路,它將裝置的時鐘訊號與外部時鐘訊號同步。 有效地使裝置以與 PLL 輸入上提供的相同的時鐘訊號節拍執行。

DPLL - 數字鎖相環是一種積體電路,除了普通的 PLL 行為外,還包含數字相位檢測器,並且可能在環路中具有數字分頻器。 因此,DPLL 輸入和輸出端的頻率可以配置。

子系統

dpll 子系統的主要目的是提供通用介面來配置使用任何型別的數字 PLL 的裝置,並且可以使用不同的輸入訊號源進行同步,以及不同型別的輸出。 主要介面是基於 NETLINK_GENERIC 的協議,並定義了一個事件監控多播組。

裝置物件

單個 dpll 裝置物件表示單個數字 PLL 電路和一堆連線的引腳。 它向用戶報告支援的操作模式和當前狀態,以響應 netlink 命令 DPLL_CMD_DEVICE_GETdo 請求,並列出子系統中註冊的 dpll,以響應同一命令的 dump netlink 請求。 更改 dpll 裝置的配置是透過 netlink DPLL_CMD_DEVICE_SET 命令的 do 請求完成的。 裝置控制代碼是 DPLL_A_ID,應提供該控制代碼以獲取或設定系統中特定裝置的配置。 它可以與 DPLL_CMD_DEVICE_GET dump 請求或 DPLL_CMD_DEVICE_ID_GET do 請求一起獲取,其中一個必須提供導致單個裝置匹配的屬性。

引腳物件

引腳是一個非晶形物件,表示輸入或輸出,它可以是裝置的內部元件,也可以是外部連線的元件。 每個 dpll 的引腳數各不相同,但通常會為單個 dpll 裝置提供多個引腳。 引腳的屬性、功能和狀態在響應 netlink DPLL_CMD_PIN_GET 命令的 do 請求時提供給使用者。 也可以使用 DPLL_CMD_PIN_GET 命令的 dump 請求列出系統中註冊的所有引腳。 可以透過 netlink DPLL_CMD_PIN_SET 命令的 do 請求來更改引腳的配置。 引腳控制代碼是 DPLL_A_PIN_ID,應提供該控制代碼以獲取或設定系統中特定引腳的配置。 可以透過 DPLL_CMD_PIN_GET dump 請求或 DPLL_CMD_PIN_ID_GET do 請求來獲取,其中使用者提供導致單個引腳匹配的屬性。

引腳選擇

通常,所選引腳(訊號驅動 dpll 裝置的引腳)可以從 DPLL_A_PIN_STATE 屬性獲得,並且對於任何 dpll 裝置,只能有一個引腳處於 DPLL_PIN_STATE_CONNECTED 狀態。

引腳選擇可以手動或自動完成,具體取決於硬體功能和活動的 dpll 裝置工作模式(DPLL_A_MODE 屬性)。 結果是,對於每種模式,可用引腳狀態都存在差異,並且對於使用者可以為 dpll 裝置請求的狀態也存在差異。

在手動模式 (DPLL_MODE_MANUAL) 中,使用者可以請求或接收以下引腳狀態之一

  • DPLL_PIN_STATE_CONNECTED - 引腳用於驅動 dpll 裝置

  • DPLL_PIN_STATE_DISCONNECTED - 引腳不用於驅動 dpll 裝置

在自動模式 (DPLL_MODE_AUTOMATIC) 中,使用者可以請求或接收以下引腳狀態之一

  • DPLL_PIN_STATE_SELECTABLE - 應將引腳視為自動選擇演算法的有效輸入

  • DPLL_PIN_STATE_DISCONNECTED - 不應將引腳視為自動選擇演算法的有效輸入

在自動模式 (DPLL_MODE_AUTOMATIC) 中,一旦自動選擇演算法使用其中一個輸入鎖定 dpll 裝置,使用者只能接收引腳狀態 DPLL_PIN_STATE_CONNECTED

共享引腳

單個引腳物件可以連線到多個 dpll 裝置。 然後有兩組配置旋鈕

  1. 在引腳上設定 - 配置影響引腳註冊到的所有 dpll 裝置(即,DPLL_A_PIN_FREQUENCY),

  2. 在引腳-dpll 元組上設定 - 配置僅影響選定的 dpll 裝置(即,DPLL_A_PIN_PRIODPLL_A_PIN_STATEDPLL_A_PIN_DIRECTION)。

MUX 型別引腳

引腳可以是 MUX 型別,它聚合子引腳並充當引腳多路複用器。 一個或多個引腳註冊為 MUX 型別,而不是直接註冊到 dpll 裝置。 註冊為 MUX 型別引腳的引腳為使用者提供額外的巢狀屬性 DPLL_A_PIN_PARENT_PIN,用於它們註冊到的每個父引腳。 如果引腳註冊了多個父引腳,則它們的行為類似於多輸出多路複用器。 在這種情況下,DPLL_CMD_PIN_GET 的輸出將包含多個引腳-父級巢狀屬性,其中包含與每個父級相關的當前狀態,例如

'pin': [{{
  'clock-id': 282574471561216,
  'module-name': 'ice',
  'capabilities': 4,
  'id': 13,
  'parent-pin': [
  {'parent-id': 2, 'state': 'connected'},
  {'parent-id': 3, 'state': 'disconnected'}
  ],
  'type': 'synce-eth-port'
  }}]

一次只有一個子引腳可以將其訊號提供給父 MUX 型別引腳,選擇是透過請求更改所需父級上的子引腳狀態來完成的,方法是使用 DPLL_A_PIN_PARENT 巢狀屬性。 在父引腳上設定狀態 netlink 訊息格式示例

DPLL_A_PIN_ID

子引腳 ID

DPLL_A_PIN_PARENT_PIN

用於請求與父引腳相關的配置的巢狀屬性

DPLL_A_PIN_PARENT_ID

父引腳 ID

DPLL_A_PIN_STATE

父引腳上請求的引腳狀態

引腳優先順序

某些裝置可能提供自動引腳選擇模式的功能(DPLL_A_MODE 屬性的列舉值 DPLL_MODE_AUTOMATIC)。 通常,自動選擇是在硬體級別執行的,這意味著只有直接連線到 dpll 的引腳才能用於自動輸入引腳選擇。 在自動選擇模式下,使用者無法手動選擇裝置的輸入引腳,而是使用者應為所有直接連線的引腳提供優先順序 DPLL_A_PIN_PRIO,裝置將選擇最高優先順序的有效訊號並使用它來控制 DPLL 裝置。 在父引腳上設定優先順序 netlink 訊息格式示例

DPLL_A_PIN_ID

配置的引腳 ID

DPLL_A_PIN_PARENT_DEVICE

用於請求與父 dpll 裝置相關的配置的巢狀屬性

DPLL_A_PIN_PARENT_ID

父 dpll 裝置 ID

DPLL_A_PIN_PRIO

在父 dpll 上請求的引腳優先順序

MUX 型別引腳的子引腳無法進行自動輸入引腳選擇,為了配置 MUX 型別引腳的活動輸入,使用者需要請求父引腳上子引腳的所需引腳狀態,如 MUX-type pins 章節所述。

相位偏移測量和調整

裝置可以提供測量引腳上的訊號及其父 dpll 裝置之間的相位差的能力。 如果支援引腳-dpll 相位偏移測量,則應為每個父 dpll 裝置提供 DPLL_A_PIN_PHASE_OFFSET 屬性。

裝置還可以提供調整引腳上訊號相位的能力。 如果支援引腳相位調整,則引腳控制代碼應在 DPLL_CMD_PIN_GET 響應中為使用者提供最小值和最大值,屬性為 DPLL_A_PIN_PHASE_ADJUST_MINDPLL_A_PIN_PHASE_ADJUST_MAX。 配置的相位調整值由引腳的 DPLL_A_PIN_PHASE_ADJUST 屬性提供,並且可以使用相同的屬性透過 DPLL_CMD_PIN_SET 命令請求更改值。

DPLL_A_PIN_ID

配置的引腳 ID

DPLL_A_PIN_PHASE_ADJUST_MIN

attr 相位調整的最小值

DPLL_A_PIN_PHASE_ADJUST_MAX

attr 相位調整的最大值

DPLL_A_PIN_PHASE_ADJUST

attr 在父 dpll 裝置上配置的相位調整值

DPLL_A_PIN_PARENT_DEVICE

用於請求給定父 dpll 裝置上的配置的巢狀屬性

DPLL_A_PIN_PARENT_ID

父 dpll 裝置 ID

DPLL_A_PIN_PHASE_OFFSET

attr 引腳和父 dpll 裝置之間測量的相位差

所有與相位相關的值均以皮秒為單位提供,表示訊號相位之間的時間差。 負值表示引腳上的訊號的相位在時間上早於 dpll 的訊號。 正值表示引腳上的訊號的相位在時間上晚於 dpll 的訊號。

相位調整(最小值和最大值)值是整數,但測量的相位偏移值是具有 3 位小數位的分數,並且應使用 DPLL_PIN_PHASE_OFFSET_DIVIDER 進行除以獲得整數部分,並進行模運算以獲得分數部分。

嵌入式 SYNC

裝置可以提供使用嵌入式 SYNC 功能的能力。 它允許將額外的 SYNC 訊號嵌入到引腳的基本頻率中 - 每次發生 SYNC 訊號脈衝時,基本頻率訊號的一個特殊脈衝。 使用者可以配置嵌入式 SYNC 的頻率。 嵌入式 SYNC 功能始終與給定的基本頻率和硬體功能相關。 根據為引腳配置的當前基本頻率,為使用者提供支援的嵌入式 SYNC 頻率範圍。

DPLL_A_PIN_ESYNC_FREQUENCY

當前嵌入式 SYNC 頻率

DPLL_A_PIN_ESYNC_FREQUENCY_SUPPORTED

巢狀可用嵌入式 SYNC 頻率範圍

DPLL_A_PIN_FREQUENCY_MIN

attr 頻率的最小值

DPLL_A_PIN_FREQUENCY_MAX

attr 頻率的最大值

DPLL_A_PIN_ESYNC_PULSE

嵌入式 SYNC 的脈衝型別

配置命令組

配置命令用於獲取有關注冊的 dpll 裝置(和引腳)的資訊,以及設定裝置或引腳的配置。 由於 dpll 裝置必須被抽象化並反映真實的硬體,因此無法從使用者空間透過 netlink 新增新的 dpll 裝置,並且每個裝置都應由其驅動程式註冊。

所有 netlink 命令都需要 GENL_ADMIN_PERM。 這是為了防止來自未經授權的使用者空間應用程式的任何垃圾郵件/DoS 攻擊。

DPLL_CMD_DEVICE_GETDPLL_CMD_PIN_GET 命令能夠進行轉儲型別的 netlink 請求,在這種情況下,響應的格式與它們的 do 請求的格式相同,但返回系統中註冊的每個裝置或引腳。

SET 命令格式

DPLL_CMD_DEVICE_SET - 要定位 dpll 裝置,使用者提供 DPLL_A_ID,它是系統中 dpll 裝置的唯一識別符號,以及配置的引數 (DPLL_A_MODE)。

通常情況下,可以一次配置多個引數,但內部每個引數更改都會單獨呼叫,且配置順序不以任何方式保證。

配置預定義列舉

enum dpll_mode

dpll可以支援的工作模式,區分 dpll 如何選擇其輸入之一以與之同步,DPLL_A_MODE 屬性的有效值

常量

DPLL_MODE_MANUAL

輸入只能透過向 dpll 傳送請求來選擇

DPLL_MODE_AUTOMATIC

最高優先順序的輸入引腳由 dpll 自動選擇

enum dpll_lock_status

提供 dpll 裝置鎖定狀態的資訊,DPLL_A_LOCK_STATUS 屬性的有效值

常量

DPLL_LOCK_STATUS_UNLOCKED

dpll 尚未鎖定到任何有效輸入(或透過將 DPLL_A_MODE 設定為 DPLL_MODE_DETACHED 強制執行)

DPLL_LOCK_STATUS_LOCKED

dpll 已鎖定到有效訊號,但沒有保持可用

DPLL_LOCK_STATUS_LOCKED_HO_ACQ

dpll 已鎖定並且已獲取保持

DPLL_LOCK_STATUS_HOLDOVER

dpll 處於保持狀態 - 丟失了有效的鎖定或透過斷開所有引腳強制執行(只有當 dpll 鎖定狀態已經是 DPLL_LOCK_STATUS_LOCKED_HO_ACQ 時才有可能,如果 dpll 鎖定狀態不是 DPLL_LOCK_STATUS_LOCKED_HO_ACQ,則 dpll 的鎖定狀態應保持 DPLL_LOCK_STATUS_UNLOCKED)

enum dpll_lock_status_error

如果之前的狀態更改是由於故障引起的,則提供 dpll 裝置鎖定狀態錯誤的資訊。DPLL_A_LOCK_STATUS_ERROR 屬性的有效值

常量

DPLL_LOCK_STATUS_ERROR_NONE

dpll 裝置鎖定狀態已更改,沒有任何錯誤

DPLL_LOCK_STATUS_ERROR_UNDEFINED

dpll 裝置鎖定狀態由於未定義的錯誤而更改。如果驅動程式無法獲得合適的精確錯誤型別,則驅動程式會填充此值。

DPLL_LOCK_STATUS_ERROR_MEDIA_DOWN

dpll 裝置鎖定狀態已更改,因為關聯的媒體已關閉。例如,如果 dpll 裝置之前已鎖定在 PIN_TYPE_SYNCE_ETH_PORT 型別的輸入引腳上,則可能會發生這種情況。

DPLL_LOCK_STATUS_ERROR_FRACTIONAL_FREQUENCY_OFFSET_TOO_HIGH

媒體上的 RX 和 TX 符號速率之間的 FFO(分數頻率偏移)太高。例如,如果 dpll 裝置之前已鎖定在 PIN_TYPE_SYNCE_ETH_PORT 型別的輸入引腳上,則可能會發生這種情況。

enum dpll_type

dpll的型別,DPLL_A_TYPE 屬性的有效值

常量

DPLL_TYPE_PPS

dpll 產生每秒脈衝訊號

DPLL_TYPE_EEC

dpll 驅動乙太網裝置時鐘

enum dpll_pin_type

定義引腳的可能型別,DPLL_A_PIN_TYPE 屬性的有效值

常量

DPLL_PIN_TYPE_MUX

聚合另一層可選擇的引腳

DPLL_PIN_TYPE_EXT

外部輸入

DPLL_PIN_TYPE_SYNCE_ETH_PORT

乙太網埠 PHY 的恢復時鐘

DPLL_PIN_TYPE_INT_OSCILLATOR

裝置內部振盪器

DPLL_PIN_TYPE_GNSS

GNSS 恢復時鐘

enum dpll_pin_direction

定義引腳的可能方向,DPLL_A_PIN_DIRECTION 屬性的有效值

常量

DPLL_PIN_DIRECTION_INPUT

引腳用作訊號的輸入

DPLL_PIN_DIRECTION_OUTPUT

引腳用於輸出訊號

enum dpll_pin_state

定義引腳的可能狀態,DPLL_A_PIN_STATE 屬性的有效值

常量

DPLL_PIN_STATE_CONNECTED

引腳已連線,鎖相環的活動輸入

DPLL_PIN_STATE_DISCONNECTED

引腳已斷開,不被視為有效輸入

DPLL_PIN_STATE_SELECTABLE

啟用引腳以進行自動輸入選擇

enum dpll_pin_capabilities

定義引腳的可能功能,DPLL_A_PIN_CAPABILITIES 屬性的有效標誌

常量

DPLL_PIN_CAPABILITIES_DIRECTION_CAN_CHANGE

可以更改引腳方向

DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE

可以更改引腳優先順序

DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE

可以更改引腳狀態

通知

dpll 裝置可以提供有關裝置狀態更改的通知,即鎖定狀態更改、輸入/輸出更改或其他警報。有一個多播組用於透過 netlink 套接字通知使用者空間應用程式:DPLL_MCGRP_MONITOR

通知訊息

DPLL_CMD_DEVICE_CREATE_NTF

dpll 裝置已建立

DPLL_CMD_DEVICE_DELETE_NTF

dpll 裝置已刪除

DPLL_CMD_DEVICE_CHANGE_NTF

dpll 裝置已更改

DPLL_CMD_PIN_CREATE_NTF

dpll 引腳已建立

DPLL_CMD_PIN_DELETE_NTF

dpll 引腳已刪除

DPLL_CMD_PIN_CHANGE_NTF

dpll 引腳已更改

事件格式與相應的 get 命令相同。DPLL_CMD_DEVICE_ 事件的格式與 DPLL_CMD_DEVICE_GET 的響應相同。DPLL_CMD_PIN_ 事件的格式與 DPLL_CMD_PIN_GET 的響應相同。

裝置驅動程式實現

裝置由 dpll_device_get() 呼叫分配。使用相同引數的第二次呼叫不會建立新物件,而是為給定引數提供指向先前建立的裝置的指標,它還會增加該物件的引用計數。裝置由 dpll_device_put() 呼叫釋放,該呼叫首先減少引用計數,一旦引用計數清除,該物件將被銷燬。

裝置應實現一組操作並透過 dpll_device_register() 註冊裝置,此時它對使用者可用。多個驅動程式例項可以使用 dpll_device_get() 獲取對其的引用,以及使用其自己的 ops 和 priv 註冊 dpll 裝置。

引腳使用 dpll_pin_get() 單獨分配,它的工作方式類似於 dpll_device_get()。函式首先建立物件,然後對於使用相同引數的每個呼叫,只有物件引用計數增加。此外,dpll_pin_put() 的工作方式類似於 dpll_device_put()。

引腳可以根據硬體需求註冊到父 dpll 裝置或父引腳。每個註冊都需要註冊者提供一組引腳回撥,以及用於呼叫它們的資料私有指標

  • dpll_pin_register() - 將引腳註冊到 dpll 裝置,

  • dpll_pin_on_pin_register() - 將引腳註冊到另一個 MUX 型別引腳。

新增或刪除 dpll 裝置的通知在子系統本身內建立。關於註冊/取消註冊引腳的通知也由子系統呼叫。關於 dpll 裝置或引腳的狀態更改的通知以兩種方式呼叫

  • 在成功請求 dpll 子系統更改後,子系統呼叫相應的通知,

  • 由裝置驅動程式使用 dpll_device_change_ntf() 或 dpll_pin_change_ntf() 請求,當驅動程式通知狀態更改時。

使用 dpll 介面的裝置驅動程式不需要實現所有回撥操作。但是,有一些需要實現。所需的 dpll 裝置級別回撥操作

  • .mode_get,

  • .lock_status_get.

所需的引腳級別回撥操作

  • .state_on_dpll_get(註冊到 dpll 裝置的引腳),

  • .state_on_pin_get(註冊到父引腳的引腳),

  • .direction_get.

檢查每個其他操作處理程式是否存在,如果缺少特定處理程式,則返回 -EOPNOTSUPP

最簡單的實現是在 OCP TimeCard 驅動程式中。ops 結構定義如下

static const struct dpll_device_ops dpll_ops = {
        .lock_status_get = ptp_ocp_dpll_lock_status_get,
        .mode_get = ptp_ocp_dpll_mode_get,
        .mode_supported = ptp_ocp_dpll_mode_supported,
};

static const struct dpll_pin_ops dpll_pins_ops = {
        .frequency_get = ptp_ocp_dpll_frequency_get,
        .frequency_set = ptp_ocp_dpll_frequency_set,
        .direction_get = ptp_ocp_dpll_direction_get,
        .direction_set = ptp_ocp_dpll_direction_set,
        .state_on_dpll_get = ptp_ocp_dpll_state_get,
};

然後註冊部分看起來像這樣

clkid = pci_get_dsn(pdev);
bp->dpll = dpll_device_get(clkid, 0, THIS_MODULE);
if (IS_ERR(bp->dpll)) {
        err = PTR_ERR(bp->dpll);
        dev_err(&pdev->dev, "dpll_device_alloc failed\n");
        goto out;
}

err = dpll_device_register(bp->dpll, DPLL_TYPE_PPS, &dpll_ops, bp);
if (err)
        goto out;

for (i = 0; i < OCP_SMA_NUM; i++) {
        bp->sma[i].dpll_pin = dpll_pin_get(clkid, i, THIS_MODULE, &bp->sma[i].dpll_prop);
        if (IS_ERR(bp->sma[i].dpll_pin)) {
                err = PTR_ERR(bp->dpll);
                goto out_dpll;
        }

        err = dpll_pin_register(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops,
                                &bp->sma[i]);
        if (err) {
                dpll_pin_put(bp->sma[i].dpll_pin);
                goto out_dpll;
        }
}

在錯誤路徑中,我們必須以相反的順序倒退每個分配

while (i) {
        --i;
        dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, &bp->sma[i]);
        dpll_pin_put(bp->sma[i].dpll_pin);
}
dpll_device_put(bp->dpll);

更復雜的示例可以在 Intel 的 ICE 驅動程式或 nVidia 的 mlx5 驅動程式中找到。

SyncE 啟用

對於 SyncE 啟用,需要允許軟體應用程式控制 dpll 裝置,該應用程式監視和配置 dpll 裝置的輸入,以響應 dpll 裝置及其輸入的當前狀態。在這種情況下,dpll 裝置輸入訊號也應可配置為使用從 PHY netdevice 恢復的訊號來驅動 dpll。這透過將引腳暴露給 netdevice 來完成 - 使用 dpll_netdev_pin_set(struct net_device *dev, struct dpll_pin *dpll_pin) 將引腳附加到 netdevice 本身。暴露的引腳 id 控制代碼 DPLL_A_PIN_ID 然後可以被使用者識別,因為它附加到 rtnetlink 以響應巢狀屬性 IFLA_DPLL_PIN 中的 get RTM_NEWLINK 命令。