diff options
Diffstat (limited to 'drivers/mmc/core/host.c')
-rw-r--r-- | drivers/mmc/core/host.c | 53 |
1 files changed, 49 insertions, 4 deletions
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index d31c78b72b0f..30055f2b0d44 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -54,6 +54,27 @@ static DEFINE_IDR(mmc_host_idr); static DEFINE_SPINLOCK(mmc_host_lock); #ifdef CONFIG_MMC_CLKGATE +static ssize_t clkgate_delay_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mmc_host *host = cls_dev_to_mmc_host(dev); + return snprintf(buf, PAGE_SIZE, "%lu\n", host->clkgate_delay); +} + +static ssize_t clkgate_delay_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct mmc_host *host = cls_dev_to_mmc_host(dev); + unsigned long flags, value; + + if (kstrtoul(buf, 0, &value)) + return -EINVAL; + + spin_lock_irqsave(&host->clk_lock, flags); + host->clkgate_delay = value; + spin_unlock_irqrestore(&host->clk_lock, flags); + return count; +} /* * Enabling clock gating will make the core call out to the host @@ -114,7 +135,7 @@ static void mmc_host_clk_gate_delayed(struct mmc_host *host) static void mmc_host_clk_gate_work(struct work_struct *work) { struct mmc_host *host = container_of(work, struct mmc_host, - clk_gate_work); + clk_gate_work.work); mmc_host_clk_gate_delayed(host); } @@ -131,6 +152,8 @@ void mmc_host_clk_hold(struct mmc_host *host) { unsigned long flags; + /* cancel any clock gating work scheduled by mmc_host_clk_release() */ + cancel_delayed_work_sync(&host->clk_gate_work); mutex_lock(&host->clk_gate_mutex); spin_lock_irqsave(&host->clk_lock, flags); if (host->clk_gated) { @@ -180,7 +203,8 @@ void mmc_host_clk_release(struct mmc_host *host) host->clk_requests--; if (mmc_host_may_gate_card(host->card) && !host->clk_requests) - queue_work(system_nrt_wq, &host->clk_gate_work); + queue_delayed_work(system_nrt_wq, &host->clk_gate_work, + msecs_to_jiffies(host->clkgate_delay)); spin_unlock_irqrestore(&host->clk_lock, flags); } @@ -213,8 +237,13 @@ static inline void mmc_host_clk_init(struct mmc_host *host) host->clk_requests = 0; /* Hold MCI clock for 8 cycles by default */ host->clk_delay = 8; + /* + * Default clock gating delay is 200ms. + * This value can be tuned by writing into sysfs entry. + */ + host->clkgate_delay = 200; host->clk_gated = false; - INIT_WORK(&host->clk_gate_work, mmc_host_clk_gate_work); + INIT_DELAYED_WORK(&host->clk_gate_work, mmc_host_clk_gate_work); spin_lock_init(&host->clk_lock); mutex_init(&host->clk_gate_mutex); } @@ -229,7 +258,7 @@ static inline void mmc_host_clk_exit(struct mmc_host *host) * Wait for any outstanding gate and then make sure we're * ungated before exiting. */ - if (cancel_work_sync(&host->clk_gate_work)) + if (cancel_delayed_work_sync(&host->clk_gate_work)) mmc_host_clk_gate_delayed(host); if (host->clk_gated) mmc_host_clk_hold(host); @@ -237,6 +266,17 @@ static inline void mmc_host_clk_exit(struct mmc_host *host) WARN_ON(host->clk_requests > 1); } +static inline void mmc_host_clk_sysfs_init(struct mmc_host *host) +{ + host->clkgate_delay_attr.show = clkgate_delay_show; + host->clkgate_delay_attr.store = clkgate_delay_store; + sysfs_attr_init(&host->clkgate_delay_attr.attr); + host->clkgate_delay_attr.attr.name = "clkgate_delay"; + host->clkgate_delay_attr.attr.mode = S_IRUGO | S_IWUSR; + if (device_create_file(&host->class_dev, &host->clkgate_delay_attr)) + pr_err("%s: Failed to create clkgate_delay sysfs entry\n", + mmc_hostname(host)); +} #else static inline void mmc_host_clk_init(struct mmc_host *host) @@ -247,6 +287,10 @@ static inline void mmc_host_clk_exit(struct mmc_host *host) { } +static inline void mmc_host_clk_sysfs_init(struct mmc_host *host) +{ +} + #endif /** @@ -335,6 +379,7 @@ int mmc_add_host(struct mmc_host *host) #ifdef CONFIG_DEBUG_FS mmc_add_host_debugfs(host); #endif + mmc_host_clk_sysfs_init(host); mmc_start_host(host); register_pm_notifier(&host->pm_notify); |