Subject: [PATCH 0/7] bcma: add PCIe host controller

From: Hauke Mehrtens <[email-masked]>
Date: Tue, 31 Jan 2012 00:03:30 +0100

These patches are adding support for a PCIe Host controller found on 
some Broadcom SoCs using bcma as the System bus. This was tested with 
one BCM4716 based device, a Netgear WNDR3400 with a BCM43224 connected 
through PCIe.
These patches are based on wireless-testing.

Hauke Mehrtens (7):
  bcma: add the core unit number
  bcma: add constants for PCI and use them
  bcma: export bcma_pcie_read()
  bcma: make some functions __devinit
  bcma: add PCIe host controller
  bcma: add bus num counter
  bcma: add extra sprom check

 arch/mips/pci/pci-bcm47xx.c                 |   49 ++-
 drivers/bcma/bcma_private.h                 |    8 +-
 drivers/bcma/driver_pci.c                   |  168 ++++-----
 drivers/bcma/driver_pci_host.c              |  578 ++++++++++++++++++++++++++-
 drivers/bcma/host_pci.c                     |    4 +-
 drivers/bcma/main.c                         |   14 +-
 drivers/bcma/scan.c                         |   14 +
 drivers/bcma/sprom.c                        |    7 +
 include/linux/bcma/bcma.h                   |    2 +
 include/linux/bcma/bcma_driver_chipcommon.h |   16 +
 include/linux/bcma/bcma_driver_pci.h        |  125 ++++++-
 include/linux/bcma/bcma_regs.h              |   27 ++
 12 files changed, 908 insertions(+), 104 deletions(-)

-- 
1.7.5.4

---

From: Hauke Mehrtens <[email-masked]>
Date: Tue, 31 Jan 2012 00:03:31 +0100

Some SoCs have two pcie or gmac cores and we need to know the number of
the specific core on the bus. This is the case for the BCM4706.

Signed-off-by: Hauke Mehrtens <[email-masked]>
---
 drivers/bcma/scan.c       |   14 ++++++++++++++
 include/linux/bcma/bcma.h |    1 +
 2 files changed, 15 insertions(+), 0 deletions(-)

diff --git a/drivers/bcma/scan.c b/drivers/bcma/scan.c
index cad9948..e513aa8 100644
--- a/drivers/bcma/scan.c
+++ b/drivers/bcma/scan.c
@@ -212,6 +212,17 @@ static struct bcma_device *bcma_find_core_by_index(struct bcma_bus *bus,
 	return NULL;
 }
 
+static struct bcma_device *bcma_find_core_reverse(struct bcma_bus *bus, u16 coreid)
+{
+	struct bcma_device *core;
+
+	list_for_each_entry_reverse(core, &bus->cores, list) {
+		if (core->id.id == coreid)
+			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)
@@ -392,6 +403,7 @@ int bcma_bus_scan(struct bcma_bus *bus)
 	bcma_scan_switch_core(bus, erombase);
 
 	while (eromptr < eromend) {
+		struct bcma_device *other_core;
 		struct bcma_device *core = kzalloc(sizeof(*core), GFP_KERNEL);
 		if (!core)
 			return -ENOMEM;
@@ -411,6 +423,8 @@ int bcma_bus_scan(struct bcma_bus *bus)
 
 		core->core_index = core_num++;
 		bus->nr_cores++;
+		other_core = bcma_find_core_reverse(bus, core->id.id);
+		core->core_unit = (other_core == NULL) ? 0 : other_core->core_unit + 1;
 
 		pr_info("Core %d found: %s "
 			"(manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n",
diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h
index 83c209f..024a6e2 100644
--- a/include/linux/bcma/bcma.h
+++ b/include/linux/bcma/bcma.h
@@ -136,6 +136,7 @@ struct bcma_device {
 	bool dev_registered;
 
 	u8 core_index;
+	u8 core_unit;
 
 	u32 addr;
 	u32 wrap;
-- 
1.7.5.4

---

From: Hauke Mehrtens <[email-masked]>
Date: Tue, 31 Jan 2012 00:03:33 +0100

This will be needed by the host controller.

Signed-off-by: Hauke Mehrtens <[email-masked]>
---
 drivers/bcma/bcma_private.h |    3 +++
 drivers/bcma/driver_pci.c   |    2 +-
 2 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h
index 0def898..6109da5 100644
--- a/drivers/bcma/bcma_private.h
+++ b/drivers/bcma/bcma_private.h
@@ -48,6 +48,9 @@ extern int __init bcma_host_pci_init(void);
 extern void __exit bcma_host_pci_exit(void);
 #endif /* CONFIG_BCMA_HOST_PCI */
 
+/* driver_pci.c */
+u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address);
+
 #ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
 void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc);
 #endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */
diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c
index 970a38e..20ceebd 100644
--- a/drivers/bcma/driver_pci.c
+++ b/drivers/bcma/driver_pci.c
@@ -17,7 +17,7 @@
  * R/W ops.
  **************************************************/
 
-static u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address)
+u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address)
 {
 	pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_ADDR, address);
 	pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_ADDR);
-- 
1.7.5.4

---

From: Hauke Mehrtens <[email-masked]>
Date: Tue, 31 Jan 2012 00:03:32 +0100

There are many magic numbers used in the PCIe code. Replace them with
some constants from the Broadcom SDK and also use them in the pcie host
controller.

Signed-off-by: Hauke Mehrtens <[email-masked]>
---
 drivers/bcma/driver_pci.c            |  124 +++++++++++++++++++---------------
 include/linux/bcma/bcma_driver_pci.h |   85 +++++++++++++++++++++++
 2 files changed, 155 insertions(+), 54 deletions(-)

diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c
index 4fde625..970a38e 100644
--- a/drivers/bcma/driver_pci.c
+++ b/drivers/bcma/driver_pci.c
@@ -4,6 +4,7 @@
  *
  * Copyright 2005, Broadcom Corporation
  * Copyright 2006, 2007, Michael Buesch <[email-masked]>
+ * Copyright 2011, 2012, Hauke Mehrtens <[email-masked]>
  *
  * Licensed under the GNU/GPL. See COPYING for details.
  */
@@ -18,38 +19,39 @@
 
 static u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address)
 {
-	pcicore_write32(pc, 0x130, address);
-	pcicore_read32(pc, 0x130);
-	return pcicore_read32(pc, 0x134);
+	pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_ADDR, address);
+	pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_ADDR);
+	return pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_DATA);
 }
 
 #if 0
 static void bcma_pcie_write(struct bcma_drv_pci *pc, u32 address, u32 data)
 {
-	pcicore_write32(pc, 0x130, address);
-	pcicore_read32(pc, 0x130);
-	pcicore_write32(pc, 0x134, data);
+	pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_ADDR, address);
+	pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_ADDR);
+	pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_DATA, data);
 }
 #endif
 
 static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u8 phy)
 {
-	const u16 mdio_control = 0x128;
-	const u16 mdio_data = 0x12C;
 	u32 v;
 	int i;
 
-	v = (1 << 30); /* Start of Transaction */
-	v |= (1 << 28); /* Write Transaction */
-	v |= (1 << 17); /* Turnaround */
-	v |= (0x1F << 18);
+	v = BCMA_CORE_PCI_MDIODATA_START;
+	v |= BCMA_CORE_PCI_MDIODATA_WRITE;
+	v |= (BCMA_CORE_PCI_MDIODATA_DEV_ADDR <<
+	      BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF);
+	v |= (BCMA_CORE_PCI_MDIODATA_BLK_ADDR <<
+	      BCMA_CORE_PCI_MDIODATA_REGADDR_SHF);
+	v |= BCMA_CORE_PCI_MDIODATA_TA;
 	v |= (phy << 4);
-	pcicore_write32(pc, mdio_data, v);
+	pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v);
 
 	udelay(10);
 	for (i = 0; i < 200; i++) {
-		v = pcicore_read32(pc, mdio_control);
-		if (v & 0x100 /* Trans complete */)
+		v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL);
+		if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE)
 			break;
 		msleep(1);
 	}
@@ -57,79 +59,84 @@ static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u8 phy)
 
 static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u8 device, u8 address)
 {
-	const u16 mdio_control = 0x128;
-	const u16 mdio_data = 0x12C;
 	int max_retries = 10;
 	u16 ret = 0;
 	u32 v;
 	int i;
 
-	v = 0x80; /* Enable Preamble Sequence */
-	v |= 0x2; /* MDIO Clock Divisor */
-	pcicore_write32(pc, mdio_control, v);
+	/* enable mdio access to SERDES */
+	v = BCMA_CORE_PCI_MDIOCTL_PREAM_EN;
+	v |= BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL;
+	pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, v);
 
 	if (pc->core->id.rev >= 10) {
 		max_retries = 200;
 		bcma_pcie_mdio_set_phy(pc, device);
+		v = (BCMA_CORE_PCI_MDIODATA_DEV_ADDR <<
+		     BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF);
+		v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF);
+	} else {
+		v = (device << BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD);
+		v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD);
 	}
 
-	v = (1 << 30); /* Start of Transaction */
-	v |= (1 << 29); /* Read Transaction */
-	v |= (1 << 17); /* Turnaround */
-	if (pc->core->id.rev < 10)
-		v |= (u32)device << 22;
-	v |= (u32)address << 18;
-	pcicore_write32(pc, mdio_data, v);
+	v = BCMA_CORE_PCI_MDIODATA_START;
+	v |= BCMA_CORE_PCI_MDIODATA_READ;
+	v |= BCMA_CORE_PCI_MDIODATA_TA;
+
+	pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v);
 	/* Wait for the device to complete the transaction */
 	udelay(10);
 	for (i = 0; i < max_retries; i++) {
-		v = pcicore_read32(pc, mdio_control);
-		if (v & 0x100 /* Trans complete */) {
+		v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL);
+		if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE) {
 			udelay(10);
-			ret = pcicore_read32(pc, mdio_data);
+			ret = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_DATA);
 			break;
 		}
 		msleep(1);
 	}
-	pcicore_write32(pc, mdio_control, 0);
+	pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0);
 	return ret;
 }
 
 static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u8 device,
 				u8 address, u16 data)
 {
-	const u16 mdio_control = 0x128;
-	const u16 mdio_data = 0x12C;
 	int max_retries = 10;
 	u32 v;
 	int i;
 
-	v = 0x80; /* Enable Preamble Sequence */
-	v |= 0x2; /* MDIO Clock Divisor */
-	pcicore_write32(pc, mdio_control, v);
+	/* enable mdio access to SERDES */
+	v = BCMA_CORE_PCI_MDIOCTL_PREAM_EN;
+	v |= BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL;
+	pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, v);
 
 	if (pc->core->id.rev >= 10) {
 		max_retries = 200;
 		bcma_pcie_mdio_set_phy(pc, device);
+		v = (BCMA_CORE_PCI_MDIODATA_DEV_ADDR <<
+		     BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF);
+		v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF);
+	} else {
+		v = (device << BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD);
+		v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD);
 	}
 
-	v = (1 << 30); /* Start of Transaction */
-	v |= (1 << 28); /* Write Transaction */
-	v |= (1 << 17); /* Turnaround */
-	if (pc->core->id.rev < 10)
-		v |= (u32)device << 22;
-	v |= (u32)address << 18;
+	v = BCMA_CORE_PCI_MDIODATA_START;
+	v |= BCMA_CORE_PCI_MDIODATA_WRITE;
+	v |= BCMA_CORE_PCI_MDIODATA_TA;
 	v |= data;
-	pcicore_write32(pc, mdio_data, v);
+	pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v);
 	/* Wait for the device to complete the transaction */
 	udelay(10);
 	for (i = 0; i < max_retries; i++) {
-		v = pcicore_read32(pc, mdio_control);
-		if (v & 0x100 /* Trans complete */)
+		v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL);
+		if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE)
 			break;
 		msleep(1);
 	}
-	pcicore_write32(pc, mdio_control, 0);
+	pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0);
 }
 
 /**************************************************
@@ -138,20 +145,29 @@ static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u8 device,
 
 static u8 bcma_pcicore_polarity_workaround(struct bcma_drv_pci *pc)
 {
-	return (bcma_pcie_read(pc, 0x204) & 0x10) ? 0xC0 : 0x80;
+	u32 tmp;
+	
+	tmp = bcma_pcie_read(pc, BCMA_CORE_PCI_PLP_STATUSREG);
+	if (tmp & BCMA_CORE_PCI_PLP_POLARITYINV_STAT)
+		return BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE |
+		       BCMA_CORE_PCI_SERDES_RX_CTRL_POLARITY;
+	else
+		return BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE;
 }
 
 static void bcma_pcicore_serdes_workaround(struct bcma_drv_pci *pc)
 {
-	const u8 serdes_pll_device = 0x1D;
-	const u8 serdes_rx_device = 0x1F;
 	u16 tmp;
 
-	bcma_pcie_mdio_write(pc, serdes_rx_device, 1 /* Control */,
-			      bcma_pcicore_polarity_workaround(pc));
-	tmp = bcma_pcie_mdio_read(pc, serdes_pll_device, 1 /* Control */);
-	if (tmp & 0x4000)
-		bcma_pcie_mdio_write(pc, serdes_pll_device, 1, tmp & ~0x4000);
+	bcma_pcie_mdio_write(pc, BCMA_CORE_PCI_MDIODATA_DEV_RX,
+	                     BCMA_CORE_PCI_SERDES_RX_CTRL,
+			     bcma_pcicore_polarity_workaround(pc));
+	tmp = bcma_pcie_mdio_read(pc, BCMA_CORE_PCI_MDIODATA_DEV_PLL,
+	                          BCMA_CORE_PCI_SERDES_PLL_CTRL);
+	if (tmp & BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN)
+		bcma_pcie_mdio_write(pc, BCMA_CORE_PCI_MDIODATA_DEV_PLL,
+		                     BCMA_CORE_PCI_SERDES_PLL_CTRL,
+		                     tmp & ~BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN);
 }
 
 /**************************************************
diff --git a/include/linux/bcma/bcma_driver_pci.h b/include/linux/bcma/bcma_driver_pci.h
index 3871b66..67ea7be 100644
--- a/include/linux/bcma/bcma_driver_pci.h
+++ b/include/linux/bcma/bcma_driver_pci.h
@@ -53,6 +53,35 @@ struct pci_dev;
 #define  BCMA_CORE_PCI_SBTOPCI1_MASK		0xFC000000
 #define BCMA_CORE_PCI_SBTOPCI2			0x0108	/* Backplane to PCI translation 2 (sbtopci2) */
 #define  BCMA_CORE_PCI_SBTOPCI2_MASK		0xC0000000
+#define BCMA_CORE_PCI_CONFIG_ADDR		0x0120	/* pcie config space access */
+#define BCMA_CORE_PCI_CONFIG_DATA		0x0124	/* pcie config space access */
+#define BCMA_CORE_PCI_MDIO_CONTROL		0x0128	/* controls the mdio access */
+#define  BCMA_CORE_PCI_MDIOCTL_DIVISOR_MASK	0x7f	/* clock to be used on MDIO */
+#define  BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL	0x2
+#define  BCMA_CORE_PCI_MDIOCTL_PREAM_EN		0x80	/* Enable preamble sequnce */
+#define  BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE	0x100	/* Tranaction complete */
+#define BCMA_CORE_PCI_MDIO_DATA			0x012c	/* Data to the mdio access */
+#define  BCMA_CORE_PCI_MDIODATA_MASK		0x0000ffff /* data 2 bytes */
+#define  BCMA_CORE_PCI_MDIODATA_TA		0x00020000 /* Turnaround */
+#define  BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD	18	/* Regaddr shift (rev < 10) */
+#define  BCMA_CORE_PCI_MDIODATA_REGADDR_MASK_OLD	0x003c0000 /* Regaddr Mask (rev < 10) */
+#define  BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD	22	/* Physmedia devaddr shift (rev < 10) */
+#define  BCMA_CORE_PCI_MDIODATA_DEVADDR_MASK_OLD	0x0fc00000 /* Physmedia devaddr Mask (rev < 10) */
+#define  BCMA_CORE_PCI_MDIODATA_REGADDR_SHF	18	/* Regaddr shift */
+#define  BCMA_CORE_PCI_MDIODATA_REGADDR_MASK	0x007c0000 /* Regaddr Mask */
+#define  BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF	23	/* Physmedia devaddr shift */
+#define  BCMA_CORE_PCI_MDIODATA_DEVADDR_MASK	0x0f800000 /* Physmedia devaddr Mask */
+#define  BCMA_CORE_PCI_MDIODATA_WRITE		0x10000000 /* write Transaction */
+#define  BCMA_CORE_PCI_MDIODATA_READ		0x20000000 /* Read Transaction */
+#define  BCMA_CORE_PCI_MDIODATA_START		0x40000000 /* start of Transaction */
+#define  BCMA_CORE_PCI_MDIODATA_DEV_ADDR	0x0	/* dev address for serdes */
+#define  BCMA_CORE_PCI_MDIODATA_BLK_ADDR	0x1F	/* blk address for serdes */
+#define  BCMA_CORE_PCI_MDIODATA_DEV_PLL		0x1d	/* SERDES PLL Dev */
+#define  BCMA_CORE_PCI_MDIODATA_DEV_TX		0x1e	/* SERDES TX Dev */
+#define  BCMA_CORE_PCI_MDIODATA_DEV_RX		0x1f	/* SERDES RX Dev */
+#define BCMA_CORE_PCI_PCIEIND_ADDR		0x0130	/* indirect access to the internal register */
+#define BCMA_CORE_PCI_PCIEIND_DATA		0x0134	/* Data to/from the internal regsiter */
+#define BCMA_CORE_PCI_CLKREQENCTRL		0x0138	/*  >= rev 6, Clkreq rdma control */
 #define BCMA_CORE_PCI_PCICFG0			0x0400	/* PCI config space 0 (rev >= 8) */
 #define BCMA_CORE_PCI_PCICFG1			0x0500	/* PCI config space 1 (rev >= 8) */
 #define BCMA_CORE_PCI_PCICFG2			0x0600	/* PCI config space 2 (rev >= 8) */
