USB HOST DWC3 初始化

https://www.cnblogs.com/newjiang/p/15675746.html

如果dr_mode为device,则初始化gadget。

如果dr_mode为host,需要初始化xHCI驱动。在dwc3_host_init函数的最后调用platform_device_add(xhci)添加platform device(xhci-hcd),用于匹配xHCI driver(xHCI driver为platform driver),参照第3节。

如果dr_mode为otg,需要根据extcon来选定一个角色(host或者device)进行初始化,所以还需要extcon驱动的支持,参照第2节

https://blog.youkuaiyun.com/z1026544682/article/details/101023041?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_utm_term~default-8-101023041-blog-124888836.235^v43^pc_blog_bottom_relevance_base6&spm=1001.2101.3001.4242.5&utm_relevant_index=11

对于设备树节点中的reg属性(地址),通过type=IORESOURCE_MEM来获得;

对于设备树节点中的interrupts属性(中断),通过type=IORESOURCE_IRQ来获得;

怎么获取转换为platform_device的设备树节点中的非标准属性,比如pin(pin=xxx)?

对于根节点"/ { };",会保存在一个全局变量of_root里面( device_node类型)。可以通过访问该全局变量(代表根节点),得到任意一个节点,得到节点后可以读出属性。

于是,可以使用内核提供的函数,直接访问device_node,从而读出这些属性。

