aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorPeter Chen <peter.chen@freescale.com>2013-08-14 05:44:11 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-08-14 15:37:20 -0400
commita107f8c505cd8606ae192d24c70b380e980fbe67 (patch)
treec1451c9051588fe93537c83c034d62def233ed8f /drivers
parentcbec6bd55a45fa88218ec5ea5ae91f9b96d158d0 (diff)
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 <marex@denx.de> Signed-off-by: Peter Chen <peter.chen@freescale.com> Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/chipidea/ci.h5
-rw-r--r--drivers/usb/chipidea/core.c28
-rw-r--r--drivers/usb/chipidea/otg.c42
-rw-r--r--drivers/usb/chipidea/otg.h1
-rw-r--r--drivers/usb/chipidea/udc.c7
5 files changed, 71 insertions, 12 deletions
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index 33cb29f36e06..316036392e11 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -132,6 +132,9 @@ struct hw_bank {
132 * @transceiver: pointer to USB PHY, if any 132 * @transceiver: pointer to USB PHY, if any
133 * @hcd: pointer to usb_hcd for ehci host driver 133 * @hcd: pointer to usb_hcd for ehci host driver
134 * @debugfs: root dentry for this controller in debugfs 134 * @debugfs: root dentry for this controller in debugfs
135 * @id_event: indicates there is an id event, and handled at ci_otg_work
136 * @b_sess_valid_event: indicates there is a vbus event, and handled
137 * at ci_otg_work
135 */ 138 */
136struct ci_hdrc { 139struct ci_hdrc {
137 struct device *dev; 140 struct device *dev;
@@ -168,6 +171,8 @@ struct ci_hdrc {
168 struct usb_phy *transceiver; 171 struct usb_phy *transceiver;
169 struct usb_hcd *hcd; 172 struct usb_hcd *hcd;
170 struct dentry *debugfs; 173 struct dentry *debugfs;
174 bool id_event;
175 bool b_sess_valid_event;
171}; 176};
172 177
173static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci) 178static 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 ec6c984d2a6e..c95e098fe9db 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)
303 if (ci->is_otg) 303 if (ci->is_otg)
304 otgsc = hw_read(ci, OP_OTGSC, ~0); 304 otgsc = hw_read(ci, OP_OTGSC, ~0);
305 305
306 if (ci->role != CI_ROLE_END) 306 /*
307 ret = ci_role(ci)->irq(ci); 307 * Handle id change interrupt, it indicates device/host function
308 * switch.
309 */
310 if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) {
311 ci->id_event = true;
312 ci_clear_otg_interrupt(ci, OTGSC_IDIS);
313 disable_irq_nosync(ci->irq);
314 queue_work(ci->wq, &ci->work);
315 return IRQ_HANDLED;
316 }
308 317
309 if (ci->is_otg && (otgsc & OTGSC_IDIS)) { 318 /*
310 hw_write(ci, OP_OTGSC, OTGSC_IDIS, OTGSC_IDIS); 319 * Handle vbus change interrupt, it indicates device connection
320 * and disconnection events.
321 */
322 if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) {
323 ci->b_sess_valid_event = true;
324 ci_clear_otg_interrupt(ci, OTGSC_BSVIS);
311 disable_irq_nosync(ci->irq); 325 disable_irq_nosync(ci->irq);
312 queue_work(ci->wq, &ci->work); 326 queue_work(ci->wq, &ci->work);
313 ret = IRQ_HANDLED; 327 return IRQ_HANDLED;
314 } 328 }
315 329
330 /* Handle device/host interrupt */
331 if (ci->role != CI_ROLE_END)
332 ret = ci_role(ci)->irq(ci);
333
316 return ret; 334 return ret;
317} 335}
318 336
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index 3b66cbe58d52..7f37484ca362 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)
37 return role; 37 return role;
38} 38}
39 39
40/** 40void ci_handle_vbus_change(struct ci_hdrc *ci)
41 * ci_role_work - perform role changing based on ID pin 41{
42 * @work: work struct 42 u32 otgsc;
43 */ 43
44static void ci_role_work(struct work_struct *work) 44 if (!ci->is_otg)
45 return;
46
47 otgsc = hw_read(ci, OP_OTGSC, ~0);
48
49 if (otgsc & OTGSC_BSV)
50 usb_gadget_vbus_connect(&ci->gadget);
51 else
52 usb_gadget_vbus_disconnect(&ci->gadget);
53}
54
55static void ci_handle_id_switch(struct ci_hdrc *ci)
45{ 56{
46 struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work);
47 enum ci_role role = ci_otg_role(ci); 57 enum ci_role role = ci_otg_role(ci);
48 58
49 if (role != ci->role) { 59 if (role != ci->role) {
@@ -53,17 +63,35 @@ static void ci_role_work(struct work_struct *work)
53 ci_role_stop(ci); 63 ci_role_stop(ci);
54 ci_role_start(ci, role); 64 ci_role_start(ci, role);
55 } 65 }
66}
67/**
68 * ci_otg_work - perform otg (vbus/id) event handle
69 * @work: work struct
70 */
71static void ci_otg_work(struct work_struct *work)
72{
73 struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work);
74
75 if (ci->id_event) {
76 ci->id_event = false;
77 ci_handle_id_switch(ci);
78 } else if (ci->b_sess_valid_event) {
79 ci->b_sess_valid_event = false;
80 ci_handle_vbus_change(ci);
81 } else
82 dev_err(ci->dev, "unexpected event occurs at %s\n", __func__);
56 83
57 enable_irq(ci->irq); 84 enable_irq(ci->irq);
58} 85}
59 86
87
60/** 88/**
61 * ci_hdrc_otg_init - initialize otg struct 89 * ci_hdrc_otg_init - initialize otg struct
62 * ci: the controller 90 * ci: the controller
63 */ 91 */
64int ci_hdrc_otg_init(struct ci_hdrc *ci) 92int ci_hdrc_otg_init(struct ci_hdrc *ci)
65{ 93{
66 INIT_WORK(&ci->work, ci_role_work); 94 INIT_WORK(&ci->work, ci_otg_work);
67 ci->wq = create_singlethread_workqueue("ci_otg"); 95 ci->wq = create_singlethread_workqueue("ci_otg");
68 if (!ci->wq) { 96 if (!ci->wq) {
69 dev_err(ci->dev, "can't create workqueue\n"); 97 dev_err(ci->dev, "can't create workqueue\n");
diff --git a/drivers/usb/chipidea/otg.h b/drivers/usb/chipidea/otg.h
index 8acf3df4e3e5..2d9f090733bc 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)
30int ci_hdrc_otg_init(struct ci_hdrc *ci); 30int ci_hdrc_otg_init(struct ci_hdrc *ci);
31void ci_hdrc_otg_destroy(struct ci_hdrc *ci); 31void ci_hdrc_otg_destroy(struct ci_hdrc *ci);
32enum ci_role ci_otg_role(struct ci_hdrc *ci); 32enum ci_role ci_otg_role(struct ci_hdrc *ci);
33void ci_handle_vbus_change(struct ci_hdrc *ci);
33 34
34#endif /* __DRIVERS_USB_CHIPIDEA_OTG_H */ 35#endif /* __DRIVERS_USB_CHIPIDEA_OTG_H */
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 24a100d751ca..c70ce3891d35 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)
85 /* interrupt, error, port change, reset, sleep/suspend */ 85 /* interrupt, error, port change, reset, sleep/suspend */
86 hw_write(ci, OP_USBINTR, ~0, 86 hw_write(ci, OP_USBINTR, ~0,
87 USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI); 87 USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI);
88 hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
88 } else { 89 } else {
89 hw_write(ci, OP_USBINTR, ~0, 0); 90 hw_write(ci, OP_USBINTR, ~0, 0);
91 hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
90 } 92 }
91 return 0; 93 return 0;
92} 94}
@@ -1460,6 +1462,7 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
1460 pm_runtime_get_sync(&_gadget->dev); 1462 pm_runtime_get_sync(&_gadget->dev);
1461 hw_device_reset(ci, USBMODE_CM_DC); 1463 hw_device_reset(ci, USBMODE_CM_DC);
1462 hw_device_state(ci, ci->ep0out->qh.dma); 1464 hw_device_state(ci, ci->ep0out->qh.dma);
1465 dev_dbg(ci->dev, "Connected to host\n");
1463 } else { 1466 } else {
1464 hw_device_state(ci, 0); 1467 hw_device_state(ci, 0);
1465 if (ci->platdata->notify_event) 1468 if (ci->platdata->notify_event)
@@ -1467,6 +1470,7 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
1467 CI_HDRC_CONTROLLER_STOPPED_EVENT); 1470 CI_HDRC_CONTROLLER_STOPPED_EVENT);
1468 _gadget_stop_activity(&ci->gadget); 1471 _gadget_stop_activity(&ci->gadget);
1469 pm_runtime_put_sync(&_gadget->dev); 1472 pm_runtime_put_sync(&_gadget->dev);
1473 dev_dbg(ci->dev, "Disconnected from host\n");
1470 } 1474 }
1471 } 1475 }
1472 1476
@@ -1822,6 +1826,9 @@ static int udc_start(struct ci_hdrc *ci)
1822 pm_runtime_no_callbacks(&ci->gadget.dev); 1826 pm_runtime_no_callbacks(&ci->gadget.dev);
1823 pm_runtime_enable(&ci->gadget.dev); 1827 pm_runtime_enable(&ci->gadget.dev);
1824 1828
1829 /* Update ci->vbus_active */
1830 ci_handle_vbus_change(ci);
1831
1825 return retval; 1832 return retval;
1826 1833
1827remove_trans: 1834remove_trans: