Newer
Older
/*
* Copyright (C) 2003 - 2006 NetXen, Inc.
* All rights reserved.
* 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.
* 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., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
* The full GNU General Public License is included in this distribution
* in the file called LICENSE.
* Contact Information:
* info@netxen.com
* NetXen,
* 3965 Freedom Circle, Fourth floor,
* Santa Clara, CA 95054
*
*
* Main source file for NetXen NIC Driver on Linux
*
*/
Amit S. Kale
committed
#include <linux/vmalloc.h>
#include <linux/highmem.h>
#include "netxen_nic_hw.h"
#include "netxen_nic.h"
#include "netxen_nic_phan_reg.h"
#include <linux/dma-mapping.h>
#include <linux/vmalloc.h>
MODULE_DESCRIPTION("NetXen Multi port (1/10) Gigabit Network Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(NETXEN_NIC_LINUX_VERSIONID);
char netxen_nic_driver_name[] = "netxen-nic";
static char netxen_nic_driver_string[] = "NetXen Network Driver version "
Amit S. Kale
committed
NETXEN_NIC_LINUX_VERSIONID;
#define NETXEN_NETDEV_WEIGHT 120
#define NETXEN_ADAPTER_UP_MAGIC 777
Amit S. Kale
committed
#define NETXEN_NIC_PEG_TUNE 0
u8 nx_p2_id = NX_P2_C0;
#define DMA_32BIT_MASK 0x00000000ffffffffULL
#define DMA_35BIT_MASK 0x00000007ffffffffULL
/* Local functions to NetXen NIC driver */
static int __devinit netxen_nic_probe(struct pci_dev *pdev,
const struct pci_device_id *ent);
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);
static void netxen_tx_timeout_task(struct work_struct *work);
static void netxen_watchdog(unsigned long);
static int netxen_handle_int(struct netxen_adapter *, struct net_device *);
static int netxen_nic_poll(struct net_device *dev, int *budget);
#ifdef CONFIG_NET_POLL_CONTROLLER
static void netxen_nic_poll_controller(struct net_device *netdev);
#endif
static irqreturn_t netxen_intr(int irq, void *data);
int physical_port[] = {0, 1, 2, 3};
/* PCI Device ID Table */
static struct pci_device_id netxen_pci_tbl[] __devinitdata = {
{PCI_DEVICE(0x4040, 0x0001)},
{PCI_DEVICE(0x4040, 0x0002)},
{PCI_DEVICE(0x4040, 0x0003)},
{PCI_DEVICE(0x4040, 0x0004)},
{PCI_DEVICE(0x4040, 0x0005)},
{PCI_DEVICE(0x4040, 0x0024)},
{PCI_DEVICE(0x4040, 0x0025)},
{0,}
};
MODULE_DEVICE_TABLE(pci, netxen_pci_tbl);
struct workqueue_struct *netxen_workq;
static void netxen_watchdog(unsigned long);
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
static inline void netxen_nic_update_cmd_producer(struct netxen_adapter *adapter,
uint32_t crb_producer)
{
switch (adapter->portnum) {
case 0:
writel(crb_producer, NETXEN_CRB_NORMALIZE
(adapter, CRB_CMD_PRODUCER_OFFSET));
return;
case 1:
writel(crb_producer, NETXEN_CRB_NORMALIZE
(adapter, CRB_CMD_PRODUCER_OFFSET_1));
return;
case 2:
writel(crb_producer, NETXEN_CRB_NORMALIZE
(adapter, CRB_CMD_PRODUCER_OFFSET_2));
return;
case 3:
writel(crb_producer, NETXEN_CRB_NORMALIZE
(adapter, CRB_CMD_PRODUCER_OFFSET_3));
return;
default:
printk(KERN_WARNING "We tried to update "
"CRB_CMD_PRODUCER_OFFSET for invalid "
"PCI function id %d\n",
adapter->portnum);
return;
}
}
static inline void netxen_nic_update_cmd_consumer(struct netxen_adapter *adapter,
u32 crb_consumer)
{
switch (adapter->portnum) {
case 0:
writel(crb_consumer, NETXEN_CRB_NORMALIZE
(adapter, CRB_CMD_CONSUMER_OFFSET));
return;
case 1:
writel(crb_consumer, NETXEN_CRB_NORMALIZE
(adapter, CRB_CMD_CONSUMER_OFFSET_1));
return;
case 2:
writel(crb_consumer, NETXEN_CRB_NORMALIZE
(adapter, CRB_CMD_CONSUMER_OFFSET_2));
return;
case 3:
writel(crb_consumer, NETXEN_CRB_NORMALIZE
(adapter, CRB_CMD_CONSUMER_OFFSET_3));
return;
default:
printk(KERN_WARNING "We tried to update "
"CRB_CMD_PRODUCER_OFFSET for invalid "
"PCI function id %d\n",
adapter->portnum);
return;
}
}
#define ADAPTER_LIST_SIZE 12
int netxen_cards_found;
/*
* netxen_nic_probe()
*
* The Linux system will invoke this after identifying the vendor ID and
* device Id in the pci_tbl supported by this module.
*
* A quad port card has one operational PCI config space, (function 0),
* which is used to access all four ports.
*
* This routine will initialize the adapter, and setup the global parameters
* along with the port's specific structure.
*/
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;
void __iomem *mem_ptr0 = NULL;
void __iomem *mem_ptr1 = NULL;
void __iomem *mem_ptr2 = NULL;
unsigned long first_page_group_end;
unsigned long first_page_group_start;
Amit S. Kale
committed
unsigned long mem_base, mem_len, db_base, db_len;
int ring;
struct netxen_recv_context *recv_ctx = NULL;
struct netxen_rcv_desc_ctx *rcv_desc = NULL;
struct netxen_cmd_buffer *cmd_buf_arr = NULL;
u64 mac_addr[FLASH_NUM_PORTS + 1];
int valid_mac = 0;
u32 val;
int pci_func_id = PCI_FUNC(pdev->devfn);
Amit S. Kale
committed
printk(KERN_INFO "%s \n", netxen_nic_driver_string);
if (pdev->class != 0x020000) {
printk(KERN_ERR"NetXen function %d, class %x will not"
"be enabled.\n",pci_func_id, pdev->class);
return -ENODEV;
}
if ((err = pci_enable_device(pdev)))
return err;
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);
pci_read_config_byte(pdev, PCI_REVISION_ID, &nx_p2_id);
if (nx_p2_id == NX_P2_C1 &&
(pci_set_dma_mask(pdev, DMA_35BIT_MASK) == 0) &&
(pci_set_consistent_dma_mask(pdev, DMA_35BIT_MASK) == 0)) {
if ((err = pci_set_dma_mask(pdev, DMA_32BIT_MASK)) ||
(err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK)))
goto err_out_free_res;
pci_using_dac = 0;
}
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_MODULE_OWNER(netdev);
SET_NETDEV_DEV(netdev, &pdev->dev);
adapter = netdev->priv;
memset(adapter, 0 , sizeof(struct netxen_adapter));
adapter->ahw.pdev = pdev;
adapter->ahw.pci_func = pci_func_id;
spin_lock_init(&adapter->tx_lock);
spin_lock_init(&adapter->lock);
/* remap phys address */
mem_base = pci_resource_start(pdev, 0); /* 0 is for BAR 0 */
mem_len = pci_resource_len(pdev, 0);
/* 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);
first_page_group_start = FIRST_PAGE_GROUP_START;
first_page_group_end = FIRST_PAGE_GROUP_END;
} 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);
first_page_group_start = 0;
first_page_group_end = 0;
} else {
err = -EIO;
goto err_out_free_netdev;
}
Amit S. Kale
committed
if (((mem_ptr0 == 0UL) && (mem_len == NETXEN_PCI_128MB_SIZE)) ||
(mem_ptr1 == 0UL) || (mem_ptr2 == 0UL)) {
Amit S. Kale
committed
"Cannot remap adapter memory aborting.:"
"0 -> %p, 1 -> %p, 2 -> %p\n",
mem_ptr0, mem_ptr1, mem_ptr2);
goto err_out_iounmap;
}
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_iounmap;
}
DPRINTK(INFO, "doorbell ioremap from %lx a size of %lx\n", db_base,
db_len);
db_ptr = ioremap(db_base, NETXEN_DB_MAPSIZE_BYTES);
printk(KERN_ERR "%s: Failed to allocate doorbell map.",
netxen_nic_driver_name);
err = -EIO;
goto err_out_iounmap;
DPRINTK(INFO, "doorbell ioremaped at %p\n", db_ptr);
adapter->ahw.pci_base0 = mem_ptr0;
adapter->ahw.first_page_group_start = first_page_group_start;
adapter->ahw.first_page_group_end = first_page_group_end;
adapter->ahw.pci_base1 = mem_ptr1;
adapter->ahw.pci_base2 = mem_ptr2;
adapter->ahw.db_base = db_ptr;
adapter->ahw.db_len = db_len;
adapter->netdev = netdev;
adapter->pdev = pdev;
dhananjay.phadke@gmail.com
committed
/* this will be read from FW later */
adapter->intr_scheme = -1;
/* This will be reset for mezz cards */
dhananjay.phadke@gmail.com
committed
adapter->status &= ~NETXEN_NETDEV_STATUS;
netdev->open = netxen_nic_open;
netdev->stop = netxen_nic_close;
netdev->hard_start_xmit = netxen_nic_xmit_frame;
netdev->get_stats = netxen_nic_get_stats;
netdev->set_multicast_list = netxen_nic_set_multi;
netdev->set_mac_address = netxen_nic_set_mac;
netdev->change_mtu = netxen_nic_change_mtu;
netdev->tx_timeout = netxen_tx_timeout;
netdev->watchdog_timeo = HZ;
netxen_nic_change_mtu(netdev, netdev->mtu);
SET_ETHTOOL_OPS(netdev, &netxen_nic_ethtool_ops);
netdev->poll = netxen_nic_poll;
netdev->weight = NETXEN_NETDEV_WEIGHT;
#ifdef CONFIG_NET_POLL_CONTROLLER
netdev->poll_controller = netxen_nic_poll_controller;
#endif
/* ScatterGather support */
netdev->features = NETIF_F_SG;
netdev->features |= NETIF_F_IP_CSUM;
netdev->features |= NETIF_F_TSO;
if (pci_using_dac)
netdev->features |= NETIF_F_HIGHDMA;
dhananjay.phadke@gmail.com
committed
if (pci_enable_msi(pdev))
adapter->flags &= ~NETXEN_NIC_MSI_ENABLED;
dhananjay.phadke@gmail.com
committed
else
adapter->flags |= NETXEN_NIC_MSI_ENABLED;
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
netdev->irq = pdev->irq;
INIT_WORK(&adapter->tx_timeout_task, netxen_tx_timeout_task);
/*
* 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;
/* initialize the adapter */
netxen_initialize_adapter_hw(adapter);
/*
* Adapter in our case is quad port so initialize it before
* initializing the ports
*/
netxen_initialize_adapter_ops(adapter);
adapter->max_tx_desc_count = MAX_CMD_DESCRIPTORS_HOST;
if ((adapter->ahw.boardcfg.board_type == NETXEN_BRDTYPE_P2_SB35_4G) ||
(adapter->ahw.boardcfg.board_type ==
NETXEN_BRDTYPE_P2_SB31_2G))
adapter->max_rx_desc_count = MAX_RCV_DESCRIPTORS_1G;
else
adapter->max_rx_desc_count = MAX_RCV_DESCRIPTORS;
adapter->max_jumbo_rx_desc_count = MAX_JUMBO_RCV_DESCRIPTORS;
adapter->max_lro_rx_desc_count = MAX_LRO_RCV_DESCRIPTORS;
cmd_buf_arr = (struct netxen_cmd_buffer *)vmalloc(TX_RINGSIZE);
if (cmd_buf_arr == NULL) {
printk(KERN_ERR
"%s: Could not allocate cmd_buf_arr memory:%d\n",
netxen_nic_driver_name, (int)TX_RINGSIZE);
err = -ENOMEM;
goto err_out_free_adapter;
}
memset(cmd_buf_arr, 0, TX_RINGSIZE);
adapter->cmd_buf_arr = cmd_buf_arr;
for (i = 0; i < MAX_RCV_CTX; ++i) {
recv_ctx = &adapter->recv_ctx[i];
for (ring = 0; ring < NUM_RCV_DESC_RINGS; ring++) {
rcv_desc = &recv_ctx->rcv_desc[ring];
switch (RCV_DESC_TYPE(ring)) {
case RCV_DESC_NORMAL:
rcv_desc->max_rx_desc_count =
adapter->max_rx_desc_count;
rcv_desc->flags = RCV_DESC_NORMAL;
rcv_desc->dma_size = RX_DMA_MAP_LEN;
rcv_desc->skb_size = MAX_RX_BUFFER_LENGTH;
break;
case RCV_DESC_JUMBO:
rcv_desc->max_rx_desc_count =
adapter->max_jumbo_rx_desc_count;
rcv_desc->flags = RCV_DESC_JUMBO;
rcv_desc->dma_size = RX_JUMBO_DMA_MAP_LEN;
rcv_desc->skb_size = MAX_RX_JUMBO_BUFFER_LENGTH;
break;
case RCV_RING_LRO:
rcv_desc->max_rx_desc_count =
adapter->max_lro_rx_desc_count;
rcv_desc->flags = RCV_DESC_LRO;
rcv_desc->dma_size = RX_LRO_DMA_MAP_LEN;
rcv_desc->skb_size = MAX_RX_LRO_BUFFER_LENGTH;
break;
}
rcv_desc->rx_buf_arr = (struct netxen_rx_buffer *)
vmalloc(RCV_BUFFSIZE);
if (rcv_desc->rx_buf_arr == NULL) {
printk(KERN_ERR "%s: Could not allocate"
"rcv_desc->rx_buf_arr memory:%d\n",
netxen_nic_driver_name,
(int)RCV_BUFFSIZE);
err = -ENOMEM;
goto err_out_free_rx_buffer;
}
memset(rcv_desc->rx_buf_arr, 0, RCV_BUFFSIZE);
}
}
netxen_initialize_adapter_sw(adapter); /* initialize the buffers in adapter */
/* Mezz cards have PCI function 0,2,3 enabled */
if ((adapter->ahw.boardcfg.board_type == NETXEN_BRDTYPE_P2_SB31_10G_IMEZ)
&& (pci_func_id >= 2))
adapter->portnum = pci_func_id - 2;
Amit S. Kale
committed
#ifdef CONFIG_IA64
if(adapter->portnum == 0) {
netxen_pinit_from_rom(adapter, 0);
udelay(500);
netxen_load_firmware(adapter);
}
Amit S. Kale
committed
#endif
init_timer(&adapter->watchdog_timer);
adapter->ahw.xg_linkup = 0;
adapter->watchdog_timer.function = &netxen_watchdog;
adapter->watchdog_timer.data = (unsigned long)adapter;
INIT_WORK(&adapter->watchdog_task, netxen_watchdog_task);
adapter->ahw.pdev = pdev;
adapter->proc_cmd_buf_counter = 0;
adapter->ahw.revision_id = nx_p2_id;
/* make sure Window == 1 */
netxen_nic_pci_change_crbwindow(adapter, 1);
netxen_nic_update_cmd_producer(adapter, 0);
netxen_nic_update_cmd_consumer(adapter, 0);
writel(0, NETXEN_CRB_NORMALIZE(adapter, CRB_HOST_CMD_ADDR_LO));
if (netxen_is_flash_supported(adapter) == 0 &&
netxen_get_flash_mac_addr(adapter, mac_addr) == 0)
valid_mac = 1;
else
valid_mac = 0;
unsigned char *p = (unsigned char *)&mac_addr[adapter->portnum];
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
netdev->dev_addr[0] = *(p + 5);
netdev->dev_addr[1] = *(p + 4);
netdev->dev_addr[2] = *(p + 3);
netdev->dev_addr[3] = *(p + 2);
netdev->dev_addr[4] = *(p + 1);
netdev->dev_addr[5] = *(p + 0);
memcpy(netdev->perm_addr, netdev->dev_addr,
netdev->addr_len);
if (!is_valid_ether_addr(netdev->perm_addr)) {
printk(KERN_ERR "%s: Bad MAC address "
"%02x:%02x:%02x:%02x:%02x:%02x.\n",
netxen_nic_driver_name,
netdev->dev_addr[0],
netdev->dev_addr[1],
netdev->dev_addr[2],
netdev->dev_addr[3],
netdev->dev_addr[4],
netdev->dev_addr[5]);
} else {
if (adapter->macaddr_set)
adapter->macaddr_set(adapter,
netdev->dev_addr);
}
}
if (adapter->portnum == 0) {
err = netxen_initialize_adapter_offload(adapter);
if (err)
goto err_out_free_rx_buffer;
val = readl(NETXEN_CRB_NORMALIZE(adapter,
NETXEN_CAM_RAM(0x1fc)));
if (val == 0x55555555) {
/* This is the first boot after power up */
dhananjay.phadke@gmail.com
committed
netxen_nic_read_w0(adapter, NETXEN_PCIE_REG(0x4), &val);
Dhananjay Phadke
committed
if (!(val & 0x4)) {
val |= 0x4;
netxen_nic_write_w0(adapter, NETXEN_PCIE_REG(0x4), val);
netxen_nic_read_w0(adapter, NETXEN_PCIE_REG(0x4), &val);
if (!(val & 0x4))
printk(KERN_ERR "%s: failed to set MSI bit in PCI-e reg\n",
netxen_nic_driver_name);
}
val = readl(NETXEN_CRB_NORMALIZE(adapter,
NETXEN_ROMUSB_GLB_SW_RESET));
printk(KERN_INFO"NetXen: read 0x%08x for reset reg.\n",val);
if (val != 0x80000f) {
/* clear the register for future unloads/loads */
dhananjay.phadke@gmail.com
committed
writel(0, NETXEN_CRB_NORMALIZE(adapter,
NETXEN_CAM_RAM(0x1fc)));
printk(KERN_ERR "ERROR in NetXen HW init sequence.\n");
err = -ENODEV;
goto err_out_free_dev;
Dhananjay Phadke
committed
/* clear the register for future unloads/loads */
writel(0, NETXEN_CRB_NORMALIZE(adapter, NETXEN_CAM_RAM(0x1fc)));
printk(KERN_INFO "State: 0x%0x\n",
readl(NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE)));
/*
* Tell the hardware our version number.
*/
i = (_NETXEN_NIC_LINUX_MAJOR << 16)
| ((_NETXEN_NIC_LINUX_MINOR << 8))
| (_NETXEN_NIC_LINUX_SUBVERSION);
writel(i, NETXEN_CRB_NORMALIZE(adapter, CRB_DRIVER_VERSION));
/* Unlock the HW, prompting the boot sequence */
writel(1,
NETXEN_CRB_NORMALIZE(adapter,
NETXEN_ROMUSB_GLB_PEGTUNE_DONE));
/* Handshake with the card before we register the devices. */
netxen_phantom_init(adapter, NETXEN_NIC_PEG_TUNE);
}
* See if the firmware gave us a virtual-physical port mapping.
i = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_V2P(adapter->portnum)));
if (i != 0x55555555)
physical_port[adapter->portnum] = i;
netif_carrier_off(netdev);
netif_stop_queue(netdev);
if ((err = register_netdev(netdev))) {
printk(KERN_ERR "%s: register_netdev failed port #%d"
" aborting\n", netxen_nic_driver_name,
adapter->portnum);
err = -EIO;
goto err_out_free_dev;
}
pci_set_drvdata(pdev, adapter);
switch (adapter->ahw.board_type) {
case NETXEN_NIC_GBE:
printk(KERN_INFO "%s: QUAD GbE board initialized\n",
netxen_nic_driver_name);
break;
case NETXEN_NIC_XGBE:
printk(KERN_INFO "%s: XGbE board initialized\n",
netxen_nic_driver_name);
break;
}
adapter->driver_mismatch = 0;
return 0;
err_out_free_dev:
if (adapter->portnum == 0)
netxen_free_adapter_offload(adapter);
err_out_free_rx_buffer:
for (i = 0; i < MAX_RCV_CTX; ++i) {
recv_ctx = &adapter->recv_ctx[i];
for (ring = 0; ring < NUM_RCV_DESC_RINGS; ring++) {
rcv_desc = &recv_ctx->rcv_desc[ring];
if (rcv_desc->rx_buf_arr != NULL) {
vfree(rcv_desc->rx_buf_arr);
rcv_desc->rx_buf_arr = NULL;
}
}
}
vfree(cmd_buf_arr);
err_out_free_adapter:
if (adapter->flags & NETXEN_NIC_MSI_ENABLED)
pci_disable_msi(pdev);
if (db_ptr)
iounmap(db_ptr);
if (mem_ptr0)
iounmap(mem_ptr0);
if (mem_ptr1)
iounmap(mem_ptr1);
if (mem_ptr2)
iounmap(mem_ptr2);
Amit S. Kale
committed
err_out_free_netdev:
free_netdev(netdev);
err_out_free_res:
err_out_disable_pdev:
pci_disable_device(pdev);
return err;
}
static void __devexit netxen_nic_remove(struct pci_dev *pdev)
{
struct netxen_adapter *adapter;
struct netxen_rx_buffer *buffer;
struct netxen_recv_context *recv_ctx;
struct netxen_rcv_desc_ctx *rcv_desc;
Dhananjay Phadke
committed
int i, ctxid, ring;
static int init_firmware_done = 0;
adapter = pci_get_drvdata(pdev);
netdev = adapter->netdev;
Dhananjay Phadke
committed
unregister_netdev(netdev);
if (adapter->stop_port)
adapter->stop_port(adapter);
Dhananjay Phadke
committed
netxen_nic_disable_int(adapter);
Dhananjay Phadke
committed
if (adapter->irq)
free_irq(adapter->irq, adapter);
Dhananjay Phadke
committed
if (adapter->is_up == NETXEN_ADAPTER_UP_MAGIC) {
init_firmware_done++;
Dhananjay Phadke
committed
}
for (ctxid = 0; ctxid < MAX_RCV_CTX; ++ctxid) {
recv_ctx = &adapter->recv_ctx[ctxid];
for (ring = 0; ring < NUM_RCV_DESC_RINGS; ring++) {
rcv_desc = &recv_ctx->rcv_desc[ring];
for (i = 0; i < rcv_desc->max_rx_desc_count; ++i) {
buffer = &(rcv_desc->rx_buf_arr[i]);
if (buffer->state == NETXEN_BUFFER_FREE)
continue;
pci_unmap_single(pdev, buffer->dma,
rcv_desc->dma_size,
PCI_DMA_FROMDEVICE);
if (buffer->skb != NULL)
dev_kfree_skb_any(buffer->skb);
}
vfree(rcv_desc->rx_buf_arr);
}
}
Dhananjay Phadke
committed
if (adapter->flags & NETXEN_NIC_MSI_ENABLED)
pci_disable_msi(pdev);
vfree(adapter->cmd_buf_arr);
Dhananjay Phadke
committed
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
pci_disable_device(pdev);
if (adapter->portnum == 0) {
if (init_firmware_done) {
dma_watchdog_shutdown_request(adapter);
msleep(100);
i = 100;
while ((dma_watchdog_shutdown_poll_result(adapter) != 1) && i) {
printk(KERN_INFO "dma_watchdog_shutdown_poll still in progress\n");
msleep(100);
i--;
}
if (i == 0) {
printk(KERN_ERR "dma_watchdog_shutdown_request failed\n");
return;
}
/* clear the register for future unloads/loads */
writel(0, NETXEN_CRB_NORMALIZE(adapter, NETXEN_CAM_RAM(0x1fc)));
printk(KERN_INFO "State: 0x%0x\n",
readl(NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE)));
/* leave the hw in the same state as reboot */
writel(0, NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE));
if (netxen_pinit_from_rom(adapter, 0))
return;
msleep(1);
if (netxen_load_firmware(adapter))
return;
netxen_phantom_init(adapter, NETXEN_NIC_PEG_TUNE);
}
/* clear the register for future unloads/loads */
writel(0, NETXEN_CRB_NORMALIZE(adapter, NETXEN_CAM_RAM(0x1fc)));
printk(KERN_INFO "State: 0x%0x\n",
readl(NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE)));
dma_watchdog_shutdown_request(adapter);
msleep(100);
i = 100;
while ((dma_watchdog_shutdown_poll_result(adapter) != 1) && i) {
printk(KERN_INFO "dma_watchdog_shutdown_poll still in progress\n");
msleep(100);
i--;
}
if (i) {
netxen_free_adapter_offload(adapter);
} else {
printk(KERN_ERR "failed to dma shutdown\n");
return;
}
}
iounmap(adapter->ahw.db_base);
iounmap(adapter->ahw.pci_base0);
iounmap(adapter->ahw.pci_base1);
iounmap(adapter->ahw.pci_base2);
pci_release_regions(pdev);
pci_set_drvdata(pdev, NULL);
}
/*
* Called when a network interface is made active
* @returns 0 on success, negative value on failure
*/
static int netxen_nic_open(struct net_device *netdev)
{
struct netxen_adapter *adapter = (struct netxen_adapter *)netdev->priv;
int err = 0;
int ctx, ring;
if (adapter->is_up != NETXEN_ADAPTER_UP_MAGIC) {
err = netxen_init_firmware(adapter);
if (err != 0) {
printk(KERN_ERR "Failed to init firmware\n");
return -EIO;
}
netxen_nic_flash_print(adapter);
/* setup all the resources for the Phantom... */
/* this include the descriptors for rcv, tx, and status */
netxen_nic_clear_stats(adapter);
err = netxen_nic_hw_resources(adapter);
if (err) {
printk(KERN_ERR "Error in setting hw resources:%d\n",
err);
return err;
}
for (ctx = 0; ctx < MAX_RCV_CTX; ++ctx) {
Amit S. Kale
committed
for (ring = 0; ring < NUM_RCV_DESC_RINGS; ring++)
netxen_post_rx_buffers(adapter, ctx, ring);
}
adapter->irq = adapter->ahw.pdev->irq;
err = request_irq(adapter->ahw.pdev->irq, netxen_intr,
IRQF_SHARED|IRQF_SAMPLE_RANDOM, netdev->name,
if (err) {
printk(KERN_ERR "request_irq failed with: %d\n", err);
netxen_free_hw_resources(adapter);
adapter->is_up = NETXEN_ADAPTER_UP_MAGIC;
}
if (!adapter->driver_mismatch)
mod_timer(&adapter->watchdog_timer, jiffies);
/* Done here again so that even if phantom sw overwrote it,
* we set it */
if (adapter->macaddr_set)
adapter->macaddr_set(adapter, netdev->dev_addr);
if (adapter->init_port
&& adapter->init_port(adapter, adapter->portnum) != 0) {
del_timer_sync(&adapter->watchdog_timer);
printk(KERN_ERR "%s: Failed to initialize port %d\n",
netxen_nic_driver_name, adapter->portnum);
return -EIO;
}
netxen_nic_set_link_parameters(adapter);
if (adapter->set_mtu)
adapter->set_mtu(adapter, netdev->mtu);
if (!adapter->driver_mismatch)
netif_start_queue(netdev);
return 0;
}
/*
* netxen_nic_close - Disables a network interface entry point
*/
static int netxen_nic_close(struct net_device *netdev)
{
struct netxen_adapter *adapter = netdev_priv(netdev);
int i, j;
struct netxen_cmd_buffer *cmd_buff;
struct netxen_skb_frag *buffrag;
netif_carrier_off(netdev);
netif_stop_queue(netdev);
cmd_buff = adapter->cmd_buf_arr;
for (i = 0; i < adapter->max_tx_desc_count; i++) {
buffrag = cmd_buff->frag_array;
if (buffrag->dma) {
pci_unmap_single(adapter->pdev, buffrag->dma,
buffrag->length, PCI_DMA_TODEVICE);
Dhananjay Phadke
committed
buffrag->dma = 0ULL;
}
for (j = 0; j < cmd_buff->frag_count; j++) {
buffrag++;
pci_unmap_page(adapter->pdev, buffrag->dma,
buffrag->length,
PCI_DMA_TODEVICE);
Dhananjay Phadke
committed
buffrag->dma = 0ULL;
/* Free the skb we received in netxen_nic_xmit_frame */
if (cmd_buff->skb) {
dev_kfree_skb_any(cmd_buff->skb);
cmd_buff->skb = NULL;
}
cmd_buff++;
Dhananjay Phadke
committed
if (adapter->is_up == NETXEN_ADAPTER_UP_MAGIC) {
FLUSH_SCHEDULED_WORK();
del_timer_sync(&adapter->watchdog_timer);
}
return 0;
}
static int netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
{
struct netxen_adapter *adapter = netdev_priv(netdev);
struct netxen_hardware_context *hw = &adapter->ahw;
unsigned int first_seg_len = skb->len - skb->data_len;
struct netxen_skb_frag *buffrag;
unsigned int i;
u32 producer = 0;
u32 saved_producer = 0;
struct cmd_desc_type0 *hwdesc;
int k;
struct netxen_cmd_buffer *pbuf = NULL;
static int dropped_packet = 0;
int frag_count;
u32 local_producer = 0;
u32 max_tx_desc_count = 0;
u32 last_cmd_consumer = 0;
int no_of_desc;
frag_count = skb_shinfo(skb)->nr_frags + 1;
if (unlikely(skb->len <= 0)) {
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
if (frag_count > MAX_BUFFERS_PER_CMD) {
printk("%s: %s netxen_nic_xmit_frame: frag_count (%d)"
"too large, can handle only %d frags\n",
netxen_nic_driver_name, netdev->name,
frag_count, MAX_BUFFERS_PER_CMD);
if ((++dropped_packet & 0xff) == 0xff)
printk("%s: %s droppped packets = %d\n",
netxen_nic_driver_name, netdev->name,
dropped_packet);
return NETDEV_TX_OK;
}
/*
* Everything is set up. Now, we just need to transmit it out.
* Note that we have to copy the contents of buffer over to
* right place. Later on, this can be optimized out by de-coupling the
* producer index from the buffer index.
*/
retry_getting_window:
spin_lock_bh(&adapter->tx_lock);
if (adapter->total_threads >= MAX_XMIT_PRODUCERS) {
spin_unlock_bh(&adapter->tx_lock);
/*
* Yield CPU
*/
if (!in_atomic())
schedule();
else {
for (i = 0; i < 20; i++)
cpu_relax(); /*This a nop instr on i386 */
}
goto retry_getting_window;
}
local_producer = adapter->cmd_producer;
/* There 4 fragments per descriptor */
no_of_desc = (frag_count + 3) >> 2;
Amit S. Kale
committed
if (netdev->features & NETIF_F_TSO) {
if (skb_shinfo(skb)->gso_size > 0) {
if ((ip_hdrlen(skb) + tcp_hdrlen(skb) +
(sizeof(struct cmd_desc_type0) - 2)) {
Amit S. Kale
committed
no_of_desc++;
}
}
}
k = adapter->cmd_producer;
max_tx_desc_count = adapter->max_tx_desc_count;
last_cmd_consumer = adapter->last_cmd_consumer;
if ((k + no_of_desc) >=
((last_cmd_consumer <= k) ? last_cmd_consumer + max_tx_desc_count :
last_cmd_consumer)) {
netif_stop_queue(netdev);
adapter->flags |= NETXEN_NETDEV_STATUS;
return NETDEV_TX_BUSY;
}
k = get_index_range(k, max_tx_desc_count, no_of_desc);
adapter->cmd_producer = k;
adapter->total_threads++;
adapter->num_threads++;
spin_unlock_bh(&adapter->tx_lock);
/* Copy the descriptors into the hardware */
producer = local_producer;
saved_producer = producer;
hwdesc = &hw->cmd_desc_head[producer];
memset(hwdesc, 0, sizeof(struct cmd_desc_type0));
/* Take skb->data itself */
pbuf = &adapter->cmd_buf_arr[producer];
Amit S. Kale
committed
if ((netdev->features & NETIF_F_TSO) && skb_shinfo(skb)->gso_size > 0) {
hwdesc->mss = cpu_to_le16(skb_shinfo(skb)->gso_size);
} else {
pbuf->mss = 0;
hwdesc->mss = 0;
}
pbuf->total_length = skb->len;
pbuf->skb = skb;
pbuf->cmd = TX_ETHER_PKT;
pbuf->frag_count = frag_count;
buffrag->dma = pci_map_single(adapter->pdev, skb->data, first_seg_len,
PCI_DMA_TODEVICE);
buffrag->length = first_seg_len;
netxen_set_cmd_desc_totallength(hwdesc, skb->len);
netxen_set_cmd_desc_num_of_buff(hwdesc, frag_count);
netxen_set_cmd_desc_opcode(hwdesc, TX_ETHER_PKT);
netxen_set_cmd_desc_port(hwdesc, adapter->portnum);
netxen_set_cmd_desc_ctxid(hwdesc, adapter->portnum);
hwdesc->buffer1_length = cpu_to_le16(first_seg_len);
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;