Subject: [PATCH 5/10] VIOC: New Network Device Driver

From: Misha Tomushev <[email-masked]> (by way of Misha Tomushev <[email-masked]>)
Date: Thu, 5 Oct 2006 11:54:03 -0700

Adding VIOC device driver. Device driver initialization/termination.

Signed-off-by: Misha Tomushev  <[email-masked]>

diff -uprN linux-2.6.17/drivers/net/vioc/vioc_vnic.h
linux-2.6.17.vioc/drivers/net/vioc/vioc_vnic.h
--- linux-2.6.17/drivers/net/vioc/vioc_vnic.h	1969-12-31 16:00:00.000000000
-0800
+++ linux-2.6.17.vioc/drivers/net/vioc/vioc_vnic.h	2006-10-04
10:10:04.000000000 -0700
@@ -0,0 +1,498 @@
+/*
+ * Fabric7 Systems Virtual IO Controller Driver
+ * Copyright (C) 2003-2005 Fabric7 Systems.  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
+ *
+ * http://www.fabric7.com/
+ *
+ * Maintainers:
+ *    [email-masked]
+ *
+ *
+ */
+#ifndef _VIOC_VNIC_H
+#define _VIOC_VNIC_H
+
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/pci.h>
+
+#include "f7/vnic_defs.h"
+#include "f7/vnic_hw_registers.h"
+#include "f7/vioc_pkts_defs.h"
+
+/*
+ * VIOC PCI constants
+ */
+#define PCI_VENDOR_ID_FABRIC7  0xfab7
+#define PCI_DEVICE_ID_VIOC_1   0x0001
+#define PCI_DEVICE_ID_VIOC_8   0x0008
+#define PCI_DEVICE_ID_IOAPIC   0x7459
+
+#define VIOC_DRV_MODULE_NAME	"vioc"
+
+#define F7PF_HLEN_MIN   8	/* Minimal (kl=0) header */
+#define F7PF_HLEN_STD   10	/* Standard (kl=1) header */
+
+#define VNIC_MAX_MTU   9180
+#define VNIC_STD_MTU   1500
+
+/* VIOC device constants */
+#define VIOC_MAX_RXDQ		16
+#define VIOC_MAX_RXCQ		16
+#define VIOC_MAX_RXQ		4
+#define VIOC_MAX_TXQ		4
+#define VIOC_NAME_LEN		16
+
+/*
+ * VIOC device state
+ */
+
+#define VIOC_STATE_INIT                0
+#define VIOC_STATE_UP          (VIOC_STATE_INIT + 1)
+
+#define RX_DESC_SIZE   sizeof (struct rx_pktBufDesc_Phys_w)
+#define RX_DESC_QUANT  (4096/RX_DESC_SIZE)
+
+#define RXC_DESC_SIZE  sizeof (struct rxc_pktDesc_Phys_w)
+#define RXC_DESC_QUANT (4096/RXC_DESC_SIZE)
+
+#define TX_DESC_SIZE   sizeof (struct tx_pktBufDesc_Phys_w)
+#define TX_DESC_QUANT  (4096/TX_DESC_SIZE)
+
+#define RXS_DESC_SIZE  sizeof (struct rxc_pktStatusBlock_w)
+
+#define VIOC_COPYOUT_THRESHOLD	128
+#define VIOC_RXD_BATCH_BITS		32
+#define ALL_BATCH_SW_OWNED		0
+#define ALL_BATCH_HW_OWNED		0xffffffff
+
+#define VIOC_ANY_VNIC			0
+#define VIOC_NONE_TO_HW			(u32) -1
+
+/*
+ * Status of the Rx operation as reflected in Rx Completion Descriptor
+ */
+#define GET_VNIC_RXC_STATUS(rxcd)      (\
+       GET_VNIC_RXC_BADCRC(rxcd) |\
+       GET_VNIC_RXC_BADLENGTH(rxcd) |\
+       GET_VNIC_RXC_BADSMPARITY(rxcd) |\
+       GET_VNIC_RXC_PKTABORT(rxcd)\
+       )
+#define VNIC_RXC_STATUS_OK_W           0
+
+#define VNIC_RXC_STATUS_MASK (\
+               VNIC_RXC_ISBADLENGTH_W | \
+               VNIC_RXC_ISBADCRC_W | \
+               VNIC_RXC_ISBADSMPARITY_W | \
+               VNIC_RXC_ISPKTABORT_W \
+       )
+
+#define VIOC_IRQ_PARAM_VIOC_ID(param)  \
+       (int) (((u64) param >> 28) & 0xf)
+#define VIOC_IRQ_PARAM_INTR_ID(param)  \
+       (int) ((u64) param & 0xffff)
+#define VIOC_IRQ_PARAM_PARAM_ID(param) \
+       (int) (((u64) param >> 16) & 0xff)
+
+#define VIOC_IRQ_PARAM_SET(vioc, intr, param) \
+               ((((u64) vioc & 0xf) << 28) | \
+               (((u64) param & 0xff) << 16) | \
+               ((u64) intr & 0xffff))
+/*
+ * Return status codes
+ */
+#define E_VIOCOK       0
+#define E_VIOCMAX      1
+#define E_VIOCINTERNAL 2
+#define E_VIOCNETREGERR 3
+#define E_VIOCPARMERR  4
+#define E_VIOCNOOP     5
+#define E_VIOCTXFULL   6
+#define E_VIOCIFNOTFOUND 7
+#define E_VIOCMALLOCERR 8
+#define E_VIOCORDERR   9
+#define E_VIOCHWACCESS 10
+#define E_VIOCHWNOTREADY 11
+#define E_ALLOCERR     12
+#define E_VIOCRXHW     13
+#define E_VIOCRXCEMPTY 14
+
+/*
+ * From the HW statnd point, every VNIC has 4 RxQ - receive queues.
+ * Every RxQ is mapped to RxDQ (a ring with buffers for Rx Packets)
+ * and RxC queue (a ring with descriptors that reflect the status of the
receive.
+ * I.e. when VIOC receives the packet on any of the 4 RxQ, it would use the
mapping to determine where
+ * to get buffer for the packet (RxDQ) and where to post the result of the
operation (RxC).
+ */
+
+struct rxd_q_prov {
+	u32 buf_size;
+	u32 entries;
+	u8 id;
+	u8 state;
+};
+
+struct vnic_prov_def {
+	struct rxd_q_prov rxd_ring[4];
+	u32 tx_entries;
+	u32 rxc_entries;
+	u8 rxc_id;
+	u8 rxc_intr_id;
+};
+
+struct vioc_run_param {
+	u32 rx_intr_timeout;
+	u32 rx_intr_cntout;
+	u32 rx_wdog_timeout;
+	u32 tx_pkts_per_bell;
+	u32 tx_pkts_per_irq;
+	int tx_intr_on_empty;
+};
+
+struct vioc_prov {
+	struct vnic_prov_def **vnic_prov_p;
+	struct vioc_run_param run_param;
+};
+
+struct vioc_irq {
+	int irq;
+	void *dev_id;
+};
+
+/*
+ * Wrapper around a pointer to a socket buffer
+ */
+struct vbuf {
+	volatile struct sk_buff *skb;
+	volatile dma_addr_t dma;
+	volatile u32 length;
+	volatile u32 special;
+	volatile unsigned long time_stamp;
+};
+
+struct rxc;
+
+/* Receive Completion set - RxC + NAPI device */
+struct napi_poll {
+	volatile u8 enabled;	/* if 0, Rx resource are not available */
+	volatile u8 stopped;	/* if 1, NAPI has stopped servicing set */
+	struct net_device poll_dev;	/* for NAPI */
+	u64 rx_interrupts;	/* Number of Rx Interrupts for VIOC stats */
+	struct rxc *rxc;
+};
+
+/* Rx Completion Queue */
+struct rxc {
+	u8 rxc_id;
+	u8 interrupt_id;
+	spinlock_t lock;
+	struct rxc_pktDesc_Phys_w *desc;
+	dma_addr_t dma;
+	u32 count;
+	u32 sw_idx;
+	u32 quota;
+	u32 budget;
+	void __iomem *va_of_vreg_ihcu_rxcintpktcnt;
+	void __iomem *va_of_vreg_ihcu_rxcinttimer;
+	void __iomem *va_of_vreg_ihcu_rxcintctl;
+	struct vioc_device *viocdev;
+	struct napi_poll napi;
+};
+
+/* Rx Descriptor Queue */
+struct rxdq {
+	u8 rxdq_id;
+	/* pointer to the Rx Buffer descriptor queue memory */
+	struct rx_pktBufDesc_Phys_w *desc;
+	dma_addr_t dma;
+	struct vbuf *vbuf;
+	u32 count;
+	u16 rx_buf_size;
+	/* A bit map of descriptors: 0 - owned by SW, 1 - owned by HW */
+	u32 *dmap;
+	u32 dmap_count;
+	/* dmap_idx is needed for proc fs */
+	u32 dmap_idx;
+	/* Descriptor desginated as a "fence", i.e. owned by SW. */
+	volatile u32 fence;
+	/* A counter that, when expires, forces a call to vioc_next_fence_run() */
+	volatile u32 skip_fence_run;
+	volatile u32 run_to_end;
+	volatile u32 to_hw;
+	volatile u32 starvations;
+	u32 prev_rxd_id;
+	u32 err_cnt;
+	u32 reset_cnt;
+	struct vioc_device *viocdev;
+};
+
+/* Tx Buffer Descriptor queue */
+struct txq {
+	u8 txq_id;		/* always TXQ0 for now */
+	u8 vnic_id;
+
+	spinlock_t lock;	/* interrupt-safe */
+	/*
+	 * Shadow of the TxD Control Register, keep it here, so we do
+	 * not have to read from HW
+	 */
+	u32 shadow_VREG_VENG_TXD_CTL;
+	/*
+	 * Address of the TxD Control Register when we ring the
+	 * bell. Keep this always ready, for expediency.
+	 */
+	void __iomem *va_of_vreg_veng_txd_ctl;
+	/*
+	 * pointer to the Tx Buffer Descriptor queue memory
+	 */
+	struct tx_pktBufDesc_Phys_w *desc;
+	dma_addr_t dma;
+	struct vbuf *vbuf;
+	u32 count;
+	u32 tx_pkts_til_irq;
+	u32 tx_pkts_til_bell;
+	u32 bells;
+	int do_ring_bell;
+	/* next descriptor to use for Tx */
+	volatile u32 next_to_use;
+	/* next descriptor to check completion of Tx */
+	volatile u32 next_to_clean;
+	/* Frags count */
+	volatile u32 frags;
+	/* Empty Tx descriptor slots */
+	volatile u32 empty;
+	u32 wraps;
+	u32 full;
+};
+
+/*  Rx Completion Status Block */
+struct rxs {
+	struct rxc_pktStatusBlock_w *block;
+	dma_addr_t dma;
+};
+
+typedef enum { RX_WDOG_DISABLED, RX_WDOG_EXPECT_PKT,
+	RX_WDOG_EXPECT_WDOGPKT
+} wdog_state_t;
+
+struct vioc_device_stats {
+	u64 tx_tasklets;	/* Number of Tx Interrupts */
+	u64 tx_timers;		/* Number of Tx watchdog timers */
+};
+
+#define        NETIF_STOP_Q    0xdead
+#define NETIF_START_Q  0xfeed
+
+struct vnic_device_stats {
+	u64 rx_fragment_errors;
+	u64 rx_dropped;
+	u64 skb_enqueued;	/* Total number of skb's enqueued */
+	u64 skb_freed;		/* Total number of skb's freed */
+	u32 netif_stops;	/* Number of times Tx was stopped */
+	u32 netif_last;		/* Last netif_  command */
+	u64 tx_on_empty_interrupts;	/* Number of Tx Empty Interrupts */
+	u32 headroom_misses;	/* Number of headroom misses */
+	u32 headroom_miss_drops;	/* Number of headroom misses */
+};
+
+struct vioc_ba {
+       void __iomem *virt;
+       unsigned long long phy;
+       unsigned long len;
+};
+
+struct vioc_device {
+	char name[VIOC_NAME_LEN];
+	u32 vioc_bits_version;
+	u32 vioc_bits_subversion;
+
+	u8 viocdev_idx;
+	u8 vioc_state;		/* Initialization state */
+	u8 mgmt_state;		/*  Management state */
+	u8 highdma;
+
+	u32 vnics_map;
+	u32 vnics_admin_map;
+	u32 vnics_link_map;
+
+	struct vioc_ba ba;	/* VIOC PCI Dev Base Address: virtual and phy */
+	struct vioc_ba ioapic_ba;	/* VIOC's IOAPIC Base Address: virtual and phy */
+	struct pci_dev *pdev;
+
+	/*
+	 * An array of pointers to net_device structures for
+	 * every subordinate VNIC
+	 */
+	struct net_device *vnic_netdev[VIOC_MAX_VNICS];
+	/*
+	 * An array describing all Rx Completion Descriptor Queues in VIOC
+	 */
+	struct rxc *rxc_p[VIOC_MAX_RXCQ];
+	struct rxc rxc_buf[VIOC_MAX_RXCQ];
+	/*
+	 * An array describing all Rx Descriptor Queues in VIOC
+	 */
+	struct rxdq *rxd_p[VIOC_MAX_RXDQ];
+	struct rxdq rxd_buf[VIOC_MAX_RXDQ];
+
+	/* Rx Completion Status Block */
+	struct rxs rxcstat;
+
+	/* ----- SIM SPECIFIC ------ */
+	/* * Round-robbin over Rx Completion queues */
+	u32 next_rxc_to_use;
+	/* Round-robbin over RxDQs, when checking them out */
+	u32 next_rxdq_to_use;
+
+	struct vioc_prov prov;	/* VIOC provisioning info */
+	struct vioc_device_stats vioc_stats;
+
+	struct timer_list bmc_wd_timer;
+	int bmc_wd_timer_active;
+
+	struct timer_list tx_timer;
+	int tx_timer_active;
+
+	u32 num_rx_irqs;
+	u32 num_irqs;
+	u32 last_msg_to_sim;
+};
+
+#define VIOC_BMC_INTR0 (1 << 0)
+#define VIOC_BMC_INTR1 (1 << 1)
+#define VIOC_BMC_INTR2 (1 << 2)
+#define VIOC_BMC_INTR3 (1 << 3)
+#define VIOC_BMC_INTR4 (1 << 4)
+#define VIOC_BMC_INTR5 (1 << 5)
+#define VIOC_BMC_INTR6 (1 << 6)
+#define VIOC_BMC_INTR7 (1 << 7)
+#define VIOC_BMC_INTR8 (1 << 8)
+#define VIOC_BMC_INTR9 (1 << 9)
+#define VIOC_BMC_INTR10        (1 << 10)
+#define VIOC_BMC_INTR11        (1 << 11)
+#define VIOC_BMC_INTR12        (1 << 12)
+#define VIOC_BMC_INTR13        (1 << 13)
+#define VIOC_BMC_INTR14        (1 << 14)
+#define VIOC_BMC_INTR15        (1 << 15)
+#define VIOC_BMC_INTR16        (1 << 16)
+#define VIOC_BMC_INTR17        (1 << 17)
+
+#define VNIC_NEXT_IDX(i, count) ((count == 0) ? 0: (((i) + 1) % (count)))
+#define VNIC_PREV_IDX(i, count) ((count == 0) ? 0: ((((i) == 0) ? ((count) -
1): ((i) - 1))))
+
+#define VNIC_RING_BELL(vnic, q_idx) vnic_ring_bell(vnic, q_idx)
+
+#define TXDS_REQUIRED(skb) 1
+
+#define TXD_WATER_MARK                 8
+
+#define GET_DESC_PTR(R, i, type) (&(((struct type *)((R)->desc))[i]))
+#define RXD_PTR(R, i)          GET_DESC_PTR(R, i, rx_pktBufDesc_Phys_w)
+#define TXD_PTR(R, i)          GET_DESC_PTR(R, i, tx_pktBufDesc_Phys_w)
+#define RXC_PTR(R, i)          GET_DESC_PTR(R, i, rxc_pktBufDesc_Phys_w)
+
+/* Receive packet fragments */
+
+/* VNIC DEVICE */
+struct vnic_device {
+	u8 vnic_id;
+	u8 rxc_id;
+	u8 rxc_intr_id;
+
+	u32 qmap;		/* VNIC rx queues mappings */
+	u32 vnic_q_en;		/* VNIC queues enables */
+
+	struct txq txq;
+	struct vioc_device *viocdev;
+	struct net_device *netdev;
+	struct net_device_stats net_stats;
+	struct vnic_device_stats vnic_stats;
+
+	u8 hw_mac[ETH_ALEN];
+};
+
+/* vioc_transmit.c */
+extern int vioc_vnic_init(struct net_device *);
+extern void vioc_tx_timer(unsigned long data);
+
+/* vioc_driver.c */
+extern struct vioc_device *vioc_viocdev(u32 vioc_id);
+extern struct net_device *vioc_alloc_vnicdev(struct vioc_device *, int);
+
+/* vioc_irq.c */
+extern int vioc_irq_init(void);
+extern void vioc_irq_exit(void);
+extern void vioc_free_irqs(u32 viocdev_idx);
+extern int vioc_request_irqs(u32 viocdev_idx);
+
+extern int vioc_set_intr_func_param(int viocdev_idx, int intr_idx,
+				    int intr_param);
+
+extern void vioc_rxc_interrupt(void *input_param);
+extern void vioc_tx_interrupt(void *input_param);
+extern void vioc_bmc_interrupt(void *input_param);
+
+/* vioc_receive.c */
+extern int vioc_rx_poll(struct net_device *dev, int *budget);
+extern int vioc_next_fence_run(struct rxdq *);
+
+/* spp.c */
+extern int spp_init(void);
+extern void spp_terminate(void);
+extern void spp_msg_from_sim(int);
+
+/* spp_vnic.c */
+extern int spp_vnic_init(void);
+extern void spp_vnic_exit(void);
+
+/* vioc_spp.c */
+extern void vioc_vnic_prov(int, u32, u32, int);
+extern struct vnic_prov_def **vioc_prov_get(int);
+extern void vioc_hb_to_bmc(int vioc_id);
+extern int vioc_handle_reset_request(int);
+extern void vioc_os_reset_notifier_exit(void);
+extern void vioc_os_reset_notifier_init(void);
+
+
+
+static inline void vioc_rxc_interrupt_disable(struct rxc *rxc)
+{
+	writel(3, rxc->va_of_vreg_ihcu_rxcintctl);
+}
+
+static inline void vioc_rxc_interrupt_enable(struct rxc *rxc)
+{
+	writel(0, rxc->va_of_vreg_ihcu_rxcintctl);
+}
+
+static inline void vioc_rxc_interrupt_clear_pend(struct rxc *rxc)
+{
+	writel(2, rxc->va_of_vreg_ihcu_rxcintctl);
+}
+
+#define POLL_WEIGHT 		32
+#define RX_INTR_TIMEOUT		2
+#define RX_INTR_PKT_CNT		8
+#define TX_PKTS_PER_IRQ		64
+#define TX_PKTS_PER_BELL	1
+#define VIOC_CSUM_OFFLOAD	CHECKSUM_HW
+#define VIOC_TRACE			0
+
+#define VIOC_LISTEN_GROUP	1
+
+#endif				/* _VIOC_VNIC_H */
diff -uprN linux-2.6.17/drivers/net/vioc/vioc_driver.c
linux-2.6.17.vioc/drivers/net/vioc/vioc_driver.c
--- linux-2.6.17/drivers/net/vioc/vioc_driver.c	1969-12-31 16:00:00.000000000
-0800
+++ linux-2.6.17.vioc/drivers/net/vioc/vioc_driver.c	2006-10-04
10:51:24.000000000 -0700
@@ -0,0 +1,872 @@
+/*
+ * Fabric7 Systems Virtual IO Controller Driver
+ * Copyright (C) 2003-2005 Fabric7 Systems.  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
+ *
+ * http://www.fabric7.com/
+ *
+ * Maintainers:
+ *    [email-masked]
+ *
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/compiler.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/sysdev.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/if_vlan.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/notifier.h>
+#include <linux/errno.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+#include "f7/vnic_hw_registers.h"
+#include "f7/vnic_defs.h"
+
+#include <linux/moduleparam.h>
+#include "vioc_vnic.h"
+#include "vioc_api.h"
+
+#include "driver_version.h"
+
+
+MODULE_AUTHOR("[email-masked]");
+MODULE_DESCRIPTION("VIOC interface driver");
+MODULE_LICENSE("GPL");
+
+MODULE_VERSION(VIOC_DISPLAY_VERSION);
+
+/*
+ * Standard parameters for ring provisioning.  Single TxQ per VNIC.
+ * Two RX sets per VIOC, with 3 RxDs, 1 RxC, 1 Rx interrupt per set.
+ */
+
+#define TXQ_SIZE			1024
+#define TX_INTR_ON_EMPTY	0
+
+#define VNIC_RXQ_MAP		0xf	/* Bits 0-3 for 4 VNIC queues */
+#define RXDQ_PERSET_COUNT	3
+#define RXDQ_COUNT			(RXSET_COUNT * RXDQ_PERSET_COUNT)
+/* RXDQ sizes (entry counts) must be multiples of this */
+#define RXDQ_ALIGN			VIOC_RXD_BATCH_BITS
+#define RXDQ_SIZE			2048
+#define RXDQ_JUMBO_SIZE		ALIGN(RXDQ_SIZE, RXDQ_ALIGN)
+#define RXDQ_STD_SIZE		ALIGN(RXDQ_SIZE, RXDQ_ALIGN)
+#define RXDQ_SMALL_SIZE		ALIGN(RXDQ_SIZE, RXDQ_ALIGN)
+#define RXDQ_EXTRA_SIZE		ALIGN(RXDQ_SIZE, RXDQ_ALIGN)
+
+#define RXC_COUNT			RXSET_COUNT
+#define RXC_SIZE
(RXDQ_JUMBO_SIZE+RXDQ_STD_SIZE+RXDQ_SMALL_SIZE+RXDQ_EXTRA_SIZE)
+
+/* VIOC devices */
+static struct pci_device_id vioc_pci_tbl[] __devinitdata = {
+	{PCI_DEVICE(PCI_VENDOR_ID_FABRIC7, PCI_DEVICE_ID_VIOC_1)},
+	{PCI_DEVICE(PCI_VENDOR_ID_FABRIC7, PCI_DEVICE_ID_VIOC_8)},
+	{0,},
+};
+
+MODULE_DEVICE_TABLE(pci, vioc_pci_tbl);
+
+static spinlock_t vioc_idx_lock = SPIN_LOCK_UNLOCKED;
+static unsigned vioc_idx = 0;
+static struct vioc_device *vioc_devices[VIOC_MAX_VIOCS];
+
+static struct vioc_device *viocdev_new(int viocdev_idx, struct pci_dev
 *pdev) +{
+	int k;
+	struct vioc_device *viocdev;
+
+	if (viocdev_idx >= VIOC_MAX_VIOCS)
+		return NULL;
+
+	viocdev = kmalloc(sizeof(struct vioc_device), GFP_KERNEL);
+	if (!viocdev)
+		return viocdev;
+
+	memset(viocdev, 0, sizeof(struct vioc_device));
+
+	viocdev->pdev = pdev;
+	viocdev->vioc_state = VIOC_STATE_INIT;
+	viocdev->viocdev_idx = viocdev_idx;
+
+	viocdev->prov.run_param.tx_pkts_per_bell = TX_PKTS_PER_BELL;
+	viocdev->prov.run_param.tx_pkts_per_irq = TX_PKTS_PER_IRQ;
+	viocdev->prov.run_param.tx_intr_on_empty = TX_INTR_ON_EMPTY;
+	viocdev->prov.run_param.rx_intr_timeout = RX_INTR_TIMEOUT;
+	viocdev->prov.run_param.rx_intr_cntout = RX_INTR_PKT_CNT;
+	viocdev->prov.run_param.rx_wdog_timeout = HZ / 4;
+
+	for (k = 0; k < VIOC_MAX_VNICS; k++) {
+		viocdev->vnic_netdev[k] = NULL;	/* spp */
+	}
+
+	vioc_devices[viocdev_idx] = viocdev;
+
+	return viocdev;
+}
+
+/*
+ * Remove all Rx descriptors from the queue.
+ */
+static void vioc_clear_rxq(struct vioc_device *viocdev, struct rxdq *rxdq)
+{
+	int j;
+
+	/* Disable this queue */
+	vioc_ena_dis_rxd_q(viocdev->viocdev_idx, rxdq->rxdq_id, 0);
+
+	if (rxdq->vbuf == NULL)
+		return;
+
+	for (j = 0; j < rxdq->count; j++) {
+		struct rx_pktBufDesc_Phys_w *rxd;
+		if (rxdq->vbuf[j].skb) {
+			pci_unmap_single(rxdq->viocdev->pdev,
+					 rxdq->vbuf[j].dma,
+					 rxdq->rx_buf_size, PCI_DMA_FROMDEVICE);
+			dev_kfree_skb_any((struct sk_buff *)rxdq->vbuf[j].skb);
+			rxdq->vbuf[j].skb = NULL;
+			rxdq->vbuf[j].dma = (dma_addr_t) NULL;
+		}
+		rxd = RXD_PTR(rxdq, j);
+		wmb();
+		CLR_VNIC_RX_OWNED(rxd);
+	}
+}
+
+/*
+ * Refill an empty Rx queue with Rx with Rx buffers
+ */
+static int vioc_refill_rxq(struct vioc_device *viocdev, struct rxdq *rxdq)
+{
+	int ret = 0;
+
+	memset(rxdq->vbuf, 0, rxdq->count * sizeof(struct vbuf));
+	memset(rxdq->dmap, 0, rxdq->dmap_count * (VIOC_RXD_BATCH_BITS / 8));
+
+	rxdq->fence = 0;
+	rxdq->to_hw = VIOC_NONE_TO_HW;
+	rxdq->starvations = 0;
+	rxdq->run_to_end = 0;
+	rxdq->skip_fence_run = rxdq->dmap_count / 4;
+
+	ret = vioc_next_fence_run(rxdq);
+	BUG_ON(ret != 0);
+	return ret;
+}
+
+struct vioc_device *vioc_viocdev(u32 viocdev_idx)
+{
+	if (viocdev_idx >= vioc_idx) {
+		BUG_ON(viocdev_idx >= vioc_idx);
+		return NULL;
+	}
+
+	BUG_ON(vioc_devices[viocdev_idx] == NULL);
+	return vioc_devices[viocdev_idx];
+}
+
+static void vioc_bmc_wdog(unsigned long data)
+{
+
+	struct vioc_device *viocdev = (struct vioc_device *)data;
+
+	if (!viocdev->bmc_wd_timer_active)
+		return;
+
+	/*
+	 * Send Heartbeat message to BMC
+	 */
+	vioc_hb_to_bmc(viocdev->viocdev_idx);
+
+	/*
+	 * Reset the timer
+	 */
+	mod_timer(&viocdev->bmc_wd_timer, jiffies + HZ);
+
+	return;
+}
+
+static int extract_vnic_prov(struct vioc_device *viocdev,
+									struct vnic_device *vnicdev)
+{
+	struct vnic_prov_def *vr;
+	int j;
+
+	vr = viocdev->prov.vnic_prov_p[vnicdev->vnic_id];
+	if (vr == NULL) {
+		dev_err(&viocdev->pdev->dev, "vioc %d: vnic %d No provisioning set\n",
+		       viocdev->viocdev_idx, vnicdev->vnic_id);
+		return E_VIOCPARMERR;
+	}
+
+	if (vr->tx_entries == 0) {
+		dev_err(&viocdev->pdev->dev, "vioc %d: vnic %d Tx ring not provisioned\n",
+		       viocdev->viocdev_idx, vnicdev->vnic_id);
+		return E_VIOCPARMERR;
+	}
+
+	if (viocdev->rxc_p[vr->rxc_id] == NULL) {
+		dev_err(&viocdev->pdev->dev,
+		       "vioc %d: vnic %d RxC ring %d  not provisioned\n",
+		       viocdev->viocdev_idx, vnicdev->vnic_id, vr->rxc_id);
+		return E_VIOCPARMERR;
+	}
+
+	if (vr->rxc_intr_id >= viocdev->num_rx_irqs) {
+		dev_err(&viocdev->pdev->dev, "vioc %d: vnic %d IRQ %d INVALID (max %d)\n",
+		       viocdev->viocdev_idx, vnicdev->vnic_id,
+		       vr->rxc_intr_id, (viocdev->num_rx_irqs - 1));
+		return E_VIOCPARMERR;
+	}
+
+	vnicdev->txq.count = vr->tx_entries;
+	vnicdev->rxc_id = vr->rxc_id;
+	vnicdev->rxc_intr_id = vr->rxc_intr_id;
+
+	for (j = 0; j < 4; j++) {
+		struct rxd_q_prov *ring = &vr->rxd_ring[j];
+
+		if (ring->state == 0)
+			continue;
+
+		if (viocdev->rxd_p[ring->id] == NULL) {
+			dev_err(&viocdev->pdev->dev,
+			       "vioc %d: vnic %d RxD ring %d  not provisioned\n",
+			       viocdev->viocdev_idx, vnicdev->vnic_id,
+			       ring->id);
+			return E_VIOCPARMERR;
+		}
+
+		/* Set Rx queue ENABLE bit for BMC_VNIC_CFG register */
+		vnicdev->vnic_q_en |= (1 << j);
+
+	}
+	vnicdev->qmap =
+	    ((vr->rxd_ring[0].id & 0xf) << (0 + 8 * 0)) |
+	    ((vr->rxd_ring[0].state & 0x1) << (7 + 8 * 0)) |
+	    ((vr->rxd_ring[1].id & 0xf) << (0 + 8 * 1)) |
+	    ((vr->rxd_ring[1].state & 0x1) << (7 + 8 * 1)) |
+	    ((vr->rxd_ring[2].id & 0xf) << (0 + 8 * 2)) |
+	    ((vr->rxd_ring[2].state & 0x1) << (7 + 8 * 2)) |
+	    ((vr->rxd_ring[3].id & 0xf) << (0 + 8 * 3)) |
+	    ((vr->rxd_ring[3].state & 0x1) << (7 + 8 * 3));
+	return E_VIOCOK;
+}
+
+struct net_device *vioc_alloc_vnicdev(struct vioc_device *viocdev, int
vnic_id)
+{
+	struct net_device *netdev;
+	struct vnic_device *vnicdev;
+
+	netdev = alloc_etherdev(sizeof(struct vnic_device));
+	if (!netdev) {
+		return NULL;
+	}
+
+	viocdev->vnic_netdev[vnic_id] = netdev;
+	vnicdev = netdev_priv(netdev);
+	vnicdev->viocdev = viocdev;
+	vnicdev->netdev = netdev;
+	vnicdev->vnic_id = vnic_id;
+	sprintf(&netdev->name[0], "ve%d.%02d", viocdev->viocdev_idx, vnic_id);
+	netdev->init = &vioc_vnic_init;	/* called when it is registered */
+	vnicdev->txq.vnic_id = vnic_id;
+
+	if (extract_vnic_prov(viocdev, vnicdev) != E_VIOCOK) {
+		free_netdev(netdev);
+		return NULL;
+	}
+
+	return netdev;
+}
+
+static void vioc_free_resources(struct vioc_device *viocdev, u32 viocidx)
+{
+	int i;
+	struct napi_poll *napi_p;
+	int start_j = jiffies;
+	int delta_j = 0;
+
+	for (i = 0; i < VIOC_MAX_RXCQ; i++) {
+		struct rxc *rxc = viocdev->rxc_p[i];
+		if (rxc == NULL)
+			continue;
+
+		napi_p = &rxc->napi;
+		/* Tell NAPI poll to stop */
+		napi_p->enabled = 0;
+
+		/* Make sure that NAPI poll gets the message */
+		netif_rx_schedule(&napi_p->poll_dev);
+
+		/* Wait for an ack from NAPI that it stopped,
+		 * so we can release resources
+		 */
+		while (!napi_p->stopped) {
+			schedule();
+			delta_j = jiffies - start_j;
+			if (delta_j > 10 * HZ) {
+				/* Looks like NAPI didn;t get to run.
+				 * Bail out hoping, we are not stuck
+				 * in NAPI poll.
+				 */
+				break;
+			}
+		}
+		if (rxc->dma) {
+			pci_free_consistent(viocdev->pdev,
+					    rxc->count * RXC_DESC_SIZE,
+					    rxc->desc, rxc->dma);
+			rxc->desc = NULL;
+			rxc->dma = (dma_addr_t) NULL;
+		}
+	}
+
+	for (i = 0; i < VIOC_MAX_RXDQ; i++) {
+		struct rxdq *rxdq = viocdev->rxd_p[i];
+		if (rxdq == NULL)
+			continue;
+
+		vioc_ena_dis_rxd_q(viocidx, rxdq->rxdq_id, 0);
+		if (rxdq->vbuf) {
+			vioc_clear_rxq(viocdev, rxdq);
+			vfree(rxdq->vbuf);
+			rxdq->vbuf = NULL;
+		}
+		if (rxdq->dmap) {
+			vfree(rxdq->dmap);
+			rxdq->dmap = NULL;
+		}
+		if (rxdq->dma) {
+			pci_free_consistent(viocdev->pdev,
+					    rxdq->count * RX_DESC_SIZE,
+					    rxdq->desc, rxdq->dma);
+			rxdq->desc = NULL;
+			rxdq->dma = (dma_addr_t) NULL;
+		}
+		viocdev->rxd_p[rxdq->rxdq_id] = NULL;
+	}
+
+	/* Free RxC status block */
+	if (viocdev->rxcstat.dma) {
+		pci_free_consistent(viocdev->pdev, RXS_DESC_SIZE,
+				    viocdev->rxcstat.block,
+				    viocdev->rxcstat.dma);
+		viocdev->rxcstat.block = NULL;
+		viocdev->rxcstat.dma = (dma_addr_t) NULL;
+	}
+
+}
+
+/*
+ * Initialize rxsets - RxS, RxCs and RxDs and push to VIOC.
+ * Return negative errno on failure.
+ */
+static int vioc_alloc_resources(struct vioc_device *viocdev, u32 viocidx)
+{
+	int j, i;
+	int ret;
+	struct vnic_prov_def *vr;
+	struct napi_poll *napi_p;
+	struct rxc *rxc;
+
+	dev_err(&viocdev->pdev->dev, "vioc%d: ENTER %s\n", viocidx, __FUNCTION__);
+
+	/* Allocate Rx Completion Status block */
+	viocdev->rxcstat.block = (struct rxc_pktStatusBlock_w *)
+	    pci_alloc_consistent(viocdev->pdev,
+				 RXS_DESC_SIZE * VIOC_MAX_RXCQ,
+				 &viocdev->rxcstat.dma);
+	if (!viocdev->rxcstat.block) {
+		dev_err(&viocdev->pdev->dev, "vioc%d: Could not allocate RxS\n", viocidx);
+		ret = -ENOMEM;
+		goto error;
+	}
+	/* Tell VIOC about this RxC Status block */
+	ret = vioc_set_rxs(viocidx, viocdev->rxcstat.dma);
+	if (ret) {
+		dev_err(&viocdev->pdev->dev, "vioc%d: Could not set RxS\n", viocidx);
+		goto error;
+	}
+
+	/* Based on provisioning request, setup RxCs and RxDs */
+	for (i = 0; i < VIOC_MAX_VNICS; i++) {
+		vr = viocdev->prov.vnic_prov_p[i];
+		if (vr == NULL) {
+			dev_err(&viocdev->pdev->dev,
+			       "vioc %d: vnic %d No provisioning set\n",
+			       viocdev->viocdev_idx, i);
+			goto error;
+		}
+
+		if (vr->rxc_id >= VIOC_MAX_RXCQ) {
+			dev_err(&viocdev->pdev->dev, "vioc%d: INVALID RxC %d provisioned\n",
+			       viocidx, vr->rxc_id);
+			goto error;
+		}
+		rxc = viocdev->rxc_p[vr->rxc_id];
+		if (rxc == NULL) {
+			rxc = &viocdev->rxc_buf[vr->rxc_id];
+			viocdev->rxc_p[vr->rxc_id] = rxc;
+			rxc->rxc_id = vr->rxc_id;
+			rxc->viocdev = viocdev;
+			rxc->quota = POLL_WEIGHT;
+			rxc->budget = POLL_WEIGHT;
+			rxc->count = vr->rxc_entries;
+
+			/* Allocate RxC ring memory */
+			rxc->desc = (struct rxc_pktDesc_Phys_w *)
+			    pci_alloc_consistent(viocdev->pdev,
+						 rxc->count * RXC_DESC_SIZE,
+						 &rxc->dma);
+			if (!rxc->desc) {
+				dev_err(&viocdev->pdev->dev,
+				       "vioc%d: Can't allocate RxC ring %d for %d entries\n",
+				       viocidx, rxc->rxc_id, rxc->count);
+				ret = -ENOMEM;
+				goto error;
+			}
+			rxc->interrupt_id = vr->rxc_intr_id;
+
+			rxc->va_of_vreg_ihcu_rxcinttimer =
+			    (&viocdev->ba)->virt + GETRELADDR(VIOC_IHCU, 0,
+							      (VREG_IHCU_RXCINTTIMER
+							       +
+							       (rxc->
+								interrupt_id <<
+								2)));
+
+			rxc->va_of_vreg_ihcu_rxcintpktcnt =
+			    (&viocdev->ba)->virt + GETRELADDR(VIOC_IHCU, 0,
+							      (VREG_IHCU_RXCINTPKTCNT
+							       +
+							       (rxc->
+								interrupt_id <<
+								2)));
+			rxc->va_of_vreg_ihcu_rxcintctl =
+			    (&viocdev->ba)->virt + GETRELADDR(VIOC_IHCU, 0,
+							      (VREG_IHCU_RXCINTCTL
+							       +
+							       (rxc->
+								interrupt_id <<
+								2)));
+			/* Set parameter (rxc->rxc_id), that will be passed to interrupt code */
+			ret = vioc_set_intr_func_param(viocdev->viocdev_idx,
+						       rxc->interrupt_id,
+						       rxc->rxc_id);
+			if (ret) {
+				dev_err(&viocdev->pdev->dev,
+				       "vioc%d: Could not set PARAM for INTR ID %d\n",
+				       viocidx, rxc->interrupt_id);
+				goto error;
+			}
+
+			/* Register RxC ring and interrupt */
+			ret = vioc_set_rxc(viocidx, rxc);
+			if (ret) {
+				dev_err(&viocdev->pdev->dev,
+				       "vioc%d: Could not set RxC %d\n",
+				       viocidx, vr->rxc_id);
+				goto error;
+			}
+
+			/* Initialize NAPI poll structure and device */
+			napi_p = &rxc->napi;
+			napi_p->enabled = 1;	/* ready for Rx poll */
+			napi_p->stopped = 0;	/* NOT stopped */
+			napi_p->rxc = rxc;
+			napi_p->poll_dev.weight = POLL_WEIGHT;
+			napi_p->poll_dev.priv = &rxc->napi;	/* Note */
+			napi_p->poll_dev.poll = &vioc_rx_poll;
+
+			dev_hold(&napi_p->poll_dev);
+			/* Enable the poll device */
+			set_bit(__LINK_STATE_START, &napi_p->poll_dev.state);
+			netif_start_queue(&napi_p->poll_dev);
+		};
+
+		/* Allocate Rx rings */
+		for (j = 0; j < 4; j++) {
+			struct rxd_q_prov *ring = &vr->rxd_ring[j];
+			struct rxdq *rxdq;
+
+			if (ring->id >= VIOC_MAX_RXDQ) {
+				dev_err(&viocdev->pdev->dev,
+				       "vioc%d: BAD Provisioning request for RXD %d\n",
+				       viocidx, ring->id);
+				goto error;
+			}
+
+			rxdq = viocdev->rxd_p[ring->id];
+			if (rxdq != NULL)
+				continue;
+
+			if (ring->state == 0)
+				continue;
+
+			rxdq = &viocdev->rxd_buf[ring->id];
+			viocdev->rxd_p[ring->id] = rxdq;
+
+			rxdq->rxdq_id = ring->id;
+			rxdq->viocdev = viocdev;
+			rxdq->count = ring->entries;
+			rxdq->rx_buf_size = ring->buf_size;
+
+			/* skb array */
+			rxdq->vbuf = vmalloc(rxdq->count * sizeof(struct vbuf));
+			if (!rxdq->vbuf) {
+				dev_err(&viocdev->pdev->dev,
+				       "vioc%d: Can't allocate RxD vbuf (%d entries)\n",
+				       viocidx, rxdq->count);
+				ret = -ENOMEM;
+				goto error;
+			}
+
+			/* Ring memory */
+			rxdq->desc = (struct rx_pktBufDesc_Phys_w *)
+			    pci_alloc_consistent(viocdev->pdev,
+						 rxdq->count * RX_DESC_SIZE,
+						 &rxdq->dma);
+			if (!rxdq->desc) {
+				dev_err(&viocdev->pdev->dev,
+				       "vioc%d: Can't allocate RxD ring (%d entries)\n",
+				       viocidx, rxdq->count);
+				ret = -ENOMEM;
+				goto error;
+			}
+			rxdq->dmap_count = rxdq->count / VIOC_RXD_BATCH_BITS;
+			/* Descriptor ownership bit map */
+			rxdq->dmap = (u32 *)
+			    vmalloc(rxdq->dmap_count *
+				    (VIOC_RXD_BATCH_BITS / 8));
+			if (!rxdq->dmap) {
+				dev_err(&viocdev->pdev->dev,
+				       "vioc%d: Could not allocate dmap\n",
+				       viocidx);
+				ret = -ENOMEM;
+				goto error;
+			}
+			/* Refill ring */
+			ret = vioc_refill_rxq(viocdev, rxdq);
+			if (ret) {
+				dev_err(&viocdev->pdev->dev,
+				       "vioc%d: Could not fill rxdq%d\n",
+				       viocidx, rxdq->rxdq_id);
+				goto error;
+			}
+			/* Tell VIOC about this queue */
+			ret = vioc_set_rxdq(viocidx, VIOC_ANY_VNIC,
+					    rxdq->rxdq_id, rxdq->rx_buf_size,
+					    rxdq->dma, rxdq->count);
+			if (ret) {
+				dev_err(&viocdev->pdev->dev,
+				       "vioc%d: Could not set rxdq%d\n",
+				       viocidx, rxdq->rxdq_id);
+				goto error;
+			}
+			/* Enable this queue */
+			ret = vioc_ena_dis_rxd_q(viocidx, rxdq->rxdq_id, 1);
+			if (ret) {
+				dev_err(&viocdev->pdev->dev,
+				       "vioc%d: Could not enable rxdq%d\n",
+				       viocidx, rxdq->rxdq_id);
+				goto error;
+			}
+		}		/* for 4 rings */
+	}
+
+	return 0;
+
+      error:
+	/* Caller is responsible for calling vioc_free_rxsets() */
+	return ret;
+}
+
+static void vioc_remove(struct pci_dev *pdev)
+{
+	struct vioc_device *viocdev = pci_get_drvdata(pdev);
+
+	if (!viocdev)
+		return;
+
+	/* Disable interrupts */
+	vioc_free_irqs(viocdev->viocdev_idx);
+
+	viocdev->vioc_state = VIOC_STATE_INIT;
+
+	if (viocdev->bmc_wd_timer.function) {
+		viocdev->bmc_wd_timer_active = 0;
+		del_timer_sync(&viocdev->bmc_wd_timer);
+	}
+
+	if (viocdev->tx_timer.function) {
+		viocdev->tx_timer_active = 0;
+		del_timer_sync(&viocdev->tx_timer);
+	}
+
+	vioc_vnic_prov(viocdev->viocdev_idx, 0, 0, 1);
+
+	vioc_free_resources(viocdev, viocdev->viocdev_idx);
+
+	/* Reset VIOC chip */
+	vioc_sw_reset(viocdev->viocdev_idx);
+
+#if defined(CONFIG_MSIX_MOD)
+	if (viocdev->num_irqs > 4) {
+		pci_disable_msix(viocdev->pdev);
+	} else {
+		pci_release_regions(viocdev->pdev);
+	}
+#else
+	pci_release_regions(viocdev->pdev);
+#endif
+
+	iounmap(viocdev->ba.virt);
+
+	if (viocdev->viocdev_idx < VIOC_MAX_VIOCS)
+		vioc_devices[viocdev->viocdev_idx] = NULL;
+
+	kfree(viocdev);
+	pci_set_drvdata(pdev, NULL);
+
+	pci_disable_device(pdev);
+}
+
+/*
+ * vioc_probe - Device Initialization Routine
+ * @pdev: PCI device information struct (VIOC PCI device)
+ * @ent: entry in vioc_pci_tbl
+ *
+ * Returns 0 on success, negative on failure
+ *
+ */
+static int __devinit vioc_probe(struct pci_dev *pdev, const
+				struct pci_device_id *ent)
+{
+	int cur_vioc_idx;
+	struct vioc_device *viocdev;
+	unsigned long long mmio_start = 0, mmio_len;
+	u32 param1, param2;
+	int ret;
+
+	viocdev = pci_get_drvdata(pdev);
+	if (viocdev) {
+		cur_vioc_idx = viocdev->viocdev_idx;
+		BUG_ON(viocdev != NULL);	/* should not happen */
+	} else {
+		spin_lock(&vioc_idx_lock);
+		cur_vioc_idx = vioc_idx++;
+		spin_unlock(&vioc_idx_lock);
+		if (cur_vioc_idx < VIOC_MAX_VIOCS) {
+			viocdev = viocdev_new(cur_vioc_idx, pdev);
+			BUG_ON(viocdev == NULL);
+			pci_set_drvdata(pdev, viocdev);
+		} else {
+			dev_err(&pdev->dev,
+			       "vioc_id %d > maximum supported, aborting.\n",
+			       cur_vioc_idx);
+			return -ENODEV;
+		}
+	}
+
+	sprintf(viocdev->name, "vioc%d", cur_vioc_idx);
+
+	if ((ret = pci_enable_device(pdev))) {
+		dev_err(&pdev->dev, "vioc%d: Cannot enable PCI device\n",
+		       cur_vioc_idx);
+		goto vioc_probe_err;
+	}
+
+	if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
+		dev_err(&pdev->dev, "vioc%d: Cannot find PCI device base address\n",
+		       cur_vioc_idx);
+		ret = -ENODEV;
+		goto vioc_probe_err;
+	}
+
+	/* Initialize interrupts: get number if Rx IRQs, 2 or 16 are valid returns
*/
+	viocdev->num_rx_irqs = vioc_request_irqs(cur_vioc_idx);
+
+	if (viocdev->num_rx_irqs == 0) {
+		dev_err(&pdev->dev, "vioc%d: Request IRQ failed\n", cur_vioc_idx);
+		goto vioc_probe_err;
+	}
+
+	pci_set_master(pdev);
+
+	/* Configure DMA attributes. */
+	if ((ret = pci_set_dma_mask(pdev, DMA_64BIT_MASK))) {
+		if ((ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK))) {
+			dev_err(&pdev->dev, "vioc%d: No usable DMA configuration\n",
+			       cur_vioc_idx);
+			goto vioc_probe_err;
+		}
+	} else {
+		viocdev->highdma = 1;
+	}
+
+	mmio_start = pci_resource_start(pdev, 0);
+	mmio_len = pci_resource_len(pdev, 0);
+
+	viocdev->ba.virt = ioremap(mmio_start, mmio_len);
+	viocdev->ba.phy = mmio_start;
+	viocdev->ba.len = mmio_len;
+
+	/* Soft reset the chip; pick up versions */
+	vioc_sw_reset(cur_vioc_idx);
+
+	viocdev->vioc_bits_version &= 0xff;
+
+	if (viocdev->vioc_bits_version < 0x74) {
+		dev_err(&pdev->dev, "VIOC version %x not supported, aborting\n",
+		       viocdev->vioc_bits_version);
+		ret = -EINVAL;
+		goto vioc_probe_err;
+	}
+
+	dev_info(&pdev->dev, "vioc%d: Detected VIOC version %x.%x\n",
+	       cur_vioc_idx,
+	       viocdev->vioc_bits_version, viocdev->vioc_bits_subversion);
+
+	viocdev->prov.vnic_prov_p = vioc_prov_get(viocdev->num_rx_irqs);
+
+	/* Allocate and provision resources */
+	if ((ret = vioc_alloc_resources(viocdev, cur_vioc_idx))) {
+		dev_err(&pdev->dev, "vioc%d: Could not allocate resources\n",
+		       cur_vioc_idx);
+		goto vioc_probe_err;
+	}
+
+	/*
+	 * Initialize heartbeat (watchdog) timer:
+	 */
+	if (viocdev->viocdev_idx == 0) {
+		/* Heartbeat is delivered ONLY by "master" VIOC in a
+		 * partition */
+		init_timer(&viocdev->bmc_wd_timer);
+		viocdev->bmc_wd_timer.function = &vioc_bmc_wdog;
+		viocdev->bmc_wd_timer.expires = jiffies + HZ;
+		viocdev->bmc_wd_timer.data = (unsigned long)viocdev;
+		add_timer(&viocdev->bmc_wd_timer);
+		viocdev->bmc_wd_timer_active = 1;
+	}
+
+	/* Disable all watchdogs by default */
+	viocdev->bmc_wd_timer_active = 0;
+
+	/*
+	 * Initialize tx_timer (tx watchdog) timer:
+	 */
+	init_timer(&viocdev->tx_timer);
+	viocdev->tx_timer.function = &vioc_tx_timer;
+	viocdev->tx_timer.expires = jiffies + HZ / 4;
+	viocdev->tx_timer.data = (unsigned long)viocdev;
+	add_timer(&viocdev->tx_timer);
+	/* !!! TESTING ONLY !!! */
+	viocdev->tx_timer_active = 1;
+
+	viocdev->vnics_map = 0;
+
+	param1 = vioc_rrd(viocdev->viocdev_idx, VIOC_BMC, 0, VREG_BMC_VNIC_EN);
+	param2 = vioc_rrd(viocdev->viocdev_idx, VIOC_BMC, 0, VREG_BMC_PORT_EN);
+	vioc_vnic_prov(viocdev->viocdev_idx, param1, param2, 1);
+
+	viocdev->vioc_state = VIOC_STATE_UP;
+
+	return ret;
+
+      vioc_probe_err:
+	vioc_remove(pdev);
+	return ret;
+}
+
+/*
+ * Set up "version" as a driver attribute.
+ */
+static ssize_t show_version(struct device_driver *drv, char *buf)
+{
+	sprintf(buf, "%s\n", VIOC_DISPLAY_VERSION);
+	return strlen(buf) + 1;
+}
+
+static DRIVER_ATTR(version, S_IRUGO, show_version, NULL);
+
+static struct pci_driver vioc_driver = {
+	.name = VIOC_DRV_MODULE_NAME,
+	.id_table = vioc_pci_tbl,
+	.probe = vioc_probe,
+	.remove = __devexit_p(vioc_remove),
+};
+
+static int __init vioc_module_init(void)
+{
+	int ret = 0;
+
+	memset(&vioc_devices, 0, sizeof(vioc_devices));
+	vioc_idx = 0;
+	spin_lock_init(&vioc_idx_lock);
+
+	vioc_irq_init();
+	spp_init();
+
+	ret = pci_module_init(&vioc_driver);
+	if (ret) {
+		printk(KERN_ERR "%s: pci_module_init() -> %d\n", __FUNCTION__,
+		       ret);
+		vioc_irq_exit();
+		return ret;
+	} else
+		driver_create_file(&vioc_driver.driver, &driver_attr_version);
+
+	vioc_os_reset_notifier_init();
+
+	return ret;
+}
+
+static void __exit vioc_module_exit(void)
+{
+	vioc_irq_exit();
+	spp_terminate();
+	flush_scheduled_work();
+	vioc_os_reset_notifier_exit();
+	driver_remove_file(&vioc_driver.driver, &driver_attr_version);
+	pci_unregister_driver(&vioc_driver);
+}
+
+module_init(vioc_module_init);
+module_exit(vioc_module_exit);
+
+#ifdef EXPORT_SYMTAB
+EXPORT_SYMBOL(vioc_viocdev);
+#endif				/* EXPORT_SYMTAB */

--
Misha Tomushev
[email-masked]