Module Name: src Committed By: jmcneill Date: Sat Aug 8 15:36:39 UTC 2015
Modified Files: src/sys/arch/arm/amlogic: amlogic_sdhc.c Log Message: Add support for UHS-I / MMC HS200 tuning process To generate a diff of this commit: cvs rdiff -u -r1.10 -r1.11 src/sys/arch/arm/amlogic/amlogic_sdhc.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/amlogic/amlogic_sdhc.c diff -u src/sys/arch/arm/amlogic/amlogic_sdhc.c:1.10 src/sys/arch/arm/amlogic/amlogic_sdhc.c:1.11 --- src/sys/arch/arm/amlogic/amlogic_sdhc.c:1.10 Sat Aug 8 14:48:41 2015 +++ src/sys/arch/arm/amlogic/amlogic_sdhc.c Sat Aug 8 15:36:39 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: amlogic_sdhc.c,v 1.10 2015/08/08 14:48:41 jmcneill Exp $ */ +/* $NetBSD: amlogic_sdhc.c,v 1.11 2015/08/08 15:36:39 jmcneill Exp $ */ /*- * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> @@ -29,7 +29,7 @@ #include "locators.h" #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: amlogic_sdhc.c,v 1.10 2015/08/08 14:48:41 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: amlogic_sdhc.c,v 1.11 2015/08/08 15:36:39 jmcneill Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -90,7 +90,9 @@ static void amlogic_sdhc_exec_command(sd static void amlogic_sdhc_card_enable_intr(sdmmc_chipset_handle_t, int); static void amlogic_sdhc_card_intr_ack(sdmmc_chipset_handle_t); static int amlogic_sdhc_signal_voltage(sdmmc_chipset_handle_t, int); +static int amlogic_sdhc_execute_tuning(sdmmc_chipset_handle_t, int); +static int amlogic_sdhc_default_rx_phase(struct amlogic_sdhc_softc *); static int amlogic_sdhc_set_clock(struct amlogic_sdhc_softc *, u_int); static int amlogic_sdhc_wait_idle(struct amlogic_sdhc_softc *); static int amlogic_sdhc_wait_ista(struct amlogic_sdhc_softc *, uint32_t, int); @@ -111,12 +113,15 @@ static struct sdmmc_chip_functions amlog .card_enable_intr = amlogic_sdhc_card_enable_intr, .card_intr_ack = amlogic_sdhc_card_intr_ack, .signal_voltage = amlogic_sdhc_signal_voltage, + .execute_tuning = amlogic_sdhc_execute_tuning, }; #define SDHC_WRITE(sc, reg, val) \ bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) #define SDHC_READ(sc, reg) \ bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) +#define SDHC_SET_CLEAR(sc, reg, set, clr) \ + amlogic_reg_set_clear((sc)->sc_bst, (sc)->sc_bsh, (reg), (set), (clr)) static int amlogic_sdhc_match(device_t parent, cfdata_t cf, void *aux) @@ -265,6 +270,33 @@ amlogic_sdhc_dmainit(struct amlogic_sdhc } static int +amlogic_sdhc_default_rx_phase(struct amlogic_sdhc_softc *sc) +{ + const u_int pll_freq = amlogic_get_rate_fixed() / 1000 / 3; + const u_int clkc = SDHC_READ(sc, SD_CLKC_REG); + const u_int clk_div = __SHIFTOUT(clkc, SD_CLKC_CLK_DIV); + const u_int act_freq = pll_freq / clk_div; + + if (act_freq > 90000) { + return 1; + } else if (act_freq > 45000) { + if (sc->sc_signal_voltage == SDMMC_SIGNAL_VOLTAGE_330) { + return 15; + } else { + return 11; + } + } else if (act_freq >= 25000) { + return 15; + } else if (act_freq > 5000) { + return 23; + } else if (act_freq > 1000) { + return 55; + } else { + return 1061; + } +} + +static int amlogic_sdhc_set_clock(struct amlogic_sdhc_softc *sc, u_int freq) { uint32_t clkc; @@ -308,25 +340,8 @@ amlogic_sdhc_set_clock(struct amlogic_sd clk2 &= ~SD_CLK2_SD_CLK_PHASE; clk2 |= __SHIFTIN(1, SD_CLK2_SD_CLK_PHASE); clk2 &= ~SD_CLK2_RX_CLK_PHASE; - - const u_int act_freq = pll_freq / clk_div; - if (act_freq > 90000) { - clk2 |= __SHIFTIN(1, SD_CLK2_RX_CLK_PHASE); - } else if (act_freq > 45000) { - if (sc->sc_signal_voltage == SDMMC_SIGNAL_VOLTAGE_330) { - clk2 |= __SHIFTIN(15, SD_CLK2_RX_CLK_PHASE); - } else { - clk2 |= __SHIFTIN(11, SD_CLK2_RX_CLK_PHASE); - } - } else if (act_freq >= 25000) { - clk2 |= __SHIFTIN(15, SD_CLK2_RX_CLK_PHASE); - } else if (act_freq > 5000) { - clk2 |= __SHIFTIN(23, SD_CLK2_RX_CLK_PHASE); - } else if (act_freq > 1000) { - clk2 |= __SHIFTIN(55, SD_CLK2_RX_CLK_PHASE); - } else { - clk2 |= __SHIFTIN(1061, SD_CLK2_RX_CLK_PHASE); - } + clk2 |= __SHIFTIN(amlogic_sdhc_default_rx_phase(sc), + SD_CLK2_RX_CLK_PHASE); SDHC_WRITE(sc, SD_CLK2_REG, clk2); return 0; @@ -720,3 +735,127 @@ amlogic_sdhc_signal_voltage(sdmmc_chipse sc->sc_signal_voltage = signal_voltage; return 0; } + +static int +amlogic_sdhc_execute_tuning(sdmmc_chipset_handle_t sch, int timing) +{ + static const uint8_t tuning_blk_8bit[] = { + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, + 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, + 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, + 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, + 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, + 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, + 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, + 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, + 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, + 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, + 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, + 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, + 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, + }; + static const uint8_t tuning_blk_4bit[] = { + 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, + 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, + 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, + 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, + 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, + 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, + 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, + 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, + }; + + struct amlogic_sdhc_softc *sc = sch; + struct sdmmc_command cmd; + uint8_t data[sizeof(tuning_blk_8bit)]; + const uint8_t *tblk; + size_t tsize; + struct window_s { + int start; + u_int size; + } best = { .start = -1, .size = 0 }, + curr = { .start = -1, .size = 0 }, + wrap = { .start = 0, .size = 0 }; + u_int ph, rx_phase, clk_div; + int opcode; + + switch (timing) { + case SDMMC_TIMING_MMC_HS200: + tblk = tuning_blk_8bit; + tsize = sizeof(tuning_blk_8bit); + opcode = MMC_SEND_TUNING_BLOCK_HS200; + break; + case SDMMC_TIMING_UHS_SDR50: + case SDMMC_TIMING_UHS_SDR104: + tblk = tuning_blk_4bit; + tsize = sizeof(tuning_blk_4bit); + opcode = MMC_SEND_TUNING_BLOCK; + break; + default: + return EINVAL; + } + + const uint32_t clkc = SDHC_READ(sc, SD_CLKC_REG); + clk_div = __SHIFTOUT(clkc, SD_CLKC_CLK_DIV); + + for (ph = 0; ph <= clk_div; ph++) { + SDHC_SET_CLEAR(sc, SD_CLK2_REG, + __SHIFTIN(ph, SD_CLK2_RX_CLK_PHASE), SD_CLK2_RX_CLK_PHASE); + delay(10); + + u_int nmatch = 0; +#define NUMTRIES 10 + for (u_int i = 0; i < NUMTRIES; i++) { + memset(data, 0, tsize); + memset(&cmd, 0, sizeof(cmd)); + cmd.c_data = data; + cmd.c_datalen = cmd.c_blklen = tsize; + cmd.c_opcode = opcode; + cmd.c_arg = 0; + cmd.c_flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1; + amlogic_sdhc_exec_command(sc, &cmd); + if (cmd.c_error == 0 && memcmp(data, tblk, tsize) == 0) + nmatch++; + } + if (nmatch == NUMTRIES) { /* good phase value */ + if (wrap.start == 0) + wrap.size++; + if (curr.start == -1) + curr.start = ph; + curr.size++; + } else { + wrap.start = -1; + if (curr.start != -1) { /* end of current window */ + if (best.start == -1 || best.size < curr.size) + best = curr; + curr = (struct window_s) + { .start = -1, .size = 0 }; + } + } +#undef NUMTRIES + } + + if (curr.start != -1) { /* the current window wraps around */ + curr.size += wrap.size; + if (curr.size > ph) + curr.size = ph; + if (best.start == -1 || best.size < curr.size) + best = curr; + } + + if (best.start == -1) { /* no window - use default rx_phase */ + rx_phase = amlogic_sdhc_default_rx_phase(sc); + } else { + rx_phase = best.start + best.size / 2; + if (rx_phase >= ph) + rx_phase -= ph; + } + + SDHC_SET_CLEAR(sc, SD_CLK2_REG, + __SHIFTIN(rx_phase, SD_CLK2_RX_CLK_PHASE), SD_CLK2_RX_CLK_PHASE); + + return 0; +}