文章目录
一、概要
根据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的控制也是通过标准接口进行封装实现。