diff options
author | San Mehat <san@google.com> | 2009-12-03 13:58:54 -0500 |
---|---|---|
committer | Daniel Walker <dwalker@codeaurora.org> | 2010-03-18 16:37:23 -0400 |
commit | d0719e59f4ad96616f7c02ef0201667e41778c88 (patch) | |
tree | 51c30578d5942737d549700c5c28a3d200340d4d /drivers/mmc/host/msm_sdcc.c | |
parent | 6ac9ea69069804d357064357d0082b0eab4c87ce (diff) |
mmc: msm_sdcc: Fix issue where clocks could be disabled mid transaction
msmsdcc_enable_clocks() was incorrectly being called depending on
the state of host->clks_on. This means the busclk idle timer was never
being deleted if the clock was already on.. Bogus.
Also fixes a possible double clk disable if the call to
del_timer_sync() in msmsdcc_disable_clocks() raced with
the busclk timer.
Signed-off-by: San Mehat <san@google.com>
Signed-off-by: Daniel Walker <dwalker@codeaurora.org>
Diffstat (limited to 'drivers/mmc/host/msm_sdcc.c')
-rw-r--r-- | drivers/mmc/host/msm_sdcc.c | 61 |
1 files changed, 27 insertions, 34 deletions
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 8329fd650c5f..47b1f2526521 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c | |||
@@ -61,7 +61,7 @@ static unsigned int msmsdcc_sdioirq; | |||
61 | #define CMD_SPINMAX 20 | 61 | #define CMD_SPINMAX 20 |
62 | 62 | ||
63 | 63 | ||
64 | static inline void | 64 | static inline void |
65 | msmsdcc_disable_clocks(struct msmsdcc_host *host, int deferr) | 65 | msmsdcc_disable_clocks(struct msmsdcc_host *host, int deferr) |
66 | { | 66 | { |
67 | WARN_ON(!host->clks_on); | 67 | WARN_ON(!host->clks_on); |
@@ -72,9 +72,14 @@ msmsdcc_disable_clocks(struct msmsdcc_host *host, int deferr) | |||
72 | mod_timer(&host->busclk_timer, jiffies + BUSCLK_TIMEOUT); | 72 | mod_timer(&host->busclk_timer, jiffies + BUSCLK_TIMEOUT); |
73 | } else { | 73 | } else { |
74 | del_timer_sync(&host->busclk_timer); | 74 | del_timer_sync(&host->busclk_timer); |
75 | clk_disable(host->clk); | 75 | /* Need to check clks_on again in case the busclk |
76 | clk_disable(host->pclk); | 76 | * timer fired |
77 | host->clks_on = 0; | 77 | */ |
78 | if (host->clks_on) { | ||
79 | clk_disable(host->clk); | ||
80 | clk_disable(host->pclk); | ||
81 | host->clks_on = 0; | ||
82 | } | ||
78 | } | 83 | } |
79 | } | 84 | } |
80 | 85 | ||
@@ -83,21 +88,21 @@ msmsdcc_enable_clocks(struct msmsdcc_host *host) | |||
83 | { | 88 | { |
84 | int rc; | 89 | int rc; |
85 | 90 | ||
86 | WARN_ON(host->clks_on); | ||
87 | |||
88 | del_timer_sync(&host->busclk_timer); | 91 | del_timer_sync(&host->busclk_timer); |
89 | 92 | ||
90 | rc = clk_enable(host->pclk); | 93 | if (!host->clks_on) { |
91 | if (rc) | 94 | rc = clk_enable(host->pclk); |
92 | return rc; | 95 | if (rc) |
93 | rc = clk_enable(host->clk); | 96 | return rc; |
94 | if (rc) { | 97 | rc = clk_enable(host->clk); |
95 | clk_disable(host->pclk); | 98 | if (rc) { |
96 | return rc; | 99 | clk_disable(host->pclk); |
100 | return rc; | ||
101 | } | ||
102 | udelay(1 + ((3 * USEC_PER_SEC) / | ||
103 | (host->clk_rate ? host->clk_rate : msmsdcc_fmin))); | ||
104 | host->clks_on = 1; | ||
97 | } | 105 | } |
98 | udelay(1 + ((3 * USEC_PER_SEC) / | ||
99 | (host->clk_rate ? host->clk_rate : msmsdcc_fmin))); | ||
100 | host->clks_on = 1; | ||
101 | return 0; | 106 | return 0; |
102 | } | 107 | } |
103 | 108 | ||
@@ -180,7 +185,8 @@ msmsdcc_dma_exec_func(struct msm_dmov_cmd *cmd) | |||
180 | struct msmsdcc_host *host = (struct msmsdcc_host *)cmd->data; | 185 | struct msmsdcc_host *host = (struct msmsdcc_host *)cmd->data; |
181 | 186 | ||
182 | msmsdcc_writel(host, host->cmd_timeout, MMCIDATATIMER); | 187 | msmsdcc_writel(host, host->cmd_timeout, MMCIDATATIMER); |
183 | msmsdcc_writel(host, (unsigned int)host->curr.xfer_size, MMCIDATALENGTH); | 188 | msmsdcc_writel(host, (unsigned int)host->curr.xfer_size, |
189 | MMCIDATALENGTH); | ||
184 | msmsdcc_writel(host, host->cmd_pio_irqmask, MMCIMASK1); | 190 | msmsdcc_writel(host, host->cmd_pio_irqmask, MMCIMASK1); |
185 | msmsdcc_writel(host, host->cmd_datactrl, MMCIDATACTRL); | 191 | msmsdcc_writel(host, host->cmd_datactrl, MMCIDATACTRL); |
186 | 192 | ||
@@ -854,13 +860,7 @@ msmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq) | |||
854 | return; | 860 | return; |
855 | } | 861 | } |
856 | 862 | ||
857 | /* Need to drop the host lock here in case | 863 | msmsdcc_enable_clocks(host); |
858 | * the busclk wd fires | ||
859 | */ | ||
860 | spin_unlock_irqrestore(&host->lock, flags); | ||
861 | if (!host->clks_on) | ||
862 | msmsdcc_enable_clocks(host); | ||
863 | spin_lock_irqsave(&host->lock, flags); | ||
864 | 864 | ||
865 | host->curr.mrq = mrq; | 865 | host->curr.mrq = mrq; |
866 | 866 | ||
@@ -893,11 +893,10 @@ msmsdcc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
893 | int rc; | 893 | int rc; |
894 | unsigned long flags; | 894 | unsigned long flags; |
895 | 895 | ||
896 | if (!host->clks_on) | ||
897 | msmsdcc_enable_clocks(host); | ||
898 | |||
899 | spin_lock_irqsave(&host->lock, flags); | 896 | spin_lock_irqsave(&host->lock, flags); |
900 | 897 | ||
898 | msmsdcc_enable_clocks(host); | ||
899 | |||
901 | if (ios->clock) { | 900 | if (ios->clock) { |
902 | if (ios->clock != host->clk_rate) { | 901 | if (ios->clock != host->clk_rate) { |
903 | rc = clk_set_rate(host->clk, ios->clock); | 902 | rc = clk_set_rate(host->clk, ios->clock); |
@@ -1026,13 +1025,9 @@ static void | |||
1026 | msmsdcc_busclk_expired(unsigned long _data) | 1025 | msmsdcc_busclk_expired(unsigned long _data) |
1027 | { | 1026 | { |
1028 | struct msmsdcc_host *host = (struct msmsdcc_host *) _data; | 1027 | struct msmsdcc_host *host = (struct msmsdcc_host *) _data; |
1029 | unsigned long flags; | ||
1030 | 1028 | ||
1031 | spin_lock_irqsave(&host->lock, flags); | ||
1032 | dev_info(mmc_dev(host->mmc), "Bus clock timer expired\n"); | ||
1033 | if (host->clks_on) | 1029 | if (host->clks_on) |
1034 | msmsdcc_disable_clocks(host, 0); | 1030 | msmsdcc_disable_clocks(host, 0); |
1035 | spin_unlock_irqrestore(&host->lock, flags); | ||
1036 | } | 1031 | } |
1037 | 1032 | ||
1038 | static int | 1033 | static int |
@@ -1324,10 +1319,8 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t state) | |||
1324 | 1319 | ||
1325 | if (mmc->card && mmc->card->type != MMC_TYPE_SDIO) | 1320 | if (mmc->card && mmc->card->type != MMC_TYPE_SDIO) |
1326 | rc = mmc_suspend_host(mmc, state); | 1321 | rc = mmc_suspend_host(mmc, state); |
1327 | if (!rc) { | 1322 | if (!rc) |
1328 | msmsdcc_writel(host, 0, MMCIMASK0); | 1323 | msmsdcc_writel(host, 0, MMCIMASK0); |
1329 | |||
1330 | } | ||
1331 | if (host->clks_on) | 1324 | if (host->clks_on) |
1332 | msmsdcc_disable_clocks(host, 0); | 1325 | msmsdcc_disable_clocks(host, 0); |
1333 | } | 1326 | } |