--- /dev/null
+From ed89b74d2dc920cb61d3094e0e97ec8775b13086 Mon Sep 17 00:00:00 2001
+From: Muhammad Husaini Zulkifli <muhammad.husaini.zulkifli@intel.com>
+Date: Mon, 15 May 2023 14:03:36 +0800
+Subject: igc: Add condition for qbv_config_change_errors counter
+
+From: Muhammad Husaini Zulkifli <muhammad.husaini.zulkifli@intel.com>
+
+commit ed89b74d2dc920cb61d3094e0e97ec8775b13086 upstream.
+
+Add condition to increase the qbv counter during taprio qbv
+configuration only.
+
+There might be a case when TC already been setup then user configure
+the ETF/CBS qdisc and this counter will increase if no condition above.
+
+Fixes: ae4fe4698300 ("igc: Add qbv_config_change_errors counter")
+Signed-off-by: Muhammad Husaini Zulkifli <muhammad.husaini.zulkifli@intel.com>
+Tested-by: Naama Meir <naamax.meir@linux.intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/ethernet/intel/igc/igc.h | 1 +
+ drivers/net/ethernet/intel/igc/igc_main.c | 2 ++
+ drivers/net/ethernet/intel/igc/igc_tsn.c | 1 +
+ 3 files changed, 4 insertions(+)
+
+--- a/drivers/net/ethernet/intel/igc/igc.h
++++ b/drivers/net/ethernet/intel/igc/igc.h
+@@ -183,6 +183,7 @@ struct igc_adapter {
+ u32 max_frame_size;
+ u32 min_frame_size;
+
++ int tc_setup_type;
+ ktime_t base_time;
+ ktime_t cycle_time;
+ bool qbv_enable;
+--- a/drivers/net/ethernet/intel/igc/igc_main.c
++++ b/drivers/net/ethernet/intel/igc/igc_main.c
+@@ -6288,6 +6288,8 @@ static int igc_setup_tc(struct net_devic
+ {
+ struct igc_adapter *adapter = netdev_priv(dev);
+
++ adapter->tc_setup_type = type;
++
+ switch (type) {
+ case TC_SETUP_QDISC_TAPRIO:
+ return igc_tsn_enable_qbv_scheduling(adapter, type_data);
+--- a/drivers/net/ethernet/intel/igc/igc_tsn.c
++++ b/drivers/net/ethernet/intel/igc/igc_tsn.c
+@@ -219,6 +219,7 @@ skip_cbs:
+ * Gate Control List (GCL) is running.
+ */
+ if ((rd32(IGC_BASET_H) || rd32(IGC_BASET_L)) &&
++ (adapter->tc_setup_type == TC_SETUP_QDISC_TAPRIO) &&
+ tsn_mode_reconfig)
+ adapter->qbv_config_change_errors++;
+ } else {
--- /dev/null
+From ae4fe46983007bc46d87dcb284a5e5851c3e1c84 Mon Sep 17 00:00:00 2001
+From: Muhammad Husaini Zulkifli <muhammad.husaini.zulkifli@intel.com>
+Date: Thu, 16 Feb 2023 10:07:31 +0800
+Subject: igc: Add qbv_config_change_errors counter
+
+From: Muhammad Husaini Zulkifli <muhammad.husaini.zulkifli@intel.com>
+
+commit ae4fe46983007bc46d87dcb284a5e5851c3e1c84 upstream.
+
+Add ConfigChangeError(qbv_config_change_errors) when user try to set the
+AdminBaseTime to past value while the current GCL is still running.
+
+The ConfigChangeError counter should not be increased when a gate control
+list is scheduled into the future.
+
+User can use "ethtool -S <interface> | grep qbv_config_change_errors"
+command to check the counter values.
+
+Signed-off-by: Muhammad Husaini Zulkifli <muhammad.husaini.zulkifli@intel.com>
+Tested-by: Naama Meir <naamax.meir@linux.intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/ethernet/intel/igc/igc.h | 1 +
+ drivers/net/ethernet/intel/igc/igc_ethtool.c | 1 +
+ drivers/net/ethernet/intel/igc/igc_main.c | 1 +
+ drivers/net/ethernet/intel/igc/igc_tsn.c | 12 ++++++++++++
+ 4 files changed, 15 insertions(+)
+
+--- a/drivers/net/ethernet/intel/igc/igc.h
++++ b/drivers/net/ethernet/intel/igc/igc.h
+@@ -186,6 +186,7 @@ struct igc_adapter {
+ ktime_t base_time;
+ ktime_t cycle_time;
+ bool qbv_enable;
++ u32 qbv_config_change_errors;
+
+ /* OS defined structs */
+ struct pci_dev *pdev;
+--- a/drivers/net/ethernet/intel/igc/igc_ethtool.c
++++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c
+@@ -67,6 +67,7 @@ static const struct igc_stats igc_gstrin
+ IGC_STAT("rx_hwtstamp_cleared", rx_hwtstamp_cleared),
+ IGC_STAT("tx_lpi_counter", stats.tlpic),
+ IGC_STAT("rx_lpi_counter", stats.rlpic),
++ IGC_STAT("qbv_config_change_errors", qbv_config_change_errors),
+ };
+
+ #define IGC_NETDEV_STAT(_net_stat) { \
+--- a/drivers/net/ethernet/intel/igc/igc_main.c
++++ b/drivers/net/ethernet/intel/igc/igc_main.c
+@@ -6106,6 +6106,7 @@ static int igc_tsn_clear_schedule(struct
+
+ adapter->base_time = 0;
+ adapter->cycle_time = NSEC_PER_SEC;
++ adapter->qbv_config_change_errors = 0;
+
+ for (i = 0; i < adapter->num_tx_queues; i++) {
+ struct igc_ring *ring = adapter->tx_ring[i];
+--- a/drivers/net/ethernet/intel/igc/igc_tsn.c
++++ b/drivers/net/ethernet/intel/igc/igc_tsn.c
+@@ -84,6 +84,7 @@ static int igc_tsn_disable_offload(struc
+ static int igc_tsn_enable_offload(struct igc_adapter *adapter)
+ {
+ struct igc_hw *hw = &adapter->hw;
++ bool tsn_mode_reconfig = false;
+ u32 tqavctrl, baset_l, baset_h;
+ u32 sec, nsec, cycle;
+ ktime_t base_time, systim;
+@@ -196,6 +197,10 @@ skip_cbs:
+ }
+
+ tqavctrl = rd32(IGC_TQAVCTRL) & ~IGC_TQAVCTRL_FUTSCDDIS;
++
++ if (tqavctrl & IGC_TQAVCTRL_TRANSMIT_MODE_TSN)
++ tsn_mode_reconfig = true;
++
+ tqavctrl |= IGC_TQAVCTRL_TRANSMIT_MODE_TSN | IGC_TQAVCTRL_ENHANCED_QAV;
+
+ cycle = adapter->cycle_time;
+@@ -209,6 +214,13 @@ skip_cbs:
+ s64 n = div64_s64(ktime_sub_ns(systim, base_time), cycle);
+
+ base_time = ktime_add_ns(base_time, (n + 1) * cycle);
++
++ /* Increase the counter if scheduling into the past while
++ * Gate Control List (GCL) is running.
++ */
++ if ((rd32(IGC_BASET_H) || rd32(IGC_BASET_L)) &&
++ tsn_mode_reconfig)
++ adapter->qbv_config_change_errors++;
+ } else {
+ /* According to datasheet section 7.5.2.9.3.3, FutScdDis bit
+ * has to be configured before the cycle time and base time.
--- /dev/null
+From 5ac1231ac14d1b8a1098048e51cad45f11b85c0a Mon Sep 17 00:00:00 2001
+From: Tan Tee Min <tee.min.tan@linux.intel.com>
+Date: Thu, 15 Dec 2022 00:29:08 +0800
+Subject: igc: enable Qbv configuration for 2nd GCL
+
+From: Tan Tee Min <tee.min.tan@linux.intel.com>
+
+commit 5ac1231ac14d1b8a1098048e51cad45f11b85c0a upstream.
+
+Make reset task only executes for i225 and Qbv disabling to allow
+i226 configure for 2nd GCL without resetting the adapter.
+
+In i226, Tx won't hang if there is a GCL is already running, so in
+this case we don't need to set FutScdDis bit.
+
+Signed-off-by: Tan Tee Min <tee.min.tan@linux.intel.com>
+Signed-off-by: Muhammad Husaini Zulkifli <muhammad.husaini.zulkifli@intel.com>
+Tested-by: Naama Meir <naamax.meir@linux.intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/ethernet/intel/igc/igc_main.c | 9 +++++----
+ drivers/net/ethernet/intel/igc/igc_tsn.c | 13 +++++++++----
+ drivers/net/ethernet/intel/igc/igc_tsn.h | 2 +-
+ 3 files changed, 15 insertions(+), 9 deletions(-)
+
+--- a/drivers/net/ethernet/intel/igc/igc_main.c
++++ b/drivers/net/ethernet/intel/igc/igc_main.c
+@@ -6097,7 +6097,7 @@ static int igc_tsn_enable_launchtime(str
+ if (err)
+ return err;
+
+- return igc_tsn_offload_apply(adapter);
++ return igc_tsn_offload_apply(adapter, qopt->enable);
+ }
+
+ static int igc_tsn_clear_schedule(struct igc_adapter *adapter)
+@@ -6121,6 +6121,7 @@ static int igc_save_qbv_schedule(struct
+ struct tc_taprio_qopt_offload *qopt)
+ {
+ bool queue_configured[IGC_MAX_TX_QUEUES] = { };
++ struct igc_hw *hw = &adapter->hw;
+ u32 start_time = 0, end_time = 0;
+ size_t n;
+ int i;
+@@ -6133,7 +6134,7 @@ static int igc_save_qbv_schedule(struct
+ if (qopt->base_time < 0)
+ return -ERANGE;
+
+- if (adapter->base_time)
++ if (igc_is_device_id_i225(hw) && adapter->base_time)
+ return -EALREADY;
+
+ if (!validate_schedule(adapter, qopt))
+@@ -6210,7 +6211,7 @@ static int igc_tsn_enable_qbv_scheduling
+ if (err)
+ return err;
+
+- return igc_tsn_offload_apply(adapter);
++ return igc_tsn_offload_apply(adapter, qopt->enable);
+ }
+
+ static int igc_save_cbs_params(struct igc_adapter *adapter, int queue,
+@@ -6278,7 +6279,7 @@ static int igc_tsn_enable_cbs(struct igc
+ if (err)
+ return err;
+
+- return igc_tsn_offload_apply(adapter);
++ return igc_tsn_offload_apply(adapter, qopt->enable);
+ }
+
+ static int igc_setup_tc(struct net_device *dev, enum tc_setup_type type,
+--- a/drivers/net/ethernet/intel/igc/igc_tsn.c
++++ b/drivers/net/ethernet/intel/igc/igc_tsn.c
+@@ -195,7 +195,7 @@ skip_cbs:
+ wr32(IGC_TXQCTL(i), txqctl);
+ }
+
+- tqavctrl = rd32(IGC_TQAVCTRL);
++ tqavctrl = rd32(IGC_TQAVCTRL) & ~IGC_TQAVCTRL_FUTSCDDIS;
+ tqavctrl |= IGC_TQAVCTRL_TRANSMIT_MODE_TSN | IGC_TQAVCTRL_ENHANCED_QAV;
+
+ cycle = adapter->cycle_time;
+@@ -212,8 +212,11 @@ skip_cbs:
+ } else {
+ /* According to datasheet section 7.5.2.9.3.3, FutScdDis bit
+ * has to be configured before the cycle time and base time.
++ * Tx won't hang if there is a GCL is already running,
++ * so in this case we don't need to set FutScdDis.
+ */
+- if (igc_is_device_id_i226(hw))
++ if (igc_is_device_id_i226(hw) &&
++ !(rd32(IGC_BASET_H) || rd32(IGC_BASET_L)))
+ tqavctrl |= IGC_TQAVCTRL_FUTSCDDIS;
+ }
+
+@@ -256,11 +259,13 @@ int igc_tsn_reset(struct igc_adapter *ad
+ return err;
+ }
+
+-int igc_tsn_offload_apply(struct igc_adapter *adapter)
++int igc_tsn_offload_apply(struct igc_adapter *adapter, bool enable)
+ {
++ struct igc_hw *hw = &adapter->hw;
+ int err;
+
+- if (netif_running(adapter->netdev)) {
++ if (netif_running(adapter->netdev) &&
++ (igc_is_device_id_i225(hw) || !enable)) {
+ schedule_work(&adapter->reset_task);
+ return 0;
+ }
+--- a/drivers/net/ethernet/intel/igc/igc_tsn.h
++++ b/drivers/net/ethernet/intel/igc/igc_tsn.h
+@@ -4,7 +4,7 @@
+ #ifndef _IGC_TSN_H_
+ #define _IGC_TSN_H_
+
+-int igc_tsn_offload_apply(struct igc_adapter *adapter);
++int igc_tsn_offload_apply(struct igc_adapter *adapter, bool enable);
+ int igc_tsn_reset(struct igc_adapter *adapter);
+
+ #endif /* _IGC_BASE_H */
--- /dev/null
+From 9c50e2b150c8ee0eee5f8154e2ad168cdd748877 Mon Sep 17 00:00:00 2001
+From: Vinicius Costa Gomes <vinicius.gomes@intel.com>
+Date: Wed, 7 Jun 2023 14:32:29 -0700
+Subject: igc: Fix race condition in PTP tx code
+
+From: Vinicius Costa Gomes <vinicius.gomes@intel.com>
+
+commit 9c50e2b150c8ee0eee5f8154e2ad168cdd748877 upstream.
+
+Currently, the igc driver supports timestamping only one tx packet at a
+time. During the transmission flow, the skb that requires hardware
+timestamping is saved in adapter->ptp_tx_skb. Once hardware has the
+timestamp, an interrupt is delivered, and adapter->ptp_tx_work is
+scheduled. In igc_ptp_tx_work(), we read the timestamp register, update
+adapter->ptp_tx_skb, and notify the network stack.
+
+While the thread executing the transmission flow (the user process
+running in kernel mode) and the thread executing ptp_tx_work don't
+access adapter->ptp_tx_skb concurrently, there are two other places
+where adapter->ptp_tx_skb is accessed: igc_ptp_tx_hang() and
+igc_ptp_suspend().
+
+igc_ptp_tx_hang() is executed by the adapter->watchdog_task worker
+thread which runs periodically so it is possible we have two threads
+accessing ptp_tx_skb at the same time. Consider the following scenario:
+right after __IGC_PTP_TX_IN_PROGRESS is set in igc_xmit_frame_ring(),
+igc_ptp_tx_hang() is executed. Since adapter->ptp_tx_start hasn't been
+written yet, this is considered a timeout and adapter->ptp_tx_skb is
+cleaned up.
+
+This patch fixes the issue described above by adding the ptp_tx_lock to
+protect access to ptp_tx_skb and ptp_tx_start fields from igc_adapter.
+Since igc_xmit_frame_ring() called in atomic context by the networking
+stack, ptp_tx_lock is defined as a spinlock, and the irq safe variants
+of lock/unlock are used.
+
+With the introduction of the ptp_tx_lock, the __IGC_PTP_TX_IN_PROGRESS
+flag doesn't provide much of a use anymore so this patch gets rid of it.
+
+Fixes: 2c344ae24501 ("igc: Add support for TX timestamping")
+Signed-off-by: Andre Guedes <andre.guedes@intel.com>
+Signed-off-by: Vinicius Costa Gomes <vinicius.gomes@intel.com>
+Reviewed-by: Kurt Kanzenbach <kurt@linutronix.de>
+Tested-by: Naama Meir <naamax.meir@linux.intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/ethernet/intel/igc/igc.h | 5 ++
+ drivers/net/ethernet/intel/igc/igc_main.c | 9 +++-
+ drivers/net/ethernet/intel/igc/igc_ptp.c | 57 ++++++++++++++++--------------
+ 3 files changed, 41 insertions(+), 30 deletions(-)
+
+--- a/drivers/net/ethernet/intel/igc/igc.h
++++ b/drivers/net/ethernet/intel/igc/igc.h
+@@ -230,6 +230,10 @@ struct igc_adapter {
+ struct ptp_clock *ptp_clock;
+ struct ptp_clock_info ptp_caps;
+ struct work_struct ptp_tx_work;
++ /* Access to ptp_tx_skb and ptp_tx_start are protected by the
++ * ptp_tx_lock.
++ */
++ spinlock_t ptp_tx_lock;
+ struct sk_buff *ptp_tx_skb;
+ struct hwtstamp_config tstamp_config;
+ unsigned long ptp_tx_start;
+@@ -431,7 +435,6 @@ enum igc_state_t {
+ __IGC_TESTING,
+ __IGC_RESETTING,
+ __IGC_DOWN,
+- __IGC_PTP_TX_IN_PROGRESS,
+ };
+
+ enum igc_tx_flags {
+--- a/drivers/net/ethernet/intel/igc/igc_main.c
++++ b/drivers/net/ethernet/intel/igc/igc_main.c
+@@ -1606,9 +1606,10 @@ done:
+ * the other timer registers before skipping the
+ * timestamping request.
+ */
+- if (adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON &&
+- !test_and_set_bit_lock(__IGC_PTP_TX_IN_PROGRESS,
+- &adapter->state)) {
++ unsigned long flags;
++
++ spin_lock_irqsave(&adapter->ptp_tx_lock, flags);
++ if (adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON && !adapter->ptp_tx_skb) {
+ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+ tx_flags |= IGC_TX_FLAGS_TSTAMP;
+
+@@ -1617,6 +1618,8 @@ done:
+ } else {
+ adapter->tx_hwtstamp_skipped++;
+ }
++
++ spin_unlock_irqrestore(&adapter->ptp_tx_lock, flags);
+ }
+
+ if (skb_vlan_tag_present(skb)) {
+--- a/drivers/net/ethernet/intel/igc/igc_ptp.c
++++ b/drivers/net/ethernet/intel/igc/igc_ptp.c
+@@ -622,6 +622,7 @@ static int igc_ptp_set_timestamp_mode(st
+ return 0;
+ }
+
++/* Requires adapter->ptp_tx_lock held by caller. */
+ static void igc_ptp_tx_timeout(struct igc_adapter *adapter)
+ {
+ struct igc_hw *hw = &adapter->hw;
+@@ -629,7 +630,6 @@ static void igc_ptp_tx_timeout(struct ig
+ dev_kfree_skb_any(adapter->ptp_tx_skb);
+ adapter->ptp_tx_skb = NULL;
+ adapter->tx_hwtstamp_timeouts++;
+- clear_bit_unlock(__IGC_PTP_TX_IN_PROGRESS, &adapter->state);
+ /* Clear the tx valid bit in TSYNCTXCTL register to enable interrupt. */
+ rd32(IGC_TXSTMPH);
+ netdev_warn(adapter->netdev, "Tx timestamp timeout\n");
+@@ -637,20 +637,20 @@ static void igc_ptp_tx_timeout(struct ig
+
+ void igc_ptp_tx_hang(struct igc_adapter *adapter)
+ {
+- bool timeout = time_is_before_jiffies(adapter->ptp_tx_start +
+- IGC_PTP_TX_TIMEOUT);
++ unsigned long flags;
+
+- if (!test_bit(__IGC_PTP_TX_IN_PROGRESS, &adapter->state))
+- return;
++ spin_lock_irqsave(&adapter->ptp_tx_lock, flags);
+
+- /* If we haven't received a timestamp within the timeout, it is
+- * reasonable to assume that it will never occur, so we can unlock the
+- * timestamp bit when this occurs.
+- */
+- if (timeout) {
+- cancel_work_sync(&adapter->ptp_tx_work);
+- igc_ptp_tx_timeout(adapter);
+- }
++ if (!adapter->ptp_tx_skb)
++ goto unlock;
++
++ if (time_is_after_jiffies(adapter->ptp_tx_start + IGC_PTP_TX_TIMEOUT))
++ goto unlock;
++
++ igc_ptp_tx_timeout(adapter);
++
++unlock:
++ spin_unlock_irqrestore(&adapter->ptp_tx_lock, flags);
+ }
+
+ /**
+@@ -660,6 +660,8 @@ void igc_ptp_tx_hang(struct igc_adapter
+ * If we were asked to do hardware stamping and such a time stamp is
+ * available, then it must have been for this skb here because we only
+ * allow only one such packet into the queue.
++ *
++ * Context: Expects adapter->ptp_tx_lock to be held by caller.
+ */
+ static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter)
+ {
+@@ -695,13 +697,7 @@ static void igc_ptp_tx_hwtstamp(struct i
+ shhwtstamps.hwtstamp =
+ ktime_add_ns(shhwtstamps.hwtstamp, adjust);
+
+- /* Clear the lock early before calling skb_tstamp_tx so that
+- * applications are not woken up before the lock bit is clear. We use
+- * a copy of the skb pointer to ensure other threads can't change it
+- * while we're notifying the stack.
+- */
+ adapter->ptp_tx_skb = NULL;
+- clear_bit_unlock(__IGC_PTP_TX_IN_PROGRESS, &adapter->state);
+
+ /* Notify the stack and free the skb after we've unlocked */
+ skb_tstamp_tx(skb, &shhwtstamps);
+@@ -712,24 +708,33 @@ static void igc_ptp_tx_hwtstamp(struct i
+ * igc_ptp_tx_work
+ * @work: pointer to work struct
+ *
+- * This work function polls the TSYNCTXCTL valid bit to determine when a
+- * timestamp has been taken for the current stored skb.
++ * This work function checks the TSYNCTXCTL valid bit to determine when
++ * a timestamp has been taken for the current stored skb.
+ */
+ static void igc_ptp_tx_work(struct work_struct *work)
+ {
+ struct igc_adapter *adapter = container_of(work, struct igc_adapter,
+ ptp_tx_work);
+ struct igc_hw *hw = &adapter->hw;
++ unsigned long flags;
+ u32 tsynctxctl;
+
+- if (!test_bit(__IGC_PTP_TX_IN_PROGRESS, &adapter->state))
+- return;
++ spin_lock_irqsave(&adapter->ptp_tx_lock, flags);
++
++ if (!adapter->ptp_tx_skb)
++ goto unlock;
+
+ tsynctxctl = rd32(IGC_TSYNCTXCTL);
+- if (WARN_ON_ONCE(!(tsynctxctl & IGC_TSYNCTXCTL_TXTT_0)))
+- return;
++ tsynctxctl &= IGC_TSYNCTXCTL_TXTT_0;
++ if (!tsynctxctl) {
++ WARN_ONCE(1, "Received a TSTAMP interrupt but no TSTAMP is ready.\n");
++ goto unlock;
++ }
+
+ igc_ptp_tx_hwtstamp(adapter);
++
++unlock:
++ spin_unlock_irqrestore(&adapter->ptp_tx_lock, flags);
+ }
+
+ /**
+@@ -978,6 +983,7 @@ void igc_ptp_init(struct igc_adapter *ad
+ return;
+ }
+
++ spin_lock_init(&adapter->ptp_tx_lock);
+ spin_lock_init(&adapter->tmreg_lock);
+ INIT_WORK(&adapter->ptp_tx_work, igc_ptp_tx_work);
+
+@@ -1042,7 +1048,6 @@ void igc_ptp_suspend(struct igc_adapter
+ cancel_work_sync(&adapter->ptp_tx_work);
+ dev_kfree_skb_any(adapter->ptp_tx_skb);
+ adapter->ptp_tx_skb = NULL;
+- clear_bit_unlock(__IGC_PTP_TX_IN_PROGRESS, &adapter->state);
+
+ if (pci_device_is_present(adapter->pdev)) {
+ igc_ptp_time_save(adapter);
--- /dev/null
+From b8897dc54e3bc9d25281bbb42a7d730782ff4588 Mon Sep 17 00:00:00 2001
+From: Muhammad Husaini Zulkifli <muhammad.husaini.zulkifli@intel.com>
+Date: Thu, 15 Dec 2022 00:29:07 +0800
+Subject: igc: remove I226 Qbv BaseTime restriction
+
+From: Muhammad Husaini Zulkifli <muhammad.husaini.zulkifli@intel.com>
+
+commit b8897dc54e3bc9d25281bbb42a7d730782ff4588 upstream.
+
+Remove the Qbv BaseTime restriction for I226 so that the BaseTime can be
+scheduled to the future time. A new register bit of Tx Qav Control
+(Bit-7: FutScdDis) was introduced to allow I226 scheduling future time as
+Qbv BaseTime and not having the Tx hang timeout issue.
+
+Besides, according to datasheet section 7.5.2.9.3.3, FutScdDis bit has to
+be configured first before the cycle time and base time.
+
+Indeed the FutScdDis bit is only active on re-configuration, thus we have
+to set the BASET_L to zero and then only set it to the desired value.
+
+Please also note that the Qbv configuration flow is moved around based on
+the Qbv programming guideline that is documented in the latest datasheet.
+
+Co-developed-by: Tan Tee Min <tee.min.tan@linux.intel.com>
+Signed-off-by: Tan Tee Min <tee.min.tan@linux.intel.com>
+Signed-off-by: Muhammad Husaini Zulkifli <muhammad.husaini.zulkifli@intel.com>
+Tested-by: Naama Meir <naamax.meir@linux.intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/ethernet/intel/igc/igc_base.c | 29 +++++++++++++++++
+ drivers/net/ethernet/intel/igc/igc_base.h | 2 +
+ drivers/net/ethernet/intel/igc/igc_defines.h | 1
+ drivers/net/ethernet/intel/igc/igc_main.c | 5 ++-
+ drivers/net/ethernet/intel/igc/igc_tsn.c | 44 +++++++++++++++++----------
+ 5 files changed, 65 insertions(+), 16 deletions(-)
+
+--- a/drivers/net/ethernet/intel/igc/igc_base.c
++++ b/drivers/net/ethernet/intel/igc/igc_base.c
+@@ -396,6 +396,35 @@ void igc_rx_fifo_flush_base(struct igc_h
+ rd32(IGC_MPC);
+ }
+
++bool igc_is_device_id_i225(struct igc_hw *hw)
++{
++ switch (hw->device_id) {
++ case IGC_DEV_ID_I225_LM:
++ case IGC_DEV_ID_I225_V:
++ case IGC_DEV_ID_I225_I:
++ case IGC_DEV_ID_I225_K:
++ case IGC_DEV_ID_I225_K2:
++ case IGC_DEV_ID_I225_LMVP:
++ case IGC_DEV_ID_I225_IT:
++ return true;
++ default:
++ return false;
++ }
++}
++
++bool igc_is_device_id_i226(struct igc_hw *hw)
++{
++ switch (hw->device_id) {
++ case IGC_DEV_ID_I226_LM:
++ case IGC_DEV_ID_I226_V:
++ case IGC_DEV_ID_I226_K:
++ case IGC_DEV_ID_I226_IT:
++ return true;
++ default:
++ return false;
++ }
++}
++
+ static struct igc_mac_operations igc_mac_ops_base = {
+ .init_hw = igc_init_hw_base,
+ .check_for_link = igc_check_for_copper_link,
+--- a/drivers/net/ethernet/intel/igc/igc_base.h
++++ b/drivers/net/ethernet/intel/igc/igc_base.h
+@@ -7,6 +7,8 @@
+ /* forward declaration */
+ void igc_rx_fifo_flush_base(struct igc_hw *hw);
+ void igc_power_down_phy_copper_base(struct igc_hw *hw);
++bool igc_is_device_id_i225(struct igc_hw *hw);
++bool igc_is_device_id_i226(struct igc_hw *hw);
+
+ /* Transmit Descriptor - Advanced */
+ union igc_adv_tx_desc {
+--- a/drivers/net/ethernet/intel/igc/igc_defines.h
++++ b/drivers/net/ethernet/intel/igc/igc_defines.h
+@@ -515,6 +515,7 @@
+ /* Transmit Scheduling */
+ #define IGC_TQAVCTRL_TRANSMIT_MODE_TSN 0x00000001
+ #define IGC_TQAVCTRL_ENHANCED_QAV 0x00000008
++#define IGC_TQAVCTRL_FUTSCDDIS 0x00000080
+
+ #define IGC_TXQCTL_QUEUE_MODE_LAUNCHT 0x00000001
+ #define IGC_TXQCTL_STRICT_CYCLE 0x00000002
+--- a/drivers/net/ethernet/intel/igc/igc_main.c
++++ b/drivers/net/ethernet/intel/igc/igc_main.c
+@@ -6035,6 +6035,7 @@ static bool validate_schedule(struct igc
+ const struct tc_taprio_qopt_offload *qopt)
+ {
+ int queue_uses[IGC_MAX_TX_QUEUES] = { };
++ struct igc_hw *hw = &adapter->hw;
+ struct timespec64 now;
+ size_t n;
+
+@@ -6047,8 +6048,10 @@ static bool validate_schedule(struct igc
+ * in the future, it will hold all the packets until that
+ * time, causing a lot of TX Hangs, so to avoid that, we
+ * reject schedules that would start in the future.
++ * Note: Limitation above is no longer in i226.
+ */
+- if (!is_base_time_past(qopt->base_time, &now))
++ if (!is_base_time_past(qopt->base_time, &now) &&
++ igc_is_device_id_i225(hw))
+ return false;
+
+ for (n = 0; n < qopt->num_entries; n++) {
+--- a/drivers/net/ethernet/intel/igc/igc_tsn.c
++++ b/drivers/net/ethernet/intel/igc/igc_tsn.c
+@@ -2,6 +2,7 @@
+ /* Copyright (c) 2019 Intel Corporation */
+
+ #include "igc.h"
++#include "igc_hw.h"
+ #include "igc_tsn.h"
+
+ static bool is_any_launchtime(struct igc_adapter *adapter)
+@@ -62,7 +63,8 @@ static int igc_tsn_disable_offload(struc
+
+ tqavctrl = rd32(IGC_TQAVCTRL);
+ tqavctrl &= ~(IGC_TQAVCTRL_TRANSMIT_MODE_TSN |
+- IGC_TQAVCTRL_ENHANCED_QAV);
++ IGC_TQAVCTRL_ENHANCED_QAV | IGC_TQAVCTRL_FUTSCDDIS);
++
+ wr32(IGC_TQAVCTRL, tqavctrl);
+
+ for (i = 0; i < adapter->num_tx_queues; i++) {
+@@ -87,20 +89,10 @@ static int igc_tsn_enable_offload(struct
+ ktime_t base_time, systim;
+ int i;
+
+- cycle = adapter->cycle_time;
+- base_time = adapter->base_time;
+-
+ wr32(IGC_TSAUXC, 0);
+ wr32(IGC_DTXMXPKTSZ, IGC_DTXMXPKTSZ_TSN);
+ wr32(IGC_TXPBS, IGC_TXPBSIZE_TSN);
+
+- tqavctrl = rd32(IGC_TQAVCTRL);
+- tqavctrl |= IGC_TQAVCTRL_TRANSMIT_MODE_TSN | IGC_TQAVCTRL_ENHANCED_QAV;
+- wr32(IGC_TQAVCTRL, tqavctrl);
+-
+- wr32(IGC_QBVCYCLET_S, cycle);
+- wr32(IGC_QBVCYCLET, cycle);
+-
+ for (i = 0; i < adapter->num_tx_queues; i++) {
+ struct igc_ring *ring = adapter->tx_ring[i];
+ u32 txqctl = 0;
+@@ -203,21 +195,43 @@ skip_cbs:
+ wr32(IGC_TXQCTL(i), txqctl);
+ }
+
++ tqavctrl = rd32(IGC_TQAVCTRL);
++ tqavctrl |= IGC_TQAVCTRL_TRANSMIT_MODE_TSN | IGC_TQAVCTRL_ENHANCED_QAV;
++
++ cycle = adapter->cycle_time;
++ base_time = adapter->base_time;
++
+ nsec = rd32(IGC_SYSTIML);
+ sec = rd32(IGC_SYSTIMH);
+
+ systim = ktime_set(sec, nsec);
+-
+ if (ktime_compare(systim, base_time) > 0) {
+- s64 n;
++ s64 n = div64_s64(ktime_sub_ns(systim, base_time), cycle);
+
+- n = div64_s64(ktime_sub_ns(systim, base_time), cycle);
+ base_time = ktime_add_ns(base_time, (n + 1) * cycle);
++ } else {
++ /* According to datasheet section 7.5.2.9.3.3, FutScdDis bit
++ * has to be configured before the cycle time and base time.
++ */
++ if (igc_is_device_id_i226(hw))
++ tqavctrl |= IGC_TQAVCTRL_FUTSCDDIS;
+ }
+
+- baset_h = div_s64_rem(base_time, NSEC_PER_SEC, &baset_l);
++ wr32(IGC_TQAVCTRL, tqavctrl);
++
++ wr32(IGC_QBVCYCLET_S, cycle);
++ wr32(IGC_QBVCYCLET, cycle);
+
++ baset_h = div_s64_rem(base_time, NSEC_PER_SEC, &baset_l);
+ wr32(IGC_BASET_H, baset_h);
++
++ /* In i226, Future base time is only supported when FutScdDis bit
++ * is enabled and only active for re-configuration.
++ * In this case, initialize the base time with zero to create
++ * "re-configuration" scenario then only set the desired base time.
++ */
++ if (tqavctrl & IGC_TQAVCTRL_FUTSCDDIS)
++ wr32(IGC_BASET_L, 0);
+ wr32(IGC_BASET_L, baset_l);
+
+ return 0;
--- /dev/null
+From 1d1b4c63ba739c6ca695cb2ea13fefa9dfbff60d Mon Sep 17 00:00:00 2001
+From: Muhammad Husaini Zulkifli <muhammad.husaini.zulkifli@intel.com>
+Date: Thu, 15 Dec 2022 00:29:09 +0800
+Subject: igc: Remove reset adapter task for i226 during disable tsn config
+
+From: Muhammad Husaini Zulkifli <muhammad.husaini.zulkifli@intel.com>
+
+commit 1d1b4c63ba739c6ca695cb2ea13fefa9dfbff60d upstream.
+
+I225 have limitation when programming the BaseTime register which required
+a power cycle of the controller. This limitation already lifted in I226.
+This patch removes the restriction so that when user configure/remove any
+TSN mode, it would not go into power cycle reset adapter.
+
+How to test:
+
+Schedule any gate control list configuration or delete it.
+
+Example:
+
+1)
+
+BASE_TIME=$(date +%s%N)
+tc qdisc replace dev $interface_name parent root handle 100 taprio \
+ num_tc 4 \
+ map 3 1 0 2 3 3 3 3 3 3 3 3 3 3 3 3 \
+ queues 1@0 1@1 1@2 1@3 \
+ base-time $BASE_TIME \
+ sched-entry S 0F 1000000 \
+ flags 0x2
+
+2) tc qdisc del dev $intername_name root
+
+Signed-off-by: Muhammad Husaini Zulkifli <muhammad.husaini.zulkifli@intel.com>
+Tested-by: Naama Meir <naamax.meir@linux.intel.com>
+Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/ethernet/intel/igc/igc_main.c | 6 +++---
+ drivers/net/ethernet/intel/igc/igc_tsn.c | 11 +++--------
+ drivers/net/ethernet/intel/igc/igc_tsn.h | 2 +-
+ 3 files changed, 7 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/ethernet/intel/igc/igc_main.c
++++ b/drivers/net/ethernet/intel/igc/igc_main.c
+@@ -6097,7 +6097,7 @@ static int igc_tsn_enable_launchtime(str
+ if (err)
+ return err;
+
+- return igc_tsn_offload_apply(adapter, qopt->enable);
++ return igc_tsn_offload_apply(adapter);
+ }
+
+ static int igc_tsn_clear_schedule(struct igc_adapter *adapter)
+@@ -6211,7 +6211,7 @@ static int igc_tsn_enable_qbv_scheduling
+ if (err)
+ return err;
+
+- return igc_tsn_offload_apply(adapter, qopt->enable);
++ return igc_tsn_offload_apply(adapter);
+ }
+
+ static int igc_save_cbs_params(struct igc_adapter *adapter, int queue,
+@@ -6279,7 +6279,7 @@ static int igc_tsn_enable_cbs(struct igc
+ if (err)
+ return err;
+
+- return igc_tsn_offload_apply(adapter, qopt->enable);
++ return igc_tsn_offload_apply(adapter);
+ }
+
+ static int igc_setup_tc(struct net_device *dev, enum tc_setup_type type,
+--- a/drivers/net/ethernet/intel/igc/igc_tsn.c
++++ b/drivers/net/ethernet/intel/igc/igc_tsn.c
+@@ -259,21 +259,16 @@ int igc_tsn_reset(struct igc_adapter *ad
+ return err;
+ }
+
+-int igc_tsn_offload_apply(struct igc_adapter *adapter, bool enable)
++int igc_tsn_offload_apply(struct igc_adapter *adapter)
+ {
+ struct igc_hw *hw = &adapter->hw;
+- int err;
+
+- if (netif_running(adapter->netdev) &&
+- (igc_is_device_id_i225(hw) || !enable)) {
++ if (netif_running(adapter->netdev) && igc_is_device_id_i225(hw)) {
+ schedule_work(&adapter->reset_task);
+ return 0;
+ }
+
+- err = igc_tsn_enable_offload(adapter);
+- if (err < 0)
+- return err;
++ igc_tsn_reset(adapter);
+
+- adapter->flags = igc_tsn_new_flags(adapter);
+ return 0;
+ }
+--- a/drivers/net/ethernet/intel/igc/igc_tsn.h
++++ b/drivers/net/ethernet/intel/igc/igc_tsn.h
+@@ -4,7 +4,7 @@
+ #ifndef _IGC_TSN_H_
+ #define _IGC_TSN_H_
+
+-int igc_tsn_offload_apply(struct igc_adapter *adapter, bool enable);
++int igc_tsn_offload_apply(struct igc_adapter *adapter);
+ int igc_tsn_reset(struct igc_adapter *adapter);
+
+ #endif /* _IGC_BASE_H */
lib-kconfig.debug-do-not-enable-debug_preempt-by-default.patch
+igc-remove-i226-qbv-basetime-restriction.patch
+igc-enable-qbv-configuration-for-2nd-gcl.patch
+igc-remove-reset-adapter-task-for-i226-during-disable-tsn-config.patch
+igc-add-qbv_config_change_errors-counter.patch
+igc-add-condition-for-qbv_config_change_errors-counter.patch
+igc-fix-race-condition-in-ptp-tx-code.patch