Imported Upstream version 16.04
[deb_dpdk.git] / drivers / net / fm10k / base / fm10k_common.c
diff --git a/drivers/net/fm10k/base/fm10k_common.c b/drivers/net/fm10k/base/fm10k_common.c
new file mode 100644 (file)
index 0000000..a90d2f0
--- /dev/null
@@ -0,0 +1,572 @@
+/*******************************************************************************
+
+Copyright (c) 2013 - 2015, Intel Corporation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the Intel Corporation nor the names of its
+    contributors may be used to endorse or promote products derived from
+    this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#include "fm10k_common.h"
+
+/**
+ *  fm10k_get_bus_info_generic - Generic set PCI bus info
+ *  @hw: pointer to hardware structure
+ *
+ *  Gets the PCI bus info (speed, width, type) then calls helper function to
+ *  store this data within the fm10k_hw structure.
+ **/
+STATIC s32 fm10k_get_bus_info_generic(struct fm10k_hw *hw)
+{
+       u16 link_cap, link_status, device_cap, device_control;
+
+       DEBUGFUNC("fm10k_get_bus_info_generic");
+
+       /* Get the maximum link width and speed from PCIe config space */
+       link_cap = FM10K_READ_PCI_WORD(hw, FM10K_PCIE_LINK_CAP);
+
+       switch (link_cap & FM10K_PCIE_LINK_WIDTH) {
+       case FM10K_PCIE_LINK_WIDTH_1:
+               hw->bus_caps.width = fm10k_bus_width_pcie_x1;
+               break;
+       case FM10K_PCIE_LINK_WIDTH_2:
+               hw->bus_caps.width = fm10k_bus_width_pcie_x2;
+               break;
+       case FM10K_PCIE_LINK_WIDTH_4:
+               hw->bus_caps.width = fm10k_bus_width_pcie_x4;
+               break;
+       case FM10K_PCIE_LINK_WIDTH_8:
+               hw->bus_caps.width = fm10k_bus_width_pcie_x8;
+               break;
+       default:
+               hw->bus_caps.width = fm10k_bus_width_unknown;
+               break;
+       }
+
+       switch (link_cap & FM10K_PCIE_LINK_SPEED) {
+       case FM10K_PCIE_LINK_SPEED_2500:
+               hw->bus_caps.speed = fm10k_bus_speed_2500;
+               break;
+       case FM10K_PCIE_LINK_SPEED_5000:
+               hw->bus_caps.speed = fm10k_bus_speed_5000;
+               break;
+       case FM10K_PCIE_LINK_SPEED_8000:
+               hw->bus_caps.speed = fm10k_bus_speed_8000;
+               break;
+       default:
+               hw->bus_caps.speed = fm10k_bus_speed_unknown;
+               break;
+       }
+
+       /* Get the PCIe maximum payload size for the PCIe function */
+       device_cap = FM10K_READ_PCI_WORD(hw, FM10K_PCIE_DEV_CAP);
+
+       switch (device_cap & FM10K_PCIE_DEV_CAP_PAYLOAD) {
+       case FM10K_PCIE_DEV_CAP_PAYLOAD_128:
+               hw->bus_caps.payload = fm10k_bus_payload_128;
+               break;
+       case FM10K_PCIE_DEV_CAP_PAYLOAD_256:
+               hw->bus_caps.payload = fm10k_bus_payload_256;
+               break;
+       case FM10K_PCIE_DEV_CAP_PAYLOAD_512:
+               hw->bus_caps.payload = fm10k_bus_payload_512;
+               break;
+       default:
+               hw->bus_caps.payload = fm10k_bus_payload_unknown;
+               break;
+       }
+
+       /* Get the negotiated link width and speed from PCIe config space */
+       link_status = FM10K_READ_PCI_WORD(hw, FM10K_PCIE_LINK_STATUS);
+
+       switch (link_status & FM10K_PCIE_LINK_WIDTH) {
+       case FM10K_PCIE_LINK_WIDTH_1:
+               hw->bus.width = fm10k_bus_width_pcie_x1;
+               break;
+       case FM10K_PCIE_LINK_WIDTH_2:
+               hw->bus.width = fm10k_bus_width_pcie_x2;
+               break;
+       case FM10K_PCIE_LINK_WIDTH_4:
+               hw->bus.width = fm10k_bus_width_pcie_x4;
+               break;
+       case FM10K_PCIE_LINK_WIDTH_8:
+               hw->bus.width = fm10k_bus_width_pcie_x8;
+               break;
+       default:
+               hw->bus.width = fm10k_bus_width_unknown;
+               break;
+       }
+
+       switch (link_status & FM10K_PCIE_LINK_SPEED) {
+       case FM10K_PCIE_LINK_SPEED_2500:
+               hw->bus.speed = fm10k_bus_speed_2500;
+               break;
+       case FM10K_PCIE_LINK_SPEED_5000:
+               hw->bus.speed = fm10k_bus_speed_5000;
+               break;
+       case FM10K_PCIE_LINK_SPEED_8000:
+               hw->bus.speed = fm10k_bus_speed_8000;
+               break;
+       default:
+               hw->bus.speed = fm10k_bus_speed_unknown;
+               break;
+       }
+
+       /* Get the negotiated PCIe maximum payload size for the PCIe function */
+       device_control = FM10K_READ_PCI_WORD(hw, FM10K_PCIE_DEV_CTRL);
+
+       switch (device_control & FM10K_PCIE_DEV_CTRL_PAYLOAD) {
+       case FM10K_PCIE_DEV_CTRL_PAYLOAD_128:
+               hw->bus.payload = fm10k_bus_payload_128;
+               break;
+       case FM10K_PCIE_DEV_CTRL_PAYLOAD_256:
+               hw->bus.payload = fm10k_bus_payload_256;
+               break;
+       case FM10K_PCIE_DEV_CTRL_PAYLOAD_512:
+               hw->bus.payload = fm10k_bus_payload_512;
+               break;
+       default:
+               hw->bus.payload = fm10k_bus_payload_unknown;
+               break;
+       }
+
+       return FM10K_SUCCESS;
+}
+
+u16 fm10k_get_pcie_msix_count_generic(struct fm10k_hw *hw)
+{
+       u16 msix_count;
+
+       DEBUGFUNC("fm10k_get_pcie_msix_count_generic");
+
+       /* read in value from MSI-X capability register */
+       msix_count = FM10K_READ_PCI_WORD(hw, FM10K_PCI_MSIX_MSG_CTRL);
+       msix_count &= FM10K_PCI_MSIX_MSG_CTRL_TBL_SZ_MASK;
+
+       /* MSI-X count is zero-based in HW */
+       msix_count++;
+
+       if (msix_count > FM10K_MAX_MSIX_VECTORS)
+               msix_count = FM10K_MAX_MSIX_VECTORS;
+
+       return msix_count;
+}
+
+/**
+ *  fm10k_init_ops_generic - Inits function ptrs
+ *  @hw: pointer to the hardware structure
+ *
+ *  Initialize the function pointers.
+ **/
+s32 fm10k_init_ops_generic(struct fm10k_hw *hw)
+{
+       struct fm10k_mac_info *mac = &hw->mac;
+
+       DEBUGFUNC("fm10k_init_ops_generic");
+
+       /* MAC */
+       mac->ops.get_bus_info = &fm10k_get_bus_info_generic;
+
+       /* initialize GLORT state to avoid any false hits */
+       mac->dglort_map = FM10K_DGLORTMAP_NONE;
+
+       return FM10K_SUCCESS;
+}
+
+/**
+ *  fm10k_start_hw_generic - Prepare hardware for Tx/Rx
+ *  @hw: pointer to hardware structure
+ *
+ *  This function sets the Tx ready flag to indicate that the Tx path has
+ *  been initialized.
+ **/
+s32 fm10k_start_hw_generic(struct fm10k_hw *hw)
+{
+       DEBUGFUNC("fm10k_start_hw_generic");
+
+       /* set flag indicating we are beginning Tx */
+       hw->mac.tx_ready = true;
+
+       return FM10K_SUCCESS;
+}
+
+/**
+ *  fm10k_disable_queues_generic - Stop Tx/Rx queues
+ *  @hw: pointer to hardware structure
+ *  @q_cnt: number of queues to be disabled
+ *
+ **/
+s32 fm10k_disable_queues_generic(struct fm10k_hw *hw, u16 q_cnt)
+{
+       u32 reg;
+       u16 i, time;
+
+       DEBUGFUNC("fm10k_disable_queues_generic");
+
+       /* clear tx_ready to prevent any false hits for reset */
+       hw->mac.tx_ready = false;
+
+       /* clear the enable bit for all rings */
+       for (i = 0; i < q_cnt; i++) {
+               reg = FM10K_READ_REG(hw, FM10K_TXDCTL(i));
+               FM10K_WRITE_REG(hw, FM10K_TXDCTL(i),
+                               reg & ~FM10K_TXDCTL_ENABLE);
+               reg = FM10K_READ_REG(hw, FM10K_RXQCTL(i));
+               FM10K_WRITE_REG(hw, FM10K_RXQCTL(i),
+                               reg & ~FM10K_RXQCTL_ENABLE);
+       }
+
+       FM10K_WRITE_FLUSH(hw);
+       usec_delay(1);
+
+       /* loop through all queues to verify that they are all disabled */
+       for (i = 0, time = FM10K_QUEUE_DISABLE_TIMEOUT; time;) {
+               /* if we are at end of rings all rings are disabled */
+               if (i == q_cnt)
+                       return FM10K_SUCCESS;
+
+               /* if queue enables cleared, then move to next ring pair */
+               reg = FM10K_READ_REG(hw, FM10K_TXDCTL(i));
+               if (!~reg || !(reg & FM10K_TXDCTL_ENABLE)) {
+                       reg = FM10K_READ_REG(hw, FM10K_RXQCTL(i));
+                       if (!~reg || !(reg & FM10K_RXQCTL_ENABLE)) {
+                               i++;
+                               continue;
+                       }
+               }
+
+               /* decrement time and wait 1 usec */
+               time--;
+               if (time)
+                       usec_delay(1);
+       }
+
+       return FM10K_ERR_REQUESTS_PENDING;
+}
+
+/**
+ *  fm10k_stop_hw_generic - Stop Tx/Rx units
+ *  @hw: pointer to hardware structure
+ *
+ **/
+s32 fm10k_stop_hw_generic(struct fm10k_hw *hw)
+{
+       DEBUGFUNC("fm10k_stop_hw_generic");
+
+       return fm10k_disable_queues_generic(hw, hw->mac.max_queues);
+}
+
+/**
+ *  fm10k_read_hw_stats_32b - Reads value of 32-bit registers
+ *  @hw: pointer to the hardware structure
+ *  @addr: address of register containing a 32-bit value
+ *
+ *  Function reads the content of the register and returns the delta
+ *  between the base and the current value.
+ *  **/
+u32 fm10k_read_hw_stats_32b(struct fm10k_hw *hw, u32 addr,
+                           struct fm10k_hw_stat *stat)
+{
+       u32 delta = FM10K_READ_REG(hw, addr) - stat->base_l;
+
+       DEBUGFUNC("fm10k_read_hw_stats_32b");
+
+       if (FM10K_REMOVED(hw->hw_addr))
+               stat->base_h = 0;
+
+       return delta;
+}
+
+/**
+ *  fm10k_read_hw_stats_48b - Reads value of 48-bit registers
+ *  @hw: pointer to the hardware structure
+ *  @addr: address of register containing the lower 32-bit value
+ *
+ *  Function reads the content of 2 registers, combined to represent a 48-bit
+ *  statistical value. Extra processing is required to handle overflowing.
+ *  Finally, a delta value is returned representing the difference between the
+ *  values stored in registers and values stored in the statistic counters.
+ *  **/
+STATIC u64 fm10k_read_hw_stats_48b(struct fm10k_hw *hw, u32 addr,
+                                  struct fm10k_hw_stat *stat)
+{
+       u32 count_l;
+       u32 count_h;
+       u32 count_tmp;
+       u64 delta;
+
+       DEBUGFUNC("fm10k_read_hw_stats_48b");
+
+       count_h = FM10K_READ_REG(hw, addr + 1);
+
+       /* Check for overflow */
+       do {
+               count_tmp = count_h;
+               count_l = FM10K_READ_REG(hw, addr);
+               count_h = FM10K_READ_REG(hw, addr + 1);
+       } while (count_h != count_tmp);
+
+       delta = ((u64)(count_h - stat->base_h) << 32) + count_l;
+       delta -= stat->base_l;
+
+       return delta & FM10K_48_BIT_MASK;
+}
+
+/**
+ *  fm10k_update_hw_base_48b - Updates 48-bit statistic base value
+ *  @stat: pointer to the hardware statistic structure
+ *  @delta: value to be updated into the hardware statistic structure
+ *
+ *  Function receives a value and determines if an update is required based on
+ *  a delta calculation. Only the base value will be updated.
+ **/
+STATIC void fm10k_update_hw_base_48b(struct fm10k_hw_stat *stat, u64 delta)
+{
+       DEBUGFUNC("fm10k_update_hw_base_48b");
+
+       if (!delta)
+               return;
+
+       /* update lower 32 bits */
+       delta += stat->base_l;
+       stat->base_l = (u32)delta;
+
+       /* update upper 32 bits */
+       stat->base_h += (u32)(delta >> 32);
+}
+
+/**
+ *  fm10k_update_hw_stats_tx_q - Updates TX queue statistics counters
+ *  @hw: pointer to the hardware structure
+ *  @q: pointer to the ring of hardware statistics queue
+ *  @idx: index pointing to the start of the ring iteration
+ *
+ *  Function updates the TX queue statistics counters that are related to the
+ *  hardware.
+ **/
+STATIC void fm10k_update_hw_stats_tx_q(struct fm10k_hw *hw,
+                                      struct fm10k_hw_stats_q *q,
+                                      u32 idx)
+{
+       u32 id_tx, id_tx_prev, tx_packets;
+       u64 tx_bytes = 0;
+
+       DEBUGFUNC("fm10k_update_hw_stats_tx_q");
+
+       /* Retrieve TX Owner Data */
+       id_tx = FM10K_READ_REG(hw, FM10K_TXQCTL(idx));
+
+       /* Process TX Ring */
+       do {
+               tx_packets = fm10k_read_hw_stats_32b(hw, FM10K_QPTC(idx),
+                                                    &q->tx_packets);
+
+               if (tx_packets)
+                       tx_bytes = fm10k_read_hw_stats_48b(hw,
+                                                          FM10K_QBTC_L(idx),
+                                                          &q->tx_bytes);
+
+               /* Re-Check Owner Data */
+               id_tx_prev = id_tx;
+               id_tx = FM10K_READ_REG(hw, FM10K_TXQCTL(idx));
+       } while ((id_tx ^ id_tx_prev) & FM10K_TXQCTL_ID_MASK);
+
+       /* drop non-ID bits and set VALID ID bit */
+       id_tx &= FM10K_TXQCTL_ID_MASK;
+       id_tx |= FM10K_STAT_VALID;
+
+       /* update packet counts */
+       if (q->tx_stats_idx == id_tx) {
+               q->tx_packets.count += tx_packets;
+               q->tx_bytes.count += tx_bytes;
+       }
+
+       /* update bases and record ID */
+       fm10k_update_hw_base_32b(&q->tx_packets, tx_packets);
+       fm10k_update_hw_base_48b(&q->tx_bytes, tx_bytes);
+
+       q->tx_stats_idx = id_tx;
+}
+
+/**
+ *  fm10k_update_hw_stats_rx_q - Updates RX queue statistics counters
+ *  @hw: pointer to the hardware structure
+ *  @q: pointer to the ring of hardware statistics queue
+ *  @idx: index pointing to the start of the ring iteration
+ *
+ *  Function updates the RX queue statistics counters that are related to the
+ *  hardware.
+ **/
+STATIC void fm10k_update_hw_stats_rx_q(struct fm10k_hw *hw,
+                                      struct fm10k_hw_stats_q *q,
+                                      u32 idx)
+{
+       u32 id_rx, id_rx_prev, rx_packets, rx_drops;
+       u64 rx_bytes = 0;
+
+       DEBUGFUNC("fm10k_update_hw_stats_rx_q");
+
+       /* Retrieve RX Owner Data */
+       id_rx = FM10K_READ_REG(hw, FM10K_RXQCTL(idx));
+
+       /* Process RX Ring */
+       do {
+               rx_drops = fm10k_read_hw_stats_32b(hw, FM10K_QPRDC(idx),
+                                                  &q->rx_drops);
+
+               rx_packets = fm10k_read_hw_stats_32b(hw, FM10K_QPRC(idx),
+                                                    &q->rx_packets);
+
+               if (rx_packets)
+                       rx_bytes = fm10k_read_hw_stats_48b(hw,
+                                                          FM10K_QBRC_L(idx),
+                                                          &q->rx_bytes);
+
+               /* Re-Check Owner Data */
+               id_rx_prev = id_rx;
+               id_rx = FM10K_READ_REG(hw, FM10K_RXQCTL(idx));
+       } while ((id_rx ^ id_rx_prev) & FM10K_RXQCTL_ID_MASK);
+
+       /* drop non-ID bits and set VALID ID bit */
+       id_rx &= FM10K_RXQCTL_ID_MASK;
+       id_rx |= FM10K_STAT_VALID;
+
+       /* update packet counts */
+       if (q->rx_stats_idx == id_rx) {
+               q->rx_drops.count += rx_drops;
+               q->rx_packets.count += rx_packets;
+               q->rx_bytes.count += rx_bytes;
+       }
+
+       /* update bases and record ID */
+       fm10k_update_hw_base_32b(&q->rx_drops, rx_drops);
+       fm10k_update_hw_base_32b(&q->rx_packets, rx_packets);
+       fm10k_update_hw_base_48b(&q->rx_bytes, rx_bytes);
+
+       q->rx_stats_idx = id_rx;
+}
+
+/**
+ *  fm10k_update_hw_stats_q - Updates queue statistics counters
+ *  @hw: pointer to the hardware structure
+ *  @q: pointer to the ring of hardware statistics queue
+ *  @idx: index pointing to the start of the ring iteration
+ *  @count: number of queues to iterate over
+ *
+ *  Function updates the queue statistics counters that are related to the
+ *  hardware.
+ **/
+void fm10k_update_hw_stats_q(struct fm10k_hw *hw, struct fm10k_hw_stats_q *q,
+                            u32 idx, u32 count)
+{
+       u32 i;
+
+       DEBUGFUNC("fm10k_update_hw_stats_q");
+
+       for (i = 0; i < count; i++, idx++, q++) {
+               fm10k_update_hw_stats_tx_q(hw, q, idx);
+               fm10k_update_hw_stats_rx_q(hw, q, idx);
+       }
+}
+
+/**
+ *  fm10k_unbind_hw_stats_q - Unbind the queue counters from their queues
+ *  @hw: pointer to the hardware structure
+ *  @q: pointer to the ring of hardware statistics queue
+ *  @idx: index pointing to the start of the ring iteration
+ *  @count: number of queues to iterate over
+ *
+ *  Function invalidates the index values for the queues so any updates that
+ *  may have happened are ignored and the base for the queue stats is reset.
+ **/
+void fm10k_unbind_hw_stats_q(struct fm10k_hw_stats_q *q, u32 idx, u32 count)
+{
+       u32 i;
+
+       for (i = 0; i < count; i++, idx++, q++) {
+               q->rx_stats_idx = 0;
+               q->tx_stats_idx = 0;
+       }
+}
+
+/**
+ *  fm10k_get_host_state_generic - Returns the state of the host
+ *  @hw: pointer to hardware structure
+ *  @host_ready: pointer to boolean value that will record host state
+ *
+ *  This function will check the health of the mailbox and Tx queue 0
+ *  in order to determine if we should report that the link is up or not.
+ **/
+s32 fm10k_get_host_state_generic(struct fm10k_hw *hw, bool *host_ready)
+{
+       struct fm10k_mbx_info *mbx = &hw->mbx;
+       struct fm10k_mac_info *mac = &hw->mac;
+       s32 ret_val = FM10K_SUCCESS;
+       u32 txdctl = FM10K_READ_REG(hw, FM10K_TXDCTL(0));
+
+       DEBUGFUNC("fm10k_get_host_state_generic");
+
+       /* process upstream mailbox in case interrupts were disabled */
+       mbx->ops.process(hw, mbx);
+
+       /* If Tx is no longer enabled link should come down */
+       if (!(~txdctl) || !(txdctl & FM10K_TXDCTL_ENABLE))
+               mac->get_host_state = true;
+
+       /* exit if not checking for link, or link cannot be changed */
+       if (!mac->get_host_state || !(~txdctl))
+               goto out;
+
+       /* if we somehow dropped the Tx enable we should reset */
+       if (hw->mac.tx_ready && !(txdctl & FM10K_TXDCTL_ENABLE)) {
+               ret_val = FM10K_ERR_RESET_REQUESTED;
+               goto out;
+       }
+
+       /* if Mailbox timed out we should request reset */
+       if (!mbx->timeout) {
+               ret_val = FM10K_ERR_RESET_REQUESTED;
+               goto out;
+       }
+
+       /* verify Mailbox is still valid */
+       if (!mbx->ops.tx_ready(mbx, FM10K_VFMBX_MSG_MTU))
+               goto out;
+
+       /* interface cannot receive traffic without logical ports */
+       if (mac->dglort_map == FM10K_DGLORTMAP_NONE)
+               goto out;
+
+       /* if we passed all the tests above then the switch is ready and we no
+        * longer need to check for link
+        */
+       mac->get_host_state = false;
+
+out:
+       *host_ready = !mac->get_host_state;
+       return ret_val;
+}