|
@@ -485,6 +485,299 @@ static void fm10k_service_task(struct work_struct *work)
|
|
|
fm10k_service_event_complete(interface);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * fm10k_configure_tx_ring - Configure Tx ring after Reset
|
|
|
+ * @interface: board private structure
|
|
|
+ * @ring: structure containing ring specific data
|
|
|
+ *
|
|
|
+ * Configure the Tx descriptor ring after a reset.
|
|
|
+ **/
|
|
|
+static void fm10k_configure_tx_ring(struct fm10k_intfc *interface,
|
|
|
+ struct fm10k_ring *ring)
|
|
|
+{
|
|
|
+ struct fm10k_hw *hw = &interface->hw;
|
|
|
+ u64 tdba = ring->dma;
|
|
|
+ u32 size = ring->count * sizeof(struct fm10k_tx_desc);
|
|
|
+ u32 txint = FM10K_INT_MAP_DISABLE;
|
|
|
+ u32 txdctl = FM10K_TXDCTL_ENABLE | (1 << FM10K_TXDCTL_MAX_TIME_SHIFT);
|
|
|
+ u8 reg_idx = ring->reg_idx;
|
|
|
+
|
|
|
+ /* disable queue to avoid issues while updating state */
|
|
|
+ fm10k_write_reg(hw, FM10K_TXDCTL(reg_idx), 0);
|
|
|
+ fm10k_write_flush(hw);
|
|
|
+
|
|
|
+ /* possible poll here to verify ring resources have been cleaned */
|
|
|
+
|
|
|
+ /* set location and size for descriptor ring */
|
|
|
+ fm10k_write_reg(hw, FM10K_TDBAL(reg_idx), tdba & DMA_BIT_MASK(32));
|
|
|
+ fm10k_write_reg(hw, FM10K_TDBAH(reg_idx), tdba >> 32);
|
|
|
+ fm10k_write_reg(hw, FM10K_TDLEN(reg_idx), size);
|
|
|
+
|
|
|
+ /* reset head and tail pointers */
|
|
|
+ fm10k_write_reg(hw, FM10K_TDH(reg_idx), 0);
|
|
|
+ fm10k_write_reg(hw, FM10K_TDT(reg_idx), 0);
|
|
|
+
|
|
|
+ /* store tail pointer */
|
|
|
+ ring->tail = &interface->uc_addr[FM10K_TDT(reg_idx)];
|
|
|
+
|
|
|
+ /* reset ntu and ntc to place SW in sync with hardwdare */
|
|
|
+ ring->next_to_clean = 0;
|
|
|
+ ring->next_to_use = 0;
|
|
|
+
|
|
|
+ /* Map interrupt */
|
|
|
+ if (ring->q_vector) {
|
|
|
+ txint = ring->q_vector->v_idx + NON_Q_VECTORS(hw);
|
|
|
+ txint |= FM10K_INT_MAP_TIMER0;
|
|
|
+ }
|
|
|
+
|
|
|
+ fm10k_write_reg(hw, FM10K_TXINT(reg_idx), txint);
|
|
|
+
|
|
|
+ /* enable use of FTAG bit in Tx descriptor, register is RO for VF */
|
|
|
+ fm10k_write_reg(hw, FM10K_PFVTCTL(reg_idx),
|
|
|
+ FM10K_PFVTCTL_FTAG_DESC_ENABLE);
|
|
|
+
|
|
|
+ /* enable queue */
|
|
|
+ fm10k_write_reg(hw, FM10K_TXDCTL(reg_idx), txdctl);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * fm10k_enable_tx_ring - Verify Tx ring is enabled after configuration
|
|
|
+ * @interface: board private structure
|
|
|
+ * @ring: structure containing ring specific data
|
|
|
+ *
|
|
|
+ * Verify the Tx descriptor ring is ready for transmit.
|
|
|
+ **/
|
|
|
+static void fm10k_enable_tx_ring(struct fm10k_intfc *interface,
|
|
|
+ struct fm10k_ring *ring)
|
|
|
+{
|
|
|
+ struct fm10k_hw *hw = &interface->hw;
|
|
|
+ int wait_loop = 10;
|
|
|
+ u32 txdctl;
|
|
|
+ u8 reg_idx = ring->reg_idx;
|
|
|
+
|
|
|
+ /* if we are already enabled just exit */
|
|
|
+ if (fm10k_read_reg(hw, FM10K_TXDCTL(reg_idx)) & FM10K_TXDCTL_ENABLE)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* poll to verify queue is enabled */
|
|
|
+ do {
|
|
|
+ usleep_range(1000, 2000);
|
|
|
+ txdctl = fm10k_read_reg(hw, FM10K_TXDCTL(reg_idx));
|
|
|
+ } while (!(txdctl & FM10K_TXDCTL_ENABLE) && --wait_loop);
|
|
|
+ if (!wait_loop)
|
|
|
+ netif_err(interface, drv, interface->netdev,
|
|
|
+ "Could not enable Tx Queue %d\n", reg_idx);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * fm10k_configure_tx - Configure Transmit Unit after Reset
|
|
|
+ * @interface: board private structure
|
|
|
+ *
|
|
|
+ * Configure the Tx unit of the MAC after a reset.
|
|
|
+ **/
|
|
|
+static void fm10k_configure_tx(struct fm10k_intfc *interface)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ /* Setup the HW Tx Head and Tail descriptor pointers */
|
|
|
+ for (i = 0; i < interface->num_tx_queues; i++)
|
|
|
+ fm10k_configure_tx_ring(interface, interface->tx_ring[i]);
|
|
|
+
|
|
|
+ /* poll here to verify that Tx rings are now enabled */
|
|
|
+ for (i = 0; i < interface->num_tx_queues; i++)
|
|
|
+ fm10k_enable_tx_ring(interface, interface->tx_ring[i]);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * fm10k_configure_rx_ring - Configure Rx ring after Reset
|
|
|
+ * @interface: board private structure
|
|
|
+ * @ring: structure containing ring specific data
|
|
|
+ *
|
|
|
+ * Configure the Rx descriptor ring after a reset.
|
|
|
+ **/
|
|
|
+static void fm10k_configure_rx_ring(struct fm10k_intfc *interface,
|
|
|
+ struct fm10k_ring *ring)
|
|
|
+{
|
|
|
+ u64 rdba = ring->dma;
|
|
|
+ struct fm10k_hw *hw = &interface->hw;
|
|
|
+ u32 size = ring->count * sizeof(union fm10k_rx_desc);
|
|
|
+ u32 rxqctl = FM10K_RXQCTL_ENABLE | FM10K_RXQCTL_PF;
|
|
|
+ u32 rxdctl = FM10K_RXDCTL_WRITE_BACK_MIN_DELAY;
|
|
|
+ u32 srrctl = FM10K_SRRCTL_BUFFER_CHAINING_EN;
|
|
|
+ u32 rxint = FM10K_INT_MAP_DISABLE;
|
|
|
+ u8 rx_pause = interface->rx_pause;
|
|
|
+ u8 reg_idx = ring->reg_idx;
|
|
|
+
|
|
|
+ /* disable queue to avoid issues while updating state */
|
|
|
+ fm10k_write_reg(hw, FM10K_RXQCTL(reg_idx), 0);
|
|
|
+ fm10k_write_flush(hw);
|
|
|
+
|
|
|
+ /* possible poll here to verify ring resources have been cleaned */
|
|
|
+
|
|
|
+ /* set location and size for descriptor ring */
|
|
|
+ fm10k_write_reg(hw, FM10K_RDBAL(reg_idx), rdba & DMA_BIT_MASK(32));
|
|
|
+ fm10k_write_reg(hw, FM10K_RDBAH(reg_idx), rdba >> 32);
|
|
|
+ fm10k_write_reg(hw, FM10K_RDLEN(reg_idx), size);
|
|
|
+
|
|
|
+ /* reset head and tail pointers */
|
|
|
+ fm10k_write_reg(hw, FM10K_RDH(reg_idx), 0);
|
|
|
+ fm10k_write_reg(hw, FM10K_RDT(reg_idx), 0);
|
|
|
+
|
|
|
+ /* store tail pointer */
|
|
|
+ ring->tail = &interface->uc_addr[FM10K_RDT(reg_idx)];
|
|
|
+
|
|
|
+ /* reset ntu and ntc to place SW in sync with hardwdare */
|
|
|
+ ring->next_to_clean = 0;
|
|
|
+ ring->next_to_use = 0;
|
|
|
+ ring->next_to_alloc = 0;
|
|
|
+
|
|
|
+ /* Configure the Rx buffer size for one buff without split */
|
|
|
+ srrctl |= FM10K_RX_BUFSZ >> FM10K_SRRCTL_BSIZEPKT_SHIFT;
|
|
|
+
|
|
|
+ /* Configure the Rx ring to supress loopback packets */
|
|
|
+ srrctl |= FM10K_SRRCTL_LOOPBACK_SUPPRESS;
|
|
|
+ fm10k_write_reg(hw, FM10K_SRRCTL(reg_idx), srrctl);
|
|
|
+
|
|
|
+ /* Enable drop on empty */
|
|
|
+#if defined(HAVE_DCBNL_IEEE) && defined(CONFIG_DCB)
|
|
|
+ if (interface->pfc_en)
|
|
|
+ rx_pause = interface->pfc_en;
|
|
|
+#endif
|
|
|
+ if (!(rx_pause & (1 << ring->qos_pc)))
|
|
|
+ rxdctl |= FM10K_RXDCTL_DROP_ON_EMPTY;
|
|
|
+
|
|
|
+ fm10k_write_reg(hw, FM10K_RXDCTL(reg_idx), rxdctl);
|
|
|
+
|
|
|
+ /* assign default VLAN to queue */
|
|
|
+ ring->vid = hw->mac.default_vid;
|
|
|
+
|
|
|
+ /* Map interrupt */
|
|
|
+ if (ring->q_vector) {
|
|
|
+ rxint = ring->q_vector->v_idx + NON_Q_VECTORS(hw);
|
|
|
+ rxint |= FM10K_INT_MAP_TIMER1;
|
|
|
+ }
|
|
|
+
|
|
|
+ fm10k_write_reg(hw, FM10K_RXINT(reg_idx), rxint);
|
|
|
+
|
|
|
+ /* enable queue */
|
|
|
+ fm10k_write_reg(hw, FM10K_RXQCTL(reg_idx), rxqctl);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * fm10k_update_rx_drop_en - Configures the drop enable bits for Rx rings
|
|
|
+ * @interface: board private structure
|
|
|
+ *
|
|
|
+ * Configure the drop enable bits for the Rx rings.
|
|
|
+ **/
|
|
|
+void fm10k_update_rx_drop_en(struct fm10k_intfc *interface)
|
|
|
+{
|
|
|
+ struct fm10k_hw *hw = &interface->hw;
|
|
|
+ u8 rx_pause = interface->rx_pause;
|
|
|
+ int i;
|
|
|
+
|
|
|
+#if defined(HAVE_DCBNL_IEEE) && defined(CONFIG_DCB)
|
|
|
+ if (interface->pfc_en)
|
|
|
+ rx_pause = interface->pfc_en;
|
|
|
+
|
|
|
+#endif
|
|
|
+ for (i = 0; i < interface->num_rx_queues; i++) {
|
|
|
+ struct fm10k_ring *ring = interface->rx_ring[i];
|
|
|
+ u32 rxdctl = FM10K_RXDCTL_WRITE_BACK_MIN_DELAY;
|
|
|
+ u8 reg_idx = ring->reg_idx;
|
|
|
+
|
|
|
+ if (!(rx_pause & (1 << ring->qos_pc)))
|
|
|
+ rxdctl |= FM10K_RXDCTL_DROP_ON_EMPTY;
|
|
|
+
|
|
|
+ fm10k_write_reg(hw, FM10K_RXDCTL(reg_idx), rxdctl);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * fm10k_configure_dglort - Configure Receive DGLORT after reset
|
|
|
+ * @interface: board private structure
|
|
|
+ *
|
|
|
+ * Configure the DGLORT description and RSS tables.
|
|
|
+ **/
|
|
|
+static void fm10k_configure_dglort(struct fm10k_intfc *interface)
|
|
|
+{
|
|
|
+ struct fm10k_dglort_cfg dglort = { 0 };
|
|
|
+ struct fm10k_hw *hw = &interface->hw;
|
|
|
+ int i;
|
|
|
+ u32 mrqc;
|
|
|
+
|
|
|
+ /* Fill out hash function seeds */
|
|
|
+ for (i = 0; i < FM10K_RSSRK_SIZE; i++)
|
|
|
+ fm10k_write_reg(hw, FM10K_RSSRK(0, i), interface->rssrk[i]);
|
|
|
+
|
|
|
+ /* Write RETA table to hardware */
|
|
|
+ for (i = 0; i < FM10K_RETA_SIZE; i++)
|
|
|
+ fm10k_write_reg(hw, FM10K_RETA(0, i), interface->reta[i]);
|
|
|
+
|
|
|
+ /* Generate RSS hash based on packet types, TCP/UDP
|
|
|
+ * port numbers and/or IPv4/v6 src and dst addresses
|
|
|
+ */
|
|
|
+ mrqc = FM10K_MRQC_IPV4 |
|
|
|
+ FM10K_MRQC_TCP_IPV4 |
|
|
|
+ FM10K_MRQC_IPV6 |
|
|
|
+ FM10K_MRQC_TCP_IPV6;
|
|
|
+
|
|
|
+ if (interface->flags & FM10K_FLAG_RSS_FIELD_IPV4_UDP)
|
|
|
+ mrqc |= FM10K_MRQC_UDP_IPV4;
|
|
|
+ if (interface->flags & FM10K_FLAG_RSS_FIELD_IPV6_UDP)
|
|
|
+ mrqc |= FM10K_MRQC_UDP_IPV6;
|
|
|
+
|
|
|
+ fm10k_write_reg(hw, FM10K_MRQC(0), mrqc);
|
|
|
+
|
|
|
+ /* configure default DGLORT mapping for RSS/DCB */
|
|
|
+ dglort.inner_rss = 1;
|
|
|
+ dglort.rss_l = fls(interface->ring_feature[RING_F_RSS].mask);
|
|
|
+ dglort.pc_l = fls(interface->ring_feature[RING_F_QOS].mask);
|
|
|
+ hw->mac.ops.configure_dglort_map(hw, &dglort);
|
|
|
+
|
|
|
+ /* assign GLORT per queue for queue mapped testing */
|
|
|
+ if (interface->glort_count > 64) {
|
|
|
+ memset(&dglort, 0, sizeof(dglort));
|
|
|
+ dglort.inner_rss = 1;
|
|
|
+ dglort.glort = interface->glort + 64;
|
|
|
+ dglort.idx = fm10k_dglort_pf_queue;
|
|
|
+ dglort.queue_l = fls(interface->num_rx_queues - 1);
|
|
|
+ hw->mac.ops.configure_dglort_map(hw, &dglort);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* assign glort value for RSS/DCB specific to this interface */
|
|
|
+ memset(&dglort, 0, sizeof(dglort));
|
|
|
+ dglort.inner_rss = 1;
|
|
|
+ dglort.glort = interface->glort;
|
|
|
+ dglort.rss_l = fls(interface->ring_feature[RING_F_RSS].mask);
|
|
|
+ dglort.pc_l = fls(interface->ring_feature[RING_F_QOS].mask);
|
|
|
+ /* configure DGLORT mapping for RSS/DCB */
|
|
|
+ dglort.idx = fm10k_dglort_pf_rss;
|
|
|
+ hw->mac.ops.configure_dglort_map(hw, &dglort);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * fm10k_configure_rx - Configure Receive Unit after Reset
|
|
|
+ * @interface: board private structure
|
|
|
+ *
|
|
|
+ * Configure the Rx unit of the MAC after a reset.
|
|
|
+ **/
|
|
|
+static void fm10k_configure_rx(struct fm10k_intfc *interface)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ /* Configure SWPRI to PC map */
|
|
|
+ fm10k_configure_swpri_map(interface);
|
|
|
+
|
|
|
+ /* Configure RSS and DGLORT map */
|
|
|
+ fm10k_configure_dglort(interface);
|
|
|
+
|
|
|
+ /* Setup the HW Rx Head and Tail descriptor pointers */
|
|
|
+ for (i = 0; i < interface->num_rx_queues; i++)
|
|
|
+ fm10k_configure_rx_ring(interface, interface->rx_ring[i]);
|
|
|
+
|
|
|
+ /* possible poll here to verify that Rx rings are now enabled */
|
|
|
+}
|
|
|
+
|
|
|
static void fm10k_napi_enable_all(struct fm10k_intfc *interface)
|
|
|
{
|
|
|
struct fm10k_q_vector *q_vector;
|
|
@@ -970,6 +1263,12 @@ void fm10k_up(struct fm10k_intfc *interface)
|
|
|
/* Enable Tx/Rx DMA */
|
|
|
hw->mac.ops.start_hw(hw);
|
|
|
|
|
|
+ /* configure Tx descriptor rings */
|
|
|
+ fm10k_configure_tx(interface);
|
|
|
+
|
|
|
+ /* configure Rx descriptor rings */
|
|
|
+ fm10k_configure_rx(interface);
|
|
|
+
|
|
|
/* configure interrupts */
|
|
|
hw->mac.ops.update_int_moderator(hw);
|
|
|
|
|
@@ -1031,6 +1330,9 @@ void fm10k_down(struct fm10k_intfc *interface)
|
|
|
|
|
|
/* Disable DMA engine for Tx/Rx */
|
|
|
hw->mac.ops.stop_hw(hw);
|
|
|
+
|
|
|
+ /* free any buffers still on the rings */
|
|
|
+ fm10k_clean_all_tx_rings(interface);
|
|
|
}
|
|
|
|
|
|
/**
|