aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLucas Stach <dev@lynxeye.de>2015-12-22 13:41:02 -0500
committerUlf Hansson <ulf.hansson@linaro.org>2015-12-28 08:14:56 -0500
commitc3c2384c3ac073cdc2d8e3bbc89b55cdcf507b8f (patch)
tree59ec32b62830ca6570a524b83c28d69c24edbba4
parent74cd42bcad7486664d13b1b42bc81a399d7ed763 (diff)
mmc: tegra: implement UHS tuning
This implements the UHS tuning sequence in a similar way to the one contained in the TRM. It deviates in the way how to check if the tap value is passing, by using the common Linux MMC function, which does not only check for data CRC errors, but also if the received block pattern is correct. Signed-off-by: Lucas Stach <dev@lynxeye.de> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
-rw-r--r--drivers/mmc/host/sdhci-tegra.c55
1 files changed, 55 insertions, 0 deletions
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index 20ce81b57d32..020154943732 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -22,6 +22,7 @@
22#include <linux/of_device.h> 22#include <linux/of_device.h>
23#include <linux/mmc/card.h> 23#include <linux/mmc/card.h>
24#include <linux/mmc/host.h> 24#include <linux/mmc/host.h>
25#include <linux/mmc/mmc.h>
25#include <linux/mmc/slot-gpio.h> 26#include <linux/mmc/slot-gpio.h>
26#include <linux/gpio/consumer.h> 27#include <linux/gpio/consumer.h>
27 28
@@ -29,6 +30,9 @@
29 30
30/* Tegra SDHOST controller vendor register definitions */ 31/* Tegra SDHOST controller vendor register definitions */
31#define SDHCI_TEGRA_VENDOR_CLOCK_CTRL 0x100 32#define SDHCI_TEGRA_VENDOR_CLOCK_CTRL 0x100
33#define SDHCI_CLOCK_CTRL_TAP_MASK 0x00ff0000
34#define SDHCI_CLOCK_CTRL_TAP_SHIFT 16
35#define SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE BIT(5)
32#define SDHCI_CLOCK_CTRL_PADPIPE_CLKEN_OVERRIDE BIT(3) 36#define SDHCI_CLOCK_CTRL_PADPIPE_CLKEN_OVERRIDE BIT(3)
33#define SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE BIT(2) 37#define SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE BIT(2)
34 38
@@ -151,6 +155,8 @@ static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
151 155
152 clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 156 clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
153 clk_ctrl &= ~SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE; 157 clk_ctrl &= ~SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE;
158 if (!(soc_data->nvquirks & NVQUIRK_DISABLE_SDR50))
159 clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE;
154 sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); 160 sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
155 161
156 tegra_host->ddr_signaling = false; 162 tegra_host->ddr_signaling = false;
@@ -214,6 +220,50 @@ static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host)
214 return clk_round_rate(pltfm_host->clk, UINT_MAX) / 2; 220 return clk_round_rate(pltfm_host->clk, UINT_MAX) / 2;
215} 221}
216 222
223static void tegra_sdhci_set_tap(struct sdhci_host *host, unsigned int tap)
224{
225 u32 reg;
226
227 reg = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
228 reg &= ~SDHCI_CLOCK_CTRL_TAP_MASK;
229 reg |= tap << SDHCI_CLOCK_CTRL_TAP_SHIFT;
230 sdhci_writel(host, reg, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
231}
232
233static int tegra_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
234{
235 unsigned int min, max;
236
237 /*
238 * Start search for minimum tap value at 10, as smaller values are
239 * may wrongly be reported as working but fail at higher speeds,
240 * according to the TRM.
241 */
242 min = 10;
243 while (min < 255) {
244 tegra_sdhci_set_tap(host, min);
245 if (!mmc_send_tuning(host->mmc, opcode, NULL))
246 break;
247 min++;
248 }
249
250 /* Find the maximum tap value that still passes. */
251 max = min + 1;
252 while (max < 255) {
253 tegra_sdhci_set_tap(host, max);
254 if (mmc_send_tuning(host->mmc, opcode, NULL)) {
255 max--;
256 break;
257 }
258 max++;
259 }
260
261 /* The TRM states the ideal tap value is at 75% in the passing range. */
262 tegra_sdhci_set_tap(host, min + ((max - min) * 3 / 4));
263
264 return mmc_send_tuning(host->mmc, opcode, NULL);
265}
266
217static const struct sdhci_ops tegra_sdhci_ops = { 267static const struct sdhci_ops tegra_sdhci_ops = {
218 .get_ro = tegra_sdhci_get_ro, 268 .get_ro = tegra_sdhci_get_ro,
219 .read_w = tegra_sdhci_readw, 269 .read_w = tegra_sdhci_readw,
@@ -221,6 +271,7 @@ static const struct sdhci_ops tegra_sdhci_ops = {
221 .set_clock = tegra_sdhci_set_clock, 271 .set_clock = tegra_sdhci_set_clock,
222 .set_bus_width = tegra_sdhci_set_bus_width, 272 .set_bus_width = tegra_sdhci_set_bus_width,
223 .reset = tegra_sdhci_reset, 273 .reset = tegra_sdhci_reset,
274 .platform_execute_tuning = tegra_sdhci_execute_tuning,
224 .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, 275 .set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
225 .get_max_clock = tegra_sdhci_get_max_clock, 276 .get_max_clock = tegra_sdhci_get_max_clock,
226}; 277};
@@ -266,6 +317,7 @@ static const struct sdhci_ops tegra114_sdhci_ops = {
266 .set_clock = tegra_sdhci_set_clock, 317 .set_clock = tegra_sdhci_set_clock,
267 .set_bus_width = tegra_sdhci_set_bus_width, 318 .set_bus_width = tegra_sdhci_set_bus_width,
268 .reset = tegra_sdhci_reset, 319 .reset = tegra_sdhci_reset,
320 .platform_execute_tuning = tegra_sdhci_execute_tuning,
269 .set_uhs_signaling = tegra_sdhci_set_uhs_signaling, 321 .set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
270 .get_max_clock = tegra_sdhci_get_max_clock, 322 .get_max_clock = tegra_sdhci_get_max_clock,
271}; 323};
@@ -350,6 +402,9 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
350 if (rc) 402 if (rc)
351 goto err_parse_dt; 403 goto err_parse_dt;
352 404
405 if (!(tegra_host->soc_data->nvquirks & NVQUIRK_DISABLE_DDR50))
406 host->mmc->caps |= MMC_CAP_1_8V_DDR;
407
353 tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power", 408 tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power",
354 GPIOD_OUT_HIGH); 409 GPIOD_OUT_HIGH);
355 if (IS_ERR(tegra_host->power_gpio)) { 410 if (IS_ERR(tegra_host->power_gpio)) {