// SPDX-License-Identifier: GPL-2.0 /* * Thunderbolt driver - Tunneling support * * Copyright (c) 2014 Andreas Noever <[email protected]> * Copyright (C) 2019, Intel Corporation */ #include <linux/delay.h> #include <linux/slab.h> #include <linux/list.h> #include <linux/ktime.h> #include <linux/string_helpers.h> #include "tunnel.h" #include "tb.h" /* PCIe adapters use always HopID of 8 for both directions */ #define TB_PCI_HOPID … #define TB_PCI_PATH_DOWN … #define TB_PCI_PATH_UP … #define TB_PCI_PRIORITY … #define TB_PCI_WEIGHT … /* USB3 adapters use always HopID of 8 for both directions */ #define TB_USB3_HOPID … #define TB_USB3_PATH_DOWN … #define TB_USB3_PATH_UP … #define TB_USB3_PRIORITY … #define TB_USB3_WEIGHT … /* DP adapters use HopID 8 for AUX and 9 for Video */ #define TB_DP_AUX_TX_HOPID … #define TB_DP_AUX_RX_HOPID … #define TB_DP_VIDEO_HOPID … #define TB_DP_VIDEO_PATH_OUT … #define TB_DP_AUX_PATH_OUT … #define TB_DP_AUX_PATH_IN … #define TB_DP_VIDEO_PRIORITY … #define TB_DP_VIDEO_WEIGHT … #define TB_DP_AUX_PRIORITY … #define TB_DP_AUX_WEIGHT … /* Minimum number of credits needed for PCIe path */ #define TB_MIN_PCIE_CREDITS … /* * Number of credits we try to allocate for each DMA path if not limited * by the host router baMaxHI. */ #define TB_DMA_CREDITS … /* Minimum number of credits for DMA path */ #define TB_MIN_DMA_CREDITS … #define TB_DMA_PRIORITY … #define TB_DMA_WEIGHT … /* * Reserve additional bandwidth for USB 3.x and PCIe bulk traffic * according to USB4 v2 Connection Manager guide. This ends up reserving * 1500 Mb/s for PCIe and 3000 Mb/s for USB 3.x taking weights into * account. */ #define USB4_V2_PCI_MIN_BANDWIDTH … #define USB4_V2_USB3_MIN_BANDWIDTH … static unsigned int dma_credits = …; module_param(dma_credits, uint, 0444); MODULE_PARM_DESC(…) …; static bool bw_alloc_mode = …; module_param(bw_alloc_mode, bool, 0444); MODULE_PARM_DESC(…) …; static const char * const tb_tunnel_names[] = …; static inline unsigned int tb_usable_credits(const struct tb_port *port) { … } /** * tb_available_credits() - Available credits for PCIe and DMA * @port: Lane adapter to check * @max_dp_streams: If non-%NULL stores maximum number of simultaneous DP * streams possible through this lane adapter */ static unsigned int tb_available_credits(const struct tb_port *port, size_t *max_dp_streams) { … } static void tb_init_pm_support(struct tb_path_hop *hop) { … } static struct tb_tunnel *tb_tunnel_alloc(struct tb *tb, size_t npaths, enum tb_tunnel_type type) { … } static int tb_pci_set_ext_encapsulation(struct tb_tunnel *tunnel, bool enable) { … } static int tb_pci_activate(struct tb_tunnel *tunnel, bool activate) { … } static int tb_pci_init_credits(struct tb_path_hop *hop) { … } static int tb_pci_init_path(struct tb_path *path) { … } /** * tb_tunnel_discover_pci() - Discover existing PCIe tunnels * @tb: Pointer to the domain structure * @down: PCIe downstream adapter * @alloc_hopid: Allocate HopIDs from visited ports * * If @down adapter is active, follows the tunnel to the PCIe upstream * adapter and back. Returns the discovered tunnel or %NULL if there was * no tunnel. */ struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down, bool alloc_hopid) { … } /** * tb_tunnel_alloc_pci() - allocate a pci tunnel * @tb: Pointer to the domain structure * @up: PCIe upstream adapter port * @down: PCIe downstream adapter port * * Allocate a PCI tunnel. The ports must be of type TB_TYPE_PCIE_UP and * TB_TYPE_PCIE_DOWN. * * Return: Returns a tb_tunnel on success or NULL on failure. */ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up, struct tb_port *down) { … } /** * tb_tunnel_reserved_pci() - Amount of bandwidth to reserve for PCIe * @port: Lane 0 adapter * @reserved_up: Upstream bandwidth in Mb/s to reserve * @reserved_down: Downstream bandwidth in Mb/s to reserve * * Can be called to any connected lane 0 adapter to find out how much * bandwidth needs to be left in reserve for possible PCIe bulk traffic. * Returns true if there is something to be reserved and writes the * amount to @reserved_down/@reserved_up. Otherwise returns false and * does not touch the parameters. */ bool tb_tunnel_reserved_pci(struct tb_port *port, int *reserved_up, int *reserved_down) { … } static bool tb_dp_is_usb4(const struct tb_switch *sw) { … } static int tb_dp_cm_handshake(struct tb_port *in, struct tb_port *out, int timeout_msec) { … } /* * Returns maximum possible rate from capability supporting only DP 2.0 * and below. Used when DP BW allocation mode is not enabled. */ static inline u32 tb_dp_cap_get_rate(u32 val) { … } /* * Returns maximum possible rate from capability supporting DP 2.1 * UHBR20, 13.5 and 10 rates as well. Use only when DP BW allocation * mode is enabled. */ static inline u32 tb_dp_cap_get_rate_ext(u32 val) { … } static inline bool tb_dp_is_uhbr_rate(unsigned int rate) { … } static inline u32 tb_dp_cap_set_rate(u32 val, u32 rate) { … } static inline u32 tb_dp_cap_get_lanes(u32 val) { … } static inline u32 tb_dp_cap_set_lanes(u32 val, u32 lanes) { … } static unsigned int tb_dp_bandwidth(unsigned int rate, unsigned int lanes) { … } static int tb_dp_reduce_bandwidth(int max_bw, u32 in_rate, u32 in_lanes, u32 out_rate, u32 out_lanes, u32 *new_rate, u32 *new_lanes) { … } static int tb_dp_xchg_caps(struct tb_tunnel *tunnel) { … } static int tb_dp_bandwidth_alloc_mode_enable(struct tb_tunnel *tunnel) { … } static int tb_dp_init(struct tb_tunnel *tunnel) { … } static void tb_dp_deinit(struct tb_tunnel *tunnel) { … } static int tb_dp_activate(struct tb_tunnel *tunnel, bool active) { … } /** * tb_dp_bandwidth_mode_maximum_bandwidth() - Maximum possible bandwidth * @tunnel: DP tunnel to check * @max_bw_rounded: Maximum bandwidth in Mb/s rounded up to the next granularity * * Returns maximum possible bandwidth for this tunnel in Mb/s. */ static int tb_dp_bandwidth_mode_maximum_bandwidth(struct tb_tunnel *tunnel, int *max_bw_rounded) { … } static int tb_dp_bandwidth_mode_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up, int *consumed_down) { … } static int tb_dp_allocated_bandwidth(struct tb_tunnel *tunnel, int *allocated_up, int *allocated_down) { … } static int tb_dp_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up, int *alloc_down) { … } static int tb_dp_wait_dprx(struct tb_tunnel *tunnel, int timeout_msec) { … } /* Read cap from tunnel DP IN */ static int tb_dp_read_cap(struct tb_tunnel *tunnel, unsigned int cap, u32 *rate, u32 *lanes) { … } static int tb_dp_maximum_bandwidth(struct tb_tunnel *tunnel, int *max_up, int *max_down) { … } static int tb_dp_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up, int *consumed_down) { … } static void tb_dp_init_aux_credits(struct tb_path_hop *hop) { … } static void tb_dp_init_aux_path(struct tb_path *path, bool pm_support) { … } static int tb_dp_init_video_credits(struct tb_path_hop *hop) { … } static int tb_dp_init_video_path(struct tb_path *path, bool pm_support) { … } static void tb_dp_dump(struct tb_tunnel *tunnel) { … } /** * tb_tunnel_discover_dp() - Discover existing Display Port tunnels * @tb: Pointer to the domain structure * @in: DP in adapter * @alloc_hopid: Allocate HopIDs from visited ports * * If @in adapter is active, follows the tunnel to the DP out adapter * and back. Returns the discovered tunnel or %NULL if there was no * tunnel. * * Return: DP tunnel or %NULL if no tunnel found. */ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in, bool alloc_hopid) { … } /** * tb_tunnel_alloc_dp() - allocate a Display Port tunnel * @tb: Pointer to the domain structure * @in: DP in adapter port * @out: DP out adapter port * @link_nr: Preferred lane adapter when the link is not bonded * @max_up: Maximum available upstream bandwidth for the DP tunnel. * %0 if no available bandwidth. * @max_down: Maximum available downstream bandwidth for the DP tunnel. * %0 if no available bandwidth. * * Allocates a tunnel between @in and @out that is capable of tunneling * Display Port traffic. * * Return: Returns a tb_tunnel on success or NULL on failure. */ struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in, struct tb_port *out, int link_nr, int max_up, int max_down) { … } static unsigned int tb_dma_available_credits(const struct tb_port *port) { … } static int tb_dma_reserve_credits(struct tb_path_hop *hop, unsigned int credits) { … } /* Path from lane adapter to NHI */ static int tb_dma_init_rx_path(struct tb_path *path, unsigned int credits) { … } /* Path from NHI to lane adapter */ static int tb_dma_init_tx_path(struct tb_path *path, unsigned int credits) { … } static void tb_dma_release_credits(struct tb_path_hop *hop) { … } static void tb_dma_deinit_path(struct tb_path *path) { … } static void tb_dma_deinit(struct tb_tunnel *tunnel) { … } /** * tb_tunnel_alloc_dma() - allocate a DMA tunnel * @tb: Pointer to the domain structure * @nhi: Host controller port * @dst: Destination null port which the other domain is connected to * @transmit_path: HopID used for transmitting packets * @transmit_ring: NHI ring number used to send packets towards the * other domain. Set to %-1 if TX path is not needed. * @receive_path: HopID used for receiving packets * @receive_ring: NHI ring number used to receive packets from the * other domain. Set to %-1 if RX path is not needed. * * Return: Returns a tb_tunnel on success or NULL on failure. */ struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi, struct tb_port *dst, int transmit_path, int transmit_ring, int receive_path, int receive_ring) { … } /** * tb_tunnel_match_dma() - Match DMA tunnel * @tunnel: Tunnel to match * @transmit_path: HopID used for transmitting packets. Pass %-1 to ignore. * @transmit_ring: NHI ring number used to send packets towards the * other domain. Pass %-1 to ignore. * @receive_path: HopID used for receiving packets. Pass %-1 to ignore. * @receive_ring: NHI ring number used to receive packets from the * other domain. Pass %-1 to ignore. * * This function can be used to match specific DMA tunnel, if there are * multiple DMA tunnels going through the same XDomain connection. * Returns true if there is match and false otherwise. */ bool tb_tunnel_match_dma(const struct tb_tunnel *tunnel, int transmit_path, int transmit_ring, int receive_path, int receive_ring) { … } static int tb_usb3_max_link_rate(struct tb_port *up, struct tb_port *down) { … } static int tb_usb3_init(struct tb_tunnel *tunnel) { … } static int tb_usb3_activate(struct tb_tunnel *tunnel, bool activate) { … } static int tb_usb3_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up, int *consumed_down) { … } static int tb_usb3_release_unused_bandwidth(struct tb_tunnel *tunnel) { … } static void tb_usb3_reclaim_available_bandwidth(struct tb_tunnel *tunnel, int *available_up, int *available_down) { … } static void tb_usb3_init_credits(struct tb_path_hop *hop) { … } static void tb_usb3_init_path(struct tb_path *path) { … } /** * tb_tunnel_discover_usb3() - Discover existing USB3 tunnels * @tb: Pointer to the domain structure * @down: USB3 downstream adapter * @alloc_hopid: Allocate HopIDs from visited ports * * If @down adapter is active, follows the tunnel to the USB3 upstream * adapter and back. Returns the discovered tunnel or %NULL if there was * no tunnel. */ struct tb_tunnel *tb_tunnel_discover_usb3(struct tb *tb, struct tb_port *down, bool alloc_hopid) { … } /** * tb_tunnel_alloc_usb3() - allocate a USB3 tunnel * @tb: Pointer to the domain structure * @up: USB3 upstream adapter port * @down: USB3 downstream adapter port * @max_up: Maximum available upstream bandwidth for the USB3 tunnel. * %0 if no available bandwidth. * @max_down: Maximum available downstream bandwidth for the USB3 tunnel. * %0 if no available bandwidth. * * Allocate an USB3 tunnel. The ports must be of type @TB_TYPE_USB3_UP and * @TB_TYPE_USB3_DOWN. * * Return: Returns a tb_tunnel on success or %NULL on failure. */ struct tb_tunnel *tb_tunnel_alloc_usb3(struct tb *tb, struct tb_port *up, struct tb_port *down, int max_up, int max_down) { … } /** * tb_tunnel_free() - free a tunnel * @tunnel: Tunnel to be freed * * Frees a tunnel. The tunnel does not need to be deactivated. */ void tb_tunnel_free(struct tb_tunnel *tunnel) { … } /** * tb_tunnel_is_invalid - check whether an activated path is still valid * @tunnel: Tunnel to check */ bool tb_tunnel_is_invalid(struct tb_tunnel *tunnel) { … } /** * tb_tunnel_restart() - activate a tunnel after a hardware reset * @tunnel: Tunnel to restart * * Return: 0 on success and negative errno in case if failure */ int tb_tunnel_restart(struct tb_tunnel *tunnel) { … } /** * tb_tunnel_activate() - activate a tunnel * @tunnel: Tunnel to activate * * Return: Returns 0 on success or an error code on failure. */ int tb_tunnel_activate(struct tb_tunnel *tunnel) { … } /** * tb_tunnel_deactivate() - deactivate a tunnel * @tunnel: Tunnel to deactivate */ void tb_tunnel_deactivate(struct tb_tunnel *tunnel) { … } /** * tb_tunnel_port_on_path() - Does the tunnel go through port * @tunnel: Tunnel to check * @port: Port to check * * Returns true if @tunnel goes through @port (direction does not matter), * false otherwise. */ bool tb_tunnel_port_on_path(const struct tb_tunnel *tunnel, const struct tb_port *port) { … } static bool tb_tunnel_is_active(const struct tb_tunnel *tunnel) { … } /** * tb_tunnel_maximum_bandwidth() - Return maximum possible bandwidth * @tunnel: Tunnel to check * @max_up: Maximum upstream bandwidth in Mb/s * @max_down: Maximum downstream bandwidth in Mb/s * * Returns maximum possible bandwidth this tunnel can go if not limited * by other bandwidth clients. If the tunnel does not support this * returns %-EOPNOTSUPP. */ int tb_tunnel_maximum_bandwidth(struct tb_tunnel *tunnel, int *max_up, int *max_down) { … } /** * tb_tunnel_allocated_bandwidth() - Return bandwidth allocated for the tunnel * @tunnel: Tunnel to check * @allocated_up: Currently allocated upstream bandwidth in Mb/s is stored here * @allocated_down: Currently allocated downstream bandwidth in Mb/s is * stored here * * Returns the bandwidth allocated for the tunnel. This may be higher * than what the tunnel actually consumes. */ int tb_tunnel_allocated_bandwidth(struct tb_tunnel *tunnel, int *allocated_up, int *allocated_down) { … } /** * tb_tunnel_alloc_bandwidth() - Change tunnel bandwidth allocation * @tunnel: Tunnel whose bandwidth allocation to change * @alloc_up: New upstream bandwidth in Mb/s * @alloc_down: New downstream bandwidth in Mb/s * * Tries to change tunnel bandwidth allocation. If succeeds returns %0 * and updates @alloc_up and @alloc_down to that was actually allocated * (it may not be the same as passed originally). Returns negative errno * in case of failure. */ int tb_tunnel_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up, int *alloc_down) { … } /** * tb_tunnel_consumed_bandwidth() - Return bandwidth consumed by the tunnel * @tunnel: Tunnel to check * @consumed_up: Consumed bandwidth in Mb/s from @dst_port to @src_port. * Can be %NULL. * @consumed_down: Consumed bandwidth in Mb/s from @src_port to @dst_port. * Can be %NULL. * * Stores the amount of isochronous bandwidth @tunnel consumes in * @consumed_up and @consumed_down. In case of success returns %0, * negative errno otherwise. */ int tb_tunnel_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up, int *consumed_down) { … } /** * tb_tunnel_release_unused_bandwidth() - Release unused bandwidth * @tunnel: Tunnel whose unused bandwidth to release * * If tunnel supports dynamic bandwidth management (USB3 tunnels at the * moment) this function makes it to release all the unused bandwidth. * * Returns %0 in case of success and negative errno otherwise. */ int tb_tunnel_release_unused_bandwidth(struct tb_tunnel *tunnel) { … } /** * tb_tunnel_reclaim_available_bandwidth() - Reclaim available bandwidth * @tunnel: Tunnel reclaiming available bandwidth * @available_up: Available upstream bandwidth (in Mb/s) * @available_down: Available downstream bandwidth (in Mb/s) * * Reclaims bandwidth from @available_up and @available_down and updates * the variables accordingly (e.g decreases both according to what was * reclaimed by the tunnel). If nothing was reclaimed the values are * kept as is. */ void tb_tunnel_reclaim_available_bandwidth(struct tb_tunnel *tunnel, int *available_up, int *available_down) { … } const char *tb_tunnel_type_name(const struct tb_tunnel *tunnel) { … }