From c9cd55383864db2c5f5c4cd1259110d4a66b6895 Mon Sep 17 00:00:00 2001 From: kiyohara Date: Thu, 23 Sep 2010 12:36:01 +0000 Subject: [PATCH] Add Marvell Secure Digital Input/Output (SDIO) Interface Driver. --- sys/dev/marvell/files.discovery | 8 +- sys/dev/marvell/mvsdio.c | 717 ++++++++++++++++++++++++++++++++ sys/dev/marvell/mvsdioreg.h | 179 ++++++++ 3 files changed, 903 insertions(+), 1 deletion(-) create mode 100644 sys/dev/marvell/mvsdio.c create mode 100644 sys/dev/marvell/mvsdioreg.h diff --git a/sys/dev/marvell/files.discovery b/sys/dev/marvell/files.discovery index 2f92261676ee..9e61da533dbe 100644 --- a/sys/dev/marvell/files.discovery +++ b/sys/dev/marvell/files.discovery @@ -1,4 +1,4 @@ -# $NetBSD: files.discovery,v 1.16 2010/07/13 13:05:05 kiyohara Exp $ +# $NetBSD: files.discovery,v 1.17 2010/09/23 12:36:01 kiyohara Exp $ # # Config file and device description for machine-independent support for # the Marvell (formerly Galileo Technology) Discovery system controllers. @@ -89,3 +89,9 @@ file dev/marvell/com_mv.c mvuart_gt | mvuart_mbus device gtidmac: dmover_service file dev/marvell/gtidmac.c gtidmac attach gtidmac at gt with gtidmac_gt + +# Secure Digital Input/Output (SDIO) Interface +device mvsdio: sdmmcbus +file dev/marvell/mvsdio.c mvsdio +defparam opt_mvsdio.h MVSDIO_CARD_DETECT + MVSDIO_WRITE_PROTECT diff --git a/sys/dev/marvell/mvsdio.c b/sys/dev/marvell/mvsdio.c new file mode 100644 index 000000000000..4b4687830cc0 --- /dev/null +++ b/sys/dev/marvell/mvsdio.c @@ -0,0 +1,717 @@ +/* $NetBSD: mvsdio.c,v 1.1 2010/09/23 12:36:01 kiyohara Exp $ */ +/* + * Copyright (c) 2010 KIYOHARA Takashi + * 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 +__KERNEL_RCSID(0, "$NetBSD: mvsdio.c,v 1.1 2010/09/23 12:36:01 kiyohara Exp $"); + +#include "opt_mvsdio.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +//#define MVSDIO_DEBUG 1 +#ifdef MVSDIO_DEBUG +#define DPRINTF(n, x) if (mvsdio_debug >= (n)) printf x +int mvsdio_debug = MVSDIO_DEBUG; +#else +#define DPRINTF(n, x) +#endif + +struct mvsdio_softc { + device_t sc_dev; + device_t sc_sdmmc; + + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + bus_dma_tag_t sc_dmat; + + struct kmutex sc_mtx; + kcondvar_t sc_cv; + + struct sdmmc_command *sc_exec_cmd; + uint32_t sc_waitintr; +}; + +static int mvsdio_match(device_t, struct cfdata *, void *); +static void mvsdio_attach(device_t, device_t, void *); + +static int mvsdio_intr(void *); + +static int mvsdio_host_reset(sdmmc_chipset_handle_t); +static uint32_t mvsdio_host_ocr(sdmmc_chipset_handle_t); +static int mvsdio_host_maxblklen(sdmmc_chipset_handle_t); +#ifdef MVSDIO_CARD_DETECT +int MVSDIO_CARD_DETECT(sdmmc_chipset_handle_t); +#else +static int mvsdio_card_detect(sdmmc_chipset_handle_t); +#endif +#ifdef MVSDIO_WRITE_PROTECT +int MVSDIO_WRITE_PROTECT(sdmmc_chipset_handle_t); +#else +static int mvsdio_write_protect(sdmmc_chipset_handle_t); +#endif +static int mvsdio_bus_power(sdmmc_chipset_handle_t, uint32_t); +static int mvsdio_bus_clock(sdmmc_chipset_handle_t, int); +static int mvsdio_bus_width(sdmmc_chipset_handle_t, int); +static int mvsdio_bus_rod(sdmmc_chipset_handle_t, int); +static void mvsdio_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *); +static void mvsdio_card_enable_intr(sdmmc_chipset_handle_t, int); +static void mvsdio_card_intr_ack(sdmmc_chipset_handle_t); + +static void mvsdio_wininit(struct mvsdio_softc *); + +static struct sdmmc_chip_functions mvsdio_chip_functions = { + /* host controller reset */ + .host_reset = mvsdio_host_reset, + + /* host controller capabilities */ + .host_ocr = mvsdio_host_ocr, + .host_maxblklen = mvsdio_host_maxblklen, + + /* card detection */ +#ifdef MVSDIO_CARD_DETECT + .card_detect = MVSDIO_CARD_DETECT, +#else + .card_detect = mvsdio_card_detect, +#endif + + /* write protect */ +#ifdef MVSDIO_WRITE_PROTECT + .write_protect = MVSDIO_WRITE_PROTECT, +#else + .write_protect = mvsdio_write_protect, +#endif + + /* bus power, clock frequency, width, rod */ + .bus_power = mvsdio_bus_power, + .bus_clock = mvsdio_bus_clock, + .bus_width = mvsdio_bus_width, + .bus_rod = mvsdio_bus_rod, + + /* command execution */ + .exec_command = mvsdio_exec_command, + + /* card interrupt */ + .card_enable_intr = mvsdio_card_enable_intr, + .card_intr_ack = mvsdio_card_intr_ack, +}; + +CFATTACH_DECL_NEW(mvsdio_mbus, sizeof(struct mvsdio_softc), + mvsdio_match, mvsdio_attach, NULL, NULL); + + +/* ARGSUSED */ +static int +mvsdio_match(device_t parent, struct cfdata *match, void *aux) +{ + struct marvell_attach_args *mva = aux; + + if (strcmp(mva->mva_name, match->cf_name) != 0) + return 0; + if (mva->mva_offset == MVA_OFFSET_DEFAULT) + return 0; + + mva->mva_size = MVSDIO_SIZE; + return 1; +} + +/* ARGSUSED */ +static void +mvsdio_attach(device_t parent, device_t self, void *aux) +{ + struct mvsdio_softc *sc = device_private(self); + struct marvell_attach_args *mva = aux; + struct sdmmcbus_attach_args saa; + uint32_t nis, eis; + + aprint_naive("\n"); + aprint_normal(": Marvell Secure Digital Input/Output Interface\n"); + + sc->sc_dev = self; + sc->sc_iot = mva->mva_iot; + if (bus_space_subregion(mva->mva_iot, mva->mva_ioh, mva->mva_offset, + mva->mva_size, &sc->sc_ioh)) { + aprint_error_dev(self, "Cannot map registers\n"); + return; + } + sc->sc_dmat = mva->mva_dmat; + + mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_SDMMC); + cv_init(&sc->sc_cv, "mvsdio_intr"); + + sc->sc_exec_cmd = NULL; + sc->sc_waitintr = 0; + + marvell_intr_establish(mva->mva_irq, IPL_SDMMC, mvsdio_intr, sc); + + mvsdio_wininit(sc); + +#if BYTE_ORDER == LITTLE_ENDIAN + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_HC, HC_BIGENDIAN); +#else + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_HC, HC_LSBFIRST); +#endif + nis = + NIS_CMDCOMPLETE /* Command Complete */ | + NIS_XFERCOMPLETE /* Transfer Complete */ | + NIS_BLOCKGAPEV /* Block gap event */ | + NIS_DMAINT /* DMA interrupt */ | + NIS_CARDINT /* Card interrupt */ | + NIS_READWAITON /* Read Wait state is on */ | + NIS_SUSPENSEON | + NIS_AUTOCMD12COMPLETE /* Auto_cmd12 is comp */| + NIS_UNEXPECTEDRESPDET | + NIS_ERRINT; /* Error interrupt */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_NIS, nis); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_NISE, nis); + +#define NIC_DYNAMIC_CONFIG_INTRS (NIS_CMDCOMPLETE | \ + NIS_XFERCOMPLETE | \ + NIS_DMAINT | \ + NIS_CARDINT | \ + NIS_AUTOCMD12COMPLETE) + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_NISIE, + nis & ~NIC_DYNAMIC_CONFIG_INTRS); + + eis = + EIS_CMDTIMEOUTERR /*Command timeout err*/ | + EIS_CMDCRCERR /* Command CRC Error */ | + EIS_CMDENDBITERR /*Command end bit err*/ | + EIS_CMDINDEXERR /*Command Index Error*/ | + EIS_DATATIMEOUTERR /* Data timeout error */ | + EIS_RDDATACRCERR /* Read data CRC err */ | + EIS_RDDATAENDBITERR /*Rd data end bit err*/ | + EIS_AUTOCMD12ERR /* Auto CMD12 error */ | + EIS_CMDSTARTBITERR /*Cmd start bit error*/ | + EIS_XFERSIZEERR /*Tx size mismatched err*/ | + EIS_RESPTBITERR /* Response T bit err */ | + EIS_CRCENDBITERR /* CRC end bit error */ | + EIS_CRCSTARTBITERR /* CRC start bit err */ | + EIS_CRCSTATERR; /* CRC status error */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_EIS, eis); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_EISE, eis); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_EISIE, eis); + + /* + * Attach the generic SD/MMC bus driver. (The bus driver must + * not invoke any chipset functions before it is attached.) + */ + memset(&saa, 0, sizeof(saa)); + saa.saa_busname = "sdmmc"; + saa.saa_sct = &mvsdio_chip_functions; + saa.saa_sch = sc; + saa.saa_dmat = sc->sc_dmat; + saa.saa_clkmin = 100; /* XXXX: 100 kHz */ + saa.saa_clkmax = MVSDIO_MAX_CLOCK; + saa.saa_caps = SMC_CAPS_AUTO_STOP | SMC_CAPS_4BIT_MODE | SMC_CAPS_DMA; +#ifndef MVSDIO_CARD_DETECT + saa.saa_caps |= SMC_CAPS_POLL_CARD_DET; +#endif + sc->sc_sdmmc = config_found(sc->sc_dev, &saa, NULL); +} + +static int +mvsdio_intr(void *arg) +{ + struct mvsdio_softc *sc = (struct mvsdio_softc *)arg; + struct sdmmc_command *cmd = sc->sc_exec_cmd; + uint32_t nis, eis; + int handled = 0, error; + + nis = bus_space_read_4(sc->sc_iot, sc->sc_ioh, MVSDIO_NIS); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_NIS, nis); + + DPRINTF(3, ("%s: intr: NIS=0x%x, NISE=0x%x, NISIE=0x%x\n", + __func__, nis, + bus_space_read_4(sc->sc_iot, sc->sc_ioh, MVSDIO_NISE), + bus_space_read_4(sc->sc_iot, sc->sc_ioh, MVSDIO_NISIE))); + + if (__predict_false(nis & NIS_ERRINT)) { + sc->sc_exec_cmd = NULL; + eis = bus_space_read_4(sc->sc_iot, sc->sc_ioh, MVSDIO_EIS); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_EIS, eis); + + DPRINTF(3, (" EIS=0x%x, EISE=0x%x, EISIE=0x%x\n", + eis, + bus_space_read_4(sc->sc_iot, sc->sc_ioh, MVSDIO_EISE), + bus_space_read_4(sc->sc_iot, sc->sc_ioh, MVSDIO_EISIE))); + + if (eis & (EIS_CMDTIMEOUTERR | EIS_DATATIMEOUTERR)) { + error = ETIMEDOUT; /* Timeouts */ + DPRINTF(2, (" Command/Data Timeout (0x%x)\n", + eis & (EIS_CMDTIMEOUTERR | EIS_DATATIMEOUTERR))); + } else { + +#define CRC_ERROR (EIS_CMDCRCERR | \ + EIS_RDDATACRCERR | \ + EIS_CRCENDBITERR | \ + EIS_CRCSTARTBITERR | \ + EIS_CRCSTATERR) + if (eis & CRC_ERROR) { + error = EIO; /* CRC errors */ + aprint_error_dev(sc->sc_dev, + "CRC Error (0x%x)\n", eis & CRC_ERROR); + } + +#define COMMAND_ERROR (EIS_CMDENDBITERR | \ + EIS_CMDINDEXERR | \ + EIS_CMDSTARTBITERR) + if (eis & COMMAND_ERROR) { + error = EIO; /*Other command errors*/ + aprint_error_dev(sc->sc_dev, + "Command Error (0x%x)\n", + eis & COMMAND_ERROR); + } + +#define MISC_ERROR (EIS_RDDATAENDBITERR | \ + EIS_AUTOCMD12ERR | \ + EIS_XFERSIZEERR | \ + EIS_RESPTBITERR) + if (eis & MISC_ERROR) { + error = EIO; /* Misc error */ + aprint_error_dev(sc->sc_dev, + "Misc Error (0x%x)\n", eis & MISC_ERROR); + } + } + + if (cmd != NULL) { + cmd->c_error = error; + cv_signal(&sc->sc_cv); + } + handled = 1; + } else if (cmd != NULL && + ((nis & sc->sc_waitintr) || (nis & NIS_UNEXPECTEDRESPDET))) { + sc->sc_exec_cmd = NULL; + sc->sc_waitintr = 0; + if (cmd->c_flags & SCF_RSP_PRESENT) { + uint16_t rh[MVSDIO_NRH + 1]; + int i, j; + + if (cmd->c_flags & SCF_RSP_136) { + for (i = 0; i < MVSDIO_NRH; i++) + rh[i + 1] = bus_space_read_4(sc->sc_iot, + sc->sc_ioh, MVSDIO_RH(i)); + rh[0] = 0; + for (j = 3, i = 1; j >= 0; j--, i += 2) { + cmd->c_resp[j] = + rh[i - 1] << 30 | + rh[i + 0] << 14 | + rh[i + 1] >> 2; + } + cmd->c_resp[3] &= 0x00ffffff; + } else { + for (i = 0; i < 3; i++) + rh[i] = bus_space_read_4(sc->sc_iot, + sc->sc_ioh, MVSDIO_RH(i)); + cmd->c_resp[0] = + ((rh[0] & 0x03ff) << 22) | + ((rh[1] ) << 6) | + ((rh[2] & 0x003f) << 0); + cmd->c_resp[1] = (rh[0] & 0xfc00) >> 10; + cmd->c_resp[2] = 0; + cmd->c_resp[3] = 0; + } + } + if (nis & NIS_UNEXPECTEDRESPDET) + cmd->c_error = EIO; + cv_signal(&sc->sc_cv); + } + + if (nis & NIS_CARDINT) + if (bus_space_read_4(sc->sc_iot, sc->sc_ioh, MVSDIO_NISIE) & + NIS_CARDINT) { + sdmmc_card_intr(sc->sc_sdmmc); + handled = 1; + } + + return handled; +} + +static int +mvsdio_host_reset(sdmmc_chipset_handle_t sch) +{ + struct mvsdio_softc *sc = (struct mvsdio_softc *)sch; + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_SR, SR_SWRESET); + return 0; +} + +static uint32_t +mvsdio_host_ocr(sdmmc_chipset_handle_t sch) +{ + + return MMC_OCR_3_3V_3_4V | MMC_OCR_3_2V_3_3V; +} + +static int +mvsdio_host_maxblklen(sdmmc_chipset_handle_t sch) +{ + + return DBS_BLOCKSIZE_MAX; +} + +#ifndef MVSDIO_CARD_DETECT +static int +mvsdio_card_detect(sdmmc_chipset_handle_t sch) +{ + struct mvsdio_softc *sc __unused = (struct mvsdio_softc *)sch; + + DPRINTF(2, ("%s: driver lacks card_detect() function.\n", + device_xname(sc->sc_dev))); + return 1; /* always detect */ +} +#endif + +#ifndef MVSDIO_WRITE_PROTECT +static int +mvsdio_write_protect(sdmmc_chipset_handle_t sch) +{ + + /* Nothing */ + + return 0; +} +#endif + +static int +mvsdio_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr) +{ + struct mvsdio_softc *sc = (struct mvsdio_softc *)sch; + uint32_t reg; + + /* Initial state is Open Drain on CMD line. */ + mutex_enter(&sc->sc_mtx); + reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, MVSDIO_HC); + reg &= ~HC_PUSHPULLEN; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_HC, reg); + mutex_exit(&sc->sc_mtx); + + return 0; +} + +static int +mvsdio_bus_clock(sdmmc_chipset_handle_t sch, int freq) +{ + struct mvsdio_softc *sc = (struct mvsdio_softc *)sch; + uint32_t reg; + int m; + + mutex_enter(&sc->sc_mtx); + reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, MVSDIO_TM); + + /* Just stop the clock. */ + if (freq == 0) { + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_TM, + reg | TM_STOPCLKEN); + goto out; + } + +#define FREQ_TO_M(f) (100000 / (f) - 1) + + m = FREQ_TO_M(freq); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_CDV, + m & CDV_CLKDVDRMVALUE_MASK); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_TM, + reg & ~TM_STOPCLKEN); + + reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, MVSDIO_HC); + if (freq > 25000) + reg |= HC_HISPEEDEN; + else + reg &= ~HC_HISPEEDEN; /* up to 25 MHz */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_HC, reg); + +out: + mutex_exit(&sc->sc_mtx); + + return 0; +} + +static int +mvsdio_bus_width(sdmmc_chipset_handle_t sch, int width) +{ + struct mvsdio_softc *sc = (struct mvsdio_softc *)sch; + uint32_t reg, v; + + switch (width) { + case 1: + v = 0; + break; + + case 4: + v = HC_DATAWIDTH; + break; + + default: + DPRINTF(0, ("%s: unsupported bus width (%d)\n", + device_xname(sc->sc_dev), width)); + return EINVAL; + } + + mutex_enter(&sc->sc_mtx); + reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, MVSDIO_HC); + reg &= ~HC_DATAWIDTH; + reg |= v; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_HC, reg); + mutex_exit(&sc->sc_mtx); + + return 0; +} + +static int +mvsdio_bus_rod(sdmmc_chipset_handle_t sch, int on) +{ + struct mvsdio_softc *sc = (struct mvsdio_softc *)sch; + uint32_t reg; + + /* Change Open-drain/Push-pull. */ + mutex_enter(&sc->sc_mtx); + reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, MVSDIO_HC); + if (on) + reg &= ~HC_PUSHPULLEN; + else + reg |= HC_PUSHPULLEN; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_HC, reg); + mutex_exit(&sc->sc_mtx); + + return 0; +} + +static void +mvsdio_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd) +{ + struct mvsdio_softc *sc = (struct mvsdio_softc *)sch; + uint32_t tm, c, hc, aacc, nisie, wait; + int blklen; + + DPRINTF(1, ("%s: start cmd %d arg=%#x data=%p dlen=%d flags=%#x\n", + device_xname(sc->sc_dev), cmd->c_opcode, cmd->c_arg, cmd->c_data, + cmd->c_datalen, cmd->c_flags)); + + mutex_enter(&sc->sc_mtx); + + tm = bus_space_read_4(sc->sc_iot, sc->sc_ioh, MVSDIO_TM); + + if (cmd->c_datalen > 0) { + bus_dma_segment_t *dm_seg = + &cmd->c_dmamap->dm_segs[cmd->c_dmaseg]; + bus_addr_t ds_addr = dm_seg->ds_addr + cmd->c_dmaoff; + + blklen = MIN(cmd->c_datalen, cmd->c_blklen); + + if (cmd->c_datalen % blklen > 0) { + aprint_error_dev(sc->sc_dev, + "data not a multiple of %u bytes\n", blklen); + cmd->c_error = EINVAL; + goto out; + } + if ((uint32_t)cmd->c_data & 0x3) { + aprint_error_dev(sc->sc_dev, + "data not 4byte aligned\n"); + cmd->c_error = EINVAL; + goto out; + } + + /* Set DMA Buffer Address */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_DMABA16LSB, + ds_addr & 0xffff); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_DMABA16MSB, + (ds_addr >> 16) & 0xffff); + + /* Set Data Block Size and Count */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_DBS, + DBS_BLOCKSIZE(blklen)); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_DBC, + DBC_BLOCKCOUNT(cmd->c_datalen / blklen)); + + tm &= ~TM_HOSTXFERMODE; /* Always DMA */ + if (cmd->c_flags & SCF_CMD_READ) + tm |= TM_DATAXFERTOWARDHOST; + else + tm &= ~TM_DATAXFERTOWARDHOST; + tm |= TM_HWWRDATAEN; + wait = NIS_XFERCOMPLETE; + } else { + tm &= ~TM_HWWRDATAEN; + wait = NIS_CMDCOMPLETE; + } + + /* Set Argument in Command */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_AC16LSB, + cmd->c_arg & 0xffff); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_AC16MSB, + (cmd->c_arg >> 16) & 0xffff); + + /* Set Host Control, exclude PushPullEn, DataWidth, HiSpeedEn. */ + hc = bus_space_read_4(sc->sc_iot, sc->sc_ioh, MVSDIO_HC); + hc |= (HC_TIMEOUTVALUE_MAX | HC_TIMEOUTEN); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_HC, hc); + + /* Data Block Gap Control: Resume */ + + /* Clock Control: SclkMasterEn */ + + if (cmd->c_opcode == MMC_READ_BLOCK_MULTIPLE || + cmd->c_opcode == MMC_WRITE_BLOCK_MULTIPLE) { + aacc = 0; +#if 1 /* XXXX: need? */ + if (cmd->c_opcode == MMC_READ_BLOCK_MULTIPLE) { + struct sdmmc_softc *sdmmc = + device_private(sc->sc_sdmmc); + struct sdmmc_function *sf = sdmmc->sc_card; + + aacc = MMC_ARG_RCA(sf->rca); + } +#endif + + /* Set Argument in Auto Cmd12 Command */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_AACC16LSBT, + aacc & 0xffff); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_AACC16MSBT, + (aacc >> 16) & 0xffff); + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_IACCT, + IACCT_AUTOCMD12BUSYCHKEN | + IACCT_AUTOCMD12INDEXCHKEN | + IACCT_AUTOCMD12INDEX); + + tm |= TM_AUTOCMD12EN; + wait = NIS_AUTOCMD12COMPLETE; + } else + tm &= ~TM_AUTOCMD12EN; + + tm |= TM_INTCHKEN; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_TM, tm); + + c = C_CMDINDEX(cmd->c_opcode); + if (cmd->c_flags & SCF_RSP_PRESENT) { + if (cmd->c_flags & SCF_RSP_136) + c |= C_RESPTYPE_136BR; + else if (!(cmd->c_flags & SCF_RSP_BSY)) + c |= C_RESPTYPE_48BR; + else + c |= C_RESPTYPE_48BRCB; + c |= C_UNEXPECTEDRESPEN; + } else + c |= C_RESPTYPE_NR; + if (cmd->c_flags & SCF_RSP_CRC) + c |= C_CMDCRCCHKEN; + if (cmd->c_flags & SCF_RSP_IDX) + c |= C_CMDINDEXCHKEN; + if (cmd->c_datalen > 0) + c |= (C_DATAPRESENT | C_DATACRC16CHKEN); + + DPRINTF(2, ("%s: TM=0x%x, C=0x%x, HC=0x%x\n", __func__, tm, c, hc)); + + nisie = bus_space_read_4(sc->sc_iot, sc->sc_ioh, MVSDIO_NISIE); + nisie &= ~(NIS_CMDCOMPLETE | NIS_XFERCOMPLETE | NIS_AUTOCMD12COMPLETE); + nisie |= wait; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_NISIE, nisie); + + /* Execute command */ + sc->sc_exec_cmd = cmd; + sc->sc_waitintr = wait; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_C, c); + + /* Wait interrupt for complete or error or timeout */ + while (sc->sc_exec_cmd == cmd) + cv_wait(&sc->sc_cv, &sc->sc_mtx); + +out: + mutex_exit(&sc->sc_mtx); + + DPRINTF(1, ("%s: cmd %d done (flags=%08x error=%d)\n", + device_xname(sc->sc_dev), + cmd->c_opcode, cmd->c_flags, cmd->c_error)); +} + +static void +mvsdio_card_enable_intr(sdmmc_chipset_handle_t sch, int enable) +{ + struct mvsdio_softc *sc = (struct mvsdio_softc *)sch; + uint32_t reg; + + mutex_enter(&sc->sc_mtx); + reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, MVSDIO_NISIE); + reg |= NIS_CARDINT; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_NISIE, reg); + mutex_exit(&sc->sc_mtx); +} + +static void +mvsdio_card_intr_ack(sdmmc_chipset_handle_t sch) +{ + + /* Nothing */ +} + + +static void +mvsdio_wininit(struct mvsdio_softc *sc) +{ + uint64_t base; + uint32_t size; + int window, target, attr, rv, i; + static int tags[] = { + MARVELL_TAG_SDRAM_CS0, + MARVELL_TAG_SDRAM_CS1, + MARVELL_TAG_SDRAM_CS2, + MARVELL_TAG_SDRAM_CS3, + + MARVELL_TAG_UNDEFINED, + }; + + for (window = 0, i = 0; + tags[i] != MARVELL_TAG_UNDEFINED && window < MVSDIO_NWINDOW; i++) { + rv = marvell_winparams_by_tag(sc->sc_dev, tags[i], + &target, &attr, &base, &size); + if (rv != 0 || size == 0) + continue; + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_WC(window), + WC_WINEN | + WC_TARGET(target) | + WC_ATTR(attr) | + WC_SIZE(size)); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_WB(window), + WB_BASE(base)); + window++; + } + for (; window < MVSDIO_NWINDOW; window++) + bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVSDIO_WC(window), 0); +} diff --git a/sys/dev/marvell/mvsdioreg.h b/sys/dev/marvell/mvsdioreg.h new file mode 100644 index 000000000000..df836ac9ca98 --- /dev/null +++ b/sys/dev/marvell/mvsdioreg.h @@ -0,0 +1,179 @@ +/* $NetBSD: mvsdioreg.h,v 1.1 2010/09/23 12:36:01 kiyohara Exp $ */ +/* + * Copyright (c) 2010 KIYOHARA Takashi + * 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 _MVSDIOREG_H_ +#define _MVSDIOREG_H_ + +#define MVSDIO_SIZE 0x10000 + +#define MVSDIO_MAX_CLOCK (50 * 1000) /* 50,000 kHz */ + +#define MVSDIO_DMABA16LSB 0x0000 /* DMA Buffer Address 16 LSB */ +#define MVSDIO_DMABA16MSB 0x0004 /* DMA Buffer Address 16 MSB */ +#define MVSDIO_DBS 0x0008 /* Data Block Size */ +#define DBS_BLOCKSIZE_MASK 0xfff +#define DBS_BLOCKSIZE(s) ((s) & DBS_BLOCKSIZE_MASK) +#define DBS_BLOCKSIZE_MAX 0x800 +#define MVSDIO_DBC 0x000c /* Data Block Count */ +#define DBC_BLOCKCOUNT_MASK 0xfff +#define DBC_BLOCKCOUNT(c) ((c) & DBC_BLOCKCOUNT_MASK) +#define MVSDIO_AC16LSB 0x0010 /* Argument in Command 16 LSB */ +#define MVSDIO_AC16MSB 0x0014 /* Argument in Command 16 MSB */ +#define MVSDIO_TM 0x0018 /* Transfer Mode */ +#define TM_SWWRDATASTART (1 << 0) /* InitWrDataXfer */ +#define TM_HWWRDATAEN (1 << 1) /* SWInitWrDataXfer */ +#define TM_AUTOCMD12EN (1 << 2) /* SWIssuesCMD12 */ +#define TM_INTCHKEN (1 << 3) /* CheckInterrupts */ +#define TM_DATAXFERTOWARDHOST (1 << 4) /* XferDataToSDHost */ +#define TM_STOPCLKEN (1 << 5) /* StopSDClocks */ +#define TM_HOSTXFERMODE (1 << 6) /* SW Write */ +#define MVSDIO_C 0x001c /* Command */ +#define C_RESPTYPE_NR (0 << 0) /* NoResponse */ +#define C_RESPTYPE_136BR (1 << 0) /* 136BitResponse */ +#define C_RESPTYPE_48BR (2 << 0) /* 48BitResponse */ +#define C_RESPTYPE_48BRCB (3 << 0) /*48BitResponseChkBusy*/ +#define C_DATACRC16CHKEN (1 << 2) /* EnableCrc16Chk */ +#define C_CMDCRCCHKEN (1 << 3) /* HCCrcChk */ +#define C_CMDINDEXCHKEN (1 << 4) /* HCChkIndex */ +#define C_DATAPRESENT (1 << 5) /* DataAwaitsTransfer */ +#define C_UNEXPECTEDRESPEN (1 << 7) /* UnexpectedRespEn */ +#define C_CMDINDEX(c) ((c) << 8) +#define MVSDIO_NRH 8 +#define MVSDIO_RH(n) (0x0020 + ((n) << 2)) /* Response Halfword n */ +#define RH_MASK 0xffff +#define MVSDIO_16DWACPU 0x0040 /* 16-bit Data Word Accessed by CPU */ +#define MVSDIO_CRC7lR 0x0044 /* CRC7 of l Response */ +#define CRC7lR_CRC7RESPTOKEN_MASK 0x7f +#define MVSDIO_HPS16LSB 0x0048 /* Host Present State 16 LSB */ +#define HPS16LSB_CMDINHIBITCMD (1 << 0) /* CmdRegWrite */ +#define HPS16LSB_CARDBUSY (1 << 1) /* Card Busy */ +#define HPS16LSB_DATLEVEL(x) (((x) >> 3) & 0xf) /* DAT[3:0] Line Signal Level */ +#define HPS16LSB_CMDLEVEL (1 << 7) /* CMD line Signal Level */ +#define HPS16LSB_TXACTIVE (1 << 8) /* TxEnabled */ +#define HPS16LSB_RXACTIVE (1 << 9) /* RxDisabled */ +#define HPS16LSB_FIFOFULL (1 << 12) /* FIFO Full */ +#define HPS16LSB_FIFOEMPTY (1 << 13) /* FIFO Empty */ +#define HPS16LSB_AUTOCMD12ACTIVE (1 << 14) /*auto_cmd12 is active*/ +#define MVSDIO_HC 0x0050 /* Host Control */ +#define HC_PUSHPULLEN (1 << 0) /* PushPullEn */ +#define HC_CARDTYPE_MASK (3 << 1) /* Card type */ +#define HC_CARDTYPE_MEMORYONLY (0 << 1) /* Mem only SD card */ +#define HC_CARDTYPE_IOONLY (1 << 1) /* IO only SD card */ +#define HC_CARDTYPE_IOMEMCOMBO (2 << 1) /* IO and mem combo */ +#define HC_CARDTYPE_MMC (3 << 1) /* MMC card */ +#define HC_BIGENDIAN (1 << 3) /* BigEndian */ +#define HC_LSBFIRST (1 << 4) /* LSB */ +#define HC_DATAWIDTH (1 << 9) /* Data Width */ +#define HC_HISPEEDEN (1 << 10) /* HighSpeedEnable */ +#define HC_TIMEOUTVALUE_MAX (0xf << 11) +#define HC_TIMEOUTEN (1 << 15) /* Timeout */ +#define MVSDIO_DBGC 0x0054 /* Data Block Gap Control */ +#define DBGC_STOPATBLOCKGAPREQ (1 << 0) /* Stop at block gap request */ +#define DBGC_CONTREQ (1 << 1) /* Continue request */ +#define DBGC_RDWAITCTL (1 << 2) /* EnableRdWait */ +#define DBGC_STOPDATXFER (1 << 3) /* StopDataXferEn */ +#define DBGC_RESUME (1 << 4) +#define DBGC_SUSPEND (1 << 5) +#define MVSDIO_CC 0x0058 /* Clock Control */ +#define CC_SCLKMASTEREN (1 << 0) /* SdclkEn */ +#define MVSDIO_SR 0x005c /* Software Reset */ +#define SR_SWRESET (1 << 8) + +#define MVSDIO_NIS 0x0060 /* Normal Interrupt Status */ +#define MVSDIO_NISE 0x0068 /* Normal Interrupt Status Enable */ +#define MVSDIO_NISIE 0x0070 /* Normal Intr Status Intr Enable */ +#define NIS_CMDCOMPLETE (1 << 0) /* Command Complete */ +#define NIS_XFERCOMPLETE (1 << 1) /* Transfer Complete */ +#define NIS_BLOCKGAPEV (1 << 2) /* Block gap event */ +#define NIS_DMAINT (1 << 3) /* DMA interrupt */ +#define NIS_TXRDY (1 << 4) +#define NIS_RXRDY (1 << 5) +#define NIS_CARDINT (1 << 8) /* Card interrupt */ +#define NIS_READWAITON (1 << 9) /* Read Wait state is on */ +#define NIS_IMBFIFO8WFULL (1 << 10) +#define NIS_IMBFIFO8WAVAIL (1 << 11) +#define NIS_SUSPENSEON (1 << 12) +#define NIS_AUTOCMD12COMPLETE (1 << 13) /* Auto_cmd12 is comp */ +#define NIS_UNEXPECTEDRESPDET (1 << 14) +#define NIS_ERRINT (1 << 15) /* Error interrupt */ +#define MVSDIO_EIS 0x0064 /* Error Interrupt Status */ +#define MVSDIO_EISE 0x006c /* Error Interrupt Status Enable */ +#define MVSDIO_EISIE 0x0074 /* Error Intr Status Interrupt Enable */ +#define EIS_CMDTIMEOUTERR (1 << 0) /*Command timeout err*/ +#define EIS_CMDCRCERR (1 << 1) /* Command CRC Error */ +#define EIS_CMDENDBITERR (1 << 2) /*Command end bit err*/ +#define EIS_CMDINDEXERR (1 << 3) /*Command Index Error*/ +#define EIS_DATATIMEOUTERR (1 << 4) /* Data timeout error */ +#define EIS_RDDATACRCERR (1 << 5) /* Read data CRC err */ +#define EIS_RDDATAENDBITERR (1 << 6) /*Rd data end bit err*/ +#define EIS_AUTOCMD12ERR (1 << 8) /* Auto CMD12 error */ +#define EIS_CMDSTARTBITERR (1 << 9) /*Cmd start bit error*/ +#define EIS_XFERSIZEERR (1 << 10) /*Tx size mismatched err*/ +#define EIS_RESPTBITERR (1 << 11) /* Response T bit err */ +#define EIS_CRCENDBITERR (1 << 12) /* CRC end bit error */ +#define EIS_CRCSTARTBITERR (1 << 13) /* CRC start bit err */ +#define EIS_CRCSTATERR (1 << 14) /* CRC status error */ + +#define MVSDIO_ACMD12IS 0x0078 /* Auto CMD12 Interrupt Status */ +#define ACMD12IS_AUTOCMD12NOTEXE (1 << 0) +#define ACMD12IS_AUTOCMD12TIMEOUTER (1 << 1) +#define ACMD12IS_AUTOCMD12CRCER (1 << 2) +#define ACMD12IS_AUTOCMD12ENDBITER (1 << 3) +#define ACMD12IS_AUTOCMD12INDEXER (1 << 4) +#define ACMD12IS_AUTOCMD12RESPTBITER (1 << 5) +#define ACMD12IS_AUTOCMD12RESPSTARTBITER (1 << 6) +#define MVSDIO_CNBRDB 0x007c/*Current Num of Bytes Remaining in Data*/ +#define MVSDIO_CNDBLBT 0x0080/*Current Num of Data Blk Left ToBe Txed*/ +#define MVSDIO_AACC16LSBT 0x0084 /*Arg in Auto Cmd12 Command 16 LSB Txed*/ +#define MVSDIO_AACC16MSBT 0x0088 /*Arg in Auto Cmd12 Command 16 MSB Txed*/ +#define MVSDIO_IACCT 0x008c /* Index of Auto Cmd12 Commands Tx-ed */ +#define IACCT_AUTOCMD12BUSYCHKEN (1 << 0) +#define IACCT_AUTOCMD12INDEXCHKEN (1 << 1) +#define IACCT_AUTOCMD12INDEX (MMC_STOP_TRANSMISSION << 8) +#define MVSDIO_ACRH(n) (0x0090 + ((n) << 2)) /* Auto Cmd12 Response Halfword n */ + + +#define MVSDIO_MCL 0x0100 /* Mbus Control Low */ +#define MVSDIO_MCH 0x0104 /* Mbus Control High */ +#define MCL_SDARBENTRY(n, x) (((x) & 0xf) << ((n) << 2)) + +#define MVSDIO_NWINDOW 4 +#define MVSDIO_WC(n) (0x0108 + ((n) << 3)) /* Window n Control */ +#define WC_WINEN (1 << 0) /* Window n Enable */ +#define WC_TARGET(t) (((t) & 0xf) << 4) +#define WC_ATTR(a) (((a) & 0xff) << 8) +#define WC_SIZE(s) (((s) - 1) & 0xffff0000) +#define MVSDIO_WB(n) (0x010c + ((n) << 3)) /* Window n Base */ +#define WB_BASE(b) ((b) & 0xffff0000) +#define MVSDIO_CDV 0x0128 /* Clock Divider Value */ +#define CDV_CLKDVDRMVALUE_MASK 0x7ff +#define MVSDIO_ADE 0x012c /* Address Decoder Error */ +#define ADE_ADD_DEC_MISS_ERR (1 << 0) +#define ADE_ADD_DEC_MULTI_ERR (1 << 1) +#define MVSDIO_ADEM 0x0130 /* Address Decoder Error Mask */ +#define ADEM_VARIOUS(x) ((x) << 0) /* Do not mask */ + +#endif /* _MVSDIOREG_H_ */