mtk OTG驅動分析


http://blog.chinaunix.net/attachment/201312/17/25810793_13872651157U4q.png
 
 
一.平台相關的重要結構體
misc/mediatek/mach/mt6735/mt_devs.c
這個結構體在加載usb20.c的時候用到platform_device 
struct platform_device mt_device_usb = {
        .name             = "mt_usb",
        .id               = -1,   //only one such device
        .dev = {
                .platform_data          = &usb_data,
                .dma_mask               = &usb_dmamask,
                .coherent_dma_mask      = DMA_BIT_MASK(32),
        /*.release=musbfsh_hcd_release,*/
        },   
};
 
這個是usb20.c中的平台driver
static struct platform_driver mt_usb_driver = {
.remove= mt_usb_remove,
.probe= mt_usb_probe,
.driver= {
.name= "mt_usb",
},
};
 
這個是 usb20.c中的平台相關的操作函數
static const struct musb_platform_ops mt_usb_ops = {
.init= mt_usb_init,
.exit= mt_usb_exit,
/*.set_mode= mt_usb_set_mode,*/
.try_idle= mt_usb_try_idle,
.enable= mt_usb_enable,
.disable= mt_usb_disable,
.set_vbus= mt_usb_set_vbus,
.vbus_status = mt_usb_get_vbus_status
};
 
平台platform_device,應該在加載driver的時候會用到
static struct platform_device usbacm_temp_device = {
.name  ="USB_ACM_Temp_Driver",
.id  = -1,
};
 
 
 
 
二.平台相關的初始化
usb20.c
這里用了fs_initcall(usb20_init);,所以很早就會執行
usb20_init
    platform_driver_register(&mt_usb_driver);
           mt_usb_probe
                musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); //動態加載platfor_device
                pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); //分配struct musb_hdrc_platform_data
                config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL); //分配struct musb_hdrc_config
                pdata->platform_ops= &mt_usb_ops; //設置usb操作函數
                musb的一些設置..............
                ret = platform_device_add(musb); //加入動態platfor_device
                retval = platform_device_register(&usbacm_temp_device);  //加入usbacm_temp_device的platfor_device是,它可以用於模擬USB串行端口。
    
 
 
 
三. Musb_core的操作過程
 
static struct platform_driver musb_driver = {
.driver = {
.name = (char *)musb_driver_name,
.bus = &platform_bus_type,
.of_match_table = apusb_of_ids,
.owner = THIS_MODULE,
.pm = MUSB_DEV_PM_OPS,
},
.probe = musb_probe,
.remove = musb_remove,
.shutdown = musb_shutdown,
};
 
 
Musb_core.c (kernel-3.10\drivers\misc\mediatek\usb20)
 
musb_init
     platform_driver_register(&musb_driver); // platfor_device在usb20.c中加載
          musb_probe
               pdev->dev.of_node = of_find_compatible_node(NULL,NULL,"mediatek,USB0"); //獲取dts里面的設備數據,kernel-3.10/arch/arm64/boot/dts/mt6735.dtsi
            status = musb_init_controller(dev, irq, base, pbase); //初始化控制器
                allocate_instance
                    musb = hcd_to_musb(hcd); //轉換之后進行一些設置Driver instance data.
                    ep->musb = musb;
    ep->epnum = epnum;            //初始化epoint
                    musb->ops = plat->platform_ops;  //設置musb中的數據,plat就是在usb20.c初始化過得musb_hdrc_platform_data*pdata。
             status = musb_platform_init(musb);
                  musb->ops->init(musb); //調用Init函數
                        mt_usb_init //也就是usb20.c中的
                            usb_nop_xceiv_register
                                 platform_device_register_simple("nop_usb_xceiv", -1, NULL, 0); 注冊平台設備
                       Phy-nop.c (kernel-3.10\drivers\usb\phy)中/* Store the otg transceiver */ usb_add_phy_dev (&fsl_otg_tc->phy, USB_PHY_TYPE_USB2);       
                        musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); //得到usb_phy結構體的數據從phy_list,otg的收發器
                        musb其他屬性的一些初始化...........
                         wake_lock_init(&musb->usb_lock, WAKE_LOCK_SUSPEND, "USB suspend lock"); //申請walelock
                        INIT_WORK(&vcore_work, vcore_workqueue);
                        vcore_wq = create_freezable_workqueue("usb20_vcore_work");  //創建工作隊列
                        musb->isr = mt_usb_interrupt; //設置中斷函數
                                generic_interrupt(irq, musb)
                                dma_controller_irq(irq, musb->dma_controller)
                                mt_usb_iddig_int
                                     schedule_delayed_work(&mtk_musb->id_pin_work,5000*HZ/1000); /這里調度工作隊列
                                            musb_id_pin_work //這里面有對於是主機模式還是從機模式的判讀
                       setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb); //設置定時器,執行定時器函數
                             musb_do_idle  //處理三種情況:OTG_STATE_B_PERIPHERAL, OTG_STATE_A_WAIT_BCON, OTG_STATE_A_HOST
                       usb_cable_connected 
                             charger_type = mt_get_charger_type(); //得到pmic的狀態
                        mt_usb_otg_init(musb);
                             mt_usb_init_drvvbus();
                                 mt_usb_init_drvvbus(); //初始化vbus的io口
                                 INIT_DELAYED_WORK(&musb->id_pin_work, musb_id_pin_work); //otg id腳的執行函數
                                 otg_int_init(); //otg int腳的初始化
                                 musb->fifo_cfg_host = fifo_cfg_host; //fifo
                                switch_dev_register(&otg_state) //注冊切換狀態的dev
                    musb_platform_enable
                         mt_usb_enable
                             vcore_hold
                                  vcorefs_request_dvfs_opp //設置核心的東西,cpu頻率等等
                             usb_phy_recover
                                    usb_enable_clock(true); // turn on USB reference clock.
                                    設置一些寄存器.......
                                 c = dma_controller_create(musb, musb->mregs);
                          controller = kzalloc(sizeof(*controller), GFP_KERNEL); //分配musb_dma_controller結構體
                          controller->controller.start = dma_controller_start;
  controller->controller.stop = dma_controller_stop;
  controller->controller.channel_alloc = dma_channel_allocate;
  controller->controller.channel_release = dma_channel_release;
  controller->controller.channel_program = dma_channel_program;
  controller->controller.channel_abort = dma_channel_abort;
  controller->controller.channel_pause = dma_channel_pause;
  controller->controller.channel_resume = dma_channel_resume;
  controller->controller.tx_status = dma_channel_tx_status;
  controller->controller.check_residue = dma_channel_check_residue;
         request_irq(irq, dma_controller_irq, 0, dev_name(musb->controller), &controller->controller) //注冊DMA中斷
                    musb_core_init //Initialize MUSB (M)HDRC part of the USB hardware subsystem;, configure endpoints, or take their config from silicon
                    setup_timer(&musb->otg_timer, musb_otg_timer_func, (unsigned long) musb); 
                         musb_otg_timer_func //Handles OTG hnp timeouts, such as b_ase0_brst,處理一些otg狀態的一些情況
                    INIT_WORK(&musb->irq_work, musb_irq_work); //Init IRQ workqueue before request_irq
                         musb_irq_work //Only used to provide driver mode change events
                    request_irq(musb->nIrq, musb->isr, IRQF_TRIGGER_LOW, dev_name(dev), musb) //注冊IRQ, mt_usb_interrupt是中斷處理函數
                    otg_set_host(musb->xceiv->otg, &hcd->self);
                                         fsl_otg_set_host //Register host controller to the OTG.  Suspend host for OTG role detection.
                           status = musb_gadget_setup(musb); //設備側驅動初始化
                                  musb_g_init_endpoints //Initialize the endpoints exposed to peripheral drivers
                                  musb_platform_try_idle(musb, 0); //
                                          mt_usb_try_idle //查看是否空閑
                                 status = usb_add_gadget_udc(musb->controller, &musb->g); //adds a new gadget to the udc class driver list
                            musb_platform_disable(musb); //initial done, turn off usb
 
 
 
 
Phy-nop.c (kernel-3.10\drivers\usb\phy)分析
platform_device 在Musb_core.c注冊
static struct platform_driver nop_usb_xceiv_driver = {
.probe= nop_usb_xceiv_probe,
.remove= nop_usb_xceiv_remove,
.driver= {
.name= "nop_usb_xceiv",
.owner= THIS_MODULE,
.of_match_table = of_match_ptr(nop_xceiv_dt_ids),
},
};
 
