[PATCH] SDIO support for Globalscale Dreamplug

Ilya Bakulin ilya at bakulin.de
Tue Jul 2 15:08:46 UTC 2013


Hi list,
I'm currently developing a SDIO driver for the Globalscale Dreamplug.
I have taken SDIO patch for Marvell SoC from [1].
After that I have written some SDIO-related code in sys/dev/mmc/mmc.c,
using OpenBSD SDIO code and the patch from Ben Gray ([2]) as a starting point.

I have taken Warner's wish to have SDIO code in MMC bus into account, so there
is no extra layer of abstraction in my code, SDIO devices will attach directly
to MMC bus. This makes possible to implement combo cards support in the future,
although I don't support them in my code atm.

What is already implemented:
 * SDIO card detection;
 * CIS reading, both common CIS and individual functions' CIS;
 * Function enable.

My questions, need answers before I can move further:
 * Where should I store information retrieved from the CIS?
   As far as I understand, I should use mmc_ivars structure for that.
   But in SDIO case the relationship between MMC bus and SDIO card is 1:1,
   and storing the information about the card in mmc_softc sounds like
   a good idea -- then I can pass only mmc_softc structure to all functions
   that need to work with the attached SDIO card.

 * Should I add any methods to the existing interface files?

 * Are there any devices on the market that use SDIO interface and which
   chipsets are supported in FreeBSD? Any Atheros devices?
   Adrian, what do you think?
   I have only Dreamplug with Marvell SDIO-based WLAN chip, that doesn't have
   an opensource driver even for Linux...

[1] http://people.freebsd.org/~raj/misc/mv_sdio.c
[2] http://lists.freebsd.org/pipermail/freebsd-arm/2012-June/003543.html

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..6fcfb32 100644
--- a/sys/dev/mmc/mmc.c
+++ b/sys/dev/mmc/mmc.c
@@ -67,6 +67,8 @@ __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"
 
@@ -96,11 +98,13 @@ struct mmc_ivars {
 	u_char read_only;	/* True when the device is read-only */
 	u_char bus_width;	/* Bus width to use */
 	u_char timing;		/* Bus timing support */
+	uint8_t mem_present;	/* Is memory present */
 	u_char high_cap;	/* High Capacity card (block addressed) */
 	uint32_t sec_count;	/* Card capacity in 512byte blocks */
 	uint32_t tran_speed;	/* Max speed in normal mode */
 	uint32_t hs_tran_speed;	/* Max speed in high speed mode */
 	uint32_t erase_sector;	/* Card native erase sector size */
+	uint8_t sdio_numfunc;	/* Number of IO functions */
 	char card_id_string[64];/* Formatted CID info (serial, MFG, etc) */
 };
 
@@ -159,10 +163,15 @@ 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 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,
@@ -470,6 +479,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 +504,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 +532,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 +560,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 +613,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 +1056,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 +1177,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 +1193,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 +1240,291 @@ mmc_log_card(device_t dev, struct mmc_ivars *ivar, int newcard)
 	    ivar->read_only ? ", read-only" : "");
 }
 
