From d66895f9df6b880fa3a280f5bdfe919169ca37dd Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 14 Aug 2013 12:44:05 +0300 Subject: usb: chipidea: udc: otg_set_peripheral is useless for some chipidea users It is useless at below cases: - If we implement both usb host and device at chipidea driver. - If we don't need phy->otg. Tested-by: Marek Vasut Signed-off-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/udc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/usb/chipidea/udc.c') diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index e475fcda1d6..116c7620388 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1805,7 +1805,12 @@ static int udc_start(struct ci_hdrc *ci) if (ci->transceiver) { retval = otg_set_peripheral(ci->transceiver->otg, &ci->gadget); - if (retval) + /* + * If we implement all USB functions using chipidea drivers, + * it doesn't need to call above API, meanwhile, if we only + * use gadget function, calling above API is useless. + */ + if (retval && retval != -ENOTSUPP) goto put_transceiver; } -- cgit v1.2.3-70-g09d2 From 3f124d233e97db96d9471d1fb346335d43d8650d Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 14 Aug 2013 12:44:07 +0300 Subject: usb: chipidea: add role init and destroy APIs - The role's init will be called at probe procedure. - The role's destroy will be called at fail patch at probe and driver's removal. - The role's start/stop will be called when specific role has started. Tested-by: Marek Vasut Signed-off-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 10 ++++++++-- drivers/usb/chipidea/host.c | 7 +++++++ drivers/usb/chipidea/host.h | 6 ++++++ drivers/usb/chipidea/udc.c | 36 +++++++++++++++++++++++++++--------- drivers/usb/chipidea/udc.h | 6 ++++++ 5 files changed, 54 insertions(+), 11 deletions(-) (limited to 'drivers/usb/chipidea/udc.c') diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 2f70080023a..75afc524b65 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -420,6 +420,12 @@ void ci_hdrc_remove_device(struct platform_device *pdev) } EXPORT_SYMBOL_GPL(ci_hdrc_remove_device); +static inline void ci_role_destroy(struct ci_hdrc *ci) +{ + ci_hdrc_gadget_destroy(ci); + ci_hdrc_host_destroy(ci); +} + static int ci_hdrc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -537,7 +543,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) free_irq(ci->irq, ci); stop: - ci_role_stop(ci); + ci_role_destroy(ci); rm_wq: flush_workqueue(ci->wq); destroy_workqueue(ci->wq); @@ -553,7 +559,7 @@ static int ci_hdrc_remove(struct platform_device *pdev) flush_workqueue(ci->wq); destroy_workqueue(ci->wq); free_irq(ci->irq, ci); - ci_role_stop(ci); + ci_role_destroy(ci); return 0; } diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index 8cf8d28c88d..6f96795dd20 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -106,6 +106,13 @@ static void host_stop(struct ci_hdrc *ci) regulator_disable(ci->platdata->reg_vbus); } + +void ci_hdrc_host_destroy(struct ci_hdrc *ci) +{ + if (ci->role == CI_ROLE_HOST) + host_stop(ci); +} + int ci_hdrc_host_init(struct ci_hdrc *ci) { struct ci_role_driver *rdrv; diff --git a/drivers/usb/chipidea/host.h b/drivers/usb/chipidea/host.h index 058875c1533..5707bf379bf 100644 --- a/drivers/usb/chipidea/host.h +++ b/drivers/usb/chipidea/host.h @@ -4,6 +4,7 @@ #ifdef CONFIG_USB_CHIPIDEA_HOST int ci_hdrc_host_init(struct ci_hdrc *ci); +void ci_hdrc_host_destroy(struct ci_hdrc *ci); #else @@ -12,6 +13,11 @@ static inline int ci_hdrc_host_init(struct ci_hdrc *ci) return -ENXIO; } +static inline void ci_hdrc_host_destroy(struct ci_hdrc *ci) +{ + +} + #endif #endif /* __DRIVERS_USB_CHIPIDEA_HOST_H */ diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 116c7620388..24a100d751c 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -27,6 +27,7 @@ #include "udc.h" #include "bits.h" #include "debug.h" +#include "otg.h" /* control endpoint description */ static const struct usb_endpoint_descriptor @@ -1844,13 +1845,13 @@ free_qh_pool: } /** - * udc_remove: parent remove must call this to remove UDC + * ci_hdrc_gadget_destroy: parent remove must call this to remove UDC * * No interrupts active, the IRQ has been released */ -static void udc_stop(struct ci_hdrc *ci) +void ci_hdrc_gadget_destroy(struct ci_hdrc *ci) { - if (ci == NULL) + if (!ci->roles[CI_ROLE_GADGET]) return; usb_del_gadget_udc(&ci->gadget); @@ -1865,15 +1866,32 @@ static void udc_stop(struct ci_hdrc *ci) if (ci->global_phy) usb_put_phy(ci->transceiver); } - /* my kobject is dynamic, I swear! */ - memset(&ci->gadget, 0, sizeof(ci->gadget)); +} + +static int udc_id_switch_for_device(struct ci_hdrc *ci) +{ + if (ci->is_otg) { + ci_clear_otg_interrupt(ci, OTGSC_BSVIS); + ci_enable_otg_interrupt(ci, OTGSC_BSVIE); + } + + return 0; +} + +static void udc_id_switch_for_host(struct ci_hdrc *ci) +{ + if (ci->is_otg) { + /* host doesn't care B_SESSION_VALID event */ + ci_clear_otg_interrupt(ci, OTGSC_BSVIS); + ci_disable_otg_interrupt(ci, OTGSC_BSVIE); + } } /** * ci_hdrc_gadget_init - initialize device related bits * ci: the controller * - * This function enables the gadget role, if the device is "device capable". + * This function initializes the gadget, if the device is "device capable". */ int ci_hdrc_gadget_init(struct ci_hdrc *ci) { @@ -1886,11 +1904,11 @@ int ci_hdrc_gadget_init(struct ci_hdrc *ci) if (!rdrv) return -ENOMEM; - rdrv->start = udc_start; - rdrv->stop = udc_stop; + rdrv->start = udc_id_switch_for_device; + rdrv->stop = udc_id_switch_for_host; rdrv->irq = udc_irq; rdrv->name = "gadget"; ci->roles[CI_ROLE_GADGET] = rdrv; - return 0; + return udc_start(ci); } diff --git a/drivers/usb/chipidea/udc.h b/drivers/usb/chipidea/udc.h index 455ac216922..e66df0020bd 100644 --- a/drivers/usb/chipidea/udc.h +++ b/drivers/usb/chipidea/udc.h @@ -84,6 +84,7 @@ struct ci_hw_req { #ifdef CONFIG_USB_CHIPIDEA_UDC int ci_hdrc_gadget_init(struct ci_hdrc *ci); +void ci_hdrc_gadget_destroy(struct ci_hdrc *ci); #else @@ -92,6 +93,11 @@ static inline int ci_hdrc_gadget_init(struct ci_hdrc *ci) return -ENXIO; } +static inline void ci_hdrc_gadget_destroy(struct ci_hdrc *ci) +{ + +} + #endif #endif /* __DRIVERS_USB_CHIPIDEA_UDC_H */ -- cgit v1.2.3-70-g09d2 From a107f8c505cd8606ae192d24c70b380e980fbe67 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 14 Aug 2013 12:44:11 +0300 Subject: usb: chipidea: add vbus interrupt handler We add vbus interrupt handler at ci_otg_work, it uses OTGSC_BSV(at otgsc) to know it is connect or disconnet event. Meanwhile, we introduce two flags id_event and b_sess_valid_event to indicate it is an id interrupt or a vbus interrupt. Tested-by: Marek Vasut Signed-off-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci.h | 5 +++++ drivers/usb/chipidea/core.c | 28 +++++++++++++++++++++++----- drivers/usb/chipidea/otg.c | 42 +++++++++++++++++++++++++++++++++++------- drivers/usb/chipidea/otg.h | 1 + drivers/usb/chipidea/udc.c | 7 +++++++ 5 files changed, 71 insertions(+), 12 deletions(-) (limited to 'drivers/usb/chipidea/udc.c') diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 33cb29f36e0..316036392e1 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -132,6 +132,9 @@ struct hw_bank { * @transceiver: pointer to USB PHY, if any * @hcd: pointer to usb_hcd for ehci host driver * @debugfs: root dentry for this controller in debugfs + * @id_event: indicates there is an id event, and handled at ci_otg_work + * @b_sess_valid_event: indicates there is a vbus event, and handled + * at ci_otg_work */ struct ci_hdrc { struct device *dev; @@ -168,6 +171,8 @@ struct ci_hdrc { struct usb_phy *transceiver; struct usb_hcd *hcd; struct dentry *debugfs; + bool id_event; + bool b_sess_valid_event; }; static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci) diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index ec6c984d2a6..c95e098fe9d 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -303,16 +303,34 @@ static irqreturn_t ci_irq(int irq, void *data) if (ci->is_otg) otgsc = hw_read(ci, OP_OTGSC, ~0); - if (ci->role != CI_ROLE_END) - ret = ci_role(ci)->irq(ci); + /* + * Handle id change interrupt, it indicates device/host function + * switch. + */ + if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) { + ci->id_event = true; + ci_clear_otg_interrupt(ci, OTGSC_IDIS); + disable_irq_nosync(ci->irq); + queue_work(ci->wq, &ci->work); + return IRQ_HANDLED; + } - if (ci->is_otg && (otgsc & OTGSC_IDIS)) { - hw_write(ci, OP_OTGSC, OTGSC_IDIS, OTGSC_IDIS); + /* + * Handle vbus change interrupt, it indicates device connection + * and disconnection events. + */ + if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) { + ci->b_sess_valid_event = true; + ci_clear_otg_interrupt(ci, OTGSC_BSVIS); disable_irq_nosync(ci->irq); queue_work(ci->wq, &ci->work); - ret = IRQ_HANDLED; + return IRQ_HANDLED; } + /* Handle device/host interrupt */ + if (ci->role != CI_ROLE_END) + ret = ci_role(ci)->irq(ci); + return ret; } diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c index 3b66cbe58d5..7f37484ca36 100644 --- a/drivers/usb/chipidea/otg.c +++ b/drivers/usb/chipidea/otg.c @@ -37,13 +37,23 @@ enum ci_role ci_otg_role(struct ci_hdrc *ci) return role; } -/** - * ci_role_work - perform role changing based on ID pin - * @work: work struct - */ -static void ci_role_work(struct work_struct *work) +void ci_handle_vbus_change(struct ci_hdrc *ci) +{ + u32 otgsc; + + if (!ci->is_otg) + return; + + otgsc = hw_read(ci, OP_OTGSC, ~0); + + if (otgsc & OTGSC_BSV) + usb_gadget_vbus_connect(&ci->gadget); + else + usb_gadget_vbus_disconnect(&ci->gadget); +} + +static void ci_handle_id_switch(struct ci_hdrc *ci) { - struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work); enum ci_role role = ci_otg_role(ci); if (role != ci->role) { @@ -53,17 +63,35 @@ static void ci_role_work(struct work_struct *work) ci_role_stop(ci); ci_role_start(ci, role); } +} +/** + * ci_otg_work - perform otg (vbus/id) event handle + * @work: work struct + */ +static void ci_otg_work(struct work_struct *work) +{ + struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work); + + if (ci->id_event) { + ci->id_event = false; + ci_handle_id_switch(ci); + } else if (ci->b_sess_valid_event) { + ci->b_sess_valid_event = false; + ci_handle_vbus_change(ci); + } else + dev_err(ci->dev, "unexpected event occurs at %s\n", __func__); enable_irq(ci->irq); } + /** * ci_hdrc_otg_init - initialize otg struct * ci: the controller */ int ci_hdrc_otg_init(struct ci_hdrc *ci) { - INIT_WORK(&ci->work, ci_role_work); + INIT_WORK(&ci->work, ci_otg_work); ci->wq = create_singlethread_workqueue("ci_otg"); if (!ci->wq) { dev_err(ci->dev, "can't create workqueue\n"); diff --git a/drivers/usb/chipidea/otg.h b/drivers/usb/chipidea/otg.h index 8acf3df4e3e..2d9f090733b 100644 --- a/drivers/usb/chipidea/otg.h +++ b/drivers/usb/chipidea/otg.h @@ -30,5 +30,6 @@ static inline void ci_disable_otg_interrupt(struct ci_hdrc *ci, u32 bits) int ci_hdrc_otg_init(struct ci_hdrc *ci); void ci_hdrc_otg_destroy(struct ci_hdrc *ci); enum ci_role ci_otg_role(struct ci_hdrc *ci); +void ci_handle_vbus_change(struct ci_hdrc *ci); #endif /* __DRIVERS_USB_CHIPIDEA_OTG_H */ diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 24a100d751c..c70ce3891d3 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -85,8 +85,10 @@ static int hw_device_state(struct ci_hdrc *ci, u32 dma) /* interrupt, error, port change, reset, sleep/suspend */ hw_write(ci, OP_USBINTR, ~0, USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI); + hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); } else { hw_write(ci, OP_USBINTR, ~0, 0); + hw_write(ci, OP_USBCMD, USBCMD_RS, 0); } return 0; } @@ -1460,6 +1462,7 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) pm_runtime_get_sync(&_gadget->dev); hw_device_reset(ci, USBMODE_CM_DC); hw_device_state(ci, ci->ep0out->qh.dma); + dev_dbg(ci->dev, "Connected to host\n"); } else { hw_device_state(ci, 0); if (ci->platdata->notify_event) @@ -1467,6 +1470,7 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) CI_HDRC_CONTROLLER_STOPPED_EVENT); _gadget_stop_activity(&ci->gadget); pm_runtime_put_sync(&_gadget->dev); + dev_dbg(ci->dev, "Disconnected from host\n"); } } @@ -1822,6 +1826,9 @@ static int udc_start(struct ci_hdrc *ci) pm_runtime_no_callbacks(&ci->gadget.dev); pm_runtime_enable(&ci->gadget.dev); + /* Update ci->vbus_active */ + ci_handle_vbus_change(ci); + return retval; remove_trans: -- cgit v1.2.3-70-g09d2 From e7e621eb84b0708f6a04ccb453d47e77d22440e4 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 14 Aug 2013 12:44:13 +0300 Subject: usb: chipidea: udc: fix misuse of REGS_SHARED and PULLUP_ON_VBUS flags CI_HDRC_REGS_SHARED stands for the controller registers is shared with other USB drivers, if all USB drivers are at chipidea/, it doesn't needed to be set. CI_HDRC_PULLUP_ON_VBUS stands for pullup dp when the vbus is on. This flag doesn't need to be set if the vbus is always on for gadget since dp has always pulled up after the gadget has initialized. So, the current code seems to misuse this two flags. - When the gadget initializes, the controller doesn't need to run if it depends on vbus (CI_HDRC_PULLUP_ON_VBUS), it does not relate to shared register. - When the gadget starts (load one gadget module), the controller can run if vbus is on (CI_HDRC_PULLUP_ON_VBUS), it also does not relate to shared register. Tested-by: Marek Vasut Signed-off-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/udc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/usb/chipidea/udc.c') diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index c70ce3891d3..45abf4d46a7 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1637,8 +1637,7 @@ static int ci_udc_start(struct usb_gadget *gadget, pm_runtime_get_sync(&ci->gadget.dev); if (ci->platdata->flags & CI_HDRC_PULLUP_ON_VBUS) { if (ci->vbus_active) { - if (ci->platdata->flags & CI_HDRC_REGS_SHARED) - hw_device_reset(ci, USBMODE_CM_DC); + hw_device_reset(ci, USBMODE_CM_DC); } else { pm_runtime_put_sync(&ci->gadget.dev); goto done; @@ -1801,7 +1800,7 @@ static int udc_start(struct ci_hdrc *ci) } } - if (!(ci->platdata->flags & CI_HDRC_REGS_SHARED)) { + if (!(ci->platdata->flags & CI_HDRC_PULLUP_ON_VBUS)) { retval = hw_device_reset(ci, USBMODE_CM_DC); if (retval) goto put_transceiver; -- cgit v1.2.3-70-g09d2 From d268e9bc23f5df4a2f4167da246d277c2a468d3d Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 14 Aug 2013 12:44:14 +0300 Subject: usb: chipidea: retire flag CI_HDRC_PULLUP_ON_VBUS Currently, the controller only runs when the ci->vbus_active is true. So the flag CI_HDRC_PULLUP_ON_VBUS is useless no longer. If the user doesn't have otgsc, he/she needs to change ci_handle_vbus_change to update ci->vbus_active. Signed-off-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci_hdrc_imx.c | 1 - drivers/usb/chipidea/ci_hdrc_msm.c | 1 - drivers/usb/chipidea/udc.c | 24 ++++++------------------ include/linux/usb/chipidea.h | 1 - 4 files changed, 6 insertions(+), 21 deletions(-) (limited to 'drivers/usb/chipidea/udc.c') diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 30fdc2fd877..cc53e63c9ce 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -88,7 +88,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) .name = "ci_hdrc_imx", .capoffset = DEF_CAPOFFSET, .flags = CI_HDRC_REQUIRE_TRANSCEIVER | - CI_HDRC_PULLUP_ON_VBUS | CI_HDRC_DISABLE_STREAMING, }; int ret; diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c index fb657ef50a9..2d51d852b47 100644 --- a/drivers/usb/chipidea/ci_hdrc_msm.c +++ b/drivers/usb/chipidea/ci_hdrc_msm.c @@ -49,7 +49,6 @@ static struct ci_hdrc_platform_data ci_hdrc_msm_platdata = { .name = "ci_hdrc_msm", .flags = CI_HDRC_REGS_SHARED | CI_HDRC_REQUIRE_TRANSCEIVER | - CI_HDRC_PULLUP_ON_VBUS | CI_HDRC_DISABLE_STREAMING, .notify_event = ci_hdrc_msm_notify_event, diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 45abf4d46a7..90c3572bd1b 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1448,9 +1448,6 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active) unsigned long flags; int gadget_ready = 0; - if (!(ci->platdata->flags & CI_HDRC_PULLUP_ON_VBUS)) - return -EOPNOTSUPP; - spin_lock_irqsave(&ci->lock, flags); ci->vbus_active = is_active; if (ci->driver) @@ -1635,13 +1632,11 @@ static int ci_udc_start(struct usb_gadget *gadget, ci->driver = driver; pm_runtime_get_sync(&ci->gadget.dev); - if (ci->platdata->flags & CI_HDRC_PULLUP_ON_VBUS) { - if (ci->vbus_active) { - hw_device_reset(ci, USBMODE_CM_DC); - } else { - pm_runtime_put_sync(&ci->gadget.dev); - goto done; - } + if (ci->vbus_active) { + hw_device_reset(ci, USBMODE_CM_DC); + } else { + pm_runtime_put_sync(&ci->gadget.dev); + goto done; } retval = hw_device_state(ci, ci->ep0out->qh.dma); @@ -1664,8 +1659,7 @@ static int ci_udc_stop(struct usb_gadget *gadget, spin_lock_irqsave(&ci->lock, flags); - if (!(ci->platdata->flags & CI_HDRC_PULLUP_ON_VBUS) || - ci->vbus_active) { + if (ci->vbus_active) { hw_device_state(ci, 0); if (ci->platdata->notify_event) ci->platdata->notify_event(ci, @@ -1800,12 +1794,6 @@ static int udc_start(struct ci_hdrc *ci) } } - if (!(ci->platdata->flags & CI_HDRC_PULLUP_ON_VBUS)) { - retval = hw_device_reset(ci, USBMODE_CM_DC); - if (retval) - goto put_transceiver; - } - if (ci->transceiver) { retval = otg_set_peripheral(ci->transceiver->otg, &ci->gadget); diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index 10a607ce909..7d399671a56 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -18,7 +18,6 @@ struct ci_hdrc_platform_data { unsigned long flags; #define CI_HDRC_REGS_SHARED BIT(0) #define CI_HDRC_REQUIRE_TRANSCEIVER BIT(1) -#define CI_HDRC_PULLUP_ON_VBUS BIT(2) #define CI_HDRC_DISABLE_STREAMING BIT(3) /* * Only set it when DCCPARAMS.DC==1 and DCCPARAMS.HC==1, -- cgit v1.2.3-70-g09d2 From 4a64783b66bbeed06a1f10ab56a1dd401dd5bfba Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 14 Aug 2013 12:44:15 +0300 Subject: usb: chipidea: udc: .pullup is valid only when vbus is there For chipidea, the IP must know vbus before the controller begins to run. So the .pullup should only be called when the vbus is there. Tested-by: Marek Vasut Signed-off-by: Peter Chen Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/udc.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/usb/chipidea/udc.c') diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 90c3572bd1b..6b4c2f2eb94 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1511,6 +1511,9 @@ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on) { struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); + if (!ci->vbus_active) + return -EOPNOTSUPP; + if (is_on) hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); else -- cgit v1.2.3-70-g09d2