Commit fdf492a1 authored by Doug Anderson's avatar Doug Anderson Committed by Chris Ball

mmc: dw_mmc: Honor requests to set the clock to 0

Previously the dw_mmc driver would ignore any requests to disable the
card's clock.  This doesn't seem like a good thing in general, but had
one extra bad side effect in the following situation:
* mmc core would set clk to 400kHz at boot time while scanning
* mmc core would set clk to 0 since no card, but it would be ignored.
* suspend to ram and resume; clocks in the dw_mmc IP block are now 0
  but dw_mmc thinks that they're 400kHz (it ignored the set to 0).
* insert card
* mmc core would set clk to 400kHz which would be considered a no-op.

Note that if there is no card in the slot and we do a suspend/resume
cycle, we _do_ still end up with differences in a dw_mmc register
dump, but the differences are clock related and we've got the clock
disabled both before and after, so this should be OK.
Signed-off-by: default avatarDoug Anderson <dianders@chromium.org>
Signed-off-by: default avatarSeungwon Jeon <tgih.jun@samsung.com>
Signed-off-by: default avatarChris Ball <cjb@laptop.org>
parent e2c63599
...@@ -88,6 +88,9 @@ struct idmac_desc { ...@@ -88,6 +88,9 @@ struct idmac_desc {
* @queue_node: List node for placing this node in the @queue list of * @queue_node: List node for placing this node in the @queue list of
* &struct dw_mci. * &struct dw_mci.
* @clock: Clock rate configured by set_ios(). Protected by host->lock. * @clock: Clock rate configured by set_ios(). Protected by host->lock.
* @__clk_old: The last updated clock with reflecting clock divider.
* Keeping track of this helps us to avoid spamming the console
* with CONFIG_MMC_CLKGATE.
* @flags: Random state bits associated with the slot. * @flags: Random state bits associated with the slot.
* @id: Number of this slot. * @id: Number of this slot.
* @last_detect_state: Most recently observed card detect state. * @last_detect_state: Most recently observed card detect state.
...@@ -105,6 +108,7 @@ struct dw_mci_slot { ...@@ -105,6 +108,7 @@ struct dw_mci_slot {
struct list_head queue_node; struct list_head queue_node;
unsigned int clock; unsigned int clock;
unsigned int __clk_old;
unsigned long flags; unsigned long flags;
#define DW_MMC_CARD_PRESENT 0 #define DW_MMC_CARD_PRESENT 0
#define DW_MMC_CARD_NEED_INIT 1 #define DW_MMC_CARD_NEED_INIT 1
...@@ -632,24 +636,31 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) ...@@ -632,24 +636,31 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg)
static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
{ {
struct dw_mci *host = slot->host; struct dw_mci *host = slot->host;
unsigned int clock = slot->clock;
u32 div; u32 div;
u32 clk_en_a; u32 clk_en_a;
if (slot->clock != host->current_speed || force_clkinit) { if (!clock) {
div = host->bus_hz / slot->clock; mci_writel(host, CLKENA, 0);
if (host->bus_hz % slot->clock && host->bus_hz > slot->clock) mci_send_cmd(slot,
SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
} else if (clock != host->current_speed || force_clkinit) {
div = host->bus_hz / clock;
if (host->bus_hz % clock && host->bus_hz > clock)
/* /*
* move the + 1 after the divide to prevent * move the + 1 after the divide to prevent
* over-clocking the card. * over-clocking the card.
*/ */
div += 1; div += 1;
div = (host->bus_hz != slot->clock) ? DIV_ROUND_UP(div, 2) : 0; div = (host->bus_hz != clock) ? DIV_ROUND_UP(div, 2) : 0;
if ((clock << div) != slot->__clk_old || force_clkinit)
dev_info(&slot->mmc->class_dev, dev_info(&slot->mmc->class_dev,
"Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ" "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n",
" div = %d)\n", slot->id, host->bus_hz, slot->clock, slot->id, host->bus_hz, clock,
div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div); div ? ((host->bus_hz / div) >> 1) :
host->bus_hz, div);
/* disable clock */ /* disable clock */
mci_writel(host, CLKENA, 0); mci_writel(host, CLKENA, 0);
...@@ -676,9 +687,12 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) ...@@ -676,9 +687,12 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
mci_send_cmd(slot, mci_send_cmd(slot,
SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
host->current_speed = slot->clock; /* keep the clock with reflecting clock dividor */
slot->__clk_old = clock << div;
} }
host->current_speed = clock;
/* Set the current slot bus width */ /* Set the current slot bus width */
mci_writel(host, CTYPE, (slot->ctype << slot->id)); mci_writel(host, CTYPE, (slot->ctype << slot->id));
} }
...@@ -807,13 +821,11 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -807,13 +821,11 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
mci_writel(slot->host, UHS_REG, regs); mci_writel(slot->host, UHS_REG, regs);
if (ios->clock) {
/* /*
* Use mirror of ios->clock to prevent race with mmc * Use mirror of ios->clock to prevent race with mmc
* core ios update when finding the minimum. * core ios update when finding the minimum.
*/ */
slot->clock = ios->clock; slot->clock = ios->clock;
}
if (drv_data && drv_data->set_ios) if (drv_data && drv_data->set_ios)
drv_data->set_ios(slot->host, ios); drv_data->set_ios(slot->host, ios);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment