1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (c) 2019 Intel Corporation */ |
3 | |
4 | #include "igc.h" |
5 | #include "igc_hw.h" |
6 | #include "igc_tsn.h" |
7 | |
8 | static bool is_any_launchtime(struct igc_adapter *adapter) |
9 | { |
10 | int i; |
11 | |
12 | for (i = 0; i < adapter->num_tx_queues; i++) { |
13 | struct igc_ring *ring = adapter->tx_ring[i]; |
14 | |
15 | if (ring->launchtime_enable) |
16 | return true; |
17 | } |
18 | |
19 | return false; |
20 | } |
21 | |
22 | static bool is_cbs_enabled(struct igc_adapter *adapter) |
23 | { |
24 | int i; |
25 | |
26 | for (i = 0; i < adapter->num_tx_queues; i++) { |
27 | struct igc_ring *ring = adapter->tx_ring[i]; |
28 | |
29 | if (ring->cbs_enable) |
30 | return true; |
31 | } |
32 | |
33 | return false; |
34 | } |
35 | |
36 | static unsigned int igc_tsn_new_flags(struct igc_adapter *adapter) |
37 | { |
38 | unsigned int new_flags = adapter->flags & ~IGC_FLAG_TSN_ANY_ENABLED; |
39 | |
40 | if (adapter->taprio_offload_enable) |
41 | new_flags |= IGC_FLAG_TSN_QBV_ENABLED; |
42 | |
43 | if (is_any_launchtime(adapter)) |
44 | new_flags |= IGC_FLAG_TSN_QBV_ENABLED; |
45 | |
46 | if (is_cbs_enabled(adapter)) |
47 | new_flags |= IGC_FLAG_TSN_QAV_ENABLED; |
48 | |
49 | return new_flags; |
50 | } |
51 | |
52 | void igc_tsn_adjust_txtime_offset(struct igc_adapter *adapter) |
53 | { |
54 | struct igc_hw *hw = &adapter->hw; |
55 | u16 txoffset; |
56 | |
57 | if (!is_any_launchtime(adapter)) |
58 | return; |
59 | |
60 | switch (adapter->link_speed) { |
61 | case SPEED_10: |
62 | txoffset = IGC_TXOFFSET_SPEED_10; |
63 | break; |
64 | case SPEED_100: |
65 | txoffset = IGC_TXOFFSET_SPEED_100; |
66 | break; |
67 | case SPEED_1000: |
68 | txoffset = IGC_TXOFFSET_SPEED_1000; |
69 | break; |
70 | case SPEED_2500: |
71 | txoffset = IGC_TXOFFSET_SPEED_2500; |
72 | break; |
73 | default: |
74 | txoffset = 0; |
75 | break; |
76 | } |
77 | |
78 | wr32(IGC_GTXOFFSET, txoffset); |
79 | } |
80 | |
81 | /* Returns the TSN specific registers to their default values after |
82 | * the adapter is reset. |
83 | */ |
84 | static int igc_tsn_disable_offload(struct igc_adapter *adapter) |
85 | { |
86 | struct igc_hw *hw = &adapter->hw; |
87 | u32 tqavctrl; |
88 | int i; |
89 | |
90 | wr32(IGC_GTXOFFSET, 0); |
91 | wr32(IGC_TXPBS, I225_TXPBSIZE_DEFAULT); |
92 | wr32(IGC_DTXMXPKTSZ, IGC_DTXMXPKTSZ_DEFAULT); |
93 | |
94 | tqavctrl = rd32(IGC_TQAVCTRL); |
95 | tqavctrl &= ~(IGC_TQAVCTRL_TRANSMIT_MODE_TSN | |
96 | IGC_TQAVCTRL_ENHANCED_QAV | IGC_TQAVCTRL_FUTSCDDIS); |
97 | |
98 | wr32(IGC_TQAVCTRL, tqavctrl); |
99 | |
100 | for (i = 0; i < adapter->num_tx_queues; i++) { |
101 | wr32(IGC_TXQCTL(i), 0); |
102 | wr32(IGC_STQT(i), 0); |
103 | wr32(IGC_ENDQT(i), NSEC_PER_SEC); |
104 | } |
105 | |
106 | wr32(IGC_QBVCYCLET_S, 0); |
107 | wr32(IGC_QBVCYCLET, NSEC_PER_SEC); |
108 | |
109 | adapter->flags &= ~IGC_FLAG_TSN_QBV_ENABLED; |
110 | |
111 | return 0; |
112 | } |
113 | |
114 | static int igc_tsn_enable_offload(struct igc_adapter *adapter) |
115 | { |
116 | struct igc_hw *hw = &adapter->hw; |
117 | u32 tqavctrl, baset_l, baset_h; |
118 | u32 sec, nsec, cycle; |
119 | ktime_t base_time, systim; |
120 | int i; |
121 | |
122 | wr32(IGC_TSAUXC, 0); |
123 | wr32(IGC_DTXMXPKTSZ, IGC_DTXMXPKTSZ_TSN); |
124 | wr32(IGC_TXPBS, IGC_TXPBSIZE_TSN); |
125 | |
126 | for (i = 0; i < adapter->num_tx_queues; i++) { |
127 | struct igc_ring *ring = adapter->tx_ring[i]; |
128 | u32 txqctl = 0; |
129 | u16 cbs_value; |
130 | u32 tqavcc; |
131 | |
132 | wr32(IGC_STQT(i), ring->start_time); |
133 | wr32(IGC_ENDQT(i), ring->end_time); |
134 | |
135 | if (adapter->taprio_offload_enable) { |
136 | /* If taprio_offload_enable is set we are in "taprio" |
137 | * mode and we need to be strict about the |
138 | * cycles: only transmit a packet if it can be |
139 | * completed during that cycle. |
140 | * |
141 | * If taprio_offload_enable is NOT true when |
142 | * enabling TSN offload, the cycle should have |
143 | * no external effects, but is only used internally |
144 | * to adapt the base time register after a second |
145 | * has passed. |
146 | * |
147 | * Enabling strict mode in this case would |
148 | * unnecessarily prevent the transmission of |
149 | * certain packets (i.e. at the boundary of a |
150 | * second) and thus interfere with the launchtime |
151 | * feature that promises transmission at a |
152 | * certain point in time. |
153 | */ |
154 | txqctl |= IGC_TXQCTL_STRICT_CYCLE | |
155 | IGC_TXQCTL_STRICT_END; |
156 | } |
157 | |
158 | if (ring->launchtime_enable) |
159 | txqctl |= IGC_TXQCTL_QUEUE_MODE_LAUNCHT; |
160 | |
161 | /* Skip configuring CBS for Q2 and Q3 */ |
162 | if (i > 1) |
163 | goto skip_cbs; |
164 | |
165 | if (ring->cbs_enable) { |
166 | if (i == 0) |
167 | txqctl |= IGC_TXQCTL_QAV_SEL_CBS0; |
168 | else |
169 | txqctl |= IGC_TXQCTL_QAV_SEL_CBS1; |
170 | |
171 | /* According to i225 datasheet section 7.5.2.7, we |
172 | * should set the 'idleSlope' field from TQAVCC |
173 | * register following the equation: |
174 | * |
175 | * value = link-speed 0x7736 * BW * 0.2 |
176 | * ---------- * ----------------- (E1) |
177 | * 100Mbps 2.5 |
178 | * |
179 | * Note that 'link-speed' is in Mbps. |
180 | * |
181 | * 'BW' is the percentage bandwidth out of full |
182 | * link speed which can be found with the |
183 | * following equation. Note that idleSlope here |
184 | * is the parameter from this function |
185 | * which is in kbps. |
186 | * |
187 | * BW = idleSlope |
188 | * ----------------- (E2) |
189 | * link-speed * 1000 |
190 | * |
191 | * That said, we can come up with a generic |
192 | * equation to calculate the value we should set |
193 | * it TQAVCC register by replacing 'BW' in E1 by E2. |
194 | * The resulting equation is: |
195 | * |
196 | * value = link-speed * 0x7736 * idleSlope * 0.2 |
197 | * ------------------------------------- (E3) |
198 | * 100 * 2.5 * link-speed * 1000 |
199 | * |
200 | * 'link-speed' is present in both sides of the |
201 | * fraction so it is canceled out. The final |
202 | * equation is the following: |
203 | * |
204 | * value = idleSlope * 61036 |
205 | * ----------------- (E4) |
206 | * 2500000 |
207 | * |
208 | * NOTE: For i225, given the above, we can see |
209 | * that idleslope is represented in |
210 | * 40.959433 kbps units by the value at |
211 | * the TQAVCC register (2.5Gbps / 61036), |
212 | * which reduces the granularity for |
213 | * idleslope increments. |
214 | * |
215 | * In i225 controller, the sendSlope and loCredit |
216 | * parameters from CBS are not configurable |
217 | * by software so we don't do any |
218 | * 'controller configuration' in respect to |
219 | * these parameters. |
220 | */ |
221 | cbs_value = DIV_ROUND_UP_ULL(ring->idleslope |
222 | * 61036ULL, 2500000); |
223 | |
224 | tqavcc = rd32(IGC_TQAVCC(i)); |
225 | tqavcc &= ~IGC_TQAVCC_IDLESLOPE_MASK; |
226 | tqavcc |= cbs_value | IGC_TQAVCC_KEEP_CREDITS; |
227 | wr32(IGC_TQAVCC(i), tqavcc); |
228 | |
229 | wr32(IGC_TQAVHC(i), |
230 | 0x80000000 + ring->hicredit * 0x7735); |
231 | } else { |
232 | /* Disable any CBS for the queue */ |
233 | txqctl &= ~(IGC_TXQCTL_QAV_SEL_MASK); |
234 | |
235 | /* Set idleSlope to zero. */ |
236 | tqavcc = rd32(IGC_TQAVCC(i)); |
237 | tqavcc &= ~(IGC_TQAVCC_IDLESLOPE_MASK | |
238 | IGC_TQAVCC_KEEP_CREDITS); |
239 | wr32(IGC_TQAVCC(i), tqavcc); |
240 | |
241 | /* Set hiCredit to zero. */ |
242 | wr32(IGC_TQAVHC(i), 0); |
243 | } |
244 | skip_cbs: |
245 | wr32(IGC_TXQCTL(i), txqctl); |
246 | } |
247 | |
248 | tqavctrl = rd32(IGC_TQAVCTRL) & ~IGC_TQAVCTRL_FUTSCDDIS; |
249 | |
250 | tqavctrl |= IGC_TQAVCTRL_TRANSMIT_MODE_TSN | IGC_TQAVCTRL_ENHANCED_QAV; |
251 | |
252 | adapter->qbv_count++; |
253 | |
254 | cycle = adapter->cycle_time; |
255 | base_time = adapter->base_time; |
256 | |
257 | nsec = rd32(IGC_SYSTIML); |
258 | sec = rd32(IGC_SYSTIMH); |
259 | |
260 | systim = ktime_set(secs: sec, nsecs: nsec); |
261 | if (ktime_compare(cmp1: systim, cmp2: base_time) > 0) { |
262 | s64 n = div64_s64(ktime_sub_ns(systim, base_time), divisor: cycle); |
263 | |
264 | base_time = ktime_add_ns(base_time, (n + 1) * cycle); |
265 | |
266 | /* Increase the counter if scheduling into the past while |
267 | * Gate Control List (GCL) is running. |
268 | */ |
269 | if ((rd32(IGC_BASET_H) || rd32(IGC_BASET_L)) && |
270 | (adapter->tc_setup_type == TC_SETUP_QDISC_TAPRIO) && |
271 | (adapter->qbv_count > 1)) |
272 | adapter->qbv_config_change_errors++; |
273 | } else { |
274 | if (igc_is_device_id_i226(hw)) { |
275 | ktime_t adjust_time, expires_time; |
276 | |
277 | /* According to datasheet section 7.5.2.9.3.3, FutScdDis bit |
278 | * has to be configured before the cycle time and base time. |
279 | * Tx won't hang if a GCL is already running, |
280 | * so in this case we don't need to set FutScdDis. |
281 | */ |
282 | if (!(rd32(IGC_BASET_H) || rd32(IGC_BASET_L))) |
283 | tqavctrl |= IGC_TQAVCTRL_FUTSCDDIS; |
284 | |
285 | nsec = rd32(IGC_SYSTIML); |
286 | sec = rd32(IGC_SYSTIMH); |
287 | systim = ktime_set(secs: sec, nsecs: nsec); |
288 | |
289 | adjust_time = adapter->base_time; |
290 | expires_time = ktime_sub_ns(adjust_time, systim); |
291 | hrtimer_start(timer: &adapter->hrtimer, tim: expires_time, mode: HRTIMER_MODE_REL); |
292 | } |
293 | } |
294 | |
295 | wr32(IGC_TQAVCTRL, tqavctrl); |
296 | |
297 | wr32(IGC_QBVCYCLET_S, cycle); |
298 | wr32(IGC_QBVCYCLET, cycle); |
299 | |
300 | baset_h = div_s64_rem(dividend: base_time, NSEC_PER_SEC, remainder: &baset_l); |
301 | wr32(IGC_BASET_H, baset_h); |
302 | |
303 | /* In i226, Future base time is only supported when FutScdDis bit |
304 | * is enabled and only active for re-configuration. |
305 | * In this case, initialize the base time with zero to create |
306 | * "re-configuration" scenario then only set the desired base time. |
307 | */ |
308 | if (tqavctrl & IGC_TQAVCTRL_FUTSCDDIS) |
309 | wr32(IGC_BASET_L, 0); |
310 | wr32(IGC_BASET_L, baset_l); |
311 | |
312 | return 0; |
313 | } |
314 | |
315 | int igc_tsn_reset(struct igc_adapter *adapter) |
316 | { |
317 | unsigned int new_flags; |
318 | int err = 0; |
319 | |
320 | new_flags = igc_tsn_new_flags(adapter); |
321 | |
322 | if (!(new_flags & IGC_FLAG_TSN_ANY_ENABLED)) |
323 | return igc_tsn_disable_offload(adapter); |
324 | |
325 | err = igc_tsn_enable_offload(adapter); |
326 | if (err < 0) |
327 | return err; |
328 | |
329 | adapter->flags = new_flags; |
330 | |
331 | return err; |
332 | } |
333 | |
334 | int igc_tsn_offload_apply(struct igc_adapter *adapter) |
335 | { |
336 | struct igc_hw *hw = &adapter->hw; |
337 | |
338 | /* Per I225/6 HW Design Section 7.5.2.1, transmit mode |
339 | * cannot be changed dynamically. Require reset the adapter. |
340 | */ |
341 | if (netif_running(dev: adapter->netdev) && |
342 | (igc_is_device_id_i225(hw) || !adapter->qbv_count)) { |
343 | schedule_work(work: &adapter->reset_task); |
344 | return 0; |
345 | } |
346 | |
347 | igc_tsn_reset(adapter); |
348 | |
349 | return 0; |
350 | } |
351 | |