mtk usb 2.0简单分析

一、概要

根据USB框架、设备树、代码简单分析一下USB

二、整体架构流程

这个图是网上找的
在这里插入图片描述
在下面第四章,musb会注册两个驱动,分别时USB主机控制器驱动和UDC驱动,因为这个平台支持usb的host和peripheral两种角色的切换,下面仅讨论手动切换,自动切换需要加入id脚去控制。

三、设备树

先贴设备树

		usb: usb0@11200000 {
			compatible = "mediatek,mt6789-usb20";
			reg = <0 0x11200000 0 0x10000>,
				<0 0x11f40000 0 0x10000>;
			interrupts = <GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH  0>;
			clocks =  <&infracfg_ao_clk CLK_IFRAO_SSUSB>,
				<&topckgen_clk CLK_TOP_USB_TOP_SEL>,
				<&apmixedsys_clk CLK_APMIXED_USBPLL>;
			clock-names = "sys_clk",
				"ref_clk",
				"src_clk";
			phys = <&u2port0 PHY_TYPE_USB2>;
			mode = <2>;
			multipoint = <1>;
			num_eps = <16>;
			interrupt-names = "mc";
			dr_mode = "otg";
			usb-role-switch;
			cdp-block;
			wakeup-source;
			pericfg = <&pericfg>;
			infracg = <&infracfg_ao>;
			port {
				musb_drd_switch: endpoint@0 {
					remote-endpoint = <&usb_role>;
				};
			};
		};

		u2phy0: usb-phy@11f40000 {
			compatible = "mediatek,generic-tphy-v2";
			#address-cells = <2>;
			#size-cells = <2>;
			ranges;
			status = "okay";
			u2port0: usb-phy@11f40000 {
				reg = <0 0x11f40000 0 0x700>;
				clocks = <&clk26m>;
				clock-names = "ref";
				#phy-cells = <1>;
				mediatek,eye-vrt = <4>;
				mediatek,eye-term = <4>;
				mediatek,rev4 = <1>;
				mediatek,rx_sqth = <5>;
				mediatek,discth = <7>;
				nvmem-cells = <&u2_phy_data>;
				nvmem-cell-names = "intr_cal";
				nvmem-cell-masks = <0x1f>;
				status = "okay";
			};
		};

四、核心代码

4.1 musb_core.c

4.1.1 musb_probe

probe这部分内容,主要是做了如下内容:
1.创建一个platform_device,使得内核能够识别和管理这些设备,从而支持相应的驱动程序与之交互‌
2.获取u2phy0
3.设置mt_usb_ops,控制platform的行为
4.创建idle 工作队列,用于不外接设备(pc/鼠标等)时,phy的控制
5.获取一堆phy的时钟
6.初始设置phy mode

