OTG驱动分析(二)

忘是亡心i 2022-08-13 18:40 288阅读 0赞

上回介绍了OTG功能的 OTG部分驱动,本片分析OTG功能的从设备部分驱动。从设备的注册过程和OTG的一样,首先注册设备。

流程是:

1 . 定义platform_device结构。
2 . 定义platform_device下的struct resource设备资源结构
3 . 定义 platform_device下的DEV设备下的平台私有数据(就是该设备私有的数据)
4 . 调用platform_device_register将platform_device结构
注 册上面4个过程调用结束后,设备的信息就被注册到系统中,等待驱动的使用 下面是按照上面顺序贴出代码

定义platform_device结构







static struct platform_device __maybe_unused dr_udc_device = {  
    . name = “fsl-usb2-udc” , 
    . id = - 1, 
    . dev = {  
        . release = dr_udc_release, 
        . dma_mask = & dr_udc_dmamask, 
        . coherent_dma_mask = 0xffffffff, 
    } , 
    . resource = otg_resources, 
    . num_resources = ARRAY_SIZE( otg_resources) , 
} ;

我们可以看到resource和OTG的resource一样,使用的都是OTG那 部分资源

定义platform_device下的struct resource设备资源结构







static struct resource otg_resources[ ] = {  
    [ 0] = {  
     . start = ( u32) ( USB_OTGREGS_BASE) , 
     . end = ( u32) ( USB_OTGREGS_BASE + 0x1ff) , 
     . flags = IORESOURCE_MEM, 
     } , 
    [ 1] = {  
     . start = MXC_INT_USB_OTG, 
     . flags = IORESOURCE_IRQ, 
     } , 
} ;

定义platform_device下的DEV设备下的平台私有数据(就是该设备私有的数据)







static struct fsl_usb2_platform_data __maybe_unused dr_utmi_config = {  
    . name = “DR” , 
    . platform_init = usbotg_init, 
    . platform_uninit = usbotg_uninit, 
    . phy_mode = FSL_USB2_PHY_UTMI_WIDE, 
    . power_budget = 500,         / via RT9706 / 
    . gpio_usb_active = gpio_usbotg_utmi_active, 
    . gpio_usb_inactive = gpio_usbotg_utmi_inactive, 
    . transceiver = “utmi” , 
    . wake_up_enable = _wake_up_enable, 
} ; 
/将设备注册 进系统/
static inline void dr_register_udc( void ) 
{  
    PDATA- > operating_mode = DR_UDC_MODE; //在OTG功能设备注册的时候这里的模式是“FSL_USB2_DR_OTG ”, 时还不知道为什么不开始就直接把模式写成这个而是后该,现在知道了 因为主从和OTG都在用一个资源,所以谁用就的把模式该了。

/#define PDATA (&dr_utmi_config)PDATA指的就是上面的结构 / 
    dr_udc_device. dev. platform_data = PDATA; 

    if ( platform_device_register( & dr_udc_device) ) 
        printk( KERN_ERR “usb: can’t register DR gadget/n” ) ; 
    else 
        printk( KERN_INFO “usb: DR gadget (%s) registered/n” , 
         PDATA- > transceiver) ; 
}

上面就完成了设备的注册。

下面看看驱动和设备的链接。







static struct platform_driver udc_driver = {
    . remove = __exit_p( fsl_udc_remove) , 
    / these suspend and resume are not usb suspend and resume 
 / 
    . suspend = fsl_udc_suspend,  //切换到主设备后的处理过程 
    . resume = fsl_udc_resume,  //切换到从设备的处理过程 
    . probe = fsl_udc_probe, 
    . driver = {
        . name = driver_name, //就是fsl-usb2-udc,用于匹配设备的 
        . owner = THIS_MODULE, 
    }, 
}

 

将驱动注册进系统,如果发现了设备就调用Probe函数,因为我们上面注册了设备所以必然调用Probe







static int init udc_init( void ) 
{  
    printk( KERN_INFO “%s (%s)/n” , driver_desc, DRIVER_VERSION) ; 
    return platform_driver_register( & udc_driver) ;//驱动注册进系统 
} 

发现设备后调用PROBE函数
static int 
init fsl_udc_probe( struct platform_device  pdev) 
{  
    struct resource 
 res; 
    struct fsl_usb2_platform_data  pdata = pdev- > dev. platform_data; 
    int ret = - ENODEV; 
    unsigned int i; 
    u32 dccparams, portsc; 

    if ( strcmp ( pdev- > name, driver_name) ) {  
        VDBG( “Wrong device/n” ) ; 
        return - ENODEV; 
    } 
/**/

static struct fsl_udc udc_controller;

全局变量定义

/*/
    udc_controller = kzalloc( sizeof ( struct fsl_udc) , GFP_KERNEL) ; 
    if ( udc_controller = = NULL ) {  
        ERR( “malloc udc failed/n” ) ; 
        return - ENOMEM; 
    } 
    udc_controller- > pdata = pdata; //私有变量赋值 

# ifdef CONFIG_USB_OTG
    / Memory and interrupt resources will be passed from OTG / 
    udc_controller- > transceiver = otg_get_transceiver( ) ;//在OTG功能中已经通过otg_set_transceiver 设置了transceiver结构,这里面就可以GET


    if ( ! udc_controller- > transceiver) {  
        printk( KERN_ERR “Can’t find OTG driver!/n” ) ; 
        ret = - ENODEV; 
        goto err1a; 
    } 

    res = otg_get_resources( ) ; //获得资源 
    if ( ! res) {  
        DBG( “resource not registered!/n” ) ; 
        return - ENODEV; 
    } 
# else 
    if ( ( pdev- > dev. parent) & & 
        ( to_platform_device( pdev- > dev. parent) - > resource) ) {  
        pdev- > resource = 
            to_platform_device( pdev- > dev. parent) - > resource; 
        pdev- > num_resources = 
            to_platform_device( pdev- > dev. parent) - > num_resources; 
    } 

    res = platform_get_resource( pdev, IORESOURCE_MEM, 0) ; 
    if ( ! res) {  
        ret = - ENXIO; 
        goto err1a; 
    } 

    if ( ! request_mem_region( res- > start, resource_size( res) , 
                driver_name) ) {  
        ERR( “request mem region for %s failed /n” , pdev- > name) ; 
        ret = - EBUSY; 
        goto err1a; 
    } 
# endif 
/将物理地址映射为驱动可以访问的虚拟地址/
    dr_regs = ioremap( res- > start, resource_size( res) ) ; 
    if ( ! dr_regs) {  
        ret = - ENOMEM; 
        goto err1; 
    } 
    pdata- > regs = ( void  ) dr_regs; //私有数据接收映射地址 
    /

     do platform specific init: check the clock, grab/config pins, etc.
    
/

/调用私有数据的初始化函数,关于初始化函数下面分析/ 
    if ( pdata- > platform_init & & pdata- > platform_init( pdev) ) {  
        ret = - ENODEV; 
        goto err2a; 
    } 

    if ( pdata- > have_sysif_regs) 
        usb_sys_regs = ( struct usb_sys_interface  ) 
                ( ( u32) dr_regs + USB_DR_SYS_OFFSET) ; 

    /
Read Device Controller Capability Parameters register
    dccparams = fsl_readl( & dr_regs- > dccparams) ; 
    if ( ! ( dccparams & DCCPARAMS_DC) ) {  
        ERR( “This SOC doesn’t support device role/n” ) ; 
        ret = - ENODEV; 
        goto err2; 
    } 
    /
Get max device endpoints
    /
DEN is bidirectional ep number, max_ep doubles the number
    udc_controller- > max_ep = ( dccparams & DCCPARAMS_DEN_MASK) 
 2; 

# ifdef CONFIG_USB_OTG
    res+ + ; 
    udc_controller- > irq = res- > start; 
# else 
    udc_controller- > irq = platform_get_irq( pdev, 0) ; 
# endif 
    if ( ! udc_controller- > irq) {  
        ret = - ENODEV; 
        goto err2; 
    } 
   /注册中断,该中断和OTG的中断共享一个/
    ret = request_irq( udc_controller- > irq, fsl_udc_irq, IRQF_SHARED, 
            driver_name, udc_controller) ; 
    if ( ret ! = 0) {  
        ERR( “cannot request irq %d err %d /n” , 
                udc_controller- > irq, ret) ; 
        goto err2; 
    } 

    / Initialize the udc structure including QH member and other member /

/对一些资源进行空间开辟等初始化操作/ 
    if ( struct_udc_setup( udc_controller, pdev) ) {  
        ERR( “Can’t initialize udc data structure/n” ) ; 
        ret = - ENOMEM; 
        goto err3; 
    } 

    if ( ! udc_controller- > transceiver) {  
        / initialize usb hw reg except for regs for EP,
        
leave usbintr reg untouched /
 
        dr_controller_setup( udc_controller) ; 
    } 

    /
Setup gadget structure
    udc_controller- > gadget. ops = & fsl_gadget_ops; 
    udc_controller- > gadget. is_dualspeed = 1; 
    udc_controller- > gadget. ep0 = & udc_controller- > eps[ 0] . ep; 
    INIT_LIST_HEAD( & udc_controller- > gadget. ep_list) ; 
    udc_controller- > gadget. speed = USB_SPEED_UNKNOWN; 
    udc_controller- > gadget. name = driver_name; 

    /
Setup gadget.dev and register with kernel
    dev_set_name( & udc_controller- > gadget. dev, “gadget” ) ; 
    udc_controller- > gadget. dev. release = fsl_udc_release; 
    udc_controller- > gadget. dev. parent = & pdev- > dev; 
    ret = device_register( & udc_controller- > gadget. dev) ; 
    if ( ret < 0) 
        goto err3; 

    if ( udc_controller- > transceiver) {  
        udc_controller- > gadget. is_otg = 1; 
        /
now didn’t support lpm in OTG mode
        device_set_wakeup_capable( & pdev- > dev, 0) ; 
    } 

    /
setup QH and epctrl for ep0
    ep0_setup( udc_controller) ; 

    /
setup udc->eps[] for ep0
    struct_ep_setup( udc_controller, 0, “ep0” , 0) ; 
    /
for ep0: the desc defined here;
     for other eps, gadget layer called ep_enable with defined desc
    

    udc_controller- > eps[ 0] . desc = & fsl_ep0_desc; 
    udc_controller- > eps[ 0] . ep. maxpacket = USB_MAX_CTRL_PAYLOAD; 

    / setup the udc->eps[] for non-control endpoints and link
    
to gadget.ep_list /
 
    for ( i = 1; i < ( int ) ( udc_controller- > max_ep / 2) ; i+ + ) {  
        char name[ 14] ; 

        sprintf ( name, “ep%dout” , i) ; 
        struct_ep_setup( udc_controller, i 
 2, name, 1) ; 
        sprintf ( name, “ep%din” , i) ; 
        struct_ep_setup( udc_controller, i  2 + 1, name, 1) ; 
    } 

    /
use dma_pool for TD management
    udc_controller- > td_pool = dma_pool_create( “udc_td” , & pdev- > dev, 
            sizeof ( struct ep_td_struct) , 
            DTD_ALIGNMENT, UDC_DMA_BOUNDARY) ; 
    if ( udc_controller- > td_pool = = NULL ) {  
        ret = - ENOMEM; 
        goto err4; 
    } 
    if ( g_iram_size) {  
        for ( i = 0; i < IRAM_PPH_NTD; i+ + ) {  
            udc_controller- > iram_buffer[ i] = 
             USB_IRAM_BASE_ADDR + i 
 g_iram_size; 
            udc_controller- > iram_buffer_v[ i] = 
             IO_ADDRESS( udc_controller- > iram_buffer[ i] ) ; 
        } 
    } 
# ifdef POSTPONE_FREE_LAST_DTD
    last_free_td = NULL ; 
# endif 

    / disable all INTR / 
    fsl_writel( 0, & dr_regs- > usbintr) ; 

    dr_wake_up_enable( udc_controller, false ) ; 
    udc_controller- > stopped = 1; 

    portsc = fsl_readl( & dr_regs- > portsc1) ; 
    portsc | = PORTSCX_PHY_LOW_POWER_SPD; 
    fsl_writel( portsc, & dr_regs- > portsc1) ; 

    if ( udc_controller- > pdata- > usb_clock_for_pm) 
        udc_controller- > pdata- > usb_clock_for_pm( false ) ; 

    create_proc_file( ) ; 
    return 0; 

err4: 
    device_unregister( & udc_controller- > gadget. dev) ; 
err3: 
    free_irq( udc_controller- > irq, udc_controller) ; 
err2: 
    if ( pdata- > platform_uninit) 
        pdata- > platform_uninit( pdata) ; 
err2a: 
    iounmap( ( u8 __iomem * ) dr_regs) ; 
err1: 
    if ( ! udc_controller- > transceiver) 
        release_mem_region( res- > start, resource_size( res) ) ; 
err1a: 
    kfree( udc_controller) ; 
    udc_controller = NULL ; 
    return ret; 
}

当设备使用主设备时 DEVICE的处理







static int udcsuspend( struct fsludc  udc) 
{  
    u32 mode, usbcmd; 

    /
open clock for register access */
 
    if ( udc_controller- > pdata- > usb_clock_for_pm) 
        udc_controller- > pdata- > usb_clock_for_pm( true ) ; 

    mode = fsl_readl( & dr_regs- > usbmode) & USB_MODE_CTRL_MODE_MASK; 
    usbcmd = fsl_readl( & dr_regs- > usbcmd) ; 

    pr_debug( “%s(): mode 0x%x stopped %d/n” , __func
 , mode, udc- > stopped) ; 

    /
    
If the controller is already stopped, then this must be a
     PM suspend. Remember this fact, so that we will leave the
    
controller stopped at PM resume time.
     /
 
    if ( udc- > stopped) {  
        pr_debug( “gadget already stopped, leaving early/n” ) ; 
        udc- > already_stopped = 1; 
        goto out; 
    } 

    if ( mode ! = USB_MODE_CTRL_MODE_DEVICE) {  
        pr_debug( “gadget not in device mode, leaving early/n” ) ; 
        goto out; 
    } 

    udc- > stopped = 1; 
    /
if the suspend is not for switch to host in otg mode
    if ( ( ! ( udc- > gadget. is_otg) ) | | 
            ( fsl_readl( & dr_regs- > otgsc) & OTGSC_STS_USB_ID) ) {  
        dr_wake_up_enable( udc, true ) ; 
        dr_phy_low_power_mode( udc, true ) ; 
    } 

    /
stop the controller */ 
    usbcmd = fsl_readl( & dr_regs- > usbcmd) & ~ USB_CMD_RUN_STOP; 
    fsl_writel( usbcmd, & dr_regs- > usbcmd) ; 

    printk( KERN_INFO “USB Gadget suspended/n” ) ; 
out: 
    if ( udc_controller- > pdata- > usb_clock_for_pm) 
        udc_controller- > pdata- > usb_clock_for_pm( false ) ; 
    return 0; 
}

