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