summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--UPDATING6
-rw-r--r--sys/conf/files4
-rw-r--r--sys/conf/kmod.mk3
-rw-r--r--sys/dev/mmc/mmc.c2
-rw-r--r--sys/dev/sdhci/sdhci.c794
-rw-r--r--sys/dev/sdhci/sdhci.h92
-rw-r--r--sys/dev/sdhci/sdhci_if.m121
-rw-r--r--sys/dev/sdhci/sdhci_pci.c440
-rw-r--r--sys/modules/Makefile1
-rw-r--r--sys/modules/sdhci/Makefile2
-rw-r--r--sys/modules/sdhci_pci/Makefile8
11 files changed, 904 insertions, 569 deletions
diff --git a/UPDATING b/UPDATING
index ef0cabb..b37e30a 100644
--- a/UPDATING
+++ b/UPDATING
@@ -24,6 +24,12 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 10.x IS SLOW:
disable the most expensive debugging functionality run
"ln -s 'abort:false,junk:false' /etc/malloc.conf".)
+20121015:
+ The sdhci driver was split in two parts: sdhci (generic SD Host
+ Controller logic) and sdhci_pci (actual hardware driver).
+ No kernel config modifications are required, but if you
+ load sdhc as a module you must switch to sdhci_pci instead.
+
20121014:
Import the FUSE kernel and userland support into base system.
diff --git a/sys/conf/files b/sys/conf/files
index 812937c..5b22469 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1905,7 +1905,9 @@ dev/scc/scc_dev_sab82532.c optional scc
dev/scc/scc_dev_z8530.c optional scc
dev/scd/scd.c optional scd isa
dev/scd/scd_isa.c optional scd isa
-dev/sdhci/sdhci.c optional sdhci pci
+dev/sdhci/sdhci.c optional sdhci
+dev/sdhci/sdhci_if.m optional sdhci
+dev/sdhci/sdhci_pci.c optional sdhci pci
dev/sf/if_sf.c optional sf pci
dev/sge/if_sge.c optional sge pci
dev/si/si.c optional si
diff --git a/sys/conf/kmod.mk b/sys/conf/kmod.mk
index 86955e9..f0d3d4d 100644
--- a/sys/conf/kmod.mk
+++ b/sys/conf/kmod.mk
@@ -345,7 +345,8 @@ MFILES?= dev/acpica/acpi_if.m dev/acpi_support/acpi_wmi_if.m \
dev/mmc/mmcbr_if.m dev/mmc/mmcbus_if.m \
dev/mii/miibus_if.m dev/mvs/mvs_if.m dev/ofw/ofw_bus_if.m \
dev/pccard/card_if.m dev/pccard/power_if.m dev/pci/pci_if.m \
- dev/pci/pcib_if.m dev/ppbus/ppbus_if.m dev/smbus/smbus_if.m \
+ dev/pci/pcib_if.m dev/ppbus/ppbus_if.m \
+ dev/sdhci/sdhci_if.m dev/smbus/smbus_if.m \
dev/sound/pci/hda/hdac_if.m \
dev/sound/pcm/ac97_if.m dev/sound/pcm/channel_if.m \
dev/sound/pcm/feeder_if.m dev/sound/pcm/mixer_if.m \
diff --git a/sys/dev/mmc/mmc.c b/sys/dev/mmc/mmc.c
index 644254f..305d21f 100644
--- a/sys/dev/mmc/mmc.c
+++ b/sys/dev/mmc/mmc.c
@@ -1730,4 +1730,4 @@ static devclass_t mmc_devclass;
DRIVER_MODULE(mmc, ti_mmchs, mmc_driver, mmc_devclass, NULL, NULL);
DRIVER_MODULE(mmc, at91_mci, mmc_driver, mmc_devclass, NULL, NULL);
-DRIVER_MODULE(mmc, sdhci, mmc_driver, mmc_devclass, NULL, NULL);
+DRIVER_MODULE(mmc, sdhci_pci, mmc_driver, mmc_devclass, NULL, NULL);
diff --git a/sys/dev/sdhci/sdhci.c b/sys/dev/sdhci/sdhci.c
index 0c78dba..d7228f78 100644
--- a/sys/dev/sdhci/sdhci.c
+++ b/sys/dev/sdhci/sdhci.c
@@ -39,9 +39,6 @@ __FBSDID("$FreeBSD$");
#include <sys/sysctl.h>
#include <sys/taskqueue.h>
-#include <dev/pci/pcireg.h>
-#include <dev/pci/pcivar.h>
-
#include <machine/bus.h>
#include <machine/resource.h>
#include <machine/stdarg.h>
@@ -52,104 +49,12 @@ __FBSDID("$FreeBSD$");
#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)
-/* Controller needs lowered frequency */
-#define SDHCI_QUIRK_LOWER_FREQUENCY (1<<9)
-
-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 },
- { 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 }
-};
+#include "sdhci_if.h"
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 mtx mtx; /* Slot mutex */
-};
-
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 */
@@ -160,63 +65,21 @@ struct sdhci_softc {
static 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))
-/* 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 *);
+#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 void sdhci_set_clock(struct sdhci_slot *slot, uint32_t clock);
static void sdhci_start(struct sdhci_slot *slot);
@@ -233,6 +96,16 @@ static void sdhci_card_task(void *, int);
#define SDHCI_ASSERT_LOCKED(_slot) mtx_assert(&_slot->mtx, MA_OWNED);
#define SDHCI_ASSERT_UNLOCKED(_slot) mtx_assert(&_slot->mtx, MA_NOTOWNED);
+static void
+sdhci_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+ if (error != 0) {
+ printf("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, ...)
{
@@ -240,7 +113,7 @@ slot_printf(struct sdhci_slot *slot, const char * fmt, ...)
int retval;
retval = printf("%s-slot%d: ",
- device_get_nameunit(slot->sc->dev), slot->num);
+ device_get_nameunit(slot->bus), slot->num);
va_start(ap, fmt);
retval += vprintf(fmt, ap);
@@ -249,16 +122,6 @@ slot_printf(struct sdhci_slot *slot, const char * fmt, ...)
}
static void
-sdhci_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
-{
- if (error != 0) {
- printf("getaddr: error %d\n", error);
- return;
- }
- *(bus_addr_t *)arg = segs[0].ds_addr;
-}
-
-static void
sdhci_dumpregs(struct sdhci_slot *slot)
{
slot_printf(slot,
@@ -295,7 +158,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;
@@ -303,7 +166,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 */
@@ -354,24 +217,6 @@ sdhci_init(struct sdhci_slot *slot)
}
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 50MHz.
- */
- 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 void
sdhci_set_clock(struct sdhci_slot *slot, uint32_t clock)
{
uint32_t res;
@@ -427,6 +272,7 @@ sdhci_set_power(struct sdhci_slot *slot, u_char power)
if (slot->power == power)
return;
+
slot->power = power;
/* Turn off the power. */
@@ -469,7 +315,7 @@ 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. */
if ((intptr_t)buffer & 3) {
@@ -483,7 +329,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;
}
@@ -523,7 +369,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;
}
@@ -577,7 +423,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);
@@ -589,281 +435,171 @@ 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;
- }
+ uint32_t caps;
+ 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, NULL, NULL,
+ &slot->dmatag);
+ if (err != 0) {
+ device_printf(dev, "Can't create DMA tag\n");
+ SDHCI_LOCK_DESTROY(slot);
+ return (err);
}
- if (result == ENXIO && class == PCIC_BASEPERIPH &&
- subclass == PCIS_BASEPERIPH_SDHC) {
- device_set_desc(dev, "Generic SD HCI");
- result = BUS_PROBE_GENERIC;
+ /* 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);
+ }
+ /* 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);
}
-
- 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;
- }
+ /* Initialize slot. */
+ sdhci_init(slot);
+ slot->version = (RD2(slot, SDHCI_HOST_VERSION)
+ >> SDHCI_SPEC_VER_SHIFT) & SDHCI_SPEC_VER_MASK;
+ 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) {
+ slot->max_clk = 50;
+ device_printf(dev, "Hardware doesn't specify base clock "
+ "frequency.\n");
}
- /* 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);
+ slot->max_clk *= 1000000;
+ /* Calculate timeout clock frequency. */
+ 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");
}
- /* 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);
+ 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");
}
- /* 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(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, NULL, NULL,
- &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);
- 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. */
- 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, 1);
- 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_TYPE_MISC | INTR_MPSAFE,
- NULL, sdhci_intr, sc, &sc->intrhand);
- 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);
+ 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;
+
+ 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, 1);
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->card_callout);
+ taskqueue_drain(taskqueue_swi_giant, &slot->card_task);
- callout_drain(&slot->card_callout);
- taskqueue_drain(taskqueue_swi_giant, &slot->card_task);
+ SDHCI_LOCK(slot);
+ d = slot->dev;
+ slot->dev = NULL;
+ SDHCI_UNLOCK(slot);
+ if (d != NULL)
+ device_delete_child(slot->bus, d);
- SDHCI_LOCK(slot);
- d = slot->dev;
- slot->dev = NULL;
- SDHCI_UNLOCK(slot);
- if (d != NULL)
- device_delete_child(dev, d);
+ 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_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)
+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;
@@ -887,7 +623,7 @@ 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);
@@ -1009,10 +745,8 @@ 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));
}
static void
@@ -1075,7 +809,7 @@ sdhci_start_data(struct sdhci_slot *slot, struct mmc_data *data)
break;
}
/* Compensate for an off-by-one error in the CaFe chip.*/
- if (slot->sc->quirks & SDHCI_QUIRK_INCR_TIMEOUT_CONTROL)
+ if (slot->quirks & SDHCI_QUIRK_INCR_TIMEOUT_CONTROL)
div++;
if (div >= 0xF) {
slot_printf(slot, "Timeout too large!\n");
@@ -1090,11 +824,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. */
@@ -1183,7 +917,7 @@ 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);
}
@@ -1194,8 +928,8 @@ sdhci_start(struct sdhci_slot *slot)
req->done(req);
}
-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);
@@ -1216,15 +950,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;
@@ -1235,8 +969,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;
@@ -1251,8 +985,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);
@@ -1382,85 +1116,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_giant,
- &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_giant,
+ &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);
@@ -1513,8 +1241,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);
@@ -1569,34 +1297,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),
-
- {0, 0},
-};
-
-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, 0, 0);
+MODULE_VERSION(sdhci, 1);
diff --git a/sys/dev/sdhci/sdhci.h b/sys/dev/sdhci/sdhci.h
index 5c0253e..851e7e8 100644
--- a/sys/dev/sdhci/sdhci.h
+++ b/sys/dev/sdhci/sdhci.h
@@ -25,26 +25,33 @@
* $FreeBSD$
*/
-/*
- * 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)
-/*
- * 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
/*
* Controller registers
@@ -197,3 +204,54 @@
#define SDHCI_VENDOR_VER_SHIFT 8
#define SDHCI_SPEC_VER_MASK 0x00FF
#define SDHCI_SPEC_VER_SHIFT 0
+
+struct sdhci_slot {
+ u_int quirks; /* Chip specific quirks */
+ device_t bus; /* Bus device */
+ device_t dev; /* Slot device */
+ u_char num; /* Slot number */
+ u_char opt; /* Slot options */
+ u_char version;
+#define SDHCI_HAVE_DMA 1
+ 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 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 mtx mtx; /* 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);
+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);
+
+#endif /* __SDHCI_H__ */
diff --git a/sys/dev/sdhci/sdhci_if.m b/sys/dev/sdhci/sdhci_if.m
new file mode 100644
index 0000000..cc8a7d3
--- /dev/null
+++ b/sys/dev/sdhci/sdhci_if.m
@@ -0,0 +1,121 @@
+#-
+# 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.
+#
+# $FreeBSD$
+#
+
+#
+# This is the set of callbacks that mmc bridges call into the bus, or
+# that mmc/sd card drivers call to make requests.
+#
+
+#include <machine/bus.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;
+}
diff --git a/sys/dev/sdhci/sdhci_pci.c b/sys/dev/sdhci/sdhci_pci.c
new file mode 100644
index 0000000..02dc2a1
--- /dev/null
+++ b/sys/dev/sdhci/sdhci_pci.c
@@ -0,0 +1,440 @@
+/*-
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#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 <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/stdarg.h>
+
+#include <dev/mmc/bridge.h>
+#include <dev/mmc/mmcreg.h>
+#include <dev/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;
+ 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 */
+ int irq_rid;
+ 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 */
+ int mem_rid[6];
+};
+
+static SYSCTL_NODE(_hw, OID_AUTO, sdhci_pci, CTLFLAG_RD, 0, "sdhci PCI driver");
+
+int sdhci_pci_debug;
+TUNABLE_INT("hw.sdhci_pci.debug", &sdhci_pci_debug);
+SYSCTL_INT(_hw_sdhci_pci, OID_AUTO, debug, CTLFLAG_RW, &sdhci_pci_debug, 0, "Debug level");
+
+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 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;
+ }
+ }
+ /* 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. */
+ 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);
+ }
+ /* Scan all slots. */
+ for (i = 0; i < slots; i++) {
+ struct sdhci_slot *slot = &sc->slots[sc->num_slots];
+
+ /* Allocate memory. */
+ sc->mem_rid[i] = PCIR_BAR(bar + i);
+ sc->mem_res[i] = bus_alloc_resource(dev,
+ SYS_RES_MEMORY, &(sc->mem_rid[i]), 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_TYPE_MISC | INTR_MPSAFE,
+ NULL, sdhci_pci_intr, sc, &sc->intrhand);
+ 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,
+ sc->irq_rid, sc->irq_res);
+
+ 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,
+ sc->mem_rid[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),
+
+ {0, 0},
+};
+
+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, 0, 0);
+MODULE_DEPEND(sdhci_pci, sdhci, 1, 1, 1);
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
index 78cd889..cc71d1a 100644
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -290,6 +290,7 @@ SUBDIR= \
scd \
${_scsi_low} \
sdhci \
+ sdhci_pci \
sem \
send \
${_sf} \
diff --git a/sys/modules/sdhci/Makefile b/sys/modules/sdhci/Makefile
index 29fc8b8..9ee9540 100644
--- a/sys/modules/sdhci/Makefile
+++ b/sys/modules/sdhci/Makefile
@@ -3,6 +3,6 @@
.PATH: ${.CURDIR}/../../dev/sdhci
KMOD= sdhci
-SRCS= sdhci.c sdhci.h device_if.h bus_if.h pci_if.h mmcbr_if.h
+SRCS= sdhci.c sdhci.h sdhci_if.c sdhci_if.h device_if.h bus_if.h mmcbr_if.h
.include <bsd.kmod.mk>
diff --git a/sys/modules/sdhci_pci/Makefile b/sys/modules/sdhci_pci/Makefile
new file mode 100644
index 0000000..fcff490
--- /dev/null
+++ b/sys/modules/sdhci_pci/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../dev/sdhci
+
+KMOD= sdhci_pci
+SRCS= sdhci_pci.c sdhci.h sdhci_if.h device_if.h bus_if.h pci_if.h mmcbr_if.h
+
+.include <bsd.kmod.mk>
OpenPOWER on IntegriCloud