u-boot代码readl实现程序返回原理(非return返回)

本文探讨了U-Boot中通过不同CPU架构调用readl等函数实现寄存器读取的方法。详细分析了readl函数的实现原理,并通过反汇编手段展示了x86栈调用及返回值的机制。

u-boot代码中对寄存器的读取是根据不同CPU架构调用readl,readb,readw的函数实现的。这几个函数实现很巧妙,利用栈实现了静默返回。这里我想以readl为例,使用反汇编手段了解一下该函数实现机理。readl的原代码如下如示:

#define dmb() __asm__ __volatile__ ("" : : : "memory")
#define __iormb() dmb()
#define __iowmb() dmb()

#define __arch_getl(a) (*(volatile unsigned int *)(a))

#define readl(c) ({ u32 __v = __arch_getl(c); __iormb(); __v; })

以上实现涉及到了下面几方面知识:

1、内存屏障。

2、反汇编

3、x86栈的调用与返回

简单说一下下面几个寄存器的定义,有助于理解反汇编代码:

  • ax(accumulator): 可用于存放函数返回值
  • bp(base pointer): 用于存放执行中的函数对应的栈帧的栈底地址
  • sp(stack poinger): 用于存放执行中的函数对应的栈帧的栈顶地址
  • ip(instruction pointer): 指向当前执行指令的下一条指令
为使用问题简化,我们先使用如下代码,研究代码段返回值原理:
#include "stdio.h"
typedef unsigned int u32;


int  main()
{
u32 rd  =0;
rd =({ u32 __v = 8;u32 __v1 = 9;__v++;__v1;});
printf("rd=%d\n",rd);
return 0;
}
我们使用反汇编指令后,查看主要代码区如下所示:

#include "stdio.h"
typedef unsigned int u32;


int  main()
{
  40156f: 90                   nop


0000000000401570 <main>:
  401570: 55                   push   %rbp
  401571: 48 89 e5             mov    %rsp,%rbp
  401574: 48 83 ec 30          sub    $0x30,%rsp
  401578: e8 d3 01 00 00       callq  401750 <__main>
u32 rd  =0;
  40157d: c7 45 fc 00 00 00 00 movl   $0x0,-0x4(%rbp)//rd放在栈的bp-4位置。
rd =({ u32 __v = 8;u32 __v1 = 9;__v++;__v1;});
  401584: c7 45 f8 08 00 00 00 movl   $0x8,-0x8(%rbp)//__v放在bp-8位置,值为8
  40158b: c7 45 f4 09 00 00 00 movl   $0x9,-0xc(%rbp)//__v1放在bp-c位置,值为9
  401592: 83 45 f8 01          addl   $0x1,-0x8(%rbp)
  401596: 8b 45 f4             mov    -0xc(%rbp),%eax//对__v1进行自增操作,并将结果存回。
  401599: 89 45 fc             mov    %eax,-0x4(%rbp)//使用ax存放处返回值给rd。bp-4位置为rd
printf("rd=%d\n",rd);
  40159c: 8b 45 fc             mov    -0x4(%rbp),%eax
  40159f: 89 c2                mov    %eax,%edx
  4015a1: 48 8d 0d 58 2a 00 00 lea    0x2a58(%rip),%rcx        # 404000 <.rdata>
  4015a8: e8 c3 15 00 00       callq  402b70 <printf>
return 0;
  4015ad: b8 00 00 00 00       mov    $0x0,%eax
}
对于寄存器操作,使用内存屏障,告诉CPU重新从寄存器取值,而不是使用缓存值。
这种实现可以在C语言中实现类似c++中内联函数的效果。


