Skip to content

Commit d83d251

Browse files
Wenchao Chenstorulf
authored andcommitted
mmc: sdhci-sprd: Add SD HS mode online tuning
First of all, Unisoc's IC provides cmd delay and read delay to ensure that the host can get the correct data. However, according to SD Spec, there is no need to do tuning in high speed mode, but with the development of chip processes, it is more and more difficult to find a suitable delay to cover all the chips. Therefore, we need SD high speed mode online tuning. In addition, we added mmc_sd_switch() and mmc_send_status() to the header file to allow it to be usable by the driver. Signed-off-by: Wenchao Chen <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Ulf Hansson <[email protected]>
1 parent bac8068 commit d83d251

File tree

1 file changed

+149
-0
lines changed

1 file changed

+149
-0
lines changed

drivers/mmc/host/sdhci-sprd.c

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include <linux/dma-mapping.h>
1010
#include <linux/highmem.h>
1111
#include <linux/iopoll.h>
12+
#include <linux/mmc/host.h>
13+
#include <linux/mmc/mmc.h>
1214
#include <linux/module.h>
1315
#include <linux/of.h>
1416
#include <linux/of_gpio.h>
@@ -72,6 +74,11 @@
7274
#define SDHCI_SPRD_CLK_DEF_RATE 26000000
7375
#define SDHCI_SPRD_PHY_DLL_CLK 52000000
7476

