diff options
author | Denis Karpov <ext-denis.2.karpov@nokia.com> | 2009-09-22 19:44:43 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-23 10:39:34 -0400 |
commit | 11dd62a741047b9fd1faf37620e2b58595f04ce5 (patch) | |
tree | 8d41e29fa2d8af9a92ab103f554086aceafaa47b /drivers/mmc/host/omap_hsmmc.c | |
parent | a3621465b4fd91b7f8560f394afc494a57b77501 (diff) |
omap_hsmmc: context save/restore support
Keep the context over PM dynamic OFF states.
Signed-off-by: Denis Karpov <ext-denis.2.karpov@nokia.com>
Signed-off-by: Adrian Hunter <adrian.hunter@nokia.com>
Acked-by: Matt Fleming <matt@console-pimps.org>
Cc: Ian Molton <ian@mnementh.co.uk>
Cc: "Roberto A. Foglietta" <roberto.foglietta@gmail.com>
Cc: Jarkko Lavinen <jarkko.lavinen@nokia.com>
Cc: Denis Karpov <ext-denis.2.karpov@nokia.com>
Cc: Pierre Ossman <pierre@ossman.eu>
Cc: Philip Langdale <philipl@overt.org>
Cc: "Madhusudhan" <madhu.cr@ti.com>
Cc: <linux-mmc@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/mmc/host/omap_hsmmc.c')
-rw-r--r-- | drivers/mmc/host/omap_hsmmc.c | 194 |
1 files changed, 184 insertions, 10 deletions
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 30b863b2c510..74d506a6ffec 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c | |||
@@ -37,6 +37,7 @@ | |||
37 | 37 | ||
38 | /* OMAP HSMMC Host Controller Registers */ | 38 | /* OMAP HSMMC Host Controller Registers */ |
39 | #define OMAP_HSMMC_SYSCONFIG 0x0010 | 39 | #define OMAP_HSMMC_SYSCONFIG 0x0010 |
40 | #define OMAP_HSMMC_SYSSTATUS 0x0014 | ||
40 | #define OMAP_HSMMC_CON 0x002C | 41 | #define OMAP_HSMMC_CON 0x002C |
41 | #define OMAP_HSMMC_BLK 0x0104 | 42 | #define OMAP_HSMMC_BLK 0x0104 |
42 | #define OMAP_HSMMC_ARG 0x0108 | 43 | #define OMAP_HSMMC_ARG 0x0108 |
@@ -96,6 +97,8 @@ | |||
96 | #define DUAL_VOLT_OCR_BIT 7 | 97 | #define DUAL_VOLT_OCR_BIT 7 |
97 | #define SRC (1 << 25) | 98 | #define SRC (1 << 25) |
98 | #define SRD (1 << 26) | 99 | #define SRD (1 << 26) |
100 | #define SOFTRESET (1 << 1) | ||
101 | #define RESETDONE (1 << 0) | ||
99 | 102 | ||
100 | /* | 103 | /* |
101 | * FIXME: Most likely all the data using these _DEVID defines should come | 104 | * FIXME: Most likely all the data using these _DEVID defines should come |
@@ -154,6 +157,8 @@ struct mmc_omap_host { | |||
154 | int slot_id; | 157 | int slot_id; |
155 | int dbclk_enabled; | 158 | int dbclk_enabled; |
156 | int response_busy; | 159 | int response_busy; |
160 | int context_loss; | ||
161 | |||
157 | struct omap_mmc_platform_data *pdata; | 162 | struct omap_mmc_platform_data *pdata; |
158 | }; | 163 | }; |
159 | 164 | ||
@@ -168,6 +173,166 @@ static void omap_mmc_stop_clock(struct mmc_omap_host *host) | |||
168 | dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n"); | 173 | dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n"); |
169 | } | 174 | } |
170 | 175 | ||
176 | #ifdef CONFIG_PM | ||
177 | |||
178 | /* | ||
179 | * Restore the MMC host context, if it was lost as result of a | ||
180 | * power state change. | ||
181 | */ | ||
182 | static int omap_mmc_restore_ctx(struct mmc_omap_host *host) | ||
183 | { | ||
184 | struct mmc_ios *ios = &host->mmc->ios; | ||
185 | struct omap_mmc_platform_data *pdata = host->pdata; | ||
186 | int context_loss = 0; | ||
187 | u32 hctl, capa, con; | ||
188 | u16 dsor = 0; | ||
189 | unsigned long timeout; | ||
190 | |||
191 | if (pdata->get_context_loss_count) { | ||
192 | context_loss = pdata->get_context_loss_count(host->dev); | ||
193 | if (context_loss < 0) | ||
194 | return 1; | ||
195 | } | ||
196 | |||
197 | dev_dbg(mmc_dev(host->mmc), "context was %slost\n", | ||
198 | context_loss == host->context_loss ? "not " : ""); | ||
199 | if (host->context_loss == context_loss) | ||
200 | return 1; | ||
201 | |||
202 | /* Wait for hardware reset */ | ||
203 | timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); | ||
204 | while ((OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) != RESETDONE | ||
205 | && time_before(jiffies, timeout)) | ||
206 | ; | ||
207 | |||
208 | /* Do software reset */ | ||
209 | OMAP_HSMMC_WRITE(host->base, SYSCONFIG, SOFTRESET); | ||
210 | timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); | ||
211 | while ((OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) != RESETDONE | ||
212 | && time_before(jiffies, timeout)) | ||
213 | ; | ||
214 | |||
215 | OMAP_HSMMC_WRITE(host->base, SYSCONFIG, | ||
216 | OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE); | ||
217 | |||
218 | if (host->id == OMAP_MMC1_DEVID) { | ||
219 | if (host->power_mode != MMC_POWER_OFF && | ||
220 | (1 << ios->vdd) <= MMC_VDD_23_24) | ||
221 | hctl = SDVS18; | ||
222 | else | ||
223 | hctl = SDVS30; | ||
224 | capa = VS30 | VS18; | ||
225 | } else { | ||
226 | hctl = SDVS18; | ||
227 | capa = VS18; | ||
228 | } | ||
229 | |||
230 | OMAP_HSMMC_WRITE(host->base, HCTL, | ||
231 | OMAP_HSMMC_READ(host->base, HCTL) | hctl); | ||
232 | |||
233 | OMAP_HSMMC_WRITE(host->base, CAPA, | ||
234 | OMAP_HSMMC_READ(host->base, CAPA) | capa); | ||
235 | |||
236 | OMAP_HSMMC_WRITE(host->base, HCTL, | ||
237 | OMAP_HSMMC_READ(host->base, HCTL) | SDBP); | ||
238 | |||
239 | timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); | ||
240 | while ((OMAP_HSMMC_READ(host->base, HCTL) & SDBP) != SDBP | ||
241 | && time_before(jiffies, timeout)) | ||
242 | ; | ||
243 | |||
244 | OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); | ||
245 | OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK); | ||
246 | OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK); | ||
247 | |||
248 | /* Do not initialize card-specific things if the power is off */ | ||
249 | if (host->power_mode == MMC_POWER_OFF) | ||
250 | goto out; | ||
251 | |||
252 | con = OMAP_HSMMC_READ(host->base, CON); | ||
253 | switch (ios->bus_width) { | ||
254 | case MMC_BUS_WIDTH_8: | ||
255 | OMAP_HSMMC_WRITE(host->base, CON, con | DW8); | ||
256 | break; | ||
257 | case MMC_BUS_WIDTH_4: | ||
258 | OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8); | ||
259 | OMAP_HSMMC_WRITE(host->base, HCTL, | ||
260 | OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT); | ||
261 | break; | ||
262 | case MMC_BUS_WIDTH_1: | ||
263 | OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8); | ||
264 | OMAP_HSMMC_WRITE(host->base, HCTL, | ||
265 | OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT); | ||
266 | break; | ||
267 | } | ||
268 | |||
269 | if (ios->clock) { | ||
270 | dsor = OMAP_MMC_MASTER_CLOCK / ios->clock; | ||
271 | if (dsor < 1) | ||
272 | dsor = 1; | ||
273 | |||
274 | if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock) | ||
275 | dsor++; | ||
276 | |||
277 | if (dsor > 250) | ||
278 | dsor = 250; | ||
279 | } | ||
280 | |||
281 | OMAP_HSMMC_WRITE(host->base, SYSCTL, | ||
282 | OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN); | ||
283 | OMAP_HSMMC_WRITE(host->base, SYSCTL, (dsor << 6) | (DTO << 16)); | ||
284 | OMAP_HSMMC_WRITE(host->base, SYSCTL, | ||
285 | OMAP_HSMMC_READ(host->base, SYSCTL) | ICE); | ||
286 | |||
287 | timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); | ||
288 | while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != ICS | ||
289 | && time_before(jiffies, timeout)) | ||
290 | ; | ||
291 | |||
292 | OMAP_HSMMC_WRITE(host->base, SYSCTL, | ||
293 | OMAP_HSMMC_READ(host->base, SYSCTL) | CEN); | ||
294 | |||
295 | con = OMAP_HSMMC_READ(host->base, CON); | ||
296 | if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) | ||
297 | OMAP_HSMMC_WRITE(host->base, CON, con | OD); | ||
298 | else | ||
299 | OMAP_HSMMC_WRITE(host->base, CON, con & ~OD); | ||
300 | out: | ||
301 | host->context_loss = context_loss; | ||
302 | |||
303 | dev_dbg(mmc_dev(host->mmc), "context is restored\n"); | ||
304 | return 0; | ||
305 | } | ||
306 | |||
307 | /* | ||
308 | * Save the MMC host context (store the number of power state changes so far). | ||
309 | */ | ||
310 | static void omap_mmc_save_ctx(struct mmc_omap_host *host) | ||
311 | { | ||
312 | struct omap_mmc_platform_data *pdata = host->pdata; | ||
313 | int context_loss; | ||
314 | |||
315 | if (pdata->get_context_loss_count) { | ||
316 | context_loss = pdata->get_context_loss_count(host->dev); | ||
317 | if (context_loss < 0) | ||
318 | return; | ||
319 | host->context_loss = context_loss; | ||
320 | } | ||
321 | } | ||
322 | |||
323 | #else | ||
324 | |||
325 | static int omap_mmc_restore_ctx(struct mmc_omap_host *host) | ||
326 | { | ||
327 | return 0; | ||
328 | } | ||
329 | |||
330 | static void omap_mmc_save_ctx(struct mmc_omap_host *host) | ||
331 | { | ||
332 | } | ||
333 | |||
334 | #endif | ||
335 | |||
171 | /* | 336 | /* |
172 | * Send init stream sequence to card | 337 | * Send init stream sequence to card |
173 | * before sending IDLE command | 338 | * before sending IDLE command |
@@ -830,6 +995,7 @@ static int omap_mmc_enable(struct mmc_host *mmc) | |||
830 | if (err) | 995 | if (err) |
831 | return err; | 996 | return err; |
832 | dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n"); | 997 | dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n"); |
998 | omap_mmc_restore_ctx(host); | ||
833 | return 0; | 999 | return 0; |
834 | } | 1000 | } |
835 | 1001 | ||
@@ -837,6 +1003,7 @@ static int omap_mmc_disable(struct mmc_host *mmc, int lazy) | |||
837 | { | 1003 | { |
838 | struct mmc_omap_host *host = mmc_priv(mmc); | 1004 | struct mmc_omap_host *host = mmc_priv(mmc); |
839 | 1005 | ||
1006 | omap_mmc_save_ctx(host); | ||
840 | clk_disable(host->fclk); | 1007 | clk_disable(host->fclk); |
841 | dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n"); | 1008 | dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n"); |
842 | return 0; | 1009 | return 0; |
@@ -941,7 +1108,7 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
941 | 1108 | ||
942 | /* Wait till the ICS bit is set */ | 1109 | /* Wait till the ICS bit is set */ |
943 | timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); | 1110 | timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); |
944 | while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != 0x2 | 1111 | while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != ICS |
945 | && time_before(jiffies, timeout)) | 1112 | && time_before(jiffies, timeout)) |
946 | msleep(1); | 1113 | msleep(1); |
947 | 1114 | ||
@@ -1021,12 +1188,19 @@ static int mmc_regs_show(struct seq_file *s, void *data) | |||
1021 | { | 1188 | { |
1022 | struct mmc_host *mmc = s->private; | 1189 | struct mmc_host *mmc = s->private; |
1023 | struct mmc_omap_host *host = mmc_priv(mmc); | 1190 | struct mmc_omap_host *host = mmc_priv(mmc); |
1191 | struct omap_mmc_platform_data *pdata = host->pdata; | ||
1192 | int context_loss = 0; | ||
1193 | |||
1194 | if (pdata->get_context_loss_count) | ||
1195 | context_loss = pdata->get_context_loss_count(host->dev); | ||
1024 | 1196 | ||
1025 | seq_printf(s, "mmc%d:\n" | 1197 | seq_printf(s, "mmc%d:\n" |
1026 | " enabled:\t%d\n" | 1198 | " enabled:\t%d\n" |
1027 | " nesting_cnt:\t%d\n" | 1199 | " nesting_cnt:\t%d\n" |
1200 | " ctx_loss:\t%d:%d\n" | ||
1028 | "\nregs:\n", | 1201 | "\nregs:\n", |
1029 | mmc->index, mmc->enabled ? 1 : 0, mmc->nesting_cnt); | 1202 | mmc->index, mmc->enabled ? 1 : 0, mmc->nesting_cnt, |
1203 | host->context_loss, context_loss); | ||
1030 | 1204 | ||
1031 | if (clk_enable(host->fclk) != 0) { | 1205 | if (clk_enable(host->fclk) != 0) { |
1032 | seq_printf(s, "can't read the regs\n"); | 1206 | seq_printf(s, "can't read the regs\n"); |
@@ -1151,6 +1325,8 @@ static int __init omap_mmc_probe(struct platform_device *pdev) | |||
1151 | goto err1; | 1325 | goto err1; |
1152 | } | 1326 | } |
1153 | 1327 | ||
1328 | omap_mmc_save_ctx(host); | ||
1329 | |||
1154 | mmc->caps |= MMC_CAP_DISABLE; | 1330 | mmc->caps |= MMC_CAP_DISABLE; |
1155 | mmc_set_disable_delay(mmc, 100); | 1331 | mmc_set_disable_delay(mmc, 100); |
1156 | if (mmc_host_enable(host->mmc) != 0) { | 1332 | if (mmc_host_enable(host->mmc) != 0) { |
@@ -1385,21 +1561,19 @@ static int omap_mmc_resume(struct platform_device *pdev) | |||
1385 | return 0; | 1561 | return 0; |
1386 | 1562 | ||
1387 | if (host) { | 1563 | if (host) { |
1388 | |||
1389 | if (mmc_host_enable(host->mmc) != 0) | ||
1390 | goto clk_en_err; | ||
1391 | |||
1392 | ret = clk_enable(host->iclk); | 1564 | ret = clk_enable(host->iclk); |
1393 | if (ret) { | 1565 | if (ret) |
1394 | mmc_host_disable(host->mmc); | ||
1395 | clk_put(host->fclk); | ||
1396 | goto clk_en_err; | 1566 | goto clk_en_err; |
1397 | } | ||
1398 | 1567 | ||
1399 | if (clk_enable(host->dbclk) != 0) | 1568 | if (clk_enable(host->dbclk) != 0) |
1400 | dev_dbg(mmc_dev(host->mmc), | 1569 | dev_dbg(mmc_dev(host->mmc), |
1401 | "Enabling debounce clk failed\n"); | 1570 | "Enabling debounce clk failed\n"); |
1402 | 1571 | ||
1572 | if (mmc_host_enable(host->mmc) != 0) { | ||
1573 | clk_disable(host->iclk); | ||
1574 | goto clk_en_err; | ||
1575 | } | ||
1576 | |||
1403 | omap_hsmmc_init(host); | 1577 | omap_hsmmc_init(host); |
1404 | 1578 | ||
1405 | if (host->pdata->resume) { | 1579 | if (host->pdata->resume) { |