mtk OTG驱动分析

时间:2024-02-19 12:10:07
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);
}