@@ -72,6 +101,62 @@ struct pci_dev;
 #define  BCMA_CORE_PCI_SBTOPCI_RC_READL		0x00000010 /* Memory read line */
 #define  BCMA_CORE_PCI_SBTOPCI_RC_READM		0x00000020 /* Memory read multiple */
 
+/* PCIE protocol PHY diagnostic registers */
+#define BCMA_CORE_PCI_PLP_MODEREG		0x200	/* Mode */
+#define BCMA_CORE_PCI_PLP_STATUSREG		0x204	/* Status */
+#define  BCMA_CORE_PCI_PLP_POLARITYINV_STAT	0x10	/* Status reg PCIE_PLP_STATUSREG */
+#define BCMA_CORE_PCI_PLP_LTSSMCTRLREG		0x208	/* LTSSM control */
+#define BCMA_CORE_PCI_PLP_LTLINKNUMREG		0x20c	/* Link Training Link number */
+#define BCMA_CORE_PCI_PLP_LTLANENUMREG		0x210	/* Link Training Lane number */
+#define BCMA_CORE_PCI_PLP_LTNFTSREG		0x214	/* Link Training N_FTS */
+#define BCMA_CORE_PCI_PLP_ATTNREG		0x218	/* Attention */
+#define BCMA_CORE_PCI_PLP_ATTNMASKREG		0x21C	/* Attention Mask */
+#define BCMA_CORE_PCI_PLP_RXERRCTR		0x220	/* Rx Error */
+#define BCMA_CORE_PCI_PLP_RXFRMERRCTR		0x224	/* Rx Framing Error */
+#define BCMA_CORE_PCI_PLP_RXERRTHRESHREG	0x228	/* Rx Error threshold */
+#define BCMA_CORE_PCI_PLP_TESTCTRLREG		0x22C	/* Test Control reg */
+#define BCMA_CORE_PCI_PLP_SERDESCTRLOVRDREG	0x230	/* SERDES Control Override */
+#define BCMA_CORE_PCI_PLP_TIMINGOVRDREG		0x234	/* Timing param override */
+#define BCMA_CORE_PCI_PLP_RXTXSMDIAGREG		0x238	/* RXTX State Machine Diag */
+#define BCMA_CORE_PCI_PLP_LTSSMDIAGREG		0x23C	/* LTSSM State Machine Diag */
+
+/* PCIE protocol DLLP diagnostic registers */
+#define BCMA_CORE_PCI_DLLP_LCREG		0x100	/* Link Control */
+#define BCMA_CORE_PCI_DLLP_LSREG		0x104	/* Link Status */
+#define BCMA_CORE_PCI_DLLP_LAREG		0x108	/* Link Attention */
+#define  BCMA_CORE_PCI_DLLP_LSREG_LINKUP	(1 << 16)
+#define BCMA_CORE_PCI_DLLP_LAMASKREG		0x10C	/* Link Attention Mask */
+#define BCMA_CORE_PCI_DLLP_NEXTTXSEQNUMREG	0x110	/* Next Tx Seq Num */
+#define BCMA_CORE_PCI_DLLP_ACKEDTXSEQNUMREG	0x114	/* Acked Tx Seq Num */
+#define BCMA_CORE_PCI_DLLP_PURGEDTXSEQNUMREG	0x118	/* Purged Tx Seq Num */
+#define BCMA_CORE_PCI_DLLP_RXSEQNUMREG		0x11C	/* Rx Sequence Number */
+#define BCMA_CORE_PCI_DLLP_LRREG		0x120	/* Link Replay */
+#define BCMA_CORE_PCI_DLLP_LACKTOREG		0x124	/* Link Ack Timeout */
+#define BCMA_CORE_PCI_DLLP_PMTHRESHREG		0x128	/* Power Management Threshold */
+#define BCMA_CORE_PCI_DLLP_RTRYWPREG		0x12C	/* Retry buffer write ptr */
+#define BCMA_CORE_PCI_DLLP_RTRYRPREG		0x130	/* Retry buffer Read ptr */
+#define BCMA_CORE_PCI_DLLP_RTRYPPREG		0x134	/* Retry buffer Purged ptr */
+#define BCMA_CORE_PCI_DLLP_RTRRWREG		0x138	/* Retry buffer Read/Write */
+#define BCMA_CORE_PCI_DLLP_ECTHRESHREG		0x13C	/* Error Count Threshold */
+#define BCMA_CORE_PCI_DLLP_TLPERRCTRREG		0x140	/* TLP Error Counter */
+#define BCMA_CORE_PCI_DLLP_ERRCTRREG		0x144	/* Error Counter */
+#define BCMA_CORE_PCI_DLLP_NAKRXCTRREG		0x148	/* NAK Received Counter */
+#define BCMA_CORE_PCI_DLLP_TESTREG		0x14C	/* Test */
+#define BCMA_CORE_PCI_DLLP_PKTBIST		0x150	/* Packet BIST */
+#define BCMA_CORE_PCI_DLLP_PCIE11		0x154	/* DLLP PCIE 1.1 reg */
+
+/* SERDES RX registers */
+#define BCMA_CORE_PCI_SERDES_RX_CTRL		1	/* Rx cntrl */
+#define  BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE	0x80	/* rxpolarity_force */
+#define  BCMA_CORE_PCI_SERDES_RX_CTRL_POLARITY	0x40	/* rxpolarity_value */
+#define BCMA_CORE_PCI_SERDES_RX_TIMER1		2	/* Rx Timer1 */
+#define BCMA_CORE_PCI_SERDES_RX_CDR		6	/* CDR */
+#define BCMA_CORE_PCI_SERDES_RX_CDRBW		7	/* CDR BW */
+
+/* SERDES PLL registers */
+#define BCMA_CORE_PCI_SERDES_PLL_CTRL		1	/* PLL control reg */
+#define BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN	0x4000	/* bit 14 is FREQDET on */
+
 /* PCIcore specific boardflags */
 #define BCMA_CORE_PCI_BFL_NOPCI			0x00000400 /* Board leaves PCI floating */
 
-- 
1.7.5.4

---

From: Hauke Mehrtens <[email-masked]>
Date: Tue, 31 Jan 2012 00:03:34 +0100

bcma_core_pci_hostmode_init() has to be in __devinit as it will call a
function in that section and so all functions calling it also have to
be in __devinit.

Signed-off-by: Hauke Mehrtens <[email-masked]>
---
 drivers/bcma/bcma_private.h          |    4 ++--
 drivers/bcma/driver_pci.c            |    6 +++---
 drivers/bcma/driver_pci_host.c       |    2 +-
 drivers/bcma/host_pci.c              |    4 ++--
 drivers/bcma/main.c                  |    2 +-
 include/linux/bcma/bcma_driver_pci.h |    2 +-
 6 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h
index 6109da5..63c5242 100644
--- a/drivers/bcma/bcma_private.h
+++ b/drivers/bcma/bcma_private.h
@@ -13,7 +13,7 @@
 struct bcma_bus;
 
 /* main.c */
-int bcma_bus_register(struct bcma_bus *bus);
+int __devinit 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,
@@ -52,7 +52,7 @@ extern void __exit bcma_host_pci_exit(void);
 u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address);
 
 #ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
-void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc);
+void __devinit bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc);
 #endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */
 
 #endif
diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c
index 20ceebd..161c8ba 100644
--- a/drivers/bcma/driver_pci.c
+++ b/drivers/bcma/driver_pci.c
@@ -174,12 +174,12 @@ static void bcma_pcicore_serdes_workaround(struct bcma_drv_pci *pc)
  * Init.
  **************************************************/
 
-static void bcma_core_pci_clientmode_init(struct bcma_drv_pci *pc)
+static void __devinit bcma_core_pci_clientmode_init(struct bcma_drv_pci *pc)
 {
 	bcma_pcicore_serdes_workaround(pc);
 }
 
-static bool bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc)
+static bool __devinit bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc)
 {
 	struct bcma_bus *bus = pc->core->bus;
 	u16 chipid_top;
@@ -204,7 +204,7 @@ static bool bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc)
 	return true;
 }
 
