Module Name:    src
Committed By:   jmcneill
Date:           Sun Jul 30 16:54:36 UTC 2017

Modified Files:
        src/sys/arch/arm/broadcom: bcm2835_emmc.c bcm2835_intr.c bcm2835_obio.c
            bcm2835reg.h files.bcm2835
        src/sys/arch/evbarm/conf: RPI
        src/sys/arch/evbarm/rpi: rpi_machdep.c
Added Files:
        src/sys/arch/arm/broadcom: bcm2835_sdhost.c

Log Message:
Add driver for the bcm2835 internal SD controller. On boards with SDIO
Wi-Fi, the internal SD controller is used for the SD card slot and the
Arasan SDHCI controller is used for SDIO.


To generate a diff of this commit:
cvs rdiff -u -r1.30 -r1.31 src/sys/arch/arm/broadcom/bcm2835_emmc.c
cvs rdiff -u -r1.11 -r1.12 src/sys/arch/arm/broadcom/bcm2835_intr.c
cvs rdiff -u -r1.26 -r1.27 src/sys/arch/arm/broadcom/bcm2835_obio.c \
    src/sys/arch/arm/broadcom/files.bcm2835
cvs rdiff -u -r0 -r1.1 src/sys/arch/arm/broadcom/bcm2835_sdhost.c
cvs rdiff -u -r1.18 -r1.19 src/sys/arch/arm/broadcom/bcm2835reg.h
cvs rdiff -u -r1.71 -r1.72 src/sys/arch/evbarm/conf/RPI
cvs rdiff -u -r1.72 -r1.73 src/sys/arch/evbarm/rpi/rpi_machdep.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/arm/broadcom/bcm2835_emmc.c
diff -u src/sys/arch/arm/broadcom/bcm2835_emmc.c:1.30 src/sys/arch/arm/broadcom/bcm2835_emmc.c:1.31
--- src/sys/arch/arm/broadcom/bcm2835_emmc.c:1.30	Thu Jun 22 13:13:51 2017
+++ src/sys/arch/arm/broadcom/bcm2835_emmc.c	Sun Jul 30 16:54:36 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: bcm2835_emmc.c,v 1.30 2017/06/22 13:13:51 jmcneill Exp $	*/
+/*	$NetBSD: bcm2835_emmc.c,v 1.31 2017/07/30 16:54:36 jmcneill Exp $	*/
 
 /*-
  * Copyright (c) 2012 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: bcm2835_emmc.c,v 1.30 2017/06/22 13:13:51 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: bcm2835_emmc.c,v 1.31 2017/07/30 16:54:36 jmcneill Exp $");
 
 #include "bcmdmac.h"
 
@@ -107,6 +107,7 @@ bcmemmc_attach(device_t parent, device_t
 	prop_dictionary_t dict = device_properties(self);
 	struct amba_attach_args *aaa = aux;
 	prop_number_t frequency;
+	bool disable = false;
 	int error;
 
 	sc->sc.sc_dev = self;
@@ -122,6 +123,13 @@ bcmemmc_attach(device_t parent, device_t
 	sc->sc.sc_clkbase = 50000;	/* Default to 50MHz */
 	sc->sc_iot = aaa->aaa_iot;
 
