root/drivers/net/dsa/sja1105/sja1105_tas.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ns_to_sja1105_delta
  2. sja1105_init_scheduling
  3. sja1105_tas_check_conflicts
  4. sja1105_setup_tc_taprio
  5. sja1105_tas_setup
  6. sja1105_tas_teardown

   1 // SPDX-License-Identifier: GPL-2.0
   2 /* Copyright (c) 2019, Vladimir Oltean <olteanv@gmail.com>
   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 /* 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.
  15  */
  16 static s64 ns_to_sja1105_delta(s64 ns)
  17 {
  18         return div_s64(ns, 200);
  19 }
  20 
  21 /* Lo and behold: the egress scheduler from hell.
  22  *
  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.
  27  *
  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".
  32  *
  33  * Subschedule (cycle) i starts when
  34  *   ptpclkval >= ptpschtm + BLK_IDX_SCHEDULE_ENTRY_POINTS[i].delta.
  35  *
  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]
  39  *
  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.
  42  *
  43  *         +---------+
  44  *         |         | BLK_IDX_SCHEDULE_ENTRY_POINTS_PARAMS
  45  *         +---------+
  46  *              |
  47  *              +-----------------+
  48  *                                | .actsubsch
  49  *  BLK_IDX_SCHEDULE_ENTRY_POINTS v
  50  *                 +-------+-------+
  51  *                 |cycle 0|cycle 1|
  52  *                 +-------+-------+
  53  *                   |  |      |  |
  54  *  +----------------+  |      |  +-------------------------------------+
  55  *  |   .subschindx     |      |             .subschindx                |
  56  *  |                   |      +---------------+                        |
  57  *  |          .address |        .address      |                        |
  58  *  |                   |                      |                        |
  59  *  |                   |                      |                        |
  60  *  |  BLK_IDX_SCHEDULE v                      v                        |
  61  *  |              +-------+-------+-------+-------+-------+------+     |
  62  *  |              |entry 0|entry 1|entry 2|entry 3|entry 4|entry5|     |
  63  *  |              +-------+-------+-------+-------+-------+------+     |
  64  *  |                                  ^                    ^  ^  ^     |
  65  *  |                                  |                    |  |  |     |
  66  *  |        +-------------------------+                    |  |  |     |
  67  *  |        |              +-------------------------------+  |  |     |
  68  *  |        |              |              +-------------------+  |     |
  69  *  |        |              |              |                      |     |
  70  *  | +---------------------------------------------------------------+ |
  71  *  | |subscheind[0]<=subscheind[1]<=subscheind[2]<=...<=subscheind[7]| |
  72  *  | +---------------------------------------------------------------+ |
  73  *  |        ^              ^                BLK_IDX_SCHEDULE_PARAMS    |
  74  *  |        |              |                                           |
  75  *  +--------+              +-------------------------------------------+
  76  *
  77  *  In the above picture there are two subschedules (cycles):
  78  *
  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)
  81  *
  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).
  85  */
  86 static 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         /* 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;
 109         }
 110 
 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;
 116         }
 117 
 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;
 123         }
 124 
 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;
 130         }
 131 
 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;
 136                         num_cycles++;
 137                 }
 138         }
 139 
 140         /* Nothing to do */
 141         if (!num_cycles)
 142                 return 0;
 143 
 144         /* Pre-allocate space in the static config tables */
 145 
 146         /* Schedule Table */
 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         /* 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);
 159         if (!table->entries)
 160                 /* Previously allocated memory will be freed automatically in
 161                  * sja1105_static_config_free. This is true for all early
 162                  * returns below.
 163                  */
 164                 return -ENOMEM;
 165         table->entry_count = SJA1105_MAX_SCHEDULE_ENTRY_POINTS_PARAMS_COUNT;
 166         schedule_entry_points_params = table->entries;
 167 
 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);
 172         if (!table->entries)
 173                 return -ENOMEM;
 174         table->entry_count = SJA1105_MAX_SCHEDULE_PARAMS_COUNT;
 175         schedule_params = table->entries;
 176 
 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,
 180                                  GFP_KERNEL);
 181         if (!table->entries)
 182                 return -ENOMEM;
 183         table->entry_count = num_cycles;
 184         schedule_entry_points = table->entries;
 185 
 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;
 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                 /* 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
 205                  * PTP clock source.
 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                 /* The subschedule end indices need to be
 214                  * monotonically increasing.
 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 /* 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).
 254  */
 255 static bool
 256 sja1105_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         /* Check if the two cycle times are multiples of one another.
 274          * If they aren't, then they will surely collide.
 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         /* Calculate the "reduced" base time of each of the two cycles
 283          * (transposed back as close to 0 as possible) by dividing to
 284          * the cycle time.
 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         /* delta1 is the relative base time of each GCL entry within
 295          * the established ports' TAS config.
 296          */
 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.
 302                  */
 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
 308                          * first cycle time.
 309                          */
 310                         for (t1 = rbt1 + delta1;
 311                              t1 <= stop_time;
 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.
 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 
 334 int 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         /* Can't change an already configured port (must delete qdisc first).
 342          * Can't delete the qdisc from an unconfigured port.
 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         /* 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
 364          * time extension.
 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 
 406 void sja1105_tas_setup(struct dsa_switch *ds)
 407 {
 408 }
 409 
 410 void 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 }

/* [<][>][^][v][top][bottom][index][help] */