Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/arch/arm/amlogic Add support for UHS-I / MMC HS200 tunin...



details:   https://anonhg.NetBSD.org/src/rev/2f11208221b3
branches:  trunk
changeset: 339760:2f11208221b3
user:      jmcneill <jmcneill%NetBSD.org@localhost>
date:      Sat Aug 08 15:36:39 2015 +0000

description:
Add support for UHS-I / MMC HS200 tuning process

diffstat:

 sys/arch/arm/amlogic/amlogic_sdhc.c |  181 +++++++++++++++++++++++++++++++----
 1 files changed, 160 insertions(+), 21 deletions(-)

diffs (234 lines):

diff -r 6b50912b38ff -r 2f11208221b3 sys/arch/arm/amlogic/amlogic_sdhc.c
--- a/sys/arch/arm/amlogic/amlogic_sdhc.c       Sat Aug 08 14:48:41 2015 +0000
+++ b/sys/arch/arm/amlogic/amlogic_sdhc.c       Sat Aug 08 15:36:39 2015 +0000
@@ -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 <jmcneill%invisible.ca@localhost>
@@ -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_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 @@
        .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 @@
 }
 
 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 @@
        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 @@
        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;
+}



Home | Main Index | Thread Index | Old Index