static int musb_probe(struct platform_device *pdev)
{
	struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data;
	struct platform_device *musb_pdev;
	struct musb_hdrc_config *config;
	struct device_node *np = pdev->dev.of_node;
	int ret = -ENOMEM;
	
	//glue这个结构体比较重要
	glue = kzalloc(sizeof(*glue), GFP_KERNEL);
	if (!glue)
		goto err0;

	/* Device name is required */
	//生成一个platform_device
	//这个设备会让musb.c加载其中的probe,其中会调用到musb_init_controller,这个也在该文件中,下面会详细讲述
	musb_pdev = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_NONE);
	if (!musb_pdev) {
		dev_notice(&pdev->dev, "failed to allocate musb pdev\n");
		goto err1;
	}
	
	//这个获取,可以查看设备树对应的usb phy :u2phy0
	glue->phy = devm_of_phy_get_by_index(&pdev->dev, np, 0);
	if (IS_ERR(glue->phy)) {
		dev_notice(&pdev->dev, "fail to getting phy %ld\n",
			PTR_ERR(glue->phy));
		return PTR_ERR(glue->phy);
	}
	
	//注册平台设备,使得内核能够识别和管理这些设备,从而支持相应的驱动程序与之交互‌
	glue->usb_phy = usb_phy_generic_register();
	if (IS_ERR(glue->usb_phy)) {
		dev_notice(&pdev->dev, " fail to registering usb-phy %ld\n",
			PTR_ERR(glue->usb_phy));
		return PTR_ERR(glue->usb_phy);
	}
	
	//获取USB的phy设备,这里使用的是UBS2的phy
	glue->xceiv = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
	if (IS_ERR(glue->xceiv)) {
		ret = PTR_ERR(glue->xceiv);
		dev_notice(&pdev->dev, " fail to getting usb-phy %d\n", ret);
		goto err_unregister_usb_phy;
	}

	//mt_usb_ops控制platform的行为
	pdata->platform_ops = &mt_usb_ops;
	
	//把pdev设备的资源数据复制给musb_pdev设备,下一节musb.c中会使用到
	//这个与platform_device_alloc(创建设备)可以配套使用,platform_device_add_resources(复制设备资源)
	//从这里即可看出,把设备mediatek,mt6789-usb20的资源数据复制到设备musb_pdev
	ret = platform_device_add_resources(musb_pdev,
				pdev->resource, pdev->num_resources);
	if (ret) {
		dev_notice(&pdev->dev, "failed to add resources\n");
		goto err2;
	}

	//这个是usb如果没有与鼠标(host)、键盘(host)、PC(device)连接,就会进入idle,会关闭一部分phy
	INIT_DELAYED_WORK(&idle_work, do_idle_work);

	DBG(0, "keep musb->power & mtk_usb_power in the same value\n");
	mtk_usb_power = false;

	//这部分内容就是获取这些phy的时钟,通过控制这些phy的时钟达到控制usb的目的
	glue->sys_clk = devm_clk_get(&pdev->dev, "sys_clk");
	glue->ref_clk = devm_clk_get_optional(&pdev->dev, "ref_clk");
	glue->src_clk = devm_clk_get_optional(&pdev->dev, "src_clk");
	glue->dma_clk = devm_clk_get_optional(&pdev->dev, "dma_clk");
	glue->phy_clk = devm_clk_get_optional(&pdev->dev, "phy_clk");
	glue->mcu_clk = devm_clk_get_optional(&pdev->dev, "mcu_clk");

	//根据dr_mode,glue中的phy切换到对应的mode
	switch (pdata->dr_mode) {
	case USB_DR_MODE_HOST:
		glue->phy_mode = PHY_MODE_USB_HOST;
		break;
	case USB_DR_MODE_PERIPHERAL:
		glue->phy_mode = PHY_MODE_USB_DEVICE;
		break;
	case USB_DR_MODE_OTG:
		glue->phy_mode = PHY_MODE_USB_OTG;
		break;
	default:
		dev_info(&pdev->dev, "Error 'dr_mode' property\n");
		return -EINVAL;
	}

	DBG(0, "get dr_mode: %d\n", pdata->dr_mode);

	return ret;
}
4.1.2 mt_usb_ops

mt_usb_ops用来控制platform的行为

static const struct musb_platform_ops mt_usb_ops = {
	.init = mt_usb_init,							
	.exit = mt_usb_exit,							
	.enable = mt_usb_enable,						
	.disable = mt_usb_disable,						
	/* .set_vbus = mt_usb_set_vbus, */
	.vbus_status = mt_usb_get_vbus_status,
	.enable_clk =  mt_usb_enable_clk,			//这个功能与mt_usb_enable类似,不过是仅使能时钟
	.disable_clk =  mt_usb_disable_clk,			//这个功能与mt_usb_disable类似,不过是仅去使能时钟
	.prepare_clk = mt_usb_prepare_clk,			//这个是对时钟prepare
	.unprepare_clk = mt_usb_unprepare_clk,		//同上
#if IS_ENABLED(CONFIG_USB_MTK_OTG)
	.enable_wakeup = mt_usb_wakeup,
#endif
};

初始化

static int mt_usb_init(struct musb *musb)
{
	int ret;
#if IS_ENABLED(CONFIG_USB_MTK_OTG)
	struct device_node *node = musb->glue->dev->of_node;
#endif

	DBG(1, "%s\n", __func__);
	
	musb->phy = glue->phy;
	musb->xceiv = glue->xceiv;
	musb->dma_irq = (int)SHARE_IRQ;
	musb->fifo_cfg = fifo_cfg;						//端点的配置
	musb->fifo_cfg_size = ARRAY_SIZE(fifo_cfg);
	musb->dyn_fifo = true;
	musb->power = false;
	musb->is_host = false;
	musb->fifo_size = 8 * 1024;						//管道的大小
	musb->usb_lock = wakeup_source_register(NULL, "USB suspend lock");		//这个是唤醒锁,持锁会防止进入深度休眠
	
	//初始化phy,远程调用u2phy0的init
	ret = phy_init(glue->phy);
	if (ret)
		goto err_phy_init;
	
	//远程调用u2phy0的set_mode
	phy_set_mode(glue->phy, glue->phy_mode);
	//远程调用u2phy0的power_on
	ret = phy_power_on(glue->phy);
	
	//设置中断,这部分后面会单独讲解
	musb->isr = mt_usb_interrupt;
	
	//这个应该是配置中断寄存器
	musb_writel(musb->mregs,
			MUSB_HSDMA_INTR, 0xff |
			(0xff << DMA_INTR_UNMASK_SET_OFFSET));
	DBG(0, "musb platform init %x\n",
			musb_readl(musb->mregs, MUSB_HSDMA_INTR));

	//这个应该是配置中断寄存器,配置的中断应该是USB的TX/RX/USB_STATUS等
	musb_writel(musb->mregs,
			USB_L1INTM,
		    TX_INT_STATUS |
		    RX_INT_STATUS |
		    USBCOM_INT_STATUS |
		    DMA_INT_STATUS);

	return ret;
}

exit

static int mt_usb_exit(struct musb *musb)
{
	del_timer_sync(&musb->idle_timer);
	//调用u2phy0的power_off和exit
	phy_power_off(glue->phy);
	phy_exit(glue->phy);

	return 0;
}

mt_usb_enable 和 mt_usb_disable

static void mt_usb_enable(struct musb *musb)
{
	/* clock already prepare before enter here */
	//这个是把需要使能的时钟全部给使能了
	usb_enable_clock(true);
}

static void mt_usb_disable(struct musb *musb)
{
	//这个是把需要使能的时钟全部给去使能了
	usb_enable_clock(false);
}
4.1.3 do_idle_work

phy进入idle

void do_idle_work(struct work_struct *data)
{
	//同上不详细描述
	usb_prepare_clock(true);
	//记录就的phy state
	old_state = musb->xceiv->otg->state;
	//如果是激活的,就说明还连着设备
	if (musb->is_active) {
		DBG(0,
			"%s active, igonre do_idle\n",
			otg_state_string(musb->xceiv->otg->state));
		goto exit;
	}
	//根据当前mode(host/device)操作
	switch (musb->xceiv->otg->state) {
	case OTG_STATE_B_PERIPHERAL:
	case OTG_STATE_A_WAIT_BCON:
		//从寄存器读取devctl,判断是否处于b_device
		devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
		if (devctl & MUSB_DEVCTL_BDEVICE) {
			//处于,就赋值为b_idle
			musb->xceiv->otg->state = OTG_STATE_B_IDLE;
			MUSB_DEV_MODE(musb);
		} else {
			//如果不是b_device,就赋值a_idle
			musb->xceiv->otg->state = OTG_STATE_A_IDLE;
			MUSB_HST_MODE(musb);
		}
		break;
	case OTG_STATE_A_HOST:
		//这里同上
		devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
		if (devctl & MUSB_DEVCTL_BDEVICE)
			musb->xceiv->otg->state = OTG_STATE_B_IDLE;
		else
			musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON;
		break;
	default:
		break;
	}
	DBG(0, "otg_state %s to %s, is_active<%d>\n",
			otg_state_string(old_state),
			otg_state_string(musb->xceiv->otg->state),
			musb->is_active);
exit:
	spin_unlock_irqrestore(&musb->lock, flags);

	usb_prepare_clock(false);
}

