基于DWC2的USB驱动开发-0x0D PHY寄存器读写代码编写与测试 (qq.com)
PHY寄存器读写
1.1前言
我们前面重点介绍了ULPI接口和PHY的寄存器,这一篇来进行PHY寄存器读写的代码编写与测试。从这一篇开始就正真进入了驱动编写的过程了。
1.2 GPVNDCTL寄存器介绍
DWC2提供了读写PHY寄存器的机制,对于应用来说就是操作一个寄存器GPVNDCTL:PHY供应商控制寄存器。能够读写PHY寄存器这在有些底层问题分析时很重要。
《DesignWare Cores USB 2.0 Hi-Speed On-TheGo (OTG) Databook》的P391 5.4.14 GPVNDCTL 有该寄存器的介绍。
IP必须配置OTG_VENDOR_CTL_INTERFACE = 1 才支持该功能。
GHWCFG3寄存器的b9查看该配置值。
对于UTMI+PHY,DWC_otg核心使用UTMI+供应商控制接口进行PHY寄存器访问。对于ULPI PHY,核心使用ULPI接口进行PHY寄存器访问。应用程序设置GPVNDCTL来访问PHY寄存器,并对PHY寄存器的访问进行计时,应用程序轮询此寄存器中的VStatus Done位来确认是否完成PHY寄存器的访问。
该寄存器的描述如下
偏移0x34 访问大小32位
我们重点关注和PHY读写有关的位域,其他的位暂时用不到,先不管。
NewRegReq: 软件写1触发一次操作。
VStsDone: 当应用程序设置NewRegReq为1时,硬件清除该位,操作完成后硬件再置位该位。软件查询该位以判断是否完成,注意代码中需要考虑查询超时机制。
VStsBsy: 忙标志,正在进行操作时硬件置位该位,操作完成时硬件清零,可以认为是和VStsDone相反的标志。
RegWr: 0表示读PHY寄存器,1表示写PHY寄存器
RegAddr: PHY寄存器地址,PHY立即寄存器的6位地址。设置为6'h2F用于扩展PHY寄存器集访问。
VCtrl:UTMI+供应商控制寄存器地址(VCtrl)供应商定义的4位并行输出总线的4位寄存器地址。该字段的位11:8被置于utmi_vontrol[3:0]上。ULPI扩展寄存器地址(ExtRegAddr)PHY扩展寄存器地址。
RegData: 写寄存器时写入寄存器的数据。读寄存器时读到的寄存器内容,设置VStatus Done时有效。
DisUlpiDrvr:读写PHY寄存器无关,
应用程序在完成ULPI Carkit中断(GINTSTS.ULPICKINT)处理后设置此位。设置后,控制器将禁用输出信号的驱动器,并屏蔽ULPI接口的输入信号。控制器在启用ULPI接口之前清除该位。
1.3 代码编写
读PHY寄存器
- RegWr设置为0
- RegAddr设置为立即寄存器的6位地址,如果是扩展寄存器则设置为0x2F且设置VCtrl为扩展寄存器的值。
- NewRegReq置1启动操作
- 等待VStsDone变为1
- 读出RegData
写PHY寄存器
- RegWr设置为1
- RegAddr设置为立即寄存器的6位地址,如果是扩展寄存器则设置为0x2F且设置VCtrl为扩展寄存器的值。
- RegData设置为待写入寄存器的值
- NewRegReq置1启动操作
- 等待VStsDone变为1
代码如下
/**
* \\fn static uint8_t hw_dwc2_is_vndctlsupt(void)
* 判断是否支持供应商控制接口(VndctlSupt)
* \\retval 0 不支持 供应商控制接口(VndctlSupt)
* \\retval 1 支持
*/
static uint8_t hw_dwc2_is_vndctlsupt(void)
{
return ((USB_OTG_READ_REG(CFG_GHWCFG3_ADDR) & VNDCTLSUPT_MASK) > > VNDCTLSUPT_OFFSET) & 0x01;
//return (uint8_t)(s_dwc2_reg_t- >ghwcfg3._b.vndctlsupt);
}
/**
* \\fn int hw_dwc2_read_phyreg(uint8_t regaddr, uint8_t* regval, uint32_t timeout)
* \\param[in] regaddr PHY寄存器,立即寄存器和扩展寄存器统一编码,高2位为0表示立即寄存器,
* 高两位不为0表示扩展寄存器。
* \\param[out] regval 存储读出的寄存器值
* \\param[in] timeout 查询是否完成的次数
* \\retval -1 不支持供应商控制接口(VndctlSupt)
* \\retval -2 读超时
* \\retval 0 读成功
*/
int hw_dwc2_read_phyreg(uint8_t regaddr, uint8_t* regval, uint32_t timeout)
{
/* 判断是否支持供应商控制接口(VndctlSupt) */
if(hw_dwc2_is_vndctlsupt() == 0)
{
return -1;
}
#if 0
s_dwc2_reg_t- >gpvndctl._b.regwr = 0; /* 读模式 */
if((regaddr & 0xC0) != 0)
{
/* 扩展寄存器 */
s_dwc2_reg_t- >gpvndctl._b.regaddr = 0x2F;
s_dwc2_reg_t- >gpvndctl._b.vctrl = regaddr;
}
else
{
/* 立即寄存器 */
s_dwc2_reg_t- >gpvndctl._b.regaddr = regaddr;
s_dwc2_reg_t- >gpvndctl._b.vctrl = 0;
}
s_dwc2_reg_t- >gpvndctl._b.newregreq = 1; /* 启动操作 */
while ((s_dwc2_reg_t- >gpvndctl._b.vstsdone == 0) && (timeout-- > 0)); /* 等待完成 */
/* 根据标志返回值或者错误 */
if(s_dwc2_reg_t- >gpvndctl._b.vstsdone != 0)
{
*regval = s_dwc2_reg_t- >gpvndctl._b.regdata;
//s_dwc2_reg_t- >gpvndctl._b.vstsdone = 1; /* newregreq =1时硬件清0,没必要手动清零 */
return 0;
}
else
{
return -2;
}
#else
uint32_t tmp = 0;
tmp &= ~REGWR_MASK; /* 读模式 */
if((regaddr & 0xC0) != 0)
{
/* 扩展寄存器 */
tmp |= ((uint32_t)0x2F < < REGADDR_OFFSET);
tmp |= ((uint32_t)regaddr < < VCTRL_OFFSET);
}
else
{
/* 立即寄存器 */
tmp |= ((uint32_t)regaddr < < REGADDR_OFFSET);
tmp |= ((uint32_t)0 < < VCTRL_OFFSET);
}
tmp |= NEWREGREQ_MASK; /* 启动操作 */
USB_OTG_WRITE_REG(CFG_GPVNDCTL_ADDR,tmp);
while(((USB_OTG_READ_REG(CFG_GPVNDCTL_ADDR)&VSTSDONE_MASK) == 0) && (timeout-- > 0)); /* 等待完成 */
if(((USB_OTG_READ_REG(CFG_GPVNDCTL_ADDR)&VSTSDONE_MASK) != 0))
{
*regval = (USB_OTG_READ_REG(CFG_GPVNDCTL_ADDR) > > REGDATA_OFFSET) & 0xFF; /* 读出寄存器的值 */
USB_OTG_WRITE_REG(CFG_GPVNDCTL_ADDR,USB_OTG_READ_REG(CFG_GPVNDCTL_ADDR) | VSTSDONE_MASK);
/* newregreq =1时硬件清0,没必要手动清零 */
return 0;
}
else
{
return -2;
}
#endif
}
/**
* \\fn int hw_dwc2_write_phyreg(uint8_t regaddr, uint8_t regval, uint32_t timeout)
* \\param[in] regaddr PHY寄存器,立即寄存器和扩展寄存器统一编码,高2位为0表示立即寄存器,
* 高两位不为0表示扩展寄存器。
* \\param[out] regval 写入寄存器的值
* \\param[in] timeout 查询是否完成的次数
* \\retval -1 不支持供应商控制接口(VndctlSupt)
* \\retval -2 读超时
* \\retval 0 读成功
*/
int hw_dwc2_write_phyreg(uint8_t regaddr, uint8_t regval, uint32_t timeout)
{
/* 判断是否支持供应商控制接口(VndctlSupt) */
if(hw_dwc2_is_vndctlsupt() == 0)
{
return -1;
}
#if 0
/* 不能使用结构体单位域操作,因为regdata需要vstsdone=1才能访问
* 且vstsdone是W1C,所以单位域读-修改-写时vstsdone写入1导致清0,导致后面regdata不能写入
*/
s_dwc2_reg_t- >gpvndctl._b.regwr = 1; /* 写模式 */
if((regaddr & 0xC0) != 0