-void bcma_core_pci_init(struct bcma_drv_pci *pc)
+void __devinit bcma_core_pci_init(struct bcma_drv_pci *pc)
 {
 	if (pc->setup_done)
 		return;
diff --git a/drivers/bcma/driver_pci_host.c b/drivers/bcma/driver_pci_host.c
index eb332b7..99e040b 100644
--- a/drivers/bcma/driver_pci_host.c
+++ b/drivers/bcma/driver_pci_host.c
@@ -8,7 +8,7 @@
 #include "bcma_private.h"
 #include <linux/bcma/bcma.h>
 
-void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc)
+void __devinit bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc)
 {
 	pr_err("No support for PCI core in hostmode yet\n");
 }
diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c
index f59244e..e3928d6 100644
--- a/drivers/bcma/host_pci.c
+++ b/drivers/bcma/host_pci.c
@@ -154,8 +154,8 @@ const struct bcma_host_ops bcma_host_pci_ops = {
 	.awrite32	= bcma_host_pci_awrite32,
 };
 
-static int bcma_host_pci_probe(struct pci_dev *dev,
-			     const struct pci_device_id *id)
+static int __devinit bcma_host_pci_probe(struct pci_dev *dev,
+					 const struct pci_device_id *id)
 {
 	struct bcma_bus *bus;
 	int err = -ENOMEM;
diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c
index febbc0a..3363036 100644
--- a/drivers/bcma/main.c
+++ b/drivers/bcma/main.c
@@ -132,7 +132,7 @@ static void bcma_unregister_cores(struct bcma_bus *bus)
 	}
 }
 
-int bcma_bus_register(struct bcma_bus *bus)
+int __devinit bcma_bus_register(struct bcma_bus *bus)
 {
 	int err;
 	struct bcma_device *core;
diff --git a/include/linux/bcma/bcma_driver_pci.h b/include/linux/bcma/bcma_driver_pci.h
index 67ea7be..679d4ca 100644
--- a/include/linux/bcma/bcma_driver_pci.h
+++ b/include/linux/bcma/bcma_driver_pci.h
@@ -169,7 +169,7 @@ struct bcma_drv_pci {
 #define pcicore_read32(pc, offset)		bcma_read32((pc)->core, offset)
 #define pcicore_write32(pc, offset, val)	bcma_write32((pc)->core, offset, val)
 
-extern void bcma_core_pci_init(struct bcma_drv_pci *pc);
+extern void __devinit bcma_core_pci_init(struct bcma_drv_pci *pc);
 extern int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc,
 				 struct bcma_device *core, bool enable);
 
-- 
1.7.5.4

---

From: Hauke Mehrtens <[email-masked]>
Date: Tue, 31 Jan 2012 00:03:36 +0100

If we have two bcma buses on one computer the second will not work
without this patch. Now each bus gets an own number.

Signed-off-by: Hauke Mehrtens <[email-masked]>
---
 drivers/bcma/main.c       |   12 +++++++++++-
 include/linux/bcma/bcma.h |    1 +
 2 files changed, 12 insertions(+), 1 deletions(-)

diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c
index 3363036..bcd1c01 100644
--- a/drivers/bcma/main.c
+++ b/drivers/bcma/main.c
@@ -13,6 +13,12 @@
 MODULE_DESCRIPTION("Broadcom's specific AMBA driver");
 MODULE_LICENSE("GPL");
 
+/* contains the number the next bus should get. */
+static unsigned int bcma_bus_next_num = 0;
+
+/* bcma_buses_mutex locks the bcma_bus_next_num */
+static DEFINE_MUTEX(bcma_buses_mutex);
+
 static int bcma_bus_match(struct device *dev, struct device_driver *drv);
 static int bcma_device_probe(struct device *dev);
 static int bcma_device_remove(struct device *dev);
@@ -93,7 +99,7 @@ static int bcma_register_cores(struct bcma_bus *bus)
 
 		core->dev.release = bcma_release_core_dev;
 		core->dev.bus = &bcma_bus_type;
-		dev_set_name(&core->dev, "bcma%d:%d", 0/*bus->num*/, dev_id);
+		dev_set_name(&core->dev, "bcma%d:%d", bus->num, dev_id);
 
 		switch (bus->hosttype) {
 		case BCMA_HOSTTYPE_PCI:
@@ -137,6 +143,10 @@ int __devinit bcma_bus_register(struct bcma_bus *bus)
 	int err;
 	struct bcma_device *core;
 
+	mutex_lock(&bcma_buses_mutex);
+	bus->num = bcma_bus_next_num++;
+	mutex_unlock(&bcma_buses_mutex);
+
 	/* Scan for devices (cores) */
 	err = bcma_bus_scan(bus);
 	if (err) {
diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h
index 024a6e2..b9f65fb 100644
--- a/include/linux/bcma/bcma.h
+++ b/include/linux/bcma/bcma.h
@@ -196,6 +196,7 @@ struct bcma_bus {
 	struct list_head cores;
 	u8 nr_cores;
 	u8 init_done:1;
+	u8 num;
 
 	struct bcma_drv_cc drv_cc;
 	struct bcma_drv_pci drv_pci;
-- 
1.7.5.4

---

From: Hauke Mehrtens <[email-masked]>
Date: Tue, 31 Jan 2012 00:03:35 +0100

Some SoCs have a PCIe host controller to make it possible to attach
some other devices to it, like an other Wifi card.
This code was tested with an Netgear WNDR3400 (bcm4716 based), but
should work with all bcma based SoCs.

Signed-off-by: Hauke Mehrtens <[email-masked]>
---
 arch/mips/pci/pci-bcm47xx.c          |   49 +++-
 drivers/bcma/bcma_private.h          |    1 +
 drivers/bcma/driver_pci.c            |   38 +--
 drivers/bcma/driver_pci_host.c       |  576 +++++++++++++++++++++++++++++++++-
 include/linux/bcma/bcma_driver_pci.h |   38 +++
 include/linux/bcma/bcma_regs.h       |   27 ++
 6 files changed, 690 insertions(+), 39 deletions(-)

diff --git a/arch/mips/pci/pci-bcm47xx.c b/arch/mips/pci/pci-bcm47xx.c
index 400535a..c682468 100644
--- a/arch/mips/pci/pci-bcm47xx.c
+++ b/arch/mips/pci/pci-bcm47xx.c
@@ -25,6 +25,7 @@
 #include <linux/types.h>
 #include <linux/pci.h>
 #include <linux/ssb/ssb.h>
+#include <linux/bcma/bcma.h>
 #include <bcm47xx.h>
 
 int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
@@ -32,15 +33,12 @@ int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 	return 0;
 }
 
-int pcibios_plat_dev_init(struct pci_dev *dev)
-{
 #ifdef CONFIG_BCM47XX_SSB
+static int bcm47xx_pcibios_plat_dev_init_ssb(struct pci_dev *dev)
+{
 	int res;
 	u8 slot, pin;
 
-	if (bcm47xx_bus_type !=  BCM47XX_BUS_TYPE_SSB)
-		return 0;
-
 	res = ssb_pcibios_plat_dev_init(dev);
 	if (res < 0) {
 		printk(KERN_ALERT "PCI: Failed to init device %s\n",
@@ -60,6 +58,47 @@ int pcibios_plat_dev_init(struct pci_dev *dev)
 	}
 
 	dev->irq = res;
+	return 0;
+}
 #endif
+
+#ifdef CONFIG_BCM47XX_BCMA
+static int bcm47xx_pcibios_plat_dev_init_bcma(struct pci_dev *dev)
+{
+	int res;
+
+	res = bcma_core_pci_plat_dev_init(dev);
+	if (res < 0) {
+		printk(KERN_ALERT "PCI: Failed to init device %s\n",
+		       pci_name(dev));
+		return res;
+	}
+
+	res = bcma_core_pci_pcibios_map_irq(dev);
+
+	/* IRQ-0 and IRQ-1 are software interrupts. */
+	if (res < 2) {
+		printk(KERN_ALERT "PCI: Failed to map IRQ of device %s\n",
+		       pci_name(dev));
+		return res;
+	}
+
+	dev->irq = res;
 	return 0;
 }
+#endif
+
+int pcibios_plat_dev_init(struct pci_dev *dev)
+{
+#ifdef CONFIG_BCM47XX_SSB
+	if (bcm47xx_bus_type ==  BCM47XX_BUS_TYPE_SSB)
+		return bcm47xx_pcibios_plat_dev_init_ssb(dev);
+	else
+#endif
+#ifdef CONFIG_BCM47XX_BCMA
+	if  (bcm47xx_bus_type ==  BCM47XX_BUS_TYPE_BCMA)
+		return bcm47xx_pcibios_plat_dev_init_bcma(dev);
+	else
+#endif
+		return 0;
+}
diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h
index 63c5242..b81755b 100644
--- a/drivers/bcma/bcma_private.h
+++ b/drivers/bcma/bcma_private.h
@@ -52,6 +52,7 @@ extern void __exit bcma_host_pci_exit(void);
 u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address);
 
 #ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
+bool __devinit bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc);
 void __devinit bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc);
 #endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */
 
diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c
index 161c8ba..82e4898 100644
--- a/drivers/bcma/driver_pci.c
+++ b/drivers/bcma/driver_pci.c
@@ -2,7 +2,7 @@
  * Broadcom specific AMBA
  * PCI Core
  *
- * Copyright 2005, Broadcom Corporation
+ * Copyright 2005, 2011, Broadcom Corporation
  * Copyright 2006, 2007, Michael Buesch <[email-masked]>
  * Copyright 2011, 2012, Hauke Mehrtens <[email-masked]>
  *
@@ -179,47 +179,19 @@ static void __devinit bcma_core_pci_clientmode_init(struct bcma_drv_pci *pc)
 	bcma_pcicore_serdes_workaround(pc);
 }
 
-static bool __devinit bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc)
-{
-	struct bcma_bus *bus = pc->core->bus;
-	u16 chipid_top;
-
-	chipid_top = (bus->chipinfo.id & 0xFF00);
-	if (chipid_top != 0x4700 &&
-	    chipid_top != 0x5300)
-		return false;
-
-#ifdef CONFIG_SSB_DRIVER_PCICORE
-	if (bus->sprom.boardflags_lo & SSB_BFL_NOPCI)
-		return false;
-#endif /* CONFIG_SSB_DRIVER_PCICORE */
-
-#if 0
-	/* TODO: on BCMA we use address from EROM instead of magic formula */
-	u32 tmp;
-	return !mips_busprobe32(tmp, (bus->mmio +
-		(pc->core->core_index * BCMA_CORE_SIZE)));
-#endif
-
-	return true;
-}
-
 void __devinit 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
