[PATCH] SDIO support for Globalscale Dreamplug

Ilya Bakulin ilya at bakulin.de
Wed Jul 3 22:20:10 UTC 2013


On Tue, Jul 02, 2013 at 01:34:37PM -0600, Warner Losh wrote:
> 
> On Jul 2, 2013, at 1:21 PM, Ilya Bakulin wrote:
> > If we add, say, sdio0 device and store this information there, we end up
> > with
> > the hierarchy suggested by Ben Gray a year ago.
> 
> Yea, and I didn't like that at all. It violates the FreeBSD device model.
OK, I ended up adding some SDIO-related variables directly to mmc_softc.
In my understanding, we should treat the SDIO card not as a separate device,
but as an extension to the MMC bridge. In particular, things like bus width,
timing information and the number of functions obviously belong to the bridge.

I have added support for CMD53 today, which is multi-byte data transfer.
Additionally, support for setting bus width and clock speed have been added.

Now I can successfully do block reads from the card config space.

I have found another interesting problem.
When reading CIS information using single-byte read, I receive these strings:

0000   0d 21 32 36 25 2c 2c 00 38 30 32 2e 31 31 20 13  |.!26%,,.802.11 .|
0010   04 09 0f 20 09 04 3a 20 32 30 00 00 bf 20 04 9f  |... ..: 20... ..|
0020   02 18 91 21 02 0c 00 00 00 00 00 00 00 00 00 00  |...!............|

And when using block read, reading the same address in CCCR gives me
the following:

0000   4d 61 72 76 65 6c 6c 00 38 30 32 2e 31 31 20 53  |Marvell.802.11 S|
0010   44 49 4f 20 49 44 3a 20 32 30 00 00 ff 20 04 df  |DIO ID: 20... ..|

So some bytes lack 0x40... This problem occurs also with other numbers
read from the card, for example, vendor ID is read as 0x029F instead of 0x02DF.

Once this is fixed, I will try to add the code for attaching child devices.

diff --git a/sys/arm/conf/KIBAB-DPLUG b/sys/arm/conf/KIBAB-DPLUG
new file mode 100644
index 0000000..033d398
--- /dev/null
+++ b/sys/arm/conf/KIBAB-DPLUG
@@ -0,0 +1,171 @@
+# Kernel config for GlobalScale Technologies DreamPlug version 1001.
+#
+# This is for units that are version 10, revision 01, with NOR SPI flash.
+# These units are identified with the number "1001" on the S/N label.
+#
+# For more information on this file, please read the handbook section on
+# Kernel Configuration Files:
+#
+#    http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files. 
+# If you are in doubt as to the purpose or necessity of a line, check first 
+# in NOTES.
+#
+# $FreeBSD$
+#
+
+ident		KIBAB-DPLUG
+
+include		"../mv/kirkwood/std.db88f6xxx"
+
+makeoptions	FDT_DTS_FILE=dreamplug-1001.dts
+
+makeoptions	MODULES_OVERRIDE=""
+
+options 	SOC_MV_KIRKWOOD
+
+options 	SCHED_4BSD		#4BSD scheduler
+options 	INET			#InterNETworking
+options 	INET6			#IPv6 communications protocols
+options 	SOFTUPDATES
+options 	CD9660			#ISO 9660 filesystem
+options 	FFS			#Berkeley Fast Filesystem
+options 	MSDOSFS			#MS DOS File System (FAT, FAT32)
+options 	NULLFS			#NULL filesystem
+options 	TMPFS			#Efficient memory filesystem
+options 	SYSVSHM			#SYSV-style shared memory
+options 	SYSVMSG			#SYSV-style message queues
+options 	SYSVSEM			#SYSV-style semaphores
+options 	_KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions
+options 	GEOM_ELI		# Disk encryption.
+options 	GEOM_LABEL		# Providers labelization.
+options 	GEOM_PART_GPT		# GPT partitioning
+
+# Flattened Device Tree
+device		fdt
+options 	FDT
+options 	FDT_DTB_STATIC
+
+# Misc pseudo devices
+device		bpf			#Required for DHCP
+device  	faith			#IPv6-to-IPv4 relaying (translation)
+device  	firmware		#firmware(9) required for USB wlan
+device  	gif			#IPv6 and IPv4 tunneling
+device		loop			#Network loopback
+device  	md			#Memory/malloc disk
+device		pty			#BSD-style compatibility pseudo ttys
+device		random			#Entropy device
+device  	tun			#Packet tunnel.
+device		ether			#Required for all ethernet devices
+device  	vlan			#802.1Q VLAN support
+device		wlan			#802.11 WLAN support
+
+# cam support for umass and ahci
+device		scbus
+device		pass
+device		da
+device 		cd
+
+# Serial ports
+device		uart
+
+# Networking
+device		mge			# Marvell Gigabit Ethernet controller
+device		mii
+device		e1000phy
+
+# USB
+options 	USB_HOST_ALIGN=32	# Align DMA to cacheline
+#options	USB_DEBUG       	# Compile in USB debug support
+device  	usb  			# Basic usb support			
+device  	ehci 			# USB host controller
+device  	umass			# Mass storage
+device  	uhid 			# Human-interface devices
+device  	rum  			# Ralink Technology RT2501USB wireless NICs
+
+# I2C (TWSI)
+device		iic
+device		iicbus
+
+# SATA
+device		mvs
+device		ahci
+
+# SDIO
+device		mv_sdio
+device		mmcsd
+device		mmc
+
+# Sound
+device 		sound
+device 		snd_uaudio
+
+#crypto
+device  	cesa			# Marvell security engine
+device  	crypto
+device  	cryptodev
+
+# IPSec
+device  	enc
+options  	IPSEC
+options  	IPSEC_NAT_T
+options  	TCP_SIGNATURE           #include support for RFC 2385
+
+#PF 
+device  	pf
+device  	pflog
+device  	pfsync
+
+# ALTQ, required for PF
+options  	ALTQ      	      # Basic ALTQ support
+options  	ALTQ_CBQ  	      # Class Based Queueing
+options  	ALTQ_RED  	      # Random Early Detection
+options  	ALTQ_RIO  	      # RED In/Out
+options  	ALTQ_HFSC 	      # Hierarchical Packet Scheduler
+options  	ALTQ_CDNR 	      # Traffic conditioner
+options  	ALTQ_PRIQ 	      # Priority Queueing
+options  	ALTQ_NOPCC	      # Required if the TSC is unusable
+#options 	ALTQ_DEBUG
+
+# Debugging
+makeoptions 	DEBUG=-g		#Build kernel with gdb(1) debug symbols
+options 	BREAK_TO_DEBUGGER
+options 	ALT_BREAK_TO_DEBUGGER
+options 	DDB
+options 	KDB
+options 	DIAGNOSTIC
+options 	INVARIANTS		#Enable calls of extra sanity checking
+options 	INVARIANT_SUPPORT	#Extra sanity checks of internal structures, required by INVARIANTS
+#options 	WITNESS			#Enable checks to detect deadlocks and cycles
+#options 	WITNESS_SKIPSPIN	#Don't run witness on spinlocks for speed
+#options 	WITNESS_KDB
+
+# Enable these options for nfs root configured via BOOTP.
+options 	NFSCL			#Network Filesystem Client
+options 	NFSLOCKD		#Network Lock Manager
+options 	NFS_ROOT		#NFS usable as /, requires NFSCLIENT
+options 	BOOTP
+options 	BOOTP_NFSROOT
+#options 	BOOTP_NFSV3
+options 	BOOTP_WIRED_TO=mge0
+
+# If not using BOOTP, use something like one of these...
+#options 	ROOTDEVNAME=\"ufs:/dev/da1a\"
+#options 	ROOTDEVNAME=\"ufs:/dev/da1s1a\"
+#options 	ROOTDEVNAME=\"ufs:/dev/da1p10\"
+#options 	ROOTDEVNAME=\"nfs:192.168.0.254/dreamplug\"
+
+# To use this configuration with the (rare) model 1001N (nand flash),
+# create a kernel config file that looks like this:
+#
+# include DREAMPLUG-1001
+# nomakeoptions FDT_DTS_FILE
+# makeoptions	FDT_DTS_FILE=dreamplug-1001N.dts
+# device 	nand
diff --git a/sys/arm/conf/KIBAB-DPLUG-NODBG b/sys/arm/conf/KIBAB-DPLUG-NODBG
new file mode 100644
index 0000000..cbced4a
--- /dev/null
+++ b/sys/arm/conf/KIBAB-DPLUG-NODBG
@@ -0,0 +1,43 @@
+# Kernel config for GlobalScale Technologies DreamPlug version 1001.
+#
+# This is for units that are version 10, revision 01, with NOR SPI flash.
+# These units are identified with the number "1001" on the S/N label.
+#
+# For more information on this file, please read the handbook section on
+# Kernel Configuration Files:
+#
+#    http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files. 
+# If you are in doubt as to the purpose or necessity of a line, check first 
+# in NOTES.
+#
+# $FreeBSD$
+#
+
+ident		KIBAB-DPLUG-NODBG
+
+include KIBAB-DPLUG
+
+# Do not compile FDT in kernel
+nomakeoptions	FDT_DTS_FILE
+nooptions 	FDT_DTB_STATIC
+
+# Debugging
+nomakeoptions 	DEBUG
+nooptions 	BREAK_TO_DEBUGGER
+nooptions 	ALT_BREAK_TO_DEBUGGER
+nooptions 	DDB
+nooptions 	KDB
+nooptions 	DIAGNOSTIC
+nooptions 	INVARIANTS		#Enable calls of extra sanity checking
+nooptions 	INVARIANT_SUPPORT	#Extra sanity checks of internal structures, required by INVARIANTS
+#options 	WITNESS			#Enable checks to detect deadlocks and cycles
+#options 	WITNESS_SKIPSPIN	#Don't run witness on spinlocks for speed
+#options 	WITNESS_KDB
diff --git a/sys/arm/conf/KIBAB-DPLUG-PROD b/sys/arm/conf/KIBAB-DPLUG-PROD
new file mode 100644
index 0000000..bae61a4
--- /dev/null
+++ b/sys/arm/conf/KIBAB-DPLUG-PROD
@@ -0,0 +1,33 @@
+# Kernel config for GlobalScale Technologies DreamPlug version 1001.
+#
+# This is for units that are version 10, revision 01, with NOR SPI flash.
+# These units are identified with the number "1001" on the S/N label.
+#
+# For more information on this file, please read the handbook section on
+# Kernel Configuration Files:
+#
+#    http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html
+#
+# The handbook is also available locally in /usr/share/doc/handbook
+# if you've installed the doc distribution, otherwise always see the
+# FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the
+# latest information.
+#
+# An exhaustive list of options and more detailed explanations of the
+# device lines is also present in the ../../conf/NOTES and NOTES files. 
+# If you are in doubt as to the purpose or necessity of a line, check first 
+# in NOTES.
+#
+# $FreeBSD$
+#
+
+
+include KIBAB-DPLUG-NODBG
+
+ident		KIBAB-DPLUG-PROD
+
+nooptions 	NFS_ROOT
+nooptions 	BOOTP
+nooptions 	BOOTP_NFSROOT
+nooptions 	BOOTP_WIRED_TO
+
diff --git a/sys/arm/mv/files.mv b/sys/arm/mv/files.mv
index 116356d..88c0b98 100644
--- a/sys/arm/mv/files.mv
+++ b/sys/arm/mv/files.mv
@@ -32,6 +32,7 @@ arm/mv/mv_sata.c		optional	ata | atamvsata
 arm/mv/mv_ts.c			standard
 arm/mv/timer.c			standard
 arm/mv/twsi.c			optional	iicbus
+arm/mv/mv_sdio.c		optional	mv_sdio
 
 dev/cesa/cesa.c			optional	cesa
 dev/mge/if_mge.c		optional	mge
