196628508SDaniel Machon // SPDX-License-Identifier: GPL-2.0+ 296628508SDaniel Machon /* Microchip Sparx5 Switch driver 396628508SDaniel Machon * 496628508SDaniel Machon * Copyright (c) 2026 Microchip Technology Inc. and its subsidiaries. 596628508SDaniel Machon */ 696628508SDaniel Machon 7*a52629f8SDaniel Machon #include <net/pkt_sched.h> 8*a52629f8SDaniel Machon 996628508SDaniel Machon #include "sparx5_main.h" 10*a52629f8SDaniel Machon #include "sparx5_qos.h" 1196628508SDaniel Machon 1296628508SDaniel Machon enum sparx5_tas_link_speed { 1396628508SDaniel Machon TAS_SPEED_NO_GB, 1496628508SDaniel Machon TAS_SPEED_10, 1596628508SDaniel Machon TAS_SPEED_100, 1696628508SDaniel Machon TAS_SPEED_1000, 1796628508SDaniel Machon TAS_SPEED_2500, 1896628508SDaniel Machon TAS_SPEED_5000, 1996628508SDaniel Machon TAS_SPEED_10000, 2096628508SDaniel Machon TAS_SPEED_25000, 2196628508SDaniel Machon }; 2296628508SDaniel Machon 23*a52629f8SDaniel Machon /* Maximum time in milliseconds to wait for TAS state transitions. */ 24*a52629f8SDaniel Machon #define TAS_TIMEOUT_MS 2000 25*a52629f8SDaniel Machon 26*a52629f8SDaniel Machon /* Minimum supported cycle time in nanoseconds */ 27*a52629f8SDaniel Machon #define TAS_MIN_CYCLE_TIME_NS (1 * NSEC_PER_USEC) /* 1 usec */ 28*a52629f8SDaniel Machon 29*a52629f8SDaniel Machon /* Maximum supported cycle time in nanoseconds */ 30*a52629f8SDaniel Machon #define TAS_MAX_CYCLE_TIME_NS ((1 * NSEC_PER_SEC) - 1) /* 999.999.999 nsec */ 31*a52629f8SDaniel Machon 32*a52629f8SDaniel Machon /* STARTUP_TIME in units of 256 ns, must be non-zero for a list to start */ 33*a52629f8SDaniel Machon #define TAS_STARTUP_TIME_DEFAULT 8 34*a52629f8SDaniel Machon 35*a52629f8SDaniel Machon /* We use 2 TAS lists per present port, as far as the chip has lists: 36*a52629f8SDaniel Machon * num_tas_lists = min(port_count * SPX5_TAS_ENTRIES_PER_PORT, n_tas_lists); 37*a52629f8SDaniel Machon * 38*a52629f8SDaniel Machon * The index for the 2 entries of the n'th present port is calculated as: 39*a52629f8SDaniel Machon * index_1 = n * SPX5_TAS_ENTRIES_PER_PORT; 40*a52629f8SDaniel Machon * index_2 = index_1 + 1; 41*a52629f8SDaniel Machon * 42*a52629f8SDaniel Machon * GCL entries are allocated from a bitmap shared by all ports and sized to the 43*a52629f8SDaniel Machon * chip's GCL pool. Each list records the range it owns. The range is freed 44*a52629f8SDaniel Machon * once hardware reports the list back in ADMIN, which for a list obsoleted by 45*a52629f8SDaniel Machon * a replace happens some time after the replace itself. Entry 0 is reserved: 46*a52629f8SDaniel Machon * unwritten NEXT_GCL fields read as 0, so a list must never own it. 47*a52629f8SDaniel Machon * 48*a52629f8SDaniel Machon * We use 1 TAS profile per port, shared by both of its lists: 49*a52629f8SDaniel Machon * index = portno; 50*a52629f8SDaniel Machon * 51*a52629f8SDaniel Machon * We do not yet support frame preemption and the default guard band 52*a52629f8SDaniel Machon * of 1536 bytes is always used on all queues. 53*a52629f8SDaniel Machon * 54*a52629f8SDaniel Machon * The gate mask of each entry is written as is to the port's 8 queues, 55*a52629f8SDaniel Machon * where bit n gates queue (QoS class) n. The mqprio parameters of the 56*a52629f8SDaniel Machon * taprio qdisc (prio_tc_map, queue ranges) are not used, so a schedule 57*a52629f8SDaniel Machon * must use 8 traffic classes mapped 1:1 to the queues, as with the 58*a52629f8SDaniel Machon * mqprio offload. Other values of num_tc are rejected. 59*a52629f8SDaniel Machon * 60*a52629f8SDaniel Machon * These are the possible combinations of states for the two TAS lists in a 61*a52629f8SDaniel Machon * running system. 62*a52629f8SDaniel Machon * 63*a52629f8SDaniel Machon * ADMIN/ADMIN: No schedules are currently operating or pending. 64*a52629f8SDaniel Machon * 65*a52629f8SDaniel Machon * ADMIN/PENDING: No schedule is currently operating but one is pending and 66*a52629f8SDaniel Machon * when current time exceeds the configured base time it will automatically 67*a52629f8SDaniel Machon * enter the OPERATING state. The state is now ADMIN/OPERATING. 68*a52629f8SDaniel Machon * 69*a52629f8SDaniel Machon * ADMIN/OPERATING: A schedule is currently operating and will run until it is 70*a52629f8SDaniel Machon * terminated manually. 71*a52629f8SDaniel Machon * 72*a52629f8SDaniel Machon * OPERATING/PENDING: A schedule is currently operating and another is 73*a52629f8SDaniel Machon * pending. When current time exceeds the configured base time for the 74*a52629f8SDaniel Machon * pending schedule it will automatically stop the operating schedule and 75*a52629f8SDaniel Machon * enter the OPERATING state. The state is now ADMIN/OPERATING. 76*a52629f8SDaniel Machon * 77*a52629f8SDaniel Machon * When we want to disable TAS on a port, we must stop schedules that are in 78*a52629f8SDaniel Machon * state PENDING or OPERATING. 79*a52629f8SDaniel Machon * Pending schedules are stopped first and then operating schedules. 80*a52629f8SDaniel Machon * The gate state for the port must be restored to "all-queues-open" manually 81*a52629f8SDaniel Machon * in case the schedule was stopped with one or more of the queues closed. 82*a52629f8SDaniel Machon * 83*a52629f8SDaniel Machon * When we add a schedule we always use a base time in the future, where base 84*a52629f8SDaniel Machon * time is at least current time + (2 * cycle time). This is a requirement 85*a52629f8SDaniel Machon * from the hardware. This means that a schedule will always start in state 86*a52629f8SDaniel Machon * PENDING. It also means that we always use the new schedule to stop an 87*a52629f8SDaniel Machon * eventually operating schedule. 88*a52629f8SDaniel Machon * 89*a52629f8SDaniel Machon * When we want to add a new schedule we must consider the current state of 90*a52629f8SDaniel Machon * the two TAS list entries: 91*a52629f8SDaniel Machon * 92*a52629f8SDaniel Machon * ADMIN/ADMIN: Just add the new schedule in one of the TAS lists. 93*a52629f8SDaniel Machon * 94*a52629f8SDaniel Machon * ADMIN/PENDING: Stop the current pending schedule and add the new one. 95*a52629f8SDaniel Machon * 96*a52629f8SDaniel Machon * ADMIN/OPERATING: Add the new schedule in the TAS list that is currently in 97*a52629f8SDaniel Machon * admin state and configure it to stop the currently operating schedule when 98*a52629f8SDaniel Machon * current time exceeds the configured base time in the new schedule. 99*a52629f8SDaniel Machon * 100*a52629f8SDaniel Machon * OPERATING/PENDING: Stop the current pending schedule, add the new one and 101*a52629f8SDaniel Machon * configure it to stop the currently operating schedule when 102*a52629f8SDaniel Machon * current time exceeds the configured base time in the new schedule. 103*a52629f8SDaniel Machon */ 104*a52629f8SDaniel Machon 105*a52629f8SDaniel Machon /* Scheduler element index (HSCH_POS) for this port's per-port TAS gate, 106*a52629f8SDaniel Machon * i.e. its position at the layer-2 boundary of the scheduling hierarchy. 107*a52629f8SDaniel Machon */ 108*a52629f8SDaniel Machon static int sparx5_tas_scheduler_get(struct sparx5 *sparx5, int portno) 109*a52629f8SDaniel Machon { 110*a52629f8SDaniel Machon if (is_sparx5(sparx5)) 111*a52629f8SDaniel Machon return SPX5_HSCH_L0_SE_CNT + SPX5_HSCH_L1_SE_CNT + portno; 112*a52629f8SDaniel Machon else 113*a52629f8SDaniel Machon return 1120 + portno; 114*a52629f8SDaniel Machon } 115*a52629f8SDaniel Machon 116*a52629f8SDaniel Machon static int sparx5_tas_list_index(struct sparx5_port *port, u8 tas_entry) 117*a52629f8SDaniel Machon { 118*a52629f8SDaniel Machon const struct sparx5_consts *consts = port->sparx5->data->consts; 119*a52629f8SDaniel Machon int portno, pidx = 0; 120*a52629f8SDaniel Machon 121*a52629f8SDaniel Machon /* Limit the index to available ports: the n'th present port uses 122*a52629f8SDaniel Machon * lists 2n and 2n + 1, so the lists of all ports fit in 123*a52629f8SDaniel Machon * port_count * SPX5_TAS_ENTRIES_PER_PORT. 124*a52629f8SDaniel Machon */ 125*a52629f8SDaniel Machon for (portno = 0; portno < consts->n_ports; ++portno) { 126*a52629f8SDaniel Machon if (portno == port->portno) 127*a52629f8SDaniel Machon return (pidx * SPX5_TAS_ENTRIES_PER_PORT) + tas_entry; 128*a52629f8SDaniel Machon if (port->sparx5->ports[portno]) 129*a52629f8SDaniel Machon pidx++; 130*a52629f8SDaniel Machon } 131*a52629f8SDaniel Machon 132*a52629f8SDaniel Machon return 0; 133*a52629f8SDaniel Machon } 134*a52629f8SDaniel Machon 135*a52629f8SDaniel Machon /* Number of TAS lists in use: two per present port, as far as the chip has 136*a52629f8SDaniel Machon * lists for. A port whose lists fall outside this range has no TAS. 137*a52629f8SDaniel Machon */ 138*a52629f8SDaniel Machon static int sparx5_tas_num_lists(struct sparx5 *sparx5) 139*a52629f8SDaniel Machon { 140*a52629f8SDaniel Machon return min_t(int, sparx5->port_count * SPX5_TAS_ENTRIES_PER_PORT, 141*a52629f8SDaniel Machon sparx5->data->consts->n_tas_lists); 142*a52629f8SDaniel Machon } 143*a52629f8SDaniel Machon 144*a52629f8SDaniel Machon static bool sparx5_tas_port_has_lists(struct sparx5_port *port) 145*a52629f8SDaniel Machon { 146*a52629f8SDaniel Machon return sparx5_tas_list_index(port, SPX5_TAS_ENTRIES_PER_PORT - 1) < 147*a52629f8SDaniel Machon sparx5_tas_num_lists(port->sparx5); 148*a52629f8SDaniel Machon } 149*a52629f8SDaniel Machon 150*a52629f8SDaniel Machon static const char *sparx5_tas_state_to_str(int state) 151*a52629f8SDaniel Machon { 152*a52629f8SDaniel Machon switch (state) { 153*a52629f8SDaniel Machon case SPX5_TAS_STATE_ADMIN: 154*a52629f8SDaniel Machon return "ADMIN"; 155*a52629f8SDaniel Machon case SPX5_TAS_STATE_ADVANCING: 156*a52629f8SDaniel Machon return "ADVANCING"; 157*a52629f8SDaniel Machon case SPX5_TAS_STATE_PENDING: 158*a52629f8SDaniel Machon return "PENDING"; 159*a52629f8SDaniel Machon case SPX5_TAS_STATE_OPERATING: 160*a52629f8SDaniel Machon return "OPERATING"; 161*a52629f8SDaniel Machon case SPX5_TAS_STATE_TERMINATING: 162*a52629f8SDaniel Machon return "TERMINATING"; 163*a52629f8SDaniel Machon default: 164*a52629f8SDaniel Machon return "??"; 165*a52629f8SDaniel Machon } 166*a52629f8SDaniel Machon } 167*a52629f8SDaniel Machon 168*a52629f8SDaniel Machon static void sparx5_tas_select_list(struct sparx5 *sparx5, int list) 169*a52629f8SDaniel Machon { 170*a52629f8SDaniel Machon spx5_rmw(HSCH_TAS_CFG_CTRL_LIST_NUM_SET(list), 171*a52629f8SDaniel Machon HSCH_TAS_CFG_CTRL_LIST_NUM, 172*a52629f8SDaniel Machon sparx5, 173*a52629f8SDaniel Machon HSCH_TAS_CFG_CTRL); 174*a52629f8SDaniel Machon } 175*a52629f8SDaniel Machon 176*a52629f8SDaniel Machon static int sparx5_tas_list_state_get(struct sparx5 *sparx5) 177*a52629f8SDaniel Machon { 178*a52629f8SDaniel Machon return HSCH_TAS_LIST_STATE_LIST_STATE_GET(spx5_rd(sparx5, 179*a52629f8SDaniel Machon HSCH_TAS_LIST_STATE)); 180*a52629f8SDaniel Machon } 181*a52629f8SDaniel Machon 182*a52629f8SDaniel Machon static int sparx5_tas_gcl_alloc(struct sparx5 *sparx5, int count) 183*a52629f8SDaniel Machon { 184*a52629f8SDaniel Machon u32 n_gcl = sparx5->data->consts->n_tas_gcl; 185*a52629f8SDaniel Machon unsigned long base; 186*a52629f8SDaniel Machon 187*a52629f8SDaniel Machon base = bitmap_find_next_zero_area(sparx5->tas_gcl_bitmap, 188*a52629f8SDaniel Machon n_gcl, 189*a52629f8SDaniel Machon 0, 190*a52629f8SDaniel Machon count, 191*a52629f8SDaniel Machon 0); 192*a52629f8SDaniel Machon if (base >= n_gcl) 193*a52629f8SDaniel Machon return -ENOSPC; 194*a52629f8SDaniel Machon 195*a52629f8SDaniel Machon bitmap_set(sparx5->tas_gcl_bitmap, base, count); 196*a52629f8SDaniel Machon 197*a52629f8SDaniel Machon return base; 198*a52629f8SDaniel Machon } 199*a52629f8SDaniel Machon 200*a52629f8SDaniel Machon static void sparx5_tas_gcl_free(struct sparx5_port *port, int slot) 201*a52629f8SDaniel Machon { 202*a52629f8SDaniel Machon if (port->tas_lists[slot].gcl_count) 203*a52629f8SDaniel Machon bitmap_clear(port->sparx5->tas_gcl_bitmap, 204*a52629f8SDaniel Machon port->tas_lists[slot].gcl_base, 205*a52629f8SDaniel Machon port->tas_lists[slot].gcl_count); 206*a52629f8SDaniel Machon 207*a52629f8SDaniel Machon port->tas_lists[slot].gcl_base = 0; 208*a52629f8SDaniel Machon port->tas_lists[slot].gcl_count = 0; 209*a52629f8SDaniel Machon } 210*a52629f8SDaniel Machon 211*a52629f8SDaniel Machon /* Free the GCL entries of every list that hardware has returned to ADMIN */ 212*a52629f8SDaniel Machon static void sparx5_tas_gcl_flush(struct sparx5 *sparx5) 213*a52629f8SDaniel Machon { 214*a52629f8SDaniel Machon struct sparx5_port *port; 215*a52629f8SDaniel Machon int i, j; 216*a52629f8SDaniel Machon 217*a52629f8SDaniel Machon lockdep_assert_held(&sparx5->tas_lock); 218*a52629f8SDaniel Machon 219*a52629f8SDaniel Machon for (i = 0; i < sparx5->data->consts->n_ports; i++) { 220*a52629f8SDaniel Machon port = sparx5->ports[i]; 221*a52629f8SDaniel Machon if (!port) 222*a52629f8SDaniel Machon continue; 223*a52629f8SDaniel Machon 224*a52629f8SDaniel Machon for (j = 0; j < SPX5_TAS_ENTRIES_PER_PORT; j++) { 225*a52629f8SDaniel Machon if (!port->tas_lists[j].gcl_count) 226*a52629f8SDaniel Machon continue; 227*a52629f8SDaniel Machon 228*a52629f8SDaniel Machon sparx5_tas_select_list(sparx5, 229*a52629f8SDaniel Machon sparx5_tas_list_index(port, j)); 230*a52629f8SDaniel Machon if (sparx5_tas_list_state_get(sparx5) == 231*a52629f8SDaniel Machon SPX5_TAS_STATE_ADMIN) 232*a52629f8SDaniel Machon sparx5_tas_gcl_free(port, j); 233*a52629f8SDaniel Machon } 234*a52629f8SDaniel Machon } 235*a52629f8SDaniel Machon } 236*a52629f8SDaniel Machon 237*a52629f8SDaniel Machon /* Request state 'req' (or only wait if 'req' < 0) on the selected list until 238*a52629f8SDaniel Machon * it reaches a state in the 'done' mask. 239*a52629f8SDaniel Machon */ 240*a52629f8SDaniel Machon static int sparx5_tas_list_state_wait(struct sparx5_port *port, int list, 241*a52629f8SDaniel Machon int req, u32 done) 242*a52629f8SDaniel Machon { 243*a52629f8SDaniel Machon struct sparx5 *sparx5 = port->sparx5; 244*a52629f8SDaniel Machon unsigned long end; 245*a52629f8SDaniel Machon int state; 246*a52629f8SDaniel Machon 247*a52629f8SDaniel Machon /* Do not wait forever for the state change */ 248*a52629f8SDaniel Machon end = jiffies + msecs_to_jiffies(TAS_TIMEOUT_MS); 249*a52629f8SDaniel Machon do { 250*a52629f8SDaniel Machon if (req >= 0) 251*a52629f8SDaniel Machon spx5_rmw(HSCH_TAS_LIST_STATE_LIST_STATE_SET(req), 252*a52629f8SDaniel Machon HSCH_TAS_LIST_STATE_LIST_STATE, 253*a52629f8SDaniel Machon sparx5, 254*a52629f8SDaniel Machon HSCH_TAS_LIST_STATE); 255*a52629f8SDaniel Machon 256*a52629f8SDaniel Machon state = sparx5_tas_list_state_get(sparx5); 257*a52629f8SDaniel Machon if (BIT(state) & done) 258*a52629f8SDaniel Machon return 0; 259*a52629f8SDaniel Machon 260*a52629f8SDaniel Machon cond_resched(); 261*a52629f8SDaniel Machon } while (!time_after(jiffies, end)); 262*a52629f8SDaniel Machon 263*a52629f8SDaniel Machon netdev_err(port->ndev, "Timeout switching TAS state %s in list %d\n", 264*a52629f8SDaniel Machon sparx5_tas_state_to_str(state), list); 265*a52629f8SDaniel Machon 266*a52629f8SDaniel Machon return -ETIME; 267*a52629f8SDaniel Machon } 268*a52629f8SDaniel Machon 269*a52629f8SDaniel Machon /* Wait for hardware to bring the selected list back to ADMIN */ 270*a52629f8SDaniel Machon static int sparx5_tas_wait_admin(struct sparx5_port *port, int list) 271*a52629f8SDaniel Machon { 272*a52629f8SDaniel Machon return sparx5_tas_list_state_wait(port, list, -1, 273*a52629f8SDaniel Machon BIT(SPX5_TAS_STATE_ADMIN)); 274*a52629f8SDaniel Machon } 275*a52629f8SDaniel Machon 276*a52629f8SDaniel Machon static int sparx5_tas_shutdown_pending(struct sparx5_port *port) 277*a52629f8SDaniel Machon { 278*a52629f8SDaniel Machon struct sparx5 *sparx5 = port->sparx5; 279*a52629f8SDaniel Machon int i, list, state, err; 280*a52629f8SDaniel Machon 281*a52629f8SDaniel Machon for (i = 0; i < SPX5_TAS_ENTRIES_PER_PORT; i++) { 282*a52629f8SDaniel Machon list = sparx5_tas_list_index(port, i); 283*a52629f8SDaniel Machon spx5_rmw(HSCH_TAS_CFG_CTRL_LIST_NUM_SET(list), 284*a52629f8SDaniel Machon HSCH_TAS_CFG_CTRL_LIST_NUM, 285*a52629f8SDaniel Machon sparx5, 286*a52629f8SDaniel Machon HSCH_TAS_CFG_CTRL); 287*a52629f8SDaniel Machon 288*a52629f8SDaniel Machon state = sparx5_tas_list_state_get(sparx5); 289*a52629f8SDaniel Machon if (state != SPX5_TAS_STATE_ADVANCING && 290*a52629f8SDaniel Machon state != SPX5_TAS_STATE_PENDING) 291*a52629f8SDaniel Machon continue; 292*a52629f8SDaniel Machon 293*a52629f8SDaniel Machon err = sparx5_tas_list_state_wait(port, list, 294*a52629f8SDaniel Machon SPX5_TAS_STATE_ADMIN, 295*a52629f8SDaniel Machon BIT(SPX5_TAS_STATE_ADMIN)); 296*a52629f8SDaniel Machon if (err) 297*a52629f8SDaniel Machon return err; 298*a52629f8SDaniel Machon 299*a52629f8SDaniel Machon sparx5_tas_gcl_free(port, i); 300*a52629f8SDaniel Machon } 301*a52629f8SDaniel Machon 302*a52629f8SDaniel Machon return 0; 303*a52629f8SDaniel Machon } 304*a52629f8SDaniel Machon 305*a52629f8SDaniel Machon static int sparx5_tas_shutdown_operating(struct sparx5_port *port) 306*a52629f8SDaniel Machon { 307*a52629f8SDaniel Machon struct sparx5 *sparx5 = port->sparx5; 308*a52629f8SDaniel Machon int i, list, state, err = 0; 309*a52629f8SDaniel Machon u32 sched; 310*a52629f8SDaniel Machon 311*a52629f8SDaniel Machon sched = sparx5_tas_scheduler_get(sparx5, port->portno); 312*a52629f8SDaniel Machon 313*a52629f8SDaniel Machon for (i = 0; i < SPX5_TAS_ENTRIES_PER_PORT; i++) { 314*a52629f8SDaniel Machon list = sparx5_tas_list_index(port, i); 315*a52629f8SDaniel Machon spx5_rmw(HSCH_TAS_CFG_CTRL_LIST_NUM_SET(list), 316*a52629f8SDaniel Machon HSCH_TAS_CFG_CTRL_LIST_NUM, 317*a52629f8SDaniel Machon sparx5, 318*a52629f8SDaniel Machon HSCH_TAS_CFG_CTRL); 319*a52629f8SDaniel Machon 320*a52629f8SDaniel Machon /* A list being stopped still runs its gate operations until 321*a52629f8SDaniel Machon * the end of its cycle, so wait for it before opening the 322*a52629f8SDaniel Machon * gates. 323*a52629f8SDaniel Machon */ 324*a52629f8SDaniel Machon state = sparx5_tas_list_state_get(sparx5); 325*a52629f8SDaniel Machon if (state != SPX5_TAS_STATE_OPERATING && 326*a52629f8SDaniel Machon state != SPX5_TAS_STATE_TERMINATING) 327*a52629f8SDaniel Machon continue; 328*a52629f8SDaniel Machon 329*a52629f8SDaniel Machon if (state == SPX5_TAS_STATE_OPERATING && 330*a52629f8SDaniel Machon sparx5_tas_list_state_wait(port, list, 331*a52629f8SDaniel Machon SPX5_TAS_STATE_TERMINATING, 332*a52629f8SDaniel Machon BIT(SPX5_TAS_STATE_TERMINATING) | 333*a52629f8SDaniel Machon BIT(SPX5_TAS_STATE_ADMIN))) { 334*a52629f8SDaniel Machon err = -ETIME; 335*a52629f8SDaniel Machon continue; 336*a52629f8SDaniel Machon } 337*a52629f8SDaniel Machon 338*a52629f8SDaniel Machon if (sparx5_tas_wait_admin(port, list)) { 339*a52629f8SDaniel Machon err = -ETIME; 340*a52629f8SDaniel Machon continue; 341*a52629f8SDaniel Machon } 342*a52629f8SDaniel Machon 343*a52629f8SDaniel Machon sparx5_tas_gcl_free(port, i); 344*a52629f8SDaniel Machon } 345*a52629f8SDaniel Machon 346*a52629f8SDaniel Machon /* Restore gate state to "all-queues-open". */ 347*a52629f8SDaniel Machon 348*a52629f8SDaniel Machon /* Select port n on layer 2 of Hierarchical Scheduler */ 349*a52629f8SDaniel Machon spx5_wr(HSCH_TAS_GATE_STATE_CTRL_HSCH_POS_SET(sched), 350*a52629f8SDaniel Machon sparx5, 351*a52629f8SDaniel Machon HSCH_TAS_GATE_STATE_CTRL); 352*a52629f8SDaniel Machon 353*a52629f8SDaniel Machon /* Set gate state to "all-queues-open" */ 354*a52629f8SDaniel Machon spx5_wr(HSCH_TAS_GATE_STATE_TAS_GATE_STATE_SET(0xff), 355*a52629f8SDaniel Machon sparx5, 356*a52629f8SDaniel Machon HSCH_TAS_GATE_STATE); 357*a52629f8SDaniel Machon 358*a52629f8SDaniel Machon return err; 359*a52629f8SDaniel Machon } 360*a52629f8SDaniel Machon 361*a52629f8SDaniel Machon /* Find an ADMIN list for a new schedule, after stopping a pending one. An 362*a52629f8SDaniel Machon * operating list is returned in obsolete, to be stopped by the new list. 363*a52629f8SDaniel Machon */ 364*a52629f8SDaniel Machon static int sparx5_tas_list_find(struct sparx5_port *port, int *new, 365*a52629f8SDaniel Machon int *obsolete) 366*a52629f8SDaniel Machon { 367*a52629f8SDaniel Machon int i, err, oper = -1, state_cnt[SPX5_NUM_TAS_STATE] = {0}; 368*a52629f8SDaniel Machon struct sparx5 *sparx5 = port->sparx5; 369*a52629f8SDaniel Machon int state[SPX5_TAS_ENTRIES_PER_PORT]; 370*a52629f8SDaniel Machon int list[SPX5_TAS_ENTRIES_PER_PORT]; 371*a52629f8SDaniel Machon bool valid = false; 372*a52629f8SDaniel Machon 373*a52629f8SDaniel Machon err = sparx5_tas_shutdown_pending(port); 374*a52629f8SDaniel Machon if (err) 375*a52629f8SDaniel Machon return err; 376*a52629f8SDaniel Machon 377*a52629f8SDaniel Machon for (i = 0; i < SPX5_TAS_ENTRIES_PER_PORT; i++) { 378*a52629f8SDaniel Machon list[i] = sparx5_tas_list_index(port, i); 379*a52629f8SDaniel Machon spx5_rmw(HSCH_TAS_CFG_CTRL_LIST_NUM_SET(list[i]), 380*a52629f8SDaniel Machon HSCH_TAS_CFG_CTRL_LIST_NUM, 381*a52629f8SDaniel Machon sparx5, 382*a52629f8SDaniel Machon HSCH_TAS_CFG_CTRL); 383*a52629f8SDaniel Machon 384*a52629f8SDaniel Machon state[i] = sparx5_tas_list_state_get(sparx5); 385*a52629f8SDaniel Machon 386*a52629f8SDaniel Machon if (state[i] == SPX5_TAS_STATE_TERMINATING) { 387*a52629f8SDaniel Machon err = sparx5_tas_wait_admin(port, list[i]); 388*a52629f8SDaniel Machon if (err) 389*a52629f8SDaniel Machon return err; 390*a52629f8SDaniel Machon state[i] = SPX5_TAS_STATE_ADMIN; 391*a52629f8SDaniel Machon } 392*a52629f8SDaniel Machon 393*a52629f8SDaniel Machon if (state[i] >= SPX5_NUM_TAS_STATE) { 394*a52629f8SDaniel Machon netdev_err(port->ndev, 395*a52629f8SDaniel Machon "Invalid tas list state %u %u %d\n", 396*a52629f8SDaniel Machon state[i], port->portno, i); 397*a52629f8SDaniel Machon return -EINVAL; 398*a52629f8SDaniel Machon } 399*a52629f8SDaniel Machon 400*a52629f8SDaniel Machon if (state[i] == SPX5_TAS_STATE_OPERATING) 401*a52629f8SDaniel Machon oper = list[i]; 402*a52629f8SDaniel Machon 403*a52629f8SDaniel Machon state_cnt[state[i]]++; 404*a52629f8SDaniel Machon } 405*a52629f8SDaniel Machon 406*a52629f8SDaniel Machon if (state_cnt[SPX5_TAS_STATE_ADMIN] == 2) 407*a52629f8SDaniel Machon valid = true; 408*a52629f8SDaniel Machon 409*a52629f8SDaniel Machon if (state_cnt[SPX5_TAS_STATE_ADMIN] == 1 && 410*a52629f8SDaniel Machon state_cnt[SPX5_TAS_STATE_OPERATING] == 1) 411*a52629f8SDaniel Machon valid = true; 412*a52629f8SDaniel Machon 413*a52629f8SDaniel Machon if (!valid) { 414*a52629f8SDaniel Machon netdev_err(port->ndev, 415*a52629f8SDaniel Machon "Invalid tas state combination: %d %d %d %d %d\n", 416*a52629f8SDaniel Machon state_cnt[SPX5_TAS_STATE_ADMIN], 417*a52629f8SDaniel Machon state_cnt[SPX5_TAS_STATE_ADVANCING], 418*a52629f8SDaniel Machon state_cnt[SPX5_TAS_STATE_PENDING], 419*a52629f8SDaniel Machon state_cnt[SPX5_TAS_STATE_OPERATING], 420*a52629f8SDaniel Machon state_cnt[SPX5_TAS_STATE_TERMINATING]); 421*a52629f8SDaniel Machon 422*a52629f8SDaniel Machon return -EINVAL; 423*a52629f8SDaniel Machon } 424*a52629f8SDaniel Machon 425*a52629f8SDaniel Machon for (i = 0; i < SPX5_TAS_ENTRIES_PER_PORT; i++) { 426*a52629f8SDaniel Machon if (state[i] == SPX5_TAS_STATE_ADMIN) { 427*a52629f8SDaniel Machon *new = list[i]; 428*a52629f8SDaniel Machon *obsolete = (oper == -1) ? *new : oper; 429*a52629f8SDaniel Machon return 0; 430*a52629f8SDaniel Machon } 431*a52629f8SDaniel Machon } 432*a52629f8SDaniel Machon 433*a52629f8SDaniel Machon return -EBUSY; /* No suitable list found */ 434*a52629f8SDaniel Machon } 435*a52629f8SDaniel Machon 436*a52629f8SDaniel Machon /* Program the list and its GCL entries [base, base + num_entries) */ 437*a52629f8SDaniel Machon static void sparx5_tas_gcl_setup(struct sparx5_port *port, int list, int base, 438*a52629f8SDaniel Machon struct tc_taprio_qopt_offload *qopt) 439*a52629f8SDaniel Machon { 440*a52629f8SDaniel Machon struct sparx5 *sparx5 = port->sparx5; 441*a52629f8SDaniel Machon u32 sched, entry, prof; 442*a52629f8SDaniel Machon int i; 443*a52629f8SDaniel Machon 444*a52629f8SDaniel Machon sched = sparx5_tas_scheduler_get(sparx5, port->portno); 445*a52629f8SDaniel Machon /* Each port uses the TAS profile of its own number */ 446*a52629f8SDaniel Machon prof = port->portno; 447*a52629f8SDaniel Machon 448*a52629f8SDaniel Machon /* Program only the chosen list; the sibling slot stays in ADMIN. */ 449*a52629f8SDaniel Machon sparx5_tas_select_list(sparx5, list); 450*a52629f8SDaniel Machon 451*a52629f8SDaniel Machon spx5_rmw(HSCH_TAS_LIST_CFG_LIST_BASE_ADDR_SET(base), 452*a52629f8SDaniel Machon HSCH_TAS_LIST_CFG_LIST_BASE_ADDR, 453*a52629f8SDaniel Machon sparx5, 454*a52629f8SDaniel Machon HSCH_TAS_LIST_CFG); 455*a52629f8SDaniel Machon 456*a52629f8SDaniel Machon if (is_sparx5(sparx5)) { 457*a52629f8SDaniel Machon spx5_rmw(HSCH_TAS_LIST_CFG_LIST_LENGTH_SET(qopt->num_entries), 458*a52629f8SDaniel Machon HSCH_TAS_LIST_CFG_LIST_LENGTH, 459*a52629f8SDaniel Machon sparx5, 460*a52629f8SDaniel Machon HSCH_TAS_LIST_CFG); 461*a52629f8SDaniel Machon } else { 462*a52629f8SDaniel Machon /* Associate TAS list with physical port number and 463*a52629f8SDaniel Machon * scheduler element. 464*a52629f8SDaniel Machon */ 465*a52629f8SDaniel Machon spx5_rmw(HSCH_TAS_LIST_CFG_LIST_PORT_NUM_SET(port->portno), 466*a52629f8SDaniel Machon HSCH_TAS_LIST_CFG_LIST_PORT_NUM, sparx5, 467*a52629f8SDaniel Machon HSCH_TAS_LIST_CFG); 468*a52629f8SDaniel Machon spx5_rmw(HSCH_TAS_LIST_CFG_LIST_HSCH_POS_SET(sched), 469*a52629f8SDaniel Machon HSCH_TAS_LIST_CFG_LIST_HSCH_POS, sparx5, 470*a52629f8SDaniel Machon HSCH_TAS_LIST_CFG); 471*a52629f8SDaniel Machon } 472*a52629f8SDaniel Machon 473*a52629f8SDaniel Machon for (i = 0; i < qopt->num_entries; i++) { 474*a52629f8SDaniel Machon u32 gcl_next = (i >= qopt->num_entries - 1) ? base : 475*a52629f8SDaniel Machon base + i + 1; 476*a52629f8SDaniel Machon u32 gate_mask = qopt->entries[i].gate_mask; 477*a52629f8SDaniel Machon 478*a52629f8SDaniel Machon /* On sparx5 GCL_ENTRY_NUM is relative to the list's BASE_ADDR. 479*a52629f8SDaniel Machon * On lan969x the GCL is a linked list and GCL_ENTRY_NUM is an 480*a52629f8SDaniel Machon * absolute index into the pool, as is NEXT_GCL. 481*a52629f8SDaniel Machon */ 482*a52629f8SDaniel Machon entry = is_sparx5(sparx5) ? i : base + i; 483*a52629f8SDaniel Machon 484*a52629f8SDaniel Machon spx5_rmw(HSCH_TAS_CFG_CTRL_GCL_ENTRY_NUM_SET(entry), 485*a52629f8SDaniel Machon HSCH_TAS_CFG_CTRL_GCL_ENTRY_NUM, 486*a52629f8SDaniel Machon sparx5, 487*a52629f8SDaniel Machon HSCH_TAS_CFG_CTRL); 488*a52629f8SDaniel Machon 489*a52629f8SDaniel Machon /* Set gate states for this GCL */ 490*a52629f8SDaniel Machon spx5_rmw(HSCH_TAS_GCL_CTRL_CFG_GATE_STATE_SET(gate_mask), 491*a52629f8SDaniel Machon HSCH_TAS_GCL_CTRL_CFG_GATE_STATE, sparx5, 492*a52629f8SDaniel Machon HSCH_TAS_GCL_CTRL_CFG); 493*a52629f8SDaniel Machon 494*a52629f8SDaniel Machon if (is_sparx5(sparx5)) { 495*a52629f8SDaniel Machon spx5_rmw(HSCH_TAS_GCL_CTRL_CFG_HSCH_POS_SET(sched) | 496*a52629f8SDaniel Machon HSCH_TAS_GCL_CTRL_CFG_PORT_PROFILE_SET(prof), 497*a52629f8SDaniel Machon HSCH_TAS_GCL_CTRL_CFG_HSCH_POS | 498*a52629f8SDaniel Machon HSCH_TAS_GCL_CTRL_CFG_PORT_PROFILE, sparx5, 499*a52629f8SDaniel Machon HSCH_TAS_GCL_CTRL_CFG); 500*a52629f8SDaniel Machon } else { 501*a52629f8SDaniel Machon /* The GCL list is a linked list on lan969x */ 502*a52629f8SDaniel Machon spx5_wr(HSCH_TAS_GCL_CTRL_CFG2_NEXT_GCL_SET(gcl_next), 503*a52629f8SDaniel Machon sparx5, HSCH_TAS_GCL_CTRL_CFG2); 504*a52629f8SDaniel Machon } 505*a52629f8SDaniel Machon 506*a52629f8SDaniel Machon spx5_wr(qopt->entries[i].interval, 507*a52629f8SDaniel Machon sparx5, 508*a52629f8SDaniel Machon HSCH_TAS_GCL_TIME_CFG); 509*a52629f8SDaniel Machon } 510*a52629f8SDaniel Machon } 511*a52629f8SDaniel Machon 512*a52629f8SDaniel Machon int sparx5_tas_enable(struct sparx5_port *port, 513*a52629f8SDaniel Machon struct tc_taprio_qopt_offload *qopt) 514*a52629f8SDaniel Machon { 515*a52629f8SDaniel Machon u64 cycle_time = qopt->cycle_time, calculated_cycle_time = 0; 516*a52629f8SDaniel Machon int i, err, base, slot, state, new_list = -1, obsolete = -1; 517*a52629f8SDaniel Machon struct sparx5 *sparx5 = port->sparx5; 518*a52629f8SDaniel Machon struct timespec64 ts; 519*a52629f8SDaniel Machon ktime_t base_time; 520*a52629f8SDaniel Machon unsigned long end; 521*a52629f8SDaniel Machon u32 startup, val; 522*a52629f8SDaniel Machon 523*a52629f8SDaniel Machon if (qopt->cycle_time_extension) { 524*a52629f8SDaniel Machon netdev_err(port->ndev, 525*a52629f8SDaniel Machon "cycle_time_extension is not supported\n"); 526*a52629f8SDaniel Machon return -EOPNOTSUPP; 527*a52629f8SDaniel Machon } 528*a52629f8SDaniel Machon 529*a52629f8SDaniel Machon /* The base time is taken from the PTP clock */ 530*a52629f8SDaniel Machon if (!sparx5->ptp) 531*a52629f8SDaniel Machon return -EOPNOTSUPP; 532*a52629f8SDaniel Machon 533*a52629f8SDaniel Machon if (qopt->base_time < 0) { 534*a52629f8SDaniel Machon netdev_err(port->ndev, "Invalid base_time %lld\n", 535*a52629f8SDaniel Machon qopt->base_time); 536*a52629f8SDaniel Machon return -EINVAL; 537*a52629f8SDaniel Machon } 538*a52629f8SDaniel Machon 539*a52629f8SDaniel Machon if (!sparx5_tas_port_has_lists(port)) { 540*a52629f8SDaniel Machon netdev_err(port->ndev, "No TAS lists left for this port\n"); 541*a52629f8SDaniel Machon return -EOPNOTSUPP; 542*a52629f8SDaniel Machon } 543*a52629f8SDaniel Machon 544*a52629f8SDaniel Machon if (qopt->mqprio.qopt.num_tc != SPX5_PRIOS) { 545*a52629f8SDaniel Machon netdev_err(port->ndev, "Only %d traffic classes supported\n", 546*a52629f8SDaniel Machon SPX5_PRIOS); 547*a52629f8SDaniel Machon return -EOPNOTSUPP; 548*a52629f8SDaniel Machon } 549*a52629f8SDaniel Machon 550*a52629f8SDaniel Machon for (i = 0; i < qopt->num_entries; i++) { 551*a52629f8SDaniel Machon if (qopt->entries[i].command != TC_TAPRIO_CMD_SET_GATES) { 552*a52629f8SDaniel Machon netdev_err(port->ndev, "Unsupported GCL command: %d\n", 553*a52629f8SDaniel Machon qopt->entries[i].command); 554*a52629f8SDaniel Machon return -EOPNOTSUPP; 555*a52629f8SDaniel Machon } 556*a52629f8SDaniel Machon } 557*a52629f8SDaniel Machon 558*a52629f8SDaniel Machon mutex_lock(&sparx5->tas_lock); 559*a52629f8SDaniel Machon 560*a52629f8SDaniel Machon sparx5_tas_gcl_flush(sparx5); 561*a52629f8SDaniel Machon 562*a52629f8SDaniel Machon if (cycle_time > TAS_MAX_CYCLE_TIME_NS) { 563*a52629f8SDaniel Machon netdev_err(port->ndev, "Invalid cycle_time %llu\n", cycle_time); 564*a52629f8SDaniel Machon err = -EINVAL; 565*a52629f8SDaniel Machon goto out; 566*a52629f8SDaniel Machon } 567*a52629f8SDaniel Machon 568*a52629f8SDaniel Machon for (i = 0; i < qopt->num_entries; i++) { 569*a52629f8SDaniel Machon u32 interval = qopt->entries[i].interval; 570*a52629f8SDaniel Machon 571*a52629f8SDaniel Machon if (interval < TAS_MIN_CYCLE_TIME_NS) { 572*a52629f8SDaniel Machon netdev_err(port->ndev, 573*a52629f8SDaniel Machon "Invalid minimum cycle time %u\n", interval); 574*a52629f8SDaniel Machon err = -EINVAL; 575*a52629f8SDaniel Machon goto out; 576*a52629f8SDaniel Machon } 577*a52629f8SDaniel Machon 578*a52629f8SDaniel Machon if (interval > TAS_MAX_CYCLE_TIME_NS) { 579*a52629f8SDaniel Machon netdev_err(port->ndev, 580*a52629f8SDaniel Machon "Invalid maximum cycle time %u\n", interval); 581*a52629f8SDaniel Machon err = -EINVAL; 582*a52629f8SDaniel Machon goto out; 583*a52629f8SDaniel Machon } 584*a52629f8SDaniel Machon 585*a52629f8SDaniel Machon calculated_cycle_time += interval; 586*a52629f8SDaniel Machon } 587*a52629f8SDaniel Machon 588*a52629f8SDaniel Machon if (calculated_cycle_time > TAS_MAX_CYCLE_TIME_NS || 589*a52629f8SDaniel Machon cycle_time < calculated_cycle_time) { 590*a52629f8SDaniel Machon netdev_err(port->ndev, "Invalid cycle_time %llu\n", cycle_time); 591*a52629f8SDaniel Machon err = -EINVAL; 592*a52629f8SDaniel Machon goto out; 593*a52629f8SDaniel Machon } 594*a52629f8SDaniel Machon 595*a52629f8SDaniel Machon sparx5_new_base_time(sparx5, cycle_time, qopt->base_time, &base_time); 596*a52629f8SDaniel Machon 597*a52629f8SDaniel Machon /* Allocate before sparx5_tas_list_find() stops a pending schedule, so 598*a52629f8SDaniel Machon * running out of GCL entries fails the replace without touching 599*a52629f8SDaniel Machon * hardware. 600*a52629f8SDaniel Machon */ 601*a52629f8SDaniel Machon base = sparx5_tas_gcl_alloc(sparx5, qopt->num_entries); 602*a52629f8SDaniel Machon if (base < 0) { 603*a52629f8SDaniel Machon netdev_err(port->ndev, "No room for %zu GCL entries\n", 604*a52629f8SDaniel Machon qopt->num_entries); 605*a52629f8SDaniel Machon err = base; 606*a52629f8SDaniel Machon goto out; 607*a52629f8SDaniel Machon } 608*a52629f8SDaniel Machon 609*a52629f8SDaniel Machon /* Select an appropriate entry to use */ 610*a52629f8SDaniel Machon err = sparx5_tas_list_find(port, &new_list, &obsolete); 611*a52629f8SDaniel Machon if (err) { 612*a52629f8SDaniel Machon bitmap_clear(sparx5->tas_gcl_bitmap, base, qopt->num_entries); 613*a52629f8SDaniel Machon goto out; 614*a52629f8SDaniel Machon } 615*a52629f8SDaniel Machon 616*a52629f8SDaniel Machon /* The new list is in ADMIN, so a range it still owns can go */ 617*a52629f8SDaniel Machon slot = new_list - sparx5_tas_list_index(port, 0); 618*a52629f8SDaniel Machon sparx5_tas_gcl_free(port, slot); 619*a52629f8SDaniel Machon 620*a52629f8SDaniel Machon sparx5_tas_gcl_setup(port, new_list, base, qopt); 621*a52629f8SDaniel Machon 622*a52629f8SDaniel Machon /* The obsolete list keeps its entries until it reaches ADMIN */ 623*a52629f8SDaniel Machon port->tas_lists[slot].gcl_base = base; 624*a52629f8SDaniel Machon port->tas_lists[slot].gcl_count = qopt->num_entries; 625*a52629f8SDaniel Machon 626*a52629f8SDaniel Machon /* Setup TAS list */ 627*a52629f8SDaniel Machon ts = ktime_to_timespec64(base_time); 628*a52629f8SDaniel Machon spx5_wr(HSCH_TAS_BASE_TIME_NSEC_BASE_TIME_NSEC_SET(ts.tv_nsec), 629*a52629f8SDaniel Machon sparx5, 630*a52629f8SDaniel Machon HSCH_TAS_BASE_TIME_NSEC); 631*a52629f8SDaniel Machon 632*a52629f8SDaniel Machon spx5_wr((ts.tv_sec & GENMASK(31, 0)), 633*a52629f8SDaniel Machon sparx5, 634*a52629f8SDaniel Machon HSCH_TAS_BASE_TIME_SEC_LSB); 635*a52629f8SDaniel Machon 636*a52629f8SDaniel Machon val = ts.tv_sec >> 32; 637*a52629f8SDaniel Machon spx5_wr(HSCH_TAS_BASE_TIME_SEC_MSB_BASE_TIME_SEC_MSB_SET(val), 638*a52629f8SDaniel Machon sparx5, 639*a52629f8SDaniel Machon HSCH_TAS_BASE_TIME_SEC_MSB); 640*a52629f8SDaniel Machon 641*a52629f8SDaniel Machon spx5_wr(cycle_time, 642*a52629f8SDaniel Machon sparx5, 643*a52629f8SDaniel Machon HSCH_TAS_CYCLE_TIME_CFG); 644*a52629f8SDaniel Machon 645*a52629f8SDaniel Machon /* Full write, to also clear a sticky STARTUP_ERROR from a previous use 646*a52629f8SDaniel Machon * of this list. 647*a52629f8SDaniel Machon */ 648*a52629f8SDaniel Machon spx5_wr(HSCH_TAS_STARTUP_CFG_OBSOLETE_IDX_SET(obsolete) | 649*a52629f8SDaniel Machon HSCH_TAS_STARTUP_CFG_STARTUP_TIME_SET(TAS_STARTUP_TIME_DEFAULT), 650*a52629f8SDaniel Machon sparx5, 651*a52629f8SDaniel Machon HSCH_TAS_STARTUP_CFG); 652*a52629f8SDaniel Machon 653*a52629f8SDaniel Machon /* Start list processing */ 654*a52629f8SDaniel Machon spx5_rmw(HSCH_TAS_LIST_STATE_LIST_STATE_SET(SPX5_TAS_STATE_ADVANCING), 655*a52629f8SDaniel Machon HSCH_TAS_LIST_STATE_LIST_STATE, 656*a52629f8SDaniel Machon sparx5, 657*a52629f8SDaniel Machon HSCH_TAS_LIST_STATE); 658*a52629f8SDaniel Machon 659*a52629f8SDaniel Machon /* Do not wait forever for the startup error flag to be reported */ 660*a52629f8SDaniel Machon end = jiffies + msecs_to_jiffies(TAS_TIMEOUT_MS); 661*a52629f8SDaniel Machon do { 662*a52629f8SDaniel Machon /* Read the state first, so an error that ended ADVANCING is 663*a52629f8SDaniel Machon * already latched when STARTUP_CFG is read. 664*a52629f8SDaniel Machon */ 665*a52629f8SDaniel Machon val = spx5_rd(sparx5, HSCH_TAS_LIST_STATE); 666*a52629f8SDaniel Machon state = HSCH_TAS_LIST_STATE_LIST_STATE_GET(val); 667*a52629f8SDaniel Machon startup = spx5_rd(sparx5, HSCH_TAS_STARTUP_CFG); 668*a52629f8SDaniel Machon if (HSCH_TAS_STARTUP_CFG_STARTUP_ERROR_GET(startup) || 669*a52629f8SDaniel Machon state != SPX5_TAS_STATE_ADVANCING) 670*a52629f8SDaniel Machon break; 671*a52629f8SDaniel Machon 672*a52629f8SDaniel Machon cond_resched(); 673*a52629f8SDaniel Machon } while (!time_after(jiffies, end)); 674*a52629f8SDaniel Machon 675*a52629f8SDaniel Machon if (HSCH_TAS_STARTUP_CFG_STARTUP_ERROR_GET(startup)) { 676*a52629f8SDaniel Machon netdev_err(port->ndev, "TAS startup error in list %d\n", 677*a52629f8SDaniel Machon new_list); 678*a52629f8SDaniel Machon err = -EIO; 679*a52629f8SDaniel Machon } else if (state == SPX5_TAS_STATE_ADVANCING) { 680*a52629f8SDaniel Machon netdev_err(port->ndev, "Timeout starting TAS list %d\n", 681*a52629f8SDaniel Machon new_list); 682*a52629f8SDaniel Machon err = -ETIME; 683*a52629f8SDaniel Machon } 684*a52629f8SDaniel Machon 685*a52629f8SDaniel Machon /* taprio sends no DESTROY for a failed offload, so do not leave a 686*a52629f8SDaniel Machon * list behind that may still start. 687*a52629f8SDaniel Machon */ 688*a52629f8SDaniel Machon if (err) 689*a52629f8SDaniel Machon sparx5_tas_shutdown_pending(port); 690*a52629f8SDaniel Machon 691*a52629f8SDaniel Machon out: 692*a52629f8SDaniel Machon mutex_unlock(&sparx5->tas_lock); 693*a52629f8SDaniel Machon return err; 694*a52629f8SDaniel Machon } 695*a52629f8SDaniel Machon 696*a52629f8SDaniel Machon int sparx5_tas_disable(struct sparx5_port *port) 697*a52629f8SDaniel Machon { 698*a52629f8SDaniel Machon int err, ret; 699*a52629f8SDaniel Machon 700*a52629f8SDaniel Machon /* Nothing was offloaded on a port without lists */ 701*a52629f8SDaniel Machon if (!sparx5_tas_port_has_lists(port)) 702*a52629f8SDaniel Machon return 0; 703*a52629f8SDaniel Machon 704*a52629f8SDaniel Machon mutex_lock(&port->sparx5->tas_lock); 705*a52629f8SDaniel Machon 706*a52629f8SDaniel Machon sparx5_tas_gcl_flush(port->sparx5); 707*a52629f8SDaniel Machon 708*a52629f8SDaniel Machon /* Stop the operating list and restore the gates even if a pending list 709*a52629f8SDaniel Machon * could not be stopped, as taprio does not retry a failed destroy. 710*a52629f8SDaniel Machon */ 711*a52629f8SDaniel Machon err = sparx5_tas_shutdown_pending(port); 712*a52629f8SDaniel Machon ret = sparx5_tas_shutdown_operating(port); 713*a52629f8SDaniel Machon if (!err) 714*a52629f8SDaniel Machon err = ret; 715*a52629f8SDaniel Machon 716*a52629f8SDaniel Machon mutex_unlock(&port->sparx5->tas_lock); 717*a52629f8SDaniel Machon 718*a52629f8SDaniel Machon return err; 719*a52629f8SDaniel Machon } 720*a52629f8SDaniel Machon 721*a52629f8SDaniel Machon int sparx5_tas_init(struct sparx5 *sparx5) 722*a52629f8SDaniel Machon { 723*a52629f8SDaniel Machon const struct sparx5_consts *consts = sparx5->data->consts; 724*a52629f8SDaniel Machon int i, num_ports, list_num_max; 725*a52629f8SDaniel Machon 726*a52629f8SDaniel Machon /* There are only 128 (sparx5) / 60 (lan969x) TAS lists, not enough 727*a52629f8SDaniel Machon * for the whole port range. LIST_NUM_MAX is the highest list index 728*a52629f8SDaniel Machon * processed, and the datasheet gives 24 as its minimum value. 729*a52629f8SDaniel Machon */ 730*a52629f8SDaniel Machon num_ports = consts->n_ports; 731*a52629f8SDaniel Machon list_num_max = max(sparx5_tas_num_lists(sparx5) - 1, 24); 732*a52629f8SDaniel Machon 733*a52629f8SDaniel Machon sparx5->tas_gcl_bitmap = devm_bitmap_zalloc(sparx5->dev, 734*a52629f8SDaniel Machon consts->n_tas_gcl, 735*a52629f8SDaniel Machon GFP_KERNEL); 736*a52629f8SDaniel Machon if (!sparx5->tas_gcl_bitmap) 737*a52629f8SDaniel Machon return -ENOMEM; 738*a52629f8SDaniel Machon 739*a52629f8SDaniel Machon /* Unwritten NEXT_GCL fields read as 0, so no list may own entry 0 */ 740*a52629f8SDaniel Machon bitmap_set(sparx5->tas_gcl_bitmap, 0, 1); 741*a52629f8SDaniel Machon 742*a52629f8SDaniel Machon mutex_init(&sparx5->tas_lock); 743*a52629f8SDaniel Machon 744*a52629f8SDaniel Machon /* Apply the guard band to gate closes of all traffic classes, not only 745*a52629f8SDaniel Machon * the classes not marked as scheduled in SCH_TRAFFIC_QUEUES. 746*a52629f8SDaniel Machon */ 747*a52629f8SDaniel Machon spx5_rmw(HSCH_TAS_CFG_CTRL_LIST_NUM_MAX_SET(list_num_max) | 748*a52629f8SDaniel Machon HSCH_TAS_CFG_CTRL_ALWAYS_GUARD_BAND_SCH_Q_SET(1), 749*a52629f8SDaniel Machon HSCH_TAS_CFG_CTRL_LIST_NUM_MAX | 750*a52629f8SDaniel Machon HSCH_TAS_CFG_CTRL_ALWAYS_GUARD_BAND_SCH_Q, 751*a52629f8SDaniel Machon sparx5, 752*a52629f8SDaniel Machon HSCH_TAS_CFG_CTRL); 753*a52629f8SDaniel Machon 754*a52629f8SDaniel Machon /* Associate profile with port (profile idx = port on lan969x) */ 755*a52629f8SDaniel Machon if (is_sparx5(sparx5)) { 756*a52629f8SDaniel Machon for (i = 0; i < num_ports; i++) { 757*a52629f8SDaniel Machon if (!sparx5->ports[i]) 758*a52629f8SDaniel Machon continue; 759*a52629f8SDaniel Machon 760*a52629f8SDaniel Machon spx5_rmw(HSCH_TAS_PROFILE_CONFIG_PORT_NUM_SET(i), 761*a52629f8SDaniel Machon HSCH_TAS_PROFILE_CONFIG_PORT_NUM, 762*a52629f8SDaniel Machon sparx5, 763*a52629f8SDaniel Machon HSCH_TAS_PROFILE_CONFIG(i)); 764*a52629f8SDaniel Machon } 765*a52629f8SDaniel Machon } 766*a52629f8SDaniel Machon 767*a52629f8SDaniel Machon return 0; 768*a52629f8SDaniel Machon } 769*a52629f8SDaniel Machon 77096628508SDaniel Machon void sparx5_tas_speed(struct sparx5_port *port, int speed) 77196628508SDaniel Machon { 77296628508SDaniel Machon struct sparx5 *sparx5 = port->sparx5; 77396628508SDaniel Machon u8 spd; 77496628508SDaniel Machon 77596628508SDaniel Machon switch (speed) { 77696628508SDaniel Machon case SPEED_10: 77796628508SDaniel Machon spd = TAS_SPEED_10; 77896628508SDaniel Machon break; 77996628508SDaniel Machon case SPEED_100: 78096628508SDaniel Machon spd = TAS_SPEED_100; 78196628508SDaniel Machon break; 78296628508SDaniel Machon case SPEED_1000: 78396628508SDaniel Machon spd = TAS_SPEED_1000; 78496628508SDaniel Machon break; 78596628508SDaniel Machon case SPEED_2500: 78696628508SDaniel Machon spd = TAS_SPEED_2500; 78796628508SDaniel Machon break; 78896628508SDaniel Machon case SPEED_5000: 78996628508SDaniel Machon spd = TAS_SPEED_5000; 79096628508SDaniel Machon break; 79196628508SDaniel Machon case SPEED_10000: 79296628508SDaniel Machon spd = TAS_SPEED_10000; 79396628508SDaniel Machon break; 79496628508SDaniel Machon case SPEED_25000: 79596628508SDaniel Machon spd = TAS_SPEED_25000; 79696628508SDaniel Machon break; 79796628508SDaniel Machon default: 79896628508SDaniel Machon netdev_err(port->ndev, "TAS: Unsupported speed: %d\n", speed); 79996628508SDaniel Machon return; 80096628508SDaniel Machon } 80196628508SDaniel Machon 80296628508SDaniel Machon spx5_rmw(HSCH_TAS_PROFILE_CONFIG_LINK_SPEED_SET(spd), 80396628508SDaniel Machon HSCH_TAS_PROFILE_CONFIG_LINK_SPEED, 80496628508SDaniel Machon sparx5, 80596628508SDaniel Machon HSCH_TAS_PROFILE_CONFIG(port->portno)); 80696628508SDaniel Machon } 807