mtk OTG驱动分析
一.平台相关的重要结构体
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 signalingmusb->xceiv->state = OTG_STATE_A_HOST; //设置为这个状态usb_hcd_resume_root_hub //called by HCD to resume its root hubqueue_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...,休眠的时候断开,没有应该不能收到irqmusb_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);elsemusb_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);
}