nop_usb_xceiv_probe
      nop = devm_kzalloc(&pdev->dev, sizeof(*nop), GFP_KERNEL); //分配nop_usb_xceiv結構體
        nop->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*nop->phy.otg) //分配usb_otg結構體
        err = clk_set_rate(nop->clk, clk_rate); //設置clk速度
        設置usb_otg和nop_usb_xceiv結構體.........
        usb_add_phy_dev //declare(申明) the USB PHY 
 
 
 
三.musb中斷:otg控制器中的中斷
Musb_core.c (kernel-3.10\drivers\misc\mediatek\usb20)
SOF: Set when a new frame starts.新的一幀開始
#ifdef MUSB_QMU_SUPPORT 有定義 musb->int_queue還有這個中斷: queue management unit 隊列管理單元
 
mt_usb_interrupt
     usb_l1_ints= musb_readl(musb->mregs,USB_L1INTS)&musb_readl(mtk_musb->mregs,USB_L1INTM); //interrupt mask register and interrupt status register
     if ((usb_l1_ints & TX_INT_STATUS) || (usb_l1_ints & RX_INT_STATUS) || (usb_l1_ints & USBCOM_INT_STATUS))//如果是這些中斷
           generic_interrupt(irq, musb) //判斷是不是一般的中斷
                /* musb_read_clear_generic_interrupt */
            musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB) & musb_readb(musb->mregs, MUSB_INTRUSBE); //判斷是不是usb中斷,或者是tx,rx中斷
    musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX) & musb_readw(musb->mregs, MUSB_INTRTXE);
    musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX) & musb_readw(musb->mregs, MUSB_INTRRXE);
            if (musb->int_usb || musb->int_tx || musb->int_rx) //如果是這三種中斷就執行后面的操作retval = musb_interrupt(musb);
                 retval = musb_interrupt(musb); //單獨簡介:1
        if (usb_l1_ints & DMA_INT_STATUS) //DMA中斷
            tmp_status = dma_controller_irq(irq, musb->dma_controller) ://單獨簡介:2
              
                           
         
 
