diff -ruN linux-3.1.1.orig/drivers/bcma/Kconfig linux-3.1.1/drivers/bcma/Kconfig
--- linux-3.1.1.orig/drivers/bcma/Kconfig	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/bcma/Kconfig	2011-11-13 17:11:58.579558787 -0800
@@ -33,6 +33,19 @@
 	help
 	  PCI core hostmode operation (external PCI bus).
 
+config BCMA_HOST_SOC
+	bool
+	depends on BCMA_DRIVER_MIPS
+
+config BCMA_DRIVER_MIPS
+	bool "BCMA Broadcom MIPS core driver"
+	depends on BCMA && MIPS
+	help
+	  Driver for the Broadcom MIPS core attached to Broadcom specific
+	  Advanced Microcontroller Bus.
+
+	  If unsure, say N
+
 config BCMA_DEBUG
 	bool "BCMA debugging"
 	depends on BCMA
diff -ruN linux-3.1.1.orig/drivers/bcma/Makefile linux-3.1.1/drivers/bcma/Makefile
--- linux-3.1.1.orig/drivers/bcma/Makefile	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/bcma/Makefile	2011-11-13 17:11:58.579558787 -0800
@@ -2,7 +2,9 @@
 bcma-y					+= driver_chipcommon.o driver_chipcommon_pmu.o
 bcma-y					+= driver_pci.o
 bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE)	+= driver_pci_host.o
+bcma-$(CONFIG_BCMA_DRIVER_MIPS)		+= driver_mips.o
 bcma-$(CONFIG_BCMA_HOST_PCI)		+= host_pci.o
+bcma-$(CONFIG_BCMA_HOST_SOC)		+= host_soc.o
 obj-$(CONFIG_BCMA)			+= bcma.o
 
 ccflags-$(CONFIG_BCMA_DEBUG)		:= -DDEBUG
diff -ruN linux-3.1.1.orig/drivers/bcma/bcma_private.h linux-3.1.1/drivers/bcma/bcma_private.h
--- linux-3.1.1.orig/drivers/bcma/bcma_private.h	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/bcma/bcma_private.h	2011-11-13 17:11:58.579558787 -0800
@@ -15,13 +15,29 @@
 /* main.c */
 int bcma_bus_register(struct bcma_bus *bus);
 void bcma_bus_unregister(struct bcma_bus *bus);
+int __init bcma_bus_early_register(struct bcma_bus *bus,
+				   struct bcma_device *core_cc,
+				   struct bcma_device *core_mips);
 
 /* scan.c */
 int bcma_bus_scan(struct bcma_bus *bus);
+int __init bcma_bus_scan_early(struct bcma_bus *bus,
+			       struct bcma_device_id *match,
+			       struct bcma_device *core);
+void bcma_init_bus(struct bcma_bus *bus);
 
 /* sprom.c */
 int bcma_sprom_get(struct bcma_bus *bus);
 
+/* driver_chipcommon.c */
+#ifdef CONFIG_BCMA_DRIVER_MIPS
+void bcma_chipco_serial_init(struct bcma_drv_cc *cc);
+#endif /* CONFIG_BCMA_DRIVER_MIPS */
+
+/* driver_chipcommon_pmu.c */
+u32 bcma_pmu_alp_clock(struct bcma_drv_cc *cc);
+u32 bcma_pmu_get_clockcpu(struct bcma_drv_cc *cc);
+
 #ifdef CONFIG_BCMA_HOST_PCI
 /* host_pci.c */
 extern int __init bcma_host_pci_init(void);
diff -ruN linux-3.1.1.orig/drivers/bcma/core.c linux-3.1.1/drivers/bcma/core.c
--- linux-3.1.1.orig/drivers/bcma/core.c	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/bcma/core.c	2011-11-13 17:11:58.579558787 -0800
@@ -110,6 +110,8 @@
 u32 bcma_core_dma_translation(struct bcma_device *core)
 {
 	switch (core->bus->hosttype) {
+	case BCMA_HOSTTYPE_SOC:
+		return 0;
 	case BCMA_HOSTTYPE_PCI:
 		if (bcma_aread32(core, BCMA_IOST) & BCMA_IOST_DMA64)
 			return BCMA_DMA_TRANSLATION_DMA64_CMT;
diff -ruN linux-3.1.1.orig/drivers/bcma/driver_chipcommon.c linux-3.1.1/drivers/bcma/driver_chipcommon.c
--- linux-3.1.1.orig/drivers/bcma/driver_chipcommon.c	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/bcma/driver_chipcommon.c	2011-11-13 17:11:58.579558787 -0800
@@ -26,6 +26,9 @@
 	u32 leddc_on = 10;
 	u32 leddc_off = 90;
 
+	if (cc->setup_done)
+		return;
+
 	if (cc->core->id.rev >= 11)
 		cc->status = bcma_cc_read32(cc, BCMA_CC_CHIPSTAT);
 	cc->capabilities = bcma_cc_read32(cc, BCMA_CC_CAP);
@@ -52,6 +55,8 @@
 			((leddc_on << BCMA_CC_GPIOTIMER_ONTIME_SHIFT) |
 			 (leddc_off << BCMA_CC_GPIOTIMER_OFFTIME_SHIFT)));
 	}
+
+	cc->setup_done = true;
 }
 
 /* Set chip watchdog reset timer to fire in 'ticks' backplane cycles */
@@ -101,3 +106,51 @@
 {
 	return bcma_cc_write32_masked(cc, BCMA_CC_GPIOPOL, mask, value);
 }
+
+#ifdef CONFIG_BCMA_DRIVER_MIPS
+void bcma_chipco_serial_init(struct bcma_drv_cc *cc)
+{
+	unsigned int irq;
+	u32 baud_base;
+	u32 i;
+	unsigned int ccrev = cc->core->id.rev;
+	struct bcma_serial_port *ports = cc->serial_ports;
+
+	if (ccrev >= 11 && ccrev != 15) {
+		/* Fixed ALP clock */
+		baud_base = bcma_pmu_alp_clock(cc);
+		if (ccrev >= 21) {
+			/* Turn off UART clock before switching clocksource. */
+			bcma_cc_write32(cc, BCMA_CC_CORECTL,
+				       bcma_cc_read32(cc, BCMA_CC_CORECTL)
+				       & ~BCMA_CC_CORECTL_UARTCLKEN);
+		}
+		/* Set the override bit so we don't divide it */
+		bcma_cc_write32(cc, BCMA_CC_CORECTL,
+			       bcma_cc_read32(cc, BCMA_CC_CORECTL)
+			       | BCMA_CC_CORECTL_UARTCLK0);
+		if (ccrev >= 21) {
+			/* Re-enable the UART clock. */
+			bcma_cc_write32(cc, BCMA_CC_CORECTL,
+				       bcma_cc_read32(cc, BCMA_CC_CORECTL)
+				       | BCMA_CC_CORECTL_UARTCLKEN);
+		}
+	} else {
+		pr_err("serial not supported on this device ccrev: 0x%x\n",
+		       ccrev);
+		return;
+	}
+
+	irq = bcma_core_mips_irq(cc->core);
+
+	/* Determine the registers of the UARTs */
+	cc->nr_serial_ports = (cc->capabilities & BCMA_CC_CAP_NRUART);
+	for (i = 0; i < cc->nr_serial_ports; i++) {
+		ports[i].regs = cc->core->io_addr + BCMA_CC_UART0_DATA +
+				(i * 256);
+		ports[i].irq = irq;
+		ports[i].baud_base = baud_base;
+		ports[i].reg_shift = 0;
+	}
+}
+#endif /* CONFIG_BCMA_DRIVER_MIPS */
diff -ruN linux-3.1.1.orig/drivers/bcma/driver_chipcommon_pmu.c linux-3.1.1/drivers/bcma/driver_chipcommon_pmu.c
--- linux-3.1.1.orig/drivers/bcma/driver_chipcommon_pmu.c	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/bcma/driver_chipcommon_pmu.c	2011-11-13 17:11:58.579558787 -0800
@@ -11,20 +11,47 @@
 #include "bcma_private.h"
 #include <linux/bcma/bcma.h>
 
-static void bcma_chipco_chipctl_maskset(struct bcma_drv_cc *cc,
-					u32 offset, u32 mask, u32 set)
+static u32 bcma_chipco_pll_read(struct bcma_drv_cc *cc, u32 offset)
 {
-	u32 value;
+	bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset);
+	bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR);
+	return bcma_cc_read32(cc, BCMA_CC_PLLCTL_DATA);
+}
 
-	bcma_cc_read32(cc, BCMA_CC_CHIPCTL_ADDR);
+void bcma_chipco_pll_write(struct bcma_drv_cc *cc, u32 offset, u32 value)
+{
+	bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset);
+	bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR);
+	bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, value);
+}
+EXPORT_SYMBOL_GPL(bcma_chipco_pll_write);
+
+void bcma_chipco_pll_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask,
+			     u32 set)
+{
+	bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset);
+	bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR);
+	bcma_cc_maskset32(cc, BCMA_CC_PLLCTL_DATA, mask, set);
+}
+EXPORT_SYMBOL_GPL(bcma_chipco_pll_maskset);
+
+void bcma_chipco_chipctl_maskset(struct bcma_drv_cc *cc,
+				 u32 offset, u32 mask, u32 set)
+{
 	bcma_cc_write32(cc, BCMA_CC_CHIPCTL_ADDR, offset);
 	bcma_cc_read32(cc, BCMA_CC_CHIPCTL_ADDR);
-	value = bcma_cc_read32(cc, BCMA_CC_CHIPCTL_DATA);
-	value &= mask;
-	value |= set;
-	bcma_cc_write32(cc, BCMA_CC_CHIPCTL_DATA, value);
-	bcma_cc_read32(cc, BCMA_CC_CHIPCTL_DATA);
+	bcma_cc_maskset32(cc, BCMA_CC_CHIPCTL_DATA, mask, set);
 }
+EXPORT_SYMBOL_GPL(bcma_chipco_chipctl_maskset);
+
+void bcma_chipco_regctl_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask,
+				u32 set)
+{
+	bcma_cc_write32(cc, BCMA_CC_REGCTL_ADDR, offset);
+	bcma_cc_read32(cc, BCMA_CC_REGCTL_ADDR);
+	bcma_cc_maskset32(cc, BCMA_CC_REGCTL_DATA, mask, set);
+}
+EXPORT_SYMBOL_GPL(bcma_chipco_regctl_maskset);
 
 static void bcma_pmu_pll_init(struct bcma_drv_cc *cc)
 {
@@ -83,6 +110,24 @@
 	}
 }
 
+/* Disable to allow reading SPROM. Don't know the adventages of enabling it. */
+void bcma_chipco_bcm4331_ext_pa_lines_ctl(struct bcma_drv_cc *cc, bool enable)
+{
+	struct bcma_bus *bus = cc->core->bus;
+	u32 val;
+
+	val = bcma_cc_read32(cc, BCMA_CC_CHIPCTL);
+	if (enable) {
+		val |= BCMA_CHIPCTL_4331_EXTPA_EN;
+		if (bus->chipinfo.pkg == 9 || bus->chipinfo.pkg == 11)
+			val |= BCMA_CHIPCTL_4331_EXTPA_ON_GPIO2_5;
+	} else {
+		val &= ~BCMA_CHIPCTL_4331_EXTPA_EN;
+		val &= ~BCMA_CHIPCTL_4331_EXTPA_ON_GPIO2_5;
+	}
+	bcma_cc_write32(cc, BCMA_CC_CHIPCTL, val);
+}
+
 void bcma_pmu_workarounds(struct bcma_drv_cc *cc)
 {
 	struct bcma_bus *bus = cc->core->bus;
@@ -92,7 +137,7 @@
 		bcma_chipco_chipctl_maskset(cc, 0, ~0, 0x7);
 		break;
 	case 0x4331:
-		pr_err("Enabling Ext PA lines not implemented\n");
+		/* BCM4331 workaround is SPROM-related, we put it in sprom.c */
 		break;
 	case 43224:
 		if (bus->chipinfo.rev == 0) {
@@ -136,3 +181,129 @@
 	bcma_pmu_swreg_init(cc);
 	bcma_pmu_workarounds(cc);
 }
+
+u32 bcma_pmu_alp_clock(struct bcma_drv_cc *cc)
+{
+	struct bcma_bus *bus = cc->core->bus;
+
+	switch (bus->chipinfo.id) {
+	case 0x4716:
+	case 0x4748:
+	case 47162:
+	case 0x4313:
+	case 0x5357:
+	case 0x4749:
+	case 53572:
+		/* always 20Mhz */
+		return 20000 * 1000;
+	case 0x5356:
+	case 0x5300:
+		/* always 25Mhz */
+		return 25000 * 1000;
+	default:
+		pr_warn("No ALP clock specified for %04X device, "
+			"pmu rev. %d, using default %d Hz\n",
+			bus->chipinfo.id, cc->pmu.rev, BCMA_CC_PMU_ALP_CLOCK);
+	}
+	return BCMA_CC_PMU_ALP_CLOCK;
+}
+
+/* Find the output of the "m" pll divider given pll controls that start with
+ * pllreg "pll0" i.e. 12 for main 6 for phy, 0 for misc.
+ */
+static u32 bcma_pmu_clock(struct bcma_drv_cc *cc, u32 pll0, u32 m)
+{
+	u32 tmp, div, ndiv, p1, p2, fc;
+	struct bcma_bus *bus = cc->core->bus;
+
+	BUG_ON((pll0 & 3) || (pll0 > BCMA_CC_PMU4716_MAINPLL_PLL0));
+
+	BUG_ON(!m || m > 4);
+
+	if (bus->chipinfo.id == 0x5357 || bus->chipinfo.id == 0x4749) {
+		/* Detect failure in clock setting */
+		tmp = bcma_cc_read32(cc, BCMA_CC_CHIPSTAT);
+		if (tmp & 0x40000)
+			return 133 * 1000000;
+	}
+
+	tmp = bcma_chipco_pll_read(cc, pll0 + BCMA_CC_PPL_P1P2_OFF);
+	p1 = (tmp & BCMA_CC_PPL_P1_MASK) >> BCMA_CC_PPL_P1_SHIFT;
+	p2 = (tmp & BCMA_CC_PPL_P2_MASK) >> BCMA_CC_PPL_P2_SHIFT;
+
+	tmp = bcma_chipco_pll_read(cc, pll0 + BCMA_CC_PPL_M14_OFF);
+	div = (tmp >> ((m - 1) * BCMA_CC_PPL_MDIV_WIDTH)) &
+		BCMA_CC_PPL_MDIV_MASK;
+
+	tmp = bcma_chipco_pll_read(cc, pll0 + BCMA_CC_PPL_NM5_OFF);
+	ndiv = (tmp & BCMA_CC_PPL_NDIV_MASK) >> BCMA_CC_PPL_NDIV_SHIFT;
+
+	/* Do calculation in Mhz */
+	fc = bcma_pmu_alp_clock(cc) / 1000000;
+	fc = (p1 * ndiv * fc) / p2;
+
+	/* Return clock in Hertz */
+	return (fc / div) * 1000000;
+}
+
+/* query bus clock frequency for PMU-enabled chipcommon */
+u32 bcma_pmu_get_clockcontrol(struct bcma_drv_cc *cc)
+{
+	struct bcma_bus *bus = cc->core->bus;
+
+	switch (bus->chipinfo.id) {
+	case 0x4716:
+	case 0x4748:
+	case 47162:
+		return bcma_pmu_clock(cc, BCMA_CC_PMU4716_MAINPLL_PLL0,
+				      BCMA_CC_PMU5_MAINPLL_SSB);
+	case 0x5356:
+		return bcma_pmu_clock(cc, BCMA_CC_PMU5356_MAINPLL_PLL0,
+				      BCMA_CC_PMU5_MAINPLL_SSB);
+	case 0x5357:
+	case 0x4749:
+		return bcma_pmu_clock(cc, BCMA_CC_PMU5357_MAINPLL_PLL0,
+				      BCMA_CC_PMU5_MAINPLL_SSB);
+	case 0x5300:
+		return bcma_pmu_clock(cc, BCMA_CC_PMU4706_MAINPLL_PLL0,
+				      BCMA_CC_PMU5_MAINPLL_SSB);
+	case 53572:
+		return 75000000;
+	default:
+		pr_warn("No backplane clock specified for %04X device, "
+			"pmu rev. %d, using default %d Hz\n",
+			bus->chipinfo.id, cc->pmu.rev, BCMA_CC_PMU_HT_CLOCK);
+	}
+	return BCMA_CC_PMU_HT_CLOCK;
+}
+
+/* query cpu clock frequency for PMU-enabled chipcommon */
+u32 bcma_pmu_get_clockcpu(struct bcma_drv_cc *cc)
+{
+	struct bcma_bus *bus = cc->core->bus;
+
+	if (bus->chipinfo.id == 53572)
+		return 300000000;
+
+	if (cc->pmu.rev >= 5) {
+		u32 pll;
+		switch (bus->chipinfo.id) {
+		case 0x5356:
+			pll = BCMA_CC_PMU5356_MAINPLL_PLL0;
+			break;
+		case 0x5357:
+		case 0x4749:
+			pll = BCMA_CC_PMU5357_MAINPLL_PLL0;
+			break;
+		default:
+			pll = BCMA_CC_PMU4716_MAINPLL_PLL0;
+			break;
+		}
+
+		/* TODO: if (bus->chipinfo.id == 0x5300)
+		  return si_4706_pmu_clock(sih, osh, cc, PMU4706_MAINPLL_PLL0, PMU5_MAINPLL_CPU); */
+		return bcma_pmu_clock(cc, pll, BCMA_CC_PMU5_MAINPLL_CPU);
+	}
+
+	return bcma_pmu_get_clockcontrol(cc);
+}
diff -ruN linux-3.1.1.orig/drivers/bcma/driver_mips.c linux-3.1.1/drivers/bcma/driver_mips.c
--- linux-3.1.1.orig/drivers/bcma/driver_mips.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-3.1.1/drivers/bcma/driver_mips.c	2011-11-13 17:11:58.579558787 -0800
@@ -0,0 +1,256 @@
+/*
+ * Broadcom specific AMBA
+ * Broadcom MIPS32 74K core driver
+ *
+ * Copyright 2009, Broadcom Corporation
+ * Copyright 2006, 2007, Michael Buesch <mb@bu3sch.de>
+ * Copyright 2010, Bernhard Loos <bernhardloos@googlemail.com>
+ * Copyright 2011, Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+
+#include <linux/bcma/bcma.h>
+
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/time.h>
+
+/* The 47162a0 hangs when reading MIPS DMP registers registers */
+static inline bool bcma_core_mips_bcm47162a0_quirk(struct bcma_device *dev)
+{
+	return dev->bus->chipinfo.id == 47162 && dev->bus->chipinfo.rev == 0 &&
+	       dev->id.id == BCMA_CORE_MIPS_74K;
+}
+
+/* The 5357b0 hangs when reading USB20H DMP registers */
+static inline bool bcma_core_mips_bcm5357b0_quirk(struct bcma_device *dev)
+{
+	return (dev->bus->chipinfo.id == 0x5357 ||
+		dev->bus->chipinfo.id == 0x4749) &&
+	       dev->bus->chipinfo.pkg == 11 &&
+	       dev->id.id == BCMA_CORE_USB20_HOST;
+}
+
+static inline u32 mips_read32(struct bcma_drv_mips *mcore,
+			      u16 offset)
+{
+	return bcma_read32(mcore->core, offset);
+}
+
+static inline void mips_write32(struct bcma_drv_mips *mcore,
+				u16 offset,
+				u32 value)
+{
+	bcma_write32(mcore->core, offset, value);
+}
+
+static const u32 ipsflag_irq_mask[] = {
+	0,
+	BCMA_MIPS_IPSFLAG_IRQ1,
+	BCMA_MIPS_IPSFLAG_IRQ2,
+	BCMA_MIPS_IPSFLAG_IRQ3,
+	BCMA_MIPS_IPSFLAG_IRQ4,
+};
+
+static const u32 ipsflag_irq_shift[] = {
+	0,
+	BCMA_MIPS_IPSFLAG_IRQ1_SHIFT,
+	BCMA_MIPS_IPSFLAG_IRQ2_SHIFT,
+	BCMA_MIPS_IPSFLAG_IRQ3_SHIFT,
+	BCMA_MIPS_IPSFLAG_IRQ4_SHIFT,
+};
+
+static u32 bcma_core_mips_irqflag(struct bcma_device *dev)
+{
+	u32 flag;
+
+	if (bcma_core_mips_bcm47162a0_quirk(dev))
+		return dev->core_index;
+	if (bcma_core_mips_bcm5357b0_quirk(dev))
+		return dev->core_index;
+	flag = bcma_aread32(dev, BCMA_MIPS_OOBSELOUTA30);
+
+	return flag & 0x1F;
+}
+
+/* Get the MIPS IRQ assignment for a specified device.
+ * If unassigned, 0 is returned.
+ */
+unsigned int bcma_core_mips_irq(struct bcma_device *dev)
+{
+	struct bcma_device *mdev = dev->bus->drv_mips.core;
+	u32 irqflag;
+	unsigned int irq;
+
+	irqflag = bcma_core_mips_irqflag(dev);
+
+	for (irq = 1; irq <= 4; irq++)
+		if (bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq)) &
+		    (1 << irqflag))
+			return irq;
+
+	return 0;
+}
+EXPORT_SYMBOL(bcma_core_mips_irq);
+
+static void bcma_core_mips_set_irq(struct bcma_device *dev, unsigned int irq)
+{
+	unsigned int oldirq = bcma_core_mips_irq(dev);
+	struct bcma_bus *bus = dev->bus;
+	struct bcma_device *mdev = bus->drv_mips.core;
+	u32 irqflag;
+
+	irqflag = bcma_core_mips_irqflag(dev);
+	BUG_ON(oldirq == 6);
+
+	dev->irq = irq + 2;
+
+	/* clear the old irq */
+	if (oldirq == 0)
+		bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0),
+			    bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0)) &
+			    ~(1 << irqflag));
+	else
+		bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq), 0);
+
+	/* assign the new one */
+	if (irq == 0) {
+		bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0),
+			    bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0)) |
+			    (1 << irqflag));
+	} else {
+		u32 oldirqflag = bcma_read32(mdev,
+					     BCMA_MIPS_MIPS74K_INTMASK(irq));
+		if (oldirqflag) {
+			struct bcma_device *core;
+
+			/* backplane irq line is in use, find out who uses
+			 * it and set user to irq 0
+			 */
+			list_for_each_entry_reverse(core, &bus->cores, list) {
+				if ((1 << bcma_core_mips_irqflag(core)) ==
+				    oldirqflag) {
+					bcma_core_mips_set_irq(core, 0);
+					break;
+				}
+			}
+		}
+		bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq),
+			     1 << irqflag);
+	}
+
+	pr_info("set_irq: core 0x%04x, irq %d => %d\n",
+		dev->id.id, oldirq + 2, irq + 2);
+}
+
+static void bcma_core_mips_print_irq(struct bcma_device *dev, unsigned int irq)
+{
+	int i;
+	static const char *irq_name[] = {"2(S)", "3", "4", "5", "6", "D", "I"};
+	printk(KERN_INFO KBUILD_MODNAME ": core 0x%04x, irq :", dev->id.id);
+	for (i = 0; i <= 6; i++)
+		printk(" %s%s", irq_name[i], i == irq ? "*" : " ");
+	printk("\n");
+}
+
+static void bcma_core_mips_dump_irq(struct bcma_bus *bus)
+{
+	struct bcma_device *core;
+
+	list_for_each_entry_reverse(core, &bus->cores, list) {
+		bcma_core_mips_print_irq(core, bcma_core_mips_irq(core));
+	}
+}
+
+u32 bcma_cpu_clock(struct bcma_drv_mips *mcore)
+{
+	struct bcma_bus *bus = mcore->core->bus;
+
+	if (bus->drv_cc.capabilities & BCMA_CC_CAP_PMU)
+		return bcma_pmu_get_clockcpu(&bus->drv_cc);
+
+	pr_err("No PMU available, need this to get the cpu clock\n");
+	return 0;
+}
+EXPORT_SYMBOL(bcma_cpu_clock);
+
+static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore)
+{
+	struct bcma_bus *bus = mcore->core->bus;
+
+	switch (bus->drv_cc.capabilities & BCMA_CC_CAP_FLASHT) {
+	case BCMA_CC_FLASHT_STSER:
+	case BCMA_CC_FLASHT_ATSER:
+		pr_err("Serial flash not supported.\n");
+		break;
+	case BCMA_CC_FLASHT_PARA:
+		pr_info("found parallel flash.\n");
+		bus->drv_cc.pflash.window = 0x1c000000;
+		bus->drv_cc.pflash.window_size = 0x02000000;
+
+		if ((bcma_read32(bus->drv_cc.core, BCMA_CC_FLASH_CFG) &
+		     BCMA_CC_FLASH_CFG_DS) == 0)
+			bus->drv_cc.pflash.buswidth = 1;
+		else
+			bus->drv_cc.pflash.buswidth = 2;
+		break;
+	default:
+		pr_err("flash not supported.\n");
+	}
+}
+
+void bcma_core_mips_init(struct bcma_drv_mips *mcore)
+{
+	struct bcma_bus *bus;
+	struct bcma_device *core;
+	bus = mcore->core->bus;
+
+	pr_info("Initializing MIPS core...\n");
+
+	if (!mcore->setup_done)
+		mcore->assigned_irqs = 1;
+
+	/* Assign IRQs to all cores on the bus */
+	list_for_each_entry_reverse(core, &bus->cores, list) {
+		int mips_irq;
+		if (core->irq)
+			continue;
+
+		mips_irq = bcma_core_mips_irq(core);
+		if (mips_irq > 4)
+			core->irq = 0;
+		else
+			core->irq = mips_irq + 2;
+		if (core->irq > 5)
+			continue;
+		switch (core->id.id) {
+		case BCMA_CORE_PCI:
+		case BCMA_CORE_PCIE:
+		case BCMA_CORE_ETHERNET:
+		case BCMA_CORE_ETHERNET_GBIT:
+		case BCMA_CORE_MAC_GBIT:
+		case BCMA_CORE_80211:
+		case BCMA_CORE_USB20_HOST:
+			/* These devices get their own IRQ line if available,
+			 * the rest goes on IRQ0
+			 */
+			if (mcore->assigned_irqs <= 4)
+				bcma_core_mips_set_irq(core,
+						       mcore->assigned_irqs++);
+			break;
+		}
+	}
+	pr_info("IRQ reconfiguration done\n");
+	bcma_core_mips_dump_irq(bus);
+
+	if (mcore->setup_done)
+		return;
+
+	bcma_chipco_serial_init(&bus->drv_cc);
+	bcma_core_mips_flash_detect(mcore);
+	mcore->setup_done = true;
+}
diff -ruN linux-3.1.1.orig/drivers/bcma/driver_pci.c linux-3.1.1/drivers/bcma/driver_pci.c
--- linux-3.1.1.orig/drivers/bcma/driver_pci.c	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/bcma/driver_pci.c	2011-11-13 17:11:58.579558787 -0800
@@ -173,7 +173,7 @@
 		return false;
 
 #ifdef CONFIG_SSB_DRIVER_PCICORE
-	if (bus->sprom.boardflags_lo & SSB_PCICORE_BFL_NOPCI)
+	if (bus->sprom.boardflags_lo & SSB_BFL_NOPCI)
 		return false;
 #endif /* CONFIG_SSB_DRIVER_PCICORE */
 
@@ -189,6 +189,9 @@
 
 void bcma_core_pci_init(struct bcma_drv_pci *pc)
 {
+	if (pc->setup_done)
+		return;
+
 	if (bcma_core_pci_is_in_hostmode(pc)) {
 #ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
 		bcma_core_pci_hostmode_init(pc);
@@ -198,6 +201,8 @@
 	} else {
 		bcma_core_pci_clientmode_init(pc);
 	}
+
+	pc->setup_done = true;
 }
 
 int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core,
@@ -205,7 +210,14 @@
 {
 	struct pci_dev *pdev = pc->core->bus->host_pci;
 	u32 coremask, tmp;
-	int err;
+	int err = 0;
+
+	if (core->bus->hosttype != BCMA_HOSTTYPE_PCI) {
+		/* This bcma device is not on a PCI host-bus. So the IRQs are
+		 * not routed through the PCI core.
+		 * So we must not enable routing through the PCI core. */
+		goto out;
+	}
 
 	err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp);
 	if (err)
diff -ruN linux-3.1.1.orig/drivers/bcma/host_soc.c linux-3.1.1/drivers/bcma/host_soc.c
--- linux-3.1.1.orig/drivers/bcma/host_soc.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-3.1.1/drivers/bcma/host_soc.c	2011-11-13 17:11:58.579558787 -0800
@@ -0,0 +1,183 @@
+/*
+ * Broadcom specific AMBA
+ * System on Chip (SoC) Host
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "bcma_private.h"
+#include "scan.h"
+#include <linux/bcma/bcma.h>
+#include <linux/bcma/bcma_soc.h>
+
+static u8 bcma_host_soc_read8(struct bcma_device *core, u16 offset)
+{
+	return readb(core->io_addr + offset);
+}
+
+static u16 bcma_host_soc_read16(struct bcma_device *core, u16 offset)
+{
+	return readw(core->io_addr + offset);
+}
+
+static u32 bcma_host_soc_read32(struct bcma_device *core, u16 offset)
+{
+	return readl(core->io_addr + offset);
+}
+
+static void bcma_host_soc_write8(struct bcma_device *core, u16 offset,
+				 u8 value)
+{
+	writeb(value, core->io_addr + offset);
+}
+
+static void bcma_host_soc_write16(struct bcma_device *core, u16 offset,
+				 u16 value)
+{
+	writew(value, core->io_addr + offset);
+}
+
+static void bcma_host_soc_write32(struct bcma_device *core, u16 offset,
+				 u32 value)
+{
+	writel(value, core->io_addr + offset);
+}
+
+#ifdef CONFIG_BCMA_BLOCKIO
+static void bcma_host_soc_block_read(struct bcma_device *core, void *buffer,
+				     size_t count, u16 offset, u8 reg_width)
+{
+	void __iomem *addr = core->io_addr + offset;
+
+	switch (reg_width) {
+	case sizeof(u8): {
+		u8 *buf = buffer;
+
+		while (count) {
+			*buf = __raw_readb(addr);
+			buf++;
+			count--;
+		}
+		break;
+	}
+	case sizeof(u16): {
+		__le16 *buf = buffer;
+
+		WARN_ON(count & 1);
+		while (count) {
+			*buf = (__force __le16)__raw_readw(addr);
+			buf++;
+			count -= 2;
+		}
+		break;
+	}
+	case sizeof(u32): {
+		__le32 *buf = buffer;
+
+		WARN_ON(count & 3);
+		while (count) {
+			*buf = (__force __le32)__raw_readl(addr);
+			buf++;
+			count -= 4;
+		}
+		break;
+	}
+	default:
+		WARN_ON(1);
+	}
+}
+
+static void bcma_host_soc_block_write(struct bcma_device *core,
+				      const void *buffer,
+				      size_t count, u16 offset, u8 reg_width)
+{
+	void __iomem *addr = core->io_addr + offset;
+
+	switch (reg_width) {
+	case sizeof(u8): {
+		const u8 *buf = buffer;
+
+		while (count) {
+			__raw_writeb(*buf, addr);
+			buf++;
+			count--;
+		}
+		break;
+	}
+	case sizeof(u16): {
+		const __le16 *buf = buffer;
+
+		WARN_ON(count & 1);
+		while (count) {
+			__raw_writew((__force u16)(*buf), addr);
+			buf++;
+			count -= 2;
+		}
+		break;
+	}
+	case sizeof(u32): {
+		const __le32 *buf = buffer;
+
+		WARN_ON(count & 3);
+		while (count) {
+			__raw_writel((__force u32)(*buf), addr);
+			buf++;
+			count -= 4;
+		}
+		break;
+	}
+	default:
+		WARN_ON(1);
+	}
+}
+#endif /* CONFIG_BCMA_BLOCKIO */
+
+static u32 bcma_host_soc_aread32(struct bcma_device *core, u16 offset)
+{
+	return readl(core->io_wrap + offset);
+}
+
+static void bcma_host_soc_awrite32(struct bcma_device *core, u16 offset,
+				  u32 value)
+{
+	writel(value, core->io_wrap + offset);
+}
+
+const struct bcma_host_ops bcma_host_soc_ops = {
+	.read8		= bcma_host_soc_read8,
+	.read16		= bcma_host_soc_read16,
+	.read32		= bcma_host_soc_read32,
+	.write8		= bcma_host_soc_write8,
+	.write16	= bcma_host_soc_write16,
+	.write32	= bcma_host_soc_write32,
+#ifdef CONFIG_BCMA_BLOCKIO
+	.block_read	= bcma_host_soc_block_read,
+	.block_write	= bcma_host_soc_block_write,
+#endif
+	.aread32	= bcma_host_soc_aread32,
+	.awrite32	= bcma_host_soc_awrite32,
+};
+
+int __init bcma_host_soc_register(struct bcma_soc *soc)
+{
+	struct bcma_bus *bus = &soc->bus;
+	int err;
+
+	/* iomap only first core. We have to read some register on this core
+	 * to scan the bus.
+	 */
+	bus->mmio = ioremap_nocache(BCMA_ADDR_BASE, BCMA_CORE_SIZE * 1);
+	if (!bus->mmio)
+		return -ENOMEM;
+
+	/* Host specific */
+	bus->hosttype = BCMA_HOSTTYPE_SOC;
+	bus->ops = &bcma_host_soc_ops;
+
+	/* Register */
+	err = bcma_bus_early_register(bus, &soc->core_cc, &soc->core_mips);
+	if (err)
+		iounmap(bus->mmio);
+
+	return err;
+}
diff -ruN linux-3.1.1.orig/drivers/bcma/main.c linux-3.1.1/drivers/bcma/main.c
--- linux-3.1.1.orig/drivers/bcma/main.c	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/bcma/main.c	2011-11-13 17:11:58.579558787 -0800
@@ -68,6 +68,10 @@
 static void bcma_release_core_dev(struct device *dev)
 {
 	struct bcma_device *core = container_of(dev, struct bcma_device, dev);
+	if (core->io_addr)
+		iounmap(core->io_addr);
+	if (core->io_wrap)
+		iounmap(core->io_wrap);
 	kfree(core);
 }
 
