1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (c) 2015 MediaTek Inc. |
4 | * Author: |
5 | * Zhigang.Wei <zhigang.wei@mediatek.com> |
6 | * Chunfeng.Yun <chunfeng.yun@mediatek.com> |
7 | */ |
8 | |
9 | #include <linux/kernel.h> |
10 | #include <linux/module.h> |
11 | #include <linux/slab.h> |
12 | |
13 | #include "xhci.h" |
14 | #include "xhci-mtk.h" |
15 | |
16 | #define SSP_BW_BOUNDARY 130000 |
17 | #define SS_BW_BOUNDARY 51000 |
18 | /* table 5-5. High-speed Isoc Transaction Limits in usb_20 spec */ |
19 | #define HS_BW_BOUNDARY 6144 |
20 | /* usb2 spec section11.18.1: at most 188 FS bytes per microframe */ |
21 | #define FS_PAYLOAD_MAX 188 |
22 | #define LS_PAYLOAD_MAX 18 |
23 | /* section 11.18.1, per fs frame */ |
24 | #define FS_BW_BOUNDARY 1157 |
25 | #define LS_BW_BOUNDARY 144 |
26 | |
27 | /* |
28 | * max number of microframes for split transfer, assume extra-cs budget is 0 |
29 | * for fs isoc in : 1 ss + 1 idle + 6 cs (roundup(1023/188)) |
30 | */ |
31 | #define TT_MICROFRAMES_MAX 8 |
32 | /* offset from SS for fs/ls isoc/intr ep (ss + idle) */ |
33 | #define CS_OFFSET 2 |
34 | |
35 | #define DBG_BUF_EN 64 |
36 | |
37 | /* schedule error type */ |
38 | #define ESCH_SS_Y6 1001 |
39 | #define ESCH_SS_OVERLAP 1002 |
40 | #define ESCH_CS_OVERFLOW 1003 |
41 | #define ESCH_BW_OVERFLOW 1004 |
42 | #define ESCH_FIXME 1005 |
43 | |
44 | /* mtk scheduler bitmasks */ |
45 | #define EP_BPKTS(p) ((p) & 0x7f) |
46 | #define EP_BCSCOUNT(p) (((p) & 0x7) << 8) |
47 | #define EP_BBM(p) ((p) << 11) |
48 | #define EP_BOFFSET(p) ((p) & 0x3fff) |
49 | #define EP_BREPEAT(p) (((p) & 0x7fff) << 16) |
50 | |
51 | static char *sch_error_string(int err_num) |
52 | { |
53 | switch (err_num) { |
54 | case ESCH_SS_Y6: |
55 | return "Can't schedule Start-Split in Y6" ; |
56 | case ESCH_SS_OVERLAP: |
57 | return "Can't find a suitable Start-Split location" ; |
58 | case ESCH_CS_OVERFLOW: |
59 | return "The last Complete-Split is greater than 7" ; |
60 | case ESCH_BW_OVERFLOW: |
61 | return "Bandwidth exceeds the maximum limit" ; |
62 | case ESCH_FIXME: |
63 | return "FIXME, to be resolved" ; |
64 | default: |
65 | return "Unknown" ; |
66 | } |
67 | } |
68 | |
69 | static int is_fs_or_ls(enum usb_device_speed speed) |
70 | { |
71 | return speed == USB_SPEED_FULL || speed == USB_SPEED_LOW; |
72 | } |
73 | |
74 | static const char * |
75 | decode_ep(struct usb_host_endpoint *ep, enum usb_device_speed speed) |
76 | { |
77 | static char buf[DBG_BUF_EN]; |
78 | struct usb_endpoint_descriptor *epd = &ep->desc; |
79 | unsigned int interval; |
80 | const char *unit; |
81 | |
82 | interval = usb_decode_interval(epd, speed); |
83 | if (interval % 1000) { |
84 | unit = "us" ; |
85 | } else { |
86 | unit = "ms" ; |
87 | interval /= 1000; |
88 | } |
89 | |
90 | snprintf(buf, DBG_BUF_EN, fmt: "%s ep%d%s %s, mpkt:%d, interval:%d/%d%s" , |
91 | usb_speed_string(speed), usb_endpoint_num(epd), |
92 | usb_endpoint_dir_in(epd) ? "in" : "out" , |
93 | usb_ep_type_string(ep_type: usb_endpoint_type(epd)), |
94 | usb_endpoint_maxp(epd), epd->bInterval, interval, unit); |
95 | |
96 | return buf; |
97 | } |
98 | |
99 | static u32 get_bw_boundary(enum usb_device_speed speed) |
100 | { |
101 | u32 boundary; |
102 | |
103 | switch (speed) { |
104 | case USB_SPEED_SUPER_PLUS: |
105 | boundary = SSP_BW_BOUNDARY; |
106 | break; |
107 | case USB_SPEED_SUPER: |
108 | boundary = SS_BW_BOUNDARY; |
109 | break; |
110 | default: |
111 | boundary = HS_BW_BOUNDARY; |
112 | break; |
113 | } |
114 | |
115 | return boundary; |
116 | } |
117 | |
118 | /* |
119 | * get the bandwidth domain which @ep belongs to. |
120 | * |
121 | * the bandwidth domain array is saved to @sch_array of struct xhci_hcd_mtk, |
122 | * each HS root port is treated as a single bandwidth domain, |
123 | * but each SS root port is treated as two bandwidth domains, one for IN eps, |
124 | * one for OUT eps. |
125 | * @real_port value is defined as follow according to xHCI spec: |
126 | * 1 for SSport0, ..., N+1 for SSportN, N+2 for HSport0, N+3 for HSport1, etc |
127 | * so the bandwidth domain array is organized as follow for simplification: |
128 | * SSport0-OUT, SSport0-IN, ..., SSportX-OUT, SSportX-IN, HSport0, ..., HSportY |
129 | */ |
130 | static struct mu3h_sch_bw_info * |
131 | get_bw_info(struct xhci_hcd_mtk *mtk, struct usb_device *udev, |
132 | struct usb_host_endpoint *ep) |
133 | { |
134 | struct xhci_hcd *xhci = hcd_to_xhci(hcd: mtk->hcd); |
135 | struct xhci_virt_device *virt_dev; |
136 | int bw_index; |
137 | |
138 | virt_dev = xhci->devs[udev->slot_id]; |
139 | if (!virt_dev->real_port) { |
140 | WARN_ONCE(1, "%s invalid real_port\n" , dev_name(&udev->dev)); |
141 | return NULL; |
142 | } |
143 | |
144 | if (udev->speed >= USB_SPEED_SUPER) { |
145 | if (usb_endpoint_dir_out(epd: &ep->desc)) |
146 | bw_index = (virt_dev->real_port - 1) * 2; |
147 | else |
148 | bw_index = (virt_dev->real_port - 1) * 2 + 1; |
149 | } else { |
150 | /* add one more for each SS port */ |
151 | bw_index = virt_dev->real_port + xhci->usb3_rhub.num_ports - 1; |
152 | } |
153 | |
154 | return &mtk->sch_array[bw_index]; |
155 | } |
156 | |
157 | static u32 get_esit(struct xhci_ep_ctx *ep_ctx) |
158 | { |
159 | u32 esit; |
160 | |
161 | esit = 1 << CTX_TO_EP_INTERVAL(le32_to_cpu(ep_ctx->ep_info)); |
162 | if (esit > XHCI_MTK_MAX_ESIT) |
163 | esit = XHCI_MTK_MAX_ESIT; |
164 | |
165 | return esit; |
166 | } |
167 | |
168 | static struct mu3h_sch_tt *find_tt(struct usb_device *udev) |
169 | { |
170 | struct usb_tt *utt = udev->tt; |
171 | struct mu3h_sch_tt *tt, **tt_index, **ptt; |
172 | bool allocated_index = false; |
173 | |
174 | if (!utt) |
175 | return NULL; /* Not below a TT */ |
176 | |
177 | /* |
178 | * Find/create our data structure. |
179 | * For hubs with a single TT, we get it directly. |
180 | * For hubs with multiple TTs, there's an extra level of pointers. |
181 | */ |
182 | tt_index = NULL; |
183 | if (utt->multi) { |
184 | tt_index = utt->hcpriv; |
185 | if (!tt_index) { /* Create the index array */ |
186 | tt_index = kcalloc(n: utt->hub->maxchild, |
187 | size: sizeof(*tt_index), GFP_KERNEL); |
188 | if (!tt_index) |
189 | return ERR_PTR(error: -ENOMEM); |
190 | utt->hcpriv = tt_index; |
191 | allocated_index = true; |
192 | } |
193 | ptt = &tt_index[udev->ttport - 1]; |
194 | } else { |
195 | ptt = (struct mu3h_sch_tt **) &utt->hcpriv; |
196 | } |
197 | |
198 | tt = *ptt; |
199 | if (!tt) { /* Create the mu3h_sch_tt */ |
200 | tt = kzalloc(size: sizeof(*tt), GFP_KERNEL); |
201 | if (!tt) { |
202 | if (allocated_index) { |
203 | utt->hcpriv = NULL; |
204 | kfree(objp: tt_index); |
205 | } |
206 | return ERR_PTR(error: -ENOMEM); |
207 | } |
208 | INIT_LIST_HEAD(list: &tt->ep_list); |
209 | *ptt = tt; |
210 | } |
211 | |
212 | return tt; |
213 | } |
214 | |
215 | /* Release the TT above udev, if it's not in use */ |
216 | static void drop_tt(struct usb_device *udev) |
217 | { |
218 | struct usb_tt *utt = udev->tt; |
219 | struct mu3h_sch_tt *tt, **tt_index, **ptt; |
220 | int i, cnt; |
221 | |
222 | if (!utt || !utt->hcpriv) |
223 | return; /* Not below a TT, or never allocated */ |
224 | |
225 | cnt = 0; |
226 | if (utt->multi) { |
227 | tt_index = utt->hcpriv; |
228 | ptt = &tt_index[udev->ttport - 1]; |
229 | /* How many entries are left in tt_index? */ |
230 | for (i = 0; i < utt->hub->maxchild; ++i) |
231 | cnt += !!tt_index[i]; |
232 | } else { |
233 | tt_index = NULL; |
234 | ptt = (struct mu3h_sch_tt **)&utt->hcpriv; |
235 | } |
236 | |
237 | tt = *ptt; |
238 | if (!tt || !list_empty(head: &tt->ep_list)) |
239 | return; /* never allocated , or still in use*/ |
240 | |
241 | *ptt = NULL; |
242 | kfree(objp: tt); |
243 | |
244 | if (cnt == 1) { |
245 | utt->hcpriv = NULL; |
246 | kfree(objp: tt_index); |
247 | } |
248 | } |
249 | |
250 | static struct mu3h_sch_ep_info * |
251 | create_sch_ep(struct xhci_hcd_mtk *mtk, struct usb_device *udev, |
252 | struct usb_host_endpoint *ep, struct xhci_ep_ctx *ep_ctx) |
253 | { |
254 | struct mu3h_sch_ep_info *sch_ep; |
255 | struct mu3h_sch_bw_info *bw_info; |
256 | struct mu3h_sch_tt *tt = NULL; |
257 | u32 len; |
258 | |
259 | bw_info = get_bw_info(mtk, udev, ep); |
260 | if (!bw_info) |
261 | return ERR_PTR(error: -ENODEV); |
262 | |
263 | if (is_fs_or_ls(speed: udev->speed)) |
264 | len = TT_MICROFRAMES_MAX; |
265 | else if ((udev->speed >= USB_SPEED_SUPER) && |
266 | usb_endpoint_xfer_isoc(epd: &ep->desc)) |
267 | len = get_esit(ep_ctx); |
268 | else |
269 | len = 1; |
270 | |
271 | sch_ep = kzalloc(struct_size(sch_ep, bw_budget_table, len), GFP_KERNEL); |
272 | if (!sch_ep) |
273 | return ERR_PTR(error: -ENOMEM); |
274 | |
275 | if (is_fs_or_ls(speed: udev->speed)) { |
276 | tt = find_tt(udev); |
277 | if (IS_ERR(ptr: tt)) { |
278 | kfree(objp: sch_ep); |
279 | return ERR_PTR(error: -ENOMEM); |
280 | } |
281 | } |
282 | |
283 | sch_ep->bw_info = bw_info; |
284 | sch_ep->sch_tt = tt; |
285 | sch_ep->ep = ep; |
286 | sch_ep->speed = udev->speed; |
287 | INIT_LIST_HEAD(list: &sch_ep->endpoint); |
288 | INIT_LIST_HEAD(list: &sch_ep->tt_endpoint); |
289 | INIT_HLIST_NODE(h: &sch_ep->hentry); |
290 | |
291 | return sch_ep; |
292 | } |
293 | |
294 | static void setup_sch_info(struct xhci_ep_ctx *ep_ctx, |
295 | struct mu3h_sch_ep_info *sch_ep) |
296 | { |
297 | u32 ep_type; |
298 | u32 maxpkt; |
299 | u32 max_burst; |
300 | u32 mult; |
301 | u32 esit_pkts; |
302 | u32 max_esit_payload; |
303 | u32 bw_per_microframe; |
304 | u32 *bwb_table; |
305 | int i; |
306 | |
307 | bwb_table = sch_ep->bw_budget_table; |
308 | ep_type = CTX_TO_EP_TYPE(le32_to_cpu(ep_ctx->ep_info2)); |
309 | maxpkt = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2)); |
310 | max_burst = CTX_TO_MAX_BURST(le32_to_cpu(ep_ctx->ep_info2)); |
311 | mult = CTX_TO_EP_MULT(le32_to_cpu(ep_ctx->ep_info)); |
312 | max_esit_payload = |
313 | (CTX_TO_MAX_ESIT_PAYLOAD_HI( |
314 | le32_to_cpu(ep_ctx->ep_info)) << 16) | |
315 | CTX_TO_MAX_ESIT_PAYLOAD(le32_to_cpu(ep_ctx->tx_info)); |
316 | |
317 | sch_ep->esit = get_esit(ep_ctx); |
318 | sch_ep->num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit; |
319 | sch_ep->ep_type = ep_type; |
320 | sch_ep->maxpkt = maxpkt; |
321 | sch_ep->offset = 0; |
322 | sch_ep->burst_mode = 0; |
323 | sch_ep->repeat = 0; |
324 | |
325 | if (sch_ep->speed == USB_SPEED_HIGH) { |
326 | sch_ep->cs_count = 0; |
327 | |
328 | /* |
329 | * usb_20 spec section5.9 |
330 | * a single microframe is enough for HS synchromous endpoints |
331 | * in a interval |
332 | */ |
333 | sch_ep->num_budget_microframes = 1; |
334 | |
335 | /* |
336 | * xHCI spec section6.2.3.4 |
337 | * @max_burst is the number of additional transactions |
338 | * opportunities per microframe |
339 | */ |
340 | sch_ep->pkts = max_burst + 1; |
341 | bwb_table[0] = maxpkt * sch_ep->pkts; |
342 | } else if (sch_ep->speed >= USB_SPEED_SUPER) { |
343 | /* usb3_r1 spec section4.4.7 & 4.4.8 */ |
344 | sch_ep->cs_count = 0; |
345 | sch_ep->burst_mode = 1; |
346 | /* |
347 | * some device's (d)wBytesPerInterval is set as 0, |
348 | * then max_esit_payload is 0, so evaluate esit_pkts from |
349 | * mult and burst |
350 | */ |
351 | esit_pkts = DIV_ROUND_UP(max_esit_payload, maxpkt); |
352 | if (esit_pkts == 0) |
353 | esit_pkts = (mult + 1) * (max_burst + 1); |
354 | |
355 | if (ep_type == INT_IN_EP || ep_type == INT_OUT_EP) { |
356 | sch_ep->pkts = esit_pkts; |
357 | sch_ep->num_budget_microframes = 1; |
358 | bwb_table[0] = maxpkt * sch_ep->pkts; |
359 | } |
360 | |
361 | if (ep_type == ISOC_IN_EP || ep_type == ISOC_OUT_EP) { |
362 | |
363 | if (sch_ep->esit == 1) |
364 | sch_ep->pkts = esit_pkts; |
365 | else if (esit_pkts <= sch_ep->esit) |
366 | sch_ep->pkts = 1; |
367 | else |
368 | sch_ep->pkts = roundup_pow_of_two(esit_pkts) |
369 | / sch_ep->esit; |
370 | |
371 | sch_ep->num_budget_microframes = |
372 | DIV_ROUND_UP(esit_pkts, sch_ep->pkts); |
373 | |
374 | sch_ep->repeat = !!(sch_ep->num_budget_microframes > 1); |
375 | bw_per_microframe = maxpkt * sch_ep->pkts; |
376 | |
377 | for (i = 0; i < sch_ep->num_budget_microframes - 1; i++) |
378 | bwb_table[i] = bw_per_microframe; |
379 | |
380 | /* last one <= bw_per_microframe */ |
381 | bwb_table[i] = maxpkt * esit_pkts - i * bw_per_microframe; |
382 | } |
383 | } else if (is_fs_or_ls(speed: sch_ep->speed)) { |
384 | sch_ep->pkts = 1; /* at most one packet for each microframe */ |
385 | |
386 | /* |
387 | * @cs_count will be updated to add extra-cs when |
388 | * check TT for INT_OUT_EP, ISOC/INT_IN_EP type |
389 | * @maxpkt <= 1023; |
390 | */ |
391 | sch_ep->cs_count = DIV_ROUND_UP(maxpkt, FS_PAYLOAD_MAX); |
392 | sch_ep->num_budget_microframes = sch_ep->cs_count; |
393 | |
394 | /* init budget table */ |
395 | if (ep_type == ISOC_OUT_EP) { |
396 | for (i = 0; i < sch_ep->cs_count - 1; i++) |
397 | bwb_table[i] = FS_PAYLOAD_MAX; |
398 | |
399 | bwb_table[i] = maxpkt - i * FS_PAYLOAD_MAX; |
400 | } else if (ep_type == INT_OUT_EP) { |
401 | /* only first one used (maxpkt <= 64), others zero */ |
402 | bwb_table[0] = maxpkt; |
403 | } else { /* INT_IN_EP or ISOC_IN_EP */ |
404 | bwb_table[0] = 0; /* start split */ |
405 | bwb_table[1] = 0; /* idle */ |
406 | /* |
407 | * @cs_count will be updated according to cs position |
408 | * (add 1 or 2 extra-cs), but assume only first |
409 | * @num_budget_microframes elements will be used later, |
410 | * although in fact it does not (extra-cs budget many receive |
411 | * some data for IN ep); |
412 | * @cs_count is 1 for INT_IN_EP (maxpkt <= 64); |
413 | */ |
414 | for (i = 0; i < sch_ep->cs_count - 1; i++) |
415 | bwb_table[i + CS_OFFSET] = FS_PAYLOAD_MAX; |
416 | |
417 | bwb_table[i + CS_OFFSET] = maxpkt - i * FS_PAYLOAD_MAX; |
418 | /* ss + idle */ |
419 | sch_ep->num_budget_microframes += CS_OFFSET; |
420 | } |
421 | } |
422 | } |
423 | |
424 | /* Get maximum bandwidth when we schedule at offset slot. */ |
425 | static u32 get_max_bw(struct mu3h_sch_bw_info *sch_bw, |
426 | struct mu3h_sch_ep_info *sch_ep, u32 offset) |
427 | { |
428 | u32 max_bw = 0; |
429 | u32 bw; |
430 | int i, j, k; |
431 | |
432 | for (i = 0; i < sch_ep->num_esit; i++) { |
433 | u32 base = offset + i * sch_ep->esit; |
434 | |
435 | for (j = 0; j < sch_ep->num_budget_microframes; j++) { |
436 | k = XHCI_MTK_BW_INDEX(base + j); |
437 | bw = sch_bw->bus_bw[k] + sch_ep->bw_budget_table[j]; |
438 | if (bw > max_bw) |
439 | max_bw = bw; |
440 | } |
441 | } |
442 | return max_bw; |
443 | } |
444 | |
445 | /* |
446 | * for OUT: get first SS consumed bw; |
447 | * for IN: get first CS consumed bw; |
448 | */ |
449 | static u16 get_fs_bw(struct mu3h_sch_ep_info *sch_ep, int offset) |
450 | { |
451 | struct mu3h_sch_tt *tt = sch_ep->sch_tt; |
452 | u16 fs_bw; |
453 | |
454 | if (sch_ep->ep_type == ISOC_OUT_EP || sch_ep->ep_type == INT_OUT_EP) |
455 | fs_bw = tt->fs_bus_bw_out[XHCI_MTK_BW_INDEX(offset)]; |
456 | else /* skip ss + idle */ |
457 | fs_bw = tt->fs_bus_bw_in[XHCI_MTK_BW_INDEX(offset + CS_OFFSET)]; |
458 | |
459 | return fs_bw; |
460 | } |
461 | |
462 | static void update_bus_bw(struct mu3h_sch_bw_info *sch_bw, |
463 | struct mu3h_sch_ep_info *sch_ep, bool used) |
464 | { |
465 | u32 base; |
466 | int i, j, k; |
467 | |
468 | for (i = 0; i < sch_ep->num_esit; i++) { |
469 | base = sch_ep->offset + i * sch_ep->esit; |
470 | for (j = 0; j < sch_ep->num_budget_microframes; j++) { |
471 | k = XHCI_MTK_BW_INDEX(base + j); |
472 | if (used) |
473 | sch_bw->bus_bw[k] += sch_ep->bw_budget_table[j]; |
474 | else |
475 | sch_bw->bus_bw[k] -= sch_ep->bw_budget_table[j]; |
476 | } |
477 | } |
478 | } |
479 | |
480 | static int check_ls_budget_microframes(struct mu3h_sch_ep_info *sch_ep, int offset) |
481 | { |
482 | struct mu3h_sch_tt *tt = sch_ep->sch_tt; |
483 | int i; |
484 | |
485 | if (sch_ep->speed != USB_SPEED_LOW) |
486 | return 0; |
487 | |
488 | if (sch_ep->ep_type == INT_OUT_EP) |
489 | i = XHCI_MTK_BW_INDEX(offset); |
490 | else if (sch_ep->ep_type == INT_IN_EP) |
491 | i = XHCI_MTK_BW_INDEX(offset + CS_OFFSET); /* skip ss + idle */ |
492 | else |
493 | return -EINVAL; |
494 | |
495 | if (tt->ls_bus_bw[i] + sch_ep->maxpkt > LS_PAYLOAD_MAX) |
496 | return -ESCH_BW_OVERFLOW; |
497 | |
498 | return 0; |
499 | } |
500 | |
501 | static int check_fs_budget_microframes(struct mu3h_sch_ep_info *sch_ep, int offset) |
502 | { |
503 | struct mu3h_sch_tt *tt = sch_ep->sch_tt; |
504 | u32 tmp; |
505 | int i, k; |
506 | |
507 | /* |
508 | * for OUT eps, will transfer exactly assigned length of data, |
509 | * so can't allocate more than 188 bytes; |
510 | * but it's not for IN eps, usually it can't receive full |
511 | * 188 bytes in a uframe, if it not assign full 188 bytes, |
512 | * can add another one; |
513 | */ |
514 | for (i = 0; i < sch_ep->num_budget_microframes; i++) { |
515 | k = XHCI_MTK_BW_INDEX(offset + i); |
516 | if (sch_ep->ep_type == ISOC_OUT_EP || sch_ep->ep_type == INT_OUT_EP) |
517 | tmp = tt->fs_bus_bw_out[k] + sch_ep->bw_budget_table[i]; |
518 | else /* ep_type : ISOC IN / INTR IN */ |
519 | tmp = tt->fs_bus_bw_in[k]; |
520 | |
521 | if (tmp > FS_PAYLOAD_MAX) |
522 | return -ESCH_BW_OVERFLOW; |
523 | } |
524 | |
525 | return 0; |
526 | } |
527 | |
528 | static int check_fs_budget_frames(struct mu3h_sch_ep_info *sch_ep, int offset) |
529 | { |
530 | struct mu3h_sch_tt *tt = sch_ep->sch_tt; |
531 | u32 head, tail; |
532 | int i, j, k; |
533 | |
534 | /* bugdet scheduled may cross at most two fs frames */ |
535 | j = XHCI_MTK_BW_INDEX(offset) / UFRAMES_PER_FRAME; |
536 | k = XHCI_MTK_BW_INDEX(offset + sch_ep->num_budget_microframes - 1) / UFRAMES_PER_FRAME; |
537 | |
538 | if (j != k) { |
539 | head = tt->fs_frame_bw[j]; |
540 | tail = tt->fs_frame_bw[k]; |
541 | } else { |
542 | head = tt->fs_frame_bw[j]; |
543 | tail = 0; |
544 | } |
545 | |
546 | j = roundup(offset, UFRAMES_PER_FRAME); |
547 | for (i = 0; i < sch_ep->num_budget_microframes; i++) { |
548 | if ((offset + i) < j) |
549 | head += sch_ep->bw_budget_table[i]; |
550 | else |
551 | tail += sch_ep->bw_budget_table[i]; |
552 | } |
553 | |
554 | if (head > FS_BW_BOUNDARY || tail > FS_BW_BOUNDARY) |
555 | return -ESCH_BW_OVERFLOW; |
556 | |
557 | return 0; |
558 | } |
559 | |
560 | static int check_fs_bus_bw(struct mu3h_sch_ep_info *sch_ep, int offset) |
561 | { |
562 | int i, base; |
563 | int ret = 0; |
564 | |
565 | for (i = 0; i < sch_ep->num_esit; i++) { |
566 | base = offset + i * sch_ep->esit; |
567 | |
568 | ret = check_ls_budget_microframes(sch_ep, offset: base); |
569 | if (ret) |
570 | goto err; |
571 | |
572 | ret = check_fs_budget_microframes(sch_ep, offset: base); |
573 | if (ret) |
574 | goto err; |
575 | |
576 | ret = check_fs_budget_frames(sch_ep, offset: base); |
577 | if (ret) |
578 | goto err; |
579 | } |
580 | |
581 | err: |
582 | return ret; |
583 | } |
584 | |
585 | static int check_ss_and_cs(struct mu3h_sch_ep_info *sch_ep, u32 offset) |
586 | { |
587 | u32 start_ss, last_ss; |
588 | u32 start_cs, last_cs; |
589 | |
590 | start_ss = offset % UFRAMES_PER_FRAME; |
591 | |
592 | if (sch_ep->ep_type == ISOC_OUT_EP) { |
593 | last_ss = start_ss + sch_ep->cs_count - 1; |
594 | |
595 | /* |
596 | * usb_20 spec section11.18: |
597 | * must never schedule Start-Split in Y6 |
598 | */ |
599 | if (!(start_ss == 7 || last_ss < 6)) |
600 | return -ESCH_SS_Y6; |
601 | |
602 | } else { |
603 | /* maxpkt <= 1023, cs <= 6 */ |
604 | u32 cs_count = DIV_ROUND_UP(sch_ep->maxpkt, FS_PAYLOAD_MAX); |
605 | |
606 | /* |
607 | * usb_20 spec section11.18: |
608 | * must never schedule Start-Split in Y6 |
609 | */ |
610 | if (start_ss == 6) |
611 | return -ESCH_SS_Y6; |
612 | |
613 | /* one uframe for ss + one uframe for idle */ |
614 | start_cs = (start_ss + CS_OFFSET) % UFRAMES_PER_FRAME; |
615 | last_cs = start_cs + cs_count - 1; |
616 | if (last_cs > 7) |
617 | return -ESCH_CS_OVERFLOW; |
618 | |
619 | /* add extra-cs */ |
620 | cs_count += (last_cs == 7) ? 1 : 2; |
621 | if (cs_count > 7) |
622 | cs_count = 7; /* HW limit */ |
623 | |
624 | sch_ep->cs_count = cs_count; |
625 | |
626 | } |
627 | |
628 | return 0; |
629 | } |
630 | |
631 | /* |
632 | * when isoc-out transfers 188 bytes in a uframe, and send isoc/intr's |
633 | * ss token in the uframe, may cause 'bit stuff error' in downstream |
634 | * port; |
635 | * when isoc-out transfer less than 188 bytes in a uframe, shall send |
636 | * isoc-in's ss after isoc-out's ss (but hw can't ensure the sequence, |
637 | * so just avoid overlap). |
638 | */ |
639 | static int check_isoc_ss_overlap(struct mu3h_sch_ep_info *sch_ep, u32 offset) |
640 | { |
641 | struct mu3h_sch_tt *tt = sch_ep->sch_tt; |
642 | int base; |
643 | int i, j, k; |
644 | |
645 | if (!tt) |
646 | return 0; |
647 | |
648 | for (i = 0; i < sch_ep->num_esit; i++) { |
649 | base = offset + i * sch_ep->esit; |
650 | |
651 | if (sch_ep->ep_type == ISOC_OUT_EP) { |
652 | for (j = 0; j < sch_ep->num_budget_microframes; j++) { |
653 | k = XHCI_MTK_BW_INDEX(base + j + CS_OFFSET); |
654 | /* use cs to indicate existence of in-ss @(base+j) */ |
655 | if (tt->fs_bus_bw_in[k]) |
656 | return -ESCH_SS_OVERLAP; |
657 | } |
658 | } else if (sch_ep->ep_type == ISOC_IN_EP || sch_ep->ep_type == INT_IN_EP) { |
659 | k = XHCI_MTK_BW_INDEX(base); |
660 | /* only check IN's ss */ |
661 | if (tt->fs_bus_bw_out[k]) |
662 | return -ESCH_SS_OVERLAP; |
663 | } |
664 | } |
665 | |
666 | return 0; |
667 | } |
668 | |
669 | static int check_sch_tt_budget(struct mu3h_sch_ep_info *sch_ep, u32 offset) |
670 | { |
671 | int ret; |
672 | |
673 | ret = check_ss_and_cs(sch_ep, offset); |
674 | if (ret) |
675 | return ret; |
676 | |
677 | ret = check_isoc_ss_overlap(sch_ep, offset); |
678 | if (ret) |
679 | return ret; |
680 | |
681 | return check_fs_bus_bw(sch_ep, offset); |
682 | } |
683 | |
684 | /* allocate microframes in the ls/fs frame */ |
685 | static int alloc_sch_portion_of_frame(struct mu3h_sch_ep_info *sch_ep) |
686 | { |
687 | struct mu3h_sch_bw_info *sch_bw = sch_ep->bw_info; |
688 | const u32 bw_boundary = get_bw_boundary(speed: sch_ep->speed); |
689 | u32 bw_max, fs_bw_min; |
690 | u32 offset, offset_min; |
691 | u16 fs_bw; |
692 | int frames; |
693 | int i, j; |
694 | int ret; |
695 | |
696 | frames = sch_ep->esit / UFRAMES_PER_FRAME; |
697 | |
698 | for (i = 0; i < UFRAMES_PER_FRAME; i++) { |
699 | fs_bw_min = FS_PAYLOAD_MAX; |
700 | offset_min = XHCI_MTK_MAX_ESIT; |
701 | |
702 | for (j = 0; j < frames; j++) { |
703 | offset = (i + j * UFRAMES_PER_FRAME) % sch_ep->esit; |
704 | |
705 | ret = check_sch_tt_budget(sch_ep, offset); |
706 | if (ret) |
707 | continue; |
708 | |
709 | /* check hs bw domain */ |
710 | bw_max = get_max_bw(sch_bw, sch_ep, offset); |
711 | if (bw_max > bw_boundary) { |
712 | ret = -ESCH_BW_OVERFLOW; |
713 | continue; |
714 | } |
715 | |
716 | /* use best-fit between frames */ |
717 | fs_bw = get_fs_bw(sch_ep, offset); |
718 | if (fs_bw < fs_bw_min) { |
719 | fs_bw_min = fs_bw; |
720 | offset_min = offset; |
721 | } |
722 | |
723 | if (!fs_bw_min) |
724 | break; |
725 | } |
726 | |
727 | /* use first-fit between microframes in a frame */ |
728 | if (offset_min < XHCI_MTK_MAX_ESIT) |
729 | break; |
730 | } |
731 | |
732 | if (offset_min == XHCI_MTK_MAX_ESIT) |
733 | return -ESCH_BW_OVERFLOW; |
734 | |
735 | sch_ep->offset = offset_min; |
736 | |
737 | return 0; |
738 | } |
739 | |
740 | static void update_sch_tt(struct mu3h_sch_ep_info *sch_ep, bool used) |
741 | { |
742 | struct mu3h_sch_tt *tt = sch_ep->sch_tt; |
743 | u16 *fs_bus_bw; |
744 | u32 base; |
745 | int i, j, k, f; |
746 | |
747 | if (sch_ep->ep_type == ISOC_OUT_EP || sch_ep->ep_type == INT_OUT_EP) |
748 | fs_bus_bw = tt->fs_bus_bw_out; |
749 | else |
750 | fs_bus_bw = tt->fs_bus_bw_in; |
751 | |
752 | for (i = 0; i < sch_ep->num_esit; i++) { |
753 | base = sch_ep->offset + i * sch_ep->esit; |
754 | |
755 | for (j = 0; j < sch_ep->num_budget_microframes; j++) { |
756 | k = XHCI_MTK_BW_INDEX(base + j); |
757 | f = k / UFRAMES_PER_FRAME; |
758 | if (used) { |
759 | if (sch_ep->speed == USB_SPEED_LOW) |
760 | tt->ls_bus_bw[k] += (u8)sch_ep->bw_budget_table[j]; |
761 | |
762 | fs_bus_bw[k] += (u16)sch_ep->bw_budget_table[j]; |
763 | tt->fs_frame_bw[f] += (u16)sch_ep->bw_budget_table[j]; |
764 | } else { |
765 | if (sch_ep->speed == USB_SPEED_LOW) |
766 | tt->ls_bus_bw[k] -= (u8)sch_ep->bw_budget_table[j]; |
767 | |
768 | fs_bus_bw[k] -= (u16)sch_ep->bw_budget_table[j]; |
769 | tt->fs_frame_bw[f] -= (u16)sch_ep->bw_budget_table[j]; |
770 | } |
771 | } |
772 | } |
773 | |
774 | if (used) |
775 | list_add_tail(new: &sch_ep->tt_endpoint, head: &tt->ep_list); |
776 | else |
777 | list_del(entry: &sch_ep->tt_endpoint); |
778 | } |
779 | |
780 | static int load_ep_bw(struct mu3h_sch_bw_info *sch_bw, |
781 | struct mu3h_sch_ep_info *sch_ep, bool loaded) |
782 | { |
783 | if (sch_ep->sch_tt) |
784 | update_sch_tt(sch_ep, used: loaded); |
785 | |
786 | /* update bus bandwidth info */ |
787 | update_bus_bw(sch_bw, sch_ep, used: loaded); |
788 | sch_ep->allocated = loaded; |
789 | |
790 | return 0; |
791 | } |
792 | |
793 | /* allocate microframes for hs/ss/ssp */ |
794 | static int alloc_sch_microframes(struct mu3h_sch_ep_info *sch_ep) |
795 | { |
796 | struct mu3h_sch_bw_info *sch_bw = sch_ep->bw_info; |
797 | const u32 bw_boundary = get_bw_boundary(speed: sch_ep->speed); |
798 | u32 offset; |
799 | u32 worst_bw; |
800 | u32 min_bw = ~0; |
801 | int min_index = -1; |
802 | |
803 | /* |
804 | * Search through all possible schedule microframes. |
805 | * and find a microframe where its worst bandwidth is minimum. |
806 | */ |
807 | for (offset = 0; offset < sch_ep->esit; offset++) { |
808 | |
809 | worst_bw = get_max_bw(sch_bw, sch_ep, offset); |
810 | if (worst_bw > bw_boundary) |
811 | continue; |
812 | |
813 | if (min_bw > worst_bw) { |
814 | min_bw = worst_bw; |
815 | min_index = offset; |
816 | } |
817 | } |
818 | |
819 | if (min_index < 0) |
820 | return -ESCH_BW_OVERFLOW; |
821 | |
822 | sch_ep->offset = min_index; |
823 | |
824 | return 0; |
825 | } |
826 | |
827 | static int check_sch_bw(struct mu3h_sch_ep_info *sch_ep) |
828 | { |
829 | int ret; |
830 | |
831 | if (sch_ep->sch_tt) |
832 | ret = alloc_sch_portion_of_frame(sch_ep); |
833 | else |
834 | ret = alloc_sch_microframes(sch_ep); |
835 | |
836 | if (ret) |
837 | return ret; |
838 | |
839 | return load_ep_bw(sch_bw: sch_ep->bw_info, sch_ep, loaded: true); |
840 | } |
841 | |
842 | static void destroy_sch_ep(struct xhci_hcd_mtk *mtk, struct usb_device *udev, |
843 | struct mu3h_sch_ep_info *sch_ep) |
844 | { |
845 | /* only release ep bw check passed by check_sch_bw() */ |
846 | if (sch_ep->allocated) |
847 | load_ep_bw(sch_bw: sch_ep->bw_info, sch_ep, loaded: false); |
848 | |
849 | if (sch_ep->sch_tt) |
850 | drop_tt(udev); |
851 | |
852 | list_del(entry: &sch_ep->endpoint); |
853 | hlist_del(n: &sch_ep->hentry); |
854 | kfree(objp: sch_ep); |
855 | } |
856 | |
857 | static bool need_bw_sch(struct usb_device *udev, |
858 | struct usb_host_endpoint *ep) |
859 | { |
860 | bool has_tt = udev->tt && udev->tt->hub->parent; |
861 | |
862 | /* only for periodic endpoints */ |
863 | if (usb_endpoint_xfer_control(epd: &ep->desc) |
864 | || usb_endpoint_xfer_bulk(epd: &ep->desc)) |
865 | return false; |
866 | |
867 | /* |
868 | * for LS & FS periodic endpoints which its device is not behind |
869 | * a TT are also ignored, root-hub will schedule them directly, |
870 | * but need set @bpkts field of endpoint context to 1. |
871 | */ |
872 | if (is_fs_or_ls(speed: udev->speed) && !has_tt) |
873 | return false; |
874 | |
875 | /* skip endpoint with zero maxpkt */ |
876 | if (usb_endpoint_maxp(epd: &ep->desc) == 0) |
877 | return false; |
878 | |
879 | return true; |
880 | } |
881 | |
882 | int xhci_mtk_sch_init(struct xhci_hcd_mtk *mtk) |
883 | { |
884 | struct xhci_hcd *xhci = hcd_to_xhci(hcd: mtk->hcd); |
885 | struct mu3h_sch_bw_info *sch_array; |
886 | int num_usb_bus; |
887 | |
888 | /* ss IN and OUT are separated */ |
889 | num_usb_bus = xhci->usb3_rhub.num_ports * 2 + xhci->usb2_rhub.num_ports; |
890 | |
891 | sch_array = kcalloc(n: num_usb_bus, size: sizeof(*sch_array), GFP_KERNEL); |
892 | if (sch_array == NULL) |
893 | return -ENOMEM; |
894 | |
895 | mtk->sch_array = sch_array; |
896 | |
897 | INIT_LIST_HEAD(list: &mtk->bw_ep_chk_list); |
898 | hash_init(mtk->sch_ep_hash); |
899 | |
900 | return 0; |
901 | } |
902 | |
903 | void xhci_mtk_sch_exit(struct xhci_hcd_mtk *mtk) |
904 | { |
905 | kfree(objp: mtk->sch_array); |
906 | } |
907 | |
908 | static int add_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev, |
909 | struct usb_host_endpoint *ep) |
910 | { |
911 | struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd); |
912 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); |
913 | struct xhci_ep_ctx *ep_ctx; |
914 | struct xhci_virt_device *virt_dev; |
915 | struct mu3h_sch_ep_info *sch_ep; |
916 | unsigned int ep_index; |
917 | |
918 | virt_dev = xhci->devs[udev->slot_id]; |
919 | ep_index = xhci_get_endpoint_index(desc: &ep->desc); |
920 | ep_ctx = xhci_get_ep_ctx(xhci, ctx: virt_dev->in_ctx, ep_index); |
921 | |
922 | if (!need_bw_sch(udev, ep)) { |
923 | /* |
924 | * set @bpkts to 1 if it is LS or FS periodic endpoint, and its |
925 | * device does not connected through an external HS hub |
926 | */ |
927 | if (usb_endpoint_xfer_int(epd: &ep->desc) |
928 | || usb_endpoint_xfer_isoc(epd: &ep->desc)) |
929 | ep_ctx->reserved[0] = cpu_to_le32(EP_BPKTS(1)); |
930 | |
931 | return 0; |
932 | } |
933 | |
934 | xhci_dbg(xhci, "%s %s\n" , __func__, decode_ep(ep, udev->speed)); |
935 | |
936 | sch_ep = create_sch_ep(mtk, udev, ep, ep_ctx); |
937 | if (IS_ERR_OR_NULL(ptr: sch_ep)) |
938 | return -ENOMEM; |
939 | |
940 | setup_sch_info(ep_ctx, sch_ep); |
941 | |
942 | list_add_tail(new: &sch_ep->endpoint, head: &mtk->bw_ep_chk_list); |
943 | hash_add(mtk->sch_ep_hash, &sch_ep->hentry, (unsigned long)ep); |
944 | |
945 | return 0; |
946 | } |
947 | |
948 | static void drop_ep_quirk(struct usb_hcd *hcd, struct usb_device *udev, |
949 | struct usb_host_endpoint *ep) |
950 | { |
951 | struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd); |
952 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); |
953 | struct mu3h_sch_ep_info *sch_ep; |
954 | struct hlist_node *hn; |
955 | |
956 | if (!need_bw_sch(udev, ep)) |
957 | return; |
958 | |
959 | xhci_dbg(xhci, "%s %s\n" , __func__, decode_ep(ep, udev->speed)); |
960 | |
961 | hash_for_each_possible_safe(mtk->sch_ep_hash, sch_ep, |
962 | hn, hentry, (unsigned long)ep) { |
963 | if (sch_ep->ep == ep) { |
964 | destroy_sch_ep(mtk, udev, sch_ep); |
965 | break; |
966 | } |
967 | } |
968 | } |
969 | |
970 | int xhci_mtk_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) |
971 | { |
972 | struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd); |
973 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); |
974 | struct xhci_virt_device *virt_dev = xhci->devs[udev->slot_id]; |
975 | struct mu3h_sch_ep_info *sch_ep; |
976 | int ret; |
977 | |
978 | xhci_dbg(xhci, "%s() udev %s\n" , __func__, dev_name(&udev->dev)); |
979 | |
980 | list_for_each_entry(sch_ep, &mtk->bw_ep_chk_list, endpoint) { |
981 | struct xhci_ep_ctx *ep_ctx; |
982 | struct usb_host_endpoint *ep = sch_ep->ep; |
983 | unsigned int ep_index = xhci_get_endpoint_index(desc: &ep->desc); |
984 | |
985 | ret = check_sch_bw(sch_ep); |
986 | if (ret) { |
987 | xhci_err(xhci, "Not enough bandwidth! (%s)\n" , |
988 | sch_error_string(-ret)); |
989 | return -ENOSPC; |
990 | } |
991 | |
992 | ep_ctx = xhci_get_ep_ctx(xhci, ctx: virt_dev->in_ctx, ep_index); |
993 | ep_ctx->reserved[0] = cpu_to_le32(EP_BPKTS(sch_ep->pkts) |
994 | | EP_BCSCOUNT(sch_ep->cs_count) |
995 | | EP_BBM(sch_ep->burst_mode)); |
996 | ep_ctx->reserved[1] = cpu_to_le32(EP_BOFFSET(sch_ep->offset) |
997 | | EP_BREPEAT(sch_ep->repeat)); |
998 | |
999 | xhci_dbg(xhci, " PKTS:%x, CSCOUNT:%x, BM:%x, OFFSET:%x, REPEAT:%x\n" , |
1000 | sch_ep->pkts, sch_ep->cs_count, sch_ep->burst_mode, |
1001 | sch_ep->offset, sch_ep->repeat); |
1002 | } |
1003 | |
1004 | ret = xhci_check_bandwidth(hcd, udev); |
1005 | if (!ret) |
1006 | list_del_init(entry: &mtk->bw_ep_chk_list); |
1007 | |
1008 | return ret; |
1009 | } |
1010 | |
1011 | void xhci_mtk_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) |
1012 | { |
1013 | struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd); |
1014 | struct xhci_hcd *xhci = hcd_to_xhci(hcd); |
1015 | struct mu3h_sch_ep_info *sch_ep, *tmp; |
1016 | |
1017 | xhci_dbg(xhci, "%s() udev %s\n" , __func__, dev_name(&udev->dev)); |
1018 | |
1019 | list_for_each_entry_safe(sch_ep, tmp, &mtk->bw_ep_chk_list, endpoint) |
1020 | destroy_sch_ep(mtk, udev, sch_ep); |
1021 | |
1022 | xhci_reset_bandwidth(hcd, udev); |
1023 | } |
1024 | |
1025 | int xhci_mtk_add_ep(struct usb_hcd *hcd, struct usb_device *udev, |
1026 | struct usb_host_endpoint *ep) |
1027 | { |
1028 | int ret; |
1029 | |
1030 | ret = xhci_add_endpoint(hcd, udev, ep); |
1031 | if (ret) |
1032 | return ret; |
1033 | |
1034 | if (ep->hcpriv) |
1035 | ret = add_ep_quirk(hcd, udev, ep); |
1036 | |
1037 | return ret; |
1038 | } |
1039 | |
1040 | int xhci_mtk_drop_ep(struct usb_hcd *hcd, struct usb_device *udev, |
1041 | struct usb_host_endpoint *ep) |
1042 | { |
1043 | int ret; |
1044 | |
1045 | ret = xhci_drop_endpoint(hcd, udev, ep); |
1046 | if (ret) |
1047 | return ret; |
1048 | |
1049 | /* needn't check @ep->hcpriv, xhci_endpoint_disable set it NULL */ |
1050 | drop_ep_quirk(hcd, udev, ep); |
1051 | |
1052 | return 0; |
1053 | } |
1054 | |