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 的支持应该已经准备就绪。