15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2df2069acSChunfeng Yun /*
3df2069acSChunfeng Yun * mtu3_gadget.c - MediaTek usb3 DRD peripheral support
4df2069acSChunfeng Yun *
5df2069acSChunfeng Yun * Copyright (C) 2016 MediaTek Inc.
6df2069acSChunfeng Yun *
7df2069acSChunfeng Yun * Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
8df2069acSChunfeng Yun */
9df2069acSChunfeng Yun
10df2069acSChunfeng Yun #include "mtu3.h"
1183374e03SChunfeng Yun #include "mtu3_trace.h"
12df2069acSChunfeng Yun
mtu3_req_complete(struct mtu3_ep * mep,struct usb_request * req,int status)13df2069acSChunfeng Yun void mtu3_req_complete(struct mtu3_ep *mep,
14df2069acSChunfeng Yun struct usb_request *req, int status)
15df2069acSChunfeng Yun __releases(mep->mtu->lock)
16df2069acSChunfeng Yun __acquires(mep->mtu->lock)
17df2069acSChunfeng Yun {
18ab4dc051SChunfeng Yun struct mtu3_request *mreq = to_mtu3_request(req);
19ab4dc051SChunfeng Yun struct mtu3 *mtu = mreq->mtu;
20df2069acSChunfeng Yun
21df2069acSChunfeng Yun list_del(&mreq->list);
22ab4dc051SChunfeng Yun if (req->status == -EINPROGRESS)
23ab4dc051SChunfeng Yun req->status = status;
24df2069acSChunfeng Yun
2583374e03SChunfeng Yun trace_mtu3_req_complete(mreq);
26df2069acSChunfeng Yun
27df2069acSChunfeng Yun /* ep0 makes use of PIO, needn't unmap it */
28df2069acSChunfeng Yun if (mep->epnum)
29df2069acSChunfeng Yun usb_gadget_unmap_request(&mtu->g, req, mep->is_in);
30df2069acSChunfeng Yun
31ab4dc051SChunfeng Yun dev_dbg(mtu->dev, "%s complete req: %p, sts %d, %d/%d\n",
32ab4dc051SChunfeng Yun mep->name, req, req->status, req->actual, req->length);
33df2069acSChunfeng Yun
34*ed50be81SChunfeng Yun spin_unlock(&mtu->lock);
35ab4dc051SChunfeng Yun usb_gadget_giveback_request(&mep->ep, req);
36df2069acSChunfeng Yun spin_lock(&mtu->lock);
37df2069acSChunfeng Yun }
38df2069acSChunfeng Yun
nuke(struct mtu3_ep * mep,const int status)39df2069acSChunfeng Yun static void nuke(struct mtu3_ep *mep, const int status)
40df2069acSChunfeng Yun {
41df2069acSChunfeng Yun struct mtu3_request *mreq = NULL;
42df2069acSChunfeng Yun
43df2069acSChunfeng Yun if (list_empty(&mep->req_list))
44df2069acSChunfeng Yun return;
45df2069acSChunfeng Yun
46df2069acSChunfeng Yun dev_dbg(mep->mtu->dev, "abort %s's req: sts %d\n", mep->name, status);
47df2069acSChunfeng Yun
48df2069acSChunfeng Yun /* exclude EP0 */
49df2069acSChunfeng Yun if (mep->epnum)
50df2069acSChunfeng Yun mtu3_qmu_flush(mep);
51df2069acSChunfeng Yun
52df2069acSChunfeng Yun while (!list_empty(&mep->req_list)) {
53df2069acSChunfeng Yun mreq = list_first_entry(&mep->req_list,
54df2069acSChunfeng Yun struct mtu3_request, list);
55df2069acSChunfeng Yun mtu3_req_complete(mep, &mreq->request, status);
56df2069acSChunfeng Yun }
57df2069acSChunfeng Yun }
58df2069acSChunfeng Yun
mtu3_ep_enable(struct mtu3_ep * mep)59df2069acSChunfeng Yun static int mtu3_ep_enable(struct mtu3_ep *mep)
60df2069acSChunfeng Yun {
61df2069acSChunfeng Yun const struct usb_endpoint_descriptor *desc;
62a29de31bSChunfeng Yun const struct usb_ss_ep_comp_descriptor *comp_desc;
63df2069acSChunfeng Yun struct mtu3 *mtu = mep->mtu;
64df2069acSChunfeng Yun u32 interval = 0;
65df2069acSChunfeng Yun u32 mult = 0;
66df2069acSChunfeng Yun u32 burst = 0;
67df2069acSChunfeng Yun int ret;
68df2069acSChunfeng Yun
69df2069acSChunfeng Yun desc = mep->desc;
70a29de31bSChunfeng Yun comp_desc = mep->comp_desc;
71df2069acSChunfeng Yun mep->type = usb_endpoint_type(desc);
7244e4439dSChunfeng Yun mep->maxp = usb_endpoint_maxp(desc);
73df2069acSChunfeng Yun
74df2069acSChunfeng Yun switch (mtu->g.speed) {
75a29de31bSChunfeng Yun case USB_SPEED_SUPER:
764d79e042SChunfeng Yun case USB_SPEED_SUPER_PLUS:
77a29de31bSChunfeng Yun if (usb_endpoint_xfer_int(desc) ||
78a29de31bSChunfeng Yun usb_endpoint_xfer_isoc(desc)) {
79a29de31bSChunfeng Yun interval = desc->bInterval;
80e3d4621cSChunfeng Yun interval = clamp_val(interval, 1, 16);
81a29de31bSChunfeng Yun if (usb_endpoint_xfer_isoc(desc) && comp_desc)
82a29de31bSChunfeng Yun mult = comp_desc->bmAttributes;
83a29de31bSChunfeng Yun }
84a29de31bSChunfeng Yun if (comp_desc)
85a29de31bSChunfeng Yun burst = comp_desc->bMaxBurst;
86a29de31bSChunfeng Yun
87a29de31bSChunfeng Yun break;
88df2069acSChunfeng Yun case USB_SPEED_HIGH:
89df2069acSChunfeng Yun if (usb_endpoint_xfer_isoc(desc) ||
90df2069acSChunfeng Yun usb_endpoint_xfer_int(desc)) {
91df2069acSChunfeng Yun interval = desc->bInterval;
92e3d4621cSChunfeng Yun interval = clamp_val(interval, 1, 16);
9344e4439dSChunfeng Yun mult = usb_endpoint_maxp_mult(desc) - 1;
94df2069acSChunfeng Yun }
95df2069acSChunfeng Yun break;
9643f3b8cbSChunfeng Yun case USB_SPEED_FULL:
9743f3b8cbSChunfeng Yun if (usb_endpoint_xfer_isoc(desc))
9843f3b8cbSChunfeng Yun interval = clamp_val(desc->bInterval, 1, 16);
9943f3b8cbSChunfeng Yun else if (usb_endpoint_xfer_int(desc))
10043f3b8cbSChunfeng Yun interval = clamp_val(desc->bInterval, 1, 255);
10143f3b8cbSChunfeng Yun
10243f3b8cbSChunfeng Yun break;
103df2069acSChunfeng Yun default:
104df2069acSChunfeng Yun break; /*others are ignored */
105df2069acSChunfeng Yun }
106df2069acSChunfeng Yun
107df2069acSChunfeng Yun dev_dbg(mtu->dev, "%s maxp:%d, interval:%d, burst:%d, mult:%d\n",
108df2069acSChunfeng Yun __func__, mep->maxp, interval, burst, mult);
109df2069acSChunfeng Yun
110df2069acSChunfeng Yun mep->ep.maxpacket = mep->maxp;
111df2069acSChunfeng Yun mep->ep.desc = desc;
112a29de31bSChunfeng Yun mep->ep.comp_desc = comp_desc;
113df2069acSChunfeng Yun
114df2069acSChunfeng Yun /* slot mainly affects bulk/isoc transfer, so ignore int */
115df2069acSChunfeng Yun mep->slot = usb_endpoint_xfer_int(desc) ? 0 : mtu->slot;
116df2069acSChunfeng Yun
117df2069acSChunfeng Yun ret = mtu3_config_ep(mtu, mep, interval, burst, mult);
118df2069acSChunfeng Yun if (ret < 0)
119df2069acSChunfeng Yun return ret;
120df2069acSChunfeng Yun
121df2069acSChunfeng Yun ret = mtu3_gpd_ring_alloc(mep);
122df2069acSChunfeng Yun if (ret < 0) {
123df2069acSChunfeng Yun mtu3_deconfig_ep(mtu, mep);
124df2069acSChunfeng Yun return ret;
125df2069acSChunfeng Yun }
126df2069acSChunfeng Yun
127df2069acSChunfeng Yun mtu3_qmu_start(mep);
128df2069acSChunfeng Yun
129df2069acSChunfeng Yun return 0;
130df2069acSChunfeng Yun }
131df2069acSChunfeng Yun
mtu3_ep_disable(struct mtu3_ep * mep)132df2069acSChunfeng Yun static int mtu3_ep_disable(struct mtu3_ep *mep)
133df2069acSChunfeng Yun {
134df2069acSChunfeng Yun struct mtu3 *mtu = mep->mtu;
135df2069acSChunfeng Yun
136df2069acSChunfeng Yun /* abort all pending requests */
137df2069acSChunfeng Yun nuke(mep, -ESHUTDOWN);
1385aba179cSChunfeng Yun mtu3_qmu_stop(mep);
139df2069acSChunfeng Yun mtu3_deconfig_ep(mtu, mep);
140df2069acSChunfeng Yun mtu3_gpd_ring_free(mep);
141df2069acSChunfeng Yun
142df2069acSChunfeng Yun mep->desc = NULL;
143df2069acSChunfeng Yun mep->ep.desc = NULL;
144a29de31bSChunfeng Yun mep->comp_desc = NULL;
145df2069acSChunfeng Yun mep->type = 0;
146df2069acSChunfeng Yun mep->flags = 0;
147df2069acSChunfeng Yun
148df2069acSChunfeng Yun return 0;
149df2069acSChunfeng Yun }
150df2069acSChunfeng Yun
mtu3_gadget_ep_enable(struct usb_ep * ep,const struct usb_endpoint_descriptor * desc)151df2069acSChunfeng Yun static int mtu3_gadget_ep_enable(struct usb_ep *ep,
152df2069acSChunfeng Yun const struct usb_endpoint_descriptor *desc)
153df2069acSChunfeng Yun {
154df2069acSChunfeng Yun struct mtu3_ep *mep;
155df2069acSChunfeng Yun struct mtu3 *mtu;
156df2069acSChunfeng Yun unsigned long flags;
157df2069acSChunfeng Yun int ret = -EINVAL;
158df2069acSChunfeng Yun
159df2069acSChunfeng Yun if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
160df2069acSChunfeng Yun pr_debug("%s invalid parameters\n", __func__);
161df2069acSChunfeng Yun return -EINVAL;
162df2069acSChunfeng Yun }
163df2069acSChunfeng Yun
164df2069acSChunfeng Yun if (!desc->wMaxPacketSize) {
165df2069acSChunfeng Yun pr_debug("%s missing wMaxPacketSize\n", __func__);
166df2069acSChunfeng Yun return -EINVAL;
167df2069acSChunfeng Yun }
168df2069acSChunfeng Yun mep = to_mtu3_ep(ep);
169df2069acSChunfeng Yun mtu = mep->mtu;
170df2069acSChunfeng Yun
171df2069acSChunfeng Yun /* check ep number and direction against endpoint */
172df2069acSChunfeng Yun if (usb_endpoint_num(desc) != mep->epnum)
173df2069acSChunfeng Yun return -EINVAL;
174df2069acSChunfeng Yun
175df2069acSChunfeng Yun if (!!usb_endpoint_dir_in(desc) ^ !!mep->is_in)
176df2069acSChunfeng Yun return -EINVAL;
177df2069acSChunfeng Yun
178df2069acSChunfeng Yun dev_dbg(mtu->dev, "%s %s\n", __func__, ep->name);
179df2069acSChunfeng Yun
180df2069acSChunfeng Yun if (mep->flags & MTU3_EP_ENABLED) {
181df2069acSChunfeng Yun dev_WARN_ONCE(mtu->dev, true, "%s is already enabled\n",
182df2069acSChunfeng Yun mep->name);
183df2069acSChunfeng Yun return 0;
184df2069acSChunfeng Yun }
185df2069acSChunfeng Yun
186df2069acSChunfeng Yun spin_lock_irqsave(&mtu->lock, flags);
187df2069acSChunfeng Yun mep->desc = desc;
188a29de31bSChunfeng Yun mep->comp_desc = ep->comp_desc;
189df2069acSChunfeng Yun
190df2069acSChunfeng Yun ret = mtu3_ep_enable(mep);
191df2069acSChunfeng Yun if (ret)
192df2069acSChunfeng Yun goto error;
193df2069acSChunfeng Yun
19454402373SChunfeng Yun mep->flags = MTU3_EP_ENABLED;
195df2069acSChunfeng Yun mtu->active_ep++;
196df2069acSChunfeng Yun
197df2069acSChunfeng Yun error:
198df2069acSChunfeng Yun spin_unlock_irqrestore(&mtu->lock, flags);
199df2069acSChunfeng Yun
200df2069acSChunfeng Yun dev_dbg(mtu->dev, "%s active_ep=%d\n", __func__, mtu->active_ep);
20183374e03SChunfeng Yun trace_mtu3_gadget_ep_enable(mep);
202df2069acSChunfeng Yun
203df2069acSChunfeng Yun return ret;
204df2069acSChunfeng Yun }
205df2069acSChunfeng Yun
mtu3_gadget_ep_disable(struct usb_ep * ep)206df2069acSChunfeng Yun static int mtu3_gadget_ep_disable(struct usb_ep *ep)
207df2069acSChunfeng Yun {
208df2069acSChunfeng Yun struct mtu3_ep *mep = to_mtu3_ep(ep);
209df2069acSChunfeng Yun struct mtu3 *mtu = mep->mtu;
210df2069acSChunfeng Yun unsigned long flags;
211df2069acSChunfeng Yun
212df2069acSChunfeng Yun dev_dbg(mtu->dev, "%s %s\n", __func__, mep->name);
21383374e03SChunfeng Yun trace_mtu3_gadget_ep_disable(mep);
214df2069acSChunfeng Yun
215df2069acSChunfeng Yun if (!(mep->flags & MTU3_EP_ENABLED)) {
216df2069acSChunfeng Yun dev_warn(mtu->dev, "%s is already disabled\n", mep->name);
217df2069acSChunfeng Yun return 0;
218df2069acSChunfeng Yun }
219df2069acSChunfeng Yun
220df2069acSChunfeng Yun spin_lock_irqsave(&mtu->lock, flags);
221df2069acSChunfeng Yun mtu3_ep_disable(mep);
22254402373SChunfeng Yun mep->flags = 0;
223df2069acSChunfeng Yun mtu->active_ep--;
224df2069acSChunfeng Yun spin_unlock_irqrestore(&(mtu->lock), flags);
225df2069acSChunfeng Yun
226df2069acSChunfeng Yun dev_dbg(mtu->dev, "%s active_ep=%d, mtu3 is_active=%d\n",
227df2069acSChunfeng Yun __func__, mtu->active_ep, mtu->is_active);
228df2069acSChunfeng Yun
229df2069acSChunfeng Yun return 0;
230df2069acSChunfeng Yun }
231df2069acSChunfeng Yun
mtu3_alloc_request(struct usb_ep * ep,gfp_t gfp_flags)232df2069acSChunfeng Yun struct usb_request *mtu3_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
233df2069acSChunfeng Yun {
234df2069acSChunfeng Yun struct mtu3_ep *mep = to_mtu3_ep(ep);
235df2069acSChunfeng Yun struct mtu3_request *mreq;
236df2069acSChunfeng Yun
237df2069acSChunfeng Yun mreq = kzalloc(sizeof(*mreq), gfp_flags);
238df2069acSChunfeng Yun if (!mreq)
239df2069acSChunfeng Yun return NULL;
240df2069acSChunfeng Yun
241df2069acSChunfeng Yun mreq->request.dma = DMA_ADDR_INVALID;
242df2069acSChunfeng Yun mreq->epnum = mep->epnum;
243df2069acSChunfeng Yun mreq->mep = mep;
2448c313e3bSChunfeng Yun INIT_LIST_HEAD(&mreq->list);
24583374e03SChunfeng Yun trace_mtu3_alloc_request(mreq);
246df2069acSChunfeng Yun
247df2069acSChunfeng Yun return &mreq->request;
248df2069acSChunfeng Yun }
249df2069acSChunfeng Yun
mtu3_free_request(struct usb_ep * ep,struct usb_request * req)250df2069acSChunfeng Yun void mtu3_free_request(struct usb_ep *ep, struct usb_request *req)
251df2069acSChunfeng Yun {
25283374e03SChunfeng Yun struct mtu3_request *mreq = to_mtu3_request(req);
25383374e03SChunfeng Yun
25483374e03SChunfeng Yun trace_mtu3_free_request(mreq);
25583374e03SChunfeng Yun kfree(mreq);
256df2069acSChunfeng Yun }
257df2069acSChunfeng Yun
mtu3_gadget_queue(struct usb_ep * ep,struct usb_request * req,gfp_t gfp_flags)258df2069acSChunfeng Yun static int mtu3_gadget_queue(struct usb_ep *ep,
259df2069acSChunfeng Yun struct usb_request *req, gfp_t gfp_flags)
260df2069acSChunfeng Yun {
261fde9156aSChunfeng Yun struct mtu3_ep *mep = to_mtu3_ep(ep);
262fde9156aSChunfeng Yun struct mtu3_request *mreq = to_mtu3_request(req);
263fde9156aSChunfeng Yun struct mtu3 *mtu = mep->mtu;
264df2069acSChunfeng Yun unsigned long flags;
265df2069acSChunfeng Yun int ret = 0;
266df2069acSChunfeng Yun
267df2069acSChunfeng Yun if (!req->buf)
268df2069acSChunfeng Yun return -ENODATA;
269df2069acSChunfeng Yun
270df2069acSChunfeng Yun if (mreq->mep != mep)
271df2069acSChunfeng Yun return -EINVAL;
272df2069acSChunfeng Yun
273df2069acSChunfeng Yun dev_dbg(mtu->dev, "%s %s EP%d(%s), req=%p, maxp=%d, len#%d\n",
274df2069acSChunfeng Yun __func__, mep->is_in ? "TX" : "RX", mreq->epnum, ep->name,
275df2069acSChunfeng Yun mreq, ep->maxpacket, mreq->request.length);
276df2069acSChunfeng Yun
27748e0d373SChunfeng Yun if (req->length > GPD_BUF_SIZE ||
27848e0d373SChunfeng Yun (mtu->gen2cp && req->length > GPD_BUF_SIZE_EL)) {
279df2069acSChunfeng Yun dev_warn(mtu->dev,
280df2069acSChunfeng Yun "req length > supported MAX:%d requested:%d\n",
28148e0d373SChunfeng Yun mtu->gen2cp ? GPD_BUF_SIZE_EL : GPD_BUF_SIZE,
28248e0d373SChunfeng Yun req->length);
283df2069acSChunfeng Yun return -EOPNOTSUPP;
284df2069acSChunfeng Yun }
285df2069acSChunfeng Yun
286df2069acSChunfeng Yun /* don't queue if the ep is down */
287df2069acSChunfeng Yun if (!mep->desc) {
288df2069acSChunfeng Yun dev_dbg(mtu->dev, "req=%p queued to %s while it's disabled\n",
289df2069acSChunfeng Yun req, ep->name);
290df2069acSChunfeng Yun return -ESHUTDOWN;
291df2069acSChunfeng Yun }
292df2069acSChunfeng Yun
293fde9156aSChunfeng Yun mreq->mtu = mtu;
294df2069acSChunfeng Yun mreq->request.actual = 0;
295df2069acSChunfeng Yun mreq->request.status = -EINPROGRESS;
296df2069acSChunfeng Yun
297df2069acSChunfeng Yun ret = usb_gadget_map_request(&mtu->g, req, mep->is_in);
298df2069acSChunfeng Yun if (ret) {
299df2069acSChunfeng Yun dev_err(mtu->dev, "dma mapping failed\n");
300df2069acSChunfeng Yun return ret;
301df2069acSChunfeng Yun }
302df2069acSChunfeng Yun
303df2069acSChunfeng Yun spin_lock_irqsave(&mtu->lock, flags);
304df2069acSChunfeng Yun
305df2069acSChunfeng Yun if (mtu3_prepare_transfer(mep)) {
306df2069acSChunfeng Yun ret = -EAGAIN;
307df2069acSChunfeng Yun goto error;
308df2069acSChunfeng Yun }
309df2069acSChunfeng Yun
310df2069acSChunfeng Yun list_add_tail(&mreq->list, &mep->req_list);
311df2069acSChunfeng Yun mtu3_insert_gpd(mep, mreq);
312df2069acSChunfeng Yun mtu3_qmu_resume(mep);
313df2069acSChunfeng Yun
314df2069acSChunfeng Yun error:
315df2069acSChunfeng Yun spin_unlock_irqrestore(&mtu->lock, flags);
31683374e03SChunfeng Yun trace_mtu3_gadget_queue(mreq);
317df2069acSChunfeng Yun
318df2069acSChunfeng Yun return ret;
319df2069acSChunfeng Yun }
320df2069acSChunfeng Yun
mtu3_gadget_dequeue(struct usb_ep * ep,struct usb_request * req)321df2069acSChunfeng Yun static int mtu3_gadget_dequeue(struct usb_ep *ep, struct usb_request *req)
322df2069acSChunfeng Yun {
323df2069acSChunfeng Yun struct mtu3_ep *mep = to_mtu3_ep(ep);
324df2069acSChunfeng Yun struct mtu3_request *mreq = to_mtu3_request(req);
325df2069acSChunfeng Yun struct mtu3_request *r;
326fde9156aSChunfeng Yun struct mtu3 *mtu = mep->mtu;
327df2069acSChunfeng Yun unsigned long flags;
328df2069acSChunfeng Yun int ret = 0;
329df2069acSChunfeng Yun
330fde9156aSChunfeng Yun if (mreq->mep != mep)
331df2069acSChunfeng Yun return -EINVAL;
332df2069acSChunfeng Yun
333df2069acSChunfeng Yun dev_dbg(mtu->dev, "%s : req=%p\n", __func__, req);
33483374e03SChunfeng Yun trace_mtu3_gadget_dequeue(mreq);
335df2069acSChunfeng Yun
336df2069acSChunfeng Yun spin_lock_irqsave(&mtu->lock, flags);
337df2069acSChunfeng Yun
338df2069acSChunfeng Yun list_for_each_entry(r, &mep->req_list, list) {
339df2069acSChunfeng Yun if (r == mreq)
340df2069acSChunfeng Yun break;
341df2069acSChunfeng Yun }
342df2069acSChunfeng Yun if (r != mreq) {
343df2069acSChunfeng Yun dev_dbg(mtu->dev, "req=%p not queued to %s\n", req, ep->name);
344df2069acSChunfeng Yun ret = -EINVAL;
345df2069acSChunfeng Yun goto done;
346df2069acSChunfeng Yun }
347df2069acSChunfeng Yun
348df2069acSChunfeng Yun mtu3_qmu_flush(mep); /* REVISIT: set BPS ?? */
349df2069acSChunfeng Yun mtu3_req_complete(mep, req, -ECONNRESET);
350df2069acSChunfeng Yun mtu3_qmu_start(mep);
351df2069acSChunfeng Yun
352df2069acSChunfeng Yun done:
353df2069acSChunfeng Yun spin_unlock_irqrestore(&mtu->lock, flags);
354df2069acSChunfeng Yun
355df2069acSChunfeng Yun return ret;
356df2069acSChunfeng Yun }
357df2069acSChunfeng Yun
358df2069acSChunfeng Yun /*
359df2069acSChunfeng Yun * Set or clear the halt bit of an EP.
360df2069acSChunfeng Yun * A halted EP won't TX/RX any data but will queue requests.
361df2069acSChunfeng Yun */
mtu3_gadget_ep_set_halt(struct usb_ep * ep,int value)362df2069acSChunfeng Yun static int mtu3_gadget_ep_set_halt(struct usb_ep *ep, int value)
363df2069acSChunfeng Yun {
364df2069acSChunfeng Yun struct mtu3_ep *mep = to_mtu3_ep(ep);
365df2069acSChunfeng Yun struct mtu3 *mtu = mep->mtu;
366df2069acSChunfeng Yun struct mtu3_request *mreq;
367df2069acSChunfeng Yun unsigned long flags;
368df2069acSChunfeng Yun int ret = 0;
369df2069acSChunfeng Yun
370df2069acSChunfeng Yun dev_dbg(mtu->dev, "%s : %s...", __func__, ep->name);
371df2069acSChunfeng Yun
372df2069acSChunfeng Yun spin_lock_irqsave(&mtu->lock, flags);
373df2069acSChunfeng Yun
374df2069acSChunfeng Yun if (mep->type == USB_ENDPOINT_XFER_ISOC) {
375df2069acSChunfeng Yun ret = -EINVAL;
376df2069acSChunfeng Yun goto done;
377df2069acSChunfeng Yun }
378df2069acSChunfeng Yun
379df2069acSChunfeng Yun mreq = next_request(mep);
380df2069acSChunfeng Yun if (value) {
381df2069acSChunfeng Yun /*
382df2069acSChunfeng Yun * If there is not request for TX-EP, QMU will not transfer
383df2069acSChunfeng Yun * data to TX-FIFO, so no need check whether TX-FIFO
384df2069acSChunfeng Yun * holds bytes or not here
385df2069acSChunfeng Yun */
386df2069acSChunfeng Yun if (mreq) {
387df2069acSChunfeng Yun dev_dbg(mtu->dev, "req in progress, cannot halt %s\n",
388df2069acSChunfeng Yun ep->name);
389df2069acSChunfeng Yun ret = -EAGAIN;
390df2069acSChunfeng Yun goto done;
391df2069acSChunfeng Yun }
392df2069acSChunfeng Yun } else {
39354402373SChunfeng Yun mep->flags &= ~MTU3_EP_WEDGE;
394df2069acSChunfeng Yun }
395df2069acSChunfeng Yun
396df2069acSChunfeng Yun dev_dbg(mtu->dev, "%s %s stall\n", ep->name, value ? "set" : "clear");
397df2069acSChunfeng Yun
398df2069acSChunfeng Yun mtu3_ep_stall_set(mep, value);
399df2069acSChunfeng Yun
400df2069acSChunfeng Yun done:
401df2069acSChunfeng Yun spin_unlock_irqrestore(&mtu->lock, flags);
40283374e03SChunfeng Yun trace_mtu3_gadget_ep_set_halt(mep);
403df2069acSChunfeng Yun
404df2069acSChunfeng Yun return ret;
405df2069acSChunfeng Yun }
406df2069acSChunfeng Yun
407df2069acSChunfeng Yun /* Sets the halt feature with the clear requests ignored */
mtu3_gadget_ep_set_wedge(struct usb_ep * ep)408df2069acSChunfeng Yun static int mtu3_gadget_ep_set_wedge(struct usb_ep *ep)
409df2069acSChunfeng Yun {
410df2069acSChunfeng Yun struct mtu3_ep *mep = to_mtu3_ep(ep);
411df2069acSChunfeng Yun
41254402373SChunfeng Yun mep->flags |= MTU3_EP_WEDGE;
413df2069acSChunfeng Yun
414df2069acSChunfeng Yun return usb_ep_set_halt(ep);
415df2069acSChunfeng Yun }
416df2069acSChunfeng Yun
417df2069acSChunfeng Yun static const struct usb_ep_ops mtu3_ep_ops = {
418df2069acSChunfeng Yun .enable = mtu3_gadget_ep_enable,
419df2069acSChunfeng Yun .disable = mtu3_gadget_ep_disable,
420df2069acSChunfeng Yun .alloc_request = mtu3_alloc_request,
421df2069acSChunfeng Yun .free_request = mtu3_free_request,
422df2069acSChunfeng Yun .queue = mtu3_gadget_queue,
423df2069acSChunfeng Yun .dequeue = mtu3_gadget_dequeue,
424df2069acSChunfeng Yun .set_halt = mtu3_gadget_ep_set_halt,
425df2069acSChunfeng Yun .set_wedge = mtu3_gadget_ep_set_wedge,
426df2069acSChunfeng Yun };
427df2069acSChunfeng Yun
mtu3_gadget_get_frame(struct usb_gadget * gadget)428df2069acSChunfeng Yun static int mtu3_gadget_get_frame(struct usb_gadget *gadget)
429df2069acSChunfeng Yun {
430df2069acSChunfeng Yun struct mtu3 *mtu = gadget_to_mtu3(gadget);
431df2069acSChunfeng Yun
432df2069acSChunfeng Yun return (int)mtu3_readl(mtu->mac_base, U3D_USB20_FRAME_NUM);
433df2069acSChunfeng Yun }
434df2069acSChunfeng Yun
function_wake_notif(struct mtu3 * mtu,u8 intf)4351742b765SChunfeng Yun static void function_wake_notif(struct mtu3 *mtu, u8 intf)
4361742b765SChunfeng Yun {
4371742b765SChunfeng Yun mtu3_writel(mtu->mac_base, U3D_DEV_NOTIF_0,
4381742b765SChunfeng Yun TYPE_FUNCTION_WAKE | DEV_NOTIF_VAL_FW(intf));
4391742b765SChunfeng Yun mtu3_setbits(mtu->mac_base, U3D_DEV_NOTIF_0, SEND_DEV_NOTIF);
4401742b765SChunfeng Yun }
4411742b765SChunfeng Yun
mtu3_gadget_wakeup(struct usb_gadget * gadget)442df2069acSChunfeng Yun static int mtu3_gadget_wakeup(struct usb_gadget *gadget)
443df2069acSChunfeng Yun {
444df2069acSChunfeng Yun struct mtu3 *mtu = gadget_to_mtu3(gadget);
445df2069acSChunfeng Yun unsigned long flags;
446df2069acSChunfeng Yun
447df2069acSChunfeng Yun dev_dbg(mtu->dev, "%s\n", __func__);
448df2069acSChunfeng Yun
449df2069acSChunfeng Yun /* remote wakeup feature is not enabled by host */
450df2069acSChunfeng Yun if (!mtu->may_wakeup)
451df2069acSChunfeng Yun return -EOPNOTSUPP;
452df2069acSChunfeng Yun
453df2069acSChunfeng Yun spin_lock_irqsave(&mtu->lock, flags);
4544d79e042SChunfeng Yun if (mtu->g.speed >= USB_SPEED_SUPER) {
4551742b765SChunfeng Yun /*
4561742b765SChunfeng Yun * class driver may do function wakeup even UFP is in U0,
4571742b765SChunfeng Yun * and UX_EXIT only takes effect in U1/U2/U3;
4581742b765SChunfeng Yun */
459a29de31bSChunfeng Yun mtu3_setbits(mtu->mac_base, U3D_LINK_POWER_CONTROL, UX_EXIT);
4601742b765SChunfeng Yun /*
4611742b765SChunfeng Yun * Assume there's only one function on the composite device
4621742b765SChunfeng Yun * and enable remote wake for the first interface.
4631742b765SChunfeng Yun * FIXME if the IAD (interface association descriptor) shows
4641742b765SChunfeng Yun * there is more than one function.
4651742b765SChunfeng Yun */
4661742b765SChunfeng Yun function_wake_notif(mtu, 0);
467a29de31bSChunfeng Yun } else {
468df2069acSChunfeng Yun mtu3_setbits(mtu->mac_base, U3D_POWER_MANAGEMENT, RESUME);
469df2069acSChunfeng Yun spin_unlock_irqrestore(&mtu->lock, flags);
470df2069acSChunfeng Yun usleep_range(10000, 11000);
471df2069acSChunfeng Yun spin_lock_irqsave(&mtu->lock, flags);
472df2069acSChunfeng Yun mtu3_clrbits(mtu->mac_base, U3D_POWER_MANAGEMENT, RESUME);
473a29de31bSChunfeng Yun }
474df2069acSChunfeng Yun spin_unlock_irqrestore(&mtu->lock, flags);
475df2069acSChunfeng Yun return 0;
476df2069acSChunfeng Yun }
477df2069acSChunfeng Yun
mtu3_gadget_set_self_powered(struct usb_gadget * gadget,int is_selfpowered)478df2069acSChunfeng Yun static int mtu3_gadget_set_self_powered(struct usb_gadget *gadget,
479df2069acSChunfeng Yun int is_selfpowered)
480df2069acSChunfeng Yun {
481df2069acSChunfeng Yun struct mtu3 *mtu = gadget_to_mtu3(gadget);
482df2069acSChunfeng Yun
483df2069acSChunfeng Yun mtu->is_self_powered = !!is_selfpowered;
484df2069acSChunfeng Yun return 0;
485df2069acSChunfeng Yun }
486df2069acSChunfeng Yun
mtu3_gadget_pullup(struct usb_gadget * gadget,int is_on)487df2069acSChunfeng Yun static int mtu3_gadget_pullup(struct usb_gadget *gadget, int is_on)
488df2069acSChunfeng Yun {
489df2069acSChunfeng Yun struct mtu3 *mtu = gadget_to_mtu3(gadget);
490df2069acSChunfeng Yun unsigned long flags;
491df2069acSChunfeng Yun
492df2069acSChunfeng Yun dev_dbg(mtu->dev, "%s (%s) for %sactive device\n", __func__,
493df2069acSChunfeng Yun is_on ? "on" : "off", mtu->is_active ? "" : "in");
494df2069acSChunfeng Yun
495427c6642SChunfeng Yun pm_runtime_get_sync(mtu->dev);
496427c6642SChunfeng Yun
497df2069acSChunfeng Yun /* we'd rather not pullup unless the device is active. */
498df2069acSChunfeng Yun spin_lock_irqsave(&mtu->lock, flags);
499df2069acSChunfeng Yun
500df2069acSChunfeng Yun is_on = !!is_on;
501df2069acSChunfeng Yun if (!mtu->is_active) {
502df2069acSChunfeng Yun /* save it for mtu3_start() to process the request */
503df2069acSChunfeng Yun mtu->softconnect = is_on;
504df2069acSChunfeng Yun } else if (is_on != mtu->softconnect) {
505df2069acSChunfeng Yun mtu->softconnect = is_on;
506a29de31bSChunfeng Yun mtu3_dev_on_off(mtu, is_on);
507df2069acSChunfeng Yun }
508df2069acSChunfeng Yun
509df2069acSChunfeng Yun spin_unlock_irqrestore(&mtu->lock, flags);
510427c6642SChunfeng Yun pm_runtime_put(mtu->dev);
511df2069acSChunfeng Yun
512df2069acSChunfeng Yun return 0;
513df2069acSChunfeng Yun }
514df2069acSChunfeng Yun
mtu3_gadget_start(struct usb_gadget * gadget,struct usb_gadget_driver * driver)515df2069acSChunfeng Yun static int mtu3_gadget_start(struct usb_gadget *gadget,
516df2069acSChunfeng Yun struct usb_gadget_driver *driver)
517df2069acSChunfeng Yun {
518df2069acSChunfeng Yun struct mtu3 *mtu = gadget_to_mtu3(gadget);
519df2069acSChunfeng Yun unsigned long flags;
520df2069acSChunfeng Yun
521df2069acSChunfeng Yun if (mtu->gadget_driver) {
522df2069acSChunfeng Yun dev_err(mtu->dev, "%s is already bound to %s\n",
523df2069acSChunfeng Yun mtu->g.name, mtu->gadget_driver->driver.name);
524df2069acSChunfeng Yun return -EBUSY;
525df2069acSChunfeng Yun }
526df2069acSChunfeng Yun
527df2069acSChunfeng Yun dev_dbg(mtu->dev, "bind driver %s\n", driver->function);
528427c6642SChunfeng Yun pm_runtime_get_sync(mtu->dev);
529df2069acSChunfeng Yun
530df2069acSChunfeng Yun spin_lock_irqsave(&mtu->lock, flags);
531df2069acSChunfeng Yun
532df2069acSChunfeng Yun mtu->softconnect = 0;
533df2069acSChunfeng Yun mtu->gadget_driver = driver;
534df2069acSChunfeng Yun
535d0ed062aSChunfeng Yun if (mtu->ssusb->dr_mode == USB_DR_MODE_PERIPHERAL)
536df2069acSChunfeng Yun mtu3_start(mtu);
537df2069acSChunfeng Yun
538df2069acSChunfeng Yun spin_unlock_irqrestore(&mtu->lock, flags);
539427c6642SChunfeng Yun pm_runtime_put(mtu->dev);
540df2069acSChunfeng Yun
541df2069acSChunfeng Yun return 0;
542df2069acSChunfeng Yun }
543df2069acSChunfeng Yun
stop_activity(struct mtu3 * mtu)544df2069acSChunfeng Yun static void stop_activity(struct mtu3 *mtu)
545df2069acSChunfeng Yun {
546df2069acSChunfeng Yun struct usb_gadget_driver *driver = mtu->gadget_driver;
547df2069acSChunfeng Yun int i;
548df2069acSChunfeng Yun
549df2069acSChunfeng Yun /* don't disconnect if it's not connected */
550df2069acSChunfeng Yun if (mtu->g.speed == USB_SPEED_UNKNOWN)
551df2069acSChunfeng Yun driver = NULL;
552df2069acSChunfeng Yun else
553df2069acSChunfeng Yun mtu->g.speed = USB_SPEED_UNKNOWN;
554df2069acSChunfeng Yun
555df2069acSChunfeng Yun /* deactivate the hardware */
556df2069acSChunfeng Yun if (mtu->softconnect) {
557df2069acSChunfeng Yun mtu->softconnect = 0;
558a29de31bSChunfeng Yun mtu3_dev_on_off(mtu, 0);
559df2069acSChunfeng Yun }
560df2069acSChunfeng Yun
561df2069acSChunfeng Yun /*
562df2069acSChunfeng Yun * killing any outstanding requests will quiesce the driver;
563df2069acSChunfeng Yun * then report disconnect
564df2069acSChunfeng Yun */
565df2069acSChunfeng Yun nuke(mtu->ep0, -ESHUTDOWN);
566df2069acSChunfeng Yun for (i = 1; i < mtu->num_eps; i++) {
567df2069acSChunfeng Yun nuke(mtu->in_eps + i, -ESHUTDOWN);
568df2069acSChunfeng Yun nuke(mtu->out_eps + i, -ESHUTDOWN);
569df2069acSChunfeng Yun }
570df2069acSChunfeng Yun
571df2069acSChunfeng Yun if (driver) {
572df2069acSChunfeng Yun spin_unlock(&mtu->lock);
573df2069acSChunfeng Yun driver->disconnect(&mtu->g);
574df2069acSChunfeng Yun spin_lock(&mtu->lock);
575df2069acSChunfeng Yun }
576df2069acSChunfeng Yun }
577df2069acSChunfeng Yun
mtu3_gadget_stop(struct usb_gadget * g)578df2069acSChunfeng Yun static int mtu3_gadget_stop(struct usb_gadget *g)
579df2069acSChunfeng Yun {
580df2069acSChunfeng Yun struct mtu3 *mtu = gadget_to_mtu3(g);
581df2069acSChunfeng Yun unsigned long flags;
582df2069acSChunfeng Yun
583df2069acSChunfeng Yun dev_dbg(mtu->dev, "%s\n", __func__);
584df2069acSChunfeng Yun
585df2069acSChunfeng Yun spin_lock_irqsave(&mtu->lock, flags);
586df2069acSChunfeng Yun
587df2069acSChunfeng Yun stop_activity(mtu);
588df2069acSChunfeng Yun mtu->gadget_driver = NULL;
589df2069acSChunfeng Yun
590d0ed062aSChunfeng Yun if (mtu->ssusb->dr_mode == USB_DR_MODE_PERIPHERAL)
591df2069acSChunfeng Yun mtu3_stop(mtu);
592df2069acSChunfeng Yun
593df2069acSChunfeng Yun spin_unlock_irqrestore(&mtu->lock, flags);
594df2069acSChunfeng Yun
59520914919SMacpaul Lin synchronize_irq(mtu->irq);
596df2069acSChunfeng Yun return 0;
597df2069acSChunfeng Yun }
598df2069acSChunfeng Yun
599dc4c1aa7SChunfeng Yun static void
mtu3_gadget_set_speed(struct usb_gadget * g,enum usb_device_speed speed)600dc4c1aa7SChunfeng Yun mtu3_gadget_set_speed(struct usb_gadget *g, enum usb_device_speed speed)
601dc4c1aa7SChunfeng Yun {
602dc4c1aa7SChunfeng Yun struct mtu3 *mtu = gadget_to_mtu3(g);
603dc4c1aa7SChunfeng Yun unsigned long flags;
604dc4c1aa7SChunfeng Yun
605dc4c1aa7SChunfeng Yun dev_dbg(mtu->dev, "%s %s\n", __func__, usb_speed_string(speed));
606dc4c1aa7SChunfeng Yun
607dc4c1aa7SChunfeng Yun spin_lock_irqsave(&mtu->lock, flags);
6082c09bdaaSChunfeng Yun mtu->speed = speed;
609dc4c1aa7SChunfeng Yun spin_unlock_irqrestore(&mtu->lock, flags);
610dc4c1aa7SChunfeng Yun }
611dc4c1aa7SChunfeng Yun
mtu3_gadget_async_callbacks(struct usb_gadget * g,bool enable)61254c4862fSChunfeng Yun static void mtu3_gadget_async_callbacks(struct usb_gadget *g, bool enable)
61354c4862fSChunfeng Yun {
61454c4862fSChunfeng Yun struct mtu3 *mtu = gadget_to_mtu3(g);
61554c4862fSChunfeng Yun unsigned long flags;
61654c4862fSChunfeng Yun
61754c4862fSChunfeng Yun dev_dbg(mtu->dev, "%s %s\n", __func__, enable ? "en" : "dis");
61854c4862fSChunfeng Yun
61954c4862fSChunfeng Yun spin_lock_irqsave(&mtu->lock, flags);
62054c4862fSChunfeng Yun mtu->async_callbacks = enable;
62154c4862fSChunfeng Yun spin_unlock_irqrestore(&mtu->lock, flags);
62254c4862fSChunfeng Yun }
62354c4862fSChunfeng Yun
624df2069acSChunfeng Yun static const struct usb_gadget_ops mtu3_gadget_ops = {
625df2069acSChunfeng Yun .get_frame = mtu3_gadget_get_frame,
626df2069acSChunfeng Yun .wakeup = mtu3_gadget_wakeup,
627df2069acSChunfeng Yun .set_selfpowered = mtu3_gadget_set_self_powered,
628df2069acSChunfeng Yun .pullup = mtu3_gadget_pullup,
629df2069acSChunfeng Yun .udc_start = mtu3_gadget_start,
630df2069acSChunfeng Yun .udc_stop = mtu3_gadget_stop,
631dc4c1aa7SChunfeng Yun .udc_set_speed = mtu3_gadget_set_speed,
63254c4862fSChunfeng Yun .udc_async_callbacks = mtu3_gadget_async_callbacks,
633df2069acSChunfeng Yun };
634df2069acSChunfeng Yun
mtu3_state_reset(struct mtu3 * mtu)6350eae4958SChunfeng Yun static void mtu3_state_reset(struct mtu3 *mtu)
6360eae4958SChunfeng Yun {
6370eae4958SChunfeng Yun mtu->address = 0;
6380eae4958SChunfeng Yun mtu->ep0_state = MU3D_EP0_STATE_SETUP;
6390eae4958SChunfeng Yun mtu->may_wakeup = 0;
6400eae4958SChunfeng Yun mtu->u1_enable = 0;
6410eae4958SChunfeng Yun mtu->u2_enable = 0;
6420eae4958SChunfeng Yun mtu->delayed_status = false;
6430eae4958SChunfeng Yun mtu->test_mode = false;
6440eae4958SChunfeng Yun }
6450eae4958SChunfeng Yun
init_hw_ep(struct mtu3 * mtu,struct mtu3_ep * mep,u32 epnum,u32 is_in)646df2069acSChunfeng Yun static void init_hw_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
647df2069acSChunfeng Yun u32 epnum, u32 is_in)
648df2069acSChunfeng Yun {
649df2069acSChunfeng Yun mep->epnum = epnum;
650df2069acSChunfeng Yun mep->mtu = mtu;
651df2069acSChunfeng Yun mep->is_in = is_in;
652df2069acSChunfeng Yun
653df2069acSChunfeng Yun INIT_LIST_HEAD(&mep->req_list);
654df2069acSChunfeng Yun
655df2069acSChunfeng Yun sprintf(mep->name, "ep%d%s", epnum,
656df2069acSChunfeng Yun !epnum ? "" : (is_in ? "in" : "out"));
657df2069acSChunfeng Yun
658df2069acSChunfeng Yun mep->ep.name = mep->name;
659df2069acSChunfeng Yun INIT_LIST_HEAD(&mep->ep.ep_list);
660df2069acSChunfeng Yun
661a29de31bSChunfeng Yun /* initialize maxpacket as SS */
662df2069acSChunfeng Yun if (!epnum) {
663a29de31bSChunfeng Yun usb_ep_set_maxpacket_limit(&mep->ep, 512);
664df2069acSChunfeng Yun mep->ep.caps.type_control = true;
665df2069acSChunfeng Yun mep->ep.ops = &mtu3_ep0_ops;
666df2069acSChunfeng Yun mtu->g.ep0 = &mep->ep;
667df2069acSChunfeng Yun } else {
668a29de31bSChunfeng Yun usb_ep_set_maxpacket_limit(&mep->ep, 1024);
669df2069acSChunfeng Yun mep->ep.caps.type_iso = true;
670df2069acSChunfeng Yun mep->ep.caps.type_bulk = true;
671df2069acSChunfeng Yun mep->ep.caps.type_int = true;
672df2069acSChunfeng Yun mep->ep.ops = &mtu3_ep_ops;
673df2069acSChunfeng Yun list_add_tail(&mep->ep.ep_list, &mtu->g.ep_list);
674df2069acSChunfeng Yun }
675df2069acSChunfeng Yun
676df2069acSChunfeng Yun dev_dbg(mtu->dev, "%s, name=%s, maxp=%d\n", __func__, mep->ep.name,
677df2069acSChunfeng Yun mep->ep.maxpacket);
678df2069acSChunfeng Yun
679df2069acSChunfeng Yun if (!epnum) {
680df2069acSChunfeng Yun mep->ep.caps.dir_in = true;
681df2069acSChunfeng Yun mep->ep.caps.dir_out = true;
682df2069acSChunfeng Yun } else if (is_in) {
683df2069acSChunfeng Yun mep->ep.caps.dir_in = true;
684df2069acSChunfeng Yun } else {
685df2069acSChunfeng Yun mep->ep.caps.dir_out = true;
686df2069acSChunfeng Yun }
687df2069acSChunfeng Yun }
688df2069acSChunfeng Yun
mtu3_gadget_init_eps(struct mtu3 * mtu)689df2069acSChunfeng Yun static void mtu3_gadget_init_eps(struct mtu3 *mtu)
690df2069acSChunfeng Yun {
691df2069acSChunfeng Yun u8 epnum;
692df2069acSChunfeng Yun
693df2069acSChunfeng Yun /* initialize endpoint list just once */
694df2069acSChunfeng Yun INIT_LIST_HEAD(&(mtu->g.ep_list));
695df2069acSChunfeng Yun
696df2069acSChunfeng Yun dev_dbg(mtu->dev, "%s num_eps(1 for a pair of tx&rx ep)=%d\n",
697df2069acSChunfeng Yun __func__, mtu->num_eps);
698df2069acSChunfeng Yun
699df2069acSChunfeng Yun init_hw_ep(mtu, mtu->ep0, 0, 0);
700df2069acSChunfeng Yun for (epnum = 1; epnum < mtu->num_eps; epnum++) {
701df2069acSChunfeng Yun init_hw_ep(mtu, mtu->in_eps + epnum, epnum, 1);
702df2069acSChunfeng Yun init_hw_ep(mtu, mtu->out_eps + epnum, epnum, 0);
703df2069acSChunfeng Yun }
704df2069acSChunfeng Yun }
705df2069acSChunfeng Yun
mtu3_gadget_setup(struct mtu3 * mtu)706df2069acSChunfeng Yun int mtu3_gadget_setup(struct mtu3 *mtu)
707df2069acSChunfeng Yun {
708df2069acSChunfeng Yun mtu->g.ops = &mtu3_gadget_ops;
709a29de31bSChunfeng Yun mtu->g.max_speed = mtu->max_speed;
710df2069acSChunfeng Yun mtu->g.speed = USB_SPEED_UNKNOWN;
711df2069acSChunfeng Yun mtu->g.sg_supported = 0;
712df2069acSChunfeng Yun mtu->g.name = MTU3_DRIVER_NAME;
7135e1fa6ddSChunfeng Yun mtu->g.irq = mtu->irq;
714df2069acSChunfeng Yun mtu->is_active = 0;
715fe7c994aSChunfeng Yun mtu->delayed_status = false;
716df2069acSChunfeng Yun
717df2069acSChunfeng Yun mtu3_gadget_init_eps(mtu);
718df2069acSChunfeng Yun
719ba428976SChunfeng Yun return usb_add_gadget_udc(mtu->dev, &mtu->g);
720df2069acSChunfeng Yun }
721df2069acSChunfeng Yun
mtu3_gadget_cleanup(struct mtu3 * mtu)722df2069acSChunfeng Yun void mtu3_gadget_cleanup(struct mtu3 *mtu)
723df2069acSChunfeng Yun {
724df2069acSChunfeng Yun usb_del_gadget_udc(&mtu->g);
725df2069acSChunfeng Yun }
726df2069acSChunfeng Yun
mtu3_gadget_resume(struct mtu3 * mtu)727df2069acSChunfeng Yun void mtu3_gadget_resume(struct mtu3 *mtu)
728df2069acSChunfeng Yun {
729df2069acSChunfeng Yun dev_dbg(mtu->dev, "gadget RESUME\n");
73054c4862fSChunfeng Yun if (mtu->async_callbacks && mtu->gadget_driver && mtu->gadget_driver->resume) {
731df2069acSChunfeng Yun spin_unlock(&mtu->lock);
732df2069acSChunfeng Yun mtu->gadget_driver->resume(&mtu->g);
733df2069acSChunfeng Yun spin_lock(&mtu->lock);
734df2069acSChunfeng Yun }
735df2069acSChunfeng Yun }
736df2069acSChunfeng Yun
737df2069acSChunfeng Yun /* called when SOF packets stop for 3+ msec or enters U3 */
mtu3_gadget_suspend(struct mtu3 * mtu)738df2069acSChunfeng Yun void mtu3_gadget_suspend(struct mtu3 *mtu)
739df2069acSChunfeng Yun {
740df2069acSChunfeng Yun dev_dbg(mtu->dev, "gadget SUSPEND\n");
74154c4862fSChunfeng Yun if (mtu->async_callbacks && mtu->gadget_driver && mtu->gadget_driver->suspend) {
742df2069acSChunfeng Yun spin_unlock(&mtu->lock);
743df2069acSChunfeng Yun mtu->gadget_driver->suspend(&mtu->g);
744df2069acSChunfeng Yun spin_lock(&mtu->lock);
745df2069acSChunfeng Yun }
746df2069acSChunfeng Yun }
747df2069acSChunfeng Yun
748df2069acSChunfeng Yun /* called when VBUS drops below session threshold, and in other cases */
mtu3_gadget_disconnect(struct mtu3 * mtu)749df2069acSChunfeng Yun void mtu3_gadget_disconnect(struct mtu3 *mtu)
750df2069acSChunfeng Yun {
751df2069acSChunfeng Yun dev_dbg(mtu->dev, "gadget DISCONNECT\n");
75254c4862fSChunfeng Yun if (mtu->async_callbacks && mtu->gadget_driver && mtu->gadget_driver->disconnect) {
753df2069acSChunfeng Yun spin_unlock(&mtu->lock);
754df2069acSChunfeng Yun mtu->gadget_driver->disconnect(&mtu->g);
755df2069acSChunfeng Yun spin_lock(&mtu->lock);
756df2069acSChunfeng Yun }
757df2069acSChunfeng Yun
7580eae4958SChunfeng Yun mtu3_state_reset(mtu);
759df2069acSChunfeng Yun usb_gadget_set_state(&mtu->g, USB_STATE_NOTATTACHED);
760df2069acSChunfeng Yun }
761df2069acSChunfeng Yun
mtu3_gadget_reset(struct mtu3 * mtu)762df2069acSChunfeng Yun void mtu3_gadget_reset(struct mtu3 *mtu)
763df2069acSChunfeng Yun {
764df2069acSChunfeng Yun dev_dbg(mtu->dev, "gadget RESET\n");
765df2069acSChunfeng Yun
766df2069acSChunfeng Yun /* report disconnect, if we didn't flush EP state */
767df2069acSChunfeng Yun if (mtu->g.speed != USB_SPEED_UNKNOWN)
768df2069acSChunfeng Yun mtu3_gadget_disconnect(mtu);
7690eae4958SChunfeng Yun else
7700eae4958SChunfeng Yun mtu3_state_reset(mtu);
771df2069acSChunfeng Yun }
772