[SSB]: add Sonics Silicon Backplane bus support
Michael Buesch [Tue, 18 Sep 2007 19:12:50 +0000 (15:12 -0400)]
SSB is an SoC bus used in a number of embedded devices.  The most
well-known of these devices is probably the Linksys WRT54G, but there
are others as well.  The bus is also used internally on the BCM43xx
and BCM44xx devices from Broadcom.

This patch also includes support for SSB ID tables in modules, so
that SSB drivers can be loaded automatically.

Signed-off-by: Michael Buesch <mb@bu3sch.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

24 files changed:
MAINTAINERS
drivers/Kconfig
drivers/Makefile
drivers/ssb/Kconfig [new file with mode: 0644]
drivers/ssb/Makefile [new file with mode: 0644]
drivers/ssb/b43_pci_bridge.c [new file with mode: 0644]
drivers/ssb/driver_chipcommon.c [new file with mode: 0644]
drivers/ssb/driver_extif.c [new file with mode: 0644]
drivers/ssb/driver_mipscore.c [new file with mode: 0644]
drivers/ssb/driver_pcicore.c [new file with mode: 0644]
drivers/ssb/main.c [new file with mode: 0644]
drivers/ssb/pci.c [new file with mode: 0644]
drivers/ssb/pcihost_wrapper.c [new file with mode: 0644]
drivers/ssb/pcmcia.c [new file with mode: 0644]
drivers/ssb/scan.c [new file with mode: 0644]
drivers/ssb/ssb_private.h [new file with mode: 0644]
include/linux/mod_devicetable.h
include/linux/ssb/ssb.h [new file with mode: 0644]
include/linux/ssb/ssb_driver_chipcommon.h [new file with mode: 0644]
include/linux/ssb/ssb_driver_extif.h [new file with mode: 0644]
include/linux/ssb/ssb_driver_mips.h [new file with mode: 0644]
include/linux/ssb/ssb_driver_pci.h [new file with mode: 0644]
include/linux/ssb/ssb_regs.h [new file with mode: 0644]
scripts/mod/file2alias.c

index 3f07d5f..7524cd8 100644 (file)
@@ -3446,6 +3446,12 @@ M:       tsbogend@alpha.franken.de
 L:     netdev@vger.kernel.org
 S:     Maintained
 
+SONICS SILICON BACKPLANE DRIVER (SSB)
+P:     Michael Buesch
+M:     mb@bu3sch.de
+L:     netdev@vger.kernel.org
+S:     Maintained
+
 SONY VAIO CONTROL DEVICE DRIVER
 P:     Mattia Dongili
 M:     malattia@linux.it
index 3e1c442..7bdae47 100644 (file)
@@ -58,6 +58,8 @@ source "drivers/power/Kconfig"
 
 source "drivers/hwmon/Kconfig"
 
+source "drivers/ssb/Kconfig"
+
 source "drivers/mfd/Kconfig"
 
 source "drivers/media/Kconfig"
index f0878b2..a168eac 100644 (file)
@@ -88,3 +88,4 @@ obj-$(CONFIG_DMA_ENGINE)      += dma/
 obj-$(CONFIG_HID)              += hid/
 obj-$(CONFIG_PPC_PS3)          += ps3/
 obj-$(CONFIG_OF)               += of/
