pcmcia: convert pcmcia_request_configuration to pcmcia_enable_device
[linux-2.6.git] / drivers / ssb / main.c
index bedb2b4..c68b3dc 100644 (file)
 #include <linux/io.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>
 
@@ -68,6 +69,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)
@@ -81,35 +139,25 @@ static void ssb_device_put(struct ssb_device *dev)
                put_device(dev->dev);
 }
 
-static int ssb_bus_resume(struct ssb_bus *bus)
+static inline struct ssb_driver *ssb_driver_get(struct ssb_driver *drv)
 {
-       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);
+       if (drv)
+               get_driver(&drv->drv);
+       return drv;
+}
 
-       return 0;
+static inline void ssb_driver_put(struct ssb_driver *drv)
+{
+       if (drv)
+               put_driver(&drv->drv);
 }
 
 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)
@@ -121,27 +169,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) {
@@ -151,105 +182,123 @@ static int ssb_device_suspend(struct device *dev, pm_message_t state)
                if (err)
                        goto out;
        }
+out:
+       return err;
+}
+
+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
 
-       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);
+       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))
-                       continue;
-               drv = drv_to_ssb_drv(dev->dev->driver);
-               if (!drv)
+               sdrv = ssb_driver_get(drv_to_ssb_drv(sdev->dev->driver));
+               if (!sdrv || SSB_WARN_ON(!sdrv->remove)) {
+                       ssb_device_put(sdev);
                        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_driver_put(sdrv);
+               ssb_device_put(sdev);
        }
 
-       return 0;
+       return result;
 }
-#endif /* CONFIG_SSB_PCIHOST */
+#endif /* CONFIG_SSB_SPROM */
 
 static void ssb_device_shutdown(struct device *dev)
 {
@@ -378,7 +427,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);
 }
@@ -428,23 +477,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.AssignedIRQ;
+                       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;
                }
 
@@ -453,7 +509,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;
@@ -505,6 +561,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;
@@ -521,6 +585,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;
@@ -537,12 +658,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,
@@ -578,12 +754,18 @@ static int ssb_bus_register(struct ssb_bus *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 +807,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);
@@ -649,7 +833,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;
@@ -679,6 +866,28 @@ int ssb_bus_pcmciabus_register(struct ssb_bus *bus,
 EXPORT_SYMBOL(ssb_bus_pcmciabus_register);
 #endif /* CONFIG_SSB_PCMCIAHOST */
 
+#ifdef CONFIG_SSB_SDIOHOST
+int 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 ssb_bus_ssbbus_register(struct ssb_bus *bus,
                            unsigned long baseaddr,
                            ssb_invariants_func_t get_invariants)
@@ -1009,29 +1218,14 @@ u32 ssb_dma_translation(struct ssb_device *dev)
        case SSB_BUSTYPE_SSB:
                return 0;
        case SSB_BUSTYPE_PCI:
-       case SSB_BUSTYPE_PCMCIA:
                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;
@@ -1044,6 +1238,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)
@@ -1147,17 +1347,26 @@ 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;
 }
 /* ssb must be initialized after PCI but before the ssb drivers.
@@ -1167,6 +1376,7 @@ fs_initcall(ssb_modinit);
 
 static void __exit ssb_modexit(void)
 {
+       ssb_gige_exit();
        b43_pci_ssb_bridge_exit();
        bus_unregister(&ssb_bustype);
 }