The USB block found in most Qualcomm platforms is modelled as three different independent device drivers, and represented in DeviceTree as two layered nodes. But as shown by the already existing layering violations in the Qualcomm glue driver they can not be operated independently. In the current model, the probing of the core is asynchronous, and in a number of places there's risk that the driver dereferences NULL pointers, as it peeks into the core's drvdata. There is also no way, in the current design to make the core notify the glue upon DRD mode changes. Among the past proposals have been attempts to provide a callback registration API, but as there is no way to know when the core is probed this doesn't work. Based on the recent refactoring its now possible to instantiate the glue and core from a single representation of the DWC3 IP-block. This will also allow for the glue to pass a callback to be called for DRD mode changes. The only overlapping handling between the Qualcomm glue and the core is the release of reset, which is left to the core to handle. Signed-off-by: Bjorn Andersson <quic_bjorande@xxxxxxxxxxx> --- drivers/usb/dwc3/dwc3-qcom.c | 49 +++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index cf6c391ba498..3c9a2b5cd559 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -686,6 +686,16 @@ static int dwc3_qcom_probe_core(struct platform_device *pdev, struct dwc3_qcom * return 0; } +static bool dwc3_qcom_has_separate_dwc3_of_node(struct device *dev) +{ + struct device_node *np; + + np = of_get_compatible_child(dev->of_node, "snps,dwc3"); + of_node_put(np); + + return !!np; +} + static int dwc3_qcom_of_register_core(struct platform_device *pdev) { struct dwc3_qcom *qcom = platform_get_drvdata(pdev); @@ -795,11 +805,14 @@ static int dwc3_qcom_probe(struct platform_device *pdev) int ret, i; bool ignore_pipe_clk; bool wakeup_source; + bool legacy_binding; qcom = devm_kzalloc(&pdev->dev, sizeof(*qcom), GFP_KERNEL); if (!qcom) return -ENOMEM; + legacy_binding = dwc3_qcom_has_separate_dwc3_of_node(dev); + platform_set_drvdata(pdev, qcom); qcom->dev = &pdev->dev; @@ -823,24 +836,26 @@ static int dwc3_qcom_probe(struct platform_device *pdev) } } - qcom->resets = devm_reset_control_array_get_optional_exclusive(dev); - if (IS_ERR(qcom->resets)) { - return dev_err_probe(&pdev->dev, PTR_ERR(qcom->resets), - "failed to get resets\n"); - } + if (legacy_binding) { + qcom->resets = devm_reset_control_array_get_optional_exclusive(dev); + if (IS_ERR(qcom->resets)) { + return dev_err_probe(&pdev->dev, PTR_ERR(qcom->resets), + "failed to get resets\n"); + } - ret = reset_control_assert(qcom->resets); - if (ret) { - dev_err(&pdev->dev, "failed to assert resets, err=%d\n", ret); - return ret; - } + ret = reset_control_assert(qcom->resets); + if (ret) { + dev_err(&pdev->dev, "failed to assert resets, err=%d\n", ret); + return ret; + } - usleep_range(10, 1000); + usleep_range(10, 1000); - ret = reset_control_deassert(qcom->resets); - if (ret) { - dev_err(&pdev->dev, "failed to deassert resets, err=%d\n", ret); - goto reset_assert; + ret = reset_control_deassert(qcom->resets); + if (ret) { + dev_err(&pdev->dev, "failed to deassert resets, err=%d\n", ret); + goto reset_assert; + } } ret = dwc3_qcom_clk_init(qcom, of_clk_get_parent_count(np)); @@ -851,7 +866,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (np) { + if (legacy_binding) { parent_res = res; } else { memcpy(&local_res, res, sizeof(struct resource)); @@ -882,7 +897,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev) if (ignore_pipe_clk) dwc3_qcom_select_utmi_clk(qcom); - if (np) + if (legacy_binding) ret = dwc3_qcom_of_register_core(pdev); else ret = dwc3_qcom_probe_core(pdev, qcom); -- 2.25.1