这些函数可以分为3来:找到节点,找到属性,获取属性的值。这些函数位于内核源码include/linux/of.h。

       /* Tertiary USB port related controller */
        usb2: ssusb@a400000 {
                compatible = "qcom,dwc-usb3-msm";
                reg = <0xa400000 0x100000>;
                reg-names = "core_base";

                iommus = <&apps_smmu 0x0800 0x0>;
                qcom,iommu-dma = "bypass";

                #address-cells = <1>;
                #size-cells = <1>;
                ranges;
                dma-ranges;

                interrupts-extended = <&pdc 127 IRQ_TYPE_EDGE_RISING>,
                                <&pdc 126 IRQ_TYPE_EDGE_RISING>,
                                <&pdc 129 IRQ_TYPE_EDGE_RISING>,
                                <&pdc 128 IRQ_TYPE_EDGE_RISING>,
                                <&pdc 131 IRQ_TYPE_EDGE_RISING>,
                                <&pdc 130 IRQ_TYPE_EDGE_RISING>,
                                <&pdc 133 IRQ_TYPE_EDGE_RISING>,
                                <&pdc 132 IRQ_TYPE_EDGE_RISING>,
                                <&pdc 16 IRQ_TYPE_LEVEL_HIGH>,
                                <&pdc 17 IRQ_TYPE_LEVEL_HIGH>;
                interrupt-names = "dp_hs_phy_irq", "dm_hs_phy_irq",
                                "dp_hs_phy_irq1", "dm_hs_phy_irq1",
                                "dp_hs_phy_irq2", "dm_hs_phy_irq2",
                                "dp_hs_phy_irq3", "dm_hs_phy_irq3",
                                "ss_phy_irq", "ss_phy_irq1";
                qcom,use-pdc-interrupts;

                USB3_GDSC-supply = <&gcc_usb30_mp_gdsc>;
                clocks = <&gcc GCC_USB30_MP_MASTER_CLK>,
                        <&gcc GCC_CFG_NOC_USB3_MP_AXI_CLK>,
                        <&gcc GCC_AGGRE_USB3_MP_AXI_CLK>,
                        <&gcc GCC_USB30_MP_MOCK_UTMI_CLK>,
                        <&gcc GCC_USB30_MP_SLEEP_CLK>,
                        <&gcc GCC_AGGRE_USB_NOC_AXI_CLK>,
                        <&gcc GCC_AGGRE_USB_NOC_NORTH_AXI_CLK>,
                        <&gcc GCC_AGGRE_USB_NOC_SOUTH_AXI_CLK>,
                        <&gcc GCC_SYS_NOC_USB_AXI_CLK>;
                clock-names = "core_clk", "iface_clk", "bus_aggr_clk",
                                "utmi_clk", "sleep_clk", "noc_aggr_clk",
                                "noc_aggr_north_clk", "noc_aggr_south_clk",
                                "noc_sys_clk";

                resets = <&gcc GCC_USB30_MP_BCR>;
                reset-names = "core_reset";

                qcom,core-clk-rate = <200000000>;
                qcom,ignore-wakeup-src-in-hostmode;

                status = "disabled";

                dwc3@a400000 {
                        compatible = "snps,dwc3";
                        reg = <0xa400000 0xd93c>;
                        interrupts = <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>;
                        usb-phy = <&usb2_phy2>, <&usb_qmp_phy0>,
                                <&usb2_phy3>, <&usb_qmp_phy1>,
                                <&usb2_phy4>, <&usb_nop_phy>,
                                <&usb2_phy5>, <&usb_nop_phy>;
                        linux,sysdev_is_parent;
                        snps,disable-clk-gating;
                        snps,has-lpm-erratum;
                        snps,hird-threshold = /bits/ 8 <0x0>;
                        snps,ssp-u3-u0-quirk;
                        snps,is-utmi-l1-suspend;
                        snps,dis_u3_susphy_quirk;
                        maximum-speed = "super-speed-plus";
                        dr_mode = "host";
                };

dwc3_probe 匹配compatible = "snps,dwc3";


static int dwc3_probe(struct platform_device *pdev)
{
        struct device           *dev = &pdev->dev;
        struct resource         *res, dwc_res;
        struct dwc3             *dwc;

        int                     ret;

        void __iomem            *regs;
        int                     irq;
        char                    dma_ipc_log_ctx_name[40];

        if (count >= DWC_CTRL_COUNT) {
                dev_err(dev, "Err dwc instance %d >= %d available\n",
                                        count, DWC_CTRL_COUNT);
                ret = -EINVAL;
                return ret;
        }

        dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL);
        if (!dwc)
                return -ENOMEM;

        dwc->dev = dev;
        //解析res reg = <0xa400000 0xd93c>;
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res) {
                dev_err(dev, "missing memory resource\n");
                return -ENODEV;
        }

        dwc->reg_phys = res->start;//0xa400000
        dwc->xhci_resources[0].start = res->start;//0xa400000
        dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
                                        DWC3_XHCI_REGS_END;
        dwc->xhci_resources[0].flags = res->flags;
        dwc->xhci_resources[0].name = res->name;
        //irq = 133
        irq = platform_get_irq(to_platform_device(dwc->dev), 0);
         
        ret = devm_request_irq(dev, irq, dwc3_interrupt, IRQF_SHARED, "dwc3",
                        dwc);
        if (ret) {
                dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
                                irq, ret);
                return -ENODEV;
        }

        if (notify_event)
                /* will be enabled in dwc3_msm_resume() */
                disable_irq(irq);

        dwc->irq = irq;
        /*
         * Request memory region but exclude xHCI regs,
         * since it will be requested by the xhci-plat driver.
         */
        dwc_res = *res;
        dwc_res.start += DWC3_GLOBALS_REGS_START;
        //获取reg 虚拟地址
        regs = devm_ioremap_resource(dev, &dwc_res);
        if (IS_ERR(regs))
                return PTR_ERR(regs);

        dwc->dwc_wq = alloc_ordered_workqueue("dwc_wq", WQ_HIGHPRI);
        if (!dwc->dwc_wq) {
                dev_err(dev,
                        "%s: Unable to create workqueue dwc_wq\n", __func__);
                goto err0;
        }

        INIT_WORK(&dwc->bh_work, dwc3_bh_work);
        dwc->regs       = regs;
        dwc->regs_size  = resource_size(&dwc_res);

        dwc3_get_properties(dwc);

        dwc->reset = devm_reset_control_array_get(dev, true, true);
        if (IS_ERR(dwc->reset))
                return PTR_ERR(dwc->reset);

        if (dev->of_node) {
                ret = devm_clk_bulk_get_all(dev, &dwc->clks);
                if (ret == -EPROBE_DEFER)
                        goto err0;
                /*
                 * Clocks are optional, but new DT platforms should support all
                 * clocks as required by the DT-binding.
                 */
                if (ret < 0)
                        dwc->num_clks = 0;
                else
                        dwc->num_clks = ret;

        }

        ret = dwc3_extract_num_phys(dwc);
        if (ret) {
                dev_err(dwc->dev, "Unable to extract number of PHYs\n");
                goto err0;
        }

        dwc->usb2_phy = devm_kzalloc(dwc->dev,
                sizeof(*dwc->usb2_phy) * dwc->num_hsphy, GFP_KERNEL);

        dwc->usb3_phy = devm_kzalloc(dwc->dev,
                sizeof(*dwc->usb3_phy) * dwc->num_ssphy, GFP_KERNEL);

        ret = reset_control_deassert(dwc->reset);
        if (ret)
                goto err0;

        ret = clk_bulk_prepare_enable(dwc->num_clks, dwc->clks);
        if (ret)
                goto assert_reset;

        platform_set_drvdata(pdev, dwc);

        init_waitqueue_head(&dwc->wait_linkstate);
        spin_lock_init(&dwc->lock);

        pm_runtime_no_callbacks(dev);
        pm_runtime_set_active(dev);
        if (dwc->enable_bus_suspend) {
                pm_runtime_set_autosuspend_delay(dev,
                        DWC3_DEFAULT_AUTOSUSPEND_DELAY);
                pm_runtime_use_autosuspend(dev);
        }
        pm_runtime_enable(dev);
        pm_runtime_forbid(dev);

        ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
        if (ret) {
                dev_err(dwc->dev, "failed to allocate event buffers\n");
                ret = -ENOMEM;
                goto err1;
        }

        ret = dwc3_alloc_scratch_buffers(dwc);
        if (ret)
                goto err2;

        dwc3_debugfs_init(dwc);
        if (!notify_event) {
                ret = dwc3_core_init(dwc);
                if (ret) {
                        if (ret != -EPROBE_DEFER)
                                dev_err(dev, "failed to initialize core: %d\n",
                                                ret);
                        goto err3;
                }

                ret = dwc3_event_buffers_setup(dwc);
                if (ret) {
                        dev_err(dwc->dev, "failed to setup event buffers\n");
                        goto err3;
                }

                ret = dwc3_core_init_mode(dwc);
                if (ret) {
                        dwc3_event_buffers_cleanup(dwc);
                        goto err3;
                }
        } else if (dwc->dr_mode == USB_DR_MODE_OTG ||
                dwc->dr_mode == USB_DR_MODE_PERIPHERAL) {
                ret = dwc3_gadget_init(dwc);
                if (ret) {
                        dev_err(dwc->dev, "gadget init failed %d\n", ret);
                        goto err3;
                }
        }

        dwc->dwc_ipc_log_ctxt = ipc_log_context_create(NUM_LOG_PAGES,
                                        dev_name(dwc->dev), 0);
        if (!dwc->dwc_ipc_log_ctxt)
                dev_dbg(dwc->dev, "ipc_log_ctxt is not available\n");
        snprintf(dma_ipc_log_ctx_name, sizeof(dma_ipc_log_ctx_name),
                                        "%s.ep_events", dev_name(dwc->dev));
        dwc->dwc_dma_ipc_log_ctxt = ipc_log_context_create(2 * NUM_LOG_PAGES,
                                                dma_ipc_log_ctx_name, 0);
        if (!dwc->dwc_dma_ipc_log_ctxt)
                dev_dbg(dwc->dev, "ipc_log_ctxt for ep_events is not available\n");

        dwc3_instance[count] = dwc;
        dwc->index = count;
        count++;

        pm_runtime_allow(dev);
        return 0;

