summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/chipidea/ci.h7
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c11
-rw-r--r--drivers/usb/chipidea/core.c104
-rw-r--r--drivers/usb/chipidea/usbmisc_imx.c12
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)
{