diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 0104a80b185e1..7cc49611af74f 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -1521,11 +1522,32 @@ static void dwc3_check_params(struct dwc3 *dwc) } } +static int dwc3_aggregate_bind(struct device *dev) +{ + return component_bind_all(dev, NULL); +} + +static void dwc3_aggregate_unbind(struct device *dev) +{ + component_unbind_all(dev, NULL); +} + +static const struct component_master_ops dwc3_aggregate_ops = { + .bind = dwc3_aggregate_bind, + .unbind = dwc3_aggregate_unbind, +}; + +static int dwc3_compare(struct device *dev, void *data) +{ + return dev == data; +} + static int dwc3_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct resource *res, dwc_res; struct dwc3 *dwc; + struct component_match *match = NULL; int ret; @@ -1646,10 +1668,21 @@ static int dwc3_probe(struct platform_device *pdev) if (ret) goto err5; + /* Add component match entry for the glue. */ + component_match_add(dwc->dev, &match, dwc3_compare, dwc->dev->parent); + + /* Everything is ready now. Declare this driver as the aggregate. */ + ret = component_master_add_with_match(dwc->dev, &dwc3_aggregate_ops, match); + if (ret) + goto err6; + pm_runtime_put(dev); return 0; +err6: + dwc3_core_exit_mode(dwc); + err5: dwc3_debugfs_exit(dwc); dwc3_event_buffers_cleanup(dwc); @@ -1696,6 +1729,8 @@ static int dwc3_remove(struct platform_device *pdev) pm_runtime_get_sync(&pdev->dev); + component_master_del(dwc->dev, &dwc3_aggregate_ops); + dwc3_core_exit_mode(dwc); dwc3_debugfs_exit(dwc); diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index 9abbd01028c5f..ffe585344d6a8 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include "core.h" @@ -81,6 +83,7 @@ struct dwc3_qcom { struct extcon_dev *host_edev; struct notifier_block vbus_nb; struct notifier_block host_nb; + struct notifier_block role_nb; const struct dwc3_acpi_pdata *acpi_pdata; @@ -717,6 +720,51 @@ dwc3_qcom_create_urs_usb_platdev(struct device *dev) return acpi_create_platform_device(adev, NULL); } +static int dwc3_qcom_role_notifier(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct dwc3_qcom *qcom = container_of(nb, struct dwc3_qcom, role_nb); + + /* Do what ever you need to do... */ + + return NOTIFY_DONE; +} + +static int dwc3_qcom_bind(struct device *dev, struct device *dwc, void *data) +{ + struct usb_role_switch *sw = usb_role_switch_find_by_fwnode(dev_fwnode(dwc)); + struct dwc3_qcom *qcom = dev_get_drvdata(dev); + + /* + * Time to finalize initilization. + * + * Our aggregate driver - dwc3 core - is guaranteed to be ready when + * this is called. That means USB role switch "sw" is also now ready. + */ + + /* Register role switch notifier */ + qcom->role_nb.notifier_call = dwc3_qcom_role_notifier; + usb_role_switch_register_notifier(sw, qcom->role_nb); + usb_role_switch_put(sw); + + return 0; +} + +static void dwc3_qcom_unbind(struct device *dev, struct device *dwc, void *data) +{ + struct usb_role_switch *sw = usb_role_switch_find_by_fwnode(dev_fwnode(dwc)); + struct dwc3_qcom *qcom = dev_get_drvdata(dev); + + /* Unregister role switch notifier */ + usb_role_switch_unregister_notifier(sw, qcom->role_nb); + usb_role_switch_put(sw); +} + +static const struct component_ops dwc3_qcom_component_ops = { + .bind = dwc3_qcom_bind, + .unbind = dwc3_qcom_unbind, +}; + static int dwc3_qcom_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -837,6 +885,10 @@ static int dwc3_qcom_probe(struct platform_device *pdev) if (ret) goto interconnect_exit; + ret = component_add(dev, &dwc3_qcom_component_ops); + if (ret) + goto interconnect_exit; + device_init_wakeup(&pdev->dev, 1); qcom->is_suspended = false; pm_runtime_set_active(dev); @@ -869,6 +921,7 @@ static int dwc3_qcom_remove(struct platform_device *pdev) struct device *dev = &pdev->dev; int i; + component_del(dev, &dwc3_qcom_component_ops); device_remove_software_node(&qcom->dwc3->dev); of_platform_depopulate(dev);