err3:
        dwc3_debugfs_exit(dwc);
        dwc3_free_scratch_buffers(dwc);
err2:
        dwc3_free_event_buffers(dwc);
err1:
        pm_runtime_allow(&pdev->dev);
        pm_runtime_disable(&pdev->dev);

        clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
assert_reset:
        reset_control_assert(dwc->reset);
        destroy_workqueue(dwc->dwc_wq);
err0:
        return ret;
}

dwc3_core_init_mode

static int __maybe_unused dwc3_core_init_mode(struct dwc3 *dwc)
{
        struct device *dev = dwc->dev;
        int ret;

        switch (dwc->dr_mode) {
        case USB_DR_MODE_PERIPHERAL:
                dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);

                if (dwc->usb2_phy[0])
                        otg_set_vbus(dwc->usb2_phy[0]->otg, false);
                phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE);
                phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE);

                ret = dwc3_gadget_init(dwc);
                if (ret) {
                        if (ret != -EPROBE_DEFER)
                                dev_err(dev, "failed to initialize gadget\n");
                        return ret;
                }

                dwc->vbus_active = true;
                break;
        case USB_DR_MODE_HOST:
                dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);

                if (dwc->usb2_phy[0])
                        otg_set_vbus(dwc->usb2_phy[0]->otg, true);
                phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
                phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);

                ret = dwc3_host_init(dwc);
                if (ret) {
                        if (ret != -EPROBE_DEFER)
                                dev_err(dev, "failed to initialize host\n");
                        return ret;
                }
                break;
        case USB_DR_MODE_OTG:
                INIT_WORK(&dwc->drd_work, __dwc3_set_mode);
                ret = dwc3_drd_init(dwc);
                if (ret) {
                        if (ret != -EPROBE_DEFER)
                                dev_err(dev, "failed to initialize dual-role\n");
                        return ret;
                }
                break;
        default:
                dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode);
                return -EINVAL;
        }

        return 0;
}

