API GUIDE / USB_PHY / API OVERVIEW

USB PHY API 概述

1. 文档范围

本文档定义 G32S10 SDK USB2.0 PHY 驱动模块的正式接口,覆盖以下三层:

  • HAL USB PHY:面向 USB 控制器、BSP 和平台适配代码的 PHY 功能接口
  • HAL CPM USB:USB PHY 所需的 CPM 侧时钟、复位、IDDIG 和参数控制接口
  • LL USB PHY:面向 USB PHY 寄存器与位操作的低层内联接口

本文档只描述 code/drivers/g32s10/usb_phy/ 及其公开头文件。DWC2 控制器、USB Host/Gadget Core、复合设备和主机类驱动属于 USB Middleware,参见 USB MID 正式接口文档。

本文档以当前 SDK 源码为准,主要核对文件如下:

类型 路径
USB PHY HAL 公开接口 code/include/drivers/g32s10/usb_phy/hal_usb_phy.h
CPM USB HAL 公开接口 code/include/drivers/g32s10/usb_phy/hal_cpm_usb.h
USB PHY LL 公开接口 code/include/drivers/g32s10/usb_phy/ll_usb_phy.h
USB PHY 寄存器定义 code/include/drivers/g32s10/usb_phy/usb_phy_regs.h
USB PHY HAL 实现 code/drivers/g32s10/usb_phy/hal_usb_phy.c
CPM USB HAL 实现 code/drivers/g32s10/usb_phy/hal_cpm_usb.c
CPM 寄存器依赖 code/include/drivers/g32s10/cpm/cpm_regs.h、ll_cpm.h、ll_pm.h
MID 平台 code/include/usb/platform.h、code/middleware/usb/platform.c
构建配置 code/drivers/g32s10/usb_phy/Kconfig、CMakeLists.txt

补充说明:

  • USB PHY HAL/LL 没有软件句柄、实例表或运行状态机。除模式枚举和供电类型枚举外,接口直接访问固定的 USB PHY/CPM 寄存器。
  • PHY HAL 中多数接口沿用旧平台回调签名,返回 void 或 int,失败通常只记录日志,不能通过返回值完整判断。
  • CPM USB HAL 的读接口会检查空指针;其余接口当前不做硬件结果确认。
  • LL 接口均为 static inline,只完成寄存器读写,不校验参数、不处理时序、不维护软件状态。
  • 当文档描述与头文件注释、实现或 Kconfig 不一致时,以当前源码行为为准。

2. 分层关系与依赖

2.1 模块分层

            Application / BSP
                   |
       +------------------------+
       |                        |
       v                        v
 USB MID                  USB PHY HAL
 DWC2 Host/Gadget          hal_usb_phy_*
       |                        |
       +-----------+------------+
                   |
                   v
             CPM USB HAL
             hal_cpm_usb_phy_*
                   |
          +--------+---------+
          |                  |
          v                  v
     USB PHY LL          CPM LL
     ll_usb_phy_*        ll_cpm_* / ll_pm_*
          |                  |
          v                  v
   USB_PHY registers    CPM registers
   0x1001_2000          0x1000_0000

2.2 职责边界

HAL USB PHY 负责:

  • PHY POR、UTMI reset、强制/释放 suspend 的时序编排
  • 根据编译期 USB 角色设置默认 IDDIG 模式
  • PHY 的 RX squelch、IO high-Z、legacy detect 和 VBUS detect 掉电配置
  • BC1.1 数据接触、Primary 和 Secondary 检测流程
  • USB PHY/CPM 相关寄存器调试输出

HAL CPM USB 负责:

  • USB PHY 24 MHz 参考时钟门控
  • CPM OPCR.SPENDN0 强制挂起控制
  • CPM USBPCR/USBPCR1 原始寄存器写入
  • CPM USBRDT 中 UTMI reset 与 IDDIG override

LL USB PHY 负责:

  • PHY 字段和单 bit 的读改写或整寄存器写入
  • 为 HAL 或高级诊断代码提供最小寄存器操作入口

USB PHY 模块不负责:

  • DWC2 控制器初始化、端点、Host/Gadget Core、枚举或中断
  • OTG 控制器时钟门 HAL_CPM_ID_GATE_OTG 的完整生命周期
  • USB VBUS 电源开关 GPIO
  • GPIO 引脚复用或产品级模拟电路设计
  • 将 PHY 状态机暴露给应用;当前模块没有状态查询句柄

