Module Name: src Committed By: jmcneill Date: Sat Dec 27 01:18:48 UTC 2014
Modified Files: src/sys/conf: files Added Files: src/sys/dev/ic: dwc_mmc.c dwc_mmc_reg.h dwc_mmc_var.h Log Message: add DesignWare SD/MMC controller driver To generate a diff of this commit: cvs rdiff -u -r1.1121 -r1.1122 src/sys/conf/files cvs rdiff -u -r0 -r1.1 src/sys/dev/ic/dwc_mmc.c src/sys/dev/ic/dwc_mmc_reg.h \ src/sys/dev/ic/dwc_mmc_var.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/conf/files diff -u src/sys/conf/files:1.1121 src/sys/conf/files:1.1122 --- src/sys/conf/files:1.1121 Fri Oct 31 07:38:36 2014 +++ src/sys/conf/files Sat Dec 27 01:18:48 2014 @@ -1,4 +1,4 @@ -# $NetBSD: files,v 1.1121 2014/10/31 07:38:36 uebayasi Exp $ +# $NetBSD: files,v 1.1122 2014/12/27 01:18:48 jmcneill Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 version 20141030 @@ -1264,6 +1264,11 @@ file dev/ic/w83l518d_sdmmc.c wb device rtsx: sdmmcbus file dev/ic/rtsx.c rtsx +# DesignWare SD/MMC host controller +defflag opt_dwc_mmc.h DWC_MMC_DEBUG +device dwcmmc: sdmmcbus +file dev/ic/dwc_mmc.c dwcmmc + # Myson MTD803 3-in-1 Fast Ethernet Controller device mtd: arp, ether, ifnet, mii file dev/ic/mtd803.c mtd Added files: Index: src/sys/dev/ic/dwc_mmc.c diff -u /dev/null src/sys/dev/ic/dwc_mmc.c:1.1 --- /dev/null Sat Dec 27 01:18:49 2014 +++ src/sys/dev/ic/dwc_mmc.c Sat Dec 27 01:18:48 2014 @@ -0,0 +1,582 @@ +/* $NetBSD: dwc_mmc.c,v 1.1 2014/12/27 01:18:48 jmcneill Exp $ */ + +/*- + * Copyright (c) 2014 Jared D. 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 "opt_dwc_mmc.h" + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: dwc_mmc.c,v 1.1 2014/12/27 01:18:48 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 <dev/sdmmc/sdmmcvar.h> +#include <dev/sdmmc/sdmmcchip.h> +#include <dev/sdmmc/sdmmc_ioreg.h> + +#include <dev/ic/dwc_mmc_reg.h> +#include <dev/ic/dwc_mmc_var.h> + +static int dwc_mmc_host_reset(sdmmc_chipset_handle_t); +static uint32_t dwc_mmc_host_ocr(sdmmc_chipset_handle_t); +static int dwc_mmc_host_maxblklen(sdmmc_chipset_handle_t); +static int dwc_mmc_card_detect(sdmmc_chipset_handle_t); +static int dwc_mmc_write_protect(sdmmc_chipset_handle_t); +static int dwc_mmc_bus_power(sdmmc_chipset_handle_t, uint32_t); +static int dwc_mmc_bus_clock(sdmmc_chipset_handle_t, int); +static int dwc_mmc_bus_width(sdmmc_chipset_handle_t, int); +static int dwc_mmc_bus_rod(sdmmc_chipset_handle_t, int); +static void dwc_mmc_exec_command(sdmmc_chipset_handle_t, + struct sdmmc_command *); +static void dwc_mmc_card_enable_intr(sdmmc_chipset_handle_t, int); +static void dwc_mmc_card_intr_ack(sdmmc_chipset_handle_t); + +static int dwc_mmc_set_clock(struct dwc_mmc_softc *, u_int); +static int dwc_mmc_update_clock(struct dwc_mmc_softc *); +static int dwc_mmc_wait_rint(struct dwc_mmc_softc *, uint32_t, int); +static int dwc_mmc_pio_wait(struct dwc_mmc_softc *, + struct sdmmc_command *); +static int dwc_mmc_pio_transfer(struct dwc_mmc_softc *, + struct sdmmc_command *); + +void dwc_mmc_dump_regs(void); + +static struct sdmmc_chip_functions dwc_mmc_chip_functions = { + .host_reset = dwc_mmc_host_reset, + .host_ocr = dwc_mmc_host_ocr, + .host_maxblklen = dwc_mmc_host_maxblklen, + .card_detect = dwc_mmc_card_detect, + .write_protect = dwc_mmc_write_protect, + .bus_power = dwc_mmc_bus_power, + .bus_clock = dwc_mmc_bus_clock, + .bus_width = dwc_mmc_bus_width, + .bus_rod = dwc_mmc_bus_rod, + .exec_command = dwc_mmc_exec_command, + .card_enable_intr = dwc_mmc_card_enable_intr, + .card_intr_ack = dwc_mmc_card_intr_ack, +}; + +#define MMC_WRITE(sc, reg, val) \ + bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) +#define MMC_READ(sc, reg) \ + bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) + +void +dwc_mmc_init(struct dwc_mmc_softc *sc) +{ + struct sdmmcbus_attach_args saa; + + mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_BIO); + cv_init(&sc->sc_intr_cv, "dwcmmcirq"); + + dwc_mmc_host_reset(sc); + dwc_mmc_bus_width(sc, 1); + dwc_mmc_bus_clock(sc, 400); + + memset(&saa, 0, sizeof(saa)); + saa.saa_busname = "sdmmc"; + saa.saa_sct = &dwc_mmc_chip_functions; + saa.saa_sch = sc; + saa.saa_clkmin = 400; + saa.saa_clkmax = 25000; + saa.saa_caps = SMC_CAPS_4BIT_MODE| + SMC_CAPS_8BIT_MODE| + SMC_CAPS_SD_HIGHSPEED| + SMC_CAPS_MMC_HIGHSPEED| + SMC_CAPS_AUTO_STOP; +#if notyet + saa.saa_dmat = sc->sc_dmat; + saa.saa_caps |= SMC_CAPS_DMA| + SMC_CAPS_MULTI_SEG_DMA; +#endif + + sc->sc_sdmmc_dev = config_found(sc->sc_dev, &saa, NULL); +} + +int +dwc_mmc_intr(void *priv) +{ + struct dwc_mmc_softc *sc = priv; + uint32_t mint, rint; + + mutex_enter(&sc->sc_intr_lock); + rint = MMC_READ(sc, DWC_MMC_RINTSTS_REG); + mint = MMC_READ(sc, DWC_MMC_MINTSTS_REG); + if (!rint && !mint) { + mutex_exit(&sc->sc_intr_lock); + return 0; + } + MMC_WRITE(sc, DWC_MMC_RINTSTS_REG, rint); + MMC_WRITE(sc, DWC_MMC_MINTSTS_REG, mint); + +#ifdef DWC_MMC_DEBUG + device_printf(sc->sc_dev, "mint %#x rint %#x\n", mint, rint); +#endif + + if (rint & DWC_MMC_INT_CARDDET) { + rint &= ~DWC_MMC_INT_CARDDET; + if (sc->sc_sdmmc_dev) { + sdmmc_needs_discover(sc->sc_sdmmc_dev); + } + } + + if (rint) { + sc->sc_intr_rint |= rint; + cv_broadcast(&sc->sc_intr_cv); + } + + mutex_exit(&sc->sc_intr_lock); + + return 1; +} + +static int +dwc_mmc_set_clock(struct dwc_mmc_softc *sc, u_int freq) +{ + u_int pll_freq = sc->sc_clock_freq / 1000; + u_int n = howmany(pll_freq, freq) >> 1; + +#ifdef DWC_MMC_DEBUG + device_printf(sc->sc_dev, "%s: n=%u freq=%u\n", + __func__, n, n ? pll_freq / (2 * n) : pll_freq); +#endif + + MMC_WRITE(sc, DWC_MMC_CLKDIV_REG, + __SHIFTIN(n, DWC_MMC_CLKDIV_CLK_DIVIDER0)); + + return dwc_mmc_update_clock(sc); +} + +static int +dwc_mmc_update_clock(struct dwc_mmc_softc *sc) +{ + uint32_t cmd; + int retry; + + cmd = DWC_MMC_CMD_START_CMD | + DWC_MMC_CMD_UPDATE_CLOCK_REGS_ONLY | + DWC_MMC_CMD_WAIT_PRVDATA_COMPLETE; + + if (sc->sc_flags & DWC_MMC_F_USE_HOLD_REG) + cmd |= DWC_MMC_CMD_USE_HOLD_REG; + + MMC_WRITE(sc, DWC_MMC_CMD_REG, cmd); + retry = 0xfffff; + while (--retry > 0) { + cmd = MMC_READ(sc, DWC_MMC_CMD_REG); + if ((cmd & DWC_MMC_CMD_START_CMD) == 0) + break; + delay(10); + } + + if (retry == 0) { + device_printf(sc->sc_dev, "timeout updating clock\n"); + return ETIMEDOUT; + } + + return 0; +} + +static int +dwc_mmc_wait_rint(struct dwc_mmc_softc *sc, uint32_t mask, int timeout) +{ + int retry, error; + + KASSERT(mutex_owned(&sc->sc_intr_lock)); + + if (sc->sc_intr_rint & mask) + return 0; + + retry = timeout / hz; + + while (retry > 0) { + error = cv_timedwait(&sc->sc_intr_cv, &sc->sc_intr_lock, hz); + if (error && error != EWOULDBLOCK) + return error; + if (sc->sc_intr_rint & mask) + return 0; + --retry; + } + + return ETIMEDOUT; +} + +static int +dwc_mmc_pio_wait(struct dwc_mmc_softc *sc, struct sdmmc_command *cmd) +{ + int retry = 0xfffff; + uint32_t bit = (cmd->c_flags & SCF_CMD_READ) ? + DWC_MMC_STATUS_FIFO_EMPTY : DWC_MMC_STATUS_FIFO_FULL; + + while (--retry > 0) { + uint32_t status = MMC_READ(sc, DWC_MMC_STATUS_REG); + if (!(status & bit)) + return 0; + delay(10); + } + +#ifdef DWC_MMC_DEBUG + device_printf(sc->sc_dev, "%s: timed out\n", __func__); +#endif + + return ETIMEDOUT; +} + +static int +dwc_mmc_pio_transfer(struct dwc_mmc_softc *sc, struct sdmmc_command *cmd) +{ + uint32_t *datap = (uint32_t *)cmd->c_data; + int i; + + for (i = 0; i < (cmd->c_resid >> 2); i++) { + if (dwc_mmc_pio_wait(sc, cmd)) + return ETIMEDOUT; + if (cmd->c_flags & SCF_CMD_READ) { + datap[i] = MMC_READ(sc, DWC_MMC_FIFO_BASE_REG); + } else { + MMC_WRITE(sc, DWC_MMC_FIFO_BASE_REG, datap[i]); + } + } + + return 0; +} + +static int +dwc_mmc_host_reset(sdmmc_chipset_handle_t sch) +{ + struct dwc_mmc_softc *sc = sch; + int retry = 1000; + uint32_t ctrl, fifoth; + uint32_t rx_wmark, tx_wmark; + + MMC_WRITE(sc, DWC_MMC_PWREN_REG, DWC_MMC_PWREN_POWER_ENABLE); + + MMC_WRITE(sc, DWC_MMC_CTRL_REG, + MMC_READ(sc, DWC_MMC_CTRL_REG) | DWC_MMC_CTRL_CONTROLLER_RESET); + while (--retry > 0) { + ctrl = MMC_READ(sc, DWC_MMC_CTRL_REG); + if (ctrl & DWC_MMC_CTRL_CONTROLLER_RESET) + break; + delay(100); + } + + MMC_WRITE(sc, DWC_MMC_TMOUT_REG, 0xffffffff); + MMC_WRITE(sc, DWC_MMC_RINTSTS_REG, 0xffffffff); + + MMC_WRITE(sc, DWC_MMC_INTMASK_REG, + DWC_MMC_INT_CD | DWC_MMC_INT_ACD | DWC_MMC_INT_DTO | + DWC_MMC_INT_ERROR | DWC_MMC_INT_CARDDET); + + rx_wmark = (sc->sc_fifo_depth / 2) - 1; + tx_wmark = sc->sc_fifo_depth / 2; + fifoth = __SHIFTIN(DWC_MMC_FIFOTH_DMA_MULTIPLE_TXN_SIZE_16, + DWC_MMC_FIFOTH_DMA_MULTIPLE_TXN_SIZE); + fifoth |= __SHIFTIN(rx_wmark, DWC_MMC_FIFOTH_RX_WMARK); + fifoth |= __SHIFTIN(tx_wmark, DWC_MMC_FIFOTH_TX_WMARK); + MMC_WRITE(sc, DWC_MMC_FIFOTH_REG, fifoth); + + ctrl = MMC_READ(sc, DWC_MMC_CTRL_REG); + ctrl |= DWC_MMC_CTRL_INT_ENABLE; + MMC_WRITE(sc, DWC_MMC_CTRL_REG, ctrl); + + return 0; +} + +static uint32_t +dwc_mmc_host_ocr(sdmmc_chipset_handle_t sch) +{ + return MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V; +} + +static int +dwc_mmc_host_maxblklen(sdmmc_chipset_handle_t sch) +{ + return 32768; +} + +static int +dwc_mmc_card_detect(sdmmc_chipset_handle_t sch) +{ + struct dwc_mmc_softc *sc = sch; + uint32_t cdetect; + + cdetect = MMC_READ(sc, DWC_MMC_CDETECT_REG); + return !!(cdetect & DWC_MMC_CDETECT_CARD_DETECT_N); +} + +static int +dwc_mmc_write_protect(sdmmc_chipset_handle_t sch) +{ + struct dwc_mmc_softc *sc = sch; + uint32_t wrtprt; + + wrtprt = MMC_READ(sc, DWC_MMC_WRTPRT_REG); + return !!(wrtprt & DWC_MMC_WRTPRT_WRITE_PROTECT); +} + +static int +dwc_mmc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr) +{ + return 0; +} + +static int +dwc_mmc_bus_clock(sdmmc_chipset_handle_t sch, int freq) +{ + struct dwc_mmc_softc *sc = sch; + uint32_t clkdiv, clkena; + +#ifdef DWC_MMC_DEBUG + device_printf(sc->sc_dev, "%s: freq %d\n", __func__, freq); +#endif + + clkena = MMC_READ(sc, DWC_MMC_CLKENA_REG); + if (clkena & DWC_MMC_CLKENA_CCLK_ENABLE) { + clkena &= ~DWC_MMC_CLKENA_CCLK_ENABLE; + MMC_WRITE(sc, DWC_MMC_CLKENA_REG, clkena); + if (dwc_mmc_update_clock(sc) != 0) + return ETIMEDOUT; + } + + if (freq) { + clkdiv = MMC_READ(sc, DWC_MMC_CLKDIV_REG); + clkdiv &= ~DWC_MMC_CLKDIV_CLK_DIVIDER0; + if (dwc_mmc_update_clock(sc) != 0) + return ETIMEDOUT; + + if (dwc_mmc_set_clock(sc, freq) != 0) + return EIO; + + clkena |= DWC_MMC_CLKENA_CCLK_ENABLE; + clkena |= DWC_MMC_CLKENA_CCLK_LOW_POWER; /* XXX SD/MMC only */ + MMC_WRITE(sc, DWC_MMC_CLKENA_REG, clkena); + if (dwc_mmc_update_clock(sc) != 0) + return ETIMEDOUT; + } + + return 0; +} + +static int +dwc_mmc_bus_width(sdmmc_chipset_handle_t sch, int width) +{ + struct dwc_mmc_softc *sc = sch; + uint32_t ctype; + + switch (width) { + case 1: + ctype = DWC_MMC_CTYPE_CARD_WIDTH_1; + break; + case 4: + ctype = DWC_MMC_CTYPE_CARD_WIDTH_4; + break; + case 8: + ctype = DWC_MMC_CTYPE_CARD_WIDTH_8; + break; + default: + return EINVAL; + } + + MMC_WRITE(sc, DWC_MMC_CTYPE_REG, ctype); + + return 0; +} + +static int +dwc_mmc_bus_rod(sdmmc_chipset_handle_t sch, int on) +{ + return ENOTSUP; +} + +static void +dwc_mmc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd) +{ + struct dwc_mmc_softc *sc = sch; + uint32_t cmdval = DWC_MMC_CMD_START_CMD; + uint32_t ctrl; + + if (sc->sc_flags & DWC_MMC_F_USE_HOLD_REG) + cmdval |= DWC_MMC_CMD_USE_HOLD_REG; + + mutex_enter(&sc->sc_intr_lock); + if (cmd->c_opcode == 0) + cmdval |= DWC_MMC_CMD_SEND_INIT; + if (cmd->c_flags & SCF_RSP_PRESENT) + cmdval |= DWC_MMC_CMD_RESP_EXPECTED; + if (cmd->c_flags & SCF_RSP_136) + cmdval |= DWC_MMC_CMD_RESP_LEN; + if (cmd->c_flags & SCF_RSP_CRC) + cmdval |= DWC_MMC_CMD_CHECK_RESP_CRC; + + if (cmd->c_datalen > 0) { + unsigned int nblks; + + cmdval |= DWC_MMC_CMD_DATA_EXPECTED; + cmdval |= DWC_MMC_CMD_WAIT_PRVDATA_COMPLETE; + if (!ISSET(cmd->c_flags, SCF_CMD_READ)) { + cmdval |= DWC_MMC_CMD_WR; + } + + nblks = cmd->c_datalen / cmd->c_blklen; + if (nblks == 0 || (cmd->c_datalen % cmd->c_blklen) != 0) + ++nblks; + + if (nblks > 1) { + cmdval |= DWC_MMC_CMD_SEND_AUTO_STOP; + } + + MMC_WRITE(sc, DWC_MMC_BLKSIZ_REG, cmd->c_blklen); + MMC_WRITE(sc, DWC_MMC_BYTCNT_REG, nblks * cmd->c_blklen); + } + + sc->sc_intr_rint = 0; + + MMC_WRITE(sc, DWC_MMC_CMDARG_REG, cmd->c_arg); + + cmd->c_resid = cmd->c_datalen; + MMC_WRITE(sc, DWC_MMC_CMD_REG, cmdval | cmd->c_opcode); + if (cmd->c_datalen > 0) { + cmd->c_error = dwc_mmc_pio_transfer(sc, cmd); + if (cmd->c_error) { + goto done; + } + } + + cmd->c_error = dwc_mmc_wait_rint(sc, + DWC_MMC_INT_ERROR|DWC_MMC_INT_CD, hz * 10); + if (cmd->c_error == 0 && (sc->sc_intr_rint & DWC_MMC_INT_ERROR)) { +#ifdef DWC_MMC_DEBUG + device_printf(sc->sc_dev, "%s: rint %#x\n", __func__, + sc->sc_intr_rint); +#endif + if (sc->sc_intr_rint & DWC_MMC_INT_RTO) { + cmd->c_error = ETIMEDOUT; + } else { + cmd->c_error = EIO; + } + } + if (cmd->c_error) { + goto done; + } + + if (cmd->c_datalen > 0) { + cmd->c_error = dwc_mmc_wait_rint(sc, + DWC_MMC_INT_ERROR|DWC_MMC_INT_ACD|DWC_MMC_INT_DTO, + hz * 10); + if (cmd->c_error == 0 && + (sc->sc_intr_rint & DWC_MMC_INT_ERROR)) { +#ifdef DWC_MMC_DEBUG + device_printf(sc->sc_dev, "%s: rint2 %#x\n", __func__, + sc->sc_intr_rint); +#endif + cmd->c_error = ETIMEDOUT; + } + if (cmd->c_error) { + goto done; + } + } + + if (cmd->c_flags & SCF_RSP_PRESENT) { + if (cmd->c_flags & SCF_RSP_136) { + cmd->c_resp[0] = MMC_READ(sc, DWC_MMC_RESP0_REG); + cmd->c_resp[1] = MMC_READ(sc, DWC_MMC_RESP1_REG); + cmd->c_resp[2] = MMC_READ(sc, DWC_MMC_RESP2_REG); + cmd->c_resp[3] = MMC_READ(sc, DWC_MMC_RESP3_REG); + if (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] = MMC_READ(sc, DWC_MMC_RESP0_REG); + } + } + +done: + cmd->c_flags |= SCF_ITSDONE; + mutex_exit(&sc->sc_intr_lock); + + ctrl = MMC_READ(sc, DWC_MMC_CTRL_REG); + ctrl |= DWC_MMC_CTRL_FIFO_RESET; + MMC_WRITE(sc, DWC_MMC_CTRL_REG, ctrl); +} + +static void +dwc_mmc_card_enable_intr(sdmmc_chipset_handle_t sch, int enable) +{ +} + +static void +dwc_mmc_card_intr_ack(sdmmc_chipset_handle_t sch) +{ +} + +void +dwc_mmc_dump_regs(void) +{ + static const struct { + const char *name; + unsigned int reg; + } regs[] = { + { "CTRL", DWC_MMC_CTRL_REG }, + { "PWREN", DWC_MMC_PWREN_REG }, + { "CLKDIV", DWC_MMC_CLKDIV_REG }, + { "CLKENA", DWC_MMC_CLKENA_REG }, + { "TMOUT", DWC_MMC_TMOUT_REG }, + { "CTYPE", DWC_MMC_CTYPE_REG }, + { "BLKSIZ", DWC_MMC_BLKSIZ_REG }, + { "BYTCNT", DWC_MMC_BYTCNT_REG }, + { "INTMASK", DWC_MMC_INTMASK_REG }, + { "MINTSTS", DWC_MMC_MINTSTS_REG }, + { "RINTSTS", DWC_MMC_RINTSTS_REG }, + { "STATUS", DWC_MMC_STATUS_REG }, + { "CDETECT", DWC_MMC_CDETECT_REG }, + { "WRTPRT", DWC_MMC_WRTPRT_REG }, + { "USRID", DWC_MMC_USRID_REG }, + { "VERID", DWC_MMC_VERID_REG }, + { "RST", DWC_MMC_RST_REG }, + { "BACK_END_POWER", DWC_MMC_BACK_END_POWER_REG }, + }; + device_t self = device_find_by_driver_unit("dwcmmc", 0); + if (self == NULL) + return; + struct dwc_mmc_softc *sc = device_private(self); + int i; + + for (i = 0; i < __arraycount(regs); i++) { + device_printf(sc->sc_dev, "%s: %#x\n", regs[i].name, + MMC_READ(sc, regs[i].reg)); + } +} Index: src/sys/dev/ic/dwc_mmc_reg.h diff -u /dev/null src/sys/dev/ic/dwc_mmc_reg.h:1.1 --- /dev/null Sat Dec 27 01:18:49 2014 +++ src/sys/dev/ic/dwc_mmc_reg.h Sat Dec 27 01:18:48 2014 @@ -0,0 +1,172 @@ +/* $NetBSD: dwc_mmc_reg.h,v 1.1 2014/12/27 01:18:48 jmcneill Exp $ */ + +/*- + * Copyright (c) 2014 Jared D. 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. + */ + +#ifndef _DWC_MMC_REG_H +#define _DWC_MMC_REG_H + +#define DWC_MMC_CTRL_REG 0x0000 +#define DWC_MMC_PWREN_REG 0x0004 +#define DWC_MMC_CLKDIV_REG 0x0008 +#define DWC_MMC_CLKENA_REG 0x0010 +#define DWC_MMC_TMOUT_REG 0x0014 +#define DWC_MMC_CTYPE_REG 0x0018 +#define DWC_MMC_BLKSIZ_REG 0x001c +#define DWC_MMC_BYTCNT_REG 0x0020 +#define DWC_MMC_INTMASK_REG 0x0024 +#define DWC_MMC_CMDARG_REG 0x0028 +#define DWC_MMC_CMD_REG 0x002c +#define DWC_MMC_RESP0_REG 0x0030 +#define DWC_MMC_RESP1_REG 0x0034 +#define DWC_MMC_RESP2_REG 0x0038 +#define DWC_MMC_RESP3_REG 0x003c +#define DWC_MMC_MINTSTS_REG 0x0040 +#define DWC_MMC_RINTSTS_REG 0x0044 +#define DWC_MMC_STATUS_REG 0x0048 +#define DWC_MMC_FIFOTH_REG 0x004c +#define DWC_MMC_CDETECT_REG 0x0050 +#define DWC_MMC_WRTPRT_REG 0x0054 +#define DWC_MMC_TCBCNT_REG 0x005c +#define DWC_MMC_TBBCNT_REG 0x0060 +#define DWC_MMC_DEBNCE_REG 0x0064 +#define DWC_MMC_USRID_REG 0x0068 +#define DWC_MMC_VERID_REG 0x006c +#define DWC_MMC_UHS_REG 0x0074 +#define DWC_MMC_RST_REG 0x0078 +#define DWC_MMC_CARDTHRCTL_REG 0x0100 +#define DWC_MMC_BACK_END_POWER_REG 0x0104 +#define DWC_MMC_FIFO_BASE_REG 0x0200 + +#define DWC_MMC_CTRL_ABORT_READ_DATA __BIT(8) +#define DWC_MMC_CTRL_SEND_IRQ_RESPONSE __BIT(7) +#define DWC_MMC_CTRL_READ_WAIT __BIT(6) +#define DWC_MMC_CTRL_DMA_ENABLE __BIT(5) +#define DWC_MMC_CTRL_INT_ENABLE __BIT(4) +#define DWC_MMC_CTRL_DMA_RESET __BIT(2) +#define DWC_MMC_CTRL_FIFO_RESET __BIT(1) +#define DWC_MMC_CTRL_CONTROLLER_RESET __BIT(0) + +#define DWC_MMC_PWREN_POWER_ENABLE __BIT(0) + +#define DWC_MMC_CLKDIV_CLK_DIVIDER0 __BITS(7,0) + +#define DWC_MMC_CLKENA_CCLK_LOW_POWER __BIT(16) +#define DWC_MMC_CLKENA_CCLK_ENABLE __BIT(0) + +#define DWC_MMC_TMOUT_DATA_TIMEOUT __BITS(31,8) +#define DWC_MMC_TMOUT_RESPONSE_TIMEOUT __BITS(7,0) + +#define DWC_MMC_CTYPE_CARD_WIDTH_8 __BIT(16) +#define DWC_MMC_CTYPE_CARD_WIDTH_4 __BIT(0) +#define DWC_MMC_CTYPE_CARD_WIDTH_1 0 + +#define DWC_MMC_INT_SDIO_INT __BIT(24) +#define DWC_MMC_INT_NEW_INT __BIT(16) +#define DWC_MMC_INT_MASK __BITS(15,0) +#define DWC_MMC_INT_EBE __BIT(15) +#define DWC_MMC_INT_ACD __BIT(14) +#define DWC_MMC_INT_SBE __BIT(13) +#define DWC_MMC_INT_HLE __BIT(12) +#define DWC_MMC_INT_FRUN __BIT(11) +#define DWC_MMC_INT_HTO __BIT(10) +#define DWC_MMC_INT_DRTO __BIT(9) +#define DWC_MMC_INT_RTO __BIT(8) +#define DWC_MMC_INT_DCRC __BIT(7) +#define DWC_MMC_INT_RCRC __BIT(6) +#define DWC_MMC_INT_RXDR __BIT(5) +#define DWC_MMC_INT_TXDR __BIT(4) +#define DWC_MMC_INT_DTO __BIT(3) +#define DWC_MMC_INT_CD __BIT(2) +#define DWC_MMC_INT_RE __BIT(1) +#define DWC_MMC_INT_CARDDET __BIT(0) +#define DWC_MMC_INT_ERROR \ + (DWC_MMC_INT_RE | DWC_MMC_INT_RCRC | DWC_MMC_INT_DCRC | \ + DWC_MMC_INT_RTO | DWC_MMC_INT_DRTO | DWC_MMC_INT_HTO | \ + DWC_MMC_INT_HLE | DWC_MMC_INT_SBE | DWC_MMC_INT_EBE) + +#define DWC_MMC_CMD_START_CMD __BIT(31) +#define DWC_MMC_CMD_USE_HOLD_REG __BIT(29) +#define DWC_MMC_CMD_VOLT_SWITCH __BIT(28) +#define DWC_MMC_CMD_BOOT_MODE __BIT(27) +#define DWC_MMC_CMD_DISABLE_BOOT __BIT(26) +#define DWC_MMC_CMD_EXPECT_BOOT_ACK __BIT(25) +#define DWC_MMC_CMD_ENABLE_BOOT __BIT(24) +#define DWC_MMC_CMD_UPDATE_CLOCK_REGS_ONLY __BIT(21) +#define DWC_MMC_CMD_SEND_INIT __BIT(15) +#define DWC_MMC_CMD_STOP_ABORT_CMD __BIT(14) +#define DWC_MMC_CMD_WAIT_PRVDATA_COMPLETE __BIT(13) +#define DWC_MMC_CMD_SEND_AUTO_STOP __BIT(12) +#define DWC_MMC_CMD_TRANSFER_MODE __BIT(11) +#define DWC_MMC_CMD_WR __BIT(10) +#define DWC_MMC_CMD_DATA_EXPECTED __BIT(9) +#define DWC_MMC_CMD_CHECK_RESP_CRC __BIT(8) +#define DWC_MMC_CMD_RESP_LEN __BIT(7) +#define DWC_MMC_CMD_RESP_EXPECTED __BIT(6) +#define DWC_MMC_CMD_INDEX __BITS(5,0) + +#define DWC_MMC_STATUS_DMA_REQ __BIT(31) +#define DWC_MMC_STATUS_DMA_ACK __BIT(30) +#define DWC_MMC_STATUS_FIFO_COUNT __BITS(29,17) +#define DWC_MMC_STATUS_RESP_INDEX __BITS(16,11) +#define DWC_MMC_STATUS_DATA_STATE_MC_BUSY __BIT(10) +#define DWC_MMC_STATUS_DATA_BUSY __BIT(9) +#define DWC_MMC_STATUS_DATA_3_STATUS __BIT(8) +#define DWC_MMC_STATUS_COMMAND_FSM_STATES __BITS(7,4) +#define DWC_MMC_STATUS_FIFO_FULL __BIT(3) +#define DWC_MMC_STATUS_FIFO_EMPTY __BIT(2) +#define DWC_MMC_STATUS_FIFO_TX_WATERMARK __BIT(1) +#define DWC_MMC_STATUS_FIFO_RX_WATERMARK __BIT(0) + +#define DWC_MMC_FIFOTH_DMA_MULTIPLE_TXN_SIZE __BITS(30,28) +#define DWC_MMC_FIFOTH_DMA_MULTIPLE_TXN_SIZE_1 0 +#define DWC_MMC_FIFOTH_DMA_MULTIPLE_TXN_SIZE_4 1 +#define DWC_MMC_FIFOTH_DMA_MULTIPLE_TXN_SIZE_8 2 +#define DWC_MMC_FIFOTH_DMA_MULTIPLE_TXN_SIZE_16 3 +#define DWC_MMC_FIFOTH_DMA_MULTIPLE_TXN_SIZE_32 4 +#define DWC_MMC_FIFOTH_DMA_MULTIPLE_TXN_SIZE_64 5 +#define DWC_MMC_FIFOTH_DMA_MULTIPLE_TXN_SIZE_128 6 +#define DWC_MMC_FIFOTH_DMA_MULTIPLE_TXN_SIZE_256 7 +#define DWC_MMC_FIFOTH_RX_WMARK __BITS(27,16) +#define DWC_MMC_FIFOTH_TX_WMARK __BITS(11,0) + +#define DWC_MMC_CDETECT_CARD_DETECT_N __BIT(0) + +#define DWC_MMC_WRTPRT_WRITE_PROTECT __BIT(0) + +#define DWC_MMC_DEBNCE_DEBOUNCE_COUNT __BITS(23,0) + +#define DWC_MMC_UHS_DDR __BIT(16) +#define DWC_MMC_UHS_VOLT __BIT(0) + +#define DWC_MMC_RST_CARD_RESET __BIT(0) + +#define DWC_MMC_CARDTHRCTL_CARDRDTHRESHOLD __BITS(27,16) +#define DWC_MMC_CARDTHRCTL_CARDRDTHREN __BIT(0) + +#define DWC_MMC_BACK_END_POWER_ENABLE __BIT(0) + +#endif /* !_DWC_MMC_REG_H */ Index: src/sys/dev/ic/dwc_mmc_var.h diff -u /dev/null src/sys/dev/ic/dwc_mmc_var.h:1.1 --- /dev/null Sat Dec 27 01:18:49 2014 +++ src/sys/dev/ic/dwc_mmc_var.h Sat Dec 27 01:18:48 2014 @@ -0,0 +1,53 @@ +/* $NetBSD: dwc_mmc_var.h,v 1.1 2014/12/27 01:18:48 jmcneill Exp $ */ + +/*- + * Copyright (c) 2014 Jared D. 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. + */ + +#ifndef _DWC_MMC_VAR_H +#define _DWC_MMC_VAR_H + +struct dwc_mmc_softc { + device_t sc_dev; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + bus_dma_tag_t sc_dmat; + void *sc_ih; + unsigned int sc_clock_freq; + unsigned int sc_fifo_depth; + uint32_t sc_flags; +#define DWC_MMC_F_USE_HOLD_REG 0x0001 + + device_t sc_sdmmc_dev; + kmutex_t sc_intr_lock; + kcondvar_t sc_intr_cv; + + uint32_t sc_intr_rint; +}; + +void dwc_mmc_init(struct dwc_mmc_softc *); +int dwc_mmc_intr(void *); + +#endif /* !_DWC_MMC_VAR_H */