+	pc->hostmode = bcma_core_pci_is_in_hostmode(pc);
+	if (pc->hostmode)
 		bcma_core_pci_hostmode_init(pc);
-#else
-		pr_err("Driver compiled without support for hostmode PCI\n");
 #endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */
-	} else {
-		bcma_core_pci_clientmode_init(pc);
-	}
 
-	pc->setup_done = true;
+	if (!pc->hostmode)
+		bcma_core_pci_clientmode_init(pc);
 }
 
 int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core,
diff --git a/drivers/bcma/driver_pci_host.c b/drivers/bcma/driver_pci_host.c
index 99e040b..4e20bcf 100644
--- a/drivers/bcma/driver_pci_host.c
+++ b/drivers/bcma/driver_pci_host.c
@@ -2,13 +2,587 @@
  * Broadcom specific AMBA
  * PCI Core in hostmode
  *
+ * Copyright 2005 - 2011, Broadcom Corporation
+ * Copyright 2006, 2007, Michael Buesch <[email-masked]>
+ * Copyright 2011, 2012, Hauke Mehrtens <[email-masked]>
+ *
  * Licensed under the GNU/GPL. See COPYING for details.
  */
 
 #include "bcma_private.h"
+#include <linux/export.h>
 #include <linux/bcma/bcma.h>
