@@ -412,11 +412,56 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
return ret;
}
-static int dwmci_setup_bus(struct dwmci_host *host, u32 freq)
+static int dwmci_control_clken(struct dwmci_host *host, bool on)
{
- u32 div, status;
+ const u32 val = on ? DWMCI_CLKEN_ENABLE | DWMCI_CLKEN_LOW_PWR : 0;
+ const u32 cmd_only_clk = DWMCI_CMD_PRV_DAT_WAIT | DWMCI_CMD_UPD_CLK;
int timeout = 10000;
+ u32 status;
+
+ dwmci_writel(host, DWMCI_CLKENA, val);
+
+ /* Inform CIU */
+ dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_START | cmd_only_clk);
+ do {
+ status = dwmci_readl(host, DWMCI_CMD);
+ if (timeout-- < 0) {
+ debug("%s: Timeout!\n", __func__);
+ return -ETIMEDOUT;
+ }
+ } while (status & DWMCI_CMD_START);
+
+ return 0;
+}
+
+/*
+ * Update the clock divider.
+ *
+ * To prevent a clock glitch keep the clock stopped during the update of
+ * clock divider and clock source.
+ */
+static int dwmci_update_div(struct dwmci_host *host, u32 div)
+{
+ int ret;
+
+ /* Disable clock */
+ ret = dwmci_control_clken(host, false);
+ if (ret)
+ return ret;
+
+ /* Set clock to desired speed */
+ dwmci_writel(host, DWMCI_CLKDIV, div);
+ dwmci_writel(host, DWMCI_CLKSRC, 0);
+
+ /* Enable clock */
+ return dwmci_control_clken(host, true);
+}
+
+static int dwmci_setup_bus(struct dwmci_host *host, u32 freq)
+{
+ u32 div;
unsigned long sclk;
+ int ret;
if ((freq == host->clock) || (freq == 0))
return 0;
@@ -439,35 +484,9 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq)
else
div = DIV_ROUND_UP(sclk, 2 * freq);
- dwmci_writel(host, DWMCI_CLKENA, 0);
- dwmci_writel(host, DWMCI_CLKSRC, 0);
-
- dwmci_writel(host, DWMCI_CLKDIV, div);
- dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT |
- DWMCI_CMD_UPD_CLK | DWMCI_CMD_START);
-
- do {
- status = dwmci_readl(host, DWMCI_CMD);
- if (timeout-- < 0) {
- debug("%s: Timeout!\n", __func__);
- return -ETIMEDOUT;
- }
- } while (status & DWMCI_CMD_START);
-
- dwmci_writel(host, DWMCI_CLKENA, DWMCI_CLKEN_ENABLE |
- DWMCI_CLKEN_LOW_PWR);
-
- dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT |
- DWMCI_CMD_UPD_CLK | DWMCI_CMD_START);
-
- timeout = 10000;
- do {
- status = dwmci_readl(host, DWMCI_CMD);
- if (timeout-- < 0) {
- debug("%s: Timeout!\n", __func__);
- return -ETIMEDOUT;
- }
- } while (status & DWMCI_CMD_START);
+ ret = dwmci_update_div(host, div);
+ if (ret)
+ return ret;
host->clock = freq;
Extract the clock divider update into dwmci_update_div() function. It's a procedure recommended in TRM, so it's better to keep it in a dedicated function to make the code clearer. While at it also extract the clock control code into a separate routine to avoid code duplication in dwmci_setup_bus(). No functional change. Signed-off-by: Sam Protsenko <semen.protsenko@linaro.org> --- Changes in v2: - Moved CLKSRC after disabling the clock as recommended in TRM, to avoid possible glitch - Extracted div update into a separate function with corresponding comment - Changed the patch subject and reworked the commit message drivers/mmc/dw_mmc.c | 81 +++++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 31 deletions(-)