hc
2024-02-20 102a0743326a03cd1a1202ceda21e175b7d3575c
kernel/drivers/thunderbolt/cap.c
....@@ -1,8 +1,9 @@
11 // SPDX-License-Identifier: GPL-2.0
22 /*
3
- * Thunderbolt Cactus Ridge driver - capabilities lookup
3
+ * Thunderbolt driver - capabilities lookup
44 *
55 * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com>
6
+ * Copyright (C) 2018, Intel Corporation
67 */
78
89 #include <linux/slab.h>
....@@ -12,14 +13,99 @@
1213
1314 #define CAP_OFFSET_MAX 0xff
1415 #define VSE_CAP_OFFSET_MAX 0xffff
16
+#define TMU_ACCESS_EN BIT(20)
1517
16
-struct tb_cap_any {
17
- union {
18
- struct tb_cap_basic basic;
19
- struct tb_cap_extended_short extended_short;
20
- struct tb_cap_extended_long extended_long;
21
- };
22
-} __packed;
18
+static int tb_port_enable_tmu(struct tb_port *port, bool enable)
19
+{
20
+ struct tb_switch *sw = port->sw;
21
+ u32 value, offset;
22
+ int ret;
23
+
24
+ /*
25
+ * Legacy devices need to have TMU access enabled before port
26
+ * space can be fully accessed.
27
+ */
28
+ if (tb_switch_is_light_ridge(sw))
29
+ offset = 0x26;
30
+ else if (tb_switch_is_eagle_ridge(sw))
31
+ offset = 0x2a;
32
+ else
33
+ return 0;
34
+
35
+ ret = tb_sw_read(sw, &value, TB_CFG_SWITCH, offset, 1);
36
+ if (ret)
37
+ return ret;
38
+
39
+ if (enable)
40
+ value |= TMU_ACCESS_EN;
41
+ else
42
+ value &= ~TMU_ACCESS_EN;
43
+
44
+ return tb_sw_write(sw, &value, TB_CFG_SWITCH, offset, 1);
45
+}
46
+
47
+static void tb_port_dummy_read(struct tb_port *port)
48
+{
49
+ /*
50
+ * When reading from next capability pointer location in port
51
+ * config space the read data is not cleared on LR. To avoid
52
+ * reading stale data on next read perform one dummy read after
53
+ * port capabilities are walked.
54
+ */
55
+ if (tb_switch_is_light_ridge(port->sw)) {
56
+ u32 dummy;
57
+
58
+ tb_port_read(port, &dummy, TB_CFG_PORT, 0, 1);
59
+ }
60
+}
61
+
62
+/**
63
+ * tb_port_next_cap() - Return next capability in the linked list
64
+ * @port: Port to find the capability for
65
+ * @offset: Previous capability offset (%0 for start)
66
+ *
67
+ * Returns dword offset of the next capability in port config space
68
+ * capability list and returns it. Passing %0 returns the first entry in
69
+ * the capability list. If no next capability is found returns %0. In case
70
+ * of failure returns negative errno.
71
+ */
72
+int tb_port_next_cap(struct tb_port *port, unsigned int offset)
73
+{
74
+ struct tb_cap_any header;
75
+ int ret;
76
+
77
+ if (!offset)
78
+ return port->config.first_cap_offset;
79
+
80
+ ret = tb_port_read(port, &header, TB_CFG_PORT, offset, 1);
81
+ if (ret)
82
+ return ret;
83
+
84
+ return header.basic.next;
85
+}
86
+
87
+static int __tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap)
88
+{
89
+ int offset = 0;
90
+
91
+ do {
92
+ struct tb_cap_any header;
93
+ int ret;
94
+
95
+ offset = tb_port_next_cap(port, offset);
96
+ if (offset < 0)
97
+ return offset;
98
+
99
+ ret = tb_port_read(port, &header, TB_CFG_PORT, offset, 1);
100
+ if (ret)
101
+ return ret;
102
+
103
+ if (header.basic.cap == cap)
104
+ return offset;
105
+ } while (offset > 0);
106
+
107
+ return -ENOENT;
108
+}
23109
24110 /**
25111 * tb_port_find_cap() - Find port capability
....@@ -32,42 +118,84 @@
32118 */
33119 int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap)
34120 {
35
- u32 offset;
121
+ int ret;
36122
37
- /*
38
- * DP out adapters claim to implement TMU capability but in
39
- * reality they do not so we hard code the adapter specific
40
- * capability offset here.
41
- */
42
- if (port->config.type == TB_TYPE_DP_HDMI_OUT)
43
- offset = 0x39;
44
- else
45
- offset = 0x1;
123
+ ret = tb_port_enable_tmu(port, true);
124
+ if (ret)
125
+ return ret;
126
+
127
+ ret = __tb_port_find_cap(port, cap);
128
+
129
+ tb_port_dummy_read(port);
130
+ tb_port_enable_tmu(port, false);
131
+
132
+ return ret;
133
+}
134
+
135
+/**
136
+ * tb_switch_next_cap() - Return next capability in the linked list
137
+ * @sw: Switch to find the capability for
138
+ * @offset: Previous capability offset (%0 for start)
139
+ *
140
+ * Finds dword offset of the next capability in router config space
141
+ * capability list and returns it. Passing %0 returns the first entry in
142
+ * the capability list. If no next capability is found returns %0. In case
143
+ * of failure returns negative errno.
144
+ */
145
+int tb_switch_next_cap(struct tb_switch *sw, unsigned int offset)
146
+{
147
+ struct tb_cap_any header;
148
+ int ret;
149
+
150
+ if (!offset)
151
+ return sw->config.first_cap_offset;
152
+
153
+ ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 2);
154
+ if (ret)
155
+ return ret;
156
+
157
+ switch (header.basic.cap) {
158
+ case TB_SWITCH_CAP_TMU:
159
+ ret = header.basic.next;
160
+ break;
161
+
162
+ case TB_SWITCH_CAP_VSE:
163
+ if (!header.extended_short.length)
164
+ ret = header.extended_long.next;
165
+ else
166
+ ret = header.extended_short.next;
167
+ break;
168
+
169
+ default:
170
+ tb_sw_dbg(sw, "unknown capability %#x at %#x\n",
171
+ header.basic.cap, offset);
172
+ ret = -EINVAL;
173
+ break;
174
+ }
175
+
176
+ return ret >= VSE_CAP_OFFSET_MAX ? 0 : ret;
177
+}
178
+
179
+/**
180
+ * tb_switch_find_cap() - Find switch capability
181
+ * @sw Switch to find the capability for
182
+ * @cap: Capability to look
183
+ *
184
+ * Returns offset to start of capability or %-ENOENT if no such
185
+ * capability was found. Negative errno is returned if there was an
186
+ * error.
187
+ */
188
+int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap)
189
+{
190
+ int offset = 0;
46191
47192 do {
48193 struct tb_cap_any header;
49194 int ret;
50195
51
- ret = tb_port_read(port, &header, TB_CFG_PORT, offset, 1);
52
- if (ret)
53
- return ret;
54
-
55
- if (header.basic.cap == cap)
196
+ offset = tb_switch_next_cap(sw, offset);
197
+ if (offset < 0)
56198 return offset;
57
-
58
- offset = header.basic.next;
59
- } while (offset);
60
-
61
- return -ENOENT;
62
-}
63
-
64
-static int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap)
65
-{
66
- int offset = sw->config.first_cap_offset;
67
-
68
- while (offset > 0 && offset < CAP_OFFSET_MAX) {
69
- struct tb_cap_any header;
70
- int ret;
71199
72200 ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 1);
73201 if (ret)
....@@ -75,9 +203,7 @@
75203
76204 if (header.basic.cap == cap)
77205 return offset;
78
-
79
- offset = header.basic.next;
80
- }
206
+ } while (offset);
81207
82208 return -ENOENT;
83209 }
....@@ -94,37 +220,24 @@
94220 */
95221 int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec)
96222 {
97
- struct tb_cap_any header;
98
- int offset;
223
+ int offset = 0;
99224
100
- offset = tb_switch_find_cap(sw, TB_SWITCH_CAP_VSE);
101
- if (offset < 0)
102
- return offset;
103
-
104
- while (offset > 0 && offset < VSE_CAP_OFFSET_MAX) {
225
+ do {
226
+ struct tb_cap_any header;
105227 int ret;
106228
107
- ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 2);
229
+ offset = tb_switch_next_cap(sw, offset);
230
+ if (offset < 0)
231
+ return offset;
232
+
233
+ ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 1);
108234 if (ret)
109235 return ret;
110236
111
- /*
112
- * Extended vendor specific capabilities come in two
113
- * flavors: short and long. The latter is used when
114
- * offset is over 0xff.
115
- */
116
- if (offset >= CAP_OFFSET_MAX) {
117
- if (header.extended_long.vsec_id == vsec)
118
- return offset;
119
- offset = header.extended_long.next;
120
- } else {
121
- if (header.extended_short.vsec_id == vsec)
122
- return offset;
123
- if (!header.extended_short.length)
124
- return -ENOENT;
125
- offset = header.extended_short.next;
126
- }
127
- }
237
+ if (header.extended_short.cap == TB_SWITCH_CAP_VSE &&
238
+ header.extended_short.vsec_id == vsec)
239
+ return offset;
240
+ } while (offset);
128241
129242 return -ENOENT;
130243 }