4.2 musb.c

//这个文件主要是注册了一个中断,这个中断比较重要

static int musb_probe(struct platform_device *pdev)
{
	//这个irq就是usb的中断
	irq = platform_get_irq(pdev, 0);
	if (irq <= 0)
		return -ENODEV;

	pr_info("%s mac=0x%lx, phy=0x%lx, irq=%d\n"
		, __func__, (unsigned long)base, (unsigned long)pbase, irq);
	status = musb_init_controller(dev, irq, base, pbase);

	return status;
}

musb_init_controller的定义在上一节的文件中

int musb_init_controller(struct device *dev, int nIrq,
				void __iomem *ctrl, void __iomem *ctrlp)
{
	int status;
	struct musb *musb;
	struct musb_hdrc_platform_data *plat = dev->platform_data;
	struct usb_hcd *hcd;
	struct device_node *np = dev->parent->of_node;

	/* allocate */
	musb = allocate_instance(dev, plat->config, ctrl);
	if (!musb) {
		status = -ENOMEM;
		goto fail0;
	}

	mtk_musb = musb;
	//一些资源的赋值
	musb->board_set_power = plat->set_power;
	musb->min_power = plat->min_power;
	musb->ops = plat->platform_ops;
	musb->nIrq = nIrq;
	glue->mtk_musb = mtk_musb;
	mtk_musb->glue = glue;
	
	//调用上一节platform_ops的init,即4.1.2 mt_usb_ops中的init func
	status = musb_platform_init(musb);
	//调用上一节platform_ops的enable,即4.1.2 mt_usb_ops中的enable func
	musb_platform_enable(musb);
	
	//下面两个func都是控制寄存器,大致功能也写了,这里不深究
	/* be sure interrupts are disabled before connecting ISR */
	musb_generic_disable(musb);
	/* setup musb parts of the core (especially endpoints) */
	/* Initialize MUSB (M)HDRC part of the USB hardware subsystem;
 	* configure endpoints, or take their config from silicon
 	*/
	status = musb_core_init(plat->config->multipoint
			? MUSB_CONTROLLER_MHDRC : MUSB_CONTROLLER_HDRC, musb);
	//这个没有细看
	timer_setup(&musb->otg_timer, musb_otg_timer_func, 0);

	/* Init IRQ workqueue before request_irq */
	//对sys下的usb mode节点进行修改,usb角色切换完成后,可以使用schedule_work(&musb->irq_work);赖更新sys下的mode节点,告知上层,usb角色改变
	INIT_WORK(&musb->irq_work, musb_irq_work);
	pr_debug("musb irq number: %d", musb->nIrq);

	/* attach to the IRQ */
	//请求中断线程,musb->nIrq是中断号,musb->isr是中断函数,
	//musb->isr见上一节中的musb->isr = mt_usb_interrupt,下面会对这个mt_usb_interrupt func进行一个讲解
	if (request_threaded_irq(musb->nIrq, NULL, musb->isr
			, IRQF_ONESHOT, dev_name(dev), musb)) {
		DBG(0, "request_irq %d failed!\n", musb->nIrq);
		status = -ENODEV;
		goto fail3;
	}

	/* host side needs more setup */
	hcd = musb_to_hcd(musb);
	otg_set_host(musb->xceiv->otg, &hcd->self);
	hcd->self.otg_port = 1;
	musb->xceiv->otg->host = &hcd->self;
	hcd->power_budget = 2 * (plat->power ? : 250);
	hcd->self.uses_pio_for_control = 1;
	
	//usb_add_hcd为usb core的标准api,注册一个usb主机控制器
	status = usb_add_hcd(hcd, 0, 0);
	if (status < 0)
		goto fail3;
	
	//注册一个usb设备侧驱动,最终会调用到usb core标准api usb_add_gadget_udc
	status = musb_gadget_setup(musb);
	if (status < 0)
		goto fail3;

	/*
	 * Dual-role-switch will turn off USB after initialize done.
	 * Therefore, no need to power off MUSB when Dual-role-switch is
	 * enabled.
	 */
	 //这个是usb切换的关键代码,这里会单独开一个章节讲解usb切换,会放在第五章
	status = mt_usb_otg_switch_init(glue);
	if (status < 0) {
		DBG(0, "failed to initialize switch\n");
		goto fail3;
	}

	return 0;

}
EXPORT_SYMBOL(musb_init_controller);