@@ -82,6 +86,7 @@
 		case BCMA_CORE_CHIPCOMMON:
 		case BCMA_CORE_PCI:
 		case BCMA_CORE_PCIE:
+		case BCMA_CORE_MIPS_74K:
 			continue;
 		}
 
@@ -95,7 +100,10 @@
 			core->dma_dev = &bus->host_pci->dev;
 			core->irq = bus->host_pci->irq;
 			break;
-		case BCMA_HOSTTYPE_NONE:
+		case BCMA_HOSTTYPE_SOC:
+			core->dev.dma_mask = &core->dev.coherent_dma_mask;
+			core->dma_dev = &core->dev;
+			break;
 		case BCMA_HOSTTYPE_SDIO:
 			break;
 		}
@@ -142,6 +150,13 @@
 		bcma_core_chipcommon_init(&bus->drv_cc);
 	}
 
+	/* Init MIPS core */
+	core = bcma_find_core(bus, BCMA_CORE_MIPS_74K);
+	if (core) {
+		bus->drv_mips.core = core;
+		bcma_core_mips_init(&bus->drv_mips);
+	}
+
 	/* Init PCIE core */
 	core = bcma_find_core(bus, BCMA_CORE_PCIE);
 	if (core) {
@@ -171,6 +186,59 @@
 	bcma_unregister_cores(bus);
 }
 
+int __init bcma_bus_early_register(struct bcma_bus *bus,
+				   struct bcma_device *core_cc,
+				   struct bcma_device *core_mips)
+{
+	int err;
+	struct bcma_device *core;
+	struct bcma_device_id match;
+
+	bcma_init_bus(bus);
+
+	match.manuf = BCMA_MANUF_BCM;
+	match.id = BCMA_CORE_CHIPCOMMON;
+	match.class = BCMA_CL_SIM;
+	match.rev = BCMA_ANY_REV;
+
+	/* Scan for chip common core */
+	err = bcma_bus_scan_early(bus, &match, core_cc);
+	if (err) {
+		pr_err("Failed to scan for common core: %d\n", err);
+		return -1;
+	}
+
+	match.manuf = BCMA_MANUF_MIPS;
+	match.id = BCMA_CORE_MIPS_74K;
+	match.class = BCMA_CL_SIM;
+	match.rev = BCMA_ANY_REV;
+
+	/* Scan for mips core */
+	err = bcma_bus_scan_early(bus, &match, core_mips);
+	if (err) {
+		pr_err("Failed to scan for mips core: %d\n", err);
+		return -1;
+	}
+
+	/* Init CC core */
+	core = bcma_find_core(bus, BCMA_CORE_CHIPCOMMON);
+	if (core) {
+		bus->drv_cc.core = core;
+		bcma_core_chipcommon_init(&bus->drv_cc);
+	}
+
+	/* Init MIPS core */
+	core = bcma_find_core(bus, BCMA_CORE_MIPS_74K);
+	if (core) {
+		bus->drv_mips.core = core;
+		bcma_core_mips_init(&bus->drv_mips);
+	}
+
+	pr_info("Early bus registered\n");
+
+	return 0;
+}
+
 int __bcma_driver_register(struct bcma_driver *drv, struct module *owner)
 {
 	drv->drv.name = drv->name;
diff -ruN linux-3.1.1.orig/drivers/bcma/scan.c linux-3.1.1/drivers/bcma/scan.c
--- linux-3.1.1.orig/drivers/bcma/scan.c	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/bcma/scan.c	2011-11-13 17:11:58.579558787 -0800
@@ -200,18 +200,162 @@
 	return addrl;
 }
 
-int bcma_bus_scan(struct bcma_bus *bus)
+static struct bcma_device *bcma_find_core_by_index(struct bcma_bus *bus,
+						   u16 index)
 {
-	u32 erombase;
-	u32 __iomem *eromptr, *eromend;
+	struct bcma_device *core;
 
+	list_for_each_entry(core, &bus->cores, list) {
+		if (core->core_index == index)
+			return core;
+	}
+	return NULL;
+}
+
+static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
+			      struct bcma_device_id *match, int core_num,
+			      struct bcma_device *core)
+{
+	s32 tmp;
+	u8 i, j;
 	s32 cia, cib;
 	u8 ports[2], wrappers[2];
 
+	/* get CIs */
+	cia = bcma_erom_get_ci(bus, eromptr);
+	if (cia < 0) {
+		bcma_erom_push_ent(eromptr);
+		if (bcma_erom_is_end(bus, eromptr))
+			return -ESPIPE;
+		return -EILSEQ;
+	}
+	cib = bcma_erom_get_ci(bus, eromptr);
+	if (cib < 0)
+		return -EILSEQ;
+
+	/* parse CIs */
+	core->id.class = (cia & SCAN_CIA_CLASS) >> SCAN_CIA_CLASS_SHIFT;
+	core->id.id = (cia & SCAN_CIA_ID) >> SCAN_CIA_ID_SHIFT;
+	core->id.manuf = (cia & SCAN_CIA_MANUF) >> SCAN_CIA_MANUF_SHIFT;
+	ports[0] = (cib & SCAN_CIB_NMP) >> SCAN_CIB_NMP_SHIFT;
+	ports[1] = (cib & SCAN_CIB_NSP) >> SCAN_CIB_NSP_SHIFT;
+	wrappers[0] = (cib & SCAN_CIB_NMW) >> SCAN_CIB_NMW_SHIFT;
+	wrappers[1] = (cib & SCAN_CIB_NSW) >> SCAN_CIB_NSW_SHIFT;
+	core->id.rev = (cib & SCAN_CIB_REV) >> SCAN_CIB_REV_SHIFT;
+
+	if (((core->id.manuf == BCMA_MANUF_ARM) &&
+	     (core->id.id == 0xFFF)) ||
+	    (ports[1] == 0)) {
+		bcma_erom_skip_component(bus, eromptr);
+		return -ENXIO;
+	}
+
+	/* check if component is a core at all */
+	if (wrappers[0] + wrappers[1] == 0) {
+		/* we could save addrl of the router
+		if (cid == BCMA_CORE_OOB_ROUTER)
+		 */
+		bcma_erom_skip_component(bus, eromptr);
+		return -ENXIO;
+	}
+
+	if (bcma_erom_is_bridge(bus, eromptr)) {
+		bcma_erom_skip_component(bus, eromptr);
+		return -ENXIO;
+	}
+
+	if (bcma_find_core_by_index(bus, core_num)) {
+		bcma_erom_skip_component(bus, eromptr);
+		return -ENODEV;
+	}
+
+	if (match && ((match->manuf != BCMA_ANY_MANUF &&
+	      match->manuf != core->id.manuf) ||
+	     (match->id != BCMA_ANY_ID && match->id != core->id.id) ||
+	     (match->rev != BCMA_ANY_REV && match->rev != core->id.rev) ||
+	     (match->class != BCMA_ANY_CLASS && match->class != core->id.class)
+	    )) {
+		bcma_erom_skip_component(bus, eromptr);
+		return -ENODEV;
+	}
+
+	/* get & parse master ports */
+	for (i = 0; i < ports[0]; i++) {
+		s32 mst_port_d = bcma_erom_get_mst_port(bus, eromptr);
+		if (mst_port_d < 0)
+			return -EILSEQ;
+	}
+
+	/* get & parse slave ports */
+	for (i = 0; i < ports[1]; i++) {
+		for (j = 0; ; j++) {
+			tmp = bcma_erom_get_addr_desc(bus, eromptr,
+				SCAN_ADDR_TYPE_SLAVE, i);
+			if (tmp < 0) {
+				/* no more entries for port _i_ */
+				/* pr_debug("erom: slave port %d "
+				 * "has %d descriptors\n", i, j); */
+				break;
+			} else {
+				if (i == 0 && j == 0)
+					core->addr = tmp;
+			}
+		}
+	}
+
+	/* get & parse master wrappers */
+	for (i = 0; i < wrappers[0]; i++) {
+		for (j = 0; ; j++) {
+			tmp = bcma_erom_get_addr_desc(bus, eromptr,
+				SCAN_ADDR_TYPE_MWRAP, i);
+			if (tmp < 0) {
+				/* no more entries for port _i_ */
+				/* pr_debug("erom: master wrapper %d "
+				 * "has %d descriptors\n", i, j); */
+				break;
+			} else {
+				if (i == 0 && j == 0)
+					core->wrap = tmp;
+			}
+		}
+	}
+
+	/* get & parse slave wrappers */
+	for (i = 0; i < wrappers[1]; i++) {
+		u8 hack = (ports[1] == 1) ? 0 : 1;
+		for (j = 0; ; j++) {
+			tmp = bcma_erom_get_addr_desc(bus, eromptr,
+				SCAN_ADDR_TYPE_SWRAP, i + hack);
+			if (tmp < 0) {
+				/* no more entries for port _i_ */
+				/* pr_debug("erom: master wrapper %d "
+				 * has %d descriptors\n", i, j); */
+				break;
+			} else {
+				if (wrappers[0] == 0 && !i && !j)
+					core->wrap = tmp;
+			}
+		}
+	}
+	if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
+		core->io_addr = ioremap_nocache(core->addr, BCMA_CORE_SIZE);
+		if (!core->io_addr)
+			return -ENOMEM;
+		core->io_wrap = ioremap_nocache(core->wrap, BCMA_CORE_SIZE);
+		if (!core->io_wrap) {
+			iounmap(core->io_addr);
+			return -ENOMEM;
+		}
+	}
+	return 0;
+}
+
+void bcma_init_bus(struct bcma_bus *bus)
+{
 	s32 tmp;
-	u8 i, j;
 
-	int err;
+	if (bus->init_done)
+		return;
 
 	INIT_LIST_HEAD(&bus->cores);
 	bus->nr_cores = 0;
@@ -222,9 +366,27 @@
 	bus->chipinfo.id = (tmp & BCMA_CC_ID_ID) >> BCMA_CC_ID_ID_SHIFT;
 	bus->chipinfo.rev = (tmp & BCMA_CC_ID_REV) >> BCMA_CC_ID_REV_SHIFT;
 	bus->chipinfo.pkg = (tmp & BCMA_CC_ID_PKG) >> BCMA_CC_ID_PKG_SHIFT;
+	bus->init_done = true;
+}
+
+int bcma_bus_scan(struct bcma_bus *bus)
+{
+	u32 erombase;
+	u32 __iomem *eromptr, *eromend;
+
+	int err, core_num = 0;
+
+	bcma_init_bus(bus);
 
 	erombase = bcma_scan_read32(bus, 0, BCMA_CC_EROM);
-	eromptr = bus->mmio;
+	if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
+		eromptr = ioremap_nocache(erombase, BCMA_CORE_SIZE);
+		if (!eromptr)
+			return -ENOMEM;
+	} else {
+		eromptr = bus->mmio;
+	}
+
 	eromend = eromptr + BCMA_CORE_SIZE / sizeof(u32);
 
 	bcma_scan_switch_core(bus, erombase);
@@ -236,125 +398,89 @@
 		INIT_LIST_HEAD(&core->list);
 		core->bus = bus;
 
-		/* get CIs */
-		cia = bcma_erom_get_ci(bus, &eromptr);
-		if (cia < 0) {
-			bcma_erom_push_ent(&eromptr);
-			if (bcma_erom_is_end(bus, &eromptr))
-				break;
-			err= -EILSEQ;
-			goto out;
-		}
-		cib = bcma_erom_get_ci(bus, &eromptr);
-		if (cib < 0) {
-			err= -EILSEQ;
-			goto out;
-		}
-
-		/* parse CIs */
-		core->id.class = (cia & SCAN_CIA_CLASS) >> SCAN_CIA_CLASS_SHIFT;
-		core->id.id = (cia & SCAN_CIA_ID) >> SCAN_CIA_ID_SHIFT;
-		core->id.manuf = (cia & SCAN_CIA_MANUF) >> SCAN_CIA_MANUF_SHIFT;
-		ports[0] = (cib & SCAN_CIB_NMP) >> SCAN_CIB_NMP_SHIFT;
-		ports[1] = (cib & SCAN_CIB_NSP) >> SCAN_CIB_NSP_SHIFT;
-		wrappers[0] = (cib & SCAN_CIB_NMW) >> SCAN_CIB_NMW_SHIFT;
-		wrappers[1] = (cib & SCAN_CIB_NSW) >> SCAN_CIB_NSW_SHIFT;
-		core->id.rev = (cib & SCAN_CIB_REV) >> SCAN_CIB_REV_SHIFT;
-
-		if (((core->id.manuf == BCMA_MANUF_ARM) &&
-		     (core->id.id == 0xFFF)) ||
-		    (ports[1] == 0)) {
-			bcma_erom_skip_component(bus, &eromptr);
+		err = bcma_get_next_core(bus, &eromptr, NULL, core_num, core);
+		if (err == -ENODEV) {
+			core_num++;
 			continue;
-		}
-
-		/* check if component is a core at all */
-		if (wrappers[0] + wrappers[1] == 0) {
-			/* we could save addrl of the router
-			if (cid == BCMA_CORE_OOB_ROUTER)
-			 */
-			bcma_erom_skip_component(bus, &eromptr);
+		} else if (err == -ENXIO)
 			continue;
-		}
+		else if (err == -ESPIPE)
+			break;
+		else if (err < 0)
+			return err;
 
-		if (bcma_erom_is_bridge(bus, &eromptr)) {
-			bcma_erom_skip_component(bus, &eromptr);
-			continue;
-		}
+		core->core_index = core_num++;
+		bus->nr_cores++;
 
-		/* get & parse master ports */
-		for (i = 0; i < ports[0]; i++) {
-			u32 mst_port_d = bcma_erom_get_mst_port(bus, &eromptr);
-			if (mst_port_d < 0) {
-				err= -EILSEQ;
-				goto out;
-			}
-		}
+		pr_info("Core %d found: %s "
+			"(manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n",
+			core->core_index, bcma_device_name(&core->id),
+			core->id.manuf, core->id.id, core->id.rev,
+			core->id.class);
 
-		/* get & parse slave ports */
-		for (i = 0; i < ports[1]; i++) {
-			for (j = 0; ; j++) {
-				tmp = bcma_erom_get_addr_desc(bus, &eromptr,
-					SCAN_ADDR_TYPE_SLAVE, i);
-				if (tmp < 0) {
-					/* no more entries for port _i_ */
-					/* pr_debug("erom: slave port %d "
-					 * "has %d descriptors\n", i, j); */
-					break;
-				} else {
-					if (i == 0 && j == 0)
-						core->addr = tmp;
-				}
-			}
-		}
+		list_add(&core->list, &bus->cores);
+	}
 
-		/* get & parse master wrappers */
-		for (i = 0; i < wrappers[0]; i++) {
-			for (j = 0; ; j++) {
-				tmp = bcma_erom_get_addr_desc(bus, &eromptr,
-					SCAN_ADDR_TYPE_MWRAP, i);
-				if (tmp < 0) {
-					/* no more entries for port _i_ */
-					/* pr_debug("erom: master wrapper %d "
-					 * "has %d descriptors\n", i, j); */
-					break;
-				} else {
-					if (i == 0 && j == 0)
-						core->wrap = tmp;
-				}
-			}
-		}
+	if (bus->hosttype == BCMA_HOSTTYPE_SOC)
+		iounmap(eromptr);
 
-		/* get & parse slave wrappers */
-		for (i = 0; i < wrappers[1]; i++) {
-			u8 hack = (ports[1] == 1) ? 0 : 1;
-			for (j = 0; ; j++) {
-				tmp = bcma_erom_get_addr_desc(bus, &eromptr,
-					SCAN_ADDR_TYPE_SWRAP, i + hack);
-				if (tmp < 0) {
-					/* no more entries for port _i_ */
-					/* pr_debug("erom: master wrapper %d "
-					 * has %d descriptors\n", i, j); */
-					break;
-				} else {
-					if (wrappers[0] == 0 && !i && !j)
-						core->wrap = tmp;
-				}
-			}
-		}
+	return 0;
+}
+
+int __init bcma_bus_scan_early(struct bcma_bus *bus,
+			       struct bcma_device_id *match,
+			       struct bcma_device *core)
+{
+	u32 erombase;
+	u32 __iomem *eromptr, *eromend;
 
+	int err = -ENODEV;
+	int core_num = 0;
+
+	erombase = bcma_scan_read32(bus, 0, BCMA_CC_EROM);
+	if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
+		eromptr = ioremap_nocache(erombase, BCMA_CORE_SIZE);
+		if (!eromptr)
+			return -ENOMEM;
+	} else {
+		eromptr = bus->mmio;
+	}
+
+	eromend = eromptr + BCMA_CORE_SIZE / sizeof(u32);
+
+	bcma_scan_switch_core(bus, erombase);
+
+	while (eromptr < eromend) {
+		memset(core, 0, sizeof(*core));
+		INIT_LIST_HEAD(&core->list);
+		core->bus = bus;
+
+		err = bcma_get_next_core(bus, &eromptr, match, core_num, core);
+		if (err == -ENODEV) {
+			core_num++;
+			continue;
+		} else if (err == -ENXIO)
+			continue;
+		else if (err == -ESPIPE)
+			break;
+		else if (err < 0)
+			return err;
+
+		core->core_index = core_num++;
+		bus->nr_cores++;
 		pr_info("Core %d found: %s "
 			"(manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n",
-			bus->nr_cores, bcma_device_name(&core->id),
+			core->core_index, bcma_device_name(&core->id),
 			core->id.manuf, core->id.id, core->id.rev,
 			core->id.class);
 
-		core->core_index = bus->nr_cores++;
 		list_add(&core->list, &bus->cores);
-		continue;
-out:
-		return err;
+		err = 0;
+		break;
 	}
 
-	return 0;
+	if (bus->hosttype == BCMA_HOSTTYPE_SOC)
+		iounmap(eromptr);
+
+	return err;
 }
diff -ruN linux-3.1.1.orig/drivers/bcma/sprom.c linux-3.1.1/drivers/bcma/sprom.c
--- linux-3.1.1.orig/drivers/bcma/sprom.c	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/bcma/sprom.c	2011-11-13 17:11:58.579558787 -0800
@@ -133,6 +133,15 @@
 		v = sprom[SPOFF(SSB_SPROM8_IL0MAC) + i];
 		*(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v);
 	}
+
+	bus->sprom.board_rev = sprom[SPOFF(SSB_SPROM8_BOARDREV)];
+
+	bus->sprom.boardflags_lo = sprom[SPOFF(SSB_SPROM8_BFLLO)];
+	bus->sprom.boardflags_hi = sprom[SPOFF(SSB_SPROM8_BFLHI)];
+	bus->sprom.boardflags2_lo = sprom[SPOFF(SSB_SPROM8_BFL2LO)];
+	bus->sprom.boardflags2_hi = sprom[SPOFF(SSB_SPROM8_BFL2HI)];
+
+	bus->sprom.country_code = sprom[SPOFF(SSB_SPROM8_CCODE)];
 }
 
 int bcma_sprom_get(struct bcma_bus *bus)
@@ -152,6 +161,9 @@
 	if (!sprom)
 		return -ENOMEM;
 
+	if (bus->chipinfo.id == 0x4331)
+		bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false);
+
 	/* Most cards have SPROM moved by additional offset 0x30 (48 dwords).
 	 * According to brcm80211 this applies to cards with PCIe rev >= 6
 	 * TODO: understand this condition and use it */
@@ -159,6 +171,9 @@
 		BCMA_CC_SPROM_PCIE6;
 	bcma_sprom_read(bus, offset, sprom);
 
+	if (bus->chipinfo.id == 0x4331)
+		bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true);
+
 	err = bcma_sprom_valid(sprom);
 	if (err)
 		goto out;
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/Kconfig linux-3.1.1/drivers/net/wireless/b43/Kconfig
--- linux-3.1.1.orig/drivers/net/wireless/b43/Kconfig	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/Kconfig	2011-11-13 17:11:58.599558349 -0800
@@ -114,22 +114,22 @@
 	  affect other devices support and may provide support for basic needs.
 
 config B43_PHY_LP
-	bool "Support for low-power (LP-PHY) devices (EXPERIMENTAL)"
-	depends on B43 && EXPERIMENTAL
+	bool "Support for low-power (LP-PHY) devices"
+	depends on B43
 	default y
 	---help---
 	  Support for the LP-PHY.
 	  The LP-PHY is a low-power PHY built into some notebooks
-	  and embedded devices. It supports 802.11a/g
+	  and embedded devices. It supports 802.11a/b/g
 	  (802.11a support is optional, and currently disabled).
 
 config B43_PHY_HT
-	bool "Support for HT-PHY devices (BROKEN)"
-	depends on B43 && BROKEN
+	bool "Support for HT-PHY (high throughput) devices (EXPERIMENTAL)"
+	depends on B43 && EXPERIMENTAL
 	---help---
 	  Support for the HT-PHY.
 
-	  Say N, this is BROKEN and crashes driver.
+	  Enables support for BCM4331 and possibly other chipsets with that PHY.
 
 config B43_PHY_LCN
 	bool "Support for LCN-PHY devices (BROKEN)"
@@ -169,13 +169,3 @@
 	  Say N, if you are a distributor or user building a release kernel
 	  for production use.
 	  Only say Y, if you are debugging a problem in the b43 driver sourcecode.
-
-config B43_FORCE_PIO
-	bool "Force usage of PIO instead of DMA"
-	depends on B43 && B43_DEBUG
-	---help---
-	  This will disable DMA and always enable PIO instead.
-
-	  Say N!
-	  This is only for debugging the PIO engine code. You do
-	  _NOT_ want to enable this.
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/b43.h linux-3.1.1/drivers/net/wireless/b43/b43.h
--- linux-3.1.1.orig/drivers/net/wireless/b43/b43.h	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/b43.h	2011-11-13 17:11:58.599558349 -0800
@@ -17,11 +17,6 @@
 #include "phy_common.h"
 
 
-/* The unique identifier of the firmware that's officially supported by
- * this driver version. */
-#define B43_SUPPORTED_FIRMWARE_ID	"FW13"
-
-
 #ifdef CONFIG_B43_DEBUG
 # define B43_DEBUG	1
 #else
@@ -112,21 +107,61 @@
 #define B43_MMIO_RADIO_HWENABLED_LO	0x49A
 #define B43_MMIO_GPIO_CONTROL		0x49C
 #define B43_MMIO_GPIO_MASK		0x49E
+#define B43_MMIO_TXE0_CTL		0x500
+#define B43_MMIO_TXE0_AUX		0x502
+#define B43_MMIO_TXE0_TS_LOC		0x504
+#define B43_MMIO_TXE0_TIME_OUT		0x506
+#define B43_MMIO_TXE0_WM_0		0x508
+#define B43_MMIO_TXE0_WM_1		0x50A
+#define B43_MMIO_TXE0_PHYCTL		0x50C
+#define B43_MMIO_TXE0_STATUS		0x50E
+#define B43_MMIO_TXE0_MMPLCP0		0x510
+#define B43_MMIO_TXE0_MMPLCP1		0x512
+#define B43_MMIO_TXE0_PHYCTL1		0x514
+#define B43_MMIO_XMTFIFODEF		0x520
+#define B43_MMIO_XMTFIFO_FRAME_CNT	0x522	/* core rev>= 16 only */
+#define B43_MMIO_XMTFIFO_BYTE_CNT	0x524	/* core rev>= 16 only */
+#define B43_MMIO_XMTFIFO_HEAD		0x526	/* core rev>= 16 only */
+#define B43_MMIO_XMTFIFO_RD_PTR		0x528	/* core rev>= 16 only */
+#define B43_MMIO_XMTFIFO_WR_PTR		0x52A	/* core rev>= 16 only */
+#define B43_MMIO_XMTFIFODEF1		0x52C	/* core rev>= 16 only */
+#define B43_MMIO_XMTFIFOCMD		0x540
+#define B43_MMIO_XMTFIFOFLUSH		0x542
+#define B43_MMIO_XMTFIFOTHRESH		0x544
+#define B43_MMIO_XMTFIFORDY		0x546
+#define B43_MMIO_XMTFIFOPRIRDY		0x548
+#define B43_MMIO_XMTFIFORQPRI		0x54A
+#define B43_MMIO_XMTTPLATETXPTR		0x54C
+#define B43_MMIO_XMTTPLATEPTR		0x550
+#define B43_MMIO_SMPL_CLCT_STRPTR	0x552	/* core rev>= 22 only */
+#define B43_MMIO_SMPL_CLCT_STPPTR	0x554	/* core rev>= 22 only */
+#define B43_MMIO_SMPL_CLCT_CURPTR	0x556	/* core rev>= 22 only */
+#define B43_MMIO_XMTTPLATEDATALO	0x560
+#define B43_MMIO_XMTTPLATEDATAHI	0x562
+#define B43_MMIO_XMTSEL			0x568
+#define B43_MMIO_XMTTXCNT		0x56A
+#define B43_MMIO_XMTTXSHMADDR		0x56C
 #define B43_MMIO_TSF_CFP_START_LOW	0x604
 #define B43_MMIO_TSF_CFP_START_HIGH	0x606
 #define B43_MMIO_TSF_CFP_PRETBTT	0x612
+#define B43_MMIO_TSF_CLK_FRAC_LOW	0x62E
+#define B43_MMIO_TSF_CLK_FRAC_HIGH	0x630
 #define B43_MMIO_TSF_0			0x632	/* core rev < 3 only */
 #define B43_MMIO_TSF_1			0x634	/* core rev < 3 only */
 #define B43_MMIO_TSF_2			0x636	/* core rev < 3 only */
 #define B43_MMIO_TSF_3			0x638	/* core rev < 3 only */
 #define B43_MMIO_RNG			0x65A
 #define B43_MMIO_IFSSLOT		0x684	/* Interframe slot time */
-#define B43_MMIO_IFSCTL			0x688 /* Interframe space control */
+#define B43_MMIO_IFSCTL			0x688	/* Interframe space control */
+#define B43_MMIO_IFSSTAT		0x690
+#define B43_MMIO_IFSMEDBUSYCTL		0x692
+#define B43_MMIO_IFTXDUR		0x694
 #define  B43_MMIO_IFSCTL_USE_EDCF	0x0004
 #define B43_MMIO_POWERUP_DELAY		0x6A8
 #define B43_MMIO_BTCOEX_CTL		0x6B4 /* Bluetooth Coexistence Control */
 #define B43_MMIO_BTCOEX_STAT		0x6B6 /* Bluetooth Coexistence Status */
 #define B43_MMIO_BTCOEX_TXCTL		0x6B8 /* Bluetooth Coexistence Transmit Control */
+#define B43_MMIO_WEPCTL			0x7C0
 
 /* SPROM boardflags_lo values */
 #define B43_BFL_BTCOEXIST		0x0001	/* implements Bluetooth coexistance */
@@ -594,6 +629,7 @@
 	struct b43_dmaring *rx_ring;
 
 	u32 translation; /* Routing bits */
+	bool translation_in_low; /* Should translation bit go into low addr? */
 	bool parity; /* Check for parity */
 };
 
@@ -694,6 +730,12 @@
 	enum b43_firmware_file_type type;
 };
 
+enum b43_firmware_hdr_format {
+	B43_FW_HDR_598,
+	B43_FW_HDR_410,
+	B43_FW_HDR_351,
+};
+
 /* Pointers to the firmware data and meta information about it. */
 struct b43_firmware {
 	/* Microcode */
@@ -710,6 +752,9 @@
 	/* Firmware patchlevel */
 	u16 patch;
 
+	/* Format of header used by firmware */
+	enum b43_firmware_hdr_format hdr_format;
+
 	/* Set to true, if we are using an opensource firmware.
 	 * Use this to check for proprietary vs opensource. */
 	bool opensource;
@@ -875,7 +920,7 @@
 	struct b43_leds leds;
 
 	/* Kmalloc'ed scratch space for PIO TX/RX. Protected by wl->mutex. */
-	u8 pio_scratchspace[110] __attribute__((__aligned__(8)));
+	u8 pio_scratchspace[118] __attribute__((__aligned__(8)));
 	u8 pio_tailspace[4] __attribute__((__aligned__(8)));
 };
 
@@ -965,12 +1010,6 @@
 	return dev->__using_pio_transfers;
 }
 
-#ifdef CONFIG_B43_FORCE_PIO
-# define B43_PIO_DEFAULT 1
-#else
-# define B43_PIO_DEFAULT 0
-#endif
-
 /* Message printing */
 void b43info(struct b43_wl *wl, const char *fmt, ...)
     __attribute__ ((format(printf, 2, 3)));
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/bus.c linux-3.1.1/drivers/net/wireless/b43/bus.c
--- linux-3.1.1.orig/drivers/net/wireless/b43/bus.c	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/bus.c	2011-11-13 17:11:58.599558349 -0800
@@ -3,6 +3,8 @@
   Broadcom B43 wireless driver
   Bus abstraction layer
 
+  Copyright (c) 2011 Rafał Miłecki <zajec5@gmail.com>
+
   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
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/dma.c linux-3.1.1/drivers/net/wireless/b43/dma.c
--- linux-3.1.1.orig/drivers/net/wireless/b43/dma.c	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/dma.c	2011-11-13 17:11:58.599558349 -0800
@@ -47,6 +47,38 @@
  * into separate slots. */
 #define TX_SLOTS_PER_FRAME	2
 
+static u32 b43_dma_address(struct b43_dma *dma, dma_addr_t dmaaddr,
+			   enum b43_addrtype addrtype)
+{
+	u32 uninitialized_var(addr);
+
+	switch (addrtype) {
+	case B43_DMA_ADDR_LOW:
+		addr = lower_32_bits(dmaaddr);
+		if (dma->translation_in_low) {
+			addr &= ~SSB_DMA_TRANSLATION_MASK;
+			addr |= dma->translation;
+		}
+		break;
+	case B43_DMA_ADDR_HIGH:
+		addr = upper_32_bits(dmaaddr);
+		if (!dma->translation_in_low) {
+			addr &= ~SSB_DMA_TRANSLATION_MASK;
+			addr |= dma->translation;
+		}
+		break;
+	case B43_DMA_ADDR_EXT:
+		if (dma->translation_in_low)
+			addr = lower_32_bits(dmaaddr);
+		else
+			addr = upper_32_bits(dmaaddr);
+		addr &= SSB_DMA_TRANSLATION_MASK;
+		addr >>= SSB_DMA_TRANSLATION_SHIFT;
+		break;
+	}
+
+	return addr;
+}
 
 /* 32bit DMA ops. */
 static
@@ -77,10 +109,9 @@
 	slot = (int)(&(desc->dma32) - descbase);
 	B43_WARN_ON(!(slot >= 0 && slot < ring->nr_slots));
 
-	addr = (u32) (dmaaddr & ~SSB_DMA_TRANSLATION_MASK);
-	addrext = (u32) (dmaaddr & SSB_DMA_TRANSLATION_MASK)
-	    >> SSB_DMA_TRANSLATION_SHIFT;
-	addr |= ring->dev->dma.translation;
+	addr = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_LOW);
+	addrext = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_EXT);
+
 	ctl = bufsize & B43_DMA32_DCTL_BYTECNT;
 	if (slot == ring->nr_slots - 1)
 		ctl |= B43_DMA32_DCTL_DTABLEEND;
@@ -170,11 +201,10 @@
 	slot = (int)(&(desc->dma64) - descbase);
 	B43_WARN_ON(!(slot >= 0 && slot < ring->nr_slots));
 
-	addrlo = (u32) (dmaaddr & 0xFFFFFFFF);
-	addrhi = (((u64) dmaaddr >> 32) & ~SSB_DMA_TRANSLATION_MASK);
-	addrext = (((u64) dmaaddr >> 32) & SSB_DMA_TRANSLATION_MASK)
-	    >> SSB_DMA_TRANSLATION_SHIFT;
-	addrhi |= ring->dev->dma.translation;
+	addrlo = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_LOW);
+	addrhi = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_HIGH);
+	addrext = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_EXT);
+
 	if (slot == ring->nr_slots - 1)
 		ctl0 |= B43_DMA64_DCTL0_DTABLEEND;
 	if (start)
@@ -389,33 +419,34 @@
 	gfp_t flags = GFP_KERNEL;
 
 	/* The specs call for 4K buffers for 30- and 32-bit DMA with 4K
-	 * alignment and 8K buffers for 64-bit DMA with 8K alignment. Testing
-	 * has shown that 4K is sufficient for the latter as long as the buffer
-	 * does not cross an 8K boundary.
-	 *
-	 * For unknown reasons - possibly a hardware error - the BCM4311 rev
-	 * 02, which uses 64-bit DMA, needs the ring buffer in very low memory,
-	 * which accounts for the GFP_DMA flag below.
-	 *
-	 * The flags here must match the flags in free_ringmemory below!
+	 * alignment and 8K buffers for 64-bit DMA with 8K alignment.
+	 * In practice we could use smaller buffers for the latter, but the
+	 * alignment is really important because of the hardware bug. If bit
+	 * 0x00001000 is used in DMA address, some hardware (like BCM4331)
+	 * copies that bit into B43_DMA64_RXSTATUS and we get false values from
+	 * B43_DMA64_RXSTATDPTR. Let's just use 8K buffers even if we don't use
+	 * more than 256 slots for ring.
 	 */