單獨簡介:1
retval = musb_interrupt(musb);

        devctl = musb_readb(musb->mregs, MUSB_DEVCTL); //讀取現在是A設備還是B設備,就是是外圍設備還是host
                if (musb->int_usb)
    retval |= musb_stage0_irq(musb, musb->int_usb, devctl);  //Interrupt Service Routine to record USB "global" interrupts.
                        //in host mode, the peripheral may issue remote wakeup.in peripheral mode, the host may resume the link.

                        if (int_usb & MUSB_INTR_RESUME)
                    if (devctl & MUSB_DEVCTL_HM)  //Set in Peripheral mode when Reset signaling is detected on the bus. Set In host mode when babble is detected.Note: Only active after the first SOF has been sent. 第一次插入吧
                     if (devctl & MUSB_DEVCTL_HM)//如果是host模式
                        switch (musb->xceiv->state) //判斷狀態
                            case OTG_STATE_A_SUSPEND: //remote wakeup?  later, GetPortStatus will stop RESUME signaling
                                musb->xceiv->state = OTG_STATE_A_HOST; //設置為這個狀態
                                usb_hcd_resume_root_hub //called by HCD to resume its root hub
                                     queue_work(pm_wq, &hcd->wakeup_work); //調用工作隊列工作
                            case OTG_STATE_B_WAIT_ACON: 
                                musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
                                musb->is_host = false;
                    esle: 如果是外圍設備模式
                        switch (musb->xceiv->state)
                            case  OTG_STATE_A_SUSPEND //possibly DISCONNECT is upcoming ,可能要馬上斷開了
                                musb->xceiv->state = OTG_STATE_A_HOST;
                                usb_hcd_resume_root_hub(musb_to_hcd(musb));
                            case OTG_STATE_B_WAIT_ACON:
                            case OTG_STATE_B_PERIPHERAL: //disconnect while suspended?  we may not get a disconnect irq...,休眠的時候斷開,沒有應該不能收到irq
                                musb_g_resume  //開始喚醒
                                    musb->gadget_driver->resume(&musb->g); //設備側的驅動
                  ///During connection as an A-Device, we may see a short,current spikes causing voltage drop, because of cable//連接時候可能會有電壓波動,導致Vbus出錯
                  if (int_usb & MUSB_INTR_VBUSERROR) 
                    switch (musb->xceiv->state)
                        case OTG_STATE_A_WAIT_VRISE:
                        case OTG_STATE_A_HOST: //
                            musb_session_restart(musb); // sometimes a short (~3ms) VBUS droop will cause HW state matching waiting forever for VBUS dropping below 0.2V
                 if (int_usb & MUSB_INTR_SUSPEND)  //如果是suspend中斷
                      switch (musb->xceiv->state)
                        case OTG_STATE_A_PERIPHERAL:
                            usb_hcd_resume_root_hub(musb_to_hcd(musb)); //喚醒root hub
    musb_root_disconnect(musb); //斷開
    musb_platform_try_idle(musb, jiffies+ msecs_to_jiffies(musb->a_wait_bcon ? : OTG_TIME_A_WAIT_BCON));
                                musb->ops->try_idle(musb, timeout); //前面這個函數有賦值
                        case OTG_STATE_B_PERIPHERAL:
                            musb_g_suspend(musb);
                            musb->is_active = otg->gadget->b_hnp_enable;
                            if (musb->is_active)
                                mod_timer(&musb->otg_timer, jiffies + msecs_to_jiffies(OTG_TIME_B_ASE0_BRST));
                        case OTG_STATE_A_WAIT_BCON: 
                         。。。。。其余情況暫不分析
                 if (int_usb & MUSB_INTR_CONNECT) //連接模式
                 if ((int_usb & MUSB_INTR_DISCONNECT) //斷開連接模式
                 if (int_usb & MUSB_INTR_RESET) //bus reset and babble share the same irq. 總線重啟和干擾,only host sees babble; only peripheral sees bus reset.
                schedule_work(&musb->irq_work); //
                     musb_irq_work //Only used to provide driver mode change events
                        sysfs_notify(&musb->controller->kobj, NULL, "mode"); //上報狀態變化
        if (musb->int_tx & 1) //這個是endpoint 0的處理函數
            if (devctl & MUSB_DEVCTL_HM) //host
                 retval |= musb_h_ep0_irq(musb); //Handle default endpoint interrupt as host. Only called in IRQ time, from musb_interrupt()
                    musb_advance_schedule //
            esle //設備模式
                 musb_g_ep0_irq  //Handle peripheral ep0 interrupt
        /* RX on endpoints 1-15 */
        if (devctl & MUSB_DEVCTL_HM) //主模式
musb_host_rx(musb, ep_num);
else //設備模式
musb_g_rx(musb, ep_num);
}
        /* TX on endpoints 1-15 */
        if (devctl & MUSB_DEVCTL_HM) //主模式
musb_host_tx(musb, ep_num);
else
musb_g_tx(musb, ep_num);
}
 
 
 
//單獨簡介:2
dma_controller_irq
    int_hsdma = musb_readb(musb->mregs, MUSB_HSDMA_INTR); //musb_read_clear_dma_interrupt寄存器
        for (bchannel = 0; bchannel < MUSB_HSDMA_CHANNELS; bchannel++) //逐個處理8個DMA
            if ((devctl & MUSB_DEVCTL_HM) //主模式
                //The programming guide says that we must clear DMAENAB before DMAMODE
                musb_dma_completion
                    /* endpoint 0 */
if (devctl & MUSB_DEVCTL_HM)
musb_h_ep0_irq(musb);
else
musb_g_ep0_irq(musb);
                        /* endpoints 1..15 */
    if (transmit) {
  if (devctl & MUSB_DEVCTL_HM)
musb_host_tx(musb, epnum);
else
musb_g_tx(musb, epnum);
     } else {
/* receive */
if (devctl & MUSB_DEVCTL_HM)
    musb_host_rx(musb, epnum);
                                //Service an RX interrupt for the given IN endpoint; docs cover bulk, iso,and high-bandwidth IN transfer cases.
                                ret = c->channel_program(dma, qh->maxpacket, dma->desired_mode, buf, length);        
                                    
else
    musb_g_rx(musb, epnum);
}
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM