|
@@ -970,6 +970,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))
|
|
@@ -979,13 +1014,23 @@ 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);
|
|
|
hba->clk_gating.delay_attr.attr.name = "clkgate_delay_ms";
|
|
|
- hba->clk_gating.delay_attr.attr.mode = S_IRUGO | S_IWUSR;
|
|
|
+ hba->clk_gating.delay_attr.attr.mode = 0644;
|
|
|
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 = 0644;
|
|
|
+ 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)
|
|
@@ -993,6 +1038,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);
|
|
|
}
|