4.3 mt_usb_interrupt分析

只要有usb的状态、RX、TX发生变化,都会触发这个中断

static irqreturn_t mt_usb_interrupt(int irq, void *dev_id)
{
	irqreturn_t tmp_status;
	irqreturn_t status = IRQ_NONE;
	struct musb *musb = (struct musb *)dev_id;
	u32 usb_l1_ints;
	unsigned long flags;

	spin_lock_irqsave(&musb->lock, flags);
	//获取中断信息
	usb_l1_ints = musb_readl(musb->mregs, USB_L1INTS) &
		musb_readl(mtk_musb->mregs, USB_L1INTM);
	DBG(1, "usb interrupt assert %x %x  %x %x %x %x %x\n", usb_l1_ints,
	    musb_readl(mtk_musb->mregs, USB_L1INTM),
	    musb_readb(musb->mregs, MUSB_INTRUSBE),
		musb_readw(musb->mregs, MUSB_INTRTX),
		musb_readw(musb->mregs, MUSB_INTRTXE),
		musb_readw(musb->mregs, MUSB_INTRRX),
		musb_readw(musb->mregs, MUSB_INTRRXE));
	
	//如果发生的中断为状态、RX、TX发生变化中任意一个,执行generic_interrupt
	if ((usb_l1_ints & TX_INT_STATUS) || (usb_l1_ints & RX_INT_STATUS)
	    || (usb_l1_ints & USBCOM_INT_STATUS)
	   ) {
	   	//下面会分析这个func
		tmp_status = generic_interrupt(irq, musb);
		if (tmp_status != IRQ_NONE)
			status = tmp_status;
	}
	spin_unlock_irqrestore(&musb->lock, flags);

	/* FIXME, workaround for device_qmu + host_dma */
/* #ifndef CONFIG_MTK_MUSB_QMU_SUPPORT */
	//这个应该是dma发生了中断,需要获取一些usb传输过来的信息
	if (usb_l1_ints & DMA_INT_STATUS) {
		//下面会分析这个func
		tmp_status = dma_controller_irq(irq, musb->dma_controller);
		if (tmp_status != IRQ_NONE)
			status = tmp_status;
	}

	return status;

}

下面是函数的调用,这里我们只对musb_stage0_irq感兴趣
generic_interrupt->
musb_interrupt->
musb_stage0_irq