77+
#define SDHCI_SPRD_MAX_RANGE 0xff
78+
#define SDHCI_SPRD_CMD_DLY_MASK GENMASK(15, 8)
79+
#define SDHCI_SPRD_POSRD_DLY_MASK GENMASK(23, 16)
80+
#define SDHCI_SPRD_CPST_EN GENMASK(27, 24)
81+
7582
struct sdhci_sprd_host {
7683
u32 version;
7784
struct clk *clk_sdio;
@@ -85,6 +92,11 @@ struct sdhci_sprd_host {
8592
u32 phy_delay[MMC_TIMING_MMC_HS400 + 2];
8693
};
8794

95+
enum sdhci_sprd_tuning_type {
96+
SDHCI_SPRD_TUNING_SD_HS_CMD,
97+
SDHCI_SPRD_TUNING_SD_HS_DATA,
98+
};
99+
88100
struct sdhci_sprd_phy_cfg {
89101
const char *property;
90102
u8 timing;
@@ -532,6 +544,138 @@ static void sdhci_sprd_hs400_enhanced_strobe(struct mmc_host *mmc,
532544
SDHCI_SPRD_REG_32_DLL_DLY);
533545
}
534546

547+
static int mmc_send_tuning_cmd(struct mmc_card *card)
548+
{
549+
return mmc_send_status(card, NULL);
550+
}
551+
552+
static int mmc_send_tuning_data(struct mmc_card *card)
553+
{
554+
u8 *status;
555+
int ret;
556+
557+
status = kmalloc(64, GFP_KERNEL);
558+
if (!status)
559+
return -ENOMEM;
560+
561+
ret = mmc_sd_switch(card, 0, 0, 0, status);
562+
563+
kfree(status);
564+
565+
return ret;
566+
}
567+
568+
static int sdhci_sprd_get_best_clk_sample(struct mmc_host *mmc, u8 *value)
569+
{
570+
int range_end = SDHCI_SPRD_MAX_RANGE;
571+
int range_length = 0;
572+
int middle_range = 0;
573+
int count = 0;
574+
int i;
575+
576+
for (i = 0; i <= SDHCI_SPRD_MAX_RANGE; i++) {
577+
if (value[i]) {
578+
pr_debug("%s: tuning ok: %d\n", mmc_hostname(mmc), i);
579+
count++;
580+
} else {
581+
pr_debug("%s: tuning fail: %d\n", mmc_hostname(mmc), i);
582+
if (range_length < count) {
583+
range_length = count;
584+
range_end = i - 1;
585+
count = 0;
586+
}
587+
}
588+
}
589+
590+
if (!count)
591+
return -EIO;
592+
593+
if (count > range_length) {
594+
range_length = count;
595+
range_end = i - 1;
596+
}
597+
598+
middle_range = range_end - (range_length - 1) / 2;
599+
600+
return middle_range;
601+
}
602+
603+
static int sdhci_sprd_tuning(struct mmc_host *mmc, struct mmc_card *card,
604+
enum sdhci_sprd_tuning_type type)
605+
{
606+
struct sdhci_host *host = mmc_priv(mmc);
607+
struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
608+
u32 *p = sprd_host->phy_delay;
609+
u32 dll_cfg, dll_dly;
610+
int best_clk_sample;
611+
int err = 0;
612+
u8 *value;
613+
int i;
614+
615+
value = kmalloc(SDHCI_SPRD_MAX_RANGE + 1, GFP_KERNEL);
616+
if (!value)
617+
return -ENOMEM;
618+
619+
sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
620+
621+
dll_cfg = sdhci_readl(host, SDHCI_SPRD_REG_32_DLL_CFG);
622+
dll_cfg &= ~SDHCI_SPRD_CPST_EN;
623+
sdhci_writel(host, dll_cfg, SDHCI_SPRD_REG_32_DLL_CFG);
624+
625+
dll_dly = p[mmc->ios.timing];
626+
627+
for (i = 0; i <= SDHCI_SPRD_MAX_RANGE; i++) {
628+
if (type == SDHCI_SPRD_TUNING_SD_HS_CMD) {
629+
dll_dly &= ~SDHCI_SPRD_CMD_DLY_MASK;
630+
dll_dly |= ((i << 8) & SDHCI_SPRD_CMD_DLY_MASK);
631+
} else {
632+
dll_dly &= ~SDHCI_SPRD_POSRD_DLY_MASK;
633+
dll_dly |= ((i << 16) & SDHCI_SPRD_POSRD_DLY_MASK);
634+
}
635+
636+
sdhci_writel(host, dll_dly, SDHCI_SPRD_REG_32_DLL_DLY);
637+
638+
if (type == SDHCI_SPRD_TUNING_SD_HS_CMD)
639+
value[i] = !mmc_send_tuning_cmd(card);
640+
else
641+
value[i] = !mmc_send_tuning_data(card);
642+
}
643+
644+
best_clk_sample = sdhci_sprd_get_best_clk_sample(mmc, value);
645+
if (best_clk_sample < 0) {
646+
dev_err(mmc_dev(host->mmc), "all tuning phase fail!\n");
647+
goto out;
648+
}
649+
650+
if (type == SDHCI_SPRD_TUNING_SD_HS_CMD) {
651+
p[mmc->ios.timing] &= ~SDHCI_SPRD_CMD_DLY_MASK;
652+
p[mmc->ios.timing] |= ((best_clk_sample << 8) & SDHCI_SPRD_CMD_DLY_MASK);
653+
} else {
654+
p[mmc->ios.timing] &= ~(SDHCI_SPRD_POSRD_DLY_MASK);
655+
p[mmc->ios.timing] |= ((best_clk_sample << 16) & SDHCI_SPRD_POSRD_DLY_MASK);
656+
}
657+
658+
pr_debug("%s: the best clk sample %d, delay value 0x%08x\n",
659+
mmc_hostname(host->mmc), best_clk_sample, p[mmc->ios.timing]);
660+
661+
out:
662+
sdhci_writel(host, p[mmc->ios.timing], SDHCI_SPRD_REG_32_DLL_DLY);
663+
664+
kfree(value);
665+
666+
return err;
667+
}
668+
669+
static int sdhci_sprd_prepare_sd_hs_cmd_tuning(struct mmc_host *mmc, struct mmc_card *card)
670+
{
671+
return sdhci_sprd_tuning(mmc, card, SDHCI_SPRD_TUNING_SD_HS_CMD);
672+
}
673+
674+
static int sdhci_sprd_execute_sd_hs_data_tuning(struct mmc_host *mmc, struct mmc_card *card)
675+
{
676+
return sdhci_sprd_tuning(mmc, card, SDHCI_SPRD_TUNING_SD_HS_DATA);
677+
}
678+
535679
static void sdhci_sprd_phy_param_parse(struct sdhci_sprd_host *sprd_host,
536680
struct device_node *np)
537681
{
@@ -576,6 +720,11 @@ static int sdhci_sprd_probe(struct platform_device *pdev)
576720
host->mmc_host_ops.request = sdhci_sprd_request;
577721
host->mmc_host_ops.hs400_enhanced_strobe =
578722
sdhci_sprd_hs400_enhanced_strobe;
723+
host->mmc_host_ops.prepare_sd_hs_tuning =
724+
sdhci_sprd_prepare_sd_hs_cmd_tuning;
725+
host->mmc_host_ops.execute_sd_hs_tuning =
726+
sdhci_sprd_execute_sd_hs_data_tuning;
727+
579728
/*
580729
* We can not use the standard ops to change and detect the voltage
581730
* signal for Spreadtrum SD host controller, since our voltage regulator

0 commit comments

Comments
 (0)