Omap4470 USB驱动分析之注册过程

平台:OMAP4470,linix3.4,Android4.2平台。
问题:
1. usb host/otg模式切换的时候容易导致系统卡顿,甚至死机。
2. omap4470的usb接了一个usb-Ethernet,正常情况下,omap4470的usb模式应该是host,但是有时候会工作模式不对,重新设置host mode也可让usb-Ethernet工作正常,需要排除是usb模式切换导致的这个问题。
3. 偶尔触发usb dongle无论怎么插拔,omap4470都没检测出usb事件儿。这个可能原因:
a.usb 模式不对。
b.vbus设置有问题。
c.omap4470 usb host 某些寄存器的值设置不对。

要想解决以上三个问题,都跟usb模式和vbus有关,那么就需要深度分析一下usb模式和vbus设置的代码。

一、首先分析一下平台相关的。
看arch/arm/mach-omap2/目录下;

jizhao@star:~/omap/kernel/android-3.4/arch/arm/mach-omap2$ ls *usb*
usb-dwc3.c  usb-fs.c    usb-host.o  usb-musb.o
usb-dwc3.o  usb-host.c  usb-musb.c  usb-tusb6010.c

在usb-musb.c中定义了usb_musb_init。

void __init usb_musb_init(struct omap_musb_board_data *musb_board_data)
{
    struct omap_hwmod       *oh;
    struct platform_device      *pdev;
    struct device           *dev;
    int             bus_id = -1;
    const char          *oh_name, *name;
    struct omap_musb_board_data *board_data;

    if (musb_board_data)
        board_data = musb_board_data;
    else
        board_data = &musb_default_board_data;

    /*
     * REVISIT: This line can be removed once all the platforms using
     * musb_core.c have been converted to use use clkdev.
     */
    musb_plat.clock = "ick";
    musb_plat.board_data = board_data;
    musb_plat.power = board_data->power>> 1;//why ?
    musb_plat.mode = board_data->mode;
    musb_plat.extvbus = board_data->extvbus;
    printk("%s %s %d musb_plat.power is %d\n",__FILE__,__func__,__LINE__,musb_plat.power);
    if (cpu_is_omap3517() || cpu_is_omap3505()) {
        oh_name = "am35x_otg_hs";
        name = "musb-am35x";
    } else if (cpu_is_ti81xx()) {
        oh_name = "usb_otg_hs";
        name = "musb-ti81xx";
    } else {
        oh_name = "usb_otg_hs";
        name = "musb-omap2430";
    }

        oh = omap_hwmod_lookup(oh_name);
        if (WARN(!oh, "%s: could not find omap_hwmod for %s\n",
                 __func__, oh_name))
                return;

    pdev = omap_device_build(name, bus_id, oh, &musb_plat,
                   sizeof(musb_plat), NULL, 0, false);
    if (IS_ERR(pdev)) {
        pr_err("Could not build omap_device for %s %s\n",
                        name, oh_name);
        return;
    }

    dev = &pdev->dev;
    get_device(dev);
    dev->dma_mask = &musb_dmamask;
    dev->coherent_dma_mask = musb_dmamask;
    put_device(dev);
}
在board-44xx-tablet.c中定义了
**omap_tablet_init--->  usb_musb_init(&musb_board_data);**
而omap_table_init则是有machine_desc定义的。
MACHINE_START(OMAP_BLAZE, "OMAP44XX Tablet board")
    .atag_offset    = 0x100,
    .reserve    = omap_tablet_reserve,
    .map_io     = omap4_map_io,
    .init_early = omap_tablet_init_early,
    .init_irq   = gic_init_irq,
    .handle_irq = gic_handle_irq,
    **.init_machine = omap_tablet_init**,
    .timer      = &omap4_timer,
    .restart    = omap_prcm_restart,
MACHINE_END

它的参数定义在本文件内:

static struct omap_musb_board_data musb_board_data = {
    .interface_type     = MUSB_INTERFACE_UTMI,
    .mode           = MUSB_OTG,
    .power          = 500,
};

在usb_musb_init中的最后执行了omap_device_build,这个函数是干啥呢?进去看一下:

