From aaf6dd2e34de5db8b8f40297ef4660c7196e8407 Mon Sep 17 00:00:00 2001 From: Noe Rubinstein Date: Thu, 9 Feb 2012 16:13:24 +0100 Subject: net: e1000: Add support for Intel EP80579's GCU The Global Configuration Unit implements an MDIO interface to support the Ethernet interfaces of Intel EP80579 (Tolapai). Signed-off-by: Noe Rubinstein --- drivers/net/ethernet/intel/e1000/Makefile | 8 +- drivers/net/ethernet/intel/e1000/gcu.h | 102 +++++++ drivers/net/ethernet/intel/e1000/gcu_if.c | 343 +++++++++++++++++++++++ drivers/net/ethernet/intel/e1000/gcu_if.h | 51 ++++ drivers/net/ethernet/intel/e1000/gcu_main.c | 419 ++++++++++++++++++++++++++++ drivers/net/ethernet/intel/e1000/gcu_reg.h | 68 +++++ 6 files changed, 990 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/intel/e1000/gcu.h create mode 100644 drivers/net/ethernet/intel/e1000/gcu_if.c create mode 100644 drivers/net/ethernet/intel/e1000/gcu_if.h create mode 100644 drivers/net/ethernet/intel/e1000/gcu_main.c create mode 100644 drivers/net/ethernet/intel/e1000/gcu_reg.h (limited to 'drivers/net/ethernet/intel/e1000') diff --git a/drivers/net/ethernet/intel/e1000/Makefile b/drivers/net/ethernet/intel/e1000/Makefile index 4a6ab152245..8a0f2c0ad22 100644 --- a/drivers/net/ethernet/intel/e1000/Makefile +++ b/drivers/net/ethernet/intel/e1000/Makefile @@ -30,6 +30,12 @@ # Makefile for the Intel(R) PRO/1000 ethernet driver # -obj-$(CONFIG_E1000) += e1000.o +gcu-objs := gcu_main.o gcu_if.o + +ifeq ($(CONFIG_E1000_EP80579),y) + obj-$(CONFIG_E1000) += gcu.o +endif e1000-objs := e1000_main.o e1000_hw.o e1000_ethtool.o e1000_param.o + +obj-$(CONFIG_E1000) += e1000.o diff --git a/drivers/net/ethernet/intel/e1000/gcu.h b/drivers/net/ethernet/intel/e1000/gcu.h new file mode 100644 index 00000000000..b409a6ee136 --- /dev/null +++ b/drivers/net/ethernet/intel/e1000/gcu.h @@ -0,0 +1,102 @@ +/***************************************************************************** + +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 + +*****************************************************************************/ + +/* Linux GCU Driver main header file */ + +#ifndef _GCU_H_ +#define _GCU_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BAR_0 0 + +#define INTEL_GCU_DEVICE(device_id) {\ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, device_id)} + +#define GCU_DEV_NAME_SIZE 16 + +#ifdef DBG +#define GCU_DBG(args...) printk(KERN_DEBUG "gcu: " args) +#else +#define GCU_DBG(args...) +#endif + +#define GCU_ERR(args...) printk(KERN_ERR "gcu: " args) + +#define PFX "gcu: " +#define DPRINTK(nlevel, klevel, fmt, args...) \ + (void)((NETIF_MSG_##nlevel & adapter->msg_enable) && \ + printk(KERN_##klevel PFX "%s: %s: " fmt, adapter->name, \ + __func__ , ## args)) + +struct gcu_adapter { + struct pci_dev *pdev; + uint32_t mem_start; + uint32_t mem_end; + uint32_t base_addr; + uint8_t *hw_addr; + char name[GCU_DEV_NAME_SIZE]; + uint32_t pci_state[16]; + int32_t msg_enable; + uint16_t device_id; + uint16_t vendor_id; + uint16_t subsystem_id; + uint16_t subsystem_vendor_id; + uint16_t pci_cmd_word; + uint8_t revision_id; + /* open/release and usage marking */ + struct module *owner; + +}; + +/* + * Exported interface functions need access to the modules + * gcu_adapter struct + */ +const struct gcu_adapter *gcu_get_adapter(void); +void gcu_release_adapter(const struct gcu_adapter **adapter); + +#endif /* _GCU_H_ */ 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); diff --git a/drivers/net/ethernet/intel/e1000/gcu_if.h b/drivers/net/ethernet/intel/e1000/gcu_if.h new file mode 100644 index 00000000000..84187f68e0e --- /dev/null +++ b/drivers/net/ethernet/intel/e1000/gcu_if.h @@ -0,0 +1,51 @@ +/***************************************************************************** + +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 + +*****************************************************************************/ + +/* + * gcu_if.h + * Shared functions for accessing and configuring the GCU + */ + +#ifndef GCU_IF_H +#define GCU_IF_H +#define GCU_DEVID 0x503E +#define GCU_MAX_ATTEMPTS 64 + +int32_t gcu_write_eth_phy(uint32_t phy_num, uint32_t reg_addr, + uint16_t phy_data); + +int32_t gcu_read_eth_phy(uint32_t phy_num, uint32_t reg_addr, + uint16_t *phy_data); + +int gcu_e1000_suspend(struct pci_dev *pdev, uint32_t state); +void gcu_e1000_resume(struct pci_dev *pdev); +#endif /* ifndef GCU_IF_H */ diff --git a/drivers/net/ethernet/intel/e1000/gcu_main.c b/drivers/net/ethernet/intel/e1000/gcu_main.c new file mode 100644 index 00000000000..4a4ccb7a4f0 --- /dev/null +++ b/drivers/net/ethernet/intel/e1000/gcu_main.c @@ -0,0 +1,419 @@ +/****************************************************************************** + +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_GENERAL + * + * @file gcu_main.c + * + * @description + * This module contains the upper-edge routines of the driver + * interface that handle initialization, resets, and shutdowns + * of the GCU. + * + **************************************************************************/ + +#include "gcu.h" + +char gcu_driver_name[] = "GCU"; +char gcu_driver_string[] = "Global Configuration Unit Driver"; +#define DRV_VERSION "1.0.0" +char gcu_driver_version[] = DRV_VERSION; +char gcu_copyright[] = "Copyright (c) 1999-2007 Intel Corporation."; + +/* gcu_pci_tbl - PCI Device ID Table + * + * Last entry must be all 0s + * + * Macro expands to... + * {PCI_DEVICE(PCI_VENDOR_ID_INTEL, device_id)} + */ +static DEFINE_PCI_DEVICE_TABLE(gcu_pci_tbl) = { + INTEL_GCU_DEVICE(0x503E), + /* required last entry */ + {0,} +}; + +MODULE_DEVICE_TABLE(pci, gcu_pci_tbl); + +enum gcu_err_type { err_ioremap, err_alloc_gcu_adapter }; + +static int gcu_init_module(void); +static void gcu_exit_module(void); +static int gcu_probe(struct pci_dev *pdev, const struct pci_device_id *ent); +static void gcu_probe_err(enum gcu_err_type err, struct pci_dev *pdev, + struct gcu_adapter *adapter); +static void __devexit gcu_remove(struct pci_dev *pdev); +static int gcu_notify_reboot(struct notifier_block *, unsigned long event, + void *ptr); +static int gcu_suspend(struct pci_dev *pdev, uint32_t state); +static struct gcu_adapter *alloc_gcu_adapter(void); +static void free_gcu_adapter(struct gcu_adapter *adapter); + +struct notifier_block gcu_notifier_reboot = { + .notifier_call = gcu_notify_reboot, + .next = NULL, + .priority = 0 +}; + +static struct pci_driver gcu_driver = { + .name = gcu_driver_name, + .id_table = gcu_pci_tbl, + .probe = gcu_probe, + .remove = __devexit_p(gcu_remove), +}; + +static struct gcu_adapter *global_adapter; +static DEFINE_SPINLOCK(global_adapter_spinlock); +static unsigned long g_intflags; + +MODULE_AUTHOR("Intel(R) Corporation"); +MODULE_DESCRIPTION("Global Configuration Unit Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0.0"); + +/* + * AFU: debug values are pulled originally from netdevice.h + * where in the orig iegbe driver, the enums were used. + * Probably want to come up with our own enum set + */ +static int debug = 0x1UL | 0x2UL; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); + +/** + * gcu_init_module - Driver Registration Routine + * + * gcu_init_module is the first routine called when the driver is + * loaded. All it does is register with the PCI subsystem. + **/ +static int __init gcu_init_module(void) +{ + int ret; + printk(KERN_INFO "%s - version %s\n", + gcu_driver_string, gcu_driver_version); + + printk(KERN_INFO "%s\n", gcu_copyright); + + ret = pci_register_driver(&gcu_driver); + if (ret >= 0) + register_reboot_notifier(&gcu_notifier_reboot); + + return ret; +} + +module_init(gcu_init_module); + +/** + * gcu_exit_module - Driver Exit Cleanup Routine + * + * gcu_exit_module is called just before the driver is removed + * from memory. + **/ +static void __exit gcu_exit_module(void) +{ + GCU_DBG("%s\n", __func__); + + unregister_reboot_notifier(&gcu_notifier_reboot); + pci_unregister_driver(&gcu_driver); +} + +module_exit(gcu_exit_module); + +/** + * gcu_probe - Device Initialization Routine + * @pdev: PCI device information struct + * @ent: entry in gcu_pci_tbl + * + * Returns 0 on success, negative on failure + * + * gcu_probe initializes an adapter identified by a pci_dev structure. + * The OS initialization, configuring of the adapter private structure, + * and a hardware reset occur. + **/ +static int __devinit +gcu_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct gcu_adapter *adapter = 0; + uint32_t mmio_start, mmio_len; + int err; + + GCU_DBG("%s\n", __func__); + + /** If we already have an adapter then we must already have been probed. */ + if (global_adapter != 0) + return 0; + + err = pci_enable_device(pdev); + if (err) { + GCU_DBG("Unable to enable PCI Device\n"); + return err; + } + + err = pci_request_regions(pdev, gcu_driver_name); + if (err) { + GCU_DBG("Unable to acquire requested memory regions\n"); + return err; + } + + /* + * acquire the adapter spinlock. Once the module is loaded, it is + * possible for someone to access the adapter struct via the interface + * functions exported in gcu_if.c + */ + spin_lock(&global_adapter_spinlock); + + adapter = alloc_gcu_adapter(); + if (!adapter) { + gcu_probe_err(err_alloc_gcu_adapter, pdev, adapter); + spin_unlock(&global_adapter_spinlock); + return -ENOMEM; + } + + pci_set_drvdata(pdev, adapter); + + adapter->pdev = pdev; + adapter->msg_enable = (1 << debug) - 1; + + mmio_start = pci_resource_start(pdev, BAR_0); + mmio_len = pci_resource_len(pdev, BAR_0); + + adapter->hw_addr = ioremap(mmio_start, mmio_len); + if (!adapter->hw_addr) { + GCU_DBG("Unable to map mmio\n"); + gcu_probe_err(err_ioremap, pdev, adapter); + spin_unlock(&global_adapter_spinlock); + return -EIO; + } + + strncpy(adapter->name, pci_name(pdev), sizeof(adapter->name) - 1); + adapter->mem_start = mmio_start; + adapter->mem_end = mmio_start + mmio_len; + + adapter->vendor_id = pdev->vendor; + adapter->device_id = pdev->device; + adapter->subsystem_vendor_id = pdev->subsystem_vendor; + adapter->subsystem_id = pdev->subsystem_device; + + pci_read_config_byte(pdev, PCI_REVISION_ID, &adapter->revision_id); + + pci_read_config_word(pdev, PCI_COMMAND, &adapter->pci_cmd_word); + + global_adapter = adapter; + spin_unlock(&global_adapter_spinlock); + + DPRINTK(PROBE, INFO, "Intel(R) GCU Initialized\n"); + + return 0; +} + +/** + * gcu_probe_err - gcu_probe error handler + * @err: gcu_err_type + * + * encapsulated error handling for gcu_probe + **/ +static void +gcu_probe_err(enum gcu_err_type err, struct pci_dev *pdev, + struct gcu_adapter *adapter) +{ + + switch (err) { + case err_ioremap: + iounmap(adapter->hw_addr); + pci_release_regions(pdev); + case err_alloc_gcu_adapter: + default: + free_gcu_adapter(adapter); + break; + } +} + +/** + * gcu_remove - Device Removal Routine + * @pdev: PCI device information struct + * + * gcu_remove is called by the PCI subsystem to alert the driver + * that it should release a PCI device. The could be caused by a + * Hot-Plug event, or because the driver is going to be removed from + * memory. + **/ +static void __devexit gcu_remove(struct pci_dev *pdev) +{ + struct gcu_adapter *adapter = pci_get_drvdata(pdev); + + GCU_DBG("%s\n", __func__); + + iounmap(adapter->hw_addr); + pci_release_regions(pdev); + free_gcu_adapter(adapter); + pci_set_drvdata(pdev, NULL); +} + +static int +gcu_notify_reboot(struct notifier_block *nb, unsigned long event, void *p) +{ + struct pci_dev *pdev = NULL; + + GCU_DBG("%s\n", __func__); + + switch (event) { + case SYS_DOWN: + case SYS_HALT: + case SYS_POWER_OFF: + while ((pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev))) + if (pci_dev_driver(pdev) == &gcu_driver) + gcu_suspend(pdev, 0x3); + } + return NOTIFY_DONE; +} + +/** + * gcu_suspend - device sleep function + * @pdev: PCI device information struct + * + * gcu_supend is generally called to place a device in sleep mode, + * however the GCU doesn't support power mangement. For this case, + * it is part of the gcu_notify_reboot() call chain to quiese the + * device before a reboot. + **/ +static int gcu_suspend(struct pci_dev *pdev, uint32_t state) +{ + /*struct gcu_adapter *adapter = pci_get_drvdata(pdev); */ + GCU_DBG("%s\n", __func__); + + pci_save_state(pdev); + + pci_disable_device(pdev); + + state = (state > 0) ? 0 : 0; + + /* + * GCU doesn't support power management, but want to + * leave a hook incase that situation changes in the future + * + * pci_set_power_state(pdev, state); + * + */ + + return state; +} + +/** + * alloc_gcu_adapter + * + * alloc_gcu_adapter is a wrapper for the kmalloc call for the + * device specific data block plus inits the global_adapter variable. + * + * Note that this function assumes that the spinlock for the global + * gcu_adapter struct as been acquired. + **/ +static struct gcu_adapter *alloc_gcu_adapter() +{ + struct gcu_adapter *adapter; + + GCU_DBG("%s\n", __func__); + + adapter = kmalloc(sizeof(*adapter), GFP_KERNEL); + + global_adapter = adapter; + + if (!adapter) { + GCU_DBG("Unable to allocate space for global gcu_adapter"); + return 0; + } + + memset(adapter, 0, sizeof(*adapter)); + + return adapter; +} + +/** + * free_gcu_adapter + * @adapter: gcu_adapter struct to be free'd + * + * free_gcu_adapter is a wrapper for the kfree call for the + * device specific data block plus clears the global_adapter variable + * + * Note that this function assumes that the spinlock for the global + * gcu_adapter struct as been acquired. + **/ +static void free_gcu_adapter(struct gcu_adapter *adapter) +{ + GCU_DBG("%s\n", __func__); + + global_adapter = 0; + + kfree(adapter); +} + +/** + * gcu_get_adapter + * + * gcu_get_adapter is used by the functions exported in gcu_if.c to get + * access to the memory addresses needed to access the MMIO registers + * of the GCU + **/ +const struct gcu_adapter *gcu_get_adapter(void) +{ + GCU_DBG("%s\n", __func__); + + if (global_adapter == NULL) { + GCU_DBG("global gcu_adapter is not available\n"); + return NULL; + } + + spin_lock_irqsave(&global_adapter_spinlock, g_intflags); + + return global_adapter; +} + +/** + * gcu_release_adapter + * + * gcu_release_adapter is used by the functions exported in gcu_if.c to get + * release the adapter spinlock and the handle to the adapter + **/ +void gcu_release_adapter(const struct gcu_adapter **adapter) +{ + GCU_DBG("%s\n", __func__); + + if (adapter == NULL) + GCU_ERR("global gcu_adapter handle is invalid\n"); + else + *adapter = 0; + + spin_unlock_irqrestore(&global_adapter_spinlock, g_intflags); + + return; +} + +/* gcu_main.c */ diff --git a/drivers/net/ethernet/intel/e1000/gcu_reg.h b/drivers/net/ethernet/intel/e1000/gcu_reg.h new file mode 100644 index 00000000000..cf77303ca5e --- /dev/null +++ b/drivers/net/ethernet/intel/e1000/gcu_reg.h @@ -0,0 +1,68 @@ +/****************************************************************************** + +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 + +******************************************************************************/ + +/* + * gcu_reg.h + * Macros and constants related to the registers available on the GCU + */ + +#ifndef GCU_REG_H +#define GCU_REG_H + +/* Register Offsets within memory map register space */ +#define MDIO_STATUS_REG 0x00000010UL +#define MDIO_COMMAND_REG 0x00000014UL + +/* MDIO_STATUS_REG fields */ +#define MDIO_STATUS_STATUS_MASK 0x80000000UL /* bit 31 = 1 on error */ +#define MDIO_STATUS_READ_DATA_MASK 0x0000FFFFUL + +/* MDIO_COMMAND_REG fields */ +#define MDIO_COMMAND_GO_MASK 0x80000000UL /* bit 31 = 1 during read + * or write, 0 on + * completion */ +#define MDIO_COMMAND_OPER_MASK 0x04000000UL /* bit = 1 is a write */ +#define MDIO_COMMAND_PHY_ADDR_MASK 0x03E00000UL +#define MDIO_COMMAND_PHY_REG_MASK 0x001F0000UL +#define MDIO_COMMAND_WRITE_DATA_MASK 0x0000FFFFUL + +#define MDIO_COMMAND_GO_OFFSET 31 +#define MDIO_COMMAND_OPER_OFFSET 26 +#define MDIO_COMMAND_PHY_ADDR_OFFSET 21 +#define MDIO_COMMAND_PHY_REG_OFFSET 16 +#define MDIO_COMMAND_WRITE_DATA_OFFSET 0 + +#define MDIO_COMMAND_PHY_ADDR_MAX 2 /* total phys supported by GCU */ +#define MDIO_COMMAND_PHY_REG_MAX 31 /* total registers available on the + * M88 Phy used on truxton */ + +#endif /* ifndef GCU_REG_H */ -- cgit v1.2.3