[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

netxen_nic support for unified firmware images



I'd like to propose we add support for unified firmware images for
netxen nics for squeeze.

HP ships servers with netxen controllers, and we've seen problems with
old firmware/new driver combinations - basically the network runs
ridiculously slow, causing network installs to take many
hours. Flashing to the latest firmware resolves the issues - but
flashing a system (or 50) before install can be a pain (once you've
finally identified firmware as the problem). There's an online flash
tool you can use post-install but, as of this writing, it is not
very Debian friendly (distributed in an RPM, needs out of tree driver,
script has incompatible paths/bashisms).

netxen_nic can load updated firmware via request_firmware, and
recently a firmware blob got accepted into the linux-firmware tree w/
a non-free-compatible license, specifically for this reason. However,
this blob is in a newer "unified" format that the 2.6.32 driver didn't
yet support.

This patch cherry picks the change to add support for the unified rom
image, as well as several fixes that came after.

In addition, I'd like to also package the firmware blob. The blob
alone is 1.7 MB, whereas the entire contents of firmware-linux-nonfree
is 940K. That implies to me that it should be a new binary package,
but I don't feel strongly about that.


Any objections to these changes?


diff --git a/sid/linux-2.6/debian/changelog b/sid/linux-2.6/debian/changelog
index 56d05b1..2474c4a 100644
--- a/sid/linux-2.6/debian/changelog
+++ b/sid/linux-2.6/debian/changelog
@@ -35,6 +35,9 @@ linux-2.6 (2.6.32-22) UNRELEASED; urgency=low
   [ Bastian Blank ]
   * Use Breaks instead of Conflicts.
 
+  [ dann frazier ]
+  * netxen_nic: add support for loading unified firmware images
+
  -- Ben Hutchings <ben@decadent.org.uk>  Fri, 27 Aug 2010 08:38:26 +0100
 
 linux-2.6 (2.6.32-21) unstable; urgency=high
diff --git a/sid/linux-2.6/debian/patches/features/all/netxen-unified-fw-image.patch b/sid/linux-2.6/debian/patches/features/all/netxen-unified-fw-image.patch
new file mode 100644
index 0000000..04294e0
--- /dev/null
+++ b/sid/linux-2.6/debian/patches/features/all/netxen-unified-fw-image.patch
@@ -0,0 +1,1566 @@
+commit 5fe4d5c338291bbd504b8ebbd4d3931adfa61772
+Author: Amit Kumar Salecha <amit.salecha@qlogic.com>
+Date:   Mon Mar 29 02:43:44 2010 +0000
+
+    netxen: fix fw load from file
+    
+    Rarely: Fw file size can be unaligned to 8.
+    
+    Signed-off-by: Amit Kumar Salecha <amit.salecha@qlogic.com>
+    Signed-off-by: David S. Miller <davem@davemloft.net>
+
+commit 3111bf7ed99902687728eeec2ad068c2a7f8e5f0
+Author: Rajesh K Borundia <rajesh.borundia@qlogic.com>
+Date:   Mon Mar 29 02:43:43 2010 +0000
+
+    netxen: validate unified romimage
+    
+    Signed-off-by: Rajesh K Borundia <rajesh.borundia@qlogic.com>
+    Signed-off-by: Amit Kumar Salecha <amit.salecha@qlogic.com>
+    
+    Validate all sections of unified romimage, before accessing them,
+      to avoid seg fault.
+    
+    Signed-off-by: Amit Kumar Salecha <amit.salecha@qlogic.com>
+    Signed-off-by: David S. Miller <davem@davemloft.net>
+
+commit e9c75051c380aa07e237fd271a191f71cc5b79ef
+Author: Amit Kumar Salecha <amit.salecha@qlogic.com>
+Date:   Fri Mar 26 00:30:07 2010 +0000
+
+    netxen: fix bios version calculation
+    
+    Bios sub version from unified fw image is calculated incorrect.
+    
+    Signed-off-by: Amit Kumar Salecha <amit.salecha@qlogic.com>
+    Signed-off-by: David S. Miller <davem@davemloft.net>
+
+commit 31ae5cfdfd99db77ef6f213d4c35422320356d3b
+Author: Amit Kumar Salecha <amit.salecha@qlogic.com>
+Date:   Tue Dec 8 20:40:57 2009 +0000
+
+    netxen: fix unified fw size check
+    
+    o Unified firmware image size can be < 1 MB
+    
+    Signed-off-by: Amit Kumar Salecha <amit.salecha@qlogic.com>
+    Signed-off-by: David S. Miller <davem@davemloft.net>
+
+commit fd1ae25890ba3e63c62f4ba53519ec21bcc7181b
+Author: Dhananjay Phadke <dhananjay@netxen.com>
+Date:   Sat Dec 5 12:23:56 2009 +0000
+
+    netxen: fix firmware type check
+    
+    Unified firmware image may not contain MN type of firmware.
+    Driver should fall back to NOMN firmware type instead
+    of going to flash.
+    
+    Signed-off-by: Dhananjay Phadke <dhananjay@netxen.com>
+    Signed-off-by: Amit Kumar Salecha <amit.salecha@qlogic.com>
+    Signed-off-by: David S. Miller <davem@davemloft.net>
+
+commit 629073465ea87a2f5792225dc4647b9f965ee2d3
+Author: Amit Kumar Salecha <amit@netxen.com>
+Date:   Sat Oct 24 16:03:58 2009 +0000
+
+    netxen: support for new firmware file format
+    
+    Add support for extracting firmware from a unified
+    file format which embeds firmware images for all chip
+    revisions. Fallback to orginal file formats if new
+    image is not found.
+    
+    Signed-off-by: Amit Kumar Salecha <amit@netxen.com>
+    Signed-off-by: Dhananjay Phadke <dhananjay@netxen.com>
+    Signed-off-by: David S. Miller <davem@davemloft.net>
+
+commit 57cf596bef5692dc543b846cd1c05ea075270741
+Author: Dhananjay Phadke <dhananjay@netxen.com>
+Date:   Fri Oct 16 15:50:10 2009 +0000
+
+    netxen: fix error codes in for tools access
+    
+    [Backported to Debian's 2.6.26 by dann frazier <dannf@debian.org>]
+    
+    Use -EIO or -EINVAL as error codes, these can get passed up
+    to applications (tools).
+    
+    Signed-off-by: Dhananjay Phadke <dhananjay@netxen.com>
+    Signed-off-by: David S. Miller <davem@davemloft.net>
+
+commit 1648870d11e31d16b0c44d9a104ec68ebb5d9b58
+Author: Amit Kumar Salecha <amit@qlogic.com>
+Date:   Fri Oct 16 15:50:06 2009 +0000
+
+    netxen: defines for next revision
+    
+    Signed-off-by: Amit Kumar Salecha <amit@netxen.com>
+    Signed-off-by: Dhananjay Phadke <dhananjay@netxen.com>
+    Signed-off-by: David S. Miller <davem@davemloft.net>
+
+commit 43afdee1baceca4bdeccde45f98f5983302e80c2
+Author: Amit Kumar Salecha <amit@netxen.com>
+Date:   Tue Oct 13 05:31:41 2009 +0000
+
+    netxen: remove sub 64-bit mem accesses
+    
+    Sub 64-bit / unaligned access to oncard memory was only used
+    by old diagnostic tools, causes some intermittent issues when
+    memory controller agent is used.  The new access method was
+    added by commit ea6828b8aa3a8ebae8d7740f32f212ba1d2f0742
+    ("netxen: improve pci memory access").  Firmware init anyway
+    uses 8-byte strides.
+    
+    This also fixes address/offset calculation for NX2031 context
+    memory (SIU). For NX3031, SIU uses same register offsets
+    as packet memory (MIU).
+    
+    Signed-off-by: Amit Kumar Salecha <amit@netxen.com>
+    Signed-off-by: Dhananjay Phadke <dhananjay@netxen.com>
+    Signed-off-by: David S. Miller <davem@davemloft.net>
+diff -urpN a/drivers/net/netxen/netxen_nic.h b/drivers/net/netxen/netxen_nic.h
+--- a/drivers/net/netxen/netxen_nic.h	2010-08-30 19:40:21.000000000 -0600
++++ b/drivers/net/netxen/netxen_nic.h	2010-08-30 19:42:40.000000000 -0600
+@@ -117,9 +117,11 @@
+ #define NX_P3_B0		0x40
+ #define NX_P3_B1		0x41
+ #define NX_P3_B2		0x42
++#define NX_P3P_A0		0x50
+ 
+ #define NX_IS_REVISION_P2(REVISION)     (REVISION <= NX_P2_C1)
+ #define NX_IS_REVISION_P3(REVISION)     (REVISION >= NX_P3_A0)
++#define NX_IS_REVISION_P3P(REVISION)     (REVISION >= NX_P3P_A0)
+ 
+ #define FIRST_PAGE_GROUP_START	0
+ #define FIRST_PAGE_GROUP_END	0x100000
+@@ -419,6 +421,33 @@ struct status_desc {
+ 	__le64 status_desc_data[2];
+ } __attribute__ ((aligned(16)));
+ 
++/* UNIFIED ROMIMAGE *************************/
++#define NX_UNI_DIR_SECT_PRODUCT_TBL	0x0
++#define NX_UNI_DIR_SECT_BOOTLD		0x6
++#define NX_UNI_DIR_SECT_FW		0x7
++
++/*Offsets */
++#define NX_UNI_CHIP_REV_OFF		10
++#define NX_UNI_FLAGS_OFF		11
++#define NX_UNI_BIOS_VERSION_OFF 	12
++#define NX_UNI_BOOTLD_IDX_OFF		27
++#define NX_UNI_FIRMWARE_IDX_OFF 	29
++
++struct uni_table_desc{
++	uint32_t	findex;
++	uint32_t	num_entries;
++	uint32_t	entry_size;
++	uint32_t	reserved[5];
++};
++
++struct uni_data_desc{
++	uint32_t	findex;
++	uint32_t	size;
++	uint32_t	reserved[5];
++};
++
++/* UNIFIED ROMIMAGE *************************/
++
+ /* The version of the main data structure */
+ #define	NETXEN_BDINFO_VERSION 1
+ 
+@@ -485,11 +514,14 @@ struct status_desc {
+ #define NX_P2_MN_ROMIMAGE	0
+ #define NX_P3_CT_ROMIMAGE	1
+ #define NX_P3_MN_ROMIMAGE	2
+-#define NX_FLASH_ROMIMAGE	3
++#define NX_UNIFIED_ROMIMAGE	3
++#define NX_FLASH_ROMIMAGE	4
++#define NX_UNKNOWN_ROMIMAGE	0xff
+ 
+ #define NX_P2_MN_ROMIMAGE_NAME		"nxromimg.bin"
+ #define NX_P3_CT_ROMIMAGE_NAME		"nx3fwct.bin"
+ #define NX_P3_MN_ROMIMAGE_NAME		"nx3fwmn.bin"
++#define NX_UNIFIED_ROMIMAGE_NAME	"phanfw.bin"
+ #define NX_FLASH_ROMIMAGE_NAME		"flash"
+ 
+ extern char netxen_nic_driver_name[];
+@@ -1187,8 +1219,8 @@ struct netxen_adapter {
+ 	u32 (*crb_read)(struct netxen_adapter *, ulong);
+ 	int (*crb_write)(struct netxen_adapter *, ulong, u32);
+ 
+-	int (*pci_mem_read)(struct netxen_adapter *, u64, void *, int);
+-	int (*pci_mem_write)(struct netxen_adapter *, u64, void *, int);
++	int (*pci_mem_read)(struct netxen_adapter *, u64, u64 *);
++	int (*pci_mem_write)(struct netxen_adapter *, u64, u64);
+ 
+ 	unsigned long (*pci_set_window)(struct netxen_adapter *,
+ 			unsigned long long);
+@@ -1215,7 +1247,7 @@ struct netxen_adapter {
+ 	nx_nic_intr_coalesce_t coal;
+ 
+ 	unsigned long state;
+-	u32 resv5;
++	__le32 file_prd_off;	/*File fw product offset*/
+ 	u32 fw_version;
+ 	const struct firmware *fw;
+ };
+diff -urpN a/drivers/net/netxen/netxen_nic_hdr.h b/drivers/net/netxen/netxen_nic_hdr.h
+--- a/drivers/net/netxen/netxen_nic_hdr.h	2009-12-02 20:51:21.000000000 -0700
++++ b/drivers/net/netxen/netxen_nic_hdr.h	2010-08-30 19:42:40.000000000 -0600
+@@ -664,40 +664,47 @@ enum {
+ #define NETXEN_NIU_AP_STATION_ADDR_0(I)    (NETXEN_CRB_NIU+0xa0040+(I)*0x10000)
+ #define NETXEN_NIU_AP_STATION_ADDR_1(I)    (NETXEN_CRB_NIU+0xa0044+(I)*0x10000)
+ 
++
++#define TEST_AGT_CTRL	(0x00)
++
++#define TA_CTL_START	1
++#define TA_CTL_ENABLE	2
++#define TA_CTL_WRITE	4
++#define TA_CTL_BUSY	8
++
+ /*
+  *   Register offsets for MN
+  */
+-#define	MIU_CONTROL	       (0x000)
+-#define MIU_TEST_AGT_CTRL      (0x090)
+-#define MIU_TEST_AGT_ADDR_LO   (0x094)
+-#define MIU_TEST_AGT_ADDR_HI   (0x098)
+-#define MIU_TEST_AGT_WRDATA_LO (0x0a0)
+-#define MIU_TEST_AGT_WRDATA_HI (0x0a4)
+-#define MIU_TEST_AGT_WRDATA(i) (0x0a0+(4*(i)))
+-#define MIU_TEST_AGT_RDDATA_LO (0x0a8)
+-#define MIU_TEST_AGT_RDDATA_HI (0x0ac)
+-#define MIU_TEST_AGT_RDDATA(i) (0x0a8+(4*(i)))
+-#define MIU_TEST_AGT_ADDR_MASK 0xfffffff8
+-#define MIU_TEST_AGT_UPPER_ADDR(off) (0)
+-
+-/* MIU_TEST_AGT_CTRL flags. work for SIU as well */
+-#define MIU_TA_CTL_START        1
+-#define MIU_TA_CTL_ENABLE       2
+-#define MIU_TA_CTL_WRITE        4
+-#define MIU_TA_CTL_BUSY         8
+-
+-#define SIU_TEST_AGT_CTRL      (0x060)
+-#define SIU_TEST_AGT_ADDR_LO   (0x064)
+-#define SIU_TEST_AGT_ADDR_HI   (0x078)
+-#define SIU_TEST_AGT_WRDATA_LO (0x068)
+-#define SIU_TEST_AGT_WRDATA_HI (0x06c)
+-#define SIU_TEST_AGT_WRDATA(i) (0x068+(4*(i)))
+-#define SIU_TEST_AGT_RDDATA_LO (0x070)
+-#define SIU_TEST_AGT_RDDATA_HI (0x074)
+-#define SIU_TEST_AGT_RDDATA(i) (0x070+(4*(i)))
++#define MIU_TEST_AGT_BASE		(0x90)
++
++#define MIU_TEST_AGT_ADDR_LO		(0x04)
++#define MIU_TEST_AGT_ADDR_HI		(0x08)
++#define MIU_TEST_AGT_WRDATA_LO		(0x10)
++#define MIU_TEST_AGT_WRDATA_HI		(0x14)
++#define MIU_TEST_AGT_WRDATA(i)		(0x10+(4*(i)))
++#define MIU_TEST_AGT_RDDATA_LO		(0x18)
++#define MIU_TEST_AGT_RDDATA_HI		(0x1c)
++#define MIU_TEST_AGT_RDDATA(i)		(0x18+(4*(i)))
++
++#define MIU_TEST_AGT_ADDR_MASK		0xfffffff8
++#define MIU_TEST_AGT_UPPER_ADDR(off)	(0)
++
++/*
++ *   Register offsets for MS
++ */
++#define SIU_TEST_AGT_BASE		(0x60)
++
++#define SIU_TEST_AGT_ADDR_LO		(0x04)
++#define SIU_TEST_AGT_ADDR_HI		(0x18)
++#define SIU_TEST_AGT_WRDATA_LO		(0x08)
++#define SIU_TEST_AGT_WRDATA_HI		(0x0c)
++#define SIU_TEST_AGT_WRDATA(i)		(0x08+(4*(i)))
++#define SIU_TEST_AGT_RDDATA_LO		(0x10)
++#define SIU_TEST_AGT_RDDATA_HI		(0x14)
++#define SIU_TEST_AGT_RDDATA(i)		(0x10+(4*(i)))
+ 
+-#define SIU_TEST_AGT_ADDR_MASK 0x3ffff8
+-#define SIU_TEST_AGT_UPPER_ADDR(off) ((off)>>22)
++#define SIU_TEST_AGT_ADDR_MASK		0x3ffff8
++#define SIU_TEST_AGT_UPPER_ADDR(off)	((off)>>22)
+ 
+ /* XG Link status */
+ #define XG_LINK_UP	0x10
+diff -urpN a/drivers/net/netxen/netxen_nic_hw.c b/drivers/net/netxen/netxen_nic_hw.c
+--- a/drivers/net/netxen/netxen_nic_hw.c	2009-12-02 20:51:21.000000000 -0700
++++ b/drivers/net/netxen/netxen_nic_hw.c	2010-08-30 19:42:40.000000000 -0600
+@@ -326,7 +326,7 @@ netxen_pcie_sem_lock(struct netxen_adapt
+ 		if (done == 1)
+ 			break;
+ 		if (++timeout >= NETXEN_PCIE_SEM_TIMEOUT)
+-			return -1;
++			return -EIO;
+ 		msleep(1);
+ 	}
+ 
+@@ -1116,7 +1116,7 @@ netxen_nic_pci_change_crbwindow_128M(str
+ }
+ 
+ /*
+- * Return -1 if off is not valid,
++ * Returns < 0 if off is not valid,
+  *	 1 if window access is needed. 'off' is set to offset from
+  *	   CRB space in 128M pci map
+  *	 0 if no window access is needed. 'off' is set to 2M addr
+@@ -1129,7 +1129,7 @@ netxen_nic_pci_get_crb_addr_2M(struct ne
+ 
+ 
+ 	if (*off >= NETXEN_CRB_MAX)
+-		return -1;
++		return -EINVAL;
+ 
+ 	if (*off >= NETXEN_PCI_CAMQM && (*off < NETXEN_PCI_CAMQM_2M_END)) {
+ 		*off = (*off - NETXEN_PCI_CAMQM) + NETXEN_PCI_CAMQM_2M_BASE +
+@@ -1138,7 +1138,7 @@ netxen_nic_pci_get_crb_addr_2M(struct ne
+ 	}
+ 
+ 	if (*off < NETXEN_PCI_CRBSPACE)
+-		return -1;
++		return -EINVAL;
+ 
+ 	*off -= NETXEN_PCI_CRBSPACE;
+ 
+@@ -1251,25 +1251,26 @@ netxen_nic_hw_write_wx_2M(struct netxen_
+ 
+ 	rv = netxen_nic_pci_get_crb_addr_2M(adapter, &off);
+ 
+-	if (rv == -1) {
+-		printk(KERN_ERR "%s: invalid offset: 0x%016lx\n",
+-				__func__, off);
+-		dump_stack();
+-		return -1;
++	if (rv == 0) {
++		writel(data, (void __iomem *)off);
++		return 0;
+ 	}
+ 
+-	if (rv == 1) {
++	if (rv > 0) {
++		/* indirect access */
+ 		write_lock_irqsave(&adapter->adapter_lock, flags);
+ 		crb_win_lock(adapter);
+ 		netxen_nic_pci_set_crbwindow_2M(adapter, &off);
+ 		writel(data, (void __iomem *)off);
+ 		crb_win_unlock(adapter);
+ 		write_unlock_irqrestore(&adapter->adapter_lock, flags);
+-	} else
+-		writel(data, (void __iomem *)off);
+-
++		return 0;
++	}
+ 
+-	return 0;
++	dev_err(&adapter->pdev->dev,
++			"%s: invalid offset: 0x%016lx\n", __func__, off);
++	dump_stack();
++	return -EIO;
+ }
+ 
+ static u32
+@@ -1281,24 +1282,24 @@ netxen_nic_hw_read_wx_2M(struct netxen_a
+ 
+ 	rv = netxen_nic_pci_get_crb_addr_2M(adapter, &off);
+ 
+-	if (rv == -1) {
+-		printk(KERN_ERR "%s: invalid offset: 0x%016lx\n",
+-				__func__, off);
+-		dump_stack();
+-		return -1;
+-	}
++	if (rv == 0)
++		return readl((void __iomem *)off);
+ 
+-	if (rv == 1) {
++	if (rv > 0) {
++		/* indirect access */
+ 		write_lock_irqsave(&adapter->adapter_lock, flags);
+ 		crb_win_lock(adapter);
+ 		netxen_nic_pci_set_crbwindow_2M(adapter, &off);
+ 		data = readl((void __iomem *)off);
+ 		crb_win_unlock(adapter);
+ 		write_unlock_irqrestore(&adapter->adapter_lock, flags);
+-	} else
+-		data = readl((void __iomem *)off);
++		return data;
++	}
+ 
+-	return data;
++	dev_err(&adapter->pdev->dev,
++			"%s: invalid offset: 0x%016lx\n", __func__, off);
++	dump_stack();
++	return -1;
+ }
+ 
+ static int netxen_pci_set_window_warning_count;
+@@ -1485,101 +1486,69 @@ netxen_nic_pci_set_window_2M(struct netx
+ 
+ static int
+ netxen_nic_pci_mem_write_128M(struct netxen_adapter *adapter,
+-		u64 off, void *data, int size)
++		u64 off, u64 data)
+ {
+ 	unsigned long   flags;
+-	int	     i, j, ret = 0, loop, sz[2], off0;
+-	uint32_t      temp;
+-	uint64_t      off8, tmpw, word[2] = {0, 0};
++	int j, ret;
++	u32 temp, off_lo, off_hi, addr_hi, data_hi, data_lo;
+ 	void __iomem *mem_crb;
+ 
+-	if (size != 8)
++	/* Only 64-bit aligned access */
++	if (off & 7)
+ 		return -EIO;
+ 
++	/* P2 has different SIU and MIU test agent base addr */
+ 	if (ADDR_IN_RANGE(off, NETXEN_ADDR_QDR_NET,
+ 				NETXEN_ADDR_QDR_NET_MAX_P2)) {
+-		mem_crb = pci_base_offset(adapter, NETXEN_CRB_QDR_NET);
++		mem_crb = pci_base_offset(adapter,
++				NETXEN_CRB_QDR_NET+SIU_TEST_AGT_BASE);
++		addr_hi = SIU_TEST_AGT_ADDR_HI;
++		data_lo = SIU_TEST_AGT_WRDATA_LO;
++		data_hi = SIU_TEST_AGT_WRDATA_HI;
++		off_lo = off & SIU_TEST_AGT_ADDR_MASK;
++		off_hi = SIU_TEST_AGT_UPPER_ADDR(off);
+ 		goto correct;
+ 	}
+ 
+ 	if (ADDR_IN_RANGE(off, NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX)) {
+-		mem_crb = pci_base_offset(adapter, NETXEN_CRB_DDR_NET);
++		mem_crb = pci_base_offset(adapter,
++				NETXEN_CRB_DDR_NET+MIU_TEST_AGT_BASE);
++		addr_hi = MIU_TEST_AGT_ADDR_HI;
++		data_lo = MIU_TEST_AGT_WRDATA_LO;
++		data_hi = MIU_TEST_AGT_WRDATA_HI;
++		off_lo = off & MIU_TEST_AGT_ADDR_MASK;
++		off_hi = 0;
+ 		goto correct;
+ 	}
+ 
+ 	return -EIO;
+ 
+ correct:
+-	off8 = off & 0xfffffff8;
+-	off0 = off & 0x7;
+-	sz[0] = (size < (8 - off0)) ? size : (8 - off0);
+-	sz[1] = size - sz[0];
+-	loop = ((off0 + size - 1) >> 3) + 1;
+-
+-	if ((size != 8) || (off0 != 0))  {
+-		for (i = 0; i < loop; i++) {
+-			if (adapter->pci_mem_read(adapter,
+-				off8 + (i << 3), &word[i], 8))
+-				return -1;
+-		}
+-	}
+-
+-	switch (size) {
+-	case 1:
+-		tmpw = *((uint8_t *)data);
+-		break;
+-	case 2:
+-		tmpw = *((uint16_t *)data);
+-		break;
+-	case 4:
+-		tmpw = *((uint32_t *)data);
+-		break;
+-	case 8:
+-	default:
+-		tmpw = *((uint64_t *)data);
+-		break;
+-	}
+-	word[0] &= ~((~(~0ULL << (sz[0] * 8))) << (off0 * 8));
+-	word[0] |= tmpw << (off0 * 8);
+-
+-	if (loop == 2) {
+-		word[1] &= ~(~0ULL << (sz[1] * 8));
+-		word[1] |= tmpw >> (sz[0] * 8);
+-	}
+-
+ 	write_lock_irqsave(&adapter->adapter_lock, flags);
+ 	netxen_nic_pci_change_crbwindow_128M(adapter, 0);
+ 
+-	for (i = 0; i < loop; i++) {
+-		writel((uint32_t)(off8 + (i << 3)),
+-			(mem_crb+MIU_TEST_AGT_ADDR_LO));
+-		writel(0,
+-			(mem_crb+MIU_TEST_AGT_ADDR_HI));
+-		writel(word[i] & 0xffffffff,
+-			(mem_crb+MIU_TEST_AGT_WRDATA_LO));
+-		writel((word[i] >> 32) & 0xffffffff,
+-			(mem_crb+MIU_TEST_AGT_WRDATA_HI));
+-		writel(MIU_TA_CTL_ENABLE|MIU_TA_CTL_WRITE,
+-			(mem_crb+MIU_TEST_AGT_CTRL));
+-		writel(MIU_TA_CTL_START|MIU_TA_CTL_ENABLE|MIU_TA_CTL_WRITE,
+-			(mem_crb+MIU_TEST_AGT_CTRL));
+-
+-		for (j = 0; j < MAX_CTL_CHECK; j++) {
+-			temp = readl(
+-			     (mem_crb+MIU_TEST_AGT_CTRL));
+-			if ((temp & MIU_TA_CTL_BUSY) == 0)
+-				break;
+-		}
+-
+-		if (j >= MAX_CTL_CHECK) {
+-			if (printk_ratelimit())
+-				dev_err(&adapter->pdev->dev,
+-					"failed to write through agent\n");
+-			ret = -1;
++	writel(off_lo, (mem_crb + MIU_TEST_AGT_ADDR_LO));
++	writel(off_hi, (mem_crb + addr_hi));
++	writel(data & 0xffffffff, (mem_crb + data_lo));
++	writel((data >> 32) & 0xffffffff, (mem_crb + data_hi));
++	writel((TA_CTL_ENABLE | TA_CTL_WRITE), (mem_crb + TEST_AGT_CTRL));
++	writel((TA_CTL_START | TA_CTL_ENABLE | TA_CTL_WRITE),
++			(mem_crb + TEST_AGT_CTRL));
++
++	for (j = 0; j < MAX_CTL_CHECK; j++) {
++		temp = readl((mem_crb + TEST_AGT_CTRL));
++		if ((temp & TA_CTL_BUSY) == 0)
+ 			break;
+-		}
+ 	}
+ 
++	if (j >= MAX_CTL_CHECK) {
++		if (printk_ratelimit())
++			dev_err(&adapter->pdev->dev,
++					"failed to write through agent\n");
++		ret = -EIO;
++	} else
++		ret = 0;
++
+ 	netxen_nic_pci_change_crbwindow_128M(adapter, 1);
+ 	write_unlock_irqrestore(&adapter->adapter_lock, flags);
+ 	return ret;
+@@ -1587,304 +1556,202 @@ correct:
+ 
+ static int
+ netxen_nic_pci_mem_read_128M(struct netxen_adapter *adapter,
+-		u64 off, void *data, int size)
++		u64 off, u64 *data)
+ {
+ 	unsigned long   flags;
+-	int	     i, j = 0, k, start, end, loop, sz[2], off0[2];
+-	uint32_t      temp;
+-	uint64_t      off8, val, word[2] = {0, 0};
++	int j, ret;
++	u32 temp, off_lo, off_hi, addr_hi, data_hi, data_lo;
++	u64 val;
+ 	void __iomem *mem_crb;
+ 
+-	if (size != 8)
++	/* Only 64-bit aligned access */
++	if (off & 7)
+ 		return -EIO;
+ 
++	/* P2 has different SIU and MIU test agent base addr */
+ 	if (ADDR_IN_RANGE(off, NETXEN_ADDR_QDR_NET,
+ 				NETXEN_ADDR_QDR_NET_MAX_P2)) {
+-		mem_crb = pci_base_offset(adapter, NETXEN_CRB_QDR_NET);
++		mem_crb = pci_base_offset(adapter,
++				NETXEN_CRB_QDR_NET+SIU_TEST_AGT_BASE);
++		addr_hi = SIU_TEST_AGT_ADDR_HI;
++		data_lo = SIU_TEST_AGT_RDDATA_LO;
++		data_hi = SIU_TEST_AGT_RDDATA_HI;
++		off_lo = off & SIU_TEST_AGT_ADDR_MASK;
++		off_hi = SIU_TEST_AGT_UPPER_ADDR(off);
+ 		goto correct;
+ 	}
+ 
+ 	if (ADDR_IN_RANGE(off, NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX)) {
+-		mem_crb = pci_base_offset(adapter, NETXEN_CRB_DDR_NET);
++		mem_crb = pci_base_offset(adapter,
++				NETXEN_CRB_DDR_NET+MIU_TEST_AGT_BASE);
++		addr_hi = MIU_TEST_AGT_ADDR_HI;
++		data_lo = MIU_TEST_AGT_RDDATA_LO;
++		data_hi = MIU_TEST_AGT_RDDATA_HI;
++		off_lo = off & MIU_TEST_AGT_ADDR_MASK;
++		off_hi = 0;
+ 		goto correct;
+ 	}
+ 
+ 	return -EIO;
+ 
+ correct:
+-	off8 = off & 0xfffffff8;
+-	off0[0] = off & 0x7;
+-	off0[1] = 0;
+-	sz[0] = (size < (8 - off0[0])) ? size : (8 - off0[0]);
+-	sz[1] = size - sz[0];
+-	loop = ((off0[0] + size - 1) >> 3) + 1;
+-
+ 	write_lock_irqsave(&adapter->adapter_lock, flags);
+ 	netxen_nic_pci_change_crbwindow_128M(adapter, 0);
+ 
+-	for (i = 0; i < loop; i++) {
+-		writel((uint32_t)(off8 + (i << 3)),
+-			(mem_crb+MIU_TEST_AGT_ADDR_LO));
+-		writel(0,
+-			(mem_crb+MIU_TEST_AGT_ADDR_HI));
+-		writel(MIU_TA_CTL_ENABLE,
+-			(mem_crb+MIU_TEST_AGT_CTRL));
+-		writel(MIU_TA_CTL_START|MIU_TA_CTL_ENABLE,
+-			(mem_crb+MIU_TEST_AGT_CTRL));
+-
+-		for (j = 0; j < MAX_CTL_CHECK; j++) {
+-			temp = readl(
+-			      (mem_crb+MIU_TEST_AGT_CTRL));
+-			if ((temp & MIU_TA_CTL_BUSY) == 0)
+-				break;
+-		}
++	writel(off_lo, (mem_crb + MIU_TEST_AGT_ADDR_LO));
++	writel(off_hi, (mem_crb + addr_hi));
++	writel(TA_CTL_ENABLE, (mem_crb + TEST_AGT_CTRL));
++	writel((TA_CTL_START|TA_CTL_ENABLE), (mem_crb + TEST_AGT_CTRL));
++
++	for (j = 0; j < MAX_CTL_CHECK; j++) {
++		temp = readl(mem_crb + TEST_AGT_CTRL);
++		if ((temp & TA_CTL_BUSY) == 0)
++			break;
++	}
+ 
+-		if (j >= MAX_CTL_CHECK) {
+-			if (printk_ratelimit())
+-				dev_err(&adapter->pdev->dev,
++	if (j >= MAX_CTL_CHECK) {
++		if (printk_ratelimit())
++			dev_err(&adapter->pdev->dev,
+ 					"failed to read through agent\n");
+-			break;
+-		}
++		ret = -EIO;
++	} else {
+ 
+-		start = off0[i] >> 2;
+-		end   = (off0[i] + sz[i] - 1) >> 2;
+-		for (k = start; k <= end; k++) {
+-			word[i] |= ((uint64_t) readl(
+-				    (mem_crb +
+-				    MIU_TEST_AGT_RDDATA(k))) << (32*k));
+-		}
++		temp = readl(mem_crb + data_hi);
++		val = ((u64)temp << 32);
++		val |= readl(mem_crb + data_lo);
++		*data = val;
++		ret = 0;
+ 	}
+ 
+ 	netxen_nic_pci_change_crbwindow_128M(adapter, 1);
+ 	write_unlock_irqrestore(&adapter->adapter_lock, flags);
+ 
+-	if (j >= MAX_CTL_CHECK)
+-		return -1;
+-
+-	if (sz[0] == 8) {
+-		val = word[0];
+-	} else {
+-		val = ((word[0] >> (off0[0] * 8)) & (~(~0ULL << (sz[0] * 8)))) |
+-			((word[1] & (~(~0ULL << (sz[1] * 8)))) << (sz[0] * 8));
+-	}
+-
+-	switch (size) {
+-	case 1:
+-		*(uint8_t  *)data = val;
+-		break;
+-	case 2:
+-		*(uint16_t *)data = val;
+-		break;
+-	case 4:
+-		*(uint32_t *)data = val;
+-		break;
+-	case 8:
+-		*(uint64_t *)data = val;
+-		break;
+-	}
+-	return 0;
++	return ret;
+ }
+ 
+ static int
+ netxen_nic_pci_mem_write_2M(struct netxen_adapter *adapter,
+-		u64 off, void *data, int size)
++		u64 off, u64 data)
+ {
+-	int i, j, ret = 0, loop, sz[2], off0;
+-	uint32_t temp;
+-	uint64_t off8, tmpw, word[2] = {0, 0};
++	unsigned long   flags;
++	int j, ret;
++	u32 temp, off8;
+ 	void __iomem *mem_crb;
+ 
+-	if (size != 8)
++	/* Only 64-bit aligned access */
++	if (off & 7)
+ 		return -EIO;
+ 
++	/* P3 onward, test agent base for MIU and SIU is same */
+ 	if (ADDR_IN_RANGE(off, NETXEN_ADDR_QDR_NET,
+ 				NETXEN_ADDR_QDR_NET_MAX_P3)) {
+-		mem_crb = netxen_get_ioaddr(adapter, NETXEN_CRB_QDR_NET);
++		mem_crb = netxen_get_ioaddr(adapter,
++				NETXEN_CRB_QDR_NET+MIU_TEST_AGT_BASE);
+ 		goto correct;
+ 	}
+ 
+ 	if (ADDR_IN_RANGE(off, NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX)) {
+-		mem_crb = netxen_get_ioaddr(adapter, NETXEN_CRB_DDR_NET);
++		mem_crb = netxen_get_ioaddr(adapter,
++				NETXEN_CRB_DDR_NET+MIU_TEST_AGT_BASE);
+ 		goto correct;
+ 	}
+ 
+ 	return -EIO;
+ 
+ correct:
+-	off8 = off & 0xfffffff8;
+-	off0 = off & 0x7;
+-	sz[0] = (size < (8 - off0)) ? size : (8 - off0);
+-	sz[1] = size - sz[0];
+-	loop = ((off0 + size - 1) >> 3) + 1;
+-
+-	if ((size != 8) || (off0 != 0)) {
+-		for (i = 0; i < loop; i++) {
+-			if (adapter->pci_mem_read(adapter,
+-					off8 + (i << 3), &word[i], 8))
+-				return -1;
+-		}
+-	}
+-
+-	switch (size) {
+-	case 1:
+-		tmpw = *((uint8_t *)data);
+-		break;
+-	case 2:
+-		tmpw = *((uint16_t *)data);
+-		break;
+-	case 4:
+-		tmpw = *((uint32_t *)data);
+-		break;
+-	case 8:
+-	default:
+-		tmpw = *((uint64_t *)data);
+-	break;
+-	}
+-
+-	word[0] &= ~((~(~0ULL << (sz[0] * 8))) << (off0 * 8));
+-	word[0] |= tmpw << (off0 * 8);
+-
+-	if (loop == 2) {
+-		word[1] &= ~(~0ULL << (sz[1] * 8));
+-		word[1] |= tmpw >> (sz[0] * 8);
+-	}
+-
+-	/*
+-	 * don't lock here - write_wx gets the lock if each time
+-	 * write_lock_irqsave(&adapter->adapter_lock, flags);
+-	 * netxen_nic_pci_change_crbwindow_128M(adapter, 0);
+-	 */
++	off8 = off & MIU_TEST_AGT_ADDR_MASK;
+ 
+-	for (i = 0; i < loop; i++) {
+-		writel(off8 + (i << 3), mem_crb+MIU_TEST_AGT_ADDR_LO);
+-		writel(0, mem_crb+MIU_TEST_AGT_ADDR_HI);
+-		writel(word[i] & 0xffffffff, mem_crb+MIU_TEST_AGT_WRDATA_LO);
+-		writel((word[i] >> 32) & 0xffffffff,
+-				mem_crb+MIU_TEST_AGT_WRDATA_HI);
+-		writel((MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE),
+-				mem_crb+MIU_TEST_AGT_CTRL);
+-		writel(MIU_TA_CTL_START | MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE,
+-				mem_crb+MIU_TEST_AGT_CTRL);
+-
+-		for (j = 0; j < MAX_CTL_CHECK; j++) {
+-			temp = readl(mem_crb + MIU_TEST_AGT_CTRL);
+-			if ((temp & MIU_TA_CTL_BUSY) == 0)
+-				break;
+-		}
++	write_lock_irqsave(&adapter->adapter_lock, flags);
+ 
+-		if (j >= MAX_CTL_CHECK) {
+-			if (printk_ratelimit())
+-				dev_err(&adapter->pdev->dev,
+-					"failed to write through agent\n");
+-			ret = -1;
++	writel(off8, (mem_crb + MIU_TEST_AGT_ADDR_LO));
++	writel(0, (mem_crb + MIU_TEST_AGT_ADDR_HI));
++	writel(data & 0xffffffff, mem_crb + MIU_TEST_AGT_WRDATA_LO);
++	writel((data >> 32) & 0xffffffff, mem_crb + MIU_TEST_AGT_WRDATA_HI);
++	writel((TA_CTL_ENABLE | TA_CTL_WRITE), (mem_crb + TEST_AGT_CTRL));
++	writel((TA_CTL_START | TA_CTL_ENABLE | TA_CTL_WRITE),
++			(mem_crb + TEST_AGT_CTRL));
++
++	for (j = 0; j < MAX_CTL_CHECK; j++) {
++		temp = readl(mem_crb + TEST_AGT_CTRL);
++		if ((temp & TA_CTL_BUSY) == 0)
+ 			break;
+-		}
+ 	}
+ 
+-	/*
+-	 * netxen_nic_pci_change_crbwindow_128M(adapter, 1);
+-	 * write_unlock_irqrestore(&adapter->adapter_lock, flags);
+-	 */
++	if (j >= MAX_CTL_CHECK) {
++		if (printk_ratelimit())
++			dev_err(&adapter->pdev->dev,
++					"failed to write through agent\n");
++		ret = -EIO;
++	} else
++		ret = 0;
++
++	write_unlock_irqrestore(&adapter->adapter_lock, flags);
++
+ 	return ret;
+ }
+ 
+ static int
+ netxen_nic_pci_mem_read_2M(struct netxen_adapter *adapter,
+-		u64 off, void *data, int size)
++		u64 off, u64 *data)
+ {
+-	int i, j = 0, k, start, end, loop, sz[2], off0[2];
+-	uint32_t      temp;
+-	uint64_t      off8, val, word[2] = {0, 0};
++	unsigned long   flags;
++	int j, ret;
++	u32 temp, off8;
++	u64 val;
+ 	void __iomem *mem_crb;
+ 
+-	if (size != 8)
++	/* Only 64-bit aligned access */
++	if (off & 7)
+ 		return -EIO;
+ 
++	/* P3 onward, test agent base for MIU and SIU is same */
+ 	if (ADDR_IN_RANGE(off, NETXEN_ADDR_QDR_NET,
+ 				NETXEN_ADDR_QDR_NET_MAX_P3)) {
+-		mem_crb = netxen_get_ioaddr(adapter, NETXEN_CRB_QDR_NET);
++		mem_crb = netxen_get_ioaddr(adapter,
++				NETXEN_CRB_QDR_NET+MIU_TEST_AGT_BASE);
+ 		goto correct;
+ 	}
+ 
+ 	if (ADDR_IN_RANGE(off, NETXEN_ADDR_DDR_NET, NETXEN_ADDR_DDR_NET_MAX)) {
+-		mem_crb = netxen_get_ioaddr(adapter, NETXEN_CRB_DDR_NET);
++		mem_crb = netxen_get_ioaddr(adapter,
++				NETXEN_CRB_DDR_NET+MIU_TEST_AGT_BASE);
+ 		goto correct;
+ 	}
+ 
+ 	return -EIO;
+ 
+ correct:
+-	off8 = off & 0xfffffff8;
+-	off0[0] = off & 0x7;
+-	off0[1] = 0;
+-	sz[0] = (size < (8 - off0[0])) ? size : (8 - off0[0]);
+-	sz[1] = size - sz[0];
+-	loop = ((off0[0] + size - 1) >> 3) + 1;
+-
+-	/*
+-	 * don't lock here - write_wx gets the lock if each time
+-	 * write_lock_irqsave(&adapter->adapter_lock, flags);
+-	 * netxen_nic_pci_change_crbwindow_128M(adapter, 0);
+-	 */
++	off8 = off & MIU_TEST_AGT_ADDR_MASK;
+ 
+-	for (i = 0; i < loop; i++) {
+-		writel(off8 + (i << 3), mem_crb + MIU_TEST_AGT_ADDR_LO);
+-		writel(0, mem_crb + MIU_TEST_AGT_ADDR_HI);
+-		writel(MIU_TA_CTL_ENABLE, mem_crb + MIU_TEST_AGT_CTRL);
+-		writel(MIU_TA_CTL_START | MIU_TA_CTL_ENABLE,
+-				mem_crb + MIU_TEST_AGT_CTRL);
+-
+-		for (j = 0; j < MAX_CTL_CHECK; j++) {
+-			temp = readl(mem_crb + MIU_TEST_AGT_CTRL);
+-			if ((temp & MIU_TA_CTL_BUSY) == 0)
+-				break;
+-		}
++	write_lock_irqsave(&adapter->adapter_lock, flags);
+ 
+-		if (j >= MAX_CTL_CHECK) {
+-			if (printk_ratelimit())
+-				dev_err(&adapter->pdev->dev,
+-					"failed to read through agent\n");
++	writel(off8, (mem_crb + MIU_TEST_AGT_ADDR_LO));
++	writel(0, (mem_crb + MIU_TEST_AGT_ADDR_HI));
++	writel(TA_CTL_ENABLE, (mem_crb + TEST_AGT_CTRL));
++	writel((TA_CTL_START | TA_CTL_ENABLE), (mem_crb + TEST_AGT_CTRL));
++
++	for (j = 0; j < MAX_CTL_CHECK; j++) {
++		temp = readl(mem_crb + TEST_AGT_CTRL);
++		if ((temp & TA_CTL_BUSY) == 0)
+ 			break;
+-		}
+-
+-		start = off0[i] >> 2;
+-		end   = (off0[i] + sz[i] - 1) >> 2;
+-		for (k = start; k <= end; k++) {
+-			temp = readl(mem_crb + MIU_TEST_AGT_RDDATA(k));
+-			word[i] |= ((uint64_t)temp << (32 * k));
+-		}
+ 	}
+ 
+-	/*
+-	 * netxen_nic_pci_change_crbwindow_128M(adapter, 1);
+-	 * write_unlock_irqrestore(&adapter->adapter_lock, flags);
+-	 */
+-
+-	if (j >= MAX_CTL_CHECK)
+-		return -1;
+-
+-	if (sz[0] == 8) {
+-		val = word[0];
++	if (j >= MAX_CTL_CHECK) {
++		if (printk_ratelimit())
++			dev_err(&adapter->pdev->dev,
++					"failed to read through agent\n");
++		ret = -EIO;
+ 	} else {
+-		val = ((word[0] >> (off0[0] * 8)) & (~(~0ULL << (sz[0] * 8)))) |
+-		((word[1] & (~(~0ULL << (sz[1] * 8)))) << (sz[0] * 8));
++		temp = readl(mem_crb + MIU_TEST_AGT_RDDATA_HI);
++		val = (u64)temp << 32;
++		val |= readl(mem_crb + MIU_TEST_AGT_RDDATA_LO);
++		*data = val;
++		ret = 0;
+ 	}
+ 
+-	switch (size) {
+-	case 1:
+-		*(uint8_t  *)data = val;
+-		break;
+-	case 2:
+-		*(uint16_t *)data = val;
+-		break;
+-	case 4:
+-		*(uint32_t *)data = val;
+-		break;
+-	case 8:
+-		*(uint64_t *)data = val;
+-		break;
+-	}
+-	return 0;
++	write_unlock_irqrestore(&adapter->adapter_lock, flags);
++
++	return ret;
+ }
+ 
+ void
+diff -urpN a/drivers/net/netxen/netxen_nic_init.c b/drivers/net/netxen/netxen_nic_init.c
+--- a/drivers/net/netxen/netxen_nic_init.c	2010-08-30 19:40:21.000000000 -0600
++++ b/drivers/net/netxen/netxen_nic_init.c	2010-08-30 19:42:40.000000000 -0600
+@@ -46,6 +46,7 @@ static unsigned int crb_addr_xform[NETXE
+ static void
+ netxen_post_rx_buffers_nodb(struct netxen_adapter *adapter,
+ 		struct nx_host_rds_ring *rds_ring);
++static int netxen_p3_has_mn(struct netxen_adapter *adapter);
+ 
+ static void crb_addr_transform_setup(void)
+ {
+@@ -607,6 +608,310 @@ int netxen_pinit_from_rom(struct netxen_
+ 	return 0;
+ }
+ 
++static struct uni_table_desc *nx_get_table_desc(const u8 *unirom, int section)
++{
++	uint32_t i;
++	struct uni_table_desc *directory = (struct uni_table_desc *) &unirom[0];
++	__le32 entries = cpu_to_le32(directory->num_entries);
++
++	for (i = 0; i < entries; i++) {
++
++		__le32 offs = cpu_to_le32(directory->findex) +
++				(i * cpu_to_le32(directory->entry_size));
++		__le32 tab_type = cpu_to_le32(*((u32 *)&unirom[offs] + 8));
++
++		if (tab_type == section)
++			return (struct uni_table_desc *) &unirom[offs];
++	}
++
++	return NULL;
++}
++
++#define	QLCNIC_FILEHEADER_SIZE	(14 * 4)
++
++static int
++netxen_nic_validate_header(struct netxen_adapter *adapter)
++ {
++	const u8 *unirom = adapter->fw->data;
++	struct uni_table_desc *directory = (struct uni_table_desc *) &unirom[0];
++	u32 fw_file_size = adapter->fw->size;
++	u32 tab_size;
++	__le32 entries;
++	__le32 entry_size;
++
++	if (fw_file_size < QLCNIC_FILEHEADER_SIZE)
++		return -EINVAL;
++
++	entries = cpu_to_le32(directory->num_entries);
++	entry_size = cpu_to_le32(directory->entry_size);
++	tab_size = cpu_to_le32(directory->findex) + (entries * entry_size);
++
++	if (fw_file_size < tab_size)
++		return -EINVAL;
++
++	return 0;
++}
++
++static int
++netxen_nic_validate_bootld(struct netxen_adapter *adapter)
++{
++	struct uni_table_desc *tab_desc;
++	struct uni_data_desc *descr;
++	const u8 *unirom = adapter->fw->data;
++	__le32 idx = cpu_to_le32(*((int *)&unirom[adapter->file_prd_off] +
++				NX_UNI_BOOTLD_IDX_OFF));
++	u32 offs;
++	u32 tab_size;
++	u32 data_size;
++
++	tab_desc = nx_get_table_desc(unirom, NX_UNI_DIR_SECT_BOOTLD);
++
++	if (!tab_desc)
++		return -EINVAL;
++
++	tab_size = cpu_to_le32(tab_desc->findex) +
++			(cpu_to_le32(tab_desc->entry_size) * (idx + 1));
++
++	if (adapter->fw->size < tab_size)
++		return -EINVAL;
++
++	offs = cpu_to_le32(tab_desc->findex) +
++		(cpu_to_le32(tab_desc->entry_size) * (idx));
++	descr = (struct uni_data_desc *)&unirom[offs];
++
++	data_size = cpu_to_le32(descr->findex) + cpu_to_le32(descr->size);
++
++	if (adapter->fw->size < data_size)
++		return -EINVAL;
++
++	return 0;
++}
++
++static int
++netxen_nic_validate_fw(struct netxen_adapter *adapter)
++{
++	struct uni_table_desc *tab_desc;
++	struct uni_data_desc *descr;
++	const u8 *unirom = adapter->fw->data;
++	__le32 idx = cpu_to_le32(*((int *)&unirom[adapter->file_prd_off] +
++				NX_UNI_FIRMWARE_IDX_OFF));
++	u32 offs;
++	u32 tab_size;
++	u32 data_size;
++
++	tab_desc = nx_get_table_desc(unirom, NX_UNI_DIR_SECT_FW);
++
++	if (!tab_desc)
++		return -EINVAL;
++
++	tab_size = cpu_to_le32(tab_desc->findex) +
++			(cpu_to_le32(tab_desc->entry_size) * (idx + 1));
++
++	if (adapter->fw->size < tab_size)
++		return -EINVAL;
++
++	offs = cpu_to_le32(tab_desc->findex) +
++		(cpu_to_le32(tab_desc->entry_size) * (idx));
++	descr = (struct uni_data_desc *)&unirom[offs];
++	data_size = cpu_to_le32(descr->findex) + cpu_to_le32(descr->size);
++
++	if (adapter->fw->size < data_size)
++		return -EINVAL;
++
++	return 0;
++}
++
++
++static int
++netxen_nic_validate_product_offs(struct netxen_adapter *adapter)
++{
++	struct uni_table_desc *ptab_descr;
++	const u8 *unirom = adapter->fw->data;
++	int mn_present = (NX_IS_REVISION_P2(adapter->ahw.revision_id)) ?
++			1 : netxen_p3_has_mn(adapter);
++	__le32 entries;
++	__le32 entry_size;
++	u32 tab_size;
++	u32 i;
++
++	ptab_descr = nx_get_table_desc(unirom, NX_UNI_DIR_SECT_PRODUCT_TBL);
++	if (ptab_descr == NULL)
++		return -EINVAL;
++
++	entries = cpu_to_le32(ptab_descr->num_entries);
++	entry_size = cpu_to_le32(ptab_descr->entry_size);
++	tab_size = cpu_to_le32(ptab_descr->findex) + (entries * entry_size);
++
++	if (adapter->fw->size < tab_size)
++		return -EINVAL;
++
++nomn:
++	for (i = 0; i < entries; i++) {
++
++		__le32 flags, file_chiprev, offs;
++		u8 chiprev = adapter->ahw.revision_id;
++		uint32_t flagbit;
++
++		offs = cpu_to_le32(ptab_descr->findex) +
++				(i * cpu_to_le32(ptab_descr->entry_size));
++		flags = cpu_to_le32(*((int *)&unirom[offs] + NX_UNI_FLAGS_OFF));
++		file_chiprev = cpu_to_le32(*((int *)&unirom[offs] +
++							NX_UNI_CHIP_REV_OFF));
++
++		flagbit = mn_present ? 1 : 2;
++
++		if ((chiprev == file_chiprev) &&
++					((1ULL << flagbit) & flags)) {
++			adapter->file_prd_off = offs;
++			return 0;
++		}
++	}
++
++	if (mn_present && NX_IS_REVISION_P3(adapter->ahw.revision_id)) {
++		mn_present = 0;
++		goto nomn;
++	}
++
++	return -EINVAL;
++}
++
++static int
++netxen_nic_validate_unified_romimage(struct netxen_adapter *adapter)
++{
++	if (netxen_nic_validate_header(adapter)) {
++		dev_err(&adapter->pdev->dev,
++				"unified image: header validation failed\n");
++		return -EINVAL;
++	}
++
++	if (netxen_nic_validate_product_offs(adapter)) {
++		dev_err(&adapter->pdev->dev,
++				"unified image: product validation failed\n");
++		return -EINVAL;
++	}
++
++	if (netxen_nic_validate_bootld(adapter)) {
++		dev_err(&adapter->pdev->dev,
++				"unified image: bootld validation failed\n");
++		return -EINVAL;
++	}
++
++	if (netxen_nic_validate_fw(adapter)) {
++		dev_err(&adapter->pdev->dev,
++				"unified image: firmware validation failed\n");
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++static struct uni_data_desc *nx_get_data_desc(struct netxen_adapter *adapter,
++			u32 section, u32 idx_offset)
++{
++	const u8 *unirom = adapter->fw->data;
++	int idx = cpu_to_le32(*((int *)&unirom[adapter->file_prd_off] +
++								idx_offset));
++	struct uni_table_desc *tab_desc;
++	__le32 offs;
++
++	tab_desc = nx_get_table_desc(unirom, section);
++
++	if (tab_desc == NULL)
++		return NULL;
++
++	offs = cpu_to_le32(tab_desc->findex) +
++			(cpu_to_le32(tab_desc->entry_size) * idx);
++
++	return (struct uni_data_desc *)&unirom[offs];
++}
++
++static u8 *
++nx_get_bootld_offs(struct netxen_adapter *adapter)
++{
++	u32 offs = NETXEN_BOOTLD_START;
++
++	if (adapter->fw_type == NX_UNIFIED_ROMIMAGE)
++		offs = cpu_to_le32((nx_get_data_desc(adapter,
++					NX_UNI_DIR_SECT_BOOTLD,
++					NX_UNI_BOOTLD_IDX_OFF))->findex);
++
++	return (u8 *)&adapter->fw->data[offs];
++}
++
++static u8 *
++nx_get_fw_offs(struct netxen_adapter *adapter)
++{
++	u32 offs = NETXEN_IMAGE_START;
++
++	if (adapter->fw_type == NX_UNIFIED_ROMIMAGE)
++		offs = cpu_to_le32((nx_get_data_desc(adapter,
++					NX_UNI_DIR_SECT_FW,
++					NX_UNI_FIRMWARE_IDX_OFF))->findex);
++
++	return (u8 *)&adapter->fw->data[offs];
++}
++
++static __le32
++nx_get_fw_size(struct netxen_adapter *adapter)
++{
++	if (adapter->fw_type == NX_UNIFIED_ROMIMAGE)
++		return cpu_to_le32((nx_get_data_desc(adapter,
++					NX_UNI_DIR_SECT_FW,
++					NX_UNI_FIRMWARE_IDX_OFF))->size);
++	else
++		return cpu_to_le32(
++				*(u32 *)&adapter->fw->data[NX_FW_SIZE_OFFSET]);
++}
++
++static __le32
++nx_get_fw_version(struct netxen_adapter *adapter)
++{
++	struct uni_data_desc *fw_data_desc;
++	const struct firmware *fw = adapter->fw;
++	__le32 major, minor, sub;
++	const u8 *ver_str;
++	int i, ret = 0;
++
++	if (adapter->fw_type == NX_UNIFIED_ROMIMAGE) {
++
++		fw_data_desc = nx_get_data_desc(adapter,
++				NX_UNI_DIR_SECT_FW, NX_UNI_FIRMWARE_IDX_OFF);
++		ver_str = fw->data + cpu_to_le32(fw_data_desc->findex) +
++				cpu_to_le32(fw_data_desc->size) - 17;
++
++		for (i = 0; i < 12; i++) {
++			if (!strncmp(&ver_str[i], "REV=", 4)) {
++				ret = sscanf(&ver_str[i+4], "%u.%u.%u ",
++							&major, &minor, &sub);
++				break;
++			}
++		}
++
++		if (ret != 3)
++			return 0;
++
++		return major + (minor << 8) + (sub << 16);
++
++	} else
++		return cpu_to_le32(*(u32 *)&fw->data[NX_FW_VERSION_OFFSET]);
++}
++
++static __le32
++nx_get_bios_version(struct netxen_adapter *adapter)
++{
++	const struct firmware *fw = adapter->fw;
++	__le32 bios_ver, prd_off = adapter->file_prd_off;
++
++	if (adapter->fw_type == NX_UNIFIED_ROMIMAGE) {
++		bios_ver = cpu_to_le32(*((u32 *) (&fw->data[prd_off])
++						+ NX_UNI_BIOS_VERSION_OFF));
++		return (bios_ver << 16) + ((bios_ver >> 8) & 0xff00) +
++							(bios_ver >> 24);
++	} else
++		return cpu_to_le32(*(u32 *)&fw->data[NX_BIOS_VERSION_OFFSET]);
++
++}
++
+ int
+ netxen_need_fw_reset(struct netxen_adapter *adapter)
+ {
+@@ -646,9 +951,8 @@ netxen_need_fw_reset(struct netxen_adapt
+ 	/* check if we have got newer or different file firmware */
+ 	if (adapter->fw) {
+ 
+-		const struct firmware *fw = adapter->fw;
++		val = nx_get_fw_version(adapter);
+ 
+-		val = cpu_to_le32(*(u32 *)&fw->data[NX_FW_VERSION_OFFSET]);
+ 		version = NETXEN_DECODE_VERSION(val);
+ 
+ 		major = NXRD32(adapter, NETXEN_FW_VERSION_MAJOR);
+@@ -658,7 +962,8 @@ netxen_need_fw_reset(struct netxen_adapt
+ 		if (version > NETXEN_VERSION_CODE(major, minor, build))
+ 			return 1;
+ 
+-		if (version == NETXEN_VERSION_CODE(major, minor, build)) {
++		if (version == NETXEN_VERSION_CODE(major, minor, build) &&
++			adapter->fw_type != NX_UNIFIED_ROMIMAGE) {
+ 
+ 			val = NXRD32(adapter, NETXEN_MIU_MN_CONTROL);
+ 			fw_type = (val & 0x4) ?
+@@ -676,6 +981,7 @@ static char *fw_name[] = {
+ 	NX_P2_MN_ROMIMAGE_NAME,
+ 	NX_P3_CT_ROMIMAGE_NAME,
+ 	NX_P3_MN_ROMIMAGE_NAME,
++	NX_UNIFIED_ROMIMAGE_NAME,
+ 	NX_FLASH_ROMIMAGE_NAME,
+ };
+ 
+@@ -698,30 +1004,42 @@ netxen_load_firmware(struct netxen_adapt
+ 
+ 		size = (NETXEN_IMAGE_START - NETXEN_BOOTLD_START) / 8;
+ 
+-		ptr64 = (u64 *)&fw->data[NETXEN_BOOTLD_START];
++		ptr64 = (u64 *)nx_get_bootld_offs(adapter);
+ 		flashaddr = NETXEN_BOOTLD_START;
+ 
+ 		for (i = 0; i < size; i++) {
+ 			data = cpu_to_le64(ptr64[i]);
+-			adapter->pci_mem_write(adapter, flashaddr, &data, 8);
++
++			if (adapter->pci_mem_write(adapter, flashaddr, data))
++				return -EIO;
++
+ 			flashaddr += 8;
+ 		}
+ 
+-		size = *(u32 *)&fw->data[NX_FW_SIZE_OFFSET];
+-		size = (__force u32)cpu_to_le32(size) / 8;
++		size = (__force u32)nx_get_fw_size(adapter) / 8;
+ 
+-		ptr64 = (u64 *)&fw->data[NETXEN_IMAGE_START];
++		ptr64 = (u64 *)nx_get_fw_offs(adapter);
+ 		flashaddr = NETXEN_IMAGE_START;
+ 
+ 		for (i = 0; i < size; i++) {
+ 			data = cpu_to_le64(ptr64[i]);
+ 
+ 			if (adapter->pci_mem_write(adapter,
+-						flashaddr, &data, 8))
++						flashaddr, data))
+ 				return -EIO;
+ 
+ 			flashaddr += 8;
+ 		}
++
++		size = (__force u32)nx_get_fw_size(adapter) % 8;
++		if (size) {
++			data = cpu_to_le64(ptr64[i]);
++
++			if (adapter->pci_mem_write(adapter,
++						flashaddr, data))
++				return -EIO;
++		}
++
+ 	} else {
+ 		u64 data;
+ 		u32 hi, lo;
+@@ -731,17 +1049,17 @@ netxen_load_firmware(struct netxen_adapt
+ 
+ 		for (i = 0; i < size; i++) {
+ 			if (netxen_rom_fast_read(adapter,
+-					flashaddr, &lo) != 0)
++					flashaddr, (int *)&lo) != 0)
+ 				return -EIO;
+ 			if (netxen_rom_fast_read(adapter,
+-					flashaddr + 4, &hi) != 0)
++					flashaddr + 4, (int *)&hi) != 0)
+ 				return -EIO;
+ 
+ 			/* hi, lo are already in host endian byteorder */
+ 			data = (((u64)hi << 32) | lo);
+ 
+ 			if (adapter->pci_mem_write(adapter,
+-						flashaddr, &data, 8))
++						flashaddr, data))
+ 				return -EIO;
+ 
+ 			flashaddr += 8;
+@@ -760,21 +1078,27 @@ netxen_load_firmware(struct netxen_adapt
+ }
+ 
+ static int
+-netxen_validate_firmware(struct netxen_adapter *adapter, const char *fwname)
++netxen_validate_firmware(struct netxen_adapter *adapter)
+ {
+ 	__le32 val;
+ 	u32 ver, min_ver, bios;
+ 	struct pci_dev *pdev = adapter->pdev;
+ 	const struct firmware *fw = adapter->fw;
++	u8 fw_type = adapter->fw_type;
+ 
+-	if (fw->size < NX_FW_MIN_SIZE)
+-		return -EINVAL;
++	if (fw_type == NX_UNIFIED_ROMIMAGE) {
++		if (netxen_nic_validate_unified_romimage(adapter))
++			return -EINVAL;
++	} else {
++		val = cpu_to_le32(*(u32 *)&fw->data[NX_FW_MAGIC_OFFSET]);
++		if ((__force u32)val != NETXEN_BDINFO_MAGIC)
++			return -EINVAL;
+ 
+-	val = cpu_to_le32(*(u32 *)&fw->data[NX_FW_MAGIC_OFFSET]);
+-	if ((__force u32)val != NETXEN_BDINFO_MAGIC)
+-		return -EINVAL;
++		if (fw->size < NX_FW_MIN_SIZE)
++			return -EINVAL;
++	}
+ 
+-	val = cpu_to_le32(*(u32 *)&fw->data[NX_FW_VERSION_OFFSET]);
++	val = nx_get_fw_version(adapter);
+ 
+ 	if (NX_IS_REVISION_P3(adapter->ahw.revision_id))
+ 		min_ver = NETXEN_VERSION_CODE(4, 0, 216);
+@@ -786,15 +1110,15 @@ netxen_validate_firmware(struct netxen_a
+ 	if ((_major(ver) > _NETXEN_NIC_LINUX_MAJOR) || (ver < min_ver)) {
+ 		dev_err(&pdev->dev,
+ 				"%s: firmware version %d.%d.%d unsupported\n",
+-				fwname, _major(ver), _minor(ver), _build(ver));
++		fw_name[fw_type], _major(ver), _minor(ver), _build(ver));
+ 		return -EINVAL;
+ 	}
+ 
+-	val = cpu_to_le32(*(u32 *)&fw->data[NX_BIOS_VERSION_OFFSET]);
++	val = nx_get_bios_version(adapter);
+ 	netxen_rom_fast_read(adapter, NX_BIOS_VERSION_OFFSET, (int *)&bios);
+ 	if ((__force u32)val != bios) {
+ 		dev_err(&pdev->dev, "%s: firmware bios is incompatible\n",
+-				fwname);
++				fw_name[fw_type]);
+ 		return -EINVAL;
+ 	}
+ 
+@@ -805,7 +1129,7 @@ netxen_validate_firmware(struct netxen_a
+ 	val = NETXEN_DECODE_VERSION(val);
+ 	if (val > ver) {
+ 		dev_info(&pdev->dev, "%s: firmware is older than flash\n",
+-				fwname);
++				fw_name[fw_type]);
+ 		return -EINVAL;
+ 	}
+ 
+@@ -813,12 +1137,51 @@ netxen_validate_firmware(struct netxen_a
+ 	return 0;
+ }
+ 
++static void
++nx_get_next_fwtype(struct netxen_adapter *adapter)
++{
++	u8 fw_type;
++
++	switch (adapter->fw_type) {
++	case NX_UNKNOWN_ROMIMAGE:
++		fw_type = NX_UNIFIED_ROMIMAGE;
++		break;
++
++	case NX_UNIFIED_ROMIMAGE:
++		if (NX_IS_REVISION_P3P(adapter->ahw.revision_id))
++			fw_type = NX_FLASH_ROMIMAGE;
++		else if (NX_IS_REVISION_P2(adapter->ahw.revision_id))
++			fw_type = NX_P2_MN_ROMIMAGE;
++		else if (netxen_p3_has_mn(adapter))
++			fw_type = NX_P3_MN_ROMIMAGE;
++		else
++			fw_type = NX_P3_CT_ROMIMAGE;
++		break;
++
++	case NX_P3_MN_ROMIMAGE:
++		fw_type = NX_P3_CT_ROMIMAGE;
++		break;
++
++	case NX_P2_MN_ROMIMAGE:
++	case NX_P3_CT_ROMIMAGE:
++	default:
++		fw_type = NX_FLASH_ROMIMAGE;
++		break;
++	}
++
++	adapter->fw_type = fw_type;
++}
++
+ static int
+ netxen_p3_has_mn(struct netxen_adapter *adapter)
+ {
+ 	u32 capability, flashed_ver;
+ 	capability = 0;
+ 
++	/* NX2031 always had MN */
++	if (NX_IS_REVISION_P2(adapter->ahw.revision_id))
++		return 1;
++
+ 	netxen_rom_fast_read(adapter,
+ 			NX_FW_VERSION_OFFSET, (int *)&flashed_ver);
+ 	flashed_ver = NETXEN_DECODE_VERSION(flashed_ver);
+@@ -834,49 +1197,29 @@ netxen_p3_has_mn(struct netxen_adapter *
+ 
+ void netxen_request_firmware(struct netxen_adapter *adapter)
+ {
+-	u8 fw_type;
+ 	struct pci_dev *pdev = adapter->pdev;
+ 	int rc = 0;
+ 
+-	if (NX_IS_REVISION_P2(adapter->ahw.revision_id)) {
+-		fw_type = NX_P2_MN_ROMIMAGE;
+-		goto request_fw;
+-	}
++	adapter->fw_type = NX_UNKNOWN_ROMIMAGE;
+ 
+-	fw_type = netxen_p3_has_mn(adapter) ?
+-		NX_P3_MN_ROMIMAGE : NX_P3_CT_ROMIMAGE;
++next:
++	nx_get_next_fwtype(adapter);
+ 
+-request_fw:
+-	rc = request_firmware(&adapter->fw, fw_name[fw_type], &pdev->dev);
+-	if (rc != 0) {
+-		if (fw_type == NX_P3_MN_ROMIMAGE) {
+-			msleep(1);
+-			fw_type = NX_P3_CT_ROMIMAGE;
+-			goto request_fw;
+-		}
+-
+-		fw_type = NX_FLASH_ROMIMAGE;
++	if (adapter->fw_type == NX_FLASH_ROMIMAGE) {
+ 		adapter->fw = NULL;
+-		goto done;
+-	}
+-
+-	rc = netxen_validate_firmware(adapter, fw_name[fw_type]);
+-	if (rc != 0) {
+-		release_firmware(adapter->fw);
+-
+-		if (fw_type == NX_P3_MN_ROMIMAGE) {
++	} else {
++		rc = request_firmware(&adapter->fw,
++				fw_name[adapter->fw_type], &pdev->dev);
++		if (rc != 0)
++			goto next;
++
++		rc = netxen_validate_firmware(adapter);
++		if (rc != 0) {
++			release_firmware(adapter->fw);
+ 			msleep(1);
+-			fw_type = NX_P3_CT_ROMIMAGE;
+-			goto request_fw;
++			goto next;
+ 		}
+-
+-		fw_type = NX_FLASH_ROMIMAGE;
+-		adapter->fw = NULL;
+-		goto done;
+ 	}
+-
+-done:
+-	adapter->fw_type = fw_type;
+ }
+ 
+ 
+diff -urpN a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c
+--- a/drivers/net/netxen/netxen_nic_main.c	2010-08-30 19:40:21.000000000 -0600
++++ b/drivers/net/netxen/netxen_nic_main.c	2010-08-30 19:42:40.000000000 -0600
+@@ -41,6 +41,7 @@ MODULE_VERSION(NETXEN_NIC_LINUX_VERSIONI
+ MODULE_FIRMWARE(NX_P2_MN_ROMIMAGE_NAME);
+ MODULE_FIRMWARE(NX_P3_CT_ROMIMAGE_NAME);
+ MODULE_FIRMWARE(NX_P3_MN_ROMIMAGE_NAME);
++MODULE_FIRMWARE(NX_UNIFIED_ROMIMAGE_NAME);
+ 
+ char netxen_nic_driver_name[] = "netxen_nic";
+ static char netxen_nic_driver_string[] = "NetXen Network Driver version "
diff --git a/sid/linux-2.6/debian/patches/series/22 b/sid/linux-2.6/debian/patches/series/22
index 1412c2a..3afe071 100644
--- a/sid/linux-2.6/debian/patches/series/22
+++ b/sid/linux-2.6/debian/patches/series/22
@@ -73,3 +73,4 @@
 + features/all/sky2/0053-sky2-Refactor-down-up-code-out-of-sky2_restart.patch
 + features/all/sky2/0054-sky2-Avoid-allocating-memory-in-sky2_resume.patch
 + features/all/sky2/0055-sky2-version-1.28.patch
++ features/all/netxen-unified-fw-image.patch


Reply to: