/* Minimum supported cycle time in nanoseconds */ #define LAN966X_TAPRIO_MIN_CYCLE_TIME_NS NSEC_PER_USEC
/* Maximum supported cycle time in nanoseconds */ #define LAN966X_TAPRIO_MAX_CYCLE_TIME_NS (NSEC_PER_SEC - 1)
/* Total number of TAS GCL entries */ #define LAN966X_TAPRIO_NUM_GCL 256
/* TAPRIO link speeds for calculation of guard band */ enum lan966x_taprio_link_speed {
LAN966X_TAPRIO_SPEED_NO_GB,
LAN966X_TAPRIO_SPEED_10,
LAN966X_TAPRIO_SPEED_100,
LAN966X_TAPRIO_SPEED_1000,
LAN966X_TAPRIO_SPEED_2500,
};
/* TAPRIO list states */ enum lan966x_taprio_state {
LAN966X_TAPRIO_STATE_ADMIN,
LAN966X_TAPRIO_STATE_ADVANCING,
LAN966X_TAPRIO_STATE_PENDING,
LAN966X_TAPRIO_STATE_OPERATING,
LAN966X_TAPRIO_STATE_TERMINATING,
LAN966X_TAPRIO_STATE_MAX,
};
end = jiffies + msecs_to_jiffies(LAN966X_TAPRIO_TIMEOUT_MS); /* It is required to try multiple times to set the state of list, * because the HW can overwrite this.
*/ do {
state = lan966x_taprio_list_state_get(port);
pending = false;
operating = false;
if (state == LAN966X_TAPRIO_STATE_ADVANCING ||
state == LAN966X_TAPRIO_STATE_PENDING) {
lan966x_taprio_list_state_set(port,
LAN966X_TAPRIO_STATE_ADMIN);
pending = true;
}
/* If the entry was in pending and now gets in admin, then there * is nothing else to do, so just bail out
*/
state = lan966x_taprio_list_state_get(port); if (pending &&
state == LAN966X_TAPRIO_STATE_ADMIN) return 0;
/* If the list was in operating and now is in terminating or * admin, then is OK to exit but it needs to wait until the list * will get in admin. It is not required to set the state * again.
*/ if (operating &&
(state == LAN966X_TAPRIO_STATE_TERMINATING ||
state == LAN966X_TAPRIO_STATE_ADMIN)) break;
} while (!time_after(jiffies, end));
end = jiffies + msecs_to_jiffies(LAN966X_TAPRIO_TIMEOUT_MS); do {
state = lan966x_taprio_list_state_get(port); if (state == LAN966X_TAPRIO_STATE_ADMIN) break;
} while (!time_after(jiffies, end));
/* If the list was in operating mode, it could be stopped while some * queues where closed, so make sure to restore "all-queues-open"
*/ if (operating) {
lan_wr(QSYS_TAS_GS_CTRL_HSCH_POS_SET(port->chip_port),
lan966x, QSYS_TAS_GS_CTRL);
staticint lan966x_taprio_shutdown(struct lan966x_port *port)
{
u32 i, list, state; int err;
for (i = 0; i < LAN966X_TAPRIO_ENTRIES_PER_PORT; ++i) {
list = lan966x_taprio_list_index(port, i);
state = lan966x_taprio_list_index_state_get(port, list); if (state == LAN966X_TAPRIO_STATE_ADMIN) continue;
err = lan966x_taprio_list_shutdown(port, list); if (err) return err;
}
return 0;
}
/* Find a suitable list for a new schedule. First priority is a list in state * pending. Second priority is a list in state admin.
*/ staticint lan966x_taprio_find_list(struct lan966x_port *port, struct tc_taprio_qopt_offload *qopt, int *new_list, int *obs_list)
{ int state[LAN966X_TAPRIO_ENTRIES_PER_PORT]; int list[LAN966X_TAPRIO_ENTRIES_PER_PORT]; int err, oper = -1;
u32 i;
*new_list = -1;
*obs_list = -1;
/* If there is already an entry in operating mode, return this list in * obs_list, such that when the new list will get activated the * operating list will be stopped. In this way is possible to have * smooth transitions between the lists
*/ for (i = 0; i < LAN966X_TAPRIO_ENTRIES_PER_PORT; ++i) {
list[i] = lan966x_taprio_list_index(port, i);
state[i] = lan966x_taprio_list_index_state_get(port, list[i]); if (state[i] == LAN966X_TAPRIO_STATE_OPERATING)
oper = list[i];
}
for (i = 0; i < LAN966X_TAPRIO_ENTRIES_PER_PORT; ++i) { if (state[i] == LAN966X_TAPRIO_STATE_PENDING) {
err = lan966x_taprio_shutdown(port); if (err) return err;
/* This is not supported by th HW */ if (qopt->cycle_time_extension) return -EOPNOTSUPP;
/* There is a limited number of gcl entries that can be used, they are * shared by all ports
*/ if (qopt->num_entries > LAN966X_TAPRIO_NUM_GCL) return -EINVAL;
/* Don't allow cycle times bigger than 1 sec or smaller than 1 usec */ if (qopt->cycle_time < LAN966X_TAPRIO_MIN_CYCLE_TIME_NS ||
qopt->cycle_time > LAN966X_TAPRIO_MAX_CYCLE_TIME_NS) return -EINVAL;
for (i = 0; i < qopt->num_entries; ++i) { struct tc_taprio_sched_entry *entry = &qopt->entries[i];
/* Don't allow intervals bigger than 1 sec or smaller than 1 * usec
*/ if (entry->interval < LAN966X_TAPRIO_MIN_CYCLE_TIME_NS ||
entry->interval > LAN966X_TAPRIO_MAX_CYCLE_TIME_NS) return -EINVAL;
if (qopt->entries[i].command != TC_TAPRIO_CMD_SET_GATES) return -EINVAL;
total_time += qopt->entries[i].interval;
}
/* Don't allow the total time of intervals be bigger than 1 sec */ if (total_time > LAN966X_TAPRIO_MAX_CYCLE_TIME_NS) return -EINVAL;
/* The HW expects that the cycle time to be at least as big as sum of * each interval of gcl
*/ if (qopt->cycle_time < total_time) return -EINVAL;
/* By default everything is free */
bitmap_fill(free_list, LAN966X_TAPRIO_NUM_GCL);
num_free = LAN966X_TAPRIO_NUM_GCL;
/* Iterate over all gcl entries and find out which are free. And mark * those that are not free.
*/
max_list = lan966x->num_phys_ports * LAN966X_TAPRIO_ENTRIES_PER_PORT; for (list = 0; list < max_list; ++list) {
state = lan966x_taprio_list_index_state_get(port, list); if (state == LAN966X_TAPRIO_STATE_ADMIN) continue;
base = lan_rd(lan966x, QSYS_TAS_LIST_CFG);
base = QSYS_TAS_LIST_CFG_LIST_BASE_ADDR_GET(base);
next = base;
if (lan966x_taprio_gcl_free_get(port, free_list) < qopt->num_entries) return -ENOSPC;
/* Select list */
lan_rmw(QSYS_TAS_CFG_CTRL_LIST_NUM_SET(list),
QSYS_TAS_CFG_CTRL_LIST_NUM,
lan966x, QSYS_TAS_CFG_CTRL);
/* Setup the address of the first gcl entry */
base = find_first_bit(free_list, LAN966X_TAPRIO_NUM_GCL);
lan_rmw(QSYS_TAS_LIST_CFG_LIST_BASE_ADDR_SET(base),
QSYS_TAS_LIST_CFG_LIST_BASE_ADDR,
lan966x, QSYS_TAS_LIST_CFG);
/* Iterate over entries and add them to the gcl list */
next = base; for (i = 0; i < qopt->num_entries; ++i) {
lan_rmw(QSYS_TAS_CFG_CTRL_GCL_ENTRY_NUM_SET(next),
QSYS_TAS_CFG_CTRL_GCL_ENTRY_NUM,
lan966x, QSYS_TAS_CFG_CTRL);
/* If the entry is last, point back to the start of the list */ if (i == qopt->num_entries - 1)
next = base; else
next = find_next_bit(free_list, LAN966X_TAPRIO_NUM_GCL,
next + 1);
/* Calculate new base_time based on cycle_time. The HW recommends to have the * new base time at least 2 * cycle type + current time
*/ staticvoid lan966x_taprio_new_base_time(struct lan966x *lan966x, const u32 cycle_time, const ktime_t org_base_time,
ktime_t *new_base_time)
{
ktime_t current_time, threshold_time; struct timespec64 ts;
/* Get the current time and calculate the threshold_time */
lan966x_ptp_gettime64(&lan966x->phc[LAN966X_PHC_PORT].info, &ts);
current_time = timespec64_to_ktime(ts);
threshold_time = current_time + (2 * cycle_time);
/* If the org_base_time is in enough in future just use it */ if (org_base_time >= threshold_time) {
*new_base_time = org_base_time; return;
}
/* If the org_base_time is smaller than current_time, calculate the new * base time as following.
*/ if (org_base_time <= current_time) {
u64 tmp = current_time - org_base_time;
u32 rem = 0;
if (tmp > cycle_time)
div_u64_rem(tmp, cycle_time, &rem);
rem = cycle_time - rem;
*new_base_time = threshold_time + rem; return;
}
/* The only left place for org_base_time is between current_time and * threshold_time. In this case the new_base_time is calculated like * org_base_time + 2 * cycletime
*/
*new_base_time = org_base_time + 2 * cycle_time;
}
int lan966x_taprio_speed_set(struct lan966x_port *port, int speed)
{ struct lan966x *lan966x = port->lan966x;
u8 taprio_speed;
switch (speed) { case SPEED_10:
taprio_speed = LAN966X_TAPRIO_SPEED_10; break; case SPEED_100:
taprio_speed = LAN966X_TAPRIO_SPEED_100; break; case SPEED_1000:
taprio_speed = LAN966X_TAPRIO_SPEED_1000; break; case SPEED_2500:
taprio_speed = LAN966X_TAPRIO_SPEED_2500; break; default: return -EINVAL;
}
/* For now we always use guard band on all queues */
lan_rmw(QSYS_TAS_CFG_CTRL_LIST_NUM_MAX_SET(num_taprio_lists) |
QSYS_TAS_CFG_CTRL_ALWAYS_GB_SCH_Q_SET(1),
QSYS_TAS_CFG_CTRL_LIST_NUM_MAX |
QSYS_TAS_CFG_CTRL_ALWAYS_GB_SCH_Q,
lan966x, QSYS_TAS_CFG_CTRL);
for (p = 0; p < lan966x->num_phys_ports; p++)
lan_rmw(QSYS_TAS_PROFILE_CFG_PORT_NUM_SET(p),
QSYS_TAS_PROFILE_CFG_PORT_NUM,
lan966x, QSYS_TAS_PROFILE_CFG(p));
}
void lan966x_taprio_deinit(struct lan966x *lan966x)
{ int p;
for (p = 0; p < lan966x->num_phys_ports; ++p) { if (!lan966x->ports[p]) continue;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.