struct platform_device __init *omap_device_build(const char *pdev_name, int pdev_id,
                      struct omap_hwmod *oh, void *pdata,
                      int pdata_len,
                      struct omap_device_pm_latency *pm_lats,
                      int pm_lats_cnt, int is_early_device)
{
    struct omap_hwmod *ohs[] = { oh };

    if (!oh)
        return ERR_PTR(-EINVAL);

    return omap_device_build_ss(pdev_name, pdev_id, ohs, 1, pdata,
                    pdata_len, pm_lats, pm_lats_cnt,
                    is_early_device);
}
struct platform_device __init *omap_device_build_ss(const char *pdev_name, int pdev_id,
                     struct omap_hwmod **ohs, int oh_cnt,
                     void *pdata, int pdata_len,
                     struct omap_device_pm_latency *pm_lats,
                     int pm_lats_cnt, int is_early_device)
{
    int ret = -ENOMEM;
    struct platform_device *pdev;
    struct omap_device *od;

    if (!ohs || oh_cnt == 0 || !pdev_name)
        return ERR_PTR(-EINVAL);

    if (!pdata && pdata_len > 0)
        return ERR_PTR(-EINVAL);

    pdev = platform_device_alloc(pdev_name, pdev_id);
    if (!pdev) {
        ret = -ENOMEM;
        goto odbs_exit;
    }

    /* Set the dev_name early to allow dev_xxx in omap_device_alloc */
    if (pdev->id != -1)
        dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
    else
        dev_set_name(&pdev->dev, "%s", pdev->name);

    od = omap_device_alloc(pdev, ohs, oh_cnt, pm_lats, pm_lats_cnt);
    if (!od)
        goto odbs_exit1;

    ret = platform_device_add_data(pdev, pdata, pdata_len);
    if (ret)
        goto odbs_exit2;

    if (is_early_device)
        ret = omap_early_device_register(pdev);
    else
        ret = omap_device_register(pdev);
    if (ret)
        goto odbs_exit2;

    omap_opp_register(&pdev->dev, ohs[0]->name);

    return pdev;

odbs_exit2:
    omap_device_delete(od);
odbs_exit1:
    platform_device_put(pdev);
odbs_exit:

    pr_err("omap_device: %s: build failed (%d)\n", pdev_name, ret);

    return ERR_PTR(ret);
}

不用继续看了吧,继续看也没啥意思,浪费时间,其实就是执行platform_device_register。
重点关心传递的参数:

    pdev = omap_device_build(name, bus_id, oh, &musb_plat,
                   sizeof(musb_plat), NULL, 0, false);

/*其中第一个参数name为oh_name = "usb_otg_hs";
        name = "musb-omap2430";
    第四个参数musb_plat。被platform_device_add_data设置为pdev->dev.platform_data。
    即名字为musb-omap2430的platform_devices的dev.platform_data.稍后看看这个有什么作用。
*/
static struct musb_hdrc_config musb_config = {
    .multipoint = 1,
    .dyn_fifo   = 1,
    .num_eps    = 16,
    .ram_bits   = 12,
};
static struct musb_hdrc_platform_data musb_plat = {
#ifdef CONFIG_USB_MUSB_OTG
    .mode       = MUSB_OTG,
#elif defined(CONFIG_USB_MUSB_HDRC_HCD)
    .mode       = MUSB_HOST,//内核配置为host。
#elif defined(CONFIG_USB_GADGET_MUSB_HDRC)
    .mode       = MUSB_PERIPHERAL,
#endif
    /* .clock is set dynamically */
    .config     = &musb_config,

    /* REVISIT charge pump on TWL4030 can supply up to
     * 100 mA ... but this value is board-specific, like
     * "mode", and should be passed to usb_musb_init().
     */
    .power      = 50,           /* up to 100 mA */ **//为什么要设置为50?**
};

对上述流程做个总结:

在arch/arm/mach-omap2/usb_musb.c.。
通过usb_musb_init注册了一个platform_device。这个platform_device的内容可以看做是如下的形式:
struct platform_device  omap2_usb_musb_devices={
.name="musb-omap2430",
.dev.platform_data=musb_plat,
};

其中musb_plat为:
static struct musb_hdrc_platform_data musb_plat = {
    .mode       = MUSB_HOST,
    .config     = &musb_config,
    .power      = 50>>1,            /* up to 100 mA */
    .clock = "ick";
    .board_data=&musb_plat,
    .extvbus=//暂时不知道等于什么。
    .platform_ops=//这里没有初始化,应该在driver的probe有赋值,稍后再看。
};
static struct musb_hdrc_config musb_config = {
    .multipoint = 1,
    .dyn_fifo   = 1,
    .num_eps    = 16,
    .ram_bits   = 12,
};

