summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSanchayan Maity <maitysanchayan@gmail.com>2016-09-15 18:45:44 +0530
committerMax Krummenacher <max.krummenacher@toradex.com>2016-09-29 15:22:37 +0200
commitbe04dc33f409996c37d8c69702748890b4968c67 (patch)
treed2b726e676981a73b9307c83970e99072240f8d8
parente60b1f9218b5e8de486451a42e44b19b4a20969a (diff)
usb: chipidea: Use extcon framework for ID and VBUS detection
Rather than relying on special USB/PHY pins/registers use the generic extcon framework with its extcon-usb-gpio implementation to detect ID and VBUS changes. Signed-off-by: Sanchayan Maity <maitysanchayan@gmail.com> Signed-off-by: Stefan Agner <stefan.agner@toradex.com>
-rw-r--r--drivers/usb/chipidea/ci.h7
-rw-r--r--drivers/usb/chipidea/core.c104
-rw-r--r--include/linux/usb/chipidea.h2
3 files changed, 108 insertions, 5 deletions
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index 207e4a34be07..7aeb92288643 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/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/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h
index fc9163946d75..b02deb0a7d7f 100644
--- a/include/linux/usb/chipidea.h
+++ b/include/linux/usb/chipidea.h
@@ -52,6 +52,8 @@ struct ci_hdrc_platform_data {
struct regulator *reg_vbus;
struct usb_otg_caps ci_otg_caps;
bool tpl_support;
+ struct extcon_dev *edev;
+
/* interrupt threshold setting */
u32 itc_setting;
u32 ahb_burst_config;