sys/dev/disk/sdhci: update to FreeBSD r270885
authorMarkus Pfeiffer <profmakx@dragonflybsd.org>
Tue, 9 Sep 2014 19:08:42 +0000 (20:08 +0100)
committerMarkus Pfeiffer <markus.pfeiffer@morphism.de>
Tue, 9 Sep 2014 22:08:34 +0000 (22:08 +0000)
sys/bus/mmc/mmc.c
sys/conf/files
sys/dev/disk/sdhci/sdhci.c
sys/dev/disk/sdhci/sdhci.h
sys/dev/disk/sdhci/sdhci_if.m [new file with mode: 0644]
sys/dev/disk/sdhci/sdhci_pci.c [new file with mode: 0644]

index 3365a63..306b6d7 100644 (file)
@@ -1523,4 +1523,4 @@ static driver_t mmc_driver = {
 static devclass_t mmc_devclass;
 
 
-DRIVER_MODULE(mmc, sdhci, mmc_driver, mmc_devclass, NULL, NULL);
+DRIVER_MODULE(mmc, sdhci_pci, mmc_driver, mmc_devclass, NULL, NULL);
index abd0165..f5c6f95 100644 (file)
@@ -775,7 +775,9 @@ dev/serial/rp/rp.c          optional nrp
 dev/serial/rp/rp_pci.c         optional nrp pci
 dev/crypto/safe/safe.c         optional safe
 dev/netif/sbsh/if_sbsh.c       optional sbsh
-dev/disk/sdhci/sdhci.c         optional sdhci pci
+dev/disk/sdhci/sdhci.c         optional sdhci
+dev/disk/sdhci/sdhci_if.m      optional sdhci
+dev/disk/sdhci/sdhci_pci.c     optional sdhci pci
 bus/smbus/smbus_if.m           optional smbus
 bus/smbus/smbconf.c            optional smbus
 bus/smbus/smbus.c              optional smbus
index ea352a9..2a9aef6 100644 (file)
@@ -30,6 +30,7 @@
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/bus.h>
+#include <sys/callout.h>
 #include <sys/conf.h>
 #include <sys/kernel.h>
 #include <sys/lock.h>
 #include <sys/sysctl.h>
 #include <sys/taskqueue.h>
 
-#include <bus/pci/pcireg.h>
-#include <bus/pci/pcivar.h>
-
 #include <bus/mmc/bridge.h>
 #include <bus/mmc/mmcreg.h>
 #include <bus/mmc/mmcbrvar.h>
 
 #include "mmcbr_if.h"
 #include "sdhci.h"
-
-#define DMA_BLOCK_SIZE 4096
-#define DMA_BOUNDARY   0       /* DMA reload every 4K */
-
-/* Controller doesn't honor resets unless we touch the clock register */
-#define SDHCI_QUIRK_CLOCK_BEFORE_RESET                 (1<<0)
-/* Controller really supports DMA */
-#define SDHCI_QUIRK_FORCE_DMA                          (1<<1)
-/* Controller has unusable DMA engine */
-#define SDHCI_QUIRK_BROKEN_DMA                         (1<<2)
-/* Controller doesn't like to be reset when there is no card inserted. */
-#define SDHCI_QUIRK_NO_CARD_NO_RESET                   (1<<3)
-/* Controller has flaky internal state so reset it on each ios change */
-#define SDHCI_QUIRK_RESET_ON_IOS                       (1<<4)
-/* Controller can only DMA chunk sizes that are a multiple of 32 bits */
-#define SDHCI_QUIRK_32BIT_DMA_SIZE                     (1<<5)
-/* Controller needs to be reset after each request to stay stable */
-#define SDHCI_QUIRK_RESET_AFTER_REQUEST                        (1<<6)
-/* Controller has an off-by-one issue with timeout value */
-#define SDHCI_QUIRK_INCR_TIMEOUT_CONTROL               (1<<7)
-/* Controller has broken read timings */
-#define SDHCI_QUIRK_BROKEN_TIMINGS                     (1<<8)
-
-static const struct sdhci_device {
-       uint32_t        model;
-       uint16_t        subvendor;
-       char            *desc;
-       u_int           quirks;
-} sdhci_devices[] = {
-       { 0x08221180,   0xffff, "RICOH R5C822 SD",
-           SDHCI_QUIRK_FORCE_DMA },
-       { 0x8034104c,   0xffff, "TI XX21/XX11 SD",
-           SDHCI_QUIRK_FORCE_DMA },
-       { 0x05501524,   0xffff, "ENE CB712 SD",
-           SDHCI_QUIRK_BROKEN_TIMINGS },
-       { 0x05511524,   0xffff, "ENE CB712 SD 2",
-           SDHCI_QUIRK_BROKEN_TIMINGS },
-       { 0x07501524,   0xffff, "ENE CB714 SD",
-           SDHCI_QUIRK_RESET_ON_IOS |
-           SDHCI_QUIRK_BROKEN_TIMINGS },
-       { 0x07511524,   0xffff, "ENE CB714 SD 2",
-           SDHCI_QUIRK_RESET_ON_IOS |
-           SDHCI_QUIRK_BROKEN_TIMINGS },
-       { 0x410111ab,   0xffff, "Marvell CaFe SD",
-           SDHCI_QUIRK_INCR_TIMEOUT_CONTROL },
-       { 0x2381197B,   0xffff, "JMicron JMB38X SD",
-           SDHCI_QUIRK_32BIT_DMA_SIZE |
-           SDHCI_QUIRK_RESET_AFTER_REQUEST },
-       { 0,            0xffff, NULL,
-           0 }
-};
-
-struct sdhci_softc;
-
-struct sdhci_slot {
-       struct sdhci_softc      *sc;
-       device_t        dev;            /* Slot device */
-       u_char          num;            /* Slot number */
-       u_char          opt;            /* Slot options */
-#define SDHCI_HAVE_DMA         1
-       uint32_t        max_clk;        /* Max possible freq */
-       uint32_t        timeout_clk;    /* Timeout freq */
-       struct resource *mem_res;       /* Memory resource */
-       int             mem_rid;
-       bus_dma_tag_t   dmatag;
-       bus_dmamap_t    dmamap;
-       u_char          *dmamem;
-       bus_addr_t      paddr;          /* DMA buffer address */
-       struct task     card_task;      /* Card presence check task */
-       struct callout  card_callout;   /* Card insert delay callout */
-       struct mmc_host host;           /* Host parameters */
-       struct mmc_request *req;        /* Current request */
-       struct mmc_command *curcmd;     /* Current command of current request */
-
-       uint32_t        intmask;        /* Current interrupt mask */
-       uint32_t        clock;          /* Current clock freq. */
-       size_t          offset;         /* Data buffer offset */
-       uint8_t         hostctrl;       /* Current host control register */
-       u_char          power;          /* Current power */
-       u_char          bus_busy;       /* Bus busy status */
-       u_char          cmd_done;       /* CMD command part done flag */
-       u_char          data_done;      /* DAT command part done flag */
-       u_char          flags;          /* Request execution flags */
-#define CMD_STARTED            1
-#define STOP_STARTED           2
-#define SDHCI_USE_DMA          4       /* Use DMA for this req. */
-       struct lock     lock;           /* Slot lock */
-};
-
-struct sdhci_softc {
-       device_t        dev;            /* Controller device */
-       u_int           quirks;         /* Chip specific quirks */
-       struct resource *irq_res;       /* IRQ resource */
-       int             irq_rid;
-       void            *intrhand;      /* Interrupt handle */
-
-       int             num_slots;      /* Number of slots on this controller */
-       struct sdhci_slot slots[6];
-};
+#include "sdhci_if.h"
 
 SYSCTL_NODE(_hw, OID_AUTO, sdhci, CTLFLAG_RD, 0, "sdhci driver");
 
-int    sdhci_debug;
+int    sdhci_debug = 0;
 TUNABLE_INT("hw.sdhci.debug", &sdhci_debug);
 SYSCTL_INT(_hw_sdhci, OID_AUTO, debug, CTLFLAG_RW, &sdhci_debug, 0, "Debug level");
 
-static inline uint8_t
-RD1(struct sdhci_slot *slot, bus_size_t off)
-{
-       bus_barrier(slot->mem_res, 0, 0xFF,
-           BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
-       return bus_read_1(slot->mem_res, off);
-}
-
-static inline void
-WR1(struct sdhci_slot *slot, bus_size_t off, uint8_t val)
-{
-       bus_barrier(slot->mem_res, 0, 0xFF,
-           BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
-       bus_write_1(slot->mem_res, off, val);
-}
-
-static inline uint16_t
-RD2(struct sdhci_slot *slot, bus_size_t off)
-{
-       bus_barrier(slot->mem_res, 0, 0xFF,
-           BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
-       return bus_read_2(slot->mem_res, off);
-}
-
-static inline void
-WR2(struct sdhci_slot *slot, bus_size_t off, uint16_t val)
-{
-       bus_barrier(slot->mem_res, 0, 0xFF,
-           BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
-       bus_write_2(slot->mem_res, off, val);
-}
-
-static inline uint32_t
-RD4(struct sdhci_slot *slot, bus_size_t off)
-{
-       bus_barrier(slot->mem_res, 0, 0xFF,
-           BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
-       return bus_read_4(slot->mem_res, off);
-}
-
-static inline void
-WR4(struct sdhci_slot *slot, bus_size_t off, uint32_t val)
-{
-       bus_barrier(slot->mem_res, 0, 0xFF,
-           BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
-       bus_write_4(slot->mem_res, off, val);
-}
+#define RD1(slot, off) SDHCI_READ_1((slot)->bus, (slot), (off))
+#define RD2(slot, off) SDHCI_READ_2((slot)->bus, (slot), (off))
+#define RD4(slot, off) SDHCI_READ_4((slot)->bus, (slot), (off))
+#define RD_MULTI_4(slot, off, ptr, count)      \
+    SDHCI_READ_MULTI_4((slot)->bus, (slot), (off), (ptr), (count))
+#define WR1(slot, off, val)    SDHCI_WRITE_1((slot)->bus, (slot), (off), (val))
+#define WR2(slot, off, val)    SDHCI_WRITE_2((slot)->bus, (slot), (off), (val))
+#define WR4(slot, off, val)    SDHCI_WRITE_4((slot)->bus, (slot), (off), (val))
+#define WR_MULTI_4(slot, off, ptr, count)      \
+    SDHCI_WRITE_MULTI_4((slot)->bus, (slot), (off), (ptr), (count))
 
 static int slot_printf(struct sdhci_slot *, const char *, ...)
               __printflike(2, 3);
 
-/* bus entry points */
-static int sdhci_probe(device_t dev);
-static int sdhci_attach(device_t dev);
-static int sdhci_detach(device_t dev);
-static void sdhci_intr(void *);
-
 static void sdhci_set_clock(struct sdhci_slot *slot, uint32_t clock);
 static void sdhci_start(struct sdhci_slot *slot);
 static void sdhci_start_data(struct sdhci_slot *slot, struct mmc_data *data);
@@ -226,6 +83,21 @@ static void sdhci_card_task(void *, int);
 #define SDHCI_ASSERT_LOCKED(_slot)     KKASSERT(lockstatus(&(_slot)->lock, curthread) != 0);
 #define SDHCI_ASSERT_UNLOCKED(_slot)   KKASSERT(lockstatus(&(_slot)->lock, curthread) == 0);
 
+#define        SDHCI_DEFAULT_MAX_FREQ  50
+
+#define        SDHCI_200_MAX_DIVIDER   256
+#define        SDHCI_300_MAX_DIVIDER   2046
+
+static void
+sdhci_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+       if (error != 0) {
+               kprintf("getaddr: error %d\n", error);
+               return;
+       }
+       *(bus_addr_t *)arg = segs[0].ds_addr;
+}
+
 static int
 slot_printf(struct sdhci_slot *slot, const char * fmt, ...)
 {
@@ -233,7 +105,7 @@ slot_printf(struct sdhci_slot *slot, const char * fmt, ...)
        int retval;
 
        retval = kprintf("%s-slot%d: ",
-           device_get_nameunit(slot->sc->dev), slot->num);
+           device_get_nameunit(slot->bus), slot->num);
 
        va_start(ap, fmt);
        retval += kvprintf(fmt, ap);
@@ -241,16 +113,6 @@ slot_printf(struct sdhci_slot *slot, const char * fmt, ...)
        return (retval);
 }
 
-static void
-sdhci_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
-{
-       if (error != 0) {
-               kprintf("getaddr: error %d\n", error);
-               return;
-       }
-       *(bus_addr_t *)arg = segs[0].ds_addr;
-}
-
 static void
 sdhci_dumpregs(struct sdhci_slot *slot)
 {
@@ -288,7 +150,7 @@ sdhci_reset(struct sdhci_slot *slot, uint8_t mask)
        int timeout;
        uint8_t res;
 
-       if (slot->sc->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
+       if (slot->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
                if (!(RD4(slot, SDHCI_PRESENT_STATE) &
                        SDHCI_CARD_PRESENT))
                        return;
@@ -296,7 +158,7 @@ sdhci_reset(struct sdhci_slot *slot, uint8_t mask)
 
        /* Some controllers need this kick or reset won't work. */
        if ((mask & SDHCI_RESET_ALL) == 0 &&
-           (slot->sc->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET)) {
+           (slot->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET)) {
                uint32_t clock;
 
                /* This is to force an update */
@@ -351,6 +213,7 @@ sdhci_set_clock(struct sdhci_slot *slot, uint32_t clock)
 {
        uint32_t res;
        uint16_t clk;
+       uint16_t div;
        int timeout;
 
        if (clock == slot->clock)
@@ -358,21 +221,49 @@ sdhci_set_clock(struct sdhci_slot *slot, uint32_t clock)
        slot->clock = clock;
 
        /* Turn off the clock. */
-       WR2(slot, SDHCI_CLOCK_CONTROL, 0);
+       clk = RD2(slot, SDHCI_CLOCK_CONTROL);
+       WR2(slot, SDHCI_CLOCK_CONTROL, clk & ~SDHCI_CLOCK_CARD_EN);
        /* If no clock requested - left it so. */
        if (clock == 0)
                return;
-       /* Looking for highest freq <= clock. */
-       res = slot->max_clk;
-       for (clk = 1; clk < 256; clk <<= 1) {
-               if (res <= clock)
-                       break;
-               res >>= 1;
+
+       /* Recalculate timeout clock frequency based on the new sd clock. */
+       if (slot->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)
+               slot->timeout_clk = slot->clock / 1000;
+
+       if (slot->version < SDHCI_SPEC_300) {
+               /* Looking for highest freq <= clock. */
+               res = slot->max_clk;
+               for (div = 1; div < SDHCI_200_MAX_DIVIDER; div <<= 1) {
+                       if (res <= clock)
+                               break;
+                       res >>= 1;
+               }
+               /* Divider 1:1 is 0x00, 2:1 is 0x01, 256:1 is 0x80 ... */
+               div >>= 1;
+       }
+       else {
+               /* Version 3.0 divisors are multiples of two up to 1023*2 */
+               if (clock >= slot->max_clk)
+                       div = 0;
+               else {
+                       for (div = 2; div < SDHCI_300_MAX_DIVIDER; div += 2) { 
+                               if ((slot->max_clk / div) <= clock) 
+                                       break;
+                       }
+               }
+               div >>= 1;
        }
-       /* Divider 1:1 is 0x00, 2:1 is 0x01, 256:1 is 0x80 ... */
-       clk >>= 1;
+
+       if (bootverbose || sdhci_debug)
+               slot_printf(slot, "Divider %d for freq %d (max %d)\n", 
+                       div, clock, slot->max_clk);
+
        /* Now we have got divider, set it. */
-       clk <<= SDHCI_DIVIDER_SHIFT;
+       clk = (div & SDHCI_DIVIDER_MASK) << SDHCI_DIVIDER_SHIFT;
+       clk |= ((div >> SDHCI_DIVIDER_MASK_LEN) & SDHCI_DIVIDER_HI_MASK)
+               << SDHCI_DIVIDER_HI_SHIFT;
+
        WR2(slot, SDHCI_CLOCK_CONTROL, clk);
        /* Enable clock. */
        clk |= SDHCI_CLOCK_INT_EN;
@@ -382,7 +273,7 @@ sdhci_set_clock(struct sdhci_slot *slot, uint32_t clock)
        while (!((clk = RD2(slot, SDHCI_CLOCK_CONTROL))
                & SDHCI_CLOCK_INT_STABLE)) {
                if (timeout == 0) {
-                       slot_printf(slot,
+                       slot_printf(slot, 
                            "Internal clock never stabilised.\n");
                        sdhci_dumpregs(slot);
                        return;
@@ -402,6 +293,7 @@ sdhci_set_power(struct sdhci_slot *slot, u_char power)
 
        if (slot->power == power)
                return;
+
        slot->power = power;
 
        /* Turn off the power. */
@@ -444,9 +336,9 @@ sdhci_read_block_pio(struct sdhci_slot *slot)
        slot->offset += left;
 
        /* If we are too fast, broken controllers return zeroes. */
-       if (slot->sc->quirks & SDHCI_QUIRK_BROKEN_TIMINGS)
+       if (slot->quirks & SDHCI_QUIRK_BROKEN_TIMINGS)
                DELAY(10);
-       /* Handle unalligned and alligned buffer cases. */
+       /* Handle unaligned and aligned buffer cases. */
        if ((intptr_t)buffer & 3) {
                while (left > 3) {
                        data = RD4(slot, SDHCI_BUFFER);
@@ -458,7 +350,7 @@ sdhci_read_block_pio(struct sdhci_slot *slot)
                        left -= 4;
                }
        } else {
-               bus_read_multi_stream_4(slot->mem_res, SDHCI_BUFFER,
+               RD_MULTI_4(slot, SDHCI_BUFFER,
                    (uint32_t *)buffer, left >> 2);
                left &= 3;
        }
@@ -486,7 +378,7 @@ sdhci_write_block_pio(struct sdhci_slot *slot)
        left = min(512, slot->curcmd->data->len - slot->offset);
        slot->offset += left;
 
-       /* Handle unalligned and alligned buffer cases. */
+       /* Handle unaligned and aligned buffer cases. */
        if ((intptr_t)buffer & 3) {
                while (left > 3) {
                        data = buffer[0] +
@@ -498,7 +390,7 @@ sdhci_write_block_pio(struct sdhci_slot *slot)
                        WR4(slot, SDHCI_BUFFER, data);
                }
        } else {
-               bus_write_multi_stream_4(slot->mem_res, SDHCI_BUFFER,
+               WR_MULTI_4(slot, SDHCI_BUFFER,
                    (uint32_t *)buffer, left >> 2);
                left &= 3;
        }
@@ -535,14 +427,14 @@ sdhci_transfer_pio(struct sdhci_slot *slot)
        }
 }
 
-static void
+static void 
 sdhci_card_delay(void *arg)
 {
        struct sdhci_slot *slot = arg;
 
        taskqueue_enqueue(taskqueue_swi, &slot->card_task);
 }
-
 static void
 sdhci_card_task(void *arg, int pending)
 {
@@ -552,7 +444,7 @@ sdhci_card_task(void *arg, int pending)
        if (RD4(slot, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT) {
                if (slot->dev == NULL) {
                        /* If card is present - attach mmc bus. */
-                       slot->dev = device_add_child(slot->sc->dev, "mmc", -1);
+                       slot->dev = device_add_child(slot->bus, "mmc", -1);
                        device_set_ivars(slot->dev, slot);
                        SDHCI_UNLOCK(slot);
                        device_probe_and_attach(slot->dev);
@@ -564,276 +456,212 @@ sdhci_card_task(void *arg, int pending)
                        device_t d = slot->dev;
                        slot->dev = NULL;
                        SDHCI_UNLOCK(slot);
-                       device_delete_child(slot->sc->dev, d);
+                       device_delete_child(slot->bus, d);
                } else
                        SDHCI_UNLOCK(slot);
        }
 }
 
-static int
-sdhci_probe(device_t dev)
+int
+sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num)
 {
-       uint32_t model;
-       uint16_t subvendor;
-       uint8_t class, subclass;
-       int i, result;
-
-       model = (uint32_t)pci_get_device(dev) << 16;
-       model |= (uint32_t)pci_get_vendor(dev) & 0x0000ffff;
-       subvendor = pci_get_subvendor(dev);
-       class = pci_get_class(dev);
-       subclass = pci_get_subclass(dev);
-
-       result = ENXIO;
-       for (i = 0; sdhci_devices[i].model != 0; i++) {
-               if (sdhci_devices[i].model == model &&
-                   (sdhci_devices[i].subvendor == 0xffff ||
-                   sdhci_devices[i].subvendor == subvendor)) {
-                       device_set_desc(dev, sdhci_devices[i].desc);
-                       result = BUS_PROBE_DEFAULT;
-                       break;
-               }
-       }
-       if (result == ENXIO && class == PCIC_BASEPERIPH &&
-           subclass == PCIS_BASEPERIPH_SDHC) {
-               device_set_desc(dev, "Generic SD HCI");
-               result = BUS_PROBE_GENERIC;
-       }
-
-       return (result);
-}
-
-static int
-sdhci_attach(device_t dev)
-{
-       struct sdhci_softc *sc = device_get_softc(dev);
-       uint32_t model;
-       uint16_t subvendor;
-       uint8_t class, subclass, progif;
-       int err, slots, bar, i;
-
-       sc->dev = dev;
-       model = (uint32_t)pci_get_device(dev) << 16;
-       model |= (uint32_t)pci_get_vendor(dev) & 0x0000ffff;
-       subvendor = pci_get_subvendor(dev);
-       class = pci_get_class(dev);
-       subclass = pci_get_subclass(dev);
-       progif = pci_get_progif(dev);
-       /* Apply chip specific quirks. */
-       for (i = 0; sdhci_devices[i].model != 0; i++) {
-               if (sdhci_devices[i].model == model &&
-                   (sdhci_devices[i].subvendor == 0xffff ||
-                   sdhci_devices[i].subvendor == subvendor)) {
-                       sc->quirks = sdhci_devices[i].quirks;
-                       break;
-               }
+       uint32_t caps, freq;
+       int err;
+
+       SDHCI_LOCK_INIT(slot);
+       slot->num = num;
+       slot->bus = dev;
+
+       /* Allocate DMA tag. */
+       err = bus_dma_tag_create(bus_get_dma_tag(dev),
+          DMA_BLOCK_SIZE, 0, BUS_SPACE_MAXADDR_32BIT,
+          BUS_SPACE_MAXADDR, NULL, NULL,
+          DMA_BLOCK_SIZE, 1, DMA_BLOCK_SIZE,
+          BUS_DMA_ALLOCNOW, 
+          &slot->dmatag);
+       if (err != 0) {
+               device_printf(dev, "Can't create DMA tag\n");
+               SDHCI_LOCK_DESTROY(slot);
+               return (err);
        }
-       /* Read slots info from PCI registers. */
-       slots = pci_read_config(dev, PCI_SLOT_INFO, 1);
-       bar = PCI_SLOT_INFO_FIRST_BAR(slots);
-       slots = PCI_SLOT_INFO_SLOTS(slots);
-       if (slots > 6 || bar > 5) {
-               device_printf(dev, "Incorrect slots information (%d, %d).\n",
-                   slots, bar);
-               return (EINVAL);
+       /* Allocate DMA memory. */
+       err = bus_dmamem_alloc(slot->dmatag, (void **)&slot->dmamem,
+           BUS_DMA_NOWAIT, &slot->dmamap);
+       if (err != 0) {
+               device_printf(dev, "Can't alloc DMA memory\n");
+               SDHCI_LOCK_DESTROY(slot);
+               return (err);
        }
-       /* Allocate IRQ. */
-       sc->irq_rid = 0;
-       sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid,
-           RF_SHAREABLE | RF_ACTIVE);
-       if (sc->irq_res == NULL) {
-               device_printf(dev, "Can't allocate IRQ\n");
-               return (ENOMEM);
+       /* Map the memory. */
+       err = bus_dmamap_load(slot->dmatag, slot->dmamap,
+           (void *)slot->dmamem, DMA_BLOCK_SIZE,
+           sdhci_getaddr, &slot->paddr, 0);
+       if (err != 0 || slot->paddr == 0) {
+               device_printf(dev, "Can't load DMA memory\n");
+               SDHCI_LOCK_DESTROY(slot);
+               if(err)
+                       return (err);
+               else
+                       return (EFAULT);
        }
-       /* Scan all slots. */
-       for (i = 0; i < slots; i++) {
-               struct sdhci_slot *slot = &sc->slots[sc->num_slots];
-               uint32_t caps;
-
-               SDHCI_LOCK_INIT(slot);
-               slot->sc = sc;
-               slot->num = sc->num_slots;
-               /* Allocate memory. */
-               slot->mem_rid = PCIR_BAR(bar + i);
-               slot->mem_res = bus_alloc_resource(dev,
-                   SYS_RES_MEMORY, &slot->mem_rid, 0ul, ~0ul, 0x100, RF_ACTIVE);
-               if (slot->mem_res == NULL) {
-                       device_printf(dev, "Can't allocate memory\n");
-                       SDHCI_LOCK_DESTROY(slot);
-                       continue;
-               }
-               /* Allocate DMA tag. */
-               err = bus_dma_tag_create(NULL,
-                   DMA_BLOCK_SIZE, 0, BUS_SPACE_MAXADDR_32BIT,
-                   BUS_SPACE_MAXADDR, NULL, NULL,
-                   DMA_BLOCK_SIZE, 1, DMA_BLOCK_SIZE,
-                   BUS_DMA_ALLOCNOW, &slot->dmatag);
-               if (err != 0) {
-                       device_printf(dev, "Can't create DMA tag\n");
-                       SDHCI_LOCK_DESTROY(slot);
-                       continue;
-               }
-               /* Allocate DMA memory. */
-               err = bus_dmamem_alloc(slot->dmatag, (void **)&slot->dmamem,
-                   BUS_DMA_NOWAIT, &slot->dmamap);
-               if (err != 0) {
-                       device_printf(dev, "Can't alloc DMA memory\n");
-                       SDHCI_LOCK_DESTROY(slot);
-                       continue;
-               }
-               /* Map the memory. */
-               err = bus_dmamap_load(slot->dmatag, slot->dmamap,
-                   (void *)slot->dmamem, DMA_BLOCK_SIZE,
-                   sdhci_getaddr, &slot->paddr, 0);
-               if (err != 0 || slot->paddr == 0) {
-                       device_printf(dev, "Can't load DMA memory\n");
-                       SDHCI_LOCK_DESTROY(slot);
-                       continue;
-               }
-               /* Initialize slot. */
-               sdhci_init(slot);
+
+       /* Initialize slot. */
+       sdhci_init(slot);
+       slot->version = (RD2(slot, SDHCI_HOST_VERSION) 
+               >> SDHCI_SPEC_VER_SHIFT) & SDHCI_SPEC_VER_MASK;
+       if (slot->quirks & SDHCI_QUIRK_MISSING_CAPS)
+               caps = slot->caps;
+       else
                caps = RD4(slot, SDHCI_CAPABILITIES);
-               /* Calculate base clock frequency. */
-               slot->max_clk =
-                       (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;
-               if (slot->max_clk == 0) {
-                       device_printf(dev, "Hardware doesn't specify base clock "
-                           "frequency.\n");
-               }
-               slot->max_clk *= 1000000;
-               /* Calculate timeout clock frequency. */
+       /* Calculate base clock frequency. */
+       if (slot->version >= SDHCI_SPEC_300)
+               freq = (caps & SDHCI_CLOCK_V3_BASE_MASK) >>
+                   SDHCI_CLOCK_BASE_SHIFT;
+       else    
+               freq = (caps & SDHCI_CLOCK_BASE_MASK) >>
+                   SDHCI_CLOCK_BASE_SHIFT;
+       if (freq != 0)
+               slot->max_clk = freq * 1000000;
+       /*
+        * If the frequency wasn't in the capabilities and the hardware driver
+        * hasn't already set max_clk we're probably not going to work right
+        * with an assumption, so complain about it.
+        */
+       if (slot->max_clk == 0) {
+               slot->max_clk = SDHCI_DEFAULT_MAX_FREQ * 1000000;
+               device_printf(dev, "Hardware doesn't specify base clock "
+                   "frequency, using %dMHz as default.\n", SDHCI_DEFAULT_MAX_FREQ);
+       }
+       /* Calculate timeout clock frequency. */
+       if (slot->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK) {
+               slot->timeout_clk = slot->max_clk / 1000;
+       } else {
                slot->timeout_clk =
                        (caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
-               if (slot->timeout_clk == 0) {
-                       device_printf(dev, "Hardware doesn't specify timeout clock "
-                           "frequency.\n");
-               }
                if (caps & SDHCI_TIMEOUT_CLK_UNIT)
                        slot->timeout_clk *= 1000;
-
-               slot->host.f_min = slot->max_clk / 256;
-               slot->host.f_max = slot->max_clk;
-               slot->host.host_ocr = 0;
-               if (caps & SDHCI_CAN_VDD_330)
-                   slot->host.host_ocr |= MMC_OCR_320_330 | MMC_OCR_330_340;
-               if (caps & SDHCI_CAN_VDD_300)
-                   slot->host.host_ocr |= MMC_OCR_290_300 | MMC_OCR_300_310;
-               if (caps & SDHCI_CAN_VDD_180)
-                   slot->host.host_ocr |= MMC_OCR_LOW_VOLTAGE;
-               if (slot->host.host_ocr == 0) {
-                       device_printf(dev, "Hardware doesn't report any "
-                           "support voltages.\n");
-               }
-               slot->host.caps = MMC_CAP_4_BIT_DATA;
-               if (caps & SDHCI_CAN_DO_HISPD)
-                       slot->host.caps |= MMC_CAP_HSPEED;
-               /* Decide if we have usable DMA. */
-               if (caps & SDHCI_CAN_DO_DMA)
-                       slot->opt |= SDHCI_HAVE_DMA;
-               if (class == PCIC_BASEPERIPH &&
-                   subclass == PCIS_BASEPERIPH_SDHC &&
-                   progif != PCI_SDHCI_IFDMA)
-                       slot->opt &= ~SDHCI_HAVE_DMA;
-               if (sc->quirks & SDHCI_QUIRK_BROKEN_DMA)
-                       slot->opt &= ~SDHCI_HAVE_DMA;
-               if (sc->quirks & SDHCI_QUIRK_FORCE_DMA)
-                       slot->opt |= SDHCI_HAVE_DMA;
-
-               if (bootverbose || sdhci_debug) {
-                       slot_printf(slot, "%uMHz%s 4bits%s%s%s %s\n",
-                           slot->max_clk / 1000000,
-                           (caps & SDHCI_CAN_DO_HISPD) ? " HS" : "",
-                           (caps & SDHCI_CAN_VDD_330) ? " 3.3V" : "",
-                           (caps & SDHCI_CAN_VDD_300) ? " 3.0V" : "",
-                           (caps & SDHCI_CAN_VDD_180) ? " 1.8V" : "",
-                           (slot->opt & SDHCI_HAVE_DMA) ? "DMA" : "PIO");
-                       sdhci_dumpregs(slot);
-               }
-
-               TASK_INIT(&slot->card_task, 0, sdhci_card_task, slot);
-               callout_init(&slot->card_callout);
-               sc->num_slots++;
        }
-       device_printf(dev, "%d slot(s) allocated\n", sc->num_slots);
-       /* Activate the interrupt */
-       err = bus_setup_intr(dev, sc->irq_res, INTR_MPSAFE, sdhci_intr, sc, &sc->intrhand, NULL);
-       if (err)
-               device_printf(dev, "Can't setup IRQ\n");
-       pci_enable_busmaster(dev);
-       /* Process cards detection. */
-       for (i = 0; i < sc->num_slots; i++) {
-               struct sdhci_slot *slot = &sc->slots[i];
-
-               sdhci_card_task(slot, 0);
+       /*
+        * If the frequency wasn't in the capabilities and the hardware driver
+        * hasn't already set timeout_clk we'll probably work okay using the
+        * max timeout, but still mention it.
+        */
+       if (slot->timeout_clk == 0) {
+               device_printf(dev, "Hardware doesn't specify timeout clock "
+                   "frequency, setting BROKEN_TIMEOUT quirk.\n");
+               slot->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
        }
 
+       slot->host.f_min = SDHCI_MIN_FREQ(slot->bus, slot);
+       slot->host.f_max = slot->max_clk;
+       slot->host.host_ocr = 0;
+       if (caps & SDHCI_CAN_VDD_330)
+           slot->host.host_ocr |= MMC_OCR_320_330 | MMC_OCR_330_340;
+       if (caps & SDHCI_CAN_VDD_300)
+           slot->host.host_ocr |= MMC_OCR_290_300 | MMC_OCR_300_310;
+       if (caps & SDHCI_CAN_VDD_180)
+           slot->host.host_ocr |= MMC_OCR_LOW_VOLTAGE;
+       if (slot->host.host_ocr == 0) {
+               device_printf(dev, "Hardware doesn't report any "
+                   "support voltages.\n");
+       }
+       slot->host.caps = MMC_CAP_4_BIT_DATA;
+       if (caps & SDHCI_CAN_DO_HISPD)
+               slot->host.caps |= MMC_CAP_HSPEED;
+       /* Decide if we have usable DMA. */
+       if (caps & SDHCI_CAN_DO_DMA)
+               slot->opt |= SDHCI_HAVE_DMA;
+
+       if (slot->quirks & SDHCI_QUIRK_BROKEN_DMA)
+               slot->opt &= ~SDHCI_HAVE_DMA;
+       if (slot->quirks & SDHCI_QUIRK_FORCE_DMA)
+               slot->opt |= SDHCI_HAVE_DMA;
+
+       /* 
+        * Use platform-provided transfer backend
+        * with PIO as a fallback mechanism
+        */
+       if (slot->opt & SDHCI_PLATFORM_TRANSFER)
+               slot->opt &= ~SDHCI_HAVE_DMA;
+
+       if (bootverbose || sdhci_debug) {
+               slot_printf(slot, "%uMHz%s 4bits%s%s%s %s\n",
+                   slot->max_clk / 1000000,
+                   (caps & SDHCI_CAN_DO_HISPD) ? " HS" : "",
+                   (caps & SDHCI_CAN_VDD_330) ? " 3.3V" : "",
+                   (caps & SDHCI_CAN_VDD_300) ? " 3.0V" : "",
+                   (caps & SDHCI_CAN_VDD_180) ? " 1.8V" : "",
+                   (slot->opt & SDHCI_HAVE_DMA) ? "DMA" : "PIO");
+               sdhci_dumpregs(slot);
+       }
+       
+       TASK_INIT(&slot->card_task, 0, sdhci_card_task, slot);
+       callout_init(&slot->card_callout);
+       callout_init(&slot->timeout_callout);
        return (0);
 }
 
-static int
-sdhci_detach(device_t dev)
+void
+sdhci_start_slot(struct sdhci_slot *slot)
 {
-       struct sdhci_softc *sc = device_get_softc(dev);
-       int i;
+       sdhci_card_task(slot, 0);
+}
 
-       bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
-       bus_release_resource(dev, SYS_RES_IRQ,
-           sc->irq_rid, sc->irq_res);
+int
+sdhci_cleanup_slot(struct sdhci_slot *slot)
+{
+       device_t d;
 
-       for (i = 0; i < sc->num_slots; i++) {
-               struct sdhci_slot *slot = &sc->slots[i];
-               device_t d;
+       callout_drain(&slot->timeout_callout);
+       callout_drain(&slot->card_callout);
+       taskqueue_drain(taskqueue_swi, &slot->card_task);
+       SDHCI_LOCK(slot);
+       d = slot->dev;
+       slot->dev = NULL;
+       SDHCI_UNLOCK(slot);
+       if (d != NULL)
+               device_delete_child(slot->bus, d);
 
-               callout_stop_sync(&slot->card_callout);
-               taskqueue_drain(taskqueue_swi, &slot->card_task);
+       SDHCI_LOCK(slot);
+       sdhci_reset(slot, SDHCI_RESET_ALL);
+       SDHCI_UNLOCK(slot);
+       bus_dmamap_unload(slot->dmatag, slot->dmamap);
+       bus_dmamem_free(slot->dmatag, slot->dmamem, slot->dmamap);
+       bus_dma_tag_destroy(slot->dmatag);
 
-               SDHCI_LOCK(slot);
-               d = slot->dev;
-               slot->dev = NULL;
-               SDHCI_UNLOCK(slot);
-               if (d != NULL)
-                       device_delete_child(dev, d);
+       SDHCI_LOCK_DESTROY(slot);
 
-               SDHCI_LOCK(slot);
-               sdhci_reset(slot, SDHCI_RESET_ALL);
-               SDHCI_UNLOCK(slot);
-               bus_dmamap_unload(slot->dmatag, slot->dmamap);
-               bus_dmamem_free(slot->dmatag, slot->dmamem, slot->dmamap);
-               bus_dma_tag_destroy(slot->dmatag);
-               bus_release_resource(dev, SYS_RES_MEMORY,
-                   slot->mem_rid, slot->mem_res);
-               SDHCI_LOCK_DESTROY(slot);
-       }
        return (0);
 }
 
-static int
-sdhci_suspend(device_t dev)
+int
+sdhci_generic_suspend(struct sdhci_slot *slot)
 {
-       struct sdhci_softc *sc = device_get_softc(dev);
-       int i, err;
+       sdhci_reset(slot, SDHCI_RESET_ALL);
 
-       err = bus_generic_suspend(dev);
-       if (err)
-               return (err);
-       for (i = 0; i < sc->num_slots; i++)
-               sdhci_reset(&sc->slots[i], SDHCI_RESET_ALL);
        return (0);
 }
 
-static int
-sdhci_resume(device_t dev)
+int
+sdhci_generic_resume(struct sdhci_slot *slot)
 {
-       struct sdhci_softc *sc = device_get_softc(dev);
-       int i;
+       sdhci_init(slot);
 
-       for (i = 0; i < sc->num_slots; i++)
-               sdhci_init(&sc->slots[i]);
-       return (bus_generic_resume(dev));
+       return (0);
 }
 
-static int
-sdhci_update_ios(device_t brdev, device_t reqdev)
+uint32_t
+sdhci_generic_min_freq(device_t brdev, struct sdhci_slot *slot)
+{
+       if (slot->version >= SDHCI_SPEC_300)
+               return (slot->max_clk / SDHCI_300_MAX_DIVIDER);
+       else
+               return (slot->max_clk / SDHCI_200_MAX_DIVIDER);
+}
+
+int
+sdhci_generic_update_ios(device_t brdev, device_t reqdev)
 {
        struct sdhci_slot *slot = device_get_ivars(reqdev);
        struct mmc_ios *ios = &slot->host.ios;
@@ -857,13 +685,41 @@ sdhci_update_ios(device_t brdev, device_t reqdev)
                slot->hostctrl &= ~SDHCI_CTRL_HISPD;
        WR1(slot, SDHCI_HOST_CONTROL, slot->hostctrl);
        /* Some controllers like reset after bus changes. */
-       if(slot->sc->quirks & SDHCI_QUIRK_RESET_ON_IOS)
+       if(slot->quirks & SDHCI_QUIRK_RESET_ON_IOS)
                sdhci_reset(slot, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
 
        SDHCI_UNLOCK(slot);
        return (0);
 }
 
+static void 
+sdhci_req_done(struct sdhci_slot *slot)
+{
+       struct mmc_request *req;
+
+       if (slot->req != NULL && slot->curcmd != NULL) {
+               callout_stop(&slot->timeout_callout);
+               req = slot->req;
+               slot->req = NULL;
+               slot->curcmd = NULL;
+               req->done(req);
+       }
+}
+static void 
+sdhci_timeout(void *arg)
+{
+       struct sdhci_slot *slot = arg;
+
+       SDHCI_LOCK(slot);
+        if (slot->curcmd != NULL) {
+                sdhci_reset(slot, SDHCI_RESET_CMD|SDHCI_RESET_DATA);
+                slot->curcmd->error = MMC_ERR_TIMEOUT;
+                sdhci_req_done(slot);
+        }
+       SDHCI_UNLOCK(slot);
+}
 static void
 sdhci_set_transfer_mode(struct sdhci_slot *slot,
        struct mmc_data *data)
@@ -889,7 +745,6 @@ sdhci_set_transfer_mode(struct sdhci_slot *slot,
 static void
 sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
 {
-       struct mmc_request *req = slot->req;
        int flags, timeout;
        uint32_t mask, state;
 
@@ -902,9 +757,7 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
        if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
                slot_printf(slot, "Unsupported response type!\n");
                cmd->error = MMC_ERR_FAILED;
-               slot->req = NULL;
-               slot->curcmd = NULL;
-               req->done(req);
+               sdhci_req_done(slot);
                return;
        }
 
@@ -916,9 +769,7 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
            slot->power == 0 ||
            slot->clock == 0) {
                cmd->error = MMC_ERR_FAILED;
-               slot->req = NULL;
-               slot->curcmd = NULL;
-               req->done(req);
+               sdhci_req_done(slot);
                return;
        }
        /* Always wait for free CMD bus. */
@@ -929,17 +780,24 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
        /* We shouldn't wait for DAT for stop commands. */
        if (cmd == slot->req->stop)
                mask &= ~SDHCI_DAT_INHIBIT;
-       /* Wait for bus no more then 10 ms. */
-       timeout = 10;
+       /*
+        *  Wait for bus no more then 250 ms.  Typically there will be no wait
+        *  here at all, but when writing a crash dump we may be bypassing the
+        *  host platform's interrupt handler, and in some cases that handler
+        *  may be working around hardware quirks such as not respecting r1b
+        *  busy indications.  In those cases, this wait-loop serves the purpose
+        *  of waiting for the prior command and data transfers to be done, and
+        *  SD cards are allowed to take up to 250ms for write and erase ops.
+        *  (It's usually more like 20-30ms in the real world.)
+        */
+       timeout = 250;
        while (state & mask) {
                if (timeout == 0) {
                        slot_printf(slot, "Controller never released "
                            "inhibit bit(s).\n");
                        sdhci_dumpregs(slot);
                        cmd->error = MMC_ERR_FAILED;
-                       slot->req = NULL;
-                       slot->curcmd = NULL;
-                       req->done(req);
+                       sdhci_req_done(slot);
                        return;
                }
                timeout--;
@@ -966,7 +824,7 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
                flags |= SDHCI_CMD_TYPE_ABORT;
        /* Prepare data. */
        sdhci_start_data(slot, cmd->data);
-       /*
+       /* 
         * Interrupt aggregation: To reduce total number of interrupts
         * group response interrupt with data interrupt when possible.
         * If there going to be data interrupt, mask response one.
@@ -979,10 +837,10 @@ sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
        WR4(slot, SDHCI_ARGUMENT, cmd->arg);
        /* Set data transfer mode. */
        sdhci_set_transfer_mode(slot, cmd->data);
-       /* Set command flags. */
-       WR1(slot, SDHCI_COMMAND_FLAGS, flags);
        /* Start command. */
-       WR1(slot, SDHCI_COMMAND, cmd->opcode);
+       WR2(slot, SDHCI_COMMAND_FLAGS, (cmd->opcode << 8) | (flags & 0xff));
+       /* Start timeout callout. */
+       callout_reset(&slot->timeout_callout, 2*hz, sdhci_timeout, slot);
 }
 
 static void
@@ -1009,11 +867,17 @@ sdhci_finish_command(struct sdhci_slot *slot)
                        uint8_t extra = 0;
                        for (i = 0; i < 4; i++) {
                                uint32_t val = RD4(slot, SDHCI_RESPONSE + i * 4);
-                               slot->curcmd->resp[3 - i] = (val << 8) + extra;
-                               extra = val >> 24;
+                               if (slot->quirks & SDHCI_QUIRK_DONT_SHIFT_RESPONSE) {
+                                       slot->curcmd->resp[3 - i] = val;
+                               } else {
+                                       slot->curcmd->resp[3 - i] =
+                                               (val << 8) | extra;
+                                       extra = val >> 24;
+                               }
                        }
-               } else
+               } else {
                        slot->curcmd->resp[0] = RD4(slot, SDHCI_RESPONSE);
+               }
        }
        /* If data ready - finish. */
        if (slot->data_done)
@@ -1035,21 +899,21 @@ sdhci_start_data(struct sdhci_slot *slot, struct mmc_data *data)
 
        /* Calculate and set data timeout.*/
        /* XXX: We should have this from mmc layer, now assume 1 sec. */
-       target_timeout = 1000000;
-       div = 0;
-       current_timeout = (1 << 13) * 1000 / slot->timeout_clk;
-       while (current_timeout < target_timeout) {
-               div++;
-               current_timeout <<= 1;
-               if (div >= 0xF)
-                       break;
-       }
-       /* Compensate for an off-by-one error in the CaFe chip.*/
-       if (slot->sc->quirks & SDHCI_QUIRK_INCR_TIMEOUT_CONTROL)
-               div++;
-       if (div >= 0xF) {
-               slot_printf(slot, "Timeout too large!\n");
-               div = 0xE;
+       if (slot->quirks & SDHCI_QUIRK_BROKEN_TIMEOUT_VAL) {
+               div = 0xe;
+       } else {
+               target_timeout = 1000000;
+               div = 0;
+               current_timeout = (1 << 13) * 1000 / slot->timeout_clk;
+               while (current_timeout < target_timeout && div < 0xE) {
+                       ++div;
+                       current_timeout <<= 1;
+               }
+               /* Compensate for an off-by-one error in the CaFe chip.*/
+               if (div < 0xE && 
+                   (slot->quirks & SDHCI_QUIRK_INCR_TIMEOUT_CONTROL)) {
+                       ++div;
+               }
        }
        WR1(slot, SDHCI_TIMEOUT_CONTROL, div);
 
@@ -1060,11 +924,11 @@ sdhci_start_data(struct sdhci_slot *slot, struct mmc_data *data)
        if ((slot->opt & SDHCI_HAVE_DMA))
                slot->flags |= SDHCI_USE_DMA;
        /* If data is small, broken DMA may return zeroes instead of data, */
-       if ((slot->sc->quirks & SDHCI_QUIRK_BROKEN_TIMINGS) &&
+       if ((slot->quirks & SDHCI_QUIRK_BROKEN_TIMINGS) &&
            (data->len <= 512))
                slot->flags &= ~SDHCI_USE_DMA;
        /* Some controllers require even block sizes. */
-       if ((slot->sc->quirks & SDHCI_QUIRK_32BIT_DMA_SIZE) &&
+       if ((slot->quirks & SDHCI_QUIRK_32BIT_DMA_SIZE) &&
            ((data->len) & 0x3))
                slot->flags &= ~SDHCI_USE_DMA;
        /* Load DMA buffer. */
@@ -1094,14 +958,14 @@ sdhci_start_data(struct sdhci_slot *slot, struct mmc_data *data)
        WR2(slot, SDHCI_BLOCK_COUNT, (data->len + 511) / 512);
 }
 
-static void
+void
 sdhci_finish_data(struct sdhci_slot *slot)
 {
        struct mmc_data *data = slot->curcmd->data;
 
        slot->data_done = 1;
        /* Interrupt aggregation: Restore command interrupt.
-        * Auxillary restore point for the case when data interrupt
+        * Auxiliary restore point for the case when data interrupt
         * happened first. */
        if (!slot->cmd_done) {
                WR4(slot, SDHCI_SIGNAL_ENABLE,
@@ -1111,11 +975,13 @@ sdhci_finish_data(struct sdhci_slot *slot)
        if (slot->flags & SDHCI_USE_DMA) {
                if (data->flags & MMC_DATA_READ) {
                        size_t left = data->len - slot->offset;
-                       bus_dmamap_sync(slot->dmatag, slot->dmamap, BUS_DMASYNC_POSTREAD);
+                       bus_dmamap_sync(slot->dmatag, slot->dmamap, 
+                           BUS_DMASYNC_POSTREAD);
                        memcpy((u_char*)data->data + slot->offset, slot->dmamem,
                            (left < DMA_BLOCK_SIZE)?left:DMA_BLOCK_SIZE);
                } else
-                       bus_dmamap_sync(slot->dmatag, slot->dmamap, BUS_DMASYNC_POSTWRITE);
+                       bus_dmamap_sync(slot->dmatag, slot->dmamap, 
+                           BUS_DMASYNC_POSTWRITE);
        }
        /* If there was error - reset the host. */
        if (slot->curcmd->error) {
@@ -1143,7 +1009,7 @@ sdhci_start(struct sdhci_slot *slot)
                sdhci_start_command(slot, req->cmd);
                return;
        }
-/*     We don't need this until using Auto-CMD12 feature
+/*     We don't need this until using Auto-CMD12 feature
        if (!(slot->flags & STOP_STARTED) && req->stop) {
                slot->flags |= STOP_STARTED;
                sdhci_start_command(slot, req->stop);
@@ -1153,19 +1019,16 @@ sdhci_start(struct sdhci_slot *slot)
        if (sdhci_debug > 1)
                slot_printf(slot, "result: %d\n", req->cmd->error);
        if (!req->cmd->error &&
-           (slot->sc->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)) {
+           (slot->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)) {
                sdhci_reset(slot, SDHCI_RESET_CMD);
                sdhci_reset(slot, SDHCI_RESET_DATA);
        }
 
-       /* We must be done -- bad idea to do this while locked? */
-       slot->req = NULL;
-       slot->curcmd = NULL;
-       req->done(req);
+       sdhci_req_done(slot);
 }
 
-static int
-sdhci_request(device_t brdev, device_t reqdev, struct mmc_request *req)
+int
+sdhci_generic_request(device_t brdev, device_t reqdev, struct mmc_request *req)
 {
        struct sdhci_slot *slot = device_get_ivars(reqdev);
 
@@ -1176,8 +1039,8 @@ sdhci_request(device_t brdev, device_t reqdev, struct mmc_request *req)
        }
        if (sdhci_debug > 1) {
                slot_printf(slot, "CMD%u arg %#x flags %#x dlen %u dflags %#x\n",
-                   req->cmd->opcode, req->cmd->arg, req->cmd->flags,
-                   (req->cmd->data)?(u_int)req->cmd->data->len:0,
+                   req->cmd->opcode, req->cmd->arg, req->cmd->flags,
+                   (req->cmd->data)?(u_int)req->cmd->data->len:0,
                    (req->cmd->data)?req->cmd->data->flags:0);
        }
        slot->req = req;
@@ -1186,15 +1049,15 @@ sdhci_request(device_t brdev, device_t reqdev, struct mmc_request *req)
        SDHCI_UNLOCK(slot);
        if (dumping) {
                while (slot->req != NULL) {
-                       sdhci_intr(slot->sc);
+                       sdhci_generic_intr(slot);
                        DELAY(10);
                }
        }
        return (0);
 }
 
-static int
-sdhci_get_ro(device_t brdev, device_t reqdev)
+int
+sdhci_generic_get_ro(device_t brdev, device_t reqdev)
 {
        struct sdhci_slot *slot = device_get_ivars(reqdev);
        uint32_t val;
@@ -1205,8 +1068,8 @@ sdhci_get_ro(device_t brdev, device_t reqdev)
        return (!(val & SDHCI_WRITE_PROTECT));
 }
 
-static int
-sdhci_acquire_host(device_t brdev, device_t reqdev)
+int
+sdhci_generic_acquire_host(device_t brdev, device_t reqdev)
 {
        struct sdhci_slot *slot = device_get_ivars(reqdev);
        int err = 0;
@@ -1221,8 +1084,8 @@ sdhci_acquire_host(device_t brdev, device_t reqdev)
        return (err);
 }
 
-static int
-sdhci_release_host(device_t brdev, device_t reqdev)
+int
+sdhci_generic_release_host(device_t brdev, device_t reqdev)
 {
        struct sdhci_slot *slot = device_get_ivars(reqdev);
 
@@ -1287,13 +1150,23 @@ sdhci_data_irq(struct sdhci_slot *slot, uint32_t intmask)
        }
        if (slot->curcmd->error) {
                /* No need to continue after any error. */
-               sdhci_finish_data(slot);
+               if (slot->flags & PLATFORM_DATA_STARTED) {
+                       slot->flags &= ~PLATFORM_DATA_STARTED;
+                       SDHCI_PLATFORM_FINISH_TRANSFER(slot->bus, slot);
+               } else
+                       sdhci_finish_data(slot);
                return;
        }
 
        /* Handle PIO interrupt. */
-       if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL))
-               sdhci_transfer_pio(slot);
+       if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) {
+               if ((slot->opt & SDHCI_PLATFORM_TRANSFER) && 
+                   SDHCI_PLATFORM_WILL_HANDLE(slot->bus, slot)) {
+                       SDHCI_PLATFORM_START_TRANSFER(slot->bus, slot, &intmask);
+                       slot->flags |= PLATFORM_DATA_STARTED;
+               } else
+                       sdhci_transfer_pio(slot);
+       }
        /* Handle DMA border. */
        if (intmask & SDHCI_INT_DMA_END) {
                struct mmc_data *data = slot->curcmd->data;
@@ -1332,15 +1205,21 @@ sdhci_data_irq(struct sdhci_slot *slot, uint32_t intmask)
                WR4(slot, SDHCI_DMA_ADDRESS, slot->paddr);
        }
        /* We have got all data. */
-       if (intmask & SDHCI_INT_DATA_END)
-               sdhci_finish_data(slot);
+       if (intmask & SDHCI_INT_DATA_END) {
+               if (slot->flags & PLATFORM_DATA_STARTED) {
+                       slot->flags &= ~PLATFORM_DATA_STARTED;
+                       SDHCI_PLATFORM_FINISH_TRANSFER(slot->bus, slot);
+               } else {
+                       sdhci_finish_data(slot);
+               }
+       }
 }
 
 static void
 sdhci_acmd_irq(struct sdhci_slot *slot)
 {
        uint16_t err;
-
+       
        err = RD4(slot, SDHCI_ACMD12_ERR);
        if (!slot->curcmd) {
                slot_printf(slot, "Got AutoCMD12 error 0x%04x, but "
@@ -1352,85 +1231,79 @@ sdhci_acmd_irq(struct sdhci_slot *slot)
        sdhci_reset(slot, SDHCI_RESET_CMD);
 }
 
-static void
-sdhci_intr(void *arg)
+void
+sdhci_generic_intr(struct sdhci_slot *slot)
 {
-       struct sdhci_softc *sc = (struct sdhci_softc *)arg;
-       int i;
-
-       for (i = 0; i < sc->num_slots; i++) {
-               struct sdhci_slot *slot = &sc->slots[i];
-               uint32_t intmask;
-
-               SDHCI_LOCK(slot);
-               /* Read slot interrupt status. */
-               intmask = RD4(slot, SDHCI_INT_STATUS);
-               if (intmask == 0 || intmask == 0xffffffff) {
-                       SDHCI_UNLOCK(slot);
-                       continue;
-               }
-               if (sdhci_debug > 2)
-                       slot_printf(slot, "Interrupt %#x\n", intmask);
-
-               /* Handle card presence interrupts. */
-               if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
-                       WR4(slot, SDHCI_INT_STATUS, intmask &
-                           (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE));
-
-                       if (intmask & SDHCI_INT_CARD_REMOVE) {
-                               if (bootverbose || sdhci_debug)
-                                       slot_printf(slot, "Card removed\n");
-                               callout_stop(&slot->card_callout);
-                               taskqueue_enqueue(taskqueue_swi,
-                                   &slot->card_task);
-                       }
-                       if (intmask & SDHCI_INT_CARD_INSERT) {
-                               if (bootverbose || sdhci_debug)
-                                       slot_printf(slot, "Card inserted\n");
-                               callout_reset(&slot->card_callout, hz / 2,
-                                   sdhci_card_delay, slot);
-                       }
-                       intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
-               }
-               /* Handle command interrupts. */
-               if (intmask & SDHCI_INT_CMD_MASK) {
-                       WR4(slot, SDHCI_INT_STATUS, intmask & SDHCI_INT_CMD_MASK);
-                       sdhci_cmd_irq(slot, intmask & SDHCI_INT_CMD_MASK);
-               }
-               /* Handle data interrupts. */
-               if (intmask & SDHCI_INT_DATA_MASK) {
-                       WR4(slot, SDHCI_INT_STATUS, intmask & SDHCI_INT_DATA_MASK);
-                       sdhci_data_irq(slot, intmask & SDHCI_INT_DATA_MASK);
-               }
-               /* Handle AutoCMD12 error interrupt. */
-               if (intmask & SDHCI_INT_ACMD12ERR) {
-                       WR4(slot, SDHCI_INT_STATUS, SDHCI_INT_ACMD12ERR);
-                       sdhci_acmd_irq(slot);
-               }
-               intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK);
-               intmask &= ~SDHCI_INT_ACMD12ERR;
-               intmask &= ~SDHCI_INT_ERROR;
-               /* Handle bus power interrupt. */
-               if (intmask & SDHCI_INT_BUS_POWER) {
-                       WR4(slot, SDHCI_INT_STATUS, SDHCI_INT_BUS_POWER);
-                       slot_printf(slot,
-                           "Card is consuming too much power!\n");
-                       intmask &= ~SDHCI_INT_BUS_POWER;
+       uint32_t intmask;
+       
+       SDHCI_LOCK(slot);
+       /* Read slot interrupt status. */
+       intmask = RD4(slot, SDHCI_INT_STATUS);
+       if (intmask == 0 || intmask == 0xffffffff) {
+               SDHCI_UNLOCK(slot);
+               return;
+       }
+       if (sdhci_debug > 2)
+               slot_printf(slot, "Interrupt %#x\n", intmask);
+
+       /* Handle card presence interrupts. */
+       if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
+               WR4(slot, SDHCI_INT_STATUS, intmask & 
+                   (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE));
+
+               if (intmask & SDHCI_INT_CARD_REMOVE) {
+                       if (bootverbose || sdhci_debug)
+                               slot_printf(slot, "Card removed\n");
+                       callout_stop(&slot->card_callout);
+                       taskqueue_enqueue(taskqueue_swi,
+                           &slot->card_task);
                }
-               /* The rest is unknown. */
-               if (intmask) {
-                       WR4(slot, SDHCI_INT_STATUS, intmask);
-                       slot_printf(slot, "Unexpected interrupt 0x%08x.\n",
-                           intmask);
-                       sdhci_dumpregs(slot);
+               if (intmask & SDHCI_INT_CARD_INSERT) {
+                       if (bootverbose || sdhci_debug)
+                               slot_printf(slot, "Card inserted\n");
+                       callout_reset(&slot->card_callout, hz / 2,
+                           sdhci_card_delay, slot);
                }
-
-               SDHCI_UNLOCK(slot);
+               intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
+       }
+       /* Handle command interrupts. */
+       if (intmask & SDHCI_INT_CMD_MASK) {
+               WR4(slot, SDHCI_INT_STATUS, intmask & SDHCI_INT_CMD_MASK);
+               sdhci_cmd_irq(slot, intmask & SDHCI_INT_CMD_MASK);
+       }
+       /* Handle data interrupts. */
+       if (intmask & SDHCI_INT_DATA_MASK) {
+               WR4(slot, SDHCI_INT_STATUS, intmask & SDHCI_INT_DATA_MASK);
+               sdhci_data_irq(slot, intmask & SDHCI_INT_DATA_MASK);
        }
+       /* Handle AutoCMD12 error interrupt. */
+       if (intmask & SDHCI_INT_ACMD12ERR) {
+               WR4(slot, SDHCI_INT_STATUS, SDHCI_INT_ACMD12ERR);
+               sdhci_acmd_irq(slot);
+       }
+       intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK);
+       intmask &= ~SDHCI_INT_ACMD12ERR;
+       intmask &= ~SDHCI_INT_ERROR;
+       /* Handle bus power interrupt. */
+       if (intmask & SDHCI_INT_BUS_POWER) {
+               WR4(slot, SDHCI_INT_STATUS, SDHCI_INT_BUS_POWER);
+               slot_printf(slot,
+                   "Card is consuming too much power!\n");
+               intmask &= ~SDHCI_INT_BUS_POWER;
+       }
+       /* The rest is unknown. */
+       if (intmask) {
+               WR4(slot, SDHCI_INT_STATUS, intmask);
+               slot_printf(slot, "Unexpected interrupt 0x%08x.\n",
+                   intmask);
+               sdhci_dumpregs(slot);
+       }
+       
+       SDHCI_UNLOCK(slot);
 }
 
-static int
-sdhci_read_ivar(device_t bus, device_t child, int which, uintptr_t *result)
+int
+sdhci_generic_read_ivar(device_t bus, device_t child, int which, uintptr_t *result)
 {
        struct sdhci_slot *slot = device_get_ivars(child);
 
@@ -1483,8 +1356,8 @@ sdhci_read_ivar(device_t bus, device_t child, int which, uintptr_t *result)
        return (0);
 }
 
-static int
-sdhci_write_ivar(device_t bus, device_t child, int which, uintptr_t value)
+int
+sdhci_generic_write_ivar(device_t bus, device_t child, int which, uintptr_t value)
 {
        struct sdhci_slot *slot = device_get_ivars(child);
 
@@ -1502,14 +1375,30 @@ sdhci_write_ivar(device_t bus, device_t child, int which, uintptr_t value)
                break;
        case MMCBR_IVAR_CLOCK:
                if (value > 0) {
-                       uint32_t clock = slot->max_clk;
+                       uint32_t max_clock;
+                       uint32_t clock;
                        int i;
 
-                       for (i = 0; i < 8; i++) {
-                               if (clock <= value)
-                                       break;
-                               clock >>= 1;
+                       max_clock = slot->max_clk;
+                       clock = max_clock;
+
+                       if (slot->version < SDHCI_SPEC_300) {
+                               for (i = 0; i < SDHCI_200_MAX_DIVIDER;
+                                   i <<= 1) {
+                                       if (clock <= value)
+                                               break;
+                                       clock >>= 1;
+                               }
+                       }
+                       else {
+                               for (i = 0; i < SDHCI_300_MAX_DIVIDER;
+                                   i += 2) {
+                                       if (clock <= value)
+                                               break;
+                                       clock = max_clock / (i + 2);
+                               }
                        }
+
                        slot->host.ios.clock = clock;
                } else
                        slot->host.ios.clock = 0;
@@ -1539,34 +1428,4 @@ sdhci_write_ivar(device_t bus, device_t child, int which, uintptr_t value)
        return (0);
 }
 
-static device_method_t sdhci_methods[] = {
-       /* device_if */
-       DEVMETHOD(device_probe, sdhci_probe),
-       DEVMETHOD(device_attach, sdhci_attach),
-       DEVMETHOD(device_detach, sdhci_detach),
-       DEVMETHOD(device_suspend, sdhci_suspend),
-       DEVMETHOD(device_resume, sdhci_resume),
-
-       /* Bus interface */
-       DEVMETHOD(bus_read_ivar,        sdhci_read_ivar),
-       DEVMETHOD(bus_write_ivar,       sdhci_write_ivar),
-
-       /* mmcbr_if */
-       DEVMETHOD(mmcbr_update_ios, sdhci_update_ios),
-       DEVMETHOD(mmcbr_request, sdhci_request),
-       DEVMETHOD(mmcbr_get_ro, sdhci_get_ro),
-       DEVMETHOD(mmcbr_acquire_host, sdhci_acquire_host),
-       DEVMETHOD(mmcbr_release_host, sdhci_release_host),
-
-       DEVMETHOD_END
-};
-
-static driver_t sdhci_driver = {
-       "sdhci",
-       sdhci_methods,
-       sizeof(struct sdhci_softc),
-};
-static devclass_t sdhci_devclass;
-
-
-DRIVER_MODULE(sdhci, pci, sdhci_driver, sdhci_devclass, NULL, NULL);
+MODULE_VERSION(sdhci, 1);
index b25fced..a11063d 100644 (file)
  * $FreeBSD: src/sys/dev/sdhci/sdhci.h,v 1.1 2008/10/21 20:33:40 mav Exp $
  */
 
-/*
- * PCI registers
- */
+#ifndef        __SDHCI_H__
+#define        __SDHCI_H__
 
-#define PCI_SDHCI_IFPIO                        0x00
-#define PCI_SDHCI_IFDMA                        0x01
-#define PCI_SDHCI_IFVENDOR             0x02
+#define DMA_BLOCK_SIZE 4096
+#define DMA_BOUNDARY   0       /* DMA reload every 4K */
 
-#define PCI_SLOT_INFO                  0x40    /* 8 bits */
-#define  PCI_SLOT_INFO_SLOTS(x)                (((x >> 4) & 7) + 1)
-#define  PCI_SLOT_INFO_FIRST_BAR(x)    ((x) & 7)
+/* Controller doesn't honor resets unless we touch the clock register */
+#define SDHCI_QUIRK_CLOCK_BEFORE_RESET                 (1<<0)
+/* Controller really supports DMA */
+#define SDHCI_QUIRK_FORCE_DMA                          (1<<1)
+/* Controller has unusable DMA engine */
+#define SDHCI_QUIRK_BROKEN_DMA                         (1<<2)
+/* Controller doesn't like to be reset when there is no card inserted. */
+#define SDHCI_QUIRK_NO_CARD_NO_RESET                   (1<<3)
+/* Controller has flaky internal state so reset it on each ios change */
+#define SDHCI_QUIRK_RESET_ON_IOS                       (1<<4)
+/* Controller can only DMA chunk sizes that are a multiple of 32 bits */
+#define SDHCI_QUIRK_32BIT_DMA_SIZE                     (1<<5)
+/* Controller needs to be reset after each request to stay stable */
+#define SDHCI_QUIRK_RESET_AFTER_REQUEST                        (1<<6)
+/* Controller has an off-by-one issue with timeout value */
+#define SDHCI_QUIRK_INCR_TIMEOUT_CONTROL               (1<<7)
+/* Controller has broken read timings */
+#define SDHCI_QUIRK_BROKEN_TIMINGS                     (1<<8)
+/* Controller needs lowered frequency */
+#define        SDHCI_QUIRK_LOWER_FREQUENCY                     (1<<9)
+/* Data timeout is invalid, should use SD clock */
+#define        SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK             (1<<10)
+/* Timeout value is invalid, should be overriden */
+#define        SDHCI_QUIRK_BROKEN_TIMEOUT_VAL                  (1<<11)
+/* SDHCI_CAPABILITIES is invalid */
+#define        SDHCI_QUIRK_MISSING_CAPS                        (1<<12)
+/* Hardware shifts the 136-bit response, don't do it in software. */
+#define        SDHCI_QUIRK_DONT_SHIFT_RESPONSE                 (1<<13)
 
 /*
  * Controller registers
  */
-
 #define SDHCI_DMA_ADDRESS      0x00
 
 #define SDHCI_BLOCK_SIZE       0x04
 #define  SDHCI_CMD_INHIBIT     0x00000001
 #define  SDHCI_DAT_INHIBIT     0x00000002
 #define  SDHCI_DAT_ACTIVE      0x00000004
+#define  SDHCI_RETUNE_REQUEST  0x00000008
 #define  SDHCI_DOING_WRITE     0x00000100
 #define  SDHCI_DOING_READ      0x00000200
 #define  SDHCI_SPACE_AVAILABLE 0x00000400
 #define  SDHCI_CARD_STABLE     0x00020000
 #define  SDHCI_CARD_PIN                0x00040000
 #define  SDHCI_WRITE_PROTECT   0x00080000
-#define  SDHCI_STATE_DAT       0x00700000
-#define  SDHCI_STATE_CMD       0x00800000
+#define  SDHCI_STATE_DAT_MASK  0x00f00000
+#define  SDHCI_STATE_CMD       0x01000000
 
 #define SDHCI_HOST_CONTROL     0x28
 #define  SDHCI_CTRL_LED                0x01
 #define  SDHCI_CTRL_SDMA       0x08
 #define  SDHCI_CTRL_ADMA2      0x10
 #define  SDHCI_CTRL_ADMA264    0x18
+#define  SDHCI_CTRL_DMA_MASK   0x18
+#define  SDHCI_CTRL_8BITBUS    0x20
 #define  SDHCI_CTRL_CARD_DET   0x40
 #define  SDHCI_CTRL_FORCE_CARD 0x80
 
 #define SDHCI_WAKE_UP_CONTROL  0x2B
 
 #define SDHCI_CLOCK_CONTROL    0x2C
+#define  SDHCI_DIVIDER_MASK    0xff
+#define  SDHCI_DIVIDER_MASK_LEN        8
 #define  SDHCI_DIVIDER_SHIFT   8
+#define  SDHCI_DIVIDER_HI_MASK 3
+#define  SDHCI_DIVIDER_HI_SHIFT        6
 #define  SDHCI_CLOCK_CARD_EN   0x0004
 #define  SDHCI_CLOCK_INT_STABLE        0x0002
 #define  SDHCI_CLOCK_INT_EN    0x0001
 #define  SDHCI_TIMEOUT_CLK_SHIFT 0
 #define  SDHCI_TIMEOUT_CLK_UNIT        0x00000080
 #define  SDHCI_CLOCK_BASE_MASK 0x00003F00
+#define  SDHCI_CLOCK_V3_BASE_MASK      0x0000FF00
 #define  SDHCI_CLOCK_BASE_SHIFT        8
 #define  SDHCI_MAX_BLOCK_MASK  0x00030000
 #define  SDHCI_MAX_BLOCK_SHIFT  16
+#define  SDHCI_CAN_DO_8BITBUS  0x00040000
 #define  SDHCI_CAN_DO_ADMA2    0x00080000
 #define  SDHCI_CAN_DO_HISPD    0x00200000
 #define  SDHCI_CAN_DO_DMA      0x00400000
 #define  SDHCI_VENDOR_VER_SHIFT        8
 #define  SDHCI_SPEC_VER_MASK   0x00FF
 #define  SDHCI_SPEC_VER_SHIFT  0
+#define        SDHCI_SPEC_100          0
+#define        SDHCI_SPEC_200          1
+#define        SDHCI_SPEC_300          2
+
+
+struct sdhci_slot {
+       u_int           quirks;         /* Chip specific quirks */
+       u_int           caps;           /* Override SDHCI_CAPABILITIES */
+       device_t        bus;            /* Bus device */
+       device_t        dev;            /* Slot device */
+       u_char          num;            /* Slot number */
+       u_char          opt;            /* Slot options */
+#define SDHCI_HAVE_DMA                 1
+#define SDHCI_PLATFORM_TRANSFER                2
+       u_char          version;
+       uint32_t        max_clk;        /* Max possible freq */
+       uint32_t        timeout_clk;    /* Timeout freq */
+       bus_dma_tag_t   dmatag;
+       bus_dmamap_t    dmamap;
+       u_char          *dmamem;
+       bus_addr_t      paddr;          /* DMA buffer address */
+       struct task     card_task;      /* Card presence check task */
+       struct callout  card_callout;   /* Card insert delay callout */
+       struct callout  timeout_callout;/* Card command/data response timeout */
+       struct mmc_host host;           /* Host parameters */
+       struct mmc_request *req;        /* Current request */
+       struct mmc_command *curcmd;     /* Current command of current request */
+       
+       uint32_t        intmask;        /* Current interrupt mask */
+       uint32_t        clock;          /* Current clock freq. */
+       size_t          offset;         /* Data buffer offset */
+       uint8_t         hostctrl;       /* Current host control register */
+       u_char          power;          /* Current power */
+       u_char          bus_busy;       /* Bus busy status */
+       u_char          cmd_done;       /* CMD command part done flag */
+       u_char          data_done;      /* DAT command part done flag */
+       u_char          flags;          /* Request execution flags */
+#define CMD_STARTED            1
+#define STOP_STARTED           2
+#define SDHCI_USE_DMA          4       /* Use DMA for this req. */
+#define PLATFORM_DATA_STARTED  8       /* Data transfer is handled by platform */
+       struct lock     lock;           /* Slot mutex */
+};
+
+int sdhci_generic_read_ivar(device_t bus, device_t child, int which, uintptr_t *result);
+int sdhci_generic_write_ivar(device_t bus, device_t child, int which, uintptr_t value);
+int sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num);
+void sdhci_start_slot(struct sdhci_slot *slot);
+/* performs generic clean-up for platform transfers */
+void sdhci_finish_data(struct sdhci_slot *slot);
+int sdhci_cleanup_slot(struct sdhci_slot *slot);
+int sdhci_generic_suspend(struct sdhci_slot *slot);
+int sdhci_generic_resume(struct sdhci_slot *slot);
+int sdhci_generic_update_ios(device_t brdev, device_t reqdev);
+int sdhci_generic_request(device_t brdev, device_t reqdev, struct mmc_request *req);
+int sdhci_generic_get_ro(device_t brdev, device_t reqdev);
+int sdhci_generic_acquire_host(device_t brdev, device_t reqdev);
+int sdhci_generic_release_host(device_t brdev, device_t reqdev);
+void sdhci_generic_intr(struct sdhci_slot *slot);
+uint32_t sdhci_generic_min_freq(device_t brdev, struct sdhci_slot *slot);
+
+#endif /* __SDHCI_H__ */
diff --git a/sys/dev/disk/sdhci/sdhci_if.m b/sys/dev/disk/sdhci/sdhci_if.m
new file mode 100644 (file)
index 0000000..eb19154
--- /dev/null
@@ -0,0 +1,153 @@
+#-
+# Copyright (c) 2006 M. Warner Losh
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# Portions of this software may have been developed with reference to
+# the SD Simplified Specification.  The following disclaimer may apply:
+#
+# The following conditions apply to the release of the simplified
+# specification ("Simplified Specification") by the SD Card Association and
+# the SD Group. The Simplified Specification is a subset of the complete SD
+# Specification which is owned by the SD Card Association and the SD
+# Group. This Simplified Specification is provided on a non-confidential
+# basis subject to the disclaimers below. Any implementation of the
+# Simplified Specification may require a license from the SD Card
+# Association, SD Group, SD-3C LLC or other third parties.
+#
+# Disclaimers:
+#
+# The information contained in the Simplified Specification is presented only
+# as a standard specification for SD Cards and SD Host/Ancillary products and
+# is provided "AS-IS" without any representations or warranties of any
+# kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD
+# Card Association for any damages, any infringements of patents or other
+# right of the SD Group, SD-3C LLC, the SD Card Association or any third
+# parties, which may result from its use. No license is granted by
+# implication, estoppel or otherwise under any patent or other rights of the
+# SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing
+# herein shall be construed as an obligation by the SD Group, the SD-3C LLC
+# or the SD Card Association to disclose or distribute any technical
+# information, know-how or other confidential information to any third party.
+#
+#
+
+#
+# This is the set of callbacks that mmc bridges call into the bus, or
+# that mmc/sd card drivers call to make requests.
+#
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
+
+#include <sys/bus.h>
+
+#include <bus/mmc/bridge.h>
+#include <bus/mmc/mmcreg.h>
+#include <dev/disk/sdhci/sdhci.h>
+
+CODE {
+       struct sdhci_slot;
+}
+
+INTERFACE sdhci;
+
+METHOD uint8_t read_1 {
+       device_t                brdev;
+       struct sdhci_slot       *slot;
+       bus_size_t              off;
+}
+
+METHOD uint16_t read_2 {
+       device_t                brdev;
+       struct sdhci_slot       *slot;
+       bus_size_t              off;
+}
+
+METHOD uint32_t read_4 {
+       device_t                brdev;
+       struct sdhci_slot       *slot;
+       bus_size_t              off;
+}
+
+METHOD void read_multi_4 {
+       device_t                brdev;
+       struct sdhci_slot       *slot;
+       bus_size_t              off;
+       uint32_t                *data;
+       bus_size_t              count;
+}
+
+METHOD void write_1 {
+       device_t                brdev;
+       struct sdhci_slot       *slot;
+       bus_size_t              off;
+       uint8_t                 val;
+}
+
+METHOD void write_2 {
+       device_t                brdev;
+       struct sdhci_slot       *slot;
+       bus_size_t              off;
+       uint16_t                val;
+}
+
+METHOD void write_4 {
+       device_t                brdev;
+       struct sdhci_slot       *slot;
+       bus_size_t              off;
+       uint32_t                val;
+}
+
+METHOD void write_multi_4 {
+       device_t                brdev;
+       struct sdhci_slot       *slot;
+       bus_size_t              off;
+       uint32_t                *data;
+       bus_size_t              count;
+}
+
+METHOD int platform_will_handle {
+       device_t                brdev;
+       struct sdhci_slot       *slot;
+}
+
+METHOD void platform_start_transfer {
+       device_t                brdev;
+       struct sdhci_slot       *slot;
+       uint32_t                *intmask;
+}
+
+METHOD void platform_finish_transfer {
+       device_t                brdev;
+       struct sdhci_slot       *slot;
+}
+
+METHOD uint32_t min_freq {
+       device_t                brdev;
+       struct sdhci_slot       *slot;
+} DEFAULT sdhci_generic_min_freq;
diff --git a/sys/dev/disk/sdhci/sdhci_pci.c b/sys/dev/disk/sdhci/sdhci_pci.c
new file mode 100644 (file)
index 0000000..134f8c8
--- /dev/null
@@ -0,0 +1,446 @@
+/*-
+ * Copyright (c) 2008 Alexander Motin <mav@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
+#include <sys/lock.h>
+
+#include <bus/pci/pcireg.h>
+#include <bus/pci/pcivar.h>
+
+#include <sys/bus.h>
+#include <sys/resource.h>
+#include <machine/stdarg.h>
+
+#include <bus/mmc/bridge.h>
+#include <bus/mmc/mmcreg.h>
+#include <bus/mmc/mmcbrvar.h>
+
+#include "sdhci.h"
+#include "mmcbr_if.h"
+#include "sdhci_if.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) + 1)
+#define  PCI_SLOT_INFO_FIRST_BAR(x)    ((x) & 7)
+
+/*
+ * RICOH specific PCI registers
+ */
+#define        SDHC_PCI_MODE_KEY               0xf9
+#define        SDHC_PCI_MODE                   0x150
+#define        SDHC_PCI_MODE_SD20              0x10
+#define        SDHC_PCI_BASE_FREQ_KEY          0xfc
+#define        SDHC_PCI_BASE_FREQ              0xe1
+
+static const struct sdhci_device {
+       uint32_t        model;
+       uint16_t        subvendor;
+       const char      *desc;
+       u_int           quirks;
+} sdhci_devices[] = {
+       { 0x08221180,   0xffff, "RICOH R5C822 SD",
+           SDHCI_QUIRK_FORCE_DMA },
+       { 0xe8221180,   0xffff, "RICOH SD",
+           SDHCI_QUIRK_FORCE_DMA },
+       { 0xe8231180,   0xffff, "RICOH R5CE823 SD",
+           SDHCI_QUIRK_LOWER_FREQUENCY },
+       { 0x8034104c,   0xffff, "TI XX21/XX11 SD",
+           SDHCI_QUIRK_FORCE_DMA },
+       { 0x05501524,   0xffff, "ENE CB712 SD",
+           SDHCI_QUIRK_BROKEN_TIMINGS },
+       { 0x05511524,   0xffff, "ENE CB712 SD 2",
+           SDHCI_QUIRK_BROKEN_TIMINGS },
+       { 0x07501524,   0xffff, "ENE CB714 SD",
+           SDHCI_QUIRK_RESET_ON_IOS |
+           SDHCI_QUIRK_BROKEN_TIMINGS },
+       { 0x07511524,   0xffff, "ENE CB714 SD 2",
+           SDHCI_QUIRK_RESET_ON_IOS |
+           SDHCI_QUIRK_BROKEN_TIMINGS },
+       { 0x410111ab,   0xffff, "Marvell CaFe SD",
+           SDHCI_QUIRK_INCR_TIMEOUT_CONTROL },
+       { 0x2381197B,   0xffff, "JMicron JMB38X SD",
+           SDHCI_QUIRK_32BIT_DMA_SIZE |
+           SDHCI_QUIRK_RESET_AFTER_REQUEST },
+       { 0,            0xffff, NULL,
+           0 }
+};
+
+struct sdhci_pci_softc {
+       device_t        dev;            /* Controller device */
+       u_int           quirks;         /* Chip specific quirks */
+       struct resource *irq_res;       /* IRQ resource */
+       void            *intrhand;      /* Interrupt handle */
+
+       int             num_slots;      /* Number of slots on this controller */
+       struct sdhci_slot slots[6];
+       struct resource *mem_res[6];    /* Memory resource */
+};
+
+static int sdhci_enable_msi = 1;
+TUNABLE_INT("hw.sdhci_enable_msi", &sdhci_enable_msi);
+
+static uint8_t
+sdhci_pci_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off)
+{
+       struct sdhci_pci_softc *sc = device_get_softc(dev);
+
+       bus_barrier(sc->mem_res[slot->num], 0, 0xFF,
+           BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
+       return bus_read_1(sc->mem_res[slot->num], off);
+}
+
+static void
+sdhci_pci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint8_t val)
+{
+       struct sdhci_pci_softc *sc = device_get_softc(dev);
+
+       bus_barrier(sc->mem_res[slot->num], 0, 0xFF,
+           BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
+       bus_write_1(sc->mem_res[slot->num], off, val);
+}
+
+static uint16_t
+sdhci_pci_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off)
+{
+       struct sdhci_pci_softc *sc = device_get_softc(dev);
+
+       bus_barrier(sc->mem_res[slot->num], 0, 0xFF,
+           BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
+       return bus_read_2(sc->mem_res[slot->num], off);
+}
+
+static void
+sdhci_pci_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint16_t val)
+{
+       struct sdhci_pci_softc *sc = device_get_softc(dev);
+
+       bus_barrier(sc->mem_res[slot->num], 0, 0xFF,
+           BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
+       bus_write_2(sc->mem_res[slot->num], off, val);
+}
+
+static uint32_t
+sdhci_pci_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off)
+{
+       struct sdhci_pci_softc *sc = device_get_softc(dev);
+
+       bus_barrier(sc->mem_res[slot->num], 0, 0xFF,
+           BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
+       return bus_read_4(sc->mem_res[slot->num], off);
+}
+
+static void
+sdhci_pci_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t val)
+{
+       struct sdhci_pci_softc *sc = device_get_softc(dev);
+
+       bus_barrier(sc->mem_res[slot->num], 0, 0xFF,
+           BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
+       bus_write_4(sc->mem_res[slot->num], off, val);
+}
+
+static void
+sdhci_pci_read_multi_4(device_t dev, struct sdhci_slot *slot,
+    bus_size_t off, uint32_t *data, bus_size_t count)
+{
+       struct sdhci_pci_softc *sc = device_get_softc(dev);
+
+       bus_read_multi_stream_4(sc->mem_res[slot->num], off, data, count);
+}
+
+static void
+sdhci_pci_write_multi_4(device_t dev, struct sdhci_slot *slot,
+    bus_size_t off, uint32_t *data, bus_size_t count)
+{
+       struct sdhci_pci_softc *sc = device_get_softc(dev);
+
+       bus_write_multi_stream_4(sc->mem_res[slot->num], off, data, count);
+}
+
+static void sdhci_pci_intr(void *arg);
+
+static void
+sdhci_lower_frequency(device_t dev)
+{
+
+       /* Enable SD2.0 mode. */
+       pci_write_config(dev, SDHC_PCI_MODE_KEY, 0xfc, 1);
+       pci_write_config(dev, SDHC_PCI_MODE, SDHC_PCI_MODE_SD20, 1);
+       pci_write_config(dev, SDHC_PCI_MODE_KEY, 0x00, 1);
+
+       /*
+        * Some SD/MMC cards don't work with the default base
+        * clock frequency of 200MHz.  Lower it to 50Hz.
+        */
+       pci_write_config(dev, SDHC_PCI_BASE_FREQ_KEY, 0x01, 1);
+       pci_write_config(dev, SDHC_PCI_BASE_FREQ, 50, 1);
+       pci_write_config(dev, SDHC_PCI_BASE_FREQ_KEY, 0x00, 1);
+}
+
+static int
+sdhci_pci_probe(device_t dev)
+{
+       uint32_t model;
+       uint16_t subvendor;
+       uint8_t class, subclass;
+       int i, result;
+
+       model = (uint32_t)pci_get_device(dev) << 16;
+       model |= (uint32_t)pci_get_vendor(dev) & 0x0000ffff;
+       subvendor = pci_get_subvendor(dev);
+       class = pci_get_class(dev);
+       subclass = pci_get_subclass(dev);
+
+       result = ENXIO;
+       for (i = 0; sdhci_devices[i].model != 0; i++) {
+               if (sdhci_devices[i].model == model &&
+                   (sdhci_devices[i].subvendor == 0xffff ||
+                   sdhci_devices[i].subvendor == subvendor)) {
+                       device_set_desc(dev, sdhci_devices[i].desc);
+                       result = BUS_PROBE_DEFAULT;
+                       break;
+               }
+       }
+       if (result == ENXIO && class == PCIC_BASEPERIPH &&
+           subclass == PCIS_BASEPERIPH_SDHC) {
+               device_set_desc(dev, "Generic SD HCI");
+               result = BUS_PROBE_GENERIC;
+       }
+
+       return (result);
+}
+
+static int
+sdhci_pci_attach(device_t dev)
+{
+       struct sdhci_pci_softc *sc = device_get_softc(dev);
+       uint32_t model;
+       uint16_t subvendor;
+       uint8_t class, subclass, progif;
+       int bar, count, err, rid, slots, i;
+
+       sc->dev = dev;
+       model = (uint32_t)pci_get_device(dev) << 16;
+       model |= (uint32_t)pci_get_vendor(dev) & 0x0000ffff;
+       subvendor = pci_get_subvendor(dev);
+       class = pci_get_class(dev);
+       subclass = pci_get_subclass(dev);
+       progif = pci_get_progif(dev);
+       /* Apply chip specific quirks. */
+       for (i = 0; sdhci_devices[i].model != 0; i++) {
+               if (sdhci_devices[i].model == model &&
+                   (sdhci_devices[i].subvendor == 0xffff ||
+                   sdhci_devices[i].subvendor == subvendor)) {
+                       sc->quirks = sdhci_devices[i].quirks;
+                       break;
+               }
+       }
+       /* Some controllers need to be bumped into the right mode. */
+       if (sc->quirks & SDHCI_QUIRK_LOWER_FREQUENCY)
+               sdhci_lower_frequency(dev);
+       /* Read slots info from PCI registers. */
+       slots = pci_read_config(dev, PCI_SLOT_INFO, 1);
+       bar = PCI_SLOT_INFO_FIRST_BAR(slots);
+       slots = PCI_SLOT_INFO_SLOTS(slots);
+       if (slots > 6 || bar > 5) {
+               device_printf(dev, "Incorrect slots information (%d, %d).\n",
+                   slots, bar);
+               return (EINVAL);
+       }
+       /* Allocate IRQ. */
+       i = 1;
+       rid = 0;
+       if (sdhci_enable_msi != 0) {
+               count = pci_msi_count(dev);
+               if (count >= 1) {
+                       count = 1;
+                       if (pci_alloc_msi(dev, &i, 1, count) == 0) {
+                               if (bootverbose)
+                                       device_printf(dev, "MSI enabled\n");
+                               rid = 1;
+                       }       
+               }
+       }
+       sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+               RF_ACTIVE | (rid != 0 ? 0 : RF_SHAREABLE));
+       if (sc->irq_res == NULL) {
+               device_printf(dev, "Can't allocate IRQ\n");
+               pci_release_msi(dev);
+               return (ENOMEM);
+       }
+       /* Scan all slots. */
+       for (i = 0; i < slots; i++) {
+               struct sdhci_slot *slot = &sc->slots[sc->num_slots];
+
+               /* Allocate memory. */
+               rid = PCIR_BAR(bar + i);
+               sc->mem_res[i] = bus_alloc_resource(dev, SYS_RES_MEMORY,
+                   &rid, 0ul, ~0ul, 0x100, RF_ACTIVE);
+               if (sc->mem_res[i] == NULL) {
+                       device_printf(dev, "Can't allocate memory for slot %d\n", i);
+                       continue;
+               }
+
+               if (sdhci_init_slot(dev, slot, i) != 0)
+                       continue;
+
+               sc->num_slots++;
+       }
+       device_printf(dev, "%d slot(s) allocated\n", sc->num_slots);
+       /* Activate the interrupt */
+       err = bus_setup_intr(dev, sc->irq_res, INTR_MPSAFE,
+           sdhci_pci_intr, sc, &sc->intrhand, NULL);
+       if (err)
+               device_printf(dev, "Can't setup IRQ\n");
+       pci_enable_busmaster(dev);
+       /* Process cards detection. */
+       for (i = 0; i < sc->num_slots; i++) {
+               struct sdhci_slot *slot = &sc->slots[i];
+
+               sdhci_start_slot(slot);
+       }
+
+       return (0);
+}
+
+static int
+sdhci_pci_detach(device_t dev)
+{
+       struct sdhci_pci_softc *sc = device_get_softc(dev);
+       int i;
+
+       bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
+       bus_release_resource(dev, SYS_RES_IRQ,
+           rman_get_rid(sc->irq_res), sc->irq_res);
+       pci_release_msi(dev);
+
+       for (i = 0; i < sc->num_slots; i++) {
+               struct sdhci_slot *slot = &sc->slots[i];
+
+               sdhci_cleanup_slot(slot);
+               bus_release_resource(dev, SYS_RES_MEMORY,
+                   rman_get_rid(sc->mem_res[i]), sc->mem_res[i]);
+       }
+       return (0);
+}
+
+static int
+sdhci_pci_suspend(device_t dev)
+{
+       struct sdhci_pci_softc *sc = device_get_softc(dev);
+       int i, err;
+
+       err = bus_generic_suspend(dev);
+       if (err)
+               return (err);
+       for (i = 0; i < sc->num_slots; i++)
+               sdhci_generic_suspend(&sc->slots[i]);
+       return (0);
+}
+
+static int
+sdhci_pci_resume(device_t dev)
+{
+       struct sdhci_pci_softc *sc = device_get_softc(dev);
+       int i;
+
+       for (i = 0; i < sc->num_slots; i++)
+               sdhci_generic_resume(&sc->slots[i]);
+       return (bus_generic_resume(dev));
+}
+
+static void
+sdhci_pci_intr(void *arg)
+{
+       struct sdhci_pci_softc *sc = (struct sdhci_pci_softc *)arg;
+       int i;
+
+       for (i = 0; i < sc->num_slots; i++) {
+               struct sdhci_slot *slot = &sc->slots[i];
+               sdhci_generic_intr(slot);
+       }
+}
+
+static device_method_t sdhci_methods[] = {
+       /* device_if */
+       DEVMETHOD(device_probe, sdhci_pci_probe),
+       DEVMETHOD(device_attach, sdhci_pci_attach),
+       DEVMETHOD(device_detach, sdhci_pci_detach),
+       DEVMETHOD(device_suspend, sdhci_pci_suspend),
+       DEVMETHOD(device_resume, sdhci_pci_resume),
+
+       /* Bus interface */
+       DEVMETHOD(bus_read_ivar,        sdhci_generic_read_ivar),
+       DEVMETHOD(bus_write_ivar,       sdhci_generic_write_ivar),
+
+       /* mmcbr_if */
+       DEVMETHOD(mmcbr_update_ios, sdhci_generic_update_ios),
+       DEVMETHOD(mmcbr_request, sdhci_generic_request),
+       DEVMETHOD(mmcbr_get_ro, sdhci_generic_get_ro),
+       DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host),
+       DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host),
+
+       /* SDHCI registers accessors */
+       DEVMETHOD(sdhci_read_1,         sdhci_pci_read_1),
+       DEVMETHOD(sdhci_read_2,         sdhci_pci_read_2),
+       DEVMETHOD(sdhci_read_4,         sdhci_pci_read_4),
+       DEVMETHOD(sdhci_read_multi_4,   sdhci_pci_read_multi_4),
+       DEVMETHOD(sdhci_write_1,        sdhci_pci_write_1),
+       DEVMETHOD(sdhci_write_2,        sdhci_pci_write_2),
+       DEVMETHOD(sdhci_write_4,        sdhci_pci_write_4),
+       DEVMETHOD(sdhci_write_multi_4,  sdhci_pci_write_multi_4),
+
+       DEVMETHOD_END
+};
+
+static driver_t sdhci_pci_driver = {
+       "sdhci_pci",
+       sdhci_methods,
+       sizeof(struct sdhci_pci_softc),
+};
+static devclass_t sdhci_pci_devclass;
+
+DRIVER_MODULE(sdhci_pci, pci, sdhci_pci_driver, sdhci_pci_devclass, NULL,
+    NULL);
+MODULE_DEPEND(sdhci_pci, sdhci, 1, 1, 1);