mmc: dw_mmc: exynos: adjust the clock rate with speed mode

Exynos's host has divider logic before 'cclk_in' to controller core.
It means that actual clock rate of ciu clock comes from this divider
value. So, source clock should be adjusted along with 'ciu_div' which
indicates the host's divider ratio. Setting clock rate basically fits
the required speed. Specially, 'cclk_in' should have double rate of
target speed in case of DDR 8-bit mode.

Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com>
Tested-by: Alim Akhtar <alim.akhtar@samsung.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
This commit is contained in:
Seungwon Jeon 2013-08-31 00:13:03 +09:00 committed by Chris Ball
parent c537a1c5ff
commit c6d9deda64
1 changed files with 42 additions and 13 deletions

View File

@ -56,6 +56,8 @@
#define DWMCI_MPSCTRL_ENCRYPTION BIT(1) #define DWMCI_MPSCTRL_ENCRYPTION BIT(1)
#define DWMCI_MPSCTRL_VALID BIT(0) #define DWMCI_MPSCTRL_VALID BIT(0)
#define EXYNOS_CCLKIN_MIN 50000000 /* unit: HZ */
/* Variations in Exynos specific dw-mshc controller */ /* Variations in Exynos specific dw-mshc controller */
enum dw_mci_exynos_type { enum dw_mci_exynos_type {
DW_MCI_TYPE_EXYNOS4210, DW_MCI_TYPE_EXYNOS4210,
@ -71,6 +73,7 @@ struct dw_mci_exynos_priv_data {
u8 ciu_div; u8 ciu_div;
u32 sdr_timing; u32 sdr_timing;
u32 ddr_timing; u32 ddr_timing;
u32 cur_speed;
}; };
static struct dw_mci_exynos_compatible { static struct dw_mci_exynos_compatible {
@ -114,16 +117,9 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host)
static int dw_mci_exynos_setup_clock(struct dw_mci *host) static int dw_mci_exynos_setup_clock(struct dw_mci *host)
{ {
struct dw_mci_exynos_priv_data *priv = host->priv; struct dw_mci_exynos_priv_data *priv = host->priv;
unsigned long rate = clk_get_rate(host->ciu_clk);
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5250 || host->bus_hz = rate / (priv->ciu_div + 1);
priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU)
host->bus_hz /= (priv->ciu_div + 1);
else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
host->bus_hz /= EXYNOS4412_FIXED_CIU_CLK_DIV;
else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
host->bus_hz /= EXYNOS4210_FIXED_CIU_CLK_DIV;
return 0; return 0;
} }
@ -187,11 +183,38 @@ static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios) static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
{ {
struct dw_mci_exynos_priv_data *priv = host->priv; struct dw_mci_exynos_priv_data *priv = host->priv;
unsigned int wanted = ios->clock;
unsigned long actual;
u8 div = priv->ciu_div + 1;
if (ios->timing == MMC_TIMING_UHS_DDR50) if (ios->timing == MMC_TIMING_UHS_DDR50) {
mci_writel(host, CLKSEL, priv->ddr_timing); mci_writel(host, CLKSEL, priv->ddr_timing);
else /* Should be double rate for DDR mode */
if (ios->bus_width == MMC_BUS_WIDTH_8)
wanted <<= 1;
} else {
mci_writel(host, CLKSEL, priv->sdr_timing); mci_writel(host, CLKSEL, priv->sdr_timing);
}
/* Don't care if wanted clock is zero */
if (!wanted)
return;
/* Guaranteed minimum frequency for cclkin */
if (wanted < EXYNOS_CCLKIN_MIN)
wanted = EXYNOS_CCLKIN_MIN;
if (wanted != priv->cur_speed) {
int ret = clk_set_rate(host->ciu_clk, wanted * div);
if (ret)
dev_warn(host->dev,
"failed to set clk-rate %u error: %d\n",
wanted * div, ret);
actual = clk_get_rate(host->ciu_clk);
host->bus_hz = actual / div;
priv->cur_speed = wanted;
host->current_speed = 0;
}
} }
static int dw_mci_exynos_parse_dt(struct dw_mci *host) static int dw_mci_exynos_parse_dt(struct dw_mci *host)
@ -214,8 +237,14 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host)
priv->ctrl_type = exynos_compat[idx].ctrl_type; priv->ctrl_type = exynos_compat[idx].ctrl_type;
} }
of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div); if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
priv->ciu_div = div; priv->ciu_div = EXYNOS4412_FIXED_CIU_CLK_DIV - 1;
else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
priv->ciu_div = EXYNOS4210_FIXED_CIU_CLK_DIV - 1;
else {
of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div);
priv->ciu_div = div;
}
ret = of_property_read_u32_array(np, ret = of_property_read_u32_array(np,
"samsung,dw-mshc-sdr-timing", timing, 2); "samsung,dw-mshc-sdr-timing", timing, 2);