aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/omap_hsmmc.c
diff options
context:
space:
mode:
authorDenis Karpov <ext-denis.2.karpov@nokia.com>2009-09-22 19:44:43 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-09-23 10:39:34 -0400
commit11dd62a741047b9fd1faf37620e2b58595f04ce5 (patch)
tree8d41e29fa2d8af9a92ab103f554086aceafaa47b /drivers/mmc/host/omap_hsmmc.c
parenta3621465b4fd91b7f8560f394afc494a57b77501 (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.c194
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 */
182static 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);
300out:
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 */
310static 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
325static int omap_mmc_restore_ctx(struct mmc_omap_host *host)
326{
327 return 0;
328}
329
330static 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) {