linux/drivers/thunderbolt/tb.c

// SPDX-License-Identifier: GPL-2.0
/*
 * Thunderbolt driver - bus logic (NHI independent)
 *
 * Copyright (c) 2014 Andreas Noever <[email protected]>
 * Copyright (C) 2019, Intel Corporation
 */

#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
#include <linux/platform_data/x86/apple.h>

#include "tb.h"
#include "tb_regs.h"
#include "tunnel.h"

#define TB_TIMEOUT
#define TB_RELEASE_BW_TIMEOUT

/*
 * Minimum bandwidth (in Mb/s) that is needed in the single transmitter/receiver
 * direction. This is 40G - 10% guard band bandwidth.
 */
#define TB_ASYM_MIN

/*
 * Threshold bandwidth (in Mb/s) that is used to switch the links to
 * asymmetric and back. This is selected as 45G which means when the
 * request is higher than this, we switch the link to asymmetric, and
 * when it is less than this we switch it back. The 45G is selected so
 * that we still have 27G (of the total 72G) for bulk PCIe traffic when
 * switching back to symmetric.
 */
#define TB_ASYM_THRESHOLD

#define MAX_GROUPS

static unsigned int asym_threshold =;
module_param_named(asym_threshold, asym_threshold, uint, 0444);
MODULE_PARM_DESC();

/**
 * struct tb_cm - Simple Thunderbolt connection manager
 * @tunnel_list: List of active tunnels
 * @dp_resources: List of available DP resources for DP tunneling
 * @hotplug_active: tb_handle_hotplug will stop progressing plug
 *		    events and exit if this is not set (it needs to
 *		    acquire the lock one more time). Used to drain wq
 *		    after cfg has been paused.
 * @remove_work: Work used to remove any unplugged routers after
 *		 runtime resume
 * @groups: Bandwidth groups used in this domain.
 */
struct tb_cm {};

static inline struct tb *tcm_to_tb(struct tb_cm *tcm)
{}

struct tb_hotplug_event {};

static void tb_handle_hotplug(struct work_struct *work);

static void tb_queue_hotplug(struct tb *tb, u64 route, u8 port, bool unplug)
{}

/* enumeration & hot plug handling */

static void tb_add_dp_resources(struct tb_switch *sw)
{}

static void tb_remove_dp_resources(struct tb_switch *sw)
{}

static void tb_discover_dp_resource(struct tb *tb, struct tb_port *port)
{}

static void tb_discover_dp_resources(struct tb *tb)
{}

/* Enables CL states up to host router */
static int tb_enable_clx(struct tb_switch *sw)
{}

/**
 * tb_disable_clx() - Disable CL states up to host router
 * @sw: Router to start
 *
 * Disables CL states from @sw up to the host router. Returns true if
 * any CL state were disabled. This can be used to figure out whether
 * the link was setup by us or the boot firmware so we don't
 * accidentally enable them if they were not enabled during discovery.
 */
static bool tb_disable_clx(struct tb_switch *sw)
{}

static int tb_increase_switch_tmu_accuracy(struct device *dev, void *data)
{}

static void tb_increase_tmu_accuracy(struct tb_tunnel *tunnel)
{}

static int tb_enable_tmu(struct tb_switch *sw)
{}

static void tb_switch_discover_tunnels(struct tb_switch *sw,
				       struct list_head *list,
				       bool alloc_hopids)
{}

static int tb_port_configure_xdomain(struct tb_port *port, struct tb_xdomain *xd)
{}

static void tb_port_unconfigure_xdomain(struct tb_port *port)
{}

static void tb_scan_xdomain(struct tb_port *port)
{}

/**
 * tb_find_unused_port() - return the first inactive port on @sw
 * @sw: Switch to find the port on
 * @type: Port type to look for
 */
static struct tb_port *tb_find_unused_port(struct tb_switch *sw,
					   enum tb_port_type type)
{}

static struct tb_port *tb_find_usb3_down(struct tb_switch *sw,
					 const struct tb_port *port)
{}

