162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Broadcom specific AMBA
362306a36Sopenharmony_ci * Bus scanning
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Licensed under the GNU/GPL. See COPYING for details.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "scan.h"
962306a36Sopenharmony_ci#include "bcma_private.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/bcma/bcma.h>
1262306a36Sopenharmony_ci#include <linux/bcma/bcma_regs.h>
1362306a36Sopenharmony_ci#include <linux/pci.h>
1462306a36Sopenharmony_ci#include <linux/io.h>
1562306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistruct bcma_device_id_name {
1962306a36Sopenharmony_ci	u16 id;
2062306a36Sopenharmony_ci	const char *name;
2162306a36Sopenharmony_ci};
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic const struct bcma_device_id_name bcma_arm_device_names[] = {
2462306a36Sopenharmony_ci	{ BCMA_CORE_4706_MAC_GBIT_COMMON, "BCM4706 GBit MAC Common" },
2562306a36Sopenharmony_ci	{ BCMA_CORE_ARM_1176, "ARM 1176" },
2662306a36Sopenharmony_ci	{ BCMA_CORE_ARM_7TDMI, "ARM 7TDMI" },
2762306a36Sopenharmony_ci	{ BCMA_CORE_ARM_CM3, "ARM CM3" },
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic const struct bcma_device_id_name bcma_bcm_device_names[] = {
3162306a36Sopenharmony_ci	{ BCMA_CORE_OOB_ROUTER, "OOB Router" },
3262306a36Sopenharmony_ci	{ BCMA_CORE_4706_CHIPCOMMON, "BCM4706 ChipCommon" },
3362306a36Sopenharmony_ci	{ BCMA_CORE_4706_SOC_RAM, "BCM4706 SOC RAM" },
3462306a36Sopenharmony_ci	{ BCMA_CORE_4706_MAC_GBIT, "BCM4706 GBit MAC" },
3562306a36Sopenharmony_ci	{ BCMA_CORE_NS_PCIEG2, "PCIe Gen 2" },
3662306a36Sopenharmony_ci	{ BCMA_CORE_NS_DMA, "DMA" },
3762306a36Sopenharmony_ci	{ BCMA_CORE_NS_SDIO3, "SDIO3" },
3862306a36Sopenharmony_ci	{ BCMA_CORE_NS_USB20, "USB 2.0" },
3962306a36Sopenharmony_ci	{ BCMA_CORE_NS_USB30, "USB 3.0" },
4062306a36Sopenharmony_ci	{ BCMA_CORE_NS_A9JTAG, "ARM Cortex A9 JTAG" },
4162306a36Sopenharmony_ci	{ BCMA_CORE_NS_DDR23, "Denali DDR2/DDR3 memory controller" },
4262306a36Sopenharmony_ci	{ BCMA_CORE_NS_ROM, "ROM" },
4362306a36Sopenharmony_ci	{ BCMA_CORE_NS_NAND, "NAND flash controller" },
4462306a36Sopenharmony_ci	{ BCMA_CORE_NS_QSPI, "SPI flash controller" },
4562306a36Sopenharmony_ci	{ BCMA_CORE_NS_CHIPCOMMON_B, "Chipcommon B" },
4662306a36Sopenharmony_ci	{ BCMA_CORE_ARMCA9, "ARM Cortex A9 core (ihost)" },
4762306a36Sopenharmony_ci	{ BCMA_CORE_AMEMC, "AMEMC (DDR)" },
4862306a36Sopenharmony_ci	{ BCMA_CORE_ALTA, "ALTA (I2S)" },
4962306a36Sopenharmony_ci	{ BCMA_CORE_INVALID, "Invalid" },
5062306a36Sopenharmony_ci	{ BCMA_CORE_CHIPCOMMON, "ChipCommon" },
5162306a36Sopenharmony_ci	{ BCMA_CORE_ILINE20, "ILine 20" },
5262306a36Sopenharmony_ci	{ BCMA_CORE_SRAM, "SRAM" },
5362306a36Sopenharmony_ci	{ BCMA_CORE_SDRAM, "SDRAM" },
5462306a36Sopenharmony_ci	{ BCMA_CORE_PCI, "PCI" },
5562306a36Sopenharmony_ci	{ BCMA_CORE_ETHERNET, "Fast Ethernet" },
5662306a36Sopenharmony_ci	{ BCMA_CORE_V90, "V90" },
5762306a36Sopenharmony_ci	{ BCMA_CORE_USB11_HOSTDEV, "USB 1.1 Hostdev" },
5862306a36Sopenharmony_ci	{ BCMA_CORE_ADSL, "ADSL" },
5962306a36Sopenharmony_ci	{ BCMA_CORE_ILINE100, "ILine 100" },
6062306a36Sopenharmony_ci	{ BCMA_CORE_IPSEC, "IPSEC" },
6162306a36Sopenharmony_ci	{ BCMA_CORE_UTOPIA, "UTOPIA" },
6262306a36Sopenharmony_ci	{ BCMA_CORE_PCMCIA, "PCMCIA" },
6362306a36Sopenharmony_ci	{ BCMA_CORE_INTERNAL_MEM, "Internal Memory" },
6462306a36Sopenharmony_ci	{ BCMA_CORE_MEMC_SDRAM, "MEMC SDRAM" },
6562306a36Sopenharmony_ci	{ BCMA_CORE_OFDM, "OFDM" },
6662306a36Sopenharmony_ci	{ BCMA_CORE_EXTIF, "EXTIF" },
6762306a36Sopenharmony_ci	{ BCMA_CORE_80211, "IEEE 802.11" },
6862306a36Sopenharmony_ci	{ BCMA_CORE_PHY_A, "PHY A" },
6962306a36Sopenharmony_ci	{ BCMA_CORE_PHY_B, "PHY B" },
7062306a36Sopenharmony_ci	{ BCMA_CORE_PHY_G, "PHY G" },
7162306a36Sopenharmony_ci	{ BCMA_CORE_USB11_HOST, "USB 1.1 Host" },
7262306a36Sopenharmony_ci	{ BCMA_CORE_USB11_DEV, "USB 1.1 Device" },
7362306a36Sopenharmony_ci	{ BCMA_CORE_USB20_HOST, "USB 2.0 Host" },
7462306a36Sopenharmony_ci	{ BCMA_CORE_USB20_DEV, "USB 2.0 Device" },
7562306a36Sopenharmony_ci	{ BCMA_CORE_SDIO_HOST, "SDIO Host" },
7662306a36Sopenharmony_ci	{ BCMA_CORE_ROBOSWITCH, "Roboswitch" },
7762306a36Sopenharmony_ci	{ BCMA_CORE_PARA_ATA, "PATA" },
7862306a36Sopenharmony_ci	{ BCMA_CORE_SATA_XORDMA, "SATA XOR-DMA" },
7962306a36Sopenharmony_ci	{ BCMA_CORE_ETHERNET_GBIT, "GBit Ethernet" },
8062306a36Sopenharmony_ci	{ BCMA_CORE_PCIE, "PCIe" },
8162306a36Sopenharmony_ci	{ BCMA_CORE_PHY_N, "PHY N" },
8262306a36Sopenharmony_ci	{ BCMA_CORE_SRAM_CTL, "SRAM Controller" },
8362306a36Sopenharmony_ci	{ BCMA_CORE_MINI_MACPHY, "Mini MACPHY" },
8462306a36Sopenharmony_ci	{ BCMA_CORE_PHY_LP, "PHY LP" },
8562306a36Sopenharmony_ci	{ BCMA_CORE_PMU, "PMU" },
8662306a36Sopenharmony_ci	{ BCMA_CORE_PHY_SSN, "PHY SSN" },
8762306a36Sopenharmony_ci	{ BCMA_CORE_SDIO_DEV, "SDIO Device" },
8862306a36Sopenharmony_ci	{ BCMA_CORE_PHY_HT, "PHY HT" },
8962306a36Sopenharmony_ci	{ BCMA_CORE_MAC_GBIT, "GBit MAC" },
9062306a36Sopenharmony_ci	{ BCMA_CORE_DDR12_MEM_CTL, "DDR1/DDR2 Memory Controller" },
9162306a36Sopenharmony_ci	{ BCMA_CORE_PCIE_RC, "PCIe Root Complex" },
9262306a36Sopenharmony_ci	{ BCMA_CORE_OCP_OCP_BRIDGE, "OCP to OCP Bridge" },
9362306a36Sopenharmony_ci	{ BCMA_CORE_SHARED_COMMON, "Common Shared" },
9462306a36Sopenharmony_ci	{ BCMA_CORE_OCP_AHB_BRIDGE, "OCP to AHB Bridge" },
9562306a36Sopenharmony_ci	{ BCMA_CORE_SPI_HOST, "SPI Host" },
9662306a36Sopenharmony_ci	{ BCMA_CORE_I2S, "I2S" },
9762306a36Sopenharmony_ci	{ BCMA_CORE_SDR_DDR1_MEM_CTL, "SDR/DDR1 Memory Controller" },
9862306a36Sopenharmony_ci	{ BCMA_CORE_SHIM, "SHIM" },
9962306a36Sopenharmony_ci	{ BCMA_CORE_PCIE2, "PCIe Gen2" },
10062306a36Sopenharmony_ci	{ BCMA_CORE_ARM_CR4, "ARM CR4" },
10162306a36Sopenharmony_ci	{ BCMA_CORE_GCI, "GCI" },
10262306a36Sopenharmony_ci	{ BCMA_CORE_CMEM, "CNDS DDR2/3 memory controller" },
10362306a36Sopenharmony_ci	{ BCMA_CORE_ARM_CA7, "ARM CA7" },
10462306a36Sopenharmony_ci	{ BCMA_CORE_DEFAULT, "Default" },
10562306a36Sopenharmony_ci};
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic const struct bcma_device_id_name bcma_mips_device_names[] = {
10862306a36Sopenharmony_ci	{ BCMA_CORE_MIPS, "MIPS" },
10962306a36Sopenharmony_ci	{ BCMA_CORE_MIPS_3302, "MIPS 3302" },
11062306a36Sopenharmony_ci	{ BCMA_CORE_MIPS_74K, "MIPS 74K" },
11162306a36Sopenharmony_ci};
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic const char *bcma_device_name(const struct bcma_device_id *id)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	const struct bcma_device_id_name *names;
11662306a36Sopenharmony_ci	int size, i;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	/* search manufacturer specific names */
11962306a36Sopenharmony_ci	switch (id->manuf) {
12062306a36Sopenharmony_ci	case BCMA_MANUF_ARM:
12162306a36Sopenharmony_ci		names = bcma_arm_device_names;
12262306a36Sopenharmony_ci		size = ARRAY_SIZE(bcma_arm_device_names);
12362306a36Sopenharmony_ci		break;
12462306a36Sopenharmony_ci	case BCMA_MANUF_BCM:
12562306a36Sopenharmony_ci		names = bcma_bcm_device_names;
12662306a36Sopenharmony_ci		size = ARRAY_SIZE(bcma_bcm_device_names);
12762306a36Sopenharmony_ci		break;
12862306a36Sopenharmony_ci	case BCMA_MANUF_MIPS:
12962306a36Sopenharmony_ci		names = bcma_mips_device_names;
13062306a36Sopenharmony_ci		size = ARRAY_SIZE(bcma_mips_device_names);
13162306a36Sopenharmony_ci		break;
13262306a36Sopenharmony_ci	default:
13362306a36Sopenharmony_ci		return "UNKNOWN";
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	for (i = 0; i < size; i++) {
13762306a36Sopenharmony_ci		if (names[i].id == id->id)
13862306a36Sopenharmony_ci			return names[i].name;
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	return "UNKNOWN";
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic u32 bcma_scan_read32(struct bcma_bus *bus, u16 offset)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	return readl(bus->mmio + offset);
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic void bcma_scan_switch_core(struct bcma_bus *bus, u32 addr)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	if (bus->hosttype == BCMA_HOSTTYPE_PCI)
15262306a36Sopenharmony_ci		pci_write_config_dword(bus->host_pci, BCMA_PCI_BAR0_WIN,
15362306a36Sopenharmony_ci				       addr);
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic u32 bcma_erom_get_ent(struct bcma_bus *bus, u32 __iomem **eromptr)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	u32 ent = readl(*eromptr);
15962306a36Sopenharmony_ci	(*eromptr)++;
16062306a36Sopenharmony_ci	return ent;
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic void bcma_erom_push_ent(u32 __iomem **eromptr)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	(*eromptr)--;
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic s32 bcma_erom_get_ci(struct bcma_bus *bus, u32 __iomem **eromptr)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	u32 ent = bcma_erom_get_ent(bus, eromptr);
17162306a36Sopenharmony_ci	if (!(ent & SCAN_ER_VALID))
17262306a36Sopenharmony_ci		return -ENOENT;
17362306a36Sopenharmony_ci	if ((ent & SCAN_ER_TAG) != SCAN_ER_TAG_CI)
17462306a36Sopenharmony_ci		return -ENOENT;
17562306a36Sopenharmony_ci	return ent;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic bool bcma_erom_is_end(struct bcma_bus *bus, u32 __iomem **eromptr)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	u32 ent = bcma_erom_get_ent(bus, eromptr);
18162306a36Sopenharmony_ci	bcma_erom_push_ent(eromptr);
18262306a36Sopenharmony_ci	return (ent == (SCAN_ER_TAG_END | SCAN_ER_VALID));
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic bool bcma_erom_is_bridge(struct bcma_bus *bus, u32 __iomem **eromptr)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	u32 ent = bcma_erom_get_ent(bus, eromptr);
18862306a36Sopenharmony_ci	bcma_erom_push_ent(eromptr);
18962306a36Sopenharmony_ci	return (((ent & SCAN_ER_VALID)) &&
19062306a36Sopenharmony_ci		((ent & SCAN_ER_TAGX) == SCAN_ER_TAG_ADDR) &&
19162306a36Sopenharmony_ci		((ent & SCAN_ADDR_TYPE) == SCAN_ADDR_TYPE_BRIDGE));
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic void bcma_erom_skip_component(struct bcma_bus *bus, u32 __iomem **eromptr)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	u32 ent;
19762306a36Sopenharmony_ci	while (1) {
19862306a36Sopenharmony_ci		ent = bcma_erom_get_ent(bus, eromptr);
19962306a36Sopenharmony_ci		if ((ent & SCAN_ER_VALID) &&
20062306a36Sopenharmony_ci		    ((ent & SCAN_ER_TAG) == SCAN_ER_TAG_CI))
20162306a36Sopenharmony_ci			break;
20262306a36Sopenharmony_ci		if (ent == (SCAN_ER_TAG_END | SCAN_ER_VALID))
20362306a36Sopenharmony_ci			break;
20462306a36Sopenharmony_ci	}
20562306a36Sopenharmony_ci	bcma_erom_push_ent(eromptr);
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic s32 bcma_erom_get_mst_port(struct bcma_bus *bus, u32 __iomem **eromptr)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	u32 ent = bcma_erom_get_ent(bus, eromptr);
21162306a36Sopenharmony_ci	if (!(ent & SCAN_ER_VALID))
21262306a36Sopenharmony_ci		return -ENOENT;
21362306a36Sopenharmony_ci	if ((ent & SCAN_ER_TAG) != SCAN_ER_TAG_MP)
21462306a36Sopenharmony_ci		return -ENOENT;
21562306a36Sopenharmony_ci	return ent;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic u32 bcma_erom_get_addr_desc(struct bcma_bus *bus, u32 __iomem **eromptr,
21962306a36Sopenharmony_ci				  u32 type, u8 port)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	u32 addrl;
22262306a36Sopenharmony_ci	u32 size;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	u32 ent = bcma_erom_get_ent(bus, eromptr);
22562306a36Sopenharmony_ci	if ((!(ent & SCAN_ER_VALID)) ||
22662306a36Sopenharmony_ci	    ((ent & SCAN_ER_TAGX) != SCAN_ER_TAG_ADDR) ||
22762306a36Sopenharmony_ci	    ((ent & SCAN_ADDR_TYPE) != type) ||
22862306a36Sopenharmony_ci	    (((ent & SCAN_ADDR_PORT) >> SCAN_ADDR_PORT_SHIFT) != port)) {
22962306a36Sopenharmony_ci		bcma_erom_push_ent(eromptr);
23062306a36Sopenharmony_ci		return (u32)-EINVAL;
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	addrl = ent & SCAN_ADDR_ADDR;
23462306a36Sopenharmony_ci	if (ent & SCAN_ADDR_AG32)
23562306a36Sopenharmony_ci		bcma_erom_get_ent(bus, eromptr);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	if ((ent & SCAN_ADDR_SZ) == SCAN_ADDR_SZ_SZD) {
23862306a36Sopenharmony_ci		size = bcma_erom_get_ent(bus, eromptr);
23962306a36Sopenharmony_ci		if (size & SCAN_SIZE_SG32)
24062306a36Sopenharmony_ci			bcma_erom_get_ent(bus, eromptr);
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	return addrl;
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic struct bcma_device *bcma_find_core_by_index(struct bcma_bus *bus,
24762306a36Sopenharmony_ci						   u16 index)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	struct bcma_device *core;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	list_for_each_entry(core, &bus->cores, list) {
25262306a36Sopenharmony_ci		if (core->core_index == index)
25362306a36Sopenharmony_ci			return core;
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci	return NULL;
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic struct bcma_device *bcma_find_core_reverse(struct bcma_bus *bus, u16 coreid)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	struct bcma_device *core;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	list_for_each_entry_reverse(core, &bus->cores, list) {
26362306a36Sopenharmony_ci		if (core->id.id == coreid)
26462306a36Sopenharmony_ci			return core;
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci	return NULL;
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci#define IS_ERR_VALUE_U32(x) ((x) >= (u32)-MAX_ERRNO)
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
27262306a36Sopenharmony_ci			      struct bcma_device_id *match, int core_num,
27362306a36Sopenharmony_ci			      struct bcma_device *core)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	u32 tmp;
27662306a36Sopenharmony_ci	u8 i, j, k;
27762306a36Sopenharmony_ci	s32 cia, cib;
27862306a36Sopenharmony_ci	u8 ports[2], wrappers[2];
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	/* get CIs */
28162306a36Sopenharmony_ci	cia = bcma_erom_get_ci(bus, eromptr);
28262306a36Sopenharmony_ci	if (cia < 0) {
28362306a36Sopenharmony_ci		bcma_erom_push_ent(eromptr);
28462306a36Sopenharmony_ci		if (bcma_erom_is_end(bus, eromptr))
28562306a36Sopenharmony_ci			return -ESPIPE;
28662306a36Sopenharmony_ci		return -EILSEQ;
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci	cib = bcma_erom_get_ci(bus, eromptr);
28962306a36Sopenharmony_ci	if (cib < 0)
29062306a36Sopenharmony_ci		return -EILSEQ;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	/* parse CIs */
29362306a36Sopenharmony_ci	core->id.class = (cia & SCAN_CIA_CLASS) >> SCAN_CIA_CLASS_SHIFT;
29462306a36Sopenharmony_ci	core->id.id = (cia & SCAN_CIA_ID) >> SCAN_CIA_ID_SHIFT;
29562306a36Sopenharmony_ci	core->id.manuf = (cia & SCAN_CIA_MANUF) >> SCAN_CIA_MANUF_SHIFT;
29662306a36Sopenharmony_ci	ports[0] = (cib & SCAN_CIB_NMP) >> SCAN_CIB_NMP_SHIFT;
29762306a36Sopenharmony_ci	ports[1] = (cib & SCAN_CIB_NSP) >> SCAN_CIB_NSP_SHIFT;
29862306a36Sopenharmony_ci	wrappers[0] = (cib & SCAN_CIB_NMW) >> SCAN_CIB_NMW_SHIFT;
29962306a36Sopenharmony_ci	wrappers[1] = (cib & SCAN_CIB_NSW) >> SCAN_CIB_NSW_SHIFT;
30062306a36Sopenharmony_ci	core->id.rev = (cib & SCAN_CIB_REV) >> SCAN_CIB_REV_SHIFT;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if (((core->id.manuf == BCMA_MANUF_ARM) &&
30362306a36Sopenharmony_ci	     (core->id.id == 0xFFF)) ||
30462306a36Sopenharmony_ci	    (ports[1] == 0)) {
30562306a36Sopenharmony_ci		bcma_erom_skip_component(bus, eromptr);
30662306a36Sopenharmony_ci		return -ENXIO;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	/* check if component is a core at all */
31062306a36Sopenharmony_ci	if (wrappers[0] + wrappers[1] == 0) {
31162306a36Sopenharmony_ci		/* Some specific cores don't need wrappers */
31262306a36Sopenharmony_ci		switch (core->id.id) {
31362306a36Sopenharmony_ci		case BCMA_CORE_4706_MAC_GBIT_COMMON:
31462306a36Sopenharmony_ci		case BCMA_CORE_NS_CHIPCOMMON_B:
31562306a36Sopenharmony_ci		case BCMA_CORE_PMU:
31662306a36Sopenharmony_ci		case BCMA_CORE_GCI:
31762306a36Sopenharmony_ci		/* Not used yet: case BCMA_CORE_OOB_ROUTER: */
31862306a36Sopenharmony_ci			break;
31962306a36Sopenharmony_ci		default:
32062306a36Sopenharmony_ci			bcma_erom_skip_component(bus, eromptr);
32162306a36Sopenharmony_ci			return -ENXIO;
32262306a36Sopenharmony_ci		}
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	if (bcma_erom_is_bridge(bus, eromptr)) {
32662306a36Sopenharmony_ci		bcma_erom_skip_component(bus, eromptr);
32762306a36Sopenharmony_ci		return -ENXIO;
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (bcma_find_core_by_index(bus, core_num)) {
33162306a36Sopenharmony_ci		bcma_erom_skip_component(bus, eromptr);
33262306a36Sopenharmony_ci		return -ENODEV;
33362306a36Sopenharmony_ci	}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	if (match && ((match->manuf != BCMA_ANY_MANUF &&
33662306a36Sopenharmony_ci	      match->manuf != core->id.manuf) ||
33762306a36Sopenharmony_ci	     (match->id != BCMA_ANY_ID && match->id != core->id.id) ||
33862306a36Sopenharmony_ci	     (match->rev != BCMA_ANY_REV && match->rev != core->id.rev) ||
33962306a36Sopenharmony_ci	     (match->class != BCMA_ANY_CLASS && match->class != core->id.class)
34062306a36Sopenharmony_ci	    )) {
34162306a36Sopenharmony_ci		bcma_erom_skip_component(bus, eromptr);
34262306a36Sopenharmony_ci		return -ENODEV;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	/* get & parse master ports */
34662306a36Sopenharmony_ci	for (i = 0; i < ports[0]; i++) {
34762306a36Sopenharmony_ci		s32 mst_port_d = bcma_erom_get_mst_port(bus, eromptr);
34862306a36Sopenharmony_ci		if (mst_port_d < 0)
34962306a36Sopenharmony_ci			return -EILSEQ;
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	/* First Slave Address Descriptor should be port 0:
35362306a36Sopenharmony_ci	 * the main register space for the core
35462306a36Sopenharmony_ci	 */
35562306a36Sopenharmony_ci	tmp = bcma_erom_get_addr_desc(bus, eromptr, SCAN_ADDR_TYPE_SLAVE, 0);
35662306a36Sopenharmony_ci	if (tmp == 0 || IS_ERR_VALUE_U32(tmp)) {
35762306a36Sopenharmony_ci		/* Try again to see if it is a bridge */
35862306a36Sopenharmony_ci		tmp = bcma_erom_get_addr_desc(bus, eromptr,
35962306a36Sopenharmony_ci					      SCAN_ADDR_TYPE_BRIDGE, 0);
36062306a36Sopenharmony_ci		if (tmp == 0 || IS_ERR_VALUE_U32(tmp)) {
36162306a36Sopenharmony_ci			return -EILSEQ;
36262306a36Sopenharmony_ci		} else {
36362306a36Sopenharmony_ci			bcma_info(bus, "Bridge found\n");
36462306a36Sopenharmony_ci			return -ENXIO;
36562306a36Sopenharmony_ci		}
36662306a36Sopenharmony_ci	}
36762306a36Sopenharmony_ci	core->addr = tmp;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	/* get & parse slave ports */
37062306a36Sopenharmony_ci	k = 0;
37162306a36Sopenharmony_ci	for (i = 0; i < ports[1]; i++) {
37262306a36Sopenharmony_ci		for (j = 0; ; j++) {
37362306a36Sopenharmony_ci			tmp = bcma_erom_get_addr_desc(bus, eromptr,
37462306a36Sopenharmony_ci				SCAN_ADDR_TYPE_SLAVE, i);
37562306a36Sopenharmony_ci			if (IS_ERR_VALUE_U32(tmp)) {
37662306a36Sopenharmony_ci				/* no more entries for port _i_ */
37762306a36Sopenharmony_ci				/* pr_debug("erom: slave port %d "
37862306a36Sopenharmony_ci				 * "has %d descriptors\n", i, j); */
37962306a36Sopenharmony_ci				break;
38062306a36Sopenharmony_ci			} else if (k < ARRAY_SIZE(core->addr_s)) {
38162306a36Sopenharmony_ci				core->addr_s[k] = tmp;
38262306a36Sopenharmony_ci				k++;
38362306a36Sopenharmony_ci			}
38462306a36Sopenharmony_ci		}
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	/* get & parse master wrappers */
38862306a36Sopenharmony_ci	for (i = 0; i < wrappers[0]; i++) {
38962306a36Sopenharmony_ci		for (j = 0; ; j++) {
39062306a36Sopenharmony_ci			tmp = bcma_erom_get_addr_desc(bus, eromptr,
39162306a36Sopenharmony_ci				SCAN_ADDR_TYPE_MWRAP, i);
39262306a36Sopenharmony_ci			if (IS_ERR_VALUE_U32(tmp)) {
39362306a36Sopenharmony_ci				/* no more entries for port _i_ */
39462306a36Sopenharmony_ci				/* pr_debug("erom: master wrapper %d "
39562306a36Sopenharmony_ci				 * "has %d descriptors\n", i, j); */
39662306a36Sopenharmony_ci				break;
39762306a36Sopenharmony_ci			} else {
39862306a36Sopenharmony_ci				if (i == 0 && j == 0)
39962306a36Sopenharmony_ci					core->wrap = tmp;
40062306a36Sopenharmony_ci			}
40162306a36Sopenharmony_ci		}
40262306a36Sopenharmony_ci	}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	/* get & parse slave wrappers */
40562306a36Sopenharmony_ci	for (i = 0; i < wrappers[1]; i++) {
40662306a36Sopenharmony_ci		u8 hack = (ports[1] == 1) ? 0 : 1;
40762306a36Sopenharmony_ci		for (j = 0; ; j++) {
40862306a36Sopenharmony_ci			tmp = bcma_erom_get_addr_desc(bus, eromptr,
40962306a36Sopenharmony_ci				SCAN_ADDR_TYPE_SWRAP, i + hack);
41062306a36Sopenharmony_ci			if (IS_ERR_VALUE_U32(tmp)) {
41162306a36Sopenharmony_ci				/* no more entries for port _i_ */
41262306a36Sopenharmony_ci				/* pr_debug("erom: master wrapper %d "
41362306a36Sopenharmony_ci				 * has %d descriptors\n", i, j); */
41462306a36Sopenharmony_ci				break;
41562306a36Sopenharmony_ci			} else {
41662306a36Sopenharmony_ci				if (wrappers[0] == 0 && !i && !j)
41762306a36Sopenharmony_ci					core->wrap = tmp;
41862306a36Sopenharmony_ci			}
41962306a36Sopenharmony_ci		}
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci	if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
42262306a36Sopenharmony_ci		core->io_addr = ioremap(core->addr, BCMA_CORE_SIZE);
42362306a36Sopenharmony_ci		if (!core->io_addr)
42462306a36Sopenharmony_ci			return -ENOMEM;
42562306a36Sopenharmony_ci		if (core->wrap) {
42662306a36Sopenharmony_ci			core->io_wrap = ioremap(core->wrap,
42762306a36Sopenharmony_ci							BCMA_CORE_SIZE);
42862306a36Sopenharmony_ci			if (!core->io_wrap) {
42962306a36Sopenharmony_ci				iounmap(core->io_addr);
43062306a36Sopenharmony_ci				return -ENOMEM;
43162306a36Sopenharmony_ci			}
43262306a36Sopenharmony_ci		}
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci	return 0;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_civoid bcma_detect_chip(struct bcma_bus *bus)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	s32 tmp;
44062306a36Sopenharmony_ci	struct bcma_chipinfo *chipinfo = &(bus->chipinfo);
44162306a36Sopenharmony_ci	char chip_id[8];
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	bcma_scan_switch_core(bus, BCMA_ADDR_BASE);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	tmp = bcma_scan_read32(bus, BCMA_CC_ID);
44662306a36Sopenharmony_ci	chipinfo->id = (tmp & BCMA_CC_ID_ID) >> BCMA_CC_ID_ID_SHIFT;
44762306a36Sopenharmony_ci	chipinfo->rev = (tmp & BCMA_CC_ID_REV) >> BCMA_CC_ID_REV_SHIFT;
44862306a36Sopenharmony_ci	chipinfo->pkg = (tmp & BCMA_CC_ID_PKG) >> BCMA_CC_ID_PKG_SHIFT;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	snprintf(chip_id, ARRAY_SIZE(chip_id),
45162306a36Sopenharmony_ci		 (chipinfo->id > 0x9999) ? "%d" : "0x%04X", chipinfo->id);
45262306a36Sopenharmony_ci	bcma_info(bus, "Found chip with id %s, rev 0x%02X and package 0x%02X\n",
45362306a36Sopenharmony_ci		  chip_id, chipinfo->rev, chipinfo->pkg);
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ciint bcma_bus_scan(struct bcma_bus *bus)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	u32 erombase;
45962306a36Sopenharmony_ci	u32 __iomem *eromptr, *eromend;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	int err, core_num = 0;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	/* Skip if bus was already scanned (e.g. during early register) */
46462306a36Sopenharmony_ci	if (bus->nr_cores)
46562306a36Sopenharmony_ci		return 0;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	erombase = bcma_scan_read32(bus, BCMA_CC_EROM);
46862306a36Sopenharmony_ci	if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
46962306a36Sopenharmony_ci		eromptr = ioremap(erombase, BCMA_CORE_SIZE);
47062306a36Sopenharmony_ci		if (!eromptr)
47162306a36Sopenharmony_ci			return -ENOMEM;
47262306a36Sopenharmony_ci	} else {
47362306a36Sopenharmony_ci		eromptr = bus->mmio;
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	eromend = eromptr + BCMA_CORE_SIZE / sizeof(u32);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	bcma_scan_switch_core(bus, erombase);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	while (eromptr < eromend) {
48162306a36Sopenharmony_ci		struct bcma_device *other_core;
48262306a36Sopenharmony_ci		struct bcma_device *core = kzalloc(sizeof(*core), GFP_KERNEL);
48362306a36Sopenharmony_ci		if (!core) {
48462306a36Sopenharmony_ci			err = -ENOMEM;
48562306a36Sopenharmony_ci			goto out;
48662306a36Sopenharmony_ci		}
48762306a36Sopenharmony_ci		INIT_LIST_HEAD(&core->list);
48862306a36Sopenharmony_ci		core->bus = bus;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci		err = bcma_get_next_core(bus, &eromptr, NULL, core_num, core);
49162306a36Sopenharmony_ci		if (err < 0) {
49262306a36Sopenharmony_ci			kfree(core);
49362306a36Sopenharmony_ci			if (err == -ENODEV) {
49462306a36Sopenharmony_ci				core_num++;
49562306a36Sopenharmony_ci				continue;
49662306a36Sopenharmony_ci			} else if (err == -ENXIO) {
49762306a36Sopenharmony_ci				continue;
49862306a36Sopenharmony_ci			} else if (err == -ESPIPE) {
49962306a36Sopenharmony_ci				break;
50062306a36Sopenharmony_ci			}
50162306a36Sopenharmony_ci			goto out;
50262306a36Sopenharmony_ci		}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci		core->core_index = core_num++;
50562306a36Sopenharmony_ci		bus->nr_cores++;
50662306a36Sopenharmony_ci		other_core = bcma_find_core_reverse(bus, core->id.id);
50762306a36Sopenharmony_ci		core->core_unit = (other_core == NULL) ? 0 : other_core->core_unit + 1;
50862306a36Sopenharmony_ci		bcma_prepare_core(bus, core);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci		bcma_info(bus, "Core %d found: %s (manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n",
51162306a36Sopenharmony_ci			  core->core_index, bcma_device_name(&core->id),
51262306a36Sopenharmony_ci			  core->id.manuf, core->id.id, core->id.rev,
51362306a36Sopenharmony_ci			  core->id.class);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci		list_add_tail(&core->list, &bus->cores);
51662306a36Sopenharmony_ci	}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	err = 0;
51962306a36Sopenharmony_ciout:
52062306a36Sopenharmony_ci	if (bus->hosttype == BCMA_HOSTTYPE_SOC)
52162306a36Sopenharmony_ci		iounmap(eromptr);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	return err;
52462306a36Sopenharmony_ci}
525