diff options
author | Peter Chen <peter.chen@freescale.com> | 2013-08-14 05:44:11 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-08-14 15:37:20 -0400 |
commit | a107f8c505cd8606ae192d24c70b380e980fbe67 (patch) | |
tree | c1451c9051588fe93537c83c034d62def233ed8f /drivers | |
parent | cbec6bd55a45fa88218ec5ea5ae91f9b96d158d0 (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.h | 5 | ||||
-rw-r--r-- | drivers/usb/chipidea/core.c | 28 | ||||
-rw-r--r-- | drivers/usb/chipidea/otg.c | 42 | ||||
-rw-r--r-- | drivers/usb/chipidea/otg.h | 1 | ||||
-rw-r--r-- | drivers/usb/chipidea/udc.c | 7 |
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 | */ |
136 | struct ci_hdrc { | 139 | struct 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 | ||
173 | static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci) | 178 | 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 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 | /** | 40 | void 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 | |
44 | static 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 | |||
55 | static 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 | */ | ||
71 | static 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 | */ |
64 | int ci_hdrc_otg_init(struct ci_hdrc *ci) | 92 | int 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) | |||
30 | int ci_hdrc_otg_init(struct ci_hdrc *ci); | 30 | int ci_hdrc_otg_init(struct ci_hdrc *ci); |
31 | void ci_hdrc_otg_destroy(struct ci_hdrc *ci); | 31 | void ci_hdrc_otg_destroy(struct ci_hdrc *ci); |
32 | enum ci_role ci_otg_role(struct ci_hdrc *ci); | 32 | enum ci_role ci_otg_role(struct ci_hdrc *ci); |
33 | void 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 | ||
1827 | remove_trans: | 1834 | remove_trans: |