Skip to content

Commit

Permalink
drivers: mmc: sunxi: fix A64 calibration routine
Browse files Browse the repository at this point in the history
The calibration facility in the A64 MMC block seems to have been
misunderstood: the result value is not the value to program into the
delay bits, but is the number of delay cells that result in a full clock
cycle delay. So this value has to be scaled by the desired phase, which
we still have to know and program.
Change the calibration routine to take a phase parameter and scale the
calibration value accordingly.
Also introduce sun50i-a64 delay parameters to store the required phase.
Looking at the BSP kernel the sample delay for anything below HS200 is
0, so we go with that value.
Once the driver supports HS200 and faster modes, we can enter confirmed
working values in there.

This fixes the MMC driver for the Pine64. SD card works, but eMMC (on
another A64 board) does not.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
  • Loading branch information
Andre-ARM committed Oct 24, 2016
1 parent 2230e78 commit d51db19
Showing 1 changed file with 21 additions and 12 deletions.
33 changes: 21 additions & 12 deletions drivers/mmc/host/sunxi-mmc.c
Expand Up @@ -223,6 +223,8 @@
#define SDXC_CLK_50M 2
#define SDXC_CLK_50M_DDR 3
#define SDXC_CLK_50M_DDR_8BIT 4
#define SDXC_CLK_200M_SDR_8BIT 5
#define SDXC_CLK_200M_DDR_8BIT 6

#define SDXC_2X_TIMING_MODE BIT(31)

Expand All @@ -238,6 +240,7 @@
struct sunxi_mmc_clk_delay {
u32 output;
u32 sample;
u32 command;
};

struct sunxi_idma_des {
Expand Down Expand Up @@ -681,15 +684,13 @@ static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en)
return 0;
}

static int sunxi_mmc_calibrate(struct sunxi_mmc_host *host, int reg_off)
static int sunxi_mmc_calibrate(struct sunxi_mmc_host *host, int reg_off,
int degrees)
{
u32 reg = readl(host->reg_base + reg_off);
u32 delay;
unsigned long timeout;

if (!host->cfg->can_calibrate)
return 0;

reg &= ~(SDXC_CAL_DL_MASK << SDXC_CAL_DL_SW_SHIFT);
reg &= ~SDXC_CAL_DL_SW_EN;

Expand All @@ -711,6 +712,7 @@ static int sunxi_mmc_calibrate(struct sunxi_mmc_host *host, int reg_off)
}

delay = (reg >> SDXC_CAL_DL_SHIFT) & SDXC_CAL_DL_MASK;
delay = degrees * delay / 360;

reg &= ~SDXC_CAL_START;
reg |= (delay << SDXC_CAL_DL_SW_SHIFT) | SDXC_CAL_DL_SW_EN;
Expand Down Expand Up @@ -748,6 +750,11 @@ static int sunxi_mmc_clk_set_phase(struct sunxi_mmc_host *host,
return -EINVAL;
}

if (host->cfg->can_calibrate)
return sunxi_mmc_calibrate(host, SDXC_REG_SAMP_DL_REG,
host->cfg->clk_delays[index].sample);
/* TODO: calibrate data strobe delay once HS-400 is supported. */

clk_set_phase(host->clk_sample, host->cfg->clk_delays[index].sample);
clk_set_phase(host->clk_output, host->cfg->clk_delays[index].output);

Expand Down Expand Up @@ -802,12 +809,6 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
if (ret)
return ret;

ret = sunxi_mmc_calibrate(host, SDXC_REG_SAMP_DL_REG);
if (ret)
return ret;

/* TODO: enable calibrate on sdc2 SDXC_REG_DS_DL_REG of A64 */

return sunxi_mmc_oclk_onoff(host, 1);
}

Expand Down Expand Up @@ -1056,6 +1057,14 @@ static const struct sunxi_mmc_clk_delay sun9i_mmc_clk_delays[] = {
[SDXC_CLK_50M_DDR_8BIT] = { .output = 72, .sample = 72 },
};

static const struct sunxi_mmc_clk_delay sun50i_mmc_clk_delays[] = {
[SDXC_CLK_400K] = { .output = 90, .sample = 0, .command = 180 },
[SDXC_CLK_25M] = { .output = 90, .sample = 0, .command = 180 },
[SDXC_CLK_50M] = { .output = 90, .sample = 0, .command = 180 },
[SDXC_CLK_50M_DDR] = { .output = 90, .sample = 0, .command = 180 },
[SDXC_CLK_50M_DDR_8BIT] = { .output = 90, .sample = 0, .command = 180 },
};

static const struct sunxi_mmc_cfg sun4i_a10_cfg = {
.idma_des_size_bits = 13,
.clk_delays = NULL,
Expand All @@ -1082,7 +1091,7 @@ static const struct sunxi_mmc_cfg sun9i_a80_cfg = {

static const struct sunxi_mmc_cfg sun50i_a64_cfg = {
.idma_des_size_bits = 16,
.clk_delays = NULL,
.clk_delays = sun50i_mmc_clk_delays,
.can_calibrate = true,
};

Expand Down Expand Up @@ -1129,7 +1138,7 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host,
return PTR_ERR(host->clk_mmc);
}

if (host->cfg->clk_delays) {
if (host->cfg->clk_delays && !host->cfg->can_calibrate) {
host->clk_output = devm_clk_get(&pdev->dev, "output");
if (IS_ERR(host->clk_output)) {
dev_err(&pdev->dev, "Could not get output clock\n");
Expand Down

0 comments on commit d51db19

Please sign in to comment.