static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, u8 devctl)
{
	struct usb_otg *otg = musb->xceiv->otg;
	irqreturn_t handled = IRQ_NONE;

	DBG(2, "<== DevCtl=%02x, int_usb=0x%x\n", devctl, int_usb);
	
	//拔掉usb 连接pc 的 线,中断一般会跑到这
	if (int_usb & MUSB_INTR_SUSPEND) {
		switch (musb->xceiv->otg->state) {
		case OTG_STATE_A_PERIPHERAL:
			break;
		case OTG_STATE_B_IDLE:
			break;
		case OTG_STATE_B_PERIPHERAL:
			break;
		case OTG_STATE_A_WAIT_BCON:
			break;
		case OTG_STATE_A_HOST:
			break;
		case OTG_STATE_B_HOST:
			break;
		default:
			break;
		}
	}

	//这个一般是接入USB设备时会触发
	if (int_usb & MUSB_INTR_CONNECT) {
		switch (musb->xceiv->otg->state) {
		case OTG_STATE_B_PERIPHERAL:
			break;
		case OTG_STATE_B_WAIT_ACON:
			break;
		default:
			break;
		}
	}
	
	//一般拔出USB设备会触发这个
	if ((int_usb & MUSB_INTR_DISCONNECT) && !musb->ignore_disconnect) {
		switch (musb->xceiv->otg->state) {
		case OTG_STATE_A_HOST:
		case OTG_STATE_A_SUSPEND:
			break;
		case OTG_STATE_B_HOST:
			break;
		case OTG_STATE_A_PERIPHERAL:
		case OTG_STATE_B_WAIT_ACON:
		case OTG_STATE_B_PERIPHERAL:
		case OTG_STATE_B_IDLE:
			break;
		default:
			break;
		}
	}

	/* mentor saves a bit: bus reset and babble share the same irq.
	 * only host sees babble; only peripheral sees bus reset.
	 */
	 //一般在peripheral下,usb 连接 pc 会出现这个
	if (int_usb & MUSB_INTR_RESET) {
			switch (musb->xceiv->otg->state) {
			case OTG_STATE_A_SUSPEND:
			case OTG_STATE_A_WAIT_BCON:	/* OPT TD.4.7-900ms */
				break;
			case OTG_STATE_A_PERIPHERAL:
				break;
			case OTG_STATE_B_WAIT_ACON:
				break;
			case OTG_STATE_B_IDLE:
			case OTG_STATE_B_PERIPHERAL:
				break;
			default:
			}
		}
	}

	//这个就是更新上层sys mode的状态,之前有提过
	schedule_work(&musb->irq_work);

	return handled;
}

五、usb角色切换代码分析

kernel-5.10/drivers/misc/mediatek/usb20/musb_dr.c

mt_usb_otg_switch_init->
	mt_usb_role_sw_register->
		usb_role_switch_register

mt_usb_otg_switch_init做了很多内容的初始化,但这里主要分析mt_usb_role_sw_register即可,usb_role_switch_register是标准的usb api

static int mt_usb_role_sw_register(struct otg_switch_mtk *otg_sx)
{
	struct usb_role_switch_desc role_sx_desc = { 0 };
	struct mt_usb_glue *glue =
		container_of(otg_sx, struct mt_usb_glue, otg_sx);
	struct musb *musb = glue->mtk_musb;
	
	/*
	*	mt_usb_role_sx_set这个是注册的重要func,
	*	extcon-usb能通过usb_role_switch_set_role这个标准api来切换usb 角色,
	*	最终调用的就是mt_usb_role_sx_set
	*/
	role_sx_desc.set = mt_usb_role_sx_set;
	role_sx_desc.get = mt_usb_role_sx_get;
	role_sx_desc.allow_userspace_control = true;
	role_sx_desc.fwnode = dev_fwnode(glue->dev);
	role_sx_desc.driver_data = glue;
	//这个就是注册usb role switch,到时使用相应的
	otg_sx->role_sw = usb_role_switch_register(glue->dev, &role_sx_desc);

	mt_usb_role_sx_set(otg_sx->role_sw, USB_ROLE_NONE);
	musb->usb_connected = 0;

	return 0;
}

这里贴一个extcon中usb切换的接口,和usb的切换api
kernel-5.10/drivers/misc/mediatek/extcon/extcon-mtk-usb.c
在这里插入图片描述
因此,我们对role_sx_desc.set = mt_usb_role_sx_set进行分析
mt_usb_role_sx_set分析

