上回介紹了otg功能的 otg部分驅動,本片分析otg功能的從裝置部分驅動。從裝置的注冊過程和otg的一樣,首先注冊裝置。
流程是:
1.定義platform_device結構。
2.定義platform_device下的struct resource裝置資源結構
3.定義platform_device下的dev裝置下的平台私有資料(就是該裝置私有的資料)
4.調用platform_device_register将platform_device結構
注冊上面4個過程調用結束後,裝置的資訊就被注冊到系統中,等待驅動的使用 下面是按照上面順序貼出代碼
定義platform_device結構
<code>static struct platform_device __maybe_unused dr_udc_device = { .name = "fsl-usb2-udc", .id = -1, .dev = { .release = dr_udc_release, .dma_mask = &dr_udc_dmamask, .coherent_dma_mask = 0xffffffff, }, .resource = otg_resources, .num_resources = array_size(otg_resources), };</code>
<code>我們可以看到resource和otg的resource一樣,使用的都是otg那部分資源</code>
定義platform_device下的struct resource裝置資源結構
<code>static struct resource otg_resources[] = { [0] = { .start = (u32)(usb_otgregs_base), .end = (u32)(usb_otgregs_base + 0x1ff), .flags = ioresource_mem, }, [1] = { .start = mxc_int_usb_otg, .flags = ioresource_irq, }, };</code>
定義platform_device下的dev裝置下的平台私有資料(就是該裝置私有的資料)
<code>static struct fsl_usb2_platform_data __maybe_unused dr_utmi_config = { .name = "dr", .platform_init = usbotg_init, .platform_uninit = usbotg_uninit, .phy_mode = fsl_usb2_phy_utmi_wide, .power_budget = 500, /* via rt9706 */ .gpio_usb_active = gpio_usbotg_utmi_active, .gpio_usb_inactive = gpio_usbotg_utmi_inactive, .transceiver = "utmi", .wake_up_enable = _wake_up_enable, }; /*将裝置注冊進系統*/ static inline void dr_register_udc(void) { pdata->operating_mode = dr_udc_mode; //在otg功能裝置注冊的時候這裡的模式是“fsl_usb2_dr_otg”,時還不知道為什麼不開始就直接把模式寫成這個而是後該,現在知道了 因為主從和otg都在用一個資源,是以誰用就的把模式該了。</code>
<code>/*#define pdata (&dr_utmi_config)pdata指的就是上面的結構 */ dr_udc_device.dev.platform_data = pdata; if (platform_device_register(&dr_udc_device)) printk(kern_err "usb: can't register dr gadget\n"); else printk(kern_info "usb: dr gadget (%s) registered\n", pdata->transceiver); }</code>
上面就完成了裝置的注冊。
下面看看驅動和裝置的連結。
<code>static struct platform_driver udc_driver = { .remove = __exit_p(fsl_udc_remove), /* these suspend and resume are not usb suspend and resume */ .suspend = fsl_udc_suspend, //切換到主裝置後的處理過程 .resume = fsl_udc_resume, //切換到從裝置的處理過程 .probe = fsl_udc_probe, .driver = { .name = driver_name, //就是fsl-usb2-udc,用于比對裝置的 .owner = this_module, }, }</code>
<code></code>
将驅動注冊進系統,如果發現了裝置就調用probe函數,因為我們上面注冊了裝置是以必然調用probe
<code>static int __init udc_init(void) { printk(kern_info "%s (%s)\n", driver_desc, driver_version); return platform_driver_register(&udc_driver);//驅動注冊進系統 }</code>
<code>發現裝置後調用probe函數 static int __init fsl_udc_probe(struct platform_device *pdev) { struct resource *res; struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; int ret = -enodev; unsigned int i; u32 dccparams, portsc; if (strcmp(pdev->name, driver_name)) { vdbg("wrong device\n"); return -enodev; } /********************************************************/</code>
<code>static struct fsl_udc *udc_controller;</code>
<code>全局變量定義</code>
<code>/*******************************************************/ udc_controller = kzalloc(sizeof(struct fsl_udc), gfp_kernel); if (udc_controller == null) { err("malloc udc failed\n"); return -enomem; } udc_controller->pdata = pdata; //私有變量指派 #ifdef config_usb_otg /* memory and interrupt resources will be passed from otg */ udc_controller->transceiver = otg_get_transceiver();//在otg功能中已經通過otg_set_transceiver 設定了transceiver結構,這裡面就可以get</code>
<code></code>
if (!udc_controller->transceiver) {
printk(kern_err "can't find otg driver!\n");
ret = -enodev;
goto err1a;
}
res = otg_get_resources(); //獲得資源
if (!res) {
dbg("resource not registered!\n");
return -enodev;
#else
if ((pdev->dev.parent) &&
(to_platform_device(pdev->dev.parent)->resource)) {
pdev->resource =
to_platform_device(pdev->dev.parent)->resource;
pdev->num_resources =
to_platform_device(pdev->dev.parent)->num_resources;
res = platform_get_resource(pdev, ioresource_mem, 0);
ret = -enxio;
if (!request_mem_region(res->start, resource_size(res),
driver_name)) {
err("request mem region for %s failed \n", pdev->name);
ret = -ebusy;
#endif
/*将實體位址映射為驅動可以通路的虛拟位址*/
dr_regs = ioremap(res->start, resource_size(res));
if (!dr_regs) {
ret = -enomem;
goto err1;
pdata->regs = (void *)dr_regs; //私有資料接收映射位址
/*
* do platform specific init: check the clock, grab/config pins, etc.
*/
/*調用私有資料的初始化函數,關于初始化函數下面分析*/
if (pdata->platform_init && pdata->platform_init(pdev)) {
goto err2a;
if (pdata->have_sysif_regs)
usb_sys_regs = (struct usb_sys_interface *)
((u32)dr_regs + usb_dr_sys_offset);
/* read device controller capability parameters register */
dccparams = fsl_readl(&dr_regs->dccparams);
if (!(dccparams & dccparams_dc)) {
err("this soc doesn't support device role\n");
goto err2;
/* get max device endpoints */
/* den is bidirectional ep number, max_ep doubles the number */
udc_controller->max_ep = (dccparams & dccparams_den_mask) * 2;
#ifdef config_usb_otg
res++;
udc_controller->irq = res->start;
udc_controller->irq = platform_get_irq(pdev, 0);
if (!udc_controller->irq) {
/*注冊中斷,該中斷和otg的中斷共享一個*/
ret = request_irq(udc_controller->irq, fsl_udc_irq, irqf_shared,
driver_name, udc_controller);
if (ret != 0) {
err("cannot request irq %d err %d \n",
udc_controller->irq, ret);
/* initialize the udc structure including qh member and other member */
<code>/*對一些資源進行空間開辟等初始化操作*/ if (struct_udc_setup(udc_controller, pdev)) { err("can't initialize udc data structure\n"); ret = -enomem; goto err3; } if (!udc_controller->transceiver) { /* initialize usb hw reg except for regs for ep, * leave usbintr reg untouched */ dr_controller_setup(udc_controller); } /* setup gadget structure */ udc_controller->gadget.ops = &fsl_gadget_ops; udc_controller->gadget.is_dualspeed = 1; udc_controller->gadget.ep0 = &udc_controller->eps[0].ep; init_list_head(&udc_controller->gadget.ep_list); udc_controller->gadget.speed = usb_speed_unknown; udc_controller->gadget.name = driver_name; /* setup gadget.dev and register with kernel */ dev_set_name(&udc_controller->gadget.dev, "gadget"); udc_controller->gadget.dev.release = fsl_udc_release; udc_controller->gadget.dev.parent = &pdev->dev; ret = device_register(&udc_controller->gadget.dev); if (ret < 0) goto err3; if (udc_controller->transceiver) { udc_controller->gadget.is_otg = 1; /* now didn't support lpm in otg mode*/ device_set_wakeup_capable(&pdev->dev, 0); } /* setup qh and epctrl for ep0 */ ep0_setup(udc_controller); /* setup udc->eps[] for ep0 */ struct_ep_setup(udc_controller, 0, "ep0", 0); /* for ep0: the desc defined here; * for other eps, gadget layer called ep_enable with defined desc */ udc_controller->eps[0].desc = &fsl_ep0_desc; udc_controller->eps[0].ep.maxpacket = usb_max_ctrl_payload; /* setup the udc->eps[] for non-control endpoints and link * to gadget.ep_list */ for (i = 1; i < (int)(udc_controller->max_ep / 2); i++) { char name[14]; sprintf(name, "ep%dout", i); struct_ep_setup(udc_controller, i * 2, name, 1); sprintf(name, "ep%din", i); struct_ep_setup(udc_controller, i * 2 + 1, name, 1); } /* use dma_pool for td management */ udc_controller->td_pool = dma_pool_create("udc_td", &pdev->dev, sizeof(struct ep_td_struct), dtd_alignment, udc_dma_boundary); if (udc_controller->td_pool == null) { ret = -enomem; goto err4; } if (g_iram_size) { for (i = 0; i < iram_pph_ntd; i++) { udc_controller->iram_buffer[i] = usb_iram_base_addr + i * g_iram_size; udc_controller->iram_buffer_v[i] = io_address(udc_controller->iram_buffer[i]); } } #ifdef postpone_free_last_dtd last_free_td = null; #endif /* disable all intr */ fsl_writel(0, &dr_regs->usbintr); dr_wake_up_enable(udc_controller, false); udc_controller->stopped = 1; portsc = fsl_readl(&dr_regs->portsc1); portsc |= portscx_phy_low_power_spd; fsl_writel(portsc, &dr_regs->portsc1); if (udc_controller->pdata->usb_clock_for_pm) udc_controller->pdata->usb_clock_for_pm(false); create_proc_file(); return 0; err4: device_unregister(&udc_controller->gadget.dev); err3: free_irq(udc_controller->irq, udc_controller); err2: if (pdata->platform_uninit) pdata->platform_uninit(pdata); err2a: iounmap((u8 __iomem *)dr_regs); err1: if (!udc_controller->transceiver) release_mem_region(res->start, resource_size(res)); err1a: kfree(udc_controller); udc_controller = null; return ret; }</code>
當裝置使用主裝置時 device的處理
<code>static int udc_suspend(struct fsl_udc *udc) { u32 mode, usbcmd; /* open clock for register access */ if (udc_controller->pdata->usb_clock_for_pm) udc_controller->pdata->usb_clock_for_pm(true); mode = fsl_readl(&dr_regs->usbmode) & usb_mode_ctrl_mode_mask; usbcmd = fsl_readl(&dr_regs->usbcmd); pr_debug("%s(): mode 0x%x stopped %d\n", __func__, mode, udc->stopped); /* * if the controller is already stopped, then this must be a * pm suspend. remember this fact, so that we will leave the * controller stopped at pm resume time. */ if (udc->stopped) { pr_debug("gadget already stopped, leaving early\n"); udc->already_stopped = 1; goto out; } if (mode != usb_mode_ctrl_mode_device) { pr_debug("gadget not in device mode, leaving early\n"); goto out; } udc->stopped = 1; /* if the suspend is not for switch to host in otg mode */ if ((!(udc->gadget.is_otg)) || (fsl_readl(&dr_regs->otgsc) & otgsc_sts_usb_id)) { dr_wake_up_enable(udc, true); dr_phy_low_power_mode(udc, true); } /* stop the controller */ usbcmd = fsl_readl(&dr_regs->usbcmd) & ~usb_cmd_run_stop; fsl_writel(usbcmd, &dr_regs->usbcmd); printk(kern_info "usb gadget suspended\n"); out: if (udc_controller->pdata->usb_clock_for_pm) udc_controller->pdata->usb_clock_for_pm(false); return 0; }</code>
當切換到從裝置時調用
<code>static int fsl_udc_resume(struct platform_device *pdev) { pr_debug("%s(): stopped %d already_stopped %d\n", __func__, udc_controller->stopped, udc_controller->already_stopped); /* * if the controller was stopped at suspend time, then * don't resume it now. */ if (udc_controller->already_stopped) { udc_controller->already_stopped = 0; pr_debug("gadget was already stopped, leaving early\n"); return 0; } /* enable dr irq reg and set controller run */ if (udc_controller->stopped) { dr_wake_up_enable(udc_controller, false); dr_phy_low_power_mode(udc_controller, false); mdelay(1); dr_controller_setup(udc_controller); dr_controller_run(udc_controller); } udc_controller->usb_state = usb_state_attached; udc_controller->ep0_dir = 0; printk(kern_info "usb gadget resumed\n"); return 0; }</code>
上面的兩個函數就是在上一篇otg 一中介紹的
gadget_pdrv->resume(gadget_pdev);
gadget_pdrv->suspend(gadget_pdev, otg_suspend_state);
這兩處就是隻想的這個函數。而把前面的函數和上面這兩個指針連結的地方就是在otg中的
fsl_otg_start_gadget函數。
從上面我們可以看到以下幾點:
1.otg功能的從裝置使用的資源和私有資料與otg裝置的一緻,(主裝置也是一緻)
2.從裝置主要為otg功能提供fsl_udc_resume和udc_suspend兩個函數。