2.3 编译与运行前置条件

项目 责任方 说明
CONFIG_DRIVER_USB_PHY=y 应用配置 编译 hal_usb_phy.c 和 hal_cpm_usb.c。
CONFIG_DRIVER_CPM=y 应用/BSP PHY HAL 使用 CPM 寄存器和 CPM LL 辅助函数。当前 DRIVER_USB_PHY 没有声明 depends on DRIVER_CPM 或 select DRIVER_CPM,应由集成配置显式保证;现有 USB PHY 示例均启用该选项。
OTG 控制器时钟门 MID/BSP hal_usb_phy_init() 不使能 HAL_CPM_ID_GATE_OTG。通过 MID 使用时由 dwc2_lowlevel_hw_enable() 使能。
USB PHY 参考时钟 PHY HAL/调用方 hal_usb_phy_reset() 会 ungated USBPHY_CLK;直接调用 hal_usb_phy_init() 前应由平台确保参考时钟可访问。
CONFIG_USB_DISABLE_VBUS 应用配置 定义时初始化/挂起流程会关闭 VBUS detect;恢复流程不会重新打开。
CONFIG_USB_DUAL_ROLE / CONFIG_USB_PERIPHERAL USB 角色配置 影响 hal_usb_phy_init() 的默认 IDDIG 选择;二者任一存在时默认 Peripheral。
CONFIG_SDK_ENABLE_DEBUG_HELPERS 应用配置 与 CONFIG_USB_PHY_ENABLE_DEBUG_HELPERS 同时开启时才编入寄存器 dump 内容。
时间基准 OS/HAL hal_usb_phy_init()、hal_usb_phy_reset() 和供电检测使用阻塞微秒/毫秒延时。
互斥 调用方 模块内部没有锁;PHY/CPM 访问必须与 USB Core 和其他 PHY 用户串行化。

3. 构建配置与资源模型

3.1 Kconfig 与源文件

选项 默认值 说明
DRIVER_USB_PHY n USB PHY 驱动模块总开关。
USB_PHY_ENABLE_DEBUG_HELPERS n 在总调试开关已开启时,构建 PHY/CPM 寄存器 dump 函数体。

构建关系:

if(CONFIG_DRIVER_USB_PHY)
    imde_component_register(NAME g32s10_usb_phy SRCS
        hal_cpm_usb.c
        hal_usb_phy.c)
endif()

DRIVER_USB_PHY 当前只控制本模块两个源文件的编译,不自动选择 CPM 驱动。DRIVER_CPM 默认值为 y,但该依赖关系没有在 Kconfig 中强制;裁剪配置时应同时确认 CPM 驱动未被关闭。

文件职责:

文件 说明
hal_usb_phy.h / .c PHY 功能接口和初始化、挂起、BC1.1 流程
hal_cpm_usb.h / .c CPM 侧辅助接口
ll_usb_phy.h PHY 寄存器内联访问接口
usb_phy_regs.h PHY 寄存器布局、字段 mask、默认值和编码
code/include/usb/platform.h struct usb_phy_ops,定义 MID 与 PHY 的适配契约

3.2 寄存器实例与基地址

实例 基地址宏 基地址 访问形式
USB PHY USB_PHY_IOBASE 0x10012000 USB_PHY,类型为 usb_phy_regs_t *
CPM CPM_IOBASE 0x10000000 CPM,由 CPM 模块提供
DWC2 OTG OTG_IOBASE 0x13500000 USB MID 私有,不属于 USB PHY 模块

USB PHY 当前只有一个固定实例:

#define USB_PHY ((usb_phy_regs_t *)(uintptr_t)USB_PHY_IOBASE)

USB PHY HAL/LL 不接收实例号,因此不能在单次构建中同时控制多个独立 USB PHY 基地址。

3.3 公开类型

3.3.1 PHY 模式

