]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - drivers/ssb/main.c
Merge branch 'samsung/exynos5' into next/soc2
[linux-2.6.git] / drivers / ssb / main.c
index 74d5182db4b2b5fb2ea970b97777364e60984bec..ff109ae947677d0b5c672539b1e1496d9ad1c344 100644 (file)
@@ -3,7 +3,7 @@
  * Subsystem core
  *
  * Copyright 2005, Broadcom Corporation
- * Copyright 2006, 2007, Michael Buesch <mb@bu3sch.de>
+ * Copyright 2006, 2007, Michael Buesch <m@bues.ch>
  *
  * Licensed under the GNU/GPL. See COPYING for details.
  */
 #include "ssb_private.h"
 
 #include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
 #include <linux/ssb/ssb.h>
 #include <linux/ssb/ssb_regs.h>
+#include <linux/ssb/ssb_driver_gige.h>
 #include <linux/dma-mapping.h>
 #include <linux/pci.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/slab.h>
 
-#include <pcmcia/cs_types.h>
-#include <pcmcia/cs.h>
 #include <pcmcia/cistpl.h>
 #include <pcmcia/ds.h>
 
@@ -67,6 +70,63 @@ found:
 }
 #endif /* CONFIG_SSB_PCIHOST */
 
+#ifdef CONFIG_SSB_PCMCIAHOST
+struct ssb_bus *ssb_pcmcia_dev_to_bus(struct pcmcia_device *pdev)
+{
+       struct ssb_bus *bus;
+
+       ssb_buses_lock();
+       list_for_each_entry(bus, &buses, list) {
+               if (bus->bustype == SSB_BUSTYPE_PCMCIA &&
+                   bus->host_pcmcia == pdev)
+                       goto found;
+       }
+       bus = NULL;
+found:
+       ssb_buses_unlock();
+
+       return bus;
+}
+#endif /* CONFIG_SSB_PCMCIAHOST */
+
+#ifdef CONFIG_SSB_SDIOHOST
+struct ssb_bus *ssb_sdio_func_to_bus(struct sdio_func *func)
+{
+       struct ssb_bus *bus;
+
+       ssb_buses_lock();
+       list_for_each_entry(bus, &buses, list) {
+               if (bus->bustype == SSB_BUSTYPE_SDIO &&
+                   bus->host_sdio == func)
+                       goto found;
+       }
+       bus = NULL;
+found:
+       ssb_buses_unlock();
+
+       return bus;
+}
+#endif /* CONFIG_SSB_SDIOHOST */
+
+int ssb_for_each_bus_call(unsigned long data,
+                         int (*func)(struct ssb_bus *bus, unsigned long data))
+{
+       struct ssb_bus *bus;
+       int res;
+
+       ssb_buses_lock();
+       list_for_each_entry(bus, &buses, list) {
+               res = func(bus, data);
+               if (res >= 0) {
+                       ssb_buses_unlock();
+                       return res;
+               }
+       }
+       ssb_buses_unlock();
+
+       return -ENODEV;
+}
+
 static struct ssb_device *ssb_device_get(struct ssb_device *dev)
 {
        if (dev)
@@ -80,35 +140,12 @@ static void ssb_device_put(struct ssb_device *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)
@@ -120,27 +157,10 @@ 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) {
@@ -150,105 +170,120 @@ static int ssb_device_suspend(struct device *dev, pm_message_t state)
                if (err)
                        goto out;
        }
+out:
+       return err;
+}
 
-       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);
+int ssb_bus_resume(struct ssb_bus *bus)
+{
+       int err;
+
+       /* Reset HW state information in memory, so that HW is
+        * completely reinitialized. */
+       bus->mapped_device = NULL;
+#ifdef CONFIG_SSB_DRIVER_PCICORE
+       bus->pcicore.setup_done = 0;
+#endif
+
+       err = ssb_bus_powerup(bus, 0);
+       if (err)
+               return err;
+       err = ssb_pcmcia_hardware_setup(bus);
+       if (err) {
+               ssb_bus_may_powerdown(bus);
+               return err;
        }
+       ssb_chipco_resume(&bus->chipco);
+       ssb_bus_may_powerdown(bus);
 
-out:
-       return err;
+       return 0;
 }
+EXPORT_SYMBOL(ssb_bus_resume);
 
