summaryrefslogtreecommitdiff
path: root/drivers/mmc/host/dw_mmc-exynos.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/dw_mmc-exynos.c')
-rw-r--r--drivers/mmc/host/dw_mmc-exynos.c55
1 files changed, 42 insertions, 13 deletions
diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c
index b86f51845230..d42e664e78bc 100644
--- a/drivers/mmc/host/dw_mmc-exynos.c
+++ b/drivers/mmc/host/dw_mmc-exynos.c
@@ -56,6 +56,8 @@
#define DWMCI_MPSCTRL_ENCRYPTION BIT(1)
#define DWMCI_MPSCTRL_VALID BIT(0)
+#define EXYNOS_CCLKIN_MIN 50000000 /* unit: HZ */
+
/* Variations in Exynos specific dw-mshc controller */
enum dw_mci_exynos_type {
DW_MCI_TYPE_EXYNOS4210,
@@ -71,6 +73,7 @@ struct dw_mci_exynos_priv_data {
u8 ciu_div;
u32 sdr_timing;
u32 ddr_timing;
+ u32 cur_speed;
};
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)
{
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 ||
- 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;
-
+ host->bus_hz = rate / (priv->ciu_div + 1);
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)
{
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);
- 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);
+ }
+
+ /* 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)
@@ -214,8 +237,14 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host)
priv->ctrl_type = exynos_compat[idx].ctrl_type;
}
- of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div);
- priv->ciu_div = div;
+ if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
+ 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,
"samsung,dw-mshc-sdr-timing", timing, 2);