enum otg_phy_mode {
    OTG_PHY_MODE_PERIPHERAL = 0,
    OTG_PHY_MODE_HOST,
    OTG_PHY_MODE_OTG,
};
枚举 值 PHY 侧行为
OTG_PHY_MODE_PERIPHERAL 0 强制 CPM USBRDT.IDDIG 为 mini-B 电平。
OTG_PHY_MODE_HOST 1 强制 CPM USBRDT.IDDIG 为 mini-A 电平。
OTG_PHY_MODE_OTG 2 关闭 IDDIG software override,改用 PHY 提供的 IDDIG。

注意:hal_usb_phy_set_mode() 只改变 CPM IDDIG 控制,不直接写 DWC2 控制器模式位。

3.3.2 供电类型

enum usb_device_power {
    USB_POWER_UNKNOWN = 0,
    USB_POWER_SDP,
    USB_POWER_DCP,
    USB_POWER_CDP,
};
枚举 含义
USB_POWER_UNKNOWN 未检测到有效 data contact,或无法归类。
USB_POWER_SDP Standard Downstream Port。
USB_POWER_DCP Dedicated Charging Port。
USB_POWER_CDP Charging Downstream Port。

3.3.3 PHY 寄存器块

typedef struct usb_phy_regs usb_phy_regs_t;

每次寄存器访问均为 32 bit。有效成员从 0x030 开始,跨度到 0x1CC;中间保留地址不能作为普通 RAM 使用。

3.3.4 MID 回调契约

hal_usb_phy.h 通过 struct usb_phy_ops 与 USB MID 对接:

struct usb_phy_ops {
    void (*phy_init)(void);
    void (*phy_reset)(void);
    void (*phy_suspend)(int suspend, int wakeup);
    int (*phy_resume_status)(void);
    enum usb_device_power (*phy_power_detect)(void);
};

code/middleware/usb/platform.c 将五个函数分别绑定到:

phy_init          -> hal_usb_phy_init
phy_reset         -> hal_usb_phy_reset
phy_suspend       -> hal_usb_phy_suspend
phy_resume_status -> hal_usb_phy_resume_status
phy_power_detect  -> hal_usb_phy_power_detect

3.4 返回值约定

接口类别 返回类型 成功/失败语义
PHY 生命周期 void 内部失败写 error log 后返回;调用方无法从返回值判断。
hal_usb_phy_set_mode int 0 成功,-1 失败;不是 Linux errno。
hal_usb_phy_resume_status int 当前实现恒为 0。
BC1.1 检测 enum usb_device_power 返回检测结果;没有独立失败码。
CPM 辅助 error_code_t 当前除读接口空指针外基本恒为 IM_OK。
Debug dump error_code_t IM_OK 或 IM_ERR_UNSUPPORTED。
LL void / uint32_t / bool 不返回状态码。

3.5 调用上下文与并发约束

  • hal_usb_phy_init()、hal_usb_phy_reset()、hal_usb_phy_suspend()、hal_usb_phy_power_detect() 和寄存器 dump 均包含阻塞访问或延时,不得在中断上下文调用。
  • hal_usb_phy_set_mode() 会改变角色输入,不得在 USB 链路正在传输或角色切换未完成时调用。
  • LL 接口本身不阻塞,但底层 MMIO 访问要求 PHY/CPM 时钟已开启。
  • 所有接口都没有内部互斥。应用不得在 MID 初始化、挂起/恢复或枚举期间并发修改同一寄存器。
  • hal_usb_phy_power_detect() 会临时覆盖 PLL_CONTROL1 的 BC detect 编程值;调用期间必须独占 PHY。

4. 推荐调用顺序

4.1 USB MID 内部调用关系

USB MID 使用平台胶水自动调用 PHY HAL。正常应用应通过 USB MID 正式接口文档 的 usb_core_* 生命周期控制,不应重复初始化 PHY。

初始化时的实际调用关系:

usb_core_init()
  -> dwc2_driver_probe()
       -> dwc2_lowlevel_hw_init()
            -> usb_phy_ops.phy_reset()
               -> hal_usb_phy_reset()
       -> dwc2_lowlevel_hw_enable()
            -> hal_cpm_clk_enable(HAL_CPM_ID_GATE_OTG)
            -> usb_phy_ops.phy_init()
               -> hal_usb_phy_init()
       -> DWC2 core reset / hwparam / role 配置

