diff options
author | Adrian Hunter <adrian.hunter@nokia.com> | 2009-09-22 16:45:01 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-23 07:39:36 -0700 |
commit | b62f622812c5aaacad217fd8f4445562b917df6d (patch) | |
tree | 51ab6f42fa7be7460fb3d6f922f0ad9bacbd54f3 /drivers/mmc | |
parent | 70a3341a711f27ae77714ae7dd360a4e7e2d5e7c (diff) | |
download | linux-3.10-b62f622812c5aaacad217fd8f4445562b917df6d.tar.gz linux-3.10-b62f622812c5aaacad217fd8f4445562b917df6d.tar.bz2 linux-3.10-b62f622812c5aaacad217fd8f4445562b917df6d.zip |
omap_hsmmc: protect the card when the cover is open
Depending on the manufacturer, there is a small possibility that removing
a card while it is being written to, can render the card permanently
unusable. To prevent that, the card is made inaccessible when the cover
is open.
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')
-rw-r--r-- | drivers/mmc/host/omap_hsmmc.c | 63 |
1 files changed, 61 insertions, 2 deletions
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 20746e4f00b..a20fafecfc6 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -167,6 +167,8 @@ struct omap_hsmmc_host { int context_loss; int dpm_state; int vdd; + int protect_card; + int reqs_blocked; struct omap_mmc_platform_data *pdata; }; @@ -351,6 +353,9 @@ static void send_init_stream(struct omap_hsmmc_host *host) int reg = 0; unsigned long timeout; + if (host->protect_card) + return; + disable_irq(host->irq); OMAP_HSMMC_WRITE(host->base, CON, OMAP_HSMMC_READ(host->base, CON) | INIT_STREAM); @@ -786,6 +791,30 @@ err: return ret; } +/* Protect the card while the cover is open */ +static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host) +{ + if (!mmc_slot(host).get_cover_state) + return; + + host->reqs_blocked = 0; + if (mmc_slot(host).get_cover_state(host->dev, host->slot_id)) { + if (host->protect_card) { + printk(KERN_INFO "%s: cover is closed, " + "card is now accessible\n", + mmc_hostname(host->mmc)); + host->protect_card = 0; + } + } else { + if (!host->protect_card) { + printk(KERN_INFO "%s: cover is open, " + "card is now inaccessible\n", + mmc_hostname(host->mmc)); + host->protect_card = 1; + } + } +} + /* * Work Item to notify the core about card insertion/removal */ @@ -803,8 +832,10 @@ static void omap_hsmmc_detect(struct work_struct *work) if (slot->card_detect) carddetect = slot->card_detect(slot->card_detect_irq); - else + else { + omap_hsmmc_protect_card(host); carddetect = -ENOSYS; + } if (carddetect) { mmc_detect_change(host->mmc, (HZ * 200) / 1000); @@ -1040,8 +1071,32 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req) * interrupts, but not if we are already in interrupt context i.e. * retries. */ - if (!in_interrupt()) + if (!in_interrupt()) { spin_lock_irqsave(&host->irq_lock, host->flags); + /* + * Protect the card from I/O if there is a possibility + * it can be removed. + */ + if (host->protect_card) { + if (host->reqs_blocked < 3) { + /* + * Ensure the controller is left in a consistent + * state by resetting the command and data state + * machines. + */ + omap_hsmmc_reset_controller_fsm(host, SRD); + omap_hsmmc_reset_controller_fsm(host, SRC); + host->reqs_blocked += 1; + } + req->cmd->error = -EBADF; + if (req->data) + req->data->error = -EBADF; + spin_unlock_irqrestore(&host->irq_lock, host->flags); + mmc_request_done(mmc, req); + return; + } else if (host->reqs_blocked) + host->reqs_blocked = 0; + } WARN_ON(host->mrq != NULL); host->mrq = req; err = omap_hsmmc_prepare_data(host, req); @@ -1732,6 +1787,8 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) mmc_host_lazy_disable(host->mmc); + omap_hsmmc_protect_card(host); + mmc_add_host(mmc); if (mmc_slot(host).name != NULL) { @@ -1897,6 +1954,8 @@ static int omap_hsmmc_resume(struct platform_device *pdev) "Unmask interrupt failed\n"); } + omap_hsmmc_protect_card(host); + /* Notify the core to resume the host */ ret = mmc_resume_host(host->mmc); if (ret == 0) |