diff options
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/chipidea/ci.h | 7 | ||||
-rw-r--r-- | drivers/usb/chipidea/ci_hdrc_imx.c | 11 | ||||
-rw-r--r-- | drivers/usb/chipidea/core.c | 104 | ||||
-rw-r--r-- | drivers/usb/chipidea/usbmisc_imx.c | 12 |
4 files changed, 129 insertions, 5 deletions
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 54cc25ca47e9..e3e6ca722a48 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -18,6 +18,7 @@ #include <linux/usb.h> #include <linux/usb/gadget.h> #include <linux/usb/otg-fsm.h> +#include <linux/extcon.h> /****************************************************************************** * DEFINE @@ -256,6 +257,12 @@ struct ci_hdrc { bool in_lpm; bool wakeup_int; enum ci_revision rev; + + struct extcon_specific_cable_nb extcon_vbus_dev; + struct extcon_specific_cable_nb extcon_id_dev; + struct notifier_block vbus_nb; + struct notifier_block id_nb; + /* register save area for suspend&resume */ u32 pm_command; u32 pm_status; diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index d2898ec1b876..b1ba565af1ba 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -109,6 +109,7 @@ struct ci_hdrc_imx_data { /* --------------------------------- */ }; +#ifdef CONFIG_POWER_SUPPLY static char *imx_usb_charger_supplied_to[] = { "imx_usb_charger", }; @@ -118,6 +119,7 @@ static enum power_supply_property imx_usb_charger_power_props[] = { POWER_SUPPLY_PROP_ONLINE, /* VBUS online */ POWER_SUPPLY_PROP_CURRENT_MAX, /* Maximum current in mA */ }; +#endif static inline bool is_imx6q_con(struct ci_hdrc_imx_data *imx_data) { @@ -318,6 +320,7 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned event) int ret = 0; switch (event) { +#ifdef CONFIG_POWER_SUPPLY case CI_HDRC_CONTROLLER_VBUS_EVENT: if (data->usbmisc_data && ci->vbus_active) { if (data->imx_usb_charger_detection) { @@ -338,6 +341,7 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned event) return ret; imx_usbmisc_charger_secondary_detection(data->usbmisc_data); break; +#endif case CI_HDRC_IMX_HSIC_ACTIVE_EVENT: if (!IS_ERR(data->pinctrl) && !IS_ERR(data->pinctrl_hsic_active)) { @@ -377,6 +381,7 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned event) return ret; } +#ifdef CONFIG_POWER_SUPPLY static int imx_usb_charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -440,6 +445,7 @@ static int imx_usb_register_charger(struct usb_charger *charger, return 0; } +#endif static int ci_hdrc_imx_probe(struct platform_device *pdev) { @@ -566,6 +572,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) if (of_find_property(np, "imx-usb-charger-detection", NULL) && data->usbmisc_data) { +#ifdef CONFIG_POWER_SUPPLY data->imx_usb_charger_detection = true; data->charger.dev = &pdev->dev; data->usbmisc_data->charger = &data->charger; @@ -576,6 +583,10 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) if (!ret) dev_dbg(&pdev->dev, "USB Charger is created\n"); +#else + dev_err(&pdev->dev, + "USB Charger requires CONFIG_POWER_SUPPLY\n"); +#endif } ret = imx_usbmisc_init(data->usbmisc_data); diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 3b77b04896f8..30dbc9ce3bcf 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -606,6 +606,17 @@ static int ci_get_platdata(struct device *dev, { int ret; + if (of_property_read_bool(dev->of_node, "extcon")) { + platdata->edev = extcon_get_edev_by_phandle(dev, 0); + if (IS_ERR(platdata->edev)) { + if (PTR_ERR(platdata->edev) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_err(dev, "Could not get extcon device: %ld\n", + PTR_ERR(platdata->edev)); + } + platdata->flags |= CI_HDRC_DUAL_ROLE_NOT_OTG; + } + if (!platdata->phy_mode) platdata->phy_mode = of_usb_get_phy_mode(dev->of_node); @@ -797,6 +808,42 @@ static inline void ci_role_destroy(struct ci_hdrc *ci) ci_hdrc_host_destroy(ci); } +static int ci_id_notifier(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct ci_hdrc *ci = container_of(nb, struct ci_hdrc, id_nb); + + pm_runtime_get_sync(ci->dev); + + ci_role_stop(ci); + + hw_wait_phy_stable(); + + if (ci_role_start(ci, event ? CI_ROLE_HOST : CI_ROLE_GADGET)) + dev_err(ci->dev, "Can't start %s role\n", ci_role(ci)->name); + + pm_runtime_put_sync(ci->dev); + + return NOTIFY_DONE; +} + +static int ci_vbus_notifier(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct ci_hdrc *ci = container_of(nb, struct ci_hdrc, vbus_nb); + + pm_runtime_get_sync(ci->dev); + + if (event) + usb_gadget_vbus_connect(&ci->gadget); + else + usb_gadget_vbus_disconnect(&ci->gadget); + + pm_runtime_put_sync(ci->dev); + + return NOTIFY_DONE; +} + static void ci_get_otg_capable(struct ci_hdrc *ci) { if (ci->platdata->flags & CI_HDRC_DUAL_ROLE_NOT_OTG) @@ -897,6 +944,22 @@ static int ci_hdrc_probe(struct platform_device *pdev) return -ENODEV; } + if (ci->platdata->edev) { + ci->vbus_nb.notifier_call = ci_vbus_notifier; + ret = extcon_register_interest(&ci->extcon_vbus_dev, + ci->platdata->edev->name, "USB", + &ci->vbus_nb); + if (ret < 0) + dev_err(dev, "failed to register notifier for USB aka VBUS\n"); + + ci->id_nb.notifier_call = ci_id_notifier; + ret = extcon_register_interest(&ci->extcon_id_dev, + ci->platdata->edev->name, "USB-HOST", + &ci->id_nb); + if (ret < 0) + dev_err(dev, "failed to register notifier for USB-HOST aka ID\n"); + } + if (ci->platdata->phy) { ci->phy = ci->platdata->phy; } else if (ci->platdata->usb_phy) { @@ -922,7 +985,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) ret = ci_usb_phy_init(ci); if (ret) { dev_err(dev, "unable to init phy: %d\n", ret); - return ret; + goto extcon_cleanup; } ci->hw_bank.phys = res->start; @@ -964,10 +1027,28 @@ static int ci_hdrc_probe(struct platform_device *pdev) } } - ci->role = ci_get_role(ci); - /* only update vbus status for peripheral */ - if (ci->role == CI_ROLE_GADGET) - ci_handle_vbus_connected(ci); + if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) { + if (ci->is_otg) { + ci->role = ci_otg_role(ci); + /* Enable ID change irq */ + hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE); + } else { + /* + * If the controller is not OTG capable, but support + * role switch, the defalt role is gadget, and the + * user can switch it through debugfs. + */ + if ((ci->platdata->edev) && (extcon_get_cable_state( + ci->platdata->edev, "USB-HOST") == true)) + ci->role = CI_ROLE_HOST; + else + ci->role = CI_ROLE_GADGET; + } + } else { + ci->role = ci->roles[CI_ROLE_HOST] + ? CI_ROLE_HOST + : CI_ROLE_GADGET; + } if (!ci_otg_is_fsm_mode(ci)) { ret = ci_role_start(ci, ci->role); @@ -976,6 +1057,9 @@ static int ci_hdrc_probe(struct platform_device *pdev) ci_role(ci)->name); goto stop; } + if ((ci->role == CI_ROLE_GADGET) && (ci->platdata->edev) && + (extcon_get_cable_state(ci->platdata->edev, "USB") == true)) + usb_gadget_vbus_connect(&ci->gadget); } platform_set_drvdata(pdev, ci); @@ -1006,6 +1090,11 @@ static int ci_hdrc_probe(struct platform_device *pdev) stop: ci_role_destroy(ci); +extcon_cleanup: + if (ci->extcon_vbus_dev.edev) + extcon_unregister_interest(&ci->extcon_vbus_dev); + if (ci->extcon_id_dev.edev) + extcon_unregister_interest(&ci->extcon_id_dev); deinit_phy: ci_usb_phy_exit(ci); @@ -1016,6 +1105,11 @@ static int ci_hdrc_remove(struct platform_device *pdev) { struct ci_hdrc *ci = platform_get_drvdata(pdev); + if (ci->extcon_vbus_dev.edev) + extcon_unregister_interest(&ci->extcon_vbus_dev); + if (ci->extcon_id_dev.edev) + extcon_unregister_interest(&ci->extcon_id_dev); + if (ci->supports_runtime_pm) { pm_runtime_get_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index e62fc732e86c..5f133fb2ca6d 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -487,6 +487,7 @@ static int usbmisc_vf610_init(struct imx_usbmisc_data *data) /***************************************************************************/ /* imx usb charger detecton */ /***************************************************************************/ +#ifdef CONFIG_POWER_SUPPLY static void usb_charger_is_present(struct usb_charger *charger, bool present) { if (present) @@ -640,6 +641,7 @@ static int usbmisc_imx6sx_power_lost_check(struct imx_usbmisc_data *data) else return 0; } +#endif static int usbmisc_imx7d_set_wakeup (struct imx_usbmisc_data *data, bool enabled) @@ -692,6 +694,7 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data) return 0; } +#ifdef CONFIG_POWER_SUPPLY static int usbmisc_imx7d_power_lost_check(struct imx_usbmisc_data *data) { struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); @@ -867,6 +870,7 @@ int imx7d_charger_secondary_detection(struct imx_usbmisc_data *data) return 0; } +#endif static int usbmisc_term_select_override(struct imx_usbmisc_data *data, bool enable, int val) @@ -915,8 +919,10 @@ static const struct usbmisc_ops imx53_usbmisc_ops = { static const struct usbmisc_ops imx6q_usbmisc_ops = { .set_wakeup = usbmisc_imx6q_set_wakeup, .init = usbmisc_imx6q_init, +#ifdef CONFIG_POWER_SUPPLY .charger_primary_detection = imx6_charger_primary_detection, .charger_secondary_detection = imx6_charger_secondary_detection, +#endif .hsic_set_connect = usbmisc_imx6_hsic_set_connect, .hsic_set_clk = usbmisc_imx6_hsic_set_clk, }; @@ -928,9 +934,11 @@ static const struct usbmisc_ops vf610_usbmisc_ops = { static const struct usbmisc_ops imx6sx_usbmisc_ops = { .set_wakeup = usbmisc_imx6q_set_wakeup, .init = usbmisc_imx6sx_init, +#ifdef CONFIG_POWER_SUPPLY .charger_primary_detection = imx6_charger_primary_detection, .charger_secondary_detection = imx6_charger_secondary_detection, .power_lost_check = usbmisc_imx6sx_power_lost_check, +#endif .hsic_set_connect = usbmisc_imx6_hsic_set_connect, .hsic_set_clk = usbmisc_imx6_hsic_set_clk, }; @@ -938,9 +946,11 @@ static const struct usbmisc_ops imx6sx_usbmisc_ops = { static const struct usbmisc_ops imx7d_usbmisc_ops = { .init = usbmisc_imx7d_init, .set_wakeup = usbmisc_imx7d_set_wakeup, +#ifdef CONFIG_POWER_SUPPLY .power_lost_check = usbmisc_imx7d_power_lost_check, .charger_primary_detection = imx7d_charger_primary_detection, .charger_secondary_detection = imx7d_charger_secondary_detection, +#endif .term_select_override = usbmisc_term_select_override, }; @@ -986,6 +996,7 @@ int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled) } EXPORT_SYMBOL_GPL(imx_usbmisc_set_wakeup); +#ifdef CONFIG_POWER_SUPPLY int imx_usbmisc_charger_detection(struct imx_usbmisc_data *data, bool connect) { struct imx_usbmisc *usbmisc; @@ -1037,6 +1048,7 @@ int imx_usbmisc_charger_secondary_detection(struct imx_usbmisc_data *data) return usbmisc->ops->charger_secondary_detection(data); } EXPORT_SYMBOL_GPL(imx_usbmisc_charger_secondary_detection); +#endif int imx_usbmisc_power_lost_check(struct imx_usbmisc_data *data) { |