1 // SPDX-License-Identifier: GPL-2.0
3 * otg.c - ChipIdea USB IP core OTG driver
5 * Copyright (C) 2013 Freescale Semiconductor, Inc.
11 * This file mainly handles otgsc register, OTG fsm operations for HNP and SRP
15 #include <linux/usb/otg.h>
16 #include <linux/usb/gadget.h>
17 #include <linux/usb/chipidea.h>
25 * hw_read_otgsc returns otgsc register bits value.
26 * @mask: bitfield mask
28 u32
hw_read_otgsc(struct ci_hdrc
*ci
, u32 mask
)
30 struct ci_hdrc_cable
*cable
;
31 u32 val
= hw_read(ci
, OP_OTGSC
, mask
);
34 * If using extcon framework for VBUS and/or ID signal
35 * detection overwrite OTGSC register value
37 cable
= &ci
->platdata
->vbus_extcon
;
38 if (!IS_ERR(cable
->edev
)) {
55 cable
= &ci
->platdata
->id_extcon
;
56 if (!IS_ERR(cable
->edev
)) {
63 val
&= ~OTGSC_ID
; /* host */
65 val
|= OTGSC_ID
; /* device */
77 * hw_write_otgsc updates target bits of OTGSC register.
78 * @mask: bitfield mask
79 * @data: to be written
81 void hw_write_otgsc(struct ci_hdrc
*ci
, u32 mask
, u32 data
)
83 struct ci_hdrc_cable
*cable
;
85 cable
= &ci
->platdata
->vbus_extcon
;
86 if (!IS_ERR(cable
->edev
)) {
87 if (data
& mask
& OTGSC_BSVIS
)
88 cable
->changed
= false;
90 /* Don't enable vbus interrupt if using external notifier */
91 if (data
& mask
& OTGSC_BSVIE
) {
92 cable
->enabled
= true;
94 } else if (mask
& OTGSC_BSVIE
) {
95 cable
->enabled
= false;
99 cable
= &ci
->platdata
->id_extcon
;
100 if (!IS_ERR(cable
->edev
)) {
101 if (data
& mask
& OTGSC_IDIS
)
102 cable
->changed
= false;
104 /* Don't enable id interrupt if using external notifier */
105 if (data
& mask
& OTGSC_IDIE
) {
106 cable
->enabled
= true;
108 } else if (mask
& OTGSC_IDIE
) {
109 cable
->enabled
= false;
113 hw_write(ci
, OP_OTGSC
, mask
| OTGSC_INT_STATUS_BITS
, data
);
117 * ci_otg_role - pick role based on ID pin state
118 * @ci: the controller
120 enum ci_role
ci_otg_role(struct ci_hdrc
*ci
)
122 enum ci_role role
= hw_read_otgsc(ci
, OTGSC_ID
)
129 void ci_handle_vbus_change(struct ci_hdrc
*ci
)
134 if (hw_read_otgsc(ci
, OTGSC_BSV
) && !ci
->vbus_active
)
135 usb_gadget_vbus_connect(&ci
->gadget
);
136 else if (!hw_read_otgsc(ci
, OTGSC_BSV
) && ci
->vbus_active
)
137 usb_gadget_vbus_disconnect(&ci
->gadget
);
141 * When we switch to device mode, the vbus value should be lower
142 * than OTGSC_BSV before connecting to host.
144 * @ci: the controller
146 * This function returns an error code if timeout
148 static int hw_wait_vbus_lower_bsv(struct ci_hdrc
*ci
)
150 unsigned long elapse
= jiffies
+ msecs_to_jiffies(5000);
151 u32 mask
= OTGSC_BSV
;
153 while (hw_read_otgsc(ci
, mask
)) {
154 if (time_after(jiffies
, elapse
)) {
155 dev_err(ci
->dev
, "timeout waiting for %08x in OTGSC\n",
165 static void ci_handle_id_switch(struct ci_hdrc
*ci
)
167 enum ci_role role
= ci_otg_role(ci
);
169 if (role
!= ci
->role
) {
170 dev_dbg(ci
->dev
, "switching from %s to %s\n",
171 ci_role(ci
)->name
, ci
->roles
[role
]->name
);
175 if (role
== CI_ROLE_GADGET
&&
176 IS_ERR(ci
->platdata
->vbus_extcon
.edev
))
178 * Wait vbus lower than OTGSC_BSV before connecting
179 * to host. If connecting status is from an external
180 * connector instead of register, we don't need to
181 * care vbus on the board, since it will not affect
182 * external connector status.
184 hw_wait_vbus_lower_bsv(ci
);
186 ci_role_start(ci
, role
);
187 /* vbus change may have already occurred */
188 if (role
== CI_ROLE_GADGET
)
189 ci_handle_vbus_change(ci
);
193 * ci_otg_work - perform otg (vbus/id) event handle
196 static void ci_otg_work(struct work_struct
*work
)
198 struct ci_hdrc
*ci
= container_of(work
, struct ci_hdrc
, work
);
200 if (ci_otg_is_fsm_mode(ci
) && !ci_otg_fsm_work(ci
)) {
205 pm_runtime_get_sync(ci
->dev
);
207 ci
->id_event
= false;
208 ci_handle_id_switch(ci
);
209 } else if (ci
->b_sess_valid_event
) {
210 ci
->b_sess_valid_event
= false;
211 ci_handle_vbus_change(ci
);
213 dev_err(ci
->dev
, "unexpected event occurs at %s\n", __func__
);
214 pm_runtime_put_sync(ci
->dev
);
221 * ci_hdrc_otg_init - initialize otg struct
224 int ci_hdrc_otg_init(struct ci_hdrc
*ci
)
226 INIT_WORK(&ci
->work
, ci_otg_work
);
227 ci
->wq
= create_freezable_workqueue("ci_otg");
229 dev_err(ci
->dev
, "can't create workqueue\n");
233 if (ci_otg_is_fsm_mode(ci
))
234 return ci_hdrc_otg_fsm_init(ci
);
240 * ci_hdrc_otg_destroy - destroy otg struct
243 void ci_hdrc_otg_destroy(struct ci_hdrc
*ci
)
246 flush_workqueue(ci
->wq
);
247 destroy_workqueue(ci
->wq
);
249 /* Disable all OTG irq and clear status */
250 hw_write_otgsc(ci
, OTGSC_INT_EN_BITS
| OTGSC_INT_STATUS_BITS
,
251 OTGSC_INT_STATUS_BITS
);
252 if (ci_otg_is_fsm_mode(ci
))
253 ci_hdrc_otg_fsm_remove(ci
);