aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS6
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/ssb/Kconfig117
-rw-r--r--drivers/ssb/Makefile18
-rw-r--r--drivers/ssb/b43_pci_bridge.c46
-rw-r--r--drivers/ssb/driver_chipcommon.c446
-rw-r--r--drivers/ssb/driver_extif.c129
-rw-r--r--drivers/ssb/driver_mipscore.c223
-rw-r--r--drivers/ssb/driver_pcicore.c576
-rw-r--r--drivers/ssb/main.c1162
-rw-r--r--drivers/ssb/pci.c740
-rw-r--r--drivers/ssb/pcihost_wrapper.c104
-rw-r--r--drivers/ssb/pcmcia.c271
-rw-r--r--drivers/ssb/scan.c413
-rw-r--r--drivers/ssb/ssb_private.h136
-rw-r--r--include/linux/mod_devicetable.h15
-rw-r--r--include/linux/ssb/ssb.h424
-rw-r--r--include/linux/ssb/ssb_driver_chipcommon.h396
-rw-r--r--include/linux/ssb/ssb_driver_extif.h204
-rw-r--r--include/linux/ssb/ssb_driver_mips.h46
-rw-r--r--include/linux/ssb/ssb_driver_pci.h106
-rw-r--r--include/linux/ssb/ssb_regs.h292
-rw-r--r--scripts/mod/file2alias.c19
24 files changed, 5892 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 3f07d5ff85f..7524cd802da 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -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
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 3e1c442deff..7bdae47d6b9 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -58,6 +58,8 @@ source "drivers/power/Kconfig"
source "drivers/hwmon/Kconfig"
+source "drivers/ssb/Kconfig"
+
source "drivers/mfd/Kconfig"
source "drivers/media/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index f0878b2ec55..a168eacdcd9 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -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
index 00000000000..b4a5e5e9d9f
--- /dev/null
+++ b/drivers/ssb/Kconfig
@@ -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
index 00000000000..7be39759580
--- /dev/null
+++ b/drivers/ssb/Makefile
@@ -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
index 00000000000..fa3bd292f5f
--- /dev/null
+++ b/drivers/ssb/b43_pci_bridge.c
@@ -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
index 00000000000..a890544e8fb
--- /dev/null
+++ b/drivers/ssb/driver_chipcommon.c
@@ -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
index 00000000000..fe55eb8b038
--- /dev/null
+++ b/drivers/ssb/driver_extif.c
@@ -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
index 00000000000..ab8691a3258
--- /dev/null
+++ b/drivers/ssb/driver_mipscore.c
@@ -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)</