summaryrefslogtreecommitdiff
path: root/drivers/usb/chipidea
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/chipidea')
-rw-r--r--drivers/usb/chipidea/ci.h2
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c14
-rw-r--r--drivers/usb/chipidea/core.c95
-rw-r--r--drivers/usb/chipidea/usbmisc_imx.c12
4 files changed, 115 insertions, 8 deletions
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index 33c99e2fc8fe..91b475685c77 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -173,6 +173,7 @@ struct hw_bank {
* @enabled_otg_timer_bits: bits of enabled otg timers
* @next_otg_timer: next nearest enabled timer to be expired
* @work: work for role changing
+ * @work_dr: work for role changing for non-OTG controllers
* @wq: workqueue thread
* @qh_pool: allocation pool for queue heads
* @td_pool: allocation pool for transfer descriptors
@@ -222,6 +223,7 @@ struct ci_hdrc {
unsigned enabled_otg_timer_bits;
enum otg_fsm_timer next_otg_timer;
struct work_struct work;
+ struct work_struct work_dr;
struct workqueue_struct *wq;
struct dma_pool *qh_pool;
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index 53f48cc2eeb5..00bb8d6caec1 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -130,6 +130,7 @@ struct ci_hdrc_imx_data {
struct pm_qos_request pm_qos_req;
};
+#ifdef CONFIG_POWER_SUPPLY
static char *imx_usb_charger_supplied_to[] = {
"imx_usb_charger",
};
@@ -139,6 +140,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
/* Common functions shared by usbmisc drivers */
@@ -319,6 +321,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) {
@@ -339,6 +342,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)) {
@@ -378,6 +382,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)
@@ -441,6 +446,7 @@ static int imx_usb_register_charger(struct usb_charger *charger,
return 0;
}
+#endif
static int ci_hdrc_imx_probe(struct platform_device *pdev)
{
@@ -575,6 +581,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 = dev;
data->usbmisc_data->charger = &data->charger;
@@ -583,7 +590,12 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
if (ret && ret != -ENODEV)
goto disable_hsic_regulator;
if (!ret)
- dev_dbg(dev, "USB Charger is created\n");
+ 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 a9b3795a150b..8269046b684d 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -569,16 +569,71 @@ static irqreturn_t ci_irq(int irq, void *data)
return ret;
}
+static void usb_roleswitch_workqueue(struct work_struct *work)
+{
+ struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work_dr);
+ struct ci_hdrc_cable *id, *vbus;
+ int ret;
+
+ pm_runtime_get_sync(ci->dev);
+
+ id = &ci->platdata->id_extcon;
+ if (!IS_ERR(id->edev)) {
+ int new_role;
+
+ ci_role_stop(ci);
+ hw_wait_phy_stable();
+
+ ret = extcon_get_cable_state_(id->edev, EXTCON_USB_HOST);
+ if (ret) {
+ new_role = CI_ROLE_HOST;
+ dev_info(ci->dev, "switching to host role\n");
+ } else {
+ new_role = CI_ROLE_GADGET;
+ dev_info(ci->dev, "switching to gadget role\n");
+ }
+ ci_role_start(ci, new_role);
+ }
+
+ vbus = &ci->platdata->vbus_extcon;
+ if (!IS_ERR(vbus->edev)) {
+ ret = extcon_get_cable_state_(vbus->edev, EXTCON_USB);
+ if (ret) {
+ usb_gadget_vbus_connect(&ci->gadget);
+ } else {
+ usb_gadget_vbus_disconnect(&ci->gadget);
+ }
+ }
+
+ pm_runtime_put_sync(ci->dev);
+
+ enable_irq(ci->irq);
+}
+
static int ci_cable_notifier(struct notifier_block *nb, unsigned long event,
void *ptr)
{
struct ci_hdrc_cable *cbl = container_of(nb, struct ci_hdrc_cable, nb);
struct ci_hdrc *ci = cbl->ci;
- cbl->connected = event;
- cbl->changed = true;
+ if (ci->platdata->flags & CI_HDRC_DUAL_ROLE_NOT_OTG) {
+ disable_irq_nosync(ci->irq);
+
+ /*
+ * This notifier might get called twice in succession,
+ * once for the ID pin and once for the VBUS pin. Make
+ * sure we only disable irq in case we successfully add
+ * work to the work queue.
+ */
+ if (!queue_work(system_power_efficient_wq, &ci->work_dr))
+ enable_irq(ci->irq);
+ } else {
+ cbl->connected = event;
+ cbl->changed = true;
+
+ ci_irq(ci->irq, ci);
+ }
- ci_irq(ci->irq, ci);
return NOTIFY_DONE;
}
@@ -858,6 +913,13 @@ static enum ci_role ci_get_role(struct ci_hdrc *ci)
* role switch, the defalt role is gadget, and the
* user can switch it through debugfs.
*/
+ struct ci_hdrc_cable *id = &ci->platdata->id_extcon;
+ if (!IS_ERR(id->edev)) {
+ if (extcon_get_cable_state_(id->edev, EXTCON_USB_HOST))
+ return CI_ROLE_HOST;
+ else
+ return CI_ROLE_GADGET;
+ }
return CI_ROLE_GADGET;
}
} else {
@@ -1020,7 +1082,15 @@ static int ci_hdrc_probe(struct platform_device *pdev)
ci_get_otg_capable(ci);
+ if (ci->platdata->flags & CI_HDRC_DUAL_ROLE_NOT_OTG)
+ INIT_WORK(&ci->work_dr, usb_roleswitch_workqueue);
+
+ ret = ci_extcon_register(ci);
+ if (ret)
+ goto stop;
+
dr_mode = ci->platdata->dr_mode;
+
/* initialize role(s) before the interrupt is requested */
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
ret = ci_hdrc_host_init(ci);
@@ -1062,6 +1132,21 @@ static int ci_hdrc_probe(struct platform_device *pdev)
ci_handle_vbus_connected(ci);
if (!ci_otg_is_fsm_mode(ci)) {
+
+ /* only update vbus status for peripheral */
+ if (dr_mode == USB_DR_MODE_PERIPHERAL) {
+ usb_gadget_vbus_connect(&ci->gadget);
+ } else if (ci->role == CI_ROLE_GADGET) {
+ struct ci_hdrc_cable *vbus = &ci->platdata->vbus_extcon;
+
+ /* Use vbus state from extcon if provided */
+ if (!IS_ERR(vbus->edev) &&
+ extcon_get_cable_state_(vbus->edev, EXTCON_USB))
+ usb_gadget_vbus_connect(&ci->gadget);
+ else
+ ci_handle_vbus_change(ci);
+ }
+
ret = ci_role_start(ci, ci->role);
if (ret) {
dev_err(dev, "can't start %s role\n",
@@ -1076,10 +1161,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)
if (ret)
goto stop;
- ret = ci_extcon_register(ci);
- if (ret)
- goto stop;
-
if (ci->supports_runtime_pm) {
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c
index b7d21b052c19..6ed020113926 100644
--- a/drivers/usb/chipidea/usbmisc_imx.c
+++ b/drivers/usb/chipidea/usbmisc_imx.c
@@ -609,6 +609,7 @@ static int usbmisc_imx7d_power_lost_check(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)
@@ -762,7 +763,9 @@ static int usbmisc_imx6sx_power_lost_check(struct imx_usbmisc_data *data)
else
return 0;
}
+#endif
+#ifdef CONFIG_POWER_SUPPLY
static void imx7_disable_charger_detector(struct imx_usbmisc_data *data)
{
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
@@ -919,6 +922,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)
@@ -967,8 +971,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,
};
@@ -980,9 +986,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,
};
@@ -990,9 +998,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,
.hsic_set_connect = usbmisc_imx6_hsic_set_connect,
.hsic_set_clk = usbmisc_imx6_hsic_set_clk,
@@ -1048,6 +1058,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;
@@ -1099,6 +1110,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)
{