summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/intel/e1000/gcu_if.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/intel/e1000/gcu_if.c')
-rw-r--r--drivers/net/ethernet/intel/e1000/gcu_if.c343
1 files changed, 343 insertions, 0 deletions
diff --git a/drivers/net/ethernet/intel/e1000/gcu_if.c b/drivers/net/ethernet/intel/e1000/gcu_if.c
new file mode 100644
index 00000000000..3b10dfa098b
--- /dev/null
+++ b/drivers/net/ethernet/intel/e1000/gcu_if.c
@@ -0,0 +1,343 @@
+/*****************************************************************************
+
+GPL LICENSE SUMMARY
+
+ Copyright(c) 2007,2008,2009 Intel Corporation. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ The full GNU General Public License is included in this distribution
+ in the file called LICENSE.GPL.
+
+ Contact Information:
+ Intel Corporation
+
+ version: Embedded.Release.Patch.L.1.0.7-5
+
+ Contact Information:
+
+ Intel Corporation, 5000 W Chandler Blvd, Chandler, AZ 85226
+
+*****************************************************************************/
+
+/**************************************************************************
+ * @ingroup GCU_INTERFACE
+ *
+ * @file gcu_if.c
+ *
+ * @description
+ * This module contains shared functions for accessing and configuring
+ * the GCU.
+ *
+ **************************************************************************/
+
+#include "gcu.h"
+#include "gcu_reg.h"
+#include "gcu_if.h"
+
+/* forward declaration for write verify used in gcu_write_eth_phy */
+static int32_t gcu_write_verify(uint32_t phy_num,
+ uint32_t reg_addr,
+ uint16_t written_data,
+ const struct gcu_adapter *adapter);
+
+/**
+ * gcu_write_eth_phy
+ * @phy_num: phy we want to write to, either 0, 1, or 2
+ * @reg_addr: address in PHY's register space to write to
+ * @phy_data: data to be written
+ *
+ * interface function for other modules to access the GCU
+ **/
+int32_t
+gcu_write_eth_phy(uint32_t phy_num, uint32_t reg_addr, uint16_t phy_data)
+{
+ const struct gcu_adapter *adapter;
+ uint32_t data = 0;
+ uint32_t timeoutCounter = 0;
+ const uint32_t timeoutCounterMax = GCU_MAX_ATTEMPTS;
+ uint32_t pending;
+
+ GCU_DBG("%s\n", __func__);
+
+ if (phy_num > MDIO_COMMAND_PHY_ADDR_MAX) {
+ GCU_ERR("phy_num = %d, which is greater than "
+ "MDIO_COMMAND_PHY_ADDR_MAX\n", phy_num);
+
+ return -1;
+ }
+
+ if (reg_addr > MDIO_COMMAND_PHY_REG_MAX) {
+ GCU_ERR("reg_addr = %d, which is greater than "
+ "MDIO_COMMAND_PHY_REG_MAX\n", phy_num);
+
+ return -1;
+ }
+
+ /* format the data to be written to the MDIO_COMMAND_REG */
+ data = phy_data;
+ data |= (reg_addr << MDIO_COMMAND_PHY_REG_OFFSET);
+ data |= (phy_num << MDIO_COMMAND_PHY_ADDR_OFFSET);
+ data |= MDIO_COMMAND_OPER_MASK | MDIO_COMMAND_GO_MASK;
+
+ /*
+ * get_gcu_adapter contains a spinlock, this may pause for a bit
+ */
+ adapter = gcu_get_adapter();
+ if (!adapter) {
+ GCU_ERR("gcu_adapter not available, cannot access MMIO\n");
+ return -1;
+ }
+
+ /*
+ * We write to MDIO_COMMAND_REG initially, then read that
+ * same register until its MDIO_GO bit is cleared. When cleared,
+ * the transaction is complete
+ */
+ iowrite32(data, adapter->hw_addr + MDIO_COMMAND_REG);
+ do {
+ timeoutCounter++;
+ udelay(0x32); /* 50 microsecond delay */
+ data = ioread32(adapter->hw_addr + MDIO_COMMAND_REG);
+ pending =
+ (data & MDIO_COMMAND_GO_MASK) >> MDIO_COMMAND_GO_OFFSET;
+ } while (pending && timeoutCounter < timeoutCounterMax);
+
+ if (timeoutCounter == timeoutCounterMax && pending) {
+ GCU_ERR("Reached maximum number of retries"
+ " accessing MDIO_COMMAND_REG\n");
+
+ gcu_release_adapter(&adapter);
+
+ return -1;
+ }
+
+ /* validate the write during debug */
+#ifdef DBG2
+ if (!gcu_write_verify(phy_num, reg_addr, phy_data, adapter)) {
+ GCU_ERR("Write verification failed for PHY=%d and addr=%d\n",
+ phy_num, reg_addr);
+
+ gcu_release_adapter(&adapter);
+
+ return -1;
+ }
+#endif
+
+ gcu_release_adapter(&adapter);
+
+ return 0;
+}
+EXPORT_SYMBOL(gcu_write_eth_phy);
+
+/**
+ * gcu_read_eth_phy
+ * @phy_num: phy we want to write to, either 0, 1, or 2
+ * @reg_addr: address in PHY's register space to write to
+ * @phy_data: data to be written
+ *
+ * interface function for other modules to access the GCU
+ **/
+int32_t
+gcu_read_eth_phy(uint32_t phy_num, uint32_t reg_addr, uint16_t *phy_data)
+{
+ const struct gcu_adapter *adapter;
+ uint32_t data = 0;
+ uint32_t timeoutCounter = 0;
+ const uint32_t timeoutCounterMax = GCU_MAX_ATTEMPTS;
+ uint32_t pending = 0;
+
+ GCU_DBG("%s\n", __func__);
+
+ if (phy_num > MDIO_COMMAND_PHY_ADDR_MAX) {
+ GCU_ERR("phy_num = %d, which is greater than "
+ "MDIO_COMMAND_PHY_ADDR_MAX\n", phy_num);
+
+ return -1;
+ }
+
+ if (reg_addr > MDIO_COMMAND_PHY_REG_MAX) {
+ GCU_ERR("reg_addr = %d, which is greater than "
+ "MDIO_COMMAND_PHY_REG_MAX\n", phy_num);
+
+ return -1;
+ }
+
+ /* format the data to be written to MDIO_COMMAND_REG */
+ data |= (reg_addr << MDIO_COMMAND_PHY_REG_OFFSET);
+ data |= (phy_num << MDIO_COMMAND_PHY_ADDR_OFFSET);
+ data |= MDIO_COMMAND_GO_MASK;
+
+ /*
+ * this call contains a spinlock, so this may pause for a bit
+ */
+ adapter = gcu_get_adapter();
+ if (!adapter) {
+ GCU_ERR("gcu_adapter not available, cannot access MMIO\n");
+ return -1;
+ }
+
+ /*
+ * We write to MDIO_COMMAND_REG initially, then read that
+ * same register until its MDIO_GO bit is cleared. When cleared,
+ * the transaction is complete
+ */
+ iowrite32(data, adapter->hw_addr + MDIO_COMMAND_REG);
+ do {
+ timeoutCounter++;
+ udelay(0x32); /* 50 microsecond delay */
+ data = ioread32(adapter->hw_addr + MDIO_COMMAND_REG);
+ pending =
+ (data & MDIO_COMMAND_GO_MASK) >> MDIO_COMMAND_GO_OFFSET;
+ } while (pending && timeoutCounter < timeoutCounterMax);
+
+ if (timeoutCounter == timeoutCounterMax && pending) {
+ GCU_ERR("Reached maximum number of retries"
+ " accessing MDIO_COMMAND_REG\n");
+
+ gcu_release_adapter(&adapter);
+
+ return -1;
+ }
+
+ /* we retrieve the data from the MDIO_STATUS_REGISTER */
+ data = ioread32(adapter->hw_addr + MDIO_STATUS_REG);
+ if ((data & MDIO_STATUS_STATUS_MASK) != 0) {
+ GCU_ERR("Unable to retrieve data from MDIO_STATUS_REG\n");
+
+ gcu_release_adapter(&adapter);
+
+ return -1;
+ }
+
+ *phy_data = (uint16_t) (data & MDIO_STATUS_READ_DATA_MASK);
+
+ gcu_release_adapter(&adapter);
+
+ return 0;
+}
+EXPORT_SYMBOL(gcu_read_eth_phy);
+
+/**
+ * gcu_write_verify
+ * @phy_num: phy we want to write to, either 0, 1, or 2
+ * @reg_addr: address in PHY's register space to write to
+ * @phy_data: data to be checked
+ * @adapter: pointer to global adapter struct
+ *
+ * This f(n) assumes that the spinlock acquired for adapter is
+ * still in force.
+ **/
+int32_t
+gcu_write_verify(uint32_t phy_num, uint32_t reg_addr, uint16_t written_data,
+ const struct gcu_adapter *adapter)
+{
+ uint32_t data = 0;
+ uint32_t timeoutCounter = 0;
+ const uint32_t timeoutCounterMax = GCU_MAX_ATTEMPTS;
+ uint32_t pending = 0;
+
+ GCU_DBG("%s\n", __func__);
+
+ if (!adapter) {
+ GCU_ERR("Invalid adapter pointer\n");
+ return 0;
+ }
+
+ if (phy_num > MDIO_COMMAND_PHY_ADDR_MAX) {
+ GCU_ERR("phy_num = %d, which is greater than "
+ "MDIO_COMMAND_PHY_ADDR_MAX\n", phy_num);
+
+ return 0;
+ }
+
+ if (reg_addr > MDIO_COMMAND_PHY_REG_MAX) {
+ GCU_ERR("reg_addr = %d, which is greater than "
+ "MDIO_COMMAND_PHY_REG_MAX\n", phy_num);
+
+ return 0;
+ }
+
+ /* format the data to be written to MDIO_COMMAND_REG */
+ data |= (reg_addr << MDIO_COMMAND_PHY_REG_OFFSET);
+ data |= (phy_num << MDIO_COMMAND_PHY_ADDR_OFFSET);
+ data |= MDIO_COMMAND_GO_MASK;
+
+ /*
+ * We write to MDIO_COMMAND_REG initially, then read that
+ * same register until its MDIO_GO bit is cleared. When cleared,
+ * the transaction is complete
+ */
+ iowrite32(data, adapter->hw_addr + MDIO_COMMAND_REG);
+ do {
+ timeoutCounter++;
+ udelay(0x32); /* 50 microsecond delay */
+ data = ioread32(adapter->hw_addr + MDIO_COMMAND_REG);
+ pending =
+ (data & MDIO_COMMAND_GO_MASK) >> MDIO_COMMAND_GO_OFFSET;
+ } while (pending && timeoutCounter < timeoutCounterMax);
+
+ if (timeoutCounter == timeoutCounterMax && pending) {
+ GCU_ERR("Reached maximum number of retries"
+ " accessing MDIO_COMMAND_REG\n");
+
+ return 0;
+ }
+
+ /* we retrieve the data from the MDIO_STATUS_REGISTER */
+ data = ioread32(adapter->hw_addr + MDIO_STATUS_REG);
+ if ((data & MDIO_STATUS_STATUS_MASK) != 0) {
+ GCU_ERR("Unable to retrieve data from MDIO_STATUS_REG\n");
+
+ return 0;
+ }
+
+ return written_data == (uint16_t) (data & MDIO_STATUS_READ_DATA_MASK);
+}
+
+/*
+ * gcu_e1000_resume
+ * @pdev: gcu pci_dev
+ * purpose - exported PM resume function used by iegbe
+ * driver to enable the GCU device.
+ */
+void gcu_e1000_resume(struct pci_dev *pdev)
+{
+ GCU_DBG("%s\n", __func__);
+
+ pci_restore_state(pdev);
+ if (!pci_enable_device(pdev))
+ GCU_DBG("pci_enable_device failed!\n");
+
+ return;
+}
+EXPORT_SYMBOL(gcu_e1000_resume);
+
+/*
+ * gcu_e1000_suspend
+ * @pdev: gcu pci_dev
+ * @state: PM state
+ * purpose - exported PM suspend function used by iegbe
+ * driver to disable the GCU device.
+ */
+int gcu_e1000_suspend(struct pci_dev *pdev, uint32_t state)
+{
+ GCU_DBG("%s\n", __func__);
+
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ state = (state > 0) ? 0 : 0;
+
+ return state;
+}
+EXPORT_SYMBOL(gcu_e1000_suspend);