1 // SPDX-License-Identifier: GPL-2.0
3 * Thunderbolt Cactus Ridge driver - path/tunnel functionality
5 * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com>
8 #include <linux/slab.h>
9 #include <linux/errno.h>
14 static void tb_dump_hop(struct tb_port
*port
, struct tb_regs_hop
*hop
)
16 tb_port_info(port
, " Hop through port %d to hop %d (%s)\n",
17 hop
->out_port
, hop
->next_hop
,
18 hop
->enable
? "enabled" : "disabled");
19 tb_port_info(port
, " Weight: %d Priority: %d Credits: %d Drop: %d\n",
20 hop
->weight
, hop
->priority
,
21 hop
->initial_credits
, hop
->drop_packages
);
22 tb_port_info(port
, " Counter enabled: %d Counter index: %d\n",
23 hop
->counter_enable
, hop
->counter
);
24 tb_port_info(port
, " Flow Control (In/Eg): %d/%d Shared Buffer (In/Eg): %d/%d\n",
25 hop
->ingress_fc
, hop
->egress_fc
,
26 hop
->ingress_shared_buffer
, hop
->egress_shared_buffer
);
27 tb_port_info(port
, " Unknown1: %#x Unknown2: %#x Unknown3: %#x\n",
28 hop
->unknown1
, hop
->unknown2
, hop
->unknown3
);
32 * tb_path_alloc() - allocate a thunderbolt path
34 * Return: Returns a tb_path on success or NULL on failure.
36 struct tb_path
*tb_path_alloc(struct tb
*tb
, int num_hops
)
38 struct tb_path
*path
= kzalloc(sizeof(*path
), GFP_KERNEL
);
41 path
->hops
= kcalloc(num_hops
, sizeof(*path
->hops
), GFP_KERNEL
);
47 path
->path_length
= num_hops
;
52 * tb_path_free() - free a deactivated path
54 void tb_path_free(struct tb_path
*path
)
56 if (path
->activated
) {
57 tb_WARN(path
->tb
, "trying to free an activated path\n")
64 static void __tb_path_deallocate_nfc(struct tb_path
*path
, int first_hop
)
67 for (i
= first_hop
; i
< path
->path_length
; i
++) {
68 res
= tb_port_add_nfc_credits(path
->hops
[i
].in_port
,
71 tb_port_warn(path
->hops
[i
].in_port
,
72 "nfc credits deallocation failed for hop %d\n",
77 static void __tb_path_deactivate_hops(struct tb_path
*path
, int first_hop
)
80 struct tb_regs_hop hop
= { };
81 for (i
= first_hop
; i
< path
->path_length
; i
++) {
82 res
= tb_port_write(path
->hops
[i
].in_port
, &hop
, TB_CFG_HOPS
,
83 2 * path
->hops
[i
].in_hop_index
, 2);
85 tb_port_warn(path
->hops
[i
].in_port
,
86 "hop deactivation failed for hop %d, index %d\n",
87 i
, path
->hops
[i
].in_hop_index
);
91 void tb_path_deactivate(struct tb_path
*path
)
93 if (!path
->activated
) {
94 tb_WARN(path
->tb
, "trying to deactivate an inactive path\n");
98 "deactivating path from %llx:%x to %llx:%x\n",
99 tb_route(path
->hops
[0].in_port
->sw
),
100 path
->hops
[0].in_port
->port
,
101 tb_route(path
->hops
[path
->path_length
- 1].out_port
->sw
),
102 path
->hops
[path
->path_length
- 1].out_port
->port
);
103 __tb_path_deactivate_hops(path
, 0);
104 __tb_path_deallocate_nfc(path
, 0);
105 path
->activated
= false;
109 * tb_path_activate() - activate a path
111 * Activate a path starting with the last hop and iterating backwards. The
112 * caller must fill path->hops before calling tb_path_activate().
114 * Return: Returns 0 on success or an error code on failure.
116 int tb_path_activate(struct tb_path
*path
)
119 enum tb_path_port out_mask
, in_mask
;
120 if (path
->activated
) {
121 tb_WARN(path
->tb
, "trying to activate already activated path\n");
126 "activating path from %llx:%x to %llx:%x\n",
127 tb_route(path
->hops
[0].in_port
->sw
),
128 path
->hops
[0].in_port
->port
,
129 tb_route(path
->hops
[path
->path_length
- 1].out_port
->sw
),
130 path
->hops
[path
->path_length
- 1].out_port
->port
);
132 /* Clear counters. */
133 for (i
= path
->path_length
- 1; i
>= 0; i
--) {
134 if (path
->hops
[i
].in_counter_index
== -1)
136 res
= tb_port_clear_counter(path
->hops
[i
].in_port
,
137 path
->hops
[i
].in_counter_index
);
142 /* Add non flow controlled credits. */
143 for (i
= path
->path_length
- 1; i
>= 0; i
--) {
144 res
= tb_port_add_nfc_credits(path
->hops
[i
].in_port
,
147 __tb_path_deallocate_nfc(path
, i
);
153 for (i
= path
->path_length
- 1; i
>= 0; i
--) {
154 struct tb_regs_hop hop
= { 0 };
157 * We do (currently) not tear down paths setup by the firmeware.
158 * If a firmware device is unplugged and plugged in again then
159 * it can happen that we reuse some of the hops from the (now
160 * defunct) firmeware path. This causes the hotplug operation to
161 * fail (the pci device does not show up). Clearing the hop
162 * before overwriting it fixes the problem.
164 * Should be removed once we discover and tear down firmeware
167 res
= tb_port_write(path
->hops
[i
].in_port
, &hop
, TB_CFG_HOPS
,
168 2 * path
->hops
[i
].in_hop_index
, 2);
170 __tb_path_deactivate_hops(path
, i
);
171 __tb_path_deallocate_nfc(path
, 0);
176 hop
.next_hop
= path
->hops
[i
].next_hop_index
;
177 hop
.out_port
= path
->hops
[i
].out_port
->port
;
178 /* TODO: figure out why these are good values */
179 hop
.initial_credits
= (i
== path
->path_length
- 1) ? 16 : 7;
184 out_mask
= (i
== path
->path_length
- 1) ?
185 TB_PATH_DESTINATION
: TB_PATH_INTERNAL
;
186 in_mask
= (i
== 0) ? TB_PATH_SOURCE
: TB_PATH_INTERNAL
;
187 hop
.weight
= path
->weight
;
189 hop
.priority
= path
->priority
;
190 hop
.drop_packages
= path
->drop_packages
;
191 hop
.counter
= path
->hops
[i
].in_counter_index
;
192 hop
.counter_enable
= path
->hops
[i
].in_counter_index
!= -1;
193 hop
.ingress_fc
= path
->ingress_fc_enable
& in_mask
;
194 hop
.egress_fc
= path
->egress_fc_enable
& out_mask
;
195 hop
.ingress_shared_buffer
= path
->ingress_shared_buffer
197 hop
.egress_shared_buffer
= path
->egress_shared_buffer
201 tb_port_info(path
->hops
[i
].in_port
, "Writing hop %d, index %d",
202 i
, path
->hops
[i
].in_hop_index
);
203 tb_dump_hop(path
->hops
[i
].in_port
, &hop
);
204 res
= tb_port_write(path
->hops
[i
].in_port
, &hop
, TB_CFG_HOPS
,
205 2 * path
->hops
[i
].in_hop_index
, 2);
207 __tb_path_deactivate_hops(path
, i
);
208 __tb_path_deallocate_nfc(path
, 0);
212 path
->activated
= true;
213 tb_info(path
->tb
, "path activation complete\n");
216 tb_WARN(path
->tb
, "path activation failed\n");
221 * tb_path_is_invalid() - check whether any ports on the path are invalid
223 * Return: Returns true if the path is invalid, false otherwise.
225 bool tb_path_is_invalid(struct tb_path
*path
)
228 for (i
= 0; i
< path
->path_length
; i
++) {
229 if (path
->hops
[i
].in_port
->sw
->is_unplugged
)
231 if (path
->hops
[i
].out_port
->sw
->is_unplugged
)