18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright (c) 2019 Intel Corporation */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include "igc.h" 58c2ecf20Sopenharmony_ci#include "igc_tsn.h" 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_cistatic bool is_any_launchtime(struct igc_adapter *adapter) 88c2ecf20Sopenharmony_ci{ 98c2ecf20Sopenharmony_ci int i; 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci for (i = 0; i < adapter->num_tx_queues; i++) { 128c2ecf20Sopenharmony_ci struct igc_ring *ring = adapter->tx_ring[i]; 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci if (ring->launchtime_enable) 158c2ecf20Sopenharmony_ci return true; 168c2ecf20Sopenharmony_ci } 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci return false; 198c2ecf20Sopenharmony_ci} 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* Returns the TSN specific registers to their default values after 228c2ecf20Sopenharmony_ci * TSN offloading is disabled. 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_cistatic int igc_tsn_disable_offload(struct igc_adapter *adapter) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 278c2ecf20Sopenharmony_ci u32 tqavctrl; 288c2ecf20Sopenharmony_ci int i; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci if (!(adapter->flags & IGC_FLAG_TSN_QBV_ENABLED)) 318c2ecf20Sopenharmony_ci return 0; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci adapter->cycle_time = 0; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci wr32(IGC_TXPBS, I225_TXPBSIZE_DEFAULT); 368c2ecf20Sopenharmony_ci wr32(IGC_DTXMXPKTSZ, IGC_DTXMXPKTSZ_DEFAULT); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci tqavctrl = rd32(IGC_TQAVCTRL); 398c2ecf20Sopenharmony_ci tqavctrl &= ~(IGC_TQAVCTRL_TRANSMIT_MODE_TSN | 408c2ecf20Sopenharmony_ci IGC_TQAVCTRL_ENHANCED_QAV); 418c2ecf20Sopenharmony_ci wr32(IGC_TQAVCTRL, tqavctrl); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci for (i = 0; i < adapter->num_tx_queues; i++) { 448c2ecf20Sopenharmony_ci struct igc_ring *ring = adapter->tx_ring[i]; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci ring->start_time = 0; 478c2ecf20Sopenharmony_ci ring->end_time = 0; 488c2ecf20Sopenharmony_ci ring->launchtime_enable = false; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci wr32(IGC_TXQCTL(i), 0); 518c2ecf20Sopenharmony_ci wr32(IGC_STQT(i), 0); 528c2ecf20Sopenharmony_ci wr32(IGC_ENDQT(i), NSEC_PER_SEC); 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci wr32(IGC_QBVCYCLET_S, NSEC_PER_SEC); 568c2ecf20Sopenharmony_ci wr32(IGC_QBVCYCLET, NSEC_PER_SEC); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci adapter->flags &= ~IGC_FLAG_TSN_QBV_ENABLED; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return 0; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int igc_tsn_enable_offload(struct igc_adapter *adapter) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct igc_hw *hw = &adapter->hw; 668c2ecf20Sopenharmony_ci u32 tqavctrl, baset_l, baset_h; 678c2ecf20Sopenharmony_ci u32 sec, nsec, cycle; 688c2ecf20Sopenharmony_ci ktime_t base_time, systim; 698c2ecf20Sopenharmony_ci int i; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (adapter->flags & IGC_FLAG_TSN_QBV_ENABLED) 728c2ecf20Sopenharmony_ci return 0; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci cycle = adapter->cycle_time; 758c2ecf20Sopenharmony_ci base_time = adapter->base_time; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci wr32(IGC_TSAUXC, 0); 788c2ecf20Sopenharmony_ci wr32(IGC_DTXMXPKTSZ, IGC_DTXMXPKTSZ_TSN); 798c2ecf20Sopenharmony_ci wr32(IGC_TXPBS, IGC_TXPBSIZE_TSN); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci tqavctrl = rd32(IGC_TQAVCTRL); 828c2ecf20Sopenharmony_ci tqavctrl |= IGC_TQAVCTRL_TRANSMIT_MODE_TSN | IGC_TQAVCTRL_ENHANCED_QAV; 838c2ecf20Sopenharmony_ci wr32(IGC_TQAVCTRL, tqavctrl); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci wr32(IGC_QBVCYCLET_S, cycle); 868c2ecf20Sopenharmony_ci wr32(IGC_QBVCYCLET, cycle); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci for (i = 0; i < adapter->num_tx_queues; i++) { 898c2ecf20Sopenharmony_ci struct igc_ring *ring = adapter->tx_ring[i]; 908c2ecf20Sopenharmony_ci u32 txqctl = 0; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci wr32(IGC_STQT(i), ring->start_time); 938c2ecf20Sopenharmony_ci wr32(IGC_ENDQT(i), ring->end_time); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci txqctl |= IGC_TXQCTL_STRICT_CYCLE | 968c2ecf20Sopenharmony_ci IGC_TXQCTL_STRICT_END; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (ring->launchtime_enable) 998c2ecf20Sopenharmony_ci txqctl |= IGC_TXQCTL_QUEUE_MODE_LAUNCHT; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci wr32(IGC_TXQCTL(i), txqctl); 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci nsec = rd32(IGC_SYSTIML); 1058c2ecf20Sopenharmony_ci sec = rd32(IGC_SYSTIMH); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci systim = ktime_set(sec, nsec); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (ktime_compare(systim, base_time) > 0) { 1108c2ecf20Sopenharmony_ci s64 n; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci n = div64_s64(ktime_sub_ns(systim, base_time), cycle); 1138c2ecf20Sopenharmony_ci base_time = ktime_add_ns(base_time, (n + 1) * cycle); 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci baset_h = div_s64_rem(base_time, NSEC_PER_SEC, &baset_l); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci wr32(IGC_BASET_H, baset_h); 1198c2ecf20Sopenharmony_ci wr32(IGC_BASET_L, baset_l); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci adapter->flags |= IGC_FLAG_TSN_QBV_ENABLED; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return 0; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ciint igc_tsn_offload_apply(struct igc_adapter *adapter) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci bool is_any_enabled = adapter->base_time || is_any_launchtime(adapter); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (!(adapter->flags & IGC_FLAG_TSN_QBV_ENABLED) && !is_any_enabled) 1318c2ecf20Sopenharmony_ci return 0; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (!is_any_enabled) { 1348c2ecf20Sopenharmony_ci int err = igc_tsn_disable_offload(adapter); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (err < 0) 1378c2ecf20Sopenharmony_ci return err; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* The BASET registers aren't cleared when writing 1408c2ecf20Sopenharmony_ci * into them, force a reset if the interface is 1418c2ecf20Sopenharmony_ci * running. 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_ci if (netif_running(adapter->netdev)) 1448c2ecf20Sopenharmony_ci schedule_work(&adapter->reset_task); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return 0; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return igc_tsn_enable_offload(adapter); 1508c2ecf20Sopenharmony_ci} 151