--- /dev/null
+/*  linux/drivers/mmc/host/sdhci-pci.c - SDHCI on PCI bus interface
+ *
+ *  Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * Thanks to the following companies for their support:
+ *
+ *     - JMicron (hardware and technical support)
+ */
+
+#include <linux/delay.h>
+#include <linux/highmem.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/mmc/host.h>
+
+#include <asm/scatterlist.h>
+#include <asm/io.h>
+
+#include "sdhci.h"
+
+/*
+ * PCI registers
+ */
+
+#define PCI_SDHCI_IFPIO                        0x00
+#define PCI_SDHCI_IFDMA                        0x01
+#define PCI_SDHCI_IFVENDOR             0x02
+
+#define PCI_SLOT_INFO                  0x40    /* 8 bits */
+#define  PCI_SLOT_INFO_SLOTS(x)                ((x >> 4) & 7)
+#define  PCI_SLOT_INFO_FIRST_BAR_MASK  0x07
+
+#define MAX_SLOTS                      8
+
+static const struct pci_device_id pci_ids[] __devinitdata = {
+       {
+               .vendor         = PCI_VENDOR_ID_RICOH,
+               .device         = PCI_DEVICE_ID_RICOH_R5C822,
+               .subvendor      = PCI_VENDOR_ID_IBM,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = SDHCI_QUIRK_CLOCK_BEFORE_RESET |
+                                 SDHCI_QUIRK_FORCE_DMA,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_RICOH,
+               .device         = PCI_DEVICE_ID_RICOH_R5C822,
+               .subvendor      = PCI_VENDOR_ID_SAMSUNG,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = SDHCI_QUIRK_FORCE_DMA |
+                                 SDHCI_QUIRK_NO_CARD_NO_RESET,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_RICOH,
+               .device         = PCI_DEVICE_ID_RICOH_R5C822,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = SDHCI_QUIRK_FORCE_DMA,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_TI,
+               .device         = PCI_DEVICE_ID_TI_XX21_XX11_SD,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = SDHCI_QUIRK_FORCE_DMA,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_ENE,
+               .device         = PCI_DEVICE_ID_ENE_CB712_SD,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = SDHCI_QUIRK_SINGLE_POWER_WRITE |
+                                 SDHCI_QUIRK_BROKEN_DMA,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_ENE,
+               .device         = PCI_DEVICE_ID_ENE_CB712_SD_2,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = SDHCI_QUIRK_SINGLE_POWER_WRITE |
+                                 SDHCI_QUIRK_BROKEN_DMA,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_ENE,
+               .device         = PCI_DEVICE_ID_ENE_CB714_SD,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = SDHCI_QUIRK_SINGLE_POWER_WRITE |
+                                 SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS |
+                                 SDHCI_QUIRK_BROKEN_DMA,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_ENE,
+               .device         = PCI_DEVICE_ID_ENE_CB714_SD_2,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = SDHCI_QUIRK_SINGLE_POWER_WRITE |
+                                 SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS |
+                                 SDHCI_QUIRK_BROKEN_DMA,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_MARVELL,
+               .device         = PCI_DEVICE_ID_MARVELL_CAFE_SD,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER |
+                                 SDHCI_QUIRK_INCR_TIMEOUT_CONTROL,
+       },
+
+       {
+               .vendor         = PCI_VENDOR_ID_JMICRON,
+               .device         = PCI_DEVICE_ID_JMICRON_JMB38X_SD,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = SDHCI_QUIRK_32BIT_DMA_ADDR |
+                                 SDHCI_QUIRK_32BIT_DMA_SIZE |
+                                 SDHCI_QUIRK_RESET_AFTER_REQUEST,
+       },
+
+       {       /* Generic SD host controller */
+               PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
+       },
+
+       { /* end: all zeroes */ },
+};
+
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+struct sdhci_pci_chip;
+
+struct sdhci_pci_slot {
+       struct sdhci_pci_chip   *chip;
+       struct sdhci_host       *host;
+
+       int                     pci_bar;
+};
+
+struct sdhci_pci_chip {
+       struct pci_dev          *pdev;
+       unsigned int             quirks;
+
+       int                     num_slots;      /* Slots on controller */
+       struct sdhci_pci_slot   *slots[MAX_SLOTS]; /* Pointers to host slots */
+};
+
+/*****************************************************************************\
+ *                                                                           *
+ * SDHCI core callbacks                                                      *
+ *                                                                           *
+\*****************************************************************************/
+
+static int sdhci_pci_enable_dma(struct sdhci_host *host)
+{
+       struct sdhci_pci_slot *slot;
+       struct pci_dev *pdev;
+       int ret;
+
+       slot = sdhci_priv(host);
+       pdev = slot->chip->pdev;
+
+       if (((pdev->class & 0xFFFF00) == (PCI_CLASS_SYSTEM_SDHCI << 8)) &&
+               ((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA) &&
+               (host->flags & SDHCI_USE_DMA)) {
+               dev_warn(&pdev->dev, "Will use DMA mode even though HW "
+                       "doesn't fully claim to support it.\n");
+       }
+
+       ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+       if (ret)
+               return ret;
+
+       pci_set_master(pdev);
+
+       return 0;
+}
+
+static struct sdhci_ops sdhci_pci_ops = {
+       .enable_dma     = sdhci_pci_enable_dma,
+};
+
+/*****************************************************************************\
+ *                                                                           *
+ * Suspend/resume                                                            *
+ *                                                                           *
+\*****************************************************************************/
+
+#ifdef CONFIG_PM
+
+static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state)
+{
+       struct sdhci_pci_chip *chip;
+       struct sdhci_pci_slot *slot;
+       int i, ret;
+
+       chip = pci_get_drvdata(pdev);
+       if (!chip)
+               return 0;
+
+       for (i = 0;i < chip->num_slots;i++) {
+               slot = chip->slots[i];
+               if (!slot)
+                       continue;
+
+               ret = sdhci_suspend_host(slot->host, state);
+
+               if (ret) {
+                       for (i--;i >= 0;i--)
+                               sdhci_resume_host(chip->slots[i]->host);
+                       return ret;
+               }
+       }
+
+       pci_save_state(pdev);
+       pci_enable_wake(pdev, pci_choose_state(pdev, state), 0);
+       pci_disable_device(pdev);
+       pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+       return 0;
+}
+
+static int sdhci_pci_resume (struct pci_dev *pdev)
+{
+       struct sdhci_pci_chip *chip;
+       struct sdhci_pci_slot *slot;
+       int i, ret;
+
+       chip = pci_get_drvdata(pdev);
+       if (!chip)
+               return 0;
+
+       pci_set_power_state(pdev, PCI_D0);
+       pci_restore_state(pdev);
+       ret = pci_enable_device(pdev);
+       if (ret)
+               return ret;
+
+       for (i = 0;i < chip->num_slots;i++) {
+               slot = chip->slots[i];
+               if (!slot)
+                       continue;
+
+               ret = sdhci_resume_host(slot->host);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+#else /* CONFIG_PM */
+
+#define sdhci_pci_suspend NULL
+#define sdhci_pci_resume NULL
+
+#endif /* CONFIG_PM */
+
+/*****************************************************************************\
+ *                                                                           *
+ * Device probing/removal                                                    *
+ *                                                                           *
+\*****************************************************************************/
+
+static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
+       struct pci_dev *pdev, struct sdhci_pci_chip *chip, int bar)
+{
+       struct sdhci_pci_slot *slot;
+       struct sdhci_host *host;
+
+       resource_size_t addr;
+
+       int ret;
+
+       if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) {
+               dev_err(&pdev->dev, "BAR %d is not iomem. Aborting.\n", bar);
+               return ERR_PTR(-ENODEV);
+       }
+
+       if (pci_resource_len(pdev, bar) != 0x100) {
+               dev_err(&pdev->dev, "Invalid iomem size. You may "
+                       "experience problems.\n");
+       }
+
+       if ((pdev->class & 0x0000FF) == PCI_SDHCI_IFVENDOR) {
+               dev_err(&pdev->dev, "Vendor specific interface. Aborting.\n");
+               return ERR_PTR(-ENODEV);
+       }
+
+       if ((pdev->class & 0x0000FF) > PCI_SDHCI_IFVENDOR) {
+               dev_err(&pdev->dev, "Unknown interface. Aborting.\n");
+               return ERR_PTR(-ENODEV);
+       }
+
+       host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_pci_slot));
+       if (IS_ERR(host)) {
+               ret = PTR_ERR(host);
+               goto unmap;
+       }
+
+       slot = sdhci_priv(host);
+
+       slot->chip = chip;
+       slot->host = host;
+       slot->pci_bar = bar;
+
+       host->hw_name = "PCI";
+       host->ops = &sdhci_pci_ops;
+       host->quirks = chip->quirks;
+
+       host->irq = pdev->irq;
+
+       ret = pci_request_region(pdev, bar, mmc_hostname(host->mmc));
+       if (ret) {
+               dev_err(&pdev->dev, "cannot request region\n");
+               return ERR_PTR(ret);
+       }
+
+       addr = pci_resource_start(pdev, bar);
+       host->ioaddr = ioremap_nocache(addr, pci_resource_len(pdev, bar));
+       if (!host->ioaddr) {
+               dev_err(&pdev->dev, "failed to remap registers\n");
+               goto release;
+       }
+
+       ret = sdhci_add_host(host);
+       if (ret)
+               goto unmap;
+
+       return slot;
+
+unmap:
+       iounmap(host->ioaddr);
+
+release:
+       pci_release_region(pdev, bar);
+       sdhci_free_host(host);
+
+       return ERR_PTR(ret);
+}
+
+static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
+{
+       sdhci_remove_host(slot->host);
+       pci_release_region(slot->chip->pdev, slot->pci_bar);
+       sdhci_free_host(slot->host);
+}
+
+static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
+                                    const struct pci_device_id *ent)
+{
+       struct sdhci_pci_chip *chip;
+       struct sdhci_pci_slot *slot;
+
+       u8 slots, rev, first_bar;
+       int ret, i;
+
+       BUG_ON(pdev == NULL);
+       BUG_ON(ent == NULL);
+
+       pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev);
+
+       dev_info(&pdev->dev, "SDHCI controller found [%04x:%04x] (rev %x)\n",
+                (int)pdev->vendor, (int)pdev->device, (int)rev);
+
+       ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &slots);
+       if (ret)
+               return ret;
+
+       slots = PCI_SLOT_INFO_SLOTS(slots) + 1;
+       dev_dbg(&pdev->dev, "found %d slot(s)\n", slots);
+       if (slots == 0)
+               return -ENODEV;
+
+       BUG_ON(slots > MAX_SLOTS);
+
+       ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &first_bar);
+       if (ret)
+               return ret;
+
+       first_bar &= PCI_SLOT_INFO_FIRST_BAR_MASK;
+
+       if (first_bar > 5) {
+               dev_err(&pdev->dev, "Invalid first BAR. Aborting.\n");
+               return -ENODEV;
+       }
+
+       ret = pci_enable_device(pdev);
+       if (ret)
+               return ret;
+
+       chip = kzalloc(sizeof(struct sdhci_pci_chip), GFP_KERNEL);
+       if (!chip) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       chip->pdev = pdev;
+       chip->quirks = ent->driver_data;
+       chip->num_slots = slots;
+
+       pci_set_drvdata(pdev, chip);
+
+       for (i = 0;i < slots;i++) {
+               slot = sdhci_pci_probe_slot(pdev, chip, first_bar + i);
+               if (IS_ERR(slot)) {
+                       for (i--;i >= 0;i--)
+                               sdhci_pci_remove_slot(chip->slots[i]);
+                       ret = PTR_ERR(slot);
+                       goto free;
+               }
+
+               chip->slots[i] = slot;
+       }
+
+       return 0;
+
+free:
+       pci_set_drvdata(pdev, NULL);
+       kfree(chip);
+
+err:
+       pci_disable_device(pdev);
+       return ret;
+}
+
+static void __devexit sdhci_pci_remove(struct pci_dev *pdev)
+{
+       int i;
+       struct sdhci_pci_chip *chip;
+
+       chip = pci_get_drvdata(pdev);
+
+       if (chip) {
+               for (i = 0;i < chip->num_slots; i++)
+                       sdhci_pci_remove_slot(chip->slots[i]);
+
+               pci_set_drvdata(pdev, NULL);
+               kfree(chip);
+       }
+
+       pci_disable_device(pdev);
+}
+
+static struct pci_driver sdhci_driver = {
+       .name =         "sdhci-pci",
+       .id_table =     pci_ids,
+       .probe =        sdhci_pci_probe,
+       .remove =       __devexit_p(sdhci_pci_remove),
+       .suspend =      sdhci_pci_suspend,
+       .resume =       sdhci_pci_resume,
+};
+
+/*****************************************************************************\
+ *                                                                           *
+ * Driver init/exit                                                          *
+ *                                                                           *
+\*****************************************************************************/
+
+static int __init sdhci_drv_init(void)
+{
+       return pci_register_driver(&sdhci_driver);
+}
+
+static void __exit sdhci_drv_exit(void)
+{
+       pci_unregister_driver(&sdhci_driver);
+}
+
+module_init(sdhci_drv_init);
+module_exit(sdhci_drv_exit);
+
+MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>");
+MODULE_DESCRIPTION("Secure Digital Host Controller Interface PCI driver");
+MODULE_LICENSE("GPL");
 
 
 #include <linux/delay.h>
 #include <linux/highmem.h>
-#include <linux/pci.h>
+#include <linux/io.h>
 #include <linux/dma-mapping.h>
 #include <linux/scatterlist.h>
 
 
 static unsigned int debug_quirks = 0;
 
-/*
- * Different quirks to handle when the hardware deviates from a strict
- * interpretation of the SDHCI specification.
- */
-
-/* Controller doesn't honor resets unless we touch the clock register */
-#define SDHCI_QUIRK_CLOCK_BEFORE_RESET                 (1<<0)
-/* Controller has bad caps bits, but really supports DMA */
-#define SDHCI_QUIRK_FORCE_DMA                          (1<<1)
-/* Controller doesn't like to be reset when there is no card inserted. */
-#define SDHCI_QUIRK_NO_CARD_NO_RESET                   (1<<2)
-/* Controller doesn't like clearing the power reg before a change */
-#define SDHCI_QUIRK_SINGLE_POWER_WRITE                 (1<<3)
-/* Controller has flaky internal state so reset it on each ios change */
-#define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS              (1<<4)
-/* Controller has an unusable DMA engine */
-#define SDHCI_QUIRK_BROKEN_DMA                         (1<<5)
-/* Controller can only DMA from 32-bit aligned addresses */
-#define SDHCI_QUIRK_32BIT_DMA_ADDR                     (1<<6)
-/* Controller can only DMA chunk sizes that are a multiple of 32 bits */
-#define SDHCI_QUIRK_32BIT_DMA_SIZE                     (1<<7)
-/* Controller needs to be reset after each request to stay stable */
-#define SDHCI_QUIRK_RESET_AFTER_REQUEST                        (1<<8)
-/* Controller needs voltage and power writes to happen separately */
-#define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER            (1<<9)
-/* Controller has an off-by-one issue with timeout value */
-#define SDHCI_QUIRK_INCR_TIMEOUT_CONTROL               (1<<10)
-
-static const struct pci_device_id pci_ids[] __devinitdata = {
-       {
-               .vendor         = PCI_VENDOR_ID_RICOH,
-               .device         = PCI_DEVICE_ID_RICOH_R5C822,
-               .subvendor      = PCI_VENDOR_ID_IBM,
-               .subdevice      = PCI_ANY_ID,
-               .driver_data    = SDHCI_QUIRK_CLOCK_BEFORE_RESET |
-                                 SDHCI_QUIRK_FORCE_DMA,
-       },
-
-       {
-               .vendor         = PCI_VENDOR_ID_RICOH,
-               .device         = PCI_DEVICE_ID_RICOH_R5C822,
-               .subvendor      = PCI_VENDOR_ID_SAMSUNG,
-               .subdevice      = PCI_ANY_ID,
-               .driver_data    = SDHCI_QUIRK_FORCE_DMA |
-                                 SDHCI_QUIRK_NO_CARD_NO_RESET,
-       },
-
-       {
-               .vendor         = PCI_VENDOR_ID_RICOH,
-               .device         = PCI_DEVICE_ID_RICOH_R5C822,
-               .subvendor      = PCI_ANY_ID,
-               .subdevice      = PCI_ANY_ID,
-               .driver_data    = SDHCI_QUIRK_FORCE_DMA,
-       },
-
-       {
-               .vendor         = PCI_VENDOR_ID_TI,
-               .device         = PCI_DEVICE_ID_TI_XX21_XX11_SD,
-               .subvendor      = PCI_ANY_ID,
-               .subdevice      = PCI_ANY_ID,
-               .driver_data    = SDHCI_QUIRK_FORCE_DMA,
-       },
-
-       {
-               .vendor         = PCI_VENDOR_ID_ENE,
-               .device         = PCI_DEVICE_ID_ENE_CB712_SD,
-               .subvendor      = PCI_ANY_ID,
-               .subdevice      = PCI_ANY_ID,
-               .driver_data    = SDHCI_QUIRK_SINGLE_POWER_WRITE |
-                                 SDHCI_QUIRK_BROKEN_DMA,
-       },
-
-       {
-               .vendor         = PCI_VENDOR_ID_ENE,
-               .device         = PCI_DEVICE_ID_ENE_CB712_SD_2,
-               .subvendor      = PCI_ANY_ID,
-               .subdevice      = PCI_ANY_ID,
-               .driver_data    = SDHCI_QUIRK_SINGLE_POWER_WRITE |
-                                 SDHCI_QUIRK_BROKEN_DMA,
-       },
-
-       {
-               .vendor         = PCI_VENDOR_ID_ENE,
-               .device         = PCI_DEVICE_ID_ENE_CB714_SD,
-               .subvendor      = PCI_ANY_ID,
-               .subdevice      = PCI_ANY_ID,
-               .driver_data    = SDHCI_QUIRK_SINGLE_POWER_WRITE |
-                                 SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS |
-                                 SDHCI_QUIRK_BROKEN_DMA,
-       },
-
-       {
-               .vendor         = PCI_VENDOR_ID_ENE,
-               .device         = PCI_DEVICE_ID_ENE_CB714_SD_2,
-               .subvendor      = PCI_ANY_ID,
-               .subdevice      = PCI_ANY_ID,
-               .driver_data    = SDHCI_QUIRK_SINGLE_POWER_WRITE |
-                                 SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS |
-                                 SDHCI_QUIRK_BROKEN_DMA,
-       },
-
-       {
-               .vendor         = PCI_VENDOR_ID_MARVELL,
-               .device         = PCI_DEVICE_ID_MARVELL_CAFE_SD,
-               .subvendor      = PCI_ANY_ID,
-               .subdevice      = PCI_ANY_ID,
-               .driver_data    = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER |
-                                 SDHCI_QUIRK_INCR_TIMEOUT_CONTROL,
-       },
-
-       {
-               .vendor         = PCI_VENDOR_ID_JMICRON,
-               .device         = PCI_DEVICE_ID_JMICRON_JMB38X_SD,
-               .subvendor      = PCI_ANY_ID,
-               .subdevice      = PCI_ANY_ID,
-               .driver_data    = SDHCI_QUIRK_32BIT_DMA_ADDR |
-                                 SDHCI_QUIRK_32BIT_DMA_SIZE |
-                                 SDHCI_QUIRK_RESET_AFTER_REQUEST,
-       },
-
-       {       /* Generic SD host controller */
-               PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
-       },
-
-       { /* end: all zeroes */ },
-};
-
-MODULE_DEVICE_TABLE(pci, pci_ids);
-
 static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data *);
 static void sdhci_finish_data(struct sdhci_host *);
 
 {
        unsigned long timeout;
 
-       if (host->chip->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
+       if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
                if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) &
                        SDHCI_CARD_PRESENT))
                        return;
         * Compensate for an off-by-one error in the CaFe hardware; otherwise,
         * a too-small count gives us interrupt timeouts.
         */