*以上仅仅是注册了一个Platform_device,而且这个过程是在内核早期初始化完成的。它初始化了一些usb模式、电流、fifo,clk寄存器地址等变量,并没有真正的操作usb host的寄存器。通常情况下,这些变量会在platform_driver中被使用,稍后要分析一下同名的platform_driver的probe函数。
搜索内核代码”musb-omap2430”*
搜索结果发现,在driver/usb/musb/omap2430.c中定义了。

static struct platform_driver omap2430_driver = {
    .probe      = omap2430_probe,
    .remove     = __devexit_p(omap2430_remove),
    .driver     = {
        .name   = "musb-omap2430",
        .pm = DEV_PM_OPS,
    },
};

那么我们就分析一下这个probe函数。

static int __devinit omap2430_probe(struct platform_device *pdev)
{
    struct musb_hdrc_platform_data  *pdata = pdev->dev.platform_data;//这正是usb-musb.c中定义的 musb_hdrc_platform_data musb_plat。
    struct platform_device      *musb;
    struct omap2430_glue        *glue;
    int             ret = -ENOMEM;

    glue = kzalloc(sizeof(*glue), GFP_KERNEL);
    if (!glue) {
        dev_err(&pdev->dev, "failed to allocate glue context\n");
        goto err0;
    }

    musb = platform_device_alloc("musb-hdrc", -1);
    if (!musb) {
        dev_err(&pdev->dev, "failed to allocate musb device\n");
        goto err1;
    }

    musb->dev.parent        = &pdev->dev;
    musb->dev.dma_mask      = &omap2430_dmamask;
    musb->dev.coherent_dma_mask = omap2430_dmamask;

    glue->dev           = &pdev->dev;
    glue->musb          = musb;
    glue->status            = OMAP_MUSB_UNKNOWN;
    glue->control_dev       = omap_control_get();

    pdata->platform_ops     = &omap2430_ops;

    platform_set_drvdata(pdev, glue);

    /*
     * REVISIT if we ever have two instances of the wrapper, we will be
     * in big trouble
     */
    _glue   = glue;

    INIT_WORK(&glue->omap_musb_mailbox_work, omap_musb_mailbox_work);

    ret = platform_device_add_resources(musb, pdev->resource,
            pdev->num_resources);
    if (ret) {
        dev_err(&pdev->dev, "failed to add resources\n");
        goto err2;
    }

    ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
    if (ret) {
        dev_err(&pdev->dev, "failed to add platform_data\n");
        goto err2;
    }

    pm_runtime_enable(&pdev->dev);

    ret = platform_device_add(musb);
    if (ret) {
        dev_err(&pdev->dev, "failed to register musb device\n");
        goto err2;
    }

    wake_lock_init(&glue->omap_musb_wakelock, WAKE_LOCK_SUSPEND,
               "omap_musb_wakelock");

    return 0;

err2:
    platform_device_put(musb);

err1:
    kfree(glue);

err0:
    return ret;
}

这个probe函数又注册了一个platform_device,它的名字为musb-hdrc,稍后再分析musb-hdrc对于的platform_driver.
在这个probe函数中,申请了两个非常重要的变量。 struct omap2430_glue *glue;struct platform_device 。
上述的 struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data;
正是usb_musb_init中的musb_plat.

在omap2430_probe中对musb_plat的platform_ops进行了赋值。
    pdata->platform_ops     = &omap2430_ops,
    musb->dev->p->driver_data=glue.
    musb->dev.platform_data=pdata=pdev->dev.platform_data=arch/arm/mach-omap2/usb-musb.c中定义的musb_plat 
    所以musb_plat的platform_ops=omap2430_ops.
    ret = platform_device_add_resources(musb, pdev->resource,
            pdev->num_resources);
/*musb的资源都被定义在了platform_device中,跟踪一下这个resource。名字为"musb-omap2430"的platform_device定义了哪些资源呢?*/

回过头来看arch/arm/mach-omap2目录下的代码,在omap_device_build_ss有个参数omap_hwmod,omap_device_alloc使用参数omap_hwmod来初始化了资源。

