天天看點

OTG驅動分析(二)

上回介紹了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 = &amp;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-&gt;operating_mode = dr_udc_mode; //在otg功能裝置注冊的時候這裡的模式是“fsl_usb2_dr_otg”,時還不知道為什麼不開始就直接把模式寫成這個而是後該,現在知道了 因為主從和otg都在用一個資源,是以誰用就的把模式該了。</code>

<code>/*#define pdata (&amp;dr_utmi_config)pdata指的就是上面的結構 */     dr_udc_device.dev.platform_data = pdata;     if (platform_device_register(&amp;dr_udc_device))         printk(kern_err "usb: can't register dr gadget\n");     else         printk(kern_info "usb: dr gadget (%s) registered\n",          pdata-&gt;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(&amp;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-&gt;dev.platform_data;     int ret = -enodev;     unsigned int i;     u32 dccparams, portsc;     if (strcmp(pdev-&gt;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-&gt;pdata = pdata; //私有變量指派 #ifdef config_usb_otg     /* memory and interrupt resources will be passed from otg */     udc_controller-&gt;transceiver = otg_get_transceiver();//在otg功能中已經通過otg_set_transceiver 設定了transceiver結構,這裡面就可以get</code>

<code></code>

    if (!udc_controller-&gt;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-&gt;dev.parent) &amp;&amp;

        (to_platform_device(pdev-&gt;dev.parent)-&gt;resource)) {

        pdev-&gt;resource =

            to_platform_device(pdev-&gt;dev.parent)-&gt;resource;

        pdev-&gt;num_resources =

            to_platform_device(pdev-&gt;dev.parent)-&gt;num_resources;

    res = platform_get_resource(pdev, ioresource_mem, 0);

        ret = -enxio;

    if (!request_mem_region(res-&gt;start, resource_size(res),

                driver_name)) {

        err("request mem region for %s failed \n", pdev-&gt;name);

        ret = -ebusy;

#endif

/*将實體位址映射為驅動可以通路的虛拟位址*/

    dr_regs = ioremap(res-&gt;start, resource_size(res));

    if (!dr_regs) {

        ret = -enomem;

        goto err1;

    pdata-&gt;regs = (void *)dr_regs; //私有資料接收映射位址

    /*

     * do platform specific init: check the clock, grab/config pins, etc.

     */

/*調用私有資料的初始化函數,關于初始化函數下面分析*/

    if (pdata-&gt;platform_init &amp;&amp; pdata-&gt;platform_init(pdev)) {

        goto err2a;

    if (pdata-&gt;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(&amp;dr_regs-&gt;dccparams);

    if (!(dccparams &amp; 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-&gt;max_ep = (dccparams &amp; dccparams_den_mask) * 2;

#ifdef config_usb_otg

    res++;

    udc_controller-&gt;irq = res-&gt;start;

    udc_controller-&gt;irq = platform_get_irq(pdev, 0);

    if (!udc_controller-&gt;irq) {

   /*注冊中斷,該中斷和otg的中斷共享一個*/

    ret = request_irq(udc_controller-&gt;irq, fsl_udc_irq, irqf_shared,

            driver_name, udc_controller);

    if (ret != 0) {

        err("cannot request irq %d err %d \n",

                udc_controller-&gt;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-&gt;transceiver) {         /* initialize usb hw reg except for regs for ep,          * leave usbintr reg untouched */         dr_controller_setup(udc_controller);     }     /* setup gadget structure */     udc_controller-&gt;gadget.ops = &amp;fsl_gadget_ops;     udc_controller-&gt;gadget.is_dualspeed = 1;     udc_controller-&gt;gadget.ep0 = &amp;udc_controller-&gt;eps[0].ep;     init_list_head(&amp;udc_controller-&gt;gadget.ep_list);     udc_controller-&gt;gadget.speed = usb_speed_unknown;     udc_controller-&gt;gadget.name = driver_name;     /* setup gadget.dev and register with kernel */     dev_set_name(&amp;udc_controller-&gt;gadget.dev, "gadget");     udc_controller-&gt;gadget.dev.release = fsl_udc_release;     udc_controller-&gt;gadget.dev.parent = &amp;pdev-&gt;dev;     ret = device_register(&amp;udc_controller-&gt;gadget.dev);     if (ret &lt; 0)         goto err3;     if (udc_controller-&gt;transceiver) {         udc_controller-&gt;gadget.is_otg = 1;         /* now didn't support lpm in otg mode*/         device_set_wakeup_capable(&amp;pdev-&gt;dev, 0);     }     /* setup qh and epctrl for ep0 */     ep0_setup(udc_controller);     /* setup udc-&gt;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-&gt;eps[0].desc = &amp;fsl_ep0_desc;     udc_controller-&gt;eps[0].ep.maxpacket = usb_max_ctrl_payload;     /* setup the udc-&gt;eps[] for non-control endpoints and link      * to gadget.ep_list */     for (i = 1; i &lt; (int)(udc_controller-&gt;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-&gt;td_pool = dma_pool_create("udc_td", &amp;pdev-&gt;dev,             sizeof(struct ep_td_struct),             dtd_alignment, udc_dma_boundary);     if (udc_controller-&gt;td_pool == null) {         ret = -enomem;         goto err4;     }     if (g_iram_size) {         for (i = 0; i &lt; iram_pph_ntd; i++) {             udc_controller-&gt;iram_buffer[i] =              usb_iram_base_addr + i * g_iram_size;             udc_controller-&gt;iram_buffer_v[i] =              io_address(udc_controller-&gt;iram_buffer[i]);         }     } #ifdef postpone_free_last_dtd     last_free_td = null; #endif     /* disable all intr */     fsl_writel(0, &amp;dr_regs-&gt;usbintr);     dr_wake_up_enable(udc_controller, false);     udc_controller-&gt;stopped = 1;     portsc = fsl_readl(&amp;dr_regs-&gt;portsc1);     portsc |= portscx_phy_low_power_spd;     fsl_writel(portsc, &amp;dr_regs-&gt;portsc1);     if (udc_controller-&gt;pdata-&gt;usb_clock_for_pm)         udc_controller-&gt;pdata-&gt;usb_clock_for_pm(false);     create_proc_file();     return 0; err4:     device_unregister(&amp;udc_controller-&gt;gadget.dev); err3:     free_irq(udc_controller-&gt;irq, udc_controller); err2:     if (pdata-&gt;platform_uninit)         pdata-&gt;platform_uninit(pdata); err2a:     iounmap((u8 __iomem *)dr_regs); err1:     if (!udc_controller-&gt;transceiver)         release_mem_region(res-&gt;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-&gt;pdata-&gt;usb_clock_for_pm)         udc_controller-&gt;pdata-&gt;usb_clock_for_pm(true);     mode = fsl_readl(&amp;dr_regs-&gt;usbmode) &amp; usb_mode_ctrl_mode_mask;     usbcmd = fsl_readl(&amp;dr_regs-&gt;usbcmd);     pr_debug("%s(): mode 0x%x stopped %d\n", __func__, mode, udc-&gt;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-&gt;stopped) {         pr_debug("gadget already stopped, leaving early\n");         udc-&gt;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-&gt;stopped = 1;     /* if the suspend is not for switch to host in otg mode */     if ((!(udc-&gt;gadget.is_otg)) ||             (fsl_readl(&amp;dr_regs-&gt;otgsc) &amp; otgsc_sts_usb_id)) {         dr_wake_up_enable(udc, true);         dr_phy_low_power_mode(udc, true);     }     /* stop the controller */     usbcmd = fsl_readl(&amp;dr_regs-&gt;usbcmd) &amp; ~usb_cmd_run_stop;     fsl_writel(usbcmd, &amp;dr_regs-&gt;usbcmd);     printk(kern_info "usb gadget suspended\n"); out:     if (udc_controller-&gt;pdata-&gt;usb_clock_for_pm)         udc_controller-&gt;pdata-&gt;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-&gt;stopped, udc_controller-&gt;already_stopped);     /*      * if the controller was stopped at suspend time, then      * don't resume it now.      */     if (udc_controller-&gt;already_stopped) {         udc_controller-&gt;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-&gt;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-&gt;usb_state = usb_state_attached;     udc_controller-&gt;ep0_dir = 0;     printk(kern_info "usb gadget resumed\n");     return 0; }</code>

上面的兩個函數就是在上一篇otg 一中介紹的

gadget_pdrv-&gt;resume(gadget_pdev);

gadget_pdrv-&gt;suspend(gadget_pdev, otg_suspend_state);

這兩處就是隻想的這個函數。而把前面的函數和上面這兩個指針連結的地方就是在otg中的

fsl_otg_start_gadget函數。

從上面我們可以看到以下幾點:

1.otg功能的從裝置使用的資源和私有資料與otg裝置的一緻,(主裝置也是一緻)

2.從裝置主要為otg功能提供fsl_udc_resume和udc_suspend兩個函數。

繼續閱讀