summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorUwe Stuehler <uwe@cvs.openbsd.org>2006-05-28 17:21:15 +0000
committerUwe Stuehler <uwe@cvs.openbsd.org>2006-05-28 17:21:15 +0000
commit90b89e27236906ec3f7f1897b01999737db91854 (patch)
tree72672d523f2651785d15b7f344b37e55cec46e6b /sys
parent5e4dc0921a9eff2e8cc4f3c24a54ddd30e6c1d4a (diff)
Support for standard SD host controllers like the Ricoh 5C822, a small
generic bus layer, and SCSI emulation for SD/MMC memory cards.
Diffstat (limited to 'sys')
-rw-r--r--sys/arch/i386/conf/GENERIC6
-rw-r--r--sys/arch/i386/conf/files.i3867
-rw-r--r--sys/conf/files7
-rw-r--r--sys/dev/pci/files.pci6
-rw-r--r--sys/dev/pci/pcireg.h3
-rw-r--r--sys/dev/pci/sdhc_pci.c108
-rw-r--r--sys/dev/sdmmc/files.sdmmc11
-rw-r--r--sys/dev/sdmmc/sdhc.c1065
-rw-r--r--sys/dev/sdmmc/sdhcreg.h173
-rw-r--r--sys/dev/sdmmc/sdhcvar.h39
-rw-r--r--sys/dev/sdmmc/sdmmc.c556
-rw-r--r--sys/dev/sdmmc/sdmmc_io.c140
-rw-r--r--sys/dev/sdmmc/sdmmc_mem.c243
-rw-r--r--sys/dev/sdmmc/sdmmc_scsi.c238
-rw-r--r--sys/dev/sdmmc/sdmmc_scsi.h39
-rw-r--r--sys/dev/sdmmc/sdmmcchip.h78
-rw-r--r--sys/dev/sdmmc/sdmmcreg.h232
-rw-r--r--sys/dev/sdmmc/sdmmcvar.h127
18 files changed, 3073 insertions, 5 deletions
diff --git a/sys/arch/i386/conf/GENERIC b/sys/arch/i386/conf/GENERIC
index a945e1295b2..8933280aab2 100644
--- a/sys/arch/i386/conf/GENERIC
+++ b/sys/arch/i386/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.497 2006/05/27 19:10:43 dlg Exp $
+# $OpenBSD: GENERIC,v 1.498 2006/05/28 17:21:14 uwe Exp $
#
# For further information on compiling OpenBSD kernels, see the config(8)
# man page.
@@ -241,6 +241,10 @@ ural* at uhub? # Ralink RT2500
udcf* at uhub? # Gude Expert mouseCLOCK
puc* at pci? # PCI "universal" communication device
+sdhc* at pci? # SD Host Controller
+sdmmc* at sdhc? # SD/MMC bus
+scsibus* at sdmmc? # SCSI emulation
+
npx0 at isa? port 0xf0 irq 13 # math coprocessor
isadma0 at isa?
isapnp0 at isa?
diff --git a/sys/arch/i386/conf/files.i386 b/sys/arch/i386/conf/files.i386
index 2feff89137d..3fbcde421ec 100644
--- a/sys/arch/i386/conf/files.i386
+++ b/sys/arch/i386/conf/files.i386
@@ -1,4 +1,4 @@
-# $OpenBSD: files.i386,v 1.146 2006/05/01 17:01:14 kettenis Exp $
+# $OpenBSD: files.i386,v 1.147 2006/05/28 17:21:14 uwe Exp $
#
# new style config file for i386 architecture
#
@@ -401,6 +401,11 @@ include "dev/gpio/files.gpio"
#
include "dev/onewire/files.onewire"
+#
+# Machine-independent SD/MMC drivers
+#
+include "dev/sdmmc/files.sdmmc"
+
include "../../../dev/acpi/files.acpi"
file arch/i386/i386/acpi_machdep.c acpi
diff --git a/sys/conf/files b/sys/conf/files
index 83f025ec433..1ce78a06430 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1,4 +1,4 @@
-# $OpenBSD: files,v 1.373 2006/05/27 23:40:27 claudio Exp $
+# $OpenBSD: files,v 1.374 2006/05/28 17:21:14 uwe Exp $
# $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
@@ -388,6 +388,7 @@ define usbus {} # USB attachment
define pcmciabus {[controller = -1], [socket = -1]} # PCMCIA attachment
define cbbus {[slot = -1]} # CardBus attachment
define pcmciaslot {[slot = -1]} # PCMCIA slot itself
+define sdmmcbus {} # SD/MMC attachment
# UHCI USB controller
device uhci: usbus
@@ -401,6 +402,10 @@ file dev/usb/ohci.c ohci needs-flag
device ehci: usbus
file dev/usb/ehci.c ehci needs-flag
+# SDHC SD/MMC controller
+device sdhc: sdmmcbus
+file dev/sdmmc/sdhc.c sdhc needs-flag
+
# radio devices, attaches to radio hardware driver
device radio
attach radio at radiobus
diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci
index 1f2b12c9682..2c8df1bd2f2 100644
--- a/sys/dev/pci/files.pci
+++ b/sys/dev/pci/files.pci
@@ -1,4 +1,4 @@
-# $OpenBSD: files.pci,v 1.206 2006/05/27 19:03:55 dlg Exp $
+# $OpenBSD: files.pci,v 1.207 2006/05/28 17:21:14 uwe Exp $
# $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $
#
# Config file and device description for machine-independent PCI code.
@@ -617,3 +617,7 @@ file dev/pci/amdiic.c amdiic
device nviic: i2cbus
attach nviic at pci
file dev/pci/nviic.c nviic
+
+# SD Host Controller
+attach sdhc at pci with sdhc_pci
+file dev/pci/sdhc_pci.c sdhc
diff --git a/sys/dev/pci/pcireg.h b/sys/dev/pci/pcireg.h
index 534d833517f..893cbb7640b 100644
--- a/sys/dev/pci/pcireg.h
+++ b/sys/dev/pci/pcireg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: pcireg.h,v 1.29 2006/05/11 23:29:12 brad Exp $ */
+/* $OpenBSD: pcireg.h,v 1.30 2006/05/28 17:21:14 uwe Exp $ */
/* $NetBSD: pcireg.h,v 1.26 2000/05/10 16:58:42 thorpej Exp $ */
/*
@@ -221,6 +221,7 @@ typedef u_int8_t pci_revision_t;
#define PCI_SUBCLASS_SYSTEM_TIMER 0x02
#define PCI_SUBCLASS_SYSTEM_RTC 0x03
#define PCI_SUBCLASS_SYSTEM_PCIHOTPLUG 0x04
+#define PCI_SUBCLASS_SYSTEM_SDHC 0x05
#define PCI_SUBCLASS_SYSTEM_MISC 0x80
/* 0x09 input subclasses */
diff --git a/sys/dev/pci/sdhc_pci.c b/sys/dev/pci/sdhc_pci.c
new file mode 100644
index 00000000000..d0857c38fc5
--- /dev/null
+++ b/sys/dev/pci/sdhc_pci.c
@@ -0,0 +1,108 @@
+/* $OpenBSD: sdhc_pci.c,v 1.1 2006/05/28 17:21:14 uwe Exp $ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/systm.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/sdmmc/sdhcreg.h>
+#include <dev/sdmmc/sdhcvar.h>
+#include <dev/sdmmc/sdmmcvar.h>
+
+struct sdhc_pci_softc {
+ struct sdhc_softc sc;
+ void *sc_ih;
+};
+
+int sdhc_pci_match(struct device *, void *, void *);
+void sdhc_pci_attach(struct device *, struct device *, void *);
+
+struct cfattach sdhc_pci_ca = {
+ sizeof(struct sdhc_pci_softc), sdhc_pci_match, sdhc_pci_attach
+};
+
+int
+sdhc_pci_match(struct device *parent, void *match, void *aux)
+{
+ struct pci_attach_args *pa = aux;
+
+ if (PCI_CLASS(pa->pa_class) == PCI_CLASS_SYSTEM &&
+ PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_SYSTEM_SDHC)
+ return 1;
+
+ return 0;
+}
+
+void
+sdhc_pci_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct sdhc_pci_softc *sc = (struct sdhc_pci_softc *)self;
+ struct pci_attach_args *pa = aux;
+ pci_intr_handle_t ih;
+ char const *intrstr;
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+ bus_size_t size;
+ int usedma;
+ int reg;
+
+ if (pci_intr_map(pa, &ih)) {
+ printf(": can't map interrupt\n");
+ return;
+ }
+
+ intrstr = pci_intr_string(pa->pa_pc, ih);
+ sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_SDMMC,
+ sdhc_intr, sc, sc->sc.sc_dev.dv_xname);
+ if (sc->sc_ih == NULL) {
+ printf(": can't establish interrupt\n");
+ return;
+ }
+ printf(": %s\n", intrstr);
+
+ /* Enable use of DMA if supported by the interface. */
+ usedma = PCI_INTERFACE(pa->pa_class) == SDHC_PCI_INTERFACE_DMA;
+
+ /*
+ * Map and attach all hosts supported by the host controller.
+ */
+ for (reg = SDHC_PCI_BAR_START; reg < SDHC_PCI_BAR_END; reg += 4) {
+
+ if (pci_mem_find(pa->pa_pc, pa->pa_tag, reg,
+ NULL, NULL, NULL) != 0)
+ continue;
+
+ if (pci_mapreg_map(pa, reg, PCI_MAPREG_TYPE_MEM, 0,
+ &iot, &ioh, NULL, &size, 0)) {
+ printf("%s at 0x%x: can't map registers\n",
+ sc->sc.sc_dev.dv_xname, reg);
+ continue;
+ }
+
+ if (sdhc_host_found(&sc->sc, iot, ioh, size, usedma) != 0)
+ printf("%s at 0x%x: can't initialize host\n",
+ sc->sc.sc_dev.dv_xname, reg);
+ }
+
+ /*
+ * Establish power and shutdown hooks.
+ */
+ (void)powerhook_establish(sdhc_power, &sc->sc);
+ (void)shutdownhook_establish(sdhc_shutdown, &sc->sc);
+}
diff --git a/sys/dev/sdmmc/files.sdmmc b/sys/dev/sdmmc/files.sdmmc
new file mode 100644
index 00000000000..6462e6e0a77
--- /dev/null
+++ b/sys/dev/sdmmc/files.sdmmc
@@ -0,0 +1,11 @@
+# $OpenBSD: files.sdmmc,v 1.1 2006/05/28 17:21:14 uwe Exp $
+#
+# Config file and device description for machine-independent SD/MMC code.
+# Included by ports that need it.
+
+device sdmmc: scsi
+attach sdmmc at sdmmcbus
+file dev/sdmmc/sdmmc.c sdmmc
+file dev/sdmmc/sdmmc_io.c sdmmc
+file dev/sdmmc/sdmmc_mem.c sdmmc
+file dev/sdmmc/sdmmc_scsi.c sdmmc
diff --git a/sys/dev/sdmmc/sdhc.c b/sys/dev/sdmmc/sdhc.c
new file mode 100644
index 00000000000..ea800debd1a
--- /dev/null
+++ b/sys/dev/sdmmc/sdhc.c
@@ -0,0 +1,1065 @@
+/* $OpenBSD: sdhc.c,v 1.1 2006/05/28 17:21:14 uwe Exp $ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * SD Host Controller driver based on the SD Host Controller Standard
+ * Simplified Specification Version 1.00 (www.sdcard.com).
+ */
+
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+#include <sys/timeout.h>
+
+#include <dev/sdmmc/sdhcreg.h>
+#include <dev/sdmmc/sdhcvar.h>
+#include <dev/sdmmc/sdmmcchip.h>
+#include <dev/sdmmc/sdmmcreg.h>
+#include <dev/sdmmc/sdmmcvar.h>
+
+#undef SDHC_DEBUG
+
+#define SDHC_COMMAND_TIMEOUT (hz*2) /* 2 seconds */
+#define SDHC_DATA_TIMEOUT (hz*2) /* 2 seconds */
+
+struct sdhc_host {
+ struct sdhc_softc *sc; /* host controller device */
+ struct device *sdmmc; /* generic SD/MMC device */
+ bus_space_tag_t iot; /* host register set tag */
+ bus_space_handle_t ioh; /* host register set handle */
+ u_int clkbase; /* base clock frequency in KHz */
+ int maxblklen; /* maximum block length */
+ int flags; /* flags for this host */
+ u_int32_t ocr; /* OCR value from capabilities */
+ struct proc *event_thread; /* event processing thread */
+ struct sdmmc_command *cmd; /* current command or NULL */
+ struct timeout cmd_to; /* command timeout */
+};
+
+#define HDEVNAME(hp) ((hp)->sdmmc->dv_xname)
+
+/* flag values */
+#define SHF_USE_DMA 0x0001
+#define SHF_CARD_PRESENT 0x0002
+#define SHF_CARD_ATTACHED 0x0004
+
+#define HREAD1(hp, reg) \
+ (bus_space_read_1((hp)->iot, (hp)->ioh, (reg)))
+#define HREAD2(hp, reg) \
+ (bus_space_read_2((hp)->iot, (hp)->ioh, (reg)))
+#define HREAD4(hp, reg) \
+ (bus_space_read_4((hp)->iot, (hp)->ioh, (reg)))
+#define HWRITE1(hp, reg, val) \
+ bus_space_write_1((hp)->iot, (hp)->ioh, (reg), (val))
+#define HWRITE2(hp, reg, val) \
+ bus_space_write_2((hp)->iot, (hp)->ioh, (reg), (val))
+#define HWRITE4(hp, reg, val) \
+ bus_space_write_4((hp)->iot, (hp)->ioh, (reg), (val))
+#define HCLR1(hp, reg, bits) \
+ HWRITE1((hp), (reg), HREAD1((hp), (reg)) & ~(bits))
+#define HCLR2(hp, reg, bits) \
+ HWRITE2((hp), (reg), HREAD2((hp), (reg)) & ~(bits))
+#define HSET1(hp, reg, bits) \
+ HWRITE1((hp), (reg), HREAD1((hp), (reg)) | (bits))
+#define HSET2(hp, reg, bits) \
+ HWRITE2((hp), (reg), HREAD2((hp), (reg)) | (bits))
+
+void sdhc_create_event_thread(void *);
+void sdhc_event_thread(void *);
+void sdhc_event_process(struct sdhc_host *);
+
+int sdhc_host_reset(sdmmc_chipset_handle_t);
+u_int32_t sdhc_host_ocr(sdmmc_chipset_handle_t);
+int sdhc_host_maxblklen(sdmmc_chipset_handle_t);
+int sdhc_card_detect(sdmmc_chipset_handle_t);
+int sdhc_bus_power(sdmmc_chipset_handle_t, u_int32_t);
+int sdhc_bus_clock(sdmmc_chipset_handle_t, int);
+int sdhc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *);
+int sdhc_wait_state(struct sdhc_host *, u_int32_t, u_int32_t);
+int sdhc_start_command(struct sdhc_host *, struct sdmmc_command *);
+int sdhc_wait_command(struct sdhc_host *, int);
+int sdhc_finish_command(struct sdhc_host *);
+void sdhc_transfer_data(struct sdhc_host *);
+void sdhc_read_data(struct sdhc_host *, u_char *, int);
+void sdhc_write_data(struct sdhc_host *, u_char *, int);
+void sdhc_command_timeout(void *);
+
+#ifdef SDHC_DEBUG
+void sdhc_dump_regs(struct sdhc_host *);
+#define DPRINTF(s) printf s
+#else
+#define DPRINTF(s) /**/
+#endif
+
+struct sdmmc_chip_functions sdhc_functions = {
+ /* host controller reset */
+ sdhc_host_reset,
+ /* host controller capabilities */
+ sdhc_host_ocr,
+ sdhc_host_maxblklen,
+ /* card detection */
+ sdhc_card_detect,
+ /* bus power and clock frequency */
+ sdhc_bus_power,
+ sdhc_bus_clock,
+ /* command execution */
+ sdhc_exec_command
+};
+
+struct cfdriver sdhc_cd = {
+ NULL, "sdhc", DV_DULL
+};
+
+/*
+ * Called by attachment driver. For each SD card slot there is one SD
+ * host controller standard register set. (1.3)
+ */
+int
+sdhc_host_found(struct sdhc_softc *sc, bus_space_tag_t iot,
+ bus_space_handle_t ioh, bus_size_t iosize, int usedma)
+{
+ struct sdmmcbus_attach_args saa;
+ struct sdhc_host *hp;
+ u_int32_t caps;
+#ifdef SDHC_DEBUG
+ u_int16_t version;
+
+ version = bus_space_read_2(iot, ioh, SDHC_HOST_CTL_VERSION);
+ printf("%s: SD Host Specification/Vendor Version ",
+ sc->sc_dev.dv_xname);
+ switch(SDHC_SPEC_VERSION(version)) {
+ case 0x00:
+ printf("1.0/%u\n", SDHC_VENDOR_VERSION(version));
+ break;
+ default:
+ printf(">1.0/%u\n", SDHC_VENDOR_VERSION(version));
+ break;
+ }
+#endif
+
+ /* Allocate one more host structure. */
+ sc->sc_nhosts++;
+ MALLOC(hp, struct sdhc_host *, sizeof(struct sdhc_host) *
+ sc->sc_nhosts, M_DEVBUF, M_WAITOK);
+ if (sc->sc_host != NULL) {
+ bcopy(sc->sc_host, hp, sizeof(struct sdhc_host) *
+ (sc->sc_nhosts-1));
+ FREE(sc->sc_host, M_DEVBUF);
+ }
+ sc->sc_host = hp;
+
+ /* Fill in the new host structure. */
+ hp = &sc->sc_host[sc->sc_nhosts-1];
+ bzero(hp, sizeof(struct sdhc_host));
+ hp->sc = sc;
+ hp->iot = iot;
+ hp->ioh = ioh;
+ timeout_set(&hp->cmd_to, sdhc_command_timeout, hp);
+
+ /*
+ * Reset the host controller and enable interrupts.
+ */
+ if (sdhc_host_reset(hp) != 0) {
+ printf("%s: host reset failed\n", sc->sc_dev.dv_xname);
+ goto err;
+ }
+
+ /* Determine host capabilities. */
+ caps = HREAD4(hp, SDHC_CAPABILITIES);
+
+ /* Use DMA if the host system and the controller support it. */
+ if (usedma && ISSET(caps, SDHC_DMA_SUPPORT))
+ SET(hp->flags, SHF_USE_DMA);
+
+ /*
+ * Determine the base clock frequency. (2.2.24)
+ */
+ if (SDHC_BASE_FREQ_KHZ(caps) != 0)
+ hp->clkbase = SDHC_BASE_FREQ_KHZ(caps);
+ if (hp->clkbase == 0) {
+ /* The attachment driver must tell us. */
+ printf("%s: base clock frequency unknown\n",
+ sc->sc_dev.dv_xname);
+ goto err;
+ } else if (hp->clkbase < 10000 || hp->clkbase > 63000) {
+ /* SDHC 1.0 supports only 10-63 Mhz. */
+ printf("%s: base clock frequency out of range: %u MHz\n",
+ sc->sc_dev.dv_xname, hp->clkbase);
+ goto err;
+ }
+
+ /*
+ * XXX Set the data timeout counter value according to
+ * capabilities. (2.2.15)
+ */
+
+ /*
+ * Determine SD bus voltage levels supported by the controller.
+ */
+ if (ISSET(caps, SDHC_VOLTAGE_SUPP_1_8V))
+ SET(hp->ocr, MMC_OCR_1_7V_1_8V | MMC_OCR_1_8V_1_9V);
+ if (ISSET(caps, SDHC_VOLTAGE_SUPP_3_0V))
+ SET(hp->ocr, MMC_OCR_2_9V_3_0V | MMC_OCR_3_0V_3_1V);
+ if (ISSET(caps, SDHC_VOLTAGE_SUPP_3_3V))
+ SET(hp->ocr, MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V);
+
+ /*
+ * Determine the maximum block length supported by the host
+ * controller. (2.2.24)
+ */
+ switch((caps >> SDHC_MAX_BLK_LEN_SHIFT) & SDHC_MAX_BLK_LEN_MASK) {
+ case SDHC_MAX_BLK_LEN_512:
+ hp->maxblklen = 512;
+ break;
+ case SDHC_MAX_BLK_LEN_1024:
+ hp->maxblklen = 1024;
+ break;
+ case SDHC_MAX_BLK_LEN_2048:
+ hp->maxblklen = 2048;
+ break;
+ default:
+ hp->maxblklen = 1;
+ break;
+ }
+
+ /*
+ * Attach the generic SD/MMC bus driver. (The bus driver must
+ * not invoke any chipset functions before it is attached.)
+ */
+ bzero(&saa, sizeof(saa));
+ saa.saa_busname = "sdmmc";
+ saa.sct = &sdhc_functions;
+ saa.sch = hp;
+
+ hp->sdmmc = config_found(&sc->sc_dev, &saa, NULL);
+ if (hp->sdmmc == NULL) {
+ printf("%s: can't attach bus\n", sc->sc_dev.dv_xname);
+ goto err;
+ }
+
+ /*
+ * Create the event thread that will attach and detach cards
+ * and perform other lengthy operations.
+ */
+#ifdef DO_CONFIG_PENDING
+ config_pending_incr();
+#endif
+ kthread_create_deferred(sdhc_create_event_thread, hp);
+
+ return 0;
+
+err:
+ /* XXX: Leaking one sdhc_host structure here. */
+ sc->sc_nhosts--;
+ return 1;
+}
+
+void
+sdhc_create_event_thread(void *arg)
+{
+ struct sdhc_host *hp = arg;
+
+ /* If there's a card, attach it. */
+ sdhc_event_process(hp);
+
+ if (kthread_create(sdhc_event_thread, hp, &hp->event_thread,
+ HDEVNAME(hp)) != 0)
+ printf("%s: can't create event thread\n", HDEVNAME(hp));
+
+#ifdef DO_CONFIG_PENDING
+ config_pending_decr();
+#endif
+}
+
+void
+sdhc_event_thread(void *arg)
+{
+ struct sdhc_host *hp = arg;
+
+ for (;;) {
+ //DPRINTF(("%s: tsleep sdhcev\n", HDEVNAME(hp)));
+ (void)tsleep((caddr_t)hp, PWAIT, "sdhcev", 0);
+ sdhc_event_process(hp);
+ }
+}
+
+void
+sdhc_event_process(struct sdhc_host *hp)
+{
+ //DPRINTF(("%s: event process\n", HDEVNAME(hp)));
+
+ /* If there's a card, attach it, if it went away, detach it. */
+ if (sdhc_card_detect(hp)) {
+ if (!ISSET(hp->flags, SHF_CARD_PRESENT)) {
+ SET(hp->flags, SHF_CARD_PRESENT);
+ if (sdmmc_card_attach(hp->sdmmc) == 0)
+ SET(hp->flags, SHF_CARD_ATTACHED);
+ }
+ } else {
+ /* XXX If a command was in progress, abort it. */
+ int s = splsdmmc();
+ if (hp->cmd != NULL) {
+ timeout_del(&hp->cmd_to);
+ printf("%s: interrupted command %u\n",
+ HDEVNAME(hp), hp->cmd->c_opcode);
+ hp->cmd = NULL;
+ }
+ splx(s);
+
+ if (ISSET(hp->flags, SHF_CARD_PRESENT)) {
+ CLR(hp->flags, SHF_CARD_PRESENT);
+ if (ISSET(hp->flags, SHF_CARD_ATTACHED)) {
+ sdmmc_card_detach(hp->sdmmc, DETACH_FORCE);
+ CLR(hp->flags, SHF_CARD_ATTACHED);
+ }
+ }
+ }
+}
+
+/*
+ * Power hook established by or called from attachment driver.
+ */
+void
+sdhc_power(int why, void *arg)
+{
+ /* struct sdhc_softc *sc = arg; */
+
+ switch(why) {
+ case PWR_STANDBY:
+ case PWR_SUSPEND:
+ /* XXX suspend or detach cards */
+ break;
+ case PWR_RESUME:
+ /* XXX resume or reattach cards */
+ break;
+ }
+}
+
+/*
+ * Shutdown hook established by or called from attachment driver.
+ */
+void
+sdhc_shutdown(void *arg)
+{
+ struct sdhc_softc *sc = arg;
+ struct sdhc_host *hp;
+ int i;
+
+ /* XXX chip locks up if we don't disable it before reboot. */
+ for (i = 0; i < sc->sc_nhosts; i++) {
+ hp = &sc->sc_host[i];
+ sdhc_host_reset(hp);
+ }
+}
+
+/*
+ * Reset the host controller. Called during initialization, when
+ * cards are removed and during error recovery.
+ */
+int
+sdhc_host_reset(sdmmc_chipset_handle_t sch)
+{
+ struct sdhc_host *hp = sch;
+ u_int16_t imask;
+ int error = 0;
+ int timo;
+ int s;
+
+ s = splsdmmc();
+
+ /* Disable all interrupts. */
+ HWRITE2(hp, SDHC_NINTR_SIGNAL_EN, 0);
+
+ /*
+ * Reset the entire host controller and wait up to 100ms.
+ */
+ HWRITE1(hp, SDHC_SOFTWARE_RESET, SDHC_RESET_MASK);
+ for (timo = 10; timo > 0; timo--) {
+ if (!ISSET(HREAD1(hp, SDHC_SOFTWARE_RESET), SDHC_RESET_MASK))
+ break;
+ sdmmc_delay(10000);
+ }
+ if (timo == 0) {
+ HWRITE1(hp, SDHC_SOFTWARE_RESET, 0);
+ error = ETIMEDOUT;
+ }
+
+ /* Set data timeout counter value to max for now. */
+ HWRITE1(hp, SDHC_TIMEOUT_CTL, SDHC_TIMEOUT_MAX);
+
+ /* Enable interrupts. */
+ imask = SDHC_CARD_REMOVAL | SDHC_CARD_INSERTION |
+ SDHC_BUFFER_READ_READY | SDHC_BUFFER_WRITE_READY |
+ SDHC_DMA_INTERRUPT | SDHC_BLOCK_GAP_EVENT |
+ SDHC_TRANSFER_COMPLETE | SDHC_COMMAND_COMPLETE;
+ HWRITE2(hp, SDHC_NINTR_STATUS_EN, imask);
+ HWRITE2(hp, SDHC_EINTR_STATUS_EN, SDHC_EINTR_STATUS_MASK);
+ HWRITE2(hp, SDHC_NINTR_SIGNAL_EN, imask);
+ HWRITE2(hp, SDHC_EINTR_SIGNAL_EN, SDHC_EINTR_SIGNAL_MASK);
+
+ splx(s);
+ return error;
+}
+
+u_int32_t
+sdhc_host_ocr(sdmmc_chipset_handle_t sch)
+{
+ struct sdhc_host *hp = sch;
+ return hp->ocr;
+}
+
+int
+sdhc_host_maxblklen(sdmmc_chipset_handle_t sch)
+{
+ struct sdhc_host *hp = sch;
+ return hp->maxblklen;
+}
+
+/*
+ * Return non-zero if the card is currently inserted.
+ */
+int
+sdhc_card_detect(sdmmc_chipset_handle_t sch)
+{
+ struct sdhc_host *hp = sch;
+ return ISSET(HREAD4(hp, SDHC_PRESENT_STATE), SDHC_CARD_INSERTED) ?
+ 1 : 0;
+}
+
+/*
+ * Set or change SD bus voltage and enable or disable SD bus power.
+ * Return zero on success.
+ */
+int
+sdhc_bus_power(sdmmc_chipset_handle_t sch, u_int32_t ocr)
+{
+ struct sdhc_host *hp = sch;
+ u_int8_t vdd;
+ int s;
+
+ s = splsdmmc();
+
+ /*
+ * Disable bus power before voltage change.
+ */
+ HWRITE1(hp, SDHC_POWER_CTL, 0);
+
+ /* If power is disabled, reset the host and return now. */
+ if (ocr == 0) {
+ splx(s);
+ if (sdhc_host_reset(hp) != 0)
+ printf("%s: host reset failed\n", HDEVNAME(hp));
+ return 0;
+ }
+
+ /*
+ * Select the maximum voltage according to capabilities.
+ */
+ ocr &= hp->ocr;
+ if (ISSET(ocr, MMC_OCR_3_2V_3_3V|MMC_OCR_3_3V_3_4V))
+ vdd = SDHC_VOLTAGE_3_3V;
+ else if (ISSET(ocr, MMC_OCR_2_9V_3_0V|MMC_OCR_3_0V_3_1V))
+ vdd = SDHC_VOLTAGE_3_0V;
+ else if (ISSET(ocr, MMC_OCR_1_7V_1_8V|MMC_OCR_1_8V_1_9V))
+ vdd = SDHC_VOLTAGE_1_8V;
+ else {
+ /* Unsupported voltage level requested. */
+ splx(s);
+ return EINVAL;
+ }
+
+ /*
+ * Enable bus power. Wait at least 1 ms (or 74 clocks) plus
+ * voltage ramp until power rises.
+ */
+ HWRITE1(hp, SDHC_POWER_CTL, (vdd << SDHC_VOLTAGE_SHIFT) |
+ SDHC_BUS_POWER);
+ sdmmc_delay(10000);
+
+ /*
+ * The host system may not power the bus due to battery low,
+ * etc. In that case, the host controller should clear the
+ * bus power bit.
+ */
+ if (!ISSET(HREAD1(hp, SDHC_POWER_CTL), SDHC_BUS_POWER)) {
+ splx(s);
+ return ENXIO;
+ }
+
+ splx(s);
+ return 0;
+}
+
+/*
+ * Return the smallest possible base clock frequency divisor value
+ * for the CLOCK_CTL register to produce `freq' (KHz).
+ */
+static int
+sdhc_clock_divisor(struct sdhc_host *hp, u_int freq)
+{
+ int div;
+
+ for (div = 1; div <= 256; div *= 2)
+ if ((hp->clkbase / div) <= freq)
+ return (div / 2);
+ /* No divisor found. */
+ return -1;
+}
+
+/*
+ * Set or change SDCLK frequency or disable the SD clock.
+ * Return zero on success.
+ */
+int
+sdhc_bus_clock(sdmmc_chipset_handle_t sch, int freq)
+{
+ struct sdhc_host *hp = sch;
+ int s;
+ int div;
+ int timo;
+ int error = 0;
+
+ s = splsdmmc();
+
+#ifdef DIAGNOSTIC
+ /* Must not stop the clock if commands are in progress. */
+ if (ISSET(HREAD4(hp, SDHC_PRESENT_STATE), SDHC_CMD_INHIBIT_MASK) &&
+ sdhc_card_detect(hp))
+ printf("sdhc_sdclk_frequency_select: command in progress\n");
+#endif
+
+ /*
+ * Stop SD clock before changing the frequency.
+ */
+ HWRITE2(hp, SDHC_CLOCK_CTL, 0);
+ if (freq == SDMMC_SDCLK_OFF)
+ goto ret;
+
+ /*
+ * Set the minimum base clock frequency divisor.
+ */
+ if ((div = sdhc_clock_divisor(hp, freq)) < 0) {
+ /* Invalid base clock frequency or `freq' value. */
+ error = EINVAL;
+ goto ret;
+ }
+ HWRITE2(hp, SDHC_CLOCK_CTL, div << SDHC_SDCLK_DIV_SHIFT);
+
+ /*
+ * Start internal clock. Wait 10ms for stabilization.
+ */
+ HSET2(hp, SDHC_CLOCK_CTL, SDHC_INTCLK_ENABLE);
+ for (timo = 1000; timo > 0; timo--) {
+ if (ISSET(HREAD2(hp, SDHC_CLOCK_CTL), SDHC_INTCLK_STABLE))
+ break;
+ sdmmc_delay(10);
+ }
+ if (timo == 0) {
+ error = ETIMEDOUT;
+ goto ret;
+ }
+
+ /*
+ * Enable SD clock.
+ */
+ HSET2(hp, SDHC_CLOCK_CTL, SDHC_SDCLK_ENABLE);
+
+ret:
+ splx(s);
+ return error;
+}
+
+/*
+ * Send a command and data to the card and return the command response
+ * and data from the card.
+ *
+ * If no callback function is specified, execute the command
+ * synchronously; otherwise, return immediately and call the function
+ * from the event thread after the command has completed.
+ */
+int
+sdhc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd)
+{
+ struct sdhc_host *hp = sch;
+ int error;
+
+ if (hp->cmd != NULL)
+ return EBUSY;
+
+ error = sdhc_start_command(hp, cmd);
+ if (error != 0)
+ goto err;
+ if (cmd->c_done != NULL)
+ /* Execute this command asynchronously. */
+ return error;
+
+ error = sdhc_wait_command(hp, SCF_DONE|SCF_CMD_DONE);
+ if (error != 0)
+ goto err;
+ return sdhc_finish_command(hp);
+ err:
+ cmd->c_error = error;
+ SET(cmd->c_flags, SCF_DONE);
+ return sdhc_finish_command(hp);
+}
+
+int
+sdhc_wait_state(struct sdhc_host *hp, u_int32_t mask, u_int32_t value)
+{
+ u_int32_t state;
+ int timeout;
+
+ for (timeout = 10; timeout > 0; timeout--) {
+ if (((state = HREAD4(hp, SDHC_PRESENT_STATE)) & mask)
+ == value)
+ return 0;
+ sdmmc_delay(10000);
+ }
+ DPRINTF(("%s: timeout waiting for %x (state=%b)\n", HDEVNAME(hp),
+ value, state, SDHC_PRESENT_STATE_BITS));
+ return ETIMEDOUT;
+}
+
+int
+sdhc_start_command(struct sdhc_host *hp, struct sdmmc_command *cmd)
+{
+ u_int16_t blksize = 0;
+ u_int16_t blkcount = 0;
+ u_int16_t mode;
+ u_int16_t command;
+ int error;
+ int s;
+
+ DPRINTF(("%s: start cmd %u\n", HDEVNAME(hp), cmd->c_opcode));
+ hp->cmd = cmd;
+
+ /* If the card went away, finish the command immediately. */
+ if (!ISSET(hp->flags, SHF_CARD_PRESENT))
+ return ETIMEDOUT;
+
+ /*
+ * The maximum block length for commands should be the minimum
+ * of the host buffer size and the card buffer size. (1.7.2)
+ */
+
+ /* Fragment the data into proper blocks. */
+ if (cmd->c_datalen > 0) {
+ blksize = cmd->c_blklen;
+ blkcount = cmd->c_datalen / blksize;
+ if (cmd->c_datalen % blksize > 0) {
+ /* XXX: Split this command. (1.7.4) */
+ printf("%s: data not a multiple of %d bytes\n",
+ HDEVNAME(hp), blksize);
+ return EINVAL;
+ }
+ }
+
+ /* Check limit imposed by 9-bit block count. (1.7.2) */
+ if (blkcount > SDHC_BLOCK_COUNT_MAX) {
+ printf("%s: too much data\n", HDEVNAME(hp));
+ return EINVAL;
+ }
+
+ /* Prepare transfer mode register value. (2.2.5) */
+ mode = 0;
+ if (ISSET(cmd->c_flags, SCF_CMD_READ))
+ mode |= SDHC_READ_MODE;
+ if (blkcount > 0) {
+ mode |= SDHC_BLOCK_COUNT_ENABLE;
+ if (blkcount > 1) {
+ mode |= SDHC_MULTI_BLOCK_MODE;
+ mode |= SDHC_AUTO_CMD12_ENABLE;
+ }
+ }
+#ifdef notyet
+ if (ISSET(hp->flags, SHF_USE_DMA))
+ mode |= SDHC_DMA_ENABLE;
+#endif
+
+ /*
+ * Prepare command register value. (2.2.6)
+ */
+ command = (cmd->c_opcode & SDHC_COMMAND_INDEX_MASK) <<
+ SDHC_COMMAND_INDEX_SHIFT;
+
+ if (ISSET(cmd->c_flags, SCF_RSP_CRC))
+ command |= SDHC_CRC_CHECK_ENABLE;
+ if (ISSET(cmd->c_flags, SCF_RSP_IDX))
+ command |= SDHC_INDEX_CHECK_ENABLE;
+ if (cmd->c_data != NULL)
+ command |= SDHC_DATA_PRESENT_SELECT;
+
+ if (!ISSET(cmd->c_flags, SCF_RSP_PRESENT))
+ command |= SDHC_NO_RESPONSE;
+ else if (ISSET(cmd->c_flags, SCF_RSP_136))
+ command |= SDHC_RESP_LEN_136;
+ else if (ISSET(cmd->c_flags, SCF_RSP_BSY))
+ command |= SDHC_RESP_LEN_48_CHK_BUSY;
+ else
+ command |= SDHC_RESP_LEN_48;
+
+ /* Wait until command and data inhibit bits are clear. (1.5) */
+ if ((error = sdhc_wait_state(hp, SDHC_CMD_INHIBIT_MASK, 0)) != 0)
+ return error;
+
+ s = splsdmmc();
+
+ /* Alert the user not to remove the card. */
+ HSET1(hp, SDHC_HOST_CTL, SDHC_LED_ON);
+
+ /* XXX: Set DMA start address if SHF_USE_DMA is set. */
+
+ /*
+ * Start a CPU data transfer. Writing to the high order byte
+ * of the SDHC_COMMAND register triggers the SD command. (1.5)
+ */
+ HWRITE2(hp, SDHC_BLOCK_SIZE, blksize);
+ HWRITE2(hp, SDHC_BLOCK_COUNT, blkcount);
+ HWRITE4(hp, SDHC_ARGUMENT, cmd->c_arg);
+ HWRITE2(hp, SDHC_TRANSFER_MODE, mode);
+ HWRITE2(hp, SDHC_COMMAND, command);
+
+ /*
+ * Start a software timeout. In the unlikely event that the
+ * controller's own timeout detection mechanism fails we will
+ * abort the transfer in software.
+ */
+ timeout_add(&hp->cmd_to, SDHC_COMMAND_TIMEOUT);
+
+ splx(s);
+ return 0;
+}
+
+int
+sdhc_wait_command(struct sdhc_host *hp, int flags)
+{
+ int s;
+
+ for (;;) {
+ /* Return if the command was aborted. */
+ if (hp->cmd == NULL)
+ return EIO;
+
+ s = splsdmmc();
+
+ /* Return if the command has reached the awaited state. */
+ if (ISSET(hp->cmd->c_flags, flags)) {
+ splx(s);
+ return 0;
+ }
+
+ DPRINTF(("%s: tsleep sdhccmd (flags=%#x)\n",
+ HDEVNAME(hp), flags));
+ (void)tsleep((caddr_t)hp, PWAIT, "sdhccmd", 0);
+
+ /* Process card events. */
+ sdhc_event_process(hp);
+
+ splx(s);
+ }
+}
+
+int
+sdhc_finish_command(struct sdhc_host *hp)
+{
+ struct sdmmc_command *cmd = hp->cmd;
+ int error;
+
+ if (cmd == NULL) {
+ DPRINTF(("%s: finish NULL cmd\n", HDEVNAME(hp)));
+ return 0;
+ }
+
+ /* Cancel command timeout. */
+ timeout_del(&hp->cmd_to);
+
+ DPRINTF(("%s: finish cmd %u (flags=%#x error=%d)\n",
+ HDEVNAME(hp), cmd->c_opcode, cmd->c_flags, cmd->c_error));
+
+ /*
+ * The host controller removes bits [0:7] from the response
+ * data (CRC) and we pass the data up unchanged to the bus
+ * driver (without padding).
+ */
+ if (cmd->c_error == 0 && ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
+ if (ISSET(cmd->c_flags, SCF_RSP_136)) {
+ u_char *p = (u_char *)cmd->c_resp;
+ int i;
+
+ for (i = 0; i < 15; i++)
+ *p++ = HREAD1(hp, SDHC_RESPONSE + i);
+ } else
+ cmd->c_resp[0] = HREAD4(hp, SDHC_RESPONSE);
+ }
+
+ if (cmd->c_error == 0 && cmd->c_data != NULL) {
+ timeout_add(&hp->cmd_to, SDHC_DATA_TIMEOUT);
+ sdhc_transfer_data(hp);
+ }
+
+ /* Turn off the LED. */
+ HCLR1(hp, SDHC_HOST_CTL, SDHC_LED_ON);
+
+ error = cmd->c_error;
+ hp->cmd = NULL;
+
+ SET(cmd->c_flags, SCF_DONE);
+ DPRINTF(("%s: cmd %u done (flags=%#x error=%d)\n",
+ HDEVNAME(hp), cmd->c_opcode, cmd->c_flags, error));
+ if (cmd->c_done != NULL)
+ cmd->c_done(hp->sdmmc, cmd);
+ return error;
+}
+
+void
+sdhc_transfer_data(struct sdhc_host *hp)
+{
+ struct sdmmc_command *cmd = hp->cmd;
+ u_char *datap = cmd->c_data;
+ int i, datalen;
+ int mask;
+ int error;
+
+ mask = ISSET(cmd->c_flags, SCF_CMD_READ) ?
+ SDHC_BUFFER_READ_ENABLE : SDHC_BUFFER_WRITE_ENABLE;
+ error = 0;
+ datalen = cmd->c_datalen;
+
+ DPRINTF(("%s: resp=%#x ", HDEVNAME(hp), MMC_R1(cmd->c_resp)));
+ DPRINTF(("datalen %u\n", datalen));
+ while (datalen > 0) {
+ error = sdhc_wait_command(hp, SCF_DONE|SCF_BUF_READY);
+ if (error != 0)
+ break;
+
+ error = sdhc_wait_state(hp, mask, mask);
+ if (error != 0)
+ break;
+
+ i = MIN(datalen, cmd->c_blklen);
+ if (ISSET(cmd->c_flags, SCF_CMD_READ))
+ sdhc_read_data(hp, datap, i);
+ else
+ sdhc_write_data(hp, datap, i);
+
+ datap += i;
+ datalen -= i;
+ CLR(cmd->c_flags, SCF_BUF_READY);
+ }
+
+ if (error == 0)
+ error = sdhc_wait_command(hp, SCF_DONE|SCF_XFR_DONE);
+
+ timeout_del(&hp->cmd_to);
+
+ if (cmd->c_error == 0) {
+ cmd->c_error = error;
+ SET(cmd->c_flags, SCF_DONE);
+ }
+
+ DPRINTF(("%s: data transfer done (error=%d)\n",
+ HDEVNAME(hp), cmd->c_error));
+}
+
+void
+sdhc_read_data(struct sdhc_host *hp, u_char *datap, int datalen)
+{
+ while (datalen > 0) {
+ if (datalen > 3) {
+ *((u_int32_t *)datap)++ = HREAD4(hp, SDHC_DATA);
+ datalen -= 4;
+ } else if (datalen > 1) {
+ *((u_int16_t *)datap)++ = HREAD2(hp, SDHC_DATA);
+ datalen -= 2;
+ } else {
+ *datap++ = HREAD1(hp, SDHC_DATA);
+ datalen -= 1;
+ }
+ }
+}
+
+void
+sdhc_write_data(struct sdhc_host *hp, u_char *datap, int datalen)
+{
+ while (datalen > 0) {
+ if (datalen > 3) {
+ HWRITE4(hp, SDHC_DATA, *(u_int32_t *)datap);
+ datap += 4;
+ datalen -= 4;
+ } else if (datalen > 1) {
+ HWRITE2(hp, SDHC_DATA, *(u_int16_t *)datap);
+ datap += 2;
+ datalen -= 2;
+ } else {
+ HWRITE1(hp, SDHC_DATA, *datap);
+ datap++;
+ datalen -= 1;
+ }
+ }
+}
+
+void
+sdhc_command_timeout(void *arg)
+{
+ struct sdhc_host *hp = arg;
+ struct sdmmc_command *cmd = hp->cmd;
+ int s;
+
+ if (cmd == NULL)
+ return;
+
+ s = splsdmmc();
+ if (!ISSET(cmd->c_flags, SCF_DONE)) {
+ DPRINTF(("%s: timeout cmd %u, resetting...\n",
+ HDEVNAME(hp), cmd->c_opcode));
+ cmd->c_error = ETIMEDOUT;
+ SET(cmd->c_flags, SCF_DONE);
+ HWRITE1(hp, SDHC_SOFTWARE_RESET, SDHC_RESET_DAT|
+ SDHC_RESET_CMD);
+ timeout_add(&hp->cmd_to, hz/2);
+ } else {
+ DPRINTF(("%s: timeout cmd %u, resetting...done\n",
+ HDEVNAME(hp), cmd->c_opcode));
+ wakeup(hp);
+ }
+ splx(s);
+}
+
+/*
+ * Established by attachment driver at interrupt priority IPL_SDMMC.
+ */
+int
+sdhc_intr(void *arg)
+{
+ struct sdhc_softc *sc = arg;
+ int done = 0;
+ int host;
+
+ /* We got an interrupt, but we don't know from which slot. */
+ for (host = 0; host < sc->sc_nhosts; host++) {
+ struct sdhc_host *hp = &sc->sc_host[host];
+ u_int16_t status;
+
+ if (hp == NULL)
+ continue;
+
+ status = HREAD2(hp, SDHC_NINTR_STATUS);
+ if (!ISSET(status, SDHC_NINTR_STATUS_MASK))
+ continue;
+
+ /* Clear interrupts we are about to handle. */
+ HWRITE2(hp, SDHC_NINTR_STATUS, status);
+#ifdef SDHC_DEBUG
+ printf("%s: interrupt status=%b\n", HDEVNAME(hp),
+ status, SDHC_NINTR_STATUS_BITS);
+#endif
+
+ /*
+ * Wake up the event thread to service the interrupt(s).
+ */
+ if (ISSET(status, SDHC_BUFFER_READ_READY|
+ SDHC_BUFFER_WRITE_READY)) {
+ if (hp->cmd != NULL &&
+ !ISSET(hp->cmd->c_flags, SCF_DONE)) {
+ SET(hp->cmd->c_flags, SCF_BUF_READY);
+ wakeup(hp);
+ }
+ done++;
+ }
+ if (ISSET(status, SDHC_COMMAND_COMPLETE)) {
+ if (hp->cmd != NULL &&
+ !ISSET(hp->cmd->c_flags, SCF_DONE)) {
+ SET(hp->cmd->c_flags, SCF_CMD_DONE);
+ wakeup(hp);
+ }
+ done++;
+ }
+ if (ISSET(status, SDHC_TRANSFER_COMPLETE)) {
+ if (hp->cmd != NULL &&
+ !ISSET(hp->cmd->c_flags, SCF_DONE)) {
+ SET(hp->cmd->c_flags, SCF_XFR_DONE);
+ wakeup(hp);
+ }
+ done++;
+ }
+ if (ISSET(status, SDHC_CARD_REMOVAL|SDHC_CARD_INSERTION)) {
+ wakeup(hp);
+ done++;
+ }
+
+ if (ISSET(status, SDHC_ERROR_INTERRUPT)) {
+ u_int16_t error;
+
+ error = HREAD2(hp, SDHC_EINTR_STATUS);
+ HWRITE2(hp, SDHC_EINTR_STATUS, error);
+
+ DPRINTF(("%s: error interrupt, status=%b\n",
+ HDEVNAME(hp), error, SDHC_EINTR_STATUS_BITS));
+
+ if (ISSET(error, SDHC_CMD_TIMEOUT_ERROR|
+ SDHC_DATA_TIMEOUT_ERROR) && hp->cmd != NULL &&
+ !ISSET(hp->cmd->c_flags, SCF_DONE)) {
+ hp->cmd->c_error = ETIMEDOUT;
+ SET(hp->cmd->c_flags, SCF_DONE);
+ /* XXX can this reset be avoided? */
+ HWRITE1(hp, SDHC_SOFTWARE_RESET,
+ SDHC_RESET_DAT|SDHC_RESET_CMD);
+ timeout_add(&hp->cmd_to, hz/2);
+ }
+ done++;
+ }
+
+ if (ISSET(status, SDHC_CARD_INTERRUPT)) {
+ HCLR2(hp, SDHC_NINTR_STATUS_EN, SDHC_CARD_INTERRUPT);
+ /* XXX service card interrupt */
+ printf("%s: card interrupt\n", HDEVNAME(hp));
+ HSET2(hp, SDHC_NINTR_STATUS_EN, SDHC_CARD_INTERRUPT);
+ }
+ }
+ return done;
+}
+
+#ifdef SDHC_DEBUG
+void
+sdhc_dump_regs(struct sdhc_host *hp)
+{
+ printf("0x%02x PRESENT_STATE: %b\n", SDHC_PRESENT_STATE,
+ HREAD4(hp, SDHC_PRESENT_STATE), SDHC_PRESENT_STATE_BITS);
+ printf("0x%02x POWER_CTL: %x\n", SDHC_POWER_CTL,
+ HREAD1(hp, SDHC_POWER_CTL));
+ printf("0x%02x NINTR_STATUS: %x\n", SDHC_NINTR_STATUS,
+ HREAD2(hp, SDHC_NINTR_STATUS));
+ printf("0x%02x EINTR_STATUS: %x\n", SDHC_EINTR_STATUS,
+ HREAD2(hp, SDHC_EINTR_STATUS));
+ printf("0x%02x NINTR_STATUS_EN: %x\n", SDHC_NINTR_STATUS_EN,
+ HREAD2(hp, SDHC_NINTR_STATUS_EN));
+ printf("0x%02x EINTR_STATUS_EN: %x\n", SDHC_EINTR_STATUS_EN,
+ HREAD2(hp, SDHC_EINTR_STATUS_EN));
+ printf("0x%02x NINTR_SIGNAL_EN: %x\n", SDHC_NINTR_SIGNAL_EN,
+ HREAD2(hp, SDHC_NINTR_SIGNAL_EN));
+ printf("0x%02x EINTR_SIGNAL_EN: %x\n", SDHC_EINTR_SIGNAL_EN,
+ HREAD2(hp, SDHC_EINTR_SIGNAL_EN));
+ printf("0x%02x CAPABILITIES: %x\n", SDHC_CAPABILITIES,
+ HREAD4(hp, SDHC_CAPABILITIES));
+ printf("0x%02x MAX_CAPABILITIES: %x\n", SDHC_MAX_CAPABILITIES,
+ HREAD4(hp, SDHC_MAX_CAPABILITIES));
+}
+#endif
diff --git a/sys/dev/sdmmc/sdhcreg.h b/sys/dev/sdmmc/sdhcreg.h
new file mode 100644
index 00000000000..6b786e1472f
--- /dev/null
+++ b/sys/dev/sdmmc/sdhcreg.h
@@ -0,0 +1,173 @@
+/* $OpenBSD: sdhcreg.h,v 1.1 2006/05/28 17:21:14 uwe Exp $ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _SDHCREG_H_
+#define _SDHCREG_H_
+
+/* PCI base address registers */
+#define SDHC_PCI_BAR_START PCI_MAPREG_START
+#define SDHC_PCI_BAR_END PCI_MAPREG_END
+
+/* PCI interface classes */
+#define SDHC_PCI_INTERFACE_NO_DMA 0x00
+#define SDHC_PCI_INTERFACE_DMA 0x01
+#define SDHC_PCI_INTERFACE_VENDOR 0x02
+
+/* Host standard register set */
+#define SDHC_DMA_ADDR 0x00
+#define SDHC_BLOCK_SIZE 0x04
+#define SDHC_BLOCK_COUNT 0x06
+#define SDHC_BLOCK_COUNT_MAX 512
+#define SDHC_ARGUMENT 0x08
+#define SDHC_TRANSFER_MODE 0x0c
+#define SDHC_MULTI_BLOCK_MODE (1<<5)
+#define SDHC_READ_MODE (1<<4)
+#define SDHC_AUTO_CMD12_ENABLE (1<<2)
+#define SDHC_BLOCK_COUNT_ENABLE (1<<1)
+#define SDHC_DMA_ENABLE (1<<0)
+#define SDHC_COMMAND 0x0e
+#define SDHC_COMMAND_INDEX_SHIFT 8
+#define SDHC_COMMAND_INDEX_MASK 0x3f
+#define SDHC_COMMAND_TYPE_ABORT (3<<6)
+#define SDHC_COMMAND_TYPE_RESUME (2<<6)
+#define SDHC_COMMAND_TYPE_SUSPEND (1<<6)
+#define SDHC_COMMAND_TYPE_NORMAL (0<<6)
+#define SDHC_DATA_PRESENT_SELECT (1<<5)
+#define SDHC_INDEX_CHECK_ENABLE (1<<4)
+#define SDHC_CRC_CHECK_ENABLE (1<<3)
+#define SDHC_NO_RESPONSE (0<<0)
+#define SDHC_RESP_LEN_136 (1<<0)
+#define SDHC_RESP_LEN_48 (2<<0)
+#define SDHC_RESP_LEN_48_CHK_BUSY (3<<0)
+#define SDHC_RESPONSE 0x10 /* - 0x1f */
+#define SDHC_DATA 0x20
+#define SDHC_PRESENT_STATE 0x24
+#define SDHC_CARD_INSERTED (1<<16)
+#define SDHC_BUFFER_READ_ENABLE (1<<11)
+#define SDHC_BUFFER_WRITE_ENABLE (1<<10)
+#define SDHC_CMD_INHIBIT_DAT (1<<1)
+#define SDHC_CMD_INHIBIT_CMD (1<<0)
+#define SDHC_CMD_INHIBIT_MASK 0x0003
+#define SDHC_HOST_CTL 0x28
+#define SDHC_HIGH_SPEED (1<<2)
+#define SDHC_4BIT_MODE (1<<1)
+#define SDHC_LED_ON (1<<0)
+#define SDHC_POWER_CTL 0x29
+#define SDHC_VOLTAGE_SHIFT 1
+#define SDHC_VOLTAGE_MASK 0x07
+#define SDHC_VOLTAGE_3_3V 0x07
+#define SDHC_VOLTAGE_3_0V 0x06
+#define SDHC_VOLTAGE_1_8V 0x05
+#define SDHC_BUS_POWER (1<<0)
+#define SDHC_BLOCK_GAP_CTL 0x2a
+#define SDHC_WAKEUP_CTL 0x2b
+#define SDHC_CLOCK_CTL 0x2c
+#define SDHC_SDCLK_DIV_SHIFT 8
+#define SDHC_SDCLK_DIV_MASK 0xff
+#define SDHC_SDCLK_ENABLE (1<<2)
+#define SDHC_INTCLK_STABLE (1<<1)
+#define SDHC_INTCLK_ENABLE (1<<0)
+#define SDHC_TIMEOUT_CTL 0x2e
+#define SDHC_TIMEOUT_MAX 0x0e
+#define SDHC_SOFTWARE_RESET 0x2f
+#define SDHC_RESET_MASK 0x5
+#define SDHC_RESET_DAT (1<<2)
+#define SDHC_RESET_CMD (1<<1)
+#define SDHC_RESET_ALL (1<<0)
+#define SDHC_NINTR_STATUS 0x30
+#define SDHC_ERROR_INTERRUPT (1<<15)
+#define SDHC_CARD_INTERRUPT (1<<8)
+#define SDHC_CARD_REMOVAL (1<<7)
+#define SDHC_CARD_INSERTION (1<<6)
+#define SDHC_BUFFER_READ_READY (1<<5)
+#define SDHC_BUFFER_WRITE_READY (1<<4)
+#define SDHC_DMA_INTERRUPT (1<<3)
+#define SDHC_BLOCK_GAP_EVENT (1<<2)
+#define SDHC_TRANSFER_COMPLETE (1<<1)
+#define SDHC_COMMAND_COMPLETE (1<<0)
+#define SDHC_NINTR_STATUS_MASK 0x81ff
+#define SDHC_EINTR_STATUS 0x32
+#define SDHC_AUTO_CMD12_ERROR (1<<8)
+#define SDHC_CURRENT_LIMIT_ERROR (1<<7)
+#define SDHC_DATA_END_BIT_ERROR (1<<6)
+#define SDHC_DATA_CRC_ERROR (1<<5)
+#define SDHC_DATA_TIMEOUT_ERROR (1<<4)
+#define SDHC_CMD_INDEX_ERROR (1<<3)
+#define SDHC_CMD_END_BIT_ERROR (1<<2)
+#define SDHC_CMD_CRC_ERROR (1<<1)
+#define SDHC_CMD_TIMEOUT_ERROR (1<<0)
+#define SDHC_EINTR_STATUS_MASK 0x01ff /* excluding vendor signals */
+#define SDHC_NINTR_STATUS_EN 0x34
+#define SDHC_EINTR_STATUS_EN 0x36
+#define SDHC_NINTR_SIGNAL_EN 0x38
+#define SDHC_NINTR_SIGNAL_MASK 0x01ff
+#define SDHC_EINTR_SIGNAL_EN 0x3a
+#define SDHC_EINTR_SIGNAL_MASK 0x01ff /* excluding vendor signals */
+#define SDHC_CMD12_ERROR_STATUS 0x3c
+#define SDHC_CAPABILITIES 0x40
+#define SDHC_VOLTAGE_SUPP_1_8V (1<<26)
+#define SDHC_VOLTAGE_SUPP_3_0V (1<<25)
+#define SDHC_VOLTAGE_SUPP_3_3V (1<<24)
+#define SDHC_DMA_SUPPORT (1<<22)
+#define SDHC_HIGH_SPEED_SUPP (1<<21)
+#define SDHC_MAX_BLK_LEN_512 0
+#define SDHC_MAX_BLK_LEN_1024 1
+#define SDHC_MAX_BLK_LEN_2048 2
+#define SDHC_MAX_BLK_LEN_SHIFT 16
+#define SDHC_MAX_BLK_LEN_MASK 0x3
+#define SDHC_BASE_FREQ_SHIFT 8
+#define SDHC_BASE_FREQ_MASK 0x2f
+#define SDHC_TIMEOUT_FREQ_UNIT (1<<7) /* 0=KHz, 1=MHz */
+#define SDHC_TIMEOUT_FREQ_SHIFT 0
+#define SDHC_TIMEOUT_FREQ_MASK 0x1f
+#define SDHC_MAX_CAPABILITIES 0x48
+#define SDHC_SLOT_INTR_STATUS 0xfc
+#define SDHC_HOST_CTL_VERSION 0xfe
+#define SDHC_SPEC_VERS_SHIFT 0
+#define SDHC_SPEC_VERS_MASK 0xff
+#define SDHC_VENDOR_VERS_SHIFT 8
+#define SDHC_VENDOR_VERS_MASK 0xff
+
+/* SDHC_CAPABILITIES decoding */
+#define SDHC_BASE_FREQ_KHZ(cap) \
+ ((((cap) >> SDHC_BASE_FREQ_SHIFT) & SDHC_BASE_FREQ_MASK) * 1000)
+#define SDHC_TIMEOUT_FREQ(cap) \
+ (((cap) >> SDHC_TIMEOUT_FREQ_SHIFT) & SDHC_TIMEOUT_FREQ_MASK)
+#define SDHC_TIMEOUT_FREQ_KHZ(cap) \
+ (((cap) & SDHC_TIMEOUT_FREQ_UNIT) ? \
+ SDHC_TIMEOUT_FREQ(cap) * 1000: \
+ SDHC_TIMEOUT_FREQ(cap))
+
+/* SDHC_HOST_CTL_VERSION decoding */
+#define SDHC_SPEC_VERSION(hcv) \
+ (((hcv) >> SDHC_SPEC_VERS_SHIFT) & SDHC_SPEC_VERS_MASK)
+#define SDHC_VENDOR_VERSION(hcv) \
+ (((hcv) >> SDHC_VENDOR_VERS_SHIFT) & SDHC_VENDOR_VERS_MASK)
+
+#define SDHC_PRESENT_STATE_BITS \
+ "\20\31CL\30D3L\27D2L\26D1L\25D0L\24WPS\23CD\22CSS\21CI" \
+ "\14BRE\13BWE\12RTA\11WTA\3DLA\2CID\1CIC"
+#define SDHC_NINTR_STATUS_BITS \
+ "\20\20ERROR\11CARD\10REMOVAL\7INSERTION\6READ\5WRITE" \
+ "\4DMA\3GAP\2XFER\1CMD"
+#define SDHC_EINTR_STATUS_BITS \
+ "\20\11ACMD12\10CL\7DEB\6DCRC\5DT\4CI\3CEB\2CCRC\1CT"
+#define SDHC_CAPABILITIES_BITS \
+ "\20\33Vdd1.8V\32Vdd3.0V\31Vdd3.3V\30SUSPEND\27DMA\26HIGHSPEED"
+
+#endif
diff --git a/sys/dev/sdmmc/sdhcvar.h b/sys/dev/sdmmc/sdhcvar.h
new file mode 100644
index 00000000000..c39fb0dd955
--- /dev/null
+++ b/sys/dev/sdmmc/sdhcvar.h
@@ -0,0 +1,39 @@
+/* $OpenBSD: sdhcvar.h,v 1.1 2006/05/28 17:21:14 uwe Exp $ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _SDHCVAR_H_
+#define _SDHCVAR_H_
+
+#include <machine/bus.h>
+
+struct sdhc_host;
+
+struct sdhc_softc {
+ struct device sc_dev;
+ struct sdhc_host *sc_host;
+ int sc_nhosts;
+};
+
+/* Host controller functions called by the attachment driver. */
+int sdhc_host_found(struct sdhc_softc *, bus_space_tag_t,
+ bus_space_handle_t, bus_size_t, int);
+void sdhc_power(int, void *);
+void sdhc_shutdown(void *);
+int sdhc_intr(void *);
+
+#endif
diff --git a/sys/dev/sdmmc/sdmmc.c b/sys/dev/sdmmc/sdmmc.c
new file mode 100644
index 00000000000..5caa81e624c
--- /dev/null
+++ b/sys/dev/sdmmc/sdmmc.c
@@ -0,0 +1,556 @@
+/* $OpenBSD: sdmmc.c,v 1.1 2006/05/28 17:21:14 uwe Exp $ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Host controller independent SD/MMC bus driver based on information
+ * from SanDisk SD Card Product Manual Revision 2.2 (SanDisk), SDIO
+ * Simple Specification Version 1.0 (SDIO) and the Linux "mmc" driver.
+ */
+
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+
+#include <dev/sdmmc/sdmmc_scsi.h>
+#include <dev/sdmmc/sdmmcchip.h>
+#ifdef notyet
+#include <dev/sdmmc/sdmmcdevs.h>
+#endif
+#include <dev/sdmmc/sdmmcreg.h>
+#include <dev/sdmmc/sdmmcvar.h>
+
+#undef SDMMC_DEBUG
+
+int sdmmc_match(struct device *, void *, void *);
+void sdmmc_attach(struct device *, struct device *, void *);
+
+int sdmmc_set_relative_addr(struct sdmmc_softc *, struct sdmmc_card *);
+int sdmmc_set_bus_width(struct sdmmc_softc *, struct sdmmc_card *);
+int sdmmc_enable(struct sdmmc_softc *);
+void sdmmc_disable(struct sdmmc_softc *);
+int sdmmc_decode_csd(struct sdmmc_softc *, sdmmc_response,
+ struct sdmmc_card *);
+int sdmmc_decode_cid(struct sdmmc_softc *, sdmmc_response,
+ struct sdmmc_card *);
+void sdmmc_print_cid(struct sdmmc_cid *);
+void sdmmc_identify_all(struct sdmmc_softc *);
+
+#ifdef SDMMC_DEBUG
+#define DPRINTF(s) printf s
+#else
+#define DPRINTF(s) /**/
+#endif
+
+struct cfattach sdmmc_ca = {
+ sizeof(struct sdmmc_softc), sdmmc_match, sdmmc_attach
+};
+
+struct cfdriver sdmmc_cd = {
+ NULL, "sdmmc", DV_DULL
+};
+
+int
+sdmmc_match(struct device *parent, void *match, void *aux)
+{
+ struct cfdata *cf = match;
+ struct sdmmcbus_attach_args *saa = aux;
+
+ return strcmp(saa->saa_busname, cf->cf_driver->cd_name) == 0;
+}
+
+void
+sdmmc_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct sdmmc_softc *sc = (struct sdmmc_softc *)self;
+ struct sdmmcbus_attach_args *saa = aux;
+
+ printf("\n");
+
+ sc->sct = saa->sct;
+ sc->sch = saa->sch;
+
+ SIMPLEQ_INIT(&sc->cs_head);
+}
+
+/*
+ * Called from the host driver when a card, or a stack of cards are
+ * inserted. Return zero if any card drivers have been attached.
+ */
+int
+sdmmc_card_attach(struct device *dev)
+{
+ struct sdmmc_softc *sc = (struct sdmmc_softc *)dev;
+ struct sdmmc_card *cs;
+
+ DPRINTF(("%s: attach card\n", SDMMCDEVNAME(sc)));
+
+ /* Power up the card (or card stack). */
+ if (sdmmc_enable(sc) != 0) {
+ printf("%s: can't enable card\n", SDMMCDEVNAME(sc));
+ return 1;
+ }
+
+ /* Scan for cards and allocate a card structure for each. */
+ sdmmc_identify_all(sc);
+
+ /* There should be at least one card now; otherwise, bail out. */
+ if (SIMPLEQ_EMPTY(&sc->cs_head)) {
+ printf("%s: can't identify card\n", SDMMCDEVNAME(sc));
+ sdmmc_disable(sc);
+ return 1;
+ }
+
+ /* Initialize all identified cards. */
+ SIMPLEQ_FOREACH(cs, &sc->cs_head, cs_list) {
+ /* Boost the bus width. */
+ (void)sdmmc_set_bus_width(sc, cs); /* XXX */
+
+ if (ISSET(sc->sc_flags, SMF_MEM_MODE) &&
+ sdmmc_mem_init(sc, cs) != 0)
+ printf("%s: init failed\n", SDMMCDEVNAME(sc));
+ }
+
+ /* XXX attach SDIO driver(s) */
+
+ /* Attach SCSI emulation for memory cards. */
+ if (ISSET(sc->sc_flags, SMF_MEM_MODE))
+ sdmmc_scsi_attach(sc);
+ return 0;
+}
+
+/*
+ * Called from host driver with DETACH_* flags from <sys/device.h>
+ * when cards are gone.
+ */
+void
+sdmmc_card_detach(struct device *dev, int flags)
+{
+ struct sdmmc_softc *sc = (struct sdmmc_softc *)dev;
+ struct sdmmc_card *cs, *csnext;
+
+ DPRINTF(("%s: detach card\n", SDMMCDEVNAME(sc)));
+
+ /* Power down. */
+ sdmmc_disable(sc);
+
+ /* Detach SCSI emulation. */
+ if (ISSET(sc->sc_flags, SMF_MEM_MODE))
+ sdmmc_scsi_detach(sc);
+
+ /* XXX detach SDIO driver(s) */
+
+ /* Free all card structures. */
+ for (cs = SIMPLEQ_FIRST(&sc->cs_head); cs != NULL; cs = csnext) {
+ csnext = SIMPLEQ_NEXT(cs, cs_list);
+ FREE(cs, M_DEVBUF);
+ }
+ SIMPLEQ_INIT(&sc->cs_head);
+}
+
+int
+sdmmc_enable(struct sdmmc_softc *sc)
+{
+ u_int32_t host_ocr;
+ int error;
+
+ /*
+ * Calculate the equivalent of the card OCR from the host
+ * capabilities and select the maximum supported bus voltage.
+ */
+ host_ocr = sdmmc_chip_host_ocr(sc->sct, sc->sch);
+ error = sdmmc_chip_bus_power(sc->sct, sc->sch, host_ocr);
+ if (error != 0) {
+ printf("%s: can't supply bus power\n", SDMMCDEVNAME(sc));
+ goto err;
+ }
+
+ /*
+ * Select the minimum clock frequency.
+ */
+ error = sdmmc_chip_bus_clock(sc->sct, sc->sch, SDMMC_SDCLK_400KHZ);
+ if (error != 0) {
+ printf("%s: can't supply clock\n", SDMMCDEVNAME(sc));
+ goto err;
+ }
+
+ /* Initialize SD I/O card function(s). */
+ if ((error = sdmmc_io_enable(sc)) != 0)
+ goto err;
+
+ /* Initialize SD/MMC memory card(s). */
+ if ((error = sdmmc_mem_enable(sc)) != 0)
+ goto err;
+
+ /* XXX */
+ if (ISSET(sc->sc_flags, SMF_SD_MODE))
+ (void)sdmmc_chip_bus_clock(sc->sct, sc->sch,
+ SDMMC_SDCLK_25MHZ);
+
+ err:
+ if (error != 0)
+ sdmmc_disable(sc);
+ return error;
+}
+
+void
+sdmmc_disable(struct sdmmc_softc *sc)
+{
+ /* XXX complete commands if card is still present. */
+
+ /* Deselect all cards. */
+ (void)sdmmc_select_card(sc, NULL);
+
+ /* Turn off bus power and clock. */
+ (void)sdmmc_chip_bus_clock(sc->sct, sc->sch, SDMMC_SDCLK_OFF);
+ (void)sdmmc_chip_bus_power(sc->sct, sc->sch, 0);
+}
+
+void
+sdmmc_delay(u_int usecs)
+{
+ int ticks = usecs / (1000000 / hz);
+
+ if (ticks > 0)
+ (void)tsleep(&sdmmc_delay, PWAIT, "sdwait", ticks);
+ else
+ delay(usecs);
+}
+
+/*
+ * Set the lowest bus voltage supported by the card and the host.
+ */
+int
+sdmmc_set_bus_power(struct sdmmc_softc *sc, u_int32_t host_ocr,
+ u_int32_t card_ocr)
+{
+ u_int32_t bit;
+
+ /* Mask off unsupported voltage levels and select the lowest. */
+ DPRINTF(("%s: host_ocr=%x ", SDMMCDEVNAME(sc), host_ocr));
+ host_ocr &= card_ocr;
+ for (bit = 4; bit < 23; bit++) {
+ if (ISSET(host_ocr, 1<<bit)) {
+ host_ocr &= 3<<bit;
+ break;
+ }
+ }
+ DPRINTF(("card_ocr=%x new_ocr=%x\n", card_ocr, host_ocr));
+
+ if (host_ocr == 0 ||
+ sdmmc_chip_bus_power(sc->sct, sc->sch, host_ocr) != 0)
+ return 1;
+ return 0;
+}
+
+/*
+ * Read the CSD and CID from all cards and assign each card a unique
+ * relative card address (RCA).
+ */
+void
+sdmmc_identify_all(struct sdmmc_softc *sc)
+{
+ struct sdmmc_command cmd;
+ struct sdmmc_card *cs;
+ u_int16_t next_rca;
+ int error;
+ int i;
+
+ /*
+ * CMD2 is a broadcast command understood by SD cards and MMC
+ * cards. All cards begin to respond to the command, but back
+ * off if another card drives the CMD line to a different level.
+ * Only one card will get its entire response through. That
+ * card remains silent once it has been assigned a RCA.
+ */
+ for (i = 0; i < 100; i++) {
+ bzero(&cmd, sizeof cmd);
+ cmd.c_opcode = MMC_ALL_SEND_CID;
+ cmd.c_flags = SCF_CMD_BCR | SCF_RSP_R2;
+
+ error = sdmmc_mmc_command(sc, &cmd);
+ if (error == ETIMEDOUT) {
+ /* No more cards there. */
+ break;
+ } else if (error != 0) {
+ DPRINTF(("%s: can't read CID\n", SDMMCDEVNAME(sc)));
+ break;
+ }
+
+ /* In MMC mode, find the next available RCA. */
+ next_rca = 0;
+ if (!ISSET(sc->sc_flags, SMF_SD_MODE))
+ SIMPLEQ_FOREACH(cs, &sc->cs_head, cs_list)
+ next_rca++;
+
+ /* Allocate a card structure. */
+ MALLOC(cs, struct sdmmc_card *, sizeof *cs, M_DEVBUF,
+ M_WAITOK);
+ bzero(cs, sizeof *cs);
+ cs->rca = next_rca;
+
+ /*
+ * Remember the CID returned in the CMD2 response for
+ * later decoding.
+ */
+ bcopy(cmd.c_resp, cs->raw_cid, sizeof cs->raw_cid);
+
+ /*
+ * Silence the card by assigning it a unique RCA, or
+ * querying it for its RCA in case of SD.
+ */
+ if (sdmmc_set_relative_addr(sc, cs) != 0) {
+ printf("%s: can't set RCA\n", SDMMCDEVNAME(sc));
+ FREE(cs, M_DEVBUF);
+ break;
+ }
+
+ SIMPLEQ_INSERT_TAIL(&sc->cs_head, cs, cs_list);
+ }
+
+ /*
+ * All cards are either inactive or awaiting further commands.
+ * Read the CSDs and decode the raw CID for each card.
+ */
+ SIMPLEQ_FOREACH(cs, &sc->cs_head, cs_list) {
+ bzero(&cmd, sizeof cmd);
+ cmd.c_opcode = MMC_SEND_CSD;
+ cmd.c_arg = MMC_ARG_RCA(cs->rca);
+ cmd.c_flags = SCF_CMD_AC | SCF_RSP_R2;
+
+ if (sdmmc_mmc_command(sc, &cmd) != 0) {
+ SET(cs->flags, SDMMCF_CARD_ERROR);
+ continue;
+ }
+
+ if (sdmmc_decode_csd(sc, cmd.c_resp, cs) != 0 ||
+ sdmmc_decode_cid(sc, cs->raw_cid, cs) != 0) {
+ SET(cs->flags, SDMMCF_CARD_ERROR);
+ continue;
+ }
+
+#ifdef SDMMC_DEBUG
+ printf("%s: CID: ", SDMMCDEVNAME(sc));
+ sdmmc_print_cid(&cs->cid);
+#endif
+ }
+}
+
+int
+sdmmc_app_command(struct sdmmc_softc *sc, struct sdmmc_command *cmd)
+{
+ struct sdmmc_command acmd;
+ int error;
+
+ bzero(&acmd, sizeof acmd);
+ acmd.c_opcode = MMC_APP_CMD;
+ acmd.c_arg = 0;
+ acmd.c_flags = SCF_CMD_AC | SCF_RSP_R1;
+
+ error = sdmmc_mmc_command(sc, &acmd);
+ if (error != 0)
+ return error;
+
+ if (!ISSET(MMC_R1(acmd.c_resp), MMC_R1_APP_CMD))
+ /* Card does not support application commands. */
+ return ENODEV;
+
+ return sdmmc_mmc_command(sc, cmd);
+}
+
+int
+sdmmc_mmc_command(struct sdmmc_softc *sc, struct sdmmc_command *cmd)
+{
+ return sdmmc_chip_exec_command(sc->sct, sc->sch, cmd);
+}
+
+/*
+ * Send the "GO IDLE STATE" command.
+ */
+void
+sdmmc_go_idle_state(struct sdmmc_softc *sc)
+{
+ struct sdmmc_command cmd;
+
+ bzero(&cmd, sizeof cmd);
+ cmd.c_opcode = MMC_GO_IDLE_STATE;
+ cmd.c_flags = SCF_CMD_BC | SCF_RSP_R0;
+
+ (void)sdmmc_mmc_command(sc, &cmd);
+}
+
+/*
+ * Retrieve (SD) or set (MMC) the relative card address (RCA).
+ */
+int
+sdmmc_set_relative_addr(struct sdmmc_softc *sc, struct sdmmc_card *cs)
+{
+ struct sdmmc_command cmd;
+
+ bzero(&cmd, sizeof cmd);
+
+ if (ISSET(sc->sc_flags, SMF_SD_MODE)) {
+ cmd.c_opcode = SD_SEND_RELATIVE_ADDR;
+ cmd.c_flags = SCF_CMD_BCR | SCF_RSP_R6;
+ } else {
+ cmd.c_opcode = MMC_SET_RELATIVE_ADDR;
+ cmd.c_arg = MMC_ARG_RCA(cs->rca);
+ cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1;
+ }
+
+ if (sdmmc_mmc_command(sc, &cmd) != 0)
+ return 1;
+
+ if (ISSET(sc->sc_flags, SMF_SD_MODE))
+ cs->rca = SD_R6_RCA(cmd.c_resp);
+ return 0;
+}
+
+/*
+ * Set the maximum supported bus width.
+ */
+int
+sdmmc_set_bus_width(struct sdmmc_softc *sc, struct sdmmc_card *cs)
+{
+ struct sdmmc_command cmd;
+ int error;
+
+ if (!ISSET(sc->sc_flags, SMF_SD_MODE))
+ return EOPNOTSUPP;
+
+ if ((error = sdmmc_select_card(sc, cs)) != 0)
+ return error;
+
+ bzero(&cmd, sizeof cmd);
+ cmd.c_opcode = SD_APP_SET_BUS_WIDTH;
+ cmd.c_arg = SD_ARG_BUS_WIDTH_4;
+ cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1;
+
+ return sdmmc_app_command(sc, &cmd);
+}
+
+int
+sdmmc_select_card(struct sdmmc_softc *sc, struct sdmmc_card *cs)
+{
+ struct sdmmc_command cmd;
+ int error;
+
+ if (sc->sc_card == cs)
+ return 0;
+
+ bzero(&cmd, sizeof cmd);
+ cmd.c_opcode = MMC_SELECT_CARD;
+ cmd.c_arg = cs == NULL ? 0 : MMC_ARG_RCA(cs->rca);
+ cmd.c_flags = SCF_CMD_AC | (cs == NULL ? SCF_RSP_R0 : SCF_RSP_R1);
+ error = sdmmc_mmc_command(sc, &cmd);
+ if (error == 0 || cs == NULL)
+ sc->sc_card = cs;
+ return error;
+}
+
+int
+sdmmc_decode_csd(struct sdmmc_softc *sc, sdmmc_response resp,
+ struct sdmmc_card *cs)
+{
+ struct sdmmc_csd *csd = &cs->csd;
+
+ if (ISSET(sc->sc_flags, SMF_SD_MODE)) {
+ /*
+ * CSD version 1.0 corresponds to SD system
+ * specification version 1.0 - 1.10. (SanDisk, 3.5.3)
+ */
+ csd->csdver = SD_CSD_CSDVER(resp);
+ if (csd->csdver != SD_CSD_CSDVER_1_0) {
+ printf("%s: unknown SD CSD structure version 0x%x\n",
+ SDMMCDEVNAME(sc), csd->csdver);
+ return 1;
+ }
+
+ csd->capacity = SD_CSD_CAPACITY(resp);
+ csd->read_bl_len = SD_CSD_READ_BL_LEN(resp);
+ } else {
+ csd->csdver = MMC_CSD_CSDVER(resp);
+ if (csd->csdver != MMC_CSD_CSDVER_1_0 &&
+ csd->csdver != MMC_CSD_CSDVER_2_0) {
+ printf("%s: unknown MMC CSD structure version 0x%x\n",
+ SDMMCDEVNAME(sc), csd->csdver);
+ return 1;
+ }
+
+ csd->mmcver = MMC_CSD_MMCVER(resp);
+ csd->capacity = MMC_CSD_CAPACITY(resp);
+ csd->read_bl_len = MMC_CSD_READ_BL_LEN(resp);
+ csd->sector_size = 1 << csd->read_bl_len;
+ }
+ csd->sector_size = MIN(1 << csd->read_bl_len,
+ sdmmc_chip_host_maxblklen(sc->sct, sc->sch));
+ return 0;
+}
+
+int
+sdmmc_decode_cid(struct sdmmc_softc *sc, sdmmc_response resp,
+ struct sdmmc_card *cs)
+{
+ struct sdmmc_cid *cid = &cs->cid;
+
+ if (ISSET(sc->sc_flags, SMF_SD_MODE)) {
+ cid->mid = SD_CID_MID(resp);
+ cid->oid = SD_CID_OID(resp);
+ SD_CID_PNM_CPY(resp, cid->pnm);
+ cid->rev = SD_CID_REV(resp);
+ cid->psn = SD_CID_PSN(resp);
+ cid->mdt = SD_CID_MDT(resp);
+ } else {
+ switch(cs->csd.mmcver) {
+ case MMC_CSD_MMCVER_1_0:
+ case MMC_CSD_MMCVER_1_4:
+ cid->mid = MMC_CID_MID_V1(resp);
+ MMC_CID_PNM_V1_CPY(resp, cid->pnm);
+ cid->rev = MMC_CID_REV_V1(resp);
+ cid->psn = MMC_CID_PSN_V1(resp);
+ cid->mdt = MMC_CID_MDT_V1(resp);
+ break;
+ case MMC_CSD_MMCVER_2_0:
+ case MMC_CSD_MMCVER_3_1:
+ case MMC_CSD_MMCVER_4_0:
+ cid->mid = MMC_CID_MID_V2(resp);
+ cid->oid = MMC_CID_OID_V2(resp);
+ MMC_CID_PNM_V2_CPY(resp, cid->pnm);
+ cid->psn = MMC_CID_PSN_V2(resp);
+ break;
+ default:
+ printf("%s: unknown MMC version %d\n",
+ SDMMCDEVNAME(sc), cs->csd.mmcver);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void
+sdmmc_print_cid(struct sdmmc_cid *cid)
+{
+ printf("mid=0x%02x oid=0x%04x pnm=\"%s\" rev=0x%02x psn=0x%08x"
+ " mdt=%03x\n", cid->mid, cid->oid, cid->pnm, cid->rev, cid->psn,
+ cid->mdt);
+}
diff --git a/sys/dev/sdmmc/sdmmc_io.c b/sys/dev/sdmmc/sdmmc_io.c
new file mode 100644
index 00000000000..3adad9bfdaf
--- /dev/null
+++ b/sys/dev/sdmmc/sdmmc_io.c
@@ -0,0 +1,140 @@
+/* $OpenBSD: sdmmc_io.c,v 1.1 2006/05/28 17:21:14 uwe Exp $ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Routines for SD I/O cards. */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <dev/sdmmc/sdmmcchip.h>
+#include <dev/sdmmc/sdmmcreg.h>
+#include <dev/sdmmc/sdmmcvar.h>
+
+#undef SDMMC_DEBUG
+
+#ifdef SDMMC_DEBUG
+#define DPRINTF(s) printf s
+#else
+#define DPRINTF(s) /**/
+#endif
+
+/*
+ * Initialize SD I/O card functions (before memory cards). The host
+ * system and controller must support card interrupts in order to use
+ * I/O functions.
+ */
+int
+sdmmc_io_enable(struct sdmmc_softc *sc)
+{
+ u_int32_t host_ocr;
+ u_int32_t card_ocr;
+
+ /* Set host mode to SD "combo" card. */
+ SET(sc->sc_flags, SMF_SD_MODE|SMF_IO_MODE|SMF_MEM_MODE);
+
+ /* Reset I/O functions (*must* do that before CMD5). */
+ sdmmc_io_reset(sc);
+
+ /*
+ * Read the I/O OCR value, determine the number of I/O
+ * functions and whether memory is also present (a "combo
+ * card") by issuing CMD5. SD memory-only and MMC cards
+ * do not respond to CMD5.
+ */
+ if (sdmmc_io_send_op_cond(sc, 0, &card_ocr) != 0) {
+ /* No SDIO card; switch to SD memory-only mode. */
+ CLR(sc->sc_flags, SMF_IO_MODE);
+ return 0;
+ }
+
+ if (SD_IO_OCR_NF(card_ocr) == 0) {
+ /* No I/O functions. */
+ DPRINTF(("%s: no I/O functions\n", SDMMCDEVNAME(sc)));
+ return 0;
+ }
+
+ /* Set the lowest voltage supported by the card and host. */
+ host_ocr = sdmmc_chip_host_ocr(sc->sct, sc->sch);
+ if (sdmmc_set_bus_power(sc, host_ocr, card_ocr) != 0) {
+ printf("%s: can't supply voltage requested by card\n",
+ SDMMCDEVNAME(sc));
+ return 1;
+ }
+
+ /* Reset I/O functions (again). */
+ sdmmc_io_reset(sc);
+
+ /* Send the new OCR value until all cards are ready. */
+ if (sdmmc_io_send_op_cond(sc, host_ocr, NULL) != 0) {
+ printf("%s: can't send I/O OCR\n", SDMMCDEVNAME(sc));
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Send the "I/O RESET" command.
+ */
+void
+sdmmc_io_reset(struct sdmmc_softc *sc)
+{
+ struct sdmmc_command cmd;
+
+ bzero(&cmd, sizeof cmd);
+ cmd.c_opcode = SD_IO_RESET;
+ cmd.c_flags = SCF_CMD_BC | SCF_RSP_R0;
+
+ (void)sdmmc_mmc_command(sc, &cmd);
+}
+
+/*
+ * Get or set the card's I/O OCR value (SDIO).
+ */
+int
+sdmmc_io_send_op_cond(struct sdmmc_softc *sc, u_int32_t ocr, u_int32_t *ocrp)
+{
+ struct sdmmc_command cmd;
+ int error;
+ int i;
+
+ /*
+ * If we change the OCR value, retry the command until the OCR
+ * we receive in response has the "CARD BUSY" bit set, meaning
+ * that all cards are ready for card identification.
+ */
+ for (i = 0; i < 100; i++) {
+ bzero(&cmd, sizeof cmd);
+ cmd.c_opcode = SD_IO_SEND_OP_COND;
+ cmd.c_arg = ocr;
+ cmd.c_flags = SCF_CMD_BCR | SCF_RSP_R4;
+
+ error = sdmmc_mmc_command(sc, &cmd);
+ if (error != 0)
+ break;
+ if (ISSET(MMC_R4(cmd.c_resp), SD_IO_OCR_MEM_READY) ||
+ ocr == 0)
+ break;
+ error = ETIMEDOUT;
+ sdmmc_delay(10000);
+ }
+ if (error == 0)
+ printf("ocr: %x\n", MMC_R4(cmd.c_resp));
+ if (error == 0 && ocrp != NULL)
+ *ocrp = MMC_R4(cmd.c_resp);
+ return error;
+}
diff --git a/sys/dev/sdmmc/sdmmc_mem.c b/sys/dev/sdmmc/sdmmc_mem.c
new file mode 100644
index 00000000000..c10e6018566
--- /dev/null
+++ b/sys/dev/sdmmc/sdmmc_mem.c
@@ -0,0 +1,243 @@
+/* $OpenBSD: sdmmc_mem.c,v 1.1 2006/05/28 17:21:14 uwe Exp $ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Routines for SD/MMC memory cards. */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <dev/sdmmc/sdmmcchip.h>
+#include <dev/sdmmc/sdmmcreg.h>
+#include <dev/sdmmc/sdmmcvar.h>
+
+#undef SDMMC_DEBUG
+
+#ifdef SDMMC_DEBUG
+#define DPRINTF(s) printf s
+#else
+#define DPRINTF(s) /**/
+#endif
+
+int sdmmc_mem_send_op_cond(struct sdmmc_softc *, u_int32_t, u_int32_t *);
+int sdmmc_mem_set_blocklen(struct sdmmc_softc *, struct sdmmc_card *);
+
+/*
+ * Initialize SD/MMC memory cards and memory in SDIO "combo" cards.
+ */
+int
+sdmmc_mem_enable(struct sdmmc_softc *sc)
+{
+ u_int32_t host_ocr;
+ u_int32_t card_ocr;
+
+ /* Set host mode to SD "combo" card or SD memory-only. */
+ SET(sc->sc_flags, SMF_SD_MODE|SMF_MEM_MODE);
+
+ /* Reset memory (*must* do that before CMD55 or CMD1). */
+ sdmmc_go_idle_state(sc);
+
+ /*
+ * Read the SD/MMC memory OCR value by issuing CMD55 followed
+ * by ACMD41 to read the OCR value from memory-only SD cards.
+ * MMC cards will not respond to CMD55 or ACMD41 and this is
+ * how we distinguish them from SD cards.
+ */
+ mmc_mode:
+ if (sdmmc_mem_send_op_cond(sc, 0, &card_ocr) != 0) {
+ DPRINTF(("flags %x\n", sc->sc_flags));
+ if (ISSET(sc->sc_flags, SMF_SD_MODE) &&
+ !ISSET(sc->sc_flags, SMF_IO_MODE)) {
+ /* Not a SD card, switch to MMC mode. */
+ CLR(sc->sc_flags, SMF_SD_MODE);
+ goto mmc_mode;
+ }
+ if (!ISSET(sc->sc_flags, SMF_SD_MODE)) {
+ printf("%s: can't read memory OCR\n",
+ SDMMCDEVNAME(sc));
+ return 1;
+ } else {
+ /* Not a "combo" card. */
+ CLR(sc->sc_flags, SMF_MEM_MODE);
+ return 0;
+ }
+ }
+
+ /* Set the lowest voltage supported by the card and host. */
+ host_ocr = sdmmc_chip_host_ocr(sc->sct, sc->sch);
+ if (sdmmc_set_bus_power(sc, host_ocr, card_ocr) != 0) {
+ printf("%s: can't supply voltage requested by card\n",
+ SDMMCDEVNAME(sc));
+ return 1;
+ }
+
+ /* Tell the card(s) to enter the idle state (again). */
+ sdmmc_go_idle_state(sc);
+
+ /* Send the new OCR value until all cards are ready. */
+ if (sdmmc_mem_send_op_cond(sc, host_ocr, NULL) != 0) {
+ printf("%s: can't send memory OCR\n", SDMMCDEVNAME(sc));
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Initialize a SD/MMC memory card.
+ */
+int
+sdmmc_mem_init(struct sdmmc_softc *sc, struct sdmmc_card *cs)
+{
+ if (sdmmc_select_card(sc, cs) != 0 ||
+ sdmmc_mem_set_blocklen(sc, cs) != 0)
+ return 1;
+ return 0;
+}
+
+/*
+ * Get or set the card's memory OCR value (SD or MMC).
+ */
+int
+sdmmc_mem_send_op_cond(struct sdmmc_softc *sc, u_int32_t ocr,
+ u_int32_t *ocrp)
+{
+ struct sdmmc_command cmd;
+ int error;
+ int i;
+
+ /*
+ * If we change the OCR value, retry the command until the OCR
+ * we receive in response has the "CARD BUSY" bit set, meaning
+ * that all cards are ready for card identification.
+ */
+ for (i = 0; i < 100; i++) {
+ bzero(&cmd, sizeof cmd);
+ cmd.c_arg = ocr;
+ cmd.c_flags = SCF_CMD_BCR | SCF_RSP_R3;
+
+ if (ISSET(sc->sc_flags, SMF_SD_MODE)) {
+ cmd.c_opcode = SD_APP_OP_COND;
+ error = sdmmc_app_command(sc, &cmd);
+ } else {
+ cmd.c_opcode = MMC_SEND_OP_COND;
+ error = sdmmc_mmc_command(sc, &cmd);
+ }
+ if (error != 0)
+ break;
+ if (ISSET(MMC_R3(cmd.c_resp), MMC_OCR_MEM_READY) ||
+ ocr == 0)
+ break;
+ error = ETIMEDOUT;
+ sdmmc_delay(10000);
+ }
+ if (error == 0 && ocrp != NULL)
+ *ocrp = MMC_R3(cmd.c_resp);
+ return error;
+}
+
+/*
+ * Set the read block length appropriately for this card, according to
+ * the card CSD register value.
+ */
+int
+sdmmc_mem_set_blocklen(struct sdmmc_softc *sc, struct sdmmc_card *cs)
+{
+ struct sdmmc_command cmd;
+
+ bzero(&cmd, sizeof cmd);
+ cmd.c_opcode = MMC_SET_BLOCKLEN;
+ cmd.c_arg = cs->csd.sector_size;
+ cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1;
+ DPRINTF(("%s: read_bl_len=%d sector_size=%d\n", SDMMCDEVNAME(sc),
+ 1 << cs->csd.read_bl_len, cs->csd.sector_size));
+
+ return sdmmc_mmc_command(sc, &cmd);
+}
+
+int
+sdmmc_mem_read_block(struct sdmmc_softc *sc, struct sdmmc_card *cs,
+ int blkno, u_char *data, size_t datalen)
+{
+ struct sdmmc_command cmd;
+ int error;
+
+ if ((error = sdmmc_select_card(sc, cs)) != 0)
+ return error;
+
+ bzero(&cmd, sizeof cmd);
+ cmd.c_data = data;
+ cmd.c_datalen = datalen;
+ cmd.c_blklen = cs->csd.sector_size;
+ cmd.c_opcode = (datalen / cmd.c_blklen) > 1 ?
+ MMC_READ_BLOCK_MULTIPLE : MMC_READ_BLOCK_SINGLE;
+ cmd.c_arg = blkno << 9;
+ cmd.c_flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1;
+
+ error = sdmmc_mmc_command(sc, &cmd);
+ if (error != 0)
+ return error;
+
+ do {
+ bzero(&cmd, sizeof cmd);
+ cmd.c_opcode = MMC_SEND_STATUS;
+ cmd.c_arg = MMC_ARG_RCA(cs->rca);
+ cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1;
+ error = sdmmc_mmc_command(sc, &cmd);
+ if (error != 0)
+ break;
+ /* XXX time out */
+ } while (!ISSET(MMC_R1(cmd.c_resp), MMC_R1_READY_FOR_DATA));
+
+ return error;
+}
+
+int
+sdmmc_mem_write_block(struct sdmmc_softc *sc, struct sdmmc_card *cs,
+ int blkno, u_char *data, size_t datalen)
+{
+ struct sdmmc_command cmd;
+ int error;
+
+ if ((error = sdmmc_select_card(sc, cs)) != 0)
+ return error;
+
+ bzero(&cmd, sizeof cmd);
+ cmd.c_data = data;
+ cmd.c_datalen = datalen;
+ cmd.c_blklen = cs->csd.sector_size;
+ cmd.c_opcode = (datalen / cmd.c_blklen) > 1 ?
+ MMC_WRITE_BLOCK_MULTIPLE : MMC_WRITE_BLOCK_SINGLE;
+ cmd.c_arg = blkno << 9;
+ cmd.c_flags = SCF_CMD_ADTC | SCF_RSP_R1;
+
+ error = sdmmc_mmc_command(sc, &cmd);
+ if (error != 0)
+ return error;
+
+ do {
+ bzero(&cmd, sizeof cmd);
+ cmd.c_opcode = MMC_SEND_STATUS;
+ cmd.c_arg = MMC_ARG_RCA(cs->rca);
+ cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1;
+ error = sdmmc_mmc_command(sc, &cmd);
+ if (error != 0)
+ break;
+ /* XXX time out */
+ } while (!ISSET(MMC_R1(cmd.c_resp), MMC_R1_READY_FOR_DATA));
+
+ return error;
+}
diff --git a/sys/dev/sdmmc/sdmmc_scsi.c b/sys/dev/sdmmc/sdmmc_scsi.c
new file mode 100644
index 00000000000..e32f27bb52b
--- /dev/null
+++ b/sys/dev/sdmmc/sdmmc_scsi.c
@@ -0,0 +1,238 @@
+/* $OpenBSD: sdmmc_scsi.c,v 1.1 2006/05/28 17:21:14 uwe Exp $ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* A SCSI bus emulation to access SD/MMC memory cards. */
+
+#include <sys/param.h>
+#include <sys/buf.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_disk.h>
+#include <scsi/scsiconf.h>
+
+#include <dev/sdmmc/sdmmc_scsi.h>
+#include <dev/sdmmc/sdmmcvar.h>
+
+#undef SDMMC_DEBUG
+
+#define SDMMC_SCSIID_HOST 0x00
+#define SDMMC_SCSIID_MAX 0x0f
+
+int sdmmc_scsi_cmd(struct scsi_xfer *);
+int sdmmc_start_xs(struct sdmmc_softc *, struct scsi_xfer *);
+int sdmmc_done_xs(struct sdmmc_softc *, struct scsi_xfer *);
+void sdmmc_complete(struct sdmmc_softc *, struct scsi_xfer *);
+void sdmmc_scsi_minphys(struct buf *);
+
+#ifdef SDMMC_DEBUG
+#define DPRINTF(s) printf s
+#else
+#define DPRINTF(s) /**/
+#endif
+
+void
+sdmmc_scsi_attach(struct sdmmc_softc *sc)
+{
+ struct sdmmc_scsi_softc *scbus;
+ struct sdmmc_card *cs;
+
+ MALLOC(scbus, struct sdmmc_scsi_softc *,
+ sizeof *scbus, M_DEVBUF, M_WAITOK);
+ bzero(scbus, sizeof *scbus);
+
+ MALLOC(scbus->sc_tgt, struct sdmmc_scsi_target *,
+ sizeof(*scbus->sc_tgt) * (SDMMC_SCSIID_MAX+1),
+ M_DEVBUF, M_WAITOK);
+ bzero(scbus->sc_tgt, sizeof(*scbus->sc_tgt) * (SDMMC_SCSIID_MAX+1));
+
+ /*
+ * Each card that sent us a CID in the identification stage
+ * gets a SCSI ID > 0, whether it is a memory card or not.
+ */
+ scbus->sc_ntargets = 1;
+ SIMPLEQ_FOREACH(cs, &sc->cs_head, cs_list) {
+ if (scbus->sc_ntargets >= SDMMC_SCSIID_MAX+1)
+ break;
+ scbus->sc_tgt[scbus->sc_ntargets].card = cs;
+ scbus->sc_ntargets++;
+ }
+
+ sc->sc_scsibus = scbus;
+
+ scbus->sc_adapter.scsi_cmd = sdmmc_scsi_cmd;
+ scbus->sc_adapter.scsi_minphys = sdmmc_scsi_minphys;
+
+ scbus->sc_link.adapter_target = SDMMC_SCSIID_HOST;
+ scbus->sc_link.adapter_buswidth = scbus->sc_ntargets;
+ scbus->sc_link.adapter_softc = sc;
+ scbus->sc_link.luns = 1;
+ scbus->sc_link.openings = 1;
+ scbus->sc_link.adapter = &scbus->sc_adapter;
+
+ scbus->sc_child = config_found(&sc->sc_dev, &scbus->sc_link,
+ scsiprint);
+}
+
+void
+sdmmc_scsi_detach(struct sdmmc_softc *sc)
+{
+ struct sdmmc_scsi_softc *scbus;
+
+ scbus = sc->sc_scsibus;
+
+ if (scbus->sc_child != NULL)
+ config_detach(scbus->sc_child, DETACH_FORCE);
+
+ if (scbus->sc_tgt != NULL)
+ FREE(scbus->sc_tgt, M_DEVBUF);
+
+ FREE(scbus, M_DEVBUF);
+ sc->sc_scsibus = NULL;
+}
+
+int
+sdmmc_scsi_cmd(struct scsi_xfer *xs)
+{
+ struct scsi_link *link = xs->sc_link;
+ struct sdmmc_softc *sc = link->adapter_softc;
+ struct sdmmc_scsi_softc *scbus = sc->sc_scsibus;
+ struct sdmmc_scsi_target *tgt = &scbus->sc_tgt[link->target];
+ struct scsi_inquiry_data inq;
+ struct scsi_read_cap_data rcd;
+
+ if (link->target >= scbus->sc_ntargets || tgt->card == NULL ||
+ link->lun != 0) {
+ DPRINTF(("%s: sdmmc_scsi_cmd: no target %d\n",
+ SDMMCDEVNAME(sc), link->target));
+ /* XXX should be XS_SENSE and sense filled out */
+ xs->error = XS_DRIVER_STUFFUP;
+ xs->flags |= ITSDONE;
+ scsi_done(xs);
+ return COMPLETE;
+ }
+
+ DPRINTF(("%s: sdmmc_scsi_cmd: target=%d xs=%p cmd=%#x "
+ "datalen=%d (poll=%d)\n", SDMMCDEVNAME(sc), link->target,
+ xs, xs->cmd->opcode, xs->datalen, xs->flags & SCSI_POLL));
+
+ xs->error = XS_NOERROR;
+
+ switch (xs->cmd->opcode) {
+ case READ_COMMAND:
+ case READ_BIG:
+ case WRITE_COMMAND:
+ case WRITE_BIG:
+ /* Deal with I/O outside the switch. */
+ break;
+
+ case INQUIRY:
+ bzero(&inq, sizeof inq);
+ inq.device = T_DIRECT;
+ inq.version = 2;
+ inq.response_format = 2;
+ inq.additional_length = 32;
+ strlcpy(inq.vendor, "SD/MMC ", sizeof(inq.vendor));
+ snprintf(inq.product, sizeof(inq.product),
+ "Drive #%02d", link->target);
+ strlcpy(inq.revision, " ", sizeof(inq.revision));
+ bcopy(&inq, xs->data, MIN(xs->datalen, sizeof inq));
+ scsi_done(xs);
+ return COMPLETE;
+
+ case TEST_UNIT_READY:
+ case START_STOP:
+ case SYNCHRONIZE_CACHE:
+ return COMPLETE;
+
+ case READ_CAPACITY:
+ bzero(&rcd, sizeof rcd);
+ _lto4b(tgt->card->csd.capacity - 1, rcd.addr);
+ _lto4b(tgt->card->csd.sector_size, rcd.length);
+ bcopy(&rcd, xs->data, MIN(xs->datalen, sizeof rcd));
+ scsi_done(xs);
+ return COMPLETE;
+
+
+ default:
+ DPRINTF(("%s: unsupported scsi command %#x\n",
+ SDMMCDEVNAME(sc), xs->cmd->opcode));
+ xs->error = XS_DRIVER_STUFFUP;
+ scsi_done(xs);
+ return COMPLETE;
+ }
+
+ /* XXX check bounds */
+
+ return sdmmc_start_xs(sc, xs);
+}
+
+int
+sdmmc_start_xs(struct sdmmc_softc *sc, struct scsi_xfer *xs)
+{
+ sdmmc_complete(sc, xs);
+ return COMPLETE;
+}
+
+void
+sdmmc_complete(struct sdmmc_softc *sc, struct scsi_xfer *xs)
+{
+ struct scsi_link *link = xs->sc_link;
+ struct sdmmc_scsi_softc *scbus = sc->sc_scsibus;
+ struct sdmmc_scsi_target *tgt = &scbus->sc_tgt[link->target];
+ struct scsi_rw *rw;
+ struct scsi_rw_big *rwb;
+ u_int32_t blockno;
+ u_int32_t blockcnt;
+ int error;
+
+ /* A read or write operation. */
+ /* XXX move to some sort of "scsi emulation layer". */
+ if (xs->cmdlen == 6) {
+ rw = (struct scsi_rw *)xs->cmd;
+ blockno = _3btol(rw->addr) & (SRW_TOPADDR << 16 | 0xffff);
+ blockcnt = rw->length ? rw->length : 0x100;
+ } else {
+ rwb = (struct scsi_rw_big *)xs->cmd;
+ blockno = _4btol(rwb->addr);
+ blockcnt = _2btol(rwb->length);
+ }
+
+ if (ISSET(xs->flags, SCSI_DATA_IN))
+ error = sdmmc_mem_read_block(sc, tgt->card, blockno,
+ xs->data, blockcnt * DEV_BSIZE);
+ else
+ error = sdmmc_mem_write_block(sc, tgt->card, blockno,
+ xs->data, blockcnt * DEV_BSIZE);
+ if (error != 0)
+ xs->error = XS_DRIVER_STUFFUP;
+
+ xs->flags |= ITSDONE;
+ xs->resid = 0;
+ scsi_done(xs);
+}
+
+void
+sdmmc_scsi_minphys(struct buf *bp)
+{
+ /* XXX limit to max. transfer size supported by card/host? */
+ if (bp->b_bcount > DEV_BSIZE)
+ bp->b_bcount = DEV_BSIZE;
+ minphys(bp);
+}
diff --git a/sys/dev/sdmmc/sdmmc_scsi.h b/sys/dev/sdmmc/sdmmc_scsi.h
new file mode 100644
index 00000000000..4dd12e32953
--- /dev/null
+++ b/sys/dev/sdmmc/sdmmc_scsi.h
@@ -0,0 +1,39 @@
+/* $OpenBSD: sdmmc_scsi.h,v 1.1 2006/05/28 17:21:14 uwe Exp $ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _SDMMC_SCSI_H_
+#define _SDMMC_SCSI_H_
+
+struct sdmmc_softc;
+
+struct sdmmc_scsi_target {
+ struct sdmmc_card *card;
+};
+
+struct sdmmc_scsi_softc {
+ struct scsi_adapter sc_adapter;
+ struct scsi_link sc_link;
+ struct device *sc_child;
+ struct sdmmc_scsi_target *sc_tgt;
+ int sc_ntargets;
+};
+
+void sdmmc_scsi_attach(struct sdmmc_softc *);
+void sdmmc_scsi_detach(struct sdmmc_softc *);
+
+#endif
diff --git a/sys/dev/sdmmc/sdmmcchip.h b/sys/dev/sdmmc/sdmmcchip.h
new file mode 100644
index 00000000000..2ac3881dd32
--- /dev/null
+++ b/sys/dev/sdmmc/sdmmcchip.h
@@ -0,0 +1,78 @@
+/* $OpenBSD: sdmmcchip.h,v 1.1 2006/05/28 17:21:14 uwe Exp $ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _SDMMC_CHIP_H_
+#define _SDMMC_CHIP_H_
+
+struct sdmmc_command;
+
+typedef struct sdmmc_chip_functions *sdmmc_chipset_tag_t;
+typedef void *sdmmc_chipset_handle_t;
+
+struct sdmmc_chip_functions {
+ /* host controller reset */
+ int (*host_reset)(sdmmc_chipset_handle_t);
+ /* host capabilities */
+ u_int32_t (*host_ocr)(sdmmc_chipset_handle_t);
+ int (*host_maxblklen)(sdmmc_chipset_handle_t);
+ /* card detection */
+ int (*card_detect)(sdmmc_chipset_handle_t);
+ /* bus power and clock frequency */
+ int (*bus_power)(sdmmc_chipset_handle_t, u_int32_t);
+ int (*bus_clock)(sdmmc_chipset_handle_t, int);
+ /* command execution */
+ int (*exec_command)(sdmmc_chipset_handle_t,
+ struct sdmmc_command *);
+};
+
+/* host controller reset */
+#define sdmmc_chip_host_reset(tag, handle) \
+ ((tag)->host_reset((handle)))
+/* host capabilities */
+#define sdmmc_chip_host_ocr(tag, handle) \
+ ((tag)->host_ocr((handle)))
+#define sdmmc_chip_host_maxblklen(tag, handle) \
+ ((tag)->host_maxblklen((handle)))
+/* card detection */
+#define sdmmc_chip_card_detect(tag, handle) \
+ ((tag)->card_detect((handle)))
+/* bus power and clock frequency */
+#define sdmmc_chip_bus_power(tag, handle, ocr) \
+ ((tag)->bus_power((handle), (ocr)))
+#define sdmmc_chip_bus_clock(tag, handle, freq) \
+ ((tag)->bus_clock((handle), (freq)))
+/* command execution */
+#define sdmmc_chip_exec_command(tag, handle, cmdp) \
+ ((tag)->exec_command((handle), (cmdp)))
+
+/* clock frequencies for sdmmc_chip_bus_clock() */
+#define SDMMC_SDCLK_OFF 0
+#define SDMMC_SDCLK_400KHZ 400
+#define SDMMC_SDCLK_25MHZ 25000
+
+struct sdmmcbus_attach_args {
+ const char *saa_busname;
+ sdmmc_chipset_tag_t sct;
+ sdmmc_chipset_handle_t sch;
+};
+
+/* host controller calls to sdmmc */
+int sdmmc_card_attach(struct device *);
+void sdmmc_card_detach(struct device *, int);
+
+#endif
diff --git a/sys/dev/sdmmc/sdmmcreg.h b/sys/dev/sdmmc/sdmmcreg.h
new file mode 100644
index 00000000000..5f0f8c34a38
--- /dev/null
+++ b/sys/dev/sdmmc/sdmmcreg.h
@@ -0,0 +1,232 @@
+/* $OpenBSD: sdmmcreg.h,v 1.1 2006/05/28 17:21:14 uwe Exp $ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _SDMMCREG_H_
+#define _SDMMCREG_H_
+
+/* MMC commands */ /* response type */
+#define MMC_GO_IDLE_STATE 0 /* R0 */
+#define MMC_SEND_OP_COND 1 /* R3 */
+#define MMC_ALL_SEND_CID 2 /* R2 */
+#define MMC_SET_RELATIVE_ADDR 3 /* R1 */
+#define MMC_SELECT_CARD 7 /* R1 */
+#define MMC_SEND_CSD 9 /* R2 */
+#define MMC_SEND_STATUS 13 /* R1 */
+#define MMC_SET_BLOCKLEN 16 /* R1 */
+#define MMC_READ_BLOCK_SINGLE 17 /* R1 */
+#define MMC_READ_BLOCK_MULTIPLE 18 /* R1 */
+#define MMC_SET_BLOCK_COUNT 23 /* R1 */
+#define MMC_WRITE_BLOCK_SINGLE 24 /* R1 */
+#define MMC_WRITE_BLOCK_MULTIPLE 25 /* R1 */
+#define MMC_APP_CMD 55 /* R1 */
+
+/* SD commands */ /* response type */
+#define SD_SEND_RELATIVE_ADDR 3 /* R6 */
+#define SD_IO_SEND_OP_COND 5 /* R4 */
+#define SD_IO_RESET 52 /* R0 */
+
+/* SD application commands */ /* response type */
+#define SD_APP_SET_BUS_WIDTH 6 /* R1 */
+#define SD_APP_OP_COND 41 /* R3 */
+
+/* OCR bits */
+#define MMC_OCR_MEM_READY (1<<31) /* memory power-up status bit */
+#define MMC_OCR_3_5V_3_6V (1<<23)
+#define MMC_OCR_3_4V_3_5V (1<<22)
+#define MMC_OCR_3_3V_3_4V (1<<21)
+#define MMC_OCR_3_2V_3_3V (1<<20)
+#define MMC_OCR_3_1V_3_2V (1<<19)
+#define MMC_OCR_3_0V_3_1V (1<<18)
+#define MMC_OCR_2_9V_3_0V (1<<17)
+#define MMC_OCR_2_8V_2_9V (1<<16)
+#define MMC_OCR_2_7V_2_8V (1<<15)
+#define MMC_OCR_2_6V_2_7V (1<<14)
+#define MMC_OCR_2_5V_2_6V (1<<13)
+#define MMC_OCR_2_4V_2_5V (1<<12)
+#define MMC_OCR_2_3V_2_4V (1<<11)
+#define MMC_OCR_2_2V_2_3V (1<<10)
+#define MMC_OCR_2_1V_2_2V (1<<9)
+#define MMC_OCR_2_0V_2_1V (1<<8)
+#define MMC_OCR_1_9V_2_0V (1<<7)
+#define MMC_OCR_1_8V_1_9V (1<<6)
+#define MMC_OCR_1_7V_1_8V (1<<5)
+#define MMC_OCR_1_6V_1_7V (1<<4)
+
+/* R1 response type bits */
+#define MMC_R1_READY_FOR_DATA (1<<8) /* ready for next transfer */
+#define MMC_R1_APP_CMD (1<<5) /* app. commands supported */
+
+/* RCA argument and response */
+#define MMC_ARG_RCA(rca) ((rca) << 16)
+#define SD_R6_RCA(resp) ((resp)[0] >> 16)
+
+/* bus width argument */
+#define SD_ARG_BUS_WIDTH_1 0
+#define SD_ARG_BUS_WIDTH_4 2
+
+/* 48-bit response decoding (32 bits w/o CRC) */
+#define MMC_R1(resp) ((resp)[0])
+#define MMC_R3(resp) ((resp)[0])
+#define MMC_R4(resp) ((resp)[0])
+
+/* MMC R2 response (CSD) */
+#define MMC_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2)
+#define MMC_CSD_CSDVER_1_0 1
+#define MMC_CSD_CSDVER_2_0 2
+#define MMC_CSD_MMCVER(resp) MMC_RSP_BITS((resp), 122, 4)
+#define MMC_CSD_MMCVER_1_0 0 /* MMC 1.0 - 1.2 */
+#define MMC_CSD_MMCVER_1_4 1 /* MMC 1.4 */
+#define MMC_CSD_MMCVER_2_0 2 /* MMC 2.0 - 2.2 */
+#define MMC_CSD_MMCVER_3_1 3 /* MMC 3.1 - 3.3 */
+#define MMC_CSD_MMCVER_4_0 4 /* MMC 4 */
+#define MMC_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4)
+#define MMC_CSD_C_SIZE(resp) MMC_RSP_BITS((resp), 62, 12)
+#define MMC_CSD_CAPACITY(resp) ((MMC_CSD_C_SIZE((resp))+1) << \
+ (MMC_CSD_C_SIZE_MULT((resp))+2))
+#define MMC_CSD_C_SIZE_MULT(resp) MMC_RSP_BITS((resp), 47, 3)
+
+/* MMC v1 R2 response (CID) */
+#define MMC_CID_MID_V1(resp) MMC_RSP_BITS((resp), 104, 24)
+#define MMC_CID_PNM_V1_CPY(resp, pnm) \
+ do { \
+ (pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \
+ (pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \
+ (pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \
+ (pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \
+ (pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \
+ (pnm)[5] = MMC_RSP_BITS((resp), 56, 8); \
+ (pnm)[6] = MMC_RSP_BITS((resp), 48, 8); \
+ (pnm)[7] = '\0'; \
+ } while (0)
+#define MMC_CID_REV_V1(resp) MMC_RSP_BITS((resp), 40, 8)
+#define MMC_CID_PSN_V1(resp) MMC_RSP_BITS((resp), 16, 24)
+#define MMC_CID_MDT_V1(resp) MMC_RSP_BITS((resp), 8, 8)
+
+/* MMC v2 R2 response (CID) */
+#define MMC_CID_MID_V2(resp) MMC_RSP_BITS((resp), 120, 8)
+#define MMC_CID_OID_V2(resp) MMC_RSP_BITS((resp), 104, 16)
+#define MMC_CID_PNM_V2_CPY(resp, pnm) \
+ do { \
+ (pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \
+ (pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \
+ (pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \
+ (pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \
+ (pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \
+ (pnm)[5] = MMC_RSP_BITS((resp), 56, 8); \
+ (pnm)[6] = '\0'; \
+ } while (0)
+#define MMC_CID_PSN_V2(resp) MMC_RSP_BITS((resp), 16, 32)
+
+/* SD R2 response (CSD) */
+#define SD_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2)
+#define SD_CSD_CSDVER_1_0 0
+#define SD_CSD_TAAC(resp) MMC_RSP_BITS((resp), 112, 8)
+#define SD_CSD_TAAC_1_5_MSEC 0x26
+#define SD_CSD_NSAC(resp) MMC_RSP_BITS((resp), 104, 8)
+#define SD_CSD_SPEED(resp) MMC_RSP_BITS((resp), 96, 8)
+#define SD_CSD_SPEED_25_MHZ 0x32
+#define SD_CSD_SPEED_50_MHZ 0x5a
+#define SD_CSD_CCC(resp) MMC_RSP_BITS((resp), 84, 12)
+#define SD_CSD_CCC_ALL 0x5f5
+#define SD_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4)
+#define SD_CSD_READ_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 79, 1)
+#define SD_CSD_WRITE_BLK_MISALIGN(resp) MMC_RSP_BITS((resp), 78, 1)
+#define SD_CSD_READ_BLK_MISALIGN(resp) MMC_RSP_BITS((resp), 77, 1)
+#define SD_CSD_DSR_IMP(resp) MMC_RSP_BITS((resp), 76, 1)
+#define SD_CSD_C_SIZE(resp) MMC_RSP_BITS((resp), 62, 12)
+#define SD_CSD_CAPACITY(resp) ((SD_CSD_C_SIZE((resp))+1) << \
+ (SD_CSD_C_SIZE_MULT((resp))+2))
+#define SD_CSD_VDD_R_CURR_MIN(resp) MMC_RSP_BITS((resp), 59, 3)
+#define SD_CSD_VDD_R_CURR_MAX(resp) MMC_RSP_BITS((resp), 56, 3)
+#define SD_CSD_VDD_W_CURR_MIN(resp) MMC_RSP_BITS((resp), 53, 3)
+#define SD_CSD_VDD_W_CURR_MAX(resp) MMC_RSP_BITS((resp), 50, 3)
+#define SD_CSD_VDD_RW_CURR_100mA 0x7
+#define SD_CSD_VDD_RW_CURR_80mA 0x6
+#define SD_CSD_C_SIZE_MULT(resp) MMC_RSP_BITS((resp), 47, 3)
+#define SD_CSD_ERASE_BLK_EN(resp) MMC_RSP_BITS((resp), 46, 1)
+#define SD_CSD_SECTOR_SIZE(resp) MMC_RSP_BITS((resp), 39, 7) /* +1 */
+#define SD_CSD_WP_GRP_SIZE(resp) MMC_RSP_BITS((resp), 32, 7) /* +1 */
+#define SD_CSD_WP_GRP_ENABLE(resp) MMC_RSP_BITS((resp), 31, 1)
+#define SD_CSD_R2W_FACTOR(resp) MMC_RSP_BITS((resp), 26, 3)
+#define SD_CSD_WRITE_BL_LEN(resp) MMC_RSP_BITS((resp), 22, 4)
+#define SD_CSD_RW_BL_LEN_2G 0xa
+#define SD_CSD_RW_BL_LEN_1G 0x9
+#define SD_CSD_WRITE_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 21, 1)
+#define SD_CSD_FILE_FORMAT_GRP(resp) MMC_RSP_BITS((resp), 15, 1)
+#define SD_CSD_COPY(resp) MMC_RSP_BITS((resp), 14, 1)
+#define SD_CSD_PERM_WRITE_PROTECT(resp) MMC_RSP_BITS((resp), 13, 1)
+#define SD_CSD_TMP_WRITE_PROTECT(resp) MMC_RSP_BITS((resp), 12, 1)
+#define SD_CSD_FILE_FORMAT(resp) MMC_RSP_BITS((resp), 10, 2)
+
+/* SD R2 response (CID) */
+#define SD_CID_MID(resp) MMC_RSP_BITS((resp), 120, 8)
+#define SD_CID_OID(resp) MMC_RSP_BITS((resp), 104, 16)
+#define SD_CID_PNM_CPY(resp, pnm) \
+ do { \
+ (pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \
+ (pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \
+ (pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \
+ (pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \
+ (pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \
+ (pnm)[5] = '\0'; \
+ } while (0)
+#define SD_CID_REV(resp) MMC_RSP_BITS((resp), 56, 8)
+#define SD_CID_PSN(resp) MMC_RSP_BITS((resp), 24, 32)
+#define SD_CID_MDT(resp) MMC_RSP_BITS((resp), 8, 12)
+
+/* SD R4 response (IO OCR) */
+#define SD_IO_OCR_MEM_READY (1<<31) /* all memory cards ready */
+#define SD_IO_OCR_MP /* memory present flag */
+#define SD_IO_OCR_NF_SHIFT 28 /* number of functions */
+#define SD_IO_OCR_NF_MASK 0x3 /* 7 functions max. */
+#define SD_IO_OCR_NF(ocr) (((ocr) >> SD_IO_OCR_NF_SHIFT) & \
+ SD_IO_OCR_NF_MASK)
+
+/* XXX slow but should work on big and little endian systems. */
+#define MMC_RSP_BITS(resp, start, len) __bitfield((resp), (start)-8, (len))
+static __inline int
+__bitfield(u_int32_t *src, int start, int len)
+{
+ u_int8_t *sp;
+ u_int32_t dst, mask;
+ int shift, bs, bc;
+
+ if (start < 0 || len < 0 || len > 32)
+ return 0;
+
+ dst = 0;
+ mask = len % 32 ? UINT_MAX >> (32 - (len % 32)) : UINT_MAX;
+ shift = 0;
+
+ while (len > 0) {
+ sp = (u_int8_t *)src + start / 8;
+ bs = start % 8;
+ bc = 8 - bs;
+ if (bc > len)
+ bc = len;
+ dst |= (*sp++ >> bs) << shift;
+ shift += bc;
+ start += bc;
+ len -= bc;
+ }
+
+ dst &= mask;
+ return (int)dst;
+}
+
+#endif
diff --git a/sys/dev/sdmmc/sdmmcvar.h b/sys/dev/sdmmc/sdmmcvar.h
new file mode 100644
index 00000000000..16ea091b88e
--- /dev/null
+++ b/sys/dev/sdmmc/sdmmcvar.h
@@ -0,0 +1,127 @@
+/* $OpenBSD: sdmmcvar.h,v 1.1 2006/05/28 17:21:14 uwe Exp $ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _SDMMCVAR_H_
+#define _SDMMCVAR_H_
+
+#include <sys/queue.h>
+#include <dev/sdmmc/sdmmcchip.h>
+#include <dev/sdmmc/sdmmcreg.h>
+
+struct sdmmc_csd {
+ int csdver; /* CSD structure format */
+ int mmcver; /* MMC version (for CID format) */
+ int capacity; /* total number of sectors */
+ int sector_size; /* sector size in bytes */
+ int read_bl_len; /* block length for reads */
+ /* ... */
+};
+
+struct sdmmc_cid {
+ int mid; /* manufacturer identification number */
+ int oid; /* OEM/product identification number */
+ char pnm[8]; /* product name (MMC v1 has the longest) */
+ int rev; /* product revision */
+ int psn; /* product serial number */
+ int mdt; /* manufacturing date */
+};
+
+struct sdmmc_command;
+
+typedef u_int32_t sdmmc_response[4];
+typedef void (*sdmmc_callback)(struct device *, struct sdmmc_command *);
+
+struct sdmmc_command {
+ u_int16_t c_opcode; /* SD or MMC command index */
+ u_int32_t c_arg; /* SD/MMC command argument */
+ sdmmc_response c_resp; /* response buffer */
+ void *c_data; /* buffer to send or read into */
+ int c_datalen; /* length of data buffer */
+ int c_blklen; /* block length */
+ int c_flags; /* see below */
+#define SCF_DONE 0x0001 /* command is finished */
+#define SCF_BUF_READY 0x0002 /* buffer ready int occurred */
+#define SCF_CMD_DONE 0x0004 /* cmd complete int occurred */
+#define SCF_XFR_DONE 0x0008 /* transfer complete int occurred */
+#define SCF_CMD_AC 0x0000
+#define SCF_CMD_ADTC 0x0010
+#define SCF_CMD_BC 0x0020
+#define SCF_CMD_BCR 0x0030
+#define SCF_CMD_READ 0x0040 /* read command (data expected) */
+#define SCF_RSP_BSY 0x0100
+#define SCF_RSP_136 0x0200
+#define SCF_RSP_CRC 0x0400
+#define SCF_RSP_IDX 0x0800
+#define SCF_RSP_PRESENT 0x1000
+/* response types */
+#define SCF_RSP_R0 0 /* none */
+#define SCF_RSP_R1 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
+#define SCF_RSP_R2 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_136)
+#define SCF_RSP_R3 (SCF_RSP_PRESENT)
+#define SCF_RSP_R4 (SCF_RSP_PRESENT)
+#define SCF_RSP_R6 (SCF_RSP_PRESENT|SCF_RSP_CRC)
+ sdmmc_callback c_done; /* callback function */
+ int c_error; /* errno value on completion */
+};
+
+struct sdmmc_card {
+ u_int16_t rca;
+ int flags;
+#define SDMMCF_CARD_ERROR 0x0010 /* card in error state */
+ sdmmc_response raw_cid;
+ struct sdmmc_cid cid;
+ struct sdmmc_csd csd;
+ SIMPLEQ_ENTRY(sdmmc_card) cs_list;
+};
+
+struct sdmmc_softc {
+ struct device sc_dev; /* base device */
+#define SDMMCDEVNAME(sc) ((sc)->sc_dev.dv_xname)
+ sdmmc_chipset_tag_t sct; /* host controller chipset tag */
+ sdmmc_chipset_handle_t sch; /* host controller chipset handle */
+ int sc_flags;
+#define SMF_SD_MODE 0x0001 /* host in SD mode (MMC otherwise) */
+#define SMF_IO_MODE 0x0002 /* host in I/O mode (SD only) */
+#define SMF_MEM_MODE 0x0004 /* host in memory mode (SD or MMC) */
+ SIMPLEQ_HEAD(, sdmmc_card) cs_head;
+ struct sdmmc_card *sc_card; /* selected card */
+ void *sc_scsibus; /* SCSI bus emulation softc */
+};
+
+#define IPL_SDMMC IPL_BIO
+#define splsdmmc() splbio()
+
+void sdmmc_delay(u_int);
+int sdmmc_set_bus_power(struct sdmmc_softc *, u_int32_t, u_int32_t);
+int sdmmc_mmc_command(struct sdmmc_softc *, struct sdmmc_command *);
+int sdmmc_app_command(struct sdmmc_softc *, struct sdmmc_command *);
+void sdmmc_go_idle_state(struct sdmmc_softc *);
+int sdmmc_select_card(struct sdmmc_softc *, struct sdmmc_card *);
+
+int sdmmc_io_enable(struct sdmmc_softc *);
+void sdmmc_io_reset(struct sdmmc_softc *);
+int sdmmc_io_send_op_cond(struct sdmmc_softc *, u_int32_t, u_int32_t *);
+
+int sdmmc_mem_enable(struct sdmmc_softc *);
+int sdmmc_mem_init(struct sdmmc_softc *, struct sdmmc_card *);
+int sdmmc_mem_read_block(struct sdmmc_softc *, struct sdmmc_card *,
+ int, u_char *, size_t);
+int sdmmc_mem_write_block(struct sdmmc_softc *, struct sdmmc_card *,
+ int, u_char *, size_t);
+
+#endif