2 * Thunderbolt Cactus Ridge driver - path/tunnel functionality
4 * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com>
7 #include <linux/slab.h>
8 #include <linux/errno.h>
13 static void tb_dump_hop(struct tb_port
*port
, struct tb_regs_hop
*hop
)
15 tb_port_info(port
, " Hop through port %d to hop %d (%s)\n",
16 hop
->out_port
, hop
->next_hop
,
17 hop
->enable
? "enabled" : "disabled");
18 tb_port_info(port
, " Weight: %d Priority: %d Credits: %d Drop: %d\n",
19 hop
->weight
, hop
->priority
,
20 hop
->initial_credits
, hop
->drop_packages
);
21 tb_port_info(port
, " Counter enabled: %d Counter index: %d\n",
22 hop
->counter_enable
, hop
->counter
);
23 tb_port_info(port
, " Flow Control (In/Eg): %d/%d Shared Buffer (In/Eg): %d/%d\n",
24 hop
->ingress_fc
, hop
->egress_fc
,
25 hop
->ingress_shared_buffer
, hop
->egress_shared_buffer
);
26 tb_port_info(port
, " Unknown1: %#x Unknown2: %#x Unknown3: %#x\n",
27 hop
->unknown1
, hop
->unknown2
, hop
->unknown3
);
31 * tb_path_alloc() - allocate a thunderbolt path
33 * Return: Returns a tb_path on success or NULL on failure.
35 struct tb_path
*tb_path_alloc(struct tb
*tb
, int num_hops
)
37 struct tb_path
*path
= kzalloc(sizeof(*path
), GFP_KERNEL
);
40 path
->hops
= kcalloc(num_hops
, sizeof(*path
->hops
), GFP_KERNEL
);
46 path
->path_length
= num_hops
;
51 * tb_path_free() - free a deactivated path
53 void tb_path_free(struct tb_path
*path
)
55 if (path
->activated
) {
56 tb_WARN(path
->tb
, "trying to free an activated path\n")
63 static void __tb_path_deallocate_nfc(struct tb_path
*path
, int first_hop
)
66 for (i
= first_hop
; i
< path
->path_length
; i
++) {
67 res
= tb_port_add_nfc_credits(path
->hops
[i
].in_port
,
70 tb_port_warn(path
->hops
[i
].in_port
,
71 "nfc credits deallocation failed for hop %d\n",
76 static void __tb_path_deactivate_hops(struct tb_path
*path
, int first_hop
)
79 struct tb_regs_hop hop
= { };
80 for (i
= first_hop
; i
< path
->path_length
; i
++) {
81 res
= tb_port_write(path
->hops
[i
].in_port
, &hop
, TB_CFG_HOPS
,
82 2 * path
->hops
[i
].in_hop_index
, 2);
84 tb_port_warn(path
->hops
[i
].in_port
,
85 "hop deactivation failed for hop %d, index %d\n",
86 i
, path
->hops
[i
].in_hop_index
);
90 void tb_path_deactivate(struct tb_path
*path
)
92 if (!path
->activated
) {
93 tb_WARN(path
->tb
, "trying to deactivate an inactive path\n");
97 "deactivating path from %llx:%x to %llx:%x\n",
98 tb_route(path
->hops
[0].in_port
->sw
),
99 path
->hops
[0].in_port
->port
,
100 tb_route(path
->hops
[path
->path_length
- 1].out_port
->sw
),
101 path
->hops
[path
->path_length
- 1].out_port
->port
);
102 __tb_path_deactivate_hops(path
, 0);
103 __tb_path_deallocate_nfc(path
, 0);
104 path
->activated
= false;
108 * tb_path_activate() - activate a path
110 * Activate a path starting with the last hop and iterating backwards. The
111 * caller must fill path->hops before calling tb_path_activate().
113 * Return: Returns 0 on success or an error code on failure.
115 int tb_path_activate(struct tb_path
*path
)
118 enum tb_path_port out_mask
, in_mask
;
119 if (path
->activated
) {
120 tb_WARN(path
->tb
, "trying to activate already activated path\n");
125 "activating path from %llx:%x to %llx:%x\n",
126 tb_route(path
->hops
[0].in_port
->sw
),
127 path
->hops
[0].in_port
->port
,
128 tb_route(path
->hops
[path
->path_length
- 1].out_port
->sw
),
129 path
->hops
[path
->path_length
- 1].out_port
->port
);
131 /* Clear counters. */
132 for (i
= path
->path_length
- 1; i
>= 0; i
--) {
133 if (path
->hops
[i
].in_counter_index
== -1)
135 res
= tb_port_clear_counter(path
->hops
[i
].in_port
,
136 path
->hops
[i
].in_counter_index
);
141 /* Add non flow controlled credits. */
142 for (i
= path
->path_length
- 1; i
>= 0; i
--) {
143 res
= tb_port_add_nfc_credits(path
->hops
[i
].in_port
,
146 __tb_path_deallocate_nfc(path
, i
);
152 for (i
= path
->path_length
- 1; i
>= 0; i
--) {
153 struct tb_regs_hop hop
= { 0 };
156 * We do (currently) not tear down paths setup by the firmeware.
157 * If a firmware device is unplugged and plugged in again then
158 * it can happen that we reuse some of the hops from the (now
159 * defunct) firmeware path. This causes the hotplug operation to
160 * fail (the pci device does not show up). Clearing the hop
161 * before overwriting it fixes the problem.
163 * Should be removed once we discover and tear down firmeware
166 res
= tb_port_write(path
->hops
[i
].in_port
, &hop
, TB_CFG_HOPS
,
167 2 * path
->hops
[i
].in_hop_index
, 2);
169 __tb_path_deactivate_hops(path
, i
);
170 __tb_path_deallocate_nfc(path
, 0);
175 hop
.next_hop
= path
->hops
[i
].next_hop_index
;
176 hop
.out_port
= path
->hops
[i
].out_port
->port
;
177 /* TODO: figure out why these are good values */
178 hop
.initial_credits
= (i
== path
->path_length
- 1) ? 16 : 7;
183 out_mask
= (i
== path
->path_length
- 1) ?
184 TB_PATH_DESTINATION
: TB_PATH_INTERNAL
;
185 in_mask
= (i
== 0) ? TB_PATH_SOURCE
: TB_PATH_INTERNAL
;
186 hop
.weight
= path
->weight
;
188 hop
.priority
= path
->priority
;
189 hop
.drop_packages
= path
->drop_packages
;
190 hop
.counter
= path
->hops
[i
].in_counter_index
;
191 hop
.counter_enable
= path
->hops
[i
].in_counter_index
!= -1;
192 hop
.ingress_fc
= path
->ingress_fc_enable
& in_mask
;
193 hop
.egress_fc
= path
->egress_fc_enable
& out_mask
;
194 hop
.ingress_shared_buffer
= path
->ingress_shared_buffer
196 hop
.egress_shared_buffer
= path
->egress_shared_buffer
200 tb_port_info(path
->hops
[i
].in_port
, "Writing hop %d, index %d",
201 i
, path
->hops
[i
].in_hop_index
);
202 tb_dump_hop(path
->hops
[i
].in_port
, &hop
);
203 res
= tb_port_write(path
->hops
[i
].in_port
, &hop
, TB_CFG_HOPS
,
204 2 * path
->hops
[i
].in_hop_index
, 2);
206 __tb_path_deactivate_hops(path
, i
);
207 __tb_path_deallocate_nfc(path
, 0);
211 path
->activated
= true;
212 tb_info(path
->tb
, "path activation complete\n");
215 tb_WARN(path
->tb
, "path activation failed\n");
220 * tb_path_is_invalid() - check whether any ports on the path are invalid
222 * Return: Returns true if the path is invalid, false otherwise.
224 bool tb_path_is_invalid(struct tb_path
*path
)
227 for (i
= 0; i
< path
->path_length
; i
++) {
228 if (path
->hops
[i
].in_port
->sw
->is_unplugged
)
230 if (path
->hops
[i
].out_port
->sw
->is_unplugged
)