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