diff --git a/sys/arm/mv/mv_sdio.c b/sys/arm/mv/mv_sdio.c
new file mode 100644
index 0000000..73faf08
--- /dev/null
+++ b/sys/arm/mv/mv_sdio.c
@@ -0,0 +1,1670 @@
+/*-
+ * Copyright (c) 2009 Semihalf, Rafal Czubak
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Driver for Marvell Integrated SDIO Host Controller.
+ * Works stable in DMA mode. PIO mode has problems with large data transfers
+ * (timeouts).
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <sys/taskqueue.h>
+
+#include <arm/mv/mvreg.h>
+#include <arm/mv/mvvar.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/mmc/bridge.h>
+#include <dev/mmc/mmcreg.h>
+#include <dev/mmc/mmcvar.h>
+#include <dev/mmc/mmcbrvar.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "mmcbr_if.h"
+
+#include "mv_sdio.h"
+
+/* Minimum DMA segment size. */
+#define MV_SDIO_DMA_SEGMENT_SIZE	4096
+
+/* Transferred block size. */
+#define MV_SDIO_BLOCK_SIZE  512
+
+/* Maximum number of blocks the controller can handle. */
+#define MV_SDIO_BLOCKS_MAX		65535
+
+/* Halfword bit masks used for command response extraction. */
+#define MV_SDIO_RSP48_BM2	0x0002	/* Lower 2 bits. */
+#define MV_SDIO_RSP48_BM6	0x003f	/* Lower 6 bits. */
+#define MV_SDIO_RSP48_BM16	0xffff	/* 16 bits */
+
+/* SDIO aggregated command interrupts */
+#define MV_SDIO_IRQS_CMD (MV_SDIO_IRQ_CMD | MV_SDIO_IRQ_UNEXPECTED_RSP)
+#define MV_SDIO_EIRQS_CMD (MV_SDIO_EIRQ_CMD_TMO | MV_SDIO_EIRQ_CMD_CRC7 | \
+    MV_SDIO_EIRQ_CMD_ENDBIT | MV_SDIO_EIRQ_CMD_INDEX | \
+    MV_SDIO_EIRQ_CMD_STARTBIT | MV_SDIO_EIRQ_RSP_TBIT)
+
+/* SDIO aggregated data interrupts */
+#define MV_SDIO_IRQS_DATA (MV_SDIO_IRQ_XFER | MV_SDIO_IRQ_TX_EMPTY | \
+    MV_SDIO_IRQ_RX_FULL | MV_SDIO_IRQ_DMA | MV_SDIO_IRQ_AUTOCMD12)
+#define MV_SDIO_EIRQS_DATA (MV_SDIO_EIRQ_DATA_TMO | \
+    MV_SDIO_EIRQ_DATA_CRC16 | MV_SDIO_EIRQ_DATA_ENDBIT | \
+    MV_SDIO_EIRQ_AUTOCMD12 | MV_SDIO_EIRQ_XFER_SIZE | \
+    MV_SDIO_EIRQ_CRC_ENDBIT | MV_SDIO_EIRQ_CRC_STARTBIT | \
+    MV_SDIO_EIRQ_CRC_STAT)
+
+/*
+ * Timing configuration.
+ */
+
+/* SDIO controller base clock frequency. */
+#define MV_SDIO_F_BASE			100000000		/* 200 MHz */
+
+/* Maximum SD clock frequency. */
+#define MV_SDIO_F_MAX			(MV_SDIO_F_BASE / 2)	/* 50 MHz */
+
+/* Maximum timeout value. */
+#define MV_SDIO_TMO_MAX			0xf
+
+/* Reset delay in microseconds. */
+#define MV_SDIO_RESET_DELAY		10000	/* 10 ms */
+
+/* Empty FIFO polling delay. */
+#define MV_SDIO_FIFO_EMPTY_DELAY	1000	/* 1 ms */
+
+/* Delays between operations on multiple blocks. */
+#define MV_SDIO_RD_DELAY		50 /*50*/	/* Read access time. */
+#define MV_SDIO_WR_DELAY		10 /*10*/	/* Write access time. */
+
+/* Maximum clock divider value. */
+#define MV_SDIO_CLK_DIV_MAX		0x7ff
+
+struct mv_sdio_softc {
+	device_t		sc_dev;
+	device_t		sc_child;
+
+	bus_space_handle_t	sc_bsh;
+	bus_space_tag_t		sc_bst;
+
+	int			sc_use_dma;
+	bus_dma_tag_t		sc_dmatag;
+	bus_dmamap_t		sc_dmamap;
+	uint8_t			*sc_dmamem;
+	bus_addr_t		sc_physaddr;
+	int			sc_mapped;
+	size_t			sc_dma_size;
+
+	struct resource		*sc_mem_res;
+	int			sc_mem_rid;
+
+	struct resource		*sc_irq_res;
+	int			sc_irq_rid;
+	void			*sc_ihl;
+
+	struct resource		*sc_cd_irq_res;
+	int			sc_cd_irq_rid;
+	void			*sc_cd_ihl;
+
+	uint32_t		sc_irq_mask;
+	uint32_t		sc_eirq_mask;
+
+	struct task		sc_card_task;
+	struct callout		sc_card_callout;
+
+	struct mtx		sc_mtx;
+
+	int			sc_bus_busy;
+	int			sc_card_present;
+	struct mmc_host		sc_host;
+	struct mmc_request	*sc_req;
+	struct mmc_command	*sc_curcmd;
+
+	uint32_t		sc_data_offset;
+};
+
+/* Read/write data from/to registers.*/
+static uint32_t MV_SDIO_RD4(struct mv_sdio_softc *, bus_size_t);
+static void MV_SDIO_WR4(struct mv_sdio_softc *, bus_size_t, uint32_t);
+
+static int mv_sdio_probe(device_t);
+static int mv_sdio_attach(device_t);
+
+static int mv_sdio_read_ivar(device_t, device_t, int, uintptr_t *);
+static int mv_sdio_write_ivar(device_t, device_t, int, uintptr_t);
+
+static int mv_sdio_update_ios(device_t, device_t);
+static int mv_sdio_request(device_t, device_t, struct mmc_request *);
+static int mv_sdio_get_ro(device_t, device_t);
+static int mv_sdio_acquire_host(device_t, device_t);
+static int mv_sdio_release_host(device_t, device_t);
+
+/* Finalizes active MMC request. */
+static void mv_sdio_finalize_request(struct mv_sdio_softc *);
+
+/* Initializes controller's registers. */
+static void mv_sdio_init(device_t);
+
+/* Initializes host structure. */
+static void mv_sdio_init_host(struct mv_sdio_softc *);
+
+/* Used to add and handle sysctls. */
+static void mv_sdio_add_sysctls(struct mv_sdio_softc *);
+static int mv_sdio_sysctl_use_dma(SYSCTL_HANDLER_ARGS);
+
+/* DMA initialization and cleanup functions. */
+static int mv_sdio_dma_init(struct mv_sdio_softc *);
+static void mv_sdio_dma_finish(struct mv_sdio_softc *);
+
+/* DMA map load callback. */
+static void mv_sdio_getaddr(void *, bus_dma_segment_t *, int, int);
+
+/* Prepare command/data before transaction. */
+static int mv_sdio_start_command(struct mv_sdio_softc *, struct
+    mmc_command *);
+static int mv_sdio_start_data(struct mv_sdio_softc *, struct mmc_data *);
+
+/* Finish command after transaction. */
+static void mv_sdio_finish_command(struct mv_sdio_softc *);
+
+/* Response handling. */
+static void mv_sdio_handle_136bit_resp(struct mv_sdio_softc *);
+static void mv_sdio_handle_48bit_resp(struct mv_sdio_softc *,
+    struct mmc_command *);
+
+/* Interrupt handler and interrupt helper functions. */
+static void mv_sdio_intr(void *);
+static void mv_sdio_cmd_intr(struct mv_sdio_softc *, uint32_t, uint32_t);
+static void mv_sdio_data_intr(struct mv_sdio_softc *, uint32_t, uint32_t);
+static void mv_sdio_disable_intr(struct mv_sdio_softc *);
+
+/* Used after card detect interrupt has been handled. */
+static void mv_sdio_card_task(void *, int);
+
+/* Read/write data from FIFO in PIO mode. */
+static uint32_t mv_sdio_read_fifo(struct mv_sdio_softc *);
+static void mv_sdio_write_fifo(struct mv_sdio_softc *, uint32_t);
+
+/*
+ * PIO mode handling.
+ *
+ * Inspired by sdhci(4) driver routines.
+ */
+static void mv_sdio_transfer_pio(struct mv_sdio_softc *);
+static void mv_sdio_read_block_pio(struct mv_sdio_softc *);
+static void mv_sdio_write_block_pio(struct mv_sdio_softc *);
+
+
+static device_method_t mv_sdio_methods[] = {
+	/* device_if */
+	DEVMETHOD(device_probe, mv_sdio_probe),
+	DEVMETHOD(device_attach, mv_sdio_attach),
+
+	/* Bus interface */
+	DEVMETHOD(bus_read_ivar, mv_sdio_read_ivar),
+	DEVMETHOD(bus_write_ivar, mv_sdio_write_ivar),
+
+	/* mmcbr_if */
+	DEVMETHOD(mmcbr_update_ios, mv_sdio_update_ios),
+	DEVMETHOD(mmcbr_request, mv_sdio_request),
+	DEVMETHOD(mmcbr_get_ro, mv_sdio_get_ro),
+	DEVMETHOD(mmcbr_acquire_host, mv_sdio_acquire_host),
+	DEVMETHOD(mmcbr_release_host, mv_sdio_release_host),
+
+	{0, 0},
+};
+
+static driver_t mv_sdio_driver = {
+	"sdio",
+	mv_sdio_methods,
+	sizeof(struct mv_sdio_softc),
+};
+static devclass_t mv_sdio_devclass;
+
+DRIVER_MODULE( sdio, simplebus, mv_sdio_driver, mv_sdio_devclass, 0, 0);
+
+
+static __inline uint32_t
+MV_SDIO_RD4(struct mv_sdio_softc *sc, bus_size_t off)
+{
+
+	return (bus_read_4(sc->sc_mem_res, off));
+}
+
+static __inline void
+MV_SDIO_WR4(struct mv_sdio_softc *sc, bus_size_t off, uint32_t val)
+{
+
+	bus_write_4(sc->sc_mem_res, off, val);
+}
+
+static int platform_sdio_slot_signal( int signal )
+{
+  switch( signal )
+  {
+    case MV_SDIO_SIG_CD:
+    {
+      return -1;
+      break;
+    }
+    case MV_SDIO_SIG_WP:
+      return 0;
+      break;
+    default:
+      return -1;
+      break;
+  }
+  
+  return 0;
+}
+
+static int
+mv_sdio_probe(device_t dev)
+{
+	uint32_t device, revision;
+  
+	if (!ofw_bus_is_compatible(dev, "mrvl,sdio"))
+		return (ENXIO);
+
+
+	soc_id(&device, &revision);
+
+	switch (device) {
+	case MV_DEV_88F6281:
+		break;
+	default:
+		return (ENXIO);
+	}
+
+	device_set_desc(dev, "Marvell Integrated SDIO Host Controller");
+
+	return (BUS_PROBE_SPECIFIC);
+}
+
+static int
+mv_sdio_attach(device_t dev)
+{
+	struct mv_sdio_softc *sc;
+	int task_initialized = 0;
+
+	sc = device_get_softc(dev);
+	sc->sc_dev = dev;
+
+	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+	/* Allocate memory and interrupt resources. */
+	sc->sc_mem_rid = 0;
+	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+	    &sc->sc_mem_rid, RF_ACTIVE);
+
+	if (sc->sc_mem_res == NULL) {
+		device_printf(dev, "Could not allocate memory!\n");
+		goto fail;
+	}
+
+	sc->sc_irq_rid = 0;
+	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+	    &sc->sc_irq_rid, RF_ACTIVE);
+
+	if (sc->sc_irq_res == NULL) {
+		device_printf(dev, "Could not allocate IRQ!\n");
+		goto fail;
+	}
+
+	sc->sc_bst = rman_get_bustag(sc->sc_mem_res);
+	sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res);
+
+  
+	/* Initialize host controller's registers. */
+	mv_sdio_init(dev);
+
+	/* Try to setup DMA. */
+	sc->sc_mapped = 0;	/* No DMA buffer is mapped. */
+	sc->sc_use_dma = 1;	/* DMA mode is preferred to PIO mode. */
+
+	if (mv_sdio_dma_init(sc) < 0) {
+		device_printf(dev, "Falling back to PIO mode.\n");
+		sc->sc_use_dma = 0;
+	}
+
+	/* Add sysctls. */
+	mv_sdio_add_sysctls(sc);
+
+	if (platform_sdio_slot_signal(MV_SDIO_SIG_CD) != -1) {
+		/* Check if card is present in the slot. */
+		if (platform_sdio_slot_signal(MV_SDIO_SIG_CD) == 1)
+			sc->sc_card_present = 1;
+	}
+
+	TASK_INIT(&sc->sc_card_task, 0, mv_sdio_card_task, sc);
+	callout_init(&sc->sc_card_callout, 1);
+	task_initialized = 1;
+
+	/* Setup interrupt. */
+	if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC |
+	    INTR_MPSAFE, NULL, mv_sdio_intr, sc, &sc->sc_ihl) != 0) {
+		device_printf(dev, "Could not setup interrupt!\n");
+		goto fail;
+	}
+
+	/* Host can be acquired. */
+	sc->sc_bus_busy = 0;
+
+	/*
+	 * Attach MMC bus only if the card is in the slot or card detect is
+	 * not supported on the platform.
+	 */
+	if ((platform_sdio_slot_signal(MV_SDIO_SIG_CD) == -1) ||
+	    sc->sc_card_present) {
+		sc->sc_child = device_add_child(dev, "mmc", -1);
+
+		if (sc->sc_child == NULL) {
+			device_printf(dev, "Could not add MMC bus!\n");
+			goto fail;
+		}
+
+		/* Initialize host structure for MMC bus. */
+		mv_sdio_init_host(sc);
+
+		device_set_ivars(sc->sc_child, &sc->sc_host);
+	}
+
+	return (bus_generic_attach(dev));
+
+fail:
+	mv_sdio_dma_finish(sc);
+	if (task_initialized) {
+		callout_drain(&sc->sc_card_callout);
+		taskqueue_drain(taskqueue_swi, &sc->sc_card_task);
+	}
+	if (sc->sc_ihl != NULL)
+		bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_ihl);
+	if (sc->sc_cd_ihl != NULL)
+		bus_teardown_intr(dev, sc->sc_cd_irq_res, sc->sc_cd_ihl);
+	if (sc->sc_irq_res != NULL)
+		bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid,
+		    sc->sc_irq_res);
+	if (sc->sc_cd_irq_res != NULL)
+		bus_release_resource(dev, SYS_RES_IRQ, sc->sc_cd_irq_rid,
+		    sc->sc_cd_irq_res);
+	if (sc->sc_mem_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
+		    sc->sc_mem_res);
+	mtx_destroy(&sc->sc_mtx);
+	return (ENXIO);
+}
+
+static int
+mv_sdio_update_ios(device_t brdev, device_t reqdev)
+{
+	struct mv_sdio_softc *sc;
+	struct mmc_host *host;
+	struct mmc_ios *ios;
+	uint32_t xfer, clk_div, host_cr;
+
+	sc = device_get_softc(brdev);
+	host = device_get_ivars(reqdev);
+	ios = &host->ios;
+
+	mtx_lock(&sc->sc_mtx);
+
+	if (ios->power_mode == power_off)
+		/* Re-initialize the controller. */
+		mv_sdio_init(brdev);
+
+	xfer = MV_SDIO_RD4(sc, MV_SDIO_XFER);
+
+	if (ios->clock == 0) {
+		/* Disable clock. */
+		xfer |= MV_SDIO_XFER_STOP_CLK;
+		MV_SDIO_WR4(sc, MV_SDIO_XFER, xfer);
+
+		/* Set maximum clock divider. */
+		MV_SDIO_WR4(sc, MV_SDIO_CLK_DIV, MV_SDIO_CLK_DIV_MAX);
+	} else {
+		/*
+		 * Calculate and set clock divider.
+		 * Clock rate value is:
+		 *     clock = MV_SDIO_F_BASE / (clk_div + 1)
+		 * Thus we calculate the divider value as:
+		 *     clk_div = (MV_SDIO_F_BASE / clock) - 1
+		 */
+		clk_div = (MV_SDIO_F_BASE / ios->clock) - 1;
+		if (clk_div > MV_SDIO_CLK_DIV_MAX)
+			clk_div = MV_SDIO_CLK_DIV_MAX;
+		MV_SDIO_WR4(sc, MV_SDIO_CLK_DIV, clk_div);
+    
+		/* Enable clock. */
+		xfer &= ~MV_SDIO_XFER_STOP_CLK;
+		MV_SDIO_WR4(sc, MV_SDIO_XFER, xfer);
+	}
+
+	host_cr = MV_SDIO_RD4(sc, MV_SDIO_HOST_CR);
+
+	/* Set card type. */
+	if (host->mode == mode_mmc)
+		host_cr |= MV_SDIO_HOST_CR_MMC;		/* MMC card. */
+	else
+		host_cr &= ~MV_SDIO_HOST_CR_MMC;	/* SD card. */
+
+	/* Set bus width. */
+	if (ios->bus_width == bus_width_4)
+		host_cr |= MV_SDIO_HOST_CR_4BIT;	/* 4-bit bus width */
+	else
+		host_cr &= ~MV_SDIO_HOST_CR_4BIT;	/* 1-bit bus width */
+
+	/* Set high/normal speed mode. */
+#if 0 /* Some cards have problems with the highspeed-mode 
+       * Not selecting High-Speed mode enables all cards to work
+       */
+  
+	if ((ios->timing == bus_timing_hs ) && ( 1 == 0 ) )
+		host_cr |= MV_SDIO_HOST_CR_HIGHSPEED;
+	else
+#endif
+		host_cr &= ~MV_SDIO_HOST_CR_HIGHSPEED;
+
+	MV_SDIO_WR4(sc, MV_SDIO_HOST_CR, host_cr);
+
+	mtx_unlock(&sc->sc_mtx);
+
+	return (0);
+}
+
+static int
+mv_sdio_request(device_t brdev, device_t reqdev, struct mmc_request *req)
+{
+	struct mv_sdio_softc *sc;
+	int rv;
+
+	sc = device_get_softc(brdev);
+	rv = EBUSY;
+
+	mtx_lock(&sc->sc_mtx);
+
+	if (sc->sc_req != NULL) {
+		mtx_unlock(&sc->sc_mtx);
+		return (rv);
+	}
+
+	sc->sc_req = req;
+/*
+  device_printf(sc->sc_dev, "cmd %d (hw state 0x%04x)\n",
+          req->cmd->opcode , MV_SDIO_RD4( sc, MV_SDIO_HOST_SR ) );
+*/
+	rv = mv_sdio_start_command(sc, req->cmd);
+
+	mtx_unlock(&sc->sc_mtx);
+
+	return (rv);
+}
+
+static int
+mv_sdio_get_ro(device_t brdev, device_t reqdev)
+{
+	int rv;
+
+	/* Check if card is read only. */
+	rv = platform_sdio_slot_signal(MV_SDIO_SIG_WP);
+
+	/*
+	 * Assume that card is not write protected, when platform doesn't
+	 * support WP signal.
+	 */
+	if (rv < 0)
+		rv = 0;
+
+	return (rv);
+}
+
+static int
+mv_sdio_acquire_host(device_t brdev, device_t reqdev)
+{
+	struct mv_sdio_softc *sc;
+	int rv;
+
+	sc = device_get_softc(brdev);
+	rv = 0;
+
+	mtx_lock(&sc->sc_mtx);
+	while (sc->sc_bus_busy)
+		rv = mtx_sleep(sc, &sc->sc_mtx, PZERO, "sdioah", 0);
+	sc->sc_bus_busy++;
+	mtx_unlock(&sc->sc_mtx);
+
+	return (rv);
+}
+
+static int
+mv_sdio_release_host(device_t brdev, device_t reqdev)
+{
+	struct mv_sdio_softc *sc;
+
+	sc = device_get_softc(brdev);
+
+	mtx_lock(&sc->sc_mtx);
+	sc->sc_bus_busy--;
+	wakeup(sc);
+	mtx_unlock(&sc->sc_mtx);
+
+	return (0);
+}
+
+static void
+mv_sdio_finalize_request(struct mv_sdio_softc *sc)
+{
+	struct mmc_request *req;
+
+	mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+	req = sc->sc_req;
+
+	if (req) {
+		/* Finalize active request. */
+    /*device_printf(sc->sc_dev, "Finalize request %i\n",req->cmd->opcode);*/
+		sc->sc_req = NULL;
+		sc->sc_curcmd = NULL;
+		req->done(req);
+    
+    
+	} else
+		device_printf(sc->sc_dev, "No active request to finalize!\n");
+}
+
+static void
+mv_sdio_init(device_t dev)
+{
+	struct mv_sdio_softc *sc;
+	uint32_t host_cr;
+
+	sc = device_get_softc(dev);
+
+	/* Disable interrupts. */
+	sc->sc_irq_mask = 0;
+	sc->sc_eirq_mask = 0;
+	MV_SDIO_WR4(sc, MV_SDIO_IRQ_EN, sc->sc_irq_mask);
+	MV_SDIO_WR4(sc, MV_SDIO_EIRQ_EN, sc->sc_eirq_mask);
+
+	/* Clear interrupt status registers. */
+	MV_SDIO_WR4(sc, MV_SDIO_IRQ_SR, MV_SDIO_IRQ_ALL);
+	MV_SDIO_WR4(sc, MV_SDIO_EIRQ_SR, MV_SDIO_EIRQ_ALL);
+
+	/* Enable interrupt status registers. */
+	MV_SDIO_WR4(sc, MV_SDIO_IRQ_SR_EN, MV_SDIO_IRQ_ALL);
+	MV_SDIO_WR4(sc, MV_SDIO_EIRQ_SR_EN, MV_SDIO_EIRQ_ALL);
+
+	/* Initialize Host Control Register. */
+	host_cr = (MV_SDIO_HOST_CR_PUSHPULL | MV_SDIO_HOST_CR_BE |
+             MV_SDIO_HOST_CR_TMOVAL(MV_SDIO_TMO_MAX) | MV_SDIO_HOST_CR_TMO);
+
+	MV_SDIO_WR4(sc, MV_SDIO_HOST_CR, host_cr);
+
+	/* Stop clock and reset Transfer Mode Register. */
+	MV_SDIO_WR4(sc, MV_SDIO_XFER, MV_SDIO_XFER_STOP_CLK);
+
+	/* Set maximum clock divider value. */
+	MV_SDIO_WR4(sc, MV_SDIO_CLK_DIV, MV_SDIO_CLK_DIV_MAX);
+
+	/* Reset status, state machine and FIFOs synchronously. */
+	MV_SDIO_WR4(sc, MV_SDIO_SW_RESET, MV_SDIO_SW_RESET_ALL);
+	    DELAY(MV_SDIO_RESET_DELAY);
+}
+
+static void
+mv_sdio_init_host(struct mv_sdio_softc *sc)
+{
+	struct mmc_host *host;
+
+	host = &sc->sc_host;
+
+	/* Clear host structure. */
+	bzero(host, sizeof(struct mmc_host));
+
+	/* Calculate minimum and maximum operating frequencies. */
+	host->f_min = MV_SDIO_F_BASE / (MV_SDIO_CLK_DIV_MAX + 1);
+	host->f_max = MV_SDIO_F_MAX;
+  
+	/* Set operation conditions (voltage). */
+	host->host_ocr = MMC_OCR_320_330 | MMC_OCR_330_340;
+
+	/* Set additional host controller capabilities. */
+	host->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_HSPEED;
+}
+
+static void
+mv_sdio_add_sysctls(struct mv_sdio_softc *sc)
+{
+	struct sysctl_ctx_list *ctx;
+	struct sysctl_oid_list *children;
+	struct sysctl_oid *tree;
+
+	ctx = device_get_sysctl_ctx(sc->sc_dev);
+	children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev));
+	tree = SYSCTL_ADD_NODE(ctx, children, OID_AUTO, "params",
+	    CTLFLAG_RD, 0, "Driver parameters");
+	children = SYSCTL_CHILDREN(tree);
+
+	SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "use_dma",
+	    CTLTYPE_UINT | CTLFLAG_RW, sc, 0, mv_sdio_sysctl_use_dma,
+	    "I", "Use DMA for data transfers (0-1)");
+}
+
+/*
+ * This sysctl allows switching between DMA and PIO modes for data transfers:
+ *
+ * dev.mv_sdio.<unit>.params.use_dma
+ *
+ * Values:
+ *
+ * - 1 sets DMA mode
+ * - 0 sets PIO mode
+ *
+ * Driver uses DMA mode by default.
+ */
+static int
+mv_sdio_sysctl_use_dma(SYSCTL_HANDLER_ARGS)
+{
+	struct mv_sdio_softc *sc;
+	uint32_t use_dma;
+	int error;
+
+	sc = (struct mv_sdio_softc *)arg1;
+
+	use_dma = sc->sc_use_dma;
+
+	error = sysctl_handle_int(oidp, &use_dma, 0, req);
+	if (error != 0 || req->newptr == NULL)
+		return (error);
+
+	if (use_dma > 1)
+		return (EINVAL);
+
+	mtx_lock(&sc->sc_mtx);
+
+	/* Check if requested mode is already being used. */
+	if (sc->sc_use_dma == use_dma) {
+		mtx_unlock(&sc->sc_mtx);
+		return (EPERM);
+	}
+
+	if (!(sc->sc_mapped)) {
+		device_printf(sc->sc_dev, "DMA not initialized!\n");
+		mtx_unlock(&sc->sc_mtx);
+		return (ENOMEM);
+	}
+
+	/* Set new mode. */
+	sc->sc_use_dma = use_dma;
+
+	mtx_unlock(&sc->sc_mtx);
+
+	return (0);
+}
+
+static void
+mv_sdio_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+
+	if (error != 0)
+		return;
+
+	/* Get first segment's physical address. */
+	*(bus_addr_t *)arg = segs->ds_addr;
+}
+
+static int
+mv_sdio_dma_init(struct mv_sdio_softc *sc)
+{
+	device_t dev;
+	bus_size_t dmabuf_size;
+
+	dev = sc->sc_dev;
+	dmabuf_size = MAXPHYS;
+
+	/* Create DMA tag. */
+	if (bus_dma_tag_create(bus_get_dma_tag(dev),	/* parent */
+	    MV_SDIO_DMA_SEGMENT_SIZE, 0,	/* alignment, boundary */
+	    BUS_SPACE_MAXADDR_32BIT,		/* lowaddr */
+	    BUS_SPACE_MAXADDR,			/* highaddr */
+	    NULL, NULL,				/* filtfunc, filtfuncarg */
+	    MAXPHYS, 1,				/* maxsize, nsegments */
+	    MAXPHYS, BUS_DMA_ALLOCNOW,		/* maxsegsz, flags */
+	    NULL, NULL,				/* lockfunc, lockfuncarg */
+	    &sc->sc_dmatag) != 0) {
+		device_printf(dev, "Could not create DMA tag!\n");
+		return (-1);
+	}
+
+	/* Allocate DMA memory. */
+	if (bus_dmamem_alloc(sc->sc_dmatag, (void **)&sc->sc_dmamem,
+	    BUS_DMA_NOWAIT, &sc->sc_dmamap) != 0) {
+		device_printf(dev, "Could not allocate DMA memory!\n");
+		mv_sdio_dma_finish(sc);
+		return (-1);
+	}
+
+	/* Find the biggest available DMA buffer size. */
+	while (bus_dmamap_load(sc->sc_dmatag, sc->sc_dmamap,
+	    (void *)sc->sc_dmamem, dmabuf_size, mv_sdio_getaddr,
+	    &sc->sc_physaddr, 0) != 0) {
+		dmabuf_size >>= 1;
+		if (dmabuf_size < MV_SDIO_BLOCK_SIZE) {
+			device_printf(dev, "Could not load DMA map!\n");
+			mv_sdio_dma_finish(sc);
+			return (-1);
+		}
+	}
+
+	sc->sc_mapped++;
+	sc->sc_dma_size = dmabuf_size;
+
+	return (0);
+}
+
+static void
+mv_sdio_dma_finish(struct mv_sdio_softc *sc)
+{
+
+	/* Free DMA resources. */
+	if (sc->sc_mapped) {
+		bus_dmamap_unload(sc->sc_dmatag, sc->sc_dmamap);
+		sc->sc_mapped--;
+	}
+	if (sc->sc_dmamem != NULL)
+		bus_dmamem_free(sc->sc_dmatag, sc->sc_dmamem, sc->sc_dmamap);
+	if (sc->sc_dmamap != NULL)
+		bus_dmamap_destroy(sc->sc_dmatag, sc->sc_dmamap);
+	if (sc->sc_dmatag != NULL)
+		bus_dma_tag_destroy(sc->sc_dmatag);
+}
+
+static int
+mv_sdio_start_command(struct mv_sdio_softc *sc, struct mmc_command *cmd)
+{
+	struct mmc_request *req;
+	uint32_t cmdreg;
+
+	mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+	req = sc->sc_req;
+
+	sc->sc_curcmd = cmd;
+
+	cmd->error = MMC_ERR_NONE;
+
+	/* Check if card is in the slot. */
+	if ((platform_sdio_slot_signal(MV_SDIO_SIG_CD) != -1) &&
+	    (sc->sc_card_present == 0)) {
+		cmd->error = MMC_ERR_FAILED;
+		mv_sdio_finalize_request(sc);
+		return (-1);
+	}
+
+	/* Check if clock is enabled. */
+	if (MV_SDIO_RD4(sc, MV_SDIO_XFER) & MV_SDIO_XFER_STOP_CLK) {
+		cmd->error = MMC_ERR_FAILED;
+		mv_sdio_finalize_request(sc);
+		return (-1);
+	}
+
+	/* Write command argument. */
+	MV_SDIO_WR4(sc, MV_SDIO_CMD_ARGL, cmd->arg & 0xffff);
+	MV_SDIO_WR4(sc, MV_SDIO_CMD_ARGH, cmd->arg >> 16);
+
+	/* Determine response type. */
+	if (cmd->flags & MMC_RSP_136)
+		cmdreg = MV_SDIO_CMD_RSP_136;
+	else if (cmd->flags & MMC_RSP_BUSY)
+		cmdreg = MV_SDIO_CMD_RSP_48_BUSY;
+	else if (cmd->flags & MMC_RSP_PRESENT)
+		cmdreg = MV_SDIO_CMD_RSP_48;
+	else {
+		/* No response. */
+		cmdreg = MV_SDIO_CMD_RSP_NONE;
+		/* Enable host to detect unexpected response. */
+		cmdreg |= MV_SDIO_CMD_UNEXPECTED_RSP;
+		sc->sc_irq_mask |= MV_SDIO_CMD_UNEXPECTED_RSP;
+	}
+
+	/* Check command checksum if needed. */
+	if (cmd->flags & MMC_RSP_CRC)
+		cmdreg |= MV_SDIO_CMD_CRC7;
+	/* Check command opcode if needed. */
+	if (cmd->flags & MMC_RSP_OPCODE)
+		cmdreg |= MV_SDIO_CMD_INDEX_CHECK;
+
+	/* Set commannd opcode. */
+	cmdreg |= MV_SDIO_CMD_INDEX(cmd->opcode);
+
+	/* Setup interrupts. */
+	sc->sc_irq_mask = MV_SDIO_IRQ_CMD;
+	sc->sc_eirq_mask = MV_SDIO_EIRQ_ALL;
+
+	/* Prepare data transfer. */
+	if (cmd->data) {
+		cmdreg |= (MV_SDIO_CMD_DATA_PRESENT | MV_SDIO_CMD_DATA_CRC16);
+		if (mv_sdio_start_data(sc, cmd->data) < 0) {
+			cmd->error = MMC_ERR_FAILED;
+      printf("mv_sdio_start_data() failed!\n");
+			mv_sdio_finalize_request(sc);
+			return (-1);
+		}
+	}
+
+	/* Write command register. */
+	MV_SDIO_WR4(sc, MV_SDIO_CMD, cmdreg);
+
+	/* Clear interrupt status. */
+	MV_SDIO_WR4(sc, MV_SDIO_IRQ_SR, ~MV_SDIO_IRQ_CARD_EVENT /*MV_SDIO_IRQ_ALL*/);
+	MV_SDIO_WR4(sc, MV_SDIO_EIRQ_SR, 0xffff /*MV_SDIO_EIRQ_ALL*/);
+
+	/* Update interrupt/error interrupt enable registers. */
+	MV_SDIO_WR4(sc, MV_SDIO_IRQ_EN, sc->sc_irq_mask);
+	MV_SDIO_WR4(sc, MV_SDIO_EIRQ_EN, sc->sc_eirq_mask);
+
+	/* Do not complete request, interrupt handler will do this. */
+	return (0);
+}
+
+static void
+mv_sdio_finish_command(struct mv_sdio_softc *sc)
+{
+	struct mmc_command *cmd;
+	struct mmc_data *data;
+
+	mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+	cmd = sc->sc_curcmd;
+	data = cmd->data;
+
+	/* Get response. */
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		if(cmd->flags & MMC_RSP_136)
+			/* 136-bit response. */
+			mv_sdio_handle_136bit_resp(sc);
+		else
+			/* 48-bit response. */
+			mv_sdio_handle_48bit_resp(sc, NULL);
+	}
+
+	if (data) {
+		/*
+		 * Disable command complete interrupt. It has already been
+		 * handled.
+		 */
+		sc->sc_irq_mask &= ~MV_SDIO_IRQ_CMD;
+
+		/* Enable XFER interrupt. */
+		sc->sc_irq_mask |= MV_SDIO_IRQ_XFER;
+
+		/* Check which data interrupts we need to activate. */
+		if (sc->sc_use_dma)
+			/* DMA transaction. */
+			sc->sc_irq_mask |= MV_SDIO_IRQ_DMA;
+		else if (data->flags & MMC_DATA_READ)
+			/* Read transaction in PIO mode. */
+			sc->sc_irq_mask |= MV_SDIO_IRQ_RX_FULL;
+		else
+			/* Write transaction in PIO mode. */
+			sc->sc_irq_mask |= MV_SDIO_IRQ_TX_EMPTY;
+
+		/* Check if Auto-CMD12 interrupt will be needed. */
+		if (sc->sc_req->stop)
+			sc->sc_irq_mask |= MV_SDIO_IRQ_AUTOCMD12;
+
+		/* Update interrupt enable register. */
+		MV_SDIO_WR4(sc, MV_SDIO_IRQ_EN, sc->sc_irq_mask);
+	} else {
+		/* We're done. Disable interrupts and finalize request. */
+		mv_sdio_disable_intr(sc);
+		mv_sdio_finalize_request(sc);
+	}
+}
+
+static int
+mv_sdio_start_data(struct mv_sdio_softc *sc, struct mmc_data *data)
+{
+	struct mmc_command *stop;
+	uint32_t autocmd12reg, xfer, host_sr;
+	size_t blk_size, blk_count;
+	int retries;
+
+	mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+	/*
+	 * No transfer can be started when FIFO_EMPTY bit in MV_SDIO_HOST_SR
+	 * is not set. This bit is sometimes not set instantly after XFER
+	 * interrupt has been asserted.
+	 */
+	host_sr = MV_SDIO_RD4(sc, MV_SDIO_HOST_SR);
+
+	retries = 10;
+	while (!(host_sr & MV_SDIO_HOST_SR_FIFO_EMPTY)) {
+		if (retries == 0)
+			return (-1);
+		retries--;
+		DELAY(MV_SDIO_FIFO_EMPTY_DELAY);
+		host_sr = MV_SDIO_RD4(sc, MV_SDIO_HOST_SR);
+	}
+
+	/* Clear data offset. */
+	sc->sc_data_offset = 0;
+
+	/*
+	 * Set block size. It can be less than or equal to MV_SDIO_BLOCK_SIZE
+	 * bytes.
+	 */
+	blk_size = (data->len < MV_SDIO_BLOCK_SIZE) ? data->len :
+	    MV_SDIO_BLOCK_SIZE;
+	MV_SDIO_WR4(sc, MV_SDIO_BLK_SIZE, blk_size);
+
+	/* Set block count. */
+	blk_count = (data->len + MV_SDIO_BLOCK_SIZE - 1) / MV_SDIO_BLOCK_SIZE;
+	MV_SDIO_WR4(sc, MV_SDIO_BLK_COUNT, blk_count);
+
+	/* We want to initiate transfer by software. */
+	xfer = MV_SDIO_XFER_SW_WR_EN;
+
+	if (sc->sc_use_dma) {
+		/* Synchronize before DMA transfer. */
+		if (data->flags & MMC_DATA_READ)
+			bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap,
+			    BUS_DMASYNC_PREREAD);
+		else {
+			memcpy(sc->sc_dmamem, data->data, data->len);
+			bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap,
+			    BUS_DMASYNC_PREWRITE);
+		}
+
+		/* Write DMA buffer address register. */
+		MV_SDIO_WR4(sc, MV_SDIO_DMA_ADDRL, sc->sc_physaddr & 0xffff);
+		MV_SDIO_WR4(sc, MV_SDIO_DMA_ADDRH, sc->sc_physaddr >> 16);
+	} else
+		/* Set PIO transfer mode. */
+		xfer |= MV_SDIO_XFER_PIO;
+
+	/*
+	 * Prepare Auto-CMD12. This command is automatically sent to the card
+	 * by the host controller to stop multiple-block data transaction.
+	 */
+	if (sc->sc_req->stop) {
+		stop = sc->sc_req->stop;
+
+		/* Set Auto-CMD12 argument. */
+		MV_SDIO_WR4(sc, MV_SDIO_AUTOCMD12_ARGL, stop->arg & 0xffff);
+		MV_SDIO_WR4(sc, MV_SDIO_AUTOCMD12_ARGH, stop->arg >> 16);
+
+		/* Set Auto-CMD12 opcode. */
+		autocmd12reg = MV_SDIO_AUTOCMD12_INDEX(stop->opcode);
+
+		/* Check busy signal if needed. */
+		if (stop->flags & MMC_RSP_BUSY)
+			autocmd12reg |= MV_SDIO_AUTOCMD12_BUSY_CHECK;
+		/* Check Auto-CMD12 index. */
+		if (stop->flags & MMC_RSP_OPCODE)
+			autocmd12reg |= MV_SDIO_AUTOCMD12_INDEX_CHECK;
+
+		MV_SDIO_WR4(sc, MV_SDIO_AUTOCMD12, autocmd12reg);
+
+		xfer |= MV_SDIO_XFER_AUTOCMD12;
+	}
+
+	/* Change data direction. */
+	if (data->flags & MMC_DATA_READ)
+		xfer |= MV_SDIO_XFER_TO_HOST;
+
+	/* Write transfer mode register. */
+	MV_SDIO_WR4(sc, MV_SDIO_XFER, xfer);
+
+	return (0);
+}
+
+static void
+mv_sdio_handle_136bit_resp(struct mv_sdio_softc *sc)
+{
+	struct mmc_command *cmd;
+	uint32_t resp[8];
+	uint32_t base, extra;
+	int i, j, off;
+
+	mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+	cmd = sc->sc_curcmd;
+
+	/* Collect raw response from the controller. */
+	for (i = 0; i < 8; i++)
+		resp[i] = MV_SDIO_RD4(sc, MV_SDIO_RSP(i));
+
+	/* Response passed to MMC bus is shifted by one byte. */
+	extra = 0;
+	for (i = 0, j = 7; i < 4; i++, j -= 2) {
+		off = (i ? 0 : 2);
+		base = resp[j] | (resp[j - 1] << (16 - off));
+		cmd->resp[3 - i] = (base << (6 + off)) + extra;
+		extra = base >> (26 - off);
+	}
+}
+
+static void
+mv_sdio_handle_48bit_resp(struct mv_sdio_softc *sc, struct mmc_command *stop)
+{
+	struct mmc_command *cmd;
+	uint32_t resp[3], word;
+	uint8_t *rp;
+	int i;
+
+	mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+	if (stop == NULL)
+		cmd = sc->sc_curcmd;
+	else
+		cmd = stop;
+
+	/* Collect raw response from the controller. */
+	for (i = 0; i < 3; i++) {
+		if (stop == NULL)
+			resp[i] = MV_SDIO_RD4(sc, MV_SDIO_RSP(i));
+		else
+			resp[i] = MV_SDIO_RD4(sc, MV_SDIO_AUTOCMD12_RSP(i));
+	}
+
+	/* Clear MMC bus response buffer. */
+	bzero(&cmd->resp[0], 4 * sizeof(uint32_t));
+
+	/*
+	 * Fill MMC bus response buffer.
+	 */
+
+	rp = (uint8_t *)&cmd->resp[0];
+
+	/* Response bits [45:14] */
+	word = (resp[1] & MV_SDIO_RSP48_BM16) |
+	    ((resp[0] & MV_SDIO_RSP48_BM16) << 16);
+
+	/* Response bits [15:14] and [13:8] */
+	*rp++ = (resp[2] & MV_SDIO_RSP48_BM6) |
+	    ((word & MV_SDIO_RSP48_BM2) << 6);
+
+	/* Response bits [15:14] are already included. */
+	word >>= 2;
+
+	/* Response bits [45:16] */
+	memcpy(rp, &word, sizeof(uint32_t));
+}
+
+static void
+mv_sdio_intr(void *arg)
+{
+	struct mv_sdio_softc *sc;
+	uint32_t irq_stat, eirq_stat;
+
+  sc = (struct mv_sdio_softc *)arg;
+#if 0
+	device_printf(sc->sc_dev,"intr 0x%04x intr_en 0x%04x hw_state 0x%04x\n",
+                MV_SDIO_RD4( sc, MV_SDIO_IRQ_SR ) ,  
+                MV_SDIO_RD4( sc, MV_SDIO_IRQ_EN ),
+                MV_SDIO_RD4( sc, MV_SDIO_HOST_SR ));
+#endif
+  
+  
+	mtx_lock(&sc->sc_mtx);
+
+  
+
+	irq_stat = MV_SDIO_RD4(sc, MV_SDIO_IRQ_SR) & sc->sc_irq_mask;
+	eirq_stat = MV_SDIO_RD4(sc, MV_SDIO_EIRQ_SR) & sc->sc_eirq_mask;
+
+	/*
+	 * In case of error interrupt, interrupt cause will be identified by
+	 * checking bits in error interrupt status register.
+	 */
+	irq_stat &= ~MV_SDIO_IRQ_ERR;
+
+	/* Handle command interrupts. */
+	if ((irq_stat & MV_SDIO_IRQS_CMD) ||
+	    (eirq_stat & MV_SDIO_EIRQS_CMD)) {
+		MV_SDIO_WR4(sc, MV_SDIO_IRQ_SR, irq_stat);
+		MV_SDIO_WR4(sc, MV_SDIO_EIRQ_SR, eirq_stat);
+		mv_sdio_cmd_intr(sc, irq_stat, eirq_stat);
+		irq_stat &= ~MV_SDIO_IRQS_CMD;
+		eirq_stat &= ~MV_SDIO_EIRQS_CMD;
+	}
+
+	/* Handle data interrupts. */
+	if ((irq_stat & MV_SDIO_IRQS_DATA) ||
+	    (eirq_stat & MV_SDIO_EIRQS_DATA)) {
+		MV_SDIO_WR4(sc, MV_SDIO_IRQ_SR, irq_stat);
+		MV_SDIO_WR4(sc, MV_SDIO_EIRQ_SR, eirq_stat);
+		mv_sdio_data_intr(sc, irq_stat, eirq_stat);
+		irq_stat &= ~MV_SDIO_IRQS_DATA;
+		eirq_stat &= ~MV_SDIO_EIRQS_DATA;
+	}
+
+	/* Handle unexpected interrupts. */
+	if (irq_stat) {
+		device_printf(sc->sc_dev, "Unexpected interrupt(s)! "
+		    "IRQ SR = 0x%08x\n", irq_stat);
+		/* Clear interrupt status. */
+		MV_SDIO_WR4(sc, MV_SDIO_IRQ_SR, irq_stat);
+	}
+	if (eirq_stat) {
+		device_printf(sc->sc_dev, "Unexpected error interrupt(s)! "
+		    "EIRQ SR = 0x%08x\n", eirq_stat);
+		/* Clear error interrupt status. */
+		MV_SDIO_WR4(sc, MV_SDIO_EIRQ_SR, eirq_stat);
+	}
+
+	mtx_unlock(&sc->sc_mtx);
+}
+
+static void
+mv_sdio_cmd_intr(struct mv_sdio_softc *sc, uint32_t irq, uint32_t eirq)
+{
+
+	mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+	if (!sc->sc_curcmd) {
+		device_printf(sc->sc_dev, "Got command interrupt, but there "
+		    "is no active command!\n");
+		return;
+	}
+
+	/* Handle unexpected response error. */
+	if (irq & MV_SDIO_IRQ_UNEXPECTED_RSP) {
+		sc->sc_curcmd->error = MMC_ERR_FAILED;
+		device_printf(sc->sc_dev, "Unexpected response!\n");
+	}
+
+	/* Handle errors. */
+	if (eirq & MV_SDIO_EIRQ_CMD_TMO) {
+		sc->sc_curcmd->error = MMC_ERR_TIMEOUT;
+		device_printf(sc->sc_dev, "Error - command %d timeout!\n",
+		    sc->sc_curcmd->opcode);
+	} else if (eirq & MV_SDIO_EIRQ_CMD_CRC7) {
+		sc->sc_curcmd->error = MMC_ERR_BADCRC;
+		device_printf(sc->sc_dev, "Error - bad command %d "
+		    "checksum!\n", sc->sc_curcmd->opcode);
+	} else if (eirq) {
+		sc->sc_curcmd->error = MMC_ERR_FAILED;
+		device_printf(sc->sc_dev, "Command %d error!\n",
+		    sc->sc_curcmd->opcode);
+	}
+
+	if (sc->sc_curcmd->error != MMC_ERR_NONE) {
+		/* Error. Disable interrupts and finalize request. */
+		mv_sdio_disable_intr(sc);
+		mv_sdio_finalize_request(sc);
+		return;
+	}
+
+	if (irq & MV_SDIO_IRQ_CMD)
+		mv_sdio_finish_command(sc);
+}
+
+static void
+mv_sdio_data_intr(struct mv_sdio_softc *sc, uint32_t irq, uint32_t eirq)
+{
+	struct mmc_command *stop;
+
+	mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+	if (!sc->sc_curcmd) {
+		device_printf(sc->sc_dev, "Got data interrupt, but there is "
+		    "no active command.\n");
+		return;
+	}
+	if ((!sc->sc_curcmd->data) && ((sc->sc_curcmd->flags &
+	    MMC_RSP_BUSY) == 0)) {
+		device_printf(sc->sc_dev, "Got data interrupt, but there is "
+		    "no active data transaction.n\n");
+		sc->sc_curcmd->error = MMC_ERR_FAILED;
+		return;
+	}
+
+	/* Handle errors. */
+	if(eirq & MV_SDIO_EIRQ_DATA_TMO) {
+		sc->sc_curcmd->error = MMC_ERR_TIMEOUT;
+		device_printf(sc->sc_dev, "Data %s timeout!\n",
+		    (sc->sc_curcmd->data->flags & MMC_DATA_READ) ? "read" :
+		    "write");
+	} else if (eirq & (MV_SDIO_EIRQ_DATA_CRC16 |
+	    MV_SDIO_EIRQ_DATA_ENDBIT)) {
+		sc->sc_curcmd->error = MMC_ERR_BADCRC;
+		device_printf(sc->sc_dev, "Bad data checksum!\n");
+	} else if (eirq) {
+		sc->sc_curcmd->error = MMC_ERR_FAILED;
+		device_printf(sc->sc_dev, "Data error!: 0x%04X \n",
+      eirq);
+
+    if( 0 != ( eirq & MV_SDIO_EIRQ_CRC_STAT ) )
+    {
+      device_printf(sc->sc_dev, "MV_SDIO_EIRQ_CRC_STAT\n");
+    }
+	}
+
+	/* Handle Auto-CMD12 error. */
+	if (eirq & MV_SDIO_EIRQ_AUTOCMD12) {
+		sc->sc_req->stop->error = MMC_ERR_FAILED;
+		sc->sc_curcmd->error = MMC_ERR_FAILED;
+		device_printf(sc->sc_dev, "Auto-CMD12 error!\n");
+	}
+
+	if (sc->sc_curcmd->error != MMC_ERR_NONE) {
+		/* Error. Disable interrupts and finalize request. */
+		mv_sdio_disable_intr(sc);
+		mv_sdio_finalize_request(sc);
+		return;
+	}
+
+	/* Handle PIO interrupt. */
+	if (irq & (MV_SDIO_IRQ_TX_EMPTY | MV_SDIO_IRQ_RX_FULL))
+		mv_sdio_transfer_pio(sc);
+
+	/* Handle DMA interrupt. */
+	if (irq & (MV_SDIO_IRQ_DMA)) {
+		/* Synchronize DMA buffer. */
+		if (MV_SDIO_RD4(sc, MV_SDIO_XFER) & MV_SDIO_XFER_TO_HOST) {
+			bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap,
+			    BUS_DMASYNC_POSTWRITE);
+			memcpy(sc->sc_curcmd->data->data, sc->sc_dmamem,
+			    sc->sc_curcmd->data->len);
+		} else
+			bus_dmamap_sync(sc->sc_dmatag, sc->sc_dmamap,
+			    BUS_DMASYNC_POSTREAD);
+
+		/* Disable DMA interrupt. */
+		sc->sc_irq_mask &= ~MV_SDIO_IRQ_DMA;
+		MV_SDIO_WR4(sc, MV_SDIO_IRQ_EN, sc->sc_irq_mask);
+	}
+
+	/* Handle Auto-CMD12 interrupt. */
+	if (irq & (MV_SDIO_IRQ_AUTOCMD12)) {
+		stop = sc->sc_req->stop;
+		/* Get 48-bit response. */
+		mv_sdio_handle_48bit_resp(sc, stop);
+
+		/* Disable Auto-CMD12 interrupt. */
+		sc->sc_irq_mask &= ~MV_SDIO_IRQ_AUTOCMD12;
+		MV_SDIO_WR4(sc, MV_SDIO_IRQ_EN, sc->sc_irq_mask);
+	}
+
+	/* Transfer finished. Disable interrupts and finalize request. */
+	if (irq & (MV_SDIO_IRQ_XFER)) {
+		mv_sdio_disable_intr(sc);
+		mv_sdio_finalize_request(sc);
+	}
+}
+
+static void
+mv_sdio_disable_intr(struct mv_sdio_softc *sc)
+{
+
+	/* Disable interrupts that were enabled. */
+	sc->sc_irq_mask &= ~(sc->sc_irq_mask);
+	sc->sc_eirq_mask &= ~(sc->sc_eirq_mask);
+	MV_SDIO_WR4(sc, MV_SDIO_IRQ_EN, sc->sc_irq_mask);
+	MV_SDIO_WR4(sc, MV_SDIO_EIRQ_EN, sc->sc_eirq_mask);
+}
+
+static void
+mv_sdio_card_task(void *arg, int pending)
+{
+	struct mv_sdio_softc *sc;
+
+	int device_probe_and_attach_ret_val = 0;
+
+	sc = (struct mv_sdio_softc *)arg;
+
+	mtx_lock(&sc->sc_mtx);
+
+	if (sc->sc_card_present) {
+		if (sc->sc_child) {
+			mtx_unlock(&sc->sc_mtx);
+			return;
+		}
+
+		/* Initialize host controller's registers. */
+		mv_sdio_init(sc->sc_dev);
+
+		sc->sc_child = device_add_child(sc->sc_dev, "mmc", -1);
+		if (sc->sc_child == NULL) {
+			device_printf(sc->sc_dev, "Could not add MMC bus!\n");
+			mtx_unlock(&sc->sc_mtx);
+			return;
+		}
+            
+		/* Initialize host structure for MMC bus. */
+		mv_sdio_init_host(sc);
+
+		device_set_ivars(sc->sc_child, &sc->sc_host);
+
+		mtx_unlock(&sc->sc_mtx);
+
+		device_probe_and_attach_ret_val = device_probe_and_attach(sc->sc_child);
+        
+		if( 0 != device_probe_and_attach_ret_val ) {
+			device_printf(sc->sc_dev, "MMC bus failed on probe "
+			"and attach! %i\n",device_probe_and_attach_ret_val);
+			device_delete_child(sc->sc_dev, sc->sc_child);
+			sc->sc_child = NULL;
+		}
+	} else {
+		if (sc->sc_child == NULL) {
+			mtx_unlock(&sc->sc_mtx);
+			return;
+		}
+
+		mtx_unlock(&sc->sc_mtx);
+		if (device_delete_child(sc->sc_dev, sc->sc_child) != 0) {
+			device_printf(sc->sc_dev, "Could not delete MMC "
+			    "bus!\n");
+		}
+		sc->sc_child = NULL;
+	}
+}
+
+static uint32_t
+mv_sdio_read_fifo(struct mv_sdio_softc *sc)
+{
+	uint32_t data;
+	device_printf(sc->sc_dev, "This is not tested, yet MV_SDIO_FIFO not ensured\n ");
+  
+ 	while (!(MV_SDIO_RD4(sc, MV_SDIO_IRQ_SR) & MV_SDIO_IRQ_RX_FULL));
+	data = MV_SDIO_RD4(sc, MV_SDIO_FIFO);
+	while (!(MV_SDIO_RD4(sc, MV_SDIO_IRQ_SR) & MV_SDIO_IRQ_RX_FULL));
+	data |= (MV_SDIO_RD4(sc, MV_SDIO_FIFO) << 16);
+	return data;
+}
+
+static void
+mv_sdio_write_fifo(struct mv_sdio_softc *sc, uint32_t val)
+{
+	while (!(MV_SDIO_RD4(sc, MV_SDIO_IRQ_SR) & MV_SDIO_IRQ_TX_EMPTY));
+	MV_SDIO_WR4(sc, MV_SDIO_FIFO, val & 0xffff);
+	while (!(MV_SDIO_RD4(sc, MV_SDIO_IRQ_SR) & MV_SDIO_IRQ_TX_EMPTY));
+	MV_SDIO_WR4(sc, MV_SDIO_FIFO, val >> 16);
+}
+
+static void
+mv_sdio_transfer_pio(struct mv_sdio_softc *sc)
+{
+	struct mmc_command *cmd;
+
+	cmd = sc->sc_curcmd;
+
+	if (cmd->data->flags & MMC_DATA_READ) {
+		while (MV_SDIO_RD4(sc, MV_SDIO_IRQ_SR) &
+		    MV_SDIO_IRQ_RX_FULL) {
+			mv_sdio_read_block_pio(sc);
+			/*
+			 * Assert delay after each block transfer to meet read
+			 * access timing constraint.
+			 */
+			DELAY(MV_SDIO_RD_DELAY);
+			if (sc->sc_data_offset >= cmd->data->len)
+				break;
+		}
+		/* All blocks read in PIO mode. Disable interrupt. */
+		sc->sc_irq_mask &= ~MV_SDIO_IRQ_RX_FULL;
+		MV_SDIO_WR4(sc, MV_SDIO_IRQ_EN, sc->sc_irq_mask);
+	} else {
+		while (MV_SDIO_RD4(sc, MV_SDIO_IRQ_SR) &
+		    MV_SDIO_IRQ_TX_EMPTY) {
+			mv_sdio_write_block_pio(sc);
+			/* Wait while card is programming the memory. */
+			while ((MV_SDIO_RD4(sc, MV_SDIO_HOST_SR) &
+              MV_SDIO_HOST_SR_CARD_BUSY));
+			/*
+			 * Assert delay after each block transfer to meet
+			 * write access timing constraint.
+			 */
+			DELAY(MV_SDIO_WR_DELAY);
+
+			if (sc->sc_data_offset >= cmd->data->len)
+				break;
+		}
+		/* All blocks written in PIO mode. Disable interrupt. */
+		sc->sc_irq_mask &= ~MV_SDIO_IRQ_TX_EMPTY;
+		MV_SDIO_WR4(sc, MV_SDIO_IRQ_EN, sc->sc_irq_mask);
+	}
+}
+
+static void
+mv_sdio_read_block_pio(struct mv_sdio_softc *sc)
+{
+	uint32_t data;
+	char *buffer;
+	size_t left;
+
+	buffer = sc->sc_curcmd->data->data;
+	buffer += sc->sc_data_offset;
+	/* Transfer one block at a time. */
+	left = min(MV_SDIO_BLOCK_SIZE, sc->sc_curcmd->data->len -
+	    sc->sc_data_offset);
+	sc->sc_data_offset += left;
+
+	/* Handle unaligned and aligned buffer cases. */
+	if ((intptr_t)buffer & 3) {
+		while (left > 3) {
+			data = mv_sdio_read_fifo(sc);
+			buffer[0] = data;
+			buffer[1] = (data >> 8);
+			buffer[2] = (data >> 16);
+			buffer[3] = (data >> 24);
+			buffer += 4;
+			left -= 4;
+		}
+	} else {
+		while (left > 3) {
+			data = mv_sdio_read_fifo(sc);
+			*((uint32_t *)buffer) = data;
+			buffer += 4;
+			left -= 4;
+		}
+	}
+	/* Handle uneven size case. */
+	if (left > 0) {
+		data = mv_sdio_read_fifo(sc);
+		while (left > 0) {
+			*(buffer++) = data;
+			data >>= 8;
+			left--;
+		}
+	}
+}
+
+static void
+mv_sdio_write_block_pio(struct mv_sdio_softc *sc)
+{
+	uint32_t data = 0;
+	char *buffer;
+	size_t left;
+
+	buffer = sc->sc_curcmd->data->data;
+	buffer += sc->sc_data_offset;
+	/* Transfer one block at a time. */
+	left = min(MV_SDIO_BLOCK_SIZE, sc->sc_curcmd->data->len -
+	    sc->sc_data_offset);
+	sc->sc_data_offset += left;
+
+	/* Handle unaligned and aligned buffer cases. */
+	if ((intptr_t)buffer & 3) {
+		while (left > 3) {
+			data = buffer[0] +
+			    (buffer[1] << 8) +
+			    (buffer[2] << 16) +
+			    (buffer[3] << 24);
+			left -= 4;
+			buffer += 4;
+			mv_sdio_write_fifo(sc, data);
+		}
+	} else {
+		while (left > 3) {
+			data = *((uint32_t *)buffer);
+			left -= 4;
+			buffer += 4;
+			mv_sdio_write_fifo(sc, data);
+		}
+	}
+	/* Handle uneven size case. */
+	if (left > 0) {
+		data = 0;
+		while (left > 0) {
+			data <<= 8;
+			data += *(buffer++);
+			left--;
+		}
+		mv_sdio_write_fifo(sc, data);
+	}
+}
+
+static int
+mv_sdio_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
+{
+	struct mv_sdio_softc *sc;
+	struct mmc_host *host;
+
+	sc = device_get_softc(dev);
+	host = device_get_ivars(child);
+
+	switch (index) {
+	case MMCBR_IVAR_BUS_MODE:
+		*(int *)result = host->ios.bus_mode;
+		break;
+	case MMCBR_IVAR_BUS_WIDTH:
+		*(int *)result = host->ios.bus_width;
+		break;
+	case MMCBR_IVAR_CHIP_SELECT:
+		*(int *)result = host->ios.chip_select;
+		break;
+	case MMCBR_IVAR_CLOCK:
+		*(int *)result = host->ios.clock;
+		break;
+	case MMCBR_IVAR_F_MIN:
+		*(int *)result = host->f_min;
+		break;
+	case MMCBR_IVAR_F_MAX:
+		*(int *)result = host->f_max;
+		break;
+	case MMCBR_IVAR_HOST_OCR:
+		*(int *)result = host->host_ocr;
+		break;
+	case MMCBR_IVAR_MODE:
+		*(int *)result = host->mode;
+		break;
+	case MMCBR_IVAR_OCR:
+		*(int *)result = host->ocr;
+		break;
+	case MMCBR_IVAR_POWER_MODE:
+		*(int *)result = host->ios.power_mode;
+		break;
+	case MMCBR_IVAR_VDD:
+		*(int *)result = host->ios.vdd;
+		break;
+	case MMCBR_IVAR_CAPS:
+		*(int *)result = host->caps;
+		break;
+	case MMCBR_IVAR_TIMING:
+		*(int *)result = host->ios.timing;
+		break;
+	case MMCBR_IVAR_MAX_DATA:
+		mtx_lock(&sc->sc_mtx);
+		/* Return maximum number of blocks the driver can handle. */
+		if (sc->sc_use_dma)
+			*(int *)result = (sc->sc_dma_size /
+			    MV_SDIO_BLOCK_SIZE);
+		else
+			*(int *)result = MV_SDIO_BLOCKS_MAX;
+		mtx_unlock(&sc->sc_mtx);
+		break;
+	default:
+		return (EINVAL);
+	}
+
+	return (0);
+}
+
+static int
+mv_sdio_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
+{
+	struct mmc_host *host;
+
+	host = device_get_ivars(child);
+
+	switch (index) {
+	case MMCBR_IVAR_BUS_MODE:
+		host->ios.bus_mode = value;
+		break;
+	case MMCBR_IVAR_BUS_WIDTH:
+		host->ios.bus_width = value;
+		break;
+	case MMCBR_IVAR_CHIP_SELECT:
+		host->ios.chip_select = value;
+		break;
+	case MMCBR_IVAR_CLOCK:
+		host->ios.clock = value;
+		break;
+	case MMCBR_IVAR_MODE:
+		host->mode = value;
+		break;
+	case MMCBR_IVAR_OCR:
+		host->ocr = value;
+		break;
+	case MMCBR_IVAR_POWER_MODE:
+		host->ios.power_mode = value;
+		break;
+	case MMCBR_IVAR_VDD:
+		host->ios.vdd = value;
+		break;
+	case MMCBR_IVAR_TIMING:
+		host->ios.timing = value;
+		break;
+	case MMCBR_IVAR_CAPS:
+	case MMCBR_IVAR_HOST_OCR:
+	case MMCBR_IVAR_F_MIN:
+	case MMCBR_IVAR_F_MAX:
+	case MMCBR_IVAR_MAX_DATA:
+	default:
+		/* Instance variable not writable. */
+		return (EINVAL);
+	}
+
+	return (0);
+}
+
diff --git a/sys/arm/mv/mv_sdio.h b/sys/arm/mv/mv_sdio.h
new file mode 100644
index 0000000..b54b59d
--- /dev/null
+++ b/sys/arm/mv/mv_sdio.h
@@ -0,0 +1,173 @@
+/*
+ *  Copyright (C) 2008 Marvell Semiconductors, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef _MVSDMMC_INCLUDE
+#define _MVSDMMC_INCLUDE
+
+
+#define MVSDMMC_DMA_SIZE			65536
+
+
+
+/*
+ * The base MMC clock rate
+ */
+
+#define MVSDMMC_CLOCKRATE_MIN			100000
+#define MVSDMMC_CLOCKRATE_MAX			50000000
+
+#define MVSDMMC_BASE_FAST_CLOCK			200000000
+
+
+/*
+ * SDIO register
+ */
+
+#define MV_SDIO_DMA_ADDRL       0x000 
+#define MV_SDIO_DMA_ADDRH       0x004
+#define MV_SDIO_BLK_SIZE				0x008
+#define MV_SDIO_BLK_COUNT				0x00c
+#define MV_SDIO_CMD             0x01c
+#define MV_SDIO_CMD_ARGL        0x010
+#define MV_SDIO_CMD_ARGH        0x014
+#define MV_SDIO_XFER    				0x018
+#define MV_SDIO_HOST_SR         0x048
+#define MV_SDIO_HOST_CR         0x050
+#define MV_SDIO_SW_RESET        0x05c
+#define MV_SDIO_IRQ_SR		      0x060
+#define MV_SDIO_EIRQ_SR         0x064
+#define MV_SDIO_IRQ_SR_EN       0x068
+#define MV_SDIO_EIRQ_SR_EN      0x06c
+#define MV_SDIO_IRQ_EN          0x070
+#define MV_SDIO_EIRQ_EN         0x074
+#define MV_SDIO_AUTOCMD12_ARGL	0x084
+#define MV_SDIO_AUTOCMD12_ARGH	0x088
+#define MV_SDIO_AUTOCMD12			  0x08c
+#define MV_SDIO_CLK_DIV				  0x128
+#define MV_SDIO_FIFO     0xa2100 /* FIXME!!! */
+
+#define MV_SDIO_RSP(i)			  (0x020 + ((i)<<2))
+#define MV_SDIO_AUTOCMD12_RSP(i)	(0x090 + ((i)<<2))
+
+/*
+ * SDIO Status-Register
+ */
+#define MV_SDIO_HOST_SR_CARD_BUSY (1<<1)
+#define MV_SDIO_HOST_SR_FIFO_EMPTY (1<<13)
+
+
+
+/*
+ * SDIO_CMD
+ */
+#define MV_SDIO_CMD_RSP_NONE			(0 << 0)
+#define MV_SDIO_CMD_RSP_136			(1 << 0)
+#define MV_SDIO_CMD_RSP_48				(2 << 0)
+#define MV_SDIO_CMD_RSP_48_BUSY			(3 << 0)
+#define MV_SDIO_CMD_DATA_CRC16 (1<<2)
+#define MV_SDIO_CMD_CRC7 (1<<3)
+#define MV_SDIO_CMD_INDEX_CHECK (1<<4)
+#define MV_SDIO_CMD_DATA_PRESENT (1<<5)
+#define MV_SDIO_CMD_UNEXPECTED_RSP (1<<7)
+#define MV_SDIO_CMD_INDEX(x) ( (x) << 8 )
+
+
+/*
+ * SDIO_XFER_MODE
+ */
+#define MV_SDIO_XFER_STOP_CLK			(1 << 5)
+#define MV_SDIO_XFER_TO_HOST			(1 << 4)
+#define MV_SDIO_XFER_PIO          (1 << 3)
+#define MV_SDIO_XFER_AUTOCMD12		(1 << 2)
+#define MV_SDIO_XFER_SW_WR_EN     (1 << 1)
+
+/*
+ * SDIO_HOST_CTRL
+ */
+#define MV_SDIO_HOST_CR_PUSHPULL  (1 <<  0)
+#define MV_SDIO_HOST_CR_MMC       (3 <<  1)
+#define MV_SDIO_HOST_CR_BE        (1 <<  3)
+#define MV_SDIO_HOST_CR_4BIT      (1 <<  9)
+#define MV_SDIO_HOST_CR_HIGHSPEED (1 << 10)
+
+#define MV_SDIO_HOST_CR_TMOVAL(x) ((x) << 11)
+#define MV_SDIO_HOST_CR_TMO       ( 1 << 15 ) 
+
+/*
+ * NORmal status bits
+ */
+
+
+#define MV_SDIO_IRQ_ERR            (1<<15)
+#define MV_SDIO_IRQ_UNEXPECTED_RSP (1<<14)
+#define MV_SDIO_IRQ_AUTOCMD12      (1<<13)
+#define MV_SDIO_IRQ_SUSPENSE_ON_IRQ_EN (1<<12)
+#define MV_SDIO_IRQ_IMB_FIFO_WORD_AVAIL (1<<11)
+#define MV_SDIO_IRQ_IMB_FIFO_WORD_FILLED (1<<10)
+#define MV_SDIO_IRQ_READ_WAIT (1<<9)
+#define MV_SDIO_IRQ_CARD_EVENT (1<<8)
+#define MV_SDIO_IRQ_RX_FULL (1<<5)
+#define MV_SDIO_IRQ_TX_EMPTY (1<<4)
+#define MV_SDIO_IRQ_DMA (1<<3)
+#define MV_SDIO_IRQ_BLOCK_GAP (1<<2)
+#define MV_SDIO_IRQ_XFER (1<<1)
+#define MV_SDIO_IRQ_CMD (1<<0)
+
+#define MV_SDIO_IRQ_ALL (MV_SDIO_IRQ_CMD | MV_SDIO_IRQ_XFER | MV_SDIO_IRQ_BLOCK_GAP | MV_SDIO_IRQ_DMA | MV_SDIO_IRQ_RX_FULL | MV_SDIO_IRQ_TX_EMPTY | MV_SDIO_IRQ_CARD_EVENT | MV_SDIO_IRQ_READ_WAIT | MV_SDIO_IRQ_IMB_FIFO_WORD_FILLED | MV_SDIO_IRQ_IMB_FIFO_WORD_AVAIL | MV_SDIO_IRQ_SUSPENSE_ON_IRQ_EN | MV_SDIO_IRQ_AUTOCMD12 | MV_SDIO_IRQ_UNEXPECTED_RSP | MV_SDIO_IRQ_ERR )
+
+//#define MV_SDIO_IRQ_SR 
+
+
+/*
+ * ERR status bits
+ */
+#define MV_SDIO_EIRQ_CRC_STAT     (1<<14)
+#define MV_SDIO_EIRQ_CRC_STARTBIT (1<<13)
+#define MV_SDIO_EIRQ_CRC_ENDBIT   (1<<12)
+#define MV_SDIO_EIRQ_RSP_TBIT     (1<<11)
+#define MV_SDIO_EIRQ_XFER_SIZE    (1<<10)
+#define MV_SDIO_EIRQ_CMD_STARTBIT (1<<9)
+#define MV_SDIO_EIRQ_AUTOCMD12    (1<<8)
+#define MV_SDIO_EIRQ_DATA_ENDBIT  (1<<6)
+#define MV_SDIO_EIRQ_DATA_CRC16   (1<<5)
+#define MV_SDIO_EIRQ_DATA_TMO     (1<<4)
+#define MV_SDIO_EIRQ_CMD_INDEX    (1<<3)
+#define MV_SDIO_EIRQ_CMD_ENDBIT   (1<<2)
+#define MV_SDIO_EIRQ_CMD_CRC7     (1<<1)
+#define MV_SDIO_EIRQ_CMD_TMO      (1<<0)
+
+#define MV_SDIO_EIRQ_ALL (MV_SDIO_EIRQ_CMD_TMO | \
+                          MV_SDIO_EIRQ_CMD_CRC7 | \
+                          MV_SDIO_EIRQ_CMD_ENDBIT | \
+                          MV_SDIO_EIRQ_CMD_INDEX | \
+                          MV_SDIO_EIRQ_DATA_TMO | \
+                          MV_SDIO_EIRQ_DATA_CRC16 | \
+                          MV_SDIO_EIRQ_DATA_ENDBIT | \
+                          MV_SDIO_EIRQ_AUTOCMD12 | \
+                          MV_SDIO_EIRQ_CMD_STARTBIT |\
+                          MV_SDIO_EIRQ_XFER_SIZE |\
+                          MV_SDIO_EIRQ_RSP_TBIT |\
+                          MV_SDIO_EIRQ_CRC_ENDBIT |\
+                          MV_SDIO_EIRQ_CRC_STARTBIT |\
+                          MV_SDIO_EIRQ_CRC_STAT)
+
+/* AUTOCMD12 register values */
+#define MV_SDIO_AUTOCMD12_BUSY_CHECK (1<<0)
+#define MV_SDIO_AUTOCMD12_INDEX_CHECK (1<<1)
+#define MV_SDIO_AUTOCMD12_INDEX(x) (x<<8)
+
+/* Software reset register */
+#define MV_SDIO_SW_RESET_ALL (1<<8)
+
+/* */
+#define MV_SDIO_SIG_CD 1
+#define MV_SDIO_SIG_WP 2
+
+#endif /* _MVSDMMC_INCLUDE */
+
diff --git a/sys/boot/uboot/common/main.c b/sys/boot/uboot/common/main.c
index 82c86b2..0a5b368 100644
--- a/sys/boot/uboot/common/main.c
+++ b/sys/boot/uboot/common/main.c
@@ -122,6 +122,7 @@ main(void)
 	struct api_signature *sig = NULL;
 	int i;
 	struct open_file f;
+	char *ub_currdev;
 
 	if (!api_search_sig(&sig))
 		return (-1);
@@ -166,6 +167,7 @@ main(void)
 	printf("(%s, %s)\n", bootprog_maker, bootprog_date);
 	meminfo();
 
+	ub_currdev = ub_env_get("currdev");
 	/*
 	 * March through the device switch probing for things.
 	 */
@@ -198,8 +200,13 @@ main(void)
 	if (devsw[i] == NULL)
 		panic("No boot device found!");
 
-	env_setenv("currdev", EV_VOLATILE, uboot_fmtdev(&currdev),
-	    uboot_setcurrdev, env_nounset);
+	if (ub_currdev) {
+		env_setenv("currdev", EV_VOLATILE, ub_currdev,
+		    uboot_setcurrdev, env_nounset);
+	} else {
+		env_setenv("currdev", EV_VOLATILE, uboot_fmtdev(&currdev),
+		    uboot_setcurrdev, env_nounset);
+	}
 	env_setenv("loaddev", EV_VOLATILE, uboot_fmtdev(&currdev),
 	    env_noset, env_nounset);
 
diff --git a/sys/dev/mmc/mmc.c b/sys/dev/mmc/mmc.c
index f101e65..53c51de 100644
--- a/sys/dev/mmc/mmc.c
+++ b/sys/dev/mmc/mmc.c
@@ -67,15 +67,38 @@ __FBSDID("$FreeBSD$");
 #include <dev/mmc/mmcreg.h>
 #include <dev/mmc/mmcbrvar.h>
 #include <dev/mmc/mmcvar.h>
