#include <linux/scatterlist.h>
#include <linux/log2.h>
#include <linux/regulator/consumer.h>
+#include <linux/wakelock.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include "sdio_ops.h"
static struct workqueue_struct *workqueue;
+static struct wake_lock mmc_delayed_work_wake_lock;
/*
* Enabling software CRCs on the data blocks can be a significant (30%)
static int mmc_schedule_delayed_work(struct delayed_work *work,
unsigned long delay)
{
+ wake_lock(&mmc_delayed_work_wake_lock);
return queue_delayed_work(workqueue, work, delay);
}
/* If the host is claimed then we do not want to disable it anymore */
if (!mmc_try_claim_host(host))
- return;
+ goto out;
mmc_host_do_disable(host, 1);
mmc_do_release_host(host);
+
+out:
+ wake_unlock(&mmc_delayed_work_wake_lock);
}
/**
*/
mmc_delay(10);
- if (host->f_min > 400000) {
- pr_warning("%s: Minimum clock frequency too high for "
- "identification mode\n", mmc_hostname(host));
- host->ios.clock = host->f_min;
- } else
- host->ios.clock = 400000;
+ host->ios.clock = host->f_min;
host->ios.power_mode = MMC_POWER_ON;
mmc_set_ios(host);
spin_unlock_irqrestore(&host->lock, flags);
}
+int mmc_resume_bus(struct mmc_host *host)
+{
+ if (!mmc_bus_needs_resume(host))
+ return -EINVAL;
+
+ printk("%s: Starting deferred resume\n", mmc_hostname(host));
+ host->bus_resume_flags &= ~MMC_BUSRESUME_NEEDS_RESUME;
+ mmc_bus_get(host);
+ if (host->bus_ops && !host->bus_dead) {
+ mmc_power_up(host);
+ BUG_ON(!host->bus_ops->resume);
+ host->bus_ops->resume(host);
+ }
+
+ if (host->bus_ops->detect && !host->bus_dead)
+ host->bus_ops->detect(host);
+
+ mmc_bus_put(host);
+ printk("%s: Deferred resume completed\n", mmc_hostname(host));
+ return 0;
+}
+
+EXPORT_SYMBOL(mmc_resume_bus);
+
/*
* Assign a mmc bus handler to a host. Only one bus handler may control a
* host at any given time.
u32 ocr;
int err;
unsigned long flags;
+ int extend_wakelock = 0;
spin_lock_irqsave(&host->lock, flags);
if ((host->bus_ops != NULL) && host->bus_ops->detect && !host->bus_dead)
host->bus_ops->detect(host);
+ /* If the card was removed the bus will be marked
+ * as dead - extend the wakelock so userspace
+ * can respond */
+ if (host->bus_dead)
+ extend_wakelock = 1;
+
mmc_bus_put(host);
if (!err) {
if (mmc_attach_sdio(host, ocr))
mmc_power_off(host);
+ extend_wakelock = 1;
goto out;
}
if (!err) {
if (mmc_attach_sd(host, ocr))
mmc_power_off(host);
+ extend_wakelock = 1;
goto out;
}
if (!err) {
if (mmc_attach_mmc(host, ocr))
mmc_power_off(host);
+ extend_wakelock = 1;
goto out;
}
mmc_power_off(host);
out:
+ if (extend_wakelock)
+ wake_lock_timeout(&mmc_delayed_work_wake_lock, HZ / 2);
+ else
+ wake_unlock(&mmc_delayed_work_wake_lock);
+
if (host->caps & MMC_CAP_NEEDS_POLL)
mmc_schedule_delayed_work(&host->detect, HZ);
}
{
int err = 0;
+ if (mmc_bus_needs_resume(host))
+ return 0;
+
if (host->caps & MMC_CAP_DISABLE)
cancel_delayed_work(&host->disable);
cancel_delayed_work(&host->detect);
int err = 0;
mmc_bus_get(host);
+ if (host->bus_resume_flags & MMC_BUSRESUME_MANUAL_RESUME) {
+ host->bus_resume_flags |= MMC_BUSRESUME_NEEDS_RESUME;
+ mmc_bus_put(host);
+ return 0;
+ }
+
if (host->bus_ops && !host->bus_dead) {
if (!(host->pm_flags & MMC_PM_KEEP_POWER)) {
mmc_power_up(host);
}
#endif
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+void mmc_set_embedded_sdio_data(struct mmc_host *host,
+ struct sdio_cis *cis,
+ struct sdio_cccr *cccr,
+ struct sdio_embedded_func *funcs,
+ int num_funcs)
+{
+ host->embedded_sdio_data.cis = cis;
+ host->embedded_sdio_data.cccr = cccr;
+ host->embedded_sdio_data.funcs = funcs;
+ host->embedded_sdio_data.num_funcs = num_funcs;
+}
+
+EXPORT_SYMBOL(mmc_set_embedded_sdio_data);
+#endif
+
static int __init mmc_init(void)
{
int ret;
+ wake_lock_init(&mmc_delayed_work_wake_lock, WAKE_LOCK_SUSPEND, "mmc_delayed_work");
+
workqueue = create_singlethread_workqueue("kmmcd");
if (!workqueue)
return -ENOMEM;
mmc_unregister_host_class();
mmc_unregister_bus();
destroy_workqueue(workqueue);
+ wake_lock_destroy(&mmc_delayed_work_wake_lock);
}
subsys_initcall(mmc_init);