Created
July 19, 2019 19:01
-
-
Save fvollmer/0291f2c01bf6768dc10ec1e6bc452983 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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 <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> | |
#include "ssb_private.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: | |
ssb_err("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: | |
ssb_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 | |
SSB_BUG_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 | |
SSB_BUG_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); | |
printk("idhi 0x%x", idhi); | |
printk("bus->mmio 0x%x", bus->mmio); | |
printk("Chipcommon:"); | |
printk("chip id: 0x%x" , readl(0xa0000000 + 0x18000000 + 0x0000)&0x0000FFFF); | |
printk("chip revision: 0x%x" , readl(0xa0000000 + 0x18000000 + 0x0000)&0x000F0000); | |
printk("package Options: 0x%x", readl(0xa0000000 + 0x18000000 + 0x0000)&0x00F00000); | |
printk("number of cores 0x%x" , readl(0xa0000000 + 0x18000000 + 0x0000)&0x0F000000); | |
printk("chip type 0x%x" , readl(0xa0000000 + 0x18000000 + 0x0000)&0xF0000000); | |
printk("capabilities: 0x%x" , readl(0xa0000000 + 0x18000000 + 0x0004)); | |
printk("core id low: 0x%x", readl(0xa0000000 + 0x18000000 + 0x0FF8)); | |
printk("core id high: 0x%x", readl(0xa0000000 + 0x18000000 + 0x0FFC)); | |
printk("1:"); | |
printk("core id low: 0x%x", readl(0xa0000000 + 0x18001000 + 0x0FF8)); | |
printk("core id high: 0x%x", readl(0xa0000000 + 0x18001000 + 0x0FFC)); | |
printk("2:"); | |
printk("core id low: 0x%x", readl(0xa0000000 + 0x18002000 + 0x0FF8)); | |
printk("core id high: 0x%x", readl(0xa0000000 + 0x18002000 + 0x0FFC)); | |
/* | |
printk("mips:"); | |
printk("core id low: 0x%x", readl(0xa0000000 + 0x18005000 + 0x0FF8)); | |
printk("core id high: 0x%x", readl(0xa0000000 + 0x18005000 + 0x0FFC)); | |
printk("memc:"); | |
printk("core id low: 0x%x", readl(0xa0000000 + 0x18008000 + 0x0FF8)); | |
printk("core id high: 0x%x", readl(0xa0000000 + 0x18008000 + 0x0FFC));*/ | |
//printk("idhi manual: 0x%x", readl(0xb8000000 + 0x0FFC)); | |
cc = (idhi & SSB_IDHIGH_CC) >> SSB_IDHIGH_CC_SHIFT; | |
rev = (idhi & SSB_IDHIGH_RCLO); | |
rev |= (idhi & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT; | |
printk("cc: 0x%x", cc); | |
printk("scan_read32: 0x%x", scan_read32(bus, 0, SSB_CHIPCO_CHIPID)); | |
bus->nr_devices = 0; | |
//if (cc == SSB_DEV_CHIPCOMMON) { | |
if (1) { | |
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; | |
} | |
} | |
ssb_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)) { | |
ssb_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; | |
} | |
printk("bus->nr_devices %d", bus->nr_devices); | |
bus->nr_devices = 5; | |
/* 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; | |
printk(KERN_INFO PFX | |
"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)) { | |
ssb_dbg("Ignoring additional 802.11 core\n"); | |
continue; | |
} | |
} | |
break; | |
case SSB_DEV_EXTIF: | |
#ifdef CONFIG_SSB_DRIVER_EXTIF | |
if (bus->extif.dev) { | |
ssb_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) { | |
ssb_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) { | |
ssb_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) { | |
ssb_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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment