162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include "lan966x_main.h"
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#define LAN966X_TAPRIO_TIMEOUT_MS		1000
662306a36Sopenharmony_ci#define LAN966X_TAPRIO_ENTRIES_PER_PORT		2
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/* Minimum supported cycle time in nanoseconds */
962306a36Sopenharmony_ci#define LAN966X_TAPRIO_MIN_CYCLE_TIME_NS	NSEC_PER_USEC
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/* Maximum supported cycle time in nanoseconds */
1262306a36Sopenharmony_ci#define LAN966X_TAPRIO_MAX_CYCLE_TIME_NS	(NSEC_PER_SEC - 1)
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/* Total number of TAS GCL entries */
1562306a36Sopenharmony_ci#define LAN966X_TAPRIO_NUM_GCL			256
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* TAPRIO link speeds for calculation of guard band */
1862306a36Sopenharmony_cienum lan966x_taprio_link_speed {
1962306a36Sopenharmony_ci	LAN966X_TAPRIO_SPEED_NO_GB,
2062306a36Sopenharmony_ci	LAN966X_TAPRIO_SPEED_10,
2162306a36Sopenharmony_ci	LAN966X_TAPRIO_SPEED_100,
2262306a36Sopenharmony_ci	LAN966X_TAPRIO_SPEED_1000,
2362306a36Sopenharmony_ci	LAN966X_TAPRIO_SPEED_2500,
2462306a36Sopenharmony_ci};
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/* TAPRIO list states */
2762306a36Sopenharmony_cienum lan966x_taprio_state {
2862306a36Sopenharmony_ci	LAN966X_TAPRIO_STATE_ADMIN,
2962306a36Sopenharmony_ci	LAN966X_TAPRIO_STATE_ADVANCING,
3062306a36Sopenharmony_ci	LAN966X_TAPRIO_STATE_PENDING,
3162306a36Sopenharmony_ci	LAN966X_TAPRIO_STATE_OPERATING,
3262306a36Sopenharmony_ci	LAN966X_TAPRIO_STATE_TERMINATING,
3362306a36Sopenharmony_ci	LAN966X_TAPRIO_STATE_MAX,
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/* TAPRIO GCL command */
3762306a36Sopenharmony_cienum lan966x_taprio_gcl_cmd {
3862306a36Sopenharmony_ci	LAN966X_TAPRIO_GCL_CMD_SET_GATE_STATES = 0,
3962306a36Sopenharmony_ci};
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic u32 lan966x_taprio_list_index(struct lan966x_port *port, u8 entry)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	return port->chip_port * LAN966X_TAPRIO_ENTRIES_PER_PORT + entry;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic u32 lan966x_taprio_list_state_get(struct lan966x_port *port)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct lan966x *lan966x = port->lan966x;
4962306a36Sopenharmony_ci	u32 val;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	val = lan_rd(lan966x, QSYS_TAS_LST);
5262306a36Sopenharmony_ci	return QSYS_TAS_LST_LIST_STATE_GET(val);
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic u32 lan966x_taprio_list_index_state_get(struct lan966x_port *port,
5662306a36Sopenharmony_ci					       u32 list)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	struct lan966x *lan966x = port->lan966x;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	lan_rmw(QSYS_TAS_CFG_CTRL_LIST_NUM_SET(list),
6162306a36Sopenharmony_ci		QSYS_TAS_CFG_CTRL_LIST_NUM,
6262306a36Sopenharmony_ci		lan966x, QSYS_TAS_CFG_CTRL);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	return lan966x_taprio_list_state_get(port);
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic void lan966x_taprio_list_state_set(struct lan966x_port *port,
6862306a36Sopenharmony_ci					  u32 state)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	struct lan966x *lan966x = port->lan966x;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	lan_rmw(QSYS_TAS_LST_LIST_STATE_SET(state),
7362306a36Sopenharmony_ci		QSYS_TAS_LST_LIST_STATE,
7462306a36Sopenharmony_ci		lan966x, QSYS_TAS_LST);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic int lan966x_taprio_list_shutdown(struct lan966x_port *port,
7862306a36Sopenharmony_ci					u32 list)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct lan966x *lan966x = port->lan966x;
8162306a36Sopenharmony_ci	bool pending, operating;
8262306a36Sopenharmony_ci	unsigned long end;
8362306a36Sopenharmony_ci	u32 state;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	end = jiffies +  msecs_to_jiffies(LAN966X_TAPRIO_TIMEOUT_MS);
8662306a36Sopenharmony_ci	/* It is required to try multiple times to set the state of list,
8762306a36Sopenharmony_ci	 * because the HW can overwrite this.
8862306a36Sopenharmony_ci	 */
8962306a36Sopenharmony_ci	do {
9062306a36Sopenharmony_ci		state = lan966x_taprio_list_state_get(port);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci		pending = false;
9362306a36Sopenharmony_ci		operating = false;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci		if (state == LAN966X_TAPRIO_STATE_ADVANCING ||
9662306a36Sopenharmony_ci		    state == LAN966X_TAPRIO_STATE_PENDING) {
9762306a36Sopenharmony_ci			lan966x_taprio_list_state_set(port,
9862306a36Sopenharmony_ci						      LAN966X_TAPRIO_STATE_ADMIN);
9962306a36Sopenharmony_ci			pending = true;
10062306a36Sopenharmony_ci		}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci		if (state == LAN966X_TAPRIO_STATE_OPERATING) {
10362306a36Sopenharmony_ci			lan966x_taprio_list_state_set(port,
10462306a36Sopenharmony_ci						      LAN966X_TAPRIO_STATE_TERMINATING);
10562306a36Sopenharmony_ci			operating = true;
10662306a36Sopenharmony_ci		}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci		/* If the entry was in pending and now gets in admin, then there
10962306a36Sopenharmony_ci		 * is nothing else to do, so just bail out
11062306a36Sopenharmony_ci		 */
11162306a36Sopenharmony_ci		state = lan966x_taprio_list_state_get(port);
11262306a36Sopenharmony_ci		if (pending &&
11362306a36Sopenharmony_ci		    state == LAN966X_TAPRIO_STATE_ADMIN)
11462306a36Sopenharmony_ci			return 0;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci		/* If the list was in operating and now is in terminating or
11762306a36Sopenharmony_ci		 * admin, then is OK to exit but it needs to wait until the list
11862306a36Sopenharmony_ci		 * will get in admin. It is not required to set the state
11962306a36Sopenharmony_ci		 * again.
12062306a36Sopenharmony_ci		 */
12162306a36Sopenharmony_ci		if (operating &&
12262306a36Sopenharmony_ci		    (state == LAN966X_TAPRIO_STATE_TERMINATING ||
12362306a36Sopenharmony_ci		     state == LAN966X_TAPRIO_STATE_ADMIN))
12462306a36Sopenharmony_ci			break;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	} while (!time_after(jiffies, end));
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	end = jiffies + msecs_to_jiffies(LAN966X_TAPRIO_TIMEOUT_MS);
12962306a36Sopenharmony_ci	do {
13062306a36Sopenharmony_ci		state = lan966x_taprio_list_state_get(port);
13162306a36Sopenharmony_ci		if (state == LAN966X_TAPRIO_STATE_ADMIN)
13262306a36Sopenharmony_ci			break;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	} while (!time_after(jiffies, end));
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/* If the list was in operating mode, it could be stopped while some
13762306a36Sopenharmony_ci	 * queues where closed, so make sure to restore "all-queues-open"
13862306a36Sopenharmony_ci	 */
13962306a36Sopenharmony_ci	if (operating) {
14062306a36Sopenharmony_ci		lan_wr(QSYS_TAS_GS_CTRL_HSCH_POS_SET(port->chip_port),
14162306a36Sopenharmony_ci		       lan966x, QSYS_TAS_GS_CTRL);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci		lan_wr(QSYS_TAS_GATE_STATE_TAS_GATE_STATE_SET(0xff),
14462306a36Sopenharmony_ci		       lan966x, QSYS_TAS_GATE_STATE);
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	return 0;
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic int lan966x_taprio_shutdown(struct lan966x_port *port)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	u32 i, list, state;
15362306a36Sopenharmony_ci	int err;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	for (i = 0; i < LAN966X_TAPRIO_ENTRIES_PER_PORT; ++i) {
15662306a36Sopenharmony_ci		list = lan966x_taprio_list_index(port, i);
15762306a36Sopenharmony_ci		state = lan966x_taprio_list_index_state_get(port, list);
15862306a36Sopenharmony_ci		if (state == LAN966X_TAPRIO_STATE_ADMIN)
15962306a36Sopenharmony_ci			continue;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci		err = lan966x_taprio_list_shutdown(port, list);
16262306a36Sopenharmony_ci		if (err)
16362306a36Sopenharmony_ci			return err;
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	return 0;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci/* Find a suitable list for a new schedule. First priority is a list in state
17062306a36Sopenharmony_ci * pending. Second priority is a list in state admin.
17162306a36Sopenharmony_ci */
17262306a36Sopenharmony_cistatic int lan966x_taprio_find_list(struct lan966x_port *port,
17362306a36Sopenharmony_ci				    struct tc_taprio_qopt_offload *qopt,
17462306a36Sopenharmony_ci				    int *new_list, int *obs_list)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	int state[LAN966X_TAPRIO_ENTRIES_PER_PORT];
17762306a36Sopenharmony_ci	int list[LAN966X_TAPRIO_ENTRIES_PER_PORT];
17862306a36Sopenharmony_ci	int err, oper = -1;
17962306a36Sopenharmony_ci	u32 i;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	*new_list = -1;
18262306a36Sopenharmony_ci	*obs_list = -1;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	/* If there is already an entry in operating mode, return this list in
18562306a36Sopenharmony_ci	 * obs_list, such that when the new list will get activated the
18662306a36Sopenharmony_ci	 * operating list will be stopped. In this way is possible to have
18762306a36Sopenharmony_ci	 * smooth transitions between the lists
18862306a36Sopenharmony_ci	 */
18962306a36Sopenharmony_ci	for (i = 0; i < LAN966X_TAPRIO_ENTRIES_PER_PORT; ++i) {
19062306a36Sopenharmony_ci		list[i] = lan966x_taprio_list_index(port, i);
19162306a36Sopenharmony_ci		state[i] = lan966x_taprio_list_index_state_get(port, list[i]);
19262306a36Sopenharmony_ci		if (state[i] == LAN966X_TAPRIO_STATE_OPERATING)
19362306a36Sopenharmony_ci			oper = list[i];
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	for (i = 0; i < LAN966X_TAPRIO_ENTRIES_PER_PORT; ++i) {
19762306a36Sopenharmony_ci		if (state[i] == LAN966X_TAPRIO_STATE_PENDING) {
19862306a36Sopenharmony_ci			err = lan966x_taprio_shutdown(port);
19962306a36Sopenharmony_ci			if (err)
20062306a36Sopenharmony_ci				return err;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci			*new_list = list[i];
20362306a36Sopenharmony_ci			*obs_list = (oper == -1) ? *new_list : oper;
20462306a36Sopenharmony_ci			return 0;
20562306a36Sopenharmony_ci		}
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	for (i = 0; i < LAN966X_TAPRIO_ENTRIES_PER_PORT; ++i) {
20962306a36Sopenharmony_ci		if (state[i] == LAN966X_TAPRIO_STATE_ADMIN) {
21062306a36Sopenharmony_ci			*new_list = list[i];
21162306a36Sopenharmony_ci			*obs_list = (oper == -1) ? *new_list : oper;
21262306a36Sopenharmony_ci			return 0;
21362306a36Sopenharmony_ci		}
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	return -ENOSPC;
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic int lan966x_taprio_check(struct tc_taprio_qopt_offload *qopt)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	u64 total_time = 0;
22262306a36Sopenharmony_ci	u32 i;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	/* This is not supported by th HW */
22562306a36Sopenharmony_ci	if (qopt->cycle_time_extension)
22662306a36Sopenharmony_ci		return -EOPNOTSUPP;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/* There is a limited number of gcl entries that can be used, they are
22962306a36Sopenharmony_ci	 * shared by all ports
23062306a36Sopenharmony_ci	 */
23162306a36Sopenharmony_ci	if (qopt->num_entries > LAN966X_TAPRIO_NUM_GCL)
23262306a36Sopenharmony_ci		return -EINVAL;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	/* Don't allow cycle times bigger than 1 sec or smaller than 1 usec */
23562306a36Sopenharmony_ci	if (qopt->cycle_time < LAN966X_TAPRIO_MIN_CYCLE_TIME_NS ||
23662306a36Sopenharmony_ci	    qopt->cycle_time > LAN966X_TAPRIO_MAX_CYCLE_TIME_NS)
23762306a36Sopenharmony_ci		return -EINVAL;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	for (i = 0; i < qopt->num_entries; ++i) {
24062306a36Sopenharmony_ci		struct tc_taprio_sched_entry *entry = &qopt->entries[i];
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci		/* Don't allow intervals bigger than 1 sec or smaller than 1
24362306a36Sopenharmony_ci		 * usec
24462306a36Sopenharmony_ci		 */
24562306a36Sopenharmony_ci		if (entry->interval < LAN966X_TAPRIO_MIN_CYCLE_TIME_NS ||
24662306a36Sopenharmony_ci		    entry->interval > LAN966X_TAPRIO_MAX_CYCLE_TIME_NS)
24762306a36Sopenharmony_ci			return -EINVAL;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci		if (qopt->entries[i].command != TC_TAPRIO_CMD_SET_GATES)
25062306a36Sopenharmony_ci			return -EINVAL;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci		total_time += qopt->entries[i].interval;
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	/* Don't allow the total time of intervals be bigger than 1 sec */
25662306a36Sopenharmony_ci	if (total_time > LAN966X_TAPRIO_MAX_CYCLE_TIME_NS)
25762306a36Sopenharmony_ci		return -EINVAL;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/* The HW expects that the cycle time to be at least as big as sum of
26062306a36Sopenharmony_ci	 * each interval of gcl
26162306a36Sopenharmony_ci	 */
26262306a36Sopenharmony_ci	if (qopt->cycle_time < total_time)
26362306a36Sopenharmony_ci		return -EINVAL;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	return 0;
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic int lan966x_taprio_gcl_free_get(struct lan966x_port *port,
26962306a36Sopenharmony_ci				       unsigned long *free_list)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	struct lan966x *lan966x = port->lan966x;
27262306a36Sopenharmony_ci	u32 num_free, state, list;
27362306a36Sopenharmony_ci	u32 base, next, max_list;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	/* By default everything is free */
27662306a36Sopenharmony_ci	bitmap_fill(free_list, LAN966X_TAPRIO_NUM_GCL);
27762306a36Sopenharmony_ci	num_free = LAN966X_TAPRIO_NUM_GCL;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	/* Iterate over all gcl entries and find out which are free. And mark
28062306a36Sopenharmony_ci	 * those that are not free.
28162306a36Sopenharmony_ci	 */
28262306a36Sopenharmony_ci	max_list = lan966x->num_phys_ports * LAN966X_TAPRIO_ENTRIES_PER_PORT;
28362306a36Sopenharmony_ci	for (list = 0; list < max_list; ++list) {
28462306a36Sopenharmony_ci		state = lan966x_taprio_list_index_state_get(port, list);
28562306a36Sopenharmony_ci		if (state == LAN966X_TAPRIO_STATE_ADMIN)
28662306a36Sopenharmony_ci			continue;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci		base = lan_rd(lan966x, QSYS_TAS_LIST_CFG);
28962306a36Sopenharmony_ci		base = QSYS_TAS_LIST_CFG_LIST_BASE_ADDR_GET(base);
29062306a36Sopenharmony_ci		next = base;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci		do {
29362306a36Sopenharmony_ci			clear_bit(next, free_list);
29462306a36Sopenharmony_ci			num_free--;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci			lan_rmw(QSYS_TAS_CFG_CTRL_GCL_ENTRY_NUM_SET(next),
29762306a36Sopenharmony_ci				QSYS_TAS_CFG_CTRL_GCL_ENTRY_NUM,
29862306a36Sopenharmony_ci				lan966x, QSYS_TAS_CFG_CTRL);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci			next = lan_rd(lan966x, QSYS_TAS_GCL_CT_CFG2);
30162306a36Sopenharmony_ci			next = QSYS_TAS_GCL_CT_CFG2_NEXT_GCL_GET(next);
30262306a36Sopenharmony_ci		} while (base != next);
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	return num_free;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic void lan966x_taprio_gcl_setup_entry(struct lan966x_port *port,
30962306a36Sopenharmony_ci					   struct tc_taprio_sched_entry *entry,
31062306a36Sopenharmony_ci					   u32 next_entry)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	struct lan966x *lan966x = port->lan966x;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	/* Setup a single gcl entry */
31562306a36Sopenharmony_ci	lan_wr(QSYS_TAS_GCL_CT_CFG_GATE_STATE_SET(entry->gate_mask) |
31662306a36Sopenharmony_ci	       QSYS_TAS_GCL_CT_CFG_HSCH_POS_SET(port->chip_port) |
31762306a36Sopenharmony_ci	       QSYS_TAS_GCL_CT_CFG_OP_TYPE_SET(LAN966X_TAPRIO_GCL_CMD_SET_GATE_STATES),
31862306a36Sopenharmony_ci	       lan966x, QSYS_TAS_GCL_CT_CFG);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	lan_wr(QSYS_TAS_GCL_CT_CFG2_PORT_PROFILE_SET(port->chip_port) |
32162306a36Sopenharmony_ci	       QSYS_TAS_GCL_CT_CFG2_NEXT_GCL_SET(next_entry),
32262306a36Sopenharmony_ci	       lan966x, QSYS_TAS_GCL_CT_CFG2);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	lan_wr(entry->interval, lan966x, QSYS_TAS_GCL_TM_CFG);
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistatic int lan966x_taprio_gcl_setup(struct lan966x_port *port,
32862306a36Sopenharmony_ci				    struct tc_taprio_qopt_offload *qopt,
32962306a36Sopenharmony_ci				    int list)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	DECLARE_BITMAP(free_list, LAN966X_TAPRIO_NUM_GCL);
33262306a36Sopenharmony_ci	struct lan966x *lan966x = port->lan966x;
33362306a36Sopenharmony_ci	u32 i, base, next;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	if (lan966x_taprio_gcl_free_get(port, free_list) < qopt->num_entries)
33662306a36Sopenharmony_ci		return -ENOSPC;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	/* Select list */
33962306a36Sopenharmony_ci	lan_rmw(QSYS_TAS_CFG_CTRL_LIST_NUM_SET(list),
34062306a36Sopenharmony_ci		QSYS_TAS_CFG_CTRL_LIST_NUM,
34162306a36Sopenharmony_ci		lan966x, QSYS_TAS_CFG_CTRL);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	/* Setup the address of the first gcl entry */
34462306a36Sopenharmony_ci	base = find_first_bit(free_list, LAN966X_TAPRIO_NUM_GCL);
34562306a36Sopenharmony_ci	lan_rmw(QSYS_TAS_LIST_CFG_LIST_BASE_ADDR_SET(base),
34662306a36Sopenharmony_ci		QSYS_TAS_LIST_CFG_LIST_BASE_ADDR,
34762306a36Sopenharmony_ci		lan966x, QSYS_TAS_LIST_CFG);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	/* Iterate over entries and add them to the gcl list */
35062306a36Sopenharmony_ci	next = base;
35162306a36Sopenharmony_ci	for (i = 0; i < qopt->num_entries; ++i) {
35262306a36Sopenharmony_ci		lan_rmw(QSYS_TAS_CFG_CTRL_GCL_ENTRY_NUM_SET(next),
35362306a36Sopenharmony_ci			QSYS_TAS_CFG_CTRL_GCL_ENTRY_NUM,
35462306a36Sopenharmony_ci			lan966x, QSYS_TAS_CFG_CTRL);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci		/* If the entry is last, point back to the start of the list */
35762306a36Sopenharmony_ci		if (i == qopt->num_entries - 1)
35862306a36Sopenharmony_ci			next = base;
35962306a36Sopenharmony_ci		else
36062306a36Sopenharmony_ci			next = find_next_bit(free_list, LAN966X_TAPRIO_NUM_GCL,
36162306a36Sopenharmony_ci					     next + 1);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci		lan966x_taprio_gcl_setup_entry(port, &qopt->entries[i], next);
36462306a36Sopenharmony_ci	}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	return 0;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci/* Calculate new base_time based on cycle_time. The HW recommends to have the
37062306a36Sopenharmony_ci * new base time at least 2 * cycle type + current time
37162306a36Sopenharmony_ci */
37262306a36Sopenharmony_cistatic void lan966x_taprio_new_base_time(struct lan966x *lan966x,
37362306a36Sopenharmony_ci					 const u32 cycle_time,
37462306a36Sopenharmony_ci					 const ktime_t org_base_time,
37562306a36Sopenharmony_ci					 ktime_t *new_base_time)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	ktime_t current_time, threshold_time;
37862306a36Sopenharmony_ci	struct timespec64 ts;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	/* Get the current time and calculate the threshold_time */
38162306a36Sopenharmony_ci	lan966x_ptp_gettime64(&lan966x->phc[LAN966X_PHC_PORT].info, &ts);
38262306a36Sopenharmony_ci	current_time = timespec64_to_ktime(ts);
38362306a36Sopenharmony_ci	threshold_time = current_time + (2 * cycle_time);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	/* If the org_base_time is in enough in future just use it */
38662306a36Sopenharmony_ci	if (org_base_time >= threshold_time) {
38762306a36Sopenharmony_ci		*new_base_time = org_base_time;
38862306a36Sopenharmony_ci		return;
38962306a36Sopenharmony_ci	}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	/* If the org_base_time is smaller than current_time, calculate the new
39262306a36Sopenharmony_ci	 * base time as following.
39362306a36Sopenharmony_ci	 */
39462306a36Sopenharmony_ci	if (org_base_time <= current_time) {
39562306a36Sopenharmony_ci		u64 tmp = current_time - org_base_time;
39662306a36Sopenharmony_ci		u32 rem = 0;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci		if (tmp > cycle_time)
39962306a36Sopenharmony_ci			div_u64_rem(tmp, cycle_time, &rem);
40062306a36Sopenharmony_ci		rem = cycle_time - rem;
40162306a36Sopenharmony_ci		*new_base_time = threshold_time + rem;
40262306a36Sopenharmony_ci		return;
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	/* The only left place for org_base_time is between current_time and
40662306a36Sopenharmony_ci	 * threshold_time. In this case the new_base_time is calculated like
40762306a36Sopenharmony_ci	 * org_base_time + 2 * cycletime
40862306a36Sopenharmony_ci	 */
40962306a36Sopenharmony_ci	*new_base_time = org_base_time + 2 * cycle_time;
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ciint lan966x_taprio_speed_set(struct lan966x_port *port, int speed)
41362306a36Sopenharmony_ci{
41462306a36Sopenharmony_ci	struct lan966x *lan966x = port->lan966x;
41562306a36Sopenharmony_ci	u8 taprio_speed;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	switch (speed) {
41862306a36Sopenharmony_ci	case SPEED_10:
41962306a36Sopenharmony_ci		taprio_speed = LAN966X_TAPRIO_SPEED_10;
42062306a36Sopenharmony_ci		break;
42162306a36Sopenharmony_ci	case SPEED_100:
42262306a36Sopenharmony_ci		taprio_speed = LAN966X_TAPRIO_SPEED_100;
42362306a36Sopenharmony_ci		break;
42462306a36Sopenharmony_ci	case SPEED_1000:
42562306a36Sopenharmony_ci		taprio_speed = LAN966X_TAPRIO_SPEED_1000;
42662306a36Sopenharmony_ci		break;
42762306a36Sopenharmony_ci	case SPEED_2500:
42862306a36Sopenharmony_ci		taprio_speed = LAN966X_TAPRIO_SPEED_2500;
42962306a36Sopenharmony_ci		break;
43062306a36Sopenharmony_ci	default:
43162306a36Sopenharmony_ci		return -EINVAL;
43262306a36Sopenharmony_ci	}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	lan_rmw(QSYS_TAS_PROFILE_CFG_LINK_SPEED_SET(taprio_speed),
43562306a36Sopenharmony_ci		QSYS_TAS_PROFILE_CFG_LINK_SPEED,
43662306a36Sopenharmony_ci		lan966x, QSYS_TAS_PROFILE_CFG(port->chip_port));
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	return 0;
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ciint lan966x_taprio_add(struct lan966x_port *port,
44262306a36Sopenharmony_ci		       struct tc_taprio_qopt_offload *qopt)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	struct lan966x *lan966x = port->lan966x;
44562306a36Sopenharmony_ci	int err, new_list, obs_list;
44662306a36Sopenharmony_ci	struct timespec64 ts;
44762306a36Sopenharmony_ci	ktime_t base_time;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	err = lan966x_taprio_check(qopt);
45062306a36Sopenharmony_ci	if (err)
45162306a36Sopenharmony_ci		return err;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	err = lan966x_taprio_find_list(port, qopt, &new_list, &obs_list);
45462306a36Sopenharmony_ci	if (err)
45562306a36Sopenharmony_ci		return err;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	err = lan966x_taprio_gcl_setup(port, qopt, new_list);
45862306a36Sopenharmony_ci	if (err)
45962306a36Sopenharmony_ci		return err;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	lan966x_taprio_new_base_time(lan966x, qopt->cycle_time,
46262306a36Sopenharmony_ci				     qopt->base_time, &base_time);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	ts = ktime_to_timespec64(base_time);
46562306a36Sopenharmony_ci	lan_wr(QSYS_TAS_BT_NSEC_NSEC_SET(ts.tv_nsec),
46662306a36Sopenharmony_ci	       lan966x, QSYS_TAS_BT_NSEC);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	lan_wr(lower_32_bits(ts.tv_sec),
46962306a36Sopenharmony_ci	       lan966x, QSYS_TAS_BT_SEC_LSB);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	lan_wr(QSYS_TAS_BT_SEC_MSB_SEC_MSB_SET(upper_32_bits(ts.tv_sec)),
47262306a36Sopenharmony_ci	       lan966x, QSYS_TAS_BT_SEC_MSB);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	lan_wr(qopt->cycle_time, lan966x, QSYS_TAS_CT_CFG);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	lan_rmw(QSYS_TAS_STARTUP_CFG_OBSOLETE_IDX_SET(obs_list),
47762306a36Sopenharmony_ci		QSYS_TAS_STARTUP_CFG_OBSOLETE_IDX,
47862306a36Sopenharmony_ci		lan966x, QSYS_TAS_STARTUP_CFG);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	/* Start list processing */
48162306a36Sopenharmony_ci	lan_rmw(QSYS_TAS_LST_LIST_STATE_SET(LAN966X_TAPRIO_STATE_ADVANCING),
48262306a36Sopenharmony_ci		QSYS_TAS_LST_LIST_STATE,
48362306a36Sopenharmony_ci		lan966x, QSYS_TAS_LST);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	return err;
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ciint lan966x_taprio_del(struct lan966x_port *port)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	return lan966x_taprio_shutdown(port);
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_civoid lan966x_taprio_init(struct lan966x *lan966x)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	int num_taprio_lists;
49662306a36Sopenharmony_ci	int p;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	lan_wr(QSYS_TAS_STM_CFG_REVISIT_DLY_SET((256 * 1000) /
49962306a36Sopenharmony_ci						lan966x_ptp_get_period_ps()),
50062306a36Sopenharmony_ci	       lan966x, QSYS_TAS_STM_CFG);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	num_taprio_lists = lan966x->num_phys_ports *
50362306a36Sopenharmony_ci			   LAN966X_TAPRIO_ENTRIES_PER_PORT;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	/* For now we always use guard band on all queues */
50662306a36Sopenharmony_ci	lan_rmw(QSYS_TAS_CFG_CTRL_LIST_NUM_MAX_SET(num_taprio_lists) |
50762306a36Sopenharmony_ci		QSYS_TAS_CFG_CTRL_ALWAYS_GB_SCH_Q_SET(1),
50862306a36Sopenharmony_ci		QSYS_TAS_CFG_CTRL_LIST_NUM_MAX |
50962306a36Sopenharmony_ci		QSYS_TAS_CFG_CTRL_ALWAYS_GB_SCH_Q,
51062306a36Sopenharmony_ci		lan966x, QSYS_TAS_CFG_CTRL);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	for (p = 0; p < lan966x->num_phys_ports; p++)
51362306a36Sopenharmony_ci		lan_rmw(QSYS_TAS_PROFILE_CFG_PORT_NUM_SET(p),
51462306a36Sopenharmony_ci			QSYS_TAS_PROFILE_CFG_PORT_NUM,
51562306a36Sopenharmony_ci			lan966x, QSYS_TAS_PROFILE_CFG(p));
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_civoid lan966x_taprio_deinit(struct lan966x *lan966x)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	int p;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	for (p = 0; p < lan966x->num_phys_ports; ++p) {
52362306a36Sopenharmony_ci		if (!lan966x->ports[p])
52462306a36Sopenharmony_ci			continue;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci		lan966x_taprio_del(lan966x->ports[p]);
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci}
529