dwc3_host_init

int dwc3_host_init(struct dwc3 *dwc)
{
        struct property_entry   props[6];
        struct platform_device  *xhci;
        int                     ret, irq;
        struct resource         *res;
        struct platform_device  *dwc3_pdev = to_platform_device(dwc->dev);
        int                     prop_idx = 0;
        struct property_entry   imod_prop;
        //获取中断号133
        irq = dwc3_host_get_irq(dwc);
        if (irq < 0)
                return irq;
       
        res = platform_get_resource_byname(dwc3_pdev, IORESOURCE_IRQ, "host");
        if (!res)
                res = platform_get_resource_byname(dwc3_pdev, IORESOURCE_IRQ,
                                "dwc_usb3");
        if (!res)
                // //获取中断寄存器地址interrupts
                res = platform_get_resource(dwc3_pdev, IORESOURCE_IRQ, 0);
        if (!res)
                return -ENOMEM;

        dwc->xhci_resources[1].start = irq;
        dwc->xhci_resources[1].end = irq;
        dwc->xhci_resources[1].flags = res->flags;
        dwc->xhci_resources[1].name = res->name;
       //分配一个name=xhci-hcd platform_device结构提初始化
        xhci = platform_device_alloc("xhc
### 关于 DWC3 Role Switch 功能 `dwc3_0_role_switch` 是与 DesignWare USB 3.0 Controller (DWC3) 相关的功能之一,主要用于支持 USB 的角色切换功能。具体来说,在双角色设备(Dual-role device, DRD)中,该功能允许设备动态地在主机模式和外围设备模式之间切换。 #### 角色切换的工作原理 USB Dual-role 设备能够根据连接状态自动调整其行为。当检测到另一端的设备作为主机时,DRD 将进入外围设备模式;而如果它本身需要充当主机,则会切换为主机模式[^1]。这种切换通常由 `type-c` 协议控制器管理器(TCPM)通过调用底层硬件接口实现。 以下是关于 `dwc3_0_role_switch` 的一些关键点: - **Role Switch 支持** 在 Linux 内核中,`dwc3-core` 提供了一个通用框架来处理不同平台上的角色切换需求。这包括初始化必要的寄存器设置以及注册回调函数以便响应来自上层软件的角色变更请求[^2]。 - **驱动加载顺序依赖关系** 正确配置低功耗子系统选项如 X86_INTEL_LPSS 和 IOSF_MBI 对确保整个系统的稳定性至关重要。这些模块提供了访问特定寄存器所需的基础设施,从而使得像 fusb302 这样的 Type-C PHY 能够正常工作并参与 role-switch 流程[^3]。 下面展示了一段简化版代码片段用于说明如何定义及导出此类操作给用户空间应用或者更高层次的服务使用: ```c static int dwc3_role_switch_set(struct usb_role_switch *sw, enum usb_role role) { struct dwc3 *dwc = dev_get_drvdata(sw->dev); switch(role){ case USB_ROLE_HOST: /* Transition to Host Mode */ break; case USB_ROLE_DEVICE: /* Enter Device/Gadget Mode */ break; default: return -EINVAL; } // Implement actual hardware transition here... } // Export this function via sysfs or other interfaces as needed. ``` 上述例子仅作示意用途,并未包含完整的错误检查逻辑或其他细节部分。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值