-#ifdef CONFIG_SSB_PCIHOST
-int ssb_devices_freeze(struct ssb_bus *bus)
+int ssb_bus_suspend(struct ssb_bus *bus)
 {
-       struct ssb_device *dev;
-       struct ssb_driver *drv;
-       int err = 0;
-       int i;
-       pm_message_t state = PMSG_FREEZE;
+       ssb_chipco_suspend(&bus->chipco);
+       ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0);
+
+       return 0;
+}
+EXPORT_SYMBOL(ssb_bus_suspend);
+
+#ifdef CONFIG_SSB_SPROM
+/** ssb_devices_freeze - Freeze all devices on the bus.
+ *
+ * After freezing no device driver will be handling a device
+ * on this bus anymore. ssb_devices_thaw() must be called after
+ * a successful freeze to reactivate the devices.
+ *
+ * @bus: The bus.
+ * @ctx: Context structure. Pass this to ssb_devices_thaw().
+ */
+int ssb_devices_freeze(struct ssb_bus *bus, struct ssb_freeze_context *ctx)
+{
+       struct ssb_device *sdev;
+       struct ssb_driver *sdrv;
+       unsigned int i;
+
+       memset(ctx, 0, sizeof(*ctx));
+       ctx->bus = bus;
+       SSB_WARN_ON(bus->nr_devices > ARRAY_SIZE(ctx->device_frozen));
 
-       /* 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)
+               sdev = ssb_device_get(&bus->devices[i]);
+
+               if (!sdev->dev || !sdev->dev->driver ||
+                   !device_is_registered(sdev->dev)) {
+                       ssb_device_put(sdev);
                        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))
+               sdrv = drv_to_ssb_drv(sdev->dev->driver);
+               if (SSB_WARN_ON(!sdrv->remove))
                        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;
-               }
+               sdrv->remove(sdev);
+               ctx->device_frozen[i] = 1;
        }
 
        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)
+/** ssb_devices_thaw - Unfreeze all devices on the bus.
+ *
+ * This will re-attach the device drivers and re-init the devices.
+ *
+ * @ctx: The context structure from ssb_devices_freeze()
+ */
+int ssb_devices_thaw(struct ssb_freeze_context *ctx)
 {
-       struct ssb_device *dev;
-       struct ssb_driver *drv;
-       int err;
-       int i;
+       struct ssb_bus *bus = ctx->bus;
+       struct ssb_device *sdev;
+       struct ssb_driver *sdrv;
+       unsigned int i;
+       int err, result = 0;
 
        for (i = 0; i < bus->nr_devices; i++) {
-               dev = &(bus->devices[i]);
-               if (!dev->dev ||
-                   !dev->dev->driver ||
-                   !device_is_registered(dev->dev))
+               if (!ctx->device_frozen[i])
                        continue;
-               drv = drv_to_ssb_drv(dev->dev->driver);
-               if (!drv)
+               sdev = &bus->devices[i];
+
+               if (SSB_WARN_ON(!sdev->dev || !sdev->dev->driver))
                        continue;
-               if (SSB_WARN_ON(!drv->resume))
+               sdrv = drv_to_ssb_drv(sdev->dev->driver);
+               if (SSB_WARN_ON(!sdrv || !sdrv->probe))
                        continue;
-               err = drv->resume(dev);
+
+               err = sdrv->probe(sdev, &sdev->id);
                if (err) {
                        ssb_printk(KERN_ERR PFX "Failed to thaw device %s\n",
-                                  dev->dev->bus_id);
+                                  dev_name(sdev->dev));
+                       result = err;
                }
+               ssb_device_put(sdev);
        }
 
-       return 0;
+       return result;
 }
-#endif /* CONFIG_SSB_PCIHOST */
+#endif /* CONFIG_SSB_SPROM */
 
 static void ssb_device_shutdown(struct device *dev)
 {
@@ -320,25 +355,48 @@ static int ssb_bus_match(struct device *dev, struct device_driver *drv)
        return 0;
 }
 
-static int ssb_device_uevent(struct device *dev, char **envp, int num_envp,
-                            char *buffer, int buffer_size)
+static int ssb_device_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
        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,
+       return add_uevent_var(env,
                             "MODALIAS=ssb:v%04Xid%04Xrev%02X",
                             ssb_dev->id.vendor, ssb_dev->id.coreid,
                             ssb_dev->id.revision);