void __init usb_musb_init(struct omap_musb_board_data *musb_board_data)
{
        ......
        oh = omap_hwmod_lookup(oh_name);
        if (WARN(!oh, "%s: could not find omap_hwmod for %s\n",
                 __func__, oh_name))
                return;

    pdev = omap_device_build(name, bus_id, oh, &musb_plat,
                   sizeof(musb_plat), NULL, 0, false);
    ......
}
接着分析一下omap_hwmod_lookup.
struct omap_hwmod *omap_hwmod_lookup(const char *name)
{
    struct omap_hwmod *oh;

    if (!name)
        return NULL;

    oh = _lookup(name);
    /* check access flag */
    if (oh && oh->flags & HWMOD_ACCESS_DISABLED) {
        pr_warning("omap_hwmod: %s: access denied\n", oh->name);
        oh = NULL;
    }

    return oh;
}
static struct omap_hwmod *_lookup(const char *name)
{
    struct omap_hwmod *oh, *temp_oh;

    oh = NULL;

    list_for_each_entry(temp_oh, &omap_hwmod_list, node) {
        if (!strcmp(name, temp_oh->name)) {
            oh = temp_oh;
            break;
        }
    }

    return oh;
}

原来是从链表omap_hwmod_list中取出来的,那么什么时候添加这个链表呢?一路追踪。发现了omap_hwmod_register函数。这个函数就在omap44xx_hwmod_init中被调用。

现在总结一下建立usb platform资源的过程:
MACHINE_START(OMAP_BLAZE, "OMAP44XX Tablet board")
    .atag_offset    = 0x100,
    .reserve    = omap_tablet_reserve,
    .map_io     = omap4_map_io,
    .init_early = omap_tablet_init_early,
    .init_irq   = gic_init_irq,
    .handle_irq = gic_handle_irq,
    .init_machine   = omap_tablet_init,
    .timer      = &omap4_timer,
    .restart    = omap_prcm_restart,
MACHINE_END
这里面有个omap_tablet_init_early
        ---->omap4430_init_early
            ---->omap44xx_hwmod_init
                ---->omap_hwmod_register(omap44xx_hwmods);
其中static __initdata struct omap_hwmod *omap44xx_hwmods[] = {
......
omap44xx_usb_otg_hs_hwmod
......
};

omap44xx_usb_otg_hs_hwmod的内容如下:

static struct omap_hwmod omap44xx_usb_otg_hs_hwmod = {
    .name       = "usb_otg_hs",
    .class      = &omap44xx_usb_otg_hs_hwmod_class,
    .clkdm_name = "l3_init_clkdm",
    .flags      = HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY,
    .mpu_irqs   = omap44xx_usb_otg_hs_irqs,
    .main_clk   = "usb_otg_hs_ick",
    .prcm = {
        .omap4 = {
            .clkctrl_offs = OMAP4_CM_L3INIT_USB_OTG_CLKCTRL_OFFSET,
            .context_offs = OMAP4_RM_L3INIT_USB_OTG_CONTEXT_OFFSET,
            .modulemode   = MODULEMODE_HWCTRL,
        },
    },
    .opt_clks   = usb_otg_hs_opt_clks,
    .opt_clks_cnt   = ARRAY_SIZE(usb_otg_hs_opt_clks),
    .slaves     = omap44xx_usb_otg_hs_slaves,
    .slaves_cnt = ARRAY_SIZE(omap44xx_usb_otg_hs_slaves),//这里就是资源
    .masters    = omap44xx_usb_otg_hs_masters,
    .masters_cnt    = ARRAY_SIZE(omap44xx_usb_otg_hs_masters),
};
static struct omap_hwmod_addr_space omap44xx_usb_otg_hs_addrs[] = {
    {
        .pa_start   = 0x4a0ab000,
        .pa_end     = 0x4a0ab003,
        .flags      = ADDR_TYPE_RT
    },
    { }
};

到此
omap2430_probe
—–>platform_device_add_resources(musb, pdev->resource,
pdev->num_resources);
—–>这里面的resource,在内核早期初始化的时候已经被赋值了,其中断资源、地址资源都定义在了omap44xx_usb_otg_hs_hwmod ,其中IO地址资源就是

    .pa_start   = 0x4a0ab000,
    .pa_end     = 0x4a0ab003,

综上分析,在omap2430_probe中主要完成下列功能:

1. 申请一个名字为musb-hdrc的platform_device。    
 musb = platform_device_alloc("musb-hdrc", -1);
2.musb的dev->p-driver_data=glue.//glue是个很关键的变量。它记录了usb的模式,包含了usb的工作队列,和wake_lock。
3.musb的dev.platform_data=musb_plat
4.把上述musb 用platform_device_register注册到系统中。

