diff options
author | Ian Stadler <istadler@sutus.com> | 2011-10-04 15:48:30 -0700 |
---|---|---|
committer | Noe Rubinstein <nrubinstein@avencall.com> | 2011-12-23 14:51:01 +0100 |
commit | a3e9ebba269b63037abf614337fc17c152fcb976 (patch) | |
tree | 829a6af1d7f6548a6000fc06b4015c4e16dafda7 /gcu_if.c | |
parent | 53bb00e2440db9d03318e4aa3c8b1b9d8b992da4 (diff) |
net: e1000: Added support for Intel EP80579 "Tolapai" GCU and some OEM phys
The Global Configuration Unit (GCU) manages the 3 gigabit MACs that
are baked onto the Intel EP80579 SoC. The GCU is registered as a
device on the PCI bus and the driver handles the init, probe, and
removal of the device as well as entering into ACPI power sleep
states.
Any number of PHYs can be attached to the MACs in order to connect
to a network. Support for the following PHYs is addded via the
e1000_oem_phy portion of the addition:
Intel M88E1000
Intel M88E1011
Intel IGP01E1000
Intel M88E1141
Vitesse VSC8211
Vitesse VSC8601
Diffstat (limited to 'gcu_if.c')
-rwxr-xr-x | gcu_if.c | 371 |
1 files changed, 371 insertions, 0 deletions
diff --git a/gcu_if.c b/gcu_if.c new file mode 100755 index 0000000..33b573f --- /dev/null +++ b/gcu_if.c @@ -0,0 +1,371 @@ +/***************************************************************************** + +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 */ +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) +{ +#if ( ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,6) ) && \ + ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) ) ) + struct net_device *netdev = pci_get_drvdata(pdev); + struct gcu_adapter *adapter = netdev_priv(netdev); +#endif + + 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) +{ +#if ( ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,6) ) && \ + ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) ) ) + struct net_device *netdev = pci_get_drvdata(pdev); + struct gcu_adapter *adapter = netdev_priv(netdev); +#endif + + 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); |