-	if (ring->type == B43_DMA_64BIT)
-		flags |= GFP_DMA;
+	u16 ring_mem_size = (ring->type == B43_DMA_64BIT) ?
+				B43_DMA64_RINGMEMSIZE : B43_DMA32_RINGMEMSIZE;
+
 	ring->descbase = dma_alloc_coherent(ring->dev->dev->dma_dev,
-					    B43_DMA_RINGMEMSIZE,
-					    &(ring->dmabase), flags);
+					    ring_mem_size, &(ring->dmabase),
+					    flags);
 	if (!ring->descbase) {
 		b43err(ring->dev->wl, "DMA ringmemory allocation failed\n");
 		return -ENOMEM;
 	}
-	memset(ring->descbase, 0, B43_DMA_RINGMEMSIZE);
+	memset(ring->descbase, 0, ring_mem_size);
 
 	return 0;
 }
 
 static void free_ringmemory(struct b43_dmaring *ring)
 {
-	dma_free_coherent(ring->dev->dev->dma_dev, B43_DMA_RINGMEMSIZE,
+	u16 ring_mem_size = (ring->type == B43_DMA_64BIT) ?
+				B43_DMA64_RINGMEMSIZE : B43_DMA32_RINGMEMSIZE;
+	dma_free_coherent(ring->dev->dev->dma_dev, ring_mem_size,
 			  ring->descbase, ring->dmabase);
 }
 
@@ -658,41 +689,37 @@
 	int err = 0;
 	u32 value;
 	u32 addrext;
-	u32 trans = ring->dev->dma.translation;
 	bool parity = ring->dev->dma.parity;
+	u32 addrlo;
+	u32 addrhi;
 
 	if (ring->tx) {
 		if (ring->type == B43_DMA_64BIT) {
 			u64 ringbase = (u64) (ring->dmabase);
+			addrext = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_EXT);
+			addrlo = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_LOW);
+			addrhi = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_HIGH);
 
-			addrext = ((ringbase >> 32) & SSB_DMA_TRANSLATION_MASK)
-			    >> SSB_DMA_TRANSLATION_SHIFT;
 			value = B43_DMA64_TXENABLE;
 			value |= (addrext << B43_DMA64_TXADDREXT_SHIFT)
 			    & B43_DMA64_TXADDREXT_MASK;
 			if (!parity)
 				value |= B43_DMA64_TXPARITYDISABLE;
 			b43_dma_write(ring, B43_DMA64_TXCTL, value);
-			b43_dma_write(ring, B43_DMA64_TXRINGLO,
-				      (ringbase & 0xFFFFFFFF));
-			b43_dma_write(ring, B43_DMA64_TXRINGHI,
-				      ((ringbase >> 32) &
-				       ~SSB_DMA_TRANSLATION_MASK)
-				      | trans);
+			b43_dma_write(ring, B43_DMA64_TXRINGLO, addrlo);
+			b43_dma_write(ring, B43_DMA64_TXRINGHI, addrhi);
 		} else {
 			u32 ringbase = (u32) (ring->dmabase);
+			addrext = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_EXT);
+			addrlo = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_LOW);
 
-			addrext = (ringbase & SSB_DMA_TRANSLATION_MASK)
-			    >> SSB_DMA_TRANSLATION_SHIFT;
 			value = B43_DMA32_TXENABLE;
 			value |= (addrext << B43_DMA32_TXADDREXT_SHIFT)
 			    & B43_DMA32_TXADDREXT_MASK;
 			if (!parity)
 				value |= B43_DMA32_TXPARITYDISABLE;
 			b43_dma_write(ring, B43_DMA32_TXCTL, value);
-			b43_dma_write(ring, B43_DMA32_TXRING,
-				      (ringbase & ~SSB_DMA_TRANSLATION_MASK)
-				      | trans);
+			b43_dma_write(ring, B43_DMA32_TXRING, addrlo);
 		}
 	} else {
 		err = alloc_initial_descbuffers(ring);
@@ -700,9 +727,10 @@
 			goto out;
 		if (ring->type == B43_DMA_64BIT) {
 			u64 ringbase = (u64) (ring->dmabase);
+			addrext = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_EXT);
+			addrlo = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_LOW);
+			addrhi = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_HIGH);
 
-			addrext = ((ringbase >> 32) & SSB_DMA_TRANSLATION_MASK)
-			    >> SSB_DMA_TRANSLATION_SHIFT;
 			value = (ring->frameoffset << B43_DMA64_RXFROFF_SHIFT);
 			value |= B43_DMA64_RXENABLE;
 			value |= (addrext << B43_DMA64_RXADDREXT_SHIFT)
@@ -710,19 +738,15 @@
 			if (!parity)
 				value |= B43_DMA64_RXPARITYDISABLE;
 			b43_dma_write(ring, B43_DMA64_RXCTL, value);
-			b43_dma_write(ring, B43_DMA64_RXRINGLO,
-				      (ringbase & 0xFFFFFFFF));
-			b43_dma_write(ring, B43_DMA64_RXRINGHI,
-				      ((ringbase >> 32) &
-				       ~SSB_DMA_TRANSLATION_MASK)
-				      | trans);
+			b43_dma_write(ring, B43_DMA64_RXRINGLO, addrlo);
+			b43_dma_write(ring, B43_DMA64_RXRINGHI, addrhi);
 			b43_dma_write(ring, B43_DMA64_RXINDEX, ring->nr_slots *
 				      sizeof(struct b43_dmadesc64));
 		} else {
 			u32 ringbase = (u32) (ring->dmabase);
+			addrext = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_EXT);
+			addrlo = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_LOW);
 
-			addrext = (ringbase & SSB_DMA_TRANSLATION_MASK)
-			    >> SSB_DMA_TRANSLATION_SHIFT;
 			value = (ring->frameoffset << B43_DMA32_RXFROFF_SHIFT);
 			value |= B43_DMA32_RXENABLE;
 			value |= (addrext << B43_DMA32_RXADDREXT_SHIFT)
@@ -730,9 +754,7 @@
 			if (!parity)
 				value |= B43_DMA32_RXPARITYDISABLE;
 			b43_dma_write(ring, B43_DMA32_RXCTL, value);
-			b43_dma_write(ring, B43_DMA32_RXRING,
-				      (ringbase & ~SSB_DMA_TRANSLATION_MASK)
-				      | trans);
+			b43_dma_write(ring, B43_DMA32_RXRING, addrlo);
 			b43_dma_write(ring, B43_DMA32_RXINDEX, ring->nr_slots *
 				      sizeof(struct b43_dmadesc32));
 		}
@@ -872,8 +894,17 @@
 		ring->current_slot = -1;
 	} else {
 		if (ring->index == 0) {
-			ring->rx_buffersize = B43_DMA0_RX_BUFFERSIZE;
-			ring->frameoffset = B43_DMA0_RX_FRAMEOFFSET;
+			switch (dev->fw.hdr_format) {
+			case B43_FW_HDR_598:
+				ring->rx_buffersize = B43_DMA0_RX_FW598_BUFSIZE;
+				ring->frameoffset = B43_DMA0_RX_FW598_FO;
+				break;
+			case B43_FW_HDR_410:
+			case B43_FW_HDR_351:
+				ring->rx_buffersize = B43_DMA0_RX_FW351_BUFSIZE;
+				ring->frameoffset = B43_DMA0_RX_FW351_FO;
+				break;
+			}
 		} else
 			B43_WARN_ON(1);
 	}
@@ -1066,6 +1097,25 @@
 	return 0;
 }
 
+/* Some hardware with 64-bit DMA seems to be bugged and looks for translation
+ * bit in low address word instead of high one.
+ */
+static bool b43_dma_translation_in_low_word(struct b43_wldev *dev,
+					    enum b43_dmatype type)
+{
+	if (type != B43_DMA_64BIT)
+		return 1;
+
+#ifdef CONFIG_B43_SSB
+	if (dev->dev->bus_type == B43_BUS_SSB &&
+	    dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI &&
+	    !(dev->dev->sdev->bus->host_pci->is_pcie &&
+	      ssb_read32(dev->dev->sdev, SSB_TMSHIGH) & SSB_TMSHIGH_DMA64))
+			return 1;
+#endif
+	return 0;
+}
+
 int b43_dma_init(struct b43_wldev *dev)
 {
 	struct b43_dma *dma = &dev->dma;
@@ -1091,6 +1141,7 @@
 		break;
 #endif
 	}
+	dma->translation_in_low = b43_dma_translation_in_low_word(dev, type);
 
 	dma->parity = true;
 #ifdef CONFIG_B43_BCMA
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/dma.h linux-3.1.1/drivers/net/wireless/b43/dma.h
--- linux-3.1.1.orig/drivers/net/wireless/b43/dma.h	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/dma.h	2011-11-13 17:11:58.599558349 -0800
@@ -161,13 +161,17 @@
 } __packed;
 
 /* Misc DMA constants */
-#define B43_DMA_RINGMEMSIZE		PAGE_SIZE
-#define B43_DMA0_RX_FRAMEOFFSET		30
+#define B43_DMA32_RINGMEMSIZE		4096
+#define B43_DMA64_RINGMEMSIZE		8192
+/* Offset of frame with actual data */
+#define B43_DMA0_RX_FW598_FO		38
+#define B43_DMA0_RX_FW351_FO		30
 
 /* DMA engine tuning knobs */
 #define B43_TXRING_SLOTS		256
 #define B43_RXRING_SLOTS		64
-#define B43_DMA0_RX_BUFFERSIZE		(B43_DMA0_RX_FRAMEOFFSET + IEEE80211_MAX_FRAME_LEN)
+#define B43_DMA0_RX_FW598_BUFSIZE	(B43_DMA0_RX_FW598_FO + IEEE80211_MAX_FRAME_LEN)
+#define B43_DMA0_RX_FW351_BUFSIZE	(B43_DMA0_RX_FW351_FO + IEEE80211_MAX_FRAME_LEN)
 
 /* Pointer poison */
 #define B43_DMA_PTR_POISON		((void *)ERR_PTR(-ENOMEM))
@@ -212,6 +216,12 @@
 	B43_DMA_64BIT	= 64,
 };
 
+enum b43_addrtype {
+	B43_DMA_ADDR_LOW,
+	B43_DMA_ADDR_HIGH,
+	B43_DMA_ADDR_EXT,
+};
+
 struct b43_dmaring {
 	/* Lowlevel DMA ops. */
 	const struct b43_dma_ops *ops;
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/main.c linux-3.1.1/drivers/net/wireless/b43/main.c
--- linux-3.1.1.orig/drivers/net/wireless/b43/main.c	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/main.c	2011-11-13 17:11:58.599558349 -0800
@@ -7,6 +7,7 @@
   Copyright (c) 2005-2009 Michael Buesch <m@bues.ch>
   Copyright (c) 2005 Danny van Dyk <kugelfang@gentoo.org>
   Copyright (c) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
+  Copyright (c) 2010-2011 Rafał Miłecki <zajec5@gmail.com>
 
   SDIO support
   Copyright (c) 2009 Albert Herranz <albert_herranz@yahoo.es>
@@ -37,7 +38,6 @@
 #include <linux/if_arp.h>
 #include <linux/etherdevice.h>
 #include <linux/firmware.h>
-#include <linux/wireless.h>
 #include <linux/workqueue.h>
 #include <linux/skbuff.h>
 #include <linux/io.h>
@@ -65,9 +65,9 @@
 MODULE_AUTHOR("Stefano Brivio");
 MODULE_AUTHOR("Michael Buesch");
 MODULE_AUTHOR("Gábor Stefanik");
+MODULE_AUTHOR("Rafał Miłecki");
 MODULE_LICENSE("GPL");
 
-MODULE_FIRMWARE(B43_SUPPORTED_FIRMWARE_ID);
 MODULE_FIRMWARE("b43/ucode11.fw");
 MODULE_FIRMWARE("b43/ucode13.fw");
 MODULE_FIRMWARE("b43/ucode14.fw");
@@ -109,12 +109,13 @@
 module_param_named(verbose, b43_modparam_verbose, int, 0644);
 MODULE_PARM_DESC(verbose, "Log message verbosity: 0=error, 1=warn, 2=info(default), 3=debug");
 
-static int b43_modparam_pio = B43_PIO_DEFAULT;
+static int b43_modparam_pio = 0;
 module_param_named(pio, b43_modparam_pio, int, 0644);
 MODULE_PARM_DESC(pio, "Use PIO accesses by default: 0=DMA, 1=PIO");
 
 #ifdef CONFIG_B43_BCMA
 static const struct bcma_device_id b43_bcma_tbl[] = {
+	BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x11, BCMA_ANY_CLASS),
 	BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x17, BCMA_ANY_CLASS),
 	BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x18, BCMA_ANY_CLASS),
 	BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1D, BCMA_ANY_CLASS),
@@ -320,6 +321,10 @@
 static int b43_wireless_core_init(struct b43_wldev *dev);
 static struct b43_wldev * b43_wireless_core_stop(struct b43_wldev *dev);
 static int b43_wireless_core_start(struct b43_wldev *dev);
+static void b43_op_bss_info_changed(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    struct ieee80211_bss_conf *conf,
+				    u32 changed);
 
 static int b43_ratelimit(struct b43_wl *wl)
 {
@@ -724,52 +729,59 @@
 	for (i = 0; i < 5; i++)
 		b43_ram_write(dev, i * 4, buffer[i]);
 
-	b43_write16(dev, 0x0568, 0x0000);
+	b43_write16(dev, B43_MMIO_XMTSEL, 0x0000);
+
 	if (dev->dev->core_rev < 11)
-		b43_write16(dev, 0x07C0, 0x0000);
+		b43_write16(dev, B43_MMIO_WEPCTL, 0x0000);
 	else
-		b43_write16(dev, 0x07C0, 0x0100);
+		b43_write16(dev, B43_MMIO_WEPCTL, 0x0100);
+
 	value = (ofdm ? 0x41 : 0x40);
-	b43_write16(dev, 0x050C, value);
-	if ((phy->type == B43_PHYTYPE_N) || (phy->type == B43_PHYTYPE_LP))
-		b43_write16(dev, 0x0514, 0x1A02);
-	b43_write16(dev, 0x0508, 0x0000);
-	b43_write16(dev, 0x050A, 0x0000);
-	b43_write16(dev, 0x054C, 0x0000);
-	b43_write16(dev, 0x056A, 0x0014);
-	b43_write16(dev, 0x0568, 0x0826);
-	b43_write16(dev, 0x0500, 0x0000);
-	if (!pa_on && (phy->type == B43_PHYTYPE_N)) {
-		//SPEC TODO
-	}
+	b43_write16(dev, B43_MMIO_TXE0_PHYCTL, value);
+	if (phy->type == B43_PHYTYPE_N || phy->type == B43_PHYTYPE_LP ||
+	    phy->type == B43_PHYTYPE_LCN)
+		b43_write16(dev, B43_MMIO_TXE0_PHYCTL1, 0x1A02);
+
+	b43_write16(dev, B43_MMIO_TXE0_WM_0, 0x0000);
+	b43_write16(dev, B43_MMIO_TXE0_WM_1, 0x0000);
+
+	b43_write16(dev, B43_MMIO_XMTTPLATETXPTR, 0x0000);
+	b43_write16(dev, B43_MMIO_XMTTXCNT, 0x0014);
+	b43_write16(dev, B43_MMIO_XMTSEL, 0x0826);
+	b43_write16(dev, B43_MMIO_TXE0_CTL, 0x0000);
+
+	if (!pa_on && phy->type == B43_PHYTYPE_N)
+		; /*b43_nphy_pa_override(dev, false) */
 
 	switch (phy->type) {
 	case B43_PHYTYPE_N:
-		b43_write16(dev, 0x0502, 0x00D0);
+	case B43_PHYTYPE_LCN:
+		b43_write16(dev, B43_MMIO_TXE0_AUX, 0x00D0);
 		break;
 	case B43_PHYTYPE_LP:
-		b43_write16(dev, 0x0502, 0x0050);
+		b43_write16(dev, B43_MMIO_TXE0_AUX, 0x0050);
 		break;
 	default:
-		b43_write16(dev, 0x0502, 0x0030);
+		b43_write16(dev, B43_MMIO_TXE0_AUX, 0x0030);
 	}
+	b43_read16(dev, B43_MMIO_TXE0_AUX);
 
 	if (phy->radio_ver == 0x2050 && phy->radio_rev <= 0x5)
 		b43_radio_write16(dev, 0x0051, 0x0017);
 	for (i = 0x00; i < max_loop; i++) {
-		value = b43_read16(dev, 0x050E);
+		value = b43_read16(dev, B43_MMIO_TXE0_STATUS);
 		if (value & 0x0080)
 			break;
 		udelay(10);
 	}
 	for (i = 0x00; i < 0x0A; i++) {
-		value = b43_read16(dev, 0x050E);
+		value = b43_read16(dev, B43_MMIO_TXE0_STATUS);
 		if (value & 0x0400)
 			break;
 		udelay(10);
 	}
 	for (i = 0x00; i < 0x19; i++) {
-		value = b43_read16(dev, 0x0690);
+		value = b43_read16(dev, B43_MMIO_IFSSTAT);
 		if (!(value & 0x0100))
 			break;
 		udelay(10);
@@ -2511,6 +2523,12 @@
 	}
 	dev->fw.rev = fwrev;
 	dev->fw.patch = fwpatch;
+	if (dev->fw.rev >= 598)
+		dev->fw.hdr_format = B43_FW_HDR_598;
+	else if (dev->fw.rev >= 410)
+		dev->fw.hdr_format = B43_FW_HDR_410;
+	else
+		dev->fw.hdr_format = B43_FW_HDR_351;
 	dev->fw.opensource = (fwdate == 0xFFFF);
 
 	/* Default to use-all-queues. */
@@ -2558,7 +2576,7 @@
 			dev->fw.rev, dev->fw.patch);
 	wiphy->hw_version = dev->dev->core_id;
 
-	if (b43_is_old_txhdr_format(dev)) {
+	if (dev->fw.hdr_format == B43_FW_HDR_351) {
 		/* We're over the deadline, but we keep support for old fw
 		 * until it turns out to be in major conflict with something new. */
 		b43warn(dev->wl, "You are using an old firmware image. "
@@ -2944,6 +2962,8 @@
 	case B43_PHYTYPE_G:
 	case B43_PHYTYPE_N:
 	case B43_PHYTYPE_LP:
+	case B43_PHYTYPE_HT:
+	case B43_PHYTYPE_LCN:
 		b43_rate_memory_write(dev, B43_OFDM_RATE_6MB, 1);
 		b43_rate_memory_write(dev, B43_OFDM_RATE_12MB, 1);
 		b43_rate_memory_write(dev, B43_OFDM_RATE_18MB, 1);
@@ -3539,7 +3559,8 @@
 	b43dbg(dev->wl, "QoS enabled\n");
 }
 
-static int b43_op_conf_tx(struct ieee80211_hw *hw, u16 _queue,
+static int b43_op_conf_tx(struct ieee80211_hw *hw,
+			  struct ieee80211_vif *vif, u16 _queue,
 			  const struct ieee80211_tx_queue_params *params)
 {
 	struct b43_wl *wl = hw_to_b43_wl(hw);
@@ -3586,7 +3607,7 @@
 	return 0;
 }
 
-static u64 b43_op_get_tsf(struct ieee80211_hw *hw)
+static u64 b43_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
 {
 	struct b43_wl *wl = hw_to_b43_wl(hw);
 	struct b43_wldev *dev;
@@ -3605,7 +3626,8 @@
 	return tsf;
 }
 
-static void b43_op_set_tsf(struct ieee80211_hw *hw, u64 tsf)
+static void b43_op_set_tsf(struct ieee80211_hw *hw,
+			   struct ieee80211_vif *vif, u64 tsf)
 {
 	struct b43_wl *wl = hw_to_b43_wl(hw);
 	struct b43_wldev *dev;
@@ -3779,14 +3801,24 @@
 	struct ieee80211_conf *conf = &hw->conf;
 	int antenna;
 	int err = 0;
+	bool reload_bss = false;
 
 	mutex_lock(&wl->mutex);
 
+	dev = wl->current_dev;
+
 	/* Switch the band (if necessary). This might change the active core. */
 	err = b43_switch_band(wl, conf->channel);
 	if (err)
 		goto out_unlock_mutex;
-	dev = wl->current_dev;
+
+	/* Need to reload all settings if the core changed */
+	if (dev != wl->current_dev) {
+		dev = wl->current_dev;
+		changed = ~0;
+		reload_bss = true;
+	}
+
 	phy = &dev->phy;
 
 	if (conf_is_ht(conf))
@@ -3847,6 +3879,9 @@
 out_unlock_mutex:
 	mutex_unlock(&wl->mutex);
 
+	if (wl->vif && reload_bss)
+		b43_op_bss_info_changed(hw, wl->vif, &wl->vif->bss_conf, ~0);
+
 	return err;
 }
 
@@ -3935,7 +3970,8 @@
 	if (changed & BSS_CHANGED_BEACON_INT &&
 	    (b43_is_mode(wl, NL80211_IFTYPE_AP) ||
 	     b43_is_mode(wl, NL80211_IFTYPE_MESH_POINT) ||
-	     b43_is_mode(wl, NL80211_IFTYPE_ADHOC)))
+	     b43_is_mode(wl, NL80211_IFTYPE_ADHOC)) &&
+	    conf->beacon_int)
 		b43_set_beacon_int(dev, conf->beacon_int);
 
 	if (changed & BSS_CHANGED_BASIC_RATES)
@@ -4108,10 +4144,13 @@
  * because the core might be gone away while we unlocked the mutex. */
 static struct b43_wldev * b43_wireless_core_stop(struct b43_wldev *dev)
 {
-	struct b43_wl *wl = dev->wl;
+	struct b43_wl *wl;
 	struct b43_wldev *orig_dev;
 	u32 mask;
 
+	if (!dev)
+		return NULL;
+	wl = dev->wl;
 redo:
 	if (!dev || b43_status(dev) < B43_STAT_STARTED)
 		return dev;
@@ -4630,8 +4669,13 @@
 	b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MAXCONT, 0x3FF);
 
 	if (b43_bus_host_is_pcmcia(dev->dev) ||
-	    b43_bus_host_is_sdio(dev->dev) ||
-	    dev->use_pio) {
+	    b43_bus_host_is_sdio(dev->dev)) {
+		dev->__using_pio_transfers = 1;
+		err = b43_pio_init(dev);
+	} else if (dev->use_pio) {
+		b43warn(dev->wl, "Forced PIO by use_pio module parameter. "
+			"This should not be needed and will result in lower "
+			"performance.\n");
 		dev->__using_pio_transfers = 1;
 		err = b43_pio_init(dev);
 	} else {
@@ -4703,6 +4747,9 @@
  out_mutex_unlock:
 	mutex_unlock(&wl->mutex);
 
+	if (err == 0)
+		b43_op_bss_info_changed(hw, vif, &vif->bss_conf, ~0);
+
 	return err;
 }
 
@@ -4773,6 +4820,9 @@
  out_mutex_unlock:
 	mutex_unlock(&wl->mutex);
 
+	/* reload configuration */
+	b43_op_config(hw, ~0);
+
 	return err;
 }
 
@@ -4929,10 +4979,18 @@
 	if (err)
 		wl->current_dev = NULL; /* Failed to init the dev. */
 	mutex_unlock(&wl->mutex);
-	if (err)
+
+	if (err) {
 		b43err(wl, "Controller restart FAILED\n");
-	else
-		b43info(wl, "Controller restarted\n");
+		return;
+	}
+
+	/* reload configuration */
+	b43_op_config(wl->hw, ~0);
+	if (wl->vif)
+		b43_op_bss_info_changed(wl->hw, wl->vif, &wl->vif->bss_conf, ~0);
+
+	b43info(wl, "Controller restarted\n");
 }
 
 static int b43_setup_bands(struct b43_wldev *dev,
@@ -5417,8 +5475,7 @@
 	feat_sdio = "S";
 #endif
 	printk(KERN_INFO "Broadcom 43xx driver loaded "
-	       "[ Features: %s%s%s%s%s, Firmware-ID: "
-	       B43_SUPPORTED_FIRMWARE_ID " ]\n",
+	       "[ Features: %s%s%s%s%s ]\n",
 	       feat_pci, feat_pcmcia, feat_nphy,
 	       feat_leds, feat_sdio);
 }
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/phy_common.c linux-3.1.1/drivers/net/wireless/b43/phy_common.c
--- linux-3.1.1.orig/drivers/net/wireless/b43/phy_common.c	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/phy_common.c	2011-11-13 17:11:58.599558349 -0800
@@ -448,6 +448,38 @@
 		channel_type == NL80211_CHAN_HT40PLUS);
 }
 
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/BmacPhyClkFgc */
+void b43_phy_force_clock(struct b43_wldev *dev, bool force)
+{
+	u32 tmp;
+
+	WARN_ON(dev->phy.type != B43_PHYTYPE_N &&
+		dev->phy.type != B43_PHYTYPE_HT);
+
+	switch (dev->dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+	case B43_BUS_BCMA:
+		tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
+		if (force)
+			tmp |= BCMA_IOCTL_FGC;
+		else
+			tmp &= ~BCMA_IOCTL_FGC;
+		bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp);
+		break;
+#endif
+#ifdef CONFIG_B43_SSB
+	case B43_BUS_SSB:
+		tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW);
+		if (force)
+			tmp |= SSB_TMSLOW_FGC;
+		else
+			tmp &= ~SSB_TMSLOW_FGC;
+		ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp);
+		break;
+#endif
+	}
+}
+
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/Cordic */
 struct b43_c32 b43_cordic(int theta)
 {
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/phy_common.h linux-3.1.1/drivers/net/wireless/b43/phy_common.h
--- linux-3.1.1.orig/drivers/net/wireless/b43/phy_common.h	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/phy_common.h	2011-11-13 17:11:58.599558349 -0800
@@ -444,6 +444,8 @@
 
 bool b43_channel_type_is_40mhz(enum nl80211_channel_type channel_type);
 
+void b43_phy_force_clock(struct b43_wldev *dev, bool force);
+
 struct b43_c32 b43_cordic(int theta);
 
 #endif /* LINUX_B43_PHY_COMMON_H_ */
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/phy_ht.c linux-3.1.1/drivers/net/wireless/b43/phy_ht.c
--- linux-3.1.1.orig/drivers/net/wireless/b43/phy_ht.c	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/phy_ht.c	2011-11-13 17:11:58.599558349 -0800
@@ -3,6 +3,8 @@
   Broadcom B43 wireless driver
   IEEE 802.11n HT-PHY support
 
+  Copyright (c) 2011 Rafał Miłecki <zajec5@gmail.com>
+
   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
@@ -152,6 +154,92 @@
 }
 
 /**************************************************
+ * Various PHY ops
+ **************************************************/
+
+static void b43_phy_ht_zero_extg(struct b43_wldev *dev)
+{
+	u8 i, j;
+	u16 base[] = { 0x40, 0x60, 0x80 };
+
+	for (i = 0; i < ARRAY_SIZE(base); i++) {
+		for (j = 0; j < 4; j++)
+			b43_phy_write(dev, B43_PHY_EXTG(base[i] + j), 0);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(base); i++)
+		b43_phy_write(dev, B43_PHY_EXTG(base[i] + 0xc), 0);
+}
+
+/* Some unknown AFE (Analog Frondned) op */
+static void b43_phy_ht_afe_unk1(struct b43_wldev *dev)
+{
+	u8 i;
+
+	const u16 ctl_regs[3][2] = {
+		{ B43_PHY_HT_AFE_CTL1, B43_PHY_HT_AFE_CTL2 },
+		{ B43_PHY_HT_AFE_CTL3, B43_PHY_HT_AFE_CTL4 },
+		{ B43_PHY_HT_AFE_CTL5, B43_PHY_HT_AFE_CTL6},
+	};
+
+	for (i = 0; i < 3; i++) {
+		/* TODO: verify masks&sets */
+		b43_phy_set(dev, ctl_regs[i][1], 0x4);
+		b43_phy_set(dev, ctl_regs[i][0], 0x4);
+		b43_phy_mask(dev, ctl_regs[i][1], ~0x1);
+		b43_phy_set(dev, ctl_regs[i][0], 0x1);
+		b43_httab_write(dev, B43_HTTAB16(8, 5 + (i * 0x10)), 0);
+		b43_phy_mask(dev, ctl_regs[i][0], ~0x4);
+	}
+}
+
+static void b43_phy_ht_force_rf_sequence(struct b43_wldev *dev, u16 rf_seq)
+{
+	u8 i;
+
+	u16 save_seq_mode = b43_phy_read(dev, B43_PHY_HT_RF_SEQ_MODE);
+	b43_phy_set(dev, B43_PHY_HT_RF_SEQ_MODE, 0x3);
+
+	b43_phy_set(dev, B43_PHY_HT_RF_SEQ_TRIG, rf_seq);
+	for (i = 0; i < 200; i++) {
+		if (!(b43_phy_read(dev, B43_PHY_HT_RF_SEQ_STATUS) & rf_seq)) {
+			i = 0;
+			break;
+		}
+		msleep(1);
+	}
+	if (i)
+		b43err(dev->wl, "Forcing RF sequence timeout\n");
+
+	b43_phy_write(dev, B43_PHY_HT_RF_SEQ_MODE, save_seq_mode);
+}
+
+static void b43_phy_ht_read_clip_detection(struct b43_wldev *dev, u16 *clip_st)
+{
+	clip_st[0] = b43_phy_read(dev, B43_PHY_HT_C1_CLIP1THRES);
+	clip_st[1] = b43_phy_read(dev, B43_PHY_HT_C2_CLIP1THRES);
+	clip_st[2] = b43_phy_read(dev, B43_PHY_HT_C3_CLIP1THRES);
+}
+
+static void b43_phy_ht_bphy_init(struct b43_wldev *dev)
+{
+	unsigned int i;
+	u16 val;
+
+	val = 0x1E1F;
+	for (i = 0; i < 16; i++) {
+		b43_phy_write(dev, B43_PHY_N_BMODE(0x88 + i), val);
+		val -= 0x202;
+	}
+	val = 0x3E3F;
+	for (i = 0; i < 16; i++) {
+		b43_phy_write(dev, B43_PHY_N_BMODE(0x98 + i), val);
+		val -= 0x202;
+	}
+	b43_phy_write(dev, B43_PHY_N_BMODE(0x38), 0x668);
+}
+
+/**************************************************
  * Channel switching ops.
  **************************************************/
 
