ssb: Add Gigabit Ethernet driver

This adds the Gigabit Ethernet driver for the SSB
Gigabit Ethernet core. This driver actually is a frontend to
the Tigon3 driver. So the real work is done by tg3.
This device is used in the Linksys WRT350N.

Signed-off-by: Michael Buesch <mb@bu3sch.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Michael Buesch
2008-02-29 11:36:12 +01:00
committed by John W. Linville
parent 69d3b6f491
commit aab547ce0d
11 changed files with 715 additions and 72 deletions

View File

@@ -60,77 +60,6 @@ static DEFINE_SPINLOCK(cfgspace_lock);
/* Core to access the external PCI config space. Can only have one. */
static struct ssb_pcicore *extpci_core;
static u32 ssb_pcicore_pcibus_iobase = 0x100;
static u32 ssb_pcicore_pcibus_membase = SSB_PCI_DMA;
int pcibios_plat_dev_init(struct pci_dev *d)
{
struct resource *res;
int pos, size;
u32 *base;
ssb_printk(KERN_INFO "PCI: Fixing up device %s\n",
pci_name(d));
/* Fix up resource bases */
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);
}
/* Fix up PCI bridge BAR0 only */
if (d->bus->number == 0 && PCI_SLOT(d->devfn) == 0)
break;
}
/* 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)
{
u8 lat;
if (dev->bus->number != 0 || PCI_SLOT(dev->devfn) != 0)
return;
ssb_printk(KERN_INFO "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) {
ssb_printk(KERN_ERR "PCI: SSB bridge enable failed\n");
return;
}
/* 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 */
lat = 168;
ssb_printk(KERN_INFO "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_fixup_pcibridge);
int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
return ssb_mips_irq(extpci_core->dev) + 2;
}
static u32 get_cfgspace_addr(struct ssb_pcicore *pc,
unsigned int bus, unsigned int dev,
@@ -320,6 +249,95 @@ static struct pci_controller ssb_pcicore_controller = {
.mem_offset = 0x24000000,
};
static u32 ssb_pcicore_pcibus_iobase = 0x100;
static u32 ssb_pcicore_pcibus_membase = SSB_PCI_DMA;
/* 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)
{
struct resource *res;
int pos, size;
u32 *base;
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 resource bases */
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);
}
/* Fix up PCI bridge BAR0 only */
if (d->bus->number == 0 && PCI_SLOT(d->devfn) == 0)
break;
}
/* 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;
}
/* Early PCI fixup for a device on the PCI-core bridge. */
static void 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;
ssb_printk(KERN_INFO "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) {
ssb_printk(KERN_ERR "PCI: SSB bridge enable failed\n");
return;
}
/* 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 */
lat = 168;
ssb_printk(KERN_INFO "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(const struct 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;
}
static void ssb_pcicore_init_hostmode(struct ssb_pcicore *pc)
{
u32 val;