Samsung USB 2.0 PHY 適配層

1. 描述

Samsung SoC 中 USB 2.0 PHY 模組的架構在許多 SoC 中相似。 儘管存在相似之處,但事實證明很難建立一個適合所有這些 PHY 控制器的驅動程式。 通常,差異很小,並且可以在 PHY 暫存器的特定位中找到。 在極少數情況下,必須更改暫存器寫入的順序或 PHY 上電過程。 此適配層是在擁有單獨的驅動程式和擁有一個驅動程式並新增對許多特殊情況的支援之間的一種折衷方案。

2. 檔案描述

  • phy-samsung-usb2.c

    這是適配層的主要檔案。 此檔案包含 probe 函式,併為通用 PHY 框架提供兩個回撥。 這兩個回撥用於開啟和關閉 phy 的電源。 它們執行所有版本的 PHY 模組都必須完成的常見工作。 根據選擇的 SoC,它們執行 SoC 特定的回撥。 透過選擇適當的相容字串來選擇特定的 SoC 版本。 此外,此檔案包含特定 SoC 的 struct of_device_id 定義。

  • phy-samsung-usb2.h

    這是包含檔案。 它聲明瞭此驅動程式使用的結構。 此外,它應包含描述特定 SoC 的結構的外部宣告。

3. 支援的 SoC

要支援新的 SoC,應將新檔案新增到 drivers/phy 目錄。 每個 SoC 的配置都儲存在 struct samsung_usb2_phy_config 的一個例項中

struct samsung_usb2_phy_config {
      const struct samsung_usb2_common_phy *phys;
      int (*rate_to_clk)(unsigned long, u32 *);
      unsigned int num_phys;
      bool has_mode_switch;
};

num_phys 是驅動程式處理的物理裝置的數量。 *phys 是一個數組,其中包含每個物理裝置的配置。 has_mode_switch 屬性是一個布林標誌,用於確定 SoC 是否在單個引腳對上具有 USB 主機和裝置。 如果是這樣,則必須修改一個特殊的暫存器以更改這些引腳在 USB 裝置或主機模組之間的內部路由。

例如,Exynos 4210 的配置如下

const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
      .has_mode_switch        = 0,
      .num_phys               = EXYNOS4210_NUM_PHYS,
      .phys                   = exynos4210_phys,
      .rate_to_clk            = exynos4210_rate_to_clk,
}
  • int (*rate_to_clk)(unsigned long, u32 *)

    rate_to_clk 回撥用於將用作 PHY 模組參考時鐘的時鐘速率轉換為應寫入硬體暫存器的值。

exynos4210_phys 配置陣列如下

static const struct samsung_usb2_common_phy exynos4210_phys[] = {
      {
              .label          = "device",
              .id             = EXYNOS4210_DEVICE,
              .power_on       = exynos4210_power_on,
              .power_off      = exynos4210_power_off,
      },
      {
              .label          = "host",
              .id             = EXYNOS4210_HOST,
              .power_on       = exynos4210_power_on,
              .power_off      = exynos4210_power_off,
      },
      {
              .label          = "hsic0",
              .id             = EXYNOS4210_HSIC0,
              .power_on       = exynos4210_power_on,
              .power_off      = exynos4210_power_off,
      },
      {
              .label          = "hsic1",
              .id             = EXYNOS4210_HSIC1,
              .power_on       = exynos4210_power_on,
              .power_off      = exynos4210_power_off,
      },
      {},
};
  • int (*power_on)(struct samsung_usb2_phy_instance *); int (*power_off)(struct samsung_usb2_phy_instance *);

    這兩個回撥用於透過修改適當的暫存器來開啟和關閉 phy 的電源。

對驅動程式的最終更改是將適當的相容值新增到 phy-samsung-usb2.c 檔案。 就 Exynos 4210 而言,以下行已新增到 struct of_device_id samsung_usb2_phy_of_match[] 陣列

#ifdef CONFIG_PHY_EXYNOS4210_USB2
      {
              .compatible = "samsung,exynos4210-usb2-phy",
              .data = &exynos4210_usb2_phy_config,
      },
#endif

為了進一步提高驅動程式的靈活性,Kconfig 檔案能夠包含對已編譯驅動程式中選定的 SoC 的支援。 Exynos 4210 的 Kconfig 條目如下

config PHY_EXYNOS4210_USB2
      bool "Support for Exynos 4210"
      depends on PHY_SAMSUNG_USB2
      depends on CPU_EXYNOS4210
      help
        Enable USB PHY support for Exynos 4210. This option requires that
        Samsung USB 2.0 PHY driver is enabled and means that support for this
        particular SoC is compiled in the driver. In case of Exynos 4210 four
        phys are available - device, host, HSCI0 and HSCI1.

支援新 SoC 的新建立的檔案也必須新增到 Makefile。 就 Exynos 4210 而言,新增的行如下

obj-$(CONFIG_PHY_EXYNOS4210_USB2)       += phy-exynos4210-usb2.o

完成這些步驟後,對新 SoC 的支援應該已經準備就緒。