xref: /linux/drivers/net/ethernet/intel/igc/igc_tsn.c (revision dfd5e53dd72113f37663f59a6337fe9a0dfbf0f6)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c)  2019 Intel Corporation */
3 
4 #include "igc.h"
5 #include "igc_tsn.h"
6 
7 static bool is_any_launchtime(struct igc_adapter *adapter)
8 {
9 	int i;
10 
11 	for (i = 0; i < adapter->num_tx_queues; i++) {
12 		struct igc_ring *ring = adapter->tx_ring[i];
13 
14 		if (ring->launchtime_enable)
15 			return true;
16 	}
17 
18 	return false;
19 }
20 
21 static bool is_cbs_enabled(struct igc_adapter *adapter)
22 {
23 	int i;
24 
25 	for (i = 0; i < adapter->num_tx_queues; i++) {
26 		struct igc_ring *ring = adapter->tx_ring[i];
27 
28 		if (ring->cbs_enable)
29 			return true;
30 	}
31 
32 	return false;
33 }
34 
35 static unsigned int igc_tsn_new_flags(struct igc_adapter *adapter)
36 {
37 	unsigned int new_flags = adapter->flags & ~IGC_FLAG_TSN_ANY_ENABLED;
38 
39 	if (adapter->base_time)
40 		new_flags |= IGC_FLAG_TSN_QBV_ENABLED;
41 
42 	if (is_any_launchtime(adapter))
43 		new_flags |= IGC_FLAG_TSN_QBV_ENABLED;
44 
45 	if (is_cbs_enabled(adapter))
46 		new_flags |= IGC_FLAG_TSN_QAV_ENABLED;
47 
48 	return new_flags;
49 }
50 
51 void igc_tsn_adjust_txtime_offset(struct igc_adapter *adapter)
52 {
53 	struct igc_hw *hw = &adapter->hw;
54 	u16 txoffset;
55 
56 	if (!is_any_launchtime(adapter))
57 		return;
58 
59 	switch (adapter->link_speed) {
60 	case SPEED_10:
61 		txoffset = IGC_TXOFFSET_SPEED_10;
62 		break;
63 	case SPEED_100:
64 		txoffset = IGC_TXOFFSET_SPEED_100;
65 		break;
66 	case SPEED_1000:
67 		txoffset = IGC_TXOFFSET_SPEED_1000;
68 		break;
69 	case SPEED_2500:
70 		txoffset = IGC_TXOFFSET_SPEED_2500;
71 		break;
72 	default:
73 		txoffset = 0;
74 		break;
75 	}
76 
77 	wr32(IGC_GTXOFFSET, txoffset);
78 }
79 
80 /* Returns the TSN specific registers to their default values after
81  * the adapter is reset.
82  */
83 static int igc_tsn_disable_offload(struct igc_adapter *adapter)
84 {
85 	struct igc_hw *hw = &adapter->hw;
86 	u32 tqavctrl;
87 	int i;
88 
89 	wr32(IGC_GTXOFFSET, 0);
90 	wr32(IGC_TXPBS, I225_TXPBSIZE_DEFAULT);
91 	wr32(IGC_DTXMXPKTSZ, IGC_DTXMXPKTSZ_DEFAULT);
92 
93 	tqavctrl = rd32(IGC_TQAVCTRL);
94 	tqavctrl &= ~(IGC_TQAVCTRL_TRANSMIT_MODE_TSN |
95 		      IGC_TQAVCTRL_ENHANCED_QAV);
96 	wr32(IGC_TQAVCTRL, tqavctrl);
97 
98 	for (i = 0; i < adapter->num_tx_queues; i++) {
99 		wr32(IGC_TXQCTL(i), 0);
100 		wr32(IGC_STQT(i), 0);
101 		wr32(IGC_ENDQT(i), NSEC_PER_SEC);
102 	}
103 
104 	wr32(IGC_QBVCYCLET_S, 0);
105 	wr32(IGC_QBVCYCLET, NSEC_PER_SEC);
106 
107 	adapter->flags &= ~IGC_FLAG_TSN_QBV_ENABLED;
108 
109 	return 0;
110 }
111 
112 static int igc_tsn_enable_offload(struct igc_adapter *adapter)
113 {
114 	struct igc_hw *hw = &adapter->hw;
115 	u32 tqavctrl, baset_l, baset_h;
116 	u32 sec, nsec, cycle;
117 	ktime_t base_time, systim;
118 	int i;
119 
120 	cycle = adapter->cycle_time;
121 	base_time = adapter->base_time;
122 
123 	wr32(IGC_TSAUXC, 0);
124 	wr32(IGC_DTXMXPKTSZ, IGC_DTXMXPKTSZ_TSN);
125 	wr32(IGC_TXPBS, IGC_TXPBSIZE_TSN);
126 
127 	tqavctrl = rd32(IGC_TQAVCTRL);
128 	tqavctrl |= IGC_TQAVCTRL_TRANSMIT_MODE_TSN | IGC_TQAVCTRL_ENHANCED_QAV;
129 	wr32(IGC_TQAVCTRL, tqavctrl);
130 
131 	wr32(IGC_QBVCYCLET_S, cycle);
132 	wr32(IGC_QBVCYCLET, cycle);
133 
134 	for (i = 0; i < adapter->num_tx_queues; i++) {
135 		struct igc_ring *ring = adapter->tx_ring[i];
136 		u32 txqctl = 0;
137 		u16 cbs_value;
138 		u32 tqavcc;
139 
140 		wr32(IGC_STQT(i), ring->start_time);
141 		wr32(IGC_ENDQT(i), ring->end_time);
142 
143 		if (adapter->base_time) {
144 			/* If we have a base_time we are in "taprio"
145 			 * mode and we need to be strict about the
146 			 * cycles: only transmit a packet if it can be
147 			 * completed during that cycle.
148 			 */
149 			txqctl |= IGC_TXQCTL_STRICT_CYCLE |
150 				IGC_TXQCTL_STRICT_END;
151 		}
152 
153 		if (ring->launchtime_enable)
154 			txqctl |= IGC_TXQCTL_QUEUE_MODE_LAUNCHT;
155 
156 		/* Skip configuring CBS for Q2 and Q3 */
157 		if (i > 1)
158 			goto skip_cbs;
159 
160 		if (ring->cbs_enable) {
161 			if (i == 0)
162 				txqctl |= IGC_TXQCTL_QAV_SEL_CBS0;
163 			else
164 				txqctl |= IGC_TXQCTL_QAV_SEL_CBS1;
165 
166 			/* According to i225 datasheet section 7.5.2.7, we
167 			 * should set the 'idleSlope' field from TQAVCC
168 			 * register following the equation:
169 			 *
170 			 * value = link-speed   0x7736 * BW * 0.2
171 			 *         ---------- *  -----------------         (E1)
172 			 *          100Mbps            2.5
173 			 *
174 			 * Note that 'link-speed' is in Mbps.
175 			 *
176 			 * 'BW' is the percentage bandwidth out of full
177 			 * link speed which can be found with the
178 			 * following equation. Note that idleSlope here
179 			 * is the parameter from this function
180 			 * which is in kbps.
181 			 *
182 			 *     BW =     idleSlope
183 			 *          -----------------                      (E2)
184 			 *          link-speed * 1000
185 			 *
186 			 * That said, we can come up with a generic
187 			 * equation to calculate the value we should set
188 			 * it TQAVCC register by replacing 'BW' in E1 by E2.
189 			 * The resulting equation is:
190 			 *
191 			 * value = link-speed * 0x7736 * idleSlope * 0.2
192 			 *         -------------------------------------   (E3)
193 			 *             100 * 2.5 * link-speed * 1000
194 			 *
195 			 * 'link-speed' is present in both sides of the
196 			 * fraction so it is canceled out. The final
197 			 * equation is the following:
198 			 *
199 			 *     value = idleSlope * 61036
200 			 *             -----------------                   (E4)
201 			 *                  2500000
202 			 *
203 			 * NOTE: For i225, given the above, we can see
204 			 *       that idleslope is represented in
205 			 *       40.959433 kbps units by the value at
206 			 *       the TQAVCC register (2.5Gbps / 61036),
207 			 *       which reduces the granularity for
208 			 *       idleslope increments.
209 			 *
210 			 * In i225 controller, the sendSlope and loCredit
211 			 * parameters from CBS are not configurable
212 			 * by software so we don't do any
213 			 * 'controller configuration' in respect to
214 			 * these parameters.
215 			 */
216 			cbs_value = DIV_ROUND_UP_ULL(ring->idleslope
217 						     * 61036ULL, 2500000);
218 
219 			tqavcc = rd32(IGC_TQAVCC(i));
220 			tqavcc &= ~IGC_TQAVCC_IDLESLOPE_MASK;
221 			tqavcc |= cbs_value | IGC_TQAVCC_KEEP_CREDITS;
222 			wr32(IGC_TQAVCC(i), tqavcc);
223 
224 			wr32(IGC_TQAVHC(i),
225 			     0x80000000 + ring->hicredit * 0x7735);
226 		} else {
227 			/* Disable any CBS for the queue */
228 			txqctl &= ~(IGC_TXQCTL_QAV_SEL_MASK);
229 
230 			/* Set idleSlope to zero. */
231 			tqavcc = rd32(IGC_TQAVCC(i));
232 			tqavcc &= ~(IGC_TQAVCC_IDLESLOPE_MASK |
233 				    IGC_TQAVCC_KEEP_CREDITS);
234 			wr32(IGC_TQAVCC(i), tqavcc);
235 
236 			/* Set hiCredit to zero. */
237 			wr32(IGC_TQAVHC(i), 0);
238 		}
239 skip_cbs:
240 		wr32(IGC_TXQCTL(i), txqctl);
241 	}
242 
243 	nsec = rd32(IGC_SYSTIML);
244 	sec = rd32(IGC_SYSTIMH);
245 
246 	systim = ktime_set(sec, nsec);
247 
248 	if (ktime_compare(systim, base_time) > 0) {
249 		s64 n;
250 
251 		n = div64_s64(ktime_sub_ns(systim, base_time), cycle);
252 		base_time = ktime_add_ns(base_time, (n + 1) * cycle);
253 	}
254 
255 	baset_h = div_s64_rem(base_time, NSEC_PER_SEC, &baset_l);
256 
257 	wr32(IGC_BASET_H, baset_h);
258 	wr32(IGC_BASET_L, baset_l);
259 
260 	return 0;
261 }
262 
263 int igc_tsn_reset(struct igc_adapter *adapter)
264 {
265 	unsigned int new_flags;
266 	int err = 0;
267 
268 	new_flags = igc_tsn_new_flags(adapter);
269 
270 	if (!(new_flags & IGC_FLAG_TSN_ANY_ENABLED))
271 		return igc_tsn_disable_offload(adapter);
272 
273 	err = igc_tsn_enable_offload(adapter);
274 	if (err < 0)
275 		return err;
276 
277 	adapter->flags = new_flags;
278 
279 	return err;
280 }
281 
282 int igc_tsn_offload_apply(struct igc_adapter *adapter)
283 {
284 	int err;
285 
286 	if (netif_running(adapter->netdev)) {
287 		schedule_work(&adapter->reset_task);
288 		return 0;
289 	}
290 
291 	err = igc_tsn_enable_offload(adapter);
292 	if (err < 0)
293 		return err;
294 
295 	adapter->flags = igc_tsn_new_flags(adapter);
296 	return 0;
297 }
298