Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: xhci-mtk: supports bandwidth scheduling with multi-TT

Supports LowSpeed and FullSpeed INT/ISOC bandwidth scheduling
with USB multi-TT

Signed-off-by: Chunfeng Yun <chunfeng.yun@mediatek.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Chunfeng Yun and committed by
Greg Kroah-Hartman
08e469de 95b516c1

+258 -10
+237 -10
drivers/usb/host/xhci-mtk-sch.c
··· 80 80 return esit; 81 81 } 82 82 83 + static struct mu3h_sch_tt *find_tt(struct usb_device *udev) 84 + { 85 + struct usb_tt *utt = udev->tt; 86 + struct mu3h_sch_tt *tt, **tt_index, **ptt; 87 + unsigned int port; 88 + bool allocated_index = false; 89 + 90 + if (!utt) 91 + return NULL; /* Not below a TT */ 92 + 93 + /* 94 + * Find/create our data structure. 95 + * For hubs with a single TT, we get it directly. 96 + * For hubs with multiple TTs, there's an extra level of pointers. 97 + */ 98 + tt_index = NULL; 99 + if (utt->multi) { 100 + tt_index = utt->hcpriv; 101 + if (!tt_index) { /* Create the index array */ 102 + tt_index = kcalloc(utt->hub->maxchild, 103 + sizeof(*tt_index), GFP_KERNEL); 104 + if (!tt_index) 105 + return ERR_PTR(-ENOMEM); 106 + utt->hcpriv = tt_index; 107 + allocated_index = true; 108 + } 109 + port = udev->ttport - 1; 110 + ptt = &tt_index[port]; 111 + } else { 112 + port = 0; 113 + ptt = (struct mu3h_sch_tt **) &utt->hcpriv; 114 + } 115 + 116 + tt = *ptt; 117 + if (!tt) { /* Create the mu3h_sch_tt */ 118 + tt = kzalloc(sizeof(*tt), GFP_KERNEL); 119 + if (!tt) { 120 + if (allocated_index) { 121 + utt->hcpriv = NULL; 122 + kfree(tt_index); 123 + } 124 + return ERR_PTR(-ENOMEM); 125 + } 126 + INIT_LIST_HEAD(&tt->ep_list); 127 + tt->usb_tt = utt; 128 + tt->tt_port = port; 129 + *ptt = tt; 130 + } 131 + 132 + return tt; 133 + } 134 + 135 + /* Release the TT above udev, if it's not in use */ 136 + static void drop_tt(struct usb_device *udev) 137 + { 138 + struct usb_tt *utt = udev->tt; 139 + struct mu3h_sch_tt *tt, **tt_index, **ptt; 140 + int i, cnt; 141 + 142 + if (!utt || !utt->hcpriv) 143 + return; /* Not below a TT, or never allocated */ 144 + 145 + cnt = 0; 146 + if (utt->multi) { 147 + tt_index = utt->hcpriv; 148 + ptt = &tt_index[udev->ttport - 1]; 149 + /* How many entries are left in tt_index? */ 150 + for (i = 0; i < utt->hub->maxchild; ++i) 151 + cnt += !!tt_index[i]; 152 + } else { 153 + tt_index = NULL; 154 + ptt = (struct mu3h_sch_tt **)&utt->hcpriv; 155 + } 156 + 157 + tt = *ptt; 158 + if (!tt || !list_empty(&tt->ep_list)) 159 + return; /* never allocated , or still in use*/ 160 + 161 + *ptt = NULL; 162 + kfree(tt); 163 + 164 + if (cnt == 1) { 165 + utt->hcpriv = NULL; 166 + kfree(tt_index); 167 + } 168 + } 169 + 83 170 static struct mu3h_sch_ep_info *create_sch_ep(struct usb_device *udev, 84 171 struct usb_host_endpoint *ep, struct xhci_ep_ctx *ep_ctx) 85 172 { 86 173 struct mu3h_sch_ep_info *sch_ep; 174 + struct mu3h_sch_tt *tt = NULL; 87 175 u32 len_bw_budget_table; 88 176 size_t mem_size; 89 177 ··· 189 101 if (!sch_ep) 190 102 return ERR_PTR(-ENOMEM); 191 103 104 + if (is_fs_or_ls(udev->speed)) { 105 + tt = find_tt(udev); 106 + if (IS_ERR(tt)) { 107 + kfree(sch_ep); 108 + return ERR_PTR(-ENOMEM); 109 + } 110 + } 111 + 112 + sch_ep->sch_tt = tt; 192 113 sch_ep->ep = ep; 193 114 194 115 return sch_ep; ··· 225 128 CTX_TO_MAX_ESIT_PAYLOAD(le32_to_cpu(ep_ctx->tx_info)); 226 129 227 130 sch_ep->esit = get_esit(ep_ctx); 131 + sch_ep->ep_type = ep_type; 132 + sch_ep->maxpkt = maxpkt; 228 133 sch_ep->offset = 0; 229 134 sch_ep->burst_mode = 0; 230 135 sch_ep->repeat = 0; ··· 296 197 } 297 198 } else if (is_fs_or_ls(udev->speed)) { 298 199 sch_ep->pkts = 1; /* at most one packet for each microframe */ 200 + 201 + /* 202 + * num_budget_microframes and cs_count will be updated when 203 + * check TT for INT_OUT_EP, ISOC/INT_IN_EP type 204 + */ 299 205 sch_ep->cs_count = DIV_ROUND_UP(maxpkt, FS_PAYLOAD_MAX); 300 - sch_ep->num_budget_microframes = sch_ep->cs_count + 2; 206 + sch_ep->num_budget_microframes = sch_ep->cs_count; 301 207 sch_ep->bw_cost_per_microframe = 302 208 (maxpkt < FS_PAYLOAD_MAX) ? maxpkt : FS_PAYLOAD_MAX; 303 209 ··· 316 212 } else { /* INT_IN_EP or ISOC_IN_EP */ 317 213 bwb_table[0] = 0; /* start split */ 318 214 bwb_table[1] = 0; /* idle */ 319 - for (i = 2; i < sch_ep->num_budget_microframes; i++) 215 + /* 216 + * due to cs_count will be updated according to cs 217 + * position, assign all remainder budget array 218 + * elements as @bw_cost_per_microframe, but only first 219 + * @num_budget_microframes elements will be used later 220 + */ 221 + for (i = 2; i < TT_MICROFRAMES_MAX; i++) 320 222 bwb_table[i] = sch_ep->bw_cost_per_microframe; 321 223 } 322 224 } ··· 374 264 } 375 265 } 376 266 267 + static int check_sch_tt(struct usb_device *udev, 268 + struct mu3h_sch_ep_info *sch_ep, u32 offset) 269 + { 270 + struct mu3h_sch_tt *tt = sch_ep->sch_tt; 271 + u32 extra_cs_count; 272 + u32 fs_budget_start; 273 + u32 start_ss, last_ss; 274 + u32 start_cs, last_cs; 275 + int i; 276 + 277 + start_ss = offset % 8; 278 + fs_budget_start = (start_ss + 1) % 8; 279 + 280 + if (sch_ep->ep_type == ISOC_OUT_EP) { 281 + last_ss = start_ss + sch_ep->cs_count - 1; 282 + 283 + /* 284 + * usb_20 spec section11.18: 285 + * must never schedule Start-Split in Y6 286 + */ 287 + if (!(start_ss == 7 || last_ss < 6)) 288 + return -ERANGE; 289 + 290 + for (i = 0; i < sch_ep->cs_count; i++) 291 + if (test_bit(offset + i, tt->split_bit_map)) 292 + return -ERANGE; 293 + 294 + } else { 295 + u32 cs_count = DIV_ROUND_UP(sch_ep->maxpkt, FS_PAYLOAD_MAX); 296 + 297 + /* 298 + * usb_20 spec section11.18: 299 + * must never schedule Start-Split in Y6 300 + */ 301 + if (start_ss == 6) 302 + return -ERANGE; 303 + 304 + /* one uframe for ss + one uframe for idle */ 305 + start_cs = (start_ss + 2) % 8; 306 + last_cs = start_cs + cs_count - 1; 307 + 308 + if (last_cs > 7) 309 + return -ERANGE; 310 + 311 + if (sch_ep->ep_type == ISOC_IN_EP) 312 + extra_cs_count = (last_cs == 7) ? 1 : 2; 313 + else /* ep_type : INTR IN / INTR OUT */ 314 + extra_cs_count = (fs_budget_start == 6) ? 1 : 2; 315 + 316 + cs_count += extra_cs_count; 317 + if (cs_count > 7) 318 + cs_count = 7; /* HW limit */ 319 + 320 + for (i = 0; i < cs_count + 2; i++) { 321 + if (test_bit(offset + i, tt->split_bit_map)) 322 + return -ERANGE; 323 + } 324 + 325 + sch_ep->cs_count = cs_count; 326 + /* one for ss, the other for idle */ 327 + sch_ep->num_budget_microframes = cs_count + 2; 328 + 329 + /* 330 + * if interval=1, maxp >752, num_budge_micoframe is larger 331 + * than sch_ep->esit, will overstep boundary 332 + */ 333 + if (sch_ep->num_budget_microframes > sch_ep->esit) 334 + sch_ep->num_budget_microframes = sch_ep->esit; 335 + } 336 + 337 + return 0; 338 + } 339 + 340 + static void update_sch_tt(struct usb_device *udev, 341 + struct mu3h_sch_ep_info *sch_ep) 342 + { 343 + struct mu3h_sch_tt *tt = sch_ep->sch_tt; 344 + u32 base, num_esit; 345 + int i, j; 346 + 347 + num_esit = XHCI_MTK_MAX_ESIT / sch_ep->esit; 348 + for (i = 0; i < num_esit; i++) { 349 + base = sch_ep->offset + i * sch_ep->esit; 350 + for (j = 0; j < sch_ep->num_budget_microframes; j++) 351 + set_bit(base + j, tt->split_bit_map); 352 + } 353 + 354 + list_add_tail(&sch_ep->tt_endpoint, &tt->ep_list); 355 + } 356 + 377 357 static int check_sch_bw(struct usb_device *udev, 378 358 struct mu3h_sch_bw_info *sch_bw, struct mu3h_sch_ep_info *sch_ep) 379 359 { ··· 473 273 u32 min_index; 474 274 u32 worst_bw; 475 275 u32 bw_boundary; 276 + u32 min_num_budget; 277 + u32 min_cs_count; 278 + bool tt_offset_ok = false; 279 + int ret; 476 280 477 281 esit = sch_ep->esit; 478 282 ··· 486 282 */ 487 283 min_bw = ~0; 488 284 min_index = 0; 285 + min_cs_count = sch_ep->cs_count; 286 + min_num_budget = sch_ep->num_budget_microframes; 489 287 for (offset = 0; offset < esit; offset++) { 288 + if (is_fs_or_ls(udev->speed)) { 289 + ret = check_sch_tt(udev, sch_ep, offset); 290 + if (ret) 291 + continue; 292 + else 293 + tt_offset_ok = true; 294 + } 295 + 490 296 if ((offset + sch_ep->num_budget_microframes) > sch_ep->esit) 491 297 break; 492 - 493 - /* 494 - * usb_20 spec section11.18: 495 - * must never schedule Start-Split in Y6 496 - */ 497 - if (is_fs_or_ls(udev->speed) && (offset % 8 == 6)) 498 - continue; 499 298 500 299 worst_bw = get_max_bw(sch_bw, sch_ep, offset); 501 300 if (min_bw > worst_bw) { 502 301 min_bw = worst_bw; 503 302 min_index = offset; 303 + min_cs_count = sch_ep->cs_count; 304 + min_num_budget = sch_ep->num_budget_microframes; 504 305 } 505 306 if (min_bw == 0) 506 307 break; 507 308 } 508 - sch_ep->offset = min_index; 509 309 510 310 bw_boundary = (udev->speed == USB_SPEED_SUPER) 511 311 ? SS_BW_BOUNDARY : HS_BW_BOUNDARY; ··· 517 309 /* check bandwidth */ 518 310 if (min_bw > bw_boundary) 519 311 return -ERANGE; 312 + 313 + sch_ep->offset = min_index; 314 + sch_ep->cs_count = min_cs_count; 315 + sch_ep->num_budget_microframes = min_num_budget; 316 + 317 + if (is_fs_or_ls(udev->speed)) { 318 + /* all offset for tt is not ok*/ 319 + if (!tt_offset_ok) 320 + return -ERANGE; 321 + 322 + update_sch_tt(udev, sch_ep); 323 + } 520 324 521 325 /* update bus bandwidth info */ 522 326 update_bus_bw(sch_bw, sch_ep, 1); ··· 635 415 ret = check_sch_bw(udev, sch_bw, sch_ep); 636 416 if (ret) { 637 417 xhci_err(xhci, "Not enough bandwidth!\n"); 418 + if (is_fs_or_ls(udev->speed)) 419 + drop_tt(udev); 420 + 638 421 kfree(sch_ep); 639 422 return -ENOSPC; 640 423 } ··· 689 466 if (sch_ep->ep == ep) { 690 467 update_bus_bw(sch_bw, sch_ep, 0); 691 468 list_del(&sch_ep->endpoint); 469 + if (is_fs_or_ls(udev->speed)) { 470 + list_del(&sch_ep->tt_endpoint); 471 + drop_tt(udev); 472 + } 692 473 kfree(sch_ep); 693 474 break; 694 475 }
+21
drivers/usb/host/xhci-mtk.h
··· 20 20 #define XHCI_MTK_MAX_ESIT 64 21 21 22 22 /** 23 + * @split_bit_map: used to avoid split microframes overlay 24 + * @ep_list: Endpoints using this TT 25 + * @usb_tt: usb TT related 26 + * @tt_port: TT port number 27 + */ 28 + struct mu3h_sch_tt { 29 + DECLARE_BITMAP(split_bit_map, XHCI_MTK_MAX_ESIT); 30 + struct list_head ep_list; 31 + struct usb_tt *usb_tt; 32 + int tt_port; 33 + }; 34 + 35 + /** 23 36 * struct mu3h_sch_bw_info: schedule information for bandwidth domain 24 37 * 25 38 * @bus_bw: array to keep track of bandwidth already used at each uframes ··· 54 41 * (@repeat==1) scheduled within the interval 55 42 * @bw_cost_per_microframe: bandwidth cost per microframe 56 43 * @endpoint: linked into bandwidth domain which it belongs to 44 + * @tt_endpoint: linked into mu3h_sch_tt's list which it belongs to 45 + * @sch_tt: mu3h_sch_tt linked into 46 + * @ep_type: endpoint type 47 + * @maxpkt: max packet size of endpoint 57 48 * @ep: address of usb_host_endpoint struct 58 49 * @offset: which uframe of the interval that transfer should be 59 50 * scheduled first time within the interval ··· 81 64 u32 num_budget_microframes; 82 65 u32 bw_cost_per_microframe; 83 66 struct list_head endpoint; 67 + struct list_head tt_endpoint; 68 + struct mu3h_sch_tt *sch_tt; 69 + u32 ep_type; 70 + u32 maxpkt; 84 71 void *ep; 85 72 /* 86 73 * mtk xHCI scheduling information put into reserved DWs