固定 Peripheral 模式下,probe 结束前会再次调用 dwc2_lowlevel_hw_disable() 关闭 OTG 时钟门;Gadget 驱动注册时 dwc2_lowlevel_hw_enable() 会重新开启门控并再次调用 hal_usb_phy_init()。因此一次固件生命周期内 hal_usb_phy_init() 可能执行不止一次。

挂起/恢复:

usb_core_suspend()
  -> dwc2_suspend()
       -> hal_usb_phy_suspend(1, wakeup_hint)
       -> __dwc2_lowlevel_hw_disable()
          -> hal_cpm_clk_disable(HAL_CPM_ID_GATE_OTG)

usb_core_resume()
  -> dwc2_resume()
       -> 若 dwc2->phy_off_for_suspend && dwc2->ll_hw_enabled:
          __dwc2_lowlevel_hw_enable()
            -> hal_cpm_clk_enable(HAL_CPM_ID_GATE_OTG)
            -> hal_usb_phy_init()
       -> hal_usb_phy_suspend(0, wakeup_hint)

Device 且 Gadget enabled 时 MID 传入 wakeup=1;其他情况传入 0。PHY HAL 当前忽略该参数。

4.2 直接使用 PHY HAL

只有 BSP、板级诊断或自带 USB 控制器的集成代码需要直接调用。下面示例只初始化 PHY,不初始化 DWC2:

#include "hal_usb_phy.h"

void board_usb_phy_start(void)
{
    /* 若平台另行管理 OTG 控制器门控,应先使能对应门控。 */
    hal_usb_phy_reset();
    hal_usb_phy_init();
    /* hal_usb_phy_init() 已按编译期角色应用默认 IDDIG 模式。 */
}

说明:

  • hal_usb_phy_reset() 会 ungated PHY refclk 并等待 550 ms。
  • hal_usb_phy_init() 执行 POR/UTMI reset 和基础 PHY 配置。
  • 直接调用仍须完成 OTG 控制器门控、DWC2 core reset、IRQ 和时钟资源配置。

4.3 挂起与恢复

/* 进入低功耗前先停止 USB Core 业务和控制器访问。 */
hal_usb_phy_suspend(1, 0);

/* 恢复参考时钟和控制资源后,再退出 PHY suspend。 */
hal_usb_phy_suspend(0, 0);

若由 MID 管理,应调用 usb_core_suspend() / usb_core_resume(),不要同时直接调用 hal_usb_phy_suspend(),否则会绕过 OTG 门控和中间层状态。

4.4 模式切换

int ret;

ret = hal_usb_phy_set_mode(OTG_PHY_MODE_HOST);
if (ret != 0) {
    /* mode 非法或 CPM 配置失败。 */
}

模式切换后仍需由 DWC2/MID 等待实际控制器进入目标模式。该接口不保证主机端口使能、Gadget pull-up 或 VBUS 已配置。

4.5 BC1.1 供电类型检测

enum usb_device_power power;

power = hal_usb_phy_power_detect();
switch (power) {
case USB_POWER_SDP:
    break;
case USB_POWER_DCP:
    break;
case USB_POWER_CDP:
    break;
case USB_POWER_UNKNOWN:
default:
    break;
}

该接口最大阻塞 60 ms。应用不应在 USB 中断或高优先级实时路径中调用。

5. 行为约束与已知限制

5.1 返回值与失败处理

  • hal_usb_phy_init()、hal_usb_phy_reset() 和 hal_usb_phy_suspend() 返回 void,失败仅写日志。
  • 因此 MID 的 void (*phy_init)(void) / void (*phy_reset)(void) 契约无法把 PHY 失败转换为 USB Core probe 错误。
  • hal_usb_phy_set_mode() 只返回 0 或 -1,不会返回 IM_ERR_* 或负 errno。
  • hal_cpm_usb_phy_*() 虽然返回 error_code_t,但除输出参数为空外,当前实现基本没有硬件失败检测路径。
  • 需要可靠诊断时,应在调用前后使用 hal_usb_phy_regs_dump()、hal_cpm_usb_phy_regs_dump() 或 LL 读接口核对关键状态。

5.2 全寄存器写风险

以下接口会覆盖整寄存器:

  • hal_cpm_usb_phy_write_parameter_control()
  • hal_cpm_usb_phy_write_parameter_control1()
  • ll_usb_phy_write_bc_detect_control()