-       if ((host->chip->quirks & SDHCI_QUIRK_INCR_TIMEOUT_CONTROL))
+       if ((host->quirks & SDHCI_QUIRK_INCR_TIMEOUT_CONTROL))
                count++;
 
        if (count >= 0xF) {
                host->flags |= SDHCI_REQ_USE_DMA;
 
        if (unlikely((host->flags & SDHCI_REQ_USE_DMA) &&
-               (host->chip->quirks & SDHCI_QUIRK_32BIT_DMA_SIZE) &&
+               (host->quirks & SDHCI_QUIRK_32BIT_DMA_SIZE) &&
                ((data->blksz * data->blocks) & 0x3))) {
                DBG("Reverting to PIO because of transfer size (%d)\n",
                        data->blksz * data->blocks);
         * translation to device address space.
         */
        if (unlikely((host->flags & SDHCI_REQ_USE_DMA) &&
-               (host->chip->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) &&
+               (host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) &&
                (data->sg->offset & 0x3))) {
                DBG("Reverting to PIO because of bad alignment\n");
                host->flags &= ~SDHCI_REQ_USE_DMA;
        if (host->flags & SDHCI_REQ_USE_DMA) {
                int count;
 
-               count = pci_map_sg(host->chip->pdev, data->sg, data->sg_len,
-                       (data->flags & MMC_DATA_READ)?PCI_DMA_FROMDEVICE:PCI_DMA_TODEVICE);
-               BUG_ON(count != 1);
+               count = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+                               (data->flags & MMC_DATA_READ) ?
+                                       DMA_FROM_DEVICE : DMA_TO_DEVICE);
+               WARN_ON(count != 1);
 
-               writel(sg_dma_address(data->sg), host->ioaddr + SDHCI_DMA_ADDRESS);
+               writel(sg_dma_address(data->sg),
+                       host->ioaddr + SDHCI_DMA_ADDRESS);
        } else {
                host->cur_sg = data->sg;
                host->num_sg = data->sg_len;
        host->data = NULL;
 
        if (host->flags & SDHCI_REQ_USE_DMA) {
-               pci_unmap_sg(host->chip->pdev, data->sg, data->sg_len,
-                       (data->flags & MMC_DATA_READ)?PCI_DMA_FROMDEVICE:PCI_DMA_TODEVICE);
+               dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+                       (data->flags & MMC_DATA_READ) ?
+                               DMA_FROM_DEVICE : DMA_TO_DEVICE);
        }
 
        /*
         * Spec says that we should clear the power reg before setting
         * a new value. Some controllers don't seem to like this though.
         */
-       if (!(host->chip->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE))
+       if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE))
                writeb(0, host->ioaddr + SDHCI_POWER_CONTROL);
 
        pwr = SDHCI_POWER_ON;
         * At least the CaFe chip gets confused if we set the voltage
         * and set turn on power at the same time, so set the voltage first.
         */
