Module Name: src Committed By: jmcneill Date: Tue Feb 25 00:08:29 UTC 2014
Modified Files: src/sys/arch/arm/allwinner: awin_io.c awin_reg.h awin_var.h files.awin Added Files: src/sys/arch/arm/allwinner: awin_mmc.c Removed Files: src/sys/arch/arm/allwinner: awin_sdhc.c Log Message: Add Allwinner SD/MMC driver. No DMA yet. To generate a diff of this commit: cvs rdiff -u -r1.6 -r1.7 src/sys/arch/arm/allwinner/awin_io.c \ src/sys/arch/arm/allwinner/files.awin cvs rdiff -u -r0 -r1.1 src/sys/arch/arm/allwinner/awin_mmc.c cvs rdiff -u -r1.11 -r1.12 src/sys/arch/arm/allwinner/awin_reg.h cvs rdiff -u -r1.4 -r0 src/sys/arch/arm/allwinner/awin_sdhc.c cvs rdiff -u -r1.7 -r1.8 src/sys/arch/arm/allwinner/awin_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/arch/arm/allwinner/awin_io.c diff -u src/sys/arch/arm/allwinner/awin_io.c:1.6 src/sys/arch/arm/allwinner/awin_io.c:1.7 --- src/sys/arch/arm/allwinner/awin_io.c:1.6 Thu Feb 20 21:48:38 2014 +++ src/sys/arch/arm/allwinner/awin_io.c Tue Feb 25 00:08:29 2014 @@ -31,7 +31,7 @@ #include <sys/cdefs.h> -__KERNEL_RCSID(1, "$NetBSD: awin_io.c,v 1.6 2014/02/20 21:48:38 matt Exp $"); +__KERNEL_RCSID(1, "$NetBSD: awin_io.c,v 1.7 2014/02/25 00:08:29 jmcneill Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -104,11 +104,11 @@ static const struct awin_locators awin_l { "awinwdt", OFFANDSIZE(TMR), NOPORT, NOINTR, AANY }, { "awinusb", OFFANDSIZE(USB1), 0, NOINTR, AANY }, { "awinusb", OFFANDSIZE(USB2), 1, NOINTR, AANY }, - { "sdhc", OFFANDSIZE(SDMMC0), 0, AWIN_IRQ_SDMMC0, AANY }, - { "sdhc", OFFANDSIZE(SDMMC1), 1, AWIN_IRQ_SDMMC1, AANY }, - { "sdhc", OFFANDSIZE(SDMMC2), 2, AWIN_IRQ_SDMMC2, AANY }, - { "sdhc", OFFANDSIZE(SDMMC3), 3, AWIN_IRQ_SDMMC3, AANY }, - { "sdhc", OFFANDSIZE(SDMMC1), 4, AWIN_IRQ_SDMMC1, AANY }, + { "awinmmc", OFFANDSIZE(SDMMC0), 0, AWIN_IRQ_SDMMC0, AANY }, + { "awinmmc", OFFANDSIZE(SDMMC1), 1, AWIN_IRQ_SDMMC1, AANY }, + { "awinmmc", OFFANDSIZE(SDMMC2), 2, AWIN_IRQ_SDMMC2, AANY }, + { "awinmmc", OFFANDSIZE(SDMMC3), 3, AWIN_IRQ_SDMMC3, AANY }, + { "awinmmc", OFFANDSIZE(SDMMC1), 4, AWIN_IRQ_SDMMC1, AANY }, { "ahcisata", OFFANDSIZE(SATA), NOPORT, AWIN_IRQ_SATA, AANY }, { "awiniic", OFFANDSIZE(TWI0), 0, AWIN_IRQ_TWI0, AANY }, { "awiniic", OFFANDSIZE(TWI1), 1, AWIN_IRQ_TWI1, AANY }, Index: src/sys/arch/arm/allwinner/files.awin diff -u src/sys/arch/arm/allwinner/files.awin:1.6 src/sys/arch/arm/allwinner/files.awin:1.7 --- src/sys/arch/arm/allwinner/files.awin:1.6 Sun Sep 8 04:06:44 2013 +++ src/sys/arch/arm/allwinner/files.awin Tue Feb 25 00:08:29 2014 @@ -1,4 +1,4 @@ -# $NetBSD: files.awin,v 1.6 2013/09/08 04:06:44 matt Exp $ +# $NetBSD: files.awin,v 1.7 2014/02/25 00:08:29 jmcneill Exp $ # # Configuration info for Allwinner ARM Peripherals # @@ -88,9 +88,10 @@ attach ohci at awinusb with ohci_awinusb attach ehci at awinusb with ehci_awinusb file arch/arm/allwinner/awin_usb.c awin_usb -# A10/A20 SD/MMC Controller (SDHC) -attach sdhc at awinio with awin_sdhc -file arch/arm/allwinner/awin_sdhc.c awin_sdhc +# A10/A20 SD/MMC Controller (SD/MMC) +device awinmmc: sdmmcbus +attach awinmmc at awinio with awin_mmc +file arch/arm/allwinner/awin_mmc.c awin_mmc # A10 WDC Controller (PATA) attach wdc at awinio with awin_wdc Index: src/sys/arch/arm/allwinner/awin_reg.h diff -u src/sys/arch/arm/allwinner/awin_reg.h:1.11 src/sys/arch/arm/allwinner/awin_reg.h:1.12 --- src/sys/arch/arm/allwinner/awin_reg.h:1.11 Mon Feb 24 16:40:29 2014 +++ src/sys/arch/arm/allwinner/awin_reg.h Tue Feb 25 00:08:29 2014 @@ -587,6 +587,35 @@ #define AWIN_AHCI_P0PHYCR_REG 0x0178 #define AWIN_AHCI_P0PHYSR_REG 0x017C +#define AWIN_MMC_GCTRL 0x0000 +#define AWIN_MMC_CLKCR 0x0004 +#define AWIN_MMC_TIMEOUT 0x0008 +#define AWIN_MMC_WIDTH 0x000C +#define AWIN_MMC_BLKSZ 0x0010 +#define AWIN_MMC_BYTECNT 0x0014 +#define AWIN_MMC_CMD 0x0018 +#define AWIN_MMC_ARG 0x001C +#define AWIN_MMC_RESP0 0x0020 +#define AWIN_MMC_RESP1 0x0024 +#define AWIN_MMC_RESP2 0x0028 +#define AWIN_MMC_RESP3 0x002C +#define AWIN_MMC_IMASK 0x0030 +#define AWIN_MMC_MINT 0x0034 +#define AWIN_MMC_RINT 0x0038 +#define AWIN_MMC_STATUS 0x003C +#define AWIN_MMC_FTRGLEVEL 0x0040 +#define AWIN_MMC_FUNCSEL 0x0044 +#define AWIN_MMC_CBCR 0x0048 +#define AWIN_MMC_BBCR 0x004C +#define AWIN_MMC_DBGC 0x0050 +#define AWIN_MMC_DMAC 0x0080 +#define AWIN_MMC_DLBA 0x0084 +#define AWIN_MMC_IDST 0x0088 +#define AWIN_MMC_IDIE 0x008C +#define AWIN_MMC_CHDA 0x0090 +#define AWIN_MMC_CBDA 0x0094 +#define AWIN_MMC_FIFO 0x0100 + #define AWIN_CPUCFG_CPU0_RST_CTRL_REG 0x0040 #define AWIN_CPUCFG_CPU0_CTRL_REG 0x0044 #define AWIN_CPUCFG_CPU0_STATUS_REG 0x0048 Index: src/sys/arch/arm/allwinner/awin_var.h diff -u src/sys/arch/arm/allwinner/awin_var.h:1.7 src/sys/arch/arm/allwinner/awin_var.h:1.8 --- src/sys/arch/arm/allwinner/awin_var.h:1.7 Thu Feb 20 21:45:06 2014 +++ src/sys/arch/arm/allwinner/awin_var.h Tue Feb 25 00:08:29 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: awin_var.h,v 1.7 2014/02/20 21:45:06 matt Exp $ */ +/* $NetBSD: awin_var.h,v 1.8 2014/02/25 00:08:29 jmcneill Exp $ */ /*- * Copyright (c) 2013 The NetBSD Foundation, Inc. * All rights reserved. @@ -95,6 +95,12 @@ awin_gpio_pindata_write(const struct awi gpiobus_pin_write(pd->pd_gc, pd->pd_pin, value); } +static inline int +awin_gpio_pindata_read(const struct awin_gpio_pindata *pd) +{ + return gpiobus_pin_read(pd->pd_gc, pd->pd_pin); +} + static void inline awin_reg_set_clear(bus_space_tag_t bst, bus_space_handle_t bsh, bus_size_t o, uint32_t set_mask, uint32_t clr_mask) Added files: Index: src/sys/arch/arm/allwinner/awin_mmc.c diff -u /dev/null src/sys/arch/arm/allwinner/awin_mmc.c:1.1 --- /dev/null Tue Feb 25 00:08:29 2014 +++ src/sys/arch/arm/allwinner/awin_mmc.c Tue Feb 25 00:08:29 2014 @@ -0,0 +1,565 @@ +/* $NetBSD: awin_mmc.c,v 1.1 2014/02/25 00:08:29 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 "locators.h" + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: awin_mmc.c,v 1.1 2014/02/25 00:08:29 jmcneill Exp $"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/device.h> +#include <sys/intr.h> +#include <sys/systm.h> + +#include <dev/sdmmc/sdmmcvar.h> +#include <dev/sdmmc/sdmmcchip.h> +#include <dev/sdmmc/sdmmc_ioreg.h> + +#include <arm/allwinner/awin_reg.h> +#include <arm/allwinner/awin_var.h> + +static int awin_mmc_match(device_t, cfdata_t, void *); +static void awin_mmc_attach(device_t, device_t, void *); + +static int awin_mmc_host_reset(sdmmc_chipset_handle_t); +static uint32_t awin_mmc_host_ocr(sdmmc_chipset_handle_t); +static int awin_mmc_host_maxblklen(sdmmc_chipset_handle_t); +static int awin_mmc_card_detect(sdmmc_chipset_handle_t); +static int awin_mmc_write_protect(sdmmc_chipset_handle_t); +static int awin_mmc_bus_power(sdmmc_chipset_handle_t, uint32_t); +static int awin_mmc_bus_clock(sdmmc_chipset_handle_t, int); +static int awin_mmc_bus_width(sdmmc_chipset_handle_t, int); +static int awin_mmc_bus_rod(sdmmc_chipset_handle_t, int); +static void awin_mmc_exec_command(sdmmc_chipset_handle_t, + struct sdmmc_command *); +static void awin_mmc_card_enable_intr(sdmmc_chipset_handle_t, int); +static void awin_mmc_card_intr_ack(sdmmc_chipset_handle_t); + +static struct sdmmc_chip_functions awin_mmc_chip_functions = { + .host_reset = awin_mmc_host_reset, + .host_ocr = awin_mmc_host_ocr, + .host_maxblklen = awin_mmc_host_maxblklen, + .card_detect = awin_mmc_card_detect, + .write_protect = awin_mmc_write_protect, + .bus_power = awin_mmc_bus_power, + .bus_clock = awin_mmc_bus_clock, + .bus_width = awin_mmc_bus_width, + .bus_rod = awin_mmc_bus_rod, + .exec_command = awin_mmc_exec_command, + .card_enable_intr = awin_mmc_card_enable_intr, + .card_intr_ack = awin_mmc_card_intr_ack, +}; + +struct awin_mmc_softc { + device_t sc_dev; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + bus_dma_tag_t sc_dmat; + + int sc_mmc_number; + int sc_mmc_width; + + device_t sc_sdmmc_dev; + unsigned int sc_pll5_freq; + unsigned int sc_mod_clk; + + bool sc_has_gpio_detect; + struct awin_gpio_pindata sc_gpio_detect; /* card detect */ + bool sc_has_gpio_wp; + struct awin_gpio_pindata sc_gpio_wp; /* write protect */ +}; + +CFATTACH_DECL_NEW(awin_mmc, sizeof(struct awin_mmc_softc), + awin_mmc_match, awin_mmc_attach, NULL, NULL); + +#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)) + +static int +awin_mmc_match(device_t parent, cfdata_t cf, void *aux) +{ + struct awinio_attach_args * const aio = aux; + const struct awin_locators * const loc = &aio->aio_loc; + const int port = cf->cf_loc[AWINIOCF_PORT]; + + if (strcmp(cf->cf_name, loc->loc_name)) + return 0; + + if (port != AWINIOCF_PORT_DEFAULT && port != loc->loc_port) + return 0; + + return 1; +} + +static void +awin_mmc_probe_clocks(struct awin_mmc_softc *sc, struct awinio_attach_args *aio) +{ + uint32_t val, freq; + int n, k, p, div; + + val = bus_space_read_4(aio->aio_core_bst, aio->aio_ccm_bsh, + AWIN_PLL5_CFG_REG); + + n = (val >> 8) & 0x1f; + k = ((val >> 4) & 3) + 1; + p = 1 << ((val >> 16) & 3); + + freq = 24000000 * n * k / p; + + sc->sc_pll5_freq = freq; + if (sc->sc_pll5_freq > 400000000) { + div = 4; + } else { + div = 3; + } + sc->sc_mod_clk = sc->sc_pll5_freq / (div + 1); + + awin_reg_set_clear(aio->aio_core_bst, aio->aio_ccm_bsh, + AWIN_SD0_CLK_REG + (sc->sc_mmc_number * 8), + AWIN_PLL_CFG_ENABLE | AWIN_PLL_CFG_EXG_MODE | div, 0); + +#ifdef AWIN_MMC_DEBUG + aprint_normal_dev(sc->sc_dev, "PLL5 @ %u Hz\n", freq); +#endif +} + +static void +awin_mmc_attach(device_t parent, device_t self, void *aux) +{ + struct awin_mmc_softc * const sc = device_private(self); + struct awinio_attach_args * const aio = aux; + const struct awin_locators * const loc = &aio->aio_loc; + struct sdmmcbus_attach_args saa; + prop_dictionary_t cfg = device_properties(self); + const char *pin_name; + + sc->sc_dev = self; + sc->sc_bst = aio->aio_core_bst; + sc->sc_dmat = aio->aio_dmat; + sc->sc_mmc_number = loc->loc_port; + bus_space_subregion(sc->sc_bst, aio->aio_core_bsh, + loc->loc_offset, loc->loc_size, &sc->sc_bsh); + + aprint_naive("\n"); + aprint_normal(": SD/MMC interface\n"); + + awin_mmc_probe_clocks(sc, aio); + + if (prop_dictionary_get_cstring_nocopy(cfg, "detect-gpio", &pin_name)) { + if (!awin_gpio_pin_reserve(pin_name, &sc->sc_gpio_detect)) { + aprint_error_dev(self, + "failed to reserve GPIO \"%s\"\n", pin_name); + } else { + sc->sc_has_gpio_detect = true; + } + } + if (prop_dictionary_get_cstring_nocopy(cfg, "wp-gpio", &pin_name)) { + if (!awin_gpio_pin_reserve(pin_name, &sc->sc_gpio_wp)) { + aprint_error_dev(self, + "failed to reserve GPIO \"%s\"\n", pin_name); + } else { + sc->sc_has_gpio_wp = true; + } + } + + awin_mmc_host_reset(sc); + awin_mmc_bus_width(sc, 1); + + memset(&saa, 0, sizeof(saa)); + saa.saa_busname = "sdmmc"; + saa.saa_sct = &awin_mmc_chip_functions; + saa.saa_sch = sc; + saa.saa_clkmin = 400; + saa.saa_clkmax = 52000; + saa.saa_caps = SMC_CAPS_4BIT_MODE| + SMC_CAPS_8BIT_MODE| + SMC_CAPS_SD_HIGHSPEED| + SMC_CAPS_MMC_HIGHSPEED| + SMC_CAPS_AUTO_STOP; + + sc->sc_sdmmc_dev = config_found(self, &saa, NULL); +} + +static int +awin_mmc_host_reset(sdmmc_chipset_handle_t sch) +{ + struct awin_mmc_softc *sc = sch; + +#ifdef AWIN_MMC_DEBUG + aprint_normal_dev(sc->sc_dev, "host reset\n"); +#endif + + MMC_WRITE(sc, AWIN_MMC_GCTRL, + MMC_READ(sc, AWIN_MMC_GCTRL) | __BITS(2,0)); + + return 0; +} + +static uint32_t +awin_mmc_host_ocr(sdmmc_chipset_handle_t sch) +{ + return MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V; +} + +static int +awin_mmc_host_maxblklen(sdmmc_chipset_handle_t sch) +{ + return 4096; +} + +static int +awin_mmc_card_detect(sdmmc_chipset_handle_t sch) +{ + struct awin_mmc_softc *sc = sch; + + if (sc->sc_has_gpio_detect == false) { + return 1; /* no card detect pin, assume present */ + } else { + return awin_gpio_pindata_read(&sc->sc_gpio_detect); + } +} + +static int +awin_mmc_write_protect(sdmmc_chipset_handle_t sch) +{ + struct awin_mmc_softc *sc = sch; + + if (sc->sc_has_gpio_wp == false) { + return 0; /* no write protect pin, assume rw */ + } else { + return awin_gpio_pindata_read(&sc->sc_gpio_wp); + } +} + +static int +awin_mmc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr) +{ + return 0; +} + +static int +awin_mmc_update_clock(struct awin_mmc_softc *sc) +{ + uint32_t cmd; + int retry; + + cmd = __BIT(31) | __BIT(21) | __BIT(13); + MMC_WRITE(sc, AWIN_MMC_CMD, cmd); + retry = 0xfffff; + while (--retry > 0) { + if (!(MMC_READ(sc, AWIN_MMC_CMD) & __BIT(31))) + break; + delay(10); + } + if (retry == 0) { + aprint_error_dev(sc->sc_dev, "timeout updating clk\n"); + return ETIMEDOUT; + } + MMC_WRITE(sc, AWIN_MMC_RINT, MMC_READ(sc, AWIN_MMC_RINT)); + return 0; +} + +static int +awin_mmc_bus_clock(sdmmc_chipset_handle_t sch, int freq) +{ + struct awin_mmc_softc *sc = sch; + unsigned int div, freq_hz; + uint32_t clkcr; + +#ifdef AWIN_MMC_DEBUG + aprint_normal_dev(sc->sc_dev, "freq = %d\n", freq); +#endif + + clkcr = MMC_READ(sc, AWIN_MMC_CLKCR); + clkcr &= ~__BIT(16); + MMC_WRITE(sc, AWIN_MMC_CLKCR, clkcr); + if (awin_mmc_update_clock(sc) != 0) + return 1; + + if (freq) { + freq_hz = freq * 1000; + div = (sc->sc_mod_clk + (freq_hz >> 1)) / freq_hz / 2; + + clkcr &= ~__BITS(15,0); + clkcr |= div; + MMC_WRITE(sc, AWIN_MMC_CLKCR, clkcr); + if (awin_mmc_update_clock(sc) != 0) + return 1; + + clkcr |= __BIT(16); + MMC_WRITE(sc, AWIN_MMC_CLKCR, clkcr); + if (awin_mmc_update_clock(sc) != 0) + return 1; + } + + return 0; +} + +static int +awin_mmc_bus_width(sdmmc_chipset_handle_t sch, int width) +{ + struct awin_mmc_softc *sc = sch; + +#ifdef AWIN_MMC_DEBUG + aprint_normal_dev(sc->sc_dev, "width = %d\n", width); +#endif + + switch (width) { + case 1: + MMC_WRITE(sc, AWIN_MMC_WIDTH, 0); + break; + case 4: + MMC_WRITE(sc, AWIN_MMC_WIDTH, 1); + break; + case 8: + MMC_WRITE(sc, AWIN_MMC_WIDTH, 2); + break; + default: + return 1; + } + + sc->sc_mmc_width = width; + + return 0; +} + +static int +awin_mmc_bus_rod(sdmmc_chipset_handle_t sch, int on) +{ + return -1; +} + +static int +awin_mmc_xfer_wait(struct awin_mmc_softc *sc, struct sdmmc_command *cmd) +{ + int retry = 0xfffff; + uint32_t bit = (cmd->c_flags & SCF_CMD_READ) ? __BIT(2) : __BIT(3); + + while (--retry > 0) { + uint32_t status = MMC_READ(sc, AWIN_MMC_STATUS); + if (!(status & bit)) + return 0; + delay(10); + } + + return ETIMEDOUT; +} + +static int +awin_mmc_xfer_data(struct awin_mmc_softc *sc, struct sdmmc_command *cmd) +{ + uint32_t *datap = (uint32_t *)cmd->c_buf; + int i; + + for (i = 0; i < (cmd->c_resid >> 2); i++) { + if (awin_mmc_xfer_wait(sc, cmd)) + return ETIMEDOUT; + if (cmd->c_flags & SCF_CMD_READ) { + datap[i] = MMC_READ(sc, AWIN_MMC_FIFO); + } else { + MMC_WRITE(sc, AWIN_MMC_FIFO, datap[i]); + } + } + + return 0; +} + +static void +awin_mmc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd) +{ + struct awin_mmc_softc *sc = sch; + uint32_t cmdval = __BIT(31); + uint32_t status; + int retry; + +#ifdef AWIN_MMC_DEBUG + aprint_normal_dev(sc->sc_dev, + "opcode %d flags 0x%x data %p datalen %d\n", + cmd->c_opcode, cmd->c_flags, cmd->c_data, cmd->c_datalen); +#endif + + if (cmd->c_opcode == 0) + cmdval |= __BIT(15); + if (cmd->c_flags & SCF_RSP_PRESENT) + cmdval |= __BIT(6); + if (cmd->c_flags & SCF_RSP_136) + cmdval |= __BIT(7); + if (cmd->c_flags & SCF_RSP_CRC) + cmdval |= __BIT(8); + + if (cmd->c_datalen > 0) { + unsigned int nblks; + + cmdval |= __BIT(9) | __BIT(13); + if (!ISSET(cmd->c_flags, SCF_CMD_READ)) { + cmdval |= __BIT(10); + } + + nblks = cmd->c_datalen / cmd->c_blklen; + if (nblks == 0 || (cmd->c_datalen % cmd->c_blklen) != 0) + ++nblks; + + if (nblks > 1) { + cmdval |= __BIT(12); + } + + MMC_WRITE(sc, AWIN_MMC_BLKSZ, cmd->c_blklen); + MMC_WRITE(sc, AWIN_MMC_BYTECNT, nblks * cmd->c_blklen); + } + + MMC_WRITE(sc, AWIN_MMC_ARG, cmd->c_arg); + +#ifdef AWIN_MMC_DEBUG + aprint_normal_dev(sc->sc_dev, "cmdval = %08x\n", cmdval); +#endif + if (cmd->c_datalen == 0) { + MMC_WRITE(sc, AWIN_MMC_CMD, cmdval | cmd->c_opcode); + } else { + MMC_WRITE(sc, AWIN_MMC_GCTRL, + MMC_READ(sc, AWIN_MMC_GCTRL) | __BIT(31)); + MMC_WRITE(sc, AWIN_MMC_CMD, cmdval | cmd->c_opcode); + cmd->c_resid = cmd->c_datalen; + cmd->c_buf = cmd->c_data; + cmd->c_error = awin_mmc_xfer_data(sc, cmd); + if (cmd->c_error) { + aprint_error_dev(sc->sc_dev, + "xfer data timeout\n"); + goto done; + } + } + + retry = 0xfffff; + while (--retry > 0) { + status = MMC_READ(sc, AWIN_MMC_RINT); + if (status & 0xbfc2) { + retry = 0; + break; + } + if (status & 0x4) + break; + delay(10); + } + if (retry == 0) { +#ifdef AWIN_MMC_DEBUG + aprint_error_dev(sc->sc_dev, + "RINT (1) timeout, status = %08x\n", status); +#endif + cmd->c_error = ETIMEDOUT; + goto done; + } +#ifdef AWIN_MMC_DEBUG + aprint_normal_dev(sc->sc_dev, "status = %08x\n", status); +#endif + + if (cmd->c_datalen > 0) { + retry = 0xffff; + while (--retry > 0) { + uint32_t done; + status = MMC_READ(sc, AWIN_MMC_RINT); + if (status & 0xbfc2) { + retry = 0; + break; + } + if (cmd->c_blklen < cmd->c_datalen) + done = status & __BIT(14); + else + done = status & __BIT(3); + if (done) + break; + delay(10); + } + if (retry == 0) { +#ifdef AWIN_MMC_DEBUG + aprint_error_dev(sc->sc_dev, + "RINT (2) timeout, status = %08x\n", status); +#endif + cmd->c_error = ETIMEDOUT; + goto done; + } + } + + if (cmd->c_flags & SCF_RSP_BSY) { + retry = 0xfffff; + while (--retry > 0) { + status = MMC_READ(sc, AWIN_MMC_STATUS); + if (status & __BIT(9)) + break; + } + if (retry == 0) { +#ifdef AWIN_MMC_DEBUG + aprint_error_dev(sc->sc_dev, + "RINT (3) timeout, status = %08x\n", status); +#endif + cmd->c_error = ETIMEDOUT; + goto done; + } + } + + if (cmd->c_flags & SCF_RSP_PRESENT) { + if (cmd->c_flags & SCF_RSP_136) { + cmd->c_resp[0] = MMC_READ(sc, AWIN_MMC_RESP0); + cmd->c_resp[1] = MMC_READ(sc, AWIN_MMC_RESP1); + cmd->c_resp[2] = MMC_READ(sc, AWIN_MMC_RESP2); + cmd->c_resp[3] = MMC_READ(sc, AWIN_MMC_RESP3); + 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, AWIN_MMC_RESP0); + } + } + +done: + cmd->c_flags |= SCF_ITSDONE; + + if (cmd->c_error) { + MMC_WRITE(sc, AWIN_MMC_GCTRL, __BITS(2,0)); + awin_mmc_update_clock(sc); + } + MMC_WRITE(sc, AWIN_MMC_RINT, __BITS(31,0)); + MMC_WRITE(sc, AWIN_MMC_GCTRL, MMC_READ(sc, AWIN_MMC_GCTRL) | __BIT(1)); +} + +static void +awin_mmc_card_enable_intr(sdmmc_chipset_handle_t sch, int enable) +{ +} + +static void +awin_mmc_card_intr_ack(sdmmc_chipset_handle_t sch) +{ +}