[openib-general] [PATCH 6/9] NetEffect 10Gb RNIC Driver: kernel network interface c file
Glenn Grundstrom
ggrundstrom at NetEffect.com
Thu Oct 26 17:17:21 PDT 2006
Kernel driver patch 6 of 9.
Signed-off-by: Glenn Grundstrom <glenng at neteffect.com>
======================================================
diff -ruNp old/drivers/infiniband/hw/nes/nes_nic.c
new/drivers/infiniband/hw/nes/nes_nic.c
--- old/drivers/infiniband/hw/nes/nes_nic.c 1969-12-31
18:00:00.000000000 -0600
+++ new/drivers/infiniband/hw/nes/nes_nic.c 2006-10-25
10:15:50.000000000 -0500
@@ -0,0 +1,567 @@
+/*
+ * Copyright (c) 2006 NetEffect, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/etherdevice.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/if_arp.h>
+
+#include "nes.h"
+
+static const u32 default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE |
NETIF_MSG_LINK
+ |
NETIF_MSG_IFUP | NETIF_MSG_IFDOWN;
+static int debug = -1;
+
+static int nes_netdev_open(struct net_device *);
+static int nes_netdev_stop(struct net_device *);
+static int nes_netdev_start_xmit(struct sk_buff *, struct net_device
*);
+static struct net_device_stats *nes_netdev_get_stats(struct net_device
*);
+static void nes_netdev_tx_timeout(struct net_device *);
+static int nes_netdev_set_mac_address(struct net_device *, void *);
+static int nes_netdev_change_mtu(struct net_device *, int);
+
+
+/**
+ * nes_netdev_open
+ *
+ * @param netdev
+ *
+ * @return int
+ */
+static int nes_netdev_open(struct net_device *netdev)
+{
+ struct nes_port *nes_port = netdev_priv(netdev);
+ struct nes_dev *nesdev = nes_port->nesdev;
+ u32 u32temp;
+ u32 nic_active_bit;
+ u32 nic_active;
+ u16 link_up = 0;
+
+ dprintk("%s:%s:%u\n", __FILE__, __FUNCTION__, __LINE__);
+
+ assert(nesdev != NULL);
+
+ if (netif_msg_ifup(nes_port))
+ dprintk(KERN_INFO PFX "%s: enabling interface\n",
netdev->name);
+
+ /* clear the MAC interrupt status */
+ u32temp = nes_read_indexed(nesdev->index_reg,
NES_IDX_MAC_INT_STATUS );
+ dprintk("Phy interrupt status = 0x%X.\n", u32temp);
+ nes_write_indexed(nesdev->index_reg, NES_IDX_MAC_INT_STATUS,
u32temp);
+
+ nes_phy_init(nesdev);
+
+ nes_nic_qp_init(nesdev, netdev);
+
+ // Set packet filters
+ nic_active_bit = 1<<PCI_FUNC(nesdev->pcidev->devfn);
+ nic_active = nes_read_indexed(nesdev->index_reg,
NES_IDX_NIC_ACTIVE);
+ nic_active |= nic_active_bit;
+ nic_active |= 2;
+ nes_write_indexed(nesdev->index_reg, NES_IDX_NIC_ACTIVE,
nic_active);
+ nic_active = nes_read_indexed(nesdev->index_reg,
NES_IDX_NIC_MULTICAST_ALL);
+ nic_active |= nic_active_bit;
+ nes_write_indexed(nesdev->index_reg, NES_IDX_NIC_MULTICAST_ALL,
nic_active);
+ nic_active = nes_read_indexed(nesdev->index_reg,
NES_IDX_NIC_BROADCAST_ON);
+ nic_active |= nic_active_bit;
+ nes_write_indexed(nesdev->index_reg, NES_IDX_NIC_BROADCAST_ON,
nic_active);
+
+
+ nes_write32(nesdev->regs+NES_CQE_ALLOC,
NES_CQE_ALLOC_NOTIFY_NEXT |
+ nesdev->hnic_cq.cq_number );
+
+ // TODO: add proper way to setup packet filters
+ // TODO: move some of the code from init_netdev?
+
+ if ( link_up ) {
+ /* Enable network packets */
+ nes_port->linkup = 1;
+ netif_start_queue(netdev);
+ } else {
+ nes_port->linkup = 0;
+ netif_carrier_off(netdev);
+ }
+
+ nes_write_indexed(nesdev->index_reg, NES_IDX_MAC_INT_MASK,
+ ~(NES_MAC_INT_LINK_STAT_CHG |
NES_MAC_INT_XGMII_EXT |
+ NES_MAC_INT_TX_UNDERFLOW
| NES_MAC_INT_TX_ERROR) );
+
+ return 0;
+}
+
+
+/**
+ * nes_netdev_stop
+ *
+ * @param netdev
+ *
+ * @return int
+ */
+static int nes_netdev_stop(struct net_device *netdev)
+{
+ struct nes_port *nes_port = netdev_priv(netdev);
+ struct nes_dev *nesdev = nes_port->nesdev;
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ struct nes_hw_nic_rq_wqe *nic_rqe;
+ u64 wqe_frag;
+ u32 cqp_head;
+ u32 nic_active_mask;
+ u32 nic_active;
+ unsigned long flags;
+ int ret;
+
+ dprintk("%s:%s:%u\n", __FILE__, __FUNCTION__, __LINE__);
+
+ nes_stop_cm(nesdev);
+
+ if (netif_msg_ifdown(nes_port))
+ dprintk(KERN_INFO PFX "%s: disabling interface\n",
netdev->name);
+
+ nes_write_indexed(nesdev->index_reg, NES_IDX_MAC_INT_MASK,
0xffffffff );
+
+ nic_active_mask = ~((u32)(1<<PCI_FUNC(nesdev->pcidev->devfn)));
+ nic_active_mask = ~((u32)(1 << (PCI_FUNC(nesdev->pcidev->devfn)
+ 1)));
+ nes_write_indexed(nesdev->index_reg,
+
NES_IDX_PERFECT_FILTER_HIGH+((PCI_FUNC(nesdev->pcidev->devfn)+1)*8), 0);
+ nes_write_indexed(nesdev->index_reg,
+
NES_IDX_PERFECT_FILTER_HIGH+(PCI_FUNC(nesdev->pcidev->devfn)*8), 0);
+ nic_active = nes_read_indexed(nesdev->index_reg,
NES_IDX_NIC_ACTIVE);
+ nic_active &= nic_active_mask;
+ nes_write_indexed(nesdev->index_reg, NES_IDX_NIC_ACTIVE,
nic_active);
+ nic_active = nes_read_indexed(nesdev->index_reg,
NES_IDX_NIC_MULTICAST_ALL);
+ nic_active &= nic_active_mask;
+ nes_write_indexed(nesdev->index_reg, NES_IDX_NIC_MULTICAST_ALL,
nic_active);
+ nic_active = nes_read_indexed(nesdev->index_reg,
NES_IDX_NIC_BROADCAST_ON);
+ nic_active &= nic_active_mask;
+ nes_write_indexed(nesdev->index_reg, NES_IDX_NIC_BROADCAST_ON,
nic_active);
+
+
+ /* Disable network packets */
+ netif_stop_queue(netdev);
+
+ // Free remaining NIC receive buffers
+ while (nesdev->hnic.rq_head != nesdev->hnic.rq_tail) {
+ nic_rqe = &nesdev->hnic.rq_vbase[nesdev->hnic.rq_tail];
+ wqe_frag =
nic_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_LOW_IDX];
+ wqe_frag +=
((u64)nic_rqe->wqe_words[NES_NIC_RQ_WQE_FRAG0_HIGH_IDX])<<32;
+ pci_unmap_single(nesdev->pcidev, (dma_addr_t)wqe_frag,
+ max_frame_len,
PCI_DMA_FROMDEVICE);
+
dev_kfree_skb(nesdev->hnic.rx_skb[nesdev->hnic.rq_tail++]);
+ nesdev->hnic.rq_tail &= nesdev->hnic.rq_size - 1;
+ }
+
+ // Destroy NIC QP
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ cqp_head = nesdev->cqp.sq_head;
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+
+ cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] =
cpu_to_le32(NES_CQP_DESTROY_QP | NES_CQP_QP_TYPE_NIC);
+ cqp_wqe->wqe_words[NES_CQP_WQE_ID_IDX] =
cpu_to_le32(nesdev->hnic_cq.cq_number);
+ cqp_wqe->wqe_words[NES_CQP_WQE_COMP_CTX_HIGH_IDX] = 0;
+ *((struct nes_hw_cqp
**)&cqp_wqe->wqe_words[NES_CQP_WQE_COMP_CTX_LOW_IDX]) = &nesdev->cqp;
+ cqp_wqe->wqe_words[NES_CQP_WQE_COMP_SCRATCH_LOW_IDX] =
cqp_head;
+ cqp_wqe->wqe_words[NES_CQP_WQE_COMP_SCRATCH_HIGH_IDX] = 0;
+ if (++cqp_head >= nesdev->cqp.sq_size) cqp_head = 0;
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+
+ // Destroy NIC CQ
+ cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] =
cpu_to_le32(NES_CQP_DESTROY_CQ | (nesdev->hnic_cq.cq_size<<16));
+ cqp_wqe->wqe_words[NES_CQP_WQE_ID_IDX] =
cpu_to_le32(nesdev->hnic_cq.cq_number |
((u32)PCI_FUNC(nesdev->pcidev->devfn)<<16));
+ cqp_wqe->wqe_words[NES_CQP_WQE_COMP_CTX_HIGH_IDX] = 0;
+ *((struct nes_hw_cqp
**)&cqp_wqe->wqe_words[NES_CQP_WQE_COMP_CTX_LOW_IDX]) = &nesdev->cqp;
+ cqp_wqe->wqe_words[NES_CQP_WQE_COMP_SCRATCH_LOW_IDX] =
cqp_head;
+ cqp_wqe->wqe_words[NES_CQP_WQE_COMP_SCRATCH_HIGH_IDX] = 0;
+ if (++cqp_head >= nesdev->cqp.sq_size) cqp_head = 0;
+
+ nesdev->cqp.sq_head = cqp_head;
+ barrier();
+
+ // Ring doorbell (2 WQEs)
+ nes_write32(nesdev->regs+NES_WQE_ALLOC, 0x02800000 |
nesdev->cqp.qp_id );
+
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ dprintk("Waiting for destroy NIC QP to complete.\n");
+ cqp_head = (cqp_head+1)&(nesdev->cqp.sq_size-1);
+ ret =
wait_event_timeout(nesdev->cqp.waitq,(nesdev->cqp.sq_tail==cqp_head),
2);
+
+ dprintk("Destroy NIC QP completed, wait_event_timeout ret =
%u.\n", ret);
+
+ // Free the NIC memory
+ pci_free_consistent(nesdev->pcidev, nesdev->nic_mem_size,
nesdev->hnic.first_frag_vbase,
+
nesdev->hnic.frag_paddr[0]);
+
+ return 0;
+}
+
+
+/**
+ * nes_netdev_start_xmit
+ *
+ * @param skb
+ * @param netdev
+ *
+ * @return int
+ */
+static int nes_netdev_start_xmit(struct sk_buff *skb, struct net_device
*netdev)
+{
+ struct nes_port *nes_port = netdev_priv(netdev);
+ struct nes_dev *nesdev = nes_port->nesdev;
+ struct nes_hw_nic *nesnic = &nesdev->hnic;
+ struct nes_hw_nic_sq_wqe *nic_sqe;
+ unsigned long flags;
+ dma_addr_t bus_address;
+
+// printk(KERN_ERR PFX "%s: Request to transmit a NIC packet length
%u (%u frags), first address = %p...\n",
+// pci_name(nesdev->pcidev), skb_headlen(skb),
skb_shinfo(skb)->nr_frags, skb->data);
+ local_irq_save(flags);
+ if (!spin_trylock(&nesnic->sq_lock)) {
+ local_irq_restore(flags);
+ return NETDEV_TX_LOCKED;
+ }
+
+ /* Check if SQ is full */
+ if ((((nesnic->sq_tail+(nesnic->sq_size*2))-nesnic->sq_head) &
(nesnic->sq_size - 1)) == 1 ) {
+ netif_stop_queue(netdev);
+ spin_unlock_irqrestore(&nesnic->sq_lock, flags);
+ dprintk(KERN_WARNING PFX "%s: HNIC SQ full when queue
awake!\n", netdev->name);
+ return NETDEV_TX_BUSY;
+ }
+
+ /* Check if too many fragments */
+ if (skb_shinfo(skb)->nr_frags) {
+ /* TODO: if too many fragments copy the data or enable
EFBs */
+ spin_unlock_irqrestore(&nesnic->sq_lock, flags);
+ kfree_skb(skb);
+ dprintk(KERN_WARNING PFX "%s: HNIC TODO: Need support
for more 4 fragments! Packet with %u fragments not sent.\n",
+ netdev->name,
skb_shinfo(skb)->nr_frags+1);
+ return NETDEV_TX_OK;
+ }
+
+ memcpy(&nesnic->first_frag_vbase[nesnic->sq_head].buffer,
+ skb->data, min(((unsigned int)NES_FIRST_FRAG_SIZE),
(skb_headlen(skb))));
+
+ nic_sqe = &nesnic->sq_vbase[nesnic->sq_head];
+
+ nic_sqe->wqe_words[NES_NIC_SQ_WQE_MISC_IDX] =
cpu_to_le32(NES_NIC_SQ_WQE_DISABLE_CHKSUM | NES_NIC_SQ_WQE_COMPLETION);
+ nic_sqe->wqe_words[NES_NIC_SQ_WQE_TOTAL_LENGTH_IDX] =
cpu_to_le32(skb_headlen(skb));
+ if (skb_headlen(skb)>NES_FIRST_FRAG_SIZE) {
+ bus_address = pci_map_single(nesdev->pcidev,
skb->data+NES_FIRST_FRAG_SIZE,
+
skb_headlen(skb)-NES_FIRST_FRAG_SIZE, PCI_DMA_TODEVICE);
+ nic_sqe->wqe_words[NES_NIC_SQ_WQE_LENGTH_2_1_IDX] =
cpu_to_le32(skb_headlen(skb)-NES_FIRST_FRAG_SIZE);
+ nic_sqe->wqe_words[NES_NIC_SQ_WQE_FRAG1_LOW_IDX] =
cpu_to_le32((u32)bus_address);
+ nic_sqe->wqe_words[NES_NIC_SQ_WQE_FRAG1_HIGH_IDX] =
cpu_to_le32((u32)((u64)bus_address>>32));
+// dprintk("Mapping sq fragment 0x%08X%08X, length = %u.\n",
+//
nic_sqe->wqe_words[NES_NIC_SQ_WQE_FRAG1_HIGH_IDX],
+//
nic_sqe->wqe_words[NES_NIC_SQ_WQE_FRAG1_LOW_IDX],
+//
nic_sqe->wqe_words[NES_NIC_SQ_WQE_LENGTH_2_1_IDX]);
+ nesnic->tx_skb[nesnic->sq_head] = skb;
+ } else {
+ nic_sqe->wqe_words[NES_NIC_SQ_WQE_LENGTH_2_1_IDX] = 0;
+ nesnic->tx_skb[nesnic->sq_head] = 0;
+ dev_kfree_skb(skb);
+ }
+
+ nesnic->sq_head++;
+ nesnic->sq_head &= nesnic->sq_size-1;
+
+ barrier();
+
+ nes_write32(nesdev->regs+NES_WQE_ALLOC, (1<<24) | (1<<23) |
nesdev->hnic.qp_id );
+
+ netdev->trans_start = jiffies;
+ nes_port->netstats.tx_packets++;
+ nes_port->netstats.tx_bytes += skb_headlen(skb);
+ spin_unlock_irqrestore(&nesnic->sq_lock, flags);
+
+ return NETDEV_TX_OK;
+}
+
+
+/**
+ * nes_netdev_get_stats
+ *
+ * @param netdev
+ *
+ * @return struct net_device_stats*
+ */
+static struct net_device_stats *nes_netdev_get_stats(struct net_device
*netdev)
+{
+ struct nes_port *nes_port = netdev_priv(netdev);
+
+ return (&nes_port->netstats);
+}
+
+
+/**
+ * nes_netdev_tx_timeout
+ *
+ * @param netdev
+ */
+static void nes_netdev_tx_timeout(struct net_device *netdev)
+{
+ struct nes_port *nes_port = netdev_priv(netdev);
+
+ if (netif_msg_timer(nes_port))
+ dprintk(KERN_DEBUG PFX "%s: tx timeout\n",
netdev->name);
+}
+
+
+/**
+ * nes_netdev_set_mac_address
+ *
+ * @param netdev
+ * @param p
+ *
+ * @return int
+ */
+static int nes_netdev_set_mac_address(struct net_device *netdev, void
*p)
+{
+ return -1;
+}
+
+
+/**
+ * nes_netdev_change_mtu
+ *
+ * @param netdev
+ * @param new_mtu
+ *
+ * @return int
+ */
+static int nes_netdev_change_mtu(struct net_device *netdev, int
new_mtu)
+{
+ int ret = 0;
+
+ if ( (new_mtu < ETH_ZLEN) || (new_mtu > max_mtu) )
+ return -EINVAL;
+
+ netdev->mtu = new_mtu;
+
+ if (netif_running(netdev)) {
+ nes_netdev_stop(netdev);
+ nes_netdev_open(netdev);
+ }
+
+ return ret;
+}
+
+
+/**
+ * nes_netdev_init - initialize network device
+ *
+ * @param nesdev
+ * @param mmio_addr
+ *
+ * @return struct net_device*
+ */
+struct net_device *nes_netdev_init(struct nes_dev *nesdev, void __iomem
*mmio_addr)
+{
+ struct nes_port *nes_port = NULL;
+ struct net_device *netdev = alloc_etherdev(sizeof(*nes_port));
+ u32 count=0;
+
+ if (!netdev) {
+ dprintk(KERN_ERR PFX "nes_port etherdev alloc failed");
+ return NULL;
+ }
+
+ SET_MODULE_OWNER(netdev);
+ SET_NETDEV_DEV(netdev, &nesdev->pcidev->dev);
+
+ netdev->open = nes_netdev_open;
+ netdev->stop = nes_netdev_stop;
+ netdev->hard_start_xmit = nes_netdev_start_xmit;
+ netdev->get_stats = nes_netdev_get_stats;
+ netdev->tx_timeout = nes_netdev_tx_timeout;
+ netdev->set_mac_address = nes_netdev_set_mac_address;
+ netdev->change_mtu = nes_netdev_change_mtu;
+ netdev->watchdog_timeo = NES_TX_TIMEOUT;
+ netdev->irq = nesdev->pcidev->irq;
+ netdev->mtu = max_mtu;
+ netdev->hard_header_len = ETH_HLEN;
+ netdev->addr_len = ETH_ALEN;
+ netdev->type = ARPHRD_ETHER;
+
+ /* Setup the burned in MAC address */
+ netdev->dev_addr[0] =
(u8)(nesdev->nesadapter->mac_addr_high>>8);
+ netdev->dev_addr[1] = (u8)nesdev->nesadapter->mac_addr_high;
+ netdev->dev_addr[2] =
(u8)(nesdev->nesadapter->mac_addr_low>>24);
+ netdev->dev_addr[3] =
(u8)(nesdev->nesadapter->mac_addr_low>>16);
+ netdev->dev_addr[4] = (u8)(nesdev->nesadapter->mac_addr_low>>8);
+ netdev->dev_addr[5] = (u8)nesdev->nesadapter->mac_addr_low;
+
+ /* Program the various MAC regs */
+ nes_write_indexed(nesdev->index_reg,
+
NES_IDX_PERFECT_FILTER_LOW+(PCI_FUNC(nesdev->pcidev->devfn)*8),
+
nesdev->nesadapter->mac_addr_low+PCI_FUNC(nesdev->pcidev->devfn));
+ nes_write_indexed(nesdev->index_reg,
+
NES_IDX_PERFECT_FILTER_HIGH+(PCI_FUNC(nesdev->pcidev->devfn)*8),
+
(u32)nesdev->nesadapter->mac_addr_high | NES_MAC_ADDR_VALID |
+
((((u32)PCI_FUNC(nesdev->pcidev->devfn))<<16)));
+ nes_write_indexed(nesdev->index_reg,
+
NES_IDX_PERFECT_FILTER_LOW+((PCI_FUNC(nesdev->pcidev->devfn)+1)*8),
+
nesdev->nesadapter->mac_addr_low+PCI_FUNC(nesdev->pcidev->devfn));
+ nes_write_indexed(nesdev->index_reg,
+
NES_IDX_PERFECT_FILTER_HIGH+((PCI_FUNC(nesdev->pcidev->devfn)+1)*8),
+
(u32)nesdev->nesadapter->mac_addr_high | NES_MAC_ADDR_VALID |
+
((((u32)PCI_FUNC(nesdev->pcidev->devfn))<<16)));
+
+ /* Fill in the port structure */
+ nes_port = netdev_priv(netdev);
+ nes_port->netdev = netdev;
+ nes_port->nesdev = nesdev;
+ nes_port->msg_enable = netif_msg_init(debug, default_msg);
+
+ spin_lock_init(&nes_port->tx_lock);
+
+ nes_cqp_init(nesdev);
+
+ // Arm the CCQ
+ nes_write32(nesdev->regs+NES_CQE_ALLOC,
NES_CQE_ALLOC_NOTIFY_NEXT |
+ PCI_FUNC(nesdev->pcidev->devfn) );
+
+ // Enable the interrupts
+ nesdev->int_req = (1<<PCI_FUNC(nesdev->pcidev->devfn)) |
+
(1<<(PCI_FUNC(nesdev->pcidev->devfn)+16)) |
+
(1<<(PCI_FUNC(nesdev->pcidev->devfn)+24));
+ nesdev->intf_int_req &= ~NES_INTF_INT_CRITERR;
+ nes_write32(nesdev->regs+NES_INTF_INT_MASK,
~(nesdev->intf_int_req));
+
+ nes_write32(nesdev->regs+NES_INT_MASK, ~nesdev->int_req);
+ dprintk("Waiting for create CQP init to complete.\n");
+ do {
+ if (count++ > 1000)
+ break;
+ udelay(10);
+ } while (!(nesdev->cqp.sq_head == nesdev->cqp.sq_tail));
+
+ nesdev->netdev = netdev;
+
+ list_add_tail(&nesdev->list, &nes_dev_list);
+
+ return netdev;
+}
+
+
+/**
+ * nes_netdev_exit
+ *
+ * @param nesdev
+ */
+void nes_netdev_exit(struct nes_dev *nesdev)
+{
+ struct nes_hw_cqp_wqe *cqp_wqe;
+ u32 count=0;
+ u32 cqp_head;
+ unsigned long flags;
+ int ret;
+
+ dprintk("Waiting for CQP work to complete.\n");
+ do {
+ if (count++ > 1000) break;
+ udelay(10);
+ } while ( !(nesdev->cqp.sq_head == nesdev->cqp.sq_tail) );
+
+ // Reset CCQ
+ nes_write32(nesdev->regs+NES_CQE_ALLOC, NES_CQE_ALLOC_RESET |
+ nesdev->ccq.cq_number );
+ // Disable device interrupts
+ nes_write32(nesdev->regs+NES_INT_MASK, 0x7fffffff );
+ // Destroy the AEQ
+ spin_lock_irqsave(&nesdev->cqp.lock, flags);
+ cqp_head = nesdev->cqp.sq_head++;
+ nesdev->cqp.sq_head &= nesdev->cqp.sq_size-1;
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+ cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] =
cpu_to_le32(NES_CQP_DESTROY_AEQ + (PCI_FUNC(nesdev->pcidev->devfn)<<8));
+ cqp_wqe->wqe_words[NES_CQP_WQE_COMP_CTX_HIGH_IDX] = 0;
+ // Destroy the CEQ
+ cqp_head = nesdev->cqp.sq_head++;
+ nesdev->cqp.sq_head &= nesdev->cqp.sq_size-1;
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+ cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] =
cpu_to_le32(NES_CQP_DESTROY_CEQ + (PCI_FUNC(nesdev->pcidev->devfn)<<8));
+ // Destroy the CCQ
+ cqp_head = nesdev->cqp.sq_head++;
+ nesdev->cqp.sq_head &= nesdev->cqp.sq_size-1;
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+ cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] =
cpu_to_le32(NES_CQP_DESTROY_CQ);
+ cqp_wqe->wqe_words[NES_CQP_WQE_ID_IDX] = cpu_to_le32(
PCI_FUNC(nesdev->pcidev->devfn) ||
(PCI_FUNC(nesdev->pcidev->devfn)<<16));
+ // Destroy CQP
+ cqp_head = nesdev->cqp.sq_head++;
+ nesdev->cqp.sq_head &= nesdev->cqp.sq_size-1;
+ cqp_wqe = &nesdev->cqp.sq_vbase[cqp_head];
+ cqp_wqe->wqe_words[NES_CQP_WQE_OPCODE_IDX] =
cpu_to_le32(NES_CQP_DESTROY_QP | NES_CQP_QP_TYPE_CQP);
+ cqp_wqe->wqe_words[NES_CQP_WQE_ID_IDX] =
cpu_to_le32(nesdev->cqp.qp_id);
+
+ barrier();
+ // Ring doorbell (4 WQEs)
+ nes_write32(nesdev->regs+NES_WQE_ALLOC, 0x04800000 |
nesdev->cqp.qp_id);
+
+ // Wait for the destroy to complete
+ spin_unlock_irqrestore(&nesdev->cqp.lock, flags);
+ dprintk("%s:Waiting for DestroyQP.\n",__FUNCTION__);
+ cqp_head = (cqp_head+1)&(nesdev->cqp.sq_size-1);
+ ret =
wait_event_timeout(nesdev->cqp.waitq,(nesdev->cqp.sq_tail==cqp_head),
2);
+ dprintk("%s:Done waiting for DestroyQP. wait_event_timeout ret =
%d.\n",__FUNCTION__, ret);
+ // Free the control structures
+ pci_free_consistent(nesdev->pcidev, nesdev->cqp_mem_size,
nesdev->cqp.sq_vbase,
+ nesdev->cqp.sq_pbase);
+ list_del(&nesdev->list);
+}
+
+
+/**
+ * nes_adapter_free - free network adapter
+ *
+ * @param nesadapter
+ */
+void nes_adapter_free(struct nes_adapter *nesadapter)
+{
+ struct nes_adapter *tmp_adapter;
+ list_for_each_entry(tmp_adapter, &nes_adapter_list, list) {
+ dprintk("%s: Nes Adapter list entry = 0x%p.\n",
__FUNCTION__, tmp_adapter);
+ }
+
+ nesadapter->ref_count--;
+ if (!nesadapter->ref_count) {
+ dprintk("nes_adapter_free: Deleting adapter from adapter
list.\n" );
+ list_del(&nesadapter->list);
+ /* TODO: free the resources from the resource list */
+ dprintk("nes_adapter_free: Freeing adapter
structure.\n");
+ kfree(nesadapter);
+ }
+ dprintk("nes_adapter_free: Done.\n");
+}
+
+
More information about the general
mailing list