Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c6d9deda authored by Seungwon Jeon's avatar Seungwon Jeon Committed by Chris Ball
Browse files

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: default avatarSeungwon Jeon <tgih.jun@samsung.com>
Tested-by: default avatarAlim Akhtar <alim.akhtar@samsung.com>
Signed-off-by: default avatarChris Ball <cjb@laptop.org>
parent c537a1c5
Loading
Loading
Loading
Loading
+42 −13
Original line number Diff line number Diff line
@@ -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,13 +183,40 @@ 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)
{
	struct dw_mci_exynos_priv_data *priv;
@@ -214,8 +237,14 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host)
			priv->ctrl_type = exynos_compat[idx].ctrl_type;
	}

	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);