162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright (C) 2021 Gerhard Engleder <gerhard@engleder-embedded.com> */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include "tsnep.h"
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <net/pkt_sched.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/* save one operation at the end for additional operation at list change */
962306a36Sopenharmony_ci#define TSNEP_MAX_GCL_NUM (TSNEP_GCL_COUNT - 1)
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistatic int tsnep_validate_gcl(struct tc_taprio_qopt_offload *qopt)
1262306a36Sopenharmony_ci{
1362306a36Sopenharmony_ci	int i;
1462306a36Sopenharmony_ci	u64 cycle_time;
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci	if (!qopt->cycle_time)
1762306a36Sopenharmony_ci		return -ERANGE;
1862306a36Sopenharmony_ci	if (qopt->num_entries > TSNEP_MAX_GCL_NUM)
1962306a36Sopenharmony_ci		return -EINVAL;
2062306a36Sopenharmony_ci	cycle_time = 0;
2162306a36Sopenharmony_ci	for (i = 0; i < qopt->num_entries; i++) {
2262306a36Sopenharmony_ci		if (qopt->entries[i].command != TC_TAPRIO_CMD_SET_GATES)
2362306a36Sopenharmony_ci			return -EINVAL;
2462306a36Sopenharmony_ci		if (qopt->entries[i].gate_mask & ~TSNEP_GCL_MASK)
2562306a36Sopenharmony_ci			return -EINVAL;
2662306a36Sopenharmony_ci		if (qopt->entries[i].interval < TSNEP_GCL_MIN_INTERVAL)
2762306a36Sopenharmony_ci			return -EINVAL;
2862306a36Sopenharmony_ci		cycle_time += qopt->entries[i].interval;
2962306a36Sopenharmony_ci	}
3062306a36Sopenharmony_ci	if (qopt->cycle_time != cycle_time)
3162306a36Sopenharmony_ci		return -EINVAL;
3262306a36Sopenharmony_ci	if (qopt->cycle_time_extension >= qopt->cycle_time)
3362306a36Sopenharmony_ci		return -EINVAL;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	return 0;
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic void tsnep_write_gcl_operation(struct tsnep_gcl *gcl, int index,
3962306a36Sopenharmony_ci				      u32 properties, u32 interval, bool flush)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	void __iomem *addr = gcl->addr +
4262306a36Sopenharmony_ci			     sizeof(struct tsnep_gcl_operation) * index;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	gcl->operation[index].properties = properties;
4562306a36Sopenharmony_ci	gcl->operation[index].interval = interval;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	iowrite32(properties, addr);
4862306a36Sopenharmony_ci	iowrite32(interval, addr + sizeof(u32));
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	if (flush) {
5162306a36Sopenharmony_ci		/* flush write with read access */
5262306a36Sopenharmony_ci		ioread32(addr);
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic u64 tsnep_change_duration(struct tsnep_gcl *gcl, int index)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	u64 duration;
5962306a36Sopenharmony_ci	int count;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	/* change needs to be triggered one or two operations before start of
6262306a36Sopenharmony_ci	 * new gate control list
6362306a36Sopenharmony_ci	 * - change is triggered at start of operation (minimum one operation)
6462306a36Sopenharmony_ci	 * - operation with adjusted interval is inserted on demand to exactly
6562306a36Sopenharmony_ci	 *   meet the start of the new gate control list (optional)
6662306a36Sopenharmony_ci	 *
6762306a36Sopenharmony_ci	 * additionally properties are read directly after start of previous
6862306a36Sopenharmony_ci	 * operation
6962306a36Sopenharmony_ci	 *
7062306a36Sopenharmony_ci	 * therefore, three operations needs to be considered for the limit
7162306a36Sopenharmony_ci	 */
7262306a36Sopenharmony_ci	duration = 0;
7362306a36Sopenharmony_ci	count = 3;
7462306a36Sopenharmony_ci	while (count) {
7562306a36Sopenharmony_ci		duration += gcl->operation[index].interval;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci		index--;
7862306a36Sopenharmony_ci		if (index < 0)
7962306a36Sopenharmony_ci			index = gcl->count - 1;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci		count--;
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	return duration;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic void tsnep_write_gcl(struct tsnep_gcl *gcl,
8862306a36Sopenharmony_ci			    struct tc_taprio_qopt_offload *qopt)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	int i;
9162306a36Sopenharmony_ci	u32 properties;
9262306a36Sopenharmony_ci	u64 extend;
9362306a36Sopenharmony_ci	u64 cut;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	gcl->base_time = ktime_to_ns(qopt->base_time);
9662306a36Sopenharmony_ci	gcl->cycle_time = qopt->cycle_time;
9762306a36Sopenharmony_ci	gcl->cycle_time_extension = qopt->cycle_time_extension;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	for (i = 0; i < qopt->num_entries; i++) {
10062306a36Sopenharmony_ci		properties = qopt->entries[i].gate_mask;
10162306a36Sopenharmony_ci		if (i == (qopt->num_entries - 1))
10262306a36Sopenharmony_ci			properties |= TSNEP_GCL_LAST;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci		tsnep_write_gcl_operation(gcl, i, properties,
10562306a36Sopenharmony_ci					  qopt->entries[i].interval, true);
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci	gcl->count = qopt->num_entries;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	/* calculate change limit; i.e., the time needed between enable and
11062306a36Sopenharmony_ci	 * start of new gate control list
11162306a36Sopenharmony_ci	 */
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	/* case 1: extend cycle time for change
11462306a36Sopenharmony_ci	 * - change duration of last operation
11562306a36Sopenharmony_ci	 * - cycle time extension
11662306a36Sopenharmony_ci	 */
11762306a36Sopenharmony_ci	extend = tsnep_change_duration(gcl, gcl->count - 1);
11862306a36Sopenharmony_ci	extend += gcl->cycle_time_extension;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/* case 2: cut cycle time for change
12162306a36Sopenharmony_ci	 * - maximum change duration
12262306a36Sopenharmony_ci	 */
12362306a36Sopenharmony_ci	cut = 0;
12462306a36Sopenharmony_ci	for (i = 0; i < gcl->count; i++)
12562306a36Sopenharmony_ci		cut = max(cut, tsnep_change_duration(gcl, i));
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	/* use maximum, because the actual case (extend or cut) can be
12862306a36Sopenharmony_ci	 * determined only after limit is known (chicken-and-egg problem)
12962306a36Sopenharmony_ci	 */
13062306a36Sopenharmony_ci	gcl->change_limit = max(extend, cut);
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic u64 tsnep_gcl_start_after(struct tsnep_gcl *gcl, u64 limit)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	u64 start = gcl->base_time;
13662306a36Sopenharmony_ci	u64 n;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	if (start <= limit) {
13962306a36Sopenharmony_ci		n = div64_u64(limit - start, gcl->cycle_time);
14062306a36Sopenharmony_ci		start += (n + 1) * gcl->cycle_time;
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	return start;
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic u64 tsnep_gcl_start_before(struct tsnep_gcl *gcl, u64 limit)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	u64 start = gcl->base_time;
14962306a36Sopenharmony_ci	u64 n;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	n = div64_u64(limit - start, gcl->cycle_time);
15262306a36Sopenharmony_ci	start += n * gcl->cycle_time;
15362306a36Sopenharmony_ci	if (start == limit)
15462306a36Sopenharmony_ci		start -= gcl->cycle_time;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	return start;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic u64 tsnep_set_gcl_change(struct tsnep_gcl *gcl, int index, u64 change,
16062306a36Sopenharmony_ci				bool insert)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	/* previous operation triggers change and properties are evaluated at
16362306a36Sopenharmony_ci	 * start of operation
16462306a36Sopenharmony_ci	 */
16562306a36Sopenharmony_ci	if (index == 0)
16662306a36Sopenharmony_ci		index = gcl->count - 1;
16762306a36Sopenharmony_ci	else
16862306a36Sopenharmony_ci		index = index - 1;
16962306a36Sopenharmony_ci	change -= gcl->operation[index].interval;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	/* optionally change to new list with additional operation in between */
17262306a36Sopenharmony_ci	if (insert) {
17362306a36Sopenharmony_ci		void __iomem *addr = gcl->addr +
17462306a36Sopenharmony_ci				     sizeof(struct tsnep_gcl_operation) * index;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci		gcl->operation[index].properties |= TSNEP_GCL_INSERT;
17762306a36Sopenharmony_ci		iowrite32(gcl->operation[index].properties, addr);
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	return change;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic void tsnep_clean_gcl(struct tsnep_gcl *gcl)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	int i;
18662306a36Sopenharmony_ci	u32 mask = TSNEP_GCL_LAST | TSNEP_GCL_MASK;
18762306a36Sopenharmony_ci	void __iomem *addr;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	/* search for insert operation and reset properties */
19062306a36Sopenharmony_ci	for (i = 0; i < gcl->count; i++) {
19162306a36Sopenharmony_ci		if (gcl->operation[i].properties & ~mask) {
19262306a36Sopenharmony_ci			addr = gcl->addr +
19362306a36Sopenharmony_ci			       sizeof(struct tsnep_gcl_operation) * i;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci			gcl->operation[i].properties &= mask;
19662306a36Sopenharmony_ci			iowrite32(gcl->operation[i].properties, addr);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci			break;
19962306a36Sopenharmony_ci		}
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic u64 tsnep_insert_gcl_operation(struct tsnep_gcl *gcl, int ref,
20462306a36Sopenharmony_ci				      u64 change, u32 interval)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	u32 properties;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	properties = gcl->operation[ref].properties & TSNEP_GCL_MASK;
20962306a36Sopenharmony_ci	/* change to new list directly after inserted operation */
21062306a36Sopenharmony_ci	properties |= TSNEP_GCL_CHANGE;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	/* last operation of list is reserved to insert operation */
21362306a36Sopenharmony_ci	tsnep_write_gcl_operation(gcl, TSNEP_GCL_COUNT - 1, properties,
21462306a36Sopenharmony_ci				  interval, false);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	return tsnep_set_gcl_change(gcl, ref, change, true);
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic u64 tsnep_extend_gcl(struct tsnep_gcl *gcl, u64 start, u32 extension)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	int ref = gcl->count - 1;
22262306a36Sopenharmony_ci	u32 interval = gcl->operation[ref].interval + extension;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	start -= gcl->operation[ref].interval;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	return tsnep_insert_gcl_operation(gcl, ref, start, interval);
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic u64 tsnep_cut_gcl(struct tsnep_gcl *gcl, u64 start, u64 cycle_time)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	u64 sum = 0;
23262306a36Sopenharmony_ci	int i;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	/* find operation which shall be cutted */
23562306a36Sopenharmony_ci	for (i = 0; i < gcl->count; i++) {
23662306a36Sopenharmony_ci		u64 sum_tmp = sum + gcl->operation[i].interval;
23762306a36Sopenharmony_ci		u64 interval;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci		/* sum up operations as long as cycle time is not exceeded */
24062306a36Sopenharmony_ci		if (sum_tmp > cycle_time)
24162306a36Sopenharmony_ci			break;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci		/* remaining interval must be big enough for hardware */
24462306a36Sopenharmony_ci		interval = cycle_time - sum_tmp;
24562306a36Sopenharmony_ci		if (interval > 0 && interval < TSNEP_GCL_MIN_INTERVAL)
24662306a36Sopenharmony_ci			break;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci		sum = sum_tmp;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci	if (sum == cycle_time) {
25162306a36Sopenharmony_ci		/* no need to cut operation itself or whole cycle
25262306a36Sopenharmony_ci		 * => change exactly at operation
25362306a36Sopenharmony_ci		 */
25462306a36Sopenharmony_ci		return tsnep_set_gcl_change(gcl, i, start + sum, false);
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci	return tsnep_insert_gcl_operation(gcl, i, start + sum,
25762306a36Sopenharmony_ci					  cycle_time - sum);
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic int tsnep_enable_gcl(struct tsnep_adapter *adapter,
26162306a36Sopenharmony_ci			    struct tsnep_gcl *gcl, struct tsnep_gcl *curr)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	u64 system_time;
26462306a36Sopenharmony_ci	u64 timeout;
26562306a36Sopenharmony_ci	u64 limit;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	/* estimate timeout limit after timeout enable, actually timeout limit
26862306a36Sopenharmony_ci	 * in hardware will be earlier than estimate so we are on the safe side
26962306a36Sopenharmony_ci	 */
27062306a36Sopenharmony_ci	tsnep_get_system_time(adapter, &system_time);
27162306a36Sopenharmony_ci	timeout = system_time + TSNEP_GC_TIMEOUT;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	if (curr)
27462306a36Sopenharmony_ci		limit = timeout + curr->change_limit;
27562306a36Sopenharmony_ci	else
27662306a36Sopenharmony_ci		limit = timeout;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	gcl->start_time = tsnep_gcl_start_after(gcl, limit);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	/* gate control time register is only 32bit => time shall be in the near
28162306a36Sopenharmony_ci	 * future (no driver support for far future implemented)
28262306a36Sopenharmony_ci	 */
28362306a36Sopenharmony_ci	if ((gcl->start_time - system_time) >= U32_MAX)
28462306a36Sopenharmony_ci		return -EAGAIN;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (curr) {
28762306a36Sopenharmony_ci		/* change gate control list */
28862306a36Sopenharmony_ci		u64 last;
28962306a36Sopenharmony_ci		u64 change;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci		last = tsnep_gcl_start_before(curr, gcl->start_time);
29262306a36Sopenharmony_ci		if ((last + curr->cycle_time) == gcl->start_time)
29362306a36Sopenharmony_ci			change = tsnep_cut_gcl(curr, last,
29462306a36Sopenharmony_ci					       gcl->start_time - last);
29562306a36Sopenharmony_ci		else if (((gcl->start_time - last) <=
29662306a36Sopenharmony_ci			  curr->cycle_time_extension) ||
29762306a36Sopenharmony_ci			 ((gcl->start_time - last) <= TSNEP_GCL_MIN_INTERVAL))
29862306a36Sopenharmony_ci			change = tsnep_extend_gcl(curr, last,
29962306a36Sopenharmony_ci						  gcl->start_time - last);
30062306a36Sopenharmony_ci		else
30162306a36Sopenharmony_ci			change = tsnep_cut_gcl(curr, last,
30262306a36Sopenharmony_ci					       gcl->start_time - last);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci		WARN_ON(change <= timeout);
30562306a36Sopenharmony_ci		gcl->change = true;
30662306a36Sopenharmony_ci		iowrite32(change & 0xFFFFFFFF, adapter->addr + TSNEP_GC_CHANGE);
30762306a36Sopenharmony_ci	} else {
30862306a36Sopenharmony_ci		/* start gate control list */
30962306a36Sopenharmony_ci		WARN_ON(gcl->start_time <= timeout);
31062306a36Sopenharmony_ci		gcl->change = false;
31162306a36Sopenharmony_ci		iowrite32(gcl->start_time & 0xFFFFFFFF,
31262306a36Sopenharmony_ci			  adapter->addr + TSNEP_GC_TIME);
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	return 0;
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic int tsnep_taprio(struct tsnep_adapter *adapter,
31962306a36Sopenharmony_ci			struct tc_taprio_qopt_offload *qopt)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	struct tsnep_gcl *gcl;
32262306a36Sopenharmony_ci	struct tsnep_gcl *curr;
32362306a36Sopenharmony_ci	int retval;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	if (!adapter->gate_control)
32662306a36Sopenharmony_ci		return -EOPNOTSUPP;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	if (qopt->cmd == TAPRIO_CMD_DESTROY) {
32962306a36Sopenharmony_ci		/* disable gate control if active */
33062306a36Sopenharmony_ci		mutex_lock(&adapter->gate_control_lock);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci		if (adapter->gate_control_active) {
33362306a36Sopenharmony_ci			iowrite8(TSNEP_GC_DISABLE, adapter->addr + TSNEP_GC);
33462306a36Sopenharmony_ci			adapter->gate_control_active = false;
33562306a36Sopenharmony_ci		}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci		mutex_unlock(&adapter->gate_control_lock);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci		return 0;
34062306a36Sopenharmony_ci	} else if (qopt->cmd != TAPRIO_CMD_REPLACE) {
34162306a36Sopenharmony_ci		return -EOPNOTSUPP;
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	retval = tsnep_validate_gcl(qopt);
34562306a36Sopenharmony_ci	if (retval)
34662306a36Sopenharmony_ci		return retval;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	mutex_lock(&adapter->gate_control_lock);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	gcl = &adapter->gcl[adapter->next_gcl];
35162306a36Sopenharmony_ci	tsnep_write_gcl(gcl, qopt);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	/* select current gate control list if active */
35462306a36Sopenharmony_ci	if (adapter->gate_control_active) {
35562306a36Sopenharmony_ci		if (adapter->next_gcl == 0)
35662306a36Sopenharmony_ci			curr = &adapter->gcl[1];
35762306a36Sopenharmony_ci		else
35862306a36Sopenharmony_ci			curr = &adapter->gcl[0];
35962306a36Sopenharmony_ci	} else {
36062306a36Sopenharmony_ci		curr = NULL;
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	for (;;) {
36462306a36Sopenharmony_ci		/* start timeout which discards late enable, this helps ensuring
36562306a36Sopenharmony_ci		 * that start/change time are in the future at enable
36662306a36Sopenharmony_ci		 */
36762306a36Sopenharmony_ci		iowrite8(TSNEP_GC_ENABLE_TIMEOUT, adapter->addr + TSNEP_GC);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci		retval = tsnep_enable_gcl(adapter, gcl, curr);
37062306a36Sopenharmony_ci		if (retval) {
37162306a36Sopenharmony_ci			mutex_unlock(&adapter->gate_control_lock);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci			return retval;
37462306a36Sopenharmony_ci		}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci		/* enable gate control list */
37762306a36Sopenharmony_ci		if (adapter->next_gcl == 0)
37862306a36Sopenharmony_ci			iowrite8(TSNEP_GC_ENABLE_A, adapter->addr + TSNEP_GC);
37962306a36Sopenharmony_ci		else
38062306a36Sopenharmony_ci			iowrite8(TSNEP_GC_ENABLE_B, adapter->addr + TSNEP_GC);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci		/* done if timeout did not happen */
38362306a36Sopenharmony_ci		if (!(ioread32(adapter->addr + TSNEP_GC) &
38462306a36Sopenharmony_ci		      TSNEP_GC_TIMEOUT_SIGNAL))
38562306a36Sopenharmony_ci			break;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci		/* timeout is acknowledged with any enable */
38862306a36Sopenharmony_ci		iowrite8(TSNEP_GC_ENABLE_A, adapter->addr + TSNEP_GC);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci		if (curr)
39162306a36Sopenharmony_ci			tsnep_clean_gcl(curr);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		/* retry because of timeout */
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	adapter->gate_control_active = true;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	if (adapter->next_gcl == 0)
39962306a36Sopenharmony_ci		adapter->next_gcl = 1;
40062306a36Sopenharmony_ci	else
40162306a36Sopenharmony_ci		adapter->next_gcl = 0;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	mutex_unlock(&adapter->gate_control_lock);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	return 0;
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistatic int tsnep_tc_query_caps(struct tsnep_adapter *adapter,
40962306a36Sopenharmony_ci			       struct tc_query_caps_base *base)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	switch (base->type) {
41262306a36Sopenharmony_ci	case TC_SETUP_QDISC_TAPRIO: {
41362306a36Sopenharmony_ci		struct tc_taprio_caps *caps = base->caps;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		if (!adapter->gate_control)
41662306a36Sopenharmony_ci			return -EOPNOTSUPP;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci		caps->gate_mask_per_txq = true;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci		return 0;
42162306a36Sopenharmony_ci	}
42262306a36Sopenharmony_ci	default:
42362306a36Sopenharmony_ci		return -EOPNOTSUPP;
42462306a36Sopenharmony_ci	}
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ciint tsnep_tc_setup(struct net_device *netdev, enum tc_setup_type type,
42862306a36Sopenharmony_ci		   void *type_data)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	switch (type) {
43362306a36Sopenharmony_ci	case TC_QUERY_CAPS:
43462306a36Sopenharmony_ci		return tsnep_tc_query_caps(adapter, type_data);
43562306a36Sopenharmony_ci	case TC_SETUP_QDISC_TAPRIO:
43662306a36Sopenharmony_ci		return tsnep_taprio(adapter, type_data);
43762306a36Sopenharmony_ci	default:
43862306a36Sopenharmony_ci		return -EOPNOTSUPP;
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ciint tsnep_tc_init(struct tsnep_adapter *adapter)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	if (!adapter->gate_control)
44562306a36Sopenharmony_ci		return 0;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	/* open all gates */
44862306a36Sopenharmony_ci	iowrite8(TSNEP_GC_DISABLE, adapter->addr + TSNEP_GC);
44962306a36Sopenharmony_ci	iowrite32(TSNEP_GC_OPEN | TSNEP_GC_NEXT_OPEN, adapter->addr + TSNEP_GC);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	adapter->gcl[0].addr = adapter->addr + TSNEP_GCL_A;
45262306a36Sopenharmony_ci	adapter->gcl[1].addr = adapter->addr + TSNEP_GCL_B;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	return 0;
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_civoid tsnep_tc_cleanup(struct tsnep_adapter *adapter)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	if (!adapter->gate_control)
46062306a36Sopenharmony_ci		return;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	if (adapter->gate_control_active) {
46362306a36Sopenharmony_ci		iowrite8(TSNEP_GC_DISABLE, adapter->addr + TSNEP_GC);
46462306a36Sopenharmony_ci		adapter->gate_control_active = false;
46562306a36Sopenharmony_ci	}
46662306a36Sopenharmony_ci}
467