-       envp[i] = NULL;
+}
+
+#define ssb_config_attr(attrib, field, format_string) \
+static ssize_t \
+attrib##_show(struct device *dev, struct device_attribute *attr, char *buf) \
+{ \
+       return sprintf(buf, format_string, dev_to_ssb_dev(dev)->field); \
+}
 
-       return ret;
+ssb_config_attr(core_num, core_index, "%u\n")
+ssb_config_attr(coreid, id.coreid, "0x%04x\n")
+ssb_config_attr(vendor, id.vendor, "0x%04x\n")
+ssb_config_attr(revision, id.revision, "%u\n")
+ssb_config_attr(irq, irq, "%u\n")
+static ssize_t
+name_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%s\n",
+                      ssb_core_name(dev_to_ssb_dev(dev)->id.coreid));
 }
 
+static struct device_attribute ssb_device_attrs[] = {
+       __ATTR_RO(name),
+       __ATTR_RO(core_num),
+       __ATTR_RO(coreid),
+       __ATTR_RO(vendor),
+       __ATTR_RO(revision),
+       __ATTR_RO(irq),
+       __ATTR_NULL,
+};
+
 static struct bus_type ssb_bustype = {
        .name           = "ssb",
        .match          = ssb_bus_match,
@@ -348,6 +406,7 @@ static struct bus_type ssb_bustype = {
        .suspend        = ssb_device_suspend,
        .resume         = ssb_device_resume,
        .uevent         = ssb_device_uevent,
+       .dev_attrs      = ssb_device_attrs,
 };
 
 static void ssb_buses_lock(void)
@@ -383,7 +442,7 @@ void ssb_bus_unregister(struct ssb_bus *bus)
        list_del(&bus->list);
        ssb_buses_unlock();
 
-       /* ssb_pcmcia_exit(bus); */
+       ssb_pcmcia_exit(bus);
        ssb_pci_exit(bus);
        ssb_iounmap(bus);
 }
@@ -433,22 +492,30 @@ static int ssb_devices_register(struct ssb_bus *bus)
 
                dev->release = ssb_release_dev;
                dev->bus = &ssb_bustype;
-               snprintf(dev->bus_id, sizeof(dev->bus_id),
-                        "ssb%u:%d", bus->busnumber, dev_idx);
+               dev_set_name(dev, "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;
+                       sdev->dma_dev = dev->parent;
 #endif
                        break;
                case SSB_BUSTYPE_PCMCIA:
 #ifdef CONFIG_SSB_PCMCIAHOST
+                       sdev->irq = bus->host_pcmcia->irq;
                        dev->parent = &bus->host_pcmcia->dev;
+#endif
+                       break;
+               case SSB_BUSTYPE_SDIO:
+#ifdef CONFIG_SSB_SDIOHOST
+                       dev->parent = &bus->host_sdio->dev;
 #endif
                        break;
                case SSB_BUSTYPE_SSB:
+                       dev->dma_mask = &dev->coherent_dma_mask;
+                       sdev->dma_dev = dev;
                        break;
                }
 
@@ -457,7 +524,7 @@ static int ssb_devices_register(struct ssb_bus *bus)
                if (err) {
                        ssb_printk(KERN_ERR PFX
                                   "Could not register %s\n",
-                                  dev->bus_id);
+                                  dev_name(dev));
                        /* Set dev to NULL to not unregister
                         * dev on error unwinding. */
                        sdev->dev = NULL;
@@ -475,7 +542,7 @@ error:
 }
 
 /* Needs ssb_buses_lock() */
-static int ssb_attach_queued_buses(void)
+static int __devinit ssb_attach_queued_buses(void)
 {
        struct ssb_bus *bus, *n;
        int err = 0;
@@ -509,6 +576,14 @@ error:
        return err;
 }
 
+static u8 ssb_ssb_read8(struct ssb_device *dev, u16 offset)
+{
+       struct ssb_bus *bus = dev->bus;
+
+       offset += dev->core_index * SSB_CORE_SIZE;
+       return readb(bus->mmio + offset);
+}
+
 static u16 ssb_ssb_read16(struct ssb_device *dev, u16 offset)
 {
        struct ssb_bus *bus = dev->bus;
@@ -525,6 +600,63 @@ static u32 ssb_ssb_read32(struct ssb_device *dev, u16 offset)
        return readl(bus->mmio + offset);
 }
 
+#ifdef CONFIG_SSB_BLOCKIO
+static void ssb_ssb_block_read(struct ssb_device *dev, void *buffer,
+                              size_t count, u16 offset, u8 reg_width)
+{
+       struct ssb_bus *bus = dev->bus;
+       void __iomem *addr;
+
+       offset += dev->core_index * SSB_CORE_SIZE;
+       addr = bus->mmio + offset;
+
+       switch (reg_width) {
+       case sizeof(u8): {
+               u8 *buf = buffer;
+
+               while (count) {
+                       *buf = __raw_readb(addr);
+                       buf++;
+                       count--;
+               }
+               break;
+       }
+       case sizeof(u16): {
+               __le16 *buf = buffer;
+
+               SSB_WARN_ON(count & 1);
+               while (count) {
+                       *buf = (__force __le16)__raw_readw(addr);
+                       buf++;
+                       count -= 2;
+               }
+               break;
+       }
+       case sizeof(u32): {
+               __le32 *buf = buffer;
+
+               SSB_WARN_ON(count & 3);
+               while (count) {
+                       *buf = (__force __le32)__raw_readl(addr);
+                       buf++;
+                       count -= 4;
+               }
+               break;
+       }
+       default:
+               SSB_WARN_ON(1);
+       }
+}
+#endif /* CONFIG_SSB_BLOCKIO */
+
+static void ssb_ssb_write8(struct ssb_device *dev, u16 offset, u8 value)
+{
+       struct ssb_bus *bus = dev->bus;
+
+       offset += dev->core_index * SSB_CORE_SIZE;
+       writeb(value, bus->mmio + offset);
+}
+
 static void ssb_ssb_write16(struct ssb_device *dev, u16 offset, u16 value)
 {
        struct ssb_bus *bus = dev->bus;
@@ -541,12 +673,67 @@ static void ssb_ssb_write32(struct ssb_device *dev, u16 offset, u32 value)
        writel(value, bus->mmio + offset);
 }
 
+#ifdef CONFIG_SSB_BLOCKIO
+static void ssb_ssb_block_write(struct ssb_device *dev, const void *buffer,
+                               size_t count, u16 offset, u8 reg_width)
+{
+       struct ssb_bus *bus = dev->bus;
+       void __iomem *addr;
+
+       offset += dev->core_index * SSB_CORE_SIZE;
+       addr = bus->mmio + offset;
+
+       switch (reg_width) {
+       case sizeof(u8): {
+               const u8 *buf = buffer;
+
+               while (count) {
+                       __raw_writeb(*buf, addr);
+                       buf++;
+                       count--;
+               }
+               break;
+       }
+       case sizeof(u16): {
+               const __le16 *buf = buffer;
+
+               SSB_WARN_ON(count & 1);
+               while (count) {
+                       __raw_writew((__force u16)(*buf), addr);
+                       buf++;
+                       count -= 2;
+               }
+               break;
+       }
+       case sizeof(u32): {
+               const __le32 *buf = buffer;
+
+               SSB_WARN_ON(count & 3);
+               while (count) {
+                       __raw_writel((__force u32)(*buf), addr);
+                       buf++;
+                       count -= 4;
+               }
+               break;
+       }
+       default:
+               SSB_WARN_ON(1);
+       }
+}
+#endif /* CONFIG_SSB_BLOCKIO */
+
 /* Ops for the plain SSB bus without a host-device (no PCI or PCMCIA). */
 static const struct ssb_bus_ops ssb_ssb_ops = {
+       .read8          = ssb_ssb_read8,
        .read16         = ssb_ssb_read16,
        .read32         = ssb_ssb_read32,
+       .write8         = ssb_ssb_write8,
        .write16        = ssb_ssb_write16,
        .write32        = ssb_ssb_write32,
+#ifdef CONFIG_SSB_BLOCKIO
+       .block_read     = ssb_ssb_block_read,
+       .block_write    = ssb_ssb_block_write,
+#endif
 };
 
 static int ssb_fetch_invariants(struct ssb_bus *bus,
@@ -561,29 +748,39 @@ static int ssb_fetch_invariants(struct ssb_bus *bus,
                goto out;
        memcpy(&bus->boardinfo, &iv.boardinfo, sizeof(iv.boardinfo));
        memcpy(&bus->sprom, &iv.sprom, sizeof(iv.sprom));
+       bus->has_cardbus_slot = iv.has_cardbus_slot;
 out:
        return err;
 }
 
-static int ssb_bus_register(struct ssb_bus *bus,
-                           ssb_invariants_func_t get_invariants,
-                           unsigned long baseaddr)
+static int __devinit 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);
+#ifdef CONFIG_SSB_EMBEDDED
+       spin_lock_init(&bus->gpio_lock);
+#endif
 
        /* Powerup the bus */
        err = ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 1);
        if (err)
                goto out;
