forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
445 lines
10 KiB
445 lines
10 KiB
/* |
|
* Sonics Silicon Backplane |
|
* Bus scanning |
|
* |
|
* Copyright (C) 2005-2007 Michael Buesch <[email protected]> |
|
* Copyright (C) 2005 Martin Langer <[email protected]> |
|
* Copyright (C) 2005 Stefano Brivio <[email protected]> |
|
* Copyright (C) 2005 Danny van Dyk <[email protected]> |
|
* Copyright (C) 2005 Andreas Jaggi <[email protected]> |
|
* Copyright (C) 2006 Broadcom Corporation. |
|
* |
|
* Licensed under the GNU/GPL. See COPYING for details. |
|
*/ |
|
|
|
#include "ssb_private.h" |
|
|
|
#include <linux/ssb/ssb.h> |
|
#include <linux/ssb/ssb_regs.h> |
|
#include <linux/pci.h> |
|
#include <linux/io.h> |
|
|
|
#include <pcmcia/cistpl.h> |
|
#include <pcmcia/ds.h> |
|
|
|
|
|
const char *ssb_core_name(u16 coreid) |
|
{ |
|
switch (coreid) { |
|
case SSB_DEV_CHIPCOMMON: |
|
return "ChipCommon"; |
|
case SSB_DEV_ILINE20: |
|
return "ILine 20"; |
|
case SSB_DEV_SDRAM: |
|
return "SDRAM"; |
|
case SSB_DEV_PCI: |
|
return "PCI"; |
|
case SSB_DEV_MIPS: |
|
return "MIPS"; |
|
case SSB_DEV_ETHERNET: |
|
return "Fast Ethernet"; |
|
case SSB_DEV_V90: |
|
return "V90"; |
|
case SSB_DEV_USB11_HOSTDEV: |
|
return "USB 1.1 Hostdev"; |
|
case SSB_DEV_ADSL: |
|
return "ADSL"; |
|
case SSB_DEV_ILINE100: |
|
return "ILine 100"; |
|
case SSB_DEV_IPSEC: |
|
return "IPSEC"; |
|
case SSB_DEV_PCMCIA: |
|
return "PCMCIA"; |
|
case SSB_DEV_INTERNAL_MEM: |
|
return "Internal Memory"; |
|
case SSB_DEV_MEMC_SDRAM: |
|
return "MEMC SDRAM"; |
|
case SSB_DEV_EXTIF: |
|
return "EXTIF"; |
|
case SSB_DEV_80211: |
|
return "IEEE 802.11"; |
|
case SSB_DEV_MIPS_3302: |
|
return "MIPS 3302"; |
|
case SSB_DEV_USB11_HOST: |
|
return "USB 1.1 Host"; |
|
case SSB_DEV_USB11_DEV: |
|
return "USB 1.1 Device"; |
|
case SSB_DEV_USB20_HOST: |
|
return "USB 2.0 Host"; |
|
case SSB_DEV_USB20_DEV: |
|
return "USB 2.0 Device"; |
|
case SSB_DEV_SDIO_HOST: |
|
return "SDIO Host"; |
|
case SSB_DEV_ROBOSWITCH: |
|
return "Roboswitch"; |
|
case SSB_DEV_PARA_ATA: |
|
return "PATA"; |
|
case SSB_DEV_SATA_XORDMA: |
|
return "SATA XOR-DMA"; |
|
case SSB_DEV_ETHERNET_GBIT: |
|
return "GBit Ethernet"; |
|
case SSB_DEV_PCIE: |
|
return "PCI-E"; |
|
case SSB_DEV_MIMO_PHY: |
|
return "MIMO PHY"; |
|
case SSB_DEV_SRAM_CTRLR: |
|
return "SRAM Controller"; |
|
case SSB_DEV_MINI_MACPHY: |
|
return "Mini MACPHY"; |
|
case SSB_DEV_ARM_1176: |
|
return "ARM 1176"; |
|
case SSB_DEV_ARM_7TDMI: |
|
return "ARM 7TDMI"; |
|
case SSB_DEV_ARM_CM3: |
|
return "ARM Cortex M3"; |
|
} |
|
return "UNKNOWN"; |
|
} |
|
|
|
static u16 pcidev_to_chipid(struct pci_dev *pci_dev) |
|
{ |
|
u16 chipid_fallback = 0; |
|
|
|
switch (pci_dev->device) { |
|
case 0x4301: |
|
chipid_fallback = 0x4301; |
|
break; |
|
case 0x4305 ... 0x4307: |
|
chipid_fallback = 0x4307; |
|
break; |
|
case 0x4403: |
|
chipid_fallback = 0x4402; |
|
break; |
|
case 0x4610 ... 0x4615: |
|
chipid_fallback = 0x4610; |
|
break; |
|
case 0x4710 ... 0x4715: |
|
chipid_fallback = 0x4710; |
|
break; |
|
case 0x4320 ... 0x4325: |
|
chipid_fallback = 0x4309; |
|
break; |
|
case PCI_DEVICE_ID_BCM4401: |
|
case PCI_DEVICE_ID_BCM4401B0: |
|
case PCI_DEVICE_ID_BCM4401B1: |
|
chipid_fallback = 0x4401; |
|
break; |
|
default: |
|
dev_err(&pci_dev->dev, "PCI-ID not in fallback list\n"); |
|
} |
|
|
|
return chipid_fallback; |
|
} |
|
|
|
static u8 chipid_to_nrcores(u16 chipid) |
|
{ |
|
switch (chipid) { |
|
case 0x5365: |
|
return 7; |
|
case 0x4306: |
|
return 6; |
|
case 0x4310: |
|
return 8; |
|
case 0x4307: |
|
case 0x4301: |
|
return 5; |
|
case 0x4401: |
|
case 0x4402: |
|
return 3; |
|
case 0x4710: |
|
case 0x4610: |
|
case 0x4704: |
|
return 9; |
|
default: |
|
pr_err("CHIPID not in nrcores fallback list\n"); |
|
} |
|
|
|
return 1; |
|
} |
|
|
|
static u32 scan_read32(struct ssb_bus *bus, u8 current_coreidx, |
|
u16 offset) |
|
{ |
|
u32 lo, hi; |
|
|
|
switch (bus->bustype) { |
|
case SSB_BUSTYPE_SSB: |
|
offset += current_coreidx * SSB_CORE_SIZE; |
|
break; |
|
case SSB_BUSTYPE_PCI: |
|
break; |
|
case SSB_BUSTYPE_PCMCIA: |
|
if (offset >= 0x800) { |
|
ssb_pcmcia_switch_segment(bus, 1); |
|
offset -= 0x800; |
|
} else |
|
ssb_pcmcia_switch_segment(bus, 0); |
|
lo = readw(bus->mmio + offset); |
|
hi = readw(bus->mmio + offset + 2); |
|
return lo | (hi << 16); |
|
case SSB_BUSTYPE_SDIO: |
|
offset += current_coreidx * SSB_CORE_SIZE; |
|
return ssb_sdio_scan_read32(bus, offset); |
|
} |
|
return readl(bus->mmio + offset); |
|
} |
|
|
|
static int scan_switchcore(struct ssb_bus *bus, u8 coreidx) |
|
{ |
|
switch (bus->bustype) { |
|
case SSB_BUSTYPE_SSB: |
|
break; |
|
case SSB_BUSTYPE_PCI: |
|
return ssb_pci_switch_coreidx(bus, coreidx); |
|
case SSB_BUSTYPE_PCMCIA: |
|
return ssb_pcmcia_switch_coreidx(bus, coreidx); |
|
case SSB_BUSTYPE_SDIO: |
|
return ssb_sdio_scan_switch_coreidx(bus, coreidx); |
|
} |
|
return 0; |
|
} |
|
|
|
void ssb_iounmap(struct ssb_bus *bus) |
|
{ |
|
switch (bus->bustype) { |
|
case SSB_BUSTYPE_SSB: |
|
case SSB_BUSTYPE_PCMCIA: |
|
iounmap(bus->mmio); |
|
break; |
|
case SSB_BUSTYPE_PCI: |
|
#ifdef CONFIG_SSB_PCIHOST |
|
pci_iounmap(bus->host_pci, bus->mmio); |
|
#else |
|
WARN_ON(1); /* Can't reach this code. */ |
|
#endif |
|
break; |
|
case SSB_BUSTYPE_SDIO: |
|
break; |
|
} |
|
bus->mmio = NULL; |
|
bus->mapped_device = NULL; |
|
} |
|
|
|
static void __iomem *ssb_ioremap(struct ssb_bus *bus, |
|
unsigned long baseaddr) |
|
{ |
|
void __iomem *mmio = NULL; |
|
|
|
switch (bus->bustype) { |
|
case SSB_BUSTYPE_SSB: |
|
/* Only map the first core for now. */ |
|
fallthrough; |
|
case SSB_BUSTYPE_PCMCIA: |
|
mmio = ioremap(baseaddr, SSB_CORE_SIZE); |
|
break; |
|
case SSB_BUSTYPE_PCI: |
|
#ifdef CONFIG_SSB_PCIHOST |
|
mmio = pci_iomap(bus->host_pci, 0, ~0UL); |
|
#else |
|
WARN_ON(1); /* Can't reach this code. */ |
|
#endif |
|
break; |
|
case SSB_BUSTYPE_SDIO: |
|
/* Nothing to ioremap in the SDIO case, just fake it */ |
|
mmio = (void __iomem *)baseaddr; |
|
break; |
|
} |
|
|
|
return mmio; |
|
} |
|
|
|
static int we_support_multiple_80211_cores(struct ssb_bus *bus) |
|
{ |
|
/* More than one 802.11 core is only supported by special chips. |
|
* There are chips with two 802.11 cores, but with dangling |
|
* pins on the second core. Be careful and reject them here. |
|
*/ |
|
|
|
#ifdef CONFIG_SSB_PCIHOST |
|
if (bus->bustype == SSB_BUSTYPE_PCI) { |
|
if (bus->host_pci->vendor == PCI_VENDOR_ID_BROADCOM && |
|
((bus->host_pci->device == 0x4313) || |
|
(bus->host_pci->device == 0x431A) || |
|
(bus->host_pci->device == 0x4321) || |
|
(bus->host_pci->device == 0x4324))) |
|
return 1; |
|
} |
|
#endif /* CONFIG_SSB_PCIHOST */ |
|
return 0; |
|
} |
|
|
|
int ssb_bus_scan(struct ssb_bus *bus, |
|
unsigned long baseaddr) |
|
{ |
|
int err = -ENOMEM; |
|
void __iomem *mmio; |
|
u32 idhi, cc, rev, tmp; |
|
int dev_i, i; |
|
struct ssb_device *dev; |
|
int nr_80211_cores = 0; |
|
|
|
mmio = ssb_ioremap(bus, baseaddr); |
|
if (!mmio) |
|
goto out; |
|
bus->mmio = mmio; |
|
|
|
err = scan_switchcore(bus, 0); /* Switch to first core */ |
|
if (err) |
|
goto err_unmap; |
|
|
|
idhi = scan_read32(bus, 0, SSB_IDHIGH); |
|
cc = (idhi & SSB_IDHIGH_CC) >> SSB_IDHIGH_CC_SHIFT; |
|
rev = (idhi & SSB_IDHIGH_RCLO); |
|
rev |= (idhi & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT; |
|
|
|
bus->nr_devices = 0; |
|
if (cc == SSB_DEV_CHIPCOMMON) { |
|
tmp = scan_read32(bus, 0, SSB_CHIPCO_CHIPID); |
|
|
|
bus->chip_id = (tmp & SSB_CHIPCO_IDMASK); |
|
bus->chip_rev = (tmp & SSB_CHIPCO_REVMASK) >> |
|
SSB_CHIPCO_REVSHIFT; |
|
bus->chip_package = (tmp & SSB_CHIPCO_PACKMASK) >> |
|
SSB_CHIPCO_PACKSHIFT; |
|
if (rev >= 4) { |
|
bus->nr_devices = (tmp & SSB_CHIPCO_NRCORESMASK) >> |
|
SSB_CHIPCO_NRCORESSHIFT; |
|
} |
|
tmp = scan_read32(bus, 0, SSB_CHIPCO_CAP); |
|
bus->chipco.capabilities = tmp; |
|
} else { |
|
if (bus->bustype == SSB_BUSTYPE_PCI) { |
|
bus->chip_id = pcidev_to_chipid(bus->host_pci); |
|
bus->chip_rev = bus->host_pci->revision; |
|
bus->chip_package = 0; |
|
} else { |
|
bus->chip_id = 0x4710; |
|
bus->chip_rev = 0; |
|
bus->chip_package = 0; |
|
} |
|
} |
|
pr_info("Found chip with id 0x%04X, rev 0x%02X and package 0x%02X\n", |
|
bus->chip_id, bus->chip_rev, bus->chip_package); |
|
if (!bus->nr_devices) |
|
bus->nr_devices = chipid_to_nrcores(bus->chip_id); |
|
if (bus->nr_devices > ARRAY_SIZE(bus->devices)) { |
|
pr_err("More than %d ssb cores found (%d)\n", |
|
SSB_MAX_NR_CORES, bus->nr_devices); |
|
goto err_unmap; |
|
} |
|
if (bus->bustype == SSB_BUSTYPE_SSB) { |
|
/* Now that we know the number of cores, |
|
* remap the whole IO space for all cores. |
|
*/ |
|
err = -ENOMEM; |
|
iounmap(mmio); |
|
mmio = ioremap(baseaddr, SSB_CORE_SIZE * bus->nr_devices); |
|
if (!mmio) |
|
goto out; |
|
bus->mmio = mmio; |
|
} |
|
|
|
/* Fetch basic information about each core/device */ |
|
for (i = 0, dev_i = 0; i < bus->nr_devices; i++) { |
|
err = scan_switchcore(bus, i); |
|
if (err) |
|
goto err_unmap; |
|
dev = &(bus->devices[dev_i]); |
|
|
|
idhi = scan_read32(bus, i, SSB_IDHIGH); |
|
dev->id.coreid = (idhi & SSB_IDHIGH_CC) >> SSB_IDHIGH_CC_SHIFT; |
|
dev->id.revision = (idhi & SSB_IDHIGH_RCLO); |
|
dev->id.revision |= (idhi & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT; |
|
dev->id.vendor = (idhi & SSB_IDHIGH_VC) >> SSB_IDHIGH_VC_SHIFT; |
|
dev->core_index = i; |
|
dev->bus = bus; |
|
dev->ops = bus->ops; |
|
|
|
pr_debug("Core %d found: %s (cc 0x%03X, rev 0x%02X, vendor 0x%04X)\n", |
|
i, ssb_core_name(dev->id.coreid), |
|
dev->id.coreid, dev->id.revision, dev->id.vendor); |
|
|
|
switch (dev->id.coreid) { |
|
case SSB_DEV_80211: |
|
nr_80211_cores++; |
|
if (nr_80211_cores > 1) { |
|
if (!we_support_multiple_80211_cores(bus)) { |
|
pr_debug("Ignoring additional 802.11 core\n"); |
|
continue; |
|
} |
|
} |
|
break; |
|
case SSB_DEV_EXTIF: |
|
#ifdef CONFIG_SSB_DRIVER_EXTIF |
|
if (bus->extif.dev) { |
|
pr_warn("WARNING: Multiple EXTIFs found\n"); |
|
break; |
|
} |
|
bus->extif.dev = dev; |
|
#endif /* CONFIG_SSB_DRIVER_EXTIF */ |
|
break; |
|
case SSB_DEV_CHIPCOMMON: |
|
if (bus->chipco.dev) { |
|
pr_warn("WARNING: Multiple ChipCommon found\n"); |
|
break; |
|
} |
|
bus->chipco.dev = dev; |
|
break; |
|
case SSB_DEV_MIPS: |
|
case SSB_DEV_MIPS_3302: |
|
#ifdef CONFIG_SSB_DRIVER_MIPS |
|
if (bus->mipscore.dev) { |
|
pr_warn("WARNING: Multiple MIPS cores found\n"); |
|
break; |
|
} |
|
bus->mipscore.dev = dev; |
|
#endif /* CONFIG_SSB_DRIVER_MIPS */ |
|
break; |
|
case SSB_DEV_PCI: |
|
case SSB_DEV_PCIE: |
|
#ifdef CONFIG_SSB_DRIVER_PCICORE |
|
if (bus->bustype == SSB_BUSTYPE_PCI) { |
|
/* Ignore PCI cores on PCI-E cards. |
|
* Ignore PCI-E cores on PCI cards. |
|
*/ |
|
if (dev->id.coreid == SSB_DEV_PCI) { |
|
if (pci_is_pcie(bus->host_pci)) |
|
continue; |
|
} else { |
|
if (!pci_is_pcie(bus->host_pci)) |
|
continue; |
|
} |
|
} |
|
if (bus->pcicore.dev) { |
|
pr_warn("WARNING: Multiple PCI(E) cores found\n"); |
|
break; |
|
} |
|
bus->pcicore.dev = dev; |
|
#endif /* CONFIG_SSB_DRIVER_PCICORE */ |
|
break; |
|
case SSB_DEV_ETHERNET: |
|
if (bus->bustype == SSB_BUSTYPE_PCI) { |
|
if (bus->host_pci->vendor == PCI_VENDOR_ID_BROADCOM && |
|
(bus->host_pci->device & 0xFF00) == 0x4300) { |
|
/* This is a dangling ethernet core on a |
|
* wireless device. Ignore it. |
|
*/ |
|
continue; |
|
} |
|
} |
|
break; |
|
default: |
|
break; |
|
} |
|
|
|
dev_i++; |
|
} |
|
bus->nr_devices = dev_i; |
|
|
|
err = 0; |
|
out: |
|
return err; |
|
err_unmap: |
|
ssb_iounmap(bus); |
|
goto out; |
|
}
|
|
|