@@ -98,6 +98,13 @@ struct igc_ring {
u32 start_time;
u32 end_time;
+ /* CBS parameters */
+ bool cbs_enable; /* indicates if CBS is enabled */
+ s32 idleslope; /* idleSlope in kbps */
+ s32 sendslope; /* sendSlope in kbps */
+ s32 hicredit; /* hiCredit in bytes */
+ s32 locredit; /* loCredit in bytes */
+
/* everything past this point are written often */
u16 next_to_clean;
u16 next_to_use;
@@ -290,8 +297,10 @@ extern char igc_driver_name[];
#define IGC_FLAG_VLAN_PROMISC BIT(15)
#define IGC_FLAG_RX_LEGACY BIT(16)
#define IGC_FLAG_TSN_QBV_ENABLED BIT(17)
+#define IGC_FLAG_TSN_QAV_ENABLED BIT(18)
-#define IGC_FLAG_TSN_ANY_ENABLED IGC_FLAG_TSN_QBV_ENABLED
+#define IGC_FLAG_TSN_ANY_ENABLED \
+ (IGC_FLAG_TSN_QBV_ENABLED | IGC_FLAG_TSN_QAV_ENABLED)
#define IGC_FLAG_RSS_FIELD_IPV4_UDP BIT(6)
#define IGC_FLAG_RSS_FIELD_IPV6_UDP BIT(7)
@@ -518,6 +518,14 @@
#define IGC_TXQCTL_QUEUE_MODE_LAUNCHT 0x00000001
#define IGC_TXQCTL_STRICT_CYCLE 0x00000002
#define IGC_TXQCTL_STRICT_END 0x00000004
+#define IGC_TXQCTL_QAV_SEL_MASK 0x000000C0
+#define IGC_TXQCTL_QAV_SEL_CBS0 0x00000080
+#define IGC_TXQCTL_QAV_SEL_CBS1 0x000000C0
+
+#define IGC_TQAVCC_IDLESLOPE_MASK 0xFFFF
+#define IGC_TQAVCC_KEEP_CREDITS BIT(30)
+
+#define IGC_MAX_SR_QUEUES 2
/* Receive Checksum Control */
#define IGC_RXCSUM_CRCOFL 0x00000800 /* CRC32 offload enable */
@@ -5904,6 +5904,74 @@ static int igc_tsn_enable_qbv_scheduling(struct igc_adapter *adapter,
return igc_tsn_offload_apply(adapter);
}
+static int igc_save_cbs_params(struct igc_adapter *adapter, int queue,
+ bool enable, int idleslope, int sendslope,
+ int hicredit, int locredit)
+{
+ bool cbs_status[IGC_MAX_SR_QUEUES] = { false };
+ struct net_device *netdev = adapter->netdev;
+ struct igc_ring *ring;
+ int i;
+
+ /* i225 has two sets of credit-based shaper logic.
+ * Supporting it only on the top two priority queues
+ */
+ if (queue < 0 || queue > 1)
+ return -EINVAL;
+
+ ring = adapter->tx_ring[queue];
+
+ for (i = 0; i < IGC_MAX_SR_QUEUES; i++)
+ if (adapter->tx_ring[i])
+ cbs_status[i] = adapter->tx_ring[i]->cbs_enable;
+
+ /* CBS should be enabled on the highest priority queue first in order
+ * for the CBS algorithm to operate as intended.
+ */
+ if (enable) {
+ if (queue == 1 && !cbs_status[0]) {
+ netdev_err(netdev,
+ "Enabling CBS on queue1 before queue0\n");
+ return -EINVAL;
+ }
+ } else {
+ if (queue == 0 && cbs_status[1]) {
+ netdev_err(netdev,
+ "Disabling CBS on queue0 before queue1\n");
+ return -EINVAL;
+ }
+ }
+
+ ring->cbs_enable = enable;
+ ring->idleslope = idleslope;
+ ring->sendslope = sendslope;
+ ring->hicredit = hicredit;
+ ring->locredit = locredit;
+
+ return 0;
+}
+
+static int igc_tsn_enable_cbs(struct igc_adapter *adapter,
+ struct tc_cbs_qopt_offload *qopt)
+{
+ struct igc_hw *hw = &adapter->hw;
+ int err;
+
+ if (hw->mac.type != igc_i225)
+ return -EOPNOTSUPP;
+
+ if (qopt->queue < 0 || qopt->queue > 1)
+ return -EINVAL;
+
+ err = igc_save_cbs_params(adapter, qopt->queue, qopt->enable,
+ qopt->idleslope, qopt->sendslope,
+ qopt->hicredit, qopt->locredit);
+ if (err)
+ return err;
+
+ return igc_tsn_offload_apply(adapter);
+}
+
static int igc_setup_tc(struct net_device *dev, enum tc_setup_type type,
void *type_data)
{
@@ -5916,6 +5984,9 @@ static int igc_setup_tc(struct net_device *dev, enum tc_setup_type type,
case TC_SETUP_QDISC_ETF:
return igc_tsn_enable_launchtime(adapter, type_data);
+ case TC_SETUP_QDISC_CBS:
+ return igc_tsn_enable_cbs(adapter, type_data);
+
default:
return -EOPNOTSUPP;
}
@@ -236,6 +236,9 @@
#define IGC_ENDQT(_n) (0x3334 + 0x4 * (_n))
#define IGC_DTXMXPKTSZ 0x355C
+#define IGC_TQAVCC(_n) (0x3004 + ((_n) * 0x40))
+#define IGC_TQAVHC(_n) (0x300C + ((_n) * 0x40))
+
/* System Time Registers */
#define IGC_SYSTIML 0x0B600 /* System time register Low - RO */
#define IGC_SYSTIMH 0x0B604 /* System time register High - RO */
@@ -18,6 +18,20 @@ static bool is_any_launchtime(struct igc_adapter *adapter)
return false;
}
+static bool is_cbs_enabled(struct igc_adapter *adapter)
+{
+ int i;
+
+ for (i = 0; i < adapter->num_tx_queues; i++) {
+ struct igc_ring *ring = adapter->tx_ring[i];
+
+ if (ring->cbs_enable)
+ return true;
+ }
+
+ return false;
+}
+
static unsigned int igc_tsn_new_flags(struct igc_adapter *adapter)
{
unsigned int new_flags = adapter->flags & ~IGC_FLAG_TSN_ANY_ENABLED;
@@ -28,6 +42,9 @@ static unsigned int igc_tsn_new_flags(struct igc_adapter *adapter)
if (is_any_launchtime(adapter))
new_flags |= IGC_FLAG_TSN_QBV_ENABLED;
+ if (is_cbs_enabled(adapter))
+ new_flags |= IGC_FLAG_TSN_QAV_ENABLED;
+
return new_flags;
}
@@ -87,6 +104,8 @@ static int igc_tsn_enable_offload(struct igc_adapter *adapter)
for (i = 0; i < adapter->num_tx_queues; i++) {
struct igc_ring *ring = adapter->tx_ring[i];
u32 txqctl = 0;
+ u16 cbs_value;
+ u32 tqavcc;
wr32(IGC_STQT(i), ring->start_time);
wr32(IGC_ENDQT(i), ring->end_time);
@@ -104,6 +123,90 @@ static int igc_tsn_enable_offload(struct igc_adapter *adapter)
if (ring->launchtime_enable)
txqctl |= IGC_TXQCTL_QUEUE_MODE_LAUNCHT;
+ /* Skip configuring CBS for Q2 and Q3 */
+ if (i > 1)
+ goto skip_cbs;
+
+ if (ring->cbs_enable) {
+ if (i == 0)
+ txqctl |= IGC_TXQCTL_QAV_SEL_CBS0;
+ else
+ txqctl |= IGC_TXQCTL_QAV_SEL_CBS1;
+
+ /* According to i225 datasheet section 7.5.2.7, we
+ * should set the 'idleSlope' field from TQAVCC
+ * register following the equation:
+ *
+ * value = link-speed 0x7736 * BW * 0.2
+ * ---------- * ----------------- (E1)
+ * 100Mbps 2.5
+ *
+ * Note that 'link-speed' is in Mbps.
+ *
+ * 'BW' is the percentage bandwidth out of full
+ * link speed which can be found with the
+ * following equation. Note that idleSlope here
+ * is the parameter from this function
+ * which is in kbps.
+ *
+ * BW = idleSlope
+ * ----------------- (E2)
+ * link-speed * 1000
+ *
+ * That said, we can come up with a generic
+ * equation to calculate the value we should set
+ * it TQAVCC register by replacing 'BW' in E1 by E2.
+ * The resulting equation is:
+ *
+ * value = link-speed * 0x7736 * idleSlope * 0.2
+ * ------------------------------------- (E3)
+ * 100 * 2.5 * link-speed * 1000
+ *
+ * 'link-speed' is present in both sides of the
+ * fraction so it is canceled out. The final
+ * equation is the following:
+ *
+ * value = idleSlope * 61036
+ * ----------------- (E4)
+ * 2500000
+ *
+ * NOTE: For i225, given the above, we can see
+ * that idleslope is represented in
+ * 40.959433 kbps units by the value at
+ * the TQAVCC register (2.5Gbps / 61036),
+ * which reduces the granularity for
+ * idleslope increments.
+ *
+ * In i225 controller, the sendSlope and loCredit
+ * parameters from CBS are not configurable
+ * by software so we don't do any
+ * 'controller configuration' in respect to
+ * these parameters.
+ */
+ cbs_value = DIV_ROUND_UP_ULL(ring->idleslope
+ * 61036ULL, 2500000);
+
+ tqavcc = rd32(IGC_TQAVCC(i));
+ tqavcc &= ~IGC_TQAVCC_IDLESLOPE_MASK;
+ tqavcc |= cbs_value | IGC_TQAVCC_KEEP_CREDITS;
+ wr32(IGC_TQAVCC(i), tqavcc);
+
+ wr32(IGC_TQAVHC(i),
+ 0x80000000 + ring->hicredit * 0x7735);
+ } else {
+ /* Disable any CBS for the queue */
+ txqctl &= ~(IGC_TXQCTL_QAV_SEL_MASK);
+
+ /* Set idleSlope to zero. */
+ tqavcc = rd32(IGC_TQAVCC(i));
+ tqavcc &= ~(IGC_TQAVCC_IDLESLOPE_MASK |
+ IGC_TQAVCC_KEEP_CREDITS);
+ wr32(IGC_TQAVCC(i), tqavcc);
+
+ /* Set hiCredit to zero. */
+ wr32(IGC_TQAVHC(i), 0);
+ }
+skip_cbs:
wr32(IGC_TXQCTL(i), txqctl);
}