+
+       /* Init SDIO-host device (if any), before the scan */
+       err = ssb_sdio_init(bus);
+       if (err)
+               goto err_disable_xtal;
+
        ssb_buses_lock();
        bus->busnumber = next_busnumber;
        /* Scan for devices (cores) */
        err = ssb_bus_scan(bus, baseaddr);
        if (err)
-               goto err_disable_xtal;
+               goto err_sdio_exit;
 
        /* Init PCI-host device (if any) */
        err = ssb_pci_init(bus);
@@ -625,11 +822,13 @@ out:
 err_dequeue:
        list_del(&bus->list);
 err_pcmcia_exit:
-/*     ssb_pcmcia_exit(bus); */
+       ssb_pcmcia_exit(bus);
 err_pci_exit:
        ssb_pci_exit(bus);
 err_unmap:
        ssb_iounmap(bus);
+err_sdio_exit:
+       ssb_sdio_exit(bus);
 err_disable_xtal:
        ssb_buses_unlock();
        ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0);
@@ -637,8 +836,8 @@ err_disable_xtal:
 }
 
 #ifdef CONFIG_SSB_PCIHOST
-int ssb_bus_pcibus_register(struct ssb_bus *bus,
-                           struct pci_dev *host_pci)
+int __devinit ssb_bus_pcibus_register(struct ssb_bus *bus,
+                                     struct pci_dev *host_pci)
 {
        int err;
 
@@ -649,7 +848,10 @@ int ssb_bus_pcibus_register(struct ssb_bus *bus,
        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);
+                          "PCI device %s\n", dev_name(&host_pci->dev));
+       } else {
+               ssb_printk(KERN_ERR PFX "Failed to register PCI version"
+                          " of SSB with error %d\n", err);
        }
 
        return err;
@@ -658,9 +860,9 @@ 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 __devinit ssb_bus_pcmciabus_register(struct ssb_bus *bus,
+                                        struct pcmcia_device *pcmcia_dev,
+                                        unsigned long baseaddr)
 {
        int err;
 
@@ -679,9 +881,32 @@ int ssb_bus_pcmciabus_register(struct ssb_bus *bus,
 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)
+#ifdef CONFIG_SSB_SDIOHOST
+int __devinit ssb_bus_sdiobus_register(struct ssb_bus *bus,
+                                      struct sdio_func *func,
+                                      unsigned int quirks)
+{
+       int err;
+
+       bus->bustype = SSB_BUSTYPE_SDIO;
+       bus->host_sdio = func;
+       bus->ops = &ssb_sdio_ops;
+       bus->quirks = quirks;
+
+       err = ssb_bus_register(bus, ssb_sdio_get_invariants, ~0);
+       if (!err) {
+               ssb_printk(KERN_INFO PFX "Sonics Silicon Backplane found on "
+                          "SDIO device %s\n", sdio_func_id(func));
+       }
+
+       return err;
+}
+EXPORT_SYMBOL(ssb_bus_sdiobus_register);
+#endif /* CONFIG_SSB_PCMCIAHOST */
+
+int __devinit ssb_bus_ssbbus_register(struct ssb_bus *bus,
+                                     unsigned long baseaddr,
+                                     ssb_invariants_func_t get_invariants)
 {
        int err;
 
@@ -762,8 +987,8 @@ u32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m)
        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;
+                       return SSB_CHIPCO_CLK_T6_M1;
+               return SSB_CHIPCO_CLK_T6_M0;
        case SSB_PLLTYPE_1: /* 48Mhz base, 3 dividers */
        case SSB_PLLTYPE_3: /* 25Mhz, 2 dividers */
        case SSB_PLLTYPE_4: /* 48Mhz, 4 dividers */
@@ -876,17 +1101,24 @@ 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) {
+       u32 rev = ssb_read32(dev, SSB_IDLOW) & SSB_IDLOW_SSBREV;
+
+       /* The REJECT bit seems to be different for Backplane rev 2.3 */
+       switch (rev) {
        case SSB_IDLOW_SSBREV_22:
-               return SSB_TMSLOW_REJECT_22;
+       case SSB_IDLOW_SSBREV_24:
+       case SSB_IDLOW_SSBREV_26:
+               return SSB_TMSLOW_REJECT;
        case SSB_IDLOW_SSBREV_23:
                return SSB_TMSLOW_REJECT_23;
+       case SSB_IDLOW_SSBREV_25:     /* TODO - find the proper REJECT bit */
+       case SSB_IDLOW_SSBREV_27:     /* same here */
+               return SSB_TMSLOW_REJECT;       /* this is a guess */
        default:
+               printk(KERN_INFO "ssb: Backplane Revision 0x%.8X\n", rev);
                WARN_ON(1);
        }
-       return (SSB_TMSLOW_REJECT_22 | SSB_TMSLOW_REJECT_23);
+       return (SSB_TMSLOW_REJECT | SSB_TMSLOW_REJECT_23);
 }
 
 int ssb_device_is_enabled(struct ssb_device *dev)