到此为止,我们分析了名字为”musb-omap2430”的platform_devices和platform_driver的probe函数。并且在这个probe函数里注册了名字为
“musb-hdrc”的platform_devices.且,这个platform_device的内容为:


简单地说就是platform_device_register一个platform_device,这个devices的内容如下:
struct platform_device  musb={
    .name= "musb-hdrc"
    .id= -1,
    .dev.parent= 名字为"musb-omap2430"platform_devices的dev//这就意味着/sys/devices/platform/.../musb_omap2430目录下将会出现一个musb-hdrc的文件夹。
    .dev.dma_mask= &omap2430_dmamask,
    .dev.coherent_dma_mask  = omap2430_dmamask,
    .dev->p->driver_data=glue,//glue
    .dev.platform_data=musb_plat.//platform_data.
    .resouce={.start=0x4a0ab000,.end=0x4a0ab003}
};
其中:musb_plat
static struct musb_hdrc_platform_data musb_plat = {
    .mode       = MUSB_HOST,
    .config     = &musb_config,
    .power      = 50>>1,            /* up to 100 mA */
    .clock = "ick";
    .board_data=&musb_plat,
    .extvbus=//暂时不知道等于什么。
    .platform_ops= &omap2430_ops; 
};
static struct musb_hdrc_config musb_config = {
    .multipoint = 1,
    .dyn_fifo   = 1,
    .num_eps    = 16,
    .ram_bits   = 12,
};
glue的内容为:
    glue->dev           = &pdev->dev;
    glue->musb          = musb;
    glue->status            = OMAP_MUSB_UNKNOWN;
INIT_WORK(&glue->omap_musb_mailbox_work, omap_musb_mailbox_work);
    wake_lock_init(&glue->omap_musb_wakelock, WAKE_LOCK_SUSPEND,
               "omap_musb_wakelock");

很显然,系统中应该还存在一个名字为”musb-hdrc”的platform_driver.
搜索”musb-hdrc”。
在driver/usb/musb/musb_core.c中发现了

在musb_core.c中

#define MUSB_DRIVER_NAME "musb-hdrc"
const char musb_driver_name[] = MUSB_DRIVER_NAME;
static struct platform_driver musb_driver = {
    .driver = {
        .name       = (char *)musb_driver_name,
        .bus        = &platform_bus_type,
        .owner      = THIS_MODULE,
        .pm     = MUSB_DEV_PM_OPS,
    },
    .probe      = musb_probe,
    .remove     = __devexit_p(musb_remove),
    .shutdown   = musb_shutdown,
};

module_init(musb_init);
static int __init musb_init(void)
{
    if (usb_disabled())
        return 0;

    musb_power_gpio = gpio_request(MUSB_POWER_EN, "usb_power_en");
    if (musb_power_gpio != 0) {
        printk("not able to acquire gpio %d\n", MUSB_POWER_EN);
    } else {
        gpio_direction_output(MUSB_POWER_EN, 1);
    }

    pr_info("%s: version " MUSB_VERSION ", ?dma?, otg (peripheral+host)\n",
        musb_driver_name);
    return platform_driver_register(&musb_driver);
}

下面看一下它的probe函数。

static int __devinit musb_probe(struct platform_device *pdev)
{
    struct device   *dev = &pdev->dev;
    int     irq = platform_get_irq_byname(pdev, "mc");
    int     status;
    struct resource *iomem;
    void __iomem    *base;
    iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!iomem || irq <= 0)
        return -ENODEV;

    base = ioremap(iomem->start, resource_size(iomem));
    if (!base) {
        dev_err(dev, "ioremap failed\n");
        return -ENOMEM;
    }
    printk("%s %s %d musb iomem->start is %p,base is %p,MUSB_DEVCTL=%d\n",__FILE__,__func__,__LINE__,iomem->start,base,MUSB_DEVCTL);//0x4a0ab000
#ifndef CONFIG_MUSB_PIO_ONLY
    /* clobbered by use_dma=n */
    orig_dma_mask = dev->dma_mask;
#endif

    status = musb_init_controller(dev, irq, base);
    if (status < 0){
        printk("musb_probe Failure\n");
        iounmap(base);
    }
    else if(status==0){
        printk("musb_probe success\n");
    }
    return status;
}

最重要的一个函数