static struct tb_tunnel *tb_find_tunnel(struct tb *tb, enum tb_tunnel_type type,
					struct tb_port *src_port,
					struct tb_port *dst_port)
{}

static struct tb_tunnel *tb_find_first_usb3_tunnel(struct tb *tb,
						   struct tb_port *src_port,
						   struct tb_port *dst_port)
{}

/**
 * tb_consumed_usb3_pcie_bandwidth() - Consumed USB3/PCIe bandwidth over a single link
 * @tb: Domain structure
 * @src_port: Source protocol adapter
 * @dst_port: Destination protocol adapter
 * @port: USB4 port the consumed bandwidth is calculated
 * @consumed_up: Consumed upsream bandwidth (Mb/s)
 * @consumed_down: Consumed downstream bandwidth (Mb/s)
 *
 * Calculates consumed USB3 and PCIe bandwidth at @port between path
 * from @src_port to @dst_port. Does not take USB3 tunnel starting from
 * @src_port and ending on @src_port into account because that bandwidth is
 * already included in as part of the "first hop" USB3 tunnel.
 */
static int tb_consumed_usb3_pcie_bandwidth(struct tb *tb,
					   struct tb_port *src_port,
					   struct tb_port *dst_port,
					   struct tb_port *port,
					   int *consumed_up,
					   int *consumed_down)
{}

/**
 * tb_consumed_dp_bandwidth() - Consumed DP bandwidth over a single link
 * @tb: Domain structure
 * @src_port: Source protocol adapter
 * @dst_port: Destination protocol adapter
 * @port: USB4 port the consumed bandwidth is calculated
 * @consumed_up: Consumed upsream bandwidth (Mb/s)
 * @consumed_down: Consumed downstream bandwidth (Mb/s)
 *
 * Calculates consumed DP bandwidth at @port between path from @src_port
 * to @dst_port. Does not take tunnel starting from @src_port and ending
 * from @src_port into account.
 *
 * If there is bandwidth reserved for any of the groups between
 * @src_port and @dst_port (but not yet used) that is also taken into
 * account in the returned consumed bandwidth.
 */
static int tb_consumed_dp_bandwidth(struct tb *tb,
				    struct tb_port *src_port,
				    struct tb_port *dst_port,
				    struct tb_port *port,
				    int *consumed_up,
				    int *consumed_down)
{}

static bool tb_asym_supported(struct tb_port *src_port, struct tb_port *dst_port,
			      struct tb_port *port)
{}

/**
 * tb_maximum_bandwidth() - Maximum bandwidth over a single link
 * @tb: Domain structure
 * @src_port: Source protocol adapter
 * @dst_port: Destination protocol adapter
 * @port: USB4 port the total bandwidth is calculated
 * @max_up: Maximum upstream bandwidth (Mb/s)
 * @max_down: Maximum downstream bandwidth (Mb/s)
 * @include_asym: Include bandwidth if the link is switched from
 *		  symmetric to asymmetric
 *
 * Returns maximum possible bandwidth in @max_up and @max_down over a
 * single link at @port. If @include_asym is set then includes the
 * additional banwdith if the links are transitioned into asymmetric to
 * direction from @src_port to @dst_port.
 */
static int tb_maximum_bandwidth(struct tb *tb, struct tb_port *src_port,
				struct tb_port *dst_port, struct tb_port *port,
				int *max_up, int *max_down, bool include_asym)
{}

/**
 * tb_available_bandwidth() - Available bandwidth for tunneling
 * @tb: Domain structure
 * @src_port: Source protocol adapter
 * @dst_port: Destination protocol adapter
 * @available_up: Available bandwidth upstream (Mb/s)
 * @available_down: Available bandwidth downstream (Mb/s)
 * @include_asym: Include bandwidth if the link is switched from
 *		  symmetric to asymmetric
 *
 * Calculates maximum available bandwidth for protocol tunneling between
 * @src_port and @dst_port at the moment. This is minimum of maximum
 * link bandwidth across all links reduced by currently consumed
 * bandwidth on that link.
 *
 * If @include_asym is true then includes also bandwidth that can be
 * added when the links are transitioned into asymmetric (but does not
 * transition the links).
 */
