Skip to content

Commit

Permalink
mmc: aspeed: add runtime tuning
Browse files Browse the repository at this point in the history
1. remove errata torvalds#75 to make 8 bit tuning works.
2. iterate each tap delay to find the best window and select center.
3. while tuning failed, reset recovery will be launched. it needs to
   save/restore register.

Signed-off-by: Cool Lee <cool_lee@aspeedtech.com>
Change-Id: I02a9029dd8bc58f0a308bbd135d2cc90145eb394
  • Loading branch information
cooool1123 committed Jul 25, 2023
1 parent 8fb5c00 commit 4c543c7
Showing 1 changed file with 92 additions and 0 deletions.
92 changes: 92 additions & 0 deletions drivers/mmc/host/sdhci-of-aspeed.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,25 @@
#define ASPEED_SDC_INFO 0x00
#define ASPEED_SDC_S1MMC8 BIT(25)
#define ASPEED_SDC_S0MMC8 BIT(24)
#define ASPEED_SDC_PHASE 0xf4
#define ASPEED_SDC_S1_PHASE_IN GENMASK(25, 21)
#define ASPEED_SDC_S0_PHASE_IN GENMASK(20, 16)
#define ASPEED_SDC_S0_PHASE_IN_SHIFT 16
#define ASPEED_SDC_S1_PHASE_OUT GENMASK(15, 11)
#define ASPEED_SDC_S1_PHASE_IN_EN BIT(10)
#define ASPEED_SDC_S1_PHASE_OUT_EN GENMASK(9, 8)
#define ASPEED_SDC_S0_PHASE_OUT GENMASK(7, 3)
#define ASPEED_SDC_S0_PHASE_IN_EN BIT(2)
#define ASPEED_SDC_S0_PHASE_OUT_EN GENMASK(1, 0)
#define ASPEED_SDC_PHASE_MAX 31

#define TIMING_PHASE_OFFSET 0xF4

#define PROBE_AFTER_ASSET_DEASSERT 0x1

#define ASPEED_SDHCI_TAP_PARAM_INVERT_CLK BIT(4)
#define ASPEED_SDHCI_NR_TAPS 15

struct aspeed_sdc_info {
uint32_t flag;
};
Expand Down Expand Up @@ -221,6 +235,9 @@ static void aspeed_sdhci_reset(struct sdhci_host *host, u8 mask)
SDHCI_INT_ENABLE,
SDHCI_SIGNAL_ENABLE};
int i;
u16 tran_mode;
u32 mmc8_mode;
u32 clk_phase;

pltfm_priv = sdhci_priv(host);
aspeed_sdhci = sdhci_pltfm_priv(pltfm_priv);
Expand All @@ -230,6 +247,10 @@ static void aspeed_sdhci_reset(struct sdhci_host *host, u8 mask)
for (i = 0; i < ARRAY_SIZE(reg_array); i++)
save_array[i] = sdhci_readl(host, reg_array[i]);

tran_mode = sdhci_readw(host, SDHCI_TRANSFER_MODE);
mmc8_mode = readl(aspeed_sdc->regs);
clk_phase = readl(aspeed_sdc->regs + ASPEED_SDC_PHASE);

reset_control_assert(aspeed_sdc->rst);
mdelay(1);
reset_control_deassert(aspeed_sdc->rst);
Expand All @@ -238,12 +259,82 @@ static void aspeed_sdhci_reset(struct sdhci_host *host, u8 mask)
for (i = 0; i < ARRAY_SIZE(reg_array); i++)
sdhci_writel(host, save_array[i], reg_array[i]);

sdhci_writew(host, tran_mode, SDHCI_TRANSFER_MODE);
writel(mmc8_mode, aspeed_sdc->regs);
writel(clk_phase, aspeed_sdc->regs + ASPEED_SDC_PHASE);

aspeed_sdhci_set_clock(host, host->clock);
}

sdhci_reset(host, mask);
}

static int aspeed_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
{
struct sdhci_pltfm_host *pltfm_priv;
struct aspeed_sdhci *sdhci;
struct aspeed_sdc *sdc;
struct device *dev;

u32 val, left, right, edge;
u32 window, oldwindow = 0, center;
u32 in_phase, out_phase, enable_mask, inverted = 0;

dev = mmc_dev(host->mmc);
pltfm_priv = sdhci_priv(host);
sdhci = sdhci_pltfm_priv(pltfm_priv);
sdc = sdhci->parent;

out_phase = readl(sdc->regs + ASPEED_SDC_PHASE) & ASPEED_SDC_S0_PHASE_OUT;

enable_mask = ASPEED_SDC_S0_PHASE_OUT_EN | ASPEED_SDC_S0_PHASE_IN_EN;

/*
* There are two window upon clock rising and falling edge.
* Iterate each tap delay to find the valid window and choose the
* bigger one, set the tap delay at the middle of window.
*/
for (edge = 0; edge < 2; edge++) {
if (edge == 1)
inverted = ASPEED_SDHCI_TAP_PARAM_INVERT_CLK;

val = (out_phase | enable_mask | (inverted << ASPEED_SDC_S0_PHASE_IN_SHIFT));

/* find the left boundary */
for (left = 0; left < ASPEED_SDHCI_NR_TAPS + 1; left++) {
in_phase = val | (left << ASPEED_SDC_S0_PHASE_IN_SHIFT);
writel(in_phase, sdc->regs + ASPEED_SDC_PHASE);

if (!mmc_send_tuning(host->mmc, opcode, NULL))
break;
}

/* find the right boundary */
for (right = left + 1; right < ASPEED_SDHCI_NR_TAPS + 1; right++) {
in_phase = val | (right << ASPEED_SDC_S0_PHASE_IN_SHIFT);
writel(in_phase, sdc->regs + ASPEED_SDC_PHASE);

if (mmc_send_tuning(host->mmc, opcode, NULL))
break;
}

window = right - left;
dev_info(dev, "tuning window = %d\n", window);

if (window > oldwindow) {
oldwindow = window;
center = (((right - 1) + left) / 2) | inverted;
}
}

val = (out_phase | enable_mask | (center << ASPEED_SDC_S0_PHASE_IN_SHIFT));
writel(val, sdc->regs + ASPEED_SDC_PHASE);

dev_info(dev, "tuning result=%x\n", val);

return mmc_send_tuning(host->mmc, opcode, NULL);
}

/*
AST2300/AST2400 : SDMA/PIO
AST2500 : ADMA/SDMA/PIO
Expand All @@ -257,6 +348,7 @@ static struct sdhci_ops aspeed_sdhci_ops = {
.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
.reset = aspeed_sdhci_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
.platform_execute_tuning = aspeed_sdhci_execute_tuning,
};

static struct sdhci_pltfm_data aspeed_sdhci_pdata = {
Expand Down

0 comments on commit 4c543c7

Please sign in to comment.