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

thunderbolt: Read router preferred credit allocation information

USB4 routers must expose their preferred credit (buffer) allocation
information through router operation. This information tells the
connection manager how the router prefers its buffers to be allocated to
get the expected bandwidth for the supported protocols.

Read this information and store it as part of struct tb_switch for each
USB4 router.

Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>

+221 -8
+43 -8
drivers/thunderbolt/switch.c
··· 488 488 } 489 489 } 490 490 491 - static void tb_dump_port(struct tb *tb, struct tb_regs_port_header *port) 491 + static void tb_dump_port(struct tb *tb, const struct tb_port *port) 492 492 { 493 + const struct tb_regs_port_header *regs = &port->config; 494 + 493 495 tb_dbg(tb, 494 496 " Port %d: %x:%x (Revision: %d, TB Version: %d, Type: %s (%#x))\n", 495 - port->port_number, port->vendor_id, port->device_id, 496 - port->revision, port->thunderbolt_version, tb_port_type(port), 497 - port->type); 497 + regs->port_number, regs->vendor_id, regs->device_id, 498 + regs->revision, regs->thunderbolt_version, tb_port_type(regs), 499 + regs->type); 498 500 tb_dbg(tb, " Max hop id (in/out): %d/%d\n", 499 - port->max_in_hop_id, port->max_out_hop_id); 500 - tb_dbg(tb, " Max counters: %d\n", port->max_counters); 501 - tb_dbg(tb, " NFC Credits: %#x\n", port->nfc_credits); 501 + regs->max_in_hop_id, regs->max_out_hop_id); 502 + tb_dbg(tb, " Max counters: %d\n", regs->max_counters); 503 + tb_dbg(tb, " NFC Credits: %#x\n", regs->nfc_credits); 504 + tb_dbg(tb, " Credits (total/control): %u/%u\n", port->total_credits, 505 + port->ctl_credits); 502 506 } 503 507 504 508 /** ··· 742 738 cap = tb_port_find_cap(port, TB_PORT_CAP_USB4); 743 739 if (cap > 0) 744 740 port->cap_usb4 = cap; 741 + 742 + /* 743 + * USB4 ports the buffers allocated for the control path 744 + * can be read from the path config space. Legacy 745 + * devices we use hard-coded value. 746 + */ 747 + if (tb_switch_is_usb4(port->sw)) { 748 + struct tb_regs_hop hop; 749 + 750 + if (!tb_port_read(port, &hop, TB_CFG_HOPS, 0, 2)) 751 + port->ctl_credits = hop.initial_credits; 752 + } 753 + if (!port->ctl_credits) 754 + port->ctl_credits = 2; 755 + 745 756 } else if (port->port != 0) { 746 757 cap = tb_port_find_cap(port, TB_PORT_CAP_ADAP); 747 758 if (cap > 0) 748 759 port->cap_adap = cap; 749 760 } 750 761 751 - tb_dump_port(port->sw->tb, &port->config); 762 + port->total_credits = 763 + (port->config.nfc_credits & ADP_CS_4_TOTAL_BUFFERS_MASK) >> 764 + ADP_CS_4_TOTAL_BUFFERS_SHIFT; 765 + 766 + tb_dump_port(port->sw->tb, port); 752 767 753 768 INIT_LIST_HEAD(&port->list); 754 769 return 0; ··· 2598 2575 tb_lc_unconfigure_port(down); 2599 2576 } 2600 2577 2578 + static void tb_switch_credits_init(struct tb_switch *sw) 2579 + { 2580 + if (tb_switch_is_icm(sw)) 2581 + return; 2582 + if (!tb_switch_is_usb4(sw)) 2583 + return; 2584 + if (usb4_switch_credits_init(sw)) 2585 + tb_sw_info(sw, "failed to determine preferred buffer allocation, using defaults\n"); 2586 + } 2587 + 2601 2588 /** 2602 2589 * tb_switch_add() - Add a switch to the domain 2603 2590 * @sw: Switch to add ··· 2638 2605 } 2639 2606 2640 2607 if (!sw->safe_mode) { 2608 + tb_switch_credits_init(sw); 2609 + 2641 2610 /* read drom */ 2642 2611 ret = tb_drom_read(sw); 2643 2612 if (ret) {
+22
drivers/thunderbolt/tb.h
··· 136 136 * @rpm_complete: Completion used to wait for runtime resume to 137 137 * complete (ICM only) 138 138 * @quirks: Quirks used for this Thunderbolt switch 139 + * @credit_allocation: Are the below buffer allocation parameters valid 140 + * @max_usb3_credits: Router preferred number of buffers for USB 3.x 141 + * @min_dp_aux_credits: Router preferred minimum number of buffers for DP AUX 142 + * @min_dp_main_credits: Router preferred minimum number of buffers for DP MAIN 143 + * @max_pcie_credits: Router preferred number of buffers for PCIe 144 + * @max_dma_credits: Router preferred number of buffers for DMA/P2P 139 145 * 140 146 * When the switch is being added or removed to the domain (other 141 147 * switches) you need to have domain lock held. ··· 184 178 u8 depth; 185 179 struct completion rpm_complete; 186 180 unsigned long quirks; 181 + bool credit_allocation; 182 + unsigned int max_usb3_credits; 183 + unsigned int min_dp_aux_credits; 184 + unsigned int min_dp_main_credits; 185 + unsigned int max_pcie_credits; 186 + unsigned int max_dma_credits; 187 187 }; 188 188 189 189 /** ··· 211 199 * @in_hopids: Currently allocated input HopIDs 212 200 * @out_hopids: Currently allocated output HopIDs 213 201 * @list: Used to link ports to DP resources list 202 + * @total_credits: Total number of buffers available for this port 203 + * @ctl_credits: Buffers reserved for control path 214 204 * 215 205 * In USB4 terminology this structure represents an adapter (protocol or 216 206 * lane adapter). ··· 234 220 struct ida in_hopids; 235 221 struct ida out_hopids; 236 222 struct list_head list; 223 + unsigned int total_credits; 224 + unsigned int ctl_credits; 237 225 }; 238 226 239 227 /** ··· 882 866 struct tb_port *tb_next_port_on_path(struct tb_port *start, struct tb_port *end, 883 867 struct tb_port *prev); 884 868 869 + static inline bool tb_port_use_credit_allocation(const struct tb_port *port) 870 + { 871 + return tb_port_is_null(port) && port->sw->credit_allocation; 872 + } 873 + 885 874 /** 886 875 * tb_for_each_port_on_path() - Iterate over each port on path 887 876 * @src: Source port ··· 1015 994 const void *buf, size_t size); 1016 995 int usb4_switch_nvm_authenticate(struct tb_switch *sw); 1017 996 int usb4_switch_nvm_authenticate_status(struct tb_switch *sw, u32 *status); 997 + int usb4_switch_credits_init(struct tb_switch *sw); 1018 998 bool usb4_switch_query_dp_resource(struct tb_switch *sw, struct tb_port *in); 1019 999 int usb4_switch_alloc_dp_resource(struct tb_switch *sw, struct tb_port *in); 1020 1000 int usb4_switch_dealloc_dp_resource(struct tb_switch *sw, struct tb_port *in);
+1
drivers/thunderbolt/tb_regs.h
··· 229 229 USB4_SWITCH_OP_NVM_SET_OFFSET = 0x23, 230 230 USB4_SWITCH_OP_DROM_READ = 0x24, 231 231 USB4_SWITCH_OP_NVM_SECTOR_SIZE = 0x25, 232 + USB4_SWITCH_OP_BUFFER_ALLOC = 0x33, 232 233 }; 233 234 234 235 /* Router TMU configuration */
+155
drivers/thunderbolt/usb4.c
··· 36 36 37 37 #define USB4_NVM_SECTOR_SIZE_MASK GENMASK(23, 0) 38 38 39 + #define USB4_BA_LENGTH_MASK GENMASK(7, 0) 40 + #define USB4_BA_INDEX_MASK GENMASK(15, 0) 41 + 42 + enum usb4_ba_index { 43 + USB4_BA_MAX_USB3 = 0x1, 44 + USB4_BA_MIN_DP_AUX = 0x2, 45 + USB4_BA_MIN_DP_MAIN = 0x3, 46 + USB4_BA_MAX_PCIE = 0x4, 47 + USB4_BA_MAX_HI = 0x5, 48 + }; 49 + 50 + #define USB4_BA_VALUE_MASK GENMASK(31, 16) 51 + #define USB4_BA_VALUE_SHIFT 16 52 + 39 53 static int usb4_switch_wait_for_bit(struct tb_switch *sw, u32 offset, u32 bit, 40 54 u32 value, int timeout_msec) 41 55 { ··· 681 667 } 682 668 683 669 return 0; 670 + } 671 + 672 + /** 673 + * usb4_switch_credits_init() - Read buffer allocation parameters 674 + * @sw: USB4 router 675 + * 676 + * Reads @sw buffer allocation parameters and initializes @sw buffer 677 + * allocation fields accordingly. Specifically @sw->credits_allocation 678 + * is set to %true if these parameters can be used in tunneling. 679 + * 680 + * Returns %0 on success and negative errno otherwise. 681 + */ 682 + int usb4_switch_credits_init(struct tb_switch *sw) 683 + { 684 + int max_usb3, min_dp_aux, min_dp_main, max_pcie, max_dma; 685 + int ret, length, i, nports; 686 + const struct tb_port *port; 687 + u32 data[NVM_DATA_DWORDS]; 688 + u32 metadata = 0; 689 + u8 status = 0; 690 + 691 + memset(data, 0, sizeof(data)); 692 + ret = usb4_switch_op_data(sw, USB4_SWITCH_OP_BUFFER_ALLOC, &metadata, 693 + &status, NULL, 0, data, ARRAY_SIZE(data)); 694 + if (ret) 695 + return ret; 696 + if (status) 697 + return -EIO; 698 + 699 + length = metadata & USB4_BA_LENGTH_MASK; 700 + if (WARN_ON(length > ARRAY_SIZE(data))) 701 + return -EMSGSIZE; 702 + 703 + max_usb3 = -1; 704 + min_dp_aux = -1; 705 + min_dp_main = -1; 706 + max_pcie = -1; 707 + max_dma = -1; 708 + 709 + tb_sw_dbg(sw, "credit allocation parameters:\n"); 710 + 711 + for (i = 0; i < length; i++) { 712 + u16 index, value; 713 + 714 + index = data[i] & USB4_BA_INDEX_MASK; 715 + value = (data[i] & USB4_BA_VALUE_MASK) >> USB4_BA_VALUE_SHIFT; 716 + 717 + switch (index) { 718 + case USB4_BA_MAX_USB3: 719 + tb_sw_dbg(sw, " USB3: %u\n", value); 720 + max_usb3 = value; 721 + break; 722 + case USB4_BA_MIN_DP_AUX: 723 + tb_sw_dbg(sw, " DP AUX: %u\n", value); 724 + min_dp_aux = value; 725 + break; 726 + case USB4_BA_MIN_DP_MAIN: 727 + tb_sw_dbg(sw, " DP main: %u\n", value); 728 + min_dp_main = value; 729 + break; 730 + case USB4_BA_MAX_PCIE: 731 + tb_sw_dbg(sw, " PCIe: %u\n", value); 732 + max_pcie = value; 733 + break; 734 + case USB4_BA_MAX_HI: 735 + tb_sw_dbg(sw, " DMA: %u\n", value); 736 + max_dma = value; 737 + break; 738 + default: 739 + tb_sw_dbg(sw, " unknown credit allocation index %#x, skipping\n", 740 + index); 741 + break; 742 + } 743 + } 744 + 745 + /* 746 + * Validate the buffer allocation preferences. If we find 747 + * issues, log a warning and fall back using the hard-coded 748 + * values. 749 + */ 750 + 751 + /* Host router must report baMaxHI */ 752 + if (!tb_route(sw) && max_dma < 0) { 753 + tb_sw_warn(sw, "host router is missing baMaxHI\n"); 754 + goto err_invalid; 755 + } 756 + 757 + nports = 0; 758 + tb_switch_for_each_port(sw, port) { 759 + if (tb_port_is_null(port)) 760 + nports++; 761 + } 762 + 763 + /* Must have DP buffer allocation (multiple USB4 ports) */ 764 + if (nports > 2 && (min_dp_aux < 0 || min_dp_main < 0)) { 765 + tb_sw_warn(sw, "multiple USB4 ports require baMinDPaux/baMinDPmain\n"); 766 + goto err_invalid; 767 + } 768 + 769 + tb_switch_for_each_port(sw, port) { 770 + if (tb_port_is_dpout(port) && min_dp_main < 0) { 771 + tb_sw_warn(sw, "missing baMinDPmain"); 772 + goto err_invalid; 773 + } 774 + if ((tb_port_is_dpin(port) || tb_port_is_dpout(port)) && 775 + min_dp_aux < 0) { 776 + tb_sw_warn(sw, "missing baMinDPaux"); 777 + goto err_invalid; 778 + } 779 + if ((tb_port_is_usb3_down(port) || tb_port_is_usb3_up(port)) && 780 + max_usb3 < 0) { 781 + tb_sw_warn(sw, "missing baMaxUSB3"); 782 + goto err_invalid; 783 + } 784 + if ((tb_port_is_pcie_down(port) || tb_port_is_pcie_up(port)) && 785 + max_pcie < 0) { 786 + tb_sw_warn(sw, "missing baMaxPCIe"); 787 + goto err_invalid; 788 + } 789 + } 790 + 791 + /* 792 + * Buffer allocation passed the validation so we can use it in 793 + * path creation. 794 + */ 795 + sw->credit_allocation = true; 796 + if (max_usb3 > 0) 797 + sw->max_usb3_credits = max_usb3; 798 + if (min_dp_aux > 0) 799 + sw->min_dp_aux_credits = min_dp_aux; 800 + if (min_dp_main > 0) 801 + sw->min_dp_main_credits = min_dp_main; 802 + if (max_pcie > 0) 803 + sw->max_pcie_credits = max_pcie; 804 + if (max_dma > 0) 805 + sw->max_dma_credits = max_dma; 806 + 807 + return 0; 808 + 809 + err_invalid: 810 + return -EINVAL; 684 811 } 685 812 686 813 /**