@@ -945,10 +1177,10 @@ void ssb_device_enable(struct ssb_device *dev, u32 core_specific_flags)
 }
 EXPORT_SYMBOL(ssb_device_enable);
 
-/* Wait for a bit in a register to get set or unset.
+/* Wait for bitmask in a register to get set or cleared.
  * timeout is in units of ten-microseconds */
-static int ssb_wait_bit(struct ssb_device *dev, u16 reg, u32 bitmask,
-                       int timeout, int set)
+static int ssb_wait_bits(struct ssb_device *dev, u16 reg, u32 bitmask,
+                        int timeout, int set)
 {
        int i;
        u32 val;
@@ -956,7 +1188,7 @@ static int ssb_wait_bit(struct ssb_device *dev, u16 reg, u32 bitmask,
        for (i = 0; i < timeout; i++) {
                val = ssb_read32(dev, reg);
                if (set) {
-                       if (val & bitmask)
+                       if ((val & bitmask) == bitmask)
                                return 0;
                } else {
                        if (!(val & bitmask))
@@ -973,20 +1205,38 @@ static int ssb_wait_bit(struct ssb_device *dev, u16 reg, u32 bitmask,
 
 void ssb_device_disable(struct ssb_device *dev, u32 core_specific_flags)
 {
-       u32 reject;
+       u32 reject, val;
 
        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);
+
+       if (ssb_read32(dev, SSB_TMSLOW) & SSB_TMSLOW_CLOCK) {
+               ssb_write32(dev, SSB_TMSLOW, reject | SSB_TMSLOW_CLOCK);
+               ssb_wait_bits(dev, SSB_TMSLOW, reject, 1000, 1);
+               ssb_wait_bits(dev, SSB_TMSHIGH, SSB_TMSHIGH_BUSY, 1000, 0);
+
+               if (ssb_read32(dev, SSB_IDLOW) & SSB_IDLOW_INITIATOR) {
+                       val = ssb_read32(dev, SSB_IMSTATE);
+                       val |= SSB_IMSTATE_REJECT;
+                       ssb_write32(dev, SSB_IMSTATE, val);
+                       ssb_wait_bits(dev, SSB_IMSTATE, SSB_IMSTATE_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);
+
+               if (ssb_read32(dev, SSB_IDLOW) & SSB_IDLOW_INITIATOR) {
+                       val = ssb_read32(dev, SSB_IMSTATE);
+                       val &= ~SSB_IMSTATE_REJECT;
+                       ssb_write32(dev, SSB_IMSTATE, val);
+               }
+       }
 
        ssb_write32(dev, SSB_TMSLOW,
                    reject | SSB_TMSLOW_RESET |
@@ -995,35 +1245,41 @@ void ssb_device_disable(struct ssb_device *dev, u32 core_specific_flags)
 }
 EXPORT_SYMBOL(ssb_device_disable);
 
+/* Some chipsets need routing known for PCIe and 64-bit DMA */
+static bool ssb_dma_translation_special_bit(struct ssb_device *dev)
+{
+       u16 chip_id = dev->bus->chip_id;
+
+       if (dev->id.coreid == SSB_DEV_80211) {
+               return (chip_id == 0x4322 || chip_id == 43221 ||
+                       chip_id == 43231 || chip_id == 43222);
+       }
+
+       return 0;
+}
+
 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;
+               if (pci_is_pcie(dev->bus->host_pci) &&
+                   ssb_read32(dev, SSB_TMSHIGH) & SSB_TMSHIGH_DMA64) {
+                       return SSB_PCIE_DMA_H32;
+               } else {
+                       if (ssb_dma_translation_special_bit(dev))
+                               return SSB_PCIE_DMA_H32;
+                       else
+                               return SSB_PCI_DMA;
+               }
+       default:
+               __ssb_dma_not_implemented(dev);
        }
        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;
@@ -1036,6 +1292,12 @@ int ssb_bus_may_powerdown(struct ssb_bus *bus)
                goto out;
 
        cc = &bus->chipco;
+
+       if (!cc->dev)
+               goto out;
+       if (cc->dev->id.revision < 5)
+               goto out;
+
        ssb_chipco_set_clockmode(cc, SSB_CLKMODE_SLOW);
        err = ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0);
        if (err)
@@ -1053,20 +1315,20 @@ 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
+
+       mode = dynamic_pctl ? SSB_CLKMODE_DYNAMIC : SSB_CLKMODE_FAST;
+       ssb_chipco_set_clockmode(&bus->chipco, mode);
+
        return 0;
 error:
        ssb_printk(KERN_ERR PFX "Bus powerup failed\n");
@@ -1074,6 +1336,37 @@ error:
 }
 EXPORT_SYMBOL(ssb_bus_powerup);
 
+static void ssb_broadcast_value(struct ssb_device *dev,
+                               u32 address, u32 data)
+{
+#ifdef CONFIG_SSB_DRIVER_PCICORE
+       /* 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);
+#endif
+
+       ssb_write32(dev, SSB_CHIPCO_BCAST_ADDR, address);
+       ssb_read32(dev, SSB_CHIPCO_BCAST_ADDR); /* flush */
+       ssb_write32(dev, SSB_CHIPCO_BCAST_DATA, data);
+       ssb_read32(dev, SSB_CHIPCO_BCAST_DATA); /* flush */
+}
+
+void ssb_commit_settings(struct ssb_bus *bus)
+{
+       struct ssb_device *dev;
+
+#ifdef CONFIG_SSB_DRIVER_PCICORE
+       dev = bus->chipco.dev ? bus->chipco.dev : bus->pcicore.dev;
+#else
+       dev = bus->chipco.dev;
+#endif
+       if (WARN_ON(!dev))
+               return;
+       /* This forces an update of the cached registers. */
+       ssb_broadcast_value(dev, 0xFD8, 0);
+}
+EXPORT_SYMBOL(ssb_commit_settings);
+
 u32 ssb_admatch_base(u32 adm)
 {
        u32 base = 0;
@@ -1139,23 +1432,36 @@ static int __init ssb_modinit(void)
        ssb_buses_lock();
        err = ssb_attach_queued_buses();
        ssb_buses_unlock();
-       if (err)
+       if (err) {
                bus_unregister(&ssb_bustype);
+               goto out;
+       }
 
        err = b43_pci_ssb_bridge_init();
        if (err) {
                ssb_printk(KERN_ERR "Broadcom 43xx PCI-SSB-bridge "
-                          "initialization failed");
+                          "initialization failed\n");
                /* don't fail SSB init because of this */
                err = 0;
        }
-
+       err = ssb_gige_init();
+       if (err) {
+               ssb_printk(KERN_ERR "SSB Broadcom Gigabit Ethernet "
+                          "driver initialization failed\n");
+               /* don't fail SSB init because of this */
+               err = 0;
+       }
+out:
        return err;
 }
-subsys_initcall(ssb_modinit);
+/* ssb must be initialized after PCI but before the ssb drivers.
+ * That means we must use some initcall between subsys_initcall
+ * and device_initcall. */
+fs_initcall(ssb_modinit);
 
 static void __exit ssb_modexit(void)
 {
+       ssb_gige_exit();
        b43_pci_ssb_bridge_exit();
        bus_unregister(&ssb_bustype);
 }