summaryrefslogtreecommitdiffstats
path: root/drivers/thunderbolt
diff options
context:
space:
mode:
authorMika Westerberg <mika.westerberg@linux.intel.com>2017-06-06 08:24:58 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-06-09 05:42:41 -0400
commitda2da04b8d4476a411feb2a12b47792aebbc142f (patch)
tree12e044a26c451f832b3c2f394301b1e526717be3 /drivers/thunderbolt
parent046bee1f9ab83b4549c185804ae9cbfbb8f9641f (diff)
thunderbolt: Rework capability handling
Organization of the capabilities in switches and ports is not so random after all. Rework the capability handling functionality so that it follows how capabilities are organized and provide two new functions (tb_switch_find_vse_cap() and tb_port_find_cap()) which can be used to extract capabilities for ports and switches. Then convert the current users over these. Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com> Reviewed-by: Yehezkel Bernat <yehezkel.bernat@intel.com> Reviewed-by: Michael Jamet <michael.jamet@intel.com> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Signed-off-by: Andreas Noever <andreas.noever@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/thunderbolt')
-rw-r--r--drivers/thunderbolt/cap.c169
-rw-r--r--drivers/thunderbolt/switch.c6
-rw-r--r--drivers/thunderbolt/tb.c8
-rw-r--r--drivers/thunderbolt/tb.h3
-rw-r--r--drivers/thunderbolt/tb_regs.h50
-rw-r--r--drivers/thunderbolt/tunnel_pci.c8
6 files changed, 142 insertions, 102 deletions
diff --git a/drivers/thunderbolt/cap.c b/drivers/thunderbolt/cap.c
index a7b47e7cddbd..38bc27a5ce4f 100644
--- a/drivers/thunderbolt/cap.c
+++ b/drivers/thunderbolt/cap.c
@@ -9,6 +9,8 @@
9 9
10#include "tb.h" 10#include "tb.h"
11 11
12#define CAP_OFFSET_MAX 0xff
13#define VSE_CAP_OFFSET_MAX 0xffff
12 14
13struct tb_cap_any { 15struct tb_cap_any {
14 union { 16 union {
@@ -18,99 +20,110 @@ struct tb_cap_any {
18 }; 20 };
19} __packed; 21} __packed;
20 22
21static bool tb_cap_is_basic(struct tb_cap_any *cap) 23/**
22{ 24 * tb_port_find_cap() - Find port capability
23 /* basic.cap is u8. This checks only the lower 8 bit of cap. */ 25 * @port: Port to find the capability for
24 return cap->basic.cap != 5; 26 * @cap: Capability to look
25} 27 *
26 28 * Returns offset to start of capability or %-ENOENT if no such
27static bool tb_cap_is_long(struct tb_cap_any *cap) 29 * capability was found. Negative errno is returned if there was an
30 * error.
31 */
32int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap)
28{ 33{
29 return !tb_cap_is_basic(cap) 34 u32 offset;
30 && cap->extended_short.next == 0
31 && cap->extended_short.length == 0;
32}
33 35
34static enum tb_cap tb_cap(struct tb_cap_any *cap) 36 /*
35{ 37 * DP out adapters claim to implement TMU capability but in
36 if (tb_cap_is_basic(cap)) 38 * reality they do not so we hard code the adapter specific
37 return cap->basic.cap; 39 * capability offset here.
40 */
41 if (port->config.type == TB_TYPE_DP_HDMI_OUT)
42 offset = 0x39;
38 else 43 else
39 /* extended_short/long have cap at the same offset. */ 44 offset = 0x1;
40 return cap->extended_short.cap; 45
46 do {
47 struct tb_cap_any header;
48 int ret;
49
50 ret = tb_port_read(port, &header, TB_CFG_PORT, offset, 1);
51 if (ret)
52 return ret;
53
54 if (header.basic.cap == cap)
55 return offset;
56
57 offset = header.basic.next;
58 } while (offset);
59
60 return -ENOENT;
41} 61}
42 62
43static u32 tb_cap_next(struct tb_cap_any *cap, u32 offset) 63static int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap)
44{ 64{
45 int next; 65 int offset = sw->config.first_cap_offset;
46 if (offset == 1) { 66
47 /* 67 while (offset > 0 && offset < CAP_OFFSET_MAX) {
48 * The first pointer is part of the switch header and always 68 struct tb_cap_any header;
49 * a simple pointer. 69 int ret;
50 */ 70
51 next = cap->basic.next; 71 ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 1);
52 } else { 72 if (ret)
53 /* 73 return ret;
54 * Somehow Intel decided to use 3 different types of capability 74
55 * headers. It is not like anyone could have predicted that 75 if (header.basic.cap == cap)
56 * single byte offsets are not enough... 76 return offset;
57 */ 77
58 if (tb_cap_is_basic(cap)) 78 offset = header.basic.next;
59 next = cap->basic.next;
60 else if (!tb_cap_is_long(cap))
61 next = cap->extended_short.next;
62 else
63 next = cap->extended_long.next;
64 } 79 }
65 /* 80
66 * "Hey, we could terminate some capability lists with a null offset 81 return -ENOENT;
67 * and others with a pointer to the last element." - "Great idea!"
68 */
69 if (next == offset)
70 return 0;
71 return next;
72} 82}
73 83
74/** 84/**
75 * tb_find_cap() - find a capability 85 * tb_switch_find_vse_cap() - Find switch vendor specific capability
86 * @sw: Switch to find the capability for
87 * @vsec: Vendor specific capability to look
76 * 88 *
77 * Return: Returns a positive offset if the capability was found and 0 if not. 89 * Functions enumerates vendor specific capabilities (VSEC) of a switch
78 * Returns an error code on failure. 90 * and returns offset when capability matching @vsec is found. If no
91 * such capability is found returns %-ENOENT. In case of error returns
92 * negative errno.
79 */ 93 */
80int tb_find_cap(struct tb_port *port, enum tb_cfg_space space, enum tb_cap cap) 94int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec)
81{ 95{
82 u32 offset = 1;
83 struct tb_cap_any header; 96 struct tb_cap_any header;
84 int res; 97 int offset;
85 int retries = 10; 98
86 while (retries--) { 99 offset = tb_switch_find_cap(sw, TB_SWITCH_CAP_VSE);
87 res = tb_port_read(port, &header, space, offset, 1); 100 if (offset < 0)
88 if (res) { 101 return offset;
89 /* Intel needs some help with linked lists. */ 102
90 if (space == TB_CFG_PORT && offset == 0xa 103 while (offset > 0 && offset < VSE_CAP_OFFSET_MAX) {
91 && port->config.type == TB_TYPE_DP_HDMI_OUT) { 104 int ret;
92 offset = 0x39; 105
93 continue; 106 ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 2);
94 } 107 if (ret)
95 return res; 108 return ret;
96 } 109
97 if (offset != 1) { 110 /*
98 if (tb_cap(&header) == cap) 111 * Extended vendor specific capabilities come in two
112 * flavors: short and long. The latter is used when
113 * offset is over 0xff.
114 */
115 if (offset >= CAP_OFFSET_MAX) {
116 if (header.extended_long.vsec_id == vsec)
99 return offset; 117 return offset;
100 if (tb_cap_is_long(&header)) { 118 offset = header.extended_long.next;
101 /* tb_cap_extended_long is 2 dwords */ 119 } else {
102 res = tb_port_read(port, &header, space, 120 if (header.extended_short.vsec_id == vsec)
103 offset, 2); 121 return offset;
104 if (res) 122 if (!header.extended_short.length)
105 return res; 123 return -ENOENT;
106 } 124 offset = header.extended_short.next;
107 } 125 }
108 offset = tb_cap_next(&header, offset);
109 if (!offset)
110 return 0;
111 } 126 }
112 tb_port_WARN(port, 127
113 "run out of retries while looking for cap %#x in config space %d, last offset: %#x\n", 128 return -ENOENT;
114 cap, space, offset);
115 return -EIO;
116} 129}
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 81f5164a6364..b379b4183bac 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -192,7 +192,7 @@ static int tb_init_port(struct tb_port *port)
192 192
193 /* Port 0 is the switch itself and has no PHY. */ 193 /* Port 0 is the switch itself and has no PHY. */
194 if (port->config.type == TB_TYPE_PORT && port->port != 0) { 194 if (port->config.type == TB_TYPE_PORT && port->port != 0) {
195 cap = tb_find_cap(port, TB_CFG_PORT, TB_CAP_PHY); 195 cap = tb_port_find_cap(port, TB_PORT_CAP_PHY);
196 196
197 if (cap > 0) 197 if (cap > 0)
198 port->cap_phy = cap; 198 port->cap_phy = cap;
@@ -394,9 +394,9 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route)
394 sw->ports[i].port = i; 394 sw->ports[i].port = i;
395 } 395 }
396 396
397 cap = tb_find_cap(&sw->ports[0], TB_CFG_SWITCH, TB_CAP_PLUG_EVENTS); 397 cap = tb_switch_find_vse_cap(sw, TB_VSE_CAP_PLUG_EVENTS);
398 if (cap < 0) { 398 if (cap < 0) {
399 tb_sw_warn(sw, "cannot find TB_CAP_PLUG_EVENTS aborting\n"); 399 tb_sw_warn(sw, "cannot find TB_VSE_CAP_PLUG_EVENTS aborting\n");
400 goto err; 400 goto err;
401 } 401 }
402 sw->cap_plug_events = cap; 402 sw->cap_plug_events = cap;
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 24b6d30c3c86..6b44076e1380 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -121,8 +121,8 @@ static struct tb_port *tb_find_unused_down_port(struct tb_switch *sw)
121 continue; 121 continue;
122 if (sw->ports[i].config.type != TB_TYPE_PCIE_DOWN) 122 if (sw->ports[i].config.type != TB_TYPE_PCIE_DOWN)
123 continue; 123 continue;
124 cap = tb_find_cap(&sw->ports[i], TB_CFG_PORT, TB_CAP_PCIE); 124 cap = tb_port_find_cap(&sw->ports[i], TB_PORT_CAP_ADAP);
125 if (cap <= 0) 125 if (cap < 0)
126 continue; 126 continue;
127 res = tb_port_read(&sw->ports[i], &data, TB_CFG_PORT, cap, 1); 127 res = tb_port_read(&sw->ports[i], &data, TB_CFG_PORT, cap, 1);
128 if (res < 0) 128 if (res < 0)
@@ -165,8 +165,8 @@ static void tb_activate_pcie_devices(struct tb *tb)
165 } 165 }
166 166
167 /* check whether port is already activated */ 167 /* check whether port is already activated */
168 cap = tb_find_cap(up_port, TB_CFG_PORT, TB_CAP_PCIE); 168 cap = tb_port_find_cap(up_port, TB_PORT_CAP_ADAP);
169 if (cap <= 0) 169 if (cap < 0)
170 continue; 170 continue;
171 if (tb_port_read(up_port, &data, TB_CFG_PORT, cap, 1)) 171 if (tb_port_read(up_port, &data, TB_CFG_PORT, cap, 1))
172 continue; 172 continue;
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index ba2b85750335..0b78bc4fbe61 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -233,7 +233,8 @@ int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged);
233int tb_port_add_nfc_credits(struct tb_port *port, int credits); 233int tb_port_add_nfc_credits(struct tb_port *port, int credits);
234int tb_port_clear_counter(struct tb_port *port, int counter); 234int tb_port_clear_counter(struct tb_port *port, int counter);
235 235
236int tb_find_cap(struct tb_port *port, enum tb_cfg_space space, enum tb_cap cap); 236int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec);
237int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap);
237 238
238struct tb_path *tb_path_alloc(struct tb *tb, int num_hops); 239struct tb_path *tb_path_alloc(struct tb *tb, int num_hops);
239void tb_path_free(struct tb_path *path); 240void tb_path_free(struct tb_path *path);
diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h
index 1e2a4a8046be..582bd1f156dc 100644
--- a/drivers/thunderbolt/tb_regs.h
+++ b/drivers/thunderbolt/tb_regs.h
@@ -23,15 +23,22 @@
23 */ 23 */
24#define TB_MAX_CONFIG_RW_LENGTH 60 24#define TB_MAX_CONFIG_RW_LENGTH 60
25 25
26enum tb_cap { 26enum tb_switch_cap {
27 TB_CAP_PHY = 0x0001, 27 TB_SWITCH_CAP_VSE = 0x05,
28 TB_CAP_TIME1 = 0x0003, 28};
29 TB_CAP_PCIE = 0x0004, 29
30 TB_CAP_I2C = 0x0005, 30enum tb_switch_vse_cap {
31 TB_CAP_PLUG_EVENTS = 0x0105, /* also EEPROM */ 31 TB_VSE_CAP_PLUG_EVENTS = 0x01, /* also EEPROM */
32 TB_CAP_TIME2 = 0x0305, 32 TB_VSE_CAP_TIME2 = 0x03,
33 TB_CAP_IECS = 0x0405, 33 TB_VSE_CAP_IECS = 0x04,
34 TB_CAP_LINK_CONTROLLER = 0x0605, /* also IECS */ 34 TB_VSE_CAP_LINK_CONTROLLER = 0x06, /* also IECS */
35};
36
37enum tb_port_cap {
38 TB_PORT_CAP_PHY = 0x01,
39 TB_PORT_CAP_TIME1 = 0x03,
40 TB_PORT_CAP_ADAP = 0x04,
41 TB_PORT_CAP_VSE = 0x05,
35}; 42};
36 43
37enum tb_port_state { 44enum tb_port_state {
@@ -49,15 +56,34 @@ struct tb_cap_basic {
49 u8 cap; /* if cap == 0x05 then we have a extended capability */ 56 u8 cap; /* if cap == 0x05 then we have a extended capability */
50} __packed; 57} __packed;
51 58
59/**
60 * struct tb_cap_extended_short - Switch extended short capability
61 * @next: Pointer to the next capability. If @next and @length are zero
62 * then we have a long cap.
63 * @cap: Base capability ID (see &enum tb_switch_cap)
64 * @vsec_id: Vendor specific capability ID (see &enum switch_vse_cap)
65 * @length: Length of this capability
66 */
52struct tb_cap_extended_short { 67struct tb_cap_extended_short {
53 u8 next; /* if next and length are zero then we have a long cap */ 68 u8 next;
54 enum tb_cap cap:16; 69 u8 cap;
70 u8 vsec_id;
55 u8 length; 71 u8 length;
56} __packed; 72} __packed;
57 73
74/**
75 * struct tb_cap_extended_long - Switch extended long capability
76 * @zero1: This field should be zero
77 * @cap: Base capability ID (see &enum tb_switch_cap)
78 * @vsec_id: Vendor specific capability ID (see &enum switch_vse_cap)
79 * @zero2: This field should be zero
80 * @next: Pointer to the next capability
81 * @length: Length of this capability
82 */
58struct tb_cap_extended_long { 83struct tb_cap_extended_long {
59 u8 zero1; 84 u8 zero1;
60 enum tb_cap cap:16; 85 u8 cap;
86 u8 vsec_id;
61 u8 zero2; 87 u8 zero2;
62 u16 next; 88 u16 next;
63 u16 length; 89 u16 length;
diff --git a/drivers/thunderbolt/tunnel_pci.c b/drivers/thunderbolt/tunnel_pci.c
index baf1cd370446..f4ce9845e42a 100644
--- a/drivers/thunderbolt/tunnel_pci.c
+++ b/drivers/thunderbolt/tunnel_pci.c
@@ -147,10 +147,10 @@ bool tb_pci_is_invalid(struct tb_pci_tunnel *tunnel)
147static int tb_pci_port_active(struct tb_port *port, bool active) 147static int tb_pci_port_active(struct tb_port *port, bool active)
148{ 148{
149 u32 word = active ? 0x80000000 : 0x0; 149 u32 word = active ? 0x80000000 : 0x0;
150 int cap = tb_find_cap(port, TB_CFG_PORT, TB_CAP_PCIE); 150 int cap = tb_port_find_cap(port, TB_PORT_CAP_ADAP);
151 if (cap <= 0) { 151 if (cap < 0) {
152 tb_port_warn(port, "TB_CAP_PCIE not found: %d\n", cap); 152 tb_port_warn(port, "TB_PORT_CAP_ADAP not found: %d\n", cap);
153 return cap ? cap : -ENXIO; 153 return cap;
154 } 154 }
155 return tb_port_write(port, &word, TB_CFG_PORT, cap, 1); 155 return tb_port_write(port, &word, TB_CFG_PORT, cap, 1);
156} 156}