diff options
author | Noe Rubinstein <nrubinstein@avencall.com> | 2012-02-09 16:13:24 +0100 |
---|---|---|
committer | Noe Rubinstein <nrubinstein@avencall.com> | 2012-02-09 18:57:31 +0100 |
commit | aaf6dd2e34de5db8b8f40297ef4660c7196e8407 (patch) | |
tree | 9049eb75460bc682dcba6c55a01d40539838f55d /drivers/net/ethernet/intel/e1000/gcu_main.c | |
parent | 3c39876c0ac6d846ec1a55938b47a0628fe502d1 (diff) |
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 <nrubinstein@avencall.com>
Diffstat (limited to 'drivers/net/ethernet/intel/e1000/gcu_main.c')
-rw-r--r-- | drivers/net/ethernet/intel/e1000/gcu_main.c | 419 |
1 files changed, 419 insertions, 0 deletions
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 */ |