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
8static 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
22static 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
36static 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
52void 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 */
84static 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
114static 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 }
244skip_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
315int 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
334int 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

source code of linux/drivers/net/ethernet/intel/igc/igc_tsn.c