不能把字段默认值直接传给整寄存器写接口。例如 USB_PHY_TX_HS_PRE_EMPHASIS_STRENGTH_DEFAULT 是 [5:3] 字段值,不是完整寄存器值;带 _REG_DEFAULT 后缀的宏才是整寄存器默认值。

若只需修改单个 bit,应优先使用 CPM/PHY LL 的 set/clear 接口或 im_mmio_* 字段操作。

5.3 复位语义

当前存在三个不同层次的复位:

操作 接口 作用
PHY 参考路径稳定 hal_usb_phy_reset() ungated refclk、释放强制 suspend、等待 550 ms。
PHY POR + UTMI reset hal_usb_phy_init() 写 CPM USBPCR POR 和 USBRDT UTMI_RST。
DWC2 core reset USB MID dwc2_core_reset() 复位 DWC2 控制器,不是 USB PHY HAL 接口。

不能只调用 hal_usb_phy_reset() 就认为 PHY 已执行完整重初始化。

5.4 挂起与恢复语义

  • hal_usb_phy_suspend() 的 wakeup 参数当前被忽略。
  • hal_usb_phy_resume_status() 当前恒为 0,不能作为实际 resume 完成判定。
  • MID resume 的正常顺序是恢复 OTG 时钟门并执行 hal_usb_phy_init(),随后调用 hal_usb_phy_suspend(0, ...) 释放 PHY suspend。直接使用 HAL 时也必须先恢复时钟资源,再释放 PHY suspend。
  • 定义 CONFIG_USB_DISABLE_VBUS 时,初始化会使 VBUS detect 保持掉电;应用必须通过外部 VBUS 检测电路驱动状态更新。
  • PHY suspend 与 DWC2 USB bus suspend 不是同一层次;USB GINTSTS.USBSUSP/WKUPINT 由 MID 处理。

5.5 寄存器修改时机

  • POR、UTMI reset 和 refclk gate 只能在 USB 控制器停止访问 PHY 时修改。
  • IDDIG 模式切换不能在传输中执行。
  • BC1.1 检测不能在正常数据枚举期间并发运行。
  • IO high-Z、DP/DM pull-down、receiver driver 和 VBUS detect 设置可能直接改变 USB 连接状态,仅应由 PHY 初始化/电源路径修改。
  • PHY 模拟调优字段没有 HAL 配置接口;需要修改时必须依据芯片 SPEC、板级 SI 测试和 USB 合规测试结果。

5.6 调试输出开关

两个 dump 函数只有同时满足以下条件才访问寄存器并输出日志:

CONFIG_SDK_ENABLE_DEBUG_HELPERS != 0
CONFIG_USB_PHY_ENABLE_DEBUG_HELPERS != 0

否则函数仍然存在并返回 IM_ERR_UNSUPPORTED。头文件在配置宏未定义时将其默认置 0。

6. 示例程序

6.1 通过 USB MID 使用

这是应用和常规 BSP 的推荐方式:

#include <usb/usb.h>

int board_usb_start(void)
{
    int ret = usb_core_init();

    if (ret != 0) {
        return ret;
    }

    /* 后续按 USB MID 文档初始化 Gadget 或 Host 类驱动。 */
    return 0;
}

PHY 的 hal_usb_phy_reset() 和 hal_usb_phy_init() 由 USB MID 平台胶水调用。

6.2 直接初始化 PHY

#include "hal_usb_phy.h"

int usb_phy_direct_init(enum otg_phy_mode mode)
{
    /* 若使用 MID 管理 OTG 门控,不要重复执行本函数。 */
    hal_usb_phy_reset();
    hal_usb_phy_init();

    if (hal_usb_phy_set_mode(mode) != 0) {
        return -1;
    }

    return 0;
}

本示例不会初始化 DWC2、USB IRQ、端点、Host 根 Hub 或 Gadget。

6.3 挂起与恢复

void usb_phy_power_down(void)
{
    hal_usb_phy_suspend(1, 0);
}

void usb_phy_power_up(void)
{
    /*
     * 实际系统中应先恢复 CPM/OTG 控制器时钟和其他 PHY 资源。
     * 通过 MID 管理时使用 usb_core_resume()。
     */
    hal_usb_phy_suspend(0, 0);
}

