2006-10-22 03:33:03 +08:00
|
|
|
/*
|
2009-02-25 23:57:56 +08:00
|
|
|
* Copyright (C) 2003 - 2009 NetXen, Inc.
|
2006-10-22 03:33:03 +08:00
|
|
|
* All rights reserved.
|
2006-12-05 01:18:00 +08:00
|
|
|
*
|
2006-10-22 03:33:03 +08:00
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
2006-12-05 01:18:00 +08:00
|
|
|
*
|
2006-10-22 03:33:03 +08:00
|
|
|
* 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.
|
2006-12-05 01:18:00 +08:00
|
|
|
*
|
2006-10-22 03:33:03 +08:00
|
|
|
* 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., 59 Temple Place - Suite 330, Boston,
|
|
|
|
* MA 02111-1307, USA.
|
2006-12-05 01:18:00 +08:00
|
|
|
*
|
2006-10-22 03:33:03 +08:00
|
|
|
* The full GNU General Public License is included in this distribution
|
|
|
|
* in the file called LICENSE.
|
2006-12-05 01:18:00 +08:00
|
|
|
*
|
2006-10-22 03:33:03 +08:00
|
|
|
* Contact Information:
|
|
|
|
* info@netxen.com
|
2009-02-25 23:57:56 +08:00
|
|
|
* NetXen Inc,
|
|
|
|
* 18922 Forge Drive
|
|
|
|
* Cupertino, CA 95014-0701
|
2006-10-22 03:33:03 +08:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2006-11-30 01:00:10 +08:00
|
|
|
#include <linux/vmalloc.h>
|
2006-12-05 01:23:25 +08:00
|
|
|
#include <linux/highmem.h>
|
2006-10-22 03:33:03 +08:00
|
|
|
#include "netxen_nic_hw.h"
|
|
|
|
|
|
|
|
#include "netxen_nic.h"
|
|
|
|
#include "netxen_nic_phan_reg.h"
|
|
|
|
|
2006-11-07 18:12:16 +08:00
|
|
|
#include <linux/dma-mapping.h>
|
2009-01-15 12:48:11 +08:00
|
|
|
#include <linux/if_vlan.h>
|
2007-03-13 07:09:15 +08:00
|
|
|
#include <net/ip.h>
|
2009-01-17 03:03:25 +08:00
|
|
|
#include <linux/ipv6.h>
|
2006-11-07 18:12:16 +08:00
|
|
|
|
2006-10-22 03:33:03 +08:00
|
|
|
MODULE_DESCRIPTION("NetXen Multi port (1/10) Gigabit Network Driver");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_VERSION(NETXEN_NIC_LINUX_VERSIONID);
|
|
|
|
|
2007-07-19 17:11:09 +08:00
|
|
|
char netxen_nic_driver_name[] = "netxen_nic";
|
2006-10-22 03:33:03 +08:00
|
|
|
static char netxen_nic_driver_string[] = "NetXen Network Driver version "
|
2006-11-30 01:00:10 +08:00
|
|
|
NETXEN_NIC_LINUX_VERSIONID;
|
2006-10-22 03:33:03 +08:00
|
|
|
|
2008-07-22 10:44:04 +08:00
|
|
|
static int port_mode = NETXEN_PORT_MODE_AUTO_NEG;
|
|
|
|
|
|
|
|
/* Default to restricted 1G auto-neg mode */
|
|
|
|
static int wol_port_mode = 5;
|
|
|
|
|
|
|
|
static int use_msi = 1;
|
|
|
|
|
|
|
|
static int use_msi_x = 1;
|
|
|
|
|
2006-10-22 03:33:03 +08:00
|
|
|
/* Local functions to NetXen NIC driver */
|
|
|
|
static int __devinit netxen_nic_probe(struct pci_dev *pdev,
|
2008-07-22 10:44:04 +08:00
|
|
|
const struct pci_device_id *ent);
|
2006-10-22 03:33:03 +08:00
|
|
|
static void __devexit netxen_nic_remove(struct pci_dev *pdev);
|
|
|
|
static int netxen_nic_open(struct net_device *netdev);
|
|
|
|
static int netxen_nic_close(struct net_device *netdev);
|
|
|
|
static int netxen_nic_xmit_frame(struct sk_buff *, struct net_device *);
|
|
|
|
static void netxen_tx_timeout(struct net_device *netdev);
|
2006-12-06 03:36:26 +08:00
|
|
|
static void netxen_tx_timeout_task(struct work_struct *work);
|
2006-10-22 03:33:03 +08:00
|
|
|
static void netxen_watchdog(unsigned long);
|
[NET]: Make NAPI polling independent of struct net_device objects.
Several devices have multiple independant RX queues per net
device, and some have a single interrupt doorbell for several
queues.
In either case, it's easier to support layouts like that if the
structure representing the poll is independant from the net
device itself.
The signature of the ->poll() call back goes from:
int foo_poll(struct net_device *dev, int *budget)
to
int foo_poll(struct napi_struct *napi, int budget)
The caller is returned the number of RX packets processed (or
the number of "NAPI credits" consumed if you want to get
abstract). The callee no longer messes around bumping
dev->quota, *budget, etc. because that is all handled in the
caller upon return.
The napi_struct is to be embedded in the device driver private data
structures.
Furthermore, it is the driver's responsibility to disable all NAPI
instances in it's ->stop() device close handler. Since the
napi_struct is privatized into the driver's private data structures,
only the driver knows how to get at all of the napi_struct instances
it may have per-device.
With lots of help and suggestions from Rusty Russell, Roland Dreier,
Michael Chan, Jeff Garzik, and Jamal Hadi Salim.
Bug fixes from Thomas Graf, Roland Dreier, Peter Zijlstra,
Joseph Fannin, Scott Wood, Hans J. Koch, and Michael Chan.
[ Ported to current tree and all drivers converted. Integrated
Stephen's follow-on kerneldoc additions, and restored poll_list
handling to the old style to fix mutual exclusion issues. -DaveM ]
Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-10-04 07:41:36 +08:00
|
|
|
static int netxen_nic_poll(struct napi_struct *napi, int budget);
|
2006-10-22 03:33:03 +08:00
|
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
|
|
static void netxen_nic_poll_controller(struct net_device *netdev);
|
|
|
|
#endif
|
2006-11-07 18:12:16 +08:00
|
|
|
static irqreturn_t netxen_intr(int irq, void *data);
|
2008-03-18 10:59:49 +08:00
|
|
|
static irqreturn_t netxen_msi_intr(int irq, void *data);
|
2009-02-09 11:20:19 +08:00
|
|
|
static irqreturn_t netxen_msix_intr(int irq, void *data);
|
2006-10-22 03:33:03 +08:00
|
|
|
|
|
|
|
/* PCI Device ID Table */
|
2008-06-23 09:04:50 +08:00
|
|
|
#define ENTRY(device) \
|
2008-09-12 21:55:14 +08:00
|
|
|
{PCI_DEVICE(PCI_VENDOR_ID_NETXEN, (device)), \
|
2008-06-23 09:04:50 +08:00
|
|
|
.class = PCI_CLASS_NETWORK_ETHERNET << 8, .class_mask = ~0}
|
|
|
|
|
2006-10-22 03:33:03 +08:00
|
|
|
static struct pci_device_id netxen_pci_tbl[] __devinitdata = {
|
2008-09-12 21:55:14 +08:00
|
|
|
ENTRY(PCI_DEVICE_ID_NX2031_10GXSR),
|
|
|
|
ENTRY(PCI_DEVICE_ID_NX2031_10GCX4),
|
|
|
|
ENTRY(PCI_DEVICE_ID_NX2031_4GCU),
|
|
|
|
ENTRY(PCI_DEVICE_ID_NX2031_IMEZ),
|
|
|
|
ENTRY(PCI_DEVICE_ID_NX2031_HMEZ),
|
|
|
|
ENTRY(PCI_DEVICE_ID_NX2031_XG_MGMT),
|
|
|
|
ENTRY(PCI_DEVICE_ID_NX2031_XG_MGMT2),
|
|
|
|
ENTRY(PCI_DEVICE_ID_NX3031),
|
2006-10-22 03:33:03 +08:00
|
|
|
{0,}
|
|
|
|
};
|
|
|
|
|
|
|
|
MODULE_DEVICE_TABLE(pci, netxen_pci_tbl);
|
|
|
|
|
2008-03-31 07:22:18 +08:00
|
|
|
static struct workqueue_struct *netxen_workq;
|
|
|
|
#define SCHEDULE_WORK(tp) queue_work(netxen_workq, tp)
|
|
|
|
#define FLUSH_SCHEDULED_WORK() flush_workqueue(netxen_workq)
|
|
|
|
|
2006-12-05 01:23:25 +08:00
|
|
|
static void netxen_watchdog(unsigned long);
|
|
|
|
|
2008-07-22 10:44:00 +08:00
|
|
|
static uint32_t crb_cmd_producer[4] = {
|
|
|
|
CRB_CMD_PRODUCER_OFFSET, CRB_CMD_PRODUCER_OFFSET_1,
|
|
|
|
CRB_CMD_PRODUCER_OFFSET_2, CRB_CMD_PRODUCER_OFFSET_3
|
|
|
|
};
|
|
|
|
|
2008-07-22 10:44:07 +08:00
|
|
|
void
|
2008-07-22 10:44:03 +08:00
|
|
|
netxen_nic_update_cmd_producer(struct netxen_adapter *adapter,
|
|
|
|
uint32_t crb_producer)
|
2007-04-20 22:52:37 +08:00
|
|
|
{
|
2008-07-22 10:44:03 +08:00
|
|
|
adapter->pci_write_normalize(adapter,
|
|
|
|
adapter->crb_addr_cmd_producer, crb_producer);
|
2007-04-20 22:52:37 +08:00
|
|
|
}
|
|
|
|
|
2008-07-22 10:44:00 +08:00
|
|
|
static uint32_t crb_cmd_consumer[4] = {
|
|
|
|
CRB_CMD_CONSUMER_OFFSET, CRB_CMD_CONSUMER_OFFSET_1,
|
|
|
|
CRB_CMD_CONSUMER_OFFSET_2, CRB_CMD_CONSUMER_OFFSET_3
|
|
|
|
};
|
|
|
|
|
2008-07-22 10:44:03 +08:00
|
|
|
static inline void
|
|
|
|
netxen_nic_update_cmd_consumer(struct netxen_adapter *adapter,
|
|
|
|
u32 crb_consumer)
|
2007-04-20 22:52:37 +08:00
|
|
|
{
|
2008-07-22 10:44:03 +08:00
|
|
|
adapter->pci_write_normalize(adapter,
|
|
|
|
adapter->crb_addr_cmd_consumer, crb_consumer);
|
2007-04-20 22:52:37 +08:00
|
|
|
}
|
|
|
|
|
2008-07-22 10:44:04 +08:00
|
|
|
static uint32_t msi_tgt_status[8] = {
|
2008-03-18 10:59:48 +08:00
|
|
|
ISR_INT_TARGET_STATUS, ISR_INT_TARGET_STATUS_F1,
|
2008-07-22 10:44:04 +08:00
|
|
|
ISR_INT_TARGET_STATUS_F2, ISR_INT_TARGET_STATUS_F3,
|
|
|
|
ISR_INT_TARGET_STATUS_F4, ISR_INT_TARGET_STATUS_F5,
|
|
|
|
ISR_INT_TARGET_STATUS_F6, ISR_INT_TARGET_STATUS_F7
|
2008-03-18 10:59:48 +08:00
|
|
|
};
|
|
|
|
|
2008-07-22 10:44:04 +08:00
|
|
|
static struct netxen_legacy_intr_set legacy_intr[] = NX_LEGACY_INTR_CONFIG;
|
|
|
|
|
2008-08-08 15:08:43 +08:00
|
|
|
static inline void netxen_nic_disable_int(struct netxen_adapter *adapter)
|
2007-07-06 07:13:55 +08:00
|
|
|
{
|
2008-08-08 15:08:43 +08:00
|
|
|
adapter->pci_write_normalize(adapter, adapter->crb_intr_mask, 0);
|
2007-07-06 07:13:55 +08:00
|
|
|
}
|
|
|
|
|
2008-08-08 15:08:43 +08:00
|
|
|
static inline void netxen_nic_enable_int(struct netxen_adapter *adapter)
|
2007-07-06 07:13:55 +08:00
|
|
|
{
|
2008-07-22 10:44:06 +08:00
|
|
|
adapter->pci_write_normalize(adapter, adapter->crb_intr_mask, 0x1);
|
2007-07-06 07:13:55 +08:00
|
|
|
|
2008-08-08 15:08:43 +08:00
|
|
|
if (!NETXEN_IS_MSI_FAMILY(adapter))
|
|
|
|
adapter->pci_write_immediate(adapter,
|
|
|
|
adapter->legacy_intr.tgt_mask_reg, 0xfbff);
|
2007-07-06 07:13:55 +08:00
|
|
|
}
|
|
|
|
|
2008-07-22 10:44:04 +08:00
|
|
|
static int nx_set_dma_mask(struct netxen_adapter *adapter, uint8_t revision_id)
|
|
|
|
{
|
|
|
|
struct pci_dev *pdev = adapter->pdev;
|
2009-03-03 00:02:16 +08:00
|
|
|
uint64_t mask, cmask;
|
|
|
|
|
|
|
|
adapter->pci_using_dac = 0;
|
2008-07-22 10:44:04 +08:00
|
|
|
|
2009-03-03 00:02:16 +08:00
|
|
|
mask = DMA_32BIT_MASK;
|
2008-07-22 10:44:04 +08:00
|
|
|
/*
|
|
|
|
* Consistent DMA mask is set to 32 bit because it cannot be set to
|
|
|
|
* 35 bits. For P3 also leave it at 32 bits for now. Only the rings
|
|
|
|
* come off this pool.
|
|
|
|
*/
|
2009-03-03 00:02:16 +08:00
|
|
|
cmask = DMA_32BIT_MASK;
|
|
|
|
|
|
|
|
#ifndef CONFIG_IA64
|
|
|
|
if (revision_id >= NX_P3_B0)
|
|
|
|
mask = DMA_39BIT_MASK;
|
|
|
|
else if (revision_id == NX_P2_C1)
|
|
|
|
mask = DMA_35BIT_MASK;
|
|
|
|
#endif
|
2008-07-22 10:44:04 +08:00
|
|
|
if (pci_set_dma_mask(pdev, mask) == 0 &&
|
2009-03-03 00:02:16 +08:00
|
|
|
pci_set_consistent_dma_mask(pdev, cmask) == 0) {
|
2008-07-22 10:44:04 +08:00
|
|
|
adapter->pci_using_dac = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-03-03 00:02:16 +08:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update addressable range if firmware supports it */
|
|
|
|
static int
|
|
|
|
nx_update_dma_mask(struct netxen_adapter *adapter)
|
|
|
|
{
|
|
|
|
int change, shift, err;
|
|
|
|
uint64_t mask, old_mask;
|
|
|
|
struct pci_dev *pdev = adapter->pdev;
|
|
|
|
|
|
|
|
change = 0;
|
|
|
|
|
|
|
|
shift = netxen_nic_reg_read(adapter, CRB_DMA_SHIFT);
|
|
|
|
if (shift >= 32)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (NX_IS_REVISION_P3(adapter->ahw.revision_id) && (shift > 9))
|
|
|
|
change = 1;
|
|
|
|
else if ((adapter->ahw.revision_id == NX_P2_C1) && (shift <= 4))
|
|
|
|
change = 1;
|
|
|
|
|
|
|
|
if (change) {
|
|
|
|
old_mask = pdev->dma_mask;
|
|
|
|
mask = (1ULL<<(32+shift)) - 1;
|
|
|
|
|
|
|
|
err = pci_set_dma_mask(pdev, mask);
|
|
|
|
if (err)
|
|
|
|
return pci_set_dma_mask(pdev, old_mask);
|
2008-07-22 10:44:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void netxen_check_options(struct netxen_adapter *adapter)
|
|
|
|
{
|
2009-03-09 16:50:56 +08:00
|
|
|
switch (adapter->ahw.board_type) {
|
2008-07-22 10:44:04 +08:00
|
|
|
case NETXEN_BRDTYPE_P3_HMEZ:
|
|
|
|
case NETXEN_BRDTYPE_P3_XG_LOM:
|
|
|
|
case NETXEN_BRDTYPE_P3_10G_CX4:
|
|
|
|
case NETXEN_BRDTYPE_P3_10G_CX4_LP:
|
|
|
|
case NETXEN_BRDTYPE_P3_IMEZ:
|
|
|
|
case NETXEN_BRDTYPE_P3_10G_SFP_PLUS:
|
2008-08-01 18:14:56 +08:00
|
|
|
case NETXEN_BRDTYPE_P3_10G_SFP_QT:
|
|
|
|
case NETXEN_BRDTYPE_P3_10G_SFP_CT:
|
2008-07-22 10:44:04 +08:00
|
|
|
case NETXEN_BRDTYPE_P3_10G_XFP:
|
|
|
|
case NETXEN_BRDTYPE_P3_10000_BASE_T:
|
|
|
|
adapter->msix_supported = !!use_msi_x;
|
|
|
|
adapter->max_rx_desc_count = MAX_RCV_DESCRIPTORS_10G;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NETXEN_BRDTYPE_P2_SB31_10G:
|
|
|
|
case NETXEN_BRDTYPE_P2_SB31_10G_CX4:
|
|
|
|
case NETXEN_BRDTYPE_P2_SB31_10G_IMEZ:
|
|
|
|
case NETXEN_BRDTYPE_P2_SB31_10G_HMEZ:
|
|
|
|
adapter->msix_supported = 0;
|
|
|
|
adapter->max_rx_desc_count = MAX_RCV_DESCRIPTORS_10G;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NETXEN_BRDTYPE_P3_REF_QG:
|
|
|
|
case NETXEN_BRDTYPE_P3_4_GB:
|
|
|
|
case NETXEN_BRDTYPE_P3_4_GB_MM:
|
2008-08-21 04:52:48 +08:00
|
|
|
adapter->msix_supported = !!use_msi_x;
|
2009-01-15 12:48:32 +08:00
|
|
|
adapter->max_rx_desc_count = MAX_RCV_DESCRIPTORS_1G;
|
2008-08-01 18:14:56 +08:00
|
|
|
break;
|
|
|
|
|
2008-07-22 10:44:04 +08:00
|
|
|
case NETXEN_BRDTYPE_P2_SB35_4G:
|
|
|
|
case NETXEN_BRDTYPE_P2_SB31_2G:
|
|
|
|
adapter->msix_supported = 0;
|
|
|
|
adapter->max_rx_desc_count = MAX_RCV_DESCRIPTORS_1G;
|
|
|
|
break;
|
|
|
|
|
2009-01-15 12:48:32 +08:00
|
|
|
case NETXEN_BRDTYPE_P3_10G_TP:
|
|
|
|
adapter->msix_supported = !!use_msi_x;
|
2009-03-09 16:50:56 +08:00
|
|
|
if (adapter->ahw.port_type == NETXEN_NIC_XGBE)
|
2009-01-15 12:48:32 +08:00
|
|
|
adapter->max_rx_desc_count = MAX_RCV_DESCRIPTORS_10G;
|
|
|
|
else
|
|
|
|
adapter->max_rx_desc_count = MAX_RCV_DESCRIPTORS_1G;
|
|
|
|
break;
|
|
|
|
|
2008-07-22 10:44:04 +08:00
|
|
|
default:
|
|
|
|
adapter->msix_supported = 0;
|
|
|
|
adapter->max_rx_desc_count = MAX_RCV_DESCRIPTORS_1G;
|
|
|
|
|
|
|
|
printk(KERN_WARNING "Unknown board type(0x%x)\n",
|
2009-03-09 16:50:56 +08:00
|
|
|
adapter->ahw.board_type);
|
2008-07-22 10:44:04 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
adapter->max_tx_desc_count = MAX_CMD_DESCRIPTORS_HOST;
|
|
|
|
adapter->max_jumbo_rx_desc_count = MAX_JUMBO_RCV_DESCRIPTORS;
|
|
|
|
adapter->max_lro_rx_desc_count = MAX_LRO_RCV_DESCRIPTORS;
|
|
|
|
|
|
|
|
adapter->max_possible_rss_rings = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
netxen_check_hw_init(struct netxen_adapter *adapter, int first_boot)
|
|
|
|
{
|
2009-01-15 12:49:00 +08:00
|
|
|
u32 val, timeout;
|
2008-07-22 10:44:04 +08:00
|
|
|
|
|
|
|
if (first_boot == 0x55555555) {
|
|
|
|
/* This is the first boot after power up */
|
2009-01-15 12:49:00 +08:00
|
|
|
adapter->pci_write_normalize(adapter,
|
|
|
|
NETXEN_CAM_RAM(0x1fc), NETXEN_BDINFO_MAGIC);
|
|
|
|
|
|
|
|
if (!NX_IS_REVISION_P2(adapter->ahw.revision_id))
|
|
|
|
return 0;
|
2008-07-22 10:44:04 +08:00
|
|
|
|
|
|
|
/* PCI bus master workaround */
|
|
|
|
adapter->hw_read_wx(adapter,
|
|
|
|
NETXEN_PCIE_REG(0x4), &first_boot, 4);
|
|
|
|
if (!(first_boot & 0x4)) {
|
|
|
|
first_boot |= 0x4;
|
|
|
|
adapter->hw_write_wx(adapter,
|
|
|
|
NETXEN_PCIE_REG(0x4), &first_boot, 4);
|
|
|
|
adapter->hw_read_wx(adapter,
|
|
|
|
NETXEN_PCIE_REG(0x4), &first_boot, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This is the first boot after power up */
|
|
|
|
adapter->hw_read_wx(adapter,
|
|
|
|
NETXEN_ROMUSB_GLB_SW_RESET, &first_boot, 4);
|
|
|
|
if (first_boot != 0x80000f) {
|
|
|
|
/* clear the register for future unloads/loads */
|
|
|
|
adapter->pci_write_normalize(adapter,
|
|
|
|
NETXEN_CAM_RAM(0x1fc), 0);
|
2009-01-15 12:49:00 +08:00
|
|
|
return -EIO;
|
2008-07-22 10:44:04 +08:00
|
|
|
}
|
|
|
|
|
2009-01-15 12:49:00 +08:00
|
|
|
/* Start P2 boot loader */
|
|
|
|
val = adapter->pci_read_normalize(adapter,
|
|
|
|
NETXEN_ROMUSB_GLB_PEGTUNE_DONE);
|
|
|
|
adapter->pci_write_normalize(adapter,
|
|
|
|
NETXEN_ROMUSB_GLB_PEGTUNE_DONE, val | 0x1);
|
|
|
|
timeout = 0;
|
|
|
|
do {
|
|
|
|
msleep(1);
|
|
|
|
val = adapter->pci_read_normalize(adapter,
|
|
|
|
NETXEN_CAM_RAM(0x1fc));
|
|
|
|
|
|
|
|
if (++timeout > 5000)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
} while (val == NETXEN_BDINFO_MAGIC);
|
2008-07-22 10:44:04 +08:00
|
|
|
}
|
2009-01-15 12:49:00 +08:00
|
|
|
return 0;
|
2008-07-22 10:44:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void netxen_set_port_mode(struct netxen_adapter *adapter)
|
|
|
|
{
|
|
|
|
u32 val, data;
|
|
|
|
|
2009-03-09 16:50:56 +08:00
|
|
|
val = adapter->ahw.board_type;
|
2008-07-22 10:44:04 +08:00
|
|
|
if ((val == NETXEN_BRDTYPE_P3_HMEZ) ||
|
|
|
|
(val == NETXEN_BRDTYPE_P3_XG_LOM)) {
|
|
|
|
if (port_mode == NETXEN_PORT_MODE_802_3_AP) {
|
|
|
|
data = NETXEN_PORT_MODE_802_3_AP;
|
|
|
|
adapter->hw_write_wx(adapter,
|
|
|
|
NETXEN_PORT_MODE_ADDR, &data, 4);
|
|
|
|
} else if (port_mode == NETXEN_PORT_MODE_XG) {
|
|
|
|
data = NETXEN_PORT_MODE_XG;
|
|
|
|
adapter->hw_write_wx(adapter,
|
|
|
|
NETXEN_PORT_MODE_ADDR, &data, 4);
|
|
|
|
} else if (port_mode == NETXEN_PORT_MODE_AUTO_NEG_1G) {
|
|
|
|
data = NETXEN_PORT_MODE_AUTO_NEG_1G;
|
|
|
|
adapter->hw_write_wx(adapter,
|
|
|
|
NETXEN_PORT_MODE_ADDR, &data, 4);
|
|
|
|
} else if (port_mode == NETXEN_PORT_MODE_AUTO_NEG_XG) {
|
|
|
|
data = NETXEN_PORT_MODE_AUTO_NEG_XG;
|
|
|
|
adapter->hw_write_wx(adapter,
|
|
|
|
NETXEN_PORT_MODE_ADDR, &data, 4);
|
|
|
|
} else {
|
|
|
|
data = NETXEN_PORT_MODE_AUTO_NEG;
|
|
|
|
adapter->hw_write_wx(adapter,
|
|
|
|
NETXEN_PORT_MODE_ADDR, &data, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((wol_port_mode != NETXEN_PORT_MODE_802_3_AP) &&
|
|
|
|
(wol_port_mode != NETXEN_PORT_MODE_XG) &&
|
|
|
|
(wol_port_mode != NETXEN_PORT_MODE_AUTO_NEG_1G) &&
|
|
|
|
(wol_port_mode != NETXEN_PORT_MODE_AUTO_NEG_XG)) {
|
|
|
|
wol_port_mode = NETXEN_PORT_MODE_AUTO_NEG;
|
|
|
|
}
|
|
|
|
adapter->hw_write_wx(adapter, NETXEN_WOL_PORT_MODE,
|
|
|
|
&wol_port_mode, 4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void netxen_set_msix_bit(struct pci_dev *pdev, int enable)
|
|
|
|
{
|
|
|
|
u32 control;
|
|
|
|
int pos;
|
|
|
|
|
|
|
|
pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX);
|
|
|
|
if (pos) {
|
|
|
|
pci_read_config_dword(pdev, pos, &control);
|
|
|
|
if (enable)
|
|
|
|
control |= PCI_MSIX_FLAGS_ENABLE;
|
|
|
|
else
|
|
|
|
control = 0;
|
|
|
|
pci_write_config_dword(pdev, pos, control);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void netxen_init_msix_entries(struct netxen_adapter *adapter)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < MSIX_ENTRIES_PER_ADAPTER; i++)
|
|
|
|
adapter->msix_entries[i].entry = i;
|
|
|
|
}
|
|
|
|
|
2008-08-08 15:08:39 +08:00
|
|
|
static int
|
|
|
|
netxen_read_mac_addr(struct netxen_adapter *adapter)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
unsigned char *p;
|
|
|
|
__le64 mac_addr;
|
|
|
|
struct net_device *netdev = adapter->netdev;
|
|
|
|
struct pci_dev *pdev = adapter->pdev;
|
|
|
|
|
|
|
|
if (netxen_is_flash_supported(adapter) != 0)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) {
|
|
|
|
if (netxen_p3_get_mac_addr(adapter, &mac_addr) != 0)
|
|
|
|
return -EIO;
|
|
|
|
} else {
|
|
|
|
if (netxen_get_flash_mac_addr(adapter, &mac_addr) != 0)
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
p = (unsigned char *)&mac_addr;
|
|
|
|
for (i = 0; i < 6; i++)
|
|
|
|
netdev->dev_addr[i] = *(p + 5 - i);
|
|
|
|
|
|
|
|
memcpy(netdev->perm_addr, netdev->dev_addr, netdev->addr_len);
|
|
|
|
|
|
|
|
/* set station address */
|
|
|
|
|
2008-10-28 06:59:26 +08:00
|
|
|
if (!is_valid_ether_addr(netdev->perm_addr))
|
|
|
|
dev_warn(&pdev->dev, "Bad MAC address %pM.\n", netdev->dev_addr);
|
|
|
|
else
|
2008-08-08 15:08:39 +08:00
|
|
|
adapter->macaddr_set(adapter, netdev->dev_addr);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-11-22 09:32:15 +08:00
|
|
|
static void netxen_set_multicast_list(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct netxen_adapter *adapter = netdev_priv(dev);
|
|
|
|
|
|
|
|
if (NX_IS_REVISION_P3(adapter->ahw.revision_id))
|
|
|
|
netxen_p3_nic_set_multi(dev);
|
|
|
|
else
|
|
|
|
netxen_p2_nic_set_multi(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct net_device_ops netxen_netdev_ops = {
|
|
|
|
.ndo_open = netxen_nic_open,
|
|
|
|
.ndo_stop = netxen_nic_close,
|
|
|
|
.ndo_start_xmit = netxen_nic_xmit_frame,
|
|
|
|
.ndo_get_stats = netxen_nic_get_stats,
|
|
|
|
.ndo_validate_addr = eth_validate_addr,
|
|
|
|
.ndo_set_multicast_list = netxen_set_multicast_list,
|
|
|
|
.ndo_set_mac_address = netxen_nic_set_mac,
|
|
|
|
.ndo_change_mtu = netxen_nic_change_mtu,
|
|
|
|
.ndo_tx_timeout = netxen_tx_timeout,
|
|
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
|
|
.ndo_poll_controller = netxen_nic_poll_controller,
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
2009-02-25 08:39:21 +08:00
|
|
|
static void
|
|
|
|
netxen_setup_intr(struct netxen_adapter *adapter)
|
|
|
|
{
|
|
|
|
struct netxen_legacy_intr_set *legacy_intrp;
|
|
|
|
struct pci_dev *pdev = adapter->pdev;
|
|
|
|
|
|
|
|
adapter->flags &= ~(NETXEN_NIC_MSI_ENABLED | NETXEN_NIC_MSIX_ENABLED);
|
|
|
|
adapter->intr_scheme = -1;
|
|
|
|
adapter->msi_mode = -1;
|
|
|
|
|
|
|
|
if (adapter->ahw.revision_id >= NX_P3_B0)
|
|
|
|
legacy_intrp = &legacy_intr[adapter->ahw.pci_func];
|
|
|
|
else
|
|
|
|
legacy_intrp = &legacy_intr[0];
|
|
|
|
adapter->legacy_intr.int_vec_bit = legacy_intrp->int_vec_bit;
|
|
|
|
adapter->legacy_intr.tgt_status_reg = legacy_intrp->tgt_status_reg;
|
|
|
|
adapter->legacy_intr.tgt_mask_reg = legacy_intrp->tgt_mask_reg;
|
|
|
|
adapter->legacy_intr.pci_int_reg = legacy_intrp->pci_int_reg;
|
|
|
|
|
|
|
|
netxen_set_msix_bit(pdev, 0);
|
|
|
|
|
|
|
|
if (adapter->msix_supported) {
|
|
|
|
|
|
|
|
netxen_init_msix_entries(adapter);
|
|
|
|
if (pci_enable_msix(pdev, adapter->msix_entries,
|
|
|
|
MSIX_ENTRIES_PER_ADAPTER))
|
|
|
|
goto request_msi;
|
|
|
|
|
|
|
|
adapter->flags |= NETXEN_NIC_MSIX_ENABLED;
|
|
|
|
netxen_set_msix_bit(pdev, 1);
|
|
|
|
dev_info(&pdev->dev, "using msi-x interrupts\n");
|
|
|
|
|
|
|
|
} else {
|
|
|
|
request_msi:
|
|
|
|
if (use_msi && !pci_enable_msi(pdev)) {
|
|
|
|
adapter->flags |= NETXEN_NIC_MSI_ENABLED;
|
|
|
|
dev_info(&pdev->dev, "using msi interrupts\n");
|
|
|
|
} else
|
|
|
|
dev_info(&pdev->dev, "using legacy interrupts\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
netxen_teardown_intr(struct netxen_adapter *adapter)
|
|
|
|
{
|
|
|
|
if (adapter->flags & NETXEN_NIC_MSIX_ENABLED)
|
|
|
|
pci_disable_msix(adapter->pdev);
|
|
|
|
if (adapter->flags & NETXEN_NIC_MSI_ENABLED)
|
|
|
|
pci_disable_msi(adapter->pdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
netxen_cleanup_pci_map(struct netxen_adapter *adapter)
|
|
|
|
{
|
|
|
|
if (adapter->ahw.db_base != NULL)
|
|
|
|
iounmap(adapter->ahw.db_base);
|
|
|
|
if (adapter->ahw.pci_base0 != NULL)
|
|
|
|
iounmap(adapter->ahw.pci_base0);
|
|
|
|
if (adapter->ahw.pci_base1 != NULL)
|
|
|
|
iounmap(adapter->ahw.pci_base1);
|
|
|
|
if (adapter->ahw.pci_base2 != NULL)
|
|
|
|
iounmap(adapter->ahw.pci_base2);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
netxen_setup_pci_map(struct netxen_adapter *adapter)
|
|
|
|
{
|
|
|
|
void __iomem *mem_ptr0 = NULL;
|
|
|
|
void __iomem *mem_ptr1 = NULL;
|
|
|
|
void __iomem *mem_ptr2 = NULL;
|
|
|
|
void __iomem *db_ptr = NULL;
|
|
|
|
|
|
|
|
unsigned long mem_base, mem_len, db_base, db_len = 0, pci_len0 = 0;
|
|
|
|
|
|
|
|
struct pci_dev *pdev = adapter->pdev;
|
|
|
|
int pci_func = adapter->ahw.pci_func;
|
|
|
|
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the CRB window to invalid. If any register in window 0 is
|
|
|
|
* accessed it should set the window to 0 and then reset it to 1.
|
|
|
|
*/
|
|
|
|
adapter->curr_window = 255;
|
|
|
|
adapter->ahw.qdr_sn_window = -1;
|
|
|
|
adapter->ahw.ddr_mn_window = -1;
|
|
|
|
|
|
|
|
/* remap phys address */
|
|
|
|
mem_base = pci_resource_start(pdev, 0); /* 0 is for BAR 0 */
|
|
|
|
mem_len = pci_resource_len(pdev, 0);
|
|
|
|
pci_len0 = 0;
|
|
|
|
|
|
|
|
adapter->hw_write_wx = netxen_nic_hw_write_wx_128M;
|
|
|
|
adapter->hw_read_wx = netxen_nic_hw_read_wx_128M;
|
|
|
|
adapter->pci_read_immediate = netxen_nic_pci_read_immediate_128M;
|
|
|
|
adapter->pci_write_immediate = netxen_nic_pci_write_immediate_128M;
|
|
|
|
adapter->pci_read_normalize = netxen_nic_pci_read_normalize_128M;
|
|
|
|
adapter->pci_write_normalize = netxen_nic_pci_write_normalize_128M;
|
|
|
|
adapter->pci_set_window = netxen_nic_pci_set_window_128M;
|
|
|
|
adapter->pci_mem_read = netxen_nic_pci_mem_read_128M;
|
|
|
|
adapter->pci_mem_write = netxen_nic_pci_mem_write_128M;
|
|
|
|
|
|
|
|
/* 128 Meg of memory */
|
|
|
|
if (mem_len == NETXEN_PCI_128MB_SIZE) {
|
|
|
|
mem_ptr0 = ioremap(mem_base, FIRST_PAGE_GROUP_SIZE);
|
|
|
|
mem_ptr1 = ioremap(mem_base + SECOND_PAGE_GROUP_START,
|
|
|
|
SECOND_PAGE_GROUP_SIZE);
|
|
|
|
mem_ptr2 = ioremap(mem_base + THIRD_PAGE_GROUP_START,
|
|
|
|
THIRD_PAGE_GROUP_SIZE);
|
|
|
|
} else if (mem_len == NETXEN_PCI_32MB_SIZE) {
|
|
|
|
mem_ptr1 = ioremap(mem_base, SECOND_PAGE_GROUP_SIZE);
|
|
|
|
mem_ptr2 = ioremap(mem_base + THIRD_PAGE_GROUP_START -
|
|
|
|
SECOND_PAGE_GROUP_START, THIRD_PAGE_GROUP_SIZE);
|
|
|
|
} else if (mem_len == NETXEN_PCI_2MB_SIZE) {
|
|
|
|
adapter->hw_write_wx = netxen_nic_hw_write_wx_2M;
|
|
|
|
adapter->hw_read_wx = netxen_nic_hw_read_wx_2M;
|
|
|
|
adapter->pci_read_immediate = netxen_nic_pci_read_immediate_2M;
|
|
|
|
adapter->pci_write_immediate =
|
|
|
|
netxen_nic_pci_write_immediate_2M;
|
|
|
|
adapter->pci_read_normalize = netxen_nic_pci_read_normalize_2M;
|
|
|
|
adapter->pci_write_normalize =
|
|
|
|
netxen_nic_pci_write_normalize_2M;
|
|
|
|
adapter->pci_set_window = netxen_nic_pci_set_window_2M;
|
|
|
|
adapter->pci_mem_read = netxen_nic_pci_mem_read_2M;
|
|
|
|
adapter->pci_mem_write = netxen_nic_pci_mem_write_2M;
|
|
|
|
|
|
|
|
mem_ptr0 = pci_ioremap_bar(pdev, 0);
|
|
|
|
if (mem_ptr0 == NULL) {
|
|
|
|
dev_err(&pdev->dev, "failed to map PCI bar 0\n");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
pci_len0 = mem_len;
|
|
|
|
|
|
|
|
adapter->ahw.ddr_mn_window = 0;
|
|
|
|
adapter->ahw.qdr_sn_window = 0;
|
|
|
|
|
|
|
|
adapter->ahw.mn_win_crb = 0x100000 + PCIX_MN_WINDOW +
|
|
|
|
(pci_func * 0x20);
|
|
|
|
adapter->ahw.ms_win_crb = 0x100000 + PCIX_SN_WINDOW;
|
|
|
|
if (pci_func < 4)
|
|
|
|
adapter->ahw.ms_win_crb += (pci_func * 0x20);
|
|
|
|
else
|
|
|
|
adapter->ahw.ms_win_crb +=
|
|
|
|
0xA0 + ((pci_func - 4) * 0x10);
|
|
|
|
} else {
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_info(&pdev->dev, "%dMB memory map\n", (int)(mem_len>>20));
|
|
|
|
|
|
|
|
adapter->ahw.pci_base0 = mem_ptr0;
|
|
|
|
adapter->ahw.pci_len0 = pci_len0;
|
|
|
|
adapter->ahw.pci_base1 = mem_ptr1;
|
|
|
|
adapter->ahw.pci_base2 = mem_ptr2;
|
|
|
|
|
|
|
|
if (NX_IS_REVISION_P3(adapter->ahw.revision_id))
|
|
|
|
goto skip_doorbell;
|
|
|
|
|
|
|
|
db_base = pci_resource_start(pdev, 4); /* doorbell is on bar 4 */
|
|
|
|
db_len = pci_resource_len(pdev, 4);
|
|
|
|
|
|
|
|
if (db_len == 0) {
|
|
|
|
printk(KERN_ERR "%s: doorbell is disabled\n",
|
|
|
|
netxen_nic_driver_name);
|
|
|
|
err = -EIO;
|
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
db_ptr = ioremap(db_base, NETXEN_DB_MAPSIZE_BYTES);
|
|
|
|
if (!db_ptr) {
|
|
|
|
printk(KERN_ERR "%s: Failed to allocate doorbell map.",
|
|
|
|
netxen_nic_driver_name);
|
|
|
|
err = -EIO;
|
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
skip_doorbell:
|
|
|
|
adapter->ahw.db_base = db_ptr;
|
|
|
|
adapter->ahw.db_len = db_len;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_out:
|
|
|
|
netxen_cleanup_pci_map(adapter);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2009-02-25 08:38:22 +08:00
|
|
|
static int
|
|
|
|
netxen_start_firmware(struct netxen_adapter *adapter)
|
|
|
|
{
|
|
|
|
int val, err, first_boot;
|
|
|
|
struct pci_dev *pdev = adapter->pdev;
|
|
|
|
|
2009-03-13 22:52:02 +08:00
|
|
|
int first_driver = 0;
|
|
|
|
if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) {
|
|
|
|
if (adapter->ahw.pci_func == 0)
|
|
|
|
first_driver = 1;
|
|
|
|
} else {
|
|
|
|
if (adapter->portnum == 0)
|
|
|
|
first_driver = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!first_driver)
|
|
|
|
return 0;
|
|
|
|
|
2009-02-25 08:38:22 +08:00
|
|
|
first_boot = adapter->pci_read_normalize(adapter,
|
|
|
|
NETXEN_CAM_RAM(0x1fc));
|
|
|
|
|
|
|
|
err = netxen_check_hw_init(adapter, first_boot);
|
|
|
|
if (err) {
|
|
|
|
dev_err(&pdev->dev, "error in init HW init sequence\n");
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (first_boot != 0x55555555) {
|
|
|
|
adapter->pci_write_normalize(adapter,
|
|
|
|
CRB_CMDPEG_STATE, 0);
|
|
|
|
netxen_pinit_from_rom(adapter, 0);
|
|
|
|
msleep(1);
|
|
|
|
}
|
2009-03-03 00:02:17 +08:00
|
|
|
|
2009-03-03 00:02:16 +08:00
|
|
|
netxen_nic_reg_write(adapter, CRB_DMA_SHIFT, 0x55555555);
|
2009-03-03 00:02:17 +08:00
|
|
|
if (NX_IS_REVISION_P3(adapter->ahw.revision_id))
|
|
|
|
netxen_set_port_mode(adapter);
|
|
|
|
|
2009-02-25 08:38:22 +08:00
|
|
|
netxen_load_firmware(adapter);
|
|
|
|
|
|
|
|
if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) {
|
|
|
|
|
|
|
|
/* Initialize multicast addr pool owners */
|
|
|
|
val = 0x7654;
|
2009-03-09 16:50:56 +08:00
|
|
|
if (adapter->ahw.port_type == NETXEN_NIC_XGBE)
|
2009-02-25 08:38:22 +08:00
|
|
|
val |= 0x0f000000;
|
|
|
|
netxen_crb_writelit_adapter(adapter,
|
|
|
|
NETXEN_MAC_ADDR_CNTL_REG, val);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
err = netxen_initialize_adapter_offload(adapter);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Tell the hardware our version number.
|
|
|
|
*/
|
|
|
|
val = (_NETXEN_NIC_LINUX_MAJOR << 16)
|
|
|
|
| ((_NETXEN_NIC_LINUX_MINOR << 8))
|
|
|
|
| (_NETXEN_NIC_LINUX_SUBVERSION);
|
|
|
|
adapter->pci_write_normalize(adapter, CRB_DRIVER_VERSION, val);
|
|
|
|
|
|
|
|
/* Handshake with the card before we register the devices. */
|
|
|
|
err = netxen_phantom_init(adapter, NETXEN_NIC_PEG_TUNE);
|
|
|
|
if (err) {
|
|
|
|
netxen_free_adapter_offload(adapter);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-03-09 16:50:54 +08:00
|
|
|
static int
|
|
|
|
netxen_nic_request_irq(struct netxen_adapter *adapter)
|
|
|
|
{
|
|
|
|
irq_handler_t handler;
|
|
|
|
unsigned long flags = IRQF_SAMPLE_RANDOM;
|
|
|
|
struct net_device *netdev = adapter->netdev;
|
|
|
|
|
|
|
|
if ((adapter->msi_mode != MSI_MODE_MULTIFUNC) ||
|
|
|
|
(adapter->intr_scheme != INTR_SCHEME_PERPORT)) {
|
|
|
|
printk(KERN_ERR "%s: Firmware interrupt scheme is "
|
|
|
|
"incompatible with driver\n",
|
|
|
|
netdev->name);
|
|
|
|
adapter->driver_mismatch = 1;
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (adapter->flags & NETXEN_NIC_MSIX_ENABLED)
|
|
|
|
handler = netxen_msix_intr;
|
|
|
|
else if (adapter->flags & NETXEN_NIC_MSI_ENABLED)
|
|
|
|
handler = netxen_msi_intr;
|
|
|
|
else {
|
|
|
|
flags |= IRQF_SHARED;
|
|
|
|
handler = netxen_intr;
|
|
|
|
}
|
|
|
|
adapter->irq = netdev->irq;
|
|
|
|
|
|
|
|
return request_irq(adapter->irq, handler,
|
|
|
|
flags, netdev->name, adapter);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
netxen_nic_up(struct netxen_adapter *adapter, struct net_device *netdev)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = adapter->init_port(adapter, adapter->physical_port);
|
|
|
|
if (err) {
|
|
|
|
printk(KERN_ERR "%s: Failed to initialize port %d\n",
|
|
|
|
netxen_nic_driver_name, adapter->portnum);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
adapter->macaddr_set(adapter, netdev->dev_addr);
|
|
|
|
|
|
|
|
netxen_nic_set_link_parameters(adapter);
|
|
|
|
|
|
|
|
netxen_set_multicast_list(netdev);
|
|
|
|
if (adapter->set_mtu)
|
|
|
|
adapter->set_mtu(adapter, netdev->mtu);
|
|
|
|
|
|
|
|
adapter->ahw.linkup = 0;
|
|
|
|
mod_timer(&adapter->watchdog_timer, jiffies);
|
|
|
|
|
|
|
|
napi_enable(&adapter->napi);
|
|
|
|
netxen_nic_enable_int(adapter);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
netxen_nic_down(struct netxen_adapter *adapter, struct net_device *netdev)
|
|
|
|
{
|
|
|
|
netif_carrier_off(netdev);
|
|
|
|
netif_stop_queue(netdev);
|
|
|
|
napi_disable(&adapter->napi);
|
|
|
|
|
|
|
|
if (adapter->stop_port)
|
|
|
|
adapter->stop_port(adapter);
|
|
|
|
|
|
|
|
netxen_nic_disable_int(adapter);
|
|
|
|
|
|
|
|
netxen_release_tx_buffers(adapter);
|
|
|
|
|
|
|
|
FLUSH_SCHEDULED_WORK();
|
|
|
|
del_timer_sync(&adapter->watchdog_timer);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
netxen_nic_attach(struct netxen_adapter *adapter)
|
|
|
|
{
|
|
|
|
struct net_device *netdev = adapter->netdev;
|
|
|
|
struct pci_dev *pdev = adapter->pdev;
|
2009-03-09 16:50:55 +08:00
|
|
|
int err, ring;
|
2009-03-09 16:50:54 +08:00
|
|
|
|
|
|
|
err = netxen_init_firmware(adapter);
|
|
|
|
if (err != 0) {
|
|
|
|
printk(KERN_ERR "Failed to init firmware\n");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (adapter->fw_major < 4)
|
|
|
|
adapter->max_rds_rings = 3;
|
|
|
|
else
|
|
|
|
adapter->max_rds_rings = 2;
|
|
|
|
|
|
|
|
err = netxen_alloc_sw_resources(adapter);
|
|
|
|
if (err) {
|
|
|
|
printk(KERN_ERR "%s: Error in setting sw resources\n",
|
|
|
|
netdev->name);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
netxen_nic_clear_stats(adapter);
|
|
|
|
|
|
|
|
err = netxen_alloc_hw_resources(adapter);
|
|
|
|
if (err) {
|
|
|
|
printk(KERN_ERR "%s: Error in setting hw resources\n",
|
|
|
|
netdev->name);
|
|
|
|
goto err_out_free_sw;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (adapter->fw_major < 4) {
|
|
|
|
adapter->crb_addr_cmd_producer =
|
|
|
|
crb_cmd_producer[adapter->portnum];
|
|
|
|
adapter->crb_addr_cmd_consumer =
|
|
|
|
crb_cmd_consumer[adapter->portnum];
|
|
|
|
|
|
|
|
netxen_nic_update_cmd_producer(adapter, 0);
|
|
|
|
netxen_nic_update_cmd_consumer(adapter, 0);
|
|
|
|
}
|
|
|
|
|
2009-03-09 16:50:55 +08:00
|
|
|
for (ring = 0; ring < adapter->max_rds_rings; ring++)
|
|
|
|
netxen_post_rx_buffers(adapter, ring);
|
2009-03-09 16:50:54 +08:00
|
|
|
|
|
|
|
err = netxen_nic_request_irq(adapter);
|
|
|
|
if (err) {
|
|
|
|
dev_err(&pdev->dev, "%s: failed to setup interrupt\n",
|
|
|
|
netdev->name);
|
|
|
|
goto err_out_free_rxbuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
adapter->is_up = NETXEN_ADAPTER_UP_MAGIC;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_out_free_rxbuf:
|
|
|
|
netxen_release_rx_buffers(adapter);
|
|
|
|
netxen_free_hw_resources(adapter);
|
|
|
|
err_out_free_sw:
|
|
|
|
netxen_free_sw_resources(adapter);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
netxen_nic_detach(struct netxen_adapter *adapter)
|
|
|
|
{
|
|
|
|
if (adapter->irq)
|
|
|
|
free_irq(adapter->irq, adapter);
|
|
|
|
|
|
|
|
netxen_release_rx_buffers(adapter);
|
|
|
|
netxen_free_hw_resources(adapter);
|
|
|
|
netxen_free_sw_resources(adapter);
|
|
|
|
|
|
|
|
adapter->is_up = 0;
|
|
|
|
}
|
|
|
|
|
2006-10-22 03:33:03 +08:00
|
|
|
static int __devinit
|
|
|
|
netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|
|
|
{
|
|
|
|
struct net_device *netdev = NULL;
|
|
|
|
struct netxen_adapter *adapter = NULL;
|
2008-07-22 10:44:04 +08:00
|
|
|
int i = 0, err;
|
2007-04-20 22:52:37 +08:00
|
|
|
int pci_func_id = PCI_FUNC(pdev->devfn);
|
2008-07-22 10:44:04 +08:00
|
|
|
uint8_t revision_id;
|
2006-10-22 03:33:03 +08:00
|
|
|
|
2008-06-16 13:59:45 +08:00
|
|
|
if (pci_func_id == 0)
|
2008-07-22 10:44:04 +08:00
|
|
|
printk(KERN_INFO "%s\n", netxen_nic_driver_string);
|
2007-04-20 22:55:26 +08:00
|
|
|
|
2007-04-20 22:52:37 +08:00
|
|
|
if (pdev->class != 0x020000) {
|
2008-06-16 13:59:45 +08:00
|
|
|
printk(KERN_DEBUG "NetXen function %d, class %x will not "
|
2007-04-20 22:52:37 +08:00
|
|
|
"be enabled.\n",pci_func_id, pdev->class);
|
2006-12-05 01:23:25 +08:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
2008-07-22 10:44:04 +08:00
|
|
|
|
2008-08-08 15:08:45 +08:00
|
|
|
if (pdev->revision >= NX_P3_A0 && pdev->revision < NX_P3_B1) {
|
|
|
|
printk(KERN_WARNING "NetXen chip revisions between 0x%x-0x%x"
|
|
|
|
"will not be enabled.\n",
|
|
|
|
NX_P3_A0, NX_P3_B1);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2006-10-22 03:33:03 +08:00
|
|
|
if ((err = pci_enable_device(pdev)))
|
|
|
|
return err;
|
2008-07-22 10:44:04 +08:00
|
|
|
|
2006-10-22 03:33:03 +08:00
|
|
|
if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
|
|
|
|
err = -ENODEV;
|
|
|
|
goto err_out_disable_pdev;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((err = pci_request_regions(pdev, netxen_nic_driver_name)))
|
|
|
|
goto err_out_disable_pdev;
|
|
|
|
|
|
|
|
pci_set_master(pdev);
|
2007-04-20 22:52:37 +08:00
|
|
|
|
|
|
|
netdev = alloc_etherdev(sizeof(struct netxen_adapter));
|
|
|
|
if(!netdev) {
|
|
|
|
printk(KERN_ERR"%s: Failed to allocate memory for the "
|
|
|
|
"device block.Check system memory resource"
|
|
|
|
" usage.\n", netxen_nic_driver_name);
|
|
|
|
goto err_out_free_res;
|
|
|
|
}
|
|
|
|
|
|
|
|
SET_NETDEV_DEV(netdev, &pdev->dev);
|
|
|
|
|
2008-11-13 15:38:14 +08:00
|
|
|
adapter = netdev_priv(netdev);
|
2008-07-22 10:44:04 +08:00
|
|
|
adapter->netdev = netdev;
|
|
|
|
adapter->pdev = pdev;
|
2007-04-20 22:53:05 +08:00
|
|
|
adapter->ahw.pci_func = pci_func_id;
|
2008-07-22 10:44:04 +08:00
|
|
|
|
|
|
|
revision_id = pdev->revision;
|
|
|
|
adapter->ahw.revision_id = revision_id;
|
|
|
|
|
|
|
|
err = nx_set_dma_mask(adapter, revision_id);
|
|
|
|
if (err)
|
|
|
|
goto err_out_free_netdev;
|
|
|
|
|
2008-07-22 10:44:03 +08:00
|
|
|
rwlock_init(&adapter->adapter_lock);
|
|
|
|
|
2009-02-25 08:39:21 +08:00
|
|
|
err = netxen_setup_pci_map(adapter);
|
|
|
|
if (err)
|
2007-04-20 22:55:26 +08:00
|
|
|
goto err_out_free_netdev;
|
2006-10-22 03:33:03 +08:00
|
|
|
|
[NET]: Make NAPI polling independent of struct net_device objects.
Several devices have multiple independant RX queues per net
device, and some have a single interrupt doorbell for several
queues.
In either case, it's easier to support layouts like that if the
structure representing the poll is independant from the net
device itself.
The signature of the ->poll() call back goes from:
int foo_poll(struct net_device *dev, int *budget)
to
int foo_poll(struct napi_struct *napi, int budget)
The caller is returned the number of RX packets processed (or
the number of "NAPI credits" consumed if you want to get
abstract). The callee no longer messes around bumping
dev->quota, *budget, etc. because that is all handled in the
caller upon return.
The napi_struct is to be embedded in the device driver private data
structures.
Furthermore, it is the driver's responsibility to disable all NAPI
instances in it's ->stop() device close handler. Since the
napi_struct is privatized into the driver's private data structures,
only the driver knows how to get at all of the napi_struct instances
it may have per-device.
With lots of help and suggestions from Rusty Russell, Roland Dreier,
Michael Chan, Jeff Garzik, and Jamal Hadi Salim.
Bug fixes from Thomas Graf, Roland Dreier, Peter Zijlstra,
Joseph Fannin, Scott Wood, Hans J. Koch, and Michael Chan.
[ Ported to current tree and all drivers converted. Integrated
Stephen's follow-on kerneldoc additions, and restored poll_list
handling to the old style to fix mutual exclusion issues. -DaveM ]
Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-10-04 07:41:36 +08:00
|
|
|
netif_napi_add(netdev, &adapter->napi,
|
2008-07-22 10:44:04 +08:00
|
|
|
netxen_nic_poll, NETXEN_NETDEV_WEIGHT);
|
|
|
|
|
2007-07-02 02:56:00 +08:00
|
|
|
/* This will be reset for mezz cards */
|
2007-04-20 22:52:37 +08:00
|
|
|
adapter->portnum = pci_func_id;
|
2007-09-03 13:03:35 +08:00
|
|
|
adapter->rx_csum = 1;
|
2008-07-22 10:44:01 +08:00
|
|
|
adapter->mc_enabled = 0;
|
2008-08-01 18:14:58 +08:00
|
|
|
if (NX_IS_REVISION_P3(revision_id))
|
2008-07-22 10:44:04 +08:00
|
|
|
adapter->max_mc_count = 38;
|
2008-08-01 18:14:58 +08:00
|
|
|
else
|
2008-07-22 10:44:04 +08:00
|
|
|
adapter->max_mc_count = 16;
|
2007-04-20 22:52:37 +08:00
|
|
|
|
2008-11-22 09:32:15 +08:00
|
|
|
netdev->netdev_ops = &netxen_netdev_ops;
|
2008-03-18 10:59:49 +08:00
|
|
|
netdev->watchdog_timeo = 2*HZ;
|
2007-04-20 22:52:37 +08:00
|
|
|
|
|
|
|
netxen_nic_change_mtu(netdev, netdev->mtu);
|
|
|
|
|
|
|
|
SET_ETHTOOL_OPS(netdev, &netxen_nic_ethtool_ops);
|
2008-11-22 09:32:15 +08:00
|
|
|
|
2009-01-27 04:34:57 +08:00
|
|
|
netdev->features |= (NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO);
|
|
|
|
netdev->vlan_features |= (NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO);
|
|
|
|
|
2008-07-22 10:44:09 +08:00
|
|
|
if (NX_IS_REVISION_P3(revision_id)) {
|
2009-01-27 04:34:57 +08:00
|
|
|
netdev->features |= (NETIF_F_IPV6_CSUM | NETIF_F_TSO6);
|
|
|
|
netdev->vlan_features |= (NETIF_F_IPV6_CSUM | NETIF_F_TSO6);
|
2008-07-22 10:44:09 +08:00
|
|
|
}
|
2007-04-20 22:52:37 +08:00
|
|
|
|
2009-01-27 04:34:57 +08:00
|
|
|
if (adapter->pci_using_dac) {
|
2007-04-20 22:52:37 +08:00
|
|
|
netdev->features |= NETIF_F_HIGHDMA;
|
2009-01-27 04:34:57 +08:00
|
|
|
netdev->vlan_features |= NETIF_F_HIGHDMA;
|
|
|
|
}
|
2007-04-20 22:52:37 +08:00
|
|
|
|
2008-06-16 13:59:45 +08:00
|
|
|
if (netxen_nic_get_board_info(adapter) != 0) {
|
|
|
|
printk("%s: Error getting board config info.\n",
|
2008-07-22 10:44:04 +08:00
|
|
|
netxen_nic_driver_name);
|
2008-06-16 13:59:45 +08:00
|
|
|
err = -EIO;
|
|
|
|
goto err_out_iounmap;
|
|
|
|
}
|
2007-04-20 22:55:26 +08:00
|
|
|
|
|
|
|
netxen_initialize_adapter_ops(adapter);
|
|
|
|
|
2007-04-20 22:53:05 +08:00
|
|
|
/* Mezz cards have PCI function 0,2,3 enabled */
|
2009-03-09 16:50:56 +08:00
|
|
|
switch (adapter->ahw.board_type) {
|
2008-06-16 13:59:43 +08:00
|
|
|
case NETXEN_BRDTYPE_P2_SB31_10G_IMEZ:
|
|
|
|
case NETXEN_BRDTYPE_P2_SB31_10G_HMEZ:
|
|
|
|
if (pci_func_id >= 2)
|
2007-04-20 22:53:05 +08:00
|
|
|
adapter->portnum = pci_func_id - 2;
|
2008-06-16 13:59:43 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2007-04-20 22:53:05 +08:00
|
|
|
|
2008-07-22 10:44:04 +08:00
|
|
|
/*
|
|
|
|
* This call will setup various max rx/tx counts.
|
|
|
|
* It must be done before any buffer/ring allocations.
|
|
|
|
*/
|
|
|
|
netxen_check_options(adapter);
|
2006-10-22 03:33:03 +08:00
|
|
|
|
2009-03-13 22:52:02 +08:00
|
|
|
err = netxen_start_firmware(adapter);
|
|
|
|
if (err)
|
|
|
|
goto err_out_iounmap;
|
2008-07-22 10:44:04 +08:00
|
|
|
|
2009-03-03 00:02:16 +08:00
|
|
|
nx_update_dma_mask(adapter);
|
|
|
|
|
2009-03-09 16:50:56 +08:00
|
|
|
netxen_nic_get_firmware_info(adapter);
|
2008-07-22 10:44:08 +08:00
|
|
|
|
2006-10-22 03:33:03 +08:00
|
|
|
/*
|
2007-04-20 22:55:26 +08:00
|
|
|
* See if the firmware gave us a virtual-physical port mapping.
|
2006-10-22 03:33:03 +08:00
|
|
|
*/
|
2008-06-16 13:59:45 +08:00
|
|
|
adapter->physical_port = adapter->portnum;
|
2009-02-24 19:42:59 +08:00
|
|
|
if (adapter->fw_major < 4) {
|
|
|
|
i = adapter->pci_read_normalize(adapter,
|
|
|
|
CRB_V2P(adapter->portnum));
|
|
|
|
if (i != 0x55555555)
|
|
|
|
adapter->physical_port = i;
|
|
|
|
}
|
2007-04-20 22:55:26 +08:00
|
|
|
|
2008-07-22 10:44:04 +08:00
|
|
|
adapter->flags &= ~(NETXEN_NIC_MSI_ENABLED | NETXEN_NIC_MSIX_ENABLED);
|
|
|
|
|
|
|
|
netxen_set_msix_bit(pdev, 0);
|
|
|
|
|
2009-02-25 08:39:21 +08:00
|
|
|
netxen_setup_intr(adapter);
|
2008-07-22 10:44:04 +08:00
|
|
|
|
|
|
|
if (adapter->flags & NETXEN_NIC_MSIX_ENABLED)
|
|
|
|
netdev->irq = adapter->msix_entries[0].vector;
|
|
|
|
else
|
|
|
|
netdev->irq = pdev->irq;
|
|
|
|
|
|
|
|
err = netxen_receive_peg_ready(adapter);
|
|
|
|
if (err)
|
|
|
|
goto err_out_disable_msi;
|
|
|
|
|
|
|
|
init_timer(&adapter->watchdog_timer);
|
|
|
|
adapter->watchdog_timer.function = &netxen_watchdog;
|
|
|
|
adapter->watchdog_timer.data = (unsigned long)adapter;
|
|
|
|
INIT_WORK(&adapter->watchdog_task, netxen_watchdog_task);
|
|
|
|
INIT_WORK(&adapter->tx_timeout_task, netxen_tx_timeout_task);
|
|
|
|
|
2008-08-08 15:08:39 +08:00
|
|
|
err = netxen_read_mac_addr(adapter);
|
|
|
|
if (err)
|
|
|
|
dev_warn(&pdev->dev, "failed to read mac addr\n");
|
2008-07-22 10:44:04 +08:00
|
|
|
|
2007-04-20 22:52:37 +08:00
|
|
|
netif_carrier_off(netdev);
|
|
|
|
netif_stop_queue(netdev);
|
|
|
|
|
2007-04-20 22:55:26 +08:00
|
|
|
if ((err = register_netdev(netdev))) {
|
|
|
|
printk(KERN_ERR "%s: register_netdev failed port #%d"
|
|
|
|
" aborting\n", netxen_nic_driver_name,
|
|
|
|
adapter->portnum);
|
|
|
|
err = -EIO;
|
2008-07-22 10:44:04 +08:00
|
|
|
goto err_out_disable_msi;
|
2007-04-20 22:55:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pci_set_drvdata(pdev, adapter);
|
2006-10-22 03:33:03 +08:00
|
|
|
|
2009-03-09 16:50:56 +08:00
|
|
|
switch (adapter->ahw.port_type) {
|
2008-07-22 10:44:04 +08:00
|
|
|
case NETXEN_NIC_GBE:
|
|
|
|
dev_info(&adapter->pdev->dev, "%s: GbE port initialized\n",
|
|
|
|
adapter->netdev->name);
|
|
|
|
break;
|
|
|
|
case NETXEN_NIC_XGBE:
|
|
|
|
dev_info(&adapter->pdev->dev, "%s: XGbE port initialized\n",
|
|
|
|
adapter->netdev->name);
|
|
|
|
break;
|
2006-10-22 03:33:03 +08:00
|
|
|
}
|
|
|
|
|
2008-07-22 10:44:04 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_out_disable_msi:
|
2009-02-25 08:39:21 +08:00
|
|
|
netxen_teardown_intr(adapter);
|
2007-04-20 22:55:26 +08:00
|
|
|
|
2009-03-13 22:52:02 +08:00
|
|
|
netxen_free_adapter_offload(adapter);
|
2006-10-22 03:33:03 +08:00
|
|
|
|
2008-07-22 10:44:04 +08:00
|
|
|
err_out_iounmap:
|
2009-02-25 08:39:21 +08:00
|
|
|
netxen_cleanup_pci_map(adapter);
|
2006-11-30 01:00:10 +08:00
|
|
|
|
2007-04-20 22:55:26 +08:00
|
|
|
err_out_free_netdev:
|
|
|
|
free_netdev(netdev);
|
|
|
|
|
|
|
|
err_out_free_res:
|
2006-10-22 03:33:03 +08:00
|
|
|
pci_release_regions(pdev);
|
2007-04-20 22:55:26 +08:00
|
|
|
|
|
|
|
err_out_disable_pdev:
|
2008-07-22 10:44:04 +08:00
|
|
|
pci_set_drvdata(pdev, NULL);
|
2006-10-22 03:33:03 +08:00
|
|
|
pci_disable_device(pdev);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __devexit netxen_nic_remove(struct pci_dev *pdev)
|
|
|
|
{
|
|
|
|
struct netxen_adapter *adapter;
|
2007-04-20 22:52:37 +08:00
|
|
|
struct net_device *netdev;
|
2006-10-22 03:33:03 +08:00
|
|
|
|
2007-04-20 22:55:26 +08:00
|
|
|
adapter = pci_get_drvdata(pdev);
|
2006-10-22 03:33:03 +08:00
|
|
|
if (adapter == NULL)
|
|
|
|
return;
|
|
|
|
|
2007-04-20 22:55:26 +08:00
|
|
|
netdev = adapter->netdev;
|
|
|
|
|
2007-07-02 12:07:57 +08:00
|
|
|
unregister_netdev(netdev);
|
|
|
|
|
|
|
|
if (adapter->is_up == NETXEN_ADAPTER_UP_MAGIC) {
|
2009-03-09 16:50:54 +08:00
|
|
|
netxen_nic_detach(adapter);
|
2009-01-15 12:49:22 +08:00
|
|
|
|
|
|
|
if (NX_IS_REVISION_P3(adapter->ahw.revision_id))
|
|
|
|
netxen_p3_free_mac_list(adapter);
|
2007-07-02 12:07:57 +08:00
|
|
|
}
|
2006-10-22 03:33:03 +08:00
|
|
|
|
2008-06-16 13:59:46 +08:00
|
|
|
if (adapter->portnum == 0)
|
|
|
|
netxen_free_adapter_offload(adapter);
|
2007-07-02 12:07:57 +08:00
|
|
|
|
2009-02-25 08:39:21 +08:00
|
|
|
netxen_teardown_intr(adapter);
|
2007-08-28 19:53:26 +08:00
|
|
|
|
2009-02-25 08:39:21 +08:00
|
|
|
netxen_cleanup_pci_map(adapter);
|
2007-04-20 22:53:52 +08:00
|
|
|
|
|
|
|
pci_release_regions(pdev);
|
2007-08-28 19:53:26 +08:00
|
|
|
pci_disable_device(pdev);
|
2007-04-20 22:53:52 +08:00
|
|
|
pci_set_drvdata(pdev, NULL);
|
|
|
|
|
2007-04-20 22:55:26 +08:00
|
|
|
free_netdev(netdev);
|
2006-10-22 03:33:03 +08:00
|
|
|
}
|
|
|
|
|
2009-03-13 22:52:02 +08:00
|
|
|
static int
|
|
|
|
netxen_nic_suspend(struct pci_dev *pdev, pm_message_t state)
|
|
|
|
{
|
|
|
|
|
|
|
|
struct netxen_adapter *adapter = pci_get_drvdata(pdev);
|
|
|
|
struct net_device *netdev = adapter->netdev;
|
|
|
|
|
|
|
|
netif_device_detach(netdev);
|
|
|
|
|
|
|
|
if (netif_running(netdev))
|
|
|
|
netxen_nic_down(adapter, netdev);
|
|
|
|
|
|
|
|
if (adapter->is_up == NETXEN_ADAPTER_UP_MAGIC)
|
|
|
|
netxen_nic_detach(adapter);
|
|
|
|
|
|
|
|
pci_save_state(pdev);
|
|
|
|
|
|
|
|
if (netxen_nic_wol_supported(adapter)) {
|
|
|
|
pci_enable_wake(pdev, PCI_D3cold, 1);
|
|
|
|
pci_enable_wake(pdev, PCI_D3hot, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
pci_disable_device(pdev);
|
|
|
|
pci_set_power_state(pdev, pci_choose_state(pdev, state));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
netxen_nic_resume(struct pci_dev *pdev)
|
|
|
|
{
|
|
|
|
struct netxen_adapter *adapter = pci_get_drvdata(pdev);
|
|
|
|
struct net_device *netdev = adapter->netdev;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
pci_set_power_state(pdev, PCI_D0);
|
|
|
|
pci_restore_state(pdev);
|
|
|
|
|
|
|
|
err = pci_enable_device(pdev);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
adapter->curr_window = 255;
|
|
|
|
|
|
|
|
err = netxen_start_firmware(adapter);
|
|
|
|
if (err) {
|
|
|
|
dev_err(&pdev->dev, "failed to start firmware\n");
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (netif_running(netdev)) {
|
|
|
|
err = netxen_nic_attach(adapter);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
err = netxen_nic_up(adapter, netdev);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
netif_device_attach(netdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-10-22 03:33:03 +08:00
|
|
|
static int netxen_nic_open(struct net_device *netdev)
|
|
|
|
{
|
2008-11-13 15:38:14 +08:00
|
|
|
struct netxen_adapter *adapter = netdev_priv(netdev);
|
2006-10-22 03:33:03 +08:00
|
|
|
int err = 0;
|
|
|
|
|
2008-06-16 13:59:45 +08:00
|
|
|
if (adapter->driver_mismatch)
|
|
|
|
return -EIO;
|
|
|
|
|
2006-10-22 03:33:03 +08:00
|
|
|
if (adapter->is_up != NETXEN_ADAPTER_UP_MAGIC) {
|
2009-03-09 16:50:54 +08:00
|
|
|
err = netxen_nic_attach(adapter);
|
|
|
|
if (err)
|
2006-10-22 03:33:03 +08:00
|
|
|
return err;
|
2006-12-05 01:23:25 +08:00
|
|
|
}
|
2008-07-22 10:44:04 +08:00
|
|
|
|
2009-03-09 16:50:54 +08:00
|
|
|
err = netxen_nic_up(adapter, netdev);
|
|
|
|
if (err)
|
|
|
|
goto err_out;
|
2008-03-18 10:59:49 +08:00
|
|
|
|
2008-06-16 13:59:45 +08:00
|
|
|
netif_start_queue(netdev);
|
2006-10-22 03:33:03 +08:00
|
|
|
|
|
|
|
return 0;
|
2008-07-22 10:44:04 +08:00
|
|
|
|
2009-03-09 16:50:54 +08:00
|
|
|
err_out:
|
|
|
|
netxen_nic_detach(adapter);
|
2008-07-22 10:44:04 +08:00
|
|
|
return err;
|
2006-10-22 03:33:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* netxen_nic_close - Disables a network interface entry point
|
|
|
|
*/
|
|
|
|
static int netxen_nic_close(struct net_device *netdev)
|
|
|
|
{
|
2007-04-20 22:52:37 +08:00
|
|
|
struct netxen_adapter *adapter = netdev_priv(netdev);
|
2006-10-22 03:33:03 +08:00
|
|
|
|
2009-03-09 16:50:54 +08:00
|
|
|
netxen_nic_down(adapter, netdev);
|
2006-10-22 03:33:03 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-01-15 12:48:11 +08:00
|
|
|
static bool netxen_tso_check(struct net_device *netdev,
|
2008-07-22 10:44:09 +08:00
|
|
|
struct cmd_desc_type0 *desc, struct sk_buff *skb)
|
|
|
|
{
|
2009-01-15 12:48:11 +08:00
|
|
|
bool tso = false;
|
|
|
|
u8 opcode = TX_ETHER_PKT;
|
2009-01-27 04:34:57 +08:00
|
|
|
__be16 protocol = skb->protocol;
|
|
|
|
u16 flags = 0;
|
|
|
|
|
2009-02-01 16:45:17 +08:00
|
|
|
if (protocol == cpu_to_be16(ETH_P_8021Q)) {
|
2009-01-27 04:34:57 +08:00
|
|
|
struct vlan_ethhdr *vh = (struct vlan_ethhdr *)skb->data;
|
|
|
|
protocol = vh->h_vlan_encapsulated_proto;
|
|
|
|
flags = FLAGS_VLAN_TAGGED;
|
|
|
|
}
|
2008-07-22 10:44:09 +08:00
|
|
|
|
2009-01-15 12:48:11 +08:00
|
|
|
if ((netdev->features & (NETIF_F_TSO | NETIF_F_TSO6)) &&
|
|
|
|
skb_shinfo(skb)->gso_size > 0) {
|
|
|
|
|
|
|
|
desc->mss = cpu_to_le16(skb_shinfo(skb)->gso_size);
|
|
|
|
desc->total_hdr_length =
|
|
|
|
skb_transport_offset(skb) + tcp_hdrlen(skb);
|
|
|
|
|
2009-02-01 16:45:17 +08:00
|
|
|
opcode = (protocol == cpu_to_be16(ETH_P_IPV6)) ?
|
2009-01-15 12:48:11 +08:00
|
|
|
TX_TCP_LSO6 : TX_TCP_LSO;
|
|
|
|
tso = true;
|
2008-07-22 10:44:09 +08:00
|
|
|
|
|
|
|
} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
|
2009-01-15 12:48:11 +08:00
|
|
|
u8 l4proto;
|
|
|
|
|
2009-02-01 16:45:17 +08:00
|
|
|
if (protocol == cpu_to_be16(ETH_P_IP)) {
|
2009-01-15 12:48:11 +08:00
|
|
|
l4proto = ip_hdr(skb)->protocol;
|
|
|
|
|
|
|
|
if (l4proto == IPPROTO_TCP)
|
|
|
|
opcode = TX_TCP_PKT;
|
|
|
|
else if(l4proto == IPPROTO_UDP)
|
|
|
|
opcode = TX_UDP_PKT;
|
2009-02-01 16:45:17 +08:00
|
|
|
} else if (protocol == cpu_to_be16(ETH_P_IPV6)) {
|
2009-01-15 12:48:11 +08:00
|
|
|
l4proto = ipv6_hdr(skb)->nexthdr;
|
|
|
|
|
|
|
|
if (l4proto == IPPROTO_TCP)
|
|
|
|
opcode = TX_TCPV6_PKT;
|
|
|
|
else if(l4proto == IPPROTO_UDP)
|
|
|
|
opcode = TX_UDPV6_PKT;
|
|
|
|
}
|
2008-07-22 10:44:09 +08:00
|
|
|
}
|
|
|
|
desc->tcp_hdr_offset = skb_transport_offset(skb);
|
|
|
|
desc->ip_hdr_offset = skb_network_offset(skb);
|
2009-01-27 04:34:57 +08:00
|
|
|
netxen_set_tx_flags_opcode(desc, flags, opcode);
|
2009-01-15 12:48:11 +08:00
|
|
|
return tso;
|
2008-07-22 10:44:09 +08:00
|
|
|
}
|
|
|
|
|
2009-01-15 12:50:00 +08:00
|
|
|
static void
|
|
|
|
netxen_clean_tx_dma_mapping(struct pci_dev *pdev,
|
|
|
|
struct netxen_cmd_buffer *pbuf, int last)
|
|
|
|
{
|
|
|
|
int k;
|
|
|
|
struct netxen_skb_frag *buffrag;
|
|
|
|
|
|
|
|
buffrag = &pbuf->frag_array[0];
|
|
|
|
pci_unmap_single(pdev, buffrag->dma,
|
|
|
|
buffrag->length, PCI_DMA_TODEVICE);
|
|
|
|
|
|
|
|
for (k = 1; k < last; k++) {
|
|
|
|
buffrag = &pbuf->frag_array[k];
|
|
|
|
pci_unmap_page(pdev, buffrag->dma,
|
|
|
|
buffrag->length, PCI_DMA_TODEVICE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-09 16:50:53 +08:00
|
|
|
static inline void
|
|
|
|
netxen_clear_cmddesc(u64 *desc)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
desc[i] = 0ULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
|
2006-10-22 03:33:03 +08:00
|
|
|
{
|
2007-04-20 22:52:37 +08:00
|
|
|
struct netxen_adapter *adapter = netdev_priv(netdev);
|
2006-10-22 03:33:03 +08:00
|
|
|
struct netxen_hardware_context *hw = &adapter->ahw;
|
|
|
|
unsigned int first_seg_len = skb->len - skb->data_len;
|
2009-01-15 12:48:11 +08:00
|
|
|
struct netxen_cmd_buffer *pbuf;
|
2006-10-22 03:33:03 +08:00
|
|
|
struct netxen_skb_frag *buffrag;
|
2009-01-15 12:48:11 +08:00
|
|
|
struct cmd_desc_type0 *hwdesc;
|
2009-01-15 12:50:00 +08:00
|
|
|
struct pci_dev *pdev = adapter->pdev;
|
|
|
|
dma_addr_t temp_dma;
|
2009-01-15 12:48:11 +08:00
|
|
|
int i, k;
|
2006-10-22 03:33:03 +08:00
|
|
|
|
2008-03-18 10:59:50 +08:00
|
|
|
u32 producer, consumer;
|
2009-01-15 12:48:11 +08:00
|
|
|
int frag_count, no_of_desc;
|
2008-03-18 10:59:50 +08:00
|
|
|
u32 num_txd = adapter->max_tx_desc_count;
|
2009-01-15 12:48:11 +08:00
|
|
|
bool is_tso = false;
|
2006-10-22 03:33:03 +08:00
|
|
|
|
|
|
|
frag_count = skb_shinfo(skb)->nr_frags + 1;
|
|
|
|
|
|
|
|
/* There 4 fragments per descriptor */
|
|
|
|
no_of_desc = (frag_count + 3) >> 2;
|
2007-12-27 02:23:58 +08:00
|
|
|
|
2008-03-18 10:59:50 +08:00
|
|
|
producer = adapter->cmd_producer;
|
|
|
|
smp_mb();
|
|
|
|
consumer = adapter->last_cmd_consumer;
|
|
|
|
if ((no_of_desc+2) > find_diff_among(producer, consumer, num_txd)) {
|
|
|
|
netif_stop_queue(netdev);
|
|
|
|
smp_mb();
|
|
|
|
return NETDEV_TX_BUSY;
|
2007-12-27 02:23:58 +08:00
|
|
|
}
|
2006-10-22 03:33:03 +08:00
|
|
|
|
|
|
|
/* Copy the descriptors into the hardware */
|
|
|
|
hwdesc = &hw->cmd_desc_head[producer];
|
2009-03-09 16:50:53 +08:00
|
|
|
netxen_clear_cmddesc((u64 *)hwdesc);
|
2006-10-22 03:33:03 +08:00
|
|
|
/* Take skb->data itself */
|
|
|
|
pbuf = &adapter->cmd_buf_arr[producer];
|
2009-01-15 12:48:11 +08:00
|
|
|
|
|
|
|
is_tso = netxen_tso_check(netdev, hwdesc, skb);
|
|
|
|
|
2006-10-22 03:33:03 +08:00
|
|
|
pbuf->skb = skb;
|
|
|
|
pbuf->frag_count = frag_count;
|
|
|
|
buffrag = &pbuf->frag_array[0];
|
2009-01-15 12:50:00 +08:00
|
|
|
temp_dma = pci_map_single(pdev, skb->data, first_seg_len,
|
2006-10-22 03:33:03 +08:00
|
|
|
PCI_DMA_TODEVICE);
|
2009-01-15 12:50:00 +08:00
|
|
|
if (pci_dma_mapping_error(pdev, temp_dma))
|
|
|
|
goto drop_packet;
|
|
|
|
|
|
|
|
buffrag->dma = temp_dma;
|
2006-10-22 03:33:03 +08:00
|
|
|
buffrag->length = first_seg_len;
|
2009-01-15 12:48:11 +08:00
|
|
|
netxen_set_tx_frags_len(hwdesc, frag_count, skb->len);
|
|
|
|
netxen_set_tx_port(hwdesc, adapter->portnum);
|
2006-10-22 03:33:03 +08:00
|
|
|
|
2009-03-09 16:50:53 +08:00
|
|
|
hwdesc->buffer_length[0] = cpu_to_le16(first_seg_len);
|
2006-10-22 03:33:03 +08:00
|
|
|
hwdesc->addr_buffer1 = cpu_to_le64(buffrag->dma);
|
|
|
|
|
|
|
|
for (i = 1, k = 1; i < frag_count; i++, k++) {
|
|
|
|
struct skb_frag_struct *frag;
|
|
|
|
int len, temp_len;
|
|
|
|
unsigned long offset;
|
|
|
|
|
|
|
|
/* move to next desc. if there is a need */
|
|
|
|
if ((i & 0x3) == 0) {
|
|
|
|
k = 0;
|
2008-03-18 10:59:50 +08:00
|
|
|
producer = get_next_index(producer, num_txd);
|
2006-10-22 03:33:03 +08:00
|
|
|
hwdesc = &hw->cmd_desc_head[producer];
|
2009-03-09 16:50:53 +08:00
|
|
|
netxen_clear_cmddesc((u64 *)hwdesc);
|
2007-12-27 02:23:58 +08:00
|
|
|
pbuf = &adapter->cmd_buf_arr[producer];
|
|
|
|
pbuf->skb = NULL;
|
2006-10-22 03:33:03 +08:00
|
|
|
}
|
|
|
|
frag = &skb_shinfo(skb)->frags[i - 1];
|
|
|
|
len = frag->size;
|
|
|
|
offset = frag->page_offset;
|
|
|
|
|
|
|
|
temp_len = len;
|
2009-01-15 12:50:00 +08:00
|
|
|
temp_dma = pci_map_page(pdev, frag->page, offset,
|
2006-10-22 03:33:03 +08:00
|
|
|
len, PCI_DMA_TODEVICE);
|
2009-01-15 12:50:00 +08:00
|
|
|
if (pci_dma_mapping_error(pdev, temp_dma)) {
|
|
|
|
netxen_clean_tx_dma_mapping(pdev, pbuf, i);
|
|
|
|
goto drop_packet;
|
|
|
|
}
|
2006-10-22 03:33:03 +08:00
|
|
|
|
|
|
|
buffrag++;
|
|
|
|
buffrag->dma = temp_dma;
|
|
|
|
buffrag->length = temp_len;
|
|
|
|
|
2009-03-09 16:50:53 +08:00
|
|
|
hwdesc->buffer_length[k] = cpu_to_le16(temp_len);
|
2006-10-22 03:33:03 +08:00
|
|
|
switch (k) {
|
|
|
|
case 0:
|
|
|
|
hwdesc->addr_buffer1 = cpu_to_le64(temp_dma);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
hwdesc->addr_buffer2 = cpu_to_le64(temp_dma);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
hwdesc->addr_buffer3 = cpu_to_le64(temp_dma);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
hwdesc->addr_buffer4 = cpu_to_le64(temp_dma);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
frag++;
|
|
|
|
}
|
2008-03-18 10:59:50 +08:00
|
|
|
producer = get_next_index(producer, num_txd);
|
2006-10-22 03:33:03 +08:00
|
|
|
|
|
|
|
/* For LSO, we need to copy the MAC/IP/TCP headers into
|
|
|
|
* the descriptor ring
|
|
|
|
*/
|
2009-01-15 12:48:11 +08:00
|
|
|
if (is_tso) {
|
2006-10-22 03:33:03 +08:00
|
|
|
int hdr_len, first_hdr_len, more_hdr;
|
2009-01-15 12:48:11 +08:00
|
|
|
hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb);
|
2006-12-05 01:23:25 +08:00
|
|
|
if (hdr_len > (sizeof(struct cmd_desc_type0) - 2)) {
|
|
|
|
first_hdr_len = sizeof(struct cmd_desc_type0) - 2;
|
2006-10-22 03:33:03 +08:00
|
|
|
more_hdr = 1;
|
|
|
|
} else {
|
|
|
|
first_hdr_len = hdr_len;
|
|
|
|
more_hdr = 0;
|
|
|
|
}
|
|
|
|
/* copy the MAC/IP/TCP headers to the cmd descriptor list */
|
|
|
|
hwdesc = &hw->cmd_desc_head[producer];
|
2007-12-27 02:23:58 +08:00
|
|
|
pbuf = &adapter->cmd_buf_arr[producer];
|
|
|
|
pbuf->skb = NULL;
|
2006-10-22 03:33:03 +08:00
|
|
|
|
|
|
|
/* copy the first 64 bytes */
|
2006-12-05 01:23:25 +08:00
|
|
|
memcpy(((void *)hwdesc) + 2,
|
2006-10-22 03:33:03 +08:00
|
|
|
(void *)(skb->data), first_hdr_len);
|
2008-03-18 10:59:50 +08:00
|
|
|
producer = get_next_index(producer, num_txd);
|
2006-10-22 03:33:03 +08:00
|
|
|
|
|
|
|
if (more_hdr) {
|
|
|
|
hwdesc = &hw->cmd_desc_head[producer];
|
2007-12-27 02:23:58 +08:00
|
|
|
pbuf = &adapter->cmd_buf_arr[producer];
|
|
|
|
pbuf->skb = NULL;
|
2006-10-22 03:33:03 +08:00
|
|
|
/* copy the next 64 bytes - should be enough except
|
|
|
|
* for pathological case
|
|
|
|
*/
|
2007-03-28 05:55:52 +08:00
|
|
|
skb_copy_from_linear_data_offset(skb, first_hdr_len,
|
|
|
|
hwdesc,
|
|
|
|
(hdr_len -
|
|
|
|
first_hdr_len));
|
2008-03-18 10:59:50 +08:00
|
|
|
producer = get_next_index(producer, num_txd);
|
2006-10-22 03:33:03 +08:00
|
|
|
}
|
|
|
|
}
|
2007-04-20 22:55:26 +08:00
|
|
|
|
2008-03-18 10:59:50 +08:00
|
|
|
adapter->cmd_producer = producer;
|
2008-01-01 02:08:57 +08:00
|
|
|
adapter->stats.txbytes += skb->len;
|
2007-04-20 22:55:26 +08:00
|
|
|
|
2008-03-18 10:59:50 +08:00
|
|
|
netxen_nic_update_cmd_producer(adapter, adapter->cmd_producer);
|
2006-10-22 03:33:03 +08:00
|
|
|
|
2008-03-18 10:59:50 +08:00
|
|
|
adapter->stats.xmitcalled++;
|
2006-10-22 03:33:03 +08:00
|
|
|
netdev->trans_start = jiffies;
|
|
|
|
|
|
|
|
return NETDEV_TX_OK;
|
2009-01-15 12:50:00 +08:00
|
|
|
|
|
|
|
drop_packet:
|
|
|
|
adapter->stats.txdropped++;
|
|
|
|
dev_kfree_skb_any(skb);
|
|
|
|
return NETDEV_TX_OK;
|
2006-10-22 03:33:03 +08:00
|
|
|
}
|
|
|
|
|
2008-07-22 10:44:05 +08:00
|
|
|
static int netxen_nic_check_temp(struct netxen_adapter *adapter)
|
|
|
|
{
|
|
|
|
struct net_device *netdev = adapter->netdev;
|
|
|
|
uint32_t temp, temp_state, temp_val;
|
|
|
|
int rv = 0;
|
|
|
|
|
|
|
|
temp = adapter->pci_read_normalize(adapter, CRB_TEMP_STATE);
|
|
|
|
|
|
|
|
temp_state = nx_get_temp_state(temp);
|
|
|
|
temp_val = nx_get_temp_val(temp);
|
|
|
|
|
|
|
|
if (temp_state == NX_TEMP_PANIC) {
|
|
|
|
printk(KERN_ALERT
|
|
|
|
"%s: Device temperature %d degrees C exceeds"
|
|
|
|
" maximum allowed. Hardware has been shut down.\n",
|
|
|
|
netxen_nic_driver_name, temp_val);
|
|
|
|
|
|
|
|
netif_carrier_off(netdev);
|
|
|
|
netif_stop_queue(netdev);
|
|
|
|
rv = 1;
|
|
|
|
} else if (temp_state == NX_TEMP_WARN) {
|
|
|
|
if (adapter->temp == NX_TEMP_NORMAL) {
|
|
|
|
printk(KERN_ALERT
|
|
|
|
"%s: Device temperature %d degrees C "
|
|
|
|
"exceeds operating range."
|
|
|
|
" Immediate action needed.\n",
|
|
|
|
netxen_nic_driver_name, temp_val);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (adapter->temp == NX_TEMP_WARN) {
|
|
|
|
printk(KERN_INFO
|
|
|
|
"%s: Device temperature is now %d degrees C"
|
|
|
|
" in normal range.\n", netxen_nic_driver_name,
|
|
|
|
temp_val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
adapter->temp = temp_state;
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void netxen_nic_handle_phy_intr(struct netxen_adapter *adapter)
|
|
|
|
{
|
|
|
|
struct net_device *netdev = adapter->netdev;
|
|
|
|
u32 val, port, linkup;
|
|
|
|
|
|
|
|
port = adapter->physical_port;
|
|
|
|
|
2008-08-01 18:14:55 +08:00
|
|
|
if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) {
|
|
|
|
val = adapter->pci_read_normalize(adapter, CRB_XG_STATE_P3);
|
|
|
|
val = XG_LINK_STATE_P3(adapter->ahw.pci_func, val);
|
|
|
|
linkup = (val == XG_LINK_UP_P3);
|
2008-07-22 10:44:07 +08:00
|
|
|
} else {
|
2008-08-01 18:14:55 +08:00
|
|
|
val = adapter->pci_read_normalize(adapter, CRB_XG_STATE);
|
2009-03-09 16:50:56 +08:00
|
|
|
if (adapter->ahw.port_type == NETXEN_NIC_GBE)
|
2008-08-01 18:14:55 +08:00
|
|
|
linkup = (val >> port) & 1;
|
|
|
|
else {
|
2008-07-22 10:44:07 +08:00
|
|
|
val = (val >> port*8) & 0xff;
|
|
|
|
linkup = (val == XG_LINK_UP);
|
|
|
|
}
|
2008-07-22 10:44:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (adapter->ahw.linkup && !linkup) {
|
|
|
|
printk(KERN_INFO "%s: %s NIC Link is down\n",
|
|
|
|
netxen_nic_driver_name, netdev->name);
|
|
|
|
adapter->ahw.linkup = 0;
|
|
|
|
if (netif_running(netdev)) {
|
|
|
|
netif_carrier_off(netdev);
|
|
|
|
netif_stop_queue(netdev);
|
|
|
|
}
|
2009-01-15 12:48:32 +08:00
|
|
|
|
|
|
|
netxen_nic_set_link_parameters(adapter);
|
2008-07-22 10:44:05 +08:00
|
|
|
} else if (!adapter->ahw.linkup && linkup) {
|
|
|
|
printk(KERN_INFO "%s: %s NIC Link is up\n",
|
|
|
|
netxen_nic_driver_name, netdev->name);
|
|
|
|
adapter->ahw.linkup = 1;
|
|
|
|
if (netif_running(netdev)) {
|
|
|
|
netif_carrier_on(netdev);
|
|
|
|
netif_wake_queue(netdev);
|
|
|
|
}
|
2009-01-15 12:48:32 +08:00
|
|
|
|
|
|
|
netxen_nic_set_link_parameters(adapter);
|
2008-07-22 10:44:05 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-10-22 03:33:03 +08:00
|
|
|
static void netxen_watchdog(unsigned long v)
|
|
|
|
{
|
|
|
|
struct netxen_adapter *adapter = (struct netxen_adapter *)v;
|
2006-12-05 01:23:25 +08:00
|
|
|
|
|
|
|
SCHEDULE_WORK(&adapter->watchdog_task);
|
2006-10-22 03:33:03 +08:00
|
|
|
}
|
|
|
|
|
2008-07-22 10:44:05 +08:00
|
|
|
void netxen_watchdog_task(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct netxen_adapter *adapter =
|
|
|
|
container_of(work, struct netxen_adapter, watchdog_task);
|
|
|
|
|
|
|
|
if ((adapter->portnum == 0) && netxen_nic_check_temp(adapter))
|
|
|
|
return;
|
|
|
|
|
|
|
|
netxen_nic_handle_phy_intr(adapter);
|
|
|
|
|
2008-08-08 15:08:41 +08:00
|
|
|
if (netif_running(adapter->netdev))
|
|
|
|
mod_timer(&adapter->watchdog_timer, jiffies + 2 * HZ);
|
2008-07-22 10:44:05 +08:00
|
|
|
}
|
|
|
|
|
2006-10-22 03:33:03 +08:00
|
|
|
static void netxen_tx_timeout(struct net_device *netdev)
|
|
|
|
{
|
2007-04-20 22:52:37 +08:00
|
|
|
struct netxen_adapter *adapter = (struct netxen_adapter *)
|
|
|
|
netdev_priv(netdev);
|
|
|
|
SCHEDULE_WORK(&adapter->tx_timeout_task);
|
2006-10-22 03:33:03 +08:00
|
|
|
}
|
|
|
|
|
2006-12-06 03:36:26 +08:00
|
|
|
static void netxen_tx_timeout_task(struct work_struct *work)
|
2006-10-22 03:33:03 +08:00
|
|
|
{
|
2007-11-24 10:23:36 +08:00
|
|
|
struct netxen_adapter *adapter =
|
2007-04-20 22:52:37 +08:00
|
|
|
container_of(work, struct netxen_adapter, tx_timeout_task);
|
2006-10-22 03:33:03 +08:00
|
|
|
|
|
|
|
printk(KERN_ERR "%s %s: transmit timeout, resetting.\n",
|
2007-04-20 22:55:26 +08:00
|
|
|
netxen_nic_driver_name, adapter->netdev->name);
|
2006-10-22 03:33:03 +08:00
|
|
|
|
2008-03-18 10:59:50 +08:00
|
|
|
netxen_nic_disable_int(adapter);
|
|
|
|
napi_disable(&adapter->napi);
|
|
|
|
|
2007-04-20 22:55:26 +08:00
|
|
|
adapter->netdev->trans_start = jiffies;
|
2008-03-18 10:59:50 +08:00
|
|
|
|
|
|
|
napi_enable(&adapter->napi);
|
|
|
|
netxen_nic_enable_int(adapter);
|
2007-04-20 22:55:26 +08:00
|
|
|
netif_wake_queue(adapter->netdev);
|
2006-10-22 03:33:03 +08:00
|
|
|
}
|
|
|
|
|
2008-07-22 10:44:05 +08:00
|
|
|
/*
|
|
|
|
* netxen_nic_get_stats - Get System Network Statistics
|
|
|
|
* @netdev: network interface device structure
|
|
|
|
*/
|
|
|
|
struct net_device_stats *netxen_nic_get_stats(struct net_device *netdev)
|
|
|
|
{
|
|
|
|
struct netxen_adapter *adapter = netdev_priv(netdev);
|
|
|
|
struct net_device_stats *stats = &adapter->net_stats;
|
|
|
|
|
|
|
|
memset(stats, 0, sizeof(*stats));
|
|
|
|
|
|
|
|
/* total packets received */
|
|
|
|
stats->rx_packets = adapter->stats.no_rcv;
|
|
|
|
/* total packets transmitted */
|
|
|
|
stats->tx_packets = adapter->stats.xmitedframes +
|
|
|
|
adapter->stats.xmitfinished;
|
|
|
|
/* total bytes received */
|
|
|
|
stats->rx_bytes = adapter->stats.rxbytes;
|
|
|
|
/* total bytes transmitted */
|
|
|
|
stats->tx_bytes = adapter->stats.txbytes;
|
|
|
|
/* bad packets received */
|
|
|
|
stats->rx_errors = adapter->stats.rcvdbadskb;
|
|
|
|
/* packet transmit problems */
|
|
|
|
stats->tx_errors = adapter->stats.nocmddescriptor;
|
|
|
|
/* no space in linux buffers */
|
|
|
|
stats->rx_dropped = adapter->stats.rxdropped;
|
|
|
|
/* no space available in linux */
|
|
|
|
stats->tx_dropped = adapter->stats.txdropped;
|
|
|
|
|
|
|
|
return stats;
|
|
|
|
}
|
|
|
|
|
2008-07-22 10:44:02 +08:00
|
|
|
static irqreturn_t netxen_intr(int irq, void *data)
|
2006-10-22 03:33:03 +08:00
|
|
|
{
|
2007-10-19 15:14:03 +08:00
|
|
|
struct netxen_adapter *adapter = data;
|
2008-08-01 18:14:57 +08:00
|
|
|
u32 status = 0;
|
|
|
|
|
|
|
|
status = adapter->pci_read_immediate(adapter, ISR_INT_VECTOR);
|
|
|
|
|
|
|
|
if (!(status & adapter->legacy_intr.int_vec_bit))
|
2008-03-18 10:59:49 +08:00
|
|
|
return IRQ_NONE;
|
2007-07-25 22:43:12 +08:00
|
|
|
|
2008-08-01 18:14:57 +08:00
|
|
|
if (adapter->ahw.revision_id >= NX_P3_B1) {
|
|
|
|
/* check interrupt state machine, to be sure */
|
|
|
|
status = adapter->pci_read_immediate(adapter,
|
|
|
|
ISR_INT_STATE_REG);
|
|
|
|
if (!ISR_LEGACY_INT_TRIGGERED(status))
|
|
|
|
return IRQ_NONE;
|
|
|
|
|
2008-08-08 15:08:43 +08:00
|
|
|
} else {
|
|
|
|
unsigned long our_int = 0;
|
2008-08-01 18:14:57 +08:00
|
|
|
|
|
|
|
our_int = adapter->pci_read_normalize(adapter, CRB_INT_VECTOR);
|
2008-08-08 15:08:43 +08:00
|
|
|
|
2008-08-01 18:14:57 +08:00
|
|
|
/* not our interrupt */
|
2008-08-08 15:08:43 +08:00
|
|
|
if (!test_and_clear_bit((7 + adapter->portnum), &our_int))
|
2008-08-01 18:14:57 +08:00
|
|
|
return IRQ_NONE;
|
|
|
|
|
2008-08-08 15:08:43 +08:00
|
|
|
/* claim interrupt */
|
|
|
|
adapter->pci_write_normalize(adapter,
|
|
|
|
CRB_INT_VECTOR, (our_int & 0xffffffff));
|
2007-07-25 22:43:12 +08:00
|
|
|
}
|
|
|
|
|
2008-08-08 15:08:43 +08:00
|
|
|
/* clear interrupt */
|
|
|
|
if (adapter->fw_major < 4)
|
|
|
|
netxen_nic_disable_int(adapter);
|
|
|
|
|
|
|
|
adapter->pci_write_immediate(adapter,
|
|
|
|
adapter->legacy_intr.tgt_status_reg,
|
|
|
|
0xffffffff);
|
|
|
|
/* read twice to ensure write is flushed */
|
|
|
|
adapter->pci_read_immediate(adapter, ISR_INT_VECTOR);
|
|
|
|
adapter->pci_read_immediate(adapter, ISR_INT_VECTOR);
|
|
|
|
|
|
|
|
napi_schedule(&adapter->napi);
|
2006-10-22 03:33:03 +08:00
|
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
2008-07-22 10:44:02 +08:00
|
|
|
static irqreturn_t netxen_msi_intr(int irq, void *data)
|
2008-03-18 10:59:49 +08:00
|
|
|
{
|
|
|
|
struct netxen_adapter *adapter = data;
|
|
|
|
|
2008-08-08 15:08:43 +08:00
|
|
|
/* clear interrupt */
|
|
|
|
adapter->pci_write_immediate(adapter,
|
|
|
|
msi_tgt_status[adapter->ahw.pci_func], 0xffffffff);
|
|
|
|
|
|
|
|
napi_schedule(&adapter->napi);
|
2008-03-18 10:59:49 +08:00
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
2009-02-09 11:20:19 +08:00
|
|
|
static irqreturn_t netxen_msix_intr(int irq, void *data)
|
|
|
|
{
|
|
|
|
struct netxen_adapter *adapter = data;
|
|
|
|
|
|
|
|
napi_schedule(&adapter->napi);
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
[NET]: Make NAPI polling independent of struct net_device objects.
Several devices have multiple independant RX queues per net
device, and some have a single interrupt doorbell for several
queues.
In either case, it's easier to support layouts like that if the
structure representing the poll is independant from the net
device itself.
The signature of the ->poll() call back goes from:
int foo_poll(struct net_device *dev, int *budget)
to
int foo_poll(struct napi_struct *napi, int budget)
The caller is returned the number of RX packets processed (or
the number of "NAPI credits" consumed if you want to get
abstract). The callee no longer messes around bumping
dev->quota, *budget, etc. because that is all handled in the
caller upon return.
The napi_struct is to be embedded in the device driver private data
structures.
Furthermore, it is the driver's responsibility to disable all NAPI
instances in it's ->stop() device close handler. Since the
napi_struct is privatized into the driver's private data structures,
only the driver knows how to get at all of the napi_struct instances
it may have per-device.
With lots of help and suggestions from Rusty Russell, Roland Dreier,
Michael Chan, Jeff Garzik, and Jamal Hadi Salim.
Bug fixes from Thomas Graf, Roland Dreier, Peter Zijlstra,
Joseph Fannin, Scott Wood, Hans J. Koch, and Michael Chan.
[ Ported to current tree and all drivers converted. Integrated
Stephen's follow-on kerneldoc additions, and restored poll_list
handling to the old style to fix mutual exclusion issues. -DaveM ]
Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-10-04 07:41:36 +08:00
|
|
|
static int netxen_nic_poll(struct napi_struct *napi, int budget)
|
2006-10-22 03:33:03 +08:00
|
|
|
{
|
2009-03-09 16:50:55 +08:00
|
|
|
struct netxen_adapter *adapter =
|
|
|
|
container_of(napi, struct netxen_adapter, napi);
|
2008-03-18 10:59:49 +08:00
|
|
|
int tx_complete;
|
[NET]: Make NAPI polling independent of struct net_device objects.
Several devices have multiple independant RX queues per net
device, and some have a single interrupt doorbell for several
queues.
In either case, it's easier to support layouts like that if the
structure representing the poll is independant from the net
device itself.
The signature of the ->poll() call back goes from:
int foo_poll(struct net_device *dev, int *budget)
to
int foo_poll(struct napi_struct *napi, int budget)
The caller is returned the number of RX packets processed (or
the number of "NAPI credits" consumed if you want to get
abstract). The callee no longer messes around bumping
dev->quota, *budget, etc. because that is all handled in the
caller upon return.
The napi_struct is to be embedded in the device driver private data
structures.
Furthermore, it is the driver's responsibility to disable all NAPI
instances in it's ->stop() device close handler. Since the
napi_struct is privatized into the driver's private data structures,
only the driver knows how to get at all of the napi_struct instances
it may have per-device.
With lots of help and suggestions from Rusty Russell, Roland Dreier,
Michael Chan, Jeff Garzik, and Jamal Hadi Salim.
Bug fixes from Thomas Graf, Roland Dreier, Peter Zijlstra,
Joseph Fannin, Scott Wood, Hans J. Koch, and Michael Chan.
[ Ported to current tree and all drivers converted. Integrated
Stephen's follow-on kerneldoc additions, and restored poll_list
handling to the old style to fix mutual exclusion issues. -DaveM ]
Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-10-04 07:41:36 +08:00
|
|
|
int work_done;
|
2006-10-22 03:33:03 +08:00
|
|
|
|
2008-03-18 10:59:49 +08:00
|
|
|
tx_complete = netxen_process_cmd_ring(adapter);
|
2006-10-22 03:33:03 +08:00
|
|
|
|
2009-03-09 16:50:55 +08:00
|
|
|
work_done = netxen_process_rcv_ring(adapter, budget);
|
2006-10-22 03:33:03 +08:00
|
|
|
|
2008-03-18 10:59:49 +08:00
|
|
|
if ((work_done < budget) && tx_complete) {
|
2009-01-20 08:43:59 +08:00
|
|
|
napi_complete(&adapter->napi);
|
2006-10-22 03:33:03 +08:00
|
|
|
netxen_nic_enable_int(adapter);
|
|
|
|
}
|
|
|
|
|
[NET]: Make NAPI polling independent of struct net_device objects.
Several devices have multiple independant RX queues per net
device, and some have a single interrupt doorbell for several
queues.
In either case, it's easier to support layouts like that if the
structure representing the poll is independant from the net
device itself.
The signature of the ->poll() call back goes from:
int foo_poll(struct net_device *dev, int *budget)
to
int foo_poll(struct napi_struct *napi, int budget)
The caller is returned the number of RX packets processed (or
the number of "NAPI credits" consumed if you want to get
abstract). The callee no longer messes around bumping
dev->quota, *budget, etc. because that is all handled in the
caller upon return.
The napi_struct is to be embedded in the device driver private data
structures.
Furthermore, it is the driver's responsibility to disable all NAPI
instances in it's ->stop() device close handler. Since the
napi_struct is privatized into the driver's private data structures,
only the driver knows how to get at all of the napi_struct instances
it may have per-device.
With lots of help and suggestions from Rusty Russell, Roland Dreier,
Michael Chan, Jeff Garzik, and Jamal Hadi Salim.
Bug fixes from Thomas Graf, Roland Dreier, Peter Zijlstra,
Joseph Fannin, Scott Wood, Hans J. Koch, and Michael Chan.
[ Ported to current tree and all drivers converted. Integrated
Stephen's follow-on kerneldoc additions, and restored poll_list
handling to the old style to fix mutual exclusion issues. -DaveM ]
Signed-off-by: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-10-04 07:41:36 +08:00
|
|
|
return work_done;
|
2006-10-22 03:33:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
|
|
static void netxen_nic_poll_controller(struct net_device *netdev)
|
|
|
|
{
|
2007-04-20 22:52:37 +08:00
|
|
|
struct netxen_adapter *adapter = netdev_priv(netdev);
|
2006-10-22 03:33:03 +08:00
|
|
|
disable_irq(adapter->irq);
|
2006-11-07 18:12:16 +08:00
|
|
|
netxen_intr(adapter->irq, adapter);
|
2006-10-22 03:33:03 +08:00
|
|
|
enable_irq(adapter->irq);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static struct pci_driver netxen_driver = {
|
|
|
|
.name = netxen_nic_driver_name,
|
|
|
|
.id_table = netxen_pci_tbl,
|
|
|
|
.probe = netxen_nic_probe,
|
2009-03-13 22:52:02 +08:00
|
|
|
.remove = __devexit_p(netxen_nic_remove),
|
|
|
|
.suspend = netxen_nic_suspend,
|
|
|
|
.resume = netxen_nic_resume
|
2006-10-22 03:33:03 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Driver Registration on NetXen card */
|
|
|
|
|
|
|
|
static int __init netxen_init_module(void)
|
|
|
|
{
|
2008-01-24 18:06:46 +08:00
|
|
|
if ((netxen_workq = create_singlethread_workqueue("netxen")) == NULL)
|
2006-12-05 01:23:25 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2007-01-22 22:52:53 +08:00
|
|
|
return pci_register_driver(&netxen_driver);
|
2006-10-22 03:33:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
module_init(netxen_init_module);
|
|
|
|
|
|
|
|
static void __exit netxen_exit_module(void)
|
|
|
|
{
|
|
|
|
pci_unregister_driver(&netxen_driver);
|
2007-02-21 22:37:06 +08:00
|
|
|
destroy_workqueue(netxen_workq);
|
2006-10-22 03:33:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
module_exit(netxen_exit_module);
|