@@ -255,8 +343,125 @@
 
 static int b43_phy_ht_op_init(struct b43_wldev *dev)
 {
+	u16 tmp;
+	u16 clip_state[3];
+
 	b43_phy_ht_tables_init(dev);
 
+	b43_phy_mask(dev, 0x0be, ~0x2);
+	b43_phy_set(dev, 0x23f, 0x7ff);
+	b43_phy_set(dev, 0x240, 0x7ff);
+	b43_phy_set(dev, 0x241, 0x7ff);
+
+	b43_phy_ht_zero_extg(dev);
+
+	b43_phy_mask(dev, B43_PHY_EXTG(0), ~0x3);
+
+	b43_phy_write(dev, B43_PHY_HT_AFE_CTL1, 0);
+	b43_phy_write(dev, B43_PHY_HT_AFE_CTL3, 0);
+	b43_phy_write(dev, B43_PHY_HT_AFE_CTL5, 0);
+
+	b43_phy_write(dev, B43_PHY_EXTG(0x103), 0x20);
+	b43_phy_write(dev, B43_PHY_EXTG(0x101), 0x20);
+	b43_phy_write(dev, 0x20d, 0xb8);
+	b43_phy_write(dev, B43_PHY_EXTG(0x14f), 0xc8);
+	b43_phy_write(dev, 0x70, 0x50);
+	b43_phy_write(dev, 0x1ff, 0x30);
+
+	if (0) /* TODO: condition */
+		; /* TODO: PHY op on reg 0x217 */
+
+	b43_phy_read(dev, 0xb0); /* TODO: what for? */
+	b43_phy_set(dev, 0xb0, 0x1);
+
+	b43_phy_set(dev, 0xb1, 0x91);
+	b43_phy_write(dev, 0x32f, 0x0003);
+	b43_phy_write(dev, 0x077, 0x0010);
+	b43_phy_write(dev, 0x0b4, 0x0258);
+	b43_phy_mask(dev, 0x17e, ~0x4000);
+
+	b43_phy_write(dev, 0x0b9, 0x0072);
+
+	b43_httab_write_few(dev, B43_HTTAB16(7, 0x14e), 2, 0x010f, 0x010f);
+	b43_httab_write_few(dev, B43_HTTAB16(7, 0x15e), 2, 0x010f, 0x010f);
+	b43_httab_write_few(dev, B43_HTTAB16(7, 0x16e), 2, 0x010f, 0x010f);
+
+	b43_phy_ht_afe_unk1(dev);
+
+	b43_httab_write_few(dev, B43_HTTAB16(7, 0x130), 9, 0x777, 0x111, 0x111,
+			    0x777, 0x111, 0x111, 0x777, 0x111, 0x111);
+
+	b43_httab_write(dev, B43_HTTAB16(7, 0x120), 0x0777);
+	b43_httab_write(dev, B43_HTTAB16(7, 0x124), 0x0777);
+
+	b43_httab_write(dev, B43_HTTAB16(8, 0x00), 0x02);
+	b43_httab_write(dev, B43_HTTAB16(8, 0x10), 0x02);
+	b43_httab_write(dev, B43_HTTAB16(8, 0x20), 0x02);
+
+	b43_httab_write_few(dev, B43_HTTAB16(8, 0x08), 4,
+			    0x8e, 0x96, 0x96, 0x96);
+	b43_httab_write_few(dev, B43_HTTAB16(8, 0x18), 4,
+			    0x8f, 0x9f, 0x9f, 0x9f);
+	b43_httab_write_few(dev, B43_HTTAB16(8, 0x28), 4,
+			    0x8f, 0x9f, 0x9f, 0x9f);
+
+	b43_httab_write_few(dev, B43_HTTAB16(8, 0x0c), 4, 0x2, 0x2, 0x2, 0x2);
+	b43_httab_write_few(dev, B43_HTTAB16(8, 0x1c), 4, 0x2, 0x2, 0x2, 0x2);
+	b43_httab_write_few(dev, B43_HTTAB16(8, 0x2c), 4, 0x2, 0x2, 0x2, 0x2);
+
+	b43_phy_maskset(dev, 0x0280, 0xff00, 0x3e);
+	b43_phy_maskset(dev, 0x0283, 0xff00, 0x3e);
+	b43_phy_maskset(dev, B43_PHY_OFDM(0x0141), 0xff00, 0x46);
+	b43_phy_maskset(dev, 0x0283, 0xff00, 0x40);
+
+	b43_httab_write_few(dev, B43_HTTAB16(00, 0x8), 4,
+			    0x09, 0x0e, 0x13, 0x18);
+	b43_httab_write_few(dev, B43_HTTAB16(01, 0x8), 4,
+			    0x09, 0x0e, 0x13, 0x18);
+	/* TODO: Did wl mean 2 instead of 40? */
+	b43_httab_write_few(dev, B43_HTTAB16(40, 0x8), 4,
+			    0x09, 0x0e, 0x13, 0x18);
+
+	b43_phy_maskset(dev, B43_PHY_OFDM(0x24), 0x3f, 0xd);
+	b43_phy_maskset(dev, B43_PHY_OFDM(0x64), 0x3f, 0xd);
+	b43_phy_maskset(dev, B43_PHY_OFDM(0xa4), 0x3f, 0xd);
+
+	b43_phy_set(dev, B43_PHY_EXTG(0x060), 0x1);
+	b43_phy_set(dev, B43_PHY_EXTG(0x064), 0x1);
+	b43_phy_set(dev, B43_PHY_EXTG(0x080), 0x1);
+	b43_phy_set(dev, B43_PHY_EXTG(0x084), 0x1);
+
+	/* Copy some tables entries */
+	tmp = b43_httab_read(dev, B43_HTTAB16(7, 0x144));
+	b43_httab_write(dev, B43_HTTAB16(7, 0x14a), tmp);
+	tmp = b43_httab_read(dev, B43_HTTAB16(7, 0x154));
+	b43_httab_write(dev, B43_HTTAB16(7, 0x15a), tmp);
+	tmp = b43_httab_read(dev, B43_HTTAB16(7, 0x164));
+	b43_httab_write(dev, B43_HTTAB16(7, 0x16a), tmp);
+
+	/* Reset CCA */
+	b43_phy_force_clock(dev, true);
+	tmp = b43_phy_read(dev, B43_PHY_HT_BBCFG);
+	b43_phy_write(dev, B43_PHY_HT_BBCFG, tmp | B43_PHY_HT_BBCFG_RSTCCA);
+	b43_phy_write(dev, B43_PHY_HT_BBCFG, tmp & ~B43_PHY_HT_BBCFG_RSTCCA);
+	b43_phy_force_clock(dev, false);
+
+	b43_mac_phy_clock_set(dev, true);
+
+	b43_phy_ht_force_rf_sequence(dev, B43_PHY_HT_RF_SEQ_TRIG_RX2TX);
+	b43_phy_ht_force_rf_sequence(dev, B43_PHY_HT_RF_SEQ_TRIG_RST2RX);
+
+	/* TODO: PHY op on reg 0xb0 */
+
+	/* TODO: Should we restore it? Or store it in global PHY info? */
+	b43_phy_ht_read_clip_detection(dev, clip_state);
+
+	if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
+		b43_phy_ht_bphy_init(dev);
+
+	b43_httab_write_bulk(dev, B43_HTTAB32(0x1a, 0xc0),
+			B43_HTTAB_1A_C0_LATE_SIZE, b43_httab_0x1a_0xc0_late);
+
 	return 0;
 }
 
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/phy_ht.h linux-3.1.1/drivers/net/wireless/b43/phy_ht.h
--- linux-3.1.1.orig/drivers/net/wireless/b43/phy_ht.h	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/phy_ht.h	2011-11-13 17:11:58.599558349 -0800
@@ -4,7 +4,11 @@
 #include "phy_common.h"
 
 
+#define B43_PHY_HT_BBCFG			0x001 /* BB config */
+#define  B43_PHY_HT_BBCFG_RSTCCA		0x4000 /* Reset CCA */
+#define  B43_PHY_HT_BBCFG_RSTRX			0x8000 /* Reset RX */
 #define B43_PHY_HT_BANDCTL			0x009 /* Band control */
+#define  B43_PHY_HT_BANDCTL_5GHZ		0x0001 /* Use the 5GHz band */
 #define B43_PHY_HT_TABLE_ADDR			0x072 /* Table address */
 #define B43_PHY_HT_TABLE_DATALO			0x073 /* Table data low */
 #define B43_PHY_HT_TABLE_DATAHI			0x074 /* Table data high */
@@ -15,6 +19,21 @@
 #define B43_PHY_HT_BW5				0x1D2
 #define B43_PHY_HT_BW6				0x1D3
 
+#define B43_PHY_HT_C1_CLIP1THRES		B43_PHY_OFDM(0x00E)
+#define B43_PHY_HT_C2_CLIP1THRES		B43_PHY_OFDM(0x04E)
+#define B43_PHY_HT_C3_CLIP1THRES		B43_PHY_OFDM(0x08E)
+
+#define B43_PHY_HT_RF_SEQ_MODE			B43_PHY_EXTG(0x000)
+#define B43_PHY_HT_RF_SEQ_TRIG			B43_PHY_EXTG(0x003)
+#define  B43_PHY_HT_RF_SEQ_TRIG_RX2TX		0x0001 /* RX2TX */
+#define  B43_PHY_HT_RF_SEQ_TRIG_TX2RX		0x0002 /* TX2RX */
+#define  B43_PHY_HT_RF_SEQ_TRIG_UPGH		0x0004 /* Update gain H */
+#define  B43_PHY_HT_RF_SEQ_TRIG_UPGL		0x0008 /* Update gain L */
+#define  B43_PHY_HT_RF_SEQ_TRIG_UPGU		0x0010 /* Update gain U */
+#define  B43_PHY_HT_RF_SEQ_TRIG_RST2RX		0x0020 /* Reset to RX */
+#define B43_PHY_HT_RF_SEQ_STATUS		B43_PHY_EXTG(0x004)
+/* Values for the status are the same as for the trigger */
+
 #define B43_PHY_HT_RF_CTL1			B43_PHY_EXTG(0x010)
 
 #define B43_PHY_HT_AFE_CTL1			B43_PHY_EXTG(0x110)
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/phy_lcn.c linux-3.1.1/drivers/net/wireless/b43/phy_lcn.c
--- linux-3.1.1.orig/drivers/net/wireless/b43/phy_lcn.c	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/phy_lcn.c	2011-11-13 17:11:58.599558349 -0800
@@ -3,6 +3,8 @@
   Broadcom B43 wireless driver
   IEEE 802.11n LCN-PHY support
 
+  Copyright (c) 2011 Rafał Miłecki <zajec5@gmail.com>
+
   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
@@ -18,6 +20,14 @@
   the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
   Boston, MA 02110-1301, USA.
 
+  This file incorporates work covered by the following copyright and
+  permission notice:
+
+      Copyright (c) 2010 Broadcom Corporation
+
+      Permission to use, copy, modify, and/or distribute this software for any
+      purpose with or without fee is hereby granted, provided that the above
+      copyright notice and this permission notice appear in all copies.
 */
 
 #include <linux/slab.h>
@@ -27,12 +37,852 @@
 #include "tables_phy_lcn.h"
 #include "main.h"
 
+struct lcn_tx_gains {
+	u16 gm_gain;
+	u16 pga_gain;
+	u16 pad_gain;
+	u16 dac_gain;
+};
+
+struct lcn_tx_iir_filter {
+	u8 type;
+	u16 values[16];
+};
+
+enum lcn_sense_type {
+	B43_SENSE_TEMP,
+	B43_SENSE_VBAT,
+};
+
+/* In theory it's PHY common function, move if needed */
+/* brcms_b_switch_macfreq */
+static void b43_phy_switch_macfreq(struct b43_wldev *dev, u8 spurmode)
+{
+	if (dev->dev->chip_id == 43224 || dev->dev->chip_id == 43225) {
+		switch (spurmode) {
+		case 2:		/* 126 Mhz */
+			b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x2082);
+			b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8);
+			break;
+		case 1:		/* 123 Mhz */
+			b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x5341);
+			b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8);
+			break;
+		default:	/* 120 Mhz */
+			b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x8889);
+			b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8);
+			break;
+		}
+	} else if (dev->phy.type == B43_PHYTYPE_LCN) {
+		switch (spurmode) {
+		case 1:		/* 82 Mhz */
+			b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x7CE0);
+			b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC);
+			break;
+		default:	/* 80 Mhz */
+			b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0xCCCD);
+			b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC);
+			break;
+		}
+	}
+}
+
+/**************************************************
+ * Radio 2064.
+ **************************************************/
+
+/* wlc_lcnphy_radio_2064_channel_tune_4313 */
+static void b43_radio_2064_channel_setup(struct b43_wldev *dev)
+{
+	u16 save[2];
+
+	b43_radio_set(dev, 0x09d, 0x4);
+	b43_radio_write(dev, 0x09e, 0xf);
+
+	/* Channel specific values in theory, in practice always the same */
+	b43_radio_write(dev, 0x02a, 0xb);
+	b43_radio_maskset(dev, 0x030, ~0x3, 0xa);
+	b43_radio_maskset(dev, 0x091, ~0x3, 0);
+	b43_radio_maskset(dev, 0x038, ~0xf, 0x7);
+	b43_radio_maskset(dev, 0x030, ~0xc, 0x8);
+	b43_radio_maskset(dev, 0x05e, ~0xf, 0x8);
+	b43_radio_maskset(dev, 0x05e, ~0xf0, 0x80);
+	b43_radio_write(dev, 0x06c, 0x80);
+
+	save[0] = b43_radio_read(dev, 0x044);
+	save[1] = b43_radio_read(dev, 0x12b);
+
+	b43_radio_set(dev, 0x044, 0x7);
+	b43_radio_set(dev, 0x12b, 0xe);
+
+	/* TODO */
+
+	b43_radio_write(dev, 0x040, 0xfb);
+
+	b43_radio_write(dev, 0x041, 0x9a);
+	b43_radio_write(dev, 0x042, 0xa3);
+	b43_radio_write(dev, 0x043, 0x0c);
+
+	/* TODO */
+
+	b43_radio_set(dev, 0x044, 0x0c);
+	udelay(1);
+
+	b43_radio_write(dev, 0x044, save[0]);
+	b43_radio_write(dev, 0x12b, save[1]);
+
+	if (dev->phy.rev == 1) {
+		/* brcmsmac uses outdated 0x3 for 0x038 */
+		b43_radio_write(dev, 0x038, 0x0);
+		b43_radio_write(dev, 0x091, 0x7);
+	}
+}
+
+/* wlc_radio_2064_init */
+static void b43_radio_2064_init(struct b43_wldev *dev)
+{
+	if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+		b43_radio_write(dev, 0x09c, 0x0020);
+		b43_radio_write(dev, 0x105, 0x0008);
+	} else {
+		/* TODO */
+	}
+	b43_radio_write(dev, 0x032, 0x0062);
+	b43_radio_write(dev, 0x033, 0x0019);
+	b43_radio_write(dev, 0x090, 0x0010);
+	b43_radio_write(dev, 0x010, 0x0000);
+	if (dev->phy.rev == 1) {
+		b43_radio_write(dev, 0x060, 0x007f);
+		b43_radio_write(dev, 0x061, 0x0072);
+		b43_radio_write(dev, 0x062, 0x007f);
+	}
+	b43_radio_write(dev, 0x01d, 0x0002);
+	b43_radio_write(dev, 0x01e, 0x0006);
+
+	b43_phy_write(dev, 0x4ea, 0x4688);
+	b43_phy_maskset(dev, 0x4eb, ~0x7, 0x2);
+	b43_phy_mask(dev, 0x4eb, ~0x01c0);
+	b43_phy_maskset(dev, 0x46a, 0xff00, 0x19);
+
+	b43_lcntab_write(dev, B43_LCNTAB16(0x00, 0x55), 0);
+
+	b43_radio_mask(dev, 0x05b, (u16) ~0xff02);
+	b43_radio_set(dev, 0x004, 0x40);
+	b43_radio_set(dev, 0x120, 0x10);
+	b43_radio_set(dev, 0x078, 0x80);
+	b43_radio_set(dev, 0x129, 0x2);
+	b43_radio_set(dev, 0x057, 0x1);
+	b43_radio_set(dev, 0x05b, 0x2);
+
+	/* TODO: wait for some bit to be set */
+	b43_radio_read(dev, 0x05c);
+
+	b43_radio_mask(dev, 0x05b, (u16) ~0xff02);
+	b43_radio_mask(dev, 0x057, (u16) ~0xff01);
+
+	b43_phy_write(dev, 0x933, 0x2d6b);
+	b43_phy_write(dev, 0x934, 0x2d6b);
+	b43_phy_write(dev, 0x935, 0x2d6b);
+	b43_phy_write(dev, 0x936, 0x2d6b);
+	b43_phy_write(dev, 0x937, 0x016b);
+
+	b43_radio_mask(dev, 0x057, (u16) ~0xff02);
+	b43_radio_write(dev, 0x0c2, 0x006f);
+}
+
+/**************************************************
+ * Various PHY ops
+ **************************************************/
+
+/* wlc_lcnphy_toggle_afe_pwdn */
+static void b43_phy_lcn_afe_set_unset(struct b43_wldev *dev)
+{
+	u16 afe_ctl2 = b43_phy_read(dev, B43_PHY_LCN_AFE_CTL2);
+	u16 afe_ctl1 = b43_phy_read(dev, B43_PHY_LCN_AFE_CTL1);
+
+	b43_phy_write(dev, B43_PHY_LCN_AFE_CTL2, afe_ctl2 | 0x1);
+	b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1 | 0x1);
+
+	b43_phy_write(dev, B43_PHY_LCN_AFE_CTL2, afe_ctl2 & ~0x1);
+	b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1 & ~0x1);
+
+	b43_phy_write(dev, B43_PHY_LCN_AFE_CTL2, afe_ctl2);
+	b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1);
+}
+
+/* wlc_lcnphy_get_pa_gain */
+static u16 b43_phy_lcn_get_pa_gain(struct b43_wldev *dev)
+{
+	return (b43_phy_read(dev, 0x4fb) & 0x7f00) >> 8;
+}
+
+/* wlc_lcnphy_set_dac_gain */
+static void b43_phy_lcn_set_dac_gain(struct b43_wldev *dev, u16 dac_gain)
+{
+	u16 dac_ctrl;
+
+	dac_ctrl = b43_phy_read(dev, 0x439);
+	dac_ctrl = dac_ctrl & 0xc7f;
+	dac_ctrl = dac_ctrl | (dac_gain << 7);
+	b43_phy_maskset(dev, 0x439, ~0xfff, dac_ctrl);
+}
+
+/* wlc_lcnphy_set_bbmult */
+static void b43_phy_lcn_set_bbmult(struct b43_wldev *dev, u8 m0)
+{
+	b43_lcntab_write(dev, B43_LCNTAB16(0x00, 0x57), m0 << 8);
+}
+
+/* wlc_lcnphy_clear_tx_power_offsets */
+static void b43_phy_lcn_clear_tx_power_offsets(struct b43_wldev *dev)
+{
+	u8 i;
+
+	if (1) { /* FIXME */
+		b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, (0x7 << 10) | 0x340);
+		for (i = 0; i < 30; i++) {
+			b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI, 0);
+			b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, 0);
+		}
+	}
+
+	b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, (0x7 << 10) | 0x80);
+	for (i = 0; i < 64; i++) {
+		b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI, 0);
+		b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, 0);
+	}
+}
+
+/* wlc_lcnphy_rev0_baseband_init */
+static void b43_phy_lcn_rev0_baseband_init(struct b43_wldev *dev)
+{
+	b43_radio_write(dev, 0x11c, 0);
+
+	b43_phy_write(dev, 0x43b, 0);
+	b43_phy_write(dev, 0x43c, 0);
+	b43_phy_write(dev, 0x44c, 0);
+	b43_phy_write(dev, 0x4e6, 0);
+	b43_phy_write(dev, 0x4f9, 0);
+	b43_phy_write(dev, 0x4b0, 0);
+	b43_phy_write(dev, 0x938, 0);
+	b43_phy_write(dev, 0x4b0, 0);
+	b43_phy_write(dev, 0x44e, 0);
+
+	b43_phy_set(dev, 0x567, 0x03);
+
+	b43_phy_set(dev, 0x44a, 0x44);
+	b43_phy_write(dev, 0x44a, 0x80);
+
+	if (!(dev->dev->bus_sprom->boardflags_lo & B43_BFL_FEM))
+		; /* TODO */
+	b43_phy_maskset(dev, 0x634, ~0xff, 0xc);
+	if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_FEM) {
+		b43_phy_maskset(dev, 0x634, ~0xff, 0xa);
+		b43_phy_write(dev, 0x910, 0x1);
+	}
+
+	b43_phy_write(dev, 0x910, 0x1);
+
+	b43_phy_maskset(dev, 0x448, ~0x300, 0x100);
+	b43_phy_maskset(dev, 0x608, ~0xff, 0x17);
+	b43_phy_maskset(dev, 0x604, ~0x7ff, 0x3ea);
+}
+
+/* wlc_lcnphy_bu_tweaks */
+static void b43_phy_lcn_bu_tweaks(struct b43_wldev *dev)
+{
+	b43_phy_set(dev, 0x805, 0x1);
+
+	b43_phy_maskset(dev, 0x42f, ~0x7, 0x3);
+	b43_phy_maskset(dev, 0x030, ~0x7, 0x3);
+
+	b43_phy_write(dev, 0x414, 0x1e10);
+	b43_phy_write(dev, 0x415, 0x0640);
+
+	b43_phy_maskset(dev, 0x4df, (u16) ~0xff00, 0xf700);
+
+	b43_phy_set(dev, 0x44a, 0x44);
+	b43_phy_write(dev, 0x44a, 0x80);
+
+	b43_phy_maskset(dev, 0x434, ~0xff, 0xfd);
+	b43_phy_maskset(dev, 0x420, ~0xff, 0x10);
+
+	if (dev->dev->bus_sprom->board_rev >= 0x1204)
+		b43_radio_set(dev, 0x09b, 0xf0);
+
+	b43_phy_write(dev, 0x7d6, 0x0902);
+
+	b43_phy_maskset(dev, 0x429, ~0xf, 0x9);
+	b43_phy_maskset(dev, 0x429, ~(0x3f << 4), 0xe << 4);
+
+	if (dev->phy.rev == 1) {
+		b43_phy_maskset(dev, 0x423, ~0xff, 0x46);
+		b43_phy_maskset(dev, 0x411, ~0xff, 1);
+		b43_phy_set(dev, 0x434, 0xff); /* FIXME: update to wl */
+
+		/* TODO: wl operates on PHY 0x416, brcmsmac is outdated here */
+
+		b43_phy_maskset(dev, 0x656, ~0xf, 2);
+		b43_phy_set(dev, 0x44d, 4);
+
+		b43_radio_set(dev, 0x0f7, 0x4);
+		b43_radio_mask(dev, 0x0f1, ~0x3);
+		b43_radio_maskset(dev, 0x0f2, ~0xf8, 0x90);
+		b43_radio_maskset(dev, 0x0f3, ~0x3, 0x2);
+		b43_radio_maskset(dev, 0x0f3, ~0xf0, 0xa0);
+
+		b43_radio_set(dev, 0x11f, 0x2);
+
+		b43_phy_lcn_clear_tx_power_offsets(dev);
+
+		/* TODO: something more? */
+	}
+}
+
+/* wlc_lcnphy_vbat_temp_sense_setup */
+static void b43_phy_lcn_sense_setup(struct b43_wldev *dev,
+				    enum lcn_sense_type sense_type)
+{
+	u8 auxpga_vmidcourse, auxpga_vmidfine, auxpga_gain;
+	u16 auxpga_vmid;
+	u8 tx_pwr_idx;
+	u8 i;
+
+	u16 save_radio_regs[6][2] = {
+		{ 0x007, 0 }, { 0x0ff, 0 }, { 0x11f, 0 }, { 0x005, 0 },
+		{ 0x025, 0 }, { 0x112, 0 },
+	};
+	u16 save_phy_regs[14][2] = {
+		{ 0x503, 0 }, { 0x4a4, 0 }, { 0x4d0, 0 }, { 0x4d9, 0 },
+		{ 0x4da, 0 }, { 0x4a6, 0 }, { 0x938, 0 }, { 0x939, 0 },
+		{ 0x4d8, 0 }, { 0x4d0, 0 }, { 0x4d7, 0 }, { 0x4a5, 0 },
+		{ 0x40d, 0 }, { 0x4a2, 0 },
+	};
+	u16 save_radio_4a4;
+
+	msleep(1);
+
+	/* Save */
+	for (i = 0; i < 6; i++)
+		save_radio_regs[i][1] = b43_radio_read(dev,
+						       save_radio_regs[i][0]);
+	for (i = 0; i < 14; i++)
+		save_phy_regs[i][1] = b43_phy_read(dev, save_phy_regs[i][0]);
+	b43_mac_suspend(dev);
+	save_radio_4a4 = b43_radio_read(dev, 0x4a4);
+	/* wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); */
+	tx_pwr_idx = dev->phy.lcn->tx_pwr_curr_idx;
+
+	/* Setup */
+	/* TODO: wlc_lcnphy_set_tx_pwr_by_index(pi, 127); */
+	b43_radio_set(dev, 0x007, 0x1);
+	b43_radio_set(dev, 0x0ff, 0x10);
+	b43_radio_set(dev, 0x11f, 0x4);
+
+	b43_phy_mask(dev, 0x503, ~0x1);
+	b43_phy_mask(dev, 0x503, ~0x4);
+	b43_phy_mask(dev, 0x4a4, ~0x4000);
+	b43_phy_mask(dev, 0x4a4, (u16) ~0x8000);
+	b43_phy_mask(dev, 0x4d0, ~0x20);
+	b43_phy_set(dev, 0x4a5, 0xff);
+	b43_phy_maskset(dev, 0x4a5, ~0x7000, 0x5000);
+	b43_phy_mask(dev, 0x4a5, ~0x700);
+	b43_phy_maskset(dev, 0x40d, ~0xff, 64);
+	b43_phy_maskset(dev, 0x40d, ~0x700, 0x600);
+	b43_phy_maskset(dev, 0x4a2, ~0xff, 64);
+	b43_phy_maskset(dev, 0x4a2, ~0x700, 0x600);
+	b43_phy_maskset(dev, 0x4d9, ~0x70, 0x20);
+	b43_phy_maskset(dev, 0x4d9, ~0x700, 0x300);
+	b43_phy_maskset(dev, 0x4d9, ~0x7000, 0x1000);
+	b43_phy_mask(dev, 0x4da, ~0x1000);
+	b43_phy_set(dev, 0x4da, 0x2000);
+	b43_phy_set(dev, 0x4a6, 0x8000);
+
+	b43_radio_write(dev, 0x025, 0xc);
+	b43_radio_set(dev, 0x005, 0x8);
+	b43_phy_set(dev, 0x938, 0x4);
+	b43_phy_set(dev, 0x939, 0x4);
+	b43_phy_set(dev, 0x4a4, 0x1000);
+
+	/* FIXME: don't hardcode */
+	b43_lcntab_write(dev, B43_LCNTAB16(0x8, 0x6), 0x640);
+
+	switch (sense_type) {
+	case B43_SENSE_TEMP:
+		b43_phy_set(dev, 0x4d7, 0x8);
+		b43_phy_maskset(dev, 0x4d7, ~0x7000, 0x1000);
+		auxpga_vmidcourse = 8;
+		auxpga_vmidfine = 0x4;
+		auxpga_gain = 2;
+		b43_radio_set(dev, 0x082, 0x20);
+		break;
+	case B43_SENSE_VBAT:
+		b43_phy_set(dev, 0x4d7, 0x8);
+		b43_phy_maskset(dev, 0x4d7, ~0x7000, 0x3000);
+		auxpga_vmidcourse = 7;
+		auxpga_vmidfine = 0xa;
+		auxpga_gain = 2;
+		break;
+	}
+	auxpga_vmid = (0x200 | (auxpga_vmidcourse << 4) | auxpga_vmidfine);
+
+	b43_phy_set(dev, 0x4d8, 0x1);
+	b43_phy_maskset(dev, 0x4d8, ~(0x3ff << 2), auxpga_vmid << 2);
+	b43_phy_set(dev, 0x4d8, 0x2);
+	b43_phy_maskset(dev, 0x4d8, ~(0x7 << 12), auxpga_gain << 12);
+	b43_phy_set(dev, 0x4d0, 0x20);
+	b43_radio_write(dev, 0x112, 0x6);
+
+	b43_dummy_transmission(dev, true, false);
+	/* Wait if not done */
+	if (!(b43_phy_read(dev, 0x476) & 0x8000))
+		udelay(10);
+
+	/* Restore */
+	for (i = 0; i < 6; i++)
+		b43_radio_write(dev, save_radio_regs[i][0],
+				save_radio_regs[i][1]);
+	for (i = 0; i < 14; i++)
+		b43_phy_write(dev, save_phy_regs[i][0], save_phy_regs[i][1]);
+	/* TODO: wlc_lcnphy_set_tx_pwr_by_index(tx_pwr_idx) */
+	b43_radio_write(dev, 0x4a4, save_radio_4a4);
+
+	b43_mac_enable(dev);
+
+	msleep(1);
+}
+
+static bool b43_phy_lcn_load_tx_iir_cck_filter(struct b43_wldev *dev,
+					       u8 filter_type)
+{
+	int i, j;
+	u16 phy_regs[] = { 0x910, 0x91e, 0x91f, 0x924, 0x925, 0x926, 0x920,
+			   0x921, 0x927, 0x928, 0x929, 0x922, 0x923, 0x930,
+			   0x931, 0x932 };
+	/* Table is from brcmsmac, values for type 25 were outdated, probably
+	 * others need updating too */
+	struct lcn_tx_iir_filter tx_iir_filters_cck[] = {
+		{ 0,  { 1, 415, 1874, 64, 128, 64, 792, 1656, 64, 128, 64, 778,
+			1582, 64, 128, 64 } },
+		{ 1,  { 1, 402, 1847, 259, 59, 259, 671, 1794, 68, 54, 68, 608,
+			1863, 93, 167, 93 } },
+		{ 2,  { 1, 415, 1874, 64, 128, 64, 792, 1656, 192, 384, 192,
+			778, 1582, 64, 128, 64 } },
+		{ 3,  { 1, 302, 1841, 129, 258, 129, 658, 1720, 205, 410, 205,
+			754, 1760, 170, 340, 170 } },
+		{ 20, { 1, 360, 1884, 242, 1734, 242, 752, 1720, 205, 1845, 205,
+			767, 1760, 256, 185, 256 } },
+		{ 21, { 1, 360, 1884, 149, 1874, 149, 752, 1720, 205, 1883, 205,
+			767, 1760, 256, 273, 256 } },
+		{ 22, { 1, 360, 1884, 98, 1948, 98, 752, 1720, 205, 1924, 205,
+			767, 1760, 256, 352, 256 } },
+		{ 23, { 1, 350, 1884, 116, 1966, 116, 752, 1720, 205, 2008, 205,
+			767, 1760, 128, 233, 128 } },
+		{ 24, { 1, 325, 1884, 32, 40, 32, 756, 1720, 256, 471, 256, 766,
+			1760, 256, 1881, 256 } },
+		{ 25, { 1, 299, 1884, 51, 64, 51, 736, 1720, 256, 471, 256, 765,
+			1760, 262, 1878, 262 } },
+		/* brcmsmac version { 25, { 1, 299, 1884, 51, 64, 51, 736, 1720,
+		 * 256, 471, 256, 765, 1760, 256, 1881, 256 } }, */
+		{ 26, { 1, 277, 1943, 39, 117, 88, 637, 1838, 64, 192, 144, 614,
+			1864, 128, 384, 288 } },
+		{ 27, { 1, 245, 1943, 49, 147, 110, 626, 1838, 256, 768, 576,
+			613, 1864, 128, 384, 288 } },
+		{ 30, { 1, 302, 1841, 61, 122, 61, 658, 1720, 205, 410, 205,
+			754, 1760, 170, 340, 170 } },
+	};
+
+	for (i = 0; i < ARRAY_SIZE(tx_iir_filters_cck); i++) {
+		if (tx_iir_filters_cck[i].type == filter_type) {
+			for (j = 0; j < 16; j++)
+				b43_phy_write(dev, phy_regs[j],
+					      tx_iir_filters_cck[i].values[j]);
+			return true;
+		}
+	}
+
+	return false;
+}
+
+static bool b43_phy_lcn_load_tx_iir_ofdm_filter(struct b43_wldev *dev,
+						u8 filter_type)
+{
+	int i, j;
+	u16 phy_regs[] = { 0x90f, 0x900, 0x901, 0x906, 0x907, 0x908, 0x902,
+			   0x903, 0x909, 0x90a, 0x90b, 0x904, 0x905, 0x90c,
+			   0x90d, 0x90e };
+	struct lcn_tx_iir_filter tx_iir_filters_ofdm[] = {
+		{ 0, { 0, 0xa2, 0x0, 0x100, 0x100, 0x0, 0x0, 0x0, 0x100, 0x0,
+		       0x0, 0x278, 0xfea0, 0x80, 0x100, 0x80 } },
+		{ 1, { 0, 374, 0xFF79, 16, 32, 16, 799, 0xFE74, 50, 32, 50, 750,
+		       0xFE2B, 212, 0xFFCE, 212 } },
+		{ 2, { 0, 375, 0xFF16, 37, 76, 37, 799, 0xFE74, 32, 20, 32, 748,
+		       0xFEF2, 128, 0xFFE2, 128 } },
+	};
+
+	for (i = 0; i < ARRAY_SIZE(tx_iir_filters_ofdm); i++) {
+		if (tx_iir_filters_ofdm[i].type == filter_type) {
+			for (j = 0; j < 16; j++)
+				b43_phy_write(dev, phy_regs[j],
+					      tx_iir_filters_ofdm[i].values[j]);
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/* wlc_lcnphy_set_tx_gain_override */
+static void b43_phy_lcn_set_tx_gain_override(struct b43_wldev *dev, bool enable)
+{
+	b43_phy_maskset(dev, 0x4b0, ~(0x1 << 7), enable << 7);
+	b43_phy_maskset(dev, 0x4b0, ~(0x1 << 14), enable << 14);
+	b43_phy_maskset(dev, 0x43b, ~(0x1 << 6), enable << 6);
+}
+
+/* wlc_lcnphy_set_tx_gain */
+static void b43_phy_lcn_set_tx_gain(struct b43_wldev *dev,
+				    struct lcn_tx_gains *target_gains)
+{
+	u16 pa_gain = b43_phy_lcn_get_pa_gain(dev);
+
+	b43_phy_write(dev, 0x4b5,
+		      (target_gains->gm_gain | (target_gains->pga_gain << 8)));
+	b43_phy_maskset(dev, 0x4fb, ~0x7fff,
+			(target_gains->pad_gain | (pa_gain << 8)));
+	b43_phy_write(dev, 0x4fc,
+		      (target_gains->gm_gain | (target_gains->pga_gain << 8)));
+	b43_phy_maskset(dev, 0x4fd, ~0x7fff,
+			(target_gains->pad_gain | (pa_gain << 8)));
+
+	b43_phy_lcn_set_dac_gain(dev, target_gains->dac_gain);
+	b43_phy_lcn_set_tx_gain_override(dev, true);
+}
+
+/* wlc_lcnphy_tx_pwr_ctrl_init */
+static void b43_phy_lcn_tx_pwr_ctl_init(struct b43_wldev *dev)
+{
+	struct lcn_tx_gains tx_gains;
+	u8 bbmult;
+
+	b43_mac_suspend(dev);
+
+	if (!dev->phy.lcn->hw_pwr_ctl_capable) {
+		if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+			tx_gains.gm_gain = 4;
+			tx_gains.pga_gain = 12;
+			tx_gains.pad_gain = 12;
+			tx_gains.dac_gain = 0;
+			bbmult = 150;
+		} else {
+			tx_gains.gm_gain = 7;
+			tx_gains.pga_gain = 15;
+			tx_gains.pad_gain = 14;
+			tx_gains.dac_gain = 0;
+			bbmult = 150;
+		}
+		b43_phy_lcn_set_tx_gain(dev, &tx_gains);
+		b43_phy_lcn_set_bbmult(dev, bbmult);
+		b43_phy_lcn_sense_setup(dev, B43_SENSE_TEMP);
+	} else {
+		b43err(dev->wl, "TX power control not supported for this HW\n");
+	}
+
+	b43_mac_enable(dev);
+}
+
+/* wlc_lcnphy_txrx_spur_avoidance_mode */
+static void b43_phy_lcn_txrx_spur_avoidance_mode(struct b43_wldev *dev,
+						 bool enable)
+{
+	if (enable) {
+		b43_phy_write(dev, 0x942, 0x7);
+		b43_phy_write(dev, 0x93b, ((1 << 13) + 23));
+		b43_phy_write(dev, 0x93c, ((1 << 13) + 1989));
+
+		b43_phy_write(dev, 0x44a, 0x084);
+		b43_phy_write(dev, 0x44a, 0x080);
+		b43_phy_write(dev, 0x6d3, 0x2222);
+		b43_phy_write(dev, 0x6d3, 0x2220);
+	} else {
+		b43_phy_write(dev, 0x942, 0x0);
+		b43_phy_write(dev, 0x93b, ((0 << 13) + 23));
+		b43_phy_write(dev, 0x93c, ((0 << 13) + 1989));
+	}
+	b43_phy_switch_macfreq(dev, enable);
+}
+
+/**************************************************
+ * Channel switching ops.
+ **************************************************/
+
+/* wlc_lcnphy_set_chanspec_tweaks */
+static void b43_phy_lcn_set_channel_tweaks(struct b43_wldev *dev, int channel)
+{
+	struct bcma_drv_cc *cc = &dev->dev->bdev->bus->drv_cc;
+
+	b43_phy_maskset(dev, 0x448, ~0x300, (channel == 14) ? 0x200 : 0x100);
+
+	if (channel == 1 || channel == 2 || channel == 3 || channel == 4 ||
+	    channel == 9 || channel == 10 || channel == 11 || channel == 12) {
+		bcma_chipco_pll_write(cc, 0x2, 0x03000c04);
+		bcma_chipco_pll_maskset(cc, 0x3, 0x00ffffff, 0x0);
+		bcma_chipco_pll_write(cc, 0x4, 0x200005c0);
+
+		bcma_cc_set32(cc, BCMA_CC_PMU_CTL, 0x400);
+
+		b43_phy_write(dev, 0x942, 0);
+
+		b43_phy_lcn_txrx_spur_avoidance_mode(dev, false);
+		b43_phy_maskset(dev, 0x424, (u16) ~0xff00, 0x1b00);
+		b43_phy_write(dev, 0x425, 0x5907);
+	} else {
+		bcma_chipco_pll_write(cc, 0x2, 0x03140c04);
+		bcma_chipco_pll_maskset(cc, 0x3, 0x00ffffff, 0x333333);
+		bcma_chipco_pll_write(cc, 0x4, 0x202c2820);
+
+		bcma_cc_set32(cc, BCMA_CC_PMU_CTL, 0x400);
+
+		b43_phy_write(dev, 0x942, 0);
+
+		b43_phy_lcn_txrx_spur_avoidance_mode(dev, true);
+		b43_phy_maskset(dev, 0x424, (u16) ~0xff00, 0x1f00);
+		b43_phy_write(dev, 0x425, 0x590a);
+	}
+
+	b43_phy_set(dev, 0x44a, 0x44);
+	b43_phy_write(dev, 0x44a, 0x80);
+}
+
+/* wlc_phy_chanspec_set_lcnphy */
+static int b43_phy_lcn_set_channel(struct b43_wldev *dev,
+				   struct ieee80211_channel *channel,
+				   enum nl80211_channel_type channel_type)
+{
+	static const u16 sfo_cfg[14][2] = {
+		{965, 1087}, {967, 1085}, {969, 1082}, {971, 1080}, {973, 1078},
+		{975, 1076}, {977, 1073}, {979, 1071}, {981, 1069}, {983, 1067},
+		{985, 1065}, {987, 1063}, {989, 1060}, {994, 1055},
+	};
+
+	b43_phy_lcn_set_channel_tweaks(dev, channel->hw_value);
+
+	b43_phy_set(dev, 0x44a, 0x44);
+	b43_phy_write(dev, 0x44a, 0x80);
+
+	b43_radio_2064_channel_setup(dev);
+	mdelay(1);
+
+	b43_phy_lcn_afe_set_unset(dev);
+
+	b43_phy_write(dev, 0x657, sfo_cfg[channel->hw_value - 1][0]);
+	b43_phy_write(dev, 0x658, sfo_cfg[channel->hw_value - 1][1]);
+
+	if (channel->hw_value == 14) {
+		b43_phy_maskset(dev, 0x448, ~(0x3 << 8), (2) << 8);
+		b43_phy_lcn_load_tx_iir_cck_filter(dev, 3);
+	} else {
+		b43_phy_maskset(dev, 0x448, ~(0x3 << 8), (1) << 8);
+		/* brcmsmac uses filter_type 2, we follow wl with 25 */
+		b43_phy_lcn_load_tx_iir_cck_filter(dev, 25);
+	}
+	/* brcmsmac uses filter_type 2, we follow wl with 0 */
+	b43_phy_lcn_load_tx_iir_ofdm_filter(dev, 0);
+
+	b43_phy_maskset(dev, 0x4eb, ~(0x7 << 3), 0x1 << 3);
+
+	return 0;
+}
+
+/**************************************************
+ * Basic PHY ops.
+ **************************************************/
+
+static int b43_phy_lcn_op_allocate(struct b43_wldev *dev)
+{
+	struct b43_phy_lcn *phy_lcn;
+
+	phy_lcn = kzalloc(sizeof(*phy_lcn), GFP_KERNEL);
+	if (!phy_lcn)
+		return -ENOMEM;
+	dev->phy.lcn = phy_lcn;
+
+	return 0;
+}
+
+static void b43_phy_lcn_op_free(struct b43_wldev *dev)
+{
+	struct b43_phy *phy = &dev->phy;
+	struct b43_phy_lcn *phy_lcn = phy->lcn;
+
+	kfree(phy_lcn);
+	phy->lcn = NULL;
+}
+
+static void b43_phy_lcn_op_prepare_structs(struct b43_wldev *dev)
+{
+	struct b43_phy *phy = &dev->phy;
+	struct b43_phy_lcn *phy_lcn = phy->lcn;
+
+	memset(phy_lcn, 0, sizeof(*phy_lcn));
+}
+
+/* wlc_phy_init_lcnphy */
+static int b43_phy_lcn_op_init(struct b43_wldev *dev)
+{
+	struct bcma_drv_cc *cc = &dev->dev->bdev->bus->drv_cc;
+
+	b43_phy_set(dev, 0x44a, 0x80);
+	b43_phy_mask(dev, 0x44a, 0x7f);
+	b43_phy_set(dev, 0x6d1, 0x80);
+	b43_phy_write(dev, 0x6d0, 0x7);
+
+	b43_phy_lcn_afe_set_unset(dev);
+
+	b43_phy_write(dev, 0x60a, 0xa0);
+	b43_phy_write(dev, 0x46a, 0x19);
+	b43_phy_maskset(dev, 0x663, 0xFF00, 0x64);
+
+	b43_phy_lcn_tables_init(dev);
+
+	b43_phy_lcn_rev0_baseband_init(dev);
+	b43_phy_lcn_bu_tweaks(dev);
+
+	if (dev->phy.radio_ver == 0x2064)
+		b43_radio_2064_init(dev);
+	else
+		B43_WARN_ON(1);
+
+	if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
+		b43_phy_lcn_tx_pwr_ctl_init(dev);
+
+	b43_switch_channel(dev, dev->phy.channel);
+
+	bcma_chipco_regctl_maskset(cc, 0, 0xf, 0x9);
+	bcma_chipco_chipctl_maskset(cc, 0, 0, 0x03cddddd);
+
+	/* TODO */
+
+	b43_phy_set(dev, 0x448, 0x4000);
+	udelay(100);
+	b43_phy_mask(dev, 0x448, ~0x4000);
+
+	/* TODO */
+
+	return 0;
+}
+
+static void b43_phy_lcn_op_software_rfkill(struct b43_wldev *dev,
+					bool blocked)
+{
+	if (b43_read32(dev, B43_MMIO_MACCTL) & B43_MACCTL_ENABLED)
+		b43err(dev->wl, "MAC not suspended\n");
+
+	if (blocked) {
+		b43_phy_mask(dev, B43_PHY_LCN_RF_CTL2, ~0x7c00);
+		b43_phy_set(dev, B43_PHY_LCN_RF_CTL1, 0x1f00);
+
+		b43_phy_mask(dev, B43_PHY_LCN_RF_CTL5, ~0x7f00);
+		b43_phy_mask(dev, B43_PHY_LCN_RF_CTL4, ~0x2);
+		b43_phy_set(dev, B43_PHY_LCN_RF_CTL3, 0x808);
+
+		b43_phy_mask(dev, B43_PHY_LCN_RF_CTL7, ~0x8);
+		b43_phy_set(dev, B43_PHY_LCN_RF_CTL6, 0x8);
+	} else {
+		b43_phy_mask(dev, B43_PHY_LCN_RF_CTL1, ~0x1f00);
+		b43_phy_mask(dev, B43_PHY_LCN_RF_CTL3, ~0x808);
+		b43_phy_mask(dev, B43_PHY_LCN_RF_CTL6, ~0x8);
+	}
+}
+
+static void b43_phy_lcn_op_switch_analog(struct b43_wldev *dev, bool on)
+{
+	if (on) {
+		b43_phy_mask(dev, B43_PHY_LCN_AFE_CTL1, ~0x7);
+	} else {
+		b43_phy_set(dev, B43_PHY_LCN_AFE_CTL2, 0x7);
+		b43_phy_set(dev, B43_PHY_LCN_AFE_CTL1, 0x7);
+	}
+}
+
+static int b43_phy_lcn_op_switch_channel(struct b43_wldev *dev,
+					unsigned int new_channel)
+{
+	struct ieee80211_channel *channel = dev->wl->hw->conf.channel;
+	enum nl80211_channel_type channel_type = dev->wl->hw->conf.channel_type;
+
+	if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+		if ((new_channel < 1) || (new_channel > 14))
+			return -EINVAL;
+	} else {
+		return -EINVAL;
+	}
+
+	return b43_phy_lcn_set_channel(dev, channel, channel_type);
+}
+
+static unsigned int b43_phy_lcn_op_get_default_chan(struct b43_wldev *dev)
+{
+	if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
+		return 1;
+	return 36;
+}
+
+static enum b43_txpwr_result
+b43_phy_lcn_op_recalc_txpower(struct b43_wldev *dev, bool ignore_tssi)
+{
+	return B43_TXPWR_RES_DONE;
+}
+
+static void b43_phy_lcn_op_adjust_txpower(struct b43_wldev *dev)
+{
+}
+
+/**************************************************
+ * R/W ops.
+ **************************************************/
+
+static u16 b43_phy_lcn_op_read(struct b43_wldev *dev, u16 reg)
+{
+	b43_write16(dev, B43_MMIO_PHY_CONTROL, reg);
+	return b43_read16(dev, B43_MMIO_PHY_DATA);
+}
+
+static void b43_phy_lcn_op_write(struct b43_wldev *dev, u16 reg, u16 value)
+{
+	b43_write16(dev, B43_MMIO_PHY_CONTROL, reg);
+	b43_write16(dev, B43_MMIO_PHY_DATA, value);
+}
+
+static void b43_phy_lcn_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask,
+				   u16 set)
+{
+	b43_write16(dev, B43_MMIO_PHY_CONTROL, reg);
+	b43_write16(dev, B43_MMIO_PHY_DATA,
+		    (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set);
+}
+
+static u16 b43_phy_lcn_op_radio_read(struct b43_wldev *dev, u16 reg)
+{
+	/* LCN-PHY needs 0x200 for read access */
+	reg |= 0x200;
+
+	b43_write16(dev, B43_MMIO_RADIO24_CONTROL, reg);
+	return b43_read16(dev, B43_MMIO_RADIO24_DATA);
+}
+
+static void b43_phy_lcn_op_radio_write(struct b43_wldev *dev, u16 reg,
+				       u16 value)
+{
+	b43_write16(dev, B43_MMIO_RADIO24_CONTROL, reg);
+	b43_write16(dev, B43_MMIO_RADIO24_DATA, value);
+}
+
 /**************************************************
  * PHY ops struct.
  **************************************************/
 
 const struct b43_phy_operations b43_phyops_lcn = {
-	/*
 	.allocate		= b43_phy_lcn_op_allocate,
 	.free			= b43_phy_lcn_op_free,
 	.prepare_structs	= b43_phy_lcn_op_prepare_structs,
@@ -48,5 +898,4 @@
 	.get_default_chan	= b43_phy_lcn_op_get_default_chan,
 	.recalc_txpower		= b43_phy_lcn_op_recalc_txpower,
 	.adjust_txpower		= b43_phy_lcn_op_adjust_txpower,
-	*/
 };
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/phy_lcn.h linux-3.1.1/drivers/net/wireless/b43/phy_lcn.h
--- linux-3.1.1.orig/drivers/net/wireless/b43/phy_lcn.h	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/phy_lcn.h	2011-11-13 17:11:58.599558349 -0800
@@ -4,11 +4,28 @@
 #include "phy_common.h"
 
 
+#define B43_PHY_LCN_AFE_CTL1			B43_PHY_OFDM(0x03B)
+#define B43_PHY_LCN_AFE_CTL2			B43_PHY_OFDM(0x03C)
+#define B43_PHY_LCN_RF_CTL1			B43_PHY_OFDM(0x04C)
+#define B43_PHY_LCN_RF_CTL2			B43_PHY_OFDM(0x04D)
+#define B43_PHY_LCN_TABLE_ADDR			B43_PHY_OFDM(0x055) /* Table address */
+#define B43_PHY_LCN_TABLE_DATALO		B43_PHY_OFDM(0x056) /* Table data low */
+#define B43_PHY_LCN_TABLE_DATAHI		B43_PHY_OFDM(0x057) /* Table data high */
+#define B43_PHY_LCN_RF_CTL3			B43_PHY_OFDM(0x0B0)
+#define B43_PHY_LCN_RF_CTL4			B43_PHY_OFDM(0x0B1)
+#define B43_PHY_LCN_RF_CTL5			B43_PHY_OFDM(0x0B7)
+#define B43_PHY_LCN_RF_CTL6			B43_PHY_OFDM(0x0F9)
+#define B43_PHY_LCN_RF_CTL7			B43_PHY_OFDM(0x0FA)
+
+
 struct b43_phy_lcn {
+	bool hw_pwr_ctl;
+	bool hw_pwr_ctl_capable;
+	u8 tx_pwr_curr_idx;
 };
 
 
 struct b43_phy_operations;
 extern const struct b43_phy_operations b43_phyops_lcn;
 
-#endif /* B43_PHY_LCN_H_ */
\ No newline at end of file
+#endif /* B43_PHY_LCN_H_ */
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/phy_n.c linux-3.1.1/drivers/net/wireless/b43/phy_n.c
--- linux-3.1.1.orig/drivers/net/wireless/b43/phy_n.c	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/phy_n.c	2011-11-13 17:11:58.609558134 -0800
@@ -4,6 +4,7 @@
   IEEE 802.11n PHY support
 
   Copyright (c) 2008 Michael Buesch <m@bues.ch>
+  Copyright (c) 2010-2011 Rafał Miłecki <zajec5@gmail.com>
 
   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
@@ -77,6 +78,7 @@
 	B43_NPHY_RSSI_TBD,
 };
 
