mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-23 17:30:02 +00:00
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:
parent
c537a1c5ff
commit
c6d9deda64
1 changed files with 42 additions and 13 deletions
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue