平台: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。