+/* TODO: reorder functions */
 static void b43_nphy_stay_in_carrier_search(struct b43_wldev *dev,
 						bool enable);
 static void b43_nphy_set_rf_sequence(struct b43_wldev *dev, u8 cmd,
@@ -87,6 +89,14 @@
 						u16 value, u8 core, bool off);
 static void b43_nphy_rf_control_intc_override(struct b43_wldev *dev, u8 field,
 						u16 value, u8 core);
+static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev);
+
+static inline bool b43_nphy_ipa(struct b43_wldev *dev)
+{
+	enum ieee80211_band band = b43_current_band(dev->wl);
+	return ((dev->phy.n->ipa2g_on && band == IEEE80211_BAND_2GHZ) ||
+		(dev->phy.n->ipa5g_on && band == IEEE80211_BAND_5GHZ));
+}
 
 void b43_nphy_set_rxantenna(struct b43_wldev *dev, int antenna)
 {//TODO
@@ -248,15 +258,25 @@
 {
 	struct b43_phy_n *nphy = dev->phy.n;
 	u8 i;
-	u16 tmp;
+	u16 bmask, val, tmp;
+	enum ieee80211_band band = b43_current_band(dev->wl);
 
 	if (nphy->hang_avoid)
 		b43_nphy_stay_in_carrier_search(dev, 1);
 
 	nphy->txpwrctrl = enable;
 	if (!enable) {
-		if (dev->phy.rev >= 3)
-			; /* TODO */
+		if (dev->phy.rev >= 3 &&
+		    (b43_phy_read(dev, B43_NPHY_TXPCTL_CMD) &
+		     (B43_NPHY_TXPCTL_CMD_COEFF |
+		      B43_NPHY_TXPCTL_CMD_HWPCTLEN |
+		      B43_NPHY_TXPCTL_CMD_PCTLEN))) {
+			/* We disable enabled TX pwr ctl, save it's state */
+			nphy->tx_pwr_idx[0] = b43_phy_read(dev,
+						B43_NPHY_C1_TXPCTL_STAT) & 0x7f;
+			nphy->tx_pwr_idx[1] = b43_phy_read(dev,
+						B43_NPHY_C2_TXPCTL_STAT) & 0x7f;
+		}
 
 		b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x6840);
 		for (i = 0; i < 84; i++)
@@ -285,10 +305,67 @@
 			b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3,
 				~B43_NPHY_BPHY_CTL3_SCALE, 0x5A);
 
-		if (dev->phy.rev < 2 && 0)
-			; /* TODO */
+		if (dev->phy.rev < 2 && dev->phy.is_40mhz)
+			b43_hf_write(dev, b43_hf_read(dev) | B43_HF_TSSIRPSMW);
 	} else {
-		b43err(dev->wl, "enabling tx pwr ctrl not implemented yet\n");
+		b43_ntab_write_bulk(dev, B43_NTAB16(26, 64), 84,
+				    nphy->adj_pwr_tbl);
+		b43_ntab_write_bulk(dev, B43_NTAB16(27, 64), 84,
+				    nphy->adj_pwr_tbl);
+
+		bmask = B43_NPHY_TXPCTL_CMD_COEFF |
+			B43_NPHY_TXPCTL_CMD_HWPCTLEN;
+		/* wl does useless check for "enable" param here */
+		val = B43_NPHY_TXPCTL_CMD_COEFF | B43_NPHY_TXPCTL_CMD_HWPCTLEN;
+		if (dev->phy.rev >= 3) {
+			bmask |= B43_NPHY_TXPCTL_CMD_PCTLEN;
+			if (val)
+				val |= B43_NPHY_TXPCTL_CMD_PCTLEN;
+		}
+		b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, ~(bmask), val);
+
+		if (band == IEEE80211_BAND_5GHZ) {
+			b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD,
+					~B43_NPHY_TXPCTL_CMD_INIT, 0x64);
+			if (dev->phy.rev > 1)
+				b43_phy_maskset(dev, B43_NPHY_TXPCTL_INIT,
+						~B43_NPHY_TXPCTL_INIT_PIDXI1,
+						0x64);
+		}
+
+		if (dev->phy.rev >= 3) {
+			if (nphy->tx_pwr_idx[0] != 128 &&
+			    nphy->tx_pwr_idx[1] != 128) {
+				/* Recover TX pwr ctl state */
+				b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD,
+						~B43_NPHY_TXPCTL_CMD_INIT,
+						nphy->tx_pwr_idx[0]);
+				if (dev->phy.rev > 1)
+					b43_phy_maskset(dev,
+						B43_NPHY_TXPCTL_INIT,
+						~0xff, nphy->tx_pwr_idx[1]);
+			}
+		}
+
+		if (dev->phy.rev >= 3) {
+			b43_phy_mask(dev, B43_NPHY_AFECTL_OVER1, ~0x100);
+			b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, ~0x100);
+		} else {
+			b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, ~0x4000);
+		}
+
+		if (dev->phy.rev == 2)
+			b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, ~0xFF, 0x3b);
+		else if (dev->phy.rev < 2)
+			b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, ~0xFF, 0x40);
+
+		if (dev->phy.rev < 2 && dev->phy.is_40mhz)
+			b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_TSSIRPSMW);
+
+		if (b43_nphy_ipa(dev)) {
+			b43_phy_mask(dev, B43_NPHY_PAPD_EN0, ~0x4);
+			b43_phy_mask(dev, B43_NPHY_PAPD_EN1, ~0x4);
+		}
 	}
 
 	if (nphy->hang_avoid)
@@ -369,22 +446,23 @@
 		else
 			b43_phy_write(dev, B43_NPHY_AFECTL_DACGAIN2, dac_gain);
 
-		b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x1D10 + i);
-		b43_phy_write(dev, B43_NPHY_TABLE_DATALO, radio_gain);
-
-		b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x3C57);
-		tmp = b43_phy_read(dev, B43_NPHY_TABLE_DATALO);
+		b43_ntab_write(dev, B43_NTAB16(0x7, 0x110 + i), radio_gain);
 
+		tmp = b43_ntab_read(dev, B43_NTAB16(0xF, 0x57));
 		if (i == 0)
 			tmp = (tmp & 0x00FF) | (bbmult << 8);
 		else
 			tmp = (tmp & 0xFF00) | bbmult;
+		b43_ntab_write(dev, B43_NTAB16(0xF, 0x57), tmp);
 
-		b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x3C57);
-		b43_phy_write(dev, B43_NPHY_TABLE_DATALO, tmp);
-
-		if (0)
-			; /* TODO */
+		if (b43_nphy_ipa(dev)) {
+			u32 tmp32;
+			u16 reg = (i == 0) ?
+				B43_NPHY_PAPD_EN0 : B43_NPHY_PAPD_EN1;
+			tmp32 = b43_ntab_read(dev, B43_NTAB32(26 + i, txpi[i]));
+			b43_phy_maskset(dev, reg, 0xE00F, (u32) tmp32 << 4);
+			b43_phy_set(dev, reg, 0x4);
+		}
 	}
 
 	b43_phy_mask(dev, B43_NPHY_BPHY_CTL2, ~B43_NPHY_BPHY_CTL2_LUT);
@@ -393,6 +471,57 @@
 		b43_nphy_stay_in_carrier_search(dev, 0);
 }
 
+static void b43_nphy_tx_gain_table_upload(struct b43_wldev *dev)
+{
+	struct b43_phy *phy = &dev->phy;
+
+	const u32 *table = NULL;
+#if 0
+	TODO: b43_ntab_papd_pga_gain_delta_ipa_2*
+	u32 rfpwr_offset;
+	u8 pga_gain;
+	int i;
+#endif
+
+	if (phy->rev >= 3) {
+		if (b43_nphy_ipa(dev)) {
+			table = b43_nphy_get_ipa_gain_table(dev);
+		} else {
+			if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+				if (phy->rev == 3)
+					table = b43_ntab_tx_gain_rev3_5ghz;
+				if (phy->rev == 4)
+					table = b43_ntab_tx_gain_rev4_5ghz;
+				else
+					table = b43_ntab_tx_gain_rev5plus_5ghz;
+			} else {
+				table = b43_ntab_tx_gain_rev3plus_2ghz;
+			}
+		}
+	} else {
+		table = b43_ntab_tx_gain_rev0_1_2;
+	}
+	b43_ntab_write_bulk(dev, B43_NTAB32(26, 192), 128, table);
+	b43_ntab_write_bulk(dev, B43_NTAB32(27, 192), 128, table);
+
+	if (phy->rev >= 3) {
+#if 0
+		nphy->gmval = (table[0] >> 16) & 0x7000;
+
+		for (i = 0; i < 128; i++) {
+			pga_gain = (table[i] >> 24) & 0xF;
+			if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
+				rfpwr_offset = b43_ntab_papd_pga_gain_delta_ipa_2g[pga_gain];
+			else
+				rfpwr_offset = b43_ntab_papd_pga_gain_delta_ipa_5g[pga_gain];
+			b43_ntab_write(dev, B43_NTAB32(26, 576 + i),
+				       rfpwr_offset);
+			b43_ntab_write(dev, B43_NTAB32(27, 576 + i),
+				       rfpwr_offset);
+		}
+#endif
+	}
+}
 
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/Radio/2055Setup */
 static void b43_radio_2055_setup(struct b43_wldev *dev,
@@ -581,14 +710,10 @@
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxLpFbw */
 static void b43_nphy_tx_lp_fbw(struct b43_wldev *dev)
 {
-	struct b43_phy_n *nphy = dev->phy.n;
 	u16 tmp;
-	enum ieee80211_band band = b43_current_band(dev->wl);
-	bool ipa = (nphy->ipa2g_on && band == IEEE80211_BAND_2GHZ) ||
-			(nphy->ipa5g_on && band == IEEE80211_BAND_5GHZ);
 
 	if (dev->phy.rev >= 3) {
-		if (ipa) {
+		if (b43_nphy_ipa(dev)) {
 			tmp = 4;
 			b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S2,
 			      (((((tmp << 3) | tmp) << 3) | tmp) << 3) | tmp);
@@ -600,49 +725,17 @@
 	}
 }
 
-/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/BmacPhyClkFgc */
-static void b43_nphy_bmac_clock_fgc(struct b43_wldev *dev, bool force)
-{
-	u32 tmp;
-
-	if (dev->phy.type != B43_PHYTYPE_N)
-		return;
-
-	switch (dev->dev->bus_type) {
-#ifdef CONFIG_B43_BCMA
-	case B43_BUS_BCMA:
-		tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL);
-		if (force)
-			tmp |= BCMA_IOCTL_FGC;
-		else
-			tmp &= ~BCMA_IOCTL_FGC;
-		bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp);
-		break;
-#endif
-#ifdef CONFIG_B43_SSB
-	case B43_BUS_SSB:
-		tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW);
-		if (force)
-			tmp |= SSB_TMSLOW_FGC;
-		else
-			tmp &= ~SSB_TMSLOW_FGC;
-		ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp);
-		break;
-#endif
-	}
-}
-
 /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CCA */
 static void b43_nphy_reset_cca(struct b43_wldev *dev)
 {
 	u16 bbcfg;
 
-	b43_nphy_bmac_clock_fgc(dev, 1);
+	b43_phy_force_clock(dev, 1);
 	bbcfg = b43_phy_read(dev, B43_NPHY_BBCFG);
 	b43_phy_write(dev, B43_NPHY_BBCFG, bbcfg | B43_NPHY_BBCFG_RSTCCA);
 	udelay(1);
 	b43_phy_write(dev, B43_NPHY_BBCFG, bbcfg & ~B43_NPHY_BBCFG_RSTCCA);
-	b43_nphy_bmac_clock_fgc(dev, 0);
+	b43_phy_force_clock(dev, 0);
 	b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX);
 }
 
@@ -931,11 +1024,7 @@
 static void b43_nphy_tx_iq_workaround(struct b43_wldev *dev)
 {
 	u16 array[4];
-	int i;
-
-	b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x3C50);
-	for (i = 0; i < 4; i++)
-		array[i] = b43_phy_read(dev, B43_NPHY_TABLE_DATALO);
+	b43_ntab_read_bulk(dev, B43_NTAB16(0xF, 0x50), 4, array);
 
 	b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW0, array[0]);
 	b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW1, array[1]);
@@ -1398,180 +1487,220 @@
 	}
 }
 
-/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/Workarounds */
-static void b43_nphy_workarounds(struct b43_wldev *dev)
+static void b43_nphy_workarounds_rev3plus(struct b43_wldev *dev)
 {
+	struct b43_phy_n *nphy = dev->phy.n;
 	struct ssb_sprom *sprom = dev->dev->bus_sprom;
-	struct b43_phy *phy = &dev->phy;
-	struct b43_phy_n *nphy = phy->n;
-
-	u8 events1[7] = { 0x0, 0x1, 0x2, 0x8, 0x4, 0x5, 0x3 };
-	u8 delays1[7] = { 0x8, 0x6, 0x6, 0x2, 0x4, 0x3C, 0x1 };
 
-	u8 events2[7] = { 0x0, 0x3, 0x5, 0x4, 0x2, 0x1, 0x8 };
-	u8 delays2[7] = { 0x8, 0x6, 0x2, 0x4, 0x4, 0x6, 0x1 };
+	/* TX to RX */
+	u8 tx2rx_events[9] = { 0x4, 0x3, 0x6, 0x5, 0x2, 0x1, 0x8, 0x1F };
+	u8 tx2rx_delays[9] = { 8, 4, 2, 2, 4, 4, 6, 1 };
+	/* RX to TX */
+	u8 rx2tx_events_ipa[9] = { 0x0, 0x1, 0x2, 0x8, 0x5, 0x6, 0xF, 0x3,
+					0x1F };
+	u8 rx2tx_delays_ipa[9] = { 8, 6, 6, 4, 4, 16, 43, 1, 1 };
+	u8 rx2tx_events[9] = { 0x0, 0x1, 0x2, 0x8, 0x5, 0x6, 0x3, 0x4, 0x1F };
+	u8 rx2tx_delays[9] = { 8, 6, 6, 4, 4, 18, 42, 1, 1 };
 
 	u16 tmp16;
 	u32 tmp32;
 
-	if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ)
-		b43_nphy_classifier(dev, 1, 0);
-	else
-		b43_nphy_classifier(dev, 1, 1);
+	tmp32 = b43_ntab_read(dev, B43_NTAB32(30, 0));
+	tmp32 &= 0xffffff;
+	b43_ntab_write(dev, B43_NTAB32(30, 0), tmp32);
+
+	b43_phy_write(dev, B43_NPHY_PHASETR_A0, 0x0125);
+	b43_phy_write(dev, B43_NPHY_PHASETR_A1, 0x01B3);
+	b43_phy_write(dev, B43_NPHY_PHASETR_A2, 0x0105);
+	b43_phy_write(dev, B43_NPHY_PHASETR_B0, 0x016E);
+	b43_phy_write(dev, B43_NPHY_PHASETR_B1, 0x00CD);
+	b43_phy_write(dev, B43_NPHY_PHASETR_B2, 0x0020);
+
+	b43_phy_write(dev, B43_NPHY_C2_CLIP1_MEDGAIN, 0x000C);
+	b43_phy_write(dev, 0x2AE, 0x000C);
+
+	/* TX to RX */
+	b43_nphy_set_rf_sequence(dev, 1, tx2rx_events, tx2rx_delays, 9);
+
+	/* RX to TX */
+	if (b43_nphy_ipa(dev))
+		b43_nphy_set_rf_sequence(dev, 1, rx2tx_events_ipa,
+					 rx2tx_delays_ipa, 9);
+	if (nphy->hw_phyrxchain != 3 &&
+	    nphy->hw_phyrxchain != nphy->hw_phytxchain) {
+		if (b43_nphy_ipa(dev)) {
+			rx2tx_delays[5] = 59;
+			rx2tx_delays[6] = 1;
+			rx2tx_events[7] = 0x1F;
+		}
+		b43_nphy_set_rf_sequence(dev, 1, rx2tx_events, rx2tx_delays, 9);
+	}
+
+	tmp16 = (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) ?
+		0x2 : 0x9C40;
+	b43_phy_write(dev, B43_NPHY_ENDROP_TLEN, tmp16);
+
+	b43_phy_maskset(dev, 0x294, 0xF0FF, 0x0700);
 
-	if (nphy->hang_avoid)
-		b43_nphy_stay_in_carrier_search(dev, 1);
+	b43_ntab_write(dev, B43_NTAB32(16, 3), 0x18D);
+	b43_ntab_write(dev, B43_NTAB32(16, 127), 0x18D);
 
-	b43_phy_set(dev, B43_NPHY_IQFLIP,
-		    B43_NPHY_IQFLIP_ADC1 | B43_NPHY_IQFLIP_ADC2);
+	b43_nphy_gain_ctrl_workarounds(dev);
 
