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_printk(KERN_ERR PFX "CHIPID not in nrcores fallback list\n"); } return 1; }
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_printk(KERN_ERR PFX "PCI-ID not in fallback list\n"); } return chipid_fallback; }
/* Lowlevel coreswitching */ int ssb_pci_switch_coreidx(struct ssb_bus *bus, u8 coreidx) { int err; int attempts = 0; u32 cur_core; while (1) { err = pci_write_config_dword(bus->host_pci, SSB_BAR0_WIN, (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE); if (err) goto error; err = pci_read_config_dword(bus->host_pci, SSB_BAR0_WIN, &cur_core); if (err) goto error; cur_core = (cur_core - SSB_ENUM_BASE) / SSB_CORE_SIZE; if (cur_core == coreidx) break; if (attempts++ > SSB_BAR0_MAX_RETRIES) goto error; udelay(10); } return 0; error: ssb_printk(KERN_ERR PFX "Failed to switch to core %u\n", coreidx); return -ENODEV; }
ssize_t ssb_attr_sprom_store(struct ssb_bus *bus, const char *buf, size_t count, int (*sprom_check_crc)(const u16 *sprom, size_t size), int (*sprom_write)(struct ssb_bus *bus, const u16 *sprom)) { u16 *sprom; int res = 0, err = -ENOMEM; size_t sprom_size_words = bus->sprom_size; struct ssb_freeze_context freeze; sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL); if (!sprom) goto out; err = hex2sprom(sprom, buf, count, sprom_size_words); if (err) { err = -EINVAL; goto out_kfree; } err = sprom_check_crc(sprom, sprom_size_words); if (err) { err = -EINVAL; goto out_kfree; } err = -ERESTARTSYS; if (mutex_lock_interruptible(&bus->sprom_mutex)) goto out_kfree; err = ssb_devices_freeze(bus, &freeze); if (err) { ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze all devices\n"); goto out_unlock; } res = sprom_write(bus, sprom); err = ssb_devices_thaw(&freeze); if (err) ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n"); out_unlock: mutex_unlock(&bus->sprom_mutex); out_kfree: kfree(sprom); out: if (res) return res; return err ? err : count; }
static int sprom_do_write(struct ssb_bus *bus, const u16 *sprom) { struct pci_dev *pdev = bus->host_pci; int i, err; u32 spromctl; ssb_printk(KERN_NOTICE PFX "Writing SPROM. Do NOT turn off the power! Please stand by...\n"); err = pci_read_config_dword(pdev, SSB_SPROMCTL, &spromctl); if (err) goto err_ctlreg; spromctl |= SSB_SPROMCTL_WE; err = pci_write_config_dword(pdev, SSB_SPROMCTL, spromctl); if (err) goto err_ctlreg; ssb_printk(KERN_NOTICE PFX "[ 0%%"); msleep(500); for (i = 0; i < SSB_SPROMSIZE_WORDS; i++) { if (i == SSB_SPROMSIZE_WORDS / 4) ssb_printk("25%%"); else if (i == SSB_SPROMSIZE_WORDS / 2) ssb_printk("50%%"); else if (i == (SSB_SPROMSIZE_WORDS / 4) * 3) ssb_printk("75%%"); else if (i % 2) ssb_printk("."); writew(sprom[i], bus->mmio + SSB_SPROM_BASE + (i * 2)); mmiowb(); msleep(20); } err = pci_read_config_dword(pdev, SSB_SPROMCTL, &spromctl); if (err) goto err_ctlreg; spromctl &= ~SSB_SPROMCTL_WE; err = pci_write_config_dword(pdev, SSB_SPROMCTL, spromctl); if (err) goto err_ctlreg; msleep(500); ssb_printk("100%% ]\n"); ssb_printk(KERN_NOTICE PFX "SPROM written.\n"); return 0; err_ctlreg: ssb_printk(KERN_ERR PFX "Could not access SPROM control register.\n"); return err; }
/* 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 ssb_pcicore_plat_dev_init(struct pci_dev *d) { if (d->bus->ops != &ssb_pcicore_pciops) { /* This is not a device on the PCI-core bridge. */ return -ENODEV; } ssb_printk(KERN_INFO "PCI: Fixing up device %s\n", pci_name(d)); /* Fix up interrupt lines */ d->irq = ssb_mips_irq(extpci_core->dev) + 2; pci_write_config_byte(d, PCI_INTERRUPT_LINE, d->irq); return 0; }
static void __init ssb_fixup_pcibridge(struct pci_dev *dev) { if (dev->bus->number != 0 || PCI_SLOT(dev->devfn) != 0) return; ssb_printk(KERN_INFO "PCI: fixing up bridge\n"); /* Enable PCI bridge bus mastering and memory space */ pci_set_master(dev); pcibios_enable_device(dev, ~0); /* Enable PCI bridge BAR1 prefetch and burst */ pci_write_config_dword(dev, SSB_BAR1_CONTROL, 3); /* Make sure our latency is high enough to handle the devices behind us */ pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0xa8); }
int ssb_pcicore_plat_dev_init(struct pci_dev *d) { struct resource *res; int pos, size; u32 *base; if (d->bus->ops != &ssb_pcicore_pciops) { return -ENODEV; } ssb_printk(KERN_INFO "PCI: Fixing up device %s\n", pci_name(d)); for (pos = 0; pos < 6; pos++) { res = &d->resource[pos]; if (res->flags & IORESOURCE_IO) base = &ssb_pcicore_pcibus_iobase; else base = &ssb_pcicore_pcibus_membase; res->flags |= IORESOURCE_PCI_FIXED; if (res->end) { size = res->end - res->start + 1; if (*base & (size - 1)) *base = (*base + size) & ~(size - 1); res->start = *base; res->end = res->start + size - 1; *base += size; pci_write_config_dword(d, PCI_BASE_ADDRESS_0 + (pos << 2), res->start); } if (d->bus->number == 0 && PCI_SLOT(d->devfn) == 0) break; } d->irq = ssb_mips_irq(extpci_core->dev) + 2; pci_write_config_byte(d, PCI_INTERRUPT_LINE, d->irq); return 0; }
int ssb_pci_switch_core(struct ssb_bus *bus, struct ssb_device *dev) { int err; unsigned long flags; #if SSB_VERBOSE_PCICORESWITCH_DEBUG ssb_printk(KERN_INFO PFX "Switching to %s core, index %d\n", ssb_core_name(dev->id.coreid), dev->core_index); #endif spin_lock_irqsave(&bus->bar_lock, flags); err = ssb_pci_switch_coreidx(bus, dev->core_index); if (!err) bus->mapped_device = dev; spin_unlock_irqrestore(&bus->bar_lock, flags); return err; }