static int mt_usb_role_sx_set(struct usb_role_switch *sw, enum usb_role role)
{
	struct mt_usb_glue *glue = usb_role_switch_get_drvdata(sw);
	struct otg_switch_mtk *otg_sx = &glue->otg_sx;
	struct device *dev = glue->dev;
	bool id_event, vbus_event;
	static bool first_init = true;
	
	//如果是切换 host id_event == true
	//如果是切换 device vbus_event == true
	id_event = (role == USB_ROLE_HOST);
	vbus_event = (role == USB_ROLE_DEVICE);

	if (!!(otg_sx->sw_state & MUSB_VBUS_VALID) ^ vbus_event) {
		if (vbus_event) {
			dev_info(dev, "%s: if vbus_event true\n", __func__);
			/* phy_set_mode(glue->phy, PHY_MODE_USB_DEVICE); */
			/* PHY mode will be set in do_connection_work */
			set_usb_phy_clear();
			phy_power_on(glue->phy);
			mt_usb_set_mailbox(otg_sx, MUSB_VBUS_VALID);
		} else {
			mt_usb_set_mailbox(otg_sx, MUSB_VBUS_OFF);
			dev_info(dev, "%s: if vbus_event false\n", __func__);
			phy_power_off(glue->phy);
		}
	}

	if (!!(otg_sx->sw_state & MUSB_ID_GROUND) ^ id_event) {
		if (id_event) {
			dev_info(dev, "%s: if id_event true\n", __func__);

			phy_power_on(glue->phy);

			/* PHY mode will be set in host_connect work */
			mt_usb_set_mailbox(otg_sx, MUSB_ID_GROUND);
		} else {
			/*
			 * add this for reduce boot 200ms
			 * and add delay 200ms for plugout
			 */
			if (!first_init)
				mdelay(200);
			else
				first_init = false;

			/* PHY mode will be set in host_disconnect work */
			mt_usb_set_mailbox(otg_sx, MUSB_ID_FLOAT);
			phy_power_off(glue->phy);
		}
	}

	return 0;
}

这里最后会经过层层调用,调用到

mt_usb_set_mailbox->
	issue_host_work->
		do_host_work->
			set_usb_phy_mode

因为后续是修改寄存器改变phy的mode,这里只对mt_usb_set_mailbox做一个简单的说明
省略了很多代码,只做简要说明

/*
 * switch to host: -> MUSB_VBUS_OFF --> MUSB_ID_GROUND
 * switch to device: -> MUSB_ID_FLOAT --> MUSB_VBUS_VALID
 */
static void mt_usb_set_mailbox(struct otg_switch_mtk *otg_sx,
	enum mt_usb_vbus_id_state status)
{
	struct mt_usb_glue *glue =
		container_of(otg_sx, struct mt_usb_glue, otg_sx);
	struct musb *musb = glue->mtk_musb;
	int i;

	dev_info(musb->controller, "mailbox %s\n", mailbox_state_string(status));
	switch (status) {
	case MUSB_ID_GROUND:
		//host切换步骤2:切换phy mode到host(PHY MODE设置成HOST)
		mt_usb_host_connect(0);
		break;
	case MUSB_ID_FLOAT:
		//device切换步骤1:断开host的连接(PHY MODE设置成INVALID)
		mt_usb_host_disconnect(0);
		break;
	case MUSB_VBUS_OFF:
		//host切换步骤1:断开device的连接(PHY MODE设置成INVALID)
		mt_usb_disconnect(); /* sync to UI */
		mt_usb_gadget_disconnect(musb); /* sync to UI */
		break;
	case MUSB_VBUS_VALID:
		//device切换步骤2:切换phy mode到device(PHY MODE设置成DEVICE)
		mt_usb_connect();
		break;
	default:
		dev_info(musb->controller, "invalid state\n");
	}
}

六、小结

简单分析了usb的一些内容注册与初始化
1.phy handle的获取与ops的操作
2.usb fifo的注册与初始化
3.usb中断的注册与初始化
4.extcon手动切换usb的注册与初始化

参考第五章,手动切换,最终是修改寄存器,改变phy mode的状态
如果由id脚自动切换,加入id中断即可,vbus会在切换的某个时机中进行打开与关闭

通过标准的usb api注册设备,其中对phy的控制也是通过标准接口进行封装实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值