From 36d8e79c022f9bcab5462eb4239e9a51bf0cfbaa Mon Sep 17 00:00:00 2001 From: Sahitya Tummala Date: Fri, 28 Mar 2014 09:42:09 +0530 Subject: [PATCH] scsi: ufs: Add sysfs node to dynamically control clock gating Provide an option to enable/disable clock gating during runtime. Write 1 or 0 to "clkgate_enable" sysfs node to enable/disable clock gating. Change-Id: I69a20e66cf91bb889ac3942eb2d20d5adc289d03 Signed-off-by: Sahitya Tummala [subhashj@codeaurora.org: resolved trivial merge conflicts] Signed-off-by: Subhash Jadavani --- drivers/scsi/ufs/ufshcd.c | 46 +++++++++++++++++++++++++++++++++++++++ drivers/scsi/ufs/ufshcd.h | 4 ++++ 2 files changed, 50 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index ca60459acf37..9287f4e4eeaf 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -939,6 +939,41 @@ static ssize_t ufshcd_clkgate_delay_store(struct device *dev, return count; } +static ssize_t ufshcd_clkgate_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", hba->clk_gating.is_enabled); +} + +static ssize_t ufshcd_clkgate_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + unsigned long flags; + u32 value; + + if (kstrtou32(buf, 0, &value)) + return -EINVAL; + + value = !!value; + if (value == hba->clk_gating.is_enabled) + goto out; + + if (value) { + ufshcd_release(hba); + } else { + spin_lock_irqsave(hba->host->host_lock, flags); + hba->clk_gating.active_reqs++; + spin_unlock_irqrestore(hba->host->host_lock, flags); + } + + hba->clk_gating.is_enabled = value; +out: + return count; +} + static void ufshcd_init_clk_gating(struct ufs_hba *hba) { if (!ufshcd_is_clkgating_allowed(hba)) @@ -948,6 +983,8 @@ static void ufshcd_init_clk_gating(struct ufs_hba *hba) INIT_DELAYED_WORK(&hba->clk_gating.gate_work, ufshcd_gate_work); INIT_WORK(&hba->clk_gating.ungate_work, ufshcd_ungate_work); + hba->clk_gating.is_enabled = true; + hba->clk_gating.delay_attr.show = ufshcd_clkgate_delay_show; hba->clk_gating.delay_attr.store = ufshcd_clkgate_delay_store; sysfs_attr_init(&hba->clk_gating.delay_attr.attr); @@ -955,6 +992,14 @@ static void ufshcd_init_clk_gating(struct ufs_hba *hba) hba->clk_gating.delay_attr.attr.mode = S_IRUGO | S_IWUSR; if (device_create_file(hba->dev, &hba->clk_gating.delay_attr)) dev_err(hba->dev, "Failed to create sysfs for clkgate_delay\n"); + + hba->clk_gating.enable_attr.show = ufshcd_clkgate_enable_show; + hba->clk_gating.enable_attr.store = ufshcd_clkgate_enable_store; + sysfs_attr_init(&hba->clk_gating.enable_attr.attr); + hba->clk_gating.enable_attr.attr.name = "clkgate_enable"; + hba->clk_gating.enable_attr.attr.mode = S_IRUGO | S_IWUSR; + if (device_create_file(hba->dev, &hba->clk_gating.enable_attr)) + dev_err(hba->dev, "Failed to create sysfs for clkgate_enable\n"); } static void ufshcd_exit_clk_gating(struct ufs_hba *hba) @@ -962,6 +1007,7 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba) if (!ufshcd_is_clkgating_allowed(hba)) return; device_remove_file(hba->dev, &hba->clk_gating.delay_attr); + device_remove_file(hba->dev, &hba->clk_gating.enable_attr); cancel_work_sync(&hba->clk_gating.ungate_work); cancel_delayed_work_sync(&hba->clk_gating.gate_work); } diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 91d52feb7806..ba9144fce057 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -308,6 +308,8 @@ enum clk_gating_state { * @is_suspended: clk gating is suspended when set to 1 which can be used * during suspend/resume * @delay_attr: sysfs attribute to control delay_attr + * @enable_attr: sysfs attribute to enable/disable clock gating + * @is_enabled: Indicates the current status of clock gating * @active_reqs: number of requests that are pending and should be waited for * completion before gating clocks. */ @@ -318,6 +320,8 @@ struct ufs_clk_gating { unsigned long delay_ms; bool is_suspended; struct device_attribute delay_attr; + struct device_attribute enable_attr; + bool is_enabled; int active_reqs; };