Merge branch 'linux-4.7' of git://github.com/skeggsb/linux into drm-fixes
[cascardo/linux.git] / drivers / mmc / host / dw_mmc-rockchip.c
index 8c20b81..358b0dc 100644 (file)
@@ -66,6 +66,70 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
        /* Make sure we use phases which we can enumerate with */
        if (!IS_ERR(priv->sample_clk))
                clk_set_phase(priv->sample_clk, priv->default_sample_phase);
+
+       /*
+        * Set the drive phase offset based on speed mode to achieve hold times.
+        *
+        * NOTE: this is _not_ a value that is dynamically tuned and is also
+        * _not_ a value that will vary from board to board.  It is a value
+        * that could vary between different SoC models if they had massively
+        * different output clock delays inside their dw_mmc IP block (delay_o),
+        * but since it's OK to overshoot a little we don't need to do complex
+        * calculations and can pick values that will just work for everyone.
+        *
+        * When picking values we'll stick with picking 0/90/180/270 since
+        * those can be made very accurately on all known Rockchip SoCs.
+        *
+        * Note that these values match values from the DesignWare Databook
+        * tables for the most part except for SDR12 and "ID mode".  For those
+        * two modes the databook calculations assume a clock in of 50MHz.  As
+        * seen above, we always use a clock in rate that is exactly the
+        * card's input clock (times RK3288_CLKGEN_DIV, but that gets divided
+        * back out before the controller sees it).
+        *
+        * From measurement of a single device, it appears that delay_o is
+        * about .5 ns.  Since we try to leave a bit of margin, it's expected
+        * that numbers here will be fine even with much larger delay_o
+        * (the 1.4 ns assumed by the DesignWare Databook would result in the
+        * same results, for instance).
+        */
+       if (!IS_ERR(priv->drv_clk)) {
+               int phase;
+
+               /*
+                * In almost all cases a 90 degree phase offset will provide
+                * sufficient hold times across all valid input clock rates
+                * assuming delay_o is not absurd for a given SoC.  We'll use
+                * that as a default.
+                */
+               phase = 90;
+
+               switch (ios->timing) {
+               case MMC_TIMING_MMC_DDR52:
+                       /*
+                        * Since clock in rate with MMC_DDR52 is doubled when
+                        * bus width is 8 we need to double the phase offset
+                        * to get the same timings.
+                        */
+                       if (ios->bus_width == MMC_BUS_WIDTH_8)
+                               phase = 180;
+                       break;
+               case MMC_TIMING_UHS_SDR104:
+               case MMC_TIMING_MMC_HS200:
+                       /*
+                        * In the case of 150 MHz clock (typical max for
+                        * Rockchip SoCs), 90 degree offset will add a delay
+                        * of 1.67 ns.  That will meet min hold time of .8 ns
+                        * as long as clock output delay is < .87 ns.  On
+                        * SoCs measured this seems to be OK, but it doesn't
+                        * hurt to give margin here, so we use 180.
+                        */
+                       phase = 180;
+                       break;
+               }
+
+               clk_set_phase(priv->drv_clk, phase);
+       }
 }
 
 #define NUM_PHASES                     360
@@ -233,10 +297,10 @@ static int dw_mci_rockchip_init(struct dw_mci *host)
 
 /* Common capabilities of RK3288 SoC */
 static unsigned long dw_mci_rk3288_dwmmc_caps[4] = {
-       MMC_CAP_ERASE,
-       MMC_CAP_ERASE,
-       MMC_CAP_ERASE,
-       MMC_CAP_ERASE,
+       MMC_CAP_ERASE | MMC_CAP_CMD23,
+       MMC_CAP_ERASE | MMC_CAP_CMD23,
+       MMC_CAP_ERASE | MMC_CAP_CMD23,
+       MMC_CAP_ERASE | MMC_CAP_CMD23,
 };
 
 static const struct dw_mci_drv_data rk2928_drv_data = {