-       if ((host->chip->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER))
+       if ((host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER))
                writeb(pwr & ~SDHCI_POWER_ON,
                                host->ioaddr + SDHCI_POWER_CONTROL);
 
         * signalling timeout and CRC errors even on CMD0. Resetting
         * it on each ios seems to solve the problem.
         */
-       if(host->chip->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS)
+       if(host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS)
                sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
 
        mmiowb();
        if (mrq->cmd->error ||
                (mrq->data && (mrq->data->error ||
                (mrq->data->stop && mrq->data->stop->error))) ||
-               (host->chip->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)) {
+               (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)) {
 
                /* Some controllers need this kick or reset won't work here */
-               if (host->chip->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) {
+               if (host->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) {
                        unsigned int clock;
 
                        /* This is to force an update */
 
 #ifdef CONFIG_PM
 
-static int sdhci_suspend (struct pci_dev *pdev, pm_message_t state)
+int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state)
 {
-       struct sdhci_chip *chip;
-       int i, ret;
-
-       chip = pci_get_drvdata(pdev);
-       if (!chip)
-               return 0;
-
-       DBG("Suspending...\n");
-
-       for (i = 0;i < chip->num_slots;i++) {
-               if (!chip->hosts[i])
-                       continue;
-               ret = mmc_suspend_host(chip->hosts[i]->mmc, state);
-               if (ret) {
-                       for (i--;i >= 0;i--)
-                               mmc_resume_host(chip->hosts[i]->mmc);
-                       return ret;
-               }
-       }
-
-       pci_save_state(pdev);
-       pci_enable_wake(pdev, pci_choose_state(pdev, state), 0);
+       int ret;
 
-       for (i = 0;i < chip->num_slots;i++) {
-               if (!chip->hosts[i])
-                       continue;
-               free_irq(chip->hosts[i]->irq, chip->hosts[i]);
-       }
+       ret = mmc_suspend_host(host->mmc, state);
+       if (ret)
+               return ret;
 
-       pci_disable_device(pdev);
-       pci_set_power_state(pdev, pci_choose_state(pdev, state));
+       free_irq(host->irq, host);
 
        return 0;
 }
 
-static int sdhci_resume (struct pci_dev *pdev)
-{
-       struct sdhci_chip *chip;
-       int i, ret;
+EXPORT_SYMBOL_GPL(sdhci_suspend_host);
 
-       chip = pci_get_drvdata(pdev);
-       if (!chip)
-               return 0;
+int sdhci_resume_host(struct sdhci_host *host)
+{
+       int ret;
 
-       DBG("Resuming...\n");
+       if (host->flags & SDHCI_USE_DMA) {
+               if (host->ops->enable_dma)
+                       host->ops->enable_dma(host);
+       }
 
-       pci_set_power_state(pdev, PCI_D0);
-       pci_restore_state(pdev);
-       ret = pci_enable_device(pdev);
+       ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
+                         mmc_hostname(host->mmc), host);
        if (ret)
                return ret;
 
-       for (i = 0;i < chip->num_slots;i++) {
-               if (!chip->hosts[i])
-                       continue;
-               if (chip->hosts[i]->flags & SDHCI_USE_DMA)
-                       pci_set_master(pdev);
-               ret = request_irq(chip->hosts[i]->irq, sdhci_irq,
-                       IRQF_SHARED, mmc_hostname(chip->hosts[i]->mmc),
-                       chip->hosts[i]);
-               if (ret)
-                       return ret;
-               sdhci_init(chip->hosts[i]);
-               mmiowb();
-               ret = mmc_resume_host(chip->hosts[i]->mmc);
-               if (ret)
-                       return ret;
-       }
+       sdhci_init(host);
+       mmiowb();
+
+       ret = mmc_resume_host(host->mmc);
+       if (ret)
+               return ret;
 
        return 0;
 }
 
-#else /* CONFIG_PM */
-
-#define sdhci_suspend NULL
-#define sdhci_resume NULL
+EXPORT_SYMBOL_GPL(sdhci_resume_host);
 
 #endif /* CONFIG_PM */
 
 /*****************************************************************************\
  *                                                                           *
- * Device probing/removal                                                    *
+ * Device allocation/registration                                            *
  *                                                                           *
 \*****************************************************************************/
 
-static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
+struct sdhci_host *sdhci_alloc_host(struct device *dev,
+       size_t priv_size)
 {
-       int ret;
-       unsigned int version;
-       struct sdhci_chip *chip;
        struct mmc_host *mmc;
        struct sdhci_host *host;
 
-       u8 first_bar;
-       unsigned int caps;
-
-       chip = pci_get_drvdata(pdev);
-       BUG_ON(!chip);
-
-       ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &first_bar);
-       if (ret)
-               return ret;
-
-       first_bar &= PCI_SLOT_INFO_FIRST_BAR_MASK;
+       WARN_ON(dev == NULL);
 
-       if (first_bar > 5) {
-               printk(KERN_ERR DRIVER_NAME ": Invalid first BAR. Aborting.\n");
-               return -ENODEV;
-       }
-
-       if (!(pci_resource_flags(pdev, first_bar + slot) & IORESOURCE_MEM)) {
-               printk(KERN_ERR DRIVER_NAME ": BAR is not iomem. Aborting.\n");
-               return -ENODEV;
-       }
-
-       if (pci_resource_len(pdev, first_bar + slot) != 0x100) {
-               printk(KERN_ERR DRIVER_NAME ": Invalid iomem size. "
-                       "You may experience problems.\n");
-       }
-
-       if ((pdev->class & 0x0000FF) == PCI_SDHCI_IFVENDOR) {
-               printk(KERN_ERR DRIVER_NAME ": Vendor specific interface. Aborting.\n");
-               return -ENODEV;
-       }
-
-       if ((pdev->class & 0x0000FF) > PCI_SDHCI_IFVENDOR) {
-               printk(KERN_ERR DRIVER_NAME ": Unknown interface. Aborting.\n");
-               return -ENODEV;
-       }
-
-       mmc = mmc_alloc_host(sizeof(struct sdhci_host), &pdev->dev);
+       mmc = mmc_alloc_host(sizeof(struct sdhci_host) + priv_size, dev);
        if (!mmc)
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
 
        host = mmc_priv(mmc);
        host->mmc = mmc;
 
-       host->chip = chip;
-       chip->hosts[slot] = host;
+       return host;
+}
 
-       host->bar = first_bar + slot;
+EXPORT_SYMBOL_GPL(sdhci_alloc_host);
 
-       host->addr = pci_resource_start(pdev, host->bar);
-       host->irq = pdev->irq;
+int sdhci_add_host(struct sdhci_host *host)
+{
+       struct mmc_host *mmc;
+       unsigned int caps;
+       unsigned int version;
+       int ret;
 
-       DBG("slot %d at 0x%08lx, irq %d\n", slot, host->addr, host->irq);
+       WARN_ON(host == NULL);
+       if (host == NULL)
+               return -EINVAL;
 
-       ret = pci_request_region(pdev, host->bar, mmc_hostname(mmc));
-       if (ret)
-               goto free;
+       mmc = host->mmc;
 
-       host->ioaddr = ioremap_nocache(host->addr,
-               pci_resource_len(pdev, host->bar));
-       if (!host->ioaddr) {
-               ret = -ENOMEM;
-               goto release;
-       }
+       if (debug_quirks)
+               host->quirks = debug_quirks;
 
        sdhci_reset(host, SDHCI_RESET_ALL);
 
 
        caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
 
-       if (chip->quirks & SDHCI_QUIRK_FORCE_DMA)
+       if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
                host->flags |= SDHCI_USE_DMA;
        else if (!(caps & SDHCI_CAN_DO_DMA))
                DBG("Controller doesn't have DMA capability\n");
        else
                host->flags |= SDHCI_USE_DMA;
 
-       if ((chip->quirks & SDHCI_QUIRK_BROKEN_DMA) &&
+       if ((host->quirks & SDHCI_QUIRK_BROKEN_DMA) &&
                (host->flags & SDHCI_USE_DMA)) {
                DBG("Disabling DMA as it is marked broken\n");
                host->flags &= ~SDHCI_USE_DMA;
        }
 
-       if (((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA) &&
-               (host->flags & SDHCI_USE_DMA)) {
-               printk(KERN_WARNING "%s: Will use DMA "
-                       "mode even though HW doesn't fully "
-                       "claim to support it.\n", mmc_hostname(mmc));
-       }
-
        if (host->flags & SDHCI_USE_DMA) {
-               if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
-                       printk(KERN_WARNING "%s: No suitable DMA available. "
-                               "Falling back to PIO.\n", mmc_hostname(mmc));
-                       host->flags &= ~SDHCI_USE_DMA;
+               if (host->ops->enable_dma) {
+                       if (host->ops->enable_dma(host)) {
+                               printk(KERN_WARNING "%s: No suitable DMA "
+                                       "available. Falling back to PIO.\n",
+                                       mmc_hostname(mmc));
+                               host->flags &= ~SDHCI_USE_DMA;
+                       }
                }
        }
 
-       if (host->flags & SDHCI_USE_DMA)
-               pci_set_master(pdev);
-       else /* XXX: Hack to get MMC layer to avoid highmem */
-               pdev->dma_mask = 0;
+       /* XXX: Hack to get MMC layer to avoid highmem */
+       if (!(host->flags & SDHCI_USE_DMA))
+               mmc_dev(host->mmc)->dma_mask = 0;
 
        host->max_clk =
                (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;
        if (host->max_clk == 0) {
                printk(KERN_ERR "%s: Hardware doesn't specify base clock "
                        "frequency.\n", mmc_hostname(mmc));
-               ret = -ENODEV;
-               goto unmap;
+               return -ENODEV;
        }
        host->max_clk *= 1000000;
 
        if (host->timeout_clk == 0) {
                printk(KERN_ERR "%s: Hardware doesn't specify timeout clock "
                        "frequency.\n", mmc_hostname(mmc));
-               ret = -ENODEV;
-               goto unmap;
+               return -ENODEV;
        }
        if (caps & SDHCI_TIMEOUT_CLK_UNIT)
                host->timeout_clk *= 1000;
        if (mmc->ocr_avail == 0) {
                printk(KERN_ERR "%s: Hardware doesn't report any "
                        "support voltages.\n", mmc_hostname(mmc));
-               ret = -ENODEV;
-               goto unmap;
+               return -ENODEV;
        }
 
        spin_lock_init(&host->lock);
        host->led.default_trigger = mmc_hostname(mmc);
        host->led.brightness_set = sdhci_led_control;
 
-       ret = led_classdev_register(&pdev->dev, &host->led);
+       ret = led_classdev_register(mmc_dev(mmc), &host->led);
        if (ret)
                goto reset;
 #endif
 
        mmc_add_host(mmc);
 
-       printk(KERN_INFO "%s: SDHCI at 0x%08lx irq %d %s\n",
-               mmc_hostname(mmc), host->addr, host->irq,
+       printk(KERN_INFO "%s: SDHCI controller on %s [%s] using %s\n",
+               mmc_hostname(mmc), host->hw_name, mmc_dev(mmc)->bus_id,
                (host->flags & SDHCI_USE_DMA)?"DMA":"PIO");
 
        return 0;
 untasklet:
        tasklet_kill(&host->card_tasklet);
        tasklet_kill(&host->finish_tasklet);
-unmap:
-       iounmap(host->ioaddr);
-release:
-       pci_release_region(pdev, host->bar);
-free:
-       mmc_free_host(mmc);
 
        return ret;
 }
 
-static void sdhci_remove_slot(struct pci_dev *pdev, int slot)
-{
-       struct sdhci_chip *chip;
-       struct mmc_host *mmc;
-       struct sdhci_host *host;
-
-       chip = pci_get_drvdata(pdev);
-       host = chip->hosts[slot];
-       mmc = host->mmc;
-
-       chip->hosts[slot] = NULL;
+EXPORT_SYMBOL_GPL(sdhci_add_host);
 
-       mmc_remove_host(mmc);
+void sdhci_remove_host(struct sdhci_host *host)
+{
+       mmc_remove_host(host->mmc);
 
 #ifdef CONFIG_LEDS_CLASS
        led_classdev_unregister(&host->led);
 
        tasklet_kill(&host->card_tasklet);
        tasklet_kill(&host->finish_tasklet);
-
-       iounmap(host->ioaddr);
-
-       pci_release_region(pdev, host->bar);
-
-       mmc_free_host(mmc);
 }
 
-static int __devinit sdhci_probe(struct pci_dev *pdev,
-       const struct pci_device_id *ent)
-{
-       int ret, i;
-       u8 slots, rev;
-       struct sdhci_chip *chip;
-
-       BUG_ON(pdev == NULL);
-       BUG_ON(ent == NULL);
-
-       pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev);
-
-       printk(KERN_INFO DRIVER_NAME
-               ": SDHCI controller found at %s [%04x:%04x] (rev %x)\n",
-               pci_name(pdev), (int)pdev->vendor, (int)pdev->device,
-               (int)rev);
-
-       ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &slots);
-       if (ret)
-               return ret;
-
-       slots = PCI_SLOT_INFO_SLOTS(slots) + 1;
-       DBG("found %d slot(s)\n", slots);
-       if (slots == 0)
-               return -ENODEV;
+EXPORT_SYMBOL_GPL(sdhci_remove_host);
 
-       ret = pci_enable_device(pdev);
-       if (ret)
-               return ret;
-
-       chip = kzalloc(sizeof(struct sdhci_chip) +
-               sizeof(struct sdhci_host*) * slots, GFP_KERNEL);
-       if (!chip) {
-               ret = -ENOMEM;
-               goto err;
-       }
-
-       chip->pdev = pdev;
-       chip->quirks = ent->driver_data;
-
-       if (debug_quirks)
-               chip->quirks = debug_quirks;
-
-       chip->num_slots = slots;
-       pci_set_drvdata(pdev, chip);
-
-       for (i = 0;i < slots;i++) {
-               ret = sdhci_probe_slot(pdev, i);
-               if (ret) {
-                       for (i--;i >= 0;i--)
-                               sdhci_remove_slot(pdev, i);
-                       goto free;
-               }
-       }
-
-       return 0;
-
-free:
-       pci_set_drvdata(pdev, NULL);
-       kfree(chip);
-
-err:
-       pci_disable_device(pdev);
-       return ret;
-}
-
-static void __devexit sdhci_remove(struct pci_dev *pdev)
+void sdhci_free_host(struct sdhci_host *host)
 {
-       int i;
-       struct sdhci_chip *chip;
-
-       chip = pci_get_drvdata(pdev);
-
-       if (chip) {
-               for (i = 0;i < chip->num_slots;i++)
-                       sdhci_remove_slot(pdev, i);
-
-               pci_set_drvdata(pdev, NULL);
-
-               kfree(chip);
-       }
-
-       pci_disable_device(pdev);
+       mmc_free_host(host->mmc);
 }
 
-static struct pci_driver sdhci_driver = {
-       .name =         DRIVER_NAME,
-       .id_table =     pci_ids,
-       .probe =        sdhci_probe,
-       .remove =       __devexit_p(sdhci_remove),
-       .suspend =      sdhci_suspend,
-       .resume =       sdhci_resume,
-};
+EXPORT_SYMBOL_GPL(sdhci_free_host);
 
 /*****************************************************************************\
  *                                                                           *
                ": Secure Digital Host Controller Interface driver\n");
        printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
 
-       return pci_register_driver(&sdhci_driver);
+       return 0;
 }
 
 static void __exit sdhci_drv_exit(void)
 {
-       DBG("Exiting\n");
-
-       pci_unregister_driver(&sdhci_driver);
 }
 
 module_init(sdhci_drv_init);
 module_param(debug_quirks, uint, 0444);
 
 MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>");
-MODULE_DESCRIPTION("Secure Digital Host Controller Interface driver");
+MODULE_DESCRIPTION("Secure Digital Host Controller Interface core driver");
 MODULE_LICENSE("GPL");
 
 MODULE_PARM_DESC(debug_quirks, "Force certain quirks.");