Merge tag 'linux-kselftest-next-5.15-rc1' of git://git.kernel.org/pub/scm/linux/kerne...
[linux-2.6-microblaze.git] / drivers / thunderbolt / cap.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Thunderbolt driver - capabilities lookup
4  *
5  * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com>
6  * Copyright (C) 2018, Intel Corporation
7  */
8
9 #include <linux/slab.h>
10 #include <linux/errno.h>
11
12 #include "tb.h"
13
14 #define CAP_OFFSET_MAX          0xff
15 #define VSE_CAP_OFFSET_MAX      0xffff
16 #define TMU_ACCESS_EN           BIT(20)
17
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 }
109
110 /**
111  * tb_port_find_cap() - Find port capability
112  * @port: Port to find the capability for
113  * @cap: Capability to look
114  *
115  * Returns offset to start of capability or %-ENOENT if no such
116  * capability was found. Negative errno is returned if there was an
117  * error.
118  */
119 int tb_port_find_cap(struct tb_port *port, enum tb_port_cap cap)
120 {
121         int ret;
122
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;
191
192         do {
193                 struct tb_cap_any header;
194                 int ret;
195
196                 offset = tb_switch_next_cap(sw, offset);
197                 if (offset < 0)
198                         return offset;
199
200                 ret = tb_sw_read(sw, &header, TB_CFG_SWITCH, offset, 1);
201                 if (ret)
202                         return ret;
203
204                 if (header.basic.cap == cap)
205                         return offset;
206         } while (offset);
207
208         return -ENOENT;
209 }
210
211 /**
212  * tb_switch_find_vse_cap() - Find switch vendor specific capability
213  * @sw: Switch to find the capability for
214  * @vsec: Vendor specific capability to look
215  *
216  * Functions enumerates vendor specific capabilities (VSEC) of a switch
217  * and returns offset when capability matching @vsec is found. If no
218  * such capability is found returns %-ENOENT. In case of error returns
219  * negative errno.
220  */
221 int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec)
222 {
223         int offset = 0;
224
225         do {
226                 struct tb_cap_any header;
227                 int ret;
228
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);
234                 if (ret)
235                         return ret;
236
237                 if (header.extended_short.cap == TB_SWITCH_CAP_VSE &&
238                     header.extended_short.vsec_id == vsec)
239                         return offset;
240         } while (offset);
241
242         return -ENOENT;
243 }