1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2019, Vladimir Oltean <olteanv@gmail.com>
6 #define SJA1105_TAS_CLKSRC_DISABLED 0
7 #define SJA1105_TAS_CLKSRC_STANDALONE 1
8 #define SJA1105_TAS_CLKSRC_AS6802 2
9 #define SJA1105_TAS_CLKSRC_PTP 3
10 #define SJA1105_TAS_MAX_DELTA BIT(19)
11 #define SJA1105_GATE_MASK GENMASK_ULL(SJA1105_NUM_TC - 1, 0)
13 /* This is not a preprocessor macro because the "ns" argument may or may not be
14 * s64 at caller side. This ensures it is properly type-cast before div_s64.
16 static s64
ns_to_sja1105_delta(s64 ns
)
18 return div_s64(ns
, 200);
21 /* Lo and behold: the egress scheduler from hell.
23 * At the hardware level, the Time-Aware Shaper holds a global linear arrray of
24 * all schedule entries for all ports. These are the Gate Control List (GCL)
25 * entries, let's call them "timeslots" for short. This linear array of
26 * timeslots is held in BLK_IDX_SCHEDULE.
28 * Then there are a maximum of 8 "execution threads" inside the switch, which
29 * iterate cyclically through the "schedule". Each "cycle" has an entry point
30 * and an exit point, both being timeslot indices in the schedule table. The
31 * hardware calls each cycle a "subschedule".
33 * Subschedule (cycle) i starts when
34 * ptpclkval >= ptpschtm + BLK_IDX_SCHEDULE_ENTRY_POINTS[i].delta.
36 * The hardware scheduler iterates BLK_IDX_SCHEDULE with a k ranging from
37 * k = BLK_IDX_SCHEDULE_ENTRY_POINTS[i].address to
38 * k = BLK_IDX_SCHEDULE_PARAMS.subscheind[i]
40 * For each schedule entry (timeslot) k, the engine executes the gate control
41 * list entry for the duration of BLK_IDX_SCHEDULE[k].delta.
44 * | | BLK_IDX_SCHEDULE_ENTRY_POINTS_PARAMS
49 * BLK_IDX_SCHEDULE_ENTRY_POINTS v
54 * +----------------+ | | +-------------------------------------+
55 * | .subschindx | | .subschindx |
56 * | | +---------------+ |
57 * | .address | .address | |
60 * | BLK_IDX_SCHEDULE v v |
61 * | +-------+-------+-------+-------+-------+------+ |
62 * | |entry 0|entry 1|entry 2|entry 3|entry 4|entry5| |
63 * | +-------+-------+-------+-------+-------+------+ |
66 * | +-------------------------+ | | | |
67 * | | +-------------------------------+ | | |
68 * | | | +-------------------+ | |
70 * | +---------------------------------------------------------------+ |
71 * | |subscheind[0]<=subscheind[1]<=subscheind[2]<=...<=subscheind[7]| |
72 * | +---------------------------------------------------------------+ |
73 * | ^ ^ BLK_IDX_SCHEDULE_PARAMS |
75 * +--------+ +-------------------------------------------+
77 * In the above picture there are two subschedules (cycles):
79 * - cycle 0: iterates the schedule table from 0 to 2 (and back)
80 * - cycle 1: iterates the schedule table from 3 to 5 (and back)
82 * All other possible execution threads must be marked as unused by making
83 * their "subschedule end index" (subscheind) equal to the last valid
84 * subschedule's end index (in this case 5).
86 static int sja1105_init_scheduling(struct sja1105_private
*priv
)
88 struct sja1105_schedule_entry_points_entry
*schedule_entry_points
;
89 struct sja1105_schedule_entry_points_params_entry
90 *schedule_entry_points_params
;
91 struct sja1105_schedule_params_entry
*schedule_params
;
92 struct sja1105_tas_data
*tas_data
= &priv
->tas_data
;
93 struct sja1105_schedule_entry
*schedule
;
94 struct sja1105_table
*table
;
95 int schedule_start_idx
;
96 s64 entry_point_delta
;
104 /* Discard previous Schedule Table */
105 table
= &priv
->static_config
.tables
[BLK_IDX_SCHEDULE
];
106 if (table
->entry_count
) {
107 kfree(table
->entries
);
108 table
->entry_count
= 0;
111 /* Discard previous Schedule Entry Points Parameters Table */
112 table
= &priv
->static_config
.tables
[BLK_IDX_SCHEDULE_ENTRY_POINTS_PARAMS
];
113 if (table
->entry_count
) {
114 kfree(table
->entries
);
115 table
->entry_count
= 0;
118 /* Discard previous Schedule Parameters Table */
119 table
= &priv
->static_config
.tables
[BLK_IDX_SCHEDULE_PARAMS
];
120 if (table
->entry_count
) {
121 kfree(table
->entries
);
122 table
->entry_count
= 0;
125 /* Discard previous Schedule Entry Points Table */
126 table
= &priv
->static_config
.tables
[BLK_IDX_SCHEDULE_ENTRY_POINTS
];
127 if (table
->entry_count
) {
128 kfree(table
->entries
);
129 table
->entry_count
= 0;
132 /* Figure out the dimensioning of the problem */
133 for (port
= 0; port
< SJA1105_NUM_PORTS
; port
++) {
134 if (tas_data
->offload
[port
]) {
135 num_entries
+= tas_data
->offload
[port
]->num_entries
;
144 /* Pre-allocate space in the static config tables */
147 table
= &priv
->static_config
.tables
[BLK_IDX_SCHEDULE
];
148 table
->entries
= kcalloc(num_entries
, table
->ops
->unpacked_entry_size
,
152 table
->entry_count
= num_entries
;
153 schedule
= table
->entries
;
155 /* Schedule Points Parameters Table */
156 table
= &priv
->static_config
.tables
[BLK_IDX_SCHEDULE_ENTRY_POINTS_PARAMS
];
157 table
->entries
= kcalloc(SJA1105_MAX_SCHEDULE_ENTRY_POINTS_PARAMS_COUNT
,
158 table
->ops
->unpacked_entry_size
, GFP_KERNEL
);
160 /* Previously allocated memory will be freed automatically in
161 * sja1105_static_config_free. This is true for all early
165 table
->entry_count
= SJA1105_MAX_SCHEDULE_ENTRY_POINTS_PARAMS_COUNT
;
166 schedule_entry_points_params
= table
->entries
;
168 /* Schedule Parameters Table */
169 table
= &priv
->static_config
.tables
[BLK_IDX_SCHEDULE_PARAMS
];
170 table
->entries
= kcalloc(SJA1105_MAX_SCHEDULE_PARAMS_COUNT
,
171 table
->ops
->unpacked_entry_size
, GFP_KERNEL
);
174 table
->entry_count
= SJA1105_MAX_SCHEDULE_PARAMS_COUNT
;
175 schedule_params
= table
->entries
;
177 /* Schedule Entry Points Table */
178 table
= &priv
->static_config
.tables
[BLK_IDX_SCHEDULE_ENTRY_POINTS
];
179 table
->entries
= kcalloc(num_cycles
, table
->ops
->unpacked_entry_size
,
183 table
->entry_count
= num_cycles
;
184 schedule_entry_points
= table
->entries
;
186 /* Finally start populating the static config tables */
187 schedule_entry_points_params
->clksrc
= SJA1105_TAS_CLKSRC_STANDALONE
;
188 schedule_entry_points_params
->actsubsch
= num_cycles
- 1;
190 for (port
= 0; port
< SJA1105_NUM_PORTS
; port
++) {
191 const struct tc_taprio_qopt_offload
*offload
;
193 offload
= tas_data
->offload
[port
];
197 schedule_start_idx
= k
;
198 schedule_end_idx
= k
+ offload
->num_entries
- 1;
199 /* TODO this is the base time for the port's subschedule,
200 * relative to PTPSCHTM. But as we're using the standalone
201 * clock source and not PTP clock as time reference, there's
202 * little point in even trying to put more logic into this,
203 * like preserving the phases between the subschedules of
204 * different ports. We'll get all of that when switching to the
207 entry_point_delta
= 1;
209 schedule_entry_points
[cycle
].subschindx
= cycle
;
210 schedule_entry_points
[cycle
].delta
= entry_point_delta
;
211 schedule_entry_points
[cycle
].address
= schedule_start_idx
;
213 /* The subschedule end indices need to be
214 * monotonically increasing.
216 for (i
= cycle
; i
< 8; i
++)
217 schedule_params
->subscheind
[i
] = schedule_end_idx
;
219 for (i
= 0; i
< offload
->num_entries
; i
++, k
++) {
220 s64 delta_ns
= offload
->entries
[i
].interval
;
222 schedule
[k
].delta
= ns_to_sja1105_delta(delta_ns
);
223 schedule
[k
].destports
= BIT(port
);
224 schedule
[k
].resmedia_en
= true;
225 schedule
[k
].resmedia
= SJA1105_GATE_MASK
&
226 ~offload
->entries
[i
].gate_mask
;
234 /* Be there 2 port subschedules, each executing an arbitrary number of gate
235 * open/close events cyclically.
236 * None of those gate events must ever occur at the exact same time, otherwise
237 * the switch is known to act in exotically strange ways.
238 * However the hardware doesn't bother performing these integrity checks.
239 * So here we are with the task of validating whether the new @admin offload
240 * has any conflict with the already established TAS configuration in
241 * tas_data->offload. We already know the other ports are in harmony with one
242 * another, otherwise we wouldn't have saved them.
243 * Each gate event executes periodically, with a period of @cycle_time and a
244 * phase given by its cycle's @base_time plus its offset within the cycle
245 * (which in turn is given by the length of the events prior to it).
246 * There are two aspects to possible collisions:
247 * - Collisions within one cycle's (actually the longest cycle's) time frame.
248 * For that, we need to compare the cartesian product of each possible
249 * occurrence of each event within one cycle time.
250 * - Collisions in the future. Events may not collide within one cycle time,
251 * but if two port schedules don't have the same periodicity (aka the cycle
252 * times aren't multiples of one another), they surely will some time in the
253 * future (actually they will collide an infinite amount of times).
256 sja1105_tas_check_conflicts(struct sja1105_private
*priv
, int port
,
257 const struct tc_taprio_qopt_offload
*admin
)
259 struct sja1105_tas_data
*tas_data
= &priv
->tas_data
;
260 const struct tc_taprio_qopt_offload
*offload
;
261 s64 max_cycle_time
, min_cycle_time
;
269 offload
= tas_data
->offload
[port
];
273 /* Check if the two cycle times are multiples of one another.
274 * If they aren't, then they will surely collide.
276 max_cycle_time
= max(offload
->cycle_time
, admin
->cycle_time
);
277 min_cycle_time
= min(offload
->cycle_time
, admin
->cycle_time
);
278 div_s64_rem(max_cycle_time
, min_cycle_time
, &rem
);
282 /* Calculate the "reduced" base time of each of the two cycles
283 * (transposed back as close to 0 as possible) by dividing to
286 div_s64_rem(offload
->base_time
, offload
->cycle_time
, &rem
);
289 div_s64_rem(admin
->base_time
, admin
->cycle_time
, &rem
);
292 stop_time
= max_cycle_time
+ max(rbt1
, rbt2
);
294 /* delta1 is the relative base time of each GCL entry within
295 * the established ports' TAS config.
297 for (i
= 0, delta1
= 0;
298 i
< offload
->num_entries
;
299 delta1
+= offload
->entries
[i
].interval
, i
++) {
300 /* delta2 is the relative base time of each GCL entry
301 * within the newly added TAS config.
303 for (j
= 0, delta2
= 0;
304 j
< admin
->num_entries
;
305 delta2
+= admin
->entries
[j
].interval
, j
++) {
306 /* t1 follows all possible occurrences of the
307 * established ports' GCL entry i within the
310 for (t1
= rbt1
+ delta1
;
312 t1
+= offload
->cycle_time
) {
313 /* t2 follows all possible occurrences
314 * of the newly added GCL entry j
315 * within the first cycle time.
317 for (t2
= rbt2
+ delta2
;
319 t2
+= admin
->cycle_time
) {
321 dev_warn(priv
->ds
->dev
,
322 "GCL entry %d collides with entry %d of port %d\n",
334 int sja1105_setup_tc_taprio(struct dsa_switch
*ds
, int port
,
335 struct tc_taprio_qopt_offload
*admin
)
337 struct sja1105_private
*priv
= ds
->priv
;
338 struct sja1105_tas_data
*tas_data
= &priv
->tas_data
;
339 int other_port
, rc
, i
;
341 /* Can't change an already configured port (must delete qdisc first).
342 * Can't delete the qdisc from an unconfigured port.
344 if (!!tas_data
->offload
[port
] == admin
->enable
)
347 if (!admin
->enable
) {
348 taprio_offload_free(tas_data
->offload
[port
]);
349 tas_data
->offload
[port
] = NULL
;
351 rc
= sja1105_init_scheduling(priv
);
355 return sja1105_static_config_reload(priv
);
358 /* The cycle time extension is the amount of time the last cycle from
359 * the old OPER needs to be extended in order to phase-align with the
360 * base time of the ADMIN when that becomes the new OPER.
361 * But of course our switch needs to be reset to switch-over between
362 * the ADMIN and the OPER configs - so much for a seamless transition.
363 * So don't add insult over injury and just say we don't support cycle
366 if (admin
->cycle_time_extension
)
369 if (!ns_to_sja1105_delta(admin
->base_time
)) {
370 dev_err(ds
->dev
, "A base time of zero is not hardware-allowed\n");
374 for (i
= 0; i
< admin
->num_entries
; i
++) {
375 s64 delta_ns
= admin
->entries
[i
].interval
;
376 s64 delta_cycles
= ns_to_sja1105_delta(delta_ns
);
377 bool too_long
, too_short
;
379 too_long
= (delta_cycles
>= SJA1105_TAS_MAX_DELTA
);
380 too_short
= (delta_cycles
== 0);
381 if (too_long
|| too_short
) {
382 dev_err(priv
->ds
->dev
,
383 "Interval %llu too %s for GCL entry %d\n",
384 delta_ns
, too_long
? "long" : "short", i
);
389 for (other_port
= 0; other_port
< SJA1105_NUM_PORTS
; other_port
++) {
390 if (other_port
== port
)
393 if (sja1105_tas_check_conflicts(priv
, other_port
, admin
))
397 tas_data
->offload
[port
] = taprio_offload_get(admin
);
399 rc
= sja1105_init_scheduling(priv
);
403 return sja1105_static_config_reload(priv
);
406 void sja1105_tas_setup(struct dsa_switch
*ds
)
410 void sja1105_tas_teardown(struct dsa_switch
*ds
)
412 struct sja1105_private
*priv
= ds
->priv
;
413 struct tc_taprio_qopt_offload
*offload
;
416 for (port
= 0; port
< SJA1105_NUM_PORTS
; port
++) {
417 offload
= priv
->tas_data
.offload
[port
];
421 taprio_offload_free(offload
);