+#include <dev/mmc/mmcioreg.h>
+
 #include "mmcbr_if.h"
 #include "mmcbus_if.h"
 
+/* CIS structure of SDIO card */
+struct sdio_function {
+	int		number;
+	uint8_t		cis1_major;
+	uint8_t		cis1_minor;
+	uint16_t	manufacturer;
+	uint16_t	product;
+	uint16_t	max_blksize;
+	uint8_t		max_tran_speed;	/* only for func0 */
+	STAILQ_ENTRY(sdio_function) sdiof_list;
+};
+
 struct mmc_softc {
 	device_t dev;
 	struct mtx sc_mtx;
 	struct intr_config_hook config_intrhook;
 	device_t owner;
 	uint32_t last_rca;
+	uint32_t __sdio_rca; /* XXX Temp; for testng only */
+	uint32_t __sdio_cis1_info;
+	uint8_t sdio_nfunc;
+	u_char sdio_bus_width;
+	uint8_t sdio_support_hs;
+	u_char sdio_timing;
+	uint32_t sdio_tran_speed;
+	struct sdio_function sdio_func0;
+	STAILQ_HEAD(, sdio_function) sdiof_head;
 };
 
 /*
@@ -102,6 +125,7 @@ struct mmc_ivars {
 	uint32_t hs_tran_speed;	/* Max speed in high speed mode */
 	uint32_t erase_sector;	/* Card native erase sector size */
 	char card_id_string[64];/* Formatted CID info (serial, MFG, etc) */
+	struct sdio_function *sdiof;
 };
 
 #define CMD_RETRIES	3
@@ -159,10 +183,16 @@ static uint32_t mmc_get_bits(uint32_t *bits, int bit_len, int start,
     int size);
 static int mmc_highest_voltage(uint32_t ocr);
 static void mmc_idle_cards(struct mmc_softc *sc);
+static int mmc_io_func_enable(struct mmc_softc *sc, uint32_t fn);
+static uint8_t mmc_io_read_1(struct mmc_softc *sc, uint32_t fn, uint32_t adr);
+static int mmc_io_rw_direct(struct mmc_softc *sc, int wr, uint32_t fn,
+    uint32_t adr, uint8_t *data);
 static void mmc_ms_delay(int ms);
 static void mmc_log_card(device_t dev, struct mmc_ivars *ivar, int newcard);
 static void mmc_power_down(struct mmc_softc *sc);
 static void mmc_power_up(struct mmc_softc *sc);
+static int mmc_probe_sdio(struct mmc_softc *sc, uint32_t ocr, uint32_t *rocr,
+    uint8_t *nfunc, uint8_t *mem_present);
 static void mmc_rescan_cards(struct mmc_softc *sc);
 static void mmc_scan(struct mmc_softc *sc);
 static int mmc_sd_switch(struct mmc_softc *sc, uint8_t mode, uint8_t grp,
@@ -220,6 +250,8 @@ mmc_attach(device_t dev)
 	sc->dev = dev;
 	MMC_LOCK_INIT(sc);
 
+	STAILQ_INIT(&sc->sdiof_head);
+
 	/* We'll probe and attach our children later, but before / mount */
 	sc->config_intrhook.ich_func = mmc_delayed_attach;
 	sc->config_intrhook.ich_arg = sc;
@@ -470,6 +502,7 @@ mmc_wait_for_command(struct mmc_softc *sc, uint32_t opcode,
 	return (0);
 }
 
+/* CMD0 */
 static void
 mmc_idle_cards(struct mmc_softc *sc)
 {
@@ -494,6 +527,7 @@ mmc_idle_cards(struct mmc_softc *sc)
 	mmc_ms_delay(1);
 }
 
+/* CMD41 -> CMD55 */
 static int
 mmc_send_app_op_cond(struct mmc_softc *sc, uint32_t ocr, uint32_t *rocr)
 {
@@ -521,6 +555,7 @@ mmc_send_app_op_cond(struct mmc_softc *sc, uint32_t ocr, uint32_t *rocr)
 	return (err);
 }
 
+/* CMD1 */
 static int
 mmc_send_op_cond(struct mmc_softc *sc, uint32_t ocr, uint32_t *rocr)
 {
@@ -548,6 +583,7 @@ mmc_send_op_cond(struct mmc_softc *sc, uint32_t ocr, uint32_t *rocr)
 	return (err);
 }
 
+/* CMD8 */
 static int
 mmc_send_if_cond(struct mmc_softc *sc, uint8_t vhs)
 {
@@ -600,6 +636,7 @@ mmc_power_down(struct mmc_softc *sc)
 	mmcbr_update_ios(dev);
 }
 
+/* CMD7 */
 static int
 mmc_select_card(struct mmc_softc *sc, uint16_t rca)
 {
@@ -1042,6 +1079,7 @@ mmc_app_decode_sd_status(uint32_t *raw_sd_status,
 	sd_status->erase_offset = mmc_get_bits(raw_sd_status, 512, 400, 2);
 }
 
+/* CMD2 */
 static int
 mmc_all_send_cid(struct mmc_softc *sc, uint32_t *rawcid)
 {
@@ -1162,6 +1200,7 @@ mmc_set_relative_addr(struct mmc_softc *sc, uint16_t resp)
 	return (err);
 }
 
+/* CMD3 */
 static int
 mmc_send_relative_addr(struct mmc_softc *sc, uint32_t *resp)
 {
@@ -1177,6 +1216,7 @@ mmc_send_relative_addr(struct mmc_softc *sc, uint32_t *resp)
 	return (err);
 }
 
+/* CMD13 */
 static int
 mmc_send_status(struct mmc_softc *sc, uint16_t rca, uint32_t *status)
 {
@@ -1223,6 +1263,384 @@ mmc_log_card(device_t dev, struct mmc_ivars *ivar, int newcard)
 	    ivar->read_only ? ", read-only" : "");
 }
 
+/*
+ * Enables the given function on SDIO card.
+ */
+static int
+mmc_io_func_enable(struct mmc_softc *sc, uint32_t fn)
+{
+	int err, i;
+	uint8_t funcs;
+
+	if (fn > sc->sdio_nfunc) {
+		device_printf(sc->dev, "Invalid function to enable: %d\n", fn);
+		return (MMC_ERR_INVALID);
+	}
+
+	funcs = mmc_io_read_1(sc, 0, SD_IO_CCCR_FN_READY);
+
+	funcs |= 1 << fn;
+	err = mmc_io_rw_direct(sc, 1, 0, SD_IO_CCCR_FN_ENABLE, &funcs);
+	if (err != MMC_ERR_NONE) {
+		device_printf(sc->dev, "Error writing SDIO func enable %d\n", err);
+		return (err);
+	}
+
+	funcs = 0;
+	for(i=0; i < 10; i++) {
+		funcs = mmc_io_read_1(sc, 0, SD_IO_CCCR_FN_READY);
+
+		if (funcs & (1 << fn))
+			return 0;
+		mmc_ms_delay(10);
+	}
+
+	device_printf(sc->dev, "Cannot enable function %d!\n", fn);
+	return (MMC_ERR_FAILED);
+}
+
+/* CMD52 */
+static int
+mmc_io_rw_direct(struct mmc_softc *sc, int wr, uint32_t fn, uint32_t adr,
+		      uint8_t *data)
+{
+	struct mmc_command cmd;
+	int err;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.opcode = SD_IO_RW_DIRECT;
+	cmd.arg = SD_IO_RW_FUNC(fn) | SD_IO_RW_ADR(adr);
+	if (wr)
+		cmd.arg |= SD_IO_RW_WR | SD_IO_RW_RAW | SD_IO_RW_DAT(*data);
+	cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
+	cmd.data = NULL;
+
+	err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES);
+	if (err)
+		return (err);
+	if (cmd.error)
+		return (cmd.error);
+
+	if (cmd.resp[0] & R5_COM_CRC_ERROR)
+		return (MMC_ERR_BADCRC);
+	if (cmd.resp[0] & (R5_ILLEGAL_COMMAND | R5_FUNCTION_NUMBER))
+		return (MMC_ERR_INVALID);
+	if (cmd.resp[0] & R5_OUT_OF_RANGE)
+		return (MMC_ERR_FAILED);
+
+	/* Just for information... */
+	if (R5_IO_CURRENT_STATE(cmd.resp[0]) != 1)
+		printf("!!! SDIO state %d\n", R5_IO_CURRENT_STATE(cmd.resp[0]));
+
+	if (cmd.resp[0] & R5_ERROR)
+		printf("An error was detected!\n");
+
+	if (cmd.resp[0] & R5_COM_CRC_ERROR)
+		printf("A CRC error was detected!\n");
+
+	*data = (uint8_t) (cmd.resp[0] & 0xff);
+	return (MMC_ERR_NONE);
+}
+
+/* CMD53 */
+static int
+mmc_io_rw_extended(struct mmc_softc *sc, int wr, uint32_t fn, uint32_t adr,
+		 uint8_t *datap, size_t datalen, uint8_t incr, uint8_t blks)
+{
+	int err;
+	struct mmc_command cmd;
+	struct mmc_data data;
+
+	memset(&cmd, 0, sizeof(cmd));
+	memset(&data, 0, sizeof(data));
+	memset(datap, 0, datalen);
+
+	cmd.opcode = SD_IO_RW_EXTENDED;
+	cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
+	cmd.arg = SD_IO_RW_FUNC(fn);
+	cmd.arg |= SD_IO_RW_ADR(adr);
+	if (blks)
+		cmd.arg |= SD_IOE_RW_BLK | SD_IOE_RW_LEN(blks);
+	else
+		cmd.arg |= SD_IOE_RW_LEN(datalen);
+	if (wr)
+		cmd.arg |= SD_IO_RW_WR;
+	if (incr)
+		cmd.arg |= SD_IO_RW_INCR;
+	cmd.data = &data;
+
+	data.data = datap;
+	data.len = datalen;
+	data.flags = wr ? MMC_DATA_WRITE : MMC_DATA_READ;
+
+	err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES);
+
+	if (err)
+		return (err);
+	if (cmd.error)
+		return (cmd.error);
+
+	if (cmd.resp[0] & R5_COM_CRC_ERROR)
+		return (MMC_ERR_BADCRC);
+	if (cmd.resp[0] & (R5_ILLEGAL_COMMAND | R5_FUNCTION_NUMBER))
+		return (MMC_ERR_INVALID);
+	if (cmd.resp[0] & R5_OUT_OF_RANGE)
+		return (MMC_ERR_FAILED);
+
+	return (MMC_ERR_NONE);
+}
+
+static uint8_t
+mmc_io_read_1(struct mmc_softc *sc, uint32_t fn, uint32_t adr)
+{
+	int err;
+	uint8_t val = 0;
+
+	err = mmc_io_rw_direct(sc, 0, fn, adr, &val);
+	if (err) {
+		device_printf(sc->dev, "Err reading FN %d addr 0x%08X: %d",
+			      fn, adr, err);
+		return (0xff);
+	}
+	return val;
+}
+
+/*
+ * Parse Card Information Structure of the SDIO card.
+ * Both Function 0 CIS and Function 1-7 CIS are supported.
+ */
+static int
+mmc_io_parse_cis(struct mmc_softc *sc, uint8_t func, uint32_t cisptr, struct sdio_function *sdio_func)
+{
+	uint32_t tmp;
+
+	uint8_t tuple_id, tuple_len, func_id;
+	uint32_t addr, maninfo_p;
+
+	char *cis1_info[4];
+	int start, i, ch, count;
+	char cis1_info_buf[256];
+
+	sdio_func->number  = func;
+
+	cis1_info[0] = NULL;
+	cis1_info[1] = NULL;
+	cis1_info[2] = NULL;
+	cis1_info[3] = NULL;
+	memset(cis1_info_buf, 0, 256);
+
+	tmp = 0;
+	addr = cisptr;
+
+	/*
+	 * XXX Some parts of this code are taken
+	 * from sys/dev/pccard/pccard_cis.c.
+	 * Need to think about making it more abstract.
+	 */
+	do {
+		tuple_id = mmc_io_read_1(sc, 0, addr++);
+		if (tuple_id == SD_IO_CISTPL_END)
+			break;
+		tuple_len = mmc_io_read_1(sc, 0, addr++);
+		if (tuple_len == 0 && tuple_id != 0x00) {
+			device_printf(sc->dev,
+			    "Parse error: 0-length tuple %02X\n", tuple_id);
+			break;
+		}
+
+		switch (tuple_id) {
+		case SD_IO_CISTPL_VERS_1:
+			maninfo_p = addr;
+
+			sdio_func->cis1_major = mmc_io_read_1(sc, 0, maninfo_p);
+			sdio_func->cis1_minor = mmc_io_read_1(sc, 0, maninfo_p + 1);
+
+			/*
+			 * XXX Temp; use this to test if multi-byte read from
+			 * cis1_info will also return crap
+			 */
+			sc->__sdio_cis1_info = maninfo_p + 2;
+			for (count = 0, start = 0, i = 0;
+			    (count < 4) && ((i + 4) < 256); i++) {
+				ch = mmc_io_read_1(sc, 0, maninfo_p + 2 + i);
+				if (ch == 0xff)
+					break;
+				cis1_info_buf[i] = ch;
+				if (ch == 0) {
+					cis1_info[count] =
+					    cis1_info_buf + start;
+					start = i + 1;
+					count++;
+				}
+			}
+			device_printf(sc->dev, "Read using 1-byte read:\n");
+			hexdump(cis1_info_buf, 256, NULL, 0);
+
+			device_printf(sc->dev, "*** Info[0]: %s\n", cis1_info[0]);
+			device_printf(sc->dev, "*** Info[1]: %s\n", cis1_info[1]);
+			device_printf(sc->dev, "*** Info[2]: %s\n", cis1_info[2]);
+			device_printf(sc->dev, "*** Info[3]: %s\n", cis1_info[3]);
+			break;
+
+		case SD_IO_CISTPL_MANFID:
+			if (tuple_len < 4) {
+				device_printf(sc->dev, "MANFID is too short\n");
+				break;
+			}
+			sdio_func->manufacturer = mmc_io_read_1(sc, 0, addr);
+			sdio_func->manufacturer |= mmc_io_read_1(sc, 0, addr + 1) << 8;
+
+			sdio_func->product = mmc_io_read_1(sc, 0, addr + 2);
+			sdio_func->product |= mmc_io_read_1(sc, 0, addr + 3) << 8;
+			break;
+
+		case SD_IO_CISTPL_FUNCID:
+			/* Function ID for SDIO devices is always 0x0C */
+			if (tuple_len < 1) {
+				device_printf(sc->dev, "FUNCID is too short\n");
+				break;
+			}
+			func_id = mmc_io_read_1(sc, 0, addr);
+			if (func_id != 0x0C)
+				device_printf(sc->dev, "func_id non-std: %d\n", func_id);
+			break;
+
+		case SD_IO_CISTPL_FUNCE:
+			if (tuple_len < 4) {
+				device_printf(sc->dev, "FUNCE is too short\n");
+				break;
+			}
+			uint8_t ext_data_type = mmc_io_read_1(sc, 0, addr);
+
+			if (func == 0) {
+				if (ext_data_type != 0x0)
+					device_printf(sc->dev,
+					    "funce for func 0 non-std: %d\n",
+					    ext_data_type);
+				sdio_func->max_blksize =
+				    mmc_io_read_1(sc, 0, addr + 1);
+				sdio_func->max_blksize |=
+				    mmc_io_read_1(sc, 0, addr + 2) << 8;
+				sdio_func->max_tran_speed =
+				    mmc_io_read_1(sc, 0, addr + 3);
+				uint8_t max_tran_rate =
+				    sdio_func->max_tran_speed & 0x7;
+				uint8_t timecode =
+				    (sdio_func->max_tran_speed >> 3) & 0xF;
+
+				device_printf(sc->dev,
+				    "*** Max tran speed: %02X (unit %d, time value code %d\n",
+				    sdio_func->max_tran_speed, max_tran_rate, timecode);
+			} else {
+				if (ext_data_type != 0x1)
+					device_printf(sc->dev,
+					    "funce for func 0 non-std: %d\n",
+					    ext_data_type);
+				sdio_func->max_blksize =
+				    mmc_io_read_1(sc, 0, addr + 0x0c);
+				sdio_func->max_blksize |=
+				    mmc_io_read_1(sc, 0, addr + 0x0d) << 8;
+
+			}
+
+			break;
+
+		default:
+			device_printf(sc->dev,
+			    "*** Skipping tuple ID %02X len %02X\n",
+			    tuple_id, tuple_len);
+			break;
+		}
+
+		addr += tuple_len;
+		tmp++;
+	} while (tuple_id != SD_IO_CISTPL_END && tmp < 10);
+
+	return 0;
+}
+
+/*
+ * Parse Card Common Control Register of the SDIO card
+ */
+static int
+mmc_io_parse_cccr(struct mmc_softc *sc)
+{
+	uint32_t cisptr = 0;
+
+	cisptr =  mmc_io_read_1(sc, 0, SD_IO_CCCR_CISPTR);
+	cisptr |= mmc_io_read_1(sc, 0, SD_IO_CCCR_CISPTR + 1) << 8;
+	cisptr |= mmc_io_read_1(sc, 0, SD_IO_CCCR_CISPTR + 2) << 16;
+
+	if (cisptr < SD_IO_CIS_START ||
+	    cisptr > SD_IO_CIS_START +  SD_IO_CIS_SIZE) {
+		device_printf(sc->dev, "Bad CIS pointer in CCCR: %08X\n", cisptr);
+		return (-1);
+	}
+
+	return mmc_io_parse_cis(sc, 0, cisptr, &sc->sdio_func0);
+}
+
+/*
+ * Parse Function Basic Register of the given function
+ */
+static int
+mmc_io_parse_fbr(struct mmc_softc *sc, uint8_t func)
+{
+	uint32_t fbr_addr, cisptr;
+
+	fbr_addr = SD_IO_FBR_START * func + 0x9;
+	cisptr =  mmc_io_read_1(sc, 0, fbr_addr);
+	cisptr |= mmc_io_read_1(sc, 0, fbr_addr + 1) << 8;
+	cisptr |= mmc_io_read_1(sc, 0, fbr_addr + 2) << 16;
+
+	if (cisptr < SD_IO_CIS_START ||
+	    cisptr > SD_IO_CIS_START +  SD_IO_CIS_SIZE) {
+		device_printf(sc->dev, "Bad CIS pointer in FBR: %08X\n", cisptr);
+		return (-1);
+	}
+
+	struct sdio_function *f = malloc(sizeof(struct sdio_function), M_DEVBUF, M_WAITOK);
+	STAILQ_INSERT_TAIL(&sc->sdiof_head, f, sdiof_list);
+
+	return mmc_io_parse_cis(sc, func, cisptr, f);
+}
+
+static void
+mmc_io_get_info(struct mmc_softc *sc)
+{
+	sc->sdio_bus_width = bus_width_1;
+	sc->sdio_support_hs = 0;
+
+	uint8_t cardcap = mmc_io_read_1(sc, 0, SD_IO_CCCR_CARDCAP);
+	uint8_t hs_info = mmc_io_read_1(sc, 0, SD_IO_CCCR_CISPTR + 0x13);
+
+	/*
+	 * If the card is a full-speed card, it supports 4-bit bus width.
+	 * If it is low-speed, we check 4BLS to determine if it
+	 * supports 4-bit width
+	 */
+	if (((cardcap & (1 << 6)) && (cardcap & (1 << 7))) ||
+	    ((cardcap & (1 << 6)) == 0))
+		sc->sdio_bus_width = bus_width_4;
+
+	sc->sdio_support_hs = hs_info & (1 << 0);
+}
+
+/* Set bus width for SDIO card */
+static int
+mmc_io_set_bus_width(struct mmc_softc *sc, int width)
+{
+	uint8_t busctrl = mmc_io_read_1(sc, 0, SD_IO_CCCR_BUS_WIDTH);
+
+	busctrl |= width == bus_width_4 ? CCCR_BUS_WIDTH_4 : 0;
+
+	if (mmc_debug)
+		device_printf(sc->dev, "Setting SDIO bus width to %d bits\n",
+		    width == bus_width_4 ? 4 : 1);
+
+	return mmc_io_rw_direct(sc, 1, 0, SD_IO_CCCR_BUS_WIDTH, &busctrl);
+}
+
 static void
 mmc_discover_cards(struct mmc_softc *sc)
 {
@@ -1233,11 +1651,87 @@ mmc_discover_cards(struct mmc_softc *sc)
 	device_t child;
 	uint16_t rca = 2;
 	u_char switch_res[64];
+	uint8_t nfunc, mem_present;
 
 	if (bootverbose || mmc_debug)
 		device_printf(sc->dev, "Probing cards\n");
 	while (1) {
-		err = mmc_all_send_cid(sc, raw_cid);
+		/*
+		 * Probe SDIO first, because SDIO cards don't have
+		 * a CID register and won't respond to the CMD2
+		 */
+		mmc_idle_cards(sc);
+		err = mmc_probe_sdio(sc, 0, NULL, &nfunc, &mem_present);
+		sc->sdio_nfunc = nfunc;
+		if (err != MMC_ERR_NONE && err != MMC_ERR_TIMEOUT) {
+			device_printf(sc->dev, "Error probing SDIO %d\n", err);
+			break;
+		}
+
+		/* The card answered OK -> SDIO */
+		if (err == MMC_ERR_NONE) {
+			device_printf(sc->dev, "Detected SDIO card\n");
+			mmc_send_relative_addr(sc, &resp); /* CMD3 */
+			uint16_t rca = resp >> 16;
+			err = mmc_select_card(sc, rca); /* CMD7 */
+			sc->__sdio_rca = rca; /* XXX Temp; for testing only */
+			if (err != MMC_ERR_NONE) {
+				device_printf(sc->dev, "Error selecting SDIO %d\n", err);
+				break;
+			}
+
+			device_printf(sc->dev, "Get card info\n");
+			mmc_io_parse_cccr(sc);
+			mmc_io_get_info(sc);
+			for(i=1; i <= nfunc; i++) {
+				device_printf(sc->dev,
+				    "Get info for function %d\n", i);
+				mmc_io_parse_fbr(sc, i);
+				mmc_io_func_enable(sc, i);
+			}
+
+			device_printf(sc->dev, "=== Functions ===\n");
+			struct sdio_function *f;
+
+			STAILQ_FOREACH(f, &sc->sdiof_head, sdiof_list)
+				device_printf(sc->dev,
+				    "FN %d, vendor %04X, product %04X; blksize %02X\n",
+				    f->number, f->manufacturer, f->product, f->max_blksize);
+
+			/*
+			 * Only set 4-bit width if both the host and the card
+			 * support it.
+			 * The card starts in 1-bit mode by default.
+			 */
+			if (mmcbr_get_caps(sc->dev) & MMC_CAP_4_BIT_DATA &&
+			    sc->sdio_bus_width == bus_width_4) {
+				mmc_io_set_bus_width(sc, sc->sdio_bus_width);
+				mmcbr_set_bus_width(sc->dev, sc->sdio_bus_width);
+			}
+
+			/* Set high speed mode if host and card support it */
+			if (mmcbr_get_caps(sc->dev) & MMC_CAP_HSPEED &&
+			    sc->sdio_support_hs) {
+				device_printf(sc->dev, "Activating high-speed mode");
+				uint8_t hs_info = 1;
+				err = mmc_io_rw_direct(sc, 1, 0,
+				    SD_IO_CCCR_CISPTR + 0x13, &hs_info);
+				if (err != MMC_ERR_NONE) {
+					device_printf(sc->dev, "Error setting HS mode%d\n", err);
+					return;
+				}
+				sc->sdio_timing = bus_timing_hs;
+				sc->sdio_tran_speed = 50 * 1000 * 1000;
+			} else {
+				sc->sdio_tran_speed = 25 * 1000 * 1000;
+				sc->sdio_timing = bus_timing_normal;
+			}
+
+			if (!mem_present)
+				return;
+		}
+
+		err = mmc_all_send_cid(sc, raw_cid); /* Command 2 */
 		if (err == MMC_ERR_TIMEOUT)
 			break;
 		if (err != MMC_ERR_NONE) {
@@ -1491,9 +1985,45 @@ mmc_delete_cards(struct mmc_softc *sc)
 	return (0);
 }
 
+/* CMD 5 */
+static int
+mmc_probe_sdio(struct mmc_softc *sc, uint32_t ocr, uint32_t *rocr, uint8_t *nfunc, uint8_t *mem_present) {
+	struct mmc_command cmd;
+	int err = MMC_ERR_NONE, i;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.opcode = IO_SEND_OP_COND;
+	cmd.arg = 0;
+	cmd.flags = MMC_RSP_R4;
+	cmd.data = NULL;
+
+	for (i = 0; i < 1000; i++) {
+		err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES);
+		if (err != MMC_ERR_NONE)
+			break;
+		if ((cmd.resp[0] & MMC_OCR_CARD_BUSY) ||
+		    (ocr & MMC_OCR_VOLTAGE) == 0)
+			break;
+		err = MMC_ERR_TIMEOUT;
+		mmc_ms_delay(10);
+	}
+
+	if (err == MMC_ERR_NONE) {
+		if (rocr)
+			*rocr = cmd.resp[0];
+		if (nfunc)
+			*nfunc = SD_IO_OCR_NUM_FUNCTIONS(cmd.resp[0]);
+		if (mem_present)
+			*mem_present = cmd.resp[0] >> 27 & 0x1;
+	}
+
+	return (err);
+}
+
 static void
 mmc_go_discovery(struct mmc_softc *sc)
 {
+	uint8_t nfunc, mem_present;
 	uint32_t ocr;
 	device_t dev;
 	int err;
@@ -1509,17 +2039,24 @@ mmc_go_discovery(struct mmc_softc *sc)
 		if (bootverbose || mmc_debug)
 			device_printf(sc->dev, "Probing bus\n");
 		mmc_idle_cards(sc);
-		err = mmc_send_if_cond(sc, 1);
+		err = mmc_send_if_cond(sc, 1);  /* SD_SEND_IF_COND = 8 */
 		if ((bootverbose || mmc_debug) && err == 0)
 			device_printf(sc->dev, "SD 2.0 interface conditions: OK\n");
-		if (mmc_send_app_op_cond(sc, 0, &ocr) != MMC_ERR_NONE) {
+		if (mmc_probe_sdio(sc, 0, &ocr, &nfunc, &mem_present) == MMC_ERR_NONE) {
+			device_printf(dev, "SDIO probe OK (OCR: 0x%08x, %d functions, memory: %d)\n", ocr, nfunc, mem_present);
+			if (nfunc > 0 && mem_present) {
+				device_printf(sc->dev, "SDIO combo cards are not supported yet");
+				return;
+			}
+		} else
+		if (mmc_send_app_op_cond(sc, 0, &ocr) != MMC_ERR_NONE) { /* retry 55 -> then 41 */
 			if (bootverbose || mmc_debug)
 				device_printf(sc->dev, "SD probe: failed\n");
 			/*
 			 * Failed, try MMC
 			 */
 			mmcbr_set_mode(dev, mode_mmc);
-			if (mmc_send_op_cond(sc, 0, &ocr) != MMC_ERR_NONE) {
+			if (mmc_send_op_cond(sc, 0, &ocr) != MMC_ERR_NONE) { /* command 1 */
 				if (bootverbose || mmc_debug)
 					device_printf(sc->dev, "MMC probe: failed\n");
 				ocr = 0; /* Failed both, powerdown. */
@@ -1553,9 +2090,11 @@ mmc_go_discovery(struct mmc_softc *sc)
 	 * Reselect the cards after we've idled them above.
 	 */
 	if (mmcbr_get_mode(dev) == mode_sd) {
-		err = mmc_send_if_cond(sc, 1);
-		mmc_send_app_op_cond(sc,
-		    (err ? 0 : MMC_OCR_CCS) | mmcbr_get_ocr(dev), NULL);
+		if (mem_present) {
+			err = mmc_send_if_cond(sc, 1); /* CMD 8 */
+			mmc_send_app_op_cond(sc, /* 41 -> 55 */
+			    (err ? 0 : MMC_OCR_CCS) | mmcbr_get_ocr(dev), NULL);
+		}
 	} else
 		mmc_send_op_cond(sc, mmcbr_get_ocr(dev), NULL);
 	mmc_discover_cards(sc);
@@ -1564,6 +2103,24 @@ mmc_go_discovery(struct mmc_softc *sc)
 	mmcbr_set_bus_mode(dev, pushpull);
 	mmcbr_update_ios(dev);
 	mmc_calculate_clock(sc);
+
+	/* XXX TESTING RW_EXTENDED */
+	mmc_select_card(sc, sc->__sdio_rca);
+
+	/* Try to do normal CMD52 that should work correctly */
+	uint8_t hs_info;
+	err = mmc_io_rw_direct(sc, 0, 0, SD_IO_CCCR_CISPTR + 0x13, &hs_info);
+	if (err)
+		device_printf(sc->dev, "HS INFO read err %d\n", err);
+
+	/* Now try actual command */
+	mmc_debug = 10;
+	uint8_t data[100];
+	err = mmc_io_rw_extended(sc, 0, 0, sc->__sdio_cis1_info, data, 100, 0, 0);
+	if (err)
+		device_printf(sc->dev, "Ext read err %d\n", err);
+	hexdump(data, 100, NULL, 0);
+
 	bus_generic_attach(dev);
 /*	mmc_update_children_sysctl(dev);*/
 }
@@ -1575,7 +2132,7 @@ mmc_calculate_clock(struct mmc_softc *sc)
 	int nkid, i, f_min, f_max;
 	device_t *kids;
 	struct mmc_ivars *ivar;
-	
+
 	f_min = mmcbr_get_f_min(sc->dev);
 	f_max = mmcbr_get_f_max(sc->dev);
 	max_dtr = max_hs_dtr = f_max;
@@ -1583,6 +2140,12 @@ mmc_calculate_clock(struct mmc_softc *sc)
 		max_timing = bus_timing_hs;
 	else
 		max_timing = bus_timing_normal;
+
+	if (sc->sdio_timing < max_timing)
+		max_timing = sc->sdio_timing;
+	if (sc->sdio_tran_speed < max_dtr)
+		max_dtr = sc->sdio_tran_speed;
+
 	if (device_get_children(sc->dev, &kids, &nkid) != 0)
 		panic("can't get children");
 	for (i = 0; i < nkid; i++) {
@@ -1735,3 +2298,4 @@ DRIVER_MODULE(mmc, at91_mci, mmc_driver, mmc_devclass, NULL, NULL);
 DRIVER_MODULE(mmc, sdhci_pci, mmc_driver, mmc_devclass, NULL, NULL);
 DRIVER_MODULE(mmc, sdhci_bcm, mmc_driver, mmc_devclass, NULL, NULL);
 DRIVER_MODULE(mmc, sdhci_fdt, mmc_driver, mmc_devclass, NULL, NULL);
+DRIVER_MODULE(mmc, sdio, mmc_driver, mmc_devclass, NULL, NULL);
diff --git a/sys/dev/mmc/mmcioreg.h b/sys/dev/mmc/mmcioreg.h
new file mode 100644
index 0000000..10e304b
--- /dev/null
+++ b/sys/dev/mmc/mmcioreg.h
@@ -0,0 +1,96 @@
+/*	$OpenBSD: sdmmc_ioreg.h,v 1.4 2007/06/02 01:48:37 uwe Exp $	*/
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe at 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_IOREG_H
+#define _SDMMC_IOREG_H
+
+/* SDIO commands */				/* response type */
+#define SD_IO_SEND_OP_COND		5	/* R4 */
+#define SD_IO_RW_DIRECT			52	/* R5 */
+#define SD_IO_RW_EXTENDED		53	/* R5? */
+
+/* CMD52 arguments */
+#define SD_ARG_CMD52_READ		(0<<31)
+#define SD_ARG_CMD52_WRITE		(1<<31)
+#define SD_ARG_CMD52_FUNC_SHIFT		28
+#define SD_ARG_CMD52_FUNC_MASK		0x7
+#define SD_ARG_CMD52_EXCHANGE		(1<<27)
+#define SD_ARG_CMD52_REG_SHIFT		9
+#define SD_ARG_CMD52_REG_MASK		0x1ffff
+#define SD_ARG_CMD52_DATA_SHIFT		0
+#define SD_ARG_CMD52_DATA_MASK		0xff
+#define SD_R5_DATA(resp)		((resp)[0] & 0xff)
+
+/* CMD53 arguments */
+#define SD_ARG_CMD53_READ		(0<<31)
+#define SD_ARG_CMD53_WRITE		(1<<31)
+#define SD_ARG_CMD53_FUNC_SHIFT		28
+#define SD_ARG_CMD53_FUNC_MASK		0x7
+#define SD_ARG_CMD53_BLOCK_MODE		(1<<27)
+#define SD_ARG_CMD53_INCREMENT		(1<<26)
+#define SD_ARG_CMD53_REG_SHIFT		9
+#define SD_ARG_CMD53_REG_MASK		0x1ffff
+#define SD_ARG_CMD53_LENGTH_SHIFT	0
+#define SD_ARG_CMD53_LENGTH_MASK	0x1ff
+#define SD_ARG_CMD53_LENGTH_MAX		64 /* XXX should be 511? */
+
+/* 48-bit response decoding (32 bits w/o CRC) */
+#define MMC_R4(resp)			((resp)[0])
+#define MMC_R5(resp)			((resp)[0])
+
+/* SD R4 response (IO OCR) */
+#define SD_IO_OCR_MEM_READY		(1<<31)
+#define SD_IO_OCR_NUM_FUNCTIONS(ocr)	(((ocr) >> 28) & 0x3)
+/* XXX big fat memory present "flag" because we don't know better */
+#define SD_IO_OCR_MEM_PRESENT		(0xf<<24)
+#define SD_IO_OCR_MASK			0x00fffff0
+
+/* Card Common Control Registers (CCCR) */
+#define SD_IO_CCCR_START		0x00000
+#define SD_IO_CCCR_SIZE			0x100
+#define SD_IO_CCCR_FN_ENABLE		0x02
+#define SD_IO_CCCR_FN_READY		0x03
+#define SD_IO_CCCR_INT_ENABLE		0x04
+#define SD_IO_CCCR_CTL			0x06
+#define  CCCR_CTL_RES			(1<<3)
+#define SD_IO_CCCR_BUS_WIDTH		0x07
+#define  CCCR_BUS_WIDTH_4		(1<<1)
+#define  CCCR_BUS_WIDTH_1		(1<<0)
+#define SD_IO_CCCR_CARDCAP		0x08
+#define SD_IO_CCCR_CISPTR		0x09 /* XXX 9-10, 10-11, or 9-12 */
+
+/* Function Basic Registers (FBR) */
+#define SD_IO_FBR_START			0x00100
+#define SD_IO_FBR_SIZE			0x00700
+
+/* Card Information Structure (CIS) */
+#define SD_IO_CIS_START			0x01000
+#define SD_IO_CIS_SIZE			0x17000
+
+/* CIS tuple codes (based on PC Card 16) */
+#define SD_IO_CISTPL_VERS_1		0x15
+#define SD_IO_CISTPL_MANFID		0x20
+#define SD_IO_CISTPL_FUNCID		0x21
+#define SD_IO_CISTPL_FUNCE		0x22
+#define SD_IO_CISTPL_END		0xff
+
+/* CISTPL_FUNCID codes */
+/* OpenBSD incorrectly defines 0x0c as FUNCTION_WLAN */
+/* #define SDMMC_FUNCTION_WLAN		0x0c */
+#endif
diff --git a/sys/dev/mmc/mmcreg.h b/sys/dev/mmc/mmcreg.h
index f454ddb..4b65d91 100644
--- a/sys/dev/mmc/mmcreg.h
+++ b/sys/dev/mmc/mmcreg.h
@@ -85,6 +85,8 @@ struct mmc_command {
 #define	MMC_RSP_R1B	(MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE | MMC_RSP_BUSY)
 #define	MMC_RSP_R2	(MMC_RSP_PRESENT | MMC_RSP_136 | MMC_RSP_CRC)
 #define	MMC_RSP_R3	(MMC_RSP_PRESENT)
+#define MMC_RSP_R4	(MMC_RSP_PRESENT)
+#define MMC_RSP_R5	(MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
 #define	MMC_RSP_R6	(MMC_RSP_PRESENT | MMC_RSP_CRC)
 #define	MMC_RSP_R7	(MMC_RSP_PRESENT | MMC_RSP_CRC)
 #define	MMC_RSP(x)	((x) & MMC_RSP_MASK)
@@ -151,6 +153,30 @@ struct mmc_command {
 #define	R1_STATE_PRG	7
 #define	R1_STATE_DIS	8
 
+/*
+ * R5 responses
+ *
+ * Types (per SD 2.0 standard)
+ *e : error bit
+ *s : status bit
+ *r : detected and set for the actual command response
+ *x : Detected and set during command execution.  The host can get
+ *    the status by issuing a command with R1 response.
+ *
+ * Clear Condition (per SD 2.0 standard)
+ *a : according to the card current state.
+ *b : always related to the previous command.  reception of a valid
+ *    command will clear it (with a delay of one command).
+ *c : clear by read
+ */
+#define R5_COM_CRC_ERROR		(1u << 15)/* er, b */
+#define R5_ILLEGAL_COMMAND		(1u << 14)/* er, b */
+#define R5_IO_CURRENT_STATE_MASK	(3u << 12)/* s, b */
+#define R5_IO_CURRENT_STATE(x) 		(((x) & R5_IO_CURRENT_STATE_MASK) >> 12)
+#define R5_ERROR			(1u << 11)/* erx, c */
+#define R5_FUNCTION_NUMBER		(1u << 9)/* er, c */
+#define R5_OUT_OF_RANGE			(1u << 8)/* er, c */
+
 struct mmc_data {
 	size_t len;		/* size of the data */
 	size_t xfer_len;
@@ -181,7 +207,7 @@ struct mmc_request {
 #define	MMC_SET_RELATIVE_ADDR	3
 #define	SD_SEND_RELATIVE_ADDR	3
 #define	MMC_SET_DSR		4
-			/* reserved: 5 */
+#define IO_SEND_OP_COND		5
 #define	MMC_SWITCH_FUNC		6
 #define	 MMC_SWITCH_FUNC_CMDS	 0
 #define	 MMC_SWITCH_FUNC_SET	 1
@@ -335,6 +361,20 @@ struct mmc_request {
 
 #define	SD_MAX_HS		50000000
 
+/*
+ * SDIO Direct & Extended I/O
+ */
+#define SD_IO_RW_WR		(1u << 31)
+#define SD_IO_RW_FUNC(x)	(((x) & 0x7) << 28)
+#define SD_IO_RW_RAW		(1u << 27)
+#define SD_IO_RW_INCR		(1u << 26)
+#define SD_IO_RW_ADR(x)		(((x) & 0x1FFFF) << 9)
+#define SD_IO_RW_DAT(x)		(((x) & 0xFF) << 0)
+#define SD_IO_RW_LEN(x)		(((x) & 0xFF) << 0)
+
+#define SD_IOE_RW_LEN(x)	(((x) & 0x1FF) << 0)
+#define SD_IOE_RW_BLK		(1u << 27)
+
 /* OCR bits */
 
 /*


More information about the freebsd-arm mailing list