1
2
3
4#include "sja1105.h"
5
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)
12
13
14
15
16static s64 ns_to_sja1105_delta(s64 ns)
17{
18 return div_s64(ns, 200);
19}
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86static int sja1105_init_scheduling(struct sja1105_private *priv)
87{
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;
97 int schedule_end_idx;
98 int num_entries = 0;
99 int num_cycles = 0;
100 int cycle = 0;
101 int i, k = 0;
102 int port;
103
104
105 table = &priv->static_config.tables[BLK_IDX_SCHEDULE];
106 if (table->entry_count) {
107 kfree(table->entries);
108 table->entry_count = 0;
109 }
110
111
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;
116 }
117
118
119 table = &priv->static_config.tables[BLK_IDX_SCHEDULE_PARAMS];
120 if (table->entry_count) {
121 kfree(table->entries);
122 table->entry_count = 0;
123 }
124
125
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;
130 }
131
132
133 for (port = 0; port < SJA1105_NUM_PORTS; port++) {
134 if (tas_data->offload[port]) {
135 num_entries += tas_data->offload[port]->num_entries;
136 num_cycles++;
137 }
138 }
139
140
141 if (!num_cycles)
142 return 0;
143
144
145
146
147 table = &priv->static_config.tables[BLK_IDX_SCHEDULE];
148 table->entries = kcalloc(num_entries, table->ops->unpacked_entry_size,
149 GFP_KERNEL);
150 if (!table->entries)
151 return -ENOMEM;
152 table->entry_count = num_entries;
153 schedule = table->entries;
154
155
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);
159 if (!table->entries)
160
161
162
163
164 return -ENOMEM;
165 table->entry_count = SJA1105_MAX_SCHEDULE_ENTRY_POINTS_PARAMS_COUNT;
166 schedule_entry_points_params = table->entries;
167
168
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);
172 if (!table->entries)
173 return -ENOMEM;
174 table->entry_count = SJA1105_MAX_SCHEDULE_PARAMS_COUNT;
175 schedule_params = table->entries;
176
177
178 table = &priv->static_config.tables[BLK_IDX_SCHEDULE_ENTRY_POINTS];
179 table->entries = kcalloc(num_cycles, table->ops->unpacked_entry_size,
180 GFP_KERNEL);
181 if (!table->entries)
182 return -ENOMEM;
183 table->entry_count = num_cycles;
184 schedule_entry_points = table->entries;
185
186
187 schedule_entry_points_params->clksrc = SJA1105_TAS_CLKSRC_STANDALONE;
188 schedule_entry_points_params->actsubsch = num_cycles - 1;
189
190 for (port = 0; port < SJA1105_NUM_PORTS; port++) {
191 const struct tc_taprio_qopt_offload *offload;
192
193 offload = tas_data->offload[port];
194 if (!offload)
195 continue;
196
197 schedule_start_idx = k;
198 schedule_end_idx = k + offload->num_entries - 1;
199
200
201
202
203
204
205
206
207 entry_point_delta = 1;
208
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;
212
213
214
215
216 for (i = cycle; i < 8; i++)
217 schedule_params->subscheind[i] = schedule_end_idx;
218
219 for (i = 0; i < offload->num_entries; i++, k++) {
220 s64 delta_ns = offload->entries[i].interval;
221
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;
227 }
228 cycle++;
229 }
230
231 return 0;
232}
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255static bool
256sja1105_tas_check_conflicts(struct sja1105_private *priv, int port,
257 const struct tc_taprio_qopt_offload *admin)
258{
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;
262 s64 delta1, delta2;
263 s64 rbt1, rbt2;
264 s64 stop_time;
265 s64 t1, t2;
266 int i, j;
267 s32 rem;
268
269 offload = tas_data->offload[port];
270 if (!offload)
271 return false;
272
273
274
275
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);
279 if (rem)
280 return true;
281
282
283
284
285
286 div_s64_rem(offload->base_time, offload->cycle_time, &rem);
287 rbt1 = rem;
288
289 div_s64_rem(admin->base_time, admin->cycle_time, &rem);
290 rbt2 = rem;
291
292 stop_time = max_cycle_time + max(rbt1, rbt2);
293
294
295
296
297 for (i = 0, delta1 = 0;
298 i < offload->num_entries;
299 delta1 += offload->entries[i].interval, i++) {
300
301
302
303 for (j = 0, delta2 = 0;
304 j < admin->num_entries;
305 delta2 += admin->entries[j].interval, j++) {
306
307
308
309
310 for (t1 = rbt1 + delta1;
311 t1 <= stop_time;
312 t1 += offload->cycle_time) {
313
314
315
316
317 for (t2 = rbt2 + delta2;
318 t2 <= stop_time;
319 t2 += admin->cycle_time) {
320 if (t1 == t2) {
321 dev_warn(priv->ds->dev,
322 "GCL entry %d collides with entry %d of port %d\n",
323 j, i, port);
324 return true;
325 }
326 }
327 }
328 }
329 }
330
331 return false;
332}
333
334int sja1105_setup_tc_taprio(struct dsa_switch *ds, int port,
335 struct tc_taprio_qopt_offload *admin)
336{
337 struct sja1105_private *priv = ds->priv;
338 struct sja1105_tas_data *tas_data = &priv->tas_data;
339 int other_port, rc, i;
340
341
342
343
344 if (!!tas_data->offload[port] == admin->enable)
345 return -EINVAL;
346
347 if (!admin->enable) {
348 taprio_offload_free(tas_data->offload[port]);
349 tas_data->offload[port] = NULL;
350
351 rc = sja1105_init_scheduling(priv);
352 if (rc < 0)
353 return rc;
354
355 return sja1105_static_config_reload(priv);
356 }
357
358
359
360
361
362
363
364
365
366 if (admin->cycle_time_extension)
367 return -ENOTSUPP;
368
369 if (!ns_to_sja1105_delta(admin->base_time)) {
370 dev_err(ds->dev, "A base time of zero is not hardware-allowed\n");
371 return -ERANGE;
372 }
373
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;
378
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);
385 return -ERANGE;
386 }
387 }
388
389 for (other_port = 0; other_port < SJA1105_NUM_PORTS; other_port++) {
390 if (other_port == port)
391 continue;
392
393 if (sja1105_tas_check_conflicts(priv, other_port, admin))
394 return -ERANGE;
395 }
396
397 tas_data->offload[port] = taprio_offload_get(admin);
398
399 rc = sja1105_init_scheduling(priv);
400 if (rc < 0)
401 return rc;
402
403 return sja1105_static_config_reload(priv);
404}
405
406void sja1105_tas_setup(struct dsa_switch *ds)
407{
408}
409
410void sja1105_tas_teardown(struct dsa_switch *ds)
411{
412 struct sja1105_private *priv = ds->priv;
413 struct tc_taprio_qopt_offload *offload;
414 int port;
415
416 for (port = 0; port < SJA1105_NUM_PORTS; port++) {
417 offload = priv->tas_data.offload[port];
418 if (!offload)
419 continue;
420
421 taprio_offload_free(offload);
422 }
423}
424