+#include <asm/paccess.h>
+
+/* Probe a 32bit value on the bus and catch bus exceptions.
+ * Returns nonzero on a bus exception.
+ * This is MIPS specific */
+#define mips_busprobe32(val, addr)	get_dbe((val), ((u32 *)(addr)))
+
+/* Assume one-hot slot wiring */
+#define BCMA_PCI_SLOT_MAX	16
+#define	PCI_CONFIG_SPACE_SIZE	256
+
+bool __devinit bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc)
+{
+	struct bcma_bus *bus = pc->core->bus;
+	u16 chipid_top;
+	u32 tmp;
+
+	chipid_top = (bus->chipinfo.id & 0xFF00);
+	if (chipid_top != 0x4700 &&
+	    chipid_top != 0x5300)
+		return false;
+
+	if (bus->sprom.boardflags_lo & BCMA_CORE_PCI_BFL_NOPCI) {
+		pr_info("This PCI core is disabled and not working\n");
+		return false;
+	}
+
+	bcma_core_enable(pc->core, 0);
+
+	return !mips_busprobe32(tmp, pc->core->io_addr);
+}
+
+static u32 bcma_pcie_read_config(struct bcma_drv_pci *pc, u32 address)
+{
+	pcicore_write32(pc, BCMA_CORE_PCI_CONFIG_ADDR, address);
+	pcicore_read32(pc, BCMA_CORE_PCI_CONFIG_ADDR);
+	return pcicore_read32(pc, BCMA_CORE_PCI_CONFIG_DATA);
+}
+
+static void bcma_pcie_write_config(struct bcma_drv_pci *pc, u32 address,
+				   u32 data)
+{
+	pcicore_write32(pc, BCMA_CORE_PCI_CONFIG_ADDR, address);
+	pcicore_read32(pc, BCMA_CORE_PCI_CONFIG_ADDR);
+	pcicore_write32(pc, BCMA_CORE_PCI_CONFIG_DATA, data);
+}
+
+static u32 bcma_get_cfgspace_addr(struct bcma_drv_pci *pc, unsigned int dev,
+			     unsigned int func, unsigned int off)
+{
+	u32 addr = 0;
+
+	/* Issue config commands only when the data link is up (atleast
+	 * one external pcie device is present).
+	 */
+	if (dev >= 2 || !(bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_LSREG)
+			  & BCMA_CORE_PCI_DLLP_LSREG_LINKUP))
+		goto out;
+
+	/* Type 0 transaction */
+	/* Slide the PCI window to the appropriate slot */
+	pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI1, BCMA_CORE_PCI_SBTOPCI_CFG0);
+	/* Calculate the address */
+	addr = pc->host_controller->host_cfg_addr;
+	addr |= (dev << BCMA_CORE_PCI_CFG_SLOT_SHIFT);
+	addr |= (func << BCMA_CORE_PCI_CFG_FUN_SHIFT);
+	addr |= (off & ~3);
+
+out:
+	return addr;
+}
+
+static int bcma_extpci_read_config(struct bcma_drv_pci *pc, unsigned int dev,
+				  unsigned int func, unsigned int off,
+				  void *buf, int len)
+{
+	int err = -EINVAL;
+	u32 addr, val;
+	void __iomem *mmio = 0;
+
+	WARN_ON(!pc->hostmode);
+	if (unlikely(len != 1 && len != 2 && len != 4))
+		goto out;
+	if (dev == 0) {
+		/* we support only two functions on device 0 */
+		if (func > 1)
+			return -EINVAL;
+
+		/* accesses to config registers with offsets >= 256
+		 * requires indirect access.
+		 */
+		if (off >= PCI_CONFIG_SPACE_SIZE) {
+			addr = (func << 12);
+			addr |= (off & 0x0FFF);
+			val = bcma_pcie_read_config(pc, addr);
+		} else {
+			addr = BCMA_CORE_PCI_PCICFG0;
+			addr |= (func << 8);
+			addr |= (off & 0xfc);
+			val = pcicore_read32(pc, addr);
+		}
+	} else {
+		addr = bcma_get_cfgspace_addr(pc, dev, func, off);
+		if (unlikely(!addr))
+			goto out;
+		err = -ENOMEM;
+		mmio = ioremap_nocache(addr, len);
+		if (!mmio)
+			goto out;
+
+		if (mips_busprobe32(val, mmio)) {
+			val = 0xffffffff;
+			goto unmap;
+		}
+
+		val = readl(mmio);
+	}
+	val >>= (8 * (off & 3));
+
+	switch (len) {
+	case 1:
+		*((u8 *)buf) = (u8)val;
+		break;
+	case 2:
+		*((u16 *)buf) = (u16)val;
+		break;
+	case 4:
+		*((u32 *)buf) = (u32)val;
+		break;
+	}
+	err = 0;
+unmap:
+	if (mmio)
+		iounmap(mmio);
+out:
+	return err;
+}
+
+static int bcma_extpci_write_config(struct bcma_drv_pci *pc, unsigned int dev,
+				   unsigned int func, unsigned int off,
+				   const void *buf, int len)
+{
+	int err = -EINVAL;
+	u32 addr = 0, val = 0;
+	void __iomem *mmio = 0;
+	u16 chipid = pc->core->bus->chipinfo.id;
+
+	WARN_ON(!pc->hostmode);
+	if (unlikely(len != 1 && len != 2 && len != 4))
+		goto out;
+	if (dev == 0) {
+		/* accesses to config registers with offsets >= 256
+		 * requires indirect access.
+		 */
+		if (off < PCI_CONFIG_SPACE_SIZE) {
+			addr = pc->core->addr + BCMA_CORE_PCI_PCICFG0;
+			addr |= (func << 8);
+			addr |= (off & 0xfc);
+			mmio = ioremap_nocache(addr, len);
+			if (!mmio)
+				goto out;
+		}
+	} else {
+		addr = bcma_get_cfgspace_addr(pc, dev, func, off);
+		if (unlikely(!addr))
+			goto out;
+		err = -ENOMEM;
+		mmio = ioremap_nocache(addr, len);
+		if (!mmio)
+			goto out;
+
+		if (mips_busprobe32(val, mmio)) {
+			val = 0xffffffff;
+			goto unmap;
+		}
+	}
+
+	switch (len) {
+	case 1:
+		val = readl(mmio);
+		val &= ~(0xFF << (8 * (off & 3)));
+		val |= *((const u8 *)buf) << (8 * (off & 3));
+		break;
+	case 2:
+		val = readl(mmio);
+		val &= ~(0xFFFF << (8 * (off & 3)));
+		val |= *((const u16 *)buf) << (8 * (off & 3));
+		break;
+	case 4:
+		val = *((const u32 *)buf);
+		break;
+	}
+	if (dev == 0 && !addr) {
+		/* accesses to config registers with offsets >= 256
+		 * requires indirect access.
+		 */
+		addr = (func << 12);
+		addr |= (off & 0x0FFF);
+		bcma_pcie_write_config(pc, addr, val);
+	} else {
+		writel(val, mmio);
+
+		if (chipid == 0x4716 || chipid == 0x4748)
+			readl(mmio);
+	}
+
+	err = 0;
+unmap:
+	if (mmio)
+		iounmap(mmio);
+out:
+	return err;
+}
+
+static int bcma_core_pci_hostmode_read_config(struct pci_bus *bus,
+					      unsigned int devfn,
+					      int reg, int size, u32 *val)
+{
+	unsigned long flags;
+	int err;
+	struct bcma_drv_pci *pc;
+	struct bcma_drv_pci_host *pc_host;
+
+	pc_host = container_of(bus->ops, struct bcma_drv_pci_host, pci_ops);
+	pc = pc_host->pdev;
+
+	spin_lock_irqsave(&pc_host->cfgspace_lock, flags);
+	err = bcma_extpci_read_config(pc, PCI_SLOT(devfn),
+				     PCI_FUNC(devfn), reg, val, size);
+	spin_unlock_irqrestore(&pc_host->cfgspace_lock, flags);
+
+	return err ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL;
+}
+
+static int bcma_core_pci_hostmode_write_config(struct pci_bus *bus,
+					       unsigned int devfn,
+					       int reg, int size, u32 val)
+{
+	unsigned long flags;
+	int err;
+	struct bcma_drv_pci *pc;
+	struct bcma_drv_pci_host *pc_host;
+
+	pc_host = container_of(bus->ops, struct bcma_drv_pci_host, pci_ops);
+	pc = pc_host->pdev;
+
+	spin_lock_irqsave(&pc_host->cfgspace_lock, flags);
+	err = bcma_extpci_write_config(pc, PCI_SLOT(devfn),
+				      PCI_FUNC(devfn), reg, &val, size);
+	spin_unlock_irqrestore(&pc_host->cfgspace_lock, flags);
+
+	return err ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL;
+}
+
+/* return cap_offset if requested capability exists in the PCI config space */
+static u8 __devinit bcma_find_pci_capability(struct bcma_drv_pci *pc,
+					     unsigned int dev,
+					     unsigned int func, u8 req_cap_id,
+					     unsigned char *buf, u32 *buflen)
+{
+	u8 cap_id;
+	u8 cap_ptr = 0;
+	u32 bufsize;
+	u8 byte_val;
+
+	/* check for Header type 0 */
+	bcma_extpci_read_config(pc, dev, func, PCI_HEADER_TYPE, &byte_val,
+				sizeof(u8));
+	if ((byte_val & 0x7f) != PCI_HEADER_TYPE_NORMAL)
+		return cap_ptr;
+
+	/* check if the capability pointer field exists */
+	bcma_extpci_read_config(pc, dev, func, PCI_STATUS, &byte_val,
+				sizeof(u8));
+	if (!(byte_val & PCI_STATUS_CAP_LIST))
+		return cap_ptr;
+
+	/* check if the capability pointer is 0x00 */
+	bcma_extpci_read_config(pc, dev, func, PCI_CAPABILITY_LIST, &cap_ptr,
+				sizeof(u8));
+	if (cap_ptr == 0x00)
+		return cap_ptr;
+
+	/* loop thr'u the capability list and see if the requested capabilty
+	 * exists */
+	bcma_extpci_read_config(pc, dev, func, cap_ptr, &cap_id, sizeof(u8));
+	while (cap_id != req_cap_id) {
+		bcma_extpci_read_config(pc, dev, func, cap_ptr + 1, &cap_ptr,
+					sizeof(u8));
+		if (cap_ptr == 0x00)
+			return cap_ptr;
+		bcma_extpci_read_config(pc, dev, func, cap_ptr, &cap_id,
+					sizeof(u8));
+	}
+
+	/* found the caller requested capability */
+	if ((buf != NULL) && (buflen != NULL)) {
+		u8 cap_data;
+
+		bufsize = *buflen;
+		if (!bufsize)
+			return cap_ptr;
+
+		*buflen = 0;
+
+		/* copy the cpability data excluding cap ID and next ptr */
+		cap_data = cap_ptr + 2;
+		if ((bufsize + cap_data)  > PCI_CONFIG_SPACE_SIZE)
+			bufsize = PCI_CONFIG_SPACE_SIZE - cap_data;
+		*buflen = bufsize;
+		while (bufsize--) {
+			bcma_extpci_read_config(pc, dev, func, cap_data, buf,
+						sizeof(u8));
+			cap_data++;
+			buf++;
+		}
+	}
+
+	return cap_ptr;
+}
+
+/* If the root port is capable of returning Config Request
+ * Retry Status (CRS) Completion Status to software then
+ * enable the feature.
+ */
+static void __devinit bcma_core_pci_enable_crs(struct bcma_drv_pci *pc)
+{
+	u8 cap_ptr, root_ctrl, root_cap, dev;
+	u16 val16;
+	int i;
+
+	cap_ptr = bcma_find_pci_capability(pc, 0, 0, PCI_CAP_ID_EXP, NULL,
+					   NULL);
+	root_cap = cap_ptr + PCI_EXP_RTCAP;
+	bcma_extpci_read_config(pc, 0, 0, root_cap, &val16, sizeof(u16));
+	if (val16 & BCMA_CORE_PCI_RC_CRS_VISIBILITY) {
+		/* Enable CRS software visibility */
+		root_ctrl = cap_ptr + PCI_EXP_RTCTL;
+		val16 = PCI_EXP_RTCTL_CRSSVE;
+		bcma_extpci_read_config(pc, 0, 0, root_ctrl, &val16,
+					sizeof(u16));
+
+		/* Initiate a configuration request to read the vendor id
+		 * field of the device function's config space header after
+		 * 100 ms wait time from the end of Reset. If the device is
+		 * not done with its internal initialization, it must at
+		 * least return a completion TLP, with a completion status
+		 * of "Configuration Request Retry Status (CRS)". The root
+		 * complex must complete the request to the host by returning
+		 * a read-data value of 0001h for the Vendor ID field and
+		 * all 1s for any additional bytes included in the request.
+		 * Poll using the config reads for max wait time of 1 sec or
+		 * until we receive the successful completion status. Repeat
+		 * the procedure for all the devices.
+		 */
+		for (dev = 1; dev < BCMA_PCI_SLOT_MAX; dev++) {
+			for (i = 0; i < 100000; i++) {
+				bcma_extpci_read_config(pc, dev, 0,
+							PCI_VENDOR_ID, &val16,
+							sizeof(val16));
+				if (val16 != 0x1)
+					break;
+				udelay(10);
+			}
+			if (val16 == 0x1)
+				pr_err("PCI: Broken device in slot %d\n", dev);
+		}
+	}
+}
 
 void __devinit bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc)
 {
-	pr_err("No support for PCI core in hostmode yet\n");
+	struct bcma_bus *bus = pc->core->bus;
+	struct bcma_drv_pci_host *pc_host;
+	u32 tmp;
+	u32 pci_membase_1G;
+	unsigned long io_map_base;
+
+	pr_info("PCIEcore in host mode found\n");
+
+	pc_host = kzalloc(sizeof(*pc_host), GFP_KERNEL);
+	if (!pc_host)  {
+		pr_err("can not allocate memory");
+		return;
+	}
+
+	pc->host_controller = pc_host;
+	pc_host->pci_controller.io_resource = &pc_host->io_resource;
+	pc_host->pci_controller.mem_resource = &pc_host->mem_resource;
+	pc_host->pci_controller.pci_ops = &pc_host->pci_ops;
+	pc_host->pdev = pc;
+
+	pci_membase_1G = BCMA_SOC_PCI_DMA;
+	pc_host->host_cfg_addr = BCMA_SOC_PCI_CFG;
+
+	pc_host->pci_ops.read = bcma_core_pci_hostmode_read_config;
+	pc_host->pci_ops.write = bcma_core_pci_hostmode_write_config;
+
+	pc_host->mem_resource.name = "BCMA PCIcore external memory",
+	pc_host->mem_resource.start = BCMA_SOC_PCI_DMA;
+	pc_host->mem_resource.end = BCMA_SOC_PCI_DMA + BCMA_SOC_PCI_DMA_SZ - 1;
+	pc_host->mem_resource.flags = IORESOURCE_MEM | IORESOURCE_PCI_FIXED;
+
+	pc_host->io_resource.name = "BCMA PCIcore external I/O",
+	pc_host->io_resource.start = 0x100;
+	pc_host->io_resource.end = 0x7FF;
+	pc_host->io_resource.flags = IORESOURCE_IO | IORESOURCE_PCI_FIXED;
+
+	/* Reset RC */
+	udelay(3000);
+	pcicore_write32(pc, BCMA_CORE_PCI_CTL, BCMA_CORE_PCI_CTL_RST_OE);
+	udelay(1000);
+	pcicore_write32(pc, BCMA_CORE_PCI_CTL, BCMA_CORE_PCI_CTL_RST |
+			BCMA_CORE_PCI_CTL_RST_OE);
+
+	/* 64 MB I/O access window. On 4716, use
+	 * sbtopcie0 to access the device registers. We
+	 * can't use address match 2 (1 GB window) region
+	 * as mips can't generate 64-bit address on the
+	 * backplane.
+	 */
+	if (bus->chipinfo.id == 0x4716 || bus->chipinfo.id == 0x4748) {
+		pc_host->mem_resource.start = BCMA_SOC_PCI_MEM;
+		pc_host->mem_resource.end = BCMA_SOC_PCI_MEM +
+					    BCMA_SOC_PCI_MEM_SZ - 1;
+		pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI0,
+				BCMA_CORE_PCI_SBTOPCI_MEM | BCMA_SOC_PCI_MEM);
+	} else if (bus->chipinfo.id == 0x5300) {
+		tmp = BCMA_CORE_PCI_SBTOPCI_MEM;
+		tmp |= BCMA_CORE_PCI_SBTOPCI_PREF;
+		tmp |= BCMA_CORE_PCI_SBTOPCI_BURST;
+		if (pc->core->core_unit == 0) {
+			pc_host->mem_resource.start = BCMA_SOC_PCI_MEM;
+			pc_host->mem_resource.end = BCMA_SOC_PCI_MEM +
+						    BCMA_SOC_PCI_MEM_SZ - 1;
+			pci_membase_1G = BCMA_SOC_PCIE_DMA_H32;
+			pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI0,
+					tmp | BCMA_SOC_PCI_MEM);
+		} else if (pc->core->core_unit == 1) {
+			pc_host->mem_resource.start = BCMA_SOC_PCI1_MEM;
+			pc_host->mem_resource.end = BCMA_SOC_PCI1_MEM +
+						    BCMA_SOC_PCI_MEM_SZ - 1;
+			pci_membase_1G = BCMA_SOC_PCIE1_DMA_H32;
+			pc_host->host_cfg_addr = BCMA_SOC_PCI1_CFG;
+			pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI0,
+					tmp | BCMA_SOC_PCI1_MEM);
+		}
+	} else
+		pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI0,
+				BCMA_CORE_PCI_SBTOPCI_IO);
+
+	/* 64 MB configuration access window */
+	pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI1, BCMA_CORE_PCI_SBTOPCI_CFG0);
+
+	/* 1 GB memory access window */
+	pcicore_write32(pc, BCMA_CORE_PCI_SBTOPCI2,
+			BCMA_CORE_PCI_SBTOPCI_MEM | pci_membase_1G);
+
+
+	/* As per PCI Express Base Spec 1.1 we need to wait for
+	 * at least 100 ms from the end of a reset (cold/warm/hot)
+	 * before issuing configuration requests to PCI Express
+	 * devices.
+	 */
+	udelay(100000);
+
+	bcma_core_pci_enable_crs(pc);
+
+	/* Enable PCI bridge BAR0 memory & master access */
+	tmp = PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
+	bcma_extpci_write_config(pc, 0, 0, PCI_COMMAND, &tmp, sizeof(tmp));
+
+	/* Enable PCI interrupts */
+	pcicore_write32(pc, BCMA_CORE_PCI_IMASK, BCMA_CORE_PCI_IMASK_INTA);
+
+	/* Ok, ready to run, register it to the system.
+	 * The following needs change, if we want to port hostmode
+	 * to non-MIPS platform. */
+	io_map_base = (unsigned long)ioremap_nocache(BCMA_SOC_PCI_MEM,
+						     0x04000000);
+	pc_host->pci_controller.io_map_base = io_map_base;
+	set_io_port_base(pc_host->pci_controller.io_map_base);
+	/* Give some time to the PCI controller to configure itself with the new
+	 * values. Not waiting at this point causes crashes of the machine. */
+	mdelay(10);
+	register_pci_controller(&pc_host->pci_controller);
+	return;
+}
+
+/* Early PCI fixup for a device on the PCI-core bridge. */
+static void bcma_core_pci_fixup_pcibridge(struct pci_dev *dev)
+{
+	if (dev->bus->ops->read != bcma_core_pci_hostmode_read_config) {
+		/* This is not a device on the PCI-core bridge. */
+		return;
+	}
+	if (PCI_SLOT(dev->devfn) != 0)
+		return;
+
+	pr_info("PCI: Fixing up bridge %s\n", pci_name(dev));
+
+	/* Enable PCI bridge bus mastering and memory space */
+	pci_set_master(dev);
+	if (pcibios_enable_device(dev, ~0) < 0) {
+		pr_err("PCI: BCMA bridge enable failed\n");
+		return;
+	}
+
+	/* Enable PCI bridge BAR1 prefetch and burst */
+	pci_write_config_dword(dev, BCMA_PCI_BAR1_CONTROL, 3);
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, bcma_core_pci_fixup_pcibridge);
+
+/* Early PCI fixup for all PCI-cores to set the correct memory address. */
+static void bcma_core_pci_fixup_addresses(struct pci_dev *dev)
+{
+	struct resource *res;
+	int pos;
+
+	if (dev->bus->ops->read != bcma_core_pci_hostmode_read_config) {
+		/* This is not a device on the PCI-core bridge. */
+		return;
+	}
+	if (PCI_SLOT(dev->devfn) == 0)
+		return;
+
+	pr_info("PCI: Fixing up addresses %s\n", pci_name(dev));
+
+	for (pos = 0; pos < 6; pos++) {
+		res = &dev->resource[pos];
+		if (res->flags & (IORESOURCE_IO | IORESOURCE_MEM))
+			pci_assign_resource(dev, pos);
+	}
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, bcma_core_pci_fixup_addresses);
+
+/* This function is called when doing a pci_enable_device().
+ * We must first check if the device is a device on the PCI-core bridge. */
+int bcma_core_pci_plat_dev_init(struct pci_dev *dev)
+{
+	struct bcma_drv_pci_host *pc_host;
+
+	if (dev->bus->ops->read != bcma_core_pci_hostmode_read_config) {
+		/* This is not a device on the PCI-core bridge. */
+		return -ENODEV;
+	}
+	pc_host = container_of(dev->bus->ops, struct bcma_drv_pci_host,
+			       pci_ops);
+
+	pr_info("PCI: Fixing up device %s\n", pci_name(dev));
+
+	/* Fix up interrupt lines */
+	dev->irq = bcma_core_mips_irq(pc_host->pdev->core) + 2;
+	pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
+
+	return 0;
+}
+EXPORT_SYMBOL(bcma_core_pci_plat_dev_init);
+
+/* PCI device IRQ mapping. */
+int bcma_core_pci_pcibios_map_irq(const struct pci_dev *dev)
+{
+	struct bcma_drv_pci_host *pc_host;
+
+	if (dev->bus->ops->read != bcma_core_pci_hostmode_read_config) {
+		/* This is not a device on the PCI-core bridge. */
+		return -ENODEV;
+	}
+
+	pc_host = container_of(dev->bus->ops, struct bcma_drv_pci_host,
+			       pci_ops);
+	return bcma_core_mips_irq(pc_host->pdev->core) + 2;
 }
