aboutsummaryrefslogtreecommitdiff
path: root/drivers/thunderbolt/tb.h
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thunderbolt/tb.h')
-rw-r--r--drivers/thunderbolt/tb.h227
1 files changed, 201 insertions, 26 deletions
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 52584c4003e3..b12c8f33d89c 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -43,6 +43,7 @@ struct tb_switch_nvm {
};
#define TB_SWITCH_KEY_SIZE 32
+#define TB_SWITCH_MAX_DEPTH 6
/**
* struct tb_switch - a thunderbolt switch
@@ -62,6 +63,7 @@ struct tb_switch_nvm {
* @device_name: Name of the device (or %NULL if not known)
* @generation: Switch Thunderbolt generation
* @cap_plug_events: Offset to the plug events capability (%0 if not found)
+ * @cap_lc: Offset to the link controller capability (%0 if not found)
* @is_unplugged: The switch is going away
* @drom: DROM of the switch (%NULL if not found)
* @nvm: Pointer to the NVM if the switch has one (%NULL otherwise)
@@ -70,7 +72,6 @@ struct tb_switch_nvm {
* @boot: Whether the switch was already authorized on boot or not
* @rpm: The switch supports runtime PM
* @authorized: Whether the switch is authorized by user or policy
- * @work: Work used to automatically authorize a switch
* @security_level: Switch supported security level
* @key: Contains the key used to challenge the device or %NULL if not
* supported. Size of the key is %TB_SWITCH_KEY_SIZE.
@@ -80,8 +81,7 @@ struct tb_switch_nvm {
* @depth: Depth in the chain this switch is connected (ICM only)
*
* When the switch is being added or removed to the domain (other
- * switches) you need to have domain lock held. For switch authorization
- * internal switch_lock is enough.
+ * switches) you need to have domain lock held.
*/
struct tb_switch {
struct device dev;
@@ -97,6 +97,7 @@ struct tb_switch {
const char *device_name;
unsigned int generation;
int cap_plug_events;
+ int cap_lc;
bool is_unplugged;
u8 *drom;
struct tb_switch_nvm *nvm;
@@ -105,7 +106,6 @@ struct tb_switch {
bool boot;
bool rpm;
unsigned int authorized;
- struct work_struct work;
enum tb_security_level security_level;
u8 *key;
u8 connection_id;
@@ -121,11 +121,14 @@ struct tb_switch {
* @remote: Remote port (%NULL if not connected)
* @xdomain: Remote host (%NULL if not connected)
* @cap_phy: Offset, zero if not found
+ * @cap_adap: Offset of the adapter specific capability (%0 if not present)
* @port: Port number on switch
* @disabled: Disabled by eeprom
* @dual_link_port: If the switch is connected using two ports, points
* to the other port.
* @link_nr: Is this primary or secondary port on the dual_link.
+ * @in_hopids: Currently allocated input HopIDs
+ * @out_hopids: Currently allocated output HopIDs
*/
struct tb_port {
struct tb_regs_port_header config;
@@ -133,19 +136,35 @@ struct tb_port {
struct tb_port *remote;
struct tb_xdomain *xdomain;
int cap_phy;
+ int cap_adap;
u8 port;
bool disabled;
struct tb_port *dual_link_port;
u8 link_nr:1;
+ struct ida in_hopids;
+ struct ida out_hopids;
};
/**
* struct tb_path_hop - routing information for a tb_path
+ * @in_port: Ingress port of a switch
+ * @out_port: Egress port of a switch where the packet is routed out
+ * (must be on the same switch than @in_port)
+ * @in_hop_index: HopID where the path configuration entry is placed in
+ * the path config space of @in_port.
+ * @in_counter_index: Used counter index (not used in the driver
+ * currently, %-1 to disable)
+ * @next_hop_index: HopID of the packet when it is routed out from @out_port
+ * @initial_credits: Number of initial flow control credits allocated for
+ * the path
*
* Hop configuration is always done on the IN port of a switch.
* in_port and out_port have to be on the same switch. Packets arriving on
* in_port with "hop" = in_hop_index will get routed to through out_port. The
- * next hop to take (on out_port->remote) is determined by next_hop_index.
+ * next hop to take (on out_port->remote) is determined by
+ * next_hop_index. When routing packet to another switch (out->remote is
+ * set) the @next_hop_index must match the @in_hop_index of that next
+ * hop to make routing possible.
*
* in_counter_index is the index of a counter (in TB_CFG_COUNTERS) on the in
* port.
@@ -154,44 +173,71 @@ struct tb_path_hop {
struct tb_port *in_port;
struct tb_port *out_port;
int in_hop_index;
- int in_counter_index; /* write -1 to disable counters for this hop. */
+ int in_counter_index;
int next_hop_index;
+ unsigned int initial_credits;
};
/**
* enum tb_path_port - path options mask
+ * @TB_PATH_NONE: Do not activate on any hop on path
+ * @TB_PATH_SOURCE: Activate on the first hop (out of src)
+ * @TB_PATH_INTERNAL: Activate on the intermediate hops (not the first/last)
+ * @TB_PATH_DESTINATION: Activate on the last hop (into dst)
+ * @TB_PATH_ALL: Activate on all hops on the path
*/
enum tb_path_port {
TB_PATH_NONE = 0,
- TB_PATH_SOURCE = 1, /* activate on the first hop (out of src) */
- TB_PATH_INTERNAL = 2, /* activate on other hops (not the first/last) */
- TB_PATH_DESTINATION = 4, /* activate on the last hop (into dst) */
+ TB_PATH_SOURCE = 1,
+ TB_PATH_INTERNAL = 2,
+ TB_PATH_DESTINATION = 4,
TB_PATH_ALL = 7,
};
/**
* struct tb_path - a unidirectional path between two ports
+ * @tb: Pointer to the domain structure
+ * @name: Name of the path (used for debugging)
+ * @nfc_credits: Number of non flow controlled credits allocated for the path
+ * @ingress_shared_buffer: Shared buffering used for ingress ports on the path
+ * @egress_shared_buffer: Shared buffering used for egress ports on the path
+ * @ingress_fc_enable: Flow control for ingress ports on the path
+ * @egress_fc_enable: Flow control for egress ports on the path
+ * @priority: Priority group if the path
+ * @weight: Weight of the path inside the priority group
+ * @drop_packages: Drop packages from queue tail or head
+ * @activated: Is the path active
+ * @clear_fc: Clear all flow control from the path config space entries
+ * when deactivating this path
+ * @hops: Path hops
+ * @path_length: How many hops the path uses
*
- * A path consists of a number of hops (see tb_path_hop). To establish a PCIe
- * tunnel two paths have to be created between the two PCIe ports.
- *
+ * A path consists of a number of hops (see &struct tb_path_hop). To
+ * establish a PCIe tunnel two paths have to be created between the two
+ * PCIe ports.
*/
struct tb_path {
struct tb *tb;
- int nfc_credits; /* non flow controlled credits */
+ const char *name;
+ int nfc_credits;
enum tb_path_port ingress_shared_buffer;
enum tb_path_port egress_shared_buffer;
enum tb_path_port ingress_fc_enable;
enum tb_path_port egress_fc_enable;
- int priority:3;
+ unsigned int priority:3;
int weight:4;
bool drop_packages;
bool activated;
+ bool clear_fc;
struct tb_path_hop *hops;
- int path_length; /* number of hops */
+ int path_length;
};
+/* HopIDs 0-7 are reserved by the Thunderbolt protocol */
+#define TB_PATH_MIN_HOPID 8
+#define TB_PATH_MAX_HOPS 7
+
/**
* struct tb_cm_ops - Connection manager specific operations vector
* @driver_ready: Called right after control channel is started. Used by
@@ -261,7 +307,20 @@ static inline struct tb_port *tb_upstream_port(struct tb_switch *sw)
return &sw->ports[sw->config.upstream_port_number];
}
-static inline u64 tb_route(struct tb_switch *sw)
+/**
+ * tb_is_upstream_port() - Is the port upstream facing
+ * @port: Port to check
+ *
+ * Returns true if @port is upstream facing port. In case of dual link
+ * ports both return true.
+ */
+static inline bool tb_is_upstream_port(const struct tb_port *port)
+{
+ const struct tb_port *upstream_port = tb_upstream_port(port->sw);
+ return port == upstream_port || port->dual_link_port == upstream_port;
+}
+
+static inline u64 tb_route(const struct tb_switch *sw)
{
return ((u64) sw->config.route_hi) << 32 | sw->config.route_lo;
}
@@ -276,9 +335,54 @@ static inline struct tb_port *tb_port_at(u64 route, struct tb_switch *sw)
return &sw->ports[port];
}
+/**
+ * tb_port_has_remote() - Does the port have switch connected downstream
+ * @port: Port to check
+ *
+ * Returns true only when the port is primary port and has remote set.
+ */
+static inline bool tb_port_has_remote(const struct tb_port *port)
+{
+ if (tb_is_upstream_port(port))
+ return false;
+ if (!port->remote)
+ return false;
+ if (port->dual_link_port && port->link_nr)
+ return false;
+
+ return true;
+}
+
+static inline bool tb_port_is_null(const struct tb_port *port)
+{
+ return port && port->port && port->config.type == TB_TYPE_PORT;
+}
+
+static inline bool tb_port_is_pcie_down(const struct tb_port *port)
+{
+ return port && port->config.type == TB_TYPE_PCIE_DOWN;
+}
+
+static inline bool tb_port_is_pcie_up(const struct tb_port *port)
+{
+ return port && port->config.type == TB_TYPE_PCIE_UP;
+}
+
+static inline bool tb_port_is_dpin(const struct tb_port *port)
+{
+ return port && port->config.type == TB_TYPE_DP_HDMI_IN;
+}
+
+static inline bool tb_port_is_dpout(const struct tb_port *port)
+{
+ return port && port->config.type == TB_TYPE_DP_HDMI_OUT;
+}
+
static inline int tb_sw_read(struct tb_switch *sw, void *buffer,
enum tb_cfg_space space, u32 offset, u32 length)
{
+ if (sw->is_unplugged)
+ return -ENODEV;
return tb_cfg_read(sw->tb->ctl,
buffer,
tb_route(sw),
@@ -291,6 +395,8 @@ static inline int tb_sw_read(struct tb_switch *sw, void *buffer,
static inline int tb_sw_write(struct tb_switch *sw, void *buffer,
enum tb_cfg_space space, u32 offset, u32 length)
{
+ if (sw->is_unplugged)
+ return -ENODEV;
return tb_cfg_write(sw->tb->ctl,
buffer,
tb_route(sw),
@@ -303,6 +409,8 @@ static inline int tb_sw_write(struct tb_switch *sw, void *buffer,
static inline int tb_port_read(struct tb_port *port, void *buffer,
enum tb_cfg_space space, u32 offset, u32 length)
{
+ if (port->sw->is_unplugged)
+ return -ENODEV;
return tb_cfg_read(port->sw->tb->ctl,
buffer,
tb_route(port->sw),
@@ -315,6 +423,8 @@ static inline int tb_port_read(struct tb_port *port, void *buffer,
static inline int tb_port_write(struct tb_port *port, const void *buffer,
enum tb_cfg_space space, u32 offset, u32 length)
{
+ if (port->sw->is_unplugged)
+ return -ENODEV;
return tb_cfg_write(port->sw->tb->ctl,
buffer,
tb_route(port->sw),
@@ -332,7 +442,7 @@ static inline int tb_port_write(struct tb_port *port, const void *buffer,
#define __TB_SW_PRINT(level, sw, fmt, arg...) \
do { \
- struct tb_switch *__sw = (sw); \
+ const struct tb_switch *__sw = (sw); \
level(__sw->tb, "%llx: " fmt, \
tb_route(__sw), ## arg); \
} while (0)
@@ -343,7 +453,7 @@ static inline int tb_port_write(struct tb_port *port, const void *buffer,
#define __TB_PORT_PRINT(level, _port, fmt, arg...) \
do { \
- struct tb_port *__port = (_port); \
+ const struct tb_port *__port = (_port); \
level(__port->sw->tb, "%llx:%x: " fmt, \
tb_route(__port->sw), __port->port, ## arg); \
} while (0)
@@ -385,6 +495,13 @@ int tb_domain_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd);
int tb_domain_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd);
int tb_domain_disconnect_all_paths(struct tb *tb);
+static inline struct tb *tb_domain_get(struct tb *tb)
+{
+ if (tb)
+ get_device(&tb->dev);
+ return tb;
+}
+
static inline void tb_domain_put(struct tb *tb)
{
put_device(&tb->dev);
@@ -401,7 +518,6 @@ void tb_switch_suspend(struct tb_switch *sw);
int tb_switch_resume(struct tb_switch *sw);
int tb_switch_reset(struct tb *tb, u64 route);
void tb_sw_set_unplugged(struct tb_switch *sw);
-struct tb_switch *get_switch_at_route(struct tb_switch *sw, u64 route);
struct tb_switch *tb_switch_find_by_link_depth(struct tb *tb, u8 link,
u8 depth);
struct tb_switch *tb_switch_find_by_uuid(struct tb *tb, const uuid_t *uuid);
@@ -431,14 +547,74 @@ static inline struct tb_switch *tb_to_switch(struct device *dev)
return NULL;
}
+static inline struct tb_switch *tb_switch_parent(struct tb_switch *sw)
+{
+ return tb_to_switch(sw->dev.parent);
+}
+
+static inline bool tb_switch_is_lr(const struct tb_switch *sw)
+{
+ return sw->config.device_id == PCI_DEVICE_ID_INTEL_LIGHT_RIDGE;
+}
+
+static inline bool tb_switch_is_er(const struct tb_switch *sw)
+{
+ return sw->config.device_id == PCI_DEVICE_ID_INTEL_EAGLE_RIDGE;
+}
+
+static inline bool tb_switch_is_cr(const struct tb_switch *sw)
+{
+ switch (sw->config.device_id) {
+ case PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_2C:
+ case PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static inline bool tb_switch_is_fr(const struct tb_switch *sw)
+{
+ switch (sw->config.device_id) {
+ case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_BRIDGE:
+ case PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_BRIDGE:
+ return true;
+ default:
+ return false;
+ }
+}
+
int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged);
int tb_port_add_nfc_credits(struct tb_port *port, int credits);
+int tb_port_set_initial_credits(struct tb_port *port, u32 credits);
int tb_port_clear_counter(struct tb_port *port, int counter);
+int tb_port_alloc_in_hopid(struct tb_port *port, int hopid, int max_hopid);
+void tb_port_release_in_hopid(struct tb_port *port, int hopid);
+int tb_port_alloc_out_hopid(struct tb_port *port, int hopid, int max_hopid);
+void tb_port_release_out_hopid(struct tb_port *port, int hopid);
+struct tb_port *tb_next_port_on_path(struct tb_port *start, struct tb_port *end,
+ struct tb_port *prev);
int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec);
int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap);
-
-struct tb_path *tb_path_alloc(struct tb *tb, int num_hops);
+bool tb_port_is_enabled(struct tb_port *port);
+
+bool tb_pci_port_is_enabled(struct tb_port *port);
+int tb_pci_port_enable(struct tb_port *port, bool enable);
+
+int tb_dp_port_hpd_is_active(struct tb_port *port);
+int tb_dp_port_hpd_clear(struct tb_port *port);
+int tb_dp_port_set_hops(struct tb_port *port, unsigned int video,
+ unsigned int aux_tx, unsigned int aux_rx);
+bool tb_dp_port_is_enabled(struct tb_port *port);
+int tb_dp_port_enable(struct tb_port *port, bool enable);
+
+struct tb_path *tb_path_discover(struct tb_port *src, int src_hopid,
+ struct tb_port *dst, int dst_hopid,
+ struct tb_port **last, const char *name);
+struct tb_path *tb_path_alloc(struct tb *tb, struct tb_port *src, int src_hopid,
+ struct tb_port *dst, int dst_hopid, int link_nr,
+ const char *name);
void tb_path_free(struct tb_path *path);
int tb_path_activate(struct tb_path *path);
void tb_path_deactivate(struct tb_path *path);
@@ -447,17 +623,16 @@ bool tb_path_is_invalid(struct tb_path *path);
int tb_drom_read(struct tb_switch *sw);
int tb_drom_read_uid_only(struct tb_switch *sw, u64 *uid);
+int tb_lc_read_uuid(struct tb_switch *sw, u32 *uuid);
+int tb_lc_configure_link(struct tb_switch *sw);
+void tb_lc_unconfigure_link(struct tb_switch *sw);
+int tb_lc_set_sleep(struct tb_switch *sw);
static inline int tb_route_length(u64 route)
{
return (fls64(route) + TB_ROUTE_SHIFT - 1) / TB_ROUTE_SHIFT;
}
-static inline bool tb_is_upstream_port(struct tb_port *port)
-{
- return port == tb_upstream_port(port->sw);
-}
-
/**
* tb_downstream_route() - get route to downstream switch
*