-	if (dev->phy.rev >= 3) {
-		tmp32 = b43_ntab_read(dev, B43_NTAB32(30, 0));
-		tmp32 &= 0xffffff;
-		b43_ntab_write(dev, B43_NTAB32(30, 0), tmp32);
-
-		b43_phy_write(dev, B43_NPHY_PHASETR_A0, 0x0125);
-		b43_phy_write(dev, B43_NPHY_PHASETR_A1, 0x01B3);
-		b43_phy_write(dev, B43_NPHY_PHASETR_A2, 0x0105);
-		b43_phy_write(dev, B43_NPHY_PHASETR_B0, 0x016E);
-		b43_phy_write(dev, B43_NPHY_PHASETR_B1, 0x00CD);
-		b43_phy_write(dev, B43_NPHY_PHASETR_B2, 0x0020);
+	b43_ntab_write(dev, B43_NTAB32(8, 0), 2);
+	b43_ntab_write(dev, B43_NTAB32(8, 16), 2);
 
-		b43_phy_write(dev, B43_NPHY_C2_CLIP1_MEDGAIN, 0x000C);
-		b43_phy_write(dev, 0x2AE, 0x000C);
+	/* TODO */
 
-		/* TODO */
+	b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_MAST_BIAS, 0x00);
+	b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_MAST_BIAS, 0x00);
+	b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_BIAS_MAIN, 0x06);
+	b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_BIAS_MAIN, 0x06);
+	b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_BIAS_AUX, 0x07);
+	b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_BIAS_AUX, 0x07);
+	b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_LOB_BIAS, 0x88);
+	b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_LOB_BIAS, 0x88);
+	b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXG_CMFB_IDAC, 0x00);
+	b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXG_CMFB_IDAC, 0x00);
+
+	/* N PHY WAR TX Chain Update with hw_phytxchain as argument */
+
+	if ((sprom->boardflags2_lo & B43_BFL2_APLL_WAR &&
+	     b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) ||
+	    (sprom->boardflags2_lo & B43_BFL2_GPLL_WAR &&
+	     b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ))
+		tmp32 = 0x00088888;
+	else
+		tmp32 = 0x88888888;
+	b43_ntab_write(dev, B43_NTAB32(30, 1), tmp32);
+	b43_ntab_write(dev, B43_NTAB32(30, 2), tmp32);
+	b43_ntab_write(dev, B43_NTAB32(30, 3), tmp32);
+
+	if (dev->phy.rev == 4 &&
+		b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
+		b43_radio_write(dev, B2056_TX0 | B2056_TX_GMBB_IDAC,
+				0x70);
+		b43_radio_write(dev, B2056_TX1 | B2056_TX_GMBB_IDAC,
+				0x70);
+	}
+
+	b43_phy_write(dev, 0x224, 0x039C);
+	b43_phy_write(dev, 0x225, 0x0357);
+	b43_phy_write(dev, 0x226, 0x0317);
+	b43_phy_write(dev, 0x227, 0x02D7);
+	b43_phy_write(dev, 0x228, 0x039C);
+	b43_phy_write(dev, 0x229, 0x0357);
+	b43_phy_write(dev, 0x22A, 0x0317);
+	b43_phy_write(dev, 0x22B, 0x02D7);
+	b43_phy_write(dev, 0x22C, 0x039C);
+	b43_phy_write(dev, 0x22D, 0x0357);
+	b43_phy_write(dev, 0x22E, 0x0317);
+	b43_phy_write(dev, 0x22F, 0x02D7);
+}
 
-		tmp16 = (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) ?
-			0x2 : 0x9C40;
-		b43_phy_write(dev, B43_NPHY_ENDROP_TLEN, tmp16);
+static void b43_nphy_workarounds_rev1_2(struct b43_wldev *dev)
+{
+	struct ssb_sprom *sprom = dev->dev->bus_sprom;
+	struct b43_phy *phy = &dev->phy;
+	struct b43_phy_n *nphy = phy->n;
 
-		b43_phy_maskset(dev, 0x294, 0xF0FF, 0x0700);
+	u8 events1[7] = { 0x0, 0x1, 0x2, 0x8, 0x4, 0x5, 0x3 };
+	u8 delays1[7] = { 0x8, 0x6, 0x6, 0x2, 0x4, 0x3C, 0x1 };
 
-		b43_ntab_write(dev, B43_NTAB32(16, 3), 0x18D);
-		b43_ntab_write(dev, B43_NTAB32(16, 127), 0x18D);
+	u8 events2[7] = { 0x0, 0x3, 0x5, 0x4, 0x2, 0x1, 0x8 };
+	u8 delays2[7] = { 0x8, 0x6, 0x2, 0x4, 0x4, 0x6, 0x1 };
 
-		b43_nphy_gain_ctrl_workarounds(dev);
+	if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ &&
+	    nphy->band5g_pwrgain) {
+		b43_radio_mask(dev, B2055_C1_TX_RF_SPARE, ~0x8);
+		b43_radio_mask(dev, B2055_C2_TX_RF_SPARE, ~0x8);
+	} else {
+		b43_radio_set(dev, B2055_C1_TX_RF_SPARE, 0x8);
+		b43_radio_set(dev, B2055_C2_TX_RF_SPARE, 0x8);
+	}
 
-		b43_ntab_write(dev, B43_NTAB32(8, 0), 2);
-		b43_ntab_write(dev, B43_NTAB32(8, 16), 2);
+	b43_ntab_write(dev, B43_NTAB16(8, 0x00), 0x000A);
+	b43_ntab_write(dev, B43_NTAB16(8, 0x10), 0x000A);
+	b43_ntab_write(dev, B43_NTAB16(8, 0x02), 0xCDAA);
+	b43_ntab_write(dev, B43_NTAB16(8, 0x12), 0xCDAA);
 
-		/* TODO */
+	if (dev->phy.rev < 2) {
+		b43_ntab_write(dev, B43_NTAB16(8, 0x08), 0x0000);
+		b43_ntab_write(dev, B43_NTAB16(8, 0x18), 0x0000);
+		b43_ntab_write(dev, B43_NTAB16(8, 0x07), 0x7AAB);
+		b43_ntab_write(dev, B43_NTAB16(8, 0x17), 0x7AAB);
+		b43_ntab_write(dev, B43_NTAB16(8, 0x06), 0x0800);
+		b43_ntab_write(dev, B43_NTAB16(8, 0x16), 0x0800);
+	}
+
+	b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO1, 0x2D8);
+	b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, 0x301);
+	b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO2, 0x2D8);
+	b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, 0x301);
+
+	if (sprom->boardflags2_lo & B43_BFL2_SKWRKFEM_BRD &&
+	    dev->dev->board_type == 0x8B) {
+		delays1[0] = 0x1;
+		delays1[5] = 0x14;
+	}
+	b43_nphy_set_rf_sequence(dev, 0, events1, delays1, 7);
+	b43_nphy_set_rf_sequence(dev, 1, events2, delays2, 7);
 
-		b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_MAST_BIAS, 0x00);
-		b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_MAST_BIAS, 0x00);
-		b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_BIAS_MAIN, 0x06);
-		b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_BIAS_MAIN, 0x06);
-		b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_BIAS_AUX, 0x07);
-		b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_BIAS_AUX, 0x07);
-		b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_LOB_BIAS, 0x88);
-		b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_LOB_BIAS, 0x88);
-		b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXG_CMFB_IDAC, 0x00);
-		b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXG_CMFB_IDAC, 0x00);
-
-		/* N PHY WAR TX Chain Update with hw_phytxchain as argument */
-
-		if ((sprom->boardflags2_lo & B43_BFL2_APLL_WAR &&
-		    b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) ||
-		    (sprom->boardflags2_lo & B43_BFL2_GPLL_WAR &&
-		    b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ))
-			tmp32 = 0x00088888;
-		else
-			tmp32 = 0x88888888;
-		b43_ntab_write(dev, B43_NTAB32(30, 1), tmp32);
-		b43_ntab_write(dev, B43_NTAB32(30, 2), tmp32);
-		b43_ntab_write(dev, B43_NTAB32(30, 3), tmp32);
-
-		if (dev->phy.rev == 4 &&
-		    b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
-			b43_radio_write(dev, B2056_TX0 | B2056_TX_GMBB_IDAC,
-					0x70);
-			b43_radio_write(dev, B2056_TX1 | B2056_TX_GMBB_IDAC,
-					0x70);
-		}
-
-		b43_phy_write(dev, 0x224, 0x039C);
-		b43_phy_write(dev, 0x225, 0x0357);
-		b43_phy_write(dev, 0x226, 0x0317);
-		b43_phy_write(dev, 0x227, 0x02D7);
-		b43_phy_write(dev, 0x228, 0x039C);
-		b43_phy_write(dev, 0x229, 0x0357);
-		b43_phy_write(dev, 0x22A, 0x0317);
-		b43_phy_write(dev, 0x22B, 0x02D7);
-		b43_phy_write(dev, 0x22C, 0x039C);
-		b43_phy_write(dev, 0x22D, 0x0357);
-		b43_phy_write(dev, 0x22E, 0x0317);
-		b43_phy_write(dev, 0x22F, 0x02D7);
-	} else {
-		if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ &&
-		    nphy->band5g_pwrgain) {
-			b43_radio_mask(dev, B2055_C1_TX_RF_SPARE, ~0x8);
-			b43_radio_mask(dev, B2055_C2_TX_RF_SPARE, ~0x8);
-		} else {
-			b43_radio_set(dev, B2055_C1_TX_RF_SPARE, 0x8);
-			b43_radio_set(dev, B2055_C2_TX_RF_SPARE, 0x8);
-		}
-
-		b43_ntab_write(dev, B43_NTAB16(8, 0x00), 0x000A);
-		b43_ntab_write(dev, B43_NTAB16(8, 0x10), 0x000A);
-		b43_ntab_write(dev, B43_NTAB16(8, 0x02), 0xCDAA);
-		b43_ntab_write(dev, B43_NTAB16(8, 0x12), 0xCDAA);
+	b43_nphy_gain_ctrl_workarounds(dev);
 
-		if (dev->phy.rev < 2) {
-			b43_ntab_write(dev, B43_NTAB16(8, 0x08), 0x0000);
-			b43_ntab_write(dev, B43_NTAB16(8, 0x18), 0x0000);
-			b43_ntab_write(dev, B43_NTAB16(8, 0x07), 0x7AAB);
-			b43_ntab_write(dev, B43_NTAB16(8, 0x17), 0x7AAB);
-			b43_ntab_write(dev, B43_NTAB16(8, 0x06), 0x0800);
-			b43_ntab_write(dev, B43_NTAB16(8, 0x16), 0x0800);
-		}
-
-		b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO1, 0x2D8);
-		b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, 0x301);
-		b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_LO2, 0x2D8);
-		b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, 0x301);
-
-		if (sprom->boardflags2_lo & 0x100 &&
-		    dev->dev->board_type == 0x8B) {
-			delays1[0] = 0x1;
-			delays1[5] = 0x14;
-		}
-		b43_nphy_set_rf_sequence(dev, 0, events1, delays1, 7);
-		b43_nphy_set_rf_sequence(dev, 1, events2, delays2, 7);
+	if (dev->phy.rev < 2) {
+		if (b43_phy_read(dev, B43_NPHY_RXCTL) & 0x2)
+			b43_hf_write(dev, b43_hf_read(dev) |
+					B43_HF_MLADVW);
+	} else if (dev->phy.rev == 2) {
+		b43_phy_write(dev, B43_NPHY_CRSCHECK2, 0);
+		b43_phy_write(dev, B43_NPHY_CRSCHECK3, 0);
+	}
 
-		b43_nphy_gain_ctrl_workarounds(dev);
+	if (dev->phy.rev < 2)
+		b43_phy_mask(dev, B43_NPHY_SCRAM_SIGCTL,
+				~B43_NPHY_SCRAM_SIGCTL_SCM);
 
-		if (dev->phy.rev < 2) {
-			if (b43_phy_read(dev, B43_NPHY_RXCTL) & 0x2)
-				b43_hf_write(dev, b43_hf_read(dev) |
-						B43_HF_MLADVW);
-		} else if (dev->phy.rev == 2) {
-			b43_phy_write(dev, B43_NPHY_CRSCHECK2, 0);
-			b43_phy_write(dev, B43_NPHY_CRSCHECK3, 0);
-		}
-
-		if (dev->phy.rev < 2)
-			b43_phy_mask(dev, B43_NPHY_SCRAM_SIGCTL,
-					~B43_NPHY_SCRAM_SIGCTL_SCM);
-
-		/* Set phase track alpha and beta */
-		b43_phy_write(dev, B43_NPHY_PHASETR_A0, 0x125);
-		b43_phy_write(dev, B43_NPHY_PHASETR_A1, 0x1B3);
-		b43_phy_write(dev, B43_NPHY_PHASETR_A2, 0x105);
-		b43_phy_write(dev, B43_NPHY_PHASETR_B0, 0x16E);
-		b43_phy_write(dev, B43_NPHY_PHASETR_B1, 0xCD);
-		b43_phy_write(dev, B43_NPHY_PHASETR_B2, 0x20);
-
-		b43_phy_mask(dev, B43_NPHY_PIL_DW1,
-				~B43_NPHY_PIL_DW_64QAM & 0xFFFF);
-		b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B1, 0xB5);
-		b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B2, 0xA4);
-		b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B3, 0x00);
+	/* Set phase track alpha and beta */
+	b43_phy_write(dev, B43_NPHY_PHASETR_A0, 0x125);
+	b43_phy_write(dev, B43_NPHY_PHASETR_A1, 0x1B3);
+	b43_phy_write(dev, B43_NPHY_PHASETR_A2, 0x105);
+	b43_phy_write(dev, B43_NPHY_PHASETR_B0, 0x16E);
+	b43_phy_write(dev, B43_NPHY_PHASETR_B1, 0xCD);
+	b43_phy_write(dev, B43_NPHY_PHASETR_B2, 0x20);
+
+	b43_phy_mask(dev, B43_NPHY_PIL_DW1,
+			~B43_NPHY_PIL_DW_64QAM & 0xFFFF);
+	b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B1, 0xB5);
+	b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B2, 0xA4);
+	b43_phy_write(dev, B43_NPHY_TXF_20CO_S2B3, 0x00);
+
+	if (dev->phy.rev == 2)
+		b43_phy_set(dev, B43_NPHY_FINERX2_CGC,
+				B43_NPHY_FINERX2_CGC_DECGC);
+}
 
-		if (dev->phy.rev == 2)
-			b43_phy_set(dev, B43_NPHY_FINERX2_CGC,
-					B43_NPHY_FINERX2_CGC_DECGC);
-	}
+/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/Workarounds */
+static void b43_nphy_workarounds(struct b43_wldev *dev)
+{
+	struct b43_phy *phy = &dev->phy;
+	struct b43_phy_n *nphy = phy->n;
+
+	if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ)
+		b43_nphy_classifier(dev, 1, 0);
+	else
+		b43_nphy_classifier(dev, 1, 1);
+
+	if (nphy->hang_avoid)
+		b43_nphy_stay_in_carrier_search(dev, 1);
+
+	b43_phy_set(dev, B43_NPHY_IQFLIP,
+		    B43_NPHY_IQFLIP_ADC1 | B43_NPHY_IQFLIP_ADC2);
+
+	if (dev->phy.rev >= 3)
+		b43_nphy_workarounds_rev3plus(dev);
+	else
+		b43_nphy_workarounds_rev1_2(dev);
 
 	if (nphy->hang_avoid)
 		b43_nphy_stay_in_carrier_search(dev, 0);
