Imported Upstream version 16.04
[deb_dpdk.git] / drivers / net / ixgbe / ixgbe_bypass.c
diff --git a/drivers/net/ixgbe/ixgbe_bypass.c b/drivers/net/ixgbe/ixgbe_bypass.c
new file mode 100644 (file)
index 0000000..73f608b
--- /dev/null
@@ -0,0 +1,414 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
+ *   All rights reserved.
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * 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.
+ *     * Neither the name of 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 <time.h>
+#include <rte_atomic.h>
+#include <rte_ethdev.h>
+#include "ixgbe_ethdev.h"
+#include "ixgbe_bypass_api.h"
+
+#define        BYPASS_STATUS_OFF_MASK  3
+
+/* Macros to check for invlaid function pointers. */
+#define        FUNC_PTR_OR_ERR_RET(func, retval) do {              \
+       if ((func) == NULL) {                               \
+               PMD_DRV_LOG(ERR, "%s:%d function not supported", \
+                           __func__, __LINE__);            \
+               return retval;                            \
+       }                                                   \
+} while (0)
+
+#define        FUNC_PTR_OR_RET(func) do {                          \
+       if ((func) == NULL) {                               \
+               PMD_DRV_LOG(ERR, "%s:%d function not supported", \
+                           __func__, __LINE__);            \
+               return;                                     \
+       }                                                   \
+} while (0)
+
+
+/**
+ *  ixgbe_bypass_set_time - Set bypass FW time epoc.
+ *
+ *  @hw: pointer to hardware structure
+ *
+ *  This function with sync the FW date stamp with that of the
+ *  system clock.
+ **/
+static void
+ixgbe_bypass_set_time(struct ixgbe_adapter *adapter)
+{
+       u32 mask, value;
+       u32 sec;
+       struct ixgbe_hw *hw = &adapter->hw;
+
+       sec = 0;
+
+       /*
+        * Send the FW our current time and turn on time_valid and
+        * timer_reset bits.
+        */
+       mask = BYPASS_CTL1_TIME_M |
+              BYPASS_CTL1_VALID_M |
+              BYPASS_CTL1_OFFTRST_M;
+       value = (sec & BYPASS_CTL1_TIME_M) |
+               BYPASS_CTL1_VALID |
+               BYPASS_CTL1_OFFTRST;
+
+       FUNC_PTR_OR_RET(adapter->bps.ops.bypass_set);
+
+       /* Store FW reset time (in seconds from epoch). */
+       adapter->bps.reset_tm = time(NULL);
+
+       /* reset FW timer. */
+       adapter->bps.ops.bypass_set(hw, BYPASS_PAGE_CTL1, mask, value);
+}
+
+/**
+ * ixgbe_bypass_init - Make some environment changes for bypass
+ *
+ * @adapter: pointer to ixgbe_adapter structure for access to state bits
+ *
+ * This function collects all the modifications needed by the bypass
+ * driver.
+ **/
+void
+ixgbe_bypass_init(struct rte_eth_dev *dev)
+{
+       struct ixgbe_adapter *adapter;
+       struct ixgbe_hw *hw;
+
+       adapter = IXGBE_DEV_TO_ADPATER(dev);
+       hw = &adapter->hw;
+
+       /* Only allow BYPASS ops on the first port */
+       if (hw->device_id != IXGBE_DEV_ID_82599_BYPASS ||
+                       hw->bus.func != 0) {
+               PMD_DRV_LOG(ERR, "bypass function is not supported on that device");
+               return;
+       }
+
+       /* set bypass ops. */
+       adapter->bps.ops.bypass_rw = &ixgbe_bypass_rw_generic;
+       adapter->bps.ops.bypass_valid_rd = &ixgbe_bypass_valid_rd_generic;
+       adapter->bps.ops.bypass_set = &ixgbe_bypass_set_generic;
+       adapter->bps.ops.bypass_rd_eep = &ixgbe_bypass_rd_eep_generic;
+
+       /* set the time for logging. */
+       ixgbe_bypass_set_time(adapter);
+
+       /* Don't have the SDP to the laser */
+       hw->mac.ops.disable_tx_laser = NULL;
+       hw->mac.ops.enable_tx_laser = NULL;
+       hw->mac.ops.flap_tx_laser = NULL;
+}
+
+s32
+ixgbe_bypass_state_show(struct rte_eth_dev *dev, u32 *state)
+{
+       struct ixgbe_hw *hw;
+       s32 ret_val;
+       u32 cmd;
+       u32 by_ctl = 0;
+       struct ixgbe_adapter *adapter = IXGBE_DEV_TO_ADPATER(dev);
+
+       hw = &adapter->hw;
+       FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_rw, -ENOTSUP);
+
+       cmd = BYPASS_PAGE_CTL0;
+       ret_val = adapter->bps.ops.bypass_rw(hw, cmd, &by_ctl);
+
+       /* Assume bypass_rw didn't error out, if it did state will
+        * be ignored anyway.
+        */
+       *state = (by_ctl >> BYPASS_STATUS_OFF_SHIFT) &  BYPASS_STATUS_OFF_MASK;
+
+       return ret_val;
+}
+
+
+s32
+ixgbe_bypass_state_store(struct rte_eth_dev *dev, u32 *new_state)
+{
+       struct ixgbe_adapter *adapter = IXGBE_DEV_TO_ADPATER(dev);
+       struct ixgbe_hw *hw;
+       s32 ret_val;
+
+       hw = &adapter->hw;
+       FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_set, -ENOTSUP);
+
+       /* Set the new state */
+       ret_val = adapter->bps.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
+                                        BYPASS_MODE_OFF_M, *new_state);
+       if (ret_val)
+               goto exit;
+
+       /* Set AUTO back on so FW can receive events */
+       ret_val = adapter->bps.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
+                                        BYPASS_MODE_OFF_M, BYPASS_AUTO);
+
+exit:
+       return ret_val;
+
+}
+
+s32
+ixgbe_bypass_event_show(struct rte_eth_dev *dev, u32 event,
+                           u32 *state)
+{
+       struct ixgbe_hw *hw;
+       s32 ret_val;
+       u32 shift;
+       u32 cmd;
+       u32 by_ctl = 0;
+       struct ixgbe_adapter *adapter = IXGBE_DEV_TO_ADPATER(dev);
+
+       hw = &adapter->hw;
+       FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_rw, -ENOTSUP);
+
+       cmd = BYPASS_PAGE_CTL0;
+       ret_val = adapter->bps.ops.bypass_rw(hw, cmd, &by_ctl);
+
+       /* Assume bypass_rw didn't error out, if it did event will
+        * be ignored anyway.
+        */
+       switch (event) {
+       case BYPASS_EVENT_WDT_TO:
+               shift = BYPASS_WDTIMEOUT_SHIFT;
+               break;
+       case BYPASS_EVENT_MAIN_ON:
+               shift = BYPASS_MAIN_ON_SHIFT;
+               break;
+       case BYPASS_EVENT_MAIN_OFF:
+               shift = BYPASS_MAIN_OFF_SHIFT;
+               break;
+       case BYPASS_EVENT_AUX_ON:
+               shift = BYPASS_AUX_ON_SHIFT;
+               break;
+       case BYPASS_EVENT_AUX_OFF:
+               shift = BYPASS_AUX_OFF_SHIFT;
+               break;
+       default:
+               return EINVAL;
+       }
+
+       *state = (by_ctl >> shift) & 0x3;
+
+       return ret_val;
+}
+
+s32
+ixgbe_bypass_event_store(struct rte_eth_dev *dev, u32 event,
+                            u32 state)
+{
+       struct ixgbe_hw *hw;
+       u32 status;
+       u32 off;
+       s32 ret_val;
+       struct ixgbe_adapter *adapter = IXGBE_DEV_TO_ADPATER(dev);
+
+       hw = &adapter->hw;
+       FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_set, -ENOTSUP);
+
+       switch (event) {
+       case BYPASS_EVENT_WDT_TO:
+               off = BYPASS_WDTIMEOUT_M;
+               status = state << BYPASS_WDTIMEOUT_SHIFT;
+               break;
+       case BYPASS_EVENT_MAIN_ON:
+               off = BYPASS_MAIN_ON_M;
+               status = state << BYPASS_MAIN_ON_SHIFT;
+               break;
+       case BYPASS_EVENT_MAIN_OFF:
+               off = BYPASS_MAIN_OFF_M;
+               status = state << BYPASS_MAIN_OFF_SHIFT;
+               break;
+       case BYPASS_EVENT_AUX_ON:
+               off = BYPASS_AUX_ON_M;
+               status = state << BYPASS_AUX_ON_SHIFT;
+               break;
+       case BYPASS_EVENT_AUX_OFF:
+               off = BYPASS_AUX_OFF_M;
+               status = state << BYPASS_AUX_OFF_SHIFT;
+               break;
+       default:
+               return EINVAL;
+       }
+
+       ret_val = adapter->bps.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
+               off, status);
+
+       return ret_val;
+}
+
+s32
+ixgbe_bypass_wd_timeout_store(struct rte_eth_dev *dev, u32 timeout)
+{
+       struct ixgbe_hw *hw;
+        u32 status;
+        u32 mask;
+       s32 ret_val;
+       struct ixgbe_adapter *adapter = IXGBE_DEV_TO_ADPATER(dev);
+
+       hw = &adapter->hw;
+       FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_set, -ENOTSUP);
+
+       /* disable the timer with timeout of zero */
+       if (timeout == RTE_BYPASS_TMT_OFF) {
+               status = 0x0;   /* WDG enable off */
+               mask = BYPASS_WDT_ENABLE_M;
+       } else {
+               /* set time out value */
+               mask = BYPASS_WDT_VALUE_M;
+
+               /* enable the timer */
+               status = timeout << BYPASS_WDT_TIME_SHIFT;
+               status |= 0x1 << BYPASS_WDT_ENABLE_SHIFT;
+               mask |= BYPASS_WDT_ENABLE_M;
+       }
+
+       ret_val = adapter->bps.ops.bypass_set(hw, BYPASS_PAGE_CTL0,
+               mask, status);
+
+       return ret_val;
+}
+
+s32
+ixgbe_bypass_ver_show(struct rte_eth_dev *dev, u32 *ver)
+{
+       struct ixgbe_hw *hw;
+       u32 cmd;
+       u32 status;
+       s32 ret_val;
+       struct ixgbe_adapter *adapter = IXGBE_DEV_TO_ADPATER(dev);
+
+       hw = &adapter->hw;
+       FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_rw, -ENOTSUP);
+
+       cmd = BYPASS_PAGE_CTL2 | BYPASS_WE;
+       cmd |= (BYPASS_EEPROM_VER_ADD << BYPASS_CTL2_OFFSET_SHIFT) &
+              BYPASS_CTL2_OFFSET_M;
+       ret_val = adapter->bps.ops.bypass_rw(hw, cmd, &status);
+       if (ret_val)
+               goto exit;
+
+       /* wait for the write to stick */
+       msleep(100);
+
+       /* Now read the results */
+       cmd &= ~BYPASS_WE;
+       ret_val = adapter->bps.ops.bypass_rw(hw, cmd, &status);
+       if (ret_val)
+               goto exit;
+
+       *ver = status & BYPASS_CTL2_DATA_M;      /* only one byte of date */
+
+exit:
+       return ret_val;
+}
+
+s32
+ixgbe_bypass_wd_timeout_show(struct rte_eth_dev *dev, u32 *wd_timeout)
+{
+       struct ixgbe_hw *hw;
+       u32 by_ctl = 0;
+       u32 cmd;
+       u32 wdg;
+       s32 ret_val;
+       struct ixgbe_adapter *adapter = IXGBE_DEV_TO_ADPATER(dev);
+
+       hw = &adapter->hw;
+       FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_rw, -ENOTSUP);
+
+       cmd = BYPASS_PAGE_CTL0;
+       ret_val = adapter->bps.ops.bypass_rw(hw, cmd, &by_ctl);
+
+       wdg = by_ctl & BYPASS_WDT_ENABLE_M;
+       if (!wdg)
+               *wd_timeout = RTE_BYPASS_TMT_OFF;
+       else
+               *wd_timeout = (by_ctl >> BYPASS_WDT_TIME_SHIFT) &
+                       BYPASS_WDT_MASK;
+
+       return ret_val;
+}
+
+s32
+ixgbe_bypass_wd_reset(struct rte_eth_dev *dev)
+{
+       u32 cmd;
+       u32 status;
+       u32 sec;
+       u32 count = 0;
+       s32 ret_val;
+       struct ixgbe_hw *hw;
+       struct ixgbe_adapter *adapter = IXGBE_DEV_TO_ADPATER(dev);
+
+       hw = &adapter->hw;
+
+       FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_rw, -ENOTSUP);
+       FUNC_PTR_OR_ERR_RET(adapter->bps.ops.bypass_valid_rd, -ENOTSUP);
+
+       /* Use the lower level bit-bang functions since we don't need
+        * to read the register first to get it's current state as we
+        * are setting every thing in this write.
+        */
+       /* Set up WD pet */
+       cmd = BYPASS_PAGE_CTL1 | BYPASS_WE | BYPASS_CTL1_WDT_PET;
+
+       /* Resync the FW time while writing to CTL1 anyway */
+       adapter->bps.reset_tm = time(NULL);
+       sec = 0;
+
+       cmd |= (sec & BYPASS_CTL1_TIME_M) | BYPASS_CTL1_VALID;
+
+       /* reset FW timer offset since we are resetting the clock */
+       cmd |= BYPASS_CTL1_OFFTRST;
+
+       ret_val = adapter->bps.ops.bypass_rw(hw, cmd, &status);
+
+       /* Read until it matches what we wrote, or we time out */
+       do {
+               if (count++ > 10) {
+                       ret_val = IXGBE_BYPASS_FW_WRITE_FAILURE;
+                       break;
+               }
+
+               if (adapter->bps.ops.bypass_rw(hw, BYPASS_PAGE_CTL1, &status)) {
+                       ret_val = IXGBE_ERR_INVALID_ARGUMENT;
+                       break;
+               }
+       } while (!adapter->bps.ops.bypass_valid_rd(cmd, status));
+
+       return ret_val;
+}