6.4 检测充电器类型

#include "hal_usb_phy.h"

enum usb_device_power detect_port_power(void)
{
    return hal_usb_phy_power_detect();
}

调用前应确认设备已连接、PHY 已上电,且当前没有其他代码修改 PLL_CONTROL1。

6.5 LL 级 BC1.1 流程

以下示例展示与 HAL 相同的检测步骤,不包含完整的平台错误恢复:

#include "im_time.h"
#include "ll_usb_phy.h"
#include <usb/ch9.h>

enum usb_device_power ll_bc_detect(void)
{
    enum usb_device_power type = USB_POWER_UNKNOWN;

    ll_usb_phy_write_bc_detect_control(
        USB_PHY, USB_PHY_BC_DETECT_CONTROL_DATA_CONTACT);
    im_time_delay_ms(20U);

    if (!ll_usb_phy_bc_data_contact_detected(USB_PHY)) {
        goto idle;
    }

    ll_usb_phy_write_bc_detect_control(
        USB_PHY, USB_PHY_BC_DETECT_CONTROL_PRIMARY);
    im_time_delay_ms(20U);

    if (!ll_usb_phy_bc_primary_detected(USB_PHY)) {
        type = USB_POWER_SDP;
        goto idle;
    }

    ll_usb_phy_write_bc_detect_control(
        USB_PHY, USB_PHY_BC_DETECT_CONTROL_SECONDARY);
    im_time_delay_ms(20U);

    type = ll_usb_phy_bc_secondary_detected(USB_PHY) ?
           USB_POWER_DCP : USB_POWER_CDP;

idle:
    ll_usb_phy_write_bc_detect_control(
        USB_PHY, USB_PHY_BC_DETECT_CONTROL_IDLE);
    return type;
}

注意:USB_POWER_* 枚举定义在 USB 标准头文件中;该示例仅用于说明 PHY 寄存器流程。

7. 错误码与返回值

返回值 出现接口 说明
IM_OK CPM 读/写、dump 操作完成。
IM_ERR_INVALID_PARAM CPM 读接口 输出指针为 NULL。
IM_ERR_UNSUPPORTED PHY/CPM dump Debug helper 未开启。
0 hal_usb_phy_set_mode、hal_usb_phy_resume_status Set mode 成功,或 resume 状态固定值。
-1 hal_usb_phy_set_mode 模式非法或 CPM 操作失败。
USB_POWER_* hal_usb_phy_power_detect 供电类型检测结果。

PHY HAL 不定义私有 error_code_t 扩展,也不返回 Linux errno。需要与 MID 错误体系交互时,由 MID 的平台 glue 决定如何处理。

8. 接口覆盖矩阵

能力 HAL/CPM HAL 接口 LL 接口 寄存器
PHY 完整初始化 hal_usb_phy_init 多处 CPM USBPCR/USBPCR1/USBRDT、PHY 0x064/0x070/0x078/0x108
参考时钟/基础恢复 hal_usb_phy_reset 无 CPM OPCR
PHY suspend hal_usb_phy_suspend receiver/VBUS detect helper CPM OPCR、PHY 0x100/0x108
resume 状态查询 hal_usb_phy_resume_status 无 当前不访问硬件
IDDIG/模式 hal_usb_phy_set_mode、hal_cpm_usb_phy_*iddig 无 CPM USBRDT
充电器检测 hal_usb_phy_power_detect BC detect/status helpers PHY 0x104/0x10C/0x110
PHY 调试 hal_usb_phy_regs_dump 无 22 个已命名 PHY 寄存器
CPM 调试 hal_cpm_usb_phy_regs_dump 无 CPM OPCR/USBPCR/USBRDT/USBPCR1
RX squelch 初始化固定 125 mV get/set_rx_squelch_trigger PHY 0x064
IO high-Z 初始化置位 enable/disable PHY 0x078
Legacy detect 初始化清除 clear PHY 0x070
VBUS detect 掉电 init/suspend/resume enable/disable PHY 0x108
Legacy receiver driver suspend/resume enable/disable PHY 0x100
DP/DM pull-down 无 HAL enable/disable PHY 0x1C0