/************************************************** * Code for hostmode operation.
**************************************************/
#ifdef CONFIG_SSB_PCICORE_HOSTMODE
#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)))
/* Global lock is OK, as we won't have more than one extpci anyway. */ static DEFINE_SPINLOCK(cfgspace_lock); /* Core to access the external PCI config space. Can only have one. */ staticstruct ssb_pcicore *extpci_core;
/* 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;
}
dev_info(&d->dev, "PCI: Fixing up device %s\n", pci_name(d));
/* Early PCI fixup for a device on the PCI-core bridge. */ staticvoid ssb_pcicore_fixup_pcibridge(struct pci_dev *dev)
{
u8 lat;
if (dev->bus->ops != &ssb_pcicore_pciops) { /* This is not a device on the PCI-core bridge. */ return;
} if (dev->bus->number != 0 || PCI_SLOT(dev->devfn) != 0) return;
dev_info(&dev->dev, "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) {
dev_err(&dev->dev, "PCI: SSB bridge enable failed\n"); return;
}
/* Make sure our latency is high enough to handle the devices behind us */
lat = 168;
dev_info(&dev->dev, "PCI: Fixing latency timer of device %s to %u\n",
pci_name(dev), lat);
pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
}
DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, ssb_pcicore_fixup_pcibridge);
/* PCI device IRQ mapping. */ int ssb_pcicore_pcibios_map_irq(conststruct pci_dev *dev, u8 slot, u8 pin)
{ if (dev->bus->ops != &ssb_pcicore_pciops) { /* This is not a device on the PCI-core bridge. */ return -ENODEV;
} return ssb_mips_irq(extpci_core->dev) + 2;
}
if (WARN_ON(extpci_core)) return;
extpci_core = pc;
dev_dbg(pc->dev->dev, "PCIcore in host mode found\n"); /* Reset devices on the external PCI bus */
val = SSB_PCICORE_CTL_RST_OE;
val |= SSB_PCICORE_CTL_CLK_OE;
pcicore_write32(pc, SSB_PCICORE_CTL, val);
val |= SSB_PCICORE_CTL_CLK; /* Clock on */
pcicore_write32(pc, SSB_PCICORE_CTL, val);
udelay(150); /* Assertion time demanded by the PCI standard */
val |= SSB_PCICORE_CTL_RST; /* Deassert RST# */
pcicore_write32(pc, SSB_PCICORE_CTL, val);
val = SSB_PCICORE_ARBCTL_INTERN;
pcicore_write32(pc, SSB_PCICORE_ARBCTL, val);
udelay(1); /* Assertion time demanded by the PCI standard */
/* * Accessing PCI config without a proper delay after devices reset (not * GPIO reset) was causing reboots on WRT300N v1.0 (BCM4704). * Tested delay 850 us lowered reboot chance to 50-80%, 1000 us fixed it * completely. Flushing all writes was also tested but with no luck. * The same problem was reported for WRT350N v1 (BCM4705), so we just * sleep here unconditionally.
*/
usleep_range(1000, 2000);
/* Ok, ready to run, register it to the system. * The following needs change, if we want to port hostmode * to non-MIPS platform.
*/
ssb_pcicore_controller.io_map_base = (unsignedlong)ioremap(SSB_PCI_MEM, 0x04000000);
set_io_port_base(ssb_pcicore_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(&ssb_pcicore_controller);
}
if (bus->sprom.boardflags_lo & SSB_PCICORE_BFL_NOPCI) return 0;
/* The 200-pin BCM4712 package does not bond out PCI. Even when * PCI is bonded out, some boards may leave the pins floating.
*/ if (bus->chip_id == 0x4712) { if (bus->chip_package == SSB_CHIPPACK_BCM4712S) return 0; if (bus->chip_package == SSB_CHIPPACK_BCM4712M) return 0;
} if (bus->chip_id == 0x5350) return 0;
v = (1 << 30); /* Start of Transaction */
v |= (1 << 29); /* Read Transaction */
v |= (1 << 17); /* Turnaround */ if (pc->dev->id.revision < 10)
v |= (u32)device << 22;
v |= (u32)address << 18;
pcicore_write32(pc, 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 */) {
udelay(10);
ret = pcicore_read32(pc, mdio_data); break;
}
msleep(1);
}
pcicore_write32(pc, mdio_control, 0); return ret;
}
v = (1 << 30); /* Start of Transaction */
v |= (1 << 28); /* Write Transaction */
v |= (1 << 17); /* Turnaround */ if (pc->dev->id.revision < 10)
v |= (u32)device << 22;
v |= (u32)address << 18;
v |= data;
pcicore_write32(pc, 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 */) break;
msleep(1);
}
pcicore_write32(pc, mdio_control, 0);
}
if (dev->bus->bustype != SSB_BUSTYPE_PCI) { /* This SSB device is not on a PCI host-bus. So the IRQs are * not routed through the PCI core. * So we must not enable routing through the PCI core.
*/ goto out;
}
if (!pdev) goto out;
bus = pdev->bus;
might_sleep_if(pdev->id.coreid != SSB_DEV_PCI);
/* Enable interrupts for this device. */ if ((pdev->id.revision >= 6) || (pdev->id.coreid == SSB_DEV_PCIE)) {
u32 coremask;
/* Calculate the "coremask" for the device. */
coremask = (1 << dev->core_index);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.