当切换到从设备时调用







static int fsludcresume( struct platform_device * pdev) 
{  
    pr_debug( “%s(): stopped %d already_stopped %d/n” , __func
 , 
         udc_controller- > stopped, udc_controller- > already_stopped) ; 

    /
    
If the controller was stopped at suspend time, then
     don’t resume it now.
    
/
 
    if ( udc_controller- > already_stopped) {  
        udc_controller- > already_stopped = 0; 
        pr_debug( “gadget was already stopped, leaving early/n” ) ; 
        return 0; 
    } 

    / Enable DR irq reg and set controller Run / 
    if ( udc_controller- > stopped) {  
        dr_wake_up_enable( udc_controller, false ) ; 
        dr_phy_low_power_mode( udc_controller, false ) ; 
        mdelay( 1) ; 

        dr_controller_setup( udc_controller) ; 
        dr_controller_run( udc_controller) ; 
    } 
    udc_controller- > usb_state = USB_STATE_ATTACHED; 
    udc_controller- > ep0_dir = 0; 

    printk( KERN_INFO “USB Gadget resumed/n” ) ; 
    return 0; 
} 

上面的两个函数就是在上一篇OTG 一中介绍的

gadget_pdrv- > resume ( gadget_pdev ) ;
gadget_pdrv - > suspend ( gadget_pdev , otg_suspend_state ) ;

这两处就是只想的这个函数。而把 前面的函数和上面这两个指针链接的地方就是在OTG中的

fsl_otg_start_gadget 函数。

从 上面我们可以看到以下几点:

1.OTG 功能的从设备使用的资源和私有数据与OTG设备的一致,(主设备也是一致)

  1. 从设备主要为OTG功能提供fsl_udc_resume和udc_suspend两个函数。

发表评论

表情:
评论列表 (有 0 条评论,288人围观)

还没有评论,来说两句吧...

相关阅读

    相关 bootdevice驱动分析

    最近在一些项目里接触到了hw笔记本+linux,hw在笔记本上自己做了一个linux内核原本没有驱动——bootdevice。研究了一下发现,这好像是从安卓的内核里面扣过来的,

    相关 OTG驱动分析

    上回介绍了OTG功能的 OTG部分驱动,本片分析OTG功能的从设备部分驱动。从设备的注册过程和OTG的一样,首先注册设备。 流程是: 1 . 定义platform\_dev

    相关 USB OTG驱动分析(一)

    前一段时间弄了 2 个礼拜的 OTG 驱动调试,感觉精神疲惫啊。主要原因还是自己对 OTG 功能不了解造成的。现在终于完成但是对实质原理还有些模糊。所以 自己重新总结一下。因为