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)); +}