+EXPORT_SYMBOL(bcma_core_pci_pcibios_map_irq);
diff --git a/include/linux/bcma/bcma_driver_pci.h b/include/linux/bcma/bcma_driver_pci.h
index 679d4ca..46c71e2 100644
--- a/include/linux/bcma/bcma_driver_pci.h
+++ b/include/linux/bcma/bcma_driver_pci.h
@@ -160,9 +160,44 @@ struct pci_dev;
 /* PCIcore specific boardflags */
 #define BCMA_CORE_PCI_BFL_NOPCI			0x00000400 /* Board leaves PCI floating */
 
+/* PCIE Config space accessing MACROS */
+#define BCMA_CORE_PCI_CFG_BUS_SHIFT		24	/* Bus shift */
+#define BCMA_CORE_PCI_CFG_SLOT_SHIFT		19	/* Slot/Device shift */
+#define BCMA_CORE_PCI_CFG_FUN_SHIFT		16	/* Function shift */
+#define BCMA_CORE_PCI_CFG_OFF_SHIFT		0	/* Register shift */
+
+#define BCMA_CORE_PCI_CFG_BUS_MASK		0xff	/* Bus mask */
+#define BCMA_CORE_PCI_CFG_SLOT_MASK		0x1f	/* Slot/Device mask */
+#define BCMA_CORE_PCI_CFG_FUN_MASK		7	/* Function mask */
+#define BCMA_CORE_PCI_CFG_OFF_MASK		0xfff	/* Register mask */
+
+/* PCIE Root Capability Register bits (Host mode only) */
+#define BCMA_CORE_PCI_RC_CRS_VISIBILITY		0x0001
+
+struct bcma_drv_pci;
+
+#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
+struct bcma_drv_pci_host {
+	struct bcma_drv_pci *pdev;
+
+	u32 host_cfg_addr;
+	spinlock_t cfgspace_lock;
+
+	struct pci_controller pci_controller;
+	struct pci_ops pci_ops;
+	struct resource mem_resource;
+	struct resource io_resource;
+};
+#endif
+
 struct bcma_drv_pci {
 	struct bcma_device *core;
 	u8 setup_done:1;
+	u8 hostmode:1;
+
+#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
+	struct bcma_drv_pci_host *host_controller;
+#endif
 };
 
 /* Register access */