+	prop_dictionary_get_bool(dict, "disable", &disable);
+	if (disable) {
+		aprint_naive(": disabled\n");
+		aprint_normal(": disabled\n");
+		return;
+	}
+
 	/* Fetch the EMMC clock frequency from property if set. */
 	frequency = prop_dictionary_get(dict, "frequency");
 	if (frequency != NULL) {
@@ -131,8 +139,8 @@ bcmemmc_attach(device_t parent, device_t
 	error = bus_space_map(sc->sc_iot, aaa->aaa_addr, aaa->aaa_size, 0,
 	    &sc->sc_ioh);
 	if (error) {
-		aprint_error_dev(self,
-		    "can't map registers for %s: %d\n", aaa->aaa_name, error);
+		aprint_error(": can't map registers for %s: %d\n",
+		    aaa->aaa_name, error);
 		return;
 	}
 	sc->sc_iob = aaa->aaa_addr;

Index: src/sys/arch/arm/broadcom/bcm2835_intr.c
diff -u src/sys/arch/arm/broadcom/bcm2835_intr.c:1.11 src/sys/arch/arm/broadcom/bcm2835_intr.c:1.12
--- src/sys/arch/arm/broadcom/bcm2835_intr.c:1.11	Sat Aug  1 14:18:00 2015
+++ src/sys/arch/arm/broadcom/bcm2835_intr.c	Sun Jul 30 16:54:36 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: bcm2835_intr.c,v 1.11 2015/08/01 14:18:00 skrll Exp $	*/
+/*	$NetBSD: bcm2835_intr.c,v 1.12 2017/07/30 16:54:36 jmcneill Exp $	*/
 
 /*-
  * Copyright (c) 2012, 2015 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: bcm2835_intr.c,v 1.11 2015/08/01 14:18:00 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: bcm2835_intr.c,v 1.12 2017/07/30 16:54:36 jmcneill Exp $");
 
 #define _INTR_PRIVATE
 
@@ -163,7 +163,7 @@ static const char * const bcm2835_source
 	"(unused 44)",	"pwa0",		"pwa1",		"(unused 47)",
 	"smi",		"gpio[0]",	"gpio[1]",	"gpio[2]",
 	"gpio[3]",	"i2c",		"spi",		"pcm",
-	"sdio",		"uart",		"(unused 58)",	"(unused 59)",
+	"sdhost",	"uart",		"(unused 58)",	"(unused 59)",
 	"(unused 60)",	"(unused 61)",	"emmc",		"(unused 63)",
 	"Timer",	"Mailbox",	"Doorbell0",	"Doorbell1",
 	"GPU0 Halted",	"GPU1 Halted",	"Illegal #1",	"Illegal #0"

Index: src/sys/arch/arm/broadcom/bcm2835_obio.c
diff -u src/sys/arch/arm/broadcom/bcm2835_obio.c:1.26 src/sys/arch/arm/broadcom/bcm2835_obio.c:1.27
--- src/sys/arch/arm/broadcom/bcm2835_obio.c:1.26	Sat Nov 21 07:41:29 2015
+++ src/sys/arch/arm/broadcom/bcm2835_obio.c	Sun Jul 30 16:54:36 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: bcm2835_obio.c,v 1.26 2015/11/21 07:41:29 mlelstv Exp $	*/
+/*	$NetBSD: bcm2835_obio.c,v 1.27 2017/07/30 16:54:36 jmcneill Exp $	*/
 
 /*-
  * Copyright (c) 2012, 2014 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: bcm2835_obio.c,v 1.26 2015/11/21 07:41:29 mlelstv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: bcm2835_obio.c,v 1.27 2017/07/30 16:54:36 jmcneill Exp $");
 
 #include "locators.h"
 #include "obio.h"
@@ -148,6 +148,13 @@ static const struct ambadev_locators bcm
 		.ad_intr = -1,
 	},
 	{
+		/* SD host interface */
+		.ad_name = "sdhost",
+		.ad_addr = BCM2835_SDHOST_BASE,
+		.ad_size = BCM2835_SDHOST_SIZE,
+		.ad_intr = BCM2835_INT_SDHOST,
+	},
+	{
 		/* eMMC interface */
 		.ad_name = "emmc",
 		.ad_addr = BCM2835_EMMC_BASE,
Index: src/sys/arch/arm/broadcom/files.bcm2835
diff -u src/sys/arch/arm/broadcom/files.bcm2835:1.26 src/sys/arch/arm/broadcom/files.bcm2835:1.27
--- src/sys/arch/arm/broadcom/files.bcm2835:1.26	Sat Nov 21 07:41:29 2015
+++ src/sys/arch/arm/broadcom/files.bcm2835	Sun Jul 30 16:54:36 2017
@@ -1,4 +1,4 @@
-#	$NetBSD: files.bcm2835,v 1.26 2015/11/21 07:41:29 mlelstv Exp $
+#	$NetBSD: files.bcm2835,v 1.27 2017/07/30 16:54:36 jmcneill Exp $
 #
 # Configuration info for Broadcom BCM2835 ARM Peripherals
 #
@@ -61,6 +61,11 @@ file	arch/arm/broadcom/bcm2835_plcom.c	b
 attach	sdhc at obio with bcmemmc
 file	arch/arm/broadcom/bcm2835_emmc.c	bcmemmc
 
+# SD Host Controller (BCM2835_SDHOST_BASE)
+device	sdhost: sdmmcbus
+attach	sdhost at obio with bcmsdhost
+file	arch/arm/broadcom/bcm2835_sdhost.c	bcmsdhost	needs-flag
+
 # DMA Controller (BCM2835_DMA0_BASE)
 device	bcmdmac
 attach	bcmdmac at obio with bcmdmac_amba

Index: src/sys/arch/arm/broadcom/bcm2835reg.h
diff -u src/sys/arch/arm/broadcom/bcm2835reg.h:1.18 src/sys/arch/arm/broadcom/bcm2835reg.h:1.19
--- src/sys/arch/arm/broadcom/bcm2835reg.h:1.18	Tue Feb  2 13:55:50 2016
+++ src/sys/arch/arm/broadcom/bcm2835reg.h	Sun Jul 30 16:54:36 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: bcm2835reg.h,v 1.18 2016/02/02 13:55:50 skrll Exp $	*/
+/*	$NetBSD: bcm2835reg.h,v 1.19 2017/07/30 16:54:36 jmcneill Exp $	*/
 
 /*-
  * Copyright (c) 2012 The NetBSD Foundation, Inc.
@@ -61,6 +61,7 @@
 #define	BCM2835_RNG_BASE	(BCM2835_PERIPHERALS_BASE_BUS + 0x00104000)
 #define	BCM2835_GPIO_BASE	(BCM2835_PERIPHERALS_BASE_BUS + 0x00200000)
 #define	BCM2835_UART0_BASE	(BCM2835_PERIPHERALS_BASE_BUS + 0x00201000)
+#define	BCM2835_SDHOST_BASE	(BCM2835_PERIPHERALS_BASE_BUS + 0x00202000)
 #define	BCM2835_PCM_BASE	(BCM2835_PERIPHERALS_BASE_BUS + 0x00203000)
 #define	BCM2835_SPI0_BASE	(BCM2835_PERIPHERALS_BASE_BUS + 0x00204000)
 #define	BCM2835_BSC0_BASE	(BCM2835_PERIPHERALS_BASE_BUS + 0x00205000)
@@ -86,6 +87,7 @@
 #define	BCM2835_BSC_SIZE	0x1000
 #define	BCM2835_PWM_SIZE	0x28
 #define	BCM2835_AUX_SIZE	0x1000
+#define	BCM2835_SDHOST_SIZE	0x1000
 #define	BCM2835_EMMC_SIZE	0x1000
 #define	BCM2835_USB_SIZE	0x20000
 #define	BCM2835_DMA15_SIZE	0x100
@@ -184,6 +186,7 @@
 #define	BCM2835_INT_BSC			(BCM2835_INT_GPU1BASE + 21)
 #define	BCM2835_INT_SPI0		(BCM2835_INT_GPU1BASE + 22)
 #define	BCM2835_INT_PCM			(BCM2835_INT_GPU1BASE + 23)
+#define	BCM2835_INT_SDHOST		(BCM2835_INT_GPU1BASE + 24)
 #define	BCM2835_INT_UART0		(BCM2835_INT_GPU1BASE + 25)
 #define	BCM2835_INT_EMMC		(BCM2835_INT_GPU1BASE + 30)
 

Index: src/sys/arch/evbarm/conf/RPI
diff -u src/sys/arch/evbarm/conf/RPI:1.71 src/sys/arch/evbarm/conf/RPI:1.72
--- src/sys/arch/evbarm/conf/RPI:1.71	Tue Dec 13 20:42:17 2016
+++ src/sys/arch/evbarm/conf/RPI	Sun Jul 30 16:54:36 2017
@@ -1,5 +1,5 @@
 #
-#	$NetBSD: RPI,v 1.71 2016/12/13 20:42:17 christos Exp $
+#	$NetBSD: RPI,v 1.72 2017/07/30 16:54:36 jmcneill Exp $
 #
 #	RPi -- Raspberry Pi
 #
@@ -96,6 +96,10 @@ bcmgpio1	at obio?	# pins 32 ... 53
 sdhc* at obio?
 sdmmc*  at sdhc?
 
+# SD host controller
+#sdhost* at obio?
+#sdmmc* at sdhost?
+
 ld*     at sdmmc?
 
 # On-board USB

Index: src/sys/arch/evbarm/rpi/rpi_machdep.c
diff -u src/sys/arch/evbarm/rpi/rpi_machdep.c:1.72 src/sys/arch/evbarm/rpi/rpi_machdep.c:1.73
--- src/sys/arch/evbarm/rpi/rpi_machdep.c:1.72	Sat Jun 17 22:50:23 2017
+++ src/sys/arch/evbarm/rpi/rpi_machdep.c	Sun Jul 30 16:54:36 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: rpi_machdep.c,v 1.72 2017/06/17 22:50:23 jmcneill Exp $	*/
+/*	$NetBSD: rpi_machdep.c,v 1.73 2017/07/30 16:54:36 jmcneill Exp $	*/
 
 /*-
  * Copyright (c) 2012 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: rpi_machdep.c,v 1.72 2017/06/17 22:50:23 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: rpi_machdep.c,v 1.73 2017/07/30 16:54:36 jmcneill Exp $");
 
 #include "opt_arm_debug.h"
 #include "opt_bcm283x.h"
@@ -42,6 +42,7 @@ __KERNEL_RCSID(0, "$NetBSD: rpi_machdep.
 #include "opt_vcprop.h"
 
 #include "sdhc.h"
+#include "bcmsdhost.h"
 #include "bcmdwctwo.h"
 #include "bcmspi.h"
 #include "bsciic.h"
@@ -220,6 +221,7 @@ static struct __aligned(16) {
 	struct vcprop_tag_cmdline	vbt_cmdline;
 	struct vcprop_tag_clockrate	vbt_emmcclockrate;
 	struct vcprop_tag_clockrate	vbt_armclockrate;
+	struct vcprop_tag_clockrate	vbt_coreclockrate;
 	struct vcprop_tag end;
 } vb = {
 	.vb_hdr = {
@@ -298,6 +300,14 @@ static struct __aligned(16) {
 		},
 		.id = VCPROP_CLK_ARM
 	},
+	.vbt_coreclockrate = {
+		.tag = {
+			.vpt_tag = VCPROPTAG_GET_CLOCKRATE,
+			.vpt_len = VCPROPTAG_LEN(vb.vbt_coreclockrate),
+			.vpt_rcode = VCPROPTAG_REQUEST
+		},
+		.id = VCPROP_CLK_CORE
+	},
 	.end = {
 		.vpt_tag = VCPROPTAG_NULL
 	}
@@ -422,6 +432,23 @@ static uint8_t cursor_mask[8 * 64], curs
 #endif
 #endif
 
+/*
+ * Return true if this model Raspberry Pi has Bluetooth/Wi-Fi support
+ */ 
+static bool
+rpi_rev_has_btwifi(uint32_t rev)
+{
+	if ((rev & VCPROP_REV_ENCFLAG) == 0)
+		return false;
+
+	switch (__SHIFTOUT(rev, VCPROP_REV_MODEL)) {
+	case RPI_MODEL_B_PI3:
+	case RPI_MODEL_ZERO_W:
+		return true;
+	default:
+		return false;
+	}
+}
 
 static void
 rpi_uartinit(void)
@@ -437,16 +464,11 @@ rpi_uartinit(void)
 
 	cpu_dcache_inv_range((vaddr_t)&vb_uart, sizeof(vb_uart));
 
-	if (vcprop_tag_success_p(&vb_uart.vbt_boardrev.tag) &&
-	    (vb_uart.vbt_boardrev.rev & VCPROP_REV_ENCFLAG) != 0) {
-		const uint32_t rev = vb_uart.vbt_boardrev.rev;
-		switch (__SHIFTOUT(rev, VCPROP_REV_MODEL)) {
-		case RPI_MODEL_B_PI3:
-		case RPI_MODEL_ZERO_W:
+	if (vcprop_tag_success_p(&vb_uart.vbt_boardrev.tag)) {
+		if (rpi_rev_has_btwifi(vb_uart.vbt_boardrev.rev)) {
 			/* Enable UART0 (PL011) on GPIO header */
 			bcm2835gpio_function_select(14, BCM2835_GPIO_ALT0);
 			bcm2835gpio_function_select(15, BCM2835_GPIO_ALT0);
-			break;
 		}
 	}
 
@@ -455,6 +477,25 @@ rpi_uartinit(void)
 }
 
 
+static void
+rpi_pinctrl(void)
+{
+#if NBCMSDHOST > 0
+	/*
+	 * If the sdhost driver is present, map the SD card slot to the
+	 * SD host controller and the sdhci driver to the SDIO pins.
+	 */
+	for (int pin = 48; pin <= 53; pin++) {
+		/* Enable SDHOST on SD card slot */
+		bcm2835gpio_function_select(pin, BCM2835_GPIO_ALT0);
+	}
+	for (int pin = 34; pin <= 39; pin++) {
+		/* Enable SDHCI on SDIO */
+		bcm2835gpio_function_select(pin, BCM2835_GPIO_ALT3);
+	}
+#endif
+}
+
 
 static void
 rpi_bootparams(void)
@@ -752,6 +793,9 @@ initarm(void *arg)
 	/* we've a specific device_register routine */
 	evbarm_device_register = rpi_device_register;
 
+	/* Change pinctrl settings */
+	rpi_pinctrl();
+
 	return initarm_common(KERNEL_VM_BASE, KERNEL_VM_SIZE, NULL, 0);
 }
 
@@ -1164,12 +1208,23 @@ rpi_device_register(device_t dev, void *
 		prop_dictionary_set_uint32(dict,
 		    "chanmask", vb.vbt_dmachan.mask);
 	}
-#if NSDHC > 0
 	if (device_is_a(dev, "sdhc") &&
 	    vcprop_tag_success_p(&vb.vbt_emmcclockrate.tag) &&
 	    vb.vbt_emmcclockrate.rate > 0) {
 		prop_dictionary_set_uint32(dict,
 		    "frequency", vb.vbt_emmcclockrate.rate);
+#if NBCMSDHOST > 0
+		if (!rpi_rev_has_btwifi(vb.vbt_boardrev.rev)) {
+			/* No btwifi and sdhost driver is present */
+			prop_dictionary_set_bool(dict, "disable", true);
+		}
+#endif
+	}
+	if (device_is_a(dev, "sdhost") &&
+	    vcprop_tag_success_p(&vb.vbt_coreclockrate.tag) &&
+	    vb.vbt_coreclockrate.rate > 0) {
+		prop_dictionary_set_uint32(dict,
+		    "frequency", vb.vbt_coreclockrate.rate);
 	}
 	if (booted_device == NULL &&
 	    device_is_a(dev, "ld") &&
@@ -1177,7 +1232,6 @@ rpi_device_register(device_t dev, void *
 		booted_partition = 0;
 		booted_device = dev;
 	}
-#endif
 	if (device_is_a(dev, "usmsc") &&
 	    vcprop_tag_success_p(&vb.vbt_macaddr.tag)) {
 		const uint8_t enaddr[ETHER_ADDR_LEN] = {
@@ -1227,15 +1281,8 @@ rpi_device_register(device_t dev, void *
 	/* BSC0 is used internally on some boards */
 	if (device_is_a(dev, "bsciic") &&
 	    ((struct amba_attach_args *)aux)->aaa_addr == BCM2835_BSC0_BASE) {
-		const uint32_t rev = vb.vbt_boardrev.rev;
-
-		if ((rev & VCPROP_REV_ENCFLAG) != 0) {
-			switch (__SHIFTOUT(rev, VCPROP_REV_MODEL)) {
-			case RPI_MODEL_B_PI3:
-			case RPI_MODEL_ZERO_W:
-				prop_dictionary_set_bool(dict, "disable", true);
-				break;
-		    	}
+		if (rpi_rev_has_btwifi(vb.vbt_boardrev.rev)) {
+			prop_dictionary_set_bool(dict, "disable", true);
 		}
 	}
 }

Added files:

Index: src/sys/arch/arm/broadcom/bcm2835_sdhost.c
diff -u /dev/null src/sys/arch/arm/broadcom/bcm2835_sdhost.c:1.1
--- /dev/null	Sun Jul 30 16:54:36 2017
+++ src/sys/arch/arm/broadcom/bcm2835_sdhost.c	Sun Jul 30 16:54:36 2017
@@ -0,0 +1,732 @@
+/* $NetBSD: bcm2835_sdhost.c,v 1.1 2017/07/30 16:54:36 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2017 Jared McNeill <jmcne...@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: bcm2835_sdhost.c,v 1.1 2017/07/30 16:54:36 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/intr.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/gpio.h>
+
+#include <arm/broadcom/bcm2835reg.h>
+#include <arm/broadcom/bcm_amba.h>
+#include <arm/broadcom/bcm2835_dmac.h>
+
+#include <dev/sdmmc/sdmmcvar.h>
+#include <dev/sdmmc/sdmmcchip.h>
+#include <dev/sdmmc/sdmmc_ioreg.h>
+
+#define	SDCMD		0x00
+#define	 SDCMD_NEW	__BIT(15)
+#define	 SDCMD_FAIL	__BIT(14)
+#define	 SDCMD_BUSY	__BIT(11)
+#define	 SDCMD_NORESP	__BIT(10)
+#define	 SDCMD_LONGRESP	__BIT(9)
+#define	 SDCMD_WRITE	__BIT(7)
+#define	 SDCMD_READ	__BIT(6)
+#define	SDARG		0x04
+#define	SDTOUT		0x08
+#define	 SDTOUT_DEFAULT	0xf00000
+#define	SDCDIV		0x0c
+#define	 SDCDIV_MASK	__BITS(10,0)
+#define	SDRSP0		0x10
+#define	SDRSP1		0x14
+#define	SDRSP2		0x18
+#define	SDRSP3		0x1c
+#define	SDHSTS		0x20
+#define	 SDHSTS_BUSY	__BIT(10)
+#define	 SDHSTS_BLOCK	__BIT(9)
+#define	 SDHSTS_SDIO	__BIT(8)
+#define	 SDHSTS_REW_TO	__BIT(7)
+#define	 SDHSTS_CMD_TO	__BIT(6)
+#define	 SDHSTS_CRC16_E	__BIT(5)
+#define	 SDHSTS_CRC7_E	__BIT(4)
+#define	 SDHSTS_FIFO_E	__BIT(3)
+#define	 SDHSTS_DATA	__BIT(0)
+#define	SDVDD		0x30
+#define	 SDVDD_POWER	__BIT(0)
+#define	SDEDM		0x34
+#define	 SDEDM_RD_FIFO	__BITS(18,14)
+#define	 SDEDM_WR_FIFO	__BITS(13,9)
+#define	SDHCFG		0x38
+#define	 SDHCFG_BUSY_EN	__BIT(10)
+#define	 SDHCFG_BLOCK_EN __BIT(8)
+#define	 SDHCFG_SDIO_EN	__BIT(5)
+#define	 SDHCFG_DATA_EN	__BIT(4)
+#define	 SDHCFG_SLOW	__BIT(3)
+#define	 SDHCFG_WIDE_EXT __BIT(2)
+#define	 SDHCFG_WIDE_INT __BIT(1)
+#define	 SDHCFG_REL_CMD	__BIT(0)
+#define	SDHBCT		0x3c
+#define	SDDATA		0x40
+#define	SDHBLC		0x50
+
+struct sdhost_softc;
+
+static int	sdhost_match(device_t, cfdata_t, void *);
+static void	sdhost_attach(device_t, device_t, void *);
+static void	sdhost_attach_i(device_t);
+
+static int	sdhost_intr(void *);
+static int	sdhost_dma_setup(struct sdhost_softc *);
+static void	sdhost_dma_done(uint32_t, uint32_t, void *);
+
+static int	sdhost_host_reset(sdmmc_chipset_handle_t);
+static uint32_t	sdhost_host_ocr(sdmmc_chipset_handle_t);
+static int	sdhost_host_maxblklen(sdmmc_chipset_handle_t);
+static int	sdhost_card_detect(sdmmc_chipset_handle_t);
+static int	sdhost_write_protect(sdmmc_chipset_handle_t);
+static int	sdhost_bus_power(sdmmc_chipset_handle_t, uint32_t);
+static int	sdhost_bus_clock(sdmmc_chipset_handle_t, int, bool);
+static int	sdhost_bus_width(sdmmc_chipset_handle_t, int);
+static int	sdhost_bus_rod(sdmmc_chipset_handle_t, int);
+static void	sdhost_exec_command(sdmmc_chipset_handle_t,
+				      struct sdmmc_command *);
+static void	sdhost_card_enable_intr(sdmmc_chipset_handle_t, int);
+static void	sdhost_card_intr_ack(sdmmc_chipset_handle_t);
+
+void		sdhost_dump_regs(void);
+
+static struct sdmmc_chip_functions sdhost_chip_functions = {
+	.host_reset = sdhost_host_reset,
+	.host_ocr = sdhost_host_ocr,
+	.host_maxblklen = sdhost_host_maxblklen,
+	.card_detect = sdhost_card_detect,
+	.write_protect = sdhost_write_protect,
+	.bus_power = sdhost_bus_power,
+	.bus_clock_ddr = sdhost_bus_clock,
+	.bus_width = sdhost_bus_width,
+	.bus_rod = sdhost_bus_rod,
+	.exec_command = sdhost_exec_command,
+	.card_enable_intr = sdhost_card_enable_intr,
+	.card_intr_ack = sdhost_card_intr_ack,
+};
+
+struct sdhost_softc {
+	device_t sc_dev;
+	bus_space_tag_t sc_bst;
+	bus_space_handle_t sc_bsh;
+	bus_dma_tag_t sc_dmat;
+
+	bus_addr_t sc_addr;
+
+	void *sc_ih;
+	kmutex_t sc_intr_lock;
+	kcondvar_t sc_intr_cv;
+	kcondvar_t sc_dma_cv;
+
+	u_int sc_rate;
+
+	int sc_mmc_width;
+	int sc_mmc_present;
+
+	device_t sc_sdmmc_dev;
+
+	struct bcm_dmac_channel *sc_dmac;
+
+	bus_dmamap_t sc_dmamap;
+	bus_dma_segment_t sc_segs[1];
+	struct bcm_dmac_conblk *sc_cblk;
+
+	uint32_t sc_intr_hsts;
+
+	uint32_t sc_dma_status;
+	uint32_t sc_dma_error;
+};
+
+CFATTACH_DECL_NEW(bcmsdhost, sizeof(struct sdhost_softc),
+	sdhost_match, sdhost_attach, NULL, NULL);
+
+#define SDHOST_WRITE(sc, reg, val)	\
+	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
+#define SDHOST_READ(sc, reg) \
+	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
+
+static int
+sdhost_match(device_t parent, cfdata_t cf, void *aux)
+{
+	struct amba_attach_args * const aaa = aux;
+
+	return strcmp(aaa->aaa_name, "sdhost") == 0;
+}
+
+static void
+sdhost_attach(device_t parent, device_t self, void *aux)
+{
+	struct sdhost_softc * const sc = device_private(self);
+	struct amba_attach_args * const aaa = aux;
+	prop_dictionary_t dict = device_properties(self);
+
+	sc->sc_dev = self;
+	sc->sc_bst = aaa->aaa_iot;
+	sc->sc_dmat = aaa->aaa_dmat;
+	sc->sc_addr = aaa->aaa_addr;
+	mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_BIO);
+	cv_init(&sc->sc_intr_cv, "sdhostintr");
+	cv_init(&sc->sc_dma_cv, "sdhostdma");
+
+	if (bus_space_map(sc->sc_bst, aaa->aaa_addr, aaa->aaa_size, 0,
+	    &sc->sc_bsh) != 0) {
+		aprint_error(": couldn't map registers\n");
+		return;
+	}
+
+	aprint_naive("\n");
+	aprint_normal(": SD HOST controller\n");
+
+sdhost_dump_regs();
+
+	prop_dictionary_get_uint32(dict, "frequency", &sc->sc_rate);
+	if (sc->sc_rate == 0) {
+		aprint_error_dev(self, "couldn't get clock frequency\n");
+		return;
+	}
+
+	aprint_normal_dev(self, "ref freq %u Hz\n", sc->sc_rate);
+
+	if (sdhost_dma_setup(sc) != 0) {
+		aprint_error_dev(self, "failed to setup DMA\n");
+		return;
+	}
+
+	sc->sc_ih = intr_establish(aaa->aaa_intr, IPL_SDMMC, IST_LEVEL,
+	    sdhost_intr, sc);
+	if (sc->sc_ih == NULL) {
+		aprint_error_dev(self, "failed to establish interrupt %d\n",
+		    aaa->aaa_intr);
+		return;
+	}
+	aprint_normal_dev(self, "interrupting on intr %d\n", aaa->aaa_intr);
+
+	config_interrupts(self, sdhost_attach_i);
+}
+
+static int
+sdhost_dma_setup(struct sdhost_softc *sc)
+{
+	int error, rseg;
+
+	sc->sc_dmac = bcm_dmac_alloc(BCM_DMAC_TYPE_NORMAL, IPL_SDMMC,
+	    sdhost_dma_done, sc);
+	if (sc->sc_dmac == NULL)
+		return ENXIO;
+
+	error = bus_dmamem_alloc(sc->sc_dmat, PAGE_SIZE, PAGE_SIZE,
+	    PAGE_SIZE, sc->sc_segs, 1, &rseg, BUS_DMA_WAITOK);
+	if (error)
+		return error;
+
+	error = bus_dmamem_map(sc->sc_dmat, sc->sc_segs, rseg, PAGE_SIZE,
+	    (void **)&sc->sc_cblk, BUS_DMA_WAITOK);
+	if (error)
+		return error;
+
+	memset(sc->sc_cblk, 0, PAGE_SIZE);
+
+	error = bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1, PAGE_SIZE, 0,
+	    BUS_DMA_WAITOK, &sc->sc_dmamap);
+	if (error)
+		return error;
+
+	error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_cblk,
+	    PAGE_SIZE, NULL, BUS_DMA_WAITOK|BUS_DMA_WRITE);
+	if (error)
+		return error;
+
+	return 0;
+}
+
+static void
+sdhost_attach_i(device_t self)
+{
+	struct sdhost_softc *sc = device_private(self);
+	struct sdmmcbus_attach_args saa;
+
+	sdhost_host_reset(sc);
+	sdhost_bus_width(sc, 1);
+	sdhost_bus_clock(sc, 400, false);
+
+	memset(&saa, 0, sizeof(saa));
+	saa.saa_busname = "sdmmc";
+	saa.saa_sct = &sdhost_chip_functions;
+	saa.saa_sch = sc;
+	saa.saa_dmat = sc->sc_dmat;
+	saa.saa_clkmin = 400;
+	saa.saa_clkmax = 50000;
+	saa.saa_caps = SMC_CAPS_DMA |
+		       SMC_CAPS_MULTI_SEG_DMA |
+		       SMC_CAPS_SD_HIGHSPEED |
+		       SMC_CAPS_MMC_HIGHSPEED |
+		       SMC_CAPS_4BIT_MODE;
+
+	sc->sc_sdmmc_dev = config_found(self, &saa, NULL);
+}
+
+static int
+sdhost_intr(void *priv)
+{
+	struct sdhost_softc * const sc = priv;
+
+	mutex_enter(&sc->sc_intr_lock);
+	const uint32_t hsts = SDHOST_READ(sc, SDHSTS);
+	if (!hsts) {
+		mutex_exit(&sc->sc_intr_lock);
+		return 0;
+	}
+	SDHOST_WRITE(sc, SDHSTS, hsts);
+
+#ifdef SDHOST_DEBUG
+	device_printf(sc->sc_dev, "mmc intr hsts %#x\n", hsts);
+#endif
+
+	if (hsts) {
+		sc->sc_intr_hsts |= hsts;
+		cv_broadcast(&sc->sc_intr_cv);
+	}
+
+	mutex_exit(&sc->sc_intr_lock);
+
+	return 1;
+}
+
+static int
+sdhost_dma_transfer(struct sdhost_softc *sc, struct sdmmc_command *cmd)
+{
+	size_t seg;
+	int error;
+
+	KASSERT(mutex_owned(&sc->sc_intr_lock));
+
+	for (seg = 0; seg < cmd->c_dmamap->dm_nsegs; seg++) {
+		sc->sc_cblk[seg].cb_ti =
+		    __SHIFTIN(13, DMAC_TI_PERMAP); /* SD HOST */
+		sc->sc_cblk[seg].cb_txfr_len =
+		    cmd->c_dmamap->dm_segs[seg].ds_len;
+		/*
+		 * All transfers are assumed to be multiples of 32-bits.
+		 */
+		KASSERTMSG((sc->sc_cblk[seg].cb_txfr_len & 0x3) == 0,
+		    "seg %zu len %d", seg, sc->sc_cblk[seg].cb_txfr_len);
+		if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
+			sc->sc_cblk[seg].cb_ti |= DMAC_TI_DEST_INC;
+			/*
+			 * Use 128-bit mode if transfer is a multiple of
+			 * 16-bytes.
+			 */
+			if ((sc->sc_cblk[seg].cb_txfr_len & 0xf) == 0)
+				sc->sc_cblk[seg].cb_ti |= DMAC_TI_DEST_WIDTH;
+			sc->sc_cblk[seg].cb_ti |= DMAC_TI_SRC_DREQ;
+			sc->sc_cblk[seg].cb_source_ad =
+			    sc->sc_addr + SDDATA;
+			sc->sc_cblk[seg].cb_dest_ad =
+			    cmd->c_dmamap->dm_segs[seg].ds_addr;
+		} else {
+			sc->sc_cblk[seg].cb_ti |= DMAC_TI_SRC_INC;
+			/*
+			 * Use 128-bit mode if transfer is a multiple of
+			 * 16-bytes.
+			 */
+			if ((sc->sc_cblk[seg].cb_txfr_len & 0xf) == 0)
+				sc->sc_cblk[seg].cb_ti |= DMAC_TI_SRC_WIDTH;
+			sc->sc_cblk[seg].cb_ti |= DMAC_TI_DEST_DREQ;
+			sc->sc_cblk[seg].cb_ti |= DMAC_TI_WAIT_RESP;
+			sc->sc_cblk[seg].cb_source_ad =
+			    cmd->c_dmamap->dm_segs[seg].ds_addr;
+			sc->sc_cblk[seg].cb_dest_ad =
+			    sc->sc_addr + SDDATA;
+		}
+		sc->sc_cblk[seg].cb_stride = 0;
+		if (seg == cmd->c_dmamap->dm_nsegs - 1) {
+			sc->sc_cblk[seg].cb_ti |= DMAC_TI_INTEN;
+			sc->sc_cblk[seg].cb_nextconbk = 0;
+		} else {
+			sc->sc_cblk[seg].cb_nextconbk =
+			    sc->sc_dmamap->dm_segs[0].ds_addr +
+			    sizeof(struct bcm_dmac_conblk) * (seg+1);
+		}
+		sc->sc_cblk[seg].cb_padding[0] = 0;
+		sc->sc_cblk[seg].cb_padding[1] = 0;
+	}
+
+	bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, 0,
+	    sc->sc_dmamap->dm_mapsize, BUS_DMASYNC_PREWRITE);
+
+	error = 0;
+
+	sc->sc_dma_status = 0;
+	sc->sc_dma_error = 0;
+
+	bcm_dmac_set_conblk_addr(sc->sc_dmac,
+	    sc->sc_dmamap->dm_segs[0].ds_addr);
+	error = bcm_dmac_transfer(sc->sc_dmac);
+	if (error)
+		return error;
+
+	return 0;
+}
+
+static int
+sdhost_dma_wait(struct sdhost_softc *sc, struct sdmmc_command *cmd)
+{
+	int error = 0;
+
+	while (sc->sc_dma_status == 0 && sc->sc_dma_error == 0) {
+		error = cv_timedwait(&sc->sc_dma_cv, &sc->sc_intr_lock, hz*5);
+		if (error == EWOULDBLOCK) {
+			device_printf(sc->sc_dev, "transfer timeout!\n");
+			bcm_dmac_halt(sc->sc_dmac);
+			error = ETIMEDOUT;
+			break;
+		}
+	}
+
+	if (sc->sc_dma_status & DMAC_CS_END) {
+		cmd->c_resid = 0;
+		error = 0;
+	} else {
+		error = EIO;
+	}
+
+	bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, 0,
+	    sc->sc_dmamap->dm_mapsize, BUS_DMASYNC_POSTWRITE);
+
+	return error;
+}
+
+static void
+sdhost_dma_done(uint32_t status, uint32_t error, void *arg)
+{
+	struct sdhost_softc * const sc = arg;
+
+	if (status != (DMAC_CS_INT|DMAC_CS_END))
+		device_printf(sc->sc_dev, "dma status %#x error %#x\n",
+		    status, error);
+
+	mutex_enter(&sc->sc_intr_lock);
+	sc->sc_dma_status = status;
+	sc->sc_dma_error = error;
+	cv_broadcast(&sc->sc_dma_cv);
+	mutex_exit(&sc->sc_intr_lock);
+}
+
+static int
+sdhost_wait_idle(struct sdhost_softc *sc, int timeout)
+{
+	int retry;
+
+	KASSERT(mutex_owned(&sc->sc_intr_lock));
+
+	retry = timeout * 1000;
+
+	while (--retry > 0) {
+		const uint32_t cmd = SDHOST_READ(sc, SDCMD);
+		if ((cmd & SDCMD_NEW) == 0)
+			return 0;
+		delay(1);
+	}
+
+	return ETIMEDOUT;
+}
+
+static int
+sdhost_host_reset(sdmmc_chipset_handle_t sch)
+{
+	struct sdhost_softc * const sc = sch;
+	uint32_t edm;
+
+	SDHOST_WRITE(sc, SDVDD, 0);
+	SDHOST_WRITE(sc, SDCMD, 0);
+	SDHOST_WRITE(sc, SDARG, 0);
+	SDHOST_WRITE(sc, SDTOUT, SDTOUT_DEFAULT);
+	SDHOST_WRITE(sc, SDCDIV, 0);
+	SDHOST_WRITE(sc, SDHSTS, SDHOST_READ(sc, SDHSTS));
+	SDHOST_WRITE(sc, SDHCFG, 0);
+	SDHOST_WRITE(sc, SDHBCT, 0);
+	SDHOST_WRITE(sc, SDHBLC, 0);
+
+	edm = SDHOST_READ(sc, SDEDM);
+	edm &= ~(SDEDM_RD_FIFO|SDEDM_WR_FIFO);
+	edm |= __SHIFTIN(4, SDEDM_RD_FIFO);
+	edm |= __SHIFTIN(4, SDEDM_WR_FIFO);
+	SDHOST_WRITE(sc, SDEDM, edm);
+	delay(20000);
+	SDHOST_WRITE(sc, SDVDD, SDVDD_POWER);
+	delay(20000);
+
+	SDHOST_WRITE(sc, SDHCFG, 0);
+	SDHOST_WRITE(sc, SDCDIV, SDCDIV_MASK);
+
+	return 0;
+}
+
+static uint32_t
+sdhost_host_ocr(sdmmc_chipset_handle_t sch)
+{
+	return MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V | MMC_OCR_HCS;
+}
+
+static int
+sdhost_host_maxblklen(sdmmc_chipset_handle_t sch)
+{
+	return 8192;
+}
+
+static int
+sdhost_card_detect(sdmmc_chipset_handle_t sch)
+{
+	return 1;	/* XXX */
+}
+
+static int
+sdhost_write_protect(sdmmc_chipset_handle_t sch)
+{
+	return 0;	/* no write protect pin, assume rw */
+}
+
+static int
+sdhost_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr)
+{
+	return 0;
+}
+
+static int
+sdhost_bus_clock(sdmmc_chipset_handle_t sch, int freq, bool ddr)
+{
+	struct sdhost_softc * const sc = sch;
+	u_int target_rate = freq * 1000;
+	int div;
+
+	if (freq == 0)
+		div = SDCDIV_MASK;
+	else {
+		div = sc->sc_rate / target_rate;
+		if (div < 2)
+			div = 2;
+		if ((sc->sc_rate / div) > target_rate)
+			div++;
+		div -= 2;
+		if (div > SDCDIV_MASK)
+			div = SDCDIV_MASK;
+	}
+
+	SDHOST_WRITE(sc, SDCDIV, div);
+
+	return 0;
+}
+
+static int
+sdhost_bus_width(sdmmc_chipset_handle_t sch, int width)
+{
+	struct sdhost_softc * const sc = sch;
+	uint32_t hcfg;
+
+#ifdef SDHOST_DEBUG
+	aprint_normal_dev(sc->sc_dev, "width = %d\n", width);
+#endif
+
+	hcfg = SDHOST_READ(sc, SDHCFG);
+	if (width == 4)
+		hcfg |= SDHCFG_WIDE_EXT;
+	else
+		hcfg &= ~SDHCFG_WIDE_EXT;
+	hcfg |= (SDHCFG_WIDE_INT | SDHCFG_SLOW);
+	SDHOST_WRITE(sc, SDHCFG, hcfg);
+
+	return 0;
+}
+
+static int
+sdhost_bus_rod(sdmmc_chipset_handle_t sch, int on)
+{
+	return -1;
+}
+
+static void
+sdhost_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd)
+{
+	struct sdhost_softc * const sc = sch;
+	uint32_t cmdval, hcfg;
+	u_int nblks;
+
+#ifdef SDHOST_DEBUG
+	aprint_normal_dev(sc->sc_dev,
+	    "opcode %d flags 0x%x data %p datalen %d blklen %d\n",
+	    cmd->c_opcode, cmd->c_flags, cmd->c_data, cmd->c_datalen,
+	    cmd->c_blklen);
+#endif
+
+	mutex_enter(&sc->sc_intr_lock);
+
+	hcfg = SDHOST_READ(sc, SDHCFG);
+	SDHOST_WRITE(sc, SDHCFG, hcfg | SDHCFG_BUSY_EN);
+
+	sc->sc_intr_hsts = 0;
+
+	cmd->c_error = sdhost_wait_idle(sc, 5000);
+	if (cmd->c_error != 0) {
+#ifdef SDHOST_DEBUG
+		device_printf(sc->sc_dev, "device is busy\n");
+#endif
+		goto done;
+	}
+
+	cmdval = SDCMD_NEW;
+	if (!ISSET(cmd->c_flags, SCF_RSP_PRESENT))
+		cmdval |= SDCMD_NORESP;
+	if (ISSET(cmd->c_flags, SCF_RSP_136))
+		cmdval |= SDCMD_LONGRESP;
+	if (ISSET(cmd->c_flags, SCF_RSP_BSY))
+		cmdval |= SDCMD_BUSY;
+
+	if (cmd->c_datalen > 0) {
+		if (ISSET(cmd->c_flags, SCF_CMD_READ))
+			cmdval |= SDCMD_READ;
+		else
+			cmdval |= SDCMD_WRITE;
+
+		nblks = cmd->c_datalen / cmd->c_blklen;
+		if (nblks == 0 || (cmd->c_datalen % cmd->c_blklen) != 0)
+			++nblks;
+
+		SDHOST_WRITE(sc, SDHBCT, cmd->c_blklen);
+		SDHOST_WRITE(sc, SDHBLC, nblks);
+
+		cmd->c_resid = cmd->c_datalen;
+		cmd->c_error = sdhost_dma_transfer(sc, cmd);
+		if (cmd->c_error != 0) {
+#ifdef SDHOST_DEBUG
+			device_printf(sc->sc_dev, "dma transfer failed: %d\n",
+			    cmd->c_error);
+#endif
+			goto done;
+		}
+	}
+
+	SDHOST_WRITE(sc, SDARG, cmd->c_arg);
+	SDHOST_WRITE(sc, SDCMD, cmdval | cmd->c_opcode);
+
+	if (cmd->c_datalen > 0) {
+		cmd->c_error = sdhost_dma_wait(sc, cmd);
+		if (cmd->c_error != 0) {
+#ifdef SDHOST_DEBUG
+			device_printf(sc->sc_dev,
+			    "wait dma failed: %d\n", cmd->c_error);
+#endif
+			goto done;
+		}
+	}
+
+	cmd->c_error = sdhost_wait_idle(sc, 5000);
+	if (cmd->c_error != 0) {
+#ifdef SDHOST_DEBUG
+		device_printf(sc->sc_dev,
+		    "wait cmd idle (%#x) failed: %d\n",
+		    SDHOST_READ(sc, SDCMD), cmd->c_error);
+#endif
+	}
+
+	if ((SDHOST_READ(sc, SDCMD) & SDCMD_FAIL) != 0) {
+#ifdef SDHOST_DEBUG
+		device_printf(sc->sc_dev, "SDCMD: %#x\n",
+		    SDHOST_READ(sc, SDCMD));
+#endif
+		cmd->c_error = EIO;
+		goto done;
+	}
+
+	if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
+		if (ISSET(cmd->c_flags, SCF_RSP_136)) {
+			cmd->c_resp[0] = SDHOST_READ(sc, SDRSP0);
+			cmd->c_resp[1] = SDHOST_READ(sc, SDRSP1);
+			cmd->c_resp[2] = SDHOST_READ(sc, SDRSP2);
+			cmd->c_resp[3] = SDHOST_READ(sc, SDRSP3);
+			if (ISSET(cmd->c_flags, SCF_RSP_CRC)) {
+				cmd->c_resp[0] = (cmd->c_resp[0] >> 8) |
+				    (cmd->c_resp[1] << 24);
+				cmd->c_resp[1] = (cmd->c_resp[1] >> 8) |
+				    (cmd->c_resp[2] << 24);
+				cmd->c_resp[2] = (cmd->c_resp[2] >> 8) |
+				    (cmd->c_resp[3] << 24);
+				cmd->c_resp[3] = (cmd->c_resp[3] >> 8);
+			}
+		} else {
+			cmd->c_resp[0] = SDHOST_READ(sc, SDRSP0);
+		}
+	}
+
+done:
+	cmd->c_flags |= SCF_ITSDONE;
+	SDHOST_WRITE(sc, SDHCFG, hcfg);
+	SDHOST_WRITE(sc, SDHSTS, SDHOST_READ(sc, SDHSTS));
+	mutex_exit(&sc->sc_intr_lock);
+
+#ifdef SDHOST_DEBUG
+	if (cmd->c_error != 0)
+		device_printf(sc->sc_dev, "command failed with error %d\n",
+		    cmd->c_error);
+#endif
+}
+
+static void
+sdhost_card_enable_intr(sdmmc_chipset_handle_t sch, int enable)
+{
+}
+
+static void
+sdhost_card_intr_ack(sdmmc_chipset_handle_t sch)
+{
+}
+
+void
+sdhost_dump_regs(void)
+{
+	device_t dev = device_find_by_driver_unit("sdhost", 0);
+	if (dev == NULL)
+		return;
+	struct sdhost_softc * const sc = device_private(dev);
+
+	device_printf(dev, "SDCMD  = %08x\n", SDHOST_READ(sc, SDCMD));
+	device_printf(dev, "SDARG  = %08x\n", SDHOST_READ(sc, SDARG));
+	device_printf(dev, "SDTOUT = %08x\n", SDHOST_READ(sc, SDTOUT));
+	device_printf(dev, "SDCDIV = %08x\n", SDHOST_READ(sc, SDCDIV));
+	device_printf(dev, "SDRSP0 = %08x\n", SDHOST_READ(sc, SDRSP0));
+	device_printf(dev, "SDRSP1 = %08x\n", SDHOST_READ(sc, SDRSP1));
+	device_printf(dev, "SDRSP2 = %08x\n", SDHOST_READ(sc, SDRSP2));
+	device_printf(dev, "SDRSP3 = %08x\n", SDHOST_READ(sc, SDRSP3));
+	device_printf(dev, "SDHSTS = %08x\n", SDHOST_READ(sc, SDHSTS));
+	device_printf(dev, "SDVDD  = %08x\n", SDHOST_READ(sc, SDVDD));
+	device_printf(dev, "SDEDM  = %08x\n", SDHOST_READ(sc, SDEDM));
+	device_printf(dev, "SDHCFG = %08x\n", SDHOST_READ(sc, SDHCFG));
+	device_printf(dev, "SDHBCT = %08x\n", SDHOST_READ(sc, SDHBCT));
+	device_printf(dev, "SDDATA = ........\n");
+	device_printf(dev, "SDHBLC = %08x\n", SDHOST_READ(sc, SDHBLC));
+}

Reply via email to