@@ -2167,7 +2296,6 @@
 
 static void b43_nphy_rev3_rssi_select(struct b43_wldev *dev, u8 code, u8 type)
 {
-	struct b43_phy_n *nphy = dev->phy.n;
 	u8 i;
 	u16 reg, val;
 
@@ -2231,10 +2359,7 @@
 					enum ieee80211_band band =
 						b43_current_band(dev->wl);
 
-					if ((nphy->ipa2g_on &&
-						band == IEEE80211_BAND_2GHZ) ||
-						(nphy->ipa5g_on &&
-						band == IEEE80211_BAND_5GHZ))
+					if (b43_nphy_ipa(dev))
 						val = (band == IEEE80211_BAND_5GHZ) ? 0xC : 0xE;
 					else
 						val = 0x11;
@@ -2609,8 +2734,8 @@
 {
 	if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
 		if (dev->phy.rev >= 6) {
-			/* TODO If the chip is 47162
-				return txpwrctrl_tx_gain_ipa_rev5 */
+			if (dev->dev->chip_id == 47162)
+				return txpwrctrl_tx_gain_ipa_rev5;
 			return txpwrctrl_tx_gain_ipa_rev6;
 		} else if (dev->phy.rev >= 5) {
 			return txpwrctrl_tx_gain_ipa_rev5;
@@ -2860,10 +2985,7 @@
 				enum ieee80211_band band =
 					b43_current_band(dev->wl);
 
-				if ((nphy->ipa2g_on &&
-				     band == IEEE80211_BAND_2GHZ) ||
-				    (nphy->ipa5g_on &&
-				     band == IEEE80211_BAND_5GHZ)) {
+				if (b43_nphy_ipa(dev)) {
 					table = b43_nphy_get_ipa_gain_table(dev);
 				} else {
 					if (band == IEEE80211_BAND_5GHZ) {
@@ -3680,7 +3802,7 @@
 	b43_phy_write(dev, B43_NPHY_AFESEQ_TX2RX_PUD_20M, 0x20);
 	b43_phy_write(dev, B43_NPHY_AFESEQ_TX2RX_PUD_40M, 0x20);
 
-	if (sprom->boardflags2_lo & 0x100 ||
+	if (sprom->boardflags2_lo & B43_BFL2_SKWRKFEM_BRD ||
 	    (dev->dev->board_vendor == PCI_VENDOR_ID_APPLE &&
 	     dev->dev->board_type == 0x8B))
 		b43_phy_write(dev, B43_NPHY_TXREALFD, 0xA0);
@@ -3699,8 +3821,7 @@
 	}
 
 	tmp2 = b43_current_band(dev->wl);
-	if ((nphy->ipa2g_on && tmp2 == IEEE80211_BAND_2GHZ) ||
-	    (nphy->ipa5g_on && tmp2 == IEEE80211_BAND_5GHZ)) {
+	if (b43_nphy_ipa(dev)) {
 		b43_phy_set(dev, B43_NPHY_PAPD_EN0, 0x1);
 		b43_phy_maskset(dev, B43_NPHY_EPS_TABLE_ADJ0, 0x007F,
 				nphy->papd_epsilon_offset[0] << 7);
@@ -3715,11 +3836,11 @@
 	b43_nphy_workarounds(dev);
 
 	/* Reset CCA, in init code it differs a little from standard way */
-	b43_nphy_bmac_clock_fgc(dev, 1);
+	b43_phy_force_clock(dev, 1);
 	tmp = b43_phy_read(dev, B43_NPHY_BBCFG);
 	b43_phy_write(dev, B43_NPHY_BBCFG, tmp | B43_NPHY_BBCFG_RSTCCA);
 	b43_phy_write(dev, B43_NPHY_BBCFG, tmp & ~B43_NPHY_BBCFG_RSTCCA);
-	b43_nphy_bmac_clock_fgc(dev, 0);
+	b43_phy_force_clock(dev, 0);
 
 	b43_mac_phy_clock_set(dev, true);
 
@@ -3738,15 +3859,7 @@
 	b43_nphy_tx_power_fix(dev);
 	/* TODO N PHY TX Power Control Idle TSSI */
 	/* TODO N PHY TX Power Control Setup */
-
-	if (phy->rev >= 3) {
-		/* TODO */
-	} else {
-		b43_ntab_write_bulk(dev, B43_NTAB32(26, 192), 128,
-					b43_ntab_tx_gain_rev0_1_2);
-		b43_ntab_write_bulk(dev, B43_NTAB32(27, 192), 128,
-					b43_ntab_tx_gain_rev0_1_2);
-	}
+	b43_nphy_tx_gain_table_upload(dev);
 
 	if (nphy->phyrxchain != 3)
 		b43_nphy_set_rx_core_state(dev, nphy->phyrxchain);
@@ -3950,6 +4063,10 @@
 	nphy->txrx_chain = 2; /* sth different than 0 and 1 for now */
 	nphy->phyrxchain = 3; /* to avoid b43_nphy_set_rx_core_state like wl */
 	nphy->perical = 2; /* avoid additional rssi cal on init (like wl) */
+	/* 128 can mean disabled-by-default state of TX pwr ctl. Max value is
+	 * 0x7f == 127 and we check for 128 when restoring TX pwr ctl. */
+	nphy->tx_pwr_idx[0] = 128;
+	nphy->tx_pwr_idx[1] = 128;
 }
 
 static void b43_nphy_op_free(struct b43_wldev *dev)
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/phy_n.h linux-3.1.1/drivers/net/wireless/b43/phy_n.h
--- linux-3.1.1.orig/drivers/net/wireless/b43/phy_n.h	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/phy_n.h	2011-11-13 17:11:58.609558134 -0800
@@ -764,6 +764,8 @@
 	u8 cal_orig_pwr_idx[2];
 	u8 measure_hold;
 	u8 phyrxchain;
+	u8 hw_phyrxchain;
+	u8 hw_phytxchain;
 	u8 perical;
 	u32 deaf_count;
 	u32 rxcalparams;
@@ -783,6 +785,8 @@
 	u16 mphase_txcal_bestcoeffs[11];
 
 	bool txpwrctrl;
+	u8 tx_pwr_idx[2];
+	u16 adj_pwr_tbl[84];
 	u16 txcal_bbmult;
 	u16 txiqlocal_bestc[11];
 	bool txiqlocal_coeffsvalid;
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/pio.c linux-3.1.1/drivers/net/wireless/b43/pio.c
--- linux-3.1.1.orig/drivers/net/wireless/b43/pio.c	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/pio.c	2011-11-13 17:11:58.609558134 -0800
@@ -611,7 +611,7 @@
 	struct b43_wldev *dev = q->dev;
 	struct b43_wl *wl = dev->wl;
 	u16 len;
-	u32 macstat;
+	u32 macstat = 0;
 	unsigned int i, padding;
 	struct sk_buff *skb;
 	const char *err_msg = NULL;
@@ -676,7 +676,15 @@
 		goto rx_error;
 	}
 
-	macstat = le32_to_cpu(rxhdr->mac_status);
+	switch (dev->fw.hdr_format) {
+	case B43_FW_HDR_598:
+		macstat = le32_to_cpu(rxhdr->format_598.mac_status);
+		break;
+	case B43_FW_HDR_410:
+	case B43_FW_HDR_351:
+		macstat = le32_to_cpu(rxhdr->format_351.mac_status);
+		break;
+	}
 	if (macstat & B43_RX_MAC_FCSERR) {
 		if (!(q->dev->wl->filter_flags & FIF_FCSFAIL)) {
 			/* Drop frames with failed FCS. */
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/radio_2055.c linux-3.1.1/drivers/net/wireless/b43/radio_2055.c
--- linux-3.1.1.orig/drivers/net/wireless/b43/radio_2055.c	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/radio_2055.c	2011-11-13 17:11:58.609558134 -0800
@@ -4,6 +4,7 @@
   IEEE 802.11n PHY and radio device data tables
 
   Copyright (c) 2008 Michael Buesch <m@bues.ch>
+  Copyright (c) 2010 Rafał Miłecki <zajec5@gmail.com>
 
   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
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/radio_2056.c linux-3.1.1/drivers/net/wireless/b43/radio_2056.c
--- linux-3.1.1.orig/drivers/net/wireless/b43/radio_2056.c	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/radio_2056.c	2011-11-13 17:11:58.609558134 -0800
@@ -3,6 +3,8 @@
   Broadcom B43 wireless driver
   IEEE 802.11n 2056 radio device data tables
 
+  Copyright (c) 2010 Rafał Miłecki <zajec5@gmail.com>
+
   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
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/radio_2056.h linux-3.1.1/drivers/net/wireless/b43/radio_2056.h
--- linux-3.1.1.orig/drivers/net/wireless/b43/radio_2056.h	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/radio_2056.h	2011-11-13 17:11:58.609558134 -0800
@@ -1,29 +1,3 @@
-/*
-
-  Broadcom B43 wireless driver
-
-  Copyright (c) 2010 Rafał Miłecki <zajec5@gmail.com>
-
-  Some parts of the code in this file are derived from the brcm80211
-  driver  Copyright (c) 2010 Broadcom Corporation
-
-  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; see the file COPYING.  If not, write to
-  the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
-  Boston, MA 02110-1301, USA.
-
-*/
-
 #ifndef B43_RADIO_2056_H_
 #define B43_RADIO_2056_H_
 
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/radio_2059.c linux-3.1.1/drivers/net/wireless/b43/radio_2059.c
--- linux-3.1.1.orig/drivers/net/wireless/b43/radio_2059.c	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/radio_2059.c	2011-11-13 17:11:58.609558134 -0800
@@ -3,6 +3,8 @@
   Broadcom B43 wireless driver
   IEEE 802.11n 2059 radio device data tables
 
+  Copyright (c) 2011 Rafał Miłecki <zajec5@gmail.com>
+
   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
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/tables_nphy.c linux-3.1.1/drivers/net/wireless/b43/tables_nphy.c
--- linux-3.1.1.orig/drivers/net/wireless/b43/tables_nphy.c	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/tables_nphy.c	2011-11-13 17:11:58.609558134 -0800
@@ -4,6 +4,7 @@
   IEEE 802.11n PHY data tables
 
   Copyright (c) 2008 Michael Buesch <m@bues.ch>
+  Copyright (c) 2010 Rafał Miłecki <zajec5@gmail.com>
 
   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
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/tables_phy_ht.c linux-3.1.1/drivers/net/wireless/b43/tables_phy_ht.c
--- linux-3.1.1.orig/drivers/net/wireless/b43/tables_phy_ht.c	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/tables_phy_ht.c	2011-11-13 17:11:58.609558134 -0800
@@ -3,6 +3,8 @@
   Broadcom B43 wireless driver
   IEEE 802.11n HT-PHY data tables
 
+  Copyright (c) 2011 Rafał Miłecki <zajec5@gmail.com>
+
   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
@@ -574,6 +576,42 @@
 	0x005d0582, 0x005805d6, 0x0053062e, 0x004e068c,
 };
 
+/* Some late-init table */
+const u32 b43_httab_0x1a_0xc0_late[] = {
+	0x10f90040, 0x10e10040, 0x10e1003c, 0x10c9003d,
+	0x10b9003c, 0x10a9003d, 0x10a1003c, 0x1099003b,
+	0x1091003b, 0x1089003a, 0x1081003a, 0x10790039,
+	0x10710039, 0x1069003a, 0x1061003b, 0x1059003d,
+	0x1051003f, 0x10490042, 0x1049003e, 0x1049003b,
+	0x1041003e, 0x1041003b, 0x1039003e, 0x1039003b,
+	0x10390038, 0x10390035, 0x1031003a, 0x10310036,
+	0x10310033, 0x1029003a, 0x10290037, 0x10290034,
+	0x10290031, 0x10210039, 0x10210036, 0x10210033,
+	0x10210030, 0x1019003c, 0x10190039, 0x10190036,
+	0x10190033, 0x10190030, 0x1019002d, 0x1019002b,
+	0x10190028, 0x1011003a, 0x10110036, 0x10110033,
+	0x10110030, 0x1011002e, 0x1011002b, 0x10110029,
+	0x10110027, 0x10110024, 0x10110022, 0x10110020,
+	0x1011001f, 0x1011001d, 0x1009003a, 0x10090037,
+	0x10090034, 0x10090031, 0x1009002e, 0x1009002c,
+	0x10090029, 0x10090027, 0x10090025, 0x10090023,
+	0x10090021, 0x1009001f, 0x1009001d, 0x1009001b,
+	0x1009001a, 0x10090018, 0x10090017, 0x10090016,
+	0x10090015, 0x10090013, 0x10090012, 0x10090011,
+	0x10090010, 0x1009000f, 0x1009000f, 0x1009000e,
+	0x1009000d, 0x1009000c, 0x1009000c, 0x1009000b,
+	0x1009000a, 0x1009000a, 0x10090009, 0x10090009,
+	0x10090008, 0x10090008, 0x10090007, 0x10090007,
+	0x10090007, 0x10090006, 0x10090006, 0x10090005,
+	0x10090005, 0x10090005, 0x10090005, 0x10090004,
+	0x10090004, 0x10090004, 0x10090004, 0x10090003,
+	0x10090003, 0x10090003, 0x10090003, 0x10090003,
+	0x10090003, 0x10090002, 0x10090002, 0x10090002,
+	0x10090002, 0x10090002, 0x10090002, 0x10090002,
+	0x10090002, 0x10090002, 0x10090001, 0x10090001,
+	0x10090001, 0x10090001, 0x10090001, 0x10090001,
+};
+
 /**************************************************
  * R/W ops.
  **************************************************/
@@ -674,6 +712,51 @@
 	return;
 }
 
+void b43_httab_write_few(struct b43_wldev *dev, u32 offset, size_t num, ...)
+{
+	va_list args;
+	u32 type, value;
+	unsigned int i;
+
+	type = offset & B43_HTTAB_TYPEMASK;
+	offset &= 0xFFFF;
+
+	va_start(args, num);
+	switch (type) {
+	case B43_HTTAB_8BIT:
+		b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset);
+		for (i = 0; i < num; i++) {
+			value = va_arg(args, int);
+			B43_WARN_ON(value & ~0xFF);
+			b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value);
+		}
+		break;
+	case B43_HTTAB_16BIT:
+		b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset);
+		for (i = 0; i < num; i++) {
+			value = va_arg(args, int);
+			B43_WARN_ON(value & ~0xFFFF);
+			b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO, value);
+		}
+		break;
+	case B43_HTTAB_32BIT:
+		b43_phy_write(dev, B43_PHY_HT_TABLE_ADDR, offset);
+		for (i = 0; i < num; i++) {
+			value = va_arg(args, int);
+			b43_phy_write(dev, B43_PHY_HT_TABLE_DATAHI,
+				      value >> 16);
+			b43_phy_write(dev, B43_PHY_HT_TABLE_DATALO,
+				      value & 0xFFFF);
+		}
+		break;
+	default:
+		B43_WARN_ON(1);
+	}
+	va_end(args);
+
+	return;
+}
+
 void b43_httab_write_bulk(struct b43_wldev *dev, u32 offset,
 			  unsigned int nr_elements, const void *_data)
 {
@@ -723,6 +806,9 @@
 	} while (0)
 void b43_phy_ht_tables_init(struct b43_wldev *dev)
 {
+	BUILD_BUG_ON(ARRAY_SIZE(b43_httab_0x1a_0xc0_late) !=
+			B43_HTTAB_1A_C0_LATE_SIZE);
+
 	httab_upload(dev, B43_HTTAB16(0x12, 0), b43_httab_0x12);
 	httab_upload(dev, B43_HTTAB16(0x27, 0), b43_httab_0x27);
 	httab_upload(dev, B43_HTTAB16(0x26, 0), b43_httab_0x26);
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/tables_phy_ht.h linux-3.1.1/drivers/net/wireless/b43/tables_phy_ht.h
--- linux-3.1.1.orig/drivers/net/wireless/b43/tables_phy_ht.h	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/tables_phy_ht.h	2011-11-13 17:11:58.609558134 -0800
@@ -14,9 +14,13 @@
 void b43_httab_read_bulk(struct b43_wldev *dev, u32 offset,
 			 unsigned int nr_elements, void *_data);
 void b43_httab_write(struct b43_wldev *dev, u32 offset, u32 value);
+void b43_httab_write_few(struct b43_wldev *dev, u32 offset, size_t num, ...);
 void b43_httab_write_bulk(struct b43_wldev *dev, u32 offset,
 			  unsigned int nr_elements, const void *_data);
 
 void b43_phy_ht_tables_init(struct b43_wldev *dev);
 
+#define B43_HTTAB_1A_C0_LATE_SIZE		128
+extern const u32 b43_httab_0x1a_0xc0_late[];
+
 #endif /* B43_TABLES_PHY_HT_H_ */
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/tables_phy_lcn.c linux-3.1.1/drivers/net/wireless/b43/tables_phy_lcn.c
--- linux-3.1.1.orig/drivers/net/wireless/b43/tables_phy_lcn.c	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/tables_phy_lcn.c	2011-11-13 17:11:58.609558134 -0800
@@ -3,6 +3,8 @@
   Broadcom B43 wireless driver
   IEEE 802.11n LCN-PHY data tables
 
+  Copyright (c) 2011 Rafał Miłecki <zajec5@gmail.com>
+
   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
@@ -25,10 +27,698 @@
 #include "phy_common.h"
 #include "phy_lcn.h"
 
+struct b43_lcntab_tx_gain_tbl_entry {
+	u8 gm;
+	u8 pga;
+	u8 pad;
+	u8 dac;
+	u8 bb_mult;
+};
+
+/**************************************************
+ * Static tables.
+ **************************************************/
+
+static const u16 b43_lcntab_0x02[] = {
+	0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d,
+	0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d,
+	0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d,
+	0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d,
+	0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d,
+	0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d,
+	0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d,
+	0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d,
+	0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d,
+	0x014d, 0x014d, 0x014d, 0x014d, 0x014d, 0x014d,
+	0x014d, 0x014d, 0x014d, 0x014d,
+};
+
+static const u16 b43_lcntab_0x01[] = {
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static const u32 b43_lcntab_0x0b[] = {
+	0x000141f8, 0x000021f8, 0x000021fb, 0x000041fb,
+	0x0001fedb, 0x0000217b, 0x00002133, 0x000040eb,
+	0x0001fea3, 0x0000024b,
+};
+
+static const u32 b43_lcntab_0x0c[] = {
+	0x00100001, 0x00200010, 0x00300001, 0x00400010,
+	0x00500022, 0x00600122, 0x00700222, 0x00800322,
+	0x00900422, 0x00a00522, 0x00b00622, 0x00c00722,
+	0x00d00822, 0x00f00922, 0x00100a22, 0x00200b22,
+	0x00300c22, 0x00400d22, 0x00500e22, 0x00600f22,
+};
+
+static const u32 b43_lcntab_0x0d[] = {
+	0x00000000, 0x00000000, 0x10000000, 0x00000000,
+	0x20000000, 0x00000000, 0x30000000, 0x00000000,
+	0x40000000, 0x00000000, 0x50000000, 0x00000000,
+	0x60000000, 0x00000000, 0x70000000, 0x00000000,
+	0x80000000, 0x00000000, 0x90000000, 0x00000008,
+	0xa0000000, 0x00000008, 0xb0000000, 0x00000008,
+	0xc0000000, 0x00000008, 0xd0000000, 0x00000008,
+	0xe0000000, 0x00000008, 0xf0000000, 0x00000008,
+	0x00000000, 0x00000009, 0x10000000, 0x00000009,
+	0x20000000, 0x00000019, 0x30000000, 0x00000019,
+	0x40000000, 0x00000019, 0x50000000, 0x00000019,
+	0x60000000, 0x00000019, 0x70000000, 0x00000019,
+	0x80000000, 0x00000019, 0x90000000, 0x00000019,
+	0xa0000000, 0x00000019, 0xb0000000, 0x00000019,
+	0xc0000000, 0x00000019, 0xd0000000, 0x00000019,
+	0xe0000000, 0x00000019, 0xf0000000, 0x00000019,
+	0x00000000, 0x0000001a, 0x10000000, 0x0000001a,
+	0x20000000, 0x0000001a, 0x30000000, 0x0000001a,
+	0x40000000, 0x0000001a, 0x50000000, 0x00000002,
+	0x60000000, 0x00000002, 0x70000000, 0x00000002,
+	0x80000000, 0x00000002, 0x90000000, 0x00000002,
+	0xa0000000, 0x00000002, 0xb0000000, 0x00000002,
+	0xc0000000, 0x0000000a, 0xd0000000, 0x0000000a,
+	0xe0000000, 0x0000000a, 0xf0000000, 0x0000000a,
+	0x00000000, 0x0000000b, 0x10000000, 0x0000000b,
+	0x20000000, 0x0000000b, 0x30000000, 0x0000000b,
+	0x40000000, 0x0000000b, 0x50000000, 0x0000001b,
+	0x60000000, 0x0000001b, 0x70000000, 0x0000001b,
+	0x80000000, 0x0000001b, 0x90000000, 0x0000001b,
+	0xa0000000, 0x0000001b, 0xb0000000, 0x0000001b,
+	0xc0000000, 0x0000001b, 0xd0000000, 0x0000001b,
+	0xe0000000, 0x0000001b, 0xf0000000, 0x0000001b,
+	0x00000000, 0x0000001c, 0x10000000, 0x0000001c,
+	0x20000000, 0x0000001c, 0x30000000, 0x0000001c,
+	0x40000000, 0x0000001c, 0x50000000, 0x0000001c,
+	0x60000000, 0x0000001c, 0x70000000, 0x0000001c,
+	0x80000000, 0x0000001c, 0x90000000, 0x0000001c,
+};
+
+static const u16 b43_lcntab_0x0e[] = {
+	0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406,
+	0x0407, 0x0408, 0x0409, 0x040a, 0x058b, 0x058c,
+	0x058d, 0x058e, 0x058f, 0x0090, 0x0091, 0x0092,
+	0x0193, 0x0194, 0x0195, 0x0196, 0x0197, 0x0198,
+	0x0199, 0x019a, 0x019b, 0x019c, 0x019d, 0x019e,
+	0x019f, 0x01a0, 0x01a1, 0x01a2, 0x01a3, 0x01a4,
+	0x01a5, 0x0000,
+};
+
+static const u16 b43_lcntab_0x0f[] = {
+	0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009,
+	0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005,
+	0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009,
+	0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005,
+	0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009,
+	0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005,
+	0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009,
+	0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005,
+	0x000a, 0x0009, 0x0006, 0x0005, 0x000a, 0x0009,
+	0x0006, 0x0005, 0x000a, 0x0009, 0x0006, 0x0005,
+	0x000a, 0x0009, 0x0006, 0x0005,
+};
+
+static const u16 b43_lcntab_0x10[] = {
+	0x005f, 0x0036, 0x0029, 0x001f, 0x005f, 0x0036,
+	0x0029, 0x001f, 0x005f, 0x0036, 0x0029, 0x001f,
+	0x005f, 0x0036, 0x0029, 0x001f,
+};
+
+static const u16 b43_lcntab_0x11[] = {
+	0x0009, 0x000f, 0x0014, 0x0018, 0x00fe, 0x0007,
+	0x000b, 0x000f, 0x00fb, 0x00fe, 0x0001, 0x0005,
+	0x0008, 0x000b, 0x000e, 0x0011, 0x0014, 0x0017,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0003, 0x0006, 0x0009, 0x000c, 0x000f,
+	0x0012, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0003,
+	0x0006, 0x0009, 0x000c, 0x000f, 0x0012, 0x0015,
+	0x0018, 0x001b, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0003, 0x00eb, 0x0000, 0x0000,
+};
+
+static const u32 b43_lcntab_0x12[] = {
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000004, 0x00000000, 0x00000004, 0x00000008,
+	0x00000001, 0x00000005, 0x00000009, 0x0000000d,
+	0x0000004d, 0x0000008d, 0x0000000d, 0x0000004d,
+	0x0000008d, 0x000000cd, 0x0000004f, 0x0000008f,
+	0x000000cf, 0x000000d3, 0x00000113, 0x00000513,
+	0x00000913, 0x00000953, 0x00000d53, 0x00001153,
+	0x00001193, 0x00005193, 0x00009193, 0x0000d193,
+	0x00011193, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000004,
+	0x00000000, 0x00000004, 0x00000008, 0x00000001,
+	0x00000005, 0x00000009, 0x0000000d, 0x0000004d,
+	0x0000008d, 0x0000000d, 0x0000004d, 0x0000008d,
+	0x000000cd, 0x0000004f, 0x0000008f, 0x000000cf,
+	0x000000d3, 0x00000113, 0x00000513, 0x00000913,
+	0x00000953, 0x00000d53, 0x00001153, 0x00005153,
+	0x00009153, 0x0000d153, 0x00011153, 0x00015153,
+	0x00019153, 0x0001d153, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+	0x00000000, 0x00000000, 0x00000000, 0x00000000,
+};
+
+static const u16 b43_lcntab_0x14[] = {
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0002, 0x0003, 0x0001, 0x0003, 0x0002, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0003,
+	0x0001, 0x0003, 0x0002, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
+	0x0001, 0x0001,
+};
+
+static const u16 b43_lcntab_0x17[] = {
+	0x001a, 0x0034, 0x004e, 0x0068, 0x009c, 0x00d0,
+	0x00ea, 0x0104, 0x0034, 0x0068, 0x009c, 0x00d0,
+	0x0138, 0x01a0, 0x01d4, 0x0208, 0x004e, 0x009c,
+	0x00ea, 0x0138, 0x01d4, 0x0270, 0x02be, 0x030c,
+	0x0068, 0x00d0, 0x0138, 0x01a0, 0x0270, 0x0340,
+	0x03a8, 0x0410, 0x0018, 0x009c, 0x00d0, 0x0104,
+	0x00ea, 0x0138, 0x0186, 0x00d0, 0x0104, 0x0104,
+	0x0138, 0x016c, 0x016c, 0x01a0, 0x0138, 0x0186,
+	0x0186, 0x01d4, 0x0222, 0x0222, 0x0270, 0x0104,
+	0x0138, 0x016c, 0x0138, 0x016c, 0x01a0, 0x01d4,
+	0x01a0, 0x01d4, 0x0208, 0x0208, 0x023c, 0x0186,
+	0x01d4, 0x0222, 0x01d4, 0x0222, 0x0270, 0x02be,
+	0x0270, 0x02be, 0x030c, 0x030c, 0x035a, 0x0036,
+	0x006c, 0x00a2, 0x00d8, 0x0144, 0x01b0, 0x01e6,
+	0x021c, 0x006c, 0x00d8, 0x0144, 0x01b0, 0x0288,
+	0x0360, 0x03cc, 0x0438, 0x00a2, 0x0144, 0x01e6,
+	0x0288, 0x03cc, 0x0510, 0x05b2, 0x0654, 0x00d8,
+	0x01b0, 0x0288, 0x0360, 0x0510, 0x06c0, 0x0798,
+	0x0870, 0x0018, 0x0144, 0x01b0, 0x021c, 0x01e6,
+	0x0288, 0x032a, 0x01b0, 0x021c, 0x021c, 0x0288,
+	0x02f4, 0x02f4, 0x0360, 0x0288, 0x032a, 0x032a,
+	0x03cc, 0x046e, 0x046e, 0x0510, 0x021c, 0x0288,
+	0x02f4, 0x0288, 0x02f4, 0x0360, 0x03cc, 0x0360,
+	0x03cc, 0x0438, 0x0438, 0x04a4, 0x032a, 0x03cc,
+	0x046e, 0x03cc, 0x046e, 0x0510, 0x05b2, 0x0510,
+	0x05b2, 0x0654, 0x0654, 0x06f6,
+};
+
+static const u16 b43_lcntab_0x00[] = {
+	0x0200, 0x0300, 0x0400, 0x0600, 0x0800, 0x0b00,
+	0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005,
+	0x1006, 0x1007, 0x1707, 0x2007, 0x2d07, 0x4007,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0200, 0x0300, 0x0400, 0x0600,
+	0x0800, 0x0b00, 0x1000, 0x1001, 0x1002, 0x1003,
+	0x1004, 0x1005, 0x1006, 0x1007, 0x1707, 0x2007,
+	0x2d07, 0x4007, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x4000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static const u32 b43_lcntab_0x18[] = {
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+	0x00080000, 0x00080000, 0x00080000, 0x00080000,
+};
+
+/**************************************************
+ * TX gain.
+ **************************************************/
+
+const struct b43_lcntab_tx_gain_tbl_entry
+	b43_lcntab_tx_gain_tbl_2ghz_ext_pa_rev0[B43_LCNTAB_TX_GAIN_SIZE] = {
+	{ 0x03, 0x00, 0x1f, 0x0, 0x48 },
+	{ 0x03, 0x00, 0x1f, 0x0, 0x46 },
+	{ 0x03, 0x00, 0x1f, 0x0, 0x44 },
+	{ 0x03, 0x00, 0x1e, 0x0, 0x43 },
+	{ 0x03, 0x00, 0x1d, 0x0, 0x44 },
+	{ 0x03, 0x00, 0x1c, 0x0, 0x44 },
+	{ 0x03, 0x00, 0x1b, 0x0, 0x45 },
+	{ 0x03, 0x00, 0x1a, 0x0, 0x46 },
+	{ 0x03, 0x00, 0x19, 0x0, 0x46 },
+	{ 0x03, 0x00, 0x18, 0x0, 0x47 },
+	{ 0x03, 0x00, 0x17, 0x0, 0x48 },
+	{ 0x03, 0x00, 0x17, 0x0, 0x46 },
+	{ 0x03, 0x00, 0x16, 0x0, 0x47 },
+	{ 0x03, 0x00, 0x15, 0x0, 0x48 },
+	{ 0x03, 0x00, 0x15, 0x0, 0x46 },
+	{ 0x03, 0x00, 0x15, 0x0, 0x44 },
+	{ 0x03, 0x00, 0x15, 0x0, 0x42 },
+	{ 0x03, 0x00, 0x15, 0x0, 0x40 },
+	{ 0x03, 0x00, 0x15, 0x0, 0x3f },
+	{ 0x03, 0x00, 0x14, 0x0, 0x40 },
+	{ 0x03, 0x00, 0x13, 0x0, 0x41 },
+	{ 0x03, 0x00, 0x13, 0x0, 0x40 },
+	{ 0x03, 0x00, 0x12, 0x0, 0x41 },
+	{ 0x03, 0x00, 0x12, 0x0, 0x40 },
+	{ 0x03, 0x00, 0x11, 0x0, 0x41 },
+	{ 0x03, 0x00, 0x11, 0x0, 0x40 },
+	{ 0x03, 0x00, 0x10, 0x0, 0x41 },
+	{ 0x03, 0x00, 0x10, 0x0, 0x40 },
+	{ 0x03, 0x00, 0x10, 0x0, 0x3e },
+	{ 0x03, 0x00, 0x10, 0x0, 0x3c },
+	{ 0x03, 0x00, 0x10, 0x0, 0x3a },
+	{ 0x03, 0x00, 0x0f, 0x0, 0x3d },
+	{ 0x03, 0x00, 0x0f, 0x0, 0x3b },
+	{ 0x03, 0x00, 0x0e, 0x0, 0x3d },
+	{ 0x03, 0x00, 0x0e, 0x0, 0x3c },
+	{ 0x03, 0x00, 0x0e, 0x0, 0x3a },
+	{ 0x03, 0x00, 0x0d, 0x0, 0x3c },
+	{ 0x03, 0x00, 0x0d, 0x0, 0x3b },
+	{ 0x03, 0x00, 0x0c, 0x0, 0x3e },
+	{ 0x03, 0x00, 0x0c, 0x0, 0x3c },
+	{ 0x03, 0x00, 0x0c, 0x0, 0x3a },
+	{ 0x03, 0x00, 0x0b, 0x0, 0x3e },
+	{ 0x03, 0x00, 0x0b, 0x0, 0x3c },
+	{ 0x03, 0x00, 0x0b, 0x0, 0x3b },
+	{ 0x03, 0x00, 0x0b, 0x0, 0x39 },
+	{ 0x03, 0x00, 0x0a, 0x0, 0x3d },
+	{ 0x03, 0x00, 0x0a, 0x0, 0x3b },
+	{ 0x03, 0x00, 0x0a, 0x0, 0x39 },
+	{ 0x03, 0x00, 0x09, 0x0, 0x3e },
+	{ 0x03, 0x00, 0x09, 0x0, 0x3c },
+	{ 0x03, 0x00, 0x09, 0x0, 0x3a },
+	{ 0x03, 0x00, 0x09, 0x0, 0x39 },
+	{ 0x03, 0x00, 0x08, 0x0, 0x3e },
+	{ 0x03, 0x00, 0x08, 0x0, 0x3c },
+	{ 0x03, 0x00, 0x08, 0x0, 0x3a },
+	{ 0x03, 0x00, 0x08, 0x0, 0x39 },
+	{ 0x03, 0x00, 0x08, 0x0, 0x37 },
+	{ 0x03, 0x00, 0x07, 0x0, 0x3d },
+	{ 0x03, 0x00, 0x07, 0x0, 0x3c },
+	{ 0x03, 0x00, 0x07, 0x0, 0x3a },
+	{ 0x03, 0x00, 0x07, 0x0, 0x38 },
+	{ 0x03, 0x00, 0x07, 0x0, 0x37 },
+	{ 0x03, 0x00, 0x06, 0x0, 0x3e },
+	{ 0x03, 0x00, 0x06, 0x0, 0x3c },
+	{ 0x03, 0x00, 0x06, 0x0, 0x3a },
+	{ 0x03, 0x00, 0x06, 0x0, 0x39 },
+	{ 0x03, 0x00, 0x06, 0x0, 0x37 },
+	{ 0x03, 0x00, 0x06, 0x0, 0x36 },
+	{ 0x03, 0x00, 0x06, 0x0, 0x34 },
+	{ 0x03, 0x00, 0x05, 0x0, 0x3d },
+	{ 0x03, 0x00, 0x05, 0x0, 0x3b },
+	{ 0x03, 0x00, 0x05, 0x0, 0x39 },
+	{ 0x03, 0x00, 0x05, 0x0, 0x38 },
+	{ 0x03, 0x00, 0x05, 0x0, 0x36 },
+	{ 0x03, 0x00, 0x05, 0x0, 0x35 },
+	{ 0x03, 0x00, 0x05, 0x0, 0x33 },
+	{ 0x03, 0x00, 0x04, 0x0, 0x3e },
+	{ 0x03, 0x00, 0x04, 0x0, 0x3c },
+	{ 0x03, 0x00, 0x04, 0x0, 0x3a },
+	{ 0x03, 0x00, 0x04, 0x0, 0x39 },
+	{ 0x03, 0x00, 0x04, 0x0, 0x37 },
+	{ 0x03, 0x00, 0x04, 0x0, 0x36 },
+	{ 0x03, 0x00, 0x04, 0x0, 0x34 },
+	{ 0x03, 0x00, 0x04, 0x0, 0x33 },
+	{ 0x03, 0x00, 0x04, 0x0, 0x31 },
+	{ 0x03, 0x00, 0x04, 0x0, 0x30 },
+	{ 0x03, 0x00, 0x04, 0x0, 0x2e },
+	{ 0x03, 0x00, 0x03, 0x0, 0x3c },
+	{ 0x03, 0x00, 0x03, 0x0, 0x3a },
+	{ 0x03, 0x00, 0x03, 0x0, 0x39 },
+	{ 0x03, 0x00, 0x03, 0x0, 0x37 },
+	{ 0x03, 0x00, 0x03, 0x0, 0x36 },
+	{ 0x03, 0x00, 0x03, 0x0, 0x34 },
+	{ 0x03, 0x00, 0x03, 0x0, 0x33 },
+	{ 0x03, 0x00, 0x03, 0x0, 0x31 },
+	{ 0x03, 0x00, 0x03, 0x0, 0x30 },
+	{ 0x03, 0x00, 0x03, 0x0, 0x2e },
+	{ 0x03, 0x00, 0x03, 0x0, 0x2d },
+	{ 0x03, 0x00, 0x03, 0x0, 0x2c },
+	{ 0x03, 0x00, 0x03, 0x0, 0x2b },
+	{ 0x03, 0x00, 0x03, 0x0, 0x29 },
+	{ 0x03, 0x00, 0x02, 0x0, 0x3d },
+	{ 0x03, 0x00, 0x02, 0x0, 0x3b },
+	{ 0x03, 0x00, 0x02, 0x0, 0x39 },
+	{ 0x03, 0x00, 0x02, 0x0, 0x38 },
+	{ 0x03, 0x00, 0x02, 0x0, 0x36 },
+	{ 0x03, 0x00, 0x02, 0x0, 0x35 },
+	{ 0x03, 0x00, 0x02, 0x0, 0x33 },
+	{ 0x03, 0x00, 0x02, 0x0, 0x32 },
+	{ 0x03, 0x00, 0x02, 0x0, 0x30 },
+	{ 0x03, 0x00, 0x02, 0x0, 0x2f },
+	{ 0x03, 0x00, 0x02, 0x0, 0x2e },
+	{ 0x03, 0x00, 0x02, 0x0, 0x2c },
+	{ 0x03, 0x00, 0x02, 0x0, 0x2b },
+	{ 0x03, 0x00, 0x02, 0x0, 0x2a },
+	{ 0x03, 0x00, 0x02, 0x0, 0x29 },
+	{ 0x03, 0x00, 0x02, 0x0, 0x27 },
+	{ 0x03, 0x00, 0x02, 0x0, 0x26 },
+	{ 0x03, 0x00, 0x02, 0x0, 0x25 },
+	{ 0x03, 0x00, 0x02, 0x0, 0x24 },
+	{ 0x03, 0x00, 0x02, 0x0, 0x23 },
+	{ 0x03, 0x00, 0x02, 0x0, 0x22 },
+	{ 0x03, 0x00, 0x02, 0x0, 0x21 },
+	{ 0x03, 0x00, 0x02, 0x0, 0x20 },
+	{ 0x03, 0x00, 0x01, 0x0, 0x3f },
+	{ 0x03, 0x00, 0x01, 0x0, 0x3d },
+	{ 0x03, 0x00, 0x01, 0x0, 0x3b },
+	{ 0x03, 0x00, 0x01, 0x0, 0x39 },
+};
+
+/**************************************************
+ * SW control.
+ **************************************************/
+
+const u16 b43_lcntab_sw_ctl_4313_epa_rev0[] = {
+	0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008,
+	0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001,
+	0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008,
+	0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001,
+	0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008,
+	0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001,
+	0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008,
+	0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001,
+	0x0002, 0x0008, 0x0004, 0x0001, 0x0002, 0x0008,
+	0x0004, 0x0001, 0x0002, 0x0008, 0x0004, 0x0001,
+	0x0002, 0x0008, 0x0004, 0x0001,
+};
+
+/**************************************************
+ * R/W ops.
+ **************************************************/
+
+u32 b43_lcntab_read(struct b43_wldev *dev, u32 offset)
+{
+	u32 type, value;
+
+	type = offset & B43_LCNTAB_TYPEMASK;
+	offset &= ~B43_LCNTAB_TYPEMASK;
+	B43_WARN_ON(offset > 0xFFFF);
+
+	switch (type) {
+	case B43_LCNTAB_8BIT:
+		b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset);
+		value = b43_phy_read(dev, B43_PHY_LCN_TABLE_DATALO) & 0xFF;
+		break;
+	case B43_LCNTAB_16BIT:
+		b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset);
+		value = b43_phy_read(dev, B43_PHY_LCN_TABLE_DATALO);
+		break;
+	case B43_LCNTAB_32BIT:
+		b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset);
+		value = b43_phy_read(dev, B43_PHY_LCN_TABLE_DATALO);
+		value |= (b43_phy_read(dev, B43_PHY_LCN_TABLE_DATAHI) << 16);
+		break;
+	default:
+		B43_WARN_ON(1);
+		value = 0;
+	}
+
+	return value;
+}
+
+void b43_lcntab_read_bulk(struct b43_wldev *dev, u32 offset,
+			  unsigned int nr_elements, void *_data)
+{
+	u32 type;
+	u8 *data = _data;
+	unsigned int i;
+
+	type = offset & B43_LCNTAB_TYPEMASK;
+	offset &= ~B43_LCNTAB_TYPEMASK;
+	B43_WARN_ON(offset > 0xFFFF);
+
+	b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset);
+
+	for (i = 0; i < nr_elements; i++) {
+		switch (type) {
+		case B43_LCNTAB_8BIT:
+			*data = b43_phy_read(dev,
+					     B43_PHY_LCN_TABLE_DATALO) & 0xFF;
+			data++;
+			break;
+		case B43_LCNTAB_16BIT:
+			*((u16 *)data) = b43_phy_read(dev,
+						      B43_PHY_LCN_TABLE_DATALO);
+			data += 2;
+			break;
+		case B43_LCNTAB_32BIT:
+			*((u32 *)data) = b43_phy_read(dev,
+						B43_PHY_LCN_TABLE_DATALO);
+			*((u32 *)data) |= (b43_phy_read(dev,
+					   B43_PHY_LCN_TABLE_DATAHI) << 16);
+			data += 4;
+			break;
+		default:
+			B43_WARN_ON(1);
+		}
+	}
+}
+
+void b43_lcntab_write(struct b43_wldev *dev, u32 offset, u32 value)
+{
+	u32 type;
+
+	type = offset & B43_LCNTAB_TYPEMASK;
+	offset &= 0xFFFF;
+
+	switch (type) {
+	case B43_LCNTAB_8BIT:
+		B43_WARN_ON(value & ~0xFF);
+		b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset);
+		b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value);
+		break;
+	case B43_LCNTAB_16BIT:
+		B43_WARN_ON(value & ~0xFFFF);
+		b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset);
+		b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value);
+		break;
+	case B43_LCNTAB_32BIT:
+		b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset);
+		b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI, value >> 16);
+		b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value & 0xFFFF);
+		break;
+	default:
+		B43_WARN_ON(1);
+	}
+
+	return;
+}
+
+void b43_lcntab_write_bulk(struct b43_wldev *dev, u32 offset,
+			   unsigned int nr_elements, const void *_data)
+{
+	u32 type, value;
+	const u8 *data = _data;
+	unsigned int i;
+
+	type = offset & B43_LCNTAB_TYPEMASK;
+	offset &= ~B43_LCNTAB_TYPEMASK;
+	B43_WARN_ON(offset > 0xFFFF);
+
+	b43_phy_write(dev, B43_PHY_LCN_TABLE_ADDR, offset);
+
+	for (i = 0; i < nr_elements; i++) {
+		switch (type) {
+		case B43_LCNTAB_8BIT:
+			value = *data;
+			data++;
+			B43_WARN_ON(value & ~0xFF);
+			b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value);
+			break;
+		case B43_LCNTAB_16BIT:
+			value = *((u16 *)data);
+			data += 2;
+			B43_WARN_ON(value & ~0xFFFF);
+			b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO, value);
+			break;
+		case B43_LCNTAB_32BIT:
+			value = *((u32 *)data);
+			data += 4;
+			b43_phy_write(dev, B43_PHY_LCN_TABLE_DATAHI,
+				      value >> 16);
+			b43_phy_write(dev, B43_PHY_LCN_TABLE_DATALO,
+				      value & 0xFFFF);
+			break;
+		default:
+			B43_WARN_ON(1);
+		}
+	}
+}
+
 /**************************************************
  * Tables ops.
  **************************************************/
 
+#define lcntab_upload(dev, offset, data) do { \
+		b43_lcntab_write_bulk(dev, offset, ARRAY_SIZE(data), data); \
+	} while (0)
+static void b43_phy_lcn_upload_static_tables(struct b43_wldev *dev)
+{
+	lcntab_upload(dev, B43_LCNTAB16(0x02, 0), b43_lcntab_0x02);
+	lcntab_upload(dev, B43_LCNTAB16(0x01, 0), b43_lcntab_0x01);
+	lcntab_upload(dev, B43_LCNTAB32(0x0b, 0), b43_lcntab_0x0b);
+	lcntab_upload(dev, B43_LCNTAB32(0x0c, 0), b43_lcntab_0x0c);
+	lcntab_upload(dev, B43_LCNTAB32(0x0d, 0), b43_lcntab_0x0d);
+	lcntab_upload(dev, B43_LCNTAB16(0x0e, 0), b43_lcntab_0x0e);
+	lcntab_upload(dev, B43_LCNTAB16(0x0f, 0), b43_lcntab_0x0f);
+	lcntab_upload(dev, B43_LCNTAB16(0x10, 0), b43_lcntab_0x10);
+	lcntab_upload(dev, B43_LCNTAB16(0x11, 0), b43_lcntab_0x11);
+	lcntab_upload(dev, B43_LCNTAB32(0x12, 0), b43_lcntab_0x12);
+	lcntab_upload(dev, B43_LCNTAB16(0x14, 0), b43_lcntab_0x14);
+	lcntab_upload(dev, B43_LCNTAB16(0x17, 0), b43_lcntab_0x17);
+	lcntab_upload(dev, B43_LCNTAB16(0x00, 0), b43_lcntab_0x00);
+	lcntab_upload(dev, B43_LCNTAB32(0x18, 0), b43_lcntab_0x18);
+}
+
+void b43_phy_lcn_load_tx_gain_tab(struct b43_wldev *dev,
+			const struct b43_lcntab_tx_gain_tbl_entry *gain_table)
+{
+	u32 i;
+	u32 val;
+
+	u16 pa_gain = 0x70;
+	if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_FEM)
+		pa_gain = 0x10;
+
+	for (i = 0; i < B43_LCNTAB_TX_GAIN_SIZE; i++) {
+		val = ((pa_gain << 24) |
+		       (gain_table[i].pad << 16) |
+		       (gain_table[i].pga << 8) |
+			gain_table[i].gm);
+		b43_lcntab_write(dev, B43_LCNTAB32(0x7, 0xc0 + i), val);
+
+		/* brcmsmac doesn't maskset, we follow newer wl here */
+		val = b43_lcntab_read(dev, B43_LCNTAB32(0x7, 0x140 + i));
+		val &= 0x000fffff;
+		val |= ((gain_table[i].dac << 28) |
+			(gain_table[i].bb_mult << 20));
+		b43_lcntab_write(dev, B43_LCNTAB32(0x7, 0x140 + i), val);
+	}
+}
+
+/* wlc_lcnphy_load_rfpower */
+static void b43_phy_lcn_load_rfpower(struct b43_wldev *dev)
+{
+	u32 bbmult, rfgain;
+	u8 i;
+
+	for (i = 0; i < 128; i++) {
+		bbmult = b43_lcntab_read(dev, B43_LCNTAB32(0x7, 0x140 + i));
+		bbmult >>= 20;
+		rfgain = b43_lcntab_read(dev, B43_LCNTAB32(0x7, 0xc0 + i));
+
+		/* TODO: calculate value for 0x240 + i table offset
+		 * b43_lcntab_write(dev, B43_LCNTAB32(0x7, 0x240 + i), val);
+		 */
+	}
+}
+
+/* Not implemented in brcmsmac, noticed in wl in MMIO dump */
+static void b43_phy_lcn_rewrite_rfpower_table(struct b43_wldev *dev)
+{
+	int i;
+	u32 tmp;
+	for (i = 0; i < 128; i++) {
+		tmp = b43_lcntab_read(dev, B43_LCNTAB32(0x7, 0x240 + i));
+		b43_lcntab_write(dev, B43_LCNTAB32(0x7, 0x240 + i), tmp);
+	}
+}
+
+/* wlc_lcnphy_clear_papd_comptable */
+static void b43_phy_lcn_clean_papd_comp_table(struct b43_wldev *dev)
+{
+	u8 i;
+
+	for (i = 0; i < 0x80; i++)
+		b43_lcntab_write(dev, B43_LCNTAB32(0x18, i), 0x80000);
+}
+
+/* wlc_lcnphy_tbl_init */
 void b43_phy_lcn_tables_init(struct b43_wldev *dev)
 {
+	struct ssb_sprom *sprom = dev->dev->bus_sprom;
+
+	b43_phy_lcn_upload_static_tables(dev);
+
+	if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+		if (sprom->boardflags_lo & B43_BFL_FEM)
+			b43_phy_lcn_load_tx_gain_tab(dev,
+				b43_lcntab_tx_gain_tbl_2ghz_ext_pa_rev0);
+		else
+			b43err(dev->wl,
+			       "TX gain table unknown for this card\n");
+	}
+
+	if (sprom->boardflags_lo & B43_BFL_FEM &&
+	    !(sprom->boardflags_hi & B43_BFH_FEM_BT))
+		b43_lcntab_write_bulk(dev, B43_LCNTAB16(0xf, 0),
+			ARRAY_SIZE(b43_lcntab_sw_ctl_4313_epa_rev0),
+			b43_lcntab_sw_ctl_4313_epa_rev0);
+	else
+		b43err(dev->wl, "SW ctl table is unknown for this card\n");
+
+	b43_phy_lcn_load_rfpower(dev);
+	b43_phy_lcn_rewrite_rfpower_table(dev);
+	b43_phy_lcn_clean_papd_comp_table(dev);
 }
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/tables_phy_lcn.h linux-3.1.1/drivers/net/wireless/b43/tables_phy_lcn.h
--- linux-3.1.1.orig/drivers/net/wireless/b43/tables_phy_lcn.h	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/tables_phy_lcn.h	2011-11-13 17:11:58.609558134 -0800
@@ -1,6 +1,24 @@
 #ifndef B43_TABLES_PHY_LCN_H_
 #define B43_TABLES_PHY_LCN_H_
 
+/* The LCN-PHY tables. */
+#define B43_LCNTAB_TYPEMASK		0xF0000000
+#define B43_LCNTAB_8BIT			0x10000000
+#define B43_LCNTAB_16BIT		0x20000000
+#define B43_LCNTAB_32BIT		0x30000000
+#define B43_LCNTAB8(table, offset)	(((table) << 10) | (offset) | B43_LCNTAB_8BIT)
+#define B43_LCNTAB16(table, offset)	(((table) << 10) | (offset) | B43_LCNTAB_16BIT)
+#define B43_LCNTAB32(table, offset)	(((table) << 10) | (offset) | B43_LCNTAB_32BIT)
+
+#define B43_LCNTAB_TX_GAIN_SIZE		128
+
+u32 b43_lcntab_read(struct b43_wldev *dev, u32 offset);
+void b43_lcntab_read_bulk(struct b43_wldev *dev, u32 offset,
+			  unsigned int nr_elements, void *_data);
+void b43_lcntab_write(struct b43_wldev *dev, u32 offset, u32 value);
+void b43_lcntab_write_bulk(struct b43_wldev *dev, u32 offset,
+			   unsigned int nr_elements, const void *_data);
+
 void b43_phy_lcn_tables_init(struct b43_wldev *dev);
 
 #endif /* B43_TABLES_PHY_LCN_H_ */
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/xmit.c linux-3.1.1/drivers/net/wireless/b43/xmit.c
--- linux-3.1.1.orig/drivers/net/wireless/b43/xmit.c	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/xmit.c	2011-11-13 17:11:58.609558134 -0800
@@ -175,6 +175,7 @@
 	}
 }
 
+/* TODO: verify if needed for SSLPN or LCN  */
 static u16 b43_generate_tx_phy_ctl1(struct b43_wldev *dev, u8 bitrate)
 {
 	const struct b43_phy *phy = &dev->phy;
@@ -256,6 +257,9 @@
 	unsigned int plcp_fragment_len;
 	u32 mac_ctl = 0;
 	u16 phy_ctl = 0;
+	bool fill_phy_ctl1 = (phy->type == B43_PHYTYPE_LP ||
+			      phy->type == B43_PHYTYPE_N ||
+			      phy->type == B43_PHYTYPE_HT);
 	u8 extra_ft = 0;
 	struct ieee80211_rate *txrate;
 	struct ieee80211_tx_rate *rates;
@@ -337,12 +341,19 @@
 			memcpy(txhdr->iv, ((u8 *) wlhdr) + wlhdr_len, iv_len);
 		}
 	}
-	if (b43_is_old_txhdr_format(dev)) {
-		b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->old_format.plcp),
+	switch (dev->fw.hdr_format) {
+	case B43_FW_HDR_598:
+		b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->format_598.plcp),
 				      plcp_fragment_len, rate);
-	} else {
-		b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->new_format.plcp),
+		break;
+	case B43_FW_HDR_351:
+		b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->format_351.plcp),
+				      plcp_fragment_len, rate);
+		break;
+	case B43_FW_HDR_410:
+		b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->format_410.plcp),
 				      plcp_fragment_len, rate);
+		break;
 	}
 	b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->plcp_fb),
 			      plcp_fragment_len, rate_fb);
@@ -415,10 +426,10 @@
 	if ((rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) ||
 	    (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)) {
 		unsigned int len;
-		struct ieee80211_hdr *hdr;
+		struct ieee80211_hdr *uninitialized_var(hdr);
 		int rts_rate, rts_rate_fb;
 		int rts_rate_ofdm, rts_rate_fb_ofdm;
-		struct b43_plcp_hdr6 *plcp;
+		struct b43_plcp_hdr6 *uninitialized_var(plcp);
 		struct ieee80211_rate *rts_cts_rate;
 
 		rts_cts_rate = ieee80211_get_rts_cts_rate(dev->wl->hw, info);
@@ -429,14 +440,21 @@
 		rts_rate_fb_ofdm = b43_is_ofdm_rate(rts_rate_fb);
 
 		if (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
-			struct ieee80211_cts *cts;
+			struct ieee80211_cts *uninitialized_var(cts);
 
-			if (b43_is_old_txhdr_format(dev)) {
+			switch (dev->fw.hdr_format) {
+			case B43_FW_HDR_598:
 				cts = (struct ieee80211_cts *)
-					(txhdr->old_format.rts_frame);
-			} else {
+					(txhdr->format_598.rts_frame);
+				break;
+			case B43_FW_HDR_351:
 				cts = (struct ieee80211_cts *)
-					(txhdr->new_format.rts_frame);
+					(txhdr->format_351.rts_frame);
+				break;
+			case B43_FW_HDR_410:
+				cts = (struct ieee80211_cts *)
+					(txhdr->format_410.rts_frame);
+				break;
 			}
 			ieee80211_ctstoself_get(dev->wl->hw, info->control.vif,
 						fragment_data, fragment_len,
@@ -444,14 +462,21 @@
 			mac_ctl |= B43_TXH_MAC_SENDCTS;
 			len = sizeof(struct ieee80211_cts);
 		} else {
-			struct ieee80211_rts *rts;
+			struct ieee80211_rts *uninitialized_var(rts);
 
-			if (b43_is_old_txhdr_format(dev)) {
+			switch (dev->fw.hdr_format) {
+			case B43_FW_HDR_598:
 				rts = (struct ieee80211_rts *)
-					(txhdr->old_format.rts_frame);
-			} else {
+					(txhdr->format_598.rts_frame);
+				break;
+			case B43_FW_HDR_351:
 				rts = (struct ieee80211_rts *)
-					(txhdr->new_format.rts_frame);
+					(txhdr->format_351.rts_frame);
+				break;
+			case B43_FW_HDR_410:
+				rts = (struct ieee80211_rts *)
+					(txhdr->format_410.rts_frame);
+				break;
 			}
 			ieee80211_rts_get(dev->wl->hw, info->control.vif,
 					  fragment_data, fragment_len,
@@ -462,22 +487,36 @@
 		len += FCS_LEN;
 
 		/* Generate the PLCP headers for the RTS/CTS frame */
-		if (b43_is_old_txhdr_format(dev))
-			plcp = &txhdr->old_format.rts_plcp;
-		else
-			plcp = &txhdr->new_format.rts_plcp;
+		switch (dev->fw.hdr_format) {
+		case B43_FW_HDR_598:
+			plcp = &txhdr->format_598.rts_plcp;
+			break;
+		case B43_FW_HDR_351:
+			plcp = &txhdr->format_351.rts_plcp;
+			break;
+		case B43_FW_HDR_410:
+			plcp = &txhdr->format_410.rts_plcp;
+			break;
+		}
 		b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)plcp,
 				      len, rts_rate);
 		plcp = &txhdr->rts_plcp_fb;
 		b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)plcp,
 				      len, rts_rate_fb);
 
-		if (b43_is_old_txhdr_format(dev)) {
+		switch (dev->fw.hdr_format) {
+		case B43_FW_HDR_598:
 			hdr = (struct ieee80211_hdr *)
-				(&txhdr->old_format.rts_frame);
-		} else {
+				(&txhdr->format_598.rts_frame);
+			break;
+		case B43_FW_HDR_351:
 			hdr = (struct ieee80211_hdr *)
-				(&txhdr->new_format.rts_frame);
+				(&txhdr->format_351.rts_frame);
+			break;
+		case B43_FW_HDR_410:
+			hdr = (struct ieee80211_hdr *)
+				(&txhdr->format_410.rts_frame);
+			break;
 		}
 		txhdr->rts_dur_fb = hdr->duration_id;
 
@@ -496,7 +535,7 @@
 			extra_ft |= B43_TXH_EFT_RTSFB_CCK;
 
 		if (rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS &&
-		    phy->type == B43_PHYTYPE_N) {
+		    fill_phy_ctl1) {
 			txhdr->phy_ctl1_rts = cpu_to_le16(
 				b43_generate_tx_phy_ctl1(dev, rts_rate));
 			txhdr->phy_ctl1_rts_fb = cpu_to_le16(
@@ -505,12 +544,19 @@
 	}
 
 	/* Magic cookie */
-	if (b43_is_old_txhdr_format(dev))
-		txhdr->old_format.cookie = cpu_to_le16(cookie);
-	else
-		txhdr->new_format.cookie = cpu_to_le16(cookie);
+	switch (dev->fw.hdr_format) {
+	case B43_FW_HDR_598:
+		txhdr->format_598.cookie = cpu_to_le16(cookie);
+		break;
+	case B43_FW_HDR_351:
+		txhdr->format_351.cookie = cpu_to_le16(cookie);
+		break;
+	case B43_FW_HDR_410:
+		txhdr->format_410.cookie = cpu_to_le16(cookie);
+		break;
+	}
 
-	if (phy->type == B43_PHYTYPE_N) {
+	if (fill_phy_ctl1) {
 		txhdr->phy_ctl1 =
 			cpu_to_le16(b43_generate_tx_phy_ctl1(dev, rate));
 		txhdr->phy_ctl1_fb =
@@ -611,8 +657,9 @@
 	struct ieee80211_hdr *wlhdr;
 	const struct b43_rxhdr_fw4 *rxhdr = _rxhdr;
 	__le16 fctl;
-	u16 phystat0, phystat3, chanstat, mactime;
-	u32 macstat;
+	u16 phystat0, phystat3;
+	u16 uninitialized_var(chanstat), uninitialized_var(mactime);
+	u32 uninitialized_var(macstat);
 	u16 chanid;
 	u16 phytype;
 	int padding;
@@ -622,9 +669,19 @@
 	/* Get metadata about the frame from the header. */
 	phystat0 = le16_to_cpu(rxhdr->phy_status0);
 	phystat3 = le16_to_cpu(rxhdr->phy_status3);
-	macstat = le32_to_cpu(rxhdr->mac_status);
-	mactime = le16_to_cpu(rxhdr->mac_time);
-	chanstat = le16_to_cpu(rxhdr->channel);
+	switch (dev->fw.hdr_format) {
+	case B43_FW_HDR_598:
+		macstat = le32_to_cpu(rxhdr->format_598.mac_status);
+		mactime = le16_to_cpu(rxhdr->format_598.mac_time);
+		chanstat = le16_to_cpu(rxhdr->format_598.channel);
+		break;
+	case B43_FW_HDR_410:
+	case B43_FW_HDR_351:
+		macstat = le32_to_cpu(rxhdr->format_351.mac_status);
+		mactime = le16_to_cpu(rxhdr->format_351.mac_time);
+		chanstat = le16_to_cpu(rxhdr->format_351.channel);
+		break;
+	}
 	phytype = chanstat & B43_RX_CHAN_PHYTYPE;
 
 	if (unlikely(macstat & B43_RX_MAC_FCSERR)) {
@@ -682,16 +739,29 @@
 	}
 
 	/* Link quality statistics */
-	if ((chanstat & B43_RX_CHAN_PHYTYPE) == B43_PHYTYPE_N) {
-//		s8 rssi = max(rxhdr->power0, rxhdr->power1);
-		//TODO: Find out what the rssi value is (dBm or percentage?)
-		//      and also find out what the maximum possible value is.
-		//      Fill status.ssi and status.signal fields.
-	} else {
+	switch (chanstat & B43_RX_CHAN_PHYTYPE) {
+	case B43_PHYTYPE_HT:
+		/* TODO: is max the right choice? */
+		status.signal = max_t(__s8,
+			max(rxhdr->phy_ht_power0, rxhdr->phy_ht_power1),
+			rxhdr->phy_ht_power2);
+		break;
+	case B43_PHYTYPE_N:
+		/* Broadcom has code for min and avg, but always uses max */
+		if (rxhdr->power0 == 16 || rxhdr->power0 == 32)
+			status.signal = max(rxhdr->power1, rxhdr->power2);
+		else
+			status.signal = max(rxhdr->power0, rxhdr->power1);
+		break;
+	case B43_PHYTYPE_A:
+	case B43_PHYTYPE_B:
+	case B43_PHYTYPE_G:
+	case B43_PHYTYPE_LP:
 		status.signal = b43_rssi_postprocess(dev, rxhdr->jssi,
 						  (phystat0 & B43_RX_PHYST0_OFDM),
 						  (phystat0 & B43_RX_PHYST0_GAINCTL),
 						  (phystat3 & B43_RX_PHYST3_TRSTATE));
+		break;
 	}
 
 	if (phystat0 & B43_RX_PHYST0_OFDM)
@@ -744,6 +814,7 @@
 		break;
 	case B43_PHYTYPE_N:
 	case B43_PHYTYPE_LP:
+	case B43_PHYTYPE_HT:
 		/* chanid is the SHM channel cookie. Which is the plain
 		 * channel number in b43. */
 		if (chanstat & B43_RX_CHAN_5GHZ) {
diff -ruN linux-3.1.1.orig/drivers/net/wireless/b43/xmit.h linux-3.1.1/drivers/net/wireless/b43/xmit.h
--- linux-3.1.1.orig/drivers/net/wireless/b43/xmit.h	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/drivers/net/wireless/b43/xmit.h	2011-11-13 17:11:58.609558134 -0800
@@ -46,7 +46,24 @@
 	__le32 timeout;			/* Timeout */
 
 	union {
-		/* The new r410 format. */
+		/* Tested with 598.314, 644.1001 and 666.2 */
+		struct {
+			__le16 mimo_antenna;            /* MIMO antenna select */
+			__le16 preload_size;            /* Preload size */
+			PAD_BYTES(2);
+			__le16 cookie;                  /* TX frame cookie */
+			__le16 tx_status;               /* TX status */
+			__le16 max_n_mpdus;
+			__le16 max_a_bytes_mrt;
+			__le16 max_a_bytes_fbr;
+			__le16 min_m_bytes;
+			struct b43_plcp_hdr6 rts_plcp;  /* RTS PLCP header */
+			__u8 rts_frame[16];             /* The RTS frame (if used) */
+			PAD_BYTES(2);
+			struct b43_plcp_hdr6 plcp;      /* Main PLCP header */
+		} format_598 __packed;
+
+		/* Tested with 410.2160, 478.104 and 508.* */
 		struct {
 			__le16 mimo_antenna;		/* MIMO antenna select */
 			__le16 preload_size;		/* Preload size */
@@ -57,9 +74,9 @@
 			__u8 rts_frame[16];		/* The RTS frame (if used) */
 			PAD_BYTES(2);
 			struct b43_plcp_hdr6 plcp;	/* Main PLCP header */
-		} new_format __packed;
+		} format_410 __packed;
 
-		/* The old r351 format. */
+		/* Tested with 351.126 */
 		struct {
 			PAD_BYTES(2);
 			__le16 cookie;			/* TX frame cookie */
@@ -68,7 +85,7 @@
 			__u8 rts_frame[16];		/* The RTS frame (if used) */
 			PAD_BYTES(2);
 			struct b43_plcp_hdr6 plcp;	/* Main PLCP header */
-		} old_format __packed;
+		} format_351 __packed;
 
 	} __packed;
 } __packed;
@@ -166,19 +183,18 @@
 #define  B43_TXH_PHY1_MODUL_QAM256	0x2000 /* QAM256 */
 
 
-/* r351 firmware compatibility stuff. */
-static inline
-bool b43_is_old_txhdr_format(struct b43_wldev *dev)
-{
-	return (dev->fw.rev <= 351);
-}
-
 static inline
 size_t b43_txhdr_size(struct b43_wldev *dev)
 {
-	if (b43_is_old_txhdr_format(dev))
+	switch (dev->fw.hdr_format) {
+	case B43_FW_HDR_598:
+		return 112 + sizeof(struct b43_plcp_hdr6);
+	case B43_FW_HDR_410:
+		return 104 + sizeof(struct b43_plcp_hdr6);
+	case B43_FW_HDR_351:
 		return 100 + sizeof(struct b43_plcp_hdr6);
-	return 104 + sizeof(struct b43_plcp_hdr6);
+	}
+	return 0;
 }
 
 
@@ -232,11 +248,47 @@
 			__s8 power1;	/* PHY RX Status 1: Power 1 */
 		} __packed;
 	} __packed;
-	__le16 phy_status2;	/* PHY RX Status 2 */
-	__le16 phy_status3;	/* PHY RX Status 3 */
-	__le32 mac_status;	/* MAC RX status */
-	__le16 mac_time;
-	__le16 channel;
+	union {
+		/* HT-PHY */
+		struct {
+			PAD_BYTES(1);
+			__s8 phy_ht_power0;
+		} __packed;
+
+		/* RSSI for N-PHYs */
+		struct {
+			__s8 power2;
+			PAD_BYTES(1);
+		} __packed;
+
+		__le16 phy_status2;	/* PHY RX Status 2 */
+	} __packed;
+	union {
+		/* HT-PHY */
+		struct {
+			__s8 phy_ht_power1;
+			__s8 phy_ht_power2;
+		} __packed;
+
+		__le16 phy_status3;	/* PHY RX Status 3 */
+	} __packed;
+	union {
+		/* Tested with 598.314, 644.1001 and 666.2 */
+		struct {
+			__le16 phy_status4;	/* PHY RX Status 4 */
+			__le16 phy_status5;	/* PHY RX Status 5 */
+			__le32 mac_status;	/* MAC RX status */
+			__le16 mac_time;
+			__le16 channel;
+		} format_598 __packed;
+
+		/* Tested with 351.126, 410.2160, 478.104 and 508.* */
+		struct {
+			__le32 mac_status;	/* MAC RX status */
+			__le16 mac_time;
+			__le16 channel;
+		} format_351 __packed;
+	} __packed;
 } __packed;
 
 /* PHY RX Status 0 */
diff -ruN linux-3.1.1.orig/include/linux/bcma/bcma.h linux-3.1.1/include/linux/bcma/bcma.h
--- linux-3.1.1.orig/include/linux/bcma/bcma.h	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/include/linux/bcma/bcma.h	2011-11-13 17:11:58.629557701 -0800
@@ -6,6 +6,7 @@
 
 #include <linux/bcma/bcma_driver_chipcommon.h>
 #include <linux/bcma/bcma_driver_pci.h>
+#include <linux/bcma/bcma_driver_mips.h>
 #include <linux/ssb/ssb.h> /* SPROM sharing */
 
 #include "bcma_regs.h"
@@ -14,9 +15,9 @@
 struct bcma_bus;
 
 enum bcma_hosttype {
-	BCMA_HOSTTYPE_NONE,
 	BCMA_HOSTTYPE_PCI,
 	BCMA_HOSTTYPE_SDIO,
+	BCMA_HOSTTYPE_SOC,
 };
 
 struct bcma_chipinfo {
@@ -130,6 +131,7 @@
 
 	struct device dev;
 	struct device *dma_dev;
+
 	unsigned int irq;
 	bool dev_registered;
 
@@ -138,6 +140,9 @@
 	u32 addr;
 	u32 wrap;
 
+	void __iomem *io_addr;
+	void __iomem *io_wrap;
+
 	void *drvdata;
 	struct list_head list;
 };
@@ -190,9 +195,11 @@
 	struct bcma_device *mapped_core;
 	struct list_head cores;
 	u8 nr_cores;
+	u8 init_done:1;
 
 	struct bcma_drv_cc drv_cc;
 	struct bcma_drv_pci drv_pci;
+	struct bcma_drv_mips drv_mips;
 
 	/* We decided to share SPROM struct with SSB as long as we do not need
 	 * any hacks for BCMA. This simplifies drivers code. */
diff -ruN linux-3.1.1.orig/include/linux/bcma/bcma_driver_chipcommon.h linux-3.1.1/include/linux/bcma/bcma_driver_chipcommon.h
--- linux-3.1.1.orig/include/linux/bcma/bcma_driver_chipcommon.h	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/include/linux/bcma/bcma_driver_chipcommon.h	2011-11-13 17:11:58.629557701 -0800
@@ -24,6 +24,7 @@
 #define   BCMA_CC_FLASHT_NONE		0x00000000	/* No flash */
 #define   BCMA_CC_FLASHT_STSER		0x00000100	/* ST serial flash */
 #define   BCMA_CC_FLASHT_ATSER		0x00000200	/* Atmel serial flash */
+#define   BCMA_CC_FLASHT_NFLASH		0x00000200
 #define	  BCMA_CC_FLASHT_PARA		0x00000700	/* Parallel flash */
 #define  BCMA_CC_CAP_PLLT		0x00038000	/* PLL Type */
 #define   BCMA_PLLTYPE_NONE		0x00000000
@@ -178,6 +179,7 @@
 #define BCMA_CC_PROG_CFG		0x0120
 #define BCMA_CC_PROG_WAITCNT		0x0124
 #define BCMA_CC_FLASH_CFG		0x0128
+#define  BCMA_CC_FLASH_CFG_DS		0x0010	/* Data size, 0=8bit, 1=16bit */
 #define BCMA_CC_FLASH_WAITCNT		0x012C
 /* 0x1E0 is defined as shared BCMA_CLKCTLST */
 #define BCMA_CC_HW_WORKAROUND		0x01E4 /* Hardware workaround (rev >= 20) */
@@ -239,6 +241,64 @@
 #define BCMA_CC_SPROM			0x0800 /* SPROM beginning */
 #define BCMA_CC_SPROM_PCIE6		0x0830 /* SPROM beginning on PCIe rev >= 6 */
 
+/* Divider allocation in 4716/47162/5356 */
+#define BCMA_CC_PMU5_MAINPLL_CPU	1
+#define BCMA_CC_PMU5_MAINPLL_MEM	2
+#define BCMA_CC_PMU5_MAINPLL_SSB	3
+
+/* PLL usage in 4716/47162 */
+#define BCMA_CC_PMU4716_MAINPLL_PLL0	12
+
+/* PLL usage in 5356/5357 */
+#define BCMA_CC_PMU5356_MAINPLL_PLL0	0
+#define BCMA_CC_PMU5357_MAINPLL_PLL0	0
+
+/* 4706 PMU */
+#define BCMA_CC_PMU4706_MAINPLL_PLL0	0
+
+/* ALP clock on pre-PMU chips */
+#define BCMA_CC_PMU_ALP_CLOCK		20000000
+/* HT clock for systems with PMU-enabled chipcommon */
+#define BCMA_CC_PMU_HT_CLOCK		80000000
+
+/* PMU rev 5 (& 6) */
+#define BCMA_CC_PPL_P1P2_OFF		0
+#define BCMA_CC_PPL_P1_MASK		0x0f000000
+#define BCMA_CC_PPL_P1_SHIFT		24
+#define BCMA_CC_PPL_P2_MASK		0x00f00000
+#define BCMA_CC_PPL_P2_SHIFT		20
+#define BCMA_CC_PPL_M14_OFF		1
+#define BCMA_CC_PPL_MDIV_MASK		0x000000ff
+#define BCMA_CC_PPL_MDIV_WIDTH		8
+#define BCMA_CC_PPL_NM5_OFF		2
+#define BCMA_CC_PPL_NDIV_MASK		0xfff00000
+#define BCMA_CC_PPL_NDIV_SHIFT		20
+#define BCMA_CC_PPL_FMAB_OFF		3
+#define BCMA_CC_PPL_MRAT_MASK		0xf0000000
+#define BCMA_CC_PPL_MRAT_SHIFT		28
+#define BCMA_CC_PPL_ABRAT_MASK		0x08000000
+#define BCMA_CC_PPL_ABRAT_SHIFT		27
+#define BCMA_CC_PPL_FDIV_MASK		0x07ffffff
+#define BCMA_CC_PPL_PLLCTL_OFF		4
+#define BCMA_CC_PPL_PCHI_OFF		5
+#define BCMA_CC_PPL_PCHI_MASK		0x0000003f
+
+/* BCM4331 ChipControl numbers. */
+#define BCMA_CHIPCTL_4331_BT_COEXIST		BIT(0)	/* 0 disable */
+#define BCMA_CHIPCTL_4331_SECI			BIT(1)	/* 0 SECI is disabled (JATG functional) */
+#define BCMA_CHIPCTL_4331_EXT_LNA		BIT(2)	/* 0 disable */
+#define BCMA_CHIPCTL_4331_SPROM_GPIO13_15	BIT(3)	/* sprom/gpio13-15 mux */
+#define BCMA_CHIPCTL_4331_EXTPA_EN		BIT(4)	/* 0 ext pa disable, 1 ext pa enabled */
+#define BCMA_CHIPCTL_4331_GPIOCLK_ON_SPROMCS	BIT(5)	/* set drive out GPIO_CLK on sprom_cs pin */
+#define BCMA_CHIPCTL_4331_PCIE_MDIO_ON_SPROMCS	BIT(6)	/* use sprom_cs pin as PCIE mdio interface */
+#define BCMA_CHIPCTL_4331_EXTPA_ON_GPIO2_5	BIT(7)	/* aband extpa will be at gpio2/5 and sprom_dout */
+#define BCMA_CHIPCTL_4331_OVR_PIPEAUXCLKEN	BIT(8)	/* override core control on pipe_AuxClkEnable */
+#define BCMA_CHIPCTL_4331_OVR_PIPEAUXPWRDOWN	BIT(9)	/* override core control on pipe_AuxPowerDown */
+#define BCMA_CHIPCTL_4331_PCIE_AUXCLKEN		BIT(10)	/* pcie_auxclkenable */
+#define BCMA_CHIPCTL_4331_PCIE_PIPE_PLLDOWN	BIT(11)	/* pcie_pipe_pllpowerdown */
+#define BCMA_CHIPCTL_4331_BT_SHD0_ON_GPIO4	BIT(16)	/* enable bt_shd0 at gpio4 */
+#define BCMA_CHIPCTL_4331_BT_SHD1_ON_GPIO5	BIT(17)	/* enable bt_shd1 at gpio5 */
+
 /* Data for the PMU, if available.
  * Check availability with ((struct bcma_chipcommon)->capabilities & BCMA_CC_CAP_PMU)
  */
@@ -247,14 +307,37 @@
 	u32 crystalfreq;	/* The active crystal frequency (in kHz) */
 };
 
+#ifdef CONFIG_BCMA_DRIVER_MIPS
+struct bcma_pflash {
+	u8 buswidth;
+	u32 window;
+	u32 window_size;
+};
+
+struct bcma_serial_port {
+	void *regs;
+	unsigned long clockspeed;
+	unsigned int irq;
+	unsigned int baud_base;
+	unsigned int reg_shift;
+};
+#endif /* CONFIG_BCMA_DRIVER_MIPS */
+
 struct bcma_drv_cc {
 	struct bcma_device *core;
 	u32 status;
 	u32 capabilities;
 	u32 capabilities_ext;
+	u8 setup_done:1;
 	/* Fast Powerup Delay constant */
 	u16 fast_pwrup_delay;
 	struct bcma_chipcommon_pmu pmu;
+#ifdef CONFIG_BCMA_DRIVER_MIPS
+	struct bcma_pflash pflash;
+
+	int nr_serial_ports;
+	struct bcma_serial_port serial_ports[4];
+#endif /* CONFIG_BCMA_DRIVER_MIPS */
 };
 
 /* Register access */
@@ -275,6 +358,8 @@
 extern void bcma_chipco_suspend(struct bcma_drv_cc *cc);
 extern void bcma_chipco_resume(struct bcma_drv_cc *cc);
 
+void bcma_chipco_bcm4331_ext_pa_lines_ctl(struct bcma_drv_cc *cc, bool enable);
+
 extern void bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc,
 					  u32 ticks);
 
@@ -293,4 +378,13 @@
 /* PMU support */
 extern void bcma_pmu_init(struct bcma_drv_cc *cc);
 
+extern void bcma_chipco_pll_write(struct bcma_drv_cc *cc, u32 offset,
+				  u32 value);
+extern void bcma_chipco_pll_maskset(struct bcma_drv_cc *cc, u32 offset,
+				    u32 mask, u32 set);
+extern void bcma_chipco_chipctl_maskset(struct bcma_drv_cc *cc,
+					u32 offset, u32 mask, u32 set);
+extern void bcma_chipco_regctl_maskset(struct bcma_drv_cc *cc,
+				       u32 offset, u32 mask, u32 set);
+
 #endif /* LINUX_BCMA_DRIVER_CC_H_ */
diff -ruN linux-3.1.1.orig/include/linux/bcma/bcma_driver_mips.h linux-3.1.1/include/linux/bcma/bcma_driver_mips.h
--- linux-3.1.1.orig/include/linux/bcma/bcma_driver_mips.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-3.1.1/include/linux/bcma/bcma_driver_mips.h	2011-11-13 17:11:58.629557701 -0800
@@ -0,0 +1,51 @@
+#ifndef LINUX_BCMA_DRIVER_MIPS_H_
+#define LINUX_BCMA_DRIVER_MIPS_H_
+
+#define BCMA_MIPS_IPSFLAG		0x0F08
+/* which sbflags get routed to mips interrupt 1 */
+#define  BCMA_MIPS_IPSFLAG_IRQ1		0x0000003F
+#define  BCMA_MIPS_IPSFLAG_IRQ1_SHIFT	0
+/* which sbflags get routed to mips interrupt 2 */
+#define  BCMA_MIPS_IPSFLAG_IRQ2		0x00003F00
+#define  BCMA_MIPS_IPSFLAG_IRQ2_SHIFT	8
+/* which sbflags get routed to mips interrupt 3 */
+#define  BCMA_MIPS_IPSFLAG_IRQ3		0x003F0000
+#define  BCMA_MIPS_IPSFLAG_IRQ3_SHIFT	16
+/* which sbflags get routed to mips interrupt 4 */
+#define  BCMA_MIPS_IPSFLAG_IRQ4		0x3F000000
+#define  BCMA_MIPS_IPSFLAG_IRQ4_SHIFT	24
+
+/* MIPS 74K core registers */
+#define BCMA_MIPS_MIPS74K_CORECTL	0x0000
+#define BCMA_MIPS_MIPS74K_EXCEPTBASE	0x0004
+#define BCMA_MIPS_MIPS74K_BIST		0x000C
+#define BCMA_MIPS_MIPS74K_INTMASK_INT0	0x0014
+#define BCMA_MIPS_MIPS74K_INTMASK(int) \
+	((int) * 4 + BCMA_MIPS_MIPS74K_INTMASK_INT0)
+#define BCMA_MIPS_MIPS74K_NMIMASK	0x002C
+#define BCMA_MIPS_MIPS74K_GPIOSEL	0x0040
+#define BCMA_MIPS_MIPS74K_GPIOOUT	0x0044
+#define BCMA_MIPS_MIPS74K_GPIOEN	0x0048
+#define BCMA_MIPS_MIPS74K_CLKCTLST	0x01E0
+
+#define BCMA_MIPS_OOBSELOUTA30		0x100
+
+struct bcma_device;
+
+struct bcma_drv_mips {
+	struct bcma_device *core;
+	u8 setup_done:1;
+	unsigned int assigned_irqs;
+};
+
+#ifdef CONFIG_BCMA_DRIVER_MIPS
+extern void bcma_core_mips_init(struct bcma_drv_mips *mcore);
+#else
+static inline void bcma_core_mips_init(struct bcma_drv_mips *mcore) { }
+#endif
+
+extern u32 bcma_cpu_clock(struct bcma_drv_mips *mcore);
+
+extern unsigned int bcma_core_mips_irq(struct bcma_device *dev);
+
+#endif /* LINUX_BCMA_DRIVER_MIPS_H_ */
diff -ruN linux-3.1.1.orig/include/linux/bcma/bcma_soc.h linux-3.1.1/include/linux/bcma/bcma_soc.h
--- linux-3.1.1.orig/include/linux/bcma/bcma_soc.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-3.1.1/include/linux/bcma/bcma_soc.h	2011-11-13 17:11:58.629557701 -0800
@@ -0,0 +1,16 @@
+#ifndef LINUX_BCMA_SOC_H_
+#define LINUX_BCMA_SOC_H_
+
+#include <linux/bcma/bcma.h>
+
+struct bcma_soc {
+	struct bcma_bus bus;
+	struct bcma_device core_cc;
+	struct bcma_device core_mips;
+};
+
+int __init bcma_host_soc_register(struct bcma_soc *soc);
+
+int bcma_bus_register(struct bcma_bus *bus);
+
+#endif /* LINUX_BCMA_SOC_H_ */
diff -ruN linux-3.1.1.orig/include/linux/ssb/ssb.h linux-3.1.1/include/linux/ssb/ssb.h
--- linux-3.1.1.orig/include/linux/ssb/ssb.h	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/include/linux/ssb/ssb.h	2011-11-13 17:11:58.629557701 -0800
@@ -25,7 +25,7 @@
 	u8 et1phyaddr;		/* MII address for enet1 */
 	u8 et0mdcport;		/* MDIO for enet0 */
 	u8 et1mdcport;		/* MDIO for enet1 */
-	u8 board_rev;		/* Board revision number from SPROM. */
+	u16 board_rev;		/* Board revision number from SPROM. */
 	u8 country_code;	/* Country Code */
 	u16 leddc_on_time;	/* LED Powersave Duty Cycle On Count */
 	u16 leddc_off_time;	/* LED Powersave Duty Cycle Off Count */
diff -ruN linux-3.1.1.orig/include/linux/ssb/ssb_regs.h linux-3.1.1/include/linux/ssb/ssb_regs.h
--- linux-3.1.1.orig/include/linux/ssb/ssb_regs.h	2011-11-11 12:19:27.000000000 -0800
+++ linux-3.1.1/include/linux/ssb/ssb_regs.h	2011-11-13 17:11:58.639557482 -0800
@@ -462,6 +462,46 @@
 #define SSB_SPROM8_OFDM5GLPO		0x014A	/* 5.2GHz OFDM power offset */
 #define SSB_SPROM8_OFDM5GHPO		0x014E	/* 5.8GHz OFDM power offset */
 
+/* Values for boardflags_lo read from SPROM */
+#define SSB_BFL_BTCOEXIST		0x0001	/* implements Bluetooth coexistance */
+#define SSB_BFL_PACTRL			0x0002	/* GPIO 9 controlling the PA */
+#define SSB_BFL_AIRLINEMODE		0x0004	/* implements GPIO 13 radio disable indication */
+#define SSB_BFL_RSSI			0x0008	/* software calculates nrssi slope. */
+#define SSB_BFL_ENETSPI			0x0010	/* has ephy roboswitch spi */
+#define SSB_BFL_XTAL_NOSLOW		0x0020	/* no slow clock available */
+#define SSB_BFL_CCKHIPWR		0x0040	/* can do high power CCK transmission */
+#define SSB_BFL_ENETADM			0x0080	/* has ADMtek switch */
+#define SSB_BFL_ENETVLAN		0x0100	/* can do vlan */
+#define SSB_BFL_AFTERBURNER		0x0200	/* supports Afterburner mode */
+#define SSB_BFL_NOPCI			0x0400	/* board leaves PCI floating */
+#define SSB_BFL_FEM			0x0800	/* supports the Front End Module */
+#define SSB_BFL_EXTLNA			0x1000	/* has an external LNA */
+#define SSB_BFL_HGPA			0x2000	/* had high gain PA */
+#define SSB_BFL_BTCMOD			0x4000	/* BFL_BTCOEXIST is given in alternate GPIOs */
+#define SSB_BFL_ALTIQ			0x8000	/* alternate I/Q settings */
+
+/* Values for boardflags_hi read from SPROM */
+#define SSB_BFH_NOPA			0x0001	/* has no PA */
+#define SSB_BFH_RSSIINV			0x0002	/* RSSI uses positive slope (not TSSI) */
+#define SSB_BFH_PAREF			0x0004	/* uses the PARef LDO */
+#define SSB_BFH_3TSWITCH		0x0008	/* uses a triple throw switch shared with bluetooth */
+#define SSB_BFH_PHASESHIFT		0x0010	/* can support phase shifter */
+#define SSB_BFH_BUCKBOOST		0x0020	/* has buck/booster */
+#define SSB_BFH_FEM_BT			0x0040	/* has FEM and switch to share antenna with bluetooth */
+
+/* Values for boardflags2_lo read from SPROM */
+#define SSB_BFL2_RXBB_INT_REG_DIS	0x0001	/* external RX BB regulator present */
+#define SSB_BFL2_APLL_WAR		0x0002	/* alternative A-band PLL settings implemented */
+#define SSB_BFL2_TXPWRCTRL_EN 		0x0004	/* permits enabling TX Power Control */
+#define SSB_BFL2_2X4_DIV		0x0008	/* 2x4 diversity switch */
+#define SSB_BFL2_5G_PWRGAIN		0x0010	/* supports 5G band power gain */
+#define SSB_BFL2_PCIEWAR_OVR		0x0020	/* overrides ASPM and Clkreq settings */
+#define SSB_BFL2_CAESERS_BRD		0x0040	/* is Caesers board (unused) */
+#define SSB_BFL2_BTC3WIRE		0x0080	/* used 3-wire bluetooth coexist */
+#define SSB_BFL2_SKWRKFEM_BRD		0x0100	/* 4321mcm93 uses Skyworks FEM */
+#define SSB_BFL2_SPUR_WAR		0x0200	/* has a workaround for clock-harmonic spurs */
+#define SSB_BFL2_GPLL_WAR		0x0400	/* altenative G-band PLL settings implemented */
+
 /* Values for SSB_SPROM1_BINF_CCODE */
 enum {
 	SSB_SPROM1CCODE_WORLD = 0,
