Module Name: src Committed By: jmcneill Date: Wed Aug 5 10:30:25 UTC 2015
Modified Files: src/sys/dev/sdmmc: sdhc.c sdhcreg.h Log Message: Implement SDHC sampling clock tuning procedure. To generate a diff of this commit: cvs rdiff -u -r1.78 -r1.79 src/sys/dev/sdmmc/sdhc.c cvs rdiff -u -r1.15 -r1.16 src/sys/dev/sdmmc/sdhcreg.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/dev/sdmmc/sdhc.c diff -u src/sys/dev/sdmmc/sdhc.c:1.78 src/sys/dev/sdmmc/sdhc.c:1.79 --- src/sys/dev/sdmmc/sdhc.c:1.78 Wed Aug 5 07:31:52 2015 +++ src/sys/dev/sdmmc/sdhc.c Wed Aug 5 10:30:25 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: sdhc.c,v 1.78 2015/08/05 07:31:52 mlelstv Exp $ */ +/* $NetBSD: sdhc.c,v 1.79 2015/08/05 10:30:25 jmcneill Exp $ */ /* $OpenBSD: sdhc.c,v 1.25 2009/01/13 19:44:20 grange Exp $ */ /* @@ -23,7 +23,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sdhc.c,v 1.78 2015/08/05 07:31:52 mlelstv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sdhc.c,v 1.79 2015/08/05 10:30:25 jmcneill Exp $"); #ifdef _KERNEL_OPT #include "opt_sdmmc.h" @@ -55,6 +55,7 @@ void sdhc_dump_regs(struct sdhc_host *); #define SDHC_BUFFER_TIMEOUT hz #define SDHC_TRANSFER_TIMEOUT hz #define SDHC_DMA_TIMEOUT (hz*3) +#define SDHC_TUNING_TIMEOUT hz struct sdhc_host { struct sdhc_softc *sc; /* host controller device */ @@ -182,6 +183,7 @@ static void sdhc_card_intr_ack(sdmmc_chi static void sdhc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *); static int sdhc_signal_voltage(sdmmc_chipset_handle_t, int); +static int sdhc_execute_tuning(sdmmc_chipset_handle_t, int); static int sdhc_start_command(struct sdhc_host *, struct sdmmc_command *); static int sdhc_wait_state(struct sdhc_host *, uint32_t, uint32_t); static int sdhc_soft_reset(struct sdhc_host *, int); @@ -224,6 +226,7 @@ static struct sdmmc_chip_functions sdhc_ /* UHS functions */ .signal_voltage = sdhc_signal_voltage, .bus_clock_ddr = sdhc_bus_clock_ddr, + .execute_tuning = sdhc_execute_tuning, }; static int @@ -318,11 +321,11 @@ sdhc_host_found(struct sdhc_softc *sc, b caps = sc->sc_caps; caps2 = sc->sc_caps2; } else { - caps = HREAD4(hp, SDHC_CAPABILITIES); + caps = sc->sc_caps = HREAD4(hp, SDHC_CAPABILITIES); if (hp->specver >= SDHC_SPEC_VERS_300) { - caps2 = HREAD4(hp, SDHC_CAPABILITIES2); + caps2 = sc->sc_caps2 = HREAD4(hp, SDHC_CAPABILITIES2); } else { - caps2 = 0; + caps2 = sc->sc_caps2 = 0; } } @@ -1227,6 +1230,97 @@ sdhc_signal_voltage(sdmmc_chipset_handle return 0; } +/* + * Sampling clock tuning procedure (UHS) + */ +static int +sdhc_execute_tuning(sdmmc_chipset_handle_t sch, int timing) +{ + struct sdhc_host *hp = (struct sdhc_host *)sch; + struct sdmmc_command cmd; + uint8_t hostctl; + int opcode, error, retry = 40; + + switch (timing) { + case SDMMC_TIMING_MMC_HS200: + opcode = MMC_SEND_TUNING_BLOCK_HS200; + break; + case SDMMC_TIMING_UHS_SDR50: + if (!ISSET(hp->sc->sc_caps2, SDHC_TUNING_SDR50)) + return 0; + /* FALLTHROUGH */ + case SDMMC_TIMING_UHS_SDR104: + opcode = MMC_SEND_TUNING_BLOCK; + break; + default: + return EINVAL; + } + + hostctl = HREAD1(hp, SDHC_HOST_CTL); + + /* enable buffer read ready interrupt */ + HSET2(hp, SDHC_NINTR_SIGNAL_EN, SDHC_BUFFER_READ_READY); + HSET2(hp, SDHC_NINTR_STATUS_EN, SDHC_BUFFER_READ_READY); + + /* disable DMA */ + HCLR1(hp, SDHC_HOST_CTL, SDHC_DMA_SELECT); + + /* reset tuning circuit */ + HCLR2(hp, SDHC_HOST_CTL2, SDHC_SAMPLING_CLOCK_SEL); + + /* start of tuning */ + HWRITE2(hp, SDHC_HOST_CTL2, SDHC_EXECUTE_TUNING); + + mutex_enter(&hp->intr_lock); + do { + memset(&cmd, 0, sizeof(cmd)); + cmd.c_opcode = opcode; + cmd.c_arg = 0; + cmd.c_flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1; + if (ISSET(hostctl, SDHC_8BIT_MODE)) { + cmd.c_blklen = cmd.c_datalen = 128; + } else { + cmd.c_blklen = cmd.c_datalen = 64; + } + + error = sdhc_start_command(hp, &cmd); + if (error) + break; + + if (!sdhc_wait_intr(hp, SDHC_BUFFER_READ_READY, + SDHC_TUNING_TIMEOUT)) { + break; + } + + delay(1000); + } while (HREAD2(hp, SDHC_HOST_CTL2) & SDHC_EXECUTE_TUNING && --retry); + mutex_exit(&hp->intr_lock); + + /* disable buffer read ready interrupt */ + HCLR2(hp, SDHC_NINTR_SIGNAL_EN, SDHC_BUFFER_READ_READY); + HCLR2(hp, SDHC_NINTR_STATUS_EN, SDHC_BUFFER_READ_READY); + + if (HREAD2(hp, SDHC_HOST_CTL2) & SDHC_EXECUTE_TUNING) { + HCLR2(hp, SDHC_HOST_CTL2, + SDHC_SAMPLING_CLOCK_SEL|SDHC_EXECUTE_TUNING); + sdhc_soft_reset(hp, SDHC_RESET_DAT|SDHC_RESET_CMD); + aprint_error_dev(hp->sc->sc_dev, + "tuning did not complete, using fixed sampling clock\n"); + return EIO; /* tuning did not complete */ + } + + if ((HREAD2(hp, SDHC_HOST_CTL2) & SDHC_SAMPLING_CLOCK_SEL) == 0) { + HCLR2(hp, SDHC_HOST_CTL2, + SDHC_SAMPLING_CLOCK_SEL|SDHC_EXECUTE_TUNING); + sdhc_soft_reset(hp, SDHC_RESET_DAT|SDHC_RESET_CMD); + aprint_error_dev(hp->sc->sc_dev, + "tuning failed, using fixed sampling clock\n"); + return EIO; /* tuning failed */ + } + + return 0; /* tuning completed */ +} + static int sdhc_wait_state(struct sdhc_host *hp, uint32_t mask, uint32_t value) { @@ -1405,7 +1499,7 @@ sdhc_start_command(struct sdhc_host *hp, command |= SDHC_CRC_CHECK_ENABLE; if (ISSET(cmd->c_flags, SCF_RSP_IDX)) command |= SDHC_INDEX_CHECK_ENABLE; - if (cmd->c_data != NULL) + if (cmd->c_datalen > 0) command |= SDHC_DATA_PRESENT_SELECT; if (!ISSET(cmd->c_flags, SCF_RSP_PRESENT)) @@ -1438,7 +1532,7 @@ sdhc_start_command(struct sdhc_host *hp, } /* Set DMA start address. */ - if (ISSET(hp->flags, SHF_USE_ADMA2_MASK) && cmd->c_datalen > 0) { + if (ISSET(hp->flags, SHF_USE_ADMA2_MASK) && cmd->c_data != NULL) { for (int seg = 0; seg < cmd->c_dmamap->dm_nsegs; seg++) { bus_addr_t paddr = cmd->c_dmamap->dm_segs[seg].ds_addr; Index: src/sys/dev/sdmmc/sdhcreg.h diff -u src/sys/dev/sdmmc/sdhcreg.h:1.15 src/sys/dev/sdmmc/sdhcreg.h:1.16 --- src/sys/dev/sdmmc/sdhcreg.h:1.15 Sun Aug 2 21:45:12 2015 +++ src/sys/dev/sdmmc/sdhcreg.h Wed Aug 5 10:30:25 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: sdhcreg.h,v 1.15 2015/08/02 21:45:12 jmcneill Exp $ */ +/* $NetBSD: sdhcreg.h,v 1.16 2015/08/05 10:30:25 jmcneill Exp $ */ /* $OpenBSD: sdhcreg.h,v 1.4 2006/07/30 17:20:40 fgsch Exp $ */ /* @@ -143,6 +143,8 @@ #define SDHC_EINTR_SIGNAL_MASK 0x03ff /* excluding vendor signals */ #define SDHC_CMD12_ERROR_STATUS 0x3c #define SDHC_HOST_CTL2 0x3e +#define SDHC_SAMPLING_CLOCK_SEL (1<<7) +#define SDHC_EXECUTE_TUNING (1<<6) #define SDHC_1_8V_SIGNAL_EN (1<<3) #define SDHC_UHS_MODE_SELECT_SHIFT 0 #define SDHC_UHS_MODE_SELECT_MASK 0x7