static int tb_available_bandwidth(struct tb *tb, struct tb_port *src_port,
				 struct tb_port *dst_port, int *available_up,
				 int *available_down, bool include_asym)
{}

static int tb_release_unused_usb3_bandwidth(struct tb *tb,
					    struct tb_port *src_port,
					    struct tb_port *dst_port)
{}

static void tb_reclaim_usb3_bandwidth(struct tb *tb, struct tb_port *src_port,
				      struct tb_port *dst_port)
{}

static int tb_tunnel_usb3(struct tb *tb, struct tb_switch *sw)
{}

static int tb_create_usb3_tunnels(struct tb_switch *sw)
{}

/**
 * tb_configure_asym() - Transition links to asymmetric if needed
 * @tb: Domain structure
 * @src_port: Source adapter to start the transition
 * @dst_port: Destination adapter
 * @requested_up: Additional bandwidth (Mb/s) required upstream
 * @requested_down: Additional bandwidth (Mb/s) required downstream
 *
 * Transition links between @src_port and @dst_port into asymmetric, with
 * three lanes in the direction from @src_port towards @dst_port and one lane
 * in the opposite direction, if the bandwidth requirements
 * (requested + currently consumed) on that link exceed @asym_threshold.
 *
 * Must be called with available >= requested over all links.
 */
static int tb_configure_asym(struct tb *tb, struct tb_port *src_port,
			     struct tb_port *dst_port, int requested_up,
			     int requested_down)
{}

/**
 * tb_configure_sym() - Transition links to symmetric if possible
 * @tb: Domain structure
 * @src_port: Source adapter to start the transition
 * @dst_port: Destination adapter
 * @keep_asym: Keep asymmetric link if preferred
 *
 * Goes over each link from @src_port to @dst_port and tries to
 * transition the link to symmetric if the currently consumed bandwidth
 * allows and link asymmetric preference is ignored (if @keep_asym is %false).
 */
static int tb_configure_sym(struct tb *tb, struct tb_port *src_port,
			    struct tb_port *dst_port, bool keep_asym)
{}

static void tb_configure_link(struct tb_port *down, struct tb_port *up,
			      struct tb_switch *sw)
{}

static void tb_scan_port(struct tb_port *port);

/*
 * tb_scan_switch() - scan for and initialize downstream switches
 */
static void tb_scan_switch(struct tb_switch *sw)
{}

/*
 * tb_scan_port() - check for and initialize switches below port
 */
static void tb_scan_port(struct tb_port *port)
{}

static void
tb_recalc_estimated_bandwidth_for_group(struct tb_bandwidth_group *group)
{}

static void tb_recalc_estimated_bandwidth(struct tb *tb)
{}

static bool __release_group_bandwidth(struct tb_bandwidth_group *group)
{}

static void __configure_group_sym(struct tb_bandwidth_group *group)
{}

static void tb_bandwidth_group_release_work(struct work_struct *work)
{}

static void tb_init_bandwidth_groups(struct tb_cm *tcm)
{}

static void tb_bandwidth_group_attach_port(struct tb_bandwidth_group *group,
					   struct tb_port *in)
{}

static struct tb_bandwidth_group *tb_find_free_bandwidth_group(struct tb_cm *tcm)
{}

static struct tb_bandwidth_group *
tb_attach_bandwidth_group(struct tb_cm *tcm, struct tb_port *in,
			  struct tb_port *out)
{}

static void tb_discover_bandwidth_group(struct tb_cm *tcm, struct tb_port *in,
					struct tb_port *out)
{}

static void tb_detach_bandwidth_group(struct tb_port *in)
{}

static void tb_discover_tunnels(struct tb *tb)
{}

