diff options
Diffstat (limited to 'drivers/net/ethernet/intel/e1000/gcu_if.c')
-rw-r--r-- | drivers/net/ethernet/intel/e1000/gcu_if.c | 343 |
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); |