<think>我们正在讨论DWC3控制器在U-Boot中的OTG模式动态切换。根据引用[2]中的设备树配置示例,我们可以看到在RK3399平台上,通过修改设备树将Type-C切换为Micro USB OTG,并设置了`dr_mode = "otg"`。现在,我们需要在U-Boot实现OTG模式的动态切换,即根据ID引脚状态和VBUS存在情况自动切换主机模式(Host)和设备模式(Peripheral)。 动态切换的关键在于: 1. 硬件检测:通过ID引脚和VBUS电压判断当前角色。 2. 软件切换:根据检测结果重新配置DWC3控制器的工作模式。 ### 一、硬件检测机制 DWC3控制器通过以下寄存器提供状态信息: - **`DWC3_OSTS`(操作状态寄存器)**: - `IDDIG`位:ID引脚状态(0表示主机模式,1表示设备模式) - `BSESVALID`:VBUS有效指示(1表示VBUS存在) - **`DWC3_GUSBSTS`(全局USB状态寄存器)**: - `CONNECTED`位:设备连接状态 ### 二、动态切换实现步骤 在OTG模式下,U-Boot需要周期性地检测状态变化(通常在定时器中断或主循环中实现)。以下是核心流程: #### 1. 初始化OTG模式 在驱动探测函数(`xhci_dwc3_probe`)中,根据设备树的`dr_mode = "otg"`,初始化控制器为OTG模式: ```c static int xhci_dwc3_probe(struct udevice *dev) { // ... 其他初始化代码 /* 设置为OTG模式 */ reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg &= ~DWC3_GCTL_PRTCAPDIR_MASK; reg |= DWC3_GCTL_PRTCAPDIR_OTG; // 0x0:主机, 0x1:设备, 0x3:OTG dwc3_writel(dwc->regs, DWC3_GCTL, reg); // 启用ID和VBUS中断 dwc3_writel(dwc->regs, DWC3_DEVTEN, DWC3_DEVTEN_OTGACNTIE | DWC3_DEVTEN_CONNECTDONE); } ``` #### 2. 状态检测与切换 在中断处理函数或轮询任务中检查状态: ```c void dwc3_otg_poll(struct dwc3 *dwc) { u32 osts = dwc3_readl(dwc->regs, DWC3_OSTS); u32 gusbsts = dwc3_readl(dwc->regs, DWC3_GUSBSTS); // 检测ID引脚变化 if (osts & DWC3_OSTS_IDDIG) { // ID=1(设备模式):连接为从机 if (dwc->current_role != DWC3_GCTL_PRTCAP_DEVICE) { dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); } } else { // ID=0(主机模式):连接为主机 if (dwc->current_role != DWC3_GCTL_PRTCAP_HOST) { dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST); } } // 检测VBUS变化(当ID=0时,VBUS应被提供;ID=1时,VBUS应被检测) if (osts & DWC3_OSTS_BSESVALID) { // VBUS存在,可能作为主机或设备 } else { // VBUS消失,断开连接 } } ``` #### 3. 模式切换函数 实现`dwc3_set_mode()`函数完成硬件重配: ```c void dwc3_set_mode(struct dwc3 *dwc, enum dwc3_role mode) { u32 reg; // 停止当前角色 if (dwc->current_role == DWC3_GCTL_PRTCAP_HOST) { xhci_stop(dwc->xhci); // 停止主机控制器 } else if (dwc->current_role == DWC3_GCTL_PRTCAP_DEVICE) { dwc3_gadget_exit(dwc); // 停止设备模式 } // 配置新角色 reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg &= ~DWC3_GCTL_PRTCAPDIR_MASK; reg |= (mode << DWC3_GCTL_PRTCAPDIR_SHIFT); dwc3_writel(dwc->regs, DWC3_GCTL, reg); // 初始化新角色 if (mode == DWC3_GCTL_PRTCAP_HOST) { xhci_init(dwc->xhci); // 初始化主机栈 } else if (mode == DWC3_GCTL_PRTCAP_DEVICE) { dwc3_gadget_init(dwc); // 初始化设备栈 } dwc->current_role = mode; } ``` ### 三、中断处理优化 为减少轮询开销,应启用DWC3的OTG事件中断: ```c // 在中断处理函数中 irqreturn_t dwc3_otg_interrupt(int irq, void *data) { u32 devt = dwc3_readl(dwc->regs, DWC3_DEVT); if (devt & DWC3_DEVT_OTG) { // 处理OTG状态变化(ID或VBUS变化) dwc3_otg_poll(dwc); return IRQ_HANDLED; } return IRQ_NONE; } ``` ### 四、设备树配置要点 如引用[2]所示,OTG模式需正确关联PHY和extcon: ```dts &usbdrd_dwc3_0 { dr_mode = "otg"; phys = <&u2phy0_otg>; // 使用USB2 PHY phy-names = "usb2-phy"; extcon = <&u2phy0>; // 通过PHY获取ID/VBUS状态[^2] }; ``` ### 五、验证动态切换 1. **ID引脚接地测试**: - 用USB线连接PC(主机)→ 应进入设备模式,PC识别为USB设备 2. **ID引脚悬空测试**: - 插入U盘 → 应进入主机模式,挂载U盘 3. **寄存器检查**: - `DWC3_GCTL.PRTCAPDIR`:切换后应为`0x1`(设备)或`0x0`(主机) --- §§相关问题§§ 1. 如何通过U-Boot命令手动触发OTG模式切换? 2. DWC3控制器在OTG模式下如何管理VBUS电源? 3. 在动态切换过程中如何保持USB连接的稳定性? 4. U-Boot中OTG模式切换的延迟要求是什么? [^1]: DWC3驱动注册结构定义于`drivers/usb/host/xhci-dwc3.c`,通过`U_BOOT_DRIVER`宏声明 [^2]: 设备树配置参考RK3399平台实现,extcon用于传递ID/VBUS状态</think>### U-Boot中DWC3控制器OTG模式动态切换实现 在U-Boot实现DWC3控制器的OTG模式动态切换,需要结合硬件状态检测、寄存器操作和中断处理。以下是关键实现步骤: --- #### 一、硬件配置基础 在设备树中声明OTG模式和支持的功能: ```dts &usbdrd_dwc3_0 { dr_mode = "otg"; // 启用OTG模式[^2] maximum-speed = "high-speed"; // 速度模式 phys = <&u2phy0_otg>; // 关联PHY控制器 phy-names = "usb2-phy"; extcon = <&u2phy0>; // 关键:外部连接器用于状态检测[^2] }; ``` --- #### 二、动态切换核心流程 在驱动代码实现状态检测和模式切换(`drivers/usb/host/xhci-dwc3.c`): ```c // 1. 状态检测函数 static void dwc3_otg_detect(struct dwc3 *dwc) { u32 osts = dwc3_readl(dwc->regs, DWC3_OSTS); // 检测ID引脚状态 (0:主机模式, 1:设备模式) if (osts & DWC3_OSTS_IDDIG) { if (dwc->current_role != DWC3_GCTL_PRTCAP_DEVICE) { dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); // 切换到设备模式 } } else { if (dwc->current_role != DWC3_GCTL_PRTCAP_HOST) { dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST); // 切换到主机模式 } } } // 2. 模式切换实现 void dwc3_set_mode(struct dwc3 *dwc, u32 mode) { u32 reg = dwc3_readl(dwc->regs, DWC3_GCTL); // 清除当前模式设置 reg &= ~DWC3_GCTL_PRTCAPDIR_MASK; switch(mode) { case DWC3_GCTL_PRTCAP_HOST: reg |= DWC3_GCTL_PRTCAP_HOST; // 0x0 xhci_init(dwc->xhci); // 初始化主机栈 break; case DWC3_GCTL_PRTCAP_DEVICE: reg |= DWC3_GCTL_PRTCAP_DEVICE; // 0x2 dwc3_gadget_init(dwc); // 初始化设备栈 break; } dwc3_writel(dwc->regs, DWC3_GCTL, reg); dwc->current_role = mode; } ``` --- #### 三、中断处理与轮询机制 ##### 方案1:中断驱动(推荐) ```c // 在probe函数中注册中断 irq_install(IRQ_OTG, dwc3_otg_interrupt); // 中断处理函数 static void dwc3_otg_interrupt(void *data) { struct dwc3 *dwc = data; u32 devt = dwc3_readl(dwc->regs, DWC3_DEVT); if (devt & DWC3_DEVT_OTG) { // OTG状态变化中断 dwc3_otg_detect(dwc); } } ``` ##### 方案2:轮询模式(无中断支持时) ```c // 在main_loop中添加 #ifdef CONFIG_USB_DWC3_OTG dwc3_otg_detect(dwc); #endif ``` --- #### 四、关键寄存器说明 | 寄存器 | 位域 | 功能描述 | |----------------|--------------------|----------------------------| | `DWC3_OSTS` | `IDDIG` (bit 4) | ID引脚状态:0=主机, 1=设备 | | `DWC3_GCTL` | `PRTCAPDIR` (1:0) | 模式控制:00=主机, 10=设备 | | `DWC3_DEVTEN` | `OTGACNTIE` (bit 5)| OTG状态变化中断使能 | --- #### 五、验证与调试 1. **ID引脚测试**: ```shell # 强制模拟ID引脚变化 mw.l 0xFE800000 0x10 1 # 设置ID=1(设备模式) mw.l 0xFE800000 0x00 1 # 设置ID=0(主机模式) ``` 2. **寄存器状态检查**: ```shell # 查看当前模式 md.l 0xFE800000 1 # DWC3_GCTL地址 # 输出:XXXXXXX1 -> 设备模式,XXXXXXX0 -> 主机模式 ``` 3. **物理测试**: - 插入USB主机:应触发设备模式 - 插入U盘:应触发主机模式 --- #### 六、注意事项 1. **PHY配置**:确保PHY支持OTG模式,如引用[2]中`u2phy0_otg`的配置 2. **电源管理**:模式切换时需重新配置VBUS供电 ```c // 设备模式启用VBUS检测 dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_ULSTCHNGEN); ``` 3. **时序要求**:模式切换后需等待100ms再操作USB栈 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

如之

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值