+static int
+mmc_io_func_enable(struct mmc_softc *sc, uint32_t fn)
+{
+	int err, i;
+	uint8_t funcs;
+
+	/* XXX Check if function number is valid */
+
+	err = mmc_io_rw_direct(sc, 0, 0, SD_IO_CCCR_FN_READY, &funcs);
+	if (err != MMC_ERR_NONE) {
+		device_printf(sc->dev, "Error reading SDIO func ready %d\n", err);
+		return (err);
+	}
+	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++) {
+		err = mmc_io_rw_direct(sc, 0, 0, SD_IO_CCCR_FN_READY, &funcs);
+		if (err != MMC_ERR_NONE) {
+			device_printf(sc->dev, "Error reading SDIO func ready %d\n", err);
+			return (err);
+		}
+
+		if (funcs & (1 << fn))
+			return 0;
+		pause("mmc_io_func_enable", 100);
+	}
+
+	device_printf(sc->dev, "Cannot enable function %d!\n", fn);
+	return 1;
+}
+
+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]));
+
+	*data = (uint8_t) (cmd.resp[0] & 0xff);
+	return (MMC_ERR_NONE);
+}
+
+/* CMD52 */
+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)
+{
+	/*
+	 * XXX Need a structure to store all information that we get from CIS!
+	 * But where to place it after that?..
+	*/
+	uint32_t tmp;
+
+	uint8_t tuple_id, tuple_len, func_id;
+	uint32_t addr, maninfo_p;
+	uint16_t manufacturer, product;
+	uint16_t fn0_blksize;
+	uint8_t max_tran_speed;
+	uint8_t cis1_major, cis1_minor;
+	char *cis1_info[4];
+
+	int start, i, ch, count;
+	char cis1_info_buf[256];
+
+	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;
+
+			cis1_major = mmc_io_read_1(sc, 0, maninfo_p);
+			cis1_minor = mmc_io_read_1(sc, 0, maninfo_p + 1);
+
+			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, "*** 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;
+			}
+			manufacturer = mmc_io_read_1(sc, 0, addr);
+			manufacturer |= mmc_io_read_1(sc, 0, addr + 1) << 8;
+
+			product = mmc_io_read_1(sc, 0, addr + 2);
+			product |= mmc_io_read_1(sc, 0, addr + 3) << 8;
+
+			device_printf(sc->dev,
+			    "*** Vendor %04X, product %04X\n",
+			    manufacturer, product);
+
+			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);
+			device_printf(sc->dev, "*** Function ext type %d\n",
+			    ext_data_type);
+
+			if (func == 0) {
+				if (ext_data_type != 0x0)
+					device_printf(sc->dev,
+					    "funce for func 0 non-std: %d\n",
+					    ext_data_type);
+				fn0_blksize = mmc_io_read_1(sc, 0, addr + 1);
+				fn0_blksize |= mmc_io_read_1(sc, 0, addr + 2) << 8;
+
+				max_tran_speed = mmc_io_read_1(sc, 0, addr + 3);
+				uint8_t max_tran_rate = max_tran_speed & 0x3;
+				uint8_t timecode = (max_tran_speed >> 3) & 0xF;
+
+				device_printf(sc->dev,
+				    "*** Max tran rate %d, timecode %d\n",
+				    max_tran_rate, timecode);
+			} else {
+				/*
+				 * XXX Do we need any information from FUNCE
+				 * for non-0 functions?
+				 */
+				device_printf(sc->dev,
+				    "I don't know how to parse FUNCE\n");
+			}
+
+			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);
+}
+
+/*
+ * 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);
+	}
+
+	return mmc_io_parse_cis(sc, func, cisptr);
+}
+
 static void
 mmc_discover_cards(struct mmc_softc *sc)
 {
@@ -1233,11 +1535,48 @@ 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);
+		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");
+			ivar = malloc(sizeof(struct mmc_ivars), M_DEVBUF,
+			    M_WAITOK | M_ZERO);
+			mmc_send_relative_addr(sc, &resp); /* CMD3 */
+			ivar->rca = resp >> 16;
+			err = mmc_select_card(sc, ivar->rca); /* CMD7 */
+			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);
+			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);
+			}
+			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 +1830,49 @@ 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) {
+		device_printf(sc->dev, "No timeout: OCR %08X, here are the values:\n", cmd.resp[0]);
+		if (rocr)
+			*rocr = cmd.resp[0];
+		if (nfunc)
+			*nfunc = cmd.resp[0] >> 28 & 0x7;
+		if (mem_present)
+			*mem_present = cmd.resp[0] >> 27 & 0x1;
+
+		device_printf(sc->dev, "NF: %d\n", cmd.resp[0] >> 28 & 0x7);
+		device_printf(sc->dev, "MEM: %d\n", 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 +1888,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 +1939,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);
@@ -1637,7 +2025,7 @@ mmc_read_ivar(device_t bus, device_t child, int which, uintptr_t *result)
 		return (EINVAL);
 	case MMC_IVAR_DSR_IMP:
 		*result = ivar->csd.dsr_imp;
-		break;
+		 break;
 	case MMC_IVAR_MEDIA_SIZE:
 		*result = ivar->sec_count;
 		break;
@@ -1735,3 +2123,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..840a863
--- /dev/null
+++ b/sys/dev/mmc/mmcioreg.h
@@ -0,0 +1,95 @@
+/*	$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_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