static void tb_deactivate_and_free_tunnel(struct tb_tunnel *tunnel)
{}

/*
 * tb_free_invalid_tunnels() - destroy tunnels of devices that have gone away
 */
static void tb_free_invalid_tunnels(struct tb *tb)
{}

/*
 * tb_free_unplugged_children() - traverse hierarchy and free unplugged switches
 */
static void tb_free_unplugged_children(struct tb_switch *sw)
{}

static struct tb_port *tb_find_pcie_down(struct tb_switch *sw,
					 const struct tb_port *port)
{}

static struct tb_port *tb_find_dp_out(struct tb *tb, struct tb_port *in)
{}

static bool tb_tunnel_one_dp(struct tb *tb, struct tb_port *in,
			     struct tb_port *out)
{}

static void tb_tunnel_dp(struct tb *tb)
{}

static void tb_enter_redrive(struct tb_port *port)
{}

static void tb_exit_redrive(struct tb_port *port)
{}

static void tb_dp_resource_unavailable(struct tb *tb, struct tb_port *port)
{}

static void tb_dp_resource_available(struct tb *tb, struct tb_port *port)
{}

static void tb_disconnect_and_release_dp(struct tb *tb)
{}

static int tb_disconnect_pci(struct tb *tb, struct tb_switch *sw)
{}

static int tb_tunnel_pci(struct tb *tb, struct tb_switch *sw)
{}

static int tb_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
				    int transmit_path, int transmit_ring,
				    int receive_path, int receive_ring)
{}

static void __tb_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
					  int transmit_path, int transmit_ring,
					  int receive_path, int receive_ring)
{}

static int tb_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
				       int transmit_path, int transmit_ring,
				       int receive_path, int receive_ring)
{}

/* hotplug handling */

/*
 * tb_handle_hotplug() - handle hotplug event
 *
 * Executes on tb->wq.
 */
static void tb_handle_hotplug(struct work_struct *work)
{}

static int tb_alloc_dp_bandwidth(struct tb_tunnel *tunnel, int *requested_up,
				 int *requested_down)
{}

static void tb_handle_dp_bandwidth_request(struct work_struct *work)
{}

static void tb_queue_dp_bandwidth_request(struct tb *tb, u64 route, u8 port)
{}

static void tb_handle_notification(struct tb *tb, u64 route,
				   const struct cfg_error_pkg *error)
{}

/*
 * tb_schedule_hotplug_handler() - callback function for the control channel
 *
 * Delegates to tb_handle_hotplug.
 */
static void tb_handle_event(struct tb *tb, enum tb_cfg_pkg_type type,
			    const void *buf, size_t size)
{}

static void tb_stop(struct tb *tb)
{}

static void tb_deinit(struct tb *tb)
{}

static int tb_scan_finalize_switch(struct device *dev, void *data)
{}

static int tb_start(struct tb *tb, bool reset)
{}

static int tb_suspend_noirq(struct tb *tb)
{}

static void tb_restore_children(struct tb_switch *sw)
{}

static int tb_resume_noirq(struct tb *tb)
{}

static int tb_free_unplugged_xdomains(struct tb_switch *sw)
{}

static int tb_freeze_noirq(struct tb *tb)
{}

static int tb_thaw_noirq(struct tb *tb)
{}

static void tb_complete(struct tb *tb)
{}

static int tb_runtime_suspend(struct tb *tb)
{}

static void tb_remove_work(struct work_struct *work)
{}

static int tb_runtime_resume(struct tb *tb)
{}

static const struct tb_cm_ops tb_cm_ops =;

/*
 * During suspend the Thunderbolt controller is reset and all PCIe
 * tunnels are lost. The NHI driver will try to reestablish all tunnels
 * during resume. This adds device links between the tunneled PCIe
 * downstream ports and the NHI so that the device core will make sure
 * NHI is resumed first before the rest.
 */
static bool tb_apple_add_links(struct tb_nhi *nhi)
{}

struct tb *tb_probe(struct tb_nhi *nhi)
{}