+obj-$(CONFIG_SSB)              += ssb/
diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig
new file mode 100644 (file)
index 0000000..b4a5e5e
--- /dev/null
@@ -0,0 +1,117 @@
+menu "Sonics Silicon Backplane"
+
+config SSB_POSSIBLE
+       bool
+       depends on HAS_IOMEM
+       default y
+
+config SSB
+       tristate "Sonics Silicon Backplane support"
+       depends on SSB_POSSIBLE
+       help
+         Support for the Sonics Silicon Backplane bus.
+         You only need to enable this option, if you are
+         configuring a kernel for an embedded system with
+         this bus.
+         It will be auto-selected if needed in other
+         environments.
+
+         The module will be called ssb.
+
+         If unsure, say N.
+
+config SSB_PCIHOST_POSSIBLE
+       bool
+       depends on SSB && PCI
+       default y
+
+config SSB_PCIHOST
+       bool "Support for SSB on PCI-bus host"
+       depends on SSB_PCIHOST_POSSIBLE
+       default y
+       help
+         Support for a Sonics Silicon Backplane on top
+         of a PCI device.
+
+         If unsure, say Y
+
+config SSB_PCMCIAHOST_POSSIBLE
+       bool
+       depends on SSB && PCMCIA && EXPERIMENTAL
+       default y
+
+config SSB_PCMCIAHOST
+       bool "Support for SSB on PCMCIA-bus host (EXPERIMENTAL)"
+       depends on SSB_PCMCIAHOST_POSSIBLE
+       help
+         Support for a Sonics Silicon Backplane on top
+         of a PCMCIA device.
+
+         If unsure, say N
+
+config SSB_SILENT
+       bool "No SSB kernel messages"
+       depends on SSB && EMBEDDED
+       help
+         This option turns off all Sonics Silicon Backplane printks.
+         Note that you won't be able to identify problems, once
+         messages are turned off.
+         This might only be desired for production kernels on
+         embedded devices to reduce the kernel size.
+
+         Say N
+
+config SSB_DEBUG
+       bool "SSB debugging"
+       depends on SSB && !SSB_SILENT
+       help
+         This turns on additional runtime checks and debugging
+         messages. Turn this on for SSB troubleshooting.
+
+         If unsure, say N
+
+config SSB_SERIAL
+       bool
+       depends on SSB
+       # ChipCommon and ExtIf serial support routines.
+
+config SSB_DRIVER_PCICORE_POSSIBLE
+       bool
+       depends on SSB_PCIHOST
+       default y
+
+config SSB_DRIVER_PCICORE
+       bool "SSB PCI core driver"
+       depends on SSB_DRIVER_PCICORE_POSSIBLE
+       help
+         Driver for the Sonics Silicon Backplane attached
+         Broadcom PCI core.
+
+         If unsure, say Y
+
+config SSB_PCICORE_HOSTMODE
+       bool "Hostmode support for SSB PCI core (EXPERIMENTAL)"
+       depends on SSB_DRIVER_PCICORE && SSB_DRIVER_MIPS && EXPERIMENTAL
+       help
+         PCIcore hostmode operation (external PCI bus).
+
+config SSB_DRIVER_MIPS
+       bool "SSB Broadcom MIPS core driver (EXPERIMENTAL)"
+       depends on SSB && MIPS && EXPERIMENTAL
+       select SSB_SERIAL
+       help
+         Driver for the Sonics Silicon Backplane attached
+         Broadcom MIPS core.
+
+         If unsure, say N
+
+config SSB_DRIVER_EXTIF
+       bool "SSB Broadcom EXTIF core driver (EXPERIMENTAL)"
+       depends on SSB_DRIVER_MIPS && EXPERIMENTAL
+       help
+         Driver for the Sonics Silicon Backplane attached
+         Broadcom EXTIF core.
+
+         If unsure, say N
+
+endmenu
diff --git a/drivers/ssb/Makefile b/drivers/ssb/Makefile
new file mode 100644 (file)
index 0000000..7be3975
--- /dev/null
@@ -0,0 +1,18 @@
+# core
+ssb-y                                  += main.o scan.o
+
+# host support
+ssb-$(CONFIG_SSB_PCIHOST)              += pci.o pcihost_wrapper.o
+ssb-$(CONFIG_SSB_PCMCIAHOST)           += pcmcia.o
+
+# built-in drivers
+ssb-y                                  += driver_chipcommon.o
+ssb-$(CONFIG_SSB_DRIVER_MIPS)          += driver_mipscore.o
+ssb-$(CONFIG_SSB_DRIVER_EXTIF)         += driver_extif.o
+ssb-$(CONFIG_SSB_DRIVER_PCICORE)       += driver_pcicore.o
+
+# b43 pci-ssb-bridge driver
+# Not strictly a part of SSB, but kept here for convenience
+ssb-$(CONFIG_SSB_PCIHOST)              += b43_pci_bridge.o
+
+obj-$(CONFIG_SSB)                      += ssb.o
diff --git a/drivers/ssb/b43_pci_bridge.c b/drivers/ssb/b43_pci_bridge.c
new file mode 100644 (file)
index 0000000..fa3bd29
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Broadcom 43xx PCI-SSB bridge module
+ *
+ * This technically is a seperate PCI driver module, but
+ * because of its small size we include it in the SSB core
+ * instead of creating a standalone module.
+ *
+ * Copyright 2007  Michael Buesch <mb@bu3sch.de>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/pci.h>
+#include <linux/ssb/ssb.h>
+
+
+static const struct pci_device_id b43_pci_bridge_tbl[] = {
+       { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4301) },
+       { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4307) },
+       { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4311) },
+       { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4312) },
+       { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4318) },
+       { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4319) },
+       { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4320) },
+       { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4321) },
+       { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4324) },
+       { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4325) },
+       { 0, },
+};
+MODULE_DEVICE_TABLE(pci, b43_pci_bridge_tbl);
+
+static struct pci_driver b43_pci_bridge_driver = {
+       .name = "b43-pci-bridge",
+       .id_table = b43_pci_bridge_tbl,
+};
+
+
+int __init b43_pci_ssb_bridge_init(void)
+{
+       return ssb_pcihost_register(&b43_pci_bridge_driver);
+}
+
+void __exit b43_pci_ssb_bridge_exit(void)
+{
+       ssb_pcihost_unregister(&b43_pci_bridge_driver);
+}
diff --git a/drivers/ssb/driver_chipcommon.c b/drivers/ssb/driver_chipcommon.c
new file mode 100644 (file)
index 0000000..a890544
--- /dev/null
@@ -0,0 +1,446 @@
+/*
+ * Sonics Silicon Backplane
+ * Broadcom ChipCommon core driver
+ *
+ * Copyright 2005, Broadcom Corporation
+ * Copyright 2006, 2007, Michael Buesch <mb@bu3sch.de>
+ *
+ * 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 "ssb_private.h"
+
+
+/* Clock sources */
+enum ssb_clksrc {
+       /* PCI clock */
+       SSB_CHIPCO_CLKSRC_PCI,
+       /* Crystal slow clock oscillator */
+       SSB_CHIPCO_CLKSRC_XTALOS,
+       /* Low power oscillator */
+       SSB_CHIPCO_CLKSRC_LOPWROS,
+};
+
+
+static inline u32 chipco_read32(struct ssb_chipcommon *cc,
+                               u16 offset)
+{
+       return ssb_read32(cc->dev, offset);
+}
+
+static inline void chipco_write32(struct ssb_chipcommon *cc,
+                                 u16 offset,
+                                 u32 value)
+{
+       ssb_write32(cc->dev, offset, value);
+}
+
+static inline void chipco_write32_masked(struct ssb_chipcommon *cc, u16 offset,
+                                        u32 mask, u32 value)
+{
+       value &= mask;
+       value |= chipco_read32(cc, offset) & ~mask;
+       chipco_write32(cc, offset, value);
+}
+
+void ssb_chipco_set_clockmode(struct ssb_chipcommon *cc,
+                             enum ssb_clkmode mode)
+{
+       struct ssb_device *ccdev = cc->dev;
+       struct ssb_bus *bus;
+       u32 tmp;
+
+       if (!ccdev)
+               return;
+       bus = ccdev->bus;
+       /* chipcommon cores prior to rev6 don't support dynamic clock control */
+       if (ccdev->id.revision < 6)
+               return;
+       /* chipcommon cores rev10 are a whole new ball game */
+       if (ccdev->id.revision >= 10)
+               return;
+       if (!(cc->capabilities & SSB_CHIPCO_CAP_PCTL))
+               return;
+
+       switch (mode) {
+       case SSB_CLKMODE_SLOW:
+               tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL);
+               tmp |= SSB_CHIPCO_SLOWCLKCTL_FSLOW;
+               chipco_write32(cc, SSB_CHIPCO_SLOWCLKCTL, tmp);
+               break;
+       case SSB_CLKMODE_FAST:
+               ssb_pci_xtal(bus, SSB_GPIO_XTAL, 1); /* Force crystal on */
+               tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL);
+               tmp &= ~SSB_CHIPCO_SLOWCLKCTL_FSLOW;
+               tmp |= SSB_CHIPCO_SLOWCLKCTL_IPLL;
+               chipco_write32(cc, SSB_CHIPCO_SLOWCLKCTL, tmp);
+               break;
+       case SSB_CLKMODE_DYNAMIC:
+               tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL);
+               tmp &= ~SSB_CHIPCO_SLOWCLKCTL_FSLOW;
+               tmp &= ~SSB_CHIPCO_SLOWCLKCTL_IPLL;
+               tmp &= ~SSB_CHIPCO_SLOWCLKCTL_ENXTAL;
+               if ((tmp & SSB_CHIPCO_SLOWCLKCTL_SRC) != SSB_CHIPCO_SLOWCLKCTL_SRC_XTAL)
+                       tmp |= SSB_CHIPCO_SLOWCLKCTL_ENXTAL;
+               chipco_write32(cc, SSB_CHIPCO_SLOWCLKCTL, tmp);
+
+               /* for dynamic control, we have to release our xtal_pu "force on" */
+               if (tmp & SSB_CHIPCO_SLOWCLKCTL_ENXTAL)
+                       ssb_pci_xtal(bus, SSB_GPIO_XTAL, 0);
+               break;
+       default:
+               SSB_WARN_ON(1);
+       }
+}
+
+/* Get the Slow Clock Source */
+static enum ssb_clksrc chipco_pctl_get_slowclksrc(struct ssb_chipcommon *cc)
+{
+       struct ssb_bus *bus = cc->dev->bus;
+       u32 uninitialized_var(tmp);
+
+       if (cc->dev->id.revision < 6) {
+               if (bus->bustype == SSB_BUSTYPE_SSB ||
+                   bus->bustype == SSB_BUSTYPE_PCMCIA)
+                       return SSB_CHIPCO_CLKSRC_XTALOS;
+               if (bus->bustype == SSB_BUSTYPE_PCI) {
+                       pci_read_config_dword(bus->host_pci, SSB_GPIO_OUT, &tmp);
+                       if (tmp & 0x10)
+                               return SSB_CHIPCO_CLKSRC_PCI;
+                       return SSB_CHIPCO_CLKSRC_XTALOS;
+               }
+       }
+       if (cc->dev->id.revision < 10) {
+               tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL);
+               tmp &= 0x7;
+               if (tmp == 0)
+                       return SSB_CHIPCO_CLKSRC_LOPWROS;
+               if (tmp == 1)
+                       return SSB_CHIPCO_CLKSRC_XTALOS;
+               if (tmp == 2)
+                       return SSB_CHIPCO_CLKSRC_PCI;
+       }
+
+       return SSB_CHIPCO_CLKSRC_XTALOS;
+}
+
+/* Get maximum or minimum (depending on get_max flag) slowclock frequency. */
+static int chipco_pctl_clockfreqlimit(struct ssb_chipcommon *cc, int get_max)
+{
+       int uninitialized_var(limit);
+       enum ssb_clksrc clocksrc;
+       int divisor = 1;
+       u32 tmp;
+
+       clocksrc = chipco_pctl_get_slowclksrc(cc);
+       if (cc->dev->id.revision < 6) {
+               switch (clocksrc) {
+               case SSB_CHIPCO_CLKSRC_PCI:
+                       divisor = 64;
+                       break;
+               case SSB_CHIPCO_CLKSRC_XTALOS:
+                       divisor = 32;
+                       break;
+               default:
+                       SSB_WARN_ON(1);
+               }
+       } else if (cc->dev->id.revision < 10) {
+               switch (clocksrc) {
+               case SSB_CHIPCO_CLKSRC_LOPWROS:
+                       break;
+               case SSB_CHIPCO_CLKSRC_XTALOS:
+               case SSB_CHIPCO_CLKSRC_PCI:
+                       tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL);
+                       divisor = (tmp >> 16) + 1;
+                       divisor *= 4;
+                       break;
+               }
+       } else {
+               tmp = chipco_read32(cc, SSB_CHIPCO_SYSCLKCTL);
+               divisor = (tmp >> 16) + 1;
+               divisor *= 4;
+       }
+
+       switch (clocksrc) {
+       case SSB_CHIPCO_CLKSRC_LOPWROS:
+               if (get_max)
+                       limit = 43000;
+               else
+                       limit = 25000;
+               break;
+       case SSB_CHIPCO_CLKSRC_XTALOS:
+               if (get_max)
+                       limit = 20200000;
+               else
+                       limit = 19800000;
+               break;
+       case SSB_CHIPCO_CLKSRC_PCI:
+               if (get_max)
+                       limit = 34000000;
+               else
+                       limit = 25000000;
+               break;
+       }
+       limit /= divisor;
+
+       return limit;
+}
+
+static void chipco_powercontrol_init(struct ssb_chipcommon *cc)
+{
+       struct ssb_bus *bus = cc->dev->bus;
+
+       if (bus->chip_id == 0x4321) {
+               if (bus->chip_rev == 0)
+                       chipco_write32(cc, SSB_CHIPCO_CHIPCTL, 0x3A4);
+               else if (bus->chip_rev == 1)
+                       chipco_write32(cc, SSB_CHIPCO_CHIPCTL, 0xA4);
+       }
+
+       if (!(cc->capabilities & SSB_CHIPCO_CAP_PCTL))
+               return;
+
+       if (cc->dev->id.revision >= 10) {
+               /* Set Idle Power clock rate to 1Mhz */
+               chipco_write32(cc, SSB_CHIPCO_SYSCLKCTL,
+                              (chipco_read32(cc, SSB_CHIPCO_SYSCLKCTL) &
+                               0x0000FFFF) | 0x00040000);
+       } else {
+               int maxfreq;
+
+               maxfreq = chipco_pctl_clockfreqlimit(cc, 1);
+               chipco_write32(cc, SSB_CHIPCO_PLLONDELAY,
+                              (maxfreq * 150 + 999999) / 1000000);
+               chipco_write32(cc, SSB_CHIPCO_FREFSELDELAY,
+                              (maxfreq * 15 + 999999) / 1000000);
+       }
+}
+
+static void calc_fast_powerup_delay(struct ssb_chipcommon *cc)
+{
+       struct ssb_bus *bus = cc->dev->bus;
+       int minfreq;
+       unsigned int tmp;
+       u32 pll_on_delay;
+
+       if (bus->bustype != SSB_BUSTYPE_PCI)
+               return;
+       if (!(cc->capabilities & SSB_CHIPCO_CAP_PCTL))
+               return;
+
+       minfreq = chipco_pctl_clockfreqlimit(cc, 0);
+       pll_on_delay = chipco_read32(cc, SSB_CHIPCO_PLLONDELAY);
+       tmp = (((pll_on_delay + 2) * 1000000) + (minfreq - 1)) / minfreq;
+       SSB_WARN_ON(tmp & ~0xFFFF);
+
+       cc->fast_pwrup_delay = tmp;
+}
+
+void ssb_chipcommon_init(struct ssb_chipcommon *cc)
+{
+       if (!cc->dev)
+               return; /* We don't have a ChipCommon */
+       chipco_powercontrol_init(cc);
+       ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST);
+       calc_fast_powerup_delay(cc);
+}
+
+void ssb_chipco_suspend(struct ssb_chipcommon *cc, pm_message_t state)
+{
+       if (!cc->dev)
+               return;
+       ssb_chipco_set_clockmode(cc, SSB_CLKMODE_SLOW);
+}
+
+void ssb_chipco_resume(struct ssb_chipcommon *cc)
+{
+       if (!cc->dev)
+               return;
+       chipco_powercontrol_init(cc);
+       ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST);
+}
+
+/* Get the processor clock */
+void ssb_chipco_get_clockcpu(struct ssb_chipcommon *cc,
+                             u32 *plltype, u32 *n, u32 *m)
+{
+       *n = chipco_read32(cc, SSB_CHIPCO_CLOCK_N);
+       *plltype = (cc->capabilities & SSB_CHIPCO_CAP_PLLT);
+       switch (*plltype) {
+       case SSB_PLLTYPE_2:
+       case SSB_PLLTYPE_4:
+       case SSB_PLLTYPE_6:
+       case SSB_PLLTYPE_7:
+               *m = chipco_read32(cc, SSB_CHIPCO_CLOCK_MIPS);
+               break;
+       case SSB_PLLTYPE_3:
+               /* 5350 uses m2 to control mips */
+               *m = chipco_read32(cc, SSB_CHIPCO_CLOCK_M2);
+               break;
+       default:
+               *m = chipco_read32(cc, SSB_CHIPCO_CLOCK_SB);
+               break;
+       }
+}
+
+/* Get the bus clock */
+void ssb_chipco_get_clockcontrol(struct ssb_chipcommon *cc,
+                                u32 *plltype, u32 *n, u32 *m)
+{
+       *n = chipco_read32(cc, SSB_CHIPCO_CLOCK_N);
+       *plltype = (cc->capabilities & SSB_CHIPCO_CAP_PLLT);
+       switch (*plltype) {
+       case SSB_PLLTYPE_6: /* 100/200 or 120/240 only */
+               *m = chipco_read32(cc, SSB_CHIPCO_CLOCK_MIPS);
+               break;
+       case SSB_PLLTYPE_3: /* 25Mhz, 2 dividers */
+               if (cc->dev->bus->chip_id != 0x5365) {
+                       *m = chipco_read32(cc, SSB_CHIPCO_CLOCK_M2);
+                       break;
+               }
+               /* Fallthough */
+       default:
+               *m = chipco_read32(cc, SSB_CHIPCO_CLOCK_SB);
+       }
+}
+
+void ssb_chipco_timing_init(struct ssb_chipcommon *cc,
+                           unsigned long ns)
+{
+       struct ssb_device *dev = cc->dev;
+       struct ssb_bus *bus = dev->bus;
+       u32 tmp;
+
+       /* set register for external IO to control LED. */
+       chipco_write32(cc, SSB_CHIPCO_PROG_CFG, 0x11);
+       tmp = DIV_ROUND_UP(10, ns) << SSB_PROG_WCNT_3_SHIFT;            /* Waitcount-3 = 10ns */
+       tmp |= DIV_ROUND_UP(40, ns) << SSB_PROG_WCNT_1_SHIFT;   /* Waitcount-1 = 40ns */
+       tmp |= DIV_ROUND_UP(240, ns);                           /* Waitcount-0 = 240ns */
+       chipco_write32(cc, SSB_CHIPCO_PROG_WAITCNT, tmp);       /* 0x01020a0c for a 100Mhz clock */
+
+       /* Set timing for the flash */
+       tmp = DIV_ROUND_UP(10, ns) << SSB_FLASH_WCNT_3_SHIFT;   /* Waitcount-3 = 10nS */
+       tmp |= DIV_ROUND_UP(10, ns) << SSB_FLASH_WCNT_1_SHIFT;  /* Waitcount-1 = 10nS */
+       tmp |= DIV_ROUND_UP(120, ns);                           /* Waitcount-0 = 120nS */
+       if ((bus->chip_id == 0x5365) ||
+           (dev->id.revision < 9))
+               chipco_write32(cc, SSB_CHIPCO_FLASH_WAITCNT, tmp);
+       if ((bus->chip_id == 0x5365) ||
+           (dev->id.revision < 9) ||
+           ((bus->chip_id == 0x5350) && (bus->chip_rev == 0)))
+               chipco_write32(cc, SSB_CHIPCO_PCMCIA_MEMWAIT, tmp);
+
+       if (bus->chip_id == 0x5350) {
+               /* Enable EXTIF */
+               tmp = DIV_ROUND_UP(10, ns) << SSB_PROG_WCNT_3_SHIFT;      /* Waitcount-3 = 10ns */
+               tmp |= DIV_ROUND_UP(20, ns) << SSB_PROG_WCNT_2_SHIFT;  /* Waitcount-2 = 20ns */
+               tmp |= DIV_ROUND_UP(100, ns) << SSB_PROG_WCNT_1_SHIFT; /* Waitcount-1 = 100ns */
+               tmp |= DIV_ROUND_UP(120, ns);                     /* Waitcount-0 = 120ns */
+               chipco_write32(cc, SSB_CHIPCO_PROG_WAITCNT, tmp); /* 0x01020a0c for a 100Mhz clock */
+       }
+}
+
+/* Set chip watchdog reset timer to fire in 'ticks' backplane cycles */
+void
+ssb_chipco_watchdog_timer_set(struct ssb_chipcommon *cc, u32 ticks)
+{
+       /* instant NMI */
+       chipco_write32(cc, SSB_CHIPCO_WATCHDOG, ticks);
+}
+
+u32 ssb_chipco_gpio_in(struct ssb_chipcommon *cc, u32 mask)
+{
+       return chipco_read32(cc, SSB_CHIPCO_GPIOIN) & mask;
+}
+
+void ssb_chipco_gpio_out(struct ssb_chipcommon *cc, u32 mask, u32 value)
+{
+       return chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUT, mask, value);
+}
+
+void ssb_chipco_gpio_outen(struct ssb_chipcommon *cc, u32 mask, u32 value)
+{
+       return chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUTEN, mask, value);
+}
+
+#ifdef CONFIG_SSB_SERIAL
+int ssb_chipco_serial_init(struct ssb_chipcommon *cc,
+                          struct ssb_serial_port *ports)
+{
+       struct ssb_bus *bus = cc->dev->bus;
+       int nr_ports = 0;
+       u32 plltype;
+       unsigned int irq;
+       u32 baud_base, div;
+       u32 i, n;
+
+       plltype = (cc->capabilities & SSB_CHIPCO_CAP_PLLT);
+       irq = ssb_mips_irq(cc->dev);
+
+       if (plltype == SSB_PLLTYPE_1) {
+               /* PLL clock */
+               baud_base = ssb_calc_clock_rate(plltype,
+                                               chipco_read32(cc, SSB_CHIPCO_CLOCK_N),
+                                               chipco_read32(cc, SSB_CHIPCO_CLOCK_M2));
+               div = 1;
+       } else {
+               if (cc->dev->id.revision >= 11) {
+                       /* Fixed ALP clock */
+                       baud_base = 20000000;
+                       div = 1;
+                       /* Set the override bit so we don't divide it */
+                       chipco_write32(cc, SSB_CHIPCO_CORECTL,
+                                      SSB_CHIPCO_CORECTL_UARTCLK0);
+               } else if (cc->dev->id.revision >= 3) {
+                       /* Internal backplane clock */
+                       baud_base = ssb_clockspeed(bus);
+                       div = chipco_read32(cc, SSB_CHIPCO_CLKDIV)
+                             & SSB_CHIPCO_CLKDIV_UART;
+               } else {
+                       /* Fixed internal backplane clock */
+                       baud_base = 88000000;
+                       div = 48;
+               }
+
+               /* Clock source depends on strapping if UartClkOverride is unset */
+               if ((cc->dev->id.revision > 0) &&
+                   !(chipco_read32(cc, SSB_CHIPCO_CORECTL) & SSB_CHIPCO_CORECTL_UARTCLK0)) {
+                       if ((cc->capabilities & SSB_CHIPCO_CAP_UARTCLK) ==
+                           SSB_CHIPCO_CAP_UARTCLK_INT) {
+                               /* Internal divided backplane clock */
+                               baud_base /= div;
+                       } else {
+                               /* Assume external clock of 1.8432 MHz */
+                               baud_base = 1843200;
+                       }
+               }
+       }
+
+       /* Determine the registers of the UARTs */
+       n = (cc->capabilities & SSB_CHIPCO_CAP_NRUART);
+       for (i = 0; i < n; i++) {
+               void __iomem *cc_mmio;
+               void __iomem *uart_regs;
+
+               cc_mmio = cc->dev->bus->mmio + (cc->dev->core_index * SSB_CORE_SIZE);
+               uart_regs = cc_mmio + SSB_CHIPCO_UART0_DATA;
+               /* Offset changed at after rev 0 */
+               if (cc->dev->id.revision == 0)
+                       uart_regs += (i * 8);
+               else
+                       uart_regs += (i * 256);
+
+               nr_ports++;
+               ports[i].regs = uart_regs;
+               ports[i].irq = irq;
+               ports[i].baud_base = baud_base;
+               ports[i].reg_shift = 0;
+       }
+
+       return nr_ports;
+}
+#endif /* CONFIG_SSB_SERIAL */
diff --git a/drivers/ssb/driver_extif.c b/drivers/ssb/driver_extif.c
new file mode 100644 (file)
index 0000000..fe55eb8
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Sonics Silicon Backplane
+ * Broadcom EXTIF core driver
+ *
+ * Copyright 2005, Broadcom Corporation
+ * Copyright 2006, 2007, Michael Buesch <mb@bu3sch.de>
+ * Copyright 2006, 2007, Felix Fietkau <nbd@openwrt.org>
+ * Copyright 2007, Aurelien Jarno <aurelien@aurel32.net>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+
+#include "ssb_private.h"
+
+
+static inline u32 extif_read32(struct ssb_extif *extif, u16 offset)
+{
+       return ssb_read32(extif->dev, offset);
+}
+
+static inline void extif_write32(struct ssb_extif *extif, u16 offset, u32 value)
+{
+       ssb_write32(extif->dev, offset, value);
+}
+
+static inline void extif_write32_masked(struct ssb_extif *extif, u16 offset,
+                                       u32 mask, u32 value)
+{
+       value &= mask;
+       value |= extif_read32(extif, offset) & ~mask;
+       extif_write32(extif, offset, value);
+}
+
+#ifdef CONFIG_SSB_SERIAL
+static bool serial_exists(u8 *regs)
+{
+       u8 save_mcr, msr = 0;
+
+       if (regs) {
+               save_mcr = regs[UART_MCR];
+               regs[UART_MCR] = (UART_MCR_LOOP | UART_MCR_OUT2 | UART_MCR_RTS);
+               msr = regs[UART_MSR] & (UART_MSR_DCD | UART_MSR_RI
+                                       | UART_MSR_CTS | UART_MSR_DSR);
+               regs[UART_MCR] = save_mcr;
+       }
+       return (msr == (UART_MSR_DCD | UART_MSR_CTS));
+}
+
+int ssb_extif_serial_init(struct ssb_extif *extif, struct ssb_serial_port *ports)
+{
+       u32 i, nr_ports = 0;
+
+       /* Disable GPIO interrupt initially */
+       extif_write32(extif, SSB_EXTIF_GPIO_INTPOL, 0);
+       extif_write32(extif, SSB_EXTIF_GPIO_INTMASK, 0);
+
+       for (i = 0; i < 2; i++) {
+               void __iomem *uart_regs;
+
+               uart_regs = ioremap_nocache(SSB_EUART, 16);
+               if (uart_regs) {
+                       uart_regs += (i * 8);
+
+                       if (serial_exists(uart_regs) && ports) {
+                               extif_write32(extif, SSB_EXTIF_GPIO_INTMASK, 2);
+
+                               nr_ports++;
+                               ports[i].regs = uart_regs;
+                               ports[i].irq = 2;
+                               ports[i].baud_base = 13500000;
+                               ports[i].reg_shift = 0;
+                       }
+                       iounmap(uart_regs);
+               }
+       }
+       return nr_ports;
+}
+#endif /* CONFIG_SSB_SERIAL */
+
+void ssb_extif_timing_init(struct ssb_extif *extif, unsigned long ns)
+{
+       u32 tmp;
+
+       /* Initialize extif so we can get to the LEDs and external UART */
+       extif_write32(extif, SSB_EXTIF_PROG_CFG, SSB_EXTCFG_EN);
+
+       /* Set timing for the flash */
+       tmp  = DIV_ROUND_UP(10, ns) << SSB_PROG_WCNT_3_SHIFT;
+       tmp |= DIV_ROUND_UP(40, ns) << SSB_PROG_WCNT_1_SHIFT;
+       tmp |= DIV_ROUND_UP(120, ns);
+       extif_write32(extif, SSB_EXTIF_PROG_WAITCNT, tmp);
+
+       /* Set programmable interface timing for external uart */
+       tmp  = DIV_ROUND_UP(10, ns) << SSB_PROG_WCNT_3_SHIFT;
+       tmp |= DIV_ROUND_UP(20, ns) << SSB_PROG_WCNT_2_SHIFT;
+       tmp |= DIV_ROUND_UP(100, ns) << SSB_PROG_WCNT_1_SHIFT;
+       tmp |= DIV_ROUND_UP(120, ns);
+       extif_write32(extif, SSB_EXTIF_PROG_WAITCNT, tmp);
+}
+
+void ssb_extif_get_clockcontrol(struct ssb_extif *extif,
+                               u32 *pll_type, u32 *n, u32 *m)
+{
+       *pll_type = SSB_PLLTYPE_1;
+       *n = extif_read32(extif, SSB_EXTIF_CLOCK_N);
+       *m = extif_read32(extif, SSB_EXTIF_CLOCK_SB);
+}
+
+u32 ssb_extif_gpio_in(struct ssb_extif *extif, u32 mask)
+{
+       return extif_read32(extif, SSB_EXTIF_GPIO_IN) & mask;
+}
+
+void ssb_extif_gpio_out(struct ssb_extif *extif, u32 mask, u32 value)
+{
+       return extif_write32_masked(extif, SSB_EXTIF_GPIO_OUT(0),
+                                  mask, value);
+}
+
+void ssb_extif_gpio_outen(struct ssb_extif *extif, u32 mask, u32 value)
+{
+       return extif_write32_masked(extif, SSB_EXTIF_GPIO_OUTEN(0),
+                                  mask, value);
+}
+
diff --git a/drivers/ssb/driver_mipscore.c b/drivers/ssb/driver_mipscore.c
new file mode 100644 (file)
index 0000000..ab8691a
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Sonics Silicon Backplane
+ * Broadcom MIPS core driver
+ *
+ * Copyright 2005, Broadcom Corporation
+ * Copyright 2006, 2007, Michael Buesch <mb@bu3sch.de>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/ssb/ssb.h>
+
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/time.h>
+
+#include "ssb_private.h"
+
+
+static inline u32 mips_read32(struct ssb_mipscore *mcore,
+                             u16 offset)
+{
+       return ssb_read32(mcore->dev, offset);
+}
+
+static inline void mips_write32(struct ssb_mipscore *mcore,
+                               u16 offset,
+                               u32 value)
+{
+       ssb_write32(mcore->dev, offset, value);
+}
+
+static const u32 ipsflag_irq_mask[] = {
+       0,
+       SSB_IPSFLAG_IRQ1,
+       SSB_IPSFLAG_IRQ2,
+       SSB_IPSFLAG_IRQ3,
+       SSB_IPSFLAG_IRQ4,
+};
+
+static const u32 ipsflag_irq_shift[] = {
+       0,
+       SSB_IPSFLAG_IRQ1_SHIFT,
+       SSB_IPSFLAG_IRQ2_SHIFT,
+       SSB_IPSFLAG_IRQ3_SHIFT,
+       SSB_IPSFLAG_IRQ4_SHIFT,
+};
+
+static inline u32 ssb_irqflag(struct ssb_device *dev)
+{
+       return ssb_read32(dev, SSB_TPSFLAG) & SSB_TPSFLAG_BPFLAG;
+}
+
+/* Get the MIPS IRQ assignment for a specified device.
+ * If unassigned, 0 is returned.
+ */
+unsigned int ssb_mips_irq(struct ssb_device *dev)
+{
+       struct ssb_bus *bus = dev->bus;
+       u32 irqflag;
+       u32 ipsflag;
+       u32 tmp;
+       unsigned int irq;
+
+       irqflag = ssb_irqflag(dev);
+       ipsflag = ssb_read32(bus->mipscore.dev, SSB_IPSFLAG);
+       for (irq = 1; irq <= 4; irq++) {
+               tmp = ((ipsflag & ipsflag_irq_mask[irq]) >> ipsflag_irq_shift[irq]);
+               if (tmp == irqflag)
+                       break;
+       }
+       if (irq == 5)
+               irq = 0;
+
+       return irq;
+}
+
+static void clear_irq(struct ssb_bus *bus, unsigned int irq)
+{
+       struct ssb_device *dev = bus->mipscore.dev;
+
+       /* Clear the IRQ in the MIPScore backplane registers */
+       if (irq == 0) {
+               ssb_write32(dev, SSB_INTVEC, 0);
+       } else {
+               ssb_write32(dev, SSB_IPSFLAG,
+                           ssb_read32(dev, SSB_IPSFLAG) |
+                           ipsflag_irq_mask[irq]);
+       }
+}
+
+static void set_irq(struct ssb_device *dev, unsigned int irq)
+{
+       unsigned int oldirq = ssb_mips_irq(dev);
+       struct ssb_bus *bus = dev->bus;
+       struct ssb_device *mdev = bus->mipscore.dev;
+       u32 irqflag = ssb_irqflag(dev);
+
+       dev->irq = irq + 2;
+
+       ssb_dprintk(KERN_INFO PFX
+                   "set_irq: core 0x%04x, irq %d => %d\n",
+                   dev->id.coreid, oldirq, irq);
+       /* clear the old irq */
+       if (oldirq == 0)
+               ssb_write32(mdev, SSB_INTVEC, (~(1 << irqflag) & ssb_read32(mdev, SSB_INTVEC)));
+       else
+               clear_irq(bus, oldirq);
+
+       /* assign the new one */
+       if (irq == 0)
+               ssb_write32(mdev, SSB_INTVEC, ((1 << irqflag) & ssb_read32(mdev, SSB_INTVEC)));
+
+       irqflag <<= ipsflag_irq_shift[irq];
+       irqflag |= (ssb_read32(mdev, SSB_IPSFLAG) & ~ipsflag_irq_mask[irq]);
+       ssb_write32(mdev, SSB_IPSFLAG, irqflag);
+}
+
+static void ssb_mips_serial_init(struct ssb_mipscore *mcore)
+{
+       struct ssb_bus *bus = mcore->dev->bus;
+
+       if (bus->extif.dev)
+               mcore->nr_serial_ports = ssb_extif_serial_init(&bus->extif, mcore->serial_ports);
+       else if (bus->chipco.dev)
+               mcore->nr_serial_ports = ssb_chipco_serial_init(&bus->chipco, mcore->serial_ports);
+       else
+               mcore->nr_serial_ports = 0;
+}
+
+static void ssb_mips_flash_detect(struct ssb_mipscore *mcore)
+{
+       struct ssb_bus *bus = mcore->dev->bus;
+
+       mcore->flash_buswidth = 2;
+       if (bus->chipco.dev) {
+               mcore->flash_window = 0x1c000000;
+               mcore->flash_window_size = 0x02000000;
+               if ((ssb_read32(bus->chipco.dev, SSB_CHIPCO_FLASH_CFG)
+                              & SSB_CHIPCO_CFG_DS16) == 0)
+                       mcore->flash_buswidth = 1;
+       } else {
+               mcore->flash_window = 0x1fc00000;
+               mcore->flash_window_size = 0x00400000;
+       }
+}
+
+u32 ssb_cpu_clock(struct ssb_mipscore *mcore)
+{
+       struct ssb_bus *bus = mcore->dev->bus;
+       u32 pll_type, n, m, rate = 0;
+
+       if (bus->extif.dev) {
+               ssb_extif_get_clockcontrol(&bus->extif, &pll_type, &n, &m);
+       } else if (bus->chipco.dev) {
+               ssb_chipco_get_clockcpu(&bus->chipco, &pll_type, &n, &m);
+       } else
+               return 0;
+
+       if ((pll_type == SSB_PLLTYPE_5) || (bus->chip_id == 0x5365)) {
+               rate = 200000000;
+       } else {
+               rate = ssb_calc_clock_rate(pll_type, n, m);
+       }
+
+       if (pll_type == SSB_PLLTYPE_6) {
+               rate *= 2;
+       }
+
+       return rate;
+}
+
+void ssb_mipscore_init(struct ssb_mipscore *mcore)
+{
+       struct ssb_bus *bus = mcore->dev->bus;
+       struct ssb_device *dev;
+       unsigned long hz, ns;
+       unsigned int irq, i;
+
+       if (!mcore->dev)
+               return; /* We don't have a MIPS core */
+
+       ssb_dprintk(KERN_INFO PFX "Initializing MIPS core...\n");
+
+       hz = ssb_clockspeed(bus);
+       if (!hz)
+               hz = 100000000;
+       ns = 1000000000 / hz;
+
+       if (bus->extif.dev)
+               ssb_extif_timing_init(&bus->extif, ns);
+       else if (bus->chipco.dev)
+               ssb_chipco_timing_init(&bus->chipco, ns);
+
+       /* Assign IRQs to all cores on the bus, start with irq line 2, because serial usually takes 1 */
+       for (irq = 2, i = 0; i < bus->nr_devices; i++) {
+               dev = &(bus->devices[i]);
+               dev->irq = ssb_mips_irq(dev) + 2;
+               switch (dev->id.coreid) {
+               case SSB_DEV_USB11_HOST:
+                       /* shouldn't need a separate irq line for non-4710, most of them have a proper
+                        * external usb controller on the pci */
+                       if ((bus->chip_id == 0x4710) && (irq <= 4)) {
+                               set_irq(dev, irq++);
+                               break;
+                       }
+                       /* fallthrough */
+               case SSB_DEV_PCI:
+               case SSB_DEV_ETHERNET:
+               case SSB_DEV_80211:
+               case SSB_DEV_USB20_HOST:
+                       /* These devices get their own IRQ line if available, the rest goes on IRQ0 */
+                       if (irq <= 4) {
+                               set_irq(dev, irq++);
+                               break;
+                       }
+               }
+       }
+
+       ssb_mips_serial_init(mcore);
+       ssb_mips_flash_detect(mcore);
+}
diff --git a/drivers/ssb/driver_pcicore.c b/drivers/ssb/driver_pcicore.c
new file mode 100644 (file)
index 0000000..2faaa90
--- /dev/null
@@ -0,0 +1,576 @@
+/*
+ * Sonics Silicon Backplane
+ * Broadcom PCI-core driver
+ *
+ * Copyright 2005, Broadcom Corporation
+ * Copyright 2006, 2007, Michael Buesch <mb@bu3sch.de>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/ssb/ssb.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include "ssb_private.h"
+
+
+static inline
+u32 pcicore_read32(struct ssb_pcicore *pc, u16 offset)
+{
+       return ssb_read32(pc->dev, offset);
+}
+
+static inline
+void pcicore_write32(struct ssb_pcicore *pc, u16 offset, u32 value)
+{
+       ssb_write32(pc->dev, offset, value);
+}
+
+/**************************************************
+ * 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)))
+
+/* Assume one-hot slot wiring */
+#define SSB_PCI_SLOT_MAX       16
+
+/* 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. */
+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;
+               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)
+{
+       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);
+}
+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,
+                            unsigned int func, unsigned int off)
+{
+       u32 addr = 0;
+       u32 tmp;
+
+       if (unlikely(pc->cardbusmode && dev > 1))
+               goto out;
+       if (bus == 0) {
+               /* Type 0 transaction */
+               if (unlikely(dev >= SSB_PCI_SLOT_MAX))
+                       goto out;
+               /* Slide the window */
+               tmp = SSB_PCICORE_SBTOPCI_CFG0;
+               tmp |= ((1 << (dev + 16)) & SSB_PCICORE_SBTOPCI1_MASK);
+               pcicore_write32(pc, SSB_PCICORE_SBTOPCI1, tmp);
+               /* Calculate the address */
+               addr = SSB_PCI_CFG;
+               addr |= ((1 << (dev + 16)) & ~SSB_PCICORE_SBTOPCI1_MASK);
+               addr |= (func << 8);
+               addr |= (off & ~3);
+       } else {
+               /* Type 1 transaction */
+               pcicore_write32(pc, SSB_PCICORE_SBTOPCI1,
+                               SSB_PCICORE_SBTOPCI_CFG1);
+               /* Calculate the address */
+               addr = SSB_PCI_CFG;
+               addr |= (bus << 16);
+               addr |= (dev << 11);
+               addr |= (func << 8);
+               addr |= (off & ~3);
+       }
+out:
+       return addr;
+}
+
+static int ssb_extpci_read_config(struct ssb_pcicore *pc,
+                                 unsigned int bus, unsigned int dev,
+                                 unsigned int func, unsigned int off,
+                                 void *buf, int len)
+{
+       int err = -EINVAL;
+       u32 addr, val;
+       void __iomem *mmio;
+
+       SSB_WARN_ON(!pc->hostmode);
+       if (unlikely(len != 1 && len != 2 && len != 4))
+               goto out;
+       addr = get_cfgspace_addr(pc, bus, dev, func, off);
+       if (unlikely(!addr))
+               goto out;
+       err = -ENOMEM;
+       mmio = ioremap_nocache(addr, len);
+       if (!mmio)
+               goto out;
+
+       if (mips_busprobe32(val, mmio)) {
+               val = 0xffffffff;
+               goto unmap;
+       }
+
+       val = readl(mmio);
+       val >>= (8 * (off & 3));
+
+       switch (len) {
+       case 1:
+               *((u8 *)buf) = (u8)val;
+               break;
+       case 2:
+               *((u16 *)buf) = (u16)val;
+               break;
+       case 4:
+               *((u32 *)buf) = (u32)val;
+               break;
+       }
+       err = 0;
+unmap:
+       iounmap(mmio);
+out:
+       return err;
+}
+
+static int ssb_extpci_write_config(struct ssb_pcicore *pc,
+                                  unsigned int bus, unsigned int dev,
+                                  unsigned int func, unsigned int off,
+                                  const void *buf, int len)
+{
+       int err = -EINVAL;
+       u32 addr, val = 0;
+       void __iomem *mmio;
+
+       SSB_WARN_ON(!pc->hostmode);
+       if (unlikely(len != 1 && len != 2 && len != 4))
+               goto out;
+       addr = get_cfgspace_addr(pc, bus, dev, func, off);
+       if (unlikely(!addr))
+               goto out;
+       err = -ENOMEM;
+       mmio = ioremap_nocache(addr, len);
+       if (!mmio)
+               goto out;
+
+       if (mips_busprobe32(val, mmio)) {
+               val = 0xffffffff;
+               goto unmap;
+       }
+
+       switch (len) {
+       case 1:
+               val = readl(mmio);
+               val &= ~(0xFF << (8 * (off & 3)));
+               val |= *((const u8 *)buf) << (8 * (off & 3));
+               break;
+       case 2:
+               val = readl(mmio);
+               val &= ~(0xFFFF << (8 * (off & 3)));
+               val |= *((const u16 *)buf) << (8 * (off & 3));
+               break;
+       case 4:
+               val = *((const u32 *)buf);
+               break;
+       }
+       writel(val, mmio);
+
+       err = 0;
+unmap:
+       iounmap(mmio);
+out:
+       return err;
+}
+
+static int ssb_pcicore_read_config(struct pci_bus *bus, unsigned int devfn,
+                                  int reg, int size, u32 *val)
+{
+       unsigned long flags;
+       int err;
+
+       spin_lock_irqsave(&cfgspace_lock, flags);
+       err = ssb_extpci_read_config(extpci_core, bus->number, PCI_SLOT(devfn),
+                                    PCI_FUNC(devfn), reg, val, size);
+       spin_unlock_irqrestore(&cfgspace_lock, flags);
+
+       return err ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL;
+}
+
+static int ssb_pcicore_write_config(struct pci_bus *bus, unsigned int devfn,
+                                   int reg, int size, u32 val)
+{
+       unsigned long flags;
+       int err;
+
+       spin_lock_irqsave(&cfgspace_lock, flags);
+       err = ssb_extpci_write_config(extpci_core, bus->number, PCI_SLOT(devfn),
+                                     PCI_FUNC(devfn), reg, &val, size);
+       spin_unlock_irqrestore(&cfgspace_lock, flags);
+
+       return err ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops ssb_pcicore_pciops = {
+       .read   = ssb_pcicore_read_config,
+       .write  = ssb_pcicore_write_config,
+};
+
+static struct resource ssb_pcicore_mem_resource = {
+       .name   = "SSB PCIcore external memory",
+       .start  = SSB_PCI_DMA,
+       .end    = SSB_PCI_DMA + SSB_PCI_DMA_SZ - 1,
+       .flags  = IORESOURCE_MEM,
+};
+
+static struct resource ssb_pcicore_io_resource = {
+       .name   = "SSB PCIcore external I/O",
+       .start  = 0x100,
+       .end    = 0x7FF,
+       .flags  = IORESOURCE_IO,
+};
+
+static struct pci_controller ssb_pcicore_controller = {
+       .pci_ops        = &ssb_pcicore_pciops,
+       .io_resource    = &ssb_pcicore_io_resource,
+       .mem_resource   = &ssb_pcicore_mem_resource,
+       .mem_offset     = 0x24000000,
+};
+
+static void ssb_pcicore_init_hostmode(struct ssb_pcicore *pc)
+{
+       u32 val;
+
+       if (WARN_ON(extpci_core))
+               return;
+       extpci_core = pc;
+
+       ssb_dprintk(KERN_INFO PFX "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 */
+
+       /*TODO cardbus mode */
+
+       /* 64MB I/O window */
+       pcicore_write32(pc, SSB_PCICORE_SBTOPCI0,
+                       SSB_PCICORE_SBTOPCI_IO);
+       /* 64MB config space */
+       pcicore_write32(pc, SSB_PCICORE_SBTOPCI1,
+                       SSB_PCICORE_SBTOPCI_CFG0);
+       /* 1GB memory window */
+       pcicore_write32(pc, SSB_PCICORE_SBTOPCI2,
+                       SSB_PCICORE_SBTOPCI_MEM | SSB_PCI_DMA);
+
+       /* Enable PCI bridge BAR0 prefetch and burst */
+       val = PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
+       ssb_extpci_write_config(pc, 0, 0, 0, PCI_COMMAND, &val, 2);
+       /* Clear error conditions */
+       val = 0;
+       ssb_extpci_write_config(pc, 0, 0, 0, PCI_STATUS, &val, 2);
+
+       /* Enable PCI interrupts */
+       pcicore_write32(pc, SSB_PCICORE_IMASK,
+                       SSB_PCICORE_IMASK_INTA);
+
+       /* Ok, ready to run, register it to the system.
+        * The following needs change, if we want to port hostmode
+        * to non-MIPS platform. */
+       set_io_port_base((unsigned long)ioremap_nocache(SSB_PCI_MEM, 0x04000000));
+       /* 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);
+}
+
+static int pcicore_is_in_hostmode(struct ssb_pcicore *pc)
+{
+       struct ssb_bus *bus = pc->dev->bus;
+       u16 chipid_top;
+       u32 tmp;
+
+       chipid_top = (bus->chip_id & 0xFF00);
+       if (chipid_top != 0x4700 &&
+           chipid_top != 0x5300)
+               return 0;
+
+       if (bus->sprom.r1.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;
+
+       return !mips_busprobe32(tmp, (bus->mmio + (pc->dev->core_index * SSB_CORE_SIZE)));
+}
+#endif /* CONFIG_SSB_PCICORE_HOSTMODE */
+
+
+/**************************************************
+ * Generic and Clientmode operation code.
+ **************************************************/
+
+static void ssb_pcicore_init_clientmode(struct ssb_pcicore *pc)
+{
+       /* Disable PCI interrupts. */
+       ssb_write32(pc->dev, SSB_INTVEC, 0);
+}
+
+void ssb_pcicore_init(struct ssb_pcicore *pc)
+{
+       struct ssb_device *dev = pc->dev;
+       struct ssb_bus *bus;
+
+       if (!dev)
+               return;
+       bus = dev->bus;
+       if (!ssb_device_is_enabled(dev))
+               ssb_device_enable(dev, 0);
+
+#ifdef CONFIG_SSB_PCICORE_HOSTMODE
+       pc->hostmode = pcicore_is_in_hostmode(pc);
+       if (pc->hostmode)
+               ssb_pcicore_init_hostmode(pc);
+#endif /* CONFIG_SSB_PCICORE_HOSTMODE */
+       if (!pc->hostmode)
+               ssb_pcicore_init_clientmode(pc);
+}
+
+static u32 ssb_pcie_read(struct ssb_pcicore *pc, u32 address)
+{
+       pcicore_write32(pc, 0x130, address);
+       return pcicore_read32(pc, 0x134);
+}
+
+static void ssb_pcie_write(struct ssb_pcicore *pc, u32 address, u32 data)
+{
+       pcicore_write32(pc, 0x130, address);
+       pcicore_write32(pc, 0x134, data);
+}
+
+static void ssb_pcie_mdio_write(struct ssb_pcicore *pc, u8 device,
+                               u8 address, u16 data)
+{
+       const u16 mdio_control = 0x128;
+       const u16 mdio_data = 0x12C;
+       u32 v;
+       int i;
+
+       v = 0x80; /* Enable Preamble Sequence */
+       v |= 0x2; /* MDIO Clock Divisor */
+       pcicore_write32(pc, mdio_control, v);
+
+       v = (1 << 30); /* Start of Transaction */
+       v |= (1 << 28); /* Write Transaction */
+       v |= (1 << 17); /* Turnaround */
+       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 < 10; i++) {
+               v = pcicore_read32(pc, mdio_control);
+               if (v & 0x100 /* Trans complete */)
+                       break;
+               msleep(1);
+       }
+       pcicore_write32(pc, mdio_control, 0);
+}
+
+static void ssb_broadcast_value(struct ssb_device *dev,
+                               u32 address, u32 data)
+{
+       /* This is used for both, PCI and ChipCommon core, so be careful. */
+       BUILD_BUG_ON(SSB_PCICORE_BCAST_ADDR != SSB_CHIPCO_BCAST_ADDR);
+       BUILD_BUG_ON(SSB_PCICORE_BCAST_DATA != SSB_CHIPCO_BCAST_DATA);
+
+       ssb_write32(dev, SSB_PCICORE_BCAST_ADDR, address);
+       ssb_read32(dev, SSB_PCICORE_BCAST_ADDR); /* flush */
+       ssb_write32(dev, SSB_PCICORE_BCAST_DATA, data);
+       ssb_read32(dev, SSB_PCICORE_BCAST_DATA); /* flush */
+}
+
+static void ssb_commit_settings(struct ssb_bus *bus)
+{
+       struct ssb_device *dev;
+
+       dev = bus->chipco.dev ? bus->chipco.dev : bus->pcicore.dev;
+       if (WARN_ON(!dev))
+               return;
+       /* This forces an update of the cached registers. */
+       ssb_broadcast_value(dev, 0xFD8, 0);
+}
+
+int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc,
+                                  struct ssb_device *dev)
+{
+       struct ssb_device *pdev = pc->dev;
+       struct ssb_bus *bus;
+       int err = 0;
+       u32 tmp;
+
+       might_sleep();
+
+       if (!pdev)
+               goto out;
+       bus = pdev->bus;
+
+       /* Enable interrupts for this device. */
+       if (bus->host_pci &&
+           ((pdev->id.revision >= 6) || (pdev->id.coreid == SSB_DEV_PCIE))) {
+               u32 coremask;
+
+               /* Calculate the "coremask" for the device. */
+               coremask = (1 << dev->core_index);
+
+               err = pci_read_config_dword(bus->host_pci, SSB_PCI_IRQMASK, &tmp);
+               if (err)
+                       goto out;
+               tmp |= coremask << 8;
+               err = pci_write_config_dword(bus->host_pci, SSB_PCI_IRQMASK, tmp);
+               if (err)
+                       goto out;
+       } else {
+               u32 intvec;
+
+               intvec = ssb_read32(pdev, SSB_INTVEC);
+               if ((bus->chip_id & 0xFF00) == 0x4400) {
+                       /* Workaround: On the BCM44XX the BPFLAG routing
+                        * bit is wrong. Use a hardcoded constant. */
+                       intvec |= 0x00000002;
+               } else {
+                       tmp = ssb_read32(dev, SSB_TPSFLAG);
+                       tmp &= SSB_TPSFLAG_BPFLAG;
+                       intvec |= tmp;
+               }
+               ssb_write32(pdev, SSB_INTVEC, intvec);
+       }
+
+       /* Setup PCIcore operation. */
+       if (pc->setup_done)
+               goto out;
+       if (pdev->id.coreid == SSB_DEV_PCI) {
+               tmp = pcicore_read32(pc, SSB_PCICORE_SBTOPCI2);
+               tmp |= SSB_PCICORE_SBTOPCI_PREF;
+               tmp |= SSB_PCICORE_SBTOPCI_BURST;
+               pcicore_write32(pc, SSB_PCICORE_SBTOPCI2, tmp);
+
+               if (pdev->id.revision < 5) {
+                       tmp = ssb_read32(pdev, SSB_IMCFGLO);
+                       tmp &= ~SSB_IMCFGLO_SERTO;
+                       tmp |= 2;
+                       tmp &= ~SSB_IMCFGLO_REQTO;
+                       tmp |= 3 << SSB_IMCFGLO_REQTO_SHIFT;
+                       ssb_write32(pdev, SSB_IMCFGLO, tmp);
+                       ssb_commit_settings(bus);
+               } else if (pdev->id.revision >= 11) {
+                       tmp = pcicore_read32(pc, SSB_PCICORE_SBTOPCI2);
+                       tmp |= SSB_PCICORE_SBTOPCI_MRM;
+                       pcicore_write32(pc, SSB_PCICORE_SBTOPCI2, tmp);
+               }
+       } else {
+               WARN_ON(pdev->id.coreid != SSB_DEV_PCIE);
+               //TODO: Better make defines for all these magic PCIE values.
+               if ((pdev->id.revision == 0) || (pdev->id.revision == 1)) {
+                       /* TLP Workaround register. */
+                       tmp = ssb_pcie_read(pc, 0x4);
+                       tmp |= 0x8;
+                       ssb_pcie_write(pc, 0x4, tmp);
+               }
+               if (pdev->id.revision == 0) {
+                       const u8 serdes_rx_device = 0x1F;
+
+                       ssb_pcie_mdio_write(pc, serdes_rx_device,
+                                           2 /* Timer */, 0x8128);
+                       ssb_pcie_mdio_write(pc, serdes_rx_device,
+                                           6 /* CDR */, 0x0100);
+                       ssb_pcie_mdio_write(pc, serdes_rx_device,
+                                           7 /* CDR BW */, 0x1466);
+               } else if (pdev->id.revision == 1) {
+                       /* DLLP Link Control register. */
+                       tmp = ssb_pcie_read(pc, 0x100);
+                       tmp |= 0x40;
+                       ssb_pcie_write(pc, 0x100, tmp);
+               }
+       }
+       pc->setup_done = 1;
+out:
+       return err;
+}
+EXPORT_SYMBOL(ssb_pcicore_dev_irqvecs_enable);
diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c
new file mode 100644 (file)
index 0000000..74d5182
--- /dev/null
@@ -0,0 +1,1162 @@
+/*
+ * Sonics Silicon Backplane
+ * Subsystem core
+ *
+ * Copyright 2005, Broadcom Corporation
+ * Copyright 2006, 2007, Michael Buesch <mb@bu3sch.de>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include "ssb_private.h"
+
+#include <linux/delay.h>
+#include <linux/ssb/ssb.h>
+#include <linux/ssb/ssb_regs.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+
+MODULE_DESCRIPTION("Sonics Silicon Backplane driver");
+MODULE_LICENSE("GPL");
+
+
+/* Temporary list of yet-to-be-attached buses */
+static LIST_HEAD(attach_queue);
+/* List if running buses */
+static LIST_HEAD(buses);
+/* Software ID counter */
+static unsigned int next_busnumber;
+/* buses_mutes locks the two buslists and the next_busnumber.
+ * Don't lock this directly, but use ssb_buses_[un]lock() below. */
+static DEFINE_MUTEX(buses_mutex);
+
+/* There are differences in the codeflow, if the bus is
+ * initialized from early boot, as various needed services
+ * are not available early. This is a mechanism to delay
+ * these initializations to after early boot has finished.
+ * It's also used to avoid mutex locking, as that's not
+ * available and needed early. */
+static bool ssb_is_early_boot = 1;
+
+static void ssb_buses_lock(void);
+static void ssb_buses_unlock(void);
+
+
+#ifdef CONFIG_SSB_PCIHOST
+struct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev)
+{
+       struct ssb_bus *bus;
+
+       ssb_buses_lock();
+       list_for_each_entry(bus, &buses, list) {
+               if (bus->bustype == SSB_BUSTYPE_PCI &&
+                   bus->host_pci == pdev)
+                       goto found;
+       }
+       bus = NULL;
+found:
+       ssb_buses_unlock();
+
+       return bus;
+}
+#endif /* CONFIG_SSB_PCIHOST */
+
+static struct ssb_device *ssb_device_get(struct ssb_device *dev)
+{
+       if (dev)
+               get_device(dev->dev);
+       return dev;
+}
+
+static void ssb_device_put(struct ssb_device *dev)
+{
+       if (dev)
+               put_device(dev->dev);
+}
+
+static int ssb_bus_resume(struct ssb_bus *bus)
+{
+       int err;
+
+       ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 1);
+       err = ssb_pcmcia_init(bus);
+       if (err) {
+               /* No need to disable XTAL, as we don't have one on PCMCIA. */
+               return err;
+       }
+       ssb_chipco_resume(&bus->chipco);
+
+       return 0;
+}
+
+static int ssb_device_resume(struct device *dev)
+{
+       struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);
+       struct ssb_driver *ssb_drv;
+       struct ssb_bus *bus;
+       int err = 0;
+
+       bus = ssb_dev->bus;
+       if (bus->suspend_cnt == bus->nr_devices) {
+               err = ssb_bus_resume(bus);
+               if (err)
+                       return err;
+       }
+       bus->suspend_cnt--;
+       if (dev->driver) {
+               ssb_drv = drv_to_ssb_drv(dev->driver);
+               if (ssb_drv && ssb_drv->resume)
+                       err = ssb_drv->resume(ssb_dev);
+               if (err)
+                       goto out;
+       }
+out:
+       return err;
+}
+
+static void ssb_bus_suspend(struct ssb_bus *bus, pm_message_t state)
+{
+       ssb_chipco_suspend(&bus->chipco, state);
+       ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0);
+
+       /* Reset HW state information in memory, so that HW is
+        * completely reinitialized on resume. */
+       bus->mapped_device = NULL;
+#ifdef CONFIG_SSB_DRIVER_PCICORE
+       bus->pcicore.setup_done = 0;
+#endif
+#ifdef CONFIG_SSB_DEBUG
+       bus->powered_up = 0;
+#endif
+}
+
+static int ssb_device_suspend(struct device *dev, pm_message_t state)
+{
+       struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);
+       struct ssb_driver *ssb_drv;
+       struct ssb_bus *bus;
+       int err = 0;
+
+       if (dev->driver) {
+               ssb_drv = drv_to_ssb_drv(dev->driver);
+               if (ssb_drv && ssb_drv->suspend)
+                       err = ssb_drv->suspend(ssb_dev, state);
+               if (err)
+                       goto out;
+       }
+
+       bus = ssb_dev->bus;
+       bus->suspend_cnt++;
+       if (bus->suspend_cnt == bus->nr_devices) {
+               /* All devices suspended. Shutdown the bus. */
+               ssb_bus_suspend(bus, state);
+       }
+
+out:
+       return err;
+}
+
+#ifdef CONFIG_SSB_PCIHOST
+int ssb_devices_freeze(struct ssb_bus *bus)
+{
+       struct ssb_device *dev;
+       struct ssb_driver *drv;
+       int err = 0;
+       int i;
+       pm_message_t state = PMSG_FREEZE;
+
+       /* First check that we are capable to freeze all devices. */
+       for (i = 0; i < bus->nr_devices; i++) {
+               dev = &(bus->devices[i]);
+               if (!dev->dev ||
+                   !dev->dev->driver ||
+                   !device_is_registered(dev->dev))
+                       continue;
+               drv = drv_to_ssb_drv(dev->dev->driver);
+               if (!drv)
+                       continue;
+               if (!drv->suspend) {
+                       /* Nope, can't suspend this one. */
+                       return -EOPNOTSUPP;
+               }
+       }
+       /* Now suspend all devices */
+       for (i = 0; i < bus->nr_devices; i++) {
+               dev = &(bus->devices[i]);
+               if (!dev->dev ||
+                   !dev->dev->driver ||
+                   !device_is_registered(dev->dev))
+                       continue;
+               drv = drv_to_ssb_drv(dev->dev->driver);
+               if (!drv)
+                       continue;
+               err = drv->suspend(dev, state);
+               if (err) {
+                       ssb_printk(KERN_ERR PFX "Failed to freeze device %s\n",
+                                  dev->dev->bus_id);
+                       goto err_unwind;
+               }
+       }
+
+       return 0;
+err_unwind:
+       for (i--; i >= 0; i--) {
+               dev = &(bus->devices[i]);
+               if (!dev->dev ||
+                   !dev->dev->driver ||
+                   !device_is_registered(dev->dev))
+                       continue;
+               drv = drv_to_ssb_drv(dev->dev->driver);
+               if (!drv)
+                       continue;
+               if (drv->resume)
+                       drv->resume(dev);
+       }
+       return err;
+}
+
+int ssb_devices_thaw(struct ssb_bus *bus)
+{
+       struct ssb_device *dev;
+       struct ssb_driver *drv;
+       int err;
+       int i;
+
+       for (i = 0; i < bus->nr_devices; i++) {
+               dev = &(bus->devices[i]);
+               if (!dev->dev ||
+                   !dev->dev->driver ||
+                   !device_is_registered(dev->dev))
+                       continue;
+               drv = drv_to_ssb_drv(dev->dev->driver);
+               if (!drv)
+                       continue;
+               if (SSB_WARN_ON(!drv->resume))
+                       continue;
+               err = drv->resume(dev);
+               if (err) {
+                       ssb_printk(KERN_ERR PFX "Failed to thaw device %s\n",
+                                  dev->dev->bus_id);
+               }
+       }
+
+       return 0;
+}
+#endif /* CONFIG_SSB_PCIHOST */
+
+static void ssb_device_shutdown(struct device *dev)
+{
+       struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);
+       struct ssb_driver *ssb_drv;
+
+       if (!dev->driver)
+               return;
+       ssb_drv = drv_to_ssb_drv(dev->driver);
+       if (ssb_drv && ssb_drv->shutdown)
+               ssb_drv->shutdown(ssb_dev);
+}
+
+static int ssb_device_remove(struct device *dev)
+{
+       struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);
+       struct ssb_driver *ssb_drv = drv_to_ssb_drv(dev->driver);
+
+       if (ssb_drv && ssb_drv->remove)
+               ssb_drv->remove(ssb_dev);
+       ssb_device_put(ssb_dev);
+
+       return 0;
+}
+
+static int ssb_device_probe(struct device *dev)
+{
+       struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);
+       struct ssb_driver *ssb_drv = drv_to_ssb_drv(dev->driver);
+       int err = 0;
+
+       ssb_device_get(ssb_dev);
+       if (ssb_drv && ssb_drv->probe)
+               err = ssb_drv->probe(ssb_dev, &ssb_dev->id);
+       if (err)
+               ssb_device_put(ssb_dev);
+
+       return err;
+}
+
+static int ssb_match_devid(const struct ssb_device_id *tabid,
+                          const struct ssb_device_id *devid)
+{
+       if ((tabid->vendor != devid->vendor) &&
+           tabid->vendor != SSB_ANY_VENDOR)
+               return 0;
+       if ((tabid->coreid != devid->coreid) &&
+           tabid->coreid != SSB_ANY_ID)
+               return 0;
+       if ((tabid->revision != devid->revision) &&
+           tabid->revision != SSB_ANY_REV)
+               return 0;
+       return 1;
+}
+
+static int ssb_bus_match(struct device *dev, struct device_driver *drv)
+{
+       struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);
+       struct ssb_driver *ssb_drv = drv_to_ssb_drv(drv);
+       const struct ssb_device_id *id;
+
+       for (id = ssb_drv->id_table;
+            id->vendor || id->coreid || id->revision;
+            id++) {
+               if (ssb_match_devid(id, &ssb_dev->id))
+                       return 1; /* found */
+       }
+
+       return 0;
+}
+
+static int ssb_device_uevent(struct device *dev, char **envp, int num_envp,
+                            char *buffer, int buffer_size)
+{
+       struct ssb_device *ssb_dev = dev_to_ssb_dev(dev);
+       int ret, i = 0, length = 0;
+
+       if (!dev)
+               return -ENODEV;
+
+       ret = add_uevent_var(envp, num_envp, &i,
+                            buffer, buffer_size, &length,
+                            "MODALIAS=ssb:v%04Xid%04Xrev%02X",
+                            ssb_dev->id.vendor, ssb_dev->id.coreid,
+                            ssb_dev->id.revision);
+       envp[i] = NULL;
+
+       return ret;
+}
+
+static struct bus_type ssb_bustype = {
+       .name           = "ssb",
+       .match          = ssb_bus_match,
+       .probe          = ssb_device_probe,
+       .remove         = ssb_device_remove,
+       .shutdown       = ssb_device_shutdown,
+       .suspend        = ssb_device_suspend,
+       .resume         = ssb_device_resume,
+       .uevent         = ssb_device_uevent,
+};
+
+static void ssb_buses_lock(void)
+{
+       /* See the comment at the ssb_is_early_boot definition */
+       if (!ssb_is_early_boot)
+               mutex_lock(&buses_mutex);
+}
+
+static void ssb_buses_unlock(void)
+{
+       /* See the comment at the ssb_is_early_boot definition */
+       if (!ssb_is_early_boot)
+               mutex_unlock(&buses_mutex);
+}
+
+static void ssb_devices_unregister(struct ssb_bus *bus)
+{
+       struct ssb_device *sdev;
+       int i;
+
+       for (i = bus->nr_devices - 1; i >= 0; i--) {
+               sdev = &(bus->devices[i]);
+               if (sdev->dev)
+                       device_unregister(sdev->dev);
+       }
+}
+
+void ssb_bus_unregister(struct ssb_bus *bus)
+{
+       ssb_buses_lock();
+       ssb_devices_unregister(bus);
+       list_del(&bus->list);
+       ssb_buses_unlock();
+
+       /* ssb_pcmcia_exit(bus); */
+       ssb_pci_exit(bus);
+       ssb_iounmap(bus);
+}
+EXPORT_SYMBOL(ssb_bus_unregister);
+
+static void ssb_release_dev(struct device *dev)
+{
+       struct __ssb_dev_wrapper *devwrap;
+
+       devwrap = container_of(dev, struct __ssb_dev_wrapper, dev);
+       kfree(devwrap);
+}
+
+static int ssb_devices_register(struct ssb_bus *bus)
+{
+       struct ssb_device *sdev;
+       struct device *dev;
+       struct __ssb_dev_wrapper *devwrap;
+       int i, err = 0;
+       int dev_idx = 0;
+
+       for (i = 0; i < bus->nr_devices; i++) {
+               sdev = &(bus->devices[i]);
+
+               /* We don't register SSB-system devices to the kernel,
+                * as the drivers for them are built into SSB. */
+               switch (sdev->id.coreid) {
+               case SSB_DEV_CHIPCOMMON:
+               case SSB_DEV_PCI:
+               case SSB_DEV_PCIE:
+               case SSB_DEV_PCMCIA:
+               case SSB_DEV_MIPS:
+               case SSB_DEV_MIPS_3302:
+               case SSB_DEV_EXTIF:
+                       continue;
+               }
+
+               devwrap = kzalloc(sizeof(*devwrap), GFP_KERNEL);
+               if (!devwrap) {
+                       ssb_printk(KERN_ERR PFX
+                                  "Could not allocate device\n");
+                       err = -ENOMEM;
+                       goto error;
+               }
+               dev = &devwrap->dev;
+               devwrap->sdev = sdev;
+
+               dev->release = ssb_release_dev;
+               dev->bus = &ssb_bustype;
+               snprintf(dev->bus_id, sizeof(dev->bus_id),
+                        "ssb%u:%d", bus->busnumber, dev_idx);
+
+               switch (bus->bustype) {
+               case SSB_BUSTYPE_PCI:
+#ifdef CONFIG_SSB_PCIHOST
+                       sdev->irq = bus->host_pci->irq;
+                       dev->parent = &bus->host_pci->dev;
+#endif
+                       break;
+               case SSB_BUSTYPE_PCMCIA:
+#ifdef CONFIG_SSB_PCMCIAHOST
+                       dev->parent = &bus->host_pcmcia->dev;
+#endif
+                       break;
+               case SSB_BUSTYPE_SSB:
+                       break;
+               }
+
+               sdev->dev = dev;
+               err = device_register(dev);
+               if (err) {
+                       ssb_printk(KERN_ERR PFX
+                                  "Could not register %s\n",
+                                  dev->bus_id);
+                       /* Set dev to NULL to not unregister
+                        * dev on error unwinding. */
+                       sdev->dev = NULL;
+                       kfree(devwrap);
+                       goto error;
+               }
+               dev_idx++;
+       }
+
+       return 0;
+error:
+       /* Unwind the already registered devices. */
+       ssb_devices_unregister(bus);
+       return err;
+}
+
+/* Needs ssb_buses_lock() */
+static int ssb_attach_queued_buses(void)
+{
+       struct ssb_bus *bus, *n;
+       int err = 0;
+       int drop_them_all = 0;
+
+       list_for_each_entry_safe(bus, n, &attach_queue, list) {
+               if (drop_them_all) {
+                       list_del(&bus->list);
+                       continue;
+               }
+               /* Can't init the PCIcore in ssb_bus_register(), as that
+                * is too early in boot for embedded systems
+                * (no udelay() available). So do it here in attach stage.
+                */
+               err = ssb_bus_powerup(bus, 0);
+               if (err)
+                       goto error;
+               ssb_pcicore_init(&bus->pcicore);
+               ssb_bus_may_powerdown(bus);
+
+               err = ssb_devices_register(bus);
+error:
+               if (err) {
+                       drop_them_all = 1;
+                       list_del(&bus->list);
+                       continue;
+               }
+               list_move_tail(&bus->list, &buses);
+       }
+
+       return err;
+}
+
+static u16 ssb_ssb_read16(struct ssb_device *dev, u16 offset)
+{
+       struct ssb_bus *bus = dev->bus;
+
+       offset += dev->core_index * SSB_CORE_SIZE;
+       return readw(bus->mmio + offset);
+}
+
+static u32 ssb_ssb_read32(struct ssb_device *dev, u16 offset)
+{
+       struct ssb_bus *bus = dev->bus;
+
+       offset += dev->core_index * SSB_CORE_SIZE;
+       return readl(bus->mmio + offset);
+}
+
+static void ssb_ssb_write16(struct ssb_device *dev, u16 offset, u16 value)
+{
+       struct ssb_bus *bus = dev->bus;
+
+       offset += dev->core_index * SSB_CORE_SIZE;
+       writew(value, bus->mmio + offset);
+}
+
+static void ssb_ssb_write32(struct ssb_device *dev, u16 offset, u32 value)
+{
+       struct ssb_bus *bus = dev->bus;
+
+       offset += dev->core_index * SSB_CORE_SIZE;
+       writel(value, bus->mmio + offset);
+}
+
+/* Ops for the plain SSB bus without a host-device (no PCI or PCMCIA). */
+static const struct ssb_bus_ops ssb_ssb_ops = {
+       .read16         = ssb_ssb_read16,
+       .read32         = ssb_ssb_read32,
+       .write16        = ssb_ssb_write16,
+       .write32        = ssb_ssb_write32,
+};
+
+static int ssb_fetch_invariants(struct ssb_bus *bus,
+                               ssb_invariants_func_t get_invariants)
+{
+       struct ssb_init_invariants iv;
+       int err;
+
+       memset(&iv, 0, sizeof(iv));
+       err = get_invariants(bus, &iv);
+       if (err)
+               goto out;
+       memcpy(&bus->boardinfo, &iv.boardinfo, sizeof(iv.boardinfo));
+       memcpy(&bus->sprom, &iv.sprom, sizeof(iv.sprom));
+out:
+       return err;
+}
+
+static int ssb_bus_register(struct ssb_bus *bus,
+                           ssb_invariants_func_t get_invariants,
+                           unsigned long baseaddr)
+{
+       int err;
+
+       spin_lock_init(&bus->bar_lock);
+       INIT_LIST_HEAD(&bus->list);
+
+       /* Powerup the bus */
+       err = ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 1);
+       if (err)
+               goto out;
+       ssb_buses_lock();
+       bus->busnumber = next_busnumber;
+       /* Scan for devices (cores) */
+       err = ssb_bus_scan(bus, baseaddr);
+       if (err)
+               goto err_disable_xtal;
+
+       /* Init PCI-host device (if any) */
+       err = ssb_pci_init(bus);
+       if (err)
+               goto err_unmap;
+       /* Init PCMCIA-host device (if any) */
+       err = ssb_pcmcia_init(bus);
+       if (err)
+               goto err_pci_exit;
+
+       /* Initialize basic system devices (if available) */
+       err = ssb_bus_powerup(bus, 0);
+       if (err)
+               goto err_pcmcia_exit;
+       ssb_chipcommon_init(&bus->chipco);
+       ssb_mipscore_init(&bus->mipscore);
+       err = ssb_fetch_invariants(bus, get_invariants);
+       if (err) {
+               ssb_bus_may_powerdown(bus);
+               goto err_pcmcia_exit;
+       }
+       ssb_bus_may_powerdown(bus);
+
+       /* Queue it for attach.
+        * See the comment at the ssb_is_early_boot definition. */
+       list_add_tail(&bus->list, &attach_queue);
+       if (!ssb_is_early_boot) {
+               /* This is not early boot, so we must attach the bus now */
+               err = ssb_attach_queued_buses();
+               if (err)
+                       goto err_dequeue;
+       }
+       next_busnumber++;
+       ssb_buses_unlock();
+
+out:
+       return err;
+
+err_dequeue:
+       list_del(&bus->list);
+err_pcmcia_exit:
+/*     ssb_pcmcia_exit(bus); */
+err_pci_exit:
+       ssb_pci_exit(bus);
+err_unmap:
+       ssb_iounmap(bus);
+err_disable_xtal:
+       ssb_buses_unlock();
+       ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0);
+       return err;
+}
+
+#ifdef CONFIG_SSB_PCIHOST
+int ssb_bus_pcibus_register(struct ssb_bus *bus,
+                           struct pci_dev *host_pci)
+{
+       int err;
+
+       bus->bustype = SSB_BUSTYPE_PCI;
+       bus->host_pci = host_pci;
+       bus->ops = &ssb_pci_ops;
+
+       err = ssb_bus_register(bus, ssb_pci_get_invariants, 0);
+       if (!err) {
+               ssb_printk(KERN_INFO PFX "Sonics Silicon Backplane found on "
+                          "PCI device %s\n", host_pci->dev.bus_id);
+       }
+
+       return err;
+}
+EXPORT_SYMBOL(ssb_bus_pcibus_register);
+#endif /* CONFIG_SSB_PCIHOST */
+
+#ifdef CONFIG_SSB_PCMCIAHOST
+int ssb_bus_pcmciabus_register(struct ssb_bus *bus,
+                              struct pcmcia_device *pcmcia_dev,
+                              unsigned long baseaddr)
+{
+       int err;
+
+       bus->bustype = SSB_BUSTYPE_PCMCIA;
+       bus->host_pcmcia = pcmcia_dev;
+       bus->ops = &ssb_pcmcia_ops;
+
+       err = ssb_bus_register(bus, ssb_pcmcia_get_invariants, baseaddr);
+       if (!err) {
+               ssb_printk(KERN_INFO PFX "Sonics Silicon Backplane found on "
+                          "PCMCIA device %s\n", pcmcia_dev->devname);
+       }
+
+       return err;
+}
+EXPORT_SYMBOL(ssb_bus_pcmciabus_register);
+#endif /* CONFIG_SSB_PCMCIAHOST */
+
+int ssb_bus_ssbbus_register(struct ssb_bus *bus,
+                           unsigned long baseaddr,
+                           ssb_invariants_func_t get_invariants)
+{
+       int err;
+
+       bus->bustype = SSB_BUSTYPE_SSB;
+       bus->ops = &ssb_ssb_ops;
+
+       err = ssb_bus_register(bus, get_invariants, baseaddr);
+       if (!err) {
+               ssb_printk(KERN_INFO PFX "Sonics Silicon Backplane found at "
+                          "address 0x%08lX\n", baseaddr);
+       }
+
+       return err;
+}
+
+int __ssb_driver_register(struct ssb_driver *drv, struct module *owner)
+{
+       drv->drv.name = drv->name;
+       drv->drv.bus = &ssb_bustype;
+       drv->drv.owner = owner;
+
+       return driver_register(&drv->drv);
+}
+EXPORT_SYMBOL(__ssb_driver_register);
+
+void ssb_driver_unregister(struct ssb_driver *drv)
+{
+       driver_unregister(&drv->drv);
+}
+EXPORT_SYMBOL(ssb_driver_unregister);
+
+void ssb_set_devtypedata(struct ssb_device *dev, void *data)
+{
+       struct ssb_bus *bus = dev->bus;
+       struct ssb_device *ent;
+       int i;
+
+       for (i = 0; i < bus->nr_devices; i++) {
+               ent = &(bus->devices[i]);
+               if (ent->id.vendor != dev->id.vendor)
+                       continue;
+               if (ent->id.coreid != dev->id.coreid)
+                       continue;
+
+               ent->devtypedata = data;
+       }
+}
+EXPORT_SYMBOL(ssb_set_devtypedata);
+
+static u32 clkfactor_f6_resolve(u32 v)
+{
+       /* map the magic values */
+       switch (v) {
+       case SSB_CHIPCO_CLK_F6_2:
+               return 2;
+       case SSB_CHIPCO_CLK_F6_3:
+               return 3;
+       case SSB_CHIPCO_CLK_F6_4:
+               return 4;
+       case SSB_CHIPCO_CLK_F6_5:
+               return 5;
+       case SSB_CHIPCO_CLK_F6_6:
+               return 6;
+       case SSB_CHIPCO_CLK_F6_7:
+               return 7;
+       }
+       return 0;
+}
+
+/* Calculate the speed the backplane would run at a given set of clockcontrol values */
+u32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m)
+{
+       u32 n1, n2, clock, m1, m2, m3, mc;
+
+       n1 = (n & SSB_CHIPCO_CLK_N1);
+       n2 = ((n & SSB_CHIPCO_CLK_N2) >> SSB_CHIPCO_CLK_N2_SHIFT);
+
+       switch (plltype) {
+       case SSB_PLLTYPE_6: /* 100/200 or 120/240 only */
+               if (m & SSB_CHIPCO_CLK_T6_MMASK)
+                       return SSB_CHIPCO_CLK_T6_M0;
+               return SSB_CHIPCO_CLK_T6_M1;
+       case SSB_PLLTYPE_1: /* 48Mhz base, 3 dividers */
+       case SSB_PLLTYPE_3: /* 25Mhz, 2 dividers */
+       case SSB_PLLTYPE_4: /* 48Mhz, 4 dividers */
+       case SSB_PLLTYPE_7: /* 25Mhz, 4 dividers */
+               n1 = clkfactor_f6_resolve(n1);
+               n2 += SSB_CHIPCO_CLK_F5_BIAS;
+               break;
+       case SSB_PLLTYPE_2: /* 48Mhz, 4 dividers */
+               n1 += SSB_CHIPCO_CLK_T2_BIAS;
+               n2 += SSB_CHIPCO_CLK_T2_BIAS;
+               SSB_WARN_ON(!((n1 >= 2) && (n1 <= 7)));
+               SSB_WARN_ON(!((n2 >= 5) && (n2 <= 23)));
+               break;
+       case SSB_PLLTYPE_5: /* 25Mhz, 4 dividers */
+               return 100000000;
+       default:
+               SSB_WARN_ON(1);
+       }
+
+       switch (plltype) {
+       case SSB_PLLTYPE_3: /* 25Mhz, 2 dividers */
+       case SSB_PLLTYPE_7: /* 25Mhz, 4 dividers */
+               clock = SSB_CHIPCO_CLK_BASE2 * n1 * n2;
+               break;
+       default:
+               clock = SSB_CHIPCO_CLK_BASE1 * n1 * n2;
+       }
+       if (!clock)
+               return 0;
+
+       m1 = (m & SSB_CHIPCO_CLK_M1);
+       m2 = ((m & SSB_CHIPCO_CLK_M2) >> SSB_CHIPCO_CLK_M2_SHIFT);
+       m3 = ((m & SSB_CHIPCO_CLK_M3) >> SSB_CHIPCO_CLK_M3_SHIFT);
+       mc = ((m & SSB_CHIPCO_CLK_MC) >> SSB_CHIPCO_CLK_MC_SHIFT);
+
+       switch (plltype) {
+       case SSB_PLLTYPE_1: /* 48Mhz base, 3 dividers */
+       case SSB_PLLTYPE_3: /* 25Mhz, 2 dividers */
+       case SSB_PLLTYPE_4: /* 48Mhz, 4 dividers */
+       case SSB_PLLTYPE_7: /* 25Mhz, 4 dividers */
+               m1 = clkfactor_f6_resolve(m1);
+               if ((plltype == SSB_PLLTYPE_1) ||
+                   (plltype == SSB_PLLTYPE_3))
+                       m2 += SSB_CHIPCO_CLK_F5_BIAS;
+               else
+                       m2 = clkfactor_f6_resolve(m2);
+               m3 = clkfactor_f6_resolve(m3);
+
+               switch (mc) {
+               case SSB_CHIPCO_CLK_MC_BYPASS:
+                       return clock;
+               case SSB_CHIPCO_CLK_MC_M1:
+                       return (clock / m1);
+               case SSB_CHIPCO_CLK_MC_M1M2:
+                       return (clock / (m1 * m2));
+               case SSB_CHIPCO_CLK_MC_M1M2M3:
+                       return (clock / (m1 * m2 * m3));
+               case SSB_CHIPCO_CLK_MC_M1M3:
+                       return (clock / (m1 * m3));
+               }
+               return 0;
+       case SSB_PLLTYPE_2:
+               m1 += SSB_CHIPCO_CLK_T2_BIAS;
+               m2 += SSB_CHIPCO_CLK_T2M2_BIAS;
+               m3 += SSB_CHIPCO_CLK_T2_BIAS;
+               SSB_WARN_ON(!((m1 >= 2) && (m1 <= 7)));
+               SSB_WARN_ON(!((m2 >= 3) && (m2 <= 10)));
+               SSB_WARN_ON(!((m3 >= 2) && (m3 <= 7)));
+
+               if (!(mc & SSB_CHIPCO_CLK_T2MC_M1BYP))
+                       clock /= m1;
+               if (!(mc & SSB_CHIPCO_CLK_T2MC_M2BYP))
+                       clock /= m2;
+               if (!(mc & SSB_CHIPCO_CLK_T2MC_M3BYP))
+                       clock /= m3;
+               return clock;
+       default:
+               SSB_WARN_ON(1);
+       }
+       return 0;
+}
+
+/* Get the current speed the backplane is running at */
+u32 ssb_clockspeed(struct ssb_bus *bus)
+{
+       u32 rate;
+       u32 plltype;
+       u32 clkctl_n, clkctl_m;
+
+       if (ssb_extif_available(&bus->extif))
+               ssb_extif_get_clockcontrol(&bus->extif, &plltype,
+                                          &clkctl_n, &clkctl_m);
+       else if (bus->chipco.dev)
+               ssb_chipco_get_clockcontrol(&bus->chipco, &plltype,
+                                           &clkctl_n, &clkctl_m);
+       else
+               return 0;
+
+       if (bus->chip_id == 0x5365) {
+               rate = 100000000;
+       } else {
+               rate = ssb_calc_clock_rate(plltype, clkctl_n, clkctl_m);
+               if (plltype == SSB_PLLTYPE_3) /* 25Mhz, 2 dividers */
+                       rate /= 2;
+       }
+
+       return rate;
+}
+EXPORT_SYMBOL(ssb_clockspeed);
+
+static u32 ssb_tmslow_reject_bitmask(struct ssb_device *dev)
+{
+       /* The REJECT bit changed position in TMSLOW between
+        * Backplane revisions. */
+       switch (ssb_read32(dev, SSB_IDLOW) & SSB_IDLOW_SSBREV) {
+       case SSB_IDLOW_SSBREV_22:
+               return SSB_TMSLOW_REJECT_22;
+       case SSB_IDLOW_SSBREV_23:
+               return SSB_TMSLOW_REJECT_23;
+       default:
+               WARN_ON(1);
+       }
+       return (SSB_TMSLOW_REJECT_22 | SSB_TMSLOW_REJECT_23);
+}
+
+int ssb_device_is_enabled(struct ssb_device *dev)
+{
+       u32 val;
+       u32 reject;
+
+       reject = ssb_tmslow_reject_bitmask(dev);
+       val = ssb_read32(dev, SSB_TMSLOW);
+       val &= SSB_TMSLOW_CLOCK | SSB_TMSLOW_RESET | reject;
+
+       return (val == SSB_TMSLOW_CLOCK);
+}
+EXPORT_SYMBOL(ssb_device_is_enabled);
+
+static void ssb_flush_tmslow(struct ssb_device *dev)
+{
+       /* Make _really_ sure the device has finished the TMSLOW
+        * register write transaction, as we risk running into
+        * a machine check exception otherwise.
+        * Do this by reading the register back to commit the
+        * PCI write and delay an additional usec for the device
+        * to react to the change. */
+       ssb_read32(dev, SSB_TMSLOW);
+       udelay(1);
+}
+
+void ssb_device_enable(struct ssb_device *dev, u32 core_specific_flags)
+{
+       u32 val;
+
+       ssb_device_disable(dev, core_specific_flags);
+       ssb_write32(dev, SSB_TMSLOW,
+                   SSB_TMSLOW_RESET | SSB_TMSLOW_CLOCK |
+                   SSB_TMSLOW_FGC | core_specific_flags);
+       ssb_flush_tmslow(dev);
+
+       /* Clear SERR if set. This is a hw bug workaround. */
+       if (ssb_read32(dev, SSB_TMSHIGH) & SSB_TMSHIGH_SERR)
+               ssb_write32(dev, SSB_TMSHIGH, 0);
+
+       val = ssb_read32(dev, SSB_IMSTATE);
+       if (val & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO)) {
+               val &= ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO);
+               ssb_write32(dev, SSB_IMSTATE, val);
+       }
+
+       ssb_write32(dev, SSB_TMSLOW,
+                   SSB_TMSLOW_CLOCK | SSB_TMSLOW_FGC |
+                   core_specific_flags);
+       ssb_flush_tmslow(dev);
+
+       ssb_write32(dev, SSB_TMSLOW, SSB_TMSLOW_CLOCK |
+                   core_specific_flags);
+       ssb_flush_tmslow(dev);
+}
+EXPORT_SYMBOL(ssb_device_enable);
+
+/* Wait for a bit in a register to get set or unset.
+ * timeout is in units of ten-microseconds */
+static int ssb_wait_bit(struct ssb_device *dev, u16 reg, u32 bitmask,
+                       int timeout, int set)
+{
+       int i;
+       u32 val;
+
+       for (i = 0; i < timeout; i++) {
+               val = ssb_read32(dev, reg);
+               if (set) {
+                       if (val & bitmask)
+                               return 0;
+               } else {
+                       if (!(val & bitmask))
+                               return 0;
+               }
+               udelay(10);
+       }
+       printk(KERN_ERR PFX "Timeout waiting for bitmask %08X on "
+                           "register %04X to %s.\n",
+              bitmask, reg, (set ? "set" : "clear"));
+
+       return -ETIMEDOUT;
+}
+
+void ssb_device_disable(struct ssb_device *dev, u32 core_specific_flags)
+{
+       u32 reject;
+
+       if (ssb_read32(dev, SSB_TMSLOW) & SSB_TMSLOW_RESET)
+               return;
+
+       reject = ssb_tmslow_reject_bitmask(dev);
+       ssb_write32(dev, SSB_TMSLOW, reject | SSB_TMSLOW_CLOCK);
+       ssb_wait_bit(dev, SSB_TMSLOW, reject, 1000, 1);
+       ssb_wait_bit(dev, SSB_TMSHIGH, SSB_TMSHIGH_BUSY, 1000, 0);
+       ssb_write32(dev, SSB_TMSLOW,
+                   SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK |
+                   reject | SSB_TMSLOW_RESET |
+                   core_specific_flags);
+       ssb_flush_tmslow(dev);
+
+       ssb_write32(dev, SSB_TMSLOW,
+                   reject | SSB_TMSLOW_RESET |
+                   core_specific_flags);
+       ssb_flush_tmslow(dev);
+}
+EXPORT_SYMBOL(ssb_device_disable);
+
+u32 ssb_dma_translation(struct ssb_device *dev)
+{
+       switch (dev->bus->bustype) {
+       case SSB_BUSTYPE_SSB:
+               return 0;
+       case SSB_BUSTYPE_PCI:
+       case SSB_BUSTYPE_PCMCIA:
+               return SSB_PCI_DMA;
+       }
+       return 0;
+}
+EXPORT_SYMBOL(ssb_dma_translation);
+
+int ssb_dma_set_mask(struct ssb_device *ssb_dev, u64 mask)
+{
+       struct device *dev = ssb_dev->dev;
+
+#ifdef CONFIG_SSB_PCIHOST
+       if (ssb_dev->bus->bustype == SSB_BUSTYPE_PCI &&
+           !dma_supported(dev, mask))
+               return -EIO;
+#endif
+       dev->coherent_dma_mask = mask;
+       dev->dma_mask = &dev->coherent_dma_mask;
+
+       return 0;
+}
+EXPORT_SYMBOL(ssb_dma_set_mask);
+
+int ssb_bus_may_powerdown(struct ssb_bus *bus)
+{
+       struct ssb_chipcommon *cc;
+       int err = 0;
+
+       /* On buses where more than one core may be working
+        * at a time, we must not powerdown stuff if there are
+        * still cores that may want to run. */
+       if (bus->bustype == SSB_BUSTYPE_SSB)
+               goto out;
+
+       cc = &bus->chipco;
+       ssb_chipco_set_clockmode(cc, SSB_CLKMODE_SLOW);
+       err = ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0);
+       if (err)
+               goto error;
+out:
+#ifdef CONFIG_SSB_DEBUG
+       bus->powered_up = 0;
+#endif
+       return err;
+error:
+       ssb_printk(KERN_ERR PFX "Bus powerdown failed\n");
+       goto out;
+}
+EXPORT_SYMBOL(ssb_bus_may_powerdown);
+
+int ssb_bus_powerup(struct ssb_bus *bus, bool dynamic_pctl)
+{
+       struct ssb_chipcommon *cc;
+       int err;
+       enum ssb_clkmode mode;
+
+       err = ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 1);
+       if (err)
+               goto error;
+       cc = &bus->chipco;
+       mode = dynamic_pctl ? SSB_CLKMODE_DYNAMIC : SSB_CLKMODE_FAST;
+       ssb_chipco_set_clockmode(cc, mode);
+
+#ifdef CONFIG_SSB_DEBUG
+       bus->powered_up = 1;
+#endif
+       return 0;
+error:
+       ssb_printk(KERN_ERR PFX "Bus powerup failed\n");
+       return err;
+}
+EXPORT_SYMBOL(ssb_bus_powerup);
+
+u32 ssb_admatch_base(u32 adm)
+{
+       u32 base = 0;
+
+       switch (adm & SSB_ADM_TYPE) {
+       case SSB_ADM_TYPE0:
+               base = (adm & SSB_ADM_BASE0);
+               break;
+       case SSB_ADM_TYPE1:
+               SSB_WARN_ON(adm & SSB_ADM_NEG); /* unsupported */
+               base = (adm & SSB_ADM_BASE1);
+               break;
+       case SSB_ADM_TYPE2:
+               SSB_WARN_ON(adm & SSB_ADM_NEG); /* unsupported */
+               base = (adm & SSB_ADM_BASE2);
+               break;
+       default:
+               SSB_WARN_ON(1);
+       }
+
+       return base;
+}
+EXPORT_SYMBOL(ssb_admatch_base);
+
+u32 ssb_admatch_size(u32 adm)
+{
+       u32 size = 0;
+
+       switch (adm & SSB_ADM_TYPE) {
+       case SSB_ADM_TYPE0:
+               size = ((adm & SSB_ADM_SZ0) >> SSB_ADM_SZ0_SHIFT);
+               break;
+       case SSB_ADM_TYPE1:
+               SSB_WARN_ON(adm & SSB_ADM_NEG); /* unsupported */
+               size = ((adm & SSB_ADM_SZ1) >> SSB_ADM_SZ1_SHIFT);
+               break;
+       case SSB_ADM_TYPE2:
+               SSB_WARN_ON(adm & SSB_ADM_NEG); /* unsupported */
+               size = ((adm & SSB_ADM_SZ2) >> SSB_ADM_SZ2_SHIFT);
+               break;
+       default:
+               SSB_WARN_ON(1);
+       }
+       size = (1 << (size + 1));
+
+       return size;
+}
+EXPORT_SYMBOL(ssb_admatch_size);
+
+static int __init ssb_modinit(void)
+{
+       int err;
+
+       /* See the comment at the ssb_is_early_boot definition */
+       ssb_is_early_boot = 0;
+       err = bus_register(&ssb_bustype);
+       if (err)
+               return err;
+
+       /* Maybe we already registered some buses at early boot.
+        * Check for this and attach them
+        */
+       ssb_buses_lock();
+       err = ssb_attach_queued_buses();
+       ssb_buses_unlock();
+       if (err)
+               bus_unregister(&ssb_bustype);
+
+       err = b43_pci_ssb_bridge_init();
+       if (err) {
+               ssb_printk(KERN_ERR "Broadcom 43xx PCI-SSB-bridge "
+                          "initialization failed");
+               /* don't fail SSB init because of this */
+               err = 0;
+       }
+
+       return err;
+}
+subsys_initcall(ssb_modinit);
+
+static void __exit ssb_modexit(void)
+{
+       b43_pci_ssb_bridge_exit();
+       bus_unregister(&ssb_bustype);
+}
+module_exit(ssb_modexit)
diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c
new file mode 100644 (file)
index 0000000..3d23ca4
--- /dev/null
@@ -0,0 +1,740 @@
+/*
+ * Sonics Silicon Backplane PCI-Hostbus related functions.
+ *
+ * Copyright (C) 2005-2006 Michael Buesch <mb@bu3sch.de>
+ * Copyright (C) 2005 Martin Langer <martin-langer@gmx.de>
+ * Copyright (C) 2005 Stefano Brivio <st3@riseup.net>
+ * Copyright (C) 2005 Danny van Dyk <kugelfang@gentoo.org>
+ * Copyright (C) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
+ *
+ * Derived from the Broadcom 4400 device driver.
+ * Copyright (C) 2002 David S. Miller (davem@redhat.com)
+ * Fixed by Pekka Pietikainen (pp@ee.oulu.fi)
+ * 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/delay.h>
+
+#include "ssb_private.h"
+
+
+/* Define the following to 1 to enable a printk on each coreswitch. */
+#define SSB_VERBOSE_PCICORESWITCH_DEBUG                0
+
+
+/* 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;
+}
+
+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;
+}
+
+/* Enable/disable the on board crystal oscillator and/or PLL. */
+int ssb_pci_xtal(struct ssb_bus *bus, u32 what, int turn_on)
+{
+       int err;
+       u32 in, out, outenable;
+       u16 pci_status;
+
+       if (bus->bustype != SSB_BUSTYPE_PCI)
+               return 0;
+
+       err = pci_read_config_dword(bus->host_pci, SSB_GPIO_IN, &in);
+       if (err)
+               goto err_pci;
+       err = pci_read_config_dword(bus->host_pci, SSB_GPIO_OUT, &out);
+       if (err)
+               goto err_pci;
+       err = pci_read_config_dword(bus->host_pci, SSB_GPIO_OUT_ENABLE, &outenable);
+       if (err)
+               goto err_pci;
+
+       outenable |= what;
+
+       if (turn_on) {
+               /* Avoid glitching the clock if GPRS is already using it.
+                * We can't actually read the state of the PLLPD so we infer it
+                * by the value of XTAL_PU which *is* readable via gpioin.
+                */
+               if (!(in & SSB_GPIO_XTAL)) {
+                       if (what & SSB_GPIO_XTAL) {
+                               /* Turn the crystal on */
+                               out |= SSB_GPIO_XTAL;
+                               if (what & SSB_GPIO_PLL)
+                                       out |= SSB_GPIO_PLL;
+                               err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT, out);
+                               if (err)
+                                       goto err_pci;
+                               err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT_ENABLE,
+                                                            outenable);
+                               if (err)
+                                       goto err_pci;
+                               msleep(1);
+                       }
+                       if (what & SSB_GPIO_PLL) {
+                               /* Turn the PLL on */
+                               out &= ~SSB_GPIO_PLL;
+                               err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT, out);
+                               if (err)
+                                       goto err_pci;
+                               msleep(5);
+                       }
+               }
+
+               err = pci_read_config_word(bus->host_pci, PCI_STATUS, &pci_status);
+               if (err)
+                       goto err_pci;
+               pci_status &= ~PCI_STATUS_SIG_TARGET_ABORT;
+               err = pci_write_config_word(bus->host_pci, PCI_STATUS, pci_status);
+               if (err)
+                       goto err_pci;
+       } else {
+               if (what & SSB_GPIO_XTAL) {
+                       /* Turn the crystal off */
+                       out &= ~SSB_GPIO_XTAL;
+               }
+               if (what & SSB_GPIO_PLL) {
+                       /* Turn the PLL off */
+                       out |= SSB_GPIO_PLL;
+               }
+               err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT, out);
+               if (err)
+                       goto err_pci;
+               err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT_ENABLE, outenable);
+               if (err)
+                       goto err_pci;
+       }
+
+out:
+       return err;
+
+err_pci:
+       printk(KERN_ERR PFX "Error: ssb_pci_xtal() could not access PCI config space!\n");
+       err = -EBUSY;
+       goto out;
+}
+
+/* Get the word-offset for a SSB_SPROM_XXX define. */
+#define SPOFF(offset)  (((offset) - SSB_SPROM_BASE) / sizeof(u16))
+/* Helper to extract some _offset, which is one of the SSB_SPROM_XXX defines. */
+#define SPEX(_outvar, _offset, _mask, _shift)  \
+       out->_outvar = ((in[SPOFF(_offset)] & (_mask)) >> (_shift))
+
+static inline u8 ssb_crc8(u8 crc, u8 data)
+{
+       /* Polynomial:   x^8 + x^7 + x^6 + x^4 + x^2 + 1   */
+       static const u8 t[] = {
+               0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,
+               0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,
+               0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,
+               0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,
+               0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,
+               0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,
+               0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,
+               0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,
+               0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,
+               0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,
+               0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,
+               0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,
+               0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,
+               0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,
+               0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,
+               0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,
+               0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,
+               0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,
+               0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,
+               0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,
+               0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,
+               0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,
+               0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,
+               0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,
+               0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,
+               0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,
+               0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,
+               0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,
+               0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,
+               0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,
+               0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,
+               0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F,
+       };
+       return t[crc ^ data];
+}
+
+static u8 ssb_sprom_crc(const u16 *sprom)
+{
+       int word;
+       u8 crc = 0xFF;
+
+       for (word = 0; word < SSB_SPROMSIZE_WORDS - 1; word++) {
+               crc = ssb_crc8(crc, sprom[word] & 0x00FF);
+               crc = ssb_crc8(crc, (sprom[word] & 0xFF00) >> 8);
+       }
+       crc = ssb_crc8(crc, sprom[SPOFF(SSB_SPROM_REVISION)] & 0x00FF);
+       crc ^= 0xFF;
+
+       return crc;
+}
+
+static int sprom_check_crc(const u16 *sprom)
+{
+       u8 crc;
+       u8 expected_crc;
+       u16 tmp;
+
+       crc = ssb_sprom_crc(sprom);
+       tmp = sprom[SPOFF(SSB_SPROM_REVISION)] & SSB_SPROM_REVISION_CRC;
+       expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT;
+       if (crc != expected_crc)
+               return -EPROTO;
+
+       return 0;
+}
+
+static void sprom_do_read(struct ssb_bus *bus, u16 *sprom)
+{
+       int i;
+
+       for (i = 0; i < SSB_SPROMSIZE_WORDS; i++)
+               sprom[i] = readw(bus->mmio + SSB_SPROM_BASE + (i * 2));
+}
+
+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;
+}
+
+static void sprom_extract_r1(struct ssb_sprom_r1 *out, const u16 *in)
+{
+       int i;
+       u16 v;
+
+       SPEX(pci_spid, SSB_SPROM1_SPID, 0xFFFF, 0);
+       SPEX(pci_svid, SSB_SPROM1_SVID, 0xFFFF, 0);
+       SPEX(pci_pid, SSB_SPROM1_PID, 0xFFFF, 0);
+       for (i = 0; i < 3; i++) {
+               v = in[SPOFF(SSB_SPROM1_IL0MAC) + i];
+               *(((u16 *)out->il0mac) + i) = cpu_to_be16(v);
+       }
+       for (i = 0; i < 3; i++) {
+               v = in[SPOFF(SSB_SPROM1_ET0MAC) + i];
+               *(((u16 *)out->et0mac) + i) = cpu_to_be16(v);
+       }
+       for (i = 0; i < 3; i++) {
+               v = in[SPOFF(SSB_SPROM1_ET1MAC) + i];
+               *(((u16 *)out->et1mac) + i) = cpu_to_be16(v);
+       }
+       SPEX(et0phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0A, 0);
+       SPEX(et1phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1A,
+            SSB_SPROM1_ETHPHY_ET1A_SHIFT);
+       SPEX(et0mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0M, 14);
+       SPEX(et1mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1M, 15);
+       SPEX(board_rev, SSB_SPROM1_BINF, SSB_SPROM1_BINF_BREV, 0);
+       SPEX(country_code, SSB_SPROM1_BINF, SSB_SPROM1_BINF_CCODE,
+            SSB_SPROM1_BINF_CCODE_SHIFT);
+       SPEX(antenna_a, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTA,
+            SSB_SPROM1_BINF_ANTA_SHIFT);
+       SPEX(antenna_bg, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTBG,
+            SSB_SPROM1_BINF_ANTBG_SHIFT);
+       SPEX(pa0b0, SSB_SPROM1_PA0B0, 0xFFFF, 0);
+       SPEX(pa0b1, SSB_SPROM1_PA0B1, 0xFFFF, 0);
+       SPEX(pa0b2, SSB_SPROM1_PA0B2, 0xFFFF, 0);
+       SPEX(pa1b0, SSB_SPROM1_PA1B0, 0xFFFF, 0);
+       SPEX(pa1b1, SSB_SPROM1_PA1B1, 0xFFFF, 0);
+       SPEX(pa1b2, SSB_SPROM1_PA1B2, 0xFFFF, 0);
+       SPEX(gpio0, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P0, 0);
+       SPEX(gpio1, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P1,
+            SSB_SPROM1_GPIOA_P1_SHIFT);
+       SPEX(gpio2, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P2, 0);
+       SPEX(gpio3, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P3,
+            SSB_SPROM1_GPIOB_P3_SHIFT);
+       SPEX(maxpwr_a, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_A,
+            SSB_SPROM1_MAXPWR_A_SHIFT);
+       SPEX(maxpwr_bg, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_BG, 0);
+       SPEX(itssi_a, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_A,
+            SSB_SPROM1_ITSSI_A_SHIFT);
+       SPEX(itssi_bg, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_BG, 0);
+       SPEX(boardflags_lo, SSB_SPROM1_BFLLO, 0xFFFF, 0);
+       SPEX(antenna_gain_a, SSB_SPROM1_AGAIN, SSB_SPROM1_AGAIN_A, 0);
+       SPEX(antenna_gain_bg, SSB_SPROM1_AGAIN, SSB_SPROM1_AGAIN_BG,
+            SSB_SPROM1_AGAIN_BG_SHIFT);
+       for (i = 0; i < 4; i++) {
+               v = in[SPOFF(SSB_SPROM1_OEM) + i];
+               *(((u16 *)out->oem) + i) = cpu_to_le16(v);
+       }
+}
+
+static void sprom_extract_r2(struct ssb_sprom_r2 *out, const u16 *in)
+{
+       int i;
+       u16 v;
+
+       SPEX(boardflags_hi, SSB_SPROM2_BFLHI,  0xFFFF, 0);
+       SPEX(maxpwr_a_hi, SSB_SPROM2_MAXP_A, SSB_SPROM2_MAXP_A_HI, 0);
+       SPEX(maxpwr_a_lo, SSB_SPROM2_MAXP_A, SSB_SPROM2_MAXP_A_LO,
+            SSB_SPROM2_MAXP_A_LO_SHIFT);
+       SPEX(pa1lob0, SSB_SPROM2_PA1LOB0, 0xFFFF, 0);
+       SPEX(pa1lob1, SSB_SPROM2_PA1LOB1, 0xFFFF, 0);
+       SPEX(pa1lob2, SSB_SPROM2_PA1LOB2, 0xFFFF, 0);
+       SPEX(pa1hib0, SSB_SPROM2_PA1HIB0, 0xFFFF, 0);
+       SPEX(pa1hib1, SSB_SPROM2_PA1HIB1, 0xFFFF, 0);
+       SPEX(pa1hib2, SSB_SPROM2_PA1HIB2, 0xFFFF, 0);
+       SPEX(ofdm_pwr_off, SSB_SPROM2_OPO, SSB_SPROM2_OPO_VALUE, 0);
+       for (i = 0; i < 4; i++) {
+               v = in[SPOFF(SSB_SPROM2_CCODE) + i];
+               *(((u16 *)out->country_str) + i) = cpu_to_le16(v);
+       }
+}
+
+static void sprom_extract_r3(struct ssb_sprom_r3 *out, const u16 *in)
+{
+       out->ofdmapo  = (in[SPOFF(SSB_SPROM3_OFDMAPO) + 0] & 0xFF00) >> 8;
+       out->ofdmapo |= (in[SPOFF(SSB_SPROM3_OFDMAPO) + 0] & 0x00FF) << 8;
+       out->ofdmapo <<= 16;
+       out->ofdmapo |= (in[SPOFF(SSB_SPROM3_OFDMAPO) + 1] & 0xFF00) >> 8;
+       out->ofdmapo |= (in[SPOFF(SSB_SPROM3_OFDMAPO) + 1] & 0x00FF) << 8;
+
+       out->ofdmalpo  = (in[SPOFF(SSB_SPROM3_OFDMALPO) + 0] & 0xFF00) >> 8;
+       out->ofdmalpo |= (in[SPOFF(SSB_SPROM3_OFDMALPO) + 0] & 0x00FF) << 8;
+       out->ofdmalpo <<= 16;
+       out->ofdmalpo |= (in[SPOFF(SSB_SPROM3_OFDMALPO) + 1] & 0xFF00) >> 8;
+       out->ofdmalpo |= (in[SPOFF(SSB_SPROM3_OFDMALPO) + 1] & 0x00FF) << 8;
+
+       out->ofdmahpo  = (in[SPOFF(SSB_SPROM3_OFDMAHPO) + 0] & 0xFF00) >> 8;
+       out->ofdmahpo |= (in[SPOFF(SSB_SPROM3_OFDMAHPO) + 0] & 0x00FF) << 8;
+       out->ofdmahpo <<= 16;
+       out->ofdmahpo |= (in[SPOFF(SSB_SPROM3_OFDMAHPO) + 1] & 0xFF00) >> 8;
+       out->ofdmahpo |= (in[SPOFF(SSB_SPROM3_OFDMAHPO) + 1] & 0x00FF) << 8;
+
+       SPEX(gpioldc_on_cnt, SSB_SPROM3_GPIOLDC, SSB_SPROM3_GPIOLDC_ON,
+            SSB_SPROM3_GPIOLDC_ON_SHIFT);
+       SPEX(gpioldc_off_cnt, SSB_SPROM3_GPIOLDC, SSB_SPROM3_GPIOLDC_OFF,
+            SSB_SPROM3_GPIOLDC_OFF_SHIFT);
+       SPEX(cckpo_1M, SSB_SPROM3_CCKPO, SSB_SPROM3_CCKPO_1M, 0);
+       SPEX(cckpo_2M, SSB_SPROM3_CCKPO, SSB_SPROM3_CCKPO_2M,
+            SSB_SPROM3_CCKPO_2M_SHIFT);
+       SPEX(cckpo_55M, SSB_SPROM3_CCKPO, SSB_SPROM3_CCKPO_55M,
+            SSB_SPROM3_CCKPO_55M_SHIFT);
+       SPEX(cckpo_11M, SSB_SPROM3_CCKPO, SSB_SPROM3_CCKPO_11M,
+            SSB_SPROM3_CCKPO_11M_SHIFT);
+
+       out->ofdmgpo  = (in[SPOFF(SSB_SPROM3_OFDMGPO) + 0] & 0xFF00) >> 8;
+       out->ofdmgpo |= (in[SPOFF(SSB_SPROM3_OFDMGPO) + 0] & 0x00FF) << 8;
+       out->ofdmgpo <<= 16;
+       out->ofdmgpo |= (in[SPOFF(SSB_SPROM3_OFDMGPO) + 1] & 0xFF00) >> 8;
+       out->ofdmgpo |= (in[SPOFF(SSB_SPROM3_OFDMGPO) + 1] & 0x00FF) << 8;
+}
+
+static int sprom_extract(struct ssb_bus *bus,
+                        struct ssb_sprom *out, const u16 *in)
+{
+       memset(out, 0, sizeof(*out));
+
+       SPEX(revision, SSB_SPROM_REVISION, SSB_SPROM_REVISION_REV, 0);
+       SPEX(crc, SSB_SPROM_REVISION, SSB_SPROM_REVISION_CRC,
+            SSB_SPROM_REVISION_CRC_SHIFT);
+
+       if ((bus->chip_id & 0xFF00) == 0x4400) {
+               /* Workaround: The BCM44XX chip has a stupid revision
+                * number stored in the SPROM.
+                * Always extract r1. */
+               sprom_extract_r1(&out->r1, in);
+       } else {
+               if (out->revision == 0)
+                       goto unsupported;
+               if (out->revision >= 1 && out->revision <= 3)
+                       sprom_extract_r1(&out->r1, in);
+               if (out->revision >= 2 && out->revision <= 3)
+                       sprom_extract_r2(&out->r2, in);
+               if (out->revision == 3)
+                       sprom_extract_r3(&out->r3, in);
+               if (out->revision >= 4)
+                       goto unsupported;
+       }
+
+       return 0;
+unsupported:
+       ssb_printk(KERN_WARNING PFX "Unsupported SPROM revision %d "
+                  "detected. Will extract v1\n", out->revision);
+       sprom_extract_r1(&out->r1, in);
+       return 0;
+}
+
+static int ssb_pci_sprom_get(struct ssb_bus *bus,
+                            struct ssb_sprom *sprom)
+{
+       int err = -ENOMEM;
+       u16 *buf;
+
+       buf = kcalloc(SSB_SPROMSIZE_WORDS, sizeof(u16), GFP_KERNEL);
+       if (!buf)
+               goto out;
+       sprom_do_read(bus, buf);
+       err = sprom_check_crc(buf);
+       if (err) {
+               ssb_printk(KERN_WARNING PFX
+                          "WARNING: Invalid SPROM CRC (corrupt SPROM)\n");
+       }
+       err = sprom_extract(bus, sprom, buf);
+
+       kfree(buf);
+out:
+       return err;
+}
+
+static void ssb_pci_get_boardinfo(struct ssb_bus *bus,
+                                 struct ssb_boardinfo *bi)
+{
+       pci_read_config_word(bus->host_pci, PCI_SUBSYSTEM_VENDOR_ID,
+                            &bi->vendor);
+       pci_read_config_word(bus->host_pci, PCI_SUBSYSTEM_ID,
+                            &bi->type);
+       pci_read_config_word(bus->host_pci, PCI_REVISION_ID,
+                            &bi->rev);
+}
+
+int ssb_pci_get_invariants(struct ssb_bus *bus,
+                          struct ssb_init_invariants *iv)
+{
+       int err;
+
+       err = ssb_pci_sprom_get(bus, &iv->sprom);
+       if (err)
+               goto out;
+       ssb_pci_get_boardinfo(bus, &iv->boardinfo);
+
+out:
+       return err;
+}
+
+#ifdef CONFIG_SSB_DEBUG
+static int ssb_pci_assert_buspower(struct ssb_bus *bus)
+{
+       if (likely(bus->powered_up))
+               return 0;
+
+       printk(KERN_ERR PFX "FATAL ERROR: Bus powered down "
+              "while accessing PCI MMIO space\n");
+       if (bus->power_warn_count <= 10) {
+               bus->power_warn_count++;
+               dump_stack();
+       }
+
+       return -ENODEV;
+}
+#else /* DEBUG */
+static inline int ssb_pci_assert_buspower(struct ssb_bus *bus)
+{
+       return 0;
+}
+#endif /* DEBUG */
+
+static u16 ssb_pci_read16(struct ssb_device *dev, u16 offset)
+{
+       struct ssb_bus *bus = dev->bus;
+
+       if (unlikely(ssb_pci_assert_buspower(bus)))
+               return 0xFFFF;
+       if (unlikely(bus->mapped_device != dev)) {
+               if (unlikely(ssb_pci_switch_core(bus, dev)))
+                       return 0xFFFF;
+       }
+       return readw(bus->mmio + offset);
+}
+
+static u32 ssb_pci_read32(struct ssb_device *dev, u16 offset)
+{
+       struct ssb_bus *bus = dev->bus;
+
+       if (unlikely(ssb_pci_assert_buspower(bus)))
+               return 0xFFFFFFFF;
+       if (unlikely(bus->mapped_device != dev)) {
+               if (unlikely(ssb_pci_switch_core(bus, dev)))
+                       return 0xFFFFFFFF;
+       }
+       return readl(bus->mmio + offset);
+}
+
+static void ssb_pci_write16(struct ssb_device *dev, u16 offset, u16 value)
+{
+       struct ssb_bus *bus = dev->bus;
+
+       if (unlikely(ssb_pci_assert_buspower(bus)))
+               return;
+       if (unlikely(bus->mapped_device != dev)) {
+               if (unlikely(ssb_pci_switch_core(bus, dev)))
+                       return;
+       }
+       writew(value, bus->mmio + offset);
+}
+
+static void ssb_pci_write32(struct ssb_device *dev, u16 offset, u32 value)
+{
+       struct ssb_bus *bus = dev->bus;
+
+       if (unlikely(ssb_pci_assert_buspower(bus)))
+               return;
+       if (unlikely(bus->mapped_device != dev)) {
+               if (unlikely(ssb_pci_switch_core(bus, dev)))
+                       return;
+       }
+       writel(value, bus->mmio + offset);
+}
+
+/* Not "static", as it's used in main.c */
+const struct ssb_bus_ops ssb_pci_ops = {
+       .read16         = ssb_pci_read16,
+       .read32         = ssb_pci_read32,
+       .write16        = ssb_pci_write16,
+       .write32        = ssb_pci_write32,
+};
+
+static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len)
+{
+       int i, pos = 0;
+
+       for (i = 0; i < SSB_SPROMSIZE_WORDS; i++) {
+               pos += snprintf(buf + pos, buf_len - pos - 1,
+                               "%04X", swab16(sprom[i]) & 0xFFFF);
+       }
+       pos += snprintf(buf + pos, buf_len - pos - 1, "\n");
+
+       return pos + 1;
+}
+
+static int hex2sprom(u16 *sprom, const char *dump, size_t len)
+{
+       char tmp[5] = { 0 };
+       int cnt = 0;
+       unsigned long parsed;
+
+       if (len < SSB_SPROMSIZE_BYTES * 2)
+               return -EINVAL;
+
+       while (cnt < SSB_SPROMSIZE_WORDS) {
+               memcpy(tmp, dump, 4);
+               dump += 4;
+               parsed = simple_strtoul(tmp, NULL, 16);
+               sprom[cnt++] = swab16((u16)parsed);
+       }
+
+       return 0;
+}
+
+static ssize_t ssb_pci_attr_sprom_show(struct device *pcidev,
+                                      struct device_attribute *attr,
+                                      char *buf)
+{
+       struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev);
+       struct ssb_bus *bus;
+       u16 *sprom;
+       int err = -ENODEV;
+       ssize_t count = 0;
+
+       bus = ssb_pci_dev_to_bus(pdev);
+       if (!bus)
+               goto out;
+       err = -ENOMEM;
+       sprom = kcalloc(SSB_SPROMSIZE_WORDS, sizeof(u16), GFP_KERNEL);
+       if (!sprom)
+               goto out;
+
+       /* Use interruptible locking, as the SPROM write might
+        * be holding the lock for several seconds. So allow userspace
+        * to cancel operation. */
+       err = -ERESTARTSYS;
+       if (mutex_lock_interruptible(&bus->pci_sprom_mutex))
+               goto out_kfree;
+       sprom_do_read(bus, sprom);
+       mutex_unlock(&bus->pci_sprom_mutex);
+
+       count = sprom2hex(sprom, buf, PAGE_SIZE);
+       err = 0;
+
+out_kfree:
+       kfree(sprom);
+out:
+       return err ? err : count;
+}
+
+static ssize_t ssb_pci_attr_sprom_store(struct device *pcidev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t count)
+{
+       struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev);
+       struct ssb_bus *bus;
+       u16 *sprom;
+       int res = 0, err = -ENODEV;
+
+       bus = ssb_pci_dev_to_bus(pdev);
+       if (!bus)
+               goto out;
+       err = -ENOMEM;
+       sprom = kcalloc(SSB_SPROMSIZE_WORDS, sizeof(u16), GFP_KERNEL);
+       if (!sprom)
+               goto out;
+       err = hex2sprom(sprom, buf, count);
+       if (err) {
+               err = -EINVAL;
+               goto out_kfree;
+       }
+       err = sprom_check_crc(sprom);
+       if (err) {
+               err = -EINVAL;
+               goto out_kfree;
+       }
+
+       /* Use interruptible locking, as the SPROM write might
+        * be holding the lock for several seconds. So allow userspace
+        * to cancel operation. */
+       err = -ERESTARTSYS;
+       if (mutex_lock_interruptible(&bus->pci_sprom_mutex))
+               goto out_kfree;
+       err = ssb_devices_freeze(bus);
+       if (err == -EOPNOTSUPP) {
+               ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze devices. "
+                          "No suspend support. Is CONFIG_PM enabled?\n");
+               goto out_unlock;
+       }
+       if (err) {
+               ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze all devices\n");
+               goto out_unlock;
+       }
+       res = sprom_do_write(bus, sprom);
+       err = ssb_devices_thaw(bus);
+       if (err)
+               ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n");
+out_unlock:
+       mutex_unlock(&bus->pci_sprom_mutex);
+out_kfree:
+       kfree(sprom);
+out:
+       if (res)
+               return res;
+       return err ? err : count;
+}
+
+static DEVICE_ATTR(ssb_sprom, 0600,
+                  ssb_pci_attr_sprom_show,
+                  ssb_pci_attr_sprom_store);
+
+void ssb_pci_exit(struct ssb_bus *bus)
+{
+       struct pci_dev *pdev;
+
+       if (bus->bustype != SSB_BUSTYPE_PCI)
+               return;
+
+       pdev = bus->host_pci;
+       device_remove_file(&pdev->dev, &dev_attr_ssb_sprom);
+}
+
+int ssb_pci_init(struct ssb_bus *bus)
+{
+       struct pci_dev *pdev;
+       int err;
+
+       if (bus->bustype != SSB_BUSTYPE_PCI)
+               return 0;
+
+       pdev = bus->host_pci;
+       mutex_init(&bus->pci_sprom_mutex);
+       err = device_create_file(&pdev->dev, &dev_attr_ssb_sprom);
+       if (err)
+               goto out;
+
+out:
+       return err;
+}
diff --git a/drivers/ssb/pcihost_wrapper.c b/drivers/ssb/pcihost_wrapper.c
new file mode 100644 (file)
index 0000000..82a10ab
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Sonics Silicon Backplane
+ * PCI Hostdevice wrapper
+ *
+ * Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>
+ * Copyright (c) 2005 Stefano Brivio <st3@riseup.net>
+ * Copyright (c) 2005 Danny van Dyk <kugelfang@gentoo.org>
+ * Copyright (c) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
+ * Copyright (c) 2005-2007 Michael Buesch <mbuesch@freenet.de>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/pci.h>
+#include <linux/ssb/ssb.h>
+
+
+#ifdef CONFIG_PM
+static int ssb_pcihost_suspend(struct pci_dev *dev, pm_message_t state)
+{
+       pci_save_state(dev);
+       pci_disable_device(dev);
+       pci_set_power_state(dev, pci_choose_state(dev, state));
+
+       return 0;
+}
+
+static int ssb_pcihost_resume(struct pci_dev *dev)
+{
+       int err;
+
+       pci_set_power_state(dev, 0);
+       err = pci_enable_device(dev);
+       if (err)
+               return err;
+       pci_restore_state(dev);
+
+       return 0;
+}
+#else /* CONFIG_PM */
+# define ssb_pcihost_suspend   NULL
+# define ssb_pcihost_resume    NULL
+#endif /* CONFIG_PM */
+
+static int ssb_pcihost_probe(struct pci_dev *dev,
+                            const struct pci_device_id *id)
+{
+       struct ssb_bus *ssb;
+       int err = -ENOMEM;
+       const char *name;
+
+       ssb = kzalloc(sizeof(*ssb), GFP_KERNEL);
+       if (!ssb)
+               goto out;
+       err = pci_enable_device(dev);
+       if (err)
+               goto err_kfree_ssb;
+       name = dev->dev.bus_id;
+       if (dev->driver && dev->driver->name)
+               name = dev->driver->name;
+       err = pci_request_regions(dev, name);
+       if (err)
+               goto err_pci_disable;
+       pci_set_master(dev);
+
+       err = ssb_bus_pcibus_register(ssb, dev);
+       if (err)
+               goto err_pci_release_regions;
+
+       pci_set_drvdata(dev, ssb);
+
+out:
+       return err;
+
+err_pci_release_regions:
+       pci_release_regions(dev);
+err_pci_disable:
+       pci_disable_device(dev);
+err_kfree_ssb:
+       kfree(ssb);
+       return err;
+}
+
+static void ssb_pcihost_remove(struct pci_dev *dev)
+{
+       struct ssb_bus *ssb = pci_get_drvdata(dev);
+
+       ssb_bus_unregister(ssb);
+       pci_release_regions(dev);
+       pci_disable_device(dev);
+       kfree(ssb);
+       pci_set_drvdata(dev, NULL);
+}
+
+int ssb_pcihost_register(struct pci_driver *driver)
+{
+       driver->probe = ssb_pcihost_probe;
+       driver->remove = ssb_pcihost_remove;
+       driver->suspend = ssb_pcihost_suspend;
+       driver->resume = ssb_pcihost_resume;
+
+       return pci_register_driver(driver);
+}
+EXPORT_SYMBOL(ssb_pcihost_register);
diff --git a/drivers/ssb/pcmcia.c b/drivers/ssb/pcmcia.c
new file mode 100644 (file)
index 0000000..7c77360
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+ * Sonics Silicon Backplane
+ * PCMCIA-Hostbus related functions
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2007 Michael Buesch <mb@bu3sch.de>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+
+#include <linux/ssb/ssb.h>
+#include <linux/delay.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include "ssb_private.h"
+
+
+/* Define the following to 1 to enable a printk on each coreswitch. */
+#define SSB_VERBOSE_PCMCIACORESWITCH_DEBUG             0
+
+
+int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus,
+                             u8 coreidx)
+{
+       struct pcmcia_device *pdev = bus->host_pcmcia;
+       int err;
+       int attempts = 0;
+       u32 cur_core;
+       conf_reg_t reg;
+       u32 addr;
+       u32 read_addr;
+
+       addr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE;
+       while (1) {
+               reg.Action = CS_WRITE;
+               reg.Offset = 0x2E;
+               reg.Value = (addr & 0x0000F000) >> 12;
+               err = pcmcia_access_configuration_register(pdev, &reg);
+               if (err != CS_SUCCESS)
+                       goto error;
+               reg.Offset = 0x30;
+               reg.Value = (addr & 0x00FF0000) >> 16;
+               err = pcmcia_access_configuration_register(pdev, &reg);
+               if (err != CS_SUCCESS)
+                       goto error;
+               reg.Offset = 0x32;
+               reg.Value = (addr & 0xFF000000) >> 24;
+               err = pcmcia_access_configuration_register(pdev, &reg);
+               if (err != CS_SUCCESS)
+                       goto error;
+
+               read_addr = 0;
+
+               reg.Action = CS_READ;
+               reg.Offset = 0x2E;
+               err = pcmcia_access_configuration_register(pdev, &reg);
+               if (err != CS_SUCCESS)
+                       goto error;
+               read_addr |= (reg.Value & 0xF) << 12;
+               reg.Offset = 0x30;
+               err = pcmcia_access_configuration_register(pdev, &reg);
+               if (err != CS_SUCCESS)
+                       goto error;
+               read_addr |= reg.Value << 16;
+               reg.Offset = 0x32;
+               err = pcmcia_access_configuration_register(pdev, &reg);
+               if (err != CS_SUCCESS)
+                       goto error;
+               read_addr |= reg.Value << 24;
+
+               cur_core = (read_addr - 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;
+}
+
+int ssb_pcmcia_switch_core(struct ssb_bus *bus,
+                          struct ssb_device *dev)
+{
+       int err;
+       unsigned long flags;
+
+#if SSB_VERBOSE_PCMCIACORESWITCH_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_pcmcia_switch_coreidx(bus, dev->core_index);
+       if (!err)
+               bus->mapped_device = dev;
+       spin_unlock_irqrestore(&bus->bar_lock, flags);
+
+       return err;
+}
+
+int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg)
+{
+       int attempts = 0;
+       unsigned long flags;
+       conf_reg_t reg;
+       int res, err = 0;
+
+       SSB_WARN_ON((seg != 0) && (seg != 1));
+       reg.Offset = 0x34;
+       reg.Function = 0;
+       spin_lock_irqsave(&bus->bar_lock, flags);
+       while (1) {
+               reg.Action = CS_WRITE;
+               reg.Value = seg;
+               res = pcmcia_access_configuration_register(bus->host_pcmcia, &reg);
+               if (unlikely(res != CS_SUCCESS))
+                       goto error;
+               reg.Value = 0xFF;
+               reg.Action = CS_READ;
+               res = pcmcia_access_configuration_register(bus->host_pcmcia, &reg);
+               if (unlikely(res != CS_SUCCESS))
+                       goto error;
+
+               if (reg.Value == seg)
+                       break;
+
+               if (unlikely(attempts++ > SSB_BAR0_MAX_RETRIES))
+                       goto error;
+               udelay(10);
+       }
+       bus->mapped_pcmcia_seg = seg;
+out_unlock:
+       spin_unlock_irqrestore(&bus->bar_lock, flags);
+       return err;
+error:
+       ssb_printk(KERN_ERR PFX "Failed to switch pcmcia segment\n");
+       err = -ENODEV;
+       goto out_unlock;
+}
+
+/* These are the main device register access functions.
+ * do_select_core is inline to have the likely hotpath inline.
+ * All unlikely codepaths are out-of-line. */
+static inline int do_select_core(struct ssb_bus *bus,
+                                struct ssb_device *dev,
+                                u16 *offset)
+{
+       int err;
+       u8 need_seg = (*offset >= 0x800) ? 1 : 0;
+
+       if (unlikely(dev != bus->mapped_device)) {
+               err = ssb_pcmcia_switch_core(bus, dev);
+               if (unlikely(err))
+                       return err;
+       }
+       if (unlikely(need_seg != bus->mapped_pcmcia_seg)) {
+               err = ssb_pcmcia_switch_segment(bus, need_seg);
+               if (unlikely(err))
+                       return err;
+       }
+       if (need_seg == 1)
+               *offset -= 0x800;
+
+       return 0;
+}
+
+static u16 ssb_pcmcia_read16(struct ssb_device *dev, u16 offset)
+{
+       struct ssb_bus *bus = dev->bus;
+       u16 x;
+
+       if (unlikely(do_select_core(bus, dev, &offset)))
+               return 0xFFFF;
+       x = readw(bus->mmio + offset);
+
+       return x;
+}
+
+static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset)
+{
+       struct ssb_bus *bus = dev->bus;
+       u32 x;
+
+       if (unlikely(do_select_core(bus, dev, &offset)))
+               return 0xFFFFFFFF;
+       x = readl(bus->mmio + offset);
+
+       return x;
+}
+
+static void ssb_pcmcia_write16(struct ssb_device *dev, u16 offset, u16 value)
+{
+       struct ssb_bus *bus = dev->bus;
+
+       if (unlikely(do_select_core(bus, dev, &offset)))
+               return;
+       writew(value, bus->mmio + offset);
+}
+
+static void ssb_pcmcia_write32(struct ssb_device *dev, u16 offset, u32 value)
+{
+       struct ssb_bus *bus = dev->bus;
+
+       if (unlikely(do_select_core(bus, dev, &offset)))
+               return;
+       readw(bus->mmio + offset);
+       writew(value >> 16, bus->mmio + offset + 2);
+       readw(bus->mmio + offset);
+       writew(value, bus->mmio + offset);
+}
+
+/* Not "static", as it's used in main.c */
+const struct ssb_bus_ops ssb_pcmcia_ops = {
+       .read16         = ssb_pcmcia_read16,
+       .read32         = ssb_pcmcia_read32,
+       .write16        = ssb_pcmcia_write16,
+       .write32        = ssb_pcmcia_write32,
+};
+
+int ssb_pcmcia_get_invariants(struct ssb_bus *bus,
+                             struct ssb_init_invariants *iv)
+{
+       //TODO
+       return 0;
+}
+
+int ssb_pcmcia_init(struct ssb_bus *bus)
+{
+       conf_reg_t reg;
+       int err;
+
+       if (bus->bustype != SSB_BUSTYPE_PCMCIA)
+               return 0;
+
+       /* Switch segment to a known state and sync
+        * bus->mapped_pcmcia_seg with hardware state. */
+       ssb_pcmcia_switch_segment(bus, 0);
+
+       /* Init IRQ routing */
+       reg.Action = CS_READ;
+       reg.Function = 0;
+       if (bus->chip_id == 0x4306)
+               reg.Offset = 0x00;
+       else
+               reg.Offset = 0x80;
+       err = pcmcia_access_configuration_register(bus->host_pcmcia, &reg);
+       if (err != CS_SUCCESS)
+               goto error;
+       reg.Action = CS_WRITE;
+       reg.Value |= 0x04 | 0x01;
+       err = pcmcia_access_configuration_register(bus->host_pcmcia, &reg);
+       if (err != CS_SUCCESS)
+               goto error;
+
+       return 0;
+error:
+       return -ENODEV;
+}
diff --git a/drivers/ssb/scan.c b/drivers/ssb/scan.c
new file mode 100644 (file)
index 0000000..96258c6
--- /dev/null
@@ -0,0 +1,413 @@
+/*
+ * Sonics Silicon Backplane
+ * Bus scanning
+ *
+ * Copyright (C) 2005-2007 Michael Buesch <mb@bu3sch.de>
+ * Copyright (C) 2005 Martin Langer <martin-langer@gmx.de>
+ * Copyright (C) 2005 Stefano Brivio <st3@riseup.net>
+ * Copyright (C) 2005 Danny van Dyk <kugelfang@gentoo.org>
+ * Copyright (C) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
+ * 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/cs_types.h>
+#include <pcmcia/cs.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";
+       }
+       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_printk(KERN_ERR PFX
+                          "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_printk(KERN_ERR PFX
+                          "CHIPID not in nrcores fallback list\n");
+       }
+
+       return 1;
+}
+
+static u32 scan_read32(struct ssb_bus *bus, u8 current_coreidx,
+                      u16 offset)
+{
+       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);
+               break;
+       }
+       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);
+       }
+       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;
+       }
+       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;
+       }
+
+       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 == 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);
+                       pci_read_config_word(bus->host_pci, PCI_REVISION_ID,
+                                            &bus->chip_rev);
+                       bus->chip_package = 0;
+               } else {
+                       bus->chip_id = 0x4710;
+                       bus->chip_rev = 0;
+                       bus->chip_package = 0;
+               }
+       }
+       if (!bus->nr_devices)
+               bus->nr_devices = chipid_to_nrcores(bus->chip_id);
+       if (bus->nr_devices > ARRAY_SIZE(bus->devices)) {
+               ssb_printk(KERN_ERR PFX
+                          "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;
+
+               ssb_dprintk(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_dprintk(KERN_INFO PFX "Ignoring additional "
+                                                   "802.11 core\n");
+                                       continue;
+                               }
+                       }
+                       break;
+               case SSB_DEV_EXTIF:
+#ifdef CONFIG_SSB_DRIVER_EXTIF
+                       if (bus->extif.dev) {
+                               ssb_printk(KERN_WARNING PFX
+                                          "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_printk(KERN_WARNING PFX
+                                          "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_printk(KERN_WARNING PFX
+                                          "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->pcicore.dev) {
+                               ssb_printk(KERN_WARNING PFX
+                                          "WARNING: Multiple PCI(E) cores found\n");
+                               break;
+                       }
+                       bus->pcicore.dev = dev;
+#endif /* CONFIG_SSB_DRIVER_PCICORE */
+                       break;
+               default:
+                       break;
+               }
+
+               dev_i++;
+       }
+       bus->nr_devices = dev_i;
+
+       err = 0;
+out:
+       return err;
+err_unmap:
+       ssb_iounmap(bus);
+       goto out;
+}
diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h
new file mode 100644 (file)
index 0000000..a789364
--- /dev/null
@@ -0,0 +1,136 @@
+#ifndef LINUX_SSB_PRIVATE_H_
+#define LINUX_SSB_PRIVATE_H_
+
+#include <linux/ssb/ssb.h>
+#include <linux/types.h>
+
+
+#define PFX    "ssb: "
+
+#ifdef CONFIG_SSB_SILENT
+# define ssb_printk(fmt, x...) do { /* nothing */ } while (0)
+#else
+# define ssb_printk            printk
+#endif /* CONFIG_SSB_SILENT */
+
+/* dprintk: Debugging printk; vanishes for non-debug compilation */
+#ifdef CONFIG_SSB_DEBUG
+# define ssb_dprintk(fmt, x...)        ssb_printk(fmt , ##x)
+#else
+# define ssb_dprintk(fmt, x...)        do { /* nothing */ } while (0)
+#endif
+
+#ifdef CONFIG_SSB_DEBUG
+# define SSB_WARN_ON(x)                WARN_ON(x)
+# define SSB_BUG_ON(x)         BUG_ON(x)
+#else
+static inline int __ssb_do_nothing(int x) { return x; }
+# define SSB_WARN_ON(x)                __ssb_do_nothing(unlikely(!!(x)))
+# define SSB_BUG_ON(x)         __ssb_do_nothing(unlikely(!!(x)))
+#endif
+
+
+/* pci.c */
+#ifdef CONFIG_SSB_PCIHOST
+extern int ssb_pci_switch_core(struct ssb_bus *bus,
+                              struct ssb_device *dev);
+extern int ssb_pci_switch_coreidx(struct ssb_bus *bus,
+                                 u8 coreidx);
+extern int ssb_pci_xtal(struct ssb_bus *bus, u32 what,
+                       int turn_on);
+extern int ssb_pci_get_invariants(struct ssb_bus *bus,
+                                 struct ssb_init_invariants *iv);
+extern void ssb_pci_exit(struct ssb_bus *bus);
+extern int ssb_pci_init(struct ssb_bus *bus);
+extern const struct ssb_bus_ops ssb_pci_ops;
+
+#else /* CONFIG_SSB_PCIHOST */
+
+static inline int ssb_pci_switch_core(struct ssb_bus *bus,
+                                     struct ssb_device *dev)
+{
+       return 0;
+}
+static inline int ssb_pci_switch_coreidx(struct ssb_bus *bus,
+                                        u8 coreidx)
+{
+       return 0;
+}
+static inline int ssb_pci_xtal(struct ssb_bus *bus, u32 what,
+                              int turn_on)
+{
+       return 0;
+}
+static inline void ssb_pci_exit(struct ssb_bus *bus)
+{
+}
+static inline int ssb_pci_init(struct ssb_bus *bus)
+{
+       return 0;
+}
+#endif /* CONFIG_SSB_PCIHOST */
+
+
+/* pcmcia.c */
+#ifdef CONFIG_SSB_PCMCIAHOST
+extern int ssb_pcmcia_switch_core(struct ssb_bus *bus,
+                                 struct ssb_device *dev);
+extern int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus,
+                                    u8 coreidx);
+extern int ssb_pcmcia_switch_segment(struct ssb_bus *bus,
+                                    u8 seg);
+extern int ssb_pcmcia_get_invariants(struct ssb_bus *bus,
+                                    struct ssb_init_invariants *iv);
+extern int ssb_pcmcia_init(struct ssb_bus *bus);
+extern const struct ssb_bus_ops ssb_pcmcia_ops;
+#else /* CONFIG_SSB_PCMCIAHOST */
+static inline int ssb_pcmcia_switch_core(struct ssb_bus *bus,
+                                        struct ssb_device *dev)
+{
+       return 0;
+}
+static inline int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus,
+                                           u8 coreidx)
+{
+       return 0;
+}
+static inline int ssb_pcmcia_switch_segment(struct ssb_bus *bus,
+                                           u8 seg)
+{
+       return 0;
+}
+static inline int ssb_pcmcia_init(struct ssb_bus *bus)
+{
+       return 0;
+}
+#endif /* CONFIG_SSB_PCMCIAHOST */
+
+
+/* scan.c */
+extern const char *ssb_core_name(u16 coreid);
+extern int ssb_bus_scan(struct ssb_bus *bus,
+                       unsigned long baseaddr);
+extern void ssb_iounmap(struct ssb_bus *ssb);
+
+
+/* core.c */
+extern u32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m);
+extern int ssb_devices_freeze(struct ssb_bus *bus);
+extern int ssb_devices_thaw(struct ssb_bus *bus);
+extern struct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev);
+
+/* b43_pci_bridge.c */
+#ifdef CONFIG_SSB_PCIHOST
+extern int __init b43_pci_ssb_bridge_init(void);
+extern void __exit b43_pci_ssb_bridge_exit(void);
+#else /* CONFIG_SSB_PCIHOST */
+static inline int b43_pci_ssb_bridge_init(void)
+{
+       return 0;
+}
+static inline void b43_pci_ssb_bridge_exit(void)
+{
+}
+#endif /* CONFIG_SSB_PCIHOST */
+
+#endif /* LINUX_SSB_PRIVATE_H_ */
index 4dc5fa8..0c522e6 100644 (file)
@@ -340,4 +340,19 @@ struct parisc_device_id {
 #define PA_HVERSION_ANY_ID     0xffff
 #define PA_SVERSION_ANY_ID     0xffffffff
 
+/* SSB core, see drivers/ssb/ */
+struct ssb_device_id {
+       __u16   vendor;
+       __u16   coreid;
+       __u8    revision;
+};
+#define SSB_DEVICE(_vendor, _coreid, _revision)  \
+       { .vendor = _vendor, .coreid = _coreid, .revision = _revision, }
+#define SSB_DEVTABLE_END  \
+       { 0, },
+
+#define SSB_ANY_VENDOR         0xFFFF
+#define SSB_ANY_ID             0xFFFF
+#define SSB_ANY_REV            0xFF
+
 #endif /* LINUX_MOD_DEVICETABLE_H */
diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h
new file mode 100644 (file)
index 0000000..2b5c312
--- /dev/null
@@ -0,0 +1,424 @@
+#ifndef LINUX_SSB_H_
+#define LINUX_SSB_H_
+
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/pci.h>
+#include <linux/mod_devicetable.h>
+
+#include <linux/ssb/ssb_regs.h>
+
+
+struct pcmcia_device;
+struct ssb_bus;
+struct ssb_driver;
+
+
+struct ssb_sprom_r1 {
+       u16 pci_spid;           /* Subsystem Product ID for PCI */
+       u16 pci_svid;           /* Subsystem Vendor ID for PCI */
+       u16 pci_pid;            /* Product ID for PCI */
+       u8 il0mac[6];           /* MAC address for 802.11b/g */
+       u8 et0mac[6];           /* MAC address for Ethernet */
+       u8 et1mac[6];           /* MAC address for 802.11a */
+       u8 et0phyaddr:5;        /* MII address for enet0 */
+       u8 et1phyaddr:5;        /* MII address for enet1 */
+       u8 et0mdcport:1;        /* MDIO for enet0 */
+       u8 et1mdcport:1;        /* MDIO for enet1 */
+       u8 board_rev;           /* Board revision */
+       u8 country_code:4;      /* Country Code */
+       u8 antenna_a:2;         /* Antenna 0/1 available for A-PHY */
+       u8 antenna_bg:2;        /* Antenna 0/1 available for B-PHY and G-PHY */
+       u16 pa0b0;
+       u16 pa0b1;
+       u16 pa0b2;
+       u16 pa1b0;
+       u16 pa1b1;
+       u16 pa1b2;
+       u8 gpio0;               /* GPIO pin 0 */
+       u8 gpio1;               /* GPIO pin 1 */
+       u8 gpio2;               /* GPIO pin 2 */
+       u8 gpio3;               /* GPIO pin 3 */
+       u16 maxpwr_a;           /* A-PHY Power Amplifier Max Power (in dBm Q5.2) */
+       u16 maxpwr_bg;          /* B/G-PHY Power Amplifier Max Power (in dBm Q5.2) */
+       u8 itssi_a;             /* Idle TSSI Target for A-PHY */
+       u8 itssi_bg;            /* Idle TSSI Target for B/G-PHY */
+       u16 boardflags_lo;      /* Boardflags (low 16 bits) */
+       u8 antenna_gain_a;      /* A-PHY Antenna gain (in dBm Q5.2) */
+       u8 antenna_gain_bg;     /* B/G-PHY Antenna gain (in dBm Q5.2) */
+       u8 oem[8];              /* OEM string (rev 1 only) */
+};
+
+struct ssb_sprom_r2 {
+       u16 boardflags_hi;      /* Boardflags (high 16 bits) */
+       u8 maxpwr_a_lo;         /* A-PHY Max Power Low */
+       u8 maxpwr_a_hi;         /* A-PHY Max Power High */
+       u16 pa1lob0;            /* A-PHY PA Low Settings */
+       u16 pa1lob1;            /* A-PHY PA Low Settings */
+       u16 pa1lob2;            /* A-PHY PA Low Settings */
+       u16 pa1hib0;            /* A-PHY PA High Settings */
+       u16 pa1hib1;            /* A-PHY PA High Settings */
+       u16 pa1hib2;            /* A-PHY PA High Settings */
+       u8 ofdm_pwr_off;        /* OFDM Power Offset from CCK Level */
+       u8 country_str[2];      /* Two char Country Code */
+};
+
+struct ssb_sprom_r3 {
+       u32 ofdmapo;            /* A-PHY OFDM Mid Power Offset */
+       u32 ofdmalpo;           /* A-PHY OFDM Low Power Offset */
+       u32 ofdmahpo;           /* A-PHY OFDM High Power Offset */
+       u8 gpioldc_on_cnt;      /* GPIO LED Powersave Duty Cycle ON count */
+       u8 gpioldc_off_cnt;     /* GPIO LED Powersave Duty Cycle OFF count */
+       u8 cckpo_1M:4;          /* CCK Power Offset for Rate 1M */
+       u8 cckpo_2M:4;          /* CCK Power Offset for Rate 2M */
+       u8 cckpo_55M:4;         /* CCK Power Offset for Rate 5.5M */
+       u8 cckpo_11M:4;         /* CCK Power Offset for Rate 11M */
+       u32 ofdmgpo;            /* G-PHY OFDM Power Offset */
+};
+
+struct ssb_sprom_r4 {
+       /* TODO */
+};
+
+struct ssb_sprom {
+       u8 revision;
+       u8 crc;
+       /* The valid r# fields are selected by the "revision".
+        * Revision 3 and lower inherit from lower revisions.
+        */
+       union {
+               struct {
+                       struct ssb_sprom_r1 r1;
+                       struct ssb_sprom_r2 r2;
+                       struct ssb_sprom_r3 r3;
+               };
+               struct ssb_sprom_r4 r4;
+       };
+};
+
+/* Information about the PCB the circuitry is soldered on. */
+struct ssb_boardinfo {
+       u16 vendor;
+       u16 type;
+       u16 rev;
+};
+
+
+struct ssb_device;
+/* Lowlevel read/write operations on the device MMIO.
+ * Internal, don't use that outside of ssb. */
+struct ssb_bus_ops {
+       u16 (*read16)(struct ssb_device *dev, u16 offset);
+       u32 (*read32)(struct ssb_device *dev, u16 offset);
+       void (*write16)(struct ssb_device *dev, u16 offset, u16 value);
+       void (*write32)(struct ssb_device *dev, u16 offset, u32 value);
+};
+
+
+/* Core-ID values. */
+#define SSB_DEV_CHIPCOMMON     0x800
+#define SSB_DEV_ILINE20                0x801
+#define SSB_DEV_SDRAM          0x803
+#define SSB_DEV_PCI            0x804
+#define SSB_DEV_MIPS           0x805
+#define SSB_DEV_ETHERNET       0x806
+#define SSB_DEV_V90            0x807
+#define SSB_DEV_USB11_HOSTDEV  0x808
+#define SSB_DEV_ADSL           0x809
+#define SSB_DEV_ILINE100       0x80A
+#define SSB_DEV_IPSEC          0x80B
+#define SSB_DEV_PCMCIA         0x80D
+#define SSB_DEV_INTERNAL_MEM   0x80E
+#define SSB_DEV_MEMC_SDRAM     0x80F
+#define SSB_DEV_EXTIF          0x811
+#define SSB_DEV_80211          0x812
+#define SSB_DEV_MIPS_3302      0x816
+#define SSB_DEV_USB11_HOST     0x817
+#define SSB_DEV_USB11_DEV      0x818
+#define SSB_DEV_USB20_HOST     0x819
+#define SSB_DEV_USB20_DEV      0x81A
+#define SSB_DEV_SDIO_HOST      0x81B
+#define SSB_DEV_ROBOSWITCH     0x81C
+#define SSB_DEV_PARA_ATA       0x81D
+#define SSB_DEV_SATA_XORDMA    0x81E
+#define SSB_DEV_ETHERNET_GBIT  0x81F
+#define SSB_DEV_PCIE           0x820
+#define SSB_DEV_MIMO_PHY       0x821
+#define SSB_DEV_SRAM_CTRLR     0x822
+#define SSB_DEV_MINI_MACPHY    0x823
+#define SSB_DEV_ARM_1176       0x824
+#define SSB_DEV_ARM_7TDMI      0x825
+
+/* Vendor-ID values */
+#define SSB_VENDOR_BROADCOM    0x4243
+
+/* Some kernel subsystems poke with dev->drvdata, so we must use the
+ * following ugly workaround to get from struct device to struct ssb_device */
+struct __ssb_dev_wrapper {
+       struct device dev;
+       struct ssb_device *sdev;
+};
+
+struct ssb_device {
+       /* Having a copy of the ops pointer in each dev struct
+        * is an optimization. */
+       const struct ssb_bus_ops *ops;
+
+       struct device *dev;
+       struct ssb_bus *bus;
+       struct ssb_device_id id;
+
+       u8 core_index;
+       unsigned int irq;
+
+       /* Internal-only stuff follows. */
+       void *drvdata;          /* Per-device data */
+       void *devtypedata;      /* Per-devicetype (eg 802.11) data */
+};
+
+/* Go from struct device to struct ssb_device. */
+static inline
+struct ssb_device * dev_to_ssb_dev(struct device *dev)
+{
+       struct __ssb_dev_wrapper *wrap;
+       wrap = container_of(dev, struct __ssb_dev_wrapper, dev);
+       return wrap->sdev;
+}
+
+/* Device specific user data */
+static inline
+void ssb_set_drvdata(struct ssb_device *dev, void *data)
+{
+       dev->drvdata = data;
+}
+static inline
+void * ssb_get_drvdata(struct ssb_device *dev)
+{
+       return dev->drvdata;
+}
+
+/* Devicetype specific user data. This is per device-type (not per device) */
+void ssb_set_devtypedata(struct ssb_device *dev, void *data);
+static inline
+void * ssb_get_devtypedata(struct ssb_device *dev)
+{
+       return dev->devtypedata;
+}
+
+
+struct ssb_driver {
+       const char *name;
+       const struct ssb_device_id *id_table;
+
+       int (*probe)(struct ssb_device *dev, const struct ssb_device_id *id);
+       void (*remove)(struct ssb_device *dev);
+       int (*suspend)(struct ssb_device *dev, pm_message_t state);
+       int (*resume)(struct ssb_device *dev);
+       void (*shutdown)(struct ssb_device *dev);
+
+       struct device_driver drv;
+};
+#define drv_to_ssb_drv(_drv) container_of(_drv, struct ssb_driver, drv)
+
+extern int __ssb_driver_register(struct ssb_driver *drv, struct module *owner);
+static inline int ssb_driver_register(struct ssb_driver *drv)
+{
+       return __ssb_driver_register(drv, THIS_MODULE);
+}
+extern void ssb_driver_unregister(struct ssb_driver *drv);
+
+
+
+
+enum ssb_bustype {
+       SSB_BUSTYPE_SSB,        /* This SSB bus is the system bus */
+       SSB_BUSTYPE_PCI,        /* SSB is connected to PCI bus */
+       SSB_BUSTYPE_PCMCIA,     /* SSB is connected to PCMCIA bus */
+};
+
+/* board_vendor */
+#define SSB_BOARDVENDOR_BCM    0x14E4  /* Broadcom */
+#define SSB_BOARDVENDOR_DELL   0x1028  /* Dell */
+#define SSB_BOARDVENDOR_HP     0x0E11  /* HP */
+/* board_type */
+#define SSB_BOARD_BCM94306MP   0x0418
+#define SSB_BOARD_BCM4309G     0x0421
+#define SSB_BOARD_BCM4306CB    0x0417
+#define SSB_BOARD_BCM4309MP    0x040C
+#define SSB_BOARD_MP4318       0x044A
+#define SSB_BOARD_BU4306       0x0416
+#define SSB_BOARD_BU4309       0x040A
+/* chip_package */
+#define SSB_CHIPPACK_BCM4712S  1       /* Small 200pin 4712 */
+#define SSB_CHIPPACK_BCM4712M  2       /* Medium 225pin 4712 */
+#define SSB_CHIPPACK_BCM4712L  0       /* Large 340pin 4712 */
+
+#include <linux/ssb/ssb_driver_chipcommon.h>
+#include <linux/ssb/ssb_driver_mips.h>
+#include <linux/ssb/ssb_driver_extif.h>
+#include <linux/ssb/ssb_driver_pci.h>
+
+struct ssb_bus {
+       /* The MMIO area. */
+       void __iomem *mmio;
+
+       const struct ssb_bus_ops *ops;
+
+       /* The core in the basic address register window. (PCI bus only) */
+       struct ssb_device *mapped_device;
+       /* Currently mapped PCMCIA segment. (bustype == SSB_BUSTYPE_PCMCIA only) */
+       u8 mapped_pcmcia_seg;
+       /* Lock for core and segment switching. */
+       spinlock_t bar_lock;
+
+       /* The bus this backplane is running on. */
+       enum ssb_bustype bustype;
+       /* Pointer to the PCI bus (only valid if bustype == SSB_BUSTYPE_PCI). */
+       struct pci_dev *host_pci;
+       /* Pointer to the PCMCIA device (only if bustype == SSB_BUSTYPE_PCMCIA). */
+       struct pcmcia_device *host_pcmcia;
+
+#ifdef CONFIG_SSB_PCIHOST
+       /* Mutex to protect the SPROM writing. */
+       struct mutex pci_sprom_mutex;
+#endif
+
+       /* ID information about the Chip. */
+       u16 chip_id;
+       u16 chip_rev;
+       u8 chip_package;
+
+       /* List of devices (cores) on the backplane. */
+       struct ssb_device devices[SSB_MAX_NR_CORES];
+       u8 nr_devices;
+
+       /* Reference count. Number of suspended devices. */
+       u8 suspend_cnt;
+
+       /* Software ID number for this bus. */
+       unsigned int busnumber;
+
+       /* The ChipCommon device (if available). */
+       struct ssb_chipcommon chipco;
+       /* The PCI-core device (if available). */
+       struct ssb_pcicore pcicore;
+       /* The MIPS-core device (if available). */
+       struct ssb_mipscore mipscore;
+       /* The EXTif-core device (if available). */
+       struct ssb_extif extif;
+
+       /* The following structure elements are not available in early
+        * SSB initialization. Though, they are available for regular
+        * registered drivers at any stage. So be careful when
+        * using them in the ssb core code. */
+
+       /* ID information about the PCB. */
+       struct ssb_boardinfo boardinfo;
+       /* Contents of the SPROM. */
+       struct ssb_sprom sprom;
+
+       /* Internal-only stuff follows. Do not touch. */
+       struct list_head list;
+#ifdef CONFIG_SSB_DEBUG
+       /* Is the bus already powered up? */
+       bool powered_up;
+       int power_warn_count;
+#endif /* DEBUG */
+};
+
+/* The initialization-invariants. */
+struct ssb_init_invariants {
+       struct ssb_boardinfo boardinfo;
+       struct ssb_sprom sprom;
+};
+/* Type of function to fetch the invariants. */
+typedef int (*ssb_invariants_func_t)(struct ssb_bus *bus,
+                                    struct ssb_init_invariants *iv);
+
+/* Register a SSB system bus. get_invariants() is called after the
+ * basic system devices are initialized.
+ * The invariants are usually fetched from some NVRAM.
+ * Put the invariants into the struct pointed to by iv. */
+extern int ssb_bus_ssbbus_register(struct ssb_bus *bus,
+                                  unsigned long baseaddr,
+                                  ssb_invariants_func_t get_invariants);
+#ifdef CONFIG_SSB_PCIHOST
+extern int ssb_bus_pcibus_register(struct ssb_bus *bus,
+                                  struct pci_dev *host_pci);
+#endif /* CONFIG_SSB_PCIHOST */
+#ifdef CONFIG_SSB_PCMCIAHOST
+extern int ssb_bus_pcmciabus_register(struct ssb_bus *bus,
+                                     struct pcmcia_device *pcmcia_dev,
+                                     unsigned long baseaddr);
+#endif /* CONFIG_SSB_PCMCIAHOST */
+
+extern void ssb_bus_unregister(struct ssb_bus *bus);
+
+extern u32 ssb_clockspeed(struct ssb_bus *bus);
+
+/* Is the device enabled in hardware? */
+int ssb_device_is_enabled(struct ssb_device *dev);
+/* Enable a device and pass device-specific SSB_TMSLOW flags.
+ * If no device-specific flags are available, use 0. */
+void ssb_device_enable(struct ssb_device *dev, u32 core_specific_flags);
+/* Disable a device in hardware and pass SSB_TMSLOW flags (if any). */
+void ssb_device_disable(struct ssb_device *dev, u32 core_specific_flags);
+
+
+/* Device MMIO register read/write functions. */
+static inline u16 ssb_read16(struct ssb_device *dev, u16 offset)
+{
+       return dev->ops->read16(dev, offset);
+}
+static inline u32 ssb_read32(struct ssb_device *dev, u16 offset)
+{
+       return dev->ops->read32(dev, offset);
+}
+static inline void ssb_write16(struct ssb_device *dev, u16 offset, u16 value)
+{
+       dev->ops->write16(dev, offset, value);
+}
+static inline void ssb_write32(struct ssb_device *dev, u16 offset, u32 value)
+{
+       dev->ops->write32(dev, offset, value);
+}
+
+
+/* Translation (routing) bits that need to be ORed to DMA
+ * addresses before they are given to a device. */
+extern u32 ssb_dma_translation(struct ssb_device *dev);
+#define SSB_DMA_TRANSLATION_MASK       0xC0000000
+#define SSB_DMA_TRANSLATION_SHIFT      30
+
+extern int ssb_dma_set_mask(struct ssb_device *ssb_dev, u64 mask);
+
+
+#ifdef CONFIG_SSB_PCIHOST
+/* PCI-host wrapper driver */
+extern int ssb_pcihost_register(struct pci_driver *driver);
+static inline void ssb_pcihost_unregister(struct pci_driver *driver)
+{
+       pci_unregister_driver(driver);
+}
+#endif /* CONFIG_SSB_PCIHOST */
+
+
+/* If a driver is shutdown or suspended, call this to signal
+ * that the bus may be completely powered down. SSB will decide,
+ * if it's really time to power down the bus, based on if there
+ * are other devices that want to run. */
+extern int ssb_bus_may_powerdown(struct ssb_bus *bus);
+/* Before initializing and enabling a device, call this to power-up the bus.
+ * If you want to allow use of dynamic-power-control, pass the flag.
+ * Otherwise static always-on powercontrol will be used. */
+extern int ssb_bus_powerup(struct ssb_bus *bus, bool dynamic_pctl);
+
+
+/* Various helper functions */
+extern u32 ssb_admatch_base(u32 adm);
+extern u32 ssb_admatch_size(u32 adm);
+
+
+#endif /* LINUX_SSB_H_ */
diff --git a/include/linux/ssb/ssb_driver_chipcommon.h b/include/linux/ssb/ssb_driver_chipcommon.h
new file mode 100644 (file)
index 0000000..4cb9954
--- /dev/null
@@ -0,0 +1,396 @@
+#ifndef LINUX_SSB_CHIPCO_H_
+#define LINUX_SSB_CHIPCO_H_
+
+/* SonicsSiliconBackplane CHIPCOMMON core hardware definitions
+ *
+ * The chipcommon core provides chip identification, SB control,
+ * jtag, 0/1/2 uarts, clock frequency control, a watchdog interrupt timer,
+ * gpio interface, extbus, and support for serial and parallel flashes.
+ *
+ * Copyright 2005, Broadcom Corporation
+ * Copyright 2006, Michael Buesch <mb@bu3sch.de>
+ *
+ * Licensed under the GPL version 2. See COPYING for details.
+ */
+
+/** ChipCommon core registers. **/
+
+#define SSB_CHIPCO_CHIPID              0x0000
+#define  SSB_CHIPCO_IDMASK             0x0000FFFF
+#define  SSB_CHIPCO_REVMASK            0x000F0000
+#define  SSB_CHIPCO_REVSHIFT           16
+#define  SSB_CHIPCO_PACKMASK           0x00F00000
+#define  SSB_CHIPCO_PACKSHIFT          20
+#define  SSB_CHIPCO_NRCORESMASK                0x0F000000
+#define  SSB_CHIPCO_NRCORESSHIFT       24
+#define SSB_CHIPCO_CAP                 0x0004          /* Capabilities */
+#define  SSB_CHIPCO_CAP_NRUART         0x00000003      /* # of UARTs */
+#define  SSB_CHIPCO_CAP_MIPSEB         0x00000004      /* MIPS in BigEndian Mode */
+#define  SSB_CHIPCO_CAP_UARTCLK                0x00000018      /* UART clock select */
+#define   SSB_CHIPCO_CAP_UARTCLK_INT   0x00000008      /* UARTs are driven by internal divided clock */
+#define  SSB_CHIPCO_CAP_UARTGPIO       0x00000020      /* UARTs on GPIO 15-12 */
+#define  SSB_CHIPCO_CAP_EXTBUS         0x000000C0      /* External buses present */
+#define  SSB_CHIPCO_CAP_FLASHT         0x00000700      /* Flash Type */
+#define   SSB_CHIPCO_FLASHT_NONE       0x00000000      /* No flash */
+#define   SSB_CHIPCO_FLASHT_STSER      0x00000100      /* ST serial flash */
+#define   SSB_CHIPCO_FLASHT_ATSER      0x00000200      /* Atmel serial flash */
+#define          SSB_CHIPCO_FLASHT_PARA        0x00000700      /* Parallel flash */
+#define  SSB_CHIPCO_CAP_PLLT           0x00038000      /* PLL Type */
+#define   SSB_PLLTYPE_NONE             0x00000000
+#define   SSB_PLLTYPE_1                        0x00010000      /* 48Mhz base, 3 dividers */
+#define   SSB_PLLTYPE_2                        0x00020000      /* 48Mhz, 4 dividers */
+#define   SSB_PLLTYPE_3                        0x00030000      /* 25Mhz, 2 dividers */
+#define   SSB_PLLTYPE_4                        0x00008000      /* 48Mhz, 4 dividers */
+#define   SSB_PLLTYPE_5                        0x00018000      /* 25Mhz, 4 dividers */
+#define   SSB_PLLTYPE_6                        0x00028000      /* 100/200 or 120/240 only */
+#define   SSB_PLLTYPE_7                        0x00038000      /* 25Mhz, 4 dividers */
+#define  SSB_CHIPCO_CAP_PCTL           0x00040000      /* Power Control */
+#define  SSB_CHIPCO_CAP_OTPS           0x00380000      /* OTP size */
+#define  SSB_CHIPCO_CAP_OTPS_SHIFT     19
+#define  SSB_CHIPCO_CAP_OTPS_BASE      5
+#define  SSB_CHIPCO_CAP_JTAGM          0x00400000      /* JTAG master present */
+#define  SSB_CHIPCO_CAP_BROM           0x00800000      /* Internal boot ROM active */
+#define  SSB_CHIPCO_CAP_64BIT          0x08000000      /* 64-bit Backplane */
+#define SSB_CHIPCO_CORECTL             0x0008
+#define  SSB_CHIPCO_CORECTL_UARTCLK0   0x00000001      /* Drive UART with internal clock */
+#define         SSB_CHIPCO_CORECTL_SE          0x00000002      /* sync clk out enable (corerev >= 3) */
+#define SSB_CHIPCO_BIST                        0x000C
+#define SSB_CHIPCO_OTPS                        0x0010          /* OTP status */
+#define         SSB_CHIPCO_OTPS_PROGFAIL       0x80000000
+#define         SSB_CHIPCO_OTPS_PROTECT        0x00000007
+#define         SSB_CHIPCO_OTPS_HW_PROTECT     0x00000001
+#define         SSB_CHIPCO_OTPS_SW_PROTECT     0x00000002
+#define         SSB_CHIPCO_OTPS_CID_PROTECT    0x00000004
+#define SSB_CHIPCO_OTPC                        0x0014          /* OTP control */
+#define         SSB_CHIPCO_OTPC_RECWAIT        0xFF000000
+#define         SSB_CHIPCO_OTPC_PROGWAIT       0x00FFFF00
+#define         SSB_CHIPCO_OTPC_PRW_SHIFT      8
+#define         SSB_CHIPCO_OTPC_MAXFAIL        0x00000038
+#define         SSB_CHIPCO_OTPC_VSEL           0x00000006
+#define         SSB_CHIPCO_OTPC_SELVL          0x00000001
+#define SSB_CHIPCO_OTPP                        0x0018          /* OTP prog */
+#define         SSB_CHIPCO_OTPP_COL            0x000000FF
+#define         SSB_CHIPCO_OTPP_ROW            0x0000FF00
+#define         SSB_CHIPCO_OTPP_ROW_SHIFT      8
+#define         SSB_CHIPCO_OTPP_READERR        0x10000000
+#define         SSB_CHIPCO_OTPP_VALUE          0x20000000
+#define         SSB_CHIPCO_OTPP_READ           0x40000000
+#define         SSB_CHIPCO_OTPP_START          0x80000000
+#define         SSB_CHIPCO_OTPP_BUSY           0x80000000
+#define SSB_CHIPCO_IRQSTAT             0x0020
+#define SSB_CHIPCO_IRQMASK             0x0024
+#define         SSB_CHIPCO_IRQ_GPIO            0x00000001      /* gpio intr */
+#define         SSB_CHIPCO_IRQ_EXT             0x00000002      /* ro: ext intr pin (corerev >= 3) */
+#define         SSB_CHIPCO_IRQ_WDRESET         0x80000000      /* watchdog reset occurred */
+#define SSB_CHIPCO_CHIPCTL             0x0028          /* Rev >= 11 only */
+#define SSB_CHIPCO_CHIPSTAT            0x002C          /* Rev >= 11 only */
+#define SSB_CHIPCO_JCMD                        0x0030          /* Rev >= 10 only */
+#define  SSB_CHIPCO_JCMD_START         0x80000000
+#define  SSB_CHIPCO_JCMD_BUSY          0x80000000
+#define  SSB_CHIPCO_JCMD_PAUSE         0x40000000
+#define  SSB_CHIPCO_JCMD0_ACC_MASK     0x0000F000
+#define  SSB_CHIPCO_JCMD0_ACC_IRDR     0x00000000
+#define  SSB_CHIPCO_JCMD0_ACC_DR       0x00001000
+#define  SSB_CHIPCO_JCMD0_ACC_IR       0x00002000
+#define  SSB_CHIPCO_JCMD0_ACC_RESET    0x00003000
+#define  SSB_CHIPCO_JCMD0_ACC_IRPDR    0x00004000
+#define  SSB_CHIPCO_JCMD0_ACC_PDR      0x00005000
+#define  SSB_CHIPCO_JCMD0_IRW_MASK     0x00000F00
+#define  SSB_CHIPCO_JCMD_ACC_MASK      0x000F0000      /* Changes for corerev 11 */
+#define  SSB_CHIPCO_JCMD_ACC_IRDR      0x00000000
+#define  SSB_CHIPCO_JCMD_ACC_DR                0x00010000
+#define  SSB_CHIPCO_JCMD_ACC_IR                0x00020000
+#define  SSB_CHIPCO_JCMD_ACC_RESET     0x00030000
+#define  SSB_CHIPCO_JCMD_ACC_IRPDR     0x00040000
+#define  SSB_CHIPCO_JCMD_ACC_PDR       0x00050000
+#define  SSB_CHIPCO_JCMD_IRW_MASK      0x00001F00
+#define  SSB_CHIPCO_JCMD_IRW_SHIFT     8
+#define  SSB_CHIPCO_JCMD_DRW_MASK      0x0000003F
+#define SSB_CHIPCO_JIR                 0x0034          /* Rev >= 10 only */
+#define SSB_CHIPCO_JDR                 0x0038          /* Rev >= 10 only */
+#define SSB_CHIPCO_JCTL                        0x003C          /* Rev >= 10 only */
+#define  SSB_CHIPCO_JCTL_FORCE_CLK     4               /* Force clock */
+#define  SSB_CHIPCO_JCTL_EXT_EN                2               /* Enable external targets */
+#define  SSB_CHIPCO_JCTL_EN            1               /* Enable Jtag master */
+#define SSB_CHIPCO_FLASHCTL            0x0040
+#define  SSB_CHIPCO_FLASHCTL_START     0x80000000
+#define  SSB_CHIPCO_FLASHCTL_BUSY      SSB_CHIPCO_FLASHCTL_START
+#define SSB_CHIPCO_FLASHADDR           0x0044
+#define SSB_CHIPCO_FLASHDATA           0x0048
+#define SSB_CHIPCO_BCAST_ADDR          0x0050
+#define SSB_CHIPCO_BCAST_DATA          0x0054
+#define SSB_CHIPCO_GPIOIN              0x0060
+#define SSB_CHIPCO_GPIOOUT             0x0064
+#define SSB_CHIPCO_GPIOOUTEN           0x0068
+#define SSB_CHIPCO_GPIOCTL             0x006C
+#define SSB_CHIPCO_GPIOPOL             0x0070
+#define SSB_CHIPCO_GPIOIRQ             0x0074
+#define SSB_CHIPCO_WATCHDOG            0x0080
+#define SSB_CHIPCO_GPIOTIMER           0x0088          /* LED powersave (corerev >= 16) */
+#define  SSB_CHIPCO_GPIOTIMER_ONTIME_SHIFT     16
+#define SSB_CHIPCO_GPIOTOUTM           0x008C          /* LED powersave (corerev >= 16) */
+#define SSB_CHIPCO_CLOCK_N             0x0090
+#define SSB_CHIPCO_CLOCK_SB            0x0094
+#define SSB_CHIPCO_CLOCK_PCI           0x0098
+#define SSB_CHIPCO_CLOCK_M2            0x009C
+#define SSB_CHIPCO_CLOCK_MIPS          0x00A0
+#define SSB_CHIPCO_CLKDIV              0x00A4          /* Rev >= 3 only */
+#define         SSB_CHIPCO_CLKDIV_SFLASH       0x0F000000
+#define         SSB_CHIPCO_CLKDIV_SFLASH_SHIFT 24
+#define         SSB_CHIPCO_CLKDIV_OTP          0x000F0000
+#define         SSB_CHIPCO_CLKDIV_OTP_SHIFT    16
+#define         SSB_CHIPCO_CLKDIV_JTAG         0x00000F00
+#define         SSB_CHIPCO_CLKDIV_JTAG_SHIFT   8
+#define         SSB_CHIPCO_CLKDIV_UART         0x000000FF
+#define SSB_CHIPCO_PLLONDELAY          0x00B0          /* Rev >= 4 only */
+#define SSB_CHIPCO_FREFSELDELAY                0x00B4          /* Rev >= 4 only */
+#define SSB_CHIPCO_SLOWCLKCTL          0x00B8          /* 6 <= Rev <= 9 only */
+#define  SSB_CHIPCO_SLOWCLKCTL_SRC     0x00000007      /* slow clock source mask */
+#define          SSB_CHIPCO_SLOWCLKCTL_SRC_LPO         0x00000000      /* source of slow clock is LPO */
+#define   SSB_CHIPCO_SLOWCLKCTL_SRC_XTAL       0x00000001      /* source of slow clock is crystal */
+#define          SSB_CHIPCO_SLOECLKCTL_SRC_PCI         0x00000002      /* source of slow clock is PCI */
+#define  SSB_CHIPCO_SLOWCLKCTL_LPOFREQ 0x00000200      /* LPOFreqSel, 1: 160Khz, 0: 32KHz */
+#define  SSB_CHIPCO_SLOWCLKCTL_LPOPD   0x00000400      /* LPOPowerDown, 1: LPO is disabled, 0: LPO is enabled */
+#define  SSB_CHIPCO_SLOWCLKCTL_FSLOW   0x00000800      /* ForceSlowClk, 1: sb/cores running on slow clock, 0: power logic control */
+#define  SSB_CHIPCO_SLOWCLKCTL_IPLL    0x00001000      /* IgnorePllOffReq, 1/0: power logic ignores/honors PLL clock disable requests from core */
+#define  SSB_CHIPCO_SLOWCLKCTL_ENXTAL  0x00002000      /* XtalControlEn, 1/0: power logic does/doesn't disable crystal when appropriate */
+#define  SSB_CHIPCO_SLOWCLKCTL_XTALPU  0x00004000      /* XtalPU (RO), 1/0: crystal running/disabled */
+#define  SSB_CHIPCO_SLOWCLKCTL_CLKDIV  0xFFFF0000      /* ClockDivider (SlowClk = 1/(4+divisor)) */
+#define  SSB_CHIPCO_SLOWCLKCTL_CLKDIV_SHIFT    16
+#define SSB_CHIPCO_SYSCLKCTL           0x00C0          /* Rev >= 3 only */
+#define         SSB_CHIPCO_SYSCLKCTL_IDLPEN    0x00000001      /* ILPen: Enable Idle Low Power */
+#define         SSB_CHIPCO_SYSCLKCTL_ALPEN     0x00000002      /* ALPen: Enable Active Low Power */
+#define         SSB_CHIPCO_SYSCLKCTL_PLLEN     0x00000004      /* ForcePLLOn */
+#define         SSB_CHIPCO_SYSCLKCTL_FORCEALP  0x00000008      /* Force ALP (or HT if ALPen is not set */
+#define         SSB_CHIPCO_SYSCLKCTL_FORCEHT   0x00000010      /* Force HT */
+#define  SSB_CHIPCO_SYSCLKCTL_CLKDIV   0xFFFF0000      /* ClkDiv  (ILP = 1/(4+divisor)) */
+#define  SSB_CHIPCO_SYSCLKCTL_CLKDIV_SHIFT     16
+#define SSB_CHIPCO_CLKSTSTR            0x00C4          /* Rev >= 3 only */
+#define SSB_CHIPCO_PCMCIA_CFG          0x0100
+#define SSB_CHIPCO_PCMCIA_MEMWAIT      0x0104
+#define SSB_CHIPCO_PCMCIA_ATTRWAIT     0x0108
+#define SSB_CHIPCO_PCMCIA_IOWAIT       0x010C
+#define SSB_CHIPCO_IDE_CFG             0x0110
+#define SSB_CHIPCO_IDE_MEMWAIT         0x0114
+#define SSB_CHIPCO_IDE_ATTRWAIT                0x0118
+#define SSB_CHIPCO_IDE_IOWAIT          0x011C
+#define SSB_CHIPCO_PROG_CFG            0x0120
+#define SSB_CHIPCO_PROG_WAITCNT                0x0124
+#define SSB_CHIPCO_FLASH_CFG           0x0128
+#define SSB_CHIPCO_FLASH_WAITCNT       0x012C
+#define SSB_CHIPCO_UART0_DATA          0x0300
+#define SSB_CHIPCO_UART0_IMR           0x0304
+#define SSB_CHIPCO_UART0_FCR           0x0308
+#define SSB_CHIPCO_UART0_LCR           0x030C
+#define SSB_CHIPCO_UART0_MCR           0x0310
+#define SSB_CHIPCO_UART0_LSR           0x0314
+#define SSB_CHIPCO_UART0_MSR           0x0318
+#define SSB_CHIPCO_UART0_SCRATCH       0x031C
+#define SSB_CHIPCO_UART1_DATA          0x0400
+#define SSB_CHIPCO_UART1_IMR           0x0404
+#define SSB_CHIPCO_UART1_FCR           0x0408
+#define SSB_CHIPCO_UART1_LCR           0x040C
+#define SSB_CHIPCO_UART1_MCR           0x0410
+#define SSB_CHIPCO_UART1_LSR           0x0414
+#define SSB_CHIPCO_UART1_MSR           0x0418
+#define SSB_CHIPCO_UART1_SCRATCH       0x041C
+
+
+
+/** Clockcontrol masks and values **/
+
+/* SSB_CHIPCO_CLOCK_N */
+#define        SSB_CHIPCO_CLK_N1               0x0000003F      /* n1 control */
+#define        SSB_CHIPCO_CLK_N2               0x00003F00      /* n2 control */
+#define        SSB_CHIPCO_CLK_N2_SHIFT         8
+#define        SSB_CHIPCO_CLK_PLLC             0x000F0000      /* pll control */
+#define        SSB_CHIPCO_CLK_PLLC_SHIFT       16
+
+/* SSB_CHIPCO_CLOCK_SB/PCI/UART */
+#define        SSB_CHIPCO_CLK_M1               0x0000003F      /* m1 control */
+#define        SSB_CHIPCO_CLK_M2               0x00003F00      /* m2 control */
+#define        SSB_CHIPCO_CLK_M2_SHIFT         8
+#define        SSB_CHIPCO_CLK_M3               0x003F0000      /* m3 control */
+#define        SSB_CHIPCO_CLK_M3_SHIFT         16
+#define        SSB_CHIPCO_CLK_MC               0x1F000000      /* mux control */
+#define        SSB_CHIPCO_CLK_MC_SHIFT         24
+
+/* N3M Clock control magic field values */
+#define        SSB_CHIPCO_CLK_F6_2             0x02            /* A factor of 2 in */
+#define        SSB_CHIPCO_CLK_F6_3             0x03            /* 6-bit fields like */
+#define        SSB_CHIPCO_CLK_F6_4             0x05            /* N1, M1 or M3 */
+#define        SSB_CHIPCO_CLK_F6_5             0x09
+#define        SSB_CHIPCO_CLK_F6_6             0x11
+#define        SSB_CHIPCO_CLK_F6_7             0x21
+
+#define        SSB_CHIPCO_CLK_F5_BIAS          5               /* 5-bit fields get this added */
+
+#define        SSB_CHIPCO_CLK_MC_BYPASS        0x08
+#define        SSB_CHIPCO_CLK_MC_M1            0x04
+#define        SSB_CHIPCO_CLK_MC_M1M2          0x02
+#define        SSB_CHIPCO_CLK_MC_M1M2M3        0x01
+#define        SSB_CHIPCO_CLK_MC_M1M3          0x11
+
+/* Type 2 Clock control magic field values */
+#define        SSB_CHIPCO_CLK_T2_BIAS          2               /* n1, n2, m1 & m3 bias */
+#define        SSB_CHIPCO_CLK_T2M2_BIAS        3               /* m2 bias */
+
+#define        SSB_CHIPCO_CLK_T2MC_M1BYP       1
+#define        SSB_CHIPCO_CLK_T2MC_M2BYP       2
+#define        SSB_CHIPCO_CLK_T2MC_M3BYP       4
+
+/* Type 6 Clock control magic field values */
+#define        SSB_CHIPCO_CLK_T6_MMASK         1               /* bits of interest in m */
+#define        SSB_CHIPCO_CLK_T6_M0            120000000       /* sb clock for m = 0 */
+#define        SSB_CHIPCO_CLK_T6_M1            100000000       /* sb clock for m = 1 */
+#define        SSB_CHIPCO_CLK_SB2MIPS_T6(sb)   (2 * (sb))
+
+/* Common clock base */
+#define        SSB_CHIPCO_CLK_BASE1            24000000        /* Half the clock freq */
+#define SSB_CHIPCO_CLK_BASE2           12500000        /* Alternate crystal on some PLL's */
+
+/* Clock control values for 200Mhz in 5350 */
+#define        SSB_CHIPCO_CLK_5350_N           0x0311
+#define        SSB_CHIPCO_CLK_5350_M           0x04020009
+
+
+/** Bits in the config registers **/
+
+#define        SSB_CHIPCO_CFG_EN               0x0001          /* Enable */
+#define        SSB_CHIPCO_CFG_EXTM             0x000E          /* Extif Mode */
+#define         SSB_CHIPCO_CFG_EXTM_ASYNC      0x0002          /* Async/Parallel flash */
+#define         SSB_CHIPCO_CFG_EXTM_SYNC       0x0004          /* Synchronous */
+#define         SSB_CHIPCO_CFG_EXTM_PCMCIA     0x0008          /* PCMCIA */
+#define         SSB_CHIPCO_CFG_EXTM_IDE        0x000A          /* IDE */
+#define        SSB_CHIPCO_CFG_DS16             0x0010          /* Data size, 0=8bit, 1=16bit */
+#define        SSB_CHIPCO_CFG_CLKDIV           0x0060          /* Sync: Clock divisor */
+#define        SSB_CHIPCO_CFG_CLKEN            0x0080          /* Sync: Clock enable */
+#define        SSB_CHIPCO_CFG_BSTRO            0x0100          /* Sync: Size/Bytestrobe */
+
+
+/** Flash-specific control/status values */
+
+/* flashcontrol opcodes for ST flashes */
+#define SSB_CHIPCO_FLASHCTL_ST_WREN    0x0006          /* Write Enable */
+#define SSB_CHIPCO_FLASHCTL_ST_WRDIS   0x0004          /* Write Disable */
+#define SSB_CHIPCO_FLASHCTL_ST_RDSR    0x0105          /* Read Status Register */
+#define SSB_CHIPCO_FLASHCTL_ST_WRSR    0x0101          /* Write Status Register */
+#define SSB_CHIPCO_FLASHCTL_ST_READ    0x0303          /* Read Data Bytes */
+#define SSB_CHIPCO_FLASHCTL_ST_PP      0x0302          /* Page Program */
+#define SSB_CHIPCO_FLASHCTL_ST_SE      0x02D8          /* Sector Erase */
+#define SSB_CHIPCO_FLASHCTL_ST_BE      0x00C7          /* Bulk Erase */
+#define SSB_CHIPCO_FLASHCTL_ST_DP      0x00B9          /* Deep Power-down */
+#define SSB_CHIPCO_FLASHCTL_ST_RSIG    0x03AB          /* Read Electronic Signature */
+
+/* Status register bits for ST flashes */
+#define SSB_CHIPCO_FLASHSTA_ST_WIP     0x01            /* Write In Progress */
+#define SSB_CHIPCO_FLASHSTA_ST_WEL     0x02            /* Write Enable Latch */
+#define SSB_CHIPCO_FLASHSTA_ST_BP      0x1C            /* Block Protect */
+#define SSB_CHIPCO_FLASHSTA_ST_BP_SHIFT        2
+#define SSB_CHIPCO_FLASHSTA_ST_SRWD    0x80            /* Status Register Write Disable */
+
+/* flashcontrol opcodes for Atmel flashes */
+#define SSB_CHIPCO_FLASHCTL_AT_READ            0x07E8
+#define SSB_CHIPCO_FLASHCTL_AT_PAGE_READ       0x07D2
+#define SSB_CHIPCO_FLASHCTL_AT_BUF1_READ       /* FIXME */
+#define SSB_CHIPCO_FLASHCTL_AT_BUF2_READ       /* FIXME */
+#define SSB_CHIPCO_FLASHCTL_AT_STATUS          0x01D7
+#define SSB_CHIPCO_FLASHCTL_AT_BUF1_WRITE      0x0384
+#define SSB_CHIPCO_FLASHCTL_AT_BUF2_WRITE      0x0387
+#define SSB_CHIPCO_FLASHCTL_AT_BUF1_ERASE_PRGM 0x0283  /* Erase program */
+#define SSB_CHIPCO_FLASHCTL_AT_BUF2_ERASE_PRGM 0x0286  /* Erase program */
+#define SSB_CHIPCO_FLASHCTL_AT_BUF1_PROGRAM    0x0288
+#define SSB_CHIPCO_FLASHCTL_AT_BUF2_PROGRAM    0x0289
+#define SSB_CHIPCO_FLASHCTL_AT_PAGE_ERASE      0x0281
+#define SSB_CHIPCO_FLASHCTL_AT_BLOCK_ERASE     0x0250
+#define SSB_CHIPCO_FLASHCTL_AT_BUF1_WRER_PRGM  0x0382  /* Write erase program */
+#define SSB_CHIPCO_FLASHCTL_AT_BUF2_WRER_PRGM  0x0385  /* Write erase program */
+#define SSB_CHIPCO_FLASHCTL_AT_BUF1_LOAD       0x0253
+#define SSB_CHIPCO_FLASHCTL_AT_BUF2_LOAD       0x0255
+#define SSB_CHIPCO_FLASHCTL_AT_BUF1_COMPARE    0x0260
+#define SSB_CHIPCO_FLASHCTL_AT_BUF2_COMPARE    0x0261
+#define SSB_CHIPCO_FLASHCTL_AT_BUF1_REPROGRAM  0x0258
+#define SSB_CHIPCO_FLASHCTL_AT_BUF2_REPROGRAM  0x0259
+
+/* Status register bits for Atmel flashes */
+#define SSB_CHIPCO_FLASHSTA_AT_READY   0x80
+#define SSB_CHIPCO_FLASHSTA_AT_MISMATCH        0x40
+#define SSB_CHIPCO_FLASHSTA_AT_ID      0x38
+#define SSB_CHIPCO_FLASHSTA_AT_ID_SHIFT        3
+
+
+/** OTP **/
+
+/* OTP regions */
+#define        SSB_CHIPCO_OTP_HW_REGION        SSB_CHIPCO_OTPS_HW_PROTECT
+#define        SSB_CHIPCO_OTP_SW_REGION        SSB_CHIPCO_OTPS_SW_PROTECT
+#define        SSB_CHIPCO_OTP_CID_REGION       SSB_CHIPCO_OTPS_CID_PROTECT
+
+/* OTP regions (Byte offsets from otp size) */
+#define        SSB_CHIPCO_OTP_SWLIM_OFF        (-8)
+#define        SSB_CHIPCO_OTP_CIDBASE_OFF      0
+#define        SSB_CHIPCO_OTP_CIDLIM_OFF       8
+
+/* Predefined OTP words (Word offset from otp size) */
+#define        SSB_CHIPCO_OTP_BOUNDARY_OFF     (-4)
+#define        SSB_CHIPCO_OTP_HWSIGN_OFF       (-3)
+#define        SSB_CHIPCO_OTP_SWSIGN_OFF       (-2)
+#define        SSB_CHIPCO_OTP_CIDSIGN_OFF      (-1)
+
+#define        SSB_CHIPCO_OTP_CID_OFF          0
+#define        SSB_CHIPCO_OTP_PKG_OFF          1
+#define        SSB_CHIPCO_OTP_FID_OFF          2
+#define        SSB_CHIPCO_OTP_RSV_OFF          3
+#define        SSB_CHIPCO_OTP_LIM_OFF          4
+
+#define        SSB_CHIPCO_OTP_SIGNATURE        0x578A
+#define        SSB_CHIPCO_OTP_MAGIC            0x4E56
+
+
+struct ssb_device;
+struct ssb_serial_port;
+
+struct ssb_chipcommon {
+       struct ssb_device *dev;
+       u32 capabilities;
+       /* Fast Powerup Delay constant */
+       u16 fast_pwrup_delay;
+};
+
+extern void ssb_chipcommon_init(struct ssb_chipcommon *cc);
+
+#include <linux/pm.h>
+extern void ssb_chipco_suspend(struct ssb_chipcommon *cc, pm_message_t state);
+extern void ssb_chipco_resume(struct ssb_chipcommon *cc);
+
+extern void ssb_chipco_get_clockcpu(struct ssb_chipcommon *cc,
+                                    u32 *plltype, u32 *n, u32 *m);
+extern void ssb_chipco_get_clockcontrol(struct ssb_chipcommon *cc,
+                                       u32 *plltype, u32 *n, u32 *m);
+extern void ssb_chipco_timing_init(struct ssb_chipcommon *cc,
+                                  unsigned long ns_per_cycle);
+
+enum ssb_clkmode {
+       SSB_CLKMODE_SLOW,
+       SSB_CLKMODE_FAST,
+       SSB_CLKMODE_DYNAMIC,
+};
+
+extern void ssb_chipco_set_clockmode(struct ssb_chipcommon *cc,
+                                    enum ssb_clkmode mode);
+
+extern void ssb_chipco_watchdog_timer_set(struct ssb_chipcommon *cc,
+                                         u32 ticks);
+
+u32 ssb_chipco_gpio_in(struct ssb_chipcommon *cc, u32 mask);
+
+void ssb_chipco_gpio_out(struct ssb_chipcommon *cc, u32 mask, u32 value);
+
+void ssb_chipco_gpio_outen(struct ssb_chipcommon *cc, u32 mask, u32 value);
+
+#ifdef CONFIG_SSB_SERIAL
+extern int ssb_chipco_serial_init(struct ssb_chipcommon *cc,
+                                 struct ssb_serial_port *ports);
+#endif /* CONFIG_SSB_SERIAL */
+
+#endif /* LINUX_SSB_CHIPCO_H_ */
diff --git a/include/linux/ssb/ssb_driver_extif.h b/include/linux/ssb/ssb_driver_extif.h
new file mode 100644 (file)
index 0000000..a916435
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * Hardware-specific External Interface I/O core definitions
+ * for the BCM47xx family of SiliconBackplane-based chips.
+ *
+ * The External Interface core supports a total of three external chip selects
+ * supporting external interfaces. One of the external chip selects is
+ * used for Flash, one is used for PCMCIA, and the other may be
+ * programmed to support either a synchronous interface or an
+ * asynchronous interface. The asynchronous interface can be used to
+ * support external devices such as UARTs and the BCM2019 Bluetooth
+ * baseband processor.
+ * The external interface core also contains 2 on-chip 16550 UARTs, clock
+ * frequency control, a watchdog interrupt timer, and a GPIO interface.
+ *
+ * Copyright 2005, Broadcom Corporation
+ * Copyright 2006, Michael Buesch
+ *
+ * Licensed under the GPL version 2. See COPYING for details.
+ */
+#ifndef LINUX_SSB_EXTIFCORE_H_
+#define LINUX_SSB_EXTIFCORE_H_
+
+/* external interface address space */
+#define        SSB_EXTIF_PCMCIA_MEMBASE(x)     (x)
+#define        SSB_EXTIF_PCMCIA_IOBASE(x)      ((x) + 0x100000)
+#define        SSB_EXTIF_PCMCIA_CFGBASE(x)     ((x) + 0x200000)
+#define        SSB_EXTIF_CFGIF_BASE(x)         ((x) + 0x800000)
+#define        SSB_EXTIF_FLASH_BASE(x)         ((x) + 0xc00000)
+
+#define SSB_EXTIF_NR_GPIOOUT           5
+/* GPIO NOTE:
+ * The multiple instances of output and output enable registers
+ * are present to allow driver software for multiple cores to control
+ * gpio outputs without needing to share a single register pair.
+ * Use the following helper macro to get a register offset value.
+ */
+#define SSB_EXTIF_GPIO_OUT(index)      ({              \
+       BUILD_BUG_ON(index >= SSB_EXTIF_NR_GPIOOUT);    \
+       SSB_EXTIF_GPIO_OUT_BASE + ((index) * 8);        \
+                                       })
+#define SSB_EXTIF_GPIO_OUTEN(index)    ({              \
+       BUILD_BUG_ON(index >= SSB_EXTIF_NR_GPIOOUT);    \
+       SSB_EXTIF_GPIO_OUTEN_BASE + ((index) * 8);      \
+                                       })
+
+/** EXTIF core registers **/
+
+#define SSB_EXTIF_CTL                  0x0000
+#define  SSB_EXTIF_CTL_UARTEN          (1 << 0) /* UART enable */
+#define SSB_EXTIF_EXTSTAT              0x0004
+#define  SSB_EXTIF_EXTSTAT_EMODE       (1 << 0) /* Endian mode (ro) */
+#define  SSB_EXTIF_EXTSTAT_EIRQPIN     (1 << 1) /* External interrupt pin (ro) */
+#define  SSB_EXTIF_EXTSTAT_GPIOIRQPIN  (1 << 2) /* GPIO interrupt pin (ro) */
+#define SSB_EXTIF_PCMCIA_CFG           0x0010
+#define SSB_EXTIF_PCMCIA_MEMWAIT       0x0014
+#define SSB_EXTIF_PCMCIA_ATTRWAIT      0x0018
+#define SSB_EXTIF_PCMCIA_IOWAIT                0x001C
+#define SSB_EXTIF_PROG_CFG             0x0020
+#define SSB_EXTIF_PROG_WAITCNT         0x0024
+#define SSB_EXTIF_FLASH_CFG            0x0028
+#define SSB_EXTIF_FLASH_WAITCNT                0x002C
+#define SSB_EXTIF_WATCHDOG             0x0040
+#define SSB_EXTIF_CLOCK_N              0x0044
+#define SSB_EXTIF_CLOCK_SB             0x0048
+#define SSB_EXTIF_CLOCK_PCI            0x004C
+#define SSB_EXTIF_CLOCK_MII            0x0050
+#define SSB_EXTIF_GPIO_IN              0x0060
+#define SSB_EXTIF_GPIO_OUT_BASE                0x0064
+#define SSB_EXTIF_GPIO_OUTEN_BASE      0x0068
+#define SSB_EXTIF_EJTAG_OUTEN          0x0090
+#define SSB_EXTIF_GPIO_INTPOL          0x0094
+#define SSB_EXTIF_GPIO_INTMASK         0x0098
+#define SSB_EXTIF_UART_DATA            0x0300
+#define SSB_EXTIF_UART_TIMER           0x0310
+#define SSB_EXTIF_UART_FCR             0x0320
+#define SSB_EXTIF_UART_LCR             0x0330
+#define SSB_EXTIF_UART_MCR             0x0340
+#define SSB_EXTIF_UART_LSR             0x0350
+#define SSB_EXTIF_UART_MSR             0x0360
+#define SSB_EXTIF_UART_SCRATCH         0x0370
+
+
+
+
+/* pcmcia/prog/flash_config */
+#define        SSB_EXTCFG_EN                   (1 << 0)        /* enable */
+#define        SSB_EXTCFG_MODE                 0xE             /* mode */
+#define        SSB_EXTCFG_MODE_SHIFT           1
+#define         SSB_EXTCFG_MODE_FLASH          0x0             /* flash/asynchronous mode */
+#define         SSB_EXTCFG_MODE_SYNC           0x2             /* synchronous mode */
+#define         SSB_EXTCFG_MODE_PCMCIA         0x4             /* pcmcia mode */
+#define        SSB_EXTCFG_DS16                 (1 << 4)        /* destsize:  0=8bit, 1=16bit */
+#define        SSB_EXTCFG_BSWAP                (1 << 5)        /* byteswap */
+#define        SSB_EXTCFG_CLKDIV               0xC0            /* clock divider */
+#define        SSB_EXTCFG_CLKDIV_SHIFT         6
+#define         SSB_EXTCFG_CLKDIV_2            0x0             /* backplane/2 */
+#define         SSB_EXTCFG_CLKDIV_3            0x40            /* backplane/3 */
+#define         SSB_EXTCFG_CLKDIV_4            0x80            /* backplane/4 */
+#define        SSB_EXTCFG_CLKEN                (1 << 8)        /* clock enable */
+#define        SSB_EXTCFG_STROBE               (1 << 9)        /* size/bytestrobe (synch only) */
+
+/* pcmcia_memwait */
+#define        SSB_PCMCIA_MEMW_0               0x0000003F      /* waitcount0 */
+#define        SSB_PCMCIA_MEMW_1               0x00001F00      /* waitcount1 */
+#define        SSB_PCMCIA_MEMW_1_SHIFT         8
+#define        SSB_PCMCIA_MEMW_2               0x001F0000      /* waitcount2 */
+#define        SSB_PCMCIA_MEMW_2_SHIFT         16
+#define        SSB_PCMCIA_MEMW_3               0x1F000000      /* waitcount3 */
+#define        SSB_PCMCIA_MEMW_3_SHIFT         24
+
+/* pcmcia_attrwait */
+#define        SSB_PCMCIA_ATTW_0               0x0000003F      /* waitcount0 */
+#define        SSB_PCMCIA_ATTW_1               0x00001F00      /* waitcount1 */
+#define        SSB_PCMCIA_ATTW_1_SHIFT         8
+#define        SSB_PCMCIA_ATTW_2               0x001F0000      /* waitcount2 */
+#define        SSB_PCMCIA_ATTW_2_SHIFT         16
+#define        SSB_PCMCIA_ATTW_3               0x1F000000      /* waitcount3 */
+#define        SSB_PCMCIA_ATTW_3_SHIFT         24
+
+/* pcmcia_iowait */
+#define        SSB_PCMCIA_IOW_0                0x0000003F      /* waitcount0 */
+#define        SSB_PCMCIA_IOW_1                0x00001F00      /* waitcount1 */
+#define        SSB_PCMCIA_IOW_1_SHIFT          8
+#define        SSB_PCMCIA_IOW_2                0x001F0000      /* waitcount2 */
+#define        SSB_PCMCIA_IOW_2_SHIFT          16
+#define        SSB_PCMCIA_IOW_3                0x1F000000      /* waitcount3 */
+#define        SSB_PCMCIA_IOW_3_SHIFT          24
+
+/* prog_waitcount */
+#define        SSB_PROG_WCNT_0                 0x0000001F      /* waitcount0 */
+#define        SSB_PROG_WCNT_1                 0x00001F00      /* waitcount1 */
+#define        SSB_PROG_WCNT_1_SHIFT           8
+#define        SSB_PROG_WCNT_2                 0x001F0000      /* waitcount2 */
+#define        SSB_PROG_WCNT_2_SHIFT           16
+#define        SSB_PROG_WCNT_3                 0x1F000000      /* waitcount3 */
+#define        SSB_PROG_WCNT_3_SHIFT           24
+
+#define SSB_PROG_W0                    0x0000000C
+#define SSB_PROG_W1                    0x00000A00
+#define SSB_PROG_W2                    0x00020000
+#define SSB_PROG_W3                    0x01000000
+
+/* flash_waitcount */
+#define        SSB_FLASH_WCNT_0                0x0000001F      /* waitcount0 */
+#define        SSB_FLASH_WCNT_1                0x00001F00      /* waitcount1 */
+#define        SSB_FLASH_WCNT_1_SHIFT          8
+#define        SSB_FLASH_WCNT_2                0x001F0000      /* waitcount2 */
+#define        SSB_FLASH_WCNT_2_SHIFT          16
+#define        SSB_FLASH_WCNT_3                0x1F000000      /* waitcount3 */
+#define        SSB_FLASH_WCNT_3_SHIFT          24
+
+/* watchdog */
+#define SSB_EXTIF_WATCHDOG_CLK         48000000        /* Hz */
+
+
+
+#ifdef CONFIG_SSB_DRIVER_EXTIF
+
+struct ssb_extif {
+       struct ssb_device *dev;
+};
+
+static inline bool ssb_extif_available(struct ssb_extif *extif)
+{
+       return (extif->dev != NULL);
+}
+
+extern void ssb_extif_get_clockcontrol(struct ssb_extif *extif,
+                                      u32 *plltype, u32 *n, u32 *m);
+
+extern void ssb_extif_timing_init(struct ssb_extif *extif,
+                                 unsigned long ns);
+
+u32 ssb_extif_gpio_in(struct ssb_extif *extif, u32 mask);
+
+void ssb_extif_gpio_out(struct ssb_extif *extif, u32 mask, u32 value);
+
+void ssb_extif_gpio_outen(struct ssb_extif *extif, u32 mask, u32 value);
+
+#ifdef CONFIG_SSB_SERIAL
+extern int ssb_extif_serial_init(struct ssb_extif *extif,
+                                struct ssb_serial_port *ports);
+#endif /* CONFIG_SSB_SERIAL */
+
+
+#else /* CONFIG_SSB_DRIVER_EXTIF */
+/* extif disabled */
+
+struct ssb_extif {
+};
+
+static inline bool ssb_extif_available(struct ssb_extif *extif)
+{
+       return 0;
+}
+
+static inline
+void ssb_extif_get_clockcontrol(struct ssb_extif *extif,
+                               u32 *plltype, u32 *n, u32 *m)
+{
+}
+
+#endif /* CONFIG_SSB_DRIVER_EXTIF */
+#endif /* LINUX_SSB_EXTIFCORE_H_ */
diff --git a/include/linux/ssb/ssb_driver_mips.h b/include/linux/ssb/ssb_driver_mips.h
new file mode 100644 (file)
index 0000000..5f44e97
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef LINUX_SSB_MIPSCORE_H_
+#define LINUX_SSB_MIPSCORE_H_
+
+#ifdef CONFIG_SSB_DRIVER_MIPS
+
+struct ssb_device;
+
+struct ssb_serial_port {
+       void *regs;
+       unsigned long clockspeed;
+       unsigned int irq;
+       unsigned int baud_base;
+       unsigned int reg_shift;
+};
+
+
+struct ssb_mipscore {
+       struct ssb_device *dev;
+
+       int nr_serial_ports;
+       struct ssb_serial_port serial_ports[4];
+
+       u8 flash_buswidth;
+       u32 flash_window;
+       u32 flash_window_size;
+};
+
+extern void ssb_mipscore_init(struct ssb_mipscore *mcore);
+extern u32 ssb_cpu_clock(struct ssb_mipscore *mcore);
+
+extern unsigned int ssb_mips_irq(struct ssb_device *dev);
+
+
+#else /* CONFIG_SSB_DRIVER_MIPS */
+
+struct ssb_mipscore {
+};
+
+static inline
+void ssb_mipscore_init(struct ssb_mipscore *mcore)
+{
+}
+
+#endif /* CONFIG_SSB_DRIVER_MIPS */
+
+#endif /* LINUX_SSB_MIPSCORE_H_ */
diff --git a/include/linux/ssb/ssb_driver_pci.h b/include/linux/ssb/ssb_driver_pci.h
new file mode 100644 (file)
index 0000000..9cfffb7
--- /dev/null
@@ -0,0 +1,106 @@
+#ifndef LINUX_SSB_PCICORE_H_
+#define LINUX_SSB_PCICORE_H_
+
+#ifdef CONFIG_SSB_DRIVER_PCICORE
+
+/* PCI core registers. */
+#define SSB_PCICORE_CTL                        0x0000  /* PCI Control */
+#define  SSB_PCICORE_CTL_RST_OE                0x00000001 /* PCI_RESET Output Enable */
+#define  SSB_PCICORE_CTL_RST           0x00000002 /* PCI_RESET driven out to pin */
+#define  SSB_PCICORE_CTL_CLK_OE                0x00000004 /* Clock gate Output Enable */
+#define  SSB_PCICORE_CTL_CLK           0x00000008 /* Gate for clock driven out to pin */
+#define SSB_PCICORE_ARBCTL             0x0010  /* PCI Arbiter Control */
+#define  SSB_PCICORE_ARBCTL_INTERN     0x00000001 /* Use internal arbiter */
+#define  SSB_PCICORE_ARBCTL_EXTERN     0x00000002 /* Use external arbiter */
+#define  SSB_PCICORE_ARBCTL_PARKID     0x00000006 /* Mask, selects which agent is parked on an idle bus */
+#define   SSB_PCICORE_ARBCTL_PARKID_LAST       0x00000000 /* Last requestor */
+#define   SSB_PCICORE_ARBCTL_PARKID_4710       0x00000002 /* 4710 */
+#define   SSB_PCICORE_ARBCTL_PARKID_EXT0       0x00000004 /* External requestor 0 */
+#define   SSB_PCICORE_ARBCTL_PARKID_EXT1       0x00000006 /* External requestor 1 */
+#define SSB_PCICORE_ISTAT              0x0020  /* Interrupt status */
+#define  SSB_PCICORE_ISTAT_INTA                0x00000001 /* PCI INTA# */
+#define  SSB_PCICORE_ISTAT_INTB                0x00000002 /* PCI INTB# */
+#define  SSB_PCICORE_ISTAT_SERR                0x00000004 /* PCI SERR# (write to clear) */
+#define  SSB_PCICORE_ISTAT_PERR                0x00000008 /* PCI PERR# (write to clear) */
+#define  SSB_PCICORE_ISTAT_PME         0x00000010 /* PCI PME# */
+#define SSB_PCICORE_IMASK              0x0024  /* Interrupt mask */
+#define  SSB_PCICORE_IMASK_INTA                0x00000001 /* PCI INTA# */
+#define  SSB_PCICORE_IMASK_INTB                0x00000002 /* PCI INTB# */
+#define  SSB_PCICORE_IMASK_SERR                0x00000004 /* PCI SERR# */
+#define  SSB_PCICORE_IMASK_PERR                0x00000008 /* PCI PERR# */
+#define  SSB_PCICORE_IMASK_PME         0x00000010 /* PCI PME# */
+#define SSB_PCICORE_MBOX               0x0028  /* Backplane to PCI Mailbox */
+#define  SSB_PCICORE_MBOX_F0_0         0x00000100 /* PCI function 0, INT 0 */
+#define  SSB_PCICORE_MBOX_F0_1         0x00000200 /* PCI function 0, INT 1 */
+#define  SSB_PCICORE_MBOX_F1_0         0x00000400 /* PCI function 1, INT 0 */
+#define  SSB_PCICORE_MBOX_F1_1         0x00000800 /* PCI function 1, INT 1 */
+#define  SSB_PCICORE_MBOX_F2_0         0x00001000 /* PCI function 2, INT 0 */
+#define  SSB_PCICORE_MBOX_F2_1         0x00002000 /* PCI function 2, INT 1 */
+#define  SSB_PCICORE_MBOX_F3_0         0x00004000 /* PCI function 3, INT 0 */
+#define  SSB_PCICORE_MBOX_F3_1         0x00008000 /* PCI function 3, INT 1 */
+#define SSB_PCICORE_BCAST_ADDR         0x0050  /* Backplane Broadcast Address */
+#define  SSB_PCICORE_BCAST_ADDR_MASK   0x000000FF
+#define SSB_PCICORE_BCAST_DATA         0x0054  /* Backplane Broadcast Data */
+#define SSB_PCICORE_GPIO_IN            0x0060  /* rev >= 2 only */
+#define SSB_PCICORE_GPIO_OUT           0x0064  /* rev >= 2 only */
+#define SSB_PCICORE_GPIO_ENABLE                0x0068  /* rev >= 2 only */
+#define SSB_PCICORE_GPIO_CTL           0x006C  /* rev >= 2 only */
+#define SSB_PCICORE_SBTOPCI0           0x0100  /* Backplane to PCI translation 0 (sbtopci0) */
+#define  SSB_PCICORE_SBTOPCI0_MASK     0xFC000000
+#define SSB_PCICORE_SBTOPCI1           0x0104  /* Backplane to PCI translation 1 (sbtopci1) */
+#define  SSB_PCICORE_SBTOPCI1_MASK     0xFC000000
+#define SSB_PCICORE_SBTOPCI2           0x0108  /* Backplane to PCI translation 2 (sbtopci2) */
+#define  SSB_PCICORE_SBTOPCI2_MASK     0xC0000000
+
+/* SBtoPCIx */
+#define SSB_PCICORE_SBTOPCI_MEM                0x00000000
+#define SSB_PCICORE_SBTOPCI_IO         0x00000001
+#define SSB_PCICORE_SBTOPCI_CFG0       0x00000002
+#define SSB_PCICORE_SBTOPCI_CFG1       0x00000003
+#define SSB_PCICORE_SBTOPCI_PREF       0x00000004 /* Prefetch enable */
+#define SSB_PCICORE_SBTOPCI_BURST      0x00000008 /* Burst enable */
+#define SSB_PCICORE_SBTOPCI_MRM                0x00000020 /* Memory Read Multiple */
+#define SSB_PCICORE_SBTOPCI_RC         0x00000030 /* Read Command mask (rev >= 11) */
+#define  SSB_PCICORE_SBTOPCI_RC_READ   0x00000000 /* Memory read */
+#define  SSB_PCICORE_SBTOPCI_RC_READL  0x00000010 /* Memory read line */
+#define  SSB_PCICORE_SBTOPCI_RC_READM  0x00000020 /* Memory read multiple */
+
+
+/* PCIcore specific boardflags */
+#define SSB_PCICORE_BFL_NOPCI          0x00000400 /* Board leaves PCI floating */
+
+
+struct ssb_pcicore {
+       struct ssb_device *dev;
+       u8 setup_done:1;
+       u8 hostmode:1;
+       u8 cardbusmode:1;
+};
+
+extern void ssb_pcicore_init(struct ssb_pcicore *pc);
+
+/* Enable IRQ routing for a specific device */
+extern int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc,
+                                         struct ssb_device *dev);
+
+
+#else /* CONFIG_SSB_DRIVER_PCICORE */
+
+
+struct ssb_pcicore {
+};
+
+static inline
+void ssb_pcicore_init(struct ssb_pcicore *pc)
+{
+}
+
+static inline
+int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc,
+                                  struct ssb_device *dev)
+{
+       return 0;
+}
+
+#endif /* CONFIG_SSB_DRIVER_PCICORE */
+#endif /* LINUX_SSB_PCICORE_H_ */
diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h
new file mode 100644 (file)
index 0000000..47c7c71
--- /dev/null
@@ -0,0 +1,292 @@
+#ifndef LINUX_SSB_REGS_H_
+#define LINUX_SSB_REGS_H_
+
+
+/* SiliconBackplane Address Map.
+ * All regions may not exist on all chips.
+ */
+#define SSB_SDRAM_BASE         0x00000000U     /* Physical SDRAM */
+#define SSB_PCI_MEM            0x08000000U     /* Host Mode sb2pcitranslation0 (64 MB) */
+#define SSB_PCI_CFG            0x0c000000U     /* Host Mode sb2pcitranslation1 (64 MB) */
+#define        SSB_SDRAM_SWAPPED       0x10000000U     /* Byteswapped Physical SDRAM */
+#define SSB_ENUM_BASE          0x18000000U     /* Enumeration space base */
+#define        SSB_ENUM_LIMIT          0x18010000U     /* Enumeration space limit */
+
+#define        SSB_FLASH2              0x1c000000U     /* Flash Region 2 (region 1 shadowed here) */
+#define        SSB_FLASH2_SZ           0x02000000U     /* Size of Flash Region 2 */
+
+#define        SSB_EXTIF_BASE          0x1f000000U     /* External Interface region base address */
+#define        SSB_FLASH1              0x1fc00000U     /* Flash Region 1 */
+#define        SSB_FLASH1_SZ           0x00400000U     /* Size of Flash Region 1 */
+
+#define SSB_PCI_DMA            0x40000000U     /* Client Mode sb2pcitranslation2 (1 GB) */
+#define SSB_PCI_DMA_SZ         0x40000000U     /* Client Mode sb2pcitranslation2 size in bytes */
+#define SSB_PCIE_DMA_L32       0x00000000U     /* PCIE Client Mode sb2pcitranslation2 (2 ZettaBytes), low 32 bits */
+#define SSB_PCIE_DMA_H32       0x80000000U     /* PCIE Client Mode sb2pcitranslation2 (2 ZettaBytes), high 32 bits */
+#define        SSB_EUART               (SSB_EXTIF_BASE + 0x00800000)
+#define        SSB_LED                 (SSB_EXTIF_BASE + 0x00900000)
+
+
+/* Enumeration space constants */
+#define SSB_CORE_SIZE          0x1000  /* Size of a core MMIO area */
+#define SSB_MAX_NR_CORES       ((SSB_ENUM_LIMIT - SSB_ENUM_BASE) / SSB_CORE_SIZE)
+
+
+/* mips address */
+#define        SSB_EJTAG               0xff200000      /* MIPS EJTAG space (2M) */
+
+
+/* SSB PCI config space registers. */
+#define SSB_PMCSR              0x44
+#define  SSB_PE                        0x100
+#define        SSB_BAR0_WIN            0x80    /* Backplane address space 0 */
+#define        SSB_BAR1_WIN            0x84    /* Backplane address space 1 */
+#define        SSB_SPROMCTL            0x88    /* SPROM control */
+#define  SSB_SPROMCTL_WE       0x10    /* SPROM write enable */
+#define        SSB_BAR1_CONTROL        0x8c    /* Address space 1 burst control */
+#define SSB_PCI_IRQS           0x90    /* PCI interrupts */
+#define SSB_PCI_IRQMASK                0x94    /* PCI IRQ control and mask (pcirev >= 6 only) */
+#define SSB_BACKPLANE_IRQS     0x98    /* Backplane Interrupts */
+#define SSB_GPIO_IN            0xB0    /* GPIO Input (pcirev >= 3 only) */
+#define SSB_GPIO_OUT           0xB4    /* GPIO Output (pcirev >= 3 only) */
+#define SSB_GPIO_OUT_ENABLE    0xB8    /* GPIO Output Enable/Disable (pcirev >= 3 only) */
+#define  SSB_GPIO_SCS          0x10    /* PCI config space bit 4 for 4306c0 slow clock source */
+#define  SSB_GPIO_HWRAD                0x20    /* PCI config space GPIO 13 for hw radio disable */
+#define  SSB_GPIO_XTAL         0x40    /* PCI config space GPIO 14 for Xtal powerup */
+#define  SSB_GPIO_PLL          0x80    /* PCI config space GPIO 15 for PLL powerdown */
+
+
+#define SSB_BAR0_MAX_RETRIES   50
+
+/* Silicon backplane configuration register definitions */
+#define SSB_IPSFLAG            0x0F08
+#define         SSB_IPSFLAG_IRQ1       0x0000003F /* which sbflags get routed to mips interrupt 1 */
+#define         SSB_IPSFLAG_IRQ1_SHIFT 0
+#define         SSB_IPSFLAG_IRQ2       0x00003F00 /* which sbflags get routed to mips interrupt 2 */
+#define         SSB_IPSFLAG_IRQ2_SHIFT 8
+#define         SSB_IPSFLAG_IRQ3       0x003F0000 /* which sbflags get routed to mips interrupt 3 */
+#define         SSB_IPSFLAG_IRQ3_SHIFT 16
+#define         SSB_IPSFLAG_IRQ4       0x3F000000 /* which sbflags get routed to mips interrupt 4 */
+#define         SSB_IPSFLAG_IRQ4_SHIFT 24
+#define SSB_TPSFLAG            0x0F18
+#define  SSB_TPSFLAG_BPFLAG    0x0000003F /* Backplane flag # */
+#define  SSB_TPSFLAG_ALWAYSIRQ 0x00000040 /* IRQ is always sent on the Backplane */
+#define SSB_TMERRLOGA          0x0F48
+#define SSB_TMERRLOG           0x0F50
+#define SSB_ADMATCH3           0x0F60
+#define SSB_ADMATCH2           0x0F68
+#define SSB_ADMATCH1           0x0F70
+#define SSB_IMSTATE            0x0F90     /* SB Initiator Agent State */
+#define  SSB_IMSTATE_PC                0x0000000f /* Pipe Count */
+#define  SSB_IMSTATE_AP_MASK   0x00000030 /* Arbitration Priority */
+#define  SSB_IMSTATE_AP_BOTH   0x00000000 /* Use both timeslices and token */
+#define  SSB_IMSTATE_AP_TS     0x00000010 /* Use timeslices only */
+#define  SSB_IMSTATE_AP_TK     0x00000020 /* Use token only */
+#define  SSB_IMSTATE_AP_RSV    0x00000030 /* Reserved */
+#define  SSB_IMSTATE_IBE       0x00020000 /* In Band Error */
+#define  SSB_IMSTATE_TO                0x00040000 /* Timeout */
+#define SSB_INTVEC             0x0F94     /* SB Interrupt Mask */
+#define  SSB_INTVEC_PCI                0x00000001 /* Enable interrupts for PCI */
+#define  SSB_INTVEC_ENET0      0x00000002 /* Enable interrupts for enet 0 */
+#define  SSB_INTVEC_ILINE20    0x00000004 /* Enable interrupts for iline20 */
+#define  SSB_INTVEC_CODEC      0x00000008 /* Enable interrupts for v90 codec */
+#define  SSB_INTVEC_USB                0x00000010 /* Enable interrupts for usb */
+#define  SSB_INTVEC_EXTIF      0x00000020 /* Enable interrupts for external i/f */
+#define  SSB_INTVEC_ENET1      0x00000040 /* Enable interrupts for enet 1 */
+#define SSB_TMSLOW             0x0F98     /* SB Target State Low */
+#define  SSB_TMSLOW_RESET      0x00000001 /* Reset */
+#define  SSB_TMSLOW_REJECT_22  0x00000002 /* Reject (Backplane rev 2.2) */
+#define  SSB_TMSLOW_REJECT_23  0x00000004 /* Reject (Backplane rev 2.3) */
+#define  SSB_TMSLOW_CLOCK      0x00010000 /* Clock Enable */
+#define  SSB_TMSLOW_FGC                0x00020000 /* Force Gated Clocks On */
+#define  SSB_TMSLOW_PE         0x40000000 /* Power Management Enable */
+#define  SSB_TMSLOW_BE         0x80000000 /* BIST Enable */
+#define SSB_TMSHIGH            0x0F9C     /* SB Target State High */
+#define  SSB_TMSHIGH_SERR      0x00000001 /* S-error */
+#define  SSB_TMSHIGH_INT       0x00000002 /* Interrupt */
+#define  SSB_TMSHIGH_BUSY      0x00000004 /* Busy */
+#define  SSB_TMSHIGH_TO                0x00000020 /* Timeout. Backplane rev >= 2.3 only */
+#define  SSB_TMSHIGH_COREFL    0x1FFF0000 /* Core specific flags */
+#define  SSB_TMSHIGH_COREFL_SHIFT      16
+#define  SSB_TMSHIGH_DMA64     0x10000000 /* 64bit DMA supported */
+#define  SSB_TMSHIGH_GCR       0x20000000 /* Gated Clock Request */
+#define  SSB_TMSHIGH_BISTF     0x40000000 /* BIST Failed */
+#define  SSB_TMSHIGH_BISTD     0x80000000 /* BIST Done */
+#define SSB_BWA0               0x0FA0
+#define SSB_IMCFGLO            0x0FA8
+#define  SSB_IMCFGLO_SERTO     0x00000007 /* Service timeout */
+#define  SSB_IMCFGLO_REQTO     0x00000070 /* Request timeout */
+#define  SSB_IMCFGLO_REQTO_SHIFT       4
+#define  SSB_IMCFGLO_CONNID    0x00FF0000 /* Connection ID */
+#define  SSB_IMCFGLO_CONNID_SHIFT      16
+#define SSB_IMCFGHI            0x0FAC
+#define SSB_ADMATCH0           0x0FB0
+#define SSB_TMCFGLO            0x0FB8
+#define SSB_TMCFGHI            0x0FBC
+#define SSB_BCONFIG            0x0FC0
+#define SSB_BSTATE             0x0FC8
+#define SSB_ACTCFG             0x0FD8
+#define SSB_FLAGST             0x0FE8
+#define SSB_IDLOW              0x0FF8
+#define  SSB_IDLOW_CFGSP       0x00000003 /* Config Space */
+#define  SSB_IDLOW_ADDRNGE     0x00000038 /* Address Ranges supported */
+#define  SSB_IDLOW_ADDRNGE_SHIFT       3
+#define  SSB_IDLOW_SYNC                0x00000040
+#define  SSB_IDLOW_INITIATOR   0x00000080
+#define  SSB_IDLOW_MIBL                0x00000F00 /* Minimum Backplane latency */
+#define  SSB_IDLOW_MIBL_SHIFT  8
+#define  SSB_IDLOW_MABL                0x0000F000 /* Maximum Backplane latency */
+#define  SSB_IDLOW_MABL_SHIFT  12
+#define  SSB_IDLOW_TIF         0x00010000 /* This Initiator is first */
+#define  SSB_IDLOW_CCW         0x000C0000 /* Cycle counter width */
+#define  SSB_IDLOW_CCW_SHIFT   18
+#define  SSB_IDLOW_TPT         0x00F00000 /* Target ports */
+#define  SSB_IDLOW_TPT_SHIFT   20
+#define  SSB_IDLOW_INITP       0x0F000000 /* Initiator ports */
+#define  SSB_IDLOW_INITP_SHIFT 24
+#define  SSB_IDLOW_SSBREV      0xF0000000 /* Sonics Backplane Revision code */
+#define  SSB_IDLOW_SSBREV_22   0x00000000 /* <= 2.2 */
+#define  SSB_IDLOW_SSBREV_23   0x10000000 /* 2.3 */
+#define SSB_IDHIGH             0x0FFC     /* SB Identification High */
+#define  SSB_IDHIGH_RCLO       0x0000000F /* Revision Code (low part) */
+#define  SSB_IDHIGH_CC         0x00008FF0 /* Core Code */
+#define  SSB_IDHIGH_CC_SHIFT   4
+#define  SSB_IDHIGH_RCHI       0x00007000 /* Revision Code (high part) */
+#define  SSB_IDHIGH_RCHI_SHIFT 8          /* yes, shift 8 is right */
+#define  SSB_IDHIGH_VC         0xFFFF0000 /* Vendor Code */
+#define  SSB_IDHIGH_VC_SHIFT   16
+
+/* SPROM shadow area. If not otherwise noted, fields are
+ * two bytes wide. Note that the SPROM can _only_ be read
+ * in two-byte quantinies.
+ */
+#define SSB_SPROMSIZE_WORDS            64
+#define SSB_SPROMSIZE_BYTES            (SSB_SPROMSIZE_WORDS * sizeof(u16))
+#define SSB_SPROM_BASE                 0x1000
+#define SSB_SPROM_REVISION             0x107E
+#define  SSB_SPROM_REVISION_REV                0x00FF  /* SPROM Revision number */
+#define  SSB_SPROM_REVISION_CRC                0xFF00  /* SPROM CRC8 value */
+#define  SSB_SPROM_REVISION_CRC_SHIFT  8
+/* SPROM Revision 1 */
+#define SSB_SPROM1_SPID                        0x1004  /* Subsystem Product ID for PCI */
+#define SSB_SPROM1_SVID                        0x1006  /* Subsystem Vendor ID for PCI */
+#define SSB_SPROM1_PID                 0x1008  /* Product ID for PCI */
+#define SSB_SPROM1_IL0MAC              0x1048  /* 6 bytes MAC address for 802.11b/g */
+#define SSB_SPROM1_ET0MAC              0x104E  /* 6 bytes MAC address for Ethernet */
+#define SSB_SPROM1_ET1MAC              0x1054  /* 6 bytes MAC address for 802.11a */
+#define SSB_SPROM1_ETHPHY              0x105A  /* Ethernet PHY settings */
+#define  SSB_SPROM1_ETHPHY_ET0A                0x001F  /* MII Address for enet0 */
+#define  SSB_SPROM1_ETHPHY_ET1A                0x03E0  /* MII Address for enet1 */
+#define  SSB_SPROM1_ETHPHY_ET1A_SHIFT  5
+#define  SSB_SPROM1_ETHPHY_ET0M                (1<<14) /* MDIO for enet0 */
+#define  SSB_SPROM1_ETHPHY_ET1M                (1<<15) /* MDIO for enet1 */
+#define SSB_SPROM1_BINF                        0x105C  /* Board info */
+#define  SSB_SPROM1_BINF_BREV          0x00FF  /* Board Revision */
+#define  SSB_SPROM1_BINF_CCODE         0x0F00  /* Country Code */
+#define  SSB_SPROM1_BINF_CCODE_SHIFT   8
+#define  SSB_SPROM1_BINF_ANTA          0x3000  /* Available A-PHY antennas */
+#define  SSB_SPROM1_BINF_ANTA_SHIFT    12
+#define  SSB_SPROM1_BINF_ANTBG         0xC000  /* Available B-PHY antennas */
+#define  SSB_SPROM1_BINF_ANTBG_SHIFT   14
+#define SSB_SPROM1_PA0B0               0x105E
+#define SSB_SPROM1_PA0B1               0x1060
+#define SSB_SPROM1_PA0B2               0x1062
+#define SSB_SPROM1_GPIOA               0x1064  /* General Purpose IO pins 0 and 1 */
+#define  SSB_SPROM1_GPIOA_P0           0x00FF  /* Pin 0 */
+#define  SSB_SPROM1_GPIOA_P1           0xFF00  /* Pin 1 */
+#define  SSB_SPROM1_GPIOA_P1_SHIFT     8
+#define SSB_SPROM1_GPIOB               0x1066  /* General Purpuse IO pins 2 and 3 */
+#define  SSB_SPROM1_GPIOB_P2           0x00FF  /* Pin 2 */
+#define  SSB_SPROM1_GPIOB_P3           0xFF00  /* Pin 3 */
+#define  SSB_SPROM1_GPIOB_P3_SHIFT     8
+#define SSB_SPROM1_MAXPWR              0x1068  /* Power Amplifier Max Power */
+#define  SSB_SPROM1_MAXPWR_BG          0x00FF  /* B-PHY and G-PHY (in dBm Q5.2) */
+#define  SSB_SPROM1_MAXPWR_A           0xFF00  /* A-PHY (in dBm Q5.2) */
+#define  SSB_SPROM1_MAXPWR_A_SHIFT     8
+#define SSB_SPROM1_PA1B0               0x106A
+#define SSB_SPROM1_PA1B1               0x106C
+#define SSB_SPROM1_PA1B2               0x106E
+#define SSB_SPROM1_ITSSI               0x1070  /* Idle TSSI Target */
+#define  SSB_SPROM1_ITSSI_BG           0x00FF  /* B-PHY and G-PHY*/
+#define  SSB_SPROM1_ITSSI_A            0xFF00  /* A-PHY */
+#define  SSB_SPROM1_ITSSI_A_SHIFT      8
+#define SSB_SPROM1_BFLLO               0x1072  /* Boardflags (low 16 bits) */
+#define SSB_SPROM1_AGAIN               0x1074  /* Antenna Gain (in dBm Q5.2) */
+#define  SSB_SPROM1_AGAIN_A            0x00FF  /* A-PHY */
+#define  SSB_SPROM1_AGAIN_BG           0xFF00  /* B-PHY and G-PHY */
+#define  SSB_SPROM1_AGAIN_BG_SHIFT     8
+#define SSB_SPROM1_OEM                 0x1076  /* 8 bytes OEM string (rev 1 only) */
+/* SPROM Revision 2 (inherits from rev 1) */
+#define SSB_SPROM2_BFLHI               0x1038  /* Boardflags (high 16 bits) */
+#define SSB_SPROM2_MAXP_A              0x103A  /* A-PHY Max Power */
+#define  SSB_SPROM2_MAXP_A_HI          0x00FF  /* Max Power High */
+#define  SSB_SPROM2_MAXP_A_LO          0xFF00  /* Max Power Low */
+#define  SSB_SPROM2_MAXP_A_LO_SHIFT    8
+#define SSB_SPROM2_PA1LOB0             0x103C  /* A-PHY PowerAmplifier Low Settings */
+#define SSB_SPROM2_PA1LOB1             0x103E  /* A-PHY PowerAmplifier Low Settings */
+#define SSB_SPROM2_PA1LOB2             0x1040  /* A-PHY PowerAmplifier Low Settings */
+#define SSB_SPROM2_PA1HIB0             0x1042  /* A-PHY PowerAmplifier High Settings */
+#define SSB_SPROM2_PA1HIB1             0x1044  /* A-PHY PowerAmplifier High Settings */
+#define SSB_SPROM2_PA1HIB2             0x1046  /* A-PHY PowerAmplifier High Settings */
+#define SSB_SPROM2_OPO                 0x1078  /* OFDM Power Offset from CCK Level */
+#define  SSB_SPROM2_OPO_VALUE          0x00FF
+#define  SSB_SPROM2_OPO_UNUSED         0xFF00
+#define SSB_SPROM2_CCODE               0x107C  /* Two char Country Code */
+/* SPROM Revision 3 (inherits from rev 2) */
+#define SSB_SPROM3_OFDMAPO             0x102C  /* A-PHY OFDM Mid Power Offset (4 bytes, BigEndian) */
+#define SSB_SPROM3_OFDMALPO            0x1030  /* A-PHY OFDM Low Power Offset (4 bytes, BigEndian) */
+#define SSB_SPROM3_OFDMAHPO            0x1034  /* A-PHY OFDM High Power Offset (4 bytes, BigEndian) */
+#define SSB_SPROM3_GPIOLDC             0x1042  /* GPIO LED Powersave Duty Cycle (4 bytes, BigEndian) */
+#define  SSB_SPROM3_GPIOLDC_OFF                0x0000FF00      /* Off Count */
+#define  SSB_SPROM3_GPIOLDC_OFF_SHIFT  8
+#define  SSB_SPROM3_GPIOLDC_ON         0x00FF0000      /* On Count */
+#define  SSB_SPROM3_GPIOLDC_ON_SHIFT   16
+#define SSB_SPROM3_CCKPO               0x1078  /* CCK Power Offset */
+#define  SSB_SPROM3_CCKPO_1M           0x000F  /* 1M Rate PO */
+#define  SSB_SPROM3_CCKPO_2M           0x00F0  /* 2M Rate PO */
+#define  SSB_SPROM3_CCKPO_2M_SHIFT     4
+#define  SSB_SPROM3_CCKPO_55M          0x0F00  /* 5.5M Rate PO */
+#define  SSB_SPROM3_CCKPO_55M_SHIFT    8
+#define  SSB_SPROM3_CCKPO_11M          0xF000  /* 11M Rate PO */
+#define  SSB_SPROM3_CCKPO_11M_SHIFT    12
+#define  SSB_SPROM3_OFDMGPO            0x107A  /* G-PHY OFDM Power Offset (4 bytes, BigEndian) */
+
+/* Values for SSB_SPROM1_BINF_CCODE */
+enum {
+       SSB_SPROM1CCODE_WORLD = 0,
+       SSB_SPROM1CCODE_THAILAND,
+       SSB_SPROM1CCODE_ISRAEL,
+       SSB_SPROM1CCODE_JORDAN,
+       SSB_SPROM1CCODE_CHINA,
+       SSB_SPROM1CCODE_JAPAN,
+       SSB_SPROM1CCODE_USA_CANADA_ANZ,
+       SSB_SPROM1CCODE_EUROPE,
+       SSB_SPROM1CCODE_USA_LOW,
+       SSB_SPROM1CCODE_JAPAN_HIGH,
+       SSB_SPROM1CCODE_ALL,
+       SSB_SPROM1CCODE_NONE,
+};
+
+/* Address-Match values and masks (SSB_ADMATCHxxx) */
+#define SSB_ADM_TYPE                   0x00000003      /* Address type */
+#define  SSB_ADM_TYPE0                 0
+#define  SSB_ADM_TYPE1                 1
+#define  SSB_ADM_TYPE2                 2
+#define SSB_ADM_AD64                   0x00000004
+#define SSB_ADM_SZ0                    0x000000F8      /* Type0 size */
+#define SSB_ADM_SZ0_SHIFT              3
+#define SSB_ADM_SZ1                    0x000001F8      /* Type1 size */
+#define SSB_ADM_SZ1_SHIFT              3
+#define SSB_ADM_SZ2                    0x000001F8      /* Type2 size */
+#define SSB_ADM_SZ2_SHIFT              3
+#define SSB_ADM_EN                     0x00000400      /* Enable */
+#define SSB_ADM_NEG                    0x00000800      /* Negative decode */
+#define SSB_ADM_BASE0                  0xFFFFFF00      /* Type0 base address */
+#define SSB_ADM_BASE0_SHIFT            8
+#define SSB_ADM_BASE1                  0xFFFFF000      /* Type1 base address for the core */
+#define SSB_ADM_BASE1_SHIFT            12
+#define SSB_ADM_BASE2                  0xFFFF0000      /* Type2 base address for the core */
+#define SSB_ADM_BASE2_SHIFT            16
+
+
+#endif /* LINUX_SSB_REGS_H_ */
index 8a09021..895ba3a 100644 (file)
@@ -484,6 +484,21 @@ static int do_parisc_entry(const char *filename, struct parisc_device_id *id,
        return 1;
 }
 
+/* Looks like: ssb:vNidNrevN. */
+static int do_ssb_entry(const char *filename,
+                       struct ssb_device_id *id, char *alias)
+{
+       id->vendor = TO_NATIVE(id->vendor);
+       id->coreid = TO_NATIVE(id->coreid);
+       id->revision = TO_NATIVE(id->revision);
+
+       strcpy(alias, "ssb:");
+       ADD(alias, "v", id->vendor != SSB_ANY_VENDOR, id->vendor);
+       ADD(alias, "id", id->coreid != SSB_ANY_ID, id->coreid);
+       ADD(alias, "rev", id->revision != SSB_ANY_REV, id->revision);
+       return 1;
+}
+
 /* Ignore any prefix, eg. v850 prepends _ */
 static inline int sym_is(const char *symbol, const char *name)
 {
@@ -599,6 +614,10 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
                do_table(symval, sym->st_size,
                         sizeof(struct parisc_device_id), "parisc",
                         do_parisc_entry, mod);
+       else if (sym_is(symname, "__mod_ssb_device_table"))
+               do_table(symval, sym->st_size,
+                        sizeof(struct ssb_device_id), "ssb",
+                        do_ssb_entry, mod);
 }
 
 /* Now add out buffered information to the generated C source */