@@ -173,4 +208,7 @@ extern void __devinit bcma_core_pci_init(struct bcma_drv_pci *pc);
 extern int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc,
 				 struct bcma_device *core, bool enable);
 
+extern int bcma_core_pci_pcibios_map_irq(const struct pci_dev *dev);
+extern int bcma_core_pci_plat_dev_init(struct pci_dev *dev);
+
 #endif /* LINUX_BCMA_DRIVER_PCI_H_ */
diff --git a/include/linux/bcma/bcma_regs.h b/include/linux/bcma/bcma_regs.h
index 9faae2a..5a71d57 100644
--- a/include/linux/bcma/bcma_regs.h
+++ b/include/linux/bcma/bcma_regs.h
@@ -56,4 +56,31 @@
 #define  BCMA_PCI_GPIO_XTAL		0x40	/* PCI config space GPIO 14 for Xtal powerup */
 #define  BCMA_PCI_GPIO_PLL		0x80	/* PCI config space GPIO 15 for PLL powerdown */
 
+/* SiliconBackplane Address Map.
+ * All regions may not exist on all chips.
+ */
+#define BCMA_SOC_SDRAM_BASE		0x00000000U	/* Physical SDRAM */
+#define BCMA_SOC_PCI_MEM		0x08000000U	/* Host Mode sb2pcitranslation0 (64 MB) */
+#define BCMA_SOC_PCI_MEM_SZ		(64 * 1024 * 1024)
+#define BCMA_SOC_PCI_CFG		0x0c000000U	/* Host Mode sb2pcitranslation1 (64 MB) */
+#define BCMA_SOC_SDRAM_SWAPPED		0x10000000U	/* Byteswapped Physical SDRAM */
+#define BCMA_SOC_SDRAM_R2		0x80000000U	/* Region 2 for sdram (512 MB) */
+
+
+#define BCMA_SOC_PCI_DMA		0x40000000U	/* Client Mode sb2pcitranslation2 (1 GB) */
+#define BCMA_SOC_PCI_DMA2		0x80000000U	/* Client Mode sb2pcitranslation2 (1 GB) */
+#define BCMA_SOC_PCI_DMA_SZ		0x40000000U	/* Client Mode sb2pcitranslation2 size in bytes */
+#define BCMA_SOC_PCIE_DMA_L32		0x00000000U	/* PCIE Client Mode sb2pcitranslation2
+							 * (2 ZettaBytes), low 32 bits
+							 */
+#define BCMA_SOC_PCIE_DMA_H32		0x80000000U	/* PCIE Client Mode sb2pcitranslation2
+							 * (2 ZettaBytes), high 32 bits
+							 */
+
+#define BCMA_SOC_PCI1_MEM		0x40000000U	/* Host Mode sb2pcitranslation0 (64 MB) */
+#define BCMA_SOC_PCI1_CFG		0x44000000U	/* Host Mode sb2pcitranslation1 (64 MB) */
+#define BCMA_SOC_PCIE1_DMA_H32		0xc0000000U	/* PCIE Client Mode sb2pcitranslation2
+							 * (2 ZettaBytes), high 32 bits
+							 */
+
 #endif /* LINUX_BCMA_REGS_H_ */
-- 
1.7.5.4

---

From: Hauke Mehrtens <[email-masked]>
Date: Tue, 31 Jan 2012 00:03:37 +0100

This check is needed on the BCM43224 device as it says in the
capabilities it has an sprom but is extra check says it has not.

Signed-off-by: Hauke Mehrtens <[email-masked]>
---
 drivers/bcma/sprom.c                        |    7 +++++++
 include/linux/bcma/bcma_driver_chipcommon.h |   16 ++++++++++++++++
 2 files changed, 23 insertions(+), 0 deletions(-)

diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c
index e35134f..ca77525 100644
--- a/drivers/bcma/sprom.c
+++ b/drivers/bcma/sprom.c
@@ -250,6 +250,7 @@ int bcma_sprom_get(struct bcma_bus *bus)
 {
 	u16 offset;
 	u16 *sprom;
+	u32 sromctrl;
 	int err = 0;
 
 	if (!bus->drv_cc.core)
@@ -258,6 +259,12 @@ int bcma_sprom_get(struct bcma_bus *bus)
 	if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM))
 		return -ENOENT;
 
+	if (bus->drv_cc.core->id.rev >= 32) {
+		sromctrl = bcma_read32(bus->drv_cc.core, BCMA_CC_SROM_CONTROL);
+		if (!(sromctrl & BCMA_CC_SROM_CONTROL_PRESENT))
+			return -ENOENT;
+	}
+
 	sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
 			GFP_KERNEL);
 	if (!sprom)
diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h
index a33086a..e72938b 100644
--- a/include/linux/bcma/bcma_driver_chipcommon.h
+++ b/include/linux/bcma/bcma_driver_chipcommon.h
@@ -181,6 +181,22 @@
 #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
+#define BCMA_CC_SROM_CONTROL		0x0190
+#define  BCMA_CC_SROM_CONTROL_START	0x80000000
+#define  BCMA_CC_SROM_CONTROL_BUSY	0x80000000
+#define  BCMA_CC_SROM_CONTROL_OPCODE	0x60000000
+#define  BCMA_CC_SROM_CONTROL_OP_READ	0x00000000
+#define  BCMA_CC_SROM_CONTROL_OP_WRITE	0x20000000
+#define  BCMA_CC_SROM_CONTROL_OP_WRDIS	0x40000000
+#define  BCMA_CC_SROM_CONTROL_OP_WREN	0x60000000
+#define  BCMA_CC_SROM_CONTROL_OTPSEL	0x00000010
+#define  BCMA_CC_SROM_CONTROL_LOCK	0x00000008
+#define  BCMA_CC_SROM_CONTROL_SIZE_MASK	0x00000006
+#define  BCMA_CC_SROM_CONTROL_SIZE_1K	0x00000000
+#define  BCMA_CC_SROM_CONTROL_SIZE_4K	0x00000002
+#define  BCMA_CC_SROM_CONTROL_SIZE_16K	0x00000004
+#define  BCMA_CC_SROM_CONTROL_SIZE_SHIFT	1
+#define  BCMA_CC_SROM_CONTROL_PRESENT	0x00000001
 /* 0x1E0 is defined as shared BCMA_CLKCTLST */
 #define BCMA_CC_HW_WORKAROUND		0x01E4 /* Hardware workaround (rev >= 20) */
 #define BCMA_CC_UART0_DATA		0x0300
-- 
1.7.5.4