static int __devinit
musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
{
    int         status;
    struct musb     *musb;
    struct musb_hdrc_platform_data *plat = dev->platform_data;

    /* The driver might handle more features than the board; OK.
     * Fail when the board needs a feature that's not enabled.
     */
    if (!plat) {
        dev_dbg(dev, "no platform_data?\n");
        status = -ENODEV;
        goto fail0;
    }

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

    pm_runtime_use_autosuspend(musb->controller);
    pm_runtime_set_autosuspend_delay(musb->controller, 200);
    pm_runtime_enable(musb->controller);

    spin_lock_init(&musb->lock);
    musb->board_mode = plat->mode;
    musb->board_set_power = plat->set_power;
    musb->min_power = plat->min_power;
    musb->ops = plat->platform_ops;

    /* The musb_platform_init() call:
     *   - adjusts musb->mregs and musb->isr if needed,
     *   - may initialize an integrated tranceiver
     *   - initializes musb->xceiv, usually by otg_get_phy()
     *   - stops powering VBUS
     *
     * There are various transceiver configurations.  Blackfin,
     * DaVinci, TUSB60x0, and others integrate them.  OMAP3 uses
     * external/discrete ones in various flavors (twl4030 family,
     * isp1504, non-OTG, etc) mostly hooking up through ULPI.
     */
    musb->isr = generic_interrupt;
    status = musb_platform_init(musb);
    if (status < 0)
        goto fail1;

    if (!musb->isr) {
        status = -ENODEV;
        goto fail2;
    }

    if (!musb->xceiv->io_ops) {
        musb->xceiv->io_dev = musb->controller;
        musb->xceiv->io_priv = musb->mregs;
        musb->xceiv->io_ops = &musb_ulpi_access;
    }

    pm_runtime_get_sync(musb->controller);

#ifndef CONFIG_MUSB_PIO_ONLY
    if (use_dma && dev->dma_mask) {
        struct dma_controller   *c;

        c = dma_controller_create(musb, musb->mregs);
        musb->dma_controller = c;
        if (c)
            (void) c->start(c);
    }
#endif
    /* ideally this would be abstracted in platform setup */
    if (!is_dma_capable() || !musb->dma_controller)
        dev->dma_mask = NULL;

    /* be sure interrupts are disabled before connecting ISR */
    musb_platform_disable(musb);
    musb_generic_disable(musb);

    /* setup musb parts of the core (especially endpoints) */
    status = musb_core_init(plat->config->multipoint
            ? MUSB_CONTROLLER_MHDRC
            : MUSB_CONTROLLER_HDRC, musb);
    if (status < 0)
        goto fail3;

    setup_timer(&musb->otg_timer, musb_otg_timer_func, (unsigned long) musb);
    setup_timer(&musbmode_cuttimer,modecut_delay_ms, (unsigned long)musb);

    /* Init IRQ workqueue before request_irq */
    INIT_WORK(&musb->irq_work, musb_irq_work);

    /* attach to the IRQ */
    if (request_irq(nIrq, musb->isr, 0, dev_name(dev), musb)) {
        dev_err(dev, "request_irq %d failed!\n", nIrq);
        status = -ENODEV;
        goto fail3;
    }
    musb->nIrq = nIrq;
/* FIXME this handles wakeup irqs wrong */
    if (enable_irq_wake(nIrq) == 0) {
        musb->irq_wake = 1;
        device_init_wakeup(dev, 1);
    } else {
        musb->irq_wake = 0;
    }

    /* host side needs more setup */
    if (is_host_enabled(musb)) {
        struct usb_hcd  *hcd = musb_to_hcd(musb);

        otg_set_host(musb->xceiv->otg, &hcd->self);

        if (is_otg_enabled(musb))
            hcd->self.otg_port = 1;
        musb->xceiv->otg->host = &hcd->self;
        hcd->power_budget = 2 * (plat->power ? : 250);

        /* program PHY to use external vBus if required */
        if (plat->extvbus) {
            u8 busctl = musb_read_ulpi_buscontrol(musb->mregs);
            busctl |= MUSB_ULPI_USE_EXTVBUS;
            musb_write_ulpi_buscontrol(musb->mregs, busctl);
        }
    }

    /* For the host-only role, we can activate right away.
     * (We expect the ID pin to be forcibly grounded!!)
     * Otherwise, wait till the gadget driver hooks up.
     */
    if (!is_otg_enabled(musb) && is_host_enabled(musb)) {
        struct usb_hcd  *hcd = musb_to_hcd(musb);

        MUSB_HST_MODE(musb);
        musb->xceiv->otg->default_a = 1;
        musb->xceiv->state = OTG_STATE_A_IDLE;

        status = usb_add_hcd(musb_to_hcd(musb), 0, 0);

        hcd->self.uses_pio_for_control = 1;
        dev_dbg(musb->controller, "%s mode, status %d, devctl %02x %c\n",
            "HOST", status,
            musb_readb(musb->mregs, MUSB_DEVCTL),
            (musb_readb(musb->mregs, MUSB_DEVCTL)
                    & MUSB_DEVCTL_BDEVICE
                ? 'B' : 'A'));

    } else /* peripheral is enabled */ {
        MUSB_DEV_MODE(musb);
        musb->xceiv->otg->default_a = 0;
        musb->xceiv->state = OTG_STATE_B_IDLE;

        status = musb_gadget_setup(musb);

        dev_dbg(musb->controller, "%s mode, status %d, dev%02x\n",
            is_otg_enabled(musb) ? "OTG" : "PERIPHERAL",
            status,
            musb_readb(musb->mregs, MUSB_DEVCTL));

    }
    if (status < 0)
        goto fail3;

    if (is_otg_enabled(musb) || is_host_enabled(musb))
        wake_lock_init(&musb->musb_wakelock, WAKE_LOCK_SUSPEND,
                        "musb_autosuspend_wake_lock");

    status = musb_init_debugfs(musb);
    if (status < 0)
        goto fail4;

#ifdef CONFIG_SYSFS
    status = sysfs_create_group(&musb->controller->kobj, &musb_attr_group);
    if (status)
        goto fail5;
#endif

    pm_runtime_put(musb->controller);

    dev_info(dev, "USB %s mode controller at %p using %s, IRQ %d\n",
            ({char *s;
             switch (musb->board_mode) {
             case MUSB_HOST:        s = "Host"; break;
             case MUSB_PERIPHERAL:  s = "Peripheral"; break;
             default:       s = "OTG"; break;
             }; s; }),
            ctrl,
            (is_dma_capable() && musb->dma_controller)
            ? "DMA" : "PIO",
            musb->nIrq);

    return 0;

fail5:
    musb_exit_debugfs(musb);

fail4:
    if (!is_otg_enabled(musb) && is_host_enabled(musb))
        usb_remove_hcd(musb_to_hcd(musb));
    else
        musb_gadget_cleanup(musb);

    if (is_otg_enabled(musb) || is_host_enabled(musb))
        wake_lock_destroy(&musb->musb_wakelock);

fail3:
    pm_runtime_put_sync(musb->controller);

fail2:
    if (musb->irq_wake)
        device_init_wakeup(dev, 0);
    musb_platform_exit(musb);

fail1:
    dev_err(musb->controller,
        "musb_init_controller failed with status %d\n", status);

    musb_free(musb);

fail0:

    return status;

}

其中注册中断,otg的timer,musb_irq_work,debugfs、文件结点都在这个函数里面:

    musb->isr = generic_interrupt;
    status = musb_platform_init(musb);
    setup_timer(&musb->otg_timer, musb_otg_timer_func, (unsigned long) musb);
    setup_timer(&musbmode_cuttimer,modecut_delay_ms, (unsigned long)musb);

    /* Init IRQ workqueue before request_irq */
    INIT_WORK(&musb->irq_work, musb_irq_work);

    /* attach to the IRQ */
    if (request_irq(nIrq, musb->isr, 0, dev_name(dev), musb)) {
        dev_err(dev, "request_irq %d failed!\n", nIrq);
        status = -ENODEV;
        goto fail3;
    }
    musb->nIrq = nIrq;

    status = musb_init_debugfs(musb);
    if (status < 0)
        goto fail4;

#ifdef CONFIG_SYSFS
    status = sysfs_create_group(&musb->controller->kobj, &musb_attr_group);
    if (status)
        goto fail5;
#endif

上面说的有点乱,现在总结一下:
这里写图片描述
最终注册了一个platform_device。
|
|
|
这里写图片描述

接下来在omap2430.c中定义了同name的platform_driver
并且在它的probe函数中又创建了一个platform_device。
这里写图片描述
这里写图片描述
接下来的章节要研究这几个timer以及中断函数generic_interrupt。

发布了21 篇原创文章 · 获赞 5 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/linchuanzhi_886/article/details/48464367